From 6ff8d36c81f9d9e9f7b3c56fd4fb6f2387b270af Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 3 Sep 2024 23:09:49 +0100 Subject: [PATCH 01/16] Rework GeoPatch to reduce size --- src/GeoPatch.cpp | 133 ++++++++++++++++++++++++---------------------- src/GeoPatch.h | 101 ++++++++++++++++++++++++++--------- src/GeoSphere.cpp | 2 +- 3 files changed, 145 insertions(+), 91 deletions(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index d6aea29d69..75a690f832 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -20,6 +20,7 @@ #include "perlin.h" #include "profiler/Profiler.h" #include "vcacheopt/vcacheopt.h" +#include "Core/Log.h" #include #include @@ -33,6 +34,14 @@ #include "graphics/Drawables.h" #endif +namespace PatchMaths{ + // in patch surface coords, [0,1] + inline vector3d GetSpherePoint(const vector3d &v0, const vector3d &v1, const vector3d &v2, const vector3d &v3, const double x, const double y) + { + return (v0 + x * (1.0 - y) * (v1 - v0) + x * y * (v2 - v0) + (1.0 - x) * y * (v3 - v0)).Normalized(); + } +} + // tri edge lengths static const double GEOPATCH_SUBDIVIDE_AT_CAMDIST = 5.0; @@ -40,54 +49,46 @@ GeoPatch::GeoPatch(const RefCountedPtr &ctx_, GeoSphere *gs, const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_, const int depth, const GeoPatchID &ID_) : m_ctx(ctx_), - m_v0(v0_), - m_v1(v1_), - m_v2(v2_), - m_v3(v3_), - m_heights(nullptr), - m_normals(nullptr), - m_colors(nullptr), - m_parent(nullptr), + m_corners(new Corners(v0_, v1_, v2_, v3_)), m_geosphere(gs), - m_depth(depth), m_PatchID(ID_), - m_HasJobRequest(false) + m_depth(depth), + m_needUpdateVBOs(false), + m_hasJobRequest(false) { - - m_clipCentroid = (m_v0 + m_v1 + m_v2 + m_v3) * 0.25; - m_centroid = m_clipCentroid.Normalized(); + m_clipCentroid = ((v0_ + v1_ + v2_ + v3_) * 0.25).Normalized(); m_clipRadius = 0.0; - m_clipRadius = std::max(m_clipRadius, (m_v0 - m_clipCentroid).Length()); - m_clipRadius = std::max(m_clipRadius, (m_v1 - m_clipCentroid).Length()); - m_clipRadius = std::max(m_clipRadius, (m_v2 - m_clipCentroid).Length()); - m_clipRadius = std::max(m_clipRadius, (m_v3 - m_clipCentroid).Length()); + m_clipRadius = std::max(m_clipRadius, (v0_ - m_clipCentroid).Length()); + m_clipRadius = std::max(m_clipRadius, (v1_ - m_clipCentroid).Length()); + m_clipRadius = std::max(m_clipRadius, (v2_ - m_clipCentroid).Length()); + m_clipRadius = std::max(m_clipRadius, (v3_ - m_clipCentroid).Length()); + double distMult; if (m_geosphere->GetSystemBody()->GetType() < SystemBody::TYPE_PLANET_ASTEROID) { distMult = 10.0 / Clamp(m_depth, 1, 10); } else { distMult = 5.0 / Clamp(m_depth, 1, 5); } - m_roughLength = GEOPATCH_SUBDIVIDE_AT_CAMDIST / pow(2.0, m_depth) * distMult; - m_needUpdateVBOs = false; + m_splitLength = GEOPATCH_SUBDIVIDE_AT_CAMDIST / pow(2.0, m_depth) * distMult; + } GeoPatch::~GeoPatch() { - m_HasJobRequest = false; + ClearHasJobRequest(); for (int i = 0; i < NUM_KIDS; i++) { m_kids[i].reset(); } - m_heights.reset(); - m_normals.reset(); - m_colors.reset(); + m_corners.reset(); + m_patchVBOData.reset(); } void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) { PROFILE_SCOPED() - if (m_needUpdateVBOs) { + if (NeedToUpdateVBOs()) { assert(renderer); - m_needUpdateVBOs = false; + ClearNeedToUpdateVBOs(); //create buffer and upload data auto vbd = Graphics::VertexBufferDesc::FromAttribSet(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_NORMAL | Graphics::ATTRIB_DIFFUSE | Graphics::ATTRIB_UV0); @@ -100,12 +101,17 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) const Sint32 edgeLen = m_ctx->GetEdgeLen(); const double frac = m_ctx->GetFrac(); - const double *pHts = m_heights.get(); - const vector3f *pNorm = m_normals.get(); - const Color3ub *pColr = m_colors.get(); + const double *pHts = m_patchVBOData->m_heights.get(); + const vector3f *pNorm = m_patchVBOData->m_normals.get(); + const Color3ub *pColr = m_patchVBOData->m_colors.get(); double minh = DBL_MAX; + const vector3d v0 = m_corners->m_v0; + const vector3d v1 = m_corners->m_v1; + const vector3d v2 = m_corners->m_v2; + const vector3d v3 = m_corners->m_v3; + // ---------------------------------------------------- // inner loops for (Sint32 y = 1; y < edgeLen - 1; y++) { @@ -114,7 +120,7 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) minh = std::min(height, minh); const double xFrac = double(x - 1) * frac; const double yFrac = double(y - 1) * frac; - const vector3d p((GetSpherePoint(xFrac, yFrac) * (height + 1.0)) - m_clipCentroid); + const vector3d p((PatchMaths::GetSpherePoint(v0, v1, v2, v3, xFrac, yFrac) * (height + 1.0)) - m_clipCentroid); m_clipRadius = std::max(m_clipRadius, p.Length()); GeoPatchContext::VBOVertex *vtxPtr = &VBOVtxPtr[x + (y * edgeLen)]; @@ -150,7 +156,7 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) const Sint32 x = innerLeft - 1; const double xFrac = double(x - 1) * frac; const double yFrac = double(y - 1) * frac; - const vector3d p((GetSpherePoint(xFrac, yFrac) * minhScale) - m_clipCentroid); + const vector3d p((PatchMaths::GetSpherePoint(v0, v1, v2, v3, xFrac, yFrac) * minhScale) - m_clipCentroid); GeoPatchContext::VBOVertex *vtxPtr = &VBOVtxPtr[outerLeft + (y * edgeLen)]; GeoPatchContext::VBOVertex *vtxInr = &VBOVtxPtr[innerLeft + (y * edgeLen)]; @@ -164,7 +170,7 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) const Sint32 x = innerRight + 1; const double xFrac = double(x - 1) * frac; const double yFrac = double(y - 1) * frac; - const vector3d p((GetSpherePoint(xFrac, yFrac) * minhScale) - m_clipCentroid); + const vector3d p((PatchMaths::GetSpherePoint(v0, v1, v2, v3, xFrac, yFrac) * minhScale) - m_clipCentroid); GeoPatchContext::VBOVertex *vtxPtr = &VBOVtxPtr[outerRight + (y * edgeLen)]; GeoPatchContext::VBOVertex *vtxInr = &VBOVtxPtr[innerRight + (y * edgeLen)]; @@ -184,7 +190,7 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) const Sint32 y = innerTop - 1; const double xFrac = double(x - 1) * frac; const double yFrac = double(y - 1) * frac; - const vector3d p((GetSpherePoint(xFrac, yFrac) * minhScale) - m_clipCentroid); + const vector3d p((PatchMaths::GetSpherePoint(v0, v1, v2, v3, xFrac, yFrac) * minhScale) - m_clipCentroid); GeoPatchContext::VBOVertex *vtxPtr = &VBOVtxPtr[x + (outerTop * edgeLen)]; GeoPatchContext::VBOVertex *vtxInr = &VBOVtxPtr[x + (innerTop * edgeLen)]; @@ -198,7 +204,7 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) const Sint32 y = innerBottom + 1; const double xFrac = double(x - 1) * frac; const double yFrac = double(y - 1) * frac; - const vector3d p((GetSpherePoint(xFrac, yFrac) * minhScale) - m_clipCentroid); + const vector3d p((PatchMaths::GetSpherePoint(v0, v1, v2, v3, xFrac, yFrac) * minhScale) - m_clipCentroid); GeoPatchContext::VBOVertex *vtxPtr = &VBOVtxPtr[x + (outerBottom * edgeLen)]; GeoPatchContext::VBOVertex *vtxInr = &VBOVtxPtr[x + (innerBottom * edgeLen)]; @@ -239,11 +245,12 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) vtxBuffer->Unmap(); // use the new vertex buffer and the shared index buffer - m_patchMesh.reset(renderer->CreateMeshObject(vtxBuffer, m_ctx->GetIndexBuffer())); + m_patchVBOData->m_patchMesh.reset(renderer->CreateMeshObject(vtxBuffer, m_ctx->GetIndexBuffer())); // Don't need this anymore so throw it away - m_normals.reset(); - m_colors.reset(); + //m_patchVBOData->m_heights.reset(); + m_patchVBOData->m_normals.reset(); + m_patchVBOData->m_colors.reset(); #ifdef DEBUG_BOUNDING_SPHERES RefCountedPtr mat(Pi::renderer->CreateMaterial("unlit", Graphics::MaterialDescriptor(), Graphics::RenderStateDesc())); @@ -278,7 +285,7 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons if (m_kids[0]) { for (int i = 0; i < NUM_KIDS; i++) m_kids[i]->Render(renderer, campos, modelView, frustum); - } else if (m_heights) { + } else if (HasHeightData()) { const vector3d relpos = m_clipCentroid - campos; renderer->SetTransform(matrix4x4f(modelView * matrix4x4d::Translation(relpos))); @@ -289,10 +296,10 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons const float fDetailFrequency = pow(2.0f, float(m_geosphere->GetMaxDepth()) - float(m_depth)); m_geosphere->GetSurfaceMaterial()->SetPushConstant("PatchDetailFrequency"_hash, fDetailFrequency); - renderer->DrawMesh(m_patchMesh.get(), m_geosphere->GetSurfaceMaterial().Get()); + renderer->DrawMesh(m_patchVBOData->m_patchMesh.get(), m_geosphere->GetSurfaceMaterial().Get()); #if DEBUG_CENTROIDS - renderer->SetTransform(matrix4x4f(modelView * matrix4x4d::Translation(m_centroid - campos) * matrix4x4d::ScaleMatrix(0.2 / pow(2.0f, m_depth)))); + renderer->SetTransform(matrix4x4f(modelView * matrix4x4d::Translation(m_clipCentroid - campos) * matrix4x4d::ScaleMatrix(0.2 / pow(2.0f, m_depth)))); Graphics::Drawables::GetAxes3DDrawable(renderer)->Draw(renderer); #endif @@ -310,7 +317,7 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustum) { // there should be no LOD update when we have active split requests - if (m_HasJobRequest) + if (HasJobRequest()) return; bool canSplit = true; @@ -318,9 +325,9 @@ void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustu // always split at first level double centroidDist = DBL_MAX; - if (m_parent) { - centroidDist = (campos - m_centroid).Length(); // distance from camera to centre of the patch - const bool tooFar = (centroidDist >= m_roughLength); // check if the distance is greater than the rough length, which is how far it should be before it can split + if (m_depth > 0) { + centroidDist = (campos - m_clipCentroid).Length(); // distance from camera to centre of the patch + const bool tooFar = (centroidDist >= m_splitLength); // check if the distance is greater than the rough length, which is how far it should be before it can split if (m_depth >= std::min(GEOPATCH_MAX_DEPTH, m_geosphere->GetMaxDepth()) || tooFar) { canSplit = false; // we're too deep in the quadtree or too far away so cannot split } @@ -338,10 +345,10 @@ void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustu } // we can see this patch so submit the jobs! - assert(!m_HasJobRequest); - m_HasJobRequest = true; + assert(!HasJobRequest()); + SetHasJobRequest(); - SQuadSplitRequest *ssrd = new SQuadSplitRequest(m_v0, m_v1, m_v2, m_v3, m_centroid.Normalized(), m_depth, + SQuadSplitRequest *ssrd = new SQuadSplitRequest(m_corners->m_v0, m_corners->m_v1, m_corners->m_v2, m_corners->m_v3, m_clipCentroid.Normalized(), m_depth, m_geosphere->GetSystemBody()->GetPath(), m_PatchID, m_ctx->GetEdgeLen() - 2, m_ctx->GetFrac(), m_geosphere->GetTerrain()); @@ -366,10 +373,10 @@ void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustu void GeoPatch::RequestSinglePatch() { - if (!m_heights) { - assert(!m_HasJobRequest); - m_HasJobRequest = true; - SSingleSplitRequest *ssrd = new SSingleSplitRequest(m_v0, m_v1, m_v2, m_v3, m_centroid.Normalized(), m_depth, + if (!HasHeightData()) { + assert(!HasJobRequest()); + SetHasJobRequest(); + SSingleSplitRequest *ssrd = new SSingleSplitRequest(m_corners->m_v0, m_corners->m_v1, m_corners->m_v2, m_corners->m_v3, m_clipCentroid.Normalized(), m_depth, m_geosphere->GetSystemBody()->GetPath(), m_PatchID, m_ctx->GetEdgeLen() - 2, m_ctx->GetFrac(), m_geosphere->GetTerrain()); m_job = Pi::GetAsyncJobQueue()->Queue(new SinglePatchJob(ssrd)); } @@ -388,7 +395,7 @@ void GeoPatch::ReceiveHeightmaps(SQuadSplitResult *psr) psr->OnCancel(); } } else { - assert(m_HasJobRequest); + assert(HasJobRequest()); const int newDepth = m_depth + 1; for (int i = 0; i < NUM_KIDS; i++) { assert(!m_kids[i]); @@ -399,44 +406,40 @@ void GeoPatch::ReceiveHeightmaps(SQuadSplitResult *psr) data.v0, data.v1, data.v2, data.v3, newDepth, data.patchID)); } - m_kids[0]->m_parent = m_kids[1]->m_parent = m_kids[2]->m_parent = m_kids[3]->m_parent = this; for (int i = 0; i < NUM_KIDS; i++) { m_kids[i]->ReceiveHeightResult(psr->data(i)); } - m_HasJobRequest = false; + ClearHasJobRequest(); } } void GeoPatch::ReceiveHeightmap(const SSingleSplitResult *psr) { PROFILE_SCOPED() - assert(nullptr == m_parent); assert(nullptr != psr); - assert(m_HasJobRequest); + assert(HasJobRequest()); ReceiveHeightResult(psr->data()); - m_HasJobRequest = false; + ClearHasJobRequest(); } void GeoPatch::ReceiveHeightResult(const SSplitResultData &data) { - m_heights.reset(data.heights); - m_normals.reset(data.normals); - m_colors.reset(data.colors); + m_patchVBOData.reset(new PatchVBOData(data.heights, data.normals, data.colors)); // skirt vertices are not present in the heights array const int edgeLen = m_ctx->GetEdgeLen() - 2; - const double h0 = m_heights[0]; - const double h1 = m_heights[edgeLen - 1]; - const double h2 = m_heights[edgeLen * (edgeLen - 1)]; - const double h3 = m_heights[edgeLen * edgeLen - 1]; + const double h0 = data.heights[0]; + const double h1 = data.heights[edgeLen - 1]; + const double h2 = data.heights[edgeLen * (edgeLen - 1)]; + const double h3 = data.heights[edgeLen * edgeLen - 1]; const double height = (h0 + h1 + h2 + h3) * 0.25; - m_centroid *= (1.0 + height); + m_clipCentroid *= (1.0 + height); - NeedToUpdateVBOs(); + SetNeedToUpdateVBOs(); } void GeoPatch::ReceiveJobHandle(Job::Handle job) diff --git a/src/GeoPatch.h b/src/GeoPatch.h index 9facbd2342..5e774bf44a 100644 --- a/src/GeoPatch.h +++ b/src/GeoPatch.h @@ -45,11 +45,6 @@ class GeoPatch { ~GeoPatch(); - inline void NeedToUpdateVBOs() - { - m_needUpdateVBOs = (nullptr != m_heights); - } - void UpdateVBOs(Graphics::Renderer *renderer); int GetChildIdx(const GeoPatch *child) const @@ -61,12 +56,6 @@ class GeoPatch { return -1; } - // in patch surface coords, [0,1] - inline vector3d GetSpherePoint(const double x, const double y) const - { - return (m_v0 + x * (1.0 - y) * (m_v1 - m_v0) + x * y * (m_v2 - m_v0) + (1.0 - x) * y * (m_v3 - m_v0)).Normalized(); - } - void Render(Graphics::Renderer *r, const vector3d &campos, const matrix4x4d &modelView, const Graphics::Frustum &frustum); inline bool canBeMerged() const @@ -77,7 +66,7 @@ class GeoPatch { merge &= m_kids[i]->canBeMerged(); } } - merge &= !(m_HasJobRequest); + merge &= !(HasJobRequest()); return merge; } @@ -89,7 +78,41 @@ class GeoPatch { void ReceiveHeightResult(const SSplitResultData &data); void ReceiveJobHandle(Job::Handle job); - inline bool HasHeightData() const { return (m_heights.get() != nullptr); } + inline bool HasHeightData() const { return (m_patchVBOData != nullptr) && (m_patchVBOData->m_heights.get() != nullptr); } + + // used by GeoSphere so must be public + inline void SetNeedToUpdateVBOs() + { + m_needUpdateVBOs = HasHeightData(); + } + +private: + + inline bool NeedToUpdateVBOs() const + { + return m_needUpdateVBOs; + } + + inline void ClearNeedToUpdateVBOs() + { + m_needUpdateVBOs = false; + } + + inline void SetHasJobRequest() + { + m_hasJobRequest = true; + } + + inline bool HasJobRequest() const + { + return m_hasJobRequest; + } + + inline void ClearHasJobRequest() + { + m_hasJobRequest = false; + } + private: static const int NUM_KIDS = 4; @@ -97,23 +120,51 @@ class GeoPatch { bool IsOverHorizon(const vector3d &camPos); RefCountedPtr m_ctx; - const vector3d m_v0, m_v1, m_v2, m_v3; - std::unique_ptr m_heights; - std::unique_ptr m_normals; - std::unique_ptr m_colors; - std::unique_ptr m_patchMesh; + struct Corners { + Corners(const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_) : + m_v0(v0_), + m_v1(v1_), + m_v2(v2_), + m_v3(v3_) + {} + Corners() = delete; + const vector3d m_v0, m_v1, m_v2, m_v3; + }; + + struct PatchVBOData { + PatchVBOData() = delete; + PatchVBOData(double* h, vector3f* n, Color3ub* c) + { + m_heights.reset(h); + m_normals.reset(n); + m_colors.reset(c); + } + ~PatchVBOData() { + m_heights.reset(); + m_normals.reset(); + m_colors.reset(); + m_patchMesh.reset(); + } + std::unique_ptr m_heights; + std::unique_ptr m_normals; + std::unique_ptr m_colors; + std::unique_ptr m_patchMesh; + }; + + std::unique_ptr m_corners; + std::unique_ptr m_patchVBOData; std::unique_ptr m_kids[NUM_KIDS]; - GeoPatch *m_parent; + + vector3d m_clipCentroid; GeoSphere *m_geosphere; - double m_roughLength; - vector3d m_clipCentroid, m_centroid; + double m_splitLength; // rough length, is how near to the camera the m_clipCentroid should be before it must split double m_clipRadius; - Sint32 m_depth; - bool m_needUpdateVBOs; - const GeoPatchID m_PatchID; Job::Handle m_job; - bool m_HasJobRequest; + Sint32 m_depth; + uint8_t m_patchUpdateState; + bool m_needUpdateVBOs; + bool m_hasJobRequest; #ifdef DEBUG_BOUNDING_SPHERES std::unique_ptr m_boundsphere; #endif diff --git a/src/GeoSphere.cpp b/src/GeoSphere.cpp index 6f2bf74e68..d6c6e179ca 100644 --- a/src/GeoSphere.cpp +++ b/src/GeoSphere.cpp @@ -342,7 +342,7 @@ void GeoSphere::Update() } break; case eReceivedFirstPatches: { for (int i = 0; i < NUM_PATCHES; i++) { - m_patches[i]->NeedToUpdateVBOs(); + m_patches[i]->SetNeedToUpdateVBOs(); } m_initStage = eDefaultUpdateState; } break; From a899fb700591817ac24356ba9183558d503a291d Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Thu, 14 Nov 2024 16:47:40 +0000 Subject: [PATCH 02/16] Gather renderable patches, distance sort them, render in near to far order. Absolutely no performance improvement at all! Possibly slightly worse. --- src/GeoPatch.cpp | 30 ++++++++++++++++++++++++++++++ src/GeoPatch.h | 6 +++++- src/GeoSphere.cpp | 31 +++++++++++++++++++++++++++++-- src/GeoSphere.h | 2 ++ 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index 75a690f832..8c97fb0243 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -286,6 +286,13 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons for (int i = 0; i < NUM_KIDS; i++) m_kids[i]->Render(renderer, campos, modelView, frustum); } else if (HasHeightData()) { + RenderImmediate(renderer, campos, modelView); + } +} + +void GeoPatch::RenderImmediate(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView) const +{ + if (m_patchVBOData->m_heights) { const vector3d relpos = m_clipCentroid - campos; renderer->SetTransform(matrix4x4f(modelView * matrix4x4d::Translation(relpos))); @@ -314,6 +321,29 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons } } +void GeoPatch::GatherRenderablePatches(std::vector &visiblePatches, Graphics::Renderer *renderer, const vector3d &campos, const Graphics::Frustum &frustum) +{ + PROFILE_SCOPED() + // must update the VBOs to calculate the clipRadius... + UpdateVBOs(renderer); + // ...before doing the frustum culling that relies on it. + if (!frustum.TestPoint(m_clipCentroid, m_clipRadius)) + return; // nothing below this patch is visible + + // only want to horizon cull patches that can actually be over the horizon! + if (IsOverHorizon(campos)) { + return; + } + + if (m_kids[0]) { + for (int i = 0; i < NUM_KIDS; i++) + m_kids[i]->GatherRenderablePatches(visiblePatches, renderer, campos, frustum); + } else if (m_patchVBOData->m_heights) { + visiblePatches.emplace_back(this); + } +} + + void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustum) { // there should be no LOD update when we have active split requests diff --git a/src/GeoPatch.h b/src/GeoPatch.h index 5e774bf44a..c9dc99b9c1 100644 --- a/src/GeoPatch.h +++ b/src/GeoPatch.h @@ -56,7 +56,9 @@ class GeoPatch { return -1; } - void Render(Graphics::Renderer *r, const vector3d &campos, const matrix4x4d &modelView, const Graphics::Frustum &frustum); + void Render(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView, const Graphics::Frustum &frustum); + void RenderImmediate(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView) const; + void GatherRenderablePatches(std::vector &visiblePatches, Graphics::Renderer *renderer, const vector3d &campos, const Graphics::Frustum &frustum); inline bool canBeMerged() const { @@ -80,6 +82,8 @@ class GeoPatch { inline bool HasHeightData() const { return (m_patchVBOData != nullptr) && (m_patchVBOData->m_heights.get() != nullptr); } + inline const vector3d &Centroid() const { return m_clipCentroid; } + // used by GeoSphere so must be public inline void SetNeedToUpdateVBOs() { diff --git a/src/GeoSphere.cpp b/src/GeoSphere.cpp index d6c6e179ca..5d8f180646 100644 --- a/src/GeoSphere.cpp +++ b/src/GeoSphere.cpp @@ -174,6 +174,8 @@ void GeoSphere::Reset() CalculateMaxPatchDepth(); + m_visiblePatches.reserve(1024); + m_initStage = eBuildFirstPatches; } @@ -191,6 +193,8 @@ GeoSphere::GeoSphere(const SystemBody *body) : CalculateMaxPatchDepth(); + m_visiblePatches.reserve(1024); + //SetUpMaterials is not called until first Render since light count is zero :) } @@ -441,8 +445,31 @@ void GeoSphere::Render(Graphics::Renderer *renderer, const matrix4x4d &modelView renderer->SetTransform(matrix4x4f(modelView)); - for (int i = 0; i < NUM_PATCHES; i++) { - m_patches[i]->Render(renderer, campos, modelView, frustum); + if (Pi::config->Int("SortGeoPatches") == 0) { + for (int i = 0; i < NUM_PATCHES; i++) { + m_patches[i]->Render(renderer, campos, modelView, frustum); + } + } else { + // Gather the patches that could be rendered + for (int i = 0; i < NUM_PATCHES; i++) { + m_patches[i]->GatherRenderablePatches(m_visiblePatches, renderer, campos, frustum); + } + + // distance sort the patches + std::sort(m_visiblePatches.begin(), m_visiblePatches.end(), [&, campos](const GeoPatch *a, const GeoPatch *b) { + return (a->Centroid() - campos).LengthSqr() < (b->Centroid() - campos).LengthSqr(); + }); + + // cull occluded patches somehow? + // create frustum from corner points, something vertical, and the campos??? Cull anything within that frustum? + + // render the sorted patches + for (GeoPatch *pPatch : m_visiblePatches) { + pPatch->RenderImmediate(renderer, campos, modelView); + } + + // must clear this after each render otherwise it just accumulates every patch ever drawn! + m_visiblePatches.clear(); } renderer->SetAmbientColor(oldAmbient); diff --git a/src/GeoSphere.h b/src/GeoSphere.h index 9330c2b617..0f3ad03a8c 100644 --- a/src/GeoSphere.h +++ b/src/GeoSphere.h @@ -80,6 +80,8 @@ class GeoSphere : public BaseSphere { void ProcessQuadSplitRequests(); std::unique_ptr m_patches[6]; + std::vector m_visiblePatches; + struct TDistanceRequest { TDistanceRequest(double dist, SQuadSplitRequest *pRequest, GeoPatch *pRequester) : mDistance(dist), From ee3325d954b547e924ec9763d993e93e32cd9723 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Mon, 18 Nov 2024 12:22:49 +0000 Subject: [PATCH 03/16] Deduplicate code into IsPatchVisible, fix patch not rendering - Created method IsPatchVisible to unify patch visibility testing - Don't test patch depth zero against horizon culling - Fixes issue 5806 --- src/GeoPatch.cpp | 42 +++++++++++++++++++++--------------------- src/GeoPatch.h | 3 ++- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index 8c97fb0243..5013999ea3 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -273,14 +273,9 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons PROFILE_SCOPED() // must update the VBOs to calculate the clipRadius... UpdateVBOs(renderer); - // ...before doing the frustum culling that relies on it. - if (!frustum.TestPoint(m_clipCentroid, m_clipRadius)) - return; // nothing below this patch is visible - - // only want to horizon cull patches that can actually be over the horizon! - if (IsOverHorizon(campos)) { + // ...before doing the visibility testing that relies on it. + if (!IsPatchVisible(frustum, campos)) return; - } if (m_kids[0]) { for (int i = 0; i < NUM_KIDS; i++) @@ -326,14 +321,9 @@ void GeoPatch::GatherRenderablePatches(std::vector &visiblePatches, PROFILE_SCOPED() // must update the VBOs to calculate the clipRadius... UpdateVBOs(renderer); - // ...before doing the frustum culling that relies on it. - if (!frustum.TestPoint(m_clipCentroid, m_clipRadius)) - return; // nothing below this patch is visible - - // only want to horizon cull patches that can actually be over the horizon! - if (IsOverHorizon(campos)) { + // ...before doing the visibility testing that relies on it. + if (!IsPatchVisible(frustum, campos)) return; - } if (m_kids[0]) { for (int i = 0; i < NUM_KIDS; i++) @@ -366,13 +356,8 @@ void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustu if (canSplit) { if (!m_kids[0]) { // Test if this patch is visible - if (!frustum.TestPoint(m_clipCentroid, m_clipRadius)) - return; // nothing below this patch is visible - - // only want to horizon cull patches that can actually be over the horizon! - if (IsOverHorizon(campos)) { + if (!IsPatchVisible(frustum, campos)) return; - } // we can see this patch so submit the jobs! assert(!HasJobRequest()); @@ -478,7 +463,22 @@ void GeoPatch::ReceiveJobHandle(Job::Handle job) m_job = static_cast(job); } -bool GeoPatch::IsOverHorizon(const vector3d &camPos) +bool GeoPatch::IsPatchVisible(const Graphics::Frustum &frustum, const vector3d &camPos) const +{ + // Test if this patch is visible + if (!frustum.TestPoint(m_clipCentroid, m_clipRadius)) + return false; // nothing below this patch is visible + + // We only want to horizon cull patches that can actually be over the horizon! + // depth == zero patches cannot be tested against the horizon + if (m_depth != 0 && IsOverHorizon(camPos)) { + return false; + } + + return true; +} + +bool GeoPatch::IsOverHorizon(const vector3d &camPos) const { const vector3d camDir(camPos - m_clipCentroid); const vector3d camDirNorm(camDir.Normalized()); diff --git a/src/GeoPatch.h b/src/GeoPatch.h index c9dc99b9c1..5b5277929c 100644 --- a/src/GeoPatch.h +++ b/src/GeoPatch.h @@ -121,7 +121,8 @@ class GeoPatch { private: static const int NUM_KIDS = 4; - bool IsOverHorizon(const vector3d &camPos); + bool IsPatchVisible(const Graphics::Frustum &frustum, const vector3d &camPos) const; + bool IsOverHorizon(const vector3d &camPos) const; RefCountedPtr m_ctx; struct Corners { From 8a3d0aee744df27c54629c6a0a35e856bb1dfeb3 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 19 Nov 2024 06:22:39 +0000 Subject: [PATCH 04/16] Experiment sub-centroid patch culling Define 5-9 smaller spheres to use in horizon culling. Idea being that you can get tighter culling without a single large sphere visible over the horizon giving false positive visibility --- src/GeoPatch.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++--- src/GeoPatch.h | 15 +++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index 5013999ea3..12e7ad39c6 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -9,7 +9,6 @@ #include "MathUtil.h" #include "Pi.h" #include "RefCounted.h" -#include "math/Sphere.h" #include "galaxy/SystemBody.h" #include "graphics/Frustum.h" #include "graphics/Graphics.h" @@ -43,7 +42,31 @@ namespace PatchMaths{ } // tri edge lengths -static const double GEOPATCH_SUBDIVIDE_AT_CAMDIST = 5.0; +static constexpr double GEOPATCH_SUBDIVIDE_AT_CAMDIST = 5.0; + +#if USE_SUB_CENTROID_CLIPPING +#if NUM_HORIZON_POINTS == 9 +static const vector2d sample[NUM_HORIZON_POINTS] = { + { 0.2, 0.2 }, { 0.5, 0.2 }, { 0.8, 0.2 }, + { 0.2, 0.5 }, { 0.5, 0.5 }, { 0.8, 0.8 }, + { 0.2, 0.8 }, { 0.5, 0.8 }, { 0.8, 0.8 } +}; +#elif NUM_HORIZON_POINTS == 5 +static const vector2d sample[NUM_HORIZON_POINTS] = { + // Naive ordering + // { 0.2, 0.2 }, { 0.8, 0.2 }, // top + //{ 0.5, 0.5 }, // middle + //{ 0.2, 0.8 }, { 0.8, 0.8 } // bottom + + // possibly more optimal elimination for early out + { 0.5, 0.5 }, // centre + { 0.2, 0.2 }, // top-left corner + { 0.8, 0.8 }, // opposite corner + { 0.8, 0.2 }, // other diagonal + { 0.2, 0.8 } // ^^^ +}; +#endif +#endif // #if USE_SUB_CENTROID_CLIPPING GeoPatch::GeoPatch(const RefCountedPtr &ctx_, GeoSphere *gs, const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_, @@ -70,7 +93,12 @@ GeoPatch::GeoPatch(const RefCountedPtr &ctx_, GeoSphere *gs, distMult = 5.0 / Clamp(m_depth, 1, 5); } m_splitLength = GEOPATCH_SUBDIVIDE_AT_CAMDIST / pow(2.0, m_depth) * distMult; - + +#if USE_SUB_CENTROID_CLIPPING + for (size_t i = 0; i < NUM_HORIZON_POINTS; i++) { + m_clipHorizon[i] = SSphere(PatchMaths::GetSpherePoint(v0_, v1_, v2_, v3_, sample[i].x, sample[i].y), m_clipRadius * CLIP_RADIUS_MULTIPLIER); + } +#endif // #if USE_SUB_CENTROID_CLIPPING } GeoPatch::~GeoPatch() @@ -287,6 +315,7 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons void GeoPatch::RenderImmediate(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView) const { + PROFILE_SCOPED() if (m_patchVBOData->m_heights) { const vector3d relpos = m_clipCentroid - campos; renderer->SetTransform(matrix4x4f(modelView * matrix4x4d::Translation(relpos))); @@ -336,6 +365,7 @@ void GeoPatch::GatherRenderablePatches(std::vector &visiblePatches, void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustum) { + PROFILE_SCOPED() // there should be no LOD update when we have active split requests if (HasJobRequest()) return; @@ -465,6 +495,7 @@ void GeoPatch::ReceiveJobHandle(Job::Handle job) bool GeoPatch::IsPatchVisible(const Graphics::Frustum &frustum, const vector3d &camPos) const { + PROFILE_SCOPED() // Test if this patch is visible if (!frustum.TestPoint(m_clipCentroid, m_clipRadius)) return false; // nothing below this patch is visible @@ -480,6 +511,7 @@ bool GeoPatch::IsPatchVisible(const Graphics::Frustum &frustum, const vector3d & bool GeoPatch::IsOverHorizon(const vector3d &camPos) const { + PROFILE_SCOPED() const vector3d camDir(camPos - m_clipCentroid); const vector3d camDirNorm(camDir.Normalized()); const vector3d cenDir(m_clipCentroid.Normalized()); @@ -488,7 +520,17 @@ bool GeoPatch::IsOverHorizon(const vector3d &camPos) const if (dotProd < 0.25 && (camDir.LengthSqr() > (m_clipRadius * m_clipRadius))) { // return the result of the Horizon Culling test, inverted to match naming semantic // eg: HC returns true==visible, but this method returns true==hidden +#if USE_SUB_CENTROID_CLIPPING + bool gather = false; + for (size_t i = 0; i < NUM_HORIZON_POINTS; i++) { + gather |= s_sph.HorizonCulling(camPos, m_clipHorizon[i]); + if (gather) + return false; // early out if any test is visible + } + return !gather; // if true then it's visible, return semantic is true == over the horizon and thus invisible so invert +#else return !s_sph.HorizonCulling(camPos, SSphere(m_clipCentroid, m_clipRadius)); +#endif // #if USE_SUB_CENTROID_CLIPPING } // not over the horizon diff --git a/src/GeoPatch.h b/src/GeoPatch.h index 5b5277929c..35f12fc9b3 100644 --- a/src/GeoPatch.h +++ b/src/GeoPatch.h @@ -13,6 +13,7 @@ #include "matrix4x4.h" #include "vector3.h" #include "graphics/Frustum.h" +#include "math/Sphere.h" #include #include @@ -37,6 +38,17 @@ class SQuadSplitResult; class SSingleSplitResult; struct SSplitResultData; +// Experiment: +// Use smaller spheres than the (m_clipCentroid, m_clipRadius) to test against the horizon. +// This can eliminate more patches due to not poking a giant clipping sphere over the horizon +// but on terrains with extreme feature heights there is over-culling of GeoPatches +// eg: Vesta in the Sol system has massive craters which are obviously culled as they go over the horizon. +#define USE_SUB_CENTROID_CLIPPING 1 +#if USE_SUB_CENTROID_CLIPPING +#define NUM_HORIZON_POINTS 5 // 9 // 9 points is more expensive +static constexpr double CLIP_RADIUS_MULTIPLIER = 0.1; +#endif // USE_SUB_CENTROID_CLIPPING + class GeoPatch { public: GeoPatch(const RefCountedPtr &_ctx, GeoSphere *gs, @@ -161,6 +173,9 @@ class GeoPatch { std::unique_ptr m_kids[NUM_KIDS]; vector3d m_clipCentroid; +#if USE_SUB_CENTROID_CLIPPING + SSphere m_clipHorizon[NUM_HORIZON_POINTS]; +#endif // #if USE_SUB_CENTROID_CLIPPING GeoSphere *m_geosphere; double m_splitLength; // rough length, is how near to the camera the m_clipCentroid should be before it must split double m_clipRadius; From d2333f0b853a94b12aa46737d808f3ef2fb72f3e Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 19 Nov 2024 06:24:26 +0000 Subject: [PATCH 05/16] Frustum method naming TestPoint->TestSphere --- src/Camera.cpp | 2 +- src/CityOnPlanet.cpp | 4 ++-- src/GasGiant.cpp | 2 +- src/GeoPatch.cpp | 2 +- src/graphics/Frustum.h | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Camera.cpp b/src/Camera.cpp index 60c6675215..e7806ab32b 100644 --- a/src/Camera.cpp +++ b/src/Camera.cpp @@ -177,7 +177,7 @@ void Camera::Update() // cull off-screen objects double rad = b->GetClipRadius(); - if (!m_context->GetFrustum().TestPointInfinite(attrs.viewCoords, rad)) + if (!m_context->GetFrustum().TestSphereInfinite(attrs.viewCoords, rad)) continue; attrs.camDist = attrs.viewCoords.Length(); diff --git a/src/CityOnPlanet.cpp b/src/CityOnPlanet.cpp index 85294fcb92..6dee41ac93 100644 --- a/src/CityOnPlanet.cpp +++ b/src/CityOnPlanet.cpp @@ -677,7 +677,7 @@ void CityOnPlanet::Render(Graphics::Renderer *r, const CameraContext *camera, co // Early frustum test of whole city. const vector3d stationPos = viewTransform * (station->GetPosition() + m_realCentre); - if (!camera->GetFrustum().TestPoint(stationPos, m_clipRadius)) + if (!camera->GetFrustum().TestSphere(stationPos, m_clipRadius)) return; // Use the station-centered frame-oriented coordinate system we generated building positions in @@ -736,7 +736,7 @@ void CityOnPlanet::Render(Graphics::Renderer *r, const CameraContext *camera, co for (size_t i = 0; i < m_enabledBuildings.size(); i++) { const auto &building = m_enabledBuildings[i]; - if (!frustum.TestPoint(building.pos, building.clipRadius)) + if (!frustum.TestSphere(building.pos, building.clipRadius)) continue; transform[building.instIndex].emplace_back(m_instanceTransforms[i]); diff --git a/src/GasGiant.cpp b/src/GasGiant.cpp index b1c654bde5..6bf6b1ac4a 100644 --- a/src/GasGiant.cpp +++ b/src/GasGiant.cpp @@ -258,7 +258,7 @@ class GasPatch { void Render(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView, const Graphics::Frustum &frustum) { - if (!frustum.TestPoint(clipCentroid, clipRadius)) + if (!frustum.TestSphere(clipCentroid, clipRadius)) return; const vector3d relpos = clipCentroid - campos; diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index 12e7ad39c6..36431d2d2b 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -497,7 +497,7 @@ bool GeoPatch::IsPatchVisible(const Graphics::Frustum &frustum, const vector3d & { PROFILE_SCOPED() // Test if this patch is visible - if (!frustum.TestPoint(m_clipCentroid, m_clipRadius)) + if (!frustum.TestSphere(m_clipCentroid, m_clipRadius)) return false; // nothing below this patch is visible // We only want to horizon cull patches that can actually be over the horizon! diff --git a/src/graphics/Frustum.h b/src/graphics/Frustum.h index efa7bff8b3..58022be577 100644 --- a/src/graphics/Frustum.h +++ b/src/graphics/Frustum.h @@ -31,8 +31,8 @@ namespace Graphics { InitFromMatrix(m); } - // test if point (sphere) is in the frustum - bool TestPoint(const vector3 &p, T radius) const + // test if sphere is in the frustum + bool TestSphere(const vector3 &p, T radius) const { for (int i = 0; i < 6; i++) if (m_planes[i].DistanceToPoint(p) + radius < 0) @@ -40,8 +40,8 @@ namespace Graphics { return true; } - // test if point (sphere) is in the frustum, ignoring the far plane - bool TestPointInfinite(const vector3 &p, T radius) const + // test if sphere is in the frustum, ignoring the far plane + bool TestSphereInfinite(const vector3 &p, T radius) const { // check all planes except far plane for (int i = 0; i < 5; i++) From bb13e4b50f3dd8dbd96143eb31f89fd44286101c Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 19 Nov 2024 18:07:58 +0000 Subject: [PATCH 06/16] WIP PiGui::PerfInfo::DrawTerrainDebug --- src/pigui/PerfInfo.cpp | 15 ++++++++++++++- src/pigui/PerfInfo.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/pigui/PerfInfo.cpp b/src/pigui/PerfInfo.cpp index de2fbcc51b..6cf427d317 100644 --- a/src/pigui/PerfInfo.cpp +++ b/src/pigui/PerfInfo.cpp @@ -4,6 +4,7 @@ #include "PerfInfo.h" #include "Frame.h" #include "Game.h" +#include "GameConfig.h" #include "Input.h" #include "LuaPiGui.h" #include "Pi.h" @@ -349,12 +350,16 @@ void PerfInfo::DrawPerfWindow() if (Pi::game && Pi::player->GetFlightState() != Ship::HYPERSPACE) { if (BeginDebugTab(s_worldIcon, "WorldView Stats")) { - DrawWorldViewStats(); EndDebugTab(); } } + if (ImGui::BeginTabItem("Terrain")) { + DrawTerrainDebug(); + ImGui::EndTabItem(); + } + PiGui::RunHandler(Pi::GetFrameTime(), "debug-tabs"); ImGui::EndTabBar(); @@ -622,6 +627,14 @@ void PerfInfo::DrawInputDebug() } } +void PiGui::PerfInfo::DrawTerrainDebug() +{ + bool sortGeoPatches = Pi::config->Int("SortGeoPatches") == 1; + if (ImGui::Checkbox("Distance Sort GeoPatches", &sortGeoPatches)) { + Pi::config->SetInt("SortGeoPatches", sortGeoPatches ? 1 : 0); + } +} + void PerfInfo::DrawImGuiStats() { ImGui::SeparatorText("ImGui Stats"); diff --git a/src/pigui/PerfInfo.h b/src/pigui/PerfInfo.h index c72c9334b2..9534e7abab 100644 --- a/src/pigui/PerfInfo.h +++ b/src/pigui/PerfInfo.h @@ -67,6 +67,7 @@ namespace PiGui { void DrawWorldViewStats(); void DrawImGuiStats(); void DrawInputDebug(); + void DrawTerrainDebug(); void DrawStatList(const Perf::Stats::FrameInfo &fi); void DrawCounter(CounterInfo &counter, const char *label, float min, float max, float height, bool drawStats = false); From 7bbc4f246a8688c05548024cb454d415dc6adc0b Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 19 Nov 2024 23:46:46 +0000 Subject: [PATCH 07/16] Slower zoom by holding either shift key in object viewer --- src/ObjectViewerView.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ObjectViewerView.cpp b/src/ObjectViewerView.cpp index bb7a018341..5212a18a24 100644 --- a/src/ObjectViewerView.cpp +++ b/src/ObjectViewerView.cpp @@ -123,8 +123,9 @@ void ObjectViewerView::ReloadState() void ObjectViewerView::Update() { - if (Pi::input->KeyState(SDLK_EQUALS)) viewingDist *= 0.99f; - if (Pi::input->KeyState(SDLK_MINUS)) viewingDist *= 1.01f; + const float zoomScaler = (Pi::input->KeyState(SDLK_LSHIFT) || Pi::input->KeyState(SDLK_RSHIFT)) ? 0.001f : 0.01f; + if (Pi::input->KeyState(SDLK_EQUALS)) viewingDist *= (1.0f - zoomScaler); + if (Pi::input->KeyState(SDLK_MINUS)) viewingDist *= (1.0f + zoomScaler); viewingDist = Clamp(viewingDist, 10.0f, 1e12f); Body *body = Pi::player->GetNavTarget(); From f49d44741927279586ba03fe9c80f9d5f9f7bcb1 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 19 Nov 2024 23:47:02 +0000 Subject: [PATCH 08/16] Fix bad formatting in GeoSphere --- src/GeoSphere.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/GeoSphere.cpp b/src/GeoSphere.cpp index 5d8f180646..fa3a46ec20 100644 --- a/src/GeoSphere.cpp +++ b/src/GeoSphere.cpp @@ -429,9 +429,7 @@ void GeoSphere::Render(Graphics::Renderer *renderer, const matrix4x4d &modelView ambient.a = 255; emission = StarSystem::starRealColors[GetSystemBody()->GetType()]; emission.a = 255; - } - - else { + } else { // give planet some ambient lighting if the viewer is close to it double camdist = 0.1 / campos.LengthSqr(); // why the fuck is this returning 0.1 when we are sat on the planet?? From 2db89c9202e9affba2ef4ee588de846264222841 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 19 Nov 2024 23:47:36 +0000 Subject: [PATCH 09/16] Label3DWrapper to wrap Label3D from Scenegraph for use elsewhere --- src/graphics/Drawables.cpp | 19 +++++++++++++++++++ src/graphics/Drawables.h | 15 +++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/graphics/Drawables.cpp b/src/graphics/Drawables.cpp index 1637bb21be..8f6afc4aab 100644 --- a/src/graphics/Drawables.cpp +++ b/src/graphics/Drawables.cpp @@ -13,6 +13,8 @@ #include "graphics/VertexBuffer.h" #include "profiler/Profiler.h" +#include + namespace Graphics { namespace Drawables { @@ -1028,6 +1030,23 @@ namespace Graphics { } } + //------------------------------------------------------------ + Label3DWrapper::Label3DWrapper(Graphics::Renderer *r, const std::string &str) + { + Graphics::Texture *sdfTex = Graphics::TextureBuilder("fonts/label3d.dds", Graphics::LINEAR_CLAMP, true, true, true).GetOrCreateTexture(r, "model"); + RefCountedPtr m_labelFont; + m_labelFont.Reset(new Text::DistanceFieldFont("fonts/sdf_definition.txt", sdfTex)); + m_label3D.reset(new SceneGraph::Label3D(r, m_labelFont)); + m_label3D->SetText(str); + } + + void Label3DWrapper::Draw(const matrix4x4f &trans) + { + if (m_label3D) { + m_label3D->Render(trans, nullptr); + } + } + } // namespace Drawables } // namespace Graphics diff --git a/src/graphics/Drawables.h b/src/graphics/Drawables.h index e0459d7c7f..f632eb4716 100644 --- a/src/graphics/Drawables.h +++ b/src/graphics/Drawables.h @@ -9,6 +9,7 @@ #include "graphics/VertexBuffer.h" #include +#include struct Aabb; @@ -122,8 +123,6 @@ namespace Graphics { void Draw(Renderer *, Material *); private: - void CreateVertexBuffer(Graphics::Renderer *r, const Uint32 size); - bool m_refreshVertexBuffer; RefCountedPtr m_pointMesh; std::unique_ptr m_va; @@ -290,6 +289,18 @@ namespace Graphics { static void DrawVertices(Graphics::VertexArray &va, const matrix4x4f &transform, const Aabb &aabb, Color color); }; + //------------------------------------------------------------ + + // Label3DWrapper + class Label3DWrapper { + public: + Label3DWrapper(Graphics::Renderer *r, const std::string &str); + void Draw(const matrix4x4f &trans); + + private: + std::unique_ptr m_label3D; + }; + } // namespace Drawables } // namespace Graphics From 946115ff060f042e315a3b55943b3b7d75bf52c3 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 19 Nov 2024 23:48:42 +0000 Subject: [PATCH 10/16] Use Label3DWrapper to show the root face ID Only used if DEBUG_PATCHES defined as 1 --- src/GeoPatch.cpp | 24 ++++++++++++++++++++++++ src/GeoPatch.h | 11 +++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index 36431d2d2b..5890218fd8 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -20,8 +20,10 @@ #include "profiler/Profiler.h" #include "vcacheopt/vcacheopt.h" #include "Core/Log.h" + #include #include +#include #define DEBUG_CENTROIDS 0 @@ -280,6 +282,7 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) m_patchVBOData->m_normals.reset(); m_patchVBOData->m_colors.reset(); +#if DEBUG_PATCHES #ifdef DEBUG_BOUNDING_SPHERES RefCountedPtr mat(Pi::renderer->CreateMaterial("unlit", Graphics::MaterialDescriptor(), Graphics::RenderStateDesc())); switch (m_depth) { @@ -291,6 +294,12 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) } m_boundsphere.reset(new Graphics::Drawables::Sphere3D(Pi::renderer, mat, 1, m_clipRadius)); #endif + if (m_label3D == nullptr) { + std::stringstream stuff; + stuff << m_PatchID.GetPatchFaceIdx(); + m_label3D.reset(new Graphics::Drawables::Label3DWrapper(Pi::renderer, stuff.str())); + } +#endif // DEBUG_PATCHES } } @@ -305,6 +314,13 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons if (!IsPatchVisible(frustum, campos)) return; +#if DEBUG_PATCHES + if (m_label3D != nullptr) { + const vector3d relpos = m_clipCentroid - campos; + m_label3D->Draw(matrix4x4f(modelView * matrix4x4d::Translation(relpos))); + } +#endif // DEBUG_PATCHES + if (m_kids[0]) { for (int i = 0; i < NUM_KIDS; i++) m_kids[i]->Render(renderer, campos, modelView, frustum); @@ -316,6 +332,14 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons void GeoPatch::RenderImmediate(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView) const { PROFILE_SCOPED() + +#if DEBUG_PATCHES + if (m_label3D != nullptr) { + const vector3d relpos = m_clipCentroid - campos; + m_label3D->Draw(matrix4x4f(modelView * matrix4x4d::Translation(relpos))); + } +#endif // DEBUG_PATCHES + if (m_patchVBOData->m_heights) { const vector3d relpos = m_clipCentroid - campos; renderer->SetTransform(matrix4x4f(modelView * matrix4x4d::Translation(relpos))); diff --git a/src/GeoPatch.h b/src/GeoPatch.h index 35f12fc9b3..8d3d2c7628 100644 --- a/src/GeoPatch.h +++ b/src/GeoPatch.h @@ -17,14 +17,18 @@ #include #include +#define DEBUG_PATCHES 0 //#define DEBUG_BOUNDING_SPHERES -#ifdef DEBUG_BOUNDING_SPHERES +#if DEBUG_PATCHES #include "graphics/Drawables.h" namespace Graphics { +#ifdef DEBUG_BOUNDING_SPHERES class RenderState; -} #endif + class Label3DWrapper; +} //namespace Graphics +#endif // DEBUG_PATCHES namespace Graphics { class Renderer; @@ -185,9 +189,12 @@ class GeoPatch { uint8_t m_patchUpdateState; bool m_needUpdateVBOs; bool m_hasJobRequest; +#if DEBUG_PATCHES #ifdef DEBUG_BOUNDING_SPHERES std::unique_ptr m_boundsphere; #endif + std::unique_ptr m_label3D; +#endif // #if DEBUG_PATCHES }; #endif /* _GEOPATCH_H */ From 81452a99b36b3fe0dc86ce84a26865d5cff9f371 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Wed, 20 Nov 2024 00:06:47 +0000 Subject: [PATCH 11/16] Change Label3DWrapper to Label3D - Reimplement the Scenegraph::Label3D for Drawables - Might convert Scenegraph::Label3D to wrap this! --- src/GeoPatch.cpp | 24 ++++++++++------- src/GeoPatch.h | 4 ++- src/graphics/Drawables.cpp | 53 ++++++++++++++++++++++++++++++++------ src/graphics/Drawables.h | 16 ++++++++---- 4 files changed, 73 insertions(+), 24 deletions(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index 5890218fd8..eb54dfc834 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -27,11 +27,11 @@ #define DEBUG_CENTROIDS 0 +#if DEBUG_PATCHES #ifdef DEBUG_BOUNDING_SPHERES #include "graphics/RenderState.h" #endif -#if DEBUG_CENTROIDS #include "graphics/Drawables.h" #endif @@ -297,7 +297,7 @@ void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) if (m_label3D == nullptr) { std::stringstream stuff; stuff << m_PatchID.GetPatchFaceIdx(); - m_label3D.reset(new Graphics::Drawables::Label3DWrapper(Pi::renderer, stuff.str())); + m_label3D.reset(new Graphics::Drawables::Label3D(Pi::renderer, stuff.str())); } #endif // DEBUG_PATCHES } @@ -315,10 +315,7 @@ void GeoPatch::Render(Graphics::Renderer *renderer, const vector3d &campos, cons return; #if DEBUG_PATCHES - if (m_label3D != nullptr) { - const vector3d relpos = m_clipCentroid - campos; - m_label3D->Draw(matrix4x4f(modelView * matrix4x4d::Translation(relpos))); - } + RenderLabelDebug(campos, modelView); #endif // DEBUG_PATCHES if (m_kids[0]) { @@ -334,10 +331,7 @@ void GeoPatch::RenderImmediate(Graphics::Renderer *renderer, const vector3d &cam PROFILE_SCOPED() #if DEBUG_PATCHES - if (m_label3D != nullptr) { - const vector3d relpos = m_clipCentroid - campos; - m_label3D->Draw(matrix4x4f(modelView * matrix4x4d::Translation(relpos))); - } + RenderLabelDebug(campos, modelView); #endif // DEBUG_PATCHES if (m_patchVBOData->m_heights) { @@ -560,3 +554,13 @@ bool GeoPatch::IsOverHorizon(const vector3d &camPos) const // not over the horizon return false; } + +#if DEBUG_PATCHES +void GeoPatch::RenderLabelDebug(const vector3d &campos, const matrix4x4d &modelView) const +{ + if (m_label3D != nullptr) { + const vector3d relpos = m_clipCentroid - campos; + m_label3D->Draw(Pi::renderer, matrix4x4f(modelView * matrix4x4d::Translation(relpos))); + } +} +#endif // DEBUG_PATCHES diff --git a/src/GeoPatch.h b/src/GeoPatch.h index 8d3d2c7628..c72a2da9a1 100644 --- a/src/GeoPatch.h +++ b/src/GeoPatch.h @@ -193,7 +193,9 @@ class GeoPatch { #ifdef DEBUG_BOUNDING_SPHERES std::unique_ptr m_boundsphere; #endif - std::unique_ptr m_label3D; + std::unique_ptr m_label3D; + + void RenderLabelDebug(const vector3d &campos, const matrix4x4d &modelView) const; #endif // #if DEBUG_PATCHES }; diff --git a/src/graphics/Drawables.cpp b/src/graphics/Drawables.cpp index 8f6afc4aab..98332a8be5 100644 --- a/src/graphics/Drawables.cpp +++ b/src/graphics/Drawables.cpp @@ -1031,19 +1031,56 @@ namespace Graphics { } //------------------------------------------------------------ - Label3DWrapper::Label3DWrapper(Graphics::Renderer *r, const std::string &str) + Label3D::Label3D(Graphics::Renderer *r, const std::string &str) { Graphics::Texture *sdfTex = Graphics::TextureBuilder("fonts/label3d.dds", Graphics::LINEAR_CLAMP, true, true, true).GetOrCreateTexture(r, "model"); - RefCountedPtr m_labelFont; - m_labelFont.Reset(new Text::DistanceFieldFont("fonts/sdf_definition.txt", sdfTex)); - m_label3D.reset(new SceneGraph::Label3D(r, m_labelFont)); - m_label3D->SetText(str); + m_font.Reset(new Text::DistanceFieldFont("fonts/sdf_definition.txt", sdfTex)); + + Graphics::MaterialDescriptor matdesc; + matdesc.textures = 1; + matdesc.alphaTest = true; + matdesc.lighting = true; + + Graphics::RenderStateDesc rsd; + rsd.depthWrite = false; + rsd.blendMode = Graphics::BLEND_ALPHA; + + m_geometry.reset(m_font->CreateVertexArray()); + m_material.Reset(r->CreateMaterial("label", matdesc, rsd)); + m_material->SetTexture("texture0"_hash, m_font->GetTexture()); + m_material->diffuse = Color::WHITE; + m_material->emissive = Color(38, 38, 38); + m_material->specular = Color::WHITE; + + SetText(r, str); + } + + void Label3D::SetText(Graphics::Renderer *r, const std::string &text) + { + //regenerate geometry + m_geometry->Clear(); + m_textMesh.reset(); + + if (!text.empty()) { + m_font->GetGeometry(*m_geometry, text, vector2f(0.f)); + + // Happens if none of the characters in the string have glyphs in the SDF font. + // Most noticeably, this means text consisting of entirely Cyrillic + // or Chinese characters will vanish when rendered on a Label3D. + if (m_geometry->IsEmpty()) { + return; + } + + //create buffer and upload data + m_textMesh.reset(r->CreateMeshObjectFromArray(m_geometry.get())); + } } - void Label3DWrapper::Draw(const matrix4x4f &trans) + void Label3D::Draw(Graphics::Renderer *r, const matrix4x4f &trans) { - if (m_label3D) { - m_label3D->Render(trans, nullptr); + if (r!=nullptr && m_textMesh.get()) { + r->SetTransform(trans); + r->DrawMesh(m_textMesh.get(), m_material.Get()); } } diff --git a/src/graphics/Drawables.h b/src/graphics/Drawables.h index f632eb4716..3e95dc4485 100644 --- a/src/graphics/Drawables.h +++ b/src/graphics/Drawables.h @@ -7,6 +7,7 @@ #include "graphics/Material.h" #include "graphics/VertexArray.h" #include "graphics/VertexBuffer.h" +#include "text/DistanceFieldFont.h" #include #include @@ -291,14 +292,19 @@ namespace Graphics { //------------------------------------------------------------ - // Label3DWrapper - class Label3DWrapper { + // Label3D + class Label3D { public: - Label3DWrapper(Graphics::Renderer *r, const std::string &str); - void Draw(const matrix4x4f &trans); + Label3D(Graphics::Renderer *r, const std::string &str); + void Draw(Graphics::Renderer *r, const matrix4x4f &trans); private: - std::unique_ptr m_label3D; + void SetText(Graphics::Renderer *r, const std::string &text); + + RefCountedPtr m_material; + std::unique_ptr m_geometry; + std::unique_ptr m_textMesh; + RefCountedPtr m_font; }; } // namespace Drawables From b4befb110e441e453beec69e512c7ac9664b6cf8 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Wed, 20 Nov 2024 16:34:51 +0000 Subject: [PATCH 12/16] Functioning Terrain tab in perfinfo with debug toggles --- src/GeoSphere.cpp | 35 ++++++++++++++++++++++++++++++----- src/GeoSphere.h | 10 ++++++++++ src/pigui/PerfInfo.cpp | 26 +++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/GeoSphere.cpp b/src/GeoSphere.cpp index fa3a46ec20..cccba4a357 100644 --- a/src/GeoSphere.cpp +++ b/src/GeoSphere.cpp @@ -39,6 +39,7 @@ static const int detail_edgeLen[5] = { static const double gs_targetPatchTriLength(100.0); static std::vector s_allGeospheres; +static Uint32 s_debugFlags = GeoSphere::DebugFlags::DEBUG_NONE; void GeoSphere::InitGeoSphere() { @@ -127,6 +128,18 @@ bool GeoSphere::OnAddSingleSplitResult(const SystemPath &path, SSingleSplitResul return false; } +//static +void GeoSphere::SetDebugFlags(Uint32 flags) +{ + s_debugFlags = flags; +} + +//static +Uint32 GeoSphere::GetDebugFlags() +{ + return s_debugFlags; +} + void GeoSphere::Reset() { { @@ -195,6 +208,12 @@ GeoSphere::GeoSphere(const SystemBody *body) : m_visiblePatches.reserve(1024); + if (Pi::config->Int("SortGeoPatches") == 0) { + SetDebugFlags(GetDebugFlags() & ~DebugFlags::DEBUG_SORTGEOPATCHES); + } else { + SetDebugFlags(GetDebugFlags() | DebugFlags::DEBUG_SORTGEOPATCHES); + } + //SetUpMaterials is not called until first Render since light count is zero :) } @@ -443,11 +462,10 @@ void GeoSphere::Render(Graphics::Renderer *renderer, const matrix4x4d &modelView renderer->SetTransform(matrix4x4f(modelView)); - if (Pi::config->Int("SortGeoPatches") == 0) { - for (int i = 0; i < NUM_PATCHES; i++) { - m_patches[i]->Render(renderer, campos, modelView, frustum); - } - } else { + if (s_debugFlags & GeoSphere::DebugFlags::DEBUG_WIREFRAME) + renderer->SetWireFrameMode(true); + + if (s_debugFlags & GeoSphere::DebugFlags::DEBUG_SORTGEOPATCHES) { // Gather the patches that could be rendered for (int i = 0; i < NUM_PATCHES; i++) { m_patches[i]->GatherRenderablePatches(m_visiblePatches, renderer, campos, frustum); @@ -468,8 +486,15 @@ void GeoSphere::Render(Graphics::Renderer *renderer, const matrix4x4d &modelView // must clear this after each render otherwise it just accumulates every patch ever drawn! m_visiblePatches.clear(); + } else { + for (int i = 0; i < NUM_PATCHES; i++) { + m_patches[i]->Render(renderer, campos, modelView, frustum); + } } + if (s_debugFlags & GeoSphere::DebugFlags::DEBUG_WIREFRAME) + renderer->SetWireFrameMode(false); + renderer->SetAmbientColor(oldAmbient); renderer->GetStats().AddToStatCount(Graphics::Stats::STAT_PLANETS, 1); diff --git a/src/GeoSphere.h b/src/GeoSphere.h index 0f3ad03a8c..b74b459a70 100644 --- a/src/GeoSphere.h +++ b/src/GeoSphere.h @@ -57,6 +57,16 @@ class GeoSphere : public BaseSphere { static void OnChangeGeoSphereDetailLevel(); static bool OnAddQuadSplitResult(const SystemPath &path, SQuadSplitResult *res); static bool OnAddSingleSplitResult(const SystemPath &path, SSingleSplitResult *res); + + enum DebugFlags : uint32_t { // + DEBUG_NONE = 0x0, + DEBUG_SORTGEOPATCHES = 0x1, + DEBUG_WIREFRAME = 0x2, + DEBUG_FACELABELS = 0x4 + }; + static void SetDebugFlags(Uint32 flags); + static Uint32 GetDebugFlags(); + // in sbody radii virtual double GetMaxFeatureHeight() const override final { return m_terrain->GetMaxHeight(); } diff --git a/src/pigui/PerfInfo.cpp b/src/pigui/PerfInfo.cpp index 6cf427d317..fe9799fbdb 100644 --- a/src/pigui/PerfInfo.cpp +++ b/src/pigui/PerfInfo.cpp @@ -5,6 +5,7 @@ #include "Frame.h" #include "Game.h" #include "GameConfig.h" +#include "GeoSphere.h" #include "Input.h" #include "LuaPiGui.h" #include "Pi.h" @@ -629,10 +630,29 @@ void PerfInfo::DrawInputDebug() void PiGui::PerfInfo::DrawTerrainDebug() { - bool sortGeoPatches = Pi::config->Int("SortGeoPatches") == 1; - if (ImGui::Checkbox("Distance Sort GeoPatches", &sortGeoPatches)) { - Pi::config->SetInt("SortGeoPatches", sortGeoPatches ? 1 : 0); + ImGui::SeparatorText("Terrain Debug"); + + using Flags = GeoSphere::DebugFlags; + uint32_t debugFlags = GeoSphere::GetDebugFlags(); + bool showSort = debugFlags & Flags::DEBUG_SORTGEOPATCHES; + bool showWire = debugFlags & Flags::DEBUG_WIREFRAME; + //bool showFace = debugFlags & Flags::DEBUG_FACELABELS; + + bool changed = ImGui::Checkbox("Distance Sort GeoPatches", &showSort); + changed |= ImGui::Checkbox("Show Wireframe", &showWire); + //changed |= ImGui::Checkbox("Show Face IDs", &showFace); + + /* clang-format off */ + if (changed) { + debugFlags = (showSort ? Flags::DEBUG_SORTGEOPATCHES : 0) + | (showWire ? Flags::DEBUG_WIREFRAME : 0); + //| (showFace ? Flags::DEBUG_FACELABELS : 0); + GeoSphere::SetDebugFlags(debugFlags); + Pi::config->SetInt("SortGeoPatches", showSort ? 1 : 0); + + Pi::config->Save(); } + /* clang-format on */ } void PerfInfo::DrawImGuiStats() From 60a00593df3e6c24efeadea243484b252b8801f5 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Wed, 20 Nov 2024 16:57:54 +0000 Subject: [PATCH 13/16] Don't include "Core/Log.h" I don't even know why this got added --- src/GeoPatch.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index eb54dfc834..e7a27ed274 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -19,7 +19,6 @@ #include "perlin.h" #include "profiler/Profiler.h" #include "vcacheopt/vcacheopt.h" -#include "Core/Log.h" #include #include From f12d1da3338f947706de7686ff77d896941c9896 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Fri, 22 Nov 2024 11:57:56 +0000 Subject: [PATCH 14/16] Fixed perf icon & tab rendering for MSVC - used wireframe planet icon for Terrain tab - MSVC requires use of u8 before icon \u string names - added comment explaining icon name values --- src/pigui/PerfInfo.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/pigui/PerfInfo.cpp b/src/pigui/PerfInfo.cpp index fe9799fbdb..3b53edd146 100644 --- a/src/pigui/PerfInfo.cpp +++ b/src/pigui/PerfInfo.cpp @@ -256,9 +256,15 @@ void PerfInfo::Draw() ImGui::ShowStackToolWindow(&m_state->stackToolOpen); } -static const char *s_rendererIcon = "\uF082"; -static const char *s_worldIcon = "\uF092"; -static const char *s_perfIcon = "\uF0F0"; +// Icons are found in the file /data/icons/icons.svg +// The value below can be calculated like this: \uF0A4 +// \uF0 ignore this part +// A == 10 zero index based so the 11th row of icons.svg +// 4 == 4 zero index based so the 5th column of icons.svg +static const char *s_rendererIcon = u8"\uF082"; +static const char *s_worldIcon = u8"\uF092"; +static const char *s_perfIcon = u8"\uF0F0"; +static const char *s_planetIcon = u8"\uF0A0"; bool BeginDebugTab(const char *icon, const char *label) { @@ -344,6 +350,11 @@ void PerfInfo::DrawPerfWindow() EndDebugTab(); } + if (BeginDebugTab(s_planetIcon, "Terrain")) { + DrawTerrainDebug(); + EndDebugTab(); + } + if (false && ImGui::BeginTabItem("Input")) { DrawInputDebug(); ImGui::EndTabItem(); @@ -356,11 +367,6 @@ void PerfInfo::DrawPerfWindow() } } - if (ImGui::BeginTabItem("Terrain")) { - DrawTerrainDebug(); - ImGui::EndTabItem(); - } - PiGui::RunHandler(Pi::GetFrameTime(), "debug-tabs"); ImGui::EndTabBar(); From 90c5bfc207c7fc4193858c1717a25b595f6a7da0 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Fri, 22 Nov 2024 12:34:22 +0000 Subject: [PATCH 15/16] Calculate distance in GatherRenderablePatches - remove defunct Centroid accessor --- src/GeoPatch.cpp | 4 ++-- src/GeoPatch.h | 4 +--- src/GeoSphere.cpp | 8 ++++---- src/GeoSphere.h | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index e7a27ed274..d5a71781f6 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -362,7 +362,7 @@ void GeoPatch::RenderImmediate(Graphics::Renderer *renderer, const vector3d &cam } } -void GeoPatch::GatherRenderablePatches(std::vector &visiblePatches, Graphics::Renderer *renderer, const vector3d &campos, const Graphics::Frustum &frustum) +void GeoPatch::GatherRenderablePatches(std::vector> &visiblePatches, Graphics::Renderer *renderer, const vector3d &campos, const Graphics::Frustum &frustum) { PROFILE_SCOPED() // must update the VBOs to calculate the clipRadius... @@ -375,7 +375,7 @@ void GeoPatch::GatherRenderablePatches(std::vector &visiblePatches, for (int i = 0; i < NUM_KIDS; i++) m_kids[i]->GatherRenderablePatches(visiblePatches, renderer, campos, frustum); } else if (m_patchVBOData->m_heights) { - visiblePatches.emplace_back(this); + visiblePatches.emplace_back((m_clipCentroid - campos).LengthSqr(), this); } } diff --git a/src/GeoPatch.h b/src/GeoPatch.h index c72a2da9a1..e9a1f635df 100644 --- a/src/GeoPatch.h +++ b/src/GeoPatch.h @@ -74,7 +74,7 @@ class GeoPatch { void Render(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView, const Graphics::Frustum &frustum); void RenderImmediate(Graphics::Renderer *renderer, const vector3d &campos, const matrix4x4d &modelView) const; - void GatherRenderablePatches(std::vector &visiblePatches, Graphics::Renderer *renderer, const vector3d &campos, const Graphics::Frustum &frustum); + void GatherRenderablePatches(std::vector> &visiblePatches, Graphics::Renderer *renderer, const vector3d &campos, const Graphics::Frustum &frustum); inline bool canBeMerged() const { @@ -98,8 +98,6 @@ class GeoPatch { inline bool HasHeightData() const { return (m_patchVBOData != nullptr) && (m_patchVBOData->m_heights.get() != nullptr); } - inline const vector3d &Centroid() const { return m_clipCentroid; } - // used by GeoSphere so must be public inline void SetNeedToUpdateVBOs() { diff --git a/src/GeoSphere.cpp b/src/GeoSphere.cpp index cccba4a357..1c2dce6ddb 100644 --- a/src/GeoSphere.cpp +++ b/src/GeoSphere.cpp @@ -472,16 +472,16 @@ void GeoSphere::Render(Graphics::Renderer *renderer, const matrix4x4d &modelView } // distance sort the patches - std::sort(m_visiblePatches.begin(), m_visiblePatches.end(), [&, campos](const GeoPatch *a, const GeoPatch *b) { - return (a->Centroid() - campos).LengthSqr() < (b->Centroid() - campos).LengthSqr(); + std::sort(m_visiblePatches.begin(), m_visiblePatches.end(), [&, campos](const std::pair &a, const std::pair &b) { + return (a.first) < (b.first); }); // cull occluded patches somehow? // create frustum from corner points, something vertical, and the campos??? Cull anything within that frustum? // render the sorted patches - for (GeoPatch *pPatch : m_visiblePatches) { - pPatch->RenderImmediate(renderer, campos, modelView); + for (std::pair &pPatch : m_visiblePatches) { + pPatch.second->RenderImmediate(renderer, campos, modelView); } // must clear this after each render otherwise it just accumulates every patch ever drawn! diff --git a/src/GeoSphere.h b/src/GeoSphere.h index b74b459a70..409163d457 100644 --- a/src/GeoSphere.h +++ b/src/GeoSphere.h @@ -90,7 +90,7 @@ class GeoSphere : public BaseSphere { void ProcessQuadSplitRequests(); std::unique_ptr m_patches[6]; - std::vector m_visiblePatches; + std::vector> m_visiblePatches; struct TDistanceRequest { TDistanceRequest(double dist, SQuadSplitRequest *pRequest, GeoPatch *pRequester) : From f0c8437bfab32631fb45831480d0e70022507257 Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Fri, 22 Nov 2024 12:39:54 +0000 Subject: [PATCH 16/16] Remove VBO and JobRequest get/setters --- src/GeoPatch.cpp | 24 ++++++++++++------------ src/GeoPatch.h | 29 +---------------------------- 2 files changed, 13 insertions(+), 40 deletions(-) diff --git a/src/GeoPatch.cpp b/src/GeoPatch.cpp index d5a71781f6..a59092a7d7 100644 --- a/src/GeoPatch.cpp +++ b/src/GeoPatch.cpp @@ -104,7 +104,7 @@ GeoPatch::GeoPatch(const RefCountedPtr &ctx_, GeoSphere *gs, GeoPatch::~GeoPatch() { - ClearHasJobRequest(); + m_hasJobRequest = false; for (int i = 0; i < NUM_KIDS; i++) { m_kids[i].reset(); } @@ -115,9 +115,9 @@ GeoPatch::~GeoPatch() void GeoPatch::UpdateVBOs(Graphics::Renderer *renderer) { PROFILE_SCOPED() - if (NeedToUpdateVBOs()) { + if (m_needUpdateVBOs) { assert(renderer); - ClearNeedToUpdateVBOs(); + m_needUpdateVBOs = false; //create buffer and upload data auto vbd = Graphics::VertexBufferDesc::FromAttribSet(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_NORMAL | Graphics::ATTRIB_DIFFUSE | Graphics::ATTRIB_UV0); @@ -384,7 +384,7 @@ void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustu { PROFILE_SCOPED() // there should be no LOD update when we have active split requests - if (HasJobRequest()) + if (m_hasJobRequest) return; bool canSplit = true; @@ -407,8 +407,8 @@ void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustu return; // we can see this patch so submit the jobs! - assert(!HasJobRequest()); - SetHasJobRequest(); + assert(!m_hasJobRequest); + m_hasJobRequest = true; SQuadSplitRequest *ssrd = new SQuadSplitRequest(m_corners->m_v0, m_corners->m_v1, m_corners->m_v2, m_corners->m_v3, m_clipCentroid.Normalized(), m_depth, m_geosphere->GetSystemBody()->GetPath(), m_PatchID, m_ctx->GetEdgeLen() - 2, @@ -436,8 +436,8 @@ void GeoPatch::LODUpdate(const vector3d &campos, const Graphics::Frustum &frustu void GeoPatch::RequestSinglePatch() { if (!HasHeightData()) { - assert(!HasJobRequest()); - SetHasJobRequest(); + assert(!m_hasJobRequest); + m_hasJobRequest = true; SSingleSplitRequest *ssrd = new SSingleSplitRequest(m_corners->m_v0, m_corners->m_v1, m_corners->m_v2, m_corners->m_v3, m_clipCentroid.Normalized(), m_depth, m_geosphere->GetSystemBody()->GetPath(), m_PatchID, m_ctx->GetEdgeLen() - 2, m_ctx->GetFrac(), m_geosphere->GetTerrain()); m_job = Pi::GetAsyncJobQueue()->Queue(new SinglePatchJob(ssrd)); @@ -457,7 +457,7 @@ void GeoPatch::ReceiveHeightmaps(SQuadSplitResult *psr) psr->OnCancel(); } } else { - assert(HasJobRequest()); + assert(m_hasJobRequest); const int newDepth = m_depth + 1; for (int i = 0; i < NUM_KIDS; i++) { assert(!m_kids[i]); @@ -472,7 +472,7 @@ void GeoPatch::ReceiveHeightmaps(SQuadSplitResult *psr) for (int i = 0; i < NUM_KIDS; i++) { m_kids[i]->ReceiveHeightResult(psr->data(i)); } - ClearHasJobRequest(); + m_hasJobRequest = false; } } @@ -480,10 +480,10 @@ void GeoPatch::ReceiveHeightmap(const SSingleSplitResult *psr) { PROFILE_SCOPED() assert(nullptr != psr); - assert(HasJobRequest()); + assert(m_hasJobRequest); ReceiveHeightResult(psr->data()); - ClearHasJobRequest(); + m_hasJobRequest = false; } void GeoPatch::ReceiveHeightResult(const SSplitResultData &data) diff --git a/src/GeoPatch.h b/src/GeoPatch.h index e9a1f635df..f3eb1df8b4 100644 --- a/src/GeoPatch.h +++ b/src/GeoPatch.h @@ -84,7 +84,7 @@ class GeoPatch { merge &= m_kids[i]->canBeMerged(); } } - merge &= !(HasJobRequest()); + merge &= !(m_hasJobRequest); return merge; } @@ -104,33 +104,6 @@ class GeoPatch { m_needUpdateVBOs = HasHeightData(); } -private: - - inline bool NeedToUpdateVBOs() const - { - return m_needUpdateVBOs; - } - - inline void ClearNeedToUpdateVBOs() - { - m_needUpdateVBOs = false; - } - - inline void SetHasJobRequest() - { - m_hasJobRequest = true; - } - - inline bool HasJobRequest() const - { - return m_hasJobRequest; - } - - inline void ClearHasJobRequest() - { - m_hasJobRequest = false; - } - private: static const int NUM_KIDS = 4;