diff --git a/src/assets.zig b/src/assets.zig index 4feb2e96..54cfa913 100644 --- a/src/assets.zig +++ b/src/assets.zig @@ -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.*; diff --git a/src/game.zig b/src/game.zig index baaa0bc6..200ca5ea 100644 --- a/src/game.zig +++ b/src/game.zig @@ -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)); } }; diff --git a/src/main.zig b/src/main.zig index 0ad7b605..02d5d077 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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); diff --git a/src/renderer.zig b/src/renderer.zig index 5e31682f..ffcd355c 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -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 visibleReduced = new SimpleList(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: diff --git a/src/settings.zig b/src/settings.zig index 2731426d..f2f74caf 100644 --- a/src/settings.zig +++ b/src/settings.zig @@ -5,4 +5,8 @@ pub const connectionTimeout = 60000; pub const version = "0.12.0"; -pub const highestLOD: u5 = 5; \ No newline at end of file +pub const highestLOD: u5 = 5; + + + +pub var fov: f32 = 45; \ No newline at end of file diff --git a/src/vec.zig b/src/vec.zig index 85b070f3..f0b60cef 100644 --- a/src/vec.zig +++ b/src/vec.zig @@ -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; + } }; \ No newline at end of file