--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/m3g/m3gcore11/src/m3g_camera.c Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,581 @@
+/*
+* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: Camera implementation
+*
+*/
+
+
+/*!
+ * \internal
+ * \file
+ * \brief Camera implementation
+ */
+
+#ifndef M3G_CORE_INCLUDE
+# error included by m3g_core.c; do not compile separately.
+#endif
+
+#include "m3g_camera.h"
+
+/* Internal frustum plane enumeration (and testing order!) */
+
+#define NEAR_PLANE 0
+#define FAR_PLANE 1
+#define LEFT_PLANE 2
+#define RIGHT_PLANE 3
+#define BOTTOM_PLANE 4
+#define TOP_PLANE 5
+
+/*----------------------------------------------------------------------
+ * Private functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \internal
+ * \brief Makes sure that the internal projection matrix is up-to-date
+ * and checks if the camera has a zero view volume.
+ */
+static void m3gValidateProjectionMatrix(Camera *camera)
+{
+ M3Gint projType = camera->projType;
+
+ /* The generic matrix is always valid, but for perspective and
+ * parallel we must regenerate the matrix */
+
+ if (projType != M3G_GENERIC) {
+ M3Gfloat m[16];
+
+ M3Gfloat clipNear = camera->clipNear;
+ M3Gfloat clipFar = camera->clipFar;
+
+ if (projType == M3G_PERSPECTIVE) {
+
+ M3Gfloat height = m3gTan(m3gMul(M3G_DEG2RAD * 0.5f,
+ camera->heightFov));
+
+ m[0] = m3gRcp(m3gMul(camera->aspect, height));
+ m[1] = m[2] = m[3] = 0.f;
+
+ m[4] = 0.f;
+ m[5] = m3gRcp(height);
+ m[6] = m[7] = 0.f;
+
+ m[8] = m[9] = 0.f;
+ m[10] = m3gDiv(-m3gAdd(clipFar, clipNear),
+ m3gSub(clipFar, clipNear));
+ m[11] = -1.f;
+
+ m[12] = m[13] = 0.f;
+ m[14] = m3gDiv(m3gMul(m3gMul(-2.f, clipFar), clipNear),
+ m3gSub(clipFar, clipNear));
+ m[15] = 0.f;
+ }
+ else if (projType == M3G_PARALLEL) {
+
+ M3Gfloat height = camera->heightFov;
+
+ m[0] = m3gDiv(2.f, m3gMul(camera->aspect, height));
+ m[1] = m[2] = m[3] = 0.f;
+
+ m[4] = 0.f;
+ m[5] = m3gDiv(2.f, height);
+ m[6] = m[7] = 0;
+
+ m[8] = m[9] = 0;
+ m[10] = m3gDiv(-2.f, m3gSub(clipFar, clipNear));
+ m[11] = 0.f;
+
+ m[12] = m[13] = 0.f;
+ m[14] = m3gDiv(-m3gAdd(clipFar, clipNear),
+ m3gSub(clipFar, clipNear));
+ m[15] = 1.f;
+ }
+ else {
+ M3G_ASSERT(M3G_FALSE); /* unknown projection type! */
+ }
+ m3gSetMatrixColumns(&camera->projMatrix, m);
+ }
+
+ {
+ M3GMatrix im;
+ if (!m3gMatrixInverse(&im, &camera->projMatrix)) {
+ camera->zeroViewVolume = M3G_TRUE;
+ }
+ else {
+ camera->zeroViewVolume = M3G_FALSE;
+ }
+ }
+
+ camera->frustumPlanesValid = M3G_FALSE;
+}
+
+/*!
+ * \internal
+ * \brief Validates the cached view frustum planes
+ */
+static void m3gValidateFrustumPlanes(Camera *camera)
+{
+ if (!camera->frustumPlanesValid) {
+ Vec4 *plane;
+ Vec4 rows[4];
+
+ m3gGetMatrixRows(&camera->projMatrix, (M3Gfloat*) rows);
+
+ plane = &camera->frustumPlanes[LEFT_PLANE];
+ *plane = rows[3];
+ m3gAddVec4(plane, &rows[0]);
+
+ plane = &camera->frustumPlanes[RIGHT_PLANE];
+ *plane = rows[3];
+ m3gSubVec4(plane, &rows[0]);
+
+ plane = &camera->frustumPlanes[BOTTOM_PLANE];
+ *plane = rows[3];
+ m3gAddVec4(plane, &rows[1]);
+
+ plane = &camera->frustumPlanes[TOP_PLANE];
+ *plane = rows[3];
+ m3gSubVec4(plane, &rows[1]);
+
+ plane = &camera->frustumPlanes[NEAR_PLANE];
+ *plane = rows[3];
+ m3gAddVec4(plane, &rows[2]);
+
+ plane = &camera->frustumPlanes[FAR_PLANE];
+ *plane = rows[3];
+ m3gSubVec4(plane, &rows[2]);
+
+ camera->frustumPlanesValid = M3G_TRUE;
+ }
+}
+
+#undef NEAR_PLANE
+#undef FAR_PLANE
+#undef LEFT_PLANE
+#undef RIGHT_PLANE
+#undef BOTTOM_PLANE
+#undef TOP_PLANE
+
+/*----------------------------------------------------------------------
+ * Internal functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \internal
+ * \brief Overloaded Object3D method.
+ *
+ * \param property animation property
+ * \retval M3G_TRUE property supported
+ * \retval M3G_FALSE property not supported
+ */
+static M3Gbool m3gCameraIsCompatible(M3Gint property)
+{
+ switch (property) {
+ case M3G_ANIM_FAR_DISTANCE:
+ case M3G_ANIM_FIELD_OF_VIEW:
+ case M3G_ANIM_NEAR_DISTANCE:
+ return M3G_TRUE;
+ default:
+ return m3gNodeIsCompatible(property);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Object3D method.
+ *
+ * \param obj Camera object
+ * \param property animation property
+ * \param valueSize size of value array
+ * \param value value array
+ */
+static void m3gCameraUpdateProperty(Object *obj,
+ M3Gint property,
+ M3Gint valueSize,
+ const M3Gfloat *value)
+{
+ Camera *camera = (Camera *) obj;
+ M3G_VALIDATE_OBJECT(camera);
+ M3G_ASSERT_PTR(value);
+
+ switch (property) {
+ case M3G_ANIM_FAR_DISTANCE:
+ M3G_ASSERT(valueSize >= 1);
+ camera->clipFar = (camera->projType == M3G_PERSPECTIVE)
+ ? m3gClampFloatPositive(value[0])
+ : value[0];
+ break;
+ case M3G_ANIM_FIELD_OF_VIEW:
+ M3G_ASSERT(valueSize >= 1);
+ camera->heightFov = (camera->projType == M3G_PERSPECTIVE)
+ ? m3gClampFloat(value[0], 0.f, 180.f)
+ : m3gClampFloatPositive(value[0]);
+ break;
+ case M3G_ANIM_NEAR_DISTANCE:
+ M3G_ASSERT(valueSize >= 1);
+ camera->clipNear = (camera->projType == M3G_PERSPECTIVE)
+ ? m3gClampFloatPositive(value[0])
+ : value[0];
+ break;
+ default:
+ m3gNodeUpdateProperty(obj, property, valueSize, value);
+ return; /* don't invalidate the matrix */
+ }
+
+ /* Validate the projection matrix if we changed any of the
+ * camera parameters */
+
+ m3gValidateProjectionMatrix(camera);
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Node method.
+ *
+ * Start render setup scene traversal.
+ *
+ * \param node Camera object
+ * \param toCamera transform to camera
+ * \param alphaFactor total alpha factor
+ * \param caller caller node
+ * \param renderQueue RenderQueue
+ *
+ * \retval M3G_TRUE continue render setup
+ * \retval M3G_FALSE abort render setup
+ */
+static M3Gbool m3gCameraSetupRender(Node *self,
+ const Node *caller,
+ SetupRenderState *s,
+ RenderQueue *renderQueue)
+{
+ Node *parent;
+ M3Gbool success = M3G_TRUE;
+
+ /* Just do the parent node. Note that we won't be needing the old
+ * state back after going up the tree, so we can overwrite it. */
+
+ parent = self->parent;
+
+ if (caller != parent && parent != NULL) {
+ Matrix t;
+
+ M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
+ if (!m3gGetInverseNodeTransform(self, &t)) {
+ return M3G_FALSE;
+ }
+ m3gMulMatrix(&s->toCamera, &t);
+ M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
+
+ /* The parent node will update the alpha factor and culling
+ * mask if necessary, so we need not touch those */
+
+ success = M3G_VFUNC(Node, parent, setupRender)(parent,
+ self, s, renderQueue);
+ }
+
+ return success;
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Object3D method.
+ *
+ * \param originalObj original Camera object
+ * \param cloneObj pointer to cloned Camera object
+ * \param pairs array for all object-duplicate pairs
+ * \param numPairs number of pairs
+ */
+static M3Gbool m3gCameraDuplicate(const Object *originalObj,
+ Object **cloneObj,
+ Object **pairs,
+ M3Gint *numPairs)
+{
+ Camera *original = (Camera *)originalObj;
+ Camera *clone = (Camera *)m3gCreateCamera(originalObj->interface);
+ *cloneObj = (Object *)clone;
+ if (*cloneObj == NULL) {
+ return M3G_FALSE;
+ }
+
+ if (m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs)) {
+ clone->projType = original->projType;
+ clone->projMatrix = original->projMatrix;
+ clone->heightFov = original->heightFov;
+ clone->aspect = original->aspect;
+ clone->clipNear = original->clipNear;
+ clone->clipFar = original->clipFar;
+ clone->zeroViewVolume = original->zeroViewVolume;
+ return M3G_TRUE;
+ }
+ else {
+ return M3G_FALSE;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Initializes a Camera object. See specification
+ * for default values.
+ *
+ * \param m3g M3G interface
+ * \param camera Camera object
+ */
+static void m3gInitCamera(Interface *m3g, Camera *camera)
+{
+ M3GMatrix m;
+
+ /* Camera is derived from node */
+ m3gInitNode(m3g, &camera->node, M3G_CLASS_CAMERA);
+
+ /* GENERIC, Identity */
+ m3gIdentityMatrix(&m);
+ m3gSetProjectionMatrix(camera, (const M3GMatrix *)&m);
+}
+
+/*!
+ * \internal
+ * \brief Sets camera matrix to OpenGL
+ * projection matrix.
+ *
+ * \param camera Camera object
+ */
+static void m3gApplyProjection(const Camera *camera)
+{
+ M3Gfloat t[16];
+
+ m3gGetMatrixColumns(&camera->projMatrix, t);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf(t);
+ glMatrixMode(GL_MODELVIEW);
+}
+
+/*!
+ * \internal
+ * \brief Returns a pointer to the camera projection matrix
+ *
+ * The matrix <em>must not</em> be accessed directly, as only this
+ * function will ensure that the returned matrix has valid values in
+ * it.
+ *
+ * \param camera Camera object
+ * \return a pointer to the projection matrix
+ */
+static const Matrix *m3gProjectionMatrix(const Camera *camera)
+{
+ M3G_VALIDATE_OBJECT(camera);
+
+ return &camera->projMatrix;
+}
+
+/*!
+ * \internal
+ * \brief Retrieves a pointer to the six camera space view frustum planes
+ */
+static const Vec4 *m3gFrustumPlanes(const Camera *camera)
+{
+ M3G_VALIDATE_OBJECT(camera);
+ m3gValidateFrustumPlanes((Camera*) camera);
+ return camera->frustumPlanes;
+}
+
+/*----------------------------------------------------------------------
+ * Virtual function table
+ *--------------------------------------------------------------------*/
+
+static const NodeVFTable m3gvf_Camera = {
+ {
+ {
+ m3gObjectApplyAnimation,
+ m3gCameraIsCompatible,
+ m3gCameraUpdateProperty,
+ m3gObjectDoGetReferences,
+ m3gObjectFindID,
+ m3gCameraDuplicate,
+ m3gDestroyNode /* no extra clean-up for Camera */
+ }
+ },
+ m3gNodeAlign,
+ NULL, /* pure virtual DoRender */
+ m3gNodeGetBBox,
+ m3gNodeRayIntersect,
+ m3gCameraSetupRender,
+ m3gNodeUpdateDuplicateReferences,
+ m3gNodeValidate
+};
+
+
+/*----------------------------------------------------------------------
+ * Public API functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates a Camera object.
+ *
+ * \param interface M3G interface
+ * \retval Camera new Camera object
+ * \retval NULL Camera creating failed
+ */
+M3G_API M3GCamera m3gCreateCamera(M3GInterface interface)
+{
+ Interface *m3g = (Interface *) interface;
+ M3G_VALIDATE_INTERFACE(m3g);
+
+ {
+ Camera *camera = m3gAllocZ(m3g, sizeof(Camera));
+
+ if (camera != NULL) {
+ m3gInitCamera(m3g, camera);
+ }
+
+ return (M3GCamera) camera;
+ }
+}
+
+/*!
+ * \brief Sets a parallel projection.
+ *
+ * \param handle Camera object
+ * \param height height (=fovy)
+ * \param aspectRatio viewport aspect ratio
+ * \param clipNear near clipping plane
+ * \param clipFar far clipping plane
+ */
+M3G_API void m3gSetParallel(M3GCamera handle,
+ M3Gfloat height,
+ M3Gfloat aspectRatio,
+ M3Gfloat clipNear, M3Gfloat clipFar)
+{
+ Camera *camera = (Camera *) handle;
+ M3G_VALIDATE_OBJECT(camera);
+
+ if (height <= 0 || aspectRatio <= 0.0f) {
+ m3gRaiseError(M3G_INTERFACE(camera), M3G_INVALID_VALUE);
+ return;
+ }
+
+ camera->projType = M3G_PARALLEL;
+ camera->heightFov = height;
+ camera->aspect = aspectRatio;
+ camera->clipNear = clipNear;
+ camera->clipFar = clipFar;
+
+ m3gValidateProjectionMatrix(camera);
+}
+
+/*!
+ * \brief Sets a perspective projection.
+ *
+ * \param handle Camera object
+ * \param fovy fovy
+ * \param aspectRatio viewport aspect ratio
+ * \param clipNear near clipping plane
+ * \param clipFar far clipping plane
+ */
+M3G_API void m3gSetPerspective(M3GCamera handle,
+ M3Gfloat fovy,
+ M3Gfloat aspectRatio,
+ M3Gfloat clipNear, M3Gfloat clipFar)
+{
+ Camera *camera = (Camera *) handle;
+ M3G_VALIDATE_OBJECT(camera);
+
+ if (fovy <= 0.0f || fovy >= 180.f
+ || aspectRatio <= 0.0f
+ || clipNear <= 0.0f || clipFar <= 0.0f) {
+ m3gRaiseError(M3G_INTERFACE(camera), M3G_INVALID_VALUE);
+ return;
+ }
+
+ camera->projType = M3G_PERSPECTIVE;
+ camera->heightFov = fovy;
+ camera->aspect = aspectRatio;
+ camera->clipNear = clipNear;
+ camera->clipFar = clipFar;
+
+ m3gValidateProjectionMatrix(camera);
+}
+
+/*!
+ * \brief Sets a generic projection.
+ *
+ * \param handle Camera object
+ * \param transform projection matrix
+ */
+M3G_API void m3gSetProjectionMatrix(M3GCamera handle,
+ const M3GMatrix *transform)
+{
+ Camera *camera = (Camera *) handle;
+ M3G_VALIDATE_OBJECT(camera);
+
+ if (transform == NULL) {
+ m3gRaiseError(M3G_INTERFACE(camera), M3G_NULL_POINTER);
+ return;
+ }
+
+ camera->projType = M3G_GENERIC;
+ m3gCopyMatrix(&camera->projMatrix, transform);
+
+ m3gValidateProjectionMatrix(camera);
+}
+
+/*!
+ * \brief Gets camera matrix.
+ *
+ * \param handle Camera object
+ * \param transform projection matrix to fill in
+ * \return projection type
+ */
+M3G_API M3Gint m3gGetProjectionAsMatrix(M3GCamera handle,
+ M3GMatrix *transform)
+{
+ Camera *camera = (Camera *) handle;
+ M3G_VALIDATE_OBJECT(camera);
+
+ if (transform != NULL) {
+ /* Check for impossible projection matrix */
+ if (camera->projType != M3G_GENERIC &&
+ camera->clipFar == camera->clipNear) {
+ m3gRaiseError(M3G_INTERFACE(camera), M3G_ARITHMETIC_ERROR);
+ return 0;
+ }
+
+ m3gCopyMatrix(transform, m3gProjectionMatrix(camera));
+ }
+
+ return camera->projType;
+}
+
+/*!
+ * \brief Gets camera parameters.
+ *
+ * \param handle Camera object
+ * \param params camera parameters to fill in
+ * \return projection type
+ */
+M3G_API M3Gint m3gGetProjectionAsParams(M3GCamera handle, M3Gfloat *params)
+{
+ Camera *camera = (Camera *) handle;
+ M3G_VALIDATE_OBJECT(camera);
+
+ if (params != NULL && camera->projType != M3G_GENERIC) {
+ params[0] = camera->heightFov;
+ params[1] = camera->aspect;
+ params[2] = camera->clipNear;
+ params[3] = camera->clipFar;
+ }
+
+ return camera->projType;
+}
+