/* (c) 2010 Perttu Ahola */ #ifndef MAPNODE_HEADER #define MAPNODE_HEADER #include #include "common_irrlicht.h" #include "light.h" #include "utility.h" #include "exceptions.h" #include "serialization.h" // Size of node in rendering units #define BS 10 #define MATERIALS_COUNT 256 // This is completely ignored. It doesn't create faces with anything. #define MATERIAL_IGNORE 255 // This is the common material through which the player can walk // and which is transparent to light #define MATERIAL_AIR 254 /* Materials-todo: GRAVEL - Dynamics of gravel: if there is a drop of more than two blocks on any side, it will drop in there. Is this doable? */ enum Material { MATERIAL_STONE=0, MATERIAL_GRASS, /* For water, the param is water pressure. 0...127. TODO: No, at least the lowest nibble is used for lighting. - Water will be a bit like light, but with different flow behavior. - Water blocks will fall down if there is empty space below. - If there is water below, the pressure of the block below is the pressure of the current block + 1, or higher. - If there is any pressure in a horizontally neighboring block, a water block will try to move away from it. - If there is >=2 of pressure in a block below, water will try to move upwards. - NOTE: To keep large operations fast, we have to keep a cache of the water-air-surfaces, just like with light */ MATERIAL_WATER, MATERIAL_LIGHT, MATERIAL_TREE, MATERIAL_LEAVES, MATERIAL_GRASS_FOOTSTEPS, MATERIAL_MESE, // This is set to the number of the actual values in this enum USEFUL_MATERIAL_COUNT }; /* If true, the material allows light propagation and brightness is stored in param. */ inline bool light_propagates_material(u8 m) { return (m == MATERIAL_AIR || m == MATERIAL_LIGHT || m == MATERIAL_WATER); } /* If true, the material allows lossless sunlight propagation. */ inline bool sunlight_propagates_material(u8 m) { return (m == MATERIAL_AIR); } /* On a node-node surface, the material of the node with higher solidness is used for drawing. 0: Invisible 1: Transparent 2: Opaque */ inline u8 material_solidness(u8 m) { if(m == MATERIAL_AIR) return 0; if(m == MATERIAL_WATER) return 1; return 2; } /* Nodes make a face if materials differ and solidness differs. Return value: 0: No face 1: Face uses m1's material 2: Face uses m2's material */ inline u8 face_materials(u8 m1, u8 m2) { if(m1 == MATERIAL_IGNORE || m2 == MATERIAL_IGNORE) return 0; bool materials_differ = (m1 != m2); bool solidness_differs = (material_solidness(m1) != material_solidness(m2)); bool makes_face = materials_differ && solidness_differs; if(makes_face == false) return 0; if(material_solidness(m1) > material_solidness(m2)) return 1; else return 2; } struct MapNode { //TODO: block type to differ from material // (e.g. grass edges or something) // block type u8 d; // Removed because light is now stored in param for air // f32 light; /* Misc parameter. Initialized to 0. - For light_propagates() blocks, this is light intensity, stored logarithmically from 0 to LIGHT_MAX. Sunlight is LIGHT_SUN, which is LIGHT_MAX+1. */ s8 param; MapNode(const MapNode & n) { *this = n; } MapNode(u8 data=MATERIAL_AIR, u8 a_param=0) { d = data; param = a_param; } bool light_propagates() { return light_propagates_material(d); } bool sunlight_propagates() { return sunlight_propagates_material(d); } u8 solidness() { return material_solidness(d); } u8 light_source() { /* Note that a block that isn't light_propagates() can be a light source. */ if(d == MATERIAL_LIGHT) return LIGHT_MAX; return 0; } u8 getLight() { // Select the brightest of [light_source, transparent_light] u8 light = 0; if(light_propagates()) light = param & 0x0f; if(light_source() > light) light = light_source(); return light; } void setLight(u8 a_light) { // If not transparent, can't set light if(light_propagates() == false) return; param = a_light; } static u32 serializedLength(u8 version) { if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapNode format not supported"); if(version == 0) return 1; else return 2; } void serialize(u8 *dest, u8 version) { if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapNode format not supported"); if(version == 0) { dest[0] = d; } else { dest[0] = d; dest[1] = param; } } void deSerialize(u8 *source, u8 version) { if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapNode format not supported"); if(version == 0) { d = source[0]; } else if(version == 1) { d = source[0]; // This version doesn't support saved lighting if(light_propagates() || light_source() > 0) param = 0; else param = source[1]; } else { d = source[0]; param = source[1]; } } }; /* Returns integer position of the node in given floating point position. */ inline v3s16 floatToInt(v3f p) { v3s16 p2( (p.X + (p.X>0 ? BS/2 : -BS/2))/BS, (p.Y + (p.Y>0 ? BS/2 : -BS/2))/BS, (p.Z + (p.Z>0 ? BS/2 : -BS/2))/BS); return p2; } inline v3f intToFloat(v3s16 p) { v3f p2( p.X * BS, p.Y * BS, p.Z * BS ); return p2; } #endif