3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013 Kahrl <kahrl@gmx.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "inventory.h"
28 #include "mapblock_mesh.h"
30 #include "wieldmesh.h"
34 #include "util/serialize.h"
43 ItemDefinition::ItemDefinition()
48 ItemDefinition::ItemDefinition(const ItemDefinition &def)
54 ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
63 description = def.description;
64 inventory_image = def.inventory_image;
65 wield_image = def.wield_image;
66 wield_scale = def.wield_scale;
67 stack_max = def.stack_max;
69 liquids_pointable = def.liquids_pointable;
70 if(def.tool_capabilities)
72 tool_capabilities = new ToolCapabilities(
73 *def.tool_capabilities);
76 node_placement_prediction = def.node_placement_prediction;
77 sound_place = def.sound_place;
78 sound_place_failed = def.sound_place_failed;
80 meshname = def.meshname;
81 meshtexture = def.meshtexture;
85 ItemDefinition::~ItemDefinition()
90 void ItemDefinition::resetInitial()
92 // Initialize pointers to NULL so reset() does not delete undefined pointers
93 tool_capabilities = NULL;
97 void ItemDefinition::reset()
102 inventory_image = "";
104 wield_scale = v3f(1.0, 1.0, 1.0);
107 liquids_pointable = false;
108 if(tool_capabilities)
110 delete tool_capabilities;
111 tool_capabilities = NULL;
114 sound_place = SimpleSoundSpec();
115 sound_place_failed = SimpleSoundSpec();
118 node_placement_prediction = "";
121 void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
123 if(protocol_version <= 17)
124 writeU8(os, 1); // version
125 else if(protocol_version <= 20)
126 writeU8(os, 2); // version
128 writeU8(os, 3); // version
130 os<<serializeString(name);
131 os<<serializeString(description);
132 os<<serializeString(inventory_image);
133 os<<serializeString(wield_image);
134 writeV3F1000(os, wield_scale);
135 writeS16(os, stack_max);
137 writeU8(os, liquids_pointable);
138 std::string tool_capabilities_s = "";
139 if(tool_capabilities){
140 std::ostringstream tmp_os(std::ios::binary);
141 tool_capabilities->serialize(tmp_os, protocol_version);
142 tool_capabilities_s = tmp_os.str();
144 os<<serializeString(tool_capabilities_s);
145 writeU16(os, groups.size());
146 for(std::map<std::string, int>::const_iterator
147 i = groups.begin(); i != groups.end(); ++i){
148 os<<serializeString(i->first);
149 writeS16(os, i->second);
151 os<<serializeString(node_placement_prediction);
152 if(protocol_version > 17){
153 //serializeSimpleSoundSpec(sound_place, os);
154 os<<serializeString(sound_place.name);
155 writeF1000(os, sound_place.gain);
157 if (protocol_version > 20) {
158 writeF1000(os, range);
159 os << serializeString(sound_place_failed.name);
160 writeF1000(os, sound_place_failed.gain);
163 //TODO check for protocol version?
164 os<<serializeString(meshname);
165 os<<serializeString(meshtexture);
168 void ItemDefinition::deSerialize(std::istream &is)
174 int version = readU8(is);
175 if(version < 1 || version > 3)
176 throw SerializationError("unsupported ItemDefinition version");
177 type = (enum ItemType)readU8(is);
178 name = deSerializeString(is);
179 description = deSerializeString(is);
180 inventory_image = deSerializeString(is);
181 wield_image = deSerializeString(is);
182 wield_scale = readV3F1000(is);
183 stack_max = readS16(is);
185 liquids_pointable = readU8(is);
186 std::string tool_capabilities_s = deSerializeString(is);
187 if(!tool_capabilities_s.empty())
189 std::istringstream tmp_is(tool_capabilities_s, std::ios::binary);
190 tool_capabilities = new ToolCapabilities;
191 tool_capabilities->deSerialize(tmp_is);
194 u32 groups_size = readU16(is);
195 for(u32 i=0; i<groups_size; i++){
196 std::string name = deSerializeString(is);
197 int value = readS16(is);
198 groups[name] = value;
201 // We cant be sure that node_placement_prediction is send in version 1
203 node_placement_prediction = deSerializeString(is);
204 }catch(SerializationError &e) {};
205 // Set the old default sound
206 sound_place.name = "default_place_node";
207 sound_place.gain = 0.5;
208 } else if(version >= 2) {
209 node_placement_prediction = deSerializeString(is);
210 //deserializeSimpleSoundSpec(sound_place, is);
211 sound_place.name = deSerializeString(is);
212 sound_place.gain = readF1000(is);
215 range = readF1000(is);
217 // If you add anything here, insert it primarily inside the try-catch
218 // block to not need to increase the version.
220 sound_place_failed.name = deSerializeString(is);
221 sound_place_failed.gain = readF1000(is);
222 } catch(SerializationError &e) {};
224 //TODO check for protocol?
225 meshname = deSerializeString(is);
226 meshtexture = deSerializeString(is);
233 // SUGG: Support chains of aliases?
236 CItemDefManager::ClientCached::ClientCached() :
237 inventory_texture(NULL),
242 CItemDefManager::CItemDefManager()
245 m_main_thread = thr_get_current_thread_id();
250 /******************************************************************************/
251 CItemDefManager::~CItemDefManager()
254 const std::vector<ClientCached*> &values = m_clientcached.getValues();
255 for(std::vector<ClientCached*>::const_iterator
256 i = values.begin(); i != values.end(); ++i) {
258 ClientCached *cc = *i;
260 cc->wield_mesh->drop();
265 for (std::map<std::string, ItemDefinition*>::iterator iter =
266 m_item_definitions.begin(); iter != m_item_definitions.end();
271 m_item_definitions.clear();
274 /******************************************************************************/
275 const ItemDefinition& CItemDefManager::get(const std::string &name_) const
277 // Convert name according to possible alias
278 std::string name = getAlias(name_);
279 // Get the definition
280 std::map<std::string, ItemDefinition*>::const_iterator i;
281 i = m_item_definitions.find(name);
282 if(i == m_item_definitions.end())
283 i = m_item_definitions.find("unknown");
284 assert(i != m_item_definitions.end());
288 /******************************************************************************/
289 std::string CItemDefManager::getAlias(const std::string &name) const
291 StringMap::const_iterator it = m_aliases.find(name);
292 if (it != m_aliases.end())
298 /******************************************************************************/
299 std::set<std::string> CItemDefManager::getAll() const
301 std::set<std::string> result;
302 for(std::map<std::string, ItemDefinition *>::const_iterator
303 it = m_item_definitions.begin();
304 it != m_item_definitions.end(); ++it) {
306 result.insert(it->first);
308 for (StringMap::const_iterator
309 it = m_aliases.begin();
310 it != m_aliases.end(); ++it) {
312 result.insert(it->first);
317 /******************************************************************************/
318 bool CItemDefManager::isKnown(const std::string &name_) const
320 // Convert name according to possible alias
321 std::string name = getAlias(name_);
322 // Get the definition
323 std::map<std::string, ItemDefinition*>::const_iterator i;
325 return m_item_definitions.find(name) != m_item_definitions.end();
329 /******************************************************************************/
330 CItemDefManager::ClientCached* CItemDefManager::createClientCachedDirect(const std::string &name,
331 IGameDef *gamedef) const
333 infostream<<"Lazily creating item texture and mesh for \""
334 <<name<<"\""<<std::endl;
336 // This is not thread-safe
337 sanity_check(thr_is_current_thread(m_main_thread));
339 // Skip if already in cache
340 ClientCached *cc = NULL;
341 m_clientcached.get(name, &cc);
345 ITextureSource *tsrc = gamedef->getTextureSource();
346 INodeDefManager *nodedef = gamedef->getNodeDefManager();
347 const ItemDefinition &def = get(name);
349 // Create new ClientCached
350 cc = new ClientCached();
352 // Create an inventory texture
353 cc->inventory_texture = NULL;
354 if (!def.inventory_image.empty())
355 cc->inventory_texture = tsrc->getTexture(def.inventory_image);
357 // Additional processing for nodes:
358 // - Create a wield mesh if WieldMeshSceneNode can't render
359 // the node on its own.
360 // - If inventory_texture isn't set yet, create one using
361 // render-to-texture.
362 if (def.type == ITEM_NODE) {
363 createNodeItemTexture(name, def, nodedef, cc, gamedef, tsrc);
365 else if (def.type == ITEM_CRAFT) {
366 if ( !def.meshname.empty())
367 createMeshItemTexture(name, def, nodedef, cc, gamedef, tsrc);
371 m_clientcached.set(name, cc);
376 /******************************************************************************/
377 CItemDefManager::ClientCached* CItemDefManager::getClientCached(const std::string &name,
378 IGameDef *gamedef) const
380 ClientCached *cc = NULL;
381 m_clientcached.get(name, &cc);
385 if (thr_is_current_thread(m_main_thread)) {
387 return createClientCachedDirect(name, gamedef);
391 // We're gonna ask the result to be put into here
392 static ResultQueue<std::string, ClientCached*, u8, u8> result_queue;
394 // Throw a request in
395 m_get_clientcached_queue.add(name, 0, 0, &result_queue);
398 // Wait result for a second
399 GetResult<std::string, ClientCached*, u8, u8>
400 result = result_queue.pop_front(1000);
402 if (result.key == name) {
407 catch(ItemNotFoundException &e)
409 errorstream<<"Waiting for clientcached " << name << " timed out."<<std::endl;
410 return &m_dummy_clientcached;
415 /******************************************************************************/
416 // Get item inventory texture
417 video::ITexture* CItemDefManager::getInventoryTexture(const std::string &name,
418 IGameDef *gamedef) const
420 ClientCached *cc = getClientCached(name, gamedef);
424 return cc->inventory_texture;
427 /******************************************************************************/
428 // Get item wield mesh
429 scene::IMesh* CItemDefManager::getWieldMesh(const std::string &name,
430 IGameDef *gamedef) const
432 ClientCached *cc = getClientCached(name, gamedef);
436 return cc->wield_mesh;
440 /******************************************************************************/
441 void CItemDefManager::clear()
443 for (std::map<std::string, ItemDefinition*>::const_iterator
444 i = m_item_definitions.begin();
445 i != m_item_definitions.end(); ++i) {
449 m_item_definitions.clear();
452 // Add the four builtin items:
454 // "unknown" is returned whenever an undefined item
455 // is accessed (is also the unknown node)
456 // "air" is the air node
457 // "ignore" is the ignore node
459 ItemDefinition* hand_def = new ItemDefinition;
461 hand_def->wield_image = "wieldhand.png";
462 hand_def->tool_capabilities = new ToolCapabilities;
463 m_item_definitions.insert(std::make_pair("", hand_def));
465 ItemDefinition* unknown_def = new ItemDefinition;
466 unknown_def->type = ITEM_NODE;
467 unknown_def->name = "unknown";
468 m_item_definitions.insert(std::make_pair("unknown", unknown_def));
470 ItemDefinition* air_def = new ItemDefinition;
471 air_def->type = ITEM_NODE;
472 air_def->name = "air";
473 m_item_definitions.insert(std::make_pair("air", air_def));
475 ItemDefinition* ignore_def = new ItemDefinition;
476 ignore_def->type = ITEM_NODE;
477 ignore_def->name = "ignore";
478 m_item_definitions.insert(std::make_pair("ignore", ignore_def));
481 /******************************************************************************/
482 void CItemDefManager::registerItem(const ItemDefinition &def)
484 verbosestream<<"ItemDefManager: registering \""<<def.name<<"\""<<std::endl;
485 // Ensure that the "" item (the hand) always has ToolCapabilities
487 FATAL_ERROR_IF(!def.tool_capabilities, "Hand does not have ToolCapabilities");
489 if (m_item_definitions.count(def.name) == 0)
490 m_item_definitions[def.name] = new ItemDefinition(def);
492 *(m_item_definitions[def.name]) = def;
494 // Remove conflicting alias if it exists
495 bool alias_removed = (m_aliases.erase(def.name) != 0);
497 infostream<<"ItemDefManager: erased alias "<<def.name
498 <<" because item was defined"<<std::endl;
501 /******************************************************************************/
502 void CItemDefManager::registerAlias(const std::string &name,
503 const std::string &convert_to)
505 if (m_item_definitions.find(name) == m_item_definitions.end()) {
507 verbosestream<<"ItemDefManager: setting alias "<<name
508 <<" -> "<<convert_to<<std::endl;
509 m_aliases[name] = convert_to;
513 /******************************************************************************/
514 void CItemDefManager::serialize(std::ostream &os, u16 protocol_version)
516 writeU8(os, 0); // version
517 u16 count = m_item_definitions.size();
520 for (std::map<std::string, ItemDefinition *>::const_iterator
521 it = m_item_definitions.begin();
522 it != m_item_definitions.end(); ++it) {
523 ItemDefinition *def = it->second;
524 // Serialize ItemDefinition and write wrapped in a string
525 std::ostringstream tmp_os(std::ios::binary);
526 def->serialize(tmp_os, protocol_version);
527 os << serializeString(tmp_os.str());
530 writeU16(os, m_aliases.size());
532 for (StringMap::const_iterator
533 it = m_aliases.begin();
534 it != m_aliases.end(); ++it) {
535 os << serializeString(it->first);
536 os << serializeString(it->second);
540 /******************************************************************************/
541 void CItemDefManager::deSerialize(std::istream &is)
546 int version = readU8(is);
548 throw SerializationError("unsupported ItemDefManager version");
549 u16 count = readU16(is);
551 for (u16 i=0; i<count; i++) {
553 // Deserialize a string and grab an ItemDefinition from it
554 std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
556 def.deSerialize(tmp_is);
561 u16 num_aliases = readU16(is);
562 for (u16 i=0; i<num_aliases; i++) {
564 std::string name = deSerializeString(is);
565 std::string convert_to = deSerializeString(is);
566 registerAlias(name, convert_to);
570 /******************************************************************************/
571 void CItemDefManager::processQueue(IGameDef *gamedef)
574 //NOTE this is only thread safe for ONE consumer thread!
575 while (!m_get_clientcached_queue.empty()) {
577 GetRequest<std::string, ClientCached*, u8, u8>
578 request = m_get_clientcached_queue.pop();
580 m_get_clientcached_queue.pushResult(request,
581 createClientCachedDirect(request.key, gamedef));
587 /******************************************************************************/
588 void CItemDefManager::createNodeItemTexture(const std::string& name,
589 const ItemDefinition& def, INodeDefManager* nodedef,
590 ClientCached* cc, IGameDef* gamedef, ITextureSource* tsrc) const
592 // Get node properties
593 content_t id = nodedef->getId(name);
594 const ContentFeatures& f = nodedef->get(id);
595 bool need_rtt_mesh = cc->inventory_texture == NULL;
596 // Keep this in sync with WieldMeshSceneNode::setItem()
597 bool need_wield_mesh = !(f.mesh_ptr[0] || f.drawtype == NDT_NORMAL
598 || f.drawtype == NDT_ALLFACES || f.drawtype == NDT_AIRLIKE);
599 scene::IMesh* node_mesh = NULL;
601 if (need_rtt_mesh || need_wield_mesh) {
604 if (f.param_type == CPT_LIGHT)
608 Make a mesh from the node
610 MeshMakeData mesh_make_data(gamedef, false);
612 if (f.param_type_2 == CPT2_WALLMOUNTED)
615 MapNode mesh_make_node(id, param1, param2);
616 mesh_make_data.fillSingleNode(&mesh_make_node);
617 MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
618 node_mesh = mapblock_mesh.getMesh();
620 video::SColor c(255, 255, 255, 255);
621 setMeshColor(node_mesh, c);
622 // scale and translate the mesh so it's a
623 // unit cube centered on the origin
624 scaleMesh(node_mesh, v3f(1.0 / BS, 1.0 / BS, 1.0 / BS));
625 translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
628 Draw node mesh into a render target texture
632 TextureFromMeshParams params;
633 params.mesh = node_mesh;
634 params.dim.set(64, 64);
635 params.rtt_texture_name = "INVENTORY_" + def.name + "_RTT";
636 params.delete_texture_on_shutdown = true;
637 params.camera_position.set(0, 1.0, -1.5);
638 params.camera_position.rotateXZBy(45);
639 params.camera_lookat.set(0, 0, 0);
640 // Set orthogonal projection
641 params.camera_projection_matrix.buildProjectionMatrixOrthoLH(1.65,
643 params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
644 params.light_position.set(10, 100, -50);
645 params.light_color.set(1.0, 0.5, 0.5, 0.5);
646 params.light_radius = 1000;
647 cc->inventory_texture = tsrc->generateTextureFromMesh(params);
648 // render-to-target didn't work
649 if (cc->inventory_texture == NULL) {
651 cc->inventory_texture = tsrc->getTexture(f.tiledef[0].name);
655 Use the node mesh as the wield mesh
657 if (need_wield_mesh) {
659 cc->wield_mesh = node_mesh;
660 cc->wield_mesh->grab();
661 // no way reference count can be smaller than 2 in this place!
662 assert(cc->wield_mesh->getReferenceCount() >= 2);
668 /******************************************************************************/
669 void CItemDefManager::renderMeshToTexture(const ItemDefinition& def, scene::IMesh* mesh,
670 ClientCached* cc, ITextureSource* tsrc) const
672 video::ITexture *itemimage = cc->inventory_texture;
675 Draw node mesh into a render target texture
677 TextureFromMeshParams params;
679 params.dim.set(64, 64);
680 params.rtt_texture_name = "INVENTORY_" + def.name + "_RTT";
681 params.delete_texture_on_shutdown = true;
682 params.camera_position.set(0, 1.0, -1.5);
683 params.camera_position.rotateXZBy(45);
684 params.camera_lookat.set(0, 0, 0);
685 // Set orthogonal projection
686 params.camera_projection_matrix.buildProjectionMatrixOrthoLH(1.65, 1.65, 0,
688 params.ambient_light.set(1.0, 0.9, 0.9, 0.9);
689 params.light_position.set(10, 100, -50);
690 params.light_color.set(1.0, 0.5, 0.5, 0.5);
691 params.light_radius = 1000;
692 cc->inventory_texture = tsrc->generateTextureFromMesh(params);
694 // render-to-target didn't work
695 if (cc->inventory_texture == NULL) {
697 cc->inventory_texture = itemimage;
701 /******************************************************************************/
702 void CItemDefManager::createMeshItemTexture(const std::string& name,
703 const ItemDefinition& def, INodeDefManager* nodedef,
704 ClientCached* cc, IGameDef* gamedef, ITextureSource* tsrc) const
706 // Get node properties
707 content_t id = nodedef->getId(name);
708 const ContentFeatures& f = nodedef->get(id);
710 if (def.meshname == "")
713 video::ITexture *texture = tsrc->getTexture(def.meshtexture);
715 infostream<<"CItemDefManager::createMeshItemTexture(): mesh"<<std::endl;
717 scene::IAnimatedMesh *mesh = gamedef->getMesh(def.meshname);
721 video::SColor c(255, 255, 255, 255);
722 setMeshColor(mesh, c);
724 rotateMeshXZby(mesh, 180);
726 // scale and translate the mesh so it's a
727 // unit cube centered on the origin
728 scaleMesh(mesh, v3f(1.0 / BS, 1.0 / BS, 1.0 / BS));
730 // Customize materials
731 for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) {
733 video::SMaterial &material = mesh->getMeshBuffer(i)->getMaterial();
734 material.setTexture(0, texture);
735 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
736 material.setFlag(video::EMF_BILINEAR_FILTER, false);
737 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
741 Draw node mesh into a render target texture
743 renderMeshToTexture(def, mesh, cc, tsrc);
746 Use the ingot mesh as the wield mesh
749 cc->wield_mesh = mesh;
750 cc->wield_mesh->grab();
751 // no way reference count can be smaller than 2 in this place!
752 assert(cc->wield_mesh->getReferenceCount() >= 2);
759 errorstream<<"CItemDefManager::createMeshItemTexture(): Could not load mesh "<<def.meshname<<std::endl;
763 /******************************************************************************/
765 IWritableItemDefManager* createItemDefManager()
767 return new CItemDefManager();