summaryrefslogtreecommitdiffstats
path: root/src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp')
-rw-r--r--src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp496
1 files changed, 496 insertions, 0 deletions
diff --git a/src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp b/src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp
new file mode 100644
index 0000000..b9f9c20
--- /dev/null
+++ b/src/runtimerender/graphobjects/Qt3DSRenderCamera.cpp
@@ -0,0 +1,496 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderCamera.h"
+#include "Qt3DSRenderPresentation.h"
+#include "foundation/Qt3DSVec2.h"
+#include "render/Qt3DSRenderTexture2D.h"
+#include "render/Qt3DSRenderContext.h"
+#include "Qt3DSTextRenderer.h"
+
+#include <qmath.h>
+
+using namespace qt3ds::render;
+
+namespace {
+
+QT3DSF32 GetAspectRatio(const NVRenderRectF &inViewport)
+{
+ return inViewport.m_Height != 0 ? inViewport.m_Width / inViewport.m_Height : 0.0f;
+}
+
+QT3DSF32 GetAspectRatio(const QT3DSVec2 &inDimensions)
+{
+ return inDimensions.y != 0 ? inDimensions.x / inDimensions.y : 0.0f;
+}
+
+bool IsCameraVerticalAdjust(CameraScaleModes::Enum inMode, QT3DSF32 inDesignAspect,
+ QT3DSF32 inActualAspect)
+{
+ return (inMode == CameraScaleModes::Fit && inActualAspect >= inDesignAspect)
+ || inMode == CameraScaleModes::FitVertical;
+}
+
+bool IsCameraHorizontalAdjust(CameraScaleModes::Enum inMode, QT3DSF32 inDesignAspect,
+ QT3DSF32 inActualAspect)
+{
+ return (inMode == CameraScaleModes::Fit && inActualAspect < inDesignAspect)
+ || inMode == CameraScaleModes::FitHorizontal;
+}
+
+bool IsFitTypeScaleMode(CameraScaleModes::Enum inMode)
+{
+ return inMode == CameraScaleModes::Fit || inMode == CameraScaleModes::FitHorizontal
+ || inMode == CameraScaleModes::FitVertical;
+}
+
+struct SPinCameraResult
+{
+ NVRenderRectF m_Viewport;
+ NVRenderRectF m_VirtualViewport;
+ SPinCameraResult(NVRenderRectF v, NVRenderRectF vv)
+ : m_Viewport(v)
+ , m_VirtualViewport(vv)
+ {
+ }
+};
+// Scale and transform the projection matrix to respect the camera anchor attribute
+// and the scale mode.
+SPinCameraResult PinCamera(const NVRenderRectF &inViewport, QT3DSVec2 inDesignDims,
+ QT3DSMat44 &ioPerspectiveMatrix, CameraScaleModes::Enum inScaleMode,
+ CameraScaleAnchors::Enum inPinLocation)
+{
+ NVRenderRectF viewport(inViewport);
+ NVRenderRectF idealViewport(inViewport.m_X, inViewport.m_Y, inDesignDims.x, inDesignDims.y);
+ QT3DSF32 designAspect = GetAspectRatio(inDesignDims);
+ QT3DSF32 actualAspect = GetAspectRatio(inViewport);
+ if (IsFitTypeScaleMode(inScaleMode)) {
+ idealViewport.m_Width = viewport.m_Width;
+ idealViewport.m_Height = viewport.m_Height;
+ }
+ // We move the viewport such that the left, top of the presentation sits against the left top
+ // edge
+ // We only need to translate in X *if* our actual aspect > design aspect
+ // And then we only need to account for whatever centering would happen.
+
+ bool pinLeft = inPinLocation == CameraScaleAnchors::SouthWest
+ || inPinLocation == CameraScaleAnchors::West
+ || inPinLocation == CameraScaleAnchors::NorthWest;
+ bool pinRight = inPinLocation == CameraScaleAnchors::SouthEast
+ || inPinLocation == CameraScaleAnchors::East
+ || inPinLocation == CameraScaleAnchors::NorthEast;
+ bool pinTop = inPinLocation == CameraScaleAnchors::NorthWest
+ || inPinLocation == CameraScaleAnchors::North
+ || inPinLocation == CameraScaleAnchors::NorthEast;
+ bool pinBottom = inPinLocation == CameraScaleAnchors::SouthWest
+ || inPinLocation == CameraScaleAnchors::South
+ || inPinLocation == CameraScaleAnchors::SouthEast;
+
+ if (inScaleMode == CameraScaleModes::SameSize) {
+ // In this case the perspective transform does not center the view,
+ // it places it in the lower-left of the viewport.
+ QT3DSF32 idealWidth = inDesignDims.x;
+ QT3DSF32 idealHeight = inDesignDims.y;
+ if (pinRight)
+ idealViewport.m_X -= ((idealWidth - inViewport.m_Width));
+ else if (!pinLeft)
+ idealViewport.m_X -= ((idealWidth - inViewport.m_Width) / 2.0f);
+
+ if (pinTop)
+ idealViewport.m_Y -= ((idealHeight - inViewport.m_Height));
+ else if (!pinBottom)
+ idealViewport.m_Y -= ((idealHeight - inViewport.m_Height) / 2.0f);
+ } else {
+ // In this case our perspective matrix will center the view and we need to decenter
+ // it as necessary
+ // if we are wider than we are high
+ if (IsCameraVerticalAdjust(inScaleMode, designAspect, actualAspect)) {
+ if (pinLeft || pinRight) {
+ QT3DSF32 idealWidth = inViewport.m_Height * designAspect;
+ QT3DSI32 halfOffset = (QT3DSI32)((idealWidth - inViewport.m_Width) / 2.0f);
+ halfOffset = pinLeft ? halfOffset : -1 * halfOffset;
+ idealViewport.m_X += halfOffset;
+ }
+ } else {
+ if (pinTop || pinBottom) {
+ QT3DSF32 idealHeight = inViewport.m_Width / designAspect;
+ QT3DSI32 halfOffset = (QT3DSI32)((idealHeight - inViewport.m_Height) / 2.0f);
+ halfOffset = pinBottom ? halfOffset : -1 * halfOffset;
+ idealViewport.m_Y += halfOffset;
+ }
+ }
+ }
+
+ ioPerspectiveMatrix = NVRenderContext::ApplyVirtualViewportToProjectionMatrix(
+ ioPerspectiveMatrix, viewport, idealViewport);
+ return SPinCameraResult(viewport, idealViewport);
+}
+}
+
+SCamera::SCamera()
+ : SNode(GraphObjectTypes::Camera)
+ , m_ClipNear(10)
+ , m_ClipFar(10000)
+ , m_FOV(60)
+ , m_FOVHorizontal(false)
+ , m_ScaleMode(CameraScaleModes::Fit)
+ , m_ScaleAnchor(CameraScaleAnchors::Center)
+{
+ TORAD(m_FOV);
+ m_Projection = QT3DSMat44::createIdentity();
+ m_Position = QT3DSVec3(0, 0, -600);
+}
+
+// Code for testing
+SCameraGlobalCalculationResult SCamera::CalculateGlobalVariables(const NVRenderRectF &inViewport,
+ const QT3DSVec2 &inDesignDimensions)
+{
+ bool wasDirty = SNode::CalculateGlobalVariables();
+ return SCameraGlobalCalculationResult(wasDirty,
+ CalculateProjection(inViewport, inDesignDimensions));
+}
+
+bool SCamera::CalculateProjection(const NVRenderRectF &inViewport, const QT3DSVec2 &inDesignDimensions)
+{
+ bool retval = false;
+ if (m_Flags.IsOrthographic())
+ retval = ComputeFrustumOrtho(inViewport, inDesignDimensions);
+ else
+ retval = ComputeFrustumPerspective(inViewport, inDesignDimensions);
+ if (retval) {
+ QT3DSF32 *writePtr(m_Projection.front());
+ m_FrustumScale.x = writePtr[0];
+ m_FrustumScale.y = writePtr[5];
+ PinCamera(inViewport, inDesignDimensions, m_Projection, m_ScaleMode, m_ScaleAnchor);
+ }
+ return retval;
+}
+
+//==============================================================================
+/**
+ * Compute the projection matrix for a perspective camera
+ * @return true if the computed projection matrix is valid
+ */
+bool SCamera::ComputeFrustumPerspective(const NVRenderRectF &inViewport,
+ const QT3DSVec2 &inDesignDimensions)
+{
+ m_Projection = QT3DSMat44::createIdentity();
+ QT3DSF32 theAngleInRadians = verticalFov(inViewport) / 2.0f;
+ QT3DSF32 theDeltaZ = m_ClipFar - m_ClipNear;
+ QT3DSF32 theSine = sinf(theAngleInRadians);
+ QT3DSF32 designAspect = GetAspectRatio(inDesignDimensions);
+ QT3DSF32 theAspectRatio = designAspect;
+ if (IsFitTypeScaleMode(m_ScaleMode))
+ theAspectRatio = GetAspectRatio(inViewport);
+
+ if ((theDeltaZ != 0) && (theSine != 0) && (theAspectRatio != 0)) {
+ QT3DSF32 *writePtr(m_Projection.front());
+ writePtr[10] = -(m_ClipFar + m_ClipNear) / theDeltaZ;
+ writePtr[11] = -1;
+ writePtr[14] = -2 * m_ClipNear * m_ClipFar / theDeltaZ;
+ writePtr[15] = 0;
+
+ if (IsCameraVerticalAdjust(m_ScaleMode, designAspect, theAspectRatio)) {
+ QT3DSF32 theCotangent = cosf(theAngleInRadians) / theSine;
+ writePtr[0] = theCotangent / theAspectRatio;
+ writePtr[5] = theCotangent;
+ } else {
+ QT3DSF32 theCotangent = cosf(theAngleInRadians) / theSine;
+ writePtr[0] = theCotangent / designAspect;
+ writePtr[5] = theCotangent * (theAspectRatio / designAspect);
+ }
+ return true;
+ } else {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+}
+
+//==============================================================================
+/**
+ * Compute the projection matrix for a orthographic camera
+ * @return true if the computed projection matrix is valid
+ */
+bool SCamera::ComputeFrustumOrtho(const NVRenderRectF &inViewport, const QT3DSVec2 &inDesignDimensions)
+{
+ m_Projection = QT3DSMat44::createIdentity();
+
+ QT3DSF32 theDeltaZ = m_ClipFar - m_ClipNear;
+ QT3DSF32 halfWidth = inDesignDimensions.x / 2.0f;
+ QT3DSF32 halfHeight = inDesignDimensions.y / 2.0f;
+ QT3DSF32 designAspect = GetAspectRatio(inDesignDimensions);
+ QT3DSF32 theAspectRatio = designAspect;
+ if (IsFitTypeScaleMode(m_ScaleMode))
+ theAspectRatio = GetAspectRatio(inViewport);
+ if (theDeltaZ != 0) {
+ QT3DSF32 *writePtr(m_Projection.front());
+ writePtr[10] = -2.0f / theDeltaZ;
+ writePtr[11] = 0.0f;
+ writePtr[14] = -(m_ClipNear + m_ClipFar) / theDeltaZ;
+ writePtr[15] = 1.0f;
+ if (IsCameraVerticalAdjust(m_ScaleMode, designAspect, theAspectRatio)) {
+ writePtr[0] = 1.0f / (halfHeight * theAspectRatio);
+ writePtr[5] = 1.0f / halfHeight;
+ } else {
+ writePtr[0] = 1.0f / halfWidth;
+ writePtr[5] = 1.0f / (halfWidth / theAspectRatio);
+ }
+ return true;
+ } else {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+}
+
+QT3DSF32 SCamera::GetOrthographicScaleFactor(const NVRenderRectF &inViewport,
+ const QT3DSVec2 &inDesignDimensions) const
+{
+ if (m_ScaleMode == CameraScaleModes::SameSize)
+ return 1.0f;
+ QT3DSMat44 temp(QT3DSMat44::createIdentity());
+ QT3DSF32 designAspect = GetAspectRatio(inDesignDimensions);
+ QT3DSF32 theAspectRatio = GetAspectRatio(inViewport);
+ if (m_ScaleMode == CameraScaleModes::Fit) {
+ if (theAspectRatio >= designAspect) {
+ return inViewport.m_Width < inDesignDimensions.x ? theAspectRatio / designAspect : 1.0f;
+
+ } else {
+ return inViewport.m_Height < inDesignDimensions.y ? designAspect / theAspectRatio
+ : 1.0f;
+ }
+ } else if (m_ScaleMode == CameraScaleModes::FitVertical) {
+ return (QT3DSF32)inDesignDimensions.y / (QT3DSF32)inViewport.m_Height;
+ } else {
+ return (QT3DSF32)inDesignDimensions.x / (QT3DSF32)inViewport.m_Width;
+ }
+}
+
+QT3DSF32 SCamera::GetTextScaleFactor(const NVRenderRectF &inViewport,
+ const QT3DSVec2 &inDesignDimensions) const
+{
+ return NVMax(1.0f, 1.0f / GetOrthographicScaleFactor(inViewport, inDesignDimensions));
+}
+
+QT3DSMat33 SCamera::GetLookAtMatrix(const QT3DSVec3 &inUpDir, const QT3DSVec3 &inDirection) const
+{
+ QT3DSVec3 theDirection(inDirection);
+
+ theDirection.normalize();
+
+ const QT3DSVec3 &theUpDir(inUpDir);
+
+ // gram-shmidt orthogonalization
+ QT3DSVec3 theCrossDir(theDirection.cross(theUpDir));
+ theCrossDir.normalize();
+ QT3DSVec3 theFinalDir(theCrossDir.cross(theDirection));
+ theFinalDir.normalize();
+ QT3DSF32 multiplier = 1.0f;
+ if (m_Flags.IsLeftHanded())
+ multiplier = -1.0f;
+
+ QT3DSMat33 theResultMatrix(theCrossDir, theFinalDir, multiplier * theDirection);
+ return theResultMatrix;
+}
+
+void SCamera::LookAt(const QT3DSVec3 &inCameraPos, const QT3DSVec3 &inUpDir, const QT3DSVec3 &inTargetPos)
+{
+ QT3DSVec3 theDirection = inTargetPos - inCameraPos;
+ if (m_Flags.IsLeftHanded())
+ theDirection.z *= -1.0f;
+ m_Rotation = GetRotationVectorFromRotationMatrix(GetLookAtMatrix(inUpDir, theDirection));
+ m_Position = inCameraPos;
+ MarkDirty(qt3ds::render::NodeTransformDirtyFlag::TransformIsDirty);
+}
+
+void SCamera::CalculateViewProjectionMatrix(QT3DSMat44 &outMatrix) const
+{
+ QT3DSMat44 globalInverse = m_GlobalTransform.getInverse();
+ outMatrix = m_Projection * globalInverse;
+}
+
+SCuboidRect SCamera::GetCameraBounds(const NVRenderRectF &inViewport,
+ const QT3DSVec2 &inDesignDimensions) const
+{
+ QT3DSMat44 unused(QT3DSMat44::createIdentity());
+ SPinCameraResult theResult =
+ PinCamera(inViewport, inDesignDimensions, unused, m_ScaleMode, m_ScaleAnchor);
+ // find the normalized edges of the view frustum given the renormalization that happens when
+ // pinning the camera.
+ SCuboidRect normalizedCuboid(-1, 1, 1, -1);
+ QT3DSVec2 translation(theResult.m_Viewport.m_X - theResult.m_VirtualViewport.m_X,
+ theResult.m_Viewport.m_Y - theResult.m_VirtualViewport.m_Y);
+ if (m_ScaleMode == CameraScaleModes::SameSize) {
+ // the cuboid ranges are the actual divided by the ideal in this case
+ QT3DSF32 xRange = 2.0f * (theResult.m_Viewport.m_Width / theResult.m_VirtualViewport.m_Width);
+ QT3DSF32 yRange =
+ 2.0f * (theResult.m_Viewport.m_Height / theResult.m_VirtualViewport.m_Height);
+ normalizedCuboid = SCuboidRect(-1, -1 + yRange, -1 + xRange, -1);
+ translation.x /= (theResult.m_VirtualViewport.m_Width / 2.0f);
+ translation.y /= (theResult.m_VirtualViewport.m_Height / 2.0f);
+ normalizedCuboid.Translate(translation);
+ }
+ // fit. This means that two parameters of the normalized cuboid will be -1, 1.
+ else {
+ // In this case our perspective matrix will center the view and we need to decenter
+ // it as necessary
+ QT3DSF32 actualAspect = GetAspectRatio(inViewport);
+ QT3DSF32 designAspect = GetAspectRatio(inDesignDimensions);
+ // if we are wider than we are high
+ QT3DSF32 idealWidth = inViewport.m_Width;
+ QT3DSF32 idealHeight = inViewport.m_Height;
+
+ if (IsCameraVerticalAdjust(m_ScaleMode, designAspect, actualAspect)) {
+ // then we just need to setup the left, right parameters of the cuboid because we know
+ // the top
+ // bottom are -1,1 due to how fit works.
+ idealWidth = (QT3DSF32)ITextRenderer::NextMultipleOf4(
+ (QT3DSU32)(inViewport.m_Height * designAspect + .5f));
+ // halfRange should always be greater than 1.0f.
+ QT3DSF32 halfRange = inViewport.m_Width / idealWidth;
+ normalizedCuboid.m_Left = -halfRange;
+ normalizedCuboid.m_Right = halfRange;
+ translation.x = translation.x / (idealWidth / 2.0f);
+ } else {
+ idealHeight = (QT3DSF32)ITextRenderer::NextMultipleOf4(
+ (QT3DSU32)(inViewport.m_Width / designAspect + .5f));
+ QT3DSF32 halfRange = inViewport.m_Height / idealHeight;
+ normalizedCuboid.m_Bottom = -halfRange;
+ normalizedCuboid.m_Top = halfRange;
+ translation.y = translation.y / (idealHeight / 2.0f);
+ }
+ normalizedCuboid.Translate(translation);
+ }
+ // Given no adjustment in the virtual rect, then this is what we would have.
+
+ return normalizedCuboid;
+}
+
+void SCamera::SetupOrthographicCameraForOffscreenRender(NVRenderTexture2D &inTexture,
+ QT3DSMat44 &outVP)
+{
+ STextureDetails theDetails(inTexture.GetTextureDetails());
+ SCamera theTempCamera;
+ SetupOrthographicCameraForOffscreenRender(inTexture, outVP, theTempCamera);
+}
+
+void SCamera::SetupOrthographicCameraForOffscreenRender(NVRenderTexture2D &inTexture,
+ QT3DSMat44 &outVP, SCamera &outCamera)
+{
+ STextureDetails theDetails(inTexture.GetTextureDetails());
+ SCamera theTempCamera;
+ theTempCamera.m_Flags.SetOrthographic(true);
+ theTempCamera.MarkDirty(NodeTransformDirtyFlag::TransformIsDirty);
+ QT3DSVec2 theDimensions((QT3DSF32)theDetails.m_Width, (QT3DSF32)theDetails.m_Height);
+ theTempCamera.CalculateGlobalVariables(
+ NVRenderRect(0, 0, theDetails.m_Width, theDetails.m_Height), theDimensions);
+ theTempCamera.CalculateViewProjectionMatrix(outVP);
+ outCamera = theTempCamera;
+}
+
+SRay SCamera::Unproject(const QT3DSVec2 &inViewportRelativeCoords, const NVRenderRectF &inViewport,
+ const QT3DSVec2 &inDesignDimensions, bool sceneCameraView) const
+{
+ SRay theRay;
+ QT3DSMat44 tempVal(QT3DSMat44::createIdentity());
+ SPinCameraResult result =
+ PinCamera(inViewport, inDesignDimensions, tempVal, m_ScaleMode, m_ScaleAnchor);
+ QT3DSVec2 globalCoords = inViewport.ToAbsoluteCoords(inViewportRelativeCoords);
+ QT3DSVec2 normalizedCoords =
+ result.m_VirtualViewport.AbsoluteToNormalizedCoordinates(globalCoords);
+ QT3DSVec3 &outOrigin(theRay.m_Origin);
+ QT3DSVec3 &outDir(theRay.m_Direction);
+ QT3DSVec2 inverseFrustumScale(1.0f / m_FrustumScale.x, 1.0f / m_FrustumScale.y);
+ QT3DSVec2 scaledCoords(inverseFrustumScale.x * normalizedCoords.x,
+ inverseFrustumScale.y * normalizedCoords.y);
+
+ if (m_Flags.IsOrthographic()) {
+ outOrigin.x = scaledCoords.x;
+ outOrigin.y = scaledCoords.y;
+ outOrigin.z = 0.0f;
+
+ outDir.x = 0.0f;
+ outDir.y = 0.0f;
+ outDir.z = -1.0f;
+ } else {
+ outOrigin.x = 0.0f;
+ outOrigin.y = 0.0f;
+ outOrigin.z = 0.0f;
+
+ outDir.x = scaledCoords.x;
+ outDir.y = scaledCoords.y;
+ outDir.z = -1.0f;
+ }
+
+ outOrigin = m_GlobalTransform.transform(outOrigin);
+
+ // CalculateNormalMatrix(), but 4x4 matrix to have scale() method
+ QT3DSMat44 theNormalMatrix = m_GlobalTransform.getInverse().getTranspose();
+ if (sceneCameraView) {
+ // When in scene camera view mode, camera scale needs to be inverted.
+ // See QT3DS-3393.
+ const float scaleX = m_GlobalTransform[0][0] / theNormalMatrix[0][0];
+ const float scaleY = m_GlobalTransform[1][1] / theNormalMatrix[1][1];
+ const float scaleZ = m_GlobalTransform[2][2] / theNormalMatrix[2][2];
+ QT3DSVec4 scaleVector(scaleX, scaleY, scaleZ, 1.0);
+ theNormalMatrix.scale(scaleVector);
+ }
+
+ outDir = theNormalMatrix.transform(outDir);
+ outDir.normalize();
+ /*
+ char printBuf[2000];
+ sprintf_s( printBuf, "normCoords %f %f outDir %f %f %f\n"
+ , normalizedCoords.x, normalizedCoords.y, outDir.x, outDir.y, outDir.z );
+ OutputDebugStringA( printBuf );
+ */
+
+ return theRay;
+}
+
+QT3DSVec3 SCamera::UnprojectToPosition(const QT3DSVec3 &inGlobalPos, const SRay &inRay) const
+{
+ QT3DSVec3 theCameraDir = GetDirection();
+ QT3DSVec3 theObjGlobalPos = inGlobalPos;
+ QT3DSF32 theDistance = -1.0f * theObjGlobalPos.dot(theCameraDir);
+ NVPlane theCameraPlane(theCameraDir, theDistance);
+ return inRay.Intersect(theCameraPlane);
+}
+
+QT3DSF32 SCamera::verticalFov(QT3DSF32 aspectRatio) const
+{
+ if (m_FOVHorizontal)
+ return 2.0f * qAtan(qTan(qreal(m_FOV) / 2.0) / qreal(aspectRatio));
+ else
+ return m_FOV;
+}
+
+QT3DSF32 SCamera::verticalFov(const NVRenderRectF &inViewport) const
+{
+ return verticalFov(GetAspectRatio(inViewport));
+}