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