HelloWorld
This commit is contained in:
commit
10645c98d1
8 changed files with 1023 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.zig-cache/
|
||||
zig-pkg/
|
||||
25
README.md
Normal file
25
README.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
## NYXMath
|
||||
Low cortisol comptime generic math library for game dev / game engines. Yes.
|
||||
|
||||
Coord System: Vulkan
|
||||
|
||||
# Includes
|
||||
```zig
|
||||
// --- GENERICS ---
|
||||
pub const Vec = @import("vec.zig").Vec;
|
||||
pub const Mat = @import("mat.zig").Mat;
|
||||
pub const Color = @import("color.zig").Color;
|
||||
|
||||
// --- VEC2 ---
|
||||
pub const Vec2 = Vec(i32, 2);
|
||||
pub const Vec2f = Vec(f32, 2);
|
||||
|
||||
// --- VEC3 ---
|
||||
pub const Vec3 = Vec(i32, 3);
|
||||
pub const Vec3f = Vec(f32, 3);
|
||||
|
||||
// --- MAT ---
|
||||
pub const Mat2x2 = Mat(f32, 2, 2);
|
||||
pub const Mat3x3 = Mat(f32, 3, 3);
|
||||
pub const Mat4x4 = Mat(f32, 4, 4);
|
||||
```
|
||||
24
build.zig
Normal file
24
build.zig
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
// --- TARGET ---
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
// --- MOD ---
|
||||
const mod = b.addModule("NYXMath", .{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const mod_tests = b.addTest(.{
|
||||
.root_module = mod,
|
||||
});
|
||||
|
||||
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&run_mod_tests.step);
|
||||
}
|
||||
17
build.zig.zon
Normal file
17
build.zig.zon
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.{
|
||||
.name = .NYXMath,
|
||||
|
||||
.version = "0.0.0",
|
||||
|
||||
.fingerprint = 0x82eb78004f923e04,
|
||||
|
||||
.minimum_zig_version = "0.16.0",
|
||||
|
||||
.dependencies = .{},
|
||||
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
||||
279
src/color.zig
Normal file
279
src/color.zig
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const std = @import("std");
|
||||
const Vec = @import("vec.zig").Vec;
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub const Color = extern struct {
|
||||
const Self = @This();
|
||||
|
||||
//
|
||||
// FIELDS
|
||||
//
|
||||
|
||||
r: f32 = 0,
|
||||
g: f32 = 0,
|
||||
b: f32 = 0,
|
||||
a: f32 = 1,
|
||||
|
||||
//
|
||||
// CONSTRUCTORS
|
||||
//
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn rgb(r: f32, g: f32, b: f32) Self {
|
||||
return .{ .r = r, .g = g, .b = b, .a = 1 };
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn rgba(r: f32, g: f32, b: f32, a: f32) Self {
|
||||
return .{ .r = r, .g = g, .b = b, .a = a };
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn hex(comptime value: u32) Self {
|
||||
const r = @as(f32, @as(u8, @truncate(value >> 16))) / 255.0;
|
||||
const g = @as(f32, @as(u8, @truncate(value >> 8))) / 255.0;
|
||||
const b = @as(f32, @as(u8, @truncate(value >> 0))) / 255.0;
|
||||
const a = if (value > 0xFFFFFF)
|
||||
@as(f32, @as(u8, @truncate(value >> 24))) / 255.0
|
||||
else
|
||||
1.0;
|
||||
return .{ .r = r, .g = g, .b = b, .a = a };
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn fromVec4(v: Vec(f32, 4)) Self {
|
||||
return .{ .r = v.value[0], .g = v.value[1], .b = v.value[2], .a = v.value[3] };
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn toVec4(self: Self) Vec(f32, 4) {
|
||||
return Vec(f32, 4).new(.{ self.r, self.g, self.b, self.a });
|
||||
}
|
||||
|
||||
//
|
||||
// ARITHMETIC
|
||||
//
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn add(self: Self, other: Self) Self {
|
||||
return .{
|
||||
.r = self.r + other.r,
|
||||
.g = self.g + other.g,
|
||||
.b = self.b + other.b,
|
||||
.a = self.a + other.a,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn sub(self: Self, other: Self) Self {
|
||||
return .{
|
||||
.r = self.r - other.r,
|
||||
.g = self.g - other.g,
|
||||
.b = self.b - other.b,
|
||||
.a = self.a - other.a,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn mul(self: Self, other: Self) Self {
|
||||
return .{
|
||||
.r = self.r * other.r,
|
||||
.g = self.g * other.g,
|
||||
.b = self.b * other.b,
|
||||
.a = self.a * other.a,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn div(self: Self, other: Self) Self {
|
||||
return .{
|
||||
.r = self.r / other.r,
|
||||
.g = self.g / other.g,
|
||||
.b = self.b / other.b,
|
||||
.a = self.a / other.a,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn scale(self: Self, scalar: f32) Self {
|
||||
return .{
|
||||
.r = self.r * scalar,
|
||||
.g = self.g * scalar,
|
||||
.b = self.b * scalar,
|
||||
.a = self.a * scalar,
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// COLOR OPERATIONS
|
||||
//
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn lerp(self: Self, other: Self, t: f32) Self {
|
||||
return .{
|
||||
.r = self.r + (other.r - self.r) * t,
|
||||
.g = self.g + (other.g - self.g) * t,
|
||||
.b = self.b + (other.b - self.b) * t,
|
||||
.a = self.a + (other.a - self.a) * t,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn blend(self: Self, other: Self) Self {
|
||||
const out_a = other.a + self.a * (1.0 - other.a);
|
||||
if (out_a == 0) return .{ .r = 0, .g = 0, .b = 0, .a = 0 };
|
||||
return .{
|
||||
.r = (other.r * other.a + self.r * self.a * (1.0 - other.a)) / out_a,
|
||||
.g = (other.g * other.a + self.g * self.a * (1.0 - other.a)) / out_a,
|
||||
.b = (other.b * other.a + self.b * self.a * (1.0 - other.a)) / out_a,
|
||||
.a = out_a,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn premultiply(self: Self) Self {
|
||||
return .{
|
||||
.r = self.r * self.a,
|
||||
.g = self.g * self.a,
|
||||
.b = self.b * self.a,
|
||||
.a = self.a,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn luminance(self: Self) f32 {
|
||||
return self.r * 0.2126 + self.g * 0.7152 + self.b * 0.0722;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn toLinear(self: Self) Self {
|
||||
const srgb_to_linear = struct {
|
||||
inline fn map(c: f32) f32 {
|
||||
if (c <= 0.04045) return c / 12.92;
|
||||
return std.math.pow(f32, (c + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
};
|
||||
return .{
|
||||
.r = srgb_to_linear.map(self.r),
|
||||
.g = srgb_to_linear.map(self.g),
|
||||
.b = srgb_to_linear.map(self.b),
|
||||
.a = self.a,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn toSRGB(self: Self) Self {
|
||||
const linear_to_srgb = struct {
|
||||
inline fn map(c: f32) f32 {
|
||||
if (c <= 0.0031308) return c * 12.92;
|
||||
return 1.055 * std.math.pow(f32, c, 1.0 / 2.4) - 0.055;
|
||||
}
|
||||
};
|
||||
return .{
|
||||
.r = linear_to_srgb.map(self.r),
|
||||
.g = linear_to_srgb.map(self.g),
|
||||
.b = linear_to_srgb.map(self.b),
|
||||
.a = self.a,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn toHSV(self: Self) struct { h: f32, s: f32, v: f32 } {
|
||||
const max = @max(self.r, @max(self.g, self.b));
|
||||
const min = @min(self.r, @min(self.g, self.b));
|
||||
const delta = max - min;
|
||||
|
||||
const v = max;
|
||||
const s = if (max == 0) 0 else delta / max;
|
||||
|
||||
var h: f32 = 0;
|
||||
if (delta != 0) {
|
||||
if (max == self.r) {
|
||||
h = 60.0 * @mod((self.g - self.b) / delta, 6.0);
|
||||
} else if (max == self.g) {
|
||||
h = 60.0 * ((self.b - self.r) / delta + 2.0);
|
||||
} else {
|
||||
h = 60.0 * ((self.r - self.g) / delta + 4.0);
|
||||
}
|
||||
}
|
||||
return .{ .h = h, .s = s, .v = v };
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn fromHSV(h: f32, s: f32, v: f32) Self {
|
||||
const c = v * s;
|
||||
const hp = h / 60.0;
|
||||
const x = c * (1.0 - @abs(@mod(hp, 2.0) - 1.0));
|
||||
const m = v - c;
|
||||
|
||||
var r: f32 = 0;
|
||||
var g: f32 = 0;
|
||||
var b: f32 = 0;
|
||||
|
||||
if (hp < 1) {
|
||||
r = c;
|
||||
g = x;
|
||||
} else if (hp < 2) {
|
||||
r = x;
|
||||
g = c;
|
||||
} else if (hp < 3) {
|
||||
g = c;
|
||||
b = x;
|
||||
} else if (hp < 4) {
|
||||
g = x;
|
||||
b = c;
|
||||
} else if (hp < 5) {
|
||||
r = x;
|
||||
b = c;
|
||||
} else {
|
||||
r = c;
|
||||
b = x;
|
||||
}
|
||||
|
||||
return .{ .r = r + m, .g = g + m, .b = b + m, .a = 1 };
|
||||
}
|
||||
|
||||
//
|
||||
// CONSTANTS
|
||||
//
|
||||
|
||||
pub const white = Self{ .r = 1, .g = 1, .b = 1, .a = 1 };
|
||||
pub const black = Self{ .r = 0, .g = 0, .b = 0, .a = 1 };
|
||||
pub const transparent = Self{ .r = 0, .g = 0, .b = 0, .a = 0 };
|
||||
|
||||
pub const red = Self{ .r = 1, .g = 0, .b = 0, .a = 1 };
|
||||
pub const green = Self{ .r = 0, .g = 1, .b = 0, .a = 1 };
|
||||
pub const blue = Self{ .r = 0, .g = 0, .b = 1, .a = 1 };
|
||||
|
||||
pub const yellow = Self{ .r = 1, .g = 1, .b = 0, .a = 1 };
|
||||
pub const cyan = Self{ .r = 0, .g = 1, .b = 1, .a = 1 };
|
||||
pub const magenta = Self{ .r = 1, .g = 0, .b = 1, .a = 1 };
|
||||
|
||||
pub const orange = Self{ .r = 1, .g = 0.55, .b = 0, .a = 1 };
|
||||
pub const purple = Self{ .r = 0.5, .g = 0, .b = 0.5, .a = 1 };
|
||||
pub const pink = Self{ .r = 1, .g = 0.75, .b = 0.8, .a = 1 };
|
||||
pub const brown = Self{ .r = 0.6, .g = 0.3, .b = 0.1, .a = 1 };
|
||||
pub const gray = Self{ .r = 0.5, .g = 0.5, .b = 0.5, .a = 1 };
|
||||
};
|
||||
370
src/mat.zig
Normal file
370
src/mat.zig
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const std = @import("std");
|
||||
const Vec = @import("vec.zig").Vec;
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub fn Mat(
|
||||
comptime T: type,
|
||||
comptime Row: usize,
|
||||
comptime Col: usize,
|
||||
) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
//
|
||||
// FIELDS
|
||||
//
|
||||
|
||||
value: [Row][Col]T,
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn zero() Self {
|
||||
var result: Self = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
result.value[r][c] = @as(T, 0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn identity() Self {
|
||||
comptime if (Row != Col) @compileError("identity only defined for square matrices");
|
||||
|
||||
var result: Self = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
result.value[r][c] = if (r == c) @as(T, 1) else @as(T, 0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn fromRows(rows: [Row][Col]T) Self {
|
||||
return .{ .value = rows };
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn fromCols(cols: [Col][Row]T) Self {
|
||||
var result: Self = undefined;
|
||||
for (0..Col) |c| {
|
||||
for (0..Row) |r| {
|
||||
result.value[r][c] = cols[c][r];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn row(self: Self, index: usize) Vec(T, Col) {
|
||||
return Vec(T, Col).new(self.value[index]);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn col(self: Self, index: usize) Vec(T, Row) {
|
||||
var data: [Row]T = undefined;
|
||||
for (0..Row) |r| {
|
||||
data[r] = self.value[r][index];
|
||||
}
|
||||
return Vec(T, Row).new(data);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn add(self: Self, other: Self) Self {
|
||||
var result: Self = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
result.value[r][c] = self.value[r][c] + other.value[r][c];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn sub(self: Self, other: Self) Self {
|
||||
var result: Self = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
result.value[r][c] = self.value[r][c] - other.value[r][c];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn mul(self: Self, other: Self) Self {
|
||||
comptime if (Row != Col) @compileError("direct matrix multiplication requires square matrices; use mulMat for non-square");
|
||||
|
||||
var result: Self = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
var sum: T = @as(T, 0);
|
||||
for (0..Col) |k| {
|
||||
sum += self.value[r][k] * other.value[k][c];
|
||||
}
|
||||
result.value[r][c] = sum;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn mulVec(self: Self, v: Vec(T, Col)) Vec(T, Row) {
|
||||
var result: [Row]T = undefined;
|
||||
for (0..Row) |r| {
|
||||
var sum: T = @as(T, 0);
|
||||
for (0..Col) |c| {
|
||||
sum += self.value[r][c] * v.value[c];
|
||||
}
|
||||
result[r] = sum;
|
||||
}
|
||||
return Vec(T, Row).new(result);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn mulScalar(self: Self, scalar: T) Self {
|
||||
var result: Self = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
result.value[r][c] = self.value[r][c] * scalar;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn divScalar(self: Self, scalar: T) Self {
|
||||
var result: Self = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
result.value[r][c] = self.value[r][c] / scalar;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn transpose(self: Self) Mat(T, Col, Row) {
|
||||
var result: Mat(T, Col, Row) = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
result.value[c][r] = self.value[r][c];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn determinant(self: Self) T {
|
||||
comptime if (Row != Col or Row < 1) @compileError("determinant only defined for non-empty square matrices");
|
||||
|
||||
if (Row == 1) return self.value[0][0];
|
||||
|
||||
var det: T = @as(T, 0);
|
||||
for (0..Col) |c| {
|
||||
det += self.value[0][c] * self.cofactor(0, c);
|
||||
}
|
||||
return det;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn inverse(self: Self) Self {
|
||||
comptime if (Row != Col) @compileError("inverse only defined for square matrices");
|
||||
|
||||
const det = self.determinant();
|
||||
var result: Self = undefined;
|
||||
for (0..Row) |r| {
|
||||
for (0..Col) |c| {
|
||||
result.value[r][c] = self.cofactor(c, r) / det;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// PRIVATE
|
||||
//
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
inline fn minor(self: Self, r_idx: usize, c_idx: usize) Mat(T, Row - 1, Col - 1) {
|
||||
comptime if (Row < 2 or Col < 2) @compileError("minor requires at least 2x2");
|
||||
|
||||
var result: Mat(T, Row - 1, Col - 1) = undefined;
|
||||
var ri: usize = 0;
|
||||
for (0..Row) |r| {
|
||||
if (r == r_idx) continue;
|
||||
var ci: usize = 0;
|
||||
for (0..Col) |c| {
|
||||
if (c == c_idx) continue;
|
||||
result.value[ri][ci] = self.value[r][c];
|
||||
ci += 1;
|
||||
}
|
||||
ri += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
inline fn cofactor(self: Self, r_idx: usize, c_idx: usize) T {
|
||||
const sign: T = if ((r_idx + c_idx) % 2 == 0) @as(T, 1) else -@as(T, 1);
|
||||
return sign * self.minor(r_idx, c_idx).determinant();
|
||||
}
|
||||
|
||||
//
|
||||
// 4X4 TRANSFORMS
|
||||
//
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn translate(x: T, y: T, z: T) Self {
|
||||
comptime if (Row != 4 or Col != 4) @compileError("translate only defined for Mat4x4");
|
||||
|
||||
var result = identity();
|
||||
result.value[0][3] = x;
|
||||
result.value[1][3] = y;
|
||||
result.value[2][3] = z;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn rotateX(angle: T) Self {
|
||||
comptime if (Row != 4 or Col != 4) @compileError("rotateX only defined for Mat4x4");
|
||||
|
||||
const c = @cos(angle);
|
||||
const s = @sin(angle);
|
||||
var result = identity();
|
||||
result.value[1][1] = c;
|
||||
result.value[1][2] = -s;
|
||||
result.value[2][1] = s;
|
||||
result.value[2][2] = c;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn rotateY(angle: T) Self {
|
||||
comptime if (Row != 4 or Col != 4) @compileError("rotateY only defined for Mat4x4");
|
||||
|
||||
const c = @cos(angle);
|
||||
const s = @sin(angle);
|
||||
var result = identity();
|
||||
result.value[0][0] = c;
|
||||
result.value[0][2] = s;
|
||||
result.value[2][0] = -s;
|
||||
result.value[2][2] = c;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn rotateZ(angle: T) Self {
|
||||
comptime if (Row != 4 or Col != 4) @compileError("rotateZ only defined for Mat4x4");
|
||||
|
||||
const c = @cos(angle);
|
||||
const s = @sin(angle);
|
||||
var result = identity();
|
||||
result.value[0][0] = c;
|
||||
result.value[0][1] = -s;
|
||||
result.value[1][0] = s;
|
||||
result.value[1][1] = c;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn scale(x: T, y: T, z: T) Self {
|
||||
comptime if (Row != 4 or Col != 4) @compileError("scale only defined for Mat4x4");
|
||||
|
||||
var result = identity();
|
||||
result.value[0][0] = x;
|
||||
result.value[1][1] = y;
|
||||
result.value[2][2] = z;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn lookAt(eye: Vec(T, 3), target: Vec(T, 3), up: Vec(T, 3)) Self {
|
||||
comptime if (Row != 4 or Col != 4) @compileError("lookAt only defined for Mat4x4");
|
||||
|
||||
const f = target.sub(eye).normalize();
|
||||
const s = f.cross(up).normalize();
|
||||
const u = s.cross(f);
|
||||
|
||||
var result = identity();
|
||||
result.value[0][0] = s.value[0];
|
||||
result.value[0][1] = s.value[1];
|
||||
result.value[0][2] = s.value[2];
|
||||
result.value[1][0] = u.value[0];
|
||||
result.value[1][1] = u.value[1];
|
||||
result.value[1][2] = u.value[2];
|
||||
result.value[2][0] = -f.value[0];
|
||||
result.value[2][1] = -f.value[1];
|
||||
result.value[2][2] = -f.value[2];
|
||||
|
||||
result.value[0][3] = -s.dot(eye);
|
||||
result.value[1][3] = -u.dot(eye);
|
||||
result.value[2][3] = f.dot(eye);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn perspective(fov: T, aspect: T, near: T, far: T) Self {
|
||||
comptime if (Row != 4 or Col != 4) @compileError("perspective only defined for Mat4x4");
|
||||
|
||||
const f = @as(T, 1) / @tan(fov / @as(T, 2));
|
||||
const range_inv = @as(T, 1) / (near - far);
|
||||
|
||||
var result = zero();
|
||||
result.value[0][0] = f / aspect;
|
||||
result.value[1][1] = -f;
|
||||
result.value[2][2] = far * range_inv;
|
||||
result.value[2][3] = near * far * range_inv;
|
||||
result.value[3][2] = -@as(T, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn orthographic(left: T, right: T, bottom: T, top: T, near: T, far: T) Self {
|
||||
comptime if (Row != 4 or Col != 4) @compileError("orthographic only defined for Mat4x4");
|
||||
|
||||
const rml = @as(T, 1) / (right - left);
|
||||
const tmb = @as(T, 1) / (top - bottom);
|
||||
const fmn = @as(T, 1) / (far - near);
|
||||
|
||||
var result = identity();
|
||||
result.value[0][0] = @as(T, 2) * rml;
|
||||
result.value[1][1] = -@as(T, 2) * tmb;
|
||||
result.value[2][2] = -fmn;
|
||||
result.value[0][3] = -(right + left) * rml;
|
||||
result.value[1][3] = (top + bottom) * tmb;
|
||||
result.value[2][3] = -near * fmn;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
169
src/root.zig
Normal file
169
src/root.zig
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
//
|
||||
// PRIVATE
|
||||
//
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
//
|
||||
// PUBLIC
|
||||
//
|
||||
|
||||
// --- GENERICS ---
|
||||
pub const Vec = @import("vec.zig").Vec;
|
||||
pub const Mat = @import("mat.zig").Mat;
|
||||
pub const Color = @import("color.zig").Color;
|
||||
|
||||
// --- VEC2 ---
|
||||
pub const Vec2 = Vec(i32, 2);
|
||||
pub const Vec2f = Vec(f32, 2);
|
||||
|
||||
// --- VEC3 ---
|
||||
pub const Vec3 = Vec(i32, 3);
|
||||
pub const Vec3f = Vec(f32, 3);
|
||||
|
||||
// --- MAT ---
|
||||
pub const Mat2x2 = Mat(f32, 2, 2);
|
||||
pub const Mat3x3 = Mat(f32, 3, 3);
|
||||
pub const Mat4x4 = Mat(f32, 4, 4);
|
||||
|
||||
//
|
||||
// TESTS
|
||||
//
|
||||
|
||||
test "init" {
|
||||
std.debug.print("\x1b[34m--- init ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const vec2: Vec2f = .as(12);
|
||||
const vec3: Vec3f = .new(.{ 1, 2, 3 });
|
||||
|
||||
std.debug.print("Vec2: {}\n", .{vec2.value});
|
||||
std.debug.print("Vec3: {}\n", .{vec3.value});
|
||||
}
|
||||
|
||||
test "math" {
|
||||
std.debug.print("\x1b[35m--- math ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const a: Vec3f = .new(.{ 1, 2, 3 });
|
||||
const b: Vec3f = .new(.{ 2, 3, 4 });
|
||||
|
||||
std.debug.print("a = {}\n", .{a.value});
|
||||
std.debug.print("b = {}\n\n", .{b.value});
|
||||
|
||||
std.debug.print("a + b = {}\n", .{a.add(b).value});
|
||||
std.debug.print("a - b = {}\n", .{a.sub(b).value});
|
||||
std.debug.print("a * b = {}\n", .{a.mul(b).value});
|
||||
std.debug.print("a / b = {:.1}\n", .{a.div(b).value});
|
||||
}
|
||||
|
||||
test "mat init" {
|
||||
std.debug.print("\x1b[34m--- mat init ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const identity: Mat4x4 = .identity();
|
||||
std.debug.print("identity[0][0] = {d}\n", .{identity.value[0][0]});
|
||||
std.debug.print("identity[3][3] = {d}\n", .{identity.value[3][3]});
|
||||
}
|
||||
|
||||
test "mat mul" {
|
||||
std.debug.print("\x1b[35m--- mat mul ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const t: Mat4x4 = .translate(1, 2, 3);
|
||||
const rz: Mat4x4 = .rotateZ(@as(f32, std.math.pi) / 4.0);
|
||||
const m = t.mul(rz);
|
||||
|
||||
std.debug.print("translate * rotateZ:\n", .{});
|
||||
for (m.value, 0..) |row, i| {
|
||||
std.debug.print("row {d}: {d:.4} {d:.4} {d:.4} {d:.4}\n", .{ i, row[0], row[1], row[2], row[3] });
|
||||
}
|
||||
}
|
||||
|
||||
test "mat det" {
|
||||
std.debug.print("\x1b[34m--- mat det ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const m: Mat3x3 = .fromRows(.{
|
||||
.{ 1, 2, 3 },
|
||||
.{ 4, 5, 6 },
|
||||
.{ 7, 8, 10 },
|
||||
});
|
||||
std.debug.print("det = {d:.2}\n", .{m.determinant()});
|
||||
}
|
||||
|
||||
test "mat inverse" {
|
||||
std.debug.print("\x1b[35m--- mat inverse ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const m: Mat4x4 = .translate(5, -3, 2);
|
||||
const inv = m.inverse();
|
||||
const back = m.mul(inv);
|
||||
|
||||
std.debug.print("M * inv(M) should be identity:\n", .{});
|
||||
for (back.value, 0..) |row, i| {
|
||||
std.debug.print("row {d}: {d:.4} {d:.4} {d:.4} {d:.4}\n", .{ i, row[0], row[1], row[2], row[3] });
|
||||
}
|
||||
}
|
||||
|
||||
test "mat perspective" {
|
||||
std.debug.print("\x1b[34m--- mat perspective (vulkan) ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const p: Mat4x4 = .perspective(@as(f32, std.math.pi) / 4.0, 16.0 / 9.0, 0.1, 100.0);
|
||||
std.debug.print("perspective:\n", .{});
|
||||
for (p.value, 0..) |row, i| {
|
||||
std.debug.print("row {d}: {d:.4} {d:.4} {d:.4} {d:.4}\n", .{ i, row[0], row[1], row[2], row[3] });
|
||||
}
|
||||
}
|
||||
|
||||
test "mat lookAt" {
|
||||
std.debug.print("\x1b[35m--- mat lookAt ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const eye: Vec3f = .new(.{ 0, 0, 5 });
|
||||
const target: Vec3f = .new(.{ 0, 0, 0 });
|
||||
const up: Vec3f = .new(.{ 0, 1, 0 });
|
||||
const view: Mat4x4 = .lookAt(eye, target, up);
|
||||
|
||||
std.debug.print("lookAt:\n", .{});
|
||||
for (view.value, 0..) |row, i| {
|
||||
std.debug.print("row {d}: {d:.4} {d:.4} {d:.4} {d:.4}\n", .{ i, row[0], row[1], row[2], row[3] });
|
||||
}
|
||||
}
|
||||
|
||||
test "color init" {
|
||||
std.debug.print("\x1b[34m--- color init ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const c: Color = .rgb(1, 0.5, 0);
|
||||
std.debug.print("orange: r={d:.1} g={d:.1} b={d:.1} a={d:.1}\n", .{ c.r, c.g, c.b, c.a });
|
||||
}
|
||||
|
||||
test "color hex" {
|
||||
std.debug.print("\x1b[35m--- color hex ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const c: Color = .hex(0xFF8800);
|
||||
std.debug.print("0xFF8800: r={d:.4} g={d:.4} b={d:.4} a={d:.1}\n", .{ c.r, c.g, c.b, c.a });
|
||||
}
|
||||
|
||||
test "color blend" {
|
||||
std.debug.print("\x1b[34m--- color blend ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const over = Color.red.blend(Color.green);
|
||||
std.debug.print("red over green: r={d:.4} g={d:.4} b={d:.4} a={d:.1}\n", .{ over.r, over.g, over.b, over.a });
|
||||
}
|
||||
|
||||
test "color hsv" {
|
||||
std.debug.print("\x1b[35m--- color hsv ---\n", .{});
|
||||
defer std.debug.print("\x1b[0m\n", .{});
|
||||
|
||||
const c: Color = .fromHSV(120, 1, 1);
|
||||
const hsv = c.toHSV();
|
||||
std.debug.print("fromHSV(120,1,1): r={d:.4} g={d:.4} b={d:.4} -> h={d:.1} s={d:.1} v={d:.1}\n", .{ c.r, c.g, c.b, hsv.h, hsv.s, hsv.v });
|
||||
}
|
||||
137
src/vec.zig
Normal file
137
src/vec.zig
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
//! ----------------------------------------------------
|
||||
//! ----------------------------------------------------
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// Generic Vector
|
||||
/// ----------------------------------------------------
|
||||
pub fn Vec(
|
||||
comptime T: type,
|
||||
comptime Len: usize,
|
||||
) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
//
|
||||
// FIELDS
|
||||
//
|
||||
|
||||
value: @Vector(Len, T),
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn as(value: T) Self {
|
||||
return .{
|
||||
.value = blk: {
|
||||
var data: [Len]T = undefined;
|
||||
for (0..data.len) |i| {
|
||||
data[i] = value;
|
||||
}
|
||||
break :blk data;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn new(value: @Vector(Len, T)) Self {
|
||||
return .{ .value = value };
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn add(
|
||||
self: Self,
|
||||
other: Self,
|
||||
) Self {
|
||||
return .{
|
||||
.value = self.value + other.value,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn sub(
|
||||
self: Self,
|
||||
other: Self,
|
||||
) Self {
|
||||
return .{
|
||||
.value = self.value - other.value,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn mul(
|
||||
self: Self,
|
||||
other: Self,
|
||||
) Self {
|
||||
return .{
|
||||
.value = self.value * other.value,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn div(
|
||||
self: Self,
|
||||
other: Self,
|
||||
) Self {
|
||||
return .{
|
||||
.value = self.value / other.value,
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn scale(self: Self, scalar: T) Self {
|
||||
return .{
|
||||
.value = self.value * @as(@Vector(Len, T), @splat(scalar)),
|
||||
};
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn negate(self: Self) Self {
|
||||
return .{ .value = -self.value };
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn dot(self: Self, other: Self) T {
|
||||
return @reduce(.Add, self.value * other.value);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn lengthSquared(self: Self) T {
|
||||
return self.dot(self);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn length(self: Self) T {
|
||||
return @sqrt(self.lengthSquared());
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn normalize(self: Self) Self {
|
||||
return self.scale(@as(T, 1) / self.length());
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------
|
||||
/// ----------------------------------------------------
|
||||
pub inline fn cross(self: Self, other: Self) Self {
|
||||
comptime if (Len != 3) @compileError("cross product only defined for Vec3");
|
||||
return .{
|
||||
.value = .{
|
||||
self.value[1] * other.value[2] - self.value[2] * other.value[1],
|
||||
self.value[2] * other.value[0] - self.value[0] * other.value[2],
|
||||
self.value[0] * other.value[1] - self.value[1] * other.value[0],
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue