476 lines
13 KiB
Plaintext
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";
|