itemdef: handle the !render-to-target case
[oweals/minetest.git] / src / itemdef.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2011 Kahrl <kahrl@gmx.net>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
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 General Public License for more details.
15
16 You should have received a copy of the GNU 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.
19 */
20
21 #include "itemdef.h"
22
23 #include "gamedef.h"
24 #include "nodedef.h"
25 #include "materials.h"
26 #include "inventory.h"
27 #ifndef SERVER
28 #include "mapblock_mesh.h"
29 #include "mesh.h"
30 #include "tile.h"
31 #endif
32 #include "log.h"
33 #include "utility.h"
34 #include <map>
35 #include <set>
36
37 /*
38         ItemDefinition
39 */
40 ItemDefinition::ItemDefinition()
41 {
42         resetInitial();
43 }
44
45 ItemDefinition::ItemDefinition(const ItemDefinition &def)
46 {
47         resetInitial();
48         *this = def;
49 }
50
51 ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
52 {
53         if(this == &def)
54                 return *this;
55
56         reset();
57
58         type = def.type;
59         name = def.name;
60         description = def.description;
61         inventory_image = def.inventory_image;
62         wield_image = def.wield_image;
63         wield_scale = def.wield_scale;
64         stack_max = def.stack_max;
65         usable = def.usable;
66         liquids_pointable = def.liquids_pointable;
67         if(def.tool_digging_properties)
68         {
69                 tool_digging_properties = new ToolDiggingProperties(
70                                 *def.tool_digging_properties);
71         }
72 #ifndef SERVER
73         inventory_texture = def.inventory_texture;
74         if(def.wield_mesh)
75         {
76                 wield_mesh = def.wield_mesh;
77                 wield_mesh->grab();
78         }
79 #endif
80         return *this;
81 }
82
83 ItemDefinition::~ItemDefinition()
84 {
85         reset();
86 }
87
88 void ItemDefinition::resetInitial()
89 {
90         // Initialize pointers to NULL so reset() does not delete undefined pointers
91         tool_digging_properties = NULL;
92 #ifndef SERVER
93         inventory_texture = NULL;
94         wield_mesh = NULL;
95 #endif
96         reset();
97 }
98
99 void ItemDefinition::reset()
100 {
101         type = ITEM_NONE;
102         name = "";
103         description = "";
104         inventory_image = "";
105         wield_image = "";
106         wield_scale = v3f(1.0, 1.0, 1.0);
107         stack_max = 99;
108         usable = false;
109         liquids_pointable = false;
110         if(tool_digging_properties)
111         {
112                 delete tool_digging_properties;
113                 tool_digging_properties = NULL;
114         }
115
116 #ifndef SERVER
117         inventory_texture = NULL;
118         if(wield_mesh)
119         {
120                 wield_mesh->drop();
121                 wield_mesh = NULL;
122         }
123 #endif
124 }
125
126 void ItemDefinition::serialize(std::ostream &os) const
127 {
128         writeU8(os, 0); // version
129         writeU8(os, type);
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);
136         writeU8(os, usable);
137         writeU8(os, liquids_pointable);
138         std::string tool_digging_properties_s = "";
139         if(tool_digging_properties)
140         {
141                 std::ostringstream tmp_os(std::ios::binary);
142                 tool_digging_properties->serialize(tmp_os);
143                 tool_digging_properties_s = tmp_os.str();
144         }
145         os<<serializeString(tool_digging_properties_s);
146 }
147
148 void ItemDefinition::deSerialize(std::istream &is)
149 {
150         // Reset everything
151         reset();
152
153         // Deserialize
154         int version = readU8(is);
155         if(version != 0)
156                 throw SerializationError("unsupported ItemDefinition version");
157         type = (enum ItemType)readU8(is);
158         name = deSerializeString(is);
159         description = deSerializeString(is);
160         inventory_image = deSerializeString(is);
161         wield_image = deSerializeString(is);
162         wield_scale = readV3F1000(is);
163         stack_max = readS16(is);
164         usable = readU8(is);
165         liquids_pointable = readU8(is);
166         std::string tool_digging_properties_s = deSerializeString(is);
167         if(!tool_digging_properties_s.empty())
168         {
169                 std::istringstream tmp_is(tool_digging_properties_s, std::ios::binary);
170                 tool_digging_properties = new ToolDiggingProperties;
171                 tool_digging_properties->deSerialize(tmp_is);
172         }
173 }
174
175 /*
176         CItemDefManager
177 */
178
179 // SUGG: Support chains of aliases?
180
181 class CItemDefManager: public IWritableItemDefManager
182 {
183 public:
184         CItemDefManager()
185         {
186                 clear();
187         }
188         virtual ~CItemDefManager()
189         {
190         }
191         virtual const ItemDefinition& get(const std::string &name_) const
192         {
193                 // Convert name according to possible alias
194                 std::string name = getAlias(name_);
195                 // Get the definition
196                 std::map<std::string, ItemDefinition*>::const_iterator i;
197                 i = m_item_definitions.find(name);
198                 if(i == m_item_definitions.end())
199                         i = m_item_definitions.find("unknown");
200                 assert(i != m_item_definitions.end());
201                 return *(i->second);
202         }
203         virtual std::string getAlias(const std::string &name) const
204         {
205                 std::map<std::string, std::string>::const_iterator i;
206                 i = m_aliases.find(name);
207                 if(i != m_aliases.end())
208                         return i->second;
209                 return name;
210         }
211         virtual std::set<std::string> getAll() const
212         {
213                 std::set<std::string> result;
214                 for(std::map<std::string, ItemDefinition*>::const_iterator
215                                 i = m_item_definitions.begin();
216                                 i != m_item_definitions.end(); i++)
217                 {
218                         result.insert(i->first);
219                 }
220                 for(std::map<std::string, std::string>::const_iterator
221                                 i = m_aliases.begin();
222                                 i != m_aliases.end(); i++)
223                 {
224                         result.insert(i->first);
225                 }
226                 return result;
227         }
228         virtual bool isKnown(const std::string &name_) const
229         {
230                 // Convert name according to possible alias
231                 std::string name = getAlias(name_);
232                 // Get the definition
233                 std::map<std::string, ItemDefinition*>::const_iterator i;
234                 return m_item_definitions.find(name) != m_item_definitions.end();
235         }
236         void clear()
237         {
238                 for(std::map<std::string, ItemDefinition*>::const_iterator
239                                 i = m_item_definitions.begin();
240                                 i != m_item_definitions.end(); i++)
241                 {
242                         delete i->second;
243                 }
244                 m_item_definitions.clear();
245                 m_aliases.clear();
246
247                 // Add the four builtin items:
248                 //   "" is the hand
249                 //   "unknown" is returned whenever an undefined item is accessed
250                 //   "air" is the air node
251                 //   "ignore" is the ignore node
252
253                 ItemDefinition* hand_def = new ItemDefinition;
254                 hand_def->name = "";
255                 hand_def->wield_image = "wieldhand.png";
256                 hand_def->tool_digging_properties = new ToolDiggingProperties;
257                 m_item_definitions.insert(std::make_pair("", hand_def));
258
259                 ItemDefinition* unknown_def = new ItemDefinition;
260                 unknown_def->name = "unknown";
261                 m_item_definitions.insert(std::make_pair("unknown", unknown_def));
262
263                 ItemDefinition* air_def = new ItemDefinition;
264                 air_def->type = ITEM_NODE;
265                 air_def->name = "air";
266                 m_item_definitions.insert(std::make_pair("air", air_def));
267
268                 ItemDefinition* ignore_def = new ItemDefinition;
269                 ignore_def->type = ITEM_NODE;
270                 ignore_def->name = "ignore";
271                 m_item_definitions.insert(std::make_pair("ignore", ignore_def));
272         }
273         virtual void registerItem(const ItemDefinition &def)
274         {
275                 infostream<<"ItemDefManager: registering \""<<def.name<<"\""<<std::endl;
276                 // Ensure that the "" item (the hand) always has ToolDiggingProperties
277                 if(def.name == "")
278                         assert(def.tool_digging_properties != NULL);
279
280                 m_item_definitions[def.name] = new ItemDefinition(def);
281
282                 // Remove conflicting alias if it exists
283                 bool alias_removed = (m_aliases.erase(def.name) != 0);
284                 if(alias_removed)
285                         infostream<<"ItemDefManager: erased alias "<<def.name
286                                         <<" because item was defined"<<std::endl;
287         }
288         virtual void registerAlias(const std::string &name,
289                         const std::string &convert_to)
290         {
291                 if(m_item_definitions.find(name) == m_item_definitions.end())
292                 {
293                         infostream<<"ItemDefManager: setting alias "<<name
294                                 <<" -> "<<convert_to<<std::endl;
295                         m_aliases[name] = convert_to;
296                 }
297         }
298
299         virtual void updateTexturesAndMeshes(IGameDef *gamedef)
300         {
301 #ifndef SERVER
302                 infostream<<"ItemDefManager::updateTexturesAndMeshes(): Updating "
303                                 <<"textures and meshes in item definitions"<<std::endl;
304
305                 ITextureSource *tsrc = gamedef->getTextureSource();
306                 INodeDefManager *nodedef = gamedef->getNodeDefManager();
307                 IrrlichtDevice *device = tsrc->getDevice();
308                 video::IVideoDriver *driver = device->getVideoDriver();
309
310                 for(std::map<std::string, ItemDefinition*>::iterator
311                                 i = m_item_definitions.begin();
312                                 i != m_item_definitions.end(); i++)
313                 {
314                         ItemDefinition *def = i->second;
315
316                         bool need_node_mesh = false;
317
318                         // Create an inventory texture
319                         def->inventory_texture = NULL;
320                         if(def->inventory_image != "")
321                         {
322                                 def->inventory_texture = tsrc->getTextureRaw(def->inventory_image);
323                         }
324                         else if(def->type == ITEM_NODE)
325                         {
326                                 need_node_mesh = true;
327                         }
328
329                         // Create a wield mesh
330                         if(def->wield_mesh != NULL)
331                         {
332                                 def->wield_mesh->drop();
333                                 def->wield_mesh = NULL;
334                         }
335                         if(def->type == ITEM_NODE && def->wield_image == "")
336                         {
337                                 need_node_mesh = true;
338                         }
339                         else if(def->wield_image != "" || def->inventory_image != "")
340                         {
341                                 // Extrude the wield image into a mesh
342
343                                 std::string imagename;
344                                 if(def->wield_image != "")
345                                         imagename = def->wield_image;
346                                 else
347                                         imagename = def->inventory_image;
348
349                                 def->wield_mesh = createExtrudedMesh(
350                                                 tsrc->getTextureRaw(imagename),
351                                                 driver,
352                                                 def->wield_scale * v3f(40.0, 40.0, 4.0));
353                                 if(def->wield_mesh == NULL)
354                                 {
355                                         infostream<<"ItemDefManager: WARNING: "
356                                                 <<"updateTexturesAndMeshes(): "
357                                                 <<"Unable to create extruded mesh for item "
358                                                 <<def->name<<std::endl;
359                                 }
360                         }
361
362                         if(need_node_mesh)
363                         {
364                                 /*
365                                         Get node properties
366                                 */
367                                 content_t id = nodedef->getId(def->name);
368                                 const ContentFeatures &f = nodedef->get(id);
369
370                                 /*
371                                         Make a mesh from the node
372                                 */
373                                 MeshMakeData mesh_make_data;
374                                 MapNode mesh_make_node(
375                                         id,
376                                         (f.param_type == CPT_LIGHT) ? 0xee : 0,
377                                         0);
378                                 mesh_make_data.fillSingleNode(1000, &mesh_make_node);
379                                 scene::IMesh *node_mesh =
380                                         makeMapBlockMesh(&mesh_make_data, gamedef);
381                                 setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
382
383                                 /*
384                                         Scale and translate the mesh so it's a unit cube
385                                         centered on the origin
386                                 */
387                                 scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
388                                 translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
389
390                                 /*
391                                         Draw node mesh into a render target texture
392                                 */
393                                 if(def->inventory_texture == NULL && node_mesh != NULL)
394                                 {
395                                         core::dimension2d<u32> dim(64,64);
396                                         std::string rtt_texture_name = "INVENTORY_"
397                                                 + def->name + "_RTT";
398                                         v3f camera_position(0, 1.0, -1.5);
399                                         camera_position.rotateXZBy(45);
400                                         v3f camera_lookat(0, 0, 0);
401                                         core::CMatrix4<f32> camera_projection_matrix;
402                                         // Set orthogonal projection
403                                         camera_projection_matrix.buildProjectionMatrixOrthoLH(
404                                                         1.65, 1.65, 0, 100);
405
406                                         video::SColorf ambient_light(0.2,0.2,0.2);
407                                         v3f light_position(10, 100, -50);
408                                         video::SColorf light_color(0.5,0.5,0.5);
409                                         f32 light_radius = 1000;
410
411                                         def->inventory_texture = generateTextureFromMesh(
412                                                 node_mesh, device, dim, rtt_texture_name,
413                                                 camera_position,
414                                                 camera_lookat,
415                                                 camera_projection_matrix,
416                                                 ambient_light,
417                                                 light_position,
418                                                 light_color,
419                                                 light_radius);
420
421                                         // render-to-target didn't work
422                                         if(def->inventory_texture == NULL)
423                                         {
424                                                 def->inventory_texture =
425                                                         tsrc->getTextureRaw(f.tname_tiles[0]);
426                                         }
427                                 }
428
429                                 /*
430                                         Use the node mesh as the wield mesh
431                                 */
432                                 if(def->wield_mesh == NULL && node_mesh != NULL)
433                                 {
434                                         // Scale to proper wield mesh proportions
435                                         scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
436                                                         * def->wield_scale);
437                                         def->wield_mesh = node_mesh;
438                                         def->wield_mesh->grab();
439                                 }
440
441
442                                 if(node_mesh != NULL)
443                                         node_mesh->drop();
444                         }
445                 }
446 #endif
447         }
448         void serialize(std::ostream &os)
449         {
450                 writeU8(os, 0); // version
451                 u16 count = m_item_definitions.size();
452                 writeU16(os, count);
453                 for(std::map<std::string, ItemDefinition*>::const_iterator
454                                 i = m_item_definitions.begin();
455                                 i != m_item_definitions.end(); i++)
456                 {
457                         ItemDefinition *def = i->second;
458                         // Serialize ItemDefinition and write wrapped in a string
459                         std::ostringstream tmp_os(std::ios::binary);
460                         def->serialize(tmp_os);
461                         os<<serializeString(tmp_os.str());
462                 }
463                 writeU16(os, m_aliases.size());
464                 for(std::map<std::string, std::string>::const_iterator
465                         i = m_aliases.begin(); i != m_aliases.end(); i++)
466                 {
467                         os<<serializeString(i->first);
468                         os<<serializeString(i->second);
469                 }
470         }
471         void deSerialize(std::istream &is)
472         {
473                 // Clear everything
474                 clear();
475                 // Deserialize
476                 int version = readU8(is);
477                 if(version != 0)
478                         throw SerializationError("unsupported ItemDefManager version");
479                 u16 count = readU16(is);
480                 for(u16 i=0; i<count; i++)
481                 {
482                         // Deserialize a string and grab an ItemDefinition from it
483                         std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
484                         ItemDefinition def;
485                         def.deSerialize(tmp_is);
486                         // Register
487                         registerItem(def);
488                 }
489                 u16 num_aliases = readU16(is);
490                 for(u16 i=0; i<num_aliases; i++)
491                 {
492                         std::string name = deSerializeString(is);
493                         std::string convert_to = deSerializeString(is);
494                         registerAlias(name, convert_to);
495                 }
496         }
497 private:
498         // Key is name
499         std::map<std::string, ItemDefinition*> m_item_definitions;
500         // Aliases
501         std::map<std::string, std::string> m_aliases;
502 };
503
504 IWritableItemDefManager* createItemDefManager()
505 {
506         return new CItemDefManager();
507 }
508