Fix water-glass and water-lava surfaces
[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         // Make CONTENT_IGNORE to not block the view when occlusion culling
168         content_features(CONTENT_IGNORE).solidness = 0;
169
170         /*
171                 Initialize mapnode content
172         */
173         content_mapnode_init();
174         
175 }
176
177 /*
178         Nodes make a face if contents differ and solidness differs.
179         Return value:
180                 0: No face
181                 1: Face uses m1's content
182                 2: Face uses m2's content
183         equivalent: Whether the blocks share the same face (eg. water and glass)
184 */
185 u8 face_contents(content_t m1, content_t m2, bool *equivalent)
186 {
187         *equivalent = false;
188
189         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
190                 return 0;
191         
192         bool contents_differ = (m1 != m2);
193         
194         // Contents don't differ for different forms of same liquid
195         if(content_liquid(m1) && content_liquid(m2)
196                         && make_liquid_flowing(m1) == make_liquid_flowing(m2))
197                 contents_differ = false;
198         
199         u8 c1 = content_solidness(m1);
200         u8 c2 = content_solidness(m2);
201
202         bool solidness_differs = (c1 != c2);
203         bool makes_face = contents_differ && solidness_differs;
204
205         if(makes_face == false)
206                 return 0;
207         
208         if(c1 == 0)
209                 c1 = content_features(m1).visual_solidness;
210         if(c2 == 0)
211                 c2 = content_features(m2).visual_solidness;
212         
213         if(c1 == c2){
214                 *equivalent = true;
215                 // If same solidness, liquid takes precense
216                 if(content_features(m1).liquid_type != LIQUID_NONE)
217                         return 1;
218                 if(content_features(m2).liquid_type != LIQUID_NONE)
219                         return 2;
220         }
221         
222         if(c1 > c2)
223                 return 1;
224         else
225                 return 2;
226 }
227
228 v3s16 facedir_rotate(u8 facedir, v3s16 dir)
229 {
230         /*
231                 Face 2 (normally Z-) direction:
232                 facedir=0: Z-
233                 facedir=1: X-
234                 facedir=2: Z+
235                 facedir=3: X+
236         */
237         v3s16 newdir;
238         if(facedir==0) // Same
239                 newdir = v3s16(dir.X, dir.Y, dir.Z);
240         else if(facedir == 1) // Face is taken from rotXZccv(-90)
241                 newdir = v3s16(-dir.Z, dir.Y, dir.X);
242         else if(facedir == 2) // Face is taken from rotXZccv(180)
243                 newdir = v3s16(-dir.X, dir.Y, -dir.Z);
244         else if(facedir == 3) // Face is taken from rotXZccv(90)
245                 newdir = v3s16(dir.Z, dir.Y, -dir.X);
246         else
247                 newdir = dir;
248         return newdir;
249 }
250
251 #ifndef SERVER
252 TileSpec MapNode::getTile(v3s16 dir)
253 {
254         if(content_features(*this).param_type == CPT_FACEDIR_SIMPLE)
255                 dir = facedir_rotate(param1, dir);
256         
257         TileSpec spec;
258         
259         s32 dir_i = -1;
260         
261         if(dir == v3s16(0,0,0))
262                 dir_i = -1;
263         else if(dir == v3s16(0,1,0))
264                 dir_i = 0;
265         else if(dir == v3s16(0,-1,0))
266                 dir_i = 1;
267         else if(dir == v3s16(1,0,0))
268                 dir_i = 2;
269         else if(dir == v3s16(-1,0,0))
270                 dir_i = 3;
271         else if(dir == v3s16(0,0,1))
272                 dir_i = 4;
273         else if(dir == v3s16(0,0,-1))
274                 dir_i = 5;
275         
276         if(dir_i == -1)
277                 // Non-directional
278                 spec = content_features(*this).tiles[0];
279         else 
280                 spec = content_features(*this).tiles[dir_i];
281         
282         /*
283                 If it contains some mineral, change texture id
284         */
285         if(content_features(*this).param_type == CPT_MINERAL && g_texturesource)
286         {
287                 u8 mineral = getMineral();
288                 std::string mineral_texture_name = mineral_block_texture(mineral);
289                 if(mineral_texture_name != "")
290                 {
291                         u32 orig_id = spec.texture.id;
292                         std::string texture_name = g_texturesource->getTextureName(orig_id);
293                         //texture_name += "^blit:";
294                         texture_name += "^";
295                         texture_name += mineral_texture_name;
296                         u32 new_id = g_texturesource->getTextureId(texture_name);
297                         spec.texture = g_texturesource->getTexture(new_id);
298                 }
299         }
300
301         return spec;
302 }
303 #endif
304
305 u8 MapNode::getMineral()
306 {
307         if(content_features(*this).param_type == CPT_MINERAL)
308         {
309                 return param1 & 0x0f;
310         }
311
312         return MINERAL_NONE;
313 }
314
315 u32 MapNode::serializedLength(u8 version)
316 {
317         if(!ser_ver_supported(version))
318                 throw VersionMismatchException("ERROR: MapNode format not supported");
319                 
320         if(version == 0)
321                 return 1;
322         else if(version <= 9)
323                 return 2;
324         else
325                 return 3;
326 }
327 void MapNode::serialize(u8 *dest, u8 version)
328 {
329         if(!ser_ver_supported(version))
330                 throw VersionMismatchException("ERROR: MapNode format not supported");
331                 
332         // Translate to wanted version
333         MapNode n_foreign = mapnode_translate_from_internal(*this, version);
334
335         u8 actual_param0 = n_foreign.param0;
336
337         // Convert special values from new version to old
338         if(version <= 18)
339         {
340                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
341                 // are 255 and 254
342                 if(actual_param0 == CONTENT_IGNORE)
343                         actual_param0 = 255;
344                 else if(actual_param0 == CONTENT_AIR)
345                         actual_param0 = 254;
346         }
347
348         if(version == 0)
349         {
350                 dest[0] = actual_param0;
351         }
352         else if(version <= 9)
353         {
354                 dest[0] = actual_param0;
355                 dest[1] = n_foreign.param1;
356         }
357         else
358         {
359                 dest[0] = actual_param0;
360                 dest[1] = n_foreign.param1;
361                 dest[2] = n_foreign.param2;
362         }
363 }
364 void MapNode::deSerialize(u8 *source, u8 version)
365 {
366         if(!ser_ver_supported(version))
367                 throw VersionMismatchException("ERROR: MapNode format not supported");
368                 
369         if(version == 0)
370         {
371                 param0 = source[0];
372         }
373         else if(version == 1)
374         {
375                 param0 = source[0];
376                 // This version doesn't support saved lighting
377                 if(light_propagates() || light_source() > 0)
378                         param1 = 0;
379                 else
380                         param1 = source[1];
381         }
382         else if(version <= 9)
383         {
384                 param0 = source[0];
385                 param1 = source[1];
386         }
387         else
388         {
389                 param0 = source[0];
390                 param1 = source[1];
391                 param2 = source[2];
392         }
393         
394         // Convert special values from old version to new
395         if(version <= 18)
396         {
397                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
398                 // are 255 and 254
399                 if(param0 == 255)
400                         param0 = CONTENT_IGNORE;
401                 else if(param0 == 254)
402                         param0 = CONTENT_AIR;
403         }
404         // version 19 is fucked up with sometimes the old values and sometimes not
405         if(version == 19)
406         {
407                 if(param0 == 255)
408                         param0 = CONTENT_IGNORE;
409                 else if(param0 == 254)
410                         param0 = CONTENT_AIR;
411         }
412
413         // Translate to our known version
414         *this = mapnode_translate_to_internal(*this, version);
415 }
416
417 /*
418         Gets lighting value at face of node
419         
420         Parameters must consist of air and !air.
421         Order doesn't matter.
422
423         If either of the nodes doesn't exist, light is 0.
424         
425         parameters:
426                 daynight_ratio: 0...1000
427                 n: getNodeParent(p)
428                 n2: getNodeParent(p + face_dir)
429                 face_dir: axis oriented unit vector from p to p2
430         
431         returns encoded light value.
432 */
433 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
434                 v3s16 face_dir)
435 {
436         try{
437                 u8 light;
438                 u8 l1 = n.getLightBlend(daynight_ratio);
439                 u8 l2 = n2.getLightBlend(daynight_ratio);
440                 if(l1 > l2)
441                         light = l1;
442                 else
443                         light = l2;
444
445                 // Make some nice difference to different sides
446
447                 // This makes light come from a corner
448                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
449                         light = diminish_light(diminish_light(light));
450                 else if(face_dir.X == -1 || face_dir.Z == -1)
451                         light = diminish_light(light);*/
452                 
453                 // All neighboring faces have different shade (like in minecraft)
454                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
455                         light = diminish_light(diminish_light(light));
456                 else if(face_dir.Z == 1 || face_dir.Z == -1)
457                         light = diminish_light(light);
458
459                 return light;
460         }
461         catch(InvalidPositionException &e)
462         {
463                 return 0;
464         }
465 }
466
467