diff --git a/src/level.cpp b/src/level.cpp index a2326fe..2bf813f 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -70,12 +70,10 @@ void Level::Clear(bool clearErrors) m_rendererQueryPoint = Vec3(); m_rendererSelectedQuadblockIndexes.clear(); m_genVisTree = false; - m_simpleVisTree = false; m_bspVis.Clear(); m_maxQuadPerLeaf = 31; m_maxLeafAxisLength = 64.0f; - m_distanceNearClip = -1.0f; - m_distanceFarClip = 1000.0f; + m_visTreeSettings = VisTreeSettings(); m_pythonConsole.clear(); m_saveScript = false; m_vrm.clear(); @@ -188,7 +186,7 @@ bool Level::GenerateBSP() if (m_bsp.IsValid()) { GenerateRenderBspData(); - if (m_genVisTree) { m_bspVis = GenerateVisTree(m_quadblocks, &m_bsp, m_simpleVisTree, m_distanceNearClip, m_distanceFarClip); } + if (m_genVisTree) { m_bspVis = GenerateVisTree(m_quadblocks, &m_bsp, m_visTreeSettings); } return true; } m_bsp.Clear(); @@ -1857,8 +1855,10 @@ void Level::GenerateRenderLevData() size_t triangleOffset = 0; for (Quadblock& qb : m_quadblocks) { - qb.SetRenderPrimitiveIndex(triangleOffset); std::vector qbTriangles = qb.ToGeometry(false); + if (qbTriangles.empty()) { continue; } + + qb.SetRenderPrimitiveIndex(triangleOffset); std::vector qbFilterTriangles = qb.ToGeometry(true); levTriangles.insert(levTriangles.end(), qbTriangles.begin(), qbTriangles.end()); filterTriangles.insert(filterTriangles.end(), qbFilterTriangles.begin(), qbFilterTriangles.end()); diff --git a/src/level.h b/src/level.h index 36998ad..317d7b4 100644 --- a/src/level.h +++ b/src/level.h @@ -97,12 +97,10 @@ class Level bool m_showLogWindow; bool m_showHotReloadWindow; bool m_loaded; - bool m_simpleVisTree; bool m_genVisTree; int m_maxQuadPerLeaf; float m_maxLeafAxisLength; - float m_distanceNearClip; - float m_distanceFarClip; + VisTreeSettings m_visTreeSettings; std::vector> m_invalidQuadblocks; std::string m_logMessage; diff --git a/src/levelui.cpp b/src/levelui.cpp index c36d06d..bf56b2d 100644 --- a/src/levelui.cpp +++ b/src/levelui.cpp @@ -749,18 +749,31 @@ void Level::RenderUI(Renderer& renderer) static ButtonUI generateBSPButton = ButtonUI(); if (ImGui::TreeNode("Advanced")) { - if (ImGui::InputInt("Max Quad Per Leaf", &m_maxQuadPerLeaf)) { m_maxQuadPerLeaf = std::max(m_maxQuadPerLeaf, 1); } - ImGui::SetItemTooltip("Lower values improve rendering performance, but increases file size and slows down vis tree generation."); - if (ImGui::InputFloat("Max Leaf Axis Length", &m_maxLeafAxisLength)) { m_maxLeafAxisLength = std::max(m_maxLeafAxisLength, 0.0f); } - ImGui::SetItemTooltip("Lower values improve rendering performance, but increases file size and slows down vis tree generation."); - if (ImGui::InputFloat("Near Clip Distance", &m_distanceNearClip)) { m_distanceNearClip = std::max(m_distanceNearClip, -1.0f); } - ImGui::SetItemTooltip("Minimum drawing distance. Higher values decrease performance and speed up the vis tree generation."); - if (ImGui::InputFloat("Far Clip Distance", &m_distanceFarClip)) { m_distanceFarClip = std::max(m_distanceFarClip, 0.0f); } - ImGui::SetItemTooltip("Maximum drawing distance. Lower values improve performance and speed up the vis tree generation."); - ImGui::Checkbox("Simple Vis Tree", &m_simpleVisTree); - ImGui::SetItemTooltip("The vis tree will be generated faster, but will be less precise"); - ImGui::Checkbox("Generate Vis Tree", &m_genVisTree); - ImGui::SetItemTooltip("Generating the vis tree may take several minutes, but the gameplay will be more performant."); + if (ImGui::TreeNodeEx("BSP Settings", ImGuiTreeNodeFlags_DefaultOpen)) + { + if (ImGui::InputInt("Max Quad Per Leaf", &m_maxQuadPerLeaf)) { m_maxQuadPerLeaf = std::max(m_maxQuadPerLeaf, 1); } + ImGui::SetItemTooltip("Lower values improve rendering performance, but increases file size and slows down vis tree generation."); + if (ImGui::InputFloat("Max Leaf Axis Length", &m_maxLeafAxisLength)) { m_maxLeafAxisLength = std::max(m_maxLeafAxisLength, 0.0f); } + ImGui::SetItemTooltip("Lower values improve rendering performance, but increases file size and slows down vis tree generation."); + + ImGui::TreePop(); + } + if (ImGui::TreeNodeEx("Vis Tree Settings", ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::Checkbox("Generate Vis Tree", &m_genVisTree); + ImGui::SetItemTooltip("Generating the vis tree may take several minutes, but the gameplay will be more performant."); + ImGui::BeginDisabled(!m_genVisTree); + if (ImGui::InputFloat("Near Clip Distance", &m_visTreeSettings.nearClipDistance)) { m_visTreeSettings.nearClipDistance = std::max(m_visTreeSettings.nearClipDistance, -1.0f); } + ImGui::SetItemTooltip("Minimum drawing distance. Higher values decrease performance and speed up the vis tree generation."); + if (ImGui::InputFloat("Far Clip Distance", &m_visTreeSettings.farClipDistance)) { m_visTreeSettings.farClipDistance = std::max(m_visTreeSettings.farClipDistance, 0.0f); } + ImGui::SetItemTooltip("Maximum drawing distance. Lower values improve performance and speed up the vis tree generation."); + ImGui::Checkbox("Assume Commutative Rays", &m_visTreeSettings.commutativeRays); + ImGui::SetItemTooltip("Speeds up VisTree generation by a factor of 2x to 3x with minimal loss of precision."); + ImGui::Checkbox("Center-Only Samples", &m_visTreeSettings.centerOnlySamples); + ImGui::SetItemTooltip("Only casts rays from each quad center (skips corner samples). Much faster, but may miss narrow visibility paths."); + ImGui::EndDisabled(); + ImGui::TreePop(); + } ImGui::TreePop(); } if (generateBSPButton.Show("Generate", buttonMessage, false)) diff --git a/src/quadblock.cpp b/src/quadblock.cpp index 9e30b97..c923c89 100644 --- a/src/quadblock.cpp +++ b/src/quadblock.cpp @@ -27,7 +27,7 @@ Quadblock::Quadblock(const std::string& name, Tri& t0, Tri& t1, Tri& t2, Tri& t3 { throw QuadException( ("Unique Vertices: " + std::to_string(uniqueCount) + "/3\n" + - "Shared Vertices: " + std::to_string(sharedCount) + "/3\n") + "Shared Vertices: " + std::to_string(sharedCount) + "/3\n") ); } @@ -193,11 +193,11 @@ Quadblock::Quadblock(const std::string& name, Tri& t0, Tri& t1, Tri& t2, Tri& t3 const Tri* uvt1 = FindTri(m_p[2].m_pos, t0, t1, t2, t3); const Tri* uvt2 = FindTri(m_p[6].m_pos, t0, t1, t2, t3); - m_uvs[0] = { GetUV(m_p[0].m_pos, *uvt0), GetUV(m_p[1].m_pos, *uvt0), GetUV(m_p[3].m_pos, *uvt0), GetUV(m_p[4].m_pos, *centerTri) }; - m_uvs[1] = { GetUV(m_p[1].m_pos, *uvt1), GetUV(m_p[2].m_pos, *uvt1), GetUV(m_p[4].m_pos, *uvt1), Vec2() }; - m_uvs[2] = { GetUV(m_p[3].m_pos, *uvt2), GetUV(m_p[4].m_pos, *uvt2), GetUV(m_p[6].m_pos, *uvt2), Vec2() }; - m_uvs[3] = { Vec2(), Vec2(), Vec2(), Vec2() }; - m_uvs[4] = { GetUV(m_p[0].m_pos, *uvt0), GetUV(m_p[2].m_pos, *uvt1), GetUV(m_p[6].m_pos, *uvt2), Vec2() }; + m_uvs[0] = {GetUV(m_p[0].m_pos, *uvt0), GetUV(m_p[1].m_pos, *uvt0), GetUV(m_p[3].m_pos, *uvt0), GetUV(m_p[4].m_pos, *centerTri)}; + m_uvs[1] = {GetUV(m_p[1].m_pos, *uvt1), GetUV(m_p[2].m_pos, *uvt1), GetUV(m_p[4].m_pos, *uvt1), Vec2()}; + m_uvs[2] = {GetUV(m_p[3].m_pos, *uvt2), GetUV(m_p[4].m_pos, *uvt2), GetUV(m_p[6].m_pos, *uvt2), Vec2()}; + m_uvs[3] = {Vec2(), Vec2(), Vec2(), Vec2()}; + m_uvs[4] = {GetUV(m_p[0].m_pos, *uvt0), GetUV(m_p[2].m_pos, *uvt1), GetUV(m_p[6].m_pos, *uvt2), Vec2()}; } else { ResetUVs(); } @@ -233,8 +233,8 @@ Quadblock::Quadblock(const std::string& name, Quad& q0, Quad& q1, Quad& q2, Quad { throw QuadException( ("Unique Vertices: " + std::to_string(uniqueCount) + "/4\n" + - "Shared Vertices: " + std::to_string(sharedCount) + "/4\n" + - "Center Vertices: " + std::to_string(centerCount) + "/1\n") + "Shared Vertices: " + std::to_string(sharedCount) + "/4\n" + + "Center Vertices: " + std::to_string(centerCount) + "/1\n") ); } @@ -306,7 +306,7 @@ Quadblock::Quadblock(const std::string& name, Quad& q0, Quad& q1, Quad& q2, Quad auto FindQuad = [](const Vec3& pos, const Quad& q0, const Quad& q1, const Quad& q2, const Quad& q3) -> const Quad* { - const Quad* quads[] = { &q0, &q1, &q2, &q3 }; + const Quad* quads[] = {&q0, &q1, &q2, &q3}; for (size_t i = 0; i < 4; i++) { const Quad* quad = quads[i]; @@ -334,10 +334,10 @@ Quadblock::Quadblock(const std::string& name, Quad& q0, Quad& q1, Quad& q2, Quad if (hasUV) { - m_uvs[0] = { GetUV(m_p[0].m_pos, *uvq0), GetUV(m_p[1].m_pos, *uvq0), GetUV(m_p[3].m_pos, *uvq0), GetUV(m_p[4].m_pos, *uvq0) }; - m_uvs[1] = { GetUV(m_p[1].m_pos, *uvq1), GetUV(m_p[2].m_pos, *uvq1), GetUV(m_p[4].m_pos, *uvq1), GetUV(m_p[5].m_pos, *uvq1) }; - m_uvs[2] = { GetUV(m_p[3].m_pos, *uvq2), GetUV(m_p[4].m_pos, *uvq2), GetUV(m_p[6].m_pos, *uvq2), GetUV(m_p[7].m_pos, *uvq2) }; - m_uvs[3] = { GetUV(m_p[4].m_pos, *uvq3), GetUV(m_p[5].m_pos, *uvq3), GetUV(m_p[7].m_pos, *uvq3), GetUV(m_p[8].m_pos, *uvq3) }; + m_uvs[0] = {GetUV(m_p[0].m_pos, *uvq0), GetUV(m_p[1].m_pos, *uvq0), GetUV(m_p[3].m_pos, *uvq0), GetUV(m_p[4].m_pos, *uvq0)}; + m_uvs[1] = {GetUV(m_p[1].m_pos, *uvq1), GetUV(m_p[2].m_pos, *uvq1), GetUV(m_p[4].m_pos, *uvq1), GetUV(m_p[5].m_pos, *uvq1)}; + m_uvs[2] = {GetUV(m_p[3].m_pos, *uvq2), GetUV(m_p[4].m_pos, *uvq2), GetUV(m_p[6].m_pos, *uvq2), GetUV(m_p[7].m_pos, *uvq2)}; + m_uvs[3] = {GetUV(m_p[4].m_pos, *uvq3), GetUV(m_p[5].m_pos, *uvq3), GetUV(m_p[7].m_pos, *uvq3), GetUV(m_p[8].m_pos, *uvq3)}; float uMin = std::numeric_limits::max(); float vMin = std::numeric_limits::max(); float uMax = -std::numeric_limits::max(); float vMax = -std::numeric_limits::max(); @@ -352,7 +352,7 @@ Quadblock::Quadblock(const std::string& name, Quad& q0, Quad& q1, Quad& q2, Quad bool indexPicked[4] = {false, false, false, false}; bool boundPicked[4] = {false, false, false, false}; - const QuadUV uvBounds = { Vec2(uMin, vMin), Vec2(uMax, vMin), Vec2(uMin, vMax), Vec2(uMax, vMax)}; + const QuadUV uvBounds = {Vec2(uMin, vMin), Vec2(uMax, vMin), Vec2(uMin, vMax), Vec2(uMax, vMax)}; for (size_t indexCount = 0; indexCount < 4; indexCount++) { @@ -390,7 +390,7 @@ Quadblock::Quadblock(const std::string& name, Quad& q0, Quad& q1, Quad& q2, Quad Quadblock::Quadblock(const PSX::Quadblock& quadblock, const std::vector& vertices, UpdateFilterCallback filterCallback) { - uint16_t reverseIndexMapping[NUM_VERTICES_QUADBLOCK] = { 0, 2, 6, 8, 1, 3, 4, 5, 7 }; + uint16_t reverseIndexMapping[NUM_VERTICES_QUADBLOCK] = {0, 2, 6, 8, 1, 3, 4, 5, 7}; for (size_t i = 0; i < NUM_VERTICES_QUADBLOCK; i++) { uint16_t index = quadblock.index[i]; @@ -449,13 +449,18 @@ Vec3 Quadblock::GetNormal() const return normal; } +const std::vector>& Quadblock::GetCollTriFacesIndexes() const +{ + return m_collTriFaces; +} + std::vector> Quadblock::GetTriFacesIndexes() const { // Return a list of (size_t, size_t, size_t) containing vertex ID // of every triface composing the quad, ordered clockwise std::vector> triFaces; - if (!m_triblock) + if (!m_triblock) { triFaces = { {0, 1, 3}, @@ -468,7 +473,7 @@ std::vector> Quadblock::GetTriFacesIndexes() const {5, 8, 7} }; } - else + else { triFaces = { {0, 1, 3}, @@ -699,6 +704,8 @@ const BoundingBox& Quadblock::GetBoundingBox() const std::vector Quadblock::ToGeometry(bool filterTriangles, const std::array* overrideUvs, const std::filesystem::path* overrideTexturePath) const { + if (GetHide()) { return std::vector(); } /* Turbo Pads */ + constexpr int NUM_VERTICES_QUAD = 4; constexpr int uvVertInd[NUM_FACES_QUADBLOCK][NUM_VERTICES_QUAD] = { @@ -756,7 +763,7 @@ std::vector Quadblock::ToGeometry(bool filterTriangles, const std::ar { 3, 4, 6 }, { 1, 4, 3 }, }; - constexpr int triblockQuadIndex[triCount] = { 0, 1, 2, 0 }; + constexpr int triblockQuadIndex[triCount] = {0, 1, 2, 0}; for (int triIndex = 0; triIndex < triCount; triIndex++) { const int quadIndex = triblockQuadIndex[triIndex]; @@ -783,7 +790,7 @@ std::vector Quadblock::ToGeometry(bool filterTriangles, const std::ar std::vector Quadblock::GetVertices() const { /* 0 1 2 3 4 5 6 7 8 */ - std::vector vertices = { m_p[0], m_p[2], m_p[6], m_p[8], m_p[1], m_p[3], m_p[4], m_p[5], m_p[7] }; + std::vector vertices = {m_p[0], m_p[2], m_p[6], m_p[8], m_p[1], m_p[3], m_p[4], m_p[5], m_p[7]}; return vertices; } @@ -894,6 +901,45 @@ void Quadblock::SetDefaultValues() m_faceDrawMode[i] = FaceDrawMode::DRAW_BOTH; m_faceRotateFlip[i] = FaceRotateFlip::NONE; } + + const bool equivalentDiagonal = ((m_p[2].m_pos - m_p[6].m_pos).Length() - ((m_p[2].m_pos - m_p[4].m_pos).Length() + (m_p[4].m_pos - m_p[6].m_pos).Length())) <= EPSILON; + if ( + equivalentDiagonal && + (((m_p[0].m_pos - m_p[2].m_pos).Length() - ((m_p[0].m_pos - m_p[1].m_pos).Length() + (m_p[1].m_pos - m_p[2].m_pos).Length())) <= EPSILON) && + (((m_p[0].m_pos - m_p[6].m_pos).Length() - ((m_p[0].m_pos - m_p[3].m_pos).Length() + (m_p[3].m_pos - m_p[6].m_pos).Length())) <= EPSILON) + ) + { + m_collTriFaces = {{0, 2, 6}}; + } + else + { + m_collTriFaces = { + {0, 1, 3}, + {1, 2, 4}, + {1, 4, 3}, + {4, 6, 3} + }; + } + + if (!m_triblock) + { + if ( + equivalentDiagonal && + (((m_p[2].m_pos - m_p[8].m_pos).Length() - ((m_p[2].m_pos - m_p[5].m_pos).Length() + (m_p[5].m_pos - m_p[8].m_pos).Length())) <= EPSILON) && + (((m_p[6].m_pos - m_p[8].m_pos).Length() - ((m_p[6].m_pos - m_p[7].m_pos).Length() + (m_p[7].m_pos - m_p[8].m_pos).Length())) <= EPSILON) + ) + { + m_collTriFaces.push_back({2, 8, 6}); + } + else + { + m_collTriFaces.push_back({2, 5, 4}); + m_collTriFaces.push_back({4, 7, 6}); + m_collTriFaces.push_back({4, 5, 7}); + m_collTriFaces.push_back({5, 8, 7}); + } + } + m_doubleSided = false; m_checkpointPathable = true; m_checkpointStatus = false; diff --git a/src/quadblock.h b/src/quadblock.h index 805938a..fac4004 100644 --- a/src/quadblock.h +++ b/src/quadblock.h @@ -117,6 +117,7 @@ class Quadblock const std::string& GetName() const; Vec3 GetCenter() const; Vec3 GetNormal() const; + const std::vector>& GetCollTriFacesIndexes() const; std::vector> GetTriFacesIndexes() const; std::array GetTriFace(size_t id0, size_t id1, size_t id2) const; uint8_t GetTerrain() const; @@ -205,6 +206,7 @@ class Quadblock size_t m_turboPadIndex; Color m_filterColor; mutable size_t m_bspID; + std::vector> m_collTriFaces; std::array m_uvs; /* Last id is reserved for low tex */ std::array m_textureIDs = { 0, 0, 0, 0, 0 }; std::array m_animTexOffset = {0, 0, 0, 0, 0}; diff --git a/src/vistree.cpp b/src/vistree.cpp index d5891e6..09663b9 100644 --- a/src/vistree.cpp +++ b/src/vistree.cpp @@ -7,7 +7,7 @@ bool BitMatrix::Get(size_t x, size_t y) const { - return m_data[(y * m_width) + x]; + return m_data[(y * m_width) + x] != 0; } size_t BitMatrix::GetWidth() const @@ -22,7 +22,14 @@ size_t BitMatrix::GetHeight() const void BitMatrix::Set(bool value, size_t x, size_t y) { - m_data[(y * m_width) + x] = value; + m_data[(y * m_width) + x] = value ? 1 : 0; +} + +void BitMatrix::SetRow(const std::vector& rowData, size_t y) +{ + if (rowData.size() != m_width) { return; } + const size_t rowStart = y * m_width; + std::copy(rowData.begin(), rowData.end(), m_data.begin() + rowStart); } bool BitMatrix::IsEmpty() const @@ -39,9 +46,9 @@ void BitMatrix::Clear() static bool WorldspaceRayTriIntersection(const Vec3& worldSpaceRayOrigin, const Vec3& worldSpaceRayDir, const std::array& tri, float& dist) { - //TODO : merge with renderer - constexpr float failsafe = 0.5f; - constexpr float barycentricTolerance = 0.5f; + //TODO : merge with renderer + constexpr float failsafe = 0.5f; + constexpr float barycentricTolerance = 0.5f; //moller-trumbore intersection test //https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm @@ -67,20 +74,17 @@ static bool WorldspaceRayTriIntersection(const Vec3& worldSpaceRayOrigin, const float v = inv_det * worldSpaceRayDir.Dot(s_cross_e1); // Allow v to be slightly outside [0, 1] range - if (v < -barycentricTolerance || v > 1.0f + barycentricTolerance) { - return false; - } + if (v < -barycentricTolerance || v > 1.0f + barycentricTolerance) { return false; } // Check if u+v is within triangle (with tolerance) // u+v > 1 means outside the triangle on the hypotenuse edge - if (u + v > 1.0f + barycentricTolerance) { - return false; - } + if (u + v > 1.0f + barycentricTolerance) { return false; } float t = inv_det * edge_2.Dot(s_cross_e1); // time value (interpolant) // Allow hits slightly behind the origin (for edge cases where ray starts on surface) - if (t > -failsafe) { + if (t > -failsafe) + { dist = t; return true; } @@ -90,8 +94,8 @@ static bool WorldspaceRayTriIntersection(const Vec3& worldSpaceRayOrigin, const static bool RayIntersectQuadblockTest(const Vec3& worldSpaceRayOrigin, const Vec3& worldSpaceRayDir, const Quadblock& qb, float& dist) { - std::vector> triFacesID = qb.GetTriFacesIndexes(); - for (std::array ids : triFacesID) + const std::vector>& triFacesID = qb.GetCollTriFacesIndexes(); + for (const std::array&ids : triFacesID) { if (WorldspaceRayTriIntersection(worldSpaceRayOrigin, worldSpaceRayDir, qb.GetTriFace(ids[0], ids[1], ids[2]), dist)) { return true; } } @@ -128,13 +132,10 @@ static bool RayIntersectBoundingBox(const Vec3& rayOrigin, const Vec3& rayDir, c && bbox.min.z - failsafe < rayOrigin.z && rayOrigin.z < bbox.max.z + failsafe) { tmin = -1.0f; // We fake tmin negative with a true return to say we are inside - return true; + return true; } // No intersection if tmax < 0 (box is behind ray) or tmin > tmax (ray misses box) - if (tmax < 0.0f || tmin > tmax) - { - return false; - } + if (tmax < 0.0f || tmin > tmax) { return false; } return true; } @@ -164,7 +165,7 @@ static void GetPotentialLeavesRecursive( // If this is a leaf node, add it's LeafWithDistance if (!node->IsBranch()) { - result.push_back({ node->GetQuadblockIndexes(), tmax }); + result.push_back({node->GetQuadblockIndexes(), tmax}); return; } @@ -198,16 +199,15 @@ static std::vector GetPotentialQuadblockIndexes( [](const LeafWithDistance& a, const LeafWithDistance& b) { return a.tmax < b.tmax; }); std::vector result; - for (const auto& leaf : leavesWithDist) + for (const LeafWithDistance& leaf : leavesWithDist) { for (size_t quadID : leaf.quadIndexes) { - const Quadblock& quad = quadblocks[quadID]; + const Quadblock& quad = quadblocks[quadID]; if (quad.GetBSPID() == leafBID || !quad.GetVisTreeTransparent()) { // Ideally, a quad has 8 normal, but I just test 2 - // Fix for triblocks please - if (quad.GetDrawDoubleSided() || quad.ComputeNormalVector(0, 2, 6).Dot(rayDir) < 0 || quad.ComputeNormalVector(2, 8, 6).Dot(rayDir) < 0) + if (quad.GetDrawDoubleSided() || quad.ComputeNormalVector(0, 2, 6).Dot(rayDir) < 0 || (quad.IsQuadblock() && quad.ComputeNormalVector(2, 8, 6).Dot(rayDir) < 0)) { result.push_back(quadID); } @@ -218,7 +218,7 @@ static std::vector GetPotentialQuadblockIndexes( return result; } -static std::vector GenerateSamplePointLeaf(const std::vector& quadblocks, const BSP& leaf, float camera_raise, bool simpleVisTree) +static std::vector GenerateSamplePointLeaf(const std::vector& quadblocks, const BSP& leaf, float camera_raise, bool centerOnlySamples) { // For a leaf node, generate all the points for the vis ray test. std::vector samples; @@ -228,21 +228,22 @@ static std::vector GenerateSamplePointLeaf(const std::vector& q const float dedupeThresholdSquared = dedupeThreshold * dedupeThreshold; // Helper to check if a point already exists in samples - auto isDuplicate = [&samples, dedupeThresholdSquared](const Vec3& point) { - for (const Vec3& existing : samples) + auto isDuplicate = [&samples, dedupeThresholdSquared](const Vec3& point) { - if ((existing - point).LengthSquared() < dedupeThresholdSquared) {return true;} - } - return false; + for (const Vec3& existing : samples) + { + if ((existing - point).LengthSquared() < dedupeThresholdSquared) { return true; } + } + return false; }; // Helper to add point if not duplicate - auto addIfUnique = [&samples, &isDuplicate](const Vec3& point, bool end) + auto addIfUnique = [&samples, &isDuplicate](const Vec3& point, bool end) { - if (!isDuplicate(point)) + if (!isDuplicate(point)) { if (end) { samples.push_back(point); } - else { samples.insert(samples.begin(),point); } + else { samples.insert(samples.begin(), point); } } }; @@ -250,12 +251,10 @@ static std::vector GenerateSamplePointLeaf(const std::vector& q { Quadblock quad = quadblocks[quadID]; float up_dist = 0.0f; - if (quad.GetFlags() & QuadFlags::GROUND) - { - up_dist = camera_raise; - } + if (quad.GetFlags() & QuadFlags::GROUND) { up_dist = camera_raise; } + addIfUnique(quad.GetCenter() + (up * up_dist), false); - if (!simpleVisTree) + if (!centerOnlySamples) { if (quad.IsQuadblock()) { @@ -277,27 +276,27 @@ static std::vector GenerateSamplePointLeaf(const std::vector& q return samples; } -float GetLeafDistanceSquared(const BSP& leaf1, const BSP& leaf2) +static float GetLeafDistanceSquared(const BSP& leaf1, const BSP& leaf2) { // Return the closest distance between the BBox of leaf1 and leaf2. // Return 0.0f if they are intersecting, or one is included in the other const BoundingBox& a = leaf1.GetBoundingBox(); const BoundingBox& b = leaf2.GetBoundingBox(); - float dx = std::max({ 0.0f, b.min.x - a.max.x, a.min.x - b.max.x }); - float dy = std::max({ 0.0f, b.min.y - a.max.y, a.min.y - b.max.y }); - float dz = std::max({ 0.0f, b.min.z - a.max.z, a.min.z - b.max.z }); - + float dx = std::max({0.0f, b.min.x - a.max.x, a.min.x - b.max.x}); + float dy = std::max({0.0f, b.min.y - a.max.y, a.min.y - b.max.y}); + float dz = std::max({0.0f, b.min.z - a.max.z, a.min.z - b.max.z}); return (dx * dx + dy * dy + dz * dz); } -BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* root, bool simpleVisTree, float minDistance, float maxDistance) +BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* root, const VisTreeSettings& settings) { auto start_time = std::chrono::high_resolution_clock::now(); - const float maxDistanceSquared = maxDistance * maxDistance; + const float maxDistanceSquared = settings.farClipDistance * settings.farClipDistance; std::vector leaves = root->GetLeaves(); BitMatrix vizMatrix = BitMatrix(leaves.size(), leaves.size()); + const int leafCount = static_cast(leaves.size()); const float cameraHeight = 5.0f; const float failsafe = 0.5f; @@ -310,23 +309,31 @@ BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* r for (size_t index : quadIndexes) { quadIndexesToLeaves[index] = i; } } - for (size_t leafA = 0; leafA < leaves.size(); leafA++) + std::vector> sourceSamples(leaves.size()); + std::vector> targetSamples(leaves.size()); + for (size_t i = 0; i < leaves.size(); i++) { - printf("Prog: %d/%d\n", static_cast(leafA + 1), static_cast(leaves.size())); - vizMatrix.Set(true, leafA, leafA); - const std::vector sampleA = GenerateSamplePointLeaf(quadblocks, *leaves[leafA], cameraHeight, simpleVisTree); - for (size_t leafB = 0; leafB < leaves.size(); leafB++) + sourceSamples[i] = GenerateSamplePointLeaf(quadblocks, *leaves[i], cameraHeight, settings.centerOnlySamples); + targetSamples[i] = GenerateSamplePointLeaf(quadblocks, *leaves[i], 0.0f, settings.centerOnlySamples); + } + + std::vector> visibilityRows(leaves.size(), std::vector(leaves.size(), 0)); +#pragma omp parallel for schedule(dynamic) + for (int leafAInt = 0; leafAInt < leafCount; leafAInt++) + { + const size_t leafA = static_cast(leafAInt); + const int leafBStart = settings.commutativeRays ? leafAInt : 0; + for (int leafBInt = leafBStart; leafBInt < leafCount; leafBInt++) { - bool foundLeafABHit = false; - const std::vector sampleB = GenerateSamplePointLeaf(quadblocks, *leaves[leafB], 0.0f, simpleVisTree); + const size_t leafB = static_cast(leafBInt); - // Pre-build sets of quadblock indices for leafA and leafB for quick lookup - const std::vector& quadIndexesA = leaves[leafA]->GetQuadblockIndexes(); - const std::vector& quadIndexesB = leaves[leafB]->GetQuadblockIndexes(); + bool foundLeafABHit = (leafA == leafB); + const std::vector& sampleA = sourceSamples[leafA]; + const std::vector& sampleB = targetSamples[leafB]; float distBboxsquared = GetLeafDistanceSquared(*leaves[leafA], *leaves[leafB]); // If minDistance is positive, and bigger than distBbox - if (minDistance > -0.0001f && minDistance * minDistance >= distBboxsquared) + if ((settings.nearClipDistance > -EPSILON) && (settings.nearClipDistance * settings.nearClipDistance >= distBboxsquared)) { foundLeafABHit = true; } @@ -353,12 +360,12 @@ BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* r } if (tmin < 0.0f) { - // We are inside the Bbox. + // We are inside the Bbox. foundLeafABHit = true; break; } - std::vector potentialQuads = GetPotentialQuadblockIndexes(quadblocks, root, pointA, directionVector, leaves[leafB]->GetId() , tmax); + std::vector potentialQuads = GetPotentialQuadblockIndexes(quadblocks, root, pointA, directionVector, leaves[leafB]->GetId(), tmax); float closestDist = std::numeric_limits::max(); size_t closestLeaf = leafA; @@ -367,10 +374,7 @@ BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* r // Single loop: test quads and check for blocking simultaneously for (size_t i = 0; i < potentialQuads.size(); i++) { - if (foundBlockingQuad) - { - break; - } + if (foundBlockingQuad) { break; } size_t testQuadIndex = potentialQuads[i]; size_t quadLeaf = quadIndexesToLeaves[testQuadIndex]; @@ -396,32 +400,31 @@ BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* r } // If we found a blocking quad, skip to next pointB - if (foundBlockingQuad) - { - continue; - } + if (foundBlockingQuad) { continue; } - if (closestLeaf == leafB) - { - foundLeafABHit = true; - } + if (closestLeaf == leafB) { foundLeafABHit = true; } } } + if (foundLeafABHit) { - vizMatrix.Set(true, leafA, leafB); + visibilityRows[leafA][leafB] = 1; + if (settings.commutativeRays) { visibilityRows[leafB][leafA] = 1; } } } } + + for (size_t leafA = 0; leafA < leaves.size(); leafA++) + { + vizMatrix.SetRow(visibilityRows[leafA], leafA); + } + int count = 0; for (size_t leafA = 0; leafA < leaves.size(); leafA++) { for (size_t leafB = 0; leafB < leaves.size(); leafB++) { - if (vizMatrix.Get(leafA, leafB)) - { - count++; - } + if (vizMatrix.Get(leafA, leafB)) { count++; } } } int max = static_cast(leaves.size() * leaves.size()); @@ -435,4 +438,4 @@ BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* r printf("Runtime %lldmin %lldsec\n", mins, secs); return vizMatrix; -} \ No newline at end of file +} diff --git a/src/vistree.h b/src/vistree.h index b363b61..c794820 100644 --- a/src/vistree.h +++ b/src/vistree.h @@ -5,24 +5,35 @@ #include "geo.h" #include +#include + +struct VisTreeSettings +{ + bool centerOnlySamples; + bool commutativeRays; + float nearClipDistance; + float farClipDistance; + VisTreeSettings() : centerOnlySamples(false), commutativeRays(false), nearClipDistance(-1.0f), farClipDistance(1000.0f) {} +}; class BitMatrix { public: - BitMatrix() {}; - BitMatrix(size_t width, size_t height) : m_width(width), m_height(height), m_data(width * height) {} + BitMatrix() : m_width(0), m_height(0) {}; + BitMatrix(size_t width, size_t height) : m_width(width), m_height(height), m_data(width * height, 0) {} bool Get(size_t x, size_t y) const; size_t GetWidth() const; size_t GetHeight() const; void Set(bool value, size_t x, size_t y); + void SetRow(const std::vector& rowData, size_t y); bool IsEmpty() const; void Clear(); private: size_t m_width; size_t m_height; - std::vector m_data; + std::vector m_data; }; -BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* root, bool simpleVisTree, float minDistance, float maxDistance); +BitMatrix GenerateVisTree(const std::vector& quadblocks, const BSP* root, const VisTreeSettings& settings);