diff --git a/src/pp/pp.c b/src/pp/pp.c new file mode 100644 index 00000000..8d51297a --- /dev/null +++ b/src/pp/pp.c @@ -0,0 +1,1258 @@ +P_Ctx P = Zi; +P_ThreadLocalCtx P_tl = Zi; + +Readonly P_Ent P_NilEnt = { + .xf = CompXformIdentity, + .look = { 0, -1 }, +}; + +//////////////////////////////////////////////////////////// +//~ Bootstrap + +void P_Bootstrap(void) +{ + // Initialize shared state + for (u64 i = 0; i < countof(P.sim_input_states); ++i) + { + P_InputState *input = &P.sim_input_states[i]; + input->arena = AcquireArena(Gibi(64)); + } + for (u64 i = 0; i < countof(P.sim_output_states); ++i) + { + P_OutputState *output = &P.sim_output_states[i]; + output->arena = AcquireArena(Gibi(64)); + } + + // Initialize thread state + P_tl.debug_arena = AcquireArena(Gibi(64)); +} + +//////////////////////////////////////////////////////////// +//~ Nil helpers + +b32 P_IsKeyNil(P_Key key) +{ + return key.v == 0; +} + +b32 P_IsEntNil(P_Ent *ent) +{ + return ent == 0 || ent == &P_NilEnt; +} + +b32 P_MatchKey(P_Key a, P_Key b) +{ + return a.v == b.v; +} + +//////////////////////////////////////////////////////////// +//~ Key helpers + +P_Key P_RandKey(void) +{ + // TODO: Don't use true randomness for entity keys. It's overkill & non-deterministic. + P_Key result = Zi; + TrueRand(StringFromStruct(&result)); + return result; +} + +//////////////////////////////////////////////////////////// +//~ Tile helpers + +String P_NameFromTileKind(P_TileKind kind) +{ + // Tile names array +#define X(name, ...) [P_TileKind_##name] = CompLit(#name), + PERSIST Readonly String tile_names[] = { + P_TilesXMacro(X) + }; +#undef X + + String result = Lit("Unknown"); + if (kind >= 0 && kind < countof(tile_names)) + { + result = tile_names[kind]; + } + return result; +} + +//////////////////////////////////////////////////////////// +//~ Delta helpers + +void P_UpdateWorldFromDelta(Arena *arena, P_World *world, P_Delta *delta) +{ + // FIXME: Bounds check tile deltas + if (0) + { + } + //- Reset + if (delta->kind == P_DeltaKind_Reset) + { + // FIXME: Free list entities + world->ents_count = 0; + world->first_ent = 0; + world->last_ent = 0; + ZeroStructs(world->tiles, P_TilesCount); + ZeroStructs(world->ent_bins, world->ent_bins_count); + } + //- Raw ent + if (delta->kind == P_DeltaKind_RawEnt) + { + P_Key key = delta->ent.key; + P_Ent *ent = P_EntFromKey(world, key); + if (!ent->valid) + { + // FIXME: Use ent free list + ent = PushStructNoZero(arena, P_Ent); + *ent = P_NilEnt; + ent->key = key; + P_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count]; + DllQueuePushNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); + DllQueuePush(world->first_ent, world->last_ent, ent); + ++world->ents_count; + } + P_Ent *old_next = ent->next; + P_Ent *old_prev = ent->prev; + P_Ent *old_next_in_bin = ent->next_in_bin; + P_Ent *old_prev_in_bin = ent->prev_in_bin; + { + P_Ent *src = &delta->ent; + *ent = *src; + } + ent->next = old_next; + ent->prev = old_prev; + ent->next_in_bin = old_next_in_bin; + ent->prev_in_bin = old_prev_in_bin; + ent->valid = 1; + } + //- Raw tiles + else if (delta->kind == P_DeltaKind_RawTiles) + { + Rng2I32 range = delta->tile_range; + for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y) + { + i32 src_tile_y = tile_y - range.p0.y; + for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x) + { + i32 src_tile_x = tile_x - range.p0.x; + Vec2 src_tile_pos = VEC2(src_tile_x, src_tile_y); + i32 src_tile_idx = P_TileIdxFromTilePos(src_tile_pos); + u8 src_tile = delta->raw_tiles[src_tile_idx]; + + Vec2 tile_pos = VEC2(tile_x, tile_y); + i32 tile_idx = P_TileIdxFromTilePos(tile_pos); + world->tiles[tile_idx] = src_tile; + } + } + } + //- Tile range + else if (delta->kind == P_DeltaKind_Tile) + { + P_TileKind tile = delta->tile_kind; + Rng2I32 range = delta->tile_range; + for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y) + { + for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x) + { + Vec2 tile_pos = VEC2(tile_x, tile_y); + i32 tile_idx = P_TileIdxFromTilePos(tile_pos); + world->tiles[tile_idx] = (u8)tile; + } + } + } +} + +//////////////////////////////////////////////////////////// +//~ Shape helpers + +P_Shape P_ShapeFromDescEx(P_ShapeDesc desc) +{ + desc.count = MaxI32(desc.count, 1); + P_Shape result = Zi; + { + result.points_count = desc.count; + CopyStructs(result.points, desc.points, result.points_count); + Vec2 accum = Zi; + for (i32 p_idx = 0; p_idx < result.points_count; ++p_idx) + { + accum = AddVec2(accum, result.points[p_idx]); + } + result.centroid = DivVec2(accum, result.points_count); + result.center_of_mass = result.centroid; + result.radius = desc.radius; + result.mass = desc.mass; + } + return result; +} + +P_Shape P_MulXformShape(Xform xf, P_Shape shape) +{ + P_Shape result = shape; + for (i32 i = 0; i < shape.points_count; ++i) + { + result.points[i] = MulXformV2(xf, shape.points[i]); + } + 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; +} + +Rng2 P_BoundingBoxFromShape(P_Shape shape) +{ + Vec2 left = P_SupportPointFromShape(shape, VEC2(-1, 0)).p; + Vec2 top = P_SupportPointFromShape(shape, VEC2(0, -1)).p; + Vec2 right = P_SupportPointFromShape(shape, VEC2(1, 0)).p; + Vec2 bottom = P_SupportPointFromShape(shape, VEC2(0, 1)).p; + + Rng2 result = Zi; + result.p0 = VEC2(left.x, top.y); + result.p1 = VEC2(right.x, bottom.y); + return result; +} + +P_Shape P_LocalShapeFromEnt(P_Ent *ent) +{ + P_Shape result = Zi; + + // TODO: This is a temporary hack. We should eventually switch to using a prefab lookup table. + if (ent->is_player) + { + result = P_ShapeFromDesc( + .mass = 10, + .count = 1, + .radius = 0.3, + ); + + // Rng2 test_rect = Zi; + // test_rect.p0 = VEC2(-1, -1); + // test_rect.p1 = VEC2(1, 1); + // result = P_ShapeFromDesc( + // // .radius = 0.5, + // .radius = 0, + // .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), + // ); + } + + return result; +} + +P_Shape P_WorldShapeFromEnt(P_Ent *ent) +{ + P_Shape local = P_LocalShapeFromEnt(ent); + P_Shape world = P_MulXformShape(ent->xf, local); + return world; +} + +//////////////////////////////////////////////////////////// +//~ Collision + +// NOTE: Everything here is pretty much copied directly from the old prototype. +// The techniques are slow and do more than what we need. For example we should +// probably just switch from GJK to SAT for shape collision testing. + +P_SupportPoint P_SupportPointFromShapeEx(P_Shape shape, Vec2 dir, i32 ignore_idx) +{ + P_SupportPoint result = Zi; + Vec2 dir_norm = NormVec2(dir); + f32 max_dot = -Inf; + if (shape.points_count == 1) + { + // Don't ignore for single-point colliders + ignore_idx = -1; + } + for (i32 point_idx = 0; point_idx < shape.points_count; ++point_idx) + { + if (point_idx != ignore_idx) + { + Vec2 p = shape.points[point_idx]; + f32 dot = DotVec2(p, dir_norm); + if (dot > max_dot) + { + max_dot = dot; + result.p = p; + result.id = point_idx; + } + } + } + result.p = AddVec2(result.p, MulVec2(dir_norm, shape.radius)); + return result; +} + +P_SupportPoint P_SupportPointFromShape(P_Shape shape, Vec2 dir) +{ + return P_SupportPointFromShapeEx(shape, dir, -1); +} + +P_MenkowskiPoint P_MenkowskiPointFromShapes(P_Shape shape0, P_Shape shape1, Vec2 dir) +{ + P_MenkowskiPoint result = Zi; + result.s0 = P_SupportPointFromShape(shape0, dir); + result.s1 = P_SupportPointFromShape(shape1, NegVec2(dir)); + result.p = SubVec2(result.s0.p, result.s1.p); + return result; +} + +P_ClippedLine P_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal) +{ + Vec2 vab0 = SubVec2(b0, a0); + Vec2 vab1 = SubVec2(b1, a1); + Vec2 va0a1 = SubVec2(a1, a0); + Vec2 vb0b1 = SubVec2(b1, b0); + f32 vab0_w = WedgeVec2(vab0, normal); + f32 vab1_w = WedgeVec2(vab1, normal); + f32 va0a1_w = WedgeVec2(va0a1, normal); + f32 vb0b1_w = WedgeVec2(vb0b1, normal); + + // FIXME: Handle 0 denominator + f32 a0t; + f32 b0t; + { + f32 w = 1 / vab0_w; + a0t = ClampF32(va0a1_w * w, 0, 1); + b0t = ClampF32(vb0b1_w * -w, 0, 1); + } + f32 a1t; + f32 b1t; + { + f32 w = 1 / vab1_w; + a1t = ClampF32(-va0a1_w * w, 0, 1); + b1t = ClampF32(-vb0b1_w * -w, 0, 1); + } + + P_ClippedLine result = Zi; + result.a0_clipped = AddVec2(a0, MulVec2(vab0, a0t)); + result.a1_clipped = AddVec2(a1, MulVec2(vab1, a1t)); + result.b0_clipped = AddVec2(b0, MulVec2(vab0, -b0t)); + result.b1_clipped = AddVec2(b1, MulVec2(vab1, -b1t)); + return result; +} + +Vec2 P_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal) +{ + Vec2 vab = SubVec2(b, a); + Vec2 vap = SubVec2(p, a); + + f32 vab_w = WedgeVec2(vab, normal); + f32 vap_w = WedgeVec2(vap, normal); + + f32 w = 1 / vab_w; + f32 t = ClampF32(vap_w * w, 0, 1); + + Vec2 result = AddVec2(a, MulVec2(vab, t)); + return result; +} + +P_CollisionResult P_CollisionResultFromShapes(P_Shape shape0, P_Shape shape1) +{ + P_CollisionResult result = Zi; + TempArena scratch = BeginScratchNoConflict(); + + f32 tolerance = 0.00005f; // How close can non-overlapping shapes be before collision is considered + f32 min_unique_pt_dist_sq = (0.001f * 0.001f); // NOTE: Should always be less than tolerance, since colliding = 1 if origin is within this distance. + u32 max_iterations = 64; // To prevent extremely large prototypes when origin is in exact center of rounded feature + + ////////////////////////////// + //- GJK + + P_MenkowskiSimplex simplex = Zi; + Vec2 non_overlapping_dir = Zi; + b32 is_overlapping = 0; + { + P_MenkowskiPoint m = Zi; + + // First point is support point in shape's general directions to eachother + Vec2 dir = SubVec2(shape1.centroid, shape0.centroid); + if (IsVec2Zero(dir)) dir = VEC2(1, 0); + simplex.a = P_MenkowskiPointFromShapes(shape0, shape1, dir); + simplex.count = 1; + + Vec2 removed_a = Zi; + Vec2 removed_b = Zi; + u32 num_removed = 0; + for (;;) + { + ////////////////////////////// + //- Find initial points in simplex + + if (simplex.count == 1) + { + // Second point is support point towards origin + dir = NegVec2(simplex.a.p); + + m = P_MenkowskiPointFromShapes(shape0, shape1, dir); + // Check that new point is far enough away from existing point + if (Vec2LenSq(SubVec2(m.p, simplex.a.p)) < min_unique_pt_dist_sq) + { + is_overlapping = 0; + break; + } + simplex.b = simplex.a; + simplex.a = m; + simplex.count = 2; + + // Third point is support point in direction of line normal towards origin + dir = PerpVec2TowardsDir(SubVec2(simplex.b.p, simplex.a.p), NegVec2(simplex.a.p)); + } + + ////////////////////////////// + //- Find third point in simplex + + { + m = P_MenkowskiPointFromShapes(shape0, shape1, dir); + // Check that new point is far enough away from existing points + if ( + Vec2LenSq(SubVec2(m.p, simplex.a.p)) < min_unique_pt_dist_sq || + Vec2LenSq(SubVec2(m.p, simplex.b.p)) < min_unique_pt_dist_sq || ( + (num_removed >= 1) && ( + (Vec2LenSq(SubVec2(m.p, removed_a)) < min_unique_pt_dist_sq) || + (num_removed >= 2 && Vec2LenSq(SubVec2(m.p, removed_b)) < min_unique_pt_dist_sq) + ) + ) || + AbsF32(WedgeVec2(SubVec2(simplex.b.p, simplex.a.p), SubVec2(m.p, simplex.a.p))) < min_unique_pt_dist_sq + ) + { + is_overlapping = 0; + break; + } + simplex.c = simplex.b; + simplex.b = simplex.a; + simplex.a = m; + simplex.count = 3; + + if ( + (AbsF32(WedgeVec2(SubVec2(simplex.b.p, simplex.a.p), NegVec2(simplex.a.p))) <= min_unique_pt_dist_sq) || + (AbsF32(WedgeVec2(SubVec2(simplex.c.p, simplex.b.p), NegVec2(simplex.b.p))) <= min_unique_pt_dist_sq) || + (AbsF32(WedgeVec2(SubVec2(simplex.c.p, simplex.a.p), NegVec2(simplex.a.p))) <= min_unique_pt_dist_sq) + ) + { + // Simplex lies on origin + is_overlapping = 1; + break; + } + } + + ////////////////////////////// + //- Determine origin region + + Vec2 vab = SubVec2(simplex.b.p, simplex.a.p); + Vec2 vac = SubVec2(simplex.c.p, simplex.a.p); + Vec2 vbc = SubVec2(simplex.c.p, simplex.b.p); + + Vec2 rab_dir = PerpVec2TowardsDir(vab, NegVec2(vac)); + Vec2 rac_dir = PerpVec2TowardsDir(vac, NegVec2(vab)); + Vec2 rbc_dir = PerpVec2TowardsDir(vbc, vab); + + f32 rab_dot = DotVec2(rab_dir, NegVec2(simplex.a.p)); + f32 rac_dot = DotVec2(rac_dir, NegVec2(simplex.a.p)); + f32 rbc_dot = DotVec2(rbc_dir, NegVec2(simplex.b.p)); + + f32 vab_dot = DotVec2(vab, NegVec2(simplex.a.p)) / Vec2LenSq(vab); + f32 vac_dot = DotVec2(vac, NegVec2(simplex.a.p)) / Vec2LenSq(vac); + f32 vbc_dot = DotVec2(vbc, NegVec2(simplex.b.p)) / Vec2LenSq(vbc); + + if (rab_dot >= 0 && vab_dot >= 0 && vab_dot <= 1) + { + // Region ab, remove c + num_removed = 1; + removed_a = simplex.c.p; + simplex.count = 2; + dir = rab_dir; // Next third point is in direction of region ab + } + else if (rac_dot >= 0 && vac_dot >= 0 && vac_dot <= 1) + { + // Region ac, remove b + num_removed = 1; + removed_a = simplex.b.p; + simplex.count = 2; + simplex.b = simplex.c; + dir = rac_dir; // Next third point is in direction of region ac + } + else if (rbc_dot >= 0 && vbc_dot >= 0 && vbc_dot <= 1) + { + // Region bc, remove a + num_removed = 1; + removed_a = simplex.a.p; + simplex.count = 2; + simplex.a = simplex.b; + simplex.b = simplex.c; + dir = rbc_dir; // Next third point is in direction of region bc + } + else if (vab_dot <= 0 && vac_dot <= 0) + { + // Region a, remove bc + num_removed = 2; + removed_a = simplex.b.p; + removed_b = simplex.c.p; + simplex.count = 1; + } + else if (vab_dot >= 1 && vbc_dot <= 0) + { + // Region b, remove ac + num_removed = 2; + removed_a = simplex.a.p; + removed_b = simplex.c.p; + simplex.count = 1; + simplex.a = simplex.b; + } + else if (vac_dot >= 1 && vbc_dot >= 1) + { + // Region c, remove ab + num_removed = 2; + removed_a = simplex.a.p; + removed_b = simplex.b.p; + simplex.count = 1; + simplex.a = simplex.c; + } + else + { + // No region, must be in simplex + is_overlapping = 1; + break; + } + } + if (!is_overlapping) + { + non_overlapping_dir = dir; + } + } + + ////////////////////////////// + //- EPA + + // Find dir from origin to closest edge + Vec2 normal = Zi; + P_MenkowskiSimplex closest_feature = Zi; + { + P_MenkowskiPoint *proto = 0; + if (is_overlapping) + { + u32 proto_count = 0; + proto = ArenaNext(scratch.arena, P_MenkowskiPoint); + { + Assert(simplex.count == 3); + P_MenkowskiPoint *tmp = PushStructsNoZero(scratch.arena, P_MenkowskiPoint, 3); + tmp[0] = simplex.a; + tmp[1] = simplex.b; + tmp[2] = simplex.c; + proto_count = 3; + } + + i32 winding = WindingFromVec2(SubVec2(simplex.c.p, simplex.a.p), SubVec2(simplex.b.p, simplex.a.p)); + + u32 epa_iterations = 0; + for (;;) + { + ++epa_iterations; + + // FIXME: Winding order of ps & pe index + P_MenkowskiPoint closest_a = Zi; + P_MenkowskiPoint closest_b = Zi; + u32 closest_b_index = 0; + { + // Find edge segment on prototype closest to the origin + f32 closest_len_sq = Inf; + for (u32 i = 0; i < proto_count; ++i) + { + u32 a_index = i; + u32 b_index = (i < proto_count - 1) ? (i + 1) : 0; + P_MenkowskiPoint a = proto[a_index]; + P_MenkowskiPoint b = proto[b_index]; + Vec2 vab = SubVec2(b.p, a.p); + Vec2 vao = NegVec2(a.p); + f32 proj_ratio = ClampF32(DotVec2(vao, vab) / Vec2LenSq(vab), 0, 1); + Vec2 proj = AddVec2(a.p, MulVec2(vab, proj_ratio)); + f32 proj_len_sq = Vec2LenSq(proj); + if (proj_len_sq < closest_len_sq - min_unique_pt_dist_sq) + { + closest_a = a; + closest_b = b; + closest_b_index = b_index; + closest_len_sq = proj_len_sq; + } + } + } + Vec2 vab = SubVec2(closest_b.p, closest_a.p); + + // Find new point in dir + Vec2 dir = MulVec2(PerpVec2(vab), winding); + P_MenkowskiPoint m = P_MenkowskiPointFromShapes(shape0, shape1, dir); + + // Check validity of new point + { + b32 valid = 1; + { + // NOTE: Changing this value affects how stable normals are for rounded colliders + //const f32 validity_epsilon = min_unique_pt_dist_sq; // Arbitrary + //const f32 validity_epsilon = 0.00000000001f; // Arbitrary + const f32 validity_epsilon = min_unique_pt_dist_sq; // Arbitrary + + Vec2 vam = SubVec2(m.p, closest_a.p); + Vec2 vbm = SubVec2(closest_b.p, closest_a.p); + + f32 dot = DotVec2(vab, vam) / Vec2LenSq(vab); + + if (dot >= -validity_epsilon && dot <= 1 - validity_epsilon && (WedgeVec2(vab, vam) * -winding) >= -validity_epsilon) + { + // New point is not between edge + valid = 0; + } + else if (Vec2LenSq(vam) < min_unique_pt_dist_sq || Vec2LenSq(vbm) < min_unique_pt_dist_sq) + { + // New point is too close to existing + valid = 0; + } + } + + if (!valid || epa_iterations >= max_iterations) + { + normal = NormVec2(dir); + closest_feature.a = closest_a; + closest_feature.b = closest_b; + closest_feature.count = 2; + break; + } + } + + // Expand prototype + PushStructNoZero(scratch.arena, P_MenkowskiPoint); + ++proto_count; + + // Shift points in prototype to make room + for (u32 i = proto_count - 1; i > closest_b_index; --i) + { + u32 shift_from = (i > 0) ? i - 1 : proto_count - 1; + u32 shift_to = i; + proto[shift_to] = proto[shift_from]; + } + + // Insert new point into prototype + proto[closest_b_index] = m; + } + + // Debug draw + // { + // P_DebugDrawPoint(simplex.a.p, VEC4(1, 0, 0, 0.5)); + // P_DebugDrawPoint(simplex.b.p, VEC4(0, 1, 0, 0.5)); + // P_DebugDrawPoint(simplex.c.p, VEC4(0, 0, 1, 0.5)); + // P_DebugDrawLine(simplex.a.p, simplex.b.p, Color_Yellow); + // P_DebugDrawLine(simplex.b.p, simplex.c.p, Color_Yellow); + // P_DebugDrawLine(simplex.c.p, simplex.a.p, Color_Yellow); + // if (proto_count > 0) + // { + // for (i64 i = 0; i < proto_count; ++i) + // { + // i64 p1_idx = i + 1; + // if (p1_idx == proto_count) + // { + // p1_idx = 0; + // } + // Vec2 p0 = proto[i].p; + // Vec2 p1 = proto[p1_idx].p; + // P_DebugDrawLine(p0, p1, VEC4(0, 1, 0, 0.5)); + // } + // } + // } + } + else + { + normal = NormVec2(non_overlapping_dir); + closest_feature.count = simplex.count; + closest_feature.a = simplex.a; + closest_feature.b = simplex.b; + } + } + + ////////////////////////////// + //- Determine collision + + b32 is_colliding = 0; + { + if (is_overlapping) + { + is_colliding = 1; + } + else + { + // Shapes not overlapping, determine if distance between shapes within tolerance + if (closest_feature.count == 1) + { + Vec2 p = NegVec2(closest_feature.a.p); + if (Vec2LenSq(p) <= (tolerance * tolerance)) + { + is_colliding = 1; + } + } + else + { + // Project origin to determine if distance is within tolerance. + Assert(closest_feature.count == 2); + Vec2 vab = SubVec2(closest_feature.b.p, closest_feature.a.p); + Vec2 vao = NegVec2(closest_feature.a.p); + f32 ratio = ClampF32(DotVec2(vab, vao) / DotVec2(vab, vab), 0, 1); + Vec2 p = AddVec2(closest_feature.a.p, MulVec2(vab, ratio)); + if (Vec2LenSq(p) <= (tolerance * tolerance)) + { + is_colliding = 1; + } + } + } + } + + ////////////////////////////// + //- Compute collision points + + // Clip to determine final points + i32 collision_points_count = 0; + P_CollisionPoint collision_points[2] = Zi; + if (is_colliding) + { + // Max vertices must be < 16 to fit in 4 bit ids + StaticAssert(countof(shape0.points) <= 16); + { + b32 collapse0 = 0; + b32 collapse1 = 0; + + P_SupportPoint a0 = closest_feature.a.s0; + P_SupportPoint a1 = closest_feature.a.s1; + P_SupportPoint b0 = closest_feature.b.s0; + P_SupportPoint b1 = closest_feature.b.s1; + // FIXME: Manually account for shapes w/ 1 & 2 points + if (closest_feature.count == 2) + { + if (a0.id == b0.id) + { + if (shape0.points_count > 1) + { + b0 = P_SupportPointFromShapeEx(shape0, normal, b0.id); + } + else + { + collapse0 = 1; + b0 = a0; + } + } + if (a1.id == b1.id) + { + if (shape1.points_count > 1) + { + b1 = P_SupportPointFromShapeEx(shape1, NegVec2(normal), b1.id); + } + else + { + collapse1 = 1; + b1 = a1; + } + } + } + else + { + collapse0 = 1; + collapse1 = 1; + b0 = a0; + b1 = a1; + } + + Vec2 vab0 = SubVec2(b0.p, a0.p); + Vec2 vab1 = SubVec2(b1.p, a1.p); + Vec2 vab0_norm = NormVec2(vab0); + Vec2 vab1_norm = NormVec2(vab1); + + // Swap points based on normal direction for consistent clipping + if (WedgeVec2(normal, vab0) < 0) + { + P_SupportPoint tmp = a0; + a0 = b0; + b0 = tmp; + vab0 = NegVec2(vab0); + } + if (WedgeVec2(normal, vab1) < 0) + { + P_SupportPoint tmp = a1; + a1 = b1; + b1 = tmp; + vab1 = NegVec2(vab1); + } + + // Collapse lines that are too far in the direction of the normal to be accurately clipped + f32 collapse_epsilon = 0.05f; + collapse0 = collapse0 || AbsF32(WedgeVec2(normal, vab0_norm)) < collapse_epsilon; + collapse1 = collapse1 || AbsF32(WedgeVec2(normal, vab1_norm)) < collapse_epsilon; + + // Collapse lines into deepest point + if (collapse0) + { + if (DotVec2(normal, vab0) > 0) + { + a0 = b0; + } + else + { + // TODO: Remove this (debugging) + b0 = a0; + } + } + if (collapse1) + { + if (DotVec2(normal, vab1) < 0) + { + a1 = b1; + } + else + { + // TODO: Remove this (debugging) + b1 = a1; + } + } + + f32 a_sep = Inf; + f32 b_sep = Inf; + Vec2 a_midpoint = Zi; + Vec2 b_midpoint = Zi; + b32 ignore_a = 1; + b32 ignore_b = 1; + if (!collapse0 && !collapse1) + { + // Clip line to line + P_ClippedLine clip_result = P_ClipLineToLine(a0.p, b0.p, a1.p, b1.p, normal); + Vec2 a0_clipped = clip_result.a0_clipped; + Vec2 a1_clipped = clip_result.a1_clipped; + Vec2 b0_clipped = clip_result.b0_clipped; + Vec2 b1_clipped = clip_result.b1_clipped; + // Calc midpoint between clipped a & b + Vec2 va0a1_clipped = SubVec2(a1_clipped, a0_clipped); + Vec2 vb0b1_clipped = SubVec2(b1_clipped, b0_clipped); + a_sep = DotVec2(va0a1_clipped, normal); + b_sep = DotVec2(vb0b1_clipped, normal); + a_midpoint = AddVec2(a0_clipped, MulVec2(va0a1_clipped, 0.5f)); + b_midpoint = AddVec2(b0_clipped, MulVec2(vb0b1_clipped, 0.5f)); + ignore_a = 0; + ignore_b = 0; + Vec2 vfin = SubVec2(b_midpoint, a_midpoint); + if (Vec2LenSq(vfin) < (0.005 * 0.005)) + { + if (a_sep > b_sep) + { + ignore_a = 1; + } + else + { + ignore_b = 1; + } + } + } + else + { + Vec2 p0 = a0.p; + Vec2 p1 = a1.p; + // TODO: Choose ID based on closest clipped point + if (collapse1 && !collapse0) + { + // Project a1 onto vab0 + p0 = P_ClipPointToLine(a0.p, b0.p, a1.p, normal); + } + if (collapse0 && !collapse1) + { + // Project a0 onto vab1 + p1 = P_ClipPointToLine(a1.p, b1.p, a0.p, normal); + } + // Calc midpoint + Vec2 vsep = SubVec2(p1, p0); + a_midpoint = AddVec2(p0, MulVec2(vsep, 0.5f)); + a_sep = DotVec2(normal, p1) - DotVec2(normal, p0); + ignore_a = 0; + } + + // Insert points + if (!ignore_a && a_sep < tolerance) + { + P_CollisionPoint *point = &collision_points[collision_points_count++]; + point->id = a0.id | (a1.id << 4); + point->separation = a_sep; + point->p = a_midpoint; + } + if (!ignore_b && b_sep < tolerance) + { + P_CollisionPoint *point = &collision_points[collision_points_count++]; + point->id = b0.id | (b1.id << 4); + point->separation = b_sep; + point->p = b_midpoint; + } + } + } + + ////////////////////////////// + //- Compute closest points + + Vec2 closest_p0 = Zi; + Vec2 closest_p1 = Zi; + if (closest_feature.count == 1) + { + closest_p0 = closest_feature.a.s0.p; + closest_p1 = closest_feature.a.s1.p; + } + else + { + Assert(closest_feature.count == 2); + // FIXME: Winding order dependent? + f32 ratio = 0; + { + // Determine ratio between edge a & b that projected origin lies + Vec2 vab = SubVec2(closest_feature.b.p, closest_feature.a.p); + Vec2 vao = NegVec2(closest_feature.a.p); + ratio = ClampF32(DotVec2(vab, vao) / DotVec2(vab, vab), 0, 1); + } + // Shape 0 + closest_p0 = SubVec2(closest_feature.b.s0.p, closest_feature.a.s0.p); + closest_p0 = MulVec2(closest_p0, ratio); + closest_p0 = AddVec2(closest_p0, closest_feature.a.s0.p); + // Shape 1 + closest_p1 = SubVec2(closest_feature.b.s1.p, closest_feature.a.s1.p); + closest_p1 = MulVec2(closest_p1, ratio); + closest_p1 = AddVec2(closest_p1, closest_feature.a.s1.p); + } + + CopyStructs(result.collision_points, collision_points, countof(collision_points)); + result.collision_points_count = collision_points_count; + result.collision_normal = normal; + result.closest_p0 = closest_p0; + result.closest_p1 = closest_p1; + + EndScratch(scratch); + return result; +} + +P_RaycastResult P_RaycastShape(P_Shape shape, Vec2 ray_start, Vec2 ray_dir) +{ + f32 radius = shape.radius; + + f32 ray_len = Vec2Len(ray_dir); + Vec2 ray_dir_norm = DivVec2(ray_dir, ray_len); + + Vec2 s = ray_start; + Vec2 e = AddVec2(ray_start, ray_dir_norm); + Vec2 vse = ray_dir_norm; + + Vec2 isect = Zi; + Vec2 isect_normal = Zi; + b32 isect_is_round = 0; + b32 isect_found = 0; + if (shape.points_count > 1) + { + // Find expanded line intersections with ray + for (i32 p_idx = 0; p_idx < shape.points_count && !isect_found; ++p_idx) + { + Vec2 a = Zi; + Vec2 b = Zi; + Vec2 vab = Zi; + Vec2 normal = Zi; + { + i32 a_idx = p_idx; + i32 b_idx = a_idx + 1; + if (b_idx >= shape.points_count) + { + b_idx = 0; + } + + Vec2 a_orig = shape.points[a_idx]; + Vec2 b_orig = shape.points[b_idx]; + vab = SubVec2(b_orig, a_orig); + normal = NegVec2(PerpVec2(NormVec2(vab))); + Vec2 radius_add = MulVec2(normal, radius); + + a = AddVec2(a_orig, radius_add); + b = AddVec2(b_orig, radius_add); + } + Vec2 vsa = SubVec2(a, s); + Vec2 vsb = SubVec2(b, s); + f32 wa = WedgeVec2(vse, vsa); + f32 wb = WedgeVec2(vse, vsb); + if (wa > 0 && wb < 0) + { + f32 t = -wa / (wb - wa); + isect = AddVec2(a, MulVec2(vab, t)); + isect_normal = normal; + isect_found = 1; + } + } + + // Find closest rounded corner + if (!isect_found && radius != 0) + { + isect_is_round = 1; + for (i32 f_idx = 0; f_idx < shape.points_count && !isect_found; ++f_idx) + { + Vec2 f_orig = shape.points[f_idx]; + Vec2 a = Zi; + Vec2 b = Zi; + Vec2 vab = Zi; + { + i32 prev_idx = f_idx - 1; + i32 next_idx = f_idx + 1; + if (prev_idx < 0) + { + prev_idx = shape.points_count - 1; + } + if (next_idx >= shape.points_count) + { + next_idx = 0; + } + Vec2 prev_orig = shape.points[prev_idx]; + Vec2 next_orig = shape.points[next_idx]; + Vec2 vpf = SubVec2(f_orig, prev_orig); + Vec2 vfn = SubVec2(next_orig, f_orig); + Vec2 vpf_norm = NormVec2(vpf); + Vec2 vfn_norm = NormVec2(vfn); + Vec2 radius_add_a = MulVec2(PerpVec2(vpf_norm), -radius); + Vec2 radius_add_b = MulVec2(PerpVec2(vfn_norm), -radius); + a = AddVec2(f_orig, radius_add_a); + b = AddVec2(f_orig, radius_add_b); + } + Vec2 vsa = SubVec2(a, s); + Vec2 vsb = SubVec2(b, s); + f32 wa = WedgeVec2(vse, vsa); + f32 wb = WedgeVec2(vse, vsb); + if (wa > 0 && wb < 0) + { + isect = f_orig; + isect_found = 1; + } + } + } + + // Find closest corner + if (!isect_found) + { + f32 min_dist = Inf; + for (i32 p_idx = 0; p_idx < shape.points_count && !isect_found; ++p_idx) + { + Vec2 p = shape.points[p_idx]; + f32 dist = AbsF32(WedgeVec2(vse, SubVec2(p, s))); + if (dist < min_dist) + { + isect = p; + min_dist = dist; + } + } + } + } + else if (shape.points_count == 1 && radius != 0) + { + isect = shape.points[0]; + isect_is_round = 1; + } + + + // Find round intersection + b32 is_intersecting = 0; + if (isect_is_round || !isect_found) + { + Vec2 vsi = SubVec2(isect, s); + + f32 dot = DotVec2(vse, vsi); + f32 wedge = WedgeVec2(vse, vsi); + + is_intersecting = AbsF32(wedge) < radius; + if (is_intersecting) + { + f32 diff = SqrtF32(radius * radius - wedge * wedge); + f32 entrance_t = dot - diff; + f32 exit_t = dot + diff; + { + Vec2 old_isect = isect; + isect = AddVec2(s, MulVec2(vse, entrance_t)); + isect_normal = NormVec2(SubVec2(isect, old_isect)); + } + } + } + else + { + is_intersecting = isect_found; + } + + P_RaycastResult result = Zi; + result.is_intersecting = is_intersecting; + if (is_intersecting) + { + result.p = isect; + result.normal = isect_normal; + } + + return result; +} + +Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir) +{ + Vec2 result = shape.centroid; + P_RaycastResult raycast = P_RaycastShape(shape, shape.centroid, NegVec2(dir)); + if (raycast.is_intersecting) + { + result = raycast.p; + } + return result; +} + +//////////////////////////////////////////////////////////// +//~ Lookup helpers + +P_Ent *P_EntFromKey(P_World *world, P_Key key) +{ + P_Ent *result = &P_NilEnt; + if (!P_IsKeyNil(key) && world->ent_bins_count > 0) + { + P_EntBin *bin = &world->ent_bins[key.v % world->ent_bins_count]; + for (P_Ent *e = bin->first; e; e = e->next_in_bin) + { + if (e->key.v == key.v) + { + result = e; + break; + } + } + } + return result; +} + +//////////////////////////////////////////////////////////// +//~ Iteration helpers + +P_Ent *P_FirstEnt(P_World *world) +{ + P_Ent *result = &P_NilEnt; + if (world->first_ent) + { + result = world->first_ent; + } + return result; +} + +P_Ent *P_NextEnt(P_Ent *e) +{ + P_Ent *result = &P_NilEnt; + if (e && e->next) + { + result = e->next; + } + return result; +} + +P_Ent *P_PushTempEnt(Arena *arena, P_EntList *list) +{ + P_EntListNode *n = PushStruct(arena, P_EntListNode); + SllQueuePush(list->first, list->last, n); + ++list->count; + P_Ent *ent = &n->ent; + *ent = P_NilEnt; + ent->valid = 1; + ent->exists = 1; + return ent; +} + +void P_SpawnEntsFromList(Arena *arena, P_World *world, P_EntList ents) +{ + for (P_EntListNode *n = ents.first; n; n = n->next) + { + P_Ent *src = &n->ent; + P_Key key = src->key; + if (!P_IsKeyNil(src->key)) + { + P_EntBin *bin = &world->ent_bins[key.v % world->ent_bins_count]; + P_Ent *dst = bin->first; + for (; dst; dst = dst->next_in_bin) + { + if (P_MatchKey(dst->key, key)) + { + break; + } + } + if (!dst) + { + // FIXME: Use free list + dst = PushStructNoZero(arena, P_Ent); + DllQueuePush(world->first_ent, world->last_ent, dst); + DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin); + } + P_Ent *old_next = dst->next; + P_Ent *old_prev = dst->prev; + P_Ent *old_next_in_bin = dst->next_in_bin; + P_Ent *old_prev_in_bin = dst->prev_in_bin; + { + *dst = *src; + } + dst->next = old_next; + dst->prev = old_prev; + dst->next_in_bin = old_next_in_bin; + dst->prev_in_bin = old_prev_in_bin; + ++world->ents_count; + } + } +} + +//////////////////////////////////////////////////////////// +//~ Debug draw + +void P_DebugDrawPoint(Vec2 p, Vec4 srgb) +{ + if (P_tl.debug_draw_enabled) + { + P_DebugDrawNode *n = PushStruct(P_tl.debug_arena, P_DebugDrawNode); + { + n->kind = P_DebugDrawKind_Point; + n->srgb32 = U32FromVec4(srgb); + n->point.p = p; + } + SllQueuePush(P_tl.first_debug_draw_node, P_tl.last_debug_draw_node, n); + P_tl.debug_draw_nodes_count += 1; + } +} + +void P_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb) +{ + if (P_tl.debug_draw_enabled) + { + P_DebugDrawNode *n = PushStruct(P_tl.debug_arena, P_DebugDrawNode); + { + n->kind = P_DebugDrawKind_Line; + n->srgb32 = U32FromVec4(srgb); + n->line.p0 = p0; + n->line.p1 = p1; + } + SllQueuePush(P_tl.first_debug_draw_node, P_tl.last_debug_draw_node, n); + P_tl.debug_draw_nodes_count += 1; + } +} + +void P_DebugDrawRect(Rng2 rect, Vec4 srgb) +{ + if (P_tl.debug_draw_enabled) + { + P_DebugDrawNode *n = PushStruct(P_tl.debug_arena, P_DebugDrawNode); + { + n->kind = P_DebugDrawKind_Rect; + n->srgb32 = U32FromVec4(srgb); + n->rect = rect; + } + SllQueuePush(P_tl.first_debug_draw_node, P_tl.last_debug_draw_node, n); + P_tl.debug_draw_nodes_count += 1; + } +} + +void P_DebugDrawShape(P_Shape shape, Vec4 srgb) +{ + if (P_tl.debug_draw_enabled) + { + P_DebugDrawNode *n = PushStruct(P_tl.debug_arena, P_DebugDrawNode); + { + n->kind = P_DebugDrawKind_Shape; + n->srgb32 = U32FromVec4(srgb); + n->shape = shape; + } + SllQueuePush(P_tl.first_debug_draw_node, P_tl.last_debug_draw_node, n); + P_tl.debug_draw_nodes_count += 1; + } +} diff --git a/src/pp/pp.h b/src/pp/pp.h new file mode 100644 index 00000000..26e2d2ab --- /dev/null +++ b/src/pp/pp.h @@ -0,0 +1,454 @@ +//////////////////////////////////////////////////////////// +//~ Key types + +#define P_NilKey ((P_Key) { 0 }) + +Struct(P_Key) +{ + u64 v; +}; + +//////////////////////////////////////////////////////////// +//~ Shape types + +Struct(P_ShapeDesc) +{ + f32 radius; + f32 mass; + i32 count; + Vec2 points[8]; +}; + +Struct(P_Shape) +{ + f32 mass; + Vec2 centroid; + Vec2 center_of_mass; + + f32 radius; + i32 points_count; + Vec2 points[8]; +}; + +//////////////////////////////////////////////////////////// +//~ Ent types + +// TODO: Move boolean fields into bitwise property flags + +// TODO: Pack efficiently, deduplicate redundant fields + +Struct(P_Ent) +{ + ////////////////////////////// + //- Internal world state + + P_Ent *next; + P_Ent *prev; + + P_Ent *next_in_bin; + P_Ent *prev_in_bin; + + ////////////////////////////// + //- Persistent data + + P_Key key; + b32 valid; + + ////////////////////////////// + //- Build data + + f32 exists; + + b32 is_player; + f32 health; + + Xform last_xf; + Xform xf; + + Vec2 move; + Vec2 look; + f32 fire_held; + f32 fire_presses; + + // TODO: Remove this (weapon testing) + i64 last_fire_ns; + b32 has_weapon; + + P_Key bullet_firer; + b32 is_bullet; + Vec2 bullet_start; + Vec2 bullet_end; + + b32 has_hit; + Vec2 hit_entry; + Vec2 hit_entry_normal; + + ////////////////////////////// + //- Solver data + + Vec2 solved_v; + f32 solved_w; +}; + +Struct(P_EntListNode) +{ + P_EntListNode *next; + P_Ent ent; +}; + +Struct(P_EntList) +{ + P_EntListNode *first; + P_EntListNode *last; + i64 count; +}; + +Struct(P_EntBin) +{ + P_Ent *first; + P_Ent *last; +}; + +//////////////////////////////////////////////////////////// +//~ World types + +Struct(P_World) +{ + u64 seed; + i64 tick; + i64 time_ns; + + i64 ents_count; + P_Ent *first_ent; + P_Ent *last_ent; + + i64 ent_bins_count; + P_EntBin *ent_bins; + + u8 *tiles; +}; + +Enum(P_DeltaKind) +{ + P_DeltaKind_Reset, + P_DeltaKind_RawEnt, + P_DeltaKind_RawTiles, + P_DeltaKind_Tile, +}; + +Struct(P_Delta) +{ + P_DeltaKind kind; + + P_Ent ent; + + u8 *raw_tiles; + P_TileKind tile_kind; + Rng2I32 tile_range; +}; + +Struct(P_DeltaNode) +{ + P_DeltaNode *next; + P_Delta delta; +}; + +Struct(P_Snapshot) +{ + u64 seed; + i64 tick; + i64 time_ns; + + i64 deltas_count; + P_DeltaNode *first_delta_node; + P_DeltaNode *last_delta_node; +}; + +Struct(P_SnapshotNode) +{ + P_SnapshotNode *next; + P_Snapshot snapshot; +}; + +//////////////////////////////////////////////////////////// +//~ Collision types + +Struct(P_SupportPoint) +{ + Vec2 p; + u32 id; // Index of the originating piont in the shape +}; + +Struct(P_CollisionPoint) +{ + Vec2 p; + f32 separation; + u32 id; // Based on polygon edge-to-edge +}; + +Struct(P_MenkowskiPoint) +{ + Vec2 p; // Menkowski difference point + P_SupportPoint s0; // Support point of first shape in dir + P_SupportPoint s1; // Support point of second shape in -dir +}; + +Struct(P_MenkowskiSimplex) +{ + i32 count; + P_MenkowskiPoint a, b, c; +}; + +Struct(P_ClippedLine) +{ + Vec2 a0_clipped, b0_clipped; + Vec2 a1_clipped, b1_clipped; +}; + +Struct(P_CollisionResult) +{ + // Collision manifold + i32 collision_points_count; + P_CollisionPoint collision_points[2]; + Vec2 collision_normal; + + // Closest points + Vec2 closest_p0; + Vec2 closest_p1; +}; + +Struct(P_RaycastResult) +{ + b32 is_intersecting; + Vec2 p; + Vec2 normal; +}; + +//////////////////////////////////////////////////////////// +//~ Constraint types + +Struct(P_ContactPoint) +{ + Vec2 vcp0; + Vec2 vcp1; + f32 starting_separation; + f32 inv_normal_mass; + f32 inv_tangent_mass; + u32 id; + + f32 solved_normal_impulse; + f32 solved_tangent_impulse; +}; + +Struct(P_Constraint) +{ + i64 last_touched_tick; + P_Key ent0; + P_Key ent1; + + Vec2 static_center0; + Vec2 static_center1; + + f32 inv_m0; + f32 inv_m1; + f32 inv_i0; + f32 inv_i1; + + Vec2 normal; + f32 friction; + + i32 points_count; + P_ContactPoint points[2]; +}; + +//////////////////////////////////////////////////////////// +//~ Debug visualization types + +Enum(P_DebugDrawKind) +{ + P_DebugDrawKind_Point, + P_DebugDrawKind_Line, + P_DebugDrawKind_Rect, + P_DebugDrawKind_Shape, +}; + +Struct(P_DebugDrawNode) +{ + P_DebugDrawNode *next; + P_DebugDrawKind kind; + u32 srgb32; + union + { + struct + { + Vec2 p; + } point; + struct + { + Vec2 p0; + Vec2 p1; + } line; + Rng2 rect; + P_Shape shape; + }; +}; + +//////////////////////////////////////////////////////////// +//~ Command types + +Enum(P_CmdKind) +{ + P_CmdKind_Nop, + P_CmdKind_Save, + P_CmdKind_Delta, + P_CmdKind_Control, +}; + +Struct(P_Cmd) +{ + P_CmdKind kind; + + // Delta + P_Delta delta; + + // Control + P_Key target; + Vec2 move; + Vec2 look; + b32 fire_held; + i32 fire_presses; +}; + +Struct(P_CmdNode) +{ + P_CmdNode *next; + P_Cmd cmd; +}; + +Struct(P_InputState) +{ + Arena *arena; + P_CmdNode *first_cmd_node; + P_CmdNode *last_cmd_node; + u64 cmds_count; +}; + +Struct(P_OutputState) +{ + Arena *arena; + P_SnapshotNode *first_snapshot_node; + P_SnapshotNode *last_snapshot_node; + u64 snapshots_count; + + P_DebugDrawNode *first_debug_draw_node; + P_DebugDrawNode *last_debug_draw_node; + i64 debug_draw_nodes_count; +}; + +//////////////////////////////////////////////////////////// +//~ State types + +Struct(P_Ctx) +{ + //- Sim input + TicketMutex sim_input_back_tm; + i32 sim_input_back_idx; + P_InputState sim_input_states[2]; + + //- Sim output + TicketMutex sim_output_back_tm; + i32 sim_output_back_idx; + P_OutputState sim_output_states[2]; +}; + +Struct(P_ThreadLocalCtx) +{ + Arena *debug_arena; + b32 debug_draw_enabled; + P_DebugDrawNode *first_debug_draw_node; + P_DebugDrawNode *last_debug_draw_node; + i64 debug_draw_nodes_count; +}; + +extern P_Ctx P; +extern ThreadLocal P_ThreadLocalCtx P_tl; + +//////////////////////////////////////////////////////////// +//~ Bootstrap + +void P_Bootstrap(void); + +//////////////////////////////////////////////////////////// +//~ Nil helpers + +b32 P_IsKeyNil(P_Key key); +b32 P_IsEntNil(P_Ent *ent); +b32 P_MatchKey(P_Key a, P_Key b); + +//////////////////////////////////////////////////////////// +//~ Key helpers + +P_Key P_RandKey(void); + +#define P_FmtKey(key) FmtHandle((key).v) + +//////////////////////////////////////////////////////////// +//~ Tile helpers + +String P_NameFromTileKind(P_TileKind kind); + +//////////////////////////////////////////////////////////// +//~ Delta helpers + +void P_UpdateWorldFromDelta(Arena *arena, P_World *world, P_Delta *delta); + +//////////////////////////////////////////////////////////// +//~ Shape helpers + +P_Shape P_ShapeFromDescEx(P_ShapeDesc desc); +#define P_ShapeFromDesc(...) P_ShapeFromDescEx((P_ShapeDesc) { __VA_ARGS__ }) + +P_Shape P_MulXformShape(Xform xf, P_Shape shape); +Rng2 P_BoundingBoxFromShape(P_Shape shape); + +P_Shape P_LocalShapeFromEnt(P_Ent *ent); +P_Shape P_WorldShapeFromEnt(P_Ent *ent); + +//////////////////////////////////////////////////////////// +//~ Collision + +P_SupportPoint P_SupportPointFromShapeEx(P_Shape shape, Vec2 dir, i32 ignore_idx); +P_SupportPoint P_SupportPointFromShape(P_Shape shape, Vec2 dir); +P_MenkowskiPoint P_MenkowskiPointFromShapes(P_Shape shape0, P_Shape shape1, Vec2 dir); +P_ClippedLine P_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal); +Vec2 P_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal); + +P_CollisionResult P_CollisionResultFromShapes(P_Shape shape0, P_Shape shape1); +P_RaycastResult P_RaycastShape(P_Shape shape, Vec2 ray_start, Vec2 ray_dir); + +Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir); + +//////////////////////////////////////////////////////////// +//~ Lookup helpers + +P_Ent *P_EntFromKey(P_World *world, P_Key key); + +//////////////////////////////////////////////////////////// +//~ Iteration helpers + +P_Ent *P_FirstEnt(P_World *world); +P_Ent *P_NextEnt(P_Ent *e); + +//////////////////////////////////////////////////////////// +//~ List helpers + +P_Ent *P_PushTempEnt(Arena *arena, P_EntList *list); +void P_SpawnEntsFromList(Arena *arena, P_World *world, P_EntList ents); + +//////////////////////////////////////////////////////////// +//~ Debug draw + +void P_DebugDrawPoint(Vec2 p, Vec4 srgb); +void P_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb); +void P_DebugDrawRect(Rng2 rect, Vec4 srgb); +void P_DebugDrawShape(P_Shape shape, Vec4 srgb); diff --git a/src/pp/pp.lay b/src/pp/pp.lay index e7c3751b..4d5f38fb 100644 --- a/src/pp/pp.lay +++ b/src/pp/pp.lay @@ -5,8 +5,23 @@ @EmbedDir P_Resources pp_res +////////////////////////////// +//- Api + +@IncludeC pp_shared.cgh +@IncludeG pp_shared.cgh +@IncludeC pp.h +@IncludeC pp_transcode.h + +@Bootstrap P_Bootstrap + ////////////////////////////// //- Impl -@DefaultDownstream Any pp_vis +@IncludeC pp_shared.cg +@IncludeG pp_shared.cg +@IncludeC pp.c +@IncludeC pp_transcode.c + @DefaultDownstream Any pp_sim +@DefaultDownstream Any pp_vis diff --git a/src/pp/pp_shared.cg b/src/pp/pp_shared.cg new file mode 100644 index 00000000..14471016 --- /dev/null +++ b/src/pp/pp_shared.cg @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////// +//~ Tile helpers + +i32 P_TileIdxFromTilePos(Vec2 p) +{ + i32 x = ClampI32(FloorF32(p.x), 0, P_TilesPitch - 1); + i32 y = ClampI32(FloorF32(p.y), 0, P_TilesPitch - 1); + i32 result = x + (y * P_TilesPitch); + return result; +} + +#if IsLanguageC + String P_TileNameFromKind(P_TileKind kind) + { + PERSIST Readonly String tile_names[P_TileKind_COUNT] = { + #define X(name, ...) [P_TileKind_##name] = CompLit(#name), + P_TilesXMacro(X) + #undef X + }; + String result = Zi; + if (kind >= 0 && kind < countof(tile_names)) + { + result = tile_names[kind]; + } + return result; + } +#endif diff --git a/src/pp/pp_shared.cgh b/src/pp/pp_shared.cgh new file mode 100644 index 00000000..47174c46 --- /dev/null +++ b/src/pp/pp_shared.cgh @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////// +//~ Tile types + +#define P_WorldPitch 64.0 + +#define P_TilesPerMeter 2.0 +#define P_TilesPerSqMeter (V_TilesPerMeter * V_TilesPerMeter) + +#define P_TilesPitch (P_WorldPitch * P_TilesPerMeter) +#define P_TilesCount (P_TilesPitch * P_TilesPitch) + +#define P_TilesXMacro(X) \ + X(Empty) \ + X(Tile) \ + X(Carpet) \ + X(Wall) \ +/* -------------------- */ + +//- Tiles kinds enum +Enum(P_TileKind) +{ + #define X(name, ...) P_TileKind_##name, + P_TilesXMacro(X) + #undef X + P_TileKind_COUNT +}; + +//////////////////////////////////////////////////////////// +//~ Tile helpers + +i32 P_TileIdxFromTilePos(Vec2 p); + +#if IsLanguageC + String P_TileNameFromKind(P_TileKind kind); +#endif diff --git a/src/pp/pp_sim/pp_sim.lay b/src/pp/pp_sim/pp_sim.lay index dc9fcb65..1b791089 100644 --- a/src/pp/pp_sim/pp_sim.lay +++ b/src/pp/pp_sim/pp_sim.lay @@ -9,11 +9,7 @@ ////////////////////////////// //- Api -@IncludeC pp_sim_shared.cgh -@IncludeG pp_sim_shared.cgh - @IncludeC pp_sim_core.h -@IncludeC pp_sim_transcode.h @Bootstrap S_Bootstrap @@ -21,6 +17,3 @@ //- Impl @IncludeC pp_sim_core.c -@IncludeC pp_sim_transcode.c -@IncludeC pp_sim_shared.cg -@IncludeG pp_sim_shared.cg diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index 6869e82a..26f40f58 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -1,29 +1,10 @@ S_Ctx S = Zi; -ThreadLocal S_ThreadLocalCtx S_tl = Zi; - -Readonly S_Ent S_NilEnt = { - .xf = CompXformIdentity, - .look = { 0, -1 }, -}; //////////////////////////////////////////////////////////// //~ Bootstrap void S_Bootstrap(void) { - // Initialize shared state - for (u64 i = 0; i < countof(S.input_states); ++i) - { - S_InputState *input = &S.input_states[i]; - input->arena = AcquireArena(Gibi(64)); - } - for (u64 i = 0; i < countof(S.output_states); ++i) - { - S_OutputState *output = &S.output_states[i]; - output->arena = AcquireArena(Gibi(64)); - } - - // Start simulating DispatchWave(Lit("Sim"), 1, S_TickForever, 0); OnExit(S_Shutdown); } @@ -34,1233 +15,16 @@ void S_Shutdown(void) YieldOnFence(&S.shutdown_fence, 1); } -//////////////////////////////////////////////////////////// -//~ Nil helpers - -b32 S_IsKeyNil(S_Key key) -{ - return key.v == 0; -} - -b32 S_IsEntNil(S_Ent *ent) -{ - return ent == 0 || ent == &S_NilEnt; -} - -b32 S_MatchKey(S_Key a, S_Key b) -{ - return a.v == b.v; -} - -//////////////////////////////////////////////////////////// -//~ Key helpers - -S_Key S_RandKey(void) -{ - // TODO: Don't use true randomness for entity keys. It's overkill & non-deterministic. - S_Key result = Zi; - TrueRand(StringFromStruct(&result)); - return result; -} - -//////////////////////////////////////////////////////////// -//~ Tile helpers - -String S_NameFromTileKind(S_TileKind kind) -{ - // Tile names array -#define X(name, ...) [S_TileKind_##name] = CompLit(#name), - PERSIST Readonly String tile_names[] = { - S_TilesXMacro(X) - }; -#undef X - - String result = Lit("Unknown"); - if (kind >= 0 && kind < countof(tile_names)) - { - result = tile_names[kind]; - } - return result; -} - -//////////////////////////////////////////////////////////// -//~ Delta helpers - -void S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta) -{ - // FIXME: Bounds check tile deltas - if (0) - { - } - //- Reset - if (delta->kind == S_DeltaKind_Reset) - { - // FIXME: Free list entities - world->ents_count = 0; - world->first_ent = 0; - world->last_ent = 0; - ZeroStructs(world->tiles, S_TilesCount); - ZeroStructs(world->ent_bins, world->ent_bins_count); - } - //- Raw ent - if (delta->kind == S_DeltaKind_RawEnt) - { - S_Key key = delta->ent.key; - S_Ent *ent = S_EntFromKey(world, key); - if (!ent->valid) - { - // FIXME: Use ent free list - ent = PushStructNoZero(arena, S_Ent); - *ent = S_NilEnt; - ent->key = key; - S_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count]; - DllQueuePushNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); - DllQueuePush(world->first_ent, world->last_ent, ent); - ++world->ents_count; - } - S_Ent *old_next = ent->next; - S_Ent *old_prev = ent->prev; - S_Ent *old_next_in_bin = ent->next_in_bin; - S_Ent *old_prev_in_bin = ent->prev_in_bin; - { - S_Ent *src = &delta->ent; - *ent = *src; - } - ent->next = old_next; - ent->prev = old_prev; - ent->next_in_bin = old_next_in_bin; - ent->prev_in_bin = old_prev_in_bin; - ent->valid = 1; - } - //- Raw tiles - else if (delta->kind == S_DeltaKind_RawTiles) - { - Rng2I32 range = delta->tile_range; - for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y) - { - i32 src_tile_y = tile_y - range.p0.y; - for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x) - { - i32 src_tile_x = tile_x - range.p0.x; - Vec2 src_tile_pos = VEC2(src_tile_x, src_tile_y); - i32 src_tile_idx = S_TileIdxFromTilePos(src_tile_pos); - u8 src_tile = delta->raw_tiles[src_tile_idx]; - - Vec2 tile_pos = VEC2(tile_x, tile_y); - i32 tile_idx = S_TileIdxFromTilePos(tile_pos); - world->tiles[tile_idx] = src_tile; - } - } - } - //- Tile range - else if (delta->kind == S_DeltaKind_Tile) - { - S_TileKind tile = delta->tile_kind; - Rng2I32 range = delta->tile_range; - for (i32 tile_y = range.p0.y; tile_y < range.p1.y; ++tile_y) - { - for (i32 tile_x = range.p0.x; tile_x < range.p1.x; ++tile_x) - { - Vec2 tile_pos = VEC2(tile_x, tile_y); - i32 tile_idx = S_TileIdxFromTilePos(tile_pos); - world->tiles[tile_idx] = (u8)tile; - } - } - } -} - -//////////////////////////////////////////////////////////// -//~ Shape helpers - -S_Shape S_ShapeFromDescEx(S_ShapeDesc desc) -{ - desc.count = MaxI32(desc.count, 1); - S_Shape result = Zi; - { - result.points_count = desc.count; - CopyStructs(result.points, desc.points, result.points_count); - Vec2 accum = Zi; - for (i32 p_idx = 0; p_idx < result.points_count; ++p_idx) - { - accum = AddVec2(accum, result.points[p_idx]); - } - result.centroid = DivVec2(accum, result.points_count); - result.center_of_mass = result.centroid; - result.radius = desc.radius; - result.mass = desc.mass; - } - return result; -} - -S_Shape S_MulXformShape(Xform xf, S_Shape shape) -{ - S_Shape result = shape; - for (i32 i = 0; i < shape.points_count; ++i) - { - result.points[i] = MulXformV2(xf, shape.points[i]); - } - 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; -} - -Rng2 S_BoundingBoxFromShape(S_Shape shape) -{ - Vec2 left = S_SupportPointFromShape(shape, VEC2(-1, 0)).p; - Vec2 top = S_SupportPointFromShape(shape, VEC2(0, -1)).p; - Vec2 right = S_SupportPointFromShape(shape, VEC2(1, 0)).p; - Vec2 bottom = S_SupportPointFromShape(shape, VEC2(0, 1)).p; - - Rng2 result = Zi; - result.p0 = VEC2(left.x, top.y); - result.p1 = VEC2(right.x, bottom.y); - return result; -} - -S_Shape S_LocalShapeFromEnt(S_Ent *ent) -{ - S_Shape result = Zi; - - // TODO: This is a temporary hack. We should eventually switch to using a prefab lookup table. - if (ent->is_player) - { - result = S_ShapeFromDesc( - .mass = 10, - .count = 1, - .radius = 0.3, - ); - - // Rng2 test_rect = Zi; - // test_rect.p0 = VEC2(-1, -1); - // test_rect.p1 = VEC2(1, 1); - // result = S_ShapeFromDesc( - // // .radius = 0.5, - // .radius = 0, - // .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), - // ); - } - - return result; -} - -S_Shape S_WorldShapeFromEnt(S_Ent *ent) -{ - S_Shape local = S_LocalShapeFromEnt(ent); - S_Shape world = S_MulXformShape(ent->xf, local); - return world; -} - -//////////////////////////////////////////////////////////// -//~ Collision - -// NOTE: Everything here is pretty much copied directly from the old prototype. -// The techniques are slow and do more than what we need. For example we should -// probably just switch from GJK to SAT for shape collision testing. - -S_SupportPoint S_SupportPointFromShapeEx(S_Shape shape, Vec2 dir, i32 ignore_idx) -{ - S_SupportPoint result = Zi; - Vec2 dir_norm = NormVec2(dir); - f32 max_dot = -Inf; - if (shape.points_count == 1) - { - // Don't ignore for single-point colliders - ignore_idx = -1; - } - for (i32 point_idx = 0; point_idx < shape.points_count; ++point_idx) - { - if (point_idx != ignore_idx) - { - Vec2 p = shape.points[point_idx]; - f32 dot = DotVec2(p, dir_norm); - if (dot > max_dot) - { - max_dot = dot; - result.p = p; - result.id = point_idx; - } - } - } - result.p = AddVec2(result.p, MulVec2(dir_norm, shape.radius)); - return result; -} - -S_SupportPoint S_SupportPointFromShape(S_Shape shape, Vec2 dir) -{ - return S_SupportPointFromShapeEx(shape, dir, -1); -} - -S_MenkowskiPoint S_MenkowskiPointFromShapes(S_Shape shape0, S_Shape shape1, Vec2 dir) -{ - S_MenkowskiPoint result = Zi; - result.s0 = S_SupportPointFromShape(shape0, dir); - result.s1 = S_SupportPointFromShape(shape1, NegVec2(dir)); - result.p = SubVec2(result.s0.p, result.s1.p); - return result; -} - -S_ClippedLine S_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal) -{ - Vec2 vab0 = SubVec2(b0, a0); - Vec2 vab1 = SubVec2(b1, a1); - Vec2 va0a1 = SubVec2(a1, a0); - Vec2 vb0b1 = SubVec2(b1, b0); - f32 vab0_w = WedgeVec2(vab0, normal); - f32 vab1_w = WedgeVec2(vab1, normal); - f32 va0a1_w = WedgeVec2(va0a1, normal); - f32 vb0b1_w = WedgeVec2(vb0b1, normal); - - // FIXME: Handle 0 denominator - f32 a0t; - f32 b0t; - { - f32 w = 1 / vab0_w; - a0t = ClampF32(va0a1_w * w, 0, 1); - b0t = ClampF32(vb0b1_w * -w, 0, 1); - } - f32 a1t; - f32 b1t; - { - f32 w = 1 / vab1_w; - a1t = ClampF32(-va0a1_w * w, 0, 1); - b1t = ClampF32(-vb0b1_w * -w, 0, 1); - } - - S_ClippedLine result = Zi; - result.a0_clipped = AddVec2(a0, MulVec2(vab0, a0t)); - result.a1_clipped = AddVec2(a1, MulVec2(vab1, a1t)); - result.b0_clipped = AddVec2(b0, MulVec2(vab0, -b0t)); - result.b1_clipped = AddVec2(b1, MulVec2(vab1, -b1t)); - return result; -} - -Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal) -{ - Vec2 vab = SubVec2(b, a); - Vec2 vap = SubVec2(p, a); - - f32 vab_w = WedgeVec2(vab, normal); - f32 vap_w = WedgeVec2(vap, normal); - - f32 w = 1 / vab_w; - f32 t = ClampF32(vap_w * w, 0, 1); - - Vec2 result = AddVec2(a, MulVec2(vab, t)); - return result; -} - -S_CollisionResult S_CollisionResultFromShapes(S_Shape shape0, S_Shape shape1) -{ - S_CollisionResult result = Zi; - TempArena scratch = BeginScratchNoConflict(); - - f32 tolerance = 0.00005f; // How close can non-overlapping shapes be before collision is considered - f32 min_unique_pt_dist_sq = (0.001f * 0.001f); // NOTE: Should always be less than tolerance, since colliding = 1 if origin is within this distance. - u32 max_iterations = 64; // To prevent extremely large prototypes when origin is in exact center of rounded feature - - ////////////////////////////// - //- GJK - - S_MenkowskiSimplex simplex = Zi; - Vec2 non_overlapping_dir = Zi; - b32 is_overlapping = 0; - { - S_MenkowskiPoint m = Zi; - - // First point is support point in shape's general directions to eachother - Vec2 dir = SubVec2(shape1.centroid, shape0.centroid); - if (IsVec2Zero(dir)) dir = VEC2(1, 0); - simplex.a = S_MenkowskiPointFromShapes(shape0, shape1, dir); - simplex.count = 1; - - Vec2 removed_a = Zi; - Vec2 removed_b = Zi; - u32 num_removed = 0; - for (;;) - { - ////////////////////////////// - //- Find initial points in simplex - - if (simplex.count == 1) - { - // Second point is support point towards origin - dir = NegVec2(simplex.a.p); - - m = S_MenkowskiPointFromShapes(shape0, shape1, dir); - // Check that new point is far enough away from existing point - if (Vec2LenSq(SubVec2(m.p, simplex.a.p)) < min_unique_pt_dist_sq) - { - is_overlapping = 0; - break; - } - simplex.b = simplex.a; - simplex.a = m; - simplex.count = 2; - - // Third point is support point in direction of line normal towards origin - dir = PerpVec2TowardsDir(SubVec2(simplex.b.p, simplex.a.p), NegVec2(simplex.a.p)); - } - - ////////////////////////////// - //- Find third point in simplex - - { - m = S_MenkowskiPointFromShapes(shape0, shape1, dir); - // Check that new point is far enough away from existing points - if ( - Vec2LenSq(SubVec2(m.p, simplex.a.p)) < min_unique_pt_dist_sq || - Vec2LenSq(SubVec2(m.p, simplex.b.p)) < min_unique_pt_dist_sq || ( - (num_removed >= 1) && ( - (Vec2LenSq(SubVec2(m.p, removed_a)) < min_unique_pt_dist_sq) || - (num_removed >= 2 && Vec2LenSq(SubVec2(m.p, removed_b)) < min_unique_pt_dist_sq) - ) - ) || - AbsF32(WedgeVec2(SubVec2(simplex.b.p, simplex.a.p), SubVec2(m.p, simplex.a.p))) < min_unique_pt_dist_sq - ) - { - is_overlapping = 0; - break; - } - simplex.c = simplex.b; - simplex.b = simplex.a; - simplex.a = m; - simplex.count = 3; - - if ( - (AbsF32(WedgeVec2(SubVec2(simplex.b.p, simplex.a.p), NegVec2(simplex.a.p))) <= min_unique_pt_dist_sq) || - (AbsF32(WedgeVec2(SubVec2(simplex.c.p, simplex.b.p), NegVec2(simplex.b.p))) <= min_unique_pt_dist_sq) || - (AbsF32(WedgeVec2(SubVec2(simplex.c.p, simplex.a.p), NegVec2(simplex.a.p))) <= min_unique_pt_dist_sq) - ) - { - // Simplex lies on origin - is_overlapping = 1; - break; - } - } - - ////////////////////////////// - //- Determine origin region - - Vec2 vab = SubVec2(simplex.b.p, simplex.a.p); - Vec2 vac = SubVec2(simplex.c.p, simplex.a.p); - Vec2 vbc = SubVec2(simplex.c.p, simplex.b.p); - - Vec2 rab_dir = PerpVec2TowardsDir(vab, NegVec2(vac)); - Vec2 rac_dir = PerpVec2TowardsDir(vac, NegVec2(vab)); - Vec2 rbc_dir = PerpVec2TowardsDir(vbc, vab); - - f32 rab_dot = DotVec2(rab_dir, NegVec2(simplex.a.p)); - f32 rac_dot = DotVec2(rac_dir, NegVec2(simplex.a.p)); - f32 rbc_dot = DotVec2(rbc_dir, NegVec2(simplex.b.p)); - - f32 vab_dot = DotVec2(vab, NegVec2(simplex.a.p)) / Vec2LenSq(vab); - f32 vac_dot = DotVec2(vac, NegVec2(simplex.a.p)) / Vec2LenSq(vac); - f32 vbc_dot = DotVec2(vbc, NegVec2(simplex.b.p)) / Vec2LenSq(vbc); - - if (rab_dot >= 0 && vab_dot >= 0 && vab_dot <= 1) - { - // Region ab, remove c - num_removed = 1; - removed_a = simplex.c.p; - simplex.count = 2; - dir = rab_dir; // Next third point is in direction of region ab - } - else if (rac_dot >= 0 && vac_dot >= 0 && vac_dot <= 1) - { - // Region ac, remove b - num_removed = 1; - removed_a = simplex.b.p; - simplex.count = 2; - simplex.b = simplex.c; - dir = rac_dir; // Next third point is in direction of region ac - } - else if (rbc_dot >= 0 && vbc_dot >= 0 && vbc_dot <= 1) - { - // Region bc, remove a - num_removed = 1; - removed_a = simplex.a.p; - simplex.count = 2; - simplex.a = simplex.b; - simplex.b = simplex.c; - dir = rbc_dir; // Next third point is in direction of region bc - } - else if (vab_dot <= 0 && vac_dot <= 0) - { - // Region a, remove bc - num_removed = 2; - removed_a = simplex.b.p; - removed_b = simplex.c.p; - simplex.count = 1; - } - else if (vab_dot >= 1 && vbc_dot <= 0) - { - // Region b, remove ac - num_removed = 2; - removed_a = simplex.a.p; - removed_b = simplex.c.p; - simplex.count = 1; - simplex.a = simplex.b; - } - else if (vac_dot >= 1 && vbc_dot >= 1) - { - // Region c, remove ab - num_removed = 2; - removed_a = simplex.a.p; - removed_b = simplex.b.p; - simplex.count = 1; - simplex.a = simplex.c; - } - else - { - // No region, must be in simplex - is_overlapping = 1; - break; - } - } - if (!is_overlapping) - { - non_overlapping_dir = dir; - } - } - - ////////////////////////////// - //- EPA - - // Find dir from origin to closest edge - Vec2 normal = Zi; - S_MenkowskiSimplex closest_feature = Zi; - { - S_MenkowskiPoint *proto = 0; - if (is_overlapping) - { - u32 proto_count = 0; - proto = ArenaNext(scratch.arena, S_MenkowskiPoint); - { - Assert(simplex.count == 3); - S_MenkowskiPoint *tmp = PushStructsNoZero(scratch.arena, S_MenkowskiPoint, 3); - tmp[0] = simplex.a; - tmp[1] = simplex.b; - tmp[2] = simplex.c; - proto_count = 3; - } - - i32 winding = WindingFromVec2(SubVec2(simplex.c.p, simplex.a.p), SubVec2(simplex.b.p, simplex.a.p)); - - u32 epa_iterations = 0; - for (;;) - { - ++epa_iterations; - - // FIXME: Winding order of ps & pe index - S_MenkowskiPoint closest_a = Zi; - S_MenkowskiPoint closest_b = Zi; - u32 closest_b_index = 0; - { - // Find edge segment on prototype closest to the origin - f32 closest_len_sq = Inf; - for (u32 i = 0; i < proto_count; ++i) - { - u32 a_index = i; - u32 b_index = (i < proto_count - 1) ? (i + 1) : 0; - S_MenkowskiPoint a = proto[a_index]; - S_MenkowskiPoint b = proto[b_index]; - Vec2 vab = SubVec2(b.p, a.p); - Vec2 vao = NegVec2(a.p); - f32 proj_ratio = ClampF32(DotVec2(vao, vab) / Vec2LenSq(vab), 0, 1); - Vec2 proj = AddVec2(a.p, MulVec2(vab, proj_ratio)); - f32 proj_len_sq = Vec2LenSq(proj); - if (proj_len_sq < closest_len_sq - min_unique_pt_dist_sq) - { - closest_a = a; - closest_b = b; - closest_b_index = b_index; - closest_len_sq = proj_len_sq; - } - } - } - Vec2 vab = SubVec2(closest_b.p, closest_a.p); - - // Find new point in dir - Vec2 dir = MulVec2(PerpVec2(vab), winding); - S_MenkowskiPoint m = S_MenkowskiPointFromShapes(shape0, shape1, dir); - - // Check validity of new point - { - b32 valid = 1; - { - // NOTE: Changing this value affects how stable normals are for rounded colliders - //const f32 validity_epsilon = min_unique_pt_dist_sq; // Arbitrary - //const f32 validity_epsilon = 0.00000000001f; // Arbitrary - const f32 validity_epsilon = min_unique_pt_dist_sq; // Arbitrary - - Vec2 vam = SubVec2(m.p, closest_a.p); - Vec2 vbm = SubVec2(closest_b.p, closest_a.p); - - f32 dot = DotVec2(vab, vam) / Vec2LenSq(vab); - - if (dot >= -validity_epsilon && dot <= 1 - validity_epsilon && (WedgeVec2(vab, vam) * -winding) >= -validity_epsilon) - { - // New point is not between edge - valid = 0; - } - else if (Vec2LenSq(vam) < min_unique_pt_dist_sq || Vec2LenSq(vbm) < min_unique_pt_dist_sq) - { - // New point is too close to existing - valid = 0; - } - } - - if (!valid || epa_iterations >= max_iterations) - { - normal = NormVec2(dir); - closest_feature.a = closest_a; - closest_feature.b = closest_b; - closest_feature.count = 2; - break; - } - } - - // Expand prototype - PushStructNoZero(scratch.arena, S_MenkowskiPoint); - ++proto_count; - - // Shift points in prototype to make room - for (u32 i = proto_count - 1; i > closest_b_index; --i) - { - u32 shift_from = (i > 0) ? i - 1 : proto_count - 1; - u32 shift_to = i; - proto[shift_to] = proto[shift_from]; - } - - // Insert new point into prototype - proto[closest_b_index] = m; - } - - // Debug draw - // { - // S_DebugDrawPoint(simplex.a.p, VEC4(1, 0, 0, 0.5)); - // S_DebugDrawPoint(simplex.b.p, VEC4(0, 1, 0, 0.5)); - // S_DebugDrawPoint(simplex.c.p, VEC4(0, 0, 1, 0.5)); - // S_DebugDrawLine(simplex.a.p, simplex.b.p, Color_Yellow); - // S_DebugDrawLine(simplex.b.p, simplex.c.p, Color_Yellow); - // S_DebugDrawLine(simplex.c.p, simplex.a.p, Color_Yellow); - // if (proto_count > 0) - // { - // for (i64 i = 0; i < proto_count; ++i) - // { - // i64 p1_idx = i + 1; - // if (p1_idx == proto_count) - // { - // p1_idx = 0; - // } - // Vec2 p0 = proto[i].p; - // Vec2 p1 = proto[p1_idx].p; - // S_DebugDrawLine(p0, p1, VEC4(0, 1, 0, 0.5)); - // } - // } - // } - } - else - { - normal = NormVec2(non_overlapping_dir); - closest_feature.count = simplex.count; - closest_feature.a = simplex.a; - closest_feature.b = simplex.b; - } - } - - ////////////////////////////// - //- Determine collision - - b32 is_colliding = 0; - { - if (is_overlapping) - { - is_colliding = 1; - } - else - { - // Shapes not overlapping, determine if distance between shapes within tolerance - if (closest_feature.count == 1) - { - Vec2 p = NegVec2(closest_feature.a.p); - if (Vec2LenSq(p) <= (tolerance * tolerance)) - { - is_colliding = 1; - } - } - else - { - // Project origin to determine if distance is within tolerance. - Assert(closest_feature.count == 2); - Vec2 vab = SubVec2(closest_feature.b.p, closest_feature.a.p); - Vec2 vao = NegVec2(closest_feature.a.p); - f32 ratio = ClampF32(DotVec2(vab, vao) / DotVec2(vab, vab), 0, 1); - Vec2 p = AddVec2(closest_feature.a.p, MulVec2(vab, ratio)); - if (Vec2LenSq(p) <= (tolerance * tolerance)) - { - is_colliding = 1; - } - } - } - } - - ////////////////////////////// - //- Compute collision points - - // Clip to determine final points - i32 collision_points_count = 0; - S_CollisionPoint collision_points[2] = Zi; - if (is_colliding) - { - // Max vertices must be < 16 to fit in 4 bit ids - StaticAssert(countof(shape0.points) <= 16); - { - b32 collapse0 = 0; - b32 collapse1 = 0; - - S_SupportPoint a0 = closest_feature.a.s0; - S_SupportPoint a1 = closest_feature.a.s1; - S_SupportPoint b0 = closest_feature.b.s0; - S_SupportPoint b1 = closest_feature.b.s1; - // FIXME: Manually account for shapes w/ 1 & 2 points - if (closest_feature.count == 2) - { - if (a0.id == b0.id) - { - if (shape0.points_count > 1) - { - b0 = S_SupportPointFromShapeEx(shape0, normal, b0.id); - } - else - { - collapse0 = 1; - b0 = a0; - } - } - if (a1.id == b1.id) - { - if (shape1.points_count > 1) - { - b1 = S_SupportPointFromShapeEx(shape1, NegVec2(normal), b1.id); - } - else - { - collapse1 = 1; - b1 = a1; - } - } - } - else - { - collapse0 = 1; - collapse1 = 1; - b0 = a0; - b1 = a1; - } - - Vec2 vab0 = SubVec2(b0.p, a0.p); - Vec2 vab1 = SubVec2(b1.p, a1.p); - Vec2 vab0_norm = NormVec2(vab0); - Vec2 vab1_norm = NormVec2(vab1); - - // Swap points based on normal direction for consistent clipping - if (WedgeVec2(normal, vab0) < 0) - { - S_SupportPoint tmp = a0; - a0 = b0; - b0 = tmp; - vab0 = NegVec2(vab0); - } - if (WedgeVec2(normal, vab1) < 0) - { - S_SupportPoint tmp = a1; - a1 = b1; - b1 = tmp; - vab1 = NegVec2(vab1); - } - - // Collapse lines that are too far in the direction of the normal to be accurately clipped - f32 collapse_epsilon = 0.05f; - collapse0 = collapse0 || AbsF32(WedgeVec2(normal, vab0_norm)) < collapse_epsilon; - collapse1 = collapse1 || AbsF32(WedgeVec2(normal, vab1_norm)) < collapse_epsilon; - - // Collapse lines into deepest point - if (collapse0) - { - if (DotVec2(normal, vab0) > 0) - { - a0 = b0; - } - else - { - // TODO: Remove this (debugging) - b0 = a0; - } - } - if (collapse1) - { - if (DotVec2(normal, vab1) < 0) - { - a1 = b1; - } - else - { - // TODO: Remove this (debugging) - b1 = a1; - } - } - - f32 a_sep = Inf; - f32 b_sep = Inf; - Vec2 a_midpoint = Zi; - Vec2 b_midpoint = Zi; - b32 ignore_a = 1; - b32 ignore_b = 1; - if (!collapse0 && !collapse1) - { - // Clip line to line - S_ClippedLine clip_result = S_ClipLineToLine(a0.p, b0.p, a1.p, b1.p, normal); - Vec2 a0_clipped = clip_result.a0_clipped; - Vec2 a1_clipped = clip_result.a1_clipped; - Vec2 b0_clipped = clip_result.b0_clipped; - Vec2 b1_clipped = clip_result.b1_clipped; - // Calc midpoint between clipped a & b - Vec2 va0a1_clipped = SubVec2(a1_clipped, a0_clipped); - Vec2 vb0b1_clipped = SubVec2(b1_clipped, b0_clipped); - a_sep = DotVec2(va0a1_clipped, normal); - b_sep = DotVec2(vb0b1_clipped, normal); - a_midpoint = AddVec2(a0_clipped, MulVec2(va0a1_clipped, 0.5f)); - b_midpoint = AddVec2(b0_clipped, MulVec2(vb0b1_clipped, 0.5f)); - ignore_a = 0; - ignore_b = 0; - Vec2 vfin = SubVec2(b_midpoint, a_midpoint); - if (Vec2LenSq(vfin) < (0.005 * 0.005)) - { - if (a_sep > b_sep) - { - ignore_a = 1; - } - else - { - ignore_b = 1; - } - } - } - else - { - Vec2 p0 = a0.p; - Vec2 p1 = a1.p; - // TODO: Choose ID based on closest clipped point - if (collapse1 && !collapse0) - { - // Project a1 onto vab0 - p0 = S_ClipPointToLine(a0.p, b0.p, a1.p, normal); - } - if (collapse0 && !collapse1) - { - // Project a0 onto vab1 - p1 = S_ClipPointToLine(a1.p, b1.p, a0.p, normal); - } - // Calc midpoint - Vec2 vsep = SubVec2(p1, p0); - a_midpoint = AddVec2(p0, MulVec2(vsep, 0.5f)); - a_sep = DotVec2(normal, p1) - DotVec2(normal, p0); - ignore_a = 0; - } - - // Insert points - if (!ignore_a && a_sep < tolerance) - { - S_CollisionPoint *point = &collision_points[collision_points_count++]; - point->id = a0.id | (a1.id << 4); - point->separation = a_sep; - point->p = a_midpoint; - } - if (!ignore_b && b_sep < tolerance) - { - S_CollisionPoint *point = &collision_points[collision_points_count++]; - point->id = b0.id | (b1.id << 4); - point->separation = b_sep; - point->p = b_midpoint; - } - } - } - - ////////////////////////////// - //- Compute closest points - - Vec2 closest_p0 = Zi; - Vec2 closest_p1 = Zi; - if (closest_feature.count == 1) - { - closest_p0 = closest_feature.a.s0.p; - closest_p1 = closest_feature.a.s1.p; - } - else - { - Assert(closest_feature.count == 2); - // FIXME: Winding order dependent? - f32 ratio = 0; - { - // Determine ratio between edge a & b that projected origin lies - Vec2 vab = SubVec2(closest_feature.b.p, closest_feature.a.p); - Vec2 vao = NegVec2(closest_feature.a.p); - ratio = ClampF32(DotVec2(vab, vao) / DotVec2(vab, vab), 0, 1); - } - // Shape 0 - closest_p0 = SubVec2(closest_feature.b.s0.p, closest_feature.a.s0.p); - closest_p0 = MulVec2(closest_p0, ratio); - closest_p0 = AddVec2(closest_p0, closest_feature.a.s0.p); - // Shape 1 - closest_p1 = SubVec2(closest_feature.b.s1.p, closest_feature.a.s1.p); - closest_p1 = MulVec2(closest_p1, ratio); - closest_p1 = AddVec2(closest_p1, closest_feature.a.s1.p); - } - - CopyStructs(result.collision_points, collision_points, countof(collision_points)); - result.collision_points_count = collision_points_count; - result.collision_normal = normal; - result.closest_p0 = closest_p0; - result.closest_p1 = closest_p1; - - EndScratch(scratch); - return result; -} - -S_RaycastResult S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir) -{ - f32 radius = shape.radius; - - f32 ray_len = Vec2Len(ray_dir); - Vec2 ray_dir_norm = DivVec2(ray_dir, ray_len); - - Vec2 s = ray_start; - Vec2 e = AddVec2(ray_start, ray_dir_norm); - Vec2 vse = ray_dir_norm; - - Vec2 isect = Zi; - Vec2 isect_normal = Zi; - b32 isect_is_round = 0; - b32 isect_found = 0; - if (shape.points_count > 1) - { - // Find expanded line intersections with ray - for (i32 p_idx = 0; p_idx < shape.points_count && !isect_found; ++p_idx) - { - Vec2 a = Zi; - Vec2 b = Zi; - Vec2 vab = Zi; - Vec2 normal = Zi; - { - i32 a_idx = p_idx; - i32 b_idx = a_idx + 1; - if (b_idx >= shape.points_count) - { - b_idx = 0; - } - - Vec2 a_orig = shape.points[a_idx]; - Vec2 b_orig = shape.points[b_idx]; - vab = SubVec2(b_orig, a_orig); - normal = NegVec2(PerpVec2(NormVec2(vab))); - Vec2 radius_add = MulVec2(normal, radius); - - a = AddVec2(a_orig, radius_add); - b = AddVec2(b_orig, radius_add); - } - Vec2 vsa = SubVec2(a, s); - Vec2 vsb = SubVec2(b, s); - f32 wa = WedgeVec2(vse, vsa); - f32 wb = WedgeVec2(vse, vsb); - if (wa > 0 && wb < 0) - { - f32 t = -wa / (wb - wa); - isect = AddVec2(a, MulVec2(vab, t)); - isect_normal = normal; - isect_found = 1; - } - } - - // Find closest rounded corner - if (!isect_found && radius != 0) - { - isect_is_round = 1; - for (i32 f_idx = 0; f_idx < shape.points_count && !isect_found; ++f_idx) - { - Vec2 f_orig = shape.points[f_idx]; - Vec2 a = Zi; - Vec2 b = Zi; - Vec2 vab = Zi; - { - i32 prev_idx = f_idx - 1; - i32 next_idx = f_idx + 1; - if (prev_idx < 0) - { - prev_idx = shape.points_count - 1; - } - if (next_idx >= shape.points_count) - { - next_idx = 0; - } - Vec2 prev_orig = shape.points[prev_idx]; - Vec2 next_orig = shape.points[next_idx]; - Vec2 vpf = SubVec2(f_orig, prev_orig); - Vec2 vfn = SubVec2(next_orig, f_orig); - Vec2 vpf_norm = NormVec2(vpf); - Vec2 vfn_norm = NormVec2(vfn); - Vec2 radius_add_a = MulVec2(PerpVec2(vpf_norm), -radius); - Vec2 radius_add_b = MulVec2(PerpVec2(vfn_norm), -radius); - a = AddVec2(f_orig, radius_add_a); - b = AddVec2(f_orig, radius_add_b); - } - Vec2 vsa = SubVec2(a, s); - Vec2 vsb = SubVec2(b, s); - f32 wa = WedgeVec2(vse, vsa); - f32 wb = WedgeVec2(vse, vsb); - if (wa > 0 && wb < 0) - { - isect = f_orig; - isect_found = 1; - } - } - } - - // Find closest corner - if (!isect_found) - { - f32 min_dist = Inf; - for (i32 p_idx = 0; p_idx < shape.points_count && !isect_found; ++p_idx) - { - Vec2 p = shape.points[p_idx]; - f32 dist = AbsF32(WedgeVec2(vse, SubVec2(p, s))); - if (dist < min_dist) - { - isect = p; - min_dist = dist; - } - } - } - } - else if (shape.points_count == 1 && radius != 0) - { - isect = shape.points[0]; - isect_is_round = 1; - } - - - // Find round intersection - b32 is_intersecting = 0; - if (isect_is_round || !isect_found) - { - Vec2 vsi = SubVec2(isect, s); - - f32 dot = DotVec2(vse, vsi); - f32 wedge = WedgeVec2(vse, vsi); - - is_intersecting = AbsF32(wedge) < radius; - if (is_intersecting) - { - f32 diff = SqrtF32(radius * radius - wedge * wedge); - f32 entrance_t = dot - diff; - f32 exit_t = dot + diff; - { - Vec2 old_isect = isect; - isect = AddVec2(s, MulVec2(vse, entrance_t)); - isect_normal = NormVec2(SubVec2(isect, old_isect)); - } - } - } - else - { - is_intersecting = isect_found; - } - - S_RaycastResult result = Zi; - result.is_intersecting = is_intersecting; - if (is_intersecting) - { - result.p = isect; - result.normal = isect_normal; - } - - return result; -} - -Vec2 S_EdgePointFromShape(S_Shape shape, Vec2 dir) -{ - Vec2 result = shape.centroid; - S_RaycastResult raycast = S_RaycastShape(shape, shape.centroid, NegVec2(dir)); - if (raycast.is_intersecting) - { - result = raycast.p; - } - return result; -} - -//////////////////////////////////////////////////////////// -//~ Lookup helpers - -S_Ent *S_EntFromKey(S_World *world, S_Key key) -{ - S_Ent *result = &S_NilEnt; - if (!S_IsKeyNil(key) && world->ent_bins_count > 0) - { - S_EntBin *bin = &world->ent_bins[key.v % world->ent_bins_count]; - for (S_Ent *e = bin->first; e; e = e->next_in_bin) - { - if (e->key.v == key.v) - { - result = e; - break; - } - } - } - return result; -} - -//////////////////////////////////////////////////////////// -//~ Iteration helpers - -S_Ent *S_FirstEnt(S_World *world) -{ - S_Ent *result = &S_NilEnt; - if (world->first_ent) - { - result = world->first_ent; - } - return result; -} - -S_Ent *S_NextEnt(S_Ent *e) -{ - S_Ent *result = &S_NilEnt; - if (e && e->next) - { - result = e->next; - } - return result; -} - -S_Ent *S_PushTempEnt(Arena *arena, S_EntList *list) -{ - S_EntListNode *n = PushStruct(arena, S_EntListNode); - SllQueuePush(list->first, list->last, n); - ++list->count; - S_Ent *ent = &n->ent; - *ent = S_NilEnt; - ent->valid = 1; - ent->exists = 1; - return ent; -} - -void S_SpawnEntsFromList(Arena *arena, S_World *world, S_EntList ents) -{ - for (S_EntListNode *n = ents.first; n; n = n->next) - { - S_Ent *src = &n->ent; - S_Key key = src->key; - if (!S_IsKeyNil(src->key)) - { - S_EntBin *bin = &world->ent_bins[key.v % world->ent_bins_count]; - S_Ent *dst = bin->first; - for (; dst; dst = dst->next_in_bin) - { - if (S_MatchKey(dst->key, key)) - { - break; - } - } - if (!dst) - { - // FIXME: Use free list - dst = PushStructNoZero(arena, S_Ent); - DllQueuePush(world->first_ent, world->last_ent, dst); - DllQueuePushNP(bin->first, bin->last, dst, next_in_bin, prev_in_bin); - } - S_Ent *old_next = dst->next; - S_Ent *old_prev = dst->prev; - S_Ent *old_next_in_bin = dst->next_in_bin; - S_Ent *old_prev_in_bin = dst->prev_in_bin; - { - *dst = *src; - } - dst->next = old_next; - dst->prev = old_prev; - dst->next_in_bin = old_next_in_bin; - dst->prev_in_bin = old_prev_in_bin; - ++world->ents_count; - } - } -} - -//////////////////////////////////////////////////////////// -//~ Debug draw - -void S_DebugDrawPoint(Vec2 p, Vec4 srgb) -{ - if (S_tl.is_sim_tick_thread && 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_tl.is_sim_tick_thread && 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_tl.is_sim_tick_thread && 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_tl.is_sim_tick_thread && 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) { - S_tl.is_sim_tick_thread = 1; Arena *perm = PermArena(); Arena *frame_arena = AcquireArena(Gibi(64)); - S.debug_draw_descs_arena = AcquireArena(Gibi(64)); - Arena *world_arena = AcquireArena(Gibi(64)); - S_World *world = 0; + P_World *world = 0; // TODO: Real per-client deltas b32 has_sent_initial_tick = 0; @@ -1272,7 +36,7 @@ void S_TickForever(WaveLaneCtx *lane) while (!shutdown) { shutdown = Atomic32Fetch(&S.shutdown); - S.debug_draw_enabled = TweakBool("Simulation debug draw", 1); + P_tl.debug_draw_enabled = TweakBool("Simulation debug draw", 1); ResetArena(frame_arena); @@ -1291,10 +55,10 @@ void S_TickForever(WaveLaneCtx *lane) { packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp")); } - S_UnpackedWorld unpacked = S_UnpackWorld(frame_arena, packed); + P_UnpackedWorld unpacked = P_UnpackWorld(frame_arena, packed); ResetArena(world_arena); - world = PushStruct(world_arena, S_World); + world = PushStruct(world_arena, P_World); world->seed = unpacked.seed; world->tick = unpacked.tick; world->time_ns = unpacked.time_ns; @@ -1304,20 +68,20 @@ void S_TickForever(WaveLaneCtx *lane) } world->ent_bins_count = Kibi(16); - world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count); + world->ent_bins = PushStructs(world_arena, P_EntBin, world->ent_bins_count); // Copy tiles - world->tiles = PushStructsNoZero(world_arena, u8, S_TilesCount); - CopyStructs(world->tiles, unpacked.tiles, S_TilesCount); + world->tiles = PushStructsNoZero(world_arena, u8, P_TilesCount); + CopyStructs(world->tiles, unpacked.tiles, P_TilesCount); // Copy ents - S_SpawnEntsFromList(world_arena, world, unpacked.ents); + P_SpawnEntsFromList(world_arena, world, unpacked.ents); } //- Swap out if (swapout) { - String packed = S_PackWorld(frame_arena, world); + String packed = P_PackWorld(frame_arena, world); WriteSwappedState(Lit("pp_sim.swp"), packed); } } @@ -1333,22 +97,22 @@ void S_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Pop sim commands - S_InputState *input = 0; - LockTicketMutex(&S.input_back_tm); + P_InputState *input = 0; + LockTicketMutex(&P.sim_input_back_tm); { - input = &S.input_states[S.input_back_idx]; - ++S.input_back_idx; - if (S.input_back_idx >= countof(S.input_states)) + input = &P.sim_input_states[P.sim_input_back_idx]; + ++P.sim_input_back_idx; + if (P.sim_input_back_idx >= countof(P.sim_input_states)) { - S.input_back_idx = 0; + P.sim_input_back_idx = 0; } } - UnlockTicketMutex(&S.input_back_tm); + UnlockTicketMutex(&P.sim_input_back_tm); ////////////////////////////// //- Update double-buffered entity data - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { ent->last_xf = ent->xf; } @@ -1359,10 +123,10 @@ void S_TickForever(WaveLaneCtx *lane) // FIXME: Only accept save command from local user b32 should_save = 0; - for (S_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) + for (P_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) { - S_Cmd *cmd = &cmd_node->cmd; - if (cmd->kind == S_CmdKind_Save) + P_Cmd *cmd = &cmd_node->cmd; + if (cmd->kind == P_CmdKind_Save) { should_save = 1; break; @@ -1380,25 +144,25 @@ void S_TickForever(WaveLaneCtx *lane) // FIXME: Only accept world deltas from users that can edit i64 applied_user_deltas_count = 0; - S_Delta **applied_user_deltas = PushStructsNoZero(frame_arena, S_Delta *, input->cmds_count); - for (S_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) + P_Delta **applied_user_deltas = PushStructsNoZero(frame_arena, P_Delta *, input->cmds_count); + for (P_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) { - S_Cmd *cmd = &cmd_node->cmd; - if (cmd->kind == S_CmdKind_Delta) + P_Cmd *cmd = &cmd_node->cmd; + if (cmd->kind == P_CmdKind_Delta) { - S_Delta *delta = &cmd->delta; + P_Delta *delta = &cmd->delta; b32 allow = 0; b32 forward = 0; - if (delta->kind == S_DeltaKind_Reset) + if (delta->kind == P_DeltaKind_Reset) { allow = 1; forward = 1; } - if (delta->kind == S_DeltaKind_RawEnt) + if (delta->kind == P_DeltaKind_RawEnt) { allow = 1; } - if (delta->kind == S_DeltaKind_Tile) + if (delta->kind == P_DeltaKind_Tile) { allow = 1; forward = 1; @@ -1410,7 +174,7 @@ void S_TickForever(WaveLaneCtx *lane) } if (allow) { - S_UpdateWorldFromDelta(world_arena, world, delta); + P_UpdateWorldFromDelta(world_arena, world, delta); } } } @@ -1418,17 +182,17 @@ void S_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Update ent controls - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { ent->fire_presses = 0; } - for (S_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) + for (P_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) { - S_Cmd cmd = cmd_node->cmd; - if (cmd.kind == S_CmdKind_Control) + P_Cmd cmd = cmd_node->cmd; + if (cmd.kind == P_CmdKind_Control) { - S_Ent *target = S_EntFromKey(world, cmd.target); + P_Ent *target = P_EntFromKey(world, cmd.target); if (target->valid) { target->move = ClampVec2Len(cmd.move, 1); @@ -1446,7 +210,7 @@ void S_TickForever(WaveLaneCtx *lane) // ////////////////////////////// // //- Push bullets - // for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + // for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // { // if (ent->fire_held) // { @@ -1461,7 +225,7 @@ void S_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Integrate control forces - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { // Xform xf = ent->xf; // Xform desired_xf = xf; @@ -1524,37 +288,37 @@ void S_TickForever(WaveLaneCtx *lane) // i64 max_constraints = 4096; // i64 constraints_count = 0; - // S_Constraint *constraints = PushStructs(frame_arena, S_Constraint, max_constraints); + // P_Constraint *constraints = PushStructs(frame_arena, P_Constraint, max_constraints); PERSIST i64 max_constraints = 4096; PERSIST i64 constraints_count = 0; - PERSIST S_Constraint *constraints = 0; + PERSIST P_Constraint *constraints = 0; if (!constraints) { - constraints = PushStructs(frame_arena, S_Constraint, max_constraints); + constraints = PushStructs(frame_arena, P_Constraint, max_constraints); } - for (S_Ent *ent0 = S_FirstEnt(world); ent0->valid; ent0 = S_NextEnt(ent0)) + for (P_Ent *ent0 = P_FirstEnt(world); ent0->valid; ent0 = P_NextEnt(ent0)) { - S_Shape shape0 = S_WorldShapeFromEnt(ent0); - for (S_Ent *ent1 = S_FirstEnt(world); ent1->valid; ent1 = S_NextEnt(ent1)) + P_Shape shape0 = P_WorldShapeFromEnt(ent0); + for (P_Ent *ent1 = P_FirstEnt(world); ent1->valid; ent1 = P_NextEnt(ent1)) { if (ent1 > ent0) { - S_Shape shape1 = S_WorldShapeFromEnt(ent1); + P_Shape shape1 = P_WorldShapeFromEnt(ent1); // TODO: World query - S_CollisionResult collision = S_CollisionResultFromShapes(shape0, shape1); + P_CollisionResult collision = P_CollisionResultFromShapes(shape0, shape1); if (collision.collision_points_count > 0) { // FIXME: Key lookup - S_Constraint *constraint = 0; + P_Constraint *constraint = 0; { b32 match = 0; for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx) { constraint = &constraints[constraint_idx]; - if (S_MatchKey(constraint->ent0, ent0->key) && S_MatchKey(constraint->ent1, ent1->key)) + if (P_MatchKey(constraint->ent0, ent0->key) && P_MatchKey(constraint->ent1, ent1->key)) { match = 1; break; @@ -1595,7 +359,7 @@ void S_TickForever(WaveLaneCtx *lane) // Delete old contacts that are no longer present for (i32 contact_point_idx = 0; contact_point_idx < constraint->points_count; ++contact_point_idx) { - S_ContactPoint *contact = &constraint->points[contact_point_idx]; + P_ContactPoint *contact = &constraint->points[contact_point_idx]; u32 id = contact->id; b32 match = 0; for (i32 collision_point_idx = 0; collision_point_idx < collision.collision_points_count; ++collision_point_idx) @@ -1618,14 +382,14 @@ void S_TickForever(WaveLaneCtx *lane) // Create / update contacts from collision for (i32 collision_point_idx = 0; collision_point_idx < collision.collision_points_count; ++collision_point_idx) { - S_CollisionPoint collision_point = collision.collision_points[collision_point_idx]; + P_CollisionPoint collision_point = collision.collision_points[collision_point_idx]; u32 id = collision_point.id; - S_ContactPoint *contact = 0; + P_ContactPoint *contact = 0; { for (i32 contact_point_idx = 0; contact_point_idx < constraint->points_count; ++contact_point_idx) { - S_ContactPoint *tmp = &constraint->points[contact_point_idx]; + P_ContactPoint *tmp = &constraint->points[contact_point_idx]; if (tmp->id == id) { contact = tmp; @@ -1650,17 +414,17 @@ void S_TickForever(WaveLaneCtx *lane) // // Debug draw // { - // // S_Ent *ent0 = S_EntFromKey(world, constraint->ent0); - // // S_Ent *ent1 = S_EntFromKey(world, constraint->ent1); + // // P_Ent *ent0 = P_EntFromKey(world, constraint->ent0); + // // P_Ent *ent1 = P_EntFromKey(world, constraint->ent1); // Vec2 normal = constraint->normal; // Vec2 center0 = Zi; // Vec2 center1 = Zi; - // if (ent0->valid) center0 = S_WorldShapeFromEnt(ent0).center_of_mass; - // if (ent1->valid) center1 = S_WorldShapeFromEnt(ent1).center_of_mass; + // if (ent0->valid) center0 = P_WorldShapeFromEnt(ent0).center_of_mass; + // if (ent1->valid) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; // Vec2 p0 = AddVec2(center0, vcp0); // Vec2 p1 = AddVec2(center1, vcp1); - // S_DebugDrawPoint(p0, Color_Cyan); - // S_DebugDrawLine(p0, AddVec2(p0, normal), Color_White); + // P_DebugDrawPoint(p0, Color_Cyan); + // P_DebugDrawLine(p0, AddVec2(p0, normal), Color_White); // } } } @@ -1681,33 +445,33 @@ void S_TickForever(WaveLaneCtx *lane) // // i64 max_constraints = 4096; // // i64 constraints_count = 0; - // // S_Constraint *constraints = PushStructs(frame_arena, S_Constraint, max_constraints); + // // P_Constraint *constraints = PushStructs(frame_arena, P_Constraint, max_constraints); // PERSIST i64 max_constraints = 4096; // PERSIST i64 constraints_count = 0; - // PERSIST S_Constraint *constraints = 0; + // PERSIST P_Constraint *constraints = 0; // if (!constraints) // { - // constraints = PushStructs(frame_arena, S_Constraint, max_constraints); + // constraints = PushStructs(frame_arena, P_Constraint, max_constraints); // } - // for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + // for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // { // if (ent->is_player) // { // Xform last_xf = ent->last_xf; // Xform xf = ent->xf; - // S_Shape local_shape = S_LocalShapeFromEnt(ent); - // S_Shape last_world_shape = S_MulXformShape(last_xf, local_shape); - // S_Shape shape0 = S_WorldShapeFromEnt(ent); + // P_Shape local_shape = P_LocalShapeFromEnt(ent); + // P_Shape last_world_shape = P_MulXformShape(last_xf, local_shape); + // P_Shape shape0 = P_WorldShapeFromEnt(ent); // // TODO: Real constraint data // Rng2 test_rect = Zi; // test_rect.p0 = VEC2(-1, -1); // test_rect.p1 = VEC2(1, 1); - // S_Shape shape1 = S_ShapeFromDesc( + // P_Shape shape1 = P_ShapeFromDesc( // // .radius = 0.5, // .radius = 2, // .count = 4, @@ -1717,20 +481,20 @@ void S_TickForever(WaveLaneCtx *lane) // .points[3] = VEC2(test_rect.p0.x, test_rect.p1.y), // ); - // S_DebugDrawShape(shape1, Color_Orange); + // P_DebugDrawShape(shape1, Color_Orange); // // TODO: World query - // S_CollisionResult collision = S_CollisionResultFromShapes(shape0, shape1); + // P_CollisionResult collision = P_CollisionResultFromShapes(shape0, shape1); // if (collision.collision_points_count > 0) // { // // FIXME: Key lookup - // S_Constraint *constraint = 0; + // P_Constraint *constraint = 0; // { // b32 match = 0; // for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx) // { // constraint = &constraints[constraint_idx]; - // if (S_MatchKey(constraint->ent0, ent->key)) + // if (P_MatchKey(constraint->ent0, ent->key)) // { // match = 1; // break; @@ -1768,7 +532,7 @@ void S_TickForever(WaveLaneCtx *lane) // // Delete old contacts that are no longer present // for (i32 contact_point_idx = 0; contact_point_idx < constraint->points_count; ++contact_point_idx) // { - // S_ContactPoint *contact = &constraint->points[contact_point_idx]; + // P_ContactPoint *contact = &constraint->points[contact_point_idx]; // u32 id = contact->id; // b32 match = 0; // for (i32 collision_point_idx = 0; collision_point_idx < collision.collision_points_count; ++collision_point_idx) @@ -1791,14 +555,14 @@ void S_TickForever(WaveLaneCtx *lane) // // Create / update contacts from collision // for (i32 collision_point_idx = 0; collision_point_idx < collision.collision_points_count; ++collision_point_idx) // { - // S_CollisionPoint collision_point = collision.collision_points[collision_point_idx]; + // P_CollisionPoint collision_point = collision.collision_points[collision_point_idx]; // u32 id = collision_point.id; - // S_ContactPoint *contact = 0; + // P_ContactPoint *contact = 0; // { // for (i32 contact_point_idx = 0; contact_point_idx < constraint->points_count; ++contact_point_idx) // { - // S_ContactPoint *tmp = &constraint->points[contact_point_idx]; + // P_ContactPoint *tmp = &constraint->points[contact_point_idx]; // if (tmp->id == id) // { // contact = tmp; @@ -1823,17 +587,17 @@ void S_TickForever(WaveLaneCtx *lane) // // Debug draw // { - // S_Ent *ent0 = S_EntFromKey(world, constraint->ent0); - // S_Ent *ent1 = S_EntFromKey(world, constraint->ent1); + // P_Ent *ent0 = P_EntFromKey(world, constraint->ent0); + // P_Ent *ent1 = P_EntFromKey(world, constraint->ent1); // Vec2 normal = constraint->normal; // Vec2 center0 = Zi; // Vec2 center1 = Zi; - // if (ent0->valid) center0 = S_WorldShapeFromEnt(ent0).center_of_mass; - // if (ent1->valid) center1 = S_WorldShapeFromEnt(ent1).center_of_mass; + // if (ent0->valid) center0 = P_WorldShapeFromEnt(ent0).center_of_mass; + // if (ent1->valid) center1 = P_WorldShapeFromEnt(ent1).center_of_mass; // Vec2 p0 = AddVec2(center0, vcp0); // Vec2 p1 = AddVec2(center1, vcp1); - // S_DebugDrawPoint(p0, Color_Cyan); - // S_DebugDrawLine(p0, AddVec2(p0, normal), Color_White); + // P_DebugDrawPoint(p0, Color_Cyan); + // P_DebugDrawLine(p0, AddVec2(p0, normal), Color_White); // } // } // } @@ -1848,11 +612,11 @@ void S_TickForever(WaveLaneCtx *lane) i64 constraint_idx = 0; while (constraint_idx < constraints_count) { - S_Constraint *constraint = &constraints[constraint_idx]; + P_Constraint *constraint = &constraints[constraint_idx]; b32 prune = 1; if (constraint->last_touched_tick == world->tick) { - if (S_EntFromKey(world, constraint->ent0)->valid || S_EntFromKey(world, constraint->ent1)->valid) + if (P_EntFromKey(world, constraint->ent0)->valid || P_EntFromKey(world, constraint->ent1)->valid) { prune = 0; } @@ -1861,7 +625,7 @@ void S_TickForever(WaveLaneCtx *lane) { // Prune by replacing with last constraint // TODO: Investigate whether the reordering here can degrade stability - S_Constraint *last_constraint = &constraints[constraints_count - 1]; + P_Constraint *last_constraint = &constraints[constraints_count - 1]; *constraint = *last_constraint; constraints_count -= 1; } @@ -1886,7 +650,7 @@ void S_TickForever(WaveLaneCtx *lane) for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx) { - S_Constraint *constraint = &constraints[constraint_idx]; + P_Constraint *constraint = &constraints[constraint_idx]; Vec2 normal = constraint->normal; Vec2 tangent = PerpVec2(normal); f32 inv_m0 = constraint->inv_m0; @@ -1896,7 +660,7 @@ void S_TickForever(WaveLaneCtx *lane) for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) { - S_ContactPoint *contact = &constraint->points[contact_idx]; + P_ContactPoint *contact = &constraint->points[contact_idx]; Vec2 vcp0 = contact->vcp0; Vec2 vcp1 = contact->vcp1; @@ -1923,10 +687,10 @@ void S_TickForever(WaveLaneCtx *lane) for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx) { - S_Constraint *constraint = &constraints[constraint_idx]; + P_Constraint *constraint = &constraints[constraint_idx]; - S_Ent *ent0 = S_EntFromKey(world, constraint->ent0); - S_Ent *ent1 = S_EntFromKey(world, constraint->ent1); + P_Ent *ent0 = P_EntFromKey(world, constraint->ent0); + P_Ent *ent1 = P_EntFromKey(world, constraint->ent1); Vec2 v0 = ent0->solved_v; Vec2 v1 = ent1->solved_v; @@ -1937,7 +701,7 @@ void S_TickForever(WaveLaneCtx *lane) Vec2 tangent = PerpVec2(normal); for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) { - S_ContactPoint *contact = &constraint->points[contact_idx]; + P_ContactPoint *contact = &constraint->points[contact_idx]; Vec2 vcp0 = contact->vcp0; Vec2 vcp1 = contact->vcp1; @@ -1967,10 +731,10 @@ void S_TickForever(WaveLaneCtx *lane) for (i64 constraint_idx = 0; constraint_idx < constraints_count; ++constraint_idx) { - S_Constraint *constraint = &constraints[constraint_idx]; + P_Constraint *constraint = &constraints[constraint_idx]; - S_Ent *ent0 = S_EntFromKey(world, constraint->ent0); - S_Ent *ent1 = S_EntFromKey(world, constraint->ent1); + P_Ent *ent0 = P_EntFromKey(world, constraint->ent0); + P_Ent *ent1 = P_EntFromKey(world, constraint->ent1); f32 inv_m0 = constraint->inv_m0; f32 inv_m1 = constraint->inv_m1; @@ -1985,18 +749,18 @@ void S_TickForever(WaveLaneCtx *lane) Vec2 center1 = constraint->static_center1; if (ent0->valid) { - center0 = S_WorldShapeFromEnt(ent0).center_of_mass; + center0 = P_WorldShapeFromEnt(ent0).center_of_mass; } if (ent1->valid) { - center1 = S_WorldShapeFromEnt(ent1).center_of_mass; + center1 = P_WorldShapeFromEnt(ent1).center_of_mass; } // Normal impulse Vec2 normal = constraint->normal; for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) { - S_ContactPoint *contact = &constraint->points[contact_idx]; + P_ContactPoint *contact = &constraint->points[contact_idx]; Vec2 vcp0 = contact->vcp0; Vec2 vcp1 = contact->vcp1; Vec2 p0 = AddVec2(center0, vcp0); @@ -2051,7 +815,7 @@ void S_TickForever(WaveLaneCtx *lane) Vec2 tangent = PerpVec2(normal); for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx) { - S_ContactPoint *contact = &constraint->points[contact_idx]; + P_ContactPoint *contact = &constraint->points[contact_idx]; Vec2 vcp0 = contact->vcp0; Vec2 vcp1 = contact->vcp1; @@ -2093,7 +857,7 @@ void S_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Integrate velocities - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { Xform xf = ent->xf; xf.og = AddVec2(xf.og, MulVec2(ent->solved_v, solver_dt)); @@ -2118,7 +882,7 @@ void S_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Move bullets - for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet)) + for (P_Ent *bullet = P_FirstEnt(world); bullet->valid; bullet = P_NextEnt(bullet)) { if (bullet->is_bullet) { @@ -2136,8 +900,8 @@ void S_TickForever(WaveLaneCtx *lane) // TODO: Remove this { - S_EntList bullets_to_spawn = Zi; - for (S_Ent *firer = S_FirstEnt(world); firer->valid; firer = S_NextEnt(firer)) + P_EntList bullets_to_spawn = Zi; + for (P_Ent *firer = P_FirstEnt(world); firer->valid; firer = P_NextEnt(firer)) { if (firer->fire_held) // if (firer->fire_presses) @@ -2160,15 +924,15 @@ void S_TickForever(WaveLaneCtx *lane) i64 tick_bullets_count = bullets_per_fire; if (tick_bullets_count > 0) { - S_Shape firer_world_shape = S_WorldShapeFromEnt(firer); + P_Shape firer_world_shape = P_WorldShapeFromEnt(firer); - Vec2 pos = S_EdgePointFromShape(firer_world_shape, firer->look); + Vec2 pos = P_EdgePointFromShape(firer_world_shape, firer->look); for (i64 bullet_idx = 0; bullet_idx < tick_bullets_count; ++bullet_idx) { - S_Ent *bullet = S_PushTempEnt(frame_arena, &bullets_to_spawn); + P_Ent *bullet = P_PushTempEnt(frame_arena, &bullets_to_spawn); bullet->is_bullet = 1; - bullet->key = S_RandKey(); + bullet->key = P_RandKey(); // FIXME: Remove this @@ -2193,7 +957,7 @@ void S_TickForever(WaveLaneCtx *lane) } } } - S_SpawnEntsFromList(world_arena, world, bullets_to_spawn); + P_SpawnEntsFromList(world_arena, world, bullets_to_spawn); } @@ -2204,7 +968,7 @@ void S_TickForever(WaveLaneCtx *lane) // TODO: Separate 'hits' from bullets, so that bullets can have multiple hits - for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet)) + for (P_Ent *bullet = P_FirstEnt(world); bullet->valid; bullet = P_NextEnt(bullet)) { if (bullet->is_bullet) { @@ -2214,21 +978,21 @@ void S_TickForever(WaveLaneCtx *lane) Vec2 ray_dir = SubVec2(ray_end, ray_start); // TODO: Real raycast query - S_Ent *closest_victim = &S_NilEnt; - S_RaycastResult victim_raycast = Zi; + P_Ent *closest_victim = &P_NilEnt; + P_RaycastResult victim_raycast = Zi; { f32 closest_len_sq = Inf; - for (S_Ent *victim = S_FirstEnt(world); victim->valid; victim = S_NextEnt(victim)) + for (P_Ent *victim = P_FirstEnt(world); victim->valid; victim = P_NextEnt(victim)) { - if (victim->is_player && !S_MatchKey(victim->key, bullet->bullet_firer)) + if (victim->is_player && !P_MatchKey(victim->key, bullet->bullet_firer)) { - S_Shape victim_world_shape = S_WorldShapeFromEnt(victim); + P_Shape victim_world_shape = P_WorldShapeFromEnt(victim); - S_RaycastResult entrance_raycast = S_RaycastShape(victim_world_shape, ray_start, ray_dir); + P_RaycastResult entrance_raycast = P_RaycastShape(victim_world_shape, ray_start, ray_dir); Vec2 entrance = entrance_raycast.p; if (entrance_raycast.is_intersecting) { - S_RaycastResult exit_raycast = S_RaycastShape(victim_world_shape, ray_start, NegVec2(ray_dir)); + P_RaycastResult exit_raycast = P_RaycastShape(victim_world_shape, ray_start, NegVec2(ray_dir)); Vec2 exit = exit_raycast.p; f32 da = DotVec2(ray_dir, SubVec2(entrance, ray_start)); f32 db = DotVec2(ray_dir, SubVec2(exit, ray_start)); @@ -2257,8 +1021,8 @@ void S_TickForever(WaveLaneCtx *lane) } Rng2 bounds = Zi; - bounds.p0 = VEC2(-S_WorldPitch / 2, -S_WorldPitch / 2); - bounds.p1 = VEC2(S_WorldPitch / 2, S_WorldPitch / 2); + bounds.p0 = VEC2(-P_WorldPitch / 2, -P_WorldPitch / 2); + bounds.p1 = VEC2(P_WorldPitch / 2, P_WorldPitch / 2); if ( bullet->bullet_start.x < bounds.p0.x || bullet->bullet_start.y < bounds.p0.y || bullet->bullet_start.x > bounds.p1.x || bullet->bullet_start.y > bounds.p1.y @@ -2274,7 +1038,7 @@ void S_TickForever(WaveLaneCtx *lane) // { - // Struct(S_Bullet) + // Struct(P_Bullet) // { // Vec2 start; // Vec2 dir; @@ -2283,11 +1047,11 @@ void S_TickForever(WaveLaneCtx *lane) // }; // PERSIST i64 bullets_count = 1; - // PERSIST S_Bullet *bullets = 0; + // PERSIST P_Bullet *bullets = 0; // if (!bullets) // { - // bullets = PushStruct(PermArena(), S_Bullet); - // S_Bullet *bullet = &bullets[0]; + // bullets = PushStruct(PermArena(), P_Bullet); + // P_Bullet *bullet = &bullets[0]; // bullet->start = VEC2(1, 0); // bullet->dir = NormVec2(VEC2(1, -1)); // bullet->speed = 1; @@ -2295,37 +1059,37 @@ void S_TickForever(WaveLaneCtx *lane) // for (i64 bullet_idx = 0; bullet_idx < bullets_count; ++bullet_idx) // { - // S_Bullet *bullet = &bullets[bullet_idx]; + // P_Bullet *bullet = &bullets[bullet_idx]; // // Raycast - // for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + // for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // { // Xform xf = ent->xf; - // S_Shape world_shape = S_MulXformShape(xf, ent->local_shape); + // P_Shape world_shape = P_MulXformShape(xf, ent->local_shape); - // if (ent == S_FirstEnt(world)) + // if (ent == P_FirstEnt(world)) // { // bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius)); // bullet->dir = NormVec2(ent->look); // } // else // { - // S_RaycastResult raycast = S_RaycastShape(world_shape, bullet->start, bullet->dir); + // P_RaycastResult raycast = P_RaycastShape(world_shape, bullet->start, bullet->dir); // Vec2 isect = raycast.p; // if (raycast.is_intersecting) // { - // S_DebugDrawPoint(isect, Color_Green); - // S_DebugDrawLine(isect, AddVec2(isect, MulVec2(raycast.normal, 0.5)), Color_White); + // P_DebugDrawPoint(isect, Color_Green); + // P_DebugDrawLine(isect, AddVec2(isect, MulVec2(raycast.normal, 0.5)), Color_White); // } // else // { - // S_DebugDrawPoint(isect, Color_Purple); + // P_DebugDrawPoint(isect, Color_Purple); // } // } // } - // S_DebugDrawLine(bullet->start, AddVec2(bullet->start, Vec2WithLen(bullet->dir, 0.5)), Color_Red); + // P_DebugDrawLine(bullet->start, AddVec2(bullet->start, Vec2WithLen(bullet->dir, 0.5)), Color_Red); // } // } @@ -2333,7 +1097,7 @@ void S_TickForever(WaveLaneCtx *lane) - // // Struct(S_Bullet) + // // Struct(P_Bullet) // // { // // Vec2 start; // // Vec2 dir; @@ -2342,11 +1106,11 @@ void S_TickForever(WaveLaneCtx *lane) // // }; // // PERSIST i64 bullets_count = 1; - // // PERSIST S_Bullet *bullets = 0; + // // PERSIST P_Bullet *bullets = 0; // // if (!bullets) // // { - // // bullets = PushStruct(PermArena(), S_Bullet); - // // S_Bullet *bullet = &bullets[0]; + // // bullets = PushStruct(PermArena(), P_Bullet); + // // P_Bullet *bullet = &bullets[0]; // // bullet->start = VEC2(1, 0); // // bullet->dir = NormVec2(VEC2(1, -1)); // // bullet->speed = 1; @@ -2354,37 +1118,37 @@ void S_TickForever(WaveLaneCtx *lane) // // for (i64 bullet_idx = 0; bullet_idx < bullets_count; ++bullet_idx) // // { - // // S_Bullet *bullet = &bullets[bullet_idx]; + // // P_Bullet *bullet = &bullets[bullet_idx]; // // // Raycast - // // for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + // // for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // // { // // Xform xf = ent->xf; - // // S_Shape world_shape = S_MulXformShape(xf, ent->local_shape); + // // P_Shape world_shape = P_MulXformShape(xf, ent->local_shape); - // // if (ent == S_FirstEnt(world)) + // // if (ent == P_FirstEnt(world)) // // { // // bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius)); // // bullet->dir = NormVec2(ent->look); // // } // // else // // { - // // S_RaycastResult raycast = S_RaycastShape(world_shape, bullet->start, bullet->dir); + // // P_RaycastResult raycast = P_RaycastShape(world_shape, bullet->start, bullet->dir); // // Vec2 isect = raycast.p; // // if (raycast.is_intersecting) // // { - // // S_DebugDrawPoint(isect, Color_Green); - // // S_DebugDrawLine(isect, AddVec2(isect, MulVec2(raycast.normal, 0.5)), Color_White); + // // P_DebugDrawPoint(isect, Color_Green); + // // P_DebugDrawLine(isect, AddVec2(isect, MulVec2(raycast.normal, 0.5)), Color_White); // // } // // else // // { - // // S_DebugDrawPoint(isect, Color_Purple); + // // P_DebugDrawPoint(isect, Color_Purple); // // } // // } // // } - // // S_DebugDrawLine(bullet->start, AddVec2(bullet->start, Vec2WithLen(bullet->dir, 0.5)), Color_Red); + // // P_DebugDrawLine(bullet->start, AddVec2(bullet->start, Vec2WithLen(bullet->dir, 0.5)), Color_Red); // // } // } @@ -2396,40 +1160,40 @@ void S_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Debug draw entities - if (S.debug_draw_enabled) + if (P_tl.debug_draw_enabled) { - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { - S_Shape world_shape = S_WorldShapeFromEnt(ent); + P_Shape world_shape = P_WorldShapeFromEnt(ent); // Draw aabb { Vec4 color = VEC4(0.4, 0.2, 0.2, 1); - Rng2 bb = S_BoundingBoxFromShape(world_shape); - S_DebugDrawRect(bb, color); + Rng2 bb = P_BoundingBoxFromShape(world_shape); + P_DebugDrawRect(bb, color); } // Draw shape { // Vec4 color = Color_Cyan; Vec4 color = VEC4(0.2, 0.4, 0.2, 1); - S_DebugDrawShape(world_shape, color); + P_DebugDrawShape(world_shape, color); } // Draw rot { Vec4 color = VEC4(0.8, 0.8, 0.8, 1); Vec2 p0 = world_shape.centroid; - Vec2 p1 = S_EdgePointFromShape(world_shape, UpFromXform(ent->xf)); - S_DebugDrawLine(p0, p1, color); + Vec2 p1 = P_EdgePointFromShape(world_shape, UpFromXform(ent->xf)); + P_DebugDrawLine(p0, p1, color); } // Draw look { Vec4 color = VEC4(0.4, 0.8, 0.4, 1); Vec2 p0 = world_shape.centroid; - Vec2 p1 = S_EdgePointFromShape(world_shape, ent->look); - S_DebugDrawLine(p0, p1, color); + Vec2 p1 = P_EdgePointFromShape(world_shape, ent->look); + P_DebugDrawLine(p0, p1, color); } } } @@ -2438,11 +1202,11 @@ void S_TickForever(WaveLaneCtx *lane) //- Publish sim state // TODO: Only copy active entities - LockTicketMutex(&S.output_back_tm); + LockTicketMutex(&P.sim_output_back_tm); { - S_OutputState *output = &S.output_states[S.output_back_idx]; - S_SnapshotNode *snapshot_node = PushStruct(output->arena, S_SnapshotNode); - S_Snapshot *snapshot = &snapshot_node->snapshot; + P_OutputState *output = &P.sim_output_states[P.sim_output_back_idx]; + P_SnapshotNode *snapshot_node = PushStruct(output->arena, P_SnapshotNode); + P_Snapshot *snapshot = &snapshot_node->snapshot; SllQueuePush(output->first_snapshot_node, output->last_snapshot_node, snapshot_node); ++output->snapshots_count; @@ -2453,65 +1217,76 @@ void S_TickForever(WaveLaneCtx *lane) // Forward user edit deltas for (i64 applied_user_delta_idx = 0; applied_user_delta_idx < applied_user_deltas_count; ++applied_user_delta_idx) { - S_Delta *delta = 0; + P_Delta *delta = 0; { - S_DeltaNode *dn = PushStruct(output->arena, S_DeltaNode); + P_DeltaNode *dn = PushStruct(output->arena, P_DeltaNode); snapshot->deltas_count += 1; SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn); delta = &dn->delta; } - S_Delta *src = applied_user_deltas[applied_user_delta_idx]; + P_Delta *src = applied_user_deltas[applied_user_delta_idx]; *delta = *src; } // Push full tile delta if (!has_sent_initial_tick) { - S_Delta *delta = 0; + P_Delta *delta = 0; { - S_DeltaNode *dn = PushStruct(output->arena, S_DeltaNode); + P_DeltaNode *dn = PushStruct(output->arena, P_DeltaNode); snapshot->deltas_count += 1; SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn); delta = &dn->delta; } - delta->kind = S_DeltaKind_RawTiles; - delta->raw_tiles = PushStructsNoZero(output->arena, u8, S_TilesCount); - delta->tile_range = RNG2I32(VEC2I32(0, 0), VEC2I32(S_TilesPitch, S_TilesPitch)); - CopyBytes(delta->raw_tiles, world->tiles, S_TilesCount); + delta->kind = P_DeltaKind_RawTiles; + delta->raw_tiles = PushStructsNoZero(output->arena, u8, P_TilesCount); + delta->tile_range = RNG2I32(VEC2I32(0, 0), VEC2I32(P_TilesPitch, P_TilesPitch)); + CopyBytes(delta->raw_tiles, world->tiles, P_TilesCount); has_sent_initial_tick = 1; } // Push raw entity deltas - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { - S_Delta *delta = 0; + P_Delta *delta = 0; { - S_DeltaNode *dn = PushStruct(output->arena, S_DeltaNode); + P_DeltaNode *dn = PushStruct(output->arena, P_DeltaNode); snapshot->deltas_count += 1; SllQueuePush(snapshot->first_delta_node, snapshot->last_delta_node, dn); delta = &dn->delta; } - delta->kind = S_DeltaKind_RawEnt; + delta->kind = P_DeltaKind_RawEnt; delta->ent = *ent; } // 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); + i64 dst_idx = 0; + P_DebugDrawNode *dst_nodes = PushStructsNoZero(output->arena, P_DebugDrawNode, P_tl.debug_draw_nodes_count); + for (P_DebugDrawNode *src = P_tl.first_debug_draw_node; src; src = src->next) + { + P_DebugDrawNode *dst = &dst_nodes[dst_idx]; + *dst = *src; + dst_idx += 1; + SllQueuePush(output->first_debug_draw_node, output->last_debug_draw_node, dst); + } + output->debug_draw_nodes_count += P_tl.debug_draw_nodes_count; + + ResetArena(P_tl.debug_arena); + P_tl.first_debug_draw_node = 0; + P_tl.last_debug_draw_node = 0; + P_tl.debug_draw_nodes_count = 0; } } - UnlockTicketMutex(&S.output_back_tm); + UnlockTicketMutex(&P.sim_output_back_tm); ////////////////////////////// //- Prune ents { i64 ents_to_prune_count = 0; - S_Ent **ents_to_prune = PushStructsNoZero(frame_arena, S_Ent *, world->ents_count); - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + P_Ent **ents_to_prune = PushStructsNoZero(frame_arena, P_Ent *, world->ents_count); + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { if (ent->exists <= 0) { @@ -2524,8 +1299,8 @@ void S_TickForever(WaveLaneCtx *lane) { // FIXME: Add to free list // FIXME: Ensure sure prunes are received by user - S_Ent *ent = ents_to_prune[prune_idx]; - S_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count]; + P_Ent *ent = ents_to_prune[prune_idx]; + P_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count]; DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); DllQueueRemove(world->first_ent, world->last_ent, ent); world->ents_count -= 1; diff --git a/src/pp/pp_sim/pp_sim_core.h b/src/pp/pp_sim/pp_sim_core.h index 264ef013..21c97a65 100644 --- a/src/pp/pp_sim/pp_sim_core.h +++ b/src/pp/pp_sim/pp_sim_core.h @@ -1,382 +1,13 @@ -//////////////////////////////////////////////////////////// -//~ Key types - -#define S_NilKey ((S_Key) { 0 }) - -Struct(S_Key) -{ - u64 v; -}; - -//////////////////////////////////////////////////////////// -//~ Shape types - -Struct(S_ShapeDesc) -{ - f32 radius; - f32 mass; - i32 count; - Vec2 points[8]; -}; - -Struct(S_Shape) -{ - f32 mass; - Vec2 centroid; - Vec2 center_of_mass; - - f32 radius; - i32 points_count; - Vec2 points[8]; -}; - -//////////////////////////////////////////////////////////// -//~ Ent types - -// TODO: Move boolean fields into bitwise property flags - -// TODO: Pack efficiently, deduplicate redundant fields - -Struct(S_Ent) -{ - ////////////////////////////// - //- Internal world state - - S_Ent *next; - S_Ent *prev; - - S_Ent *next_in_bin; - S_Ent *prev_in_bin; - - ////////////////////////////// - //- Persistent data - - S_Key key; - b32 valid; - - ////////////////////////////// - //- Build data - - f32 exists; - - b32 is_player; - f32 health; - - Xform last_xf; - Xform xf; - - Vec2 move; - Vec2 look; - f32 fire_held; - f32 fire_presses; - - // TODO: Remove this (weapon testing) - i64 last_fire_ns; - b32 has_weapon; - - S_Key bullet_firer; - b32 is_bullet; - Vec2 bullet_start; - Vec2 bullet_end; - - b32 has_hit; - Vec2 hit_entry; - Vec2 hit_entry_normal; - - ////////////////////////////// - //- Solver data - - Vec2 solved_v; - f32 solved_w; -}; - -Struct(S_EntListNode) -{ - S_EntListNode *next; - S_Ent ent; -}; - -Struct(S_EntList) -{ - S_EntListNode *first; - S_EntListNode *last; - i64 count; -}; - -Struct(S_EntBin) -{ - S_Ent *first; - S_Ent *last; -}; - -//////////////////////////////////////////////////////////// -//~ Collision types - -Struct(S_SupportPoint) -{ - Vec2 p; - u32 id; // Index of the originating piont in the shape -}; - -Struct(S_CollisionPoint) -{ - Vec2 p; - f32 separation; - u32 id; // Based on polygon edge-to-edge -}; - -Struct(S_MenkowskiPoint) -{ - Vec2 p; // Menkowski difference point - S_SupportPoint s0; // Support point of first shape in dir - S_SupportPoint s1; // Support point of second shape in -dir -}; - -Struct(S_MenkowskiSimplex) -{ - i32 count; - S_MenkowskiPoint a, b, c; -}; - -Struct(S_ClippedLine) -{ - Vec2 a0_clipped, b0_clipped; - Vec2 a1_clipped, b1_clipped; -}; - -Struct(S_CollisionResult) -{ - // Collision manifold - i32 collision_points_count; - S_CollisionPoint collision_points[2]; - Vec2 collision_normal; - - // Closest points - Vec2 closest_p0; - Vec2 closest_p1; -}; - -Struct(S_RaycastResult) -{ - b32 is_intersecting; - Vec2 p; - Vec2 normal; -}; - -//////////////////////////////////////////////////////////// -//~ Constraint types - -Struct(S_ContactPoint) -{ - Vec2 vcp0; - Vec2 vcp1; - f32 starting_separation; - f32 inv_normal_mass; - f32 inv_tangent_mass; - u32 id; - - f32 solved_normal_impulse; - f32 solved_tangent_impulse; -}; - -Struct(S_Constraint) -{ - i64 last_touched_tick; - S_Key ent0; - S_Key ent1; - - Vec2 static_center0; - Vec2 static_center1; - - f32 inv_m0; - f32 inv_m1; - f32 inv_i0; - f32 inv_i1; - - Vec2 normal; - f32 friction; - - i32 points_count; - S_ContactPoint points[2]; -}; - -//////////////////////////////////////////////////////////// -//~ World types - -Struct(S_World) -{ - u64 seed; - i64 tick; - i64 time_ns; - - i64 ents_count; - S_Ent *first_ent; - S_Ent *last_ent; - - i64 ent_bins_count; - S_EntBin *ent_bins; - - u8 *tiles; -}; - -Enum(S_DeltaKind) -{ - S_DeltaKind_Reset, - S_DeltaKind_RawEnt, - S_DeltaKind_RawTiles, - S_DeltaKind_Tile, -}; - -Struct(S_Delta) -{ - S_DeltaKind kind; - - S_Ent ent; - - u8 *raw_tiles; - S_TileKind tile_kind; - Rng2I32 tile_range; -}; - -Struct(S_DeltaNode) -{ - S_DeltaNode *next; - S_Delta delta; -}; - -Struct(S_Snapshot) -{ - u64 seed; - i64 tick; - i64 time_ns; - - i64 deltas_count; - S_DeltaNode *first_delta_node; - S_DeltaNode *last_delta_node; -}; - -Struct(S_SnapshotNode) -{ - S_SnapshotNode *next; - S_Snapshot snapshot; -}; - -//////////////////////////////////////////////////////////// -//~ Command types - -Enum(S_CmdKind) -{ - S_CmdKind_Nop, - S_CmdKind_Save, - S_CmdKind_Delta, - S_CmdKind_Control, -}; - -Struct(S_Cmd) -{ - S_CmdKind kind; - - // Delta - S_Delta delta; - - // Control - S_Key target; - Vec2 move; - Vec2 look; - b32 fire_held; - i32 fire_presses; -}; - -Struct(S_CmdNode) -{ - S_CmdNode *next; - 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 -Struct(S_InputState) -{ - Arena *arena; - S_CmdNode *first_cmd_node; - S_CmdNode *last_cmd_node; - u64 cmds_count; -}; - -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) { Atomic32 shutdown; Fence shutdown_fence; - - b32 debug_draw_enabled; - Arena *debug_draw_descs_arena; - - //- Sim input - TicketMutex input_back_tm; - i32 input_back_idx; - S_InputState input_states[2]; - - //- Sim output - TicketMutex output_back_tm; - i32 output_back_idx; - S_OutputState output_states[2]; -}; - -Struct(S_ThreadLocalCtx) -{ - b32 is_sim_tick_thread; }; extern S_Ctx S; -extern ThreadLocal S_ThreadLocalCtx S_tl; -extern Readonly S_Ent S_NilEnt; //////////////////////////////////////////////////////////// //~ Bootstrap @@ -384,81 +15,6 @@ extern Readonly S_Ent S_NilEnt; void S_Bootstrap(void); void S_Shutdown(void); -//////////////////////////////////////////////////////////// -//~ Nil helpers - -b32 S_IsKeyNil(S_Key key); -b32 S_IsEntNil(S_Ent *ent); -b32 S_MatchKey(S_Key a, S_Key b); - -//////////////////////////////////////////////////////////// -//~ Key helpers - -S_Key S_RandKey(void); - -#define S_FmtKey(key) FmtHandle((key).v) - -//////////////////////////////////////////////////////////// -//~ Tile helpers - -String S_NameFromTileKind(S_TileKind kind); - -//////////////////////////////////////////////////////////// -//~ Delta helpers - -void S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta); - -//////////////////////////////////////////////////////////// -//~ Shape helpers - -S_Shape S_ShapeFromDescEx(S_ShapeDesc desc); -#define S_ShapeFromDesc(...) S_ShapeFromDescEx((S_ShapeDesc) { __VA_ARGS__ }) - -S_Shape S_MulXformShape(Xform xf, S_Shape shape); -Rng2 S_BoundingBoxFromShape(S_Shape shape); - -S_Shape S_LocalShapeFromEnt(S_Ent *ent); -S_Shape S_WorldShapeFromEnt(S_Ent *ent); - -//////////////////////////////////////////////////////////// -//~ Collision - -S_SupportPoint S_SupportPointFromShapeEx(S_Shape shape, Vec2 dir, i32 ignore_idx); -S_SupportPoint S_SupportPointFromShape(S_Shape shape, Vec2 dir); -S_MenkowskiPoint S_MenkowskiPointFromShapes(S_Shape shape0, S_Shape shape1, Vec2 dir); -S_ClippedLine S_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal); -Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal); - -S_CollisionResult S_CollisionResultFromShapes(S_Shape shape0, S_Shape shape1); -S_RaycastResult S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir); - -Vec2 S_EdgePointFromShape(S_Shape shape, Vec2 dir); - -//////////////////////////////////////////////////////////// -//~ Lookup helpers - -S_Ent *S_EntFromKey(S_World *world, S_Key key); - -//////////////////////////////////////////////////////////// -//~ Iteration helpers - -S_Ent *S_FirstEnt(S_World *world); -S_Ent *S_NextEnt(S_Ent *e); - -//////////////////////////////////////////////////////////// -//~ List helpers - -S_Ent *S_PushTempEnt(Arena *arena, S_EntList *list); -void S_SpawnEntsFromList(Arena *arena, S_World *world, S_EntList ents); - -//////////////////////////////////////////////////////////// -//~ 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_shared.cg b/src/pp/pp_sim/pp_sim_shared.cg deleted file mode 100644 index bd67b318..00000000 --- a/src/pp/pp_sim/pp_sim_shared.cg +++ /dev/null @@ -1,27 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Tile helpers - -i32 S_TileIdxFromTilePos(Vec2 p) -{ - i32 x = ClampI32(FloorF32(p.x), 0, S_TilesPitch - 1); - i32 y = ClampI32(FloorF32(p.y), 0, S_TilesPitch - 1); - i32 result = x + (y * S_TilesPitch); - return result; -} - -#if IsLanguageC - String S_TileNameFromKind(S_TileKind kind) - { - PERSIST Readonly String tile_names[S_TileKind_COUNT] = { - #define X(name, ...) [S_TileKind_##name] = CompLit(#name), - S_TilesXMacro(X) - #undef X - }; - String result = Zi; - if (kind >= 0 && kind < countof(tile_names)) - { - result = tile_names[kind]; - } - return result; - } -#endif diff --git a/src/pp/pp_sim/pp_sim_shared.cgh b/src/pp/pp_sim/pp_sim_shared.cgh deleted file mode 100644 index 993c2f36..00000000 --- a/src/pp/pp_sim/pp_sim_shared.cgh +++ /dev/null @@ -1,35 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Tile types - -#define S_WorldPitch 64.0 - -#define S_TilesPerMeter 2.0 -#define S_TilesPerSqMeter (V_TilesPerMeter * V_TilesPerMeter) - -#define S_TilesPitch (S_WorldPitch * S_TilesPerMeter) -#define S_TilesCount (S_TilesPitch * S_TilesPitch) - -#define S_TilesXMacro(X) \ - X(Empty) \ - X(Tile) \ - X(Carpet) \ - X(Wall) \ -/* -------------------- */ - -//- Tiles kinds enum -Enum(S_TileKind) -{ - #define X(name, ...) S_TileKind_##name, - S_TilesXMacro(X) - #undef X - S_TileKind_COUNT -}; - -//////////////////////////////////////////////////////////// -//~ Tile helpers - -i32 S_TileIdxFromTilePos(Vec2 p); - -#if IsLanguageC - String S_TileNameFromKind(S_TileKind kind); -#endif diff --git a/src/pp/pp_sim/pp_sim_transcode.h b/src/pp/pp_sim/pp_sim_transcode.h deleted file mode 100644 index e243693b..00000000 --- a/src/pp/pp_sim/pp_sim_transcode.h +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Transcode types - -Enum(S_Tv) -{ - S_Tv_None = 0, - S_Tv_Initial = 1, - - S_Tv_COUNT -}; - -#define S_Tv_Latest (S_Tv_COUNT - 1) - -Struct(S_UnpackedWorld) -{ - S_Tv version; - - u64 seed; - i64 tick; - i64 time_ns; - - S_EntList ents; - u8 *tiles; -}; - -//////////////////////////////////////////////////////////// -//~ Transcode - -String S_PackWorld(Arena *arena, S_World *src_world); -S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed); diff --git a/src/pp/pp_sim/pp_sim_transcode.c b/src/pp/pp_transcode.c similarity index 89% rename from src/pp/pp_sim/pp_sim_transcode.c rename to src/pp/pp_transcode.c index ed18e8ac..ebb757d7 100644 --- a/src/pp/pp_sim/pp_sim_transcode.c +++ b/src/pp/pp_transcode.c @@ -1,19 +1,13 @@ //////////////////////////////////////////////////////////// //~ Transcode - - -// FIXME: Header - - - -String S_PackWorld(Arena *arena, S_World *src_world) +String P_PackWorld(Arena *arena, P_World *src_world) { String result = Zi; result.text = ArenaNext(arena, u8); TempArena scratch = BeginScratch(arena); - result.len += StringF(arena, "version: %F\n", FmtUint(S_Tv_Latest)).len; + result.len += StringF(arena, "version: %F\n", FmtUint(P_Tv_Latest)).len; result.len += StringF(arena, "\n").len; result.len += StringF(arena, "seed: 0x%F\n", FmtHex(src_world->seed)).len; @@ -24,7 +18,7 @@ String S_PackWorld(Arena *arena, S_World *src_world) // FIXME: Precision result.len += PushString(arena, Lit("\nentities:\n")).len; result.len += PushString(arena, Lit("{\n")).len; - for (S_Ent *ent = S_FirstEnt(src_world); ent->valid; ent = S_NextEnt(ent)) + for (P_Ent *ent = P_FirstEnt(src_world); ent->valid; ent = P_NextEnt(ent)) { // TODO: Pack bullets if (!ent->is_bullet) @@ -60,7 +54,7 @@ String S_PackWorld(Arena *arena, S_World *src_world) result.len += PushString(arena, Lit("\ntiles:\n")).len; result.len += PushString(arena, Lit("{\n")).len; { - String tiles_str = Base64FromString(scratch.arena, STRING(S_TilesCount, src_world->tiles)); + String tiles_str = Base64FromString(scratch.arena, STRING(P_TilesCount, src_world->tiles)); u64 tile_chars_per_line = 128; u64 tile_char_pos = 0; while (tile_char_pos < tiles_str.len) @@ -80,15 +74,15 @@ String S_PackWorld(Arena *arena, S_World *src_world) return result; } -S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed) +P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed) { - S_UnpackedWorld result = Zi; + P_UnpackedWorld result = Zi; TempArena scratch = BeginScratch(arena); CR_Item *root = CR_ItemFromString(scratch.arena, packed); // Unpack version - S_Tv version = 0; + P_Tv version = 0; for (CR_Item *root_item = root->first; root_item; root_item = root_item->next) { if (MatchString(root_item->name, Lit("version"))) @@ -119,8 +113,8 @@ S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed) { for (CR_Item *ent_item = top_item->first; ent_item; ent_item = ent_item->next) { - S_Ent *ent = S_PushTempEnt(arena, &result.ents); - ent->key = (S_Key) { .v = CR_IntFromString(ent_item->name) }; + P_Ent *ent = P_PushTempEnt(arena, &result.ents); + ent->key = (P_Key) { .v = CR_IntFromString(ent_item->name) }; for (CR_Item *attr = ent_item->first; attr; attr = attr->next) { if (MatchString(attr->name, Lit("props"))) @@ -175,7 +169,7 @@ S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed) tiles_base64.len += PushString(scratch.arena, tile_item->value).len; } } - if (StringLenFromBase64Len(tiles_base64.len) == S_TilesCount) + if (StringLenFromBase64Len(tiles_base64.len) == P_TilesCount) { result.tiles = StringFromBase64(arena, tiles_base64).text; } @@ -183,7 +177,7 @@ S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed) if (!result.tiles) { - result.tiles = PushStructs(arena, u8, S_TilesCount); + result.tiles = PushStructs(arena, u8, P_TilesCount); } EndScratch(scratch); diff --git a/src/pp/pp_transcode.h b/src/pp/pp_transcode.h new file mode 100644 index 00000000..4f4fbea4 --- /dev/null +++ b/src/pp/pp_transcode.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////// +//~ Transcode types + +Enum(P_Tv) +{ + P_Tv_None = 0, + P_Tv_Initial = 1, + + P_Tv_COUNT +}; + +#define P_Tv_Latest (P_Tv_COUNT - 1) + +Struct(P_UnpackedWorld) +{ + P_Tv version; + + u64 seed; + i64 tick; + i64 time_ns; + + P_EntList ents; + u8 *tiles; +}; + +//////////////////////////////////////////////////////////// +//~ Transcode + +String P_PackWorld(Arena *arena, P_World *src_world); +P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed); diff --git a/src/pp/pp_vis/pp_vis.lay b/src/pp/pp_vis/pp_vis.lay index fe4c85df..93799617 100644 --- a/src/pp/pp_vis/pp_vis.lay +++ b/src/pp/pp_vis/pp_vis.lay @@ -4,7 +4,6 @@ //- Dependencies @Dep pp -@Dep pp_sim @Dep sprite @Dep gpu @Dep glyph_cache diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 70d87220..9de15255 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -41,10 +41,10 @@ V_Cmd *V_PushVisCmd(String name) return cmd; } -S_Cmd *V_PushSimCmd(S_CmdKind kind) +P_Cmd *V_PushSimCmd(P_CmdKind kind) { V_Frame *frame = V_CurrentFrame(); - S_CmdNode *n = PushStruct(frame->arena, S_CmdNode); + P_CmdNode *n = PushStruct(frame->arena, P_CmdNode); n->cmd.kind = kind; SllQueuePush(frame->first_sim_cmd_node, frame->last_sim_cmd_node, n); ++frame->sim_cmds_count; @@ -198,7 +198,7 @@ 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_DrawShape(P_Shape shape, Vec4 srgb, i32 detail, V_DrawFlag flags) { if (shape.radius == 0) { @@ -218,7 +218,7 @@ void V_DrawShape(S_Shape shape, Vec4 srgb, i32 detail, V_DrawFlag flags) { f32 rad = ((f32)i / (f32)detail) * Tau; Vec2 dir = Vec2FromAngle(rad); - Vec2 sp = S_SupportPointFromShape(shape, dir).p; + Vec2 sp = P_SupportPointFromShape(shape, dir).p; draw_points.points[i] = sp; } V_DrawPoly(draw_points, srgb, flags); @@ -255,7 +255,7 @@ void V_DrawRect(Rng2 rect, Vec4 srgb, V_DrawFlag flags) void V_DrawPoint(Vec2 p, Vec4 srgb) { - S_Shape ui_shape = S_ShapeFromDesc( + P_Shape ui_shape = P_ShapeFromDesc( .count = 1, .points = { p }, .radius = 5 @@ -326,7 +326,7 @@ void V_TickForever(WaveLaneCtx *lane) Arena *perm = PermArena(); G_ArenaHandle gpu_perm = G_PermArena(); - const i32 world_pitch = S_WorldPitch; + const i32 world_pitch = P_WorldPitch; const f32 zoom_rate = 1.50; const f32 min_zoom = 0.03; const f32 max_zoom = 15.0; @@ -336,17 +336,17 @@ void V_TickForever(WaveLaneCtx *lane) //- Init vis state Arena *sim_debug_arena = AcquireArena(Gibi(64)); - u64 sim_debug_draw_descs_count = 0; - S_DebugDrawDesc *sim_debug_draw_descs = 0; + P_DebugDrawNode *first_sim_debug_draw_node = 0; + P_DebugDrawNode *last_sim_debug_draw_node = 0; Arena *world_arena = AcquireArena(Gibi(64)); - S_World *world = PushStruct(world_arena, S_World); + P_World *world = PushStruct(world_arena, P_World); world->ent_bins_count = Kibi(16); - world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count); - world->tiles = PushStructs(world_arena, u8, S_TilesCount); + world->ent_bins = PushStructs(world_arena, P_EntBin, world->ent_bins_count); + world->tiles = PushStructs(world_arena, u8, P_TilesCount); - Vec2I32 tiles_dims = VEC2I32(S_TilesPitch, S_TilesPitch); - Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * S_WorldPitch, V_CellsPerMeter * S_WorldPitch); + Vec2I32 tiles_dims = VEC2I32(P_TilesPitch, P_TilesPitch); + Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * P_WorldPitch, V_CellsPerMeter * P_WorldPitch); // Init gpu state G_ResourceHandle gpu_state = Zi; @@ -489,6 +489,7 @@ void V_TickForever(WaveLaneCtx *lane) while (!shutdown) { shutdown = Atomic32Fetch(&V.shutdown); + P_tl.debug_draw_enabled = 1; ////////////////////////////// //- Begin frame @@ -532,9 +533,9 @@ void V_TickForever(WaveLaneCtx *lane) frame->dt = SecondsFromNs(frame->dt_ns); frame->rand = last_frame->rand; - if (S_IsKeyNil(V.player_key)) + if (P_IsKeyNil(V.player_key)) { - V.player_key = S_RandKey(); + V.player_key = P_RandKey(); } ////////////////////////////// @@ -633,17 +634,17 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Pop sim output - S_OutputState *sim_output = 0; - LockTicketMutex(&S.output_back_tm); + P_OutputState *sim_output = 0; + LockTicketMutex(&P.sim_output_back_tm); { - sim_output = &S.output_states[S.output_back_idx]; - ++S.output_back_idx; - if (S.output_back_idx >= countof(S.output_states)) + sim_output = &P.sim_output_states[P.sim_output_back_idx]; + ++P.sim_output_back_idx; + if (P.sim_output_back_idx >= countof(P.sim_output_states)) { - S.output_back_idx = 0; + P.sim_output_back_idx = 0; } } - UnlockTicketMutex(&S.output_back_tm); + UnlockTicketMutex(&P.sim_output_back_tm); ////////////////////////////// //- Apply sim snapshots @@ -653,59 +654,45 @@ void V_TickForever(WaveLaneCtx *lane) b32 received_unseen_tick = 0; b32 tiles_dirty = 0; b32 should_clear_particles = 0; - for (S_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next) + for (P_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next) { - S_Snapshot *snapshot = &n->snapshot; + P_Snapshot *snapshot = &n->snapshot; if (snapshot->tick > world->tick) { world->seed = snapshot->seed; world->tick = snapshot->tick; world->time_ns = snapshot->time_ns; - for (S_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next) + for (P_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next) { - S_Delta *delta = &dn->delta; - if (delta->kind == S_DeltaKind_Reset) + P_Delta *delta = &dn->delta; + if (delta->kind == P_DeltaKind_Reset) { tiles_dirty = 1; should_clear_particles = 1; } - if (delta->kind == S_DeltaKind_RawTiles || delta->kind == S_DeltaKind_Tile) + if (delta->kind == P_DeltaKind_RawTiles || delta->kind == P_DeltaKind_Tile) { tiles_dirty = 1; } - S_UpdateWorldFromDelta(world_arena, world, delta); + P_UpdateWorldFromDelta(world_arena, world, delta); } received_unseen_tick = 1; } } - ////////////////////////////// - //- Copy sim debug info - - if (received_unseen_tick) - { - ResetArena(sim_debug_arena); - - // Copy sim debug info - sim_debug_draw_descs_count = sim_output->debug_draw_descs_count; - sim_debug_draw_descs = PushStructsNoZero(sim_debug_arena, S_DebugDrawDesc, sim_debug_draw_descs_count); - CopyStructs(sim_debug_draw_descs, sim_output->debug_draw_descs, sim_debug_draw_descs_count); - } - - // ////////////////////////////// // //- Update tiles from sim // { - // for (S_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next) + // for (P_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next) // { - // S_Snapshot *snapshot = &n->snapshot; + // P_Snapshot *snapshot = &n->snapshot; // if (snapshot->tick > world->tick) // { // for (u64 placement_idx = 0; placement_idx < snapshot->tile_placements_count; ++placement_idx) // { - // S_TilePlacement placement = snapshot->tile_placements[placement_idx]; - // Rng2I32 dirty_rect = S_UpdateTilesInPlaceFromPlacement(tiles, placement); + // P_TilePlacement placement = snapshot->tile_placements[placement_idx]; + // Rng2I32 dirty_rect = P_UpdateTilesInPlaceFromPlacement(tiles, placement); // G_CopyCpuToTexture( // frame->cl, // gpu_tiles, VEC3I32(dirty_rect.p0.x, dirty_rect.p0.y, 0), @@ -723,12 +710,12 @@ void V_TickForever(WaveLaneCtx *lane) // if (sim_output->last_snapshot_node && sim_output->last_snapshot_node->snapshot.tick > world->tick) // { // ResetArena(world_arena); - // world = S_WorldFromSnapshot(world_arena, &sim_output->last_snapshot_node->snapshot); - // V.lookup = S_LookupFromWorld(world_arena, world); + // world = P_WorldFromSnapshot(world_arena, &sim_output->last_snapshot_node->snapshot); + // V.lookup = P_LookupFromWorld(world_arena, world); // // Copy sim debug info // sim_debug_draw_descs_count = sim_output->debug_draw_descs_count; - // sim_debug_draw_descs = PushStructsNoZero(world_arena, S_DebugDrawDesc, sim_debug_draw_descs_count); + // sim_debug_draw_descs = PushStructsNoZero(world_arena, P_DebugDrawDesc, sim_debug_draw_descs_count); // CopyStructs(sim_debug_draw_descs, sim_output->debug_draw_descs, sim_debug_draw_descs_count); // } @@ -860,8 +847,8 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 look_ratio = Zi; look_ratio.y = 0.25; look_ratio.x = look_ratio.y / (16.0 / 9.0); - S_Ent *player = S_EntFromKey(world, V.player_key); - target_camera_pos = S_WorldShapeFromEnt(player).centroid; + P_Ent *player = P_EntFromKey(world, V.player_key); + target_camera_pos = P_WorldShapeFromEnt(player).centroid; target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(player->look, look_ratio)); target_camera_zoom = 1; } @@ -929,7 +916,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->xf.cell_to_world = XformIdentity; { frame->xf.world_to_cell = ScaleXform(frame->xf.world_to_cell, VEC2(V_CellsPerMeter, V_CellsPerMeter)); - frame->xf.world_to_cell = TranslateXform(frame->xf.world_to_cell, VEC2((S_WorldPitch / 2.0), (S_WorldPitch / 2.0))); + frame->xf.world_to_cell = TranslateXform(frame->xf.world_to_cell, VEC2((P_WorldPitch / 2.0), (P_WorldPitch / 2.0))); frame->xf.cell_to_world = InvertXform(frame->xf.world_to_cell); } @@ -938,8 +925,8 @@ void V_TickForever(WaveLaneCtx *lane) frame->xf.world_to_tile = XformIdentity; frame->xf.tile_to_world = XformIdentity; { - frame->xf.world_to_tile = ScaleXform(frame->xf.world_to_tile, VEC2(S_TilesPerMeter, S_TilesPerMeter)); - frame->xf.world_to_tile = TranslateXform(frame->xf.world_to_tile, VEC2((S_WorldPitch / 2.0), (S_WorldPitch / 2.0))); + frame->xf.world_to_tile = ScaleXform(frame->xf.world_to_tile, VEC2(P_TilesPerMeter, P_TilesPerMeter)); + frame->xf.world_to_tile = TranslateXform(frame->xf.world_to_tile, VEC2((P_WorldPitch / 2.0), (P_WorldPitch / 2.0))); frame->xf.tile_to_world = InvertXform(frame->xf.world_to_tile); } @@ -964,12 +951,12 @@ void V_TickForever(WaveLaneCtx *lane) if (m1_held) { frame->is_selecting = 1; - // frame->equipped_tile = S_TileKind_Floor; + // frame->equipped_tile = P_TileKind_Floor; } else if (m2_held) { frame->is_selecting = 1; - // frame->equipped_tile = S_TileKind_Empty; + // frame->equipped_tile = P_TileKind_Empty; } if (frame->is_selecting && last_frame->is_selecting) @@ -1003,8 +990,8 @@ void V_TickForever(WaveLaneCtx *lane) tile_range.p0 = Vec2I32FromVec(FloorVec2(MulXformV2(frame->xf.world_to_tile, last_frame->world_selection.p0))); tile_range.p1 = Vec2I32FromVec(CeilVec2(MulXformV2(frame->xf.world_to_tile, last_frame->world_selection.p1))); - S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta); - cmd->delta.kind = S_DeltaKind_Tile; + P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Delta); + cmd->delta.kind = P_DeltaKind_Tile; cmd->delta.tile_kind = last_frame->equipped_tile; cmd->delta.tile_range = tile_range; } @@ -1013,15 +1000,15 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Query entities - S_Ent *player = S_EntFromKey(world, V.player_key); - S_Ent *hovered_ent = &S_NilEnt; + P_Ent *player = P_EntFromKey(world, V.player_key); + P_Ent *hovered_ent = &P_NilEnt; { // TODO: Real world query - S_Shape cursor_shape = S_ShapeFromDesc(.count = 1, .points = { frame->world_cursor }); - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + P_Shape cursor_shape = P_ShapeFromDesc(.count = 1, .points = { frame->world_cursor }); + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { - S_Shape ent_shape = S_WorldShapeFromEnt(ent); - b32 is_hovered = S_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0; + P_Shape ent_shape = P_WorldShapeFromEnt(ent); + b32 is_hovered = P_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0; if (is_hovered) { hovered_ent = ent; @@ -1551,9 +1538,9 @@ void V_TickForever(WaveLaneCtx *lane) UI_Push(Tag, window->key.v); if (window->is_tile_window) { - for (S_TileKind tile_kind = 0; tile_kind < S_TileKind_COUNT; ++tile_kind) + for (P_TileKind tile_kind = 0; tile_kind < P_TileKind_COUNT; ++tile_kind) { - String name = S_NameFromTileKind(tile_kind); + String name = P_NameFromTileKind(tile_kind); UI_Key key = UI_KeyF("Tile %F", FmtString(name)); UI_BoxReport rep = UI_ReportsFromKey(key).draw; @@ -2232,13 +2219,13 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); Vec2 tile_pos = MulXformV2(frame->xf.world_to_tile, frame->world_cursor); Vec2 cell_pos = MulXformV2(frame->xf.world_to_cell, frame->world_cursor); - i32 tile_idx = S_TileIdxFromTilePos(tile_pos); + i32 tile_idx = P_TileIdxFromTilePos(tile_pos); UI_BuildLabelF("Camera pos: %F", FmtFloat2(frame->camera_pos)); UI_BuildLabelF("Cursor world pos: %F", FmtFloat2(frame->world_cursor)); UI_BuildLabelF("Cursor tile pos: %F", FmtFloat2(tile_pos)); UI_BuildLabelF("Cursor tile idx: %F", FmtSint(tile_idx)); UI_BuildLabelF("Cursor cell pos: %F", FmtFloat2(cell_pos)); - UI_BuildLabelF("Hovered ent: %F", S_FmtKey(hovered_ent->key)); + UI_BuildLabelF("Hovered ent: %F", P_FmtKey(hovered_ent->key)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); { @@ -2511,8 +2498,8 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 player_pos = VEC2(5, 0); if (kind == V_CmdKind_reset_world) { - S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta); - cmd->delta.kind = S_DeltaKind_Reset; + P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Delta); + cmd->delta.kind = P_DeltaKind_Reset; } else { @@ -2520,10 +2507,10 @@ void V_TickForever(WaveLaneCtx *lane) } // Spawn player { - S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta); - cmd->delta.kind = S_DeltaKind_RawEnt; - S_Ent *ent = &cmd->delta.ent; - *ent = S_NilEnt; + P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Delta); + cmd->delta.kind = P_DeltaKind_RawEnt; + P_Ent *ent = &cmd->delta.ent; + *ent = P_NilEnt; ent->key = V.player_key; ent->xf = XformFromPos(player_pos); ent->is_player = 1; @@ -2534,11 +2521,11 @@ void V_TickForever(WaveLaneCtx *lane) case V_CmdKind_spawn_dummy: { - S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta); - cmd->delta.kind = S_DeltaKind_RawEnt; - S_Ent *ent = &cmd->delta.ent; - *ent = S_NilEnt; - ent->key = S_RandKey(); + P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Delta); + cmd->delta.kind = P_DeltaKind_RawEnt; + P_Ent *ent = &cmd->delta.ent; + *ent = P_NilEnt; + ent->key = P_RandKey(); ent->xf = XformFromPos(frame->world_cursor); ent->is_player = 1; ent->has_weapon = 1; @@ -2549,9 +2536,9 @@ void V_TickForever(WaveLaneCtx *lane) { if (hovered_ent->valid) { - S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Delta); - cmd->delta.kind = S_DeltaKind_RawEnt; - S_Ent *ent = &cmd->delta.ent; + P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Delta); + cmd->delta.kind = P_DeltaKind_RawEnt; + P_Ent *ent = &cmd->delta.ent; ent->key = hovered_ent->key; ent->exists = 0; } @@ -2561,7 +2548,7 @@ void V_TickForever(WaveLaneCtx *lane) { if (frame->is_editing) { - S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Save); + P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Save); } } break; @@ -2587,7 +2574,7 @@ void V_TickForever(WaveLaneCtx *lane) f32 fire_presses = fire_held && !last_frame->held_buttons[Button_M1]; Vec2 look = Zi; { - Vec2 center = S_WorldShapeFromEnt(player).centroid; + Vec2 center = P_WorldShapeFromEnt(player).centroid; look = SubVec2(frame->world_cursor, center); } if (frame->is_editing) @@ -2609,7 +2596,7 @@ void V_TickForever(WaveLaneCtx *lane) // Push control cmd { - S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Control); + P_Cmd *cmd = V_PushSimCmd(P_CmdKind_Control); cmd->target = V.player_key; cmd->move = frame->move; cmd->look = frame->look; @@ -2620,18 +2607,18 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Submit sim commands - LockTicketMutex(&S.input_back_tm); + LockTicketMutex(&P.sim_input_back_tm); { - S_InputState *v2s = &S.input_states[S.input_back_idx]; - for (S_CmdNode *src = frame->first_sim_cmd_node; src; src = src->next) + P_InputState *v2s = &P.sim_input_states[P.sim_input_back_idx]; + for (P_CmdNode *src = frame->first_sim_cmd_node; src; src = src->next) { - S_CmdNode *cmd_node = PushStruct(v2s->arena, S_CmdNode); + P_CmdNode *cmd_node = PushStruct(v2s->arena, P_CmdNode); cmd_node->cmd = src->cmd; SllQueuePush(v2s->first_cmd_node, v2s->last_cmd_node, cmd_node); ++v2s->cmds_count; } } - UnlockTicketMutex(&S.input_back_tm); + UnlockTicketMutex(&P.sim_input_back_tm); @@ -2648,7 +2635,7 @@ void V_TickForever(WaveLaneCtx *lane) - for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet)) + for (P_Ent *bullet = P_FirstEnt(world); bullet->valid; bullet = P_NextEnt(bullet)) { if (bullet->is_bullet) { @@ -2828,7 +2815,7 @@ void V_TickForever(WaveLaneCtx *lane) if (0) { - for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet)) + for (P_Ent *bullet = P_FirstEnt(world); bullet->valid; bullet = P_NextEnt(bullet)) { if (bullet->is_bullet && bullet->has_hit) { @@ -2905,29 +2892,29 @@ void V_TickForever(WaveLaneCtx *lane) // { - // for (S_Ent *firer = S_FirstEnt(world); firer->valid; firer = S_NextEnt(firer)) + // for (P_Ent *firer = P_FirstEnt(world); firer->valid; firer = P_NextEnt(firer)) // { // if (firer->fire_held) // { // Xform firer_xf = firer->xf; - // S_Shape firer_world_shape = S_MulXformShape(firer_xf, firer->local_shape); + // P_Shape firer_world_shape = P_MulXformShape(firer_xf, firer->local_shape); // Vec2 ray_start = firer_world_shape.centroid; // Vec2 ray_dir = firer->look; // // TODO: Real raycast query - // S_Ent *closest_victim = &S_NilEnt; - // S_RaycastResult victim_raycast = Zi; + // P_Ent *closest_victim = &P_NilEnt; + // P_RaycastResult victim_raycast = Zi; // { // f32 closest_len_sq = Inf; - // for (S_Ent *victim = S_FirstEnt(world); victim->valid; victim = S_NextEnt(victim)) + // for (P_Ent *victim = P_FirstEnt(world); victim->valid; victim = P_NextEnt(victim)) // { // if (victim != firer) // { // Xform victim_xf = victim->xf; - // S_Shape victim_world_shape = S_MulXformShape(victim_xf, victim->local_shape); + // P_Shape victim_world_shape = P_MulXformShape(victim_xf, victim->local_shape); - // S_RaycastResult raycast = S_RaycastShape(victim_world_shape, ray_start, ray_dir); + // P_RaycastResult raycast = P_RaycastShape(victim_world_shape, ray_start, ray_dir); // if (raycast.is_intersecting) // { // f32 len_sq = Vec2LenSq(SubVec2(raycast.p, ray_start)); @@ -2990,8 +2977,8 @@ void V_TickForever(WaveLaneCtx *lane) // } - // // for (S_QueryResult query = S_FirstRaycast(wrold, ray_start, ray_dir); query. - // // S_RaycastWorldResult hits = S_RaycastWorld(world, ray_start, ray_dir) + // // for (P_QueryResult query = P_FirstRaycast(wrold, ray_start, ray_dir); query. + // // P_RaycastWorldResult hits = P_RaycastWorld(world, ray_start, ray_dir) // // { // // } // } @@ -3027,14 +3014,98 @@ void V_TickForever(WaveLaneCtx *lane) V_PushParticles(emitter); } + ////////////////////////////// + //- Debug draw + + { + // Copy debug draw data from sim + if (received_unseen_tick) + { + ResetArena(sim_debug_arena); + first_sim_debug_draw_node = 0; + last_sim_debug_draw_node = 0; + { + i64 dst_idx = 0; + P_DebugDrawNode *dst_nodes = PushStructsNoZero(sim_debug_arena, P_DebugDrawNode, sim_output->debug_draw_nodes_count); + for (P_DebugDrawNode *src = sim_output->first_debug_draw_node; src; src = src->next) + { + P_DebugDrawNode *dst = &dst_nodes[dst_idx]; + *dst = *src; + dst_idx += 1; + SllQueuePush(first_sim_debug_draw_node, last_sim_debug_draw_node, dst); + } + } + } + + // Merge vis debug draws with sim debug draws + P_DebugDrawNode *first_debug_draw_node = first_sim_debug_draw_node; + P_DebugDrawNode *last_debug_draw_node = last_sim_debug_draw_node; + if (P_tl.first_debug_draw_node) + { + if (last_debug_draw_node) + { + last_debug_draw_node->next = P_tl.first_debug_draw_node; + } + else + { + first_debug_draw_node = P_tl.first_debug_draw_node; + } + last_debug_draw_node = P_tl.last_debug_draw_node; + } + + for (P_DebugDrawNode *n = first_debug_draw_node; n; n = n->next) + { + Vec4 color = Vec4FromU32(n->srgb32); + i32 detail = 24; + f32 radius = 5; + switch(n->kind) + { + case P_DebugDrawKind_Point: + { + Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, n->point.p); + V_DrawPoint(ui_p, color); + } break; + + case P_DebugDrawKind_Line: + { + Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, n->line.p0); + Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, n->line.p1); + V_DrawLine(ui_p0, ui_p1, color); + } break; + + case P_DebugDrawKind_Rect: + { + Rng2 ui_rect = Zi; + ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, n->rect.p0); + ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, n->rect.p1); + V_DrawRect(ui_rect, color, V_DrawFlag_Line); + } break; + + case P_DebugDrawKind_Shape: + { + P_Shape ui_shape = P_MulXformShape(frame->xf.world_to_ui, n->shape); + V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line); + // V_DrawShape(ui_shape, color, detail, V_DrawFlag_None); + } break; + } + } + + // Reset vis debug draws + ResetArena(P_tl.debug_arena); + P_tl.first_debug_draw_node = 0; + P_tl.last_debug_draw_node = 0; + P_tl.debug_draw_nodes_count = 0; + } + + ////////////////////////////// //- Draw entities - // for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + // for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) // { // Xform ent_to_world_xf = ent->xf; // Xform ent_to_draw_xf = MulXform(frame->xf.world_to_draw, ent_to_world_xf); - // S_Shape draw_shape = S_MulXformShape(ent_to_draw_xf, ent->local_shape); + // P_Shape draw_shape = P_MulXformShape(ent_to_draw_xf, ent->local_shape); // f32 opacity = 0.5; @@ -3056,7 +3127,7 @@ void V_TickForever(WaveLaneCtx *lane) // // color.w *= opacity; // // f32 width = 0.1; // // f32 height = 0.75; - // // S_Shape local_shape = S_ShapeFromDesc( + // // P_Shape local_shape = P_ShapeFromDesc( // // .count = 4, // // .points = { // // VEC2(-width / 2, -height), VEC2(width / 2, -height), @@ -3065,7 +3136,7 @@ void V_TickForever(WaveLaneCtx *lane) // // ); // // 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); + // // P_Shape shape = P_MulXformShape(xf, local_shape); // // V_DrawShape(shape, color, 10, V_DrawFlag_Line); // // } @@ -3073,53 +3144,12 @@ void V_TickForever(WaveLaneCtx *lane) // // { // // Vec4 color = Color_Orange; // // color.w *= opacity; - // // Rng2 bb = S_BoundingBoxFromShape(draw_shape); + // // Rng2 bb = P_BoundingBoxFromShape(draw_shape); // // V_DrawRect(bb, color, V_DrawFlag_Line); // // } // } // } - ////////////////////////////// - //- Draw sim debug shapes - - for (u64 desc_idx = 0; desc_idx < sim_debug_draw_descs_count; ++desc_idx) - { - S_DebugDrawDesc *desc = &sim_debug_draw_descs[desc_idx]; - Vec4 color = Vec4FromU32(desc->srgb32); - i32 detail = 24; - f32 radius = 5; - switch(desc->kind) - { - case S_DebugDrawKind_Point: - { - Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, desc->point.p); - V_DrawPoint(ui_p, color); - } break; - - case S_DebugDrawKind_Line: - { - Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, desc->line.p0); - Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, desc->line.p1); - V_DrawLine(ui_p0, ui_p1, color); - } break; - - case S_DebugDrawKind_Rect: - { - Rng2 ui_rect = Zi; - ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, desc->rect.p0); - ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, desc->rect.p1); - V_DrawRect(ui_rect, color, V_DrawFlag_Line); - } break; - - case S_DebugDrawKind_Shape: - { - S_Shape ui_shape = S_MulXformShape(frame->xf.world_to_ui, desc->shape); - V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line); - // V_DrawShape(ui_shape, color, detail, V_DrawFlag_None); - } break; - } - } - ////////////////////////////// //- Render @@ -3207,9 +3237,9 @@ void V_TickForever(WaveLaneCtx *lane) // Fill tile textures { - for (S_TileKind tile_kind = 0; tile_kind < S_TileKind_COUNT; ++tile_kind) + for (P_TileKind tile_kind = 0; tile_kind < P_TileKind_COUNT; ++tile_kind) { - String tile_name = S_TileNameFromKind(tile_kind); + String tile_name = P_TileNameFromKind(tile_kind); String sheet_name = StringF(frame->arena, "tile/%F.ase", FmtString(tile_name)); ResourceKey sheet_resource = ResourceKeyFromStore(&P_Resources, sheet_name); SPR_SheetKey sheet = SPR_SheetKeyFromResource(sheet_resource); @@ -3343,8 +3373,8 @@ void V_TickForever(WaveLaneCtx *lane) { i64 ents_to_prune_count = 0; - S_Ent **ents_to_prune = PushStructsNoZero(frame->arena, S_Ent *, world->ents_count); - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + P_Ent **ents_to_prune = PushStructsNoZero(frame->arena, P_Ent *, world->ents_count); + for (P_Ent *ent = P_FirstEnt(world); ent->valid; ent = P_NextEnt(ent)) { if (ent->exists <= 0) { @@ -3356,8 +3386,8 @@ void V_TickForever(WaveLaneCtx *lane) for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx) { // FIXME: Add to free list - S_Ent *ent = ents_to_prune[prune_idx]; - S_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count]; + P_Ent *ent = ents_to_prune[prune_idx]; + P_EntBin *bin = &world->ent_bins[ent->key.v % world->ent_bins_count]; DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); DllQueueRemove(world->first_ent, world->last_ent, ent); world->ents_count -= 1; diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index d29889df..f745678c 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -234,7 +234,7 @@ Struct(V_Frame) // Editor state V_EditMode edit_mode; - S_TileKind equipped_tile; + P_TileKind equipped_tile; // Editor b32 is_selecting; @@ -273,8 +273,8 @@ Struct(V_Frame) // Sim cmds u64 sim_cmds_count; - S_CmdNode *first_sim_cmd_node; - S_CmdNode *last_sim_cmd_node; + P_CmdNode *first_sim_cmd_node; + P_CmdNode *last_sim_cmd_node; // Emitters i64 emitters_count; @@ -284,7 +284,7 @@ Struct(V_Frame) Struct(V_Ctx) { - S_Key player_key; + P_Key player_key; i64 panels_count; i64 windows_count; @@ -315,7 +315,7 @@ void V_Shutdown(void); V_Frame *V_CurrentFrame(void); V_Frame *V_LastFrame(void); V_Cmd *V_PushVisCmd(String name); -S_Cmd *V_PushSimCmd(S_CmdKind kind); +P_Cmd *V_PushSimCmd(P_CmdKind kind); String V_StringFromHotkey(Arena *arena, V_Hotkey hotkey); void V_PushParticles(V_Emitter src); @@ -323,7 +323,7 @@ void V_PushParticles(V_Emitter src); //~ Draw helpers 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_DrawShape(P_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); void V_DrawPoint(Vec2 p, Vec4 srgb); diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g index 13077b73..7e919a61 100644 --- a/src/pp/pp_vis/pp_vis_gpu.g +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -69,7 +69,7 @@ ComputeShader2D(V_BackdropCS, 8, 8) { V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; RWTexture2D target = G_Dereference(params.target_rw); - Texture2D tiles = G_Dereference(params.tiles); + Texture2D tiles = G_Dereference(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)); @@ -82,10 +82,10 @@ ComputeShader2D(V_BackdropCS, 8, 8) 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)); - S_TileKind tile = tiles.Load(Vec3(tile_pos, 0)); + P_TileKind tile = tiles.Load(Vec3(tile_pos, 0)); f32 half_thickness = 1; - f32 half_bounds_size = S_WorldPitch * 0.5; + 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) && @@ -125,17 +125,17 @@ ComputeShader2D(V_BackdropCS, 8, 8) // Tile test // TODO: Remove this { - S_TileKind tile_tl = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y - 1, 0)); - S_TileKind tile_tr = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y - 1, 0)); - S_TileKind tile_br = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y + 1, 0)); - S_TileKind tile_bl = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y + 1, 0)); - S_TileKind tile_t = tiles.Load(Vec3(tile_pos.x, tile_pos.y - 1, 0)); - S_TileKind tile_r = tiles.Load(Vec3(tile_pos.x + 1, tile_pos.y, 0)); - S_TileKind tile_b = tiles.Load(Vec3(tile_pos.x, tile_pos.y + 1, 0)); - S_TileKind tile_l = tiles.Load(Vec3(tile_pos.x - 1, tile_pos.y, 0)); + 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; - S_TileKind edge_tile = tile; + 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)))); } @@ -145,14 +145,14 @@ ComputeShader2D(V_BackdropCS, 8, 8) 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 == S_TileKind_Wall) + if (tile == P_TileKind_Wall) { Vec4 outer = LinearFromSrgb(Vec4(0.05, 0.05, 0.05, 1)); Vec4 inner = LinearFromSrgb(Vec4(0.10, 0.10, 0.10, 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 != S_TileKind_Empty) + else if (tile != P_TileKind_Empty) { SamplerState wrap_sampler = G_Dereference(params.pt_wrap_sampler); SPR_Slice slice = params.tile_slices[tile]; @@ -166,12 +166,12 @@ ComputeShader2D(V_BackdropCS, 8, 8) // { // default: break; - // case S_TileKind_Floor: + // case P_TileKind_Floor: // { // result = Color_Blue; // } break; - // case S_TileKind_Wall: + // case P_TileKind_Wall: // { // // result = Color_Red; // result = Color_Black; @@ -517,10 +517,10 @@ PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input) 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)); - S_TileKind equipped_tile = params.equipped_tile; + P_TileKind equipped_tile = params.equipped_tile; f32 half_thickness = 1; - f32 half_bounds_size = S_WorldPitch * 0.5; + 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) && @@ -558,10 +558,10 @@ PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input) // else { if ( - world_pos.x > -(S_WorldPitch / 2) && - world_pos.y > -(S_WorldPitch / 2) && - world_pos.x < (S_WorldPitch / 2) && - world_pos.y < (S_WorldPitch / 2) && + 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 && diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index cc1e5c5f..5c079bfd 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -71,7 +71,7 @@ Struct(V_GpuParams) G_SamplerStateRef pt_wrap_sampler; V_SelectionMode selection_mode; - S_TileKind equipped_tile; + P_TileKind equipped_tile; b32 has_mouse_focus; b32 has_keyboard_focus; @@ -99,7 +99,7 @@ Struct(V_GpuParams) G_RWTexture2DRef stains; G_RWTexture2DRef drynesses; - SPR_Slice tile_slices[S_TileKind_COUNT]; + SPR_Slice tile_slices[P_TileKind_COUNT]; }; //////////////////////////////////////////////////////////// diff --git a/src/sprite/sprite.c b/src/sprite/sprite.c index 4f3ad62d..8768a501 100644 --- a/src/sprite/sprite.c +++ b/src/sprite/sprite.c @@ -92,6 +92,7 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet, String slice_name) SPR_CmdNode *n = SPR.submit.first_free; if (n) { + SllStackPop(SPR.submit.first_free); ZeroStruct(n); } else