simulation debug drawing. begin collision resolution work.

This commit is contained in:
jacob 2026-01-03 01:44:07 -06:00
parent 5f6c1f629e
commit 0224b27902
5 changed files with 460 additions and 44 deletions

View File

@ -148,6 +148,8 @@ S_Shape S_MulXformShape(Xform xf, S_Shape shape)
}
Vec2 scale = ScaleFromXform(xf);
result.radius *= MaxF32(scale.x, scale.y);
result.centroid = MulXformV2(xf, shape.centroid);
result.center_of_mass = MulXformV2(xf, shape.center_of_mass);
return result;
}
@ -298,13 +300,63 @@ MergesortCompareFuncDef(S_SortEntsByKeyCmp, arg_a, arg_b, _)
return result;
}
////////////////////////////////////////////////////////////
//~ Debug draw
void S_DebugDrawPoint(Vec2 p, Vec4 srgb)
{
if (S.debug_draw_enabled)
{
S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc);
desc->kind = S_DebugDrawKind_Point;
desc->srgb32 = U32FromVec4(srgb);
desc->point.p = p;
}
}
void S_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb)
{
if (S.debug_draw_enabled)
{
S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc);
desc->kind = S_DebugDrawKind_Line;
desc->srgb32 = U32FromVec4(srgb);
desc->line.p0 = p0;
desc->line.p1 = p1;
}
}
void S_DebugDrawRect(Rng2 rect, Vec4 srgb)
{
if (S.debug_draw_enabled)
{
S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc);
desc->kind = S_DebugDrawKind_Rect;
desc->srgb32 = U32FromVec4(srgb);
desc->rect = rect;
}
}
void S_DebugDrawShape(S_Shape shape, Vec4 srgb)
{
if (S.debug_draw_enabled)
{
S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc);
desc->kind = S_DebugDrawKind_Shape;
desc->srgb32 = U32FromVec4(srgb);
desc->shape = shape;
}
}
////////////////////////////////////////////////////////////
//~ Sim tick
void S_TickForever(WaveLaneCtx *lane)
{
Arena *frame_arena = AcquireArena(Gibi(64));
Arena *perm = PermArena();
Arena *frame_arena = AcquireArena(Gibi(64));
S.debug_draw_descs_arena = AcquireArena(Gibi(64));
//- World data
Arena *ents_arena = AcquireArena(Gibi(64));
@ -324,6 +376,7 @@ void S_TickForever(WaveLaneCtx *lane)
while (!shutdown)
{
shutdown = Atomic32Fetch(&S.shutdown);
S.debug_draw_enabled = TweakBool("Simulation debug draw", 1);
ResetArena(frame_arena);
S_Iter iter = Zi;
@ -392,6 +445,14 @@ void S_TickForever(WaveLaneCtx *lane)
}
UnlockTicketMutex(&S.input_back_tm);
//////////////////////////////
//- Store double-buffered entity data
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
{
ent->last_xf = ent->xf;
}
//////////////////////////////
//- Process world edit commands
@ -458,19 +519,223 @@ void S_TickForever(WaveLaneCtx *lane)
}
//////////////////////////////
//- Apply control forces
//- Integrate control forces
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
{
Xform xf = ent->xf;
Xform desired_xf = xf;
if (!IsVec2Zero(ent->look))
{
xf = XformWithWorldRotation(xf, AngleFromVec2(ent->look));
desired_xf = XformWithWorldRotation(xf, AngleFromVec2(ent->look));
}
xf.og = AddVec2(xf.og, MulVec2(ent->move, ent->move_speed));
desired_xf.og = AddVec2(xf.og, MulVec2(ent->move, ent->move_speed));
Vec2 pos_diff = SubVec2(desired_xf.og, xf.og);
f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf));
ent->solved_dv = pos_diff;
ent->solved_dw = angle_diff;
}
//////////////////////////////
//- Prune constraints
//////////////////////////////
//- Generate player wall constraints
// TODO: Not like this
i64 max_constraints = 4096;
i64 constraints_count = 0;
S_Constraint *constraints = PushStructsNoZero(frame_arena, S_Constraint, max_constraints);
// for (i32 tile_y = bb_tiles.p0.y; tile_y < bb_tiles.p1.y; ++tile_y)
// {
// for (i32 tile_x = bb_tiles.p0.x; tile_x < bb_tiles.p1.x; ++tile_x)
// {
// Vec2I32 tile_pos = VEC2I32(tile_x, tile_y);
// S_TileKind tile = S_TileFromPos(tile_pos);
// }
// }
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
{
Xform last_xf = ent->last_xf;
S_Shape last_world_shape = S_MulXformShape(last_xf, ent->local_shape);
Xform xf = ent->xf;
S_Shape world_shape = S_MulXformShape(xf, ent->local_shape);
Rng2 bb0 = S_BoundingBoxFromShape(last_world_shape);
Rng2 bb1 = S_BoundingBoxFromShape(world_shape);
if (constraints_count < max_constraints)
{
S_Constraint *constraint = &constraints[constraints_count];
// TODO: Real constraint data
constraint->ent0 = ent->key;
constraint->shape0 = world_shape;
Rng2 test_rect = Zi;
test_rect.p0 = VEC2(-1, -1);
test_rect.p1 = VEC2(1, 1);
constraint->shape1 = S_ShapeFromDesc(
.count = 4,
.points[0] = VEC2(test_rect.p0.x, test_rect.p0.y),
.points[1] = VEC2(test_rect.p1.x, test_rect.p0.y),
.points[2] = VEC2(test_rect.p1.x, test_rect.p1.y),
.points[3] = VEC2(test_rect.p0.x, test_rect.p1.y),
);
constraints_count += 1;
}
}
//////////////////////////////
//- Solve constraints
for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx)
{
S_Constraint *constraint = &constraints[constraint_idx];
S_Ent *ent0 = S_EntFromKey(&lookup, constraint->ent0);
S_Ent *ent1 = S_EntFromKey(&lookup, constraint->ent1);
Vec2 old_dv0 = ent0->solved_dv;
Vec2 old_dv1 = ent1->solved_dv;
Vec2 dv0 = VEC2(0, 0);
Vec2 dv1 = VEC2(0, 0);
f32 dw0 = 0;
f32 dw1 = 0;
{
// // Get shapes
// S_Shape shape0 = S_ShapeFromDesc(.count = 1);
// S_Shape shape1 = S_ShapeFromDesc(.count = 1);
// if (ent0->active)
// {
// shape0 = S_MulXformShape(ent0->xf, ent0->local_shape);
// }
// if (ent1->active)
// {
// shape1 = S_MulXformShape(ent1->xf, ent1->local_shape);
// }
S_Shape shape0 = constraint->shape0;
S_Shape shape1 = constraint->shape1;
S_DebugDrawShape(shape1, Color_Cyan);
// TODO: Real dir
Vec2 shape_dir = NormVec2(SubVec2(shape1.centroid, shape0.centroid));
Vec2 neg_shape_dir = NegVec2(shape_dir);
// TODO: Real relative velocity
Vec2 rel_vel = SubVec2(old_dv1, old_dv0);
// Vec2 normal = NormVec2(rel_vel);
// Vec2 neg_normal = NegVec2(normal);
Vec2 normal = NormVec2(shape_dir);
Vec2 neg_normal = NegVec2(normal);
Vec2 shape0_pt = S_SupportPointFromShape(shape0, shape_dir);
Vec2 shape1_pt = S_SupportPointFromShape(shape1, neg_shape_dir);
Vec2 sep = SubVec2(shape1_pt, shape0_pt);
f32 sep_along_normal = DotVec2(sep, normal);
f32 rel_vel_along_normal = DotVec2(rel_vel, normal);
// f32 sep_normal = DotVec2(sep, normal);
S_DebugDrawPoint(shape0_pt, Color_Cyan);
S_DebugDrawPoint(shape1_pt, Color_Cyan);
if (sep_along_normal < 0)
{
dv0 = AddVec2(dv0, MulVec2(normal, sep_along_normal));
}
// dv0 = VEC2(sep_normal, sep_normal);
// f32 separation = constraint->sep;
}
// Update solved velocity
if (ent0->active)
{
ent0->solved_dv = AddVec2(ent0->solved_dv, dv0);
ent0->solved_dw += dw0;
}
if (ent1->active)
{
ent1->solved_dv = AddVec2(ent1->solved_dv, dv1);
ent1->solved_dw += dw1;
}
}
//////////////////////////////
//- Integrate velocities
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
{
Xform xf = ent->xf;
xf.og = AddVec2(xf.og, ent->solved_dv);
xf = RotateXform(xf, ent->solved_dw);
ent->xf = xf;
}
//////////////////////////////
//- Debug draw entities
if (S.debug_draw_enabled)
{
for (S_Ent *ent = S_FirstEnt(&iter, world); ent->active; ent = S_NextEnt(&iter))
{
Xform xf = ent->xf;
S_Shape world_shape = S_MulXformShape(xf, ent->local_shape);
// Draw aabb
{
Vec4 color = VEC4(0.4, 0.2, 0.2, 1);
Rng2 bb = S_BoundingBoxFromShape(world_shape);
S_DebugDrawRect(bb, color);
}
// Draw shape
{
// Vec4 color = Color_Cyan;
Vec4 color = VEC4(0.2, 0.4, 0.2, 1);
S_DebugDrawShape(world_shape, color);
}
// Draw look
{
Vec4 color = VEC4(0.8, 0.8, 0.8, 1);
Vec2 p0 = world_shape.centroid;
Vec2 p1 = S_SupportPointFromShape(world_shape, ent->look);
S_DebugDrawLine(p0, p1, color);
}
}
}
//////////////////////////////
//- Publish sim state
@ -528,6 +793,14 @@ void S_TickForever(WaveLaneCtx *lane)
}
}
}
// Push debug draw information
{
output->debug_draw_descs_count = ArenaCount(S.debug_draw_descs_arena, S_DebugDrawDesc);
output->debug_draw_descs = PushStructsNoZero(output->arena, S_DebugDrawDesc, output->debug_draw_descs_count);
CopyStructs(output->debug_draw_descs, ArenaFirst(S.debug_draw_descs_arena, S_DebugDrawDesc), output->debug_draw_descs_count);
ResetArena(S.debug_draw_descs_arena);
}
}
UnlockTicketMutex(&S.output_back_tm);

View File

@ -65,7 +65,9 @@ Struct(S_Ent)
//////////////////////////////
//- Build data
Xform last_xf;
Xform xf;
S_Shape local_shape;
f32 move_speed;
@ -74,11 +76,16 @@ Struct(S_Ent)
b32 has_weapon;
//////////////////////////////
//- Solver data
Vec2 solved_dv;
f32 solved_dw;
//////////////////////////////
//- Internal sim data
i64 next_free_ent_num;
};
////////////////////////////////////////////////////////////
@ -103,6 +110,18 @@ Struct(S_EntList)
u64 count;
};
////////////////////////////////////////////////////////////
//~ Constraint types
Struct(S_Constraint)
{
S_Key ent0;
S_Key ent1;
S_Shape shape0;
S_Shape shape1;
};
////////////////////////////////////////////////////////////
//~ Lookup types
@ -190,6 +209,40 @@ Struct(S_CmdNode)
S_Cmd cmd;
};
////////////////////////////////////////////////////////////
//~ Debug visualization types
Enum(S_DebugDrawKind)
{
S_DebugDrawKind_Point,
S_DebugDrawKind_Line,
S_DebugDrawKind_Rect,
S_DebugDrawKind_Shape,
};
Struct(S_DebugDrawDesc)
{
S_DebugDrawKind kind;
u32 srgb32;
union
{
struct
{
Vec2 p;
} point;
struct
{
Vec2 p0;
Vec2 p1;
} line;
Rng2 rect;
S_Shape shape;
};
};
////////////////////////////////////////////////////////////
//~ State types
@ -204,9 +257,13 @@ Struct(S_InputState)
Struct(S_OutputState)
{
Arena *arena;
S_SnapshotNode *first_snapshot_node;
S_SnapshotNode *last_snapshot_node;
u64 snapshots_count;
u64 debug_draw_descs_count;
S_DebugDrawDesc *debug_draw_descs;
};
Struct(S_Ctx)
@ -214,6 +271,9 @@ Struct(S_Ctx)
Atomic32 shutdown;
Fence shutdown_fence;
b32 debug_draw_enabled;
Arena *debug_draw_descs_arena;
//- Sim input
TicketMutex input_back_tm;
i32 input_back_idx;
@ -290,6 +350,14 @@ S_World *S_WorldFromSnapshot(Arena *arena, S_Snapshot *snapshot);
MergesortCompareFuncDef(S_SortEntsByKeyCmp, arg_a, arg_b, _);
////////////////////////////////////////////////////////////
//~ Debug draw
void S_DebugDrawPoint(Vec2 p, Vec4 srgb);
void S_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb);
void S_DebugDrawRect(Rng2 rect, Vec4 srgb);
void S_DebugDrawShape(S_Shape shape, Vec4 srgb);
////////////////////////////////////////////////////////////
//~ Sim tick

View File

@ -82,18 +82,20 @@ S_TranscodeResult S_TranscodeLevel(Arena *arena, b32 pack, String packed, S_Worl
// TODO: Compress entity data
// Write entity size & alignment
u32 ent_size = sizeof(S_Ent);
u32 ent_align = alignof(S_Ent);
if (result.ok)
{
if (pack)
{
BB_WriteUBits(&bw, sizeof(S_Ent), 32);
BB_WriteUBits(&bw, alignof(S_Ent), 32);
BB_WriteUBits(&bw, ent_size, 32);
BB_WriteUBits(&bw, ent_align, 32);
}
else
{
u32 ent_size = BB_ReadUBits(&br, 32);
u32 ent_align = BB_ReadUBits(&br, 32);
result.ok = ent_size == sizeof(S_Ent) && ent_align == alignof(S_Ent);
ent_size = BB_ReadUBits(&br, 32);
ent_align = BB_ReadUBits(&br, 32);
}
}
@ -116,9 +118,12 @@ S_TranscodeResult S_TranscodeLevel(Arena *arena, b32 pack, String packed, S_Worl
S_Ent *ents_raw = (S_Ent *)BB_ReadBytesRaw(&br, ents_count * sizeof(S_Ent));
if (ents_raw)
{
result.unpacked.world.ents_count = ents_count;
result.unpacked.world.ents = PushStructsNoZero(arena, S_Ent, ents_count);
CopyStructs(result.unpacked.world.ents, ents_raw, ents_count);
if (ent_size == sizeof(S_Ent) && ent_align == alignof(S_Ent))
{
result.unpacked.world.ents_count = ents_count;
result.unpacked.world.ents = PushStructsNoZero(arena, S_Ent, ents_count);
CopyStructs(result.unpacked.world.ents, ents_raw, ents_count);
}
}
else
{

View File

@ -100,7 +100,7 @@ void V_DrawPoly(Vec2Array points, Vec4 srgb, V_DrawFlag flags)
{
i32 a_idx = line_idx;
i32 b_idx = line_idx + 1;
if (b_idx >= lines_count)
if (b_idx >= points.count)
{
b_idx = 0;
}
@ -200,13 +200,25 @@ void V_DrawShape(S_Shape shape, Vec4 srgb, i32 detail, V_DrawFlag flags)
}
}
void V_DrawLine(Vec2 p0, Vec2 p1, Vec4 srgb)
{
Vec2 points[2] = {
p0,
p1
};
Vec2Array points_arr = Zi;
points_arr.points = points;
points_arr.count = countof(points);
V_DrawPoly(points_arr, srgb, V_DrawFlag_Line);
}
void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags)
{
Vec2 points[4] = {
VEC2(rect.p0.x, rect.p0.y),
VEC2(rect.p1.x, rect.p0.y),
VEC2(rect.p1.x, rect.p1.y),
VEC2(rect.p0.x, rect.p1.y),
VEC2(rect.p0.x, rect.p1.y)
};
Vec2Array points_arr = Zi;
points_arr.points = points;
@ -555,6 +567,11 @@ void V_TickForever(WaveLaneCtx *lane)
ResetArena(V.world_arena);
V.world = S_WorldFromSnapshot(V.world_arena, &sim_output->last_snapshot_node->snapshot);
V.lookup = S_LookupFromWorld(V.world_arena, V.world);
// Copy sim debug info
V.debug_draw_descs_count = sim_output->debug_draw_descs_count;
V.debug_draw_descs = PushStructsNoZero(V.world_arena, S_DebugDrawDesc, V.debug_draw_descs_count);
CopyStructs(V.debug_draw_descs, sim_output->debug_draw_descs, V.debug_draw_descs_count);
}
//////////////////////////////
@ -2353,7 +2370,7 @@ void V_TickForever(WaveLaneCtx *lane)
{
//////////////////////////////
//- Build render data
//- Draw entities
// Build shape buffers
for (S_Ent *ent = S_FirstEnt(&iter, V.world); ent->active; ent = S_NextEnt(&iter))
@ -2362,41 +2379,91 @@ void V_TickForever(WaveLaneCtx *lane)
Xform ent_to_draw_xf = MulXform(frame->world_to_draw_xf, ent_to_world_xf);
S_Shape draw_shape = S_MulXformShape(ent_to_draw_xf, ent->local_shape);
f32 opacity = 0.5;
b32 is_visible = 1;
if (is_visible)
{
// Draw shape
{
Vec4 color = Color_Purple;
i32 detail = 32;
V_DrawShape(draw_shape, color, detail, V_DrawFlag_Line);
}
// // Draw shape
// {
// Vec4 color = Color_Purple;
// color.w *= opacity;
// i32 detail = 32;
// V_DrawShape(draw_shape, color, detail, V_DrawFlag_Line);
// }
// Draw weapon
if (ent->has_weapon)
{
Vec4 color = Color_Cyan;
f32 width = 0.1;
f32 height = 0.75;
S_Shape local_shape = S_ShapeFromDesc(
.count = 4,
.points = {
VEC2(-width / 2, -height), VEC2(width / 2, -height),
VEC2(width / 2, 0), VEC2(-width / 2, 0),
}
);
Xform local_xf = XformFromTrs(TRS(.t = { 0, 0 }, .r = Tau / 4));
Xform xf = MulXform(ent_to_draw_xf, local_xf);
S_Shape shape = S_MulXformShape(xf, local_shape);
V_DrawShape(shape, color, 10, V_DrawFlag_Line);
}
// if (ent->has_weapon)
// {
// Vec4 color = Color_Cyan;
// color.w *= opacity;
// f32 width = 0.1;
// f32 height = 0.75;
// S_Shape local_shape = S_ShapeFromDesc(
// .count = 4,
// .points = {
// VEC2(-width / 2, -height), VEC2(width / 2, -height),
// VEC2(width / 2, 0), VEC2(-width / 2, 0),
// }
// );
// Xform local_xf = XformFromTrs(TRS(.t = { 0, 0 }, .r = Tau / 4));
// Xform xf = MulXform(ent_to_draw_xf, local_xf);
// S_Shape shape = S_MulXformShape(xf, local_shape);
// V_DrawShape(shape, color, 10, V_DrawFlag_Line);
// }
// Draw aabb
// // Draw aabb
// {
// Vec4 color = Color_Orange;
// color.w *= opacity;
// Rng2 bb = S_BoundingBoxFromShape(draw_shape);
// V_DrawRect(bb, color, V_DrawFlag_Line);
// }
}
}
//////////////////////////////
//- Draw sim debug shapes
for (u64 desc_idx = 0; desc_idx < V.debug_draw_descs_count; ++desc_idx)
{
S_DebugDrawDesc *desc = &V.debug_draw_descs[desc_idx];
Vec4 color = Vec4FromU32(desc->srgb32);
i32 detail = 24;
f32 radius = 5;
switch(desc->kind)
{
case S_DebugDrawKind_Point:
{
Vec4 color = Color_Orange;
Rng2 bb = S_BoundingBoxFromShape(draw_shape);
V_DrawRect(bb, color, V_DrawFlag_Line);
}
Vec2 ui_p = MulXformV2(frame->world_to_ui_xf, desc->point.p);
S_Shape ui_shape = S_ShapeFromDesc(
.count = 1,
.points = { ui_p },
.radius = radius
);
V_DrawShape(ui_shape, color, detail, V_DrawFlag_None);
} break;
case S_DebugDrawKind_Line:
{
Vec2 ui_p0 = MulXformV2(frame->world_to_ui_xf, desc->line.p0);
Vec2 ui_p1 = MulXformV2(frame->world_to_ui_xf, desc->line.p1);
V_DrawLine(ui_p0, ui_p1, color);
} break;
case S_DebugDrawKind_Rect:
{
Rng2 ui_rect = Zi;
ui_rect.p0 = MulXformV2(frame->world_to_ui_xf, desc->rect.p0);
ui_rect.p1 = MulXformV2(frame->world_to_ui_xf, desc->rect.p1);
V_DrawRect(ui_rect, color, V_DrawFlag_Line);
} break;
case S_DebugDrawKind_Shape:
{
S_Shape ui_shape = S_MulXformShape(frame->world_to_ui_xf, desc->shape);
V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
} break;
}
}

View File

@ -283,6 +283,8 @@ Struct(V_Ctx)
S_World *world;
S_Lookup lookup;
S_Key player_key;
u64 debug_draw_descs_count;
S_DebugDrawDesc *debug_draw_descs;
i64 panels_count;
i64 windows_count;
@ -318,6 +320,7 @@ String V_StringFromHotkey(Arena *arena, V_Hotkey hotkey);
void V_DrawPoly(Vec2Array points, Vec4 srgb, V_DrawFlag flags);
void V_DrawShape(S_Shape shape, Vec4 srgb, i32 detail, V_DrawFlag flags);
void V_DrawLine(Vec2 p0, Vec2 p1, Vec4 srgb);
void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags);
////////////////////////////////////////////////////////////