From a4bb13d59db2fc67cba4c05adf151af9dad64371 Mon Sep 17 00:00:00 2001 From: jacob Date: Sat, 14 Feb 2026 01:08:25 -0600 Subject: [PATCH] particle occluder testing --- src/gpu/gpu_shader_core.cgh | 26 +- src/pp/pp_vis/pp_vis_core.c | 72 ++++- src/pp/pp_vis/pp_vis_gpu.g | 449 +++++++++++++++++++++----------- src/pp/pp_vis/pp_vis_shared.cgh | 12 +- 4 files changed, 380 insertions(+), 179 deletions(-) diff --git a/src/gpu/gpu_shader_core.cgh b/src/gpu/gpu_shader_core.cgh index df3660fb..2d0aab1d 100644 --- a/src/gpu/gpu_shader_core.cgh +++ b/src/gpu/gpu_shader_core.cgh @@ -82,27 +82,27 @@ G_ForceDeclConstant(f32, G_ShaderConst_TweakF32, 10 template StructuredBuffer G_Dereference(G_StructuredBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } template RWStructuredBuffer G_Dereference(G_RWStructuredBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - ByteAddressBuffer G_Dereference(G_ByteAddressBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - RWByteAddressBuffer G_Dereference(G_RWByteAddressBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } + ByteAddressBuffer G_Dereference(G_ByteAddressBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } + RWByteAddressBuffer G_Dereference(G_RWByteAddressBufferRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - template Texture1D G_Dereference(G_Texture1DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - template Texture2D G_Dereference(G_Texture2DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - template Texture3D G_Dereference(G_Texture3DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - template RWTexture1D G_Dereference(G_RWTexture1DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - template RWTexture2D G_Dereference(G_RWTexture2DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - template RWTexture3D G_Dereference(G_RWTexture3DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } + template Texture1D G_Dereference(G_Texture1DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } + template Texture2D G_Dereference(G_Texture2DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } + template Texture3D G_Dereference(G_Texture3DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } + template RWTexture1D G_Dereference(G_RWTexture1DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } + template RWTexture2D G_Dereference(G_RWTexture2DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } + template RWTexture3D G_Dereference(G_RWTexture3DRef r) { return ResourceDescriptorHeap[NonUniformResourceIndex(r.v)]; } - SamplerState G_Dereference(G_SamplerStateRef r) { return SamplerDescriptorHeap[NonUniformResourceIndex(r.v)]; } + SamplerState G_Dereference(G_SamplerStateRef r) { return SamplerDescriptorHeap[NonUniformResourceIndex(r.v)]; } #endif //////////////////////////////////////////////////////////// //~ Resource countof #if IsGpu - template u32 countof(StructuredBuffer obj) { u32 result; obj.GetDimensions(result); return result; } - template u32 countof(RWStructuredBuffer obj) { u32 result; u32 stride; obj.GetDimensions(result, stride); return result; } - u32 countof(ByteAddressBuffer obj) { u32 result; obj.GetDimensions(result); return result; } - u32 countof(RWByteAddressBuffer obj) { u32 result; obj.GetDimensions(result); return result; } + template u32 countof(StructuredBuffer obj) { u32 result; obj.GetDimensions(result); return result; } + template u32 countof(RWStructuredBuffer obj) { u32 result; u32 stride; obj.GetDimensions(result, stride); return result; } + u32 countof(ByteAddressBuffer obj) { u32 result; obj.GetDimensions(result); return result; } + u32 countof(RWByteAddressBuffer obj) { u32 result; obj.GetDimensions(result); return result; } template u32 countof(Texture1D obj) { u32 result; obj.GetDimensions(result); return result; } template u32 countof(RWTexture1D obj) { u32 result; obj.GetDimensions(result); return result; } template Vec2U32 countof(Texture2D obj) { Vec2U32 result; obj.GetDimensions(result.x, result.y); return result; } diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 9f5114fc..a8e004a1 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -398,6 +398,7 @@ void V_TickForever(WaveLaneCtx *lane) G_ResourceHandle gpu_ground_densities_res = Zi; G_ResourceHandle gpu_air_densities_res = Zi; G_ResourceHandle gpu_drynesses_res = Zi; + G_ResourceHandle gpu_occluders_res = Zi; G_Texture2DRef gpu_tiles = Zi; G_RWStructuredBufferRef gpu_particles = Zi; @@ -407,6 +408,7 @@ void V_TickForever(WaveLaneCtx *lane) G_RWTexture2DRef gpu_ground_densities = Zi; G_RWTexture2DRef gpu_air_densities = Zi; G_RWTexture2DRef gpu_drynesses = Zi; + G_RWTexture2DRef gpu_occluders = Zi; { G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct); { @@ -505,6 +507,18 @@ void V_TickForever(WaveLaneCtx *lane) ); gpu_drynesses = G_PushRWTexture2DRef(gpu_perm, gpu_drynesses_res); } + // Init occluders texture + { + gpu_occluders_res = G_PushTexture2D( + gpu_perm, cl, + G_Format_R32_Uint, + cells_dims, + G_Layout_DirectQueue_ShaderReadWrite, + .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite, + .name = Lit("Occluders cells") + ); + gpu_occluders = G_PushRWTexture2DRef(gpu_perm, gpu_occluders_res); + } } G_CommitCommandList(cl); } @@ -629,6 +643,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->ground_densities = gpu_ground_densities; frame->air_densities = gpu_air_densities; frame->drynesses = gpu_drynesses; + frame->occluders = gpu_occluders; } ////////////////////////////// @@ -2158,7 +2173,7 @@ void V_TickForever(WaveLaneCtx *lane) Affine wep_uv_to_world_af = ScaleAffine(wep_pix_to_world_af, DimsFromRng2(wep.tex_rect)); V_Quad *quad = PushStruct(frame->quads_arena, V_Quad); - quad->quad_uv_to_screen_af = MulAffine(frame->af.world_to_screen, wep_uv_to_world_af); + 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); } @@ -2169,7 +2184,7 @@ void V_TickForever(WaveLaneCtx *lane) Affine body_uv_to_world_af = ScaleAffine(body_pix_to_world_af, DimsFromRng2(body.tex_rect)); V_Quad *quad = PushStruct(frame->quads_arena, V_Quad); - quad->quad_uv_to_screen_af = MulAffine(frame->af.world_to_screen, body_uv_to_world_af); + 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); } @@ -2527,19 +2542,56 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// - //- Push test explosion + //- Push test emitter + if (frame->held_buttons[Button_F]) // if (frame->held_buttons[Button_F] && !prev_frame->held_buttons[Button_F]) - // { - // } + { + { + V_Emitter emitter = Zi; + + emitter.kind = V_ParticleKind_Test; + + 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 = 100; + 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 = 128; + // emitter.count = 128; + // emitter.count = 32; + // emitter.count = 1; + + V_PushParticles(emitter); + } + } ////////////////////////////// - //- Push test emitter + //- Push test explosion - // if (frame->held_buttons[Button_G]) - if (frame->held_buttons[Button_G] && !prev_frame->held_buttons[Button_G]) + if (frame->held_buttons[Button_G]) + // if (frame->held_buttons[Button_G] && !prev_frame->held_buttons[Button_G]) { // Debris { @@ -2554,8 +2606,8 @@ void V_TickForever(WaveLaneCtx *lane) // f32 angle_spread = 0; // f32 speed = 25; - f32 speed = 50; - // f32 speed = 100; + // f32 speed = 50; + f32 speed = 100; f32 speed_spread = speed * 2; emitter.pos.p0 = emitter.pos.p1 = frame->world_cursor; diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g index e1207579..6928b6d3 100644 --- a/src/pp/pp_vis/pp_vis_gpu.g +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -36,38 +36,52 @@ ComputeShader2D(V_PrepareShadeCS, 8, 8) ComputeShader2D(V_PrepareCellsCS, 8, 8) { V_SharedFrame frame = G_Dereference(V_ShaderConst_Frame)[0]; + Texture2D tiles = G_Dereference(frame.tiles); RWTexture2D ground_cells = G_Dereference(frame.ground_cells); RWTexture2D air_cells = G_Dereference(frame.air_cells); RWTexture2D ground_densities = G_Dereference(frame.ground_densities); RWTexture2D air_densities = G_Dereference(frame.air_densities); RWTexture2D drynesses = G_Dereference(frame.drynesses); + RWTexture2D occluders = G_Dereference(frame.occluders); - Vec2 cells_pos = SV_DispatchThreadID + 0.5; - if (all(cells_pos < countof(air_cells))) + Vec2 cell_pos = SV_DispatchThreadID + 0.5; + if (all(cell_pos < countof(air_cells))) { + Vec2 world_pos = mul(frame.af.cell_to_world, Vec3(cell_pos, 1)); + Vec2 tile_pos = mul(frame.af.world_to_tile, Vec3(world_pos, 1)); + P_TileKind tile = tiles[tile_pos]; + // Clear cells - ground_cells[cells_pos] = 0; - air_cells[cells_pos] = 0; + ground_cells[cell_pos] = 0; + air_cells[cell_pos] = 0; // Clear densities - ground_densities[cells_pos] = 0; - air_densities[cells_pos] = 0; + ground_densities[cell_pos] = 0; + air_densities[cell_pos] = 0; // Increase dryness // TODO: Use simulation dt f32 dry_rate = frame.dt * 0.1; { - f32 old_dryness = drynesses[cells_pos]; + f32 old_dryness = drynesses[cell_pos]; f32 new_dryness = lerp(old_dryness, 1, dry_rate); - drynesses[cells_pos] = new_dryness; + drynesses[cell_pos] = new_dryness; } + // Reset occluders + P_OccluderKind occluder = P_OccluderKind_None; + if (tile == P_TileKind_Wall) + { + occluder = P_OccluderKind_Wall; + } + occluders[cell_pos] = occluder; + // Clear stain if (frame.should_clear_particles) { RWTexture2D stains = G_Dereference(frame.stains); - stains[cells_pos] = 0; - drynesses[cells_pos] = 0; + stains[cell_pos] = 0; + drynesses[cell_pos] = 0; } } } @@ -98,7 +112,8 @@ VertexShader(V_QuadVS, V_QuadPSInput) V_Quad quad = quads[SV_InstanceID]; Vec2 rect_uv = RectUvFromIdx(SV_VertexID); - Vec2 screen_pos = mul(quad.quad_uv_to_screen_af, Vec3(rect_uv, 1)); + Vec2 world_pos = mul(quad.quad_uv_to_world_af, Vec3(rect_uv, 1)); + Vec2 screen_pos = mul(frame.af.world_to_screen, Vec3(world_pos, 1)); Vec2 samp_uv = lerp(quad.tex_slice_uv.p0, quad.tex_slice_uv.p1, rect_uv); @@ -163,6 +178,7 @@ ComputeShader(V_EmitParticlesCS, 64) ComputeShader(V_SimParticlesCS, 64) { V_SharedFrame frame = G_Dereference(V_ShaderConst_Frame)[0]; + Texture2D tiles = G_Dereference(frame.tiles); RWStructuredBuffer particles = G_Dereference(frame.particles); RWTexture2D stains = G_Dereference(frame.stains); RWTexture2D ground_cells = G_Dereference(frame.ground_cells); @@ -170,7 +186,7 @@ ComputeShader(V_SimParticlesCS, 64) RWTexture2D ground_densities = G_Dereference(frame.ground_densities); RWTexture2D air_densities = G_Dereference(frame.air_densities); RWTexture2D drynesses = G_Dereference(frame.drynesses); - Texture2D tiles = G_Dereference(frame.tiles); + RWTexture2D occluders = G_Dereference(frame.occluders); u32 particle_idx = SV_DispatchThreadID; if (particle_idx < V_ParticlesCap) @@ -205,28 +221,15 @@ ComputeShader(V_SimParticlesCS, 64) particle.velocity = Vec2(cos(initial_angle), sin(initial_angle)) * initial_speed; } + + + + + + + if (particle.kind > V_ParticleKind_None && particle.kind < V_ParticleKind_COUNT) { - ////////////////////////////// - //- Velocity effects - - { - // TODO: Separate particle basis for whisp effects - u64 seed1 = MixU64(seed0); - f32 rand_whisp = Norm16(seed1 >> 0); - - b32 is_whispy = particle.kind == V_ParticleKind_Smoke; - if (is_whispy) - { - f32 whisp = lerp(-1, 1, rand_whisp); - particle.velocity.x += whisp * cos(particle.life * 10); - particle.velocity.y += whisp * sin(particle.life * 10); - - // f32 whisp = lerp(0, Tau, rand_whisp) * frame.dt; - // particle.velocity = RotateVec2Angle(particle.velocity, whisp); - } - } - ////////////////////////////// //- Move @@ -239,16 +242,16 @@ ComputeShader(V_SimParticlesCS, 64) Vec2 p1 = particle.pos + particle.velocity * frame.dt; f32 t = 1; { - 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 dda_p0 = floor(tile_p0); - Vec2I32 dda_p1 = floor(tile_p1); + 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 cell_p0 = floor(occluder_p0); + Vec2I32 cell_p1 = floor(occluder_p1); - Vec2 delta = tile_p1 - tile_p0; + 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 - tile_p0; + Vec2 t_max = cell_p0 - occluder_p0; t_max.x += dda_step_dir.x > 0; t_max.y += dda_step_dir.y > 0; t_max *= inv_delta; @@ -256,22 +259,23 @@ ComputeShader(V_SimParticlesCS, 64) f32 t_hit = 1; - Vec2I32 dda_pos = dda_p0; + Vec2I32 cell_pos = cell_p0; b32 stepped_x = 0; b32 stepped_y = 0; - u32 max_iterations = 32; + // 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) + if (cell_pos.x == cell_p1.x && cell_pos.y == cell_p1.y) { done = 1; } else if (t_max.x < t_max.y) { - dda_pos.x += dda_step_dir.x; + cell_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; @@ -279,21 +283,27 @@ ComputeShader(V_SimParticlesCS, 64) } else { - dda_pos.y += dda_step_dir.y; + cell_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(tiles))) + if (all(cell_pos >= 0) && all(cell_pos < countof(occluders))) { - P_TileKind tile = tiles[dda_pos]; - if (tile == P_TileKind_Wall) + P_OccluderKind occluder = (P_OccluderKind)occluders[cell_pos]; + if (occluder != P_OccluderKind_None) { done = 1; collision = 1; + particle.velocity *= 0.5; } + + + + stains[cell_pos] = Color_Black; + // ground_cells[cell_pos] = Color_White; } else { @@ -337,128 +347,257 @@ ComputeShader(V_SimParticlesCS, 64) ////////////////////////////// //- 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; - b32 is_ground_particle = particle.kind == V_ParticleKind_Debris; - 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; - - // // 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_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 + 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); - if (should_draw_ground) + 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) { - InterlockedMax(ground_cells[cell_pos], packed); - InterlockedAdd(ground_densities[cell_pos], 1); + 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); + } } - if (should_draw_air) + // Prune + if (!is_in_world) { - InterlockedMax(air_cells[cell_pos], packed); - InterlockedAdd(air_densities[cell_pos], 1); + particle.kind = V_ParticleKind_None; } + + // Increment life + particle.life += frame.dt; } - // { - // 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_visible = 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; - // } - // } - // } - - // Increment life - particle.life += frame.dt; - - // Prune - if (!is_in_world) - { - particle.kind = V_ParticleKind_None; - } } else { 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; } } @@ -692,7 +831,7 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input) { u64 seed = MixU64(P_ParticleCompositeBasis ^ particle_idx); f32 rand_color = Norm16(seed >> 0); - if (kind == V_ParticleKind_Debris) + if (kind == V_ParticleKind_Debris || kind == V_ParticleKind_Test) { color.rgb = Color_Orange.rgb; } @@ -732,7 +871,7 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input) { u64 seed = MixU64(P_ParticleCompositeBasis ^ particle_idx); f32 rand_color = Norm16(seed >> 0); - if (kind == V_ParticleKind_Debris) + if (kind == V_ParticleKind_Debris || kind == V_ParticleKind_Test) { color.rgb = Color_Orange.rgb; } diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index dc2f30f9..6c3eaf89 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -152,11 +152,21 @@ Struct(V_SharedFrame) G_RWTexture2DRef ground_densities; G_RWTexture2DRef air_densities; G_RWTexture2DRef drynesses; + G_RWTexture2DRef occluders; G_StructuredBufferRef dverts; G_StructuredBufferRef quads; }; +//////////////////////////////////////////////////////////// +//~ Occluder types + +Enum(P_OccluderKind) +{ + P_OccluderKind_None, + P_OccluderKind_Wall, +}; + //////////////////////////////////////////////////////////// //~ Particle types @@ -223,7 +233,7 @@ Enum(V_QuadFlag) Struct(V_Quad) { V_QuadFlag flags; - Affine quad_uv_to_screen_af; + Affine quad_uv_to_world_af; G_Texture2DRef tex; Rng2 tex_slice_uv; };