using System;
using System.Collections.Generic;
namespace Sandbox.Clutter;
public sealed partial class ClutterGridSystem
{
///
/// Manages storage and serialization of painted clutter instances.
/// Uses binary serialization via BlobData for efficient storage.
///
public sealed class ClutterStorage : BlobData
{
public override int Version => 1;
public record struct Instance( Vector3 Position, Rotation Rotation, float Scale = 1f );
private Dictionary> _instances = [];
public ClutterStorage() { }
///
/// Gets the total number of instances across all models.
///
public int TotalCount
{
get
{
var count = 0;
foreach ( var list in _instances.Values )
count += list.Count;
return count;
}
}
///
/// Gets all model paths that have instances.
///
public IEnumerable ModelPaths => _instances.Keys;
///
/// Gets instances for a specific model path.
///
public IReadOnlyList GetInstances( string modelPath )
{
if ( _instances.TryGetValue( modelPath, out var list ) )
return list;
return [];
}
///
/// Gets all instances grouped by model path.
///
public IReadOnlyDictionary> GetAllInstances() => _instances;
///
/// Adds a single instance for a model.
///
public void AddInstance( string modelPath, Vector3 position, Rotation rotation, float scale = 1f )
{
if ( string.IsNullOrEmpty( modelPath ) ) return;
if ( !_instances.TryGetValue( modelPath, out var list ) )
{
list = [];
_instances[modelPath] = list;
}
list.Add( new Instance( position, rotation, scale ) );
}
///
/// Adds multiple instances for a model.
///
public void AddInstances( string modelPath, IEnumerable instances )
{
if ( string.IsNullOrEmpty( modelPath ) ) return;
if ( !_instances.TryGetValue( modelPath, out var list ) )
{
list = [];
_instances[modelPath] = list;
}
list.AddRange( instances );
}
///
/// Erases all instances within a radius of a position.
///
public int Erase( Vector3 position, float radius )
{
if ( _instances.Count == 0 ) return 0;
var radiusSquared = radius * radius;
var totalRemoved = 0;
foreach ( var list in _instances.Values )
{
var removed = list.RemoveAll( i => i.Position.DistanceSquared( position ) <= radiusSquared );
totalRemoved += removed;
}
return totalRemoved;
}
///
/// Clears all instances for a specific model.
///
public bool ClearModel( string modelPath )
{
return _instances.Remove( modelPath );
}
///
/// Clears all instances.
///
public void ClearAll()
{
_instances.Clear();
}
///
/// Serialize to binary format.
///
public override void Serialize( ref Writer writer )
{
// Write model count
writer.Stream.Write( _instances.Count );
foreach ( var (modelPath, instances) in _instances )
{
// Write model path
writer.Stream.Write( modelPath );
// Write instance count
writer.Stream.Write( instances.Count );
// Write each instance
foreach ( var instance in instances )
{
writer.Stream.Write( instance.Position );
writer.Stream.Write( instance.Rotation );
writer.Stream.Write( instance.Scale );
}
}
}
///
/// Deserialize from binary format.
///
public override void Deserialize( ref Reader reader )
{
_instances.Clear();
// Read model count
var modelCount = reader.Stream.Read();
for ( int m = 0; m < modelCount; m++ )
{
// Read model path
var modelPath = reader.Stream.Read();
// Read instance count
var instanceCount = reader.Stream.Read();
var instances = new List( instanceCount );
// Read each instance
for ( int i = 0; i < instanceCount; i++ )
{
var position = reader.Stream.Read();
var rotation = reader.Stream.Read();
var scale = reader.Stream.Read();
instances.Add( new Instance( position, rotation, scale ) );
}
if ( !string.IsNullOrEmpty( modelPath ) && instances.Count > 0 )
{
_instances[modelPath] = instances;
}
}
}
}
}