using Godot; using System.Collections; using System.Collections.Generic; using Godot.Collections; namespace Rokojori { [Tool] [GlobalClass] public partial class LODParent:Node3D { public enum DistanceMode { Via_Array, Via_Curve } [Export] public DistanceMode mode; [ExportGroup("Array Mode")] [Export] public bool arrayCulling = true; [Export] public float[] arrayTresholds; [ExportGroup("Curve Mode")] [Export] public bool curveCulling = true; [Export] public float curveCullDistance = 4000; [Export] public Curve curveDistribution = MathX.Curve( 0, 1 ); [ExportGroup("LODs")] [Export] public Node3D[] lods; [Export] public float updateDistance = 10; [ExportGroup("Testing")] [Export] public float X_processingCameraDistance = 0; [Export] public float X_realCameraDistance = 0; [Export] public float X_selectedIndex = 0; [Export] public bool testMode = false; [Export] public float testDistance = 0; [Export] public bool processOnlyWhenVisible = true; float lastSquaredDistance = -1; int lastSelectedIndex = -1; public int changeBlock = 0; public int blocker = 0; public override void _Process( double delta ) { if ( processOnlyWhenVisible && ! Visible ) { return; } if ( blocker > 0 ) { blocker --; return; } if ( lods == null || lods.Length == 0 ) { return; } var camera = GetViewport().GetCamera3D(); #if TOOLS if ( Engine.IsEditorHint() ) { camera = EditorInterface.Singleton.GetEditorViewport3D().GetCamera3D(); } #endif var squaredDistance = GlobalPosition.DistanceSquaredTo( camera.GlobalPosition ); X_realCameraDistance = Mathf.Sqrt( squaredDistance ); if ( testMode ) { squaredDistance = testDistance * testDistance; } if ( Mathf.Abs( lastSquaredDistance - squaredDistance ) < updateDistance * updateDistance ) { return; } lastSquaredDistance = squaredDistance; var realDistance = Mathf.Sqrt( lastSquaredDistance ); X_processingCameraDistance = realDistance; var selectedIndex = GetLODIndex( realDistance ); X_selectedIndex = selectedIndex; if ( lastSelectedIndex == selectedIndex ) { return; } lastSelectedIndex = selectedIndex; blocker = changeBlock; if ( selectedIndex >= lods.Length ) { Arrays.ForEach( lods, l => NodeState.Set( l, false ) ); } else { var selectedLOD = lods[ selectedIndex ]; Arrays.ForEach( lods, l => NodeState.Set( l, selectedLOD == l ) ); } } int GetLODIndex( float realDistance ) { if ( DistanceMode.Via_Array == mode ) { for ( int i = 0; i < arrayTresholds.Length; i++ ) { if ( realDistance <= arrayTresholds[ i ] ) { return i; } } return arrayCulling ? lods.Length : ( lods.Length - 1 ); } else if ( DistanceMode.Via_Curve == mode ) { if ( curveCulling && realDistance >= curveCullDistance ) { return lods.Length; } var normalizedDistance = MathX.NormalizeClamped( realDistance, 0, curveCullDistance ); var active = curveDistribution.Sample( normalizedDistance ) * lods.Length; return Mathf.Min( lods.Length - 1, Mathf.RoundToInt( active ) ); } return -1; } } }