utility.h: Change Buffer's interface to be more compatible with SharedBuffer's interf...
[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 v3s16 facedir_rotate(u8 facedir, v3s16 dir)
178 {
179         /*
180                 Face 2 (normally Z-) direction:
181                 facedir=0: Z-
182                 facedir=1: X-
183                 facedir=2: Z+
184                 facedir=3: X+
185         */
186         v3s16 newdir;
187         if(facedir==0) // Same
188                 newdir = v3s16(dir.X, dir.Y, dir.Z);
189         else if(facedir == 1) // Face is taken from rotXZccv(-90)
190                 newdir = v3s16(-dir.Z, dir.Y, dir.X);
191         else if(facedir == 2) // Face is taken from rotXZccv(180)
192                 newdir = v3s16(-dir.X, dir.Y, -dir.Z);
193         else if(facedir == 3) // Face is taken from rotXZccv(90)
194                 newdir = v3s16(dir.Z, dir.Y, -dir.X);
195         else
196                 newdir = dir;
197         return newdir;
198 }
199
200 #ifndef SERVER
201 TileSpec MapNode::getTile(v3s16 dir)
202 {
203         if(content_features(*this).param_type == CPT_FACEDIR_SIMPLE)
204                 dir = facedir_rotate(param1, dir);
205         
206         TileSpec spec;
207         
208         s32 dir_i = -1;
209         
210         if(dir == v3s16(0,0,0))
211                 dir_i = -1;
212         else if(dir == v3s16(0,1,0))
213                 dir_i = 0;
214         else if(dir == v3s16(0,-1,0))
215                 dir_i = 1;
216         else if(dir == v3s16(1,0,0))
217                 dir_i = 2;
218         else if(dir == v3s16(-1,0,0))
219                 dir_i = 3;
220         else if(dir == v3s16(0,0,1))
221                 dir_i = 4;
222         else if(dir == v3s16(0,0,-1))
223                 dir_i = 5;
224         
225         if(dir_i == -1)
226                 // Non-directional
227                 spec = content_features(*this).tiles[0];
228         else 
229                 spec = content_features(*this).tiles[dir_i];
230         
231         /*
232                 If it contains some mineral, change texture id
233         */
234         if(content_features(*this).param_type == CPT_MINERAL && g_texturesource)
235         {
236                 u8 mineral = getMineral();
237                 std::string mineral_texture_name = mineral_block_texture(mineral);
238                 if(mineral_texture_name != "")
239                 {
240                         u32 orig_id = spec.texture.id;
241                         std::string texture_name = g_texturesource->getTextureName(orig_id);
242                         //texture_name += "^blit:";
243                         texture_name += "^";
244                         texture_name += mineral_texture_name;
245                         u32 new_id = g_texturesource->getTextureId(texture_name);
246                         spec.texture = g_texturesource->getTexture(new_id);
247                 }
248         }
249
250         return spec;
251 }
252 #endif
253
254 u8 MapNode::getMineral()
255 {
256         if(content_features(*this).param_type == CPT_MINERAL)
257         {
258                 return param1 & 0x0f;
259         }
260
261         return MINERAL_NONE;
262 }
263
264 u32 MapNode::serializedLength(u8 version)
265 {
266         if(!ser_ver_supported(version))
267                 throw VersionMismatchException("ERROR: MapNode format not supported");
268                 
269         if(version == 0)
270                 return 1;
271         else if(version <= 9)
272                 return 2;
273         else
274                 return 3;
275 }
276 void MapNode::serialize(u8 *dest, u8 version)
277 {
278         if(!ser_ver_supported(version))
279                 throw VersionMismatchException("ERROR: MapNode format not supported");
280                 
281         // Translate to wanted version
282         MapNode n_foreign = mapnode_translate_from_internal(*this, version);
283
284         u8 actual_param0 = n_foreign.param0;
285
286         // Convert special values from new version to old
287         if(version <= 18)
288         {
289                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
290                 // are 255 and 254
291                 if(actual_param0 == CONTENT_IGNORE)
292                         actual_param0 = 255;
293                 else if(actual_param0 == CONTENT_AIR)
294                         actual_param0 = 254;
295         }
296
297         if(version == 0)
298         {
299                 dest[0] = actual_param0;
300         }
301         else if(version <= 9)
302         {
303                 dest[0] = actual_param0;
304                 dest[1] = n_foreign.param1;
305         }
306         else
307         {
308                 dest[0] = actual_param0;
309                 dest[1] = n_foreign.param1;
310                 dest[2] = n_foreign.param2;
311         }
312 }
313 void MapNode::deSerialize(u8 *source, u8 version)
314 {
315         if(!ser_ver_supported(version))
316                 throw VersionMismatchException("ERROR: MapNode format not supported");
317                 
318         if(version == 0)
319         {
320                 param0 = source[0];
321         }
322         else if(version == 1)
323         {
324                 param0 = source[0];
325                 // This version doesn't support saved lighting
326                 if(light_propagates() || light_source() > 0)
327                         param1 = 0;
328                 else
329                         param1 = source[1];
330         }
331         else if(version <= 9)
332         {
333                 param0 = source[0];
334                 param1 = source[1];
335         }
336         else
337         {
338                 param0 = source[0];
339                 param1 = source[1];
340                 param2 = source[2];
341         }
342         
343         // Convert special values from old version to new
344         if(version <= 18)
345         {
346                 // In these versions, CONTENT_IGNORE and CONTENT_AIR
347                 // are 255 and 254
348                 if(param0 == 255)
349                         param0 = CONTENT_IGNORE;
350                 else if(param0 == 254)
351                         param0 = CONTENT_AIR;
352         }
353         // version 19 is fucked up with sometimes the old values and sometimes not
354         if(version == 19)
355         {
356                 if(param0 == 255)
357                         param0 = CONTENT_IGNORE;
358                 else if(param0 == 254)
359                         param0 = CONTENT_AIR;
360         }
361
362         // Translate to our known version
363         *this = mapnode_translate_to_internal(*this, version);
364 }
365
366 /*
367         Gets lighting value at face of node
368         
369         Parameters must consist of air and !air.
370         Order doesn't matter.
371
372         If either of the nodes doesn't exist, light is 0.
373         
374         parameters:
375                 daynight_ratio: 0...1000
376                 n: getNodeParent(p)
377                 n2: getNodeParent(p + face_dir)
378                 face_dir: axis oriented unit vector from p to p2
379         
380         returns encoded light value.
381 */
382 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
383                 v3s16 face_dir)
384 {
385         try{
386                 u8 light;
387                 u8 l1 = n.getLightBlend(daynight_ratio);
388                 u8 l2 = n2.getLightBlend(daynight_ratio);
389                 if(l1 > l2)
390                         light = l1;
391                 else
392                         light = l2;
393
394                 // Make some nice difference to different sides
395
396                 // This makes light come from a corner
397                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
398                         light = diminish_light(diminish_light(light));
399                 else if(face_dir.X == -1 || face_dir.Z == -1)
400                         light = diminish_light(light);*/
401                 
402                 // All neighboring faces have different shade (like in minecraft)
403                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
404                         light = diminish_light(diminish_light(light));
405                 else if(face_dir.Z == 1 || face_dir.Z == -1)
406                         light = diminish_light(light);
407
408                 return light;
409         }
410         catch(InvalidPositionException &e)
411         {
412                 return 0;
413         }
414 }
415
416