You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1047 lines
23 KiB
C

// The Digital Grove Codebase
// Copyright (c) Ryan Fleury. All rights reserved.
////////////////////////////////
//~ rjf: C Runtime Implementations
void *
memset(void *buffer, int c, UAddr n)
{
for(UAddr off = 0; off < n; off += 1)
{
((U8 *)buffer)[off] = c;
}
return buffer;
}
void *
memcpy(void *dst, void *src, UAddr n)
{
for(UAddr off = 0; off < n; off += 1)
{
((U8 *)dst)[off] = ((U8 *)src)[off];
}
return dst;
}
////////////////////////////////
//~ rjf: Scalar Math Functions
function F32
SinF32(F32 turns)
{
// NOTE(rjf): "normalized frequency" implementation from https://mooooo.ooo/chebyshev-sine-approximation/.
//
// this implementation computes sin(pi*x) - in our case, we want turns, so we
// just adjust x first. at some point, would be better to just compute the
// coefficients for sin(2*pi*x) rather than sin(pi*x), but I don't know how
// to do that, and so I am just letting this ride for now.
//
F32 x_unwrapped = turns * 2.f;
F32 x = ModF32(x_unwrapped + 1.f, 2.f) - 1.f;
F32 x2 = x*x;
F32 p = 0.000385937753182769f;
p += -0.006860187425683514f; p *= x2;
p += 0.0751872634325299f; p *= x2;
p += -0.5240361513980939f; p *= x2;
p += 2.0261194642649887f; p *= x2;
p += -3.1415926444234477f;
F32 result = (x-1.f) * (x+1.f) * p * x;
return result;
}
////////////////////////////////
//~ rjf: Vector Functions
//- rjf: constructors
function Vec2U32
V2U32(U32 x, U32 y)
{
Vec2U32 v = {x, y};
return v;
}
function Vec2F32
V2F32(F32 x, F32 y)
{
Vec2F32 v = {x, y};
return v;
}
function Vec3F32
V3F32(F32 x, F32 y, F32 z)
{
Vec3F32 v = {x, y, z};
return v;
}
function Vec4F32
V4F32(F32 x, F32 y, F32 z, F32 w)
{
Vec4F32 v = {x, y, z, w};
return v;
}
//- rjf: 2-vector ops
function Vec2F32
Add2F32(Vec2F32 l, Vec2F32 r)
{
Vec2F32 result = {l.x+r.x, l.y+r.y};
return result;
}
function Vec2F32
Sub2F32(Vec2F32 l, Vec2F32 r)
{
Vec2F32 result = {l.x-r.x, l.y-r.y};
return result;
}
function Vec2F32
Mul2F32(Vec2F32 l, Vec2F32 r)
{
Vec2F32 result = {l.x*r.x, l.y*r.y};
return result;
}
function Vec2F32
Div2F32(Vec2F32 l, Vec2F32 r)
{
Vec2F32 result = {l.x/r.x, l.y/r.y};
return result;
}
function F32
LengthSquared2F32(Vec2F32 v)
{
F32 result = Dot2F32(v, v);
return result;
}
function F32
Length2F32(Vec2F32 v)
{
F32 result_squared = LengthSquared2F32(v);
F32 result = SquareRootF32(result_squared);
return result;
}
function Vec2F32
Scale2F32(Vec2F32 v, F32 s)
{
Vec2F32 v_scaled = {v.x*s, v.y*s};
return v_scaled;
}
function Vec2F32
Normalize2F32(Vec2F32 v)
{
F32 v_length = Length2F32(v);
Vec2F32 v_normalized = Scale2F32(v, 1.f / v_length);
return v_normalized;
}
function F32
Dot2F32(Vec2F32 l, Vec2F32 r)
{
F32 result = (l.x*r.x + l.y*r.y);
return result;
}
//- rjf: 3-vector ops
function Vec3F32
Add3F32(Vec3F32 l, Vec3F32 r)
{
Vec3F32 result = {l.x+r.x, l.y+r.y, l.z+r.z};
return result;
}
function Vec3F32
Sub3F32(Vec3F32 l, Vec3F32 r)
{
Vec3F32 result = {l.x-r.x, l.y-r.y, l.z-r.z};
return result;
}
function Vec3F32
Mul3F32(Vec3F32 l, Vec3F32 r)
{
Vec3F32 result = {l.x*r.x, l.y*r.y, l.z*r.z};
return result;
}
function Vec3F32
Div3F32(Vec3F32 l, Vec3F32 r)
{
Vec3F32 result = {l.x/r.x, l.y/r.y, l.z/r.z};
return result;
}
function F32
LengthSquared3F32(Vec3F32 v)
{
F32 result = Dot3F32(v, v);
return result;
}
function F32
Length3F32(Vec3F32 v)
{
F32 result_squared = LengthSquared3F32(v);
F32 result = SquareRootF32(result_squared);
return result;
}
function Vec3F32
Scale3F32(Vec3F32 v, F32 s)
{
Vec3F32 v_scaled = {v.x*s, v.y*s, v.z*s};
return v_scaled;
}
function Vec3F32
Normalize3F32(Vec3F32 v)
{
F32 v_length = Length3F32(v);
Vec3F32 v_normalized = Scale3F32(v, v_length > 0 ? (1.f/v_length) : 0);
return v_normalized;
}
function F32
Dot3F32(Vec3F32 l, Vec3F32 r)
{
F32 result = (l.x*r.x + l.y*r.y + l.z*r.z);
return result;
}
function Vec3F32
Cross3F32(Vec3F32 l, Vec3F32 r)
{
Vec3F32 result = {l.y*r.z - l.z*r.y, l.z*r.x - l.x*r.z, l.x*r.y - l.y*r.x};
return result;
}
//- rjf: 4-vector ops
function Vec4F32
Add4F32(Vec4F32 l, Vec4F32 r)
{
Vec4F32 result = {l.x+r.x, l.y+r.y, l.z+r.z, l.w+r.w};
return result;
}
function Vec4F32
Sub4F32(Vec4F32 l, Vec4F32 r)
{
Vec4F32 result = {l.x-r.x, l.y-r.y, l.z-r.z, l.w-r.w};
return result;
}
function Vec4F32
Mul4F32(Vec4F32 l, Vec4F32 r)
{
Vec4F32 result = {l.x*r.x, l.y*r.y, l.z*r.z, l.w*r.w};
return result;
}
function Vec4F32
Div4F32(Vec4F32 l, Vec4F32 r)
{
Vec4F32 result = {l.x/r.x, l.y/r.y, l.z/r.z, l.w/r.w};
return result;
}
function F32
LengthSquared4F32(Vec4F32 v)
{
F32 result = Dot4F32(v, v);
return result;
}
function F32
Length4F32(Vec4F32 v)
{
F32 result_squared = LengthSquared4F32(v);
F32 result = SquareRootF32(result_squared);
return result;
}
function Vec4F32
Scale4F32(Vec4F32 v, F32 s)
{
Vec4F32 v_scaled = {v.x*s, v.y*s, v.z*s};
return v_scaled;
}
function Vec4F32
Normalize4F32(Vec4F32 v)
{
F32 v_length = Length4F32(v);
Vec4F32 v_normalized = Scale4F32(v, 1.f / v_length);
return v_normalized;
}
function F32
Dot4F32(Vec4F32 l, Vec4F32 r)
{
F32 result = l.x*r.x + l.y*r.y + l.z*r.z + l.w*r.w;
return result;
}
function Vec4F32
XForm4F32(Mat4x4F32 m, Vec4F32 v)
{
Vec4F32 result;
for(int i = 0; i < 4; i += 1)
{
result.v[i] = (v.v[0]*m.v[0][i] +
v.v[1]*m.v[1][i] +
v.v[2]*m.v[2][i] +
v.v[3]*m.v[3][i]);
}
return result;
}
////////////////////////////////
//~ rjf: Matrix Functions
function Mat3x3F32
MakeMat3x3F32(F32 d)
{
Mat3x3F32 result =
{
{
{d, 0, 0},
{0, d, 0},
{0, 0, d},
},
};
return result;
}
function Mat3x3F32
MakeTranslate3x3F32(Vec2F32 translation)
{
Mat3x3F32 result = MakeMat3x3F32(1.f);
result.v[2][0] = translation.x;
result.v[2][1] = translation.y;
return result;
}
function Mat3x3F32
MakeScale3x3F32(Vec2F32 scale)
{
Mat3x3F32 result = MakeMat3x3F32(1.f);
result.v[0][0] = scale.x;
result.v[1][1] = scale.y;
return result;
}
function Mat3x3F32
MakeRotate3x3F32(F32 turns)
{
Mat3x3F32 result = MakeMat3x3F32(1.f);
result.v[0][0] = +CosF32(turns);
result.v[1][0] = -SinF32(turns);
result.v[0][1] = +SinF32(turns);
result.v[1][1] = +CosF32(turns);
return result;
}
function Mat4x4F32
MakeMat4x4F32(F32 d)
{
Mat4x4F32 result =
{
{
{d, 0, 0, 0},
{0, d, 0, 0},
{0, 0, d, 0},
{0, 0, 0, d},
}
};
return result;
}
function Mat4x4F32
MakeTranslate4x4F32(Vec3F32 translation)
{
Mat4x4F32 result = MakeMat4x4F32(1.f);
result.v[3][0] = translation.x;
result.v[3][1] = translation.y;
result.v[3][2] = translation.z;
return result;
}
function Mat4x4F32
MakeScale4x4F32(Vec3F32 scale)
{
Mat4x4F32 result = MakeMat4x4F32(1.f);
result.v[0][0] = scale.x;
result.v[1][1] = scale.y;
result.v[2][2] = scale.z;
return result;
}
function Mat4x4F32
MakePerspective4x4F32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z)
{
Mat4x4F32 result = MakeMat4x4F32(1.f);
F32 tan_theta_over_2 = TanF32(fov / 2);
result.v[0][0] = 1.f / tan_theta_over_2;
result.v[1][1] = aspect_ratio / tan_theta_over_2;
result.v[2][3] = 1.f;
result.v[2][2] = -(near_z + far_z) / (near_z - far_z);
result.v[3][2] = (2.f * near_z * far_z) / (near_z - far_z);
result.v[3][3] = 0.f;
return result;
}
function Mat4x4F32
MakeOrthographic4x4F32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z)
{
Mat4x4F32 result = MakeMat4x4F32(1.f);
result.v[0][0] = 2.f / (right - left);
result.v[1][1] = 2.f / (top - bottom);
result.v[2][2] = 2.f / (far_z - near_z);
result.v[3][3] = 1.f;
result.v[3][0] = (left + right) / (left - right);
result.v[3][1] = (bottom + top) / (bottom - top);
result.v[3][2] = (near_z + far_z) / (near_z - far_z);
return result;
}
function Mat4x4F32
MakeLookAt4x4F32(Vec3F32 eye, Vec3F32 center, Vec3F32 up)
{
Mat4x4F32 result;
Vec3F32 f = Normalize3F32(Sub3F32(eye, center));
Vec3F32 s = Normalize3F32(Cross3F32(f, up));
Vec3F32 u = Cross3F32(s, f);
result.v[0][0] = s.x;
result.v[0][1] = u.x;
result.v[0][2] = -f.x;
result.v[0][3] = 0.0f;
result.v[1][0] = s.y;
result.v[1][1] = u.y;
result.v[1][2] = -f.y;
result.v[1][3] = 0.0f;
result.v[2][0] = s.z;
result.v[2][1] = u.z;
result.v[2][2] = -f.z;
result.v[2][3] = 0.0f;
result.v[3][0] = -Dot3F32(s, eye);
result.v[3][1] = -Dot3F32(u, eye);
result.v[3][2] = Dot3F32(f, eye);
result.v[3][3] = 1.0f;
return result;
}
function Mat4x4F32
MakeRotate4x4F32(Vec3F32 axis, F32 turns)
{
Mat4x4F32 result = MakeMat4x4F32(1.f);
axis = Normalize3F32(axis);
F32 sin_theta = SinF32(turns);
F32 cos_theta = CosF32(turns);
F32 cos_value = 1.f - cos_theta;
result.v[0][0] = (axis.x * axis.x * cos_value) + cos_theta;
result.v[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta);
result.v[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta);
result.v[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta);
result.v[1][1] = (axis.y * axis.y * cos_value) + cos_theta;
result.v[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta);
result.v[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta);
result.v[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta);
result.v[2][2] = (axis.z * axis.z * cos_value) + cos_theta;
return result;
}
function Mat3x3F32
Mul3x3F32(Mat3x3F32 a, Mat3x3F32 b)
{
Mat3x3F32 c = {0};
for(int j = 0; j < 3; j += 1)
{
for(int i = 0; i < 3; i += 1)
{
c.v[i][j] = (a.v[0][j]*b.v[i][0] +
a.v[1][j]*b.v[i][1] +
a.v[2][j]*b.v[i][2]);
}
}
return c;
}
function Mat3x3F32
Scale3x3F32(Mat3x3F32 m, F32 scale)
{
for(int j = 0; j < 3; j += 1)
{
for(int i = 0; i < 3; i += 1)
{
m.v[i][j] *= scale;
}
}
return m;
}
function Mat4x4F32
Mul4x4F32(Mat4x4F32 a, Mat4x4F32 b)
{
Mat4x4F32 c = {0};
for(int j = 0; j < 4; j += 1)
{
for(int i = 0; i < 4; i += 1)
{
c.v[i][j] = (a.v[0][j]*b.v[i][0] +
a.v[1][j]*b.v[i][1] +
a.v[2][j]*b.v[i][2] +
a.v[3][j]*b.v[i][3]);
}
}
return c;
}
function Mat4x4F32
Scale4x4F32(Mat4x4F32 m, F32 scale)
{
for(int j = 0; j < 4; j += 1)
{
for(int i = 0; i < 4; i += 1)
{
m.v[i][j] *= scale;
}
}
return m;
}
function Mat4x4F32
Inverse4x4F32(Mat4x4F32 m)
{
F32 coef00 = m.v[2][2] * m.v[3][3] - m.v[3][2] * m.v[2][3];
F32 coef02 = m.v[1][2] * m.v[3][3] - m.v[3][2] * m.v[1][3];
F32 coef03 = m.v[1][2] * m.v[2][3] - m.v[2][2] * m.v[1][3];
F32 coef04 = m.v[2][1] * m.v[3][3] - m.v[3][1] * m.v[2][3];
F32 coef06 = m.v[1][1] * m.v[3][3] - m.v[3][1] * m.v[1][3];
F32 coef07 = m.v[1][1] * m.v[2][3] - m.v[2][1] * m.v[1][3];
F32 coef08 = m.v[2][1] * m.v[3][2] - m.v[3][1] * m.v[2][2];
F32 coef10 = m.v[1][1] * m.v[3][2] - m.v[3][1] * m.v[1][2];
F32 coef11 = m.v[1][1] * m.v[2][2] - m.v[2][1] * m.v[1][2];
F32 coef12 = m.v[2][0] * m.v[3][3] - m.v[3][0] * m.v[2][3];
F32 coef14 = m.v[1][0] * m.v[3][3] - m.v[3][0] * m.v[1][3];
F32 coef15 = m.v[1][0] * m.v[2][3] - m.v[2][0] * m.v[1][3];
F32 coef16 = m.v[2][0] * m.v[3][2] - m.v[3][0] * m.v[2][2];
F32 coef18 = m.v[1][0] * m.v[3][2] - m.v[3][0] * m.v[1][2];
F32 coef19 = m.v[1][0] * m.v[2][2] - m.v[2][0] * m.v[1][2];
F32 coef20 = m.v[2][0] * m.v[3][1] - m.v[3][0] * m.v[2][1];
F32 coef22 = m.v[1][0] * m.v[3][1] - m.v[3][0] * m.v[1][1];
F32 coef23 = m.v[1][0] * m.v[2][1] - m.v[2][0] * m.v[1][1];
Vec4F32 fac0 = { coef00, coef00, coef02, coef03 };
Vec4F32 fac1 = { coef04, coef04, coef06, coef07 };
Vec4F32 fac2 = { coef08, coef08, coef10, coef11 };
Vec4F32 fac3 = { coef12, coef12, coef14, coef15 };
Vec4F32 fac4 = { coef16, coef16, coef18, coef19 };
Vec4F32 fac5 = { coef20, coef20, coef22, coef23 };
Vec4F32 vec0 = { m.v[1][0], m.v[0][0], m.v[0][0], m.v[0][0] };
Vec4F32 vec1 = { m.v[1][1], m.v[0][1], m.v[0][1], m.v[0][1] };
Vec4F32 vec2 = { m.v[1][2], m.v[0][2], m.v[0][2], m.v[0][2] };
Vec4F32 vec3 = { m.v[1][3], m.v[0][3], m.v[0][3], m.v[0][3] };
Vec4F32 inv0 = Add4F32(Sub4F32(Mul4F32(vec1, fac0), Mul4F32(vec2, fac1)), Mul4F32(vec3, fac2));
Vec4F32 inv1 = Add4F32(Sub4F32(Mul4F32(vec0, fac0), Mul4F32(vec2, fac3)), Mul4F32(vec3, fac4));
Vec4F32 inv2 = Add4F32(Sub4F32(Mul4F32(vec0, fac1), Mul4F32(vec1, fac3)), Mul4F32(vec3, fac5));
Vec4F32 inv3 = Add4F32(Sub4F32(Mul4F32(vec0, fac2), Mul4F32(vec1, fac4)), Mul4F32(vec2, fac5));
Vec4F32 sign_a = { +1, -1, +1, -1 };
Vec4F32 sign_b = { -1, +1, -1, +1 };
Mat4x4F32 inverse;
for(U32 i = 0; i < 4; i += 1)
{
inverse.v[0][i] = inv0.v[i] * sign_a.v[i];
inverse.v[1][i] = inv1.v[i] * sign_b.v[i];
inverse.v[2][i] = inv2.v[i] * sign_a.v[i];
inverse.v[3][i] = inv3.v[i] * sign_b.v[i];
}
Vec4F32 row0 = { inverse.v[0][0], inverse.v[1][0], inverse.v[2][0], inverse.v[3][0] };
Vec4F32 m0 = { m.v[0][0], m.v[0][1], m.v[0][2], m.v[0][3] };
Vec4F32 dot0 = Mul4F32(m0, row0);
F32 dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w);
F32 one_over_det = 1 / dot1;
return Scale4x4F32(inverse, one_over_det);
}
function Mat4x4F32
RemoveRotation4x4F32(Mat4x4F32 mat)
{
Vec3F32 scale =
{
Length3F32(V3F32(mat.v[0][0], mat.v[0][1], mat.v[0][2])),
Length3F32(V3F32(mat.v[1][0], mat.v[1][1], mat.v[1][2])),
Length3F32(V3F32(mat.v[2][0], mat.v[2][1], mat.v[2][2])),
};
mat.v[0][0] = scale.x;
mat.v[1][0] = 0.f;
mat.v[2][0] = 0.f;
mat.v[0][1] = 0.f;
mat.v[1][1] = scale.y;
mat.v[2][1] = 0.f;
mat.v[0][2] = 0.f;
mat.v[1][2] = 0.f;
mat.v[2][2] = scale.z;
return mat;
}
////////////////////////////////
//~ rjf: Arenas
function Arena *
ArenaMakeStatic(U8 *buffer, UAddr buffer_size)
{
Arena *arena = 0;
if(buffer_size >= sizeof(Arena))
{
arena = (Arena *)buffer;
arena->cap = buffer_size;
arena->pos = sizeof(Arena);
}
return arena;
}
function void *
ArenaPush(Arena *arena, UAddr size, UAddr align)
{
void *result = 0;
{
UAddr addr = (UAddr)((U8 *)arena + arena->pos);
UAddr addr_aligned = (addr + align - 1) & (~(align - 1));
UAddr addr_aligned_pushed = addr_aligned + size;
UAddr addr_max = (UAddr)((U8 *)arena + arena->cap);
if(addr_aligned_pushed <= addr_max)
{
result = (void *)addr_aligned;
arena->pos += (addr_aligned_pushed - addr);
}
}
return result;
}
function UAddr
ArenaPos(Arena *arena)
{
return arena->pos;
}
function void
ArenaPopTo(Arena *arena, UAddr pos)
{
UAddr pos_popped = pos;
if(pos_popped < sizeof(Arena))
{
pos_popped = sizeof(Arena);
}
arena->pos = pos_popped;
}
function void
ArenaClear(Arena *arena)
{
ArenaPopTo(arena, 0);
}
function void
ArenaPop(Arena *arena, UAddr amt)
{
if(ArenaPos(arena) >= amt)
{
ArenaPopTo(arena, ArenaPos(arena) - amt);
}
}
////////////////////////////////
//~ rjf: Arena Temporary Scopes
function Temp
TempBegin(Arena *arena)
{
Temp temp = {arena, ArenaPos(arena)};
return temp;
}
function void
TempEnd(Temp temp)
{
ArenaPopTo(temp.arena, temp.pos);
}
////////////////////////////////
//~ rjf: Thread Context
//- rjf: Thread Context Helpers
function Arena *
GetScratch(Arena *conflict)
{
Arena *result = 0;
ThreadCtx *tctx = GetThreadCtx();
for EachElement(idx, tctx->scratch_arenas)
{
if(tctx->scratch_arenas[idx] != conflict)
{
result = tctx->scratch_arenas[idx];
break;
}
}
return result;
}
////////////////////////////////
//~ rjf: Strings
function UAddr
CStr8Size(char *cstr)
{
UAddr result = 0;
for(;cstr[result]; result += 1);
return result;
}
function String8
Str8(U8 *str, UAddr size)
{
String8 result = {str, size};
return result;
}
function String8
Str8FV(Arena *arena, char *fmt, va_list args)
{
String8 result = {0};
if(fmt != 0)
{
for(B32 write = 0; write <= 1; write += 1)
{
//- rjf: iterate bytes & format specifiers / arguments, compute string size or fill string
UAddr string_size = 0;
{
va_list args2;
va_copy(args2, args);
for(UAddr off = 0; fmt[off] != 0;)
{
//- rjf: % -> format specifier
if(fmt[off] == '%')
{
U64 int_val = 0;
U8 radix = 10;
B32 uppercase = 0;
B32 treat_as_signed = 0;
B32 leading_zero = 0;
U32 leading_count = 0;
//- rjf: advance past %
off += 1;
//- rjf: parse modifiers
for(B32 done = 0; !done;)
{
switch(fmt[off])
{
default:{done = 1;}break;
case '0':
{
off += 1;
leading_zero = 1;
// TODO(rjf): parse leading count
}break;
// TODO(rjf): case '-': left-justify
// TODO(rjf): case '+': leading plus
// TODO(rjf): case ' ': leading space
// TODO(rjf): case '#": leading 0x
// TODO(rjf): case '\'': digit separator commas
// TODO(rjf): case '$': kilo marker
// TODO(rjf): case '_': no space between metric suffix and number
// TODO(rjf): case '0': leading zero
}
if(!done)
{
off += 1;
}
}
//- rjf: do replacement
switch(fmt[off])
{
//- rjf: %% -> escaped %
case '%':
{
off += 1;
if(write)
{
result.str[string_size] = '%';
}
string_size += 1;
}break;
//- rjf: %s -> c string
case 's':
{
off += 1;
char *cstr = va_arg(args, char *);
UAddr cstr_size = CStr8Size(cstr);
if(write)
{
MemoryCopy(result.str, cstr, cstr_size);
}
string_size += cstr_size;
}break;
//- rjf: %S -> string
case 'S':
{
off += 1;
String8 string = va_arg(args, String8);
if(write)
{
MemoryCopy(result.str, string.str, string.size);
}
string_size += string.size;
}break;
//- rjf: %i<N>[s|u|x|X|o|O|b] -> N-wide integer, [s]igned, [u]nsigned, or he[x], or [o]ctal, or [b]inary
case 'i':
case 'I':
{
off += 1;
if(fmt[off] == '3' && fmt[off+1] == '2')
{
int_val = VariadicU32(args2);
off += 2;
}
else if(fmt[off] == '6' && fmt[off+1] == '4')
{
int_val = VariadicU64(args2);
off += 2;
}
else
{
int_val = VariadicInt(args2);
}
switch(fmt[off])
{
default:{treat_as_signed = 1; radix = 10;}break;
case 's':{off += 1; radix = 10; treat_as_signed = 1;}break;
case 'u':{off += 1; radix = 10; treat_as_signed = 0;}break;
case 'b':{off += 1; radix = 2; treat_as_signed = 0;}break;
case 'O':{uppercase = 1;} // fallthrough
case 'o':{off += 1; radix = 8; treat_as_signed = 0;}break;
case 'X':{uppercase = 1;} // fallthrough
case 'x':{off += 1; radix = 16; treat_as_signed = 0;}break;
}
}goto int_case;
//- rjf: %P, %p -> pointer value (specialization of integer case)
case 'P': uppercase = 1; // fallthrough
case 'p':
{
off += 1;
int_val = VariadicAddr(args2);
radix = 16;
}goto int_case;
//- rjf: all integer value cases
int_case:
{
// rjf: radix prefix
switch(radix)
{
default:{}break;
case 2:{if(write) {result.str[string_size] = '0'; result.str[string_size+1] = 'b';} string_size += 2;}break; // leading 0b
case 8:{if(write) {result.str[string_size] = '0'; result.str[string_size+1] = 'o';} string_size += 2;}break; // leading 0o
case 16:{if(write) {result.str[string_size] = '0'; result.str[string_size+1] = 'x';} string_size += 2;}break; // leading 0x
}
// rjf: treat as signed? -> push `-` if needed
if(treat_as_signed)
{
if(int_val & (1ull<<63))
{
if(write)
{
result.str[string_size] = '-';
}
string_size += 1;
}
}
// rjf: make digits in reverse order
UAddr start_size = string_size;
switch(radix)
{
case 2:
{
U64 int_val_shifted = int_val;
for(;;)
{
if(write)
{
U8 bit_char = (int_val_shifted & 1) ? '1' : '0';
result.str[string_size] = bit_char;
}
string_size += 1;
int_val_shifted >>= 1;
if(int_val_shifted == 0)
{
break;
}
}
}break;
case 10:
{
U64 int_val_dived = int_val;
for(;;)
{
if(write)
{
result.str[string_size] = dec_chars[int_val_dived%10];
}
string_size += 1;
int_val_dived /= 10;
if(int_val_dived == 0)
{
break;
}
}
}break;
case 16:
{
U8 *hex_chars = uppercase ? hex_chars_upper : hex_chars_lower;
U64 int_val_shifted = int_val;
for(;;)
{
if(write)
{
result.str[string_size] = hex_chars[int_val_shifted&0xf];
}
string_size += 1;
int_val_shifted >>= 4;
if(int_val_shifted == 0)
{
break;
}
}
}break;
}
// rjf: reverse digits
if(write)
{
UAddr digit_count = string_size - start_size;
for(UAddr digit_idx = 0; digit_idx < digit_count/2; digit_idx += 1)
{
U8 swap = result.str[string_size - 1 - digit_idx];
result.str[string_size - 1 - digit_idx] = result.str[start_size + digit_idx];
result.str[start_size + digit_idx] = swap;
}
}
}break;
}
}
//- rjf: no % -> just copy
else
{
if(write)
{
result.str[string_size] = fmt[off];
}
off += 1;
string_size += 1;
}
}
va_end(args2);
}
//- rjf: allocate string on the read step
if(!write)
{
result.size = string_size;
result.str = PushArrayNoZero(arena, U8, result.size+1);
}
else
{
result.str[string_size] = 0;
}
}
}
return result;
}
function String8
Str8F(Arena *arena, char *fmt, ...)
{
va_list args;
va_start(args, fmt);
String8 result = Str8FV(arena, fmt, args);
va_end(args);
return result;
}
function String8
ByteStringFromData(Arena *arena, String8 data)
{
String8 result = {0};
if(data.size != 0)
{
U8 byte_chars[4] = {0};
UAddr chars_per_byte = ArrayCount(byte_chars);
result.size = (data.size * chars_per_byte - 2);
result.str = PushArray(arena, U8, result.size);
for EachIndex(idx, data.size)
{
U8 byte = data.str[idx];
byte_chars[0] = hex_chars_lower[(byte&0xf0) >> 4];
byte_chars[1] = hex_chars_lower[(byte&0x0f) >> 0];
byte_chars[2] = ',';
byte_chars[3] = ' ';
MemoryCopy(result.str + idx*chars_per_byte, byte_chars, idx+1 == data.size ? chars_per_byte-2 : chars_per_byte);
}
}
return result;
}
////////////////////////////////
//~ rjf: Colors
function U16
RGBA5551From4F32(Vec4F32 rgba)
{
U16 rgba5551 = (rgba.w == 1.f ? 1 : 0) | ((U16)(rgba.x * 0x1f) << 11) | ((U16)(rgba.y * 0x1f) << 6) | ((U16)(rgba.z * 0x1f) << 1);
return rgba5551;
}
function U32
RGBA32From4F32(Vec4F32 rgba)
{
U32 rgba32 = ((U32)(rgba.x * 255) << 24) | ((U32)(rgba.y * 255) << 16) | ((U32)(rgba.z * 255) << 8) | ((U32)(rgba.w * 255) << 0);
return rgba32;
}
function Vec4F32
RGBA4F32From32(U32 rgba)
{
Vec4F32 rgba4f32 = {((rgba&0xff000000)>>24) / 255.f, ((rgba&0x00ff0000)>>16) / 255.f, ((rgba&0x0000ff00)>>8) / 255.f, ((rgba&0x000000ff)>>0) / 255.f};
return rgba4f32;
}