namespace Sandbox; public class ModelParts { private readonly Model _model; private List _all; /// /// All body parts in this model. /// public IReadOnlyList All { get { _all ??= BuildParts(); return _all; } } /// /// How many body parts there are in this model. /// public int Count { get; private set; } /// /// Default body groups mask for this model. /// public ulong DefaultMask { get; private set; } private ModelParts() { } public ModelParts( Model model ) { _model = model ?? throw new ArgumentNullException( nameof( model ) ); Count = _model.native.GetNumBodyParts(); DefaultMask = _model.native.GetDefaultMeshGroupMask(); } private List BuildParts() { var list = new List( Count ); for ( int i = 0; i < Count; i++ ) { var meshCount = _model.native.GetNumBodyPartMeshes( i ); var choices = new List( meshCount ); for ( int m = 0; m < meshCount; m++ ) { var name = _model.native.GetBodyPartMeshName( i, m ); var mask = _model.native.GetBodyPartMeshMask( i, m ); choices.Add( new Model.BodyPart.Choice( name, mask ) ); } var part = new Model.BodyPart { Index = i, Name = _model.native.GetBodyPartName( i ), Mask = _model.native.GetBodyPartMask( i ), Choices = choices.AsReadOnly() }; if ( string.IsNullOrWhiteSpace( part.Name ) ) part.Name = "empty"; list.Add( part ); } return list; } internal void Dispose() { _all?.Clear(); _all = null; } /// /// Get body part by name. /// public Model.BodyPart Get( string name ) { return All.FirstOrDefault( x => x.Name == name ); } } partial class Model { ModelParts _parts; /// /// Access to body parts of this model. /// public ModelParts Parts { get { _parts ??= new ModelParts( this ); return _parts; } } [Obsolete( $"Use {nameof( Parts )}" )] public int BodyGroupCount => Parts.Count; [Obsolete( $"Use {nameof( Parts )}" )] public ulong DefaultBodyGroupMask => Parts.DefaultMask; [Obsolete( $"Use {nameof( Parts )}" )] public IEnumerable BodyParts => Parts.All; public sealed class BodyPart { public int Index { get; internal set; } public string Name { get; internal set; } public ulong Mask { get; internal set; } public IReadOnlyList Choices { get; internal set; } public sealed record Choice( string Name, ulong Mask ); internal int GetChoiceIndex( string name ) { for ( int i = 0; i < Choices.Count; i++ ) { if ( Choices[i].Name == name ) return i; } return -1; } } /// /// Used to mark properties as a body group mask, so the correct editor can be used /// public sealed class BodyGroupMaskAttribute : System.Attribute { public string ModelParameter { get; set; } = "Model"; } }