blood wall staining

This commit is contained in:
jacob 2026-02-14 09:08:40 -06:00
parent 6957be38b5
commit fff1b69eff
4 changed files with 131 additions and 101 deletions

View File

@ -2190,7 +2190,7 @@ void V_TickForever(WaveLaneCtx *lane)
quad->quad_uv_to_world_af = wep_uv_to_world_af; quad->quad_uv_to_world_af = wep_uv_to_world_af;
quad->tex = wep.tex; quad->tex = wep.tex;
quad->tex_slice_uv = DivRng2Vec2(wep.tex_rect, wep.tex_dims); quad->tex_slice_uv = DivRng2Vec2(wep.tex_rect, wep.tex_dims);
quad->occluder = V_OccluderKind_Guy; // quad->occluder = V_OccluderKind_Guy;
} }
//- Push body quad //- Push body quad
@ -2560,8 +2560,8 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Push test emitter //- Push test emitter
if (frame->held_buttons[Button_F]) // if (frame->held_buttons[Button_F])
// if (frame->held_buttons[Button_F] && !prev_frame->held_buttons[Button_F]) if (frame->held_buttons[Button_F] && !prev_frame->held_buttons[Button_F])
{ {
{ {
V_Emitter emitter = Zi; V_Emitter emitter = Zi;

View File

@ -346,8 +346,9 @@ ComputeShader(V_SimParticlesCS, 64)
b32 stepped_x = 0; b32 stepped_x = 0;
b32 stepped_y = 0; b32 stepped_y = 0;
// u32 max_iterations = 32; // TODO: Tune this
u32 max_iterations = 128; u32 max_iterations = 128;
b32 done = 0; b32 done = 0;
f32 t_diff = 0; f32 t_diff = 0;
for (u32 iteration_idx = 0; iteration_idx < max_iterations && !done; ++iteration_idx) for (u32 iteration_idx = 0; iteration_idx < max_iterations && !done; ++iteration_idx)
@ -393,32 +394,45 @@ ComputeShader(V_SimParticlesCS, 64)
V_OccluderKind occluder = (V_OccluderKind)occluders[cell_pos]; V_OccluderKind occluder = (V_OccluderKind)occluders[cell_pos];
if (occluder != V_OccluderKind_None) if (occluder != V_OccluderKind_None)
{ {
collision = 1; u64 collision_seed = MixU64(V_ParticleCellBasis ^ seed0 ^ particle.cells_count);
done = 1; f32 rand_collision_angle = Norm16(collision_seed >> 0);
f32 rand_collision_velocity = Norm16(collision_seed >> 16);
f32 rand_collision_penetration = Norm16(collision_seed >> 32);
if (rand_collision_penetration >= desc.pen_rate)
{ {
collision = 1;
done = 1;
{
if (stepped_x)
{
if (!AnyBit(desc.flags, V_ParticleFlag_NoReflect))
{
particle.velocity.x *= -1;
}
t = saturate(t_hit.x);
}
else if (stepped_y)
{
if (!AnyBit(desc.flags, V_ParticleFlag_NoReflect))
{
particle.velocity.y *= -1;
}
t = saturate(t_hit.y);
}
{
f32 collision_angle = lerp(-0.05 * Tau, 0.05 * Tau, rand_collision_angle);
if (stepped_x) f32 collision_velocity_falloff = lerp(50, 100, rand_collision_velocity);
{ // f32 collision_velocity_falloff = lerp(5000, 10000, rand_collision_velocity);
particle.velocity.x *= -1; // f32 collision_velocity_falloff = lerp(500, 10000, rand_collision_velocity);
t = saturate(t_hit.x); // f32 collision_velocity_falloff = 0;
particle.velocity = RotateVec2Angle(particle.velocity, collision_angle);
particle.velocity *= 1.0f - saturate(collision_velocity_falloff * frame.dt);
}
} }
else if (stepped_y)
{
particle.velocity.y *= -1;
t = saturate(t_hit.y);
}
{
u64 collision_seed = MixU64(V_ParticleCollisionBasis ^ seed0 ^ particle.collisions_count);
f32 rand_collision_angle = Norm16(collision_seed >> 0);
f32 rand_collision_velocity = Norm16(collision_seed >> 16);
f32 collision_angle = lerp(-0.05 * Tau, 0.05 * Tau, rand_collision_angle);
// f32 collision_velocity_falloff = lerp(0, 1, rand_collision_velocity);
f32 collision_velocity_falloff = 0;
particle.velocity = RotateVec2Angle(particle.velocity, collision_angle);
particle.velocity *= 1.0f - collision_velocity_falloff;
}
++particle.collisions_count;
} }
} }
if (AnyBit(desc.flags, V_ParticleFlag_PruneWhenStill)) if (AnyBit(desc.flags, V_ParticleFlag_PruneWhenStill))
@ -434,31 +448,34 @@ ComputeShader(V_SimParticlesCS, 64)
particle.stain_accum += 1; particle.stain_accum += 1;
} }
//- Stain if (!collision)
u32 stains_count = floor(particle.stain_accum);
if (stains_count > 0)
{ {
InterlockedMax(stain_cells[cell_pos], packed); //- Stain
InterlockedAdd(stain_densities[cell_pos], stains_count); u32 stains_count = floor(particle.stain_accum);
drynesses[cell_pos] = 0; if (stains_count > 0)
particle.stain_accum -= stains_count;
}
//- Draw
{
b32 should_draw_ground = !collision && is_visible && AnyBit(desc.flags, V_ParticleFlag_Ground);
b32 should_draw_air = !collision && is_visible && AnyBit(desc.flags, V_ParticleFlag_Air);
if (should_draw_ground)
{ {
InterlockedMax(ground_cells[cell_pos], packed); InterlockedMax(stain_cells[cell_pos], packed);
InterlockedAdd(ground_densities[cell_pos], 1); InterlockedAdd(stain_densities[cell_pos], stains_count);
drynesses[cell_pos] = 0;
particle.stain_accum -= stains_count;
} }
if (should_draw_air) //- Draw
{ {
InterlockedMax(air_cells[cell_pos], packed); b32 should_draw_ground = is_visible && AnyBit(desc.flags, V_ParticleFlag_Ground);
InterlockedAdd(air_densities[cell_pos], 1); b32 should_draw_air = is_visible && AnyBit(desc.flags, V_ParticleFlag_Air);
if (should_draw_ground)
{
InterlockedMax(ground_cells[cell_pos], packed);
InterlockedAdd(ground_densities[cell_pos], 1);
}
if (should_draw_air)
{
InterlockedMax(air_cells[cell_pos], packed);
InterlockedAdd(air_densities[cell_pos], 1);
}
} }
} }
} }
@ -467,6 +484,8 @@ ComputeShader(V_SimParticlesCS, 64)
done = 1; done = 1;
prune = 1; prune = 1;
} }
particle.cells_count += 1;
iteration_idx += 1; iteration_idx += 1;
} }
} }
@ -691,7 +710,7 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
stain_particle_color = V_ColorFromParticle(particle_kind, particle_idx, density, dryness); stain_particle_color = V_ColorFromParticle(particle_kind, particle_idx, density, dryness);
} }
} }
stain_particle_color.rgb *= 1.0 - (0.75 * tile_is_wall); // Darken wall stains stain_particle_color.rgb *= 1.0 - (0.30 * tile_is_wall); // Darken wall stains
stain_particle_color.rgb *= stain_particle_color.a; stain_particle_color.rgb *= stain_particle_color.a;
} }
//- Ground //- Ground

View File

@ -11,37 +11,43 @@ V_ParticleDesc V_DescFromParticleKind(V_ParticleKind kind)
V_ParticleDesc result; V_ParticleDesc result;
{ {
PERSIST Readonly V_ParticleFlag flags[V_ParticleKind_COUNT] = { PERSIST Readonly V_ParticleFlag flags[V_ParticleKind_COUNT] = {
#define X(name, flags, stain_rate, r, g, b, a) flags, #define X(name, flags, stain_rate, pen_rate, r, g, b, a) flags,
V_ParticlesXList(X) V_ParticlesXList(X)
#undef X #undef X
}; };
PERSIST Readonly f32 stain_rates[V_ParticleKind_COUNT] = { PERSIST Readonly f32 stain_rates[V_ParticleKind_COUNT] = {
#define X(name, flags, stain_rate, r, g, b, a) stain_rate, #define X(name, flags, stain_rate, pen_rate, r, g, b, a) stain_rate,
V_ParticlesXList(X)
#undef X
};
PERSIST Readonly f32 pen_rates[V_ParticleKind_COUNT] = {
#define X(name, flags, stain_rate, pen_rate, r, g, b, a) pen_rate,
V_ParticlesXList(X) V_ParticlesXList(X)
#undef X #undef X
}; };
PERSIST Readonly f32 r[V_ParticleKind_COUNT] = { PERSIST Readonly f32 r[V_ParticleKind_COUNT] = {
#define X(name, flags, stain_rate, r, g, b, a) r, #define X(name, flags, stain_rate, pen_rate, r, g, b, a) r,
V_ParticlesXList(X) V_ParticlesXList(X)
#undef X #undef X
}; };
PERSIST Readonly f32 g[V_ParticleKind_COUNT] = { PERSIST Readonly f32 g[V_ParticleKind_COUNT] = {
#define X(name, flags, stain_rate, r, g, b, a) g, #define X(name, flags, stain_rate, pen_rate, r, g, b, a) g,
V_ParticlesXList(X) V_ParticlesXList(X)
#undef X #undef X
}; };
PERSIST Readonly f32 b[V_ParticleKind_COUNT] = { PERSIST Readonly f32 b[V_ParticleKind_COUNT] = {
#define X(name, flags, stain_rate, r, g, b, a) b, #define X(name, flags, stain_rate, pen_rate, r, g, b, a) b,
V_ParticlesXList(X) V_ParticlesXList(X)
#undef X #undef X
}; };
PERSIST Readonly f32 a[V_ParticleKind_COUNT] = { PERSIST Readonly f32 a[V_ParticleKind_COUNT] = {
#define X(name, flags, stain_rate, r, g, b, a) a, #define X(name, flags, stain_rate, pen_rate, r, g, b, a) a,
V_ParticlesXList(X) V_ParticlesXList(X)
#undef X #undef X
}; };
result.flags = flags[kind]; result.flags = flags[kind];
result.stain_rate = stain_rates[kind]; result.stain_rate = stain_rates[kind];
result.pen_rate = pen_rates[kind];
result.color = LinearFromSrgb(VEC4(r[kind], g[kind], b[kind], a[kind])); result.color = LinearFromSrgb(VEC4(r[kind], g[kind], b[kind], a[kind]));
} }
return result; return result;

View File

@ -174,60 +174,64 @@ Enum(V_OccluderKind)
#define V_ParticleSimBasis 0xb49f2d9e406873b9ull #define V_ParticleSimBasis 0xb49f2d9e406873b9ull
#define V_ParticleColorBasis 0x569aa8341ecc0ea3ull #define V_ParticleColorBasis 0x569aa8341ecc0ea3ull
#define V_ParticleCollisionBasis 0xf60c0cff344b0c5dull #define V_ParticleCellBasis 0xf60c0cff344b0c5dull
#define V_ParticleStainBasis 0x3c64e8226d98d376ull #define V_ParticleStainBasis 0x3c64e8226d98d376ull
Enum(V_ParticleFlag) Enum(V_ParticleFlag)
{ {
V_ParticleFlag_None = 0, V_ParticleFlag_None = 0,
V_ParticleFlag_Ground = (1 << 0), V_ParticleFlag_Ground = (1 << 0),
V_ParticleFlag_Air = (1 << 1), V_ParticleFlag_Air = (1 << 1),
V_ParticleFlag_PruneWhenStill = (1 << 2), V_ParticleFlag_PruneWhenStill = (1 << 2),
V_ParticleFlag_StainWhenPruned = (1 << 3), V_ParticleFlag_StainWhenPruned = (1 << 3),
V_ParticleFlag_NoReflect = (1 << 4),
}; };
// NOTE: Higher particle enum values take priority over lower ones // NOTE: Higher particle enum values take priority over lower ones
#define V_ParticlesXList(X) \ #define V_ParticlesXList(X) \
X( \ X( \
None, \ /* Name */ None, \
V_ParticleFlag_None, \ /* Flags */ V_ParticleFlag_None, \
0, \ /* Stain rate, pen chance */ 0, 0, \
0, 0, 0, 0 \ /* Base color */ 0, 0, 0, 0 \
) \ ) \
/* Ground particles */ \ \
X( \ /* Ground particles */ \
Blood, \ X( \
V_ParticleFlag_Ground | V_ParticleFlag_PruneWhenStill, \ /* Name */ Blood, \
500, \ /* Flags */ V_ParticleFlag_None | V_ParticleFlag_NoReflect, \
0.5, 0.1, 0.1, 1 \ /* Stain rate, pen chance */ 500, 0.25, \
) \ /* Base color */ 0.5, 0.1, 0.1, 1 \
X( \ ) \
Debris, \ X( \
V_ParticleFlag_Ground | V_ParticleFlag_PruneWhenStill | V_ParticleFlag_StainWhenPruned , \ /* Name */ Debris, \
0, \ /* Flags */ V_ParticleFlag_Ground | V_ParticleFlag_PruneWhenStill | V_ParticleFlag_StainWhenPruned, \
1, 0.5, 0, 1 \ /* Stain rate, pen chance */ 0, 0, \
) \ /* Base color */ 1, 0.5, 0, 1 \
/* Air particles */ \ ) \
X( \ \
Smoke, \ /* Air particles */ \
V_ParticleFlag_Air, \ X( \
0, \ /* Name */ Smoke, \
0.15, 0.15, 0.15, 0.5 \ /* Flags */ V_ParticleFlag_Air, \
) \ /* Stain rate, pen chance */ 0, 0, \
X( \ /* Base color */ 0.15, 0.15, 0.15, 0.5 \
BulletTrail, \ ) \
V_ParticleFlag_Air, \ X( \
0, \ /* Name */ BulletTrail, \
1, 0, 1, 1 \ /* Flags */ V_ParticleFlag_Air, \
) \ /* Stain rate, pen chance */ 0, 0, \
/* Test particles */ \ /* Base color */ 1, 0, 1, 1 \
X( \ ) \
Test, \ \
V_ParticleFlag_PruneWhenStill, \ /* Test particles */ \
0, \ X( \
1, 1, 0, 1 \ /* Name */ Test, \
) \ /* Flags */ V_ParticleFlag_PruneWhenStill, \
/* -------------------------------------------------------------------------------- */ /* Stain rate, pen chance */ 0, 0, \
/* Base color */ 1, 1, 0, 1 \
) \
/* -------------------------------------------------------------------------- */
Enum(V_ParticleKind) Enum(V_ParticleKind)
{ {
@ -255,7 +259,7 @@ Struct(V_Particle)
i32 kind; // If >= 0, then map to V_ParticleKind. Otherwize initialize particle using emitter at index [abs(kind) - 1] i32 kind; // If >= 0, then map to V_ParticleKind. Otherwize initialize particle using emitter at index [abs(kind) - 1]
f32 life; f32 life;
f32 stain_accum; f32 stain_accum;
u32 collisions_count; u32 cells_count;
Vec2 pos; Vec2 pos;
Vec2 velocity; Vec2 velocity;
}; };
@ -264,6 +268,7 @@ Struct(V_ParticleDesc)
{ {
V_ParticleFlag flags; V_ParticleFlag flags;
f32 stain_rate; f32 stain_rate;
f32 pen_rate;
Vec4 color; Vec4 color;
}; };