Node texture animation
authorPerttu Ahola <celeron55@gmail.com>
Sat, 16 Jun 2012 00:40:45 +0000 (03:40 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 16 Jun 2012 13:47:28 +0000 (16:47 +0300)
16 files changed:
doc/lua_api.txt
games/minimal/mods/default/init.lua
games/minimal/mods/default/textures/default_lava_flowing_animated.png [new file with mode: 0644]
games/minimal/mods/default/textures/default_lava_source_animated.png [new file with mode: 0644]
src/clientserver.h
src/content_mapblock.cpp
src/game.cpp
src/itemdef.cpp
src/mapblock_mesh.cpp
src/mapblock_mesh.h
src/nodedef.cpp
src/nodedef.h
src/scriptapi.cpp
src/test.cpp
src/tile.cpp
src/tile.h

index 4dec9b3948ee2abefa2924e0bad29be8a73f9f9c..6f2d0e033f8be8fd9766e238522c6ecc277df5e5 100644 (file)
@@ -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)
 {
     <all fields allowed in item definitions>,
 
     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",
index 1ffe24837442d738ec6b69f2b80c117ab0dd67b9..03a54794c2d199761141a1cf73ca3032c8b780d1 100644 (file)
@@ -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 (file)
index 0000000..cef8e36
Binary files /dev/null and b/games/minimal/mods/default/textures/default_lava_flowing_animated.png differ
diff --git a/games/minimal/mods/default/textures/default_lava_source_animated.png b/games/minimal/mods/default/textures/default_lava_source_animated.png
new file mode 100644 (file)
index 0000000..9339a68
Binary files /dev/null and b/games/minimal/mods/default/textures/default_lava_source_animated.png differ
index 7b8fe8e39b077795afd957351074473bb0e901aa..86e929f617c72f3296f0d59280232600ac3b8d71 100644 (file)
@@ -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
index 7585abb691eceb74e49ece3ffe6f650523438f4a..9d95e1758e9fd33220e1da9bd277a1a443bde5f7 100644 (file)
@@ -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
index 6ccf02677cc1bee5a93d85b339792b654d554779..ac6d13af106677b4601cfc5bc6793cfb6ba426e5 100644 (file)
@@ -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);
                                }
index e8de063876e58499c4f895a0be0ac1eb0ad23cdc..c8771c30c9f2fc85034ab4fec49c621b5d5f29bd 100644 (file)
@@ -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);
                                        }
                                }
 
index 5584216ba18afe9364113de7c192b0293bcd698c..fd5937bbe36c420da8c0633f9a94728c781c8ae3 100644 (file)
@@ -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<<tsrc->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<u32, TileSpec>::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<<tsrc->getTextureName(tile.texture.id);
+               os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
+               // Set the texture
+               AtlasPointer ap = tsrc->getTexture(os.str());
+               buf->getMaterial().setTexture(0, ap.atlas);
+       }
 
        // Day-night transition
        if(daynight_ratio != m_last_daynight_ratio)
index 0877677bc9c568bcb147035e6cb14307974ffbe8..d5733120b8b3cce4a6a4a9979e836a35991f2a22 100644 (file)
@@ -122,6 +122,12 @@ private:
        // Maps mesh buffer (i.e. material) indices to base texture names
        std::map<u32, std::string> m_crack_materials;
 
+       // Animation info: texture animationi
+       // Maps meshbuffers to TileSpecs
+       std::map<u32, TileSpec> m_animation_tiles;
+       std::map<u32, int> m_animation_frames; // last animation frame
+       std::map<u32, int> m_animation_frame_offsets;
+       
        // Animation info: day/night transitions
        // Last daynight_ratio value passed to animate()
        u32 m_last_daynight_ratio;
index 72f1ea2ea070b68352be809cf3ea200b393d3758..d0ce3c19d1fe7706f2a076d82c464100ff61a04f 100644 (file)
@@ -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<<serializeString(tname);
-       writeU8(os, backface_culling);
+       writeU8(os, 0); // version
+       os<<serializeString(name);
+       writeU8(os, animation.type);
+       writeU16(os, animation.aspect_w);
+       writeU16(os, animation.aspect_h);
+       writeF1000(os, animation.length);
 }
 
-void MaterialSpec::deSerialize(std::istream &is)
+void TileDef::deSerialize(std::istream &is)
 {
-       tname = deSerializeString(is);
-       backface_culling = readU8(is);
+       int version = readU8(is);
+       if(version != 0)
+               throw SerializationError("unsupported TileDef version");
+       name = deSerializeString(is);
+       animation.type = (TileAnimationType)readU8(is);
+       animation.aspect_w = readU16(is);
+       animation.aspect_h = readU16(is);
+       animation.length = readF1000(is);
 }
 
 /*
@@ -133,9 +143,9 @@ void ContentFeatures::reset()
        drawtype = NDT_NORMAL;
        visual_scale = 1.0;
        for(u32 i=0; i<6; i++)
-               tname_tiles[i] = "";
+               tiledef[i] = TileDef();
        for(u16 j=0; j<CF_SPECIAL_COUNT; j++)
-               mspec_special[j] = MaterialSpec();
+               tiledef_special[j] = TileDef();
        alpha = 255;
        post_effect_color = video::SColor(0, 0, 0, 0);
        param_type = CPT_NONE;
@@ -164,7 +174,7 @@ void ContentFeatures::reset()
 
 void ContentFeatures::serialize(std::ostream &os)
 {
-       writeU8(os, 3); // version
+       writeU8(os, 4); // version
        os<<serializeString(name);
        writeU16(os, groups.size());
        for(ItemGroupList::const_iterator
@@ -176,10 +186,10 @@ void ContentFeatures::serialize(std::ostream &os)
        writeF1000(os, visual_scale);
        writeU8(os, 6);
        for(u32 i=0; i<6; i++)
-               os<<serializeString(tname_tiles[i]);
+               tiledef[i].serialize(os);
        writeU8(os, CF_SPECIAL_COUNT);
        for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
-               mspec_special[i].serialize(os);
+               tiledef_special[i].serialize(os);
        }
        writeU8(os, alpha);
        writeU8(os, post_effect_color.getAlpha());
@@ -214,7 +224,7 @@ void ContentFeatures::serialize(std::ostream &os)
 void ContentFeatures::deSerialize(std::istream &is)
 {
        int version = readU8(is);
-       if(version != 3)
+       if(version != 4 && version != 3)
                throw SerializationError("unsupported ContentFeatures version");
        name = deSerializeString(is);
        groups.clear();
@@ -228,12 +238,21 @@ void ContentFeatures::deSerialize(std::istream &is)
        visual_scale = readF1000(is);
        if(readU8(is) != 6)
                throw SerializationError("unsupported tile count");
-       for(u32 i=0; i<6; i++)
-               tname_tiles[i] = deSerializeString(is);
+       for(u32 i=0; i<6; i++){
+               if(version == 4)
+                       tiledef[i].deSerialize(is);
+               else if(version == 3) // Allow connecting to older servers
+                       tiledef[i].name = deSerializeString(is);
+       }
        if(readU8(is) != CF_SPECIAL_COUNT)
                throw SerializationError("unsupported CF_SPECIAL_COUNT");
        for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
-               mspec_special[i].deSerialize(is);
+               if(version == 4){
+                       tiledef_special[i].deSerialize(is);
+               } else if(version == 3){ // Allow connecting to older servers
+                       tiledef_special[i].name = deSerializeString(is);
+                       tiledef_special[i].backface_culling = readU8(is);
+               }
        }
        alpha = readU8(is);
        post_effect_color.setAlpha(readU8(is));
@@ -260,12 +279,12 @@ void ContentFeatures::deSerialize(std::istream &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);
        // If you add anything here, insert it primarily inside the try-catch
        // block to not need to increase the version.
        try{
-               deSerializeSimpleSoundSpec(sound_footstep, is);
-               deSerializeSimpleSoundSpec(sound_dig, is);
-               deSerializeSimpleSoundSpec(sound_dug, is);
        }catch(SerializationError &e) {};
 }
 
@@ -494,14 +513,15 @@ public:
                for(u16 i=0; i<=MAX_CONTENT; i++)
                {
                        ContentFeatures *f = &m_content_features[i];
-
-                       std::string tname_tiles[6];
+                       
+                       // Figure out the actual tiles to use
+                       TileDef tiledef[6];
                        for(u32 j=0; j<6; j++)
                        {
-                               tname_tiles[j] = f->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; j<CF_SPECIAL_COUNT; j++){
-                               f->special_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
index 04f375043c745280c85a54cefb11b5dcef236b61..a376da30a7587f165a070d9b2f844824227fa89b 100644 (file)
@@ -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.
index afd8546d9a789be0cebabee3bb0bf4ed8174f629..3868d1035a9204ccc660ff266b50faf09f365ff1 100644 (file)
@@ -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++;
index 5b969017fb78cc8c1f24ecafb4f0c7bc9052ca8e..448ba23ceddd63ff918e4ba8fe33cf70a897b157 100644 (file)
@@ -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);
index d2a61b931e5aa90eb01893e04c28b14d0b17115b..92c56c2772d2098f1bdebecf6ec8027c065b17b3 100644 (file)
@@ -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=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+                       
+                       v2u32 frame_size = baseimg->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=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       core::dimension2d<u32> dim = frame_size;
+                       core::position2d<s32> pos_dst(0, 0);
+                       core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
+                       baseimg->copyToWithAlpha(img, pos_dst,
+                                       core::rect<s32>(pos_src, dim),
+                                       video::SColor(255,255,255,255),
+                                       NULL);
+                       // Replace baseimg
+                       baseimg->drop();
+                       baseimg = img;
+               }
                else
                {
                        errorstream<<"generate_image(): Invalid "
index 1211569bc1ae48f923a94602fbc75831ea12a9cc..8b19b4c32e3ee55106ef0a8415732f6509d8a58b 100644 (file)
@@ -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