133 lines
4.5 KiB
Markdown
133 lines
4.5 KiB
Markdown
# zmath v0.10.0 - 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` into a subdirectory of your project and add the following to your `build.zig.zon` .dependencies:
|
|
```zig
|
|
.zmath = .{ .path = "libs/zmath" },
|
|
```
|
|
|
|
Then in your `build.zig` add:
|
|
|
|
```zig
|
|
pub fn build(b: *std.Build) void {
|
|
const exe = b.addExecutable(.{ ... });
|
|
|
|
const zmath = b.dependency("zmath", .{});
|
|
exe.root_module.addImport("zmath", zmath.module("root"));
|
|
}
|
|
```
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
```
|