rj-action-library/Runtime/VirtualCameras/VirtualCamera3DManager.cs

443 lines
9.3 KiB
C#

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<VirtualCamera3DManager>.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<float> prios = new List<float>();
public override void _Process( double delta )
{
if ( ! active )
{
return;
}
ProcessCameras( delta );
ProcessPostProcess( delta );
}
PostProcessEffectProcessor<GlowEffect> glowEffectProcessor = new PostProcessEffectProcessor<GlowEffect>();
PostProcessEffectProcessor<FogEffect> fogEffectProcessor = new PostProcessEffectProcessor<FogEffect>();
PostProcessEffectProcessor<_XX_DepthOfFieldEffect> depthOfFieldProcessor = new PostProcessEffectProcessor<_XX_DepthOfFieldEffect>();
void SetAllPostProcessors()
{
_allPostProcessors =new List<IPostProcessEffectProcessor>()
{
glowEffectProcessor,
fogEffectProcessor,
depthOfFieldProcessor
};
}
List<IPostProcessEffectProcessor> _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<VirtualCamera3DSlot> _cameraSlots = new List<VirtualCamera3DSlot>();
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<VirtualCamera3DSlot>( 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;
}
}
}