cursor based tile placement

This commit is contained in:
jacob 2025-12-17 19:22:58 -06:00
parent 60608fc3cd
commit 767be8cf03
7 changed files with 238 additions and 83 deletions

View File

@ -99,6 +99,30 @@ u32 U32FromVec4(Vec4 v)
return result; return result;
} }
f32 LinearFromSrgbF32(f32 srgb)
{
f32 result;
if (srgb <= 0.04045f)
{
result = srgb / 12.92f;
}
else
{
result = pow((srgb + 0.055f) / 1.055f, 2.4f);
}
return result;
}
Vec4 LinearFromSrgb(Vec4 srgb)
{
Vec4 result;
result.x = LinearFromSrgbF32(srgb.x);
result.y = LinearFromSrgbF32(srgb.y);
result.z = LinearFromSrgbF32(srgb.z);
result.w = srgb.w;
return result;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Vertex ID helpers //~ Vertex ID helpers

View File

@ -20,6 +20,8 @@
@PixelShader V_DQuadPS @PixelShader V_DQuadPS
@VertexShader V_DVertVS @VertexShader V_DVertVS
@PixelShader V_DVertPS @PixelShader V_DVertPS
@VertexShader V_OverlayVS
@PixelShader V_OverlayPS
////////////////////////////// //////////////////////////////
//- Api //- Api

View File

@ -67,11 +67,17 @@ void V_TickForever(WaveLaneCtx *lane)
G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite
); );
gpu_tiles_ref = G_PushTexture2DRef(gpu_perm, gpu_tiles); gpu_tiles_ref = G_PushTexture2DRef(gpu_perm, gpu_tiles);
/* TODO: Clear tiles from GPU (instead of copy from cpu) */
G_CopyCpuToTexture(
cl,
gpu_tiles, VEC3I32(0, 0, 0),
tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1))
);
} }
G_CommitCommandList(cl); G_CommitCommandList(cl);
} }
for (u32 i = 0; i < countof(V.frames); ++i) for (u32 i = 0; i < countof(V.frames); ++i)
{ {
V_Frame *frame = &V.frames[i]; V_Frame *frame = &V.frames[i];
@ -439,11 +445,56 @@ void V_TickForever(WaveLaneCtx *lane)
frame->draw_to_world_xf = InvertXform(frame->world_to_draw_xf); frame->draw_to_world_xf = InvertXform(frame->world_to_draw_xf);
} }
/* Cursors */ //////////////////////////////
//- Update cursors / selection
frame->ui_cursor = ui_frame->cursor_pos; frame->ui_cursor = ui_frame->cursor_pos;
frame->draw_cursor = MulXformV2(frame->ui_to_draw_xf, frame->ui_cursor); frame->draw_cursor = MulXformV2(frame->ui_to_draw_xf, frame->ui_cursor);
frame->world_cursor = MulXformV2(frame->ui_to_world_xf, frame->ui_cursor); frame->world_cursor = MulXformV2(frame->ui_to_world_xf, frame->ui_cursor);
frame->ui_selection_start = frame->ui_cursor;
if (frame->is_editing)
{
frame->selection_mode = V_SelectionMode_Tile;
frame->is_selecting = frame->held_buttons[Button_M1];
if (frame->is_selecting && last_frame->is_selecting)
{
frame->ui_selection_start = last_frame->ui_selection_start;
}
}
frame->ui_selection.p0.x = MinF32(last_frame->ui_cursor.x, last_frame->ui_selection_start.x);
frame->ui_selection.p0.y = MinF32(last_frame->ui_cursor.y, last_frame->ui_selection_start.y);
frame->ui_selection.p1.x = MaxF32(last_frame->ui_cursor.x, last_frame->ui_selection_start.x);
frame->ui_selection.p1.y = MaxF32(last_frame->ui_cursor.y, last_frame->ui_selection_start.y);
frame->draw_selection.p0 = MulXformV2(last_frame->ui_to_draw_xf, frame->ui_selection.p0);
frame->draw_selection.p1 = MulXformV2(last_frame->ui_to_draw_xf, frame->ui_selection.p1);
frame->world_selection.p0 = MulXformV2(last_frame->ui_to_world_xf, frame->ui_selection.p0);
frame->world_selection.p1 = MulXformV2(last_frame->ui_to_world_xf, frame->ui_selection.p1);
//////////////////////////////
//- Place tiles
/* TODO: Push vis cmd */
if (frame->is_editing && last_frame->is_selecting && !frame->is_selecting)
{
if (last_frame->selection_mode == V_SelectionMode_Tile)
{
Rng2I32 tile_range = Zi;
tile_range.p0 = S_TilePosFromWorldPos(frame->world_selection.p0);
tile_range.p1 = S_TilePosFromWorldPos(frame->world_selection.p1);
tile_range.p1 = AddVec2I32(tile_range.p1, VEC2I32(1, 1));
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Tile);
cmd->tile_placement.placement_kind = S_TilePlacementKind_Range;
cmd->tile_placement.tile_kind = S_TileKind_Floor;
cmd->tile_placement.range = tile_range;
}
}
////////////////////////////// //////////////////////////////
//- Build command palette //- Build command palette
@ -647,7 +698,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Tile); S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Tile);
cmd->tile_placement.placement_kind = S_TilePlacementKind_Range; cmd->tile_placement.placement_kind = S_TilePlacementKind_Range;
cmd->tile_placement.tile_kind = S_TileKind_Floor; cmd->tile_placement.tile_kind = S_TileKind_Wall;
cmd->tile_placement.range.p0 = VEC2I32(100, 100); cmd->tile_placement.range.p0 = VEC2I32(100, 100);
cmd->tile_placement.range.p1 = VEC2I32(110, 110); cmd->tile_placement.range.p1 = VEC2I32(110, 110);
// cmd->tile_placement.range.p0 = VEC2I32(2, 2); // cmd->tile_placement.range.p0 = VEC2I32(2, 2);
@ -702,53 +753,6 @@ void V_TickForever(WaveLaneCtx *lane)
} }
UnlockTicketMutex(&S.input_back_tm); UnlockTicketMutex(&S.input_back_tm);
// LockTicketMutex(&S.input_back_tm);
// {
// S_InputState *v2s = &S.input_states[S.input_back_idx];
// /* Submit control cmd */
// {
// S_Cmd *cmd = 0;
// {
// S_CmdNode *cmd_node = PushStruct(v2s->arena, S_CmdNode);
// SllQueuePush(v2s->first_cmd_node, v2s->last_cmd_node, cmd_node);
// ++v2s->cmds_count;
// cmd = &cmd_node->cmd;
// }
// cmd->kind = S_CmdKind_Control;
// cmd->target = V.player_key;
// cmd->move = frame->move;
// cmd->look = frame->look;
// }
// /* Submit tile cmds */
// /* Submit spawn cmds */
// if (spawn_ents.count > 0)
// {
// S_Cmd *cmd = 0;
// {
// S_CmdNode *cmd_node = PushStruct(v2s->arena, S_CmdNode);
// SllQueuePush(v2s->first_cmd_node, v2s->last_cmd_node, cmd_node);
// ++v2s->cmds_count;
// cmd = &cmd_node->cmd;
// }
// cmd->kind = S_CmdKind_Spawn;
// S_EntList *dst = &cmd->ents;
// for (S_EntListNode *src_n = spawn_ents.first; src_n; src_n = src_n->next)
// {
// S_EntListNode *dst_n = PushStruct(v2s->arena, S_EntListNode);
// {
// SllQueuePush(dst->first, dst->last, dst_n);
// ++dst->count;
// }
// dst_n->ent = src_n->ent;
// }
// }
// }
// UnlockTicketMutex(&S.input_back_tm);
////////////////////////////// //////////////////////////////
//- Render //- Render
@ -825,20 +829,20 @@ void V_TickForever(WaveLaneCtx *lane)
params.world_to_draw_xf = frame->world_to_draw_xf; params.world_to_draw_xf = frame->world_to_draw_xf;
params.draw_to_world_xf = frame->draw_to_world_xf; params.draw_to_world_xf = frame->draw_to_world_xf;
params.target_cursor_pos = frame->draw_cursor; params.selection_mode = frame->selection_mode;
params.ui_cursor = frame->ui_cursor;
params.draw_cursor = frame->draw_cursor;
params.world_cursor = frame->world_cursor;
params.ui_selection = frame->ui_selection;
params.draw_selection = frame->draw_selection;
params.world_selection = frame->world_selection;
params.camera_pos = frame->camera_pos; params.camera_pos = frame->camera_pos;
params.camera_zoom = frame->camera_zoom; params.camera_zoom = frame->camera_zoom;
params.world_size = world_size; params.world_size = world_size;
params.tiles = gpu_tiles_ref; params.tiles = gpu_tiles_ref;
params.shape_verts = dverts_ro; params.shape_verts = dverts_ro;
params.background_color_a = LinearFromSrgb(VEC4(0.30, 0.30, 0.30, 1));
params.background_color_b = LinearFromSrgb(VEC4(0.15, 0.15, 0.15, 1));
params.grid_color = LinearFromSrgb(VEC4(0, 0, 0, 1));
params.x_axis_color = LinearFromSrgb(VEC4(0.75, 0, 0, 1));
params.y_axis_color = LinearFromSrgb(VEC4(0, 0.75, 0, 1));
params.bounds_color = LinearFromSrgb(VEC4(0.75, 0.75, 0, 1));
} }
G_ResourceHandle params_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(&params)); G_ResourceHandle params_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(&params));
G_StructuredBufferRef params_ro = G_PushStructuredBufferRef(frame->gpu_arena, params_buff, V_DParams); G_StructuredBufferRef params_ro = G_PushStructuredBufferRef(frame->gpu_arena, params_buff, V_DParams);
@ -859,7 +863,6 @@ void V_TickForever(WaveLaneCtx *lane)
G_DumbMemoryLayoutSync(frame->cl, draw_target, G_Layout_DirectQueue_ShaderReadWrite); G_DumbMemoryLayoutSync(frame->cl, draw_target, G_Layout_DirectQueue_ShaderReadWrite);
/* Backdrop pass */
{ {
G_Compute(frame->cl, V_BackdropCS, V_BackdropCSThreadSizeFromTexSize(frame->draw_dims)); G_Compute(frame->cl, V_BackdropCS, V_BackdropCSThreadSizeFromTexSize(frame->draw_dims));
} }
@ -869,14 +872,29 @@ void V_TickForever(WaveLaneCtx *lane)
G_DumbMemoryLayoutSync(frame->cl, draw_target, G_Layout_DirectQueue_RenderTargetWrite); G_DumbMemoryLayoutSync(frame->cl, draw_target, G_Layout_DirectQueue_RenderTargetWrite);
/* Shapes pass */
{ {
G_Rasterize(frame->cl, G_Rasterize(
V_DVertVS, V_DVertPS, frame->cl,
1, dvert_idxs_ib, V_DVertVS, V_DVertPS,
1, &draw_target, 1, dvert_idxs_ib,
viewport, scissor, 1, &draw_target,
G_RasterMode_TriangleList); viewport, scissor,
G_RasterMode_TriangleList
);
}
//////////////////////////////
//- Overlay pass
{
G_Rasterize(
frame->cl,
V_OverlayVS, V_OverlayPS,
1, G_QuadIndices(),
1, &draw_target,
viewport, scissor,
G_RasterMode_TriangleList
);
} }
////////////////////////////// //////////////////////////////

View File

@ -113,7 +113,10 @@ Struct(V_Frame)
V_EditMode edit_mode; V_EditMode edit_mode;
/* Editor */ /* Editor */
b32 is_selecting;
b32 is_panning; b32 is_panning;
V_SelectionMode selection_mode;
Vec2 ui_selection_start;
Vec2 edit_camera_pos; Vec2 edit_camera_pos;
f32 edit_camera_zoom; f32 edit_camera_zoom;
@ -137,6 +140,9 @@ Struct(V_Frame)
Vec2 ui_cursor; Vec2 ui_cursor;
Vec2 draw_cursor; Vec2 draw_cursor;
Vec2 world_cursor; Vec2 world_cursor;
Rng2 ui_selection;
Rng2 draw_selection;
Rng2 world_selection;
/* Control */ /* Control */
Vec2 move; Vec2 move;

View File

@ -3,6 +3,12 @@
G_DeclConstant(G_StructuredBufferRef, V_ShaderConst_Params, 0); G_DeclConstant(G_StructuredBufferRef, V_ShaderConst_Params, 0);
Enum(V_SelectionMode)
{
V_SelectionMode_None,
V_SelectionMode_Tile,
};
Struct(V_DParams) Struct(V_DParams)
{ {
Vec2I32 target_size; Vec2I32 target_size;
@ -12,7 +18,14 @@ Struct(V_DParams)
Xform world_to_draw_xf; Xform world_to_draw_xf;
Xform draw_to_world_xf; Xform draw_to_world_xf;
Vec2 target_cursor_pos; V_SelectionMode selection_mode;
Vec2 ui_cursor;
Vec2 draw_cursor;
Vec2 world_cursor;
Rng2 ui_selection;
Rng2 draw_selection;
Rng2 world_selection;
Vec2 camera_pos; Vec2 camera_pos;
f32 camera_zoom; f32 camera_zoom;
@ -20,13 +33,6 @@ Struct(V_DParams)
G_Texture2DRef tiles; G_Texture2DRef tiles;
G_StructuredBufferRef quads; G_StructuredBufferRef quads;
G_StructuredBufferRef shape_verts; G_StructuredBufferRef shape_verts;
Vec4 background_color_a;
Vec4 background_color_b;
Vec4 grid_color;
Vec4 x_axis_color;
Vec4 y_axis_color;
Vec4 bounds_color;
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -7,6 +7,13 @@ ComputeShader2D(V_BackdropCS, 8, 8)
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw); RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
Texture2D<uint> tiles = G_Dereference<uint>(params.tiles); Texture2D<uint> tiles = G_Dereference<uint>(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));
const Vec4 grid_color = LinearFromSrgb(Vec4(0, 0, 0, 1));
const Vec4 x_axis_color = LinearFromSrgb(Vec4(0.75, 0, 0, 1));
const Vec4 y_axis_color = LinearFromSrgb(Vec4(0, 0.75, 0, 1));
const Vec4 bounds_color = LinearFromSrgb(Vec4(0.75, 0.75, 0, 1));
Vec2U32 target_pos = SV_DispatchThreadID; Vec2U32 target_pos = SV_DispatchThreadID;
Vec2I32 target_size = params.target_size; Vec2I32 target_size = params.target_size;
if (target_pos.x < target_size.x && target_pos.y < target_size.y) if (target_pos.x < target_size.x && target_pos.y < target_size.y)
@ -29,15 +36,16 @@ ComputeShader2D(V_BackdropCS, 8, 8)
{ {
i32 color_idx = 0; i32 color_idx = 0;
Vec4 colors[2] = { Vec4 colors[2] = {
params.background_color_a, background_color_a,
params.background_color_b background_color_b
}; };
Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(1.0, 1.0)); const f32 checker_size = 0.5;
if (world_pos_modded.x < 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; color_idx = !color_idx;
} }
if (world_pos_modded.y < 0.5) if (world_pos_modded.y < checker_size)
{ {
color_idx = !color_idx; color_idx = !color_idx;
} }
@ -62,7 +70,7 @@ ComputeShader2D(V_BackdropCS, 8, 8)
grid_dist = min(grid_dist, abs(target_pos.y - grid_screen_p1.y)); grid_dist = min(grid_dist, abs(target_pos.y - grid_screen_p1.y));
if (grid_dist <= half_thickness) if (grid_dist <= half_thickness)
{ {
result = params.grid_color; result = grid_color;
} }
} }
/* Tile */ /* Tile */
@ -91,11 +99,11 @@ ComputeShader2D(V_BackdropCS, 8, 8)
f32 y_dist = abs(target_pos.y - zero_screen.y); f32 y_dist = abs(target_pos.y - zero_screen.y);
if (y_dist <= half_thickness) if (y_dist <= half_thickness)
{ {
result = params.x_axis_color; result = x_axis_color;
} }
else if (x_dist <= half_thickness) else if (x_dist <= half_thickness)
{ {
result = params.y_axis_color; result = y_axis_color;
} }
} }
/* World bounds */ /* World bounds */
@ -107,7 +115,7 @@ ComputeShader2D(V_BackdropCS, 8, 8)
bounds_dist = min(bounds_dist, abs(target_pos.y - bounds_screen_p1.y)); bounds_dist = min(bounds_dist, abs(target_pos.y - bounds_screen_p1.y));
if (bounds_dist <= half_thickness) if (bounds_dist <= half_thickness)
{ {
result = params.bounds_color; result = bounds_color;
} }
} }
} }
@ -189,3 +197,76 @@ PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input)
output.sv_target0 = input.color_lin; output.sv_target0 = input.color_lin;
return output; return output;
} }
////////////////////////////////////////////////////////////
//~ Overlay shader
//////////////////////////////
//- 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_DParams params = G_Dereference<V_DParams>(V_ShaderConst_Params)[0];
Vec2 screen_pos = input.sv_position.xy;
Vec4 result = 0;
Vec2 world_pos = mul(params.draw_to_world_xf, Vec3(screen_pos, 1));
Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos);
f32 half_thickness = 1;
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;
Rng2I32 tile_selection;
tile_selection.p0 = S_TilePosFromWorldPos(world_selection.p0);
tile_selection.p1 = S_TilePosFromWorldPos(world_selection.p1);
if (params.selection_mode == V_SelectionMode_Tile)
{
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)
{
result = border_color;
}
else
{
if (world_pos.x > -(S_WorldSize / 2) &&
world_pos.y > -(S_WorldSize / 2) &&
world_pos.x < (S_WorldSize / 2) &&
world_pos.y < (S_WorldSize / 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;
}
}
}
V_OverlayPSOutput output;
output.sv_target0 = result;
return output;
}

View File

@ -27,6 +27,20 @@ Struct(V_DVertPSOutput)
Semantic(Vec4, sv_target0); Semantic(Vec4, sv_target0);
}; };
////////////////////////////////////////////////////////////
//~ Overlay shader types
Struct(V_OverlayPSInput)
{
Semantic(Vec4, sv_position);
};
Struct(V_OverlayPSOutput)
{
Semantic(Vec4, sv_target0);
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Shaders //~ Shaders
@ -40,3 +54,7 @@ PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input);
//- Shape //- Shape
VertexShader(V_DVertVS, V_DVertPSInput); VertexShader(V_DVertVS, V_DVertPSInput);
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input); PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input);
//- Overlay
VertexShader(V_OverlayVS, V_OverlayPSInput);
PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input);