Node texture animation

This commit is contained in:
Perttu Ahola 2012-06-16 03:40:45 +03:00
parent f0678979b1
commit fd1135c7af
16 changed files with 421 additions and 82 deletions

@ -1124,17 +1124,29 @@ Item definition (register_node, register_craftitem, register_tool)
^ The default functions handle regular use cases. ^ 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) Node definition (register_node)
{ {
<all fields allowed in item definitions>, <all fields allowed in item definitions>,
drawtype = "normal", drawtype = "normal",
visual_scale = 1.0, visual_scale = 1.0,
tile_images = {"default_unknown_block.png"}, tiles = {tile definition 1, def2, def3, def4, def5, def6},
special_materials = { ^ List can be shortened to needed length
{image="", backface_culling=true}, ^ Old field name: tile_images
{image="", backface_culling=true}, special_tiles = {tile definition 1, Tile definition 2},
}, ^ List can be shortened to needed length
^ Old field name: special_materials
alpha = 255, alpha = 255,
post_effect_color = {a=0, r=0, g=0, b=0}, post_effect_color = {a=0, r=0, g=0, b=0},
paramtype = "none", paramtype = "none",

@ -1040,8 +1040,16 @@ minetest.register_node("default:lava_flowing", {
damage_per_second = 4*2, damage_per_second = 4*2,
post_effect_color = {a=192, r=255, g=64, b=0}, post_effect_color = {a=192, r=255, g=64, b=0},
special_materials = { 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}, groups = {lava=3, liquid=2, hot=3},
}) })
@ -1050,7 +1058,10 @@ minetest.register_node("default:lava_source", {
description = "Lava", description = "Lava",
inventory_image = minetest.inventorycube("default_lava.png"), inventory_image = minetest.inventorycube("default_lava.png"),
drawtype = "liquid", 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", paramtype = "light",
light_source = LIGHT_MAX - 1, light_source = LIGHT_MAX - 1,
walkable = false, walkable = false,

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -54,6 +54,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL_VERSION 10: PROTOCOL_VERSION 10:
TOCLIENT_PRIVILEGES TOCLIENT_PRIVILEGES
Version raised to force 'fly' and 'fast' privileges into effect. 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 #define PROTOCOL_VERSION 10

@ -471,7 +471,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
pa_liquid.x0(), pa_liquid.y0()), 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}; s32 corner_resolve[4] = {3,2,1,0};
for(s32 i=0; i<4; i++) 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.Y += corner_levels[j];
vertices[i].Pos += intToFloat(p, BS); 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}; u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector // Add to mesh collector

@ -2206,7 +2206,7 @@ void the_game(
infotext = narrow_to_wide(meta->getString("infotext")); infotext = narrow_to_wide(meta->getString("infotext"));
} else { } else {
MapNode n = map.getNode(nodepos); 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 = L"Unknown node: ";
infotext += narrow_to_wide(nodedef->get(n).name); infotext += narrow_to_wide(nodedef->get(n).name);
} }

@ -451,7 +451,7 @@ public:
if(def->inventory_texture == NULL) if(def->inventory_texture == NULL)
{ {
def->inventory_texture = def->inventory_texture =
tsrc->getTextureRaw(f.tname_tiles[0]); tsrc->getTextureRaw(f.tiledef[0].name);
} }
} }

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gamedef.h" #include "gamedef.h"
#include "mesh.h" #include "mesh.h"
#include "content_mapblock.h" #include "content_mapblock.h"
#include "noise.h"
/* /*
MeshMakeData MeshMakeData
@ -559,6 +560,11 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
spec.material_flags |= MATERIAL_FLAG_CRACK; spec.material_flags |= MATERIAL_FLAG_CRACK;
spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture); 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; return spec;
} }
@ -983,6 +989,23 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
crack_basename += "^[crack"; crack_basename += "^[crack";
m_crack_materials.insert(std::make_pair(i, crack_basename)); 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 // - Lighting
for(u32 j = 0; j < p.vertices.size(); j++) 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 // Check if animation is required for this mesh
m_has_animation = m_has_animation =
!m_crack_materials.empty() || !m_crack_materials.empty() ||
!m_daynight_diffs.empty(); !m_daynight_diffs.empty() ||
!m_animation_tiles.empty();
} }
MapBlockMesh::~MapBlockMesh() MapBlockMesh::~MapBlockMesh()
@ -1094,6 +1118,34 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
m_last_crack = crack; 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 // Day-night transition
if(daynight_ratio != m_last_daynight_ratio) if(daynight_ratio != m_last_daynight_ratio)

@ -122,6 +122,12 @@ private:
// Maps mesh buffer (i.e. material) indices to base texture names // Maps mesh buffer (i.e. material) indices to base texture names
std::map<u32, std::string> m_crack_materials; 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 // Animation info: day/night transitions
// Last daynight_ratio value passed to animate() // Last daynight_ratio value passed to animate()
u32 m_last_daynight_ratio; u32 m_last_daynight_ratio;

@ -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, 0); // version
writeU8(os, backface_culling); 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); int version = readU8(is);
backface_culling = 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; drawtype = NDT_NORMAL;
visual_scale = 1.0; visual_scale = 1.0;
for(u32 i=0; i<6; i++) for(u32 i=0; i<6; i++)
tname_tiles[i] = ""; tiledef[i] = TileDef();
for(u16 j=0; j<CF_SPECIAL_COUNT; j++) for(u16 j=0; j<CF_SPECIAL_COUNT; j++)
mspec_special[j] = MaterialSpec(); tiledef_special[j] = TileDef();
alpha = 255; alpha = 255;
post_effect_color = video::SColor(0, 0, 0, 0); post_effect_color = video::SColor(0, 0, 0, 0);
param_type = CPT_NONE; param_type = CPT_NONE;
@ -164,7 +174,7 @@ void ContentFeatures::reset()
void ContentFeatures::serialize(std::ostream &os) void ContentFeatures::serialize(std::ostream &os)
{ {
writeU8(os, 3); // version writeU8(os, 4); // version
os<<serializeString(name); os<<serializeString(name);
writeU16(os, groups.size()); writeU16(os, groups.size());
for(ItemGroupList::const_iterator for(ItemGroupList::const_iterator
@ -176,10 +186,10 @@ void ContentFeatures::serialize(std::ostream &os)
writeF1000(os, visual_scale); writeF1000(os, visual_scale);
writeU8(os, 6); writeU8(os, 6);
for(u32 i=0; i<6; i++) for(u32 i=0; i<6; i++)
os<<serializeString(tname_tiles[i]); tiledef[i].serialize(os);
writeU8(os, CF_SPECIAL_COUNT); writeU8(os, CF_SPECIAL_COUNT);
for(u32 i=0; i<CF_SPECIAL_COUNT; i++){ for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
mspec_special[i].serialize(os); tiledef_special[i].serialize(os);
} }
writeU8(os, alpha); writeU8(os, alpha);
writeU8(os, post_effect_color.getAlpha()); writeU8(os, post_effect_color.getAlpha());
@ -214,7 +224,7 @@ void ContentFeatures::serialize(std::ostream &os)
void ContentFeatures::deSerialize(std::istream &is) void ContentFeatures::deSerialize(std::istream &is)
{ {
int version = readU8(is); int version = readU8(is);
if(version != 3) if(version != 4 && version != 3)
throw SerializationError("unsupported ContentFeatures version"); throw SerializationError("unsupported ContentFeatures version");
name = deSerializeString(is); name = deSerializeString(is);
groups.clear(); groups.clear();
@ -228,12 +238,21 @@ void ContentFeatures::deSerialize(std::istream &is)
visual_scale = readF1000(is); visual_scale = readF1000(is);
if(readU8(is) != 6) if(readU8(is) != 6)
throw SerializationError("unsupported tile count"); throw SerializationError("unsupported tile count");
for(u32 i=0; i<6; i++) for(u32 i=0; i<6; i++){
tname_tiles[i] = deSerializeString(is); 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) if(readU8(is) != CF_SPECIAL_COUNT)
throw SerializationError("unsupported CF_SPECIAL_COUNT"); throw SerializationError("unsupported CF_SPECIAL_COUNT");
for(u32 i=0; i<CF_SPECIAL_COUNT; i++){ for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
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); alpha = readU8(is);
post_effect_color.setAlpha(readU8(is)); post_effect_color.setAlpha(readU8(is));
@ -260,12 +279,12 @@ void ContentFeatures::deSerialize(std::istream &is)
selection_box.deSerialize(is); selection_box.deSerialize(is);
legacy_facedir_simple = readU8(is); legacy_facedir_simple = readU8(is);
legacy_wallmounted = 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 // If you add anything here, insert it primarily inside the try-catch
// block to not need to increase the version. // block to not need to increase the version.
try{ try{
deSerializeSimpleSoundSpec(sound_footstep, is);
deSerializeSimpleSoundSpec(sound_dig, is);
deSerializeSimpleSoundSpec(sound_dug, is);
}catch(SerializationError &e) {}; }catch(SerializationError &e) {};
} }
@ -494,14 +513,15 @@ public:
for(u16 i=0; i<=MAX_CONTENT; i++) for(u16 i=0; i<=MAX_CONTENT; i++)
{ {
ContentFeatures *f = &m_content_features[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++) for(u32 j=0; j<6; j++)
{ {
tname_tiles[j] = f->tname_tiles[j]; tiledef[j] = f->tiledef[j];
if(tname_tiles[j] == "") if(tiledef[j].name == "")
tname_tiles[j] = "unknown_block.png"; tiledef[j].name = "unknown_block.png";
} }
switch(f->drawtype){ switch(f->drawtype){
default: default:
@ -547,7 +567,7 @@ public:
f->drawtype = NDT_NORMAL; f->drawtype = NDT_NORMAL;
f->solidness = 2; f->solidness = 2;
for(u32 i=0; i<6; i++){ for(u32 i=0; i<6; i++){
tname_tiles[i] += std::string("^[noalpha"); tiledef[i].name += std::string("^[noalpha");
} }
} }
break; break;
@ -560,29 +580,92 @@ public:
break; break;
} }
// Tile textures // Tiles (fill in f->tiles[])
for(u16 j=0; j<6; j++){ 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; f->tiles[j].alpha = f->alpha;
if(f->alpha == 255) if(f->alpha == 255)
f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE; f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
else else
f->tiles[j].material_type = MATERIAL_ALPHA_VERTEX; f->tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
// Material flags
f->tiles[j].material_flags = 0; f->tiles[j].material_flags = 0;
if(f->backface_culling) if(f->backface_culling)
f->tiles[j].material_flags |= MATERIAL_FLAG_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++){ 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; f->special_tiles[j].alpha = f->alpha;
if(f->alpha == 255) if(f->alpha == 255)
f->special_tiles[j].material_type = MATERIAL_ALPHA_SIMPLE; f->special_tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
else else
f->special_tiles[j].material_type = MATERIAL_ALPHA_VERTEX; f->special_tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
// Material flags
f->special_tiles[j].material_flags = 0; 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; 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 #endif

@ -95,15 +95,33 @@ struct NodeBox
struct MapNode; struct MapNode;
class NodeMetadata; 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; std::string name;
bool backface_culling; bool backface_culling; // Takes effect only in special cases
struct{
MaterialSpec(const std::string &tname_="", bool backface_culling_=true): enum TileAnimationType type;
tname(tname_), int aspect_w; // width for aspect ratio
backface_culling(backface_culling_) 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 serialize(std::ostream &os) const;
void deSerialize(std::istream &is); void deSerialize(std::istream &is);
@ -159,8 +177,8 @@ struct ContentFeatures
// Visual definition // Visual definition
enum NodeDrawType drawtype; enum NodeDrawType drawtype;
float visual_scale; // Misc. scale parameter float visual_scale; // Misc. scale parameter
std::string tname_tiles[6]; TileDef tiledef[6];
MaterialSpec mspec_special[CF_SPECIAL_COUNT]; // Use setter methods TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
u8 alpha; u8 alpha;
// Post effect color, drawn when the camera is inside the node. // Post effect color, drawn when the camera is inside the node.

@ -426,6 +426,13 @@ struct EnumString es_CraftMethod[] =
{0, NULL}, {0, NULL},
}; };
struct EnumString es_TileAnimationType[] =
{
{TAT_NONE, "none"},
{TAT_VERTICAL_FRAMES, "vertical_frames"},
{0, NULL},
};
/* /*
C struct <-> Lua table converter functions C struct <-> Lua table converter functions
*/ */
@ -991,6 +998,50 @@ static ItemDefinition read_item_definition(lua_State *L, int index,
return def; 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 ContentFeatures
*/ */
@ -1026,18 +1077,23 @@ static ContentFeatures read_content_features(lua_State *L, int index)
f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType, f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType,
NDT_NORMAL); NDT_NORMAL);
getfloatfield(L, index, "visual_scale", f.visual_scale); 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)){ if(lua_istable(L, -1)){
int table = lua_gettop(L); int table = lua_gettop(L);
lua_pushnil(L); lua_pushnil(L);
int i = 0; int i = 0;
while(lua_next(L, table) != 0){ while(lua_next(L, table) != 0){
// key at index -2 and value at index -1 // Read tiledef from value
if(lua_isstring(L, -1)) f.tiledef[i] = read_tiledef(L, -1);
f.tname_tiles[i] = lua_tostring(L, -1);
else
f.tname_tiles[i] = "";
// removes value, keeps key for next iteration // removes value, keeps key for next iteration
lua_pop(L, 1); lua_pop(L, 1);
i++; i++;
@ -1048,29 +1104,31 @@ static ContentFeatures read_content_features(lua_State *L, int index)
} }
// Copy last value to all remaining textures // Copy last value to all remaining textures
if(i >= 1){ if(i >= 1){
std::string lastname = f.tname_tiles[i-1]; TileDef lasttile = f.tiledef[i-1];
while(i < 6){ while(i < 6){
f.tname_tiles[i] = lastname; f.tiledef[i] = lasttile;
i++; i++;
} }
} }
} }
lua_pop(L, 1); 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)){ if(lua_istable(L, -1)){
int table = lua_gettop(L); int table = lua_gettop(L);
lua_pushnil(L); lua_pushnil(L);
int i = 0; int i = 0;
while(lua_next(L, table) != 0){ while(lua_next(L, table) != 0){
// key at index -2 and value at index -1 // Read tiledef from value
int smtable = lua_gettop(L); f.tiledef_special[i] = read_tiledef(L, -1);
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;
// removes value, keeps key for next iteration // removes value, keeps key for next iteration
lua_pop(L, 1); lua_pop(L, 1);
i++; i++;

@ -80,7 +80,7 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n
f = ContentFeatures(); f = ContentFeatures();
f.name = itemdef.name; f.name = itemdef.name;
for(int i = 0; i < 6; i++) 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; f.is_ground_content = true;
idef->registerItem(itemdef); idef->registerItem(itemdef);
ndef->set(i, f); ndef->set(i, f);
@ -100,10 +100,10 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n
"{default_dirt.png&default_grass_side.png"; "{default_dirt.png&default_grass_side.png";
f = ContentFeatures(); f = ContentFeatures();
f.name = itemdef.name; f.name = itemdef.name;
f.tname_tiles[0] = "default_grass.png"; f.tiledef[0].name = "default_grass.png";
f.tname_tiles[1] = "default_dirt.png"; f.tiledef[1].name = "default_dirt.png";
for(int i = 2; i < 6; i++) 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; f.is_ground_content = true;
idef->registerItem(itemdef); idef->registerItem(itemdef);
ndef->set(i, f); ndef->set(i, f);

@ -858,7 +858,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
const ContentFeatures &f = ndef->get(j); const ContentFeatures &f = ndef->get(j);
for(u32 i=0; i<6; i++) for(u32 i=0; i<6; i++)
{ {
std::string name = f.tname_tiles[i]; std::string name = f.tiledef[i].name;
sourcelist[name] = true; sourcelist[name] = true;
} }
} }
@ -988,7 +988,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
src_x = pos_in_atlas.X; src_x = pos_in_atlas.X;
} }
s32 y = y0 + pos_in_atlas.Y; 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; s32 dst_y = y;
video::SColor c = atlas_img->getPixel(src_x, src_y); video::SColor c = atlas_img->getPixel(src_x, src_y);
atlas_img->setPixel(dst_x,dst_y,c); 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(); 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 else
{ {
errorstream<<"generate_image(): Invalid " errorstream<<"generate_image(): Invalid "

@ -162,6 +162,9 @@ enum MaterialType{
// Should the crack be drawn on transparent pixels (unset) or not (set)? // Should the crack be drawn on transparent pixels (unset) or not (set)?
// Ignored if MATERIAL_FLAG_CRACK is not set. // Ignored if MATERIAL_FLAG_CRACK is not set.
#define MATERIAL_FLAG_CRACK_OVERLAY 0x04 #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. This fully defines the looks of a tile.
@ -178,7 +181,9 @@ struct TileSpec
material_flags( material_flags(
//0 // <- DEBUG, Use the one below //0 // <- DEBUG, Use the one below
MATERIAL_FLAG_BACKFACE_CULLING MATERIAL_FLAG_BACKFACE_CULLING
) ),
animation_frame_count(1),
animation_frame_length_ms(0)
{ {
} }
@ -227,10 +232,12 @@ struct TileSpec
AtlasPointer texture; AtlasPointer texture;
// Vertex alpha // Vertex alpha
u8 alpha; u8 alpha;
// Material type // Material parameters
u8 material_type; u8 material_type;
// Material flags
u8 material_flags; u8 material_flags;
// Animation parameters
u8 animation_frame_count;
u16 animation_frame_length_ms;
}; };
#endif #endif