Code/PerformantTerrainScatterer/PerformantTerrainScattererManager.Rendering.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Sandbox;
public sealed partial class PerformantTerrainScatterer
{
private void RebuildRenderCache()
{
var newCache = new ChunkRenderData[_activeChunks.Count];
int index = 0;
foreach ( var chunk in _activeChunks.Values )
{
var batches = new ModelRenderBatch[chunk.TransformsByModel.Count];
int bIndex = 0;
foreach ( var kvp in chunk.TransformsByModel )
{
int mIdx = kvp.Key;
batches[bIndex++] = new ModelRenderBatch
{
Model = ModelEntries[mIdx].Model,
Transforms = kvp.Value,
RenderDistSq = _modelRenderDistSq[mIdx],
LodDistancesSq = _modelLodDistancesSq[mIdx],
MaxLod = Math.Max( 0, _modelLodCounts[mIdx] - 1 )
};
}
newCache[index++] = new ChunkRenderData
{
Center = chunk.Center,
Batches = batches
};
}
_renderCache = newCache;
}
private void RenderClutter( SceneObject obj )
{
var cache = _renderCache;
if ( cache.Length == 0 ) return;
var camera = Scene.Camera;
if ( !camera.IsValid() ) return;
Vector3 cameraPos = camera.WorldPosition;
Vector3 cameraForward = camera.WorldRotation.Forward;
float frustumMinDistSq = FrustumCullMinDistance * FrustumCullMinDistance;
float maxRenderDistSq = _maxRenderDistance * _maxRenderDistance;
bool useFrustum = UseFrustumCulling;
if ( _lodBuckets == null )
{
_lodBuckets = new List<Transform>[16];
for ( int i = 0; i < _lodBuckets.Length; i++ )
_lodBuckets[i] = new List<Transform>();
}
for ( int c = 0; c < cache.Length; c++ )
{
ref var chunk = ref cache[c];
Vector3 toChunk = chunk.Center - cameraPos;
float chunkDistSq = toChunk.LengthSquared;
if ( chunkDistSq > maxRenderDistSq ) continue;
if ( useFrustum && chunkDistSq > frustumMinDistSq )
{
float dot = Vector3.Dot( cameraForward, toChunk );
if ( dot < 0f && (dot * dot) > 0.04f * chunkDistSq )
continue;
}
var batches = chunk.Batches;
for ( int b = 0; b < batches.Length; b++ )
{
ref var batch = ref batches[b];
if ( chunkDistSq > batch.RenderDistSq ) continue;
int maxLod = batch.MaxLod;
for ( int i = 0; i <= maxLod; i++ )
_lodBuckets[i].Clear();
var transforms = batch.Transforms;
var lodDists = batch.LodDistancesSq;
for ( int i = 0; i < transforms.Length; i++ )
{
Vector3 instPos = transforms[i].Position;
float instDistSq = (instPos - cameraPos).LengthSquared;
if ( instDistSq > batch.RenderDistSq ) continue;
int lod = 0;
for ( int l = 0; l < lodDists.Length; l++ )
{
if ( instDistSq > lodDists[l] )
lod = l + 1;
else
break;
}
if ( lod > maxLod ) lod = maxLod;
_lodBuckets[lod].Add( transforms[i] );
}
for ( int l = 0; l <= maxLod; l++ )
{
var bucket = _lodBuckets[l];
if ( bucket.Count > 0 )
{
Graphics.DrawModelInstanced( batch.Model, CollectionsMarshal.AsSpan( bucket ), l );
}
}
}
}
}
}