using System.Collections; using System.Collections.Generic; using Godot; using System; using System.Linq; namespace Rokojori { [Tool] [GlobalClass] public partial class MassRenderer:Node3D { public enum Mode { MultiMeshInstance3D, Combined, MeshInstance3D } [Export] public Mode mode = Mode.MultiMeshInstance3D; [Export] public int randomizeQueue = 1984; [ExportGroup("Mesh")] [Export] public Mesh mesh; [Export] public Material materialOveride; [ExportGroup("Layout")] [Export] public Vector3[] positions = new Vector3[ 0 ]; [Export] public Vector4[] rotations = new Vector4[ 0 ]; [Export] public Vector3[] scales = new Vector3[ 0 ]; [ExportGroup("Outputs")] [Export] public LODMultiMeshInstance3D[] multiMeshNodes; [Export] public float multiMeshSplitSize = 25; [Export] public Vector3 multiMeshSplitScatterRelative = new Vector3( 2, 0, 2 ); [Export] public Vector3 multiMeshSplitScatterScale = new Vector3( 10, 10, 10 ); Dictionary multiMeshMapping = new Dictionary(); [Export] public Node3D meshesContainer; [Export] public MeshInstance3D combinedMesh; List _queuedTransforms = new List(); public void QueueObject( Transform3D transform ) { _queuedTransforms.Add( transform ); } public void AddQueued() { if ( positions == null ) { positions = new Vector3[0]; } if ( rotations == null ) { rotations = new Vector4[0]; } if ( scales == null ) { scales = new Vector3[0]; } if ( randomizeQueue >= 0 ) { _queuedTransforms = RandomList.Randomize( _queuedTransforms, LCG.WithSeed( randomizeQueue ) ); } var queuedPositions = Lists.Map( _queuedTransforms, t => t.Origin ); var queuedRotations = Lists.Map( _queuedTransforms, t => Math3D.QuaternionToVector4( t.Basis.GetRotationQuaternion() ) ); var queuedScales = Lists.Map( _queuedTransforms, t => t.Basis.Scale ); positions = Arrays.Concat( positions, queuedPositions.ToArray() ); rotations = Arrays.Concat( rotations, queuedRotations.ToArray() ); scales = Arrays.Concat( scales, queuedScales.ToArray() ); _queuedTransforms.Clear(); } public void Create() { AddQueued(); if ( Mode.MultiMeshInstance3D == mode ) { CreateMultiMeshes(); } else if ( Mode.MeshInstance3D == mode ) { CreateMeshes(); } else if ( Mode.Combined == mode ) { CreateCombined(); } } public void Clear() { if ( multiMeshNodes != null ) { for ( int i = 0 ; i < multiMeshNodes.Length; i++ ) { multiMeshNodes[ i ] = Nodes.EnsureValid( multiMeshNodes[ i ] ); if ( multiMeshNodes[ i ] != null ) { Nodes.RemoveAndDelete( multiMeshNodes[ i ] ); } } multiMeshNodes = []; } meshesContainer = Nodes.EnsureValid( meshesContainer ); combinedMesh = Nodes.EnsureValid( combinedMesh ); if ( meshesContainer != null ) { Nodes.RemoveAndDeleteChildren( meshesContainer ); } if ( combinedMesh != null ) { combinedMesh.Mesh = null; } } void CreateMultiMeshes() { var instancesSorted = new MapList(); for ( int i = 0; i < positions.Length; i++ ) { var floatIndex = ( positions[ i ] / multiMeshSplitSize ); floatIndex += Noise.Perlin3( positions[ i ] * multiMeshSplitScatterScale ) * multiMeshSplitScatterRelative; var index = floatIndex.RoundToInt(); instancesSorted.Add( index, i ); } var keys = instancesSorted.Keys.ToList(); var numMultiMeshes = keys.Count; multiMeshNodes = new LODMultiMeshInstance3D[ numMultiMeshes ]; for ( int i = 0; i < numMultiMeshes; i++ ) { var multiMeshNode = this.CreateChild(); multiMeshNode.Multimesh = new MultiMesh(); multiMeshNode.Multimesh.TransformFormat = MultiMesh.TransformFormatEnum.Transform3D; multiMeshNodes[ i ] = multiMeshNode; var mm = multiMeshNode.Multimesh; var meshIndices = instancesSorted[ keys[ i ] ]; mm.Mesh = mesh; mm.InstanceCount = meshIndices.Count; if ( materialOveride != null ) { multiMeshNode.MaterialOverride = materialOveride; } this.LogInfo( i, ">>", keys[ i ], ">>", meshIndices.Count ); for ( int j = 0; j < meshIndices.Count; j++ ) { var index = meshIndices[ j ]; var trsf = Math3D.TRS( positions[ index ], rotations[ index ], scales[ index ] ); mm.SetInstanceTransform( j, trsf ); } } } void CreateMeshes() { if ( meshesContainer == null ) { meshesContainer = this.CreateChild(); } for ( int i = 0; i < positions.Length; i++ ) { var trsf = Math3D.TRS( positions[ i ], rotations[ i ], scales[ i ] ); var child = meshesContainer.CreateChild(); child.Mesh = mesh; if ( materialOveride != null ) { child.MaterialOverride = materialOveride; } child.GlobalTransform = trsf; } } void CreateCombined() { if ( meshesContainer == null ) { combinedMesh = this.CreateChild(); } var combinedMG = new MeshGeometry(); var meshMG = MeshGeometry.From( mesh as ArrayMesh ); for ( int i = 0; i < positions.Length; i++ ) { var trsf = Math3D.TRS( positions[ i ], rotations[ i ], scales[ i ] ); combinedMG.Add( meshMG, trsf ); } if ( materialOveride != null ) { combinedMesh.MaterialOverride = materialOveride; } } } }