diff --git a/assets/shaders/equirect_to_cube.comp b/assets/shaders/equirect_to_cube.comp index e5fc964..a0cca9e 100644 --- a/assets/shaders/equirect_to_cube.comp +++ b/assets/shaders/equirect_to_cube.comp @@ -8,6 +8,45 @@ layout(set = 0, binding = 2, rgba16f) uniform writeonly restrict imageCube _Cube const float INV_PI = 0.31830987; const float HALF_INV_PI = 0.15915494; +const mat3x3 MATRIX_2D_TO_CUBE[6] = mat3x3[]( + // Positive X + mat3x3( + 0, 0, -1, + 0, -1, 0, + 1, 0, 0 + ), + // Negative X + mat3x3( + 0, 0, 1, + 0, -1, 0, + -1, 0, 0 + ), + // Positive Y + mat3x3( + 1, 0, 0, + 0, 0, 1, + 0, 1, 0 + ), + // Negative Y + mat3x3( + 1, 0, 0, + 0, 0, -1, + 0, -1, 0 + ), + // Positive Z + mat3x3( + 1, 0, 0, + 0, -1, 0, + 0, 0, 1 + ), + // Negative Z + mat3x3( + -1, 0, 0, + 0, -1, 0, + 0, 0, -1 + ) +); + layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; void main() { vec2 size = vec2(imageSize(_CubemapImage).xy); @@ -16,27 +55,7 @@ void main() { texCoord = texCoord * vec2(2.0) - vec2(1.0); // Map to range [-1, 1] - vec3 cubeCoord; - if (layerIndex == 0) { - // Positive X - cubeCoord = vec3(1, -texCoord.y, -texCoord.x); - } else if (layerIndex == 1) { - // Negative X - cubeCoord = vec3(-1, -texCoord.y, texCoord.x); - } else if (layerIndex == 2) { - // Positive Y - cubeCoord = vec3(texCoord.x, 1, texCoord.y); - } else if (layerIndex == 3) { - // Negative Y - cubeCoord = vec3(texCoord.x, -1, -texCoord.y); - } else if (layerIndex == 4) { - // Positive Z - cubeCoord = vec3(texCoord.x, -texCoord.y, 1); - } else if (layerIndex == 5) { - // Negative Z - cubeCoord = vec3(-texCoord.x, -texCoord.y, -1); - } - + vec3 cubeCoord = MATRIX_2D_TO_CUBE[layerIndex] * vec3(texCoord, 1.0); vec3 cubeDir = normalize(cubeCoord); float theta = atan(cubeDir.y, cubeDir.x); @@ -45,5 +64,5 @@ void main() { vec2 equirectCoord = vec2(theta * HALF_INV_PI, phi * INV_PI) + vec2(0.5); vec4 irradiance = texture(sampler2D(_EquirectangularTexture, _EquirectangularSampler), equirectCoord); - imageStore(_CubemapImage, ivec3(ivec2(gl_GlobalInvocationID.xy), layerIndex), irradiance); + imageStore(_CubemapImage, ivec3(gl_GlobalInvocationID.xyz), irradiance); } diff --git a/src/Chunks.zig b/src/Chunks.zig index 3ded3eb..2c5f788 100644 --- a/src/Chunks.zig +++ b/src/Chunks.zig @@ -14,7 +14,7 @@ const Vector3Int = math.Vector3Int; chunks: std.AutoHashMapUnmanaged([3]i16, Chunk), -const SweepHit = struct { +const RaycastHit = struct { normal_frac: Vector3Int, projected_distance_sv: i32, }; @@ -54,7 +54,7 @@ pub fn isSolid(self: *const Chunks, vx: Vector3Int) bool { return maybe_id != .air; } -pub fn sweepCastDown(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, distance_sv: i32) ?SweepHit { +pub fn sweepCastDown(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, distance_sv: i32) ?RaycastHit { const min_x_vx = c.subvoxelsToVoxels(.border_up, min_sv.getX()); const min_y_vx = c.subvoxelsToVoxels(.border_up, min_sv.getY()); const max_x_vx = c.subvoxelsToVoxels(.border_down, max_sv.getX()); @@ -84,7 +84,7 @@ pub fn sweepCastDown(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int return null; } -pub fn sweepCastUp(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, distance_sv: i32) ?SweepHit { +pub fn sweepCastUp(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, distance_sv: i32) ?RaycastHit { const min_x_vx = c.subvoxelsToVoxels(.border_up, min_sv.getX()); const min_y_vx = c.subvoxelsToVoxels(.border_up, min_sv.getY()); const max_x_vx = c.subvoxelsToVoxels(.border_down, max_sv.getX()); @@ -114,11 +114,11 @@ pub fn sweepCastUp(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, return null; } -pub fn sweepCastHorizontal(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, ray_sv: Vector2Int) ?SweepHit { +pub fn sweepCastHorizontal(self: *const Chunks, min_sv: Vector3Int, max_sv: Vector3Int, ray_sv: Vector2Int) ?RaycastHit { const min_z_vx = c.subvoxelsToVoxels(.border_up, min_sv.getZ()); const max_z_vx = c.subvoxelsToVoxels(.border_down, max_sv.getZ()); - var hit: ?SweepHit = null; + var hit: ?RaycastHit = null; var hit_distance_squared = std.math.inf(f32); const fdydx: f32 = @as(f32, @floatFromInt(ray_sv.getY())) / @as(f32, @floatFromInt(ray_sv.getX())); @@ -284,3 +284,188 @@ pub fn sweepCastHorizontal(self: *const Chunks, min_sv: Vector3Int, max_sv: Vect return hit; } + +pub fn raycast(self: *const Chunks, origin_sv: Vector3Int, ray_sv: Vector3Int) ?RaycastHit { + const end_sv = origin_sv.add(ray_sv); + + std.debug.print("Raycast from ({d}:{X}, {d}:{X}, {d}:{X}) to ({d}:{X}, {d}:{X}, {d}:{X})\n", .{ + c.subvoxelsToVoxels(.border_up, origin_sv.getX()), + c.subvoxelsToVoxelSubvoxels(origin_sv.getX()), + c.subvoxelsToVoxels(.border_up, origin_sv.getY()), + c.subvoxelsToVoxelSubvoxels(origin_sv.getY()), + c.subvoxelsToVoxels(.border_up, origin_sv.getZ()), + c.subvoxelsToVoxelSubvoxels(origin_sv.getZ()), + c.subvoxelsToVoxels(.border_up, end_sv.getX()), + c.subvoxelsToVoxelSubvoxels(end_sv.getX()), + c.subvoxelsToVoxels(.border_up, end_sv.getY()), + c.subvoxelsToVoxelSubvoxels(end_sv.getY()), + c.subvoxelsToVoxels(.border_up, end_sv.getZ()), + c.subvoxelsToVoxelSubvoxels(end_sv.getZ()), + }); + + const zero = Vector3Int.zero.vector; + const one = Vector3Int.one.vector; + + const ray_positive: Vector3Int = .{ .vector = @select(i32, ray_sv.vector > zero, one, zero) }; + const ray_negative: Vector3Int = .{ .vector = @select(i32, ray_sv.vector < zero, one, zero) }; + const ray_sign: Vector3Int = .sub(ray_positive, ray_negative); + + const start_vx = origin_sv.sub(ray_positive).divScalar(c.sv_per_vx).add(ray_sign); + const end_vx = end_sv.sub(ray_positive).divScalar(c.sv_per_vx); + const abs_ray_sv = ray_sv.abs(); + + std.debug.print("Start [VX]: ({d}, {d}, {d}) | End [VX]: ({d}, {d}, {d})\n", .{ + start_vx.getX(), + start_vx.getY(), + start_vx.getZ(), + end_vx.getX(), + end_vx.getY(), + end_vx.getZ(), + }); + + var i_index: usize = undefined; + var j_index: usize = undefined; + var k_index: usize = undefined; + + if (abs_ray_sv.getX() > abs_ray_sv.getY() and abs_ray_sv.getX() > abs_ray_sv.getZ()) { + // Main axis: ±X + i_index, j_index, k_index = .{ 0, 1, 2 }; + std.debug.print("Order is (X, Y, Z)\n", .{}); + } else if (abs_ray_sv.getY() > abs_ray_sv.getZ()) { + // Main axis: ±Y + i_index, j_index, k_index = .{ 1, 2, 0 }; + std.debug.print("Order is (Y, Z, X)\n", .{}); + } else if (abs_ray_sv.getZ() > 0) { + // Main axis: ±Z + i_index, j_index, k_index = .{ 2, 0, 1 }; + std.debug.print("Order is (Z, X, Y)\n", .{}); + } else { + std.debug.assert(abs_ray_sv.getX() == 0); + std.debug.assert(abs_ray_sv.getY() == 0); + std.debug.assert(abs_ray_sv.getZ() == 0); + return null; + } + + const oi = origin_sv.vector[i_index]; + const oj = origin_sv.vector[j_index]; + const ok = origin_sv.vector[k_index]; + + const di = ray_sv.vector[i_index]; + const dj = ray_sv.vector[j_index]; + const dk = ray_sv.vector[k_index]; + + var i_vx = start_vx.vector[i_index]; + + var enter_i_sv = oi; + var enter_di_sv = enter_i_sv - oi; + var enter_jdi_sv = oj * di + enter_di_sv * dj; + var enter_kdi_sv = ok * di + enter_di_sv * dk; + + var enter_i_vx = @divFloor(enter_i_sv - ray_positive.vector[i_index], c.sv_per_vx); + var enter_j_vx = @divFloor(enter_jdi_sv - ray_positive.vector[j_index], c.sv_per_vx * di); + var enter_k_vx = @divFloor(enter_kdi_sv - ray_positive.vector[k_index], c.sv_per_vx * di); + + var exit_i_sv = c.voxelsToSubvoxels(i_vx + ray_negative.vector[i_index]); + var exit_di_sv = exit_i_sv - oi; + var exit_jdi_sv = oj * di + exit_di_sv * dj; + var exit_kdi_sv = ok * di + exit_di_sv * dk; + + var exit_i_vx = @divFloor(exit_i_sv - ray_positive.vector[i_index], c.sv_per_vx); + var exit_j_vx = @divFloor(exit_jdi_sv - ray_positive.vector[j_index], c.sv_per_vx * di); + var exit_k_vx = @divFloor(exit_kdi_sv - ray_positive.vector[k_index], c.sv_per_vx * di); + + std.debug.print("({d}:{X}, {d:.3}, {d:.3}) → ({d}:{X}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d})\n", .{ + c.subvoxelsToVoxels(.border_up, enter_i_sv), + c.subvoxelsToVoxelSubvoxels(enter_i_sv), + @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + c.subvoxelsToVoxels(.border_up, exit_i_sv), + c.subvoxelsToVoxelSubvoxels(exit_i_sv), + @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + enter_i_vx, + enter_j_vx, + enter_k_vx, + exit_i_vx, + exit_j_vx, + exit_k_vx, + }); + + while (i_vx != end_vx.vector[i_index]) : (i_vx += ray_sign.vector[i_index]) { + enter_i_sv = exit_i_sv; + enter_di_sv = exit_di_sv; + enter_jdi_sv = exit_jdi_sv; + enter_kdi_sv = exit_kdi_sv; + + enter_i_vx = exit_i_vx; + enter_j_vx = exit_j_vx; + enter_k_vx = exit_k_vx; + + exit_i_sv = c.voxelsToSubvoxels(i_vx + ray_positive.vector[i_index]); + exit_di_sv = exit_i_sv - oi; + exit_jdi_sv = oj * di + exit_di_sv * dj; + exit_kdi_sv = ok * di + exit_di_sv * dk; + + exit_i_vx = @divFloor(exit_i_sv - ray_positive.vector[i_index], c.sv_per_vx); + exit_j_vx = @divFloor(exit_jdi_sv - ray_positive.vector[j_index], c.sv_per_vx * di); + exit_k_vx = @divFloor(exit_kdi_sv - ray_positive.vector[k_index], c.sv_per_vx * di); + + std.debug.print("({d}:{X}, {d:.3}, {d:.3}) → ({d}:{X}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d})\n", .{ + c.subvoxelsToVoxels(.border_up, enter_i_sv), + c.subvoxelsToVoxelSubvoxels(enter_i_sv), + @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + c.subvoxelsToVoxels(.border_up, exit_i_sv), + c.subvoxelsToVoxelSubvoxels(exit_i_sv), + @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + enter_i_vx, + enter_j_vx, + enter_k_vx, + exit_i_vx, + exit_j_vx, + exit_k_vx, + }); + } + + enter_i_sv = exit_i_sv; + enter_di_sv = exit_di_sv; + enter_jdi_sv = exit_jdi_sv; + enter_kdi_sv = exit_kdi_sv; + + enter_i_vx = exit_i_vx; + enter_j_vx = exit_j_vx; + enter_k_vx = exit_k_vx; + + exit_i_sv = oi + di; + exit_di_sv = exit_i_sv - oi; + exit_jdi_sv = oj * di + exit_di_sv * dj; + exit_kdi_sv = ok * di + exit_di_sv * dk; + + exit_i_vx = @divFloor(exit_i_sv - ray_positive.vector[i_index], c.sv_per_vx); + exit_j_vx = @divFloor(exit_jdi_sv - ray_positive.vector[j_index], c.sv_per_vx * di); + exit_k_vx = @divFloor(exit_kdi_sv - ray_positive.vector[k_index], c.sv_per_vx * di); + + std.debug.print("({d}:{X}, {d:.3}, {d:.3}) → ({d}:{X}, {d:.3}, {d:.3}) | ({d}, {d}, {d}) → ({d}, {d}, {d})\n", .{ + c.subvoxelsToVoxels(.border_up, enter_i_sv), + c.subvoxelsToVoxelSubvoxels(enter_i_sv), + @as(f64, @floatFromInt(enter_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + @as(f64, @floatFromInt(enter_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + c.subvoxelsToVoxels(.border_up, exit_i_sv), + c.subvoxelsToVoxelSubvoxels(exit_i_sv), + @as(f64, @floatFromInt(exit_jdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + @as(f64, @floatFromInt(exit_kdi_sv)) / @as(f64, @floatFromInt(di * c.sv_per_vx)), + enter_i_vx, + enter_j_vx, + enter_k_vx, + exit_i_vx, + exit_j_vx, + exit_k_vx, + }); + + // XXX + std.debug.print("\n", .{}); + + _ = self; + return null; +} diff --git a/src/Player.zig b/src/Player.zig index 7db32b5..1435748 100644 --- a/src/Player.zig +++ b/src/Player.zig @@ -8,6 +8,7 @@ const math = @import("math.zig"); const Blocks = @import("assets/Blocks.zig"); const Chunks = @import("Chunks.zig"); const Iterator2 = math.Iterator2; +const Quaternion = math.Quaternion; const Vector2 = math.Vector2; const Vector3 = math.Vector3; const Vector2Int = math.Vector2Int; @@ -200,6 +201,8 @@ pub const ground_acceleration_sv = sv(56.12); pub const air_acceleration_sv = sv(56.12); pub const air_speed_limit_sv = sv(0.5612); +pub const raycast_length_sv = sv(5.0); + pub fn init(position_sv: Vector3Int, pitch_rad: f32, yaw_rad: f32) Player { return .{ .position_sv = position_sv, @@ -229,6 +232,17 @@ pub fn onMouseMove(self: *Player, dx: f32, dy: f32) void { pub fn update(self: *Player, dt: f32, chunks: *const Chunks) void { defer self.resetAllButtons(); + //const raycast_origin_sv = self.position_sv + // .add(.init(0, 0, @intFromFloat(@round(c.sv_per_vx * camera_height_vx)))); + //const raycast_ray_sv = Vector3 + // .init(0, raycast_length_sv, 0) + // .rotate(.mulQuaternion( + // .initRotationXY(self.yaw_rad), + // .initRotationYZ(self.pitch_rad), + // )) + // .asVector3Int(); + //const raycast_hit = chunks.raycast(raycast_origin_sv, raycast_ray_sv); + // --- GATHER INPUTS ------------------------------------------------------- const horizontal_input_vector_frac = blk: {