Improve collision to (hopefully) work with ladders

This commit is contained in:
codemob-dev 2025-07-29 11:54:44 -04:00
parent 9c0e0e3912
commit 22b66021de
2 changed files with 119 additions and 61 deletions

View File

@ -77,6 +77,106 @@ pub const collision = struct {
pub fn join(self: AABB, other: AABB) AABB {
return .{.min = @min(self.min, other.min), .max = @max(self.max, other.max)};
}
pub fn intersectsTriangle(self: AABB, triangle: [3]Vec3d) bool {
const X = 0;
const Y = 1;
const Z = 2;
const box_center = self.center();
const box_extents = self.extent();
// Translate triangle as conceptually moving AABB to origin
const v0 = triangle[0] - box_center;
const v1 = triangle[1] - box_center;
const v2 = triangle[2] - box_center;
// Compute edge vectors for triangle
const f0 = triangle[1] - triangle[0];
const f1 = triangle[2] - triangle[1];
const f2 = triangle[0] - triangle[2];
// Test axis a00
const a00 = Vec3d{0, -f0[Z], f0[Y]};
if(!test_axis(a00, v0, v1, v2, box_extents[Y]*@abs(f0[Z]) + box_extents[Z]*@abs(f0[Y]))) {
return false;
}
// Test axis a01
const a01 = Vec3d{0, -f1[Z], f1[Y]};
if(!test_axis(a01, v0, v1, v2, box_extents[Y]*@abs(f1[Z]) + box_extents[Z]*@abs(f1[Y]))) {
return false;
}
// Test axis a02
const a02 = Vec3d{0, -f2[Z], f2[Y]};
if(!test_axis(a02, v0, v1, v2, box_extents[Y]*@abs(f2[Z]) + box_extents[Z]*@abs(f2[Y]))) {
return false;
}
// Test axis a10
const a10 = Vec3d{f0[Z], 0, -f0[X]};
if(!test_axis(a10, v0, v1, v2, box_extents[X]*@abs(f0[Z]) + box_extents[Z]*@abs(f0[X]))) {
return false;
}
// Test axis a11
const a11 = Vec3d{f1[Z], 0, -f1[X]};
if(!test_axis(a11, v0, v1, v2, box_extents[X]*@abs(f1[Z]) + box_extents[Z]*@abs(f1[X]))) {
return false;
}
// Test axis a12
const a12 = Vec3d{f2[Z], 0, -f2[X]};
if(!test_axis(a12, v0, v1, v2, box_extents[X]*@abs(f2[Z]) + box_extents[Z]*@abs(f2[X]))) {
return false;
}
// Test axis a20
const a20 = Vec3d{-f0[Y], f0[X], 0};
if(!test_axis(a20, v0, v1, v2, box_extents[X]*@abs(f0[Y]) + box_extents[Y]*@abs(f0[X]))) {
return false;
}
// Test axis a21
const a21 = Vec3d{-f1[Y], f1[X], 0};
if(!test_axis(a21, v0, v1, v2, box_extents[X]*@abs(f1[Y]) + box_extents[Y]*@abs(f1[X]))) {
return false;
}
// Test axis a22
const a22 = Vec3d{-f2[Y], f2[X], 0};
if(!test_axis(a22, v0, v1, v2, box_extents[X]*@abs(f2[Y]) + box_extents[Y]*@abs(f2[X]))) {
return false;
}
// Test the three axes corresponding to the face normals of AABB
if(@max(v0[X], @max(v1[X], v2[X])) < -box_extents[X] or @min(v0[X], @min(v1[X], v2[X])) > box_extents[X]) {
return false;
}
if(@max(v0[Y], @max(v1[Y], v2[Y])) < -box_extents[Y] or @min(v0[Y], @min(v1[Y], v2[Y])) > box_extents[Y]) {
return false;
}
if(@max(v0[Z], @max(v1[Z], v2[Z])) < -box_extents[Z] or @min(v0[Z], @min(v1[Z], v2[Z])) > box_extents[Z]) {
return false;
}
// Test separating axis corresponding to triangle face normal
const plane_normal = vec.cross(f0, f1);
const plane_distance = @abs(vec.dot(plane_normal, v0));
const r = box_extents[X]*@abs(plane_normal[X]) + box_extents[Y]*@abs(plane_normal[Y]) + box_extents[Z]*@abs(plane_normal[Z]);
return plane_distance <= r;
}
fn test_axis(axis: Vec3d, v0: Vec3d, v1: Vec3d, v2: Vec3d, r: f64) bool {
const p0 = vec.dot(v0, axis);
const p1 = vec.dot(v1, axis);
const p2 = vec.dot(v2, axis);
const min_p = @min(p0, @min(p1, p2));
const max_p = @max(p0, @max(p1, p2));
return @max(-max_p, min_p) <= r;
}
};
const Direction = enum(u2) {x = 0, y = 1, z = 2};

View File

@ -7,6 +7,7 @@ const main = @import("main");
const vec = @import("vec.zig");
const Vec3i = vec.Vec3i;
const Vec3f = vec.Vec3f;
const Vec3d = vec.Vec3d;
const Vec2f = vec.Vec2f;
const Mat4f = vec.Mat4f;
const FaceData = main.renderer.chunk_meshing.FaceData;
@ -202,75 +203,32 @@ pub const Model = struct {
return modelIndex;
}
fn rayIntersectsTriangle(ray_origin: Vec3f, ray_direction: Vec3f, triangle: *const [3]Vec3f) bool {
const epsilon = 1e-8;
const v0 = triangle[0];
const v1 = triangle[1];
const v2 = triangle[2];
const edge1 = v1 - v0;
const edge2 = v2 - v0;
const h = vec.cross(ray_direction, edge2);
const a = vec.dot(edge1, h);
if(-epsilon < a and a < epsilon) {
return false;
}
const f = 1.0/a;
const s = ray_origin - v0;
const u = f*vec.dot(s, h);
if(u < 0.0 or u > 1.0) {
return false;
}
const q = vec.cross(s, edge1);
const v = f*vec.dot(ray_direction, q);
if(v < 0.0 or u + v > 1.0) {
return false;
}
const t = f*vec.dot(edge2, q);
return t > epsilon;
}
fn generateCollision(self: *Model, modelQuads: []QuadInfo) void {
var grid: [meshGridSize][meshGridSize][meshGridSize]bool = undefined;
const voxelSize: Vec3f = @splat(1.0/@as(f32, meshGridSize));
for(0..meshGridSize) |x| {
for(0..meshGridSize) |y| {
for(0..meshGridSize) |z| {
grid[x][y][z] = false;
const blockX = (@as(f32, @floatFromInt(x)) + 0.5)/meshGridSize;
const blockY = (@as(f32, @floatFromInt(y)) + 0.5)/meshGridSize;
const blockZ = (@as(f32, @floatFromInt(z)) + 0.5)/meshGridSize;
const blockX = @as(f32, @floatFromInt(x))/meshGridSize;
const blockY = @as(f32, @floatFromInt(y))/meshGridSize;
const blockZ = @as(f32, @floatFromInt(z))/meshGridSize;
const pos = Vec3f{blockX, blockY, blockZ};
// Fences have weird models, so this is necesarry to make them work
for(Neighbor.iterable) |neighbor| {
const dir: Vec3f = @floatFromInt(neighbor.relPos());
const voxel = AABB {.min = @floatCast(pos), .max = @floatCast(pos + voxelSize)};
for(modelQuads) |quad| {
const shift = quad.normalVec() * voxelSize * @as(Vec3f, @splat(0.5));
const triangle1: [3]Vec3d = .{
@floatCast(quad.cornerVec(0) - shift),
@floatCast(quad.cornerVec(1) - shift),
@floatCast(quad.cornerVec(2) - shift),
};
const triangle2: [3]Vec3d = .{
@floatCast(quad.cornerVec(1) - shift),
@floatCast(quad.cornerVec(2) - shift),
@floatCast(quad.cornerVec(3) - shift),
};
var signed_intersections: i32 = 0;
for(modelQuads) |quad| {
const triangle1: [3]Vec3f = .{
quad.cornerVec(0),
quad.cornerVec(1),
quad.cornerVec(2),
};
const triangle2: [3]Vec3f = .{
quad.cornerVec(1),
quad.cornerVec(2),
quad.cornerVec(3),
};
if(rayIntersectsTriangle(pos, dir, &triangle1) or rayIntersectsTriangle(pos, dir, &triangle2)) {
if(vec.dot(dir, quad.normalVec()) > 0) {
signed_intersections -= 1;
} else {
signed_intersections += 1;
}
}
}
if(signed_intersections != 0) {
if(voxel.intersectsTriangle(triangle1) or voxel.intersectsTriangle(triangle2)) {
grid[x][y][z] = true;
break;
}