Improve glass
[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 #include "porting.h"
23 #include <string>
24 #include "mineral.h"
25 #include "main.h" // For g_settings
26 #include "nodedef.h"
27 #include "content_mapnode.h" // For mapnode_translate_*_internal
28 #include "serialization.h" // For ser_ver_supported
29
30 #ifndef SERVER
31 /*
32         Nodes make a face if contents differ and solidness differs.
33         Return value:
34                 0: No face
35                 1: Face uses m1's content
36                 2: Face uses m2's content
37         equivalent: Whether the blocks share the same face (eg. water and glass)
38
39         TODO: Add 3: Both faces drawn with backface culling, remove equivalent
40 */
41 u8 face_contents(content_t m1, content_t m2, bool *equivalent,
42                 INodeDefManager *nodemgr)
43 {
44         *equivalent = false;
45
46         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
47                 return 0;
48         
49         bool contents_differ = (m1 != m2);
50         
51         const ContentFeatures &f1 = nodemgr->get(m1);
52         const ContentFeatures &f2 = nodemgr->get(m2);
53
54         // Contents don't differ for different forms of same liquid
55         if(f1.sameLiquid(f2))
56                 contents_differ = false;
57         
58         u8 c1 = f1.solidness;
59         u8 c2 = f2.solidness;
60
61         bool solidness_differs = (c1 != c2);
62         bool makes_face = contents_differ && solidness_differs;
63
64         if(makes_face == false)
65                 return 0;
66         
67         if(c1 == 0)
68                 c1 = f1.visual_solidness;
69         if(c2 == 0)
70                 c2 = f2.visual_solidness;
71         
72         if(c1 == c2){
73                 *equivalent = true;
74                 // If same solidness, liquid takes precense
75                 if(f1.isLiquid())
76                         return 1;
77                 if(f2.isLiquid())
78                         return 2;
79         }
80         
81         if(c1 > c2)
82                 return 1;
83         else
84                 return 2;
85 }
86 #endif
87
88 v3s16 facedir_rotate(u8 facedir, v3s16 dir)
89 {
90         /*
91                 Face 2 (normally Z-) direction:
92                 facedir=0: Z-
93                 facedir=1: X-
94                 facedir=2: Z+
95                 facedir=3: X+
96         */
97         v3s16 newdir;
98         if(facedir==0) // Same
99                 newdir = v3s16(dir.X, dir.Y, dir.Z);
100         else if(facedir == 1) // Face is taken from rotXZccv(-90)
101                 newdir = v3s16(-dir.Z, dir.Y, dir.X);
102         else if(facedir == 2) // Face is taken from rotXZccv(180)
103                 newdir = v3s16(-dir.X, dir.Y, -dir.Z);
104         else if(facedir == 3) // Face is taken from rotXZccv(90)
105                 newdir = v3s16(dir.Z, dir.Y, -dir.X);
106         else
107                 newdir = dir;
108         return newdir;
109 }
110
111 u8 packDir(v3s16 dir)
112 {
113         u8 b = 0;
114
115         if(dir.X > 0)
116                 b |= (1<<0);
117         else if(dir.X < 0)
118                 b |= (1<<1);
119
120         if(dir.Y > 0)
121                 b |= (1<<2);
122         else if(dir.Y < 0)
123                 b |= (1<<3);
124
125         if(dir.Z > 0)
126                 b |= (1<<4);
127         else if(dir.Z < 0)
128                 b |= (1<<5);
129         
130         return b;
131 }
132 v3s16 unpackDir(u8 b)
133 {
134         v3s16 d(0,0,0);
135
136         if(b & (1<<0))
137                 d.X = 1;
138         else if(b & (1<<1))
139                 d.X = -1;
140
141         if(b & (1<<2))
142                 d.Y = 1;
143         else if(b & (1<<3))
144                 d.Y = -1;
145
146         if(b & (1<<4))
147                 d.Z = 1;
148         else if(b & (1<<5))
149                 d.Z = -1;
150         
151         return d;
152 }
153
154 /*
155         MapNode
156 */
157
158 void MapNode::setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr)
159 {
160         // If node doesn't contain light data, ignore this
161         if(nodemgr->get(*this).param_type != CPT_LIGHT)
162                 return;
163         if(bank == LIGHTBANK_DAY)
164         {
165                 param1 &= 0xf0;
166                 param1 |= a_light & 0x0f;
167         }
168         else if(bank == LIGHTBANK_NIGHT)
169         {
170                 param1 &= 0x0f;
171                 param1 |= (a_light & 0x0f)<<4;
172         }
173         else
174                 assert(0);
175 }
176
177 u8 MapNode::getLight(enum LightBank bank, INodeDefManager *nodemgr) const
178 {
179         // Select the brightest of [light source, propagated light]
180         u8 light = 0;
181         if(nodemgr->get(*this).param_type == CPT_LIGHT)
182         {
183                 if(bank == LIGHTBANK_DAY)
184                         light = param1 & 0x0f;
185                 else if(bank == LIGHTBANK_NIGHT)
186                         light = (param1>>4)&0x0f;
187                 else
188                         assert(0);
189         }
190         if(nodemgr->get(*this).light_source > light)
191                 light = nodemgr->get(*this).light_source;
192         return light;
193 }
194
195 u8 MapNode::getLightBanksWithSource(INodeDefManager *nodemgr) const
196 {
197         // Select the brightest of [light source, propagated light]
198         u8 lightday = 0;
199         u8 lightnight = 0;
200         if(nodemgr->get(*this).param_type == CPT_LIGHT)
201         {
202                 lightday = param1 & 0x0f;
203                 lightnight = (param1>>4)&0x0f;
204         }
205         if(nodemgr->get(*this).light_source > lightday)
206                 lightday = nodemgr->get(*this).light_source;
207         if(nodemgr->get(*this).light_source > lightnight)
208                 lightnight = nodemgr->get(*this).light_source;
209         return (lightday&0x0f) | ((lightnight<<4)&0xf0);
210 }
211
212 u8 MapNode::getMineral(INodeDefManager *nodemgr) const
213 {
214         if(nodemgr->get(*this).param_type == CPT_MINERAL)
215         {
216                 return param1 & 0x0f;
217         }
218
219         return MINERAL_NONE;
220 }
221
222 u32 MapNode::serializedLength(u8 version)
223 {
224         if(!ser_ver_supported(version))
225                 throw VersionMismatchException("ERROR: MapNode format not supported");
226                 
227         if(version == 0)
228                 return 1;
229         else if(version <= 9)
230                 return 2;
231         else
232                 return 3;
233 }
234 void MapNode::serialize(u8 *dest, u8 version)
235 {
236         if(!ser_ver_supported(version))
237                 throw VersionMismatchException("ERROR: MapNode format not supported");
238                 
239         // Translate to wanted version
240         MapNode n_foreign = mapnode_translate_from_internal(*this, version);
241
242         u8 actual_param0 = n_foreign.param0;
243
244         // Convert special values from new version to old
245         if(version <= 18)
246         {
247                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
248                 // are 255 and 254
249                 if(actual_param0 == CONTENT_IGNORE)
250                         actual_param0 = 255;
251                 else if(actual_param0 == CONTENT_AIR)
252                         actual_param0 = 254;
253         }
254
255         if(version == 0)
256         {
257                 dest[0] = actual_param0;
258         }
259         else if(version <= 9)
260         {
261                 dest[0] = actual_param0;
262                 dest[1] = n_foreign.param1;
263         }
264         else
265         {
266                 dest[0] = actual_param0;
267                 dest[1] = n_foreign.param1;
268                 dest[2] = n_foreign.param2;
269         }
270 }
271 void MapNode::deSerialize(u8 *source, u8 version, INodeDefManager *nodemgr)
272 {
273         if(!ser_ver_supported(version))
274                 throw VersionMismatchException("ERROR: MapNode format not supported");
275                 
276         if(version == 0)
277         {
278                 param0 = source[0];
279         }
280         else if(version == 1)
281         {
282                 param0 = source[0];
283                 // This version doesn't support saved lighting
284                 if(nodemgr->get(*this).light_propagates || nodemgr->get(*this).light_source > 0)
285                         param1 = 0;
286                 else
287                         param1 = source[1];
288         }
289         else if(version <= 9)
290         {
291                 param0 = source[0];
292                 param1 = source[1];
293         }
294         else
295         {
296                 param0 = source[0];
297                 param1 = source[1];
298                 param2 = source[2];
299         }
300         
301         // Convert special values from old version to new
302         if(version <= 18)
303         {
304                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
305                 // are 255 and 254
306                 if(param0 == 255)
307                         param0 = CONTENT_IGNORE;
308                 else if(param0 == 254)
309                         param0 = CONTENT_AIR;
310         }
311         // version 19 is fucked up with sometimes the old values and sometimes not
312         if(version == 19)
313         {
314                 if(param0 == 255)
315                         param0 = CONTENT_IGNORE;
316                 else if(param0 == 254)
317                         param0 = CONTENT_AIR;
318         }
319
320         // Translate to our known version
321         *this = mapnode_translate_to_internal(*this, version);
322 }
323
324 /*
325         Gets lighting value at face of node
326         
327         Parameters must consist of air and !air.
328         Order doesn't matter.
329
330         If either of the nodes doesn't exist, light is 0.
331         
332         parameters:
333                 daynight_ratio: 0...1000
334                 n: getNode(p) (uses only the lighting value)
335                 n2: getNode(p + face_dir) (uses only the lighting value)
336                 face_dir: axis oriented unit vector from p to p2
337         
338         returns encoded light value.
339 */
340 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
341                 v3s16 face_dir, INodeDefManager *nodemgr)
342 {
343         try{
344                 u8 light;
345                 u8 l1 = n.getLightBlend(daynight_ratio, nodemgr);
346                 u8 l2 = n2.getLightBlend(daynight_ratio, nodemgr);
347                 if(l1 > l2)
348                         light = l1;
349                 else
350                         light = l2;
351
352                 // Make some nice difference to different sides
353
354                 // This makes light come from a corner
355                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
356                         light = diminish_light(diminish_light(light));
357                 else if(face_dir.X == -1 || face_dir.Z == -1)
358                         light = diminish_light(light);*/
359                 
360                 // All neighboring faces have different shade (like in minecraft)
361                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
362                         light = diminish_light(diminish_light(light));
363                 else if(face_dir.Z == 1 || face_dir.Z == -1)
364                         light = diminish_light(light);
365
366                 return light;
367         }
368         catch(InvalidPositionException &e)
369         {
370                 return 0;
371         }
372 }
373
374