From 6957be38b59b747dbf8a0f77f3fae030c1954e86 Mon Sep 17 00:00:00 2001 From: jacob Date: Sat, 14 Feb 2026 08:20:46 -0600 Subject: [PATCH] continuous particle staining --- src/pp/pp_vis/pp_vis_core.c | 12 +- src/pp/pp_vis/pp_vis_gpu.g | 435 ++++++++------------------------ src/pp/pp_vis/pp_vis_shared.cg | 16 +- src/pp/pp_vis/pp_vis_shared.cgh | 43 ++-- 4 files changed, 143 insertions(+), 363 deletions(-) diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 2d0b0295..4f1ab701 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -2190,7 +2190,7 @@ void V_TickForever(WaveLaneCtx *lane) quad->quad_uv_to_world_af = wep_uv_to_world_af; quad->tex = wep.tex; quad->tex_slice_uv = DivRng2Vec2(wep.tex_rect, wep.tex_dims); - quad->occluder = P_OccluderKind_Guy; + quad->occluder = V_OccluderKind_Guy; } //- Push body quad @@ -2202,7 +2202,7 @@ void V_TickForever(WaveLaneCtx *lane) quad->quad_uv_to_world_af = body_uv_to_world_af; quad->tex = body.tex; quad->tex_slice_uv = DivRng2Vec2(body.tex_rect, body.tex_dims); - quad->occluder = P_OccluderKind_Guy; + quad->occluder = V_OccluderKind_Guy; } } } @@ -2575,8 +2575,8 @@ void V_TickForever(WaveLaneCtx *lane) // f32 angle_spread = 0; // f32 speed = 5; - f32 speed = 25; - // f32 speed = 50; + // f32 speed = 25; + f32 speed = 50; // f32 speed = 100; f32 speed_spread = speed * 2; @@ -2639,8 +2639,8 @@ void V_TickForever(WaveLaneCtx *lane) // emitter.count = Mebi(16); // emitter.count = Mebi(2); // emitter.count = Kibi(32); - // emitter.count = Kibi(8); - emitter.count = 128; + emitter.count = Kibi(8); + // emitter.count = 128; // emitter.count = 128; // emitter.count = 32; // emitter.count = 1; diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g index aa245dfe..34bca99a 100644 --- a/src/pp/pp_vis/pp_vis_gpu.g +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -15,7 +15,7 @@ Vec4 V_ColorFromParticle(V_ParticleKind particle_kind, u32 particle_idx, u32 den V_ParticleDesc desc = V_DescFromParticleKind(particle_kind); Vec4 result = 0; - u64 seed = MixU64(P_ParticleColorBasis ^ particle_idx); + u64 seed = MixU64(V_ParticleColorBasis ^ particle_idx); f32 rand_color = Norm16(seed >> 0); result = desc.color; @@ -40,16 +40,22 @@ Vec4 V_ColorFromParticle(V_ParticleKind particle_kind, u32 particle_idx, u32 den if (particle_kind == V_ParticleKind_Smoke) { // f32 t = saturate(density / 10.0); - // f32 t = smoothstep(-10, 32, density); - f32 t = smoothstep(0, 2, (f32)density); + f32 t = smoothstep(-10, 32, density); + // f32 t = smoothstep(0, 2, (f32)density); result.a = lerp(0, 0.85, t); } else if (particle_kind == V_ParticleKind_Blood) { - f32 t = (f32)density / 10000; - t = pow(t, 2); - t = saturate(t); - result.rgb *= 1.0 - (t * 0.9); + // f32 t = (f32)density / 5; + // t = pow(t, 2); + // t = saturate(t); + // result.rgb *= 1.0 - (t * 0.9); + + f32 t = (f32)density / 5; + // t = smoothstep(-10, 10, t); + t = smoothstep(-5, 5, t); + // result.rgb *= 1.0 - (t * 0.9); + result.a = t; } } @@ -128,10 +134,10 @@ ComputeShader2D(V_PrepareCellsCS, 8, 8) air_densities[cell_pos] = 0; // Reset occluders - P_OccluderKind occluder = P_OccluderKind_None; + V_OccluderKind occluder = V_OccluderKind_None; if (tile == P_TileKind_Wall) { - occluder = P_OccluderKind_Wall; + occluder = V_OccluderKind_Wall; } occluders[cell_pos] = occluder; } @@ -198,8 +204,8 @@ PixelShader(V_QuadPS, V_QuadPSOutput, V_QuadPSInput input) if (is_in_world) { - // TODO: Remove this - if (albedo.a > 0 && quad.occluder != P_OccluderKind_None && is_in_world) + // TODO: Don't write occluders using screen space result. Do separate draw pass instead. + if (albedo.a > 0 && quad.occluder != V_OccluderKind_None && is_in_world) { InterlockedMax(occluders[cell_pos], quad.occluder); } @@ -234,7 +240,12 @@ ComputeShader(V_EmitParticlesCS, 64) for (u32 emitter_particle_idx = 0; emitter_particle_idx < emitter.count; ++emitter_particle_idx) { u32 particle_idx = (emitter.first_particle_seq + emitter_particle_idx) % (u32)V_ParticlesCap; - particles[particle_idx].kind = semantic_particle_kind; + + // InterlockedMin guarantees that the highest emitter index (reflected + // as negative particle kind) will be used to initialize the particle + // this frame, in case multiple emitters target the same particle (e.g. + // more particles pushed this frame than are available in the buffer) + InterlockedMin(particles[particle_idx].kind, semantic_particle_kind); } } } @@ -260,13 +271,14 @@ ComputeShader(V_SimParticlesCS, 64) if (particle_idx < V_ParticlesCap) { V_Particle particle = particles[particle_idx]; + b32 prune = 0; ////////////////////////////// //- Initialize particle - if (particle.kind != 0) + if (particle.kind != V_ParticleKind_None) { - u64 seed0 = MixU64(P_ParticleSimBasis ^ particle_idx); + u64 seed0 = MixU64(V_ParticleSimBasis ^ particle_idx); f32 rand_offset = Norm16(seed0 >> 0); f32 rand_angle = Norm16(seed0 >> 16); f32 rand_speed = Norm16(seed0 >> 32); @@ -283,23 +295,16 @@ ComputeShader(V_SimParticlesCS, 64) f32 initial_angle = lerp(emitter.angle.min, emitter.angle.max, rand_angle); f32 initial_speed = lerp(emitter.speed.min, emitter.speed.max, rand_speed); + particle = (V_Particle)0; particle.kind = emitter.kind; particle.life = 0; particle.pos = lerp(emitter.pos.p0, emitter.pos.p1, rand_offset); particle.velocity = Vec2(cos(initial_angle), sin(initial_angle)) * initial_speed; } - - - - - - - if (particle.kind > V_ParticleKind_None && particle.kind < V_ParticleKind_COUNT) { V_ParticleDesc desc = V_DescFromParticleKind((V_ParticleKind)particle.kind); - V_ParticleFlag flags = desc.flags; u32 packed = 0; packed |= (particle_idx & ((1 >> 24) - 1)) << 0; @@ -313,7 +318,6 @@ ComputeShader(V_SimParticlesCS, 64) b32 collision = 0; - // TODO: Use simulation dt // TODO: Clip to avoid unnecessary iterations outside of world bounds { Vec2 p0 = particle.pos; @@ -335,7 +339,7 @@ ComputeShader(V_SimParticlesCS, 64) t_max *= inv_delta; t_max = abs(t_max); - f32 t_hit = 0; + Vec2 t_hit = 0; Vec2I32 cell_pos = cell_p0; @@ -345,15 +349,9 @@ ComputeShader(V_SimParticlesCS, 64) // u32 max_iterations = 32; u32 max_iterations = 128; b32 done = 0; + f32 t_diff = 0; for (u32 iteration_idx = 0; iteration_idx < max_iterations && !done; ++iteration_idx) { - Vec2 cell_screen_pos_p0 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(floor(cell_pos), 1)), 1)); - Vec2 cell_screen_pos_p1 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(ceil(cell_pos), 1)), 1)); - cell_screen_pos_p1 = max(cell_screen_pos_p1, cell_screen_pos_p0 + 1); - - b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells)); - b32 is_visible = all(cell_screen_pos_p1 >= 0) && all(cell_screen_pos_p0 < frame.screen_dims); - if (cell_pos.x == cell_p1.x && cell_pos.y == cell_p1.y) { done = 1; @@ -361,7 +359,9 @@ ComputeShader(V_SimParticlesCS, 64) else if (t_max.x < t_max.y) { cell_pos.x += dda_step_dir.x; - t_hit = t_max.x - t_delta.x * 0.01; + f32 old = t_hit.x; + t_hit.x = t_max.x - t_delta.x; + t_diff = t_hit.x - old; t_max.x += t_delta.x; stepped_x = 1; stepped_y = 0; @@ -369,39 +369,85 @@ ComputeShader(V_SimParticlesCS, 64) else { cell_pos.y += dda_step_dir.y; - t_hit = t_max.y - t_delta.y * 0.01; + f32 old = t_hit.y; + t_hit.y = t_max.y - t_delta.y; + t_diff = t_hit.y - old; t_max.y += t_delta.y; stepped_x = 0; stepped_y = 1; } + Vec2 cell_screen_pos_p0 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(floor(cell_pos), 1)), 1)); + Vec2 cell_screen_pos_p1 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(ceil(cell_pos), 1)), 1)); + cell_screen_pos_p1 = max(cell_screen_pos_p1, cell_screen_pos_p0 + 1); + + b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells)); + b32 is_visible = all(cell_screen_pos_p1 >= 0) && all(cell_screen_pos_p0 < frame.screen_dims); + if (is_in_world) { - // Check collision - P_OccluderKind occluder = (P_OccluderKind)occluders[cell_pos]; - if (occluder != P_OccluderKind_None) + f32 stain_delta = abs(t_diff) * desc.stain_rate * frame.dt; + particle.stain_accum += stain_delta; + + //- Handle collision + V_OccluderKind occluder = (V_OccluderKind)occluders[cell_pos]; + if (occluder != V_OccluderKind_None) { - done = 1; collision = 1; - // particle.velocity *= 0.5; + done = 1; + { + + if (stepped_x) + { + particle.velocity.x *= -1; + t = saturate(t_hit.x); + } + 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; + } } - - // Draw + if (AnyBit(desc.flags, V_ParticleFlag_PruneWhenStill)) { - b32 should_stain = 0; - b32 should_draw_ground = !collision && is_visible && AnyBit(flags, V_ParticleFlag_Ground); - b32 should_draw_air = !collision && is_visible && AnyBit(flags, V_ParticleFlag_Air); - if (AnyBit(flags, V_ParticleFlag_Stain)) + if (dot(particle.velocity, particle.velocity) < 0.0001) { - should_stain = 1; + prune = 1; } + } - if (should_stain) - { - // InterlockedMax(stain_cells[cell_pos], packed); - // InterlockedAdd(stain_densities[cell_pos], 1); - } + if (prune && AnyBit(desc.flags, V_ParticleFlag_StainWhenPruned)) + { + particle.stain_accum += 1; + } + + //- Stain + u32 stains_count = floor(particle.stain_accum); + if (stains_count > 0) + { + InterlockedMax(stain_cells[cell_pos], packed); + InterlockedAdd(stain_densities[cell_pos], stains_count); + drynesses[cell_pos] = 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) { @@ -415,49 +461,14 @@ ComputeShader(V_SimParticlesCS, 64) InterlockedAdd(air_densities[cell_pos], 1); } } - - - - // stain_cells[cell_pos] = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); - // stain_cells[cell_pos] = Color_Black; - // ground_cells[cell_pos] = Color_White; - - // if (should_stain) - // { - // InterlockedMax(ground_cells[cell_pos], packed); - // InterlockedAdd(ground_densities[cell_pos], 1); - // } } else { done = 1; + prune = 1; } iteration_idx += 1; } - - if (collision) - { - t = saturate(t_hit); - if (stepped_x) - { - particle.velocity.x *= -1; - } - else if (stepped_y) - { - particle.velocity.y *= -1; - } - { - u64 collision_seed = MixU64s(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; - } } f32 falloff = saturate(lerp(10, 20, rand_falloff) * frame.dt); @@ -467,263 +478,15 @@ ComputeShader(V_SimParticlesCS, 64) particle.pos = p0 + (p1 - p0) * t; } - ////////////////////////////// - //- Commit - - // { - // Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); - // Vec2 cell_screen_pos_p0 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(floor(cell_pos), 1)), 1)); - // Vec2 cell_screen_pos_p1 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(ceil(cell_pos), 1)), 1)); - // cell_screen_pos_p1 = max(cell_screen_pos_p1, cell_screen_pos_p0 + 1); - - // b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells)); - // b32 is_visible = all(cell_screen_pos_p1 >= 0) && all(cell_screen_pos_p0 < frame.screen_dims); - - // b32 should_draw_ground = is_in_world && is_visible && AnyBit(flags, V_ParticleFlag_Ground); - // b32 should_draw_air = is_in_world && is_visible && AnyBit(flags, V_ParticleFlag_Air); - - // // Draw - // if (should_draw_ground || should_draw_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); - // } - // } - - // // Prune - // if (!is_in_world) - // { - // particle.kind = V_ParticleKind_None; - // } - - // // Increment life - // particle.life += frame.dt; - // } - - - { - Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); - b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells)); - - // Prune - if (!is_in_world) - { - particle.kind = V_ParticleKind_None; - } - // Increment life particle.life += frame.dt; - } - - } - else + + if (prune) { particle.kind = V_ParticleKind_None; } - - - - - - - - - - - - - - - - - - - - - - - // if (particle.kind > V_ParticleKind_None && particle.kind < V_ParticleKind_COUNT) - // { - // ////////////////////////////// - // //- Move - - // b32 collision = 0; - - // // TODO: Use simulation dt - // // TODO: Clip to avoid unnecessary iterations outside of world bounds - // { - // Vec2 p0 = particle.pos; - // Vec2 p1 = particle.pos + particle.velocity * frame.dt; - // f32 t = 1; - // { - // Vec2 occluder_p0 = mul(frame.af.world_to_cell, Vec3(p0, 1)); - // Vec2 occluder_p1 = mul(frame.af.world_to_cell, Vec3(p1, 1)); - // Vec2I32 dda_p0 = floor(occluder_p0); - // Vec2I32 dda_p1 = floor(occluder_p1); - - // Vec2 delta = occluder_p1 - occluder_p0; - // Vec2 inv_delta = 1.0 / delta; - // Vec2 dda_step_dir = Vec2((delta.x > 0) - (delta.x < 0), (delta.y > 0) - (delta.y < 0)); - // Vec2 t_delta = abs(inv_delta); - // Vec2 t_max = dda_p0 - occluder_p0; - // t_max.x += dda_step_dir.x > 0; - // t_max.y += dda_step_dir.y > 0; - // t_max *= inv_delta; - // t_max = abs(t_max); - - // f32 t_hit = 1; - - // Vec2I32 dda_pos = dda_p0; - - // b32 stepped_x = 0; - // b32 stepped_y = 0; - - // // u32 max_iterations = 32; - // u32 max_iterations = 128; - // b32 done = 0; - // for (u32 iteration_idx = 0; iteration_idx < max_iterations && !done; ++iteration_idx) - // { - // if (dda_pos.x == dda_p1.x && dda_pos.y == dda_p1.y) - // { - // done = 1; - // } - // else if (t_max.x < t_max.y) - // { - // dda_pos.x += dda_step_dir.x; - // t_hit = t_max.x - t_delta.x * 0.01; - // t_max.x += t_delta.x; - // stepped_x = 1; - // stepped_y = 0; - // } - // else - // { - // dda_pos.y += dda_step_dir.y; - // t_hit = t_max.y - t_delta.y * 0.01; - // t_max.y += t_delta.y; - // stepped_x = 0; - // stepped_y = 1; - // } - - // if (all(dda_pos >= 0) && all(dda_pos < countof(occluders))) - // { - // P_OccluderKind occluder = (P_OccluderKind)occluders[dda_pos]; - // if (occluder == P_OccluderKind_Wall) - // { - // done = 1; - // collision = 1; - // } - // } - // else - // { - // done = 1; - // } - // iteration_idx += 1; - // } - - // if (collision) - // { - // t = saturate(t_hit); - // if (stepped_x) - // { - // particle.velocity.x *= -1; - // } - // else if (stepped_y) - // { - // particle.velocity.y *= -1; - // } - // { - // u64 collision_seed = MixU64s(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; - // } - // } - - // f32 falloff = saturate(lerp(10, 20, rand_falloff) * frame.dt); - // // f32 falloff = saturate(lerp(1, 2, rand_falloff) * frame.dt); - // particle.velocity *= 1.0f - falloff; - - // particle.pos = p0 + (p1 - p0) * t; - // } - - // ////////////////////////////// - // //- Commit - - // Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); - // Vec2 cell_screen_pos_p0 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(floor(cell_pos), 1)), 1)); - // Vec2 cell_screen_pos_p1 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(ceil(cell_pos), 1)), 1)); - // cell_screen_pos_p1 = max(cell_screen_pos_p1, cell_screen_pos_p0 + 1); - - // b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells)); - // b32 is_visible = all(cell_screen_pos_p1 >= 0) && all(cell_screen_pos_p0 < frame.screen_dims); - - // // TODO: Particle based flags - // b32 is_stain_particle = particle.kind == V_ParticleKind_Debris || particle.kind == V_ParticleKind_Test; - // b32 is_ground_particle = particle.kind == V_ParticleKind_Debris || particle.kind == V_ParticleKind_Test; - // b32 is_air_particle = particle.kind == V_ParticleKind_Smoke; - - // b32 should_stain = is_stain_particle && is_in_world; - // b32 should_draw_ground = is_ground_particle && is_in_world && (is_visible || should_stain); - // b32 should_draw_air = is_air_particle && is_in_world && is_visible; - - // // Draw - // if (should_draw_ground || should_draw_air) - // { - // u32 packed = 0; - // packed |= (particle_idx & ((1 >> 24) - 1)) << 0; - // packed |= (particle.kind & 0xFF) << 24; - // StaticAssert(V_ParticlesCap <= (1 << 24)); // particle idx must fit in 24 bits - // StaticAssert(V_ParticleKind_COUNT <= 0xFF); // particle kind must fit in 8 bits - - // 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); - // } - // } - - // // Increment life - // particle.life += frame.dt; - - // // Prune - // if (!is_in_world) - // { - // particle.kind = V_ParticleKind_None; - // } - // } - // else - // { - // particle.kind = V_ParticleKind_None; - // } - - - - - - - particles[particle_idx] = particle; } } diff --git a/src/pp/pp_vis/pp_vis_shared.cg b/src/pp/pp_vis/pp_vis_shared.cg index 625d6cdf..029e5371 100644 --- a/src/pp/pp_vis/pp_vis_shared.cg +++ b/src/pp/pp_vis/pp_vis_shared.cg @@ -11,31 +11,37 @@ V_ParticleDesc V_DescFromParticleKind(V_ParticleKind kind) V_ParticleDesc result; { PERSIST Readonly V_ParticleFlag flags[V_ParticleKind_COUNT] = { - #define X(name, flags, r, g, b, a) flags, + #define X(name, flags, stain_rate, r, g, b, a) flags, + V_ParticlesXList(X) + #undef X + }; + PERSIST Readonly f32 stain_rates[V_ParticleKind_COUNT] = { + #define X(name, flags, stain_rate, r, g, b, a) stain_rate, V_ParticlesXList(X) #undef X }; PERSIST Readonly f32 r[V_ParticleKind_COUNT] = { - #define X(name, flags, r, g, b, a) r, + #define X(name, flags, stain_rate, r, g, b, a) r, V_ParticlesXList(X) #undef X }; PERSIST Readonly f32 g[V_ParticleKind_COUNT] = { - #define X(name, flags, r, g, b, a) g, + #define X(name, flags, stain_rate, r, g, b, a) g, V_ParticlesXList(X) #undef X }; PERSIST Readonly f32 b[V_ParticleKind_COUNT] = { - #define X(name, flags, r, g, b, a) b, + #define X(name, flags, stain_rate, r, g, b, a) b, V_ParticlesXList(X) #undef X }; PERSIST Readonly f32 a[V_ParticleKind_COUNT] = { - #define X(name, flags, r, g, b, a) a, + #define X(name, flags, stain_rate, r, g, b, a) a, V_ParticlesXList(X) #undef X }; result.flags = flags[kind]; + result.stain_rate = stain_rates[kind]; result.color = LinearFromSrgb(VEC4(r[kind], g[kind], b[kind], a[kind])); } return result; diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index 7caed4a2..3157b6e4 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -162,25 +162,28 @@ Struct(V_SharedFrame) //////////////////////////////////////////////////////////// //~ Occluder types -Enum(P_OccluderKind) +Enum(V_OccluderKind) { - P_OccluderKind_None, - P_OccluderKind_Guy, - P_OccluderKind_Wall, + V_OccluderKind_None, + V_OccluderKind_Guy, + V_OccluderKind_Wall, }; //////////////////////////////////////////////////////////// //~ Particle types -#define P_ParticleSimBasis 0xb49f2d9e406873b9ull -#define P_ParticleColorBasis 0x569aa8341ecc0ea3ull +#define V_ParticleSimBasis 0xb49f2d9e406873b9ull +#define V_ParticleColorBasis 0x569aa8341ecc0ea3ull +#define V_ParticleCollisionBasis 0xf60c0cff344b0c5dull +#define V_ParticleStainBasis 0x3c64e8226d98d376ull Enum(V_ParticleFlag) { - V_ParticleFlag_None = 0, - V_ParticleFlag_Stain = (1 << 0), - V_ParticleFlag_Ground = (1 << 1), - V_ParticleFlag_Air = (1 << 2), + V_ParticleFlag_None = 0, + V_ParticleFlag_Ground = (1 << 0), + V_ParticleFlag_Air = (1 << 1), + V_ParticleFlag_PruneWhenStill = (1 << 2), + V_ParticleFlag_StainWhenPruned = (1 << 3), }; // NOTE: Higher particle enum values take priority over lower ones @@ -188,34 +191,40 @@ Enum(V_ParticleFlag) X( \ None, \ V_ParticleFlag_None, \ + 0, \ 0, 0, 0, 0 \ ) \ /* Ground particles */ \ X( \ Blood, \ - V_ParticleFlag_Stain, \ - 0.5, 0.1, 0.1, 0.5 \ + V_ParticleFlag_Ground | V_ParticleFlag_PruneWhenStill, \ + 500, \ + 0.5, 0.1, 0.1, 1 \ ) \ X( \ Debris, \ - V_ParticleFlag_Ground | V_ParticleFlag_Stain, \ + V_ParticleFlag_Ground | V_ParticleFlag_PruneWhenStill | V_ParticleFlag_StainWhenPruned , \ + 0, \ 1, 0.5, 0, 1 \ ) \ /* Air particles */ \ X( \ Smoke, \ V_ParticleFlag_Air, \ + 0, \ 0.15, 0.15, 0.15, 0.5 \ ) \ X( \ BulletTrail, \ V_ParticleFlag_Air, \ + 0, \ 1, 0, 1, 1 \ ) \ /* Test particles */ \ X( \ Test, \ - V_ParticleFlag_Stain, \ + V_ParticleFlag_PruneWhenStill, \ + 0, \ 1, 1, 0, 1 \ ) \ /* -------------------------------------------------------------------------------- */ @@ -244,7 +253,8 @@ Struct(V_Emitter) Struct(V_Particle) { i32 kind; // If >= 0, then map to V_ParticleKind. Otherwize initialize particle using emitter at index [abs(kind) - 1] - f32 life; // How many seconds has this particle been alive for + f32 life; + f32 stain_accum; u32 collisions_count; Vec2 pos; Vec2 velocity; @@ -253,6 +263,7 @@ Struct(V_Particle) Struct(V_ParticleDesc) { V_ParticleFlag flags; + f32 stain_rate; Vec4 color; }; @@ -275,7 +286,7 @@ Enum(V_QuadFlag) Struct(V_Quad) { V_QuadFlag flags; - P_OccluderKind occluder; + V_OccluderKind occluder; Affine quad_uv_to_world_af; G_Texture2DRef tex; Rng2 tex_slice_uv;