فهرست منبع

Added basic camera support

Vicente Penades 6 سال پیش
والد
کامیت
5203315308
2فایلهای تغییر یافته به همراه269 افزوده شده و 6 حذف شده
  1. 181 6
      src/SharpGLTF.Core/Schema2/gltf.Camera.cs
  2. 88 0
      src/SharpGLTF.Core/Transforms/Camera.cs

+ 181 - 6
src/SharpGLTF.Core/Schema2/gltf.Camera.cs

@@ -4,7 +4,7 @@ using System.Text;
 
 namespace SharpGLTF.Schema2
 {
-    [System.Diagnostics.DebuggerDisplay("Camera[{LogicalIndex}] {Name}")]
+    [System.Diagnostics.DebuggerDisplay("Camera[{LogicalIndex}] {Name} {_type}")]
     public sealed partial class Camera
     {
         #region lifecycle
@@ -20,11 +20,12 @@ namespace SharpGLTF.Schema2
         /// </summary>
         public int LogicalIndex => this.LogicalParent.LogicalCameras.IndexOfReference(this);
 
-        public CameraType Type
-        {
-            get => this._type;
-            set => this._type = value;
-        }
+        public ICamera Settings => GetCamera();
+
+        /// <summary>
+        /// Gets the projection matrix for the current <see cref="Settings"/>
+        /// </summary>
+        public System.Numerics.Matrix4x4 Matrix => GetCamera().Matrix;
 
         #endregion
 
@@ -36,6 +37,180 @@ namespace SharpGLTF.Schema2
             return base.GetLogicalChildren().ConcatItems(_orthographic, _perspective);
         }
 
+        internal ICamera GetCamera()
+        {
+            if (this._orthographic != null && this._perspective != null)
+            {
+                switch (this._type)
+                {
+                    case CameraType.orthographic: return this._orthographic;
+                    case CameraType.perspective: return this._perspective;
+                    default: throw new NotImplementedException();
+                }
+            }
+
+            if (this._orthographic != null) return this._orthographic;
+            if (this._perspective != null) return this._perspective;
+
+            return null;
+        }
+
+        /// <summary>
+        /// Configures this <see cref="Camera"/> to use Orthographic projection.
+        /// </summary>
+        /// <param name="xmag">Magnification in the X axis.</param>
+        /// <param name="ymag">Magnification in the Y axis.</param>
+        /// <param name="znear">Distance to the near pane in the Z axis.</param>
+        /// <param name="zfar">Distance to the far plane in the Z axis.</param>
+        public void SetOrthographicMode(float xmag, float ymag, float znear, float zfar)
+        {
+            Guard.MustBeGreaterThanOrEqualTo(znear, 0, nameof(znear));
+            Guard.MustBeGreaterThanOrEqualTo(zfar, 0, nameof(zfar));
+            Guard.MustBeGreaterThan(zfar, znear, nameof(zfar));
+            Guard.MustBeLessThan(zfar, float.PositiveInfinity, nameof(zfar));
+
+            this._perspective = null;
+            this._orthographic = new CameraOrthographic(xmag, ymag, znear, zfar);
+
+            this._type = CameraType.orthographic;
+        }
+
+        /// <summary>
+        /// Configures this <see cref="Camera"/> to use perspective projection.
+        /// </summary>
+        /// <param name="aspectRatio">The aspect ratio between horizontal and vertical. (optional)</param>
+        /// <param name="yfov">The vertical field of view, in radians.</param>
+        /// <param name="znear">Distance to the near pane in the Z axis.</param>
+        /// <param name="zfar">Distance to the far plane in the Z axis.</param>
+        public void SetPerspectiveMode(float? aspectRatio, float yfov, float znear, float zfar)
+        {
+            if (aspectRatio.HasValue) Guard.MustBeGreaterThanOrEqualTo(aspectRatio.Value, 0, nameof(aspectRatio));
+            Guard.MustBeGreaterThan(yfov, 0, nameof(yfov));
+            Guard.MustBeLessThan(yfov, (float)Math.PI, nameof(yfov));
+
+            Guard.MustBeGreaterThanOrEqualTo(znear, 0, nameof(znear));
+            Guard.MustBeGreaterThanOrEqualTo(zfar, 0, nameof(zfar));
+            Guard.MustBeGreaterThan(zfar, znear, nameof(zfar));
+
+            this._orthographic = null;
+            this._perspective = new CameraPerspective(aspectRatio, yfov, znear, zfar);
+
+            this._type = CameraType.perspective;
+        }
+
+        #endregion
+    }
+
+    public interface ICamera
+    {
+        bool IsOrthographic { get; }
+        bool IsPerspective { get; }
+
+        System.Numerics.Matrix4x4 Matrix { get; }
+    }
+
+    [System.Diagnostics.DebuggerDisplay("Orthographic ({XMag},{YMag})  {ZNear} < {ZFar}")]
+    public sealed partial class CameraOrthographic : ICamera
+    {
+        #region lifecycle
+
+        internal CameraOrthographic() { }
+
+        internal CameraOrthographic(float xmag, float ymag, float znear, float zfar)
+        {
+            this._xmag = xmag;
+            this._ymag = ymag;
+            this._znear = znear;
+            this._zfar = zfar;
+        }
+
+        #endregion
+
+        #region properties
+
+        public bool IsPerspective => false;
+
+        public bool IsOrthographic => true;
+
+        /// <summary>
+        /// Gets the magnification factor in the X axis
+        /// </summary>
+        public float XMag => (float)_xmag;
+
+        /// <summary>
+        /// Gets the magnification factor in the Y axis
+        /// </summary>
+        public float YMag => (float)_ymag;
+
+        /// <summary>
+        /// Gets the near plane distance in the Z axis.
+        /// </summary>
+        public float ZNear => (float)_znear;
+
+        /// <summary>
+        /// Gets the far plane distance in the Z axis.
+        /// </summary>
+        public float ZFar => (float)_zfar;
+
+        /// <summary>
+        /// Gets the projection matrix for the current settings
+        /// </summary>
+        public System.Numerics.Matrix4x4 Matrix => Transforms.Camera.CreateOrthographicMatrix(XMag, YMag, ZNear, ZFar);
+
+        #endregion
+    }
+
+    [System.Diagnostics.DebuggerDisplay("Perspective {AspectRatio} {VerticalFOV}   {ZNear} < {ZFar}")]
+    public sealed partial class CameraPerspective : ICamera
+    {
+        #region lifecycle
+
+        internal CameraPerspective() { }
+
+        internal CameraPerspective(float? aspectRatio, float yfov, float znear, float zfar)
+        {
+            this._aspectRatio = aspectRatio ?? null;
+            this._yfov = yfov;
+            this._znear = znear;
+            this._zfar = float.IsPositiveInfinity(zfar) ? (double?)null : (double)zfar;
+        }
+
+        #endregion
+
+        #region properties
+
+        public bool IsOrthographic => false;
+
+        public bool IsPerspective => true;
+
+        /// <summary>
+        /// Gets the aspect ratio between horizontal window size and vertical window size.
+        /// </summary>
+        public float? AspectRatio => (float?)(this._aspectRatio ?? null);
+
+        /// <summary>
+        /// Gets the vertical field of view, in radians
+        /// </summary>
+        public float VerticalFOV => (float)this._yfov;
+
+        /// <summary>
+        /// Gets the near plane distance in the Z axis.
+        /// </summary>
+        public float ZNear => (float)_znear;
+
+        /// <summary>
+        /// Gets the far plane distance in the Z axis.
+        /// </summary>
+        /// <remarks>
+        /// This value can be a finite value, or positive infinity.
+        /// </remarks>
+        public float ZFar => this._zfar.HasValue ? (float)_zfar.Value : float.PositiveInfinity;
+
+        /// <summary>
+        /// Gets the projection matrix for the current settings
+        /// </summary>
+        public System.Numerics.Matrix4x4 Matrix => Transforms.Camera.CreateOrthographicMatrix(AspectRatio.AsValue(1), VerticalFOV, ZNear, ZFar);
+
         #endregion
     }
 

+ 88 - 0
src/SharpGLTF.Core/Transforms/Camera.cs

@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Transforms
+{
+    /// <summary>
+    /// Utility class to calculate camera matrices
+    /// </summary>
+    /// <see href="https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#projection-matrices"/>
+    public static class Camera
+    {
+        /// <summary>
+        /// Calculates an orthographic projection matrix.
+        /// </summary>
+        /// <param name="xmag">Magnification in the X axis.</param>
+        /// <param name="ymag">Magnification in the Y axis.</param>
+        /// <param name="znear">Distance to the near pane in the Z axis.</param>
+        /// <param name="zfar">Distance to the far plane in the Z axis.</param>
+        /// <returns>A projection matrix</returns>
+        public static Matrix4x4 CreateOrthographicMatrix(float xmag, float ymag, float znear, float zfar)
+        {
+            Guard.MustBeGreaterThanOrEqualTo(znear, 0, nameof(znear));
+            Guard.MustBeGreaterThanOrEqualTo(zfar, 0, nameof(zfar));
+            Guard.MustBeGreaterThan(zfar, znear, nameof(zfar));
+            Guard.MustBeLessThan(zfar, float.PositiveInfinity, nameof(zfar));
+
+            var d = znear - zfar;
+
+            var x = 1 / xmag;
+            var y = 1 / ymag;
+            var z = 2 / d;
+            var w = (zfar + znear) / d;
+
+            return new Matrix4x4
+                (
+                x, 0, 0, 0,
+                0, y, 0, 0,
+                0, 0, z, w,
+                0, 0, 0, 1
+                );
+        }
+
+        /// <summary>
+        /// Calculates a perspective projection matrix.
+        /// </summary>
+        /// <param name="aspectRatio">The aspect ratio between horizontal and vertical. (optional)</param>
+        /// <param name="yfov">The vertical field of view, in radians.</param>
+        /// <param name="znear">Distance to the near pane in the Z axis.</param>
+        /// <param name="zfar">Distance to the far plane in the Z axis. Optionally, this value can be positive infinity</param>
+        /// <returns>A projection matrix</returns>
+        public static Matrix4x4 CreatePerspectiveMatrix(float aspectRatio, float yfov, float znear, float zfar = float.PositiveInfinity)
+        {
+            Guard.MustBeGreaterThan(aspectRatio, 0, nameof(aspectRatio));
+            Guard.MustBeGreaterThan(yfov, 0, nameof(yfov));
+            Guard.MustBeLessThan(yfov, (float)Math.PI, nameof(yfov));
+
+            Guard.MustBeGreaterThanOrEqualTo(znear, 0, nameof(znear));
+            Guard.MustBeGreaterThanOrEqualTo(zfar, 0, nameof(zfar));
+            Guard.MustBeGreaterThan(zfar, znear, nameof(zfar));
+
+            var v = (float)Math.Tan(0.5 * yfov);
+
+            var x = 1 / (aspectRatio * v);
+            var y = 1 / v;
+            var z = -1f;
+            var w = -2 * znear;
+
+            if (!float.IsPositiveInfinity(zfar))
+            {
+                var d = znear - zfar;
+                z = (zfar + znear) / d;
+                w = (2f * zfar * znear) / d;
+            }
+
+            return new Matrix4x4
+                (
+                x, 0, 0, 0,
+                0, y, 0, 0,
+                0, 0, z, w,
+                0, 0, -1, 0
+                );
+        }
+
+
+    }
+}