From d4f6d8c17b2c5d9ff3fa2848accf1daab0bdbf9c Mon Sep 17 00:00:00 2001 From: cutealien Date: Thu, 19 Oct 2023 16:04:43 +0000 Subject: [PATCH] Add lens shift support for the camera and the perspective projection functions As Blender docs describe it so nicely: Using lens shift is equivalent to rendering an image with a larger FOV and cropping it off-center. This can be quite useful for architecture renderings, but I guess also has it's use in other situations. Note: Didn't make the ICameraSceneNode functions pure virtual so users don't have to update their cameras for this Also some change in serialization - same as in other places by now, do use existing values as defaults values when they are not found instead of resetting them to 0. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6565 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 1 + include/ICameraSceneNode.h | 17 +++++++++++++ include/matrix4.h | 36 +++++++++++++++------------- source/Irrlicht/CCameraSceneNode.cpp | 31 ++++++++++++++++-------- source/Irrlicht/CCameraSceneNode.h | 7 ++++++ 5 files changed, 65 insertions(+), 27 deletions(-) diff --git a/changes.txt b/changes.txt index 61b5db0..89217af 100644 --- a/changes.txt +++ b/changes.txt @@ -1,6 +1,7 @@ -------------------------- Changes in 1.9 (not yet released) +- Add lens shift support for the camera and the perspective projection functions - TGA loader no longer reduces 24&32 bit TGA's with palettes to 16 bit. Thanks @erlehmann for report: https://irrlicht.sourceforge.io/forum/viewtopic.php?t=52925 - Fix compile error with OS X 10.10 SDK, bug #463. Thanks @Ryan Schmidt for report and patch. - Optimize quaternion::rotationFromTo. Thanks @Robert Eisele for patch and proof (https://raw.org/proof/quaternion-from-two-vectors) diff --git a/include/ICameraSceneNode.h b/include/ICameraSceneNode.h index 245cf7d..773159b 100644 --- a/include/ICameraSceneNode.h +++ b/include/ICameraSceneNode.h @@ -120,6 +120,13 @@ namespace scene /** \return The field of view of the camera in radians. */ virtual f32 getFOV() const =0; + //! Get the horizontal and vertical lens/projection plane shift + /** \return Project plane offset */ + virtual core::vector2df getLensShift() const + { + return core::vector2df(0.f, 0.f); + } + //! Sets the value of the near clipping plane. (default: 1.0f) /** Also changes projection matrix and resets IsOrthogonal flag. \param zn: New z near value. */ @@ -140,6 +147,16 @@ namespace scene \param fovy: New field of view in radians. */ virtual void setFOV(f32 fovy) =0; + //! Set the horizontal and vertical lens/projection plane shift + /** Like rendering a larger field of view and then cropping + it off-center. Allows for things like 2-point perspective. + \param shift: Offset by which the projection plane is moved. + If you move by 1 or -1 it will move the center by half a screen. + Positive X go to the left and positive Y go down. + By default it will be 0,0 */ + virtual void setLensShift(const core::vector2df& shift) + {} + //! Get the view frustum. /** \return The current view frustum. */ virtual const SViewFrustum* getViewFrustum() const =0; diff --git a/include/matrix4.h b/include/matrix4.h index 0c0d2ba..3b894b1 100644 --- a/include/matrix4.h +++ b/include/matrix4.h @@ -311,26 +311,28 @@ namespace core \param zNear: Distance to near plane \param zFar: Distance to far plane param zClipFromZero: Clipping of z can be projected from 0 to w when true (D3D style) and from -w to w when false (OGL style) - \param zSign: 1 for left-handed projection matrix, -1 for right-handed projection matrix */ - CMatrix4& buildProjectionMatrixPerspectiveFov(T sx, T sy, T zNear, T zFar, bool zClipFromZero, T zSign); + \param zSign: 1 for left-handed projection matrix, -1 for right-handed projection matrix + \param shiftX: Shift projection plane of camera left/right + \param shiftY: Shift projection plane of camera up/down */ + CMatrix4& buildProjectionMatrixPerspectiveFov(T sx, T sy, T zNear, T zFar, bool zClipFromZero, T zSign, T shiftX, T shiftY); //! Builds a right-handed perspective projection matrix based on a field of view //\param zClipFromZero: Clipping of z can be projected from 0 to w when true (D3D style) and from -w to w when false (OGL style). - CMatrix4& buildProjectionMatrixPerspectiveFovRH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero=true); + CMatrix4& buildProjectionMatrixPerspectiveFovRH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero=true, f32 shiftX=0.f, f32 shiftY=0.f); //! Builds a left-handed perspective projection matrix based on a field of view - CMatrix4& buildProjectionMatrixPerspectiveFovLH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero=true); + CMatrix4& buildProjectionMatrixPerspectiveFovLH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero=true, f32 shiftX=0.f, f32 shiftY=0.f); //! Builds a left-handed perspective projection matrix based on a field of view, with far plane at infinity CMatrix4& buildProjectionMatrixPerspectiveFovInfinityLH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 epsilon=0); //! Builds a right-handed perspective projection matrix. - CMatrix4& buildProjectionMatrixPerspectiveRH(f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero=true); + CMatrix4& buildProjectionMatrixPerspectiveRH(f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero=true, f32 shiftX=0.f, f32 shiftY=0.f); //! Builds a left-handed perspective projection matrix. //\param widthOfViewVolume: width of clipped near frustum plane //\param heightOfViewVolume: height of clipped near frustum plane - CMatrix4& buildProjectionMatrixPerspectiveLH(f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero=true); + CMatrix4& buildProjectionMatrixPerspectiveLH(f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero=true, f32 shiftX=0.f, f32 shiftY=0.f); //! Builds a left-handed orthogonal projection matrix. //\param zClipFromZero: Clipping of z can be projected from 0 to 1 when true (D3D style) and from -1 to 1 when false (OGL style). @@ -1583,7 +1585,7 @@ namespace core // Builds a perspective projection matrix template - inline CMatrix4& CMatrix4::buildProjectionMatrixPerspectiveFov(T sx, T sy, T zNear, T zFar, bool zClipFromZero, T zSign) + inline CMatrix4& CMatrix4::buildProjectionMatrixPerspectiveFov(T sx, T sy, T zNear, T zFar, bool zClipFromZero, T zSign, T shiftX, T shiftY) { IRR_DEBUG_BREAK_IF(zNear==zFar); //divide by zero M[0] = sx; @@ -1596,8 +1598,8 @@ namespace core M[6] = 0; M[7] = 0; - M[8] = 0; - M[9] = 0; + M[8] = shiftX; + M[9] = shiftY; //M[10] below M[11] = zSign; @@ -1626,23 +1628,23 @@ namespace core // Builds a right-handed perspective projection matrix based on a field of view template inline CMatrix4& CMatrix4::buildProjectionMatrixPerspectiveFovRH( - f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero) + f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero, f32 shiftX, f32 shiftY) { const f64 sy = reciprocal(tan(fieldOfViewRadians*0.5)); IRR_DEBUG_BREAK_IF(aspectRatio==0.f); //divide by zero const T sx = static_cast(sy / aspectRatio); - return buildProjectionMatrixPerspectiveFov(sx, static_cast(sy), zNear, zFar, zClipFromZero, (T)-1); + return buildProjectionMatrixPerspectiveFov(sx, static_cast(sy), zNear, zFar, zClipFromZero, (T)-1, shiftX, shiftY); } // Builds a left-handed perspective projection matrix based on a field of view template inline CMatrix4& CMatrix4::buildProjectionMatrixPerspectiveFovLH( - f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero) + f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero, f32 shiftX, f32 shiftY) { const f64 sy = reciprocal(tan(fieldOfViewRadians*0.5)); IRR_DEBUG_BREAK_IF(aspectRatio==0.f); //divide by zero const T sx = static_cast(sy / aspectRatio); - return buildProjectionMatrixPerspectiveFov(sx, static_cast(sy), zNear, zFar, zClipFromZero, (T)1); + return buildProjectionMatrixPerspectiveFov(sx, static_cast(sy), zNear, zFar, zClipFromZero, (T)1, shiftX, shiftY); } // Builds a left-handed perspective projection matrix based on a field of view, with far plane culling at infinity @@ -1775,26 +1777,26 @@ namespace core // Builds a right-handed perspective projection matrix. template inline CMatrix4& CMatrix4::buildProjectionMatrixPerspectiveRH( - f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero) + f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero, f32 shiftX, f32 shiftY) { IRR_DEBUG_BREAK_IF(widthOfViewVolume==0.f); //divide by zero IRR_DEBUG_BREAK_IF(heightOfViewVolume==0.f); //divide by zero const T sx = (T)(2*zNear/widthOfViewVolume); const T sy = (T)(2*zNear/heightOfViewVolume); - return buildProjectionMatrixPerspectiveFov(sx, sy, zNear, zFar, zClipFromZero, (T)-1); + return buildProjectionMatrixPerspectiveFov(sx, sy, zNear, zFar, zClipFromZero, (T)-1, shiftX, shiftY); } // Builds a left-handed perspective projection matrix. template inline CMatrix4& CMatrix4::buildProjectionMatrixPerspectiveLH( - f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero) + f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero, f32 shiftX, f32 shiftY) { IRR_DEBUG_BREAK_IF(widthOfViewVolume==0.f); //divide by zero IRR_DEBUG_BREAK_IF(heightOfViewVolume==0.f); //divide by zero const T sx = (T)(2*zNear/widthOfViewVolume); const T sy = (T)(2*zNear/heightOfViewVolume); - return buildProjectionMatrixPerspectiveFov(sx, sy, zNear, zFar, zClipFromZero, (T)1); + return buildProjectionMatrixPerspectiveFov(sx, sy, zNear, zFar, zClipFromZero, (T)1, shiftX, shiftY); } diff --git a/source/Irrlicht/CCameraSceneNode.cpp b/source/Irrlicht/CCameraSceneNode.cpp index 3c97ee5..7bbc7c3 100644 --- a/source/Irrlicht/CCameraSceneNode.cpp +++ b/source/Irrlicht/CCameraSceneNode.cpp @@ -201,6 +201,11 @@ f32 CCameraSceneNode::getFOV() const return Fovy; } +core::vector2df CCameraSceneNode::getLensShift() const +{ + return LensShift; +} + void CCameraSceneNode::setNearValue(f32 f) { @@ -231,10 +236,15 @@ void CCameraSceneNode::setFOV(f32 f) recalculateProjectionMatrix(); } +void CCameraSceneNode::setLensShift(const core::vector2df& shift) +{ + LensShift = shift; + recalculateProjectionMatrix(); +} void CCameraSceneNode::recalculateProjectionMatrix() { - ViewArea.getTransform ( video::ETS_PROJECTION ).buildProjectionMatrixPerspectiveFovLH(Fovy, Aspect, ZNear, ZFar, HasD3DStyleProjectionMatrix); + ViewArea.getTransform ( video::ETS_PROJECTION ).buildProjectionMatrixPerspectiveFovLH(Fovy, Aspect, ZNear, ZFar, HasD3DStyleProjectionMatrix, LensShift.X, LensShift.Y); IsOrthogonal = false; } @@ -330,6 +340,7 @@ void CCameraSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeR out->addFloat("Aspect", Aspect); out->addFloat("ZNear", ZNear); out->addFloat("ZFar", ZFar); + out->addVector2d("LensShift", LensShift); out->addBool("Binding", TargetAndRotationAreBound); out->addBool("ReceiveInput", InputReceiverEnabled); } @@ -339,15 +350,15 @@ void CCameraSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttribute { ICameraSceneNode::deserializeAttributes(in, options); - Target = in->getAttributeAsVector3d("Target"); - UpVector = in->getAttributeAsVector3d("UpVector"); - Fovy = in->getAttributeAsFloat("Fovy"); - Aspect = in->getAttributeAsFloat("Aspect"); - ZNear = in->getAttributeAsFloat("ZNear"); - ZFar = in->getAttributeAsFloat("ZFar"); - TargetAndRotationAreBound = in->getAttributeAsBool("Binding"); - if ( in->findAttribute("ReceiveInput") ) - InputReceiverEnabled = in->getAttributeAsBool("ReceiveInput"); + Target = in->getAttributeAsVector3d("Target", Target); + UpVector = in->getAttributeAsVector3d("UpVector", UpVector); + Fovy = in->getAttributeAsFloat("Fovy", Fovy); + Aspect = in->getAttributeAsFloat("Aspect", Aspect); + ZNear = in->getAttributeAsFloat("ZNear", ZNear); + ZFar = in->getAttributeAsFloat("ZFar", ZFar); + LensShift = in->getAttributeAsVector2d("LensShift", LensShift); + TargetAndRotationAreBound = in->getAttributeAsBool("Binding", TargetAndRotationAreBound); + InputReceiverEnabled = in->getAttributeAsBool("ReceiveInput", InputReceiverEnabled); recalculateProjectionMatrix(); recalculateViewArea(); diff --git a/source/Irrlicht/CCameraSceneNode.h b/source/Irrlicht/CCameraSceneNode.h index 2f6d4e4..f7fa1f2 100644 --- a/source/Irrlicht/CCameraSceneNode.h +++ b/source/Irrlicht/CCameraSceneNode.h @@ -95,6 +95,9 @@ namespace scene //! \return Field of view of the camera virtual f32 getFOV() const IRR_OVERRIDE; + //! Get the horizontal and vertical lens/projection plane shift + virtual core::vector2df getLensShift() const IRR_OVERRIDE; + //! Sets the value of the near clipping plane. (default: 1.0f) virtual void setNearValue(f32 zn) IRR_OVERRIDE; @@ -107,6 +110,9 @@ namespace scene //! Sets the field of view (Default: PI / 3.5f) virtual void setFOV(f32 fovy) IRR_OVERRIDE; + //! Set the horizontal and vertical lens/projection plane shift + virtual void setLensShift(const core::vector2df& shift) IRR_OVERRIDE; + //! PreRender event virtual void OnRegisterSceneNode() IRR_OVERRIDE; @@ -162,6 +168,7 @@ namespace scene f32 Aspect; // Aspect ratio. f32 ZNear; // value of the near view-plane. f32 ZFar; // Z-value of the far view-plane. + core::vector2df LensShift; // For rendering off-center SViewFrustum ViewArea; core::matrix4 Affector;