Skip to content

Rewrite LODSphereMesh as adaptive chunked-LOD single-draw renderer#2618

Draft
levinli303 wants to merge 9 commits into
CelestiaProject:masterfrom
celestiamobile:lodsphere-chunked-lod
Draft

Rewrite LODSphereMesh as adaptive chunked-LOD single-draw renderer#2618
levinli303 wants to merge 9 commits into
CelestiaProject:masterfrom
celestiamobile:lodsphere-chunked-lod

Conversation

@levinli303

Copy link
Copy Markdown
Collaborator

wip

@levinli303
levinli303 force-pushed the lodsphere-chunked-lod branch from 918609e to d815b4d Compare June 30, 2026 23:19
Port the cube-sphere adaptive LOD and single-draw-call batching into the
lat/long LODSphereMesh, adapting the cube blueprint to a single
equirectangular (longitude, latitude) parameterization:

- Quadtree roots at 2 longitude cells x 1 latitude cell; screen-space
  sagitta error drives splitting, with 2:1 balance and one-level stitch
  templates sealing LOD seams in a single batched draw per texID group.
- Longitude edges wrap across the meridian seam; pole edges have no
  neighbour and are left unstitched (each pole is a single point, the
  collapsed triangles are degenerate and harmless).
- resolveTile reproduces the legacy renderSection texcoord transform, so
  leaves map directly onto VirtualTexture tiles without a new texture type.
- render() takes eyePos, pixelSize, enableHorizonCull and geometryScale;
  cull bounds and the LOD metric are scaled by geometryScale so the cloud
  and atmosphere shells (scaled in the modelview) are not over-culled.
- Optional macro-gated wireframe mode mirroring the cube-sphere renderer.

Plumb pixelSize through RenderInfo and update the surface, cloud and
atmosphere call sites to the new signature.
@levinli303
levinli303 force-pushed the lodsphere-chunked-lod branch from d815b4d to 78a26c9 Compare June 30, 2026 23:45
Divide the screen-space split error by the grazing cosine so limb chunks,
compressed tangentially into thin screen slivers, refine while head-on chunks
stay coarse. This targets the hard lit/sky edge aliasing without globally
lowering the pixel-error threshold and over-tessellating the front face.

Also fold the chunkRadius/edge reductions into single std::max initializer
lists and hoist the edge sqrt out of the per-corner norms.
@levinli303
levinli303 force-pushed the lodsphere-chunked-lod branch from 6ae3203 to 2954633 Compare July 1, 2026 04:52
- render(): extract cullLeaves/buildBatch/uploadAndBindBatch/drawBatch to
  cut cognitive complexity from 60 to within limit.
- ensureStitchTemplates(): extract buildStitchTriangles (and the wireframe
  buildStitchLines) file-local helpers to cut complexity from 33.
- bufferOffset(): annotate the GL-mandated void* return with NOSONAR.
- LODSPHERE_WIREFRAME: keep the debug macro but mark it NOSONAR.
@levinli303
levinli303 force-pushed the lodsphere-chunked-lod branch 2 times, most recently from 104ddfb to 6a42249 Compare July 1, 2026 05:18
MSVC rejects using the constexpr locals N/stride inside the vid/snap lambdas
without an explicit capture (C3493), while capturing them trips Clang's
unused-capture warning. Reference the namespace-scope CHUNK_RES directly so no
capture is needed on either compiler.
@levinli303
levinli303 force-pushed the lodsphere-chunked-lod branch from 6a42249 to 106db31 Compare July 1, 2026 05:19
The batch buffers are rebuilt every frame from every visible chunk. appendChunk
grew them with per-element push_back and no reserved capacity, so it repeatedly
reallocated mid-frame and paid a capacity check per float (a profile hot spot).
Reserve the exact vertex total (constant per chunk) plus an index upper bound
once in buildBatch, then write through raw pointers in appendChunk.

Also raise CHUNK_PIXEL_ERROR 0.5 -> 1.0: the grazing-angle term now sharpens
the silhouette on its own, so the front-face budget can loosen without the limb
degrading (effective limb threshold is CHUNK_GRAZING_FLOOR * this).
Replace the per-leaf root-descent balancing and edge-mask stitching
(coveringDepth/neighborOf/computeEdgeMask/gatherLeaves) with a single
top-down walk that carries each node's four same-level neighbour indices.
Child neighbours are derived O(1) from the parent context, and imbalanced
leaves are collected to a fixpoint split list rather than re-descending
from the roots each query.

QuadNode gains an int depth; LeafRef/balanceScratch are removed in favour
of a reused splitList. Cuts balanceLeaves inclusive cost from ~4.1% to
~2.4% of frame time on the close-surface benchmark.
Add a flat, uniform-LOD path (setTerrainEnabled=false by default) that skips
the adaptive quadtree, 2:1 balance and stitch when no terrain displacement is
used, matching/beating the pre-chunked renderer on close-surface views while
keeping the adaptive path available for future terrain.

Also resolve SonarCloud issues on the PR: C-style arrays -> std::array,
if init-statements, const member functions, an error-prone loop, a structured
binding, group batch/draw members into a Batch struct (<=20 fields), and bundle
renderCloudsUnlit cloud params into a struct (<=7 params).
Drop the single-use wantSplit init-statement variable (flagged as assigned but
never read); test the split predicate directly in the if.
@sonarqubecloud

sonarqubecloud Bot commented Jul 3, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant