Files
coven/core/math.jai

476 lines
13 KiB
Plaintext

DEGREES_TO_RADIANS : float : 0.017453292;
RADIANS_TO_DEGREES : float : 57.295779;
hfov_to_vfov :: (hfov: float, aspect_ratio: float) -> float {
return 2.0 * atan(tan(hfov * DEGREES_TO_RADIANS * 0.5) / aspect_ratio) * RADIANS_TO_DEGREES;
}
lerp_angle :: (from: float, to: float, weight: float) -> float {
start_angle := fmod_cycling(from, 2*PI);
end_angle := fmod_cycling(to, 2*PI);
angle_distance := fmod_cycling(end_angle - start_angle + PI, 2 * PI) - PI;
interpolated_angle := start_angle + angle_distance * weight;
return fmod_cycling(interpolated_angle, 2*PI);
}
short_angle_dist :: (from: float, to: float) -> float {
max_angle :: PI * 2.0;
difference := fmod_cycling(to - from, max_angle);
return fmod_cycling(2.0 * difference, max_angle) - difference;
}
degrees_to_radians :: (degrees: float) -> float#expand {
constant : float : 0.01745329;
return degrees * constant;
}
radians_to_degrees :: (radians: float) -> float#expand {
constant : float : 57.295779;
return radians * constant;
}
Vector2i :: struct {
x: s32;
y: s32;
}
Vector3i :: struct {
x: int;
y: int;
z: int;
}
AABB :: struct {
min: Vector3 = .{FLOAT32_INFINITY, FLOAT32_INFINITY, FLOAT32_INFINITY};
max: Vector3 = .{-FLOAT32_INFINITY, -FLOAT32_INFINITY, -FLOAT32_INFINITY};
}
apply_min_max :: (min: *Vector3, max: *Vector3, p: Vector3) {
if p.x < min.x min.x = p.x;
if p.y < min.y min.y = p.y;
if p.z < min.z min.z = p.z;
if p.x > max.x max.x = p.x;
if p.y > max.y max.y = p.y;
if p.z > max.z max.z = p.z;
}
point_inside_aabb :: (aabb: AABB, point: Vector3) -> bool {
return point.x >= aabb.min.x && point.x <= aabb.max.x
&& point.y >= aabb.min.y && point.y <= aabb.max.y
&& point.z >= aabb.min.z && point.z <= aabb.max.z;
}
aabb_add :: (aabb: *AABB, point: Vector3) {
aabb.min.x = min(point.x, aabb.min.x);
aabb.min.y = min(point.y, aabb.min.y);
aabb.min.z = min(point.z, aabb.min.z);
aabb.max.x = max(point.x, aabb.max.x);
aabb.max.y = max(point.y, aabb.max.y);
aabb.max.z = max(point.z, aabb.max.z);
}
operator == :: inline (a: Vector2i, b: Vector2i) -> bool {
return a.x == b.x && a.y == b.y;
}
operator + :: inline (a: Vector2i, b: Vector2i) -> Vector2i {
return .{a.x + b.x, a.y + b.y};
}
operator - :: inline (a: Vector2i, b: Vector2i) -> Vector2i {
return .{a.x - b.x, a.y - b.y};
}
operator == :: inline (a: Vector3i, b: Vector3i) -> bool {
return a.x == b.x && a.y == b.y && a.z == b.z;
}
operator + :: inline (a: Vector3i, b: Vector3i) -> Vector3i {
return .{a.x + b.x, a.y + b.y, a.z + b.z};
}
operator - :: inline (a: Vector3i, b: Vector3i) -> Vector3i {
return .{a.x - b.x, a.y - b.y, a.z - b.z};
}
transform_position :: (position: Vector3, matrix: Matrix4) -> Vector3 {
return to_v3(matrix * to_v4(position));
}
to_v4 :: (v3: Vector3) -> Vector4 {
v4 : Vector4;
v4.x = v3.x;
v4.y = v3.y;
v4.z = v3.z;
v4.w = 1.0;
return v4;
}
to_v3 :: (v4: Vector4) -> Vector3 {
v3 : Vector3;
v3.x = v4.x;
v3.y = v4.y;
v3.z = v4.z;
return v3;
}
to_v3 :: (v2: Vector2) -> Vector3 {
v : Vector3;
v.x = v2.x;
v.y = v2.y;
return v;
}
to_v2 :: (v3: Vector3) -> Vector2 {
v : Vector2;
v.x = v3.x;
v.y = v3.y;
return v;
}
round :: (val: float) -> float {
return floor(val + 0.5);
}
move_towards :: (origin: float, target: float, amount: float) -> float {
if origin < target {
return min(origin + amount, target);
} else if origin > target {
return max(origin - amount, target);
} else {
return target;
}
}
move_towards :: (origin: Vector3, target: Vector3, amount: float) -> Vector3 {
result : Vector3;
dir := normalize(target - origin);
result.x = move_towards(origin.x, target.x, abs(dir.x) * amount);
result.y = move_towards(origin.y, target.y, abs(dir.y) * amount);
result.z = move_towards(origin.z, target.z, abs(dir.z) * amount);
return result;
}
move_towards :: (origin: Vector2, target: Vector2, amount: float) -> Vector2 {
result : Vector2;
dir := normalize(target - origin);
result.x = move_towards(origin.x, target.x, abs(dir.x) * amount);
result.y = move_towards(origin.y, target.y, abs(dir.y) * amount);
return result;
}
smooth_damp :: (current: Vector3, target: Vector3, current_velocity: *Vector3, smooth_time: float, max_speed: float, delta_time: float) -> Vector3 {
output_x := 0.0;
output_y := 0.0;
output_z := 0.0;
// Based on Game Programming Gems 4 Chapter 1.10
smooth_time = max(0.0001, smooth_time);
omega := 2.0 / smooth_time;
x := omega * delta_time;
exp := 1.0 / (1.0 + x + 0.48 * x * x + 0.235 * x * x * x);
change_x := current.x - target.x;
change_y := current.y - target.y;
change_z := current.z - target.z;
original_to := target;
// Clamp maximum speed
max_change := max_speed * smooth_time;
max_change_sq := max_change * max_change;
sqrmag := change_x * change_x + change_y * change_y + change_z * change_z;
if sqrmag > max_change_sq {
mag := cast(float)sqrt(sqrmag);
change_x = change_x / mag * max_change;
change_y = change_y / mag * max_change;
change_z = change_z / mag * max_change;
}
final_target := target;
final_target.x = current.x - change_x;
final_target.y = current.y - change_y;
final_target.z = current.z - change_z;
temp_x := (current_velocity.x + omega * change_x) * delta_time;
temp_y := (current_velocity.y + omega * change_y) * delta_time;
temp_z := (current_velocity.z + omega * change_z) * delta_time;
current_velocity.x = (current_velocity.x - omega * temp_x) * exp;
current_velocity.y = (current_velocity.y - omega * temp_y) * exp;
current_velocity.z = (current_velocity.z - omega * temp_z) * exp;
output_x = final_target.x + (change_x + temp_x) * exp;
output_y = final_target.y + (change_y + temp_y) * exp;
output_z = final_target.z + (change_z + temp_z) * exp;
// Prevent overshooting
orig_minus_current_x := original_to.x - current.x;
orig_minus_current_y := original_to.y - current.y;
orig_minus_current_z := original_to.z - current.z;
out_minus_orig_x := output_x - original_to.x;
out_minus_orig_y := output_y - original_to.y;
out_minus_orig_z := output_z - original_to.z;
if orig_minus_current_x * out_minus_orig_x + orig_minus_current_y * out_minus_orig_y + orig_minus_current_z * out_minus_orig_z > 0 {
output_x = original_to.x;
output_y = original_to.y;
output_z = original_to.z;
current_velocity.x = (output_x - original_to.x) / delta_time;
current_velocity.y = (output_y - original_to.y) / delta_time;
current_velocity.z = (output_z - original_to.z) / delta_time;
}
return .{output_x, output_y, output_z};
}
smooth_damp :: (current: float, target: float, current_velocity: *float, smooth_time: float, max_speed: float, delta_time: float) -> float {
smooth_time = max(0.0001, smooth_time);
omega := 2.0 / smooth_time;
x := omega * delta_time;
exp := 1.0 / (1.0 + x + 0.48 * x * x + 0.235 * x * x * x);
change := current - target;
original_to := target;
// Clamp maximum speed
max_change := max_speed * smooth_time;
change = clamp(change, -max_change, max_change);
target = current - change;
temp := (current_velocity.* + omega * change) * delta_time;
current_velocity.* = (current_velocity.* - omega * temp) * exp;
output := target + (change + temp) * exp;
// Prevent overshooting
if (original_to - current > 0.0) == (output > original_to) {
output = original_to;
current_velocity.* = (output - original_to) / delta_time;
}
return output;
}
random_in_unit_circle :: () -> Vector2 {
result : Vector2;
angle := random_get_within_range(0.0, PI * 2.0);
radius := random_get_within_range(0.0, 1.0);
result.x = radius * cos(angle);
result.y = radius * sin(angle);
return result;
}
random_in_unit_sphere :: () -> Vector3 {
result : Vector3;
result.x = random_get_within_range(-1.0, 1.0);
result.y = random_get_within_range(-1.0, 1.0);
result.z = random_get_within_range(-1.0, 1.0);
return normalize(result);
}
look_at_lh :: (position: Vector3, target: Vector3, up: Vector3) -> Matrix4 {
z_axis := normalize(target - position);
x_axis := normalize(cross(up, z_axis));
y_axis := cross(z_axis, x_axis);
m : Matrix4;
m._11 = x_axis.x;
m._12 = x_axis.y;
m._13 = x_axis.z;
m._14 = -dot(x_axis, position);
m._21 = y_axis.x;
m._22 = y_axis.y;
m._23 = y_axis.z;
m._24 = -dot(y_axis, position);
m._31 = z_axis.x;
m._32 = z_axis.y;
m._33 = z_axis.z;
m._34 = -dot(z_axis, position);
m._41 = 0.0;
m._42 = 0.0;
m._43 = 0.0;
m._44 = 1.0;
return m;
}
project :: (v1: Vector3, v2: Vector3) -> Vector3 {
return (v2*v1)/(v2*v2)*v2;
}
reject :: (v1: Vector3, v2: Vector3) -> Vector3 {
return v1 - project(v1, v2);
}
horizontal_distance :: inline (v1: Vector3, v2: Vector3) -> float {
return distance(Vector2.{v1.x,v1.z}, Vector2.{v2.x,v2.z});
}
horizontal_direction :: inline (from: Vector3, to: Vector3) -> Vector3 {
adjusted_from := from;
adjusted_from.y = to.y;
return normalize(to - adjusted_from);
}
closest_point_on_line_segment :: (a: Vector3, b: Vector3, point: Vector3, ignore_y: bool = false) -> Vector3, float {
actual_a := a;
actual_b := b;
if ignore_y {
actual_a.y = point.y;
actual_b.y = point.y;
}
ab := actual_b - actual_a;
ap := point - actual_a;
proj := dot(ap, ab);
ab_len_sq := length_squared(ab);
d := proj / ab_len_sq;
cp : Vector3 = ---;
if d <= 0.0 {
cp = actual_a;
} else if d >= 1.0 {
cp = actual_b;
} else {
cp = actual_a + ab * d;
}
return cp, distance(point, cp);
}
reflect :: (incident: Vector3, normal: Vector3) -> Vector3 {
dot_product := dot(incident, normal);
reflected : Vector3 = ---;
reflected.x = incident.x - 2.0 * dot_product * normal.x;
reflected.y = incident.y - 2.0 * dot_product * normal.y;
reflected.z = incident.z - 2.0 * dot_product * normal.z;
return reflected;
}
get_rotated_direction :: (direction: Vector3, by_pitch: float, by_yaw: float, by_roll: float) -> Vector3 {
pitch := by_pitch * DEGREES_TO_RADIANS;
yaw := by_yaw * DEGREES_TO_RADIANS;
roll := by_roll * DEGREES_TO_RADIANS;
pitch_mat := Matrix4.{
1, 0, 0, 0,
0, cos(pitch), sin(pitch), 0,
0, -sin(pitch), cos(pitch), 0,
0, 0, 0, 1};
yaw_mat := Matrix4.{
cos(yaw), 0, -sin(yaw), 0,
0, 1, 0, 0,
sin(yaw), 0, cos(yaw), 0,
0, 0, 0, 1};
roll_mat := Matrix4.{
cos(roll), sin(roll), 0, 0,
-sin(roll), cos(roll), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1};
matrix := yaw_mat * pitch_mat * roll_mat;
return normalize(to_v3(matrix * Vector4.{direction.x, direction.y, direction.z, 0.0}));
}
quat_to_pitch_yaw_roll :: (using q: Quaternion) -> pitch: float, yaw: float, roll: float {
roll := atan2(2*y*w - 2*x*z, 1 - 2*y*y - 2*z*z);
pitch := atan2(2*x*w - 2*y*z, 1 - 2*x*x - 2*z*z);
yaw := asin(2*x*y + 2*z*w);
return pitch, yaw, roll;
}
ease_in :: (x : float, exp : int = 2) -> float {
return pow(x, xx exp);
}
ease_out :: (x : float, exp : int = 2) -> float {
return 1.0 - pow(1 - x, xx exp);
}
ease_in_sine :: (x: float) -> float {
return 1.0 - cos((x * PI) * 0.5);
}
ease_out_sine :: (x: float) -> float {
return 1.0 - sin((x * PI) * 0.5);
}
ease_in_out_sine :: (x: float) -> float {
return -(cos(PI * x) - 1.0) * 0.5;
}
// Color
Color :: #type,isa Vector4;
Colored_Vert :: struct {
position: Vector2;
color : Color;
}
linear_to_srgb :: (c: float) -> float {
if (c <= 0.0031308)
return 12.92 * c;
else
return 1.055 * pow(c, 1.0 / 2.4) - 0.055;
}
linear_to_srgb :: (c: Vector3) -> Vector3 {
return Vector3.{
linear_to_srgb(c.x),
linear_to_srgb(c.y),
linear_to_srgb(c.z)
};
}
linear_to_srgb :: (c: $T) -> T {
srgb_color := c;
srgb_color.x = linear_to_srgb(c.x);
srgb_color.y = linear_to_srgb(c.y);
srgb_color.z = linear_to_srgb(c.z);
return srgb_color;
}
srgb_to_linear :: (c: float) -> float {
if c <= 0.04045
return c / 12.92;
else
return pow((c + 0.055) / 1.055, 2.4);
}
srgb_to_linear :: (c: Vector3) -> Vector3 {
return float3(
srgbToLinear(c.r),
srgbToLinear(c.g),
srgbToLinear(c.b)
);
}
srgb_to_linear :: (c: $T) -> T {
linear_color := c;
linear_color.x = srgb_to_linear(c.x);
linear_color.y = srgb_to_linear(c.y);
linear_color.z = srgb_to_linear(c.z);
return linear_color;
}
#import "PCG";
#import "Math";
#load "frustum.jai";