os/graphics/m3g/m3gcore11/src/m3g_camera.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /*
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     5 * under the terms of the License "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description: Camera implementation
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Camera implementation
    23  */
    24 
    25 #ifndef M3G_CORE_INCLUDE
    26 #   error included by m3g_core.c; do not compile separately.
    27 #endif
    28 
    29 #include "m3g_camera.h"
    30 
    31 /* Internal frustum plane enumeration (and testing order!) */
    32 
    33 #define NEAR_PLANE      0
    34 #define FAR_PLANE       1
    35 #define LEFT_PLANE      2
    36 #define RIGHT_PLANE     3
    37 #define BOTTOM_PLANE    4
    38 #define TOP_PLANE       5
    39 
    40 /*----------------------------------------------------------------------
    41  * Private functions
    42  *--------------------------------------------------------------------*/
    43 
    44 /*!
    45  * \internal
    46  * \brief Makes sure that the internal projection matrix is up-to-date
    47  *        and checks if the camera has a zero view volume.
    48  */
    49 static void m3gValidateProjectionMatrix(Camera *camera)
    50 {
    51     M3Gint projType = camera->projType;
    52 
    53     /* The generic matrix is always valid, but for perspective and
    54      * parallel we must regenerate the matrix */
    55     
    56     if (projType != M3G_GENERIC) {
    57         M3Gfloat m[16];
    58 
    59         M3Gfloat clipNear = camera->clipNear;
    60         M3Gfloat clipFar = camera->clipFar;
    61 
    62         if (projType == M3G_PERSPECTIVE) {
    63             
    64             M3Gfloat height = m3gTan(m3gMul(M3G_DEG2RAD * 0.5f,
    65                                             camera->heightFov));
    66             
    67             m[0] = m3gRcp(m3gMul(camera->aspect, height));
    68             m[1] = m[2] = m[3] = 0.f;
    69             
    70             m[4] = 0.f;
    71             m[5] = m3gRcp(height);
    72             m[6] = m[7] = 0.f;
    73             
    74             m[8] = m[9] = 0.f;
    75             m[10] = m3gDiv(-m3gAdd(clipFar, clipNear),
    76                            m3gSub(clipFar, clipNear));
    77             m[11] = -1.f;
    78             
    79             m[12] = m[13] = 0.f;
    80             m[14] = m3gDiv(m3gMul(m3gMul(-2.f, clipFar), clipNear),
    81                            m3gSub(clipFar, clipNear));
    82             m[15] = 0.f;
    83         }
    84         else if (projType == M3G_PARALLEL) {
    85 
    86             M3Gfloat height = camera->heightFov;
    87             
    88             m[0] = m3gDiv(2.f, m3gMul(camera->aspect, height));
    89             m[1] = m[2] = m[3] = 0.f;
    90 
    91             m[4] = 0.f;
    92             m[5] = m3gDiv(2.f, height);
    93             m[6] = m[7] = 0;
    94             
    95             m[8] = m[9] = 0;
    96             m[10] = m3gDiv(-2.f, m3gSub(clipFar, clipNear));
    97             m[11] = 0.f;
    98             
    99             m[12] = m[13] = 0.f;
   100             m[14] = m3gDiv(-m3gAdd(clipFar, clipNear),
   101                            m3gSub(clipFar, clipNear));
   102             m[15] = 1.f;
   103         }
   104         else {
   105             M3G_ASSERT(M3G_FALSE); /* unknown projection type! */
   106         }
   107         m3gSetMatrixColumns(&camera->projMatrix, m); 
   108     }
   109 
   110     {
   111         M3GMatrix im;
   112         if (!m3gMatrixInverse(&im, &camera->projMatrix)) {
   113             camera->zeroViewVolume = M3G_TRUE;
   114         }
   115         else {
   116             camera->zeroViewVolume = M3G_FALSE;
   117         }
   118     }
   119 
   120     camera->frustumPlanesValid = M3G_FALSE;
   121 }
   122 
   123 /*!
   124  * \internal
   125  * \brief Validates the cached view frustum planes
   126  */
   127 static void m3gValidateFrustumPlanes(Camera *camera) 
   128 {
   129     if (!camera->frustumPlanesValid) {
   130         Vec4 *plane;
   131         Vec4 rows[4];
   132         
   133         m3gGetMatrixRows(&camera->projMatrix, (M3Gfloat*) rows);
   134 
   135         plane = &camera->frustumPlanes[LEFT_PLANE];
   136         *plane = rows[3];
   137         m3gAddVec4(plane, &rows[0]);
   138 
   139         plane = &camera->frustumPlanes[RIGHT_PLANE];
   140         *plane = rows[3];
   141         m3gSubVec4(plane, &rows[0]);
   142 
   143         plane = &camera->frustumPlanes[BOTTOM_PLANE];
   144         *plane = rows[3];
   145         m3gAddVec4(plane, &rows[1]);
   146 
   147         plane = &camera->frustumPlanes[TOP_PLANE];
   148         *plane = rows[3];
   149         m3gSubVec4(plane, &rows[1]);
   150 
   151         plane = &camera->frustumPlanes[NEAR_PLANE];
   152         *plane = rows[3];
   153         m3gAddVec4(plane, &rows[2]);
   154 
   155         plane = &camera->frustumPlanes[FAR_PLANE];
   156         *plane = rows[3];
   157         m3gSubVec4(plane, &rows[2]);
   158 
   159         camera->frustumPlanesValid = M3G_TRUE;
   160     }
   161 }
   162 
   163 #undef NEAR_PLANE
   164 #undef FAR_PLANE
   165 #undef LEFT_PLANE
   166 #undef RIGHT_PLANE
   167 #undef BOTTOM_PLANE
   168 #undef TOP_PLANE
   169 
   170 /*----------------------------------------------------------------------
   171  * Internal functions
   172  *--------------------------------------------------------------------*/
   173 
   174 /*!
   175  * \internal
   176  * \brief Overloaded Object3D method.
   177  *
   178  * \param property      animation property
   179  * \retval M3G_TRUE     property supported
   180  * \retval M3G_FALSE    property not supported
   181  */
   182 static M3Gbool m3gCameraIsCompatible(M3Gint property)
   183 {
   184     switch (property) {
   185     case M3G_ANIM_FAR_DISTANCE:
   186     case M3G_ANIM_FIELD_OF_VIEW:
   187     case M3G_ANIM_NEAR_DISTANCE:
   188         return M3G_TRUE;
   189     default:
   190         return m3gNodeIsCompatible(property);
   191     }
   192 }
   193 
   194 /*!
   195  * \internal
   196  * \brief Overloaded Object3D method.
   197  *
   198  * \param obj          Camera object
   199  * \param property      animation property
   200  * \param valueSize     size of value array
   201  * \param value         value array
   202  */
   203 static void m3gCameraUpdateProperty(Object *obj,
   204                                     M3Gint property,
   205                                     M3Gint valueSize,
   206                                     const M3Gfloat *value)
   207 {
   208     Camera *camera = (Camera *) obj;
   209     M3G_VALIDATE_OBJECT(camera);
   210     M3G_ASSERT_PTR(value);
   211 
   212     switch (property) {
   213     case M3G_ANIM_FAR_DISTANCE:
   214         M3G_ASSERT(valueSize >= 1);
   215         camera->clipFar =   (camera->projType == M3G_PERSPECTIVE)
   216                             ? m3gClampFloatPositive(value[0])
   217                             : value[0];
   218         break;
   219     case M3G_ANIM_FIELD_OF_VIEW:
   220         M3G_ASSERT(valueSize >= 1);
   221         camera->heightFov = (camera->projType == M3G_PERSPECTIVE)
   222                             ? m3gClampFloat(value[0], 0.f, 180.f)
   223                             : m3gClampFloatPositive(value[0]);
   224         break;
   225     case M3G_ANIM_NEAR_DISTANCE:
   226         M3G_ASSERT(valueSize >= 1);
   227         camera->clipNear =  (camera->projType == M3G_PERSPECTIVE)
   228                             ? m3gClampFloatPositive(value[0])
   229                             : value[0];
   230         break;
   231     default:
   232         m3gNodeUpdateProperty(obj, property, valueSize, value);
   233         return; /* don't invalidate the matrix */
   234     }
   235 
   236     /* Validate the projection matrix if we changed any of the
   237      * camera parameters */
   238 
   239     m3gValidateProjectionMatrix(camera);
   240 }
   241 
   242 /*!
   243  * \internal
   244  * \brief Overloaded Node method.
   245  *
   246  * Start render setup scene traversal.
   247  *
   248  * \param node Camera object
   249  * \param toCamera transform to camera
   250  * \param alphaFactor total alpha factor
   251  * \param caller caller node
   252  * \param renderQueue RenderQueue
   253  *
   254  * \retval M3G_TRUE continue render setup
   255  * \retval M3G_FALSE abort render setup
   256  */
   257 static M3Gbool m3gCameraSetupRender(Node *self,
   258                                     const Node *caller,
   259                                     SetupRenderState *s,
   260                                     RenderQueue *renderQueue)
   261 {
   262     Node *parent;
   263     M3Gbool success = M3G_TRUE;
   264 
   265     /* Just do the parent node.  Note that we won't be needing the old
   266      * state back after going up the tree, so we can overwrite it. */
   267     
   268     parent = self->parent;
   269 
   270     if (caller != parent && parent != NULL) {
   271         Matrix t;
   272         
   273         M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
   274         if (!m3gGetInverseNodeTransform(self, &t)) {
   275             return M3G_FALSE;
   276         }
   277         m3gMulMatrix(&s->toCamera, &t);
   278         M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
   279 
   280         /* The parent node will update the alpha factor and culling
   281          * mask if necessary, so we need not touch those */
   282         
   283         success = M3G_VFUNC(Node, parent, setupRender)(parent,
   284                                                        self, s, renderQueue);
   285     }
   286 
   287     return success;
   288 }
   289 
   290 /*!
   291  * \internal
   292  * \brief Overloaded Object3D method.
   293  *
   294  * \param originalObj original Camera object
   295  * \param cloneObj pointer to cloned Camera object
   296  * \param pairs array for all object-duplicate pairs
   297  * \param numPairs number of pairs
   298  */
   299 static M3Gbool m3gCameraDuplicate(const Object *originalObj,
   300                                   Object **cloneObj,
   301                                   Object **pairs,
   302                                   M3Gint *numPairs)
   303 {
   304     Camera *original = (Camera *)originalObj;
   305     Camera *clone = (Camera *)m3gCreateCamera(originalObj->interface);
   306     *cloneObj = (Object *)clone;
   307     if (*cloneObj == NULL) {
   308         return M3G_FALSE;
   309     }
   310 
   311     if (m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs)) {
   312         clone->projType = original->projType;
   313         clone->projMatrix = original->projMatrix;
   314         clone->heightFov = original->heightFov;
   315         clone->aspect = original->aspect;
   316         clone->clipNear = original->clipNear;
   317         clone->clipFar = original->clipFar;
   318         clone->zeroViewVolume = original->zeroViewVolume;
   319         return M3G_TRUE;
   320     }
   321     else {
   322         return M3G_FALSE;
   323     }
   324 }
   325 
   326 /*!
   327  * \internal
   328  * \brief Initializes a Camera object. See specification
   329  * for default values.
   330  *
   331  * \param m3g           M3G interface
   332  * \param camera        Camera object
   333  */
   334 static void m3gInitCamera(Interface *m3g, Camera *camera)
   335 {
   336     M3GMatrix m;
   337 
   338     /* Camera is derived from node */
   339     m3gInitNode(m3g, &camera->node, M3G_CLASS_CAMERA);
   340 
   341     /* GENERIC, Identity */
   342     m3gIdentityMatrix(&m);
   343     m3gSetProjectionMatrix(camera, (const M3GMatrix *)&m);
   344 }
   345 
   346 /*!
   347  * \internal
   348  * \brief Sets camera matrix to OpenGL
   349  * projection matrix.
   350  *
   351  * \param camera Camera object
   352  */
   353 static void m3gApplyProjection(const Camera *camera)
   354 {
   355     M3Gfloat t[16];
   356 
   357     m3gGetMatrixColumns(&camera->projMatrix, t);
   358     
   359     glMatrixMode(GL_PROJECTION);
   360     glLoadMatrixf(t);
   361     glMatrixMode(GL_MODELVIEW);
   362 }
   363 
   364 /*!
   365  * \internal
   366  * \brief Returns a pointer to the camera projection matrix
   367  *
   368  * The matrix <em>must not</em> be accessed directly, as only this
   369  * function will ensure that the returned matrix has valid values in
   370  * it.
   371  * 
   372  * \param camera Camera object
   373  * \return a pointer to the projection matrix
   374  */
   375 static const Matrix *m3gProjectionMatrix(const Camera *camera)
   376 {
   377     M3G_VALIDATE_OBJECT(camera);
   378 
   379     return &camera->projMatrix;
   380 }
   381 
   382 /*!
   383  * \internal
   384  * \brief Retrieves a pointer to the six camera space view frustum planes
   385  */
   386 static const Vec4 *m3gFrustumPlanes(const Camera *camera)
   387 {
   388     M3G_VALIDATE_OBJECT(camera);
   389     m3gValidateFrustumPlanes((Camera*) camera);
   390     return camera->frustumPlanes;
   391 }
   392 
   393 /*----------------------------------------------------------------------
   394  * Virtual function table
   395  *--------------------------------------------------------------------*/
   396 
   397 static const NodeVFTable m3gvf_Camera = {
   398     {
   399         {
   400             m3gObjectApplyAnimation,
   401             m3gCameraIsCompatible,
   402             m3gCameraUpdateProperty,
   403             m3gObjectDoGetReferences,
   404             m3gObjectFindID,
   405             m3gCameraDuplicate,
   406             m3gDestroyNode /* no extra clean-up for Camera */
   407         }
   408     },
   409     m3gNodeAlign,
   410     NULL, /* pure virtual DoRender */
   411     m3gNodeGetBBox,
   412     m3gNodeRayIntersect,
   413     m3gCameraSetupRender,
   414     m3gNodeUpdateDuplicateReferences,
   415     m3gNodeValidate
   416 };
   417 
   418 
   419 /*----------------------------------------------------------------------
   420  * Public API functions
   421  *--------------------------------------------------------------------*/
   422 
   423 /*!
   424  * \brief Creates a Camera object.
   425  *
   426  * \param interface     M3G interface
   427  * \retval Camera new Camera object
   428  * \retval NULL Camera creating failed
   429  */
   430 M3G_API M3GCamera m3gCreateCamera(M3GInterface interface)
   431 {
   432     Interface *m3g = (Interface *) interface;
   433     M3G_VALIDATE_INTERFACE(m3g);
   434 
   435     {
   436         Camera *camera =  m3gAllocZ(m3g, sizeof(Camera));
   437 
   438         if (camera != NULL) {
   439             m3gInitCamera(m3g, camera);
   440         }
   441 
   442         return (M3GCamera) camera;
   443     }
   444 }
   445 
   446 /*!
   447  * \brief Sets a parallel projection.
   448  *
   449  * \param handle        Camera object
   450  * \param height        height (=fovy)
   451  * \param aspectRatio   viewport aspect ratio
   452  * \param clipNear      near clipping plane
   453  * \param clipFar       far clipping plane
   454  */
   455 M3G_API void m3gSetParallel(M3GCamera handle,
   456                             M3Gfloat height,
   457                             M3Gfloat aspectRatio,
   458                             M3Gfloat clipNear, M3Gfloat clipFar)
   459 {
   460     Camera *camera = (Camera *) handle;
   461     M3G_VALIDATE_OBJECT(camera);
   462 
   463     if (height <= 0 || aspectRatio <= 0.0f) {
   464         m3gRaiseError(M3G_INTERFACE(camera), M3G_INVALID_VALUE);
   465         return;
   466     }
   467 
   468     camera->projType   = M3G_PARALLEL;
   469     camera->heightFov  = height;
   470     camera->aspect     = aspectRatio;
   471     camera->clipNear   = clipNear;
   472     camera->clipFar    = clipFar;
   473 
   474     m3gValidateProjectionMatrix(camera);
   475 }
   476 
   477 /*!
   478  * \brief Sets a perspective projection.
   479  *
   480  * \param handle        Camera object
   481  * \param fovy          fovy
   482  * \param aspectRatio   viewport aspect ratio
   483  * \param clipNear      near clipping plane
   484  * \param clipFar       far clipping plane
   485  */
   486 M3G_API void m3gSetPerspective(M3GCamera handle,
   487                                M3Gfloat fovy,
   488                                M3Gfloat aspectRatio,
   489                                M3Gfloat clipNear, M3Gfloat clipFar)
   490 {
   491     Camera *camera = (Camera *) handle;
   492     M3G_VALIDATE_OBJECT(camera);
   493 
   494     if (fovy <= 0.0f || fovy >= 180.f
   495         || aspectRatio <= 0.0f
   496         || clipNear <= 0.0f || clipFar <= 0.0f) {
   497         m3gRaiseError(M3G_INTERFACE(camera), M3G_INVALID_VALUE);
   498         return;
   499     }
   500 
   501     camera->projType   = M3G_PERSPECTIVE;
   502     camera->heightFov  = fovy;
   503     camera->aspect     = aspectRatio;
   504     camera->clipNear   = clipNear;
   505     camera->clipFar    = clipFar;
   506 
   507     m3gValidateProjectionMatrix(camera);
   508 }
   509 
   510 /*!
   511  * \brief Sets a generic projection.
   512  *
   513  * \param handle        Camera object
   514  * \param transform     projection matrix
   515  */
   516 M3G_API void m3gSetProjectionMatrix(M3GCamera handle,
   517                                     const M3GMatrix *transform)
   518 {
   519     Camera *camera = (Camera *) handle;
   520     M3G_VALIDATE_OBJECT(camera);
   521 
   522     if (transform == NULL) {
   523         m3gRaiseError(M3G_INTERFACE(camera), M3G_NULL_POINTER);
   524         return;
   525     }
   526     
   527     camera->projType = M3G_GENERIC;    
   528     m3gCopyMatrix(&camera->projMatrix, transform);
   529     
   530     m3gValidateProjectionMatrix(camera);
   531 }
   532 
   533 /*!
   534  * \brief Gets camera matrix.
   535  *
   536  * \param handle        Camera object
   537  * \param transform     projection matrix to fill in
   538  * \return              projection type
   539  */
   540 M3G_API M3Gint m3gGetProjectionAsMatrix(M3GCamera handle,
   541                                         M3GMatrix *transform)
   542 {
   543     Camera *camera = (Camera *) handle;
   544     M3G_VALIDATE_OBJECT(camera);
   545 
   546     if (transform != NULL) {
   547         /* Check for impossible projection matrix */
   548         if (camera->projType != M3G_GENERIC && 
   549             camera->clipFar == camera->clipNear) {
   550             m3gRaiseError(M3G_INTERFACE(camera), M3G_ARITHMETIC_ERROR);
   551             return 0;
   552         }
   553 
   554         m3gCopyMatrix(transform, m3gProjectionMatrix(camera));
   555     }
   556 
   557     return camera->projType;
   558 }
   559 
   560 /*!
   561  * \brief Gets camera parameters.
   562  *
   563  * \param handle        Camera object
   564  * \param params        camera parameters to fill in
   565  * \return              projection type
   566  */
   567 M3G_API M3Gint m3gGetProjectionAsParams(M3GCamera handle, M3Gfloat *params)
   568 {
   569     Camera *camera = (Camera *) handle;
   570     M3G_VALIDATE_OBJECT(camera);
   571 
   572     if (params != NULL && camera->projType != M3G_GENERIC) {
   573         params[0] = camera->heightFov;
   574         params[1] = camera->aspect;
   575         params[2] = camera->clipNear;
   576         params[3] = camera->clipFar;
   577     }
   578 
   579     return camera->projType;
   580 }
   581