From edb664f20055354c4ef73d3f4db1623459fcb19f Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 24 Jul 2023 00:50:46 -0600 Subject: [PATCH] lightpreview: preliminary vis culling - q2 only - not handling bmodels properly --- lightpreview/glview.cpp | 192 +++++++++++++++++++++++++++++++++++++++- lightpreview/glview.h | 32 ++++++- 2 files changed, 220 insertions(+), 4 deletions(-) diff --git a/lightpreview/glview.cpp b/lightpreview/glview.cpp index 76088198..a9458a8e 100644 --- a/lightpreview/glview.cpp +++ b/lightpreview/glview.cpp @@ -79,6 +79,7 @@ GLView::~GLView() placeholder_texture.reset(); lightmap_texture.reset(); + face_visibility_texture.reset(); m_drawcalls.clear(); doneCurrent(); @@ -121,10 +122,24 @@ static const char *s_vertShader_Wireframe = R"( #version 330 core layout (location = 0) in vec3 position; +layout (location = 6) in int face_index; uniform mat4 MVP; +uniform usampler1D face_visibility_sampler; + +bool is_culled() { + int byte_index = face_index; + + uint sampled = texelFetch(face_visibility_sampler, byte_index, 0).r; + + return sampled != 16u; +} void main() { + if (is_culled()) { + gl_Position = vec4(0.0); + return; + } gl_Position = MVP * vec4(position, 1.0); } )"; @@ -193,6 +208,7 @@ layout (location = 2) in vec2 vertex_lightmap_uv; layout (location = 3) in vec3 vertex_normal; layout (location = 4) in vec3 vertex_flat_color; layout (location = 5) in uint vertex_styles; +layout (location = 6) in int face_index; out vec2 uv; out vec2 lightmap_uv; @@ -201,8 +217,22 @@ flat out vec3 flat_color; flat out uint styles; uniform mat4 MVP; +uniform usampler1D face_visibility_sampler; + +bool is_culled() { + int byte_index = face_index; + + uint sampled = texelFetch(face_visibility_sampler, byte_index, 0).r; + + return sampled != 16u; +} void main() { + if (is_culled()) { + gl_Position = vec4(0.0); + return; + } + gl_Position = MVP * vec4(position.x, position.y, position.z, 1.0); uv = vertex_uv; @@ -277,6 +307,7 @@ layout (location = 2) in vec2 vertex_lightmap_uv; layout (location = 3) in vec3 vertex_normal; layout (location = 4) in vec3 vertex_flat_color; layout (location = 5) in uint vertex_styles; +layout (location = 6) in int face_index; out vec3 fragment_world_pos; out vec2 lightmap_uv; @@ -286,8 +317,22 @@ flat out uint styles; uniform mat4 MVP; uniform vec3 eye_origin; +uniform usampler1D face_visibility_sampler; + +bool is_culled() { + int byte_index = face_index; + + uint sampled = texelFetch(face_visibility_sampler, byte_index, 0).r; + + return sampled != 16u; +} void main() { + if (is_culled()) { + gl_Position = vec4(0.0); + return; + } + gl_Position = MVP * vec4(position, 1.0); fragment_world_pos = position; @@ -298,6 +343,72 @@ void main() { } )"; +void GLView::updateFaceVisibility() +{ + if (!m_bsp) + return; + + const mbsp_t &bsp = *m_bsp; + const auto &world = bsp.dmodels.at(0); + + auto *leaf = BSP_FindLeafAtPoint(&bsp, &world, qvec3d{m_cameraOrigin.x(), m_cameraOrigin.y(), m_cameraOrigin.z()}); + + int leafnum = leaf - bsp.dleafs.data(); + int clusternum = leaf->cluster; + + if (m_lastLeaf == clusternum) { + qDebug() << "reusing last frame visdata for leaf " << leafnum << " cluster " << clusternum; + return; + } + + qDebug() << "looking up pvs for clusternum " << clusternum; + + auto it = m_decompressedVis.find(clusternum); + if (it == m_decompressedVis.end()) { + qDebug() << "no visdata, must be in void"; + + m_lastLeaf = clusternum; + setFaceVisibilityToAllVisible(); + + return; + } + + Q_assert(it != m_decompressedVis.end()); + + const auto &pvs = it->second; + qDebug() << "found bitvec of size " << pvs.size(); + + // check leaf visibility + + auto leaf_sees = [&](const mleaf_t *b) -> bool { + if (b->cluster < 0) + return true; + + return !!(pvs[b->cluster >> 3] & (1 << (b->cluster & 7))); + }; + + const int face_visibility_width = m_bsp->dfaces.size(); + + std::vector face_flags; + face_flags.resize(face_visibility_width, 0); + + // check all leafs + // FIXME: only world? + + for (auto &leaf : bsp.dleafs) { + if (leaf_sees(&leaf)) { + for (int ms = 0; ms < leaf.nummarksurfaces; ++ms) { + int fnum = bsp.dleaffaces[leaf.firstmarksurface + ms]; + face_flags[fnum] = 16; + } + } + } + + setFaceVisibilityArray(face_flags.data()); + + m_lastLeaf = clusternum; +} + bool GLView::shouldLiveUpdate() const { if (m_keysPressed) @@ -357,6 +468,7 @@ void GLView::initializeGL() m_program_mvp_location = m_program->uniformLocation("MVP"); m_program_texture_sampler_location = m_program->uniformLocation("texture_sampler"); m_program_lightmap_sampler_location = m_program->uniformLocation("lightmap_sampler"); + m_program_face_visibility_sampler_location = m_program->uniformLocation("face_visibility_sampler"); m_program_opacity_location = m_program->uniformLocation("opacity"); m_program_alpha_test_location = m_program->uniformLocation("alpha_test"); m_program_lightmap_only_location = m_program->uniformLocation("lightmap_only"); @@ -371,6 +483,7 @@ void GLView::initializeGL() m_skybox_program_eye_direction_location = m_skybox_program->uniformLocation("eye_origin"); m_skybox_program_texture_sampler_location = m_skybox_program->uniformLocation("texture_sampler"); m_skybox_program_lightmap_sampler_location = m_skybox_program->uniformLocation("lightmap_sampler"); + m_skybox_program_face_visibility_sampler_location = m_skybox_program->uniformLocation("face_visibility_sampler"); m_skybox_program_opacity_location = m_skybox_program->uniformLocation("opacity"); m_skybox_program_lightmap_only_location = m_skybox_program->uniformLocation("lightmap_only"); m_skybox_program_fullbright_location = m_skybox_program->uniformLocation("fullbright"); @@ -381,6 +494,8 @@ void GLView::initializeGL() m_program_wireframe->bind(); m_program_wireframe_mvp_location = m_program_wireframe->uniformLocation("MVP"); + m_program_wireframe_face_visibility_sampler_location = + m_program_wireframe->uniformLocation("face_visibility_sampler"); m_program_wireframe->release(); m_program_simple->bind(); @@ -415,6 +530,9 @@ 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); @@ -433,6 +551,7 @@ void GLView::paintGL() m_program->setUniformValue(m_program_mvp_location, MVP); m_program->setUniformValue(m_program_texture_sampler_location, 0 /* texture unit */); m_program->setUniformValue(m_program_lightmap_sampler_location, 1 /* texture unit */); + m_program->setUniformValue(m_program_face_visibility_sampler_location, 2 /* texture unit */); m_program->setUniformValue(m_program_opacity_location, 1.0f); m_program->setUniformValue(m_program_alpha_test_location, false); m_program->setUniformValue(m_program_lightmap_only_location, m_lighmapOnly); @@ -445,6 +564,7 @@ void GLView::paintGL() m_skybox_program->setUniformValue(m_skybox_program_eye_direction_location, m_cameraOrigin); m_skybox_program->setUniformValue(m_skybox_program_texture_sampler_location, 0 /* texture unit */); m_skybox_program->setUniformValue(m_skybox_program_lightmap_sampler_location, 1 /* texture unit */); + m_skybox_program->setUniformValue(m_skybox_program_face_visibility_sampler_location, 2 /* texture unit */); m_skybox_program->setUniformValue(m_skybox_program_opacity_location, 1.0f); m_skybox_program->setUniformValue(m_skybox_program_lightmap_only_location, m_lighmapOnly); m_skybox_program->setUniformValue(m_skybox_program_fullbright_location, m_fullbright); @@ -469,6 +589,9 @@ void GLView::paintGL() draw.texture->bind(0 /* texture unit */); lightmap_texture->bind(1 /* texture unit */); + if (face_visibility_texture) { + face_visibility_texture->bind(2 /* texture unit */); + } QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao); @@ -498,6 +621,9 @@ void GLView::paintGL() draw.texture->bind(0 /* texture unit */); lightmap_texture->bind(1 /* texture unit */); + if (face_visibility_texture) { + face_visibility_texture->bind(2 /* texture unit */); + } if (active_program == m_program) { m_program->setUniformValue(m_program_opacity_location, draw.key.opacity); @@ -520,6 +646,12 @@ void GLView::paintGL() if (m_showTris || m_showTrisSeeThrough) { m_program_wireframe->bind(); m_program_wireframe->setUniformValue(m_program_wireframe_mvp_location, MVP); + m_program_wireframe->setUniformValue( + m_program_wireframe_face_visibility_sampler_location, 2 /* texture unit */); + + if (face_visibility_texture) { + face_visibility_texture->bind(2 /* texture unit */); + } if (m_showTrisSeeThrough) glDisable(GL_DEPTH_TEST); @@ -705,10 +837,55 @@ void GLView::takeScreenshot(QString destPath, int w, int h) update(); } +void GLView::setFaceVisibilityArray(uint8_t *data) +{ + // one byte per face + int face_visibility_width = m_bsp->dfaces.size(); + + face_visibility_texture.reset(); + + face_visibility_texture = std::make_shared(QOpenGLTexture::Target1D); + face_visibility_texture->setSize(face_visibility_width); + face_visibility_texture->setFormat(QOpenGLTexture::R8U); + face_visibility_texture->setMagnificationFilter(QOpenGLTexture::Nearest); + face_visibility_texture->setMinificationFilter(QOpenGLTexture::Nearest); + face_visibility_texture->allocateStorage(); + + face_visibility_texture->setData( + 0, QOpenGLTexture::Red_Integer, QOpenGLTexture::UInt8, reinterpret_cast(data)); + + logging::print("uploaded {} bytes face visibility texture", face_visibility_width); +} + +void GLView::setFaceVisibilityToAllVisible() +{ + // one byte per face + int face_visibility_width = m_bsp->dfaces.size(); + + uint8_t *data = new uint8_t[face_visibility_width]; + for (int x = 0; x < face_visibility_width; ++x) { + data[x] = 16; + } + + setFaceVisibilityArray(data); + + delete[] data; +} + void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries_t &bspx, const std::vector &entities, const full_atlas_t &lightmap, const settings::common_settings &settings, bool use_bspx_normals) { + // copy the bsp for later use (FIXME: just store a pointer to MainWindow's?) + m_bsp = bsp; + if (bsp.dvis.bits.empty()) { + logging::print("no visdata\n"); + m_decompressedVis.clear(); + } else { + logging::print("decompressing visdata...\n"); + m_decompressedVis = DecompressAllVis(&bsp, true); + } + img::load_textures(&bsp, settings); std::optional facenormals; @@ -723,6 +900,7 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries // clear old data placeholder_texture.reset(); lightmap_texture.reset(); + face_visibility_texture.reset(); m_drawcalls.clear(); m_vbo.bind(); m_vbo.allocate(0); @@ -736,6 +914,7 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries m_portalIndexBuffer.allocate(0); num_leak_points = 0; num_portal_indices = 0; + m_lastLeaf = -1; int32_t highest_depth = 0; @@ -795,6 +974,11 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries delete[] data; } + // upload face visibility + if (!bsp.dfaces.empty()) { + setFaceVisibilityToAllVisible(); + } + struct face_payload { const mface_t *face; @@ -983,6 +1167,7 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries qvec3f normal; qvec3f flat_color; uint32_t styles; + int32_t face_index; }; std::vector verts; std::vector indexBuffer; @@ -1047,7 +1232,8 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries .normal = vertex_normal, .flat_color = flat_color, .styles = (uint32_t)(f->styles[0]) | (uint32_t)(f->styles[1] << 8) | - (uint32_t)(f->styles[2] << 16) | (uint32_t)(f->styles[3] << 24)}); + (uint32_t)(f->styles[2] << 16) | (uint32_t)(f->styles[3] << 24), + .face_index = fnum}); } // output the vertex indices for this face @@ -1123,6 +1309,10 @@ void GLView::renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries glEnableVertexAttribArray(5 /* attrib */); glVertexAttribIPointer( 5 /* attrib */, 1, GL_UNSIGNED_INT, sizeof(vertex_t), (void *)offsetof(vertex_t, styles)); + + // face indices + glEnableVertexAttribArray(6 /* attrib */); + glVertexAttribIPointer(6 /* attrib */, 1, GL_INT, sizeof(vertex_t), (void *)offsetof(vertex_t, face_index)); } // initialize style values diff --git a/lightpreview/glview.h b/lightpreview/glview.h index e7088a76..9e1c6e1d 100644 --- a/lightpreview/glview.h +++ b/lightpreview/glview.h @@ -56,6 +56,9 @@ class GLView : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core Q_OBJECT private: + std::optional m_bsp; + std::unordered_map> m_decompressedVis; + uint32_t m_keysPressed; std::optional m_lastFrame; std::optional m_lastMouseDownPos; @@ -64,6 +67,12 @@ private: */ float m_moveSpeed; + // vis culling stuff + /** + * -1 indicates solid leaf or no visdata (render all) + */ + int m_lastLeaf = -1; + // camera stuff float m_displayAspect; QVector3D m_cameraOrigin; @@ -113,6 +122,14 @@ private: std::shared_ptr placeholder_texture; std::shared_ptr lightmap_texture; + /** + * 1D texture, one uint8 per face. + * + * 0 = render normally + * 1 = skip face + */ + std::shared_ptr face_visibility_texture; + struct drawcall_t { material_key key; @@ -128,10 +145,11 @@ private: QOpenGLShaderProgram *m_program_wireframe = nullptr; QOpenGLShaderProgram *m_program_simple = nullptr; - // uniform locations + // uniform locations (default program) int m_program_mvp_location = 0; int m_program_texture_sampler_location = 0; int m_program_lightmap_sampler_location = 0; + int m_program_face_visibility_sampler_location = 0; int m_program_opacity_location = 0; int m_program_alpha_test_location = 0; int m_program_lightmap_only_location = 0; @@ -140,11 +158,12 @@ private: int m_program_drawflat_location = 0; int m_program_style_scalars_location = 0; - // uniform locations + // uniform locations (skybox program) int m_skybox_program_mvp_location = 0; int m_skybox_program_eye_direction_location = 0; int m_skybox_program_texture_sampler_location = 0; int m_skybox_program_lightmap_sampler_location = 0; + int m_skybox_program_face_visibility_sampler_location = 0; int m_skybox_program_opacity_location = 0; int m_skybox_program_lightmap_only_location = 0; int m_skybox_program_fullbright_location = 0; @@ -154,8 +173,9 @@ private: // uniform locations (wireframe program) int m_program_wireframe_mvp_location = 0; + int m_program_wireframe_face_visibility_sampler_location = 0; - // uniform locations + // uniform locations (simple program) int m_program_simple_mvp_location = 0; int m_program_simple_color_location = 0; @@ -163,6 +183,11 @@ public: GLView(QWidget *parent = nullptr); ~GLView(); +private: + void setFaceVisibilityArray(uint8_t *data); + void setFaceVisibilityToAllVisible(); + +public: void renderBSP(const QString &file, const mbsp_t &bsp, const bspxentries_t &bspx, const std::vector &entities, const full_atlas_t &lightmap, const settings::common_settings &settings, bool use_bspx_normals); @@ -189,6 +214,7 @@ protected: void resizeGL(int width, int height) override; private: + void updateFaceVisibility(); bool shouldLiveUpdate() const; void handleLoggedMessage(const QOpenGLDebugMessage &debugMessage);