185 lines
5.1 KiB
Zig
185 lines
5.1 KiB
Zig
const std = @import("std");
|
|
|
|
/// Smooth lerp
|
|
pub inline fn lerpTime(a: anytype, b: anytype, t: f32, comptime f: f32) @TypeOf(a, b) {
|
|
@setFloatMode(.optimized);
|
|
|
|
return lerpTimeLn(a, b, t, @log(f));
|
|
}
|
|
|
|
pub fn lerpTimeLn(a: anytype, b: anytype, t: f32, lnf: f32) @TypeOf(a, b) {
|
|
@setFloatMode(.optimized);
|
|
|
|
return lerp(b, a, @exp(lnf * t));
|
|
}
|
|
|
|
pub fn lerp(a: anytype, b: anytype, f: f32) @TypeOf(a, b) {
|
|
@setFloatMode(.optimized);
|
|
|
|
const a_factor = 1.0 - f;
|
|
const b_factor = f;
|
|
|
|
switch (@typeInfo(@TypeOf(a, b))) {
|
|
.float => return a_factor * a + b_factor * b,
|
|
.vector => return @as(@TypeOf(a), @splat(a_factor)) * a + @as(@TypeOf(b), @splat(b_factor)) * b,
|
|
else => @compileError("Can only interpolate between vector or float values"),
|
|
}
|
|
}
|
|
|
|
/// Spherical lerp
|
|
pub inline fn slerpTime(a: anytype, b: anytype, t: f32, comptime f: f32) @TypeOf(a, b) {
|
|
@setFloatMode(.optimized);
|
|
|
|
return slerpTimeLn(a, b, t, @log(f));
|
|
}
|
|
|
|
pub fn slerpTimeLn(a: anytype, b: anytype, t: f32, lnf: f32) @TypeOf(a, b) {
|
|
@setFloatMode(.optimized);
|
|
|
|
return slerp(b, a, @exp(lnf * t));
|
|
}
|
|
|
|
pub fn slerp(a: anytype, b: anytype, f: f32) @TypeOf(a, b) {
|
|
@setFloatMode(.optimized);
|
|
|
|
const cos = @reduce(.Add, a * b);
|
|
if (cos > 0.999) {
|
|
return lerp(a, b, f);
|
|
}
|
|
|
|
const angle = std.math.acos(cos);
|
|
|
|
const a_angle_factor = 1 - f;
|
|
const b_angle_factor = f;
|
|
|
|
const rev_angle_sin = 1.0 / std.math.sin(angle);
|
|
const a_sin = std.math.sin(a_angle_factor * angle);
|
|
const b_sin = std.math.sin(b_angle_factor * angle);
|
|
|
|
const a_factor = a_sin * rev_angle_sin;
|
|
const b_factor = b_sin * rev_angle_sin;
|
|
|
|
return @as(@TypeOf(a), @splat(a_factor)) * a + @as(@TypeOf(b), @splat(b_factor)) * b;
|
|
}
|
|
|
|
// Step interpolation
|
|
pub fn step(a: f32, b: f32, l: f32) f32 {
|
|
@setFloatMode(.optimized);
|
|
|
|
if (b > a) {
|
|
return @min(a + l, b);
|
|
} else {
|
|
return @max(b, a - l);
|
|
}
|
|
}
|
|
|
|
pub fn stepVector(a: anytype, b: anytype, l: f32) @TypeOf(a, b) {
|
|
@setFloatMode(.optimized);
|
|
|
|
return a + limitLength(b - a, l);
|
|
}
|
|
|
|
pub fn limitLength(vector: anytype, max_length: f32) @TypeOf(vector) {
|
|
@setFloatMode(.optimized);
|
|
|
|
const length_square = @reduce(.Add, vector * vector);
|
|
if (length_square > max_length * max_length) {
|
|
return vector * @as(@TypeOf(vector), @splat(max_length / @sqrt(length_square)));
|
|
}
|
|
return vector;
|
|
}
|
|
|
|
pub fn length(vector: anytype) f32 {
|
|
@setFloatMode(.optimized);
|
|
|
|
return @sqrt(dot(vector, vector));
|
|
}
|
|
|
|
pub fn dot(a: anytype, b: anytype) f32 {
|
|
@setFloatMode(.optimized);
|
|
|
|
return @reduce(.Add, a * b);
|
|
}
|
|
|
|
pub fn lengthInt(vector: anytype) f32 {
|
|
@setFloatMode(.optimized);
|
|
|
|
return @sqrt(@as(f32, @floatFromInt(dotInt(vector, vector))));
|
|
}
|
|
|
|
pub fn dotInt(a: anytype, b: anytype) (@typeInfo(@TypeOf(a)).vector.child) {
|
|
@setFloatMode(.optimized);
|
|
|
|
return @reduce(.Add, a * b);
|
|
}
|
|
|
|
pub fn Sway(comptime T: type) type {
|
|
const STABILIZATION = -1;
|
|
return packed struct {
|
|
value: T = 0,
|
|
velocity: T = 0,
|
|
frequency: T,
|
|
amplitude: T,
|
|
|
|
const Self = @This();
|
|
pub fn update(self: *Self, delta: f32) void {
|
|
@setFloatMode(.optimized);
|
|
|
|
const dist = delta * -2 * std.math.pi * self.frequency;
|
|
const sin = std.math.sin(dist);
|
|
const cos = std.math.cos(dist);
|
|
var len = length(@Vector(2, T){ self.value, self.velocity });
|
|
if (len < 0.001) {
|
|
self.value = 0.001;
|
|
self.velocity = 0;
|
|
len = 0.001;
|
|
}
|
|
const new_value = self.value * cos - self.velocity * sin;
|
|
const new_velocity = self.value * sin + self.velocity * cos;
|
|
const target_len = lerpTimeLn(len, self.amplitude, delta, STABILIZATION);
|
|
const mult = target_len / len;
|
|
self.value = new_value * mult;
|
|
self.velocity = new_velocity * mult;
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn raycast(
|
|
origin: @Vector(3, f32),
|
|
target: @Vector(3, f32),
|
|
plane: @Vector(4, f32),
|
|
) @Vector(3, f32) {
|
|
@setFloatMode(.optimized);
|
|
|
|
const offset = target - origin;
|
|
const plane_dir = @Vector(3, f32){ plane[0], plane[1], plane[2] };
|
|
const dist = plane[3];
|
|
const dist_mod = dist / dot(plane_dir, plane_dir);
|
|
const num = dot(plane_dir, plane_dir * @as(@Vector(3, f32), @splat(dist_mod)) - origin);
|
|
var den = dot(offset, plane_dir);
|
|
if (@abs(den) < 0.0001) {
|
|
den = 0.0001;
|
|
}
|
|
|
|
return origin + offset * @as(@Vector(3, f32), @splat(num / den));
|
|
}
|
|
|
|
pub fn limit(vector: anytype, value: f32) @TypeOf(vector) {
|
|
const max = @reduce(.Max, vector);
|
|
if (max > value)
|
|
return vector * @as(@TypeOf(vector), @splat(value / max))
|
|
else
|
|
return vector;
|
|
}
|
|
|
|
pub fn norm(vector: anytype) @TypeOf(vector) {
|
|
const len = length(vector);
|
|
if (len < 1.01 and len > 0.99) return vector;
|
|
if (len < 1e-10) {
|
|
var output = @as(@TypeOf(vector), @splat(0));
|
|
output[@typeInfo(@TypeOf(vector)).vector.len - 1] = 1;
|
|
return output;
|
|
}
|
|
return vector * @as(@TypeOf(vector), @splat(1.0 / len));
|
|
}
|