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 +