1
0
mirror of https://github.com/luanti-org/luanti.git synced 2025-10-27 22:05:17 +01:00

Move crack overlay to shader

This commit is contained in:
sfan5
2025-10-19 13:14:00 +02:00
parent c58a7ad98a
commit 4756e23477
10 changed files with 132 additions and 94 deletions

View File

@@ -1,4 +1,6 @@
uniform sampler2D baseTexture;
#define crackTexture texture1
uniform sampler2D crackTexture;
uniform vec3 dayLight;
uniform lowp vec4 fogColor;
@@ -9,6 +11,10 @@ uniform float fogShadingParameter;
uniform highp vec3 cameraOffset;
uniform vec3 cameraPosition;
uniform float animationTimer;
uniform float crackAnimationLength;
uniform float crackLevel;
uniform float crackTextureScale;
#ifdef ENABLE_DYNAMIC_SHADOWS
// shadow texture
uniform sampler2D ShadowMapSampler;
@@ -411,9 +417,18 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
#endif
#endif
// maps [0, N] to [0, 1] like GL_REPEAT would
vec2 uv_repeat(vec2 v)
{
if (v.x > 1.0)
v.x = fract(v.x);
if (v.y > 1.0)
v.y = fract(v.y);
return v;
}
void main(void)
{
vec3 color;
vec2 uv = varTexCoord.st;
vec4 base = texture2D(baseTexture, uv).rgba;
@@ -429,8 +444,19 @@ void main(void)
discard;
#endif
color = base.rgb;
vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
// Apply crack overlay
float crack_progress = min(crackLevel, crackAnimationLength - 1.0);
if (crack_progress >= 0.0) {
// undo scaling of e.g. world-aligned nodes
vec2 orig_uv = uv_repeat(uv * vec2(crackTextureScale));
vec2 cuv_offset = vec2(0.0, crack_progress / crackAnimationLength);
vec2 cuv_factor = vec2(1.0, 1.0 / crackAnimationLength);
vec4 crack = texture2D(crackTexture, cuv_offset + orig_uv * cuv_factor);
base = mix(base, crack, crack.a);
}
vec4 col = vec4(base.rgb * varColor.rgb, 1.0);
#ifdef ENABLE_DYNAMIC_SHADOWS
// Fragment normal, can differ from vNormal which is derived from vertex normals.
@@ -570,5 +596,5 @@ void main(void)
col = mix(fogColor * pow(fogColor / fogColorMax, vec4(2.0 * clarity)), col, clarity);
col = vec4(col.rgb, base.a);
gl_FragData[0] = col;
gl_FragColor = col;
}

View File

@@ -285,6 +285,7 @@ public:
return m_animation_time;
}
/// @return integer ∊ [0, crack_animation_length] or -1 for invalid
int getCrackLevel();
v3s16 getCrackPos();
void setCrack(int level, v3s16 pos);

View File

@@ -1054,7 +1054,7 @@ void MapblockMeshGenerator::drawTorchlikeNode()
default: tileindex = 2; // side (or invalid, shouldn't happen)
}
TileSpec tile;
useTile(&tile, tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
useTile(&tile, tileindex, 0, MATERIAL_FLAG_BACKFACE_CULLING);
float size = BS / 2 * cur_node.f->visual_scale;
v3f vertices[4] = {
@@ -1108,7 +1108,7 @@ void MapblockMeshGenerator::drawSignlikeNode()
{
u8 wall = cur_node.n.getWallMounted(nodedef);
TileSpec tile;
useTile(&tile, 0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
useTile(&tile, 0, 0, MATERIAL_FLAG_BACKFACE_CULLING);
static const float offset = BS / 16;
float size = BS / 2 * cur_node.f->visual_scale;
// Wall at X+ of node
@@ -1294,9 +1294,10 @@ void MapblockMeshGenerator::drawPlantlikeNode()
void MapblockMeshGenerator::drawPlantlikeRootedNode()
{
drawSolidNode();
TileSpec tile;
useTile(&tile, 0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
cur_node.origin += v3f(0.0, BS, 0.0);
useTile(&tile, 0, 0, 0, true);
cur_node.origin += v3f(0, BS, 0);
cur_node.p.Y++;
if (data->m_smooth_lighting) {
getSmoothLightFrame();
@@ -1379,10 +1380,7 @@ void MapblockMeshGenerator::drawFirelikeNode()
void MapblockMeshGenerator::drawFencelikeNode()
{
TileSpec tile_nocrack;
useTile(&tile_nocrack, 0, 0, 0);
for (auto &layer : tile_nocrack.layers)
layer.material_flags &= ~MATERIAL_FLAG_CRACK;
useTile(&tile_nocrack, 0, 0, MATERIAL_FLAG_CRACK);
// Put wood the right way around in the posts
TileSpec tile_rot = tile_nocrack;
@@ -1529,7 +1527,7 @@ void MapblockMeshGenerator::drawRaillikeNode()
}
TileSpec tile;
useTile(&tile, tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
useTile(&tile, tile_index, 0, MATERIAL_FLAG_BACKFACE_CULLING);
static const float offset = BS / 64;
static const float size = BS / 2;

View File

@@ -72,7 +72,7 @@ private:
video::SColor blendLightColor(const v3f &vertex_pos);
video::SColor blendLightColor(const v3f &vertex_pos, const v3f &vertex_normal);
void useTile(TileSpec *tile_ret, int index = 0, u8 set_flags = MATERIAL_FLAG_CRACK_OVERLAY,
void useTile(TileSpec *tile_ret, int index = 0, u8 set_flags = 0,
u8 reset_flags = 0, bool special = false);
void getTile(int index, TileSpec *tile_ret);
void getTile(v3s16 direction, TileSpec *tile_ret);

View File

@@ -179,21 +179,29 @@ class GameGlobalShaderUniformSetter : public IShaderUniformSetter
{
Sky *m_sky;
Client *m_client;
CachedVertexShaderSetting<float> m_animation_timer_vertex{"animationTimer"};
CachedPixelShaderSetting<float> m_animation_timer_pixel{"animationTimer"};
CachedVertexShaderSetting<float>
m_animation_timer_delta_vertex{"animationTimerDelta"};
CachedPixelShaderSetting<float>
m_animation_timer_delta_pixel{"animationTimerDelta"};
int m_crack_animation_length_i;
CachedPixelShaderSetting<float> m_crack_animation_length{"crackAnimationLength"};
int m_crack_level_i = -1;
CachedPixelShaderSetting<float> m_crack_level{"crackLevel"};
int m_crack_texture_scale_i = 0;
CachedPixelShaderSetting<float> m_crack_texture_scale{"crackTextureScale"};
CachedPixelShaderSetting<float, 3> m_day_light{"dayLight"};
CachedPixelShaderSetting<float, 3> m_minimap_yaw{"yawVec"};
CachedPixelShaderSetting<float, 3> m_camera_offset_pixel{"cameraOffset"};
CachedVertexShaderSetting<float, 3> m_camera_offset_vertex{"cameraOffset"};
CachedPixelShaderSetting<float, 3> m_camera_position_pixel{ "cameraPosition" };
CachedVertexShaderSetting<float, 3> m_camera_position_vertex{ "cameraPosition" };
CachedPixelShaderSetting<float, 3> m_camera_position_pixel{"cameraPosition"};
CachedVertexShaderSetting<float, 3> m_camera_position_vertex{"cameraPosition"};
CachedVertexShaderSetting<float, 2> m_texel_size0_vertex{"texelSize0"};
CachedPixelShaderSetting<float, 2> m_texel_size0_pixel{"texelSize0"};
v2f m_texel_size0;
CachedStructPixelShaderSetting<float, 7> m_exposure_params_pixel{
"exposureParams",
std::array<const char*, 7> {
@@ -235,9 +243,9 @@ public:
void setSky(Sky *sky) { m_sky = sky; }
GameGlobalShaderUniformSetter(Sky *sky, Client *client) :
GameGlobalShaderUniformSetter(Sky *sky, Game *game) :
m_sky(sky),
m_client(client)
m_client(game->getClient())
{
for (auto &name : SETTING_CALLBACKS)
g_settings->registerChangedCallback(name, settingsCallback, this);
@@ -245,6 +253,7 @@ public:
m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f);
m_bloom_enabled = g_settings->getBool("enable_bloom");
m_volumetric_light_enabled = g_settings->getBool("enable_volumetric_lighting") && m_bloom_enabled;
m_crack_animation_length_i = game->crack_animation_length;
}
~GameGlobalShaderUniformSetter()
@@ -284,6 +293,15 @@ public:
m_texel_size0_vertex.set(m_texel_size0, services);
m_texel_size0_pixel.set(m_texel_size0, services);
{
float tmp = m_crack_animation_length_i;
m_crack_animation_length.set(&tmp, services);
tmp = m_crack_level_i;
m_crack_level.set(&tmp, services);
tmp = m_crack_texture_scale_i;
m_crack_texture_scale.set(&tmp, services);
}
const auto &lighting = m_client->getEnv().getLocalPlayer()->getLighting();
const AutoExposure &exposure_params = lighting.exposure;
@@ -357,6 +375,11 @@ public:
void onSetMaterial(const video::SMaterial &material) override
{
// This is set only for node materials which have a crack, see mapblock_mesh.cpp.
auto pair = MapBlockMesh::unpackCrackMaterialParam(material.MaterialTypeParam);
m_crack_level_i = pair.first;
m_crack_texture_scale_i = pair.second;
video::ITexture *texture = material.getTexture(0);
if (texture) {
core::dimension2du size = texture->getSize();
@@ -371,11 +394,11 @@ public:
class GameGlobalShaderUniformSetterFactory : public IShaderUniformSetterFactory
{
Sky *m_sky = nullptr;
Client *m_client;
std::vector<GameGlobalShaderUniformSetter *> created_nosky;
Game *m_game;
std::vector<GameGlobalShaderUniformSetter*> created_nosky;
public:
GameGlobalShaderUniformSetterFactory(Client *client) :
m_client(client)
GameGlobalShaderUniformSetterFactory(Game *game) :
m_game(game)
{}
void setSky(Sky *sky)
@@ -391,7 +414,7 @@ public:
{
if (str_starts_with(name, "shadow/"))
return nullptr;
auto *scs = new GameGlobalShaderUniformSetter(m_sky, m_client);
auto *scs = new GameGlobalShaderUniformSetter(m_sky, m_game);
if (!m_sky)
created_nosky.push_back(scs);
return scs;
@@ -957,10 +980,19 @@ bool Game::createClient(const GameStartData &start_data)
return false;
}
// Pre-calculate crack length
video::ITexture *t = texture_src->getTexture("crack_anylength.png");
if (t) {
v2u32 size = t->getOriginalSize();
crack_animation_length = size.Y / size.X;
} else {
crack_animation_length = 5;
}
shader_src->addShaderConstantSetter(
std::make_unique<NodeShaderConstantSetter>());
auto scsf_up = std::make_unique<GameGlobalShaderUniformSetterFactory>(client);
auto scsf_up = std::make_unique<GameGlobalShaderUniformSetterFactory>(this);
auto* scsf = scsf_up.get();
shader_src->addShaderUniformSetterFactory(std::move(scsf_up));
@@ -988,16 +1020,6 @@ bool Game::createClient(const GameStartData &start_data)
sky = make_irr<Sky>(-1, m_rendering_engine, texture_src, shader_src);
scsf->setSky(sky.get());
/* Pre-calculated values
*/
video::ITexture *t = texture_src->getTexture("crack_anylength.png");
if (t) {
v2u32 size = t->getOriginalSize();
crack_animation_length = size.Y / size.X;
} else {
crack_animation_length = 5;
}
if (!initGui())
return false;

View File

@@ -1039,17 +1039,14 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
// A special texture modification
/*
[crack:N:P
[cracko:N:P
[crack[o][:<tiles>]:<frame_count>:<frame>
Adds a cracking texture
N = animation frame count, P = crack progression
NOTE: Crack rendering does not use this, it's for mods only.
*/
if (str_starts_with(part_of_name, "[crack"))
{
CHECK_BASEIMG();
// Crack image number and overlay option
// Format: crack[o][:<tiles>]:<frame_count>:<frame>
bool use_overlay = (part_of_name[6] == 'o');
Strfnd sf(part_of_name);
sf.next(":");
@@ -1276,9 +1273,7 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
[inventorycube{topimage{leftimage{rightimage
In every subimage, replace ^ with &.
Create an "inventory cube".
NOTE: This should be used only on its own.
Example (a grass block (not actually used in game):
"[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
NOTE: Inventory rendering does not use this, it's for mods only.
*/
else if (str_starts_with(part_of_name, "[inventorycube"))
{

View File

@@ -648,25 +648,6 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data):
p.applyTileColor();
// Generate animation data
// - Cracks
if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
// Find the texture name plus ^[crack:N:
std::ostringstream os(std::ios::binary);
os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
os << "o"; // use ^[cracko
u8 tiles = p.layer.scale;
if (tiles > 1)
os << ":" << (u32)tiles;
os << ":" << (u32)p.layer.animation_frame_count << ":";
m_crack_materials.insert(std::make_pair(
std::pair<u8, u32>(layer, i), os.str()));
// Replace tile texture with the cracked one
p.layer.texture = m_tsrc->getTextureForMesh(
os.str() + "0",
&p.layer.texture_id);
}
// - Texture animation
if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
// Add to MapBlockMesh in order to animate these tiles
m_animation_info.emplace(std::make_pair(layer, i), AnimationInfo(p.layer));
@@ -689,6 +670,17 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data):
p.layer.applyMaterialOptions(material, layer);
}
// Handle crack
if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
auto *t = m_tsrc->getTextureForMesh("crack_anylength.png");
material.setTexture(TEXTURE_LAYER_CRACK, t);
material.MaterialTypeParam =
packCrackMaterialParam(-1, MYMAX(1, p.layer.scale));
m_crack_materials.emplace_back(layer, i);
}
// Add to buffer
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
buf->Material = material;
if (p.layer.isTransparent()) {
@@ -752,22 +744,14 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack,
// Cracks
if (crack != m_last_crack) {
for (auto &crack_material : m_crack_materials) {
for (auto &it : m_crack_materials) {
scene::IMeshBuffer *buf = m_mesh[it.first]->getMeshBuffer(it.second);
assert(buf);
video::SMaterial &mat = buf->getMaterial();
// TODO crack on animated tiles does not work
auto anim_it = m_animation_info.find(crack_material.first);
if (anim_it != m_animation_info.end())
continue;
scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
getMeshBuffer(crack_material.first.second);
// Create new texture name from original
std::string s = crack_material.second + itos(crack);
u32 new_texture_id = 0;
video::ITexture *new_texture =
m_tsrc->getTextureForMesh(s, &new_texture_id);
buf->getMaterial().setTexture(0, new_texture);
auto pair = unpackCrackMaterialParam(mat.MaterialTypeParam);
pair.first = crack;
mat.MaterialTypeParam = packCrackMaterialParam(pair.first, pair.second);
}
m_last_crack = crack;
@@ -776,6 +760,7 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack,
// Texture animation
for (auto &it : m_animation_info) {
scene::IMeshBuffer *buf = m_mesh[it.first.first]->getMeshBuffer(it.first.second);
assert(buf);
video::SMaterial &material = buf->getMaterial();
it.second.updateTexture(material, time);
}

View File

@@ -185,7 +185,7 @@ public:
// faraway: whether the block is far away from the camera (~50 nodes)
// time: the global animation time, 0 .. 60 (repeats every minute)
// daynight_ratio: 0 .. 1000
// crack: -1 .. CRACK_ANIMATION_LENGTH-1 (-1 for off)
// crack: -1 .. CRACK_ANIMATION_LENGTH (-1 for off)
// Returns true if anything has been changed.
bool animate(bool faraway, float time, int crack, u32 daynight_ratio);
@@ -257,8 +257,28 @@ public:
return m_transparent_buffers;
}
/**
* Texture layer in SMaterial where the crack texture is put
*/
static const int TEXTURE_LAYER_CRACK = 1;
static float packCrackMaterialParam(int crack, u8 layer_scale)
{
// +1 so that the default MaterialTypeParam = 0 is a no-op,
// since the shader needs to know when to actually apply the crack.
u32 n = (layer_scale << 16) | (u16) (crack + 1);
return n;
}
static std::pair<int, u8> unpackCrackMaterialParam(float param)
{
u32 n = param;
return std::make_pair<int, u8>((n & 0xffff) - 1, (n >> 16) & 0xff);
}
private:
typedef std::pair<u8 /* layer index */, u32 /* buffer index */> MeshIndex;
irr_ptr<scene::IMesh> m_mesh[MAX_TILE_LAYERS];
std::vector<MinimapMapblock*> m_minimap_mapblocks;
ITextureSource *m_tsrc;
@@ -274,13 +294,12 @@ private:
// Animation info: cracks
// Last crack value passed to animate()
int m_last_crack;
// Maps mesh and mesh buffer (i.e. material) indices to base texture names
std::map<std::pair<u8, u32>, std::string> m_crack_materials;
// Indicates which materials to apply the crack to
std::vector<MeshIndex> m_crack_materials;
// Animation info: texture animation
// Maps mesh and mesh buffer indices to TileSpecs
// Keys are pairs of (mesh index, buffer index in the mesh)
std::map<std::pair<u8, u32>, AnimationInfo> m_animation_info;
std::map<MeshIndex, AnimationInfo> m_animation_info;
// list of all semitransparent triangles in the mapblock
std::vector<MeshTriangle> m_transparent_triangles;

View File

@@ -288,7 +288,7 @@ class MainShaderUniformSetter : public IShaderUniformSetter
CachedPixelShaderSetting<SamplerLayer_t> m_texture2{"texture2"};
CachedPixelShaderSetting<SamplerLayer_t> m_texture3{"texture3"};
// commonly used way to pass material color to shader
// common material variables passed to shader
video::SColor m_material_color;
CachedPixelShaderSetting<float, 4> m_material_color_setting{"materialColor"};
@@ -700,12 +700,8 @@ void ShaderSource::generateShader(ShaderInfo &shaderinfo)
)";
}
// map legacy semantic texture names to texture identifiers
fragment_header += R"(
#define baseTexture texture0
#define normalTexture texture1
#define textureFlags texture2
)";
// legacy semantic texture name
fragment_header += "#define baseTexture texture0\n";
/// Unique name of this shader, for debug/logging
std::string log_name = name;

View File

@@ -30,11 +30,8 @@ enum MaterialType : u8 {
#define MATERIAL_FLAG_BACKFACE_CULLING 0x01
// Should a crack be drawn?
#define MATERIAL_FLAG_CRACK 0x02
// 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
// Does this layer have texture animation?
#define MATERIAL_FLAG_ANIMATION 0x08
//#define MATERIAL_FLAG_HIGHLIGHTED 0x10
#define MATERIAL_FLAG_TILEABLE_HORIZONTAL 0x20
#define MATERIAL_FLAG_TILEABLE_VERTICAL 0x40
@@ -78,7 +75,7 @@ struct TileLayer
}
/*!
* Two tiles are not equal if they must have different vertices.
* Two layers are not equal if they must have different vertices.
*/
bool operator!=(const TileLayer &other) const
{
@@ -127,7 +124,6 @@ struct TileLayer
MaterialType material_type = TILE_MATERIAL_BASIC;
u8 material_flags =
//0 // <- DEBUG, Use the one below
MATERIAL_FLAG_BACKFACE_CULLING |
MATERIAL_FLAG_TILEABLE_HORIZONTAL|
MATERIAL_FLAG_TILEABLE_VERTICAL;