[cogl] math: Adds an experimental euler API
- From: Robert Bragg <rbragg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [cogl] math: Adds an experimental euler API
- Date: Mon, 16 May 2011 13:14:04 +0000 (UTC)
commit df1915d95735f4d13489f955235f5e532161494a
Author: Robert Bragg <robert linux intel com>
Date: Thu Feb 25 01:40:29 2010 +0000
math: Adds an experimental euler API
This adds an experimental CoglEuler data type and the following new
functions:
cogl_euler_init
cogl_euler_init_from_matrix
cogl_euler_init_from_quaternion
cogl_euler_equal
cogl_euler_copy
cogl_euler_free
cogl_quaternion_init_from_euler
Since this is experimental API you need to define
COGL_ENABLE_EXPERIMENTAL_API before including cogl.h
cogl/Makefile.am | 2 +
cogl/cogl-euler.c | 183 ++++++++++++++++++++++
cogl/cogl-euler.h | 252 ++++++++++++++++++++++++++++++
cogl/cogl-quaternion.c | 37 +++++
cogl/cogl-quaternion.h | 6 +
cogl/cogl-types.h | 8 +
cogl/cogl.h | 1 +
doc/reference/cogl-2.0/cogl-sections.txt | 11 ++
doc/reference/cogl/cogl-sections.txt | 12 ++
9 files changed, 512 insertions(+), 0 deletions(-)
---
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 8e66aa4..ae0d374 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-euler.h \
$(srcdir)/cogl-quaternion.h \
$(srcdir)/cogl-matrix.h \
$(srcdir)/cogl-offscreen.h \
@@ -231,6 +232,7 @@ cogl_sources_c = \
$(srcdir)/cogl-primitive.c \
$(srcdir)/cogl-matrix.c \
$(srcdir)/cogl-vector.c \
+ $(srcdir)/cogl-euler.c \
$(srcdir)/cogl-quaternion.c \
$(srcdir)/cogl-matrix-private.h \
$(srcdir)/cogl-matrix-stack.c \
diff --git a/cogl/cogl-euler.c b/cogl/cogl-euler.c
new file mode 100644
index 0000000..afe24be
--- /dev/null
+++ b/cogl/cogl-euler.c
@@ -0,0 +1,183 @@
+/*
+ * 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 <robert linux intel com>
+ */
+
+#include <cogl.h>
+#include <cogl-euler.h>
+
+#include <math.h>
+#include <string.h>
+
+void
+cogl_euler_init (CoglEuler *euler,
+ float heading,
+ float pitch,
+ float roll)
+{
+ euler->heading = heading;
+ euler->pitch = pitch;
+ euler->roll = roll;
+}
+
+void
+cogl_euler_init_from_matrix (CoglEuler *euler,
+ const CoglMatrix *matrix)
+{
+ /*
+ * Extracting a canonical Euler angle from a matrix:
+ * (where it is assumed the matrix contains no scaling, mirroring or
+ * skewing)
+ *
+ * A Euler angle is a combination of three rotations around mutually
+ * perpendicular axis. For this algorithm they are:
+ *
+ * Heading: A rotation about the Y axis by an angle H:
+ * | cosH 0 sinH|
+ * | 0 1 0|
+ * |-sinH 0 cosH|
+ *
+ * Pitch: A rotation around the X axis by an angle P:
+ * |1 0 0|
+ * |0 cosP -sinP|
+ * |0 sinP cosP|
+ *
+ * Roll: A rotation about the Z axis by an angle R:
+ * |cosR -sinR 0|
+ * |sinR cosR 0|
+ * | 0 0 1|
+ *
+ * When multiplied as matrices this gives:
+ * | cosHcosR+sinHsinPsinR sinRcosP -sinHcosR+cosHsinPsinR|
+ * M = |-cosHsinR+sinHsinPcosR cosRcosP sinRsinH+cosHsinPcosB|
+ * | sinHcosP -sinP cosHcosP |
+ *
+ * Given that there are an infinite number of ways to represent
+ * a given orientation, the "canonical" Euler angle is any such that:
+ * -180 < H < 180,
+ * -180 < R < 180 and
+ * -90 < P < 90
+ *
+ * M[3][2] = -sinP lets us immediately solve for P = asin(-M[3][2])
+ * (Note: asin has a range of +-90)
+ * This gives cosP
+ * This means we can use M[3][1] to calculate sinH:
+ * sinH = M[3][1]/cosP
+ * And use M[3][3] to calculate cosH:
+ * cosH = M[3][3]/cosP
+ * This lets us calculate H = atan2(sinH,cosH), but we optimise this:
+ * 1st note: atan2(x, y) does: atan(x/y) and uses the sign of x and y to
+ * determine the quadrant of the final angle.
+ * 2nd note: we know cosP is > 0 (ignoring cosP == 0)
+ * Therefore H = atan2((M[3][1]/cosP) / (M[3][3]/cosP)) can be simplified
+ * by skipping the division by cosP since it won't change the x/y ratio
+ * nor will it change their sign. This gives:
+ * H = atan2(M[3][1], M[3][3])
+ * R is computed in the same way as H from M[1][2] and M[2][2] so:
+ * R = atan2(M[1][2], M[2][2])
+ * Note: If cosP were == 0 then H and R could not be calculated as above
+ * because all the necessary matrix values would == 0. In other words we are
+ * pitched vertically and so H and R would now effectively rotate around the
+ * same axis - known as "Gimbal lock". In this situation we will set all the
+ * rotation on H and set R = 0.
+ * So with P = R = 0 we have cosP = 0, sinR = 0 and cosR = 1
+ * We can substitute those into the above equation for M giving:
+ * | cosH 0 -sinH|
+ * |sinHsinP 0 cosHsinP|
+ * | 0 -sinP 0|
+ * And calculate H as atan2 (-M[3][2], M[1][1])
+ */
+
+ float sinP;
+ float H; /* heading */
+ float P; /* pitch */
+ float R; /* roll */
+
+ /* NB: CoglMatrix provides struct members named according to the
+ * [row][column] indexed. So matrix->zx is row 3 column 1. */
+ sinP = -matrix->zy;
+
+ /* Determine the Pitch, avoiding domain errors with asin () which
+ * might occur due to previous imprecision in manipulating the
+ * matrix. */
+ if (sinP <= -1.0f)
+ P = -G_PI_2;
+ else if (sinP >= 1.0f)
+ P = G_PI_2;
+ else
+ P = asinf (sinP);
+
+ /* If P is too close to 0 then we have hit Gimbal lock */
+ if (sinP > 0.999f)
+ {
+ H = atan2f (-matrix->zy, matrix->xx);
+ R = 0;
+ }
+ else
+ {
+ H = atan2f (matrix->zx, matrix->zz);
+ R = atan2f (matrix->xy, matrix->yy);
+ }
+
+ euler->heading = H;
+ euler->pitch = P;
+ euler->roll = R;
+}
+
+gboolean
+cogl_euler_equal (gconstpointer v1, gconstpointer v2)
+{
+ const CoglEuler *a = v1;
+ const CoglEuler *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->heading == b->heading &&
+ a->pitch == b->pitch &&
+ a->roll == b->roll);
+}
+
+CoglEuler *
+cogl_euler_copy (const CoglEuler *src)
+{
+ if (G_LIKELY (src))
+ {
+ CoglEuler *new = g_slice_new (CoglEuler);
+ memcpy (new, src, sizeof (float) * 3);
+ return new;
+ }
+ else
+ return NULL;
+}
+
+void
+cogl_euler_free (CoglEuler *euler)
+{
+ g_slice_free (CoglEuler, euler);
+}
+
diff --git a/cogl/cogl-euler.h b/cogl/cogl-euler.h
new file mode 100644
index 0000000..ca623e8
--- /dev/null
+++ b/cogl/cogl-euler.h
@@ -0,0 +1,252 @@
+/*
+ * 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 <robert linux intel com>
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_EULER_H
+#define __COGL_EULER_H
+
+#include <cogl/cogl-types.h>
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:cogl-euler
+ * @short_description: Functions for initializing and manipulating
+ * euler angles.
+ *
+ * Euler angles are a simple representation of a 3 dimensional
+ * rotation; comprised of 3 ordered heading, pitch and roll rotations.
+ * An important thing to understand is that the axis of rotation
+ * belong to the object being rotated and so they also rotate as each
+ * of the heading, pitch and roll rotations are applied.
+ *
+ * One way to consider euler angles is to imagine controlling an
+ * aeroplane, where you first choose a heading (Such as flying south
+ * east), then you set the pitch (such as 30 degrees to take off) and
+ * then you might set a roll, by dipping the left, wing as you prepare
+ * to turn.
+ *
+ * They have some advantages and limitations that it helps to be
+ * aware of:
+ *
+ * Advantages:
+ * <itemizedlist>
+ * <listitem>
+ * Easy to understand and use, compared to quaternions and matrices,
+ * so may be a good choice for a user interface.
+ * <listitem>
+ * <listitem>
+ * Efficient storage, needing only 3 components any rotation can be
+ * represented.
+ * <note>Actually the #CoglEuler type isn't optimized for size because
+ * we may cache the equivalent #CoglQuaternion along with a euler
+ * rotation, but it would be trivial for an application to track the
+ * components of euler rotations in a packed float array if optimizing
+ * for size was important. The values could be passed to Cogl only when
+ * manipulation is necessary.</note>
+ * </listitem>
+ * </itemizedlist>
+ *
+ * Disadvantages:
+ * <itemizedlist>
+ * <listitem>
+ * Aliasing: it's possible to represent some rotations with multiple
+ * different heading, pitch and roll rotations.
+ * </listitem>
+ * <listitem>
+ * They can suffer from a problem called Gimbal Lock. A good
+ * explanation of this can be seen on wikipedia here:
+ * http://en.wikipedia.org/wiki/Gimbal_lock but basically two
+ * of the axis of rotation may become aligned and so you loose a
+ * degree of freedom. For example a pitch of +-90° would mean that
+ * heading and bank rotate around the same axis.
+ * </listitem>
+ * <listitem>
+ * If you use euler angles to orient something in 3D space and try to
+ * transition between orientations by interpolating the component
+ * angles you probably wont get the transitions you expect as they may
+ * not follow the shortest path between the two orientations.
+ * </listitem>
+ * <listitem>
+ * There's no standard to what order the component axis rotations are
+ * applied. The most common convention seems to be what we do in Cogl
+ * with heading (y-axis), pitch (x-axis) and then roll (z-axis), but
+ * other software might apply x-axis, y-axis then z-axis or any other
+ * order so you need to consider this if you are accepting euler
+ * rotations from some other software. Other software may also use
+ * slightly different aeronautical terms, such as "yaw" instead of
+ * "heading" or "bank" instead of "roll".
+ * </listitem>
+ * </itemlist>
+ *
+ * To minimize the aliasing issue we may refer to "Canonical Euler"
+ * angles where heading and roll are restricted to +- 180° and pitch is
+ * restricted to +- 90°. If pitch is +- 90° bank is set to 0°.
+ *
+ * Quaternions don't suffer from Gimbal Lock and they can be nicely
+ * interpolated between, their disadvantage is that they don't have an
+ * intuitive representation.
+ *
+ * A common practice is to accept angles in the intuitive Euler form
+ * and convert them to quaternions internally to avoid Gimbal Lock and
+ * handle interpolations. See cogl_quaternion_init_from_euler().
+ */
+
+/**
+ * CoglEuler:
+ * @heading: Angle to rotate around an object's y axis
+ * @pitch: Angle to rotate around an object's x axis
+ * @roll: Angle to rotate around an object's z axis
+ *
+ * Represents an ordered rotation first of @heading degrees around an
+ * object's y axis, then @pitch degrees around an object's x axis and
+ * finally @roll degrees around an object's z axis.
+ *
+ * <note>It's important to understand the that axis are associated
+ * with the object being rotated, so the axis also rotate in sequence
+ * with the rotations being applied.</note>
+ *
+ * The members of a #CoglEuler can be initialized, for example, with
+ * cogl_euler_init() and cogl_euler_init_from_quaternion ().
+ *
+ * You may also want to look at cogl_quaternion_init_from_euler() if
+ * you want to do interpolation between 3d rotations.
+ *
+ * Since: 2.0
+ */
+struct _CoglEuler
+{
+ /*< public > */
+ float heading;
+ float pitch;
+ float roll;
+
+ /*< private > */
+ /* May cached a quaternion here in the future */
+ float padding0;
+ float padding1;
+ float padding2;
+ float padding3;
+ float padding4;
+};
+
+/**
+ * cogl_euler_init:
+ * @euler: The #CoglEuler angle to initialize
+ * @heading: Angle to rotate around an object's y axis
+ * @pitch: Angle to rotate around an object's x axis
+ * @roll: Angle to rotate around an object's z axis
+ *
+ * Initializes @euler to represent a rotation of @x_angle degrees
+ * around the x axis, then @y_angle degrees around the y_axis and
+ * @z_angle degrees around the z axis.
+ *
+ * Since: 2.0
+ */
+void
+cogl_euler_init (CoglEuler *euler,
+ float heading,
+ float pitch,
+ float roll);
+
+/**
+ * cogl_euler_init_from_matrix:
+ * @euler: The #CoglEuler angle to initialize
+ * @matrix: A #CoglMatrix containing a rotation, but no scaling,
+ * mirroring or skewing.
+ *
+ * Extracts a euler rotation from the given @matrix and
+ * initializses @euler with the component x, y and z rotation angles.
+ */
+void
+cogl_euler_init_from_matrix (CoglEuler *euler,
+ const CoglMatrix *matrix);
+
+/**
+ * cogl_euler_init_from_quaternion:
+ * @euler: The #CoglEuler angle to initialize
+ * @quaternion: A #CoglEuler with the rotation to initialize with
+ *
+ * Initializes a @euler rotation with the equivalent rotation
+ * represented by the given @quaternion.
+ */
+void
+cogl_euler_init_from_quaternion (CoglEuler *euler,
+ const CoglQuaternion *quaternion);
+
+/**
+ * cogl_euler_equal:
+ * @v1: The first euler angle to compare
+ * @v1: The second euler angle to compare
+ *
+ * Compares the two given euler angles @v1 and @v1 and it they are
+ * equal returns %TRUE else %FALSE.
+ *
+ * <note>This function only checks that all three components rotations
+ * are numerically equal, it does not consider that some rotations
+ * can be represented with different component rotations</note>
+ *
+ * Returns: %TRUE if @v1 and @v2 are equal else %FALSE.
+ * Since: 2.0
+ */
+gboolean
+cogl_euler_equal (gconstpointer v1, gconstpointer v2);
+
+/**
+ * cogl_euler_copy:
+ * @src: A #CoglEuler to copy
+ *
+ * Allocates a new #CoglEuler and initilizes it with the component
+ * angles of @src. The newly allocated euler should be freed using
+ * cogl_euler_free().
+ *
+ * Returns: A newly allocated #CoglEuler
+ * Since: 2.0
+ */
+CoglEuler *
+cogl_euler_copy (const CoglEuler *src);
+
+/**
+ * cogl_euler_free:
+ * @euler: A #CoglEuler allocated via cogl_euler_copy()
+ *
+ * Frees a #CoglEuler that was previously allocated using
+ * cogl_euler_copy().
+ *
+ * Since: 2.0
+ */
+void
+cogl_euler_free (CoglEuler *euler);
+
+G_END_DECLS
+
+#endif /* __COGL_EULER_H */
+
diff --git a/cogl/cogl-quaternion.c b/cogl/cogl-quaternion.c
index ae5c798..ff59720 100644
--- a/cogl/cogl-quaternion.c
+++ b/cogl/cogl-quaternion.c
@@ -39,6 +39,7 @@
#include <cogl-quaternion-private.h>
#include <cogl-matrix.h>
#include <cogl-vector.h>
+#include <cogl-euler.h>
#include <string.h>
#include <math.h>
@@ -175,6 +176,42 @@ cogl_quaternion_init_from_z_rotation (CoglQuaternion *quaternion,
}
void
+cogl_quaternion_init_from_euler (CoglQuaternion *quaternion,
+ const CoglEuler *euler)
+{
+ /* 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 sin_heading =
+ sinf (euler->heading * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f);
+ float sin_pitch =
+ sinf (euler->pitch * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f);
+ float sin_roll =
+ sinf (euler->roll * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f);
+ float cos_heading =
+ cosf (euler->heading * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f);
+ float cos_pitch =
+ cosf (euler->pitch * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f);
+ float cos_roll =
+ cosf (euler->roll * _COGL_QUATERNION_DEGREES_TO_RADIANS * 0.5f);
+
+ quaternion->w =
+ cos_heading * cos_pitch * cos_roll +
+ sin_heading * sin_pitch * sin_roll;
+
+ quaternion->x =
+ cos_heading * sin_pitch * cos_roll +
+ sin_heading * cos_pitch * sin_roll;
+ quaternion->y =
+ sin_heading * cos_pitch * cos_roll -
+ cos_heading * sin_pitch * sin_roll;
+ quaternion->z =
+ cos_heading * cos_pitch * sin_roll -
+ sin_heading * sin_pitch * cos_roll;
+}
+
+void
cogl_quaternion_init_from_quaternion (CoglQuaternion *quaternion,
CoglQuaternion *src)
{
diff --git a/cogl/cogl-quaternion.h b/cogl/cogl-quaternion.h
index da4e765..1b83bac 100644
--- a/cogl/cogl-quaternion.h
+++ b/cogl/cogl-quaternion.h
@@ -49,6 +49,8 @@ G_BEGIN_DECLS
* rotation may become aligned and you loose a degree of freedom.
* (<ulink url="http://en.wikipedia.org/wiki/Gimbal_lock"/>).
*/
+#include <cogl/cogl-vector.h>
+#include <cogl/cogl-euler.h>
/**
* CoglQuaternion:
@@ -244,6 +246,10 @@ void
cogl_quaternion_init_from_z_rotation (CoglQuaternion *quaternion,
float angle);
+void
+cogl_quaternion_init_from_euler (CoglQuaternion *quaternion,
+ const CoglEuler *euler);
+
/**
* cogl_quaternion_equal:
* @a: A #CoglQuaternion
diff --git a/cogl/cogl-types.h b/cogl/cogl-types.h
index 28644a9..36f5269 100644
--- a/cogl/cogl-types.h
+++ b/cogl/cogl-types.h
@@ -122,6 +122,14 @@ typedef void (* CoglFuncPtr) (void);
* between cogl-matrix.h, cogl-euler.h and cogl-quaterion.h */
typedef struct _CoglMatrix CoglMatrix;
+/* Same as above we forward declared CoglQuaternion to avoid
+ * circular dependencies. */
+typedef struct _CoglQuaternion CoglQuaternion;
+
+/* Same as above we forward declared CoglEuler to avoid
+ * circular dependencies. */
+typedef struct _CoglEuler CoglEuler;
+
/**
* CoglFixed:
*
diff --git a/cogl/cogl.h b/cogl/cogl.h
index 21a453d..52c2940 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -77,6 +77,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer;
#include <cogl/cogl-buffer.h>
#include <cogl/cogl-pixel-array.h>
#include <cogl/cogl-vector.h>
+#include <cogl/cogl-euler.h>
#include <cogl/cogl-quaternion.h>
#include <cogl/cogl-texture-3d.h>
#include <cogl/cogl-index-array.h>
diff --git a/doc/reference/cogl-2.0/cogl-sections.txt b/doc/reference/cogl-2.0/cogl-sections.txt
index 883135e..3a0d0d2 100644
--- a/doc/reference/cogl-2.0/cogl-sections.txt
+++ b/doc/reference/cogl-2.0/cogl-sections.txt
@@ -355,6 +355,17 @@ cogl_matrix_get_array
cogl_matrix_get_inverse
</SECTION>
+<FILE>cogl-euler</FILE>
+<TITLE>Eulers (Rotations)</TITLE>
+CoglEuler
+cogl_euler_init
+cogl_euler_init_from_matrix
+cogl_euler_init_from_quaternion
+cogl_euler_equal
+cogl_euler_copy
+cogl_euler_free
+</SECTION>
+
<SECTION>
<FILE>cogl-quaternion</FILE>
<TITLE>Quaternions (Rotations)</TITLE>
diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt
index 10423e0..4e521a5 100644
--- a/doc/reference/cogl/cogl-sections.txt
+++ b/doc/reference/cogl/cogl-sections.txt
@@ -660,6 +660,18 @@ cogl_offscreen_unref
</SECTION>
<SECTION>
+<FILE>cogl-euler</FILE>
+<TITLE>Eulers (Rotations)</TITLE>
+CoglEuler
+cogl_euler_init
+cogl_euler_init_from_matrix
+cogl_euler_init_from_quaternion
+cogl_euler_equal
+cogl_euler_copy
+cogl_euler_free
+</SECTION>
+
+<SECTION>
<FILE>cogl-quaternion</FILE>
<TITLE>Quaternions (Rotations)</TITLE>
CoglQuaternion
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]