Add hardware node coloring. Includes:

- Increase ContentFeatures serialization version
- Color property and palettes for nodes
- paramtype2 = "color", "colored facedir" or "colored wallmounted"
This commit is contained in:
Dániel Juhász 2017-01-12 15:46:30 +01:00 committed by Ekdohibs
parent 43822de5c6
commit d04d8aba70
27 changed files with 1207 additions and 554 deletions

@ -1,7 +1,8 @@
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;
@ -14,6 +15,8 @@ varying vec3 tsEyeVec;
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;
@ -119,31 +122,23 @@ float disp_z;
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);
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);
// 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);
}

@ -1,7 +1,8 @@
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;
@ -13,6 +14,8 @@ varying vec3 lightVec;
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;
@ -112,31 +115,23 @@ void main(void)
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);
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);
// 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);
}

@ -1,7 +1,6 @@
uniform mat4 mWorldViewProj;
uniform mat4 mWorld;
uniform float dayNightRatio;
uniform vec3 eyePosition;
uniform float animationTimer;

@ -638,6 +638,19 @@ node definition:
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 = {
@ -3707,6 +3720,9 @@ Definition tables
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)
@ -3749,8 +3765,17 @@ Definition tables
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

@ -378,9 +378,6 @@ public:
video::ITexture* generateTextureFromMesh(
const TextureFromMeshParams &params);
// 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);

@ -108,6 +108,12 @@ public:
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 &params)=0;
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
@ -192,7 +198,6 @@ struct TileSpec
texture(NULL),
normal_texture(NULL),
flags_texture(NULL),
alpha(255),
material_type(TILE_MATERIAL_BASIC),
material_flags(
//0 // <- DEBUG, Use the one below
@ -201,22 +206,30 @@ struct TileSpec
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);
@ -233,7 +246,7 @@ struct TileSpec
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;
@ -274,8 +287,6 @@ struct TileSpec
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;
@ -286,5 +297,14 @@ struct TileSpec
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

@ -334,9 +334,7 @@ void ClientEnvironment::step(float dtime)
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);
}
/*

@ -36,24 +36,25 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// 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
// 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.
// 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,
@ -66,38 +67,53 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
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++)
@ -164,6 +180,31 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
}
}
// 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)
{
@ -181,6 +222,18 @@ static inline int NeighborToIndex(const v3s16 &pos)
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, &copy.color);
return copy;
}
/*
TODO: Fix alpha blending for special nodes
Currently only the last element rendered is blended correct
@ -227,8 +280,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
/*
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));
@ -237,9 +295,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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
*/
@ -285,15 +340,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// 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),
};
/*
@ -359,10 +417,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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);
@ -380,8 +438,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
/*
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));
@ -404,7 +462,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// 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);
@ -552,7 +613,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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;
@ -574,15 +635,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// 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),
};
/*
@ -656,10 +720,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
{
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
@ -720,8 +784,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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
@ -731,13 +795,17 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// 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
@ -784,12 +852,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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];
@ -801,14 +877,15 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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;
@ -947,7 +1024,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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++)
@ -971,16 +1049,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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,
@ -1004,22 +1087,24 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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
@ -1046,7 +1131,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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
@ -1087,7 +1173,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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;
@ -1132,7 +1219,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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
@ -1302,7 +1390,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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;
@ -1437,7 +1526,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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;
@ -1456,7 +1546,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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;
@ -1477,11 +1568,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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
@ -1503,11 +1594,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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:
@ -1616,7 +1707,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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;
@ -1653,10 +1745,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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);
@ -1696,11 +1794,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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;
@ -1747,18 +1840,19 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// 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);
@ -1771,10 +1865,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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
@ -1783,10 +1880,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
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();
}

@ -642,7 +642,7 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
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;
@ -674,7 +674,7 @@ public:
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"),
@ -717,8 +717,14 @@ public:
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;
@ -840,7 +846,8 @@ bool nodePlacementPrediction(Client &client,
// 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))) {
@ -852,7 +859,8 @@ bool nodePlacementPrediction(Client &client,
}
}
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)) {
@ -3749,11 +3757,9 @@ PointedThing Game::updatePointedThing(
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;
@ -3964,7 +3970,7 @@ void Game::handleDigging(GameRunData *runData,
const ContentFeatures &features =
client->getNodeDefManager()->get(n);
client->getParticleManager()->addPunchingParticles(client, smgr,
player, nodepos, features.tiles);
player, nodepos, n, features);
}
}
@ -4011,7 +4017,7 @@ void Game::handleDigging(GameRunData *runData,
const ContentFeatures &features =
client->getNodeDefManager()->get(wasnode);
client->getParticleManager()->addDiggingParticles(client, smgr,
player, nodepos, features.tiles);
player, nodepos, wasnode, features);
}
runData->dig_time = 0;

@ -32,12 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#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
*/
@ -321,19 +315,34 @@ u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
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)
{
s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
s32 b = rg;
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;
}
// Moonlight is blue
b += (day - night) / 13;
rg -= (day - night) / 23;
void final_color_blend(video::SColor *result,
u16 light, u32 daynight_ratio)
{
video::SColorf dayLight;
get_sunlight_color(&dayLight, daynight_ratio);
final_color_blend(result,
encode_light_and_color(light, video::SColor(0xFFFFFFFF), 0), dayLight);
}
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
@ -341,19 +350,13 @@ void finalColorBlend(video::SColor& result,
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));
}
/*
@ -430,7 +433,7 @@ struct FastFace
};
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;
@ -580,24 +583,25 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
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;
}
@ -664,7 +668,10 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
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;
@ -747,8 +754,7 @@ static void getTileInfo(
v3s16 &p_corrected,
v3s16 &face_dir_corrected,
u16 *lights,
TileSpec &tile,
u8 &light_source
TileSpec &tile
)
{
VoxelManipulator &vmanip = data->m_vmanip;
@ -763,7 +769,8 @@ static void getTileInfo(
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;
@ -783,20 +790,19 @@ static void getTileInfo(
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)
@ -845,10 +851,9 @@ static void updateFastFaceRow(
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++)
{
@ -862,7 +867,6 @@ static void updateFastFaceRow(
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
@ -873,7 +877,7 @@ static void updateFastFaceRow(
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
@ -884,9 +888,10 @@ static void updateFastFaceRow(
&& 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 {
@ -938,8 +943,7 @@ static void updateFastFaceRow(
}
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++){
@ -958,7 +962,6 @@ static void updateFastFaceRow(
lights[2] = next_lights[2];
lights[3] = next_lights[3];
tile = next_tile;
light_source = next_light_source;
p = p_next;
}
}
@ -1083,12 +1086,14 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
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);
@ -1148,43 +1153,30 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
p.tile.texture = animation_frame.texture;
}
u32 vertex_count = m_use_tangent_vertices ?
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++) {
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);
}
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);
}
}
@ -1358,19 +1350,19 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
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;
@ -1452,7 +1444,7 @@ void MeshCollector::append(const TileSpec &tile,
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;
@ -1478,10 +1470,15 @@ void MeshCollector::append(const TileSpec &tile,
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);
@ -1489,8 +1486,12 @@ void MeshCollector::append(const TileSpec &tile,
} 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);
}
}
@ -1500,3 +1501,33 @@ void MeshCollector::append(const TileSpec &tile,
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());
}

@ -156,8 +156,8 @@ private:
// 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;
@ -192,28 +192,53 @@ struct MeshCollector
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

@ -55,6 +55,15 @@ MapNode::MapNode(INodeDefManager *ndef, const std::string &name,
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
@ -146,7 +155,8 @@ bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodem
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;
}
@ -154,7 +164,8 @@ u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const
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;
}
@ -176,7 +187,7 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot)
{
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
@ -216,7 +227,8 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot)
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;

@ -20,9 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#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>
@ -187,6 +185,14 @@ struct MapNode
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);

@ -33,13 +33,25 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#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);
@ -172,29 +184,18 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
}
}
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);
}
video::SColor *vc = &(vertex->Color);
// Reset color
*vc = *buffercolor;
// Apply shading
applyFacesShading(*vc, vertex->Normal);
}
}

@ -23,6 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#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).
@ -48,11 +54,7 @@ void translateMesh(scene::IMesh *mesh, v3f vec);
*/
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.
@ -87,7 +89,7 @@ void rotateMeshYZby (scene::IMesh *mesh, f64 degrees);
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

@ -144,7 +144,7 @@ MinimapPixel *MinimapUpdateThread::getMinimapPixel(v3s16 pos,
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;
}
@ -187,7 +187,7 @@ void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool is_radar)
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) {
@ -195,14 +195,14 @@ void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool 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;
}
}
@ -372,10 +372,21 @@ void Mapper::blitMinimapPixelsToImageSurface(
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);
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, c);
map_image->setPixel(x, data->map_size - z - 1, tilecolor);
u32 h = mmpixel->height;
heightmap_image->setPixel(x,data->map_size - z - 1,
@ -617,7 +628,7 @@ void MinimapMapblock::getMinimapNodes(VoxelManipulator *vmanip, v3s16 pos)
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++;
@ -625,7 +636,7 @@ void MinimapMapblock::getMinimapNodes(VoxelManipulator *vmanip, v3s16 pos)
}
if (!surface_found)
mmpixel->id = CONTENT_AIR;
mmpixel->n = MapNode(CONTENT_AIR);
mmpixel->air_count = air_count;
}

@ -52,10 +52,10 @@ struct MinimapModeDef {
};
struct MinimapPixel {
u16 id;
//! The topmost node that the minimap displays.
MapNode n;
u16 height;
u16 air_count;
u16 light;
};
struct MinimapMapblock {

@ -143,9 +143,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
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

@ -189,7 +189,9 @@ void NodeBox::deSerialize(std::istream &is)
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);
@ -205,6 +207,14 @@ void TileDef::serialize(std::ostream &os, u16 protocol_version) const
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)
@ -218,6 +228,14 @@ void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, con
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) ||
@ -351,25 +369,35 @@ void ContentFeatures::reset()
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);
// general
os << serializeString(name);
writeU16(os, groups.size());
for(ItemGroupList::const_iterator
i = groups.begin(); i != groups.end(); ++i){
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++)
@ -379,65 +407,94 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
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);
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);
@ -446,8 +503,12 @@ void ContentFeatures::deSerialize(std::istream &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)
throw SerializationError("unsupported tile count");
@ -458,65 +519,72 @@ void ContentFeatures::deSerialize(std::istream &is)
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
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
@ -536,6 +604,13 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
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) {
@ -681,6 +756,9 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
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",
@ -696,14 +774,14 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
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 != "")) {
@ -731,14 +809,18 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
}
//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)) {
} 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]);
@ -775,6 +857,9 @@ public:
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);
@ -823,6 +908,9 @@ private:
// 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;
@ -1062,7 +1150,8 @@ void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features,
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),
@ -1309,6 +1398,78 @@ void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath
}
}
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)
@ -1325,10 +1486,13 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef,
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
@ -1429,6 +1593,19 @@ IWritableNodeDefManager *createNodeDefManager()
//// 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
@ -1454,7 +1631,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
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);
@ -1502,7 +1679,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
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);
@ -1530,6 +1707,68 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
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");
@ -1642,6 +1881,72 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version)
drowning = readU8(is);
leveled = readU8(is);
liquid_range = readU8(is);
} 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");
}
@ -1736,19 +2041,23 @@ bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
// 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
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]);
return (f2.connect_sides
& rot[(connect_face * 4) + (to.param2 & 0x1F)]);
}
return (f2.connect_sides & connect_face);
}

@ -68,7 +68,13 @@ enum ContentParamType2
// 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
@ -170,6 +176,11 @@ struct TileDef
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()
@ -178,6 +189,8 @@ struct TileDef
backface_culling = true;
tileable_horizontal = true;
tileable_vertical = true;
has_color = false;
color = video::SColor(0xFFFFFFFF);
animation.type = TAT_NONE;
}
@ -213,10 +226,17 @@ struct ContentFeatures
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
@ -226,19 +246,38 @@ struct ContentFeatures
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;
@ -250,12 +289,12 @@ struct ContentFeatures
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.
@ -271,29 +310,28 @@ struct ContentFeatures
// 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;
// --- SOUND PROPERTIES ---
SimpleSoundSpec sound_footstep;
SimpleSoundSpec sound_dig;
SimpleSoundSpec sound_dug;
// --- 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;
// for NDT_CONNECTED pairing
u8 connect_sides;
// Sound properties
SimpleSoundSpec sound_footstep;
SimpleSoundSpec sound_dig;
SimpleSoundSpec sound_dug;
std::vector<std::string> connects_to;
std::set<content_t> connects_to_ids;
/*
Methods
@ -306,6 +344,14 @@ struct ContentFeatures
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
@ -321,7 +367,7 @@ struct ContentFeatures
#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

@ -56,7 +56,8 @@ Particle::Particle(
v2f texpos,
v2f texsize,
const struct TileAnimationParams &anim,
u8 glow
u8 glow,
video::SColor color
):
scene::ISceneNode(smgr->getRootSceneNode(), smgr)
{
@ -77,6 +78,10 @@ Particle::Particle(
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;
@ -183,12 +188,15 @@ void Particle::updateLight()
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) {
@ -210,14 +218,14 @@ void Particle::updateVertices()
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++)
@ -589,35 +597,39 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client,
}
}
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;
@ -638,6 +650,12 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s
(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,
@ -655,7 +673,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s
texpos,
texsize,
anim,
0);
0,
color);
addParticle(toadd);
}

@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
struct ClientEvent;
class ParticleManager;
class ClientEnvironment;
class MapNode;
class ContentFeatures;
class Particle : public scene::ISceneNode
{
@ -53,7 +55,8 @@ class Particle : public scene::ISceneNode
v2f texpos,
v2f texsize,
const struct TileAnimationParams &anim,
u8 glow
u8 glow,
video::SColor color = video::SColor(0xFFFFFFFF)
);
~Particle();
@ -100,7 +103,10 @@ private:
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;
@ -184,13 +190,16 @@ public:
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);

@ -332,6 +332,10 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
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);
@ -450,6 +454,13 @@ ContentFeatures read_content_features(lua_State *L, int index)
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");
@ -461,6 +472,13 @@ ContentFeatures read_content_features(lua_State *L, int index)
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'");

@ -59,6 +59,9 @@ struct EnumString ScriptApiNode::es_ContentParamType2[] =
{CPT2_LEVELED, "leveled"},
{CPT2_DEGROTATE, "degrotate"},
{CPT2_MESHOPTIONS, "meshoptions"},
{CPT2_COLOR, "color"},
{CPT2_COLORED_FACEDIR, "colorfacedir"},
{CPT2_COLORED_WALLMOUNTED, "colorwallmounted"},
{0, NULL},
};

@ -543,7 +543,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
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;

@ -318,6 +318,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
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 != "") {
@ -358,28 +359,30 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
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;
@ -393,11 +396,28 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
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()
@ -464,7 +484,6 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item)
} 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());
@ -477,8 +496,6 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item)
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) {
@ -492,28 +509,29 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item)
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;

@ -70,6 +70,11 @@ private:
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