Frustum culling
This commit is contained in:
parent
7e8efe82ec
commit
f2ccdff499
2 changed files with 144 additions and 0 deletions
110
src/3d/Math3D.h
110
src/3d/Math3D.h
|
|
@ -610,6 +610,116 @@ struct quat {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Frustum - View frustum for culling
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
struct Plane {
|
||||||
|
vec3 normal;
|
||||||
|
float distance;
|
||||||
|
|
||||||
|
Plane() : normal(0, 1, 0), distance(0) {}
|
||||||
|
Plane(const vec3& n, float d) : normal(n), distance(d) {}
|
||||||
|
|
||||||
|
// Distance from plane to point (positive = in front, negative = behind)
|
||||||
|
float distanceToPoint(const vec3& point) const {
|
||||||
|
return normal.dot(point) + distance;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Frustum {
|
||||||
|
// Six planes: left, right, bottom, top, near, far
|
||||||
|
Plane planes[6];
|
||||||
|
|
||||||
|
// Extract frustum planes from view-projection matrix
|
||||||
|
// Uses Gribb/Hartmann method
|
||||||
|
void extractFromMatrix(const mat4& viewProj) {
|
||||||
|
const float* m = viewProj.m;
|
||||||
|
|
||||||
|
// Left plane
|
||||||
|
planes[0].normal.x = m[3] + m[0];
|
||||||
|
planes[0].normal.y = m[7] + m[4];
|
||||||
|
planes[0].normal.z = m[11] + m[8];
|
||||||
|
planes[0].distance = m[15] + m[12];
|
||||||
|
|
||||||
|
// Right plane
|
||||||
|
planes[1].normal.x = m[3] - m[0];
|
||||||
|
planes[1].normal.y = m[7] - m[4];
|
||||||
|
planes[1].normal.z = m[11] - m[8];
|
||||||
|
planes[1].distance = m[15] - m[12];
|
||||||
|
|
||||||
|
// Bottom plane
|
||||||
|
planes[2].normal.x = m[3] + m[1];
|
||||||
|
planes[2].normal.y = m[7] + m[5];
|
||||||
|
planes[2].normal.z = m[11] + m[9];
|
||||||
|
planes[2].distance = m[15] + m[13];
|
||||||
|
|
||||||
|
// Top plane
|
||||||
|
planes[3].normal.x = m[3] - m[1];
|
||||||
|
planes[3].normal.y = m[7] - m[5];
|
||||||
|
planes[3].normal.z = m[11] - m[9];
|
||||||
|
planes[3].distance = m[15] - m[13];
|
||||||
|
|
||||||
|
// Near plane
|
||||||
|
planes[4].normal.x = m[3] + m[2];
|
||||||
|
planes[4].normal.y = m[7] + m[6];
|
||||||
|
planes[4].normal.z = m[11] + m[10];
|
||||||
|
planes[4].distance = m[15] + m[14];
|
||||||
|
|
||||||
|
// Far plane
|
||||||
|
planes[5].normal.x = m[3] - m[2];
|
||||||
|
planes[5].normal.y = m[7] - m[6];
|
||||||
|
planes[5].normal.z = m[11] - m[10];
|
||||||
|
planes[5].distance = m[15] - m[14];
|
||||||
|
|
||||||
|
// Normalize all planes
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
float len = planes[i].normal.length();
|
||||||
|
if (len > 0.0001f) {
|
||||||
|
planes[i].normal = planes[i].normal / len;
|
||||||
|
planes[i].distance /= len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if a point is inside the frustum
|
||||||
|
bool containsPoint(const vec3& point) const {
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (planes[i].distanceToPoint(point) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if a sphere intersects or is inside the frustum
|
||||||
|
bool containsSphere(const vec3& center, float radius) const {
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (planes[i].distanceToPoint(center) < -radius) {
|
||||||
|
return false; // Sphere is completely behind this plane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if an axis-aligned bounding box intersects the frustum
|
||||||
|
bool containsAABB(const vec3& min, const vec3& max) const {
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
// Find the positive vertex (furthest along plane normal)
|
||||||
|
vec3 pVertex;
|
||||||
|
pVertex.x = (planes[i].normal.x >= 0) ? max.x : min.x;
|
||||||
|
pVertex.y = (planes[i].normal.y >= 0) ? max.y : min.y;
|
||||||
|
pVertex.z = (planes[i].normal.z >= 0) ? max.z : min.z;
|
||||||
|
|
||||||
|
// If positive vertex is behind plane, box is outside
|
||||||
|
if (planes[i].distanceToPoint(pVertex) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Utility constants and functions
|
// Utility constants and functions
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
|
||||||
|
|
@ -515,10 +515,23 @@ void Viewport3D::renderEntities(const mat4& view, const mat4& proj) {
|
||||||
#ifdef MCRF_HAS_GL
|
#ifdef MCRF_HAS_GL
|
||||||
if (!entities_ || !shader_ || !shader_->isValid()) return;
|
if (!entities_ || !shader_ || !shader_->isValid()) return;
|
||||||
|
|
||||||
|
// Extract frustum for culling
|
||||||
|
mat4 viewProj = proj * view;
|
||||||
|
Frustum frustum;
|
||||||
|
frustum.extractFromMatrix(viewProj);
|
||||||
|
|
||||||
// Render non-skeletal entities first
|
// Render non-skeletal entities first
|
||||||
shader_->bind();
|
shader_->bind();
|
||||||
for (auto& entity : *entities_) {
|
for (auto& entity : *entities_) {
|
||||||
if (entity && entity->isVisible()) {
|
if (entity && entity->isVisible()) {
|
||||||
|
// Frustum culling - use entity position with generous bounding radius
|
||||||
|
vec3 pos = entity->getWorldPos();
|
||||||
|
float boundingRadius = entity->getScale().x * 2.0f; // Approximate bounding sphere
|
||||||
|
|
||||||
|
if (!frustum.containsSphere(pos, boundingRadius)) {
|
||||||
|
continue; // Skip this entity - outside view frustum
|
||||||
|
}
|
||||||
|
|
||||||
auto model = entity->getModel();
|
auto model = entity->getModel();
|
||||||
if (!model || !model->hasSkeleton()) {
|
if (!model || !model->hasSkeleton()) {
|
||||||
entity->render(view, proj, shader_->getProgram());
|
entity->render(view, proj, shader_->getProgram());
|
||||||
|
|
@ -554,6 +567,14 @@ void Viewport3D::renderEntities(const mat4& view, const mat4& proj) {
|
||||||
|
|
||||||
for (auto& entity : *entities_) {
|
for (auto& entity : *entities_) {
|
||||||
if (entity && entity->isVisible()) {
|
if (entity && entity->isVisible()) {
|
||||||
|
// Frustum culling for skeletal entities too
|
||||||
|
vec3 pos = entity->getWorldPos();
|
||||||
|
float boundingRadius = entity->getScale().x * 2.0f;
|
||||||
|
|
||||||
|
if (!frustum.containsSphere(pos, boundingRadius)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto model = entity->getModel();
|
auto model = entity->getModel();
|
||||||
if (model && model->hasSkeleton()) {
|
if (model && model->hasSkeleton()) {
|
||||||
entity->render(view, proj, skinnedShader_->getProgram());
|
entity->render(view, proj, skinnedShader_->getProgram());
|
||||||
|
|
@ -594,6 +615,11 @@ void Viewport3D::renderBillboards(const mat4& view, const mat4& proj) {
|
||||||
#ifdef MCRF_HAS_GL
|
#ifdef MCRF_HAS_GL
|
||||||
if (!billboards_ || billboards_->empty() || !shader_ || !shader_->isValid()) return;
|
if (!billboards_ || billboards_->empty() || !shader_ || !shader_->isValid()) return;
|
||||||
|
|
||||||
|
// Extract frustum for culling
|
||||||
|
mat4 viewProj = proj * view;
|
||||||
|
Frustum frustum;
|
||||||
|
frustum.extractFromMatrix(viewProj);
|
||||||
|
|
||||||
shader_->bind();
|
shader_->bind();
|
||||||
unsigned int shaderProgram = shader_->getProgram();
|
unsigned int shaderProgram = shader_->getProgram();
|
||||||
vec3 cameraPos = camera_.getPosition();
|
vec3 cameraPos = camera_.getPosition();
|
||||||
|
|
@ -607,6 +633,14 @@ void Viewport3D::renderBillboards(const mat4& view, const mat4& proj) {
|
||||||
|
|
||||||
for (auto& billboard : *billboards_) {
|
for (auto& billboard : *billboards_) {
|
||||||
if (billboard && billboard->isVisible()) {
|
if (billboard && billboard->isVisible()) {
|
||||||
|
// Frustum culling for billboards
|
||||||
|
vec3 pos = billboard->getPosition();
|
||||||
|
float boundingRadius = billboard->getScale() * 2.0f; // Approximate
|
||||||
|
|
||||||
|
if (!frustum.containsSphere(pos, boundingRadius)) {
|
||||||
|
continue; // Skip - outside frustum
|
||||||
|
}
|
||||||
|
|
||||||
billboard->render(shaderProgram, view, proj, cameraPos);
|
billboard->render(shaderProgram, view, proj, cameraPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue