os/graphics/m3g/m3gcore11/src/m3g_camera.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/graphics/m3g/m3gcore11/src/m3g_camera.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,581 @@
     1.4 +/*
     1.5 +* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     1.6 +* All rights reserved.
     1.7 +* This component and the accompanying materials are made available
     1.8 +* under the terms of the License "Eclipse Public License v1.0"
     1.9 +* which accompanies this distribution, and is available
    1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.11 +*
    1.12 +* Initial Contributors:
    1.13 +* Nokia Corporation - initial contribution.
    1.14 +*
    1.15 +* Contributors:
    1.16 +*
    1.17 +* Description: Camera implementation
    1.18 +*
    1.19 +*/
    1.20 +
    1.21 +
    1.22 +/*!
    1.23 + * \internal
    1.24 + * \file
    1.25 + * \brief Camera implementation
    1.26 + */
    1.27 +
    1.28 +#ifndef M3G_CORE_INCLUDE
    1.29 +#   error included by m3g_core.c; do not compile separately.
    1.30 +#endif
    1.31 +
    1.32 +#include "m3g_camera.h"
    1.33 +
    1.34 +/* Internal frustum plane enumeration (and testing order!) */
    1.35 +
    1.36 +#define NEAR_PLANE      0
    1.37 +#define FAR_PLANE       1
    1.38 +#define LEFT_PLANE      2
    1.39 +#define RIGHT_PLANE     3
    1.40 +#define BOTTOM_PLANE    4
    1.41 +#define TOP_PLANE       5
    1.42 +
    1.43 +/*----------------------------------------------------------------------
    1.44 + * Private functions
    1.45 + *--------------------------------------------------------------------*/
    1.46 +
    1.47 +/*!
    1.48 + * \internal
    1.49 + * \brief Makes sure that the internal projection matrix is up-to-date
    1.50 + *        and checks if the camera has a zero view volume.
    1.51 + */
    1.52 +static void m3gValidateProjectionMatrix(Camera *camera)
    1.53 +{
    1.54 +    M3Gint projType = camera->projType;
    1.55 +
    1.56 +    /* The generic matrix is always valid, but for perspective and
    1.57 +     * parallel we must regenerate the matrix */
    1.58 +    
    1.59 +    if (projType != M3G_GENERIC) {
    1.60 +        M3Gfloat m[16];
    1.61 +
    1.62 +        M3Gfloat clipNear = camera->clipNear;
    1.63 +        M3Gfloat clipFar = camera->clipFar;
    1.64 +
    1.65 +        if (projType == M3G_PERSPECTIVE) {
    1.66 +            
    1.67 +            M3Gfloat height = m3gTan(m3gMul(M3G_DEG2RAD * 0.5f,
    1.68 +                                            camera->heightFov));
    1.69 +            
    1.70 +            m[0] = m3gRcp(m3gMul(camera->aspect, height));
    1.71 +            m[1] = m[2] = m[3] = 0.f;
    1.72 +            
    1.73 +            m[4] = 0.f;
    1.74 +            m[5] = m3gRcp(height);
    1.75 +            m[6] = m[7] = 0.f;
    1.76 +            
    1.77 +            m[8] = m[9] = 0.f;
    1.78 +            m[10] = m3gDiv(-m3gAdd(clipFar, clipNear),
    1.79 +                           m3gSub(clipFar, clipNear));
    1.80 +            m[11] = -1.f;
    1.81 +            
    1.82 +            m[12] = m[13] = 0.f;
    1.83 +            m[14] = m3gDiv(m3gMul(m3gMul(-2.f, clipFar), clipNear),
    1.84 +                           m3gSub(clipFar, clipNear));
    1.85 +            m[15] = 0.f;
    1.86 +        }
    1.87 +        else if (projType == M3G_PARALLEL) {
    1.88 +
    1.89 +            M3Gfloat height = camera->heightFov;
    1.90 +            
    1.91 +            m[0] = m3gDiv(2.f, m3gMul(camera->aspect, height));
    1.92 +            m[1] = m[2] = m[3] = 0.f;
    1.93 +
    1.94 +            m[4] = 0.f;
    1.95 +            m[5] = m3gDiv(2.f, height);
    1.96 +            m[6] = m[7] = 0;
    1.97 +            
    1.98 +            m[8] = m[9] = 0;
    1.99 +            m[10] = m3gDiv(-2.f, m3gSub(clipFar, clipNear));
   1.100 +            m[11] = 0.f;
   1.101 +            
   1.102 +            m[12] = m[13] = 0.f;
   1.103 +            m[14] = m3gDiv(-m3gAdd(clipFar, clipNear),
   1.104 +                           m3gSub(clipFar, clipNear));
   1.105 +            m[15] = 1.f;
   1.106 +        }
   1.107 +        else {
   1.108 +            M3G_ASSERT(M3G_FALSE); /* unknown projection type! */
   1.109 +        }
   1.110 +        m3gSetMatrixColumns(&camera->projMatrix, m); 
   1.111 +    }
   1.112 +
   1.113 +    {
   1.114 +        M3GMatrix im;
   1.115 +        if (!m3gMatrixInverse(&im, &camera->projMatrix)) {
   1.116 +            camera->zeroViewVolume = M3G_TRUE;
   1.117 +        }
   1.118 +        else {
   1.119 +            camera->zeroViewVolume = M3G_FALSE;
   1.120 +        }
   1.121 +    }
   1.122 +
   1.123 +    camera->frustumPlanesValid = M3G_FALSE;
   1.124 +}
   1.125 +
   1.126 +/*!
   1.127 + * \internal
   1.128 + * \brief Validates the cached view frustum planes
   1.129 + */
   1.130 +static void m3gValidateFrustumPlanes(Camera *camera) 
   1.131 +{
   1.132 +    if (!camera->frustumPlanesValid) {
   1.133 +        Vec4 *plane;
   1.134 +        Vec4 rows[4];
   1.135 +        
   1.136 +        m3gGetMatrixRows(&camera->projMatrix, (M3Gfloat*) rows);
   1.137 +
   1.138 +        plane = &camera->frustumPlanes[LEFT_PLANE];
   1.139 +        *plane = rows[3];
   1.140 +        m3gAddVec4(plane, &rows[0]);
   1.141 +
   1.142 +        plane = &camera->frustumPlanes[RIGHT_PLANE];
   1.143 +        *plane = rows[3];
   1.144 +        m3gSubVec4(plane, &rows[0]);
   1.145 +
   1.146 +        plane = &camera->frustumPlanes[BOTTOM_PLANE];
   1.147 +        *plane = rows[3];
   1.148 +        m3gAddVec4(plane, &rows[1]);
   1.149 +
   1.150 +        plane = &camera->frustumPlanes[TOP_PLANE];
   1.151 +        *plane = rows[3];
   1.152 +        m3gSubVec4(plane, &rows[1]);
   1.153 +
   1.154 +        plane = &camera->frustumPlanes[NEAR_PLANE];
   1.155 +        *plane = rows[3];
   1.156 +        m3gAddVec4(plane, &rows[2]);
   1.157 +
   1.158 +        plane = &camera->frustumPlanes[FAR_PLANE];
   1.159 +        *plane = rows[3];
   1.160 +        m3gSubVec4(plane, &rows[2]);
   1.161 +
   1.162 +        camera->frustumPlanesValid = M3G_TRUE;
   1.163 +    }
   1.164 +}
   1.165 +
   1.166 +#undef NEAR_PLANE
   1.167 +#undef FAR_PLANE
   1.168 +#undef LEFT_PLANE
   1.169 +#undef RIGHT_PLANE
   1.170 +#undef BOTTOM_PLANE
   1.171 +#undef TOP_PLANE
   1.172 +
   1.173 +/*----------------------------------------------------------------------
   1.174 + * Internal functions
   1.175 + *--------------------------------------------------------------------*/
   1.176 +
   1.177 +/*!
   1.178 + * \internal
   1.179 + * \brief Overloaded Object3D method.
   1.180 + *
   1.181 + * \param property      animation property
   1.182 + * \retval M3G_TRUE     property supported
   1.183 + * \retval M3G_FALSE    property not supported
   1.184 + */
   1.185 +static M3Gbool m3gCameraIsCompatible(M3Gint property)
   1.186 +{
   1.187 +    switch (property) {
   1.188 +    case M3G_ANIM_FAR_DISTANCE:
   1.189 +    case M3G_ANIM_FIELD_OF_VIEW:
   1.190 +    case M3G_ANIM_NEAR_DISTANCE:
   1.191 +        return M3G_TRUE;
   1.192 +    default:
   1.193 +        return m3gNodeIsCompatible(property);
   1.194 +    }
   1.195 +}
   1.196 +
   1.197 +/*!
   1.198 + * \internal
   1.199 + * \brief Overloaded Object3D method.
   1.200 + *
   1.201 + * \param obj          Camera object
   1.202 + * \param property      animation property
   1.203 + * \param valueSize     size of value array
   1.204 + * \param value         value array
   1.205 + */
   1.206 +static void m3gCameraUpdateProperty(Object *obj,
   1.207 +                                    M3Gint property,
   1.208 +                                    M3Gint valueSize,
   1.209 +                                    const M3Gfloat *value)
   1.210 +{
   1.211 +    Camera *camera = (Camera *) obj;
   1.212 +    M3G_VALIDATE_OBJECT(camera);
   1.213 +    M3G_ASSERT_PTR(value);
   1.214 +
   1.215 +    switch (property) {
   1.216 +    case M3G_ANIM_FAR_DISTANCE:
   1.217 +        M3G_ASSERT(valueSize >= 1);
   1.218 +        camera->clipFar =   (camera->projType == M3G_PERSPECTIVE)
   1.219 +                            ? m3gClampFloatPositive(value[0])
   1.220 +                            : value[0];
   1.221 +        break;
   1.222 +    case M3G_ANIM_FIELD_OF_VIEW:
   1.223 +        M3G_ASSERT(valueSize >= 1);
   1.224 +        camera->heightFov = (camera->projType == M3G_PERSPECTIVE)
   1.225 +                            ? m3gClampFloat(value[0], 0.f, 180.f)
   1.226 +                            : m3gClampFloatPositive(value[0]);
   1.227 +        break;
   1.228 +    case M3G_ANIM_NEAR_DISTANCE:
   1.229 +        M3G_ASSERT(valueSize >= 1);
   1.230 +        camera->clipNear =  (camera->projType == M3G_PERSPECTIVE)
   1.231 +                            ? m3gClampFloatPositive(value[0])
   1.232 +                            : value[0];
   1.233 +        break;
   1.234 +    default:
   1.235 +        m3gNodeUpdateProperty(obj, property, valueSize, value);
   1.236 +        return; /* don't invalidate the matrix */
   1.237 +    }
   1.238 +
   1.239 +    /* Validate the projection matrix if we changed any of the
   1.240 +     * camera parameters */
   1.241 +
   1.242 +    m3gValidateProjectionMatrix(camera);
   1.243 +}
   1.244 +
   1.245 +/*!
   1.246 + * \internal
   1.247 + * \brief Overloaded Node method.
   1.248 + *
   1.249 + * Start render setup scene traversal.
   1.250 + *
   1.251 + * \param node Camera object
   1.252 + * \param toCamera transform to camera
   1.253 + * \param alphaFactor total alpha factor
   1.254 + * \param caller caller node
   1.255 + * \param renderQueue RenderQueue
   1.256 + *
   1.257 + * \retval M3G_TRUE continue render setup
   1.258 + * \retval M3G_FALSE abort render setup
   1.259 + */
   1.260 +static M3Gbool m3gCameraSetupRender(Node *self,
   1.261 +                                    const Node *caller,
   1.262 +                                    SetupRenderState *s,
   1.263 +                                    RenderQueue *renderQueue)
   1.264 +{
   1.265 +    Node *parent;
   1.266 +    M3Gbool success = M3G_TRUE;
   1.267 +
   1.268 +    /* Just do the parent node.  Note that we won't be needing the old
   1.269 +     * state back after going up the tree, so we can overwrite it. */
   1.270 +    
   1.271 +    parent = self->parent;
   1.272 +
   1.273 +    if (caller != parent && parent != NULL) {
   1.274 +        Matrix t;
   1.275 +        
   1.276 +        M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
   1.277 +        if (!m3gGetInverseNodeTransform(self, &t)) {
   1.278 +            return M3G_FALSE;
   1.279 +        }
   1.280 +        m3gMulMatrix(&s->toCamera, &t);
   1.281 +        M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
   1.282 +
   1.283 +        /* The parent node will update the alpha factor and culling
   1.284 +         * mask if necessary, so we need not touch those */
   1.285 +        
   1.286 +        success = M3G_VFUNC(Node, parent, setupRender)(parent,
   1.287 +                                                       self, s, renderQueue);
   1.288 +    }
   1.289 +
   1.290 +    return success;
   1.291 +}
   1.292 +
   1.293 +/*!
   1.294 + * \internal
   1.295 + * \brief Overloaded Object3D method.
   1.296 + *
   1.297 + * \param originalObj original Camera object
   1.298 + * \param cloneObj pointer to cloned Camera object
   1.299 + * \param pairs array for all object-duplicate pairs
   1.300 + * \param numPairs number of pairs
   1.301 + */
   1.302 +static M3Gbool m3gCameraDuplicate(const Object *originalObj,
   1.303 +                                  Object **cloneObj,
   1.304 +                                  Object **pairs,
   1.305 +                                  M3Gint *numPairs)
   1.306 +{
   1.307 +    Camera *original = (Camera *)originalObj;
   1.308 +    Camera *clone = (Camera *)m3gCreateCamera(originalObj->interface);
   1.309 +    *cloneObj = (Object *)clone;
   1.310 +    if (*cloneObj == NULL) {
   1.311 +        return M3G_FALSE;
   1.312 +    }
   1.313 +
   1.314 +    if (m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs)) {
   1.315 +        clone->projType = original->projType;
   1.316 +        clone->projMatrix = original->projMatrix;
   1.317 +        clone->heightFov = original->heightFov;
   1.318 +        clone->aspect = original->aspect;
   1.319 +        clone->clipNear = original->clipNear;
   1.320 +        clone->clipFar = original->clipFar;
   1.321 +        clone->zeroViewVolume = original->zeroViewVolume;
   1.322 +        return M3G_TRUE;
   1.323 +    }
   1.324 +    else {
   1.325 +        return M3G_FALSE;
   1.326 +    }
   1.327 +}
   1.328 +
   1.329 +/*!
   1.330 + * \internal
   1.331 + * \brief Initializes a Camera object. See specification
   1.332 + * for default values.
   1.333 + *
   1.334 + * \param m3g           M3G interface
   1.335 + * \param camera        Camera object
   1.336 + */
   1.337 +static void m3gInitCamera(Interface *m3g, Camera *camera)
   1.338 +{
   1.339 +    M3GMatrix m;
   1.340 +
   1.341 +    /* Camera is derived from node */
   1.342 +    m3gInitNode(m3g, &camera->node, M3G_CLASS_CAMERA);
   1.343 +
   1.344 +    /* GENERIC, Identity */
   1.345 +    m3gIdentityMatrix(&m);
   1.346 +    m3gSetProjectionMatrix(camera, (const M3GMatrix *)&m);
   1.347 +}
   1.348 +
   1.349 +/*!
   1.350 + * \internal
   1.351 + * \brief Sets camera matrix to OpenGL
   1.352 + * projection matrix.
   1.353 + *
   1.354 + * \param camera Camera object
   1.355 + */
   1.356 +static void m3gApplyProjection(const Camera *camera)
   1.357 +{
   1.358 +    M3Gfloat t[16];
   1.359 +
   1.360 +    m3gGetMatrixColumns(&camera->projMatrix, t);
   1.361 +    
   1.362 +    glMatrixMode(GL_PROJECTION);
   1.363 +    glLoadMatrixf(t);
   1.364 +    glMatrixMode(GL_MODELVIEW);
   1.365 +}
   1.366 +
   1.367 +/*!
   1.368 + * \internal
   1.369 + * \brief Returns a pointer to the camera projection matrix
   1.370 + *
   1.371 + * The matrix <em>must not</em> be accessed directly, as only this
   1.372 + * function will ensure that the returned matrix has valid values in
   1.373 + * it.
   1.374 + * 
   1.375 + * \param camera Camera object
   1.376 + * \return a pointer to the projection matrix
   1.377 + */
   1.378 +static const Matrix *m3gProjectionMatrix(const Camera *camera)
   1.379 +{
   1.380 +    M3G_VALIDATE_OBJECT(camera);
   1.381 +
   1.382 +    return &camera->projMatrix;
   1.383 +}
   1.384 +
   1.385 +/*!
   1.386 + * \internal
   1.387 + * \brief Retrieves a pointer to the six camera space view frustum planes
   1.388 + */
   1.389 +static const Vec4 *m3gFrustumPlanes(const Camera *camera)
   1.390 +{
   1.391 +    M3G_VALIDATE_OBJECT(camera);
   1.392 +    m3gValidateFrustumPlanes((Camera*) camera);
   1.393 +    return camera->frustumPlanes;
   1.394 +}
   1.395 +
   1.396 +/*----------------------------------------------------------------------
   1.397 + * Virtual function table
   1.398 + *--------------------------------------------------------------------*/
   1.399 +
   1.400 +static const NodeVFTable m3gvf_Camera = {
   1.401 +    {
   1.402 +        {
   1.403 +            m3gObjectApplyAnimation,
   1.404 +            m3gCameraIsCompatible,
   1.405 +            m3gCameraUpdateProperty,
   1.406 +            m3gObjectDoGetReferences,
   1.407 +            m3gObjectFindID,
   1.408 +            m3gCameraDuplicate,
   1.409 +            m3gDestroyNode /* no extra clean-up for Camera */
   1.410 +        }
   1.411 +    },
   1.412 +    m3gNodeAlign,
   1.413 +    NULL, /* pure virtual DoRender */
   1.414 +    m3gNodeGetBBox,
   1.415 +    m3gNodeRayIntersect,
   1.416 +    m3gCameraSetupRender,
   1.417 +    m3gNodeUpdateDuplicateReferences,
   1.418 +    m3gNodeValidate
   1.419 +};
   1.420 +
   1.421 +
   1.422 +/*----------------------------------------------------------------------
   1.423 + * Public API functions
   1.424 + *--------------------------------------------------------------------*/
   1.425 +
   1.426 +/*!
   1.427 + * \brief Creates a Camera object.
   1.428 + *
   1.429 + * \param interface     M3G interface
   1.430 + * \retval Camera new Camera object
   1.431 + * \retval NULL Camera creating failed
   1.432 + */
   1.433 +M3G_API M3GCamera m3gCreateCamera(M3GInterface interface)
   1.434 +{
   1.435 +    Interface *m3g = (Interface *) interface;
   1.436 +    M3G_VALIDATE_INTERFACE(m3g);
   1.437 +
   1.438 +    {
   1.439 +        Camera *camera =  m3gAllocZ(m3g, sizeof(Camera));
   1.440 +
   1.441 +        if (camera != NULL) {
   1.442 +            m3gInitCamera(m3g, camera);
   1.443 +        }
   1.444 +
   1.445 +        return (M3GCamera) camera;
   1.446 +    }
   1.447 +}
   1.448 +
   1.449 +/*!
   1.450 + * \brief Sets a parallel projection.
   1.451 + *
   1.452 + * \param handle        Camera object
   1.453 + * \param height        height (=fovy)
   1.454 + * \param aspectRatio   viewport aspect ratio
   1.455 + * \param clipNear      near clipping plane
   1.456 + * \param clipFar       far clipping plane
   1.457 + */
   1.458 +M3G_API void m3gSetParallel(M3GCamera handle,
   1.459 +                            M3Gfloat height,
   1.460 +                            M3Gfloat aspectRatio,
   1.461 +                            M3Gfloat clipNear, M3Gfloat clipFar)
   1.462 +{
   1.463 +    Camera *camera = (Camera *) handle;
   1.464 +    M3G_VALIDATE_OBJECT(camera);
   1.465 +
   1.466 +    if (height <= 0 || aspectRatio <= 0.0f) {
   1.467 +        m3gRaiseError(M3G_INTERFACE(camera), M3G_INVALID_VALUE);
   1.468 +        return;
   1.469 +    }
   1.470 +
   1.471 +    camera->projType   = M3G_PARALLEL;
   1.472 +    camera->heightFov  = height;
   1.473 +    camera->aspect     = aspectRatio;
   1.474 +    camera->clipNear   = clipNear;
   1.475 +    camera->clipFar    = clipFar;
   1.476 +
   1.477 +    m3gValidateProjectionMatrix(camera);
   1.478 +}
   1.479 +
   1.480 +/*!
   1.481 + * \brief Sets a perspective projection.
   1.482 + *
   1.483 + * \param handle        Camera object
   1.484 + * \param fovy          fovy
   1.485 + * \param aspectRatio   viewport aspect ratio
   1.486 + * \param clipNear      near clipping plane
   1.487 + * \param clipFar       far clipping plane
   1.488 + */
   1.489 +M3G_API void m3gSetPerspective(M3GCamera handle,
   1.490 +                               M3Gfloat fovy,
   1.491 +                               M3Gfloat aspectRatio,
   1.492 +                               M3Gfloat clipNear, M3Gfloat clipFar)
   1.493 +{
   1.494 +    Camera *camera = (Camera *) handle;
   1.495 +    M3G_VALIDATE_OBJECT(camera);
   1.496 +
   1.497 +    if (fovy <= 0.0f || fovy >= 180.f
   1.498 +        || aspectRatio <= 0.0f
   1.499 +        || clipNear <= 0.0f || clipFar <= 0.0f) {
   1.500 +        m3gRaiseError(M3G_INTERFACE(camera), M3G_INVALID_VALUE);
   1.501 +        return;
   1.502 +    }
   1.503 +
   1.504 +    camera->projType   = M3G_PERSPECTIVE;
   1.505 +    camera->heightFov  = fovy;
   1.506 +    camera->aspect     = aspectRatio;
   1.507 +    camera->clipNear   = clipNear;
   1.508 +    camera->clipFar    = clipFar;
   1.509 +
   1.510 +    m3gValidateProjectionMatrix(camera);
   1.511 +}
   1.512 +
   1.513 +/*!
   1.514 + * \brief Sets a generic projection.
   1.515 + *
   1.516 + * \param handle        Camera object
   1.517 + * \param transform     projection matrix
   1.518 + */
   1.519 +M3G_API void m3gSetProjectionMatrix(M3GCamera handle,
   1.520 +                                    const M3GMatrix *transform)
   1.521 +{
   1.522 +    Camera *camera = (Camera *) handle;
   1.523 +    M3G_VALIDATE_OBJECT(camera);
   1.524 +
   1.525 +    if (transform == NULL) {
   1.526 +        m3gRaiseError(M3G_INTERFACE(camera), M3G_NULL_POINTER);
   1.527 +        return;
   1.528 +    }
   1.529 +    
   1.530 +    camera->projType = M3G_GENERIC;    
   1.531 +    m3gCopyMatrix(&camera->projMatrix, transform);
   1.532 +    
   1.533 +    m3gValidateProjectionMatrix(camera);
   1.534 +}
   1.535 +
   1.536 +/*!
   1.537 + * \brief Gets camera matrix.
   1.538 + *
   1.539 + * \param handle        Camera object
   1.540 + * \param transform     projection matrix to fill in
   1.541 + * \return              projection type
   1.542 + */
   1.543 +M3G_API M3Gint m3gGetProjectionAsMatrix(M3GCamera handle,
   1.544 +                                        M3GMatrix *transform)
   1.545 +{
   1.546 +    Camera *camera = (Camera *) handle;
   1.547 +    M3G_VALIDATE_OBJECT(camera);
   1.548 +
   1.549 +    if (transform != NULL) {
   1.550 +        /* Check for impossible projection matrix */
   1.551 +        if (camera->projType != M3G_GENERIC && 
   1.552 +            camera->clipFar == camera->clipNear) {
   1.553 +            m3gRaiseError(M3G_INTERFACE(camera), M3G_ARITHMETIC_ERROR);
   1.554 +            return 0;
   1.555 +        }
   1.556 +
   1.557 +        m3gCopyMatrix(transform, m3gProjectionMatrix(camera));
   1.558 +    }
   1.559 +
   1.560 +    return camera->projType;
   1.561 +}
   1.562 +
   1.563 +/*!
   1.564 + * \brief Gets camera parameters.
   1.565 + *
   1.566 + * \param handle        Camera object
   1.567 + * \param params        camera parameters to fill in
   1.568 + * \return              projection type
   1.569 + */
   1.570 +M3G_API M3Gint m3gGetProjectionAsParams(M3GCamera handle, M3Gfloat *params)
   1.571 +{
   1.572 +    Camera *camera = (Camera *) handle;
   1.573 +    M3G_VALIDATE_OBJECT(camera);
   1.574 +
   1.575 +    if (params != NULL && camera->projType != M3G_GENERIC) {
   1.576 +        params[0] = camera->heightFov;
   1.577 +        params[1] = camera->aspect;
   1.578 +        params[2] = camera->clipNear;
   1.579 +        params[3] = camera->clipFar;
   1.580 +    }
   1.581 +
   1.582 +    return camera->projType;
   1.583 +}
   1.584 +