diff --git a/src/Camera.h b/src/Camera.h index 0ca22c9..2ceb9e1 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -1,10 +1,14 @@ #ifndef CAMERA_H #define CAMERA_H +#include + #include #include #include "Player.h" +#include "Frustum.h" + class Camera { @@ -41,9 +45,77 @@ class Camera glm::vec3 getEyePosition() const { return player->getEyePosition(); } + + // Build frustum from view-projection matrix (more reliable method) + Frustum createFrustumFromMatrix(const glm::mat4& viewProj) { + Frustum frustum; + + // Extract planes from view-projection matrix + // Left plane + frustum.leftFace.normal.x = viewProj[0][3] + viewProj[0][0]; + frustum.leftFace.normal.y = viewProj[1][3] + viewProj[1][0]; + frustum.leftFace.normal.z = viewProj[2][3] + viewProj[2][0]; + frustum.leftFace.distance = viewProj[3][3] + viewProj[3][0]; + + // Right plane + frustum.rightFace.normal.x = viewProj[0][3] - viewProj[0][0]; + frustum.rightFace.normal.y = viewProj[1][3] - viewProj[1][0]; + frustum.rightFace.normal.z = viewProj[2][3] - viewProj[2][0]; + frustum.rightFace.distance = viewProj[3][3] - viewProj[3][0]; + + // Bottom plane + frustum.bottomFace.normal.x = viewProj[0][3] + viewProj[0][1]; + frustum.bottomFace.normal.y = viewProj[1][3] + viewProj[1][1]; + frustum.bottomFace.normal.z = viewProj[2][3] + viewProj[2][1]; + frustum.bottomFace.distance = viewProj[3][3] + viewProj[3][1]; + + // Top plane + frustum.topFace.normal.x = viewProj[0][3] - viewProj[0][1]; + frustum.topFace.normal.y = viewProj[1][3] - viewProj[1][1]; + frustum.topFace.normal.z = viewProj[2][3] - viewProj[2][1]; + frustum.topFace.distance = viewProj[3][3] - viewProj[3][1]; + + // Near plane + frustum.nearFace.normal.x = viewProj[0][3] + viewProj[0][2]; + frustum.nearFace.normal.y = viewProj[1][3] + viewProj[1][2]; + frustum.nearFace.normal.z = viewProj[2][3] + viewProj[2][2]; + frustum.nearFace.distance = viewProj[3][3] + viewProj[3][2]; + + // Far plane + frustum.farFace.normal.x = viewProj[0][3] - viewProj[0][2]; + frustum.farFace.normal.y = viewProj[1][3] - viewProj[1][2]; + frustum.farFace.normal.z = viewProj[2][3] - viewProj[2][2]; + frustum.farFace.distance = viewProj[3][3] - viewProj[3][2]; + + // Normalize all planes + float length; + + length = glm::length(frustum.leftFace.normal); + frustum.leftFace.normal /= length; + frustum.leftFace.distance /= length; + + length = glm::length(frustum.rightFace.normal); + frustum.rightFace.normal /= length; + frustum.rightFace.distance /= length; + + length = glm::length(frustum.bottomFace.normal); + frustum.bottomFace.normal /= length; + frustum.bottomFace.distance /= length; + + length = glm::length(frustum.topFace.normal); + frustum.topFace.normal /= length; + frustum.topFace.distance /= length; + + length = glm::length(frustum.nearFace.normal); + frustum.nearFace.normal /= length; + frustum.nearFace.distance /= length; + + length = glm::length(frustum.farFace.normal); + frustum.farFace.normal /= length; + frustum.farFace.distance /= length; + + return frustum; + } }; #endif - - - diff --git a/src/Frustum.h b/src/Frustum.h new file mode 100644 index 0000000..08a95fe --- /dev/null +++ b/src/Frustum.h @@ -0,0 +1,71 @@ +#ifndef FRUSTUM_H +#define FRUSTUM_H + +#include + +struct Plane { + glm::vec3 normal = { 0.0f, 1.0f, 0.0f }; + float distance = 0.0f; + + // Distance from point to plane (positive = in front, negative = behind) + float distanceToPoint(const glm::vec3& point) const { + return glm::dot(normal, point) + distance; + } +}; + +struct AABB { + glm::vec3 min; + glm::vec3 max; + + // Get center point + glm::vec3 getCenter() const { + return (min + max) * 0.5f; + } + + // Get positive extents (half-size) + glm::vec3 getExtents() const { + return (max - min) * 0.5f; + } +}; + +struct Frustum { + Plane topFace; + Plane bottomFace; + Plane rightFace; + Plane leftFace; + Plane farFace; + Plane nearFace; + + // Test if AABB is in frustum (returns true if visible) + bool isAABBVisible(const AABB& aabb) const { + // For each plane, check if the AABB is completely behind it + // If it's behind any plane, it's outside the frustum + return isOnOrForwardPlane(topFace, aabb) && + isOnOrForwardPlane(bottomFace, aabb) && + isOnOrForwardPlane(rightFace, aabb) && + isOnOrForwardPlane(leftFace, aabb) && + isOnOrForwardPlane(farFace, aabb) && + isOnOrForwardPlane(nearFace, aabb); + } + +private: + // Check if AABB is on or in front of plane + bool isOnOrForwardPlane(const Plane& plane, const AABB& aabb) const { + // Get the positive vertex (furthest point in direction of normal) + const glm::vec3 center = aabb.getCenter(); + const glm::vec3 extents = aabb.getExtents(); + + // Project extents onto plane normal to get the effective radius + const float radius = extents.x * std::abs(plane.normal.x) + + extents.y * std::abs(plane.normal.y) + + extents.z * std::abs(plane.normal.z); + + // Distance from center to plane + const float dist = plane.distanceToPoint(center); + + // AABB is visible if center is within radius distance from plane + return dist >= -radius; + } +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 9db3d37..22e9b0a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -430,6 +430,10 @@ int main() { std::floor(player.position.z / CHUNK_SIZE) ); + // Create frustum for culling from view-projection matrix + glm::mat4 viewProj = projection * view; + Frustum frustum = camera.createFrustumFromMatrix(viewProj); + // ---- PASS 1: Draw solid grey cubes ---- glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.0, 1.0); // Push solid faces "back" @@ -442,6 +446,17 @@ int main() { glm::ivec3 chunk_offset = glm::ivec3(x, y, z); glm::ivec3 chunk_pos = camera_chunk + chunk_offset; + // Create AABB for this chunk + glm::vec3 chunkWorldPos = glm::vec3(chunk_pos * CHUNK_SIZE); + AABB chunkAABB; + chunkAABB.min = chunkWorldPos; + chunkAABB.max = chunkWorldPos + glm::vec3(CHUNK_SIZE); + + // Frustum culling - skip if chunk is not visible + if (!frustum.isAABBVisible(chunkAABB)) { + continue; + } + glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(chunk_pos * CHUNK_SIZE)); glm::mat4 mvp = projection * view * model; @@ -472,6 +487,17 @@ int main() { glm::ivec3 chunk_offset = glm::ivec3(x, y, z); glm::ivec3 chunk_pos = camera_chunk + chunk_offset; + // Create AABB for this chunk + glm::vec3 chunkWorldPos = glm::vec3(chunk_pos * CHUNK_SIZE); + AABB chunkAABB; + chunkAABB.min = chunkWorldPos; + chunkAABB.max = chunkWorldPos + glm::vec3(CHUNK_SIZE); + + // Frustum culling - skip if chunk is not visible + if (!frustum.isAABBVisible(chunkAABB)) { + continue; + } + glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(chunk_pos * CHUNK_SIZE)); glm::mat4 mvp = projection * view * model;