Fix crack overlay for animated textures

This commit is contained in:
Kahrl 2013-08-02 21:31:52 +02:00 committed by PilzAdam
parent 2336d21efd
commit 96c34d369e
2 changed files with 146 additions and 113 deletions

@ -1133,12 +1133,17 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
if(p.tile.material_flags & MATERIAL_FLAG_CRACK) if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
{ {
ITextureSource *tsrc = data->m_gamedef->tsrc(); ITextureSource *tsrc = data->m_gamedef->tsrc();
std::string crack_basename = tsrc->getTextureName(p.tile.texture_id); // Find the texture name plus ^[crack:N:
std::ostringstream os(std::ios::binary);
os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
crack_basename += "^[cracko"; os<<"o"; // use ^[cracko
else os<<":"<<(u32)p.tile.animation_frame_count<<":";
crack_basename += "^[crack"; m_crack_materials.insert(std::make_pair(i, os.str()));
m_crack_materials.insert(std::make_pair(i, crack_basename)); // Replace tile texture with the cracked one
p.tile.texture = tsrc->getTexture(
os.str()+"0",
&p.tile.texture_id);
} }
// - Texture animation // - Texture animation
if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
@ -1313,8 +1318,22 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
ITextureSource *tsrc = m_gamedef->getTextureSource(); ITextureSource *tsrc = m_gamedef->getTextureSource();
std::ostringstream os; std::ostringstream os;
os<<basename<<crack; os<<basename<<crack;
buf->getMaterial().setTexture(0, u32 new_texture_id = 0;
tsrc->getTexture(os.str())); video::ITexture *new_texture =
tsrc->getTexture(os.str(), &new_texture_id);
buf->getMaterial().setTexture(0, new_texture);
// If the current material is also animated,
// update animation info
std::map<u32, TileSpec>::iterator anim_iter =
m_animation_tiles.find(i->first);
if(anim_iter != m_animation_tiles.end()){
TileSpec &tile = anim_iter->second;
tile.texture = new_texture;
tile.texture_id = new_texture_id;
// force animation update
m_animation_frames[i->first] = -1;
}
} }
m_last_crack = crack; m_last_crack = crack;

@ -379,11 +379,11 @@ public:
const TextureFromMeshParams &params); const TextureFromMeshParams &params);
// Generates an image from a full string like // Generates an image from a full string like
// "stone.png^mineral_coal.png^[crack0". // "stone.png^mineral_coal.png^[crack:1:0".
// Shall be called from the main thread. // Shall be called from the main thread.
video::IImage* generateImageFromScratch(std::string name); video::IImage* generateImageFromScratch(std::string name);
// Generate image based on a string like "stone.png" or "[crack0". // Generate image based on a string like "stone.png" or "[crack:1:0".
// if baseimg is NULL, it is created. Otherwise stuff is made on it. // if baseimg is NULL, it is created. Otherwise stuff is made on it.
// Shall be called from the main thread. // Shall be called from the main thread.
bool generateImage(std::string part_of_name, video::IImage *& baseimg); bool generateImage(std::string part_of_name, video::IImage *& baseimg);
@ -543,14 +543,21 @@ u32 TextureSource::getTextureId(const std::string &name)
return 0; return 0;
} }
// Overlay image on top of another image (used for cracks)
void overlay(video::IImage *image, video::IImage *overlay);
// Draw an image on top of an another one, using the alpha channel of the // Draw an image on top of an another one, using the alpha channel of the
// source image // source image
static void blit_with_alpha(video::IImage *src, video::IImage *dst, static void blit_with_alpha(video::IImage *src, video::IImage *dst,
v2s32 src_pos, v2s32 dst_pos, v2u32 size); v2s32 src_pos, v2s32 dst_pos, v2u32 size);
// Like blit_with_alpha, but only modifies destination pixels that
// are fully opaque
static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
v2s32 src_pos, v2s32 dst_pos, v2u32 size);
// Draw or overlay a crack
static void draw_crack(video::IImage *crack, video::IImage *dst,
bool use_overlay, u32 frame_count, u32 progression,
video::IVideoDriver *driver);
// Brighten image // Brighten image
void brighten(video::IImage *image); void brighten(video::IImage *image);
// Parse a transform name // Parse a transform name
@ -1032,8 +1039,10 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas
<<std::endl;*/ <<std::endl;*/
/* /*
[crackN [crack:N:P
[cracko:N:P
Adds a cracking texture Adds a cracking texture
N = animation frame count, P = crack progression
*/ */
if(part_of_name.substr(0,6) == "[crack") if(part_of_name.substr(0,6) == "[crack")
{ {
@ -1044,24 +1053,14 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas
<<"\", cancelling."<<std::endl; <<"\", cancelling."<<std::endl;
return false; return false;
} }
// Crack image number and overlay option
s32 progression = 0;
bool use_overlay = false;
if(part_of_name.substr(6,1) == "o")
{
progression = stoi(part_of_name.substr(7));
use_overlay = true;
}
else
{
progression = stoi(part_of_name.substr(6));
use_overlay = false;
}
// Size of the base image // Crack image number and overlay option
core::dimension2d<u32> dim_base = baseimg->getDimension(); bool use_overlay = (part_of_name[6] == 'o');
Strfnd sf(part_of_name);
sf.next(":");
u32 frame_count = stoi(sf.next(":"));
u32 progression = stoi(sf.next(":"));
/* /*
Load crack image. Load crack image.
@ -1070,60 +1069,12 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas
*/ */
video::IImage *img_crack = m_sourcecache.getOrLoad( video::IImage *img_crack = m_sourcecache.getOrLoad(
"crack_anylength.png", m_device); "crack_anylength.png", m_device);
if(img_crack && progression >= 0) if(img_crack && progression >= 0)
{ {
// Dimension of original image draw_crack(img_crack, baseimg,
core::dimension2d<u32> dim_crack use_overlay, frame_count,
= img_crack->getDimension(); progression, driver);
// Count of crack stages
s32 crack_count = dim_crack.Height / dim_crack.Width;
// Limit progression
if(progression > crack_count-1)
progression = crack_count-1;
// Dimension of a single crack stage
core::dimension2d<u32> dim_crack_cropped(
dim_crack.Width,
dim_crack.Width
);
// Create cropped and scaled crack images
video::IImage *img_crack_cropped = driver->createImage(
video::ECF_A8R8G8B8, dim_crack_cropped);
video::IImage *img_crack_scaled = driver->createImage(
video::ECF_A8R8G8B8, dim_base);
if(img_crack_cropped && img_crack_scaled)
{
// Crop crack image
v2s32 pos_crack(0, progression*dim_crack.Width);
img_crack->copyTo(img_crack_cropped,
v2s32(0,0),
core::rect<s32>(pos_crack, dim_crack_cropped));
// Scale crack image by copying
img_crack_cropped->copyToScaling(img_crack_scaled);
// Copy or overlay crack image
if(use_overlay)
{
overlay(baseimg, img_crack_scaled);
}
else
{
/*img_crack_scaled->copyToWithAlpha(
baseimg,
v2s32(0,0),
core::rect<s32>(v2s32(0,0), dim_base),
video::SColor(255,255,255,255));*/
blit_with_alpha(img_crack_scaled, baseimg,
v2s32(0,0), v2s32(0,0), dim_base);
}
}
if(img_crack_scaled)
img_crack_scaled->drop();
if(img_crack_cropped)
img_crack_cropped->drop();
img_crack->drop(); img_crack->drop();
} }
} }
@ -1499,37 +1450,6 @@ bool TextureSource::generateImage(std::string part_of_name, video::IImage *& bas
return true; return true;
} }
void overlay(video::IImage *image, video::IImage *overlay)
{
/*
Copy overlay to image, taking alpha into account.
Where image is transparent, don't copy from overlay.
Images sizes must be identical.
*/
if(image == NULL || overlay == NULL)
return;
core::dimension2d<u32> dim = image->getDimension();
core::dimension2d<u32> dim_overlay = overlay->getDimension();
assert(dim == dim_overlay);
for(u32 y=0; y<dim.Height; y++)
for(u32 x=0; x<dim.Width; x++)
{
video::SColor c1 = image->getPixel(x,y);
video::SColor c2 = overlay->getPixel(x,y);
u32 a1 = c1.getAlpha();
u32 a2 = c2.getAlpha();
if(a1 == 255 && a2 != 0)
{
c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
}
image->setPixel(x,y,c1);
}
}
/* /*
Draw an image on top of an another one, using the alpha channel of the Draw an image on top of an another one, using the alpha channel of the
source image source image
@ -1554,6 +1474,100 @@ static void blit_with_alpha(video::IImage *src, video::IImage *dst,
} }
} }
/*
Draw an image on top of an another one, using the alpha channel of the
source image; only modify fully opaque pixels in destinaion
*/
static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
v2s32 src_pos, v2s32 dst_pos, v2u32 size)
{
for(u32 y0=0; y0<size.Y; y0++)
for(u32 x0=0; x0<size.X; x0++)
{
s32 src_x = src_pos.X + x0;
s32 src_y = src_pos.Y + y0;
s32 dst_x = dst_pos.X + x0;
s32 dst_y = dst_pos.Y + y0;
video::SColor src_c = src->getPixel(src_x, src_y);
video::SColor dst_c = dst->getPixel(dst_x, dst_y);
if(dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
{
dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
dst->setPixel(dst_x, dst_y, dst_c);
}
}
}
static void draw_crack(video::IImage *crack, video::IImage *dst,
bool use_overlay, u32 frame_count, u32 progression,
video::IVideoDriver *driver)
{
// Dimension of destination image
core::dimension2d<u32> dim_dst = dst->getDimension();
// Dimension of original image
core::dimension2d<u32> dim_crack = crack->getDimension();
// Count of crack stages
u32 crack_count = dim_crack.Height / dim_crack.Width;
// Limit frame_count
if(frame_count > dim_dst.Height)
frame_count = dim_dst.Height;
if(frame_count == 0)
frame_count = 1;
// Limit progression
if(progression > crack_count-1)
progression = crack_count-1;
// Dimension of a single crack stage
core::dimension2d<u32> dim_crack_cropped(
dim_crack.Width,
dim_crack.Width
);
// Dimension of the scaled crack stage,
// which is the same as the dimension of a single destination frame
core::dimension2d<u32> dim_crack_scaled(
dim_dst.Width,
dim_dst.Height / frame_count
);
// Create cropped and scaled crack images
video::IImage *crack_cropped = driver->createImage(
video::ECF_A8R8G8B8, dim_crack_cropped);
video::IImage *crack_scaled = driver->createImage(
video::ECF_A8R8G8B8, dim_crack_scaled);
if(crack_cropped && crack_scaled)
{
// Crop crack image
v2s32 pos_crack(0, progression*dim_crack.Width);
crack->copyTo(crack_cropped,
v2s32(0,0),
core::rect<s32>(pos_crack, dim_crack_cropped));
// Scale crack image by copying
crack_cropped->copyToScaling(crack_scaled);
// Copy or overlay crack image onto each frame
for(u32 i = 0; i < frame_count; ++i)
{
v2s32 dst_pos(0, dim_crack_scaled.Height * i);
if(use_overlay)
{
blit_with_alpha_overlay(crack_scaled, dst,
v2s32(0,0), dst_pos,
dim_crack_scaled);
}
else
{
blit_with_alpha(crack_scaled, dst,
v2s32(0,0), dst_pos,
dim_crack_scaled);
}
}
}
if(crack_scaled)
crack_scaled->drop();
if(crack_cropped)
crack_cropped->drop();
}
void brighten(video::IImage *image) void brighten(video::IImage *image)
{ {
if(image == NULL) if(image == NULL)