uniform mat4 mWorldViewProj;
uniform mat4 mWorld;
-uniform float dayNightRatio;
+// Color of the light emitted by the sun.
+uniform vec3 dayLight;
uniform vec3 eyePosition;
uniform float animationTimer;
varying vec3 tsLightVec;
varying float area_enable_parallax;
+// Color of the light emitted by the light sources.
+const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
const float e = 2.718281828459;
const float BS = 10.0;
v.z = dot(eyeVec, normal);
tsEyeVec = normalize (v);
+ // Calculate color.
+ // Red, green and blue components are pre-multiplied with
+ // the brightness, so now we have to multiply these
+ // colors with the color of the incoming light.
+ // The pre-baked colors are halved to prevent overflow.
vec4 color;
- float day = gl_Color.r;
- float night = gl_Color.g;
- float light_source = gl_Color.b;
-
- float rg = mix(night, day, dayNightRatio);
- rg += light_source * 2.5; // Make light sources brighter
- float b = rg;
-
- // Moonlight is blue
- b += (day - night) / 13.0;
- rg -= (day - night) / 23.0;
-
+ // The alpha gives the ratio of sunlight in the incoming light.
+ float nightRatio = 1 - gl_Color.a;
+ color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb +
+ nightRatio * artificialLight.rgb) * 2;
+ color.a = 1;
+
// Emphase blue a bit in darker places
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
- b += max(0.0, (1.0 - abs(b - 0.13) / 0.17) * 0.025);
-
- // Artificial light is yellow-ish
- // See C++ implementation in mapblock_mesh.cpp finalColorBlend()
- rg += max(0.0, (1.0 - abs(rg - 0.85) / 0.15) * 0.065);
-
- color.r = rg;
- color.g = rg;
- color.b = b;
-
- color.a = gl_Color.a;
+ float brightness = (color.r + color.g + color.b) / 3;
+ color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) +
+ 0.07 * brightness);
+
gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0);
}
uniform mat4 mWorldViewProj;
uniform mat4 mWorld;
-uniform float dayNightRatio;
+// Color of the light emitted by the sun.
+uniform vec3 dayLight;
uniform vec3 eyePosition;
uniform float animationTimer;
varying vec3 tsEyeVec;
varying vec3 tsLightVec;
+// Color of the light emitted by the light sources.
+const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
const float e = 2.718281828459;
const float BS = 10.0;
eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz;
tsEyeVec = eyeVec * tbnMatrix;
+ // Calculate color.
+ // Red, green and blue components are pre-multiplied with
+ // the brightness, so now we have to multiply these
+ // colors with the color of the incoming light.
+ // The pre-baked colors are halved to prevent overflow.
vec4 color;
- float day = gl_Color.r;
- float night = gl_Color.g;
- float light_source = gl_Color.b;
-
- float rg = mix(night, day, dayNightRatio);
- rg += light_source * 2.5; // Make light sources brighter
- float b = rg;
-
- // Moonlight is blue
- b += (day - night) / 13.0;
- rg -= (day - night) / 23.0;
-
+ // The alpha gives the ratio of sunlight in the incoming light.
+ float nightRatio = 1 - gl_Color.a;
+ color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb +
+ nightRatio * artificialLight.rgb) * 2;
+ color.a = 1;
+
// Emphase blue a bit in darker places
// See C++ implementation in mapblock_mesh.cpp finalColorBlend()
- b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025);
-
- // Artificial light is yellow-ish
- // See C++ implementation in mapblock_mesh.cpp finalColorBlend()
- rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065);
-
- color.r = rg;
- color.g = rg;
- color.b = b;
-
- color.a = gl_Color.a;
- gl_FrontColor = gl_BackColor = clamp(color,0.0,1.0);
+ float brightness = (color.r + color.g + color.b) / 3;
+ color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) +
+ 0.07 * brightness);
+
+ gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0);
}
uniform mat4 mWorldViewProj;
uniform mat4 mWorld;
-uniform float dayNightRatio;
uniform vec3 eyePosition;
uniform float animationTimer;
bit 4 (0x10) - Makes the plant mesh 1.4x larger
bit 5 (0x20) - Moves each face randomly a small bit down (1/8 max)
bits 6-7 are reserved for future use.
+ paramtype2 == "color"
+ ^ `param2` tells which color is picked from the palette.
+ The palette should have 256 pixels.
+ paramtype2 == "colorfacedir"
+ ^ Same as `facedir`, but with colors.
+ The first three bits of `param2` tells which color
+ is picked from the palette.
+ The palette should have 8 pixels.
+ paramtype2 == "colorwallmounted"
+ ^ Same as `wallmounted`, but with colors.
+ The first five bits of `param2` tells which color
+ is picked from the palette.
+ The palette should have 32 pixels.
collision_box = {
type = "fixed",
fixed = {
when displacement mapping is used
Directions are from the point of view of the tile texture,
not the node it's on
+* `{name="image.png", color=ColorSpec}`
+ * the texture's color will be multiplied with this color.
+ * the tile's color overrides the owning node's color in all cases.
* deprecated, yet still supported field names:
* `image` (name)
special_tiles = {tile definition 1, Tile definition 2}, --[[
^ Special textures of node; used rarely (old field name: special_materials)
^ List can be shortened to needed length ]]
- alpha = 255,
+ color = ColorSpec, --[[
+ ^ The node's original color will be multiplied with this color.
+ ^ If the node has a palette, then this setting only has an effect
+ ^ in the inventory and on the wield item. ]]
use_texture_alpha = false, -- Use texture's alpha channel
+ palette = "palette.png", --[[
+ ^ The node's `param2` is used to select a pixel from the image
+ ^ (pixels are arranged from left to right and from top to bottom).
+ ^ The node's color will be multiplied with the selected pixel's
+ ^ color. Tiles can override this behavior.
+ ^ Only when `paramtype2` supports palettes. ]]
post_effect_color = "green#0F", -- If player is inside node, see "ColorSpec"
paramtype = "none", -- See "Nodes" --[[
^ paramtype = "light" allows light to propagate from or through the node with light value
video::ITexture* generateTextureFromMesh(
const TextureFromMeshParams ¶ms);
- // Generates an image from a full string like
- // "stone.png^mineral_coal.png^[crack:1:0".
- // Shall be called from the main thread.
video::IImage* generateImage(const std::string &name);
video::ITexture* getNormalTexture(const std::string &name);
const std::string &name, u32 *id = NULL) = 0;
virtual IrrlichtDevice* getDevice()=0;
virtual bool isKnownSourceImage(const std::string &name)=0;
+ /*! Generates an image from a full string like
+ * "stone.png^mineral_coal.png^[crack:1:0".
+ * Shall be called from the main thread.
+ * The returned Image should be dropped.
+ */
+ virtual video::IImage* generateImage(const std::string &name)=0;
virtual video::ITexture* generateTextureFromMesh(
const TextureFromMeshParams ¶ms)=0;
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
texture(NULL),
normal_texture(NULL),
flags_texture(NULL),
- alpha(255),
material_type(TILE_MATERIAL_BASIC),
material_flags(
//0 // <- DEBUG, Use the one below
shader_id(0),
animation_frame_count(1),
animation_frame_length_ms(0),
- rotation(0)
+ rotation(0),
+ has_color(false),
+ color(),
+ emissive_light(0)
{
}
+ /*!
+ * Two tiles are equal if they can be appended to
+ * the same mesh buffer.
+ */
bool operator==(const TileSpec &other) const
{
return (
texture_id == other.texture_id &&
- /* texture == other.texture && */
- alpha == other.alpha &&
material_type == other.material_type &&
material_flags == other.material_flags &&
rotation == other.rotation
);
}
+ /*!
+ * Two tiles are not equal if they must be in different mesh buffers.
+ */
bool operator!=(const TileSpec &other) const
{
return !(*this == other);
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_TRANSPARENT:
- material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
video::ITexture *normal_texture;
video::ITexture *flags_texture;
- // Vertex alpha (when MATERIAL_ALPHA_VERTEX is used)
- u8 alpha;
// Material parameters
u8 material_type;
u8 material_flags;
std::vector<FrameSpec> frames;
u8 rotation;
+ //! If true, the tile has its own color.
+ bool has_color;
+ /*!
+ * The color of the tile, or if the tile does not own
+ * a color then the color of the node owning this tile.
+ */
+ video::SColor color;
+ //! This much light does the tile emit.
+ u8 emissive_light;
};
#endif
node_at_lplayer = m_map->getNodeNoEx(p);
u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
- u8 day = light & 0xff;
- u8 night = (light >> 8) & 0xff;
- finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
+ final_color_blend(&lplayer->light_color, light, day_night_ratio);
}
/*
// Create a cuboid.
-// collector - the MeshCollector for the resulting polygons
-// box - the position and size of the box
-// tiles - the tiles (materials) to use (for all 6 faces)
-// tilecount - number of entries in tiles, 1<=tilecount<=6
-// c - vertex colour - used for all
-// txc - texture coordinates - this is a list of texture coordinates
-// for the opposite corners of each face - therefore, there
-// should be (2+2)*6=24 values in the list. Alternatively, pass
-// NULL to use the entire texture for each face. The order of
-// the faces in the list is up-down-right-left-back-front
-// (compatible with ContentFeatures). If you specified 0,0,1,1
-// for each face, that would be the same as passing NULL.
+// collector - the MeshCollector for the resulting polygons
+// box - the position and size of the box
+// tiles - the tiles (materials) to use (for all 6 faces)
+// tilecount - number of entries in tiles, 1<=tilecount<=6
+// c - colors of the cuboid's six sides
+// txc - texture coordinates - this is a list of texture coordinates
+// for the opposite corners of each face - therefore, there
+// should be (2+2)*6=24 values in the list. Alternatively,
+// pass NULL to use the entire texture for each face. The
+// order of the faces in the list is up-down-right-left-back-
+// front (compatible with ContentFeatures). If you specified
+// 0,0,1,1 for each face, that would be the same as
+// passing NULL.
+// light source - if greater than zero, the box's faces will not be shaded
void makeCuboid(MeshCollector *collector, const aabb3f &box,
- TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc)
+ TileSpec *tiles, int tilecount, const video::SColor *c,
+ const f32* txc, const u8 light_source)
{
assert(tilecount >= 1 && tilecount <= 6); // pre-condition
v3f min = box.MinEdge;
v3f max = box.MaxEdge;
-
-
if(txc == NULL) {
static const f32 txc_default[24] = {
0,0,1,1,
txc = txc_default;
}
+ video::SColor c1 = c[0];
+ video::SColor c2 = c[1];
+ video::SColor c3 = c[2];
+ video::SColor c4 = c[3];
+ video::SColor c5 = c[4];
+ video::SColor c6 = c[5];
+ if (!light_source) {
+ applyFacesShading(c1, v3f(0, 1, 0));
+ applyFacesShading(c2, v3f(0, -1, 0));
+ applyFacesShading(c3, v3f(1, 0, 0));
+ applyFacesShading(c4, v3f(-1, 0, 0));
+ applyFacesShading(c5, v3f(0, 0, 1));
+ applyFacesShading(c6, v3f(0, 0, -1));
+ }
+
video::S3DVertex vertices[24] =
{
// up
- video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
- video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
- video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
- video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
+ video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c1, txc[0],txc[1]),
+ video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c1, txc[2],txc[1]),
+ video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c1, txc[2],txc[3]),
+ video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c1, txc[0],txc[3]),
// down
- video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
- video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
- video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
- video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
+ video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c2, txc[4],txc[5]),
+ video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c2, txc[6],txc[5]),
+ video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c2, txc[6],txc[7]),
+ video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c2, txc[4],txc[7]),
// right
- video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
- video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
- video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
- video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
+ video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c3, txc[ 8],txc[9]),
+ video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c3, txc[10],txc[9]),
+ video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c3, txc[10],txc[11]),
+ video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c3, txc[ 8],txc[11]),
// left
- video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
- video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
- video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
- video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
+ video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c4, txc[12],txc[13]),
+ video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c4, txc[14],txc[13]),
+ video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c4, txc[14],txc[15]),
+ video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c4, txc[12],txc[15]),
// back
- video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
- video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
- video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
- video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
+ video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c5, txc[16],txc[17]),
+ video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c5, txc[18],txc[17]),
+ video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c5, txc[18],txc[19]),
+ video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c5, txc[16],txc[19]),
// front
- video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
- video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
- video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
- video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
+ video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c6, txc[20],txc[21]),
+ video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c6, txc[22],txc[21]),
+ video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c6, txc[22],txc[23]),
+ video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c6, txc[20],txc[23]),
};
for(int i = 0; i < 6; i++)
}
}
+// Create a cuboid.
+// collector - the MeshCollector for the resulting polygons
+// box - the position and size of the box
+// tiles - the tiles (materials) to use (for all 6 faces)
+// tilecount - number of entries in tiles, 1<=tilecount<=6
+// c - color of the cuboid
+// txc - texture coordinates - this is a list of texture coordinates
+// for the opposite corners of each face - therefore, there
+// should be (2+2)*6=24 values in the list. Alternatively,
+// pass NULL to use the entire texture for each face. The
+// order of the faces in the list is up-down-right-left-back-
+// front (compatible with ContentFeatures). If you specified
+// 0,0,1,1 for each face, that would be the same as
+// passing NULL.
+// light source - if greater than zero, the box's faces will not be shaded
+void makeCuboid(MeshCollector *collector, const aabb3f &box, TileSpec *tiles,
+ int tilecount, const video::SColor &c, const f32* txc,
+ const u8 light_source)
+{
+ video::SColor color[6];
+ for (u8 i = 0; i < 6; i++)
+ color[i] = c;
+ makeCuboid(collector, box, tiles, tilecount, color, txc, light_source);
+}
+
static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
MeshMakeData *data, MapNode n, int v, int *neighbors)
{
return 9 * pos.X + 3 * pos.Y + pos.Z + 13;
}
+/*!
+ * Returns the i-th special tile for a map node.
+ */
+static TileSpec getSpecialTile(const ContentFeatures &f,
+ const MapNode &n, u8 i)
+{
+ TileSpec copy = f.special_tiles[i];
+ if (!copy.has_color)
+ n.getColor(f, ©.color);
+ return copy;
+}
+
/*
TODO: Fix alpha blending for special nodes
Currently only the last element rendered is blended correct
/*
Add water sources to mesh if using new style
*/
- TileSpec tile_liquid = f.special_tiles[0];
+ TileSpec tile_liquid = getSpecialTile(f, n, 0);
TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);
+ u16 l = getInteriorLight(n, 0, nodedef);
+ video::SColor c1 = encode_light_and_color(l,
+ tile_liquid.color, f.light_source);
+ video::SColor c2 = encode_light_and_color(l,
+ tile_liquid_bfculled.color, f.light_source);
bool top_is_same_liquid = false;
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
top_is_same_liquid = true;
- u16 l = getInteriorLight(n, 0, nodedef);
- video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
-
/*
Generate sides
*/
// Use backface culled material if neighbor doesn't have a
// solidness of 0
const TileSpec *current_tile = &tile_liquid;
- if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
+ video::SColor *c = &c1;
+ if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) {
current_tile = &tile_liquid_bfculled;
+ c = &c2;
+ }
video::S3DVertex vertices[4] =
{
- video::S3DVertex(-BS/2,0,BS/2,0,0,0, c, 0,1),
- video::S3DVertex(BS/2,0,BS/2,0,0,0, c, 1,1),
- video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
- video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
+ video::S3DVertex(-BS/2,0,BS/2,0,0,0, *c, 0,1),
+ video::S3DVertex(BS/2,0,BS/2,0,0,0, *c, 1,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0),
};
/*
video::S3DVertex vertices[4] =
{
- video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
- video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
- video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
- video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1),
+ video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0),
+ video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0),
};
v3f offset(p.X * BS, (p.Y + 0.5) * BS, p.Z * BS);
/*
Add flowing liquid to mesh
*/
- TileSpec tile_liquid = f.special_tiles[0];
- TileSpec tile_liquid_bfculled = f.special_tiles[1];
+ TileSpec tile_liquid = getSpecialTile(f, n, 0);
+ TileSpec tile_liquid_bfculled = getSpecialTile(f, n, 1);
bool top_is_same_liquid = false;
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
// Otherwise use the light of this node (the liquid)
else
l = getInteriorLight(n, 0, nodedef);
- video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
+ video::SColor c1 = encode_light_and_color(l,
+ tile_liquid.color, f.light_source);
+ video::SColor c2 = encode_light_and_color(l,
+ tile_liquid_bfculled.color, f.light_source);
u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
is liquid, don't draw side face
*/
if (top_is_same_liquid &&
- neighbor_data.flags & neighborflag_top_is_same_liquid)
+ (neighbor_data.flags & neighborflag_top_is_same_liquid))
continue;
content_t neighbor_content = neighbor_data.content;
// Use backface culled material if neighbor doesn't have a
// solidness of 0
const TileSpec *current_tile = &tile_liquid;
- if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
+ video::SColor *c = &c1;
+ if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) {
current_tile = &tile_liquid_bfculled;
+ c = &c2;
+ }
video::S3DVertex vertices[4] =
{
- video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
- video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
- video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
- video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0),
};
/*
{
video::S3DVertex vertices[4] =
{
- video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
- video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
- video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
- video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1),
+ video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0),
+ video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0),
};
// To get backface culling right, the vertices need to go
TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
u16 l = getInteriorLight(n, 1, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
-
+ video::SColor c = encode_light_and_color(l, tile.color,
+ f.light_source);
for(u32 j=0; j<6; j++)
{
// Check this neighbor
// Don't make face if neighbor is of same type
if(n2.getContent() == n.getContent())
continue;
+ video::SColor c2=c;
+ if(!f.light_source)
+ applyFacesShading(c2, v3f(dir.X, dir.Y, dir.Z));
+
// The face at Z+
video::S3DVertex vertices[4] = {
- video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,1),
- video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,1),
- video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,0),
- video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,0),
+ video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,1),
+ video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,1),
+ video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,0),
+ video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,0),
};
// Rotations in the g_6dirs format
v3s16( 0, 0,-1)
};
+ u16 l = getInteriorLight(n, 1, nodedef);
u8 i;
TileSpec tiles[6];
for (i = 0; i < 6; i++)
tiles[i] = getNodeTile(n, p, dirs[i], data);
+ video::SColor tile0color = encode_light_and_color(l,
+ tiles[0].color, f.light_source);
+ video::SColor tile0colors[6];
+ for (i = 0; i < 6; i++)
+ tile0colors[i] = tile0color;
+
TileSpec glass_tiles[6];
+ video::SColor glasscolor[6];
if (tiles[1].texture && tiles[2].texture && tiles[3].texture) {
glass_tiles[0] = tiles[2];
glass_tiles[1] = tiles[3];
for (i = 0; i < 6; i++)
glass_tiles[i] = tiles[1];
}
+ for (i = 0; i < 6; i++)
+ glasscolor[i] = encode_light_and_color(l, glass_tiles[i].color,
+ f.light_source);
u8 param2 = n.getParam2();
bool H_merge = ! bool(param2 & 128);
bool V_merge = ! bool(param2 & 64);
param2 = param2 & 63;
- u16 l = getInteriorLight(n, 1, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
v3f pos = intToFloat(p, BS);
static const float a = BS / 2;
static const float g = a - 0.003;
1-tx2, 1-ty2, 1-tx1, 1-ty1,
tx1, 1-ty2, tx2, 1-ty1,
};
- makeCuboid(&collector, box, &tiles[0], 1, c, txc1);
+ makeCuboid(&collector, box, &tiles[0], 1, tile0colors,
+ txc1, f.light_source);
}
for(i = 0; i < 6; i++)
1-tx2, 1-ty2, 1-tx1, 1-ty1,
tx1, 1-ty2, tx2, 1-ty1,
};
- makeCuboid(&collector, box, &glass_tiles[i], 1, c, txc2);
+ makeCuboid(&collector, box, &glass_tiles[i], 1, glasscolor,
+ txc2, f.light_source);
}
if (param2 > 0 && f.special_tiles[0].texture) {
// Interior volume level is in range 0 .. 63,
// convert it to -0.5 .. 0.5
float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0);
+ TileSpec tile=getSpecialTile(f, n, 0);
+ video::SColor special_color = encode_light_and_color(l,
+ tile.color, f.light_source);
TileSpec interior_tiles[6];
for (i = 0; i < 6; i++)
- interior_tiles[i] = f.special_tiles[0];
+ interior_tiles[i] = tile;
+
float offset = 0.003;
box = aabb3f(visible_faces[3] ? -b : -a + offset,
visible_faces[1] ? -b : -a + offset,
1-tx2, 1-ty2, 1-tx1, 1-ty1,
tx1, 1-ty2, tx2, 1-ty1,
};
- makeCuboid(&collector, box, interior_tiles, 6, c, txc3);
+ makeCuboid(&collector, box, interior_tiles, 6, special_color,
+ txc3, f.light_source);
}
break;}
case NDT_ALLFACES:
{
TileSpec tile_leaves = getNodeTile(n, p,
v3s16(0,0,0), data);
-
u16 l = getInteriorLight(n, 1, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
+ video::SColor c = encode_light_and_color(l,
+ tile_leaves.color, f.light_source);
v3f pos = intToFloat(p, BS);
aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
box.MinEdge += pos;
box.MaxEdge += pos;
- makeCuboid(&collector, box, &tile_leaves, 1, c, NULL);
+ makeCuboid(&collector, box, &tile_leaves, 1, c, NULL,
+ f.light_source);
break;}
case NDT_ALLFACES_OPTIONAL:
// This is always pre-converted to something else
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
u16 l = getInteriorLight(n, 1, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
+ video::SColor c = encode_light_and_color(l, tile.color,
+ f.light_source);
float s = BS/2*f.visual_scale;
// Wall at X+ of node
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
u16 l = getInteriorLight(n, 0, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
+ video::SColor c = encode_light_and_color(l, tile.color,
+ f.light_source);
float d = (float)BS/16;
float s = BS/2*f.visual_scale;
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
u16 l = getInteriorLight(n, 1, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
+ video::SColor c = encode_light_and_color(l, tile.color,
+ f.light_source);
float s = BS / 2 * f.visual_scale;
// add sqrt(2) visual scale
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
u16 l = getInteriorLight(n, 1, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
+ video::SColor c = encode_light_and_color(l, tile.color,
+ f.light_source);
float s = BS / 2 * f.visual_scale;
tile_rot.rotation = 1;
u16 l = getInteriorLight(n, 1, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
+ video::SColor c = encode_light_and_color(l, tile.color,
+ f.light_source);
const f32 post_rad=(f32)BS/8;
const f32 bar_rad=(f32)BS/16;
4/16.,0,8/16.,1,
8/16.,0,12/16.,1,
12/16.,0,16/16.,1};
- makeCuboid(&collector, post, &tile_rot, 1, c, postuv);
+ makeCuboid(&collector, post, &tile_rot, 1, c, postuv,
+ f.light_source);
// Now a section of fence, +X, if there's a post there
v3s16 p2 = p;
0/16.,8/16.,16/16.,10/16.,
0/16.,14/16.,16/16.,16/16.};
makeCuboid(&collector, bar, &tile_nocrack, 1,
- c, xrailuv);
+ c, xrailuv, f.light_source);
bar.MinEdge.Y -= BS/2;
bar.MaxEdge.Y -= BS/2;
makeCuboid(&collector, bar, &tile_nocrack, 1,
- c, xrailuv);
+ c, xrailuv, f.light_source);
}
// Now a section of fence, +Z, if there's a post there
6/16.,6/16.,8/16.,8/16.,
10/16.,10/16.,12/16.,12/16.};
makeCuboid(&collector, bar, &tile_nocrack, 1,
- c, zrailuv);
+ c, zrailuv, f.light_source);
bar.MinEdge.Y -= BS/2;
bar.MaxEdge.Y -= BS/2;
makeCuboid(&collector, bar, &tile_nocrack, 1,
- c, zrailuv);
+ c, zrailuv, f.light_source);
}
break;}
case NDT_RAILLIKE:
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
u16 l = getInteriorLight(n, 0, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
+ video::SColor c = encode_light_and_color(l, tile.color,
+ f.light_source);
float d = (float)BS/64;
float s = BS/2;
video::S3DVertex vertices[4] =
{
- video::S3DVertex(-s, -s+d,-s, 0,0,0, c,0,1),
- video::S3DVertex( s, -s+d,-s, 0,0,0, c,1,1),
- video::S3DVertex( s, g*s+d, s, 0,0,0, c,1,0),
- video::S3DVertex(-s, g*s+d, s, 0,0,0, c,0,0),
+ video::S3DVertex(-s, -s+d, -s, 0, 0, 0, c, 0, 1),
+ video::S3DVertex( s, -s+d, -s, 0, 0, 0, c, 1, 1),
+ video::S3DVertex( s, g*s+d, s, 0, 0, 0, c, 1, 0),
+ video::S3DVertex(-s, g*s+d, s, 0, 0, 0, c, 0, 0),
};
for(s32 i=0; i<4; i++)
v3s16(0, 0, 1),
v3s16(0, 0, -1)
};
- TileSpec tiles[6];
u16 l = getInteriorLight(n, 1, nodedef);
- video::SColor c = MapBlock_LightColor(255, l, f.light_source);
+ TileSpec tiles[6];
+ video::SColor colors[6];
+ for(int j = 0; j < 6; j++) {
+ // Handles facedir rotation for textures
+ tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
+ colors[j]= encode_light_and_color(l, tiles[j].color,
+ f.light_source);
+ }
v3f pos = intToFloat(p, BS);
i = boxes.begin();
i != boxes.end(); ++i)
{
- for(int j = 0; j < 6; j++)
- {
- // Handles facedir rotation for textures
- tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
- }
aabb3f box = *i;
box.MinEdge += pos;
box.MaxEdge += pos;
// front
tx1, 1-ty2, tx2, 1-ty1,
};
- makeCuboid(&collector, box, tiles, 6, c, txc);
+ makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source);
}
break;}
case NDT_MESH:
{
v3f pos = intToFloat(p, BS);
- video::SColor c = MapBlock_LightColor(255, getInteriorLight(n, 1, nodedef), f.light_source);
-
+ u16 l = getInteriorLight(n, 1, nodedef);
u8 facedir = 0;
- if (f.param_type_2 == CPT2_FACEDIR) {
+ if (f.param_type_2 == CPT2_FACEDIR ||
+ f.param_type_2 == CPT2_COLORED_FACEDIR) {
facedir = n.getFaceDir(nodedef);
- } else if (f.param_type_2 == CPT2_WALLMOUNTED) {
+ } else if (f.param_type_2 == CPT2_WALLMOUNTED ||
+ f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
//convert wallmounted to 6dfacedir.
//when cache enabled, it is already converted
facedir = n.getWallMounted(nodedef);
if (f.mesh_ptr[facedir]) {
// use cached meshes
for(u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) {
+ const TileSpec &tile = getNodeTileN(n, p, j, data);
scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j);
- collector.append(getNodeTileN(n, p, j, data),
- (video::S3DVertex *)buf->getVertices(), buf->getVertexCount(),
- buf->getIndices(), buf->getIndexCount(), pos, c);
+ collector.append(tile, (video::S3DVertex *)
+ buf->getVertices(), buf->getVertexCount(),
+ buf->getIndices(), buf->getIndexCount(), pos,
+ encode_light_and_color(l, tile.color, f.light_source),
+ f.light_source);
}
} else if (f.mesh_ptr[0]) {
// no cache, clone and rotate mesh
recalculateBoundingBox(mesh);
meshmanip->recalculateNormals(mesh, true, false);
for(u16 j = 0; j < mesh->getMeshBufferCount(); j++) {
+ const TileSpec &tile = getNodeTileN(n, p, j, data);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- collector.append(getNodeTileN(n, p, j, data),
- (video::S3DVertex *)buf->getVertices(), buf->getVertexCount(),
- buf->getIndices(), buf->getIndexCount(), pos, c);
+ collector.append(tile, (video::S3DVertex *)
+ buf->getVertices(), buf->getVertexCount(),
+ buf->getIndices(), buf->getIndexCount(), pos,
+ encode_light_and_color(l, tile.color, f.light_source),
+ f.light_source);
}
mesh->drop();
}
CachedPixelShaderSetting<float> m_fog_distance;
CachedVertexShaderSetting<float> m_animation_timer_vertex;
CachedPixelShaderSetting<float> m_animation_timer_pixel;
- CachedPixelShaderSetting<float> m_day_night_ratio;
+ CachedPixelShaderSetting<float, 3> m_day_light;
CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
CachedVertexShaderSetting<float, 3> m_eye_position_vertex;
CachedPixelShaderSetting<float, 3> m_minimap_yaw;
m_fog_distance("fogDistance"),
m_animation_timer_vertex("animationTimer"),
m_animation_timer_pixel("animationTimer"),
- m_day_night_ratio("dayNightRatio"),
+ m_day_light("dayLight"),
m_eye_position_pixel("eyePosition"),
m_eye_position_vertex("eyePosition"),
m_minimap_yaw("yawVec"),
m_fog_distance.set(&fog_distance, services);
- float daynight_ratio = (float)m_client->getEnv().getDayNightRatio() / 1000.f;
- m_day_night_ratio.set(&daynight_ratio, services);
+ u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio();
+ video::SColorf sunlight;
+ get_sunlight_color(&sunlight, daynight_ratio);
+ float dnc[3] = {
+ sunlight.r,
+ sunlight.g,
+ sunlight.b };
+ m_day_light.set(dnc, services);
u32 animation_timer = porting::getTimeMs() % 100000;
float animation_timer_f = (float)animation_timer / 100000.f;
// Predict param2 for facedir and wallmounted nodes
u8 param2 = 0;
- if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) {
+ if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED ||
+ nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) {
v3s16 dir = nodepos - neighbourpos;
if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
}
}
- if (nodedef->get(id).param_type_2 == CPT2_FACEDIR) {
+ if (nodedef->get(id).param_type_2 == CPT2_FACEDIR ||
+ nodedef->get(id).param_type_2 == CPT2_COLORED_FACEDIR) {
v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
if (abs(dir.X) > abs(dir.Z)) {
light_level = node_light;
}
- video::SColor c = MapBlock_LightColor(255, light_level, 0);
- u8 day = c.getRed();
- u8 night = c.getGreen();
u32 daynight_ratio = client->getEnv().getDayNightRatio();
- finalColorBlend(c, day, night, daynight_ratio);
+ video::SColor c;
+ final_color_blend(&c, light_level, daynight_ratio);
// Modify final color a bit with time
u32 timer = porting::getTimeMs() % 5000;
const ContentFeatures &features =
client->getNodeDefManager()->get(n);
client->getParticleManager()->addPunchingParticles(client, smgr,
- player, nodepos, features.tiles);
+ player, nodepos, n, features);
}
}
const ContentFeatures &features =
client->getNodeDefManager()->get(wasnode);
client->getParticleManager()->addDiggingParticles(client, smgr,
- player, nodepos, features.tiles);
+ player, nodepos, wasnode, features);
}
runData->dig_time = 0;
#include "util/directiontables.h"
#include <IMeshManipulator.h>
-static void applyFacesShading(video::SColor &color, const float factor)
-{
- color.setRed(core::clamp(core::round32(color.getRed() * factor), 0, 255));
- color.setGreen(core::clamp(core::round32(color.getGreen() * factor), 0, 255));
-}
-
/*
MeshMakeData
*/
return getSmoothLightCombined(p, data);
}
-/*
- Converts from day + night color values (0..255)
- and a given daynight_ratio to the final SColor shown on screen.
-*/
-void finalColorBlend(video::SColor& result,
- u8 day, u8 night, u32 daynight_ratio)
+void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
+ f32 rg = daynight_ratio / 1000.0f - 0.04f;
+ f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
+ sunlight->r = rg;
+ sunlight->g = rg;
+ sunlight->b = b;
+}
+
+void final_color_blend(video::SColor *result,
+ u16 light, u32 daynight_ratio)
{
- s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
- s32 b = rg;
+ video::SColorf dayLight;
+ get_sunlight_color(&dayLight, daynight_ratio);
+ final_color_blend(result,
+ encode_light_and_color(light, video::SColor(0xFFFFFFFF), 0), dayLight);
+}
- // Moonlight is blue
- b += (day - night) / 13;
- rg -= (day - night) / 23;
+void final_color_blend(video::SColor *result,
+ const video::SColor &data, const video::SColorf &dayLight)
+{
+ static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
+
+ video::SColorf c(data);
+ f32 n = 1 - c.a;
+
+ f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
+ f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
+ f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
// Emphase blue a bit in darker places
// Each entry of this array represents a range of 8 blue levels
1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
- b += emphase_blue_when_dark[irr::core::clamp(b, 0, 255) / 8];
- b = irr::core::clamp(b, 0, 255);
- // Artificial light is yellow-ish
- static const u8 emphase_yellow_when_artificial[16] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
- };
- rg += emphase_yellow_when_artificial[night/16];
- rg = irr::core::clamp(rg, 0, 255);
+ b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
+ 0, 255) / 8] / 255.0f;
- result.setRed(rg);
- result.setGreen(rg);
- result.setBlue(b);
+ result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
+ result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
+ result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
}
/*
};
static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
- v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
+ v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
{
// Position is at the center of the cube.
v3f pos = p * BS;
v3f normal(dir.X, dir.Y, dir.Z);
- u8 alpha = tile.alpha;
-
dest.push_back(FastFace());
FastFace& face = *dest.rbegin();
- face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
- MapBlock_LightColor(alpha, li0, light_source),
- core::vector2d<f32>(x0+w*abs_scale, y0+h));
- face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
- MapBlock_LightColor(alpha, li1, light_source),
- core::vector2d<f32>(x0, y0+h));
- face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
- MapBlock_LightColor(alpha, li2, light_source),
- core::vector2d<f32>(x0, y0));
- face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
- MapBlock_LightColor(alpha, li3, light_source),
- core::vector2d<f32>(x0+w*abs_scale, y0));
+ u16 li[4] = { li0, li1, li2, li3 };
+ v2f32 f[4] = {
+ core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
+ core::vector2d<f32>(x0, y0 + h),
+ core::vector2d<f32>(x0, y0),
+ core::vector2d<f32>(x0 + w * abs_scale, y0) };
+
+ for (u8 i = 0; i < 4; i++) {
+ video::SColor c = encode_light_and_color(li[i], tile.color,
+ tile.emissive_light);
+ if (!tile.emissive_light)
+ applyFacesShading(c, normal);
+
+ face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
+ }
face.tile = tile;
}
TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
{
INodeDefManager *ndef = data->m_client->ndef();
- TileSpec spec = ndef->get(mn).tiles[tileindex];
+ const ContentFeatures &f = ndef->get(mn);
+ TileSpec spec = f.tiles[tileindex];
+ if (!spec.has_color)
+ mn.getColor(f, &spec.color);
// Apply temporary crack
if (p == data->m_crack_pos_relative)
spec.material_flags |= MATERIAL_FLAG_CRACK;
v3s16 &p_corrected,
v3s16 &face_dir_corrected,
u16 *lights,
- TileSpec &tile,
- u8 &light_source
+ TileSpec &tile
)
{
VoxelManipulator &vmanip = data->m_vmanip;
return;
}
- const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
+ const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(
+ blockpos_nodes + p + face_dir);
if (n1.getContent() == CONTENT_IGNORE) {
makes_face = false;
makes_face = true;
- if(mf == 1)
- {
- tile = getNodeTile(n0, p, face_dir, data);
+ MapNode n = n0;
+
+ if (mf == 1) {
p_corrected = p;
face_dir_corrected = face_dir;
- light_source = ndef->get(n0).light_source;
- }
- else
- {
- tile = getNodeTile(n1, p + face_dir, -face_dir, data);
+ } else {
+ n = n1;
p_corrected = p + face_dir;
face_dir_corrected = -face_dir;
- light_source = ndef->get(n1).light_source;
}
+ tile = getNodeTile(n, p_corrected, face_dir_corrected, data);
+ const ContentFeatures &f = ndef->get(n);
+ tile.emissive_light = f.light_source;
// eg. water and glass
- if(equivalent)
+ if (equivalent)
tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
- if(data->m_smooth_lighting == false)
+ if (data->m_smooth_lighting == false)
{
lights[0] = lights[1] = lights[2] = lights[3] =
getFaceLight(n0, n1, face_dir, ndef);
v3s16 face_dir_corrected;
u16 lights[4] = {0,0,0,0};
TileSpec tile;
- u8 light_source = 0;
getTileInfo(data, p, face_dir,
makes_face, p_corrected, face_dir_corrected,
- lights, tile, light_source);
+ lights, tile);
for(u16 j=0; j<MAP_BLOCKSIZE; j++)
{
v3s16 next_face_dir_corrected;
u16 next_lights[4] = {0,0,0,0};
TileSpec next_tile;
- u8 next_light_source = 0;
// If at last position, there is nothing to compare to and
// the face must be drawn anyway
getTileInfo(data, p_next, face_dir,
next_makes_face, next_p_corrected,
next_face_dir_corrected, next_lights,
- next_tile, next_light_source);
+ next_tile);
if(next_makes_face == makes_face
&& next_p_corrected == p_corrected + translate_dir
&& next_lights[3] == lights[3]
&& next_tile == tile
&& tile.rotation == 0
- && next_light_source == light_source
&& (tile.material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
- && (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)) {
+ && (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)
+ && tile.color == next_tile.color
+ && tile.emissive_light == next_tile.emissive_light) {
next_is_different = false;
continuous_tiles_count++;
} else {
}
makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
- sp, face_dir_corrected, scale, light_source,
- dest);
+ sp, face_dir_corrected, scale, dest);
g_profiler->avg("Meshgen: faces drawn by tiling", 0);
for(int i = 1; i < continuous_tiles_count; i++){
lights[2] = next_lights[2];
lights[3] = next_lights[3];
tile = next_tile;
- light_source = next_light_source;
p = p_next;
}
}
const u16 *indices_p = indices;
/*
- Revert triangles for nicer looking gradient if vertices
- 1 and 3 have same color or 0 and 2 have different color.
- getRed() is the day color.
+ Revert triangles for nicer looking gradient if the
+ brightness of vertices 1 and 3 differ less than
+ the brightness of vertices 0 and 2.
*/
- if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
- || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
+ if (abs(f.vertices[0].Color.getAverage()
+ - f.vertices[2].Color.getAverage())
+ > abs(f.vertices[1].Color.getAverage()
+ - f.vertices[3].Color.getAverage()))
indices_p = indices_alternate;
collector.append(f.tile, f.vertices, 4, indices_p, 6);
p.tile.texture = animation_frame.texture;
}
- u32 vertex_count = m_use_tangent_vertices ?
- p.tangent_vertices.size() : p.vertices.size();
- for (u32 j = 0; j < vertex_count; j++) {
- v3f *Normal;
- video::SColor *vc;
- if (m_use_tangent_vertices) {
- vc = &p.tangent_vertices[j].Color;
- Normal = &p.tangent_vertices[j].Normal;
- } else {
- vc = &p.vertices[j].Color;
- Normal = &p.vertices[j].Normal;
- }
- // Note applyFacesShading second parameter is precalculated sqrt
- // value for speed improvement
- // Skip it for lightsources and top faces.
- if (!vc->getBlue()) {
- if (Normal->Y < -0.5) {
- applyFacesShading(*vc, 0.447213);
- } else if (Normal->X > 0.5) {
- applyFacesShading(*vc, 0.670820);
- } else if (Normal->X < -0.5) {
- applyFacesShading(*vc, 0.670820);
- } else if (Normal->Z > 0.5) {
- applyFacesShading(*vc, 0.836660);
- } else if (Normal->Z < -0.5) {
- applyFacesShading(*vc, 0.836660);
- }
- }
- if (!m_enable_shaders) {
- // - Classic lighting (shaders handle this by themselves)
- // Set initial real color and store for later updates
- u8 day = vc->getRed();
- u8 night = vc->getGreen();
- finalColorBlend(*vc, day, night, 1000);
- if (day != night) {
- m_daynight_diffs[i][j] = std::make_pair(day, night);
+ if (!m_enable_shaders) {
+ // Extract colors for day-night animation
+ // Dummy sunlight to handle non-sunlit areas
+ video::SColorf sunlight;
+ get_sunlight_color(&sunlight, 0);
+ u32 vertex_count =
+ m_use_tangent_vertices ?
+ p.tangent_vertices.size() : p.vertices.size();
+ for (u32 j = 0; j < vertex_count; j++) {
+ video::SColor *vc;
+ if (m_use_tangent_vertices) {
+ vc = &p.tangent_vertices[j].Color;
+ } else {
+ vc = &p.vertices[j].Color;
}
+ video::SColor copy(*vc);
+ if (vc->getAlpha() == 0) // No sunlight - no need to animate
+ final_color_blend(vc, copy, sunlight); // Finalize color
+ else // Record color to animate
+ m_daynight_diffs[i][j] = copy;
+
+ // The sunlight ratio has been stored,
+ // delete alpha (for the final rendering).
+ vc->setAlpha(255);
}
}
if (m_enable_vbo) {
m_mesh->setDirty();
}
- for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
+ video::SColorf day_color;
+ get_sunlight_color(&day_color, daynight_ratio);
+ for(std::map<u32, std::map<u32, video::SColor > >::iterator
i = m_daynight_diffs.begin();
i != m_daynight_diffs.end(); ++i)
{
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
- for(std::map<u32, std::pair<u8, u8 > >::iterator
+ for(std::map<u32, video::SColor >::iterator
j = i->second.begin();
j != i->second.end(); ++j)
{
- u8 day = j->second.first;
- u8 night = j->second.second;
- finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
+ final_color_blend(&(vertices[j->first].Color), j->second, day_color);
}
}
m_last_daynight_ratio = daynight_ratio;
void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices,
- v3f pos, video::SColor c)
+ v3f pos, video::SColor c, u8 light_source)
{
if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
p = &prebuffers[prebuffers.size() - 1];
}
+ video::SColor original_c = c;
u32 vertex_count;
if (m_use_tangent_vertices) {
vertex_count = p->tangent_vertices.size();
for (u32 i = 0; i < numVertices; i++) {
+ if (!light_source) {
+ c = original_c;
+ applyFacesShading(c, vertices[i].Normal);
+ }
video::S3DVertexTangents vert(vertices[i].Pos + pos,
vertices[i].Normal, c, vertices[i].TCoords);
p->tangent_vertices.push_back(vert);
} else {
vertex_count = p->vertices.size();
for (u32 i = 0; i < numVertices; i++) {
- video::S3DVertex vert(vertices[i].Pos + pos,
- vertices[i].Normal, c, vertices[i].TCoords);
+ if (!light_source) {
+ c = original_c;
+ applyFacesShading(c, vertices[i].Normal);
+ }
+ video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c,
+ vertices[i].TCoords);
p->vertices.push_back(vert);
}
}
p->indices.push_back(j);
}
}
+
+video::SColor encode_light_and_color(u16 light, const video::SColor &color,
+ u8 emissive_light)
+{
+ // Get components
+ f32 day = (light & 0xff) / 255.0f;
+ f32 night = (light >> 8) / 255.0f;
+ // Add emissive light
+ night += emissive_light * 0.01f;
+ if (night > 255)
+ night = 255;
+ // Since we don't know if the day light is sunlight or
+ // artificial light, assume it is artificial when the night
+ // light bank is also lit.
+ if (day < night)
+ day = 0;
+ else
+ day = day - night;
+ f32 sum = day + night;
+ // Ratio of sunlight:
+ float r;
+ if (sum > 0)
+ r = day / sum;
+ else
+ r = 0;
+ // Average light:
+ float b = (day + night) / 2;
+ return video::SColor(r * 255, b * color.getRed(), b * color.getGreen(),
+ b * color.getBlue());
+}
// Animation info: day/night transitions
// Last daynight_ratio value passed to animate()
u32 m_last_daynight_ratio;
- // For each meshbuffer, maps vertex indices to (day,night) pairs
- std::map<u32, std::map<u32, std::pair<u8, u8> > > m_daynight_diffs;
+ // For each meshbuffer, stores pre-baked colors of sunlit vertices
+ std::map<u32, std::map<u32, video::SColor > > m_daynight_diffs;
// Camera offset info -> do we have to translate the mesh?
v3s16 m_camera_offset;
void append(const TileSpec &material,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices,
- v3f pos, video::SColor c);
+ v3f pos, video::SColor c, u8 light_source);
};
-// This encodes
-// alpha in the A channel of the returned SColor
-// day light (0-255) in the R channel of the returned SColor
-// night light (0-255) in the G channel of the returned SColor
-// light source (0-255) in the B channel of the returned SColor
-inline video::SColor MapBlock_LightColor(u8 alpha, u16 light, u8 light_source=0)
-{
- return video::SColor(alpha, (light & 0xff), (light >> 8), light_source);
-}
+/*!
+ * Encodes light and color of a node.
+ * The result is not the final color, but a
+ * half-baked vertex color.
+ *
+ * \param light the first 8 bits are day light,
+ * the last 8 bits are night light
+ * \param color the node's color
+ * \param emissive_light amount of light the surface emits,
+ * from 0 to LIGHT_SUN.
+ */
+video::SColor encode_light_and_color(u16 light, const video::SColor &color,
+ u8 emissive_light);
// Compute light at node
u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef);
u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef);
u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data);
-// Converts from day + night color values (0..255)
-// and a given daynight_ratio to the final SColor shown on screen.
-void finalColorBlend(video::SColor& result,
- u8 day, u8 night, u32 daynight_ratio);
+/*!
+ * Returns the sunlight's color from the current
+ * day-night ratio.
+ */
+void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio);
+
+/*!
+ * Gives the final SColor shown on screen.
+ *
+ * \param result output color
+ * \param light first 8 bits are day light, second 8 bits are
+ * night light
+ */
+void final_color_blend(video::SColor *result,
+ u16 light, u32 daynight_ratio);
+
+/*!
+ * Gives the final SColor shown on screen.
+ *
+ * \param result output color
+ * \param data the half-baked vertex color
+ * \param dayLight color of the sunlight
+ */
+void final_color_blend(video::SColor *result,
+ const video::SColor &data, const video::SColorf &dayLight);
// Retrieves the TileSpec of a face of a node
// Adds MATERIAL_FLAG_CRACK if the node is cracked
param2 = a_param2;
}
+void MapNode::getColor(const ContentFeatures &f, video::SColor *color) const
+{
+ if (f.palette) {
+ *color = (*f.palette)[param2];
+ return;
+ }
+ *color = f.color;
+}
+
void MapNode::setLight(enum LightBank bank, u8 a_light, const ContentFeatures &f)
{
// If node doesn't contain light data, ignore this
u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
- if(f.param_type_2 == CPT2_FACEDIR)
+ if (f.param_type_2 == CPT2_FACEDIR ||
+ f.param_type_2 == CPT2_COLORED_FACEDIR)
return (getParam2() & 0x1F) % 24;
return 0;
}
u8 MapNode::getWallMounted(INodeDefManager *nodemgr) const
{
const ContentFeatures &f = nodemgr->get(*this);
- if(f.param_type_2 == CPT2_WALLMOUNTED)
+ if (f.param_type_2 == CPT2_WALLMOUNTED ||
+ f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
return getParam2() & 0x07;
return 0;
}
{
ContentParamType2 cpt2 = nodemgr->get(*this).param_type_2;
- if (cpt2 == CPT2_FACEDIR) {
+ if (cpt2 == CPT2_FACEDIR || cpt2 == CPT2_COLORED_FACEDIR) {
static const u8 rotate_facedir[24 * 4] = {
// Table value = rotated facedir
// Columns: 0, 90, 180, 270 degrees rotation around vertical axis
u8 index = facedir * 4 + rot;
param2 &= ~31;
param2 |= rotate_facedir[index];
- } else if (cpt2 == CPT2_WALLMOUNTED) {
+ } else if (cpt2 == CPT2_WALLMOUNTED ||
+ cpt2 == CPT2_COLORED_WALLMOUNTED) {
u8 wmountface = (param2 & 7);
if (wmountface <= 1)
return;
#ifndef MAPNODE_HEADER
#define MAPNODE_HEADER
-#include "irrlichttypes.h"
-#include "irr_v3d.h"
-#include "irr_aabb3d.h"
+#include "irrlichttypes_bloated.h"
#include "light.h"
#include <string>
#include <vector>
param2 = p;
}
+ /*!
+ * Returns the color of the node.
+ *
+ * \param f content features of this node
+ * \param color output, contains the node's color.
+ */
+ void getColor(const ContentFeatures &f, video::SColor *color) const;
+
void setLight(enum LightBank bank, u8 a_light, const ContentFeatures &f);
void setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr);
#define MY_ETLM_READ_ONLY video::ETLM_READ_ONLY
#endif
-static void applyFacesShading(video::SColor& color, float factor)
+inline static void applyShadeFactor(video::SColor& color, float factor)
{
color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255));
}
+void applyFacesShading(video::SColor &color, const v3f &normal)
+{
+ // Many special drawtypes have normals set to 0,0,0 and this
+ // must result in maximum brightness (no face shadng).
+ if (normal.Y < -0.5f)
+ applyShadeFactor (color, 0.447213f);
+ else if (normal.X > 0.5f || normal.X < -0.5f)
+ applyShadeFactor (color, 0.670820f);
+ else if (normal.Z > 0.5f || normal.Z < -0.5f)
+ applyShadeFactor (color, 0.836660f);
+}
+
scene::IAnimatedMesh* createCubeMesh(v3f scale)
{
video::SColor c(255,255,255,255);
}
}
-void shadeMeshFaces(scene::IMesh *mesh)
+void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor)
{
- if (mesh == NULL)
- return;
-
- u32 mc = mesh->getMeshBufferCount();
- for (u32 j = 0; j < mc; j++) {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- const u32 stride = getVertexPitchFromType(buf->getVertexType());
- u32 vertex_count = buf->getVertexCount();
- u8 *vertices = (u8 *)buf->getVertices();
- for (u32 i = 0; i < vertex_count; i++) {
- video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride);
- video::SColor &vc = vertex->Color;
- // Many special drawtypes have normals set to 0,0,0 and this
- // must result in maximum brightness (no face shadng).
- if (vertex->Normal.Y < -0.5f)
- applyFacesShading (vc, 0.447213f);
- else if (vertex->Normal.X > 0.5f || vertex->Normal.X < -0.5f)
- applyFacesShading (vc, 0.670820f);
- else if (vertex->Normal.Z > 0.5f || vertex->Normal.Z < -0.5f)
- applyFacesShading (vc, 0.836660f);
- }
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *) buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++) {
+ video::S3DVertex *vertex = (video::S3DVertex *) (vertices + i * stride);
+ video::SColor *vc = &(vertex->Color);
+ // Reset color
+ *vc = *buffercolor;
+ // Apply shading
+ applyFacesShading(*vc, vertex->Normal);
}
}
#include "irrlichttypes_extrabloated.h"
#include "nodedef.h"
+/*!
+ * Applies shading to a color based on the surface's
+ * normal vector.
+ */
+void applyFacesShading(video::SColor &color, const v3f &normal);
+
/*
Create a new cube mesh.
Vertices are at (+-scale.X/2, +-scale.Y/2, +-scale.Z/2).
*/
void setMeshColor(scene::IMesh *mesh, const video::SColor &color);
-/*
- Shade mesh faces according to their normals
-*/
-
-void shadeMeshFaces(scene::IMesh *mesh);
+void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor);
/*
Set the color of all vertices in the mesh.
scene::IMesh* cloneMesh(scene::IMesh *src_mesh);
/*
- Convert nodeboxes to mesh.
+ Convert nodeboxes to mesh. Each tile goes into a different buffer.
boxes - set of nodeboxes to be converted into cuboids
uv_coords[24] - table of texture uv coords for each cuboid face
expand - factor by which cuboids will be resized
if (it != m_blocks_cache.end()) {
MinimapMapblock *mmblock = it->second;
MinimapPixel *pixel = &mmblock->data[relpos.Z * MAP_BLOCKSIZE + relpos.X];
- if (pixel->id != CONTENT_AIR) {
+ if (pixel->n.param0 != CONTENT_AIR) {
*pixel_height = height + pixel->height;
return pixel;
}
for (s16 x = 0; x < size; x++)
for (s16 z = 0; z < size; z++) {
- u16 id = CONTENT_AIR;
+ MapNode n(CONTENT_AIR);
MinimapPixel *mmpixel = &data->minimap_scan[x + z * size];
if (!is_radar) {
MinimapPixel *cached_pixel =
getMinimapPixel(v3s16(p.X + x, p.Y, p.Z + z), height, &pixel_height);
if (cached_pixel) {
- id = cached_pixel->id;
+ n = cached_pixel->n;
mmpixel->height = pixel_height;
}
} else {
mmpixel->air_count = getAirCount(v3s16(p.X + x, p.Y, p.Z + z), height);
}
- mmpixel->id = id;
+ mmpixel->n = n;
}
}
for (s16 z = 0; z < data->map_size; z++) {
MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size];
- video::SColor c = m_ndef->get(mmpixel->id).minimap_color;
- c.setAlpha(240);
-
- map_image->setPixel(x, data->map_size - z - 1, c);
+ const ContentFeatures &f = m_ndef->get(mmpixel->n);
+ const TileDef *tile = &f.tiledef[0];
+ // Color of the 0th tile (mostly this is the topmost)
+ video::SColor tilecolor;
+ if(tile->has_color)
+ tilecolor = tile->color;
+ else
+ mmpixel->n.getColor(f, &tilecolor);
+ tilecolor.setRed(tilecolor.getRed() * f.minimap_color.getRed() / 255);
+ tilecolor.setGreen(tilecolor.getGreen() * f.minimap_color.getGreen()
+ / 255);
+ tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255);
+ tilecolor.setAlpha(240);
+
+ map_image->setPixel(x, data->map_size - z - 1, tilecolor);
u32 h = mmpixel->height;
heightmap_image->setPixel(x,data->map_size - z - 1,
MapNode n = vmanip->getNodeNoEx(pos + p);
if (!surface_found && n.getContent() != CONTENT_AIR) {
mmpixel->height = y;
- mmpixel->id = n.getContent();
+ mmpixel->n = n;
surface_found = true;
} else if (n.getContent() == CONTENT_AIR) {
air_count++;
}
if (!surface_found)
- mmpixel->id = CONTENT_AIR;
+ mmpixel->n = MapNode(CONTENT_AIR);
mmpixel->air_count = air_count;
}
};
struct MinimapPixel {
- u16 id;
+ //! The topmost node that the minimap displays.
+ MapNode n;
u16 height;
u16 air_count;
- u16 light;
};
struct MinimapMapblock {
serialization of TileAnimation params changed
TAT_SHEET_2D
Removed client-sided chat perdiction
+ PROTOCOL VERSION 30:
+ New ContentFeatures serialization version
+ Add node and tile color and palette
*/
-#define LATEST_PROTOCOL_VERSION 29
+#define LATEST_PROTOCOL_VERSION 30
// Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 13
void TileDef::serialize(std::ostream &os, u16 protocol_version) const
{
- if (protocol_version >= 29)
+ if (protocol_version >= 30)
+ writeU8(os, 4);
+ else if (protocol_version >= 29)
writeU8(os, 3);
else if (protocol_version >= 26)
writeU8(os, 2);
writeU8(os, tileable_horizontal);
writeU8(os, tileable_vertical);
}
+ if (protocol_version >= 30) {
+ writeU8(os, has_color);
+ if (has_color) {
+ writeU8(os, color.getRed());
+ writeU8(os, color.getGreen());
+ writeU8(os, color.getBlue());
+ }
+ }
}
void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
tileable_horizontal = readU8(is);
tileable_vertical = readU8(is);
}
+ if (version >= 4) {
+ has_color = readU8(is);
+ if (has_color) {
+ color.setRed(readU8(is));
+ color.setGreen(readU8(is));
+ color.setBlue(readU8(is));
+ }
+ }
if ((contenfeatures_version < 8) &&
((drawtype == NDT_MESH) ||
connects_to.clear();
connects_to_ids.clear();
connect_sides = 0;
+ color = video::SColor(0xFFFFFFFF);
+ palette_name = "";
+ palette = NULL;
}
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
{
- if(protocol_version < 24){
+ if (protocol_version < 30) {
serializeOld(os, protocol_version);
return;
}
- writeU8(os, protocol_version < 27 ? 7 : 8);
+ // version
+ writeU8(os, 9);
- os<<serializeString(name);
+ // general
+ os << serializeString(name);
writeU16(os, groups.size());
- for(ItemGroupList::const_iterator
- i = groups.begin(); i != groups.end(); ++i){
- os<<serializeString(i->first);
+ for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end();
+ ++i) {
+ os << serializeString(i->first);
writeS16(os, i->second);
}
+ writeU8(os, param_type);
+ writeU8(os, param_type_2);
+
+ // visual
writeU8(os, drawtype);
+ os << serializeString(mesh);
writeF1000(os, visual_scale);
writeU8(os, 6);
- for(u32 i = 0; i < 6; i++)
+ for (u32 i = 0; i < 6; i++)
tiledef[i].serialize(os, protocol_version);
writeU8(os, CF_SPECIAL_COUNT);
- for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){
+ for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
tiledef_special[i].serialize(os, protocol_version);
}
writeU8(os, alpha);
+ writeU8(os, color.getRed());
+ writeU8(os, color.getGreen());
+ writeU8(os, color.getBlue());
+ os << serializeString(palette_name);
+ writeU8(os, waving);
+ writeU8(os, connect_sides);
+ writeU16(os, connects_to_ids.size());
+ for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
+ i != connects_to_ids.end(); ++i)
+ writeU16(os, *i);
writeU8(os, post_effect_color.getAlpha());
writeU8(os, post_effect_color.getRed());
writeU8(os, post_effect_color.getGreen());
writeU8(os, post_effect_color.getBlue());
- writeU8(os, param_type);
- if ((protocol_version < 28) && (param_type_2 == CPT2_MESHOPTIONS))
- writeU8(os, CPT2_NONE);
- else
- writeU8(os, param_type_2);
- writeU8(os, is_ground_content);
+ writeU8(os, leveled);
+
+ // lighting
writeU8(os, light_propagates);
writeU8(os, sunlight_propagates);
+ writeU8(os, light_source);
+
+ // map generation
+ writeU8(os, is_ground_content);
+
+ // interaction
writeU8(os, walkable);
writeU8(os, pointable);
writeU8(os, diggable);
writeU8(os, climbable);
writeU8(os, buildable_to);
- os<<serializeString(""); // legacy: used to be metadata_name
+ writeU8(os, rightclickable);
+ writeU32(os, damage_per_second);
+
+ // liquid
writeU8(os, liquid_type);
- os<<serializeString(liquid_alternative_flowing);
- os<<serializeString(liquid_alternative_source);
+ os << serializeString(liquid_alternative_flowing);
+ os << serializeString(liquid_alternative_source);
writeU8(os, liquid_viscosity);
writeU8(os, liquid_renewable);
- writeU8(os, light_source);
- writeU32(os, damage_per_second);
+ writeU8(os, liquid_range);
+ writeU8(os, drowning);
+ writeU8(os, floodable);
+
+ // node boxes
node_box.serialize(os, protocol_version);
selection_box.serialize(os, protocol_version);
- writeU8(os, legacy_facedir_simple);
- writeU8(os, legacy_wallmounted);
+ collision_box.serialize(os, protocol_version);
+
+ // sound
serializeSimpleSoundSpec(sound_footstep, os);
serializeSimpleSoundSpec(sound_dig, os);
serializeSimpleSoundSpec(sound_dug, os);
- writeU8(os, rightclickable);
- writeU8(os, drowning);
- writeU8(os, leveled);
- writeU8(os, liquid_range);
- writeU8(os, waving);
- // Stuff below should be moved to correct place in a version that otherwise changes
- // the protocol version
- os<<serializeString(mesh);
- collision_box.serialize(os, protocol_version);
- writeU8(os, floodable);
- writeU16(os, connects_to_ids.size());
- for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
- i != connects_to_ids.end(); ++i)
- writeU16(os, *i);
- writeU8(os, connect_sides);
+
+ // legacy
+ writeU8(os, legacy_facedir_simple);
+ writeU8(os, legacy_wallmounted);
+}
+
+void ContentFeatures::correctAlpha()
+{
+ if (alpha == 0 || alpha == 255)
+ return;
+
+ for (u32 i = 0; i < 6; i++) {
+ std::stringstream s;
+ s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha);
+ tiledef[i].name = s.str();
+ }
+
+ for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
+ std::stringstream s;
+ s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha);
+ tiledef_special[i].name = s.str();
+ }
}
void ContentFeatures::deSerialize(std::istream &is)
{
+ // version detection
int version = readU8(is);
- if (version < 7) {
+ if (version < 9) {
deSerializeOld(is, version);
return;
- } else if (version > 8) {
+ } else if (version > 9) {
throw SerializationError("unsupported ContentFeatures version");
}
+ // general
name = deSerializeString(is);
groups.clear();
u32 groups_size = readU16(is);
- for(u32 i = 0; i < groups_size; i++){
+ for (u32 i = 0; i < groups_size; i++) {
std::string name = deSerializeString(is);
int value = readS16(is);
groups[name] = value;
}
- drawtype = (enum NodeDrawType)readU8(is);
+ param_type = (enum ContentParamType) readU8(is);
+ param_type_2 = (enum ContentParamType2) readU8(is);
+ // visual
+ drawtype = (enum NodeDrawType) readU8(is);
+ mesh = deSerializeString(is);
visual_scale = readF1000(is);
- if(readU8(is) != 6)
+ if (readU8(is) != 6)
throw SerializationError("unsupported tile count");
- for(u32 i = 0; i < 6; i++)
+ for (u32 i = 0; i < 6; i++)
tiledef[i].deSerialize(is, version, drawtype);
- if(readU8(is) != CF_SPECIAL_COUNT)
+ if (readU8(is) != CF_SPECIAL_COUNT)
throw SerializationError("unsupported CF_SPECIAL_COUNT");
- for(u32 i = 0; i < CF_SPECIAL_COUNT; i++)
+ for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
tiledef_special[i].deSerialize(is, version, drawtype);
alpha = readU8(is);
+ color.setRed(readU8(is));
+ color.setGreen(readU8(is));
+ color.setBlue(readU8(is));
+ palette_name = deSerializeString(is);
+ waving = readU8(is);
+ connect_sides = readU8(is);
+ u16 connects_to_size = readU16(is);
+ connects_to_ids.clear();
+ for (u16 i = 0; i < connects_to_size; i++)
+ connects_to_ids.insert(readU16(is));
post_effect_color.setAlpha(readU8(is));
post_effect_color.setRed(readU8(is));
post_effect_color.setGreen(readU8(is));
post_effect_color.setBlue(readU8(is));
- param_type = (enum ContentParamType)readU8(is);
- param_type_2 = (enum ContentParamType2)readU8(is);
- is_ground_content = readU8(is);
+ leveled = readU8(is);
+
+ // lighting-related
light_propagates = readU8(is);
sunlight_propagates = readU8(is);
+ light_source = readU8(is);
+ light_source = MYMIN(light_source, LIGHT_MAX);
+
+ // map generation
+ is_ground_content = readU8(is);
+
+ // interaction
walkable = readU8(is);
pointable = readU8(is);
diggable = readU8(is);
climbable = readU8(is);
buildable_to = readU8(is);
- deSerializeString(is); // legacy: used to be metadata_name
- liquid_type = (enum LiquidType)readU8(is);
+ rightclickable = readU8(is);
+ damage_per_second = readU32(is);
+
+ // liquid
+ liquid_type = (enum LiquidType) readU8(is);
liquid_alternative_flowing = deSerializeString(is);
liquid_alternative_source = deSerializeString(is);
liquid_viscosity = readU8(is);
liquid_renewable = readU8(is);
- light_source = readU8(is);
- light_source = MYMIN(light_source, LIGHT_MAX);
- damage_per_second = readU32(is);
+ liquid_range = readU8(is);
+ drowning = readU8(is);
+ floodable = readU8(is);
+
+ // node boxes
node_box.deSerialize(is);
selection_box.deSerialize(is);
- legacy_facedir_simple = readU8(is);
- legacy_wallmounted = readU8(is);
+ collision_box.deSerialize(is);
+
+ // sounds
deSerializeSimpleSoundSpec(sound_footstep, is);
deSerializeSimpleSoundSpec(sound_dig, is);
deSerializeSimpleSoundSpec(sound_dug, is);
- rightclickable = readU8(is);
- drowning = readU8(is);
- leveled = readU8(is);
- liquid_range = readU8(is);
- waving = readU8(is);
- // If you add anything here, insert it primarily inside the try-catch
- // block to not need to increase the version.
- try{
- // Stuff below should be moved to correct place in a version that
- // otherwise changes the protocol version
- mesh = deSerializeString(is);
- collision_box.deSerialize(is);
- floodable = readU8(is);
- u16 connects_to_size = readU16(is);
- connects_to_ids.clear();
- for (u16 i = 0; i < connects_to_size; i++)
- connects_to_ids.insert(readU16(is));
- connect_sides = readU8(is);
- }catch(SerializationError &e) {};
+
+ // read legacy properties
+ legacy_facedir_simple = readU8(is);
+ legacy_wallmounted = readU8(is);
}
#ifndef SERVER
void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
TileDef *tiledef, u32 shader_id, bool use_normal_texture,
- bool backface_culling, u8 alpha, u8 material_type)
+ bool backface_culling, u8 material_type)
{
tile->shader_id = shader_id;
tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
- tile->alpha = alpha;
tile->material_type = material_type;
// Normal texture and shader flags texture
if (tiledef->tileable_vertical)
tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
+ // Color
+ tile->has_color = tiledef->has_color;
+ if (tiledef->has_color)
+ tile->color = tiledef->color;
+ else
+ tile->color = color;
+
// Animation parameters
int frame_count = 1;
if (tile->material_flags & MATERIAL_FLAG_ANIMATION) {
is_water_surface = true;
}
+ // Vertex alpha is no longer supported, correct if necessary.
+ correctAlpha();
+
u32 tile_shader[6];
for (u16 j = 0; j < 6; j++) {
tile_shader[j] = shdsrc->getShader("nodes_shader",
for (u16 j = 0; j < 6; j++) {
fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
tsettings.use_normal_texture,
- tiledef[j].backface_culling, alpha, material_type);
+ tiledef[j].backface_culling, material_type);
}
// Special tiles (fill in f->special_tiles[])
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
tile_shader[j], tsettings.use_normal_texture,
- tiledef_special[j].backface_culling, alpha, material_type);
+ tiledef_special[j].backface_culling, material_type);
}
if ((drawtype == NDT_MESH) && (mesh != "")) {
}
//Cache 6dfacedir and wallmounted rotated clones of meshes
- if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_FACEDIR)) {
+ if (tsettings.enable_mesh_cache && mesh_ptr[0] &&
+ (param_type_2 == CPT2_FACEDIR
+ || param_type_2 == CPT2_COLORED_FACEDIR)) {
for (u16 j = 1; j < 24; j++) {
mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
rotateMeshBy6dFacedir(mesh_ptr[j], j);
recalculateBoundingBox(mesh_ptr[j]);
meshmanip->recalculateNormals(mesh_ptr[j], true, false);
}
- } else if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_WALLMOUNTED)) {
- static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
+ } else if (tsettings.enable_mesh_cache && mesh_ptr[0]
+ && (param_type_2 == CPT2_WALLMOUNTED ||
+ param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
+ static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 };
for (u16 j = 1; j < 6; j++) {
mesh_ptr[j] = cloneMesh(mesh_ptr[0]);
rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]);
virtual void removeNode(const std::string &name);
virtual void updateAliases(IItemDefManager *idef);
virtual void applyTextureOverrides(const std::string &override_filepath);
+ //! Returns a palette or NULL if not found. Only on client.
+ std::vector<video::SColor> *getPalette(const ContentFeatures &f,
+ const IGameDef *gamedef);
virtual void updateTextures(IGameDef *gamedef,
void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
void *progress_cbk_args);
// Next possibly free id
content_t m_next_id;
+ // Maps image file names to loaded palettes.
+ UNORDERED_MAP<std::string, std::vector<video::SColor> > m_palettes;
+
// NodeResolvers to callback once node registration has ended
std::vector<NodeResolver *> m_pending_resolve_callbacks;
if (nodebox.type == NODEBOX_LEVELED) {
half_processed.MaxEdge.Y = +BS / 2;
}
- if (features.param_type_2 == CPT2_FACEDIR) {
+ if (features.param_type_2 == CPT2_FACEDIR ||
+ features.param_type_2 == CPT2_COLORED_FACEDIR) {
// Get maximal coordinate
f32 coords[] = {
fabsf(half_processed.MinEdge.X),
}
}
+std::vector<video::SColor> *CNodeDefManager::getPalette(
+ const ContentFeatures &f, const IGameDef *gamedef)
+{
+#ifndef SERVER
+ // This works because colors always use the most significant bits
+ // of param2. If you add a new colored type which uses param2
+ // in a more advanced way, you should change this code, too.
+ u32 palette_pixels = 0;
+ switch (f.param_type_2) {
+ case CPT2_COLOR:
+ palette_pixels = 256;
+ break;
+ case CPT2_COLORED_FACEDIR:
+ palette_pixels = 8;
+ break;
+ case CPT2_COLORED_WALLMOUNTED:
+ palette_pixels = 32;
+ break;
+ default:
+ return NULL;
+ }
+ // This many param2 values will have the same color
+ u32 step = 256 / palette_pixels;
+ const std::string &name = f.palette_name;
+ if (name == "")
+ return NULL;
+ Client *client = (Client *) gamedef;
+ ITextureSource *tsrc = client->tsrc();
+
+ UNORDERED_MAP<std::string, std::vector<video::SColor> >::iterator it =
+ m_palettes.find(name);
+ if (it == m_palettes.end()) {
+ // Create palette
+ if (!tsrc->isKnownSourceImage(name)) {
+ warningstream << "CNodeDefManager::getPalette(): palette \"" << name
+ << "\" could not be loaded." << std::endl;
+ return NULL;
+ }
+ video::IImage *img = tsrc->generateImage(name);
+ std::vector<video::SColor> new_palette;
+ u32 w = img->getDimension().Width;
+ u32 h = img->getDimension().Height;
+ // Real area of the image
+ u32 area = h * w;
+ if (area != palette_pixels)
+ warningstream << "CNodeDefManager::getPalette(): the "
+ << "specified palette image \"" << name << "\" does not "
+ << "contain exactly " << palette_pixels
+ << " pixels." << std::endl;
+ if (area > palette_pixels)
+ area = palette_pixels;
+ // For each pixel in the image
+ for (u32 i = 0; i < area; i++) {
+ video::SColor c = img->getPixel(i % w, i / w);
+ // Fill in palette with 'step' colors
+ for (u32 j = 0; j < step; j++)
+ new_palette.push_back(c);
+ }
+ img->drop();
+ // Fill in remaining elements
+ while (new_palette.size() < 256)
+ new_palette.push_back(video::SColor(0xFFFFFFFF));
+ m_palettes[name] = new_palette;
+ it = m_palettes.find(name);
+ }
+ if (it != m_palettes.end())
+ return &((*it).second);
+
+#endif
+ return NULL;
+}
+
void CNodeDefManager::updateTextures(IGameDef *gamedef,
void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
void *progress_callback_args)
TextureSettings tsettings;
tsettings.readSettings();
+ m_palettes.clear();
u32 size = m_content_features.size();
for (u32 i = 0; i < size; i++) {
- m_content_features[i].updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
+ ContentFeatures *f = &(m_content_features[i]);
+ f->palette = getPalette(*f, gamedef);
+ f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
progress_callback(progress_callback_args, i, size);
}
#endif
//// Serialization of old ContentFeatures formats
void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
{
+ u8 compatible_param_type_2 = param_type_2;
+ if ((protocol_version < 28)
+ && (compatible_param_type_2 == CPT2_MESHOPTIONS))
+ compatible_param_type_2 = CPT2_NONE;
+ else if (protocol_version < 30) {
+ if (compatible_param_type_2 == CPT2_COLOR)
+ compatible_param_type_2 = CPT2_NONE;
+ else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR)
+ compatible_param_type_2 = CPT2_FACEDIR;
+ else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED)
+ compatible_param_type_2 = CPT2_WALLMOUNTED;
+ }
+
if (protocol_version == 13)
{
writeU8(os, 5); // version
writeU8(os, post_effect_color.getGreen());
writeU8(os, post_effect_color.getBlue());
writeU8(os, param_type);
- writeU8(os, param_type_2);
+ writeU8(os, compatible_param_type_2);
writeU8(os, is_ground_content);
writeU8(os, light_propagates);
writeU8(os, sunlight_propagates);
os<<serializeString(name);
writeU16(os, groups.size());
for (ItemGroupList::const_iterator
- i = groups.begin(); i != groups.end(); ++i) {
- os<<serializeString(i->first);
- writeS16(os, i->second);
+ i = groups.begin(); i != groups.end(); ++i) {
+ os<<serializeString(i->first);
+ writeS16(os, i->second);
}
writeU8(os, drawtype);
writeF1000(os, visual_scale);
writeU8(os, post_effect_color.getGreen());
writeU8(os, post_effect_color.getBlue());
writeU8(os, param_type);
- writeU8(os, param_type_2);
+ writeU8(os, compatible_param_type_2);
writeU8(os, is_ground_content);
writeU8(os, light_propagates);
writeU8(os, sunlight_propagates);
writeU8(os, drowning);
writeU8(os, leveled);
writeU8(os, liquid_range);
+ }
+ else if(protocol_version >= 24 && protocol_version < 30) {
+ writeU8(os, protocol_version < 27 ? 7 : 8);
+
+ os << serializeString(name);
+ writeU16(os, groups.size());
+ for (ItemGroupList::const_iterator i = groups.begin();
+ i != groups.end(); ++i) {
+ os << serializeString(i->first);
+ writeS16(os, i->second);
+ }
+ writeU8(os, drawtype);
+ writeF1000(os, visual_scale);
+ writeU8(os, 6);
+ for (u32 i = 0; i < 6; i++)
+ tiledef[i].serialize(os, protocol_version);
+ writeU8(os, CF_SPECIAL_COUNT);
+ for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
+ tiledef_special[i].serialize(os, protocol_version);
+ writeU8(os, alpha);
+ writeU8(os, post_effect_color.getAlpha());
+ writeU8(os, post_effect_color.getRed());
+ writeU8(os, post_effect_color.getGreen());
+ writeU8(os, post_effect_color.getBlue());
+ writeU8(os, param_type);
+ writeU8(os, compatible_param_type_2);
+ writeU8(os, is_ground_content);
+ writeU8(os, light_propagates);
+ writeU8(os, sunlight_propagates);
+ writeU8(os, walkable);
+ writeU8(os, pointable);
+ writeU8(os, diggable);
+ writeU8(os, climbable);
+ writeU8(os, buildable_to);
+ os << serializeString(""); // legacy: used to be metadata_name
+ writeU8(os, liquid_type);
+ os << serializeString(liquid_alternative_flowing);
+ os << serializeString(liquid_alternative_source);
+ writeU8(os, liquid_viscosity);
+ writeU8(os, liquid_renewable);
+ writeU8(os, light_source);
+ writeU32(os, damage_per_second);
+ node_box.serialize(os, protocol_version);
+ selection_box.serialize(os, protocol_version);
+ writeU8(os, legacy_facedir_simple);
+ writeU8(os, legacy_wallmounted);
+ serializeSimpleSoundSpec(sound_footstep, os);
+ serializeSimpleSoundSpec(sound_dig, os);
+ serializeSimpleSoundSpec(sound_dug, os);
+ writeU8(os, rightclickable);
+ writeU8(os, drowning);
+ writeU8(os, leveled);
+ writeU8(os, liquid_range);
+ writeU8(os, waving);
+ os << serializeString(mesh);
+ collision_box.serialize(os, protocol_version);
+ writeU8(os, floodable);
+ writeU16(os, connects_to_ids.size());
+ for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
+ i != connects_to_ids.end(); ++i)
+ writeU16(os, *i);
+ writeU8(os, connect_sides);
} else
throw SerializationError("ContentFeatures::serialize(): "
"Unsupported version requested");
drowning = readU8(is);
leveled = readU8(is);
liquid_range = readU8(is);
- } else {
+ } else if (version == 7 || version == 8){
+ name = deSerializeString(is);
+ groups.clear();
+ u32 groups_size = readU16(is);
+ for (u32 i = 0; i < groups_size; i++) {
+ std::string name = deSerializeString(is);
+ int value = readS16(is);
+ groups[name] = value;
+ }
+ drawtype = (enum NodeDrawType) readU8(is);
+
+ visual_scale = readF1000(is);
+ if (readU8(is) != 6)
+ throw SerializationError("unsupported tile count");
+ for (u32 i = 0; i < 6; i++)
+ tiledef[i].deSerialize(is, version, drawtype);
+ if (readU8(is) != CF_SPECIAL_COUNT)
+ throw SerializationError("unsupported CF_SPECIAL_COUNT");
+ for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
+ tiledef_special[i].deSerialize(is, version, drawtype);
+ alpha = readU8(is);
+ post_effect_color.setAlpha(readU8(is));
+ post_effect_color.setRed(readU8(is));
+ post_effect_color.setGreen(readU8(is));
+ post_effect_color.setBlue(readU8(is));
+ param_type = (enum ContentParamType) readU8(is);
+ param_type_2 = (enum ContentParamType2) readU8(is);
+ is_ground_content = readU8(is);
+ light_propagates = readU8(is);
+ sunlight_propagates = readU8(is);
+ walkable = readU8(is);
+ pointable = readU8(is);
+ diggable = readU8(is);
+ climbable = readU8(is);
+ buildable_to = readU8(is);
+ deSerializeString(is); // legacy: used to be metadata_name
+ liquid_type = (enum LiquidType) readU8(is);
+ liquid_alternative_flowing = deSerializeString(is);
+ liquid_alternative_source = deSerializeString(is);
+ liquid_viscosity = readU8(is);
+ liquid_renewable = readU8(is);
+ light_source = readU8(is);
+ light_source = MYMIN(light_source, LIGHT_MAX);
+ damage_per_second = readU32(is);
+ node_box.deSerialize(is);
+ selection_box.deSerialize(is);
+ legacy_facedir_simple = readU8(is);
+ legacy_wallmounted = readU8(is);
+ deSerializeSimpleSoundSpec(sound_footstep, is);
+ deSerializeSimpleSoundSpec(sound_dig, is);
+ deSerializeSimpleSoundSpec(sound_dug, is);
+ rightclickable = readU8(is);
+ drowning = readU8(is);
+ leveled = readU8(is);
+ liquid_range = readU8(is);
+ waving = readU8(is);
+ try {
+ mesh = deSerializeString(is);
+ collision_box.deSerialize(is);
+ floodable = readU8(is);
+ u16 connects_to_size = readU16(is);
+ connects_to_ids.clear();
+ for (u16 i = 0; i < connects_to_size; i++)
+ connects_to_ids.insert(readU16(is));
+ connect_sides = readU8(is);
+ } catch (SerializationError &e) {};
+ }else{
throw SerializationError("unsupported ContentFeatures version");
}
}
// does to node declare usable faces?
if (f2.connect_sides > 0) {
- if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
- static const u8 rot[33 * 4] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
- 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 32, 16, 8, 4 // 32 - left
- };
- return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
+ if ((f2.param_type_2 == CPT2_FACEDIR ||
+ f2.param_type_2 == CPT2_COLORED_FACEDIR)
+ && (connect_face >= 4)) {
+ static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, // 4 - back
+ 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, // 8 - right
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, // 16 - front
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left
+ };
+ return (f2.connect_sides
+ & rot[(connect_face * 4) + (to.param2 & 0x1F)]);
}
return (f2.connect_sides & connect_face);
}
// 2D rotation for things like plants
CPT2_DEGROTATE,
// Mesh options for plants
- CPT2_MESHOPTIONS
+ CPT2_MESHOPTIONS,
+ // Index for palette
+ CPT2_COLOR,
+ // 3 bits of palette index, then facedir
+ CPT2_COLORED_FACEDIR,
+ // 5 bits of palette index, then wallmounted
+ CPT2_COLORED_WALLMOUNTED
};
enum LiquidType
bool backface_culling; // Takes effect only in special cases
bool tileable_horizontal;
bool tileable_vertical;
+ //! If true, the tile has its own color.
+ bool has_color;
+ //! The color of the tile.
+ video::SColor color;
+
struct TileAnimationParams animation;
TileDef()
backface_culling = true;
tileable_horizontal = true;
tileable_vertical = true;
+ has_color = false;
+ color = video::SColor(0xFFFFFFFF);
animation.type = TAT_NONE;
}
{
/*
Cached stuff
- */
+ */
#ifndef SERVER
// 0 1 2 3 4 5
// up down right left back front
/*
Actual data
- */
+ */
+
+ // --- GENERAL PROPERTIES ---
std::string name; // "" = undefined node
ItemGroupList groups; // Same as in itemdef
+ // Type of MapNode::param1
+ ContentParamType param_type;
+ // Type of MapNode::param2
+ ContentParamType2 param_type_2;
+
+ // --- VISUAL PROPERTIES ---
- // Visual definition
enum NodeDrawType drawtype;
std::string mesh;
#ifndef SERVER
float visual_scale; // Misc. scale parameter
TileDef tiledef[6];
TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
+ // If 255, the node is opaque.
+ // Otherwise it uses texture alpha.
u8 alpha;
-
+ // The color of the node.
+ video::SColor color;
+ std::string palette_name;
+ std::vector<video::SColor> *palette;
+ // Used for waving leaves/plants
+ u8 waving;
+ // for NDT_CONNECTED pairing
+ u8 connect_sides;
+ std::vector<std::string> connects_to;
+ std::set<content_t> connects_to_ids;
// Post effect color, drawn when the camera is inside the node.
video::SColor post_effect_color;
+ // Flowing liquid or snow, value = default level
+ u8 leveled;
+
+ // --- LIGHTING-RELATED ---
- // Type of MapNode::param1
- ContentParamType param_type;
- // Type of MapNode::param2
- ContentParamType2 param_type_2;
- // True for all ground-like things like stone and mud, false for eg. trees
- bool is_ground_content;
bool light_propagates;
bool sunlight_propagates;
+ // Amount of light the node emits
+ u8 light_source;
+
+ // --- MAP GENERATION ---
+
+ // True for all ground-like things like stone and mud, false for eg. trees
+ bool is_ground_content;
+
+ // --- INTERACTION PROPERTIES ---
+
// This is used for collision detection.
// Also for general solidness queries.
bool walkable;
bool climbable;
// Player can build on these
bool buildable_to;
- // Liquids flow into and replace node
- bool floodable;
// Player cannot build to these (placement prediction disabled)
bool rightclickable;
- // Flowing liquid or snow, value = default level
- u8 leveled;
+ u32 damage_per_second;
+
+ // --- LIQUID PROPERTIES ---
+
// Whether the node is non-liquid, source liquid or flowing liquid
enum LiquidType liquid_type;
// If the content is liquid, this is the flowing version of the liquid.
// Number of flowing liquids surrounding source
u8 liquid_range;
u8 drowning;
- // Amount of light the node emits
- u8 light_source;
- u32 damage_per_second;
+ // Liquids flow into and replace node
+ bool floodable;
+
+ // --- NODEBOXES ---
+
NodeBox node_box;
NodeBox selection_box;
NodeBox collision_box;
- // Used for waving leaves/plants
- u8 waving;
- // Compatibility with old maps
- // Set to true if paramtype used to be 'facedir_simple'
- bool legacy_facedir_simple;
- // Set to true if wall_mounted used to be set to true
- bool legacy_wallmounted;
- // for NDT_CONNECTED pairing
- u8 connect_sides;
- // Sound properties
+ // --- SOUND PROPERTIES ---
+
SimpleSoundSpec sound_footstep;
SimpleSoundSpec sound_dig;
SimpleSoundSpec sound_dug;
- std::vector<std::string> connects_to;
- std::set<content_t> connects_to_ids;
+ // --- LEGACY ---
+
+ // Compatibility with old maps
+ // Set to true if paramtype used to be 'facedir_simple'
+ bool legacy_facedir_simple;
+ // Set to true if wall_mounted used to be set to true
+ bool legacy_wallmounted;
/*
Methods
void deSerialize(std::istream &is);
void serializeOld(std::ostream &os, u16 protocol_version) const;
void deSerializeOld(std::istream &is, int version);
+ /*!
+ * Since vertex alpha is no lnger supported, this method
+ * adds instructions to the texture names to blend alpha there.
+ *
+ * tiledef, tiledef_special and alpha must be initialized
+ * before calling this.
+ */
+ void correctAlpha();
/*
Some handy methods
#ifndef SERVER
void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef,
u32 shader_id, bool use_normal_texture, bool backface_culling,
- u8 alpha, u8 material_type);
+ u8 material_type);
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings);
#endif
v2f texpos,
v2f texsize,
const struct TileAnimationParams &anim,
- u8 glow
+ u8 glow,
+ video::SColor color
):
scene::ISceneNode(smgr->getRootSceneNode(), smgr)
{
m_animation_frame = 0;
m_animation_time = 0.0;
+ // Color
+ m_base_color = color;
+ m_color = color;
+
// Particle related
m_pos = pos;
m_velocity = velocity;
else
light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
- m_light = decode_light(light + m_glow);
+ u8 m_light = decode_light(light + m_glow);
+ m_color.set(255,
+ m_light * m_base_color.getRed() / 255,
+ m_light * m_base_color.getGreen() / 255,
+ m_light * m_base_color.getBlue() / 255);
}
void Particle::updateVertices()
{
- video::SColor c(255, m_light, m_light, m_light);
f32 tx0, tx1, ty0, ty1;
if (m_animation.type != TAT_NONE) {
ty1 = m_texpos.Y + m_texsize.Y;
}
- m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
- c, tx0, ty1);
- m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0,
- c, tx1, ty1);
- m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0,
- c, tx1, ty0);
- m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0,
- c, tx0, ty0);
+ m_vertices[0] = video::S3DVertex(-m_size / 2, -m_size / 2,
+ 0, 0, 0, 0, m_color, tx0, ty1);
+ m_vertices[1] = video::S3DVertex(m_size / 2, -m_size / 2,
+ 0, 0, 0, 0, m_color, tx1, ty1);
+ m_vertices[2] = video::S3DVertex(m_size / 2, m_size / 2,
+ 0, 0, 0, 0, m_color, tx1, ty0);
+ m_vertices[3] = video::S3DVertex(-m_size / 2, m_size / 2,
+ 0, 0, 0, 0, m_color, tx0, ty0);
v3s16 camera_offset = m_env->getCameraOffset();
for(u16 i=0; i<4; i++)
}
}
-void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
- LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+void ParticleManager::addDiggingParticles(IGameDef* gamedef,
+ scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos,
+ const MapNode &n, const ContentFeatures &f)
{
for (u16 j = 0; j < 32; j++) // set the amount of particles here
{
- addNodeParticle(gamedef, smgr, player, pos, tiles);
+ addNodeParticle(gamedef, smgr, player, pos, n, f);
}
}
-void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
- LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+void ParticleManager::addPunchingParticles(IGameDef* gamedef,
+ scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos,
+ const MapNode &n, const ContentFeatures &f)
{
- addNodeParticle(gamedef, smgr, player, pos, tiles);
+ addNodeParticle(gamedef, smgr, player, pos, n, f);
}
-void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
- LocalPlayer *player, v3s16 pos, const TileSpec tiles[])
+void ParticleManager::addNodeParticle(IGameDef* gamedef,
+ scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos,
+ const MapNode &n, const ContentFeatures &f)
{
// Texture
u8 texid = myrand_range(0, 5);
+ const TileSpec &tile = f.tiles[texid];
video::ITexture *texture;
struct TileAnimationParams anim;
anim.type = TAT_NONE;
// Only use first frame of animated texture
- if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION)
- texture = tiles[texid].frames[0].texture;
+ if (tile.material_flags & MATERIAL_FLAG_ANIMATION)
+ texture = tile.frames[0].texture;
else
- texture = tiles[texid].texture;
+ texture = tile.texture;
float size = rand() % 64 / 512.;
float visual_size = BS * size;
(f32) pos.Z + rand() %100 /200. - 0.25
);
+ video::SColor color;
+ if (tile.has_color)
+ color = tile.color;
+ else
+ n.getColor(f, &color);
+
Particle* toadd = new Particle(
gamedef,
smgr,
texpos,
texsize,
anim,
- 0);
+ 0,
+ color);
addParticle(toadd);
}
struct ClientEvent;
class ParticleManager;
class ClientEnvironment;
+class MapNode;
+class ContentFeatures;
class Particle : public scene::ISceneNode
{
v2f texpos,
v2f texsize,
const struct TileAnimationParams &anim,
- u8 glow
+ u8 glow,
+ video::SColor color = video::SColor(0xFFFFFFFF)
);
~Particle();
v3f m_acceleration;
LocalPlayer *m_player;
float m_size;
- u8 m_light;
+ //! Color without lighting
+ video::SColor m_base_color;
+ //! Final rendered color
+ video::SColor m_color;
bool m_collisiondetection;
bool m_collision_removal;
bool m_vertical;
scene::ISceneManager* smgr, LocalPlayer *player);
void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
- LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
+ LocalPlayer *player, v3s16 pos, const MapNode &n,
+ const ContentFeatures &f);
void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr,
- LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
+ LocalPlayer *player, v3s16 pos, const MapNode &n,
+ const ContentFeatures &f);
void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr,
- LocalPlayer *player, v3s16 pos, const TileSpec tiles[]);
+ LocalPlayer *player, v3s16 pos, const MapNode &n,
+ const ContentFeatures &f);
protected:
void addParticle(Particle* toadd);
L, index, "tileable_horizontal", default_tiling);
tiledef.tileable_vertical = getboolfield_default(
L, index, "tileable_vertical", default_tiling);
+ // color = ...
+ lua_getfield(L, index, "color");
+ tiledef.has_color = read_color(L, -1, &tiledef.color);
+ lua_pop(L, 1);
// animation = {}
lua_getfield(L, index, "animation");
tiledef.animation = read_animation_definition(L, -1);
if (usealpha)
f.alpha = 0;
+ // Read node color.
+ lua_getfield(L, index, "color");
+ read_color(L, -1, &f.color);
+ lua_pop(L, 1);
+
+ getstringfield(L, index, "palette", f.palette_name);
+
/* Other stuff */
lua_getfield(L, index, "post_effect_color");
f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2",
ScriptApiNode::es_ContentParamType2, CPT2_NONE);
+ if (f.palette_name != "" &&
+ !(f.param_type_2 == CPT2_COLOR ||
+ f.param_type_2 == CPT2_COLORED_FACEDIR ||
+ f.param_type_2 == CPT2_COLORED_WALLMOUNTED))
+ warningstream << "Node " << f.name.c_str()
+ << " has a palette, but not a suitable paramtype2." << std::endl;
+
// Warn about some deprecated fields
warn_if_field_exists(L, index, "wall_mounted",
"Deprecated; use paramtype2 = 'wallmounted'");
{CPT2_LEVELED, "leveled"},
{CPT2_DEGROTATE, "degrotate"},
{CPT2_MESHOPTIONS, "meshoptions"},
+ {CPT2_COLOR, "color"},
+ {CPT2_COLORED_FACEDIR, "colorfacedir"},
+ {CPT2_COLORED_WALLMOUNTED, "colorwallmounted"},
{0, NULL},
};
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_TRANSPARENT:
- shaderinfo.base_material = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+ shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
shaderinfo.base_material = video::EMT_SOLID;
u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
m_material_type = shdrsrc->getShaderInfo(shader_id).material;
}
+ m_colors.clear();
// If wield_image is defined, it overrides everything else
if (def.wield_image != "") {
material_count = 6;
}
for (u32 i = 0; i < material_count; ++i) {
+ const TileSpec *tile = &(f.tiles[i]);
video::SMaterial &material = m_meshnode->getMaterial(i);
material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
- bool animated = (f.tiles[i].animation_frame_count > 1);
+ bool animated = (tile->animation_frame_count > 1);
if (animated) {
- FrameSpec animation_frame = f.tiles[i].frames[0];
+ FrameSpec animation_frame = tile->frames[0];
material.setTexture(0, animation_frame.texture);
} else {
- material.setTexture(0, f.tiles[i].texture);
+ material.setTexture(0, tile->texture);
}
+ m_colors.push_back(tile->color);
material.MaterialType = m_material_type;
if (m_enable_shaders) {
- if (f.tiles[i].normal_texture) {
+ if (tile->normal_texture) {
if (animated) {
- FrameSpec animation_frame = f.tiles[i].frames[0];
+ FrameSpec animation_frame = tile->frames[0];
material.setTexture(1, animation_frame.normal_texture);
} else {
- material.setTexture(1, f.tiles[i].normal_texture);
+ material.setTexture(1, tile->normal_texture);
}
}
- material.setTexture(2, f.tiles[i].flags_texture);
+ material.setTexture(2, tile->flags_texture);
}
}
return;
changeToMesh(NULL);
}
-void WieldMeshSceneNode::setColor(video::SColor color)
+void WieldMeshSceneNode::setColor(video::SColor c)
{
assert(!m_lighting);
- setMeshColor(m_meshnode->getMesh(), color);
- shadeMeshFaces(m_meshnode->getMesh());
+ scene::IMesh *mesh=m_meshnode->getMesh();
+ if (mesh == NULL)
+ return;
+
+ u8 red = c.getRed();
+ u8 green = c.getGreen();
+ u8 blue = c.getBlue();
+ u32 mc = mesh->getMeshBufferCount();
+ for (u32 j = 0; j < mc; j++) {
+ video::SColor bc(0xFFFFFFFF);
+ if (m_colors.size() > j)
+ bc = m_colors[j];
+ video::SColor buffercolor(255,
+ bc.getRed() * red / 255,
+ bc.getGreen() * green / 255,
+ bc.getBlue() * blue / 255);
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
+ colorizeMeshBuffer(buf, &buffercolor);
+ }
}
void WieldMeshSceneNode::render()
} else if (f.drawtype == NDT_PLANTLIKE) {
mesh = getExtrudedMesh(tsrc,
tsrc->getTextureName(f.tiles[0].texture_id));
- return mesh;
} else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
|| f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
mesh = cloneMesh(g_extrusion_mesh_cache->createCube());
mesh = cloneMesh(mapblock_mesh.getMesh());
translateMesh(mesh, v3f(-BS, -BS, -BS));
scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
- rotateMeshXZby(mesh, -45);
- rotateMeshYZby(mesh, -30);
u32 mc = mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; ++i) {
material1.setTexture(3, material2.getTexture(3));
material1.MaterialType = material2.MaterialType;
}
- return mesh;
}
- shadeMeshFaces(mesh);
- rotateMeshXZby(mesh, -45);
- rotateMeshYZby(mesh, -30);
-
u32 mc = mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; ++i) {
- video::SMaterial &material = mesh->getMeshBuffer(i)->getMaterial();
+ const TileSpec *tile = &(f.tiles[i]);
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+ colorizeMeshBuffer(buf, &tile->color);
+ video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_TRILINEAR_FILTER, false);
material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_LIGHTING, false);
- if (f.tiles[i].animation_frame_count > 1) {
- FrameSpec animation_frame = f.tiles[i].frames[0];
+ if (tile->animation_frame_count > 1) {
+ FrameSpec animation_frame = tile->frames[0];
material.setTexture(0, animation_frame.texture);
} else {
- material.setTexture(0, f.tiles[i].texture);
+ material.setTexture(0, tile->texture);
}
}
+
+ rotateMeshXZby(mesh, -45);
+ rotateMeshYZby(mesh, -30);
return mesh;
}
return NULL;
bool m_anisotropic_filter;
bool m_bilinear_filter;
bool m_trilinear_filter;
+ /*!
+ * Stores the colors of the mesh's mesh buffers.
+ * This does not include lighting.
+ */
+ std::vector<video::SColor> m_colors;
// Bounding box culling is disabled for this type of scene node,
// so this variable is just required so we can implement