diff --git a/readme.md b/readme.md index 7dc1b9a5..5ab70425 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ Run `build.bat` from a terminal with MSVC/Clang available. ### Codebase structure --- -This codebase is organized into a series of layers with acyclic dependencies, denoted by the file tree and by namespacing in source code. The `.lay` files that exist alongside each layer specify these dependencies along with things like source files, CPU/GPU linkage information, and binary assets to embed with the layer. These files are parsed by the metaprogram (controlled by build.bat) in order to assemble the final executable. The base layer is the only layer that doesn't adhere to these rules, so that the metaprogram can itself depend on it. +This codebase is organized into a series of layers with acyclic dependencies, denoted by the file tree and by namespacing in source code. The `.lay` files that exist alongside each layer specify these dependencies along with things like source files, CPU/GPU linkage information, and binary assets to embed with the layer. These files are parsed by the metaprogram in order to assemble the final executable. The base layer is the only layer that doesn't adhere to these rules, so that the metaprogram can itself depend on it. Other than `.lay` files, there are three types of source files present: * `.c`/`.h` C code @@ -14,4 +14,4 @@ Other than `.lay` files, there are three types of source files present: ### Media --- -For project media, see https://projects.cagori.com/#power-play \ No newline at end of file +For project media, see https://projects.cagori.com/#power-play diff --git a/src/collider/collider.c b/src/collider/collider.c deleted file mode 100644 index 284722e5..00000000 --- a/src/collider/collider.c +++ /dev/null @@ -1,1112 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Debug helpers - -#if COLLIDER_DEBUG -void CLD_DebugBreakable(void) -{ -#if IsRtcEnabled - DEBUGBREAKABLE; -#endif -} -#endif - -//////////////////////////////////////////////////////////// -//~ Shape - -CLD_Shape CLD_ShapeFromQuad(Quad quad) -{ - CLD_Shape result; - result.points[0] = quad.p0; - result.points[1] = quad.p1; - result.points[2] = quad.p2; - result.points[3] = quad.p3; - result.count = 4; - result.radius = 0; - return result; -} - -//////////////////////////////////////////////////////////// -//~ Menkowski support point - -CLD_SupportPoint CLD_SupportPointFromDirEx(CLD_Shape *shape, Affine af, Vec2 dir, i32 ignore) -{ - Vec2 *points = shape->points; - u32 count = shape->count; - f32 radius = shape->radius; - - dir = RotateVec2(dir, -RotationFromAffine(af)); - dir = MulVec2Vec2(dir, ScaleFromAffine(af)); - - if (count == 1) - { - // Skip 'ignore' on single point colliders - ignore = -1; - } - - Vec2 furthest = Zi; - u32 furthest_index = 0; - f32 furthest_dot = -F32Infinity; - - for (u32 i = 0; i < count; ++i) - { - if ((i32)i == ignore) - { - continue; - } - Vec2 p = points[i]; - f32 dot = DotVec2(dir, p); - if (dot > furthest_dot) - { - furthest = p; - furthest_dot = dot; - furthest_index = i; - } - } - - if (radius > 0.0) - { - dir = Vec2WithLen(dir, radius); - furthest = AddVec2(furthest, dir); - } - - furthest = MulAffineVec2(af, furthest); - - CLD_SupportPoint result; - result.p = furthest; - result.i = furthest_index; - return result; -} - -CLD_SupportPoint CLD_SupportPointFromDir(CLD_Shape *shape, Affine af, Vec2 dir) -{ - return CLD_SupportPointFromDirEx(shape, af, dir, -1); -} - -CLD_MenkowskiPoint CLD_MenkowskiPointFromDir(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, Vec2 dir) -{ - CLD_MenkowskiPoint result; - result.s0 = CLD_SupportPointFromDir(shape0, af0, dir); - result.s1 = CLD_SupportPointFromDir(shape1, af1, NegVec2(dir)); - result.p = SubVec2(result.s0.p, result.s1.p); - return result; -} - -//////////////////////////////////////////////////////////// -//~ Aabb - -Aabb CLD_AabbFromShape(CLD_Shape *shape, Affine af) -{ - Aabb result; - result.p0.x = CLD_SupportPointFromDir(shape, af, VEC2(-1, 0)).p.x - CLD_CollisionTolerance; - result.p0.y = CLD_SupportPointFromDir(shape, af, VEC2(0, -1)).p.y - CLD_CollisionTolerance; - result.p1.x = CLD_SupportPointFromDir(shape, af, VEC2(1, 0)).p.x + CLD_CollisionTolerance; - result.p1.y = CLD_SupportPointFromDir(shape, af, VEC2(0, 1)).p.y + CLD_CollisionTolerance; - return result; -} - -Aabb CLD_CombineAabb(Aabb b0, Aabb b1) -{ - Aabb result; - result.p0.x = MinF32(MinF32(b0.p0.x, b0.p1.x), MinF32(b1.p0.x, b1.p1.x)); - result.p0.y = MinF32(MinF32(b0.p0.y, b0.p1.y), MinF32(b1.p0.y, b1.p1.y)); - result.p1.x = MaxF32(MaxF32(b0.p0.x, b0.p1.x), MaxF32(b1.p0.x, b1.p1.x)); - result.p1.y = MaxF32(MaxF32(b0.p0.y, b0.p1.y), MaxF32(b1.p0.y, b1.p1.y)); - return result; -} - -b32 CLD_TestAabb(Aabb box0, Aabb box1) -{ - f32 b0_x0 = box0.p0.x; - f32 b0_x1 = box0.p1.x; - f32 b1_x0 = box1.p0.x; - f32 b1_x1 = box1.p1.x; - f32 b0_y0 = box0.p0.y; - f32 b0_y1 = box0.p1.y; - f32 b1_y0 = box1.p0.y; - f32 b1_y1 = box1.p1.y; - return ((b0_x0 >= b1_x0 && b0_x0 <= b1_x1) || (b0_x1 >= b1_x0 && b0_x1 <= b1_x1) || (b1_x0 >= b0_x0 && b1_x0 <= b0_x1) || (b1_x1 >= b0_x0 && b1_x1 <= b0_x1)) && - ((b0_y0 >= b1_y0 && b0_y0 <= b1_y1) || (b0_y1 >= b1_y0 && b0_y1 <= b1_y1) || (b1_y0 >= b0_y0 && b1_y0 <= b0_y1) || (b1_y1 >= b0_y0 && b1_y1 <= b0_y1)); -} - -//////////////////////////////////////////////////////////// -//~ Gjk - -// -// Determine simplex in menkowksi difference that encapsulates origin if shapes -// overlap, or closest edge / point to origin on CLD_Menkowski difference if they -// do not. -// - -#if COLLIDER_DEBUG -CLD_GjkData CLD_GjkDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, f32 min_unique_pt_dist_sq, u32 dbg_step) -#else -CLD_GjkData CLD_GjkDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, f32 min_unique_pt_dist_sq) -#endif -{ - b32 overlapping = 0; - CLD_MenkowskiSimplex s = Zi; - Vec2 dir = Zi; - CLD_MenkowskiPoint m = Zi; - - // First point is support point in shape's general directions to eachother - dir = SubVec2(af1.og, af0.og); - if (IsVec2Zero(dir)) dir = VEC2(1, 0); - s.a = CLD_MenkowskiPointFromDir(shape0, shape1, af0, af1, dir); - s.len = 1; - - Vec2 removed_a = Zi; - Vec2 removed_b = Zi; - u32 num_removed = 0; - for (;;) - { - ////////////////////////////// - //- Find initial points in simplex - - if (s.len == 1) - { - // Second point is support point towards origin - dir = NegVec2(s.a.p); - - CLD_DBGSTEP; - m = CLD_MenkowskiPointFromDir(shape0, shape1, af0, af1, dir); - // Check that new point is far enough away from existing point - if (Vec2LenSq(SubVec2(m.p, s.a.p)) < min_unique_pt_dist_sq) - { - overlapping = 0; - break; - } - s.b = s.a; - s.a = m; - s.len = 2; - - // Third point is support point in direction of line normal towards origin - dir = PerpVec2TowardsDir(SubVec2(s.b.p, s.a.p), NegVec2(s.a.p)); - } - - ////////////////////////////// - //- Find third point in simplex - - { - CLD_DBGSTEP; - m = CLD_MenkowskiPointFromDir(shape0, shape1, af0, af1, dir); - // Check that new point is far enough away from existing points - if ( - Vec2LenSq(SubVec2(m.p, s.a.p)) < min_unique_pt_dist_sq || - Vec2LenSq(SubVec2(m.p, s.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(s.b.p, s.a.p), SubVec2(m.p, s.a.p))) < min_unique_pt_dist_sq - ) - { - overlapping = 0; - break; - } - s.c = s.b; - s.b = s.a; - s.a = m; - s.len = 3; - - if ( - (AbsF32(WedgeVec2(SubVec2(s.b.p, s.a.p), NegVec2(s.a.p))) <= min_unique_pt_dist_sq) || - (AbsF32(WedgeVec2(SubVec2(s.c.p, s.b.p), NegVec2(s.b.p))) <= min_unique_pt_dist_sq) || - (AbsF32(WedgeVec2(SubVec2(s.c.p, s.a.p), NegVec2(s.a.p))) <= min_unique_pt_dist_sq) - ) - { - // Simplex lies on origin - overlapping = 1; - break; - } - } - - ////////////////////////////// - //- Determine origin region - - CLD_DBGSTEP; - Vec2 vab = SubVec2(s.b.p, s.a.p); - Vec2 vac = SubVec2(s.c.p, s.a.p); - Vec2 vbc = SubVec2(s.c.p, s.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(s.a.p)); - f32 rac_dot = DotVec2(rac_dir, NegVec2(s.a.p)); - f32 rbc_dot = DotVec2(rbc_dir, NegVec2(s.b.p)); - - f32 vab_dot = DotVec2(vab, NegVec2(s.a.p)) / Vec2LenSq(vab); - f32 vac_dot = DotVec2(vac, NegVec2(s.a.p)) / Vec2LenSq(vac); - f32 vbc_dot = DotVec2(vbc, NegVec2(s.b.p)) / Vec2LenSq(vbc); - - if (rab_dot >= 0 && vab_dot >= 0 && vab_dot <= 1) - { - // Region ab, remove c - num_removed = 1; - removed_a = s.c.p; - s.len = 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 = s.b.p; - s.len = 2; - s.b = s.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 = s.a.p; - s.len = 2; - s.a = s.b; - s.b = s.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 = s.b.p; - removed_b = s.c.p; - s.len = 1; - } - else if (vab_dot >= 1 && vbc_dot <= 0) - { - // Region b, remove ac - num_removed = 2; - removed_a = s.a.p; - removed_b = s.c.p; - s.len = 1; - s.a = s.b; - } - else if (vac_dot >= 1 && vbc_dot >= 1) - { - // Region c, remove ab - num_removed = 2; - removed_a = s.a.p; - removed_b = s.b.p; - s.len = 1; - s.a = s.c; - } - else - { - // No region, must be in simplex - overlapping = 1; - break; - } - } - -#if COLLIDER_DEBUG - abort: -#endif - - CLD_GjkData result = { - .simplex = s, - .overlapping = overlapping, - .final_dir = dir, -#if COLLIDER_DEBUG - .dbg_step = dbg_step -#endif - }; - - return result; -} - -//////////////////////////////////////////////////////////// -//~ Epa - -// Expands upon result of GJK computation to determine collision normal & -// closest edge when shapes are overlapping - -#if COLLIDER_DEBUG -CLD_EpaData CLD_EpaDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, CLD_GjkData gjk_result, f32 min_unique_pt_dist_sq, u32 max_iterations, u32 dbg_step) -#else -CLD_EpaData CLD_EpaDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, CLD_GjkData gjk_result, f32 min_unique_pt_dist_sq, u32 max_iterations) -#endif -{ - TempArena scratch = BeginScratchNoConflict(); - - CLD_MenkowskiFeature closest_feature = Zi; - Vec2 normal = Zi; - - CLD_MenkowskiPoint *proto = 0; - u32 proto_count = 0; - if (gjk_result.overlapping) - { - CLD_MenkowskiSimplex s = gjk_result.simplex; - proto = ArenaNext(scratch.arena, CLD_MenkowskiPoint); - { - Assert(s.len == 3); - CLD_MenkowskiPoint *tmp = PushStructsNoZero(scratch.arena, CLD_MenkowskiPoint, 3); - tmp[0] = s.a; - tmp[1] = s.b; - tmp[2] = s.c; - proto_count = 3; - } - - i32 winding = WindingFromVec2(SubVec2(s.c.p, s.a.p), SubVec2(s.b.p, s.a.p)); - - u32 epa_iterations = 0; - for (;;) - { - ++epa_iterations; - - // Find dir from origin to closest edge - // FIXME: Winding order of ps & pe index - f32 closest_len_sq = F32Infinity; - CLD_MenkowskiPoint closest_a = Zi; - CLD_MenkowskiPoint closest_b = Zi; - u32 closest_b_index = 0; - for (u32 i = 0; i < proto_count; ++i) - { - u32 a_index = i; - u32 b_index = (i < proto_count - 1) ? (i + 1) : 0; - CLD_MenkowskiPoint a = proto[a_index]; - CLD_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); - CLD_MenkowskiPoint m = CLD_MenkowskiPointFromDir(shape0, shape1, af0, af1, dir); - - #if COLLIDER_DEBUG - { - // If debug step count is reached, we still want to inspect the normal at the step - normal = NormVec2(dir); - closest_feature.a = closest_a; - closest_feature.b = closest_b; - closest_feature.len = 2; - } -#endif - - // Check validity of new point - CLD_DBGSTEP; - { - b32 valid = 1; - - { - // NOTE: Changing this value affects how stable normals are for circular 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.len = 2; - break; - } - } - - // Expand prototype - PushStructNoZero(scratch.arena, CLD_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; - } - } - else - { - normal = NormVec2(gjk_result.final_dir); - closest_feature.len = gjk_result.simplex.len; - closest_feature.a = gjk_result.simplex.a; - closest_feature.b = gjk_result.simplex.b; - } - -#if COLLIDER_DEBUG - abort: -#endif - - CLD_EpaData result = { - .normal = normal, - .closest_feature = closest_feature - }; - -#if COLLIDER_DEBUG - result.dbg_step = dbg_step; - u32 len = MinU32(proto_count, countof(result.prototype.points)); - for (u32 i = 0; i < len; ++i) - { - result.prototype.points[i] = proto[i].p; - } - result.prototype.len = len; -#endif - - EndScratch(scratch); - return result; -} - -//////////////////////////////////////////////////////////// -//~ Clipping - -CLD_ClippedLine CLD_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); - } - - CLD_ClippedLine result; - 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 CLD_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 t; - { - f32 w = 1 / vab_w; - t = ClampF32(vap_w * w, 0, 1); - } - - Vec2 result = AddVec2(a, MulVec2(vab, t)); - return result; -} - -//////////////////////////////////////////////////////////// -//~ Collision points - -CLD_CollisionData CLD_CollisionDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1) -{ - CLD_CollisionData result = Zi; - - const f32 tolerance = CLD_CollisionTolerance; - const f32 min_unique_pt_dist_sq = CLD_MinUniquePtDistSq; - const u32 max_epa_iterations = CLD_MaxEpaIterations; - - CLD_CollisionPoint points[2] = Zi; - u32 num_points = 0; - b32 colliding = 0; - Vec2 normal = Zi; - -#if COLLIDER_DEBUG - u32 dbg_step = 0; -#endif - - CLD_GjkData gjk_result = Zi; - CLD_EpaData epa_result = Zi; - - // Run GJK -#if COLLIDER_DEBUG - gjk_result = CLD_GjkDataFromShapes(shape0, shape1, af0, af1, min_unique_pt_dist_sq, dbg_step); - dbg_step = gjk_result.dbg_step; - #else - gjk_result = CLD_GjkDataFromShapes(shape0, shape1, af0, af1, min_unique_pt_dist_sq); -#endif - CLD_DBGSTEP; - - // Run EPA -#if COLLIDER_DEBUG - epa_result = CLD_EpaDataFromShapes(shape0, shape1, af0, af1, gjk_result, min_unique_pt_dist_sq, max_epa_iterations, dbg_step); - dbg_step = epa_result.dbg_step; - #else - epa_result = CLD_EpaDataFromShapes(shape0, shape1, af0, af1, gjk_result, min_unique_pt_dist_sq, max_epa_iterations); -#endif - normal = epa_result.normal; - CLD_DBGSTEP; - - // Determine collision - if (gjk_result.overlapping) - { - colliding = 1; - } - else - { - CLD_MenkowskiFeature f = epa_result.closest_feature; - // Shapes not overlapping, determine if distance between shapes within tolerance - if (f.len == 1) - { - Vec2 p = NegVec2(f.a.p); - if (Vec2LenSq(p) <= (tolerance * tolerance)) - { - colliding = 1; - } - } - else - { - // Project origin to determine if distance is within tolerance. - Assert(f.len == 2); - Vec2 vab = SubVec2(f.b.p, f.a.p); - Vec2 vao = NegVec2(f.a.p); - f32 ratio = ClampF32(DotVec2(vab, vao) / DotVec2(vab, vab), 0, 1); - Vec2 p = AddVec2(f.a.p, MulVec2(vab, ratio)); - if (Vec2LenSq(p) <= (tolerance * tolerance)) - { - colliding = 1; - } - } - } - - // Clip to determine final points - if (colliding) - { - // Max vertices must be < 16 to fit in 4 bit ids - StaticAssert(countof(shape0->points) <= 16); - - CLD_MenkowskiFeature f = epa_result.closest_feature; - - { - b32 collapse0 = 0; - b32 collapse1 = 0; - - CLD_SupportPoint a0 = f.a.s0; - CLD_SupportPoint a1 = f.a.s1; - CLD_SupportPoint b0 = f.b.s0; - CLD_SupportPoint b1 = f.b.s1; - // FIXME: Manually account for shapes w/ 1 & 2 points - if (f.len == 2) - { - if (a0.i == b0.i) - { - if (shape0->count > 1) - { - b0 = CLD_SupportPointFromDirEx(shape0, af0, normal, b0.i); - } - else - { - collapse0 = 1; - b0 = a0; - } - } - if (a1.i == b1.i) - { - if (shape1->count > 1) - { - b1 = CLD_SupportPointFromDirEx(shape1, af1, NegVec2(normal), b1.i); - } - 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) - { - CLD_SupportPoint tmp = a0; - a0 = b0; - b0 = tmp; - vab0 = NegVec2(vab0); - } - if (WedgeVec2(normal, vab1) < 0) - { - CLD_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 = F32Infinity; - f32 b_sep = F32Infinity; - Vec2 a_midpoint = Zi; - Vec2 b_midpoint = Zi; - b32 ignore_a = 1; - b32 ignore_b = 1; - if (!collapse0 && !collapse1) - { - // Clip line to line - CLD_ClippedLine clip_result = CLD_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; - } - } - result.a0_clipped = a0_clipped; - result.a1_clipped = a1_clipped; - result.b0_clipped = b0_clipped; - result.b1_clipped = b1_clipped; - } - 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 = CLD_ClipPointToLine(a0.p, b0.p, a1.p, normal); - } - if (collapse0 && !collapse1) - { - // Project a0 onto vab1 - p1 = CLD_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; - result.a0_clipped = p0; - result.a1_clipped = p1; - result.b0_clipped = p0; - result.b1_clipped = p1; - } - - // Insert points - if (!ignore_a && a_sep < tolerance) - { - CLD_CollisionPoint *point = &points[num_points++]; - point->id = a0.i | (a1.i << 4); - point->separation = a_sep; - point->point = a_midpoint; - } - if (!ignore_b && b_sep < tolerance) - { - CLD_CollisionPoint *point = &points[num_points++]; - point->id = b0.i | (b1.i << 4); - point->separation = b_sep; - point->point = b_midpoint; - } - - result.a0 = a0.p; - result.a1 = a1.p; - result.b0 = b0.p; - result.b1 = b1.p; - } - } - -#if COLLIDER_DEBUG - result.solved = 1; - abort: - result.simplex = gjk_result.simplex; - result.prototype.len = epa_result.prototype.len; - CopyBytes(result.prototype.points, epa_result.prototype.points, sizeof(result.prototype.points[0]) * result.prototype.len); -#endif - result.normal = normal; - result.points[0] = points[0]; - result.points[1] = points[1]; - result.num_points = num_points; - return result; -} - -//////////////////////////////////////////////////////////// -//~ Closest points - -// TODO: De-duplicate code between CLD_ClosestPointDataFromShapes & CLD_CollisionDataFromShapes - -CLD_ClosestPointData CLD_ClosestPointDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1) -{ - CLD_ClosestPointData result = Zi; - - const f32 tolerance = CLD_CollisionTolerance; - const f32 min_unique_pt_dist_sq = CLD_MinUniquePtDistSq; - const u32 max_epa_iterations = CLD_MaxEpaIterations; - - Vec2 p0 = Zi; - Vec2 p1 = Zi; - b32 colliding = 0; - -#if COLLIDER_DEBUG - u32 dbg_step = 0; -#endif - - CLD_GjkData gjk_result = Zi; - CLD_EpaData epa_result = Zi; - - ////////////////////////////// - //- Run GJK - -#if COLLIDER_DEBUG - gjk_result = CLD_GjkDataFromShapes(shape0, shape1, af0, af1, min_unique_pt_dist_sq, dbg_step); - dbg_step = gjk_result.dbg_step; -#else - gjk_result = CLD_GjkDataFromShapes(shape0, shape1, af0, af1, min_unique_pt_dist_sq); -#endif - CLD_DBGSTEP; - - ////////////////////////////// - //- Run EPA - -#if COLLIDER_DEBUG - epa_result = CLD_EpaDataFromShapes(shape0, shape1, af0, af1, gjk_result, min_unique_pt_dist_sq, max_epa_iterations, dbg_step); - dbg_step = epa_result.dbg_step; -#else - epa_result = CLD_EpaDataFromShapes(shape0, shape1, af0, af1, gjk_result, min_unique_pt_dist_sq, max_epa_iterations); -#endif - CLD_DBGSTEP; - - ////////////////////////////// - //- Resolve points - - colliding = gjk_result.overlapping; - CLD_MenkowskiFeature f = epa_result.closest_feature; - if (f.len == 1) - { - p0 = f.a.s0.p; - p1 = f.a.s1.p; - colliding = gjk_result.overlapping || Vec2LenSq(NegVec2(f.a.p)) <= (tolerance * tolerance); - } - else - { - Assert(f.len == 2); - // FIXME: Winding order dependent? - f32 ratio; - { - // Determine ratio between edge a & b that projected origin lies - Vec2 vab = SubVec2(f.b.p, f.a.p); - Vec2 vao = NegVec2(f.a.p); - ratio = ClampF32(DotVec2(vab, vao) / DotVec2(vab, vab), 0, 1); - } - // Shape 0 - p0 = SubVec2(f.b.s0.p, f.a.s0.p); - p0 = MulVec2(p0, ratio); - p0 = AddVec2(p0, f.a.s0.p); - // Shape 1 - p1 = SubVec2(f.b.s1.p, f.a.s1.p); - p1 = MulVec2(p1, ratio); - p1 = AddVec2(p1, f.a.s1.p); - colliding = gjk_result.overlapping || Vec2LenSq(SubVec2(p1, p0)) <= (tolerance * tolerance); - } - -#if COLLIDER_DEBUG - result.solved = 1; - abort: - result.simplex = gjk_result.simplex; - result.prototype.len = epa_result.prototype.len; - CopyBytes(result.prototype.points, epa_result.prototype.points, sizeof(result.prototype.points[0]) * result.prototype.len); - result.simplex = gjk_result.simplex; -#endif - result.p0 = p0; - result.p1 = p1; - result.colliding = colliding; - return result; -} - -//////////////////////////////////////////////////////////// -//~ Time of impact - -// Takes 2 shapes and their affines at t=0 and t=1. -// Returns time of impact in range [0, 1]. -f32 CLD_TimeOfImpact(CLD_Shape *c0, CLD_Shape *c1, Affine af0_t0, Affine af1_t0, Affine af0_t1, Affine af1_t1, f32 tolerance, u32 max_iterations) -{ - f32 t0 = 0; - f32 t1 = 1; - f32 t0_sep = 0; - f32 t1_sep = 0; - f32 t = 0; - f32 t_sep = F32Infinity; - - // Find direction p0 -> p1 at t=0 - Vec2 dir; - Vec2 dir_neg; - { - CLD_ClosestPointData closest_points = CLD_ClosestPointDataFromShapes(c0, c1, af0_t0, af1_t0); - if (closest_points.colliding) - { - // Shapes are penetrating at t=0 - return 0; - } - dir = SubVec2(closest_points.p1, closest_points.p0); - t0_sep = Vec2Len(dir); - dir = DivVec2(dir, t0_sep); // Normalize - dir_neg = NegVec2(dir); - } - - { - Vec2 p0 = CLD_SupportPointFromDir(c0, af0_t1, dir).p; - Vec2 p1 = CLD_SupportPointFromDir(c1, af1_t1, dir_neg).p; - t1_sep = DotVec2(dir, SubVec2(p1, p0)); - if (t1_sep > 0) - { - // Shapes are not penetrating at t=1 - return 1; - } - } - - u32 iteration = 0; - while (AbsF32(t_sep) > tolerance && iteration < max_iterations) - { - // Use mix of bisection & 0 position method to find root - // (as described in https://box2d.org/files/ErinCatto_ContinuousCollision_GDC2013.pdf) - if (iteration & 1) - { - // Bisect - t = (t1 + t0) / 2.0; - } - else - { - // False position (fastest for linear case) - f32 m = (t1_sep - t0_sep) / (t1 - t0); - t = (-t1_sep / m) + t1; - } - - Affine af0 = LerpAffine(af0_t0, af0_t1, t); - Affine af1 = LerpAffine(af1_t0, af1_t1, t); - - Vec2 p0 = CLD_SupportPointFromDir(c0, af0, dir).p; - Vec2 p1 = CLD_SupportPointFromDir(c1, af1, dir_neg).p; - t_sep = DotVec2(dir, SubVec2(p1, p0)); - - // Update bracket - if (t_sep > 0) - { - t0 = t; - t0_sep = t_sep; - } - else - { - t1 = t; - t1_sep = t_sep; - } - - ++iteration; - } - - return t; -} - -//////////////////////////////////////////////////////////// -//~ Point cloud debug - -// TODO: Remove this (debugging) -Vec2Array CLD_Menkowski(Arena *arena, CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, u32 detail) -{ - Vec2Array result = { .points = ArenaNext(arena, Vec2) }; - for (u64 i = 0; i < detail; ++i) - { - f32 angle = ((f32)i / detail) * (2 * Pi); - Vec2 dir = Vec2FromAngle(angle); - CLD_MenkowskiPoint m = CLD_MenkowskiPointFromDir(shape0, shape1, af0, af1, dir); - if (result.count == 0 || !MatchVec2(m.p, result.points[result.count - 1])) - { - *PushStructNoZero(arena, Vec2) = m.p; - ++result.count; - } - } - return result; -} - -// TODO: Remove this (debugging) -Vec2Array CLD_PointCloud(Arena *arena, CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1) -{ - // FIXME: Account for radius - Vec2Array result = { .points = ArenaNext(arena, Vec2) }; - Vec2 *points0 = shape0->points; - Vec2 *points1 = shape1->points; - u32 count0 = shape0->count; - u32 count1 = shape1->count; - for (u64 i = 0; i < count0; ++i) - { - Vec2 p0 = MulAffineVec2(af0, points0[i]); - for (u64 j = 0; j < count1; ++j) - { - Vec2 p1 = MulAffineVec2(af1, points1[j]); - *PushStructNoZero(arena, Vec2) = SubVec2(p0, p1); - ++result.count; - } - } - return result; -} - -//////////////////////////////////////////////////////////// -//~ Boolean GJK (unused) - -#if 0 -b32 CLD_GjkBoolean(CLD_Shape *shape0, CLD_Shape *shape1) -{ - struct { Vec2 a, b, c; } s = Zi; - - // FIXME: Infinite loop when shapes exactly overlap same space? - Vec2 dir, p; - - // First point is support point in shape's general directions to eachother - dir = SubVec2(starting_point(shape1), starting_point(shape0)); - if (IsVec2Zero(dir)) dir = VEC2(1, 0); - s.a = CLD_MenkowskiPointFromDir(shape0, shape1, dir); - - // Second point is support point towards origin - dir = NegVec2(s.a); - p = CLD_MenkowskiPointFromDir(shape0, shape1, dir); - if (DotVec2(dir, p) >= 0) - { - s.b = s.a; - s.a = p; - for (;;) - { - // Third point is support point in direction of line normal towards origin - dir = PerpVec2TowardsDir(SubVec2(s.b, s.a), NegVec2(s.a)); - p = CLD_MenkowskiPointFromDir(shape0, shape1, dir); - if (DotVec2(dir, p) < 0) - { - // New point did not cross origin, collision impossible - break; - } - - s.c = s.b; - s.b = s.a; - s.a = p; - - Vec2 vab = SubVec2(s.b, s.a); - Vec2 vac = SubVec2(s.c, s.a); - Vec2 a_to_origin = NegVec2(s.a); - - dir = PerpVec2TowardsDir(vab, NegVec2(vac)); // Normal of ab pointing away from c - if (DotVec2(dir, a_to_origin) >= 0) - { - // Point is in region ab, remove c from simplex (will happen automatically next iteration) - } - else - { - // Point is not in region ab - dir = PerpVec2TowardsDir(vac, NegVec2(vab)); // Normal of ac pointing away from b - if (DotVec2(dir, a_to_origin) >= 0) - { - // Point is in region ac, remove b from simplex - s.b = s.c; - } - else - { - // Point is in simplex - return 1; - } - } - } - } - - return 0; -} -#endif diff --git a/src/collider/collider.h b/src/collider/collider.h deleted file mode 100644 index 6c71616c..00000000 --- a/src/collider/collider.h +++ /dev/null @@ -1,217 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Tweakable defines - -// How close can non-overlapping shapes be before collision is considered -#define CLD_CollisionTolerance 0.005f - -// NOTE: Should always be less than tolerance, since colliding = 1 if origin is within this distance. -#define CLD_MinUniquePtDistSq (0.001f * 0.001f) - -// To prevent extremely large prototypes when origin is in exact center of rounded feature -#define CLD_MaxEpaIterations 64 - -//////////////////////////////////////////////////////////// -//~ Shape types - -Struct(CLD_Shape) -{ - Vec2 points[8]; - u32 count; - f32 radius; -}; - -//////////////////////////////////////////////////////////// -//~ Menkowski types - -Struct(CLD_SupportPoint) -{ - Vec2 p; - u32 i; // Index of original point in shape -}; - -Struct(CLD_MenkowskiPoint) -{ - Vec2 p; // Menkowski difference point - CLD_SupportPoint s0; // Support point of first shape in dir - CLD_SupportPoint s1; // Support point of second shape in -dir -}; - -Struct(CLD_MenkowskiSimplex) -{ - u32 len; - CLD_MenkowskiPoint a, b, c; -}; - -Struct(CLD_MenkowskiFeature) -{ - u32 len; - CLD_MenkowskiPoint a, b; -}; - -//////////////////////////////////////////////////////////// -//~ Collision types - -Struct(CLD_CollisionPoint) -{ - Vec2 point; - f32 separation; - u32 id; // Based on polygon edge-to-edge -}; - -Struct(CLD_Prototype) -{ - Vec2 points[64]; - u32 len; -}; - -Struct(CLD_CollisionData) -{ - Vec2 normal; - CLD_CollisionPoint points[2]; - u32 num_points; - - // For debugging - b32 solved; - CLD_MenkowskiSimplex simplex; - CLD_Prototype prototype; - - // For debugging - Vec2 a0, b0, a1, b1; - Vec2 a0_clipped, b0_clipped, a1_clipped, b1_clipped; -}; - -Struct(CLD_ClosestPointData) -{ - Vec2 p0, p1; - b32 colliding; - - // For debugging - b32 solved; - CLD_MenkowskiSimplex simplex; - CLD_Prototype prototype; -}; - -//////////////////////////////////////////////////////////// -//~ Clipping types - -Struct(CLD_ClippedLine) -{ - Vec2 a0_clipped, b0_clipped; - Vec2 a1_clipped, b1_clipped; -}; - -//////////////////////////////////////////////////////////// -//~ Gjk types - -Struct(CLD_GjkData) -{ - CLD_MenkowskiSimplex simplex; - Vec2 final_dir; - - // If 1, simplex represents triangle inside of menkowski difference - // encapsulating the origin. If 0, simplex represents the closest - // feature on menkowski difference to the origin. - b32 overlapping; - -#if COLLIDER_DEBUG - u32 dbg_step; -#endif -}; - -//////////////////////////////////////////////////////////// -//~ Epa types - -Struct(CLD_EpaData) -{ - Vec2 normal; - CLD_MenkowskiFeature closest_feature; // Represents closest feature (edge or point) to origin on menkowski difference - -#if COLLIDER_DEBUG - CLD_Prototype prototype; - u32 dbg_step; -#endif -}; - -//////////////////////////////////////////////////////////// -//~ Debug helpers - -#if COLLIDER_DEBUG -void CLD_DebugBreakable(void); - -#define CLD_DBGSTEP \ - dbg_step++; \ - if (dbg_step >= GetGstat(DebugSteps)) \ - { \ - goto abort; \ - } \ - else if (dbg_step >= GetGstat(DebugSteps) - 1) \ - { \ - CLD_DebugBreakable(); \ - } (void)0 -#else - #define CLD_DBGSTEP -#endif - -//////////////////////////////////////////////////////////// -//~ Shape - -CLD_Shape CLD_ShapeFromQuad(Quad quad); - -//////////////////////////////////////////////////////////// -//~ Menkowski - -CLD_SupportPoint CLD_SupportPointFromDirEx(CLD_Shape *shape, Affine af, Vec2 dir, i32 ignore); -CLD_SupportPoint CLD_SupportPointFromDir(CLD_Shape *shape, Affine af, Vec2 dir); -CLD_MenkowskiPoint CLD_MenkowskiPointFromDir(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, Vec2 dir); - -//////////////////////////////////////////////////////////// -//~ Aabb - -Aabb CLD_AabbFromShape(CLD_Shape *shape, Affine af); -Aabb CLD_CombineAabb(Aabb b0, Aabb b1); -b32 CLD_TestAabb(Aabb box0, Aabb box1); - -//////////////////////////////////////////////////////////// -//~ Gjk - -#if COLLIDER_DEBUG -CLD_GjkData CLD_GjkDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, f32 min_unique_pt_dist_sq, u32 dbg_step); -#else -CLD_GjkData CLD_GjkDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, f32 min_unique_pt_dist_sq); -#endif - -//////////////////////////////////////////////////////////// -//~ Epa - -#if COLLIDER_DEBUG -CLD_EpaData CLD_EpaDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, CLD_GjkData gjk_result, f32 min_unique_pt_dist_sq, u32 max_iterations, u32 dbg_step); -#else -CLD_EpaData CLD_EpaDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, CLD_GjkData gjk_result, f32 min_unique_pt_dist_sq, u32 max_iterations); -#endif - -//////////////////////////////////////////////////////////// -//~ Clipping - -CLD_ClippedLine CLD_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal); -Vec2 CLD_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal); - -//////////////////////////////////////////////////////////// -//~ Collision point - -CLD_CollisionData CLD_CollisionDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1); - -//////////////////////////////////////////////////////////// -//~ Closest point - -CLD_ClosestPointData CLD_ClosestPointDataFromShapes(CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1); - -//////////////////////////////////////////////////////////// -//~ Time of impact - -f32 CLD_TimeOfImpact(CLD_Shape *c0, CLD_Shape *c1, Affine af0_t0, Affine af1_t0, Affine af0_t1, Affine af1_t1, f32 tolerance, u32 max_iterations); - -//////////////////////////////////////////////////////////// -//~ Point cloud debug - -Vec2Array CLD_Menkowski(Arena *arena, CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1, u32 detail); -Vec2Array CLD_PointCloud(Arena *arena, CLD_Shape *shape0, CLD_Shape *shape1, Affine af0, Affine af1); diff --git a/src/collider/collider.lay b/src/collider/collider.lay deleted file mode 100644 index cf16c7ca..00000000 --- a/src/collider/collider.lay +++ /dev/null @@ -1,11 +0,0 @@ -@Layer collider - -////////////////////////////// -//- Api - -@IncludeC collider.h - -////////////////////////////// -//- Impl - -@IncludeC collider.c diff --git a/src/draw/draw.c b/src/draw/draw.c deleted file mode 100644 index 8d282442..00000000 --- a/src/draw/draw.c +++ /dev/null @@ -1,468 +0,0 @@ -D_Ctx D = Zi; - -//////////////////////////////////////////////////////////// -//~ Bootstrap - -void D_Bootstrap(void) -{ - u32 pixel_white = 0xFFFFFFFF; - D.solid_white_texture = GPU_AcquireTexture(GP_TEXTURE_FORMAT_R8G8B8A8_UNORM, 0, VEC2I32(1, 1), &pixel_white); -} - -//////////////////////////////////////////////////////////// -//~ Material - -void D_DrawMaterial(GPU_RenderSig *sig, D_MaterialParams params) -{ - GPU_RenderCmdDesc cmd = ZI; - cmd.kind = GP_RENDER_CMD_KIND_DRAW_MATERIAL; - cmd.material.af = params.af; - cmd.material.texture = params.texture; - cmd.material.clip = params.clip; - cmd.material.tint = params.tint; - cmd.material.is_light = params.is_light; - cmd.material.light_emittance = params.light_emittance; - GPU_PushRenderCmd(sig, &cmd); -} - -//////////////////////////////////////////////////////////// -//~ Solid shapes - -void D_DrawPolyEx(GPU_RenderSig *sig, Vec2Array vertices, GPU_Indices indices, u32 color) -{ - GPU_RenderCmdDesc cmd = ZI; - cmd.kind = GP_RENDER_CMD_KIND_DRAW_UI_SHAPE; - cmd.ui_shape.vertices = vertices; - cmd.ui_shape.indices = indices; - cmd.ui_shape.color = color; - GPU_PushRenderCmd(sig, &cmd); -} - -// Draws a filled polygon using triangles in a fan pattern -void D_DrawPoly(GPU_RenderSig *sig, Vec2Array vertices, u32 color) -{ - if (vertices.count >= 3) - { - TempArena scratch = BeginScratchNoConflict(); - - u32 num_tris = vertices.count - 2; - u32 num_indices = num_tris * 3; - - // Generate indices in a fan pattern - GPU_Indices indices = ZI; - indices.count = num_indices; - indices.indices = PushStructsNoZero(scratch.arena, u32, num_indices); - for (u32 i = 0; i < num_tris; ++i) - { - u32 tri_offset = i * 3; - indices.indices[tri_offset + 0] = 0; - indices.indices[tri_offset + 1] = (i + 1); - indices.indices[tri_offset + 2] = (i + 2); - } - - D_DrawPolyEx(sig, vertices, indices, color); - - EndScratch(scratch); - } -} - -void D_DrawCircle(GPU_RenderSig *sig, Vec2 pos, f32 radius, u32 color, u32 detail) -{ - TempArena scratch = BeginScratchNoConflict(); - - Vec2 *points = PushStructsNoZero(scratch.arena, Vec2, detail); - for (u32 i = 0; i < detail; ++i) - { - f32 angle = ((f32)i / (f32)detail) * Tau; - Vec2 p = VEC2( - radius * CosF32(angle), - radius * SinF32(angle) - ); - points[i] = AddVec2(pos, p); - } - - Vec2Array vertices = { - .points = points, - .count = detail - }; - D_DrawPoly(sig, vertices, color); - - EndScratch(scratch); -} - -void D_DrawQuad(GPU_RenderSig *sig, Quad quad, u32 color) -{ - PERSIST const u32 indices_array[6] = { - 0, 1, 2, - 0, 2, 3 - }; - Vec2Array vertices = { .count = 4, .points = quad.e }; - GPU_Indices indices = { .count = 6, .indices = indices_array }; - D_DrawPolyEx(sig, vertices, indices, color); -} - -//////////////////////////////////////////////////////////// -//~ Line shapes - -void D_DrawLineGradient(GPU_RenderSig *sig, Vec2 start, Vec2 end, f32 thickness, u32 start_color, u32 end_color) -{ -#if 0 - Quad quad = QuadFromLine(start, end, thickness); - D_DrawMaterial(sig, D_MATERIALPARAMS(.texture = D.solid_white_texture, .tint0 = start_color, .tint1 = end_color, .quad = quad)); -#else - // Placeholder - Quad quad = QuadFromLine(start, end, thickness); - D_DrawQuad(sig, quad, start_color); -#endif -} - -void D_DrawLine(GPU_RenderSig *sig, Vec2 start, Vec2 end, f32 thickness, u32 color) -{ - Quad quad = QuadFromLine(start, end, thickness); - D_DrawQuad(sig, quad, color); -} - -void D_DrawRay(GPU_RenderSig *sig, Vec2 pos, Vec2 rel, f32 thickness, u32 color) -{ - Quad quad = QuadFromRay(pos, rel, thickness); - D_DrawQuad(sig, quad, color); -} - -void D_DrawPolyLine(GPU_RenderSig *sig, Vec2Array points, b32 loop, f32 thickness, u32 color) -{ - if (points.count >= 2) - { - for (u64 i = 1; i < points.count; ++i) - { - Vec2 p1 = points.points[i - 1]; - Vec2 p2 = points.points[i]; - Quad q = QuadFromLine(p1, p2, thickness); - D_DrawQuad(sig, q, color); - } - if (loop && points.count > 2) - { - Vec2 p1 = points.points[points.count - 1]; - Vec2 p2 = points.points[0]; - Quad q = QuadFromLine(p1, p2, thickness); - D_DrawQuad(sig, q, color); - } - } -} - -void D_DrawCircleLine(GPU_RenderSig *sig, Vec2 pos, f32 radius, f32 thickness, u32 color, u32 detail) -{ - TempArena scratch = BeginScratchNoConflict(); - - Vec2 *points = PushStructsNoZero(scratch.arena, Vec2, detail); - for (u32 i = 0; i < detail; ++i) - { - f32 angle = ((f32)i / (f32)detail) * Tau; - Vec2 p = VEC2( - radius * CosF32(angle), - radius * SinF32(angle) - ); - points[i] = AddVec2(pos, p); - } - - Vec2Array a = { - .points = points, - .count = detail - }; - D_DrawPolyLine(sig, a, 1, thickness, color); - - EndScratch(scratch); -} - -void D_DrawQuadLine(GPU_RenderSig *sig, Quad quad, f32 thickness, u32 color) -{ - Vec2 points[] = { quad.p0, quad.p1, quad.p2, quad.p3 }; - Vec2Array a = { .points = points, .count = countof(points) }; - D_DrawPolyLine(sig, a, 1, thickness, color); -} - -void D_DrawArrowLine(GPU_RenderSig *sig, Vec2 start, Vec2 end, f32 thickness, f32 arrowhead_height, u32 color) -{ - const f32 head_width_ratio = 0.5f; // Width of arrowhead relative to its length - - const f32 max_height_to_line_ratio = 0.9f; // Maximum length of arrowhead relative to total line length - arrowhead_height = MinF32(arrowhead_height, Vec2Len(SubVec2(end, start)) * max_height_to_line_ratio); - - Vec2 head_start_dir = SubVec2(start, end); - head_start_dir = NormVec2(head_start_dir); - head_start_dir = MulVec2(head_start_dir, arrowhead_height); - - Vec2 head_start = AddVec2(end, head_start_dir); - - Vec2 head_p1_dir = MulPerpVec2(head_start_dir, head_width_ratio); - Vec2 head_p2_dir = NegVec2(head_p1_dir); - - Vec2 head_p1 = AddVec2(head_start, head_p1_dir); - Vec2 head_p2 = AddVec2(head_start, head_p2_dir); - - Vec2 head_points[] = { end, head_p1, head_p2 }; - Vec2Array head_points_v2_array = { - .points = head_points, - .count = countof(head_points) - }; - D_DrawPoly(sig, head_points_v2_array, color); - - Quad line_quad = QuadFromLine(start, head_start, thickness); - D_DrawQuad(sig, line_quad, color); -} - -void D_DrawArrowRay(GPU_RenderSig *sig, Vec2 pos, Vec2 rel, f32 thickness, f32 arrowhead_height, u32 color) -{ - Vec2 end = AddVec2(pos, rel); - D_DrawArrowLine(sig, pos, end, thickness, arrowhead_height, color); -} - -void D_DrawColliderLine(GPU_RenderSig *sig, CLD_Shape shape, Affine shape_af, f32 thickness, u32 color, u32 detail) -{ - TempArena scratch = BeginScratchNoConflict(); - Vec2Array poly = ZI; - if (shape.radius == 0) - { - poly.count = shape.count; - poly.points = PushStructsNoZero(scratch.arena, Vec2, shape.count); - for (u32 i = 0; i < shape.count; ++i) - { - Vec2 p = MulAffineVec2(shape_af, shape.points[i]); - poly.points[i] = p; - } - } - else - { - poly.count = detail; - poly.points = PushStructsNoZero(scratch.arena, Vec2, detail); - for (u32 i = 0; i < detail; ++i) - { - f32 angle = ((f32)i / (f32)detail) * Tau; - Vec2 dir = VEC2(CosF32(angle), SinF32(angle)); - Vec2 p = CLD_SupportPointFromDir(&shape, shape_af, dir).p; - poly.points[i] = p; - } - } - D_DrawPolyLine(sig, poly, 1, thickness, color); - EndScratch(scratch); -} - -//////////////////////////////////////////////////////////// -//~ Grid - -void D_DrawGrid(GPU_RenderSig *sig, Affine af, u32 bg0_color, u32 bg1_color, u32 line_color, u32 x_color, u32 y_color, f32 thickness, f32 spacing, Vec2 offset) -{ - i32 grid_id = 0; - { - GPU_RenderCmdDesc cmd = ZI; - cmd.kind = GP_RENDER_CMD_KIND_PUSH_GRID; - cmd.grid.bg0_color = bg0_color; - cmd.grid.bg1_color = bg1_color; - cmd.grid.line_color = line_color; - cmd.grid.x_color = x_color; - cmd.grid.y_color = y_color; - cmd.grid.line_thickness = thickness; - cmd.grid.line_spacing = spacing; - cmd.grid.offset = offset; - grid_id = GPU_PushRenderCmd(sig, &cmd); - } - - GPU_RenderCmdDesc cmd = ZI; - cmd.kind = GP_RENDER_CMD_KIND_DRAW_MATERIAL; - cmd.material.af = af; - cmd.material.tint = Color_White; - cmd.material.grid_cmd_id = grid_id; - GPU_PushRenderCmd(sig, &cmd); -} - -//////////////////////////////////////////////////////////// -//~ Ui - -void D_DrawUiRect(GPU_RenderSig *sig, D_UiRectParams params) -{ - GPU_RenderCmdDesc cmd = ZI; - cmd.kind = GP_RENDER_CMD_KIND_DRAW_UI_RECT; - cmd.ui_rect.af = params.af; - cmd.ui_rect.texture = params.texture; - cmd.ui_rect.clip = params.clip; - cmd.ui_rect.tint = params.tint; - GPU_PushRenderCmd(sig, &cmd); -} - -//////////////////////////////////////////////////////////// -//~ Text - -// Returns the rect of the text area -Rect draw_text(GPU_RenderSig *sig, D_TextParams params) -{ - TempArena scratch = BeginScratchNoConflict(); - - f32 inv_font_image_width = 1.0 / (f32)params.font->image_width; - f32 inv_font_image_height = 1.0 / (f32)params.font->image_height; - f32 line_spacing = params.font->point_size * 1.5f * params.scale; - - //- Build line glyphs - - u64 num_lines = 0; - f32 widest_line = 0; - - D_TextLine *first_line = 0; - D_TextLine *last_line = 0; - f32 first_line_top_offset = 0; - f32 last_line_bottom_offset = 0; - - if (params.str.len > 0) - { - b32 string_done = 0; - CodepointIter iter = BeginCodepointIter(params.str); - while (!string_done) - { - f32 line_width = 0; - f32 top_offset = 0; - f32 bottom_offset = 0; - u64 num_line_glyphs = 0; - D_TextGlyph *line_glyphs = PushDry(scratch.arena, D_TextGlyph); - - b32 line_done = 0; - while (!line_done) - { - string_done = !AdvanceCodepointIter(&iter); - if (string_done) - { - line_done = 1; - } - else - { - u32 codepoint = iter.codepoint; - if (codepoint == '\n') - { - line_done = 1; - } - else - { - D_TextGlyph *tg = PushStruct(scratch.arena, D_TextGlyph); - ++num_line_glyphs; - F_Glyph *glyph = F_GetGlyph(params.font, codepoint); - tg->off_x = glyph->off_x * params.scale; - tg->off_y = glyph->off_y * params.scale; - tg->width = glyph->width * params.scale; - tg->height = glyph->height * params.scale; - tg->advance = glyph->advance * params.scale; - Rect glyph_atlas_rect = glyph->atlas_rect; - tg->clip = (ClipRect) { - { - glyph_atlas_rect.x * inv_font_image_width, - glyph_atlas_rect.y * inv_font_image_height - }, - { - (glyph_atlas_rect.x + glyph_atlas_rect.width) * inv_font_image_width, - (glyph_atlas_rect.y + glyph_atlas_rect.height) * inv_font_image_height - } - }; - line_width += tg->advance; - top_offset = MinF32(top_offset, tg->off_y); - bottom_offset = MaxF32(bottom_offset, tg->off_y + tg->height); - } - } - } - - // Line ended - // TODO: Only create nodes for non-empty lines. Embed line number in the node. - D_TextLine *node = PushStruct(scratch.arena, D_TextLine); - node->line_width = line_width; - node->num_glyphs = num_line_glyphs; - node->glyphs = line_glyphs; - if (last_line) - { - last_line->next = node; - } - else - { - first_line = node; - first_line_top_offset = top_offset; - } - last_line = node; - last_line_bottom_offset = bottom_offset; - widest_line = MaxF32(widest_line, line_width); - ++num_lines; - } - EndCodepointIter(&iter); - } - - //- Determine text bounds - - Rect bounds = ZI; - bounds.x = params.pos.x; - bounds.y = params.pos.y; - bounds.width = widest_line; - bounds.height = num_lines * line_spacing + first_line_top_offset + last_line_bottom_offset; - - // Offset bounds X - switch (params.offset_x) - { - case DRAW_TEXT_OFFSET_X_LEFT: break; - case DRAW_TEXT_OFFSET_X_CENTER: - { - bounds.x -= bounds.width / 2.f; - } break; - case DRAW_TEXT_OFFSET_X_RIGHT: - { - bounds.x -= bounds.width; - } break; - } - - // Offset bounds Y - switch (params.offset_y) - { - case DRAW_TEXT_OFFSET_Y_TOP: break; - case DRAW_TEXT_OFFSET_Y_CENTER: - { - bounds.y -= bounds.height / 2.f; - } break; - case DRAW_TEXT_OFFSET_Y_BOTTOM: - { - if (last_line) - { - bounds.y -= bounds.height + last_line_bottom_offset; - } - } break; - } - - //- Draw lines - - u64 line_number = 0; - for (D_TextLine *line = first_line; line; line = line->next) - { - Vec2 draw_pos = bounds.pos; - draw_pos.y += line_number * line_spacing - first_line_top_offset; - - // Alignment - switch (params.alignment) - { - case DRAW_TEXT_ALIGNMENT_LEFT: break; - case DRAW_TEXT_ALIGNMENT_CENTER: - { - draw_pos.x += (bounds.width - line->line_width) / 2.f; - } break; - case DRAW_TEXT_ALIGNMENT_RIGHT: - { - draw_pos.x += bounds.width - line->line_width; - } break; - } - - // Draw glyphs in line - for (u64 i = 0; i < line->num_glyphs; ++i) - { - D_TextGlyph *tg = &line->glyphs[i]; - Vec2 pos = VEC2(draw_pos.x + tg->off_x, draw_pos.y + tg->off_y); - Vec2 size = VEC2(tg->width, tg->height); - Affine af = AffineFromRect(RectFromVec2(pos, size)); - D_DrawUiRect(sig, D_UIRECTPARAMS(.af = af, .texture = params.font->texture, .tint = params.color, .clip = tg->clip)); - draw_pos.x += tg->advance; - - } - ++line_number; - } - - EndScratch(scratch); - return bounds; -} diff --git a/src/draw/draw.h b/src/draw/draw.h deleted file mode 100644 index 403aac84..00000000 --- a/src/draw/draw.h +++ /dev/null @@ -1,155 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Material types - -Struct(D_MaterialParams) -{ - Affine af; - GPU_Resource *texture; - ClipRect clip; - u32 tint; - b32 is_light; - Vec3 light_emittance; -}; -#define D_MATERIALPARAMS(...) ((D_MaterialParams) { \ - .tint = Color_White, \ - .clip = AllClipped, \ - __VA_ARGS__ \ -}) - -//////////////////////////////////////////////////////////// -//~ Ui types - -Struct(D_UiRectParams) -{ - Affine af; - GPU_Resource *texture; - ClipRect clip; - u32 tint; -}; -#define D_UIRECTPARAMS(...) ((D_UiRectParams) { \ - .tint = Color_White, \ - .clip = AllClipped, \ - __VA_ARGS__ \ -}) - -//////////////////////////////////////////////////////////// -//~ Text types - -// How is text aligned within its area -Enum(D_TextAlignment) -{ - DRAW_TEXT_ALIGNMENT_LEFT, // Default - DRAW_TEXT_ALIGNMENT_CENTER, - DRAW_TEXT_ALIGNMENT_RIGHT -}; - -// How does the specified text position relate to the text area. -// E.g. BOTTOM & RIGHT means the bottom-right of the text area will snap to -// the specified position. -Enum(D_TextOffsetX) -{ - DRAW_TEXT_OFFSET_X_LEFT, // Default - DRAW_TEXT_OFFSET_X_CENTER, - DRAW_TEXT_OFFSET_X_RIGHT -}; - -Enum(D_TextOffsetY) -{ - DRAW_TEXT_OFFSET_Y_TOP, // Default - DRAW_TEXT_OFFSET_Y_CENTER, - DRAW_TEXT_OFFSET_Y_BOTTOM -}; - -Struct(D_TextGlyph) -{ - f32 off_x; - f32 off_y; - f32 width; - f32 height; - f32 advance; - ClipRect clip; -}; - -Struct(D_TextLine) -{ - f32 line_width; - u32 num_glyphs; - D_TextGlyph *glyphs; - D_TextLine *next; -}; - -Struct(D_TextParams) -{ - F_Font *font; - Vec2 pos; - f32 scale; - u32 color; - D_TextAlignment alignment; - D_TextOffsetX offset_x; - D_TextOffsetY offset_y; - String str; -}; -#define D_TEXTPARAMS(...) ((D_TextParams) { \ - .scale = 1.0, \ - .alignment = DRAW_TEXT_ALIGNMENT_LEFT, \ - .offset_x = DRAW_TEXT_OFFSET_X_LEFT, \ - .offset_y = DRAW_TEXT_OFFSET_Y_TOP, \ - .color = Color_White, \ - __VA_ARGS__ \ -}) - -//////////////////////////////////////////////////////////// -//~ State types - -Struct(D_Ctx) -{ - GPU_Resource *solid_white_texture; -}; - -extern D_Ctx D; - -//////////////////////////////////////////////////////////// -//~ Bootstrap - -void D_Bootstrap(void); - -//////////////////////////////////////////////////////////// -//~ Material - -void D_DrawMaterial(GPU_RenderSig *sig, D_MaterialParams params); - -//////////////////////////////////////////////////////////// -//~ Solid shape - -void D_DrawPolyEx(GPU_RenderSig *sig, Vec2Array vertices, GPU_Indices indices, u32 color); -void D_DrawPoly(GPU_RenderSig *sig, Vec2Array points, u32 color); -void D_DrawCircle(GPU_RenderSig *sig, Vec2 pos, f32 radius, u32 color, u32 detail); -void D_DrawQuad(GPU_RenderSig *sig, Quad quad, u32 color); - -//////////////////////////////////////////////////////////// -//~ Line shape - -void D_DrawLineGradient(GPU_RenderSig *sig, Vec2 start, Vec2 end, f32 thickness, u32 start_color, u32 end_color); -void D_DrawLine(GPU_RenderSig *sig, Vec2 start, Vec2 end, f32 thickness, u32 color); -void D_DrawRay(GPU_RenderSig *sig, Vec2 pos, Vec2 rel, f32 thickness, u32 color); -void D_DrawPolyLine(GPU_RenderSig *sig, Vec2Array points, b32 loop, f32 thickness, u32 color); -void D_DrawCircleLine(GPU_RenderSig *sig, Vec2 pos, f32 radius, f32 thickness, u32 color, u32 detail); -void D_DrawQuadLine(GPU_RenderSig *sig, Quad quad, f32 thickness, u32 color); -void D_DrawArrowLine(GPU_RenderSig *sig, Vec2 start, Vec2 end, f32 thickness, f32 arrowhead_height, u32 color); -void D_DrawArrowRay(GPU_RenderSig *sig, Vec2 pos, Vec2 rel, f32 thickness, f32 arrowhead_height, u32 color); -void D_DrawColliderLine(GPU_RenderSig *sig, CLD_Shape shape, Affine shape_af, f32 thickness, u32 color, u32 detail); - -//////////////////////////////////////////////////////////// -//~ Grid - -void D_DrawGrid(GPU_RenderSig *sig, Affine af, u32 bg0_color, u32 bg1_color, u32 line_color, u32 x_color, u32 y_color, f32 thickness, f32 spacing, Vec2 offset); - -//////////////////////////////////////////////////////////// -//~ Ui - -void D_DrawUiRect(GPU_RenderSig *sig, D_UiRectParams params); - -//////////////////////////////////////////////////////////// -//~ Text - -Rect draw_text(GPU_RenderSig *sig, D_TextParams params); diff --git a/src/draw/draw.lay b/src/draw/draw.lay deleted file mode 100644 index d7cb53c8..00000000 --- a/src/draw/draw.lay +++ /dev/null @@ -1,22 +0,0 @@ -@Layer draw - -////////////////////////////// -//- Dependencies - -@Dep base -@Dep gpu -@Dep sprite -@Dep font -@Dep collider - -////////////////////////////// -//- Api - -@IncludeC draw.h - -@Bootstrap D_Bootstrap - -////////////////////////////// -//- Impl - -@IncludeC draw.c diff --git a/src/json/json.c b/src/json/json.c deleted file mode 100644 index f1636cf2..00000000 --- a/src/json/json.c +++ /dev/null @@ -1,930 +0,0 @@ -// TODO (if we want to be JSON standard compliant): -// - Support unicode escape sequences in strings (\u) -// - Don't allow leading 0s in numbers - -//////////////////////////////////////////////////////////// -//~ Lex - -JSON_Token *JSON_PushToken(Arena *arena, JSON_TokenList *list) -{ - JSON_Token *t = PushStruct(arena, JSON_Token); - if (!list->token_first) - { - list->token_first = t; - } - else - { - list->token_last->next = t; - } - list->token_last = t; - return t; -} - -JSON_TokenList JSON_TokensFromString(Arena *arena, String src) -{ - JSON_TokenList result = ZI; - - JSON_Token *bof = JSON_PushToken(arena, &result); - bof->kind = JSON_TokenKind_Bof; - - u64 pos = 0; - b32 lexing_done = 0; - while (!lexing_done) - { - // Skip whitespace - b32 whitespace_done = 0; - while (!whitespace_done && pos < src.len) - { - switch (src.text[pos]) - { - default: - { - whitespace_done = 1; - } break; - - case JSON_Case_Newline: - case JSON_Case_Space: - { - ++pos; - } break; - } - } - - // Create token - JSON_Token *t = JSON_PushToken(arena, &result); - t->start = pos; - - if (pos >= src.len) - { - t->kind = JSON_TokenKind_Eof; - t->next = t; // Self reference - lexing_done = 1; - } - else - { - // Lex known token kinds - switch (src.text[pos]) - { - default: break; - - // Symbols - case ',': - { - t->kind = JSON_TokenKind_Comma; - ++pos; - } break; - - case ':': - { - t->kind = JSON_TokenKind_Colon; - ++pos; - } break; - - case '[': - { - t->kind = JSON_TokenKind_SquareBraceOpen; - ++pos; - } break; - - case ']': - { - t->kind = JSON_TokenKind_SquareBraceClose; - ++pos; - } break; - - case '{': - { - t->kind = JSON_TokenKind_CurlyBraceOpen; - ++pos; - } break; - - case '}': - { - t->kind = JSON_TokenKind_CurlyBraceClose; - ++pos; - } break; - - // Number - case '-': - { - // Verify '-' precedes digit - b32 next_is_digit = 0; - if ((pos + 1) < src.len) - { - switch (src.text[pos + 1]) - { - case JSON_Case_Digit0Through9: - { - next_is_digit = 1; - } break; - } - } - ++pos; - if (!next_is_digit) - { - break; - } - } FALLTHROUGH; - case JSON_Case_Digit0Through9: - { - t->kind = JSON_TokenKind_Number; - JSON_LexNumberState state = JSON_LexNumberState_Whole; - b32 number_done = 0; - while (!number_done && pos < src.len) - { - switch (src.text[pos]) - { - default: - { - number_done = 1; - } break; - - case JSON_Case_Digit0Through9: - { - ++pos; - } break; - - case '.': - { - u64 consume = 0; - if (state == JSON_LexNumberState_Whole && (pos + 1) < src.len) - { - u8 c1 = src.text[pos + 1]; - switch (c1) - { - default: break; - - case JSON_Case_Digit0Through9: - { - // Consume '.' - ++consume; - } break; - } - } - if (consume) - { - state = JSON_LexNumberState_Fraction; - pos += consume; - } - else - { - number_done = 1; - } - } break; - - case 'e': - case 'E': - { - u64 consume = 0; - if ((state == JSON_LexNumberState_Whole || state == JSON_LexNumberState_Fraction) && (pos + 1) < src.len) - { - u8 c1 = src.text[pos + 1]; - switch (c1) - { - case JSON_Case_Digit0Through9: - { - // Consume 'E'/'e' - ++consume; - } break; - - case '-': - case '+': - { - if ((pos + 2) < src.len) - { - u8 c2 = src.text[pos + 2]; - switch (c2) - { - default: break; - - case JSON_Case_Digit0Through9: - { - // Consume 'E'/'e' & '+'/'-' - consume += 2; - } break; - } - } - } break; - - default: break; - } - } - if (consume) - { - state = JSON_LexNumberState_Exponent; - pos += consume; - } - else - { - number_done = 1; - } - } break; - } - } - } break; - - // String - case '"': - { - ++pos; - - b32 string_done = 0; - b32 next_escaped = 0; - while (!string_done && pos < src.len) - { - b32 escaped = next_escaped; - next_escaped = 0; - switch (src.text[pos]) - { - default: - { - ++pos; - } break; - - case JSON_Case_Newline: - { - ++pos; - string_done = 1; - } break; - - case '"': - { - ++pos; - if (!escaped) - { - t->kind = JSON_TokenKind_String; - string_done = 1; - } - } break; - - case '\\': - { - ++pos; - if (!escaped) - { - next_escaped = 1; - } - } break; - } - } - } break; - - // Keywords - case 't': - case 'f': - case 'n': - { - String keyword = JSON_keyword_strings[src.text[pos]]; - - b32 match = 1; - if ((pos + keyword.len - 1) < src.len) - { - if ((pos + keyword.len) < src.len) - { - // Don't match if word continues past keyword - switch (src.text[pos + keyword.len]) - { - default: - { - match = 0; - } break; - - case JSON_Case_Symbol: - case JSON_Case_Space: - case JSON_Case_Newline: - { - } break; - } - } - if (match) - { - String cmp_str = { - .len = keyword.len, - .text = &src.text[pos] - }; - match = MatchString(cmp_str, keyword); - } - } - - if (match) - { - t->kind = JSON_keyword_types[src.text[pos]]; - pos += keyword.len; - } - } break; - } - } - - // Lex unknown token - if (t->kind == JSON_TokenKind_Unknown) - { - b32 unknown_done = 0; - while (!unknown_done && pos < src.len) - { - switch (src.text[pos]) - { - default: - { - ++pos; - } break; - - case JSON_Case_Symbol: - case JSON_Case_Space: - case JSON_Case_Newline: - { - unknown_done = 1; - } break; - } - } - t->end = pos; - - // Exit early if unknown token encountered - return result; - } - else - { - t->end = pos; - } - } - - return result; -} - -//////////////////////////////////////////////////////////// -//~ Interpret - -f64 interpret_number(String src) -{ - b32 whole_present = 0; - u64 whole_left = 0; - u64 whole_right = 0; - i32 whole_sign = 1; - - b32 fraction_present = 0; - u64 fraction_left = 0; - u64 fraction_right = 0; - - b32 exponent_present = 0; - u64 exponent_left = 0; - u64 exponent_right = 0; - i32 exponent_sign = 1; - - // Lex number parts - { - u64 pos = 0; - if (src.len > 0 && src.text[0] == '-') - { - whole_sign = -1; - ++pos; - } - - JSON_LexNumberState state = JSON_LexNumberState_Whole; - while (pos < src.len) - { - switch (src.text[pos]) - { - default: - { - // Unreachable - Assert(0); - ++pos; - } break; - - case JSON_Case_Digit0Through9: - { - switch (state) - { - case JSON_LexNumberState_Whole: - { - if (!whole_present) - { - whole_present = 1; - whole_left = pos; - } - whole_right = pos; - ++pos; - } break; - - case JSON_LexNumberState_Fraction: - { - if (!fraction_present) - { - fraction_present = 1; - fraction_left = pos; - } - fraction_right = pos; - ++pos; - } break; - - case JSON_LexNumberState_Exponent: - { - if (!exponent_present) - { - exponent_present = 1; - exponent_left = pos; - } - exponent_right = pos; - ++pos; - } break; - } - } break; - - case '.': - { - state = JSON_LexNumberState_Fraction; - ++pos; - } break; - - case 'e': - case 'E': - { - state = JSON_LexNumberState_Exponent; - ++pos; - } break; - - case '-': - { - switch (state) - { - default: - { - // Unreachable - Assert(0); - ++pos; - } break; - - case JSON_LexNumberState_Whole: - { - whole_sign = -1; - ++pos; - } break; - - case JSON_LexNumberState_Exponent: - { - exponent_sign = -1; - ++pos; - } break; - } - } break; - - case '+': - { - switch (state) - { - default: - { - // Unreachable - Assert(0); - ++pos; - } break; - - case JSON_LexNumberState_Exponent: - { - exponent_sign = 1; - ++pos; - } break; - } - } break; - } - } - } - - f64 result = 0; - - // Process whole part - if (whole_present) - { - u64 pos = whole_left; - while (pos <= whole_right) - { - u8 digit = MinU8(src.text[pos] - 48, 9); - u64 exp = whole_right - pos; - result += digit * PowU64(10, exp); - ++pos; - } - result *= whole_sign; - } - - // Process fraction part - if (fraction_present) - { - u64 frac_whole = 0; - u64 pos = fraction_left; - while (pos <= fraction_right) - { - u8 digit = MinU8(src.text[pos] - 48, 9); - u64 exp = fraction_right - pos; - frac_whole += digit * PowU64(10, exp); - ++pos; - } - - result += (f64)frac_whole / PowU64(10, (fraction_right - fraction_left + 1)); - } - - // Process exponent part - if (exponent_present) - { - u64 exponent_whole = 0; - u64 pos = exponent_left; - while (pos <= exponent_right) - { - u8 digit = MinU8(src.text[pos] - 48, 9); - u64 exp = exponent_right - pos; - exponent_whole += digit * PowU64(10, exp); - ++pos; - } - - if (exponent_sign >= 0) - { - result *= PowU64(10, exponent_whole); - } - else - { - result /= PowU64(10, exponent_whole); - } - } - - return result; -} - -String interpret_string(Arena *arena, String src, String *error) -{ - String result = { - .len = 0, - .text = PushDry(arena, u8) - }; - - if (src.len < 2) - { - if (error) - { - *error = Lit("Malformed string."); - } - return result; - } - // Ignore beginning quote - u64 pos = 1; - - b32 valid_close = 0; - b32 string_done = 0; - b32 next_escaped = 0; - while (!string_done && pos < src.len) - { - b32 escaped = next_escaped; - next_escaped = 0; - - if (escaped) - { - switch (src.text[pos]) - { - default: - { - if (error) - { - *error = Lit("Invalid escape character in string."); - return result; - } - } break; - - case '"': - case '\\': - case '/': - { - *PushStructNoZero(arena, u8) = src.text[pos]; - ++result.len; - ++pos; - } break; - - // Backspace - case 'b': - { - *PushStructNoZero(arena, u8) = '\b'; - ++result.len; - ++pos; - } break; - - // Formfeed - case 'f': - { - *PushStructNoZero(arena, u8) = '\f'; - ++result.len; - ++pos; - } break; - - // Linefeed - case 'n': - { - *PushStructNoZero(arena, u8) = '\n'; - ++result.len; - ++pos; - } break; - - // Carriage return - case 'r': - { - *PushStructNoZero(arena, u8) = '\r'; - ++result.len; - ++pos; - } break; - - // Horizontal tab - case 't': - { - *PushStructNoZero(arena, u8) = '\t'; - ++result.len; - ++pos; - } break; - - // TODO: Unicode escape support - // case 'u': - // { - // // TODO - // } break; - } - } - else - { - switch (src.text[pos]) - { - default: - { - *PushStructNoZero(arena, u8) = src.text[pos]; - ++result.len; - ++pos; - } break; - - case '\\': - { - escaped = 1; - ++pos; - } break; - - case '"': - { - string_done = 1; - valid_close = 1; - ++pos; - } break; - } - } - } - - if (!valid_close) - { - if (error) - { - *error = Lit("Expected end of string."); - } - } - - return result; -} - -//////////////////////////////////////////////////////////// -//~ Parse - -void JSON_PushError(Arena *arena, JSON_Parser *p, JSON_Token *t, String msg) -{ - JSON_Error *error = PushStruct(arena, JSON_Error); - error->msg = msg; - error->start = t->start; - error->end = t->end; - - JSON_ErrorList *list = &p->errors; - if (!list->first) - { - list->first = error; - } - else - { - list->last->next = error; - } - list->last = error; - ++list->count; -} - -void JSON_Parse(Arena *arena, JSON_Parser *p) -{ - TempArena scratch = BeginScratch(arena); - - JSON_Blob *root = PushStruct(arena, JSON_Blob); - JSON_Token *at = p->at; - String src = p->src; - - if (at->kind == JSON_TokenKind_Bof) - { - at = at->next; - } - - // Depth first stack - *PushStructNoZero(scratch.arena, JSON_Blob *) = root; - u64 stack_count = 1; - - while (stack_count > 0) - { - JSON_Blob *json = 0; - PopStruct(scratch.arena, JSON_Blob *, &json); - --stack_count; - - JSON_Blob *parent_json = json->parent; - b32 is_new_parent = 0; - if (json->type == JSON_Type_Object || json->type == JSON_Type_Array) - { - // No more children to parse for object/array, check for closing brace. - JSON_TokenKind tok_close_kind = json->type == JSON_Type_Object ? JSON_TokenKind_CurlyBraceClose : JSON_TokenKind_SquareBraceClose; - if (at->kind == tok_close_kind) - { - at = at->next; - } - else - { - JSON_PushError(arena, p, at, Lit("Expected comma.")); - at = at->next; - goto abort; - } - } - else - { - if (parent_json) - { - if (parent_json->type == JSON_Type_Object) - { - // Parse key - if (at->kind == JSON_TokenKind_String) - { - String t_text = (String) { .len = at->end - at->start, .text = &src.text[at->start] }; - String error = ZI; - String key = interpret_string(arena, t_text, &error); - if (error.len > 0) - { - JSON_PushError(arena, p, at, error); - goto abort; - } - else - { - json->key = key; - at = at->next; - } - } - else - { - JSON_PushError(arena, p, at, Lit("Key expected.")); - goto abort; - } - - // Parse colon - if (at->kind == JSON_TokenKind_Colon) - { - at = at->next; - } - else - { - JSON_PushError(arena, p, at, Lit("Colon expected.")); - goto abort; - } - } - - if (parent_json->child_last) - { - parent_json->child_last->next = json; - } - else - { - parent_json->child_first = json; - } - parent_json->child_last = json; - } - - // Parse value - switch (at->kind) - { - default: - { - JSON_PushError(arena, p, at, Lit("Value expected.")); - at = at->next; - goto abort; - } break; - - case JSON_TokenKind_Number: - { - String t_text = STRING(at->end - at->start, &src.text[at->start]); - f64 value = interpret_number(t_text); - json->type = JSON_Type_Number; - json->value.number = value; - at = at->next; - } break; - - case JSON_TokenKind_String: - { - String t_text = STRING(at->end - at->start, &src.text[at->start]); - String error = ZI; - String value = interpret_string(arena, t_text, &error); - if (error.len > 0) - { - JSON_PushError(arena, p, at, error); - goto abort; - } - else - { - json->type = JSON_Type_String; - json->value.string = value; - at = at->next; - } - } break; - - case JSON_TokenKind_KeywordTrue: - { - json->type = JSON_Type_Bool; - json->value.boolean = 1; - at = at->next; - } break; - - case JSON_TokenKind_KeywordFalse: - { - json->type = JSON_Type_Bool; - json->value.boolean = 0; - at = at->next; - } break; - - case JSON_TokenKind_KeywordNull: - { - json->type = JSON_Type_Null; - at = at->next; - } break; - - case JSON_TokenKind_CurlyBraceOpen: - { - json->type = JSON_Type_Object; - at = at->next; - is_new_parent = 1; - } break; - - case JSON_TokenKind_SquareBraceOpen: - { - json->type = JSON_Type_Array; - at = at->next; - is_new_parent = 1; - } break; - } - } - - if (is_new_parent) - { - // Push self back to stack to re-check for closing brace later - *PushStructNoZero(scratch.arena, JSON_Blob *) = json; - ++stack_count; - - // Create child & push to stack - JSON_Blob *child = PushStruct(arena, JSON_Blob); - child->parent = json; - *PushStructNoZero(scratch.arena, JSON_Blob *) = child; - ++stack_count; - } - else if (parent_json) - { - // Check for comma - if (at->kind == JSON_TokenKind_Comma) - { - // Create sibling & push to stack - JSON_Blob *sibling = PushStruct(arena, JSON_Blob); - sibling->parent = parent_json; - *PushStructNoZero(scratch.arena, JSON_Blob *) = sibling; - ++stack_count; - at = at->next; - } - } - } - - abort: - - p->at = at; - p->root = root; - - EndScratch(scratch); -} - -JSON_Result JSON_BlobFromString(Arena *arena, String src) -{ - TempArena scratch = BeginScratch(arena); - - JSON_TokenList tl = JSON_TokensFromString(scratch.arena, src); - - // Parse root - JSON_Parser p = ZI; - p.src = src; - p.at = tl.token_first; - JSON_Parse(arena, &p); - - // Verify end of file - if (p.errors.count == 0 && p.at->kind != JSON_TokenKind_Eof) - { - JSON_PushError(arena, &p, p.at, Lit("Expected end of file.")); - } - - EndScratch(scratch); - - JSON_Result result = ZI; - result.root = p.root; - result.errors = p.errors; - return result; -} diff --git a/src/json/json.h b/src/json/json.h deleted file mode 100644 index 85e7a73a..00000000 --- a/src/json/json.h +++ /dev/null @@ -1,159 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Blob types - -Enum(JSON_Type) -{ - JSON_Type_Null, - JSON_Type_Bool, - JSON_Type_Number, - JSON_Type_String, - JSON_Type_Array, - JSON_Type_Object -}; - -Struct(JSON_Blob) -{ - JSON_Type type; - String key; - - JSON_Blob *parent; - JSON_Blob *next; - JSON_Blob *child_first; - JSON_Blob *child_last; - - union - { - String string; - f64 number; - b32 boolean; - } value; -}; - -Struct(JSON_Error) -{ - String msg; - u64 start; - u64 end; - JSON_Error *next; -}; - -Struct(JSON_ErrorList) -{ - u64 count; - JSON_Error *first; - JSON_Error *last; -}; - -Struct(JSON_Result) -{ - JSON_Blob *root; - JSON_ErrorList errors; -}; - -//////////////////////////////////////////////////////////// -//~ Lexer types - -#define JSON_Case_Newline \ - 0x0A: /* Line feed or New line */ \ - case 0x0D /* Carriage return */ - -#define JSON_Case_Space \ - 0x20: /* Space */ \ - case 0x09 /* Horizontal tab */ - -#define JSON_Case_Digit0Through9 \ - '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9' - -#define JSON_Case_Digit1Through9 \ - '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9' - -#define JSON_Case_Symbol \ - ',': case ':': case '[': case ']': case '{': case '}' - -Enum(JSON_TokenKind) -{ - JSON_TokenKind_Unknown, - - JSON_TokenKind_Number, - JSON_TokenKind_String, - - JSON_TokenKind_KeywordTrue, - JSON_TokenKind_KeywordFalse, - JSON_TokenKind_KeywordNull, - - JSON_TokenKind_Comma, - JSON_TokenKind_Colon, - JSON_TokenKind_SquareBraceOpen, - JSON_TokenKind_SquareBraceClose, - JSON_TokenKind_CurlyBraceOpen, - JSON_TokenKind_CurlyBraceClose, - - JSON_TokenKind_Bof, - JSON_TokenKind_Eof -}; - -Struct(JSON_Token) -{ - JSON_TokenKind kind; - u64 start; - u64 end; - JSON_Token *next; -}; - -Struct(JSON_TokenList) -{ - JSON_Token *token_first; - JSON_Token *token_last; -}; - -Enum(JSON_LexNumberState) -{ - JSON_LexNumberState_Whole, - JSON_LexNumberState_Fraction, - JSON_LexNumberState_Exponent -}; - -Global Readonly String JSON_keyword_strings[] = { - ['t'] = CompLit("true"), - ['f'] = CompLit("false"), - ['n'] = CompLit("null") -}; - -Global Readonly JSON_TokenKind JSON_keyword_types[] = { - ['t'] = JSON_TokenKind_KeywordTrue, - ['f'] = JSON_TokenKind_KeywordFalse, - ['n'] = JSON_TokenKind_KeywordNull -}; - -//////////////////////////////////////////////////////////// -//~ Parser types - -Struct(JSON_Parser) -{ - // Input - String src; - JSON_Token *at; - - // Output - JSON_Blob *root; - JSON_ErrorList errors; -}; - -//////////////////////////////////////////////////////////// -//~ Lex - -JSON_Token *JSON_PushToken(Arena *arena, JSON_TokenList *list); -JSON_TokenList JSON_TokensFromString(Arena *arena, String src); - -//////////////////////////////////////////////////////////// -//~ Interpret - -f64 interpret_number(String src); -String interpret_string(Arena *arena, String src, String *error); - -//////////////////////////////////////////////////////////// -//~ Parse - -void JSON_PushError(Arena *arena, JSON_Parser *p, JSON_Token *t, String msg); -void JSON_Parse(Arena *arena, JSON_Parser *p); -JSON_Result JSON_BlobFromString(Arena *arena, String src); diff --git a/src/json/json.lay b/src/json/json.lay deleted file mode 100644 index f382c1d9..00000000 --- a/src/json/json.lay +++ /dev/null @@ -1,16 +0,0 @@ -@Layer json - -////////////////////////////// -//- Dependencies - -@Dep base - -////////////////////////////// -//- Api - -@IncludeC json_core.h - -////////////////////////////// -//- Impl - -@IncludeC json_core.c diff --git a/src/mp3/mp3.h b/src/mp3/mp3.h deleted file mode 100644 index 3c92ee95..00000000 --- a/src/mp3/mp3.h +++ /dev/null @@ -1,20 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Mp3 types - -Enum(MP3_DecodeFlag) -{ - MP3_DecodeFlag_None = 0, - MP3_DecodeFlag_Stereo = (1 << 0), -}; - -Struct(MP3_Result) -{ - u64 samples_count; - i16 *samples; - b32 ok; -}; - -//////////////////////////////////////////////////////////// -//~ Mp3 - -MP3_Result MP3_Decode(Arena *arena, String encoded, u32 sample_rate, MP3_DecodeFlag flags); diff --git a/src/mp3/mp3.lay b/src/mp3/mp3.lay deleted file mode 100644 index 8750eaa3..00000000 --- a/src/mp3/mp3.lay +++ /dev/null @@ -1,11 +0,0 @@ -@Layer mp3 - -////////////////////////////// -//- Api - -@IncludeC mp3.h - -////////////////////////////// -//- Impl - -@DefaultDownstream mp3_mmf diff --git a/src/mp3/mp3_mmf/mp3_mmf.c b/src/mp3/mp3_mmf/mp3_mmf.c deleted file mode 100644 index edb5b402..00000000 --- a/src/mp3/mp3_mmf/mp3_mmf.c +++ /dev/null @@ -1,120 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Decode - -MP3_Result MP3_Decode(Arena *arena, String encoded, u32 sample_rate, MP3_DecodeFlag flags) -{ - MP3_Result result = ZI; - u64 bytes_per_sample = 2; - - u64 channel_count; - u32 channel_mask; - if (flags & MP3_DecodeFlag_Stereo) - { - channel_count = 2; - channel_mask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; - } - else - { - channel_count = 1; - channel_mask = SPEAKER_FRONT_CENTER; - } - - ////////////////////////////// - //- Startup - - MFStartup(MF_VERSION, MFSTARTUP_LITE); - - // Create IStream from encoded string - IStream *i_stream = SHCreateMemStream(encoded.text, encoded.len); - - // Create IMFByteStream from IStream - IMFByteStream *byte_stream = 0; - MFCreateMFByteStreamOnStream(i_stream, &byte_stream); - - // Create reader from IMFByteStream - IMFSourceReader *reader; - MFCreateSourceReaderFromByteStream(byte_stream, 0, &reader); - - ////////////////////////////// - //- Get media type - - // Read only first audio stream - IMFSourceReader_SetStreamSelection(reader, (DWORD)MF_SOURCE_READER_ALL_STREAMS, 0); - IMFSourceReader_SetStreamSelection(reader, (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 1); - - WAVEFORMATEXTENSIBLE format = { - .Format = { - .wFormatTag = WAVE_FORMAT_EXTENSIBLE, - .nChannels = (WORD)channel_count, - .nSamplesPerSec = (WORD)sample_rate, - .nAvgBytesPerSec = (DWORD)(sample_rate * channel_count * bytes_per_sample), - .nBlockAlign = (WORD)(channel_count * bytes_per_sample), - .wBitsPerSample = (WORD)(8 * bytes_per_sample), - .cbSize = sizeof(format) - sizeof(format.Format) - }, - .Samples.wValidBitsPerSample = 8 * bytes_per_sample, - .dwChannelMask = channel_mask, - .SubFormat = MEDIASUBTYPE_PCM - }; - - // Media Foundation in Windows 8+ allows reader to convert output to different format than native - IMFMediaType *type; - MFCreateMediaType(&type); - MFInitMediaTypeFromWaveFormatEx(type, &format.Format, sizeof(format)); - IMFSourceReader_SetCurrentMediaType(reader, (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, type); - IMFMediaType_Release(type); - - ////////////////////////////// - //- Read - - result.samples = ArenaNext(arena, i16); - u64 sample_bytes_read = 0; - for (;;) - { - IMFSample *sample; - DWORD sample_flags = 0; - HRESULT hr = IMFSourceReader_ReadSample(reader, (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, 0, &sample_flags, 0, &sample); - if (FAILED(hr)) - { - break; - } - - // Check if done - if (sample_flags & MF_SOURCE_READERF_ENDOFSTREAM) - { - result.ok = 1; - break; - } - Assert(sample_flags == 0); - - // Read samples - IMFMediaBuffer *buffer; - IMFSample_ConvertToContiguousBuffer(sample, &buffer); - - BYTE *src; - DWORD size_bytes; - IMFMediaBuffer_Lock(buffer, &src, 0, &size_bytes); - { - i16 *dst = PushStructsNoZero(arena, i16, (size_bytes + 1) >> 1); - CopyBytes(dst, src, size_bytes); - sample_bytes_read += size_bytes; - } - IMFMediaBuffer_Unlock(buffer); - - IMediaBuffer_Release(buffer); - IMFSample_Release(sample); - } - - result.samples_count = sample_bytes_read / bytes_per_sample; - - ////////////////////////////// - //- Cleanup - - IMFSourceReader_Release(reader); - IMFByteStream_Close(byte_stream); - // FIXME: Enable this - //IStream_Release(i_stream); - MFShutdown(); - - return result; -} diff --git a/src/mp3/mp3_mmf/mp3_mmf.h b/src/mp3/mp3_mmf/mp3_mmf.h deleted file mode 100644 index 145eaec8..00000000 --- a/src/mp3/mp3_mmf/mp3_mmf.h +++ /dev/null @@ -1,14 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Windows headers - -#pragma warning(push, 0) - #include - #include - #include - #include - #include -#pragma warning(pop) - -#pragma comment(lib, "mfplat") -#pragma comment(lib, "mfreadwrite") -#pragma comment(lib, "shlwapi") diff --git a/src/mp3/mp3_mmf/mp3_mmf.lay b/src/mp3/mp3_mmf/mp3_mmf.lay deleted file mode 100644 index ee14e124..00000000 --- a/src/mp3/mp3_mmf/mp3_mmf.lay +++ /dev/null @@ -1,11 +0,0 @@ -@Layer mp3_mmf - -////////////////////////////// -//- Api - -@IncludeC mp3_mmf.h - -////////////////////////////// -//- Impl - -@IncludeC mp3_mmf.c diff --git a/src/sound/sound.c b/src/sound/sound.c deleted file mode 100644 index 41b30b7d..00000000 --- a/src/sound/sound.c +++ /dev/null @@ -1,133 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Load job - -JobImpl(SND_Load, sig, id) -{ - TempArena scratch = BeginScratchNoConflict(); - ResourceKey resource = sig->resource; - String name = NameFromResource(resource); - AC_Asset *asset = sig->asset; - SND_SoundFlag flags = sig->flags; - - LogInfoF("Loading sound \"%F\"", FmtString(name)); - i64 start_ns = TimeNs(); - - String error_msg = Lit("Unknown error"); - - // Decode - MP3_Result decoded = ZI; - String resource_data = DataFromResource(resource); - if (resource_data.len > 0) - { - u64 decode_flags = 0; - if (flags & SND_SoundFlag_Stereo) - { - decode_flags |= MP3_DecodeFlag_Stereo; - } - decoded = MP3_Decode(scratch.arena, resource_data, SND_SampleRate, decode_flags); - if (!decoded.ok) - { - error_msg = Lit("Failed to decode sound file"); - } - } - else - { - error_msg = Lit("Missing resource data"); - } - - if (decoded.ok) - { - // Store - SND_Sound *sound = 0; - u64 samples_count = decoded.samples_count; - i16 *samples = 0; - { - AC_Store store = AC_OpenStore(); - sound = PushStructNoZero(store.arena, SND_Sound); - samples = PushStructsNoZero(store.arena, i16, samples_count); - AC_CloseStore(&store); - } - - // Initialize - ZeroStruct(sound); - sound->flags = flags; - sound->samples_count = samples_count; - sound->samples = samples; - CopyBytes(sound->samples, decoded.samples, decoded.samples_count * sizeof(*decoded.samples)); - - LogSuccessF("Loaded sound \"%F\" in %F seconds", FmtString(name), FmtFloat(SecondsFromNs(TimeNs() - start_ns))); - AC_MarkReady(asset, sound); - } - else - { - LogErrorF("Error loading sound \"%F\": %F", FmtString(name), FmtString(error_msg)); - - // Store - SND_Sound *sound = 0; - { - AC_Store store = AC_OpenStore(); - sound = PushStructNoZero(store.arena, SND_Sound); - AC_CloseStore(&store); - } - *sound = (SND_Sound) { 0 }; - AC_MarkReady(asset, sound); - } - - EndScratch(scratch); -} - -//////////////////////////////////////////////////////////// -//~ Load sound - -AC_Asset *SND_LoadAsset(ResourceKey resource, SND_SoundFlag flags, b32 wait) -{ - TempArena scratch = BeginScratchNoConflict(); - - // Generate and append sound flags to name key - String name = NameFromResource(resource); - String key = StringF( - scratch.arena, - "%F%F_sound", - FmtString(name), - FmtUint((u64)flags) - ); - u64 hash = AC_HashFromKey(key); - b32 is_first_touch; - AC_Asset *asset = AC_TouchCache(key, hash, &is_first_touch); - - if (is_first_touch) - { - AC_MarkLoading(asset); - { - Job *job = OpenJob(SND_Load, AsyncPool()); - SND_Load_Sig *sig = PushStruct(job->arena, SND_Load_Sig); - job->sig = sig; - sig->resource = resource; - sig->asset = asset; - sig->flags = flags; - CloseJob(job); - } - if (wait) - { - AC_YieldOnAssetReady(asset); - } - } - - EndScratch(scratch); - return asset; -} - -SND_Sound *SND_LoadSoundAsync(ResourceKey resource, SND_SoundFlag flags) -{ - AC_Asset *asset = SND_LoadAsset(resource, flags, 0); - SND_Sound *sound = (SND_Sound *)AC_DataFromStore(asset); - return sound; -} - -SND_Sound *SND_LoadSoundWait(ResourceKey resource, SND_SoundFlag flags) -{ - AC_Asset *asset = SND_LoadAsset(resource, flags, 1); - AC_YieldOnAssetReady(asset); - SND_Sound *sound = (SND_Sound *)AC_DataFromStore(asset); - return sound; -} diff --git a/src/sound/sound.h b/src/sound/sound.h deleted file mode 100644 index 20e4710d..00000000 --- a/src/sound/sound.h +++ /dev/null @@ -1,25 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Sound types - -#define SND_SampleRate 48000 - -Enum(SND_SoundFlag) -{ - SND_SoundFlag_None = 0, - SND_SoundFlag_Stereo = (1 << 0) -}; - -Struct(SND_Sound) -{ - SND_SoundFlag flags; - u64 samples_count; - i16 *samples; -}; - -//////////////////////////////////////////////////////////// -//~ Load sound - -JobDecl(SND_Load, { SND_SoundFlag flags; AC_Asset *asset; ResourceKey resource; }); -AC_Asset *SND_LoadAsset(ResourceKey resource, SND_SoundFlag flags, b32 wait); -SND_Sound *SND_LoadSoundAsync(ResourceKey resource, SND_SoundFlag flags); -SND_Sound *SND_LoadSoundWait(ResourceKey resource, SND_SoundFlag flags); diff --git a/src/sound/sound.lay b/src/sound/sound.lay deleted file mode 100644 index e638c7b1..00000000 --- a/src/sound/sound.lay +++ /dev/null @@ -1,18 +0,0 @@ -@Layer sound - -////////////////////////////// -//- Dependencies - -@Dep platform -@Dep mp3 -@Dep asset_cache - -////////////////////////////// -//- Api - -@IncludeC sound.h - -////////////////////////////// -//- Impl - -@IncludeC sound.c