--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/m3g/m3gcore11/src/m3g_keyframesequence.c Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,1004 @@
+/*
+* 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: KeyframeSequence implementation
+*
+*/
+
+
+/*!
+ * \internal
+ * \file
+ * \brief KeyframeSequence implementation
+ */
+
+#ifndef M3G_CORE_INCLUDE
+# error included by m3g_core.c; do not compile separately.
+#endif
+
+#include "m3g_keyframesequence.h"
+#include "m3g_memory.h"
+
+/*----------------------------------------------------------------------
+ * Internal functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \internal
+ * \brief Destroys this KeyframeSequence object.
+ *
+ * \param obj KeyframeSequence object
+ */
+static void m3gDestroyKeyframeSequence(Object *obj)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *) obj;
+ M3G_VALIDATE_OBJECT(sequence);
+ {
+ Interface *m3g = M3G_INTERFACE(sequence);
+ m3gFree(m3g, sequence->keyframes);
+ m3gFree(m3g, sequence->keyframeTimes);
+ m3gFree(m3g, sequence->inTangents);
+ m3gFree(m3g, sequence->outTangents);
+ m3gFree(m3g, sequence->a);
+ m3gFree(m3g, sequence->b);
+ }
+ m3gDestroyObject(&sequence->object);
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Object3D method.
+ *
+ * \param originalObj original KeyframeSequence object
+ * \param cloneObj pointer to cloned KeyframeSequence object
+ * \param pairs array for all object-duplicate pairs
+ * \param numPairs number of pairs
+ */
+static M3Gbool m3gKeyframeSequenceDuplicate(const Object *originalObj,
+ Object **cloneObj,
+ Object **pairs,
+ M3Gint *numPairs)
+{
+ KeyframeSequence *original = (KeyframeSequence *)originalObj;
+ KeyframeSequence *clone =
+ (KeyframeSequence *)m3gCreateKeyframeSequence(originalObj->interface,
+ original->numKeyframes,
+ original->numComponents,
+ original->interpolation);
+ *cloneObj = (Object *)clone;
+ if (*cloneObj == NULL) {
+ return M3G_FALSE;
+ }
+
+ if(m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
+ M3Gsizei n = original->numKeyframes * original->numComponents;
+
+ m3gCopy(clone->keyframes, original->keyframes, n * sizeof(M3Gfloat));
+ m3gCopy(clone->keyframeTimes, original->keyframeTimes, original->numKeyframes * sizeof(M3Gint));
+ if (original->dirty == M3G_FALSE) {
+ if (original->inTangents) {
+ m3gCopy(clone->inTangents, original->inTangents, n * sizeof(M3Gfloat));
+ m3gCopy(clone->outTangents, original->outTangents, n * sizeof(M3Gfloat));
+ }
+ if (original->a) {
+ m3gCopy(clone->a, original->a, original->numKeyframes * sizeof(Quat));
+ m3gCopy(clone->b, original->b, original->numKeyframes * sizeof(Quat));
+ }
+ }
+ else {
+ clone->dirty = M3G_TRUE;
+ }
+
+ clone->duration = original->duration;
+ clone->closed = original->closed;
+ clone->firstValid = original->firstValid;
+ clone->lastValid = original->lastValid;
+ return M3G_TRUE;
+ }
+ else {
+ return M3G_FALSE;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Initializes a KeyframeSequence object. See specification
+ * for default values.
+ *
+ * \param m3g M3G interface
+ * \param sequence KeyframeSequence object
+ * \param numKeyframes number of keyframes
+ * \param numComponents number of components
+ * \param interpolation interpolation type
+ * \retval KeyframeSequence initialized KeyframeSequence object
+ * \retval NULL initialization failed
+ */
+static KeyframeSequence *m3gInitKeyframeSequence(Interface *m3g,
+ KeyframeSequence *sequence,
+ M3Gint numKeyframes,
+ M3Gint numComponents,
+ M3Gint interpolation)
+{
+ m3gInitObject(&sequence->object, m3g, M3G_CLASS_KEYFRAME_SEQUENCE);
+
+ /* Set keyframe parameters */
+
+ sequence->numKeyframes = numKeyframes;
+ sequence->numComponents = numComponents;
+ sequence->interpolation = interpolation;
+ sequence->lastValid = numKeyframes - 1; /* firstValid defaults to 0 */
+
+ /* Allocate keyframe and tangent data */
+ {
+ M3Gsizei n = numKeyframes * numComponents;
+
+ sequence->keyframes = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
+ if (sequence->keyframes == NULL) {
+ goto AllocFailed;
+ }
+ sequence->keyframeTimes = (M3Gint *)m3gAllocZ(m3g, numKeyframes * sizeof(M3Gint));
+ if (sequence->keyframeTimes == NULL) {
+ goto AllocFailed;
+ }
+
+ if (interpolation == M3G_SPLINE) {
+ sequence->inTangents = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
+ sequence->outTangents = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
+ if (sequence->inTangents == NULL || sequence->outTangents == NULL) {
+ goto AllocFailed;
+ }
+ }
+ else if (interpolation == M3G_SQUAD) {
+ sequence->a = (Quat *)m3gAlloc(m3g, numKeyframes * sizeof(Quat));
+ sequence->b = (Quat *)m3gAlloc(m3g, numKeyframes * sizeof(Quat));
+ if (sequence->a == NULL || sequence->b == NULL) {
+ goto AllocFailed;
+ }
+ }
+
+ /* Success; just make a note that the data is not valid yet */
+
+ sequence->dirty = M3G_TRUE;
+ return sequence;
+
+AllocFailed:
+ /* The destructor contains exactly the code we need for this,
+ * so just call that */
+
+ m3gDestroyKeyframeSequence((Object*) sequence);
+ return NULL;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Checks the validity of keyframe timings
+ *
+ * \param sequence KeyframeSequence object
+ * \retval M3G_TRUE sequence valid
+ * \retval M3G_FALSE sequence invalid
+ */
+static M3Gbool m3gValidSequence(const KeyframeSequence *sequence)
+{
+ const M3Gint last = sequence->lastValid;
+ M3Gint current = sequence->firstValid;
+
+ while (current != last) {
+ M3Gint next = (current < sequence->numKeyframes-1) ? current + 1 : 0;
+ if (sequence->keyframeTimes[next] < sequence->keyframeTimes[current]) {
+ return M3G_FALSE;
+ }
+ current = next;
+ }
+ return (sequence->keyframeTimes[last] <= sequence->duration);
+}
+
+
+/*!
+ * \internal
+ * \brief Get number of components
+ *
+ * \param sequence KeyframeSequence object
+ * \return number of components
+ */
+static M3Gint m3gGetNumComponents(const KeyframeSequence *sequence)
+{
+ return sequence->numComponents;
+}
+
+/*!
+ * \internal
+ * \brief Get next keyframe index
+ *
+ * \param sequence KeyframeSequence object
+ * \param ind current index
+ * \return next index
+ */
+static M3Gint m3gNextKeyframeIndex(const KeyframeSequence *sequence, M3Gint ind)
+{
+ if (ind == sequence->lastValid) {
+ return sequence->firstValid;
+ }
+ else if (ind == sequence->numKeyframes - 1) {
+ return 0;
+ }
+ else {
+ return (ind + 1);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Get previous keyframe index
+ *
+ * \param sequence KeyframeSequence object
+ * \param ind current index
+ * \return previous index
+ */
+static M3Gint m3gPreviousKeyframeIndex(const KeyframeSequence *sequence, M3Gint ind)
+{
+ if (ind == sequence->firstValid) {
+ return sequence->lastValid;
+ }
+ else if (ind == 0) {
+ return (sequence->numKeyframes - 1);
+ }
+ else {
+ return (ind - 1);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Get keyframe at index
+ *
+ * \param seq KeyframeSequence object
+ * \param idx keyframe index
+ * \return keyframe value
+ */
+static M3G_INLINE const M3Gfloat *m3gKeyframeAt(const KeyframeSequence *seq, M3Gint idx)
+{
+ return seq->keyframes + idx * seq->numComponents;
+}
+
+/*!
+ * \internal
+ * \brief Get keyframe at index -1
+ *
+ * \param seq KeyframeSequence object
+ * \param idx keyframe index
+ * \return keyframe value
+ */
+static M3G_INLINE const M3Gfloat *m3gKeyframeBefore(const KeyframeSequence *seq, M3Gint idx)
+{
+ return m3gKeyframeAt(seq, m3gPreviousKeyframeIndex(seq, idx));
+}
+
+/*!
+ * \internal
+ * \brief Get keyframe at index + 1
+ *
+ * \param seq KeyframeSequence object
+ * \param idx keyframe index
+ * \return keyframe value
+ */
+static M3G_INLINE const M3Gfloat *m3gKeyframeAfter(const KeyframeSequence *seq, M3Gint idx)
+{
+ return m3gKeyframeAt(seq, m3gNextKeyframeIndex(seq, idx));
+}
+
+/*!
+ * \internal
+ * \brief Get tangent to index
+ *
+ * \param seq KeyframeSequence object
+ * \param idx keyframe index
+ * \return tangent value
+ */
+static M3G_INLINE const M3Gfloat *m3gTangentTo(const KeyframeSequence *seq, M3Gint idx)
+{
+ M3G_ASSERT(seq->inTangents != NULL);
+ return seq->inTangents + idx * seq->numComponents;
+}
+
+/*!
+ * \internal
+ * \brief Get tangent from index
+ *
+ * \param seq KeyframeSequence object
+ * \param idx keyframe index
+ * \return tangent value
+ */
+static M3G_INLINE const M3Gfloat *m3gTangentFrom(const KeyframeSequence *seq, M3Gint idx)
+{
+ M3G_ASSERT(seq->outTangents != NULL);
+ return seq->outTangents + idx * seq->numComponents;
+}
+
+/*!
+ * \internal
+ * \brief Get time delta
+ *
+ * \param sequence KeyframeSequence object
+ * \param ind keyframe index
+ * \return time delta
+ */
+static M3Gint m3gTimeDelta(const KeyframeSequence *sequence, M3Gint ind)
+{
+ if (ind == sequence->lastValid) {
+ return
+ (sequence->duration
+ - sequence->keyframeTimes[sequence->lastValid])
+ + sequence->keyframeTimes[sequence->firstValid];
+ }
+ return sequence->keyframeTimes[m3gNextKeyframeIndex(sequence, ind)]
+ - sequence->keyframeTimes[ind];
+}
+
+/*!
+ * \internal
+ * \brief Get incoming tangent scale
+ *
+ * \param sequence KeyframeSequence object
+ * \param ind keyframe index
+ * \return tangent scale
+ */
+static M3Gfloat m3gIncomingTangentScale(const KeyframeSequence *sequence,
+ M3Gint ind)
+{
+ if (!sequence->closed
+ && (ind == sequence->firstValid || ind == sequence->lastValid)) {
+ return 0;
+ }
+ else {
+ M3Gint prevind = m3gPreviousKeyframeIndex(sequence, ind);
+ return m3gDiv(m3gMul(2.0f, (M3Gfloat) m3gTimeDelta(sequence, prevind)),
+ (M3Gfloat)(m3gTimeDelta(sequence, ind)
+ + m3gTimeDelta(sequence, prevind)));
+ }
+}
+
+/*!
+ * \internal
+ * \brief Get outgoing tangent scale
+ *
+ * \param sequence KeyframeSequence object
+ * \param ind keyframe index
+ * \return tangent scale
+ */
+static M3Gfloat m3gOutgoingTangentScale(const KeyframeSequence *sequence,
+ M3Gint ind)
+{
+ if (!sequence->closed
+ && (ind == sequence->firstValid || ind == sequence->lastValid)) {
+ return 0;
+ }
+ else {
+ M3Gint prevind = m3gPreviousKeyframeIndex(sequence, ind);
+ return m3gDiv(m3gMul(2.0f, (M3Gfloat) m3gTimeDelta(sequence, ind)),
+ (M3Gfloat)(m3gTimeDelta(sequence, ind)
+ + m3gTimeDelta(sequence, prevind)));
+ }
+}
+
+/*!
+ * \internal
+ * \brief Precalculate all tangents
+ *
+ * \param sequence KeyframeSequence object
+ */
+static void m3gPrecalculateTangents(KeyframeSequence *sequence)
+{
+ M3Gint i, kf = sequence->firstValid;
+ do {
+ const M3Gfloat *prev = m3gKeyframeBefore(sequence, kf);
+ const M3Gfloat *next = m3gKeyframeAfter(sequence, kf);
+ const M3Gfloat sIn = m3gIncomingTangentScale(sequence, kf);
+ const M3Gfloat sOut = m3gOutgoingTangentScale(sequence, kf);
+ M3Gfloat *in = (M3Gfloat *) m3gTangentTo(sequence, kf);
+ M3Gfloat *out = (M3Gfloat *) m3gTangentFrom(sequence, kf);
+
+ for (i = 0; i < sequence->numComponents; ++i) {
+ in[i] = m3gMul(m3gMul(0.5f, (m3gSub(next[i], prev[i]))), sIn);
+ out[i] = m3gMul(m3gMul(0.5f, (m3gSub(next[i], prev[i]))), sOut);
+ }
+
+ kf = m3gNextKeyframeIndex(sequence, kf);
+ } while (kf != sequence->firstValid);
+}
+
+/*!
+ * \internal
+ * \brief Precalculate A and B
+ *
+ * \param sequence KeyframeSequence object
+ */
+static void m3gPrecalculateAB(KeyframeSequence *sequence)
+{
+ Quat start, end, prev, next;
+ Vec3 tangent, cfd;
+ M3Gfloat temp[4]; /* used for both quats and vectors */
+
+ M3Gint kf = sequence->firstValid;
+ do {
+
+ m3gSetQuat(&prev, m3gKeyframeBefore(sequence, kf));
+ m3gSetQuat(&start, m3gKeyframeAt(sequence, kf));
+ m3gSetQuat(&end, m3gKeyframeAfter(sequence, kf));
+ m3gSetQuat(&next, m3gKeyframeAfter(sequence, m3gNextKeyframeIndex(sequence, kf)));
+
+ /* Compute the centered finite difference at this
+ keyframe; note that this would be the tangent for basic
+ Catmull-Rom interpolation. */
+
+ m3gLogDiffQuat(&cfd, &start, &end);
+ m3gLogDiffQuat((Vec3*)temp, &prev, &start);
+ m3gAddVec3(&cfd, (Vec3*)temp);
+ m3gScaleVec3(&cfd, 0.5f);
+
+ /* Compute the outgoing tangent, scaled to compensate for
+ keyframe timing, then compute the "A" intermediate
+ quaternion. */
+
+ tangent = cfd;
+ m3gScaleVec3(&tangent, m3gOutgoingTangentScale(sequence, kf));
+
+ m3gLogDiffQuat((Vec3*)temp, &start, &end);
+ m3gSubVec3(&tangent, (Vec3*)temp);
+ m3gScaleVec3(&tangent, 0.5f);
+ m3gExpQuat((Quat*)temp, &tangent);
+ sequence->a[kf] = start;
+ m3gMulQuat(&(sequence->a[kf]), (Quat*)temp);
+
+ /* Then repeat the same steps for the incoming tangent and
+ the "B" intermediate quaternion. */
+
+ tangent = cfd;
+ m3gScaleVec3(&tangent, m3gIncomingTangentScale(sequence, kf));
+
+ m3gLogDiffQuat((Vec3*)temp, &prev, &start);
+ m3gSubVec3((Vec3*)temp, &tangent);
+ m3gScaleVec3((Vec3*)temp, 0.5f);
+ m3gExpQuat((Quat*)temp, (Vec3*)temp);
+ sequence->b[kf] = start;
+ m3gMulQuat(&(sequence->b[kf]), (Quat*)temp);
+
+ kf = m3gNextKeyframeIndex(sequence, kf);
+ } while (kf != sequence->firstValid);
+}
+
+/*!
+ * \internal
+ * \brief Update all tangents
+ *
+ * \param sequence KeyframeSequence object
+ */
+static void m3gUpdateTangents(KeyframeSequence *sequence)
+{
+ if (sequence->interpolation == M3G_SPLINE) {
+ m3gPrecalculateTangents(sequence);
+ }
+ else if (sequence->interpolation == M3G_SQUAD) {
+ m3gPrecalculateAB(sequence);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Linear interpolate
+ *
+ * \param sequence KeyframeSequence object
+ * \param sample input samples
+ * \param s speed
+ * \param startIndex start index
+ * \param endIndex end index
+ */
+static M3G_INLINE void m3gLerpSample(const KeyframeSequence *sequence,
+ M3Gfloat *sample,
+ M3Gfloat s,
+ M3Gint startIndex, M3Gint endIndex)
+{
+ const M3Gfloat *start = m3gKeyframeAt(sequence, startIndex);
+ const M3Gfloat *end = m3gKeyframeAt(sequence, endIndex);
+
+ m3gLerp(sequence->numComponents, sample, s, start, end);
+}
+
+/*!
+ * \internal
+ * \brief Spline interpolate
+ *
+ * \param sequence KeyframeSequence object
+ * \param sample input samples
+ * \param s speed
+ * \param startIndex start index
+ * \param endIndex end index
+ */
+static M3G_INLINE void m3gSplineSample(const KeyframeSequence *sequence,
+ M3Gfloat *sample,
+ M3Gfloat s,
+ M3Gint startIndex, M3Gint endIndex)
+{
+ const M3Gfloat *start, *end;
+ const M3Gfloat *tStart, *tEnd;
+
+ /* Get the required keyframe values and the (one-sided) tangents
+ * at the ends of the segment. */
+
+ start = m3gKeyframeAt(sequence, startIndex);
+ end = m3gKeyframeAt(sequence, endIndex);
+
+ tStart = m3gTangentFrom(sequence, startIndex);
+ tEnd = m3gTangentTo(sequence, endIndex);
+
+ /* Interpolate the final value using a Hermite spline. */
+
+ m3gHermite(sequence->numComponents, sample, s, start, end, tStart, tEnd);
+}
+
+/*!
+ * \internal
+ * \brief Spherical linear interpolate
+ *
+ * \param sequence KeyframeSequence object
+ * \param sample input samples
+ * \param s speed
+ * \param startIndex start index
+ * \param endIndex end index
+ */
+static M3G_INLINE void m3gSlerpSample(const KeyframeSequence *sequence,
+ M3Gfloat *sample,
+ M3Gfloat s,
+ M3Gint startIndex, M3Gint endIndex)
+{
+ if (sequence->numComponents != 4) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
+ return;
+ }
+
+ m3gSlerpQuat((Quat *) sample,
+ s,
+ (const Quat *) m3gKeyframeAt(sequence, startIndex),
+ (const Quat *) m3gKeyframeAt(sequence, endIndex));
+}
+
+/*!
+ * \internal
+ * \brief Spline interpolate quats
+ *
+ * \param sequence KeyframeSequence object
+ * \param sample input samples
+ * \param s speed
+ * \param startIndex start index
+ * \param endIndex end index
+ */
+static M3G_INLINE void m3gSquadSample(const KeyframeSequence *sequence,
+ M3Gfloat *sample,
+ M3Gfloat s,
+ M3Gint startIndex, M3Gint endIndex)
+{
+ if (sequence->numComponents != 4) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
+ return;
+ }
+
+ m3gSquadQuat((Quat *) sample,
+ s,
+ (const Quat *) m3gKeyframeAt(sequence, startIndex),
+ &(sequence->a[startIndex]),
+ &(sequence->b[endIndex]),
+ (const Quat *) m3gKeyframeAt(sequence, endIndex));
+}
+
+/*!
+ * \internal
+ * \brief Get sample
+ *
+ * \param sequence KeyframeSequence object
+ * \param time time
+ * \param sample pointer to sample
+ * \return sample validity
+ */
+static M3Gint m3gGetSample(KeyframeSequence *sequence,
+ M3Gint time,
+ M3Gfloat *sample)
+{
+ M3Gint start, end, i;
+ const M3Gfloat *value;
+ M3Gfloat s;
+
+ M3G_VALIDATE_OBJECT(sequence);
+
+ if (sequence->dirty == M3G_TRUE) {
+ if (!m3gValidSequence(sequence)) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_OPERATION);
+ return 0;
+ }
+ m3gUpdateTangents(sequence);
+ sequence->dirty = M3G_FALSE;
+ sequence->probablyNext = sequence->firstValid;
+ }
+
+ /* First, map the time to the valid range of a repeating
+ sequence, or handle the special end cases of an open-ended
+ sequence. */
+
+ if (sequence->closed) {
+ if (time < 0)
+ time = (time % sequence->duration) + sequence->duration;
+ else
+ time = time % sequence->duration;
+ if (time < sequence->keyframeTimes[sequence->firstValid]) {
+ time += sequence->duration;
+ }
+ }
+ else {
+ if (time < sequence->keyframeTimes[sequence->firstValid]) {
+ value = m3gKeyframeAt(sequence, sequence->firstValid);
+ for (i = 0; i < sequence->numComponents; i++)
+ sample[i] = value[i];
+ return (sequence->keyframeTimes[sequence->firstValid] - time);
+ }
+ else if (time >= sequence->keyframeTimes[sequence->lastValid]) {
+ value = m3gKeyframeAt(sequence, sequence->lastValid);
+ for (i = 0; i < sequence->numComponents; i++)
+ sample[i] = value[i];
+ /* \ define a meaningful constant */
+ return 0x7FFFFFFF;
+ }
+ }
+
+ /* Search for the starting keyframe of the segment to
+ interpolate. Starting the search from the previously
+ used keyframe, we are very likely to find the match
+ sooner than if we'd start from the first keyframe. */
+
+ start = sequence->probablyNext;
+ if (sequence->keyframeTimes[start] > time)
+ start = sequence->firstValid;
+ while (start != sequence->lastValid &&
+ sequence->keyframeTimes[m3gNextKeyframeIndex(sequence, start)] <= time) {
+ start = m3gNextKeyframeIndex(sequence, start);
+ }
+ sequence->probablyNext = start;
+
+ /* Calculate the interpolation factor if necessary; the quick
+ exit also avoids a division by zero in the case that we
+ have a quirky sequence with only multiple coincident
+ keyframes. */
+
+ if (time == sequence->keyframeTimes[start] || sequence->interpolation == M3G_STEP) {
+ value = m3gKeyframeAt(sequence, start);
+ for (i = 0; i < sequence->numComponents; i++)
+ sample[i] = value[i];
+ return (sequence->interpolation == M3G_STEP)
+ ? (m3gTimeDelta(sequence, start) - (time - sequence->keyframeTimes[start]))
+ : 1;
+ }
+ s = m3gDivif(time - sequence->keyframeTimes[start], m3gTimeDelta(sequence, start));
+
+ /* Pick the correct interpolation function and pass the
+ segment start and end keyframe indices. */
+
+ end = m3gNextKeyframeIndex(sequence, start);
+
+ switch (sequence->interpolation) {
+ case M3G_LINEAR:
+ m3gLerpSample(sequence, sample, s, start, end);
+ break;
+ case M3G_SLERP:
+ m3gSlerpSample(sequence, sample, s, start, end);
+ break;
+ case M3G_SPLINE:
+ m3gSplineSample(sequence, sample, s, start, end);
+ break;
+ case M3G_SQUAD:
+ m3gSquadSample(sequence, sample, s, start, end);
+ break;
+ default:
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_ENUM);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------
+ * Virtual function table
+ *--------------------------------------------------------------------*/
+
+static const ObjectVFTable m3gvf_KeyframeSequence = {
+ m3gObjectApplyAnimation,
+ m3gObjectIsCompatible,
+ m3gObjectUpdateProperty,
+ m3gObjectDoGetReferences,
+ m3gObjectFindID,
+ m3gKeyframeSequenceDuplicate,
+ m3gDestroyKeyframeSequence
+};
+
+
+/*----------------------------------------------------------------------
+ * Public API functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates a new KeyframeSequence with default values
+ *
+ * \param hInterface M3G interface
+ * \param numKeyframes number of keyframes
+ * \param numComponents number of components
+ * \param interpolation interpolation type
+ * \retval KeyframeSequence new KeyframeSequence object
+ * \retval NULL KeyframeSequence creating failed
+ */
+/*@access M3GInterface@*/
+/*@access M3Gappearance@*/
+M3G_API M3GKeyframeSequence m3gCreateKeyframeSequence(M3GInterface hInterface,
+ M3Gint numKeyframes,
+ M3Gint numComponents,
+ M3Gint interpolation)
+{
+ Interface *m3g = (Interface *) hInterface;
+ M3G_VALIDATE_INTERFACE(m3g);
+
+ if (numKeyframes < 1 || numComponents < 1
+ || interpolation < M3G_LINEAR || interpolation > M3G_STEP
+ || ((interpolation == M3G_SLERP || interpolation == M3G_SQUAD)
+ && numComponents != 4)) {
+ m3gRaiseError(m3g, M3G_INVALID_VALUE);
+ return NULL;
+ }
+
+ {
+ KeyframeSequence *sequence = m3gAllocZ(m3g, sizeof(KeyframeSequence));
+
+ if (sequence != NULL) {
+ if (m3gInitKeyframeSequence(m3g,
+ sequence,
+ numKeyframes, numComponents,
+ interpolation) == NULL) {
+ m3gFree(m3g, sequence);
+ return NULL;
+ }
+ }
+
+ return (M3GKeyframeSequence) sequence;
+ }
+}
+
+/*!
+ * \brief Assigns a time and value to a keyframe sequence entry
+ *
+ * \param handle handle of the keyframe sequence object
+ * \param ind index of the entry to set
+ * \param time time to set in the entry
+ * \param valueSize number of elements in the value; this must match
+ * the number of elements given when constructing
+ * the sequence
+ * \param value pointer to an array of \c valueSize floats
+ */
+M3G_API void m3gSetKeyframe(M3GKeyframeSequence handle,
+ M3Gint ind,
+ M3Gint time,
+ M3Gint valueSize, const M3Gfloat *value)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+
+ /* Check for invalid inputs */
+
+ if (value == NULL) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_NULL_POINTER);
+ return;
+ }
+ if (valueSize < sequence->numComponents || time < 0) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
+ return;
+ }
+ if (ind < 0 || ind >= sequence->numKeyframes) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
+ return;
+ }
+
+ /* Assign the time and value. Quaternion keyframes are also
+ * normalized, as indicated in the specification. */
+ {
+ M3Gfloat *kf = (M3Gfloat *) m3gKeyframeAt(sequence, ind);
+ int c;
+
+ sequence->keyframeTimes[ind] = time;
+
+ for (c = 0; c < sequence->numComponents; ++c) {
+ kf[c] = value[c];
+ }
+
+ if (sequence->interpolation == M3G_SLERP
+ || sequence->interpolation == M3G_SQUAD) {
+ m3gNormalizeQuat((Quat*) kf);
+ }
+ }
+
+ sequence->dirty = M3G_TRUE;
+}
+
+/*!
+ * \brief Set valid range
+ *
+ * \param handle handle of the keyframe sequence object
+ * \param first first valid keyframe
+ * \param last last valid keyframe
+ */
+M3G_API void m3gSetValidRange(M3GKeyframeSequence handle,
+ M3Gint first, M3Gint last)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+
+ if (first < 0 || first >= sequence->numKeyframes ||
+ last < 0 || last >= sequence->numKeyframes) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
+ return;
+ }
+
+ sequence->firstValid = first;
+ sequence->lastValid = last;
+ sequence->dirty = M3G_TRUE;
+}
+
+/*!
+ * \brief Set duration
+ *
+ * \param handle handle of the keyframe sequence object
+ * \param duration duration
+ */
+M3G_API void m3gSetDuration(M3GKeyframeSequence handle, M3Gint duration)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+
+ /* Check for errors */
+ if (duration <= 0) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
+ return;
+ }
+
+ sequence->duration = duration;
+ sequence->dirty = M3G_TRUE;
+}
+
+/*!
+ * \brief Get duration
+ *
+ * \param handle handle of the keyframe sequence object
+ * \return duration
+ */
+M3G_API M3Gint m3gGetDuration(M3GKeyframeSequence handle)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+ return sequence->duration;
+}
+
+/*!
+ * \brief Get component count
+ *
+ * \param handle handle of the keyframe sequence object
+ * \return component count
+ */
+M3G_API M3Gint m3gGetComponentCount(M3GKeyframeSequence handle)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+ return sequence->numComponents;
+}
+
+/*!
+ * \brief Get interpolation type
+ *
+ * \param handle handle of the keyframe sequence object
+ * \return interpolation type
+ */
+M3G_API M3Gint m3gGetInterpolationType(M3GKeyframeSequence handle)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+ return sequence->interpolation;
+}
+
+/*!
+ * \brief Get keyframe value
+ *
+ * \param handle handle of the keyframe sequence object
+ * \param frameIndex keyframe index
+ * \param value value array
+
+ * \return time value of the keyframe
+ */
+M3G_API M3Gint m3gGetKeyframe (M3GKeyframeSequence handle, M3Gint frameIndex, M3Gfloat *value)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+
+ if (frameIndex < 0 || frameIndex >= sequence->numKeyframes) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
+ return 0;
+ }
+
+ if (value != NULL) {
+ m3gCopy( value,
+ sequence->keyframes + frameIndex * sequence->numComponents,
+ sequence->numComponents * sizeof(M3Gfloat));
+ }
+
+ return sequence->keyframeTimes[frameIndex];
+}
+
+/*!
+ * \brief Get keyframe count
+ *
+ * \param handle handle of the keyframe sequence object
+ * \return keyframe count
+ */
+M3G_API M3Gint m3gGetKeyframeCount(M3GKeyframeSequence handle)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+ return sequence->numKeyframes;
+}
+
+/*!
+ * \brief Get valid range
+ *
+ * \param handle handle of the keyframe sequence object
+ * \param first pointer to valid range start
+ * \param last pointer to valid range end
+ */
+M3G_API void m3gGetValidRange(M3GKeyframeSequence handle, M3Gint *first, M3Gint *last)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+ *first = sequence->firstValid;
+ *last = sequence->lastValid;
+}
+
+/*!
+ * \brief Set repeat mode
+ *
+ * \param handle handle of the keyframe sequence object
+ * \param mode repeat mode
+ */
+M3G_API void m3gSetRepeatMode(M3GKeyframeSequence handle, M3Genum mode)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+ if (mode != M3G_CONSTANT && mode != M3G_LOOP) {
+ m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_ENUM);
+ return;
+ }
+ sequence->closed = (mode == M3G_LOOP) ? M3G_TRUE : M3G_FALSE;
+}
+
+/*!
+ * \brief Get repeat mode
+ *
+ * \param handle handle of the keyframe sequence object
+ * \return repeat mode
+ */
+M3G_API M3Genum m3gGetRepeatMode(M3GKeyframeSequence handle)
+{
+ KeyframeSequence *sequence = (KeyframeSequence *)handle;
+ M3G_VALIDATE_OBJECT(sequence);
+ return (sequence->closed ? M3G_LOOP : M3G_CONSTANT);
+}
+