/*
 *
 * VRML to RW converter plug-in
 */

/****************************************************************************
 *                                                                         
 * Hierarchical animation controller
 * Copyright (C) 1998 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : rpanimfr.c
 *                                                                         
 * Purpose : Functions for creating and controlling articulated hierarchical
 *           keyframe animation
 *                                                                         
 ****************************************************************************/

/****************************************************************************
 Includes
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "rpplugin.h"
#include <rpdbgerr.h>

#include "rpvrmlanim.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: rpvrmlanim.c,v 1.31 2001/02/07 11:10:41 Blakem Exp $";

#if defined (__MWERKS__)
#if (defined(RWVERBOSE))
#pragma message (__FILE__ "/" _SKY_EXPAND(__LINE__) ": __MWERKS__ == " _SKY_EXPAND(__MWERKS__))
#endif /* (defined (__MWERKS__)) */
#if (__option (global_optimizer))
#pragma always_inline off
#endif /* (__option (global_optimizer)) */
#endif /*  defined (__MWERKS__) */

/****************************************************************************
 Local Defines
 */

#define PI ((RwReal)(3.1415926535))
#define PAD (15)

#define RWFRAMEGETANIM(frame) \
    ((RpFrameVRMLAnim *)((((RwInt32)frame) + PAD + frameAnimOffset)&~0xf))
#define RWFRAMEGETCONSTANIM(frame) \
    ((const RpFrameVRMLAnim *)((((RwInt32)frame) + PAD + frameAnimOffset)&~0xf))

#define RWATOMICGETANIM(atomic) \
    ((RpAtomicAnim *)(((RwInt32 )atomic) + atomicAnimOffset))
#define RWATOMICGETCONSTANIM(atomic) \
    ((const RpAtomicAnim *)(((const RwInt32)atomic) + atomicAnimOffset))

/****************************************************************************
 External non API RW functions
 */

/****************************************************************************
 Local (static) Types
 */

typedef struct RpFrameVRMLAnim RpFrameVRMLAnim;
struct RpFrameVRMLAnim
{
    /* This defines all the animation types applicable to frames, and is an
     * extension to RwFrames
     */
    RpVRMLAnimTranslate *animTranslate;
    RpVRMLAnimRotate   *animRotate;
    RpVRMLAnimScale    *animScale;
    RpVRMLAnimRotate   *animScaleOrient;
    RpVRMLAnimTransformState matrixComp;
    RwChar             *name;
};

enum _rpFrameVRMLAnimContents
{
    rpFRAMEVRMLANIM_TRANSLATION = 0x00000001,
    rpFRAMEVRMLANIM_TRANSLATIONSTATE = 0x00000002,
    rpFRAMEVRMLANIM_ROTATION = 0x00000004,
    rpFRAMEVRMLANIM_ROTATIONSTATE = 0x00000008,
    rpFRAMEVRMLANIM_SCALE = 0x00000010,
    rpFRAMEVRMLANIM_SCALESTATE = 0x00000020,
    rpFRAMEVRMLANIM_SCALEORIENT = 0x00000040,
    rpFRAMEVRMLANIM_SCALEORIENTSTATE = 0x00000080,
    rpFRAMEVRMLANIM_NAME = 0x00000100,
    rpFRAMEVRMLANIMCONTENTSFORCEENUMSIZEINT = RWFORCEENUMSIZEINT
};
typedef enum _rpFrameVRMLAnimContents _rpFrameVRMLAnimContents;

typedef struct _rpBinaryAnimInterpolator _rpBinaryAnimInterpolator;
struct _rpBinaryAnimInterpolator
{
    /* This is for the binary storage of animation data */
    RwInt32             flags;
    RwInt32             startKeyFrame;
    RwInt32             endKeyFrame;
    RwReal              time;
    /* Even though the interpolators are joined in a loop, we don't rely on this,
     * * we store the sequence explicitly.  Makes it easier later!!!
     */
    RwInt32             nextInterpIndex;
};

/* used for animation optimization */

typedef struct rpVRMLInterpInfo rpVRMLInterpInfo;
struct rpVRMLInterpInfo
{
    RwInt32             newIndex;
    RwInt32             refCntr;
    RwBool              optimized;
    RwBool              loopStart;
    RwBool              cut;
    RwInt32             visitedBy;
    RpVRMLAnimInterpolator *interp;
    rpVRMLInterpInfo   *next;
};

typedef struct rpVRMLKeyInfo rpVRMLKeyInfo;
struct rpVRMLKeyInfo
{
    RwBool              used;
    RwInt16             newIndex;
};

typedef RwBool (*rpVRMLInterpInfoSpanTestCallBack) (rpVRMLInterpInfo ** interpSpanStart, RwInt32 interval, void *keys, RwReal delta);
/****************************************************************************
 Local (static) Globals
 */

/****************************************************************************
 Public Globals
 */

RwModuleInfo        vrmlAnimModule;

static RwInt32      frameAnimOffset = 0;

/****************************************************************************
 Local (Static) Prototypes
 */

/* No binary stream stuff for atomics */

static RwChar      *
StrDup(RwChar * name)
{
    RwChar             *newName;

    RWFUNCTION(RWSTRING("StrDup"));

    newName = (RwChar *) RwMalloc(rwstrlen(name) + 1);
    if (newName)
    {
        rwstrcpy(newName, name);
    }
    RWRETURN(newName);
}

static RwStream    *
VRMLAnimStateWriteStream(RwStream * stream, RpVRMLAnimState * animState)
{
    RwInt32             numInterpolators;
    RwInt32             i;

    RWFUNCTION(RWSTRING("VRMLAnimStateWriteStream"));
    RWASSERT(stream);
    RWASSERT(animState);

    numInterpolators = animState->numInterpolators;
    if (!RwStreamWriteInt
        (stream, &numInterpolators, sizeof(numInterpolators)))
    {
        /* Failed to write to the stream */
        RWRETURN((RwStream *)NULL);
    }

    for (i = 0; i < numInterpolators; i++)
    {
        _rpBinaryAnimInterpolator binAnimInterp;

        binAnimInterp.flags = animState->allInterps[i].flags;
        binAnimInterp.startKeyFrame =
            animState->allInterps[i].startKeyFrame;
        binAnimInterp.endKeyFrame =
            animState->allInterps[i].endKeyFrame;
        binAnimInterp.time = animState->allInterps[i].time;
        binAnimInterp.nextInterpIndex = animState->allInterps[i].next
            - animState->allInterps;

        RwMemRealToFloat32(&binAnimInterp.time,
                           sizeof(binAnimInterp.time));
        RwMemLittleEndian(&binAnimInterp, sizeof(binAnimInterp));

        if (!RwStreamWrite
            (stream, &binAnimInterp, sizeof(binAnimInterp)))
        {
            /* Failed to write to the stream, ooops */
            RWRETURN((RwStream *)NULL);
        }
    }

    RWRETURN(stream);
}

static              RwReal
DefaultFrameAnimCB(RpVRMLAnimState * animCntrl, RwReal position)
{
    RWFUNCTION(RWSTRING("DefaultFrameAnimCB"));
    RWASSERT(animCntrl);

    if (animCntrl)
    {
        RwReal              newPos;

        newPos = position - animCntrl->interp->time;
        if (newPos < (RwReal) (0.0))
        {
            newPos = (RwReal) (0.0);
        }

        animCntrl->interp = animCntrl->interp->next;

        RWRETURN(newPos);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(((RwReal) (-1.0)));
}

static RpVRMLAnimState *
VRMLAnimStateReadStream(RwStream * stream)
{
    RpVRMLAnimState    *newAnimState;
    RwInt32             numInterpolators;
    RwInt32             i;

    RWFUNCTION(RWSTRING("VRMLAnimStateReadStream"));
    RWASSERT(stream);

    if (!RwStreamReadInt
        (stream, &numInterpolators, sizeof(numInterpolators)))
    {
        /* Failed to read form the stream */
        RWRETURN((RpVRMLAnimState *)NULL);
    }

    /* Create the state with the right number of interpolators */
    newAnimState = _rpVrmAnimStateCreate(numInterpolators);
    if (!newAnimState)
    {
        /* Failed to create the anim state */
        RWRETURN((RpVRMLAnimState *)NULL);
    }

    for (i = 0; i < numInterpolators; i++)
    {
        _rpBinaryAnimInterpolator binAnimInterp;
        RwUInt32            size;

        size = sizeof(binAnimInterp);
        if (RwStreamRead(stream, &binAnimInterp, size) != size)
        {
            /* Failed to read the stream, ooops */
            _rpVrmAnimStateDestroy(newAnimState);
            RWRETURN((RpVRMLAnimState *)NULL);
        }

        RwMemNative(&binAnimInterp, sizeof(binAnimInterp));
        RwMemFloat32ToReal(&binAnimInterp.time,
                           sizeof(binAnimInterp.time));

        /* Do it this way so we can do our own thing with the next pointer!! */
        newAnimState->allInterps[i].flags = binAnimInterp.flags;
        newAnimState->allInterps[i].startKeyFrame =
            (RwInt16) binAnimInterp.startKeyFrame;
        newAnimState->allInterps[i].endKeyFrame =
            (RwInt16) binAnimInterp.endKeyFrame;
        newAnimState->allInterps[i].time = binAnimInterp.time;
        newAnimState->allInterps[i].recipTime =
            ((RwReal) (1.0)) / (binAnimInterp.time);
        newAnimState->allInterps[i].next =
            &newAnimState->allInterps[binAnimInterp.nextInterpIndex];
    }

    /* Set the callback to the default - we can't save this is the binary format */
    _rpVrmAnimStateSetCallBack(newAnimState, DefaultFrameAnimCB);

    /* Set the position back to the start */
    newAnimState->position = (RwReal) (0.0);
    newAnimState->interp = newAnimState->allInterps;

    RWRETURN(newAnimState);
}

static              RwInt32
VRMLAnimStateSizeStream(RpVRMLAnimState * animState)
{
    RwInt32             size;

    RWFUNCTION(RWSTRING("VRMLAnimStateSizeStream"));
    RWASSERT(animState);

    size = sizeof(RwInt32);    /* numInterpolators */
    size +=
        (animState->numInterpolators *
         sizeof(_rpBinaryAnimInterpolator));

    RWRETURN(size);
}

static RpVRMLAnimState *
VRMLAnimStateCopy(const RpVRMLAnimState * srcAnimCntrl)
{
    RpVRMLAnimState    *dstAnimCntrl;
    RpVRMLAnimInterpolator *dstInterpolators, *srcInterpolators;
    RwInt32             interpolatorNum, numInterpolators;

    RWFUNCTION(RWSTRING("VRMLAnimStateCopy"));
    RWASSERT(srcAnimCntrl);

    numInterpolators = srcAnimCntrl->numInterpolators;
    dstAnimCntrl = _rpVrmAnimStateCreate(numInterpolators);
    if (!dstAnimCntrl)
    {
        RWRETURN((RpVRMLAnimState *)NULL);
    }

    /* copy the interpolators */
    dstInterpolators = dstAnimCntrl->allInterps;
    srcInterpolators = srcAnimCntrl->allInterps;
    for (interpolatorNum = 0; interpolatorNum < numInterpolators;
         interpolatorNum++)
    {
        dstInterpolators->flags = srcInterpolators->flags;
        dstInterpolators->startKeyFrame =
            srcInterpolators->startKeyFrame;
        dstInterpolators->endKeyFrame = srcInterpolators->endKeyFrame;
        dstInterpolators->time = srcInterpolators->time;
        dstInterpolators->recipTime = srcInterpolators->recipTime;
        dstInterpolators->next = dstAnimCntrl->allInterps +
            (srcInterpolators->next - srcAnimCntrl->allInterps);

        dstInterpolators++;
        srcInterpolators++;
    }

    /* get the current interpolator */
    dstAnimCntrl->interp = dstAnimCntrl->allInterps +
        (srcAnimCntrl->interp - srcAnimCntrl->allInterps);

    /* copy the position */
    dstAnimCntrl->position = srcAnimCntrl->position;

    /* get the callback */
    dstAnimCntrl->fpAnimCntrlCB = srcAnimCntrl->fpAnimCntrlCB;

    RWRETURN(dstAnimCntrl);
}

static              RwBool
TranslateKeyBlend(RpVRMLTranslateKey * blendKey, RpVRMLTranslateKey *
                  startKey, RpVRMLTranslateKey * endKey, RwReal along)
{
    RWFUNCTION(RWSTRING("TranslateKeyBlend"));
    RWASSERT(blendKey);
    RWASSERT(startKey);
    RWASSERT(endKey);

    if (blendKey && startKey && endKey)
    {
        RwV3d              *start, *end, *blend;

        start = (RwV3d *) startKey;
        end = (RwV3d *) endKey;
        blend = (RwV3d *) blendKey;

        RwV3dSub(blend, end, start);
        RwV3dScale(blend, blend, along);
        RwV3dAdd(blend, blend, start);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static RwMatrix    *
Rotate2Matrix(RpVRMLRotate * rotate, RwMatrix * matrix)
{
    RWFUNCTION(RWSTRING("Rotate2Matrix"));
    RWASSERT(rotate);
    RWASSERT(matrix);

    if (rotate && matrix)
    {
        RwV3d               axis;
        RwReal              angle =
            ((rotate->angle)) * (((RwReal) (180.0)) / (PI));

        axis = rotate->axis;
        RwMatrixSetIdentity(matrix);
        RwMatrixRotate(matrix, &axis, angle, rwCOMBINEPRECONCAT);

        RWRETURN(matrix);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RwMatrix *)NULL);
}

static              RwBool
SetupSlerp(RtSlerp * slerp, RpVRMLRotateKey * startKey,
           RpVRMLRotateKey * endKey)
{
    RWFUNCTION(RWSTRING("SetupSlerp"));
    RWASSERT(slerp);
    RWASSERT(startKey);
    RWASSERT(endKey);

    if (slerp && startKey && endKey)
    {
        RwMatrix            startMatrix, endMatrix;

        Rotate2Matrix(startKey, &startMatrix);
        Rotate2Matrix(endKey, &endMatrix);

        if (!RtSlerpInitialize(slerp, &startMatrix, &endMatrix))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static RwFrame     *
FrameVRMLAnimAddTranslateDelta(RwFrame * frame, RwReal delta)
{
    RpVRMLAnimTranslate *translateAnim;
    RpVRMLAnimState    *translateCntrl;
    RpVRMLAnimInterpolator *interpolator;
    RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

    RWFUNCTION(RWSTRING("FrameVRMLAnimAddTranslateDelta"));
    RWASSERT(frame);

    translateAnim = _rpVrmlFrameAnimGetTranslations(frame);
    if (!translateAnim)
    {
        RWRETURN((RwFrame *)NULL);
    }
    translateCntrl = _rpVrmAnimTranslateGetState(translateAnim);
    if (!translateCntrl)
    {
        RWRETURN((RwFrame *)NULL);
    }

    interpolator = translateCntrl->interp;
    if (!interpolator)
    {
        RWRETURN((RwFrame *)NULL);
    }

    translateCntrl->position += delta;
    while (translateCntrl->position > translateCntrl->interp->time)
    {
        translateCntrl->position =
            translateCntrl->fpAnimCntrlCB(translateCntrl,
                                          translateCntrl->position);
    }

    {
        RpVRMLTranslateKey *startKey, *endKey, blendKey;
        RwInt32             startKeyNum, endKeyNum;
        RwReal              along;
        RpVRMLAnimTransformState *matrixComp;

        startKeyNum = translateCntrl->interp->startKeyFrame;
        endKeyNum = translateCntrl->interp->endKeyFrame;
        startKey = &translateAnim->translationKeys[startKeyNum];
        endKey = &translateAnim->translationKeys[endKeyNum];
        along =
            translateCntrl->position *
            translateCntrl->interp->recipTime;

        TranslateKeyBlend(&blendKey, startKey, endKey, along);

        matrixComp = &frameAnim->matrixComp;
        matrixComp->translation = blendKey;
    }

    RWRETURN(frame);
}

static RwFrame     *
FrameVRMLAnimAddRotateDelta(RwFrame * frame, RwReal delta)
{
    RpVRMLAnimRotate   *rotateAnim;
    RpVRMLAnimState    *rotateCntrl;
    RpVRMLAnimInterpolator *interpolator;
    RwBool              setNewSlerp = FALSE;
    RwReal              along;
    RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

    RWFUNCTION(RWSTRING("FrameVRMLAnimAddRotateDelta"));
    RWASSERT(frame);

    rotateAnim = _rpVrmlFrameAnimGetRotations(frame);
    if (!rotateAnim)
    {
        RWRETURN((RwFrame *)NULL);
    }

    rotateCntrl = _rpVrmAnimRotateGetState(rotateAnim);
    if (!rotateCntrl)
    {
        RWRETURN((RwFrame *)NULL);
    }

    interpolator = rotateCntrl->interp;
    if (!interpolator)
    {
        RWRETURN((RwFrame *)NULL);
    }

    if (rotateCntrl->position == (RwReal) (0.0))
    {
        setNewSlerp = TRUE;
    }

    rotateCntrl->position += delta;
    while (rotateCntrl->position > rotateCntrl->interp->time)
    {
        rotateCntrl->position =
            rotateCntrl->fpAnimCntrlCB(rotateCntrl,
                                       rotateCntrl->position);
        setNewSlerp = TRUE;
    }

    along = rotateCntrl->position * rotateCntrl->interp->recipTime;

#if 0
    /* temp hack */
    if (along == -1.0f)
    {
        setNewSlerp = TRUE;
    }
#endif /* 0 */

    {
        RpVRMLRotateKey    *startKey, *endKey;
        RwInt32             startKeyNum, endKeyNum;
        RpVRMLAnimTransformState *matrixComp;
        RwMatrix           *rotate;
        RtSlerp            *slerp;
        RwBool              useSlerp = TRUE;

        startKeyNum = rotateCntrl->interp->startKeyFrame;
        endKeyNum = rotateCntrl->interp->endKeyFrame;
        startKey = &rotateAnim->rotationKeys[startKeyNum];
        endKey = &rotateAnim->rotationKeys[endKeyNum];
        matrixComp = &frameAnim->matrixComp;
        rotate = &matrixComp->rotationMat;
        slerp = rotateAnim->slerp;

        if ((startKey->axis.x == endKey->axis.x) &&
            (startKey->axis.y == endKey->axis.y) &&
            (startKey->axis.z == endKey->axis.z) &&
            (startKey->angle == endKey->angle))
        {
            useSlerp = FALSE;
        }

        if (setNewSlerp)
        {
            /* this horrible bit of code is to get around a bug in the slerp code */
            if (useSlerp)
            {
                SetupSlerp(slerp, startKey, endKey);
                RtSlerpGetMatrix(slerp, rotate, along);
            }
            else
            {
                Rotate2Matrix(startKey, rotate);
            }
        }
        else
        {
            if (useSlerp)
            {
                RtSlerpGetMatrix(slerp, rotate, along);
            }
        }
    }

    RWRETURN(frame);
}

static RwFrame     *
FrameVRMLAnimAddScaleDelta(RwFrame * frame, RwReal delta)
{
    RpVRMLAnimScale    *scaleAnim;
    RpVRMLAnimState    *scaleCntrl;
    RpVRMLAnimInterpolator *interpolator;
    RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

    RWFUNCTION(RWSTRING("FrameVRMLAnimAddScaleDelta"));
    RWASSERT(frame);

    scaleAnim = _rpVrmlFrameAnimGetScales(frame);
    if (!scaleAnim)
    {
        RWRETURN((RwFrame *)NULL);
    }

    scaleCntrl = _rpVrmAnimScaleGetState(scaleAnim);
    if (!scaleCntrl)
    {
        RWRETURN((RwFrame *)NULL);
    }

    interpolator = scaleCntrl->interp;
    if (!interpolator)
    {
        RWRETURN((RwFrame *)NULL);
    }

    scaleCntrl->position += delta;
    while (scaleCntrl->position > scaleCntrl->interp->time)
    {
        scaleCntrl->position =
            scaleCntrl->fpAnimCntrlCB(scaleCntrl, scaleCntrl->position);
    }

    {
        RpVRMLScaleKey     *startKey, *endKey, blendKey;
        RwInt32             startKeyNum, endKeyNum;
        RwReal              along;
        RpVRMLAnimTransformState *matrixComp;

        startKeyNum = scaleCntrl->interp->startKeyFrame;
        endKeyNum = scaleCntrl->interp->endKeyFrame;
        startKey = &scaleAnim->scaleKeys[startKeyNum];
        endKey = &scaleAnim->scaleKeys[endKeyNum];
        along = scaleCntrl->position * scaleCntrl->interp->recipTime;

        TranslateKeyBlend(&blendKey, startKey, endKey, along);

        matrixComp = &frameAnim->matrixComp;
        matrixComp->scale = blendKey;
    }

    RWRETURN(frame);
}

static RwFrame     *
FrameVRMLAnimAddScaleOrientDelta(RwFrame * frame, RwReal delta)
{
    RpVRMLAnimRotate   *scaleOrientAnim;
    RpVRMLAnimState    *scaleOrientCntrl;
    RpVRMLAnimInterpolator *interpolator;
    RwBool              setNewSlerp = FALSE;
    RwReal              along;
    RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

    RWFUNCTION(RWSTRING("FrameVRMLAnimAddScaleOrientDelta"));
    RWASSERT(frame);

    scaleOrientAnim = _rpVrmlFrameAnimGetScaleOrients(frame);
    if (!scaleOrientAnim)
    {
        RWRETURN((RwFrame *)NULL);
    }

    scaleOrientCntrl = _rpVrmAnimRotateGetState(scaleOrientAnim);
    if (!scaleOrientCntrl)
    {
        RWRETURN((RwFrame *)NULL);
    }

    interpolator = scaleOrientCntrl->interp;
    if (!interpolator)
    {
        RWRETURN((RwFrame *)NULL);
    }

    if (scaleOrientCntrl->position == (RwReal) (0.0))
    {
        setNewSlerp = TRUE;
    }

    scaleOrientCntrl->position += delta;
    while (scaleOrientCntrl->position > scaleOrientCntrl->interp->time)
    {
        scaleOrientCntrl->position =
            scaleOrientCntrl->fpAnimCntrlCB(scaleOrientCntrl,
                                            scaleOrientCntrl->position);
        setNewSlerp = TRUE;
    }

    along =
        scaleOrientCntrl->position *
        scaleOrientCntrl->interp->recipTime;

#if 0
    /* temp hack */
    if (along == -1.0f)
    {
        setNewSlerp = TRUE;
    }
#endif /* 0 */

    {
        RpVRMLRotateKey    *startKey, *endKey;
        RwInt32             startKeyNum, endKeyNum;
        RpVRMLAnimTransformState *matrixComp;
        RwMatrix           *rotate;
        RtSlerp            *slerp;
        RwBool              useSlerp = TRUE;

        startKeyNum = scaleOrientCntrl->interp->startKeyFrame;
        endKeyNum = scaleOrientCntrl->interp->endKeyFrame;
        startKey = &scaleOrientAnim->rotationKeys[startKeyNum];
        endKey = &scaleOrientAnim->rotationKeys[endKeyNum];
        matrixComp = &frameAnim->matrixComp;
        rotate = &matrixComp->scaleOrientMat;
        slerp = scaleOrientAnim->slerp;

        if ((startKey->axis.x == endKey->axis.x) &&
            (startKey->axis.y == endKey->axis.y) &&
            (startKey->axis.z == endKey->axis.z) &&
            (startKey->angle == endKey->angle))
        {
            useSlerp = FALSE;
        }

        if (setNewSlerp)
        {
            /* this horrible bit of code is to get around a bug in the slerp code */
            if (useSlerp)
            {
                SetupSlerp(slerp, startKey, endKey);
                RtSlerpGetMatrix(slerp, rotate, along);
            }
            else
            {
                Rotate2Matrix(startKey, rotate);
            }
        }
        else
        {
            if (useSlerp)
            {
                RtSlerpGetMatrix(slerp, rotate, along);
            }
        }
    }

    RWRETURN(frame);
}

static RpVRMLAnimTranslate *
VRMLAnimTranslateCopy(const RpVRMLAnimTranslate * srcTranslateAnim)
{
    RpVRMLAnimTranslate *dstTranslateAnim;
    RwInt32             numTranslationKeys;
    RpVRMLTranslateKey *dstTranslationKeys, *srcTranslationKeys;

    RWFUNCTION(RWSTRING("VRMLAnimTranslateCopy"));
    RWASSERT(srcTranslateAnim);

    dstTranslateAnim = _rpVrmAnimTranslateCreate();
    if (!dstTranslateAnim)
    {
        RWRETURN((RpVRMLAnimTranslate *)NULL);
    }

    if (srcTranslateAnim->translateCntrl)
    {
        dstTranslateAnim->translateCntrl =
            VRMLAnimStateCopy(srcTranslateAnim->translateCntrl);
    }

    numTranslationKeys = srcTranslateAnim->numTranslationKeys;
    srcTranslationKeys = srcTranslateAnim->translationKeys;
    if (numTranslationKeys && srcTranslationKeys)
    {
        if (_rpVrmAnimTranslateAddKeys
            (dstTranslateAnim, numTranslationKeys))
        {
            dstTranslationKeys =
                _rpVrmAnimTranslateGetKeys(dstTranslateAnim);
            if (dstTranslationKeys)
            {
                memcpy(dstTranslationKeys, srcTranslationKeys,
                       numTranslationKeys * sizeof(RpVRMLTranslateKey));
            }
        }
    }

    RWRETURN(dstTranslateAnim);
}

static              RwInt32
FrameVRMLAnimWhatsHere(const RwFrame * frame)
{
    RwInt32             whatsHere;
    RpVRMLAnimTranslate *animTrans;
    RpVRMLAnimRotate   *animRotate;
    RpVRMLAnimScale    *animScale;
    RpVRMLAnimRotate   *animScOri;
    RwChar             *name;

    RWFUNCTION(RWSTRING("FrameVRMLAnimWhatsHere"));
    RWASSERT(frame);

    whatsHere = 0;
    animTrans = _rpVrmlFrameAnimGetTranslations(frame);
    if (animTrans)
    {
        whatsHere |= rpFRAMEVRMLANIM_TRANSLATION;
        if (_rpVrmAnimTranslateGetState(animTrans))
        {
            whatsHere |= rpFRAMEVRMLANIM_TRANSLATIONSTATE;
        }
    }

    animRotate = _rpVrmlFrameAnimGetRotations(frame);
    if (animRotate)
    {
        whatsHere |= rpFRAMEVRMLANIM_ROTATION;
        if (_rpVrmAnimRotateGetState(animRotate))
        {
            whatsHere |= rpFRAMEVRMLANIM_ROTATIONSTATE;
        }
    }

    animScale = _rpVrmlFrameAnimGetScales(frame);
    if (animScale)
    {
        whatsHere |= rpFRAMEVRMLANIM_SCALE;
        if (_rpVrmAnimScaleGetState(animScale))
        {
            whatsHere |= rpFRAMEVRMLANIM_SCALESTATE;
        }
    }

    animScOri = _rpVrmlFrameAnimGetScaleOrients(frame);
    if (animScOri)
    {
        whatsHere |= rpFRAMEVRMLANIM_SCALEORIENT;
        if (_rpVrmAnimRotateGetState(animScOri))
        {
            whatsHere |= rpFRAMEVRMLANIM_SCALEORIENTSTATE;
        }
    }

    name = _rpVrmlFrameAnimGetName(frame);
    if (name)
    {
        whatsHere |= rpFRAMEVRMLANIM_NAME;
    }
    RWRETURN(whatsHere);
}

static RwStream    *
FrameVRMLAnimWriteStream(RwStream * stream,
                         RwInt32 __RWUNUSED__ binaryLength,
                         const void *object,
                         RwInt32 __RWUNUSED__ offsetInObject,
                         RwInt32 __RWUNUSED__ sizeInObject)
{
    const RwFrame      *frame = (const RwFrame *) object;
    RwInt32             whatsHere;

    RWFUNCTION(RWSTRING("FrameVRMLAnimWriteStream"));
    RWASSERT(stream);
    RWASSERT(object);

    /* Set a bit field describing what we are going to save */
    whatsHere = FrameVRMLAnimWhatsHere(frame);

    if (!RwStreamWriteInt(stream, &whatsHere, sizeof(whatsHere)))
    {
        /* Failed to write to stream, ooops */
        RWRETURN((RwStream *)NULL);
    }

    if (whatsHere & rpFRAMEVRMLANIM_NAME)
    {
        RwInt32             len;
        RwChar             *name;

        /* save the name (preceeded by the size in bytes) */
        name = _rpVrmlFrameAnimGetName(frame);
        len = rwstrlen(name) * sizeof(RwChar);

        if (!RwStreamWriteInt(stream, &len, sizeof(len)))
        {
            /* Failed to write to stream, ooops */
            RWRETURN((RwStream *)NULL);
        }
        if (!RwStreamWrite(stream, name, len))
        {
            /* Failed to write to stream, ooops */
            RWRETURN((RwStream *)NULL);
        }
    }

    if (whatsHere & rpFRAMEVRMLANIM_TRANSLATION)
    {
        RpVRMLAnimTranslate *animTrans =
            _rpVrmlFrameAnimGetTranslations(frame);
        RwInt32             numKeys;

        numKeys = _rpVrmAnimTranslateGetNumKeys(animTrans);
        if (!RwStreamWriteInt(stream, &numKeys, sizeof(numKeys)))
        {
            /* Failed to write, ooops */
            RWRETURN((RwStream *)NULL);
        }

        if (whatsHere & rpFRAMEVRMLANIM_TRANSLATIONSTATE)
        {
            /* Write out the frame anim */
            if (!VRMLAnimStateWriteStream(stream,
                                          _rpVrmAnimTranslateGetState
                                          (animTrans)))
            {
                /* Failed to write to stream, ooops */
                RWRETURN((RwStream *)NULL);
            }
        }

        if (numKeys)
        {
            /* Write out the keys - these are composed entirely of RwReals (lucky us) */
            if (!RwStreamWriteReal(stream, (RwReal *)
                                   _rpVrmAnimTranslateGetKeys
                                   (animTrans),
                                   numKeys *
                                   sizeof(RpVRMLTranslateKey)))
            {
                /* Failed to write to stream, ooops */
                RWRETURN((RwStream *)NULL);
            }
        }
    }

    if (whatsHere & rpFRAMEVRMLANIM_ROTATION)
    {
        RpVRMLAnimRotate   *animRotate =
            _rpVrmlFrameAnimGetRotations(frame);
        RwInt32             numKeys;

        numKeys = _rpVrmAnimRotateGetNumKeys(animRotate);
        if (!RwStreamWriteInt(stream, &numKeys, sizeof(numKeys)))
        {
            /* Failed to write, ooops */
            RWRETURN((RwStream *)NULL);
        }

        if (whatsHere & rpFRAMEVRMLANIM_ROTATIONSTATE)
        {
            /* Write out the frame anim */
            if (!VRMLAnimStateWriteStream
                (stream, _rpVrmAnimRotateGetState(animRotate)))
            {
                /* Failed to write to stream, ooops */
                RWRETURN((RwStream *)NULL);
            }
        }

        if (numKeys)
        {
            /* Write out the keys - these are composed entirely of RwReals (lucky us!) */
            if (!RwStreamWriteReal(stream, (RwReal *)
                                   _rpVrmAnimRotateGetKeys(animRotate),
                                   numKeys * sizeof(RpVRMLRotateKey)))
            {
                /* Failed to write to stream, ooops */
                RWRETURN((RwStream *)NULL);
            }
        }
    }

    if (whatsHere & rpFRAMEVRMLANIM_SCALE)
    {
        RpVRMLAnimScale    *animScale =
            _rpVrmlFrameAnimGetScales(frame);
        RwInt32             numKeys;

        numKeys = _rpVrmAnimScaleGetNumKeys(animScale);
        if (!RwStreamWriteInt(stream, &numKeys, sizeof(numKeys)))
        {
            /* Failed to write, ooops */
            RWRETURN((RwStream *)NULL);
        }

        if (whatsHere & rpFRAMEVRMLANIM_TRANSLATIONSTATE)
        {
            /* Write out the frame anim */
            if (!VRMLAnimStateWriteStream
                (stream, _rpVrmAnimScaleGetState(animScale)))
            {
                /* Failed to write to stream, ooops */
                RWRETURN((RwStream *)NULL);
            }
        }

        if (numKeys)
        {
            /* Write out the keys - these are RwV3ds */
            if (!RwStreamWriteReal(stream, (RwReal *)
                                   _rpVrmAnimScaleGetKeys(animScale),
                                   numKeys * sizeof(RpVRMLScaleKey)))
            {
                /* Failed to write to stream, ooops */
                RWRETURN((RwStream *)NULL);
            }
        }
    }

    if (whatsHere & rpFRAMEVRMLANIM_SCALEORIENT)
    {
        RpVRMLAnimRotate   *animScOri =
            _rpVrmlFrameAnimGetScaleOrients(frame);
        RwInt32             numKeys;

        numKeys = _rpVrmAnimRotateGetNumKeys(animScOri);
        if (!RwStreamWriteInt(stream, &numKeys, sizeof(numKeys)))
        {
            /* Failed to write, ooops */
            RWRETURN((RwStream *)NULL);
        }

        if (whatsHere & rpFRAMEVRMLANIM_TRANSLATIONSTATE)
        {
            /* Write out the frame anim */
            if (!VRMLAnimStateWriteStream
                (stream, _rpVrmAnimRotateGetState(animScOri)))
            {
                /* Failed to write to stream, ooops */
                RWRETURN((RwStream *)NULL);
            }
        }

        if (numKeys)
        {
            /* Write out the keys - these are RwV3ds */
            if (!RwStreamWriteReal(stream, (RwReal *)
                                   _rpVrmAnimRotateGetKeys(animScOri),
                                   numKeys * sizeof(RpVRMLRotateKey)))
            {
                /* Failed to write to stream, ooops */
                RWRETURN((RwStream *)NULL);
            }
        }
    }

    /* All done - phew!!! */
    RWRETURN(stream);
}

static RwStream    *
FrameVRMLAnimReadStream(RwStream * stream,
                        RwInt32 __RWUNUSED__ binaryLength,
                        void *object,
                        RwInt32 __RWUNUSED__ offsetInObject,
                        RwInt32 __RWUNUSED__ sizeInObject)
{
    RwFrame            *frame = (RwFrame *) object;
    RwInt32             whatsHere;
    RwInt32             len;
    RwChar              name[64];

    RWFUNCTION(RWSTRING("FrameVRMLAnimReadStream"));
    RWASSERT(stream);
    RWASSERT(object);

    /* Read in the bit field which says what we have */
    if (!RwStreamReadInt(stream, &whatsHere, sizeof(whatsHere)))
    {
        /* Failed to read from the stream, ooops */
        RWRETURN((RwStream *)NULL);
    }

    if (whatsHere & rpFRAMEVRMLANIM_NAME)
    {
        if (!RwStreamReadInt(stream, &len, sizeof(len)))
        {
            /* Failed to read stream, ooops */
            RWRETURN((RwStream *)NULL);
        }
        if (RwStreamRead(stream, name, len) != (RwUInt32) len)
        {
            /* Failed to read stream, ooops */
            RWRETURN((RwStream *)NULL);
        }
        _rpVrmlFrameAnimSetName(frame, name);
    }

    if (whatsHere & rpFRAMEVRMLANIM_TRANSLATION)
    {
        RpVRMLAnimTranslate *animTrans;
        RwInt32             numKeys;

        animTrans = _rpVrmAnimTranslateCreate();
        if (!animTrans)
        {
            /* Failed to create structure element, so fail!!! */
            RWRETURN((RwStream *)NULL);
        }
        _rpVrmlFrameAnimSetTranslations(frame, animTrans);

        if (!RwStreamReadInt(stream, &numKeys, sizeof(numKeys)))
        {
            RWRETURN((RwStream *)NULL);
        }

        if (!_rpVrmAnimTranslateAddKeys(animTrans, numKeys))
        {
            RWRETURN((RwStream *)NULL);
        }

        if (whatsHere & rpFRAMEVRMLANIM_TRANSLATIONSTATE)
        {
            RpVRMLAnimState    *newState;

            newState = VRMLAnimStateReadStream(stream);
            if (!newState)
            {
                RWRETURN((RwStream *)NULL);
            }
            _rpVrmAnimTranslateSetState(animTrans, newState);
        }

        if (numKeys)
        {
            if (!RwStreamReadReal(stream, (RwReal *)
                                  _rpVrmAnimTranslateGetKeys(animTrans),
                                  numKeys * sizeof(RpVRMLTranslateKey)))
            {
                RWRETURN((RwStream *)NULL);
            }
        }
    }

    if (whatsHere & rpFRAMEVRMLANIM_ROTATION)
    {
        RpVRMLAnimRotate   *animRotate;
        RwInt32             numKeys;

        animRotate = _rpVrmAnimRotateCreate();
        if (!animRotate)
        {
            /* Failed to create structure element, so fail!!! */
            RWRETURN((RwStream *)NULL);
        }
        _rpVrmlFrameAnimSetRotations(frame, animRotate);

        if (!RwStreamReadInt(stream, &numKeys, sizeof(numKeys)))
        {
            RWRETURN((RwStream *)NULL);
        }

        if (!_rpVrmAnimRotateAddKeys(animRotate, numKeys))
        {
            RWRETURN((RwStream *)NULL);
        }

        if (whatsHere & rpFRAMEVRMLANIM_ROTATIONSTATE)
        {
            RpVRMLAnimState    *newState;

            newState = VRMLAnimStateReadStream(stream);
            if (!newState)
            {
                RWRETURN((RwStream *)NULL);
            }
            _rpVrmAnimRotateSetState(animRotate, newState);
        }

        if (numKeys)
        {
            if (!RwStreamReadReal(stream, (RwReal *)
                                  _rpVrmAnimRotateGetKeys(animRotate),
                                  numKeys * sizeof(RpVRMLRotateKey)))
            {
                RWRETURN((RwStream *)NULL);
            }
        }
    }

    if (whatsHere & rpFRAMEVRMLANIM_SCALE)
    {
        RpVRMLAnimScale    *animScale;
        RwInt32             numKeys;

        animScale = _rpVrmAnimScaleCreate();
        if (!animScale)
        {
            /* Failed to create structure element, so fail!!! */
            RWRETURN((RwStream *)NULL);
        }
        _rpVrmlFrameAnimSetScales(frame, animScale);

        if (!RwStreamReadInt(stream, &numKeys, sizeof(numKeys)))
        {
            RWRETURN((RwStream *)NULL);
        }

        if (!_rpVrmAnimScaleAddKeys(animScale, numKeys))
        {
            RWRETURN((RwStream *)NULL);
        }

        if (whatsHere & rpFRAMEVRMLANIM_SCALESTATE)
        {
            RpVRMLAnimState    *newState;

            newState = VRMLAnimStateReadStream(stream);
            if (!newState)
            {
                RWRETURN((RwStream *)NULL);
            }
            _rpVrmAnimScaleSetState(animScale, newState);
        }

        if (numKeys)
        {
            if (!RwStreamReadReal(stream, (RwReal *)
                                  _rpVrmAnimScaleGetKeys(animScale),
                                  numKeys * sizeof(RpVRMLScaleKey)))
            {
                RWRETURN((RwStream *)NULL);
            }
        }
    }

    if (whatsHere & rpFRAMEVRMLANIM_SCALEORIENT)
    {
        RpVRMLAnimRotate   *animScOri;
        RwInt32             numKeys;

        animScOri = _rpVrmAnimRotateCreate();
        if (!animScOri)
        {
            /* Failed to create structure element, so fail!!! */
            RWRETURN((RwStream *)NULL);
        }
        _rpVrmlFrameAnimSetScaleOrients(frame, animScOri);

        if (!RwStreamReadInt(stream, &numKeys, sizeof(numKeys)))
        {
            RWRETURN((RwStream *)NULL);
        }

        if (!_rpVrmAnimRotateAddKeys(animScOri, numKeys))
        {
            RWRETURN((RwStream *)NULL);
        }

        if (whatsHere & rpFRAMEVRMLANIM_SCALEORIENTSTATE)
        {
            RpVRMLAnimState    *newState;

            newState = VRMLAnimStateReadStream(stream);
            if (!newState)
            {
                RWRETURN((RwStream *)NULL);
            }
            _rpVrmAnimRotateSetState(animScOri, newState);
        }

        if (numKeys)
        {
            if (!RwStreamReadReal(stream, (RwReal *)
                                  _rpVrmAnimRotateGetKeys(animScOri),
                                  numKeys * sizeof(RpVRMLRotateKey)))
            {
                RWRETURN((RwStream *)NULL);
            }
        }
    }

    RWRETURN(stream);
}

static              RwInt32
FrameVRMLAnimSizeStream(const void *object,
                        RwInt32 __RWUNUSED__ offsetInObject,
                        RwInt32 __RWUNUSED__ sizeInObject)
{
    RwInt32             whatsHere;
    const RwFrame      *frame = (const RwFrame *) object;
    RwInt32             size;

    RWFUNCTION(RWSTRING("FrameVRMLAnimSizeStream"));
    RWASSERT(object);

    whatsHere = FrameVRMLAnimWhatsHere(frame);

    size = sizeof(RwInt32);    /* whatsHere */

    /* Add on the bits we need */
    if (whatsHere & rpFRAMEVRMLANIM_TRANSLATION)
    {
        RpVRMLAnimTranslate *animTrans =
            _rpVrmlFrameAnimGetTranslations(frame);

        size += sizeof(RwInt32); /* numKeys */
        if (whatsHere & rpFRAMEVRMLANIM_TRANSLATIONSTATE)
        {
            size +=
                VRMLAnimStateSizeStream(_rpVrmAnimTranslateGetState
                                        (animTrans));
        }
        size +=
            (_rpVrmAnimTranslateGetNumKeys(animTrans) *
             sizeof(RpVRMLTranslateKey));
    }

    if (whatsHere & rpFRAMEVRMLANIM_ROTATION)
    {
        RpVRMLAnimRotate   *animRotate =
            _rpVrmlFrameAnimGetRotations(frame);

        size += sizeof(RwInt32); /* numKeys */
        if (whatsHere & rpFRAMEVRMLANIM_ROTATIONSTATE)
        {
            size +=
                VRMLAnimStateSizeStream(_rpVrmAnimRotateGetState
                                        (animRotate));
        }
        size +=
            (_rpVrmAnimRotateGetNumKeys(animRotate) *
             sizeof(RpVRMLRotateKey));
    }

    if (whatsHere & rpFRAMEVRMLANIM_SCALE)
    {
        RpVRMLAnimScale    *animScale =
            _rpVrmlFrameAnimGetScales(frame);

        size += sizeof(RwInt32); /* numKeys */
        if (whatsHere & rpFRAMEVRMLANIM_SCALESTATE)
        {
            size +=
                VRMLAnimStateSizeStream(_rpVrmAnimScaleGetState
                                        (animScale));
        }
        size +=
            (_rpVrmAnimScaleGetNumKeys(animScale) *
             sizeof(RpVRMLScaleKey));
    }

    if (whatsHere & rpFRAMEVRMLANIM_SCALEORIENT)
    {
        RpVRMLAnimRotate   *animScOri =
            _rpVrmlFrameAnimGetRotations(frame);

        size += sizeof(RwInt32); /* numKeys */
        if (whatsHere & rpFRAMEVRMLANIM_SCALEORIENTSTATE)
        {
            size +=
                VRMLAnimStateSizeStream(_rpVrmAnimRotateGetState
                                        (animScOri));
        }
        size +=
            (_rpVrmAnimRotateGetNumKeys(animScOri) *
             sizeof(RpVRMLRotateKey));
    }

    /* Return the accumulated total */
    RWRETURN(size);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   RpFrameVRMLAnim functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwFrame            *
_rpVrmlFrameAnimSetName(RwFrame * frame, const RwChar * name)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimSetName"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

            frameAnim->name = (RwChar *)
                RwMalloc((rwstrlen(name) + 1) * sizeof(RwChar));
            if (frameAnim->name)
            {
                rwstrcpy(frameAnim->name, name);
                RWRETURN(frame);
            }
            RWRETURN((RwFrame *)NULL);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwFrame *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwFrame *)NULL);
}

RwChar             *
_rpVrmlFrameAnimGetName(const RwFrame * frame)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimGetName"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            const RpFrameVRMLAnim *frameAnim =
                RWFRAMEGETCONSTANIM(frame);

            RWRETURN(frameAnim->name);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((char *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((char *)NULL);
}

RwFrame            *
_rpVrmlFrameAnimSetTranslations(RwFrame * frame,
                                RpVRMLAnimTranslate * translateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimSetTranslations"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

            frameAnim->animTranslate = translateAnim;

            RWRETURN(frame);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwFrame *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwFrame *)NULL);
}

RpVRMLAnimTranslate *
_rpVrmlFrameAnimGetTranslations(const RwFrame * frame)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimGetTranslations"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            const RpFrameVRMLAnim *frameAnim =
                RWFRAMEGETCONSTANIM(frame);

            RWRETURN(frameAnim->animTranslate);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimTranslate *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimTranslate *)NULL);
}

RwFrame            *
_rpVrmlFrameAnimSetRotations(RwFrame * frame,
                             RpVRMLAnimRotate * rotateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimSetRotations"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

            frameAnim->animRotate = rotateAnim;

            RWRETURN(frame);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwFrame *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwFrame *)NULL);
}

RpVRMLAnimRotate   *
_rpVrmlFrameAnimGetRotations(const RwFrame * frame)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimGetRotations"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            const RpFrameVRMLAnim *frameAnim =
                RWFRAMEGETCONSTANIM(frame);

            RWRETURN(frameAnim->animRotate);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimRotate *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimRotate *)NULL);
}

RwFrame            *
_rpVrmlFrameAnimSetScales(RwFrame * frame, RpVRMLAnimScale * scaleAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimSetScales"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

            frameAnim->animScale = scaleAnim;

            RWRETURN(frame);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwFrame *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwFrame *)NULL);
}

RpVRMLAnimScale    *
_rpVrmlFrameAnimGetScales(const RwFrame * frame)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimGetScales"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            const RpFrameVRMLAnim *frameAnim =
                RWFRAMEGETCONSTANIM(frame);

            RWRETURN(frameAnim->animScale);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimScale *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimScale *)NULL);
}

RwFrame            *
_rpVrmlFrameAnimSetScaleOrients(RwFrame * frame,
                                RpVRMLAnimRotate * scaleOrientAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimSetScaleOrients"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);

            frameAnim->animScaleOrient = scaleOrientAnim;

            RWRETURN(frame);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwFrame *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwFrame *)NULL);
}

RpVRMLAnimRotate   *
_rpVrmlFrameAnimGetScaleOrients(const RwFrame * frame)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimGetScaleOrients"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            const RpFrameVRMLAnim *frameAnim =
                RWFRAMEGETCONSTANIM(frame);

            RWRETURN(frameAnim->animScaleOrient);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimRotate *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimRotate *)NULL);
}

RpVRMLAnimTransformState *
_rpVrmlFrameAnimGetTransformState(const RwFrame * frame)
{
    RpVRMLAnimTransformState *result = (RpVRMLAnimTransformState *)NULL;

    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimGetTransformState"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (!vrmlAnimModule.numInstances)
    {

        RWERROR((E_RW_PLUGINNOTINIT));
    }
    else
    {
        if (!frame)
        {
            RWERROR((E_RW_NULLP));
        }
        else
        {
            const RpFrameVRMLAnim *frameAnim =
                RWFRAMEGETCONSTANIM(frame);

            result =
                (RpVRMLAnimTransformState *) & frameAnim->matrixComp;
        }
    }

    RWRETURN(result);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   RpVRMLAnimTranslate functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RpVRMLAnimTranslate *
_rpVrmAnimTranslateCreate(void)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimTranslateCreate"));
    RWASSERT(vrmlAnimModule.numInstances);

    if (vrmlAnimModule.numInstances)
    {
        RpVRMLAnimTranslate *translateAnim;

        translateAnim =
            (RpVRMLAnimTranslate *)
            RwMalloc(sizeof(RpVRMLAnimTranslate));
        if (translateAnim)
        {
            translateAnim->translateCntrl = (RpVRMLAnimState *)NULL;
            translateAnim->numTranslationKeys = 0;
            translateAnim->translationKeys = (RwV3d *)NULL;
        }

        RWRETURN(translateAnim);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimTranslate *)NULL);
}

RwBool
_rpVrmAnimTranslateDestroy(RpVRMLAnimTranslate * translateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimTranslateDestroy"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(translateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (translateAnim)
        {
            if (translateAnim->translateCntrl)
            {
                _rpVrmAnimStateDestroy(translateAnim->translateCntrl);
            }

            if (translateAnim->translationKeys)
            {
                RwFree(translateAnim->translationKeys);
            }

            RwFree(translateAnim);

            RWRETURN(TRUE);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(FALSE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

RwInt32
_rpVrmAnimTranslateGetNumKeys(const RpVRMLAnimTranslate * translateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimTranslateGetNumKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(translateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (translateAnim)
        {
            RWRETURN(translateAnim->numTranslationKeys);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(-1);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(-1);
}

RpVRMLTranslateKey *
_rpVrmAnimTranslateGetKeys(const RpVRMLAnimTranslate * translateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimTranslateGetKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(translateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (translateAnim)
        {
            RWRETURN(translateAnim->translationKeys);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwV3d *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwV3d *)NULL);
}

RpVRMLAnimTranslate *
_rpVrmAnimTranslateAddKeys(RpVRMLAnimTranslate * translateAnim,
                           RwInt32 numKeys)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimTranslateAddKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(translateAnim);
    RWASSERT(numKeys > 0);

    if (vrmlAnimModule.numInstances)
    {
        if (translateAnim)
        {
            RpVRMLTranslateKey *translateKeys;

            if (numKeys < 1)
            {
                RWRETURN((RpVRMLAnimTranslate *)NULL);
            }

            translateKeys = (RpVRMLTranslateKey *)
                RwMalloc(sizeof(RpVRMLTranslateKey) * numKeys);
            if (translateKeys)
            {
                translateAnim->numTranslationKeys = numKeys;
                translateAnim->translationKeys = translateKeys;
            }

            RWRETURN(translateAnim);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimTranslate *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimTranslate *)NULL);
}

RpVRMLAnimTranslate *
_rpVrmAnimTranslateSetState(RpVRMLAnimTranslate * translateAnim,
                            RpVRMLAnimState * translateCntrl)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimTranslateSetState"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(translateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (translateAnim)
        {
            translateAnim->translateCntrl = translateCntrl;

            RWRETURN(translateAnim);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimTranslate *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimTranslate *)NULL);
}

RpVRMLAnimState    *
_rpVrmAnimTranslateGetState(const RpVRMLAnimTranslate * translateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimTranslateGetState"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(translateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (translateAnim)
        {
            RWRETURN(translateAnim->translateCntrl);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimState *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimState *)NULL);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   RpVRMLAnimRotate functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RpVRMLAnimRotate   *
_rpVrmAnimRotateCreate(void)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimRotateCreate"));
    RWASSERT(vrmlAnimModule.numInstances);

    if (vrmlAnimModule.numInstances)
    {
        RpVRMLAnimRotate   *rotateAnim;

        rotateAnim =
            (RpVRMLAnimRotate *) RwMalloc(sizeof(RpVRMLAnimRotate));
        if (rotateAnim)
        {
            rotateAnim->rotateCntrl = (RpVRMLAnimState *)NULL;
            rotateAnim->numRotationKeys = 0;
            rotateAnim->rotationKeys = (RpVRMLRotate *)NULL;
            rotateAnim->slerp = RtSlerpCreate(rtSLERPREFNONE);
            if (!rotateAnim->slerp)
            {
                _rpVrmAnimRotateDestroy(rotateAnim);

                RWRETURN((RpVRMLAnimRotate *)NULL);
            }
        }

        RWRETURN(rotateAnim);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimRotate *)NULL);
}

RwBool
_rpVrmAnimRotateDestroy(RpVRMLAnimRotate * rotateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimRotateDestroy"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(rotateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (rotateAnim)
        {
            if (rotateAnim->rotateCntrl)
            {
                _rpVrmAnimStateDestroy(rotateAnim->rotateCntrl);
            }

            if (rotateAnim->rotationKeys)
            {
                RwFree(rotateAnim->rotationKeys);
            }

            if (rotateAnim->slerp)
            {
                RtSlerpDestroy(rotateAnim->slerp);
            }

            RwFree(rotateAnim);

            RWRETURN(TRUE);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(FALSE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

static RpVRMLAnimRotate *
VRMLAnimRotateCopy(const RpVRMLAnimRotate * srcRotateAnim)
{
    RpVRMLAnimRotate   *dstRotateAnim;
    RwInt32             numRotationKeys;
    RpVRMLRotateKey    *dstRotationKeys, *srcRotationKeys;

    RWFUNCTION(RWSTRING("VRMLAnimRotateCopy"));
    RWASSERT(srcRotateAnim);

    dstRotateAnim = _rpVrmAnimRotateCreate();
    if (!dstRotateAnim)
    {
        RWRETURN((RpVRMLAnimRotate *)NULL);
    }

    if (srcRotateAnim->rotateCntrl)
    {
        dstRotateAnim->rotateCntrl =
            VRMLAnimStateCopy(srcRotateAnim->rotateCntrl);
    }

    numRotationKeys = srcRotateAnim->numRotationKeys;
    srcRotationKeys = srcRotateAnim->rotationKeys;
    if (numRotationKeys && srcRotationKeys)
    {
        if (_rpVrmAnimRotateAddKeys(dstRotateAnim, numRotationKeys))
        {
            dstRotationKeys = _rpVrmAnimRotateGetKeys(dstRotateAnim);
            if (dstRotationKeys)
            {
                memcpy(dstRotationKeys, srcRotationKeys,
                       numRotationKeys * sizeof(RpVRMLRotateKey));
            }
        }
    }

    RWRETURN(dstRotateAnim);
}

RwInt32
_rpVrmAnimRotateGetNumKeys(const RpVRMLAnimRotate * rotateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimRotateGetNumKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(rotateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (rotateAnim)
        {
            RWRETURN(rotateAnim->numRotationKeys);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(-1);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(-1);
}

RpVRMLRotateKey    *
_rpVrmAnimRotateGetKeys(const RpVRMLAnimRotate * rotateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimRotateGetKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(rotateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (rotateAnim)
        {
            RWRETURN(rotateAnim->rotationKeys);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLRotate *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLRotate *)NULL);
}

RpVRMLAnimRotate   *
_rpVrmAnimRotateAddKeys(RpVRMLAnimRotate * rotateAnim, RwInt32 numKeys)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimRotateAddKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(rotateAnim);
    RWASSERT(numKeys > 0);

    if (vrmlAnimModule.numInstances)
    {
        if (rotateAnim)
        {
            RpVRMLRotateKey    *rotateKeys;

            if (numKeys < 1)
            {
                RWRETURN((RpVRMLAnimRotate *)NULL);
            }

            rotateKeys = (RpVRMLRotateKey *)
                RwMalloc(sizeof(RpVRMLRotateKey) * numKeys);
            if (rotateKeys)
            {
                rotateAnim->numRotationKeys = numKeys;
                rotateAnim->rotationKeys = rotateKeys;
            }

            RWRETURN(rotateAnim);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimRotate *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimRotate *)NULL);
}

RpVRMLAnimRotate   *
_rpVrmAnimRotateSetState(RpVRMLAnimRotate * rotateAnim,
                         RpVRMLAnimState * rotateCntrl)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimRotateSetState"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(rotateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (rotateAnim)
        {
            rotateAnim->rotateCntrl = rotateCntrl;

            RWRETURN(rotateAnim);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimRotate *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimRotate *)NULL);
}

RpVRMLAnimState    *
_rpVrmAnimRotateGetState(const RpVRMLAnimRotate * rotateAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimRotateGetState"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(rotateAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (rotateAnim)
        {
            RWRETURN(rotateAnim->rotateCntrl);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimState *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimState *)NULL);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   RpVRMLAnimScale functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RpVRMLAnimScale    *
_rpVrmAnimScaleCreate(void)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimScaleCreate"));
    RWASSERT(vrmlAnimModule.numInstances);

    if (vrmlAnimModule.numInstances)
    {
        RpVRMLAnimScale    *scaleAnim;

        scaleAnim =
            (RpVRMLAnimScale *) RwMalloc(sizeof(RpVRMLAnimScale));
        if (scaleAnim)
        {
            scaleAnim->scaleCntrl = (RpVRMLAnimState *)NULL;
            scaleAnim->numScaleKeys = 0;
            scaleAnim->scaleKeys = (RwV3d *)NULL;
        }

        RWRETURN(scaleAnim);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimScale *)NULL);
}

RwBool
_rpVrmAnimScaleDestroy(RpVRMLAnimScale * scaleAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimScaleDestroy"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(scaleAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (scaleAnim)
        {
            if (scaleAnim->scaleCntrl)
            {
                _rpVrmAnimStateDestroy(scaleAnim->scaleCntrl);
            }

            if (scaleAnim->scaleKeys)
            {
                RwFree(scaleAnim->scaleKeys);
            }

            RwFree(scaleAnim);

            RWRETURN(TRUE);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(FALSE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

static RpVRMLAnimScale *
VRMLAnimScaleCopy(const RpVRMLAnimScale * srcScaleAnim)
{
    RpVRMLAnimScale    *dstScaleAnim;
    RwInt32             numScaleKeys;
    RpVRMLScaleKey     *dstScaleKeys, *srcScaleKeys;

    RWFUNCTION(RWSTRING("VRMLAnimScaleCopy"));
    RWASSERT(srcScaleAnim);

    dstScaleAnim = _rpVrmAnimScaleCreate();
    if (!dstScaleAnim)
    {
        RWRETURN((RpVRMLAnimScale *)NULL);
    }

    if (srcScaleAnim->scaleCntrl)
    {
        dstScaleAnim->scaleCntrl =
            VRMLAnimStateCopy(srcScaleAnim->scaleCntrl);
    }

    numScaleKeys = srcScaleAnim->numScaleKeys;
    srcScaleKeys = srcScaleAnim->scaleKeys;
    if (numScaleKeys && srcScaleKeys)
    {
        if (_rpVrmAnimScaleAddKeys(dstScaleAnim, numScaleKeys))
        {
            dstScaleKeys = _rpVrmAnimScaleGetKeys(dstScaleAnim);
            if (dstScaleKeys)
            {
                memcpy(dstScaleKeys, srcScaleKeys, numScaleKeys *
                       sizeof(RpVRMLScaleKey));
            }
        }
    }

    RWRETURN(dstScaleAnim);
}

RwInt32
_rpVrmAnimScaleGetNumKeys(const RpVRMLAnimScale * scaleAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimScaleGetNumKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(scaleAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (scaleAnim)
        {
            RWRETURN(scaleAnim->numScaleKeys);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(-1);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(-1);
}

RpVRMLScaleKey     *
_rpVrmAnimScaleGetKeys(const RpVRMLAnimScale * scaleAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimScaleGetKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(scaleAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (scaleAnim)
        {
            RWRETURN(scaleAnim->scaleKeys);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwV3d *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwV3d *)NULL);
}

RpVRMLAnimScale    *
_rpVrmAnimScaleAddKeys(RpVRMLAnimScale * scaleAnim, RwInt32 numKeys)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimScaleAddKeys"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(scaleAnim);
    RWASSERT(numKeys > 0);

    if (vrmlAnimModule.numInstances)
    {
        if (scaleAnim)
        {
            RpVRMLScaleKey     *scaleKeys;

            if (numKeys < 1)
            {
                RWRETURN((RpVRMLAnimScale *)NULL);
            }

            scaleKeys =
                (RpVRMLScaleKey *) RwMalloc(sizeof(RpVRMLScaleKey) *
                                            numKeys);
            if (scaleKeys)
            {
                scaleAnim->numScaleKeys = numKeys;
                scaleAnim->scaleKeys = scaleKeys;
            }

            RWRETURN(scaleAnim);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimScale *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimScale *)NULL);
}

RpVRMLAnimScale    *
_rpVrmAnimScaleSetState(RpVRMLAnimScale * scaleAnim,
                        RpVRMLAnimState * scaleCntrl)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimScaleSetState"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(scaleAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (scaleAnim)
        {
            scaleAnim->scaleCntrl = scaleCntrl;

            RWRETURN(scaleAnim);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimScale *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimScale *)NULL);
}

RpVRMLAnimState    *
_rpVrmAnimScaleGetState(const RpVRMLAnimScale * scaleAnim)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimScaleGetState"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(scaleAnim);

    if (vrmlAnimModule.numInstances)
    {
        if (scaleAnim)
        {
            RWRETURN(scaleAnim->scaleCntrl);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(FALSE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   RpVRMLAnimState functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RpVRMLAnimState    *
_rpVrmAnimStateCreate(RwInt32 numInterpolators)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimStateCreate"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(numInterpolators > 0);

    if (vrmlAnimModule.numInstances)
    {
        RpVRMLAnimState    *animCntrl;
        RpVRMLAnimInterpolator *interpolators;

        if (numInterpolators < 1)
        {
            RWRETURN((RpVRMLAnimState *)NULL);
        }

        animCntrl =
            (RpVRMLAnimState *) RwMalloc(sizeof(RpVRMLAnimState));
        if (!animCntrl)
        {
            RWRETURN((RpVRMLAnimState *)NULL);
        }

        interpolators = (RpVRMLAnimInterpolator *)
            RwMalloc(numInterpolators * sizeof(RpVRMLAnimInterpolator));
        if (!interpolators)
        {
            RwFree(animCntrl);

            RWRETURN((RpVRMLAnimState *)NULL);
        }
        /* Clear the interpolators */
        memset(interpolators, 0,
               numInterpolators * sizeof(RpVRMLAnimInterpolator));

        animCntrl->numInterpolators = numInterpolators;
        animCntrl->interp = interpolators;
        animCntrl->position = (RwReal) (0.0);
        animCntrl->fpAnimCntrlCB = DefaultFrameAnimCB;
        animCntrl->allInterps = interpolators;

        RWRETURN(animCntrl);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

RwBool
_rpVrmAnimStateDestroy(RpVRMLAnimState * animCntrl)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimStateDestroy"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(animCntrl);

    if (vrmlAnimModule.numInstances)
    {
        if (animCntrl)
        {
            if (animCntrl->allInterps)
            {
                RwFree(animCntrl->allInterps);
            }

            RwFree(animCntrl);

            RWRETURN(TRUE);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(FALSE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

RpVRMLAnimInterpolator *
_rpVrmAnimStateAddInterpolator(RpVRMLAnimState * animCntrl,
                               RwInt32 interpNum, RwInt16 startKey,
                               RwInt16 endKey, RwReal duration)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimStateAddInterpolator"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(animCntrl);

    if (vrmlAnimModule.numInstances)
    {
        if (animCntrl)
        {
            RpVRMLAnimInterpolator *interpolator;

            if ((interpNum < 0)
                || (interpNum > animCntrl->numInterpolators))
            {
                RWASSERT(FALSE);
                RWRETURN((RpVRMLAnimInterpolator *)NULL);
            }

            interpolator = &animCntrl->allInterps[interpNum];
            interpolator->flags = 0;
            interpolator->startKeyFrame = startKey;
            interpolator->endKeyFrame = endKey;
            interpolator->time = duration;
            /* stop division by zero */
            if (duration != (RwReal) (0.0))
            {
                interpolator->recipTime = ((RwReal) (1.0)) / (duration);
            }
            else
            {
                interpolator->recipTime = (RwReal) (0.0);
            }

            if (interpNum == (animCntrl->numInterpolators - 1))
            {
                interpolator->next = &animCntrl->allInterps[0];
            }
            else
            {
                interpolator->next = interpolator + 1;
            }

            RWRETURN(interpolator);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RpVRMLAnimInterpolator *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpVRMLAnimInterpolator *)NULL);
}

RwBool
_rpVrmAnimStateSetCallBack(RpVRMLAnimState * animCntrl,
                           RpVRMLAnimStateCallBack animCB)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimStateSetCallBack"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(animCntrl);
    RWASSERT(animCB);

    if (vrmlAnimModule.numInstances)
    {
        if (animCntrl && animCB)
        {
            animCntrl->fpAnimCntrlCB = animCB;

            RWRETURN(TRUE);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN(FALSE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

RpVRMLAnimStateCallBack
_rpVrmAnimStateGetCallBack(const RpVRMLAnimState * animCntrl)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimStateGetCallBack"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(animCntrl);

    if (vrmlAnimModule.numInstances)
    {
        if (animCntrl)
        {
            RWRETURN(animCntrl->fpAnimCntrlCB);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((float (*)(RpVRMLAnimState *, float))NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((float (*)(RpVRMLAnimState *, float))NULL);
}

RwFrame            *
_rpVrmlFrameAnimAddTime(RwFrame * frame, RwReal time)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimAddTime"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(frame);
            RpVRMLAnimTransformState *matrixComp =
                &frameAnim->matrixComp;
            RwBool              updateMatrix = FALSE;

            /* translation */
            if (frameAnim->animTranslate)
            {
                FrameVRMLAnimAddTranslateDelta(frame, time);
                updateMatrix = TRUE;
            }

            /* rotation */
            if (frameAnim->animRotate)
            {
                FrameVRMLAnimAddRotateDelta(frame, time);
                updateMatrix = TRUE;
            }

            /* scale */
            if (frameAnim->animScale)
            {
                FrameVRMLAnimAddScaleDelta(frame, time);
                updateMatrix = TRUE;
            }

            /* scaleOrient */
            if (frameAnim->animScaleOrient)
            {
                FrameVRMLAnimAddScaleOrientDelta(frame, time);
                updateMatrix = TRUE;
            }

            /* compose the new matrix */
            if (updateMatrix)
            {
                RwMatrix           *matrix;

                matrix = RwFrameGetMatrix(frame);
                _rpVrmAnimGetMatrix(matrixComp, matrix);

                /* And mark the hierarchy as dirty */
                RwFrameUpdateObjects(frame);
            }

            RWRETURN(frame);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwFrame *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwFrame *)NULL);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Matrix functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

static void
_matrixSetTranslation(RwMatrix * matrix, RwV3d * translation,
                      RwBool inv)
{
    RWFUNCTION(RWSTRING("_matrixSetTranslation"));
    RWASSERT(matrix);
    RWASSERT(translation);

    if (matrix && translation)
    {
        RwV3d               newTranslation;

        newTranslation = *translation;
        if (inv)
        {
            RwV3dScale(&newTranslation, &newTranslation,
                       (RwReal) (-1.0));
        }
        RwMatrixTranslate(matrix, &newTranslation, rwCOMBINEPOSTCONCAT);
    }

    RWRETURNVOID();
}

static void
_matrixSetRotation(RwMatrix * matrix, RwMatrix * rotation, RwBool inv)
{
    RWFUNCTION(RWSTRING("_matrixSetRotation"));
    RWASSERT(matrix);
    RWASSERT(rotation);

    if (matrix && rotation)
    {
        if (inv)
        {
            RwMatrix            invRotation;

            RwMatrixInvert(&invRotation, rotation);
            RwMatrixTransform(matrix, &invRotation,
                              rwCOMBINEPOSTCONCAT);
        }
        else
        {
            RwMatrixTransform(matrix, rotation, rwCOMBINEPOSTCONCAT);
        }
    }

    RWRETURNVOID();
}

static void
_matrixSetScale(RwMatrix * matrix, RwV3d * scale, RwBool inv)
{
    RWFUNCTION(RWSTRING("_matrixSetScale"));
    RWASSERT(matrix);
    RWASSERT(scale);

    if (matrix && scale)
    {
        RwV3d               newScale;

        newScale = *scale;
        if (inv)
        {
            RwV3dScale(&newScale, &newScale, (RwReal) (-1.0));
        }
        RwMatrixScale(matrix, &newScale, rwCOMBINEPOSTCONCAT);
    }

    RWRETURNVOID();
}

RwMatrix           *
_rpVrmAnimGetMatrix(RpVRMLAnimTransformState * compMatrix,
                    RwMatrix * matrix)
{
    RWFUNCTION(RWSTRING("_rpVrmAnimGetMatrix"));
    RWASSERT(compMatrix);
    RWASSERT(matrix);

    if (compMatrix && matrix)
    {
        RwMatrixSetIdentity(matrix);

        /* P' = T * C * R * SR * S * -SR * -C * P */

        _matrixSetTranslation(matrix, &compMatrix->offset, TRUE);
        _matrixSetRotation(matrix, &compMatrix->scaleOrientMat, TRUE);
        _matrixSetScale(matrix, &compMatrix->scale, FALSE);
        _matrixSetRotation(matrix, &compMatrix->scaleOrientMat, FALSE);
        _matrixSetRotation(matrix, &compMatrix->rotationMat, FALSE);
        _matrixSetTranslation(matrix, &compMatrix->offset, FALSE);
        _matrixSetTranslation(matrix, &compMatrix->translation, FALSE);

        RWRETURN(matrix);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RwMatrix *)NULL);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Interpolator/Keyframe optimization code 

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

static rpVRMLInterpInfo **
VRMLInterpInfoGetCycle(rpVRMLInterpInfo * interpInfo,
                       rpVRMLInterpInfo ** interpInfoCycle,
                       RwInt32 cycleID)
{
    RWFUNCTION(RWSTRING("VRMLInterpInfoGetCycle"));
    RWASSERT(interpInfo);
    RWASSERT(interpInfoCycle);

    if (interpInfo && interpInfoCycle)
    {
        rpVRMLInterpInfo  **cycle = interpInfoCycle;

        while (interpInfo->visitedBy != cycleID)
        {
            interpInfo->visitedBy = cycleID;

            *cycle = interpInfo;
            cycle++;
            interpInfo = interpInfo->next;
        }

        /* mark the end of the cycle */
        *cycle = (rpVRMLInterpInfo *)NULL;

        RWRETURN(interpInfoCycle);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((rpVRMLInterpInfo **)NULL);
}

static              RwBool
TranslateKeyCompare(RpVRMLTranslateKey * keyA,
                    RpVRMLTranslateKey * keyB, RwReal delta)
{
    RWFUNCTION(RWSTRING("TranslateKeyCompare"));
    RWASSERT(keyA);
    RWASSERT(keyB);

    if (RwRealAbs((keyA->x) - (keyB->x)) > delta)
    {
        RWRETURN(FALSE);
    }

    if (RwRealAbs((keyA->y) - (keyB->y)) > delta)
    {
        RWRETURN(FALSE);
    }

    if (RwRealAbs((keyA->z) - (keyB->z)) > delta)
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

static              RwBool
VRMLInterpInfoPosSpanTest(rpVRMLInterpInfo ** interpSpanStart, RwInt32
                          interval, RpVRMLTranslateKey * keys,
                          RwReal delta)
{
    RWFUNCTION(RWSTRING("VRMLInterpInfoPosSpanTest"));
    RWASSERT(interpSpanStart);
    RWASSERT(keys);

    if (interpSpanStart && keys)
    {
        RwInt32             keyIndex, i;
        RpVRMLTranslateKey  blendKey, *startKey, *endKey;
        RwReal              along;

        keyIndex = interpSpanStart[0]->interp->startKeyFrame;
        startKey = &keys[keyIndex];

        keyIndex = interpSpanStart[interval]->interp->endKeyFrame;
        endKey = &keys[keyIndex];

        along = (RwReal) (0.0);

        for (i = 0; i < interval; i++)
        {
            RpVRMLTranslateKey *realKey;

            /* get the blend key */
            along += interpSpanStart[i]->interp->time;
            TranslateKeyBlend(&blendKey, startKey, endKey, along);

            /* get the real key */
            keyIndex = interpSpanStart[i]->interp->endKeyFrame;
            realKey = &keys[keyIndex];

            /* compare real and blend */
            if (!TranslateKeyCompare(realKey, &blendKey, delta))
            {
                RWRETURN(FALSE);
            }
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
RotationKeyCompare(RpVRMLRotateKey * keyA, RpVRMLRotateKey * keyB,
                   RwReal delta)
{
    RWFUNCTION(RWSTRING("RotationKeyCompare"));
    RWASSERT(keyA);
    RWASSERT(keyB);

    if (RwRealAbs((keyA->axis.x) - (keyB->axis.x)) > delta)
    {
        RWRETURN(FALSE);
    }

    if (RwRealAbs((keyA->axis.y) - (keyB->axis.y)) > delta)
    {
        RWRETURN(FALSE);
    }

    if (RwRealAbs((keyA->axis.z) - (keyB->axis.z)) > delta)
    {
        RWRETURN(FALSE);
    }

    if (RwRealAbs((keyA->angle) - (keyB->angle)) > delta)
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

static              RwBool
VRMLInterpInfoOrientationSpanTest(rpVRMLInterpInfo ** interpSpanStart,
                                  RwInt32 interval,
                                  RpVRMLRotateKey * keys, RwReal delta)
{
    RWFUNCTION(RWSTRING("VRMLInterpInfoOrientationSpanTest"));
    RWASSERT(interpSpanStart);
    RWASSERT(keys);

    if (interpSpanStart && keys)
    {
        RtSlerp             slerp;
        RwMatrix           *rotate;
        RwBool              useSlerp;
        RwInt32             keyIndex, i;
        RpVRMLRotateKey     blendKey, *startKey, *endKey;
        RwReal              along;

        rotate = RwMatrixCreate();
        if (!rotate)
        {
            RWRETURN(FALSE);
        }

        keyIndex = interpSpanStart[0]->interp->startKeyFrame;
        startKey = &keys[keyIndex];

        keyIndex = interpSpanStart[interval]->interp->endKeyFrame;
        endKey = &keys[keyIndex];

        /* setup the slerp */
        if ((startKey->axis.x != endKey->axis.x) ||
            (startKey->axis.y != endKey->axis.y) ||
            (startKey->axis.z != endKey->axis.z) ||
            (startKey->angle != endKey->angle))
        {
            /* this horrible bit of code is to get around a bug in the slerp code */
            useSlerp = TRUE;
            SetupSlerp(&slerp, startKey, endKey);
        }
        else
        {
            useSlerp = FALSE;
            blendKey = *startKey;
        }

        along = (RwReal) (0.0);

        for (i = 0; i < interval; i++)
        {
            RpVRMLRotateKey    *realKey;

            /* get the blend key */
            along += interpSpanStart[i]->interp->time;
            if (useSlerp)
            {
                RwV3d               axis, center;
                RwReal              angle;

                /* this is extremly inefficient and needs to be optimized */
                RtSlerpGetMatrix(&slerp, rotate, along);
                RwMatrixQueryRotate(rotate, &axis, &angle, &center);
                angle = (angle) * ((PI) / ((RwReal) (180.0)));

                blendKey.axis = axis;
                blendKey.angle = angle;
            }

            /* get the real key */
            keyIndex = interpSpanStart[i]->interp->endKeyFrame;
            realKey = &keys[keyIndex];

            /* compare real and blend */
            if (!RotationKeyCompare(realKey, &blendKey, delta))
            {
                RwMatrixDestroy(rotate);
                RWRETURN(FALSE);
            }
        }

        RwMatrixDestroy(rotate);
        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwInt32
VRMLInterpInfoFindSpan(rpVRMLInterpInfo ** interpSpanStart,
                       RwInt32 posA, RwInt32 posB, void *keys,
                       RwReal delta,
                       rpVRMLInterpInfoSpanTestCallBack spanCB)
{
    RwInt32             mid;

    RWFUNCTION(RWSTRING("VRMLInterpInfoFindSpan"));
    RWASSERT(interpSpanStart);

    /* takes nlog(n) time */

    /* have we homed in on the soloution */
    if (posA == posB)
    {
        RWRETURN(posA);
    }

    if ((posB - posA) == 1)
    {
        if (spanCB(interpSpanStart, posB, keys, delta))
        {
            RWRETURN(posB);
        }
        else
        {
            RWRETURN(posA);
        }
    }

    /* find the mid point */
    mid = posA + ((posB - posA) / 2);

    /* is the soloution in the upper bound */
    if (spanCB(interpSpanStart, mid, keys, delta))
    {
        RWRETURN(VRMLInterpInfoFindSpan(interpSpanStart, mid, posB,
                                        keys, delta, spanCB));
    }

    /* else must be in the lower bound */
    RWRETURN(VRMLInterpInfoFindSpan(interpSpanStart, posA, mid, keys,
                                    delta, spanCB));
}

static RpVRMLAnimInterpolator *
VRMLInterpInfoCreateNewInterpolator(rpVRMLInterpInfo ** interpInfoSpan,
                                    RwSList * interpList, RwInt32 span)
{
    RWFUNCTION(RWSTRING("VRMLInterpInfoCreateNewInterpolator"));
    RWASSERT(interpInfoSpan);
    RWASSERT(interpList);

    if (interpInfoSpan && interpList)
    {
        RpVRMLAnimInterpolator *interp;
        RpVRMLAnimInterpolator *first, *last;
        RwInt32             i;
        RwReal              time = (RwReal) (0.0);

        interp =
            (RpVRMLAnimInterpolator *) rwSListGetNewEntry(interpList);
        if (!interp)
        {
            RWRETURN((RpVRMLAnimInterpolator *)NULL);
        }

        first = interpInfoSpan[0]->interp;
        last = interpInfoSpan[span]->interp;

        interp->startKeyFrame = first->startKeyFrame;
        interp->endKeyFrame = last->endKeyFrame;

        for (i = 0; i <= span; i++)
        {
            time += interpInfoSpan[i]->interp->time;
        }

        interp->time = time;
        if (time != (RwReal) (0.0))
        {
            interp->recipTime = ((RwReal) (1.0)) / (time);
        }
        else
        {
            interp->recipTime = (RwReal) (0.0);
        }

        interp->flags = 0;

        interp->next = (RpVRMLAnimInterpolator *)NULL;

        RWRETURN(interp);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RpVRMLAnimInterpolator *)NULL);
}

static RpVRMLAnimState *
VRMLAnimStateOptimizeCycle(RpVRMLAnimState * animState,
                           rpVRMLInterpInfo ** interpInfoSpan,
                           void *keys, RwReal delta,
                           rpVRMLInterpInfoSpanTestCallBack spanCB)
{
    RWFUNCTION(RWSTRING("VRMLAnimStateOptimizeCycle"));
    RWASSERT(animState);
    RWASSERT(interpInfoSpan);
    RWASSERT(keys);

    if (animState && interpInfoSpan && keys)
    {
        RwSList            *interpList;
        RwInt32             i, numNewInterps;
        RwInt32             start, end, interval, along;
        RwInt32             maxSpan = 0;
        RpVRMLAnimInterpolator *newInterps;

        interpList = (RwSList *)
            rwSListCreate(sizeof(RpVRMLAnimInterpolator));
        if (!interpList)
        {
            RWRETURN((RpVRMLAnimState *)NULL);
        }

        start = end = interval = along = 0;
        while (interpInfoSpan[interval])
        {
            interval++;
        }

        end = interval - 1;

        while (along < interval)
        {
            RwInt32             newSpan;

            newSpan = VRMLInterpInfoFindSpan(&interpInfoSpan[along], 0,
                                             end - along, keys, delta,
                                             spanCB);

            if (!VRMLInterpInfoCreateNewInterpolator
                (&interpInfoSpan[along], interpList, newSpan))
            {
                rwSListDestroy(interpList);

                RWRETURN((RpVRMLAnimState *)NULL);
            }

            if (newSpan > maxSpan)
            {
                maxSpan = newSpan;
            }

            along += newSpan + 1;
        }

        numNewInterps = rwSListGetNumEntries(interpList);
        {
            RpVRMLAnimInterpolator *tempInterps;
            RwInt32             size;

            tempInterps = (RpVRMLAnimInterpolator *)
                rwSListGetArray(interpList);
            size = sizeof(RpVRMLAnimInterpolator) * numNewInterps;

            newInterps = (RpVRMLAnimInterpolator *) RwMalloc(size);
            if (!newInterps)
            {
                RWRETURN((RpVRMLAnimState *)NULL);
            }
            memcpy(newInterps, tempInterps, size);
            rwSListDestroy(interpList);
        }

        /* set up the anim cycle :Assumes one big cycle */
        for (i = 0; i < numNewInterps; i++)
        {
            if (i != numNewInterps - 1)
            {
                newInterps[i].next = &newInterps[i + 1];
            }
            else
            {
                newInterps[i].next = &newInterps[0];
            }
        }

        RwFree(animState->allInterps);
        animState->allInterps = newInterps;
        animState->interp = newInterps;
        animState->numInterpolators = numNewInterps;
        animState->position = (RwReal) (0.0);

        RWRETURN(animState);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RpVRMLAnimState *)NULL);
}

static RpVRMLAnimState *
VRMLAnimStateOptimize(RpVRMLAnimState * animState, void *keys, RwReal
                      delta, rpVRMLInterpInfoSpanTestCallBack spanCB)
{
    RWFUNCTION(RWSTRING("VRMLAnimStateOptimize"));
    RWASSERT(animState);
    RWASSERT(keys);

    if (animState && keys)
    {
        rpVRMLInterpInfo   *interpInfoArray;
        rpVRMLInterpInfo  **interpInfoCycle;
        RpVRMLAnimInterpolator *interpolators;
        RwInt32             i, numInterpolators;

        numInterpolators = animState->numInterpolators;
        interpInfoArray = (rpVRMLInterpInfo *)
            RwMalloc(sizeof(rpVRMLInterpInfo) * numInterpolators);
        if (!interpInfoArray)
        {
            RWRETURN((RpVRMLAnimState *)NULL);
        }

        interpInfoCycle =
            (rpVRMLInterpInfo * *)RwMalloc(sizeof(rpVRMLInterpInfo *) *
                                           (numInterpolators + 1));
        if (!interpInfoCycle)
        {
            RwFree(interpInfoArray);

            RWRETURN((RpVRMLAnimState *)NULL);
        }

        interpolators = animState->allInterps;

        /* setup the interpInfoArray */
        for (i = 0; i < numInterpolators; i++)
        {
            interpInfoArray[i].newIndex = -1;
            interpInfoArray[i].refCntr = 0;
            interpInfoArray[i].optimized = FALSE;
            interpInfoArray[i].loopStart = FALSE;
            interpInfoArray[i].cut = FALSE;
            interpInfoArray[i].visitedBy = -1;
            interpInfoArray[i].interp = &interpolators[i];
            interpInfoArray[i].next = (rpVRMLInterpInfo *)NULL;
        }

        /* compute the ref counts */
        for (i = 0; i < numInterpolators; i++)
        {
            RpVRMLAnimInterpolator *currentInterp, *nextInterp;

            currentInterp = interpInfoArray[i].interp;
            nextInterp = currentInterp->next;
            if (nextInterp)
            {
                RwInt32             interpInd;

                interpInd = nextInterp - interpolators;

                interpInfoArray[interpInd].refCntr++;

                interpInfoArray[i].next = &interpInfoArray[interpInd];

                if (currentInterp->endKeyFrame !=
                    nextInterp->startKeyFrame)
                {
                    interpInfoArray[interpInd].cut = TRUE;
                }
            }
        }

        /* get each animation cycle and optimize it */
        /* for now assume the animation forms one big loop */
        /* TODO: update to cope with multiple animation cycles */

        /* check we have a single loop */
        for (i = 0; i < numInterpolators; i++)
        {
            if (interpInfoArray[i].refCntr != 1)
            {
                RwFree(interpInfoArray);
                RwFree(interpInfoCycle);

                RWRETURN((RpVRMLAnimState *)NULL);
            }
        }

#if 0
        /* find any run-in's */
        for (i = 0; i < numInterpolators; i++)
        {
            if ((interpInfoArray[i].visitedBy == -1) &&
                (interpInfoArray[i].refCntr == 0))
            {
                VRMLInterpInfoGetCycle(&interpInfoCycle[i], i,
                                       numInterpolators);
            }
        }
#endif /* 0 */

        VRMLInterpInfoGetCycle(interpInfoArray, interpInfoCycle, 1);

        VRMLAnimStateOptimizeCycle(animState, interpInfoCycle, keys,
                                   delta, spanCB);

        RwFree(interpInfoArray);
        RwFree(interpInfoCycle);

        RWRETURN(animState);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RpVRMLAnimState *)NULL);
}

static rpVRMLKeyInfo *
VRMLKeyInfoArrayCreate(RpVRMLAnimState * animState, RwInt32 numKeys,
                       RwInt32 * numNewKeys)
{
    RWFUNCTION(RWSTRING("VRMLKeyInfoArrayCreate"));
    RWASSERT(animState);
    RWASSERT(numNewKeys);

    if (animState && numNewKeys)
    {
        RpVRMLAnimInterpolator *interpolators;
        rpVRMLKeyInfo      *keyInfo;
        RwInt32             i, numInterpolators;
        RwInt16             newKeyIndex;

        /* create the keyInfo array */
        keyInfo =
            (rpVRMLKeyInfo *) RwMalloc(sizeof(rpVRMLKeyInfo) * numKeys);
        if (!keyInfo)
        {
            RWRETURN((rpVRMLKeyInfo *)NULL);
        }

        /* intialize te keyInfo array */
        for (i = 0; i < numKeys; i++)
        {
            keyInfo[i].used = FALSE;
            keyInfo[i].newIndex = -1;
        }

        /* find out which keys are used */
        numInterpolators = animState->numInterpolators;
        interpolators = animState->allInterps;
        for (i = 0; i < numInterpolators; i++)
        {
            RwInt16             startKey, endKey;

            startKey = interpolators[i].startKeyFrame;
            endKey = interpolators[i].endKeyFrame;

            keyInfo[startKey].used = TRUE;
            keyInfo[endKey].used = TRUE;
        }

        /* reindex the used keys */
        newKeyIndex = 0;
        for (i = 0; i < numKeys; i++)
        {
            if (keyInfo[i].used)
            {
                keyInfo[i].newIndex = newKeyIndex;
                newKeyIndex++;
            }
        }

        *numNewKeys = newKeyIndex;

        RWRETURN(keyInfo);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((rpVRMLKeyInfo *)NULL);
}

static              RwBool
VRMLKeyInfoArrayDestroy(rpVRMLKeyInfo * keyInfo)
{
    RWFUNCTION(RWSTRING("VRMLKeyInfoArrayDestroy"));
    RWASSERT(keyInfo);

    if (keyInfo)
    {
        RwFree(keyInfo);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static RpVRMLAnimInterpolator *
VRMLAnimInterpolatorsRemap(RpVRMLAnimInterpolator * interps, RwInt32
                           numInterpolators, rpVRMLKeyInfo * keyInfo)
{
    RWFUNCTION(RWSTRING("VRMLAnimInterpolatorsRemap"));
    RWASSERT(interps);
    RWASSERT(keyInfo);

    if (interps && keyInfo)
    {
        RwInt32             i;
        RpVRMLAnimInterpolator *interpolators;

        interpolators = interps;

        /* remap the interpolators */
        for (i = 0; i < numInterpolators; i++)
        {
            RwInt16             startKey, endKey;
            RwInt16             newStartKey, newEndKey;

            startKey = interps->startKeyFrame;
            endKey = interps->endKeyFrame;

            newStartKey = keyInfo[startKey].newIndex;
            newEndKey = keyInfo[endKey].newIndex;

            interps->startKeyFrame = newStartKey;
            interps->endKeyFrame = newEndKey;

            interps++;
        }

        RWRETURN(interpolators);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RpVRMLAnimInterpolator *)NULL);
}

static RpVRMLAnimTranslate *
VRMLAnimTranslateOptimizeKeys(RpVRMLAnimTranslate * animTrans)
{
    RWFUNCTION(RWSTRING("VRMLAnimTranslateOptimizeKeys"));
    RWASSERT(animTrans);

    if (animTrans)
    {
        RpVRMLAnimState    *animState;
        RwInt32             numKeys, numNewKeys;
        rpVRMLKeyInfo      *keyInfo;

        animState = _rpVrmAnimTranslateGetState(animTrans);
        numKeys = _rpVrmAnimTranslateGetNumKeys(animTrans);

        keyInfo =
            VRMLKeyInfoArrayCreate(animState, numKeys, &numNewKeys);
        if (!keyInfo)
        {
            RWRETURN((RpVRMLAnimTranslate *)NULL);
        }

        /* create new key array */
        if (numNewKeys < numKeys)
        {
            RpVRMLAnimInterpolator *interpolators;
            RwInt32             i, numInterpolators;
            RpVRMLTranslateKey *keys, *newKeys;

            numInterpolators = animState->numInterpolators;
            interpolators = animState->allInterps;

            /* create the new keys */
            keys = _rpVrmAnimTranslateGetKeys(animTrans);
            newKeys = (RpVRMLTranslateKey *) RwMalloc(sizeof
                                                      (RpVRMLTranslateKey)
                                                      * numNewKeys);
            if (!newKeys)
            {
                VRMLKeyInfoArrayDestroy(keyInfo);

                RWRETURN((RpVRMLAnimTranslate *)NULL);
            }

            for (i = 0; i < numKeys; i++)
            {
                if (keyInfo[i].used)
                {
                    RwInt16             newKeyIndex;

                    newKeyIndex = keyInfo[i].newIndex;
                    newKeys[newKeyIndex] = keys[i];
                }
            }

            /* remap the interpolators */
            VRMLAnimInterpolatorsRemap(interpolators, numInterpolators,
                                       keyInfo);

            /* assign the new keys */
            animTrans->translationKeys = newKeys;
            animTrans->numTranslationKeys = numNewKeys;

            /* free the old keys */
            RwFree(keys);
        }

        VRMLKeyInfoArrayDestroy(keyInfo);

        RWRETURN(animTrans);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RpVRMLAnimTranslate *)NULL);
}

static RpVRMLAnimRotate *
VRMLAnimRotateOptimizeKeys(RpVRMLAnimRotate * animRotate)
{
    RWFUNCTION(RWSTRING("VRMLAnimRotateOptimizeKeys"));
    RWASSERT(animRotate);

    if (animRotate)
    {
        RpVRMLAnimState    *animState;
        RwInt32             numKeys, numNewKeys;
        rpVRMLKeyInfo      *keyInfo;

        animState = _rpVrmAnimRotateGetState(animRotate);
        numKeys = _rpVrmAnimRotateGetNumKeys(animRotate);

        keyInfo =
            VRMLKeyInfoArrayCreate(animState, numKeys, &numNewKeys);
        if (!keyInfo)
        {
            RWRETURN((RpVRMLAnimRotate *)NULL);
        }

        /* create new key array */
        if (numNewKeys < numKeys)
        {
            RpVRMLAnimInterpolator *interpolators;
            RwInt32             i, numInterpolators;
            RpVRMLRotateKey    *keys, *newKeys;

            numInterpolators = animState->numInterpolators;
            interpolators = animState->allInterps;

            /* create the new keys */
            keys = _rpVrmAnimRotateGetKeys(animRotate);
            newKeys = (RpVRMLRotateKey *) RwMalloc(sizeof
                                                   (RpVRMLRotateKey) *
                                                   numNewKeys);
            if (!newKeys)
            {
                VRMLKeyInfoArrayDestroy(keyInfo);

                RWRETURN((RpVRMLAnimRotate *)NULL);
            }

            for (i = 0; i < numKeys; i++)
            {
                if (keyInfo[i].used)
                {
                    RwInt16             newKeyIndex;

                    newKeyIndex = keyInfo[i].newIndex;
                    newKeys[newKeyIndex] = keys[i];
                }
            }

            /* remap the interpolators */
            VRMLAnimInterpolatorsRemap(interpolators, numInterpolators,
                                       keyInfo);

            /* assign the new keys */
            animRotate->rotationKeys = newKeys;
            animRotate->numRotationKeys = numNewKeys;

            /* free the old keys */
            RwFree(keys);
        }

        VRMLKeyInfoArrayDestroy(keyInfo);

        RWRETURN(animRotate);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RpVRMLAnimRotate *)NULL);
}

static RpVRMLAnimScale *
VRMLAnimScaleOptimizeKeys(RpVRMLAnimScale * animScale)
{
    RWFUNCTION(RWSTRING("VRMLAnimScaleOptimizeKeys"));
    RWASSERT(animScale);

    if (animScale)
    {
        RpVRMLAnimState    *animState;
        RwInt32             numKeys, numNewKeys;
        rpVRMLKeyInfo      *keyInfo;

        animState = _rpVrmAnimScaleGetState(animScale);
        numKeys = _rpVrmAnimScaleGetNumKeys(animScale);

        keyInfo =
            VRMLKeyInfoArrayCreate(animState, numKeys, &numNewKeys);
        if (!keyInfo)
        {
            RWRETURN((RpVRMLAnimScale *)NULL);
        }

        /* create new key array */
        if (numNewKeys < numKeys)
        {
            RpVRMLAnimInterpolator *interpolators;
            RwInt32             i, numInterpolators;
            RpVRMLScaleKey     *keys, *newKeys;

            numInterpolators = animState->numInterpolators;
            interpolators = animState->allInterps;

            /* create the new keys */
            keys = _rpVrmAnimScaleGetKeys(animScale);
            newKeys = (RpVRMLScaleKey *)
                RwMalloc(sizeof(RpVRMLScaleKey) * numNewKeys);
            if (!newKeys)
            {
                VRMLKeyInfoArrayDestroy(keyInfo);

                RWRETURN((RpVRMLAnimScale *)NULL);
            }

            for (i = 0; i < numKeys; i++)
            {
                if (keyInfo[i].used)
                {
                    RwInt16             newKeyIndex;

                    newKeyIndex = keyInfo[i].newIndex;
                    newKeys[newKeyIndex] = keys[i];
                }
            }

            /* remap the interpolators */
            VRMLAnimInterpolatorsRemap(interpolators, numInterpolators,
                                       keyInfo);

            /* assign the new keys */
            animScale->scaleKeys = newKeys;
            animScale->numScaleKeys = numNewKeys;

            /* free the old keys */
            RwFree(keys);
        }

        VRMLKeyInfoArrayDestroy(keyInfo);

        RWRETURN(animScale);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RpVRMLAnimScale *)NULL);
}

RwFrame            *
_rpVrmlFrameAnimOptimize(RwFrame * frame, RwReal delta)
{
    RWFUNCTION(RWSTRING("_rpVrmlFrameAnimOptimize"));
    RWASSERT(vrmlAnimModule.numInstances);
    RWASSERT(frame);

    if (vrmlAnimModule.numInstances)
    {
        if (frame)
        {
            RpVRMLAnimTranslate *animTrans;
            RpVRMLAnimRotate   *animRotate;
            RpVRMLAnimScale    *animScale;
            RpVRMLAnimRotate   *animScaleOrient;

            /* translation */
            animTrans = _rpVrmlFrameAnimGetTranslations(frame);
            if (animTrans)
            {
                RpVRMLAnimState    *animState;
                RpVRMLTranslateKey *keys;
                rpVRMLInterpInfoSpanTestCallBack spanCB;

                animState = _rpVrmAnimTranslateGetState(animTrans);
                keys = _rpVrmAnimTranslateGetKeys(animTrans);
                spanCB =
                    (rpVRMLInterpInfoSpanTestCallBack)
                    VRMLInterpInfoPosSpanTest;
                VRMLAnimStateOptimize(animState, (void *) keys, delta,
                                      spanCB);
                VRMLAnimTranslateOptimizeKeys(animTrans);
            }

            /* rotation */
            animRotate = _rpVrmlFrameAnimGetRotations(frame);
            if (animRotate)
            {
                RpVRMLAnimState    *animState;
                RpVRMLRotateKey    *keys;
                rpVRMLInterpInfoSpanTestCallBack spanCB;

                animState = _rpVrmAnimRotateGetState(animRotate);
                keys = _rpVrmAnimRotateGetKeys(animRotate);
                spanCB =
                    (rpVRMLInterpInfoSpanTestCallBack)
                    VRMLInterpInfoOrientationSpanTest;
                VRMLAnimStateOptimize(animState, (void *) keys, delta,
                                      spanCB);
                VRMLAnimRotateOptimizeKeys(animRotate);
            }

            /* sacle */
            animScale = _rpVrmlFrameAnimGetScales(frame);
            if (animScale)
            {
                RpVRMLAnimState    *animState;
                RpVRMLScaleKey     *keys;
                rpVRMLInterpInfoSpanTestCallBack spanCB;

                animState = _rpVrmAnimScaleGetState(animScale);
                keys = _rpVrmAnimScaleGetKeys(animScale);
                spanCB =
                    (rpVRMLInterpInfoSpanTestCallBack)
                    VRMLInterpInfoPosSpanTest;
                VRMLAnimStateOptimize(animState, (void *) keys, delta,
                                      spanCB);
                VRMLAnimScaleOptimizeKeys(animScale);
            }

            /* sacleOrient */
            animScaleOrient = _rpVrmlFrameAnimGetScaleOrients(frame);
            if (animScaleOrient)
            {
                RpVRMLAnimState    *animState;
                RpVRMLRotateKey    *keys;
                rpVRMLInterpInfoSpanTestCallBack spanCB;

                animState = _rpVrmAnimRotateGetState(animScaleOrient);
                keys = _rpVrmAnimRotateGetKeys(animScaleOrient);
                spanCB =
                    (rpVRMLInterpInfoSpanTestCallBack)
                    VRMLInterpInfoOrientationSpanTest;
                VRMLAnimStateOptimize(animState, (void *) keys, delta,
                                      spanCB);
                VRMLAnimRotateOptimizeKeys(animScaleOrient);
            }

            RWRETURN(frame);
        }

        RWERROR((E_RW_NULLP));
        RWRETURN((RwFrame *)NULL);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RwFrame *)NULL);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Plugin constructor/destructor functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

static void        *
VRMLAnimOpen(void *instance,
             RwInt32 __RWUNUSED__ offset, RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION(RWSTRING("VRMLAnimOpen"));
    RWASSERT(instance);

    /* one more module instance */
    vrmlAnimModule.numInstances++;

    RWRETURN(instance);
}

static void        *
VRMLAnimClose(void *instance,
              RwInt32 __RWUNUSED__ offset, RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION(RWSTRING("VRMLAnimClose"));
    RWASSERT(instance);

    /* one less module instance */
    vrmlAnimModule.numInstances--;

    RWRETURN(instance);
}

static void        *
FrameVRMLAnimCtor(void *object,
                  RwInt32 __RWUNUSED__ offsetInObject,
                  RwInt32 __RWUNUSED__ sizeInObject)
{
    RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(object);
    RpVRMLAnimTransformState *matrixComp;

    RWFUNCTION(RWSTRING("FrameVRMLAnimCtor"));
    RWASSERT(object);

    RWASSERT(((RwObject *) (object))->type == (rwFRAME));

    frameAnim->animTranslate = (RpVRMLAnimTranslate *)NULL;
    frameAnim->animRotate = (RpVRMLAnimRotate *)NULL;
    frameAnim->animScale = (RpVRMLAnimScale *)NULL;
    frameAnim->animScaleOrient = (RpVRMLAnimRotate *)NULL;

    /* matrix */
    matrixComp = &frameAnim->matrixComp;
    /* offset */
    matrixComp->offset.x = (RwReal) (0.0);
    matrixComp->offset.y = (RwReal) (0.0);
    matrixComp->offset.z = (RwReal) (0.0);
    /* rotation */
    RwMatrixSetIdentity(&matrixComp->rotationMat);
    /* translation */
    matrixComp->translation.x = (RwReal) (0.0);
    matrixComp->translation.y = (RwReal) (0.0);
    matrixComp->translation.z = (RwReal) (0.0);
    /* scale */
    matrixComp->scale.x = (RwReal) (1.0);
    matrixComp->scale.y = (RwReal) (1.0);
    matrixComp->scale.z = (RwReal) (1.0);
    /* scale orientation */
    RwMatrixSetIdentity(&matrixComp->scaleOrientMat);

    frameAnim->name = (char *)NULL;

    RWRETURN(object);
}

static void        *
FrameVRMLAnimDtor(void *object,
                  RwInt32 __RWUNUSED__ offsetInObject,
                  RwInt32 __RWUNUSED__ sizeInObject)
{
    RpFrameVRMLAnim    *frameAnim = RWFRAMEGETANIM(object);

    RWFUNCTION(RWSTRING("FrameVRMLAnimDtor"));
    RWASSERT(object);

    RWASSERT(((RwObject *) (object))->type == (rwFRAME));

    if (frameAnim->name)
    {
        RwFree(frameAnim->name);
        frameAnim->name = (char *)NULL;
    }

    if (frameAnim->animTranslate)
    {
        _rpVrmAnimTranslateDestroy(frameAnim->animTranslate);
        frameAnim->animTranslate = (RpVRMLAnimTranslate *)NULL;
    }

    if (frameAnim->animRotate)
    {
        _rpVrmAnimRotateDestroy(frameAnim->animRotate);
        frameAnim->animRotate = (RpVRMLAnimRotate *)NULL;
    }

    if (frameAnim->animScale)
    {
        _rpVrmAnimScaleDestroy(frameAnim->animScale);
        frameAnim->animScale = (RpVRMLAnimScale *)NULL;
    }

    if (frameAnim->animScaleOrient)
    {
        _rpVrmAnimRotateDestroy(frameAnim->animScaleOrient);
        frameAnim->animScaleOrient = (RpVRMLAnimRotate *)NULL;
    }

    RWRETURN(object);
}

static void        *
FrameVRMLAnimCopy(void *dstObject,
                  const void *srcObject,
                  RwInt32 __RWUNUSED__ offsetInObject,
                  RwInt32 __RWUNUSED__ sizeInObject)
{
    RpFrameVRMLAnim    *dstFrameAnim = RWFRAMEGETANIM(dstObject);
    const RpFrameVRMLAnim *srcFrameAnim =
        RWFRAMEGETCONSTANIM(srcObject);

    RWFUNCTION(RWSTRING("FrameVRMLAnimCopy"));
    RWASSERT(dstObject);
    RWASSERT(srcObject);

    RWASSERT(((RwObject *) (dstObject))->type == (rwFRAME));
    RWASSERT(((const RwObject *) (srcObject))->type == (rwFRAME));

    /* just does a dumb copy, may impleament animation instancing later */
    if (srcFrameAnim->animTranslate)
    {
        dstFrameAnim->animTranslate =
            VRMLAnimTranslateCopy(srcFrameAnim->animTranslate);
    }

    if (srcFrameAnim->animRotate)
    {
        dstFrameAnim->animRotate =
            VRMLAnimRotateCopy(srcFrameAnim->animRotate);
    }

    if (srcFrameAnim->animScale)
    {
        dstFrameAnim->animScale =
            VRMLAnimScaleCopy(srcFrameAnim->animScale);
    }

    if (srcFrameAnim->animScaleOrient)
    {
        dstFrameAnim->animScaleOrient =
            VRMLAnimRotateCopy(srcFrameAnim->animScaleOrient);
    }

    if (dstFrameAnim->name)
    {
        dstFrameAnim->name = StrDup(srcFrameAnim->name);
    }

    RpVRMLAnimTransformStateAssign(&dstFrameAnim->matrixComp,
                                   &srcFrameAnim->matrixComp);

    RWRETURN(dstObject);
}

RwBool
_rpVrmlAnimPluginAttach(void)
{
    RWFUNCTION(RWSTRING("_rpVrmlAnimPluginAttach"));

    if (RwEngineRegisterPlugin(0, rwID_VRMLANIMPLUGIN,
                               VRMLAnimOpen, VRMLAnimClose) < 0)
    {
        RWRETURN(FALSE);
    }

    /* Extend into frames */

    /* FRAME ANIMATION */
    frameAnimOffset =
        RwFrameRegisterPlugin((sizeof(RpFrameVRMLAnim) + PAD),
                              rwID_VRMLANIMPLUGIN, FrameVRMLAnimCtor,
                              FrameVRMLAnimDtor, FrameVRMLAnimCopy);
    if (frameAnimOffset < 0)
    {
        RWRETURN(FALSE);
    }

    if (RwFrameRegisterPluginStream
        (rwID_VRMLANIMPLUGIN, FrameVRMLAnimReadStream,
         FrameVRMLAnimWriteStream, FrameVRMLAnimSizeStream) < 0)
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

#if defined (__MWERKS__)
#if (defined(RWVERBOSE))
#pragma message (__FILE__ "/" _SKY_EXPAND(__LINE__) ": __MWERKS__ == " _SKY_EXPAND(__MWERKS__))
#endif /* (defined (__MWERKS__)) */
#if (__option (global_optimizer))
#pragma always_inline on
#endif /* (__option (global_optimizer)) */
#endif /*  defined (__MWERKS__) */
