rj-action-library/Runtime/LOD/LODMultiMesh.cs

131 lines
3.0 KiB
C#

using Godot;
using System.Collections;
using System.Collections.Generic;
using Godot.Collections;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class LODMultiMesh:MultiMeshInstance3D
{
[Export]
public float minDistance = 0;
[Export]
public float cullDistance = 100;
[Export]
public Curve fillCurve = MathX.Curve( 0, 1 );
float _distance = -10;
[Export]
public float distanceTreshold = 0.01f;
[Export( PropertyHint.Range, "0,1" )]
public float scaleRange = 0.5f;
[Export]
public float downMax = 0.1f;
[Export]
Vector3[] cachedOrigins = null;
[Export]
Vector3[] cachedBasis = null;
public void CacheTransforms()
{
var mm = Multimesh;
cachedOrigins = new Vector3[ mm.InstanceCount ];
cachedBasis = new Vector3[ mm.InstanceCount * 3 ];
for ( int i = 0; i < mm.InstanceCount; i++ )
{
var trsf = mm.GetInstanceTransform( i );
cachedBasis[ i * 3 + 0 ] = trsf.Basis.Column0;
cachedBasis[ i * 3 + 1 ] = trsf.Basis.Column1;
cachedBasis[ i * 3 + 2 ] = trsf.Basis.Column2;
cachedOrigins[ i ] =( trsf.Origin );
}
}
public override void _Process(double delta)
{
var camera = GetViewport().GetCamera3D();
#if TOOLS
if ( Engine.IsEditorHint() )
{
camera = EditorInterface.Singleton.GetEditorViewport3D().GetCamera3D();
}
#endif
var distance = ( camera.GlobalPosition - GlobalPosition ).Length();
if ( Mathf.Abs( _distance - distance ) < distanceTreshold )
{
return;
}
_distance = distance;
var mapped = MathX.RemapClamped( distance, minDistance, cullDistance, 1, 0 );
var mm = Multimesh;
// this.LogInfo( "MM", (int) distance, ( (int) ( 100 * mapped ) ) + "%", (int)( fillCurve.Sample( mapped ) * mm.InstanceCount) + " instances" );
if ( mm == null )
{
return;
}
if ( distance >= cullDistance )
{
mm.VisibleInstanceCount = 0;
return;
}
var count = Mathf.RoundToInt( fillCurve.Sample( mapped ) * mm.InstanceCount );
count = Mathf.Clamp( count, 0, mm.InstanceCount );
var fadeStart = count * ( 1 - scaleRange );
for ( int i = 0; i < count; i++ )
{
var cbasis = new Basis( cachedBasis[ i * 3 + 0 ], cachedBasis[ i * 3 + 1 ], cachedBasis[ i * 3 + 2 ] );
var trsf = new Transform3D( cbasis, cachedOrigins[ i ] );
if ( i >= fadeStart )
{
var amount = MathX.RemapClamped( i, fadeStart, count, 0, 1 );
var basis = trsf.Basis.Scaled( new Vector3( 1, Mathf.Max( ( 1f - amount ), 0.0001f ), 1 ));
var offsetTrsf = new Transform3D( basis, trsf.Origin + downMax * Vector3.Down);
mm.SetInstanceTransform( i, offsetTrsf );
}
else
{
mm.SetInstanceTransform( i, trsf );
}
}
mm.VisibleInstanceCount = count;
}
}
}