Add Frustum Culling.

This commit is contained in:
IntegratedQuantum 2022-09-15 13:04:13 +02:00
parent e6659675b2
commit faa018093f
6 changed files with 109 additions and 26 deletions

View File

@ -141,10 +141,10 @@ pub const BlockPalette = struct {
try self.palette.append(try allocator.dupe(u8, "cubyz:air"));
} else {
var palette = try main.threadAllocator.alloc(?[]const u8, jsonObject.JsonObject.count());
defer main.threadAllocator.free(palette);
for(palette) |*val| {
val.* = null;
}
defer main.threadAllocator.free(palette);
var iterator = jsonObject.JsonObject.iterator();
while(iterator.next()) |entry| {
palette[entry.value_ptr.as(usize, std.math.maxInt(usize))] = entry.key_ptr.*;

View File

@ -8,25 +8,25 @@ const graphics = @import("graphics.zig");
const Fog = graphics.Fog;
pub const camera = struct {
var rotation: Vec3f = Vec3f{.x = 0, .y = 0, .z = 0};
var direction: Vec3f = Vec3f{0, 0, 0};
var rotation: Vec3f = Vec3f{.x=0, .y=0, .z=0};
var direction: Vec3f = Vec3f{.x=0, .y=0, .z=0};
pub var viewMatrix: Mat4f = Mat4f.identity();
pub fn moveRotation(mouseX: f32, mouseY: f32) void {
// Mouse movement along the x-axis rotates the image along the y-axis.
rotation.x += mouseY;
if(rotation.x > std.math.pi/2) {
rotation.x = std.math.pi/2;
} else if(rotation.x < -std.math.pi/2) {
rotation.x = -std.math.pi/2;
if(rotation.x > std.math.pi/2.0) {
rotation.x = std.math.pi/2.0;
} else if(rotation.x < -std.math.pi/2.0) {
rotation.x = -std.math.pi/2.0;
}
// Mouse movement along the y-axis rotates the image along the x-axis.
rotation.y += mouseX;
direction = Vec3f.rotateX(Vec3f{0, 0, -1}, rotation.x).rotateY(rotation.y);
direction = Vec3f.rotateX(Vec3f{.x=0, .y=0, .z=-1}, rotation.x).rotateY(rotation.y);
}
pub fn updateViewMatrix() void {
viewMatrix = Mat4f.rotationY(rotation.y).mul(Mat4f.rotationX(rotation.x));
viewMatrix = Mat4f.rotationX(rotation.x).mul(Mat4f.rotationY(rotation.y));
}
};

View File

@ -7,6 +7,7 @@ const game = @import("game.zig");
const graphics = @import("graphics.zig");
const renderer = @import("renderer.zig");
const network = @import("network.zig");
const settings = @import("settings.zig");
const utils = @import("utils.zig");
const Vec2f = @import("vec.zig").Vec2f;
@ -61,7 +62,7 @@ pub const Window = struct {
std.log.info("Framebuffer: {}, {}", .{newWidth, newHeight});
width = @intCast(u31, newWidth);
height = @intCast(u31, newHeight);
renderer.updateViewport(width, height, 45);// TODO: Get fov from settings.
renderer.updateViewport(width, height, settings.fov);
}
fn glDebugOutput(_: c_uint, typ: c_uint, _: c_uint, severity: c_uint, length: c_int, message: [*c]const u8, _: ?*const anyopaque) callconv(.C) void {
if(typ == c.GL_DEBUG_TYPE_ERROR or typ == c.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR or typ == c.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR or typ == c.GL_DEBUG_TYPE_PORTABILITY or typ == c.GL_DEBUG_TYPE_PERFORMANCE) {
@ -196,6 +197,7 @@ pub fn main() !void {
std.log.err("Got opengl error: {}", .{err});
}
}
game.camera.moveRotation(0.01, 0);
c.glfwSwapBuffers(Window.window);
c.glfwPollEvents();
try renderer.RenderOctree.update(conn2, .{.x = 25, .y = 11, .z = -703}, 4, 2.0);

View File

@ -213,11 +213,8 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, directionalLight: Vec3f,
// Meshes.cleanUp();
game.camera.updateViewMatrix();
//TODO: // Uses FrustumCulling on the chunks.
// Matrix4f frustumMatrix = new Matrix4f();
// frustumMatrix.set(frustumProjectionMatrix);
// frustumMatrix.mul(Camera.getViewMatrix());
// frustumInt.set(frustumMatrix);
// Uses FrustumCulling on the chunks.
var frustum = Frustum.init(Vec3f{.x=0, .y=0, .z=0}, game.camera.viewMatrix, settings.fov, zFarLOD, main.Window.width, main.Window.height);
const time = @intCast(u32, std.time.milliTimestamp() & std.math.maxInt(u32));
const waterFog = Fog{.active=true, .color=.{.x=0.0, .y=0.1, .z=0.2}, .density=0.1};
@ -243,7 +240,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, directionalLight: Vec3f,
// SimpleList<ReducedChunkMesh> visibleReduced = new SimpleList<ReducedChunkMesh>(new ReducedChunkMesh[64]);
var meshes = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.threadAllocator);
defer meshes.deinit();
try RenderOctree.getRenderChunks(playerPos, &meshes);
try RenderOctree.getRenderChunks(playerPos, frustum, &meshes);
// for (ChunkMesh mesh : Cubyz.chunkTree.getRenderChunks(frustumInt, x0, y0, z0)) {
// if (mesh instanceof NormalChunkMesh) {
// visibleChunks.add((NormalChunkMesh)mesh);
@ -351,9 +348,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, directionalLight: Vec3f,
//TODO EntityRenderer.renderNames(playerPosition);
}
// private final Matrix4f frustumProjectionMatrix = new Matrix4f();
// private final FrustumIntersection frustumInt = new FrustumIntersection();
//
// private float playerBobbing;
// private boolean bobbingUp;
//
@ -395,6 +389,45 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, directionalLight: Vec3f,
// }
//}
pub const Frustum = struct {
const Plane = struct {
pos: Vec3f,
norm: Vec3f,
};
planes: [5]Plane, // Who cares about the near plane anyways?
pub fn init(cameraPos: Vec3f, rotationMatrix: Mat4f, fovY: f32, _zFar: f32, width: u31, height: u31) Frustum {
var invRotationMatrix = rotationMatrix.transpose();
var cameraDir = Vec3f.xyz(invRotationMatrix.mulVec(Vec4f{.x=0, .y=0, .z=1, .w=1}));
var cameraUp = Vec3f.xyz(invRotationMatrix.mulVec(Vec4f{.x=0, .y=1, .z=0, .w=1}));
var cameraRight = Vec3f.xyz(invRotationMatrix.mulVec(Vec4f{.x=1, .y=0, .z=0, .w=1}));
const halfVSide = _zFar*std.math.tan(std.math.degreesToRadians(f32, fovY)*0.5);
const halfHSide = halfVSide*@intToFloat(f32, width)/@intToFloat(f32, height);
const frontMultFar = cameraDir.mulScalar(_zFar);
var self: Frustum = undefined;
self.planes[0] = Plane{.pos = cameraPos.add(frontMultFar), .norm=cameraDir.mulScalar(-1)}; // far
self.planes[1] = Plane{.pos = cameraPos, .norm=cameraUp.cross(frontMultFar.add(cameraRight.mulScalar(halfHSide)))}; // right
self.planes[2] = Plane{.pos = cameraPos, .norm=frontMultFar.sub(cameraRight.mulScalar(halfHSide)).cross(cameraUp)}; // left
self.planes[3] = Plane{.pos = cameraPos, .norm=cameraRight.cross(frontMultFar.sub(cameraUp.mulScalar(halfVSide)))}; // top
self.planes[4] = Plane{.pos = cameraPos, .norm=frontMultFar.add(cameraUp.mulScalar(halfVSide)).cross(cameraRight)}; // bottom
return self;
}
pub fn testAAB(self: Frustum, pos: Vec3f, dim: Vec3f) bool {
inline for(self.planes) |plane| {
var dist: f32 = pos.sub(plane.pos).dot(plane.norm);
// Find the most positive corner:
dist += @maximum(0, dim.x*plane.norm.x);
dist += @maximum(0, dim.y*plane.norm.y);
dist += @maximum(0, dim.z*plane.norm.z);
if(dist < 128) return false;
}
return true;
}
};
pub const RenderOctree = struct {
pub var allocator: std.mem.Allocator = undefined;
var gpa: std.heap.GeneralPurposeAllocator(.{}) = undefined;
@ -490,17 +523,25 @@ pub const RenderOctree = struct {
}
}
fn getChunks(self: *Node, playerPos: Vec3d, meshes: *std.ArrayList(*chunk.meshing.ChunkMesh)) !void {
fn getChunks(self: *Node, playerPos: Vec3d, frustum: Frustum, meshes: *std.ArrayList(*chunk.meshing.ChunkMesh)) !void {
self.mutex.lock();
defer self.mutex.unlock();
if(self.children) |children| {
for(children) |child| {
try child.getChunks(playerPos, meshes);
try child.getChunks(playerPos, frustum, meshes);
}
} else {
// TODO: if(testFrustum(frustumInt, x0, y0, z0)) {
if(frustum.testAAB(Vec3f{
.x = @floatCast(f32, @intToFloat(f64, self.mesh.pos.wx) - playerPos.x),
.y = @floatCast(f32, @intToFloat(f64, self.mesh.pos.wy) - playerPos.y),
.z = @floatCast(f32, @intToFloat(f64, self.mesh.pos.wz) - playerPos.z),
}, Vec3f{
.x = @intToFloat(f32, self.size),
.y = @intToFloat(f32, self.size),
.z = @intToFloat(f32, self.size),
})) {
try meshes.append(&self.mesh);
//}
}
}
}
// TODO:
@ -693,12 +734,12 @@ pub const RenderOctree = struct {
try updatableList.append(mesh);
}
pub fn getRenderChunks(playerPos: Vec3d, meshes: *std.ArrayList(*chunk.meshing.ChunkMesh)) !void {
pub fn getRenderChunks(playerPos: Vec3d, frustum: Frustum, meshes: *std.ArrayList(*chunk.meshing.ChunkMesh)) !void {
mutex.lock();
defer mutex.unlock();
var iterator = roots.valueIterator();
while(iterator.next()) |node| {
try node.*.getChunks(playerPos, meshes);
try node.*.getChunks(playerPos, frustum, meshes);
}
}
// TODO:

View File

@ -5,4 +5,8 @@ pub const connectionTimeout = 60000;
pub const version = "0.12.0";
pub const highestLOD: u5 = 5;
pub const highestLOD: u5 = 5;
pub var fov: f32 = 45;

View File

@ -39,6 +39,18 @@ pub const Vec3f = extern struct {// This one gets a bit of extra functionality f
.z = self.z,
};
}
pub fn cross(self: @This(), other: @This()) @This() {
return @This() {
.x = self.y*other.z - self.z*other.y,
.y = self.z*other.x - self.x*other.z,
.z = self.x*other.y - self.y*other.x,
};
}
pub fn xyz(self: Vec4f) Vec3f {
return Vec3f{.x=self.x, .y=self.y, .z=self.z};
}
};
pub const Vec3d = GenericVector3(f64);
pub const Vec4i = GenericVector4(i32);
@ -71,6 +83,14 @@ fn GenericVectorMath(comptime Vec: type, comptime T: type) type {
return result;
}
pub fn mulScalar(self: Vec, scalar: T) Vec {
var result: Vec = undefined;
inline for(@typeInfo(Vec).Struct.fields) |field| {
@field(result, field.name) = @field(self, field.name) * scalar;
}
return result;
}
pub fn div(self: Vec, other: Vec) Vec {
if(@typeInfo(T) == .Float) {
var result: Vec = undefined;
@ -117,6 +137,12 @@ fn GenericVectorMath(comptime Vec: type, comptime T: type) type {
}
}
pub fn mulEqualScalar(self: *Vec, scalar: T) Vec {
inline for(@typeInfo(Vec).Struct.fields) |field| {
@field(self, field.name) *= scalar;
}
}
pub fn divEqual(self: *Vec, other: Vec) void {
if(@typeInfo(T) == .Float) {
inline for(@typeInfo(Vec).Struct.fields) |field| {
@ -251,4 +277,14 @@ pub const Mat4f = struct {
}
return result;
}
pub fn mulVec(self: Mat4f, vec: Vec4f) Vec4f {
var transposeSelf = self.transpose();
var result: Vec4f = undefined;
result.x = transposeSelf.columns[0].dot(vec);
result.y = transposeSelf.columns[1].dot(vec);
result.z = transposeSelf.columns[2].dot(vec);
result.w = transposeSelf.columns[3].dot(vec);
return result;
}
};