Переглянути джерело

Added SetProjection() to camera, which sets custom projection matrix. This is reset by modifying any of the standard projection parameters (farClip, FOV etc.) Make sure camera's actual far & near clip are based on the projection matrix.

Lasse Öörni 9 роки тому
батько
коміт
bc8074a6f5

+ 1 - 0
Source/Urho3D/AngelScript/GraphicsAPI.cpp

@@ -120,6 +120,7 @@ static void RegisterCamera(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Camera", "void set_fillMode(FillMode)", asMETHOD(Camera, SetFillMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Camera", "FillMode get_fillMode() const", asMETHOD(Camera, GetFillMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Camera", "const Frustum& get_frustum() const", asMETHOD(Camera, GetFrustum), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Camera", "void set_projection(const Matrix4&in)", asMETHOD(Camera, SetProjection), asCALL_THISCALL);
     engine->RegisterObjectMethod("Camera", "Matrix4 get_projection() const", asMETHOD(Camera, GetProjection), asCALL_THISCALL);
     engine->RegisterObjectMethod("Camera", "Matrix4 get_gpuProjection() const", asMETHOD(Camera, GetGPUProjection), asCALL_THISCALL);
     engine->RegisterObjectMethod("Camera", "const Matrix3x4& get_view() const", asMETHOD(Camera, GetView), asCALL_THISCALL);

+ 42 - 18
Source/Urho3D/Graphics/Camera.cpp

@@ -241,25 +241,44 @@ void Camera::SetUseClipping(bool enable)
 void Camera::SetClipPlane(const Plane& plane)
 {
     clipPlane_ = plane;
-    projectionDirty_ = true;
     MarkNetworkUpdate();
 }
 
-
 void Camera::SetFlipVertical(bool enable)
 {
     flipVertical_ = enable;
     MarkNetworkUpdate();
 }
 
+void Camera::SetProjection(const Matrix4& projection)
+{
+    projection_ = projection;
+    Matrix4 projInverse = projection_.Inverse();
+
+    // Calculate the actual near & far clip from the custom matrix
+    projNearClip_ = (projInverse * Vector3(0.0f, 0.0f, 0.0f)).z_;
+    projFarClip_ = (projInverse * Vector3(0.0f, 0.0f, 1.0f)).z_;
+    projectionDirty_ = false;
+    autoAspectRatio_ = false;
+    frustumDirty_ = true;
+    // Called due to autoAspectRatio changing state, the projection itself is not serialized
+    MarkNetworkUpdate();
+}
+
 float Camera::GetNearClip() const
 {
-    // Orthographic camera has always near clip at 0 to avoid trouble with shader depth parameters,
-    // and unlike in perspective mode there should be no depth buffer precision issue
-    if (!orthographic_)
-        return nearClip_;
-    else
-        return 0.0f;
+    if (projectionDirty_)
+        UpdateProjection();
+
+    return projNearClip_;
+}
+
+float Camera::GetFarClip() const
+{
+    if (projectionDirty_)
+        UpdateProjection();
+
+    return projFarClip_;
 }
 
 const Frustum& Camera::GetFrustum() const
@@ -282,8 +301,8 @@ Frustum Camera::GetSplitFrustum(float nearClip, float farClip) const
     if (projectionDirty_)
         UpdateProjection();
 
-    nearClip = Max(nearClip, GetNearClip());
-    farClip = Min(farClip, farClip_);
+    nearClip = Max(nearClip, projNearClip_);
+    farClip = Min(farClip, projFarClip_);
     if (farClip < nearClip)
         farClip = nearClip;
 
@@ -311,8 +330,8 @@ Frustum Camera::GetViewSpaceSplitFrustum(float nearClip, float farClip) const
     if (projectionDirty_)
         UpdateProjection();
 
-    nearClip = Max(nearClip, GetNearClip());
-    farClip = Min(farClip, farClip_);
+    nearClip = Max(nearClip, projNearClip_);
+    farClip = Min(farClip, projFarClip_);
     if (farClip < nearClip)
         farClip = nearClip;
     
@@ -513,7 +532,7 @@ Matrix3x4 Camera::GetEffectiveWorldTransform() const
 
 bool Camera::IsProjectionValid() const
 {
-    return farClip_ > GetNearClip();
+    return GetFarClip() > GetNearClip();
 }
 
 const Matrix3x4& Camera::GetView() const
@@ -581,13 +600,15 @@ void Camera::OnMarkedDirty(Node* node)
 
 void Camera::UpdateProjection() const
 {
+    // Start from a zero matrix in case it was custom previously
+    projection_ = Matrix4::ZERO;
+
     if (!orthographic_)
     {
-        float nearClip = GetNearClip();
         float h = (1.0f / tanf(fov_ * M_DEGTORAD * 0.5f)) * zoom_;
         float w = h / aspectRatio_;
-        float q = farClip_ / (farClip_ - nearClip);
-        float r = -q * nearClip;
+        float q = farClip_ / (farClip_ - nearClip_);
+        float r = -q * nearClip_;
 
         projection_.m00_ = w;
         projection_.m02_ = projectionOffset_.x_ * 2.0f;
@@ -596,7 +617,8 @@ void Camera::UpdateProjection() const
         projection_.m22_ = q;
         projection_.m23_ = r;
         projection_.m32_ = 1.0f;
-        projection_.m33_ = 0.0f;
+        projNearClip_ = nearClip_;
+        projFarClip_ = farClip_;
     }
     else
     {
@@ -612,8 +634,10 @@ void Camera::UpdateProjection() const
         projection_.m13_ = projectionOffset_.y_ * 2.0f;
         projection_.m22_ = q;
         projection_.m23_ = r;
-        projection_.m32_ = 0.0f;
         projection_.m33_ = 1.0f;
+        // Near clip does not affect depth accuracy in ortho projection, so let it stay 0 to avoid problems with shader depth parameters
+        projNearClip_ = 0.0f;
+        projFarClip_ = farClip_;
     }
 
     projectionDirty_ = false;

+ 16 - 7
Source/Urho3D/Graphics/Camera.h

@@ -94,11 +94,16 @@ public:
     void SetClipPlane(const Plane& plane);
     /// Set vertical flipping mode. Called internally by View to resolve OpenGL / Direct3D9 rendertarget sampling differences.
     void SetFlipVertical(bool enable);
+    /// Set custom projection matrix, which should be specified in D3D convention with depth range 0 - 1. Disables auto aspect ratio.
+    /** Change any of the standard view parameters (FOV, far clip, zoom etc.) to revert to the standard projection. 
+        Note that the custom projection is not serialized or replicated through the network.
+     */
+    void SetProjection(const Matrix4& projection);
 
-    /// Return far clip distance.
-    float GetFarClip() const { return farClip_; }
+    /// Return far clip distance. If a custom projection matrix is in use, is calculated from it instead of the value assigned with SetFarClip().
+    float GetFarClip() const;
 
-    /// Return near clip distance.
+    /// Return near clip distance. If a custom projection matrix is in use, is calculated from it instead of the value assigned with SetNearClip().
     float GetNearClip() const;
 
     /// Return vertical field of view in degrees.
@@ -133,7 +138,7 @@ public:
 
     /// Return frustum in world space.
     const Frustum& GetFrustum() const;
-    /// Return projection matrix. It's in D3D convention with depth range 0-1.
+    /// Return projection matrix. It's in D3D convention with depth range 0 - 1.
     Matrix4 GetProjection() const;
     /// Return projection matrix converted to API-specific format for use as a shader parameter.
     Matrix4 GetGPUProjection() const;
@@ -149,11 +154,11 @@ public:
     Frustum GetViewSpaceFrustum() const;
     /// Return split frustum in view space.
     Frustum GetViewSpaceSplitFrustum(float nearClip, float farClip) const;
-    /// Return ray corresponding to normalized screen coordinates (0.0 - 1.0), with origin on the near clip plane.
+    /// Return ray corresponding to normalized screen coordinates (0 - 1), with origin on the near clip plane.
     Ray GetScreenRay(float x, float y) const;
-    /// Convert a world space point to normalized screen coordinates (0.0 - 1.0).
+    /// Convert a world space point to normalized screen coordinates (0 - 1).
     Vector2 WorldToScreenPoint(const Vector3& worldPos) const;
-    /// Convert normalized screen coordinates (0.0 - 1.0) and distance along view Z axis (in Z coordinate) to a world space point. The distance can not be closer than the near clip plane.
+    /// Convert normalized screen coordinates (0 - 1) and distance along view Z axis (in Z coordinate) to a world space point. The distance can not be closer than the near clip plane.
     /** Note that a HitDistance() from the camera screen ray is not the same as distance along the view Z axis, as under a perspective projection the ray is likely to not be Z-aligned.
      */
     Vector3 ScreenToWorldPoint(const Vector3& screenPos) const;
@@ -229,6 +234,10 @@ private:
     mutable bool frustumDirty_;
     /// Orthographic mode flag.
     bool orthographic_;
+    /// Cached actual near clip distance.
+    mutable float projNearClip_;
+    /// Cached actual far clip distance.
+    mutable float projFarClip_;
     /// Near clip distance.
     float nearClip_;
     /// Far clip distance.

+ 1 - 0
Source/Urho3D/LuaScript/pkgs/Graphics/Camera.pkg

@@ -27,6 +27,7 @@ class Camera : public Component
     void SetReflectionPlane(const Plane& reflectionPlane);
     void SetUseClipping(bool enable);
     void SetClipPlane(const Plane& clipPlane);
+    void SetProjection(const Matrix4& projection);
 
     float GetFarClip() const;
     float GetNearClip() const;