]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapnode.h
work-in-progress texture atlas optimization
[dragonfireclient.git] / src / mapnode.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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 #ifndef MAPNODE_HEADER
21 #define MAPNODE_HEADER
22
23 #include <iostream>
24 #include "common_irrlicht.h"
25 #include "light.h"
26 #include "utility.h"
27 #include "exceptions.h"
28 #include "serialization.h"
29 #include "tile.h"
30 #include "iirrlichtwrapper.h"
31
32 /*
33         Initializes all kind of stuff in here.
34         Many things depend on this.
35
36         irrlicht: Used for getting texture ids.
37 */
38 void init_mapnode(IIrrlichtWrapper *irrlicht);
39
40 // Initializes g_content_inventory_texture_paths
41 void init_content_inventory_texture_paths();
42
43
44 // NOTE: This is not used appropriately everywhere.
45 #define MATERIALS_COUNT 256
46
47 /*
48         Ignored node.
49
50         Anything that stores MapNodes doesn't have to preserve parameters
51         associated with this material.
52         
53         Doesn't create faces with anything and is considered being
54         out-of-map in the game map.
55 */
56 #define CONTENT_IGNORE 255
57 #define CONTENT_IGNORE_DEFAULT_PARAM 0
58
59 /*
60         The common material through which the player can walk and which
61         is transparent to light
62 */
63 #define CONTENT_AIR 254
64
65 /*
66         Suggested materials:
67         - Gravel
68         - Sand
69         
70         New naming scheme:
71         - Material = irrlicht's Material class
72         - Content = (u8) content of a node
73         - Tile = (u16) Material ID at some side of a node
74 */
75
76 #define CONTENT_STONE 0
77 #define CONTENT_GRASS 1
78 #define CONTENT_WATER 2
79 #define CONTENT_TORCH 3
80 #define CONTENT_TREE 4
81 #define CONTENT_LEAVES 5
82 #define CONTENT_GRASS_FOOTSTEPS 6
83 #define CONTENT_MESE 7
84 #define CONTENT_MUD 8
85 #define CONTENT_WATERSOURCE 9
86 // Pretty much useless, clouds won't be drawn this way
87 #define CONTENT_CLOUD 10
88 #define CONTENT_COALSTONE 11
89 #define CONTENT_WOOD 12
90 #define CONTENT_SAND 13
91
92 /*
93         This is used by all kinds of things to allocate memory for all
94         contents except CONTENT_AIR and CONTENT_IGNORE
95 */
96 #define USEFUL_CONTENT_COUNT 14
97
98 /*
99         Content feature list
100 */
101
102 enum ContentParamType
103 {
104         CPT_NONE,
105         CPT_LIGHT,
106         CPT_MINERAL
107 };
108
109 enum LiquidType
110 {
111         LIQUID_NONE,
112         LIQUID_FLOWING,
113         LIQUID_SOURCE
114 };
115
116 class MapNode;
117
118 struct ContentFeatures
119 {
120         // If non-NULL, content is translated to this when deserialized
121         MapNode *translate_to;
122
123         // Type of MapNode::param
124         ContentParamType param_type;
125
126         /*
127                 0: up
128                 1: down
129                 2: right
130                 3: left
131                 4: back
132                 5: front
133         */
134         TileSpec tiles[6];
135         
136         // TODO: Somehow specify inventory image
137         //std::string inventory_image_path;
138         //TextureSpec inventory_texture;
139         //u32 inventory_texture_id;
140
141         bool is_ground_content; //TODO: Remove, use walkable instead
142         bool light_propagates;
143         bool sunlight_propagates;
144         u8 solidness; // Used when choosing which face is drawn
145         bool walkable;
146         bool pointable;
147         bool diggable;
148         bool buildable_to;
149         enum LiquidType liquid_type;
150         bool wall_mounted; // If true, param2 is set to direction when placed
151
152         //TODO: Move more properties here
153
154         ContentFeatures()
155         {
156                 translate_to = NULL;
157                 param_type = CPT_NONE;
158                 is_ground_content = false;
159                 light_propagates = false;
160                 sunlight_propagates = false;
161                 solidness = 2;
162                 walkable = true;
163                 pointable = true;
164                 diggable = true;
165                 buildable_to = false;
166                 liquid_type = LIQUID_NONE;
167                 wall_mounted = false;
168         }
169
170         ~ContentFeatures();
171         
172         /*
173                 Quickhands for simple materials
174         */
175         
176         void setTexture(u16 i, std::string name, u8 alpha=255);
177
178         void setAllTextures(std::string name, u8 alpha=255)
179         {
180                 for(u16 i=0; i<6; i++)
181                 {
182                         setTexture(i, name, alpha);
183                 }
184         }
185
186         /*void setTexture(u16 i, AtlasPointer p, u8 alpha=255)
187         {
188                 tiles[i].texture = p;
189                 if(alpha != 255)
190                 {
191                         tiles[i].alpha = alpha;
192                         tiles[i].material_type = MATERIAL_ALPHA_VERTEX;
193                 }
194         }
195         void setAllTextures(AtlasPointer p, u8 alpha=255)
196         {
197                 for(u16 i=0; i<6; i++)
198                 {
199                         setTexture(i, p, alpha);
200                 }
201         }*/
202
203         void setTile(u16 i, const TileSpec &tile)
204         {
205                 tiles[i] = tile;
206         }
207         void setAllTiles(const TileSpec &tile)
208         {
209                 for(u16 i=0; i<6; i++)
210                 {
211                         setTile(i, tile);
212                 }
213         }
214
215         /*void setInventoryTexture(const TextureSpec &spec)
216         {
217                 inventory_texture = spec;
218         }*/
219
220         /*void setInventoryImage(std::string imgname)
221         {
222                 inventory_image_path = porting::getDataPath(imgname.c_str());
223         }*/
224 };
225
226 /*
227         Call this to access the ContentFeature list
228 */
229 ContentFeatures & content_features(u8 i);
230
231 /*
232         If true, the material allows light propagation and brightness is stored
233         in param.
234         NOTE: Don't use, use "content_features(m).whatever" instead
235 */
236 inline bool light_propagates_content(u8 m)
237 {
238         return content_features(m).light_propagates;
239         //return (m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
240 }
241
242 /*
243         If true, the material allows lossless sunlight propagation.
244         NOTE: It doesn't seem to go through torches regardlessly of this
245         NOTE: Don't use, use "content_features(m).whatever" instead
246 */
247 inline bool sunlight_propagates_content(u8 m)
248 {
249         return content_features(m).sunlight_propagates;
250         //return (m == CONTENT_AIR || m == CONTENT_TORCH);
251 }
252
253 /*
254         On a node-node surface, the material of the node with higher solidness
255         is used for drawing.
256         0: Invisible
257         1: Transparent
258         2: Opaque
259         NOTE: Don't use, use "content_features(m).whatever" instead
260 */
261 inline u8 content_solidness(u8 m)
262 {
263         return content_features(m).solidness;
264         /*// As of now, every pseudo node like torches are added to this
265         if(m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER)
266                 return 0;
267         if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
268                 return 1;
269         return 2;*/
270 }
271
272 // Objects collide with walkable contents
273 // NOTE: Don't use, use "content_features(m).whatever" instead
274 inline bool content_walkable(u8 m)
275 {
276         return content_features(m).walkable;
277         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE && m != CONTENT_TORCH);
278 }
279
280 // NOTE: Don't use, use "content_features(m).whatever" instead
281 inline bool content_liquid(u8 m)
282 {
283         return content_features(m).liquid_type != LIQUID_NONE;
284         //return (m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
285 }
286
287 // NOTE: Don't use, use "content_features(m).whatever" instead
288 inline bool content_flowing_liquid(u8 m)
289 {
290         return content_features(m).liquid_type == LIQUID_FLOWING;
291         //return (m == CONTENT_WATER);
292 }
293
294 // NOTE: Don't use, use "content_features(m).whatever" instead
295 inline bool content_liquid_source(u8 m)
296 {
297         return content_features(m).liquid_type == LIQUID_SOURCE;
298         //return (m == CONTENT_WATERSOURCE);
299 }
300
301 // CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER
302 // CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA
303 inline u8 make_liquid_flowing(u8 m)
304 {
305         if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
306                 return CONTENT_WATER;
307         assert(0);
308 }
309
310 // Pointable contents can be pointed to in the map
311 // NOTE: Don't use, use "content_features(m).whatever" instead
312 inline bool content_pointable(u8 m)
313 {
314         return content_features(m).pointable;
315         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
316 }
317
318 // NOTE: Don't use, use "content_features(m).whatever" instead
319 inline bool content_diggable(u8 m)
320 {
321         return content_features(m).diggable;
322         //return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
323 }
324
325 // NOTE: Don't use, use "content_features(m).whatever" instead
326 inline bool content_buildable_to(u8 m)
327 {
328         return content_features(m).buildable_to;
329         //return (m == CONTENT_AIR || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
330 }
331
332 /*
333         Returns true for contents that form the base ground that
334         follows the main heightmap
335 */
336 /*inline bool is_ground_content(u8 m)
337 {
338         return content_features(m).is_ground_content;
339 }*/
340
341 /*
342         Nodes make a face if contents differ and solidness differs.
343         Return value:
344                 0: No face
345                 1: Face uses m1's content
346                 2: Face uses m2's content
347 */
348 inline u8 face_contents(u8 m1, u8 m2)
349 {
350         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
351                 return 0;
352         
353         bool contents_differ = (m1 != m2);
354         
355         // Contents don't differ for different forms of same liquid
356         if(content_liquid(m1) && content_liquid(m2)
357                         && make_liquid_flowing(m1) == make_liquid_flowing(m2))
358                 contents_differ = false;
359         
360         bool solidness_differs = (content_solidness(m1) != content_solidness(m2));
361         bool makes_face = contents_differ && solidness_differs;
362
363         if(makes_face == false)
364                 return 0;
365
366         if(content_solidness(m1) > content_solidness(m2))
367                 return 1;
368         else
369                 return 2;
370 }
371
372 /*
373         Packs directions like (1,0,0), (1,-1,0)
374 */
375 inline u8 packDir(v3s16 dir)
376 {
377         u8 b = 0;
378
379         if(dir.X > 0)
380                 b |= (1<<0);
381         else if(dir.X < 0)
382                 b |= (1<<1);
383
384         if(dir.Y > 0)
385                 b |= (1<<2);
386         else if(dir.Y < 0)
387                 b |= (1<<3);
388
389         if(dir.Z > 0)
390                 b |= (1<<4);
391         else if(dir.Z < 0)
392                 b |= (1<<5);
393         
394         return b;
395 }
396 inline v3s16 unpackDir(u8 b)
397 {
398         v3s16 d(0,0,0);
399
400         if(b & (1<<0))
401                 d.X = 1;
402         else if(b & (1<<1))
403                 d.X = -1;
404
405         if(b & (1<<2))
406                 d.Y = 1;
407         else if(b & (1<<3))
408                 d.Y = -1;
409
410         if(b & (1<<4))
411                 d.Z = 1;
412         else if(b & (1<<5))
413                 d.Z = -1;
414         
415         return d;
416 }
417
418 enum LightBank
419 {
420         LIGHTBANK_DAY,
421         LIGHTBANK_NIGHT
422 };
423
424 /*
425         This is the stuff what the whole world consists of.
426 */
427
428 struct MapNode
429 {
430         // Content
431         u8 d;
432
433         /*
434                 Misc parameter. Initialized to 0.
435                 - For light_propagates() blocks, this is light intensity,
436                   stored logarithmically from 0 to LIGHT_MAX.
437                   Sunlight is LIGHT_SUN, which is LIGHT_MAX+1.
438                 - Contains 2 values, day- and night lighting. Each takes 4 bits.
439         */
440         s8 param;
441         
442         union
443         {
444                 /*
445                         The second parameter. Initialized to 0.
446                         Direction for torches and flowing water.
447                 */
448                 u8 param2;
449                 u8 dir;
450         };
451
452         MapNode(const MapNode & n)
453         {
454                 *this = n;
455         }
456         
457         MapNode(u8 data=CONTENT_AIR, u8 a_param=0, u8 a_param2=0)
458         {
459                 d = data;
460                 param = a_param;
461                 param2 = a_param2;
462         }
463
464         /*MapNode & operator=(const MapNode &other)
465         {
466                 d = other.d;
467                 param = other.param;
468                 param2 = other.param2;
469                 return *this;
470         }*/
471
472         bool operator==(const MapNode &other)
473         {
474                 return (d == other.d
475                                 && param == other.param
476                                 && param2 == other.param2);
477         }
478
479         bool light_propagates()
480         {
481                 return light_propagates_content(d);
482         }
483         
484         bool sunlight_propagates()
485         {
486                 return sunlight_propagates_content(d);
487         }
488         
489         u8 solidness()
490         {
491                 return content_solidness(d);
492         }
493
494         u8 light_source()
495         {
496                 /*
497                         Note that a block that isn't light_propagates() can be a light source.
498                 */
499                 if(d == CONTENT_TORCH)
500                         return LIGHT_MAX;
501                 
502                 return 0;
503         }
504
505         u8 getLightBanksWithSource()
506         {
507                 // Select the brightest of [light source, propagated light]
508                 u8 lightday = 0;
509                 u8 lightnight = 0;
510                 if(light_propagates())
511                 {
512                         lightday = param & 0x0f;
513                         lightnight = (param>>4)&0x0f;
514                 }
515                 if(light_source() > lightday)
516                         lightday = light_source();
517                 if(light_source() > lightnight)
518                         lightnight = light_source();
519                 return (lightday&0x0f) | ((lightnight<<4)&0xf0);
520         }
521
522         void setLightBanks(u8 a_light)
523         {
524                 param = a_light;
525         }
526
527         u8 getLight(enum LightBank bank)
528         {
529                 // Select the brightest of [light source, propagated light]
530                 u8 light = 0;
531                 if(light_propagates())
532                 {
533                         if(bank == LIGHTBANK_DAY)
534                                 light = param & 0x0f;
535                         else if(bank == LIGHTBANK_NIGHT)
536                                 light = (param>>4)&0x0f;
537                         else
538                                 assert(0);
539                 }
540                 if(light_source() > light)
541                         light = light_source();
542                 return light;
543         }
544         
545         // 0 <= daylight_factor <= 1000
546         // 0 <= return value <= LIGHT_SUN
547         u8 getLightBlend(u32 daylight_factor)
548         {
549                 u8 l = ((daylight_factor * getLight(LIGHTBANK_DAY)
550                         + (1000-daylight_factor) * getLight(LIGHTBANK_NIGHT))
551                         )/1000;
552                 u8 max = LIGHT_MAX;
553                 if(getLight(LIGHTBANK_DAY) == LIGHT_SUN)
554                         max = LIGHT_SUN;
555                 if(l > max)
556                         l = max;
557                 return l;
558         }
559         /*// 0 <= daylight_factor <= 1000
560         // 0 <= return value <= 255
561         u8 getLightBlend(u32 daylight_factor)
562         {
563                 u8 daylight = decode_light(getLight(LIGHTBANK_DAY));
564                 u8 nightlight = decode_light(getLight(LIGHTBANK_NIGHT));
565                 u8 mix = ((daylight_factor * daylight
566                         + (1000-daylight_factor) * nightlight)
567                         )/1000;
568                 return mix;
569         }*/
570
571         void setLight(enum LightBank bank, u8 a_light)
572         {
573                 // If not transparent, can't set light
574                 if(light_propagates() == false)
575                         return;
576                 if(bank == LIGHTBANK_DAY)
577                 {
578                         param &= 0xf0;
579                         param |= a_light & 0x0f;
580                 }
581                 else if(bank == LIGHTBANK_NIGHT)
582                 {
583                         param &= 0x0f;
584                         param |= (a_light & 0x0f)<<4;
585                 }
586                 else
587                         assert(0);
588         }
589         
590         // In mapnode.cpp
591         TileSpec getTile(v3s16 dir);
592
593         u8 getMineral();
594
595         /*
596                 These serialization functions are used when informing client
597                 of a single node add
598         */
599
600         static u32 serializedLength(u8 version)
601         {
602                 if(!ser_ver_supported(version))
603                         throw VersionMismatchException("ERROR: MapNode format not supported");
604                         
605                 if(version == 0)
606                         return 1;
607                 else if(version <= 9)
608                         return 2;
609                 else
610                         return 3;
611         }
612         void serialize(u8 *dest, u8 version)
613         {
614                 if(!ser_ver_supported(version))
615                         throw VersionMismatchException("ERROR: MapNode format not supported");
616                         
617                 if(version == 0)
618                 {
619                         dest[0] = d;
620                 }
621                 else if(version <= 9)
622                 {
623                         dest[0] = d;
624                         dest[1] = param;
625                 }
626                 else
627                 {
628                         dest[0] = d;
629                         dest[1] = param;
630                         dest[2] = param2;
631                 }
632         }
633         void deSerialize(u8 *source, u8 version)
634         {
635                 if(!ser_ver_supported(version))
636                         throw VersionMismatchException("ERROR: MapNode format not supported");
637                         
638                 if(version == 0)
639                 {
640                         d = source[0];
641                 }
642                 else if(version == 1)
643                 {
644                         d = source[0];
645                         // This version doesn't support saved lighting
646                         if(light_propagates() || light_source() > 0)
647                                 param = 0;
648                         else
649                                 param = source[1];
650                 }
651                 else if(version <= 9)
652                 {
653                         d = source[0];
654                         param = source[1];
655                 }
656                 else
657                 {
658                         d = source[0];
659                         param = source[1];
660                         param2 = source[2];
661                 }
662
663                 // Translate deprecated stuff
664                 // NOTE: This doesn't get used because MapBlock handles node
665                 // parameters directly
666                 MapNode *translate_to = content_features(d).translate_to;
667                 if(translate_to)
668                 {
669                         dstream<<"MapNode: WARNING: Translating "<<d<<" to "
670                                         <<translate_to->d<<std::endl;
671                         *this = *translate_to;
672                 }
673         }
674 };
675
676 /*
677         Returns integer position of the node in given
678         floating point position.
679 */
680 inline v3s16 floatToInt(v3f p)
681 {
682         v3s16 p2(
683                 (p.X + (p.X>0 ? BS/2 : -BS/2))/BS,
684                 (p.Y + (p.Y>0 ? BS/2 : -BS/2))/BS,
685                 (p.Z + (p.Z>0 ? BS/2 : -BS/2))/BS);
686         return p2;
687 }
688
689 /*
690         The same thing backwards
691 */
692 inline v3f intToFloat(v3s16 p)
693 {
694         v3f p2(
695                 p.X * BS,
696                 p.Y * BS,
697                 p.Z * BS
698         );
699         return p2;
700 }
701
702
703
704 #endif
705