using T3.Core.Rendering; using T3.Core.Utils; using T3.Core.Utils.Geometry; namespace Lib.render.camera; [Guid("9e27c32d-b187-4b7c-9761-0c5bb4ae3c45")] internal sealed class CameraWithRotation : Instance, ICameraPropertiesProvider, ICamera { [Output(Guid = "70395556-2008-43ec-a73d-b4b35ae8ce58")] public readonly Slot Output = new(); [Output(Guid = "9e10954c-3f9e-4f99-8442-cdeeb0a765fc")] public readonly Slot Reference = new(); public CameraWithRotation() { Output.UpdateAction = UpdateOutputWithSubtree; Reference.UpdateAction = UpdateCameraDefinition; Reference.Value = this; } private void UpdateOutputWithSubtree(EvaluationContext context) { if (!Reference.HasInputConnections || Reference.DirtyFlag.IsDirty) UpdateCameraDefinition(context); Reference.DirtyFlag.Clear(); if (context.BypassCameras) { Command.GetValue(context); return; } // Set properties and evaluate sub tree var prevWorldToCamera = context.WorldToCamera; var prevCameraToClipSpace = context.CameraToClipSpace; context.WorldToCamera = WorldToCamera; context.CameraToClipSpace = CameraToClipSpace; Command.GetValue(context); context.CameraToClipSpace = prevCameraToClipSpace; context.WorldToCamera = prevWorldToCamera; } private void UpdateCameraDefinition(EvaluationContext context) { LastObjectToWorld = context.ObjectToWorld; var aspectRatio = AspectRatio.GetValue(context); if (aspectRatio < 0.0001f) { aspectRatio = (float)context.RequestedResolution.Width / context.RequestedResolution.Height; } var position = Position.GetValue(context); var rotationMode = RotationMode.GetEnumValue(context); var quaternion = RotationQuaternion.GetValue(context); var rotationFactor = RotationFactor.GetValue(context); var rotationOffset2 = RotationOffset2.GetValue(context); var eulerAngles = Rotation.GetValue(context) * rotationFactor + rotationOffset2; Matrix4x4 rotationMatrix = Matrix4x4.Identity; switch (rotationMode) { case RotationModes.Euler: { // This does not work because axis have to be applied in reverse order // rotationMatrix = Matrix4x4.CreateFromYawPitchRoll(eulerAngles.Y.ToRadians(), // Yaw // eulerAngles.X.ToRadians(), // Pitch (ups should have been X) // eulerAngles.Z.ToRadians()); // Roll // From Quaternion.cs: // Roll first, about axis the object is facing, then // pitch upward, then yaw to face into the new heading var pitch = Matrix4x4.CreateRotationY(eulerAngles.X.ToRadians()); // Yaw: Rotation around Y-axis var heading = Matrix4x4.CreateRotationX(eulerAngles.Y.ToRadians()); // Pitch: Rotation around X-axis var roll = Matrix4x4.CreateRotationZ(eulerAngles.Z.ToRadians()); // Roll: Rotation around Z-axis rotationMatrix = heading * pitch * roll; break; } case RotationModes.Quaternion: rotationMatrix = Matrix4x4.CreateFromQuaternion(new Quaternion(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W)); break; } var deltaFromPosition = Vector3.TransformNormal(Vector3.UnitZ, rotationMatrix); _target = position + deltaFromPosition; _camDef = new CameraDefinition { NearFarClip = ClipPlanes.GetValue(context), LensShift = LensShift.GetValue(context), PositionOffset = PositionOffset.GetValue(context), Position = position, Target = _target, Up = Up.GetValue(context), AspectRatio = aspectRatio, FieldOfView = FOV.GetValue(context).ToRadians(), Roll = eulerAngles.Z.ToRadians(), RotationOffset = RotationOffset.GetValue(context), OffsetAffectsTarget = AlsoOffsetTarget.GetValue(context) }; var camToClipSpace = GraphicsMath.PerspectiveFovRH(_camDef.FieldOfView, _camDef.AspectRatio, _camDef.NearFarClip.X, _camDef.NearFarClip.Y); camToClipSpace.M31 = _camDef.LensShift.X; camToClipSpace.M32 = _camDef.LensShift.Y; var translationMatrix = Matrix4x4.CreateTranslation(-position); var worldToCamera = translationMatrix * rotationMatrix; CameraToClipSpace = camToClipSpace; WorldToCamera = worldToCamera; } private enum RotationModes { Euler, Quaternion, } private Vector3 _target; public float CameraRoll { get; set; } private CameraDefinition _camDef; public Matrix4x4 CameraToClipSpace { get; set; } public CameraDefinition CameraDefinition => _camDef; public Matrix4x4 WorldToCamera { get; set; } public Matrix4x4 LastObjectToWorld { get; set; } // Implement ICamera public Vector3 CameraPosition { get { return Position.Value; } set { Animator.UpdateVector3InputValue(Position, value); } } public Vector3 CameraTarget { get => _target; set { Log.Warning("Can't set Rotation camera target"); } } [Input(Guid = "3e155fc2-9a11-46d1-8da5-36793140ae3f")] public readonly InputSlot Command = new(); [Input(Guid = "6bd84cf7-e89f-4e48-84ca-63fe5bab6291")] public readonly InputSlot Position = new(); [Input(Guid = "E5756357-6EDC-4C1C-BCEE-BD160EB2EC62", MappedType = typeof(RotationModes))] public readonly InputSlot RotationMode = new(); [Input(Guid = "D4087018-EB4B-4EFC-9387-4B1D2D81A395")] public readonly InputSlot Rotation = new(); [Input(Guid = "7054CB41-0641-4C50-88A9-27EEA0472B52")] public readonly InputSlot RotationFactor = new(); [Input(Guid = "0FE2745D-5A87-47E1-84D4-544E96DF42A7")] public readonly InputSlot RotationOffset2 = new(); // [Input(Guid = "66fa647e-c0f9-495d-8074-64fe05cf1977")] // public readonly InputSlot Target = new(); [Input(Guid = "ddd02480-dcfd-4ae9-a057-ee56472bc0ec")] public readonly InputSlot FOV = new(); [Input(Guid = "28683A86-6CE3-44A9-8226-1F6CF157BDE4")] public readonly InputSlot RotationQuaternion = new(); // [Input(Guid = "a3c2610a-527f-469b-8ee8-7e6c49493776")] // public readonly InputSlot Roll = new(); // --- offset [Input(Guid = "d386ca2c-6ffe-4871-a1f4-f713b383e892")] public readonly InputSlot PositionOffset = new(); [Input(Guid = "55c59681-f50b-44bc-80bc-3039b73b1c00")] public readonly InputSlot AlsoOffsetTarget = new(); [Input(Guid = "325b86e7-3d2a-4eeb-95d3-3138696a8a94")] public readonly InputSlot RotationOffset = new(); [Input(Guid = "2ba9e2c0-f494-49aa-9030-172177f06eda")] public readonly InputSlot LensShift = new(); // --- options [Input(Guid = "766a9e48-9f3b-4204-973c-751bb7180400")] public readonly InputSlot ClipPlanes = new(); [Input(Guid = "d5761f3a-b2d0-435d-900c-3ff185373269")] public readonly InputSlot AspectRatio = new(); [Input(Guid = "1b672745-d0fd-4611-862f-f27f59d3d416")] public readonly InputSlot Up = new(); }