From f16a102cfaa165666ba96333d2f2bee6a818a8f0 Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 13 Feb 2026 02:44:18 -0600 Subject: [PATCH] particle tile collisions --- src/base/base_math.c | 2 +- src/meta/meta.c | 10 +- src/pp/pp_vis/pp_vis_core.c | 394 +++++++++++++++++--------------- src/pp/pp_vis/pp_vis_gpu.g | 327 ++++++++++++++++++-------- src/pp/pp_vis/pp_vis_shared.cgh | 51 ++--- 5 files changed, 457 insertions(+), 327 deletions(-) diff --git a/src/base/base_math.c b/src/base/base_math.c index 5c1bcfaf..cca11bf3 100644 --- a/src/base/base_math.c +++ b/src/base/base_math.c @@ -56,7 +56,7 @@ i32 SignF64(f64 v) f32 Norm8(u32 v) { - return (v & 0xFF) / (f32)0x1FF; + return (v & 0xFF) / (f32)0x100; } f32 Norm16(u32 v) diff --git a/src/meta/meta.c b/src/meta/meta.c index c57168a6..1a411031 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -519,22 +519,14 @@ void M_BuildEntryPoint(WaveLaneCtx *lane) PushStringToList(perm, &cp.warnings_clang, Lit("-Wframe-larger-than=1048575")); PushStringToList(perm, &cp.warnings_clang, Lit("-Wmissing-prototypes")); PushStringToList(perm, &cp.warnings_clang, Lit("-Wmissing-declarations")); - PushStringToList(perm, &cp.warnings_clang, Lit("-Wunused-variable")); - PushStringToList(perm, &cp.warnings_clang, Lit("-Wunused-but-set-variable")); - PushStringToList(perm, &cp.warnings_clang, Lit("-Wunused-parameter")); PushStringToList(perm, &cp.warnings_clang, Lit("-Wimplicit-fallthrough")); - PushStringToList(perm, &cp.warnings_clang, Lit("-Wswitch")); // Disable warnings PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-initializer-overrides")); PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-microsoft-enum-forward-reference")); PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-microsoft-anon-tag")); - PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-unused-variable")); - PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-unused-but-set-variable")); - PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-unused-parameter")); PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-incompatible-function-pointer-types")); PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-missing-braces")); - PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-switch")); PushStringToList(perm, &cp.warnings_clang, Lit("-Wno-unused-value")); } @@ -546,7 +538,7 @@ void M_BuildEntryPoint(WaveLaneCtx *lane) // Enable warnings PushStringToList(perm, &cp.warnings_dxc, Lit("-Wall")); - PushStringToList(perm, &cp.warnings_dxc, Lit("-WX")); + PushStringToList(perm, &cp.warnings_dxc, Lit("-Werror")); PushStringToList(perm, &cp.warnings_dxc, Lit("-Wshadow")); // Disable warnings diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 730826c6..dd203917 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -2150,78 +2150,78 @@ void V_TickForever(WaveLaneCtx *lane) // TODO: Not like this - for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) - { - if (bullet->is_bullet) - { - Vec2 start = bullet->bullet_start; - Vec2 end = bullet->bullet_end; + // for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) + // { + // if (bullet->is_bullet) + // { + // Vec2 start = bullet->bullet_start; + // Vec2 end = bullet->bullet_end; - b32 skip = 0; - if (bullet->has_hit) - { - Vec2 hit_pos = bullet->hit_entry; - if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0) - { - skip = 1; - } - // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red); - // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple); - end = hit_pos; - } + // b32 skip = 0; + // if (bullet->has_hit) + // { + // Vec2 hit_pos = bullet->hit_entry; + // if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0) + // { + // skip = 1; + // } + // // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red); + // // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple); + // end = hit_pos; + // } - if (!skip) - { - f32 trail_len = Vec2Len(SubVec2(end, start)); - f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second - particles_count = MaxF32(particles_count, 1); + // if (!skip) + // { + // f32 trail_len = Vec2Len(SubVec2(end, start)); + // f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second + // particles_count = MaxF32(particles_count, 1); - f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); - // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); + // f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start))); + // // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start))); - V_Emitter emitter = Zi; - { - // emitter.flags |= V_ParticleFlag_StainOnPrune; - // emitter.flags |= V_ParticleFlag_StainTrail; + // V_Emitter emitter = Zi; + // { + // // emitter.flags |= V_ParticleFlag_StainOnPrune; + // // emitter.flags |= V_ParticleFlag_StainTrail; - // emitter.lifetime = 1; - // emitter.lifetime_spread = 2; + // // emitter.lifetime = 1; + // // emitter.lifetime_spread = 2; - emitter.count = particles_count; + // emitter.count = particles_count; - // emitter.lifetime = 1; - // emitter.lifetime = 0.15; - emitter.lifetime = 0.075; - // emitter.lifetime = 0.05; - // emitter.lifetime = 0.04; - emitter.lifetime_spread = emitter.lifetime * 2; + // // emitter.lifetime = 1; + // // emitter.lifetime = 0.15; + // emitter.lifetime = 0.075; + // // emitter.lifetime = 0.05; + // // emitter.lifetime = 0.04; + // emitter.lifetime_spread = emitter.lifetime * 2; - emitter.angle = angle; - // emitter.angle_spread = Tau / 4; - emitter.angle_spread = Tau / 4; + // emitter.angle = angle; + // // emitter.angle_spread = Tau / 4; + // emitter.angle_spread = Tau / 4; - emitter.start = start; - emitter.end = end; + // emitter.start = start; + // emitter.end = end; - // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); + // // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1)); - // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.8, 0.8, 0.25)); - emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); - // emitter.color_spread = LinearFromSrgb(VEC4(0, 0, 0, 0.2)); + // // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.8, 0.8, 0.25)); + // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1)); + // // emitter.color_spread = LinearFromSrgb(VEC4(0, 0, 0, 0.2)); - emitter.speed = 0; - emitter.speed_spread = 1; + // emitter.speed = 0; + // emitter.speed_spread = 1; - // emitter.speed = 1; - // emitter.speed_spread = 1; + // // emitter.speed = 1; + // // emitter.speed_spread = 1; - // emitter.velocity_falloff = 1; - // emitter.velocity_falloff_spread = 0; - } - V_PushParticles(emitter); - } - } - } + // // emitter.velocity_falloff = 1; + // // emitter.velocity_falloff_spread = 0; + // } + // V_PushParticles(emitter); + // } + // } + // } @@ -2234,156 +2234,157 @@ void V_TickForever(WaveLaneCtx *lane) // if (0) - { - for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) - { - if (bullet->is_bullet && bullet->has_hit) - { - P_MaterialKind material = bullet->hit_material; + // { + // for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet)) + // { + // if (bullet->is_bullet && bullet->has_hit) + // { + // P_MaterialKind material = bullet->hit_material; - Vec2 hit_entry = bullet->hit_entry; - Vec2 hit_entry_normal = bullet->hit_entry_normal; - Vec2 bullet_vel = SubVec2(bullet->bullet_end, bullet->bullet_start); + // Vec2 hit_entry = bullet->hit_entry; + // Vec2 hit_entry_normal = bullet->hit_entry_normal; + // Vec2 bullet_vel = SubVec2(bullet->bullet_end, bullet->bullet_start); - V_DrawLine(bullet->bullet_start, bullet->bullet_end, Color_Cyan); + // V_DrawLine(bullet->bullet_start, bullet->bullet_end, Color_Cyan); - ////////////////////////////// - //- Wall particles + // ////////////////////////////// + // //- Wall particles - if (material != P_MaterialKind_Flesh) - { - //- Wall debris - { - V_Emitter emitter = Zi; - { - emitter.flags |= V_ParticleFlag_PruneWhenStill; - emitter.flags |= V_ParticleFlag_StainOnPrune; + // if (material != P_MaterialKind_Flesh) + // { + // //- Wall debris + // { + // V_Emitter emitter = Zi; + // { + // // emitter.flags |= V_ParticleFlag_PruneWhenStill; + // // emitter.flags |= V_ParticleFlag_StainOnPrune; - emitter.count = 4; - emitter.start = hit_entry; - emitter.end = emitter.start; + // emitter.count = 4; - emitter.color_lin = LinearFromSrgb(VEC4(0.4, 0.3, 0.2, 0.75)); - emitter.color_spread = VEC4(0, 0, 0, 0.25); + // emitter.start = hit_entry; + // emitter.end = emitter.start; - emitter.speed = 2; - // emitter.speed_spread = emitter.speed * 2; - emitter.speed_spread = emitter.speed * 2.5; + // emitter.color_lin = LinearFromSrgb(VEC4(0.4, 0.3, 0.2, 0.75)); + // emitter.color_spread = VEC4(0, 0, 0, 0.25); - emitter.velocity_falloff = 5; - emitter.velocity_falloff_spread = emitter.velocity_falloff_spread * 1.5; + // emitter.speed = 2; + // // emitter.speed_spread = emitter.speed * 2; + // emitter.speed_spread = emitter.speed * 2.5; - Vec2 dir = hit_entry_normal; + // emitter.velocity_falloff = 5; + // emitter.velocity_falloff_spread = emitter.velocity_falloff_spread * 1.5; - emitter.angle = AngleFromVec2(dir); - emitter.angle_spread = Tau * 0.5; + // Vec2 dir = hit_entry_normal; - // emitter.lifetime = 0.25; - // emitter.lifetime = 0.05; - // emitter.lifetime = 0.04; - emitter.lifetime_spread = emitter.lifetime * 2; - } - V_PushParticles(emitter); - } + // emitter.angle = AngleFromVec2(dir); + // emitter.angle_spread = Tau * 0.5; - //- Wall dust - { - V_Emitter emitter = Zi; - { - // emitter.flags |= V_ParticleFlag_PruneWhenStill; + // // emitter.lifetime = 0.25; + // // emitter.lifetime = 0.05; + // // emitter.lifetime = 0.04; + // emitter.lifetime_spread = emitter.lifetime * 2; + // } + // V_PushParticles(emitter); + // } - emitter.count = 32; + // //- Wall dust + // { + // V_Emitter emitter = Zi; + // { + // // emitter.flags |= V_ParticleFlag_PruneWhenStill; - emitter.start = hit_entry; - emitter.end = emitter.start; + // emitter.count = 32; - emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.5, 0.5, 0.75)); + // emitter.start = hit_entry; + // emitter.end = emitter.start; - emitter.speed = 4; - // emitter.speed_spread = emitter.speed * 2; - emitter.speed_spread = emitter.speed * 2; + // emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.5, 0.5, 0.75)); - emitter.velocity_falloff = 12; - emitter.velocity_falloff_spread = emitter.velocity_falloff_spread * 1.5; + // emitter.speed = 4; + // // emitter.speed_spread = emitter.speed * 2; + // emitter.speed_spread = emitter.speed * 2; - Vec2 dir = hit_entry_normal; + // emitter.velocity_falloff = 12; + // emitter.velocity_falloff_spread = emitter.velocity_falloff_spread * 1.5; - emitter.angle = AngleFromVec2(dir); - emitter.angle_spread = Tau * 0.1; + // Vec2 dir = hit_entry_normal; - emitter.lifetime = 1; - // emitter.lifetime = 0.05; - // emitter.lifetime = 0.04; - emitter.lifetime_spread = emitter.lifetime * 2; - } - V_PushParticles(emitter); - } - } + // emitter.angle = AngleFromVec2(dir); + // emitter.angle_spread = Tau * 0.1; - ////////////////////////////// - //- Blood particles + // emitter.lifetime = 1; + // // emitter.lifetime = 0.05; + // // emitter.lifetime = 0.04; + // emitter.lifetime_spread = emitter.lifetime * 2; + // } + // V_PushParticles(emitter); + // } + // } - if (material == P_MaterialKind_Flesh) - { - // Vec2 bullet_start = bullet->start; - // Vec2 bullet_end = bullet->end; + // ////////////////////////////// + // //- Blood particles - V_ParticleFlag flags = 0; - flags |= V_ParticleFlag_PruneWhenStill; - flags |= V_ParticleFlag_StainOnPrune; - if (TweakBool("Emitter stain trail", 1)) - { - flags |= V_ParticleFlag_StainTrail; - } - // f32 count = TweakFloat("Emitter count", 50, 0, 10000); - f32 count = TweakFloat("Emitter count", 20, 1, 1000); - f32 speed = TweakFloat("Emitter speed", 20, 0, 100); - f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100); - f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau; + // if (material == P_MaterialKind_Flesh) + // { + // // Vec2 bullet_start = bullet->start; + // // Vec2 bullet_end = bullet->end; - V_Emitter emitter = Zi; - { - emitter.count = count; - emitter.flags = flags; + // V_ParticleFlag flags = 0; + // flags |= V_ParticleFlag_PruneWhenStill; + // flags |= V_ParticleFlag_StainOnPrune; + // if (TweakBool("Emitter stain trail", 1)) + // { + // flags |= V_ParticleFlag_StainTrail; + // } + // // f32 count = TweakFloat("Emitter count", 50, 0, 10000); + // f32 count = TweakFloat("Emitter count", 20, 1, 1000); + // f32 speed = TweakFloat("Emitter speed", 20, 0, 100); + // f32 falloff = TweakFloat("Emitter falloff", 50, 0, 100); + // f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau; - // Vec2 dir = hit_entry_normal; - Vec2 dir = NormVec2(NegVec2(bullet_vel)); + // V_Emitter emitter = Zi; + // { + // emitter.count = count; + // emitter.flags = flags; - emitter.start = hit_entry; - emitter.end = emitter.start; + // // Vec2 dir = hit_entry_normal; + // Vec2 dir = NormVec2(NegVec2(bullet_vel)); - emitter.speed = speed; - emitter.speed_spread = speed * 2; + // emitter.start = hit_entry; + // emitter.end = emitter.start; - emitter.velocity_falloff = falloff; - emitter.velocity_falloff_spread = falloff * 1.5; + // emitter.speed = speed; + // emitter.speed_spread = speed * 2; - emitter.angle = AngleFromVec2(dir); - // emitter.angle_spread = Tau / 4; - emitter.angle_spread = angle_spread; - // emitter.angle_spread = Tau / 32; + // emitter.velocity_falloff = falloff; + // emitter.velocity_falloff_spread = falloff * 1.5; - // emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1)); - emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 0.5)); + // emitter.angle = AngleFromVec2(dir); + // // emitter.angle_spread = Tau / 4; + // emitter.angle_spread = angle_spread; + // // emitter.angle_spread = Tau / 32; - // emitter.color_spread = VEC4(0.1, 0, 0, 0); - emitter.color_spread = VEC4(0.1, 0, 0, 0.5); + // // emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1)); + // emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 0.5)); - // emitter.color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); + // // emitter.color_spread = VEC4(0.1, 0, 0, 0); + // emitter.color_spread = VEC4(0.1, 0, 0, 0.5); - // emitter.angle_spread = 1; - // emitter.angle_spread = 0.5; - // emitter.angle_spread = Tau; - } - V_PushParticles(emitter); - } + // // emitter.color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); - // V_DrawPoint(victim_raycast.p, Color_Green); - // V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White); - } - } - } + // // emitter.angle_spread = 1; + // // emitter.angle_spread = 0.5; + // // emitter.angle_spread = Tau; + // } + // V_PushParticles(emitter); + // } + + // // V_DrawPoint(victim_raycast.p, Color_Green); + // // V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White); + // } + // } + // } @@ -2499,36 +2500,49 @@ void V_TickForever(WaveLaneCtx *lane) - ////////////////////////////// //- Push test emitter - if (frame->held_buttons[Button_G]) + // if (frame->held_buttons[Button_G]) + if (frame->held_buttons[Button_G] && !prev_frame->held_buttons[Button_G]) { V_Emitter emitter = Zi; - Vec2 dir = frame->look; - emitter.start = frame->world_cursor; - emitter.end = emitter.start; - emitter.angle = AngleFromVec2(dir); + emitter.kind = V_ParticleKind_Test; - // emitter.flags |= V_ParticleFlag_StainTrail; + f32 angle = AngleFromVec2(frame->look); + // f32 angle = 0; + // f32 angle_spread = Tau * 0.25; + f32 angle_spread = Tau; + // f32 angle_spread = 0; + f32 speed = 25; + // f32 speed = 50; + // f32 speed = 1000; + f32 speed_spread = speed * 2; + + emitter.pos.p0 = emitter.pos.p1 = frame->world_cursor; + emitter.speed.min = speed - speed_spread * 0.5; + emitter.speed.max = speed + speed_spread * 0.5; + emitter.angle.min = angle - angle_spread * 0.5; + emitter.angle.max = angle + angle_spread * 0.5; + + // emitter.falloff.min = emitter.falloff.max = 0; + + // emitter.count = CeilF32(Kibi(64) * frame->dt); + // emitter.count = Mebi(16); + // emitter.count = Mebi(2); + // emitter.count = Kibi(32); + emitter.count = Kibi(8); + // emitter.count = Kibi(1); // emitter.count = 128; - emitter.count = CeilF32(Kibi(16) * frame->dt); - emitter.speed = 10; - - emitter.color_lin = LinearFromSrgb(Color_Yellow); - - emitter.speed_spread = 1; - // emitter.angle_spread = 1; - // emitter.angle_spread = 0.5; - emitter.angle_spread = Tau / 4; - // emitter.angle_spread = Tau; + // emitter.count = 32; + // emitter.count = 1; V_PushParticles(emitter); } + ////////////////////////////// //- Debug draw diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g index 220e3fda..a28c5621 100644 --- a/src/pp/pp_vis/pp_vis_gpu.g +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -72,7 +72,7 @@ ComputeShader(V_ClearParticlesCS, 64) u32 particle_idx = SV_DispatchThreadID; if (particle_idx < V_ParticlesCap) { - particles[particle_idx].exists = 0; + particles[particle_idx].kind = V_ParticleKind_None; } } @@ -136,13 +136,15 @@ ComputeShader(V_EmitParticlesCS, 64) if (emitter_idx < frame.emitters_count) { V_Emitter emitter = emitters[emitter_idx]; + i32 semantic_particle_kind = V_ParticleKind_None; + if (emitter.kind > V_ParticleKind_None) + { + semantic_particle_kind = (i32)(emitter_idx + 1) * -1; + } for (u32 emitter_particle_idx = 0; emitter_particle_idx < emitter.count; ++emitter_particle_idx) { - u32 particle_seq = emitter.first_particle_seq + emitter_particle_idx; - u32 particle_idx = particle_seq % (u32)V_ParticlesCap; - particles[particle_idx].exists = 1; - particles[particle_idx].emitter_init_num = emitter_idx + 1; - particles[particle_idx].seq = particle_seq; + u32 particle_idx = (emitter.first_particle_seq + emitter_particle_idx) % (u32)V_ParticlesCap; + particles[particle_idx].kind = semantic_particle_kind; } } } @@ -157,130 +159,269 @@ ComputeShader(V_SimParticlesCS, 64) RWTexture2D cells = G_Dereference(frame.cells); RWTexture2D stains = G_Dereference(frame.stains); RWTexture2D drynesses = G_Dereference(frame.drynesses); + Texture2D tiles = G_Dereference(frame.tiles); u32 particle_idx = SV_DispatchThreadID; if (particle_idx < V_ParticlesCap) { V_Particle particle = particles[particle_idx]; - if (particle.exists > 0) + + ////////////////////////////// + //- Initialize particle + + if (particle.kind != 0) { + u64 seed0 = MixU64(particle_idx); + f32 rand_offset = Norm16(seed0 >> 0); + f32 rand_angle = Norm16(seed0 >> 16); + f32 rand_speed = Norm16(seed0 >> 32); + f32 rand_falloff = Norm16(seed0 >> 48); + ////////////////////////////// - //- Initialize particle + //- Init - if (particle.emitter_init_num != 0) + if (particle.kind < 0) { - V_Emitter emitter = G_Dereference(frame.emitters)[particle.emitter_init_num - 1]; + u32 emitter_idx = -particle.kind - 1; + V_Emitter emitter = G_Dereference(frame.emitters)[emitter_idx]; - u64 seed0 = MixU64(particle.seq); - u64 seed1 = MixU64(seed0); + f32 initial_angle = lerp(emitter.angle.min, emitter.angle.max, rand_angle); + f32 initial_speed = lerp(emitter.speed.min, emitter.speed.max, rand_speed); - f32 rand_speed = Norm16(seed0 >> 0); - f32 rand_angle = Norm16(seed0 >> 16); - f32 rand_offset = Norm16(seed0 >> 32); - f32 rand_falloff = Norm16(seed0 >> 48); - - f32 rand_r = Norm8(seed1 >> 0); - f32 rand_g = Norm8(seed1 >> 8); - f32 rand_b = Norm8(seed1 >> 16); - f32 rand_a = Norm8(seed1 >> 24); - f32 rand_lifetime = Norm16(seed1 >> 32); - - f32 speed = emitter.speed + (rand_speed - 0.5) * emitter.speed_spread; - f32 angle = emitter.angle + (rand_angle - 0.5) * emitter.angle_spread; - f32 velocity_falloff = emitter.velocity_falloff + (rand_falloff - 0.5) * emitter.velocity_falloff_spread; - f32 lifetime = emitter.lifetime + (rand_lifetime - 0.5) * emitter.lifetime_spread; - Vec4 color = emitter.color_lin + (Vec4(rand_r, rand_g, rand_b, rand_a) - 0.5) * emitter.color_spread; - Vec2 offset = (emitter.end - emitter.start) * rand_offset; - - particle.flags = emitter.flags; - particle.pos = emitter.start + offset; - particle.velocity = Vec2(cos(angle), sin(angle)) * speed; - particle.velocity_falloff = velocity_falloff; - particle.color = color; - particle.lifetime = lifetime; - if (emitter.lifetime == 0) - { - particle.lifetime = Inf; - } - - particle.emitter_init_num = 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; } - ////////////////////////////// - //- Simulate particle - - f32 prev_exists = particle.exists; + if (particle.kind > V_ParticleKind_None && particle.kind < V_ParticleKind_COUNT) { - 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(cells)); + ////////////////////////////// + //- Move - // Simulate + b32 collision = 0; + + // TODO: Use simulation dt + // TODO: Clip to avoid unnecessary iterations outside of world bounds { - // TODO: Use simulation dt - particle.pos += particle.velocity * frame.dt; - particle.velocity = lerp(particle.velocity, 0, particle.velocity_falloff * frame.dt); - particle.exists -= frame.dt / particle.lifetime; - if ((particle.flags & V_ParticleFlag_PruneWhenStill) && (dot(particle.velocity, particle.velocity) < (0.1 * 0.1))) + Vec2 p0 = particle.pos; + Vec2 p1 = particle.pos + particle.velocity * frame.dt; + f32 t = 1; { - particle.exists = 0; - } - if (particle.exists < 0.000001) - { - particle.exists = 0; + Vec2 tile_p0 = mul(frame.af.world_to_tile, Vec3(p0, 1)); + Vec2 tile_p1 = mul(frame.af.world_to_tile, Vec3(p1, 1)); + Vec2I32 grid_p0 = floor(tile_p0); + Vec2I32 grid_p1 = floor(tile_p1); + + Vec2 delta = tile_p1 - tile_p0; + Vec2 inv_delta = 1.0 / delta; + Vec2 grid_step_dir = Vec2((delta.x > 0) - (delta.x < 0), (delta.y > 0) - (delta.y < 0)); + Vec2 t_delta = abs(inv_delta); + Vec2 t_crossover_next = grid_p0 - tile_p0; + t_crossover_next.x += grid_step_dir.x > 0; + t_crossover_next.y += grid_step_dir.y > 0; + t_crossover_next *= inv_delta; + t_crossover_next = abs(t_crossover_next); + + f32 t_hit = 0; + + Vec2I32 grid_pos = grid_p0; + + b32 stepped_x = 0; + b32 stepped_y = 0; + + // TODO: Tune this + u32 max_iterations = 32; + + u32 iteration_idx = 0; + b32 done = 0; + while (!done && iteration_idx < max_iterations) + { + if (grid_pos.x == grid_p1.x && grid_pos.y == grid_p1.y) + { + done = 1; + } + else if (t_crossover_next.x < t_crossover_next.y) + { + grid_pos.x += grid_step_dir.x; + t_hit = t_crossover_next.x - t_delta.x; + t_crossover_next.x += t_delta.x; + stepped_x = 1; + stepped_y = 0; + } + else + { + grid_pos.y += grid_step_dir.y; + t_hit = t_crossover_next.y - t_delta.y; + t_crossover_next.y += t_delta.y; + stepped_x = 0; + stepped_y = 1; + } + + if (all(grid_pos >= 0) && all(grid_pos < countof(tiles))) + { + P_TileKind tile = tiles[grid_pos]; + if (tile == P_TileKind_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(particle_idx, 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; + f32 x = particle.velocity.x; + f32 y = particle.velocity.y; + f32 c = cos(collision_angle); + f32 s = sin(collision_angle); + particle.velocity.x = x * c - y * s; + particle.velocity.y = x * s + y * c; + 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 particle + ////////////////////////////// + //- Commit - // FIXME: Atomic writes + // FIXME: Atomic writes - { Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); Vec2 screen_pos = mul(frame.af.world_to_screen, Vec3(particle.pos, 1)); b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(cells)); b32 is_in_screen = all(screen_pos >= 0) && all(screen_pos < frame.screen_dims); - Vec4 color = particle.color; - color.a *= prev_exists; + b32 should_draw = is_in_world && is_in_screen; + b32 should_stain = 0; - // Stain - if (is_in_world) + // TODO: Remove this + Vec4 color = Color_Purple; + switch (particle.kind) { - b32 should_stain = ( - AnyBit(particle.flags, V_ParticleFlag_StainTrail) || - (AnyBit(particle.flags, V_ParticleFlag_StainOnPrune) && particle.exists == 0) - ); - - if (should_stain) + case V_ParticleKind_Test: { - f32 old_dryness = drynesses[cell_pos]; - Vec4 old_stain = stains[cell_pos]; - // old_stain = V_DryColor(old_stain, drynesses[cell_pos] * 0.5); - // old_stain = V_DryColor(old_stain, old_dryness); - Vec4 new_stain = 0; - new_stain.rgb = (color.rgb * color.a) + (old_stain.rgb * (1.0 - color.a)); - new_stain.a = color.a + (old_stain.a * (1.0 - color.a)); - // new_stain = V_DryColor(new_stain, old_dryness * 0.1); - // new_stain = V_DryColor(new_stain, old_dryness * 0.5); - - stains[cell_pos] = new_stain; - drynesses[cell_pos] = 0; - } + color = Color_Yellow; + } break; } + + // // Stain + // if (is_in_world) + // { + // b32 should_stain = ( + // AnyBit(particle.flags, V_ParticleFlag_StainTrail) || + // (AnyBit(particle.flags, V_ParticleFlag_StainOnPrune) && particle.exists == 0) + // ); + + // if (should_stain) + // { + // f32 old_dryness = drynesses[cell_pos]; + // Vec4 old_stain = stains[cell_pos]; + // // old_stain = V_DryColor(old_stain, drynesses[cell_pos] * 0.5); + // // old_stain = V_DryColor(old_stain, old_dryness); + // Vec4 new_stain = 0; + // new_stain.rgb = (color.rgb * color.a) + (old_stain.rgb * (1.0 - color.a)); + // new_stain.a = color.a + (old_stain.a * (1.0 - color.a)); + // // new_stain = V_DryColor(new_stain, old_dryness * 0.1); + // // new_stain = V_DryColor(new_stain, old_dryness * 0.5); + + // stains[cell_pos] = new_stain; + // drynesses[cell_pos] = 0; + // } + // } + // Draw + if (should_draw) { - b32 should_draw = is_in_world; - - if (should_draw) - { - cells[cell_pos] = color; - } + cells[cell_pos] = color; } + // { + // Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); + // Vec2 screen_pos = mul(frame.af.world_to_screen, Vec3(particle.pos, 1)); + // b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(cells)); + // b32 is_in_screen = all(screen_pos >= 0) && all(screen_pos < frame.screen_dims); + + // Vec4 color = particle.color; + // color.a *= prev_exists; + + // // Stain + // if (is_in_world) + // { + // b32 should_stain = ( + // AnyBit(particle.flags, V_ParticleFlag_StainTrail) || + // (AnyBit(particle.flags, V_ParticleFlag_StainOnPrune) && particle.exists == 0) + // ); + + // if (should_stain) + // { + // f32 old_dryness = drynesses[cell_pos]; + // Vec4 old_stain = stains[cell_pos]; + // // old_stain = V_DryColor(old_stain, drynesses[cell_pos] * 0.5); + // // old_stain = V_DryColor(old_stain, old_dryness); + // Vec4 new_stain = 0; + // new_stain.rgb = (color.rgb * color.a) + (old_stain.rgb * (1.0 - color.a)); + // new_stain.a = color.a + (old_stain.a * (1.0 - color.a)); + // // new_stain = V_DryColor(new_stain, old_dryness * 0.1); + // // new_stain = V_DryColor(new_stain, old_dryness * 0.5); + + // stains[cell_pos] = new_stain; + // drynesses[cell_pos] = 0; + // } + // } + + // // Draw + // { + // b32 should_draw = is_in_world; + + // if (should_draw) + // { + // cells[cell_pos] = color; + // } + // } + // } + + // 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.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index cad59ecc..da890795 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -157,56 +157,39 @@ Struct(V_SharedFrame) //////////////////////////////////////////////////////////// //~ Particle types -Enum(V_ParticleFlag) +// NOTE: Higher particle kinds draw over lower ones +Enum(V_ParticleKind) { - V_ParticleFlag_None = 0, - V_ParticleFlag_PruneWhenStill = (1 << 0), - V_ParticleFlag_StainOnPrune = (1 << 1), - V_ParticleFlag_StainTrail = (1 << 2), + V_ParticleKind_None, + + V_ParticleKind_Blood, + V_ParticleKind_Debris, + V_ParticleKind_Test, + V_ParticleKind_BulletTrail, + + V_ParticleKind_COUNT, }; Struct(V_Emitter) { - V_ParticleFlag flags; + V_ParticleKind kind; u32 first_particle_seq; u32 count; - Vec2 start; - Vec2 end; - - f32 lifetime; - f32 lifetime_spread; - - f32 speed; - f32 speed_spread; - - f32 angle; - f32 angle_spread; - - f32 velocity_falloff; - f32 velocity_falloff_spread; - - Vec4 color_lin; - Vec4 color_spread; + Rng2 pos; + Rng speed; + Rng angle; }; // TODO: Pack this efficiently Struct(V_Particle) { - u32 emitter_init_num; // if != 0, then initialize using emitter at index [emitter_init_num - 1] - u32 seq; - - V_ParticleFlag flags; - + 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 + u32 collisions_count; Vec2 pos; Vec2 velocity; - - f32 exists; - f32 lifetime; - - f32 velocity_falloff; - Vec4 color; }; #if IsCpu