Skip to content

Commit

Permalink
Rework GeoPatch to reduce size
Browse files Browse the repository at this point in the history
  • Loading branch information
fluffyfreak authored and impaktor committed Dec 17, 2024
1 parent 397e264 commit 6ff8d36
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 91 deletions.
133 changes: 68 additions & 65 deletions src/GeoPatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "perlin.h"
#include "profiler/Profiler.h"
#include "vcacheopt/vcacheopt.h"
#include "Core/Log.h"
#include <algorithm>
#include <deque>

Expand All @@ -33,61 +34,61 @@
#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;

GeoPatch::GeoPatch(const RefCountedPtr<GeoPatchContext> &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);
Expand All @@ -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++) {
Expand All @@ -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)];
Expand Down Expand Up @@ -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)];
Expand All @@ -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)];
Expand All @@ -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)];
Expand All @@ -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)];
Expand Down Expand Up @@ -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<Graphics::Material> mat(Pi::renderer->CreateMaterial("unlit", Graphics::MaterialDescriptor(), Graphics::RenderStateDesc()));
Expand Down Expand Up @@ -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)));

Expand All @@ -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

Expand All @@ -310,17 +317,17 @@ 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;
bool canMerge = bool(m_kids[0]);

// 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
}
Expand All @@ -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());

Expand All @@ -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));
}
Expand All @@ -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]);
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 6ff8d36

Please sign in to comment.