632 lines
21 KiB
Plaintext
632 lines
21 KiB
Plaintext
////////////////////////////////////////////////////////////
|
|
//~ Helpers
|
|
|
|
f32 V_RandFromPos(Vec3 pos)
|
|
{
|
|
Texture3D<u32> noise3d = G_Dereference<u32>(V_ShaderConst_NoiseTex);
|
|
Vec3I32 dims = countof(noise3d);
|
|
u32 noise = noise3d[floor(pos) % dims];
|
|
f32 rand = (f32)noise / 0xFFFF;
|
|
return rand;
|
|
}
|
|
|
|
Vec4 V_DryColor(Vec4 color, f32 dryness)
|
|
{
|
|
Vec4 result = color;
|
|
result.rgb *= 1.0 - (dryness * 0.75);
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Utility shaders
|
|
|
|
//- Clear cells
|
|
ComputeShader2D(V_ClearCellsCS, 8, 8)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells);
|
|
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
|
|
RWTexture2D<f32> drynesses = G_Dereference<f32>(params.drynesses);
|
|
Vec2 cells_idx = SV_DispatchThreadID;
|
|
if (cells_idx.x < countof(cells).x && cells_idx.y < countof(cells).y)
|
|
{
|
|
// Clear cell
|
|
cells[cells_idx] = 0;
|
|
|
|
// Clear stain
|
|
if (params.should_clear_stains)
|
|
{
|
|
stains[cells_idx] = 0;
|
|
drynesses[cells_idx] = 0;
|
|
}
|
|
|
|
// Increase dryness
|
|
f32 dry_rate = params.dt * 0.1;
|
|
{
|
|
f32 old_dryness = drynesses[cells_idx];
|
|
f32 new_dryness = lerp(old_dryness, 1, dry_rate);
|
|
drynesses[cells_idx] = new_dryness;
|
|
}
|
|
}
|
|
}
|
|
|
|
//- Clear particles
|
|
ComputeShader(V_ClearParticlesCS, 64)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
|
|
u32 particle_idx = SV_DispatchThreadID;
|
|
if (particle_idx < V_ParticlesCap)
|
|
{
|
|
particles[particle_idx].exists = 0;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Backdrop
|
|
|
|
ComputeShader2D(V_BackdropCS, 8, 8)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
|
Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(params.tiles);
|
|
|
|
const Vec4 background_color_a = LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1));
|
|
const Vec4 background_color_b = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1));
|
|
|
|
Vec2 ui_pos = SV_DispatchThreadID + Vec2(0.5, 0.5);
|
|
if (ui_pos.x < params.target_size.x && ui_pos.y < params.target_size.y)
|
|
{
|
|
Vec4 result = Vec4(0.025, 0.025, 0.025, 1);
|
|
Vec2 world_pos = mul(params.xf.ui_to_world, Vec3(ui_pos, 1));
|
|
Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(world_pos, 1)));
|
|
Vec2 tile_pos = mul(params.xf.world_to_tile, Vec3(world_pos, 1));
|
|
|
|
P_TileKind tile = tiles.Load(Vec3(tile_pos, 0));
|
|
|
|
f32 half_thickness = 1;
|
|
f32 half_bounds_size = P_WorldPitch * 0.5;
|
|
Vec2 bounds_screen_p0 = mul(params.xf.world_to_ui, Vec3(-half_bounds_size, -half_bounds_size, 1));
|
|
Vec2 bounds_screen_p1 = mul(params.xf.world_to_ui, Vec3(half_bounds_size, half_bounds_size, 1));
|
|
bool is_in_bounds = ui_pos.x > (bounds_screen_p0.x - half_thickness) &&
|
|
ui_pos.y > (bounds_screen_p0.y - half_thickness) &&
|
|
ui_pos.x < (bounds_screen_p1.x + half_thickness) &&
|
|
ui_pos.y < (bounds_screen_p1.y + half_thickness);
|
|
if (is_in_bounds)
|
|
{
|
|
// Grid checker
|
|
{
|
|
i32 color_idx = 0;
|
|
Vec4 colors[2] = {
|
|
background_color_a,
|
|
background_color_b
|
|
};
|
|
const f32 checker_size = 0.5;
|
|
Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(checker_size * 2, checker_size * 2));
|
|
if (world_pos_modded.x < checker_size)
|
|
{
|
|
color_idx = !color_idx;
|
|
}
|
|
if (world_pos_modded.y < checker_size)
|
|
{
|
|
color_idx = !color_idx;
|
|
}
|
|
if (world_pos.x < 0)
|
|
{
|
|
color_idx = !color_idx;
|
|
}
|
|
if (world_pos.y < 0)
|
|
{
|
|
color_idx = !color_idx;
|
|
}
|
|
result = colors[color_idx];
|
|
}
|
|
|
|
// Tile test
|
|
// TODO: Remove this
|
|
{
|
|
P_TileKind tile_tl = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y - 1, 0));
|
|
P_TileKind tile_tr = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y - 1, 0));
|
|
P_TileKind tile_br = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y + 1, 0));
|
|
P_TileKind tile_bl = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y + 1, 0));
|
|
P_TileKind tile_t = tiles.Load(Vec3(tile_pos.x, tile_pos.y - 1, 0));
|
|
P_TileKind tile_r = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y, 0));
|
|
P_TileKind tile_b = tiles.Load(Vec3(tile_pos.x, tile_pos.y + 1, 0));
|
|
P_TileKind tile_l = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y, 0));
|
|
|
|
f32 tile_edge_dist = Inf;
|
|
P_TileKind edge_tile = tile;
|
|
if (tile_tl != tile) { edge_tile = tile_tl; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(floor(tile_pos.x), floor(tile_pos.y)))); }
|
|
if (tile_tr != tile) { edge_tile = tile_tr; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(ceil(tile_pos.x), floor(tile_pos.y)))); }
|
|
if (tile_br != tile) { edge_tile = tile_br; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(ceil(tile_pos.x), ceil(tile_pos.y)))); }
|
|
if (tile_bl != tile) { edge_tile = tile_bl; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(floor(tile_pos.x), ceil(tile_pos.y)))); }
|
|
if (tile_l != tile) { edge_tile = tile_l; tile_edge_dist = min(tile_edge_dist, frac(tile_pos.x)); }
|
|
if (tile_r != tile) { edge_tile = tile_r; tile_edge_dist = min(tile_edge_dist, 1.0 - frac(tile_pos.x)); }
|
|
if (tile_t != tile) { edge_tile = tile_t; tile_edge_dist = min(tile_edge_dist, frac(tile_pos.y)); }
|
|
if (tile_b != tile) { edge_tile = tile_b; tile_edge_dist = min(tile_edge_dist, 1.0 - frac(tile_pos.y)); }
|
|
|
|
if (tile == P_TileKind_Wall)
|
|
{
|
|
Vec4 outer = LinearFromSrgb(Vec4(0.05, 0.05, 0.05, 1));
|
|
Vec4 inner = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1));
|
|
result = lerp(outer, inner, smoothstep(0, 1, tile_edge_dist / 0.375));
|
|
// result = lerp(outer, inner, smoothstep(0, 1, tile_edge_dist / 0.5));
|
|
}
|
|
else if (tile != P_TileKind_Empty)
|
|
{
|
|
SamplerState wrap_sampler = G_Dereference(params.pt_wrap_sampler);
|
|
SPR_Slice slice = params.tile_slices[tile];
|
|
Texture2D<Vec4> tile_tex = G_Dereference<Vec4>(slice.tex);
|
|
Vec4 tile_col = tile_tex.Sample(wrap_sampler, world_pos);
|
|
|
|
result = tile_col;
|
|
}
|
|
|
|
// switch (tile)
|
|
// {
|
|
// default: break;
|
|
|
|
// case P_TileKind_Floor:
|
|
// {
|
|
// result = Color_Blue;
|
|
// } break;
|
|
|
|
// case P_TileKind_Wall:
|
|
// {
|
|
// // result = Color_Red;
|
|
// result = Color_Black;
|
|
// } break;
|
|
// }
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Remove this
|
|
// Cells test
|
|
{
|
|
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
|
|
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells);
|
|
RWTexture2D<f32> drynesses = G_Dereference<f32>(params.drynesses);
|
|
|
|
Vec4 stain = stains.Load(cell_pos);
|
|
Vec4 cell = cells.Load(cell_pos);
|
|
f32 dryness = drynesses[cell_pos];
|
|
|
|
stain = V_DryColor(stain, dryness);
|
|
|
|
// cell.rgb *= cell.a;
|
|
// stain.rgb *= stain.a;
|
|
|
|
result.rgb = (stain.rgb * stain.a) + (result.rgb * (1.0 - stain.a));
|
|
result.a = (stain.a * 1) + (result.a * (1.0 - stain.a));
|
|
|
|
result.rgb = (cell.rgb * cell.a) + (result.rgb * (1.0 - cell.a));
|
|
result.a = (cell.a * 1) + (result.a * (1.0 - cell.a));
|
|
|
|
// Vec4 cell = cells.Load(cell_pos);
|
|
// if (cell.a != 0)
|
|
// {
|
|
// result = cell;
|
|
// }
|
|
// else
|
|
// {
|
|
// Vec4 stain = stains.Load(cell_pos);
|
|
// if (stain.a != 0)
|
|
// {
|
|
// result = stain;
|
|
// }
|
|
// }
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // TODO: Remove this
|
|
// // Cells test
|
|
// {
|
|
// RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells);
|
|
// Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(world_pos, 1)));
|
|
// Vec4 cell = cells.Load(cell_pos);
|
|
// if (cell.a != 0)
|
|
// {
|
|
// result = cell;
|
|
// }
|
|
// else
|
|
// {
|
|
// RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
|
|
// Vec4 stain = stains.Load(cell_pos);
|
|
// if (stain.a != 0)
|
|
// {
|
|
// result = stain;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
|
|
|
|
// TODO: Remove this
|
|
// Stains test
|
|
// {
|
|
// RWTexture2D<V_ParticleKind> stains = G_Dereference<V_ParticleKind>(params.stains);
|
|
// Vec2 cell_pos = mul(params.xf.world_to_cell, Vec3(world_pos, 1));
|
|
// V_ParticleKind stain = stains.Load(cell_pos);
|
|
|
|
// if (stain == V_ParticleKind_Test)
|
|
// {
|
|
// // result = Color_Yellow;
|
|
// // result = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
|
|
// // result = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
|
|
// }
|
|
// }
|
|
|
|
}
|
|
|
|
target[SV_DispatchThreadID] = result;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Quads
|
|
|
|
//////////////////////////////
|
|
//- Vertex shader
|
|
|
|
VertexShader(V_DQuadVS, V_DQuadPSInput)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(params.quads);
|
|
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
|
|
|
V_DQuad quad = quads[SV_InstanceID];
|
|
|
|
Vec2 rect_uv = RectUvFromVertexId(SV_VertexID);
|
|
// Vec2 tex_uv = lerp(quad.tex_uv0, quad.tex_uv1, rect_uv);
|
|
// Vec2 target_pos = lerp(quad.p0, quad.p1, rect_uv);
|
|
Vec2 target_pos = 0;
|
|
|
|
V_DQuadPSInput result;
|
|
result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1);
|
|
result.quad_idx = SV_InstanceID;
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- Pixel shader
|
|
|
|
PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(params.quads);
|
|
V_DQuad quad = quads[input.quad_idx];
|
|
|
|
Vec4 final_color = 0;
|
|
|
|
V_DQuadPSOutput output;
|
|
output.sv_target0 = final_color;
|
|
return output;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Particle simulation
|
|
|
|
//////////////////////////////
|
|
//- Particle emitter shader
|
|
|
|
// TODO: Initialize particles in per-particle-sim-thread rather than sequentially in emitter thread
|
|
|
|
ComputeShader(V_EmitParticlesCS, 64)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
RWStructuredBuffer<V_GpuState> state_buff = G_Dereference<V_GpuState>(V_ShaderConst_State);
|
|
StructuredBuffer<V_Emitter> emitters = G_Dereference<V_Emitter>(params.emitters);
|
|
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
|
|
|
|
u32 emitter_idx = SV_DispatchThreadID;
|
|
if (emitter_idx < params.emitters_count)
|
|
{
|
|
V_Emitter emitter = emitters[emitter_idx];
|
|
for (u32 i = 0; i < emitter.count; ++i)
|
|
{
|
|
u32 particle_seq = emitter.first_particle_seq + i;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- Particle sim shader
|
|
|
|
ComputeShader(V_SimParticlesCS, 64)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
|
|
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells);
|
|
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
|
|
RWTexture2D<f32> drynesses = G_Dereference<f32>(params.drynesses);
|
|
|
|
u32 particle_idx = SV_DispatchThreadID;
|
|
if (particle_idx < V_ParticlesCap)
|
|
{
|
|
V_Particle particle = particles[particle_idx];
|
|
if (particle.exists > 0)
|
|
{
|
|
// Initialize
|
|
if (particle.emitter_init_num != 0)
|
|
{
|
|
V_Emitter emitter = G_Dereference<V_Emitter>(params.emitters)[particle.emitter_init_num - 1];
|
|
|
|
u64 seed0 = MixU64(emitter.seed + particle.seq);
|
|
u64 seed1 = MixU64(seed0);
|
|
|
|
f32 rand_speed = (f32)((seed0 >> 0) & 0xFFFF) / (f32)0xFFFF;
|
|
f32 rand_angle = (f32)((seed0 >> 16) & 0xFFFF) / (f32)0xFFFF;
|
|
f32 rand_offset = (f32)((seed0 >> 32) & 0xFFFF) / (f32)0xFFFF;
|
|
f32 rand_falloff = (f32)((seed0 >> 48) & 0xFFFF) / (f32)0xFFFF;
|
|
|
|
f32 rand_r = (f32)((seed1 >> 0) & 0xFF) / (f32)0xFF;
|
|
f32 rand_g = (f32)((seed1 >> 8) & 0xFF) / (f32)0xFF;
|
|
f32 rand_b = (f32)((seed1 >> 16) & 0xFF) / (f32)0xFF;
|
|
f32 rand_a = (f32)((seed1 >> 24) & 0xFF) / (f32)0xFF;
|
|
f32 rand_lifetime = (f32)((seed1 >> 32) & 0xFFFF) / (f32)0xFFFF;
|
|
|
|
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;
|
|
}
|
|
|
|
Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(particle.pos, 1)));
|
|
b32 is_in_bounds = cell_pos.x >= 0 && cell_pos.y >= 0 && cell_pos.x < countof(stains).x && cell_pos.y < countof(stains).y;
|
|
|
|
// Simulate
|
|
f32 old_exists = particle.exists;
|
|
{
|
|
particle.pos += particle.velocity * params.dt;
|
|
particle.velocity = lerp(particle.velocity, 0, particle.velocity_falloff * params.dt);
|
|
particle.exists -= params.dt / particle.lifetime;
|
|
if ((particle.flags & V_ParticleFlag_PruneWhenStill) && (dot(particle.velocity, particle.velocity) < (0.1 * 0.1)))
|
|
{
|
|
particle.exists = 0;
|
|
}
|
|
if (particle.exists < 0.000001)
|
|
{
|
|
particle.exists = 0;
|
|
}
|
|
if (!is_in_bounds)
|
|
{
|
|
particle.exists = 0;
|
|
}
|
|
}
|
|
|
|
// Commit
|
|
{
|
|
// FIXME: Atomic write
|
|
if (is_in_bounds)
|
|
{
|
|
b32 should_stain = 0;
|
|
if ((particle.flags & V_ParticleFlag_StainTrail) || ((particle.flags & V_ParticleFlag_StainOnPrune) && particle.exists == 0))
|
|
{
|
|
should_stain = 1;
|
|
}
|
|
Vec4 color = particle.color;
|
|
color.a *= old_exists;
|
|
cells[cell_pos] = color;
|
|
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;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Prune out of bounds particle
|
|
particle.exists = 0;
|
|
}
|
|
}
|
|
particles[particle_idx] = particle;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Shapes
|
|
|
|
//////////////////////////////
|
|
//- Vertex shader
|
|
|
|
VertexShader(V_DVertVS, V_DVertPSInput)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
StructuredBuffer<V_DVert> verts = G_Dereference<V_DVert>(params.shape_verts);
|
|
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
|
|
|
V_DVert vert = verts[SV_VertexID];
|
|
|
|
Vec2 target_pos = vert.pos;
|
|
|
|
V_DVertPSInput result;
|
|
result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1);
|
|
result.color_lin = vert.color_lin;
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- Pixel shader
|
|
|
|
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input)
|
|
{
|
|
V_DVertPSOutput output;
|
|
output.sv_target0 = input.color_lin;
|
|
return output;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Overlay
|
|
|
|
//////////////////////////////
|
|
//- Vertex shader
|
|
|
|
VertexShader(V_OverlayVS, V_OverlayPSInput)
|
|
{
|
|
Vec2 uv = RectUvFromVertexId(SV_VertexID);
|
|
V_OverlayPSInput result;
|
|
result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1);
|
|
return result;
|
|
}
|
|
|
|
//////////////////////////////
|
|
//- Pixel shader
|
|
|
|
PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input)
|
|
{
|
|
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
|
Vec2 ui_pos = input.sv_position.xy;
|
|
Vec4 result = 0;
|
|
|
|
Vec2 world_pos = mul(params.xf.ui_to_world, Vec3(ui_pos, 1));
|
|
Vec2 tile_pos = mul(params.xf.world_to_tile, Vec3(world_pos, 1));
|
|
P_TileKind equipped_tile = params.equipped_tile;
|
|
|
|
f32 half_thickness = 1;
|
|
f32 half_bounds_size = P_WorldPitch * 0.5;
|
|
Vec2 bounds_screen_p0 = mul(params.xf.world_to_ui, Vec3(-half_bounds_size, -half_bounds_size, 1));
|
|
Vec2 bounds_screen_p1 = mul(params.xf.world_to_ui, Vec3(half_bounds_size, half_bounds_size, 1));
|
|
bool is_in_bounds = ui_pos.x > (bounds_screen_p0.x - half_thickness) &&
|
|
ui_pos.y > (bounds_screen_p0.y - half_thickness) &&
|
|
ui_pos.x < (bounds_screen_p1.x + half_thickness) &&
|
|
ui_pos.y < (bounds_screen_p1.y + half_thickness);
|
|
|
|
Vec4 border_color = LinearFromSrgb(Vec4(1, 1, 1, 1));
|
|
// Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.4, 0.4, 0.25));
|
|
|
|
Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.8, 0.4, 0.6));
|
|
|
|
Rng2 screen_selection = params.draw_selection;
|
|
Rng2 world_selection = params.world_selection;
|
|
|
|
Rng2 tile_selection;
|
|
tile_selection.p0 = floor(mul(params.xf.world_to_tile, Vec3(world_selection.p0, 1)));
|
|
tile_selection.p1 = ceil(mul(params.xf.world_to_tile, Vec3(world_selection.p1, 1)));
|
|
|
|
if (params.has_mouse_focus)
|
|
{
|
|
if (params.selection_mode == V_SelectionMode_Tile)
|
|
{
|
|
f32 dist = 100000000;
|
|
dist = min(dist, ui_pos.x - screen_selection.p0.x);
|
|
dist = min(dist, ui_pos.y - screen_selection.p0.y);
|
|
dist = min(dist, screen_selection.p1.x - ui_pos.x);
|
|
dist = min(dist, screen_selection.p1.y - ui_pos.y);
|
|
dist = -dist;
|
|
|
|
// if (dist >= -half_thickness && dist <= half_thickness)
|
|
// {
|
|
// result = border_color;
|
|
// }
|
|
// else
|
|
{
|
|
if (
|
|
world_pos.x > -(P_WorldPitch / 2) &&
|
|
world_pos.y > -(P_WorldPitch / 2) &&
|
|
world_pos.x < (P_WorldPitch / 2) &&
|
|
world_pos.y < (P_WorldPitch / 2) &&
|
|
tile_pos.x >= tile_selection.p0.x &&
|
|
tile_pos.x <= tile_selection.p1.x &&
|
|
tile_pos.y >= tile_selection.p0.y &&
|
|
tile_pos.y <= tile_selection.p1.y
|
|
)
|
|
{
|
|
result = inner_color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_in_bounds)
|
|
{
|
|
// Grid outline
|
|
if (V_ShaderConst_GpuFlags & V_GpuFlag_DebugDraw)
|
|
{
|
|
const Vec4 grid_color = LinearFromSrgb(Vec4(1, 1, 1, 0.1));
|
|
Vec2 grid_screen_p0 = mul(params.xf.world_to_ui, Vec3(floor(world_pos), 1));
|
|
Vec2 grid_screen_p1 = mul(params.xf.world_to_ui, Vec3(ceil(world_pos), 1));
|
|
f32 grid_dist = 100000;
|
|
grid_dist = min(grid_dist, abs(ui_pos.x - grid_screen_p0.x));
|
|
grid_dist = min(grid_dist, abs(ui_pos.x - grid_screen_p1.x));
|
|
grid_dist = min(grid_dist, abs(ui_pos.y - grid_screen_p0.y));
|
|
grid_dist = min(grid_dist, abs(ui_pos.y - grid_screen_p1.y));
|
|
if (grid_dist <= half_thickness * 0.5)
|
|
{
|
|
result = grid_color;
|
|
}
|
|
}
|
|
// Axis
|
|
if (V_ShaderConst_GpuFlags & V_GpuFlag_DebugDraw)
|
|
{
|
|
const Vec4 x_axis_color = LinearFromSrgb(Vec4(0.75, 0, 0, 1));
|
|
const Vec4 y_axis_color = LinearFromSrgb(Vec4(0, 0.75, 0, 1));
|
|
|
|
Vec2 zero_screen = mul(params.xf.world_to_ui, Vec3(0, 0, 1));
|
|
f32 x_dist = abs(ui_pos.x - zero_screen.x);
|
|
f32 y_dist = abs(ui_pos.y - zero_screen.y);
|
|
if (y_dist <= half_thickness)
|
|
{
|
|
result = x_axis_color;
|
|
}
|
|
else if (x_dist <= half_thickness)
|
|
{
|
|
result = y_axis_color;
|
|
}
|
|
}
|
|
// World bounds
|
|
{
|
|
const Vec4 bounds_color = LinearFromSrgb(Vec4(0.75, 0.75, 0, 1));
|
|
f32 bounds_dist = 100000;
|
|
bounds_dist = min(bounds_dist, abs(ui_pos.x - bounds_screen_p0.x));
|
|
bounds_dist = min(bounds_dist, abs(ui_pos.x - bounds_screen_p1.x));
|
|
bounds_dist = min(bounds_dist, abs(ui_pos.y - bounds_screen_p0.y));
|
|
bounds_dist = min(bounds_dist, abs(ui_pos.y - bounds_screen_p1.y));
|
|
if (bounds_dist <= half_thickness)
|
|
{
|
|
result = bounds_color;
|
|
}
|
|
}
|
|
}
|
|
|
|
V_OverlayPSOutput output;
|
|
output.sv_target0 = result;
|
|
return output;
|
|
}
|