Trying to improve collisions and failing
This commit is contained in:
518
src/Player.zig
518
src/Player.zig
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user