Update contrib.
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
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".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
14 * Description: Camera implementation
22 * \brief Camera implementation
25 #ifndef M3G_CORE_INCLUDE
26 # error included by m3g_core.c; do not compile separately.
29 #include "m3g_camera.h"
31 /* Internal frustum plane enumeration (and testing order!) */
37 #define BOTTOM_PLANE 4
40 /*----------------------------------------------------------------------
42 *--------------------------------------------------------------------*/
46 * \brief Makes sure that the internal projection matrix is up-to-date
47 * and checks if the camera has a zero view volume.
49 static void m3gValidateProjectionMatrix(Camera *camera)
51 M3Gint projType = camera->projType;
53 /* The generic matrix is always valid, but for perspective and
54 * parallel we must regenerate the matrix */
56 if (projType != M3G_GENERIC) {
59 M3Gfloat clipNear = camera->clipNear;
60 M3Gfloat clipFar = camera->clipFar;
62 if (projType == M3G_PERSPECTIVE) {
64 M3Gfloat height = m3gTan(m3gMul(M3G_DEG2RAD * 0.5f,
67 m[0] = m3gRcp(m3gMul(camera->aspect, height));
68 m[1] = m[2] = m[3] = 0.f;
71 m[5] = m3gRcp(height);
75 m[10] = m3gDiv(-m3gAdd(clipFar, clipNear),
76 m3gSub(clipFar, clipNear));
80 m[14] = m3gDiv(m3gMul(m3gMul(-2.f, clipFar), clipNear),
81 m3gSub(clipFar, clipNear));
84 else if (projType == M3G_PARALLEL) {
86 M3Gfloat height = camera->heightFov;
88 m[0] = m3gDiv(2.f, m3gMul(camera->aspect, height));
89 m[1] = m[2] = m[3] = 0.f;
92 m[5] = m3gDiv(2.f, height);
96 m[10] = m3gDiv(-2.f, m3gSub(clipFar, clipNear));
100 m[14] = m3gDiv(-m3gAdd(clipFar, clipNear),
101 m3gSub(clipFar, clipNear));
105 M3G_ASSERT(M3G_FALSE); /* unknown projection type! */
107 m3gSetMatrixColumns(&camera->projMatrix, m);
112 if (!m3gMatrixInverse(&im, &camera->projMatrix)) {
113 camera->zeroViewVolume = M3G_TRUE;
116 camera->zeroViewVolume = M3G_FALSE;
120 camera->frustumPlanesValid = M3G_FALSE;
125 * \brief Validates the cached view frustum planes
127 static void m3gValidateFrustumPlanes(Camera *camera)
129 if (!camera->frustumPlanesValid) {
133 m3gGetMatrixRows(&camera->projMatrix, (M3Gfloat*) rows);
135 plane = &camera->frustumPlanes[LEFT_PLANE];
137 m3gAddVec4(plane, &rows[0]);
139 plane = &camera->frustumPlanes[RIGHT_PLANE];
141 m3gSubVec4(plane, &rows[0]);
143 plane = &camera->frustumPlanes[BOTTOM_PLANE];
145 m3gAddVec4(plane, &rows[1]);
147 plane = &camera->frustumPlanes[TOP_PLANE];
149 m3gSubVec4(plane, &rows[1]);
151 plane = &camera->frustumPlanes[NEAR_PLANE];
153 m3gAddVec4(plane, &rows[2]);
155 plane = &camera->frustumPlanes[FAR_PLANE];
157 m3gSubVec4(plane, &rows[2]);
159 camera->frustumPlanesValid = M3G_TRUE;
170 /*----------------------------------------------------------------------
172 *--------------------------------------------------------------------*/
176 * \brief Overloaded Object3D method.
178 * \param property animation property
179 * \retval M3G_TRUE property supported
180 * \retval M3G_FALSE property not supported
182 static M3Gbool m3gCameraIsCompatible(M3Gint property)
185 case M3G_ANIM_FAR_DISTANCE:
186 case M3G_ANIM_FIELD_OF_VIEW:
187 case M3G_ANIM_NEAR_DISTANCE:
190 return m3gNodeIsCompatible(property);
196 * \brief Overloaded Object3D method.
198 * \param obj Camera object
199 * \param property animation property
200 * \param valueSize size of value array
201 * \param value value array
203 static void m3gCameraUpdateProperty(Object *obj,
206 const M3Gfloat *value)
208 Camera *camera = (Camera *) obj;
209 M3G_VALIDATE_OBJECT(camera);
210 M3G_ASSERT_PTR(value);
213 case M3G_ANIM_FAR_DISTANCE:
214 M3G_ASSERT(valueSize >= 1);
215 camera->clipFar = (camera->projType == M3G_PERSPECTIVE)
216 ? m3gClampFloatPositive(value[0])
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]);
225 case M3G_ANIM_NEAR_DISTANCE:
226 M3G_ASSERT(valueSize >= 1);
227 camera->clipNear = (camera->projType == M3G_PERSPECTIVE)
228 ? m3gClampFloatPositive(value[0])
232 m3gNodeUpdateProperty(obj, property, valueSize, value);
233 return; /* don't invalidate the matrix */
236 /* Validate the projection matrix if we changed any of the
237 * camera parameters */
239 m3gValidateProjectionMatrix(camera);
244 * \brief Overloaded Node method.
246 * Start render setup scene traversal.
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
254 * \retval M3G_TRUE continue render setup
255 * \retval M3G_FALSE abort render setup
257 static M3Gbool m3gCameraSetupRender(Node *self,
260 RenderQueue *renderQueue)
263 M3Gbool success = M3G_TRUE;
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. */
268 parent = self->parent;
270 if (caller != parent && parent != NULL) {
273 M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
274 if (!m3gGetInverseNodeTransform(self, &t)) {
277 m3gMulMatrix(&s->toCamera, &t);
278 M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
280 /* The parent node will update the alpha factor and culling
281 * mask if necessary, so we need not touch those */
283 success = M3G_VFUNC(Node, parent, setupRender)(parent,
284 self, s, renderQueue);
292 * \brief Overloaded Object3D method.
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
299 static M3Gbool m3gCameraDuplicate(const Object *originalObj,
304 Camera *original = (Camera *)originalObj;
305 Camera *clone = (Camera *)m3gCreateCamera(originalObj->interface);
306 *cloneObj = (Object *)clone;
307 if (*cloneObj == NULL) {
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;
328 * \brief Initializes a Camera object. See specification
329 * for default values.
331 * \param m3g M3G interface
332 * \param camera Camera object
334 static void m3gInitCamera(Interface *m3g, Camera *camera)
338 /* Camera is derived from node */
339 m3gInitNode(m3g, &camera->node, M3G_CLASS_CAMERA);
341 /* GENERIC, Identity */
342 m3gIdentityMatrix(&m);
343 m3gSetProjectionMatrix(camera, (const M3GMatrix *)&m);
348 * \brief Sets camera matrix to OpenGL
351 * \param camera Camera object
353 static void m3gApplyProjection(const Camera *camera)
357 m3gGetMatrixColumns(&camera->projMatrix, t);
359 glMatrixMode(GL_PROJECTION);
361 glMatrixMode(GL_MODELVIEW);
366 * \brief Returns a pointer to the camera projection matrix
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
372 * \param camera Camera object
373 * \return a pointer to the projection matrix
375 static const Matrix *m3gProjectionMatrix(const Camera *camera)
377 M3G_VALIDATE_OBJECT(camera);
379 return &camera->projMatrix;
384 * \brief Retrieves a pointer to the six camera space view frustum planes
386 static const Vec4 *m3gFrustumPlanes(const Camera *camera)
388 M3G_VALIDATE_OBJECT(camera);
389 m3gValidateFrustumPlanes((Camera*) camera);
390 return camera->frustumPlanes;
393 /*----------------------------------------------------------------------
394 * Virtual function table
395 *--------------------------------------------------------------------*/
397 static const NodeVFTable m3gvf_Camera = {
400 m3gObjectApplyAnimation,
401 m3gCameraIsCompatible,
402 m3gCameraUpdateProperty,
403 m3gObjectDoGetReferences,
406 m3gDestroyNode /* no extra clean-up for Camera */
410 NULL, /* pure virtual DoRender */
413 m3gCameraSetupRender,
414 m3gNodeUpdateDuplicateReferences,
419 /*----------------------------------------------------------------------
420 * Public API functions
421 *--------------------------------------------------------------------*/
424 * \brief Creates a Camera object.
426 * \param interface M3G interface
427 * \retval Camera new Camera object
428 * \retval NULL Camera creating failed
430 M3G_API M3GCamera m3gCreateCamera(M3GInterface interface)
432 Interface *m3g = (Interface *) interface;
433 M3G_VALIDATE_INTERFACE(m3g);
436 Camera *camera = m3gAllocZ(m3g, sizeof(Camera));
438 if (camera != NULL) {
439 m3gInitCamera(m3g, camera);
442 return (M3GCamera) camera;
447 * \brief Sets a parallel projection.
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
455 M3G_API void m3gSetParallel(M3GCamera handle,
457 M3Gfloat aspectRatio,
458 M3Gfloat clipNear, M3Gfloat clipFar)
460 Camera *camera = (Camera *) handle;
461 M3G_VALIDATE_OBJECT(camera);
463 if (height <= 0 || aspectRatio <= 0.0f) {
464 m3gRaiseError(M3G_INTERFACE(camera), M3G_INVALID_VALUE);
468 camera->projType = M3G_PARALLEL;
469 camera->heightFov = height;
470 camera->aspect = aspectRatio;
471 camera->clipNear = clipNear;
472 camera->clipFar = clipFar;
474 m3gValidateProjectionMatrix(camera);
478 * \brief Sets a perspective projection.
480 * \param handle Camera object
482 * \param aspectRatio viewport aspect ratio
483 * \param clipNear near clipping plane
484 * \param clipFar far clipping plane
486 M3G_API void m3gSetPerspective(M3GCamera handle,
488 M3Gfloat aspectRatio,
489 M3Gfloat clipNear, M3Gfloat clipFar)
491 Camera *camera = (Camera *) handle;
492 M3G_VALIDATE_OBJECT(camera);
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);
501 camera->projType = M3G_PERSPECTIVE;
502 camera->heightFov = fovy;
503 camera->aspect = aspectRatio;
504 camera->clipNear = clipNear;
505 camera->clipFar = clipFar;
507 m3gValidateProjectionMatrix(camera);
511 * \brief Sets a generic projection.
513 * \param handle Camera object
514 * \param transform projection matrix
516 M3G_API void m3gSetProjectionMatrix(M3GCamera handle,
517 const M3GMatrix *transform)
519 Camera *camera = (Camera *) handle;
520 M3G_VALIDATE_OBJECT(camera);
522 if (transform == NULL) {
523 m3gRaiseError(M3G_INTERFACE(camera), M3G_NULL_POINTER);
527 camera->projType = M3G_GENERIC;
528 m3gCopyMatrix(&camera->projMatrix, transform);
530 m3gValidateProjectionMatrix(camera);
534 * \brief Gets camera matrix.
536 * \param handle Camera object
537 * \param transform projection matrix to fill in
538 * \return projection type
540 M3G_API M3Gint m3gGetProjectionAsMatrix(M3GCamera handle,
541 M3GMatrix *transform)
543 Camera *camera = (Camera *) handle;
544 M3G_VALIDATE_OBJECT(camera);
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);
554 m3gCopyMatrix(transform, m3gProjectionMatrix(camera));
557 return camera->projType;
561 * \brief Gets camera parameters.
563 * \param handle Camera object
564 * \param params camera parameters to fill in
565 * \return projection type
567 M3G_API M3Gint m3gGetProjectionAsParams(M3GCamera handle, M3Gfloat *params)
569 Camera *camera = (Camera *) handle;
570 M3G_VALIDATE_OBJECT(camera);
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;
579 return camera->projType;