From 26d2c589f50f443dc70eac6466fae7515c0e564d Mon Sep 17 00:00:00 2001
From: Robert Menzel <menzel@informatik.rwth-aachen.de>
Date: Mon, 23 Nov 2015 18:07:20 +0100
Subject: [PATCH] added more projection matrix types

---
 include/ACGL/Scene/CameraBase.hh    | 11 +++++
 include/ACGL/Scene/FixedCamera.hh   | 14 ++++++-
 include/ACGL/Scene/GenericCamera.hh | 21 ++++++++--
 src/ACGL/Scene/FixedCamera.cc       | 65 ++++++++++++++++++++++++++++-
 src/ACGL/Scene/GenericCamera.cc     | 42 ++++++++++++++++---
 5 files changed, 141 insertions(+), 12 deletions(-)

diff --git a/include/ACGL/Scene/CameraBase.hh b/include/ACGL/Scene/CameraBase.hh
index dc5af02b..52c69756 100644
--- a/include/ACGL/Scene/CameraBase.hh
+++ b/include/ACGL/Scene/CameraBase.hh
@@ -45,6 +45,17 @@ public:
      * @return the 2-dimensional size of the viewport
      */
     virtual glm::uvec2 getViewportSize() const = 0;
+    /**
+     * @brief Gets the near clipping plane as a distance from the camera.
+     * @return the near clipping plane
+     */
+    virtual float getNearClippingPlane() const = 0;
+    /**
+     * @brief Gets the far clipping plane as a distance from the camera. Note that it could be inf!
+     * Not all projection matrices have a real far plane but some are unlimited!
+     * @return the near clipping plane
+     */
+    virtual float getFarClippingPlane() const = 0;
 };
 
 }
diff --git a/include/ACGL/Scene/FixedCamera.hh b/include/ACGL/Scene/FixedCamera.hh
index 7140e40e..f1ca79b7 100644
--- a/include/ACGL/Scene/FixedCamera.hh
+++ b/include/ACGL/Scene/FixedCamera.hh
@@ -6,7 +6,8 @@ namespace ACGL{
 namespace Scene{
 
 /**
- * @brief A fixed camera
+ * @brief A fixed camera where all attributes are set explicitly except for the near/far plane
+ *        which are derrived from the projection matrix.
  */
 class FixedCamera : public CameraBase
 {
@@ -17,6 +18,11 @@ private:
     glm::mat4 mProjectionMatrix;
     glm::uvec2 mViewportSize;
 
+    // will get calculated based on the projection matrix
+    // so there are no explicit setters for this!
+    float mNearPlane;
+    float mFarPlane;
+
 public:
     /// CAUTION: default ctor with zero values
     FixedCamera();
@@ -32,11 +38,15 @@ public:
 
     // Getter, Setter for Camera ProjectionMatrix
     virtual glm::mat4 getProjectionMatrix() const { return mProjectionMatrix; }
-    virtual void setProjectionMatrix(glm::mat4 const& _val) { mProjectionMatrix = _val; }
+    virtual void setProjectionMatrix(glm::mat4 const& _val);
 
     // Getter, Setter for Camera ViewportSize
     virtual glm::uvec2 getViewportSize() const { return mViewportSize; }
     virtual void setViewportSize(glm::uvec2 const& _val) { mViewportSize = _val; }
+
+    // getters for near/far plane (far can be inf!)
+    virtual float getNearClippingPlane() const { return mNearPlane; }
+    virtual float getFarClippingPlane()  const { return mFarPlane; }
 };
 
 }
diff --git a/include/ACGL/Scene/GenericCamera.hh b/include/ACGL/Scene/GenericCamera.hh
index 23db9586..c26662f4 100644
--- a/include/ACGL/Scene/GenericCamera.hh
+++ b/include/ACGL/Scene/GenericCamera.hh
@@ -57,10 +57,23 @@ class GenericCamera : public CameraBase, public MoveableObject
         // Helping enums:
         //
         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+        /// use the DX reverse mode with:
+        /// * an infinite far plane
+        /// * a float z-buffer
+        /// * glClearDepth(0.0)
+        /// * glDepthFunc(GL_GREATER)
+        /// * either:
+        ///     * glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE) (DX style mapping of the Z values)
+        ///   or:
+        ///     * glDepthRangedNV(-1.0, 1.0)
+        ///
+        /// this way near will get mapped to 1.0 and infinite to 0.0
         enum ProjectionMode
         {
             ISOMETRIC_PROJECTION   = 0,
-            PERSPECTIVE_PROJECTION
+            PERSPECTIVE_PROJECTION_OPENGL,    // maps to -1..1
+            PERSPECTIVE_PROJECTION_DX_REVERSE // maps to  1..0
         };
 
         enum StereoMode
@@ -172,16 +185,17 @@ class GenericCamera : public CameraBase, public MoveableObject
 		 */
         void setNearClippingPlane( float _plane );
         /// Gets the near clip distance
-        float getNearClippingPlane() const { return mNearClippingPlane; }
+        virtual float getNearClippingPlane() const { return mNearClippingPlane; }
 
 		/**
 		 * Set the far clipping plane of the camera.
 		 * The plane is defined only by a distance from the camera.
+         * This distance might be inf!
          * @param _plane        New far clipping plane of the camera.
 		 */
         void setFarClippingPlane( float _plane );
         /// Gets the far clip distance
-        float getFarClippingPlane() const { return mFarClippingPlane; }
+        virtual float getFarClippingPlane() const { return mFarClippingPlane; }
 
         /// Gets size of the viewport
         virtual glm::uvec2 getViewportSize() const { return mViewportSize; }
@@ -237,7 +251,6 @@ class GenericCamera : public CameraBase, public MoveableObject
         /// Gets the currently active projection matrix (depends on stereo mode and current eye)
         virtual glm::mat4 getProjectionMatrix() const;
 
-
         ///////////////////////////////////////////////////////////////////////////////////////////
         //
         // Explicit view and projection matrices. These DON'T obey the set stereo/eye settings.
diff --git a/src/ACGL/Scene/FixedCamera.cc b/src/ACGL/Scene/FixedCamera.cc
index 2dbf9ae3..f2bc2291 100644
--- a/src/ACGL/Scene/FixedCamera.cc
+++ b/src/ACGL/Scene/FixedCamera.cc
@@ -16,9 +16,72 @@ FixedCamera::FixedCamera()
 FixedCamera::FixedCamera(const glm::vec3 &_pos, const glm::mat4 &_view, const glm::mat4 &_proj, const glm::uvec2 &_viewport) :
     mPosition(_pos),
     mViewMatrix(_view),
-    mProjectionMatrix(_proj),
     mViewportSize(_viewport)
 {
+    setProjectionMatrix( _proj );
+}
+
+void FixedCamera::setProjectionMatrix(glm::mat4 const& _val)
+{
+    mProjectionMatrix = _val;
+
+    //
+    // Calculate near and far plane from the matrix
+    // As the matrix does not have to be a standard OpenGL
+    // projection matrix, we can't derrive the values from
+    // the matrix entries directly but we have to reproject
+    // points from the NDC cube... ...but as some matrices
+    // project to -1..1 and others to 0..1 we have to figure
+    // this out first... ...keeping in mind, that some also
+    // invert the range inside of the Z buffer, so Z=1 might
+    // be the near plane while Z=0 is the far plane...
+    // ...oh, and the far plane might be at infinity...
+    //
+
+    //
+    // First step, figure out which values project to
+    // -1, 0 and 1:
+    //
+    glm::mat4 invProj     = glm::inverse(mProjectionMatrix);
+    glm::vec4 tmp = invProj * glm::vec4( 0.0f, 0.0f, -1.0f, 1.0f);
+    float reproj_minusOne = tmp.z / tmp.w;
+    tmp = invProj * glm::vec4( 0.0f, 0.0f, 0.0f, 1.0f);
+    float reproj_Zero = tmp.z / tmp.w;
+    tmp = invProj * glm::vec4( 0.0f, 0.0f, 1.0f, 1.0f);
+    float reproj_One = tmp.z / tmp.w;
+
+    //
+    // Next step, figure out which value is the near plane and which is the far plane.
+    //
+    // right handed coordinate system -> the camera was looking along the negative Z axis
+    // if it was an OpenGL matrix, the projections will result in -near, -something, -far
+    // if it was an OpenGL matrix with infinit far plane, the projections will result in -near, -something, -inf
+    // if it was a reverse DX style matrix it will result in +near, -inf, -near
+
+    if (reproj_minusOne > 0.0f) {
+        // near,far mapped to 1,0 or 0,1 as -1 was mapped to a positive value which is behind the camera
+        // if we assume we are looking along the negative Z axis!
+        if (-reproj_Zero > -reproj_One) {
+            // the far plane is mapped to 0 -> inverse DX style
+            mFarPlane  = -reproj_Zero;
+            mNearPlane = -reproj_One;
+        } else {
+            // the near plane is mapped to zero -> DX style
+            mFarPlane  = -reproj_One;
+            mNearPlane = -reproj_Zero;
+        }
+    } else {
+        // OpenGL style, mapping to -1..1 or 1..-1
+        if (-reproj_minusOne > -reproj_One) {
+            // the far plane is mapped to -1 -> inverse GL style
+            mFarPlane  = -reproj_minusOne;
+            mNearPlane = -reproj_One;
+        } else {
+            // the near plane is mapped to -1 -> GL style
+            mFarPlane  = -reproj_One;
+            mNearPlane = -reproj_minusOne;
+        }
+    }
 }
 
 }
diff --git a/src/ACGL/Scene/GenericCamera.cc b/src/ACGL/Scene/GenericCamera.cc
index 08d93e62..dfa3d9cc 100644
--- a/src/ACGL/Scene/GenericCamera.cc
+++ b/src/ACGL/Scene/GenericCamera.cc
@@ -21,7 +21,7 @@ using namespace ACGL::Utils::StringHelpers;
 using namespace std;
 
 GenericCamera::GenericCamera() :
-    mProjectionMode(PERSPECTIVE_PROJECTION),
+    mProjectionMode(PERSPECTIVE_PROJECTION_OPENGL),
     mStereoMode(MONO),
     mCurrentEye(EYE_LEFT),
     mHorizontalFieldOfView(75.0),
@@ -247,10 +247,42 @@ glm::mat4 GenericCamera::getMonoProjectionMatrix() const
         projectionMatrix[ 2][3] = -(mFarClippingPlane+mNearClippingPlane)/(mFarClippingPlane-mNearClippingPlane);
         projectionMatrix[ 3][3] =  1.0;
     }
-    else if ( mProjectionMode == PERSPECTIVE_PROJECTION )
+    else if ( mProjectionMode == PERSPECTIVE_PROJECTION_OPENGL )
     {
-        projectionMatrix = glm::perspective( glm::radians( (float)getHorizontalFieldOfView()), (float)getAspectRatio(), (float)mNearClippingPlane, (float)mFarClippingPlane );
+        if (std::isinf(mFarClippingPlane)) {
+
+            float e = 1.0f / tan(ACGL::Math::Functions::calcDegToRad( getVerticalFieldOfView()*0.5f ));
+            const float a = getAspectRatio();
+
+            // infinite Perspective matrix reversed mapping to 1..-1
+            projectionMatrix = {
+                   e/a, 0.0f,  0.0f,                    0.0f,
+                  0.0f,    e,  0.0f,                    0.0f,
+                  0.0f, 0.0f, -1.0f,                   -1.0f,
+                  0.0f, 0.0f, -2.0*mNearClippingPlane,  0.0f
+            };
+        } else {
+            projectionMatrix = glm::perspective( glm::radians( (float)getHorizontalFieldOfView()), (float)getAspectRatio(), (float)mNearClippingPlane, (float)mFarClippingPlane );
+        }
+    } else if ( mProjectionMode == PERSPECTIVE_PROJECTION_DX_REVERSE )
+    {
+        if (std::isinf(mFarClippingPlane)) {
+
+            float e = 1.0f / tan(ACGL::Math::Functions::calcDegToRad( getVerticalFieldOfView()*0.5f ));
+            const float a = getAspectRatio();
+
+            // infinite Perspective matrix reversed mapping to 1..0
+            projectionMatrix = {
+                  e/a,  0.0f, 0.0f,               0.0f,
+                  0.0f,    e, 0.0f,               0.0f,
+                  0.0f, 0.0f, 0.0f,              -1.0f,
+                  0.0f, 0.0f, mNearClippingPlane, 0.0f
+            };
+        } else {
+            assert(0 && "unsupported projection mode");
+        }
     }
+
     else assert(0 && "unsupported projection mode");
 
     return projectionMatrix;
@@ -320,7 +352,7 @@ std::string GenericCamera::storeStateToString() const
     state += toString( mPosition ) + " | ";
     state += toString( mRotationMatrix ) + " | ";
     if ( mProjectionMode == ISOMETRIC_PROJECTION )   state += "ISOMETRIC_PROJECTION | ";
-    if ( mProjectionMode == PERSPECTIVE_PROJECTION ) state += "PERSPECTIVE_PROJECTION | ";
+    if ( mProjectionMode == PERSPECTIVE_PROJECTION_OPENGL ) state += "PERSPECTIVE_PROJECTION | ";
     if ( mStereoMode     == MONO)                    state += "MONO | ";
     if ( mStereoMode     == PARALLEL_SHIFT)          state += "PARALLEL_SHIFT | ";
     if ( mStereoMode     == OFF_AXIS)                state += "OFF_AXIS | ";
@@ -358,7 +390,7 @@ void GenericCamera::setStateFromString( const std::string &_state )
     mPosition       = toVec3( token[pos++] );
     mRotationMatrix = toMat3( token[pos++] );
     if ( token[pos] == "ISOMETRIC_PROJECTION" )   mProjectionMode = ISOMETRIC_PROJECTION;
-    if ( token[pos] == "PERSPECTIVE_PROJECTION" ) mProjectionMode = PERSPECTIVE_PROJECTION;
+    if ( token[pos] == "PERSPECTIVE_PROJECTION" ) mProjectionMode = PERSPECTIVE_PROJECTION_OPENGL;
     pos++;
     if ( token[pos]     == "MONO")                mStereoMode = MONO;
     if ( token[pos]     == "PARALLEL_SHIFT")      mStereoMode = PARALLEL_SHIFT;
-- 
GitLab