1610 lines
36 KiB
C
1610 lines
36 KiB
C
|
// NOTE(allen): Things I added durring this jam
|
||
|
// Inf32
|
||
|
// NegInf32
|
||
|
// AbsoluteValue
|
||
|
// StringSkip
|
||
|
// StringChop
|
||
|
// "strings.c"
|
||
|
// RangeuContains
|
||
|
// RangeSplit
|
||
|
// bug in RectContains
|
||
|
// bug in RectOverlaps
|
||
|
// StringMatchFlag_MatchCase -> StringMatchFlag_CaseInsensitive
|
||
|
// bug in StringListJoin
|
||
|
// fleshed out Length/Norm types for vectors
|
||
|
// TODO(allen): change the other scalar functions
|
||
|
// StringSplit
|
||
|
// S8Range
|
||
|
// f32Round
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Scalar
|
||
|
|
||
|
#define FMod fmodf
|
||
|
#define SquareRoot sqrtf
|
||
|
#define Sin sinf
|
||
|
#define Cos cosf
|
||
|
#define Tan tanf
|
||
|
#define Pow32 powf
|
||
|
|
||
|
internal i32
|
||
|
i32Ceil(f32 x)
|
||
|
{
|
||
|
if (x > 0)
|
||
|
{
|
||
|
return(((i32) x) + 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return(((i32) x));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal i32
|
||
|
i32Floor(f32 x)
|
||
|
{
|
||
|
if (x > 0)
|
||
|
{
|
||
|
return(((i32) x));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return(((i32) x) - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
f32Ceil(f32 x)
|
||
|
{
|
||
|
if (x > 0)
|
||
|
{
|
||
|
return((f32) ((i32) x) + 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return((f32) ((i32) x));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
f32Floor(f32 x)
|
||
|
{
|
||
|
if (x > 0)
|
||
|
{
|
||
|
return((f32) ((i32) x));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return((f32) ((i32) x) - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
f32Round(f32 x)
|
||
|
{
|
||
|
return(f32Floor(x + 0.5f));
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
Lerp(f32 a, f32 t, f32 b)
|
||
|
{
|
||
|
return(a + (b - a)*t);
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
AbsoluteValue(f32 f){
|
||
|
union { u32 u; f32 f; } x;
|
||
|
x.f = f;
|
||
|
x.u &= ~Sign32;
|
||
|
return(x.f);
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
Inf32(void){
|
||
|
union { u32 u; f32 f; } x;
|
||
|
x.u = Exponent32;
|
||
|
return(x.f);
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
NegInf32(void){
|
||
|
union { u32 u; f32 f; } x;
|
||
|
x.u = Sign32 | Exponent32;
|
||
|
return(x.f);
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
SignOfSide(Side side){
|
||
|
return((side == Side_Min)?-1.f:1.f);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): vector
|
||
|
|
||
|
#define V2Expand(v) ((v).x), ((v).y)
|
||
|
#define V3Expand(v) ((v).x), ((v).y), ((v).z)
|
||
|
#define V4Expand(v) ((v).x), ((v).y), ((v).z), ((v).w)
|
||
|
|
||
|
internal v2
|
||
|
V2Add(v2 a, v2 b)
|
||
|
{
|
||
|
v2 c = { a.x + b.x, a.y + b.y };
|
||
|
return c;
|
||
|
}
|
||
|
internal v3
|
||
|
V3Add(v3 a, v3 b)
|
||
|
{
|
||
|
v3 c = { a.x + b.x, a.y + b.y, a.z + b.z };
|
||
|
return c;
|
||
|
}
|
||
|
internal v4
|
||
|
V4Add(v4 a, v4 b)
|
||
|
{
|
||
|
v4 c = { a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w };
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
internal v2
|
||
|
V2Sub(v2 a, v2 b)
|
||
|
{
|
||
|
v2 c = { a.x - b.x, a.y - b.y };
|
||
|
return c;
|
||
|
}
|
||
|
internal v3
|
||
|
V3Sub(v3 a, v3 b)
|
||
|
{
|
||
|
v3 c = { a.x - b.x, a.y - b.y, a.z - b.z };
|
||
|
return c;
|
||
|
}
|
||
|
internal v4
|
||
|
V4Sub(v4 a, v4 b)
|
||
|
{
|
||
|
v4 c = { a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w };
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
internal v2
|
||
|
V2Mul(v2 v, f32 f)
|
||
|
{
|
||
|
v.x *= f;
|
||
|
v.y *= f;
|
||
|
return v;
|
||
|
}
|
||
|
internal v3
|
||
|
V3Mul(v3 v, f32 f)
|
||
|
{
|
||
|
v.x *= f;
|
||
|
v.y *= f;
|
||
|
v.z *= f;
|
||
|
return v;
|
||
|
}
|
||
|
internal v4
|
||
|
V4Mul(v4 v, f32 f)
|
||
|
{
|
||
|
v.x *= f;
|
||
|
v.y *= f;
|
||
|
v.z *= f;
|
||
|
v.w *= f;
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
V2Dot(v2 a, v2 b)
|
||
|
{
|
||
|
return a.x*b.x + a.y*b.y;
|
||
|
}
|
||
|
internal f32
|
||
|
V3Dot(v3 a, v3 b)
|
||
|
{
|
||
|
return a.x*b.x + a.y*b.y + a.z*b.z;
|
||
|
}
|
||
|
internal f32
|
||
|
V4Dot(v4 a, v4 b)
|
||
|
{
|
||
|
return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
|
||
|
}
|
||
|
|
||
|
internal v2
|
||
|
V2Hadamard(v2 a, v2 b)
|
||
|
{
|
||
|
v2 v = {a.x*b.x, a.y*b.y};
|
||
|
return(v);
|
||
|
}
|
||
|
internal v3
|
||
|
V3Hadamard(v3 a, v3 b)
|
||
|
{
|
||
|
v3 v = {a.x*b.x, a.y*b.y, a.z*b.z};
|
||
|
return(v);
|
||
|
}
|
||
|
internal v4
|
||
|
V4Hadamard(v4 a, v4 b)
|
||
|
{
|
||
|
v4 v = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w};
|
||
|
return(v);
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
V2Length1(v2 a)
|
||
|
{
|
||
|
return(AbsoluteValue(a.x) + AbsoluteValue(a.y));
|
||
|
}
|
||
|
internal f32
|
||
|
V3Length1(v3 a)
|
||
|
{
|
||
|
return(AbsoluteValue(a.x) + AbsoluteValue(a.y) + AbsoluteValue(a.z));
|
||
|
}
|
||
|
internal f32
|
||
|
V4Length1(v4 a)
|
||
|
{
|
||
|
return(AbsoluteValue(a.x) + AbsoluteValue(a.y) + AbsoluteValue(a.z) + AbsoluteValue(a.w));
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
V2Length2(v2 a)
|
||
|
{
|
||
|
return SquareRoot(V2Dot(a,a));
|
||
|
}
|
||
|
internal f32
|
||
|
V3Length2(v3 a)
|
||
|
{
|
||
|
return SquareRoot(V3Dot(a,a));
|
||
|
}
|
||
|
internal f32
|
||
|
V4Length2(v4 a)
|
||
|
{
|
||
|
return SquareRoot(V4Dot(a,a));
|
||
|
}
|
||
|
|
||
|
#define V2Length V2Length2
|
||
|
#define V3Length V3Length2
|
||
|
#define V4Length V4Length2
|
||
|
|
||
|
internal f32
|
||
|
V2LengthInf(v2 a)
|
||
|
{
|
||
|
f32 f1 = AbsoluteValue(a.x);
|
||
|
f32 f2 = AbsoluteValue(a.y);
|
||
|
f1 = Max(f1, f2);
|
||
|
return(f1);
|
||
|
}
|
||
|
internal f32
|
||
|
V3LengthInf(v3 a)
|
||
|
{
|
||
|
f32 f1 = AbsoluteValue(a.x);
|
||
|
f32 f2 = AbsoluteValue(a.y);
|
||
|
f32 f3 = AbsoluteValue(a.z);
|
||
|
f1 = Max(f1, f2);
|
||
|
f1 = Max(f1, f3);
|
||
|
return(f1);
|
||
|
}
|
||
|
internal f32
|
||
|
V4LengthInf(v4 a)
|
||
|
{
|
||
|
f32 f1 = AbsoluteValue(a.x);
|
||
|
f32 f2 = AbsoluteValue(a.y);
|
||
|
f32 f3 = AbsoluteValue(a.z);
|
||
|
f32 f4 = AbsoluteValue(a.w);
|
||
|
f1 = Max(f1, f2);
|
||
|
f1 = Max(f1, f3);
|
||
|
f1 = Max(f1, f4);
|
||
|
return(f1);
|
||
|
}
|
||
|
|
||
|
#define V2Max V2LengthInf
|
||
|
#define V3Max V3LengthInf
|
||
|
#define V4Max V4LengthInf
|
||
|
|
||
|
internal f32
|
||
|
V2Min(v2 a)
|
||
|
{
|
||
|
f32 f1 = AbsoluteValue(a.x);
|
||
|
f32 f2 = AbsoluteValue(a.y);
|
||
|
f1 = Min(f1, f2);
|
||
|
return(f1);
|
||
|
}
|
||
|
internal f32
|
||
|
V3Min(v3 a)
|
||
|
{
|
||
|
f32 f1 = AbsoluteValue(a.x);
|
||
|
f32 f2 = AbsoluteValue(a.y);
|
||
|
f32 f3 = AbsoluteValue(a.z);
|
||
|
f1 = Min(f1, f2);
|
||
|
f1 = Min(f1, f3);
|
||
|
return(f1);
|
||
|
}
|
||
|
internal f32
|
||
|
V4Min(v4 a)
|
||
|
{
|
||
|
f32 f1 = AbsoluteValue(a.x);
|
||
|
f32 f2 = AbsoluteValue(a.y);
|
||
|
f32 f3 = AbsoluteValue(a.z);
|
||
|
f32 f4 = AbsoluteValue(a.w);
|
||
|
f1 = Min(f1, f2);
|
||
|
f1 = Min(f1, f3);
|
||
|
f1 = Min(f1, f4);
|
||
|
return(f1);
|
||
|
}
|
||
|
|
||
|
internal v2
|
||
|
V2Norm1(v2 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V2Length1(v);
|
||
|
v2 result = { v.x*inv_length, v.y*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
internal v3
|
||
|
V3Norm1(v3 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V3Length1(v);
|
||
|
v3 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
internal v4
|
||
|
V4Norm1(v4 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V4Length1(v);
|
||
|
v4 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, v.w*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal v2
|
||
|
V2Norm2(v2 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V2Length2(v);
|
||
|
v2 result = { v.x*inv_length, v.y*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
internal v3
|
||
|
V3Norm2(v3 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V3Length2(v);
|
||
|
v3 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
internal v4
|
||
|
V4Norm2(v4 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V4Length2(v);
|
||
|
v4 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, v.w*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
#define V2Normalize V2Norm2
|
||
|
#define V3Normalize V3Norm2
|
||
|
#define V4Normalize V4Norm2
|
||
|
|
||
|
internal v2
|
||
|
V2NormInf(v2 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V2LengthInf(v);
|
||
|
v2 result = { v.x*inv_length, v.y*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
internal v3
|
||
|
V3NormInf(v3 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V3LengthInf(v);
|
||
|
v3 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
internal v4
|
||
|
V4NormInf(v4 v)
|
||
|
{
|
||
|
f32 inv_length = 1.f/V4LengthInf(v);
|
||
|
v4 result = { v.x*inv_length, v.y*inv_length, v.z*inv_length, v.w*inv_length, };
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal v3
|
||
|
V3Cross(v3 a, v3 b)
|
||
|
{
|
||
|
v3 result =
|
||
|
{
|
||
|
a.y*b.z - a.z*b.y,
|
||
|
a.z*b.x - a.x*b.z,
|
||
|
a.x*b.y - a.y*b.x,
|
||
|
};
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): matrix
|
||
|
|
||
|
internal m4
|
||
|
M4InitD(f32 diagonal)
|
||
|
{
|
||
|
m4 m =
|
||
|
{
|
||
|
{
|
||
|
{ diagonal },
|
||
|
{ 0.f, diagonal },
|
||
|
{ 0.f, 0.f, diagonal },
|
||
|
{ 0.f, 0.f, 0.f, diagonal },
|
||
|
}
|
||
|
};
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
internal m4
|
||
|
M4MultiplyM4(m4 a, m4 b)
|
||
|
{
|
||
|
m4 c = {0};
|
||
|
|
||
|
for(int j = 0; j < 4; ++j)
|
||
|
{
|
||
|
for(int i = 0; i < 4; ++i)
|
||
|
{
|
||
|
c.elements[i][j] = (a.elements[0][j]*b.elements[i][0] +
|
||
|
a.elements[1][j]*b.elements[i][1] +
|
||
|
a.elements[2][j]*b.elements[i][2] +
|
||
|
a.elements[3][j]*b.elements[i][3]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
internal m4
|
||
|
M4MultiplyF32(m4 a, f32 b)
|
||
|
{
|
||
|
for(int j = 0; j < 4; ++j)
|
||
|
{
|
||
|
for(int i = 0; i < 4; ++i)
|
||
|
{
|
||
|
a.elements[i][j] *= b;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
internal v4
|
||
|
V4MultiplyM4(v4 v, m4 m)
|
||
|
{
|
||
|
v4 result = {0};
|
||
|
|
||
|
for(int i = 0; i < 4; ++i)
|
||
|
{
|
||
|
result.elements[i] = (v.elements[0]*m.elements[0][i] +
|
||
|
v.elements[1]*m.elements[1][i] +
|
||
|
v.elements[2]*m.elements[2][i] +
|
||
|
v.elements[3]*m.elements[3][i]);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal m4
|
||
|
M4TranslateV3(v3 translation)
|
||
|
{
|
||
|
m4 result = M4InitD(1.f);
|
||
|
result.elements[3][0] = translation.x;
|
||
|
result.elements[3][1] = translation.y;
|
||
|
result.elements[3][2] = translation.z;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal m4
|
||
|
M4ScaleV3(v3 scale)
|
||
|
{
|
||
|
m4 result = M4InitD(1.f);
|
||
|
result.elements[0][0] = scale.x;
|
||
|
result.elements[1][1] = scale.y;
|
||
|
result.elements[2][2] = scale.z;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal m4
|
||
|
M4Perspective(f32 fov, f32 aspect_ratio, f32 near_z, f32 far_z)
|
||
|
{
|
||
|
m4 result = {0};
|
||
|
f32 tan_theta_over_2 = Tan(fov * (PI / 360.f));
|
||
|
result.elements[0][0] = 1.f / tan_theta_over_2;
|
||
|
result.elements[1][1] = aspect_ratio / tan_theta_over_2;
|
||
|
result.elements[2][3] = -1.f;
|
||
|
result.elements[2][2] = (near_z + far_z) / (near_z - far_z);
|
||
|
result.elements[3][2] = (2.f * near_z * far_z) / (near_z - far_z);
|
||
|
result.elements[3][3] = 0.f;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal m4
|
||
|
M4LookAt(v3 eye, v3 center, v3 up)
|
||
|
{
|
||
|
m4 result;
|
||
|
|
||
|
v3 f = V3Normalize(V3Sub(center, eye));
|
||
|
v3 s = V3Normalize(V3Cross(f, up));
|
||
|
v3 u = V3Cross(s, f);
|
||
|
|
||
|
result.elements[0][0] = s.x;
|
||
|
result.elements[0][1] = u.x;
|
||
|
result.elements[0][2] = -f.x;
|
||
|
result.elements[0][3] = 0.0f;
|
||
|
|
||
|
result.elements[1][0] = s.y;
|
||
|
result.elements[1][1] = u.y;
|
||
|
result.elements[1][2] = -f.y;
|
||
|
result.elements[1][3] = 0.0f;
|
||
|
|
||
|
result.elements[2][0] = s.z;
|
||
|
result.elements[2][1] = u.z;
|
||
|
result.elements[2][2] = -f.z;
|
||
|
result.elements[2][3] = 0.0f;
|
||
|
|
||
|
result.elements[3][0] = -V3Dot(s, eye);
|
||
|
result.elements[3][1] = -V3Dot(u, eye);
|
||
|
result.elements[3][2] = V3Dot(f, eye);
|
||
|
result.elements[3][3] = 1.0f;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal m4
|
||
|
M4Inverse(m4 m)
|
||
|
{
|
||
|
f32 coef00 = m.elements[2][2] * m.elements[3][3] - m.elements[3][2] * m.elements[2][3];
|
||
|
f32 coef02 = m.elements[1][2] * m.elements[3][3] - m.elements[3][2] * m.elements[1][3];
|
||
|
f32 coef03 = m.elements[1][2] * m.elements[2][3] - m.elements[2][2] * m.elements[1][3];
|
||
|
f32 coef04 = m.elements[2][1] * m.elements[3][3] - m.elements[3][1] * m.elements[2][3];
|
||
|
f32 coef06 = m.elements[1][1] * m.elements[3][3] - m.elements[3][1] * m.elements[1][3];
|
||
|
f32 coef07 = m.elements[1][1] * m.elements[2][3] - m.elements[2][1] * m.elements[1][3];
|
||
|
f32 coef08 = m.elements[2][1] * m.elements[3][2] - m.elements[3][1] * m.elements[2][2];
|
||
|
f32 coef10 = m.elements[1][1] * m.elements[3][2] - m.elements[3][1] * m.elements[1][2];
|
||
|
f32 coef11 = m.elements[1][1] * m.elements[2][2] - m.elements[2][1] * m.elements[1][2];
|
||
|
f32 coef12 = m.elements[2][0] * m.elements[3][3] - m.elements[3][0] * m.elements[2][3];
|
||
|
f32 coef14 = m.elements[1][0] * m.elements[3][3] - m.elements[3][0] * m.elements[1][3];
|
||
|
f32 coef15 = m.elements[1][0] * m.elements[2][3] - m.elements[2][0] * m.elements[1][3];
|
||
|
f32 coef16 = m.elements[2][0] * m.elements[3][2] - m.elements[3][0] * m.elements[2][2];
|
||
|
f32 coef18 = m.elements[1][0] * m.elements[3][2] - m.elements[3][0] * m.elements[1][2];
|
||
|
f32 coef19 = m.elements[1][0] * m.elements[2][2] - m.elements[2][0] * m.elements[1][2];
|
||
|
f32 coef20 = m.elements[2][0] * m.elements[3][1] - m.elements[3][0] * m.elements[2][1];
|
||
|
f32 coef22 = m.elements[1][0] * m.elements[3][1] - m.elements[3][0] * m.elements[1][1];
|
||
|
f32 coef23 = m.elements[1][0] * m.elements[2][1] - m.elements[2][0] * m.elements[1][1];
|
||
|
|
||
|
v4 fac0 = { coef00, coef00, coef02, coef03 };
|
||
|
v4 fac1 = { coef04, coef04, coef06, coef07 };
|
||
|
v4 fac2 = { coef08, coef08, coef10, coef11 };
|
||
|
v4 fac3 = { coef12, coef12, coef14, coef15 };
|
||
|
v4 fac4 = { coef16, coef16, coef18, coef19 };
|
||
|
v4 fac5 = { coef20, coef20, coef22, coef23 };
|
||
|
|
||
|
v4 vec0 = { m.elements[1][0], m.elements[0][0], m.elements[0][0], m.elements[0][0] };
|
||
|
v4 vec1 = { m.elements[1][1], m.elements[0][1], m.elements[0][1], m.elements[0][1] };
|
||
|
v4 vec2 = { m.elements[1][2], m.elements[0][2], m.elements[0][2], m.elements[0][2] };
|
||
|
v4 vec3 = { m.elements[1][3], m.elements[0][3], m.elements[0][3], m.elements[0][3] };
|
||
|
|
||
|
v4 inv0 = V4Add(V4Sub(V4Hadamard(vec1, fac0), V4Hadamard(vec2, fac1)), V4Hadamard(vec3, fac2));
|
||
|
v4 inv1 = V4Add(V4Sub(V4Hadamard(vec0, fac0), V4Hadamard(vec2, fac3)), V4Hadamard(vec3, fac4));
|
||
|
v4 inv2 = V4Add(V4Sub(V4Hadamard(vec0, fac1), V4Hadamard(vec1, fac3)), V4Hadamard(vec3, fac5));
|
||
|
v4 inv3 = V4Add(V4Sub(V4Hadamard(vec0, fac2), V4Hadamard(vec1, fac4)), V4Hadamard(vec2, fac5));
|
||
|
|
||
|
v4 sign_a = { +1, -1, +1, -1 };
|
||
|
v4 sign_b = { -1, +1, -1, +1 };
|
||
|
|
||
|
m4 inverse;
|
||
|
for(u32 i = 0; i < 4; ++i)
|
||
|
{
|
||
|
inverse.elements[0][i] = inv0.elements[i] * sign_a.elements[i];
|
||
|
inverse.elements[1][i] = inv1.elements[i] * sign_b.elements[i];
|
||
|
inverse.elements[2][i] = inv2.elements[i] * sign_a.elements[i];
|
||
|
inverse.elements[3][i] = inv3.elements[i] * sign_b.elements[i];
|
||
|
}
|
||
|
|
||
|
v4 row0 = { inverse.elements[0][0], inverse.elements[1][0], inverse.elements[2][0], inverse.elements[3][0] };
|
||
|
v4 m0 = { m.elements[0][0], m.elements[0][1], m.elements[0][2], m.elements[0][3] };
|
||
|
v4 dot0 = V4Hadamard(m0, row0);
|
||
|
f32 dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w);
|
||
|
|
||
|
f32 one_over_det = 1 / dot1;
|
||
|
|
||
|
return M4MultiplyF32(inverse, one_over_det);
|
||
|
}
|
||
|
|
||
|
internal m4
|
||
|
M4RemoveRotation(m4 mat)
|
||
|
{
|
||
|
v3 scale =
|
||
|
{
|
||
|
V3Length(v3(mat.elements[0][0], mat.elements[0][1], mat.elements[0][2])),
|
||
|
V3Length(v3(mat.elements[1][0], mat.elements[1][1], mat.elements[1][2])),
|
||
|
V3Length(v3(mat.elements[2][0], mat.elements[2][1], mat.elements[2][2])),
|
||
|
};
|
||
|
|
||
|
mat.elements[0][0] = scale.x;
|
||
|
mat.elements[1][0] = 0.f;
|
||
|
mat.elements[2][0] = 0.f;
|
||
|
|
||
|
mat.elements[0][1] = 0.f;
|
||
|
mat.elements[1][1] = scale.y;
|
||
|
mat.elements[2][1] = 0.f;
|
||
|
|
||
|
mat.elements[0][2] = 0.f;
|
||
|
mat.elements[1][2] = 0.f;
|
||
|
mat.elements[2][2] = scale.z;
|
||
|
|
||
|
return mat;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): color
|
||
|
|
||
|
internal v3
|
||
|
RGBToHSV(v3 rgb)
|
||
|
{
|
||
|
f32 c_max = V3Max(rgb);
|
||
|
f32 c_min = V3Min(rgb);
|
||
|
f32 delta = c_max - c_min;
|
||
|
b32 c_max_is_r = rgb.r > rgb.g && rgb.r > rgb.b;
|
||
|
b32 c_max_is_g = rgb.g > rgb.r && rgb.g > rgb.b;
|
||
|
b32 c_max_is_b = rgb.b > rgb.r && rgb.b > rgb.g;
|
||
|
f32 h = (c_max_is_r ? (rgb.g - rgb.b) / delta + 0 : c_max_is_g ? (rgb.b - rgb.r) / delta + 2 : c_max_is_b ? (rgb.r - rgb.g) / delta + 4 : 0);
|
||
|
f32 s = c_max == 0 ? 0 : (delta / c_max);
|
||
|
f32 v = c_max;
|
||
|
v3 hsv = {h / 6.f, s, v};
|
||
|
return hsv;
|
||
|
}
|
||
|
|
||
|
internal v3
|
||
|
HSVToRGB(v3 hsv)
|
||
|
{
|
||
|
f32 h = FMod(hsv.x * 360.f, 360.f);
|
||
|
f32 s = hsv.y;
|
||
|
f32 v = hsv.z;
|
||
|
|
||
|
f32 c = v * s;
|
||
|
f32 x = c * (1 - AbsoluteValue(FMod((h / 60.f), 2) - 1));
|
||
|
f32 m = v - c;
|
||
|
|
||
|
f32 r;
|
||
|
f32 g;
|
||
|
f32 b;
|
||
|
|
||
|
if((h >= 0.f && h < 60.f) || (h >= 360.f && h < 420.f))
|
||
|
{
|
||
|
r = c;
|
||
|
g = x;
|
||
|
b = 0;
|
||
|
}
|
||
|
else if(h >= 60.f && h < 120.f)
|
||
|
{
|
||
|
r = x;
|
||
|
g = c;
|
||
|
b = 0;
|
||
|
}
|
||
|
else if(h >= 120.f && h < 180.f)
|
||
|
{
|
||
|
r = 0;
|
||
|
g = c;
|
||
|
b = x;
|
||
|
}
|
||
|
else if(h >= 180.f && h < 240.f)
|
||
|
{
|
||
|
r = 0;
|
||
|
g = x;
|
||
|
b = c;
|
||
|
}
|
||
|
else if(h >= 240.f && h < 300.f)
|
||
|
{
|
||
|
r = x;
|
||
|
g = 0;
|
||
|
b = c;
|
||
|
}
|
||
|
else if((h >= 300.f && h <= 360.f) ||
|
||
|
(h >= -60.f && h <= 0.f))
|
||
|
{
|
||
|
r = c;
|
||
|
g = 0;
|
||
|
b = x;
|
||
|
}
|
||
|
|
||
|
v3 rgb = {r + m, g + m, b + m};
|
||
|
return rgb;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): interval
|
||
|
|
||
|
internal Range
|
||
|
MakeRange(f32 a, f32 b)
|
||
|
{
|
||
|
Range range = {a, b};
|
||
|
if (a > b)
|
||
|
{
|
||
|
Swap(f32, range.min, range.max);
|
||
|
}
|
||
|
return(range);
|
||
|
}
|
||
|
internal Rangei
|
||
|
MakeRangei(i64 a, i64 b)
|
||
|
{
|
||
|
Rangei range = {a, b};
|
||
|
if (a > b)
|
||
|
{
|
||
|
Swap(i64, range.min, range.max);
|
||
|
}
|
||
|
return(range);
|
||
|
}
|
||
|
internal Rangeu
|
||
|
MakeRangeu(u64 a, u64 b)
|
||
|
{
|
||
|
Rangeu range = {a, b};
|
||
|
if (a > b)
|
||
|
{
|
||
|
Swap(u64, range.min, range.max);
|
||
|
}
|
||
|
return(range);
|
||
|
}
|
||
|
|
||
|
#define RangeSize(range) ((range).max - (range).min)
|
||
|
|
||
|
internal Range
|
||
|
RangeUnion(Range a, Range b)
|
||
|
{
|
||
|
Range result;
|
||
|
result.min = Min(a.min, b.min);
|
||
|
result.max = Max(a.max, b.max);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal Range
|
||
|
RangeIntersection(Range a, Range b)
|
||
|
{
|
||
|
Range result;
|
||
|
result.min = Max(a.min, b.min);
|
||
|
result.max = Min(a.max, b.max);
|
||
|
result.min = ClampTop(result.min, result.max);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal Range
|
||
|
RangeGrow(Range range, f32 x)
|
||
|
{
|
||
|
range.min -= x;
|
||
|
range.max += x;
|
||
|
return(range);
|
||
|
}
|
||
|
|
||
|
internal Range
|
||
|
RangeShrink(Range range, f32 x)
|
||
|
{
|
||
|
range.min += x;
|
||
|
range.max -= x;
|
||
|
range.min = ClampTop(range.min, range.max);
|
||
|
return(range);
|
||
|
}
|
||
|
|
||
|
internal Range
|
||
|
RangeSplit(Range *free_range, Side side, f32 amt){
|
||
|
Range range;
|
||
|
if (side == Side_Min){
|
||
|
range.min = free_range->min;
|
||
|
range.max = range.min + amt;
|
||
|
range.max = ClampTop(range.max, free_range->max);
|
||
|
free_range->min = range.max;
|
||
|
}
|
||
|
else{
|
||
|
range.max = free_range->max;
|
||
|
range.min = range.max - amt;
|
||
|
range.min = ClampBot(range.min, free_range->min);
|
||
|
free_range->max = range.min;
|
||
|
}
|
||
|
return(range);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
RangeContains(Range range, f32 x)
|
||
|
{
|
||
|
return(range.min <= x && x < range.max);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
RangeOverlaps(Range a, Range b)
|
||
|
{
|
||
|
return(a.min < b.max && b.min < a.max);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
RangeuContains(Rangeu range, u64 x)
|
||
|
{
|
||
|
return(range.min <= x && x < range.max);
|
||
|
}
|
||
|
|
||
|
internal Rect
|
||
|
MakeRect(f32 x0, f32 y0, f32 x1, f32 y1)
|
||
|
{
|
||
|
Rect rect = {x0, y0, x1, y1};
|
||
|
return(rect);
|
||
|
}
|
||
|
internal Rect
|
||
|
MakeRectVec(v2 p0, v2 p1)
|
||
|
{
|
||
|
Rect rect = {p0.x, p0.y, p1.x, p1.y};
|
||
|
return(rect);
|
||
|
}
|
||
|
internal Rect
|
||
|
MakeRectRanges(Range x, Range y)
|
||
|
{
|
||
|
Rect rect = {x.min, y.min, x.max, y.max};
|
||
|
return(rect);
|
||
|
}
|
||
|
|
||
|
internal Rect
|
||
|
RectUnion(Rect a, Rect b)
|
||
|
{
|
||
|
Rect result;
|
||
|
result.x0 = Min(a.x0, b.x0);
|
||
|
result.y0 = Min(a.y0, b.y0);
|
||
|
result.x1 = Max(a.x1, b.x1);
|
||
|
result.y1 = Max(a.y1, b.y1);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal Rect
|
||
|
RectIntersect(Rect a, Rect b)
|
||
|
{
|
||
|
Rect result;
|
||
|
result.x0 = Max(a.x0, b.x0);
|
||
|
result.y0 = Max(a.y0, b.y0);
|
||
|
result.x1 = Min(a.x1, b.x1);
|
||
|
result.y1 = Min(a.y1, b.y1);
|
||
|
result.x0 = ClampTop(result.x0, result.x1);
|
||
|
result.y0 = ClampTop(result.y0, result.y1);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal Rect
|
||
|
RectGrow(Rect rect, f32 v)
|
||
|
{
|
||
|
rect.x0 -= v;
|
||
|
rect.y0 -= v;
|
||
|
rect.x1 += v;
|
||
|
rect.y1 += v;
|
||
|
return(rect);
|
||
|
}
|
||
|
|
||
|
internal Rect
|
||
|
RectShrink(Rect rect, f32 v)
|
||
|
{
|
||
|
rect.x0 += v;
|
||
|
rect.y0 += v;
|
||
|
rect.x1 -= v;
|
||
|
rect.y1 -= v;
|
||
|
rect.x0 = ClampTop(rect.x0, rect.x1);
|
||
|
rect.y0 = ClampTop(rect.y0, rect.y1);
|
||
|
return(rect);
|
||
|
}
|
||
|
|
||
|
internal Range
|
||
|
RectGetRange(Rect rect, Dimension dim)
|
||
|
{
|
||
|
Range range = {rect.p0.v[dim], rect.p1.v[dim]};
|
||
|
return(range);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
RectContains(Rect rect, v2 p)
|
||
|
{
|
||
|
return(rect.x0 <= p.x && p.x < rect.x1 && rect.y0 <= p.y && p.y < rect.y1);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
RectOverlaps(Rect a, Rect b)
|
||
|
{
|
||
|
return(a.x0 < b.x1 && b.x0 < a.x1 && a.y0 < b.y1 && b.y0 < a.y1);
|
||
|
}
|
||
|
|
||
|
internal v2
|
||
|
RectGetDim(Rect rect)
|
||
|
{
|
||
|
v2 p = {rect.x1 - rect.x0, rect.y1 - rect.y0};
|
||
|
return(p);
|
||
|
}
|
||
|
#define RectSize(r) RectGetDim(r)
|
||
|
|
||
|
internal v2
|
||
|
RectGetCenter(Rect rect)
|
||
|
{
|
||
|
v2 p = {0.5f*(rect.x1 + rect.x0), 0.5f*(rect.y1 + rect.y0)};
|
||
|
return(p);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
//~ NOTE(allen): String
|
||
|
|
||
|
internal b32
|
||
|
CharIsSpace(char c)
|
||
|
{
|
||
|
return((c) <= 32);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
CharIsAlpha(char c)
|
||
|
{
|
||
|
return ((c >= 'a' && c <= 'z') ||
|
||
|
(c >= 'A' && c <= 'Z'));
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
CharIsDigit(char c)
|
||
|
{
|
||
|
return (c >= '0' && c <= '9');
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
CharIsSymbol(char c)
|
||
|
{
|
||
|
return (c == '~' ||
|
||
|
c == '!' ||
|
||
|
c == '%' ||
|
||
|
c == '^' ||
|
||
|
c == '&' ||
|
||
|
c == '*' ||
|
||
|
c == '(' ||
|
||
|
c == ')' ||
|
||
|
c == '[' ||
|
||
|
c == ']' ||
|
||
|
c == '{' ||
|
||
|
c == '}' ||
|
||
|
c == '-' ||
|
||
|
c == '+' ||
|
||
|
c == '=' ||
|
||
|
c == ';' ||
|
||
|
c == ':' ||
|
||
|
c == '<' ||
|
||
|
c == '>' ||
|
||
|
c == '/' ||
|
||
|
c == '?' ||
|
||
|
c == '.' ||
|
||
|
c == ',');
|
||
|
}
|
||
|
|
||
|
internal char
|
||
|
CharToLower(char c)
|
||
|
{
|
||
|
if(c >= 'A' && c <= 'Z')
|
||
|
{
|
||
|
return c + 32;
|
||
|
}
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
internal char
|
||
|
CharToUpper(char c)
|
||
|
{
|
||
|
if(c >= 'a' && c <= 'z')
|
||
|
{
|
||
|
return c - 32;
|
||
|
}
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
S8(u8 *str, u64 size)
|
||
|
{
|
||
|
String8 result = {str, size};
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
S8Range(u8 *first, u8 *one_past_last)
|
||
|
{
|
||
|
String8 result = {first, one_past_last - first};
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
S8Zero(void){
|
||
|
String8 result = {0, 0};
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
String8FromCString(char *cstring)
|
||
|
{
|
||
|
String8 string = {0};
|
||
|
string.str = (u8 *)cstring;
|
||
|
string.size = CalculateCStringLength(cstring);
|
||
|
return string;
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
StringMatchGeneric(String8 a, String8 b, StringMatchFlags flags)
|
||
|
{
|
||
|
b32 result = 0;
|
||
|
|
||
|
if(a.size == b.size || (flags & StringMatchFlag_RightSideSloppy))
|
||
|
{
|
||
|
b32 insensitive = (flags & StringMatchFlag_CaseInsensitive);
|
||
|
u64 size = Min(a.size, b.size);
|
||
|
result = 1;
|
||
|
for(u64 i = 0; i < size; ++i)
|
||
|
{
|
||
|
u8 at = a.str[i];
|
||
|
u8 bt = b.str[i];
|
||
|
if(insensitive)
|
||
|
{
|
||
|
at = CharToUpper(at);
|
||
|
bt = CharToUpper(bt);
|
||
|
}
|
||
|
if(at != bt)
|
||
|
{
|
||
|
result = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
StringMatch(String8 a, String8 b)
|
||
|
{
|
||
|
return StringMatchGeneric(a, b, 0);
|
||
|
}
|
||
|
|
||
|
internal b32
|
||
|
StringMatchCaseInsensitive(String8 a, String8 b)
|
||
|
{
|
||
|
return StringMatchGeneric(a, b, StringMatchFlag_CaseInsensitive);
|
||
|
}
|
||
|
|
||
|
internal u64
|
||
|
StringFindSubstringStart(String8 a, String8 sub, StringMatchFlags flags)
|
||
|
{
|
||
|
u64 result = ~(u64)0;
|
||
|
for (u64 i = 0; i < a.size; i += 1){
|
||
|
if (a.str[i] == sub.str[0]){
|
||
|
if (StringMatchGeneric(a, sub, flags | StringMatchFlag_RightSideSloppy)){
|
||
|
result = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
StringSubstring(String8 string, Rangeu range)
|
||
|
{
|
||
|
range.max = ClampTop(range.max, string.size);
|
||
|
range.min = ClampTop(range.min, string.size);
|
||
|
string.str += range.min;
|
||
|
string.size = RangeSize(range);
|
||
|
return(string);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
StringPrefix(String8 string, u64 size)
|
||
|
{
|
||
|
string.size = ClampTop(size, string.size);
|
||
|
return(string);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
StringSkip(String8 string, u64 val)
|
||
|
{
|
||
|
val = ClampTop(val, string.size);
|
||
|
string.str += val;
|
||
|
string.size -= val;
|
||
|
return(string);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
StringPostfix(String8 string, u64 size)
|
||
|
{
|
||
|
size = ClampTop(size, string.size);
|
||
|
string.str = (string.str + string.size) - size;
|
||
|
string.size = size;
|
||
|
return(string);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
StringChop(String8 string, u64 val)
|
||
|
{
|
||
|
val = ClampTop(val, string.size);
|
||
|
string.size -= val;
|
||
|
return(string);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
PushStringCat(M_Arena *arena, String8 a, String8 b){
|
||
|
String8 str;
|
||
|
str.size = a.size + b.size;
|
||
|
str.str = PushArray(arena, u8, str.size);
|
||
|
MemoryCopy(str.str, a.str, a.size);
|
||
|
MemoryCopy(str.str + a.size, b.str, b.size);
|
||
|
return(str);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
PushStringFV(M_Arena *arena, char *format, va_list args)
|
||
|
{
|
||
|
va_list args2;
|
||
|
va_copy(args2, args);
|
||
|
u32 needed_bytes = vsnprintf(0, 0, format, args) + 1;
|
||
|
String8 result = {0};
|
||
|
result.str = PushArray(arena, u8, needed_bytes);
|
||
|
result.size = vsnprintf((char*)result.str, needed_bytes, format, args2);
|
||
|
result.str[result.size] = 0;
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
PushStringF(M_Arena *arena, char *fmt, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
va_start(args, fmt);
|
||
|
String8 result = PushStringFV(arena, fmt, args);
|
||
|
va_end(args);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
//~ NOTE(allen): String List
|
||
|
|
||
|
internal String8_Node*
|
||
|
StringListPush(M_Arena *arena, String8_List *list, String8 string)
|
||
|
{
|
||
|
String8_Node *node = PushArray(arena, String8_Node, 1);
|
||
|
SLLQueuePush(list->first, list->last, node);
|
||
|
list->node_count += 1;
|
||
|
list->total_size += string.size;
|
||
|
node->string = string;
|
||
|
return(node);
|
||
|
}
|
||
|
|
||
|
internal String8_Node*
|
||
|
StringListPushFront(M_Arena *arena, String8_List *list, String8 string)
|
||
|
{
|
||
|
String8_Node *node = PushArray(arena, String8_Node, 1);
|
||
|
SLLQueuePushFront(list->first, list->last, node);
|
||
|
list->node_count += 1;
|
||
|
list->total_size += string.size;
|
||
|
node->string = string;
|
||
|
return(node);
|
||
|
}
|
||
|
|
||
|
internal String8_Node*
|
||
|
StringListPushF(M_Arena *arena, String8_List *list, char *fmt, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
va_start(args, fmt);
|
||
|
String8 string = PushStringFV(arena, fmt, args);
|
||
|
String8_Node *result = StringListPush(arena, list, string);
|
||
|
va_end(args);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal String8_Node*
|
||
|
StringListPushFrontF(M_Arena *arena, String8_List *list, char *fmt, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
va_start(args, fmt);
|
||
|
String8 string = PushStringFV(arena, fmt, args);
|
||
|
String8_Node *result = StringListPushFront(arena, list, string);
|
||
|
va_end(args);
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
StringListRemoveEmpties(String8_List *list)
|
||
|
{
|
||
|
String8_Node *new_first = 0;
|
||
|
String8_Node *new_last = 0;
|
||
|
u64 new_count = 0;
|
||
|
for (String8_Node *node = list->first, *next = 0;
|
||
|
node != 0;
|
||
|
node = next)
|
||
|
{
|
||
|
next = node->next;
|
||
|
if (node->string.size > 0){
|
||
|
SLLQueuePush(new_first, new_last, node);
|
||
|
new_count += 1;
|
||
|
}
|
||
|
}
|
||
|
list->first = new_first;
|
||
|
list->last = new_last;
|
||
|
list->node_count = new_count;
|
||
|
}
|
||
|
|
||
|
internal String8_List
|
||
|
StringSplit(M_Arena *arena, String8 string, u8 *split_chars, u64 split_char_count)
|
||
|
{
|
||
|
String8_List list = {0};
|
||
|
|
||
|
u8 *ptr = string.str;
|
||
|
u8 *opl = string.str + string.size;
|
||
|
for (;ptr < opl;)
|
||
|
{
|
||
|
u8 *first = ptr;
|
||
|
for (;ptr < opl; ptr += 1)
|
||
|
{
|
||
|
u8 c = *ptr;
|
||
|
b32 is_split = 0;
|
||
|
for (u64 i = 0; i < split_char_count; i += 1)
|
||
|
{
|
||
|
if (split_chars[i] == c)
|
||
|
{
|
||
|
is_split = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (is_split)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
StringListPush(arena, &list, S8Range(first, ptr));
|
||
|
ptr += 1;
|
||
|
}
|
||
|
|
||
|
return(list);
|
||
|
}
|
||
|
|
||
|
internal String8
|
||
|
StringListJoin(M_Arena *arena, String8_List *list, String_Join *optional_join)
|
||
|
{
|
||
|
String_Join join = {0};
|
||
|
if (optional_join != 0)
|
||
|
{
|
||
|
MemoryCopyStruct(&join, optional_join);
|
||
|
}
|
||
|
|
||
|
String8 result;
|
||
|
result.size = join.pre.size + join.post.size + (list->node_count - 1)*join.sep.size + list->total_size;
|
||
|
u8 *ptr = result.str = PushArray(arena, u8, result.size);
|
||
|
|
||
|
MemoryCopy(ptr, join.pre.str, join.pre.size);
|
||
|
ptr += join.pre.size;
|
||
|
for (String8_Node *node = list->first;
|
||
|
node != 0;
|
||
|
node = node->next)
|
||
|
{
|
||
|
MemoryCopy(ptr, node->string.str, node->string.size);
|
||
|
ptr += node->string.size;
|
||
|
if (node->next != 0)
|
||
|
{
|
||
|
MemoryCopy(ptr, join.sep.str, join.sep.size);
|
||
|
ptr += join.sep.size;
|
||
|
}
|
||
|
}
|
||
|
MemoryCopy(ptr, join.post.str, join.post.size);
|
||
|
ptr += join.post.size;
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
//~ NOTE(allen): Additional String Stuff
|
||
|
|
||
|
internal i64
|
||
|
GetFirstIntegerFromString(String8 string)
|
||
|
{
|
||
|
i32 result = 0;
|
||
|
b32 found_first_digit = 0;
|
||
|
u32 integer_write_pos = 0;
|
||
|
u8 integer[64] = {0};
|
||
|
u32 read_pos = 0;
|
||
|
for(;; ++read_pos)
|
||
|
{
|
||
|
if(string.str[read_pos] == 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
if(found_first_digit)
|
||
|
{
|
||
|
if(integer_write_pos == sizeof(integer))
|
||
|
{
|
||
|
integer[sizeof(integer) - 1] = 0;
|
||
|
break;
|
||
|
}
|
||
|
if(CharIsDigit(string.str[read_pos]) || string.str[read_pos] == '-')
|
||
|
{
|
||
|
integer[integer_write_pos++] = string.str[read_pos];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
integer[integer_write_pos++] = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(CharIsDigit(string.str[read_pos]) || string.str[read_pos] == '-')
|
||
|
{
|
||
|
integer[integer_write_pos++] = string.str[read_pos];
|
||
|
found_first_digit = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
result = CStringToI32(integer);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal f32
|
||
|
GetFirstF32FromCString(char *str)
|
||
|
{
|
||
|
f32 result = 0;
|
||
|
b32 found_first_digit = 0;
|
||
|
u32 float_write_pos = 0;
|
||
|
char float_str[64] = {0};
|
||
|
u32 read_pos = 0;
|
||
|
for(;; ++read_pos)
|
||
|
{
|
||
|
if(str[read_pos] == 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
if(found_first_digit)
|
||
|
{
|
||
|
if(float_write_pos == sizeof(float_str))
|
||
|
{
|
||
|
float_str[sizeof(float_str) - 1] = 0;
|
||
|
break;
|
||
|
}
|
||
|
if(CharIsDigit(str[read_pos]) || str[read_pos] == '.' || str[read_pos] == '-')
|
||
|
{
|
||
|
float_str[float_write_pos++] = str[read_pos];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
float_str[float_write_pos++] = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(CharIsDigit(str[read_pos]) || str[read_pos] == '.' || str[read_pos] == '-')
|
||
|
{
|
||
|
float_str[float_write_pos++] = str[read_pos];
|
||
|
found_first_digit = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
result = CStringToF32(float_str);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
CopySubstringToStringUntilCharN(char *str1, u32 str1_max,
|
||
|
const char *str2, char str2_term)
|
||
|
{
|
||
|
u32 write_pos = 0;
|
||
|
while(1)
|
||
|
{
|
||
|
if(str2[write_pos] == str2_term || write_pos == str1_max - 1)
|
||
|
{
|
||
|
str1[write_pos++] = 0;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
str1[write_pos] = str2[write_pos];
|
||
|
++write_pos;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
CopyStringToFixedSizeBuffer(char *dest, u32 dest_max,
|
||
|
const char *src)
|
||
|
{
|
||
|
u32 read_pos = 0;
|
||
|
u32 write_pos = 0;
|
||
|
for(;;)
|
||
|
{
|
||
|
if(src[read_pos] == 0 || write_pos >= dest_max)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
dest[write_pos++] = src[read_pos++];
|
||
|
}
|
||
|
if(write_pos >= dest_max)
|
||
|
{
|
||
|
dest[dest_max - 1] = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dest[write_pos++] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal u32
|
||
|
CStringIndexAfterSubstring(char *str, char *substr)
|
||
|
{
|
||
|
u32 result = 0;
|
||
|
for(u32 i = 0; str[i]; ++i)
|
||
|
{
|
||
|
if(str[i] == substr[0])
|
||
|
{
|
||
|
if(CStringMatchCaseInsensitiveN(str + i, substr, CalculateCStringLength(substr)))
|
||
|
{
|
||
|
result = i + CalculateCStringLength(substr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal u32
|
||
|
CStringFirstIndexAfterSubstring(char *str, char *substr)
|
||
|
{
|
||
|
u32 result = 0;
|
||
|
for(u32 i = 0; str[i]; ++i)
|
||
|
{
|
||
|
if(str[i] == substr[0])
|
||
|
{
|
||
|
if(CStringMatchCaseInsensitiveN(str + i, substr, CalculateCStringLength(substr)))
|
||
|
{
|
||
|
result = i + CalculateCStringLength(substr);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
CopyCStringToFixedSizeBuffer(char *destination, u32 destination_max, char *source)
|
||
|
{
|
||
|
for(u32 i = 0; i < destination_max; ++i)
|
||
|
{
|
||
|
destination[i] = source[i];
|
||
|
if(source[i] == 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
destination[destination_max-1] = 0;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
CopyCStringToFixedSizeBufferN(char *destination, u32 destination_max, char *source, u32 source_max)
|
||
|
{
|
||
|
for(u32 i = 0; i < destination_max && i < source_max; ++i)
|
||
|
{
|
||
|
destination[i] = source[i];
|
||
|
if(source[i] == 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
destination[destination_max-1] = 0;
|
||
|
}
|
||
|
|
||
|
internal char *
|
||
|
ConvertCStringToLowercase(char *str)
|
||
|
{
|
||
|
for(int i = 0; str[i]; ++i)
|
||
|
{
|
||
|
str[i] = CharToLower(str[i]);
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
internal char *
|
||
|
ConvertCStringToUppercase(char *str)
|
||
|
{
|
||
|
for(int i = 0; str[i]; ++i)
|
||
|
{
|
||
|
str[i] = CharToUpper(str[i]);
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
internal char *
|
||
|
ConvertCStringToLowercaseWithUnderscores(char *str)
|
||
|
{
|
||
|
for(int i = 0; str[i]; ++i)
|
||
|
{
|
||
|
if(str[i] == ' ')
|
||
|
{
|
||
|
str[i] = '_';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
str[i] = CharToLower(str[i]);
|
||
|
}
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
static unsigned int global_crc32_table[] =
|
||
|
{
|
||
|
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
|
||
|
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
|
||
|
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
||
|
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||
|
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
|
||
|
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
||
|
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
|
||
|
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
|
||
|
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||
|
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
|
||
|
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
|
||
|
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
||
|
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
|
||
|
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||
|
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
||
|
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
|
||
|
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
|
||
|
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
||
|
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||
|
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
|
||
|
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
||
|
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
|
||
|
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
|
||
|
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||
|
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
|
||
|
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
|
||
|
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
||
|
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
|
||
|
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||
|
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
||
|
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
|
||
|
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
|
||
|
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
||
|
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||
|
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
|
||
|
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
||
|
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
|
||
|
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
|
||
|
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||
|
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
|
||
|
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
|
||
|
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
||
|
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
|
||
|
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||
|
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
||
|
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
|
||
|
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
|
||
|
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
||
|
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||
|
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
|
||
|
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
||
|
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
|
||
|
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
|
||
|
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||
|
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
|
||
|
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
|
||
|
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
||
|
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
|
||
|
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||
|
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
||
|
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
|
||
|
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
|
||
|
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
||
|
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
|
||
|
};
|
||
|
|
||
|
internal u32
|
||
|
CStringCRC32N(char *name, u32 n)
|
||
|
{
|
||
|
u32 crc = 0;
|
||
|
for(u32 i = 0; name[i] && i < n; ++i)
|
||
|
{
|
||
|
crc = (crc << 8) ^ global_crc32_table[((crc >> 24) ^ name[i]) & 255];
|
||
|
}
|
||
|
return crc;
|
||
|
}
|
||
|
|
||
|
internal u32
|
||
|
CStringCRC32(char *name)
|
||
|
{
|
||
|
return CStringCRC32N(name, (u32)(u32)(-1));
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
AppendToFixedSizeCString(char *destination, u32 destination_max, char *str)
|
||
|
{
|
||
|
u32 i = 0;
|
||
|
for(; i < destination_max && destination[i]; ++i);
|
||
|
if(destination_max > i)
|
||
|
{
|
||
|
CopyCStringToFixedSizeBuffer(destination + i, destination_max - i, str);
|
||
|
}
|
||
|
}
|