From 6ff8d36c81f9d9e9f7b3c56fd4fb6f2387b270af Mon Sep 17 00:00:00 2001 From: Andrew Copland Date: Tue, 3 Sep 2024 23:09:49 +0100 Subject: [PATCH] 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;