Files
2026-07-13 13:13:17 +08:00

235 lines
9.7 KiB
C#

using T3.Core.Rendering;
using T3.Core.Utils;
using T3.Core.Utils.Geometry;
// ReSharper disable SuggestVarOrType_SimpleTypes
namespace Lib.render.camera;
[Guid("6415ed0e-3692-45e2-8e70-fe0cf4d29ebc")]
internal sealed class OrbitCamera : Instance<OrbitCamera>
, ICameraPropertiesProvider, ICamera
{
[Output(Guid = "14a63b62-5fbb-4f82-8cf3-d0faf279eff8", DirtyFlagTrigger = DirtyFlagTrigger.Animated)]
public readonly Slot<Command> Output = new();
[Output(Guid = "451245E2-AC0B-435A-841E-7C9EDC804606", DirtyFlagTrigger = DirtyFlagTrigger.Animated)]
public readonly Slot<Object> Reference = new();
public OrbitCamera()
{
Output.UpdateAction += UpdateOutputWithSubtree;
Reference.UpdateAction += UpdateCameraDefinition;
Reference.Value = this;
}
private void UpdateOutputWithSubtree(EvaluationContext context)
{
if (!Reference.HasInputConnections || Reference.DirtyFlag.IsDirty)
UpdateCameraDefinition(context);
if (context.BypassCameras)
{
Command.GetValue(context);
return;
}
// Set properties and evaluate sub tree
var prevCameraToClipSpace = context.CameraToClipSpace;
var prevWorldToCamera = context.WorldToCamera;
context.CameraToClipSpace = CameraToClipSpace;
context.WorldToCamera = WorldToCamera;
Command.GetValue(context);
context.CameraToClipSpace = prevCameraToClipSpace;
context.WorldToCamera = prevWorldToCamera;
}
private void UpdateCameraDefinition(EvaluationContext context)
{
LastObjectToWorld = context.ObjectToWorld;
var damping = Damping.GetValue(context).Clamp(0, 1);
var isOverriding = OverrideTime.HasInputConnections;
var time = isOverriding ? OverrideTime.GetValue(context) : context.LocalFxTime;
var fov = MathUtils.ToRad * (FOV.GetValue(context));
var aspectRatio = AspectRatio.GetValue(context);
if (aspectRatio < 0.0001f)
{
aspectRatio = (float)context.RequestedResolution.Width / context.RequestedResolution.Height;
}
var positionOffset = PositionOffset.GetValue(context);
Vector2 clip = NearFarClip.GetValue(context);
var camToClipSpace = GraphicsMath.PerspectiveFovRH(fov, aspectRatio, clip.X, clip.Y);
CameraJitter.ApplyFromContext(context, ref camToClipSpace);
CameraToClipSpace = camToClipSpace;
var radiusValue = DistanceToTarget.GetValue(context) == 0.0f ? .0001f : DistanceToTarget.GetValue(context); // avoid 0 radius that causes issues
Vector3 p = new Vector3(0, 0, radiusValue);
var seed = Seed.GetValue(context);
var wobbleSpeed = WobbleSpeed.GetValue(context);
var wobbleComplexity = (int)WobbleComplexity.GetValue(context).Clamp(1, 8);
var rotOffset = RotationOffset.GetValue(context);
// Orbit rotation
Vector3 t = CameraTargetPosition.GetValue(context);
Vector3 center = new Vector3(t.X, t.Y, t.Z);
var spinOffset = SpinOffset.GetValue(context);
var orbitYaw = ComputeAngle(SpinAngleAndWobble, 1)
+ MathUtils.ToRad * ((float)((SpinRate.GetValue(context) * time) * 360
+ spinOffset
+ MathUtils.PerlinNoise(0, 1, 6, seed) * 360));
var orbitPitch = -ComputeAngle(OrbitAngleAndWobble, 2);
var rot = Matrix4x4.CreateFromYawPitchRoll(
orbitYaw,
orbitPitch,
0);
var p2 = Vector3.Transform(p, rot);
var eye = new Vector3(p2.X, p2.Y, p2.Z);
// View rotation
var viewDirection = center - eye;
var viewYaw = ComputeAngle(AimYawAngleAndWobble, 3) + rotOffset.X * MathUtils.ToRad;
var viewPitch = ComputeAngle(AimPitchAngleAndWobble, 4) + rotOffset.Y * MathUtils.ToRad;
var rotateAim = Matrix4x4.CreateFromYawPitchRoll(
viewYaw,
viewPitch,
0);
var adjustedViewDirection = Vector3.TransformNormal(viewDirection, rotateAim);
adjustedViewDirection = Vector3.Normalize(adjustedViewDirection);
// Computing matrix
var up = Up.GetValue(context);
var roll = ComputeAngle(AimRollAngleAndWobble, 5) + rotOffset.Z * MathUtils.ToRad;
var rotateAroundViewDirection = Matrix4x4.CreateFromAxisAngle(adjustedViewDirection, roll);
up = Vector3.TransformNormal(up, rotateAroundViewDirection);
up = Vector3.Normalize(up);
eye+= Vector3.TransformNormal(positionOffset, rot);
//eye+= positionOffset;
var target = eye + adjustedViewDirection;
_dampedEye = Vector3.Lerp(eye, _dampedEye, damping);
_dampedTarget = Vector3.Lerp(target, _dampedTarget, damping);
_cameraDefinition.Target = _dampedTarget;
_cameraDefinition.Position = _dampedEye;
_cameraDefinition.Roll = roll;
_cameraDefinition.Up = up;
_cameraDefinition.AspectRatio = aspectRatio;
_cameraDefinition.NearFarClip = clip;
_cameraDefinition.FieldOfView = fov;
_cameraDefinition.PositionOffset = positionOffset;
_cameraDefinition.RotationOffset = rotOffset;
WorldToCamera = GraphicsMath.LookAtRH(_dampedEye, _dampedTarget, up);
CameraPosition = eye;
CameraTarget = eye + adjustedViewDirection;
float ComputeAngle(Slot<Vector2> angleAndWobbleInput, int seedIndex)
{
var angleAndWobble = angleAndWobbleInput.GetValue(context);
var wobble = Math.Abs(angleAndWobble.Y) < 0.001f
? 0
: (MathUtils.PerlinNoise((float)time * wobbleSpeed,
1,
wobbleComplexity,
seed + 123 * seedIndex)
- 0.5f) * 2 * angleAndWobble.Y;
return MathUtils.ToRad * (angleAndWobble.X + wobble);
}
}
public Matrix4x4 CameraToClipSpace { get; set; }
public Vector3 CameraPosition { get; set; }
public Vector3 CameraTarget { get; set; }
public float CameraRoll { get; set; }
public Matrix4x4 WorldToCamera { get; set; }
public Matrix4x4 LastObjectToWorld { get; set; }
private Vector3 _dampedTarget;
private Vector3 _dampedEye;
private CameraDefinition _cameraDefinition;
public CameraDefinition CameraDefinition => _cameraDefinition;
[Input(Guid = "33752356-8348-4938-8f73-6257e6bb1c1f")]
public readonly InputSlot<Command> Command = new InputSlot<Command>();
[Input(Guid = "ACF14901-3373-4B0C-8567-03EA0051A21F")]
public readonly InputSlot<Vector3> CameraTargetPosition = new InputSlot<Vector3>();
[Input(Guid = "21f595ad-0808-48f1-bdd8-118d1527944c")]
public readonly InputSlot<float> FOV = new InputSlot<float>();
[Input(Guid = "DD92FB0A-4B3E-4492-BF59-437D914A1AD3")]
public readonly InputSlot<float> DistanceToTarget = new InputSlot<float>();
[Input(Guid = "DF65E717-E2FD-4E4F-9E41-D6BCD3FE67F1")]
public readonly InputSlot<float> SpinRate = new InputSlot<float>();
[Input(Guid = "8C4DAB88-68CB-40CC-B576-CA3F3EA8461F")]
public readonly InputSlot<float> SpinOffset = new InputSlot<float>();
[Input(Guid = "8B75047F-03B7-4619-8869-2906E66731D1")]
public readonly InputSlot<Vector2> OrbitAngleAndWobble = new InputSlot<Vector2>();
[Input(Guid = "1AF76B2E-CFFE-4E3F-8793-C7A59D00430B")]
public readonly InputSlot<Vector2> AimRollAngleAndWobble = new InputSlot<Vector2>();
[Input(Guid = "4D2D2D2D-00BD-4DF9-B209-62F0C7926C38")]
public readonly InputSlot<Vector2> AimPitchAngleAndWobble = new InputSlot<Vector2>();
[Input(Guid = "066CD0E7-DE72-4E04-BF13-686CCC301C5A")]
public readonly InputSlot<Vector2> AimYawAngleAndWobble = new InputSlot<Vector2>();
[Input(Guid = "7412E22C-1F15-4471-883B-4FCD792146F7")]
public readonly InputSlot<Vector2> SpinAngleAndWobble = new InputSlot<Vector2>();
[Input(Guid = "B6BF6FE1-6733-46C0-A274-FAB2A950F606")]
public readonly InputSlot<float> WobbleSpeed = new InputSlot<float>();
[Input(Guid = "0AD10E15-06EF-4DDE-8EB3-ED7FE989C88E")]
public readonly InputSlot<int> WobbleComplexity = new InputSlot<int>();
[Input(Guid = "FB113FD1-3EDF-4DB6-9DFB-800C62070D69")]
public readonly InputSlot<float> Damping = new InputSlot<float>();
[Input(Guid = "DD81B2AA-3252-4130-8DEF-A5B399D3E283")]
public readonly InputSlot<int> Seed = new InputSlot<int>();
[Input(Guid = "353e2f08-a55f-48ce-ae65-53d5d081b6f0")]
public readonly InputSlot<Vector2> NearFarClip = new InputSlot<Vector2>();
[Input(Guid = "C81B91C6-2D06-4E3E-97BD-01D60F5F0F7D")]
public readonly InputSlot<Vector3> RotationOffset = new InputSlot<Vector3>();
[Input(Guid = "11B13DB2-3C91-4199-8245-50AF200E3A9D")]
public readonly InputSlot<Vector3> PositionOffset = new InputSlot<Vector3>();
[Input(Guid = "bd1bc8a5-72ce-42b0-8914-4f6e124a18ae")]
public readonly InputSlot<float> AspectRatio = new InputSlot<float>();
[Input(Guid = "f51b38d7-2380-457f-897d-2429b2ad6ac3")]
public readonly InputSlot<Vector3> Up = new InputSlot<Vector3>();
[Input(Guid = "46AA9CE1-EC54-43C5-AD5E-2CA679A254FD")]
public readonly InputSlot<float> OverrideTime = new();
}