/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt3D documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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 Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file.  Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: http://www.gnu.org/copyleft/fdl.html.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \ingroup qt3d
    \ingroup qt3d::math
    \since 4.8
    \title Camera and View Frustum
    \page qt3d-camera-frustum.html
    \keyword Camera View Frustum
    \brief Basis for the vamera and view frustum culling test.

    The QGLCamera class provides useful viewing functionality, such as being able
    to position and orient view point within a scene.  In combination with the
    QGLPainter::isCullable() function, basic view frustum culling is available.
    To understand in more depth these concepts, or to verify the implementation
    more details of these are presented here.  To simply use the functionality
    read the documentation.

    \list
    \li QGLCamera
    \li QGLPainter::isCullable()
    \endlist

    \section1 The Default Camera

    Qt 3D uses the OpenGL co-ordinate system, that is a right-handed system.  In
    this system you can think of X as pointing to the right, Y as pointing up and
    Z as pointing toward you.  If you hold your right-hand so that the thumb,
    index and middle fingers are all at right angles to each other, then

    \list
    \li Thumb - X axis - points to the right
    \li Index - Y axis - points upward
    \li Middle - Z axis - points toward you
    \endlist

    When you first create a QGLCamera object, by default it has
    \code
    QGLCamera camera;
    qDebug() camera;
    --
    // displays:
    QGLCamera
        projection: Perspective -- viewsize: 2 x 2
        near-plane: 5 -- far-plane: 1000 -- field-of-view: 0
        rotation: 0  -- motion adjust: QVector3D(0, 0, 1)  -- aspect adjust: true
        eye: QVector3D(0, 0, 10) -- center: QVector3D(0, 0, 0) -- up: QVector3D(0, 1, 0)
    \endcode

    In the visualisation just mentioned, with positive Z pointing toward you the
    camera is sitting at the tip of your middle finger, looking in the same
    direction you are, that is down toward the negative end of the Z axis.

    \section1 Positioning a Custom Camera.

    In this image we have a camera positioned with the following settings:
    \code
    camera.setNearPlane(2.2f);
    camera.setFarPlane(14.0f);
    camera.setViewSize(QSizeF(1.257f, 1.257f));
    camera.setEye(QVector3D(0, 0, -8.0f));
    \endcode

    This screen capture from a 3D modelling program shows how this camera is
    set up in a scene, where its viewing a cube.  The image shows a purple area
    which indicates the \e{view frustum}, which you can think of for
    now as the area captured by the camera.  Actually its more correct to think
    of the volume captured by the camera, and imagine that the purple indication
    in the image is the shadow cast by this volume.

    \image view-frustum-angle-shot.png

    The odd value for view size comes from the far plane size which was set at
    8 x 8 when creating the diagram: by similar triangles (8 / 14) x 2.2 = 1.257.
    There is no API call for setting the size of the far-plane, same as in OpenGL.
    So to get this 8 x 8 far plane we had to work out the near plane size.

    \image view-frustum-origin.png

    This plan view shows the parameters of the camera by reference to a grid.
    You can see that the camera is situated - by QGLCamera::setEye() - to a point
    8 units back along the negative z axis.  The far plane is 14 units away from
    the eye - set by QGLCamera::setFarPlane().

    Note that here also the camera is sitting down the negative z axis, looking
    back toward us - the opposite of the default.  Its near plane and far plane
    are also much closer.  The camera is still pointing to the origin - where a
    3D cube is situated, ready to be viewed by the camera.

    \section1 The View Frustum

    The camera can only see a \b part of the scene - not all of it.  Anything
    too far to the camera's right or left is out of the range of its view, and
    even though that part of the scene may contain geometry that is sent to the GPU
    for processing, those triangles will not be visible on the screen.  The GPU
    will clip them away - but it still has to do work to do this.

    The part of the scene which the camera can see, and which will get displayed
    is called the \b{View Frustum}.  A Frustum is a geometric shape, like a
    pyramid with its top cut off.  It has sloping sides due to the perspective
    transformation performed by OpenGL, when simulating the effect of the camera
    in the 3D scene.

    The cube in this scene has 6 sides, just like the view frustum, so we can
    compare the names of these:

    \table
    \header
    \li Cube
    \li View Frustum
    \row
    \li Front
    \li Near Plane
    \row
    \li Back
    \li Far Plane
    \row
    \li Left side
    \li Left side
    \row
    \li Right side
    \li Right side
    \row
    \li Top
    \li Top
    \row
    \li Bottom
    \li Bottom
    \endtable

    Like the cube, the frustum is a solid, and can contain things.  You can think of
    it as having a set of bounds.  Anything inside these bounds is rendered by OpenGL.

    \image view-frustum-10-degrees-angle-shot.png

    In this image, we see the camera as a black-line wire-frame.  The rectangle of the
    front of the camera represents the \b{aperture}.

    The triangular purple colored area on the floor corresponds to the camera's eye
    and spreads out, as determined by the size of the camera's aperture.  If you extended
    the lines from the camera's eye to the four corners of its aperture, those lines would
    define the view frustum.  That view frustum would be the same size as the purple
    view triangle, except the part behind the camera aperture is sliced off.

    Anything that falls inside the black wire-frame of the camera itself \b{does not
    get displayed}.

    Because the scene needs to be projected onto a camera aperture of a non-zero size,
    this means there is this area between the eye of the camera and its aperture which
    cannot be displayed.  Notice that the camera aperture and the Near Plane of the view
    frustum are the same thing.

    We can also say that anything which falls outside the fiew frustum \b{does not get
    displayed} including anything in front of the near plane.  Poor camera positioning
    in a scene can often result in this near plane culling which can produce odd visual
    effects as geometry appears and disappears when it passes in front and behind of
    the near plane.

    \section1 View Frustum Culling

    Carefully examine the next 3 screenshots, which show the same plan view of the
    camera, cube and view - except the cube is rotated around the eye of the camera
    by 10 degrees each time.

    \image view-frustum-10-degrees.png

    In this image the cube is slightly out of the view frustum.  It still must be sent
    to the GPU, but some of its pixels will be clipped off during rendering.

    \image view-frustum-20-degrees.png

    In this image the cube is mostly out of the view frustum.  It still must be sent
    to the GPU, but most of its pixels will be clipped off during rendering.

    \image view-frustum-30-degrees.png

    In this image the cube is completey out of the view frustum.  Performance could be
    improved by not sending the cubes geometry to the GPU at all.  This test must
    be made on the CPU side, so therefore it must be very cheap to do in order to beat
    the cost of just sending it anyway and allowing the GPU to clip away all of it.

    The optimisation technique where you detect this case is called \b{View Frustum
    Culling} and it can a simple and valuable performance enhancement.  Qt 3D implements
    view frustum culling via the QGLPainter::isCullable() function.

    \section1 Aspect-Ratio Correction

    The default camera has a viewport which is 2 x 2 in size, as discussed above.  However
    when the camera is used to display a scene on a QGLAbstractSurface, for example a
    QGLWidgetSurface, the \b{aspect ratio} of that surface is taken into account in
    the View Frustum.  Specifically code in the QGLCamera::projectionMatrix() function
    takes the aspect ratio and expands the default viewport to match.  If the surface is
    wider than it is high, the aspect ratio is greater than 1 and the viewport width is
    multiplied by the aspect ratio to make it larger.  If the surface is higher than it
    is wide, the aspect ratio is less than 1 and the viewport height is divided by the
    aspect ratio to make it larger.  The result is the viewport aspect ratio matches that
    of the surface it is to be displayed on.

    \code
    QGLWidget widget;              // creates a 640 x 480 widget by default
    QGLPainter painter(&widget)    // internally makes a 640 x 480 QGLWidgetSurface
    qreal asp = painter.currentSurface()->aspectRatio();
    // asp == 1.3333 which is (640 / 480)
    QGLCamera camera;              // default camera as above
    painter.setCamera(&camera);    // applies the aspect ratio to the perspective projection
    QMatrix4x4 p = painter.projectionMatrix().top();
    QMatrix4x4 q;
    q.frustum(-asp, asp, -1, 1, camera.nearPlane(), camera.farPlane());
    qDebug() << q == p;                     // prints "true"
    \endcode

    One result of this is that clipping to the view frustum can only be done with the
    knowledge of the surface because more of the scene is visible to the left and right
    than would be suggested by the default camera settings.
*/
