rj-action-library/Runtime/LOD/PointClouds/PointCloudGenerator.cs

258 lines
6.5 KiB
C#

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<Material>( 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<MeshInstance3D>();
var lodBuilder = LODBuilder.ByCameraDistance();
lodBuilder.SetMaterial( material );
// lodBuilder.Add( 0, mesh.Mesh, mesh.GetSurfaceOverrideMaterial( 0 ) );
var meshGeometries = new List<MeshGeometry>();
var infos = new List<string>();
for ( int i = level + 1; i < depth; i++ )
{
var filledLevels = levelMap[ i ].Filter( l => l.values != null );
var points = new List<Point>();
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<MeshInstance3D>();
meshInstance.Mesh = mesh;
meshInstance.MaterialOverride = material;
levelInstances.Add( meshInstance );
}
}
if ( createOutputs )
{
outputs = levelInstances.ToArray();
}
outputInfos = infos.ToArray();
var lodMesh = this.CreateChild<MeshInstance3D>( "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<MeshInstance3D>();
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;
}
}
}