diff --git a/Runtime/Bits/Bytes.cs b/Runtime/Bits/Bytes.cs index 3da5202..0b1b006 100644 --- a/Runtime/Bits/Bytes.cs +++ b/Runtime/Bits/Bytes.cs @@ -8,6 +8,31 @@ namespace Rokojori { public class Bytes { + public static string ConvertToString( ulong numBytes, int basis = 1000 ) + { + var unitIndex = 0; + var units = new List() + { + "Bytes", + "KB", + "MB", + "GB", + "TB" + }; + + + + double num = numBytes; + + while ( num >= 1000 && unitIndex < ( units.Count - 1 ) ) + { + num = num / 1000.0; + unitIndex ++; + } + + var fNum = (float) num; + return RegexUtility._FFF( fNum ) + " " + units[ unitIndex ]; + } public static byte SetBit( byte value, int position ) { diff --git a/Runtime/Math/Geometry/Box2.cs b/Runtime/Math/Geometry/Box2.cs index cf95867..28d105a 100644 --- a/Runtime/Math/Geometry/Box2.cs +++ b/Runtime/Math/Geometry/Box2.cs @@ -13,6 +13,11 @@ namespace Rokojori { this.min = min; this.max = max; + } + + public static Box2 With( float width, float height, float x = 0, float y = 0 ) + { + return new Box2( new Vector2( x, y ), new Vector2( x + width, y + height ) ); } public Box2() diff --git a/Runtime/Math/MathX.cs b/Runtime/Math/MathX.cs index c08b571..7948fc0 100644 --- a/Runtime/Math/MathX.cs +++ b/Runtime/Math/MathX.cs @@ -189,13 +189,24 @@ namespace Rokojori return value * 0.5f + 0.5f; } - public static int NextPowerOfTwo( int num ) + public static int NextPowerOfTwoExponent( int num ) { var p = Exponent( 2, num ); return Mathf.CeilToInt( p ); } + public static int NextPowerOfTwoExponent( float num ) + { + return NextPowerOfTwoExponent( Mathf.RoundToInt( num ) ); + } + + public static int NextPowerOfTwo( float num ) + { + var exponent = NextPowerOfTwoExponent( num ); + return 1 << exponent; + } + public static float Repeat( float value, float length ) { while ( value > length ) diff --git a/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/AlbedoBakingPass.cs b/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/AlbedoBakingPass.cs index 9a7807a..f89d583 100644 --- a/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/AlbedoBakingPass.cs +++ b/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/AlbedoBakingPass.cs @@ -62,7 +62,7 @@ namespace Rokojori if ( material == null ) { - n.LogInfo( "No material found" ); + // n.LogInfo( "No material found" ); return; } @@ -72,7 +72,7 @@ namespace Rokojori if ( material is StandardMaterial3D sm ) { - n.LogInfo( "StandardMaterial3D found" ); + // n.LogInfo( "StandardMaterial3D found" ); appliedMaterial.AlbedoColor = sm.AlbedoColor; appliedMaterial.AlbedoTexture = sm.AlbedoTexture; @@ -90,7 +90,7 @@ namespace Rokojori } else { - n.LogInfo( "No StandardMaterial3D found", material.GetType().Name ); + // n.LogInfo( "No StandardMaterial3D found", material.GetType().Name ); } diff --git a/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/NormalsBakingPass.cs b/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/NormalsBakingPass.cs index 6dda28a..993fdee 100644 --- a/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/NormalsBakingPass.cs +++ b/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/NormalsBakingPass.cs @@ -67,7 +67,7 @@ namespace Rokojori if ( material == null ) { - n.LogInfo( "No material found" ); + // n.LogInfo( "No material found" ); return; } @@ -81,7 +81,7 @@ namespace Rokojori if ( material is StandardMaterial3D sm ) { - n.LogInfo( "StandardMaterial3D found" ); + // n.LogInfo( "StandardMaterial3D found" ); appliedMaterial.SetShaderParameter( "use_normalmap", sm.NormalTexture != null ); appliedMaterial.SetShaderParameter( "normal_texture", sm.NormalTexture ); @@ -99,7 +99,7 @@ namespace Rokojori } else { - n.LogInfo( "No StandardMaterial3D found", material.GetType().Name ); + // n.LogInfo( "No StandardMaterial3D found", material.GetType().Name ); } diff --git a/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/ORMBakingPass.cs b/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/ORMBakingPass.cs index ccfec61..ae52dc1 100644 --- a/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/ORMBakingPass.cs +++ b/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/ORMBakingPass.cs @@ -92,7 +92,7 @@ namespace Rokojori if ( material == null ) { - n.LogInfo( "No material found" ); + // n.LogInfo( "No material found" ); return; } @@ -103,7 +103,7 @@ namespace Rokojori if ( material is StandardMaterial3D sm ) { - n.LogInfo( "StandardMaterial3D found" ); + // n.LogInfo( "StandardMaterial3D found" ); SetChannel( "A", appliedMaterial, sm.AOTexture, 0 ); SetChannel( "B", appliedMaterial, sm.RoughnessTexture, 1 ); @@ -128,7 +128,7 @@ namespace Rokojori } else { - n.LogInfo( "No StandardMaterial3D found", material.GetType().Name ); + // n.LogInfo( "No StandardMaterial3D found", material.GetType().Name ); } diff --git a/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/UVBakingPass.cs b/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/UVBakingPass.cs index a330094..5712beb 100644 --- a/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/UVBakingPass.cs +++ b/Runtime/Procedural/Baking/BakingMaterials/TextureBakers/Passes/UVBakingPass.cs @@ -67,7 +67,7 @@ namespace Rokojori if ( material == null ) { - n.LogInfo( "No material found" ); + // n.LogInfo( "No material found" ); return; } @@ -78,7 +78,7 @@ namespace Rokojori if ( material is StandardMaterial3D sm ) { - n.LogInfo( "StandardMaterial3D found" ); + // n.LogInfo( "StandardMaterial3D found" ); appliedMaterial.SetShaderParameter( "uv1_scale", sm.Uv1Scale ); appliedMaterial.SetShaderParameter( "uv1_offset", sm.Uv1Offset ); @@ -86,7 +86,7 @@ namespace Rokojori } else { - n.LogInfo( "No StandardMaterial3D found", material.GetType().Name ); + // n.LogInfo( "No StandardMaterial3D found", material.GetType().Name ); } diff --git a/Runtime/Procedural/Baking/MultiBaker/CrossBraces_Baker.cs b/Runtime/Procedural/Baking/MultiBaker/CrossBraces_Baker.cs index 817cbc4..7786567 100644 --- a/Runtime/Procedural/Baking/MultiBaker/CrossBraces_Baker.cs +++ b/Runtime/Procedural/Baking/MultiBaker/CrossBraces_Baker.cs @@ -143,6 +143,7 @@ namespace Rokojori } + mg.ApplyTranslation( -mb.targetPivot ); mb.X_outputMesh.Mesh = mg.GenerateMesh(); } diff --git a/Runtime/Procedural/Baking/MultiBaker/Cylinder_Baker.cs b/Runtime/Procedural/Baking/MultiBaker/Cylinder_Baker.cs index bc1ae50..7f159f8 100644 --- a/Runtime/Procedural/Baking/MultiBaker/Cylinder_Baker.cs +++ b/Runtime/Procedural/Baking/MultiBaker/Cylinder_Baker.cs @@ -159,6 +159,7 @@ namespace Rokojori } + mg.ApplyTranslation( -mb.targetPivot ); mb.X_outputMesh.Mesh = mg.GenerateMesh(); diff --git a/Runtime/Procedural/Baking/MultiBaker/Flat_Baker.cs b/Runtime/Procedural/Baking/MultiBaker/Flat_Baker.cs index 8d65144..b73752a 100644 --- a/Runtime/Procedural/Baking/MultiBaker/Flat_Baker.cs +++ b/Runtime/Procedural/Baking/MultiBaker/Flat_Baker.cs @@ -15,7 +15,7 @@ namespace Rokojori public override int GetNumViews() { - return 2; + return useDoubleSidedMaterial ? 1 : 2; } public override void CreateBakers() @@ -24,7 +24,6 @@ namespace Rokojori var distance = multiBaker.GetCameraDistance(); var outputScale = multiBaker.GetOutputScale(); - RJLog.Log( "outputScale", outputScale ); var bakerCameras = multiBaker.bakerCameras; var mb = multiBaker; @@ -62,6 +61,8 @@ namespace Rokojori } + mg.ApplyTranslation( -mb.targetPivot ); + mb.X_outputMesh.Mesh = mg.GenerateMesh(); } diff --git a/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs b/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs index 37a8653..289ac23 100644 --- a/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs +++ b/Runtime/Procedural/Baking/MultiBaker/MultiBaker.cs @@ -13,7 +13,7 @@ namespace Rokojori public partial class MultiBaker:Node { [ExportToolButton( "Bake")] - public Callable BakeButton => Callable.From( () => StartBaking() ); + public Callable BakeButton => Callable.From( () => Bake() ); [Export] public bool cleanUpAfterBaking = true; @@ -21,31 +21,10 @@ namespace Rokojori [Export] public _XX_MultiBakeMode bakeMode; - public enum MaterialMode - { - Full_Seperated, - Simple_Prebaked - } - - // [ExportGroup("Textures")] - [Export] public _XX_MultiTextureBaker textureBaker; - // [Export] - // public MaterialMode materialMode; - - // [Export] - // public int dilationRadius = 64; - // [ExportGroup("Material/Full Seperated")] - // [Export] - // public bool mmfs_Normals = true; - // [Export] - // public bool mmfs_Depth = true; - // [Export] - // public bool mmfs_ORM = true; - [Export] public BakingViewSettings viewSettings; @@ -61,9 +40,6 @@ namespace Rokojori public Vector3 targetPivot; - - - public float cameraZoom { get @@ -150,10 +126,12 @@ namespace Rokojori [Export] public bool X_baking = false; - public void StartBaking() + public async Task Bake() { Initialize(); - Bake(); + await CreateBakes(); + + return; } public void Initialize() @@ -234,7 +212,12 @@ namespace Rokojori // _bakerCameras.ForEach( b => b.viewport.en) Nodes.RemoveAndDeleteChildren( X_bakingTargetContainer ); - target.DeepCopyTo( X_bakingTargetContainer ); + var root = (Node3D)target.DeepCopyTo( X_bakingTargetContainer ); + + root.GlobalPosition = Vector3.Zero; + root.SetGlobalQuaternion( Quaternion.Identity ); + root.Scale = Vector3.One; + _targetIsInInitialState = true; @@ -272,7 +255,7 @@ namespace Rokojori } - public async Task Bake() + public async Task CreateBakes() { if ( X_baking ) { @@ -307,39 +290,9 @@ namespace Rokojori var passes = textureBaker.GetPasses( this ); - // X_setBakingMaterials.SetTarget( X_bakingTargetContainer ); - - // var bakingMaterialModes = new List(); - - // var preview_QuickMaterial = MaterialMode.Simple_Prebaked == materialMode; - - // bakeMode.CreateMaterial( preview_QuickMaterial ); bakeMode.CreateMaterial( passes ); - // if ( preview_QuickMaterial ) - // { - // bakingMaterialModes.Add( BakingMaterialMode.Preview ); - // } - // else - // { - // bakingMaterialModes.Add( BakingMaterialMode.Albedo ); - - // if ( mmfs_Normals ) - // { - // bakingMaterialModes.Add( BakingMaterialMode.Normals ); - // } - - // if ( mmfs_Depth ) - // { - // bakingMaterialModes.Add( BakingMaterialMode.Depth ); - // } - - // if ( mmfs_ORM ) - // { - // bakingMaterialModes.Add( BakingMaterialMode.ORM ); - // } - // } this.LogInfo( "Prepared baking modes" ); X_textureMerger.textureSize = outputTextureSize; @@ -347,13 +300,6 @@ namespace Rokojori X_textureMerger.CreateLayout(); - // this.LogInfo( "Prepared texture merger" ); - // var fovDistance = viewSettings.fovDistance.ComputeFOVDistance( - // var objectDistance = GetCameraDistance(); - - // this.LogInfo( "Set Camera Distance", objectDistance ); - - for ( int i = 0; i < passes.Count; i++ ) { ResetOriginalTargetState(); @@ -366,34 +312,7 @@ namespace Rokojori bakeMode.AssignMaterial( passes ); - // for ( int i = 0; i < bakingMaterialModes.Count; i++ ) - // { - // this.LogInfo( "Baking mode:", bakingMaterialModes[ i ] ); - // X_setBakingMaterials.mode = bakingMaterialModes[ i ]; - // X_setBakingMaterials.ApplyBakingMaterials( objectDistance, _targetBoundingSphere.radius ); - - // this.LogInfo( "Materials changed:", bakingMaterialModes[ i ] ); - - // _bakers.ForEach( b => b.ApplyCameraSettings() ); - - // await this.RequestNextFrame(); - - // X_textureDilate.viewport = X_textureMerger.X_textureMergerViewport; - // Texture2D texture = await X_textureDilate.Grab(); - - - // this.LogInfo( "Assigning Texture", bakingMaterialModes[ i ] ); - // bakeMode.AssignMaterial( bakingMaterialModes[ i ], texture ); - - // this.LogInfo( "Baking done:", bakingMaterialModes[ i ] ); - - // await this.RequestNextFrame(); - - // } - - // await this.RequestNextFrame(); - - X_outputMesh.GlobalPosition = target.GlobalPosition - targetPivot; + X_outputMesh.GlobalPosition = target.GlobalPosition;// - targetPivot; X_outputMesh.Scale = Vector3.One * cameraZoom; @@ -424,27 +343,6 @@ namespace Rokojori return Nodes.AllIn( X_views, null, false ); } - // public void CacheTexture( BakingMaterialMode mode, Texture2D texture ) - // { - // if ( BakingMaterialMode.Albedo == mode ) - // { - // X_bakedTextureAlbedo = texture; - // } - // else if ( BakingMaterialMode.Normals == mode ) - // { - // X_bakedTextureNormal = texture; - // } - // else if ( BakingMaterialMode.ORM == mode ) - // { - // X_bakedTextureORM = texture; - // } - // else if ( BakingMaterialMode.Depth == mode ) - // { - // X_bakedTextureDepth = texture; - // } - - // } - public void CleanUp() { var mesh = X_outputMesh; diff --git a/Runtime/Procedural/Baking/MultiBaker/_XX_MultiBakeModeBillboardBase.cs b/Runtime/Procedural/Baking/MultiBaker/_XX_MultiBakeModeBillboardBase.cs index a5beb40..3c1d003 100644 --- a/Runtime/Procedural/Baking/MultiBaker/_XX_MultiBakeModeBillboardBase.cs +++ b/Runtime/Procedural/Baking/MultiBaker/_XX_MultiBakeModeBillboardBase.cs @@ -10,6 +10,9 @@ namespace Rokojori [Tool][GlobalClass] public partial class _XX_MultiBakeModeBillboardBase:_XX_MultiBakeMode { + [Export] + public bool useDoubleSidedMaterial = false; + public override void CreateMaterial( List<_XX_BakingPass> passes ) { var preview = passes.Find( p => p.ContainsBakingOutput( BakingTargetType.Albedo ) ) == null; @@ -25,6 +28,11 @@ namespace Rokojori material.AlphaAntialiasingEdge = 0.95f; material.NormalEnabled = ! preview; + if ( useDoubleSidedMaterial ) + { + material.CullMode = BaseMaterial3D.CullModeEnum.Disabled; + } + Materials.Set( mb.X_outputMesh, material ); } diff --git a/Runtime/Procedural/Baking/TextureDilate.cs b/Runtime/Procedural/Baking/TextureDilate.cs index 735d03c..55b0bf0 100644 --- a/Runtime/Procedural/Baking/TextureDilate.cs +++ b/Runtime/Procedural/Baking/TextureDilate.cs @@ -41,10 +41,16 @@ namespace Rokojori public void SetDilationRadius( Vector2 size ) { SetDilationRadius( Mathf.RoundToInt( Mathf.Max( size.X, size.Y ) ) ); - } + } + + public bool skipMode = false; public async Task Grab( bool srgb, bool alpha, bool mipmaps = false ) { + + this.LogInfo( "Current Render Device Memory", RDContext.Current().GetMemoryInfo() ); + + var updateMode = viewport.RenderTargetUpdateMode; if ( updateMode != SubViewport.UpdateMode.Always ) @@ -54,19 +60,30 @@ namespace Rokojori viewport.RenderTargetUpdateMode = updateMode; } + if ( skipMode ) + { + this.LogInfo( "Skipping Creation" ); + var skipImage = viewport.GetTexture().GetImage(); + var skipBuffer = TextureCombinerBuffer.From( ImageTexture.CreateFromImage( skipImage ), -1 ); + var skipTexture = skipBuffer.CreateImageTexture( alpha, mipmaps ); + return skipTexture; + } + + var viewPortFormat = RDTextureFormats.GetDataFormat( viewport ); var viewPortData = viewport.GetTexture().GetImage().GetData(); var viewPortSize = viewport.Size; - this.LogInfo( "Grabbed viewport context", viewPortFormat, viewPortSize ); - this.LogInfo( "Creating context" ); + // this.LogInfo( "Grabbed viewport context", viewPortFormat, viewPortSize ); + + // this.LogInfo( "Creating context" ); var ctx = RDContext.Local(); ctx.computeSize = viewPortSize; // ctx.messageLogLevel = Messages.GetLevel( MessageType.Verbose ); - this.LogInfo( "Creating textures" ); + // this.LogInfo( "Creating textures" ); var bufferTexture = RDTexture.Create( ctx, viewport.Size, viewPortFormat ); var bufferTexture2 = RDTexture.Create( ctx, viewport.Size, viewPortFormat ); @@ -77,11 +94,11 @@ namespace Rokojori var inputTexture = RDTexture.Create( ctx, viewport.Size, viewPortFormat ); inputTexture.SetData( viewPortData ); - this.LogInfo( "Creating graph" ); + // this.LogInfo( "Creating graph" ); var graph = CreateGraph( ctx, inputTexture, initTexture, bufferTexture, bufferTexture2, resultTexture ); - this.LogInfo( "Process graph" ); + // this.LogInfo( "Process graph" ); graph.ProcessForView(); ctx.SubmitAndSync(); @@ -97,7 +114,7 @@ namespace Rokojori await this.RequestNextFrame(); - this.LogInfo( "Copying texture:", format ); + // this.LogInfo( "Copying texture:", format ); var image = Image.CreateFromData( width, height, false, format, data ); @@ -115,7 +132,15 @@ namespace Rokojori target = texture; - ctx.CleanUp(); + // ctx.messageLogLevel = Messages.GetLevel( MessageType.Verbose ); + // ctx.CleanUp(); + // ctx.messageLogLevel = Messages.GetLevel( MessageType.Info ); + + ctx.CleanUpAndFree(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + return texture; diff --git a/Runtime/Procedural/Baking/TextureMerger.cs b/Runtime/Procedural/Baking/TextureMerger.cs index 196968c..4a6d3e5 100644 --- a/Runtime/Procedural/Baking/TextureMerger.cs +++ b/Runtime/Procedural/Baking/TextureMerger.cs @@ -242,7 +242,7 @@ namespace Rokojori var uvRectangle = GetUVRectangle( alignment, i ); - RJLog.Log( "Set Texture" + ( i + 1 ), uvRectangle.min, uvRectangle.max ); + // RJLog.Log( "Set Texture" + ( i + 1 ), uvRectangle.min, uvRectangle.max ); SetMeshCoordinates( meshInstance, uvRectangle, i ); var dilatedMaterial = new DilationDrawerMaterial(); @@ -310,7 +310,18 @@ namespace Rokojori mesh.GlobalPosition = new Vector3( start.X + size.X/2, start.Y + size.Y/2, -1 ); - RJLog.Log( "Set Mesh", index, "Size:", size, "Min:", min, ">>", start, "Max:", max, ">>", end ); + // RJLog.Log( "Set Mesh", index, "Size:", size, "Min:", min, ">>", start, "Max:", max, ">>", end ); + } + + public static Box2 GetUVAlignmentBoxFor( Vector2I alignment, int index ) + { + var x = index % alignment.X; + var y = index / alignment.X; + + var sizeX = 1f / alignment.X; + var sizeY = 1f / alignment.Y; + + return Box2.With( sizeX, sizeY, x * sizeX, y * sizeY ); } public static Vector2I ComputeTextureAlignment( int numElements ) diff --git a/Runtime/Procedural/Mesh/MaterialSurfaceContainer.cs b/Runtime/Procedural/Mesh/MaterialSurfaceContainer.cs new file mode 100644 index 0000000..803d06f --- /dev/null +++ b/Runtime/Procedural/Mesh/MaterialSurfaceContainer.cs @@ -0,0 +1,296 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public abstract class MaterialSurfaceContainer + { + public enum MaterialSlot + { + None, + MeshSurface, + MeshSurfaceOverride, + Override, + Overlay + } + + 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 Material GetMaterialInSlot( MaterialSlot slot ); + public abstract void SetMaterialInSlot( MaterialSlot slot, Material material ); + + public void SetActiveMaterial( Material material ) + { + SetMaterialInSlot( GetActiveMaterialSlot(), material ); + } + + public Material GetActiveMaterial() + { + return 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 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 MeshSurfaceContainer.MaterialSlot GetActiveMaterialSlot() + { + if ( node.MaterialOverride != null ) + { + return MeshSurfaceContainer.MaterialSlot.Override; + } + + if ( node.Mesh == null || surfaceIndex == -1 ) + { + return MeshSurfaceContainer.MaterialSlot.None; + } + + if ( node.GetSurfaceOverrideMaterial( surfaceIndex ) != null ) + { + return MeshSurfaceContainer.MaterialSlot.MeshSurfaceOverride; + } + + var material = node.Mesh.SurfaceGetMaterial( surfaceIndex ); + + return material == null ? MeshSurfaceContainer.MaterialSlot.None : MeshSurfaceContainer.MaterialSlot.MeshSurface; + } + + public override void SetMaterialInSlot( MaterialSlot slot, Material material ) + { + if ( surfaceIndex == -1 || MaterialSlot.None == slot ) + { + 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 Material GetMaterialInSlot( MaterialSlot slot ) + { + if ( MaterialSlot.None == slot ) + { + return null; + } + else if ( MaterialSlot.MeshSurface == slot ) + { + return node.Mesh.SurfaceGetMaterial( surfaceIndex ); + } + else if ( MaterialSlot.MeshSurfaceOverride == slot ) + { + return node.GetSurfaceOverrideMaterial( surfaceIndex ); + } + else if ( MaterialSlot.Override == slot ) + { + return node.MaterialOverride; + } + else if ( MaterialSlot.Overlay == slot ) + { + return 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 MeshSurfaceContainer.MaterialSlot GetActiveMaterialSlot() + { + if ( node.MaterialOverride != null ) + { + return MeshSurfaceContainer.MaterialSlot.Override; + } + + if ( node.Multimesh == null || node.Multimesh.Mesh == null || surfaceIndex == -1 ) + { + return MeshSurfaceContainer.MaterialSlot.None; + } + + + var material = node.Multimesh.Mesh.SurfaceGetMaterial( surfaceIndex ); + + return material == null ? MeshSurfaceContainer.MaterialSlot.None : MeshSurfaceContainer.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 Material GetMaterialInSlot( MaterialSlot slot ) + { + if ( MaterialSlot.None == slot ) + { + return null; + } + else if ( MaterialSlot.MeshSurface == slot ) + { + return node.Multimesh.Mesh.SurfaceGetMaterial( surfaceIndex ); + } + else if ( MaterialSlot.Override == slot ) + { + return node.MaterialOverride; + } + else if ( MaterialSlot.Overlay == slot ) + { + return 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() ); + } + + } + } +} diff --git a/Runtime/Procedural/Mesh/MaterialType.cs b/Runtime/Procedural/Mesh/MaterialType.cs new file mode 100644 index 0000000..e5a9be8 --- /dev/null +++ b/Runtime/Procedural/Mesh/MaterialType.cs @@ -0,0 +1,252 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + +namespace Rokojori +{ + + public class MaterialType + { + public Trillean isStandard = Trillean.Any; + public BaseMaterial3D.TransparencyEnum transparency; + public BaseMaterial3D.BlendModeEnum blendMode; + public BaseMaterial3D.CullModeEnum cullMode; + public BaseMaterial3D.DepthDrawModeEnum depthDrawMode; + public bool noDepthTest = false; + + public BaseMaterial3D.ShadingModeEnum shadingMode; + public BaseMaterial3D.DiffuseModeEnum diffuseMode; + public BaseMaterial3D.SpecularModeEnum specularMode; + public bool disableAmbientLight = false; + public bool disableFog = false; + + + public bool Equals( MaterialType materialType ) + { + if ( isStandard != materialType.isStandard ) + { + return false; + } + + if ( transparency != materialType.transparency ) + { + return false; + } + + if ( blendMode != materialType.blendMode ) + { + return false; + } + + if ( cullMode != materialType.cullMode ) + { + return false; + } + + if ( depthDrawMode != materialType.depthDrawMode ) + { + return false; + } + + if ( noDepthTest != materialType.noDepthTest ) + { + return false; + } + + + if ( shadingMode != materialType.shadingMode ) + { + return false; + } + + if ( diffuseMode != materialType.diffuseMode ) + { + return false; + } + + if ( specularMode != materialType.specularMode ) + { + return false; + } + + if ( disableAmbientLight != materialType.disableAmbientLight ) + { + return false; + } + + if ( disableFog != materialType.disableFog ) + { + return false; + } + + return true; + + } + + public bool EqualsTo( Material materialType ) + { + if ( materialType is StandardMaterial3D sm ) + { + return EqualsTo( sm ); + } + + return false; + } + + public bool EqualsTo( StandardMaterial3D materialType ) + { + if ( ! TrilleanLogic.Matches( isStandard, true ) ) + { + return false; + } + + if ( transparency != materialType.Transparency ) + { + return false; + } + + if ( blendMode != materialType.BlendMode ) + { + return false; + } + + if ( cullMode != materialType.CullMode ) + { + return false; + } + + if ( depthDrawMode != materialType.DepthDrawMode ) + { + return false; + } + + if ( noDepthTest != materialType.NoDepthTest ) + { + return false; + } + + + if ( shadingMode != materialType.ShadingMode ) + { + return false; + } + + if ( diffuseMode != materialType.DiffuseMode ) + { + return false; + } + + if ( specularMode != materialType.SpecularMode ) + { + return false; + } + + if ( disableAmbientLight != materialType.DisableAmbientLight ) + { + return false; + } + + if ( disableFog != materialType.DisableFog ) + { + return false; + } + + return true; + + } + + public void CopyFrom( MaterialType materialType ) + { + isStandard = materialType.isStandard; + + transparency = materialType.transparency; + blendMode = materialType.blendMode; + cullMode = materialType.cullMode; + depthDrawMode = materialType.depthDrawMode; + noDepthTest = materialType.noDepthTest; + + shadingMode = materialType.shadingMode; + diffuseMode = materialType.diffuseMode; + specularMode = materialType.specularMode; + disableAmbientLight = materialType.disableAmbientLight; + disableFog = materialType.disableFog; + } + + public override string ToString() + { + return JSON.StringifyObject( this ); + } + + public static MaterialType From( Material material ) + { + if ( material is StandardMaterial3D sm ) + { + return From( sm ); + } + + return null; + } + + public static MaterialType From( StandardMaterial3D standardMaterial3D ) + { + var mt = new MaterialType(); + + mt.isStandard = Trillean.True; + + // public (?:\w+\.)?\w+ (\w+).*?; + // mt.$1 = standardMaterial3D.$1; + + mt.transparency = standardMaterial3D.Transparency; + mt.blendMode = standardMaterial3D.BlendMode; + mt.cullMode = standardMaterial3D.CullMode; + mt.depthDrawMode = standardMaterial3D.DepthDrawMode; + mt.noDepthTest = standardMaterial3D.NoDepthTest; + + mt.shadingMode = standardMaterial3D.ShadingMode; + mt.diffuseMode = standardMaterial3D.DiffuseMode; + mt.specularMode = standardMaterial3D.SpecularMode; + mt.disableAmbientLight = standardMaterial3D.DisableAmbientLight; + mt.disableFog = standardMaterial3D.DisableFog; + + return mt; + } + + public static MaterialType From( MaterialType materialType ) + { + var mt = new MaterialType(); + + mt.isStandard = materialType.isStandard; + + mt.transparency = materialType.transparency; + mt.blendMode = materialType.blendMode; + mt.cullMode = materialType.cullMode; + mt.depthDrawMode = materialType.depthDrawMode; + mt.noDepthTest = materialType.noDepthTest; + + mt.shadingMode = materialType.shadingMode; + mt.diffuseMode = materialType.diffuseMode; + mt.specularMode = materialType.specularMode; + mt.disableAmbientLight = materialType.disableAmbientLight; + mt.disableFog = materialType.disableFog; + + return mt; + } + + public void ApplyTo( StandardMaterial3D standardMaterial3D ) + { + standardMaterial3D.Transparency = transparency; + standardMaterial3D.BlendMode = blendMode; + standardMaterial3D.CullMode = cullMode; + standardMaterial3D.DepthDrawMode = depthDrawMode; + standardMaterial3D.NoDepthTest = noDepthTest; + + standardMaterial3D.ShadingMode = shadingMode; + standardMaterial3D.DiffuseMode = diffuseMode; + standardMaterial3D.SpecularMode = specularMode; + standardMaterial3D.DisableAmbientLight = disableAmbientLight; + standardMaterial3D.DisableFog = disableFog; + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Mesh/MaterialType.cs.uid b/Runtime/Procedural/Mesh/MaterialType.cs.uid new file mode 100644 index 0000000..23d2f70 --- /dev/null +++ b/Runtime/Procedural/Mesh/MaterialType.cs.uid @@ -0,0 +1 @@ +uid://bsiasb06vmuw2 diff --git a/Runtime/Procedural/Mesh/MeshCombiner.cs b/Runtime/Procedural/Mesh/MeshCombiner.cs index 1ec8919..a232349 100644 --- a/Runtime/Procedural/Mesh/MeshCombiner.cs +++ b/Runtime/Procedural/Mesh/MeshCombiner.cs @@ -2,8 +2,9 @@ using System.Collections; using System.Collections.Generic; using Godot; using System; +using System.Linq; - +using System.Threading.Tasks; namespace Rokojori { @@ -36,6 +37,9 @@ namespace Rokojori [ExportGroup( "Material")] [Export] public bool combineMaterials = true; + + [Export] + public bool combineORM = true; public enum TextureSizeMode { @@ -53,6 +57,10 @@ namespace Rokojori [ExportGroup( "Output")] [Export] public MeshInstance3D outputMesh; + + [Export] + public Node3D outputContainer; + [Export] public Material[] outputMaterials = []; @@ -73,23 +81,57 @@ namespace Rokojori return _meshGeometryCache[ surface.mesh ][ surface.index ]; } - List> _surfaces; - List _materials; + List> _surfaces = new List>(); + List _materials = new List(); MapList> _materiaList = new MapList>() ; Dictionary _uvTransform = new Dictionary(); + MapList _materialTypeGroups = new MapList(); + Dictionary _combinedMaterials = new Dictionary(); - - public void Combine() + void Clear() { - GrabSurfaces(); - GrabMaterials(); - - CombineMaterials(); - CombineMeshes(); + _surfaces.Clear(); + _materials.Clear(); + _materiaList.Clear(); + _uvTransform.Clear(); + _materialTypeGroups.Clear(); + _combinedMaterials.Clear(); + + outputMesh = null; } - void GrabSurfaces() + public async Task Combine() { + Clear(); + + try + { + await GrabSurfaces(); + await GrabMaterials(); + + await CombineMaterials(); + + if ( combineMeshes ) + { + await CombineMeshes(); + } + else + { + await ReassignMaterials(); + } + + + } + catch ( System.Exception e ) + { + this.LogError( e ); + } + + return; + } + + async Task GrabSurfaces() + { _surfaces = new List>(); foreach ( var n in sourceNodes ) @@ -98,7 +140,7 @@ namespace Rokojori } } - void GrabMaterials() + async Task GrabMaterials() { _materials = new List(); _materiaList = new MapList>(); @@ -124,18 +166,229 @@ namespace Rokojori } - void CombineMaterials() + async Task CombineMaterials() { + _materials.ForEach( + m => + { + var type = _materialTypeGroups.FindKey( k => k.EqualsTo( m ) ); + + if ( type == null ) + { + type = MaterialType.From( m ); + } + + if ( type == null ) + { + return; + } + + _materialTypeGroups.Add( type, m ); + } + ); + + this.LogInfo( "Found Groups:", _materialTypeGroups.Count ); + + foreach ( var gm in _materialTypeGroups ) + { + this.LogInfo( "Group:", gm.Key, ">>", gm.Value.Count ); + + if ( gm.Value.Count > 1 ) + { + await CombineStandardMaterials( gm.Value ); + } + + } } - void CombineMeshes() + async Task CombineStandardMaterials( List anyMaterials ) + { + var materials = Lists.FilterType( anyMaterials ); + + var outputMaterial = (StandardMaterial3D) materials[ 0 ].Duplicate(); + + var alignment = TextureMerger.ComputeTextureAlignment( anyMaterials.Count ); + + for ( int i = 0; i < materials.Count; i++ ) + { + var box = TextureMerger.GetUVAlignmentBoxFor( alignment, i ); + var transform = Transform2D.Identity; + transform = transform.ScaledLocal( box.size ); + transform.Origin = box.min; + + _uvTransform[ materials[ i ] ] = transform; + _combinedMaterials[ materials[ i ] ] = outputMaterial; + + this.LogInfo( "Set UV transform:", i, box.min, box.size.Inverse() ); + } + + var textureTypes = new List + { + "albedo", + "metallic", + "roughness", + "emission", + "normal", + "rim", + "clearcoat", + "anisotropy_flowmap", + "ao", + "height", + "subsurf_scatter", + "backlight", + "refraction" + }; + + var hasAlebdoAlpha = true; + + var mm = new StandardMaterial3D(); + + foreach ( var t in textureTypes ) + { + if ( combineORM && ( t == "roughness" || t == "metallic" ) ) + { + continue; + } + + var hasAlpha = hasAlebdoAlpha && t == "albedo"; + + var name = Texture2DPropertyName.Create( t + "_texture" ); + var textures = Lists.Map( materials, m => name.Get( m ) ); + + var noTextures = textures.Find( t => t != null ) == null; + + if ( noTextures ) + { + continue; + } + + var fallBackColors = Lists.Map( + textures, i => + { + if ( t == "ao" && combineORM ) + { + return new Color( 1, 1, 0 ); + } + + if ( t == "albedo" ) + { + return new Color( 1, 1, 1 ); + } + + if ( t == "normal" ) + { + return new Color( 0.5f, 0.5f, 1 ); + } + + return new Color( 0, 0, 0 ); + } + ); + + + + var textureSize = new Vector2( 512, 512 ); + + if ( TextureSizeMode.Custom == textureSizeMode ) + { + textureSize = customTextureSize; + } + else if ( TextureSizeMode.KeepOriginal == textureSizeMode ) + { + textures.ForEach( + t => + { + if ( t == null ) + { + return; + } + + textureSize.X = Mathf.Max( t.GetWidth(),textureSize.X ); + textureSize.Y = Mathf.Max( t.GetWidth(),textureSize.Y ); + } + ); + + this.LogInfo( "Computing next power of two texture Size", textureSize, alignment ); + + textureSize.X = MathX.NextPowerOfTwo( textureSize.X * alignment.X ); + textureSize.Y = MathX.NextPowerOfTwo( textureSize.Y * alignment.Y ); + } + + this.LogInfo( "Texture Size", t, ">>", textures.Count, textureSize, alignment ); + + + var texture = TextureCombinerBuffer.GridMerge( + (int) textureSize.X, (int)textureSize.Y, + new Color( 1, 0, 0, hasAlpha ? 0 : 1 ), + hasAlpha, true, alignment, + textures, fallBackColors + ); + + this.LogInfo( "Combined textures:", t ); + + name.Set( outputMaterial, texture ); + + + await this.RequestNextFrame(); + + } + + + return outputMaterial; + } + + async Task ReassignMaterials() + { + if ( outputContainer == null ) + { + outputContainer = this.CreateChild(); + } + + outputContainer.DestroyChildren(); + + foreach ( var n in sourceNodes ) + { + var root = n.DeepCopyTo( outputContainer ); + var materialContainers = MeshExtractor.ExtractMaterialContainersInHierarchy( root, null, true ); + + materialContainers.ForEach( + mc => + { + mc.MakeUnique( false ); + mc.ForAllSurfaces( + ( surface )=> + { + var slot = surface.GetActiveMaterialSlot(); + + if ( MaterialSurfaceContainer.MaterialSlot.None == slot ) + { + return; + } + + var material = surface.GetActiveMaterial(); + + if ( _combinedMaterials.ContainsKey( material ) ) + { + surface.SetActiveMaterial( _combinedMaterials[ material ] ); + } + } + ); + } + ); + } + } + + async Task CombineMeshes() { var arrayMesh = new ArrayMesh(); this.LogInfo( "Combining", _surfaces.Count, "meshes" ); var index = 0; + + var combined = new MapList(); + + var outputMaterials = new List(); _materials.ForEach( ( m )=> @@ -155,6 +408,7 @@ namespace Rokojori if ( uvTransform != null ) { + this.LogInfo( "Appling uvTransform:", uvTransform ); smg.ApplyUVTransform( (Transform2D) uvTransform ); } @@ -171,22 +425,61 @@ namespace Rokojori } ); + var isCombinedMaterial = _combinedMaterials.ContainsKey( m ); + + if ( isCombinedMaterial ) + { + this.LogInfo( "Add material groups", m ); + combined.Add( _combinedMaterials[ m ], meshGeometry ); + return; + } + meshGeometry.GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh ); arrayMesh.SurfaceSetMaterial( index, m ); + outputMaterials.Add( m ); index ++; } ); + this.LogInfo( "Combining material groups", combined.Count ); + + foreach ( var cm in combined ) + { + var material = cm.Key; + var meshes = cm.Value; + this.LogInfo( "Combining meshes", meshes.Count); + var combinedMG = MeshGeometry.Combine( meshes ); + + this.LogInfo( "Combed meshes, num tris:", combinedMG.numTriangles ); + combinedMG.GenerateMesh( Mesh.PrimitiveType.Triangles, arrayMesh ); + + this.LogInfo( "Add surface", index); + arrayMesh.SurfaceSetMaterial( index, material ); + + this.LogInfo( "Add material", material ); + outputMaterials.Add( material ); + + this.LogInfo( "Increment index" ); + index ++; + } + + this.LogInfo( "Processing done, adding outputs", arrayMesh.GetSurfaceCount() ); + if ( outputMesh == null ) { + this.LogInfo( "Created outputMesh"); outputMesh = this.CreateChild(); } - outputMesh.Mesh = arrayMesh; - outputMaterials = _materials.ToArray(); + + this.outputMaterials = outputMaterials.ToArray(); + + outputMesh.Mesh = arrayMesh; + + return; } diff --git a/Runtime/Procedural/Mesh/MeshExtractor.cs b/Runtime/Procedural/Mesh/MeshExtractor.cs index b13f140..4e45274 100644 --- a/Runtime/Procedural/Mesh/MeshExtractor.cs +++ b/Runtime/Procedural/Mesh/MeshExtractor.cs @@ -9,6 +9,62 @@ namespace Rokojori { public class MeshExtractor { + public static List ExtractMaterialContainersInHierarchy( Node n, List list = null, bool ownersOnly = false ) + { + list = list == null ? new List() : list; + + Nodes.ForEach( n, + + n => + { + ExtractMaterialContainers( n, list, ownersOnly ); + } + ); + + return list; + } + + + public static List ExtractMaterialContainers( Node n, List list = null, bool ownersOnly = false ) + { + list = list == null ? new List() : list; + + + if ( n is MeshInstance3D mi && mi.Mesh != null ) + { + if ( ! ownersOnly ) + { + for ( int i = 0; i < mi.Mesh.GetSurfaceCount(); i++ ) + { + list.Add( new MeshSurfaceContainer( mi, i ) ); + } + } + else + { + list.Add( new MeshSurfaceContainer( mi, -1 ) ); + + } + + } + + if ( n is MultiMeshInstance3D mmi && mmi.Multimesh != null && mmi.Multimesh.Mesh != null ) + { + if ( ! ownersOnly ) + { + for ( int i = 0; i < mmi.Multimesh.Mesh.GetSurfaceCount(); i++ ) + { + list.Add( new MultiMeshSurfaceContainer( mmi, i ) ); + } + } + else + { + list.Add( new MultiMeshSurfaceContainer( mmi, -1 ) ); + } + } + + return list; + } + public static bool CanExtract( Node3D n ) { if ( n is MeshInstance3D mi ) @@ -102,18 +158,20 @@ namespace Rokojori return list; } - public static List> ExtractSurfaces( Node n, List> list = null ) + public static List> ExtractSurfaces( Node n, List> list = null, bool trackOwners = false ) { list = list == null ? new List>() : list; if ( n is MeshInstance3D mi ) - { + { + var owner = trackOwners ? mi : null; var mesh = mi.Mesh; + for ( int i = 0; i < mesh.GetSurfaceCount(); i++ ) { - var surface = new MeshSurface( mesh, mesh.SurfaceGetMaterial( i ), i ); + var surface = new MeshSurface( mesh, mesh.SurfaceGetMaterial( i ), i, owner ); if ( i < mi.GetSurfaceOverrideMaterialCount() && mi.GetSurfaceOverrideMaterial( i ) != null ) { @@ -131,6 +189,7 @@ namespace Rokojori if ( n is MultiMeshInstance3D mmi ) { + var owner = trackOwners ? mmi : null; var mm = mmi.Multimesh; for ( var j = 0; j < mm.InstanceCount; j++ ) @@ -140,7 +199,7 @@ namespace Rokojori for ( int i = 0; i < mesh.GetSurfaceCount(); i++ ) { - var surface = new MeshSurface( mesh, mesh.SurfaceGetMaterial( i ), i ); + var surface = new MeshSurface( mesh, mesh.SurfaceGetMaterial( i ), i, mmi, j ); if ( mmi.MaterialOverride != null ) { diff --git a/Runtime/Procedural/Mesh/MeshGeometry.cs b/Runtime/Procedural/Mesh/MeshGeometry.cs index 3981374..bb0259a 100644 --- a/Runtime/Procedural/Mesh/MeshGeometry.cs +++ b/Runtime/Procedural/Mesh/MeshGeometry.cs @@ -437,7 +437,14 @@ namespace Rokojori { this.name = name; Initialize( normals, uvs, colors, uvs2 ); - } + } + + public static MeshGeometry Combine( List meshes ) + { + var mg = new MeshGeometry(); + meshes.ForEach( m => mg.Add( m ) ); + return mg; + } public static MeshGeometry BillboardQuad( float size = 1 ) { diff --git a/Runtime/Procedural/Mesh/MeshSurface.cs b/Runtime/Procedural/Mesh/MeshSurface.cs index 099f3ee..9f196a6 100644 --- a/Runtime/Procedural/Mesh/MeshSurface.cs +++ b/Runtime/Procedural/Mesh/MeshSurface.cs @@ -12,12 +12,16 @@ namespace Rokojori public Mesh mesh; public Material material; public int index; + public Node owner; + public int instanceIndex; - public MeshSurface( Mesh mesh, Material material, int index = 0 ) + public MeshSurface( Mesh mesh, Material material, int index, Node owner = null, int instanceIndex = -1 ) { this.index = index; this.mesh = mesh; this.material = material; + this.owner = owner; + this.instanceIndex = instanceIndex; } } } diff --git a/Runtime/Procedural/Parametric/Plane/ClipMapPlaneMeshType.cs b/Runtime/Procedural/Parametric/Plane/ClipMapPlaneMeshType.cs index 17a5dff..3b7ab62 100644 --- a/Runtime/Procedural/Parametric/Plane/ClipMapPlaneMeshType.cs +++ b/Runtime/Procedural/Parametric/Plane/ClipMapPlaneMeshType.cs @@ -145,7 +145,7 @@ namespace Rokojori return divisions; } - var v = MathX.NextPowerOfTwo( divisions ); + var v = MathX.NextPowerOfTwoExponent( divisions ); return v * v; } diff --git a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBuffer.cs b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBuffer.cs index 2cdd8fc..2b6455c 100644 --- a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBuffer.cs +++ b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBuffer.cs @@ -37,6 +37,22 @@ namespace Rokojori } } + public void Fill( Color color, int x, int y, int w, int h ) + { + for ( int i = 0; i < w; i++ ) + { + for ( int j = 0; j < h; j++ ) + { + SetAt( i + x, j + y, color ); + } + } + } + + public void Fill( Color color ) + { + Fill( color, 0, 0, width, height ); + } + public static TextureCombinerBuffer From( Texture2D texture, int srgbConversion = 0 ) { var buffer = Create( texture.GetWidth(), texture.GetHeight() ); @@ -45,6 +61,79 @@ namespace Rokojori return buffer; } + public void CopyFromSampled( TextureCombinerBuffer source, int x, int y, int w, int h ) + { + RJLog.Log( "Copy Sampled", x,y, w, h ); + + for ( int i = 0; i < w; i ++ ) + { + for ( int j = 0; j < h; j++ ) + { + float u = i / (float)( w ); + float v = j / (float)( h ); + + var sourceX = Mathf.RoundToInt( u * source.width ); + var sourceY = Mathf.RoundToInt( v * source.height ); + + var color = source.GetAt( sourceX, sourceY ); + SetAt( x + i, y + j, color ); + } + } + } + + public static Texture2D GridMerge( int w, int h, Color background, bool alpha, bool mipMaps, + Vector2I alignment, + List textures, List nullTextureColors ) + { + + var positions = new List(); + var sizes = new List(); + + var mergeSize = new Vector2( w, h ); + var gridSize = (Vector2I)( mergeSize / alignment ); + + RJLog.Log( "GridMerge" ); + + for ( int i = 0; i < textures.Count; i++ ) + { + var box = TextureMerger.GetUVAlignmentBoxFor( alignment, i ); + var position = (Vector2I) ( box.min * mergeSize ); + positions.Add( position ); + sizes.Add( gridSize ); + + RJLog.Log( i, position, gridSize ); + } + + + + return Merge( w, h, background, alpha, mipMaps, textures, nullTextureColors, positions, sizes ); + } + + public static Texture2D Merge( int w, int h, Color background, bool alpha, bool mipMaps, + List textures, List nullTextureColors, List positions, List sizes ) + { + var buffer = Create( w, h ); + + buffer.Fill( background ); + + for ( int i = 0; i < textures.Count; i++ ) + { + var position = positions[ i ]; + var size = sizes[ i ]; + + if ( textures[ i ] == null ) + { + buffer.Fill( nullTextureColors[ i ], position.X, position.Y, size.X, size.Y ); + continue; + } + + var imageBuffer = From( textures[ i ] ); + buffer.CopyFromSampled( imageBuffer, position.X, position.Y, size.X, size.Y ); + } + + return buffer.CreateImageTexture( alpha, mipMaps ); + } + public ImageTexture CreateImageTexture( bool alpha = true, bool generateMipmaps = false ) { var image = Image.CreateEmpty( width, height, generateMipmaps, alpha ? Image.Format.Rgba8 : Image.Format.Rgb8); @@ -75,7 +164,7 @@ namespace Rokojori return buffer; - } + } public void CopyFrom( Image image, int sourceX, int sourceY, int ownX, int ownY, int w, int h, int srgbConversion = 0 ) { diff --git a/Runtime/Rendering/Context/RDContext.CleanUp.cs b/Runtime/Rendering/Context/RDContext.CleanUp.cs index 4f53932..c19f347 100644 --- a/Runtime/Rendering/Context/RDContext.CleanUp.cs +++ b/Runtime/Rendering/Context/RDContext.CleanUp.cs @@ -1,15 +1,18 @@ using Godot; using System.Collections.Generic; +using System.Text; namespace Rokojori { public partial class RDContext { - protected List _cleanUps = new List(); + protected List _cleanUps = new List(); protected List _cleanUpInfo = new List(); - public void Free( RenderingObject ro, string info = null ) + protected bool cleanAll = false; + + public void Free( RDObject ro, string info = null ) { if ( ro == null ) { @@ -23,27 +26,54 @@ namespace Rokojori return; } - if ( ro is RDPipeline ) + if ( ! cleanAll && ro is RDPipeline ) { Verbose( "Not cleaning pipelines" ); return; } - if ( ro is RDUniformSet ) + if ( ! cleanAll && ro is RDUniformSet ) { Verbose( "Not cleaning uniform sets" ); return; } Verbose( "Cleaning up: ", info, ro.rid ); - renderingDevice.FreeRid( ro.rid ); + ro.Free( this ); } + public string GetMemoryInfo() + { + var types = new List(){ + RenderingDevice.MemoryType.Buffers, + RenderingDevice.MemoryType.Textures, + RenderingDevice.MemoryType.Total + }; + + var sb = new StringBuilder(); + + types.ForEach( + ( t ) => + { + var size = renderingDevice.GetMemoryUsage( t ); + var sizeInfo = Bytes.ConvertToString( size ); + sb.Append( t + ": " + sizeInfo + "(" + size + " bytes) " ); + } + ); + + // Verbose( "Cleaning up:", renderingDevice.GetMemoryUsage() ); + + return sb.ToString(); + } public void CleanUp() { var index = 0; + Verbose( "Memory before cleanup"); + + Verbose( GetMemoryInfo() ); + _cleanUps.ForEach( c => { @@ -52,10 +82,26 @@ namespace Rokojori } ); + Verbose( "Memory after cleanup" ); + Verbose( GetMemoryInfo() ); + _cleanUps.Clear(); + _cleanUpInfo.Clear(); } - public void AddToCleanUp( RenderingObject ro, string info = null ) + public void Free() + { + renderingDevice.Free(); + _renderingDevice = null; + } + + public void CleanUpAndFree() + { + CleanUp(); + Free(); + } + + public void AddToCleanUp( RDObject ro, string info = null ) { if ( _cleanUps.Contains( ro ) ) { @@ -66,7 +112,7 @@ namespace Rokojori _cleanUpInfo.Add( info ); } - public void AddToCleanUp( List ro, string info = null ) + public void AddToCleanUp( List ro, string info = null ) { var index = 0; info = info == null ? "" : info; diff --git a/Runtime/Rendering/Context/RDContext.cs b/Runtime/Rendering/Context/RDContext.cs index a762d88..7182f84 100644 --- a/Runtime/Rendering/Context/RDContext.cs +++ b/Runtime/Rendering/Context/RDContext.cs @@ -26,6 +26,13 @@ namespace Rokojori return ctx; } + public static RDContext Current() + { + var ctx = new RDContext(); + ctx.Initialize( false ); + return ctx; + } + public void Initialize( bool local = false) { _localRenderingDevice = local; diff --git a/Runtime/Rendering/Objects/RDObject.cs b/Runtime/Rendering/Objects/RDObject.cs new file mode 100644 index 0000000..d8d6ac1 --- /dev/null +++ b/Runtime/Rendering/Objects/RDObject.cs @@ -0,0 +1,36 @@ + +using Godot; + +namespace Rokojori +{ + public class RDObject + { + protected RDContext _context; + public RDContext context => _context; + + protected Rid _rid; + public Rid rid => _rid; + + protected bool _isDestroyed = false; + public bool valid => ! _isDestroyed && _context != null && _rid.IsValid; + + public RDObject( RDContext context, Rid rid ) + { + _context = context; + _rid = rid; + + _context.AddToCleanUp( this, GetType().Name + "@" + _rid ); + + context.Verbose( "Creating", GetType().Name, rid ); + } + + public void Free( RDContext context ) + { + context.renderingDevice.FreeRid( _rid ); + _rid = new Rid(); + _context = null; + _isDestroyed = true; + } + + } +} \ No newline at end of file diff --git a/Runtime/Rendering/Objects/RDObject.cs.uid b/Runtime/Rendering/Objects/RDObject.cs.uid new file mode 100644 index 0000000..3847490 --- /dev/null +++ b/Runtime/Rendering/Objects/RDObject.cs.uid @@ -0,0 +1 @@ +uid://0oqflnt3k5lr diff --git a/Runtime/Rendering/Objects/RenderingObject.cs.uid b/Runtime/Rendering/Objects/RDObject.uid similarity index 100% rename from Runtime/Rendering/Objects/RenderingObject.cs.uid rename to Runtime/Rendering/Objects/RDObject.uid diff --git a/Runtime/Rendering/Objects/RDPipeline.cs b/Runtime/Rendering/Objects/RDPipeline.cs index 307fabe..1f0565b 100644 --- a/Runtime/Rendering/Objects/RDPipeline.cs +++ b/Runtime/Rendering/Objects/RDPipeline.cs @@ -3,7 +3,7 @@ using Godot; namespace Rokojori { - public class RDPipeline: RenderingObject + public class RDPipeline: RDObject { public RDPipeline( RDContext context, Rid rid ):base( context, rid ) {} diff --git a/Runtime/Rendering/Objects/RDSampler.cs b/Runtime/Rendering/Objects/RDSampler.cs index 6ba9751..58776a6 100644 --- a/Runtime/Rendering/Objects/RDSampler.cs +++ b/Runtime/Rendering/Objects/RDSampler.cs @@ -3,7 +3,7 @@ using Godot; namespace Rokojori { - public class RDSampler: RenderingObject + public class RDSampler: RDObject { public RDSampler( RDContext effect, Rid rid ):base( effect, rid ) {} diff --git a/Runtime/Rendering/Objects/RDShader.cs b/Runtime/Rendering/Objects/RDShader.cs index ac9e600..261725d 100644 --- a/Runtime/Rendering/Objects/RDShader.cs +++ b/Runtime/Rendering/Objects/RDShader.cs @@ -3,7 +3,7 @@ using Godot; namespace Rokojori { - public class RDShader: RenderingObject + public class RDShader: RDObject { public RDShader( RDContext context, Rid rid ):base( context, rid ) {} diff --git a/Runtime/Rendering/Objects/RDTexture.cs b/Runtime/Rendering/Objects/RDTexture.cs index 9c291c1..e5fd1fe 100644 --- a/Runtime/Rendering/Objects/RDTexture.cs +++ b/Runtime/Rendering/Objects/RDTexture.cs @@ -3,7 +3,7 @@ using Godot; namespace Rokojori { - public class RDTexture: RenderingObject + public class RDTexture: RDObject { public RDTexture( RDContext context, Rid rid ):base( context, rid ){} diff --git a/Runtime/Rendering/Objects/RDTextureFormats.cs b/Runtime/Rendering/Objects/RDTextureFormats.cs index bc2226c..1be47d6 100644 --- a/Runtime/Rendering/Objects/RDTextureFormats.cs +++ b/Runtime/Rendering/Objects/RDTextureFormats.cs @@ -187,7 +187,7 @@ namespace Rokojori public static RenderingDevice.DataFormat ImageFormatToDataFormat( Image.Format imageFormat ) { - RJLog.Log( "ImageFormatToDataFormat", imageFormat ); + // RJLog.Log( "ImageFormatToDataFormat", imageFormat ); switch ( imageFormat ) { diff --git a/Runtime/Rendering/Objects/RDUniformSet.cs b/Runtime/Rendering/Objects/RDUniformSet.cs index 8305132..1691b16 100644 --- a/Runtime/Rendering/Objects/RDUniformSet.cs +++ b/Runtime/Rendering/Objects/RDUniformSet.cs @@ -4,7 +4,7 @@ using Godot.Collections; namespace Rokojori { - public class RDUniformSet:RenderingObject + public class RDUniformSet:RDObject { protected int _setIndex = -1; public int setIndex => _setIndex; diff --git a/Runtime/Rendering/Objects/RenderingObject.cs b/Runtime/Rendering/Objects/RenderingObject.cs deleted file mode 100644 index ae0b137..0000000 --- a/Runtime/Rendering/Objects/RenderingObject.cs +++ /dev/null @@ -1,27 +0,0 @@ - -using Godot; - -namespace Rokojori -{ - public class RenderingObject - { - protected RDContext _context; - public RDContext context => _context; - - protected Rid _rid; - public Rid rid => _rid; - - public bool valid => _rid.IsValid; - - public RenderingObject( RDContext context, Rid rid ) - { - this._context = context; - this._rid = rid; - - this._context.AddToCleanUp( this, GetType().Name + "@" + _rid ); - - context.Verbose( "Creating", GetType().Name, rid ); - } - - } -} \ No newline at end of file diff --git a/Runtime/Shading/Library/Transform.gdshaderinc b/Runtime/Shading/Library/Transform.gdshaderinc index 0d5f1f0..079ad19 100644 --- a/Runtime/Shading/Library/Transform.gdshaderinc +++ b/Runtime/Shading/Library/Transform.gdshaderinc @@ -6,13 +6,7 @@ vec3 applyMatrix( vec3 v, mat4 m ) vec3 applyMatrixWithoutTranslation( vec3 v, mat4 m ) { - mat4 mw = m; - mw[ 3 ][ 0 ] = 0.0; - mw[ 3 ][ 1 ] = 0.0; - mw[ 3 ][ 2 ] = 0.0; - mw[ 3 ][ 3 ] = 1.0; - - return ( mw * vec4( v, 1.0 ) ).xyz; + return ( m * vec4( v, 0.0 ) ).xyz; } vec3 applyMatrix( vec3 v, mat3 m ) @@ -20,35 +14,20 @@ vec3 applyMatrix( vec3 v, mat3 m ) return ( m * v ).xyz; } -// L W V -// L -// W -// V - -// LD WD VD -// LD -// WD -// VD vec3 localToWorld( vec3 local, mat4 _MODEL_MATRIX ) { - return ( _MODEL_MATRIX * vec4( local, 1.0 ) ).xyz; + return applyMatrix( local, _MODEL_MATRIX ); } vec3 localToWorldDirection( vec3 local, mat4 _MODEL_MATRIX ) { - mat4 mw = _MODEL_MATRIX; - mw[ 3 ][ 0 ] = 0.0; - mw[ 3 ][ 1 ] = 0.0; - mw[ 3 ][ 2 ] = 0.0; - mw[ 3 ][ 3 ] = 1.0; - - return ( mw * vec4( local, 1.0 ) ).xyz; + return applyMatrixWithoutTranslation( local, _MODEL_MATRIX ); } vec3 localToView( vec3 local, mat4 _MODELVIEW_MATRIX ) { - return ( _MODELVIEW_MATRIX * vec4( local, 1.0 ) ).xyz; + return applyMatrix( local, _MODELVIEW_MATRIX ); } vec3 localToViewDirection( vec3 local, mat4 _MODELVIEW_MATRIX ) @@ -63,35 +42,25 @@ vec3 localToViewDirection( vec3 local, mat3 _MODELVIEW_NORMAL_MATRIX ) vec3 worldToLocal( vec3 world, mat4 _MODEL_MATRIX ) { - return ( inverse( _MODEL_MATRIX ) * vec4( world, 1.0 ) ).xyz; + mat4 inversedMatrix = inverse( _MODEL_MATRIX ); + return applyMatrix( world, inv ); } vec3 worldToLocalDirection( vec3 world, mat4 _MODEL_MATRIX ) { - mat4 mw = inverse( _MODEL_MATRIX ); - mw[ 3 ][ 0 ] = 0.0; - mw[ 3 ][ 1 ] = 0.0; - mw[ 3 ][ 2 ] = 0.0; - mw[ 3 ][ 3 ] = 1.0; - - return ( mw * vec4( world, 1.0 ) ).xyz; + mat4 inversedMatrix = inverse( _MODEL_MATRIX ); + return applyMatrixWithoutTranslation( world, inversedMatrix ); } vec3 worldToView( vec3 world, mat4 _VIEW_MATRIX ) -{ - return ( _VIEW_MATRIX * vec4( world, 1.0 ) ).xyz; +{ + return applyMatrix( world, _VIEW_MATRIX ); } vec3 worldToViewDirection( vec3 worldDirection, mat4 _VIEW_MATRIX ) { - mat4 mw = _VIEW_MATRIX; - mw[ 3 ][ 0 ] = 0.0; - mw[ 3 ][ 1 ] = 0.0; - mw[ 3 ][ 2 ] = 0.0; - mw[ 3 ][ 3 ] = 1.0; - - return ( mw * vec4( worldDirection, 1.0 ) ).xyz; + return applyMatrixWithoutTranslation( worldDirection, _VIEW_MATRIX ); } vec3 viewToWorld( vec3 view, mat4 _INV_VIEW_MATRIX ) @@ -101,14 +70,7 @@ vec3 viewToWorld( vec3 view, mat4 _INV_VIEW_MATRIX ) vec3 viewToWorldDirection( vec3 viewDirection, mat4 _INV_VIEW_MATRIX ) { - mat4 mw = _INV_VIEW_MATRIX; - mw[ 3 ][ 0 ] = 0.0; - mw[ 3 ][ 1 ] = 0.0; - mw[ 3 ][ 2 ] = 0.0; - mw[ 3 ][ 3 ] = 1.0; - - - return applyMatrix( viewDirection, mw ); + return applyMatrixWithoutTranslation( viewDirection, _INV_VIEW_MATRIX ); } vec3 viewToLocal( vec3 view, mat4 _INV_VIEW_MATRIX, mat4 _MODEL_MATRIX ) diff --git a/Runtime/Shading/Properties/ShaderPropertyName.cs b/Runtime/Shading/Properties/ShaderPropertyName.cs index ad47475..db71f32 100644 --- a/Runtime/Shading/Properties/ShaderPropertyName.cs +++ b/Runtime/Shading/Properties/ShaderPropertyName.cs @@ -63,7 +63,7 @@ namespace Rokojori var value = p.GetValue( material ); - RJLog.Log( "GetStandardValue", _cSharpName, p.Name, ">>", value ); + // RJLog.Log( "GetStandardValue", _cSharpName, p.Name, ">>", value ); return (T) p.GetValue( material ); } @@ -82,6 +82,12 @@ namespace Rokojori if ( ! RegexUtility.StartsWithUpperCase( propertyName ) ) { _cSharpName = GDScriptNames.ToCS( propertyName ); + + if ( propertyName.StartsWith( "ao" ) ) + { + _cSharpName = _cSharpName[ 0 ] + ( _cSharpName[ 1 ] + "" ).ToUpper() + _cSharpName.Substring( 2 ); + } + } else { @@ -89,7 +95,7 @@ namespace Rokojori } var p = type.GetProperty( _cSharpName ); - RJLog.Log( "_cSharpName", _cSharpName, material ); + // RJLog.Log( "_cSharpName", _cSharpName, material ); _propertyInfoCache.Set( type, _cSharpName, p ); return _propertyInfoCache[ type ][ _cSharpName ]; diff --git a/Runtime/Structures/Map.cs b/Runtime/Structures/Map.cs index 680b184..f661e2d 100644 --- a/Runtime/Structures/Map.cs +++ b/Runtime/Structures/Map.cs @@ -17,6 +17,21 @@ namespace Rokojori callback( kv.Key, kv.Value ); } } + + public K FindKey( Func evaluater ) + { + foreach ( var kv in this ) + { + if ( evaluater( kv.Key ) ) + { + return kv.Key; + } + } + + return default( K ); + + } + } public class StringMap : Map diff --git a/Runtime/Text/JSON/Serializers/JSONSerializer.cs b/Runtime/Text/JSON/Serializers/JSONSerializer.cs index 9d8362b..c8497f7 100644 --- a/Runtime/Text/JSON/Serializers/JSONSerializer.cs +++ b/Runtime/Text/JSON/Serializers/JSONSerializer.cs @@ -295,7 +295,17 @@ namespace Rokojori } else if ( value is Enum ) { - jsonObject.Set( name, (int) value ); + var size = ReflectionHelper.GetEnumSizeInBits( value.GetType() ); + + if ( size == 32 ) + { + jsonObject.Set( name, (int) value ); + } + else if ( size == 64 ) + { + jsonObject.Set( name, (long) value ); + } + } else if ( ReflectionHelper.IsList( value ) ) { diff --git a/Runtime/Tools/ReflectionHelper.cs b/Runtime/Tools/ReflectionHelper.cs index bbeadc5..775a7b6 100644 --- a/Runtime/Tools/ReflectionHelper.cs +++ b/Runtime/Tools/ReflectionHelper.cs @@ -8,6 +8,35 @@ namespace Rokojori { public class ReflectionHelper { + public static T Create( Type type, params object[] args ) + { + Type[] argTypes = Array.ConvertAll( args, arg => arg.GetType() ); + + var constructorMethod = type.GetConstructor( + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, argTypes, null + ); + + if ( constructorMethod == null ) + { + return default(T); + } + + return (T)constructorMethod.Invoke( args ); + + } + + public static int GetEnumSizeInBits( Type enumType ) + { + if ( ! enumType.IsEnum ) + { + return -1; + } + + var underlyingType = Enum.GetUnderlyingType( enumType ); + return System.Runtime.InteropServices.Marshal.SizeOf( underlyingType ) * 8; + } + public static Type GetTypeByNameFromAssembly( string name, Assembly assembly ) { var type = assembly.GetType( name );