using System.Collections; using System.Collections.Generic; using Godot; using System; namespace Rokojori { public enum MaterialSlot { None, MeshSurface, MeshSurfaceOverride, Override, Overlay } public abstract class MaterialSurfaceContainer { protected Node3D _owner; protected int _surfaceIndex; public int surfaceIndex => _surfaceIndex; protected MaterialSurfaceContainer( Node3D owner, int surfaceIndex ) { _owner = owner; _surfaceIndex = surfaceIndex; } public abstract MaterialSlot GetActiveMaterialSlot(); public abstract T GetMaterialInSlot( MaterialSlot slot ) where T:Material; public abstract void SetMaterialInSlot( MaterialSlot slot, Material material ); public static void SetMaterialInSlot( Node3D owner, int surfaceIndex, MaterialSlot slot, Material material ) { var c = MaterialSurfaceContainer.From( owner, surfaceIndex ); c.SetMaterialInSlot( slot, material ); } public static T GetMaterialInSlot( Node3D owner, int surfaceIndex, MaterialSlot slot ) where T:Material { var c = MaterialSurfaceContainer.From( owner, surfaceIndex ); return c.GetMaterialInSlot( slot ); } public void SetActiveMaterial( Material material ) { SetMaterialInSlot( GetActiveMaterialSlot(), material ); } public T GetActiveMaterial() where T:Material { return (T)GetMaterialInSlot( GetActiveMaterialSlot() ); } protected abstract void _MakeUnique( bool materials ); public void MakeUnique( bool materials ) { if ( ! isOwner ) { return; } _MakeUnique( materials ); } public bool isOwner => _surfaceIndex == -1; public virtual List GetOwnedSurfaceContainers() { if ( ! isOwner ) { return null; } var surfaces = numSurfaces; var type = GetType(); var list = new List(); for ( int i = 0; i < surfaces; i++ ) { var surfaceContainer = ReflectionHelper.Create( type, _owner, i ); list.Add( surfaceContainer ); } return list; } public virtual void ForAllSurfaces( Action action ) { var surfaces = GetOwnedSurfaceContainers(); surfaces.ForEach( s => action( s ) ); } public abstract int numSurfaces { get; } public static void SetMaterialInSlot( Node3D owner, MaterialSlot slot, Material material, int surfaceIndex = 0 ) { var mc = From( owner, surfaceIndex ); if ( mc == null ) { return; } mc.SetMaterialInSlot( slot, material ); } public static T GetMaterialInSlot( Node3D owner, MaterialSlot slot, int surfaceIndex = 0 ) where T:Material { var mc = From( owner, surfaceIndex ); if ( mc == null ) { return null; } return mc.GetMaterialInSlot( slot ); } public static MaterialSurfaceContainer From( Node3D owner, int surfaceIndex = -1 ) { if ( owner is MeshInstance3D mesh ) { return new MeshSurfaceContainer( mesh, surfaceIndex ); } if ( owner is MultiMeshInstance3D multi ) { return new MultiMeshSurfaceContainer( multi, surfaceIndex ); } return null; } } public abstract class MaterialSurfaceContainer:MaterialSurfaceContainer where T:Node3D { public T node => (T)_owner; protected MaterialSurfaceContainer( Node3D owner, int surfaceIndex ):base( owner, surfaceIndex ){} } public class MeshSurfaceContainer: MaterialSurfaceContainer { public MeshSurfaceContainer( MeshInstance3D owner, int surfaceIndex = -1 ):base( owner, surfaceIndex ){} public override int numSurfaces => node == null || node.Mesh == null ? -1 : node.Mesh.GetSurfaceCount(); public override MaterialSlot GetActiveMaterialSlot() { if ( node.MaterialOverride != null ) { return MaterialSlot.Override; } if ( node.Mesh == null || surfaceIndex == -1 ) { return MaterialSlot.None; } if ( node.GetSurfaceOverrideMaterial( surfaceIndex ) != null ) { return MaterialSlot.MeshSurfaceOverride; } var material = node.Mesh.SurfaceGetMaterial( surfaceIndex ); return material == null ? MaterialSlot.None : MaterialSlot.MeshSurface; } public override void SetMaterialInSlot( MaterialSlot slot, Material material ) { if ( surfaceIndex == -1 || MaterialSlot.None == slot || material == null || node == null ) { return; } if ( MaterialSlot.MeshSurface == slot ) { node.Mesh.SurfaceSetMaterial( surfaceIndex, material ); } else if ( MaterialSlot.MeshSurfaceOverride == slot ) { node.SetSurfaceOverrideMaterial( surfaceIndex, material ); } else if ( MaterialSlot.Override == slot ) { node.MaterialOverride = material; } else if ( MaterialSlot.Overlay == slot ) { node.MaterialOverlay = material; } } public override T GetMaterialInSlot( MaterialSlot slot ) { if ( MaterialSlot.None == slot ) { return null; } else if ( MaterialSlot.MeshSurface == slot ) { return (T) node.Mesh.SurfaceGetMaterial( surfaceIndex ); } else if ( MaterialSlot.MeshSurfaceOverride == slot ) { return (T) node.GetSurfaceOverrideMaterial( surfaceIndex ); } else if ( MaterialSlot.Override == slot ) { return (T) node.MaterialOverride; } else if ( MaterialSlot.Overlay == slot ) { return (T) node.MaterialOverlay; } return null; } protected override void _MakeUnique( bool materials ) { if ( node.Mesh == null ) { return; } node.Mesh = (Mesh)node.Mesh.Duplicate(); if ( ! materials ) { return; } for ( int i = 0; i < numSurfaces; i++ ) { var m = node.Mesh.SurfaceGetMaterial( i ); node.Mesh.SurfaceSetMaterial( i, (Material)m.Duplicate() ); } } } public class MultiMeshSurfaceContainer: MaterialSurfaceContainer { public MultiMeshSurfaceContainer( MultiMeshInstance3D mi, int surfaceIndex = -1 ):base( mi, surfaceIndex ){} public override int numSurfaces => node == null || node.Multimesh == null || node.Multimesh.Mesh == null ? -1 : node.Multimesh.Mesh.GetSurfaceCount(); public override MaterialSlot GetActiveMaterialSlot() { if ( node.MaterialOverride != null ) { return MaterialSlot.Override; } if ( node.Multimesh == null || node.Multimesh.Mesh == null || surfaceIndex == -1 ) { return MaterialSlot.None; } var material = node.Multimesh.Mesh.SurfaceGetMaterial( surfaceIndex ); return material == null ? MaterialSlot.None : MaterialSlot.MeshSurface; } public override void SetMaterialInSlot( MaterialSlot slot, Material material ) { if ( surfaceIndex == -1 || MaterialSlot.None == slot ) { return; } if ( MaterialSlot.MeshSurface == slot ) { node.Multimesh.Mesh.SurfaceSetMaterial( surfaceIndex, material ); } else if ( MaterialSlot.Override == slot ) { node.MaterialOverride = material; } else if ( MaterialSlot.Overlay == slot ) { node.MaterialOverlay = material; } } public override T GetMaterialInSlot( MaterialSlot slot ) { if ( MaterialSlot.None == slot ) { return null; } else if ( MaterialSlot.MeshSurface == slot ) { return (T) node.Multimesh.Mesh.SurfaceGetMaterial( surfaceIndex ); } else if ( MaterialSlot.Override == slot ) { return (T) node.MaterialOverride; } else if ( MaterialSlot.Overlay == slot ) { return (T) node.MaterialOverlay; } return null; } protected override void _MakeUnique( bool materials ) { if ( node.Multimesh == null || node.Multimesh.Mesh == null ) { return; } node.Multimesh.Mesh = (Mesh)node.Multimesh.Mesh.Duplicate(); if ( ! materials ) { return; } for ( int i = 0; i < numSurfaces; i++ ) { var m = node.Multimesh.Mesh .SurfaceGetMaterial( i ); node.Multimesh.Mesh.SurfaceSetMaterial( i, (Material)m.Duplicate() ); } } } }