using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System; using Godot; using System.Linq; namespace Rokojori { [Tool] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/VirtualCameraManager.svg") ] public partial class VirtualCamera3DManager:NetworkNode { [Export] public Camera3D camera; [Export] public WorldEnvironment worldEnvironment; [Export] public bool refreshSlots = false; [Export] public bool active = false; [Export] public bool postProcess = false; [Export] public PostProcessVolume[] postProcessVolumes = []; public static VirtualCamera3DManager Get() { return Unique.Get(); } public VirtualCamera3DSlot activeSlot { get { return Lists.GetWithHighestValue( _cameraSlots, c => c.priority ); } } [ExportGroup("Read Only" )] [Export] public bool lerpingCameras; [Export] public bool lerpingPostProcessing; bool _lerpingCameras = false; const int minimalLerpFrames = 100; int _cameraLerpCounter = 0; int _postProcessLerpCounter = 0; bool _lerpingPostProcessVolumes = false; public void StartLerping() { _lerpingCameras = true; lerpingCameras = _lerpingCameras; _cameraLerpCounter = minimalLerpFrames; this.LogInfo( "Started Lerping" ); } List prios = new List(); public override void _Process( double delta ) { if ( ! active ) { return; } ProcessCameras( delta ); ProcessPostProcess( delta ); } PostProcessEffectProcessor glowEffectProcessor = new PostProcessEffectProcessor(); PostProcessEffectProcessor fogEffectProcessor = new PostProcessEffectProcessor(); PostProcessEffectProcessor<_XX_DepthOfFieldEffect> depthOfFieldProcessor = new PostProcessEffectProcessor<_XX_DepthOfFieldEffect>(); void SetAllPostProcessors() { _allPostProcessors =new List() { glowEffectProcessor, fogEffectProcessor, depthOfFieldProcessor }; } List _allPostProcessors; bool updatePostEffectStack = true; [Export] public bool postStackUpdate = false; void ProcessPostProcess( double delta ) { if ( ! postProcess || postProcessVolumes == null ) { return; } postProcessVolumes.ForEach( v => { if ( v == null ) { return; } v.UpdateEffects( this ); } ); if ( postStackUpdate ) { updatePostEffectStack =true; postStackUpdate = false; } UpdatePostEffectStack(); _allPostProcessors.ForEach( p => p.Apply( worldEnvironment ) ); } int _postEffectsSize = -1; void UpdatePostEffectStack() { if ( _allPostProcessors == null || _allPostProcessors.Count == 0 ) { SetAllPostProcessors(); } if ( _postEffectsSize != postProcessVolumes.Length ) { updatePostEffectStack = true; } if ( ! updatePostEffectStack ) { return; } _postEffectsSize = postProcessVolumes.Length; _allPostProcessors.ForEach( p => p.ClearForStackUpdate() ); postProcessVolumes.ForEach( volume => { var allEffects = volume.effects.ToList(); _allPostProcessors.ForEach( p => p.OnVolumeUpdate( volume, allEffects ) ); } ); } void ProcessCameras( double delta ) { if ( ! _lerpingCameras ) { RefreshSlots(); var cam = activeSlot; if ( camera == null || cam == null || cam.camera == null ) { return; } SetSingleCamera( cam ); } else { LerpCameras( delta ); } } void SetSingleCamera( VirtualCamera3DSlot c ) { var rotation = c.GetCameraRotation(); if ( ! rotation.IsFinite() || rotation.Length() == 0 ) { rotation = new Quaternion(); rotation.X = 0; rotation.Y = 0; rotation.Z = 0; rotation.W = 1; rotation = rotation.Normalized(); } var vUp = rotation * Vector3.Up; var vForward = rotation * Vector3.Forward; var position = c.GetCameraPosition(); var up = vUp; var forward = vForward; var fov = c.GetCameraFOV(); if ( forward.LengthSquared() == 0 ) { forward = camera.Basis.Z; } else { forward = forward.Normalized(); } if ( up.LengthSquared() == 0 ) { up = camera.Basis.Y; } else { up = up.Normalized(); } // RJLog.Log( "Set Cam", position ); camera.GlobalPosition = position; camera.LookAt( position - forward, up ); camera.Fov = fov; } [Export] public float CameraPrioritySmoothingCoefficient = 0.1f; [Export] public float CameraPrioritySmoothingStepFPS = 120; public float smoothStepDelta => 1f / CameraPrioritySmoothingStepFPS; public float safeSmoothing => Mathf.Max( 0, CameraPrioritySmoothingCoefficient ); List _cameraSlots = new List(); public VirtualCamera3DSlot GetSlot( int index ) { return _cameraSlots[ index ]; } public VirtualCamera3DSlot GetSlot( VirtualCamera3D virtualCamera3D ) { return _cameraSlots.Find( s => s.camera == virtualCamera3D ); } public VirtualCamera3DSlot GetSlot( SelectorFlag[] flags ) { return _cameraSlots.Find( s => Arrays.ContainsAll( s.flags, flags ) ); } public void SetActiveSlot( VirtualCamera3DSlot slot ) { _cameraSlots.ForEach( c => c.priority = ( c == slot ? 1 : 0 ) ); StartLerping(); } public void RefreshSlots( bool forceUpdate = false ) { var needsUpdate = forceUpdate || refreshSlots || _cameraSlots == null || _cameraSlots.Count == 0; if ( ! needsUpdate ) { return; } refreshSlots = false; _cameraSlots = Nodes.GetDirectChildren( this ); StartLerping(); } public void RemoveSlot( VirtualCamera3DSlot slot ) { if ( slot == null || slot.GetParent() != this ) { return; } Nodes.RemoveAndDelete( slot ); RefreshSlots( true ); } void LerpCameras( double delta ) { RefreshSlots(); var sumPriority = 0f; if ( prios.Count != _cameraSlots.Count ) { prios = _cameraSlots.Map( c => c.smoothedPriority ); } var numWithNonZero = 0; _cameraSlots.ForEach( c => { c.Update( delta, this ); sumPriority += MathF.Max( 0, c.smoothedPriority ); if ( c.smoothedPriority > 0.001f ) { numWithNonZero ++; } } ); if ( _cameraLerpCounter > 0 ) { _cameraLerpCounter --; } if ( _cameraLerpCounter == 0 && numWithNonZero == 1 ) { _lerpingCameras = false; lerpingCameras = _lerpingCameras; // this.LogInfo( "Stopped Lerping" ); // return; } if ( sumPriority == 0 ) { return; } var position = new Vector3(); var up = new Vector3(); var forward = new Vector3(); var fov = 0f; _cameraSlots.ForEach( c => { var priority = MathF.Max( 0, c.smoothedPriority ); var rotation = c.GetCameraRotation(); if ( ! rotation.IsFinite() || rotation.Length() == 0 ) { rotation = new Quaternion(); rotation.X = 0; rotation.Y = 0; rotation.Z = 0; rotation.W = 1; rotation = rotation.Normalized(); } var vUp = rotation * Vector3.Up; var vForward = rotation * Vector3.Forward; position += priority * c.GetCameraPosition(); up += priority * vUp; forward += priority * vForward; fov += priority * c.GetCameraFOV(); } ); position /= sumPriority; fov /= sumPriority; if ( forward.LengthSquared() == 0 ) { forward = camera.Basis.Z; } else { forward = forward.Normalized(); } if ( up.LengthSquared() == 0 ) { up = camera.Basis.Y; } else { up = up.Normalized(); } // RJLog.Log( "Set Cam", position ); camera.GlobalPosition = position; camera.LookAt( position - forward, up ); camera.Fov = fov; } public VirtualCamera3D GetCamera( int index ) { return _cameraSlots[ index ].camera; } public int GetCameraIndex( VirtualCamera3D camera3D ) { return _cameraSlots.FindIndex( c => c.camera == camera3D ); } public float GetCameraPriority( int index ) { return _cameraSlots[ index ].priority; } public void SetCameraPriority( int index, float priority ) { _cameraSlots[ index ].priority = priority; } } }