aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2012-06-16 03:40:45 +0300
committerPerttu Ahola <celeron55@gmail.com>2012-06-16 16:47:28 +0300
commitfd1135c7af46eb2f5b99a11f48bf9f9ae335ea9c (patch)
tree69fe59e684b1e75e817b5a31cfc9a147c2d0a3da
parentf0678979b1ed5c70095a48820a8110eb631ed74d (diff)
downloadminetest-fd1135c7af46eb2f5b99a11f48bf9f9ae335ea9c.tar.gz
minetest-fd1135c7af46eb2f5b99a11f48bf9f9ae335ea9c.tar.bz2
minetest-fd1135c7af46eb2f5b99a11f48bf9f9ae335ea9c.zip
Node texture animation
-rw-r--r--doc/lua_api.txt22
-rw-r--r--games/minimal/mods/default/init.lua17
-rw-r--r--games/minimal/mods/default/textures/default_lava_flowing_animated.pngbin0 -> 8715 bytes
-rw-r--r--games/minimal/mods/default/textures/default_lava_source_animated.pngbin0 -> 3481 bytes
-rw-r--r--src/clientserver.h2
-rw-r--r--src/content_mapblock.cpp50
-rw-r--r--src/game.cpp2
-rw-r--r--src/itemdef.cpp2
-rw-r--r--src/mapblock_mesh.cpp54
-rw-r--r--src/mapblock_mesh.h6
-rw-r--r--src/nodedef.cpp147
-rw-r--r--src/nodedef.h38
-rw-r--r--src/scriptapi.cpp96
-rw-r--r--src/test.cpp8
-rw-r--r--src/tile.cpp46
-rw-r--r--src/tile.h13
16 files changed, 421 insertions, 82 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 4dec9b394..6f2d0e033 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)
{
<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",
diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua
index 1ffe24837..03a54794c 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 000000000..cef8e3611
--- /dev/null
+++ b/games/minimal/mods/default/textures/default_lava_flowing_animated.png
Binary files 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
index 000000000..9339a684e
--- /dev/null
+++ b/games/minimal/mods/default/textures/default_lava_source_animated.png
Binary files differ
diff --git a/src/clientserver.h b/src/clientserver.h
index 7b8fe8e39..86e929f61 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 7585abb69..9d95e1758 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 6ccf02677..ac6d13af1 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 e8de06387..c8771c30c 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 5584216ba..fd5937bbe 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<<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)
diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h
index 0877677bc..d5733120b 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<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;
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index 72f1ea2ea..d0ce3c19d 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<<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
diff --git a/src/nodedef.h b/src/nodedef.h
index 04f375043..a376da30a 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 afd8546d9..3868d1035 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
*/
@@ -992,6 +999,50 @@ static ItemDefinition read_item_definition(lua_State *L, int index,
}
/*
+ 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 5b969017f..448ba23ce 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 d2a61b931..92c56c277 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=\""<<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 "
diff --git a/src/tile.h b/src/tile.h
index 1211569bc..8b19b4c32 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