Commit 21372dc2 authored by Andy Nichols's avatar Andy Nichols
Browse files

Add additional projection types to support steroscopic rendering

parent 39dc87a6
Pipeline #4557 passed with stage
in 4 minutes and 46 seconds
......@@ -41,7 +41,9 @@ Module {
name: "QSSGCameraProjectionMode"
values: {
"Perspective": 0,
"Orthographic": 1
"Orthographic": 1,
"Frustum": 2,
"Custom": 3
}
}
Property { name: "clipNear"; type: "float" }
......@@ -49,9 +51,12 @@ Module {
Property { name: "fieldOfView"; type: "float" }
Property { name: "isFieldOfViewHorizontal"; type: "bool" }
Property { name: "projectionMode"; type: "QSSGCameraProjectionMode" }
Property { name: "scaleMode"; type: "QSSGCameraScaleModes" }
Property { name: "scaleAnchor"; type: "QSSGCameraScaleAnchors" }
Property { name: "enableFrustumCulling"; type: "bool" }
Property { name: "frustumTop"; type: "float" }
Property { name: "frustumBottom"; type: "float" }
Property { name: "frustumRight"; type: "float" }
Property { name: "frustumLeft"; type: "float" }
Property { name: "customProjection"; type: "QMatrix4x4" }
Signal {
name: "clipNearChanged"
Parameter { name: "clipNear"; type: "float" }
......@@ -68,14 +73,6 @@ Module {
name: "isFieldOfViewHorizontalChanged"
Parameter { name: "isFieldOfViewHorizontal"; type: "bool" }
}
Signal {
name: "scaleModeChanged"
Parameter { name: "scaleMode"; type: "QSSGCameraScaleModes" }
}
Signal {
name: "scaleAnchorChanged"
Parameter { name: "scaleAnchor"; type: "QSSGCameraScaleAnchors" }
}
Signal {
name: "projectionModeChanged"
Parameter { name: "projectionMode"; type: "QSSGCameraProjectionMode" }
......@@ -84,6 +81,26 @@ Module {
name: "enableFrustumCullingChanged"
Parameter { name: "enableFrustumCulling"; type: "bool" }
}
Signal {
name: "frustumTopChanged"
Parameter { name: "frustumTop"; type: "float" }
}
Signal {
name: "frustumBottomChanged"
Parameter { name: "frustumBottom"; type: "float" }
}
Signal {
name: "frustumRightChanged"
Parameter { name: "frustumRight"; type: "float" }
}
Signal {
name: "frustumLeftChanged"
Parameter { name: "frustumLeft"; type: "float" }
}
Signal {
name: "customProjectionChanged"
Parameter { name: "customProjection"; type: "QMatrix4x4" }
}
Method {
name: "setClipNear"
Parameter { name: "clipNear"; type: "float" }
......@@ -100,14 +117,6 @@ Module {
name: "setIsFieldOfViewHorizontal"
Parameter { name: "isFieldOFViewHorizontal"; type: "bool" }
}
Method {
name: "setScaleMode"
Parameter { name: "scaleMode"; type: "QSSGCameraScaleModes" }
}
Method {
name: "setScaleAnchor"
Parameter { name: "scaleAnchor"; type: "QSSGCameraScaleAnchors" }
}
Method {
name: "setProjectionMode"
Parameter { name: "projectionMode"; type: "QSSGCameraProjectionMode" }
......@@ -116,6 +125,26 @@ Module {
name: "setEnableFrustumCulling"
Parameter { name: "enableFrustumCulling"; type: "bool" }
}
Method {
name: "setFrustumTop"
Parameter { name: "frustumTop"; type: "float" }
}
Method {
name: "setFrustumBottom"
Parameter { name: "frustumBottom"; type: "float" }
}
Method {
name: "setFrustumRight"
Parameter { name: "frustumRight"; type: "float" }
}
Method {
name: "setFrustumLeft"
Parameter { name: "frustumLeft"; type: "float" }
}
Method {
name: "setCustomProjection"
Parameter { name: "customProjection"; type: "QMatrix4x4" }
}
Method {
name: "worldToViewport"
type: "QVector3D"
......
......@@ -41,7 +41,42 @@ QT_BEGIN_NAMESPACE
/*!
\qmltype Camera
\inqmlmodule QtQuick3D
\brief Defines a Camera node for viewing the content of a 3D scene.
\brief Defines a Camera for viewing the content of a 3D scene.
A Camera is always necessary view the content of a 3D scene. A camera
defines how to project the content of a 3D scene into a 2D coordinate space
which can then be used on a 2D surface. When a camera is present in the scene
it can be used to direct what is displayed in a View3D.
To determine the projection of this camera a high level API is provided.
First it is possible to position this Camera like any other spatial Node in
the scene. This determines where the Camera is in the scene, and what
direction it is facing. The default direction of the camera is such that the
forward vector is looking up the +Z axis, and the up direction vector is up
the +Y axis. With this in mind any transformation applied to the camera as
well as the transformations inherited from it's parent Nodes you can define
exactly where and in what direction your camera is facing.
The second part of determining the projection of the camera is defining the
frustum that defines the what parts of the scenes are visible, as well as
how they are visible. The Camera API provides multiple levels of abstraction
to determine the shape of the Camera's frustum. By setting the
Camera::projectionMode property it is possible to control what type of
projection is created and how much control is needed.
The high level API for Camera is used by either selection the
Camera::Perspective or Camera::Orthographic projectionModes. This allows for
a sensible default for either of the two main projection types.
For finer grain control of how the frustum is defined, there is the
Camera::Frustum projectionMode. This allows for setting the
Camera::frustumTop, Camera::frustumBottom, Camera::frustomRight, and
Camera::frustumLeft properties. This is useful in creating asymetrical
frustums.
If you need full-control over how the projection matrix is created there is
also the Camera::Custom mode which lets you define the projection matrix
directly.
*/
/*!
......@@ -105,70 +140,103 @@ bool QQuick3DCamera::isFieldOfViewHorizontal() const
}
/*!
* \qmlproperty enumeration Camera::scaleMode
*
* This property defines how the cameras output retangle is scaled to match the viewport.
*
* \list
* \li Camera.Fit
* \li Camera.SameSize
* \li Camera.FitHorizontal
* \li Camera.FitVertical
* \endlist
*
*/
* \internal
*/\
QQuick3DObject::Type QQuick3DCamera::type() const
{
return QQuick3DObject::Camera;
}
QQuick3DCamera::QSSGCameraScaleModes QQuick3DCamera::scaleMode() const
/*!
* \internal
*/\
QSSGRenderCamera *QQuick3DCamera::getCameraNode() const
{
return m_scaleMode;
return m_cameraNode;
}
/*!
\qmlproperty float Camera::frustumTop
This property defines the top plane of the camera view frustum.
\note This property only has an effect when using \c Camera.Frustum for the
Camera::projectionMode property
*/
float QQuick3DCamera::frustumTop() const
{
return m_frustumTop;
}
/*!
* \qmlproperty enumeration Camera::scaleAnchor
*
* This property sets the anchor point to perform scaling when necessary.
*
* \list
* \li Camera.Center
* \li Camera.North
* \li Camera.NorthEast
* \li Camera.East
* \li Camera.SouthEast
* \li Camera.South
* \li Camera.SouthWest
* \li Camera.West
* \li Camera.NorthWest
* \endlist
*/
\qmlproperty float Camera::frustumBottom
QQuick3DCamera::QSSGCameraScaleAnchors QQuick3DCamera::scaleAnchor() const
This property defines the bottom plane of the camera view frustum.
\note This property only has an effect when using \c Camera.Frustum for the
Camera::projectionMode property
*/
float QQuick3DCamera::frustumBottom() const
{
return m_scaleAnchor;
return m_frustumBottom;
}
/*!
* \internal
*/\
QQuick3DObject::Type QQuick3DCamera::type() const
\qmlproperty float Camera::frustumRight
This property defines the right plane of the camera view frustum.
\note This property only has an effect when using \c Camera.Frustum for the
Camera::projectionMode property
*/
float QQuick3DCamera::frustumRight() const
{
return QQuick3DObject::Camera;
return m_frustumRight;
}
/*!
* \internal
*/\
QSSGRenderCamera *QQuick3DCamera::getCameraNode() const
\qmlproperty float Camera::frustumLeft
This property defines the left plane of the camera view frustum.
\note This property only has an effect when using \c Camera.Frustum for the
Camera::projectionMode property
*/
float QQuick3DCamera::frustumLeft() const
{
return m_cameraNode;
return m_frustumLeft;
}
/*!
\qmlproperty float Camera::customProjection
This property defines a custom projection matrix. This property should only
be used for handling more advanced projections.
\note This property only has an effect when using \c Camera.Custom for the
Camera::projectionMode property
*/
QMatrix4x4 QQuick3DCamera::customProjection() const
{
return m_customProjection;
}
/*!
* \qmlproperty enumeration Camera::projectionMode
*
* This property defines the type of projection that will be to render the
* scene. The most common cases are \c Camera.Perspective and
* \c Camera.Orthographic which will automatically generate a projection
* matrix.
*
* It also possible to use either \c Camera.Frustum or \c Camera.Custom to
* have finer control over how the projection is defined.
*
* \list
* \li Camera.Projection
* \li Camera.Perspective
* \li Camera.Orthographic
* \li Camera.Frustum
* \li Camera.Custom
* \endlist
*
*/
......@@ -178,6 +246,30 @@ QQuick3DCamera::QSSGCameraProjectionMode QQuick3DCamera::projectionMode() const
return m_projectionMode;
}
/*!
* \qmlproperty bool Camera::enableFrustumCulling
*
* When enabled this property determines whether frustum culling is enabled for
* this camera. What this means is that when the scene is being rendered only
* items that are within the bounds of the fustum are rendered. For scenes
* where there are lots of expensive items outside of the view of the camera
* time will not be spent rendering content that will never be shown. There is
* however a cost for doing this as the scene has to be iterated on the CPU to
* determine what is and isn't inside the frustum. If you know that everything
* in the scene will always be indie the camera frustum, this step is an
* unnecessary use of resources.
*
* There are also cases with shadowing where shadows can disapear before they
* are out of view because the item causing the shadow is outside of the camera
* frustum, but the shadow it is casting still is.
*
*/
bool QQuick3DCamera::enableFrustumCulling() const
{
return m_enableFrustumCulling;
}
void QQuick3DCamera::setClipNear(float clipNear)
{
if (qFuzzyCompare(m_clipNear, clipNear))
......@@ -218,48 +310,79 @@ void QQuick3DCamera::setIsFieldOfViewHorizontal(bool isFieldOfViewHorizontal)
update();
}
void QQuick3DCamera::setScaleMode(QQuick3DCamera::QSSGCameraScaleModes scaleMode)
void QQuick3DCamera::setProjectionMode(QQuick3DCamera::QSSGCameraProjectionMode projectionMode)
{
if (m_scaleMode == scaleMode)
if (m_projectionMode == projectionMode)
return;
m_scaleMode = scaleMode;
emit scaleModeChanged(m_scaleMode);
m_projectionMode = projectionMode;
emit projectionModeChanged(m_projectionMode);
update();
}
void QQuick3DCamera::setScaleAnchor(QQuick3DCamera::QSSGCameraScaleAnchors scaleAnchor)
void QQuick3DCamera::setEnableFrustumCulling(bool enableFrustumCulling)
{
if (m_scaleAnchor == scaleAnchor)
if (m_enableFrustumCulling == enableFrustumCulling)
return;
m_scaleAnchor = scaleAnchor;
emit scaleAnchorChanged(m_scaleAnchor);
m_enableFrustumCulling = enableFrustumCulling;
emit enableFrustumCullingChanged(m_enableFrustumCulling);
update();
}
void QQuick3DCamera::setFrustumTop(float frustumTop)
{
if (qFuzzyCompare(m_frustumTop, frustumTop))
return;
void QQuick3DCamera::setProjectionMode(QQuick3DCamera::QSSGCameraProjectionMode projectionMode)
m_frustumTop = frustumTop;
emit frustumTopChanged(m_frustumTop);
update();
}
void QQuick3DCamera::setFrustumBottom(float frustumBottom)
{
if (m_projectionMode == projectionMode)
if (qFuzzyCompare(m_frustumBottom, frustumBottom))
return;
m_projectionMode = projectionMode;
emit projectionModeChanged(m_projectionMode);
m_frustumBottom = frustumBottom;
emit frustumBottomChanged(m_frustumBottom);
update();
}
void QQuick3DCamera::setEnableFrustumCulling(bool enableFrustumCulling)
void QQuick3DCamera::setFrustumRight(float frustumRight)
{
if (m_enableFrustumCulling == enableFrustumCulling)
if (qFuzzyCompare(m_frustumRight, frustumRight))
return;
m_enableFrustumCulling = enableFrustumCulling;
emit enableFrustumCullingChanged(m_enableFrustumCulling);
m_frustumRight = frustumRight;
emit frustumRightChanged(m_frustumRight);
update();
}
void QQuick3DCamera::setFrustumLeft(float frustumLeft)
{
if (qFuzzyCompare(m_frustumLeft, frustumLeft))
return;
m_frustumLeft = frustumLeft;
emit frustumLeftChanged(m_frustumLeft);
update();
}
void QQuick3DCamera::setCustomProjection(QMatrix4x4 customProjection)
{
if (m_customProjection == customProjection)
return;
m_customProjection = customProjection;
emit customProjectionChanged(m_customProjection);
update();
}
/*!
* \qmlmethod vector3d Camera::woldToViewport(vector3d worldPos)
*
* Transforms \a worldPos from world space into viewport space. The position
* is normalized, with the top-left of the viewport being [0,0] and
* the botton-right being [1,1]. The returned z value will contain the distance from the
......@@ -304,6 +427,8 @@ QVector3D QQuick3DCamera::worldToViewport(const QVector3D &worldPos) const
}
/*!
* \qmlmethod vector3d Camera::viewportToWorld(vector3d viewportPos)
*
* Transforms \a viewportPos from viewport space into world space. \a The x-, and y
* values of \l viewportPos needs to be normalized, with the top-left of the viewport
* being [0,0] and the botton-right being [1,1]. The z value should be the distance
......@@ -353,16 +478,49 @@ QVector3D QQuick3DCamera::viewportToWorld(const QVector3D &viewportPos) const
return worldPos;
}
/*!
* \qmlproperty bool Camera::enableFrustumCulling
*
*
*
*/
bool QQuick3DCamera::enableFrustumCulling() const
{
return m_enableFrustumCulling;
namespace {
bool updateProjectionFlags(QSSGRenderCamera *camera, QQuick3DCamera::QSSGCameraProjectionMode projectionMode)
{
if (projectionMode == QQuick3DCamera::Perspective) {
if (camera->flags.testFlag(QSSGRenderNode::Flag::Orthographic) ||
camera->flags.testFlag(QSSGRenderNode::Flag::CameraFrustumProjection) ||
camera->flags.testFlag(QSSGRenderNode::Flag::CameraCustomProjection)) {
camera->flags.setFlag(QSSGRenderNode::Flag::CameraFrustumProjection, false);
camera->flags.setFlag(QSSGRenderNode::Flag::CameraCustomProjection, false);
camera->flags.setFlag(QSSGRenderNode::Flag::Orthographic, false);
return true;
}
} else if (projectionMode == QQuick3DCamera::Orthographic) {
if (!camera->flags.testFlag(QSSGRenderNode::Flag::Orthographic) ||
camera->flags.testFlag(QSSGRenderNode::Flag::CameraFrustumProjection) ||
camera->flags.testFlag(QSSGRenderNode::Flag::CameraCustomProjection)) {
camera->flags.setFlag(QSSGRenderNode::Flag::CameraFrustumProjection, false);
camera->flags.setFlag(QSSGRenderNode::Flag::CameraCustomProjection, false);
camera->flags.setFlag(QSSGRenderNode::Flag::Orthographic, true);
return true;
}
} else if (projectionMode == QQuick3DCamera::Frustum) {
if (camera->flags.testFlag(QSSGRenderNode::Flag::Orthographic) ||
!camera->flags.testFlag(QSSGRenderNode::Flag::CameraFrustumProjection) ||
camera->flags.testFlag(QSSGRenderNode::Flag::CameraCustomProjection)) {
camera->flags.setFlag(QSSGRenderNode::Flag::CameraFrustumProjection, true);
camera->flags.setFlag(QSSGRenderNode::Flag::CameraCustomProjection, false);
camera->flags.setFlag(QSSGRenderNode::Flag::Orthographic, false);
return true;
}
} else if (projectionMode == QQuick3DCamera::Custom) {
if (camera->flags.testFlag(QSSGRenderNode::Flag::Orthographic) ||
camera->flags.testFlag(QSSGRenderNode::Flag::CameraFrustumProjection) ||
!camera->flags.testFlag(QSSGRenderNode::Flag::CameraCustomProjection)) {
camera->flags.setFlag(QSSGRenderNode::Flag::CameraFrustumProjection, false);
camera->flags.setFlag(QSSGRenderNode::Flag::CameraCustomProjection, true);
camera->flags.setFlag(QSSGRenderNode::Flag::Orthographic, false);
return true;
}
}
return false;
}
}
/*!
......@@ -382,15 +540,26 @@ QSSGRenderGraphObject *QQuick3DCamera::updateSpatialNode(QSSGRenderGraphObject *
changed |= qUpdateIfNeeded(camera->clipFar, m_clipFar);
changed |= qUpdateIfNeeded(camera->fov, qDegreesToRadians(m_fieldOfView));
changed |= qUpdateIfNeeded(camera->fovHorizontal, m_isFieldOfViewHorizontal);
changed |= qUpdateIfNeeded(camera->scaleMode, QSSGRenderCamera::ScaleModes(m_scaleMode));
changed |= qUpdateIfNeeded(camera->scaleAnchor, QSSGRenderCamera::ScaleAnchors(m_scaleAnchor));
changed |= qUpdateIfNeeded(camera->enableFrustumClipping, m_enableFrustumCulling);
const bool wasOrtho = camera->flags.testFlag(QSSGRenderNode::Flag::Orthographic);
const bool ortho = m_projectionMode == Orthographic;
camera->flags.setFlag(QSSGRenderNode::Flag::Orthographic, ortho);
changed |= wasOrtho != ortho;
// Set Projection mode based properties
switch (m_projectionMode) {
case Orthographic:
break;
case Frustum:
changed |= qUpdateIfNeeded(camera->top, m_frustumTop);
changed |= qUpdateIfNeeded(camera->bottom, m_frustumBottom);
changed |= qUpdateIfNeeded(camera->right, m_frustumRight);
changed |= qUpdateIfNeeded(camera->left, m_frustumLeft);
break;
case Custom:
changed |= qUpdateIfNeeded(camera->projection, m_customProjection);
break;
case Perspective:
break;
}
changed |= updateProjectionFlags(camera, m_projectionMode);
m_cameraNode = camera;
......
......@@ -54,9 +54,15 @@ class Q_QUICK3D_EXPORT QQuick3DCamera : public QQuick3DNode
Q_PROPERTY(float fieldOfView READ fieldOfView WRITE setFieldOfView NOTIFY fieldOfViewChanged)
Q_PROPERTY(bool isFieldOfViewHorizontal READ isFieldOfViewHorizontal WRITE setIsFieldOfViewHorizontal NOTIFY isFieldOfViewHorizontalChanged)
Q_PROPERTY(QSSGCameraProjectionMode projectionMode READ projectionMode WRITE setProjectionMode NOTIFY projectionModeChanged)
Q_PROPERTY(QSSGCameraScaleModes scaleMode READ scaleMode WRITE setScaleMode NOTIFY scaleModeChanged)
Q_PROPERTY(QSSGCameraScaleAnchors scaleAnchor READ scaleAnchor WRITE setScaleAnchor NOTIFY scaleAnchorChanged)
Q_PROPERTY(bool enableFrustumCulling READ enableFrustumCulling WRITE setEnableFrustumCulling NOTIFY enableFrustumCullingChanged)
// Frustum Mode
Q_PROPERTY(float frustumTop READ frustumTop WRITE setFrustumTop NOTIFY frustumTopChanged)
Q_PROPERTY(float frustumBottom READ frustumBottom WRITE setFrustumBottom NOTIFY frustumBottomChanged)
Q_PROPERTY(float frustumRight READ frustumRight WRITE setFrustumRight NOTIFY frustumRightChanged)
Q_PROPERTY(float frustumLeft READ frustumRight WRITE setFrustumLeft NOTIFY frustumLeftChanged)
// Custom Mode
Q_PROPERTY(QMatrix4x4 customProjection READ customProjection WRITE setCustomProjection NOTIFY customProjectionChanged)
public:
enum QSSGCameraScaleModes {
......@@ -82,7 +88,9 @@ public:
enum QSSGCameraProjectionMode {
Perspective,
Orthographic
Orthographic,
Frustum,
Custom
};
Q_ENUM(QSSGCameraProjectionMode)
......@@ -103,26 +111,43 @@ public:
QSSGRenderCamera *getCameraNode() const;
float frustumTop() const;
float frustumBottom() const;
float frustumRight() const;
float frustumLeft() const;
QMatrix4x4 customProjection() const;
public Q_SLOTS:
void setClipNear(float clipNear);
void setClipFar(float clipFar);
void setFieldOfView(float fieldOfView);
void setIsFieldOfViewHorizontal(bool isFieldOFViewHorizontal);
void setScaleMode(QSSGCameraScaleModes scaleMode);
void setScaleAnchor(QSSGCameraScaleAnchors scaleAnchor);
void setProjectionMode(QSSGCameraProjectionMode projectionMode);
void setEnableFrustumCulling(bool enableFrustumCulling);
void setFrustumTop(float frustumTop);
void setFrustumBottom(float frustumBottom);
void setFrustumRight(float frustumRight);
void setFrustumLeft(float frustumLeft);
void setCustomProjection(QMatrix4x4 customProjection);
Q_SIGNALS:
void clipNearChanged(float clipNear);
void clipFarChanged(float clipFar);
void fieldOfViewChanged(float fieldOfView);
void isFieldOfViewHorizontalChanged(bool isFieldOfViewHorizontal);
void scaleModeChanged(QSSGCameraScaleModes scaleMode);
void scaleAnchorChanged(QSSGCameraScaleAnchors scaleAnchor);
void projectionModeChanged(QSSGCameraProjectionMode projectionMode);
void enableFrustumCullingChanged(bool enableFrustumCulling);
void frustumTopChanged(float frustumTop);
void frustumBottomChanged(float frustumBottom);
void frustumRightChanged(float frustumRight);
void frustumLeftChanged(float frustumLeft);
void customProjectionChanged(QMatrix4x4 customProjection);
protected:
QSSGRenderGraphObject *updateSpatialNode(QSSGRenderGraphObject *node) override;
......@@ -130,13 +155,18 @@ private:
float m_clipNear = 10.0f;
float m_clipFar = 10000.0f;
float m_fieldOfView = 60.0f;
QSSGCameraScaleModes m_scaleMode = QSSGCameraScaleModes::Fit;
QSSGCameraScaleAnchors m_scaleAnchor = QSSGCameraScaleAnchors::Center;
bool m_isFieldOfViewHorizontal = false;
QSSGRenderCamera *m_cameraNode = nullptr;
QSSGCameraProjectionMode m_projectionMode = QSSGCameraProjectionMode::Perspective;
bool m_enableFrustumCulling = true;
float m_frustumTop = 0.0f;
float m_frustumBottom = 0.0f;
float m_frustumRight = 0.0f;
float m_frustumLeft = 0.0f;
QMatrix4x4 m_customProjection;