using System.Runtime.InteropServices; namespace Sandbox.Clutter; /// /// Custom scene object for rendering batched clutter models. /// Groups instances by model type for efficient GPU instanced rendering. /// internal class ClutterBatchSceneObject : SceneCustomObject { /// /// Batches by model /// private readonly Dictionary _batches = []; public ClutterBatchSceneObject( SceneWorld world ) : base( world ) { managedNative.ExecuteOnMainThread = false; Flags.IsOpaque = true; Flags.IsTranslucent = false; Flags.CastShadows = true; Flags.WantsPrePass = true; } /// /// Adds a clutter instance to the appropriate batch. /// public void AddInstance( ClutterInstance instance ) { if ( instance.Entry?.Model == null ) return; var model = instance.Entry.Model; if ( !_batches.TryGetValue( model, out var batch ) ) { batch = new ClutterModelBatch( model ); _batches[model] = batch; } batch.AddInstance( instance.Transform ); } /// /// Clears all batches. /// public void Clear() { foreach ( var batch in _batches.Values ) batch.Clear(); _batches.Clear(); } /// /// Called when the batch is deleted. Cleans up resources. /// public new void Delete() { Clear(); base.Delete(); } /// /// Renders all batched instances using GPU instancing. /// public override void RenderSceneObject() { if ( _batches.Count == 0 ) return; foreach ( var (model, batch) in _batches ) { if ( batch.Transforms.Count == 0 || model == null ) continue; Graphics.DrawModelInstanced( model, CollectionsMarshal.AsSpan( batch.Transforms ) ); } } }