From 084cdea6862cb65fe4bb807a211a9e1c17cffec8 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 24 Dec 2016 14:26:03 +0100 Subject: Irrlicht 1.9 support --- src/client/tile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/tile.cpp') diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 8f0c39465..7f7535df6 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -938,7 +938,7 @@ video::ITexture* TextureSource::generateTextureFromMesh( smgr->drop(); // Unset render target - driver->setRenderTarget(0, false, true, 0); + driver->setRenderTarget(0, false, true, video::SColor(0,0,0,0)); if (params.delete_texture_on_shutdown) m_texture_trash.push_back(rtt); -- cgit v1.2.3 From 7057c196c442ff3484b53f48d940f4c9e0ffe23a Mon Sep 17 00:00:00 2001 From: Luke Puchner-Hardman Date: Tue, 23 Sep 2014 14:39:34 +0200 Subject: Added "[sheet" to the texture special commands. "[sheet:WxH:X,Y" assumes the base image is a tilesheet with W*H tiles on it and crops to the tile at position X,Y. Basically it works like "[verticalframe" but in 2D. For testing, I combined the four default_chest images into one. --- doc/lua_api.txt | 5 +++ games/minimal/mods/default/init.lua | 10 +++-- .../mods/default/textures/default_chest.png | Bin 0 -> 263 bytes .../mods/default/textures/default_chest_front.png | Bin 114 -> 0 bytes .../mods/default/textures/default_chest_lock.png | Bin 145 -> 0 bytes .../mods/default/textures/default_chest_side.png | Bin 98 -> 0 bytes .../mods/default/textures/default_chest_top.png | Bin 93 -> 0 bytes src/client/tile.cpp | 43 +++++++++++++++++++++ 8 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 games/minimal/mods/default/textures/default_chest.png delete mode 100644 games/minimal/mods/default/textures/default_chest_front.png delete mode 100644 games/minimal/mods/default/textures/default_chest_lock.png delete mode 100644 games/minimal/mods/default/textures/default_chest_side.png delete mode 100644 games/minimal/mods/default/textures/default_chest_top.png (limited to 'src/client/tile.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d2ddc635b..648a29303 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -403,6 +403,11 @@ Apply a mask to the base image. The mask is applied using binary AND. +#### `[sheet:x:,` +Retrieves a tile at position x,y from the base image +which it assumes to be a tilesheet with dimensions w,h. + + #### `[colorize::` Colorize the textures with the given color. `` is specified as a `ColorString`. diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index bff7860e3..f532e7193 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1130,8 +1130,9 @@ minetest.register_node("default:sign_wall", { minetest.register_node("default:chest", { description = "Chest", - tiles ={"default_chest_top.png", "default_chest_top.png", "default_chest_side.png", - "default_chest_side.png", "default_chest_side.png", "default_chest_front.png"}, + tiles ={"default_chest.png^[sheet:2x2:0,0", "default_chest.png^[sheet:2x2:0,0", + "default_chest.png^[sheet:2x2:1,0", "default_chest.png^[sheet:2x2:1,0", + "default_chest.png^[sheet:2x2:1,0", "default_chest.png^[sheet:2x2:0,1"}, paramtype2 = "facedir", groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2}, legacy_facedir_simple = true, @@ -1164,8 +1165,9 @@ end minetest.register_node("default:chest_locked", { description = "Locked Chest", - tiles ={"default_chest_top.png", "default_chest_top.png", "default_chest_side.png", - "default_chest_side.png", "default_chest_side.png", "default_chest_lock.png"}, + tiles ={"default_chest.png^[sheet:2x2:0,0", "default_chest.png^[sheet:2x2:0,0", + "default_chest.png^[sheet:2x2:1,0", "default_chest.png^[sheet:2x2:1,0", + "default_chest.png^[sheet:2x2:1,0", "default_chest.png^[sheet:2x2:1,1"}, paramtype2 = "facedir", groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2}, legacy_facedir_simple = true, diff --git a/games/minimal/mods/default/textures/default_chest.png b/games/minimal/mods/default/textures/default_chest.png new file mode 100644 index 000000000..9746a3fd9 Binary files /dev/null and b/games/minimal/mods/default/textures/default_chest.png differ diff --git a/games/minimal/mods/default/textures/default_chest_front.png b/games/minimal/mods/default/textures/default_chest_front.png deleted file mode 100644 index 55b076c35..000000000 Binary files a/games/minimal/mods/default/textures/default_chest_front.png and /dev/null differ diff --git a/games/minimal/mods/default/textures/default_chest_lock.png b/games/minimal/mods/default/textures/default_chest_lock.png deleted file mode 100644 index 4b2d1af6c..000000000 Binary files a/games/minimal/mods/default/textures/default_chest_lock.png and /dev/null differ diff --git a/games/minimal/mods/default/textures/default_chest_side.png b/games/minimal/mods/default/textures/default_chest_side.png deleted file mode 100644 index ae4847cb6..000000000 Binary files a/games/minimal/mods/default/textures/default_chest_side.png and /dev/null differ diff --git a/games/minimal/mods/default/textures/default_chest_top.png b/games/minimal/mods/default/textures/default_chest_top.png deleted file mode 100644 index ac41551b0..000000000 Binary files a/games/minimal/mods/default/textures/default_chest_top.png and /dev/null differ diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 7f7535df6..4d2166342 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1827,6 +1827,49 @@ bool TextureSource::generateImagePart(std::string part_of_name, baseimg->setPixel(x, y, c); } } + /* + [sheet:WxH:X,Y + Retrieves a tile at position X,Y (in tiles) + from the base image it assumes to be a + tilesheet with dimensions W,H (in tiles). + */ + else if (part_of_name.substr(0,7) == "[sheet:") { + if (baseimg == NULL) { + errorstream << "generateImagePart(): baseimg != NULL " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + Strfnd sf(part_of_name); + sf.next(":"); + u32 w0 = stoi(sf.next("x")); + u32 h0 = stoi(sf.next(":")); + u32 x0 = stoi(sf.next(",")); + u32 y0 = stoi(sf.next(":")); + + core::dimension2d img_dim = baseimg->getDimension(); + core::dimension2d tile_dim(v2u32(img_dim) / v2u32(w0, h0)); + + video::IImage *img = driver->createImage( + video::ECF_A8R8G8B8, tile_dim); + if (!img) { + errorstream << "generateImagePart(): Could not create image " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + img->fill(video::SColor(0,0,0,0)); + v2u32 vdim(tile_dim); + core::rect rect(v2s32(x0 * vdim.X, y0 * vdim.Y), tile_dim); + baseimg->copyToWithAlpha(img, v2s32(0), rect, + video::SColor(255,255,255,255), NULL); + + // Replace baseimg + baseimg->drop(); + baseimg = img; + } else { errorstream << "generateImagePart(): Invalid " -- cgit v1.2.3 From d04d8aba7029a2501854a2838fd282b81358a54e Mon Sep 17 00:00:00 2001 From: Dániel Juhász Date: Thu, 12 Jan 2017 15:46:30 +0100 Subject: Add hardware node coloring. Includes: - Increase ContentFeatures serialization version - Color property and palettes for nodes - paramtype2 = "color", "colored facedir" or "colored wallmounted" --- client/shaders/nodes_shader/opengl_vertex.glsl | 43 +- .../water_surface_shader/opengl_vertex.glsl | 45 +- client/shaders/wielded_shader/opengl_vertex.glsl | 1 - doc/lua_api.txt | 27 +- src/client/tile.cpp | 3 - src/client/tile.h | 34 +- src/clientenvironment.cpp | 4 +- src/content_mapblock.cpp | 326 ++++++++----- src/game.cpp | 30 +- src/mapblock_mesh.cpp | 263 ++++++----- src/mapblock_mesh.h | 57 ++- src/mapnode.cpp | 20 +- src/mapnode.h | 12 +- src/mesh.cpp | 47 +- src/mesh.h | 14 +- src/minimap.cpp | 31 +- src/minimap.h | 4 +- src/network/networkprotocol.h | 5 +- src/nodedef.cpp | 513 +++++++++++++++++---- src/nodedef.h | 108 +++-- src/particles.cpp | 65 ++- src/particles.h | 19 +- src/script/common/c_content.cpp | 18 + src/script/cpp_api/s_node.cpp | 3 + src/shader.cpp | 2 +- src/wieldmesh.cpp | 62 ++- src/wieldmesh.h | 5 + 27 files changed, 1207 insertions(+), 554 deletions(-) (limited to 'src/client/tile.cpp') diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 44c48cc4c..3ac79c26d 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -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); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85) / 0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; + float brightness = (color.r + color.g + color.b) / 3; + color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) + + 0.07 * brightness); + gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0); } diff --git a/client/shaders/water_surface_shader/opengl_vertex.glsl b/client/shaders/water_surface_shader/opengl_vertex.glsl index a930e7b8f..112db9bb5 100644 --- a/client/shaders/water_surface_shader/opengl_vertex.glsl +++ b/client/shaders/water_surface_shader/opengl_vertex.glsl @@ -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); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; - gl_FrontColor = gl_BackColor = clamp(color,0.0,1.0); + float brightness = (color.r + color.g + color.b) / 3; + color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) + + 0.07 * brightness); + + gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0); } diff --git a/client/shaders/wielded_shader/opengl_vertex.glsl b/client/shaders/wielded_shader/opengl_vertex.glsl index 86c626896..9f05b833a 100644 --- a/client/shaders/wielded_shader/opengl_vertex.glsl +++ b/client/shaders/wielded_shader/opengl_vertex.glsl @@ -1,7 +1,6 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; uniform vec3 eyePosition; uniform float animationTimer; diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e5a3362ee..2a0b72053 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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 diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 4d2166342..539c29445 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -378,9 +378,6 @@ public: video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms); - // Generates an image from a full string like - // "stone.png^mineral_coal.png^[crack:1:0". - // Shall be called from the main thread. video::IImage* generateImage(const std::string &name); video::ITexture* getNormalTexture(const std::string &name); diff --git a/src/client/tile.h b/src/client/tile.h index 452804801..d04ab918a 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -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 ¶ms)=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 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 diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index b32a02f2d..77f53d214 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -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); } /* diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index a7134590b..742bfb1fd 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -32,28 +32,29 @@ with this program; if not, write to the Free Software Foundation, Inc., // Create a cuboid. -// collector - the MeshCollector for the resulting polygons -// box - the position and size of the box -// tiles - the tiles (materials) to use (for all 6 faces) -// tilecount - number of entries in tiles, 1<=tilecount<=6 -// c - vertex colour - used for all -// txc - texture coordinates - this is a list of texture coordinates -// for the opposite corners of each face - therefore, there -// should be (2+2)*6=24 values in the list. Alternatively, pass -// NULL to use the entire texture for each face. The order of -// the faces in the list is up-down-right-left-back-front -// (compatible with ContentFeatures). If you specified 0,0,1,1 -// for each face, that would be the same as passing NULL. +// collector - the MeshCollector for the resulting polygons +// box - the position and size of the box +// tiles - the tiles (materials) to use (for all 6 faces) +// tilecount - number of entries in tiles, 1<=tilecount<=6 +// c - colors of the cuboid's six sides +// txc - texture coordinates - this is a list of texture coordinates +// for the opposite corners of each face - therefore, there +// should be (2+2)*6=24 values in the list. Alternatively, +// pass NULL to use the entire texture for each face. The +// order of the faces in the list is up-down-right-left-back- +// front (compatible with ContentFeatures). If you specified +// 0,0,1,1 for each face, that would be the same as +// passing NULL. +// light source - if greater than zero, the box's faces will not be shaded void makeCuboid(MeshCollector *collector, const aabb3f &box, - TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc) + TileSpec *tiles, int tilecount, const video::SColor *c, + const f32* txc, const u8 light_source) { assert(tilecount >= 1 && tilecount <= 6); // pre-condition v3f min = box.MinEdge; v3f max = box.MaxEdge; - - if(txc == NULL) { static const f32 txc_default[24] = { 0,0,1,1, @@ -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, ©.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; @@ -1627,10 +1719,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - video::S3DVertex(-s, -s+d,-s, 0,0,0, c,0,1), - video::S3DVertex( s, -s+d,-s, 0,0,0, c,1,1), - video::S3DVertex( s, g*s+d, s, 0,0,0, c,1,0), - video::S3DVertex(-s, g*s+d, s, 0,0,0, c,0,0), + video::S3DVertex(-s, -s+d, -s, 0, 0, 0, c, 0, 1), + video::S3DVertex( s, -s+d, -s, 0, 0, 0, c, 1, 1), + video::S3DVertex( s, g*s+d, s, 0, 0, 0, c, 1, 0), + video::S3DVertex(-s, g*s+d, s, 0, 0, 0, c, 0, 0), }; for(s32 i=0; i<4; i++) @@ -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(); } diff --git a/src/game.cpp b/src/game.cpp index 1070cb1b2..07d429c6b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -642,7 +642,7 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_fog_distance; CachedVertexShaderSetting m_animation_timer_vertex; CachedPixelShaderSetting m_animation_timer_pixel; - CachedPixelShaderSetting m_day_night_ratio; + CachedPixelShaderSetting m_day_light; CachedPixelShaderSetting m_eye_position_pixel; CachedVertexShaderSetting m_eye_position_vertex; CachedPixelShaderSetting 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; diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 143adb410..dfd6f55a5 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -32,12 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/directiontables.h" #include -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) +void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){ + f32 rg = daynight_ratio / 1000.0f - 0.04f; + f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f; + sunlight->r = rg; + sunlight->g = rg; + sunlight->b = b; +} + +void final_color_blend(video::SColor *result, + u16 light, u32 daynight_ratio) { - s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000; - s32 b = rg; + video::SColorf dayLight; + get_sunlight_color(&dayLight, daynight_ratio); + final_color_blend(result, + encode_light_and_color(light, video::SColor(0xFFFFFFFF), 0), dayLight); +} - // Moonlight is blue - b += (day - night) / 13; - rg -= (day - night) / 23; +void final_color_blend(video::SColor *result, + const video::SColor &data, const video::SColorf &dayLight) +{ + static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f); + + video::SColorf c(data); + f32 n = 1 - c.a; + + f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f; + f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f; + f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f; // Emphase blue a bit in darker places // Each entry of this array represents a range of 8 blue levels @@ -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 &dest) + v3f p, v3s16 dir, v3f scale, std::vector &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(x0+w*abs_scale, y0+h)); - face.vertices[1] = video::S3DVertex(vertex_pos[1], normal, - MapBlock_LightColor(alpha, li1, light_source), - core::vector2d(x0, y0+h)); - face.vertices[2] = video::S3DVertex(vertex_pos[2], normal, - MapBlock_LightColor(alpha, li2, light_source), - core::vector2d(x0, y0)); - face.vertices[3] = video::S3DVertex(vertex_pos[3], normal, - MapBlock_LightColor(alpha, li3, light_source), - core::vector2d(x0+w*abs_scale, y0)); + u16 li[4] = { li0, li1, li2, li3 }; + v2f32 f[4] = { + core::vector2d(x0 + w * abs_scale, y0 + h), + core::vector2d(x0, y0 + h), + core::vector2d(x0, y0), + core::vector2d(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,26 +790,25 @@ 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) + if (equivalent) tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - if(data->m_smooth_lighting == false) + if (data->m_smooth_lighting == false) { lights[0] = lights[1] = lights[2] = lights[3] = getFaceLight(n0, n1, face_dir, ndef); @@ -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; javg("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 ? - p.tangent_vertices.size() : p.vertices.size(); - for (u32 j = 0; j < vertex_count; j++) { - v3f *Normal; - video::SColor *vc; - if (m_use_tangent_vertices) { - vc = &p.tangent_vertices[j].Color; - Normal = &p.tangent_vertices[j].Normal; - } else { - vc = &p.vertices[j].Color; - Normal = &p.vertices[j].Normal; - } - // Note applyFacesShading second parameter is precalculated sqrt - // value for speed improvement - // Skip it for lightsources and top faces. - if (!vc->getBlue()) { - if (Normal->Y < -0.5) { - applyFacesShading(*vc, 0.447213); - } else if (Normal->X > 0.5) { - applyFacesShading(*vc, 0.670820); - } else if (Normal->X < -0.5) { - applyFacesShading(*vc, 0.670820); - } else if (Normal->Z > 0.5) { - applyFacesShading(*vc, 0.836660); - } else if (Normal->Z < -0.5) { - applyFacesShading(*vc, 0.836660); - } - } - if (!m_enable_shaders) { - // - Classic lighting (shaders handle this by themselves) - // Set initial real color and store for later updates - u8 day = vc->getRed(); - u8 night = vc->getGreen(); - finalColorBlend(*vc, day, night, 1000); - if (day != night) { - m_daynight_diffs[i][j] = std::make_pair(day, night); + if (!m_enable_shaders) { + // Extract colors for day-night animation + // Dummy sunlight to handle non-sunlit areas + video::SColorf sunlight; + get_sunlight_color(&sunlight, 0); + u32 vertex_count = + m_use_tangent_vertices ? + p.tangent_vertices.size() : p.vertices.size(); + for (u32 j = 0; j < vertex_count; j++) { + video::SColor *vc; + if (m_use_tangent_vertices) { + vc = &p.tangent_vertices[j].Color; + } else { + vc = &p.vertices[j].Color; } + video::SColor copy(*vc); + if (vc->getAlpha() == 0) // No sunlight - no need to animate + final_color_blend(vc, copy, sunlight); // Finalize color + else // Record color to animate + m_daynight_diffs[i][j] = copy; + + // The sunlight ratio has been stored, + // delete alpha (for the final rendering). + vc->setAlpha(255); } } @@ -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 > >::iterator + video::SColorf day_color; + get_sunlight_color(&day_color, daynight_ratio); + for(std::map >::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 >::iterator + for(std::map::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="<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()); +} diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 5adb7df3f..916703f3e 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -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 > > m_daynight_diffs; + // For each meshbuffer, stores pre-baked colors of sunlit vertices + std::map > 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 diff --git a/src/mapnode.cpp b/src/mapnode.cpp index f1a7f3e61..d835daba2 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -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; diff --git a/src/mapnode.h b/src/mapnode.h index ae0245cfe..9c56a7e17 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -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 #include @@ -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); diff --git a/src/mesh.cpp b/src/mesh.cpp index 50465748c..84689b631 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -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); - } + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + u32 vertex_count = buf->getVertexCount(); + u8 *vertices = (u8 *) buf->getVertices(); + for (u32 i = 0; i < vertex_count; i++) { + video::S3DVertex *vertex = (video::S3DVertex *) (vertices + i * stride); + video::SColor *vc = &(vertex->Color); + // Reset color + *vc = *buffercolor; + // Apply shading + applyFacesShading(*vc, vertex->Normal); } } diff --git a/src/mesh.h b/src/mesh.h index 10df97015..bcf0d771c 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -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 diff --git a/src/minimap.cpp b/src/minimap.cpp index f49adb517..a2e501751 100644 --- a/src/minimap.cpp +++ b/src/minimap.cpp @@ -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); - - map_image->setPixel(x, data->map_size - z - 1, c); + const ContentFeatures &f = m_ndef->get(mmpixel->n); + const TileDef *tile = &f.tiledef[0]; + // Color of the 0th tile (mostly this is the topmost) + video::SColor tilecolor; + if(tile->has_color) + tilecolor = tile->color; + else + mmpixel->n.getColor(f, &tilecolor); + tilecolor.setRed(tilecolor.getRed() * f.minimap_color.getRed() / 255); + tilecolor.setGreen(tilecolor.getGreen() * f.minimap_color.getGreen() + / 255); + tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255); + tilecolor.setAlpha(240); + + map_image->setPixel(x, data->map_size - z - 1, tilecolor); u32 h = mmpixel->height; heightmap_image->setPixel(x,data->map_size - z - 1, @@ -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; } diff --git a/src/minimap.h b/src/minimap.h index 743b2bff2..81ed0e49f 100644 --- a/src/minimap.h +++ b/src/minimap.h @@ -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 { diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 23c8a665b..a511d169b 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -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 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index a4af26e87..98f795c7a 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -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,172 +369,222 @@ 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); - os<first); + for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end(); + ++i) { + os << serializeString(i->first); writeS16(os, i->second); } + writeU8(os, param_type); + writeU8(os, param_type_2); + + // visual writeU8(os, drawtype); + os << serializeString(mesh); writeF1000(os, visual_scale); writeU8(os, 6); - for(u32 i = 0; i < 6; i++) + for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); - for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){ + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { tiledef_special[i].serialize(os, protocol_version); } writeU8(os, alpha); + writeU8(os, color.getRed()); + writeU8(os, color.getGreen()); + writeU8(os, color.getBlue()); + os << serializeString(palette_name); + writeU8(os, waving); + writeU8(os, connect_sides); + writeU16(os, connects_to_ids.size()); + for (std::set::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<::const_iterator i = connects_to_ids.begin(); - i != connects_to_ids.end(); ++i) - writeU16(os, *i); - writeU8(os, connect_sides); + + // legacy + writeU8(os, legacy_facedir_simple); + writeU8(os, legacy_wallmounted); +} + +void ContentFeatures::correctAlpha() +{ + if (alpha == 0 || alpha == 255) + return; + + for (u32 i = 0; i < 6; i++) { + std::stringstream s; + s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha); + tiledef[i].name = s.str(); + } + + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { + std::stringstream s; + s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha); + tiledef_special[i].name = s.str(); + } } void ContentFeatures::deSerialize(std::istream &is) { + // version detection int version = readU8(is); - if (version < 7) { + if (version < 9) { deSerializeOld(is, version); return; - } else if (version > 8) { + } else if (version > 9) { throw SerializationError("unsupported ContentFeatures version"); } + // general name = deSerializeString(is); groups.clear(); u32 groups_size = readU16(is); - for(u32 i = 0; i < groups_size; i++){ + for (u32 i = 0; i < groups_size; i++) { std::string name = deSerializeString(is); int value = readS16(is); groups[name] = value; } - drawtype = (enum NodeDrawType)readU8(is); + param_type = (enum ContentParamType) readU8(is); + param_type_2 = (enum ContentParamType2) readU8(is); + // visual + drawtype = (enum NodeDrawType) readU8(is); + mesh = deSerializeString(is); visual_scale = readF1000(is); - if(readU8(is) != 6) + if (readU8(is) != 6) throw SerializationError("unsupported tile count"); - for(u32 i = 0; i < 6; i++) + for (u32 i = 0; i < 6; i++) tiledef[i].deSerialize(is, version, drawtype); - if(readU8(is) != CF_SPECIAL_COUNT) + if (readU8(is) != CF_SPECIAL_COUNT) throw SerializationError("unsupported CF_SPECIAL_COUNT"); - for(u32 i = 0; i < CF_SPECIAL_COUNT; i++) + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) tiledef_special[i].deSerialize(is, version, drawtype); alpha = readU8(is); + color.setRed(readU8(is)); + color.setGreen(readU8(is)); + color.setBlue(readU8(is)); + palette_name = deSerializeString(is); + waving = readU8(is); + connect_sides = readU8(is); + u16 connects_to_size = readU16(is); + connects_to_ids.clear(); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); post_effect_color.setAlpha(readU8(is)); post_effect_color.setRed(readU8(is)); post_effect_color.setGreen(readU8(is)); post_effect_color.setBlue(readU8(is)); - param_type = (enum ContentParamType)readU8(is); - param_type_2 = (enum ContentParamType2)readU8(is); - is_ground_content = readU8(is); + leveled = readU8(is); + + // lighting-related light_propagates = readU8(is); sunlight_propagates = readU8(is); + light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); + + // map generation + is_ground_content = readU8(is); + + // interaction walkable = readU8(is); pointable = readU8(is); diggable = readU8(is); climbable = readU8(is); buildable_to = readU8(is); - deSerializeString(is); // legacy: used to be metadata_name - liquid_type = (enum LiquidType)readU8(is); + rightclickable = readU8(is); + damage_per_second = readU32(is); + + // liquid + liquid_type = (enum LiquidType) readU8(is); liquid_alternative_flowing = deSerializeString(is); liquid_alternative_source = deSerializeString(is); liquid_viscosity = readU8(is); liquid_renewable = readU8(is); - light_source = readU8(is); - light_source = MYMIN(light_source, LIGHT_MAX); - damage_per_second = readU32(is); + liquid_range = readU8(is); + drowning = readU8(is); + floodable = readU8(is); + + // node boxes node_box.deSerialize(is); selection_box.deSerialize(is); - legacy_facedir_simple = readU8(is); - legacy_wallmounted = readU8(is); + collision_box.deSerialize(is); + + // sounds deSerializeSimpleSoundSpec(sound_footstep, is); deSerializeSimpleSoundSpec(sound_dig, is); deSerializeSimpleSoundSpec(sound_dug, is); - rightclickable = readU8(is); - drowning = readU8(is); - leveled = readU8(is); - liquid_range = readU8(is); - waving = readU8(is); - // If you add anything here, insert it primarily inside the try-catch - // block to not need to increase the version. - try{ - // Stuff below should be moved to correct place in a version that - // otherwise changes the protocol version - mesh = deSerializeString(is); - collision_box.deSerialize(is); - floodable = readU8(is); - u16 connects_to_size = readU16(is); - connects_to_ids.clear(); - for (u16 i = 0; i < connects_to_size; i++) - connects_to_ids.insert(readU16(is)); - connect_sides = readU8(is); - }catch(SerializationError &e) {}; + + // read legacy properties + legacy_facedir_simple = readU8(is); + legacy_wallmounted = readU8(is); } #ifndef SERVER void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, u32 shader_id, bool use_normal_texture, - bool backface_culling, u8 alpha, u8 material_type) + bool backface_culling, u8 material_type) { tile->shader_id = shader_id; tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id); - tile->alpha = alpha; tile->material_type = material_type; // Normal texture and shader flags texture @@ -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,15 +809,19 @@ 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)) { - static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2}; + } else if (tsettings.enable_mesh_cache && mesh_ptr[0] + && (param_type_2 == CPT2_WALLMOUNTED || + param_type_2 == CPT2_COLORED_WALLMOUNTED)) { + static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 }; for (u16 j = 1; j < 6; j++) { mesh_ptr[j] = cloneMesh(mesh_ptr[0]); rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]); @@ -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 *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 > m_palettes; + // NodeResolvers to callback once node registration has ended std::vector 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 *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 >::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 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); @@ -1483,9 +1660,9 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const os<first); - writeS16(os, i->second); + i = groups.begin(); i != groups.end(); ++i) { + os<first); + writeS16(os, i->second); } writeU8(os, drawtype); writeF1000(os, visual_scale); @@ -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::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,7 +1881,73 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version) drowning = readU8(is); leveled = readU8(is); liquid_range = readU8(is); - } else { + } else if (version == 7 || version == 8){ + name = deSerializeString(is); + groups.clear(); + u32 groups_size = readU16(is); + for (u32 i = 0; i < groups_size; i++) { + std::string name = deSerializeString(is); + int value = readS16(is); + groups[name] = value; + } + drawtype = (enum NodeDrawType) readU8(is); + + visual_scale = readF1000(is); + if (readU8(is) != 6) + throw SerializationError("unsupported tile count"); + for (u32 i = 0; i < 6; i++) + tiledef[i].deSerialize(is, version, drawtype); + if (readU8(is) != CF_SPECIAL_COUNT) + throw SerializationError("unsupported CF_SPECIAL_COUNT"); + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) + tiledef_special[i].deSerialize(is, version, drawtype); + alpha = readU8(is); + post_effect_color.setAlpha(readU8(is)); + post_effect_color.setRed(readU8(is)); + post_effect_color.setGreen(readU8(is)); + post_effect_color.setBlue(readU8(is)); + param_type = (enum ContentParamType) readU8(is); + param_type_2 = (enum ContentParamType2) readU8(is); + is_ground_content = readU8(is); + light_propagates = readU8(is); + sunlight_propagates = readU8(is); + walkable = readU8(is); + pointable = readU8(is); + diggable = readU8(is); + climbable = readU8(is); + buildable_to = readU8(is); + deSerializeString(is); // legacy: used to be metadata_name + liquid_type = (enum LiquidType) readU8(is); + liquid_alternative_flowing = deSerializeString(is); + liquid_alternative_source = deSerializeString(is); + liquid_viscosity = readU8(is); + liquid_renewable = readU8(is); + light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); + damage_per_second = readU32(is); + node_box.deSerialize(is); + selection_box.deSerialize(is); + legacy_facedir_simple = readU8(is); + legacy_wallmounted = readU8(is); + deSerializeSimpleSoundSpec(sound_footstep, is); + deSerializeSimpleSoundSpec(sound_dig, is); + deSerializeSimpleSoundSpec(sound_dug, is); + rightclickable = readU8(is); + drowning = readU8(is); + leveled = readU8(is); + liquid_range = readU8(is); + waving = readU8(is); + try { + mesh = deSerializeString(is); + collision_box.deSerialize(is); + floodable = readU8(is); + u16 connects_to_size = readU16(is); + connects_to_ids.clear(); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); + connect_sides = readU8(is); + } catch (SerializationError &e) {}; + }else{ throw SerializationError("unsupported ContentFeatures version"); } } @@ -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 - }; - return (f2.connect_sides & rot[(connect_face * 4) + to.param2]); + if ((f2.param_type_2 == CPT2_FACEDIR || + f2.param_type_2 == CPT2_COLORED_FACEDIR) + && (connect_face >= 4)) { + static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 4 - back + 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 8 - right + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 16 - front + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left + }; + return (f2.connect_sides + & rot[(connect_face * 4) + (to.param2 & 0x1F)]); } return (f2.connect_sides & connect_face); } diff --git a/src/nodedef.h b/src/nodedef.h index 183b95d87..6275b41ce 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -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; } @@ -191,7 +204,7 @@ struct ContentFeatures { /* Cached stuff - */ + */ #ifndef SERVER // 0 1 2 3 4 5 // up down right left back front @@ -211,12 +224,19 @@ 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 *palette; + // Used for waving leaves/plants + u8 waving; + // for NDT_CONNECTED pairing + u8 connect_sides; + std::vector connects_to; + std::set 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; - // Compatibility with old maps - // Set to true if paramtype used to be 'facedir_simple' - bool legacy_facedir_simple; - // Set to true if wall_mounted used to be set to true - bool legacy_wallmounted; - // for NDT_CONNECTED pairing - u8 connect_sides; - // Sound properties + // --- SOUND PROPERTIES --- + SimpleSoundSpec sound_footstep; SimpleSoundSpec sound_dig; SimpleSoundSpec sound_dug; - std::vector connects_to; - std::set connects_to_ids; + // --- LEGACY --- + + // Compatibility with old maps + // Set to true if paramtype used to be 'facedir_simple' + bool legacy_facedir_simple; + // Set to true if wall_mounted used to be set to true + bool legacy_wallmounted; /* Methods @@ -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 diff --git a/src/particles.cpp b/src/particles.cpp index 5f17763e0..e1f292fb6 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -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); } diff --git a/src/particles.h b/src/particles.h index 5464e6672..3177f2cfd 100644 --- a/src/particles.h +++ b/src/particles.h @@ -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); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 84af4583b..ebc951295 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -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'"); diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index 379ed773f..23c8f43b9 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -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}, }; diff --git a/src/shader.cpp b/src/shader.cpp index c0ecf738d..79485025b 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -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; diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index c305238fe..089a67f33 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -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; diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 0162c5e5a..2e78232ae 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -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 m_colors; // Bounding box culling is disabled for this type of scene node, // so this variable is just required so we can implement -- cgit v1.2.3 From e761b9f48626db2af8b62a0cf85691208951cf0d Mon Sep 17 00:00:00 2001 From: sapier Date: Sat, 14 Jan 2017 23:16:58 +0100 Subject: Add multiply texture modifier Allows colorizing of textures using a color multiplication method. --- doc/lua_api.txt | 7 +++++++ src/client/tile.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) (limited to 'src/client/tile.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9a1cb6bac..219882f46 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -419,6 +419,13 @@ the word "`alpha`", then each texture pixel will contain the RGB of `` and the alpha of `` multiplied by the alpha of the texture pixel. +#### `[multiply:` +Multiplies texture colors with the given color. +`` is specified as a `ColorString`. +Result is more like what you'd expect if you put a color on top of another +color. Meaning white surfaces get a lot of your new color while black parts don't +change very much. + Sounds ------ Only Ogg Vorbis files are supported. diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 539c29445..fbc0f1709 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -555,7 +555,11 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst, // color alpha with the destination alpha. // Otherwise, any pixels that are not fully transparent get the color alpha. static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size, - video::SColor color, int ratio, bool keep_alpha); + const video::SColor &color, int ratio, bool keep_alpha); + +// paint a texture using the given color +static void apply_multiplication(video::IImage *dst, v2u32 dst_pos, v2u32 size, + const video::SColor &color); // Apply a mask to an image static void apply_mask(video::IImage *mask, video::IImage *dst, @@ -1656,6 +1660,30 @@ bool TextureSource::generateImagePart(std::string part_of_name, << filename << "\"."; } } + /* + [multiply:color + multiplys a given color to any pixel of an image + color = color as ColorString + */ + else if (str_starts_with(part_of_name, "[multiply:")) { + Strfnd sf(part_of_name); + sf.next(":"); + std::string color_str = sf.next(":"); + + if (baseimg == NULL) { + errorstream << "generateImagePart(): baseimg != NULL " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + video::SColor color; + + if (!parseColorString(color_str, color, false)) + return false; + + apply_multiplication(baseimg, v2u32(0, 0), baseimg->getDimension(), color); + } /* [colorize:color Overlays image with given color @@ -1960,7 +1988,7 @@ static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst Apply color to destination */ static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size, - video::SColor color, int ratio, bool keep_alpha) + const video::SColor &color, int ratio, bool keep_alpha) { u32 alpha = color.getAlpha(); video::SColor dst_c; @@ -1994,6 +2022,27 @@ static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size, } } +/* + Apply color to destination +*/ +static void apply_multiplication(video::IImage *dst, v2u32 dst_pos, v2u32 size, + const video::SColor &color) +{ + video::SColor dst_c; + + for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++) + for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) { + dst_c = dst->getPixel(x, y); + dst_c.set( + dst_c.getAlpha(), + (dst_c.getRed() * color.getRed()) / 255, + (dst_c.getGreen() * color.getGreen()) / 255, + (dst_c.getBlue() * color.getBlue()) / 255 + ); + dst->setPixel(x, y, dst_c); + } +} + /* Apply mask to destination */ -- cgit v1.2.3 From 3e30731c1ac313b504ff15eb7f40ce6a387d3da2 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Sun, 5 Feb 2017 23:59:18 -0800 Subject: Prevent SIGFPE on entity tile loading issue. (#5178) While experimenting with entities I ran into this unresolvable error where the server is sending some texture that the client crashes on. The crash prevents the client from ever reconnecting, resulting in a server that has to use clearobjects. We shouldn't crash but just ignore the object and move on. ``` 0x00000000004dc0de in TextureSource::generateImagePart (this=this@entry=0x7118eb0, part_of_name="[applyfiltersformesh", baseimg=@0x7fffffffbe98: 0x9f1b010) at /home/sofar/git/minetest/src/client/tile.cpp:1744 1744 u32 xscale = scaleto / dim.Width; (gdb) bt #0 0x00000000004dc0de in TextureSource::generateImagePart (this=this@entry=0x7118eb0, part_of_name="[applyfiltersformesh", baseimg=@0x7fffffffbe98: 0x9f1b010) at /home/sofar/git/minetest/src/client/tile.cpp:1744 ``` After reconnecting, the client now can connect without issues and displays an error message: ``` ERROR[Main]: generateImagePart(): Illegal 0 dimension for part_of_name="[applyfiltersformesh", cancelling. ERROR[Main]: generateImage(): Failed to generate "[applyfiltersformesh" ERROR[Main]: Irrlicht: Invalid size of image for OpenGL Texture. ``` --- src/client/tile.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/client/tile.cpp') diff --git a/src/client/tile.cpp b/src/client/tile.cpp index fbc0f1709..000c766d0 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1741,6 +1741,12 @@ bool TextureSource::generateImagePart(std::string part_of_name, * equal to the target minimum. If e.g. this is a vertical frames * animation, the short dimension will be the real size. */ + if ((dim.Width == 0) || (dim.Height == 0)) { + errorstream << "generateImagePart(): Illegal 0 dimension " + << "for part_of_name=\""<< part_of_name + << "\", cancelling." << std::endl; + return false; + } u32 xscale = scaleto / dim.Width; u32 yscale = scaleto / dim.Height; u32 scale = (xscale > yscale) ? xscale : yscale; -- cgit v1.2.3 From 072bbba69aa2528c309b76aaec288bdba52e119c Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Wed, 22 Mar 2017 21:41:02 +0100 Subject: Some performance optimizations (#5424) * Some performance optimizations This is globally removing some memory useless copy * use a const ref return on std::string Settings::get to prevent data copy on getters which doesn't need to copy it * pass some stack created strings to static const as they are not modified anywhere * Camera: return nametags per const ref instead of a list pointer, we only need to read it * INodeDefManager: getAll should be a result ref writer instead of a return copy * INodeDefManager: getAlias should return a const std::string ref * Minimap: unroll a Scolor creation in blitMinimapPixersToImageRadar to prvent many variable construct/destruct which are unneeded (we rewrite the content in the loop) * CNodeDefManager::updateAliases: prevent a idef getall copy * Profiler: constness * rollback_interface: create real_name later, and use const ref * MapBlockMesh updateFastFaceRow: unroll TileSpec next_tile, which has a cost of 1.8% CPU due to variable allocation/destruction, * MapBlockMesh updateFastFaceRow: copy next_tile to tile only if it's a different tilespec * MapBlockMesh updateFastFaceRow: use memcpy to copy next_lights to lights to do it in a single cpu operation --- src/camera.h | 3 +-- src/client/clientlauncher.cpp | 2 +- src/client/tile.cpp | 12 +++++------ src/drawscene.cpp | 2 +- src/game.cpp | 2 +- src/itemdef.cpp | 7 +++---- src/itemdef.h | 8 ++++---- src/mapblock_mesh.cpp | 48 ++++++++++--------------------------------- src/minimap.cpp | 30 ++++++++++++++------------- src/nodedef.cpp | 9 ++++---- src/profiler.h | 33 +++++++++++++---------------- src/remoteplayer.cpp | 4 ++-- src/rollback_interface.cpp | 5 +++-- src/settings.cpp | 29 +------------------------- src/settings.h | 4 +--- src/sky.h | 15 ++++++++------ 16 files changed, 79 insertions(+), 134 deletions(-) (limited to 'src/client/tile.cpp') diff --git a/src/camera.h b/src/camera.h index b5be26718..f57efdf10 100644 --- a/src/camera.h +++ b/src/camera.h @@ -172,8 +172,7 @@ public: void removeNametag(Nametag *nametag); - std::list *getNametags() - { return &m_nametags; } + const std::list &getNametags() { return m_nametags; } void drawNametags(); diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 2adac53c2..bdd16205f 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -530,7 +530,7 @@ bool ClientLauncher::create_engine_device() // Determine driver video::E_DRIVER_TYPE driverType = video::EDT_OPENGL; - std::string driverstring = g_settings->get("video_driver"); + const std::string &driverstring = g_settings->get("video_driver"); std::vector drivers = porting::getSupportedVideoDrivers(); u32 i; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 000c766d0..85d388d6e 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -134,9 +134,8 @@ std::string getTexturePath(const std::string &filename) /* Check from texture_path */ - std::string texture_path = g_settings->get("texture_path"); - if (texture_path != "") - { + const std::string &texture_path = g_settings->get("texture_path"); + if (texture_path != "") { std::string testpath = texture_path + DIR_DELIM + filename; // Check all filename extensions. Returns "" if not found. fullpath = getImagePath(testpath); @@ -1854,7 +1853,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, for (u32 x = 0; x < dim.Width; x++) { video::SColor c = baseimg->getPixel(x, y); - c.color ^= mask; + c.color ^= mask; baseimg->setPixel(x, y, c); } } @@ -2266,7 +2265,8 @@ video::ITexture* TextureSource::getNormalTexture(const std::string &name) if (isKnownSourceImage("override_normal.png")) return getTexture("override_normal.png"); std::string fname_base = name; - std::string normal_ext = "_normal.png"; + static const char *normal_ext = "_normal.png"; + static const uint32_t normal_ext_size = strlen(normal_ext); size_t pos = fname_base.find("."); std::string fname_normal = fname_base.substr(0, pos) + normal_ext; if (isKnownSourceImage(fname_normal)) { @@ -2274,7 +2274,7 @@ video::ITexture* TextureSource::getNormalTexture(const std::string &name) size_t i = 0; while ((i = fname_base.find(".", i)) != std::string::npos) { fname_base.replace(i, 4, normal_ext); - i += normal_ext.length(); + i += normal_ext_size; } return getTexture(fname_base); } diff --git a/src/drawscene.cpp b/src/drawscene.cpp index 3a03743ee..663c8828c 100644 --- a/src/drawscene.cpp +++ b/src/drawscene.cpp @@ -494,7 +494,7 @@ void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr, catch(SettingNotFoundException) {} #endif - std::string draw_mode = g_settings->get("3d_mode"); + const std::string &draw_mode = g_settings->get("3d_mode"); smgr->drawAll(); diff --git a/src/game.cpp b/src/game.cpp index 5b49d34c0..9d52e4326 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4094,7 +4094,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, Drawing begins */ - video::SColor skycolor = sky->getSkyColor(); + const video::SColor &skycolor = sky->getSkyColor(); TimeTaker tt_draw("mainloop: draw"); driver->beginScene(true, true, skycolor); diff --git a/src/itemdef.cpp b/src/itemdef.cpp index f43e5c970..627ad1b6c 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -275,16 +275,16 @@ public: assert(i != m_item_definitions.end()); return *(i->second); } - virtual std::string getAlias(const std::string &name) const + virtual const std::string &getAlias(const std::string &name) const { StringMap::const_iterator it = m_aliases.find(name); if (it != m_aliases.end()) return it->second; return name; } - virtual std::set getAll() const + virtual void getAll(std::set &result) const { - std::set result; + result.clear(); for(std::map::const_iterator it = m_item_definitions.begin(); it != m_item_definitions.end(); ++it) { @@ -295,7 +295,6 @@ public: it != m_aliases.end(); ++it) { result.insert(it->first); } - return result; } virtual bool isKnown(const std::string &name_) const { diff --git a/src/itemdef.h b/src/itemdef.h index 2ade6116a..01ec4fa2f 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -100,9 +100,9 @@ public: // Get item definition virtual const ItemDefinition& get(const std::string &name) const=0; // Get alias definition - virtual std::string getAlias(const std::string &name) const=0; + virtual const std::string &getAlias(const std::string &name) const=0; // Get set of all defined item names and aliases - virtual std::set getAll() const=0; + virtual void getAll(std::set &result) const=0; // Check if item is known virtual bool isKnown(const std::string &name) const=0; #ifndef SERVER @@ -126,9 +126,9 @@ public: // Get item definition virtual const ItemDefinition& get(const std::string &name) const=0; // Get alias definition - virtual std::string getAlias(const std::string &name) const=0; + virtual const std::string &getAlias(const std::string &name) const=0; // Get set of all defined item names and aliases - virtual std::set getAll() const=0; + virtual void getAll(std::set &result) const=0; // Check if item is known virtual bool isKnown(const std::string &name) const=0; #ifndef SERVER diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index f76033ea8..eddb061b4 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -855,8 +855,9 @@ static void updateFastFaceRow( makes_face, p_corrected, face_dir_corrected, lights, tile); - for(u16 j=0; jadd("Meshgen: diff: next_makes_face != makes_face", - next_makes_face != makes_face ? 1 : 0); - g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir", - (next_p_corrected != p_corrected + translate_dir) ? 1 : 0); - g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr", - next_face_dir_corrected != face_dir_corrected ? 1 : 0); - g_profiler->add("Meshgen: diff: next_lights[] != lights[]", - (next_lights[0] != lights[0] || - next_lights[0] != lights[0] || - next_lights[0] != lights[0] || - next_lights[0] != lights[0]) ? 1 : 0); - g_profiler->add("Meshgen: diff: !(next_tile == tile)", - !(next_tile == tile) ? 1 : 0); - }*/ } - /*g_profiler->add("Meshgen: Total faces checked", 1); - if(makes_face) - g_profiler->add("Meshgen: Total makes_face checked", 1);*/ - } else { - /*if(makes_face) - g_profiler->add("Meshgen: diff: last position", 1);*/ } - if(next_is_different) - { + if (next_is_different) { /* Create a face if there should be one */ - if(makes_face) - { + if (makes_face) { // Floating point conversion of the position vector v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z); // Center point of face (kind of) @@ -957,11 +933,9 @@ static void updateFastFaceRow( makes_face = next_makes_face; p_corrected = next_p_corrected; face_dir_corrected = next_face_dir_corrected; - lights[0] = next_lights[0]; - lights[1] = next_lights[1]; - lights[2] = next_lights[2]; - lights[3] = next_lights[3]; - tile = next_tile; + std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16)); + if (next_is_different) + tile = next_tile; p = p_next; } } diff --git a/src/minimap.cpp b/src/minimap.cpp index ee29c58ba..a7f4822c9 100644 --- a/src/minimap.cpp +++ b/src/minimap.cpp @@ -105,7 +105,7 @@ void MinimapUpdateThread::doUpdate() // Swap two values in the map using single lookup std::pair::iterator, bool> result = m_blocks_cache.insert(std::make_pair(update.pos, update.data)); - if (result.second == false) { + if (!result.second) { delete result.first->second; result.first->second = update.data; } @@ -322,13 +322,15 @@ void Minimap::setAngle(f32 angle) void Minimap::blitMinimapPixelsToImageRadar(video::IImage *map_image) { + video::SColor c(240, 0, 0, 0); for (s16 x = 0; x < data->map_size; x++) for (s16 z = 0; z < data->map_size; z++) { MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; - video::SColor c(240, 0, 0, 0); if (mmpixel->air_count > 0) c.setGreen(core::clamp(core::round32(32 + mmpixel->air_count * 8), 0, 255)); + else + c.setGreen(0); map_image->setPixel(x, data->map_size - z - 1, c); } @@ -337,21 +339,23 @@ void Minimap::blitMinimapPixelsToImageRadar(video::IImage *map_image) void Minimap::blitMinimapPixelsToImageSurface( video::IImage *map_image, video::IImage *heightmap_image) { + // This variable creation/destruction has a 1% cost on rendering minimap + video::SColor tilecolor; for (s16 x = 0; x < data->map_size; x++) for (s16 z = 0; z < data->map_size; z++) { MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; 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.setGreen(tilecolor.getGreen() * f.minimap_color.getGreen() / 255); tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255); tilecolor.setAlpha(240); @@ -391,7 +395,7 @@ video::ITexture *Minimap::getMinimapTexture() if (minimap_mask) { for (s16 y = 0; y < MINIMAP_MAX_SY; y++) for (s16 x = 0; x < MINIMAP_MAX_SX; x++) { - video::SColor mask_col = minimap_mask->getPixel(x, y); + const video::SColor &mask_col = minimap_mask->getPixel(x, y); if (!mask_col.getAlpha()) minimap_image->setPixel(x, y, video::SColor(0,0,0,0)); } @@ -430,7 +434,7 @@ scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() scene::SMeshBuffer *buf = new scene::SMeshBuffer(); buf->Vertices.set_used(4); buf->Indices.set_used(6); - video::SColor c(255, 255, 255, 255); + static const video::SColor c(255, 255, 255, 255); buf->Vertices[0] = video::S3DVertex(-1, -1, 0, 0, 0, 1, c, 0, 1); buf->Vertices[1] = video::S3DVertex(-1, 1, 0, 0, 0, 1, c, 0, 0); @@ -550,15 +554,13 @@ void Minimap::updateActiveMarkers() video::IImage *minimap_mask = data->minimap_shape_round ? data->minimap_mask_round : data->minimap_mask_square; - std::list *nametags = client->getCamera()->getNametags(); + const std::list &nametags = client->getCamera()->getNametags(); m_active_markers.clear(); - for (std::list::const_iterator - i = nametags->begin(); - i != nametags->end(); ++i) { - Nametag *nametag = *i; - v3s16 pos = floatToInt(nametag->parent_node->getPosition() + + for (std::list::const_iterator i = nametags.begin(); + i != nametags.end(); ++i) { + v3s16 pos = floatToInt((*i)->parent_node->getPosition() + intToFloat(client->getCamera()->getOffset(), BS), BS); pos -= data->pos - v3s16(data->map_size / 2, data->scan_height / 2, @@ -570,7 +572,7 @@ void Minimap::updateActiveMarkers() } pos.X = ((float)pos.X / data->map_size) * MINIMAP_MAX_SX; pos.Z = ((float)pos.Z / data->map_size) * MINIMAP_MAX_SY; - video::SColor mask_col = minimap_mask->getPixel(pos.X, pos.Z); + const video::SColor &mask_col = minimap_mask->getPixel(pos.X, pos.Z); if (!mask_col.getAlpha()) { continue; } diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 3532eea1e..2745a45e8 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1332,12 +1332,13 @@ void CNodeDefManager::removeNode(const std::string &name) void CNodeDefManager::updateAliases(IItemDefManager *idef) { - std::set all = idef->getAll(); + std::set all; + idef->getAll(all); m_name_id_mapping_with_aliases.clear(); - for (std::set::iterator + for (std::set::const_iterator i = all.begin(); i != all.end(); ++i) { - std::string name = *i; - std::string convert_to = idef->getAlias(name); + const std::string &name = *i; + const std::string &convert_to = idef->getAlias(name); content_t id; if (m_name_id_mapping.getId(convert_to, id)) { m_name_id_mapping_with_aliases.insert( diff --git a/src/profiler.h b/src/profiler.h index e8eac86b1..6da115972 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -119,39 +119,34 @@ public: u32 minindex, maxindex; paging(m_data.size(), page, pagecount, minindex, maxindex); - for(std::map::iterator - i = m_data.begin(); - i != m_data.end(); ++i) - { - if(maxindex == 0) + for (std::map::const_iterator i = m_data.begin(); + i != m_data.end(); ++i) { + if (maxindex == 0) break; maxindex--; - if(minindex != 0) - { + if (minindex != 0) { minindex--; continue; } - std::string name = i->first; int avgcount = 1; - std::map::iterator n = m_avgcounts.find(name); - if(n != m_avgcounts.end()){ + std::map::const_iterator n = m_avgcounts.find(i->first); + if (n != m_avgcounts.end()) { if(n->second >= 1) avgcount = n->second; } - o<<" "<first << ": "; s32 clampsize = 40; - s32 space = clampsize - name.size(); - for(s32 j=0; jfirst.size(); + for(s32 j = 0; j < space; j++) { + if (j % 2 == 0 && j < space - 1) + o << "-"; else - o<<" "; + o << " "; } - o<<(i->second / avgcount); - o<second / avgcount); + o << std::endl; } } diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 0a4591410..c8e5b9132 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -141,7 +141,7 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, m_dirty = true; //args.getS32("version"); // Version field value not used - std::string name = args.get("name"); + const std::string &name = args.get("name"); strlcpy(m_name, name.c_str(), PLAYERNAME_SIZE); if (sao) { @@ -167,7 +167,7 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, } catch (SettingNotFoundException &e) {} try { - std::string extended_attributes = args.get("extended_attributes"); + const std::string &extended_attributes = args.get("extended_attributes"); Json::Reader reader; Json::Value attr_root; reader.parse(extended_attributes, attr_root); diff --git a/src/rollback_interface.cpp b/src/rollback_interface.cpp index 7345c4a7d..40a33a51d 100644 --- a/src/rollback_interface.cpp +++ b/src/rollback_interface.cpp @@ -190,7 +190,6 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam case TYPE_MODIFY_INVENTORY_STACK: { InventoryLocation loc; loc.deSerialize(inventory_location); - std::string real_name = gamedef->idef()->getAlias(inventory_stack.name); Inventory *inv = imgr->getInventory(loc); if (!inv) { infostream << "RollbackAction::applyRevert(): Could not get " @@ -211,10 +210,12 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam << inventory_location << std::endl; return false; } + // If item was added, take away item, otherwise add removed item if (inventory_add) { // Silently ignore different current item - if (list->getItem(inventory_index).name != real_name) + if (list->getItem(inventory_index).name != + gamedef->idef()->getAlias(inventory_stack.name)) return false; list->takeItem(inventory_index, inventory_stack.count); } else { diff --git a/src/settings.cpp b/src/settings.cpp index c4c3c9073..b4083264e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -90,33 +90,6 @@ bool Settings::checkValueValid(const std::string &value) return true; } - -std::string Settings::sanitizeName(const std::string &name) -{ - std::string n = trim(name); - - for (const char *s = "=\"{}#"; *s; s++) - n.erase(std::remove(n.begin(), n.end(), *s), n.end()); - - return n; -} - - -std::string Settings::sanitizeValue(const std::string &value) -{ - std::string v(value); - size_t p = 0; - - if (v.substr(0, 3) == "\"\"\"") - v.erase(0, 3); - - while ((p = v.find("\n\"\"\"")) != std::string::npos) - v.erase(p, 4); - - return v; -} - - std::string Settings::getMultiline(std::istream &is, size_t *num_lines) { size_t lines = 1; @@ -398,7 +371,7 @@ Settings *Settings::getGroup(const std::string &name) const } -std::string Settings::get(const std::string &name) const +const std::string &Settings::get(const std::string &name) const { const SettingsEntry &entry = getEntry(name); if (entry.is_group) diff --git a/src/settings.h b/src/settings.h index b19733514..777d0eff5 100644 --- a/src/settings.h +++ b/src/settings.h @@ -129,8 +129,6 @@ public: static bool checkNameValid(const std::string &name); static bool checkValueValid(const std::string &value); - static std::string sanitizeName(const std::string &name); - static std::string sanitizeValue(const std::string &value); static std::string getMultiline(std::istream &is, size_t *num_lines=NULL); static void printEntry(std::ostream &os, const std::string &name, const SettingsEntry &entry, u32 tab_depth=0); @@ -141,7 +139,7 @@ public: const SettingsEntry &getEntry(const std::string &name) const; Settings *getGroup(const std::string &name) const; - std::string get(const std::string &name) const; + const std::string &get(const std::string &name) const; bool getBool(const std::string &name) const; u16 getU16(const std::string &name) const; s16 getS16(const std::string &name) const; diff --git a/src/sky.h b/src/sky.h index f19891773..17d6c5f73 100644 --- a/src/sky.h +++ b/src/sky.h @@ -56,18 +56,21 @@ public: void update(float m_time_of_day, float time_brightness, float direct_brightness, bool sunlight_seen, CameraMode cam_mode, float yaw, float pitch); - + float getBrightness(){ return m_brightness; } - video::SColor getBgColor(){ + const video::SColor &getBgColor() const + { return m_visible ? m_bgcolor : m_fallback_bg_color; } - video::SColor getSkyColor(){ + + const video::SColor &getSkyColor() const + { return m_visible ? m_skycolor : m_fallback_bg_color; } - - bool getCloudsVisible(){ return m_clouds_visible && m_visible; } - video::SColorf getCloudColor(){ return m_cloudcolor_f; } + + bool getCloudsVisible() { return m_clouds_visible && m_visible; } + const video::SColorf &getCloudColor() { return m_cloudcolor_f; } void setVisible(bool visible){ m_visible = visible; } void setFallbackBgColor(const video::SColor &fallback_bg_color){ -- cgit v1.2.3 From 5a59ad230744a84a1474e8c3b97cf3d9080536d7 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Fri, 24 Mar 2017 20:05:15 +0100 Subject: Tile.cpp: Fix MSVC build broken by 072bbba --- src/client/tile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/tile.cpp') diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 85d388d6e..86ca7d422 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -2266,7 +2266,7 @@ video::ITexture* TextureSource::getNormalTexture(const std::string &name) return getTexture("override_normal.png"); std::string fname_base = name; static const char *normal_ext = "_normal.png"; - static const uint32_t normal_ext_size = strlen(normal_ext); + static const u32 normal_ext_size = strlen(normal_ext); size_t pos = fname_base.find("."); std::string fname_normal = fname_base.substr(0, pos) + normal_ext; if (isKnownSourceImage(fname_normal)) { @@ -2277,7 +2277,7 @@ video::ITexture* TextureSource::getNormalTexture(const std::string &name) i += normal_ext_size; } return getTexture(fname_base); - } + } return NULL; } -- cgit v1.2.3 From 58d83a7bb2f992194c3df304b1dcbb81f98f78c0 Mon Sep 17 00:00:00 2001 From: Dániel Juhász Date: Fri, 10 Mar 2017 18:25:58 +0100 Subject: Hardware coloring for itemstacks Adds the possibility to colorize item stacks based on their metadata. In the item/node definition you can specify palette (an image file) and color (fallback color if the item has no palette or metadata). Then you can add palette_index to the metadata. Dropped itemstacks with different colors do not merge. --- builtin/game/item_entity.lua | 31 +++++++++---- doc/lua_api.txt | 18 ++++++++ src/camera.cpp | 3 +- src/client/tile.cpp | 69 +++++++++++++++++++++++++++- src/client/tile.h | 15 ++++--- src/content_cao.cpp | 39 +++++++++------- src/hud.cpp | 20 +++++++-- src/itemdef.cpp | 55 +++++++++++++++++++---- src/itemdef.h | 18 +++++++- src/nodedef.cpp | 82 +--------------------------------- src/object_properties.cpp | 2 + src/object_properties.h | 2 + src/script/common/c_content.cpp | 14 +++++- src/script/common/c_content.h | 3 +- src/script/cpp_api/s_entity.cpp | 5 ++- src/script/lua_api/l_itemstackmeta.cpp | 5 +++ src/script/lua_api/l_metadata.cpp | 14 ++++++ src/script/lua_api/l_metadata.h | 3 ++ src/script/lua_api/l_nodemeta.cpp | 5 +++ src/script/lua_api/l_object.cpp | 2 +- src/script/lua_api/l_storage.cpp | 5 +++ src/wieldmesh.cpp | 19 +++++--- src/wieldmesh.h | 18 +++++++- 23 files changed, 308 insertions(+), 139 deletions(-) (limited to 'src/client/tile.cpp') diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index be158c119..7b8247116 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -53,6 +53,8 @@ core.register_entity(":__builtin:item", { if itemtable then itemname = stack:to_table().name end + -- Backwards compatibility: old clients use the texture + -- to get the type of the item local item_texture = nil local item_type = "" if core.registered_items[itemname] then @@ -66,6 +68,7 @@ core.register_entity(":__builtin:item", { visual_size = {x = s, y = s}, collisionbox = {-c, -c, -c, c, c, c}, automatic_rotate = math.pi * 0.5, + wield_item = itemstring, } self.object:set_properties(prop) end, @@ -101,31 +104,39 @@ core.register_entity(":__builtin:item", { self:set_item(self.itemstring) end, + -- moves items from this stack to an other stack try_merge_with = function(self, own_stack, object, obj) + -- other item's stack local stack = ItemStack(obj.itemstring) - if own_stack:get_name() == stack:get_name() and stack:get_free_space() > 0 then + -- only merge if items are the same + if own_stack:get_name() == stack:get_name() and + own_stack:get_meta() == stack:get_meta() and + own_stack:get_wear() == stack:get_wear() and + stack:get_free_space() > 0 then local overflow = false local count = stack:get_count() + own_stack:get_count() local max_count = stack:get_stack_max() if count > max_count then overflow = true + stack:set_count(max_count) count = count - max_count + own_stack:set_count(count) else self.itemstring = '' + stack:set_count(count) end local pos = object:getpos() pos.y = pos.y + (count - stack:get_count()) / max_count * 0.15 object:moveto(pos, false) local s, c - local max_count = stack:get_stack_max() - local name = stack:get_name() if not overflow then - obj.itemstring = name .. " " .. count + obj.itemstring = stack:to_string() s = 0.2 + 0.1 * (count / max_count) c = s object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = obj.itemstring }) self.object:remove() -- merging succeeded @@ -133,18 +144,20 @@ core.register_entity(":__builtin:item", { else s = 0.4 c = 0.3 + obj.itemstring = stack:to_string() object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = obj.itemstring }) - obj.itemstring = name .. " " .. max_count s = 0.2 + 0.1 * (count / max_count) c = s + self.itemstring = own_stack:to_string() self.object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = self.itemstring }) - self.itemstring = name .. " " .. count end end -- merging didn't succeed diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ca1b5d14c..721f5448a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1480,6 +1480,9 @@ Item metadata only contains a key-value store. Some of the values in the key-value store are handled specially: * `description`: Set the itemstack's description. Defaults to idef.description +* `color`: A `ColorString`, which sets the stack's color. +* `palette_index`: If the item has a palette, this is used to get the + current color from the palette. Example stuff: @@ -2855,6 +2858,8 @@ See `StorageRef`, `NodeMetaRef` and `ItemStackMetaRef`. * Any non-table value will clear the metadata * See "Node Metadata" for an example * returns `true` on success +* `equals(other)` + * returns `true` if this metadata has the same key-value pairs as `other` ### `NodeMetaRef` Node metadata: reference extra data and functionality stored in a node. @@ -3735,6 +3740,19 @@ Definition tables {hard = 1, metal = 1, spikes = 1} inventory_image = "default_tool_steelaxe.png", wield_image = "", + palette = "", + --[[ + ^ An image file containing the palette of a node. + ^ You can set the currently used color as the + ^ "palette_index" field of the item stack metadata. + ^ The palette is always stretched to fit indices + ^ between 0 and 255, to ensure compatibility with + ^ "colorfacedir" and "colorwallmounted" nodes. + ]] + color = "0xFFFFFFFF", + --[[ + ^ The color of the item. The palette overrides this. + ]] wield_scale = {x = 1, y = 1, z = 1}, stack_max = 99, range = 4.0, diff --git a/src/camera.cpp b/src/camera.cpp index d5c337fe8..7e83dadeb 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -501,7 +501,8 @@ void Camera::setDigging(s32 button) void Camera::wield(const ItemStack &item) { - if (item.name != m_wield_item_next.name) { + if (item.name != m_wield_item_next.name || + item.metadata != m_wield_item_next.metadata) { m_wield_item_next = item; if (m_wield_change_timer > 0) m_wield_change_timer = -m_wield_change_timer; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 86ca7d422..0aa06980c 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -341,6 +341,8 @@ public: */ video::ITexture* getTextureForMesh(const std::string &name, u32 *id); + virtual Palette* getPalette(const std::string &name); + // Returns a pointer to the irrlicht device virtual IrrlichtDevice* getDevice() { @@ -377,8 +379,6 @@ public: video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms); - video::IImage* generateImage(const std::string &name); - video::ITexture* getNormalTexture(const std::string &name); video::SColor getTextureAverageColor(const std::string &name); video::ITexture *getShaderFlagsTexture(bool normamap_present); @@ -401,6 +401,13 @@ private: // if baseimg is NULL, it is created. Otherwise stuff is made on it. bool generateImagePart(std::string part_of_name, video::IImage *& baseimg); + /*! 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. + */ + video::IImage* generateImage(const std::string &name); + // Thread-safe cache of what source images are known (true = known) MutexedMap m_source_image_existence; @@ -419,6 +426,9 @@ private: // but can't be deleted because the ITexture* might still be used std::vector m_texture_trash; + // Maps image file names to loaded palettes. + UNORDERED_MAP m_palettes; + // Cached settings needed for making textures from meshes bool m_setting_trilinear_filter; bool m_setting_bilinear_filter; @@ -682,6 +692,61 @@ video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 * return getTexture(name + "^[applyfiltersformesh", id); } +Palette* TextureSource::getPalette(const std::string &name) +{ + // Only the main thread may load images + sanity_check(thr_is_current_thread(m_main_thread)); + + if (name == "") + return NULL; + + UNORDERED_MAP::iterator it = m_palettes.find(name); + if (it == m_palettes.end()) { + // Create palette + video::IImage *img = generateImage(name); + if (!img) { + warningstream << "TextureSource::getPalette(): palette \"" << name + << "\" could not be loaded." << std::endl; + return NULL; + } + Palette new_palette; + u32 w = img->getDimension().Width; + u32 h = img->getDimension().Height; + // Real area of the image + u32 area = h * w; + if (area == 0) + return NULL; + if (area > 256) { + warningstream << "TextureSource::getPalette(): the specified" + << " palette image \"" << name << "\" is larger than 256" + << " pixels, using the first 256." << std::endl; + area = 256; + } else if (256 % area != 0) + warningstream << "TextureSource::getPalette(): the " + << "specified palette image \"" << name << "\" does not " + << "contain power of two pixels." << std::endl; + // We stretch the palette so it will fit 256 values + // This many param2 values will have the same color + u32 step = 256 / area; + // 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); + return NULL; +} + void TextureSource::processQueue() { /* diff --git a/src/client/tile.h b/src/client/tile.h index d04ab918a..5eec0f2ea 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -33,6 +33,8 @@ class IGameDef; struct TileSpec; struct TileDef; +typedef std::vector Palette; + /* tile.{h,cpp}: Texture handling stuff. */ @@ -106,14 +108,15 @@ public: const std::string &name, u32 *id = NULL)=0; virtual video::ITexture* getTextureForMesh( const std::string &name, u32 *id = NULL) = 0; + /*! + * Returns a palette from the given texture name. + * The pointer is valid until the texture source is + * destructed. + * Should be called from the main thread. + */ + virtual Palette* getPalette(const std::string &name) = 0; virtual IrrlichtDevice* getDevice()=0; virtual bool isKnownSourceImage(const std::string &name)=0; - /*! Generates an image from a full string like - * "stone.png^mineral_coal.png^[crack:1:0". - * Shall be called from the main thread. - * The returned Image should be dropped. - */ - virtual video::IImage* generateImage(const std::string &name)=0; virtual video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms)=0; virtual video::ITexture* getNormalTexture(const std::string &name)=0; diff --git a/src/content_cao.cpp b/src/content_cao.cpp index e0b1c4cd2..84f198b75 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -933,23 +933,30 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, errorstream<<"GenericCAO::addToScene(): Could not load mesh "<= 1){ - infostream<<"textures[0]: "<idef(); - ItemStack item(m_prop.textures[0], 1, 0, idef); - - m_wield_meshnode = new WieldMeshSceneNode( - smgr->getRootSceneNode(), smgr, -1); - m_wield_meshnode->setItem(item, m_client); - - m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2, - m_prop.visual_size.Y/2, - m_prop.visual_size.X/2)); - u8 li = m_last_light; - m_wield_meshnode->setColor(video::SColor(255,li,li,li)); + ItemStack item; + infostream << "GenericCAO::addToScene(): wielditem" << std::endl; + if (m_prop.wield_item == "") { + // Old format, only textures are specified. + infostream << "textures: " << m_prop.textures.size() << std::endl; + if (m_prop.textures.size() >= 1) { + infostream << "textures[0]: " << m_prop.textures[0] + << std::endl; + IItemDefManager *idef = m_client->idef(); + item = ItemStack(m_prop.textures[0], 1, 0, idef); + } + } else { + infostream << "serialized form: " << m_prop.wield_item << std::endl; + item.deSerialize(m_prop.wield_item, m_client->idef()); } + m_wield_meshnode = new WieldMeshSceneNode(smgr->getRootSceneNode(), + smgr, -1); + m_wield_meshnode->setItem(item, m_client); + + m_wield_meshnode->setScale( + v3f(m_prop.visual_size.X / 2, m_prop.visual_size.Y / 2, + m_prop.visual_size.X / 2)); + u8 li = m_last_light; + m_wield_meshnode->setColor(video::SColor(255, li, li, li)); } else { infostream<<"GenericCAO::addToScene(): \""< #ifdef HAVE_TOUCHSCREENGUI @@ -642,9 +643,10 @@ void drawItemStack(video::IVideoDriver *driver, } const ItemDefinition &def = item.getDefinition(client->idef()); - scene::IMesh* mesh = client->idef()->getWieldMesh(def.name, client); + ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client); - if (mesh) { + if (imesh && imesh->mesh) { + scene::IMesh *mesh = imesh->mesh; driver->clearZBuffer(); s32 delta = 0; if (rotation_kind < IT_ROT_NONE) { @@ -667,16 +669,28 @@ void drawItemStack(video::IVideoDriver *driver, matrix.makeIdentity(); if (enable_animations) { - float timer_f = (float)delta / 5000.0; + float timer_f = (float) delta / 5000.0; matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0)); } driver->setTransform(video::ETS_WORLD, matrix); driver->setViewPort(rect); + video::SColor basecolor = + client->idef()->getItemstackColor(item, client); + u32 mc = mesh->getMeshBufferCount(); for (u32 j = 0; j < mc; ++j) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); + // we can modify vertices relatively fast, + // because these meshes are not buffered. + assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER); + video::SColor c = basecolor; + if (imesh->buffer_colors.size() > j) { + std::pair p = imesh->buffer_colors[j]; + c = p.first ? p.second : basecolor; + } + colorizeMeshBuffer(buf, &c); video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; material.Lighting = false; diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 627ad1b6c..51d8f1d5d 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -82,6 +82,8 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) sound_place = def.sound_place; sound_place_failed = def.sound_place_failed; range = def.range; + palette_image = def.palette_image; + color = def.color; return *this; } @@ -104,6 +106,8 @@ void ItemDefinition::reset() description = ""; inventory_image = ""; wield_image = ""; + palette_image = ""; + color = video::SColor(0xFFFFFFFF); wield_scale = v3f(1.0, 1.0, 1.0); stack_max = 99; usable = false; @@ -153,6 +157,8 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const writeF1000(os, range); os << serializeString(sound_place_failed.name); writeF1000(os, sound_place_failed.gain); + os << serializeString(palette_image); + writeU32(os, color.color); } void ItemDefinition::deSerialize(std::istream &is) @@ -209,6 +215,8 @@ void ItemDefinition::deSerialize(std::istream &is) try { sound_place_failed.name = deSerializeString(is); sound_place_failed.gain = readF1000(is); + palette_image = deSerializeString(is); + color.set(readU32(is)); } catch(SerializationError &e) {}; } @@ -224,11 +232,13 @@ class CItemDefManager: public IWritableItemDefManager struct ClientCached { video::ITexture *inventory_texture; - scene::IMesh *wield_mesh; + ItemMesh wield_mesh; + Palette *palette; ClientCached(): inventory_texture(NULL), - wield_mesh(NULL) + wield_mesh(), + palette(NULL) {} }; #endif @@ -250,8 +260,8 @@ public: i = values.begin(); i != values.end(); ++i) { ClientCached *cc = *i; - if (cc->wield_mesh) - cc->wield_mesh->drop(); + if (cc->wield_mesh.mesh) + cc->wield_mesh.mesh->drop(); delete cc; } @@ -335,8 +345,9 @@ public: ItemStack item = ItemStack(); item.name = def.name; - scene::IMesh *mesh = getItemMesh(client, item); - cc->wield_mesh = mesh; + getItemMesh(client, item, &(cc->wield_mesh)); + + cc->palette = tsrc->getPalette(def.palette_image); // Put in cache m_clientcached.set(name, cc); @@ -390,13 +401,41 @@ public: return cc->inventory_texture; } // Get item wield mesh - virtual scene::IMesh* getWieldMesh(const std::string &name, + virtual ItemMesh* getWieldMesh(const std::string &name, Client *client) const { ClientCached *cc = getClientCached(name, client); if(!cc) return NULL; - return cc->wield_mesh; + return &(cc->wield_mesh); + } + + // Get item palette + virtual Palette* getPalette(const std::string &name, + Client *client) const + { + ClientCached *cc = getClientCached(name, client); + if(!cc) + return NULL; + return cc->palette; + } + + virtual video::SColor getItemstackColor(const ItemStack &stack, + Client *client) const + { + // Look for direct color definition + const std::string &colorstring = stack.metadata.getString("color", 0); + video::SColor directcolor; + if ((colorstring != "") + && parseColorString(colorstring, directcolor, true)) + return directcolor; + // See if there is a palette + Palette *palette = getPalette(stack.name, client); + const std::string &index = stack.metadata.getString("palette_index", 0); + if ((palette != NULL) && (index != "")) + return (*palette)[mystoi(index, 0, 255)]; + // Fallback color + return get(stack.name).color; } #endif void clear() diff --git a/src/itemdef.h b/src/itemdef.h index 01ec4fa2f..2d7ff570d 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -30,6 +30,11 @@ with this program; if not, write to the Free Software Foundation, Inc., class IGameDef; class Client; struct ToolCapabilities; +#ifndef SERVER +#include "client/tile.h" +struct ItemMesh; +struct ItemStack; +#endif /* Base item definition @@ -57,6 +62,8 @@ struct ItemDefinition */ std::string inventory_image; // Optional for nodes, mandatory for tools/craftitems std::string wield_image; // If empty, inventory_image or mesh (only nodes) is used + std::string palette_image; // If specified, the item will be colorized based on this + video::SColor color; // The fallback color of the node. v3f wield_scale; /* @@ -110,8 +117,15 @@ public: virtual video::ITexture* getInventoryTexture(const std::string &name, Client *client) const=0; // Get item wield mesh - virtual scene::IMesh* getWieldMesh(const std::string &name, + virtual ItemMesh* getWieldMesh(const std::string &name, Client *client) const=0; + // Get item palette + virtual Palette* getPalette(const std::string &name, + Client *client) const = 0; + // Returns the base color of an item stack: the color of all + // tiles that do not define their own color. + virtual video::SColor getItemstackColor(const ItemStack &stack, + Client *client) const = 0; #endif virtual void serialize(std::ostream &os, u16 protocol_version)=0; @@ -136,7 +150,7 @@ public: virtual video::ITexture* getInventoryTexture(const std::string &name, Client *client) const=0; // Get item wield mesh - virtual scene::IMesh* getWieldMesh(const std::string &name, + virtual ItemMesh* getWieldMesh(const std::string &name, Client *client) const=0; #endif diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 2745a45e8..558acafd6 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -786,6 +786,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc tiledef_special[j].backface_culling, material_type); } + palette = tsrc->getPalette(palette_name); + if ((drawtype == NDT_MESH) && (mesh != "")) { // Meshnode drawtype // Read the mesh and apply scale @@ -859,9 +861,6 @@ 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 *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); @@ -910,9 +909,6 @@ private: // Next possibly free id content_t m_next_id; - // Maps image file names to loaded palettes. - UNORDERED_MAP > m_palettes; - // NodeResolvers to callback once node registration has ended std::vector m_pending_resolve_callbacks; @@ -1401,78 +1397,6 @@ void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath } } -std::vector *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 >::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 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) @@ -1489,12 +1413,10 @@ 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++) { 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); } diff --git a/src/object_properties.cpp b/src/object_properties.cpp index f4e4953ba..a77368151 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -117,6 +117,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeARGB8(os, nametag_color); writeF1000(os, automatic_face_movement_max_rotation_per_sec); os << serializeString(infotext); + os << serializeString(wield_item); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this @@ -159,6 +160,7 @@ void ObjectProperties::deSerialize(std::istream &is) nametag_color = readARGB8(is); automatic_face_movement_max_rotation_per_sec = readF1000(is); infotext = deSerializeString(is); + wield_item = deSerializeString(is); }catch(SerializationError &e){} } else diff --git a/src/object_properties.h b/src/object_properties.h index 082d9a529..908757a64 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -52,6 +52,8 @@ struct ObjectProperties video::SColor nametag_color; f32 automatic_face_movement_max_rotation_per_sec; std::string infotext; + //! For dropped items, this contains item information. + std::string wield_item; ObjectProperties(); std::string dump(); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 99e12cd82..bcae874b9 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -58,6 +58,12 @@ ItemDefinition read_item_definition(lua_State* L,int index, getstringfield(L, index, "description", def.description); getstringfield(L, index, "inventory_image", def.inventory_image); getstringfield(L, index, "wield_image", def.wield_image); + getstringfield(L, index, "palette", def.palette_image); + + // Read item color. + lua_getfield(L, index, "color"); + read_color(L, -1, &def.color); + lua_pop(L, 1); lua_getfield(L, index, "wield_scale"); if(lua_istable(L, -1)){ @@ -118,7 +124,7 @@ ItemDefinition read_item_definition(lua_State* L,int index, /******************************************************************************/ void read_object_properties(lua_State *L, int index, - ObjectProperties *prop) + ObjectProperties *prop, IItemDefManager *idef) { if(index < 0) index = lua_gettop(L) + 1 + index; @@ -216,6 +222,10 @@ void read_object_properties(lua_State *L, int index, } lua_pop(L, 1); getstringfield(L, -1, "infotext", prop->infotext); + lua_getfield(L, -1, "wield_item"); + if (!lua_isnil(L, -1)) + prop->wield_item = read_item(L, -1, idef).getItemString(); + lua_pop(L, 1); } /******************************************************************************/ @@ -284,6 +294,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "automatic_face_movement_max_rotation_per_sec"); lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size()); lua_setfield(L, -2, "infotext"); + lua_pushlstring(L, prop->wield_item.c_str(), prop->wield_item.size()); + lua_setfield(L, -2, "wield_item"); } /******************************************************************************/ diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 10cccbb01..949b136eb 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -89,7 +89,8 @@ void push_tool_capabilities (lua_State *L, ItemDefinition read_item_definition (lua_State *L, int index, ItemDefinition default_def); void read_object_properties (lua_State *L, int index, - ObjectProperties *prop); + ObjectProperties *prop, + IItemDefManager *idef); void push_object_properties (lua_State *L, ObjectProperties *prop); diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index 9e2193970..2e1d277e4 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "object_properties.h" #include "common/c_converter.h" #include "common/c_content.h" +#include "server.h" bool ScriptApiEntity::luaentity_Add(u16 id, const char *name) { @@ -187,11 +188,11 @@ void ScriptApiEntity::luaentity_GetProperties(u16 id, getstringfield(L, -1, "mesh", prop->mesh); // Deprecated: read object properties directly - read_object_properties(L, -1, prop); + read_object_properties(L, -1, prop, getServer()->idef()); // Read initial_properties lua_getfield(L, -1, "initial_properties"); - read_object_properties(L, -1, prop); + read_object_properties(L, -1, prop, getServer()->idef()); lua_pop(L, 1); } diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp index efdd77b51..c37a82116 100644 --- a/src/script/lua_api/l_itemstackmeta.cpp +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -92,6 +92,10 @@ void ItemStackMetaRef::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + lua_pop(L, 1); // drop metatable luaL_openlib(L, 0, methods, 0); // fill methodtable @@ -111,5 +115,6 @@ const luaL_Reg ItemStackMetaRef::methods[] = { luamethod(MetaDataRef, set_float), luamethod(MetaDataRef, to_table), luamethod(MetaDataRef, from_table), + luamethod(MetaDataRef, equals), {0,0} }; diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp index b54005bac..5f4e984cb 100644 --- a/src/script/lua_api/l_metadata.cpp +++ b/src/script/lua_api/l_metadata.cpp @@ -250,3 +250,17 @@ bool MetaDataRef::handleFromTable(lua_State *L, int table, Metadata *meta) return true; } + +// equals(self, other) +int MetaDataRef::l_equals(lua_State *L) +{ + MetaDataRef *ref1 = checkobject(L, 1); + Metadata *data1 = ref1->getmeta(false); + MetaDataRef *ref2 = checkobject(L, 2); + Metadata *data2 = ref2->getmeta(false); + if (data1 == NULL || data2 == NULL) + lua_pushboolean(L, data1 == data2); + else + lua_pushboolean(L, *data1 == *data2); + return 1; +} diff --git a/src/script/lua_api/l_metadata.h b/src/script/lua_api/l_metadata.h index be31d95ad..a4d8214d3 100644 --- a/src/script/lua_api/l_metadata.h +++ b/src/script/lua_api/l_metadata.h @@ -67,6 +67,9 @@ protected: // from_table(self, table) static int l_from_table(lua_State *L); + + // equals(self, other) + static int l_equals(lua_State *L); }; #endif /* L_NODEMETA_H_ */ diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 55d11fc13..c65d56f14 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -204,6 +204,10 @@ void NodeMetaRef::RegisterCommon(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + lua_pop(L, 1); // drop metatable } @@ -225,6 +229,7 @@ const luaL_Reg NodeMetaRef::methodsServer[] = { luamethod(MetaDataRef, to_table), luamethod(MetaDataRef, from_table), luamethod(NodeMetaRef, get_inventory), + luamethod(MetaDataRef, equals), {0,0} }; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index f9d2754e7..0699705cb 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -737,7 +737,7 @@ int ObjectRef::l_set_properties(lua_State *L) ObjectProperties *prop = co->accessObjectProperties(); if (!prop) return 0; - read_object_properties(L, 2, prop); + read_object_properties(L, 2, prop, getServer(L)->idef()); co->notifyObjectPropertiesModified(); return 0; } diff --git a/src/script/lua_api/l_storage.cpp b/src/script/lua_api/l_storage.cpp index 59906dda5..4c6b2a182 100644 --- a/src/script/lua_api/l_storage.cpp +++ b/src/script/lua_api/l_storage.cpp @@ -98,6 +98,10 @@ void StorageRef::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + lua_pop(L, 1); // drop metatable luaL_openlib(L, 0, methods, 0); // fill methodtable @@ -138,5 +142,6 @@ const luaL_Reg StorageRef::methods[] = { luamethod(MetaDataRef, set_float), luamethod(MetaDataRef, to_table), luamethod(MetaDataRef, from_table), + luamethod(MetaDataRef, equals), {0,0} }; diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 089a67f33..40af0be5f 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -318,11 +318,15 @@ 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; } + + // Color-related m_colors.clear(); + video::SColor basecolor = idef->getItemstackColor(item, client); // If wield_image is defined, it overrides everything else if (def.wield_image != "") { setExtruded(def.wield_image, def.wield_scale, tsrc, 1); + m_colors.push_back(basecolor); return; } // Handle nodes @@ -371,7 +375,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) } else { material.setTexture(0, tile->texture); } - m_colors.push_back(tile->color); + m_colors.push_back(tile->has_color ? tile->color : basecolor); material.MaterialType = m_material_type; if (m_enable_shaders) { if (tile->normal_texture) { @@ -389,6 +393,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) } else if (def.inventory_image != "") { setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); + m_colors.push_back(basecolor); return; } @@ -455,7 +460,7 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh) m_meshnode->setVisible(true); } -scene::IMesh *getItemMesh(Client *client, const ItemStack &item) +void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) { ITextureSource *tsrc = client->getTextureSource(); IItemDefManager *idef = client->getItemDefManager(); @@ -475,12 +480,13 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) // If inventory_image is defined, it overrides everything else if (def.inventory_image != "") { mesh = getExtrudedMesh(tsrc, def.inventory_image); - return mesh; + result->mesh = mesh; + result->buffer_colors.push_back( + std::pair(false, video::SColor(0xFFFFFFFF))); } else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { mesh = cloneMesh(f.mesh_ptr[0]); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); - setMeshColor(mesh, video::SColor (255, 255, 255, 255)); } else if (f.drawtype == NDT_PLANTLIKE) { mesh = getExtrudedMesh(tsrc, tsrc->getTextureName(f.tiles[0].texture_id)); @@ -515,6 +521,8 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) for (u32 i = 0; i < mc; ++i) { const TileSpec *tile = &(f.tiles[i]); scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + result->buffer_colors.push_back( + std::pair(tile->has_color, tile->color)); colorizeMeshBuffer(buf, &tile->color); video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -532,9 +540,8 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) rotateMeshXZby(mesh, -45); rotateMeshYZby(mesh, -30); - return mesh; + result->mesh = mesh; } - return NULL; } scene::IMesh * getExtrudedMesh(ITextureSource *tsrc, diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 200587058..94edb1de6 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -28,6 +28,22 @@ class Client; class ITextureSource; struct TileSpec; +struct ItemMesh +{ + scene::IMesh* mesh; + /*! + * Stores the color of each mesh buffer. + * If the boolean is true, the color is fixed, else + * palettes can modify it. + */ + std::vector > buffer_colors; + + ItemMesh(): + mesh(NULL), + buffer_colors() + {} +}; + /* Wield item scene node, renders the wield mesh of some item */ @@ -79,7 +95,7 @@ private: aabb3f m_bounding_box; }; -scene::IMesh *getItemMesh(Client *client, const ItemStack &item); +void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result); scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename); #endif -- cgit v1.2.3 From 73de17afa821ccea84a119096b267e05a77db3ff Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Sun, 16 Apr 2017 14:44:15 +0200 Subject: Android progressbar fix (#5601) * Fix progressbar for Android Fixes #5599 Fixed #5403 * draw_load_screen: use texturesource this permits to unify texture loading code * scale progress bar * Add gl version check for GL_OES_texture_npot. This fixed the texture on loading screen * Remove two sanity checks pointed by @celeron55 * sfan5 comments + android ratio fixes --- src/client.cpp | 14 ++++++++------ src/client/tile.cpp | 15 ++++++++++++--- src/drawscene.cpp | 28 +++++++++++----------------- src/drawscene.h | 4 ++-- src/game.cpp | 14 +++++++++----- 5 files changed, 42 insertions(+), 33 deletions(-) (limited to 'src/client/tile.cpp') diff --git a/src/client.cpp b/src/client.cpp index 246525f62..5ca51bd9c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1782,6 +1782,7 @@ typedef struct TextureUpdateArgs { u32 last_time_ms; u16 last_percent; const wchar_t* text_base; + ITextureSource *tsrc; } TextureUpdateArgs; void texture_update_progress(void *args, u32 progress, u32 max_progress) @@ -1803,8 +1804,8 @@ void texture_update_progress(void *args, u32 progress, u32 max_progress) targs->last_time_ms = time_ms; std::basic_stringstream strm; strm << targs->text_base << " " << targs->last_percent << "%..."; - draw_load_screen(strm.str(), targs->device, targs->guienv, 0, - 72 + (u16) ((18. / 100.) * (double) targs->last_percent)); + draw_load_screen(strm.str(), targs->device, targs->guienv, targs->tsrc, 0, + 72 + (u16) ((18. / 100.) * (double) targs->last_percent), true); } } @@ -1824,21 +1825,21 @@ void Client::afterContentReceived(IrrlichtDevice *device) // Rebuild inherited images and recreate textures infostream<<"- Rebuilding images and textures"<rebuildImagesAndTextures(); delete[] text; // Rebuild shaders infostream<<"- Rebuilding shaders"<rebuildShaders(); delete[] text; // Update node aliases infostream<<"- Updating node aliases"<updateAliases(m_itemdef); std::string texture_path = g_settings->get("texture_path"); if (texture_path != "" && fs::IsDir(texture_path)) @@ -1855,6 +1856,7 @@ void Client::afterContentReceived(IrrlichtDevice *device) tu_args.last_time_ms = getTimeMs(); tu_args.last_percent = 0; tu_args.text_base = wgettext("Initializing nodes"); + tu_args.tsrc = m_tsrc; m_nodedef->updateTextures(this, texture_update_progress, &tu_args); delete[] tu_args.text_base; @@ -1871,7 +1873,7 @@ void Client::afterContentReceived(IrrlichtDevice *device) } text = wgettext("Done!"); - draw_load_screen(text, device, guienv, 0, 100); + draw_load_screen(text, device, guienv, m_tsrc, 0, 100); infostream<<"Client::afterContentReceived() done"<name); #ifdef __ANDROID__ img = Align2Npot2(img, driver); - sanity_check(img->getDimension().Height == npot2(img->getDimension().Height)); - sanity_check(img->getDimension().Width == npot2(img->getDimension().Width)); #endif // Create texture from resulting image video::ITexture *t = NULL; @@ -1124,6 +1122,14 @@ video::IImage* TextureSource::generateImage(const std::string &name) * @param driver driver to use for image operations * @return image or copy of image aligned to npot2 */ + +inline u16 get_GL_major_version() +{ + const GLubyte *gl_version = glGetString(GL_VERSION); + std::string gl_ver((const char *)gl_version); + return (u16) (gl_version[0] - '0'); +} + video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver) { @@ -1134,7 +1140,10 @@ video::IImage * Align2Npot2(video::IImage * image, core::dimension2d dim = image->getDimension(); std::string extensions = (char*) glGetString(GL_EXTENSIONS); - if (extensions.find("GL_OES_texture_npot") != std::string::npos) { + + // Only GLES2 is trusted to correctly report npot support + if (get_GL_major_version() > 1 && + extensions.find("GL_OES_texture_npot") != std::string::npos) { return image; } diff --git a/src/drawscene.cpp b/src/drawscene.cpp index 421b96f12..7d2d1d12f 100644 --- a/src/drawscene.cpp +++ b/src/drawscene.cpp @@ -599,7 +599,8 @@ void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr, Additionally, a progressbar can be drawn when percent is set between 0 and 100. */ void draw_load_screen(const std::wstring &text, IrrlichtDevice* device, - gui::IGUIEnvironment* guienv, float dtime, int percent, bool clouds ) + gui::IGUIEnvironment* guienv, ITextureSource *tsrc, + float dtime, int percent, bool clouds) { video::IVideoDriver* driver = device->getVideoDriver(); v2u32 screensize = porting::getWindowSize(); @@ -625,27 +626,20 @@ void draw_load_screen(const std::wstring &text, IrrlichtDevice* device, // draw progress bar if ((percent >= 0) && (percent <= 100)) { - const std::string &texture_path = g_settings->get("texture_path"); - std::string tp_progress_bar = texture_path + "/progress_bar.png"; - std::string tp_progress_bar_bg = texture_path + "/progress_bar_bg.png"; - - if (!(fs::PathExists(tp_progress_bar) && - fs::PathExists(tp_progress_bar_bg))) { - std::string gamepath = fs::RemoveRelativePathComponents( - porting::path_share + DIR_DELIM + "textures"); - tp_progress_bar = gamepath + "/base/pack/progress_bar.png"; - tp_progress_bar_bg = gamepath + "/base/pack/progress_bar_bg.png"; - } - - video::ITexture *progress_img = - driver->getTexture(tp_progress_bar.c_str()); - video::ITexture *progress_img_bg = - driver->getTexture(tp_progress_bar_bg.c_str()); + video::ITexture *progress_img = tsrc->getTexture("progress_bar.png"); + video::ITexture *progress_img_bg = tsrc->getTexture("progress_bar_bg.png"); if (progress_img && progress_img_bg) { +#ifndef __ANDROID__ const core::dimension2d &img_size = progress_img_bg->getSize(); u32 imgW = rangelim(img_size.Width, 200, 600); u32 imgH = rangelim(img_size.Height, 24, 72); +#else + const core::dimension2d img_size(256, 48); + float imgRatio = (float) img_size.Height / img_size.Width; + u32 imgW = screensize.X / 2.2f; + u32 imgH = floor(imgW * imgRatio); +#endif v2s32 img_pos((screensize.X - imgW) / 2, (screensize.Y - imgH) / 2); draw2DImageFilterScaled( diff --git a/src/drawscene.h b/src/drawscene.h index 4965a0889..4a71b1f4e 100644 --- a/src/drawscene.h +++ b/src/drawscene.h @@ -27,8 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., void draw_load_screen(const std::wstring &text, IrrlichtDevice *device, - gui::IGUIEnvironment *guienv, float dtime = 0, int percent = 0, - bool clouds = true); + gui::IGUIEnvironment *guienv, ITextureSource *tsrc, float dtime = 0, + int percent = 0, bool clouds = true); void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr, Camera &camera, Client &client, LocalPlayer *player, diff --git a/src/game.cpp b/src/game.cpp index 6ef471c9e..f584a58ef 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1725,9 +1725,10 @@ bool Game::init( u16 port, const SubgameSpec &gamespec) { + texture_src = createTextureSource(device); + showOverlayMessage(wgettext("Loading..."), 0, 0); - texture_src = createTextureSource(device); shader_src = createShaderSource(device); itemdef_manager = createItemDefManager(); @@ -2183,12 +2184,14 @@ bool Game::getServerContent(bool *aborted) if (!client->itemdefReceived()) { const wchar_t *text = wgettext("Item definitions..."); progress = 25; - draw_load_screen(text, device, guienv, dtime, progress); + draw_load_screen(text, device, guienv, texture_src, + dtime, progress); delete[] text; } else if (!client->nodedefReceived()) { const wchar_t *text = wgettext("Node definitions..."); progress = 30; - draw_load_screen(text, device, guienv, dtime, progress); + draw_load_screen(text, device, guienv, texture_src, + dtime, progress); delete[] text; } else { std::stringstream message; @@ -2212,7 +2215,7 @@ bool Game::getServerContent(bool *aborted) progress = 30 + client->mediaReceiveProgress() * 35 + 0.5; draw_load_screen(utf8_to_wide(message.str()), device, - guienv, dtime, progress); + guienv, texture_src, dtime, progress); } } @@ -4357,7 +4360,8 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime) void Game::showOverlayMessage(const wchar_t *msg, float dtime, int percent, bool draw_clouds) { - draw_load_screen(msg, device, guienv, dtime, percent, draw_clouds); + draw_load_screen(msg, device, guienv, texture_src, dtime, percent, + draw_clouds); delete[] msg; } -- cgit v1.2.3 From 907be0ab918a99c30c37c4abfe3e11722691f3bc Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 17 Apr 2017 09:37:12 +0200 Subject: Remove an unused variable in Android Build --- src/client/tile.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/client/tile.cpp') diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 509da3c92..f0f1800b0 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1126,7 +1126,6 @@ video::IImage* TextureSource::generateImage(const std::string &name) inline u16 get_GL_major_version() { const GLubyte *gl_version = glGetString(GL_VERSION); - std::string gl_ver((const char *)gl_version); return (u16) (gl_version[0] - '0'); } -- cgit v1.2.3 From 35267406aa849195b72bf2edf63fecb16be013bc Mon Sep 17 00:00:00 2001 From: you Date: Fri, 19 May 2017 08:08:12 +0200 Subject: Do not create dummy normalmaps (#4180) fixes #1811 --- src/client/tile.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/client/tile.cpp') diff --git a/src/client/tile.cpp b/src/client/tile.cpp index f0f1800b0..99495132b 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1208,17 +1208,17 @@ bool TextureSource::generateImagePart(std::string part_of_name, #endif if (image == NULL) { if (part_of_name != "") { - if (part_of_name.find("_normal.png") == std::string::npos){ - errorstream<<"generateImage(): Could not load image \"" - <