smoke particle testing

This commit is contained in:
jacob 2026-02-13 23:33:49 -06:00
parent d9228b78a3
commit 463e142727
8 changed files with 313 additions and 142 deletions

View File

@ -551,7 +551,7 @@ Vec2 CeilVec2(Vec2 a)
return VEC2(CeilF32(a.x), CeilF32(a.y)); return VEC2(CeilF32(a.x), CeilF32(a.y));
} }
//- Angle //- Rotation
// Returns 1 if winding between vectors a & b is clockwise or straight, -1 if counter-clockwise // Returns 1 if winding between vectors a & b is clockwise or straight, -1 if counter-clockwise
i32 WindingFromVec2(Vec2 a, Vec2 b) i32 WindingFromVec2(Vec2 a, Vec2 b)

View File

@ -384,7 +384,7 @@ Vec2 RoundVec2(Vec2 a);
Vec2 FloorVec2(Vec2 a); Vec2 FloorVec2(Vec2 a);
Vec2 CeilVec2(Vec2 a); Vec2 CeilVec2(Vec2 a);
//- Angle //- Rotation
i32 WindingFromVec2(Vec2 a, Vec2 b); i32 WindingFromVec2(Vec2 a, Vec2 b);
Vec2 RotateVec2Angle(Vec2 v, f32 a); Vec2 RotateVec2Angle(Vec2 v, f32 a);
Vec2 RotateVec2(Vec2 a, Vec2 b); Vec2 RotateVec2(Vec2 a, Vec2 b);

View File

@ -138,12 +138,33 @@ Inline f64 Norm53(u64 v)
#define MatchFloor(a, b) all(floor(a) == floor(b)) #define MatchFloor(a, b) all(floor(a) == floor(b))
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Derivative helpers //~ Rotation
Inline Vec2 RotateVec2Angle(Vec2 v, f32 a)
{
f32 c = cos(a);
f32 s = sin(a);
return Vec2(v.x * c - v.y * s, v.x * s + v.y * c);
}
Inline Vec2 RotateVec2(Vec2 a, Vec2 b)
{
return Vec2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);
}
Inline Vec2 InvertRot(Vec2 r)
{
r.y = -r.y;
return r;
}
////////////////////////////////////////////////////////////
//~ Derivatives
#define fwidth_fine(v) (abs(ddx_fine((v))) + abs(ddy_fine((v)))) #define fwidth_fine(v) (abs(ddx_fine((v))) + abs(ddy_fine((v))))
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Color helpers //~ Colors
Inline Vec4 Vec4FromU32(u32 v) Inline Vec4 Vec4FromU32(u32 v)
{ {

View File

@ -533,9 +533,11 @@ void M_BuildEntryPoint(WaveLaneCtx *lane)
//- Dxc //- Dxc
{ {
PushStringToList(perm, &cp.flags_dxc, Lit("-O3")); PushStringToList(perm, &cp.flags_dxc, Lit("-O3"));
PushStringToList(perm, &cp.flags_dxc, Lit("-Zi -Qembed_debug"));
PushStringToList(perm, &cp.flags_dxc, Lit("-HV 202x")); // 202x makes numeric literals less weird PushStringToList(perm, &cp.flags_dxc, Lit("-HV 202x")); // 202x makes numeric literals less weird
// TODO: Export debug info separately for release builds
PushStringToList(perm, &cp.flags_dxc, Lit("-Zi -Qembed_debug"));
// Enable warnings // Enable warnings
PushStringToList(perm, &cp.warnings_dxc, Lit("-Wall")); PushStringToList(perm, &cp.warnings_dxc, Lit("-Wall"));
PushStringToList(perm, &cp.warnings_dxc, Lit("-Werror")); PushStringToList(perm, &cp.warnings_dxc, Lit("-Werror"));
@ -544,6 +546,7 @@ void M_BuildEntryPoint(WaveLaneCtx *lane)
// Disable warnings // Disable warnings
PushStringToList(perm, &cp.warnings_dxc, Lit("-Wno-unused-variable")); PushStringToList(perm, &cp.warnings_dxc, Lit("-Wno-unused-variable"));
PushStringToList(perm, &cp.warnings_dxc, Lit("-Wno-conversion")); PushStringToList(perm, &cp.warnings_dxc, Lit("-Wno-conversion"));
PushStringToList(perm, &cp.warnings_dxc, Lit("-Wno-switch"));
} }
} }

View File

@ -49,7 +49,7 @@ String P_PackWorld(Arena *arena, P_World *src_world)
} }
if (ent->is_weapon) if (ent->is_weapon)
{ {
result.len += PushString(arena, Lit(" is_weapon\n")).len; result.len += PushString(arena, Lit(" weapon\n")).len;
} }
if (ent->is_bullet) if (ent->is_bullet)
{ {

View File

@ -392,17 +392,21 @@ void V_TickForever(WaveLaneCtx *lane)
// Init gpu state // Init gpu state
G_ResourceHandle gpu_tiles_res = Zi; G_ResourceHandle gpu_tiles_res = Zi;
G_ResourceHandle gpu_particles_res = Zi; G_ResourceHandle gpu_particles_res = Zi;
G_ResourceHandle gpu_cells_res = Zi;
G_ResourceHandle gpu_stains_res = Zi; G_ResourceHandle gpu_stains_res = Zi;
G_ResourceHandle gpu_ground_cells_res = Zi;
G_ResourceHandle gpu_air_cells_res = Zi;
G_ResourceHandle gpu_ground_densities_res = Zi;
G_ResourceHandle gpu_air_densities_res = Zi;
G_ResourceHandle gpu_drynesses_res = Zi; G_ResourceHandle gpu_drynesses_res = Zi;
G_ResourceHandle gpu_densities_res = Zi;
G_Texture2DRef gpu_tiles = Zi; G_Texture2DRef gpu_tiles = Zi;
G_RWStructuredBufferRef gpu_particles = Zi; G_RWStructuredBufferRef gpu_particles = Zi;
G_RWTexture2DRef gpu_cells = Zi;
G_RWTexture2DRef gpu_stains = Zi; G_RWTexture2DRef gpu_stains = Zi;
G_RWTexture2DRef gpu_ground_cells = Zi;
G_RWTexture2DRef gpu_air_cells = Zi;
G_RWTexture2DRef gpu_ground_densities = Zi;
G_RWTexture2DRef gpu_air_densities = Zi;
G_RWTexture2DRef gpu_drynesses = Zi; G_RWTexture2DRef gpu_drynesses = Zi;
G_RWTexture2DRef gpu_densities = Zi;
{ {
G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct); G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct);
{ {
@ -429,29 +433,10 @@ void V_TickForever(WaveLaneCtx *lane)
); );
gpu_particles = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles_res, V_Particle); gpu_particles = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles_res, V_Particle);
} }
// Init cells texture // Init stains texture
{
gpu_cells_res = G_PushTexture2D(
gpu_perm, cl,
// G_Format_R8_Uint,
G_Format_R32_Uint,
// G_Format_R11G11B10_Float,
// G_Format_R10G10B10A2_Unorm,
// G_Format_R16G16B16A16_Float,
cells_dims,
G_Layout_DirectQueue_ShaderReadWrite,
.flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite,
.name = Lit("Cells")
);
gpu_cells = G_PushRWTexture2DRef(gpu_perm, gpu_cells_res);
}
// Init stain texture
{ {
gpu_stains_res = G_PushTexture2D( gpu_stains_res = G_PushTexture2D(
gpu_perm, cl, gpu_perm, cl,
// G_Format_R8_Uint,
// G_Format_R11G11B10_Float,
// G_Format_R10G10B10A2_Unorm,
G_Format_R16G16B16A16_Float, G_Format_R16G16B16A16_Float,
cells_dims, cells_dims,
G_Layout_DirectQueue_ShaderReadWrite, G_Layout_DirectQueue_ShaderReadWrite,
@ -460,14 +445,58 @@ void V_TickForever(WaveLaneCtx *lane)
); );
gpu_stains = G_PushRWTexture2DRef(gpu_perm, gpu_stains_res); gpu_stains = G_PushRWTexture2DRef(gpu_perm, gpu_stains_res);
} }
// Init ground cells texture
{
gpu_ground_cells_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("Ground cells")
);
gpu_ground_cells = G_PushRWTexture2DRef(gpu_perm, gpu_ground_cells_res);
}
// Init air cells texture
{
gpu_air_cells_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("Air cells")
);
gpu_air_cells = G_PushRWTexture2DRef(gpu_perm, gpu_air_cells_res);
}
// Init ground densities texture
{
gpu_ground_densities_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("Ground densities")
);
gpu_ground_densities = G_PushRWTexture2DRef(gpu_perm, gpu_ground_densities_res);
}
// Init air densities texture
{
gpu_air_densities_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("Air densities")
);
gpu_air_densities = G_PushRWTexture2DRef(gpu_perm, gpu_air_densities_res);
}
// Init drynesses texture // Init drynesses texture
{ {
gpu_drynesses_res = G_PushTexture2D( gpu_drynesses_res = G_PushTexture2D(
gpu_perm, cl, gpu_perm, cl,
// G_Format_R8_Uint,
// G_Format_R11G11B10_Float,
// G_Format_R10G10B10A2_Unorm,
// G_Format_R16_Float,
G_Format_R32_Float, G_Format_R32_Float,
cells_dims, cells_dims,
G_Layout_DirectQueue_ShaderReadWrite, G_Layout_DirectQueue_ShaderReadWrite,
@ -476,22 +505,6 @@ void V_TickForever(WaveLaneCtx *lane)
); );
gpu_drynesses = G_PushRWTexture2DRef(gpu_perm, gpu_drynesses_res); gpu_drynesses = G_PushRWTexture2DRef(gpu_perm, gpu_drynesses_res);
} }
// Init densities texture
{
gpu_densities_res = G_PushTexture2D(
gpu_perm, cl,
// G_Format_R8_Uint,
// G_Format_R11G11B10_Float,
// G_Format_R10G10B10A2_Unorm,
// G_Format_R16_Float,
G_Format_R32_Uint,
cells_dims,
G_Layout_DirectQueue_ShaderReadWrite,
.flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite,
.name = Lit("Densities")
);
gpu_densities = G_PushRWTexture2DRef(gpu_perm, gpu_densities_res);
}
} }
G_CommitCommandList(cl); G_CommitCommandList(cl);
} }
@ -610,10 +623,12 @@ void V_TickForever(WaveLaneCtx *lane)
frame->pt_clamp_sampler = G_BasicPointClampSampler(); frame->pt_clamp_sampler = G_BasicPointClampSampler();
frame->pt_wrap_sampler = G_BasicPointWrapSampler(); frame->pt_wrap_sampler = G_BasicPointWrapSampler();
frame->particles = gpu_particles; frame->particles = gpu_particles;
frame->cells = gpu_cells;
frame->stains = gpu_stains; frame->stains = gpu_stains;
frame->ground_cells = gpu_ground_cells;
frame->air_cells = gpu_air_cells;
frame->ground_densities = gpu_ground_densities;
frame->air_densities = gpu_air_densities;
frame->drynesses = gpu_drynesses; frame->drynesses = gpu_drynesses;
frame->densities = gpu_densities;
} }
////////////////////////////// //////////////////////////////
@ -2523,43 +2538,84 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Push test emitter //- 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]) if (frame->held_buttons[Button_G] && !prev_frame->held_buttons[Button_G])
{ {
V_Emitter emitter = Zi; // Debris
{
V_Emitter emitter = Zi;
emitter.kind = V_ParticleKind_Test; emitter.kind = V_ParticleKind_Debris;
f32 angle = AngleFromVec2(frame->look); f32 angle = AngleFromVec2(frame->look);
// f32 angle = 0; // f32 angle = 0;
// f32 angle_spread = Tau * 0.25; // f32 angle_spread = Tau * 0.25;
f32 angle_spread = Tau; f32 angle_spread = Tau;
// f32 angle_spread = 0; // f32 angle_spread = 0;
f32 speed = 25; // f32 speed = 25;
// f32 speed = 50; f32 speed = 50;
// f32 speed = 1000; // f32 speed = 100;
f32 speed_spread = speed * 2; f32 speed_spread = speed * 2;
emitter.pos.p0 = emitter.pos.p1 = frame->world_cursor; emitter.pos.p0 = emitter.pos.p1 = frame->world_cursor;
emitter.speed.min = speed - speed_spread * 0.5; emitter.speed.min = speed - speed_spread * 0.5;
emitter.speed.max = speed + speed_spread * 0.5; emitter.speed.max = speed + speed_spread * 0.5;
emitter.angle.min = angle - angle_spread * 0.5; emitter.angle.min = angle - angle_spread * 0.5;
emitter.angle.max = angle + angle_spread * 0.5; emitter.angle.max = angle + angle_spread * 0.5;
// emitter.falloff.min = emitter.falloff.max = 0; // emitter.falloff.min = emitter.falloff.max = 0;
// emitter.count = CeilF32(Kibi(64) * frame->dt); // emitter.count = CeilF32(Kibi(64) * frame->dt);
// emitter.count = Mebi(16); // emitter.count = Mebi(16);
// emitter.count = Mebi(2); // emitter.count = Mebi(2);
// emitter.count = Kibi(32); // emitter.count = Kibi(32);
// emitter.count = Kibi(8); // emitter.count = Kibi(8);
emitter.count = Kibi(1); emitter.count = 128;
// emitter.count = 128; // emitter.count = 128;
// emitter.count = 32; // emitter.count = 32;
// emitter.count = 1; // emitter.count = 1;
V_PushParticles(emitter); V_PushParticles(emitter);
}
// Smoke
{
V_Emitter emitter = Zi;
emitter.kind = V_ParticleKind_Smoke;
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 = 50;
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 = 32;
// emitter.count = 1;
V_PushParticles(emitter);
}
} }
@ -4816,6 +4872,7 @@ void V_TickForever(WaveLaneCtx *lane)
if (frame->should_clear_particles) if (frame->should_clear_particles)
{ {
G_Compute(frame->cl, V_ClearParticlesCS, V_ThreadGroupSizeFromBufferSize(V_ParticlesCap)); G_Compute(frame->cl, V_ClearParticlesCS, V_ThreadGroupSizeFromBufferSize(V_ParticlesCap));
V.particle_seq = 0;
} }
// Prepare screen RT // Prepare screen RT

View File

@ -36,18 +36,22 @@ ComputeShader2D(V_PrepareShadeCS, 8, 8)
ComputeShader2D(V_PrepareCellsCS, 8, 8) ComputeShader2D(V_PrepareCellsCS, 8, 8)
{ {
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0]; V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
RWTexture2D<u32> cells = G_Dereference<u32>(frame.cells); RWTexture2D<u32> ground_cells = G_Dereference<u32>(frame.ground_cells);
RWTexture2D<u32> air_cells = G_Dereference<u32>(frame.air_cells);
RWTexture2D<u32> ground_densities = G_Dereference<u32>(frame.ground_densities);
RWTexture2D<u32> air_densities = G_Dereference<u32>(frame.air_densities);
RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses); RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses);
RWTexture2D<u32> densities = G_Dereference<u32>(frame.densities);
Vec2 cells_pos = SV_DispatchThreadID + 0.5; Vec2 cells_pos = SV_DispatchThreadID + 0.5;
if (all(cells_pos < countof(cells))) if (all(cells_pos < countof(air_cells)))
{ {
// Clear cell // Clear cells
cells[cells_pos] = 0; ground_cells[cells_pos] = 0;
air_cells[cells_pos] = 0;
// Clear density // Clear densities
densities[cells_pos] = 0; ground_densities[cells_pos] = 0;
air_densities[cells_pos] = 0;
// Increase dryness // Increase dryness
// TODO: Use simulation dt // TODO: Use simulation dt
@ -160,10 +164,12 @@ ComputeShader(V_SimParticlesCS, 64)
{ {
V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0]; V_SharedFrame frame = G_Dereference<V_SharedFrame>(V_ShaderConst_Frame)[0];
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(frame.particles); RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(frame.particles);
RWTexture2D<u32> cells = G_Dereference<u32>(frame.cells);
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(frame.stains); RWTexture2D<Vec4> stains = G_Dereference<Vec4>(frame.stains);
RWTexture2D<u32> ground_cells = G_Dereference<u32>(frame.ground_cells);
RWTexture2D<u32> air_cells = G_Dereference<u32>(frame.air_cells);
RWTexture2D<u32> ground_densities = G_Dereference<u32>(frame.ground_densities);
RWTexture2D<u32> air_densities = G_Dereference<u32>(frame.air_densities);
RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses); RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses);
RWTexture2D<u32> densities = G_Dereference<u32>(frame.densities);
Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(frame.tiles); Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(frame.tiles);
u32 particle_idx = SV_DispatchThreadID; u32 particle_idx = SV_DispatchThreadID;
@ -176,11 +182,11 @@ ComputeShader(V_SimParticlesCS, 64)
if (particle.kind != 0) if (particle.kind != 0)
{ {
u64 seed = MixU64(P_ParticleSimBasis ^ particle_idx); u64 seed0 = MixU64(P_ParticleSimBasis ^ particle_idx);
f32 rand_offset = Norm16(seed >> 0); f32 rand_offset = Norm16(seed0 >> 0);
f32 rand_angle = Norm16(seed >> 16); f32 rand_angle = Norm16(seed0 >> 16);
f32 rand_speed = Norm16(seed >> 32); f32 rand_speed = Norm16(seed0 >> 32);
f32 rand_falloff = Norm16(seed >> 48); f32 rand_falloff = Norm16(seed0 >> 48);
////////////////////////////// //////////////////////////////
//- Init //- Init
@ -201,6 +207,26 @@ ComputeShader(V_SimParticlesCS, 64)
if (particle.kind > V_ParticleKind_None && particle.kind < V_ParticleKind_COUNT) 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 //- Move
@ -288,18 +314,13 @@ ComputeShader(V_SimParticlesCS, 64)
particle.velocity.y *= -1; particle.velocity.y *= -1;
} }
{ {
u64 collision_seed = MixU64s(seed, particle.collisions_count); u64 collision_seed = MixU64s(seed0, particle.collisions_count);
f32 rand_collision_angle = Norm16(collision_seed >> 0); f32 rand_collision_angle = Norm16(collision_seed >> 0);
f32 rand_collision_velocity = Norm16(collision_seed >> 16); f32 rand_collision_velocity = Norm16(collision_seed >> 16);
f32 collision_angle = lerp(-0.05 * Tau, 0.05 * Tau, rand_collision_angle); 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 = lerp(0, 1, rand_collision_velocity);
f32 collision_velocity_falloff = 0; f32 collision_velocity_falloff = 0;
f32 x = particle.velocity.x; particle.velocity = RotateVec2Angle(particle.velocity, collision_angle);
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.velocity *= 1.0f - collision_velocity_falloff;
} }
++particle.collisions_count; ++particle.collisions_count;
@ -317,12 +338,21 @@ ComputeShader(V_SimParticlesCS, 64)
//- Commit //- Commit
Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); 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)); Vec2 cell_screen_pos_p0 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(floor(cell_pos), 1)), 1));
b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(cells)); Vec2 cell_screen_pos_p1 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(ceil(cell_pos), 1)), 1));
b32 is_in_screen = all(screen_pos >= 0) && all(screen_pos < frame.screen_dims); cell_screen_pos_p1 = max(cell_screen_pos_p1, cell_screen_pos_p0 + 1);
b32 should_draw = is_in_world && is_in_screen; b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells));
b32 should_stain = 0; 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 // // Stain
// if (is_in_world) // if (is_in_world)
@ -350,7 +380,7 @@ ComputeShader(V_SimParticlesCS, 64)
// } // }
// Draw // Draw
if (should_draw) if (should_draw_ground || should_draw_air)
{ {
u32 packed = 0; u32 packed = 0;
packed |= (particle_idx & ((1 >> 24) - 1)) << 0; packed |= (particle_idx & ((1 >> 24) - 1)) << 0;
@ -358,15 +388,24 @@ ComputeShader(V_SimParticlesCS, 64)
StaticAssert(V_ParticlesCap <= (1 << 24)); // particle idx must fit in 24 bits StaticAssert(V_ParticlesCap <= (1 << 24)); // particle idx must fit in 24 bits
StaticAssert(V_ParticleKind_COUNT <= 0xFF); // particle kind must fit in 8 bits StaticAssert(V_ParticleKind_COUNT <= 0xFF); // particle kind must fit in 8 bits
InterlockedMax(cells[cell_pos], packed); if (should_draw_ground)
InterlockedAdd(densities[cell_pos], 1); {
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);
}
} }
// { // {
// Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); // 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)); // 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_world = all(cell_pos >= 0) && all(cell_pos < countof(cells));
// b32 is_in_screen = all(screen_pos >= 0) && all(screen_pos < frame.screen_dims); // b32 is_visible = all(screen_pos >= 0) && all(screen_pos < frame.screen_dims);
// Vec4 color = particle.color; // Vec4 color = particle.color;
// color.a *= prev_exists; // color.a *= prev_exists;
@ -407,6 +446,9 @@ ComputeShader(V_SimParticlesCS, 64)
// } // }
// } // }
// Increment life
particle.life += frame.dt;
// Prune // Prune
if (!is_in_world) if (!is_in_world)
{ {
@ -417,7 +459,6 @@ ComputeShader(V_SimParticlesCS, 64)
{ {
particle.kind = V_ParticleKind_None; particle.kind = V_ParticleKind_None;
} }
particles[particle_idx] = particle; particles[particle_idx] = particle;
} }
} }
@ -485,11 +526,13 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
// Texture2D<Vec4> shade_tex = G_Dereference<Vec4>(frame.shade_ro); // Texture2D<Vec4> shade_tex = G_Dereference<Vec4>(frame.shade_ro);
Texture2D<Vec4> albedo_tex = G_Dereference<Vec4>(frame.albedo_ro); Texture2D<Vec4> albedo_tex = G_Dereference<Vec4>(frame.albedo_ro);
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(frame.stains); RWTexture2D<Vec4> stains = G_Dereference<Vec4>(frame.stains);
RWTexture2D<u32> cells = G_Dereference<u32>(frame.cells); RWTexture2D<u32> ground_cells = G_Dereference<u32>(frame.ground_cells);
RWTexture2D<u32> air_cells = G_Dereference<u32>(frame.air_cells);
RWTexture2D<u32> ground_densities = G_Dereference<u32>(frame.ground_densities);
RWTexture2D<u32> air_densities = G_Dereference<u32>(frame.air_densities);
RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses); RWTexture2D<f32> drynesses = G_Dereference<f32>(frame.drynesses);
Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(frame.tiles); Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(frame.tiles);
SamplerState clamp_sampler = G_Dereference(frame.pt_clamp_sampler); SamplerState clamp_sampler = G_Dereference(frame.pt_clamp_sampler);
RWTexture2D<u32> densities = G_Dereference<u32>(frame.densities);
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(frame.particles); RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(frame.particles);
Vec2 screen_pos = input.sv_position.xy; Vec2 screen_pos = input.sv_position.xy;
@ -501,7 +544,7 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5; Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5;
Vec2 world_bounds_screen_p0 = mul(frame.af.world_to_screen, Vec3(-half_world_dims.xy, 1)); Vec2 world_bounds_screen_p0 = mul(frame.af.world_to_screen, Vec3(-half_world_dims.xy, 1));
Vec2 world_bounds_screen_p1 = mul(frame.af.world_to_screen, Vec3(half_world_dims.xy, 1)); Vec2 world_bounds_screen_p1 = mul(frame.af.world_to_screen, Vec3(half_world_dims.xy, 1));
b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(cells)); b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(ground_cells));
P_TileKind tile = tiles[tile_pos]; P_TileKind tile = tiles[tile_pos];
P_TileKind equipped_tile = frame.equipped_tile; P_TileKind equipped_tile = frame.equipped_tile;
@ -632,45 +675,83 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
} }
////////////////////////////// //////////////////////////////
//- Particle //- Ground particle
// TODO: Remove this // TODO: Remove this
// Vec4 particle_color = cells[cell_pos]; Vec4 ground_particle_color = 0;
// particle_color.rgb *= particle_color.a;
Vec4 particle_color = 0;
{ {
u32 packed = cells[cell_pos]; Vec4 color = 0;
V_ParticleKind kind = (V_ParticleKind)((packed >> 24) & 0xFF);
if (kind != V_ParticleKind_None)
{ {
u32 particle_idx = packed & ((1 << 24) - 1); u32 packed = ground_cells[cell_pos];
V_ParticleKind kind = (V_ParticleKind)((packed >> 24) & 0xFF);
if (particle_idx < V_ParticlesCap) if (kind != V_ParticleKind_None)
{ {
if (kind == V_ParticleKind_Test) u32 particle_idx = packed & ((1 << 24) - 1);
if (particle_idx < V_ParticlesCap)
{ {
u64 seed = MixU64(P_ParticleCompositeBasis ^ particle_idx); u64 seed = MixU64(P_ParticleCompositeBasis ^ particle_idx);
f32 rand_color = Norm16(seed >> 0); f32 rand_color = Norm16(seed >> 0);
if (kind == V_ParticleKind_Debris)
Vec4 color = Vec4(0.15, 0.15, 0.15, 1);
color.rgb += (rand_color - 0.5) * 0.025;
{ {
f32 density = densities[cell_pos]; color.rgb = Color_Orange.rgb;
}
else if (kind == V_ParticleKind_Smoke)
{
color.rgb = Vec3(0.15, 0.15, 0.15);
}
color.rgb += (rand_color - 0.5) * 0.025;
{
f32 density = ground_densities[cell_pos];
// f32 t = saturate(density / 10.0); // f32 t = saturate(density / 10.0);
f32 t = smoothstep(-10, 32, density); f32 t = smoothstep(-10, 32, density);
color.a = lerp(0, 0.85, t); color.a = lerp(0, 0.85, t);
} }
particle_color = color;
} }
} }
} }
ground_particle_color = color;
ground_particle_color.rgb *= ground_particle_color.a;
}
particle_color.rgb *= particle_color.a; //////////////////////////////
//- Air particle
// TODO: Remove this
Vec4 air_particle_color = 0;
{
Vec4 color = 0;
{
u32 packed = air_cells[cell_pos];
V_ParticleKind kind = (V_ParticleKind)((packed >> 24) & 0xFF);
if (kind != V_ParticleKind_None)
{
u32 particle_idx = packed & ((1 << 24) - 1);
if (particle_idx < V_ParticlesCap)
{
u64 seed = MixU64(P_ParticleCompositeBasis ^ particle_idx);
f32 rand_color = Norm16(seed >> 0);
if (kind == V_ParticleKind_Debris)
{
color.rgb = Color_Orange.rgb;
}
else if (kind == V_ParticleKind_Smoke)
{
color.rgb = Vec3(0.15, 0.15, 0.15);
}
color.rgb += (rand_color - 0.5) * 0.025;
{
f32 density = air_densities[cell_pos];
// f32 t = saturate(density / 10.0);
f32 t = smoothstep(-10, 32, density);
color.a = lerp(0, 0.85, t);
}
}
}
}
air_particle_color = color;
air_particle_color.rgb *= air_particle_color.a;
} }
@ -679,7 +760,8 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
// world_color = BlendPremul(shade_color, world_color); // world_color = BlendPremul(shade_color, world_color);
world_color = BlendPremul(albedo_color, world_color); world_color = BlendPremul(albedo_color, world_color);
world_color = BlendPremul(particle_color, world_color); world_color = BlendPremul(ground_particle_color, world_color);
world_color = BlendPremul(air_particle_color, world_color);
} }
////////////////////////////// //////////////////////////////

View File

@ -1,7 +1,7 @@
// #define V_ParticlesCap Kibi(128) // #define V_ParticlesCap Kibi(128)
// #define V_ParticlesCap Mebi(1) // #define V_ParticlesCap Mebi(1)
// #define V_ParticlesCap Mebi(2) #define V_ParticlesCap Mebi(2)
#define V_ParticlesCap Mebi(16) // #define V_ParticlesCap Mebi(16)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ State types //~ State types
@ -146,10 +146,12 @@ Struct(V_SharedFrame)
G_StructuredBufferRef emitters; G_StructuredBufferRef emitters;
G_RWStructuredBufferRef particles; G_RWStructuredBufferRef particles;
G_RWTexture2DRef cells;
G_RWTexture2DRef stains; G_RWTexture2DRef stains;
G_RWTexture2DRef ground_cells;
G_RWTexture2DRef air_cells;
G_RWTexture2DRef ground_densities;
G_RWTexture2DRef air_densities;
G_RWTexture2DRef drynesses; G_RWTexture2DRef drynesses;
G_RWTexture2DRef densities;
G_StructuredBufferRef dverts; G_StructuredBufferRef dverts;
G_StructuredBufferRef quads; G_StructuredBufferRef quads;
@ -166,11 +168,17 @@ Enum(V_ParticleKind)
{ {
V_ParticleKind_None, V_ParticleKind_None,
//- Ground particles
V_ParticleKind_Blood, V_ParticleKind_Blood,
V_ParticleKind_Debris, V_ParticleKind_Debris,
V_ParticleKind_Test,
//- Air particles
V_ParticleKind_Smoke,
V_ParticleKind_BulletTrail, V_ParticleKind_BulletTrail,
//- Test particles
V_ParticleKind_Test,
V_ParticleKind_COUNT, V_ParticleKind_COUNT,
}; };