258 lines
6.5 KiB
C#
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;
|
|
|
|
}
|
|
}
|
|
} |