From fd1135c7af46eb2f5b99a11f48bf9f9ae335ea9c Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 16 Jun 2012 03:40:45 +0300 Subject: [PATCH] Node texture animation --- doc/lua_api.txt | 22 ++- games/minimal/mods/default/init.lua | 17 +- .../default_lava_flowing_animated.png | Bin 0 -> 8715 bytes .../textures/default_lava_source_animated.png | Bin 0 -> 3481 bytes src/clientserver.h | 2 + src/content_mapblock.cpp | 50 +++++- src/game.cpp | 2 +- src/itemdef.cpp | 2 +- src/mapblock_mesh.cpp | 54 ++++++- src/mapblock_mesh.h | 6 + src/nodedef.cpp | 147 ++++++++++++++---- src/nodedef.h | 38 +++-- src/scriptapi.cpp | 96 +++++++++--- src/test.cpp | 8 +- src/tile.cpp | 46 +++++- src/tile.h | 13 +- 16 files changed, 421 insertions(+), 82 deletions(-) create mode 100644 games/minimal/mods/default/textures/default_lava_flowing_animated.png create mode 100644 games/minimal/mods/default/textures/default_lava_source_animated.png diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 4dec9b39..6f2d0e03 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1124,17 +1124,29 @@ Item definition (register_node, register_craftitem, register_tool) ^ The default functions handle regular use cases. } +Tile definition: +- "image.png" +- {name="image.png", animation={Tile Animation definition}} +- {name="image.png", backface_culling=bool} + ^ backface culling only supported in special tiles +- deprecated still supported field names: + - image -> name + +Tile animation definition: +- {type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0} + Node definition (register_node) { , drawtype = "normal", visual_scale = 1.0, - tile_images = {"default_unknown_block.png"}, - special_materials = { - {image="", backface_culling=true}, - {image="", backface_culling=true}, - }, + tiles = {tile definition 1, def2, def3, def4, def5, def6}, + ^ List can be shortened to needed length + ^ Old field name: tile_images + special_tiles = {tile definition 1, Tile definition 2}, + ^ List can be shortened to needed length + ^ Old field name: special_materials alpha = 255, post_effect_color = {a=0, r=0, g=0, b=0}, paramtype = "none", diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index 1ffe2483..03a54794 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1040,8 +1040,16 @@ minetest.register_node("default:lava_flowing", { damage_per_second = 4*2, post_effect_color = {a=192, r=255, g=64, b=0}, special_materials = { - {image="default_lava.png", backface_culling=false}, - {image="default_lava.png", backface_culling=true}, + { + image="default_lava_flowing_animated.png", + backface_culling=false, + animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.3} + }, + { + image="default_lava_flowing_animated.png", + backface_culling=true, + animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.3} + }, }, groups = {lava=3, liquid=2, hot=3}, }) @@ -1050,7 +1058,10 @@ minetest.register_node("default:lava_source", { description = "Lava", inventory_image = minetest.inventorycube("default_lava.png"), drawtype = "liquid", - tile_images = {"default_lava.png"}, + --tile_images = {"default_lava.png"}, + tile_images = { + {name="default_lava_source_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}} + }, paramtype = "light", light_source = LIGHT_MAX - 1, walkable = false, diff --git a/games/minimal/mods/default/textures/default_lava_flowing_animated.png b/games/minimal/mods/default/textures/default_lava_flowing_animated.png new file mode 100644 index 0000000000000000000000000000000000000000..cef8e36113597ea1b2427c6709db33322d4cf406 GIT binary patch literal 8715 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
XkA=Nl4togvEtc#1_&v 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 Hu0mjfFEda< literal 0 HcmV?d00001 diff --git a/src/clientserver.h b/src/clientserver.h index 7b8fe8e3..86e929f6 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -54,6 +54,8 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL_VERSION 10: TOCLIENT_PRIVILEGES Version raised to force 'fly' and 'fast' privileges into effect. + Node metadata change (came in later; somewhat incompatible) + TileDef in ContentFeatures (non-TileDef deserialization is supported) */ #define PROTOCOL_VERSION 10 diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 7585abb6..9d95e175 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -471,7 +471,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data, pa_liquid.x0(), pa_liquid.y0()), }; - // This fixes a strange bug + // To get backface culling right, the vertices need to go + // clockwise around the front of the face. And we happened to + // calculate corner levels in exact reverse order. s32 corner_resolve[4] = {3,2,1,0}; for(s32 i=0; i<4; i++) @@ -482,6 +484,52 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos.Y += corner_levels[j]; vertices[i].Pos += intToFloat(p, BS); } + + // Default downwards-flowing texture animation goes from + // -Z towards +Z, thus the direction is +Z. + // Rotate texture to make animation go in flow direction + // Positive if liquid moves towards +Z + int dz = (corner_levels[side_corners[2][0]] + + corner_levels[side_corners[2][1]] < + corner_levels[side_corners[3][0]] + + corner_levels[side_corners[3][1]]); + // Positive if liquid moves towards +X + int dx = (corner_levels[side_corners[0][0]] + + corner_levels[side_corners[0][1]] < + corner_levels[side_corners[1][0]] + + corner_levels[side_corners[1][1]]); + // -X + if(-dx >= abs(dz)) + { + v2f t = vertices[0].TCoords; + vertices[0].TCoords = vertices[1].TCoords; + vertices[1].TCoords = vertices[2].TCoords; + vertices[2].TCoords = vertices[3].TCoords; + vertices[3].TCoords = t; + } + // +X + if(dx >= abs(dz)) + { + v2f t = vertices[0].TCoords; + vertices[0].TCoords = vertices[3].TCoords; + vertices[3].TCoords = vertices[2].TCoords; + vertices[2].TCoords = vertices[1].TCoords; + vertices[1].TCoords = t; + } + // -Z + if(-dz >= abs(dx)) + { + v2f t = vertices[0].TCoords; + vertices[0].TCoords = vertices[3].TCoords; + vertices[3].TCoords = vertices[2].TCoords; + vertices[2].TCoords = vertices[1].TCoords; + vertices[1].TCoords = t; + t = vertices[0].TCoords; + vertices[0].TCoords = vertices[3].TCoords; + vertices[3].TCoords = vertices[2].TCoords; + vertices[2].TCoords = vertices[1].TCoords; + vertices[1].TCoords = t; + } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector diff --git a/src/game.cpp b/src/game.cpp index 6ccf0267..ac6d13af 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2206,7 +2206,7 @@ void the_game( infotext = narrow_to_wide(meta->getString("infotext")); } else { MapNode n = map.getNode(nodepos); - if(nodedef->get(n).tname_tiles[0] == "unknown_block.png"){ + if(nodedef->get(n).tiledef[0].name == "unknown_block.png"){ infotext = L"Unknown node: "; infotext += narrow_to_wide(nodedef->get(n).name); } diff --git a/src/itemdef.cpp b/src/itemdef.cpp index e8de0638..c8771c30 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -451,7 +451,7 @@ public: if(def->inventory_texture == NULL) { def->inventory_texture = - tsrc->getTextureRaw(f.tname_tiles[0]); + tsrc->getTextureRaw(f.tiledef[0].name); } } diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 5584216b..fd5937bb 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "mesh.h" #include "content_mapblock.h" +#include "noise.h" /* MeshMakeData @@ -559,6 +560,11 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) spec.material_flags |= MATERIAL_FLAG_CRACK; spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture); } + // If animated, replace tile texture with one without texture atlas + if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) + { + spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture); + } return spec; } @@ -983,6 +989,23 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data): crack_basename += "^[crack"; m_crack_materials.insert(std::make_pair(i, crack_basename)); } + // - Texture animation + if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) + { + ITextureSource *tsrc = data->m_gamedef->tsrc(); + // Add to MapBlockMesh in order to animate these tiles + m_animation_tiles[i] = p.tile; + m_animation_frames[i] = 0; + // 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)); + // Replace tile texture with the first animation frame + std::ostringstream os(std::ios::binary); + os<getTextureName(p.tile.texture.id); + os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0"; + p.tile.texture = tsrc->getTexture(os.str()); + } // - Lighting for(u32 j = 0; j < p.vertices.size(); j++) { @@ -1055,7 +1078,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data): // Check if animation is required for this mesh m_has_animation = !m_crack_materials.empty() || - !m_daynight_diffs.empty(); + !m_daynight_diffs.empty() || + !m_animation_tiles.empty(); } MapBlockMesh::~MapBlockMesh() @@ -1094,6 +1118,34 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat m_last_crack = crack; } + + // Texture animation + for(std::map::iterator + i = m_animation_tiles.begin(); + i != m_animation_tiles.end(); i++) + { + const TileSpec &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 + + frameoffset) % tile.animation_frame_count; + // If frame doesn't change, skip + if(frame == m_animation_frames[i->first]) + continue; + + m_animation_frames[i->first] = frame; + + scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); + ITextureSource *tsrc = m_gamedef->getTextureSource(); + + // Create new texture name from original + std::ostringstream os(std::ios::binary); + os<getTextureName(tile.texture.id); + os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<getTexture(os.str()); + buf->getMaterial().setTexture(0, ap.atlas); + } // Day-night transition if(daynight_ratio != m_last_daynight_ratio) diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 0877677b..d5733120 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -122,6 +122,12 @@ private: // Maps mesh buffer (i.e. material) indices to base texture names std::map m_crack_materials; + // Animation info: texture animationi + // Maps meshbuffers to TileSpecs + std::map m_animation_tiles; + std::map m_animation_frames; // last animation frame + std::map m_animation_frame_offsets; + // Animation info: day/night transitions // Last daynight_ratio value passed to animate() u32 m_last_daynight_ratio; diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 72f1ea2e..d0ce3c19 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -63,19 +63,29 @@ void NodeBox::deSerialize(std::istream &is) } /* - MaterialSpec + TileDef */ - -void MaterialSpec::serialize(std::ostream &os) const + +void TileDef::serialize(std::ostream &os) const { - os<tname_tiles[j]; - if(tname_tiles[j] == "") - tname_tiles[j] = "unknown_block.png"; - } + tiledef[j] = f->tiledef[j]; + if(tiledef[j].name == "") + tiledef[j].name = "unknown_block.png"; + } switch(f->drawtype){ default: @@ -547,7 +567,7 @@ public: f->drawtype = NDT_NORMAL; f->solidness = 2; for(u32 i=0; i<6; i++){ - tname_tiles[i] += std::string("^[noalpha"); + tiledef[i].name += std::string("^[noalpha"); } } break; @@ -560,29 +580,92 @@ public: break; } - // Tile textures + // Tiles (fill in f->tiles[]) for(u16 j=0; j<6; j++){ - f->tiles[j].texture = tsrc->getTexture(tname_tiles[j]); + // Texture + f->tiles[j].texture = tsrc->getTexture(tiledef[j].name); + // Alpha f->tiles[j].alpha = f->alpha; if(f->alpha == 255) f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE; else f->tiles[j].material_type = MATERIAL_ALPHA_VERTEX; + // Material flags f->tiles[j].material_flags = 0; if(f->backface_culling) f->tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; + if(tiledef[j].animation.type == TAT_VERTICAL_FRAMES) + f->tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + // Animation parameters + if(f->tiles[j].material_flags & + MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) + { + // Get raw texture size to determine frame count by + // aspect ratio + video::ITexture *t = tsrc->getTextureRaw(tiledef[j].name); + v2u32 size = t->getOriginalSize(); + int frame_height = (float)size.X / + (float)tiledef[j].animation.aspect_w * + (float)tiledef[j].animation.aspect_h; + int frame_count = size.Y / frame_height; + int frame_length_ms = 1000.0 * + tiledef[j].animation.length / frame_count; + f->tiles[j].animation_frame_count = frame_count; + f->tiles[j].animation_frame_length_ms = frame_length_ms; + + // If there are no frames for an animation, switch + // animation off (so that having specified an animation + // for something but not using it in the texture pack + // gives no overhead) + if(frame_count == 1){ + f->tiles[j].material_flags &= + ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + } + } } - // Special tiles + // Special tiles (fill in f->special_tiles[]) for(u16 j=0; jspecial_tiles[j].texture = tsrc->getTexture(f->mspec_special[j].tname); + // Texture + f->special_tiles[j].texture = + tsrc->getTexture(f->tiledef_special[j].name); + // Alpha f->special_tiles[j].alpha = f->alpha; if(f->alpha == 255) f->special_tiles[j].material_type = MATERIAL_ALPHA_SIMPLE; else f->special_tiles[j].material_type = MATERIAL_ALPHA_VERTEX; + // Material flags f->special_tiles[j].material_flags = 0; - if(f->mspec_special[j].backface_culling) + if(f->tiledef_special[j].backface_culling) f->special_tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; + if(f->tiledef_special[j].animation.type == TAT_VERTICAL_FRAMES) + f->special_tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + // Animation parameters + if(f->special_tiles[j].material_flags & + MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) + { + // Get raw texture size to determine frame count by + // aspect ratio + video::ITexture *t = tsrc->getTextureRaw(f->tiledef_special[j].name); + v2u32 size = t->getOriginalSize(); + int frame_height = (float)size.X / + (float)f->tiledef_special[j].animation.aspect_w * + (float)f->tiledef_special[j].animation.aspect_h; + int frame_count = size.Y / frame_height; + int frame_length_ms = 1000.0 * + f->tiledef_special[j].animation.length / frame_count; + f->special_tiles[j].animation_frame_count = frame_count; + f->special_tiles[j].animation_frame_length_ms = frame_length_ms; + + // If there are no frames for an animation, switch + // animation off (so that having specified an animation + // for something but not using it in the texture pack + // gives no overhead) + if(frame_count == 1){ + f->special_tiles[j].material_flags &= + ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + } + } } } #endif diff --git a/src/nodedef.h b/src/nodedef.h index 04f37504..a376da30 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -95,15 +95,33 @@ struct NodeBox struct MapNode; class NodeMetadata; -struct MaterialSpec +/* + Stand-alone definition of a TileSpec (basically a server-side TileSpec) +*/ +enum TileAnimationType{ + TAT_NONE=0, + TAT_VERTICAL_FRAMES=1, +}; +struct TileDef { - std::string tname; - bool backface_culling; - - MaterialSpec(const std::string &tname_="", bool backface_culling_=true): - tname(tname_), - backface_culling(backface_culling_) - {} + std::string name; + bool backface_culling; // Takes effect only in special cases + struct{ + enum TileAnimationType type; + int aspect_w; // width for aspect ratio + int aspect_h; // height for aspect ratio + float length; // seconds + } animation; + + TileDef() + { + name = ""; + backface_culling = true; + animation.type = TAT_NONE; + animation.aspect_w = 1; + animation.aspect_h = 1; + animation.length = 1.0; + } void serialize(std::ostream &os) const; void deSerialize(std::istream &is); @@ -159,8 +177,8 @@ struct ContentFeatures // Visual definition enum NodeDrawType drawtype; float visual_scale; // Misc. scale parameter - std::string tname_tiles[6]; - MaterialSpec mspec_special[CF_SPECIAL_COUNT]; // Use setter methods + TileDef tiledef[6]; + TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid u8 alpha; // Post effect color, drawn when the camera is inside the node. diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index afd8546d..3868d103 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -426,6 +426,13 @@ struct EnumString es_CraftMethod[] = {0, NULL}, }; +struct EnumString es_TileAnimationType[] = +{ + {TAT_NONE, "none"}, + {TAT_VERTICAL_FRAMES, "vertical_frames"}, + {0, NULL}, +}; + /* C struct <-> Lua table converter functions */ @@ -991,6 +998,50 @@ static ItemDefinition read_item_definition(lua_State *L, int index, return def; } +/* + TileDef +*/ + +static TileDef read_tiledef(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + TileDef tiledef; + + // key at index -2 and value at index + if(lua_isstring(L, index)){ + // "default_lava.png" + tiledef.name = lua_tostring(L, index); + } + else if(lua_istable(L, index)) + { + // {name="default_lava.png", animation={}} + tiledef.name = ""; + getstringfield(L, index, "name", tiledef.name); + getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. + tiledef.backface_culling = getboolfield_default( + L, index, "backface_culling", true); + // 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.aspect_w = + getintfield_default(L, -1, "aspect_w", 16); + tiledef.animation.aspect_h = + getintfield_default(L, -1, "aspect_h", 16); + tiledef.animation.length = + getfloatfield_default(L, -1, "length", 1.0); + } + lua_pop(L, 1); + } + + return tiledef; +} + /* ContentFeatures */ @@ -1026,18 +1077,23 @@ static ContentFeatures read_content_features(lua_State *L, int index) f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType, NDT_NORMAL); getfloatfield(L, index, "visual_scale", f.visual_scale); - - lua_getfield(L, index, "tile_images"); + + // tiles = {} + lua_getfield(L, index, "tiles"); + // If nil, try the deprecated name "tile_images" instead + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + warn_if_field_exists(L, index, "tile_images", + "Deprecated; new name is \"tiles\"."); + lua_getfield(L, index, "tile_images"); + } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - if(lua_isstring(L, -1)) - f.tname_tiles[i] = lua_tostring(L, -1); - else - f.tname_tiles[i] = ""; + // Read tiledef from value + f.tiledef[i] = read_tiledef(L, -1); // removes value, keeps key for next iteration lua_pop(L, 1); i++; @@ -1048,29 +1104,31 @@ static ContentFeatures read_content_features(lua_State *L, int index) } // Copy last value to all remaining textures if(i >= 1){ - std::string lastname = f.tname_tiles[i-1]; + TileDef lasttile = f.tiledef[i-1]; while(i < 6){ - f.tname_tiles[i] = lastname; + f.tiledef[i] = lasttile; i++; } } } lua_pop(L, 1); - - lua_getfield(L, index, "special_materials"); + + // special_tiles = {} + lua_getfield(L, index, "special_tiles"); + // If nil, try the deprecated name "special_materials" instead + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + warn_if_field_exists(L, index, "special_materials", + "Deprecated; new name is \"special_tiles\"."); + lua_getfield(L, index, "special_materials"); + } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - int smtable = lua_gettop(L); - std::string tname = getstringfield_default( - L, smtable, "image", ""); - bool backface_culling = getboolfield_default( - L, smtable, "backface_culling", true); - MaterialSpec mspec(tname, backface_culling); - f.mspec_special[i] = mspec; + // Read tiledef from value + f.tiledef_special[i] = read_tiledef(L, -1); // removes value, keeps key for next iteration lua_pop(L, 1); i++; diff --git a/src/test.cpp b/src/test.cpp index 5b969017..448ba23c 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -80,7 +80,7 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n f = ContentFeatures(); f.name = itemdef.name; for(int i = 0; i < 6; i++) - f.tname_tiles[i] = "default_stone.png"; + f.tiledef[i].name = "default_stone.png"; f.is_ground_content = true; idef->registerItem(itemdef); ndef->set(i, f); @@ -100,10 +100,10 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n "{default_dirt.png&default_grass_side.png"; f = ContentFeatures(); f.name = itemdef.name; - f.tname_tiles[0] = "default_grass.png"; - f.tname_tiles[1] = "default_dirt.png"; + f.tiledef[0].name = "default_grass.png"; + f.tiledef[1].name = "default_dirt.png"; for(int i = 2; i < 6; i++) - f.tname_tiles[i] = "default_dirt.png^default_grass_side.png"; + f.tiledef[i].name = "default_dirt.png^default_grass_side.png"; f.is_ground_content = true; idef->registerItem(itemdef); ndef->set(i, f); diff --git a/src/tile.cpp b/src/tile.cpp index d2a61b93..92c56c27 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -858,7 +858,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) const ContentFeatures &f = ndef->get(j); for(u32 i=0; i<6; i++) { - std::string name = f.tname_tiles[i]; + std::string name = f.tiledef[i].name; sourcelist[name] = true; } } @@ -988,7 +988,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) src_x = pos_in_atlas.X; } s32 y = y0 + pos_in_atlas.Y; - s32 src_y = MYMAX(pos_in_atlas.Y, MYMIN(pos_in_atlas.Y + dim.Height - 1, y)); + s32 src_y = MYMAX((int)pos_in_atlas.Y, MYMIN((int)pos_in_atlas.Y + (int)dim.Height - 1, y)); s32 dst_y = y; video::SColor c = atlas_img->getPixel(src_x, src_y); atlas_img->setPixel(dst_x,dst_y,c); @@ -1638,6 +1638,48 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, img2->drop(); } } + /* + [verticalframe:N:I + Crops a frame of a vertical animation. + N = frame count, I = frame index + */ + else if(part_of_name.substr(0,15) == "[verticalframe:") + { + Strfnd sf(part_of_name); + sf.next(":"); + u32 frame_count = stoi(sf.next(":")); + u32 frame_index = stoi(sf.next(":")); + + if(baseimg == NULL){ + errorstream<<"generate_image(): baseimg!=NULL " + <<"for part_of_name=\""<getDimension(); + frame_size.Y /= frame_count; + + video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, + frame_size); + if(!img){ + errorstream<<"generate_image(): Could not create image " + <<"for part_of_name=\""< dim = frame_size; + core::position2d pos_dst(0, 0); + core::position2d pos_src(0, frame_index * frame_size.Y); + baseimg->copyToWithAlpha(img, pos_dst, + core::rect(pos_src, dim), + video::SColor(255,255,255,255), + NULL); + // Replace baseimg + baseimg->drop(); + baseimg = img; + } else { errorstream<<"generate_image(): Invalid " diff --git a/src/tile.h b/src/tile.h index 1211569b..8b19b4c3 100644 --- a/src/tile.h +++ b/src/tile.h @@ -162,6 +162,9 @@ enum MaterialType{ // Should the crack be drawn on transparent pixels (unset) or not (set)? // Ignored if MATERIAL_FLAG_CRACK is not set. #define MATERIAL_FLAG_CRACK_OVERLAY 0x04 +// Animation made up by splitting the texture to vertical frames, as +// defined by extra parameters +#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08 /* This fully defines the looks of a tile. @@ -178,7 +181,9 @@ struct TileSpec material_flags( //0 // <- DEBUG, Use the one below MATERIAL_FLAG_BACKFACE_CULLING - ) + ), + animation_frame_count(1), + animation_frame_length_ms(0) { } @@ -227,10 +232,12 @@ struct TileSpec AtlasPointer texture; // Vertex alpha u8 alpha; - // Material type + // Material parameters u8 material_type; - // Material flags u8 material_flags; + // Animation parameters + u8 animation_frame_count; + u16 animation_frame_length_ms; }; #endif