diff --git a/cogl/Makefile.am b/cogl/Makefile.am index dc88ba351..8e66aa482 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -64,6 +64,7 @@ cogl_public_h = \ $(srcdir)/cogl-material-compat.h \ $(srcdir)/cogl-pipeline.h \ $(srcdir)/cogl-vector.h \ + $(srcdir)/cogl-quaternion.h \ $(srcdir)/cogl-matrix.h \ $(srcdir)/cogl-offscreen.h \ $(srcdir)/cogl-primitives.h \ @@ -230,6 +231,7 @@ cogl_sources_c = \ $(srcdir)/cogl-primitive.c \ $(srcdir)/cogl-matrix.c \ $(srcdir)/cogl-vector.c \ + $(srcdir)/cogl-quaternion.c \ $(srcdir)/cogl-matrix-private.h \ $(srcdir)/cogl-matrix-stack.c \ $(srcdir)/cogl-matrix-stack.h \ diff --git a/cogl/cogl-matrix-mesa.c b/cogl/cogl-matrix-mesa.c index 0356a1127..9f5f3c96c 100644 --- a/cogl/cogl-matrix-mesa.c +++ b/cogl/cogl-matrix-mesa.c @@ -70,6 +70,7 @@ */ #include "cogl-matrix-mesa.h" +#include "cogl-quaternion-private.h" #include #include @@ -1586,6 +1587,43 @@ _math_matrix_init_from_array (CoglMatrix *matrix, const float *array) matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); } +/* + */ +void +_math_matrix_init_from_quaternion (CoglMatrix *matrix, + CoglQuaternion *quaternion) +{ + float qnorm = _COGL_QUATERNION_NORM (quaternion); + float s = (qnorm > 0.0f) ? (2.0f / qnorm) : 0.0f; + float xs = quaternion->x * s; + float ys = quaternion->y * s; + float zs = quaternion->z * s; + float wx = quaternion->w * xs; + float wy = quaternion->w * ys; + float wz = quaternion->w * zs; + float xx = quaternion->x * xs; + float xy = quaternion->x * ys; + float xz = quaternion->x * zs; + float yy = quaternion->y * ys; + float yz = quaternion->y * zs; + float zz = quaternion->z * zs; + + matrix->xx = 1.0f - (yy + zz); + matrix->yx = xy + wz; + matrix->zx = xz - wy; + matrix->xy = xy - wz; + matrix->yy = 1.0f - (xx + zz); + matrix->zy = yz + wx; + matrix->xz = xz + wy; + matrix->yz = yz - wx; + matrix->zz = 1.0f - (xx + yy); + matrix->xw = matrix->yw = matrix->zw = 0.0f; + matrix->wx = matrix->wy = matrix->wz = 0.0f; + matrix->ww = 1.0f; + + matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); +} + /*@}*/ diff --git a/cogl/cogl-matrix-mesa.h b/cogl/cogl-matrix-mesa.h index 72ac8650f..e6f3cd9a2 100644 --- a/cogl/cogl-matrix-mesa.h +++ b/cogl/cogl-matrix-mesa.h @@ -51,6 +51,7 @@ #define _M_MATRIX_H #include +#include #include @@ -110,6 +111,10 @@ _math_matrix_multiply_array (CoglMatrix *result, const float *b); void _math_matrix_init_from_array (CoglMatrix *matrix, const float *array); +void +_math_matrix_init_from_quaternion (CoglMatrix *matrix, + CoglQuaternion *quaternion); + void _math_matrix_translate (CoglMatrix *matrix, float x, float y, float z); diff --git a/cogl/cogl-matrix.c b/cogl/cogl-matrix.c index 625b89f68..6e45d1dbf 100644 --- a/cogl/cogl-matrix.c +++ b/cogl/cogl-matrix.c @@ -32,6 +32,8 @@ #include #include "cogl-debug.h" +#include +#include #include #include #ifdef USE_MESA_MATRIX_API @@ -73,6 +75,13 @@ cogl_matrix_init_identity (CoglMatrix *matrix) _COGL_MATRIX_DEBUG_PRINT (matrix); } +void +cogl_matrix_init_from_quaternion (CoglMatrix *matrix, + CoglQuaternion *quaternion) +{ + _math_matrix_init_from_quaternion (matrix, quaternion); +} + void cogl_matrix_multiply (CoglMatrix *result, const CoglMatrix *a, diff --git a/cogl/cogl-matrix.h b/cogl/cogl-matrix.h index 1db1d8fa5..f94a64427 100644 --- a/cogl/cogl-matrix.h +++ b/cogl/cogl-matrix.h @@ -30,6 +30,10 @@ #include #include "cogl-types.h" +#ifdef COGL_ENABLE_EXPERIMENTAL_API +#include "cogl-quaternion.h" +#endif + G_BEGIN_DECLS /** @@ -41,8 +45,6 @@ G_BEGIN_DECLS * be used for direct manipulation of these matrices. */ -typedef struct _CoglMatrix CoglMatrix; - /** * CoglMatrix: * @@ -360,6 +362,21 @@ cogl_matrix_init_from_array (CoglMatrix *matrix, G_CONST_RETURN float * cogl_matrix_get_array (const CoglMatrix *matrix); +#ifdef COGL_ENABLE_EXPERIMENTAL_API +/** + * cogl_matrix_init_from_quaternion: + * @matrix: A 4x4 transformation matrix + * @quaternion: A #CoglQuaternion + * + * Initializes @matrix from a #CoglQuaternion rotation. + * + * Return value: a pointer to the float array + */ +void +cogl_matrix_init_from_quaternion (CoglMatrix *matrix, + CoglQuaternion *quaternion); +#endif + /** * cogl_matrix_equal: * @v1: A 4x4 transformation matrix diff --git a/cogl/cogl-quaternion-private.h b/cogl/cogl-quaternion-private.h new file mode 100644 index 000000000..882e34147 --- /dev/null +++ b/cogl/cogl-quaternion-private.h @@ -0,0 +1,39 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2008,2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_QUATERNION_PRIVATE_H__ +#define __COGL_QUATERNION_PRIVATE_H__ + +#include + +/* squared length */ +#define _COGL_QUATERNION_NORM(Q) \ + ((Q)->x*(Q)->x + (Q)->y*(Q)->y + (Q)->z*(Q)->z + (Q)->w*(Q)->w) + +#define _COGL_QUATERNION_DEGREES_TO_RADIANS (G_PI / 180.0f) +#define _COGL_QUATERNION_RADIANS_TO_DEGREES (180.0f / G_PI) + +#endif /* __COGL_QUATERNION_PRIVATE_H__ */ diff --git a/cogl/cogl-quaternion.c b/cogl/cogl-quaternion.c new file mode 100644 index 000000000..ae5c79851 --- /dev/null +++ b/cogl/cogl-quaternion.c @@ -0,0 +1,617 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2010 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Robert Bragg + * + * Various references relating to quaternions: + * + * http://www.cs.caltech.edu/courses/cs171/quatut.pdf + * http://mathworld.wolfram.com/Quaternion.html + * http://www.gamedev.net/reference/articles/article1095.asp + * http://www.cprogramming.com/tutorial/3d/quaternions.html + * http://www.isner.com/tutorials/quatSpells/quaternion_spells_12.htm + * http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56 + * 3D Maths Primer for Graphics and Game Development ISBN-10: 1556229119 + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define FLOAT_EPSILON 1e-03 + +static CoglQuaternion zero_quaternion = +{ + 0.0, 0.0, 0.0, 0.0, +}; + +static CoglQuaternion identity_quaternion = +{ + 1.0, 0.0, 0.0, 0.0, +}; + +void +_cogl_quaternion_print (CoglQuaternion *quaternion) +{ + g_print ("[ %6.4f (%6.4f, %6.4f, %6.4f)]\n", + quaternion->w, + quaternion->x, + quaternion->y, + quaternion->z); +} + +void +cogl_quaternion_init (CoglQuaternion *quaternion, + float angle, + float x, + float y, + float z) +{ + CoglVector3 axis = { x, y, z}; + cogl_quaternion_init_from_angle_vector (quaternion, angle, &axis); +} + +void +cogl_quaternion_init_from_angle_vector (CoglQuaternion *quaternion, + float angle, + const CoglVector3 *axis_in) +{ + /* NB: We are using quaternions to represent an axis (a), angle (πœƒ) pair + * in this form: + * [w=cos(πœƒ/2) ( x=sin(πœƒ/2)*a.x, y=sin(πœƒ/2)*a.y, z=sin(πœƒ/2)*a.x )] + */ + CoglVector3 axis; + float half_angle; + float sin_half_angle; + + /* XXX: Should we make cogl_vector3_normalize have separate in and + * out args? */ + axis = *axis_in; + cogl_vector3_normalize (&axis); + + half_angle = angle * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f; + sin_half_angle = sinf (half_angle); + + quaternion->w = cosf (half_angle); + + quaternion->x = axis.x * sin_half_angle; + quaternion->y = axis.y * sin_half_angle; + quaternion->z = axis.z * sin_half_angle; + + cogl_quaternion_normalize (quaternion); +} + +void +cogl_quaternion_init_identity (CoglQuaternion *quaternion) +{ + quaternion->w = 1.0; + + quaternion->x = 0.0; + quaternion->y = 0.0; + quaternion->z = 0.0; +} + +void +cogl_quaternion_init_from_array (CoglQuaternion *quaternion, + const float *array) +{ + memcpy (&quaternion->x, array, sizeof (float) * 4); +} + +void +cogl_quaternion_init_from_x_rotation (CoglQuaternion *quaternion, + float angle) +{ + /* NB: We are using quaternions to represent an axis (a), angle (πœƒ) pair + * in this form: + * [w=cos(πœƒ/2) ( x=sin(πœƒ/2)*a.x, y=sin(πœƒ/2)*a.y, z=sin(πœƒ/2)*a.x )] + */ + float half_angle = angle * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f; + + quaternion->w = cosf (half_angle); + + quaternion->x = sinf (half_angle); + quaternion->y = 0.0f; + quaternion->z = 0.0f; +} + +void +cogl_quaternion_init_from_y_rotation (CoglQuaternion *quaternion, + float angle) +{ + /* NB: We are using quaternions to represent an axis (a), angle (πœƒ) pair + * in this form: + * [w=cos(πœƒ/2) ( x=sin(πœƒ/2)*a.x, y=sin(πœƒ/2)*a.y, z=sin(πœƒ/2)*a.x )] + */ + float half_angle = angle * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f; + + quaternion->w = cosf (half_angle); + + quaternion->x = 0.0f; + quaternion->y = sinf (half_angle); + quaternion->z = 0.0f; +} + +void +cogl_quaternion_init_from_z_rotation (CoglQuaternion *quaternion, + float angle) +{ + /* NB: We are using quaternions to represent an axis (a), angle (πœƒ) pair + * in this form: + * [w=cos(πœƒ/2) ( x=sin(πœƒ/2)*a.x, y=sin(πœƒ/2)*a.y, z=sin(πœƒ/2)*a.x )] + */ + float half_angle = angle * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f; + + quaternion->w = cosf (half_angle); + + quaternion->x = 0.0f; + quaternion->y = 0.0f; + quaternion->z = sinf (half_angle); +} + +void +cogl_quaternion_init_from_quaternion (CoglQuaternion *quaternion, + CoglQuaternion *src) +{ + memcpy (quaternion, src, sizeof (float) * 4); +} + +/* XXX: it could be nice to make something like this public... */ +/* + * COGL_MATRIX_READ: + * @MATRIX: A 4x4 transformation matrix + * @ROW: The row of the value you want to read + * @COLUMN: The column of the value you want to read + * + * Reads a value from the given matrix using integers to index + * into the matrix. + */ +#define COGL_MATRIX_READ(MATRIX, ROW, COLUMN) \ + (((const float *)matrix)[COLUMN * 4 + ROW]) + +/** + * cogl_quaternion_init_from_matrix: + * @quaternion: A Cogl Quaternion + * @matrix: A rotation matrix with which to initialize the quaternion + * + * Initializes a quaternion from a rotation matrix. + * + * Since: 1.4 + */ +void +cogl_quaternion_init_from_matrix (CoglQuaternion *quaternion, + const CoglMatrix *matrix) +{ + /* Algorithm devised by Ken Shoemake, Ref: + * http://campar.in.tum.de/twiki/pub/Chair/DwarfTutorial/quatut.pdf + */ + + /* 3D maths literature refers to the diagonal of a matrix as the + * "trace" of a matrix... */ + float trace = matrix->xx + matrix->yy + matrix->zz; + float root; + + if (trace > 0.0f) + { + root = sqrtf (trace + 1); + quaternion->w = root * 0.5f; + root = 0.5f / root; + quaternion->x = (matrix->zy - matrix->yz) * root; + quaternion->y = (matrix->xz - matrix->zx) * root; + quaternion->z = (matrix->yx - matrix->xy) * root; + } + else + { +#define X 0 +#define Y 1 +#define Z 2 +#define W 3 + int h = X; + if (matrix->yy > matrix->xx) + h = Y; + if (matrix->zz > COGL_MATRIX_READ (matrix, h, h)) + h = Z; + switch (h) + { +#define CASE_MACRO(i, j, k, I, J, K) \ + case I: \ + root = sqrtf ((COGL_MATRIX_READ (matrix, I, I) - \ + (COGL_MATRIX_READ (matrix, J, J) + \ + COGL_MATRIX_READ (matrix, K, K))) + \ + COGL_MATRIX_READ (matrix, W, W)); \ + quaternion->i = root * 0.5f;\ + root = 0.5f / root;\ + quaternion->j = (COGL_MATRIX_READ (matrix, I, J) + \ + COGL_MATRIX_READ (matrix, J, I)) * root; \ + quaternion->k = (COGL_MATRIX_READ (matrix, K, I) + \ + COGL_MATRIX_READ (matrix, I, K)) * root; \ + quaternion->w = (COGL_MATRIX_READ (matrix, K, J) - \ + COGL_MATRIX_READ (matrix, J, K)) * root;\ + break + CASE_MACRO (x, y, z, X, Y, Z); + CASE_MACRO (y, z, x, Y, Z, X); + CASE_MACRO (z, x, y, Z, X, Y); +#undef CASE_MACRO +#undef X +#undef Y +#undef Z + } + } + + if (matrix->ww != 1.0f) + { + float s = 1.0 / sqrtf (matrix->ww); + quaternion->w *= s; + quaternion->x *= s; + quaternion->y *= s; + quaternion->z *= s; + } +} + +gboolean +cogl_quaternion_equal (gconstpointer v1, gconstpointer v2) +{ + const CoglQuaternion *a = v1; + const CoglQuaternion *b = v2; + + g_return_val_if_fail (v1 != NULL, FALSE); + g_return_val_if_fail (v2 != NULL, FALSE); + + if (v1 == v2) + return TRUE; + + return (a->w == b->w && + a->x == b->x && + a->y == b->y && + a->z == b->z); +} + +CoglQuaternion * +cogl_quaternion_copy (const CoglQuaternion *src) +{ + if (G_LIKELY (src)) + { + CoglQuaternion *new = g_slice_new (CoglQuaternion); + memcpy (new, src, sizeof (float) * 4); + return new; + } + else + return NULL; +} + +void +cogl_quaternion_free (CoglQuaternion *quaternion) +{ + g_slice_free (CoglQuaternion, quaternion); +} + +float +cogl_quaternion_get_rotation_angle (const CoglQuaternion *quaternion) +{ + /* NB: We are using quaternions to represent an axis (a), angle (πœƒ) pair + * in this form: + * [w=cos(πœƒ/2) ( x=sin(πœƒ/2)*a.x, y=sin(πœƒ/2)*a.y, z=sin(πœƒ/2)*a.x )] + */ + + /* FIXME: clamp [-1, 1] */ + return 2.0f * acosf (quaternion->w) * _COGL_QUATERNION_RADIANS_TO_DEGREES; +} + +void +cogl_quaternion_get_rotation_axis (const CoglQuaternion *quaternion, + CoglVector3 *vector) +{ + float sin_half_angle_sqr; + float one_over_sin_angle_over_2; + + /* NB: We are using quaternions to represent an axis (a), angle (πœƒ) pair + * in this form: + * [w=cos(πœƒ/2) ( x=sin(πœƒ/2)*a.x, y=sin(πœƒ/2)*a.y, z=sin(πœƒ/2)*a.x )] + */ + + /* NB: sinΒ²(πœƒ) + cosΒ²(πœƒ) = 1 */ + + sin_half_angle_sqr = 1.0f - quaternion->w * quaternion->w; + + if (sin_half_angle_sqr <= 0.0f) + { + /* Either an identity quaternion or numerical imprecision. + * Either way we return an arbitrary vector. */ + vector->x = 1; + vector->y = 0; + vector->z = 0; + return; + } + + /* Calculate 1 / sin(πœƒ/2) */ + one_over_sin_angle_over_2 = 1.0f / sqrtf (sin_half_angle_sqr); + + vector->x = quaternion->x * one_over_sin_angle_over_2; + vector->x = quaternion->x * one_over_sin_angle_over_2; + vector->x = quaternion->x * one_over_sin_angle_over_2; +} + +void +cogl_quaternion_normalize (CoglQuaternion *quaternion) +{ + float slen = _COGL_QUATERNION_NORM (quaternion); + float factor = 1.0f / sqrtf (slen); + + quaternion->x *= factor; + quaternion->y *= factor; + quaternion->z *= factor; + + quaternion->w *= factor; + + return; +} + +float +cogl_quaternion_dot_product (const CoglQuaternion *a, + const CoglQuaternion *b) +{ + return a->w * b->w + a->x * b->x + a->y * b->y + a->z * b->z; +} + +void +cogl_quaternion_invert (CoglQuaternion *quaternion) +{ + quaternion->x = -quaternion->x; + quaternion->y = -quaternion->y; + quaternion->z = -quaternion->z; +} + +void +cogl_quaternion_multiply (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b) +{ + result->w = a->w * b->w - a->x * b->x - a->y * b->y - a->z * b->z; + + result->x = a->w * b->x + a->x * b->w + a->y * b->z - a->z * b->y; + result->y = a->w * b->y + a->y * b->w + a->z * b->x - a->x * b->z; + result->z = a->w * b->z + a->z * b->w + a->x * b->y - a->y * b->x; +} + +void +cogl_quaternion_pow (CoglQuaternion *quaternion, float exponent) +{ + float half_angle; + float new_half_angle; + float factor; + + /* Try and identify and nop identity quaternions to avoid + * dividing by zero */ + if (fabs (quaternion->w) > 0.9999f) + return; + + /* NB: We are using quaternions to represent an axis (a), angle (πœƒ) pair + * in this form: + * [w=cos(πœƒ/2) ( x=sin(πœƒ/2)*a.x, y=sin(πœƒ/2)*a.y, z=sin(πœƒ/2)*a.x )] + */ + + /* FIXME: clamp [-1, 1] */ + /* Extract πœƒ/2 from w */ + half_angle = acosf (quaternion->w); + + /* Compute the new πœƒ/2 */ + new_half_angle = half_angle * exponent; + + /* Compute the new w value */ + quaternion->w = cosf (new_half_angle); + + /* And new xyz values */ + factor = sinf (new_half_angle) / sinf (half_angle); + quaternion->x *= factor; + quaternion->y *= factor; + quaternion->z *= factor; +} + +void +cogl_quaternion_slerp (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b, + float t) +{ + float cos_difference; + float qb_w; + float qb_x; + float qb_y; + float qb_z; + float fa; + float fb; + + g_return_if_fail (t >=0 && t <= 1.0f); + + if (t == 0) + { + *result = *a; + return; + } + else if (t == 1) + { + *result = *b; + return; + } + + /* compute the cosine of the angle between the two given quaternions */ + cos_difference = cogl_quaternion_dot_product (a, b); + + /* If negative, use -b. Two quaternions q and -q represent the same angle but + * may produce a different slerp. We choose b or -b to rotate using the acute + * angle. + */ + if (cos_difference < 0.0f) + { + qb_w = -b->w; + qb_x = -b->x; + qb_y = -b->y; + qb_z = -b->z; + cos_difference = -cos_difference; + } + else + { + qb_w = b->w; + qb_x = b->x; + qb_y = b->y; + qb_z = b->z; + } + + /* If we have two unit quaternions the dot should be <= 1.0 */ + g_assert (cos_difference < 1.1f); + + + /* Determine the interpolation factors for each quaternion, simply using + * linear interpolation for quaternions that are nearly exactly the same. + * (this will avoid divisions by zero) + */ + + if (cos_difference > 0.9999f) + { + fa = 1.0f - t; + fb = t; + + /* XXX: should we also normalize() at the end in this case? */ + } + else + { + /* Calculate the sin of the angle between the two quaternions using the + * trig identity: sinΒ²(πœƒ) + cosΒ²(πœƒ) = 1 + */ + float sin_difference = sqrtf (1.0f - cos_difference * cos_difference); + + float difference = atan2f (sin_difference, cos_difference); + float one_over_sin_difference = 1.0f / sin_difference; + fa = sinf ((1.0f - t) * difference) * one_over_sin_difference; + fb = sinf (t * difference) * one_over_sin_difference; + } + + /* Finally interpolate the two quaternions */ + + result->x = fa * a->x + fb * qb_x; + result->y = fa * a->y + fb * qb_y; + result->z = fa * a->z + fb * qb_z; + result->w = fa * a->w + fb * qb_w; +} + +void +cogl_quaternion_nlerp (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b, + float t) +{ + float cos_difference; + float qb_w; + float qb_x; + float qb_y; + float qb_z; + float fa; + float fb; + + g_return_if_fail (t >=0 && t <= 1.0f); + + if (t == 0) + { + *result = *a; + return; + } + else if (t == 1) + { + *result = *b; + return; + } + + /* compute the cosine of the angle between the two given quaternions */ + cos_difference = cogl_quaternion_dot_product (a, b); + + /* If negative, use -b. Two quaternions q and -q represent the same angle but + * may produce a different slerp. We choose b or -b to rotate using the acute + * angle. + */ + if (cos_difference < 0.0f) + { + qb_w = -b->w; + qb_x = -b->x; + qb_y = -b->y; + qb_z = -b->z; + cos_difference = -cos_difference; + } + else + { + qb_w = b->w; + qb_x = b->x; + qb_y = b->y; + qb_z = b->z; + } + + /* If we have two unit quaternions the dot should be <= 1.0 */ + g_assert (cos_difference < 1.1f); + + fa = 1.0f - t; + fb = t; + + result->x = fa * a->x + fb * qb_x; + result->y = fa * a->y + fb * qb_y; + result->z = fa * a->z + fb * qb_z; + result->w = fa * a->w + fb * qb_w; + + cogl_quaternion_normalize (result); +} + +/** + * cogl_quaternion_squad: + * + */ +void +cogl_quaternion_squad (CoglQuaternion *result, + const CoglQuaternion *prev, + const CoglQuaternion *a, + const CoglQuaternion *b, + const CoglQuaternion *next, + float t) +{ + CoglQuaternion slerp0; + CoglQuaternion slerp1; + + cogl_quaternion_slerp (&slerp0, a, b, t); + cogl_quaternion_slerp (&slerp1, prev, next, t); + cogl_quaternion_slerp (result, &slerp0, &slerp1, 2.0f * t * (1.0f - t)); +} + +const CoglQuaternion * +cogl_get_static_identity_quaternion (void) +{ + return &identity_quaternion; +} + +const CoglQuaternion * +cogl_get_static_zero_quaternion (void) +{ + return &zero_quaternion; +} + diff --git a/cogl/cogl-quaternion.h b/cogl/cogl-quaternion.h new file mode 100644 index 000000000..da4e7659f --- /dev/null +++ b/cogl/cogl-quaternion.h @@ -0,0 +1,486 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2010 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Robert Bragg + */ + +#if !defined(__COGL_H_INSIDE__) && !defined(CLUTTER_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __COGL_QUATERNION_H__ +#define __COGL_QUATERNION_H__ + +#include +#include + +G_BEGIN_DECLS + +/** + * SECTION:cogl-quaternion + * @short_description: Functions for initializing and manipulating + * quaternions. + * + * Quaternions have become a standard form for representing 3D + * rotations and have some nice properties when compared with other + * representation such as (roll,pitch,yaw) Euler angles. They can be + * used to interpolate between different rotations and they don't + * suffer from a problem called "Gimbal lock" where two of the axis of + * rotation may become aligned and you loose a degree of freedom. + * (). + */ + +/** + * CoglQuaternion: + * + * A quaternion is comprised of a scalar component and a 3D vector + * component. The scalar component is normally referred to as w and the + * vector might either be referred to as v or a (for axis) or expanded + * with the individual components: (x, y, z) A full quaternion would + * then be written as
[w (x, y, z)]
. + * + * Quaternions can be considered to represent an axis and angle + * pair although sadly these numbers are buried somewhat under some + * maths... + * + * For the curious you can see here that a given axis (a) and angle (πœƒ) + * pair are represented in a quaternion as follows: + * |[ + * [w=cos(πœƒ/2) ( x=sin(πœƒ/2)*a.x, y=sin(πœƒ/2)*a.y, z=sin(πœƒ/2)*a.x )] + * ]| + * + * Unit Quaternions: + * When using Quaternions to represent spatial orientations for 3D + * graphics it's always assumed you have a unit quaternion. The + * magnitude of a quaternion is defined as: + * |[ + * sqrt (wΒ² + xΒ² + yΒ² + zΒ²) + * ]| + * and a unit quaternion satisfies this equation: + * |[ + * wΒ² + xΒ² + yΒ² + zΒ² = 1 + * ]| + * + * Thankfully most of the time we don't actually have to worry about + * the maths that goes on behind the scenes but if you are curious to + * learn more here are some external references: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * 3D Maths Primer for Graphics and Game Development ISBN-10: 1556229119 + * + * + * + * + * + * + * + * + * + * @w: based on the angle of rotation it is cos(πœƒ/2) + * @x: based on the angle of rotation and x component of the axis of + * rotation it is sin(πœƒ/2)*axis.x + * @y: based on the angle of rotation and y component of the axis of + * rotation it is sin(πœƒ/2)*axis.y + * @z: based on the angle of rotation and z component of the axis of + * rotation it is sin(πœƒ/2)*axis.z + */ +struct _CoglQuaternion +{ + float w; + + float x; + float y; + float z; + + float padding0; + float padding1; + float padding2; + float padding3; +}; + +/** + * cogl_quaternion_init: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle you want to rotate around the given axis + * @x: The x component of your axis vector about which you want to + * rotate. + * @y: The y component of your axis vector about which you want to + * rotate. + * @z: The z component of your axis vector about which you want to + * rotate. + * + * Initializes a quaternion that rotates @angle degrees around the + * axis vector (@x, @y, @z). The axis vector does not need to be + * normalized. + * + * Returns: A normalized, unit quaternion representing an orientation + * rotated @angle degrees around the axis vector (@x, @y, @z) + * + * Since: 2.0 + */ +void +cogl_quaternion_init (CoglQuaternion *quaternion, + float angle, + float x, + float y, + float z); + +/** + * cogl_quaternion_init_from_angle_vector: + * @quaternion: An uninitialized #CoglQuaternion + * @axis: your axis vector about which you want to rotate. + * + * Initializes a quaternion that rotates @angle degrees around the + * given @axis vector. The axis vector does not need to be + * normalized. + * + * Returns: A normalized, unit quaternion representing an orientation + * rotated @angle degrees around the given @axis vector. + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_angle_vector (CoglQuaternion *quaternion, + float angle, + const CoglVector3 *axis); + +/** + * cogl_quaternion_init_identity: + * @quaternion: An uninitialized #CoglQuaternion + * + * Initializes the quaternion with the canonical quaternion identity + * [1 (0, 0, 0)] which represents no rotation. Multiplying a + * quaternion with this identity leaves the quaternion unchanged. + * + * You might also want to consider using + * cogl_get_static_identity_quaternion(). + * + * Since: 2.0 + */ +void +cogl_quaternion_init_identity (CoglQuaternion *quaternion); + +/** + * cogl_quaternion_init_from_array: + * @quaternion: A #CoglQuaternion + * @array: An array of 4 floats (x,y,z),w + * + * Initializes a [w (x, y,z)] quaternion directly from an array of 4 + * floats: [w,x,y,z]. + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_array (CoglQuaternion *quaternion, + const float *array); + +/** + * cogl_quaternion_init_from_x_rotation: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle to rotate around the x axis + * + * XXX: check which direction this rotates + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_x_rotation (CoglQuaternion *quaternion, + float angle); + +/** + * cogl_quaternion_init_from_y_rotation: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle to rotate around the y axis + * + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_y_rotation (CoglQuaternion *quaternion, + float angle); + +/** + * cogl_quaternion_init_from_z_rotation: + * @quaternion: An uninitialized #CoglQuaternion + * @angle: The angle to rotate around the y axis + * + * + * Since: 2.0 + */ +void +cogl_quaternion_init_from_z_rotation (CoglQuaternion *quaternion, + float angle); + +/** + * cogl_quaternion_equal: + * @a: A #CoglQuaternion + * @b: A #CoglQuaternion + * + * Compares that all the components of quaternions @a and @b are + * equal. + * + * An epsilon value is not used to compare the float components, but + * the == operator is at least used so that 0 and -0 are considered + * equal. + * + * Returns: %TRUE if the quaternions are equal else %FALSE. + * + * Since: 2.0 + */ +gboolean +cogl_quaternion_equal (gconstpointer v1, gconstpointer v2); + +/** + * cogl_quaternion_copy: + * @src: A #CoglQuaternion + * + * Allocates a new #CoglQuaternion on the stack and initializes it with + * the same values as @src. + * + * Returns: A newly allocated #CoglQuaternion which should be freed + * using cogl_quaternion_free() + * + * Since: 2.0 + */ +CoglQuaternion * +cogl_quaternion_copy (const CoglQuaternion *src); + +/** + * cogl_quaternion_free: + * @quaternion: A #CoglQuaternion + * + * Frees a #CoglQuaternion that was previously allocated via + * cogl_quaternion_copy(). + * + * Since: 2.0 + */ +void +cogl_quaternion_free (CoglQuaternion *quaternion); + +/** + * cogl_quaternion_get_rotation_angle: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +float +cogl_quaternion_get_rotation_angle (const CoglQuaternion *quaternion); + +/** + * cogl_quaternion_get_rotation_axis: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +void +cogl_quaternion_get_rotation_axis (const CoglQuaternion *quaternion, + CoglVector3 *vector); + +/** + * cogl_quaternion_normalize: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +void +cogl_quaternion_normalize (CoglQuaternion *quaternion); + +/** + * cogl_quaternion_dot_product: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +float +cogl_quaternion_dot_product (const CoglQuaternion *a, + const CoglQuaternion *b); + +/** + * cogl_quaternion_invert: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +void +cogl_quaternion_invert (CoglQuaternion *quaternion); + +/** + * cogl_quaternion_multiply: + * @result: The destination #CoglQuaternion + * @left: The second #CoglQuaternion rotation to apply + * @right: The first #CoglQuaternion rotation to apply + * + * This combines the rotations of two quaternions into @result. The + * operation is not commutative so the order is important because AxB + * != BxA. Cogl follows the standard convention for quaternions here + * so the rotations are applied @right to @left. This is similar to the + * combining of matrices. + * + * Since: 2.0 + */ +void +cogl_quaternion_multiply (CoglQuaternion *result, + const CoglQuaternion *left, + const CoglQuaternion *right); + +/** + * cogl_quaternion_pow: + * @quaternion: A #CoglQuaternion + * + * + * Since: 2.0 + */ +void +cogl_quaternion_pow (CoglQuaternion *quaternion, float exponent); + +/** + * cogl_quaternion_slerp: + * + * Performs a spherical linear interpolation between two quaternions. + * + * Noteable properties: + * + * + * commutative: No + * + * + * constant velocity: Yes + * + * + * torque minimal (travels along the surface of the 4-sphere): Yes + * + * + * more expensive than cogl_quaternion_nlerp() + * + * + */ +void +cogl_quaternion_slerp (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b, + float t); + +/** + * cogl_quaternion_nlerp: + * @result: The destination #CoglQuaternion + * @a: The first #CoglQuaternion + * @b: The second #CoglQuaternion + * @t: The factor in the range [0,1] used to interpolate between + * quaterion @a and @b. + * + * Performs a normalized linear interpolation between two quaternions. + * That is it does a linear interpolation of the quaternion components + * and then normalizes the result. This will follow the shortest arc + * between the two orientations (just like the slerp() function) but + * will not progress at a constant speed. Unlike slerp() nlerp is + * commutative which is useful if you are blending animations + * together. (I.e. nlerp (tmp, a, b) followed by nlerp (result, tmp, + * d) is the same as nlerp (tmp, a, d) followed by nlerp (result, tmp, + * b)). Finally nlerp is cheaper than slerp so it can be a good choice + * if you don't need the constant speed property of the slerp() function. + * + * Notable properties: + * + * + * commutative: Yes + * + * + * constant velocity: No + * + * + * torque minimal (travels along the surface of the 4-sphere): Yes + * + * + * faster than cogl_quaternion_slerp() + * + * + */ +void +cogl_quaternion_nlerp (CoglQuaternion *result, + const CoglQuaternion *a, + const CoglQuaternion *b, + float t); +/** + * cogl_quaternion_squad: + * + * + * Since: 2.0 + */ +void +cogl_quaternion_squad (CoglQuaternion *result, + const CoglQuaternion *prev, + const CoglQuaternion *a, + const CoglQuaternion *b, + const CoglQuaternion *next, + float t); + +/** + * cogl_get_static_identity_quaternion: + * + * Returns a pointer to a singleton quaternion constant describing the + * canonical identity [1 (0, 0, 0)] which represents no rotation. + * + * If you multiply a quaternion with the identity quaternion you will + * get back the same value as the original quaternion. + * + * Returns: A pointer to an identity quaternion + * + * Since: 2.0 + */ +const CoglQuaternion * +cogl_get_static_identity_quaternion (void); + +/** + * cogl_get_static_zero_quaternion: + * + * Returns: a pointer to a singleton quaternion constant describing a + * rotation of 180 degrees around a degenerate axis: + * [0 (0, 0, 0)] + * + * Since: 2.0 + */ +const CoglQuaternion * +cogl_get_static_zero_quaternion (void); + +G_END_DECLS + +#endif /* __COGL_QUATERNION_H__ */ + diff --git a/cogl/cogl-types.h b/cogl/cogl-types.h index 47a17b12e..28644a942 100644 --- a/cogl/cogl-types.h +++ b/cogl/cogl-types.h @@ -118,6 +118,10 @@ cogl_object_unref (void *object); */ typedef void (* CoglFuncPtr) (void); +/* We forward declare this in cogl-types to avoid circular dependencies + * between cogl-matrix.h, cogl-euler.h and cogl-quaterion.h */ +typedef struct _CoglMatrix CoglMatrix; + /** * CoglFixed: * diff --git a/cogl/cogl.h b/cogl/cogl.h index cbd2d0c0c..21a453d91 100644 --- a/cogl/cogl.h +++ b/cogl/cogl.h @@ -77,6 +77,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer; #include #include #include +#include #include #include #include diff --git a/doc/reference/cogl-2.0/cogl-docs.xml.in b/doc/reference/cogl-2.0/cogl-docs.xml.in index 6e02ecffc..70a476c69 100644 --- a/doc/reference/cogl-2.0/cogl-docs.xml.in +++ b/doc/reference/cogl-2.0/cogl-docs.xml.in @@ -112,6 +112,7 @@ + diff --git a/doc/reference/cogl-2.0/cogl-sections.txt b/doc/reference/cogl-2.0/cogl-sections.txt index e73a3480e..883135e72 100644 --- a/doc/reference/cogl-2.0/cogl-sections.txt +++ b/doc/reference/cogl-2.0/cogl-sections.txt @@ -355,6 +355,34 @@ cogl_matrix_get_array cogl_matrix_get_inverse +
+cogl-quaternion +Quaternions (Rotations) +CoglQuaternion +cogl_quaternion_init_identity +cogl_quaternion_init +cogl_quaternion_init_from_angle_vector +cogl_quaternion_init_from_array +cogl_quaternion_init_from_x_rotation +cogl_quaternion_init_from_y_rotation +cogl_quaternion_init_from_z_rotation +cogl_quaternion_equal +cogl_quaternion_copy +cogl_quaternion_free +cogl_quaternion_get_rotation_angle +cogl_quaternion_get_rotation_axis +cogl_quaternion_normalize +cogl_quaternion_dot_product +cogl_quaternion_invert +cogl_quaternion_multiply +cogl_quaternion_pow +cogl_quaternion_slerp +cogl_quaternion_nlerp +cogl_quaternion_squad +cogl_get_static_identity_quaternion +cogl_get_static_zero_quaternion +
+
cogl-pipeline Pipeline diff --git a/doc/reference/cogl/cogl-docs.xml.in b/doc/reference/cogl/cogl-docs.xml.in index a2a315933..4f3c45220 100644 --- a/doc/reference/cogl/cogl-docs.xml.in +++ b/doc/reference/cogl/cogl-docs.xml.in @@ -97,6 +97,7 @@ + diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 359bfac70..10423e08b 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -658,3 +658,31 @@ cogl_program_uniform_matrix cogl_offscreen_ref cogl_offscreen_unref
+ +
+cogl-quaternion +Quaternions (Rotations) +CoglQuaternion +cogl_quaternion_init_identity +cogl_quaternion_init +cogl_quaternion_init_from_angle_vector +cogl_quaternion_init_from_array +cogl_quaternion_init_from_x_rotation +cogl_quaternion_init_from_y_rotation +cogl_quaternion_init_from_z_rotation +cogl_quaternion_equal +cogl_quaternion_copy +cogl_quaternion_free +cogl_quaternion_get_rotation_angle +cogl_quaternion_get_rotation_axis +cogl_quaternion_normalize +cogl_quaternion_dot_product +cogl_quaternion_invert +cogl_quaternion_multiply +cogl_quaternion_pow +cogl_quaternion_slerp +cogl_quaternion_nlerp +cogl_quaternion_squad +cogl_get_static_identity_quaternion +cogl_get_static_zero_quaternion +