//////////////////////////////////////////////////////////// //~ Helpers f32 V_RandFromPos(Vec3 pos) { Texture3D noise3d = G_Dereference(V_ShaderConst_NoiseTex); // TODO: Compile-time noise dims u32 noise = noise3d[(Vec3U32)pos % countof(noise3d)]; 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_ShaderConst_Params)[0]; RWTexture2D cells = G_Dereference(params.cells); RWTexture2D stains = G_Dereference(params.stains); RWTexture2D drynesses = G_Dereference(params.drynesses); Vec2 cells_idx = SV_DispatchThreadID; if (all(cells_idx < countof(cells))) { // 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_ShaderConst_Params)[0]; RWStructuredBuffer particles = G_Dereference(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_ShaderConst_Params)[0]; RWTexture2D screen = G_Dereference(params.screen_rw); Texture2D tiles = G_Dereference(params.tiles); SamplerState wrap_sampler = G_Dereference(params.pt_wrap_sampler); 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 screen_pos = SV_DispatchThreadID + Vec2(0.5, 0.5); Vec4 result = Vec4(0.025, 0.025, 0.025, 1); Vec2 world_pos = mul(params.xf.screen_to_world, Vec3(screen_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_world_bounds_size = P_WorldPitch * 0.5; Vec2 world_bounds_screen_p0 = mul(params.xf.world_to_screen, Vec3(-half_world_bounds_size, -half_world_bounds_size, 1)); Vec2 world_bounds_screen_p1 = mul(params.xf.world_to_screen, Vec3(half_world_bounds_size, half_world_bounds_size, 1)); b32 is_in_world_bounds = screen_pos.x > (world_bounds_screen_p0.x - half_thickness) && screen_pos.y > (world_bounds_screen_p0.y - half_thickness) && screen_pos.x < (world_bounds_screen_p1.x + half_thickness) && screen_pos.y < (world_bounds_screen_p1.y + half_thickness); if (is_in_world_bounds) { // // Checkered grid // { // 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) { SPR_Slice slice = params.tile_slices[tile]; Texture2D tile_tex = G_Dereference(slice.tex); Vec4 tile_col = tile_tex.SampleLevel(wrap_sampler, world_pos, 0); 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 stains = G_Dereference(params.stains); RWTexture2D cells = G_Dereference(params.cells); RWTexture2D drynesses = G_Dereference(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 cells = G_Dereference(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 stains = G_Dereference(params.stains); // Vec4 stain = stains.Load(cell_pos); // if (stain.a != 0) // { // result = stain; // } // } // } // TODO: Remove this // Stains test // { // RWTexture2D stains = G_Dereference(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)); // } // } } if (all(screen_pos < countof(screen))) { screen[screen_pos] = result; } } //////////////////////////////////////////////////////////// //~ Quads ////////////////////////////// //- Vertex shader VertexShader(V_QuadVS, V_QuadPSInput) { V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; StructuredBuffer quads = G_Dereference(params.quads); RWTexture2D screen = G_Dereference(params.screen_rw); V_Quad quad = quads[SV_InstanceID]; Vec2 rect_uv = RectUvFromVertexId(SV_VertexID); // Vec2 tex_uv = lerp(quad.tex_uv0, quad.tex_uv1, rect_uv); // Vec2 screen_pos = lerp(quad.p0, quad.p1, rect_uv); Vec2 screen_pos = 0; V_QuadPSInput result; result.sv_position = Vec4(NdcFromPos(screen_pos, countof(screen)).xy, 0, 1); result.quad_idx = SV_InstanceID; return result; } ////////////////////////////// //- Pixel shader PixelShader(V_QuadPS, V_QuadPSOutput, V_QuadPSInput input) { V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; StructuredBuffer quads = G_Dereference(params.quads); V_Quad quad = quads[input.quad_idx]; Vec4 final_color = 0; V_QuadPSOutput 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_ShaderConst_Params)[0]; RWStructuredBuffer state_buff = G_Dereference(V_ShaderConst_State); StructuredBuffer emitters = G_Dereference(params.emitters); RWStructuredBuffer particles = G_Dereference(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_ShaderConst_Params)[0]; RWStructuredBuffer particles = G_Dereference(params.particles); RWTexture2D cells = G_Dereference(params.cells); RWTexture2D stains = G_Dereference(params.stains); RWTexture2D drynesses = G_Dereference(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(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_world_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_world_bounds) { particle.exists = 0; } } // Commit { // FIXME: Atomic write if (is_in_world_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; } } } //////////////////////////////////////////////////////////// //~ Shade ComputeShader2D(V_ShadeCS, 8, 8) { V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; RWTexture2D shade_tex = G_Dereference(params.shade_rw); Texture2D tiles = G_Dereference(params.tiles); SamplerState wrap_sampler = G_Dereference(params.pt_wrap_sampler); Vec2 shade_pos = SV_DispatchThreadID + Vec2(0.5, 0.5); Vec2 world_pos = mul(params.xf.shade_to_world, Vec3(shade_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)); Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5; b32 is_in_world_bounds = all(world_pos > -half_world_dims) && all(world_pos < half_world_dims); //- Tile color // TODO: Remove this b32 tile_is_wall = 0; Vec4 tile_color = 0; if (is_in_world_bounds) { 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)); tile_color = lerp(outer, inner, smoothstep(0, 1, tile_edge_dist / 0.375)); // tile_color = lerp(outer, inner, smoothstep(0, 1, tile_edge_dist / 0.5)); tile_is_wall = 1; } else if (tile != P_TileKind_Empty) { SPR_Slice slice = params.tile_slices[tile]; Texture2D tile_tex = G_Dereference(slice.tex); tile_color = tile_tex.SampleLevel(wrap_sampler, world_pos, 0); } // Checkered grid else if (tile == P_TileKind_Empty) { i32 color_idx = 0; Vec4 colors[2] = { LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1)), LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1)) }; 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; } tile_color = colors[color_idx]; } // switch (tile) // { // default: break; // case P_TileKind_Floor: // { // tile_color = Color_Blue; // } break; // case P_TileKind_Wall: // { // // tile_color = Color_Red; // tile_color = Color_Black; // } break; // } } //- Composite Vec4 result = 0; result = BlendPremul(!tile_is_wall * tile_color, result); // Blend floor tile result = BlendPremul(tile_is_wall * tile_color, result); // Blend wall tile if (all(shade_pos < countof(shade_tex))) { shade_tex[shade_pos] = result; } } //////////////////////////////////////////////////////////// //~ Composite ////////////////////////////// //- Vertex shader VertexShader(V_CompositeVS, V_CompositePSInput) { Vec2 uv = RectUvFromVertexId(SV_VertexID); V_CompositePSInput result; result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1); return result; } ////////////////////////////// //- Pixel shader PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input) { V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; Texture2D shade_tex = G_Dereference(params.shade_ro); SamplerState clamp_sampler = G_Dereference(params.pt_clamp_sampler); Vec2 screen_pos = input.sv_position.xy; Vec2 world_pos = mul(params.xf.screen_to_world, Vec3(screen_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; Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5; Vec2 world_bounds_screen_p0 = mul(params.xf.world_to_screen, Vec3(-half_world_dims.xy, 1)); Vec2 world_bounds_screen_p1 = mul(params.xf.world_to_screen, Vec3(half_world_dims.xy, 1)); b32 is_in_world_bounds = ( screen_pos.x > (world_bounds_screen_p0.x - half_thickness) && screen_pos.y > (world_bounds_screen_p0.y - half_thickness) && screen_pos.x < (world_bounds_screen_p1.x + half_thickness) && screen_pos.y < (world_bounds_screen_p1.y + half_thickness) ); Vec2 shade_pos = mul(params.xf.screen_to_shade, Vec3(screen_pos.xy, 1)); //- Shaded color Vec4 shade_color = 0; if (all(shade_pos >= Vec2(0, 0)) && all(shade_pos < countof(shade_tex))) { Vec2 shade_uv = shade_pos / countof(shade_tex); shade_color = shade_tex.SampleLevel(clamp_sampler, shade_uv, 0); } //- Tile selection overlay Vec4 selection_color = 0; if (params.has_mouse_focus && params.selection_mode == V_SelectionMode_Tile) { 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.screen_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))); f32 dist = 100000000; dist = min(dist, screen_pos.x - screen_selection.p0.x); dist = min(dist, screen_pos.y - screen_selection.p0.y); dist = min(dist, screen_selection.p1.x - screen_pos.x); dist = min(dist, screen_selection.p1.y - screen_pos.y); dist = -dist; // if (dist >= -half_thickness && dist <= half_thickness) // { // selection_color = 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 ) { selection_color = inner_color; } } // Premultiply selection_color.rgb *= selection_color.a; } //- Grid overlay Vec4 overlay_color = 0; if (is_in_world_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_screen, Vec3(floor(world_pos), 1)); Vec2 grid_screen_p1 = mul(params.xf.world_to_screen, Vec3(ceil(world_pos), 1)); f32 grid_dist = 100000; grid_dist = min(grid_dist, abs(screen_pos.x - grid_screen_p0.x)); grid_dist = min(grid_dist, abs(screen_pos.x - grid_screen_p1.x)); grid_dist = min(grid_dist, abs(screen_pos.y - grid_screen_p0.y)); grid_dist = min(grid_dist, abs(screen_pos.y - grid_screen_p1.y)); if (grid_dist <= half_thickness * 0.5) { overlay_color = 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_screen, Vec3(0, 0, 1)); f32 x_dist = abs(screen_pos.x - zero_screen.x); f32 y_dist = abs(screen_pos.y - zero_screen.y); if (y_dist <= half_thickness) { overlay_color = x_axis_color; } else if (x_dist <= half_thickness) { overlay_color = 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(screen_pos.x - world_bounds_screen_p0.x)); bounds_dist = min(bounds_dist, abs(screen_pos.x - world_bounds_screen_p1.x)); bounds_dist = min(bounds_dist, abs(screen_pos.y - world_bounds_screen_p0.y)); bounds_dist = min(bounds_dist, abs(screen_pos.y - world_bounds_screen_p1.y)); if (bounds_dist <= half_thickness) { overlay_color = bounds_color; } } // Premultiply overlay_color.rgb *= overlay_color.a; } //- Composite Vec4 result = 0; result = BlendPremul(shade_color, result); result = BlendPremul(selection_color, result); result = BlendPremul(overlay_color, result); result = Unpremul(result); V_CompositePSOutput output; output.sv_target0 = result; return output; } //////////////////////////////////////////////////////////// //~ Debug shapes ////////////////////////////// //- Vertex shader VertexShader(V_DVertVS, V_DVertPSInput) { V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; StructuredBuffer verts = G_Dereference(params.shape_verts); RWTexture2D screen = G_Dereference(params.screen_rw); V_DVert vert = verts[SV_VertexID]; Vec2 screen_pos = vert.pos; V_DVertPSInput result; result.sv_position = Vec4(NdcFromPos(screen_pos, countof(screen)).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; }