mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-22 02:43:12 -04:00
Use AABBs for collision (#1726)
Fixes #826 Fixes #495 --------- Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> Co-authored-by: IntegratedQuantum <jahe788@gmail.com>
This commit is contained in:
parent
b47579e41e
commit
83d37bca40
195
src/game.zig
195
src/game.zig
@ -52,102 +52,22 @@ pub const camera = struct { // MARK: camera
|
||||
};
|
||||
|
||||
pub const collision = struct {
|
||||
pub fn triangleAABB(triangle: [3]Vec3d, box_center: Vec3d, box_extents: Vec3d) bool {
|
||||
const X = 0;
|
||||
const Y = 1;
|
||||
const Z = 2;
|
||||
pub const Box = struct {
|
||||
min: Vec3d,
|
||||
max: Vec3d,
|
||||
|
||||
// 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;
|
||||
pub fn center(self: Box) Vec3d {
|
||||
return (self.min + self.max)*@as(Vec3d, @splat(0.5));
|
||||
}
|
||||
|
||||
// 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;
|
||||
pub fn extent(self: Box) Vec3d {
|
||||
return (self.max - self.min)*@as(Vec3d, @splat(0.5));
|
||||
}
|
||||
|
||||
// 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;
|
||||
pub fn intersects(self: Box, other: Box) bool {
|
||||
return @reduce(.And, (self.max >= other.min)) and @reduce(.And, (self.min <= other.max));
|
||||
}
|
||||
|
||||
// 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};
|
||||
|
||||
@ -158,61 +78,21 @@ pub const collision = struct {
|
||||
const model = block.mode().model(block).model();
|
||||
|
||||
const pos = Vec3d{@floatFromInt(x), @floatFromInt(y), @floatFromInt(z)};
|
||||
const entityCollision = Box{.min = entityPosition - entityBoundingBoxExtent, .max = entityPosition + entityBoundingBoxExtent};
|
||||
|
||||
for(model.neighborFacingQuads) |quads| {
|
||||
for(quads) |quadIndex| {
|
||||
const quad = quadIndex.quadInfo();
|
||||
if(triangleAABB(.{quad.cornerVec(0) + quad.normalVec() + pos, quad.cornerVec(2) + quad.normalVec() + pos, quad.cornerVec(1) + quad.normalVec() + pos}, entityPosition, entityBoundingBoxExtent)) {
|
||||
const min = @min(@min(quad.cornerVec(0), quad.cornerVec(1)), @min(quad.cornerVec(2), quad.cornerVec(3))) + quad.normalVec() + pos;
|
||||
const max = @max(@max(quad.cornerVec(0), quad.cornerVec(1)), @max(quad.cornerVec(2), quad.cornerVec(3))) + quad.normalVec() + pos;
|
||||
const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max));
|
||||
if(dist < minDistance) {
|
||||
resultBox = .{.min = min, .max = max};
|
||||
minDistance = dist;
|
||||
} else if(dist == minDistance) {
|
||||
resultBox.?.min = @min(resultBox.?.min, min);
|
||||
resultBox.?.max = @min(resultBox.?.max, max);
|
||||
}
|
||||
}
|
||||
if(triangleAABB(.{quad.cornerVec(1) + quad.normalVec() + pos, quad.cornerVec(2) + quad.normalVec() + pos, quad.cornerVec(3) + quad.normalVec() + pos}, entityPosition, entityBoundingBoxExtent)) {
|
||||
const min = @min(@min(quad.cornerVec(0), quad.cornerVec(1)), @min(quad.cornerVec(2), quad.cornerVec(3))) + quad.normalVec() + pos;
|
||||
const max = @max(@max(quad.cornerVec(0), quad.cornerVec(1)), @max(quad.cornerVec(2), quad.cornerVec(3))) + quad.normalVec() + pos;
|
||||
const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max));
|
||||
if(dist < minDistance) {
|
||||
resultBox = .{.min = min, .max = max};
|
||||
minDistance = dist;
|
||||
} else if(dist == minDistance) {
|
||||
resultBox.?.min = @min(resultBox.?.min, min);
|
||||
resultBox.?.max = @min(resultBox.?.max, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(model.collision) |relativeBlockCollision| {
|
||||
const blockCollision = Box{.min = relativeBlockCollision.min + pos, .max = relativeBlockCollision.max + pos};
|
||||
if(blockCollision.intersects(entityCollision)) {
|
||||
const dotMin = vec.dot(directionVector, blockCollision.min);
|
||||
const dotMax = vec.dot(directionVector, blockCollision.max);
|
||||
|
||||
for(model.internalQuads) |quadIndex| {
|
||||
const quad = quadIndex.quadInfo();
|
||||
if(triangleAABB(.{quad.cornerVec(0) + pos, quad.cornerVec(2) + pos, quad.cornerVec(1) + pos}, entityPosition, entityBoundingBoxExtent)) {
|
||||
const min = @min(@min(quad.cornerVec(0), quad.cornerVec(1)), @min(quad.cornerVec(2), quad.cornerVec(3))) + pos;
|
||||
const max = @max(@max(quad.cornerVec(0), quad.cornerVec(1)), @max(quad.cornerVec(2), quad.cornerVec(3))) + pos;
|
||||
const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max));
|
||||
if(dist < minDistance) {
|
||||
resultBox = .{.min = min, .max = max};
|
||||
minDistance = dist;
|
||||
} else if(dist == minDistance) {
|
||||
resultBox.?.min = @min(resultBox.?.min, min);
|
||||
resultBox.?.max = @min(resultBox.?.max, max);
|
||||
}
|
||||
}
|
||||
if(triangleAABB(.{quad.cornerVec(1) + pos, quad.cornerVec(2) + pos, quad.cornerVec(3) + pos}, entityPosition, entityBoundingBoxExtent)) {
|
||||
const min = @min(@min(quad.cornerVec(0), quad.cornerVec(1)), @min(quad.cornerVec(2), quad.cornerVec(3))) + pos;
|
||||
const max = @max(@max(quad.cornerVec(0), quad.cornerVec(1)), @max(quad.cornerVec(2), quad.cornerVec(3))) + pos;
|
||||
const dist = @min(vec.dot(directionVector, min), vec.dot(directionVector, max));
|
||||
if(dist < minDistance) {
|
||||
resultBox = .{.min = min, .max = max};
|
||||
minDistance = dist;
|
||||
} else if(dist == minDistance) {
|
||||
resultBox.?.min = @min(resultBox.?.min, min);
|
||||
resultBox.?.max = @min(resultBox.?.max, max);
|
||||
const distance = @min(dotMin, dotMax);
|
||||
|
||||
if(distance < minDistance) {
|
||||
resultBox = blockCollision;
|
||||
minDistance = distance;
|
||||
} else if(distance == minDistance) {
|
||||
resultBox = .{.min = @min(resultBox.?.min, blockCollision.min), .max = @max(resultBox.?.max, blockCollision.max)};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -459,18 +339,14 @@ pub const collision = struct {
|
||||
fn isBlockIntersecting(block: Block, posX: i32, posY: i32, posZ: i32, center: Vec3d, extent: Vec3d) bool {
|
||||
const model = block.mode().model(block).model();
|
||||
const position = Vec3d{@floatFromInt(posX), @floatFromInt(posY), @floatFromInt(posZ)};
|
||||
for(model.neighborFacingQuads) |quads| {
|
||||
for(quads) |quadIndex| {
|
||||
const quad = quadIndex.quadInfo();
|
||||
if(triangleAABB(.{quad.cornerVec(0) + quad.normalVec() + position, quad.cornerVec(2) + quad.normalVec() + position, quad.cornerVec(1) + quad.normalVec() + position}, center, extent) or
|
||||
triangleAABB(.{quad.cornerVec(1) + quad.normalVec() + position, quad.cornerVec(2) + quad.normalVec() + position, quad.cornerVec(3) + quad.normalVec() + position}, center, extent)) return true;
|
||||
const entityBox = Box{.min = center - extent, .max = center + extent};
|
||||
for(model.collision) |relativeBlockCollision| {
|
||||
const blockBox = Box{.min = position + relativeBlockCollision.min, .max = position + relativeBlockCollision.max};
|
||||
if(blockBox.intersects(entityBox)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(model.internalQuads) |quadIndex| {
|
||||
const quad = quadIndex.quadInfo();
|
||||
if(triangleAABB(.{quad.cornerVec(0) + position, quad.cornerVec(2) + position, quad.cornerVec(1) + position}, center, extent) or
|
||||
triangleAABB(.{quad.cornerVec(1) + position, quad.cornerVec(2) + position, quad.cornerVec(3) + position}, center, extent)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -510,19 +386,6 @@ pub const collision = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const Box = struct {
|
||||
min: Vec3d,
|
||||
max: Vec3d,
|
||||
|
||||
pub fn center(self: Box) Vec3d {
|
||||
return (self.min + self.max)*@as(Vec3d, @splat(0.5));
|
||||
}
|
||||
|
||||
pub fn extent(self: Box) Vec3d {
|
||||
return (self.max - self.min)*@as(Vec3d, @splat(0.5));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const Gamemode = enum(u8) {survival = 0, creative = 1};
|
||||
|
216
src/models.zig
216
src/models.zig
@ -7,10 +7,13 @@ 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;
|
||||
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
|
||||
const Box = main.game.collision.Box;
|
||||
|
||||
var quadSSBO: graphics.SSBO = undefined;
|
||||
|
||||
@ -40,6 +43,8 @@ const ExtraQuadInfo = struct {
|
||||
};
|
||||
|
||||
const gridSize = 4096;
|
||||
const collisionGridSize = 16;
|
||||
const CollisionGridInteger = std.meta.Int(.unsigned, collisionGridSize);
|
||||
|
||||
fn snapToGrid(x: anytype) @TypeOf(x) {
|
||||
const T = @TypeOf(x);
|
||||
@ -92,6 +97,7 @@ pub const Model = struct {
|
||||
allNeighborsOccluded: bool,
|
||||
noNeighborsOccluded: bool,
|
||||
hasNeighborFacingQuads: bool,
|
||||
collision: []Box,
|
||||
|
||||
fn getFaceNeighbor(quad: *const QuadInfo) ?chunk.Neighbor {
|
||||
var allZero: @Vector(3, bool) = .{true, true, true};
|
||||
@ -195,9 +201,218 @@ pub const Model = struct {
|
||||
self.allNeighborsOccluded = self.allNeighborsOccluded and self.isNeighborOccluded[neighbor];
|
||||
self.noNeighborsOccluded = self.noNeighborsOccluded and !self.isNeighborOccluded[neighbor];
|
||||
}
|
||||
generateCollision(self, adjustedQuads);
|
||||
return modelIndex;
|
||||
}
|
||||
|
||||
fn edgeInterp(y: f32, x0: f32, y0: f32, x1: f32, y1: f32) f32 {
|
||||
if(y1 == y0) return x0;
|
||||
return x0 + (x1 - x0)*(y - y0)/(y1 - y0);
|
||||
}
|
||||
|
||||
fn solveDepth(normal: Vec3f, v0: Vec3f, xIndex: usize, yIndex: usize, zIndex: usize, u: f32, v: f32) f32 {
|
||||
const nX = normal[xIndex];
|
||||
const nY = normal[yIndex];
|
||||
const nZ = normal[zIndex];
|
||||
|
||||
const planeOffset = -vec.dot(v0, normal);
|
||||
|
||||
return (-(nX*u + nY*v + planeOffset))/nZ;
|
||||
}
|
||||
|
||||
fn rasterize(triangle: [3]Vec3f, grid: *[collisionGridSize][collisionGridSize]CollisionGridInteger, normal: Vec3f) void {
|
||||
var xIndex: usize = undefined;
|
||||
var yIndex: usize = undefined;
|
||||
var zIndex: usize = undefined;
|
||||
|
||||
const v0 = triangle[0]*@as(Vec3f, @splat(@floatFromInt(collisionGridSize)));
|
||||
const v1 = triangle[1]*@as(Vec3f, @splat(@floatFromInt(collisionGridSize)));
|
||||
const v2 = triangle[2]*@as(Vec3f, @splat(@floatFromInt(collisionGridSize)));
|
||||
|
||||
const absNormal = @abs(normal);
|
||||
if(absNormal[0] >= absNormal[1] and absNormal[0] >= absNormal[2]) {
|
||||
xIndex = 1;
|
||||
yIndex = 2;
|
||||
zIndex = 0;
|
||||
} else if(absNormal[1] >= absNormal[0] and absNormal[1] >= absNormal[2]) {
|
||||
xIndex = 0;
|
||||
yIndex = 2;
|
||||
zIndex = 1;
|
||||
} else {
|
||||
xIndex = 0;
|
||||
yIndex = 1;
|
||||
zIndex = 2;
|
||||
}
|
||||
|
||||
const min: Vec3f = @min(v0, v1, v2);
|
||||
const max: Vec3f = @max(v0, v1, v2);
|
||||
|
||||
const voxelMin: Vec3i = @max(@as(Vec3i, @intFromFloat(@floor(min))), @as(Vec3i, @splat(0)));
|
||||
const voxelMax: Vec3i = @max(@as(Vec3i, @intFromFloat(@ceil(max))), @as(Vec3i, @splat(0)));
|
||||
|
||||
var p0 = Vec2f{v0[xIndex], v0[yIndex]};
|
||||
var p1 = Vec2f{v1[xIndex], v1[yIndex]};
|
||||
var p2 = Vec2f{v2[xIndex], v2[yIndex]};
|
||||
|
||||
if(p0[1] > p1[1]) {
|
||||
std.mem.swap(Vec2f, &p0, &p1);
|
||||
}
|
||||
if(p0[1] > p2[1]) {
|
||||
std.mem.swap(Vec2f, &p0, &p2);
|
||||
}
|
||||
if(p1[1] > p2[1]) {
|
||||
std.mem.swap(Vec2f, &p1, &p2);
|
||||
}
|
||||
|
||||
for(@intCast(voxelMin[yIndex])..@intCast(voxelMax[yIndex])) |y| {
|
||||
if(y >= collisionGridSize) continue;
|
||||
const yf = std.math.clamp(@as(f32, @floatFromInt(y)) + 0.5, min[yIndex], max[yIndex]);
|
||||
var xa: f32 = undefined;
|
||||
var xb: f32 = undefined;
|
||||
if(yf < p1[1]) {
|
||||
xa = edgeInterp(yf, p0[0], p0[1], p1[0], p1[1]);
|
||||
xb = edgeInterp(yf, p0[0], p0[1], p2[0], p2[1]);
|
||||
} else {
|
||||
xa = edgeInterp(yf, p1[0], p1[1], p2[0], p2[1]);
|
||||
xb = edgeInterp(yf, p0[0], p0[1], p2[0], p2[1]);
|
||||
}
|
||||
|
||||
const xStart: f32 = @min(xa, xb);
|
||||
const xEnd: f32 = @max(xa, xb);
|
||||
|
||||
const voxelXStart: usize = @intFromFloat(@max(@floor(xStart), 0.0));
|
||||
const voxelXEnd: usize = @intFromFloat(@max(@ceil(xEnd), 0.0));
|
||||
|
||||
for(voxelXStart..voxelXEnd) |x| {
|
||||
if(x < 0 or x >= collisionGridSize) continue;
|
||||
const xf = std.math.clamp(@as(f32, @floatFromInt(x)) + 0.5, xStart, xEnd);
|
||||
|
||||
const zf = solveDepth(normal, v0, xIndex, yIndex, zIndex, xf, yf);
|
||||
if(zf < 0.0) continue;
|
||||
const z: usize = @intFromFloat(zf);
|
||||
|
||||
if(z >= collisionGridSize) continue;
|
||||
|
||||
const pos: [3]usize = .{x, y, z};
|
||||
var realPos: [3]usize = undefined;
|
||||
realPos[xIndex] = pos[0];
|
||||
realPos[yIndex] = pos[1];
|
||||
realPos[zIndex] = pos[2];
|
||||
grid[realPos[0]][realPos[1]] |= @as(CollisionGridInteger, 1) << @intCast(realPos[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generateCollision(self: *Model, modelQuads: []QuadInfo) void {
|
||||
var hollowGrid: [collisionGridSize][collisionGridSize]CollisionGridInteger = @splat(@splat(0));
|
||||
const voxelSize: Vec3f = @splat(1.0/@as(f32, collisionGridSize));
|
||||
|
||||
for(modelQuads) |quad| {
|
||||
var shift = Vec3f{0, 0, 0};
|
||||
for(0..3) |i| {
|
||||
if(@abs(quad.normalVec()[i]) == 1.0 and @floor(quad.corners[0][i]*collisionGridSize) == quad.corners[0][i]*collisionGridSize) {
|
||||
shift = quad.normalVec()*voxelSize*@as(Vec3f, @splat(0.5));
|
||||
}
|
||||
}
|
||||
const triangle1: [3]Vec3f = .{
|
||||
quad.cornerVec(0) - shift,
|
||||
quad.cornerVec(1) - shift,
|
||||
quad.cornerVec(2) - shift,
|
||||
};
|
||||
const triangle2: [3]Vec3f = .{
|
||||
quad.cornerVec(1) - shift,
|
||||
quad.cornerVec(2) - shift,
|
||||
quad.cornerVec(3) - shift,
|
||||
};
|
||||
|
||||
rasterize(triangle1, &hollowGrid, quad.normalVec());
|
||||
rasterize(triangle2, &hollowGrid, quad.normalVec());
|
||||
}
|
||||
|
||||
const allOnes = ~@as(CollisionGridInteger, 0);
|
||||
var grid: [collisionGridSize][collisionGridSize]CollisionGridInteger = @splat(@splat(allOnes));
|
||||
|
||||
var floodfillQueue = main.utils.CircularBufferQueue(struct {x: usize, y: usize, val: CollisionGridInteger}).init(main.stackAllocator, 1024);
|
||||
defer floodfillQueue.deinit();
|
||||
|
||||
for(0..collisionGridSize) |x| {
|
||||
for(0..collisionGridSize) |y| {
|
||||
var val = 1 | @as(CollisionGridInteger, 1) << (@bitSizeOf(CollisionGridInteger) - 1);
|
||||
if(x == 0 or x == collisionGridSize - 1 or y == 0 or y == collisionGridSize - 1) val = allOnes;
|
||||
|
||||
floodfillQueue.pushBack(.{.x = x, .y = y, .val = val});
|
||||
}
|
||||
}
|
||||
|
||||
while(floodfillQueue.popFront()) |elem| {
|
||||
const oldValue = grid[elem.x][elem.y];
|
||||
const newValue = oldValue & ~(~hollowGrid[elem.x][elem.y] & elem.val);
|
||||
if(oldValue == newValue) continue;
|
||||
grid[elem.x][elem.y] = newValue;
|
||||
|
||||
if(elem.x != 0) floodfillQueue.pushBack(.{.x = elem.x - 1, .y = elem.y, .val = ~newValue});
|
||||
if(elem.x != collisionGridSize - 1) floodfillQueue.pushBack(.{.x = elem.x + 1, .y = elem.y, .val = ~newValue});
|
||||
if(elem.y != 0) floodfillQueue.pushBack(.{.x = elem.x, .y = elem.y - 1, .val = ~newValue});
|
||||
if(elem.y != collisionGridSize - 1) floodfillQueue.pushBack(.{.x = elem.x, .y = elem.y + 1, .val = ~newValue});
|
||||
floodfillQueue.pushBack(.{.x = elem.x, .y = elem.y, .val = ~newValue << 1 | ~newValue >> 1});
|
||||
}
|
||||
|
||||
var collision: main.List(Box) = .init(main.globalAllocator);
|
||||
|
||||
for(0..collisionGridSize) |x| {
|
||||
for(0..collisionGridSize) |y| {
|
||||
while(grid[x][y] != 0) {
|
||||
const startZ = @ctz(grid[x][y]);
|
||||
const height = @min(@bitSizeOf(CollisionGridInteger) - startZ, @ctz(~grid[x][y] >> @intCast(startZ)));
|
||||
const mask = allOnes << @intCast(startZ) & ~((allOnes << 1) << @intCast(height + startZ - 1));
|
||||
|
||||
const boxMin = Vec3i{@intCast(x), @intCast(y), startZ};
|
||||
var boxMax = Vec3i{@intCast(x + 1), @intCast(y + 1), startZ + height};
|
||||
|
||||
while(canExpand(&grid, boxMin, boxMax, .x, mask)) boxMax[0] += 1;
|
||||
while(canExpand(&grid, boxMin, boxMax, .y, mask)) boxMax[1] += 1;
|
||||
disableAll(&grid, boxMin, boxMax, mask);
|
||||
|
||||
const min = @as(Vec3f, @floatFromInt(boxMin))/@as(Vec3f, @splat(collisionGridSize));
|
||||
const max = @as(Vec3f, @floatFromInt(boxMax))/@as(Vec3f, @splat(collisionGridSize));
|
||||
|
||||
collision.append(Box{.min = min, .max = max});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.collision = collision.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn allTrue(grid: *const [collisionGridSize][collisionGridSize]CollisionGridInteger, min: Vec3i, max: Vec3i, mask: CollisionGridInteger) bool {
|
||||
if(max[0] > collisionGridSize or max[1] > collisionGridSize) {
|
||||
return false;
|
||||
}
|
||||
for(@intCast(min[0])..@intCast(max[0])) |x| {
|
||||
for(@intCast(min[1])..@intCast(max[1])) |y| {
|
||||
if((grid[x][y] & mask) != mask) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn disableAll(grid: *[collisionGridSize][collisionGridSize]CollisionGridInteger, min: Vec3i, max: Vec3i, mask: CollisionGridInteger) void {
|
||||
for(@intCast(min[0])..@intCast(max[0])) |x| {
|
||||
for(@intCast(min[1])..@intCast(max[1])) |y| {
|
||||
grid[x][y] &= ~mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn canExpand(grid: *const [collisionGridSize][collisionGridSize]CollisionGridInteger, min: Vec3i, max: Vec3i, dir: enum {x, y}, mask: CollisionGridInteger) bool {
|
||||
return switch(dir) {
|
||||
.x => allTrue(grid, Vec3i{max[0], min[1], min[2]}, Vec3i{max[0] + 1, max[1], max[2]}, mask),
|
||||
.y => allTrue(grid, Vec3i{min[0], max[1], min[2]}, Vec3i{max[0], max[1] + 1, max[2]}, mask),
|
||||
};
|
||||
}
|
||||
|
||||
fn addVert(vert: Vec3f, vertList: *main.List(Vec3f)) usize {
|
||||
const ind = for(vertList.*.items, 0..) |vertex, index| {
|
||||
if(vertex == vert) break index;
|
||||
@ -386,6 +601,7 @@ pub const Model = struct {
|
||||
main.globalAllocator.free(self.neighborFacingQuads[i]);
|
||||
}
|
||||
main.globalAllocator.free(self.internalQuads);
|
||||
main.globalAllocator.free(self.collision);
|
||||
}
|
||||
|
||||
pub fn getRawFaces(model: Model, quadList: *main.List(QuadInfo)) void {
|
||||
|
Loading…
x
Reference in New Issue
Block a user