first commit
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
/build
|
|
||||||
/debug
|
|
||||||
.cache
|
.cache
|
||||||
.vscode
|
.vscode
|
||||||
|
/zig-cache
|
||||||
|
/zig-out
|
||||||
|
|||||||
55
build.zig
Normal file
55
build.zig
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const zmath = @import("lib/zmath/build.zig");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard release options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||||
|
const mode = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "somaesque-native-zig",
|
||||||
|
.root_source_file = .{ .path = "src/main.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = mode,
|
||||||
|
});
|
||||||
|
exe.addIncludePath("/usr/local/include");
|
||||||
|
|
||||||
|
exe.linkLibC();
|
||||||
|
exe.linkSystemLibrary("glfw3");
|
||||||
|
exe.linkSystemLibrary("glm");
|
||||||
|
exe.linkSystemLibrary("GL");
|
||||||
|
exe.addIncludePath("lib/c");
|
||||||
|
|
||||||
|
exe.addCSourceFile("lib/c/glad/glad.c", &[_][]const u8{"-std=c11"});
|
||||||
|
|
||||||
|
exe.install();
|
||||||
|
|
||||||
|
// zmath
|
||||||
|
const zmath_pkg = zmath.package(b, target, mode, .{
|
||||||
|
.options = .{ .enable_cross_platform_determinism = true },
|
||||||
|
});
|
||||||
|
zmath_pkg.link(exe);
|
||||||
|
|
||||||
|
const run_cmd = exe.run();
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
//const exe_tests = b.addTest("src/main.zig");
|
||||||
|
//exe_tests.setTarget(target);
|
||||||
|
//exe_tests.setBuildMode(mode);
|
||||||
|
|
||||||
|
//const test_step = b.step("test", "Run unit tests");
|
||||||
|
//test_step.dependOn(&exe_tests.step);
|
||||||
|
}
|
||||||
1739
lib/c/loaders/tinyobj.h
Normal file
1739
lib/c/loaders/tinyobj.h
Normal file
File diff suppressed because it is too large
Load Diff
138
lib/zmath/README.md
Normal file
138
lib/zmath/README.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# zmath v0.9.6 - SIMD math library for game developers
|
||||||
|
|
||||||
|
Tested on x86_64 and AArch64.
|
||||||
|
|
||||||
|
Provides ~140 optimized routines and ~70 extensive tests.
|
||||||
|
|
||||||
|
Can be used with any graphics API.
|
||||||
|
|
||||||
|
Documentation can be found [here](https://github.com/michal-z/zig-gamedev/blob/main/libs/zmath/src/zmath.zig).
|
||||||
|
|
||||||
|
Benchamrks can be found [here](https://github.com/michal-z/zig-gamedev/blob/main/libs/zmath/src/benchmark.zig).
|
||||||
|
|
||||||
|
An intro article can be found [here](https://zig.news/michalz/fast-multi-platform-simd-math-library-in-zig-2adn).
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
Copy `zmath` folder to a `libs` subdirectory of the root of your project.
|
||||||
|
|
||||||
|
Then in your `build.zig` add:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const std = @import("std");
|
||||||
|
const zmath = @import("libs/zmath/build.zig");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
...
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
zmath_pkg = zmath.package(b, target, optimize, .{
|
||||||
|
.options = .{ .enable_cross_platform_determinism = true },
|
||||||
|
});
|
||||||
|
|
||||||
|
zmath_pkg.link(exe);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now in your code you may import and use zmath:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const zm = @import("zmath");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
//
|
||||||
|
// OpenGL/Vulkan example
|
||||||
|
//
|
||||||
|
const object_to_world = zm.rotationY(..);
|
||||||
|
const world_to_view = zm.lookAtRh(
|
||||||
|
zm.f32x4(3.0, 3.0, 3.0, 1.0), // eye position
|
||||||
|
zm.f32x4(0.0, 0.0, 0.0, 1.0), // focus point
|
||||||
|
zm.f32x4(0.0, 1.0, 0.0, 0.0), // up direction ('w' coord is zero because this is a vector not a point)
|
||||||
|
);
|
||||||
|
// `perspectiveFovRhGl` produces Z values in [-1.0, 1.0] range (Vulkan app should use `perspectiveFovRh`)
|
||||||
|
const view_to_clip = zm.perspectiveFovRhGl(0.25 * math.pi, aspect_ratio, 0.1, 20.0);
|
||||||
|
|
||||||
|
const object_to_view = zm.mul(object_to_world, world_to_view);
|
||||||
|
const object_to_clip = zm.mul(object_to_view, view_to_clip);
|
||||||
|
|
||||||
|
// Transposition is needed because GLSL uses column-major matrices by default
|
||||||
|
gl.uniformMatrix4fv(0, 1, gl.TRUE, zm.arrNPtr(&object_to_clip));
|
||||||
|
|
||||||
|
// In GLSL: gl_Position = vec4(in_position, 1.0) * object_to_clip;
|
||||||
|
|
||||||
|
//
|
||||||
|
// DirectX example
|
||||||
|
//
|
||||||
|
const object_to_world = zm.rotationY(..);
|
||||||
|
const world_to_view = zm.lookAtLh(
|
||||||
|
zm.f32x4(3.0, 3.0, -3.0, 1.0), // eye position
|
||||||
|
zm.f32x4(0.0, 0.0, 0.0, 1.0), // focus point
|
||||||
|
zm.f32x4(0.0, 1.0, 0.0, 0.0), // up direction ('w' coord is zero because this is a vector not a point)
|
||||||
|
);
|
||||||
|
const view_to_clip = zm.perspectiveFovLh(0.25 * math.pi, aspect_ratio, 0.1, 20.0);
|
||||||
|
|
||||||
|
const object_to_view = zm.mul(object_to_world, world_to_view);
|
||||||
|
const object_to_clip = zm.mul(object_to_view, view_to_clip);
|
||||||
|
|
||||||
|
// Transposition is needed because HLSL uses column-major matrices by default
|
||||||
|
const mem = allocateUploadMemory(...);
|
||||||
|
zm.storeMat(mem, zm.transpose(object_to_clip));
|
||||||
|
|
||||||
|
// In HLSL: out_position_sv = mul(float4(in_position, 1.0), object_to_clip);
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'WASD' camera movement example
|
||||||
|
//
|
||||||
|
{
|
||||||
|
const speed = zm.f32x4s(10.0);
|
||||||
|
const delta_time = zm.f32x4s(demo.frame_stats.delta_time);
|
||||||
|
const transform = zm.mul(zm.rotationX(demo.camera.pitch), zm.rotationY(demo.camera.yaw));
|
||||||
|
var forward = zm.normalize3(zm.mul(zm.f32x4(0.0, 0.0, 1.0, 0.0), transform));
|
||||||
|
|
||||||
|
zm.storeArr3(&demo.camera.forward, forward);
|
||||||
|
|
||||||
|
const right = speed * delta_time * zm.normalize3(zm.cross3(zm.f32x4(0.0, 1.0, 0.0, 0.0), forward));
|
||||||
|
forward = speed * delta_time * forward;
|
||||||
|
|
||||||
|
var cam_pos = zm.loadArr3(demo.camera.position);
|
||||||
|
|
||||||
|
if (keyDown('W')) {
|
||||||
|
cam_pos += forward;
|
||||||
|
} else if (keyDown('S')) {
|
||||||
|
cam_pos -= forward;
|
||||||
|
}
|
||||||
|
if (keyDown('D')) {
|
||||||
|
cam_pos += right;
|
||||||
|
} else if (keyDown('A')) {
|
||||||
|
cam_pos -= right;
|
||||||
|
}
|
||||||
|
|
||||||
|
zm.storeArr3(&demo.camera.position, cam_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SIMD wave equation solver example (works with vector width 4, 8 and 16)
|
||||||
|
// 'T' can be F32x4, F32x8 or F32x16
|
||||||
|
//
|
||||||
|
var z_index: i32 = 0;
|
||||||
|
while (z_index < grid_size) : (z_index += 1) {
|
||||||
|
const z = scale * @intToFloat(f32, z_index - grid_size / 2);
|
||||||
|
const vz = zm.splat(T, z);
|
||||||
|
|
||||||
|
var x_index: i32 = 0;
|
||||||
|
while (x_index < grid_size) : (x_index += zm.veclen(T)) {
|
||||||
|
const x = scale * @intToFloat(f32, x_index - grid_size / 2);
|
||||||
|
const vx = zm.splat(T, x) + voffset * zm.splat(T, scale);
|
||||||
|
|
||||||
|
const d = zm.sqrt(vx * vx + vz * vz);
|
||||||
|
const vy = zm.sin(d - vtime);
|
||||||
|
|
||||||
|
const index = @intCast(usize, x_index + z_index * grid_size);
|
||||||
|
zm.store(xslice[index..], vx, 0);
|
||||||
|
zm.store(yslice[index..], vy, 0);
|
||||||
|
zm.store(zslice[index..], vz, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
97
lib/zmath/build.zig
Normal file
97
lib/zmath/build.zig
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
enable_cross_platform_determinism: bool = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Package = struct {
|
||||||
|
options: Options,
|
||||||
|
zmath: *std.Build.Module,
|
||||||
|
zmath_options: *std.Build.Module,
|
||||||
|
|
||||||
|
pub fn link(pkg: Package, exe: *std.Build.CompileStep) void {
|
||||||
|
exe.addModule("zmath", pkg.zmath);
|
||||||
|
exe.addModule("zmath_options", pkg.zmath_options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn package(
|
||||||
|
b: *std.Build,
|
||||||
|
_: std.zig.CrossTarget,
|
||||||
|
_: std.builtin.Mode,
|
||||||
|
args: struct {
|
||||||
|
options: Options = .{},
|
||||||
|
},
|
||||||
|
) Package {
|
||||||
|
const step = b.addOptions();
|
||||||
|
step.addOption(
|
||||||
|
bool,
|
||||||
|
"enable_cross_platform_determinism",
|
||||||
|
args.options.enable_cross_platform_determinism,
|
||||||
|
);
|
||||||
|
|
||||||
|
const zmath_options = step.createModule();
|
||||||
|
|
||||||
|
const zmath = b.createModule(.{
|
||||||
|
.source_file = .{ .path = thisDir() ++ "/src/main.zig" },
|
||||||
|
.dependencies = &.{
|
||||||
|
.{ .name = "zmath_options", .module = zmath_options },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.options = args.options,
|
||||||
|
.zmath = zmath,
|
||||||
|
.zmath_options = zmath_options,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run zmath tests");
|
||||||
|
test_step.dependOn(runTests(b, optimize, target));
|
||||||
|
|
||||||
|
const benchmark_step = b.step("benchmark", "Run zmath benchmarks");
|
||||||
|
benchmark_step.dependOn(runBenchmarks(b, target));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runTests(
|
||||||
|
b: *std.Build,
|
||||||
|
optimize: std.builtin.Mode,
|
||||||
|
target: std.zig.CrossTarget,
|
||||||
|
) *std.Build.Step {
|
||||||
|
const tests = b.addTest(.{
|
||||||
|
.name = "zmath-tests",
|
||||||
|
.root_source_file = .{ .path = thisDir() ++ "/src/main.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const zmath_pkg = package(b, target, optimize, .{});
|
||||||
|
tests.addModule("zmath_options", zmath_pkg.zmath_options);
|
||||||
|
|
||||||
|
return &tests.run().step;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runBenchmarks(
|
||||||
|
b: *std.Build,
|
||||||
|
target: std.zig.CrossTarget,
|
||||||
|
) *std.Build.Step {
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "zmath-benchmarks",
|
||||||
|
.root_source_file = .{ .path = thisDir() ++ "/src/benchmark.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = .ReleaseFast,
|
||||||
|
});
|
||||||
|
|
||||||
|
const zmath_pkg = package(b, target, .ReleaseFast, .{});
|
||||||
|
exe.addModule("zmath", zmath_pkg.zmath);
|
||||||
|
|
||||||
|
return &exe.run().step;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn thisDir() []const u8 {
|
||||||
|
return comptime std.fs.path.dirname(@src().file) orelse ".";
|
||||||
|
}
|
||||||
469
lib/zmath/src/benchmark.zig
Normal file
469
lib/zmath/src/benchmark.zig
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// zmath - benchmarks
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// 'zig build benchmark' in the root project directory will build and run 'ReleaseFast' configuration.
|
||||||
|
//
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// 'AMD Ryzen 9 3950X 16-Core Processor', Windows 11, Zig 0.10.0-dev.2620+0e9458a3f
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// matrix mul benchmark (AOS) - scalar version: 1.5880s, zmath version: 1.0642s
|
||||||
|
// cross3, scale, bias benchmark (AOS) - scalar version: 0.9318s, zmath version: 0.6888s
|
||||||
|
// cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.2258s, zmath version: 1.1095s
|
||||||
|
// quaternion mul benchmark (AOS) - scalar version: 1.4123s, zmath version: 0.6958s
|
||||||
|
// wave benchmark (SOA) - scalar version: 4.8165s, zmath version: 0.7338s
|
||||||
|
//
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// 'AMD Ryzen 7 5800X 8-Core Processer', Linux 5.17.14, Zig 0.10.0-dev.2624+d506275a0
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// matrix mul benchmark (AOS) - scalar version: 1.3672s, zmath version: 0.8617s
|
||||||
|
// cross3, scale, bias benchmark (AOS) - scalar version: 0.6586s, zmath version: 0.4803s
|
||||||
|
// cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.0620s, zmath version: 0.8942s
|
||||||
|
// quaternion mul benchmark (AOS) - scalar version: 1.1324s, zmath version: 0.6064s
|
||||||
|
// wave benchmark (SOA) - scalar version: 3.6598s, zmath version: 0.4231s
|
||||||
|
//
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// 'Apple M1 Max', macOS Version 12.4, Zig 0.10.0-dev.2657+74442f350
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// matrix mul benchmark (AOS) - scalar version: 1.0297s, zmath version: 1.0538s
|
||||||
|
// cross3, scale, bias benchmark (AOS) - scalar version: 0.6294s, zmath version: 0.6532s
|
||||||
|
// cross3, dot3, scale, bias benchmark (AOS) - scalar version: 0.9807s, zmath version: 1.0988s
|
||||||
|
// quaternion mul benchmark (AOS) - scalar version: 1.5413s, zmath version: 0.7800s
|
||||||
|
// wave benchmark (SOA) - scalar version: 3.4220s, zmath version: 1.0255s
|
||||||
|
//
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// '11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz', Windows 11, Zig 0.10.0-dev.2620+0e9458a3f
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// matrix mul benchmark (AOS) - scalar version: 2.2308s, zmath version: 0.9376s
|
||||||
|
// cross3, scale, bias benchmark (AOS) - scalar version: 1.0821s, zmath version: 0.5110s
|
||||||
|
// cross3, dot3, scale, bias benchmark (AOS) - scalar version: 1.6580s, zmath version: 0.9167s
|
||||||
|
// quaternion mul benchmark (AOS) - scalar version: 2.0139s, zmath version: 0.5856s
|
||||||
|
// wave benchmark (SOA) - scalar version: 3.7832s, zmath version: 0.3642s
|
||||||
|
//
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
// m = mul(ma, mb); data set fits in L1 cache; AOS data layout.
|
||||||
|
try mat4MulBenchmark(allocator, 100_000);
|
||||||
|
|
||||||
|
// v = 0.01 * cross3(va, vb) + vec3(1.0); data set fits in L1 cache; AOS data layout.
|
||||||
|
try cross3ScaleBiasBenchmark(allocator, 10_000);
|
||||||
|
|
||||||
|
// v = dot3(va, vb) * (0.1 * cross3(va, vb) + vec3(1.0)); data set fits in L1 cache; AOS data layout.
|
||||||
|
try cross3Dot3ScaleBiasBenchmark(allocator, 10_000);
|
||||||
|
|
||||||
|
// q = qmul(qa, qb); data set fits in L1 cache; AOS data layout.
|
||||||
|
try quatBenchmark(allocator, 10_000);
|
||||||
|
|
||||||
|
// d = sqrt(x * x + z * z); y = sin(d - t); SOA layout.
|
||||||
|
try waveBenchmark(allocator, 1_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const time = std.time;
|
||||||
|
const Timer = time.Timer;
|
||||||
|
const zm = @import("zmath");
|
||||||
|
|
||||||
|
var prng = std.rand.DefaultPrng.init(0);
|
||||||
|
const random = prng.random();
|
||||||
|
|
||||||
|
noinline fn mat4MulBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||||
|
std.debug.print("\n", .{});
|
||||||
|
std.debug.print("{s:>42} - ", .{"matrix mul benchmark (AOS)"});
|
||||||
|
|
||||||
|
var data0 = std.ArrayList([16]f32).init(allocator);
|
||||||
|
defer data0.deinit();
|
||||||
|
var data1 = std.ArrayList([16]f32).init(allocator);
|
||||||
|
defer data1.deinit();
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < 64) : (i += 1) {
|
||||||
|
try data0.append([16]f32{
|
||||||
|
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||||
|
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||||
|
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||||
|
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||||
|
});
|
||||||
|
try data1.append([16]f32{
|
||||||
|
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||||
|
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||||
|
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||||
|
random.float(f32), random.float(f32), random.float(f32), random.float(f32),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warmup, fills L1 cache.
|
||||||
|
i = 0;
|
||||||
|
while (i < 100) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const ma = zm.loadMat(a[0..]);
|
||||||
|
const mb = zm.loadMat(b[0..]);
|
||||||
|
const r = zm.mul(ma, mb);
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const r = [16]f32{
|
||||||
|
a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12],
|
||||||
|
a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13],
|
||||||
|
a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14],
|
||||||
|
a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15],
|
||||||
|
a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12],
|
||||||
|
a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13],
|
||||||
|
a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14],
|
||||||
|
a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15],
|
||||||
|
a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12],
|
||||||
|
a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13],
|
||||||
|
a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14],
|
||||||
|
a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15],
|
||||||
|
a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12],
|
||||||
|
a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13],
|
||||||
|
a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14],
|
||||||
|
a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15],
|
||||||
|
};
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const ma = zm.loadMat(a[0..]);
|
||||||
|
const mb = zm.loadMat(b[0..]);
|
||||||
|
const r = zm.mul(ma, mb);
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noinline fn cross3ScaleBiasBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||||
|
std.debug.print("{s:>42} - ", .{"cross3, scale, bias benchmark (AOS)"});
|
||||||
|
|
||||||
|
var data0 = std.ArrayList([3]f32).init(allocator);
|
||||||
|
defer data0.deinit();
|
||||||
|
var data1 = std.ArrayList([3]f32).init(allocator);
|
||||||
|
defer data1.deinit();
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < 256) : (i += 1) {
|
||||||
|
try data0.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
|
||||||
|
try data1.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warmup, fills L1 cache.
|
||||||
|
i = 0;
|
||||||
|
while (i < 100) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const va = zm.loadArr3(a);
|
||||||
|
const vb = zm.loadArr3(b);
|
||||||
|
const cp = zm.f32x4s(0.01) * zm.cross3(va, vb) + zm.f32x4s(1.0);
|
||||||
|
std.mem.doNotOptimizeAway(&cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const r = [3]f32{
|
||||||
|
0.01 * (a[1] * b[2] - a[2] * b[1]) + 1.0,
|
||||||
|
0.01 * (a[2] * b[0] - a[0] * b[2]) + 1.0,
|
||||||
|
0.01 * (a[0] * b[1] - a[1] * b[0]) + 1.0,
|
||||||
|
};
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const va = zm.loadArr3(a);
|
||||||
|
const vb = zm.loadArr3(b);
|
||||||
|
const cp = zm.f32x4s(0.01) * zm.cross3(va, vb) + zm.f32x4s(1.0);
|
||||||
|
std.mem.doNotOptimizeAway(&cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noinline fn cross3Dot3ScaleBiasBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||||
|
std.debug.print("{s:>42} - ", .{"cross3, dot3, scale, bias benchmark (AOS)"});
|
||||||
|
|
||||||
|
var data0 = std.ArrayList([3]f32).init(allocator);
|
||||||
|
defer data0.deinit();
|
||||||
|
var data1 = std.ArrayList([3]f32).init(allocator);
|
||||||
|
defer data1.deinit();
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < 256) : (i += 1) {
|
||||||
|
try data0.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
|
||||||
|
try data1.append([3]f32{ random.float(f32), random.float(f32), random.float(f32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warmup, fills L1 cache.
|
||||||
|
i = 0;
|
||||||
|
while (i < 100) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const va = zm.loadArr3(a);
|
||||||
|
const vb = zm.loadArr3(b);
|
||||||
|
const r = (zm.dot3(va, vb) * (zm.f32x4s(0.1) * zm.cross3(va, vb) + zm.f32x4s(1.0)))[0];
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const d = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||||
|
const r = [3]f32{
|
||||||
|
d * (0.1 * (a[1] * b[2] - a[2] * b[1]) + 1.0),
|
||||||
|
d * (0.1 * (a[2] * b[0] - a[0] * b[2]) + 1.0),
|
||||||
|
d * (0.1 * (a[0] * b[1] - a[1] * b[0]) + 1.0),
|
||||||
|
};
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const va = zm.loadArr3(a);
|
||||||
|
const vb = zm.loadArr3(b);
|
||||||
|
const r = zm.dot3(va, vb) * (zm.f32x4s(0.1) * zm.cross3(va, vb) + zm.f32x4s(1.0));
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noinline fn quatBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||||
|
std.debug.print("{s:>42} - ", .{"quaternion mul benchmark (AOS)"});
|
||||||
|
|
||||||
|
var data0 = std.ArrayList([4]f32).init(allocator);
|
||||||
|
defer data0.deinit();
|
||||||
|
var data1 = std.ArrayList([4]f32).init(allocator);
|
||||||
|
defer data1.deinit();
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < 256) : (i += 1) {
|
||||||
|
try data0.append([4]f32{ random.float(f32), random.float(f32), random.float(f32), random.float(f32) });
|
||||||
|
try data1.append([4]f32{ random.float(f32), random.float(f32), random.float(f32), random.float(f32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warmup, fills L1 cache.
|
||||||
|
i = 0;
|
||||||
|
while (i < 100) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const va = zm.loadArr4(a);
|
||||||
|
const vb = zm.loadArr4(b);
|
||||||
|
const r = zm.qmul(va, vb);
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const r = [4]f32{
|
||||||
|
(b[3] * a[0]) + (b[0] * a[3]) + (b[1] * a[2]) - (b[2] * a[1]),
|
||||||
|
(b[3] * a[1]) - (b[0] * a[2]) + (b[1] * a[3]) + (b[2] * a[0]),
|
||||||
|
(b[3] * a[2]) + (b[0] * a[1]) - (b[1] * a[0]) + (b[2] * a[3]),
|
||||||
|
(b[3] * a[3]) - (b[0] * a[0]) - (b[1] * a[1]) - (b[2] * a[2]),
|
||||||
|
};
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
for (data1.items) |b| {
|
||||||
|
for (data0.items) |a| {
|
||||||
|
const va = zm.loadArr4(a);
|
||||||
|
const vb = zm.loadArr4(b);
|
||||||
|
const r = zm.qmul(va, vb);
|
||||||
|
std.mem.doNotOptimizeAway(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noinline fn waveBenchmark(allocator: std.mem.Allocator, comptime count: comptime_int) !void {
|
||||||
|
_ = allocator;
|
||||||
|
std.debug.print("{s:>42} - ", .{"wave benchmark (SOA)"});
|
||||||
|
|
||||||
|
const grid_size = 1024;
|
||||||
|
{
|
||||||
|
var t: f32 = 0.0;
|
||||||
|
|
||||||
|
const scale: f32 = 0.05;
|
||||||
|
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
|
||||||
|
var iter: usize = 0;
|
||||||
|
while (iter < count) : (iter += 1) {
|
||||||
|
var z_index: i32 = 0;
|
||||||
|
while (z_index < grid_size) : (z_index += 1) {
|
||||||
|
const z = scale * @intToFloat(f32, z_index - grid_size / 2);
|
||||||
|
|
||||||
|
var x_index: i32 = 0;
|
||||||
|
while (x_index < grid_size) : (x_index += 4) {
|
||||||
|
const x0 = scale * @intToFloat(f32, x_index + 0 - grid_size / 2);
|
||||||
|
const x1 = scale * @intToFloat(f32, x_index + 1 - grid_size / 2);
|
||||||
|
const x2 = scale * @intToFloat(f32, x_index + 2 - grid_size / 2);
|
||||||
|
const x3 = scale * @intToFloat(f32, x_index + 3 - grid_size / 2);
|
||||||
|
|
||||||
|
const d0 = zm.sqrt(x0 * x0 + z * z);
|
||||||
|
const d1 = zm.sqrt(x1 * x1 + z * z);
|
||||||
|
const d2 = zm.sqrt(x2 * x2 + z * z);
|
||||||
|
const d3 = zm.sqrt(x3 * x3 + z * z);
|
||||||
|
|
||||||
|
const y0 = zm.sin(d0 - t);
|
||||||
|
const y1 = zm.sin(d1 - t);
|
||||||
|
const y2 = zm.sin(d2 - t);
|
||||||
|
const y3 = zm.sin(d3 - t);
|
||||||
|
|
||||||
|
std.mem.doNotOptimizeAway(&y0);
|
||||||
|
std.mem.doNotOptimizeAway(&y1);
|
||||||
|
std.mem.doNotOptimizeAway(&y2);
|
||||||
|
std.mem.doNotOptimizeAway(&y3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t += 0.001;
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("scalar version: {d:.4}s, ", .{elapsed_s});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const T = zm.F32x16;
|
||||||
|
|
||||||
|
const static = struct {
|
||||||
|
const offsets = [16]f32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||||
|
};
|
||||||
|
const voffset = zm.load(static.offsets[0..], T, 0);
|
||||||
|
var vt = zm.splat(T, 0.0);
|
||||||
|
|
||||||
|
const scale: f32 = 0.05;
|
||||||
|
|
||||||
|
var timer = try Timer.start();
|
||||||
|
const start = timer.lap();
|
||||||
|
|
||||||
|
var iter: usize = 0;
|
||||||
|
while (iter < count) : (iter += 1) {
|
||||||
|
var z_index: i32 = 0;
|
||||||
|
while (z_index < grid_size) : (z_index += 1) {
|
||||||
|
const z = scale * @intToFloat(f32, z_index - grid_size / 2);
|
||||||
|
const vz = zm.splat(T, z);
|
||||||
|
|
||||||
|
var x_index: i32 = 0;
|
||||||
|
while (x_index < grid_size) : (x_index += zm.veclen(T)) {
|
||||||
|
const x = scale * @intToFloat(f32, x_index - grid_size / 2);
|
||||||
|
const vx = zm.splat(T, x) + voffset * zm.splat(T, scale);
|
||||||
|
|
||||||
|
const d = zm.sqrt(vx * vx + vz * vz);
|
||||||
|
|
||||||
|
const vy = zm.sin(d - vt);
|
||||||
|
|
||||||
|
std.mem.doNotOptimizeAway(&vy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vt += zm.splat(T, 0.001);
|
||||||
|
}
|
||||||
|
const end = timer.read();
|
||||||
|
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||||
|
|
||||||
|
std.debug.print("zmath version: {d:.4}s\n", .{elapsed_s});
|
||||||
|
}
|
||||||
|
}
|
||||||
18
lib/zmath/src/main.zig
Normal file
18
lib/zmath/src/main.zig
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// SIMD math library for game developers
|
||||||
|
// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath
|
||||||
|
//
|
||||||
|
// See zmath.zig for more details.
|
||||||
|
// See util.zig for additional functionality.
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
pub const version = @import("std").SemanticVersion{ .major = 0, .minor = 9, .patch = 6 };
|
||||||
|
|
||||||
|
pub usingnamespace @import("zmath.zig");
|
||||||
|
pub const util = @import("util.zig");
|
||||||
|
|
||||||
|
// ensure transitive closure of test coverage
|
||||||
|
comptime {
|
||||||
|
_ = util;
|
||||||
|
}
|
||||||
182
lib/zmath/src/util.zig
Normal file
182
lib/zmath/src/util.zig
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
// ==============================================================================
|
||||||
|
//
|
||||||
|
// Collection of useful functions building on top of, and extending, core zmath.
|
||||||
|
// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath
|
||||||
|
//
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// 1. Matrix functions
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// As an example, in a left handed Y-up system:
|
||||||
|
// getAxisX is equivalent to the right vector
|
||||||
|
// getAxisY is equivalent to the up vector
|
||||||
|
// getAxisZ is equivalent to the forward vector
|
||||||
|
//
|
||||||
|
// getTranslationVec(m: Mat) Vec
|
||||||
|
// getAxisX(m: Mat) Vec
|
||||||
|
// getAxisY(m: Mat) Vec
|
||||||
|
// getAxisZ(m: Mat) Vec
|
||||||
|
//
|
||||||
|
// ==============================================================================
|
||||||
|
|
||||||
|
const zm = @import("zmath.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
const math = std.math;
|
||||||
|
const expect = std.testing.expect;
|
||||||
|
|
||||||
|
pub fn getTranslationVec(m: zm.Mat) zm.Vec {
|
||||||
|
var translation = m[3];
|
||||||
|
translation[3] = 0;
|
||||||
|
return translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getScaleVec(m: zm.Mat) zm.Vec {
|
||||||
|
const scale_x = zm.length3(zm.f32x4(m[0][0], m[1][0], m[2][0], 0))[0];
|
||||||
|
const scale_y = zm.length3(zm.f32x4(m[0][1], m[1][1], m[2][1], 0))[0];
|
||||||
|
const scale_z = zm.length3(zm.f32x4(m[0][2], m[1][2], m[2][2], 0))[0];
|
||||||
|
return zm.f32x4(scale_x, scale_y, scale_z, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getRotationQuat(_m: zm.Mat) zm.Quat {
|
||||||
|
// Ortho normalize given matrix.
|
||||||
|
const c1 = zm.normalize3(zm.f32x4(_m[0][0], _m[1][0], _m[2][0], 0));
|
||||||
|
const c2 = zm.normalize3(zm.f32x4(_m[0][1], _m[1][1], _m[2][1], 0));
|
||||||
|
const c3 = zm.normalize3(zm.f32x4(_m[0][2], _m[1][2], _m[2][2], 0));
|
||||||
|
var m = _m;
|
||||||
|
m[0][0] = c1[0];
|
||||||
|
m[1][0] = c1[1];
|
||||||
|
m[2][0] = c1[2];
|
||||||
|
m[0][1] = c2[0];
|
||||||
|
m[1][1] = c2[1];
|
||||||
|
m[2][1] = c2[2];
|
||||||
|
m[0][2] = c3[0];
|
||||||
|
m[1][2] = c3[1];
|
||||||
|
m[2][2] = c3[2];
|
||||||
|
|
||||||
|
// Extract rotation
|
||||||
|
return zm.quatFromMat(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAxisX(m: zm.Mat) zm.Vec {
|
||||||
|
return zm.normalize3(zm.f32x4(m[0][0], m[0][1], m[0][2], 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAxisY(m: zm.Mat) zm.Vec {
|
||||||
|
return zm.normalize3(zm.f32x4(m[1][0], m[1][1], m[1][2], 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAxisZ(m: zm.Mat) zm.Vec {
|
||||||
|
return zm.normalize3(zm.f32x4(m[2][0], m[2][1], m[2][2], 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zmath.util.mat.translation" {
|
||||||
|
// zig fmt: off
|
||||||
|
const mat_data = [18]f32{
|
||||||
|
1.0,
|
||||||
|
2.0, 3.0, 4.0, 5.0,
|
||||||
|
6.0, 7.0, 8.0, 9.0,
|
||||||
|
10.0,11.0, 12.0,13.0,
|
||||||
|
14.0, 15.0, 16.0, 17.0,
|
||||||
|
18.0,
|
||||||
|
};
|
||||||
|
// zig fmt: on
|
||||||
|
const mat = zm.loadMat(mat_data[1..]);
|
||||||
|
const translation = getTranslationVec(mat);
|
||||||
|
try expect(zm.approxEqAbs(translation, zm.f32x4(14.0, 15.0, 16.0, 0.0), 0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zmath.util.mat.scale" {
|
||||||
|
const mat = zm.mul(zm.scaling(3, 4, 5), zm.translation(6, 7, 8));
|
||||||
|
const scale = getScaleVec(mat);
|
||||||
|
try expect(zm.approxEqAbs(scale, zm.f32x4(3.0, 4.0, 5.0, 0.0), 0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zmath.util.mat.rotation" {
|
||||||
|
const rotate_origin = zm.matFromRollPitchYaw(0.1, 1.2, 2.3);
|
||||||
|
const mat = zm.mul(zm.mul(rotate_origin, zm.scaling(3, 4, 5)), zm.translation(6, 7, 8));
|
||||||
|
const rotate_get = getRotationQuat(mat);
|
||||||
|
const v0 = zm.mul(zm.f32x4s(1), rotate_origin);
|
||||||
|
const v1 = zm.mul(zm.f32x4s(1), zm.quatToMat(rotate_get));
|
||||||
|
try expect(zm.approxEqAbs(v0, v1, 0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zmath.util.mat.z_vec" {
|
||||||
|
const degToRad = std.math.degreesToRadians;
|
||||||
|
var identity = zm.identity();
|
||||||
|
var z_vec = getAxisZ(identity);
|
||||||
|
try expect(zm.approxEqAbs(z_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.0001));
|
||||||
|
const rot_yaw = zm.rotationY(degToRad(f32, 90));
|
||||||
|
identity = zm.mul(identity, rot_yaw);
|
||||||
|
z_vec = getAxisZ(identity);
|
||||||
|
try expect(zm.approxEqAbs(z_vec, zm.f32x4(1.0, 0.0, 0.0, 0), 0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zmath.util.mat.y_vec" {
|
||||||
|
const degToRad = std.math.degreesToRadians;
|
||||||
|
var identity = zm.identity();
|
||||||
|
var y_vec = getAxisY(identity);
|
||||||
|
try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
|
||||||
|
const rot_yaw = zm.rotationY(degToRad(f32, 90));
|
||||||
|
identity = zm.mul(identity, rot_yaw);
|
||||||
|
y_vec = getAxisY(identity);
|
||||||
|
try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
|
||||||
|
const rot_pitch = zm.rotationX(degToRad(f32, 90));
|
||||||
|
identity = zm.mul(identity, rot_pitch);
|
||||||
|
y_vec = getAxisY(identity);
|
||||||
|
try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zmath.util.mat.right" {
|
||||||
|
const degToRad = std.math.degreesToRadians;
|
||||||
|
var identity = zm.identity();
|
||||||
|
var right = getAxisX(identity);
|
||||||
|
try expect(zm.approxEqAbs(right, zm.f32x4(1.0, 0.0, 0.0, 0), 0.01));
|
||||||
|
const rot_yaw = zm.rotationY(degToRad(f32, 90));
|
||||||
|
identity = zm.mul(identity, rot_yaw);
|
||||||
|
right = getAxisX(identity);
|
||||||
|
try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 0.0, -1.0, 0), 0.01));
|
||||||
|
const rot_pitch = zm.rotationX(degToRad(f32, 90));
|
||||||
|
identity = zm.mul(identity, rot_pitch);
|
||||||
|
right = getAxisX(identity);
|
||||||
|
try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// ALTERNATIVE A - MIT License
|
||||||
|
// Copyright (c) 2022 Michal Ziulek and Contributors
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining identity copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
// so, subject to the following conditions:
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
// software, either in source code form or as identity compiled binary, for any purpose,
|
||||||
|
// commercial or non-commercial, and by any means.
|
||||||
|
// In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
// software dedicate any and all copyright interest in the software to the public
|
||||||
|
// domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
// the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
// overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
// this software under copyright law.
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
4442
lib/zmath/src/zmath.zig
Normal file
4442
lib/zmath/src/zmath.zig
Normal file
File diff suppressed because it is too large
Load Diff
9
src/c.zig
Normal file
9
src/c.zig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
pub usingnamespace @cImport({
|
||||||
|
@cInclude("glad/glad.h");
|
||||||
|
@cInclude("GLFW/glfw3.h");
|
||||||
|
|
||||||
|
@cDefine("STB_IMAGE_IMPLEMENTATION", "");
|
||||||
|
@cDefine("TINYOBJ_LOADER_C_IMPLEMENTATION", "");
|
||||||
|
@cInclude("loaders/stb_image.h");
|
||||||
|
@cInclude("loaders/tinyobj.h");
|
||||||
|
});
|
||||||
43
src/gfx/Color.zig
Normal file
43
src/gfx/Color.zig
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
fn hue_to_rgb(p: f32, q: f32, t: f32) f32 {
|
||||||
|
if (t < 0) {
|
||||||
|
t += 1;
|
||||||
|
} else if (t > 1) {
|
||||||
|
t -= 1;
|
||||||
|
}
|
||||||
|
if (t < 1.0 / 6) return p + (q - p) * 6 * t;
|
||||||
|
if (t < 1.0 / 2) return q;
|
||||||
|
if (t < 2.0 / 3) return p + (q - p) * (2.0 / 3 - t) * 6;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hsl_to_hex(h: f32, s: f32, l: f32) @Vector(3, f32) {
|
||||||
|
h /= 360;
|
||||||
|
s /= 100;
|
||||||
|
l /= 100;
|
||||||
|
const r: f32;
|
||||||
|
const g: f32;
|
||||||
|
const b: f32;
|
||||||
|
if (s == 0) {
|
||||||
|
r = l;
|
||||||
|
g = l;
|
||||||
|
b = l;
|
||||||
|
} else {
|
||||||
|
const q = if (l < 0.5) l * (1 + s) else l + s - l * s;
|
||||||
|
const p = 2 * l - q;
|
||||||
|
r = hue_to_rgb(p, q, h + 1.0 / 3);
|
||||||
|
g = hue_to_rgb(p, q, h);
|
||||||
|
b = hue_to_rgb(p, q, h - 1.0 / 3);
|
||||||
|
}
|
||||||
|
return @Vector(3, f32){ r, g, b };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn color_from_index(index: i32) @Vector(3, f32) {
|
||||||
|
const color_wheel_cycle = @floor(index / 6.0);
|
||||||
|
const darkness_cycle = @floor(index / 12.0);
|
||||||
|
const spacing = (360.0 / 6.0);
|
||||||
|
const offset = if (color_wheel_cycle == 0) 0 else spacing / (color_wheel_cycle + 2);
|
||||||
|
const hue = spacing * (index % 6) + offset;
|
||||||
|
const saturation = 100.0f;
|
||||||
|
const lightness = 1.0f / (2 + darkness_cycle) * 100;
|
||||||
|
return hsl_to_hex(hue, saturation, lightness);
|
||||||
|
}
|
||||||
94
src/gfx/Mesh.zig
Normal file
94
src/gfx/Mesh.zig
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const c = @import("../c.zig");
|
||||||
|
const djleddaGeom = @import("djleddaGeom.zig");
|
||||||
|
|
||||||
|
pub const Mesh = struct {
|
||||||
|
vao: c_uint,
|
||||||
|
vbo_xyz: c_uint,
|
||||||
|
vbo_uv: c_uint,
|
||||||
|
vbo_norm: c_uint,
|
||||||
|
ebo: c_uint,
|
||||||
|
num_indices: c_uint,
|
||||||
|
|
||||||
|
pub fn from_shape(shape: *const djleddaGeom.Shape) void {
|
||||||
|
const mesh = Mesh{};
|
||||||
|
mesh.num_indices = shape.indices.len;
|
||||||
|
c.glGenVertexArrays(1, &mesh.vao);
|
||||||
|
c.glGenBuffers(1, &mesh.vbo_xyz);
|
||||||
|
c.glGenBuffers(1, &mesh.vbo_uv);
|
||||||
|
c.glGenBuffers(1, &mesh.ebo);
|
||||||
|
|
||||||
|
c.glBindVertexArray(mesh.vao);
|
||||||
|
|
||||||
|
c.glBindBuffer(c.GL_ARRAY_BUFFER, mesh.vbo_xyz);
|
||||||
|
c.glBufferData(c.GL_ARRAY_BUFFER, shape.xyz.ptr * @sizeOf(float), shape.xyz, c.GL_STATIC_DRAW);
|
||||||
|
c.glVertexAttribPointer(0, 3, c.GL_FLOAT, c.GL_FALSE, 3 * @sizeOf(f32), @as(*void, 0));
|
||||||
|
c.glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
|
c.glBindBuffer(c.GL_ARRAY_BUFFER, mesh.vbo_uv);
|
||||||
|
c.glBufferData(c.GL_ARRAY_BUFFER, shape.uv.ptr * @sizeOf(f32), shape.uv, c.GL_STATIC_DRAW);
|
||||||
|
c.glVertexAttribPointer(1, 2, c.GL_FLOAT, c.GL_FALSE, 2 * @sizeOf(f32), @as(*void, 0));
|
||||||
|
c.glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
|
c.glBindBuffer(c.GL_ELEMENT_ARRAY_BUFFER, mesh.ebo);
|
||||||
|
c.glBufferData(c.GL_ELEMENT_ARRAY_BUFFER, shape.indices.len * @sizeOf(c_uint), shape.indices.ptr, c.GL_STATIC_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn init(obj_file: *[]const u8) void {
|
||||||
|
// const reader = c.tinyobj.ObjReader();
|
||||||
|
// const success = reader.ParseFromFile(obj_file);
|
||||||
|
// std.debug.print("{}\n", .{reader.Error()});
|
||||||
|
//
|
||||||
|
// const attrib = reader.GetAttrib();
|
||||||
|
//
|
||||||
|
// const indices_t = reader.GetShapes().at(0).mesh.indices;
|
||||||
|
// const indices = ArrayList(c_uint)(indices_t.size());
|
||||||
|
//
|
||||||
|
// const vertices = ArrayList()(3*indices_t.size());
|
||||||
|
// const normals = ArrayList()(3*indices_t.size());
|
||||||
|
// const texcoords = ArrayList()(2*indices_t.size());
|
||||||
|
//
|
||||||
|
// for (int i = 0; i < indices_t.size(); i++) {
|
||||||
|
// const vertex_data = indices_t[i];
|
||||||
|
// vertices[3*i] = attrib.vertices[3*vertex_data.vertex_index];
|
||||||
|
// vertices[3*i+1] = attrib.vertices[3*vertex_data.vertex_index + 1];
|
||||||
|
// vertices[3*i+2] = attrib.vertices[3*vertex_data.vertex_index + 2];
|
||||||
|
//
|
||||||
|
// normals[3*i] = attrib.normals[3*vertex_data.normal_index];
|
||||||
|
// normals[3*i+1] = attrib.normals[3*vertex_data.normal_index + 1];
|
||||||
|
// normals[3*i+2] = attrib.normals[3*vertex_data.normal_index + 2];
|
||||||
|
//
|
||||||
|
// texcoords[2*i] = attrib.texcoords[2*vertex_data.texcoord_index];
|
||||||
|
// texcoords[2*i+1] = attrib.texcoords[2*vertex_data.texcoord_index + 1];
|
||||||
|
//
|
||||||
|
// indices[i] = i;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// num_indices = indices_t.size();
|
||||||
|
// glGenVertexArrays(1, &vao);
|
||||||
|
// glGenBuffers(1, &vbo_xyz);
|
||||||
|
// glGenBuffers(1, &vbo_uv);
|
||||||
|
// glGenBuffers(1, &vbo_norm);
|
||||||
|
// //glGenBuffers(1, &ebo);
|
||||||
|
//
|
||||||
|
// glBindVertexArray(vao);
|
||||||
|
//
|
||||||
|
// glBindBuffer(GL_ARRAY_BUFFER, vbo_xyz);
|
||||||
|
// glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
|
||||||
|
// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||||
|
// glEnableVertexAttribArray(0);
|
||||||
|
//
|
||||||
|
// glBindBuffer(GL_ARRAY_BUFFER, vbo_uv);
|
||||||
|
// glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(float), texcoords.data(), GL_STATIC_DRAW);
|
||||||
|
// glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
|
||||||
|
// glEnableVertexAttribArray(1);
|
||||||
|
//
|
||||||
|
// glBindBuffer(GL_ARRAY_BUFFER, vbo_norm);
|
||||||
|
// glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), normals.data(), GL_STATIC_DRAW);
|
||||||
|
// glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||||
|
// glEnableVertexAttribArray(2);
|
||||||
|
//
|
||||||
|
// //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
|
// //glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
|
||||||
|
// }
|
||||||
|
};
|
||||||
56
src/gfx/Shader.zig
Normal file
56
src/gfx/Shader.zig
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const c = @import("../c.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const ShaderType = enum(u32) {
|
||||||
|
fragment = c.GL_FRAGMENT_SHADER,
|
||||||
|
vertex = c.GL_VERTEX_SHADER,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn create_shader(file_path: []const u8, shader_type: ShaderType, info_log: *[]const u8, allocator: *std.mem.Allocator) c_uint {
|
||||||
|
const file = try std.fs.openFileAbsolute(file_path);
|
||||||
|
|
||||||
|
const file_reader = file.reader(file);
|
||||||
|
const shader_code = std.ArrayList(u8);
|
||||||
|
shader_code.initCapacity(allocator, 1024);
|
||||||
|
defer allocator.free(shader_code);
|
||||||
|
|
||||||
|
file_reader.readAllArrayList(shader_code, 1024 * 1024);
|
||||||
|
|
||||||
|
const vertex_shader = c.glCreateShader(shader_type);
|
||||||
|
c.glShaderSource(vertex_shader, 1, &shader_code.items, c.NULL);
|
||||||
|
c.glCompileShader(vertex_shader);
|
||||||
|
const success: i32 = undefined;
|
||||||
|
c.glGetShaderiv(vertex_shader, c.GL_COMPILE_STATUS, &success);
|
||||||
|
if (success != 0) {
|
||||||
|
c.glGetShaderInfoLog(vertex_shader, 512, c.NULL, info_log);
|
||||||
|
const shader_type_name = if (shader_type == ShaderType.fragment) "FRAGMENT" else "VERTEX";
|
||||||
|
std.debug.print("ERROR::SHADER::{}::COMPILATION_FAILED\n{}\n", .{ shader_type_name, info_log });
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertex_shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Shader = struct {
|
||||||
|
prog_id: c_uint,
|
||||||
|
|
||||||
|
pub fn init(self: Shader, vertex_path: *[]const u8, fragment_path: *[]const u8, allocator: *std.mem.Allocator) void {
|
||||||
|
const info_log = [512]u8{};
|
||||||
|
const vertex_shader = create_shader(vertex_path, ShaderType.vertex, &info_log, allocator);
|
||||||
|
const fragment_shader = create_shader(fragment_path, ShaderType.fragment, &info_log, allocator);
|
||||||
|
|
||||||
|
self.prog_id = c.glCreateProgram();
|
||||||
|
c.glAttachShader(self.prog_id, vertex_shader);
|
||||||
|
c.glAttachShader(self.prog_id, fragment_shader);
|
||||||
|
c.glLinkProgram(self.prog_id);
|
||||||
|
|
||||||
|
const success: c_uint = undefined;
|
||||||
|
c.glGetProgramiv(self.prog_id, c.GL_LINK_STATUS, &success);
|
||||||
|
if (!success) {
|
||||||
|
c.glGetProgramInfoLog(self.prog_id, 512, c.NULL, &info_log);
|
||||||
|
std.debug.print("ERROR::SHADER::PROGRAM::LINK_FAILED\n{}\n", .{info_log});
|
||||||
|
}
|
||||||
|
|
||||||
|
c.glDeleteShader(vertex_shader);
|
||||||
|
c.glDeleteShader(fragment_shader);
|
||||||
|
}
|
||||||
|
};
|
||||||
57
src/gfx/djleddaGeom.zig
Normal file
57
src/gfx/djleddaGeom.zig
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Buffer layout:
|
||||||
|
// X, Y, Z, U, V
|
||||||
|
|
||||||
|
pub const Shape = struct {
|
||||||
|
indices: []c_uint,
|
||||||
|
uv: []f32,
|
||||||
|
xyz: []f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const triangle_vertices = []f32{
|
||||||
|
-0.5, -0.5, 0.0, 1.0, 1.0,
|
||||||
|
0.5, -0.5, 0.0, 0.5, 0.5,
|
||||||
|
0.0, 0.5, 0.0, 0.0, 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const triangle_indices = []c_uint{ 0, 1, 2 };
|
||||||
|
|
||||||
|
const cube_vertices = []f32{ -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, -0.5, 0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, -0.5, 1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0 };
|
||||||
|
|
||||||
|
const cube_indices = []c_uint{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
|
||||||
|
|
||||||
|
const square_xyz = []f32{
|
||||||
|
0.5, 0.5, 0.0,
|
||||||
|
0.5, -0.5, 0.0,
|
||||||
|
-0.5, -0.5, 0.0,
|
||||||
|
-0.5, 0.5, 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const square_uv = []f32{
|
||||||
|
1.0, 1.0,
|
||||||
|
1.0, 0.0,
|
||||||
|
0.0, 0.0,
|
||||||
|
0.0, 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const square_indices = []c_uint{
|
||||||
|
0, 1, 3,
|
||||||
|
1, 2, 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TRIANGLE = Shape{
|
||||||
|
.indices = triangle_indices,
|
||||||
|
.uv = triangle_vertices,
|
||||||
|
.xyz = triangle_vertices,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SQUARE = Shape{
|
||||||
|
.indices = square_indices,
|
||||||
|
.uv = square_uv,
|
||||||
|
.xyz = square_xyz,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CUBE = Shape{
|
||||||
|
.indices = cube_indices,
|
||||||
|
.uv = triangle_vertices,
|
||||||
|
.xyz = triangle_vertices,
|
||||||
|
};
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -10,6 +9,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "glad/glad.h"
|
#include "glad/glad.h"
|
||||||
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|||||||
402
src/main.zig
Normal file
402
src/main.zig
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const c = @import("c.zig");
|
||||||
|
const zm = @import("zm");
|
||||||
|
|
||||||
|
const Mesh = @import("gfx/Mesh.zig").Mesh;
|
||||||
|
|
||||||
|
const ArrayList = std.ArrayList;
|
||||||
|
|
||||||
|
fn print_mat(matrix: *const zm.Mat) void {
|
||||||
|
std.debug.print("{}, {}, {}, {}\n", .{ matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3] });
|
||||||
|
std.debug.print("{}, {}, {}, {}\n", .{ matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3] });
|
||||||
|
std.debug.print("{}, {}, {}, {}\n", .{ matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3] });
|
||||||
|
std.debug.print("{}, {}, {}, {}\n", .{ matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3] });
|
||||||
|
}
|
||||||
|
|
||||||
|
const Camera = struct {
|
||||||
|
view: zm.Mat = .{ zm.f32x4s(0.0), zm.f32x4s(0.0), zm.f32x4s(0.0), zm.f32x4s(0.0) },
|
||||||
|
proj: zm.Mat,
|
||||||
|
pos: zm.Vec = zm.f32x4s(0.0),
|
||||||
|
up: zm.Vec = zm.f32x4s(0.0),
|
||||||
|
target: zm.Vec,
|
||||||
|
|
||||||
|
pub fn init(self: Camera, aspect_ratio: f32) void {
|
||||||
|
self.proj = zm.perspectiveFovRh(std.math.degreesToRadians(45.0), aspect_ratio, 0.1, 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(aspect_ratio: f32) Camera {
|
||||||
|
const cam = Camera{};
|
||||||
|
init(cam, aspect_ratio);
|
||||||
|
return cam;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn look_at(self: Camera, x: f32, y: f32, z: f32) void {
|
||||||
|
self.target = zm.f32x4(x, y, z, 0.0);
|
||||||
|
self.view = zm.lookAtRh(self.pos, self.target, self.up);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_up(self: Camera, up_x: f32, up_y: f32, up_z: f32) void {
|
||||||
|
self.up = zm.f32x4(up_x, up_y, up_z, 0.0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const GlobalAppState = struct {
|
||||||
|
current_polycube: i32,
|
||||||
|
last_polycube_visible: i32,
|
||||||
|
active_shader: ?*Shader,
|
||||||
|
polycubes: ArrayList(Polycube),
|
||||||
|
};
|
||||||
|
|
||||||
|
const app_state: GlobalAppState = .{};
|
||||||
|
|
||||||
|
const WindowDims = struct {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Entity = struct {
|
||||||
|
mesh: *Mesh,
|
||||||
|
tex: *Texture,
|
||||||
|
visible: bool,
|
||||||
|
scene_graph_node: i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SceneGraphNode = struct {
|
||||||
|
local: zm.Mat,
|
||||||
|
world: zm.Mat,
|
||||||
|
translation: zm.Vec,
|
||||||
|
rotation: zm.Quat,
|
||||||
|
scale: zm.Vec,
|
||||||
|
children: ArrayList(i32),
|
||||||
|
entity: ?i32,
|
||||||
|
|
||||||
|
pub fn reset(self: SceneGraphNode) void {
|
||||||
|
self.scale = zm.f32x4(1.0, 1.0, 1.0, 0.0);
|
||||||
|
self.translation = zm.f32x4s(0.0);
|
||||||
|
self.rotation = zm.f32x4s(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(self: SceneGraphNode) void {
|
||||||
|
self.reset();
|
||||||
|
self.local = zm.identity();
|
||||||
|
self.world = self.local;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_local(self: SceneGraphNode) void {
|
||||||
|
const scaling = zm.scaling(self.scale);
|
||||||
|
const translation = zm.translation(self.translation);
|
||||||
|
const rotation = zm.quatToMat(self.rotation);
|
||||||
|
self.local = zm.mul(zm.mul(translation, rotation), scaling);
|
||||||
|
self.local = scaling(
|
||||||
|
zm.translate(
|
||||||
|
zm.identity(),
|
||||||
|
self.translation
|
||||||
|
) * toMat4(self.rotation),
|
||||||
|
self.scale
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const Polycube = struct {
|
||||||
|
graph_node: i32,
|
||||||
|
color: zm.Vec,
|
||||||
|
|
||||||
|
pub fn show(self: Polycube) void {
|
||||||
|
const node = get_scene_graph_node(self.graph_node);
|
||||||
|
for (node.children.items) |child_id| {
|
||||||
|
const child_node = get_scene_graph_node(child_id);
|
||||||
|
if (child_node.entity) |entity_id| {
|
||||||
|
get_entity(entity_id).visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hide(self: Polycube) void {
|
||||||
|
const node = get_scene_graph_node(self.graph_node);
|
||||||
|
for (node.children.items) |child_id| {
|
||||||
|
const child_node = get_scene_graph_node(child_id);
|
||||||
|
if (child_node.entity) |entity_id| {
|
||||||
|
get_entity(entity_id).visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_centre(self: Polycube) zm.Vec {
|
||||||
|
const centre = zm.Vec(0.0);
|
||||||
|
for (get_scene_graph_node(self.graph_node).children.items) |child_id| {
|
||||||
|
centre += get_scene_graph_node(child_id).translation;
|
||||||
|
}
|
||||||
|
centre /= get_scene_graph_node(self.graph_node).children.size();
|
||||||
|
return centre;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Frame = struct {
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
cam: *Camera,
|
||||||
|
|
||||||
|
pub fn new(camera: *Camera, width: i32, height: i32) Frame {
|
||||||
|
const frame = Frame{};
|
||||||
|
camera.init(@as(f32, width) / @as(f32, height));
|
||||||
|
frame.cam = camera;
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn framebuffer_size_callback(width: i32, height: i32) void {
|
||||||
|
c.glViewport(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_window_and_gl(window_dims: *WindowDims) ?*c.GLFWwindow {
|
||||||
|
c.glfwInit();
|
||||||
|
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||||
|
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 6);
|
||||||
|
c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
const window = c.glfwCreateWindow(window_dims.width, window_dims.height, "Somaesque", c.NULL, c.NULL);
|
||||||
|
if (window == c.NULL) {
|
||||||
|
std.debug.print("Failed to create GLFW window");
|
||||||
|
c.glfwTerminate();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
c.glfwMakeContextCurrent(window);
|
||||||
|
|
||||||
|
if (!c.gladLoadGLLoader(@as(c.GLADloadproc, c.glfwGetProcAddress))) {
|
||||||
|
std.debug.print("Failed to initialize GLAD");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.glViewport(0, 0, 800, 600);
|
||||||
|
c.glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||||
|
c.glEnable(c.GL_DEPTH_TEST);
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gl_update_viewport(window_dims: *WindowDims, frame: *Frame) void {
|
||||||
|
c.glViewport(frame.x, window_dims.height - frame.y - frame.height, frame.width, frame.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cube_mesh = Mesh{};
|
||||||
|
const wall_tex = Texture{};
|
||||||
|
const entities = ArrayList(Entity);
|
||||||
|
const scene_graph_nodes = ArrayList(SceneGraphNode);
|
||||||
|
|
||||||
|
fn process_input(window: *c.GLFWwindow) void {
|
||||||
|
const static = struct {
|
||||||
|
wireframe: bool = false,
|
||||||
|
last_frame_state_press_enter: bool = false,
|
||||||
|
last_frame_state_press: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (c.glfwGetKey(window, c.GLFW_KEY_ESCAPE) == c.GLFW_PRESS) {
|
||||||
|
c.glfwSetWindowShouldClose(window, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.glfwGetKey(window, c.GLFW_KEY_SPACE) == c.GLFW_PRESS and !static.last_frame_state_press) {
|
||||||
|
c.glPolygonMode(c.GL_FRONT_AND_BACK, if (!static.wireframe) c.GL_LINE else c.GL_FILL);
|
||||||
|
static.wireframe = !static.wireframe;
|
||||||
|
static.last_frame_state_press = true;
|
||||||
|
} else if (c.glfwGetKey(window, c.GLFW_KEY_SPACE) == c.GLFW_RELEASE) {
|
||||||
|
static.last_frame_state_press = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.glfwGetKey(window, c.GLFW_KEY_ENTER) == c.GLFW_PRESS and !static.last_frame_state_press_enter) {
|
||||||
|
if (app_state.current_polycube == 6) {
|
||||||
|
app_state.current_polycube = 0;
|
||||||
|
} else {
|
||||||
|
app_state.current_polycube += 1;
|
||||||
|
}
|
||||||
|
static.last_frame_state_press_enter = true;
|
||||||
|
} else if (c.glfwGetKey(window, c.GLFW_KEY_ENTER) == c.GLFW_RELEASE) {
|
||||||
|
static.last_frame_state_press_enter = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn new_entity() i32 {
|
||||||
|
entities.append(.{});
|
||||||
|
scene_graph_nodes.append(.{});
|
||||||
|
entities.items[entities.items.len - 1].scene_graph_node = scene_graph_nodes.items.len;
|
||||||
|
scene_graph_nodes.items[scene_graph_nodes.items.len - 1].entity = entities.items.len;
|
||||||
|
return entities.items.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_entity(id: i32) ?*Entity {
|
||||||
|
if (entities.items[id - 1]) {
|
||||||
|
return &entities.items[id - 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_scene_graph_node(id: i32) *SceneGraphNode {
|
||||||
|
if (scene_graph_nodes.items[id - 1]) {
|
||||||
|
return &scene_graph_nodes.items[id - 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_graph_node() i32 {
|
||||||
|
scene_graph_nodes.append(.{});
|
||||||
|
return scene_graph_nodes.items.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_entity(entity: *Entity) void {
|
||||||
|
const modelUniformLoc = c.glGetUniformLocation(app_state.active_shader.prog_id, "model");
|
||||||
|
c.glUniformMatrix4fv(modelUniformLoc, 1, c.GL_FALSE, &get_scene_graph_node(entity.scene_graph_node).world);
|
||||||
|
c.glBindTexture(c.GL_TEXTURE_2D, entity.tex.tex_id);
|
||||||
|
c.glBindVertexArray(entity.mesh.vao);
|
||||||
|
c.glDrawArrays(c.GL_TRIANGLES, 0, entity.mesh.num_indices);
|
||||||
|
//c.glDrawElements(c.GL_TRIANGLES, entity.mesh.num_indices, c.GL_UNSIGNED_INT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_polycube_from_repr(repr: *Voxel.Space) Polycube {
|
||||||
|
const polycube_id = new_graph_node();
|
||||||
|
get_scene_graph_node(polycube_id).init();
|
||||||
|
var x: usize = 1;
|
||||||
|
var y: usize = 1;
|
||||||
|
var z: usize = 1;
|
||||||
|
while (x < repr.dim_x) : (x += 1) {
|
||||||
|
while (y < repr.dim_y) : (y += 1) {
|
||||||
|
while (z < repr.dim_z) : (z += 1) {
|
||||||
|
if (Voxel.filledAt(repr, x, y, z)) {
|
||||||
|
const polycube_segment = get_entity(new_entity());
|
||||||
|
polycube_segment.mesh = &cube_mesh;
|
||||||
|
polycube_segment.tex = &wall_tex;
|
||||||
|
const graph_node = get_scene_graph_node(polycube_segment.scene_graph_node);
|
||||||
|
graph_node.init();
|
||||||
|
graph_node.translation = zm.f32x4(
|
||||||
|
-((repr.dim_z - 1)/2.0) + z,
|
||||||
|
((repr.dim_x - 1)/2.0) - x,
|
||||||
|
-((repr.dim_y - 1)/2.0) + y,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
graph_node.update_local();
|
||||||
|
get_scene_graph_node(polycube_id).children.append(polycube_segment.scene_graph_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = Polycube{
|
||||||
|
.graph_node = polycube_id,
|
||||||
|
.color = zm.f32x4s(1.0),
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recalculate_scene_graph(top: *SceneGraphNode) void {
|
||||||
|
if (top.children.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (top.children.items) |child_id| {
|
||||||
|
const graph_node = get_scene_graph_node(child_id);
|
||||||
|
graph_node.update_local();
|
||||||
|
graph_node.world = zm.mul(top.world, graph_node.local);
|
||||||
|
recalculate_scene_graph(graph_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() void {
|
||||||
|
const window_dims = WindowDims{ 800, 600 };
|
||||||
|
const window = init_window_and_gl(&window_dims);
|
||||||
|
if (window == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
app_state = GlobalAppState{
|
||||||
|
.current_polycube=0,
|
||||||
|
.last_polycube_visible=6,
|
||||||
|
.active_shader=null,
|
||||||
|
.polycubes={},
|
||||||
|
};
|
||||||
|
|
||||||
|
const phong_shader = Shader{};
|
||||||
|
phong_shader.init("../assets/shaders/phong-solid.vertex.glsl", "../assets/shaders/phong-solid.fragment.glsl");
|
||||||
|
app_state.active_shader = &phong_shader;
|
||||||
|
|
||||||
|
cube_mesh.init("../assets/models/c000000.obj");
|
||||||
|
wall_tex.init("../assets/textures/brick-wall.jpg");
|
||||||
|
|
||||||
|
const little_frame = Frame{ .width=80, .height=60, .x=20, .y=20 };
|
||||||
|
const big_frame = Frame{ .width=800, .height=600, .x=0, .y=0 };
|
||||||
|
const main_cam = Camera{};
|
||||||
|
const other_cam = Camera{};
|
||||||
|
little_frame.init(&other_cam);
|
||||||
|
big_frame.init(&main_cam);
|
||||||
|
const frames = [_]*Frame{ &big_frame, &little_frame };
|
||||||
|
|
||||||
|
const root_node = SceneGraphNode{};
|
||||||
|
root_node.init();
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < SomaSolve.STD_SOMA.items.len) : (i += 1) {
|
||||||
|
const voxel_space = voxel.Space{ SomaSolve.STD_SOMA[i], 3, 3, 3 };
|
||||||
|
voxel.cullEmptySpace(&voxel_space);
|
||||||
|
const polycube = create_polycube_from_repr(&voxel_space);
|
||||||
|
polycube.color = color.color_from_index(i);
|
||||||
|
app_state.polycubes.append(polycube);
|
||||||
|
root_node.children.append(app_state.polycubes.items[app_state.polycubes.items.len - 1].graph_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
main_cam.pos = zm.f32x4(4.0, 4.0, 4.0, 0.0);
|
||||||
|
main_cam.look_at(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
const light_pos = zm.f32x4(6.0, 6.0, 6.0, 0.0);
|
||||||
|
|
||||||
|
c.glUseProgram(app_state.active_shader.prog_id);
|
||||||
|
const view_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "view");
|
||||||
|
const proj_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "projection");
|
||||||
|
const light_pos_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "light_pos");
|
||||||
|
c.glUniform3fv(light_pos_loc, 1, &light_pos);
|
||||||
|
c.glUniformMatrix4fv(proj_loc, 1, GL_FALSE, &main_cam.proj);
|
||||||
|
c.glUniformMatrix4fv(view_loc, 1, GL_FALSE, &main_cam.view);
|
||||||
|
|
||||||
|
var last_frame = c.glfwGetTime();
|
||||||
|
var time_delta = 1.0/60.0;
|
||||||
|
while (!c.glfwWindowShouldClose(window)) {
|
||||||
|
time_delta = c.glfwGetTime() - last_frame;
|
||||||
|
process_input(window);
|
||||||
|
|
||||||
|
if (app_state.last_polycube_visible != app_state.current_polycube) {
|
||||||
|
app_state.polycubes[app_state.last_polycube_visible].hide();
|
||||||
|
app_state.polycubes[app_state.current_polycube].show();
|
||||||
|
app_state.last_polycube_visible = app_state.current_polycube;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
c.glClear(c.GL_DEPTH_BUFFER_BIT | c.GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
c.gl_update_viewport(&window_dims, &big_frame);
|
||||||
|
const current_polycube = &app_state.polycubes[app_state.current_polycube];
|
||||||
|
c.get_scene_graph_node(current_polycube.graph_node).rotation = zm.quatFromRollPitchYaw(0.0, c.glfwGetTime() / 2.0, 0.0);
|
||||||
|
|
||||||
|
c.glBindVertexArray(cube_mesh.vao);
|
||||||
|
//glBindTexture(GL_TEXTURE_2D, entity.tex->tex_id);
|
||||||
|
recalculate_scene_graph(&root_node);
|
||||||
|
const model_uniform_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "model");
|
||||||
|
const solid_color_loc = c.glGetUniformLocation(app_state.active_shader.prog_id, "solid_color");
|
||||||
|
c.glUniform3fv(solid_color_loc, 1, ¤t_polycube.color);
|
||||||
|
while (entities.items) |entity| {
|
||||||
|
if (entity.visible) {
|
||||||
|
c.glUniformMatrix4fv(model_uniform_loc, 1, c.GL_FALSE, &get_scene_graph_node(entity.scene_graph_node).world);
|
||||||
|
c.glDrawArrays(c.GL_TRIANGLES, 0, entity.mesh.num_indices);
|
||||||
|
//glDrawElements(GL_TRIANGLES, entity->mesh->num_indices, GL_UNSIGNED_INT, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.glfwSwapBuffers(window);
|
||||||
|
c.glfwPollEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
c.glfwTerminate();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//test "simple test" {
|
||||||
|
// var list = std.ArrayList(i32).init(std.testing.allocator);
|
||||||
|
// defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
|
||||||
|
// try list.append(42);
|
||||||
|
// try std.testing.expectEqual(@as(i32, 42), list.pop());
|
||||||
|
//}
|
||||||
2
vendor/loaders/stb_image.cpp
vendored
2
vendor/loaders/stb_image.cpp
vendored
@@ -1,2 +0,0 @@
|
|||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include "stb_image.h"
|
|
||||||
2
vendor/loaders/tinyobj.cpp
vendored
2
vendor/loaders/tinyobj.cpp
vendored
@@ -1,2 +0,0 @@
|
|||||||
#define TINYOBJLOADER_IMPLEMENTATION
|
|
||||||
#include "tinyobj.h"
|
|
||||||
3455
vendor/loaders/tinyobj.h
vendored
3455
vendor/loaders/tinyobj.h
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user