// 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[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; }