Trying to improve collisions and failing

This commit is contained in:
2025-12-19 01:36:50 +01:00
parent 04ae797196
commit 71015874a9
10 changed files with 805 additions and 254 deletions

View File

@@ -1,6 +1,7 @@
const Game = @This();
const std = @import("std");
const c = @import("const.zig");
const glfw = @import("zglfw");
const math = @import("math.zig");
const shaders = @import("shaders.zig");
@@ -23,6 +24,7 @@ const Textures = @import("assets/Textures.zig");
const Vector2 = math.Vector2;
const Vector2x8 = math.Vector2x8;
const Vector3 = math.Vector3;
const Vector3Int = math.Vector3Int;
allocator: std.mem.Allocator,
engine: *Engine,
@@ -591,7 +593,10 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
}
}
const player_position = Vector3.init(0, 0, worldgen.heightF(world_seed, .zero) + 0.5);
const player_position_vx = Vector3Int.init(0, 0, worldgen.heightI(world_seed, .zero) + 1);
const player_position_sv = player_position_vx
.mulScalar(c.sv_per_vx)
.add(.init(c.sv_per_vx / 2, c.sv_per_vx / 2, 0));
var chunk_it = chunks.iterator();
while (chunk_it.next()) |entry| {
@@ -655,7 +660,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
.chunks = chunks,
.skybox = skybox,
.player = .init(player_position, 0, 0),
.player = .init(player_position_sv, 0, 0),
};
}
@@ -757,7 +762,10 @@ fn render(self: *Game) !void {
@floatFromInt(extent.height),
);
const camera_position = self.player.position.add(.init(0, 0, Player.camera_height));
const camera_position = self.player.position_sv
.asVector3()
.divScalar(c.sv_per_vx)
.add(.init(0, 0, Player.camera_height_vx));
const camera_rotation = Quaternion.mulQuaternion(
.initRotationXY(self.player.yaw_rad),
.initRotationYZ(self.player.pitch_rad),

View File

@@ -1,13 +1,17 @@
const Player = @This();
const std = @import("std");
const c = @import("const.zig");
const glfw = @import("zglfw");
const math = @import("math.zig");
const Blocks = @import("assets/Blocks.zig");
const Chunk = @import("assets/Chunk.zig");
const Iterator2 = math.Iterator2;
const Vector2 = math.Vector2;
const Vector3 = math.Vector3;
const Vector2Int = math.Vector2Int;
const Vector3Int = math.Vector3Int;
const AxisState = enum {
none,
@@ -16,13 +20,29 @@ const AxisState = enum {
both_positive,
both_negative,
pub fn getComponent(self: AxisState) f32 {
pub fn getComponentFloat(self: AxisState) f32 {
return switch (self) {
.none => 0,
.positive, .both_positive => 1,
.negative, .both_negative => -1,
};
}
pub fn getComponentInt(self: AxisState) i32 {
return switch (self) {
.none => 0,
.positive, .both_positive => 1,
.negative, .both_negative => -1,
};
}
pub fn getComponentIntFrac(self: AxisState) i32 {
return switch (self) {
.none => 0,
.positive, .both_positive => std.math.maxInt(i32),
.negative, .both_negative => -std.math.maxInt(i32),
};
}
};
const ButtonState = struct {
@@ -80,8 +100,16 @@ const Axis = struct {
}
}
pub fn getComponent(self: Axis) f32 {
return self.state.getComponent();
pub fn getComponentFloat(self: Axis) f32 {
return self.state.getComponentFloat();
}
pub fn getComponentInt(self: Axis) i32 {
return self.state.getComponentInt();
}
pub fn getComponentIntFrac(self: Axis) i32 {
return self.state.getComponentIntFrac();
}
};
@@ -128,16 +156,16 @@ const Button = struct {
const MovementState = union(enum) {
ground: struct {
horizontal_velocity: Vector2,
horizontal_velocity_sv: Vector2Int,
},
air: struct {
horizontal_velocity: Vector2,
vertical_velocity: f32,
horizontal_velocity_sv: Vector2Int,
vertical_velocity_sv: i32,
},
flight: void,
};
position: Vector3,
position_sv: Vector3Int,
pitch_rad: f32,
yaw_rad: f32,
@@ -151,31 +179,30 @@ vertical_fov_deg: f32 = 80,
movement_state: MovementState,
pub const camera_height = 1.62;
pub const camera_height_vx = 1.62;
pub const horizontal_speed = 11.0;
pub const vertical_speed = 7.49;
pub const horizontal_speed_sv = c.sv(11.0);
pub const vertical_speed_sv = c.sv(7.49);
pub const min_pitch_rad = -0.5 * std.math.pi;
pub const max_pitch_rad = 0.5 * std.math.pi;
pub const half_width = 0.4;
pub const height = 1.8;
pub const speed = 5.612;
pub const step_up = 0.53125;
pub const step_down = 0.53125;
pub const jump_velocity = 6.1237245;
pub const gravity = 15;
pub const vertical_velocity_cap = 30;
pub const ground_acceleration = 56.12;
pub const air_acceleration = 56.12;
pub const air_speed_limit = 0.5612;
pub const collision_half_width_sv = c.sv(0.4);
pub const collision_height_sv = c.sv(1.8);
const distance_from_surface = 0.0009765625;
pub const speed_sv = c.sv(5.612);
pub const step_up_sv = c.sv(0.53125);
pub const step_down_sv = c.sv(0.53125);
pub const jump_velocity_sv = c.sv(6.1237245);
pub const gravity_sv = c.sv(15);
pub const vertical_velocity_cap_sv = c.sv(30);
pub const ground_acceleration_sv = c.sv(56.12);
pub const air_acceleration_sv = c.sv(56.12);
pub const air_speed_limit_sv = c.sv(0.5612);
pub fn init(position: Vector3, pitch_rad: f32, yaw_rad: f32) Player {
pub fn init(position_sv: Vector3Int, pitch_rad: f32, yaw_rad: f32) Player {
return .{
.position = position,
.position_sv = position_sv,
.pitch_rad = pitch_rad,
.yaw_rad = yaw_rad,
.movement_state = .flight,
@@ -204,17 +231,17 @@ pub fn update(self: *Player, dt: f32, chunks: *const std.AutoHashMapUnmanaged([3
// --- GATHER INPUTS -------------------------------------------------------
const horizontal_input_vector = blk: {
const horizontal_input_vector_frac = blk: {
var ret = Vector2
.init(self.x_input.getComponent(), self.y_input.getComponent())
.init(self.x_input.getComponentFloat(), self.y_input.getComponentFloat())
.rotate(self.yaw_rad);
const len_squared = ret.lenSquared();
if (len_squared > 1) {
ret = ret.divScalar(@sqrt(len_squared));
}
break :blk ret;
break :blk ret.asVector2IntFrac();
};
const vertical_input_vector = self.z_input.getComponent();
const vertical_input_vector_frac = self.z_input.getComponentIntFrac();
// --- STATE TRANSITIONS ---------------------------------------------------
@@ -223,8 +250,8 @@ pub fn update(self: *Player, dt: f32, chunks: *const std.AutoHashMapUnmanaged([3
if (self.jump_input.isPressed()) {
self.movement_state = .{
.air = .{
.horizontal_velocity = ground.horizontal_velocity,
.vertical_velocity = jump_velocity,
.horizontal_velocity_sv = ground.horizontal_velocity_sv,
.vertical_velocity_sv = jump_velocity_sv,
},
};
}
@@ -238,73 +265,88 @@ pub fn update(self: *Player, dt: f32, chunks: *const std.AutoHashMapUnmanaged([3
.ground => {},
.air => {},
.flight => {
const horizontal_velocity = horizontal_input_vector.mulScalar(horizontal_speed);
const vertical_velocity = vertical_input_vector * vertical_speed;
const horizontal_velocity_sv = horizontal_input_vector_frac.mulScalarFrac(horizontal_speed_sv);
const vertical_velocity_sv = math.mulFrac(vertical_input_vector_frac, vertical_speed_sv);
var horizontal_displacement = horizontal_velocity.mulScalar(dt);
var vertical_displacement = vertical_velocity * dt;
var horizontal_displacement_sv = horizontal_velocity_sv.mulScalarFloat(dt);
var vertical_displacement_sv = math.mulIntFloat(vertical_velocity_sv, dt);
if (vertical_displacement > 0.0) {
if (sweepCastUp(self.position, vertical_displacement, chunks)) |hit| {
vertical_displacement = hit.projected_distance - distance_from_surface;
var position_sv = self.position_sv;
if (vertical_displacement_sv > 0) {
if (sweepCastUp(position_sv, vertical_displacement_sv, chunks)) |hit| {
vertical_displacement_sv -= hit.projected_distance_sv;
}
position_sv = .add(position_sv, .init(0, 0, vertical_displacement_sv));
}
var i: usize = 0;
while (i < 2) : (i += 1) {
if (sweepCastHorizontal(self.position, horizontal_displacement, chunks)) |hit| {
horizontal_displacement = .add(
horizontal_displacement,
hit.normal
.asVector2()
.mulScalar(hit.projected_distance + distance_from_surface),
if (sweepCastHorizontal(position_sv, horizontal_displacement_sv, chunks)) |hit| {
const adjustment = hit.normal_frac
.asVector2Int()
.mulScalarFrac(hit.projected_distance_sv);
std.debug.print("i={} | n={} | d={} | adj={} | disp={}->", .{
i,
hit.normal_frac.vector,
hit.projected_distance_sv,
adjustment.vector,
horizontal_displacement_sv.vector,
});
horizontal_displacement_sv = .add(
horizontal_displacement_sv,
adjustment,
);
std.debug.print("{}\n", .{horizontal_displacement_sv.vector});
} else {
break;
}
}
position_sv = .add(position_sv, horizontal_displacement_sv.asVector3Int(0));
if (vertical_displacement < 0.0) {
if (sweepCastDown(self.position, -vertical_displacement, chunks)) |hit| {
vertical_displacement = -(hit.projected_distance - distance_from_surface);
if (vertical_displacement_sv < 0.0) {
if (sweepCastDown(position_sv, -vertical_displacement_sv, chunks)) |hit| {
vertical_displacement_sv += hit.projected_distance_sv;
}
position_sv = .add(position_sv, .init(0, 0, vertical_displacement_sv));
}
const displacement = horizontal_displacement.asVector3(vertical_displacement);
self.position = .add(self.position, displacement);
self.position_sv = position_sv;
},
}
}
const SweepHit = struct {
normal: Vector3,
projected_distance: f32,
normal_frac: Vector3Int,
projected_distance_sv: i32,
};
fn sweepCastDown(origin: Vector3, distance: f32, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit {
const min_origin = origin.add(.init(-half_width, -half_width, 0.0));
const max_origin = origin.add(.init(half_width, half_width, height));
fn sweepCastDown(origin_sv: Vector3Int, distance_sv: i32, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit {
const min_origin_sv = origin_sv.add(.init(-collision_half_width_sv, -collision_half_width_sv, 0));
const max_origin_sv = origin_sv.add(.init(collision_half_width_sv, collision_half_width_sv, collision_height_sv));
const min_x = blockCoord(.border_up, min_origin.getX());
const min_y = blockCoord(.border_up, min_origin.getY());
const max_x = blockCoord(.border_down, max_origin.getX());
const max_y = blockCoord(.border_down, max_origin.getY());
const min_x_vx = voxelFromSubvoxel(.border_up, min_origin_sv.getX());
const min_y_vx = voxelFromSubvoxel(.border_up, min_origin_sv.getY());
const max_x_vx = voxelFromSubvoxel(.border_down, max_origin_sv.getX());
const max_y_vx = voxelFromSubvoxel(.border_down, max_origin_sv.getY());
const start_z = blockCoord(.next_down_with_border, min_origin.getZ());
const end_z = blockCoord(.border_down, min_origin.getZ() - distance);
const start_z_vx = voxelFromSubvoxel(.next_down, min_origin_sv.getZ());
const end_z_vx = voxelFromSubvoxel(.border_up, min_origin_sv.getZ() - distance_sv);
var z: i32 = start_z;
while (z >= end_z) : (z -= 1) {
const fz = @as(f32, @floatFromInt(z)) + 1.0;
var z_vx: i32 = start_z_vx;
while (z_vx >= end_z_vx) : (z_vx -= 1) {
const z_sv = (z_vx + 1) * c.sv_per_vx;
var it = Iterator2(i32).init(.{
.min = .{ min_x, min_y },
.max = .{ max_x, max_y },
.min = .{ min_x_vx, min_y_vx },
.max = .{ max_x_vx, max_y_vx },
});
while (it.next()) |xy| {
const x, const y = xy;
if (!isSolid(chunks, x, y, z)) continue;
while (it.next()) |xy_vx| {
const x_vx, const y_vx = xy_vx;
if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue;
return .{
.projected_distance = distance - (min_origin.getZ() - fz),
.normal = .unit_z,
.projected_distance_sv = distance_sv - (min_origin_sv.getZ() - z_sv),
.normal_frac = .unit_z_frac,
};
}
}
@@ -312,32 +354,32 @@ fn sweepCastDown(origin: Vector3, distance: f32, chunks: *const std.AutoHashMapU
return null;
}
fn sweepCastUp(origin: Vector3, distance: f32, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit {
const min_origin = origin.add(.init(-half_width, -half_width, 0.0));
const max_origin = origin.add(.init(half_width, half_width, height));
fn sweepCastUp(origin_sv: Vector3Int, distance_sv: i32, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit {
const min_origin_sv = origin_sv.add(.init(-collision_half_width_sv, -collision_half_width_sv, 0));
const max_origin_sv = origin_sv.add(.init(collision_half_width_sv, collision_half_width_sv, collision_height_sv));
const min_x = blockCoord(.border_up, min_origin.getX());
const min_y = blockCoord(.border_up, min_origin.getY());
const max_x = blockCoord(.border_down, max_origin.getX());
const max_y = blockCoord(.border_down, max_origin.getY());
const min_x_vx = voxelFromSubvoxel(.border_up, min_origin_sv.getX());
const min_y_vx = voxelFromSubvoxel(.border_up, min_origin_sv.getY());
const max_x_vx = voxelFromSubvoxel(.border_down, max_origin_sv.getX());
const max_y_vx = voxelFromSubvoxel(.border_down, max_origin_sv.getY());
const start_z = blockCoord(.next_up_with_border, max_origin.getZ());
const end_z = blockCoord(.border_up, max_origin.getZ() + distance);
const start_z_vx = voxelFromSubvoxel(.next_up, max_origin_sv.getZ());
const end_z_vx = voxelFromSubvoxel(.border_up, max_origin_sv.getZ() + distance_sv);
var z: i32 = start_z;
while (z <= end_z) : (z += 1) {
const fz = @as(f32, @floatFromInt(z));
var z_vx: i32 = start_z_vx;
while (z_vx <= end_z_vx) : (z_vx += 1) {
const z_sv = z_vx * c.sv_per_vx;
var it = Iterator2(i32).init(.{
.min = .{ min_x, min_y },
.max = .{ max_x, max_y },
.min = .{ min_x_vx, min_y_vx },
.max = .{ max_x_vx, max_y_vx },
});
while (it.next()) |xy| {
const x, const y = xy;
if (!isSolid(chunks, x, y, z)) continue;
while (it.next()) |xy_vx| {
const x_vx, const y_vx = xy_vx;
if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue;
return .{
.projected_distance = distance - (fz - max_origin.getZ()),
.normal = .negate(.unit_z),
.projected_distance_sv = distance_sv - (z_sv - max_origin_sv.getZ()),
.normal_frac = .unit_nz_frac,
};
}
}
@@ -345,176 +387,178 @@ fn sweepCastUp(origin: Vector3, distance: f32, chunks: *const std.AutoHashMapUnm
return null;
}
fn sweepCastHorizontal(origin: Vector3, ray: Vector2, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit {
const min_origin = origin.add(.init(-half_width, -half_width, 0));
const max_origin = origin.add(.init(half_width, half_width, height));
fn sweepCastHorizontal(origin_sv: Vector3Int, ray_sv: Vector2Int, chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk)) ?SweepHit {
const min_origin_sv = origin_sv.add(.init(-collision_half_width_sv, -collision_half_width_sv, 0));
const max_origin_sv = origin_sv.add(.init(collision_half_width_sv, collision_half_width_sv, collision_height_sv));
const min_z = blockCoord(.border_up, min_origin.getZ());
const max_z = blockCoord(.border_down, max_origin.getZ());
const min_z_vx = voxelFromSubvoxel(.border_up, min_origin_sv.getZ());
const max_z_vx = voxelFromSubvoxel(.border_down, max_origin_sv.getZ());
var closest_hit: ?SweepHit = null;
var closest_hit_distance = std.math.inf(f32);
var hit: ?SweepHit = null;
var hit_distance_squared = std.math.inf(f32);
const dydx = ray.getY() / ray.getX();
const dxdy = ray.getX() / ray.getY();
const fdydx: f32 = @as(f32, @floatFromInt(ray_sv.getY())) / @as(f32, @floatFromInt(ray_sv.getX()));
const fdxdy: f32 = @as(f32, @floatFromInt(ray_sv.getX())) / @as(f32, @floatFromInt(ray_sv.getY()));
// Positive X
if (ray.getX() > 0.0) {
const x0 = max_origin.getX();
const y0 = min_origin.getY();
const y1 = max_origin.getY();
if (ray_sv.getX() > 0) {
const x0_sv = max_origin_sv.getX();
const y0_sv = min_origin_sv.getY();
const y1_sv = max_origin_sv.getY();
const start_x = blockCoord(.next_up_with_border, x0);
const end_x = blockCoord(.border_up, x0 + ray.getX());
const start_x_vx = voxelFromSubvoxel(.next_up, x0_sv);
const end_x_vx = voxelFromSubvoxel(.border_down, x0_sv + ray_sv.getX());
var x: i32 = start_x;
px: while (x <= end_x) : (x += 1) {
const fx = @as(f32, @floatFromInt(x));
const min_y = blockCoord(.border_up, (fx - x0) * dydx + y0);
const max_y = blockCoord(.border_down, (fx - x0) * dydx + y1);
var x_vx: i32 = start_x_vx;
px: while (x_vx <= end_x_vx) : (x_vx += 1) {
const x_sv = x_vx * c.sv_per_vx;
const min_y_vx = voxelFromSubvoxel(.border_up, wideMulDivFloor(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y0_sv);
const max_y_vx = voxelFromSubvoxel(.border_down, wideMulDivCeil(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y1_sv);
var it = Iterator2(i32).init(.{
.min = .{ min_y, min_z },
.max = .{ max_y, max_z },
.min = .{ min_y_vx, min_z_vx },
.max = .{ max_y_vx, max_z_vx },
});
while (it.next()) |yz| {
const y, const z = yz;
if (!isSolid(chunks, x, y, z)) continue;
while (it.next()) |yz_vx| {
const y_vx, const z_vx = yz_vx;
if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue;
const dx = fx - x0;
const dy = dydx * dx;
const distance = Vector2.init(dx, dy).len();
const dx = x_sv - x0_sv;
const fdx: f32 = @floatFromInt(dx);
const fdy: f32 = fdx * fdydx;
if (distance >= closest_hit_distance) {
break :px;
}
closest_hit = .{
.projected_distance = ray.getX() - dx,
.normal = .negate(.unit_x),
hit = .{
.projected_distance_sv = ray_sv.getX() - dx,
.normal_frac = .unit_nx_frac,
};
closest_hit_distance = distance;
hit_distance_squared = fdx * fdx + fdy * fdy;
std.debug.print("+X fdx={d} fdy={d} dist2={d}\n", .{ fdx, fdy, hit_distance_squared });
break :px;
}
}
}
// Negative X
if (ray.getX() < 0.0) {
const x0 = min_origin.getX();
const y0 = min_origin.getY();
const y1 = max_origin.getY();
if (ray_sv.getX() < 0) {
const x0_sv = min_origin_sv.getX();
const y0_sv = min_origin_sv.getY();
const y1_sv = max_origin_sv.getY();
const start_x = blockCoord(.next_down_with_border, x0);
const end_x = blockCoord(.border_down, x0 + ray.getX());
const start_x_vx = voxelFromSubvoxel(.next_down, x0_sv);
const end_x_vx = voxelFromSubvoxel(.border_up, x0_sv + ray_sv.getX());
var x: i32 = start_x;
nx: while (x >= end_x) : (x -= 1) {
const fx = @as(f32, @floatFromInt(x)) + 1.0;
const min_y = blockCoord(.border_up, (fx - x0) * dydx + y0);
const max_y = blockCoord(.border_down, (fx - x0) * dydx + y1);
var x_vx: i32 = start_x_vx;
nx: while (x_vx >= end_x_vx) : (x_vx -= 1) {
const x_sv = (x_vx + 1) * c.sv_per_vx;
const min_y_vx = voxelFromSubvoxel(.border_up, wideMulDivFloor(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y0_sv);
const max_y_vx = voxelFromSubvoxel(.border_down, wideMulDivCeil(x_sv - x0_sv, ray_sv.getY(), ray_sv.getX()) + y1_sv);
var it = Iterator2(i32).init(.{
.min = .{ min_y, min_z },
.max = .{ max_y, max_z },
.min = .{ min_y_vx, min_z_vx },
.max = .{ max_y_vx, max_z_vx },
});
while (it.next()) |yz| {
const y, const z = yz;
if (!isSolid(chunks, x, y, z)) continue;
while (it.next()) |yz_vx| {
const y_vx, const z_vx = yz_vx;
if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue;
const dx = fx - x0;
const dy = dydx * dx;
const distance = Vector2.init(dx, dy).len();
const dx = x_sv - x0_sv;
const fdx: f32 = @floatFromInt(dx);
const fdy: f32 = fdx * fdydx;
if (distance >= closest_hit_distance) {
break :nx;
}
closest_hit = .{
.projected_distance = -(ray.getX() - dx),
.normal = .unit_x,
hit = .{
.projected_distance_sv = -(ray_sv.getX() - dx),
.normal_frac = .unit_x_frac,
};
closest_hit_distance = distance;
hit_distance_squared = fdx * fdx + fdy * fdy;
std.debug.print("-X fdx={d} fdy={d} dist2={d}\n", .{ fdx, fdy, hit_distance_squared });
break :nx;
}
}
}
// Positive Y
if (ray.getY() > 0.0) {
const y0 = max_origin.getY();
const x0 = min_origin.getX();
const x1 = max_origin.getX();
if (ray_sv.getY() > 0) {
const y0_sv = max_origin_sv.getY();
const x0_sv = min_origin_sv.getX();
const x1_sv = max_origin_sv.getX();
const start_y: i32 = @intFromFloat(@ceil(y0));
const end_y: i32 = @intFromFloat(@floor(y0 + ray.getY()));
const start_y_vx = voxelFromSubvoxel(.next_up, y0_sv);
const end_y_vx = voxelFromSubvoxel(.border_down, y0_sv + ray_sv.getY());
var y: i32 = start_y;
py: while (y <= end_y) : (y += 1) {
const fy = @as(f32, @floatFromInt(y));
const min_x: i32 = @intFromFloat(@floor(fy - y0) * dxdy + x0);
const max_x: i32 = @intFromFloat(@ceil((fy - y0) * dxdy + x1 - 1.0));
var y_vx = start_y_vx;
py: while (y_vx <= end_y_vx) : (y_vx += 1) {
const y_sv = y_vx * c.sv_per_vx;
const min_x_vx = voxelFromSubvoxel(.border_up, wideMulDivFloor(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x0_sv);
const max_x_vx = voxelFromSubvoxel(.border_down, wideMulDivCeil(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x1_sv);
var it = Iterator2(i32).init(.{
.min = .{ min_x, min_z },
.max = .{ max_x, max_z },
.min = .{ min_x_vx, min_z_vx },
.max = .{ max_x_vx, max_z_vx },
});
while (it.next()) |xz| {
const x, const z = xz;
if (!isSolid(chunks, x, y, z)) continue;
while (it.next()) |xz_vx| {
const x_vx, const z_vx = xz_vx;
if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue;
const dy = fy - y0;
const dx = dxdy * dy;
const distance = Vector2.init(dx, dy).len();
const dy = y_sv - y0_sv;
const fdy: f32 = @floatFromInt(dy);
const fdx: f32 = fdy * fdxdy;
if (distance >= closest_hit_distance) {
break :py;
const this_hit_distance_squared = fdx * fdx + fdy * fdy;
std.debug.print("+Y fdx={d} fdy={d} dist2={d}\n", .{ fdx, fdy, this_hit_distance_squared });
if (this_hit_distance_squared < hit_distance_squared) {
hit = .{
.projected_distance_sv = ray_sv.getY() - dy,
.normal_frac = .unit_ny_frac,
};
hit_distance_squared = this_hit_distance_squared;
}
closest_hit = .{
.projected_distance = ray.getY() - dy,
.normal = .negate(.unit_y),
};
closest_hit_distance = distance;
break :py;
}
}
}
// Negative Y
if (ray.getY() < 0.0) {
const y0 = min_origin.getY();
const x0 = min_origin.getX();
const x1 = max_origin.getX();
if (ray_sv.getY() < 0.0) {
const y0_sv = min_origin_sv.getY();
const x0_sv = min_origin_sv.getX();
const x1_sv = max_origin_sv.getX();
const start_y = blockCoord(.next_down_with_border, y0);
const end_y = blockCoord(.border_down, y0 + ray.getY());
const start_y_vx = voxelFromSubvoxel(.next_down, y0_sv);
const end_y_vx = voxelFromSubvoxel(.border_up, y0_sv + ray_sv.getY());
var y: i32 = start_y;
py: while (y >= end_y) : (y -= 1) {
const fy = @as(f32, @floatFromInt(y)) + 1.0;
const min_x: i32 = @intFromFloat(@floor(fy - y0) * dxdy + x0);
const max_x: i32 = @intFromFloat(@ceil((fy - y0) * dxdy + x1 - 1.0));
var y_vx = start_y_vx;
ny: while (y_vx >= end_y_vx) : (y_vx -= 1) {
const y_sv = (y_vx + 1) * c.sv_per_vx;
const min_x_vx = voxelFromSubvoxel(.border_up, wideMulDivFloor(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x0_sv);
const max_x_vx = voxelFromSubvoxel(.border_down, wideMulDivCeil(y_sv - y0_sv, ray_sv.getX(), ray_sv.getY()) + x1_sv);
var it = Iterator2(i32).init(.{
.min = .{ min_x, min_z },
.max = .{ max_x, max_z },
.min = .{ min_x_vx, min_z_vx },
.max = .{ max_x_vx, max_z_vx },
});
while (it.next()) |xz| {
const x, const z = xz;
if (!isSolid(chunks, x, y, z)) continue;
while (it.next()) |xz_vx| {
const x_vx, const z_vx = xz_vx;
if (!isSolid(chunks, .init(x_vx, y_vx, z_vx))) continue;
const dy = fy - y0;
const dx = dxdy * dy;
const distance = Vector2.init(dx, dy).len();
const dy = y_sv - y0_sv;
const fdy: f32 = @floatFromInt(dy);
const fdx: f32 = fdy * fdxdy;
if (distance >= closest_hit_distance) {
break :py;
const this_hit_distance_squared = fdx * fdx + fdy * fdy;
std.debug.print("-Y fdx={d} fdy={d} dist2={d}\n", .{ fdx, fdy, this_hit_distance_squared });
if (this_hit_distance_squared < hit_distance_squared) {
hit = .{
.projected_distance_sv = -(ray_sv.getY() - dy),
.normal_frac = .unit_y_frac,
};
hit_distance_squared = this_hit_distance_squared;
}
closest_hit = .{
.projected_distance = -(ray.getY() - dy),
.normal = .unit_y,
};
closest_hit_distance = distance;
break :ny;
}
}
}
return closest_hit;
return hit;
}
fn resetAllButtons(self: *Player) void {
@@ -525,50 +569,52 @@ fn resetAllButtons(self: *Player) void {
}
}
fn isSolid(chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk), x: i32, y: i32, z: i32) bool {
const chunk_x = std.math.cast(i16, @divFloor(x, Chunk.chunk_size)) orelse return true;
const chunk_y = std.math.cast(i16, @divFloor(y, Chunk.chunk_size)) orelse return true;
const chunk_z = std.math.cast(i16, @divFloor(z, Chunk.chunk_size)) orelse return true;
if (chunks.get(.{ chunk_x, chunk_y, chunk_z })) |chunk| {
const local_x: u4 = @intCast(@mod(x, Chunk.chunk_size));
const local_y: u4 = @intCast(@mod(y, Chunk.chunk_size));
const local_z: u4 = @intCast(@mod(z, Chunk.chunk_size));
return chunk.blocks[local_z][local_y][local_x] != .air;
} else {
return false;
fn getVoxelAt(chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk), vx: Vector3Int) ?Blocks.Id {
const min_ck = Vector3Int.initScalar(std.math.minInt(i16));
const max_ck = Vector3Int.initScalar(std.math.maxInt(i16));
const ck = vx.divScalar(c.vx_per_ck);
if (@reduce(.Or, (ck.vector < min_ck.vector) | (ck.vector > max_ck.vector))) {
return null;
}
if (chunks.get(.{
@intCast(ck.getX()),
@intCast(ck.getY()),
@intCast(ck.getZ()),
})) |chunk| {
const ckvx = vx.modScalar(c.vx_per_ck);
return chunk.blocks[@intCast(ckvx.getZ())][@intCast(ckvx.getY())][@intCast(ckvx.getX())];
} else {
return .air;
}
}
fn isSolid(chunks: *const std.AutoHashMapUnmanaged([3]i16, Chunk), vx: Vector3Int) bool {
const maybe_id = getVoxelAt(chunks, vx);
// NOTE `null` is considered solid, as it's out of bounds.
return maybe_id != .air;
}
const RoundingMode = enum {
border_down,
border_up,
next_down_with_border,
next_down_without_border,
next_up_with_border,
next_up_without_border,
next_down,
next_up,
};
inline fn blockCoord(comptime rounding_mode: RoundingMode, coord: f32) i32 {
return switch (rounding_mode) {
.border_down => @intFromFloat(@ceil(coord - 1.0)),
.border_up => @intFromFloat(@floor(coord)),
.next_down_with_border => @intFromFloat(@floor(coord - 1.0)),
.next_down_without_border => @intFromFloat(@ceil(coord - 2.0)),
.next_up_with_border => @intFromFloat(@ceil(coord)),
.next_up_without_border => @intFromFloat(@floor(coord + 1.0)),
};
inline fn wideMulDivFloor(a: i32, mul: i32, div: i32) i32 {
return @intCast(@divFloor(@as(i64, a) * @as(i64, mul), div));
}
inline fn blockCoord2(comptime rounding_mode: RoundingMode, coord: Vector2) [2]i32 {
return switch (rounding_mode) {
.border_down => @as(Vector2.Vector, @intFromFloat(@ceil(coord.sub(.one).vector))),
.border_up => @as(Vector2.Vector, @intFromFloat(@floor(coord.vector))),
};
inline fn wideMulDivCeil(a: i32, mul: i32, div: i32) i32 {
return @intCast(@divFloor(@as(i64, a) * @as(i64, mul) + @as(i64, div) - 1, div));
}
inline fn blockCoord3(comptime rounding_mode: RoundingMode, coord: Vector3) [3]i32 {
inline fn voxelFromSubvoxel(comptime rounding_mode: RoundingMode, subvoxel: i32) i32 {
return switch (rounding_mode) {
.border_down => @as(Vector3.Vector, @intFromFloat(@ceil(coord.sub(.one).vector))),
.border_up => @as(Vector3.Vector, @intFromFloat(@floor(coord.vector))),
.border_down => @divFloor(subvoxel - 1, c.sv_per_vx),
.border_up => @divFloor(subvoxel, c.sv_per_vx),
.next_down => @divFloor(subvoxel - c.sv_per_vx, c.sv_per_vx),
.next_up => @divFloor(subvoxel + (c.sv_per_vx - 1), c.sv_per_vx),
};
}

View File

@@ -1,6 +1,6 @@
const std = @import("std");
const Vector2 = @import("math.zig").Vector2;
const Vector2Int = @import("math.zig").Vector2Int;
pub const app_name = "voxel-game";
pub const app_version_string = @import("config").version;
@@ -11,12 +11,33 @@ pub const max_frametime = 1.0 / min_framerate;
pub const default_window_width = 1280;
pub const default_window_height = 720;
pub const default_window_size = Vector2.init(default_window_width, default_window_height);
pub const default_window_size = Vector2Int.init(default_window_width, default_window_height);
pub const min_window_width = 640;
pub const min_window_height = 360;
pub const min_window_size = Vector2.init(default_window_width, default_window_height);
pub const min_window_size = Vector2Int.init(default_window_width, default_window_height);
pub const window_title = "Voxel Game";
pub const temp_allocator_capacity = 16 * 1024 * 1024;
// Coordinate spaces:
// - CS (clip space, as required by Vulkan)
// - VS (view space, looking towards +Y, +X is right and +Z is up)
// - WS (world space, origin at a corner of voxel (0, 0, 0), +Z is up)
// Units:
// - SV (subvoxel) |
// - VX (voxels) | 1 [VX] = 4096 [SV]
// - CK (chunks) | 1 [CK] = 16 [VX]
// Relative units:
// CKSV subvoxels relative to chunk | [0, 65536)
// CKVX voxels relative to chunk | [0, 16)
// VXSV subvoxels relative to voxel | [0, 4096)
pub const sv_per_vx = 4096;
pub const vx_per_ck = 16;
pub const sv_per_ck = sv_per_vx * vx_per_ck;
pub inline fn sv(comptime vx: comptime_float) comptime_int {
return @intFromFloat(@round(vx * sv_per_vx));
}

View File

@@ -1,10 +1,14 @@
const std = @import("std");
pub const Iterator2 = @import("math/Iterator2.zig").Iterator2;
pub const Iterator3 = @import("math/Iterator3.zig").Iterator3;
pub const Matrix4x4 = @import("math/Matrix4x4.zig").Matrix4x4;
pub const Quaternion = @import("math/Quaternion.zig").Quaternion;
pub const Vector2 = @import("math/Vector2.zig").Vector2;
pub const Vector2Int = @import("math/Vector2Int.zig").Vector2Int;
pub const Vector2x8 = @import("math/Vector2x8.zig").Vector2x8;
pub const Vector3 = @import("math/Vector3.zig").Vector3;
pub const Vector3Int = @import("math/Vector3Int.zig").Vector3Int;
pub const Vector4 = @import("math/Vector4.zig").Vector4;
pub const f32x8 = @Vector(8, f32);
@@ -35,3 +39,37 @@ pub inline fn lerp(a: f32, b: f32, t: f32) f32 {
pub const noise2 = @import("math/noise.zig").noise2;
pub const noise2x8 = @import("math/noise.zig").noise2x8;
pub inline fn asFloatFrac(frac: i32) f32 {
const numerator: f64 = @floatFromInt(frac);
const denominator: f64 = std.math.maxInt(i32);
return @floatCast(numerator / denominator);
}
pub inline fn asIntFrac(frac: f32) i32 {
const fraction: f64 = @floatCast(frac);
const scale: f64 = std.math.maxInt(i32);
return @intFromFloat(@round(fraction * scale));
}
pub inline fn mulIntFloat(int: i32, float: f32) i32 {
const int_float: f64 = @floatFromInt(int);
const float_wide: f64 = @floatCast(float);
return @intFromFloat(@round(int_float * float_wide));
}
pub inline fn mulFrac(a: i32, b: i32) i32 {
const denominator: i64 = std.math.maxInt(i32);
const rounding_bias: i64 = denominator >> 1;
const a_wide: i64 = a;
const b_wide: i64 = b;
return @intCast(@divFloor(a_wide * b_wide + rounding_bias, denominator));
}
pub inline fn mulFracFrac(a: i32, b: i32) i32 {
const denominator: i64 = std.math.maxInt(i32) * std.math.maxInt(i32);
const rounding_bias: i64 = denominator >> 1;
const a_wide: i64 = a;
const b_wide: i64 = b;
return .{ .vector = @intCast(@divFloor(a_wide * b_wide + rounding_bias, denominator)) };
}

View File

@@ -1,5 +1,6 @@
const std = @import("std");
const Vector2Int = @import("Vector2Int.zig").Vector2Int;
const Vector3 = @import("Vector3.zig").Vector3;
const Vector4 = @import("Vector3.zig").Vector4;
@@ -7,26 +8,33 @@ pub const Vector2 = extern struct {
vector: Vector,
pub const Vector = @Vector(2, f32);
pub const Array = [2]f32;
pub const Mask = enum(i32) { x = 0, y = 1 };
pub const zero = Vector2.init(0, 0);
pub const one = Vector2.init(1, 1);
pub const zero = Vector2.initScalar(0);
pub const one = Vector2.initScalar(1);
pub const unit_x = Vector2.init(1, 0);
pub const unit_y = Vector2.init(0, 1);
pub const unit_nx = Vector2.init(-1, 0);
pub const unit_ny = Vector2.init(0, -1);
// --- INIT ---
pub inline fn initScalar(scalar: f32) Vector2 {
return .{ .vector = @splat(scalar) };
}
pub inline fn init(x: f32, y: f32) Vector2 {
return .{ .vector = .{ x, y } };
}
pub inline fn initArray(array: [2]f32) Vector2 {
pub inline fn initArray(array: Array) Vector2 {
return .{ .vector = array };
}
// --- CONVERSION ---
pub inline fn asArray(self: Vector2) [2]f32 {
pub inline fn asArray(self: Vector2) Array {
return self.vector;
}
@@ -35,6 +43,16 @@ pub const Vector2 = extern struct {
return @as(@Vector(2, T), @intFromFloat(@round(self.vector * scale_vector)));
}
pub inline fn asVector2Int(self: Vector2) Vector2Int {
return .{ .vector = @intFromFloat(@round(self.vector)) };
}
pub inline fn asVector2IntFrac(self: Vector2) Vector2Int {
const fraction: @Vector(2, f64) = @floatCast(self.vector);
const scale: @Vector(2, f64) = @splat(std.math.maxInt(i32));
return .{ .vector = @intFromFloat(@round(fraction * scale)) };
}
pub inline fn asVector3(self: Vector2, z: f32) Vector3 {
const z_vector: @Vector(3, f32) = .{ undefined, undefined, z };
return .{ .vector = @shuffle(f32, self.vector, z_vector, [_]i32{ 0, 1, ~@as(i32, 2) }) };

191
src/math/Vector2Int.zig Normal file
View File

@@ -0,0 +1,191 @@
const std = @import("std");
const Vector2 = @import("Vector2.zig").Vector2;
const Vector3Int = @import("Vector3Int.zig").Vector3Int;
pub const Vector2Int = extern struct {
vector: Vector,
pub const Vector = @Vector(2, i32);
pub const Array = [2]i32;
pub const Mask = enum(i32) { x = 0, y = 1 };
pub const zero = Vector2Int.initScalar(0);
pub const one = Vector2Int.initScalar(1);
pub const one_frac = Vector2Int.initScalar(std.math.maxInt(i32));
pub const unit_x = Vector2Int.init(1, 0);
pub const unit_y = Vector2Int.init(0, 1);
pub const unit_nx = Vector2Int.init(-1, 0);
pub const unit_ny = Vector2Int.init(0, -1);
pub const unit_x_frac = Vector2Int.init(std.math.maxInt(i32), 0);
pub const unit_y_frac = Vector2Int.init(0, std.math.maxInt(i32));
pub const unit_nx_frac = Vector2Int.init(-std.math.maxInt(i32), 0);
pub const unit_ny_frac = Vector2Int.init(0, -std.math.maxInt(i32));
// --- INIT ---
pub inline fn initScalar(scalar: i32) Vector2Int {
return .{ .vector = @splat(scalar) };
}
pub inline fn init(x: i32, y: i32) Vector2Int {
return .{ .vector = .{ x, y } };
}
pub inline fn initArray(array: Array) Vector2Int {
return .{ .vector = array };
}
// --- CONVERSION ---
pub inline fn asArray(self: Vector2Int) Array {
return self.vector;
}
pub inline fn asVector2(self: Vector2Int) Vector2 {
return .{ .vector = @floatFromInt(self.vector) };
}
pub inline fn asVector2Frac(self: Vector2Int) Vector2 {
const numerator: @Vector(2, f64) = @floatFromInt(self.vector);
const denominator: @Vector(2, f64) = @splat(std.math.maxInt(i32));
return .{ .vector = @floatCast(numerator / denominator) };
}
pub inline fn asVector3Int(self: Vector2Int, z: i32) Vector3Int {
const z_vector: @Vector(3, i32) = .{ undefined, undefined, z };
return .{ .vector = @shuffle(i32, self.vector, z_vector, [_]i32{ 0, 1, ~@as(i32, 2) }) };
}
// --- ACCESSORS ---
pub inline fn getX(self: Vector2Int) i32 {
return self.vector[0];
}
pub inline fn getY(self: Vector2Int) i32 {
return self.vector[1];
}
pub inline fn setX(self: Vector2Int, x: i32) Vector2Int {
const x_vector: Vector = @splat(x);
return .{ .vector = @shuffle(i32, self.vector, x_vector, .{ ~@as(i32, 0), 1 }) };
}
pub inline fn setY(self: Vector2Int, y: i32) Vector2Int {
const y_vector: Vector = @splat(y);
return .{ .vector = @shuffle(i32, self.vector, y_vector, .{ 0, ~@as(i32, 1) }) };
}
// --- COMPONENT-WISE ---
pub inline fn add(self: Vector2Int, other: Vector2Int) Vector2Int {
return .{ .vector = self.vector + other.vector };
}
pub inline fn sub(self: Vector2Int, other: Vector2Int) Vector2Int {
return .{ .vector = self.vector - other.vector };
}
pub inline fn mul(self: Vector2Int, other: Vector2Int) Vector2Int {
return .{ .vector = self.vector * other.vector };
}
pub inline fn mulFrac(self: Vector2Int, other: Vector2Int) Vector2Int {
const denominator: @Vector(2, i64) = @splat(std.math.maxInt(i32));
const rounding_bias: @Vector(2, i64) = denominator >> @as(@Vector(2, u6), @splat(1));
const self_wide: @Vector(2, i64) = self.vector;
const other_wide: @Vector(2, i64) = other.vector;
return .{ .vector = @intCast(@divFloor(self_wide * other_wide + rounding_bias, denominator)) };
}
pub inline fn mulFracFrac(self: Vector2Int, other: Vector2Int) Vector2Int {
const denominator: @Vector(2, i64) = @splat(std.math.maxInt(i32) * std.math.maxInt(i32));
const rounding_bias: @Vector(2, i64) = denominator >> @as(@Vector(2, u6), @splat(1));
const self_wide: @Vector(2, i64) = self.vector;
const other_wide: @Vector(2, i64) = other.vector;
return .{ .vector = @intCast(@divFloor(self_wide * other_wide + rounding_bias, denominator)) };
}
pub inline fn div(self: Vector2Int, other: Vector2Int) Vector2Int {
return .{ .vector = @divFloor(self.vector, other.vector) };
}
pub inline fn mod(self: Vector2Int, other: Vector2Int) Vector2Int {
return .{ .vector = @mod(self.vector, other.vector) };
}
pub inline fn negate(self: Vector2Int) Vector2Int {
return .{ .vector = -self.vector };
}
pub inline fn mulScalar(self: Vector2Int, scalar: i32) Vector2Int {
const scalar_vector: Vector = @splat(scalar);
return .{ .vector = self.vector * scalar_vector };
}
pub inline fn mulScalarFloat(self: Vector2Int, scalar: f32) Vector2Int {
const self_float: @Vector(2, f64) = @floatFromInt(self.vector);
const scalar_vector_float: @Vector(2, f64) = @splat(scalar);
return .{ .vector = @intFromFloat(@round(self_float * scalar_vector_float)) };
}
pub inline fn mulScalarFrac(self: Vector2Int, scalar: i32) Vector2Int {
const denominator: @Vector(2, i64) = @splat(std.math.maxInt(i32));
const rounding_bias: @Vector(2, i64) = denominator >> @as(@Vector(2, u6), @splat(1));
const self_wide: @Vector(2, i64) = self.vector;
const scalar_wide: @Vector(2, i64) = @splat(scalar);
return .{ .vector = @intCast(@divFloor(self_wide * scalar_wide + rounding_bias, denominator)) };
}
pub inline fn mulScalarFracFrac(self: Vector2Int, scalar: i32) Vector2Int {
const denominator: @Vector(2, i64) = @splat(std.math.maxInt(i32) * std.math.maxInt(i32));
const rounding_bias: @Vector(2, i64) = denominator >> @as(@Vector(2, u6), @splat(1));
const self_wide: @Vector(2, i64) = self.vector;
const scalar_wide: @Vector(2, i64) = @splat(scalar);
return .{ .vector = @intCast(@divFloor(self_wide * scalar_wide + rounding_bias, denominator)) };
}
pub inline fn divScalar(self: Vector2Int, scalar: i32) Vector2Int {
const scalar_vector: Vector = @splat(scalar);
return .{ .vector = @divFloor(self.vector, scalar_vector) };
}
pub inline fn divScalarFloat(self: Vector2Int, scalar: f32) Vector2Int {
const self_float: @Vector(2, f64) = @floatFromInt(self.vector);
const scalar_vector_float: @Vector(2, f64) = @splat(scalar);
return .{ .vector = @intFromFloat(@round(self_float / scalar_vector_float)) };
}
pub inline fn modScalar(self: Vector2Int, scalar: i32) Vector2Int {
const scalar_vector: Vector = @splat(scalar);
return .{ .vector = @mod(self.vector, scalar_vector) };
}
// --- SWIZZLE ---
pub inline fn swizzle2(self: Vector2Int, comptime mask: [2]Mask) Vector2Int {
return .{ .vector = @shuffle(f32, self.vector, undefined, @as([2]i32, @bitCast(mask))) };
}
pub inline fn swizzle3(self: Vector2Int, comptime mask: [3]Mask) Vector3Int {
return .{ .vector = @shuffle(f32, self.vector, undefined, @as([3]i32, @bitCast(mask))) };
}
// --- OTHER ---
pub inline fn lenSquared(self: Vector2Int) i64 {
const self_wide: @Vector(2, i64) = self.vector;
return @reduce(.Add, self_wide * self_wide);
}
pub inline fn dot(self: Vector2Int, other: Vector2Int) i64 {
const self_wide: @Vector(2, i64) = self.vector;
const other_wide: @Vector(2, i64) = other.vector;
return @reduce(.Add, self_wide * other_wide);
}
pub inline fn cross(self: Vector2Int, other: Vector2Int) i64 {
return @as(i64, self.getX()) * @as(i64, other.getY()) - @as(i64, self.getY()) * @as(i64, other.getX());
}
};

View File

@@ -14,6 +14,8 @@ pub const Vector2x8 = extern struct {
pub const one = Vector2x8.init(ps(1), ps(1));
pub const unit_x = Vector2x8.init(ps(1), ps(0));
pub const unit_y = Vector2x8.init(ps(0), ps(1));
pub const unit_nx = Vector2x8.init(ps(-1), ps(0));
pub const unit_ny = Vector2x8.init(ps(0), ps(-1));
// --- INIT ---

View File

@@ -2,33 +2,42 @@ const std = @import("std");
const Quaternion = @import("Quaternion.zig").Quaternion;
const Vector2 = @import("Vector2.zig").Vector2;
const Vector3Int = @import("Vector3Int.zig").Vector3Int;
const Vector4 = @import("Vector4.zig").Vector4;
pub const Vector3 = extern struct {
vector: Vector,
pub const Vector = @Vector(3, f32);
pub const Array = [3]f32;
pub const Mask = enum(i32) { x = 0, y = 1, z = 2 };
pub const zero = Vector3.init(0, 0, 0);
pub const one = Vector3.init(1, 1, 1);
pub const zero = Vector3.initScalar(0);
pub const one = Vector3.initScalar(1);
pub const unit_x = Vector3.init(1, 0, 0);
pub const unit_y = Vector3.init(0, 1, 0);
pub const unit_z = Vector3.init(0, 0, 1);
pub const unit_nx = Vector3.init(-1, 0, 0);
pub const unit_ny = Vector3.init(0, -1, 0);
pub const unit_nz = Vector3.init(0, 0, -1);
// --- INIT ---
pub inline fn initScalar(scalar: f32) Vector3 {
return .{ .vector = @splat(scalar) };
}
pub inline fn init(x: f32, y: f32, z: f32) Vector3 {
return .{ .vector = .{ x, y, z } };
}
pub inline fn initArray(array: [3]f32) Vector3 {
pub inline fn initArray(array: Array) Vector3 {
return .{ .vector = array };
}
// --- CONVERSION ---
pub inline fn asArray(self: Vector3) [3]f32 {
pub inline fn asArray(self: Vector3) Array {
return self.vector;
}
@@ -37,6 +46,16 @@ pub const Vector3 = extern struct {
return @as(@Vector(3, T), @intFromFloat(@round(self.vector * scale_vector)));
}
pub inline fn asVector3Int(self: Vector3) Vector3Int {
return .{ .vector = @intFromFloat(@round(self.vector)) };
}
pub inline fn asVector2IntFrac(self: Vector3) Vector3Int {
const fraction: @Vector(3, f64) = @floatCast(self.vector);
const scale: @Vector(3, f64) = @splat(std.math.maxInt(i32));
return .{ .vector = @intFromFloat(@round(fraction * scale)) };
}
pub inline fn asVector2(self: Vector3) Vector2 {
return .{ .vector = @shuffle(f32, self.vector, undefined, [_]i32{ 0, 1 }) };
}

199
src/math/Vector3Int.zig Normal file
View File

@@ -0,0 +1,199 @@
const std = @import("std");
const Vector2Int = @import("Vector2Int.zig").Vector2Int;
const Vector3 = @import("Vector3.zig").Vector3;
pub const Vector3Int = extern struct {
vector: Vector,
pub const Vector = @Vector(3, i32);
pub const Array = [3]i32;
pub const Mask = enum(i32) { x = 0, y = 1, z = 2 };
pub const zero = Vector3Int.initScalar(0);
pub const one = Vector3Int.initScalar(1);
pub const one_frac = Vector3Int.initScalar(std.math.maxInt(i32));
pub const unit_x = Vector3Int.init(1, 0, 0);
pub const unit_y = Vector3Int.init(0, 1, 0);
pub const unit_z = Vector3Int.init(0, 0, 1);
pub const unit_nx = Vector3Int.init(-1, 0, 0);
pub const unit_ny = Vector3Int.init(0, -1, 0);
pub const unit_nz = Vector3Int.init(0, 0, -1);
pub const unit_x_frac = Vector3Int.init(std.math.maxInt(i32), 0, 0);
pub const unit_y_frac = Vector3Int.init(0, std.math.maxInt(i32), 0);
pub const unit_z_frac = Vector3Int.init(0, 0, std.math.maxInt(i32));
pub const unit_nx_frac = Vector3Int.init(-std.math.maxInt(i32), 0, 0);
pub const unit_ny_frac = Vector3Int.init(0, -std.math.maxInt(i32), 0);
pub const unit_nz_frac = Vector3Int.init(0, 0, -std.math.maxInt(i32));
// --- INIT ---
pub inline fn initScalar(scalar: i32) Vector3Int {
return .{ .vector = @splat(scalar) };
}
pub inline fn init(x: i32, y: i32, z: i32) Vector3Int {
return .{ .vector = .{ x, y, z } };
}
pub inline fn initArray(array: Array) Vector3Int {
return .{ .vector = array };
}
// --- CONVERSION ---
pub inline fn asArray(self: Vector3Int) Array {
return self.vector;
}
pub inline fn asVector3(self: Vector3Int) Vector3 {
return .{ .vector = @floatFromInt(self.vector) };
}
pub inline fn asVector3Frac(self: Vector3Int) Vector3 {
const numerator: @Vector(3, f64) = @floatFromInt(self.vector);
const denominator: @Vector(3, f64) = @splat(std.math.maxInt(i32));
return .{ .vector = @floatCast(numerator / denominator) };
}
pub inline fn asVector2Int(self: Vector3Int) Vector2Int {
return .{ .vector = @shuffle(i32, self.vector, undefined, [_]i32{ 0, 1 }) };
}
// --- ACCESSORS ---
pub inline fn getX(self: Vector3Int) i32 {
return self.vector[0];
}
pub inline fn getY(self: Vector3Int) i32 {
return self.vector[1];
}
pub inline fn getZ(self: Vector3Int) i32 {
return self.vector[2];
}
pub inline fn setX(self: Vector3Int, x: i32) Vector3Int {
const x_vector: Vector = @splat(x);
return .{ .vector = @shuffle(i32, self.vector, x_vector, [_]i32{ ~@as(i32, 0), 1, 2 }) };
}
pub inline fn setY(self: Vector3Int, y: i32) Vector3Int {
const y_vector: Vector = @splat(y);
return .{ .vector = @shuffle(i32, self.vector, y_vector, [_]i32{ 0, ~@as(i32, 1), 2 }) };
}
pub inline fn setZ(self: Vector3Int, z: i32) Vector3Int {
const z_vector: Vector = @splat(z);
return .{ .vector = @shuffle(i32, self.vector, z_vector, [_]i32{ 0, 1, ~@as(i32, 2) }) };
}
// --- COMPONENT-WISE ---
pub inline fn add(self: Vector3Int, other: Vector3Int) Vector3Int {
return .{ .vector = self.vector + other.vector };
}
pub inline fn sub(self: Vector3Int, other: Vector3Int) Vector3Int {
return .{ .vector = self.vector - other.vector };
}
pub inline fn mul(self: Vector3Int, other: Vector3Int) Vector3Int {
return .{ .vector = self.vector * other.vector };
}
pub inline fn mulFrac(self: Vector3Int, other: Vector3Int) Vector3Int {
const denominator: @Vector(3, i64) = @splat(std.math.maxInt(i32));
const rounding_bias: @Vector(3, i64) = denominator >> @as(@Vector(2, u6), @splat(1));
const self_wide: @Vector(3, i64) = self.vector;
const other_wide: @Vector(3, i64) = other.vector;
return .{ .vector = @intCast(@divFloor(self_wide * other_wide + rounding_bias, denominator)) };
}
pub inline fn mulFracFrac(self: Vector3Int, other: Vector3Int) Vector3Int {
const denominator: @Vector(3, i64) = @splat(std.math.maxInt(i32) * std.math.maxInt(i32));
const rounding_bias: @Vector(3, i64) = denominator >> @as(@Vector(2, u6), @splat(1));
const self_wide: @Vector(3, i64) = self.vector;
const other_wide: @Vector(3, i64) = other.vector;
return .{ .vector = @intCast(@divFloor(self_wide * other_wide + rounding_bias, denominator)) };
}
pub inline fn div(self: Vector3Int, other: Vector3Int) Vector3Int {
return .{ .vector = @divFloor(self.vector, other.vector) };
}
pub inline fn mod(self: Vector3Int, other: Vector3Int) Vector3Int {
return .{ .vector = @mod(self.vector, other.vector) };
}
pub inline fn negate(self: Vector3Int) Vector3Int {
return .{ .vector = -self.vector };
}
pub inline fn mulScalar(self: Vector3Int, scalar: i32) Vector3Int {
const scalar_vector: Vector = @splat(scalar);
return .{ .vector = self.vector * scalar_vector };
}
pub inline fn mulScalarFloat(self: Vector3Int, scalar: f32) Vector3Int {
const self_float: @Vector(3, f64) = @floatFromInt(self.vector);
const scalar_vector_float: @Vector(3, f64) = @splat(scalar);
return .{ .vector = @intFromFloat(@round(self_float * scalar_vector_float)) };
}
pub inline fn mulScalarFrac(self: Vector3Int, scalar: i32) Vector3Int {
const denominator: @Vector(3, i64) = @splat(std.math.maxInt(i32));
const rounding_bias: @Vector(3, i64) = denominator >> @as(@Vector(3, u6), @splat(1));
const self_wide: @Vector(3, i64) = self;
const scalar_wide: @Vector(3, i64) = @splat(scalar);
return .{ .vector = @intCast(@divFloor(self_wide * scalar_wide + rounding_bias, denominator)) };
}
pub inline fn mulScalarFracFrac(self: Vector3Int, scalar: i32) Vector3Int {
const denominator: @Vector(3, i64) = @splat(std.math.maxInt(i32) * std.math.maxInt(i32));
const rounding_bias: @Vector(3, i64) = denominator >> @as(@Vector(3, u6), @splat(1));
const self_wide: @Vector(3, i64) = self;
const scalar_wide: @Vector(3, i64) = @splat(scalar);
return .{ .vector = @intCast(@divFloor(self_wide * scalar_wide + rounding_bias, denominator)) };
}
pub inline fn divScalar(self: Vector3Int, scalar: i32) Vector3Int {
const scalar_vector: Vector = @splat(scalar);
return .{ .vector = @divFloor(self.vector, scalar_vector) };
}
pub inline fn divScalarFloat(self: Vector3Int, scalar: f32) Vector3Int {
const self_float: @Vector(3, f64) = @floatFromInt(self.vector);
const scalar_vector_float: @Vector(3, f64) = @splat(scalar);
return .{ .vector = @intFromFloat(@round(self_float / scalar_vector_float)) };
}
pub inline fn modScalar(self: Vector3Int, scalar: i32) Vector3Int {
const scalar_vector: Vector = @splat(scalar);
return .{ .vector = @mod(self.vector, scalar_vector) };
}
// --- SWIZZLE ---
pub inline fn swizzle2(self: Vector3Int, comptime mask: [2]Mask) Vector2Int {
return .{ .vector = @shuffle(f32, self.vector, undefined, @as([2]i32, @bitCast(mask))) };
}
pub inline fn swizzle3(self: Vector3Int, comptime mask: [3]Mask) Vector3Int {
return .{ .vector = @shuffle(f32, self.vector, undefined, @as([3]i32, @bitCast(mask))) };
}
// --- OTHER ---
pub inline fn lenSquared(self: Vector3Int) i64 {
const self_wide: @Vector(2, i64) = self.vector;
return @reduce(.Add, self_wide * self_wide);
}
pub inline fn dot(self: Vector3Int, other: Vector3Int) i64 {
const self_wide: @Vector(3, i64) = self.vector;
const other_wide: @Vector(3, i64) = other.vector;
return @reduce(.Add, self_wide * other_wide);
}
};

View File

@@ -7,28 +7,37 @@ pub const Vector4 = extern struct {
vector: Vector,
pub const Vector = @Vector(4, f32);
pub const Array = [4]f32;
pub const Mask = enum(i32) { x = 0, y = 1, z = 2, w = 3 };
pub const zero = Vector4.init(0, 0, 0, 0);
pub const one = Vector4.init(1, 1, 1, 1);
pub const zero = Vector4.initScalar(0);
pub const one = Vector4.initScalar(1);
pub const unit_x = Vector4.init(1, 0, 0, 0);
pub const unit_y = Vector4.init(0, 1, 0, 0);
pub const unit_z = Vector4.init(0, 0, 1, 0);
pub const unit_w = Vector4.init(0, 0, 0, 1);
pub const unit_nx = Vector4.init(-1, 0, 0, 0);
pub const unit_ny = Vector4.init(0, -1, 0, 0);
pub const unit_nz = Vector4.init(0, 0, -1, 0);
pub const unit_nw = Vector4.init(0, 0, 0, -1);
// --- INIT ---
pub inline fn initScalar(scalar: f32) Vector4 {
return .{ .vector = @splat(scalar) };
}
pub inline fn init(x: f32, y: f32, z: f32, w: f32) Vector4 {
return .{ .vector = .{ x, y, z, w } };
}
pub inline fn initArray(array: [4]f32) Vector4 {
pub inline fn initArray(array: Array) Vector4 {
return .{ .vector = array };
}
// --- CONVERSION ---
pub inline fn asArray(self: Vector4) [4]f32 {
pub inline fn asArray(self: Vector4) Array {
return self.vector;
}