Automate texture listing for texture atlas making
[oweals/minetest.git] / src / mapnode.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "common_irrlicht.h"
21 #include "mapnode.h"
22 #ifndef SERVER
23 #include "tile.h"
24 #endif
25 #include "porting.h"
26 #include <string>
27 #include "mineral.h"
28 // For g_settings
29 #include "main.h"
30 #include "content_mapnode.h"
31 #include "nodemetadata.h"
32
33 ContentFeatures::~ContentFeatures()
34 {
35         delete initial_metadata;
36 #ifndef SERVER
37         delete special_material;
38         delete special_atlas;
39 #endif
40 }
41
42 #ifndef SERVER
43 void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha)
44 {
45         used_texturenames[name] = true;
46         
47         if(g_texturesource)
48         {
49                 tiles[i].texture = g_texturesource->getTexture(name);
50         }
51         
52         if(alpha != 255)
53         {
54                 tiles[i].alpha = alpha;
55                 tiles[i].material_type = MATERIAL_ALPHA_VERTEX;
56         }
57
58         if(inventory_texture == NULL)
59                 setInventoryTexture(name);
60 }
61
62 void ContentFeatures::setInventoryTexture(std::string imgname)
63 {
64         if(g_texturesource == NULL)
65                 return;
66         
67         imgname += "^[forcesingle";
68         
69         inventory_texture = g_texturesource->getTextureRaw(imgname);
70 }
71
72 void ContentFeatures::setInventoryTextureCube(std::string top,
73                 std::string left, std::string right)
74 {
75         if(g_texturesource == NULL)
76                 return;
77         
78         str_replace_char(top, '^', '&');
79         str_replace_char(left, '^', '&');
80         str_replace_char(right, '^', '&');
81
82         std::string imgname_full;
83         imgname_full += "[inventorycube{";
84         imgname_full += top;
85         imgname_full += "{";
86         imgname_full += left;
87         imgname_full += "{";
88         imgname_full += right;
89         inventory_texture = g_texturesource->getTextureRaw(imgname_full);
90 }
91 #endif
92
93 struct ContentFeatures g_content_features[MAX_CONTENT+1];
94
95 ContentFeatures & content_features(content_t i)
96 {
97         return g_content_features[i];
98 }
99 ContentFeatures & content_features(MapNode &n)
100 {
101         return content_features(n.getContent());
102 }
103
104 /*
105         See mapnode.h for description.
106 */
107 void init_mapnode()
108 {
109         if(g_texturesource == NULL)
110         {
111                 dstream<<"INFO: Initial run of init_mapnode with "
112                                 "g_texturesource=NULL. If this segfaults, "
113                                 "there is a bug with something not checking for "
114                                 "the NULL value."<<std::endl;
115         }
116         else
117         {
118                 dstream<<"INFO: Full run of init_mapnode with "
119                                 "g_texturesource!=NULL"<<std::endl;
120         }
121
122         /*// Read some settings
123         bool new_style_water = g_settings.getBool("new_style_water");
124         bool new_style_leaves = g_settings.getBool("new_style_leaves");*/
125
126         /*
127                 Initialize content feature table
128         */
129
130 #ifndef SERVER
131         /*
132                 Set initial material type to same in all tiles, so that the
133                 same material can be used in more stuff.
134                 This is set according to the leaves because they are the only
135                 differing material to which all materials can be changed to
136                 get this optimization.
137         */
138         u8 initial_material_type = MATERIAL_ALPHA_SIMPLE;
139         /*if(new_style_leaves)
140                 initial_material_type = MATERIAL_ALPHA_SIMPLE;
141         else
142                 initial_material_type = MATERIAL_ALPHA_NONE;*/
143         for(u16 i=0; i<MAX_CONTENT+1; i++)
144         {
145                 ContentFeatures *f = &g_content_features[i];
146                 // Re-initialize
147                 f->reset();
148
149                 for(u16 j=0; j<6; j++)
150                         f->tiles[j].material_type = initial_material_type;
151         }
152 #endif
153
154         /*
155                 Initially set every block to be shown as an unknown block.
156                 Don't touch CONTENT_IGNORE or CONTENT_AIR.
157         */
158         for(u16 i=0; i<MAX_CONTENT+1; i++)
159         {
160                 if(i == CONTENT_IGNORE || i == CONTENT_AIR)
161                         continue;
162                 ContentFeatures *f = &g_content_features[i];
163                 f->setAllTextures("unknown_block.png");
164                 f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
165         }
166
167         /*
168                 Initialize mapnode content
169         */
170         content_mapnode_init();
171         
172 }
173
174 v3s16 facedir_rotate(u8 facedir, v3s16 dir)
175 {
176         /*
177                 Face 2 (normally Z-) direction:
178                 facedir=0: Z-
179                 facedir=1: X-
180                 facedir=2: Z+
181                 facedir=3: X+
182         */
183         v3s16 newdir;
184         if(facedir==0) // Same
185                 newdir = v3s16(dir.X, dir.Y, dir.Z);
186         else if(facedir == 1) // Face is taken from rotXZccv(-90)
187                 newdir = v3s16(-dir.Z, dir.Y, dir.X);
188         else if(facedir == 2) // Face is taken from rotXZccv(180)
189                 newdir = v3s16(-dir.X, dir.Y, -dir.Z);
190         else if(facedir == 3) // Face is taken from rotXZccv(90)
191                 newdir = v3s16(dir.Z, dir.Y, -dir.X);
192         else
193                 newdir = dir;
194         return newdir;
195 }
196
197 #ifndef SERVER
198 TileSpec MapNode::getTile(v3s16 dir)
199 {
200         if(content_features(*this).param_type == CPT_FACEDIR_SIMPLE)
201                 dir = facedir_rotate(param1, dir);
202         
203         TileSpec spec;
204         
205         s32 dir_i = -1;
206         
207         if(dir == v3s16(0,0,0))
208                 dir_i = -1;
209         else if(dir == v3s16(0,1,0))
210                 dir_i = 0;
211         else if(dir == v3s16(0,-1,0))
212                 dir_i = 1;
213         else if(dir == v3s16(1,0,0))
214                 dir_i = 2;
215         else if(dir == v3s16(-1,0,0))
216                 dir_i = 3;
217         else if(dir == v3s16(0,0,1))
218                 dir_i = 4;
219         else if(dir == v3s16(0,0,-1))
220                 dir_i = 5;
221         
222         if(dir_i == -1)
223                 // Non-directional
224                 spec = content_features(*this).tiles[0];
225         else 
226                 spec = content_features(*this).tiles[dir_i];
227         
228         /*
229                 If it contains some mineral, change texture id
230         */
231         if(content_features(*this).param_type == CPT_MINERAL && g_texturesource)
232         {
233                 u8 mineral = getMineral();
234                 std::string mineral_texture_name = mineral_block_texture(mineral);
235                 if(mineral_texture_name != "")
236                 {
237                         u32 orig_id = spec.texture.id;
238                         std::string texture_name = g_texturesource->getTextureName(orig_id);
239                         //texture_name += "^blit:";
240                         texture_name += "^";
241                         texture_name += mineral_texture_name;
242                         u32 new_id = g_texturesource->getTextureId(texture_name);
243                         spec.texture = g_texturesource->getTexture(new_id);
244                 }
245         }
246
247         return spec;
248 }
249 #endif
250
251 u8 MapNode::getMineral()
252 {
253         if(content_features(*this).param_type == CPT_MINERAL)
254         {
255                 return param1 & 0x0f;
256         }
257
258         return MINERAL_NONE;
259 }
260
261 u32 MapNode::serializedLength(u8 version)
262 {
263         if(!ser_ver_supported(version))
264                 throw VersionMismatchException("ERROR: MapNode format not supported");
265                 
266         if(version == 0)
267                 return 1;
268         else if(version <= 9)
269                 return 2;
270         else
271                 return 3;
272 }
273 void MapNode::serialize(u8 *dest, u8 version)
274 {
275         if(!ser_ver_supported(version))
276                 throw VersionMismatchException("ERROR: MapNode format not supported");
277                 
278         // Translate to wanted version
279         MapNode n_foreign = mapnode_translate_from_internal(*this, version);
280
281         u8 actual_param0 = n_foreign.param0;
282
283         // Convert special values from new version to old
284         if(version <= 18)
285         {
286                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
287                 // are 255 and 254
288                 if(actual_param0 == CONTENT_IGNORE)
289                         actual_param0 = 255;
290                 else if(actual_param0 == CONTENT_AIR)
291                         actual_param0 = 254;
292         }
293
294         if(version == 0)
295         {
296                 dest[0] = actual_param0;
297         }
298         else if(version <= 9)
299         {
300                 dest[0] = actual_param0;
301                 dest[1] = n_foreign.param1;
302         }
303         else
304         {
305                 dest[0] = actual_param0;
306                 dest[1] = n_foreign.param1;
307                 dest[2] = n_foreign.param2;
308         }
309 }
310 void MapNode::deSerialize(u8 *source, u8 version)
311 {
312         if(!ser_ver_supported(version))
313                 throw VersionMismatchException("ERROR: MapNode format not supported");
314                 
315         if(version == 0)
316         {
317                 param0 = source[0];
318         }
319         else if(version == 1)
320         {
321                 param0 = source[0];
322                 // This version doesn't support saved lighting
323                 if(light_propagates() || light_source() > 0)
324                         param1 = 0;
325                 else
326                         param1 = source[1];
327         }
328         else if(version <= 9)
329         {
330                 param0 = source[0];
331                 param1 = source[1];
332         }
333         else
334         {
335                 param0 = source[0];
336                 param1 = source[1];
337                 param2 = source[2];
338         }
339         
340         // Convert special values from old version to new
341         if(version <= 18)
342         {
343                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
344                 // are 255 and 254
345                 if(param0 == 255)
346                         param0 = CONTENT_IGNORE;
347                 else if(param0 == 254)
348                         param0 = CONTENT_AIR;
349         }
350         // version 19 is fucked up with sometimes the old values and sometimes not
351         if(version == 19)
352         {
353                 if(param0 == 255)
354                         param0 = CONTENT_IGNORE;
355                 else if(param0 == 254)
356                         param0 = CONTENT_AIR;
357         }
358
359         // Translate to our known version
360         *this = mapnode_translate_to_internal(*this, version);
361 }
362
363 /*
364         Gets lighting value at face of node
365         
366         Parameters must consist of air and !air.
367         Order doesn't matter.
368
369         If either of the nodes doesn't exist, light is 0.
370         
371         parameters:
372                 daynight_ratio: 0...1000
373                 n: getNodeParent(p)
374                 n2: getNodeParent(p + face_dir)
375                 face_dir: axis oriented unit vector from p to p2
376         
377         returns encoded light value.
378 */
379 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
380                 v3s16 face_dir)
381 {
382         try{
383                 u8 light;
384                 u8 l1 = n.getLightBlend(daynight_ratio);
385                 u8 l2 = n2.getLightBlend(daynight_ratio);
386                 if(l1 > l2)
387                         light = l1;
388                 else
389                         light = l2;
390
391                 // Make some nice difference to different sides
392
393                 // This makes light come from a corner
394                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
395                         light = diminish_light(diminish_light(light));
396                 else if(face_dir.X == -1 || face_dir.Z == -1)
397                         light = diminish_light(light);*/
398                 
399                 // All neighboring faces have different shade (like in minecraft)
400                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
401                         light = diminish_light(diminish_light(light));
402                 else if(face_dir.Z == 1 || face_dir.Z == -1)
403                         light = diminish_light(light);
404
405                 return light;
406         }
407         catch(InvalidPositionException &e)
408         {
409                 return 0;
410         }
411 }
412
413