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