remove unused layers

This commit is contained in:
jacob 2026-04-06 12:19:39 -05:00
parent 9d4e74f0b6
commit 2561d162f5
18 changed files with 2 additions and 3444 deletions

View File

@ -5,7 +5,7 @@ Run `build.bat` from a terminal with MSVC/Clang available.
### Codebase structure ### 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: Other than `.lay` files, there are three types of source files present:
* `.c`/`.h` C code * `.c`/`.h` C code
@ -14,4 +14,4 @@ Other than `.lay` files, there are three types of source files present:
### Media ### Media
--- ---
For project media, see https://projects.cagori.com/#power-play For project media, see https://projects.cagori.com/#power-play

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -1,11 +0,0 @@
@Layer collider
//////////////////////////////
//- Api
@IncludeC collider.h
//////////////////////////////
//- Impl
@IncludeC collider.c

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -1,16 +0,0 @@
@Layer json
//////////////////////////////
//- Dependencies
@Dep base
//////////////////////////////
//- Api
@IncludeC json_core.h
//////////////////////////////
//- Impl
@IncludeC json_core.c

View File

@ -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);

View File

@ -1,11 +0,0 @@
@Layer mp3
//////////////////////////////
//- Api
@IncludeC mp3.h
//////////////////////////////
//- Impl
@DefaultDownstream mp3_mmf

View File

@ -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;
}

View File

@ -1,14 +0,0 @@
////////////////////////////////////////////////////////////
//~ Windows headers
#pragma warning(push, 0)
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <Shlwapi.h>
#include <objidl.h>
#pragma warning(pop)
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "shlwapi")

View File

@ -1,11 +0,0 @@
@Layer mp3_mmf
//////////////////////////////
//- Api
@IncludeC mp3_mmf.h
//////////////////////////////
//- Impl
@IncludeC mp3_mmf.c

View File

@ -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;
}

View File

@ -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);

View File

@ -1,18 +0,0 @@
@Layer sound
//////////////////////////////
//- Dependencies
@Dep platform
@Dep mp3
@Dep asset_cache
//////////////////////////////
//- Api
@IncludeC sound.h
//////////////////////////////
//- Impl
@IncludeC sound.c