lightpreview: Frustum culling and cull freeze. (#398)

* lightpreview: Frustum culling and cull freeze.

* lightpreview: Remove unused desired vis tracking.
This commit is contained in:
Daniel Svensson 2024-01-05 20:08:21 +01:00 committed by GitHub
parent 04c3a5117a
commit e1ad344949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 248 additions and 25 deletions

View File

@ -56,12 +56,16 @@ GLView::GLView(QWidget *parent)
m_moveSpeed(1000),
m_displayAspect(1),
m_cameraOrigin(0, 0, 0),
m_cullOrigin(0, 0, 0),
m_cameraFwd(0, 1, 0),
m_vao(),
m_indexBuffer(QOpenGLBuffer::IndexBuffer),
m_leakVao(),
m_portalVao(),
m_portalIndexBuffer(QOpenGLBuffer::IndexBuffer)
m_portalIndexBuffer(QOpenGLBuffer::IndexBuffer),
m_frustumVao(),
m_frustumFacesIndexBuffer(QOpenGLBuffer::IndexBuffer),
m_frustumEdgesIndexBuffer(QOpenGLBuffer::IndexBuffer)
{
for (auto &hullVao : m_hullVaos) {
hullVao.indexBuffer = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
@ -89,6 +93,11 @@ GLView::~GLView()
m_portalIndexBuffer.destroy();
m_portalVao.destroy();
m_frustumVbo.destroy();
m_frustumFacesIndexBuffer.destroy();
m_frustumEdgesIndexBuffer.destroy();
m_frustumVao.destroy();
for (auto &hullVao : m_hullVaos) {
hullVao.vbo.destroy();
hullVao.indexBuffer.destroy();
@ -370,9 +379,10 @@ GLView::face_visibility_key_t GLView::desiredFaceVisibility() const
if (m_visCulling) {
const mbsp_t &bsp = *m_bsp;
const auto &world = bsp.dmodels.at(0);
const auto &origin = m_keepCullOrigin ? m_cullOrigin : m_cameraOrigin;
auto *leaf =
BSP_FindLeafAtPoint(&bsp, &world, qvec3d{m_cameraOrigin.x(), m_cameraOrigin.y(), m_cameraOrigin.z()});
BSP_FindLeafAtPoint(&bsp, &world, qvec3d{origin.x(), origin.y(), origin.z()});
int leafnum = leaf - bsp.dleafs.data();
@ -390,7 +400,26 @@ GLView::face_visibility_key_t GLView::desiredFaceVisibility() const
return result;
}
void GLView::updateFaceVisibility()
bool GLView::isVolumeInFrustum(const std::array<QVector4D, 4>& frustum, const qvec3f& mins, const qvec3f& maxs) {
for (auto &plane : frustum) {
// Select the p-vertex (positive vertex) - the vertex of the bounding
// box most aligned with the plane normal
const auto p = qvec3f(
plane.x() > 0 ? maxs[0] : mins[0],
plane.y() > 0 ? maxs[1] : mins[1],
plane.z() > 0 ? maxs[2] : mins[2]
);
// Check if the p-vertex is outside the plane
if (plane.x() * p[0] + plane.y() * p[1] + plane.z() * p[2] + plane.w() < 0) {
return false;
}
}
return true;
}
void GLView::updateFaceVisibility(const std::array<QVector4D, 4>& frustum)
{
if (!m_bsp)
return;
@ -400,12 +429,6 @@ void GLView::updateFaceVisibility()
const face_visibility_key_t desired = desiredFaceVisibility();
if (m_uploaded_face_visibility &&
*m_uploaded_face_visibility == desired) {
//qDebug() << "reusing last frame visdata";
return;
}
qDebug() << "looking up pvs for clusternum " << desired.clusternum;
const int face_visibility_width = m_bsp->dfaces.size();
@ -433,7 +456,7 @@ void GLView::updateFaceVisibility()
// visit all world leafs: if they're visible, mark the appropriate faces
BSP_VisitAllLeafs(bsp, bsp.dmodels[0], [&](const mleaf_t &leaf) {
if (Pvs_LeafVisible(&bsp, pvs, &leaf)) {
if (Pvs_LeafVisible(&bsp, pvs, &leaf) && isVolumeInFrustum(frustum, leaf.mins, leaf.maxs)) {
for (int ms = 0; ms < leaf.nummarksurfaces; ++ms) {
int fnum = bsp.dleaffaces[leaf.firstmarksurface + ms];
face_flags[fnum] = 16;
@ -461,8 +484,6 @@ void GLView::updateFaceVisibility()
}
setFaceVisibilityArray(face_flags.data());
m_uploaded_face_visibility = desired;
}
bool GLView::shouldLiveUpdate() const
@ -571,6 +592,20 @@ void GLView::initializeGL()
glFrontFace(GL_CW);
}
std::array<QVector4D, 4> GLView::getFrustumPlanes(const QMatrix4x4& MVP)
{
return {
// left
(MVP.row(3) + MVP.row(0)).normalized(),
// right
(MVP.row(3) - MVP.row(0)).normalized(),
// top
(MVP.row(3) - MVP.row(1)).normalized(),
// bottom
(MVP.row(3) + MVP.row(1)).normalized(),
};
}
void GLView::paintGL()
{
// calculate frame time + update m_lastFrame
@ -588,13 +623,6 @@ void GLView::paintGL()
applyMouseMotion();
applyFlyMovement(duration_seconds);
// update vis culling if needed
updateFaceVisibility();
// draw
glClearColor(0.1, 0.1, 0.1, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 modelMatrix;
QMatrix4x4 viewMatrix;
QMatrix4x4 projectionMatrix;
@ -603,6 +631,16 @@ void GLView::paintGL()
QMatrix4x4 MVP = projectionMatrix * viewMatrix * modelMatrix;
const auto frustum = m_keepCullOrigin && m_keepCullFrustum ?
getFrustumPlanes(projectionMatrix * m_cullViewMatrix * modelMatrix) : getFrustumPlanes(MVP);
// update vis culling if needed
updateFaceVisibility(frustum);
// draw
glClearColor(0.1, 0.1, 0.1, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QOpenGLShaderProgram *active_program = nullptr;
m_program->bind();
@ -750,6 +788,40 @@ void GLView::paintGL()
m_program_wireframe->release();
}
if (m_visCulling && m_keepCullOrigin) {
const QMatrix4x4 cullMVP = projectionMatrix * viewMatrix * m_cullModelMatrix;
m_program_simple->bind();
m_program_simple->setUniformValue(m_program_simple_mvp_location, cullMVP);
QOpenGLVertexArrayObject::Binder vaoBinder(&m_frustumVao);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex((GLuint)-1);
m_frustumEdgesIndexBuffer.bind();
m_program_simple->setUniformValue(m_program_simple_color_location, QVector4D{1.0, 1.0, 1.0, 1.0});
glDrawElements(GL_LINE_LOOP, 30, GL_UNSIGNED_INT, 0);
m_frustumEdgesIndexBuffer.release();
glDisable(GL_PRIMITIVE_RESTART);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_frustumFacesIndexBuffer.bind();
m_program_simple->setUniformValue(m_program_simple_color_location, QVector4D{1.0, 1.0, 1.0, 0.05});
glDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_INT, 0);
m_frustumFacesIndexBuffer.release();
glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);
m_program_simple->release();
}
if (m_drawLeak && num_leak_points) {
m_program_simple->bind();
m_program_simple->setUniformValue(m_program_simple_mvp_location, MVP);
@ -872,6 +944,33 @@ void GLView::setVisCulling(bool viscull)
update();
}
void GLView::setKeepCullFrustum(bool keepcullfrustum)
{
m_keepCullFrustum = keepcullfrustum;
update();
}
void GLView::setKeepCullOrigin(bool keepcullorigin)
{
m_keepCullOrigin = keepcullorigin;
if (keepcullorigin) {
m_cullOrigin = m_cameraOrigin;
QMatrix4x4 rotation, position;
m_cullViewMatrix.setToIdentity();
m_cullViewMatrix.lookAt(m_cameraOrigin, m_cameraOrigin + m_cameraFwd, QVector3D(0, 0, 1));
rotation = m_cullViewMatrix.inverted();
rotation.setColumn(3, QVector4D(0, 0, 0, 1));
position.translate(m_cameraOrigin);
m_cullModelMatrix = position * rotation;
}
update();
}
void GLView::setDrawFlat(bool drawflat)
{
m_drawFlat = drawflat;
@ -929,7 +1028,6 @@ void GLView::setDrawTranslucencyAsOpaque(bool drawopaque)
void GLView::setShowBmodels(bool bmodels)
{
// force re-upload of face visibility
m_uploaded_face_visibility = std::nullopt;
m_showBmodels = bmodels;
update();
}
@ -983,8 +1081,36 @@ void GLView::setFaceVisibilityArray(uint8_t *data)
face_visibility_texture->bind();
glTexBuffer(GL_TEXTURE_BUFFER, GL_R8UI, face_visibility_buffer->bufferId());
face_visibility_texture->release();
}
//logging::print("uploaded {} bytes face visibility texture", face_visibility_width);
std::vector<QVector3D> GLView::getFrustumCorners(float displayAspect) {
QMatrix4x4 projectionMatrix;
projectionMatrix.perspective(90, displayAspect, 1.0f, 8192.0f);
const QMatrix4x4 invProjectionMatrix = projectionMatrix.inverted();
const std::vector ndcCorners = {
QVector4D(-1.0f, -1.0f, -1.0f, 1.0f), // 0: near bottom left
QVector4D( 1.0f, -1.0f, -1.0f, 1.0f), // 1: near bottom right
QVector4D (1.0f, 1.0f, -1.0f, 1.0f), // 2: near top right
QVector4D(-1.0f, 1.0f, -1.0f, 1.0f), // 3: near top left
QVector4D(-1.0f, -1.0f, 1.0f, 1.0f), // far bottom left
QVector4D( 1.0f, -1.0f, 1.0f, 1.0f), // far bottom right
QVector4D( 1.0f, 1.0f, 1.0f, 1.0f), // far top left
QVector4D(-1.0f, 1.0f, 1.0f, 1.0f) // far top right
};
std::vector<QVector3D> corners(8);
// Transform to world space
for (int i = 0; i < 8; i++) {
QVector4D worldSpaceCorner = invProjectionMatrix * ndcCorners[i];
worldSpaceCorner /= worldSpaceCorner.w(); // Perspective divide
corners[i] = worldSpaceCorner.toVector3D();
}
return corners;
}
void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries_t &bspx,
@ -1035,9 +1161,15 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries
hullVao.indexBuffer.allocate(0);
}
m_frustumVbo.bind();
m_frustumVbo.allocate(0);
m_frustumFacesIndexBuffer.bind();
m_frustumFacesIndexBuffer.allocate(0);
m_frustumEdgesIndexBuffer.bind();
m_frustumEdgesIndexBuffer.allocate(0);
num_leak_points = 0;
num_portal_indices = 0;
m_uploaded_face_visibility = std::nullopt;
int32_t highest_depth = 0;
@ -1461,6 +1593,55 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries
qvec3f pos;
};
{
QOpenGLVertexArrayObject::Binder vaoBinder(&m_frustumVao);
auto corners = getFrustumCorners(m_displayAspect);
m_frustumVbo.create();
m_frustumVbo.bind();
m_frustumVbo.allocate(corners.data(), corners.size() * sizeof(QVector3D));
glEnableVertexAttribArray(0 /* attrib */);
glVertexAttribPointer(0 /* attrib */, 3, GL_FLOAT, GL_FALSE, sizeof(QVector3D), 0);
// Near Plane: Far Plane:
// 3----2 7----6
// | | | |
// | | | |
// 0----1 4----5
GLuint faceIndices[] = {
// Left face
0, 4, 7, 0, 7, 3,
// Right face
1, 2, 6, 1, 6, 5,
// Top face
2, 3, 7, 2, 7, 6,
// Bottom face
0, 1, 5, 0, 5, 4
};
GLuint edgeIndices[] = {
// Front face
0, 1, 1, 2, 2, 3, 3, 0, (GLuint)-1,
// Back face
4, 5, 5, 6, 6, 7, 7, 4, (GLuint)-1,
// Connecting edges
0, 4, (GLuint)-1,
1, 5, (GLuint)-1,
2, 6, (GLuint)-1,
3, 7, (GLuint)-1
};
m_frustumFacesIndexBuffer.create();
m_frustumFacesIndexBuffer.bind();
m_frustumFacesIndexBuffer.allocate(faceIndices, sizeof(faceIndices));
m_frustumEdgesIndexBuffer.create();
m_frustumEdgesIndexBuffer.bind();
m_frustumEdgesIndexBuffer.allocate(edgeIndices, sizeof(edgeIndices));
}
if (fs::exists(leakFile)) {
QOpenGLVertexArrayObject::Binder leakVaoBinder(&m_leakVao);
@ -1612,9 +1793,20 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries
update();
}
void GLView::updateFrustumVBO()
{
if (m_frustumVbo.isCreated()) {
const std::vector<QVector3D> corners = getFrustumCorners(m_displayAspect);
m_frustumVbo.bind();
glBufferData(GL_ARRAY_BUFFER, sizeof(QVector3D) * corners.size(), corners.data(), GL_DYNAMIC_DRAW);
m_frustumVbo.release();
}
}
void GLView::resizeGL(int width, int height)
{
m_displayAspect = static_cast<float>(width) / static_cast<float>(height);
updateFrustumVBO();
}
void GLView::applyMouseMotion()

View File

@ -85,13 +85,15 @@ private:
};
face_visibility_key_t desiredFaceVisibility() const;
std::optional<face_visibility_key_t> m_uploaded_face_visibility;
bool m_visCulling = true;
// camera stuff
float m_displayAspect;
QVector3D m_cameraOrigin;
QVector3D m_cameraFwd; // unit vec
QVector3D m_cullOrigin;
QMatrix4x4 m_cullViewMatrix;
QMatrix4x4 m_cullModelMatrix;
QVector3D cameraRight() const
{
QVector3D v = QVector3D::crossProduct(m_cameraFwd, QVector3D(0, 0, 1));
@ -108,6 +110,8 @@ private:
bool m_showTrisSeeThrough = false;
bool m_drawFlat = false;
bool m_keepOrigin = false;
bool m_keepCullFrustum = true;
bool m_keepCullOrigin = false;
bool m_drawPortals = false;
bool m_drawLeak = false;
QOpenGLTexture::Filter m_filter = QOpenGLTexture::Linear;
@ -125,6 +129,11 @@ private:
QOpenGLBuffer m_portalVbo;
QOpenGLBuffer m_portalIndexBuffer;
QOpenGLVertexArrayObject m_frustumVao;
QOpenGLBuffer m_frustumVbo;
QOpenGLBuffer m_frustumFacesIndexBuffer;
QOpenGLBuffer m_frustumEdgesIndexBuffer;
struct leaf_vao_t {
QOpenGLVertexArrayObject vao;
QOpenGLBuffer vbo;
@ -212,6 +221,9 @@ public:
private:
void setFaceVisibilityArray(uint8_t *data);
static bool isVolumeInFrustum(const std::array<QVector4D, 4>& frustum, const qvec3f& mins, const qvec3f& maxs);
static std::vector<QVector3D> getFrustumCorners(float displayAspect);
static std::array<QVector4D, 4> getFrustumPlanes(const QMatrix4x4& MVP);
public:
void renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries_t &bspx,
@ -228,6 +240,8 @@ public:
void setVisCulling(bool viscull);
void setDrawFlat(bool drawflat);
void setKeepOrigin(bool keeporigin);
void setKeepCullFrustum(bool keepfrustum);
void setKeepCullOrigin(bool keeporigin);
void setDrawPortals(bool drawportals);
void setDrawLeak(bool drawleak);
// intensity = 0 to 200
@ -245,7 +259,8 @@ protected:
void resizeGL(int width, int height) override;
private:
void updateFaceVisibility();
void updateFrustumVBO();
void updateFaceVisibility(const std::array<QVector4D, 4>& frustum);
bool shouldLiveUpdate() const;
void handleLoggedMessage(const QOpenGLDebugMessage &debugMessage);

View File

@ -205,6 +205,8 @@ void MainWindow::createPropertiesSidebar()
visculling->setChecked(true);
auto *keepposition = new QCheckBox(tr("Keep Camera Pos"));
auto *keepcullfrustum = new QCheckBox(tr("Keep Cull Frustum"));
auto *keepcullposition = new QCheckBox(tr("Keep Cull Pos"));
nearest = new QCheckBox(tr("Nearest Filter"));
@ -229,6 +231,8 @@ void MainWindow::createPropertiesSidebar()
formLayout->addRow(showtris);
formLayout->addRow(showtris_seethrough);
formLayout->addRow(visculling);
formLayout->addRow(keepcullposition);
formLayout->addRow(keepcullfrustum);
formLayout->addRow(keepposition);
formLayout->addRow(nearest);
formLayout->addRow(bspx_decoupled_lm);
@ -266,6 +270,9 @@ void MainWindow::createPropertiesSidebar()
common_options->setText(s.value("common_options").toString());
qbsp_options->setText(s.value("qbsp_options").toString());
vis_checkbox->setChecked(s.value("vis_enabled").toBool());
keepcullposition->setEnabled(vis_checkbox->isChecked());
keepcullfrustum->setEnabled(keepcullposition->isChecked());
keepcullfrustum->setChecked(true);
vis_options->setText(s.value("vis_options").toString());
light_options->setText(s.value("light_options").toString());
nearest->setChecked(s.value("nearest").toBool());
@ -282,7 +289,16 @@ void MainWindow::createPropertiesSidebar()
connect(showtris, &QAbstractButton::toggled, this, [=](bool checked) { glView->setShowTris(checked); });
connect(showtris_seethrough, &QAbstractButton::toggled, this,
[=](bool checked) { glView->setShowTrisSeeThrough(checked); });
connect(visculling, &QAbstractButton::toggled, this, [=](bool checked) { glView->setVisCulling(checked); });
connect(visculling, &QAbstractButton::toggled, this, [=](bool checked) {
glView->setVisCulling(checked);
keepcullposition->setEnabled(checked);
keepcullfrustum->setEnabled(keepcullposition->isEnabled());
});
connect(keepcullposition, &QAbstractButton::toggled, this, [=](bool checked) {
glView->setKeepCullOrigin(checked);
keepcullfrustum->setEnabled(checked);
});
connect(keepcullfrustum, &QAbstractButton::toggled, this, [=](bool checked) { glView->setKeepCullFrustum(checked); });
connect(drawflat, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawFlat(checked); });
connect(hull0, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{0} : std::nullopt); });
connect(hull1, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{1} : std::nullopt); });