diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index 1fb0ec56..d90009b4 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -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); diff --git a/src/pp/pp_sim/pp_sim_core.h b/src/pp/pp_sim/pp_sim_core.h index 1c1f878c..2fa1127e 100644 --- a/src/pp/pp_sim/pp_sim_core.h +++ b/src/pp/pp_sim/pp_sim_core.h @@ -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 diff --git a/src/pp/pp_sim/pp_sim_transcode.c b/src/pp/pp_sim/pp_sim_transcode.c index eeb5fbd4..5595a36a 100644 --- a/src/pp/pp_sim/pp_sim_transcode.c +++ b/src/pp/pp_sim/pp_sim_transcode.c @@ -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 { diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index c5a8e0dc..433747ea 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -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; } } diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index 5e522e03..9c28656e 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -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); ////////////////////////////////////////////////////////////