From 523f0e8c5bce0cb58215dc1f9d027cf32394e3c3 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 23 Dec 2016 13:48:32 +0100 Subject: Move TileAnimation code to seperate file --- src/script/common/c_content.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 541744895..6cd1d040b 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -338,11 +338,11 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) tiledef.animation.type = (TileAnimationType) getenumfield(L, -1, "type", es_TileAnimationType, TAT_NONE); - tiledef.animation.aspect_w = + tiledef.animation.vertical_frames.aspect_w = getintfield_default(L, -1, "aspect_w", 16); - tiledef.animation.aspect_h = + tiledef.animation.vertical_frames.aspect_h = getintfield_default(L, -1, "aspect_h", 16); - tiledef.animation.length = + tiledef.animation.vertical_frames.length = getfloatfield_default(L, -1, "length", 1.0); } lua_pop(L, 1); -- cgit v1.2.3 From a07b032245bef76a7695e139a9daca7cb646a73d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 23 Dec 2016 14:43:56 +0100 Subject: Add 2D sheet animation for nodes --- doc/lua_api.txt | 21 ++++++++- games/minimal/mods/default/init.lua | 7 ++- .../textures/default_lava_source_animated.png | Bin 2902 -> 3145 bytes src/nodedef.cpp | 2 +- src/particles.cpp | 2 +- src/script/common/c_content.cpp | 25 +++++++--- src/tileanimation.cpp | 52 +++++++++++++++------ src/tileanimation.h | 6 +++ 8 files changed, 88 insertions(+), 27 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 648a29303..6166826af 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3702,7 +3702,26 @@ Definition tables * `image` (name) ### Tile animation definition -* `{type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}` + + { + type = "vertical_frames", + aspect_w = 16, + -- ^ specify width of a frame in pixels + aspect_h = 16, + -- ^ specify height of a frame in pixels + length = 3.0, + -- ^ specify full loop length + } + + { + type = "sheet_2d", + frames_w = 5, + -- ^ specify width in number of frames + frames_h = 3, + -- ^ specify height in number of frames + frame_length = 0.5, + -- ^ specify length of a single frame + } ### Node definition (`register_node`) diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index f532e7193..2f73b53ef 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1044,8 +1044,11 @@ minetest.register_node("default:lava_source", { inventory_image = minetest.inventorycube("default_lava.png"), drawtype = "liquid", --tiles ={"default_lava.png"}, - tiles ={ - {name="default_lava_source_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}} + tiles = { + { + name = "default_lava_source_animated.png", + animation = {type="sheet_2d", frames_w=3, frames_h=2, frame_length=0.5} + } }, special_tiles = { -- New-style lava source material (mostly unused) diff --git a/games/minimal/mods/default/textures/default_lava_source_animated.png b/games/minimal/mods/default/textures/default_lava_source_animated.png index aa9d57cf1..54f4c0ddd 100644 Binary files a/games/minimal/mods/default/textures/default_lava_source_animated.png and b/games/minimal/mods/default/textures/default_lava_source_animated.png differ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 21bceb94d..92cdf738e 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -528,7 +528,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, tile->material_flags = 0; if (backface_culling) tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - if (tiledef->animation.type == TAT_VERTICAL_FRAMES) + if (tiledef->animation.type != TAT_NONE) tile->material_flags |= MATERIAL_FLAG_ANIMATION; if (tiledef->tileable_horizontal) tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; diff --git a/src/particles.cpp b/src/particles.cpp index e9ddba986..97f42e2c4 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -570,7 +570,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s video::ITexture *texture; // Only use first frame of animated texture - if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) + if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) texture = tiles[texid].frames[0].texture; else texture = tiles[texid].texture; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 6cd1d040b..b9bcfef69 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -39,6 +39,7 @@ struct EnumString es_TileAnimationType[] = { {TAT_NONE, "none"}, {TAT_VERTICAL_FRAMES, "vertical_frames"}, + {TAT_SHEET_2D, "sheet_2d"}, {0, NULL}, }; @@ -334,16 +335,26 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) // animation = {} lua_getfield(L, index, "animation"); if(lua_istable(L, -1)){ - // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} tiledef.animation.type = (TileAnimationType) getenumfield(L, -1, "type", es_TileAnimationType, TAT_NONE); - tiledef.animation.vertical_frames.aspect_w = - getintfield_default(L, -1, "aspect_w", 16); - tiledef.animation.vertical_frames.aspect_h = - getintfield_default(L, -1, "aspect_h", 16); - tiledef.animation.vertical_frames.length = - getfloatfield_default(L, -1, "length", 1.0); + if (tiledef.animation.type == TAT_VERTICAL_FRAMES) { + // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} + tiledef.animation.vertical_frames.aspect_w = + getintfield_default(L, -1, "aspect_w", 16); + tiledef.animation.vertical_frames.aspect_h = + getintfield_default(L, -1, "aspect_h", 16); + tiledef.animation.vertical_frames.length = + getfloatfield_default(L, -1, "length", 1.0); + } else if (tiledef.animation.type == TAT_SHEET_2D) { + // {type="sheet_2d", frames_w=5, frames_h=3, frame_length=0.5} + getintfield(L, -1, "frames_w", + tiledef.animation.sheet_2d.frames_w); + getintfield(L, -1, "frames_h", + tiledef.animation.sheet_2d.frames_h); + getfloatfield(L, -1, "frame_length", + tiledef.animation.sheet_2d.frame_length); + } } lua_pop(L, 1); } diff --git a/src/tileanimation.cpp b/src/tileanimation.cpp index 891478c9f..a23eecc2e 100644 --- a/src/tileanimation.cpp +++ b/src/tileanimation.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., void TileAnimationParams::serialize(std::ostream &os, u16 protocol_version) const { - if(protocol_version < 29 /* TODO bump */) { + if (protocol_version < 29) { if (type == TAT_VERTICAL_FRAMES) { writeU8(os, type); writeU16(os, vertical_frames.aspect_w); @@ -41,47 +41,69 @@ void TileAnimationParams::serialize(std::ostream &os, u16 protocol_version) cons writeU16(os, vertical_frames.aspect_w); writeU16(os, vertical_frames.aspect_h); writeF1000(os, vertical_frames.length); + } else if (type == TAT_SHEET_2D) { + writeU8(os, sheet_2d.frames_w); + writeU8(os, sheet_2d.frames_h); + writeF1000(os, sheet_2d.frame_length); } } void TileAnimationParams::deSerialize(std::istream &is, u16 protocol_version) { type = (TileAnimationType) readU8(is); - if(protocol_version < 29 /* TODO bump */) { + if (protocol_version < 29) { vertical_frames.aspect_w = readU16(is); vertical_frames.aspect_h = readU16(is); vertical_frames.length = readF1000(is); return; } - if(type == TAT_VERTICAL_FRAMES) { + if (type == TAT_VERTICAL_FRAMES) { vertical_frames.aspect_w = readU16(is); vertical_frames.aspect_h = readU16(is); vertical_frames.length = readF1000(is); + } else if (type == TAT_SHEET_2D) { + sheet_2d.frames_w = readU8(is); + sheet_2d.frames_h = readU8(is); + sheet_2d.frame_length = readF1000(is); } } void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const { - if (type == TAT_NONE) { + if (type == TAT_VERTICAL_FRAMES) { + int frame_height = (float)texture_size.X / + (float)vertical_frames.aspect_w * + (float)vertical_frames.aspect_h; + int _frame_count = texture_size.Y / frame_height; + if (frame_count) + *frame_count = _frame_count; + if (frame_length_ms) + *frame_length_ms = 1000.0 * vertical_frames.length / _frame_count; + } else if (type == TAT_SHEET_2D) { + if (frame_count) + *frame_count = sheet_2d.frames_w * sheet_2d.frames_h; + if (frame_length_ms) + *frame_length_ms = 1000 * sheet_2d.frame_length; + } else { // TAT_NONE *frame_count = 1; *frame_length_ms = 1000; - return; } - int frame_height = (float)texture_size.X / - (float)vertical_frames.aspect_w * - (float)vertical_frames.aspect_h; - if (frame_count) - *frame_count = texture_size.Y / frame_height; - if (frame_length_ms) - *frame_length_ms = 1000.0 * vertical_frames.length / (texture_size.Y / frame_height); } void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const { if (type == TAT_NONE) return; - int frame_count; - determineParams(texture_size, &frame_count, NULL); - os << "^[verticalframe:" << frame_count << ":" << frame; + if (type == TAT_VERTICAL_FRAMES) { + int frame_count; + determineParams(texture_size, &frame_count, NULL); + os << "^[verticalframe:" << frame_count << ":" << frame; + } else if (type == TAT_SHEET_2D) { + int q, r; + q = frame / sheet_2d.frames_w; + r = frame % sheet_2d.frames_w; + os << "^[sheet:" << sheet_2d.frames_w << "x" << sheet_2d.frames_h + << ":" << r << "," << q; + } } diff --git a/src/tileanimation.h b/src/tileanimation.h index d5172ed50..289ce515b 100644 --- a/src/tileanimation.h +++ b/src/tileanimation.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., enum TileAnimationType { TAT_NONE = 0, TAT_VERTICAL_FRAMES = 1, + TAT_SHEET_2D = 2, }; struct TileAnimationParams { @@ -38,6 +39,11 @@ struct TileAnimationParams { int aspect_h; // height for aspect ratio float length; // seconds } vertical_frames; + struct { + int frames_w; // number of frames left-to-right + int frames_h; // number of frames top-to-bottom + float frame_length; // seconds + } sheet_2d; }; void serialize(std::ostream &os, u16 protocol_version) const; -- cgit v1.2.3 From 7279f0b37335396c85f6bdd7dc67ff56e53df0f9 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 14 Jan 2017 16:48:49 +0100 Subject: Add particle animation, glow This is implemented by reusing and extending the TileAnimation code for the methods used by particles. --- doc/lua_api.txt | 7 ++- games/minimal/mods/experimental/init.lua | 37 ++++++++++++++ src/client.h | 5 ++ src/network/clientpackethandler.cpp | 18 +++++++ src/network/networkpacket.cpp | 2 +- src/network/networkpacket.h | 5 +- src/nodedef.cpp | 2 +- src/particles.cpp | 74 ++++++++++++++++++++++----- src/particles.h | 12 ++++- src/script/common/c_content.cpp | 60 +++++++++++++--------- src/script/common/c_content.h | 1 + src/script/lua_api/l_particles.cpp | 27 +++++++++- src/server.cpp | 85 +++++++++++++++++++++++--------- src/server.h | 18 ++++--- src/tileanimation.cpp | 32 ++++++++++-- src/tileanimation.h | 4 +- 16 files changed, 311 insertions(+), 78 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c96131455..9bdc01c07 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4180,10 +4180,15 @@ The Biome API is still in an experimental phase and subject to change. -- ^ vertical: if true faces player using y axis only texture = "image.png", -- ^ Uses texture (string) - playername = "singleplayer" + playername = "singleplayer", -- ^ optional, if specified spawns particle only on the player's client + animation = {Tile Animation definition}, + -- ^ optional, specifies how to animate the particle texture + glow = 0 + -- ^ optional, specify particle self-luminescence in darkness } + ### `ParticleSpawner` definition (`add_particlespawner`) { diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index 142734cda..5e98e1a80 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -523,6 +523,43 @@ minetest.register_craft({ } }) +minetest.register_craftitem("experimental:tester_tool_2", { + description = "Tester Tool 2", + inventory_image = "experimental_tester_tool_1.png^[invert:g", + on_use = function(itemstack, user, pointed_thing) + local pos = minetest.get_pointed_thing_position(pointed_thing, true) + if pos == nil then return end + pos = vector.add(pos, {x=0, y=0.5, z=0}) + local tex, anim + if math.random(0, 1) == 0 then + tex = "default_lava_source_animated.png" + anim = {type="sheet_2d", frames_w=3, frames_h=2, frame_length=0.5} + else + tex = "default_lava_flowing_animated.png" + anim = {type="vertical_frames", aspect_w=16, aspect_h=16, length=3.3} + end + + minetest.add_particle({ + pos = pos, + velocity = {x=0, y=0, z=0}, + acceleration = {x=0, y=0.04, z=0}, + expirationtime = 6, + collisiondetection = true, + texture = tex, + animation = anim, + size = 4, + glow = math.random(0, 5), + }) + end, +}) + +minetest.register_craft({ + output = 'experimental:tester_tool_2', + recipe = { + {'group:crumbly','group:crumbly'}, + } +}) + --[[minetest.register_on_joinplayer(function(player) minetest.after(3, function() player:set_inventory_formspec("size[8,7.5]".. diff --git a/src/client.h b/src/client.h index f84246deb..b33358d94 100644 --- a/src/client.h +++ b/src/client.h @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "hud.h" #include "particles.h" #include "mapnode.h" +#include "tileanimation.h" struct MeshMakeData; class MapBlockMesh; @@ -186,6 +187,8 @@ struct ClientEvent bool collision_removal; bool vertical; std::string *texture; + struct TileAnimationParams animation; + u8 glow; } spawn_particle; struct{ u16 amount; @@ -206,6 +209,8 @@ struct ClientEvent bool vertical; std::string *texture; u32 id; + struct TileAnimationParams animation; + u8 glow; } add_particlespawner; struct{ u32 id; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 411982f69..b11f73e86 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/clientopcodes.h" #include "util/serialize.h" #include "util/srp.h" +#include "tileanimation.h" void Client::handleCommand_Deprecated(NetworkPacket* pkt) { @@ -896,9 +897,14 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) std::string texture = deSerializeLongString(is); bool vertical = false; bool collision_removal = false; + struct TileAnimationParams animation; + animation.type = TAT_NONE; + u8 glow = 0; try { vertical = readU8(is); collision_removal = readU8(is); + animation.deSerialize(is, m_proto_ver); + glow = readU8(is); } catch (...) {} ClientEvent event; @@ -912,6 +918,8 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) event.spawn_particle.collision_removal = collision_removal; event.spawn_particle.vertical = vertical; event.spawn_particle.texture = new std::string(texture); + event.spawn_particle.animation = animation; + event.spawn_particle.glow = glow; m_client_event_queue.push(event); } @@ -943,12 +951,20 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) bool vertical = false; bool collision_removal = false; + struct TileAnimationParams animation; + animation.type = TAT_NONE; + u8 glow = 0; u16 attached_id = 0; try { *pkt >> vertical; *pkt >> collision_removal; *pkt >> attached_id; + // This is horrible but required (why are there two ways to deserialize pkts?) + std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes()); + std::istringstream is(datastring, std::ios_base::binary); + animation.deSerialize(is, m_proto_ver); + glow = readU8(is); } catch (...) {} ClientEvent event; @@ -971,6 +987,8 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) event.add_particlespawner.vertical = vertical; event.add_particlespawner.texture = new std::string(texture); event.add_particlespawner.id = id; + event.add_particlespawner.animation = animation; + event.add_particlespawner.glow = glow; m_client_event_queue.push(event); } diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp index 388afc18e..91e6c58e2 100644 --- a/src/network/networkpacket.cpp +++ b/src/network/networkpacket.cpp @@ -63,7 +63,7 @@ void NetworkPacket::putRawPacket(u8 *data, u32 datasize, u16 peer_id) m_data = std::vector(&data[2], &data[2 + m_datasize]); } -char* NetworkPacket::getString(u32 from_offset) +const char* NetworkPacket::getString(u32 from_offset) { checkReadOffset(from_offset, 0); diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index 524470999..3e436aba9 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -41,12 +41,15 @@ public: u16 getPeerId() { return m_peer_id; } u16 getCommand() { return m_command; } const u32 getRemainingBytes() const { return m_datasize - m_read_offset; } + const char* getRemainingString() { return getString(m_read_offset); } // Returns a c-string without copying. // A better name for this would be getRawString() - char* getString(u32 from_offset); + const char* getString(u32 from_offset); // major difference to putCString(): doesn't write len into the buffer void putRawString(const char* src, u32 len); + void putRawString(const std::string &src) + { putRawString(src.c_str(), src.size()); } NetworkPacket& operator>>(std::string& dst); NetworkPacket& operator<<(std::string src); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index b7d023897..a4af26e87 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -541,7 +541,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, if (tile->material_flags & MATERIAL_FLAG_ANIMATION) { int frame_length_ms; tiledef->animation.determineParams(tile->texture->getOriginalSize(), - &frame_count, &frame_length_ms); + &frame_count, &frame_length_ms, NULL); tile->animation_frame_count = frame_count; tile->animation_frame_length_ms = frame_length_ms; } diff --git a/src/particles.cpp b/src/particles.cpp index d9eb3cfa5..5f17763e0 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -54,7 +54,9 @@ Particle::Particle( bool vertical, video::ITexture *texture, v2f texpos, - v2f texsize + v2f texsize, + const struct TileAnimationParams &anim, + u8 glow ): scene::ISceneNode(smgr->getRootSceneNode(), smgr) { @@ -71,7 +73,9 @@ Particle::Particle( m_material.setTexture(0, texture); m_texpos = texpos; m_texsize = texsize; - + m_animation = anim; + m_animation_frame = 0; + m_animation_time = 0.0; // Particle related m_pos = pos; @@ -84,6 +88,7 @@ Particle::Particle( m_collisiondetection = collisiondetection; m_collision_removal = collision_removal; m_vertical = vertical; + m_glow = glow; // Irrlicht stuff m_collisionbox = aabb3f @@ -142,6 +147,18 @@ void Particle::step(float dtime) m_velocity += m_acceleration * dtime; m_pos += m_velocity * dtime; } + if (m_animation.type != TAT_NONE) { + m_animation_time += dtime; + int frame_length_i, frame_count; + m_animation.determineParams( + m_material.getTexture(0)->getSize(), + &frame_count, &frame_length_i, NULL); + float frame_length = frame_length_i / 1000.0; + while (m_animation_time > frame_length) { + m_animation_frame++; + m_animation_time -= frame_length; + } + } // Update lighting updateLight(); @@ -166,16 +183,32 @@ void Particle::updateLight() else light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); - m_light = decode_light(light); + m_light = decode_light(light + m_glow); } void Particle::updateVertices() { video::SColor c(255, m_light, m_light, m_light); - f32 tx0 = m_texpos.X; - f32 tx1 = m_texpos.X + m_texsize.X; - f32 ty0 = m_texpos.Y; - f32 ty1 = m_texpos.Y + m_texsize.Y; + f32 tx0, tx1, ty0, ty1; + + if (m_animation.type != TAT_NONE) { + const v2u32 texsize = m_material.getTexture(0)->getSize(); + v2f texcoord, framesize_f; + v2u32 framesize; + texcoord = m_animation.getTextureCoords(texsize, m_animation_frame); + m_animation.determineParams(texsize, NULL, NULL, &framesize); + framesize_f = v2f(framesize.X / (float) texsize.X, framesize.Y / (float) texsize.Y); + + tx0 = m_texpos.X + texcoord.X; + tx1 = m_texpos.X + texcoord.X + framesize_f.X * m_texsize.X; + ty0 = m_texpos.Y + texcoord.Y; + ty1 = m_texpos.Y + texcoord.Y + framesize_f.Y * m_texsize.Y; + } else { + tx0 = m_texpos.X; + tx1 = m_texpos.X + m_texsize.X; + ty0 = m_texpos.Y; + 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); @@ -210,7 +243,9 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical, - video::ITexture *texture, u32 id, ParticleManager *p_manager) : + video::ITexture *texture, u32 id, const struct TileAnimationParams &anim, + u8 glow, + ParticleManager *p_manager) : m_particlemanager(p_manager) { m_gamedef = gamedef; @@ -234,6 +269,8 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, m_vertical = vertical; m_texture = texture; m_time = 0; + m_animation = anim; + m_glow = glow; for (u16 i = 0; i<=m_amount; i++) { @@ -309,7 +346,9 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) m_vertical, m_texture, v2f(0.0, 0.0), - v2f(1.0, 1.0)); + v2f(1.0, 1.0), + m_animation, + m_glow); m_particlemanager->addParticle(toadd); } i = m_spawntimes.erase(i); @@ -363,7 +402,9 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) m_vertical, m_texture, v2f(0.0, 0.0), - v2f(1.0, 1.0)); + v2f(1.0, 1.0), + m_animation, + m_glow); m_particlemanager->addParticle(toadd); } } @@ -494,6 +535,8 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, event->add_particlespawner.vertical, texture, event->add_particlespawner.id, + event->add_particlespawner.animation, + event->add_particlespawner.glow, this); /* delete allocated content of event */ @@ -529,13 +572,16 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, event->spawn_particle.vertical, texture, v2f(0.0, 0.0), - v2f(1.0, 1.0)); + v2f(1.0, 1.0), + event->spawn_particle.animation, + event->spawn_particle.glow); addParticle(toadd); delete event->spawn_particle.pos; delete event->spawn_particle.vel; delete event->spawn_particle.acc; + delete event->spawn_particle.texture; break; } @@ -564,6 +610,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s // Texture u8 texid = myrand_range(0, 5); 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) @@ -605,7 +653,9 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s false, texture, texpos, - texsize); + texsize, + anim, + 0); addParticle(toadd); } diff --git a/src/particles.h b/src/particles.h index 00cb2c08e..5464e6672 100644 --- a/src/particles.h +++ b/src/particles.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/tile.h" #include "localplayer.h" #include "environment.h" +#include "tileanimation.h" struct ClientEvent; class ParticleManager; @@ -50,7 +51,9 @@ class Particle : public scene::ISceneNode bool vertical, video::ITexture *texture, v2f texpos, - v2f texsize + v2f texsize, + const struct TileAnimationParams &anim, + u8 glow ); ~Particle(); @@ -102,6 +105,10 @@ private: bool m_collision_removal; bool m_vertical; v3s16 m_camera_offset; + struct TileAnimationParams m_animation; + float m_animation_time; + int m_animation_frame; + u8 m_glow; }; class ParticleSpawner @@ -123,6 +130,7 @@ class ParticleSpawner bool vertical, video::ITexture *texture, u32 id, + const struct TileAnimationParams &anim, u8 glow, ParticleManager* p_manager); ~ParticleSpawner(); @@ -156,6 +164,8 @@ class ParticleSpawner bool m_collision_removal; bool m_vertical; u16 m_attached_id; + struct TileAnimationParams m_animation; + u8 m_glow; }; /** diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index b9bcfef69..84af4583b 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -322,7 +322,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) } else if(lua_istable(L, index)) { - // {name="default_lava.png", animation={}} + // name="default_lava.png" tiledef.name = ""; getstringfield(L, index, "name", tiledef.name); getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. @@ -334,28 +334,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) L, index, "tileable_vertical", default_tiling); // animation = {} lua_getfield(L, index, "animation"); - if(lua_istable(L, -1)){ - tiledef.animation.type = (TileAnimationType) - getenumfield(L, -1, "type", es_TileAnimationType, - TAT_NONE); - if (tiledef.animation.type == TAT_VERTICAL_FRAMES) { - // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} - tiledef.animation.vertical_frames.aspect_w = - getintfield_default(L, -1, "aspect_w", 16); - tiledef.animation.vertical_frames.aspect_h = - getintfield_default(L, -1, "aspect_h", 16); - tiledef.animation.vertical_frames.length = - getfloatfield_default(L, -1, "length", 1.0); - } else if (tiledef.animation.type == TAT_SHEET_2D) { - // {type="sheet_2d", frames_w=5, frames_h=3, frame_length=0.5} - getintfield(L, -1, "frames_w", - tiledef.animation.sheet_2d.frames_w); - getintfield(L, -1, "frames_h", - tiledef.animation.sheet_2d.frames_h); - getfloatfield(L, -1, "frame_length", - tiledef.animation.sheet_2d.frame_length); - } - } + tiledef.animation = read_animation_definition(L, -1); lua_pop(L, 1); } @@ -925,6 +904,41 @@ void read_inventory_list(lua_State *L, int tableindex, } } +/******************************************************************************/ +struct TileAnimationParams read_animation_definition(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + struct TileAnimationParams anim; + anim.type = TAT_NONE; + if (!lua_istable(L, index)) + return anim; + + anim.type = (TileAnimationType) + getenumfield(L, index, "type", es_TileAnimationType, + TAT_NONE); + if (anim.type == TAT_VERTICAL_FRAMES) { + // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} + anim.vertical_frames.aspect_w = + getintfield_default(L, index, "aspect_w", 16); + anim.vertical_frames.aspect_h = + getintfield_default(L, index, "aspect_h", 16); + anim.vertical_frames.length = + getfloatfield_default(L, index, "length", 1.0); + } else if (anim.type == TAT_SHEET_2D) { + // {type="sheet_2d", frames_w=5, frames_h=3, frame_length=0.5} + getintfield(L, index, "frames_w", + anim.sheet_2d.frames_w); + getintfield(L, index, "frames_h", + anim.sheet_2d.frames_h); + getfloatfield(L, index, "frame_length", + anim.sheet_2d.frame_length); + } + + return anim; +} + /******************************************************************************/ ToolCapabilities read_tool_capabilities( lua_State *L, int table) diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 2a2228b6d..9641f5c9e 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -79,6 +79,7 @@ void push_hit_params (lua_State *L, ItemStack read_item (lua_State *L, int index, Server *srv); +struct TileAnimationParams read_animation_definition(lua_State *L, int index); ToolCapabilities read_tool_capabilities (lua_State *L, int table); void push_tool_capabilities (lua_State *L, diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 667ac7272..7f415844a 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_object.h" #include "lua_api/l_internal.h" #include "common/c_converter.h" +#include "common/c_content.h" #include "server.h" #include "particles.h" @@ -34,6 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc., // collision_removal = bool // vertical = bool // texture = e.g."default_wood.png" +// animation = TileAnimation definition +// glow = num int ModApiParticles::l_add_particle(lua_State *L) { MAP_LOCK_REQUIRED; @@ -47,10 +50,13 @@ int ModApiParticles::l_add_particle(lua_State *L) bool collisiondetection, vertical, collision_removal; collisiondetection = vertical = collision_removal = false; + struct TileAnimationParams animation; std::string texture = ""; std::string playername = ""; + u8 glow = 0; + if (lua_gettop(L) > 1) // deprecated { log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition"); @@ -101,11 +107,18 @@ int ModApiParticles::l_add_particle(lua_State *L) collision_removal = getboolfield_default(L, 1, "collision_removal", collision_removal); vertical = getboolfield_default(L, 1, "vertical", vertical); + + lua_getfield(L, 1, "animation"); + animation = read_animation_definition(L, -1); + lua_pop(L, 1); + texture = getstringfield_default(L, 1, "texture", ""); playername = getstringfield_default(L, 1, "playername", ""); + + glow = getintfield_default(L, 1, "glow", 0); } getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, - collisiondetection, collision_removal, vertical, texture); + collisiondetection, collision_removal, vertical, texture, animation, glow); return 1; } @@ -127,6 +140,8 @@ int ModApiParticles::l_add_particle(lua_State *L) // collision_removal = bool // vertical = bool // texture = e.g."default_wood.png" +// animation = TileAnimation definition +// glow = num int ModApiParticles::l_add_particlespawner(lua_State *L) { MAP_LOCK_REQUIRED; @@ -139,9 +154,11 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) time= minexptime= maxexptime= minsize= maxsize= 1; bool collisiondetection, vertical, collision_removal; collisiondetection = vertical = collision_removal = false; + struct TileAnimationParams animation; ServerActiveObject *attached = NULL; std::string texture = ""; std::string playername = ""; + u8 glow = 0; if (lua_gettop(L) > 1) //deprecated { @@ -201,6 +218,10 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) collision_removal = getboolfield_default(L, 1, "collision_removal", collision_removal); + lua_getfield(L, 1, "animation"); + animation = read_animation_definition(L, -1); + lua_pop(L, 1); + lua_getfield(L, 1, "attached"); if (!lua_isnil(L, -1)) { ObjectRef *ref = ObjectRef::checkobject(L, -1); @@ -211,6 +232,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) vertical = getboolfield_default(L, 1, "vertical", vertical); texture = getstringfield_default(L, 1, "texture", ""); playername = getstringfield_default(L, 1, "playername", ""); + glow = getintfield_default(L, 1, "glow", 0); } u32 id = getServer(L)->addParticleSpawner(amount, time, @@ -223,7 +245,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) collision_removal, attached, vertical, - texture, playername); + texture, playername, + animation, glow); lua_pushnumber(L, id); return 1; diff --git a/src/server.cpp b/src/server.cpp index 74d9541c9..d3d5fd3d1 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1662,12 +1662,28 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec, } // Spawns a particle on peer with peer_id -void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, +void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version, + v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture) + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow) { DSTACK(FUNCTION_NAME); + if (peer_id == PEER_ID_INEXISTENT) { + // This sucks and should be replaced by a better solution in a refactor: + std::vector clients = m_clients.getClientIDs(); + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { + RemotePlayer *player = m_env->getPlayer(*i); + if (!player) + continue; + SendSpawnParticle(*i, player->protocol_version, + pos, velocity, acceleration, + expirationtime, size, collisiondetection, + collision_removal, vertical, texture, animation, glow); + } + return; + } NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id); @@ -1676,22 +1692,39 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat pkt.putLongString(texture); pkt << vertical; pkt << collision_removal; + // This is horrible but required (why are there two ways to serialize pkts?) + std::ostringstream os(std::ios_base::binary); + animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + pkt << glow; - if (peer_id != PEER_ID_INEXISTENT) { - Send(&pkt); - } - else { - m_clients.sendToAll(0, &pkt, true); - } + Send(&pkt); } // Adds a ParticleSpawner on peer with peer_id -void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, +void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version, + u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, - u16 attached_id, bool vertical, const std::string &texture, u32 id) + u16 attached_id, bool vertical, const std::string &texture, u32 id, + const struct TileAnimationParams &animation, u8 glow) { DSTACK(FUNCTION_NAME); + if (peer_id == PEER_ID_INEXISTENT) { + // This sucks and should be replaced: + std::vector clients = m_clients.getClientIDs(); + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { + RemotePlayer *player = m_env->getPlayer(*i); + if (!player) + continue; + SendAddParticleSpawner(*i, player->protocol_version, + amount, spawntime, minpos, maxpos, + minvel, maxvel, minacc, maxacc, minexptime, maxexptime, + minsize, maxsize, collisiondetection, collision_removal, + attached_id, vertical, texture, id, animation, glow); + } + return; + } NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id); @@ -1704,13 +1737,13 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3 pkt << id << vertical; pkt << collision_removal; pkt << attached_id; + // This is horrible but required + std::ostringstream os(std::ios_base::binary); + animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + pkt << glow; - if (peer_id != PEER_ID_INEXISTENT) { - Send(&pkt); - } - else { - m_clients.sendToAll(0, &pkt, true); - } + Send(&pkt); } void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) @@ -3165,23 +3198,25 @@ void Server::spawnParticle(const std::string &playername, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture) + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow) { // m_env will be NULL if the server is initializing if (!m_env) return; - u16 peer_id = PEER_ID_INEXISTENT; + u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0; if (playername != "") { RemotePlayer *player = m_env->getPlayer(playername.c_str()); if (!player) return; peer_id = player->peer_id; + proto_ver = player->protocol_version; } - SendSpawnParticle(peer_id, pos, velocity, acceleration, + SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration, expirationtime, size, collisiondetection, - collision_removal, vertical, texture); + collision_removal, vertical, texture, animation, glow); } u32 Server::addParticleSpawner(u16 amount, float spawntime, @@ -3189,18 +3224,20 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, ServerActiveObject *attached, bool vertical, const std::string &texture, - const std::string &playername) + const std::string &playername, const struct TileAnimationParams &animation, + u8 glow) { // m_env will be NULL if the server is initializing if (!m_env) return -1; - u16 peer_id = PEER_ID_INEXISTENT; + u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0; if (playername != "") { RemotePlayer *player = m_env->getPlayer(playername.c_str()); if (!player) return -1; peer_id = player->peer_id; + proto_ver = player->protocol_version; } u16 attached_id = attached ? attached->getId() : 0; @@ -3211,11 +3248,11 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, else id = m_env->addParticleSpawner(spawntime, attached_id); - SendAddParticleSpawner(peer_id, amount, spawntime, + SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime, minpos, maxpos, minvel, maxvel, minacc, maxacc, minexptime, maxexptime, minsize, maxsize, collisiondetection, collision_removal, attached_id, vertical, - texture, id); + texture, id, animation, glow); return id; } diff --git a/src/server.h b/src/server.h index a86f75f1d..e5121bdc3 100644 --- a/src/server.h +++ b/src/server.h @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mods.h" #include "inventorymanager.h" #include "subgame.h" +#include "tileanimation.h" // struct TileAnimationParams #include "util/numeric.h" #include "util/thread.h" #include "util/basic_macros.h" @@ -252,7 +253,8 @@ public: v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture); + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow); u32 addParticleSpawner(u16 amount, float spawntime, v3f minpos, v3f maxpos, @@ -263,7 +265,8 @@ public: bool collisiondetection, bool collision_removal, ServerActiveObject *attached, bool vertical, const std::string &texture, - const std::string &playername); + const std::string &playername, const struct TileAnimationParams &animation, + u8 glow); void deleteParticleSpawner(const std::string &playername, u32 id); @@ -428,7 +431,8 @@ private: void sendDetachedInventories(u16 peer_id); // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) - void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, + void SendAddParticleSpawner(u16 peer_id, u16 protocol_version, + u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, @@ -436,16 +440,18 @@ private: float minsize, float maxsize, bool collisiondetection, bool collision_removal, u16 attached_id, - bool vertical, const std::string &texture, u32 id); + bool vertical, const std::string &texture, u32 id, + const struct TileAnimationParams &animation, u8 glow); void SendDeleteParticleSpawner(u16 peer_id, u32 id); // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all) - void SendSpawnParticle(u16 peer_id, + void SendSpawnParticle(u16 peer_id, u16 protocol_version, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture); + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow); u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas); void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true); diff --git a/src/tileanimation.cpp b/src/tileanimation.cpp index a23eecc2e..67d27d396 100644 --- a/src/tileanimation.cpp +++ b/src/tileanimation.cpp @@ -69,7 +69,8 @@ void TileAnimationParams::deSerialize(std::istream &is, u16 protocol_version) } } -void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const +void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, + int *frame_length_ms, v2u32 *frame_size) const { if (type == TAT_VERTICAL_FRAMES) { int frame_height = (float)texture_size.X / @@ -80,15 +81,17 @@ void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, *frame_count = _frame_count; if (frame_length_ms) *frame_length_ms = 1000.0 * vertical_frames.length / _frame_count; + if (frame_size) + *frame_size = v2u32(texture_size.X, frame_height); } else if (type == TAT_SHEET_2D) { if (frame_count) *frame_count = sheet_2d.frames_w * sheet_2d.frames_h; if (frame_length_ms) *frame_length_ms = 1000 * sheet_2d.frame_length; - } else { // TAT_NONE - *frame_count = 1; - *frame_length_ms = 1000; + if (frame_size) + *frame_size = v2u32(texture_size.X / sheet_2d.frames_w, texture_size.Y / sheet_2d.frames_h); } + // caller should check for TAT_NONE } void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const @@ -97,7 +100,7 @@ void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size return; if (type == TAT_VERTICAL_FRAMES) { int frame_count; - determineParams(texture_size, &frame_count, NULL); + determineParams(texture_size, &frame_count, NULL, NULL); os << "^[verticalframe:" << frame_count << ":" << frame; } else if (type == TAT_SHEET_2D) { int q, r; @@ -107,3 +110,22 @@ void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size << ":" << r << "," << q; } } + +v2f TileAnimationParams::getTextureCoords(v2u32 texture_size, int frame) const +{ + v2u32 ret(0, 0); + if (type == TAT_VERTICAL_FRAMES) { + int frame_height = (float)texture_size.X / + (float)vertical_frames.aspect_w * + (float)vertical_frames.aspect_h; + ret = v2u32(0, frame_height * frame); + } else if (type == TAT_SHEET_2D) { + v2u32 frame_size; + determineParams(texture_size, NULL, NULL, &frame_size); + int q, r; + q = frame / sheet_2d.frames_w; + r = frame % sheet_2d.frames_w; + ret = v2u32(r * frame_size.X, q * frame_size.Y); + } + return v2f(ret.X / (float) texture_size.X, ret.Y / (float) texture_size.Y); +} diff --git a/src/tileanimation.h b/src/tileanimation.h index 289ce515b..eecd3eb96 100644 --- a/src/tileanimation.h +++ b/src/tileanimation.h @@ -48,8 +48,10 @@ struct TileAnimationParams { void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is, u16 protocol_version); - void determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const; + void determineParams(v2u32 texture_size, int *frame_count, + int *frame_length_ms, v2u32 *frame_size) const; void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const; + v2f getTextureCoords(v2u32 texture_size, int frame) const; }; #endif -- 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/script/common/c_content.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 f2aa2c6a986dec47856c49ae5f54fbf3c688e027 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 31 Jan 2017 19:49:01 +0000 Subject: Add ItemStack key-value meta storage --- build/android/jni/Android.mk | 2 + doc/lua_api.txt | 45 ++++++++++--- src/CMakeLists.txt | 1 + src/content_cao.cpp | 2 +- src/craftdef.cpp | 3 +- src/inventory.cpp | 88 ++++--------------------- src/inventory.h | 14 ++-- src/itemstackmetadata.cpp | 43 ++++++++++++ src/itemstackmetadata.h | 35 ++++++++++ src/metadata.cpp | 2 + src/metadata.h | 38 +++++------ src/script/common/c_content.cpp | 31 +++++++-- src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_env.cpp | 2 +- src/script/lua_api/l_item.cpp | 38 +++++++++-- src/script/lua_api/l_item.h | 5 ++ src/script/lua_api/l_itemstackmeta.cpp | 115 +++++++++++++++++++++++++++++++++ src/script/lua_api/l_itemstackmeta.h | 59 +++++++++++++++++ src/script/scripting_game.cpp | 2 + src/util/serialize.cpp | 49 ++++++++++++++ src/util/serialize.h | 7 ++ 21 files changed, 459 insertions(+), 123 deletions(-) create mode 100644 src/itemstackmetadata.cpp create mode 100644 src/itemstackmetadata.h create mode 100644 src/script/lua_api/l_itemstackmeta.cpp create mode 100644 src/script/lua_api/l_itemstackmeta.h (limited to 'src/script/common/c_content.cpp') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 70a3d29c0..47f37cfa5 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -164,6 +164,7 @@ LOCAL_SRC_FILES := \ jni/src/inventory.cpp \ jni/src/inventorymanager.cpp \ jni/src/itemdef.cpp \ + jni/src/itemstackmetadata.cpp \ jni/src/keycode.cpp \ jni/src/light.cpp \ jni/src/localplayer.cpp \ @@ -305,6 +306,7 @@ LOCAL_SRC_FILES += \ jni/src/script/lua_api/l_env.cpp \ jni/src/script/lua_api/l_inventory.cpp \ jni/src/script/lua_api/l_item.cpp \ + jni/src/script/lua_api/l_itemstackmeta.cpp\ jni/src/script/lua_api/l_mainmenu.cpp \ jni/src/script/lua_api/l_mapgen.cpp \ jni/src/script/lua_api/l_metadata.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 219882f46..2f5e3706c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1411,7 +1411,7 @@ the entity itself. * `direction` is a unit vector, pointing from the source of the punch to the punched object. * `damage` damage that will be done to entity -Return value of this function will determin if damage is done by this function +Return value of this function will determin if damage is done by this function (retval true) or shall be done by engine (retval false) To punch an entity/object in Lua, call: @@ -1427,9 +1427,9 @@ Node Metadata ------------- The instance of a node in the world normally only contains the three values mentioned in "Nodes". However, it is possible to insert extra data into a -node. It is called "node metadata"; See "`NodeMetaRef`". +node. It is called "node metadata"; See `NodeMetaRef`. -Metadata contains two things: +Node metadata contains two things: * A key-value store * An inventory @@ -1467,6 +1467,18 @@ Example stuff: } }) +Item Metadata +------------- +Item stacks can store metadata too. See `ItemStackMetaRef`. + +Item metadata only contains a key-value store. + +Example stuff: + + local meta = stack:get_meta() + meta:set_string("key", "value") + print(dump(meta:to_table())) + Formspec -------- Formspec defines a menu. Currently not much else than inventories are @@ -2774,9 +2786,8 @@ These functions return the leftover itemstack. Class reference --------------- -### `NodeMetaRef` -Node metadata: reference extra data and functionality stored in a node. -Can be gotten via `minetest.get_meta(pos)`. +### `MetaDataRef` +See `NodeMetaRef` and `ItemStackMetaRef`. #### Methods * `set_string(name, value)` @@ -2785,13 +2796,29 @@ Can be gotten via `minetest.get_meta(pos)`. * `get_int(name)` * `set_float(name, value)` * `get_float(name)` -* `get_inventory()`: returns `InvRef` -* `to_table()`: returns `nil` or `{fields = {...}, inventory = {list1 = {}, ...}}` +* `to_table()`: returns `nil` or a table with keys: + * `fields`: key-value storage + * `inventory`: `{list1 = {}, ...}}` (NodeMetaRef only) * `from_table(nil or {})` * Any non-table value will clear the metadata - * See "Node Metadata" + * See "Node Metadata" for an example * returns `true` on success +### `NodeMetaRef` +Node metadata: reference extra data and functionality stored in a node. +Can be gotten via `minetest.get_meta(pos)`. + +#### Methods +* All methods in MetaDataRef +* `get_inventory()`: returns `InvRef` + +### `ItemStackMetaRef` +ItemStack metadata: reference extra data and functionality stored in a stack. +Can be gotten via `item:get_meta()`. + +#### Methods +* All methods in MetaDataRef + ### `NodeTimerRef` Node Timers: a high resolution persistent per-node timer. Can be gotten via `minetest.get_node_timer(pos)`. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index afb591a04..30e6c85e4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -415,6 +415,7 @@ set(common_SRCS inventory.cpp inventorymanager.cpp itemdef.cpp + itemstackmetadata.cpp light.cpp log.cpp map.cpp diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 5dc3866cf..e0b1c4cd2 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -938,7 +938,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, if(m_prop.textures.size() >= 1){ infostream<<"textures[0]: "<idef(); - ItemStack item(m_prop.textures[0], 1, 0, "", idef); + ItemStack item(m_prop.textures[0], 1, 0, idef); m_wield_meshnode = new WieldMeshSceneNode( smgr->getRootSceneNode(), smgr, -1); diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 45d3018a7..286d1eada 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -139,7 +139,7 @@ static std::vector craftGetItems( for (std::vector::size_type i = 0; i < items.size(); i++) { result.push_back(ItemStack(std::string(items[i]), (u16)1, - (u16)0, "", gamedef->getItemDefManager())); + (u16)0, gamedef->getItemDefManager())); } return result; } @@ -1126,4 +1126,3 @@ IWritableCraftDefManager* createCraftDefManager() { return new CCraftDefManager(); } - diff --git a/src/inventory.cpp b/src/inventory.cpp index cb8faecbc..6d5b49916 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -45,82 +45,16 @@ static content_t content_translate_from_19_to_internal(content_t c_from) return c_from; } -// If the string contains spaces, quotes or control characters, encodes as JSON. -// Else returns the string unmodified. -static std::string serializeJsonStringIfNeeded(const std::string &s) -{ - for(size_t i = 0; i < s.size(); ++i) - { - if(s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"') - return serializeJsonString(s); - } - return s; -} - -// Parses a string serialized by serializeJsonStringIfNeeded. -static std::string deSerializeJsonStringIfNeeded(std::istream &is) -{ - std::ostringstream tmp_os; - bool expect_initial_quote = true; - bool is_json = false; - bool was_backslash = false; - for(;;) - { - char c = is.get(); - if(is.eof()) - break; - if(expect_initial_quote && c == '"') - { - tmp_os << c; - is_json = true; - } - else if(is_json) - { - tmp_os << c; - if(was_backslash) - was_backslash = false; - else if(c == '\\') - was_backslash = true; - else if(c == '"') - break; // Found end of string - } - else - { - if(c == ' ') - { - // Found end of word - is.unget(); - break; - } - else - { - tmp_os << c; - } - } - expect_initial_quote = false; - } - if(is_json) - { - std::istringstream tmp_is(tmp_os.str(), std::ios::binary); - return deSerializeJsonString(tmp_is); - } - else - return tmp_os.str(); -} - - -ItemStack::ItemStack(std::string name_, u16 count_, - u16 wear_, std::string metadata_, - IItemDefManager *itemdef) +ItemStack::ItemStack(const std::string &name_, u16 count_, + u16 wear_, IItemDefManager *itemdef) { name = itemdef->getAlias(name_); count = count_; wear = wear_; - metadata = metadata_; - if(name.empty() || count == 0) + if (name.empty() || count == 0) clear(); - else if(itemdef->get(name).type == ITEM_TOOL) + else if (itemdef->get(name).type == ITEM_TOOL) count = 1; } @@ -137,7 +71,7 @@ void ItemStack::serialize(std::ostream &os) const parts = 2; if(wear != 0) parts = 3; - if(metadata != "") + if (!metadata.empty()) parts = 4; os<= 3) os<<" "<= 4) - os<<" "<= 4) { + os << " "; + metadata.serialize(os); + } } void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) @@ -289,7 +225,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) wear = stoi(wear_str); // Read metadata - metadata = deSerializeJsonStringIfNeeded(is); + metadata.deSerialize(is); // In case fields are added after metadata, skip space here: //std::getline(is, tmp, ' '); @@ -335,7 +271,7 @@ ItemStack ItemStack::addItem(const ItemStack &newitem_, *this = newitem; newitem.clear(); } - // If item name or metadata differs, bail out + // If item name or metadata differs, bail out else if (name != newitem.name || metadata != newitem.metadata) { @@ -375,7 +311,7 @@ bool ItemStack::itemFits(const ItemStack &newitem_, { newitem.clear(); } - // If item name or metadata differs, bail out + // If item name or metadata differs, bail out else if (name != newitem.name || metadata != newitem.metadata) { diff --git a/src/inventory.h b/src/inventory.h index 7d7e58d61..fe1639728 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "itemdef.h" #include "irrlichttypes.h" +#include "itemstackmetadata.h" #include #include #include @@ -32,10 +33,10 @@ struct ToolCapabilities; struct ItemStack { - ItemStack(): name(""), count(0), wear(0), metadata("") {} - ItemStack(std::string name_, u16 count_, - u16 wear, std::string metadata_, - IItemDefManager *itemdef); + ItemStack(): name(""), count(0), wear(0) {} + ItemStack(const std::string &name_, u16 count_, + u16 wear, IItemDefManager *itemdef); + ~ItemStack() {} // Serialization @@ -61,7 +62,7 @@ struct ItemStack name = ""; count = 0; wear = 0; - metadata = ""; + metadata.clear(); } void add(u16 n) @@ -166,7 +167,7 @@ struct ItemStack std::string name; u16 count; u16 wear; - std::string metadata; + ItemStackMetadata metadata; }; class InventoryList @@ -313,4 +314,3 @@ private: }; #endif - diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp new file mode 100644 index 000000000..c3d602245 --- /dev/null +++ b/src/itemstackmetadata.cpp @@ -0,0 +1,43 @@ +#include "itemstackmetadata.h" +#include "util/serialize.h" +#include "util/strfnd.h" + +#define DESERIALIZE_START '\x01' +#define DESERIALIZE_KV_DELIM '\x02' +#define DESERIALIZE_PAIR_DELIM '\x03' +#define DESERIALIZE_START_STR "\x01" +#define DESERIALIZE_KV_DELIM_STR "\x02" +#define DESERIALIZE_PAIR_DELIM_STR "\x03" + +void ItemStackMetadata::serialize(std::ostream &os) const +{ + std::ostringstream os2; + os2 << DESERIALIZE_START; + for (StringMap::const_iterator + it = m_stringvars.begin(); + it != m_stringvars.end(); ++it) { + os2 << it->first << DESERIALIZE_KV_DELIM + << it->second << DESERIALIZE_PAIR_DELIM; + } + os << serializeJsonStringIfNeeded(os2.str()); +} + +void ItemStackMetadata::deSerialize(std::istream &is) +{ + std::string in = deSerializeJsonStringIfNeeded(is); + + m_stringvars.clear(); + + if (!in.empty() && in[0] == DESERIALIZE_START) { + Strfnd fnd(in); + fnd.to(1); + while (!fnd.at_end()) { + std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR); + std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR); + m_stringvars[name] = var; + } + } else { + // BACKWARDS COMPATIBILITY + m_stringvars[""] = in; + } +} diff --git a/src/itemstackmetadata.h b/src/itemstackmetadata.h new file mode 100644 index 000000000..c56c58fd2 --- /dev/null +++ b/src/itemstackmetadata.h @@ -0,0 +1,35 @@ +/* +Minetest +Copyright (C) 2010-2013 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef ITEMSTACKMETADATA_HEADER +#define ITEMSTACKMETADATA_HEADER + +#include "metadata.h" + +class Inventory; +class IItemDefManager; + +class ItemStackMetadata : public Metadata +{ +public: + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is); +}; + +#endif diff --git a/src/metadata.cpp b/src/metadata.cpp index 96453d710..3cc45f919 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "log.h" #include +#include "constants.h" // MAP_BLOCKSIZE +#include /* Metadata diff --git a/src/metadata.h b/src/metadata.h index a629c0615..4bb3c2ee7 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -25,35 +25,37 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/string.h" -/* - NodeMetadata stores arbitary amounts of data for special blocks. - Used for furnaces, chests and signs. - - There are two interaction methods: inventory menu and text input. - Only one can be used for a single metadata, thus only inventory OR - text input should exist in a metadata. -*/ - -class Inventory; -class IItemDefManager; - class Metadata { public: - void clear(); - bool empty() const; + virtual ~Metadata() {} + + virtual void clear(); + virtual bool empty() const; - // Generic key/value store + bool operator==(const Metadata &other) const; + inline bool operator!=(const Metadata &other) const + { + return !(*this == other); + } + + // + // Key-value related + // + + size_t size() const; + bool contains(const std::string &name) const; const std::string &getString(const std::string &name, u16 recursion = 0) const; void setString(const std::string &name, const std::string &var); - // Support variable names in values - const std::string &resolveString(const std::string &str, u16 recursion = 0) const; const StringMap &getStrings() const { return m_stringvars; } -private: + // Add support for variable names in values + const std::string &resolveString(const std::string &str, u16 recursion = 0) const; +protected: StringMap m_stringvars; + }; #endif diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index ebc951295..8925b51f4 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -824,11 +824,32 @@ ItemStack read_item(lua_State* L, int index,Server* srv) std::string name = getstringfield_default(L, index, "name", ""); int count = getintfield_default(L, index, "count", 1); int wear = getintfield_default(L, index, "wear", 0); - std::string metadata = getstringfield_default(L, index, "metadata", ""); - return ItemStack(name, count, wear, metadata, idef); - } - else - { + + ItemStack istack(name, count, wear, idef); + + lua_getfield(L, index, "metadata"); + + // Support old metadata format by checking type + int fieldstable = lua_gettop(L); + if (lua_istable(L, fieldstable)) { + lua_pushnil(L); + while (lua_next(L, fieldstable) != 0) { + // key at index -2 and value at index -1 + std::string key = lua_tostring(L, -2); + size_t value_len; + const char *value_cs = lua_tolstring(L, -1, &value_len); + std::string value(value_cs, value_len); + istack.metadata.setString(name, value); + lua_pop(L, 1); // removes value, keeps key for next iteration + } + } else { + // BACKWARDS COMPATIBLITY + std::string value = getstringfield_default(L, index, "metadata", ""); + istack.metadata.setString("", value); + } + + return istack; + } else { throw LuaError("Expecting itemstack, itemstring, table or nil"); } } diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index efccce515..070234eba 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -5,6 +5,7 @@ set(common_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_itemstackmeta.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_mapgen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_metadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_nodemeta.cpp diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 3d9db7917..2722e35a4 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -284,7 +284,7 @@ int ModApiEnvMod::l_place_node(lua_State *L) return 1; } // Create item to place - ItemStack item(ndef->get(n).name, 1, 0, "", idef); + ItemStack item(ndef->get(n).name, 1, 0, idef); // Make pointed position PointedThing pointed; pointed.type = POINTEDTHING_NODE; diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index ff0baea14..f0293bed8 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "common/c_content.h" @@ -137,16 +138,28 @@ int LuaItemStack::l_set_wear(lua_State *L) return 1; } +// get_meta(self) -> string +int LuaItemStack::l_get_meta(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStackMetaRef::create(L, &o->m_stack); + return 1; +} + +// DEPRECATED // get_metadata(self) -> string int LuaItemStack::l_get_metadata(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); + const std::string &value = item.metadata.getString(""); + lua_pushlstring(L, value.c_str(), value.size()); return 1; } +// DEPRECATED // set_metadata(self, string) int LuaItemStack::l_set_metadata(lua_State *L) { @@ -156,7 +169,7 @@ int LuaItemStack::l_set_metadata(lua_State *L) size_t len = 0; const char *ptr = luaL_checklstring(L, 2, &len); - item.metadata.assign(ptr, len); + item.metadata.setString("", std::string(ptr, len)); lua_pushboolean(L, true); return 1; @@ -211,8 +224,24 @@ int LuaItemStack::l_to_table(lua_State *L) lua_setfield(L, -2, "count"); lua_pushinteger(L, item.wear); lua_setfield(L, -2, "wear"); - lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); - lua_setfield(L, -2, "metadata"); + + if (item.metadata.size() == 1 && item.metadata.contains("")) { + const std::string &value = item.metadata.getString(""); + lua_pushlstring(L, value.c_str(), value.size()); + lua_setfield(L, -2, "metadata"); + } else { + lua_newtable(L); + const StringMap &fields = item.metadata.getStrings(); + for (StringMap::const_iterator it = fields.begin(); + it != fields.end(); ++it) { + const std::string &name = it->first; + const std::string &value = it->second; + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + lua_setfield(L, -2, "metadata"); + } } return 1; } @@ -443,6 +472,7 @@ const luaL_reg LuaItemStack::methods[] = { luamethod(LuaItemStack, set_count), luamethod(LuaItemStack, get_wear), luamethod(LuaItemStack, set_wear), + luamethod(LuaItemStack, get_meta), luamethod(LuaItemStack, get_metadata), luamethod(LuaItemStack, set_metadata), luamethod(LuaItemStack, clear), diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h index be919b701..1ba5d79e0 100644 --- a/src/script/lua_api/l_item.h +++ b/src/script/lua_api/l_item.h @@ -56,9 +56,14 @@ private: // set_wear(self, number) static int l_set_wear(lua_State *L); + // get_meta(self) -> string + static int l_get_meta(lua_State *L); + + // DEPRECATED // get_metadata(self) -> string static int l_get_metadata(lua_State *L); + // DEPRECATED // set_metadata(self, string) static int l_set_metadata(lua_State *L); diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp new file mode 100644 index 000000000..304a7cdf3 --- /dev/null +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -0,0 +1,115 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" + +/* + NodeMetaRef +*/ +ItemStackMetaRef* ItemStackMetaRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(ItemStackMetaRef**)ud; // unbox pointer +} + +Metadata* ItemStackMetaRef::getmeta(bool auto_create) +{ + return &istack->metadata; +} + +void ItemStackMetaRef::clearMeta() +{ + istack->metadata.clear(); +} + +void ItemStackMetaRef::reportMetadataChange() +{ + // TODO +} + +// Exported functions + +// garbage collector +int ItemStackMetaRef::gc_object(lua_State *L) { + ItemStackMetaRef *o = *(ItemStackMetaRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// Creates an NodeMetaRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void ItemStackMetaRef::create(lua_State *L, ItemStack *istack) +{ + ItemStackMetaRef *o = new ItemStackMetaRef(istack); + //infostream<<"NodeMetaRef::create: o="< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef L_ITEMSTACKMETA_H_ +#define L_ITEMSTACKMETA_H_ + +#include "lua_api/l_base.h" +#include "lua_api/l_metadata.h" +#include "irrlichttypes_bloated.h" +#include "inventory.h" + +class ItemStackMetaRef : public MetaDataRef +{ +private: + ItemStack *istack; + + static const char className[]; + static const luaL_reg methods[]; + + static ItemStackMetaRef *checkobject(lua_State *L, int narg); + + virtual Metadata* getmeta(bool auto_create); + + virtual void clearMeta(); + + virtual void reportMetadataChange(); + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); +public: + ItemStackMetaRef(ItemStack *istack): istack(istack) {} + ~ItemStackMetaRef() {} + + // Creates an ItemStackMetaRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, ItemStack *istack); + + static void Register(lua_State *L); +}; + +#endif diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index e313d55f8..7becef6dc 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_env.h" #include "lua_api/l_inventory.h" #include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" #include "lua_api/l_mapgen.h" #include "lua_api/l_nodemeta.h" #include "lua_api/l_nodetimer.h" @@ -94,6 +95,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top) // Register reference classes (userdata) InvRef::Register(L); + ItemStackMetaRef::Register(L); LuaAreaStore::Register(L); LuaItemStack::Register(L); LuaPerlinNoise::Register(L); diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp index 99cb990f1..61d369bc4 100644 --- a/src/util/serialize.cpp +++ b/src/util/serialize.cpp @@ -354,6 +354,55 @@ std::string deSerializeJsonString(std::istream &is) return os.str(); } +std::string serializeJsonStringIfNeeded(const std::string &s) +{ + for (size_t i = 0; i < s.size(); ++i) { + if (s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"') + return serializeJsonString(s); + } + return s; +} + +std::string deSerializeJsonStringIfNeeded(std::istream &is) +{ + std::ostringstream tmp_os; + bool expect_initial_quote = true; + bool is_json = false; + bool was_backslash = false; + for (;;) { + char c = is.get(); + if (is.eof()) + break; + + if (expect_initial_quote && c == '"') { + tmp_os << c; + is_json = true; + } else if(is_json) { + tmp_os << c; + if (was_backslash) + was_backslash = false; + else if (c == '\\') + was_backslash = true; + else if (c == '"') + break; // Found end of string + } else { + if (c == ' ') { + // Found end of word + is.unget(); + break; + } else { + tmp_os << c; + } + } + expect_initial_quote = false; + } + if (is_json) { + std::istringstream tmp_is(tmp_os.str(), std::ios::binary); + return deSerializeJsonString(tmp_is); + } else + return tmp_os.str(); +} + //// //// String/Struct conversions //// diff --git a/src/util/serialize.h b/src/util/serialize.h index 36324a675..e22434191 100644 --- a/src/util/serialize.h +++ b/src/util/serialize.h @@ -405,6 +405,13 @@ std::string serializeJsonString(const std::string &plain); // Reads a string encoded in JSON format std::string deSerializeJsonString(std::istream &is); +// If the string contains spaces, quotes or control characters, encodes as JSON. +// Else returns the string unmodified. +std::string serializeJsonStringIfNeeded(const std::string &s); + +// Parses a string serialized by serializeJsonStringIfNeeded. +std::string deSerializeJsonStringIfNeeded(std::istream &is); + // Creates a string consisting of the hexadecimal representation of `data` std::string serializeHexString(const std::string &data, bool insert_spaces=false); -- cgit v1.2.3 From 0680c47d6c7d3e98e2b96b823f8cc9ca76d5e7f8 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 5 Feb 2017 18:15:46 +0000 Subject: Fix incompatibility of ItemStack.to_table() introduced by stack meta --- src/script/common/c_content.cpp | 15 +++++++++++++++ src/script/lua_api/l_item.cpp | 28 +++++++++++++--------------- 2 files changed, 28 insertions(+), 15 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 8925b51f4..399aa88d0 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -848,6 +848,21 @@ ItemStack read_item(lua_State* L, int index,Server* srv) istack.metadata.setString("", value); } + lua_getfield(L, index, "meta"); + fieldstable = lua_gettop(L); + if (lua_istable(L, fieldstable)) { + lua_pushnil(L); + while (lua_next(L, fieldstable) != 0) { + // key at index -2 and value at index -1 + std::string key = lua_tostring(L, -2); + size_t value_len; + const char *value_cs = lua_tolstring(L, -1, &value_len); + std::string value(value_cs, value_len); + istack.metadata.setString(name, value); + lua_pop(L, 1); // removes value, keeps key for next iteration + } + } + return istack; } else { throw LuaError("Expecting itemstack, itemstring, table or nil"); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index f0293bed8..9638740e8 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -225,23 +225,21 @@ int LuaItemStack::l_to_table(lua_State *L) lua_pushinteger(L, item.wear); lua_setfield(L, -2, "wear"); - if (item.metadata.size() == 1 && item.metadata.contains("")) { - const std::string &value = item.metadata.getString(""); + const std::string &metadata_str = item.metadata.getString(""); + lua_pushlstring(L, metadata_str.c_str(), metadata_str.size()); + lua_setfield(L, -2, "metadata"); + + lua_newtable(L); + const StringMap &fields = item.metadata.getStrings(); + for (StringMap::const_iterator it = fields.begin(); + it != fields.end(); ++it) { + const std::string &name = it->first; + const std::string &value = it->second; + lua_pushlstring(L, name.c_str(), name.size()); lua_pushlstring(L, value.c_str(), value.size()); - lua_setfield(L, -2, "metadata"); - } else { - lua_newtable(L); - const StringMap &fields = item.metadata.getStrings(); - for (StringMap::const_iterator it = fields.begin(); - it != fields.end(); ++it) { - const std::string &name = it->first; - const std::string &value = it->second; - lua_pushlstring(L, name.c_str(), name.size()); - lua_pushlstring(L, value.c_str(), value.size()); - lua_settable(L, -3); - } - lua_setfield(L, -2, "metadata"); + lua_settable(L, -3); } + lua_setfield(L, -2, "meta"); } return 1; } -- cgit v1.2.3 From 2d1fca51e975a01f84fa3fd9b266435316fbe548 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 19 Feb 2017 20:57:46 +0000 Subject: Fix wrong meta key in item meta on ItemStack construction --- src/script/common/c_content.cpp | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 399aa88d0..a963856b7 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -827,29 +827,13 @@ ItemStack read_item(lua_State* L, int index,Server* srv) ItemStack istack(name, count, wear, idef); - lua_getfield(L, index, "metadata"); - - // Support old metadata format by checking type - int fieldstable = lua_gettop(L); - if (lua_istable(L, fieldstable)) { - lua_pushnil(L); - while (lua_next(L, fieldstable) != 0) { - // key at index -2 and value at index -1 - std::string key = lua_tostring(L, -2); - size_t value_len; - const char *value_cs = lua_tolstring(L, -1, &value_len); - std::string value(value_cs, value_len); - istack.metadata.setString(name, value); - lua_pop(L, 1); // removes value, keeps key for next iteration - } - } else { - // BACKWARDS COMPATIBLITY - std::string value = getstringfield_default(L, index, "metadata", ""); - istack.metadata.setString("", value); - } + // BACKWARDS COMPATIBLITY + std::string value = getstringfield_default(L, index, "metadata", ""); + istack.metadata.setString("", value); + // Get meta lua_getfield(L, index, "meta"); - fieldstable = lua_gettop(L); + int fieldstable = lua_gettop(L); if (lua_istable(L, fieldstable)) { lua_pushnil(L); while (lua_next(L, fieldstable) != 0) { @@ -858,7 +842,7 @@ ItemStack read_item(lua_State* L, int index,Server* srv) size_t value_len; const char *value_cs = lua_tolstring(L, -1, &value_len); std::string value(value_cs, value_len); - istack.metadata.setString(name, value); + istack.metadata.setString(key, value); lua_pop(L, 1); // removes value, keeps key for next iteration } } -- cgit v1.2.3 From 88df9fb5b6c78df9485e8bf3750e2608bd78e14c Mon Sep 17 00:00:00 2001 From: red-001 Date: Tue, 31 Jan 2017 13:18:52 +0000 Subject: Add `get_wielded_item` --- clientmods/preview/init.lua | 22 ++++++++++++++++++++++ doc/client_lua_api.txt | 6 ++++++ src/gamedef.h | 1 + src/script/clientscripting.cpp | 3 +++ src/script/common/c_content.cpp | 7 ++----- src/script/common/c_content.h | 3 ++- src/script/cpp_api/s_item.cpp | 12 ++++++------ src/script/lua_api/l_base.cpp | 6 ++++++ src/script/lua_api/l_base.h | 3 +++ src/script/lua_api/l_client.cpp | 19 +++++++++++++++++++ src/script/lua_api/l_client.h | 3 ++- src/script/lua_api/l_env.cpp | 2 +- src/script/lua_api/l_inventory.cpp | 10 +++++----- src/script/lua_api/l_item.cpp | 26 +++++++++++++------------- src/script/lua_api/l_object.cpp | 2 +- 15 files changed, 92 insertions(+), 33 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index db4f28350..2ca4594d3 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -60,6 +60,28 @@ end) core.register_on_punchnode(function(pos, node) print("The local player punched a node!") + local itemstack = core.get_wielded_item() + --[[ + -- getters + print(dump(itemstack:is_empty())) + print(dump(itemstack:get_name())) + print(dump(itemstack:get_count())) + print(dump(itemstack:get_wear())) + print(dump(itemstack:get_meta())) + print(dump(itemstack:get_metadata())) + print(dump(itemstack:is_known())) + --print(dump(itemstack:get_definition())) + print(dump(itemstack:get_tool_capabilities())) + print(dump(itemstack:to_string())) + print(dump(itemstack:to_table())) + -- setters + print(dump(itemstack:set_name("default:dirt"))) + print(dump(itemstack:set_count("95"))) + print(dump(itemstack:set_wear(934))) + print(dump(itemstack:get_meta())) + print(dump(itemstack:get_metadata())) + --]] + print(dump(itemstack:to_table())) print("pos:" .. dump(pos)) print("node:" .. dump(node)) return false diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 5ffffe938..7440c4014 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -721,6 +721,7 @@ Call these functions only at load time! * `minetest.after(time, func, ...)` * Call the function `func` after `time` seconds, may be fractional * Optional: Variable number of arguments that are passed to `func` + ### Map * `minetest.get_node(pos)` * Returns the node at the given position as table in the format @@ -728,6 +729,11 @@ Call these functions only at load time! for unloaded areas. * `minetest.get_node_or_nil(pos)` * Same as `get_node` but returns `nil` for unloaded areas. + +### Player +* `minetest.get_wielded_item()` + * Returns the itemstack the local player is holding + ### Misc. * `minetest.parse_json(string[, nullvalue])`: returns something * Convert a string containing JSON data into the Lua equivalent diff --git a/src/gamedef.h b/src/gamedef.h index 16b53e24f..593d27e30 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define GAMEDEF_HEADER #include +#include #include "irrlichttypes.h" class IItemDefManager; diff --git a/src/script/clientscripting.cpp b/src/script/clientscripting.cpp index aaed2865d..c1e308012 100644 --- a/src/script/clientscripting.cpp +++ b/src/script/clientscripting.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_storage.h" #include "lua_api/l_sound.h" #include "lua_api/l_util.h" +#include "lua_api/l_item.h" ClientScripting::ClientScripting(Client *client): ScriptApiBase() @@ -55,4 +56,6 @@ void ClientScripting::InitializeModApi(lua_State *L, int top) ModApiClient::Initialize(L, top); ModApiSound::Initialize(L, top); ModApiStorage::Initialize(L, top); + + LuaItemStack::Register(L); } diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index a963856b7..99e12cd82 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_types.h" #include "nodedef.h" -#include "itemdef.h" #include "object_properties.h" #include "cpp_api/s_node.h" #include "lua_api/l_object.h" @@ -784,7 +783,7 @@ bool string_to_enum(const EnumString *spec, int &result, } /******************************************************************************/ -ItemStack read_item(lua_State* L, int index,Server* srv) +ItemStack read_item(lua_State* L, int index, IItemDefManager *idef) { if(index < 0) index = lua_gettop(L) + 1 + index; @@ -803,7 +802,6 @@ ItemStack read_item(lua_State* L, int index,Server* srv) { // Convert from itemstring std::string itemstring = lua_tostring(L, index); - IItemDefManager *idef = srv->idef(); try { ItemStack item; @@ -820,7 +818,6 @@ ItemStack read_item(lua_State* L, int index,Server* srv) else if(lua_istable(L, index)) { // Convert from table - IItemDefManager *idef = srv->idef(); std::string name = getstringfield_default(L, index, "name", ""); int count = getintfield_default(L, index, "count", 1); int wear = getintfield_default(L, index, "wear", 0); @@ -1187,7 +1184,7 @@ std::vector read_items(lua_State *L, int index, Server *srv) if (items.size() < (u32) key) { items.resize(key); } - items[key - 1] = read_item(L, -1, srv); + items[key - 1] = read_item(L, -1, srv->idef()); lua_pop(L, 1); } return items; diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 9641f5c9e..10cccbb01 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -38,6 +38,7 @@ extern "C" { #include "irrlichttypes_bloated.h" #include "util/string.h" #include "itemgroup.h" +#include "itemdef.h" namespace Json { class Value; } @@ -77,7 +78,7 @@ void push_dig_params (lua_State *L, void push_hit_params (lua_State *L, const HitParams ¶ms); -ItemStack read_item (lua_State *L, int index, Server *srv); +ItemStack read_item (lua_State *L, int index, IItemDefManager *idef); struct TileAnimationParams read_animation_definition(lua_State *L, int index); diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index 3c84fb8cf..cbb833807 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -47,7 +47,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item, PCALL_RES(lua_pcall(L, 3, 1, error_handler)); if (!lua_isnil(L, -1)) { try { - item = read_item(L,-1, getServer()); + item = read_item(L, -1, getServer()->idef()); } catch (LuaError &e) { throw LuaError(std::string(e.what()) + ". item=" + item.name); } @@ -74,7 +74,7 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item, PCALL_RES(lua_pcall(L, 3, 1, error_handler)); if (!lua_isnil(L, -1)) { try { - item = read_item(L,-1, getServer()); + item = read_item(L, -1, getServer()->idef()); } catch (LuaError &e) { throw LuaError(std::string(e.what()) + ". item=" + item.name); } @@ -101,7 +101,7 @@ bool ScriptApiItem::item_OnUse(ItemStack &item, PCALL_RES(lua_pcall(L, 3, 1, error_handler)); if(!lua_isnil(L, -1)) { try { - item = read_item(L,-1, getServer()); + item = read_item(L, -1, getServer()->idef()); } catch (LuaError &e) { throw LuaError(std::string(e.what()) + ". item=" + item.name); } @@ -127,7 +127,7 @@ bool ScriptApiItem::item_OnSecondaryUse(ItemStack &item, ServerActiveObject *use PCALL_RES(lua_pcall(L, 3, 1, error_handler)); if (!lua_isnil(L, -1)) { try { - item = read_item(L, -1, getServer()); + item = read_item(L, -1, getServer()->idef()); } catch (LuaError &e) { throw LuaError(std::string(e.what()) + ". item=" + item.name); } @@ -159,7 +159,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, PCALL_RES(lua_pcall(L, 4, 1, error_handler)); if (!lua_isnil(L, -1)) { try { - item = read_item(L,-1, getServer()); + item = read_item(L, -1, getServer()->idef()); } catch (LuaError &e) { throw LuaError(std::string(e.what()) + ". item=" + item.name); } @@ -191,7 +191,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user, PCALL_RES(lua_pcall(L, 4, 1, error_handler)); if (!lua_isnil(L, -1)) { try { - item = read_item(L,-1, getServer()); + item = read_item(L, -1, getServer()->idef()); } catch (LuaError &e) { throw LuaError(std::string(e.what()) + ". item=" + item.name); } diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index f2703718a..dfe743b72 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -43,6 +43,12 @@ Client *ModApiBase::getClient(lua_State *L) return getScriptApiBase(L)->getClient(); } #endif + +IGameDef *ModApiBase::getGameDef(lua_State *L) +{ + return getScriptApiBase(L)->getGameDef(); +} + Environment *ModApiBase::getEnv(lua_State *L) { return getScriptApiBase(L)->getEnv(); diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index dc1b1b226..cd382629d 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_types.h" #include "common/c_internal.h" +#include "gamedef.h" extern "C" { #include @@ -46,6 +47,8 @@ public: static Client* getClient(lua_State *L); #endif // !SERVER + static IGameDef* getGameDef(lua_State *L); + static Environment* getEnv(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L); // When we are not loading the mod, this function returns "." diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index 41e33889c..1673a62ce 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gettext.h" #include "common/c_converter.h" #include "common/c_content.h" +#include "lua_api/l_item.h" int ModApiClient::l_get_current_modname(lua_State *L) { @@ -132,6 +133,23 @@ int ModApiClient::l_get_node_or_nil(lua_State *L) return 1; } +int ModApiClient::l_get_wielded_item(lua_State *L) +{ + Client *client = getClient(L); + + Inventory local_inventory(client->idef()); + client->getLocalInventory(local_inventory); + + InventoryList *mlist = local_inventory.getList("main"); + + if (mlist && client->getPlayerItem() < mlist->getSize()) { + LuaItemStack::create(L, mlist->getItem(client->getPlayerItem())); + } else { + LuaItemStack::create(L, ItemStack()); + } + return 1; +} + void ModApiClient::Initialize(lua_State *L, int top) { API_FCT(get_current_modname); @@ -143,4 +161,5 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(gettext); API_FCT(get_node); API_FCT(get_node_or_nil); + API_FCT(get_wielded_item); } diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index 207a5bca0..def9b48a3 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -53,7 +53,8 @@ private: // get_node_or_nil(pos) static int l_get_node_or_nil(lua_State *L); - + // get_wielded_item() + static int l_get_wielded_item(lua_State *L); public: static void Initialize(lua_State *L, int top); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 442c4b99a..14df558d3 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -472,7 +472,7 @@ int ModApiEnvMod::l_add_item(lua_State *L) // pos //v3f pos = checkFloatPos(L, 1); // item - ItemStack item = read_item(L, 2,getServer(L)); + ItemStack item = read_item(L, 2,getServer(L)->idef()); if(item.empty() || !item.isKnown(getServer(L)->idef())) return 0; diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 38eade609..9a4aa845d 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -194,7 +194,7 @@ int InvRef::l_set_stack(lua_State *L) InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); int i = luaL_checknumber(L, 3) - 1; - ItemStack newitem = read_item(L, 4, getServer(L)); + ItemStack newitem = read_item(L, 4, getServer(L)->idef()); InventoryList *list = getlist(L, ref, listname); if(list != NULL && i >= 0 && i < (int) list->getSize()){ list->changeItem(i, newitem); @@ -295,7 +295,7 @@ int InvRef::l_add_item(lua_State *L) NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3, getServer(L)); + ItemStack item = read_item(L, 3, getServer(L)->idef()); InventoryList *list = getlist(L, ref, listname); if(list){ ItemStack leftover = list->addItem(item); @@ -315,7 +315,7 @@ int InvRef::l_room_for_item(lua_State *L) NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3, getServer(L)); + ItemStack item = read_item(L, 3, getServer(L)->idef()); InventoryList *list = getlist(L, ref, listname); if(list){ lua_pushboolean(L, list->roomForItem(item)); @@ -332,7 +332,7 @@ int InvRef::l_contains_item(lua_State *L) NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3, getServer(L)); + ItemStack item = read_item(L, 3, getServer(L)->idef()); InventoryList *list = getlist(L, ref, listname); if(list){ lua_pushboolean(L, list->containsItem(item)); @@ -349,7 +349,7 @@ int InvRef::l_remove_item(lua_State *L) NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3, getServer(L)); + ItemStack item = read_item(L, 3, getServer(L)->idef()); InventoryList *list = getlist(L, ref, listname); if(list){ ItemStack removed = list->removeItem(item); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 9638740e8..7e6f457e1 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -190,7 +190,7 @@ int LuaItemStack::l_replace(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); - o->m_stack = read_item(L,2,getServer(L)); + o->m_stack = read_item(L, 2, getGameDef(L)->idef()); lua_pushboolean(L, true); return 1; } @@ -250,7 +250,7 @@ int LuaItemStack::l_get_stack_max(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - lua_pushinteger(L, item.getStackMax(getServer(L)->idef())); + lua_pushinteger(L, item.getStackMax(getGameDef(L)->idef())); return 1; } @@ -260,7 +260,7 @@ int LuaItemStack::l_get_free_space(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - lua_pushinteger(L, item.freeSpace(getServer(L)->idef())); + lua_pushinteger(L, item.freeSpace(getGameDef(L)->idef())); return 1; } @@ -271,7 +271,7 @@ int LuaItemStack::l_is_known(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - bool is_known = item.isKnown(getServer(L)->idef()); + bool is_known = item.isKnown(getGameDef(L)->idef()); lua_pushboolean(L, is_known); return 1; } @@ -307,7 +307,7 @@ int LuaItemStack::l_get_tool_capabilities(lua_State *L) LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; const ToolCapabilities &prop = - item.getToolCapabilities(getServer(L)->idef()); + item.getToolCapabilities(getGameDef(L)->idef()); push_tool_capabilities(L, prop); return 1; } @@ -322,7 +322,7 @@ int LuaItemStack::l_add_wear(lua_State *L) LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; int amount = lua_tointeger(L, 2); - bool result = item.addWear(amount, getServer(L)->idef()); + bool result = item.addWear(amount, getGameDef(L)->idef()); lua_pushboolean(L, result); return 1; } @@ -334,8 +334,8 @@ int LuaItemStack::l_add_item(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - ItemStack newitem = read_item(L,-1, getServer(L)); - ItemStack leftover = item.addItem(newitem, getServer(L)->idef()); + ItemStack newitem = read_item(L, -1, getGameDef(L)->idef()); + ItemStack leftover = item.addItem(newitem, getGameDef(L)->idef()); create(L, leftover); return 1; } @@ -348,9 +348,9 @@ int LuaItemStack::l_item_fits(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - ItemStack newitem = read_item(L, 2, getServer(L)); + ItemStack newitem = read_item(L, 2, getGameDef(L)->idef()); ItemStack restitem; - bool fits = item.itemFits(newitem, &restitem, getServer(L)->idef()); + bool fits = item.itemFits(newitem, &restitem, getGameDef(L)->idef()); lua_pushboolean(L, fits); // first return value create(L, restitem); // second return value return 2; @@ -407,7 +407,7 @@ ItemStack& LuaItemStack::getItem() int LuaItemStack::create_object(lua_State *L) { NO_MAP_LOCK_REQUIRED; - ItemStack item = read_item(L, 1, getServer(L)); + ItemStack item = read_item(L, 1, getGameDef(L)->idef()); LuaItemStack *o = new LuaItemStack(item); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); @@ -596,7 +596,7 @@ int ModApiItemMod::l_get_content_id(lua_State *L) NO_MAP_LOCK_REQUIRED; std::string name = luaL_checkstring(L, 1); - INodeDefManager *ndef = getServer(L)->getNodeDefManager(); + INodeDefManager *ndef = getGameDef(L)->getNodeDefManager(); content_t c = ndef->getId(name); lua_pushinteger(L, c); @@ -609,7 +609,7 @@ int ModApiItemMod::l_get_name_from_content_id(lua_State *L) NO_MAP_LOCK_REQUIRED; content_t c = luaL_checkint(L, 1); - INodeDefManager *ndef = getServer(L)->getNodeDefManager(); + INodeDefManager *ndef = getGameDef(L)->getNodeDefManager(); const char *name = ndef->get(c).name.c_str(); lua_pushstring(L, name); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index be454ad45..d5681b809 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -364,7 +364,7 @@ int ObjectRef::l_set_wielded_item(lua_State *L) ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; // Do it - ItemStack item = read_item(L, 2, getServer(L)); + ItemStack item = read_item(L, 2, getServer(L)->idef()); bool success = co->setWieldedItem(item); if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { getServer(L)->SendInventory(((PlayerSAO*)co)); -- 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/script/common/c_content.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 f3fe62a0bf9e775b3e6e838f104ab605a2238792 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Wed, 19 Apr 2017 23:02:07 +0200 Subject: Fix various copy instead of const ref reported by cppcheck (#5615) * Also remove InventoryList::peekItem unused function * Fix some post increment to preincrement reported by cppcheck --- src/chat.cpp | 2 +- src/chat.h | 6 +++--- src/craftdef.h | 11 +++++++---- src/environment.cpp | 4 ++-- src/exceptions.h | 9 +++------ src/game.cpp | 9 ++------- src/httpfetch.cpp | 5 +++-- src/inventory.cpp | 28 +++++++++------------------- src/inventory.h | 7 ++----- src/script/common/c_content.cpp | 19 +++++++------------ src/script/common/c_content.h | 4 ++-- src/script/lua_api/l_item.cpp | 2 +- src/tool.h | 8 ++++---- 13 files changed, 46 insertions(+), 68 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/src/chat.cpp b/src/chat.cpp index 46555b3dc..de7483e22 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -386,7 +386,7 @@ s32 ChatBuffer::getBottomScrollPos() const -ChatPrompt::ChatPrompt(std::wstring prompt, u32 history_limit): +ChatPrompt::ChatPrompt(const std::wstring &prompt, u32 history_limit): m_prompt(prompt), m_line(L""), m_history(), diff --git a/src/chat.h b/src/chat.h index 11061fd39..5de676a2e 100644 --- a/src/chat.h +++ b/src/chat.h @@ -38,14 +38,14 @@ struct ChatLine // message text EnrichedString text; - ChatLine(std::wstring a_name, std::wstring a_text): + ChatLine(const std::wstring &a_name, const std::wstring &a_text): age(0.0), name(a_name), text(a_text) { } - ChatLine(EnrichedString a_name, EnrichedString a_text): + ChatLine(const EnrichedString &a_name, const EnrichedString &a_text): age(0.0), name(a_name), text(a_text) @@ -148,7 +148,7 @@ private: class ChatPrompt { public: - ChatPrompt(std::wstring prompt, u32 history_limit); + ChatPrompt(const std::wstring &prompt, u32 history_limit); ~ChatPrompt(); // Input character or string diff --git a/src/craftdef.h b/src/craftdef.h index 695ee0c2c..bdd741f7c 100644 --- a/src/craftdef.h +++ b/src/craftdef.h @@ -97,7 +97,7 @@ struct CraftOutput CraftOutput(): item(""), time(0) {} - CraftOutput(std::string item_, float time_): + CraftOutput(const std::string &item_, float time_): item(item_), time(time_) {} std::string dump() const; @@ -124,7 +124,7 @@ struct CraftReplacements CraftReplacements(): pairs() {} - CraftReplacements(std::vector > pairs_): + CraftReplacements(const std::vector > &pairs_): pairs(pairs_) {} std::string dump() const; @@ -359,10 +359,13 @@ public: CraftDefinitionFuel(): recipe(""), hash_inited(false), burntime() {} - CraftDefinitionFuel(std::string recipe_, + CraftDefinitionFuel(const std::string &recipe_, float burntime_, const CraftReplacements &replacements_): - recipe(recipe_), hash_inited(false), burntime(burntime_), replacements(replacements_) + recipe(recipe_), + hash_inited(false), + burntime(burntime_), + replacements(replacements_) {} virtual ~CraftDefinitionFuel(){} diff --git a/src/environment.cpp b/src/environment.cpp index 9c2ea8896..c1aeeec60 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -70,7 +70,7 @@ void Environment::setTimeOfDay(u32 time) { MutexAutoLock lock(this->m_time_lock); if (m_time_of_day > time) - m_day_count++; + ++m_day_count; m_time_of_day = time; m_time_of_day_f = (float)time / 24000.0; } @@ -103,7 +103,7 @@ void Environment::stepTimeOfDay(float dtime) // Sync at overflow if (m_time_of_day + units >= 24000) { sync_f = true; - m_day_count++; + ++m_day_count; } m_time_of_day = (m_time_of_day + units) % 24000; if (sync_f) diff --git a/src/exceptions.h b/src/exceptions.h index 67a2d0df6..1b39c6725 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -27,10 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class BaseException : public std::exception { public: - BaseException(const std::string &s) throw() - { - m_s = s; - } + BaseException(const std::string &s) throw(): m_s(s) {} ~BaseException() throw() {} virtual const char * what() const throw() { @@ -122,12 +119,12 @@ public: class ClientStateError : public BaseException { public: - ClientStateError(std::string s): BaseException(s) {} + ClientStateError(const std::string &s): BaseException(s) {} }; class PrngException : public BaseException { public: - PrngException(std::string s): BaseException(s) {} + PrngException(const std::string &s): BaseException(s) {} }; class ModError : public BaseException { diff --git a/src/game.cpp b/src/game.cpp index bcf378e4e..fbc0cf35f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -127,8 +127,8 @@ struct TextDestPlayerInventory : public TextDest struct LocalFormspecHandler : public TextDest { - LocalFormspecHandler(std::string formname): - m_client(0) + LocalFormspecHandler(const std::string &formname): + m_client(NULL) { m_formname = formname; } @@ -139,11 +139,6 @@ struct LocalFormspecHandler : public TextDest m_formname = formname; } - void gotText(const std::wstring &message) - { - errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl; - } - void gotText(const StringMap &fields) { if (m_formname == "MT_PAUSE_MENU") { diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index f64c9f717..21400355a 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -207,7 +207,7 @@ public: class HTTPFetchOngoing { public: - HTTPFetchOngoing(HTTPFetchRequest request, CurlHandlePool *pool); + HTTPFetchOngoing(const HTTPFetchRequest &request, CurlHandlePool *pool); ~HTTPFetchOngoing(); CURLcode start(CURLM *multi); @@ -228,7 +228,8 @@ private: }; -HTTPFetchOngoing::HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *pool_): +HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_, + CurlHandlePool *pool_): pool(pool_), curl(NULL), multi(NULL), diff --git a/src/inventory.cpp b/src/inventory.cpp index 6d5b49916..8617f7263 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -46,12 +46,11 @@ static content_t content_translate_from_19_to_internal(content_t c_from) } ItemStack::ItemStack(const std::string &name_, u16 count_, - u16 wear_, IItemDefManager *itemdef) + u16 wear_, IItemDefManager *itemdef) : + name(itemdef->getAlias(name_)), + count(count_), + wear(wear_) { - name = itemdef->getAlias(name_); - count = count_; - wear = wear_; - if (name.empty() || count == 0) clear(); else if (itemdef->get(name).type == ITEM_TOOL) @@ -370,14 +369,13 @@ ItemStack ItemStack::peekItem(u32 peekcount) const Inventory */ -InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef) +InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef): + m_name(name), + m_size(size), + m_width(0), + m_itemdef(itemdef) { - m_name = name; - m_size = size; - m_width = 0; - m_itemdef = itemdef; clearItems(); - //m_dirty = false; } InventoryList::~InventoryList() @@ -712,14 +710,6 @@ ItemStack InventoryList::takeItem(u32 i, u32 takecount) return taken; } -ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const -{ - if(i >= m_items.size()) - return ItemStack(); - - return m_items[i].peekItem(peekcount); -} - void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count) { // Take item from source list diff --git a/src/inventory.h b/src/inventory.h index fe1639728..a9fef3b05 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -173,7 +173,7 @@ struct ItemStack class InventoryList { public: - InventoryList(std::string name, u32 size, IItemDefManager *itemdef); + InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef); ~InventoryList(); void clearItems(); void setSize(u32 newsize); @@ -239,9 +239,6 @@ public: // Returns empty item if couldn't take any. ItemStack takeItem(u32 i, u32 takecount); - // Similar to takeItem, but keeps the slot intact. - ItemStack peekItem(u32 i, u32 peekcount) const; - // Move an item to a different list (or a different stack in the same list) // count is the maximum number of items to move (0 for everything) // returns number of moved items @@ -254,8 +251,8 @@ public: private: std::vector m_items; - u32 m_size, m_width; std::string m_name; + u32 m_size, m_width; IItemDefManager *m_itemdef; }; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index bcae874b9..8dfb851e6 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -43,15 +43,12 @@ struct EnumString es_TileAnimationType[] = }; /******************************************************************************/ -ItemDefinition read_item_definition(lua_State* L,int index, - ItemDefinition default_def) +void read_item_definition(lua_State* L, int index, + const ItemDefinition &default_def, ItemDefinition &def) { - if(index < 0) + if (index < 0) index = lua_gettop(L) + 1 + index; - // Read the item definition - ItemDefinition def = default_def; - def.type = (ItemType)getenumfield(L, index, "type", es_ItemType, ITEM_NONE); getstringfield(L, index, "name", def.name); @@ -118,8 +115,6 @@ ItemDefinition read_item_definition(lua_State* L,int index, // "" = no prediction getstringfield(L, index, "node_placement_prediction", def.node_placement_prediction); - - return def; } /******************************************************************************/ @@ -873,7 +868,7 @@ void push_tool_capabilities(lua_State *L, lua_newtable(L); // For each groupcap for (ToolGCMap::const_iterator i = toolcap.groupcaps.begin(); - i != toolcap.groupcaps.end(); i++) { + i != toolcap.groupcaps.end(); ++i) { // Create groupcap table lua_newtable(L); const std::string &name = i->first; @@ -881,7 +876,7 @@ void push_tool_capabilities(lua_State *L, // Create subtable "times" lua_newtable(L); for (UNORDERED_MAP::const_iterator - i = groupcap.times.begin(); i != groupcap.times.end(); i++) { + i = groupcap.times.begin(); i != groupcap.times.end(); ++i) { lua_pushinteger(L, i->first); lua_pushnumber(L, i->second); lua_settable(L, -3); @@ -900,7 +895,7 @@ void push_tool_capabilities(lua_State *L, lua_newtable(L); // For each damage group for (DamageGroup::const_iterator i = toolcap.damageGroups.begin(); - i != toolcap.damageGroups.end(); i++) { + i != toolcap.damageGroups.end(); ++i) { // Create damage group table lua_pushinteger(L, i->second); lua_setfield(L, -2, i->first.c_str()); @@ -939,7 +934,7 @@ void read_inventory_list(lua_State *L, int tableindex, InventoryList *invlist = inv->addList(name, listsize); int index = 0; for(std::vector::const_iterator - i = items.begin(); i != items.end(); i++){ + i = items.begin(); i != items.end(); ++i){ if(forcesize != -1 && index == forcesize) break; invlist->changeItem(index, *i); diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 949b136eb..c701c0384 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -86,8 +86,8 @@ ToolCapabilities read_tool_capabilities (lua_State *L, int table); void push_tool_capabilities (lua_State *L, const ToolCapabilities &prop); -ItemDefinition read_item_definition (lua_State *L, int index, - ItemDefinition default_def); +void read_item_definition (lua_State *L, int index, const ItemDefinition &default_def, + ItemDefinition &def); void read_object_properties (lua_State *L, int index, ObjectProperties *prop, IItemDefManager *idef); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 2d32093a7..0e4fc4ef0 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -526,7 +526,7 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) def.node_placement_prediction = "__default"; // Read the item definition - def = read_item_definition(L, table, def); + read_item_definition(L, table, def, def); // Default to having client-side placement prediction for nodes // ("" in item definition sets it off) diff --git a/src/tool.h b/src/tool.h index ebba5b749..f33152355 100644 --- a/src/tool.h +++ b/src/tool.h @@ -63,8 +63,8 @@ struct ToolCapabilities ToolCapabilities( float full_punch_interval_=1.4, int max_drop_level_=1, - ToolGCMap groupcaps_=ToolGCMap(), - DamageGroup damageGroups_=DamageGroup() + const ToolGCMap &groupcaps_ = ToolGCMap(), + const DamageGroup &damageGroups_ = DamageGroup() ): full_punch_interval(full_punch_interval_), max_drop_level(max_drop_level_), @@ -85,8 +85,8 @@ struct DigParams u16 wear; std::string main_group; - DigParams(bool a_diggable=false, float a_time=0, u16 a_wear=0, - std::string a_main_group=""): + DigParams(bool a_diggable = false, float a_time = 0.0f, u16 a_wear = 0, + const std::string &a_main_group = ""): diggable(a_diggable), time(a_time), wear(a_wear), -- cgit v1.2.3 From 1ffb180868ffcec6812cd3aac8f56ffefb91c8bc Mon Sep 17 00:00:00 2001 From: Dániel Juhász Date: Fri, 21 Apr 2017 15:34:59 +0200 Subject: Soft node overlay (#5186) This commit adds node overlays, which are tiles that are drawn on top of other tiles. --- doc/lua_api.txt | 6 + src/client.cpp | 16 +- src/client/tile.h | 96 +++++--- src/clientmap.cpp | 56 +++-- src/content_mapblock.cpp | 61 ++--- src/content_mapblock.h | 4 +- src/hud.cpp | 5 +- src/mapblock_mesh.cpp | 525 ++++++++++++++++++++++++---------------- src/mapblock_mesh.h | 57 +++-- src/mesh.cpp | 80 +++--- src/mesh.h | 7 +- src/network/networkprotocol.h | 4 +- src/nodedef.cpp | 36 ++- src/nodedef.h | 4 +- src/particles.cpp | 2 +- src/script/common/c_content.cpp | 28 +++ src/wieldmesh.cpp | 188 +++++++------- src/wieldmesh.h | 55 ++++- 18 files changed, 763 insertions(+), 467 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 16e662e0c..4e328ac76 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3908,6 +3908,12 @@ Definition tables tiles = {tile definition 1, def2, def3, def4, def5, def6}, --[[ ^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images) ^ List can be shortened to needed length ]] + overlay_tiles = {tile definition 1, def2, def3, def4, def5, def6}, --[[ + ^ Same as `tiles`, but these textures are drawn on top of the + ^ base tiles. You can use this to colorize only specific parts of + ^ your texture. If the texture name is an empty string, that + ^ overlay is not drawn. Since such tiles are drawn twice, it + ^ is not recommended to use overlays on very common nodes. 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 ]] diff --git a/src/client.cpp b/src/client.cpp index 3cea4fbf4..ce42d025e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -487,13 +487,17 @@ void Client::step(float dtime) minimap_mapblock = r.mesh->moveMinimapMapblock(); if (minimap_mapblock == NULL) do_mapper_update = false; - } - if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) { - delete r.mesh; - } else { - // Replace with the new mesh - block->mesh = r.mesh; + bool is_empty = true; + for (int l = 0; l < MAX_TILE_LAYERS; l++) + if (r.mesh->getMesh(l)->getMeshBufferCount() != 0) + is_empty = false; + + if (is_empty) + delete r.mesh; + else + // Replace with the new mesh + block->mesh = r.mesh; } } else { delete r.mesh; diff --git a/src/client/tile.h b/src/client/tile.h index ef01d2a1f..c6ebee006 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -194,19 +194,22 @@ struct FrameSpec video::ITexture *flags_texture; }; -struct TileSpec +#define MAX_TILE_LAYERS 2 + +//! Defines a layer of a tile. +struct TileLayer { - TileSpec(): + TileLayer(): texture(NULL), texture_id(0), color(), material_type(TILE_MATERIAL_BASIC), material_flags( //0 // <- DEBUG, Use the one below - MATERIAL_FLAG_BACKFACE_CULLING + MATERIAL_FLAG_BACKFACE_CULLING | + MATERIAL_FLAG_TILEABLE_HORIZONTAL| + MATERIAL_FLAG_TILEABLE_VERTICAL ), - rotation(0), - emissive_light(0), shader_id(0), normal_texture(NULL), flags_texture(NULL), @@ -217,49 +220,41 @@ struct TileSpec } /*! - * Two tiles are equal if they can be appended to - * the same mesh buffer. + * Two layers are equal if they can be merged. */ - bool operator==(const TileSpec &other) const + bool operator==(const TileLayer &other) const { - return ( + return texture_id == other.texture_id && material_type == other.material_type && material_flags == other.material_flags && - rotation == other.rotation - ); + color == other.color; } /*! - * Two tiles are not equal if they must be in different mesh buffers. + * Two tiles are not equal if they must have different vertices. */ - bool operator!=(const TileSpec &other) const + bool operator!=(const TileLayer &other) const { return !(*this == other); } - + // Sets everything else except the texture in the material void applyMaterialOptions(video::SMaterial &material) const { switch (material_type) { case TILE_MATERIAL_BASIC: + case TILE_MATERIAL_WAVING_LEAVES: + case TILE_MATERIAL_WAVING_PLANTS: material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; break; case TILE_MATERIAL_ALPHA: - material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - break; case TILE_MATERIAL_LIQUID_TRANSPARENT: material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_OPAQUE: material.MaterialType = video::EMT_SOLID; break; - case TILE_MATERIAL_WAVING_LEAVES: - material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - break; - case TILE_MATERIAL_WAVING_PLANTS: - material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - break; } material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false; @@ -285,26 +280,26 @@ struct TileSpec } } - // ordered for performance! please do not reorder unless you pahole it first. + bool isTileable() const + { + return (material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL) + && (material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL); + } + video::ITexture *texture; u32 texture_id; - // The color of the tile, or if the tile does not own - // a color then the color of the node owning this tile. + /*! + * 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; // Material parameters u8 material_type; u8 material_flags; - - u8 rotation; - //! This much light does the tile emit. - u8 emissive_light; - u32 shader_id; - video::ITexture *normal_texture; - // cacheline (64) - video::ITexture *flags_texture; + // Animation parameters u16 animation_frame_length_ms; u8 animation_frame_count; @@ -313,4 +308,39 @@ struct TileSpec std::vector frames; }; + +/*! + * Defines a face of a node. May have up to two layers. + */ +struct TileSpec +{ + TileSpec(): + rotation(0), + emissive_light(0) + { + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) + layers[layer] = TileLayer(); + } + + /*! + * Returns true if this tile can be merged with the other tile. + */ + bool isTileable(const TileSpec &other) const { + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + if (layers[layer] != other.layers[layer]) + return false; + if (!layers[layer].isTileable()) + return false; + } + return rotation == 0 + && rotation == other.rotation + && emissive_light == other.emissive_light; + } + + u8 rotation; + //! This much light does the tile emit. + u8 emissive_light; + //! The first is base texture, the second is overlay. + TileLayer layers[MAX_TILE_LAYERS]; +}; #endif diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 4cae03bf2..6cd24ffc6 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -290,6 +290,11 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) struct MeshBufList { + /*! + * Specifies in which layer the list is. + * All lists which are in a lower layer are rendered before this list. + */ + u8 layer; video::SMaterial m; std::vector bufs; }; @@ -303,7 +308,7 @@ struct MeshBufListList lists.clear(); } - void add(scene::IMeshBuffer *buf) + void add(scene::IMeshBuffer *buf, u8 layer) { const video::SMaterial &m = buf->getMaterial(); for(std::vector::iterator i = lists.begin(); @@ -315,12 +320,16 @@ struct MeshBufListList if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture) continue; + if(l.layer != layer) + continue; + if (l.m == m) { l.bufs.push_back(buf); return; } } MeshBufList l; + l.layer = layer; l.m = m; l.bufs.push_back(buf); lists.push_back(l); @@ -434,29 +443,34 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); - scene::IMesh *mesh = mapBlockMesh->getMesh(); - assert(mesh); + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + scene::IMesh *mesh = mapBlockMesh->getMesh(layer); + assert(mesh); - u32 c = mesh->getMeshBufferCount(); - for (u32 i = 0; i < c; i++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + u32 c = mesh->getMeshBufferCount(); + for (u32 i = 0; i < c; i++) { + scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); - video::SMaterial& material = buf->getMaterial(); - video::IMaterialRenderer* rnd = + video::SMaterial& material = buf->getMaterial(); + video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); - bool transparent = (rnd && rnd->isTransparent()); - if (transparent == is_transparent_pass) { - if (buf->getVertexCount() == 0) - errorstream << "Block [" << analyze_block(block) - << "] contains an empty meshbuf" << std::endl; - - material.setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter); - material.setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter); - material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter); - material.setFlag(video::EMF_WIREFRAME, m_control.show_wireframe); - - drawbufs.add(buf); + bool transparent = (rnd && rnd->isTransparent()); + if (transparent == is_transparent_pass) { + if (buf->getVertexCount() == 0) + errorstream << "Block [" << analyze_block(block) + << "] contains an empty meshbuf" << std::endl; + + material.setFlag(video::EMF_TRILINEAR_FILTER, + m_cache_trilinear_filter); + material.setFlag(video::EMF_BILINEAR_FILTER, + m_cache_bilinear_filter); + material.setFlag(video::EMF_ANISOTROPIC_FILTER, + m_cache_anistropic_filter); + material.setFlag(video::EMF_WIREFRAME, + m_control.show_wireframe); + + drawbufs.add(buf, layer); + } } } } diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 153dacf42..9f4223bac 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -78,17 +78,19 @@ void MapblockMeshGenerator::useTile(int index, bool disable_backface_culling) { tile = getNodeTileN(n, p, index, data); if (!data->m_smooth_lighting) - color = encode_light_and_color(light, tile.color, f->light_source); - if (disable_backface_culling) - tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; - tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; + color = encode_light(light, f->light_source); + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + tile.layers[layer].material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; + if (disable_backface_culling) + tile.layers[layer].material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; + } } void MapblockMeshGenerator::useDefaultTile(bool set_color) { tile = getNodeTile(n, p, v3s16(0, 0, 0), data); if (set_color && !data->m_smooth_lighting) - color = encode_light_and_color(light, tile.color, f->light_source); + color = encode_light(light, f->light_source); } TileSpec MapblockMeshGenerator::getTile(const v3s16& direction) @@ -106,7 +108,7 @@ void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal) vertices[j].Pos = coords[j] + origin; vertices[j].Normal = normal2; if (data->m_smooth_lighting) - vertices[j].Color = blendLight(coords[j], tile.color); + vertices[j].Color = blendLight(coords[j]); else vertices[j].Color = color; if (shade_face) @@ -137,8 +139,7 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box, video::SColor colors[6]; if (!data->m_smooth_lighting) { for (int face = 0; face != 6; ++face) { - int tileindex = MYMIN(face, tilecount - 1); - colors[face] = encode_light_and_color(light, tiles[tileindex].color, f->light_source); + colors[face] = encode_light(light, f->light_source); } if (!f->light_source) { applyFacesShading(colors[0], v3f(0, 1, 0)); @@ -240,9 +241,8 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box, if (data->m_smooth_lighting) { for (int j = 0; j < 24; ++j) { - int tileindex = MYMIN(j / 4, tilecount - 1); - vertices[j].Color = encode_light_and_color(lights[light_indices[j]], - tiles[tileindex].color, f->light_source); + vertices[j].Color = encode_light(lights[light_indices[j]], + f->light_source); if (!f->light_source) applyFacesShading(vertices[j].Color, vertices[j].Normal); } @@ -289,17 +289,16 @@ u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos) // Calculates vertex color to be used in mapblock mesh // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so) // tile_color - node's tile color -video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos, - video::SColor tile_color) +video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos) { u16 light = blendLight(vertex_pos); - return encode_light_and_color(light, tile_color, f->light_source); + return encode_light(light, f->light_source); } -video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos, - const v3f &vertex_normal, video::SColor tile_color) +video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos, + const v3f &vertex_normal) { - video::SColor color = blendLight(vertex_pos, tile_color); + video::SColor color = blendLight(vertex_pos); if (!f->light_source) applyFacesShading(color, vertex_normal); return color; @@ -367,8 +366,13 @@ 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); + for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) { + TileLayer *layer = ©.layers[layernum]; + if (layer->texture_id == 0) + continue; + if (!layer->has_color) + n.getColor(f, &(layer->color)); + } return copy; } @@ -395,8 +399,8 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing) light = getInteriorLight(ntop, 0, nodedef); } - color_liquid_top = encode_light_and_color(light, tile_liquid_top.color, f->light_source); - color = encode_light_and_color(light, tile_liquid.color, f->light_source); + color_liquid_top = encode_light(light, f->light_source); + color = encode_light(light, f->light_source); } void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing) @@ -547,7 +551,7 @@ void MapblockMeshGenerator::drawLiquidSides(bool flowing) else pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS; if (data->m_smooth_lighting) - color = blendLight(pos, tile_liquid.color); + color = blendLightColor(pos); pos += origin; vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v); }; @@ -574,7 +578,7 @@ void MapblockMeshGenerator::drawLiquidTop(bool flowing) int w = corner_resolve[i][1]; vertices[i].Pos.Y += corner_levels[w][u]; if (data->m_smooth_lighting) - vertices[i].Color = blendLight(vertices[i].Pos, tile_liquid_top.color); + vertices[i].Color = blendLightColor(vertices[i].Pos); vertices[i].Pos += origin; } @@ -659,7 +663,9 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() tiles[face] = getTile(g_6dirs[face]); TileSpec glass_tiles[6]; - if (tiles[0].texture && tiles[3].texture && tiles[4].texture) { + if (tiles[1].layers[0].texture && + tiles[2].layers[0].texture && + tiles[3].layers[0].texture) { glass_tiles[0] = tiles[4]; glass_tiles[1] = tiles[0]; glass_tiles[2] = tiles[4]; @@ -763,7 +769,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() // Optionally render internal liquid level defined by param2 // Liquid is textured with 1 tile defined in nodedef 'special_tiles' if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL && - f->special_tiles[0].texture) { + f->special_tiles[0].layers[0].texture) { // Internal liquid level has param2 range 0 .. 63, // convert it to -0.5 .. 0.5 float vlev = (param2 / 63.0) * 2.0 - 1.0; @@ -998,7 +1004,8 @@ void MapblockMeshGenerator::drawFencelikeNode() { useDefaultTile(false); TileSpec tile_nocrack = tile; - tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK; + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) + tile_nocrack.layers[layer].material_flags &= ~MATERIAL_FLAG_CRACK; // Put wood the right way around in the posts TileSpec tile_rot = tile; @@ -1253,7 +1260,7 @@ void MapblockMeshGenerator::drawMeshNode() // vertex right here. for (int k = 0; k < vertex_count; k++) { video::S3DVertex &vertex = vertices[k]; - vertex.Color = blendLight(vertex.Pos, vertex.Normal, tile.color); + vertex.Color = blendLightColor(vertex.Pos, vertex.Normal); vertex.Pos += origin; } collector->append(tile, vertices, vertex_count, diff --git a/src/content_mapblock.h b/src/content_mapblock.h index c8425024f..6866a4498 100644 --- a/src/content_mapblock.h +++ b/src/content_mapblock.h @@ -60,8 +60,8 @@ public: // lighting void getSmoothLightFrame(); u16 blendLight(const v3f &vertex_pos); - video::SColor blendLight(const v3f &vertex_pos, video::SColor tile_color); - video::SColor blendLight(const v3f &vertex_pos, const v3f &vertex_normal, video::SColor tile_color); + video::SColor blendLightColor(const v3f &vertex_pos); + video::SColor blendLightColor(const v3f &vertex_pos, const v3f &vertex_normal); void useTile(int index, bool disable_backface_culling); void useDefaultTile(bool set_color = true); diff --git a/src/hud.cpp b/src/hud.cpp index f558acf1e..c482912e9 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -687,8 +687,9 @@ void drawItemStack(video::IVideoDriver *driver, 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; + ItemPartColor *p = &imesh->buffer_colors[j]; + if (p->override_base) + c = p->color; } colorizeMeshBuffer(buf, &c); video::SMaterial &material = buf->getMaterial(); diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 933dfc32a..0bba644e6 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -323,7 +323,7 @@ void final_color_blend(video::SColor *result, video::SColorf dayLight; get_sunlight_color(&dayLight, daynight_ratio); final_color_blend(result, - encode_light_and_color(light, video::SColor(0xFFFFFFFF), 0), dayLight); + encode_light(light, 0), dayLight); } void final_color_blend(video::SColor *result, @@ -422,12 +422,19 @@ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs) struct FastFace { - TileSpec tile; + TileLayer layer; video::S3DVertex vertices[4]; // Precalculated vertices + /*! + * The face is divided into two triangles. If this is true, + * vertices 0 and 2 are connected, othervise vertices 1 and 3 + * are connected. + */ + bool vertex_0_2_connected; + u8 layernum; }; static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, - v3f p, v3s16 dir, v3f scale, std::vector &dest) + v3f p, v3s16 dir, v3f scale, std::vector &dest) { // Position is at the center of the cube. v3f pos = p * BS; @@ -577,27 +584,50 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, v3f normal(dir.X, dir.Y, dir.Z); - dest.push_back(FastFace()); + u16 li[4] = { li0, li1, li2, li3 }; + u16 day[4]; + u16 night[4]; - FastFace& face = *dest.rbegin(); + for (u8 i = 0; i < 4; i++) { + day[i] = li[i] >> 8; + night[i] = li[i] & 0xFF; + } + + bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2]) + < abs(day[1] - day[3]) + abs(night[1] - night[3]); - 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); + for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) { + TileLayer *layer = &tile.layers[layernum]; + if (layer->texture_id == 0) + continue; - face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]); - } + dest.push_back(FastFace()); + FastFace& face = *dest.rbegin(); + + for (u8 i = 0; i < 4; i++) { + video::SColor c = encode_light(li[i], tile.emissive_light); + if (!tile.emissive_light) + applyFacesShading(c, normal); - face.tile = tile; + face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]); + } + + /* + Revert triangles for nicer looking gradient if the + brightness of vertices 1 and 3 differ less than + the brightness of vertices 0 and 2. + */ + face.vertex_0_2_connected = vertex_0_2_connected; + + face.layer = *layer; + face.layernum = layernum; + } } /* @@ -663,13 +693,20 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) { INodeDefManager *ndef = data->m_client->ndef(); const ContentFeatures &f = ndef->get(mn); - TileSpec spec = f.tiles[tileindex]; - if (!spec.has_color) - mn.getColor(f, &spec.color); + TileSpec tile = f.tiles[tileindex]; + TileLayer *top_layer = NULL; + for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) { + TileLayer *layer = &tile.layers[layernum]; + if (layer->texture_id == 0) + continue; + top_layer = layer; + if (!layer->has_color) + mn.getColor(f, &(layer->color)); + } // Apply temporary crack if (p == data->m_crack_pos_relative) - spec.material_flags |= MATERIAL_FLAG_CRACK; - return spec; + top_layer->material_flags |= MATERIAL_FLAG_CRACK; + return tile; } /* @@ -732,10 +769,9 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) }; u16 tile_index=facedir*16 + dir_i; - TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data); - spec.rotation=dir_to_tile[tile_index + 1]; - spec.texture = data->m_client->tsrc()->getTexture(spec.texture_id); - return spec; + TileSpec tile = getNodeTileN(mn, p, dir_to_tile[tile_index], data); + tile.rotation = dir_to_tile[tile_index + 1]; + return tile; } static void getTileInfo( @@ -800,7 +836,9 @@ static void getTileInfo( // eg. water and glass if (equivalent) - tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; + for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) + tile.layers[layernum].material_flags |= + MATERIAL_FLAG_BACKFACE_CULLING; if (data->m_smooth_lighting == false) { @@ -880,12 +918,7 @@ static void updateFastFaceRow( && next_lights[1] == lights[1] && next_lights[2] == lights[2] && next_lights[3] == lights[3] - && next_tile == tile - && tile.rotation == 0 - && (tile.material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL) - && (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL) - && tile.color == next_tile.color - && tile.emissive_light == next_tile.emissive_light) { + && next_tile.isTileable(tile)) { next_is_different = false; continuous_tiles_count++; } @@ -988,7 +1021,6 @@ static void updateAllFastFaceRows(MeshMakeData *data, */ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): - m_mesh(new scene::SMesh()), m_minimap_mapblock(NULL), m_client(data->m_client), m_driver(m_client->tsrc()->getDevice()->getVideoDriver()), @@ -1000,6 +1032,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): m_last_daynight_ratio((u32) -1), m_daynight_diffs() { + for (int m = 0; m < MAX_TILE_LAYERS; m++) + m_mesh[m] = new scene::SMesh(); m_enable_shaders = data->m_use_shaders; m_use_tangent_vertices = data->m_use_tangent_vertices; m_enable_vbo = g_settings->getBool("enable_vbo"); @@ -1048,23 +1082,14 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): const u16 indices[] = {0,1,2,2,3,0}; const u16 indices_alternate[] = {0,1,3,2,3,1}; - if(f.tile.texture == NULL) + if (f.layer.texture == NULL) continue; - const u16 *indices_p = indices; + const u16 *indices_p = + f.vertex_0_2_connected ? indices : indices_alternate; - /* - 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 (fabs(f.vertices[0].Color.getLuminance() - - f.vertices[2].Color.getLuminance()) - > fabs(f.vertices[1].Color.getLuminance() - - f.vertices[3].Color.getLuminance())) - indices_p = indices_alternate; - - collector.append(f.tile, f.vertices, 4, indices_p, 6); + collector.append(f.layer, f.vertices, 4, indices_p, 6, + f.layernum); } } @@ -1081,146 +1106,151 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): generator.generate(); } + collector.applyTileColors(); + /* Convert MeshCollector to SMesh */ - for(u32 i = 0; i < collector.prebuffers.size(); i++) - { - PreMeshBuffer &p = collector.prebuffers[i]; - - // Generate animation data - // - Cracks - if(p.tile.material_flags & MATERIAL_FLAG_CRACK) + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + for(u32 i = 0; i < collector.prebuffers[layer].size(); i++) { - // Find the texture name plus ^[crack:N: - std::ostringstream os(std::ios::binary); - os<getTextureName(p.tile.texture_id)<<"^[crack"; - if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) - os<<"o"; // use ^[cracko - os<<":"<<(u32)p.tile.animation_frame_count<<":"; - m_crack_materials.insert(std::make_pair(i, os.str())); - // Replace tile texture with the cracked one - p.tile.texture = m_tsrc->getTextureForMesh( - os.str()+"0", - &p.tile.texture_id); - } - // - Texture animation - if (p.tile.material_flags & MATERIAL_FLAG_ANIMATION) { - // Add to MapBlockMesh in order to animate these tiles - m_animation_tiles[i] = p.tile; - m_animation_frames[i] = 0; - if(g_settings->getBool("desynchronize_mapblock_texture_animation")){ - // Get starting position from noise - m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d( - data->m_blockpos.X, data->m_blockpos.Y, - data->m_blockpos.Z, 0)); - } else { - // Play all synchronized - m_animation_frame_offsets[i] = 0; - } - // Replace tile texture with the first animation frame - FrameSpec animation_frame = p.tile.frames[0]; - p.tile.texture = animation_frame.texture; - } + PreMeshBuffer &p = collector.prebuffers[layer][i]; - 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; + // Generate animation data + // - Cracks + if(p.layer.material_flags & MATERIAL_FLAG_CRACK) + { + // Find the texture name plus ^[crack:N: + std::ostringstream os(std::ios::binary); + os<getTextureName(p.layer.texture_id)<<"^[crack"; + if(p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) + os<<"o"; // use ^[cracko + os<<":"<<(u32)p.layer.animation_frame_count<<":"; + m_crack_materials.insert(std::make_pair(std::pair(layer, i), os.str())); + // Replace tile texture with the cracked one + p.layer.texture = m_tsrc->getTextureForMesh( + os.str()+"0", + &p.layer.texture_id); + } + // - Texture animation + if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) { + // Add to MapBlockMesh in order to animate these tiles + m_animation_tiles[std::pair(layer, i)] = p.layer; + m_animation_frames[std::pair(layer, i)] = 0; + if(g_settings->getBool("desynchronize_mapblock_texture_animation")){ + // Get starting position from noise + m_animation_frame_offsets[std::pair(layer, i)] = 100000 * (2.0 + noise3d( + data->m_blockpos.X, data->m_blockpos.Y, + data->m_blockpos.Z, 0)); } else { - vc = &p.vertices[j].Color; + // Play all synchronized + m_animation_frame_offsets[std::pair(layer, i)] = 0; } - 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); + // Replace tile texture with the first animation frame + FrameSpec animation_frame = p.layer.frames[0]; + p.layer.texture = animation_frame.texture; } - } - // Create material - video::SMaterial material; - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, true); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - material.setFlag(video::EMF_FOG_ENABLE, true); - material.setTexture(0, p.tile.texture); + 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[std::pair(layer, i)][j] = copy; + + // The sunlight ratio has been stored, + // delete alpha (for the final rendering). + vc->setAlpha(255); + } + } - if (m_enable_shaders) { - material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material; - p.tile.applyMaterialOptionsWithShaders(material); - if (p.tile.normal_texture) { - material.setTexture(1, p.tile.normal_texture); + // Create material + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + material.setTexture(0, p.layer.texture); + + if (m_enable_shaders) { + material.MaterialType = m_shdrsrc->getShaderInfo(p.layer.shader_id).material; + p.layer.applyMaterialOptionsWithShaders(material); + if (p.layer.normal_texture) { + material.setTexture(1, p.layer.normal_texture); + } + material.setTexture(2, p.layer.flags_texture); + } else { + p.layer.applyMaterialOptions(material); + } + + scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer]; + + // Create meshbuffer, add to mesh + if (m_use_tangent_vertices) { + scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents(); + // Set material + buf->Material = material; + // Add to mesh + mesh->addMeshBuffer(buf); + // Mesh grabbed it + buf->drop(); + buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(), + &p.indices[0], p.indices.size()); + } else { + scene::SMeshBuffer *buf = new scene::SMeshBuffer(); + // Set material + buf->Material = material; + // Add to mesh + mesh->addMeshBuffer(buf); + // Mesh grabbed it + buf->drop(); + buf->append(&p.vertices[0], p.vertices.size(), + &p.indices[0], p.indices.size()); } - material.setTexture(2, p.tile.flags_texture); - } else { - p.tile.applyMaterialOptions(material); } - scene::SMesh *mesh = (scene::SMesh *)m_mesh; - // Create meshbuffer, add to mesh + /* + Do some stuff to the mesh + */ + m_camera_offset = camera_offset; + translateMesh(m_mesh[layer], + intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS)); + if (m_use_tangent_vertices) { - scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents(); - // Set material - buf->Material = material; - // Add to mesh - mesh->addMeshBuffer(buf); - // Mesh grabbed it - buf->drop(); - buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(), - &p.indices[0], p.indices.size()); - } else { - scene::SMeshBuffer *buf = new scene::SMeshBuffer(); - // Set material - buf->Material = material; - // Add to mesh - mesh->addMeshBuffer(buf); - // Mesh grabbed it - buf->drop(); - buf->append(&p.vertices[0], p.vertices.size(), - &p.indices[0], p.indices.size()); + scene::IMeshManipulator* meshmanip = + m_client->getSceneManager()->getMeshManipulator(); + meshmanip->recalculateTangents(m_mesh[layer], true, false, false); } - } - - /* - Do some stuff to the mesh - */ - m_camera_offset = camera_offset; - translateMesh(m_mesh, - intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS)); - if (m_use_tangent_vertices) { - scene::IMeshManipulator* meshmanip = - m_client->getSceneManager()->getMeshManipulator(); - meshmanip->recalculateTangents(m_mesh, true, false, false); - } - - if (m_mesh) - { + if (m_mesh[layer]) + { #if 0 - // Usually 1-700 faces and 1-7 materials - std::cout<<"Updated MapBlock has "<getMeshBufferCount() - <<" materials (meshbuffers)"<getMeshBufferCount() + <<" materials (meshbuffers)"<setHardwareMappingHint(scene::EHM_STATIC); + // Use VBO for mesh (this just would set this for ever buffer) + if (m_enable_vbo) { + m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC); + } } } @@ -1235,14 +1265,15 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): MapBlockMesh::~MapBlockMesh() { - if (m_enable_vbo && m_mesh) { - for (u32 i = 0; i < m_mesh->getMeshBufferCount(); i++) { - scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i); - m_driver->removeHardwareBuffer(buf); - } + for (int m = 0; m < MAX_TILE_LAYERS; m++) { + if (m_enable_vbo && m_mesh[m]) + for (u32 i = 0; i < m_mesh[m]->getMeshBufferCount(); i++) { + scene::IMeshBuffer *buf = m_mesh[m]->getMeshBuffer(i); + m_driver->removeHardwareBuffer(buf); + } + m_mesh[m]->drop(); + m_mesh[m] = NULL; } - m_mesh->drop(); - m_mesh = NULL; delete m_minimap_mapblock; } @@ -1259,9 +1290,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat // Cracks if(crack != m_last_crack) { - for (UNORDERED_MAP::iterator i = m_crack_materials.begin(); - i != m_crack_materials.end(); ++i) { - scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); + for (std::map, std::string>::iterator i = + m_crack_materials.begin(); i != m_crack_materials.end(); ++i) { + scene::IMeshBuffer *buf = m_mesh[i->first.first]-> + getMeshBuffer(i->first.second); std::string basename = i->second; // Create new texture name from original @@ -1274,10 +1306,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat // If the current material is also animated, // update animation info - UNORDERED_MAP::iterator anim_iter = - m_animation_tiles.find(i->first); + std::map, TileLayer>::iterator anim_iter = + m_animation_tiles.find(i->first); if (anim_iter != m_animation_tiles.end()){ - TileSpec &tile = anim_iter->second; + TileLayer &tile = anim_iter->second; tile.texture = new_texture; tile.texture_id = new_texture_id; // force animation update @@ -1289,9 +1321,9 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat } // Texture animation - for (UNORDERED_MAP::iterator i = m_animation_tiles.begin(); - i != m_animation_tiles.end(); ++i) { - const TileSpec &tile = i->second; + for (std::map, TileLayer>::iterator i = + m_animation_tiles.begin(); i != m_animation_tiles.end(); ++i) { + const TileLayer &tile = i->second; // Figure out current frame int frameoffset = m_animation_frame_offsets[i->first]; int frame = (int)(time * 1000 / tile.animation_frame_length_ms @@ -1302,7 +1334,8 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat m_animation_frames[i->first] = frame; - scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); + scene::IMeshBuffer *buf = m_mesh[i->first.first]-> + getMeshBuffer(i->first.second); FrameSpec animation_frame = tile.frames[frame]; buf->getMaterial().setTexture(0, animation_frame.texture); @@ -1318,22 +1351,24 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) { // Force reload mesh to VBO - if (m_enable_vbo) { - m_mesh->setDirty(); - } + if (m_enable_vbo) + for (int m = 0; m < MAX_TILE_LAYERS; m++) + m_mesh[m]->setDirty(); video::SColorf day_color; get_sunlight_color(&day_color, daynight_ratio); - for(std::map >::iterator + for(std::map, std::map >::iterator i = m_daynight_diffs.begin(); i != m_daynight_diffs.end(); ++i) { - scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); + scene::IMeshBuffer *buf = m_mesh[i->first.first]-> + getMeshBuffer(i->first.second); video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices(); for(std::map::iterator j = i->second.begin(); j != i->second.end(); ++j) { - final_color_blend(&(vertices[j->first].Color), j->second, day_color); + final_color_blend(&(vertices[j->first].Color), + j->second, day_color); } } m_last_daynight_ratio = daynight_ratio; @@ -1345,9 +1380,12 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat void MapBlockMesh::updateCameraOffset(v3s16 camera_offset) { if (camera_offset != m_camera_offset) { - translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS)); - if (m_enable_vbo) { - m_mesh->setDirty(); + for (u8 layer = 0; layer < 2; layer++) { + translateMesh(m_mesh[layer], + intToFloat(m_camera_offset - camera_offset, BS)); + if (m_enable_vbo) { + m_mesh[layer]->setDirty(); + } } m_camera_offset = camera_offset; } @@ -1360,16 +1398,30 @@ void MapBlockMesh::updateCameraOffset(v3s16 camera_offset) void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices) +{ + for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) { + const TileLayer *layer = &tile.layers[layernum]; + if (layer->texture_id == 0) + continue; + append(*layer, vertices, numVertices, indices, numIndices, + layernum); + } +} + +void MeshCollector::append(const TileLayer &layer, + const video::S3DVertex *vertices, u32 numVertices, + const u16 *indices, u32 numIndices, u8 layernum) { if (numIndices > 65535) { dstream<<"FIXME: MeshCollector::append() called with numIndices="< *buffers = &prebuffers[layernum]; PreMeshBuffer *p = NULL; - for (u32 i = 0; i < prebuffers.size(); i++) { - PreMeshBuffer &pp = prebuffers[i]; - if (pp.tile != tile) + for (u32 i = 0; i < buffers->size(); i++) { + PreMeshBuffer &pp = (*buffers)[i]; + if (pp.layer != layer) continue; if (pp.indices.size() + numIndices > 65535) continue; @@ -1380,9 +1432,9 @@ void MeshCollector::append(const TileSpec &tile, if (p == NULL) { PreMeshBuffer pp; - pp.tile = tile; - prebuffers.push_back(pp); - p = &prebuffers[prebuffers.size() - 1]; + pp.layer = layer; + buffers->push_back(pp); + p = &(*buffers)[buffers->size() - 1]; } u32 vertex_count; @@ -1416,16 +1468,31 @@ void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, v3f pos, video::SColor c, u8 light_source) +{ + for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) { + const TileLayer *layer = &tile.layers[layernum]; + if (layer->texture_id == 0) + continue; + append(*layer, vertices, numVertices, indices, numIndices, pos, + c, light_source, layernum); + } +} + +void MeshCollector::append(const TileLayer &layer, + const video::S3DVertex *vertices, u32 numVertices, + const u16 *indices, u32 numIndices, + v3f pos, video::SColor c, u8 light_source, u8 layernum) { if (numIndices > 65535) { dstream<<"FIXME: MeshCollector::append() called with numIndices="< *buffers = &prebuffers[layernum]; PreMeshBuffer *p = NULL; - for (u32 i = 0; i < prebuffers.size(); i++) { - PreMeshBuffer &pp = prebuffers[i]; - if(pp.tile != tile) + for (u32 i = 0; i < buffers->size(); i++) { + PreMeshBuffer &pp = (*buffers)[i]; + if(pp.layer != layer) continue; if(pp.indices.size() + numIndices > 65535) continue; @@ -1436,9 +1503,9 @@ void MeshCollector::append(const TileSpec &tile, if (p == NULL) { PreMeshBuffer pp; - pp.tile = tile; - prebuffers.push_back(pp); - p = &prebuffers[prebuffers.size() - 1]; + pp.layer = layer; + buffers->push_back(pp); + p = &(*buffers)[buffers->size() - 1]; } video::SColor original_c = c; @@ -1473,14 +1540,49 @@ void MeshCollector::append(const TileSpec &tile, } } -video::SColor encode_light_and_color(u16 light, const video::SColor &color, - u8 emissive_light) +void MeshCollector::applyTileColors() +{ + if (m_use_tangent_vertices) + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + std::vector *p = &prebuffers[layer]; + for (std::vector::iterator it = p->begin(); + it != p->end(); ++it) { + video::SColor tc = it->layer.color; + if (tc == video::SColor(0xFFFFFFFF)) + continue; + for (u32 index = 0; index < it->tangent_vertices.size(); index++) { + video::SColor *c = &it->tangent_vertices[index].Color; + c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255, + c->getGreen() * tc.getGreen() / 255, + c->getBlue() * tc.getBlue() / 255); + } + } + } + else + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + std::vector *p = &prebuffers[layer]; + for (std::vector::iterator it = p->begin(); + it != p->end(); ++it) { + video::SColor tc = it->layer.color; + if (tc == video::SColor(0xFFFFFFFF)) + continue; + for (u32 index = 0; index < it->vertices.size(); index++) { + video::SColor *c = &it->vertices[index].Color; + c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255, + c->getGreen() * tc.getGreen() / 255, + c->getBlue() * tc.getBlue() / 255); + } + } + } +} + +video::SColor encode_light(u16 light, u8 emissive_light) { // Get components - f32 day = (light & 0xff) / 255.0f; - f32 night = (light >> 8) / 255.0f; + u32 day = (light & 0xff); + u32 night = (light >> 8); // Add emissive light - night += emissive_light * 0.01f; + night += emissive_light * 2.5f; if (night > 255) night = 255; // Since we don't know if the day light is sunlight or @@ -1490,15 +1592,14 @@ video::SColor encode_light_and_color(u16 light, const video::SColor &color, day = 0; else day = day - night; - f32 sum = day + night; + u32 sum = day + night; // Ratio of sunlight: - float r; + u32 r; if (sum > 0) - r = day / sum; + r = day * 255 / 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()); + return video::SColor(r, b, b, b); } diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 25c699e1c..f32df3958 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -108,7 +108,12 @@ public: scene::IMesh *getMesh() { - return m_mesh; + return m_mesh[0]; + } + + scene::IMesh *getMesh(u8 layer) + { + return m_mesh[layer]; } MinimapMapblock *moveMinimapMapblock() @@ -132,7 +137,7 @@ public: void updateCameraOffset(v3s16 camera_offset); private: - scene::IMesh *m_mesh; + scene::IMesh *m_mesh[MAX_TILE_LAYERS]; MinimapMapblock *m_minimap_mapblock; Client *m_client; video::IVideoDriver *m_driver; @@ -150,20 +155,23 @@ private: // Animation info: cracks // Last crack value passed to animate() int m_last_crack; - // Maps mesh buffer (i.e. material) indices to base texture names - UNORDERED_MAP m_crack_materials; + // Maps mesh and mesh buffer (i.e. material) indices to base texture names + std::map, std::string> m_crack_materials; // Animation info: texture animationi - // Maps meshbuffers to TileSpecs - UNORDERED_MAP m_animation_tiles; - UNORDERED_MAP m_animation_frames; // last animation frame - UNORDERED_MAP m_animation_frame_offsets; + // Maps mesh and mesh buffer indices to TileSpecs + // Keys are pairs of (mesh index, buffer index in the mesh) + std::map, TileLayer> m_animation_tiles; + std::map, int> m_animation_frames; // last animation frame + std::map, int> m_animation_frame_offsets; // Animation info: day/night transitions // Last daynight_ratio value passed to animate() u32 m_last_daynight_ratio; - // For each meshbuffer, stores pre-baked colors of sunlit vertices - std::map > m_daynight_diffs; + // For each mesh and mesh buffer, stores pre-baked colors + // of sunlit vertices + // Keys are pairs of (mesh index, buffer index in the mesh) + std::map, std::map > m_daynight_diffs; // Camera offset info -> do we have to translate the mesh? v3s16 m_camera_offset; @@ -176,7 +184,7 @@ private: */ struct PreMeshBuffer { - TileSpec tile; + TileLayer layer; std::vector indices; std::vector vertices; std::vector tangent_vertices; @@ -184,7 +192,7 @@ struct PreMeshBuffer struct MeshCollector { - std::vector prebuffers; + std::vector prebuffers[MAX_TILE_LAYERS]; bool m_use_tangent_vertices; MeshCollector(bool use_tangent_vertices): @@ -193,27 +201,38 @@ struct MeshCollector } void append(const TileSpec &material, + const video::S3DVertex *vertices, u32 numVertices, + const u16 *indices, u32 numIndices); + void append(const TileLayer &material, const video::S3DVertex *vertices, u32 numVertices, - const u16 *indices, u32 numIndices); + const u16 *indices, u32 numIndices, u8 layernum); void append(const TileSpec &material, + const video::S3DVertex *vertices, u32 numVertices, + const u16 *indices, u32 numIndices, v3f pos, + video::SColor c, u8 light_source); + void append(const TileLayer &material, const video::S3DVertex *vertices, u32 numVertices, - const u16 *indices, u32 numIndices, - v3f pos, video::SColor c, u8 light_source); + const u16 *indices, u32 numIndices, v3f pos, + video::SColor c, u8 light_source, u8 layernum); + /*! + * Colorizes all vertices in the collector. + */ + void applyTileColors(); }; /*! - * Encodes light and color of a node. + * Encodes light of a node. * The result is not the final color, but a * half-baked vertex color. + * You have to multiply the resulting color + * with the node's 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); +video::SColor encode_light(u16 light, u8 emissive_light); // Compute light at node u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef); diff --git a/src/mesh.cpp b/src/mesh.cpp index a79264ef0..d776f6185 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -385,48 +385,50 @@ void recalculateBoundingBox(scene::IMesh *src_mesh) src_mesh->setBoundingBox(bbox); } -scene::IMesh* cloneMesh(scene::IMesh *src_mesh) +scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer) +{ + scene::IMeshBuffer *clone = NULL; + switch (mesh_buffer->getVertexType()) { + case video::EVT_STANDARD: { + video::S3DVertex *v = (video::S3DVertex *) mesh_buffer->getVertices(); + u16 *indices = (u16*) mesh_buffer->getIndices(); + scene::SMeshBuffer *temp_buf = new scene::SMeshBuffer(); + temp_buf->append(v, mesh_buffer->getVertexCount(), indices, + mesh_buffer->getIndexCount()); + return temp_buf; + break; + } + case video::EVT_2TCOORDS: { + video::S3DVertex2TCoords *v = + (video::S3DVertex2TCoords *) mesh_buffer->getVertices(); + u16 *indices = (u16*) mesh_buffer->getIndices(); + scene::SMeshBufferTangents *temp_buf = new scene::SMeshBufferTangents(); + temp_buf->append(v, mesh_buffer->getVertexCount(), indices, + mesh_buffer->getIndexCount()); + break; + } + case video::EVT_TANGENTS: { + video::S3DVertexTangents *v = + (video::S3DVertexTangents *) mesh_buffer->getVertices(); + u16 *indices = (u16*) mesh_buffer->getIndices(); + scene::SMeshBufferTangents *temp_buf = new scene::SMeshBufferTangents(); + temp_buf->append(v, mesh_buffer->getVertexCount(), indices, + mesh_buffer->getIndexCount()); + break; + } + } + return clone; +} + +scene::SMesh* cloneMesh(scene::IMesh *src_mesh) { scene::SMesh* dst_mesh = new scene::SMesh(); for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) { - scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j); - switch (buf->getVertexType()) { - case video::EVT_STANDARD: { - video::S3DVertex *v = - (video::S3DVertex *) buf->getVertices(); - u16 *indices = (u16*)buf->getIndices(); - scene::SMeshBuffer *temp_buf = new scene::SMeshBuffer(); - temp_buf->append(v, buf->getVertexCount(), - indices, buf->getIndexCount()); - dst_mesh->addMeshBuffer(temp_buf); - temp_buf->drop(); - break; - } - case video::EVT_2TCOORDS: { - video::S3DVertex2TCoords *v = - (video::S3DVertex2TCoords *) buf->getVertices(); - u16 *indices = (u16*)buf->getIndices(); - scene::SMeshBufferTangents *temp_buf = - new scene::SMeshBufferTangents(); - temp_buf->append(v, buf->getVertexCount(), - indices, buf->getIndexCount()); - dst_mesh->addMeshBuffer(temp_buf); - temp_buf->drop(); - break; - } - case video::EVT_TANGENTS: { - video::S3DVertexTangents *v = - (video::S3DVertexTangents *) buf->getVertices(); - u16 *indices = (u16*)buf->getIndices(); - scene::SMeshBufferTangents *temp_buf = - new scene::SMeshBufferTangents(); - temp_buf->append(v, buf->getVertexCount(), - indices, buf->getIndexCount()); - dst_mesh->addMeshBuffer(temp_buf); - temp_buf->drop(); - break; - } - } + scene::IMeshBuffer *temp_buf = cloneMeshBuffer( + src_mesh->getMeshBuffer(j)); + dst_mesh->addMeshBuffer(temp_buf); + temp_buf->drop(); + } return dst_mesh; } diff --git a/src/mesh.h b/src/mesh.h index bcf0d771c..0e946caab 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -82,11 +82,16 @@ void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir); void rotateMeshXYby (scene::IMesh *mesh, f64 degrees); void rotateMeshXZby (scene::IMesh *mesh, f64 degrees); void rotateMeshYZby (scene::IMesh *mesh, f64 degrees); + +/* + * Clone the mesh buffer. + */ +scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer); /* Clone the mesh. */ -scene::IMesh* cloneMesh(scene::IMesh *src_mesh); +scene::SMesh* cloneMesh(scene::IMesh *src_mesh); /* Convert nodeboxes to mesh. Each tile goes into a different buffer. diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index ea532d9e0..b586fa70b 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -148,9 +148,11 @@ with this program; if not, write to the Free Software Foundation, Inc., Add node and tile color and palette Fix plantlike visual_scale being applied squared and add compatibility with pre-30 clients by sending sqrt(visual_scale) + PROTOCOL VERSION 31: + Add tile overlay */ -#define LATEST_PROTOCOL_VERSION 30 +#define LATEST_PROTOCOL_VERSION 31 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 24 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index ce3e378a0..db28325aa 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -378,13 +378,13 @@ void ContentFeatures::reset() void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const { - if (protocol_version < 30) { + if (protocol_version < 31) { serializeOld(os, protocol_version); return; } // version - writeU8(os, 9); + writeU8(os, 10); // general os << serializeString(name); @@ -404,6 +404,8 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); + for (u32 i = 0; i < 6; i++) + tiledef_overlay[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); @@ -492,7 +494,7 @@ void ContentFeatures::deSerialize(std::istream &is) if (version < 9) { deSerializeOld(is, version); return; - } else if (version > 9) { + } else if (version > 10) { throw SerializationError("unsupported ContentFeatures version"); } @@ -516,6 +518,9 @@ void ContentFeatures::deSerialize(std::istream &is) throw SerializationError("unsupported tile count"); for (u32 i = 0; i < 6; i++) tiledef[i].deSerialize(is, version, drawtype); + if (version >= 10) + for (u32 i = 0; i < 6; i++) + tiledef_overlay[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++) @@ -581,7 +586,7 @@ void ContentFeatures::deSerialize(std::istream &is) } #ifndef SERVER -void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, +void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile, TileDef *tiledef, u32 shader_id, bool use_normal_texture, bool backface_culling, u8 material_type) { @@ -774,14 +779,18 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc // Tiles (fill in f->tiles[]) for (u16 j = 0; j < 6; j++) { - fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j], + fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader[j], tsettings.use_normal_texture, tiledef[j].backface_culling, material_type); + if (tiledef_overlay[j].name!="") + fillTileAttribs(tsrc, &tiles[j].layers[1], &tiledef_overlay[j], + tile_shader[j], tsettings.use_normal_texture, + 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], + fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tiledef_special[j], tile_shader[j], tsettings.use_normal_texture, tiledef_special[j].backface_culling, material_type); } @@ -1538,8 +1547,19 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const if (protocol_version < 30 && drawtype == NDT_PLANTLIKE) compatible_visual_scale = sqrt(visual_scale); + TileDef compatible_tiles[6]; + for (u8 i = 0; i < 6; i++) { + compatible_tiles[i] = tiledef[i]; + if (tiledef_overlay[i].name != "") { + std::stringstream s; + s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name + << ")"; + compatible_tiles[i].name = s.str(); + } + } + // Protocol >= 24 - if (protocol_version < 30) { + if (protocol_version < 31) { writeU8(os, protocol_version < 27 ? 7 : 8); os << serializeString(name); @@ -1553,7 +1573,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) - tiledef[i].serialize(os, protocol_version); + compatible_tiles[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); diff --git a/src/nodedef.h b/src/nodedef.h index 83968ce27..4d3bacc6c 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -280,6 +280,8 @@ struct ContentFeatures #endif float visual_scale; // Misc. scale parameter TileDef tiledef[6]; + // These will be drawn over the base tiles. + TileDef tiledef_overlay[6]; TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid // If 255, the node is opaque. // Otherwise it uses texture alpha. @@ -405,7 +407,7 @@ struct ContentFeatures } #ifndef SERVER - void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, + void fillTileAttribs(ITextureSource *tsrc, TileLayer *tile, TileDef *tiledef, u32 shader_id, bool use_normal_texture, bool backface_culling, u8 material_type); void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, diff --git a/src/particles.cpp b/src/particles.cpp index e1f292fb6..7f406d874 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -620,7 +620,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, { // Texture u8 texid = myrand_range(0, 5); - const TileSpec &tile = f.tiles[texid]; + const TileLayer &tile = f.tiles[texid].layers[0]; video::ITexture *texture; struct TileAnimationParams anim; anim.type = TAT_NONE; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 8dfb851e6..573347b4c 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -426,6 +426,34 @@ ContentFeatures read_content_features(lua_State *L, int index) } lua_pop(L, 1); + // overlay_tiles = {} + lua_getfield(L, index, "overlay_tiles"); + if (lua_istable(L, -1)) { + int table = lua_gettop(L); + lua_pushnil(L); + int i = 0; + while (lua_next(L, table) != 0) { + // Read tiledef from value + f.tiledef_overlay[i] = read_tiledef(L, -1, f.drawtype); + // removes value, keeps key for next iteration + lua_pop(L, 1); + i++; + if (i == 6) { + lua_pop(L, 1); + break; + } + } + // Copy last value to all remaining textures + if (i >= 1) { + TileDef lasttile = f.tiledef_overlay[i - 1]; + while (i < 6) { + f.tiledef_overlay[i] = lasttile; + i++; + } + } + } + lua_pop(L, 1); + // special_tiles = {} lua_getfield(L, index, "special_tiles"); // If nil, try the deprecated name "special_materials" instead diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 40af0be5f..2b23d9e02 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -235,27 +235,16 @@ WieldMeshSceneNode::~WieldMeshSceneNode() g_extrusion_mesh_cache = NULL; } -void WieldMeshSceneNode::setCube(const TileSpec tiles[6], +void WieldMeshSceneNode::setCube(const ContentFeatures &f, v3f wield_scale, ITextureSource *tsrc) { scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube(); - changeToMesh(cubemesh); + scene::SMesh *copy = cloneMesh(cubemesh); cubemesh->drop(); - + postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors); + changeToMesh(copy); + copy->drop(); m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR); - - // Customize materials - for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) { - assert(i < 6); - video::SMaterial &material = m_meshnode->getMaterial(i); - if (tiles[i].animation_frame_count == 1) { - material.setTexture(0, tiles[i].texture); - } else { - FrameSpec animation_frame = tiles[i].frames[0]; - material.setTexture(0, animation_frame.texture); - } - tiles[i].applyMaterialOptions(material); - } } void WieldMeshSceneNode::setExtruded(const std::string &imagename, @@ -274,8 +263,10 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, dim = core::dimension2d(dim.Width, frame_height); } scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim); - changeToMesh(mesh); + scene::SMesh *copy = cloneMesh(mesh); mesh->drop(); + changeToMesh(copy); + copy->drop(); m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED); @@ -321,12 +312,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) // Color-related m_colors.clear(); - video::SColor basecolor = idef->getItemstackColor(item, client); + m_base_color = 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); + m_colors.push_back(ItemPartColor()); return; } // Handle nodes @@ -334,66 +325,50 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { // e.g. mesh nodes and nodeboxes - changeToMesh(f.mesh_ptr[0]); - // mesh_ptr[0] is pre-scaled by BS * f->visual_scale + scene::SMesh *mesh = cloneMesh(f.mesh_ptr[0]); + postProcessNodeMesh(mesh, f, m_enable_shaders, true, + &m_material_type, &m_colors); + changeToMesh(mesh); + mesh->drop(); + // mesh is pre-scaled by BS * f->visual_scale m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } else if (f.drawtype == NDT_AIRLIKE) { changeToMesh(NULL); } else if (f.drawtype == NDT_PLANTLIKE) { - setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count); + setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), + def.wield_scale, tsrc, + f.tiles[0].layers[0].animation_frame_count); } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) { - setCube(f.tiles, def.wield_scale, tsrc); + setCube(f, def.wield_scale, tsrc); } else { MeshMakeData mesh_make_data(client, false); MapNode mesh_make_node(id, 255, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); - changeToMesh(mapblock_mesh.getMesh()); - translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS)); + scene::SMesh *mesh = cloneMesh(mapblock_mesh.getMesh()); + translateMesh(mesh, v3f(-BS, -BS, -BS)); + postProcessNodeMesh(mesh, f, m_enable_shaders, true, + &m_material_type, &m_colors); + changeToMesh(mesh); + mesh->drop(); m_meshnode->setScale( def.wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); } u32 material_count = m_meshnode->getMaterialCount(); - if (material_count > 6) { - errorstream << "WieldMeshSceneNode::setItem: Invalid material " - "count " << material_count << ", truncating to 6" << std::endl; - 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 = (tile->animation_frame_count > 1); - if (animated) { - FrameSpec animation_frame = tile->frames[0]; - material.setTexture(0, animation_frame.texture); - } else { - material.setTexture(0, tile->texture); - } - m_colors.push_back(tile->has_color ? tile->color : basecolor); - material.MaterialType = m_material_type; - if (m_enable_shaders) { - if (tile->normal_texture) { - if (animated) { - FrameSpec animation_frame = tile->frames[0]; - material.setTexture(1, animation_frame.normal_texture); - } else { - material.setTexture(1, tile->normal_texture); - } - } - material.setTexture(2, tile->flags_texture); - } } return; } else if (def.inventory_image != "") { setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); - m_colors.push_back(basecolor); + m_colors.push_back(ItemPartColor()); return; } @@ -413,9 +388,9 @@ void WieldMeshSceneNode::setColor(video::SColor c) 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 bc(m_base_color); + if ((m_colors.size() > j) && (m_colors[j].override_base)) + bc = m_colors[j].color; video::SColor buffercolor(255, bc.getRed() * red / 255, bc.getGreen() * green / 255, @@ -439,19 +414,7 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh) m_meshnode->setMesh(dummymesh); dummymesh->drop(); // m_meshnode grabbed it } else { - if (m_lighting) { - m_meshnode->setMesh(mesh); - } else { - /* - Lighting is disabled, this means the caller can (and probably will) - call setColor later. We therefore need to clone the mesh so that - setColor will only modify this scene node's mesh, not others'. - */ - scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator(); - scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh); - m_meshnode->setMesh(new_mesh); - new_mesh->drop(); // m_meshnode grabbed it - } + m_meshnode->setMesh(mesh); } m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting); @@ -475,24 +438,24 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) g_extrusion_mesh_cache->grab(); } - scene::IMesh *mesh; + scene::SMesh *mesh; // If inventory_image is defined, it overrides everything else if (def.inventory_image != "") { mesh = getExtrudedMesh(tsrc, def.inventory_image); - result->mesh = mesh; - result->buffer_colors.push_back( - std::pair(false, video::SColor(0xFFFFFFFF))); + result->buffer_colors.push_back(ItemPartColor()); } 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)); } else if (f.drawtype == NDT_PLANTLIKE) { mesh = getExtrudedMesh(tsrc, - tsrc->getTextureName(f.tiles[0].texture_id)); + tsrc->getTextureName(f.tiles[0].layers[0].texture_id)); } 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()); + scene::IMesh *cube = g_extrusion_mesh_cache->createCube(); + mesh = cloneMesh(cube); + cube->drop(); scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); } else { MeshMakeData mesh_make_data(client, false); @@ -519,32 +482,27 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) u32 mc = mesh->getMeshBufferCount(); 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; 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 (tile->animation_frame_count > 1) { - FrameSpec animation_frame = tile->frames[0]; - material.setTexture(0, animation_frame.texture); - } else { - material.setTexture(0, tile->texture); - } } rotateMeshXZby(mesh, -45); rotateMeshYZby(mesh, -30); - result->mesh = mesh; + + postProcessNodeMesh(mesh, f, false, false, NULL, + &result->buffer_colors); } + result->mesh = mesh; } -scene::IMesh * getExtrudedMesh(ITextureSource *tsrc, + + +scene::SMesh * getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename) { video::ITexture *texture = tsrc->getTextureForMesh(imagename); @@ -553,7 +511,9 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc, } core::dimension2d dim = texture->getSize(); - scene::IMesh *mesh = cloneMesh(g_extrusion_mesh_cache->create(dim)); + scene::IMesh *original = g_extrusion_mesh_cache->create(dim); + scene::SMesh *mesh = cloneMesh(original); + original->drop(); // Customize material video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial(); @@ -569,3 +529,57 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc, return mesh; } + +void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, + bool use_shaders, bool set_material, video::E_MATERIAL_TYPE *mattype, + std::vector *colors) +{ + u32 mc = mesh->getMeshBufferCount(); + // Allocate colors for existing buffers + colors->clear(); + for (u32 i = 0; i < mc; ++i) + colors->push_back(ItemPartColor()); + + for (u32 i = 0; i < mc; ++i) { + const TileSpec *tile = &(f.tiles[i]); + scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) { + const TileLayer *layer = &tile->layers[layernum]; + if (layer->texture_id == 0) + continue; + if (layernum != 0) { + scene::IMeshBuffer *copy = cloneMeshBuffer(buf); + copy->getMaterial() = buf->getMaterial(); + mesh->addMeshBuffer(copy); + copy->drop(); + buf = copy; + colors->push_back( + ItemPartColor(layer->has_color, layer->color)); + } else { + (*colors)[i] = ItemPartColor(layer->has_color, layer->color); + } + video::SMaterial &material = buf->getMaterial(); + if (set_material) + layer->applyMaterialOptions(material); + if (mattype) { + material.MaterialType = *mattype; + } + if (layer->animation_frame_count > 1) { + FrameSpec animation_frame = layer->frames[0]; + material.setTexture(0, animation_frame.texture); + } else { + material.setTexture(0, layer->texture); + } + if (use_shaders) { + if (layer->normal_texture) { + if (layer->animation_frame_count > 1) { + FrameSpec animation_frame = layer->frames[0]; + material.setTexture(1, animation_frame.normal_texture); + } else + material.setTexture(1, layer->normal_texture); + } + material.setTexture(2, layer->flags_texture); + } + } + } +} diff --git a/src/wieldmesh.h b/src/wieldmesh.h index d3946b4e0..c98b469d9 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -26,17 +26,41 @@ with this program; if not, write to the Free Software Foundation, Inc., struct ItemStack; class Client; class ITextureSource; -struct TileSpec; +struct ContentFeatures; + +/*! + * Holds color information of an item mesh's buffer. + */ +struct ItemPartColor { + /*! + * If this is false, the global base color of the item + * will be used instead of the specific color of the + * buffer. + */ + bool override_base; + /*! + * The color of the buffer. + */ + video::SColor color; + + ItemPartColor(): + override_base(false), + color(0) + {} + + ItemPartColor(bool override, video::SColor color): + override_base(override), + color(color) + {} +}; 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; + std::vector buffer_colors; ItemMesh() : mesh(NULL), buffer_colors() {} }; @@ -51,7 +75,8 @@ public: s32 id = -1, bool lighting = false); virtual ~WieldMeshSceneNode(); - void setCube(const TileSpec tiles[6], v3f wield_scale, ITextureSource *tsrc); + void setCube(const ContentFeatures &f, v3f wield_scale, + ITextureSource *tsrc); void setExtruded(const std::string &imagename, v3f wield_scale, ITextureSource *tsrc, u8 num_frames); void setItem(const ItemStack &item, Client *client); @@ -84,7 +109,12 @@ private: * Stores the colors of the mesh's mesh buffers. * This does not include lighting. */ - std::vector m_colors; + std::vector m_colors; + /*! + * The base color of this mesh. This is the default + * for all mesh buffers. + */ + video::SColor m_base_color; // Bounding box culling is disabled for this type of scene node, // so this variable is just required so we can implement @@ -94,5 +124,16 @@ private: void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result); -scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename); +scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename); + +/*! + * Applies overlays, textures and optionally materials to the given mesh and + * extracts tile colors for colorization. + * \param mattype overrides the buffer's material type, but can also + * be NULL to leave the original material. + * \param colors returns the colors of the mesh buffers in the mesh. + */ +void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, + bool use_shaders, bool set_material, video::E_MATERIAL_TYPE *mattype, + std::vector *colors); #endif -- cgit v1.2.3 From dc5bc6cac7b81ab27e0064bc25b5fd1d8d617340 Mon Sep 17 00:00:00 2001 From: Vincent Glize Date: Sat, 29 Apr 2017 12:08:16 +0200 Subject: [CSM] Add event on_place_node API lua (#5548) * [CSM] Add event on_place_node API lua --- builtin/client/register.lua | 1 + clientmods/preview/init.lua | 7 +++++++ doc/client_lua_api.md | 2 ++ src/game.cpp | 3 +++ src/script/common/c_content.cpp | 44 +++++++++++++++++++++++++++++++++++++++++ src/script/common/c_content.h | 6 ++++++ src/script/cpp_api/s_base.cpp | 15 ++------------ src/script/cpp_api/s_base.h | 1 - src/script/cpp_api/s_client.cpp | 18 +++++++++++++++++ src/script/cpp_api/s_client.h | 3 +++ src/script/cpp_api/s_entity.cpp | 2 +- src/script/cpp_api/s_item.cpp | 23 +-------------------- 12 files changed, 88 insertions(+), 37 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/builtin/client/register.lua b/builtin/client/register.lua index e6ce25654..b35ecc849 100644 --- a/builtin/client/register.lua +++ b/builtin/client/register.lua @@ -69,3 +69,4 @@ core.registered_on_damage_taken, core.register_on_damage_taken = make_registrati core.registered_on_formspec_input, core.register_on_formspec_input = make_registration() core.registered_on_dignode, core.register_on_dignode = make_registration() core.registered_on_punchnode, core.register_on_punchnode = make_registration() +core.registered_on_placenode, core.register_on_placenode = make_registration() diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index 4b31fa323..255c0b83f 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -10,6 +10,13 @@ core.register_on_connect(function() print("[PREVIEW] Player connection completed") end) +core.register_on_placenode(function(pointed_thing, node) + print("The local player place a node!") + print("pointed_thing :" .. dump(pointed_thing)) + print("node placed :" .. dump(node)) + return false +end) + -- This is an example function to ensure it's working properly, should be removed before merge core.register_on_receiving_chat_messages(function(message) print("[PREVIEW] Received message " .. message) diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index 86fcd02a7..d435c4aae 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -667,6 +667,8 @@ Call these functions only at load time! * Called when the local player punches a node * Newest functions are called first * If any function returns true, the punch is ignored +* `minetest.register_on_placenode(function(pointed_thing, node))` + * Called when a node has been placed ### Sounds * `minetest.sound_play(spec, parameters)`: returns a handle * `spec` is a `SimpleSoundSpec` diff --git a/src/game.cpp b/src/game.cpp index eb59ee5ae..12fe6a6e9 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3730,6 +3730,9 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio // Read the sound soundmaker->m_player_rightpunch_sound = playeritem_def.sound_place; + + if (client->moddingEnabled()) + client->getScript()->on_placenode(pointed, playeritem_def); } else { soundmaker->m_player_rightpunch_sound = SimpleSoundSpec(); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 573347b4c..5fe5af58d 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "mg_schematic.h" #include "noise.h" +#include "util/pointedthing.h" #include struct EnumString es_TileAnimationType[] = @@ -117,6 +118,16 @@ void read_item_definition(lua_State* L, int index, def.node_placement_prediction); } +/******************************************************************************/ +void push_item_definition(lua_State *L, const ItemDefinition &i) +{ + lua_newtable(L); + lua_pushstring(L, i.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, i.description.c_str()); + lua_setfield(L, -2, "description"); +} + /******************************************************************************/ void read_object_properties(lua_State *L, int index, ObjectProperties *prop, IItemDefManager *idef) @@ -1427,3 +1438,36 @@ void read_json_value(lua_State *L, Json::Value &root, int index, u8 recursion) } lua_pop(L, 1); // Pop value } + +void push_pointed_thing(lua_State *L, const PointedThing &pointed) +{ + lua_newtable(L); + if (pointed.type == POINTEDTHING_NODE) { + lua_pushstring(L, "node"); + lua_setfield(L, -2, "type"); + push_v3s16(L, pointed.node_undersurface); + lua_setfield(L, -2, "under"); + push_v3s16(L, pointed.node_abovesurface); + lua_setfield(L, -2, "above"); + } else if (pointed.type == POINTEDTHING_OBJECT) { + lua_pushstring(L, "object"); + lua_setfield(L, -2, "type"); + push_objectRef(L, pointed.object_id); + lua_setfield(L, -2, "ref"); + } else { + lua_pushstring(L, "nothing"); + lua_setfield(L, -2, "type"); + } +} + +void push_objectRef(lua_State *L, const u16 id) +{ + // Get core.object_refs[i] + lua_getglobal(L, "core"); + lua_getfield(L, -1, "object_refs"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushnumber(L, id); + lua_gettable(L, -2); + lua_remove(L, -2); // object_refs + lua_remove(L, -2); // core +} diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index c701c0384..219c5eb7c 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -88,6 +88,8 @@ void push_tool_capabilities (lua_State *L, void read_item_definition (lua_State *L, int index, const ItemDefinition &default_def, ItemDefinition &def); +void push_item_definition (lua_State *L, + const ItemDefinition &i); void read_object_properties (lua_State *L, int index, ObjectProperties *prop, IItemDefManager *idef); @@ -162,6 +164,10 @@ bool push_json_value (lua_State *L, void read_json_value (lua_State *L, Json::Value &root, int index, u8 recursion = 0); +void push_pointed_thing (lua_State *L, const PointedThing &pointed); + +void push_objectRef (lua_State *L, const u16 id); + extern struct EnumString es_TileAnimationType[]; #endif /* C_CONTENT_H_ */ diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 6a843810f..e72af22c6 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -42,6 +42,7 @@ extern "C" { #include #include +#include "script/common/c_content.h" #include @@ -320,22 +321,10 @@ void ScriptApiBase::objectrefGetOrCreate(lua_State *L, if (cobj == NULL || cobj->getId() == 0) { ObjectRef::create(L, cobj); } else { - objectrefGet(L, cobj->getId()); + push_objectRef(L, cobj->getId()); } } -void ScriptApiBase::objectrefGet(lua_State *L, u16 id) -{ - // Get core.object_refs[i] - lua_getglobal(L, "core"); - lua_getfield(L, -1, "object_refs"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); - lua_gettable(L, -2); - lua_remove(L, -2); // object_refs - lua_remove(L, -2); // core -} - Server* ScriptApiBase::getServer() { return dynamic_cast(m_gamedef); diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index 19d71df65..5b047a081 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -115,7 +115,6 @@ protected: void setGuiEngine(GUIEngine* guiengine) { m_guiengine = guiengine; } void objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj); - void objectrefGet(lua_State *L, u16 id); RecursiveMutex m_luastackmutex; std::string m_last_run_mod; diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp index a8a7d5e26..4bc368d1d 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client.h" #include "common/c_converter.h" #include "common/c_content.h" +#include "s_item.h" void ScriptApiClient::on_shutdown() { @@ -189,6 +190,23 @@ bool ScriptApiClient::on_punchnode(v3s16 p, MapNode node) return blocked; } +bool ScriptApiClient::on_placenode(const PointedThing &pointed, const ItemDefinition &item) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_placenode + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_placenode"); + + // Push data + push_pointed_thing(L, pointed); + push_item_definition(L, item); + + // Call functions + runCallbacks(2, RUN_CALLBACKS_MODE_OR); + return lua_toboolean(L, -1); +} + void ScriptApiClient::setEnv(ClientEnvironment *env) { ScriptApiBase::setEnv(env); diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h index 94a597b2c..f252cf499 100644 --- a/src/script/cpp_api/s_client.h +++ b/src/script/cpp_api/s_client.h @@ -21,8 +21,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef S_CLIENT_H_ #define S_CLIENT_H_ +#include "util/pointedthing.h" #include "cpp_api/s_base.h" #include "mapnode.h" +#include "itemdef.h" #include "util/string.h" #ifdef _CRT_MSVCP_CURRENT @@ -51,6 +53,7 @@ public: bool on_dignode(v3s16 p, MapNode node); bool on_punchnode(v3s16 p, MapNode node); + bool on_placenode(const PointedThing &pointed, const ItemDefinition &item); void setEnv(ClientEnvironment *env); }; diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index 2e1d277e4..4c1e296d4 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -57,7 +57,7 @@ bool ScriptApiEntity::luaentity_Add(u16 id, const char *name) // Add object reference // This should be userdata with metatable ObjectRef - objectrefGet(L, id); + push_objectRef(L, id); luaL_checktype(L, -1, LUA_TUSERDATA); if (!luaL_checkudata(L, -1, "ObjectRef")) luaL_typerror(L, -1, "ObjectRef"); diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index cbb833807..032018f2f 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -249,27 +249,6 @@ void ScriptApiItem::pushPointedThing(const PointedThing& pointed) { lua_State* L = getStack(); - lua_newtable(L); - if(pointed.type == POINTEDTHING_NODE) - { - lua_pushstring(L, "node"); - lua_setfield(L, -2, "type"); - push_v3s16(L, pointed.node_undersurface); - lua_setfield(L, -2, "under"); - push_v3s16(L, pointed.node_abovesurface); - lua_setfield(L, -2, "above"); - } - else if(pointed.type == POINTEDTHING_OBJECT) - { - lua_pushstring(L, "object"); - lua_setfield(L, -2, "type"); - objectrefGet(L, pointed.object_id); - lua_setfield(L, -2, "ref"); - } - else - { - lua_pushstring(L, "nothing"); - lua_setfield(L, -2, "type"); - } + push_pointed_thing(L, pointed); } -- cgit v1.2.3 From bd921a7916f0fafc493b1c4d0eeb5e2bb1d6a7c2 Mon Sep 17 00:00:00 2001 From: Brandon Date: Sun, 10 Jul 2016 00:08:26 -0500 Subject: Sound API: Add fading sounds --- doc/lua_api.txt | 7 +++ src/client.cpp | 1 + src/client.h | 1 + src/network/clientopcodes.cpp | 2 +- src/network/clientpackethandler.cpp | 35 +++++++++++++- src/network/networkprotocol.h | 11 ++++- src/script/common/c_content.cpp | 2 + src/script/common/c_converter.h | 2 + src/script/lua_api/l_server.cpp | 11 +++++ src/script/lua_api/l_server.h | 3 ++ src/server.cpp | 64 +++++++++++++++++++++++-- src/server.h | 6 ++- src/sound.h | 28 +++++++---- src/sound_openal.cpp | 94 ++++++++++++++++++++++++++++++++++++- 14 files changed, 248 insertions(+), 19 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 479e38a2e..77ffb88e2 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -456,11 +456,13 @@ Examples of sound parameter tables: -- Play locationless on all clients { gain = 1.0, -- default + fade = 0.0, -- default, change to a value > 0 to fade the sound in } -- Play locationless to one player { to_player = name, gain = 1.0, -- default + fade = 0.0, -- default, change to a value > 0 to fade the sound in } -- Play locationless to one player, looped { @@ -2587,6 +2589,11 @@ These functions return the leftover itemstack. * `spec` is a `SimpleSoundSpec` * `parameters` is a sound parameter table * `minetest.sound_stop(handle)` +* `minetest.sound_fade(handle, step, gain)` + * `handle` is a handle returned by minetest.sound_play + * `step` determines how fast a sound will fade. + Negative step will lower the sound volume, positive step will increase the sound volume + * `gain` the target gain for the fade. ### Timing * `minetest.after(time, func, ...)` diff --git a/src/client.cpp b/src/client.cpp index 3c5a70f21..3269c573a 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -407,6 +407,7 @@ void Client::step(float dtime) // Step environment m_env.step(dtime); + m_sound->step(dtime); /* Get events diff --git a/src/client.h b/src/client.h index 7cbfadd50..e8db7de44 100644 --- a/src/client.h +++ b/src/client.h @@ -328,6 +328,7 @@ public: void handleCommand_ItemDef(NetworkPacket* pkt); void handleCommand_PlaySound(NetworkPacket* pkt); void handleCommand_StopSound(NetworkPacket* pkt); + void handleCommand_FadeSound(NetworkPacket *pkt); void handleCommand_Privileges(NetworkPacket* pkt); void handleCommand_InventoryFormSpec(NetworkPacket* pkt); void handleCommand_DetachedInventory(NetworkPacket* pkt); diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 1be6e5522..bdcb1dfce 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -109,7 +109,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52 { "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53 { "TOCLIENT_CLOUD_PARAMS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CloudParams }, // 0x54 - null_command_handler, + { "TOCLIENT_FADE_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FadeSound }, // 0x55 null_command_handler, null_command_handler, null_command_handler, diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index defc83f31..a895acc84 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -755,21 +755,39 @@ void Client::handleCommand_ItemDef(NetworkPacket* pkt) void Client::handleCommand_PlaySound(NetworkPacket* pkt) { + /* + [0] u32 server_id + [4] u16 name length + [6] char name[len] + [ 6 + len] f32 gain + [10 + len] u8 type + [11 + len] (f32 * 3) pos + [23 + len] u16 object_id + [25 + len] bool loop + [26 + len] f32 fade + */ + s32 server_id; std::string name; + float gain; u8 type; // 0=local, 1=positional, 2=object v3f pos; u16 object_id; bool loop; + float fade = 0; *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop; + try { + *pkt >> fade; + } catch (SerializationError &e) {}; + // Start playing int client_id = -1; switch(type) { case 0: // local - client_id = m_sound->playSound(name, loop, gain); + client_id = m_sound->playSound(name, loop, gain, fade); break; case 1: // positional client_id = m_sound->playSoundAt(name, loop, gain, pos); @@ -808,6 +826,21 @@ void Client::handleCommand_StopSound(NetworkPacket* pkt) } } +void Client::handleCommand_FadeSound(NetworkPacket *pkt) +{ + s32 sound_id; + float step; + float gain; + + *pkt >> sound_id >> step >> gain; + + UNORDERED_MAP::iterator i = + m_sounds_server_to_client.find(sound_id); + + if (i != m_sounds_server_to_client.end()) + m_sound->fadeSound(i->second, step, gain); +} + void Client::handleCommand_Privileges(NetworkPacket* pkt) { m_privileges.clear(); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index a1a4f5bfa..70cad85d8 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -153,9 +153,11 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL VERSION 31: Add tile overlay Stop sending TOSERVER_CLIENT_READY + PROTOCOL VERSION 32: + Add fading sounds */ -#define LATEST_PROTOCOL_VERSION 31 +#define LATEST_PROTOCOL_VERSION 32 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 24 @@ -620,6 +622,13 @@ enum ToClientCommand v2f1000 speed */ + TOCLIENT_FADE_SOUND = 0x55, + /* + s32 sound_id + float step + float gain + */ + TOCLIENT_SRP_BYTES_S_B = 0x60, /* Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP. diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 5fe5af58d..8696ad7cb 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -680,6 +680,7 @@ void read_server_sound_params(lua_State *L, int index, if(lua_istable(L, index)){ getfloatfield(L, index, "gain", params.gain); getstringfield(L, index, "to_player", params.to_player); + getfloatfield(L, index, "fade", params.fade); lua_getfield(L, index, "pos"); if(!lua_isnil(L, -1)){ v3f p = read_v3f(L, -1)*BS; @@ -712,6 +713,7 @@ void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec) } else if(lua_istable(L, index)){ getstringfield(L, index, "name", spec.name); getfloatfield(L, index, "gain", spec.gain); + getfloatfield(L, index, "fade", spec.fade); } else if(lua_isstring(L, index)){ spec.name = lua_tostring(L, index); } diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h index a5fbee765..b0f61a8ca 100644 --- a/src/script/common/c_converter.h +++ b/src/script/common/c_converter.h @@ -77,6 +77,8 @@ void setfloatfield(lua_State *L, int table, const char *fieldname, float value); void setboolfield(lua_State *L, int table, const char *fieldname, bool value); +void setstringfield(lua_State *L, int table, + const char *fieldname, const char *value); v3f checkFloatPos (lua_State *L, int index); v2f check_v2f (lua_State *L, int index); diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 7b723d14c..ea993d7b7 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -455,6 +455,16 @@ int ModApiServer::l_sound_stop(lua_State *L) return 0; } +int ModApiServer::l_sound_fade(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + s32 handle = luaL_checkinteger(L, 1); + float step = luaL_checknumber(L, 2); + float gain = luaL_checknumber(L, 3); + getServer(L)->fadeSound(handle, step, gain); + return 0; +} + // is_singleplayer() int ModApiServer::l_is_singleplayer(lua_State *L) { @@ -518,6 +528,7 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(show_formspec); API_FCT(sound_play); API_FCT(sound_stop); + API_FCT(sound_fade); API_FCT(get_player_information); API_FCT(get_player_privs); diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index 3a4a917c0..251a0ce89 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -68,6 +68,9 @@ private: // sound_stop(handle) static int l_sound_stop(lua_State *L); + // sound_fade(handle, step, gain) + static int l_sound_fade(lua_State *L); + // get_player_privs(name, text) static int l_get_player_privs(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 9ef69cb37..190a1baf2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2100,15 +2100,23 @@ s32 Server::playSound(const SimpleSoundSpec &spec, m_playing_sounds[id] = ServerPlayingSound(); ServerPlayingSound &psound = m_playing_sounds[id]; psound.params = params; + psound.spec = spec; + float gain = params.gain * spec.gain; NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0); - pkt << id << spec.name << (float) (spec.gain * params.gain) - << (u8) params.type << pos << params.object << params.loop; + pkt << id << spec.name << gain + << (u8) params.type << pos << params.object + << params.loop << params.fade; - for(std::vector::iterator i = dst_clients.begin(); + // Backwards compability + bool play_sound = gain > 0; + + for (std::vector::iterator i = dst_clients.begin(); i != dst_clients.end(); ++i) { - psound.clients.insert(*i); - m_clients.send(*i, 0, &pkt, true); + if (play_sound || m_clients.getProtocolVersion(*i) >= 32) { + psound.clients.insert(*i); + m_clients.send(*i, 0, &pkt, true); + } } return id; } @@ -2132,6 +2140,52 @@ void Server::stopSound(s32 handle) m_playing_sounds.erase(i); } +void Server::fadeSound(s32 handle, float step, float gain) +{ + // Get sound reference + UNORDERED_MAP::iterator i = + m_playing_sounds.find(handle); + if (i == m_playing_sounds.end()) + return; + + ServerPlayingSound &psound = i->second; + psound.params.gain = gain; + + NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4); + pkt << handle << step << gain; + + // Backwards compability + bool play_sound = gain > 0; + ServerPlayingSound compat_psound = psound; + compat_psound.clients.clear(); + + NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4); + compat_pkt << handle; + + for (UNORDERED_SET::iterator it = psound.clients.begin(); + it != psound.clients.end();) { + if (m_clients.getProtocolVersion(*it) >= 32) { + // Send as reliable + m_clients.send(*it, 0, &pkt, true); + ++it; + } else { + compat_psound.clients.insert(*it); + // Stop old sound + m_clients.send(*it, 0, &compat_pkt, true); + psound.clients.erase(it++); + } + } + + // Remove sound reference + if (!play_sound || psound.clients.size() == 0) + m_playing_sounds.erase(i); + + if (play_sound && compat_psound.clients.size() > 0) { + // Play new sound volume on older clients + playSound(compat_psound.spec, compat_psound.params); + } +} + void Server::sendRemoveNode(v3s16 p, u16 ignore_id, std::vector *far_players, float far_d_nodes) { diff --git a/src/server.h b/src/server.h index 3a082b9a4..5e6211637 100644 --- a/src/server.h +++ b/src/server.h @@ -115,6 +115,7 @@ struct ServerSoundParams u16 object; float max_hear_distance; bool loop; + float fade; ServerSoundParams(): gain(1.0), @@ -123,7 +124,8 @@ struct ServerSoundParams pos(0,0,0), object(0), max_hear_distance(32*BS), - loop(false) + loop(false), + fade(0) {} v3f getPos(ServerEnvironment *env, bool *pos_exists) const; @@ -132,6 +134,7 @@ struct ServerSoundParams struct ServerPlayingSound { ServerSoundParams params; + SimpleSoundSpec spec; UNORDERED_SET clients; // peer ids }; @@ -231,6 +234,7 @@ public: // Envlock s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams ¶ms); void stopSound(s32 handle); + void fadeSound(s32 handle, float step, float gain); // Envlock std::set getPlayerEffectivePrivs(const std::string &name); diff --git a/src/sound.h b/src/sound.h index 98f7692d5..7bdb6a26b 100644 --- a/src/sound.h +++ b/src/sound.h @@ -34,8 +34,8 @@ public: struct SimpleSoundSpec { - SimpleSoundSpec(const std::string &name = "", float gain = 1.0) - : name(name), gain(gain) + SimpleSoundSpec(const std::string &name = "", float gain = 1.0, float fade = 0.0) + : name(name), gain(gain), fade(fade) { } @@ -43,13 +43,13 @@ struct SimpleSoundSpec std::string name; float gain; + float fade; }; class ISoundManager { public: virtual ~ISoundManager() {} - // Multiple sounds can be loaded per name; when played, the sound // should be chosen randomly from alternatives // Return value determines success/failure @@ -63,16 +63,21 @@ public: // playSound functions return -1 on failure, otherwise a handle to the // sound. If name=="", call should be ignored without error. - virtual int playSound(const std::string &name, bool loop, float volume) = 0; - virtual int playSoundAt( - const std::string &name, bool loop, float volume, v3f pos) = 0; + virtual int playSound(const std::string &name, bool loop, float volume, + float fade = 0) = 0; + virtual int playSoundAt(const std::string &name, bool loop, float volume, + v3f pos) = 0; virtual void stopSound(int sound) = 0; virtual bool soundExists(int sound) = 0; virtual void updateSoundPosition(int sound, v3f pos) = 0; + virtual bool updateSoundGain(int id, float gain) = 0; + virtual float getSoundGain(int id) = 0; + virtual void step(float dtime) = 0; + virtual void fadeSound(int sound, float step, float gain) = 0; int playSound(const SimpleSoundSpec &spec, bool loop) { - return playSound(spec.name, loop, spec.gain); + return playSound(spec.name, loop, spec.gain, spec.fade); } int playSoundAt(const SimpleSoundSpec &spec, bool loop, v3f pos) { @@ -93,7 +98,10 @@ public: } void updateListener(v3f pos, v3f vel, v3f at, v3f up) {} void setListenerGain(float gain) {} - int playSound(const std::string &name, bool loop, float volume) { return 0; } + int playSound(const std::string &name, bool loop, float volume, float fade) + { + return 0; + } int playSoundAt(const std::string &name, bool loop, float volume, v3f pos) { return 0; @@ -101,6 +109,10 @@ public: void stopSound(int sound) {} bool soundExists(int sound) { return false; } void updateSoundPosition(int sound, v3f pos) {} + bool updateSoundGain(int id, float gain) { return false; } + float getSoundGain(int id) { return 0; } + void step(float dtime) { } + void fadeSound(int sound, float step, float gain) { } }; // Global DummySoundManager singleton diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index b9af9e3a9..a425af827 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -274,6 +274,19 @@ private: UNORDERED_MAP > m_buffers; UNORDERED_MAP m_sounds_playing; v3f m_listener_pos; + struct FadeState { + FadeState() {} + FadeState(float step, float current_gain, float target_gain): + step(step), + current_gain(current_gain), + target_gain(target_gain) {} + float step; + float current_gain; + float target_gain; + }; + + UNORDERED_MAP m_sounds_fading; + float m_fade_delay; public: bool m_is_initialized; OpenALSoundManager(OnDemandSoundFetcher *fetcher): @@ -281,6 +294,7 @@ public: m_device(NULL), m_context(NULL), m_next_id(1), + m_fade_delay(0), m_is_initialized(false) { ALCenum error = ALC_NO_ERROR; @@ -349,6 +363,11 @@ public: infostream<<"Audio: Deinitialized."< >::iterator i = @@ -515,6 +534,7 @@ public: addBuffer(name, buf); return false; } + bool loadSoundData(const std::string &name, const std::string &filedata) { @@ -541,7 +561,7 @@ public: alListenerf(AL_GAIN, gain); } - int playSound(const std::string &name, bool loop, float volume) + int playSound(const std::string &name, bool loop, float volume, float fade) { maintain(); if(name == "") @@ -552,8 +572,16 @@ public: < 0) { + handle = playSoundRaw(buf, loop, 0); + fadeSound(handle, fade, volume); + } else { + handle = playSoundRaw(buf, loop, volume); + } + return handle; } + int playSoundAt(const std::string &name, bool loop, float volume, v3f pos) { maintain(); @@ -567,16 +595,55 @@ public: } return playSoundRawAt(buf, loop, volume, pos); } + void stopSound(int sound) { maintain(); deleteSound(sound); } + + void fadeSound(int soundid, float step, float gain) + { + m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain); + } + + void doFades(float dtime) + { + m_fade_delay += dtime; + + if (m_fade_delay < 0.1f) + return; + + float chkGain = 0; + for (UNORDERED_MAP::iterator i = m_sounds_fading.begin(); + i != m_sounds_fading.end();) { + if (i->second.step < 0.f) + chkGain = -(i->second.current_gain); + else + chkGain = i->second.current_gain; + + if (chkGain < i->second.target_gain) { + i->second.current_gain += (i->second.step * m_fade_delay); + i->second.current_gain = rangelim(i->second.current_gain, 0, 1); + + updateSoundGain(i->first, i->second.current_gain); + ++i; + } else { + if (i->second.target_gain <= 0.f) + stopSound(i->first); + + m_sounds_fading.erase(i++); + } + } + m_fade_delay = 0; + } + bool soundExists(int sound) { maintain(); return (m_sounds_playing.count(sound) != 0); } + void updateSoundPosition(int id, v3f pos) { UNORDERED_MAP::iterator i = m_sounds_playing.find(id); @@ -589,6 +656,29 @@ public: alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0); } + + bool updateSoundGain(int id, float gain) + { + UNORDERED_MAP::iterator i = m_sounds_playing.find(id); + if (i == m_sounds_playing.end()) + return false; + + PlayingSound *sound = i->second; + alSourcef(sound->source_id, AL_GAIN, gain); + return true; + } + + float getSoundGain(int id) + { + UNORDERED_MAP::iterator i = m_sounds_playing.find(id); + if (i == m_sounds_playing.end()) + return 0; + + PlayingSound *sound = i->second; + ALfloat gain; + alGetSourcef(sound->source_id, AL_GAIN, &gain); + return gain; + } }; ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher) -- cgit v1.2.3 From 772944daf69ab463568fbbf35a9011e4c8e6c44a Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 20 May 2017 15:45:49 +0100 Subject: Fix CSM crash (#5779) Caused by dc5bc6c and them made worse by 5ebf8f9 --- doc/client_lua_api.md | 2 +- src/script/common/c_content.cpp | 12 +++++++++--- src/script/common/c_content.h | 2 +- src/script/cpp_api/s_client.cpp | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index 2c0351a11..5b70c2a67 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -202,7 +202,7 @@ For helper functions see "Vector helpers". ### pointed_thing * `{type="nothing"}` * `{type="node", under=pos, above=pos}` -* `{type="object", ref=ObjectRef}` +* `{type="object", id=ObjectID}` Flag Specifier Format --------------------- diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 8696ad7cb..7eb1d094b 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1441,7 +1441,7 @@ void read_json_value(lua_State *L, Json::Value &root, int index, u8 recursion) lua_pop(L, 1); // Pop value } -void push_pointed_thing(lua_State *L, const PointedThing &pointed) +void push_pointed_thing(lua_State *L, const PointedThing &pointed, bool csm) { lua_newtable(L); if (pointed.type == POINTEDTHING_NODE) { @@ -1454,8 +1454,14 @@ void push_pointed_thing(lua_State *L, const PointedThing &pointed) } else if (pointed.type == POINTEDTHING_OBJECT) { lua_pushstring(L, "object"); lua_setfield(L, -2, "type"); - push_objectRef(L, pointed.object_id); - lua_setfield(L, -2, "ref"); + + if (csm) { + lua_pushinteger(L, pointed.object_id); + lua_setfield(L, -2, "id"); + } else { + push_objectRef(L, pointed.object_id); + lua_setfield(L, -2, "ref"); + } } else { lua_pushstring(L, "nothing"); lua_setfield(L, -2, "type"); diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 219c5eb7c..28d8b1e8c 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -164,7 +164,7 @@ bool push_json_value (lua_State *L, void read_json_value (lua_State *L, Json::Value &root, int index, u8 recursion = 0); -void push_pointed_thing (lua_State *L, const PointedThing &pointed); +void push_pointed_thing (lua_State *L, const PointedThing &pointed, bool csm = false); void push_objectRef (lua_State *L, const u16 id); diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp index d5ec52407..55d309fda 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -199,7 +199,7 @@ bool ScriptApiClient::on_placenode(const PointedThing &pointed, const ItemDefini lua_getfield(L, -1, "registered_on_placenode"); // Push data - push_pointed_thing(L, pointed); + push_pointed_thing(L, pointed, true); push_item_definition(L, item); // Call functions @@ -217,7 +217,7 @@ bool ScriptApiClient::on_item_use(const ItemStack &item, const PointedThing &poi // Push data LuaItemStack::create(L, item); - push_pointed_thing(L, pointed); + push_pointed_thing(L, pointed, true); // Call functions runCallbacks(2, RUN_CALLBACKS_MODE_OR); -- cgit v1.2.3 From dfa0c15ce045705f05487d623dc7beca6c945b4b Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Sun, 21 May 2017 07:40:55 -0500 Subject: [CSM] Add function to get the definition of items (#5732) Add node def and item def documentation. Please be ready for merge! --- doc/client_lua_api.md | 98 ++++++++++++++- src/script/common/c_content.cpp | 255 ++++++++++++++++++++++++++++++++++++++++ src/script/common/c_content.h | 18 +++ src/script/lua_api/l_client.cpp | 47 ++++++++ src/script/lua_api/l_client.h | 10 +- 5 files changed, 425 insertions(+), 3 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index 5b70c2a67..ce0746df4 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -989,6 +989,96 @@ Can be obtained via `minetest.get_meta(pos)`. * `inventory`: `{list1 = {}, ...}}` ----------------- +### Definitions +* `minetest.get_node_def(nodename)` + * Returns [node definition](#node-definition) table of `nodename` +* `minetest.get_item_def(itemstring)` + * Returns item definition table of `itemstring` + +#### Node Definition + +```lua + { + has_on_construct = bool, -- Whether the node has the on_construct callback defined + has_on_destruct = bool, -- Whether the node has the on_destruct callback defined + has_after_destruct = bool, -- Whether the node has the after_destruct callback defined + name = string, -- The name of the node e.g. "air", "default:dirt" + groups = table, -- The groups of the node + paramtype = string, -- Paramtype of the node + paramtype2 = string, -- ParamType2 of the node + drawtype = string, -- Drawtype of the node + mesh = , -- Mesh name if existant + minimap_color = , -- Color of node on minimap *May not exist* + visual_scale = number, -- Visual scale of node + alpha = number, -- Alpha of the node. Only used for liquids + color = , -- Color of node *May not exist* + palette_name = , -- Filename of palette *May not exist* + palette = <{ -- List of colors + Color, + Color + }>, + waving = number, -- 0 of not waving, 1 if waving + connect_sides = number, -- Used for connected nodes + connects_to = { -- List of nodes to connect to + "node1", + "node2" + }, + post_effect_color = Color, -- Color overlayed on the screen when the player is in the node + leveled = number, -- Max level for node + sunlight_propogates = bool, -- Whether light passes through the block + light_source = number, -- Light emitted by the block + is_ground_content = bool, -- Whether caves should cut through the node + walkable = bool, -- Whether the player collides with the node + pointable = bool, -- Whether the player can select the node + diggable = bool, -- Whether the player can dig the node + climbable = bool, -- Whether the player can climb up the node + buildable_to = bool, -- Whether the player can replace the node by placing a node on it + rightclickable = bool, -- Whether the player can place nodes pointing at this node + damage_per_second = number, -- HP of damage per second when the player is in the node + liquid_type = , -- A string containing "none", "flowing", or "source" *May not exist* + liquid_alternative_flowing = , -- Alternative node for liquid *May not exist* + liquid_alternative_source = , -- Alternative node for liquid *May not exist* + liquid_viscosity = , -- How fast the liquid flows *May not exist* + liquid_renewable = , -- Whether the liquid makes an infinite source *May not exist* + liquid_range = , -- How far the liquid flows *May not exist* + drowning = bool, -- Whether the player will drown in the node + floodable = bool, -- Whether nodes will be replaced by liquids (flooded) + node_box = table, -- Nodebox to draw the node with + collision_box = table, -- Nodebox to set the collision area + selection_box = table, -- Nodebox to set the area selected by the player + sounds = { -- Table of sounds that the block makes + sound_footstep = SimpleSoundSpec, + sound_dig = SimpleSoundSpec, + sound_dug = SimpleSoundSpec + }, + legacy_facedir_simple = bool, -- Whether to use old facedir + legacy_wallmounted = bool -- Whether to use old wallmounted + } +``` + +#### Item Definition + +```lua + { + name = string, -- Name of the item e.g. "default:stone" + description = string, -- Description of the item e.g. "Stone" + type = string, -- Item type: "none", "node", "craftitem", "tool" + inventory_image = string, -- Image in the inventory + wield_image = string, -- Image in wieldmesh + palette_image = string, -- Image for palette + color = Color, -- Color for item + wield_scale = Vector, -- Wieldmesh scale + stack_max = number, -- Number of items stackable together + usable = bool, -- Has on_use callback defined + liquids_pointable = bool, -- Whether you can point at liquids with the item + tool_capabilities = , -- If the item is a tool, tool capabiltites of the item + groups = table, -- Groups of the item + sound_place = SimpleSoundSpec, -- Sound played when placed + sound_place_failed = SimpleSoundSpec, -- Sound played when placement failed + node_placement_prediction = string -- Node placed in client until server catches up + } +``` +----------------- ### Chat command definition (`register_chatcommand`) @@ -1014,7 +1104,7 @@ Most text can contain escape sequences, that can for example color the text. There are a few exceptions: tab headers, dropdowns and vertical labels can't. The following functions provide escape sequences: * `minetest.get_color_escape_sequence(color)`: - * `color` is a ColorString + * `color` is a [ColorString](#colorstring) * The escape sequence sets the text color to `color` * `minetest.colorize(color, message)`: * Equivalent to: @@ -1022,7 +1112,7 @@ The following functions provide escape sequences: message .. minetest.get_color_escape_sequence("#ffffff")` * `color.get_background_escape_sequence(color)` - * `color` is a ColorString + * `color` is a [ColorString](#colorstring) * The escape sequence sets the background of the whole text element to `color`. Only defined for item descriptions and tooltips. * `color.strip_foreground_colors(str)` @@ -1047,3 +1137,7 @@ Named colors are also supported and are equivalent to To specify the value of the alpha channel, append `#AA` to the end of the color name (e.g. `colorname#08`). For named colors the hexadecimal string representing the alpha value must (always) be two hexadecimal digits. + +`Color` +------------- +`{a = alpha, r = red, g = green, b = blue}` defines an ARGB8 color. diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 7eb1d094b..c0e29abf0 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_schematic.h" #include "noise.h" #include "util/pointedthing.h" +#include "debug.h" // For FATAL_ERROR #include struct EnumString es_TileAnimationType[] = @@ -128,6 +129,51 @@ void push_item_definition(lua_State *L, const ItemDefinition &i) lua_setfield(L, -2, "description"); } +void push_item_definition_full(lua_State *L, const ItemDefinition &i) +{ + std::string type(es_ItemType[(int)i.type].str); + + lua_newtable(L); + lua_pushstring(L, i.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, i.description.c_str()); + lua_setfield(L, -2, "description"); + lua_pushstring(L, type.c_str()); + lua_setfield(L, -2, "type"); + lua_pushstring(L, i.inventory_image.c_str()); + lua_setfield(L, -2, "inventory_image"); + lua_pushstring(L, i.wield_image.c_str()); + lua_setfield(L, -2, "wield_image"); + lua_pushstring(L, i.palette_image.c_str()); + lua_setfield(L, -2, "palette_image"); + push_ARGB8(L, i.color); + lua_setfield(L, -2, "color"); + push_v3f(L, i.wield_scale); + lua_setfield(L, -2, "wield_scale"); + lua_pushinteger(L, i.stack_max); + lua_setfield(L, -2, "stack_max"); + lua_pushboolean(L, i.usable); + lua_setfield(L, -2, "usable"); + lua_pushboolean(L, i.liquids_pointable); + lua_setfield(L, -2, "liquids_pointable"); + if (i.type == ITEM_TOOL) { + push_tool_capabilities(L, ToolCapabilities( + i.tool_capabilities->full_punch_interval, + i.tool_capabilities->max_drop_level, + i.tool_capabilities->groupcaps, + i.tool_capabilities->damageGroups)); + lua_setfield(L, -2, "tool_capabilities"); + } + push_groups(L, i.groups); + lua_setfield(L, -2, "groups"); + push_soundspec(L, i.sound_place); + lua_setfield(L, -2, "sound_place"); + push_soundspec(L, i.sound_place_failed); + lua_setfield(L, -2, "sound_place_failed"); + lua_pushstring(L, i.node_placement_prediction.c_str()); + lua_setfield(L, -2, "node_placement_prediction"); +} + /******************************************************************************/ void read_object_properties(lua_State *L, int index, ObjectProperties *prop, IItemDefManager *idef) @@ -669,6 +715,204 @@ ContentFeatures read_content_features(lua_State *L, int index) return f; } +void push_content_features(lua_State *L, const ContentFeatures &c) +{ + std::string paramtype(ScriptApiNode::es_ContentParamType[(int)c.param_type].str); + std::string paramtype2(ScriptApiNode::es_ContentParamType2[(int)c.param_type_2].str); + std::string drawtype(ScriptApiNode::es_DrawType[(int)c.drawtype].str); + std::string liquid_type(ScriptApiNode::es_LiquidType[(int)c.liquid_type].str); + + /* Missing "tiles" because I don't see a usecase (at least not yet). */ + + lua_newtable(L); + lua_pushboolean(L, c.has_on_construct); + lua_setfield(L, -2, "has_on_construct"); + lua_pushboolean(L, c.has_on_destruct); + lua_setfield(L, -2, "has_on_destruct"); + lua_pushboolean(L, c.has_after_destruct); + lua_setfield(L, -2, "has_after_destruct"); + lua_pushstring(L, c.name.c_str()); + lua_setfield(L, -2, "name"); + push_groups(L, c.groups); + lua_setfield(L, -2, "groups"); + lua_pushstring(L, paramtype.c_str()); + lua_setfield(L, -2, "paramtype"); + lua_pushstring(L, paramtype2.c_str()); + lua_setfield(L, -2, "paramtype2"); + lua_pushstring(L, drawtype.c_str()); + lua_setfield(L, -2, "drawtype"); + if (!c.mesh.empty()) { + lua_pushstring(L, c.mesh.c_str()); + lua_setfield(L, -2, "mesh"); + } +#ifndef SERVER + push_ARGB8(L, c.minimap_color); // I know this is not set-able w/ register_node, + lua_setfield(L, -2, "minimap_color"); // but the people need to know! +#endif + lua_pushnumber(L, c.visual_scale); + lua_setfield(L, -2, "visual_scale"); + lua_pushnumber(L, c.alpha); + lua_setfield(L, -2, "alpha"); + if (!c.palette_name.empty()) { + push_ARGB8(L, c.color); + lua_setfield(L, -2, "color"); + + lua_pushstring(L, c.palette_name.c_str()); + lua_setfield(L, -2, "palette_name"); + + push_palette(L, c.palette); + lua_setfield(L, -2, "palette"); + } + lua_pushnumber(L, c.waving); + lua_setfield(L, -2, "waving"); + lua_pushnumber(L, c.connect_sides); + lua_setfield(L, -2, "connect_sides"); + + lua_newtable(L); + u16 i = 1; + for (std::vector::const_iterator it = c.connects_to.begin(); + it != c.connects_to.end(); ++it) { + lua_pushlstring(L, it->c_str(), it->size()); + lua_rawseti(L, -2, i); + } + lua_setfield(L, -2, "connects_to"); + + push_ARGB8(L, c.post_effect_color); + lua_setfield(L, -2, "post_effect_color"); + lua_pushnumber(L, c.leveled); + lua_setfield(L, -2, "leveled"); + lua_pushboolean(L, c.sunlight_propagates); + lua_setfield(L, -2, "sunlight_propagates"); + lua_pushnumber(L, c.light_source); + lua_setfield(L, -2, "light_source"); + lua_pushboolean(L, c.is_ground_content); + lua_setfield(L, -2, "is_ground_content"); + lua_pushboolean(L, c.walkable); + lua_setfield(L, -2, "walkable"); + lua_pushboolean(L, c.pointable); + lua_setfield(L, -2, "pointable"); + lua_pushboolean(L, c.diggable); + lua_setfield(L, -2, "diggable"); + lua_pushboolean(L, c.climbable); + lua_setfield(L, -2, "climbable"); + lua_pushboolean(L, c.buildable_to); + lua_setfield(L, -2, "buildable_to"); + lua_pushboolean(L, c.rightclickable); + lua_setfield(L, -2, "rightclickable"); + lua_pushnumber(L, c.damage_per_second); + lua_setfield(L, -2, "damage_per_second"); + if (c.isLiquid()) { + lua_pushstring(L, liquid_type.c_str()); + lua_setfield(L, -2, "liquid_type"); + lua_pushstring(L, c.liquid_alternative_flowing.c_str()); + lua_setfield(L, -2, "liquid_alternative_flowing"); + lua_pushstring(L, c.liquid_alternative_source.c_str()); + lua_setfield(L, -2, "liquid_alternative_source"); + lua_pushnumber(L, c.liquid_viscosity); + lua_setfield(L, -2, "liquid_viscosity"); + lua_pushboolean(L, c.liquid_renewable); + lua_setfield(L, -2, "liquid_renewable"); + lua_pushnumber(L, c.liquid_range); + lua_setfield(L, -2, "liquid_range"); + } + lua_pushnumber(L, c.drowning); + lua_setfield(L, -2, "drowning"); + lua_pushboolean(L, c.floodable); + lua_setfield(L, -2, "floodable"); + push_nodebox(L, c.node_box); + lua_setfield(L, -2, "node_box"); + push_nodebox(L, c.selection_box); + lua_setfield(L, -2, "selection_box"); + push_nodebox(L, c.collision_box); + lua_setfield(L, -2, "collision_box"); + lua_newtable(L); + push_soundspec(L, c.sound_footstep); + lua_setfield(L, -2, "sound_footstep"); + push_soundspec(L, c.sound_dig); + lua_setfield(L, -2, "sound_dig"); + push_soundspec(L, c.sound_dug); + lua_setfield(L, -2, "sound_dug"); + lua_setfield(L, -2, "sounds"); + lua_pushboolean(L, c.legacy_facedir_simple); + lua_setfield(L, -2, "legacy_facedir_simple"); + lua_pushboolean(L, c.legacy_wallmounted); + lua_setfield(L, -2, "legacy_wallmounted"); +} + +/******************************************************************************/ +void push_nodebox(lua_State *L, const NodeBox &box) +{ + lua_newtable(L); + switch (box.type) + { + case NODEBOX_REGULAR: + lua_pushstring(L, "regular"); + lua_setfield(L, -2, "type"); + break; + case NODEBOX_LEVELED: + case NODEBOX_FIXED: + lua_pushstring(L, "fixed"); + lua_setfield(L, -2, "type"); + push_box(L, box.fixed); + lua_setfield(L, -2, "fixed"); + break; + case NODEBOX_WALLMOUNTED: + lua_pushstring(L, "wallmounted"); + lua_setfield(L, -2, "type"); + push_aabb3f(L, box.wall_top); + lua_setfield(L, -2, "wall_top"); + push_aabb3f(L, box.wall_bottom); + lua_setfield(L, -2, "wall_bottom"); + push_aabb3f(L, box.wall_side); + lua_setfield(L, -2, "wall_side"); + break; + case NODEBOX_CONNECTED: + lua_pushstring(L, "connected"); + lua_setfield(L, -2, "type"); + push_box(L, box.connect_top); + lua_setfield(L, -2, "connect_top"); + push_box(L, box.connect_bottom); + lua_setfield(L, -2, "connect_bottom"); + push_box(L, box.connect_front); + lua_setfield(L, -2, "connect_front"); + push_box(L, box.connect_back); + lua_setfield(L, -2, "connect_back"); + push_box(L, box.connect_left); + lua_setfield(L, -2, "connect_left"); + push_box(L, box.connect_right); + lua_setfield(L, -2, "connect_right"); + break; + default: + FATAL_ERROR("Invalid box.type"); + break; + } +} + +void push_box(lua_State *L, const std::vector &box) +{ + lua_newtable(L); + u8 i = 1; + for (std::vector::const_iterator it = box.begin(); + it != box.end(); ++it) { + push_aabb3f(L, (*it)); + lua_rawseti(L, -2, i); + } +} + +/******************************************************************************/ +void push_palette(lua_State *L, const std::vector *palette) +{ + lua_createtable(L, palette->size(), 0); + int newTable = lua_gettop(L); + int index = 1; + std::vector::const_iterator iter; + for (iter = palette->begin(); iter != palette->end(); ++iter) { + push_ARGB8(L, (*iter)); + lua_rawseti(L, newTable, index); + index++; + } +} + /******************************************************************************/ void read_server_sound_params(lua_State *L, int index, ServerSoundParams ¶ms) @@ -719,6 +963,17 @@ void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec) } } +void push_soundspec(lua_State *L, const SimpleSoundSpec &spec) +{ + lua_newtable(L); + lua_pushstring(L, spec.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushnumber(L, spec.gain); + lua_setfield(L, -2, "gain"); + lua_pushnumber(L, spec.fade); + lua_setfield(L, -2, "fade"); +} + /******************************************************************************/ NodeBox read_nodebox(lua_State *L, int index) { diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 28d8b1e8c..9b8796297 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -39,6 +39,7 @@ extern "C" { #include "util/string.h" #include "itemgroup.h" #include "itemdef.h" +#include "c_types.h" namespace Json { class Value; } @@ -64,8 +65,20 @@ class Schematic; ContentFeatures read_content_features (lua_State *L, int index); +void push_content_features (lua_State *L, + const ContentFeatures &c); + +void push_nodebox (lua_State *L, + const NodeBox &box); +void push_box (lua_State *L, + const std::vector &box); + +void push_palette (lua_State *L, + const std::vector *palette); + TileDef read_tiledef (lua_State *L, int index, u8 drawtype); + void read_soundspec (lua_State *L, int index, SimpleSoundSpec &spec); NodeBox read_nodebox (lua_State *L, int index); @@ -90,6 +103,9 @@ void read_item_definition (lua_State *L, int index, const ItemDefinition &defaul ItemDefinition &def); void push_item_definition (lua_State *L, const ItemDefinition &i); +void push_item_definition_full (lua_State *L, + const ItemDefinition &i); + void read_object_properties (lua_State *L, int index, ObjectProperties *prop, IItemDefManager *idef); @@ -147,6 +163,8 @@ std::vector read_items (lua_State *L, void read_soundspec (lua_State *L, int index, SimpleSoundSpec &spec); +void push_soundspec (lua_State *L, + const SimpleSoundSpec &spec); bool string_to_enum (const EnumString *spec, int &result, diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index 22045e06d..87cfa00fe 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mainmenumanager.h" #include "map.h" #include "util/string.h" +#include "nodedef.h" extern MainGameCallback *g_gamecallback; @@ -260,6 +261,50 @@ int ModApiClient::l_get_server_info(lua_State *L) return 1; } +// get_item_def(itemstring) +int ModApiClient::l_get_item_def(lua_State *L) +{ + IGameDef *gdef = getGameDef(L); + assert(gdef); + + IItemDefManager *idef = gdef->idef(); + assert(idef); + + if (!lua_isstring(L, 1)) + return 0; + + const std::string &name(lua_tostring(L, 1)); + if (!idef->isKnown(name)) + return 0; + const ItemDefinition &def = idef->get(name); + + push_item_definition_full(L, def); + + return 1; +} + +// get_node_def(nodename) +int ModApiClient::l_get_node_def(lua_State *L) +{ + IGameDef *gdef = getGameDef(L); + assert(gdef); + + INodeDefManager *ndef = gdef->ndef(); + assert(ndef); + + if (!lua_isstring(L, 1)) + return 0; + + const std::string &name = lua_tostring(L, 1); + const ContentFeatures &cf = ndef->get(ndef->getId(name)); + if (cf.name != name) // Unknown node. | name = , cf.name = ignore + return 0; + + push_content_features(L, cf); + + return 1; +} + int ModApiClient::l_take_screenshot(lua_State *L) { Client *client = getClient(L); @@ -286,5 +331,7 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(sound_play); API_FCT(sound_stop); API_FCT(get_server_info); + API_FCT(get_item_def); + API_FCT(get_node_def); API_FCT(take_screenshot); } diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index 2267a4c9d..9813a5604 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define L_CLIENT_H_ #include "lua_api/l_base.h" +#include "itemdef.h" +#include "tool.h" class ModApiClient : public ModApiBase { @@ -38,7 +40,7 @@ private: // get_player_names() static int l_get_player_names(lua_State *L); - // show_formspec(name, fornspec) + // show_formspec(name, formspec) static int l_show_formspec(lua_State *L); // send_respawn() @@ -74,6 +76,12 @@ private: // get_server_info() static int l_get_server_info(lua_State *L); + + // get_item_def(itemstring) + static int l_get_item_def(lua_State *L); + + // get_node_def(nodename) + static int l_get_node_def(lua_State *L); static int l_take_screenshot(lua_State *L); -- cgit v1.2.3