Compare commits

..

No commits in common. "3f3d96cc30d7a8ac86246b8f5df411238358bf55" and "62f5d8394be3efda59807d47c08deec55d2032ae" have entirely different histories.

6 changed files with 53 additions and 1135 deletions

4
INBOX
View file

@ -16,7 +16,3 @@
it to add a 'SIMD' constant that we set for
targets that will almost always support SIMD
instructions; unsure for now.
05.31.25 Jesse
platform/arch merge sounds good, we can keep that going. No thoughts so far on that.
The caching is a good idea, I wonder if we should make them enum_flags.
I don't know if it will go over a u64 though.

4
TODO
View file

@ -4,8 +4,10 @@
017 [x] re-implement dynamic arrays (should mirror procedures on 'Static_Array')
[Jesse]
011 [math] add more Vec math procedures
034 [math] Rect '#type,distinct Vec(4, T)' type + rectcut
002 [math] use simd for Mat4 operations
041 [math] small test suite
*** UP NEXT (NO PARTICULAR ORDER) ***
@ -72,8 +74,6 @@
*** DONE ***
041 [math] small test suite
011 [math] add more Vec math procedures
010 [x] make base jai files standalone (ie. [array], [memory], etc... big maybe)
042 [x] use absolute imports internally (ie. #import "jc/math"), update symlink/install process in README
000 [math] add easing procedures

View file

@ -1,86 +0,0 @@
PI :: 3.1415926;
TAU :: 6.2831853;
#if UNITS == .turns {
to_rad :: turn_to_rad;
from_rad :: rad_to_turn;
}
else #if UNITS == .radians {
to_rad :: rad_to_rad;
from_rad :: rad_to_rad;
}
else #if UNITS == .degrees {
to_rad :: deg_to_rad;
from_rad :: rad_to_deg;
}
turn_to_rad :: (turns: float64) -> float #expand #no_debug {
CONSTANT :float64: PI*2.0;
return cast(float)(CONSTANT*turns);
}
turn_to_rad :: (turns: float) -> float #expand #no_debug {
CONSTANT :float: xx PI*2.0;
return CONSTANT*turns;
}
deg_to_rad :: (deg: float64) -> float #expand #no_debug {
CONSTANT :float64: PI/180.0;
return cast(float)(CONSTANT*deg);
}
deg_to_rad :: (deg: float) -> float #expand #no_debug {
CONSTANT :float: xx (PI/180.0);
return CONSTANT*deg;
}
rad_to_rad :: (rad: float64) -> float #expand #no_debug {
return cast(float)rad;
}
rad_to_rad :: (rad: float) -> float #expand #no_debug {
return rad;
}
rad_to_turn :: (rad: float64) -> float #expand {
CONSTANT :float64: (1.0/(PI*2.0));
return cast(float)(CONSTANT*rad);
}
rad_to_turn :: (rad: float) -> float #expand {
CONSTANT :float: xx (1.0/(PI*2.0));
return CONSTANT*rad;
}
rad_to_deg :: (rad: float64) -> float #expand {
CONSTANT :float64: (180.0/PI);
return cast(float)(CONSTANT*rad);
}
rad_to_deg :: (rad: float) -> float #expand {
CONSTANT :float: xx (180.0/PI);
return CONSTANT*rad;
}
sin :: (ang: float) -> float #expand {
return math.sin(to_rad(ang));
}
cos :: (ang: float) -> float #expand {
return math.cos(to_rad(ang));
}
atan2 :: (y: float, x: float) -> float #expand {
return from_rad(math.atan2(y, x));
}
asin :: (ang: float) -> float #expand {
return from_rad(math.asin(ang));
}
abs :: (v: $T) -> T
#modify { return meta.type_is_scalar(T), "Only accepting scalar types, integer and float"; } {
return ifx v < 0 then -v else v;
}
#scope_file
// sin, cos
meta :: #import "jc/meta";
math :: #import "Math";

View file

@ -1,24 +1,5 @@
Mat2 :: #type,distinct Vec(2 * 2, float);
Mat4 :: #type,distinct Vec(4 * 4, float);
m2 :: (_xx: float, xy: float, yx: float, yy: float) -> Mat2 #expand {
m: Mat2 = ---;
m._00 = _xx;
m._01 = xy;
m._10 = yx;
m._11 = yy;
return m;
}
m2 :: (r0: Vec2, r1: Vec2) -> Mat2 #expand {
return .{
components = .[
r0.x, r0.y,
r1.x, r1.y,
]
};
}
m4 :: (r0: Vec4, r1: Vec4, r2: Vec4, r3: Vec4) -> Mat4 #expand {
return .{
components = .[
@ -30,66 +11,17 @@ m4 :: (r0: Vec4, r1: Vec4, r2: Vec4, r3: Vec4) -> Mat4 #expand {
};
}
m2_identity :: Mat2.{_00=1, _11=1};
m4_identity :: Mat4.{_00=1, _11=1, _22=1, _33=1};
m2d :: (diag: float) -> Mat2 #expand {
r: Mat2;
r._00 = diag;
r._11 = diag;
return r;
}
m4_identity :: #bake_arguments m4d(diag = 1);
m4d :: (diag: float) -> Mat4 #expand {
res: Mat4;
res._00 = diag;
res._11 = diag;
res._22 = diag;
res._33 = diag;
res[0][0] = diag;
res[1][1] = diag;
res[2][2] = diag;
res[3][3] = diag;
return res;
}
operator [] :: inline (m: Mat2, idx: int) -> Vec2 #expand {
bounds_check_index(idx * 2, m.N);
return (m.components[idx * 2]).(*Vec4).*;
}
operator *[] :: inline (m: *Mat2, $$idx: int) -> *Vec2 #no_abc {
N :: Mat2.{}.N; // @note(judah): dumb workaround for not being able to access pointer type field constants
bounds_check_index(idx * 2, N);
return (*m.components[idx * 2]).(*Vec4);
}
operator []= :: inline (m: *Mat2, $$idx: int, value: Vec2) #no_abc {
N :: Mat2.{}.N;
bounds_check_index(idx * 4, N);
ptr := (*m.components[idx * 4]).(*Vec2);
ptr.*= value;
}
operator* :: inline (a: Mat2, b: Mat2) -> Mat2 {
r: Mat2 = ---;
r._00 = a._00*b._00 + a._01*b._10;
r._01 = a._00*b._01 + a._01*b._11;
r._10 = a._10*b._00 + a._11*b._10;
r._11 = a._10*b._01 + a._11*b._11;
return r;
}
operator* :: inline (a: Mat2, b: Vec2) -> Vec2 {
v: Vec2 = ---;
v.x = a._00*b.x + a._01*b.y;
v.y = a._10*b.x + a._11*b.y;
return v;
}
// Note(Jesse): Probably never used except for testing
operator== :: (a: Mat2, b: Mat2) -> bool #no_abc {
for a.components if it != b.components[it_index] return false;
return true;
}
operator [] :: inline (m: Mat4, idx: int) -> Vec4 #expand {
meta.check_bounds(idx * 4, m.N);
return (m.components[idx * 4]).(*Vec4).*;
@ -109,294 +41,3 @@ operator []= :: inline (m: *Mat4, $$idx: int, value: Vec4) #no_abc {
ptr.*= value;
}
// Note(Jesse): Probably never used except for testing
operator== :: (a: Mat4, b: Mat4) -> bool #no_abc {
for a.components if it != b.components[it_index] return false;
return true;
}
translate :: (from: Mat4, with: Vec3) -> Mat4 {
r := from;
r._03 += with.x*r._00 + with.y*r._01 + with.z*r._02;
r._13 += with.x*r._10 + with.y*r._11 + with.z*r._12;
r._23 += with.x*r._20 + with.y*r._21 + with.z*r._22;
return r;
}
translate :: (with: Vec3) -> Mat4 {
r: Mat4 = m4_identity;
r._03 = with.x;
r._13 = with.y;
r._23 = with.z;
return r;
}
make_look_at :: (camera: Vec3, at: Vec3, up_vector: Vec3) -> Mat4
{
// -Z forward, Y up, X right
forward := normalize(at - camera);
right := normalize(cross(up_vector, forward)); // up_vector
up := normalize(cross(forward, right));
look_at: Mat4;
look_at._00 = right.x;
look_at._01 = right.y;
look_at._02 = right.z;
look_at._10 = up.x;
look_at._11 = up.y;
look_at._12 = up.z;
look_at._20 = -forward.x;
look_at._21 = -forward.y;
look_at._22 = -forward.z;
look_at._33 = 1;
look_at = inline translate(look_at, -camera);
return look_at;
}
operator* :: (a: Mat4, b: Mat4) -> Mat4 {
r: Mat4 = ---;
r._00 = a._00*b._00 + a._01*b._10 + a._02*b._20 + a._03*b._30;
r._01 = a._00*b._01 + a._01*b._11 + a._02*b._21 + a._03*b._31;
r._02 = a._00*b._02 + a._01*b._12 + a._02*b._22 + a._03*b._32;
r._03 = a._00*b._03 + a._01*b._13 + a._02*b._23 + a._03*b._33;
r._10 = a._10*b._00 + a._11*b._10 + a._12*b._20 + a._13*b._30;
r._11 = a._10*b._01 + a._11*b._11 + a._12*b._21 + a._13*b._31;
r._12 = a._10*b._02 + a._11*b._12 + a._12*b._22 + a._13*b._32;
r._13 = a._10*b._03 + a._11*b._13 + a._12*b._23 + a._13*b._33;
r._20 = a._20*b._00 + a._21*b._10 + a._22*b._20 + a._23*b._30;
r._21 = a._20*b._01 + a._21*b._11 + a._22*b._21 + a._23*b._31;
r._22 = a._20*b._02 + a._21*b._12 + a._22*b._22 + a._23*b._32;
r._23 = a._20*b._03 + a._21*b._13 + a._22*b._23 + a._23*b._33;
r._30 = a._30*b._00 + a._31*b._10 + a._32*b._20 + a._33*b._30;
r._31 = a._30*b._01 + a._31*b._11 + a._32*b._21 + a._33*b._31;
r._32 = a._30*b._02 + a._31*b._12 + a._32*b._22 + a._33*b._32;
r._33 = a._30*b._03 + a._31*b._13 + a._32*b._23 + a._33*b._33;
return r;
}
// Note(Jesse): If you want to make it symmetric go ahead, I usually don't
operator* :: (a: Mat4, v: Vec4) -> Vec4 {
r: Vec4 = ---;
r.x = a._00*v.x + a._01*v.y + a._02*v.z + a._03*v.w;
r.y = a._10*v.x + a._11*v.y + a._12*v.z + a._13*v.w;
r.z = a._20*v.x + a._21*v.y + a._22*v.z + a._23*v.w;
r.w = a._30*v.x + a._31*v.y + a._32*v.z + a._33*v.w;
return r;
}
transpose :: (m: Mat2) -> Mat2 {
r: Mat2 = ---;
// Todo(Jesse): This can be one call to simd shuffle
r._00 = m._00;
r._01 = m._10;
r._10 = m._01;
r._11 = m._11;
return r;
}
transpose :: (m: Mat4) -> Mat4 {
r: Mat4 = ---;
r._00 = m._00;
r._01 = m._10;
r._02 = m._20;
r._03 = m._30;
r._10 = m._01;
r._11 = m._11;
r._12 = m._21;
r._13 = m._31;
r._20 = m._02;
r._21 = m._12;
r._22 = m._22;
r._23 = m._32;
r._30 = m._03;
r._31 = m._13;
r._32 = m._23;
r._33 = m._33;
return r;
}
// aspect is width/height
// NDC Depth depth_01
// DirectX: 0-1 true
// Vulkan: 0-1 true
// Opengl: -1-1 false
perspective_projection :: (aspect: float, fov: float, near: float, far: float, $$depth_01: bool) -> Mat4 {
r: Mat4 = ---;
// Y up, X right, Z- forward
fov_part := 1.0/tan(fov*0.5);
denom := (far - near);
r._00 = fov_part/aspect;
r._11 = fov_part;
r._22 = -(far + near)/denom;
r._23 = -2*far*near/denom;
r._32 = -1;
r._33 = 1;
if depth_01 {
// Makes depth range from 0 to 1
// To map -1,1 depth range to 0,1 we transform z as follows: z' = z * 0.5 + 0.5
r._22 = r._22 * 0.5 + r._32 * 0.5;
r._23 = r._23 * 0.5 + r._33 * 0.5;
}
return r;
}
perspective_projection_left_handed :: (aspect: float, fov: float, near: float, far: float, $$depth_01: bool) -> Mat4 {
r: Mat4 = ---;
// Y up, X right, Z- forward
fov_part := 1.0/tan(fov*0.5);
denom := (far - near);
r._00 = fov_part/aspect;
r._11 = fov_part;
r._22 = (far + near)/denom;
r._23 = 2*far*near/denom;
r._32 = -1;
r._33 = 1;
if depth_01 {
// Makes depth range from 0 to 1
// To map -1,1 depth range to 0,1 we transform z as follows: z' = z * 0.5 + 0.5
r._22 = r._22 * 0.5 + r._32 * 0.5;
r._23 = r._23 * 0.5 + r._33 * 0.5;
}
return r;
}
// Note(Jesse): Typically left/right/top/bottom are pixel coords
// NDC Depth depth_01
// DirectX: 0-1 true
// Vulkan: 0-1 true
// Opengl: -1-1 false
orthographic_projection :: (left: float, right: float, top: float, bottom: float, near: float, far: float, $$depth_01: bool) -> Mat4 {
r: Mat4 = m4_identity;
r._00 = 2.0/(right - left);
r._11 = 2.0/(top - bottom);
r._22 = -1.0/(far - near);
if depth_01 == false then r._22 *= 2;
r._30 = -(right + left)/(right - left);
r._31 = -(top + bottom)/(top - bottom);
r._32 = -(far + near)/(far - near);
r._33 = 1;
return r;
}
rotation_mat2 :: (angle: float) -> Mat2 {
m: Mat2 = ---;
m._00 = cos(angle);
m._01 = -sin(angle);
m._10 = sin(angle);
m._11 = cos(angle);
return m;
}
rotation_mat4 :: (axis: Vec3, angle: float) -> Mat4 {
m := m4_identity;
a := normalize(axis);
sin_theta := sin(angle);
cos_theta := cos(angle);
inv_cos := 1.0 - cos_theta;
// Contributors to vector's X
m._00 = (a.x*a.x*inv_cos) + cos_theta;
m._01 = (a.x*a.y*inv_cos) + (a.z*sin_theta);
m._02 = (a.x*a.z*inv_cos) - (a.y*sin_theta);
// Contributors to vector's Y
m._10 = (a.y*a.x*inv_cos) - (a.z*sin_theta);
m._11 = (a.y*a.y*inv_cos) + cos_theta;
m._12 = (a.y*a.z*inv_cos) + (a.x*sin_theta);
// Contributors to vector's Z
m._20 = (a.z*a.x*inv_cos) + (a.y*sin_theta);
m._21 = (a.z*a.y*inv_cos) - (a.x*sin_theta);
m._22 = (a.z*a.z*inv_cos) + cos_theta;
return m;
}
rotation_mat4 :: (q: Quat) -> Mat4 {
m := m4_identity;
xs := 2*q.x;
ys := 2*q.y;
zs := 2*q.z;
wx := q.w*xs;
wy := q.w*ys;
wz := q.w*zs;
_xx := q.x*xs;
xy := q.x*ys;
xz := q.x*zs;
yy := q.y*ys;
yz := q.y*zs;
zz := q.z*zs;
m._00 = 1.0 - (yy + zz);
m._01 = xy - wz;
m._02 = xz + wy;
m._10 = xy + wz;
m._11 = 1.0 - (_xx + zz);
m._12 = yz - wx;
m._20 = xz - wy;
m._21 = yz + wx;
m._22 = 1.0 - (_xx + yy);
return m;
}
determinate :: (m: Mat2) -> float {
return m._00*m._11 - m._01*m._10;
}
determinate :: (m: Mat4) -> float {
c01 := cross(m.v4[0].xyz, m.v4[1].xyz);
c23 := cross(m.v4[2].xyz, m.v4[3].xyz);
b10 := m.v4[0].xyz*m.v4[1].w - m.v4[1].xyz*m.v4[0].w;
b32 := m.v4[2].xyz*m.v4[3].w - m.v4[3].xyz*m.v4[2].w;
return dot(c01, b32) + dot(c23, b10);
}
inverse :: (m: Mat2) -> Mat2 {
r: Mat2 = ---;
invdet := 1.0/determinate(m);
r._00 = invdet* m._11;
r._01 = invdet*-m._01;
r._10 = invdet*-m._10;
r._11 = invdet* m._00;
return r;
}
inverse :: (m: Mat4) -> Mat4 {
c01 := cross(m.v4[0].xyz, m.v4[1].xyz);
c23 := cross(m.v4[2].xyz, m.v4[3].xyz);
b10 := m.v4[0].xyz*m.v4[1].w - m.v4[1].xyz*m.v4[0].w;
b32 := m.v4[2].xyz*m.v4[3].w - m.v4[3].xyz*m.v4[2].w;
det := dot(c01, b32) + dot(c23, b10);
if abs(det) < 0.0001
return m4_identity;
inv_det := 1.0/det;
c01 *= inv_det;
c23 *= inv_det;
b10 *= inv_det;
b32 *= inv_det;
r: Mat4 = ---;
r.v4[0] = v4f(cross(m.v4[1].xyz, b32) + c23*m.v4[1].w, -dot(m.v4[1].xyz, c23));
r.v4[1] = v4f(cross(b32, m.v4[0].xyz) + c23*m.v4[0].w, dot(m.v4[0].xyz, c23));
r.v4[2] = v4f(cross(m.v4[3].xyz, b10) + c01*m.v4[3].w, -dot(m.v4[3].xyz, c01));
r.v4[3] = v4f(cross(b10, m.v4[2].xyz) + c01*m.v4[2].w, dot(m.v4[2].xyz, c01));
return transpose(r);
}

View file

@ -21,359 +21,14 @@ F64_Min, F64_Max :: #run meta.lo_for(float64), #run meta.hi_for(float64);
#load "vec.jai";
#load "mat.jai";
#load "ease.jai";
#load "common.jai";
#scope_module;
#if RUN_TESTS #run {
#if RUN_TESTS {
test :: #import "jc/test";
vec2_tests();
vec3_tests();
vec4_tests();
vecn_tests();
mat2_tests();
mat4_tests();
quat_tests();
}
#scope_file;
meta :: #import "jc/meta";
basic :: #import "Basic"; // @future
math :: #import "Math";
test :: #import "jc/test";
vec2_tests :: () {
dot_print("Vec2 % tests", UNITS);
t: test.T;
{
a: Vec2 = v2f(0.0, 1.0);
b: Vec2 = v2f(1.0, 2.0);
test.expect(*t, a + b == v2f(1.0, 3.0));
test.expect(*t, b - a == v2f(1.0, 1.0));
test.expect(*t, a*b == v2f(0.0, 2.0));
test.expect(*t, a/b == v2f(0.0, 0.5));
}
{
a: Vec(2, int) = v2i(2, 1);
b: Vec(2, int) = v2i(1, 0);
test.expect(*t, a + b == v2i(3, 1));
test.expect(*t, b - a == v2i(-1, -1));
test.expect(*t, a*b == v2i(2, 0));
test.expect(*t, b/a == v2i(0, 0));
}
{
a: Vec2 = v2f(2.3, -4.1);
b: Vec2 = v2f(1.0, 3.6);
c := min(a, b);
test.expect(*t, c == v2f(1.0, -4.1));
c = max(a, b);
test.expect(*t, c == v2f(2.3, 3.6));
}
}
vec3_tests :: () {
dot_print("Vec3 % tests", UNITS);
t: test.T;
{
a: Vec3 = v3f(0.0, 1.0, 2.0);
b: Vec3 = v3f(1.0, 2.0, 3.0);
test.expect(*t, a + b == v3f(1.0, 3.0, 5.0));
test.expect(*t, b - a == v3f(1.0, 1.0, 1.0));
test.expect(*t, a*b == v3f(0.0, 2.0, 6.0));
test.expect(*t, a/b == v3f(0.0, 0.5, 0.66666667));
a = v3f(1.0, 1.0, 0.0);
b = v3f(1.0, 0.0, 0.0);
test.expect(*t, reflect(a, b) == v3f(1.0, -1.0, 0.0));
test.expect(*t, round(v3f(1.2, 1.7, 1.5)) == v3f(1.0, 2.0, 2.0));
test.expect(*t, round(v3f(-1.2, -1.7, -1.5)) == v3f(-1.0, -2.0, -2.0));
a = v3f(1.0, 0.0, 0.0);
b = v3f(0.0, 1.0, 0.0);
test.expect(*t, cross(a, b) == v3f(0.0, 0.0, 1.0));
}
{
a: Vec3 = v3f(2.3, 4.1, 9.0);
b: Vec3 = v3f(1.0, -3.6, 5.0);
c := min(a, b);
test.expect(*t, c == v3f(1.0, -3.6, 5.0));
c = max(a, b);
test.expect(*t, c == v3f(2.3, 4.1, 9.0));
}
}
vec4_tests :: () {
dot_print("Vec4 % tests", UNITS);
t: test.T;
{
a: Vec4 = v4f(2.25, 1.0, 2.0, 1.0);
b: Vec4 = v4f(4.0, 2.0, 3.0, 1.0);
test.expect(*t, a + b == v4f(6.25, 3.0, 5.0, 2.0));
test.expect(*t, b - a == v4f(1.75, 1.0, 1.0, 0.0));
test.expect(*t, a*b == v4f(9.0, 2.0, 6.0, 1.0));
test.expect(*t, a/b == v4f(0.5625, 0.5, 2.0/3.0, 1.0));
}
}
vecn_tests :: () {
dot_print("VecN % tests", UNITS);
t: test.T;
{
a: Vec(16, float);
b: Vec(16, float);
for *a {
it.* = xx it_index;
}
for *b {
it.* = xx(it_index + 1);
}
test.expect(*t, a + b == Vec(16, float).{.[1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0, 25.0, 27.0, 29.0, 31.0]});
test.expect(*t, b - a == Vec(16, float).{.[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]});
test.expect(*t, a*b == Vec(16, float).{.[0.0, 2.0, 6.0, 12.0, 20.0, 30.0, 42.0, 56.0, 72.0, 90.0, 110.0, 132.0, 156.0, 182.0, 210.0, 240.0]});
test.expect(*t, min(a, b) == a);
test.expect(*t, max(a, b) == b);
test.expect(*t, max(a, 12) == Vec(16, float).{.[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 12.0, 12.0, 12.0]});
test.expect(*t, min(a, 13.2) == Vec(16, float).{.[13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 13.2, 14.0, 15.0]});
test.expect(*t, clamp(a, 7.25, 12.0) == Vec(16, float).{.[7.25, 7.25, 7.25, 7.25, 7.25, 7.25, 7.25, 7.25, 8, 9, 10, 11, 12, 12, 12, 12]});
a1: Vec(16, float) = Vec(16, float).{.[1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 4.7, -1.2, -1.0, -1.5, 11.2, 14.0, 15.0, 14.0, 15.0, 65536.2]};
basic.print(">> a1: %\n", a1);
basic.print(">> ceil(a1): %\n", ceil(a1));
basic.print(">> floor(a1): %\n", floor(a1));
test.expect(*t, ceil(a1) == Vec(16, float).{.[2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 5.0, -1.0, -1.0, -1.0, 12.0, 14.0, 15.0, 14.0, 15.0, 65537]});
test.expect(*t, floor(a1) == Vec(16, float).{.[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 4.0, -2.0, -1.0, -2.0, 11.0, 14.0, 15.0, 14.0, 15.0, 65536]});
test.expect(*t, dot(a, b) == 1360.0);
test.expect(*t, abs(a) == a);
c := a;
for *c { // Check making every other component negative
if it_index%2 == 0
it.* = -it.*;
}
test.expect(*t, abs(c) == a);
test.expect(*t, float_eq(length(normalize(a)), 1));
test.expect(*t, a + 2 == Vec(16, float).{.[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]});
test.expect(*t, a - 2 == Vec(16, float).{.[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]});
test.expect(*t, a*2 == Vec(16, float).{.[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]});
}
}
mat2_tests :: () {
dot_print("Mat2 % tests", UNITS);
t: test.T;
{
identity := m2_identity;
a: Mat2 = Mat2.{.[1, 2, 3, 4]};
test.expect(*t, a*identity == a);
}
{
basic.print("rotation matrix\n");
rotator := rotation_mat2(from_rad(PI));
v2 := v2f(1.0, 0.5);
basic.print("rotating: %\n", v2);
v2 = rotator*v2;
basic.print("after rotate: %\n", v2);
test.expect(*t, v2_eq(v2, v2f(-1.0, -0.5)));
v2 = rotator*v2;
basic.print("rotate back: %\n", v2);
test.expect(*t, v2_eq(v2, v2f(1.0, 0.5)));
}
{
basic.print("determinate\n");
m := m2(1.0, 3.0, 2.0, 2.0);
basic.print("m2: %\n", m);
det := determinate(m);
basic.print("determinate: %\n", det);
test.expect(*t, det == -4);
}
{
basic.print("inverse test\n");
rotator := rotation_mat2(from_rad(PI));
inv_rotator := inverse(rotator);
v2 := v2f(0.1, 1.0);
basic.print(" : %\n", rotator);
basic.print("inv: %\n", inv_rotator);
basic.print("inv_mat*mat = %\n", inv_rotator*rotator);
test.expect(*t, inv_rotator*rotator == m2_identity);
test.expect(*t, inv_rotator*(rotator*v2) == v2);
basic.print("v2 : %\n", v2);
v2 = rotator*v2;
basic.print("rot : %\n", v2);
v2 = inv_rotator*v2;
basic.print("inv : %\n", v2);
}
}
mat4_tests :: () {
dot_print("Mat4 % tests", UNITS);
t: test.T;
{
identity := m4_identity;
a: Mat4 = Mat4.{.[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]};
test.expect(*t, a*identity == a);
}
{
UP_VECTOR :: Vec3.{.[0.0, 1.0, 0.0]};
camera := v3f(1.0, 0.0, 1.0);
looking_at := v3f(0.0, 0.0, 0.0);
look_at := make_look_at(camera, looking_at, UP_VECTOR);
basic.print("lookat matrix: %\n", look_at);
basic.print("lookat from 0,1,0: %\n", look_at*v4f(0.0, 1.0, 0.0, 1.0));
}
{
translator := translate(v3f(10.0, 5.0, 2.0));
v3 := v3f(1.0, 2.0, 1.0);
v4 := v4f(v3, 1.0);
test.expect(*t, v4 == v4f(1.0, 2.0, 1.0, 1.0));
test.expect(*t, translator*v4 == v4f(11.0, 7.0, 3.0, 1.0));
}
{
basic.print("rotation matrix\n");
rotator := rotation_mat4(v3f(0.0, 1.0, 0.0), from_rad(PI));
v4 := v4f(1.0, 0.5, 0.1, 1.0);
basic.print("rotating: %\n", v4);
v4 = rotator*v4;
basic.print("after rotate: %\n", v4);
test.expect(*t, v4_eq(v4, v4f(-1.0, 0.5, -0.1, 1.0)));
v4 = rotator*v4;
basic.print("rotate back: %\n", v4);
test.expect(*t, v4_eq(v4, v4f(1.0, 0.5, 0.1, 1.0)));
}
{
rotator_x := rotation_mat4(v3f(1.0, 0.0, 0.0), from_rad(0.4*PI));
rotator_y := rotation_mat4(v3f(0.0, 1.0, 0.0), from_rad(PI));
camera_rotator := rotator_x*rotator_y;
basic.print("big rotator: %\n", camera_rotator);
det := determinate(camera_rotator);
basic.print("determinate: %\n", det);
}
{
basic.print("inverse test\n");
rotator := rotation_mat4(v3f(0.0, 0.0, 1.0), from_rad(PI));
inv_rotator := inverse(rotator);
v4 := v4f(0.1, 1.0, 0.0, 1.0);
basic.print(" : %\n", rotator);
basic.print("inv: %\n", inv_rotator);
basic.print("inv_mat*mat = %\n", inv_rotator*rotator);
test.expect(*t, inv_rotator*rotator == m4_identity);
basic.print("v4 : %\n", v4);
v4 = rotator*v4;
basic.print("rot : %\n", v4);
v4 = inv_rotator*v4;
basic.print("inv : %\n", v4);
}
}
quat_tests :: () {
dot_print("Quaternion % tests", UNITS);
t: test.T;
{
qi := quat_identity;
basic.print("qi lensq: %\n", length_squared(qi));
basic.print("qi len : %\n", length(qi));
q: Quat = rotation_quat(from_rad(PI), v3f(0.0, 1.0, 0.0));
q2: Quat = rotation_quat(from_rad(PI), v3f(1.0, 0.0, 1.0));
basic.print("q : %\n", q);
basic.print("q2 : %\n", q2);
basic.print("dot : %\n", dot(q, q2));
basic.print("mul : %\n", q*q2);
qc := conjugate(q);
basic.print("conjugate : %\n", qc);
inv_q := inverse(q);
basic.print("inverse : %\n", inv_q);
test.expect(*t, q*inv_q == qi);
q1 := quat(2, 0, 0, 0);
q2 = quat(1, 1, -1, 0);
basic.print("q1: %\n", q1);
basic.print("q2: %\n", q2);
basic.print("q1*q2: %\n", q1*q2);
basic.print("dot(q2,q2): %\n", dot(q2, q2));
basic.print("dot(q1,q2): %\n", dot(q1, q2));
basic.print("q1*q2*conj(q1): %\n", q1*q2*conjugate(q1));
c := q1*q2*conjugate(q1);
test.expect(*t, float_eq(c.w, 0.0));
q = rotation_quat(from_rad(PI/4.0), v3f(0.0, 0.0, 1.0));
p := v3f(2.0, 0.0, 0.0);
basic.print("q: %\n", q);
basic.print("p: %\n", p);
c1 := q*quat(p, 0)*conjugate(q);
basic.print("q*quat(p, 0)*conjugate(q): %\n", c1);
c2 := q*p;
basic.print("q*p : %\n", c2);
test.expect(*t, v3_eq(c2, v3f(math.sqrt(2.0), math.sqrt(2.0), 0.0)));
test.expect(*t, v3_eq(c1.xyz, c2));
basic.print("quaternion to matrix rotation\n");
q = rotation_quat(from_rad(PI), v3f(0.0, 1.0, 0.0));
basic.print("q: %\n", q);
m := rotation_mat4(q);
print_mat4(m);
p1 := v4f(2.0, 0.0, 0.0, 1.0);
basic.print("p : %\n", p1);
basic.print("p': %\n", m*p1);
test.expect(*t, v4_eq(m*p1, v4f(-2.0, 0.0, -0.0, 1.0)));
q1 = rotation_quat(from_rad(PI), v3f(0.0, 1.0, 0.0));
q2 = rotation_quat(from_rad(2.0*PI), v3f(0.0, 1.0, 0.0));
perc := 0.5;
q = slerp(q1, q2, perc);
basic.print("q1: %\n", q1);
basic.print("q2: %\n", q2);
basic.print("slerp q1, q2, %: %\n", perc, q);
q = rotation_quat(from_rad(PI/4.0), v3f(0.0, 0.0, 1.0));
print_mat4(rotation_mat4(q));
}
}
dot_print :: (fmt: string, args: ..Any, width: int = 30) {
dots := ".................................................................";
str := basic.tprint(fmt, args);
dots.count = width - str.count;
basic.print("%0%", str, dots);
basic.print("\n");
}
check :: (cond: bool, loc := #caller_location) -> bool {
if !cond {
basic.print("\tTest failed at %:%\n", loc.fully_pathed_filename, loc.line_number);
}
return cond;
}
v2_eq :: (a: Vec2, b: Vec2) -> bool {
return float_eq(a.x, b.x) && float_eq(a.y, b.y);
}
v3_eq :: (a: Vec3, b: Vec3) -> bool {
return float_eq(a.x, b.x) && float_eq(a.y, b.y) && float_eq(a.z, b.z);
}
v4_eq :: (a: Vec4, b: Vec4) -> bool {
return float_eq(a.x, b.x) && float_eq(a.y, b.y) && float_eq(a.z, b.z) && float_eq(a.w, b.w);
}
// Smallest difference where a float is basically that value
EPSILON :: 0.001;
float_eq :: (f: float, with: float) -> bool {
return f > with - EPSILON && f < with + EPSILON;
}
print_mat4 :: (m: Mat4) {
basic.print("| % % % % |\n", m._00, m._01, m._02, m._03);
basic.print("| % % % % |\n", m._10, m._11, m._12, m._13);
basic.print("| % % % % |\n", m._20, m._21, m._22, m._23);
basic.print("| % % % % |\n", m._30, m._31, m._32, m._33);
}

View file

@ -66,38 +66,6 @@ Vec :: struct(N: int, T: Type)
}
}
// compound accessors
basic.append(*b, "#place components;\n");
if N >= 4 {
basic.append(*b, "union {\n");
basic.append(*b, "\txy: Vec2 = ---;\n");
basic.append(*b, "\txyz: Vec3 = ---;\n");
basic.append(*b, "};");
}
else if N >= 3 {
basic.append(*b, "xy: T = ---;\n");
}
// Vec4 row accessors (Possibly becoming/used as SIMD lanes)
if N/4 > 1 {
basic.append(*b, "#place components;\n");
basic.print_to_builder(*b, "v4: [%]Vec4 = ---;\n", N/4);
}
// Matrix Accessors
if N == 4 { // Mat2
basic.append(*b, "#place components;\n");
for i: 0..3 {
basic.print_to_builder(*b, "_%0%: T = ---;\n", i/2, i%2);
}
}
if N == 16 { // Mat4
basic.append(*b, "#place components;\n");
for i: 0..15 {
basic.print_to_builder(*b, "_%0%: T = ---;\n", i/4, i%4);
}
}
return basic.builder_to_string(*b);
};
}
@ -132,268 +100,118 @@ for_expansion :: (v: *Vec, body: Code, flags: For_Flags) #expand {
operator + :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l.x + r.x;
res.y = l.y + r.y;
#if l.N >= 3 then res.z = l.z + r.z;
#if l.N == 4 then res.w = l.w + r.w; // @todo(jesse): SIMD
}
else {
for l res[it_index] = it + r[it_index];
}
// @todo(judah): unroll for N <= 4
for l res[it_index] = it + r[it_index];
return res;
}
operator - :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l.x - r.x;
res.y = l.y - r.y;
#if l.N >= 3 then res.z = l.z - r.z;
#if l.N == 4 then res.w = l.w - r.w; // @todo(jesse): SIMD
}
else {
for l res[it_index] = it - r[it_index];
}
// @todo(judah): unroll for N <= 4
for l res[it_index] = it - r[it_index];
return res;
}
operator * :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l.x*r.x;
res.y = l.y*r.y;
#if l.N >= 3 then res.z = l.z*r.z;
#if l.N == 4 then res.w = l.w*r.w; // @todo(jesse): SIMD
}
else {
for l res[it_index] = it * r[it_index];
}
// @todo(judah): unroll for N <= 4
for l res[it_index] = it * r[it_index];
return res;
}
operator / :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l.x/r.x;
res.y = l.y/r.y;
#if l.N >= 3 then res.z = l.z/r.z;
#if l.N == 4 then res.w = l.w/r.w; // @todo(jesse): SIMD
}
else {
for l res[it_index] = it / r[it_index];
}
// @todo(judah): unroll for N <= 4
for l res[it_index] = it / r[it_index];
return res;
}
operator + :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric
#modify { return meta.type_is_scalar(R), "type is not integer or float"; } {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l.x + r;
res.y = l.y + r;
#if l.N >= 3 then res.z = l.z + r;
#if l.N == 4 then res.w = l.w + r; // @todo(jesse): SIMD
}
else {
for l res[it_index] = it + r;
}
for l res[it_index] = it + r;
return res;
}
operator - :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc
#modify { return meta.type_is_scalar(R), "type is not integer or float"; } {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l.x - r;
res.y = l.y - r;
#if l.N >= 3 then res.z = l.z - r;
#if l.N == 4 then res.w = l.w - r; // @todo(jesse): SIMD
}
else {
for l res[it_index] = it - r;
}
for l res[it_index] = it - r;
return res;
}
operator - :: inline (l: $R, r: Vec) -> Vec(l.N, l.T) #no_abc
#modify { return meta.type_is_scalar(R), "type is not integer or float"; } {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l - r.x;
res.y = l - r.y;
#if r.N >= 3 then res.z = l - r.z;
#if r.N == 4 then res.w = l - r.w; // @todo(jesse): SIMD
}
else {
for l res[it_index] = r - it;
}
return res;
}
operator- :: inline(v: Vec) -> Vec(v.N, v.T) #no_abc {
res: Vec(v.N, v.T) = ---;
#if v.N <= 4 {
res.x = -v.x;
res.y = -v.y;
#if v.N >= 3 then res.z = -v.z;
#if v.N == 4 then res.w = -v.w; // @todo(jesse): SIMD
}
else {
for v res[it_index] = -it;
}
for l res[it_index] = r - it;
return res;
}
operator * :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric
#modify { return meta.type_is_scalar(R), "type is not integer or float"; } {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l.x*r;
res.y = l.y*r;
#if l.N >= 3 then res.z = l.z*r;
#if l.N == 4 then res.w = l.w*r; // @todo(jesse): SIMD
}
else {
for l res[it_index] = it*r;
}
for l res[it_index] = it*r;
return res;
}
operator / :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc
#modify { return meta.type_is_scalar(R), "type is not integer or float"; } {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = l.x/r;
res.y = l.y/r;
#if l.N >= 3 then res.z = l.z/r;
#if l.N == 4 then res.w = l.w/r; // @todo(jesse): SIMD
}
else {
for l res[it_index] = it/r;
}
for l res[it_index] = it/r;
return res;
}
operator == :: inline (l: Vec, r: Vec(l.N, l.T)) -> bool #no_abc {
#if l.N <= 4 {
res: bool = l.x == r.x && l.y == r.y;
#if l.N >= 3 then res &= l.z == r.z;
#if l.N == 4 then res &= l.w == r.w; // @todo(jesse): SIMD
return res;
}
else {
for l if it != r[it_index] return false;
return true;
}
for l if it != r[it_index] return false;
return true;
}
min :: (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = ifx l.x < r.x then l.x else r.x;
res.y = ifx l.y < r.y then l.y else r.y;
#if l.N >= 3 then res.z = ifx l.z < r.z then l.z else r.z;
#if l.N == 4 then res.w = ifx l.w < r.w then l.w else r.w; // @todo(jesse): SIMD
}
else {
n := l.N - 1;
while n >= 0 {
if l[n] < r[n]
res[n] = l[n];
else
res[n] = r[n];
n -= 1;
}
n := l.N - 1;
while n >= 0 {
if l[n] < r[n]
res[n] = l[n];
else
res[n] = r[n];
n -= 1;
}
return res;
}
max :: (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = ifx l.x > r.x then l.x else r.x;
res.y = ifx l.y > r.y then l.y else r.y;
#if l.N >= 3 then res.z = ifx l.z > r.z then l.z else r.z;
#if l.N == 4 then res.w = ifx l.w > r.w then l.w else r.w; // @todo(jesse): SIMD
}
else {
n := l.N - 1;
while n >= 0 {
if l[n] > r[n]
res[n] = l[n];
else
res[n] = r[n];
n -= 1;
}
n := l.N - 1;
while n >= 0 {
if l[n] > r[n]
res[n] = l[n];
else
res[n] = r[n];
n -= 1;
}
return res;
}
min :: (l: Vec, r: l.T) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = ifx l.x > r then l.x else r;
res.y = ifx l.y > r then l.y else r;
#if l.N >= 3 then res.z = ifx l.z > r.z then l.z else r;
#if l.N == 4 then res.w = ifx l.w > r.w then l.w else r; // @todo(jesse): SIMD
}
else {
n := l.N - 1;
while n >= 0 {
if l[n] > r
res[n] = l[n];
else
res[n] = r;
n -= 1;
}
}
return res;
}
max :: (l: Vec, r: l.T) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---;
#if l.N <= 4 {
res.x = ifx l.x < r then l.x else r;
res.y = ifx l.y < r then l.y else r;
#if l.N >= 3 then res.z = ifx l.z < r then l.z else r;
#if l.N == 4 then res.w = ifx l.w < r then l.w else r; // @todo(jesse): SIMD
}
else {
n := l.N - 1;
while n >= 0 {
if l[n] < r
res[n] = l[n];
else
res[n] = r;
n -= 1;
}
}
return res;
}
// @todo(jesse): SIMD
ceil :: (l: Vec) -> Vec(l.N, l.T) #no_abc {
ceil :: (l: Vec, x: l.T) -> Vec(l.N, l.T) #no_abc {
r: Vec(l.N, l.T) = ---;
n := l.N - 1;
sign: float;
while n >= 0 {
int_part := l[n].(int);
add_part := ifx l[n] > int_part.(float) then 1.0 else 0.0;
r[n] = int_part + add_part;
if x < l[n]
r[n] = x;
else
r[n] = l[n];
n -= 1;
}
return r;
}
// @todo(jesse): SIMD
floor :: (l: Vec) -> Vec(l.N, l.T) #no_abc {
floor :: (l: Vec, x: l.T) -> Vec(l.N, l.T) #no_abc {
r: Vec(l.N, l.T) = ---;
n := l.N - 1;
sign: float;
while n >= 0 {
int_part := l[n].(int);
sub_part := ifx l[n] < int_part.(float) then 1.0 else 0.0;
r[n] = int_part - sub_part;
if x > l[n]
r[n] = x;
else
r[n] = l[n];
n -= 1;
}
return r;
@ -425,11 +243,11 @@ dot :: (a: Vec, b: Vec(a.N, a.T)) -> a.T #no_abc {
}
length_squared :: (v: Vec) -> float #no_abc {
return inline dot(v, v);
return dot(v, v);
}
length :: (v: Vec) -> float #no_abc {
return math.sqrt(inline dot(v, v));
return math.sqrt(dot(v, v));
}
abs :: (v: Vec) -> Vec(v.N, v.T) #no_abc {
@ -445,11 +263,9 @@ abs :: (v: Vec) -> Vec(v.N, v.T) #no_abc {
return r;
}
// @todo(Jesse): SIMD
norm :: normalize;
normalize :: (v: Vec) -> Vec(v.N, v.T) #no_abc {
inv_len := 1.0/length(v);
return inv_len*v;
return v/length(v);
}
lerp :: (a: Vec, b: Vec(a.N, a.T), t: float) -> Vec(a.N, a.T) #no_abc {
@ -491,7 +307,7 @@ round :: (v: Vec($N, $T)) -> Vec(N, T) #no_abc
Vec2 :: Vec(2, float);
Vec3 :: Vec(3, float);
Vec4 :: Vec(4, float);
Quat :: #type,distinct Vec4; // Note(Jesse): I Had to make this distinct, otherwise operators stomp on eachother
Quat :: Vec4;
v2f :: (x: $T = 0, y: T = 0) -> Vec2
#modify { return meta.type_is_float(T), "use v2i for integer arguments"; }
@ -523,122 +339,18 @@ v4f :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec4
return .{ x = x, y = y, z = z, w = w };
}
v4f :: (v: Vec3, $$w: float) -> Vec4 #expand {
return .{ xyz=v, w=w };
}
v4i :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec(4, T)
#modify { return meta.type_is_integer(T), "use v4f for float arguments"; }
#expand {
return .{ x = x, y = y, z = z, w = w };
}
quat :: (x: float = 0, y: float = 0, z: float = 0, w: float = 1) -> Quat #expand {
quat :: (x: float = 0, y: float = 0, z: float = 0, w: float = 0) -> Quat #expand {
return .{ x = x, y = y, z = z, w = w };
}
quat :: (xyz: Vec3, w: float) -> Quat #expand {
return .{xyz=xyz, w=w};
}
quat_identity :: Quat.{x=0, y=0, z=0, w=1};
operator* :: (a: Quat, b: Quat) -> Quat {
r: Quat = ---;
r.xyz = a.w*b.xyz + b.w*a.xyz + cross(a.xyz, b.xyz);
r.w = a.w*b.w - dot(a.xyz, b.xyz);
return r;
}
operator* :: (q: Quat, v: Vec3) -> Vec3 #symmetric {
r: Vec3 = 2*cross(q.xyz, v);
return v + q.w*r + cross(q.xyz, r);
}
operator* :: (q: Quat, r: $T) -> Quat #symmetric {
return .{x=q.x*r, y=q.y*r, z=q.z*r, w=q.w*r};
}
operator+ :: (a: Quat, b: Quat) -> Quat {
return .{x=a.x*b.x, y=a.y*b.y, z=a.z*b.z, w=a.w*b.w};
}
operator/ :: (l: Quat, r: $T) -> Quat {
return .{xyz=l.xyz/r, w=l.w/r};
}
operator== :: (l: Quat, r: Quat) -> bool {
return l.x == r.x && l.y == r.y && l.z == r.z && l.w == r.w;
}
conjugate :: (q: Quat) -> Quat #expand {
r: Quat = ---;
r.xyz = -q.xyz;
r.w = q.w;
return r;
}
// Note(Jesse): iff q is a unit vector
inverse_unit :: (q: Quat) -> Quat {
return inline conjugate(q);
}
inverse :: (q: Quat) -> Quat {
return conjugate(q)/length(q);
}
rotation_quat :: (theta: float, axis: Vec3) -> Quat {
q: Quat = ---;
a := normalize(axis);
q.xyz = sin(theta/2.0)*a;
q.w = cos(theta/2.0);
return q;
}
operator- :: (q: Quat) -> Quat {
r: Quat = ---;
r.x = -q.x;
r.y = -q.y;
r.z = -q.z;
r.w = -q.w;
return r;
}
length_squared :: (q: Quat) -> float {
return dot(q, q);
}
length :: (q: Quat) -> float {
return math.sqrt(dot(q, q));
}
dot :: (a: Quat, b: Quat) -> float {
return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
}
slerp :: (a: Quat, _b: Quat, t: float) -> Quat {
b := _b;
cos_a := dot(a, b);
if cos_a < 0.0 {
cos_a = -cos_a;
b = -b;
}
alpha := math.acos(cos_a);
sin_a := math.sin(alpha);
p1 := math.sin((1-t)*alpha)/sin_a;
p2 := math.sin(t*alpha)/sin_a;
return a*p1 + b*p2;
}
cross :: (a: Vec3, b: Vec3) -> Vec3 {
v: Vec3 = ---;
v.x = a.y*b.z - a.z*b.y;
v.y = a.z*b.x - a.x*b.z;
v.z = a.x*b.y - a.y*b.x;
return v;
}
#scope_file
#scope_file;
meta :: #import "jc/meta";
math :: #import "Math"; // @future