First public contribution.
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
4 * This component and the accompanying materials are made available
5 * under the terms of the License "Eclipse Public License v1.0"
6 * which accompanies this distribution, and is available
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
14 * Description: KeyframeSequence implementation
22 * \brief KeyframeSequence implementation
25 #ifndef M3G_CORE_INCLUDE
26 # error included by m3g_core.c; do not compile separately.
29 #include "m3g_keyframesequence.h"
30 #include "m3g_memory.h"
32 /*----------------------------------------------------------------------
34 *--------------------------------------------------------------------*/
38 * \brief Destroys this KeyframeSequence object.
40 * \param obj KeyframeSequence object
42 static void m3gDestroyKeyframeSequence(Object *obj)
44 KeyframeSequence *sequence = (KeyframeSequence *) obj;
45 M3G_VALIDATE_OBJECT(sequence);
47 Interface *m3g = M3G_INTERFACE(sequence);
48 m3gFree(m3g, sequence->keyframes);
49 m3gFree(m3g, sequence->keyframeTimes);
50 m3gFree(m3g, sequence->inTangents);
51 m3gFree(m3g, sequence->outTangents);
52 m3gFree(m3g, sequence->a);
53 m3gFree(m3g, sequence->b);
55 m3gDestroyObject(&sequence->object);
60 * \brief Overloaded Object3D method.
62 * \param originalObj original KeyframeSequence object
63 * \param cloneObj pointer to cloned KeyframeSequence object
64 * \param pairs array for all object-duplicate pairs
65 * \param numPairs number of pairs
67 static M3Gbool m3gKeyframeSequenceDuplicate(const Object *originalObj,
72 KeyframeSequence *original = (KeyframeSequence *)originalObj;
73 KeyframeSequence *clone =
74 (KeyframeSequence *)m3gCreateKeyframeSequence(originalObj->interface,
75 original->numKeyframes,
76 original->numComponents,
77 original->interpolation);
78 *cloneObj = (Object *)clone;
79 if (*cloneObj == NULL) {
83 if(m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
84 M3Gsizei n = original->numKeyframes * original->numComponents;
86 m3gCopy(clone->keyframes, original->keyframes, n * sizeof(M3Gfloat));
87 m3gCopy(clone->keyframeTimes, original->keyframeTimes, original->numKeyframes * sizeof(M3Gint));
88 if (original->dirty == M3G_FALSE) {
89 if (original->inTangents) {
90 m3gCopy(clone->inTangents, original->inTangents, n * sizeof(M3Gfloat));
91 m3gCopy(clone->outTangents, original->outTangents, n * sizeof(M3Gfloat));
94 m3gCopy(clone->a, original->a, original->numKeyframes * sizeof(Quat));
95 m3gCopy(clone->b, original->b, original->numKeyframes * sizeof(Quat));
99 clone->dirty = M3G_TRUE;
102 clone->duration = original->duration;
103 clone->closed = original->closed;
104 clone->firstValid = original->firstValid;
105 clone->lastValid = original->lastValid;
115 * \brief Initializes a KeyframeSequence object. See specification
116 * for default values.
118 * \param m3g M3G interface
119 * \param sequence KeyframeSequence object
120 * \param numKeyframes number of keyframes
121 * \param numComponents number of components
122 * \param interpolation interpolation type
123 * \retval KeyframeSequence initialized KeyframeSequence object
124 * \retval NULL initialization failed
126 static KeyframeSequence *m3gInitKeyframeSequence(Interface *m3g,
127 KeyframeSequence *sequence,
129 M3Gint numComponents,
130 M3Gint interpolation)
132 m3gInitObject(&sequence->object, m3g, M3G_CLASS_KEYFRAME_SEQUENCE);
134 /* Set keyframe parameters */
136 sequence->numKeyframes = numKeyframes;
137 sequence->numComponents = numComponents;
138 sequence->interpolation = interpolation;
139 sequence->lastValid = numKeyframes - 1; /* firstValid defaults to 0 */
141 /* Allocate keyframe and tangent data */
143 M3Gsizei n = numKeyframes * numComponents;
145 sequence->keyframes = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
146 if (sequence->keyframes == NULL) {
149 sequence->keyframeTimes = (M3Gint *)m3gAllocZ(m3g, numKeyframes * sizeof(M3Gint));
150 if (sequence->keyframeTimes == NULL) {
154 if (interpolation == M3G_SPLINE) {
155 sequence->inTangents = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
156 sequence->outTangents = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
157 if (sequence->inTangents == NULL || sequence->outTangents == NULL) {
161 else if (interpolation == M3G_SQUAD) {
162 sequence->a = (Quat *)m3gAlloc(m3g, numKeyframes * sizeof(Quat));
163 sequence->b = (Quat *)m3gAlloc(m3g, numKeyframes * sizeof(Quat));
164 if (sequence->a == NULL || sequence->b == NULL) {
169 /* Success; just make a note that the data is not valid yet */
171 sequence->dirty = M3G_TRUE;
175 /* The destructor contains exactly the code we need for this,
176 * so just call that */
178 m3gDestroyKeyframeSequence((Object*) sequence);
185 * \brief Checks the validity of keyframe timings
187 * \param sequence KeyframeSequence object
188 * \retval M3G_TRUE sequence valid
189 * \retval M3G_FALSE sequence invalid
191 static M3Gbool m3gValidSequence(const KeyframeSequence *sequence)
193 const M3Gint last = sequence->lastValid;
194 M3Gint current = sequence->firstValid;
196 while (current != last) {
197 M3Gint next = (current < sequence->numKeyframes-1) ? current + 1 : 0;
198 if (sequence->keyframeTimes[next] < sequence->keyframeTimes[current]) {
203 return (sequence->keyframeTimes[last] <= sequence->duration);
209 * \brief Get number of components
211 * \param sequence KeyframeSequence object
212 * \return number of components
214 static M3Gint m3gGetNumComponents(const KeyframeSequence *sequence)
216 return sequence->numComponents;
221 * \brief Get next keyframe index
223 * \param sequence KeyframeSequence object
224 * \param ind current index
227 static M3Gint m3gNextKeyframeIndex(const KeyframeSequence *sequence, M3Gint ind)
229 if (ind == sequence->lastValid) {
230 return sequence->firstValid;
232 else if (ind == sequence->numKeyframes - 1) {
242 * \brief Get previous keyframe index
244 * \param sequence KeyframeSequence object
245 * \param ind current index
246 * \return previous index
248 static M3Gint m3gPreviousKeyframeIndex(const KeyframeSequence *sequence, M3Gint ind)
250 if (ind == sequence->firstValid) {
251 return sequence->lastValid;
254 return (sequence->numKeyframes - 1);
263 * \brief Get keyframe at index
265 * \param seq KeyframeSequence object
266 * \param idx keyframe index
267 * \return keyframe value
269 static M3G_INLINE const M3Gfloat *m3gKeyframeAt(const KeyframeSequence *seq, M3Gint idx)
271 return seq->keyframes + idx * seq->numComponents;
276 * \brief Get keyframe at index -1
278 * \param seq KeyframeSequence object
279 * \param idx keyframe index
280 * \return keyframe value
282 static M3G_INLINE const M3Gfloat *m3gKeyframeBefore(const KeyframeSequence *seq, M3Gint idx)
284 return m3gKeyframeAt(seq, m3gPreviousKeyframeIndex(seq, idx));
289 * \brief Get keyframe at index + 1
291 * \param seq KeyframeSequence object
292 * \param idx keyframe index
293 * \return keyframe value
295 static M3G_INLINE const M3Gfloat *m3gKeyframeAfter(const KeyframeSequence *seq, M3Gint idx)
297 return m3gKeyframeAt(seq, m3gNextKeyframeIndex(seq, idx));
302 * \brief Get tangent to index
304 * \param seq KeyframeSequence object
305 * \param idx keyframe index
306 * \return tangent value
308 static M3G_INLINE const M3Gfloat *m3gTangentTo(const KeyframeSequence *seq, M3Gint idx)
310 M3G_ASSERT(seq->inTangents != NULL);
311 return seq->inTangents + idx * seq->numComponents;
316 * \brief Get tangent from index
318 * \param seq KeyframeSequence object
319 * \param idx keyframe index
320 * \return tangent value
322 static M3G_INLINE const M3Gfloat *m3gTangentFrom(const KeyframeSequence *seq, M3Gint idx)
324 M3G_ASSERT(seq->outTangents != NULL);
325 return seq->outTangents + idx * seq->numComponents;
330 * \brief Get time delta
332 * \param sequence KeyframeSequence object
333 * \param ind keyframe index
336 static M3Gint m3gTimeDelta(const KeyframeSequence *sequence, M3Gint ind)
338 if (ind == sequence->lastValid) {
341 - sequence->keyframeTimes[sequence->lastValid])
342 + sequence->keyframeTimes[sequence->firstValid];
344 return sequence->keyframeTimes[m3gNextKeyframeIndex(sequence, ind)]
345 - sequence->keyframeTimes[ind];
350 * \brief Get incoming tangent scale
352 * \param sequence KeyframeSequence object
353 * \param ind keyframe index
354 * \return tangent scale
356 static M3Gfloat m3gIncomingTangentScale(const KeyframeSequence *sequence,
359 if (!sequence->closed
360 && (ind == sequence->firstValid || ind == sequence->lastValid)) {
364 M3Gint prevind = m3gPreviousKeyframeIndex(sequence, ind);
365 return m3gDiv(m3gMul(2.0f, (M3Gfloat) m3gTimeDelta(sequence, prevind)),
366 (M3Gfloat)(m3gTimeDelta(sequence, ind)
367 + m3gTimeDelta(sequence, prevind)));
373 * \brief Get outgoing tangent scale
375 * \param sequence KeyframeSequence object
376 * \param ind keyframe index
377 * \return tangent scale
379 static M3Gfloat m3gOutgoingTangentScale(const KeyframeSequence *sequence,
382 if (!sequence->closed
383 && (ind == sequence->firstValid || ind == sequence->lastValid)) {
387 M3Gint prevind = m3gPreviousKeyframeIndex(sequence, ind);
388 return m3gDiv(m3gMul(2.0f, (M3Gfloat) m3gTimeDelta(sequence, ind)),
389 (M3Gfloat)(m3gTimeDelta(sequence, ind)
390 + m3gTimeDelta(sequence, prevind)));
396 * \brief Precalculate all tangents
398 * \param sequence KeyframeSequence object
400 static void m3gPrecalculateTangents(KeyframeSequence *sequence)
402 M3Gint i, kf = sequence->firstValid;
404 const M3Gfloat *prev = m3gKeyframeBefore(sequence, kf);
405 const M3Gfloat *next = m3gKeyframeAfter(sequence, kf);
406 const M3Gfloat sIn = m3gIncomingTangentScale(sequence, kf);
407 const M3Gfloat sOut = m3gOutgoingTangentScale(sequence, kf);
408 M3Gfloat *in = (M3Gfloat *) m3gTangentTo(sequence, kf);
409 M3Gfloat *out = (M3Gfloat *) m3gTangentFrom(sequence, kf);
411 for (i = 0; i < sequence->numComponents; ++i) {
412 in[i] = m3gMul(m3gMul(0.5f, (m3gSub(next[i], prev[i]))), sIn);
413 out[i] = m3gMul(m3gMul(0.5f, (m3gSub(next[i], prev[i]))), sOut);
416 kf = m3gNextKeyframeIndex(sequence, kf);
417 } while (kf != sequence->firstValid);
422 * \brief Precalculate A and B
424 * \param sequence KeyframeSequence object
426 static void m3gPrecalculateAB(KeyframeSequence *sequence)
428 Quat start, end, prev, next;
430 M3Gfloat temp[4]; /* used for both quats and vectors */
432 M3Gint kf = sequence->firstValid;
435 m3gSetQuat(&prev, m3gKeyframeBefore(sequence, kf));
436 m3gSetQuat(&start, m3gKeyframeAt(sequence, kf));
437 m3gSetQuat(&end, m3gKeyframeAfter(sequence, kf));
438 m3gSetQuat(&next, m3gKeyframeAfter(sequence, m3gNextKeyframeIndex(sequence, kf)));
440 /* Compute the centered finite difference at this
441 keyframe; note that this would be the tangent for basic
442 Catmull-Rom interpolation. */
444 m3gLogDiffQuat(&cfd, &start, &end);
445 m3gLogDiffQuat((Vec3*)temp, &prev, &start);
446 m3gAddVec3(&cfd, (Vec3*)temp);
447 m3gScaleVec3(&cfd, 0.5f);
449 /* Compute the outgoing tangent, scaled to compensate for
450 keyframe timing, then compute the "A" intermediate
454 m3gScaleVec3(&tangent, m3gOutgoingTangentScale(sequence, kf));
456 m3gLogDiffQuat((Vec3*)temp, &start, &end);
457 m3gSubVec3(&tangent, (Vec3*)temp);
458 m3gScaleVec3(&tangent, 0.5f);
459 m3gExpQuat((Quat*)temp, &tangent);
460 sequence->a[kf] = start;
461 m3gMulQuat(&(sequence->a[kf]), (Quat*)temp);
463 /* Then repeat the same steps for the incoming tangent and
464 the "B" intermediate quaternion. */
467 m3gScaleVec3(&tangent, m3gIncomingTangentScale(sequence, kf));
469 m3gLogDiffQuat((Vec3*)temp, &prev, &start);
470 m3gSubVec3((Vec3*)temp, &tangent);
471 m3gScaleVec3((Vec3*)temp, 0.5f);
472 m3gExpQuat((Quat*)temp, (Vec3*)temp);
473 sequence->b[kf] = start;
474 m3gMulQuat(&(sequence->b[kf]), (Quat*)temp);
476 kf = m3gNextKeyframeIndex(sequence, kf);
477 } while (kf != sequence->firstValid);
482 * \brief Update all tangents
484 * \param sequence KeyframeSequence object
486 static void m3gUpdateTangents(KeyframeSequence *sequence)
488 if (sequence->interpolation == M3G_SPLINE) {
489 m3gPrecalculateTangents(sequence);
491 else if (sequence->interpolation == M3G_SQUAD) {
492 m3gPrecalculateAB(sequence);
498 * \brief Linear interpolate
500 * \param sequence KeyframeSequence object
501 * \param sample input samples
503 * \param startIndex start index
504 * \param endIndex end index
506 static M3G_INLINE void m3gLerpSample(const KeyframeSequence *sequence,
509 M3Gint startIndex, M3Gint endIndex)
511 const M3Gfloat *start = m3gKeyframeAt(sequence, startIndex);
512 const M3Gfloat *end = m3gKeyframeAt(sequence, endIndex);
514 m3gLerp(sequence->numComponents, sample, s, start, end);
519 * \brief Spline interpolate
521 * \param sequence KeyframeSequence object
522 * \param sample input samples
524 * \param startIndex start index
525 * \param endIndex end index
527 static M3G_INLINE void m3gSplineSample(const KeyframeSequence *sequence,
530 M3Gint startIndex, M3Gint endIndex)
532 const M3Gfloat *start, *end;
533 const M3Gfloat *tStart, *tEnd;
535 /* Get the required keyframe values and the (one-sided) tangents
536 * at the ends of the segment. */
538 start = m3gKeyframeAt(sequence, startIndex);
539 end = m3gKeyframeAt(sequence, endIndex);
541 tStart = m3gTangentFrom(sequence, startIndex);
542 tEnd = m3gTangentTo(sequence, endIndex);
544 /* Interpolate the final value using a Hermite spline. */
546 m3gHermite(sequence->numComponents, sample, s, start, end, tStart, tEnd);
551 * \brief Spherical linear interpolate
553 * \param sequence KeyframeSequence object
554 * \param sample input samples
556 * \param startIndex start index
557 * \param endIndex end index
559 static M3G_INLINE void m3gSlerpSample(const KeyframeSequence *sequence,
562 M3Gint startIndex, M3Gint endIndex)
564 if (sequence->numComponents != 4) {
565 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
569 m3gSlerpQuat((Quat *) sample,
571 (const Quat *) m3gKeyframeAt(sequence, startIndex),
572 (const Quat *) m3gKeyframeAt(sequence, endIndex));
577 * \brief Spline interpolate quats
579 * \param sequence KeyframeSequence object
580 * \param sample input samples
582 * \param startIndex start index
583 * \param endIndex end index
585 static M3G_INLINE void m3gSquadSample(const KeyframeSequence *sequence,
588 M3Gint startIndex, M3Gint endIndex)
590 if (sequence->numComponents != 4) {
591 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
595 m3gSquadQuat((Quat *) sample,
597 (const Quat *) m3gKeyframeAt(sequence, startIndex),
598 &(sequence->a[startIndex]),
599 &(sequence->b[endIndex]),
600 (const Quat *) m3gKeyframeAt(sequence, endIndex));
607 * \param sequence KeyframeSequence object
609 * \param sample pointer to sample
610 * \return sample validity
612 static M3Gint m3gGetSample(KeyframeSequence *sequence,
616 M3Gint start, end, i;
617 const M3Gfloat *value;
620 M3G_VALIDATE_OBJECT(sequence);
622 if (sequence->dirty == M3G_TRUE) {
623 if (!m3gValidSequence(sequence)) {
624 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_OPERATION);
627 m3gUpdateTangents(sequence);
628 sequence->dirty = M3G_FALSE;
629 sequence->probablyNext = sequence->firstValid;
632 /* First, map the time to the valid range of a repeating
633 sequence, or handle the special end cases of an open-ended
636 if (sequence->closed) {
638 time = (time % sequence->duration) + sequence->duration;
640 time = time % sequence->duration;
641 if (time < sequence->keyframeTimes[sequence->firstValid]) {
642 time += sequence->duration;
646 if (time < sequence->keyframeTimes[sequence->firstValid]) {
647 value = m3gKeyframeAt(sequence, sequence->firstValid);
648 for (i = 0; i < sequence->numComponents; i++)
649 sample[i] = value[i];
650 return (sequence->keyframeTimes[sequence->firstValid] - time);
652 else if (time >= sequence->keyframeTimes[sequence->lastValid]) {
653 value = m3gKeyframeAt(sequence, sequence->lastValid);
654 for (i = 0; i < sequence->numComponents; i++)
655 sample[i] = value[i];
656 /* \ define a meaningful constant */
661 /* Search for the starting keyframe of the segment to
662 interpolate. Starting the search from the previously
663 used keyframe, we are very likely to find the match
664 sooner than if we'd start from the first keyframe. */
666 start = sequence->probablyNext;
667 if (sequence->keyframeTimes[start] > time)
668 start = sequence->firstValid;
669 while (start != sequence->lastValid &&
670 sequence->keyframeTimes[m3gNextKeyframeIndex(sequence, start)] <= time) {
671 start = m3gNextKeyframeIndex(sequence, start);
673 sequence->probablyNext = start;
675 /* Calculate the interpolation factor if necessary; the quick
676 exit also avoids a division by zero in the case that we
677 have a quirky sequence with only multiple coincident
680 if (time == sequence->keyframeTimes[start] || sequence->interpolation == M3G_STEP) {
681 value = m3gKeyframeAt(sequence, start);
682 for (i = 0; i < sequence->numComponents; i++)
683 sample[i] = value[i];
684 return (sequence->interpolation == M3G_STEP)
685 ? (m3gTimeDelta(sequence, start) - (time - sequence->keyframeTimes[start]))
688 s = m3gDivif(time - sequence->keyframeTimes[start], m3gTimeDelta(sequence, start));
690 /* Pick the correct interpolation function and pass the
691 segment start and end keyframe indices. */
693 end = m3gNextKeyframeIndex(sequence, start);
695 switch (sequence->interpolation) {
697 m3gLerpSample(sequence, sample, s, start, end);
700 m3gSlerpSample(sequence, sample, s, start, end);
703 m3gSplineSample(sequence, sample, s, start, end);
706 m3gSquadSample(sequence, sample, s, start, end);
709 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_ENUM);
716 /*----------------------------------------------------------------------
717 * Virtual function table
718 *--------------------------------------------------------------------*/
720 static const ObjectVFTable m3gvf_KeyframeSequence = {
721 m3gObjectApplyAnimation,
722 m3gObjectIsCompatible,
723 m3gObjectUpdateProperty,
724 m3gObjectDoGetReferences,
726 m3gKeyframeSequenceDuplicate,
727 m3gDestroyKeyframeSequence
731 /*----------------------------------------------------------------------
732 * Public API functions
733 *--------------------------------------------------------------------*/
736 * \brief Creates a new KeyframeSequence with default values
738 * \param hInterface M3G interface
739 * \param numKeyframes number of keyframes
740 * \param numComponents number of components
741 * \param interpolation interpolation type
742 * \retval KeyframeSequence new KeyframeSequence object
743 * \retval NULL KeyframeSequence creating failed
745 /*@access M3GInterface@*/
746 /*@access M3Gappearance@*/
747 M3G_API M3GKeyframeSequence m3gCreateKeyframeSequence(M3GInterface hInterface,
749 M3Gint numComponents,
750 M3Gint interpolation)
752 Interface *m3g = (Interface *) hInterface;
753 M3G_VALIDATE_INTERFACE(m3g);
755 if (numKeyframes < 1 || numComponents < 1
756 || interpolation < M3G_LINEAR || interpolation > M3G_STEP
757 || ((interpolation == M3G_SLERP || interpolation == M3G_SQUAD)
758 && numComponents != 4)) {
759 m3gRaiseError(m3g, M3G_INVALID_VALUE);
764 KeyframeSequence *sequence = m3gAllocZ(m3g, sizeof(KeyframeSequence));
766 if (sequence != NULL) {
767 if (m3gInitKeyframeSequence(m3g,
769 numKeyframes, numComponents,
770 interpolation) == NULL) {
771 m3gFree(m3g, sequence);
776 return (M3GKeyframeSequence) sequence;
781 * \brief Assigns a time and value to a keyframe sequence entry
783 * \param handle handle of the keyframe sequence object
784 * \param ind index of the entry to set
785 * \param time time to set in the entry
786 * \param valueSize number of elements in the value; this must match
787 * the number of elements given when constructing
789 * \param value pointer to an array of \c valueSize floats
791 M3G_API void m3gSetKeyframe(M3GKeyframeSequence handle,
794 M3Gint valueSize, const M3Gfloat *value)
796 KeyframeSequence *sequence = (KeyframeSequence *)handle;
797 M3G_VALIDATE_OBJECT(sequence);
799 /* Check for invalid inputs */
802 m3gRaiseError(M3G_INTERFACE(sequence), M3G_NULL_POINTER);
805 if (valueSize < sequence->numComponents || time < 0) {
806 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
809 if (ind < 0 || ind >= sequence->numKeyframes) {
810 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
814 /* Assign the time and value. Quaternion keyframes are also
815 * normalized, as indicated in the specification. */
817 M3Gfloat *kf = (M3Gfloat *) m3gKeyframeAt(sequence, ind);
820 sequence->keyframeTimes[ind] = time;
822 for (c = 0; c < sequence->numComponents; ++c) {
826 if (sequence->interpolation == M3G_SLERP
827 || sequence->interpolation == M3G_SQUAD) {
828 m3gNormalizeQuat((Quat*) kf);
832 sequence->dirty = M3G_TRUE;
836 * \brief Set valid range
838 * \param handle handle of the keyframe sequence object
839 * \param first first valid keyframe
840 * \param last last valid keyframe
842 M3G_API void m3gSetValidRange(M3GKeyframeSequence handle,
843 M3Gint first, M3Gint last)
845 KeyframeSequence *sequence = (KeyframeSequence *)handle;
846 M3G_VALIDATE_OBJECT(sequence);
848 if (first < 0 || first >= sequence->numKeyframes ||
849 last < 0 || last >= sequence->numKeyframes) {
850 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
854 sequence->firstValid = first;
855 sequence->lastValid = last;
856 sequence->dirty = M3G_TRUE;
860 * \brief Set duration
862 * \param handle handle of the keyframe sequence object
863 * \param duration duration
865 M3G_API void m3gSetDuration(M3GKeyframeSequence handle, M3Gint duration)
867 KeyframeSequence *sequence = (KeyframeSequence *)handle;
868 M3G_VALIDATE_OBJECT(sequence);
870 /* Check for errors */
872 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
876 sequence->duration = duration;
877 sequence->dirty = M3G_TRUE;
881 * \brief Get duration
883 * \param handle handle of the keyframe sequence object
886 M3G_API M3Gint m3gGetDuration(M3GKeyframeSequence handle)
888 KeyframeSequence *sequence = (KeyframeSequence *)handle;
889 M3G_VALIDATE_OBJECT(sequence);
890 return sequence->duration;
894 * \brief Get component count
896 * \param handle handle of the keyframe sequence object
897 * \return component count
899 M3G_API M3Gint m3gGetComponentCount(M3GKeyframeSequence handle)
901 KeyframeSequence *sequence = (KeyframeSequence *)handle;
902 M3G_VALIDATE_OBJECT(sequence);
903 return sequence->numComponents;
907 * \brief Get interpolation type
909 * \param handle handle of the keyframe sequence object
910 * \return interpolation type
912 M3G_API M3Gint m3gGetInterpolationType(M3GKeyframeSequence handle)
914 KeyframeSequence *sequence = (KeyframeSequence *)handle;
915 M3G_VALIDATE_OBJECT(sequence);
916 return sequence->interpolation;
920 * \brief Get keyframe value
922 * \param handle handle of the keyframe sequence object
923 * \param frameIndex keyframe index
924 * \param value value array
926 * \return time value of the keyframe
928 M3G_API M3Gint m3gGetKeyframe (M3GKeyframeSequence handle, M3Gint frameIndex, M3Gfloat *value)
930 KeyframeSequence *sequence = (KeyframeSequence *)handle;
931 M3G_VALIDATE_OBJECT(sequence);
933 if (frameIndex < 0 || frameIndex >= sequence->numKeyframes) {
934 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
940 sequence->keyframes + frameIndex * sequence->numComponents,
941 sequence->numComponents * sizeof(M3Gfloat));
944 return sequence->keyframeTimes[frameIndex];
948 * \brief Get keyframe count
950 * \param handle handle of the keyframe sequence object
951 * \return keyframe count
953 M3G_API M3Gint m3gGetKeyframeCount(M3GKeyframeSequence handle)
955 KeyframeSequence *sequence = (KeyframeSequence *)handle;
956 M3G_VALIDATE_OBJECT(sequence);
957 return sequence->numKeyframes;
961 * \brief Get valid range
963 * \param handle handle of the keyframe sequence object
964 * \param first pointer to valid range start
965 * \param last pointer to valid range end
967 M3G_API void m3gGetValidRange(M3GKeyframeSequence handle, M3Gint *first, M3Gint *last)
969 KeyframeSequence *sequence = (KeyframeSequence *)handle;
970 M3G_VALIDATE_OBJECT(sequence);
971 *first = sequence->firstValid;
972 *last = sequence->lastValid;
976 * \brief Set repeat mode
978 * \param handle handle of the keyframe sequence object
979 * \param mode repeat mode
981 M3G_API void m3gSetRepeatMode(M3GKeyframeSequence handle, M3Genum mode)
983 KeyframeSequence *sequence = (KeyframeSequence *)handle;
984 M3G_VALIDATE_OBJECT(sequence);
985 if (mode != M3G_CONSTANT && mode != M3G_LOOP) {
986 m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_ENUM);
989 sequence->closed = (mode == M3G_LOOP) ? M3G_TRUE : M3G_FALSE;
993 * \brief Get repeat mode
995 * \param handle handle of the keyframe sequence object
996 * \return repeat mode
998 M3G_API M3Genum m3gGetRepeatMode(M3GKeyframeSequence handle)
1000 KeyframeSequence *sequence = (KeyframeSequence *)handle;
1001 M3G_VALIDATE_OBJECT(sequence);
1002 return (sequence->closed ? M3G_LOOP : M3G_CONSTANT);