continuous particle staining
This commit is contained in:
parent
e4426ab0d2
commit
6957be38b5
@ -2190,7 +2190,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
quad->quad_uv_to_world_af = wep_uv_to_world_af;
|
quad->quad_uv_to_world_af = wep_uv_to_world_af;
|
||||||
quad->tex = wep.tex;
|
quad->tex = wep.tex;
|
||||||
quad->tex_slice_uv = DivRng2Vec2(wep.tex_rect, wep.tex_dims);
|
quad->tex_slice_uv = DivRng2Vec2(wep.tex_rect, wep.tex_dims);
|
||||||
quad->occluder = P_OccluderKind_Guy;
|
quad->occluder = V_OccluderKind_Guy;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- Push body quad
|
//- Push body quad
|
||||||
@ -2202,7 +2202,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
quad->quad_uv_to_world_af = body_uv_to_world_af;
|
quad->quad_uv_to_world_af = body_uv_to_world_af;
|
||||||
quad->tex = body.tex;
|
quad->tex = body.tex;
|
||||||
quad->tex_slice_uv = DivRng2Vec2(body.tex_rect, body.tex_dims);
|
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 angle_spread = 0;
|
||||||
|
|
||||||
// f32 speed = 5;
|
// f32 speed = 5;
|
||||||
f32 speed = 25;
|
// f32 speed = 25;
|
||||||
// f32 speed = 50;
|
f32 speed = 50;
|
||||||
// f32 speed = 100;
|
// f32 speed = 100;
|
||||||
f32 speed_spread = speed * 2;
|
f32 speed_spread = speed * 2;
|
||||||
|
|
||||||
@ -2639,8 +2639,8 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
// 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 = 128;
|
// emitter.count = 128;
|
||||||
// emitter.count = 128;
|
// emitter.count = 128;
|
||||||
// emitter.count = 32;
|
// emitter.count = 32;
|
||||||
// emitter.count = 1;
|
// emitter.count = 1;
|
||||||
|
|||||||
@ -15,7 +15,7 @@ Vec4 V_ColorFromParticle(V_ParticleKind particle_kind, u32 particle_idx, u32 den
|
|||||||
V_ParticleDesc desc = V_DescFromParticleKind(particle_kind);
|
V_ParticleDesc desc = V_DescFromParticleKind(particle_kind);
|
||||||
|
|
||||||
Vec4 result = 0;
|
Vec4 result = 0;
|
||||||
u64 seed = MixU64(P_ParticleColorBasis ^ particle_idx);
|
u64 seed = MixU64(V_ParticleColorBasis ^ particle_idx);
|
||||||
f32 rand_color = Norm16(seed >> 0);
|
f32 rand_color = Norm16(seed >> 0);
|
||||||
|
|
||||||
result = desc.color;
|
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)
|
if (particle_kind == V_ParticleKind_Smoke)
|
||||||
{
|
{
|
||||||
// f32 t = saturate(density / 10.0);
|
// f32 t = saturate(density / 10.0);
|
||||||
// f32 t = smoothstep(-10, 32, density);
|
f32 t = smoothstep(-10, 32, density);
|
||||||
f32 t = smoothstep(0, 2, (f32)density);
|
// f32 t = smoothstep(0, 2, (f32)density);
|
||||||
result.a = lerp(0, 0.85, t);
|
result.a = lerp(0, 0.85, t);
|
||||||
}
|
}
|
||||||
else if (particle_kind == V_ParticleKind_Blood)
|
else if (particle_kind == V_ParticleKind_Blood)
|
||||||
{
|
{
|
||||||
f32 t = (f32)density / 10000;
|
// f32 t = (f32)density / 5;
|
||||||
t = pow(t, 2);
|
// t = pow(t, 2);
|
||||||
t = saturate(t);
|
// t = saturate(t);
|
||||||
result.rgb *= 1.0 - (t * 0.9);
|
// 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;
|
air_densities[cell_pos] = 0;
|
||||||
|
|
||||||
// Reset occluders
|
// Reset occluders
|
||||||
P_OccluderKind occluder = P_OccluderKind_None;
|
V_OccluderKind occluder = V_OccluderKind_None;
|
||||||
if (tile == P_TileKind_Wall)
|
if (tile == P_TileKind_Wall)
|
||||||
{
|
{
|
||||||
occluder = P_OccluderKind_Wall;
|
occluder = V_OccluderKind_Wall;
|
||||||
}
|
}
|
||||||
occluders[cell_pos] = occluder;
|
occluders[cell_pos] = occluder;
|
||||||
}
|
}
|
||||||
@ -198,8 +204,8 @@ PixelShader(V_QuadPS, V_QuadPSOutput, V_QuadPSInput input)
|
|||||||
|
|
||||||
if (is_in_world)
|
if (is_in_world)
|
||||||
{
|
{
|
||||||
// TODO: Remove this
|
// TODO: Don't write occluders using screen space result. Do separate draw pass instead.
|
||||||
if (albedo.a > 0 && quad.occluder != P_OccluderKind_None && is_in_world)
|
if (albedo.a > 0 && quad.occluder != V_OccluderKind_None && is_in_world)
|
||||||
{
|
{
|
||||||
InterlockedMax(occluders[cell_pos], quad.occluder);
|
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)
|
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;
|
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)
|
if (particle_idx < V_ParticlesCap)
|
||||||
{
|
{
|
||||||
V_Particle particle = particles[particle_idx];
|
V_Particle particle = particles[particle_idx];
|
||||||
|
b32 prune = 0;
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Initialize particle
|
//- 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_offset = Norm16(seed0 >> 0);
|
||||||
f32 rand_angle = Norm16(seed0 >> 16);
|
f32 rand_angle = Norm16(seed0 >> 16);
|
||||||
f32 rand_speed = Norm16(seed0 >> 32);
|
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_angle = lerp(emitter.angle.min, emitter.angle.max, rand_angle);
|
||||||
f32 initial_speed = lerp(emitter.speed.min, emitter.speed.max, rand_speed);
|
f32 initial_speed = lerp(emitter.speed.min, emitter.speed.max, rand_speed);
|
||||||
|
|
||||||
|
particle = (V_Particle)0;
|
||||||
particle.kind = emitter.kind;
|
particle.kind = emitter.kind;
|
||||||
particle.life = 0;
|
particle.life = 0;
|
||||||
particle.pos = lerp(emitter.pos.p0, emitter.pos.p1, rand_offset);
|
particle.pos = lerp(emitter.pos.p0, emitter.pos.p1, rand_offset);
|
||||||
particle.velocity = Vec2(cos(initial_angle), sin(initial_angle)) * initial_speed;
|
particle.velocity = Vec2(cos(initial_angle), sin(initial_angle)) * initial_speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (particle.kind > V_ParticleKind_None && particle.kind < V_ParticleKind_COUNT)
|
if (particle.kind > V_ParticleKind_None && particle.kind < V_ParticleKind_COUNT)
|
||||||
{
|
{
|
||||||
V_ParticleDesc desc = V_DescFromParticleKind((V_ParticleKind)particle.kind);
|
V_ParticleDesc desc = V_DescFromParticleKind((V_ParticleKind)particle.kind);
|
||||||
V_ParticleFlag flags = desc.flags;
|
|
||||||
|
|
||||||
u32 packed = 0;
|
u32 packed = 0;
|
||||||
packed |= (particle_idx & ((1 >> 24) - 1)) << 0;
|
packed |= (particle_idx & ((1 >> 24) - 1)) << 0;
|
||||||
@ -313,7 +318,6 @@ ComputeShader(V_SimParticlesCS, 64)
|
|||||||
|
|
||||||
b32 collision = 0;
|
b32 collision = 0;
|
||||||
|
|
||||||
// TODO: Use simulation dt
|
|
||||||
// TODO: Clip to avoid unnecessary iterations outside of world bounds
|
// TODO: Clip to avoid unnecessary iterations outside of world bounds
|
||||||
{
|
{
|
||||||
Vec2 p0 = particle.pos;
|
Vec2 p0 = particle.pos;
|
||||||
@ -335,7 +339,7 @@ ComputeShader(V_SimParticlesCS, 64)
|
|||||||
t_max *= inv_delta;
|
t_max *= inv_delta;
|
||||||
t_max = abs(t_max);
|
t_max = abs(t_max);
|
||||||
|
|
||||||
f32 t_hit = 0;
|
Vec2 t_hit = 0;
|
||||||
|
|
||||||
Vec2I32 cell_pos = cell_p0;
|
Vec2I32 cell_pos = cell_p0;
|
||||||
|
|
||||||
@ -345,15 +349,9 @@ ComputeShader(V_SimParticlesCS, 64)
|
|||||||
// u32 max_iterations = 32;
|
// u32 max_iterations = 32;
|
||||||
u32 max_iterations = 128;
|
u32 max_iterations = 128;
|
||||||
b32 done = 0;
|
b32 done = 0;
|
||||||
|
f32 t_diff = 0;
|
||||||
for (u32 iteration_idx = 0; iteration_idx < max_iterations && !done; ++iteration_idx)
|
for (u32 iteration_idx = 0; iteration_idx < max_iterations && !done; ++iteration_idx)
|
||||||
{
|
{
|
||||||
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)
|
if (cell_pos.x == cell_p1.x && cell_pos.y == cell_p1.y)
|
||||||
{
|
{
|
||||||
done = 1;
|
done = 1;
|
||||||
@ -361,7 +359,9 @@ ComputeShader(V_SimParticlesCS, 64)
|
|||||||
else if (t_max.x < t_max.y)
|
else if (t_max.x < t_max.y)
|
||||||
{
|
{
|
||||||
cell_pos.x += dda_step_dir.x;
|
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;
|
t_max.x += t_delta.x;
|
||||||
stepped_x = 1;
|
stepped_x = 1;
|
||||||
stepped_y = 0;
|
stepped_y = 0;
|
||||||
@ -369,39 +369,85 @@ ComputeShader(V_SimParticlesCS, 64)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
cell_pos.y += dda_step_dir.y;
|
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;
|
t_max.y += t_delta.y;
|
||||||
stepped_x = 0;
|
stepped_x = 0;
|
||||||
stepped_y = 1;
|
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)
|
if (is_in_world)
|
||||||
{
|
{
|
||||||
// Check collision
|
f32 stain_delta = abs(t_diff) * desc.stain_rate * frame.dt;
|
||||||
P_OccluderKind occluder = (P_OccluderKind)occluders[cell_pos];
|
particle.stain_accum += stain_delta;
|
||||||
if (occluder != P_OccluderKind_None)
|
|
||||||
|
//- Handle collision
|
||||||
|
V_OccluderKind occluder = (V_OccluderKind)occluders[cell_pos];
|
||||||
|
if (occluder != V_OccluderKind_None)
|
||||||
{
|
{
|
||||||
done = 1;
|
|
||||||
collision = 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (AnyBit(desc.flags, V_ParticleFlag_PruneWhenStill))
|
||||||
// Draw
|
|
||||||
{
|
{
|
||||||
b32 should_stain = 0;
|
if (dot(particle.velocity, particle.velocity) < 0.0001)
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
should_stain = 1;
|
prune = 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (should_stain)
|
if (prune && AnyBit(desc.flags, V_ParticleFlag_StainWhenPruned))
|
||||||
{
|
{
|
||||||
// InterlockedMax(stain_cells[cell_pos], packed);
|
particle.stain_accum += 1;
|
||||||
// InterlockedAdd(stain_densities[cell_pos], 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)
|
if (should_draw_ground)
|
||||||
{
|
{
|
||||||
@ -415,49 +461,14 @@ ComputeShader(V_SimParticlesCS, 64)
|
|||||||
InterlockedAdd(air_densities[cell_pos], 1);
|
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
|
else
|
||||||
{
|
{
|
||||||
done = 1;
|
done = 1;
|
||||||
|
prune = 1;
|
||||||
}
|
}
|
||||||
iteration_idx += 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(10, 20, rand_falloff) * frame.dt);
|
||||||
@ -467,263 +478,15 @@ ComputeShader(V_SimParticlesCS, 64)
|
|||||||
particle.pos = p0 + (p1 - p0) * t;
|
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
|
// Increment life
|
||||||
particle.life += frame.dt;
|
particle.life += frame.dt;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (prune)
|
||||||
{
|
{
|
||||||
particle.kind = V_ParticleKind_None;
|
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;
|
particles[particle_idx] = particle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,31 +11,37 @@ V_ParticleDesc V_DescFromParticleKind(V_ParticleKind kind)
|
|||||||
V_ParticleDesc result;
|
V_ParticleDesc result;
|
||||||
{
|
{
|
||||||
PERSIST Readonly V_ParticleFlag flags[V_ParticleKind_COUNT] = {
|
PERSIST Readonly V_ParticleFlag flags[V_ParticleKind_COUNT] = {
|
||||||
#define X(name, flags, 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)
|
V_ParticlesXList(X)
|
||||||
#undef X
|
#undef X
|
||||||
};
|
};
|
||||||
PERSIST Readonly f32 r[V_ParticleKind_COUNT] = {
|
PERSIST Readonly f32 r[V_ParticleKind_COUNT] = {
|
||||||
#define X(name, flags, r, g, b, a) r,
|
#define X(name, flags, stain_rate, r, g, b, a) r,
|
||||||
V_ParticlesXList(X)
|
V_ParticlesXList(X)
|
||||||
#undef X
|
#undef X
|
||||||
};
|
};
|
||||||
PERSIST Readonly f32 g[V_ParticleKind_COUNT] = {
|
PERSIST Readonly f32 g[V_ParticleKind_COUNT] = {
|
||||||
#define X(name, flags, r, g, b, a) g,
|
#define X(name, flags, stain_rate, r, g, b, a) g,
|
||||||
V_ParticlesXList(X)
|
V_ParticlesXList(X)
|
||||||
#undef X
|
#undef X
|
||||||
};
|
};
|
||||||
PERSIST Readonly f32 b[V_ParticleKind_COUNT] = {
|
PERSIST Readonly f32 b[V_ParticleKind_COUNT] = {
|
||||||
#define X(name, flags, r, g, b, a) b,
|
#define X(name, flags, stain_rate, r, g, b, a) b,
|
||||||
V_ParticlesXList(X)
|
V_ParticlesXList(X)
|
||||||
#undef X
|
#undef X
|
||||||
};
|
};
|
||||||
PERSIST Readonly f32 a[V_ParticleKind_COUNT] = {
|
PERSIST Readonly f32 a[V_ParticleKind_COUNT] = {
|
||||||
#define X(name, flags, r, g, b, a) a,
|
#define X(name, flags, stain_rate, r, g, b, a) a,
|
||||||
V_ParticlesXList(X)
|
V_ParticlesXList(X)
|
||||||
#undef X
|
#undef X
|
||||||
};
|
};
|
||||||
result.flags = flags[kind];
|
result.flags = flags[kind];
|
||||||
|
result.stain_rate = stain_rates[kind];
|
||||||
result.color = LinearFromSrgb(VEC4(r[kind], g[kind], b[kind], a[kind]));
|
result.color = LinearFromSrgb(VEC4(r[kind], g[kind], b[kind], a[kind]));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -162,25 +162,28 @@ Struct(V_SharedFrame)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Occluder types
|
//~ Occluder types
|
||||||
|
|
||||||
Enum(P_OccluderKind)
|
Enum(V_OccluderKind)
|
||||||
{
|
{
|
||||||
P_OccluderKind_None,
|
V_OccluderKind_None,
|
||||||
P_OccluderKind_Guy,
|
V_OccluderKind_Guy,
|
||||||
P_OccluderKind_Wall,
|
V_OccluderKind_Wall,
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//~ Particle types
|
//~ Particle types
|
||||||
|
|
||||||
#define P_ParticleSimBasis 0xb49f2d9e406873b9ull
|
#define V_ParticleSimBasis 0xb49f2d9e406873b9ull
|
||||||
#define P_ParticleColorBasis 0x569aa8341ecc0ea3ull
|
#define V_ParticleColorBasis 0x569aa8341ecc0ea3ull
|
||||||
|
#define V_ParticleCollisionBasis 0xf60c0cff344b0c5dull
|
||||||
|
#define V_ParticleStainBasis 0x3c64e8226d98d376ull
|
||||||
|
|
||||||
Enum(V_ParticleFlag)
|
Enum(V_ParticleFlag)
|
||||||
{
|
{
|
||||||
V_ParticleFlag_None = 0,
|
V_ParticleFlag_None = 0,
|
||||||
V_ParticleFlag_Stain = (1 << 0),
|
V_ParticleFlag_Ground = (1 << 0),
|
||||||
V_ParticleFlag_Ground = (1 << 1),
|
V_ParticleFlag_Air = (1 << 1),
|
||||||
V_ParticleFlag_Air = (1 << 2),
|
V_ParticleFlag_PruneWhenStill = (1 << 2),
|
||||||
|
V_ParticleFlag_StainWhenPruned = (1 << 3),
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: Higher particle enum values take priority over lower ones
|
// NOTE: Higher particle enum values take priority over lower ones
|
||||||
@ -188,34 +191,40 @@ Enum(V_ParticleFlag)
|
|||||||
X( \
|
X( \
|
||||||
None, \
|
None, \
|
||||||
V_ParticleFlag_None, \
|
V_ParticleFlag_None, \
|
||||||
|
0, \
|
||||||
0, 0, 0, 0 \
|
0, 0, 0, 0 \
|
||||||
) \
|
) \
|
||||||
/* Ground particles */ \
|
/* Ground particles */ \
|
||||||
X( \
|
X( \
|
||||||
Blood, \
|
Blood, \
|
||||||
V_ParticleFlag_Stain, \
|
V_ParticleFlag_Ground | V_ParticleFlag_PruneWhenStill, \
|
||||||
0.5, 0.1, 0.1, 0.5 \
|
500, \
|
||||||
|
0.5, 0.1, 0.1, 1 \
|
||||||
) \
|
) \
|
||||||
X( \
|
X( \
|
||||||
Debris, \
|
Debris, \
|
||||||
V_ParticleFlag_Ground | V_ParticleFlag_Stain, \
|
V_ParticleFlag_Ground | V_ParticleFlag_PruneWhenStill | V_ParticleFlag_StainWhenPruned , \
|
||||||
|
0, \
|
||||||
1, 0.5, 0, 1 \
|
1, 0.5, 0, 1 \
|
||||||
) \
|
) \
|
||||||
/* Air particles */ \
|
/* Air particles */ \
|
||||||
X( \
|
X( \
|
||||||
Smoke, \
|
Smoke, \
|
||||||
V_ParticleFlag_Air, \
|
V_ParticleFlag_Air, \
|
||||||
|
0, \
|
||||||
0.15, 0.15, 0.15, 0.5 \
|
0.15, 0.15, 0.15, 0.5 \
|
||||||
) \
|
) \
|
||||||
X( \
|
X( \
|
||||||
BulletTrail, \
|
BulletTrail, \
|
||||||
V_ParticleFlag_Air, \
|
V_ParticleFlag_Air, \
|
||||||
|
0, \
|
||||||
1, 0, 1, 1 \
|
1, 0, 1, 1 \
|
||||||
) \
|
) \
|
||||||
/* Test particles */ \
|
/* Test particles */ \
|
||||||
X( \
|
X( \
|
||||||
Test, \
|
Test, \
|
||||||
V_ParticleFlag_Stain, \
|
V_ParticleFlag_PruneWhenStill, \
|
||||||
|
0, \
|
||||||
1, 1, 0, 1 \
|
1, 1, 0, 1 \
|
||||||
) \
|
) \
|
||||||
/* -------------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------------- */
|
||||||
@ -244,7 +253,8 @@ Struct(V_Emitter)
|
|||||||
Struct(V_Particle)
|
Struct(V_Particle)
|
||||||
{
|
{
|
||||||
i32 kind; // If >= 0, then map to V_ParticleKind. Otherwize initialize particle using emitter at index [abs(kind) - 1]
|
i32 kind; // If >= 0, then map to V_ParticleKind. Otherwize initialize particle using emitter at index [abs(kind) - 1]
|
||||||
f32 life; // How many seconds has this particle been alive for
|
f32 life;
|
||||||
|
f32 stain_accum;
|
||||||
u32 collisions_count;
|
u32 collisions_count;
|
||||||
Vec2 pos;
|
Vec2 pos;
|
||||||
Vec2 velocity;
|
Vec2 velocity;
|
||||||
@ -253,6 +263,7 @@ Struct(V_Particle)
|
|||||||
Struct(V_ParticleDesc)
|
Struct(V_ParticleDesc)
|
||||||
{
|
{
|
||||||
V_ParticleFlag flags;
|
V_ParticleFlag flags;
|
||||||
|
f32 stain_rate;
|
||||||
Vec4 color;
|
Vec4 color;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -275,7 +286,7 @@ Enum(V_QuadFlag)
|
|||||||
Struct(V_Quad)
|
Struct(V_Quad)
|
||||||
{
|
{
|
||||||
V_QuadFlag flags;
|
V_QuadFlag flags;
|
||||||
P_OccluderKind occluder;
|
V_OccluderKind occluder;
|
||||||
Affine quad_uv_to_world_af;
|
Affine quad_uv_to_world_af;
|
||||||
G_Texture2DRef tex;
|
G_Texture2DRef tex;
|
||||||
Rng2 tex_slice_uv;
|
Rng2 tex_slice_uv;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user