OverviewFilesCommitsIssuesDocsReleasesPeers

ifsrenderer-linux / IFSEngine / Model / Camera.cs

118 lines · csharp ref:
using System.Numerics;
using System.Runtime.Serialization;

using IFSEngine.Rendering.GpuStructs;
using IFSEngine.Utility;

namespace IFSEngine.Model;

//http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/
public class Camera
{
    public Quaternion Orientation { get; set; } = Quaternion.Identity;//no rotation
    public Vector3 Position { get; set; } = new Vector3(0.0f, 0.0f, -10.0f);

    [IgnoreDataMember]
    public Vector3 RightDirection { get; private set; } = new Vector3(1.0f, 0.0f, 0.0f);
    [IgnoreDataMember]
    public Vector3 UpDirection { get; private set; } = new Vector3(0.0f, 1.0f, 0.0f);
    [IgnoreDataMember]
    public Vector3 ForwardDirection { get; private set; } = new Vector3(0.0f, 0.0f, 1.0f);

    /// <summary>
    /// Vertical field of view in degrees. Ranges from 0 to 180 exclusive.
    /// </summary>
    public double FieldOfView { get; set; } = 60;
    public double Aperture { get; set; } = 0.0;
    public double FocusDistance { get; set; } = 10.0;
    public double DepthOfField { get; set; } = 0.25;
    public ProjectionType Projection { get; set; } = ProjectionType.Perspective;

    /// <summary>
    /// Used with fisheye projection, tilting is useful for planetarium setups where the dome projector is offset from the center.
    /// </summary>
    public Vector3 ProjectionTilt { get; set; } = Vector3.Zero;

    /// <summary>
    /// Moves camera position by a translate vector given in camera space.
    /// </summary>
    /// <param name="translateVector"></param>
    public void Translate(Vector3 translateVector)
    {
        Position += RightDirection * translateVector.X
                 + UpDirection * translateVector.Y
                 + ForwardDirection * translateVector.Z;
    }

    /// <summary>
    /// Rotates the camera orientation by the specified Euler angles.
    /// </summary>
    /// <param name="rotVector">Euler angle (Yaw, Pitch, Roll) deltas in radians.</param>
    public void Rotate(Vector3 rotVector)
    {
        Quaternion rotq = Quaternion.CreateFromYawPitchRoll(rotVector.X, rotVector.Y, rotVector.Z);
        Orientation *= rotq;
        Orientation = Quaternion.Normalize(Orientation);
        UpdateDirectionVectors();
    }

    /// <summary>
    /// Orbits the camera around a pivot point while keeping the camera aimed at the pivot.
    /// </summary>
    /// <param name="pivot">The world-space point to orbit around.</param>
    /// <param name="rotVector">Euler angle (Yaw, Pitch, Roll) deltas in radians.</param>
    public void OrbitAround(Vector3 pivot, Vector3 rotVector)
    {
        Quaternion rotq = Quaternion.CreateFromYawPitchRoll(rotVector.X, rotVector.Y, rotVector.Z);
        Orientation *= rotq;
        Orientation = Quaternion.Normalize(Orientation);
        UpdateDirectionVectors();

        float distanceToPivot = Vector3.Distance(Position, pivot);
        Position = pivot - ForwardDirection * distanceToPivot;
    }

    private void UpdateDirectionVectors()
    {
        RightDirection = Vector3.Transform(new Vector3(1.0f, 0.0f, 0.0f), Orientation);
        UpDirection = Vector3.Transform(new Vector3(0.0f, 1.0f, 0.0f), Orientation);
        ForwardDirection = Vector3.Transform(new Vector3(0.0f, 0.0f, 1.0f), Orientation);
    }

    private Matrix4x4 GetViewProjectionMatrix()
    {
        //Matrix4x4.CreateLookAt uses different handedness so direction vectors are inverted here to get the correct view matrix.
        var viewMatrix = Matrix4x4.CreateLookAt(Position, Position - ForwardDirection, -UpDirection);
        var projectionMatrix = Matrix4x4.CreatePerspectiveFieldOfView(NumericExtensions.ToRadians(1 + (float)FieldOfView % 179), 1.0f, 0.001f, float.PositiveInfinity);

        if (Projection == ProjectionType.Fisheye)
        {//tilted projection
            projectionMatrix
                = Matrix4x4.CreateRotationX(NumericExtensions.ToRadians(ProjectionTilt.X))
                * Matrix4x4.CreateRotationY(NumericExtensions.ToRadians(ProjectionTilt.Y))
                * Matrix4x4.CreateRotationZ(NumericExtensions.ToRadians(ProjectionTilt.Z))
                * projectionMatrix;
        }

        return viewMatrix * projectionMatrix;
    }

    internal CameraStruct GetCameraParameters()
    {
        UpdateDirectionVectors();
        return new CameraStruct
        {
            position = new Vector4(Position, 1.0f),
            forward = new Vector4(ForwardDirection, 1.0f),
            viewProjMatrix = GetViewProjectionMatrix(),
            aperture = (float)Aperture,
            focus_distance = (float)FocusDistance,
            depth_of_field = (float)DepthOfField,
            focus_point = new Vector4(Position + (float)FocusDistance * ForwardDirection, 0.0f),
            projection_type = (int)Projection
        };
    }


}