os/graphics/m3g/m3gcore11/src/m3g_keyframesequence.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
/*
sl@0
     2
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     3
* All rights reserved.
sl@0
     4
* This component and the accompanying materials are made available
sl@0
     5
* under the terms of the License "Eclipse Public License v1.0"
sl@0
     6
* which accompanies this distribution, and is available
sl@0
     7
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     8
*
sl@0
     9
* Initial Contributors:
sl@0
    10
* Nokia Corporation - initial contribution.
sl@0
    11
*
sl@0
    12
* Contributors:
sl@0
    13
*
sl@0
    14
* Description: KeyframeSequence implementation
sl@0
    15
*
sl@0
    16
*/
sl@0
    17
sl@0
    18
sl@0
    19
/*!
sl@0
    20
 * \internal
sl@0
    21
 * \file
sl@0
    22
 * \brief KeyframeSequence implementation
sl@0
    23
 */
sl@0
    24
sl@0
    25
#ifndef M3G_CORE_INCLUDE
sl@0
    26
#   error included by m3g_core.c; do not compile separately.
sl@0
    27
#endif
sl@0
    28
sl@0
    29
#include "m3g_keyframesequence.h"
sl@0
    30
#include "m3g_memory.h"
sl@0
    31
sl@0
    32
/*----------------------------------------------------------------------
sl@0
    33
 * Internal functions
sl@0
    34
 *--------------------------------------------------------------------*/
sl@0
    35
sl@0
    36
/*!
sl@0
    37
 * \internal
sl@0
    38
 * \brief Destroys this KeyframeSequence object.
sl@0
    39
 *
sl@0
    40
 * \param obj KeyframeSequence object
sl@0
    41
 */
sl@0
    42
static void m3gDestroyKeyframeSequence(Object *obj)
sl@0
    43
{
sl@0
    44
    KeyframeSequence *sequence = (KeyframeSequence *) obj;
sl@0
    45
    M3G_VALIDATE_OBJECT(sequence);
sl@0
    46
    {
sl@0
    47
        Interface *m3g = M3G_INTERFACE(sequence);
sl@0
    48
        m3gFree(m3g, sequence->keyframes);
sl@0
    49
        m3gFree(m3g, sequence->keyframeTimes);
sl@0
    50
        m3gFree(m3g, sequence->inTangents);
sl@0
    51
        m3gFree(m3g, sequence->outTangents);
sl@0
    52
        m3gFree(m3g, sequence->a);
sl@0
    53
        m3gFree(m3g, sequence->b);
sl@0
    54
    }
sl@0
    55
    m3gDestroyObject(&sequence->object);
sl@0
    56
}
sl@0
    57
sl@0
    58
/*!
sl@0
    59
 * \internal
sl@0
    60
 * \brief Overloaded Object3D method.
sl@0
    61
 *
sl@0
    62
 * \param originalObj original KeyframeSequence object
sl@0
    63
 * \param cloneObj pointer to cloned KeyframeSequence object
sl@0
    64
 * \param pairs array for all object-duplicate pairs
sl@0
    65
 * \param numPairs number of pairs
sl@0
    66
 */
sl@0
    67
static M3Gbool m3gKeyframeSequenceDuplicate(const Object *originalObj,
sl@0
    68
                                            Object **cloneObj,
sl@0
    69
                                            Object **pairs,
sl@0
    70
                                            M3Gint *numPairs)
sl@0
    71
{
sl@0
    72
    KeyframeSequence *original = (KeyframeSequence *)originalObj;
sl@0
    73
    KeyframeSequence *clone =
sl@0
    74
        (KeyframeSequence *)m3gCreateKeyframeSequence(originalObj->interface,
sl@0
    75
                                                      original->numKeyframes,
sl@0
    76
                                                      original->numComponents,
sl@0
    77
                                                      original->interpolation);
sl@0
    78
    *cloneObj = (Object *)clone;
sl@0
    79
    if (*cloneObj == NULL) {
sl@0
    80
        return M3G_FALSE;
sl@0
    81
    }
sl@0
    82
sl@0
    83
    if(m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
sl@0
    84
        M3Gsizei n = original->numKeyframes * original->numComponents;
sl@0
    85
        
sl@0
    86
        m3gCopy(clone->keyframes, original->keyframes, n * sizeof(M3Gfloat));
sl@0
    87
        m3gCopy(clone->keyframeTimes, original->keyframeTimes, original->numKeyframes * sizeof(M3Gint));
sl@0
    88
        if (original->dirty == M3G_FALSE) {
sl@0
    89
            if (original->inTangents) {
sl@0
    90
                m3gCopy(clone->inTangents, original->inTangents, n * sizeof(M3Gfloat));
sl@0
    91
                m3gCopy(clone->outTangents, original->outTangents, n * sizeof(M3Gfloat));
sl@0
    92
            }
sl@0
    93
            if (original->a) {
sl@0
    94
                m3gCopy(clone->a, original->a, original->numKeyframes * sizeof(Quat));
sl@0
    95
                m3gCopy(clone->b, original->b, original->numKeyframes * sizeof(Quat));
sl@0
    96
            }
sl@0
    97
        }
sl@0
    98
        else {
sl@0
    99
            clone->dirty = M3G_TRUE;
sl@0
   100
        }
sl@0
   101
sl@0
   102
        clone->duration = original->duration;
sl@0
   103
        clone->closed = original->closed;
sl@0
   104
        clone->firstValid = original->firstValid;
sl@0
   105
        clone->lastValid = original->lastValid;
sl@0
   106
        return M3G_TRUE;
sl@0
   107
    }
sl@0
   108
    else {
sl@0
   109
        return M3G_FALSE;
sl@0
   110
    }
sl@0
   111
}
sl@0
   112
sl@0
   113
/*!
sl@0
   114
 * \internal
sl@0
   115
 * \brief Initializes a KeyframeSequence object. See specification
sl@0
   116
 * for default values.
sl@0
   117
 *
sl@0
   118
 * \param m3g                   M3G interface
sl@0
   119
 * \param sequence              KeyframeSequence object
sl@0
   120
 * \param numKeyframes          number of keyframes
sl@0
   121
 * \param numComponents         number of components
sl@0
   122
 * \param interpolation         interpolation type
sl@0
   123
 * \retval                      KeyframeSequence initialized KeyframeSequence object
sl@0
   124
 * \retval                      NULL initialization failed
sl@0
   125
 */
sl@0
   126
static KeyframeSequence *m3gInitKeyframeSequence(Interface *m3g,
sl@0
   127
                                                 KeyframeSequence *sequence,
sl@0
   128
                                                 M3Gint numKeyframes,
sl@0
   129
                                                 M3Gint numComponents,
sl@0
   130
                                                 M3Gint interpolation)
sl@0
   131
{
sl@0
   132
    m3gInitObject(&sequence->object, m3g, M3G_CLASS_KEYFRAME_SEQUENCE);
sl@0
   133
sl@0
   134
    /* Set keyframe parameters */
sl@0
   135
    
sl@0
   136
    sequence->numKeyframes = numKeyframes;
sl@0
   137
    sequence->numComponents = numComponents;
sl@0
   138
    sequence->interpolation = interpolation;
sl@0
   139
    sequence->lastValid = numKeyframes - 1; /* firstValid defaults to 0 */
sl@0
   140
sl@0
   141
    /* Allocate keyframe and tangent data */
sl@0
   142
    {    
sl@0
   143
        M3Gsizei n = numKeyframes * numComponents;
sl@0
   144
        
sl@0
   145
        sequence->keyframes = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
sl@0
   146
        if (sequence->keyframes == NULL) {
sl@0
   147
            goto AllocFailed;
sl@0
   148
        }
sl@0
   149
        sequence->keyframeTimes = (M3Gint *)m3gAllocZ(m3g, numKeyframes * sizeof(M3Gint));
sl@0
   150
        if (sequence->keyframeTimes == NULL) {
sl@0
   151
            goto AllocFailed;
sl@0
   152
        }
sl@0
   153
        
sl@0
   154
        if (interpolation == M3G_SPLINE) {
sl@0
   155
            sequence->inTangents  = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
sl@0
   156
            sequence->outTangents = (M3Gfloat *)m3gAllocZ(m3g, n * sizeof(M3Gfloat));
sl@0
   157
            if (sequence->inTangents == NULL || sequence->outTangents == NULL) {
sl@0
   158
                goto AllocFailed;
sl@0
   159
            }
sl@0
   160
        }
sl@0
   161
        else if (interpolation == M3G_SQUAD) {
sl@0
   162
            sequence->a = (Quat *)m3gAlloc(m3g, numKeyframes * sizeof(Quat));
sl@0
   163
            sequence->b = (Quat *)m3gAlloc(m3g, numKeyframes * sizeof(Quat));
sl@0
   164
            if (sequence->a == NULL || sequence->b == NULL) {
sl@0
   165
                goto AllocFailed;
sl@0
   166
            }
sl@0
   167
        }
sl@0
   168
sl@0
   169
        /* Success; just make a note that the data is not valid yet */
sl@0
   170
        
sl@0
   171
        sequence->dirty = M3G_TRUE;
sl@0
   172
        return sequence;
sl@0
   173
sl@0
   174
AllocFailed:
sl@0
   175
        /* The destructor contains exactly the code we need for this,
sl@0
   176
         * so just call that */
sl@0
   177
        
sl@0
   178
        m3gDestroyKeyframeSequence((Object*) sequence);
sl@0
   179
        return NULL;
sl@0
   180
    }
sl@0
   181
}
sl@0
   182
sl@0
   183
/*!
sl@0
   184
 * \internal
sl@0
   185
 * \brief Checks the validity of keyframe timings
sl@0
   186
 *
sl@0
   187
 * \param sequence   KeyframeSequence object
sl@0
   188
 * \retval M3G_TRUE  sequence valid
sl@0
   189
 * \retval M3G_FALSE sequence invalid
sl@0
   190
 */
sl@0
   191
static M3Gbool m3gValidSequence(const KeyframeSequence *sequence)
sl@0
   192
{    
sl@0
   193
    const M3Gint last = sequence->lastValid;
sl@0
   194
    M3Gint current = sequence->firstValid;
sl@0
   195
    
sl@0
   196
    while (current != last) {
sl@0
   197
        M3Gint next = (current < sequence->numKeyframes-1) ? current + 1 : 0;
sl@0
   198
        if (sequence->keyframeTimes[next] < sequence->keyframeTimes[current]) {
sl@0
   199
            return M3G_FALSE;
sl@0
   200
        }
sl@0
   201
        current = next;
sl@0
   202
    }
sl@0
   203
    return (sequence->keyframeTimes[last] <= sequence->duration);
sl@0
   204
}
sl@0
   205
sl@0
   206
sl@0
   207
/*!
sl@0
   208
 * \internal
sl@0
   209
 * \brief Get number of components
sl@0
   210
 *
sl@0
   211
 * \param sequence   KeyframeSequence object
sl@0
   212
 * \return number of components
sl@0
   213
 */
sl@0
   214
static M3Gint m3gGetNumComponents(const KeyframeSequence *sequence)
sl@0
   215
{
sl@0
   216
    return sequence->numComponents;
sl@0
   217
}
sl@0
   218
sl@0
   219
/*!
sl@0
   220
 * \internal
sl@0
   221
 * \brief Get next keyframe index
sl@0
   222
 *
sl@0
   223
 * \param sequence  KeyframeSequence object
sl@0
   224
 * \param ind       current index
sl@0
   225
 * \return next index
sl@0
   226
 */
sl@0
   227
static M3Gint m3gNextKeyframeIndex(const KeyframeSequence *sequence, M3Gint ind)
sl@0
   228
{
sl@0
   229
    if (ind == sequence->lastValid) {
sl@0
   230
        return sequence->firstValid;
sl@0
   231
    }
sl@0
   232
    else if (ind == sequence->numKeyframes - 1) {
sl@0
   233
        return 0;
sl@0
   234
    }
sl@0
   235
    else {
sl@0
   236
        return (ind + 1);
sl@0
   237
    }
sl@0
   238
}
sl@0
   239
    
sl@0
   240
/*!
sl@0
   241
 * \internal
sl@0
   242
 * \brief Get previous keyframe index
sl@0
   243
 *
sl@0
   244
 * \param sequence  KeyframeSequence object
sl@0
   245
 * \param ind       current index
sl@0
   246
 * \return previous index
sl@0
   247
 */
sl@0
   248
static M3Gint m3gPreviousKeyframeIndex(const KeyframeSequence *sequence, M3Gint ind)
sl@0
   249
{
sl@0
   250
    if (ind == sequence->firstValid) {
sl@0
   251
        return sequence->lastValid;
sl@0
   252
    }
sl@0
   253
    else if (ind == 0) {
sl@0
   254
        return (sequence->numKeyframes - 1);
sl@0
   255
    }
sl@0
   256
    else {
sl@0
   257
        return (ind - 1);
sl@0
   258
    }
sl@0
   259
}
sl@0
   260
sl@0
   261
/*!
sl@0
   262
 * \internal
sl@0
   263
 * \brief Get keyframe at index
sl@0
   264
 *
sl@0
   265
 * \param seq       KeyframeSequence object
sl@0
   266
 * \param idx       keyframe index
sl@0
   267
 * \return keyframe value
sl@0
   268
 */
sl@0
   269
static M3G_INLINE const M3Gfloat *m3gKeyframeAt(const KeyframeSequence *seq, M3Gint idx)
sl@0
   270
{
sl@0
   271
    return seq->keyframes + idx * seq->numComponents;
sl@0
   272
}
sl@0
   273
sl@0
   274
/*!
sl@0
   275
 * \internal
sl@0
   276
 * \brief Get keyframe at index -1
sl@0
   277
 *
sl@0
   278
 * \param seq       KeyframeSequence object
sl@0
   279
 * \param idx       keyframe index
sl@0
   280
 * \return keyframe value
sl@0
   281
 */
sl@0
   282
static M3G_INLINE const M3Gfloat *m3gKeyframeBefore(const KeyframeSequence *seq, M3Gint idx)
sl@0
   283
{
sl@0
   284
    return m3gKeyframeAt(seq, m3gPreviousKeyframeIndex(seq, idx));
sl@0
   285
}
sl@0
   286
sl@0
   287
/*!
sl@0
   288
 * \internal
sl@0
   289
 * \brief Get keyframe at index + 1
sl@0
   290
 *
sl@0
   291
 * \param seq       KeyframeSequence object
sl@0
   292
 * \param idx       keyframe index
sl@0
   293
 * \return keyframe value
sl@0
   294
 */
sl@0
   295
static M3G_INLINE const M3Gfloat *m3gKeyframeAfter(const KeyframeSequence *seq, M3Gint idx)
sl@0
   296
{
sl@0
   297
    return m3gKeyframeAt(seq, m3gNextKeyframeIndex(seq, idx));
sl@0
   298
}
sl@0
   299
sl@0
   300
/*!
sl@0
   301
 * \internal
sl@0
   302
 * \brief Get tangent to index
sl@0
   303
 *
sl@0
   304
 * \param seq       KeyframeSequence object
sl@0
   305
 * \param idx       keyframe index
sl@0
   306
 * \return tangent value
sl@0
   307
 */
sl@0
   308
static M3G_INLINE const M3Gfloat *m3gTangentTo(const KeyframeSequence *seq, M3Gint idx)
sl@0
   309
{
sl@0
   310
    M3G_ASSERT(seq->inTangents != NULL);
sl@0
   311
    return seq->inTangents + idx * seq->numComponents;
sl@0
   312
}
sl@0
   313
sl@0
   314
/*!
sl@0
   315
 * \internal
sl@0
   316
 * \brief Get tangent from index
sl@0
   317
 *
sl@0
   318
 * \param seq       KeyframeSequence object
sl@0
   319
 * \param idx       keyframe index
sl@0
   320
 * \return tangent value
sl@0
   321
 */
sl@0
   322
static M3G_INLINE const M3Gfloat *m3gTangentFrom(const KeyframeSequence *seq, M3Gint idx)
sl@0
   323
{
sl@0
   324
    M3G_ASSERT(seq->outTangents != NULL);
sl@0
   325
    return seq->outTangents + idx * seq->numComponents;
sl@0
   326
}
sl@0
   327
sl@0
   328
/*!
sl@0
   329
 * \internal
sl@0
   330
 * \brief Get time delta
sl@0
   331
 *
sl@0
   332
 * \param sequence  KeyframeSequence object
sl@0
   333
 * \param ind       keyframe index
sl@0
   334
 * \return time delta
sl@0
   335
 */
sl@0
   336
static M3Gint m3gTimeDelta(const KeyframeSequence *sequence, M3Gint ind)
sl@0
   337
{
sl@0
   338
    if (ind == sequence->lastValid) {
sl@0
   339
        return
sl@0
   340
            (sequence->duration
sl@0
   341
             - sequence->keyframeTimes[sequence->lastValid])
sl@0
   342
            + sequence->keyframeTimes[sequence->firstValid];
sl@0
   343
    }
sl@0
   344
    return sequence->keyframeTimes[m3gNextKeyframeIndex(sequence, ind)]
sl@0
   345
        - sequence->keyframeTimes[ind];
sl@0
   346
}
sl@0
   347
sl@0
   348
/*!
sl@0
   349
 * \internal
sl@0
   350
 * \brief Get incoming tangent scale
sl@0
   351
 *
sl@0
   352
 * \param sequence  KeyframeSequence object
sl@0
   353
 * \param ind       keyframe index
sl@0
   354
 * \return tangent scale
sl@0
   355
 */
sl@0
   356
static M3Gfloat m3gIncomingTangentScale(const KeyframeSequence *sequence,
sl@0
   357
                                        M3Gint ind)
sl@0
   358
{
sl@0
   359
    if (!sequence->closed
sl@0
   360
        && (ind == sequence->firstValid || ind == sequence->lastValid)) {
sl@0
   361
        return 0;
sl@0
   362
    }
sl@0
   363
    else {
sl@0
   364
        M3Gint prevind = m3gPreviousKeyframeIndex(sequence, ind);
sl@0
   365
        return m3gDiv(m3gMul(2.0f, (M3Gfloat) m3gTimeDelta(sequence, prevind)),
sl@0
   366
                      (M3Gfloat)(m3gTimeDelta(sequence, ind)
sl@0
   367
                                 + m3gTimeDelta(sequence, prevind)));
sl@0
   368
    }
sl@0
   369
}
sl@0
   370
sl@0
   371
/*!
sl@0
   372
 * \internal
sl@0
   373
 * \brief Get outgoing tangent scale
sl@0
   374
 *
sl@0
   375
 * \param sequence  KeyframeSequence object
sl@0
   376
 * \param ind       keyframe index
sl@0
   377
 * \return tangent scale
sl@0
   378
 */
sl@0
   379
static M3Gfloat m3gOutgoingTangentScale(const KeyframeSequence *sequence,
sl@0
   380
                                        M3Gint ind)
sl@0
   381
{
sl@0
   382
    if (!sequence->closed
sl@0
   383
        && (ind == sequence->firstValid || ind == sequence->lastValid)) {
sl@0
   384
        return 0;
sl@0
   385
    }
sl@0
   386
    else {
sl@0
   387
        M3Gint prevind = m3gPreviousKeyframeIndex(sequence, ind);
sl@0
   388
        return m3gDiv(m3gMul(2.0f, (M3Gfloat) m3gTimeDelta(sequence, ind)),
sl@0
   389
                      (M3Gfloat)(m3gTimeDelta(sequence, ind)
sl@0
   390
                                 + m3gTimeDelta(sequence, prevind)));
sl@0
   391
    }
sl@0
   392
}
sl@0
   393
sl@0
   394
/*!
sl@0
   395
 * \internal
sl@0
   396
 * \brief Precalculate all tangents
sl@0
   397
 *
sl@0
   398
 * \param sequence  KeyframeSequence object
sl@0
   399
 */
sl@0
   400
static void m3gPrecalculateTangents(KeyframeSequence *sequence)
sl@0
   401
{
sl@0
   402
    M3Gint i, kf = sequence->firstValid;
sl@0
   403
    do {
sl@0
   404
        const M3Gfloat *prev = m3gKeyframeBefore(sequence, kf);
sl@0
   405
        const M3Gfloat *next = m3gKeyframeAfter(sequence, kf);
sl@0
   406
        const M3Gfloat sIn  = m3gIncomingTangentScale(sequence, kf);
sl@0
   407
        const M3Gfloat sOut = m3gOutgoingTangentScale(sequence, kf);
sl@0
   408
        M3Gfloat *in  = (M3Gfloat *) m3gTangentTo(sequence, kf);
sl@0
   409
        M3Gfloat *out = (M3Gfloat *) m3gTangentFrom(sequence, kf);        
sl@0
   410
        
sl@0
   411
        for (i = 0; i < sequence->numComponents; ++i) {
sl@0
   412
            in[i]  = m3gMul(m3gMul(0.5f, (m3gSub(next[i], prev[i]))), sIn);
sl@0
   413
            out[i] = m3gMul(m3gMul(0.5f, (m3gSub(next[i], prev[i]))), sOut);
sl@0
   414
        }
sl@0
   415
        
sl@0
   416
        kf = m3gNextKeyframeIndex(sequence, kf);
sl@0
   417
    } while (kf != sequence->firstValid);
sl@0
   418
}
sl@0
   419
sl@0
   420
/*!
sl@0
   421
 * \internal
sl@0
   422
 * \brief Precalculate A and B
sl@0
   423
 *
sl@0
   424
 * \param sequence  KeyframeSequence object
sl@0
   425
 */
sl@0
   426
static void m3gPrecalculateAB(KeyframeSequence *sequence)
sl@0
   427
{
sl@0
   428
    Quat start, end, prev, next;
sl@0
   429
    Vec3 tangent, cfd;
sl@0
   430
    M3Gfloat temp[4]; /* used for both quats and vectors */
sl@0
   431
    
sl@0
   432
    M3Gint kf = sequence->firstValid;
sl@0
   433
    do {
sl@0
   434
sl@0
   435
        m3gSetQuat(&prev, m3gKeyframeBefore(sequence, kf));
sl@0
   436
        m3gSetQuat(&start, m3gKeyframeAt(sequence, kf));
sl@0
   437
        m3gSetQuat(&end, m3gKeyframeAfter(sequence, kf));
sl@0
   438
        m3gSetQuat(&next, m3gKeyframeAfter(sequence, m3gNextKeyframeIndex(sequence, kf)));
sl@0
   439
sl@0
   440
        /* Compute the centered finite difference at this
sl@0
   441
           keyframe; note that this would be the tangent for basic
sl@0
   442
           Catmull-Rom interpolation. */
sl@0
   443
sl@0
   444
        m3gLogDiffQuat(&cfd, &start, &end);
sl@0
   445
        m3gLogDiffQuat((Vec3*)temp, &prev, &start);
sl@0
   446
        m3gAddVec3(&cfd, (Vec3*)temp);
sl@0
   447
        m3gScaleVec3(&cfd, 0.5f);
sl@0
   448
sl@0
   449
        /* Compute the outgoing tangent, scaled to compensate for
sl@0
   450
           keyframe timing, then compute the "A" intermediate
sl@0
   451
           quaternion. */
sl@0
   452
sl@0
   453
        tangent = cfd;
sl@0
   454
        m3gScaleVec3(&tangent, m3gOutgoingTangentScale(sequence, kf));
sl@0
   455
sl@0
   456
        m3gLogDiffQuat((Vec3*)temp, &start, &end);
sl@0
   457
        m3gSubVec3(&tangent, (Vec3*)temp);
sl@0
   458
        m3gScaleVec3(&tangent, 0.5f);
sl@0
   459
        m3gExpQuat((Quat*)temp, &tangent);
sl@0
   460
        sequence->a[kf] = start;
sl@0
   461
        m3gMulQuat(&(sequence->a[kf]), (Quat*)temp);
sl@0
   462
sl@0
   463
        /* Then repeat the same steps for the incoming tangent and
sl@0
   464
           the "B" intermediate quaternion. */
sl@0
   465
sl@0
   466
        tangent = cfd;
sl@0
   467
        m3gScaleVec3(&tangent, m3gIncomingTangentScale(sequence, kf));
sl@0
   468
sl@0
   469
        m3gLogDiffQuat((Vec3*)temp, &prev, &start);
sl@0
   470
        m3gSubVec3((Vec3*)temp, &tangent);
sl@0
   471
        m3gScaleVec3((Vec3*)temp, 0.5f);
sl@0
   472
        m3gExpQuat((Quat*)temp, (Vec3*)temp);
sl@0
   473
        sequence->b[kf] = start;
sl@0
   474
        m3gMulQuat(&(sequence->b[kf]), (Quat*)temp);
sl@0
   475
        
sl@0
   476
        kf = m3gNextKeyframeIndex(sequence, kf);
sl@0
   477
    } while (kf != sequence->firstValid);
sl@0
   478
}
sl@0
   479
sl@0
   480
/*!
sl@0
   481
 * \internal
sl@0
   482
 * \brief Update all tangents
sl@0
   483
 *
sl@0
   484
 * \param sequence  KeyframeSequence object
sl@0
   485
 */
sl@0
   486
static void m3gUpdateTangents(KeyframeSequence *sequence)
sl@0
   487
{
sl@0
   488
    if (sequence->interpolation == M3G_SPLINE) {
sl@0
   489
        m3gPrecalculateTangents(sequence);
sl@0
   490
    }
sl@0
   491
    else if (sequence->interpolation == M3G_SQUAD) {
sl@0
   492
        m3gPrecalculateAB(sequence);
sl@0
   493
    }
sl@0
   494
}
sl@0
   495
sl@0
   496
/*!
sl@0
   497
 * \internal
sl@0
   498
 * \brief Linear interpolate
sl@0
   499
 *
sl@0
   500
 * \param sequence      KeyframeSequence object
sl@0
   501
 * \param sample        input samples
sl@0
   502
 * \param s             speed
sl@0
   503
 * \param startIndex    start index
sl@0
   504
 * \param endIndex      end index
sl@0
   505
 */
sl@0
   506
static M3G_INLINE void m3gLerpSample(const KeyframeSequence *sequence,
sl@0
   507
                                     M3Gfloat *sample,
sl@0
   508
                                     M3Gfloat s,
sl@0
   509
                                     M3Gint startIndex, M3Gint endIndex)
sl@0
   510
{
sl@0
   511
    const M3Gfloat *start = m3gKeyframeAt(sequence, startIndex);
sl@0
   512
    const M3Gfloat *end   = m3gKeyframeAt(sequence, endIndex);
sl@0
   513
sl@0
   514
    m3gLerp(sequence->numComponents, sample, s, start, end);
sl@0
   515
}
sl@0
   516
sl@0
   517
/*!
sl@0
   518
 * \internal
sl@0
   519
 * \brief Spline interpolate
sl@0
   520
 *
sl@0
   521
 * \param sequence      KeyframeSequence object
sl@0
   522
 * \param sample        input samples
sl@0
   523
 * \param s             speed
sl@0
   524
 * \param startIndex    start index
sl@0
   525
 * \param endIndex      end index
sl@0
   526
 */
sl@0
   527
static M3G_INLINE void m3gSplineSample(const KeyframeSequence *sequence,
sl@0
   528
                                       M3Gfloat *sample,
sl@0
   529
                                       M3Gfloat s,
sl@0
   530
                                       M3Gint startIndex, M3Gint endIndex)
sl@0
   531
{
sl@0
   532
    const M3Gfloat *start, *end;
sl@0
   533
    const M3Gfloat *tStart, *tEnd;
sl@0
   534
    
sl@0
   535
    /* Get the required keyframe values and the (one-sided) tangents
sl@0
   536
     * at the ends of the segment. */
sl@0
   537
    
sl@0
   538
    start = m3gKeyframeAt(sequence, startIndex);
sl@0
   539
    end   = m3gKeyframeAt(sequence, endIndex);
sl@0
   540
sl@0
   541
    tStart = m3gTangentFrom(sequence, startIndex);
sl@0
   542
    tEnd = m3gTangentTo(sequence, endIndex);
sl@0
   543
sl@0
   544
    /* Interpolate the final value using a Hermite spline. */
sl@0
   545
sl@0
   546
    m3gHermite(sequence->numComponents, sample, s, start, end, tStart, tEnd);
sl@0
   547
}
sl@0
   548
sl@0
   549
/*!
sl@0
   550
 * \internal
sl@0
   551
 * \brief Spherical linear interpolate
sl@0
   552
 *
sl@0
   553
 * \param sequence      KeyframeSequence object
sl@0
   554
 * \param sample        input samples
sl@0
   555
 * \param s             speed
sl@0
   556
 * \param startIndex    start index
sl@0
   557
 * \param endIndex      end index
sl@0
   558
 */
sl@0
   559
static M3G_INLINE void m3gSlerpSample(const KeyframeSequence *sequence,
sl@0
   560
                                      M3Gfloat *sample,
sl@0
   561
                                      M3Gfloat s,
sl@0
   562
                                      M3Gint startIndex, M3Gint endIndex)
sl@0
   563
{
sl@0
   564
    if (sequence->numComponents != 4) {
sl@0
   565
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
sl@0
   566
        return;
sl@0
   567
    }
sl@0
   568
    
sl@0
   569
    m3gSlerpQuat((Quat *) sample,
sl@0
   570
                 s,
sl@0
   571
                 (const Quat *) m3gKeyframeAt(sequence, startIndex),
sl@0
   572
                 (const Quat *) m3gKeyframeAt(sequence, endIndex));
sl@0
   573
}
sl@0
   574
sl@0
   575
/*!
sl@0
   576
 * \internal
sl@0
   577
 * \brief Spline interpolate quats
sl@0
   578
 *
sl@0
   579
 * \param sequence      KeyframeSequence object
sl@0
   580
 * \param sample        input samples
sl@0
   581
 * \param s             speed
sl@0
   582
 * \param startIndex    start index
sl@0
   583
 * \param endIndex      end index
sl@0
   584
 */
sl@0
   585
static M3G_INLINE void m3gSquadSample(const KeyframeSequence *sequence,
sl@0
   586
                                      M3Gfloat *sample,
sl@0
   587
                                      M3Gfloat s,
sl@0
   588
                                      M3Gint startIndex, M3Gint endIndex)
sl@0
   589
{
sl@0
   590
    if (sequence->numComponents != 4) {
sl@0
   591
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
sl@0
   592
        return;
sl@0
   593
    }
sl@0
   594
sl@0
   595
    m3gSquadQuat((Quat *) sample,
sl@0
   596
                 s,
sl@0
   597
                 (const Quat *) m3gKeyframeAt(sequence, startIndex),
sl@0
   598
                 &(sequence->a[startIndex]),
sl@0
   599
                 &(sequence->b[endIndex]),
sl@0
   600
                 (const Quat *) m3gKeyframeAt(sequence, endIndex));
sl@0
   601
}
sl@0
   602
sl@0
   603
/*!
sl@0
   604
 * \internal
sl@0
   605
 * \brief Get sample
sl@0
   606
 *
sl@0
   607
 * \param sequence      KeyframeSequence object
sl@0
   608
 * \param time          time
sl@0
   609
 * \param sample        pointer to sample
sl@0
   610
 * \return              sample validity
sl@0
   611
 */
sl@0
   612
static M3Gint m3gGetSample(KeyframeSequence *sequence,
sl@0
   613
                           M3Gint time,
sl@0
   614
                           M3Gfloat *sample)
sl@0
   615
{
sl@0
   616
    M3Gint start, end, i;
sl@0
   617
    const M3Gfloat *value;
sl@0
   618
    M3Gfloat s;
sl@0
   619
    
sl@0
   620
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   621
    
sl@0
   622
    if (sequence->dirty == M3G_TRUE) {
sl@0
   623
        if (!m3gValidSequence(sequence)) {
sl@0
   624
            m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_OPERATION);
sl@0
   625
            return 0;
sl@0
   626
        }
sl@0
   627
        m3gUpdateTangents(sequence);
sl@0
   628
        sequence->dirty = M3G_FALSE;
sl@0
   629
        sequence->probablyNext = sequence->firstValid;
sl@0
   630
    }
sl@0
   631
sl@0
   632
    /* First, map the time to the valid range of a repeating
sl@0
   633
       sequence, or handle the special end cases of an open-ended
sl@0
   634
       sequence. */
sl@0
   635
    
sl@0
   636
    if (sequence->closed) {
sl@0
   637
        if (time < 0)
sl@0
   638
            time = (time % sequence->duration) + sequence->duration;
sl@0
   639
        else
sl@0
   640
            time = time % sequence->duration;
sl@0
   641
        if (time < sequence->keyframeTimes[sequence->firstValid]) {
sl@0
   642
            time += sequence->duration;
sl@0
   643
        }
sl@0
   644
    }
sl@0
   645
    else {
sl@0
   646
        if (time < sequence->keyframeTimes[sequence->firstValid]) {
sl@0
   647
            value = m3gKeyframeAt(sequence, sequence->firstValid);
sl@0
   648
            for (i = 0; i < sequence->numComponents; i++)
sl@0
   649
                sample[i] = value[i];
sl@0
   650
            return (sequence->keyframeTimes[sequence->firstValid] - time);
sl@0
   651
        }
sl@0
   652
        else if (time >= sequence->keyframeTimes[sequence->lastValid]) {
sl@0
   653
            value = m3gKeyframeAt(sequence, sequence->lastValid);
sl@0
   654
            for (i = 0; i < sequence->numComponents; i++)
sl@0
   655
                sample[i] = value[i];
sl@0
   656
            /* \ define a meaningful constant */
sl@0
   657
            return 0x7FFFFFFF;
sl@0
   658
        }
sl@0
   659
    }
sl@0
   660
sl@0
   661
    /* Search for the starting keyframe of the segment to
sl@0
   662
       interpolate. Starting the search from the previously
sl@0
   663
       used keyframe, we are very likely to find the match
sl@0
   664
       sooner than if we'd start from the first keyframe. */
sl@0
   665
sl@0
   666
    start = sequence->probablyNext;
sl@0
   667
    if (sequence->keyframeTimes[start] > time)
sl@0
   668
        start = sequence->firstValid;
sl@0
   669
    while (start != sequence->lastValid &&
sl@0
   670
           sequence->keyframeTimes[m3gNextKeyframeIndex(sequence, start)] <= time) {
sl@0
   671
        start = m3gNextKeyframeIndex(sequence, start);
sl@0
   672
    }
sl@0
   673
    sequence->probablyNext = start;
sl@0
   674
    
sl@0
   675
    /* Calculate the interpolation factor if necessary; the quick
sl@0
   676
       exit also avoids a division by zero in the case that we
sl@0
   677
       have a quirky sequence with only multiple coincident
sl@0
   678
       keyframes. */
sl@0
   679
sl@0
   680
    if (time == sequence->keyframeTimes[start] || sequence->interpolation == M3G_STEP) {
sl@0
   681
        value = m3gKeyframeAt(sequence, start);
sl@0
   682
        for (i = 0; i < sequence->numComponents; i++)
sl@0
   683
            sample[i] = value[i];
sl@0
   684
        return (sequence->interpolation == M3G_STEP)
sl@0
   685
               ? (m3gTimeDelta(sequence, start) - (time - sequence->keyframeTimes[start]))
sl@0
   686
               : 1;
sl@0
   687
    }
sl@0
   688
    s = m3gDivif(time - sequence->keyframeTimes[start], m3gTimeDelta(sequence, start));
sl@0
   689
    
sl@0
   690
    /* Pick the correct interpolation function and pass the
sl@0
   691
       segment start and end keyframe indices. */
sl@0
   692
sl@0
   693
    end = m3gNextKeyframeIndex(sequence, start);
sl@0
   694
sl@0
   695
    switch (sequence->interpolation) {
sl@0
   696
    case M3G_LINEAR:
sl@0
   697
        m3gLerpSample(sequence, sample, s, start, end);
sl@0
   698
        break;
sl@0
   699
    case M3G_SLERP:
sl@0
   700
        m3gSlerpSample(sequence, sample, s, start, end);
sl@0
   701
        break;
sl@0
   702
    case M3G_SPLINE:
sl@0
   703
        m3gSplineSample(sequence, sample, s, start, end);
sl@0
   704
        break;
sl@0
   705
    case M3G_SQUAD:
sl@0
   706
        m3gSquadSample(sequence, sample, s, start, end);
sl@0
   707
        break;
sl@0
   708
    default:
sl@0
   709
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_ENUM);
sl@0
   710
        return 0;
sl@0
   711
    }
sl@0
   712
sl@0
   713
    return 1;
sl@0
   714
}
sl@0
   715
sl@0
   716
/*----------------------------------------------------------------------
sl@0
   717
 * Virtual function table
sl@0
   718
 *--------------------------------------------------------------------*/
sl@0
   719
sl@0
   720
static const ObjectVFTable m3gvf_KeyframeSequence = {
sl@0
   721
    m3gObjectApplyAnimation,
sl@0
   722
    m3gObjectIsCompatible,
sl@0
   723
    m3gObjectUpdateProperty,
sl@0
   724
    m3gObjectDoGetReferences,
sl@0
   725
    m3gObjectFindID,
sl@0
   726
    m3gKeyframeSequenceDuplicate,
sl@0
   727
    m3gDestroyKeyframeSequence
sl@0
   728
};
sl@0
   729
sl@0
   730
sl@0
   731
/*----------------------------------------------------------------------
sl@0
   732
 * Public API functions
sl@0
   733
 *--------------------------------------------------------------------*/
sl@0
   734
sl@0
   735
/*!
sl@0
   736
 * \brief Creates a new KeyframeSequence with default values
sl@0
   737
 * 
sl@0
   738
 * \param hInterface            M3G interface
sl@0
   739
 * \param numKeyframes          number of keyframes
sl@0
   740
 * \param numComponents         number of components
sl@0
   741
 * \param interpolation         interpolation type
sl@0
   742
 * \retval KeyframeSequence new KeyframeSequence object
sl@0
   743
 * \retval NULL KeyframeSequence creating failed
sl@0
   744
 */
sl@0
   745
/*@access M3GInterface@*/
sl@0
   746
/*@access M3Gappearance@*/
sl@0
   747
M3G_API M3GKeyframeSequence m3gCreateKeyframeSequence(M3GInterface hInterface,
sl@0
   748
                                                      M3Gint numKeyframes,
sl@0
   749
                                                      M3Gint numComponents,
sl@0
   750
                                                      M3Gint interpolation)
sl@0
   751
{
sl@0
   752
    Interface *m3g = (Interface *) hInterface;
sl@0
   753
    M3G_VALIDATE_INTERFACE(m3g);
sl@0
   754
sl@0
   755
    if (numKeyframes < 1 || numComponents < 1
sl@0
   756
        || interpolation < M3G_LINEAR || interpolation > M3G_STEP
sl@0
   757
        || ((interpolation == M3G_SLERP || interpolation == M3G_SQUAD)
sl@0
   758
            && numComponents != 4)) {
sl@0
   759
        m3gRaiseError(m3g, M3G_INVALID_VALUE);
sl@0
   760
        return NULL;
sl@0
   761
    }
sl@0
   762
sl@0
   763
    {
sl@0
   764
        KeyframeSequence *sequence = m3gAllocZ(m3g, sizeof(KeyframeSequence));
sl@0
   765
sl@0
   766
        if (sequence != NULL) {
sl@0
   767
            if (m3gInitKeyframeSequence(m3g,
sl@0
   768
                                        sequence,
sl@0
   769
                                        numKeyframes, numComponents,
sl@0
   770
                                        interpolation) == NULL) {
sl@0
   771
                m3gFree(m3g, sequence);
sl@0
   772
                return NULL;
sl@0
   773
            }
sl@0
   774
        }
sl@0
   775
sl@0
   776
        return (M3GKeyframeSequence) sequence;
sl@0
   777
    }
sl@0
   778
}
sl@0
   779
sl@0
   780
/*!
sl@0
   781
 * \brief Assigns a time and value to a keyframe sequence entry
sl@0
   782
 *
sl@0
   783
 * \param handle    handle of the keyframe sequence object
sl@0
   784
 * \param ind       index of the entry to set
sl@0
   785
 * \param time      time to set in the entry
sl@0
   786
 * \param valueSize number of elements in the value; this must match
sl@0
   787
 *                  the number of elements given when constructing
sl@0
   788
 *                  the sequence
sl@0
   789
 * \param value     pointer to an array of \c valueSize floats
sl@0
   790
 */
sl@0
   791
M3G_API void m3gSetKeyframe(M3GKeyframeSequence handle,
sl@0
   792
                            M3Gint ind,
sl@0
   793
                            M3Gint time,
sl@0
   794
                            M3Gint valueSize, const M3Gfloat *value)
sl@0
   795
{
sl@0
   796
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   797
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   798
sl@0
   799
    /* Check for invalid inputs */
sl@0
   800
    
sl@0
   801
    if (value == NULL) {
sl@0
   802
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_NULL_POINTER);
sl@0
   803
        return;
sl@0
   804
    }
sl@0
   805
    if (valueSize < sequence->numComponents || time < 0) {
sl@0
   806
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
sl@0
   807
        return;
sl@0
   808
    }
sl@0
   809
    if (ind < 0 || ind >= sequence->numKeyframes) {
sl@0
   810
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
sl@0
   811
        return;
sl@0
   812
    }
sl@0
   813
sl@0
   814
    /* Assign  the  time  and  value. Quaternion  keyframes  are  also
sl@0
   815
     * normalized, as indicated in the specification. */    
sl@0
   816
    {
sl@0
   817
        M3Gfloat *kf = (M3Gfloat *) m3gKeyframeAt(sequence, ind);
sl@0
   818
        int c;
sl@0
   819
        
sl@0
   820
        sequence->keyframeTimes[ind] = time;
sl@0
   821
        
sl@0
   822
        for (c = 0; c < sequence->numComponents; ++c) {
sl@0
   823
            kf[c] = value[c];
sl@0
   824
        }
sl@0
   825
sl@0
   826
        if (sequence->interpolation == M3G_SLERP
sl@0
   827
            || sequence->interpolation == M3G_SQUAD) {
sl@0
   828
            m3gNormalizeQuat((Quat*) kf);
sl@0
   829
        }
sl@0
   830
    }
sl@0
   831
    
sl@0
   832
    sequence->dirty = M3G_TRUE;        
sl@0
   833
}
sl@0
   834
sl@0
   835
/*!
sl@0
   836
 * \brief Set valid range
sl@0
   837
 *
sl@0
   838
 * \param handle    handle of the keyframe sequence object
sl@0
   839
 * \param first     first valid keyframe
sl@0
   840
 * \param last      last valid keyframe
sl@0
   841
 */
sl@0
   842
M3G_API void m3gSetValidRange(M3GKeyframeSequence handle,
sl@0
   843
                              M3Gint first, M3Gint last)
sl@0
   844
{
sl@0
   845
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   846
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   847
sl@0
   848
    if (first < 0 || first >= sequence->numKeyframes ||
sl@0
   849
        last < 0 || last >= sequence->numKeyframes) {
sl@0
   850
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
sl@0
   851
        return;
sl@0
   852
    }
sl@0
   853
sl@0
   854
    sequence->firstValid = first;
sl@0
   855
    sequence->lastValid = last;
sl@0
   856
    sequence->dirty = M3G_TRUE;
sl@0
   857
}
sl@0
   858
sl@0
   859
/*!
sl@0
   860
 * \brief Set duration
sl@0
   861
 *
sl@0
   862
 * \param handle    handle of the keyframe sequence object
sl@0
   863
 * \param duration  duration
sl@0
   864
 */
sl@0
   865
M3G_API void m3gSetDuration(M3GKeyframeSequence handle, M3Gint duration)
sl@0
   866
{
sl@0
   867
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   868
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   869
sl@0
   870
    /* Check for errors */
sl@0
   871
    if (duration <= 0) {
sl@0
   872
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_VALUE);
sl@0
   873
        return;
sl@0
   874
    }
sl@0
   875
sl@0
   876
    sequence->duration = duration;
sl@0
   877
    sequence->dirty = M3G_TRUE;
sl@0
   878
}
sl@0
   879
sl@0
   880
/*!
sl@0
   881
 * \brief Get duration
sl@0
   882
 *
sl@0
   883
 * \param handle    handle of the keyframe sequence object
sl@0
   884
 * \return          duration
sl@0
   885
 */
sl@0
   886
M3G_API M3Gint m3gGetDuration(M3GKeyframeSequence handle)
sl@0
   887
{
sl@0
   888
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   889
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   890
    return sequence->duration;
sl@0
   891
}
sl@0
   892
sl@0
   893
/*!
sl@0
   894
 * \brief Get component count
sl@0
   895
 *
sl@0
   896
 * \param handle    handle of the keyframe sequence object
sl@0
   897
 * \return          component count
sl@0
   898
 */
sl@0
   899
M3G_API M3Gint m3gGetComponentCount(M3GKeyframeSequence handle)
sl@0
   900
{
sl@0
   901
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   902
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   903
    return sequence->numComponents;
sl@0
   904
}
sl@0
   905
sl@0
   906
/*!
sl@0
   907
 * \brief Get interpolation type
sl@0
   908
 *
sl@0
   909
 * \param handle    handle of the keyframe sequence object
sl@0
   910
 * \return          interpolation type
sl@0
   911
 */
sl@0
   912
M3G_API M3Gint m3gGetInterpolationType(M3GKeyframeSequence handle)
sl@0
   913
{
sl@0
   914
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   915
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   916
    return sequence->interpolation;
sl@0
   917
}
sl@0
   918
sl@0
   919
/*!
sl@0
   920
 * \brief Get keyframe value
sl@0
   921
 *
sl@0
   922
 * \param handle     handle of the keyframe sequence object
sl@0
   923
 * \param frameIndex keyframe index
sl@0
   924
 * \param value      value array
sl@0
   925
 
sl@0
   926
 * \return           time value of the keyframe
sl@0
   927
 */
sl@0
   928
M3G_API M3Gint m3gGetKeyframe  (M3GKeyframeSequence handle, M3Gint frameIndex, M3Gfloat *value)
sl@0
   929
{
sl@0
   930
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   931
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   932
sl@0
   933
    if (frameIndex < 0 || frameIndex >= sequence->numKeyframes) {
sl@0
   934
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_INDEX);
sl@0
   935
        return 0;
sl@0
   936
    }
sl@0
   937
sl@0
   938
    if (value != NULL) {
sl@0
   939
        m3gCopy(    value,
sl@0
   940
                    sequence->keyframes + frameIndex * sequence->numComponents,
sl@0
   941
                    sequence->numComponents * sizeof(M3Gfloat));
sl@0
   942
    }
sl@0
   943
sl@0
   944
    return sequence->keyframeTimes[frameIndex];
sl@0
   945
}
sl@0
   946
sl@0
   947
/*!
sl@0
   948
 * \brief Get keyframe count
sl@0
   949
 *
sl@0
   950
 * \param handle    handle of the keyframe sequence object
sl@0
   951
 * \return          keyframe count
sl@0
   952
 */
sl@0
   953
M3G_API M3Gint m3gGetKeyframeCount(M3GKeyframeSequence handle)
sl@0
   954
{
sl@0
   955
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   956
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   957
    return sequence->numKeyframes;
sl@0
   958
}
sl@0
   959
sl@0
   960
/*!
sl@0
   961
 * \brief Get valid range
sl@0
   962
 *
sl@0
   963
 * \param handle    handle of the keyframe sequence object
sl@0
   964
 * \param first     pointer to valid range start
sl@0
   965
 * \param last      pointer to valid range end
sl@0
   966
 */
sl@0
   967
M3G_API void m3gGetValidRange(M3GKeyframeSequence handle, M3Gint *first, M3Gint *last)
sl@0
   968
{
sl@0
   969
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   970
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   971
    *first = sequence->firstValid;
sl@0
   972
    *last = sequence->lastValid;
sl@0
   973
}
sl@0
   974
sl@0
   975
/*!
sl@0
   976
 * \brief Set repeat mode
sl@0
   977
 *
sl@0
   978
 * \param handle    handle of the keyframe sequence object
sl@0
   979
 * \param mode      repeat mode
sl@0
   980
 */
sl@0
   981
M3G_API void m3gSetRepeatMode(M3GKeyframeSequence handle, M3Genum mode)
sl@0
   982
{
sl@0
   983
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
   984
    M3G_VALIDATE_OBJECT(sequence);
sl@0
   985
    if (mode != M3G_CONSTANT && mode != M3G_LOOP) {
sl@0
   986
        m3gRaiseError(M3G_INTERFACE(sequence), M3G_INVALID_ENUM);
sl@0
   987
        return;
sl@0
   988
    }
sl@0
   989
    sequence->closed = (mode == M3G_LOOP) ? M3G_TRUE : M3G_FALSE;
sl@0
   990
}
sl@0
   991
sl@0
   992
/*!
sl@0
   993
 * \brief Get repeat mode
sl@0
   994
 *
sl@0
   995
 * \param handle    handle of the keyframe sequence object
sl@0
   996
 * \return          repeat mode
sl@0
   997
 */
sl@0
   998
M3G_API M3Genum m3gGetRepeatMode(M3GKeyframeSequence handle)
sl@0
   999
{
sl@0
  1000
    KeyframeSequence *sequence = (KeyframeSequence *)handle;
sl@0
  1001
    M3G_VALIDATE_OBJECT(sequence);
sl@0
  1002
    return (sequence->closed ? M3G_LOOP : M3G_CONSTANT);
sl@0
  1003
}
sl@0
  1004