mirror of
https://github.com/minetest/minetest.git
synced 2024-11-23 08:03:45 +01:00
Node texture animation
This commit is contained in:
parent
f0678979b1
commit
fd1135c7af
@ -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",
|
||||
|
@ -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,
|
||||
|
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:
|
||||
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
|
||||
|
@ -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++)
|
||||
@ -483,6 +485,52 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||
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
|
||||
collector.append(tile_liquid, vertices, 4, indices, 6);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
@ -1095,6 +1119,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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
141
src/nodedef.cpp
141
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);
|
||||
// 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);
|
||||
// If you add anything here, insert it primarily inside the try-catch
|
||||
// block to not need to increase the version.
|
||||
try{
|
||||
}catch(SerializationError &e) {};
|
||||
}
|
||||
|
||||
@ -495,12 +514,13 @@ public:
|
||||
{
|
||||
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){
|
||||
@ -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
|
||||
|
@ -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;
|
||||
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;
|
||||
|
||||
MaterialSpec(const std::string &tname_="", bool backface_culling_=true):
|
||||
tname(tname_),
|
||||
backface_culling(backface_culling_)
|
||||
{}
|
||||
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.
|
||||
|
@ -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
|
||||
*/
|
||||
@ -1027,17 +1078,22 @@ static ContentFeatures read_content_features(lua_State *L, int index)
|
||||
NDT_NORMAL);
|
||||
getfloatfield(L, index, "visual_scale", f.visual_scale);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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++;
|
||||
|
@ -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);
|
||||
|
46
src/tile.cpp
46
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 "
|
||||
|
13
src/tile.h
13
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
|
||||
|
Loading…
Reference in New Issue
Block a user