using System.Collections; using System.Collections.Generic; using Godot; using System; using System.Threading.Tasks; namespace Rokojori.PointClouds { [Tool] [GlobalClass] public partial class PointCloudGenerator:Node3D { [Export] public MeshInstance3D mesh; [Export] public PointCloudSampler.SampleMode sampleMode = PointCloudSampler.SampleMode.Center; [Export] public float samplingPixelDensity = 0.01f; [Export] public float outputPixelDensity = 0.5f; [Export] public int numPoints = 0; [Export] public int squareTexture; [Export] public Material material; [Export] public bool createOutputs = false; [Export] public MeshInstance3D[] outputs; [ExportGroup("Lerping")] [Export] public Curve lowerLerp; [Export] public Curve higherLerp; [Export] public int lerpSteps; [ExportGroup("")] [Export] public LODNode lod; [Export] bool working = false; [Export] public string[] outputInfos = []; [ExportToolButton( "Generate")] public Callable GenerateButton => Callable.From( () => { GeneratePointCloud(); } ); public async void GeneratePointCloud() { if ( working ) { return; } working = true; numPoints = -1; this.LogInfo( "Generating Point Cloud" ); var sampler = new PointCloudSampler(); if ( samplingPixelDensity > 0 ) { sampler.densityResolution = samplingPixelDensity; } sampler.materials = Materials.GetAll( mesh ); sampler.albedoTexture = sampler.materials.Map( m => Texture2DPropertyName.albedoTexture ); sampler.albedo = sampler.materials.Map( m => ColorPropertyName.albedo ); ; var cloud = await sampler.SampleFromMesh( sampleMode, (ArrayMesh) mesh.Mesh, this ); this.LogInfo( "Sampled mesh" ); var time = Async.GetTimeMS(); // cloud.ComputeBoundingBox(); time = Async.PrintAndUpdateMS( time ); var longestCM = cloud.boundingBox.size.MaxDimension() * 100f; var nextP2CM = MathX.NextPowerOfTwo( longestCM ); var rootSize = nextP2CM / 100f; var ocMin = cloud.boundingBox.center - Vector3.One * rootSize; var ocMax = cloud.boundingBox.center + Vector3.One * rootSize; var depth = Mathf.CeilToInt( MathX.Exponent( 2, rootSize/ samplingPixelDensity ) ); var ocTree = new PointCloudOcTree( ocMin, ocMax, rootSize, depth ); var minSize = ocTree.GetSizeForLevel( depth ); this.LogInfo( "OcTree MinDepth", minSize._CM() ); this.RequestNextFrame(); time = Async.PrintAndUpdateMS( time, "Inserting to octree" ); ocTree.Insert( cloud.points ); time = Async.PrintAndUpdateMS( time, "Inserting to octree Done" ); var level = ocTree.GetLevelForSize( outputPixelDensity ); this.LogInfo( "Combining" ); this.RequestNextFrame(); time = Async.PrintAndUpdateMS( time, "Combining" ); ocTree.normalSeperation = false; ocTree.CombineUntilLevel( level ); time = Async.PrintAndUpdateMS( time, "Combining Done" ); this.LogInfo( "Combining level", level, "Max Level", depth ); this.LogInfo( "Cloud Points:", cloud.points.Count ); if ( outputs != null ) { outputs = []; } this.DestroyChildren(); var levelMap = ocTree.GetLevelMap(); var levelInstances = new List(); var lodBuilder = LODBuilder.ByCameraDistance(); lodBuilder.SetMaterial( material ); // lodBuilder.Add( 0, mesh.Mesh, mesh.GetSurfaceOverrideMaterial( 0 ) ); var meshGeometries = new List(); var infos = new List(); for ( int i = level + 1; i < depth; i++ ) { var filledLevels = levelMap[ i ].Filter( l => l.values != null ); var points = new List(); filledLevels.ForEach( l => { points.AddRange( l.values ); } ); var pc = new PointCloud(); pc.points = points; pc.ComputeCenter(); pc.SortByDistanceToCenter(); var levelSize = ocTree.GetSizeForLevel( i ); var distance = Cameras.ComputePixelDistanceForSizeVertical( 60, levelSize, new Vector2( 1920, 1080 ) ) * 4; // this.LogInfo( // "Level", i, // "Points", pc.points.Count, // "Size", RegexUtility._CM( levelSize ), // "Distance", RegexUtility._M( distance ) // ); var mg = pc.GenerateMeshGeometry( levelSize/2f ); meshGeometries.Add( mg ); infos.Add( RJLog.Stringify( "Level", i, "Points", pc.points.Count, "Size", levelSize._CM(), "Vertices", mg.numVertices, "Normals", mg.numNormals, "UVs", mg.numUV2s, "Colors", mg.numColors ) ); var t = MathX.Map( i, level + 1, depth - 1, 1, 0 ); mg.lodEdgeLength = levelSize; // mg.ApplyTranslation( new Vector3( 100 * t, 0, 0 ) ); var mesh = pc.GenerateMesh( levelSize / 2f ); lodBuilder.Add( levelSize, mesh ); if ( createOutputs ) { var meshInstance = this.CreateChild(); meshInstance.Mesh = mesh; meshInstance.MaterialOverride = material; levelInstances.Add( meshInstance ); } } if ( createOutputs ) { outputs = levelInstances.ToArray(); } outputInfos = infos.ToArray(); var lodMesh = this.CreateChild( "LOD Mesh" ); meshGeometries.Reverse(); var mainGeometry = meshGeometries[ 0 ]; var lerpingData = new LODLerpingData(); lerpingData.lowerLevelWeights = lowerLerp; lerpingData.higherLevelWeights = higherLerp; lerpingData.lerpSteps = lerpSteps; lodMesh.Mesh = MeshGeometry.GeneratePointsLODMesh( meshGeometries, lerpingData, false ); lodMesh.SetSurfaceOverrideMaterial( 0, material ); lod = lodBuilder.Create( this, "LOD" ); lod.lod0 = lod.CreateChild(); lod.lod0.Mesh = mesh.Mesh; lod.lod0.MaterialOverride = mesh.GetSurfaceOverrideMaterial( 0 ); lod.distanceScale = 4; // cloud.SortByDistanceToCenter(); // numPoints = cloud.points.Count; // squareTexture = (int)Mathf.Pow( numPoints, 0.5f ); // output.Mesh = cloud.GenerateMesh(); working = false; } } }