TCB Position Key frame Animator

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

TCB Position Key frame Animator

Post by Klunk »

I needed something to test tcb export from my max exporter, so had to reverse engineer the interpolation. This is really only rough test code but someone may find a use for it.

Code: Select all

//********************************************************************************************
 
class TCBKey
{
public:
 
    int     time;
    int     flags;
    float   tension;
    float   continuity;
    float   bias;
    float   easeIn;
    float   easeOut;
};
 
//********************************************************************************************
 
class TCBVector3dKey : public TCBKey
{
public:
 
    vector3df value;
};
 
//********************************************************************************************
// compute hermite spline params
 
void Hermite(float t, float& h1, float& h2, float& h3, float& h4)
{
    float t2, t3;
 
    t2 = t * t;
    t3 = t * t2;
 
    h2 = 3.0 * t2 - t3 - t3;
    h1 = 1.0 - h2;
    h4 = t3 - t2;
    h3 = h4 - t2 + t;
}
 
//********************************************************************************************
// Handles 3ds max TCB easeFrom easeTo params
 
float TCB_EaseFromEaseTo(float t, float easefrom, float easeto)
{
    float span = easefrom + easeto;
    if(span == 0) 
        return t;       
    if(span > 1.0f)
    {
        easefrom /= span; 
        easeto /= span;
    }
    float c = 1.0/(2.0 - easefrom - easeto);
    if( t < easefrom) 
        return c/easefrom * t * t;
    else if( t > 1.0 - easeto)
        return 1.0 - c/easeto * (t * (t - 2.0) + 1.0);
    else
        return c * (2.0 * t - easefrom);        
}
 
//********************************************************************************************
 
void TCBInTangent(TCBVector3dKey* key, TCBVector3dKey* prev, TCBVector3dKey* next, vector3df& tangent)
{
    vector3df a = (key->value - prev->value) * (1 - key->tension) * (1 - key->continuity) * (1 + key->bias); 
    vector3df b = (next->value - key->value) * (1 - key->tension) * (1 + key->continuity) * (1 - key->bias);
            
    float t = (float)(key->time - prev->time)/(float)(next->time - prev->time);
    tangent = (b + a) * lerp(t,0.5f,fabs(key->continuity));   
}
 
//********************************************************************************************
 
void TCBOutTangent(TCBVector3dKey* key, TCBVector3dKey* prev, TCBVector3dKey* next, vector3df& tangent)
{
    vector3df a = (key->value - prev->value) * (1 - key->tension) * (1 + key->continuity) * (1 + key->bias);
    vector3df b = (next->value - key->value) * (1 - key->tension) * (1 - key->continuity) * (1 - key->bias);
            
    float t = (float)(next->time - key->time)/(float)(next->time - prev->time);
    tangent = (b + a) * lerp(t, 0.5f,fabs(key->continuity));  
}
 
//********************************************************************************************
 
class CTCBPositionAnim : public ISceneNodeAnimator
{
    short           framerate;
    short           numkeys;
    short           frame;
    short           key;
    vector3df       outtangent;
    vector3df       intangent;  
    tcbposkeybuf    pos;
    int             oldtime;
    bool            play;
    bool            reverse;
    bool            bounce;
    bool            loop;
    bool            new_segment;
 
public:
 
    CTCBPositionAnim(short nkeys, TCBVector3dKey* pkeys) 
        : numkeys(nkeys), pos(nkeys), oldtime(0), framerate(30), key(0),
            frame(-1), reverse(false), bounce(false), loop(false), play(true), new_segment(true)
    { 
        memmove(&pos[0], pkeys, nkeys * sizeof(TCBVector3dKey));
    }
    void Loop()     { loop = true; bounce = false;}
    void Bounce()   { loop = false; bounce = true;}
    void Play()     { play = true; }
    void Stop()     { play = false; }
    void Reverse()  
    { 
        reverse = true; 
        key = numkeys - 1;
        frame = pos[key].time + 1;
    }
    void Fowards()  
    { 
        reverse = false; 
        frame = -1;
        key = 0;
    }
    virtual void animateNode(ISceneNode* node, u32 timeMs)
    {   
        if(!play)
            return;
 
        if(numkeys == 1)
        {
            node->setPosition(pos[0].value);
            return;
        }   
 
        if((timeMs - oldtime) * framerate >= MS_PER_SEC)
        {
            oldtime = timeMs;
            if(reverse)
            {
                frame--;
                if(frame <= pos[key - 1].time)
                {
                    new_segment = true;
                    key--;
                    if(key <= 0)
                    {
                        if(bounce)
                        {
                            reverse = false;
                            frame++;
                            key++;
                        }
                        else if(loop)
                        {
                            key = numkeys - 1;
                            frame = pos[key].time;
                        }
                        else
                        {
                            frame = 0;
                            key = 0;
                            play = false;
                        }
                    }
                }
            }
            else
            {
                frame++;
                if(frame > pos[key].time)
                {
                    new_segment = true;
                    key++;
                    if(key > numkeys - 1)
                    {
                        if(bounce)
                        {
                            reverse = true;
                            frame--;
                            key--;
                            new_segment = false;
                        }
                        else if(loop)
                        {
                            key = 0;
                            frame = 0;
                        }
                        else
                        {
                            frame--;
                            key--;
                            play = false;
                        }
                    }
                }
            }
            if(frame == 0)
                node->setPosition(pos[0].value);
            else
            {
                short outkey = key - 1;
                if(new_segment) // in a new segment so compute new in and out tangents
                {
                    TCBVector3dKey* next, *prev;
                    prev = key == 0 ? &pos[0] : &pos[outkey];
                    next = key == numkeys - 1 ? &pos[numkeys - 1] : &pos[key + 1];
                    TCBInTangent(&pos[key], prev, next, intangent);
                    prev = outkey == 0 ? &pos[0] : &pos[outkey - 1]; 
                    TCBOutTangent(&pos[outkey], prev, &pos[key], outtangent);
 
// handle the end cases                 
                    
                 if(key == 1) 
                       outtangent = outtangent * 1.5 - intangent * 0.5 * (1.0f - pos[0].tension);
                 if(key == numkeys - 1) 
                        intangent = intangent * 1.5 - outtangent * 0.5 * (1.0f - pos[numkeys - 1].tension);
 
                    new_segment = false;
                }
                float h1, h2, h3, h4;
                
// interpolation time               
                
                float t = (float)(frame - pos[outkey].time)/(float)(pos[key].time - pos[outkey].time);
                
// adjust for the ease              
                
                
                t = TCB_EaseFromEaseTo(t, pos[outkey].easeOut, pos[key].easeIn);
                
// hermite time             
                
                Hermite(t,h1,h2,h3,h4);
                
// interpolate              
                
                node->setPosition(h1 * pos[outkey].value + h2 * pos[key].value  + h3 * outtangent +  h4 * intangent);
            }
        }
    }
    virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const {}
    virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) {}
    virtual ESCENE_NODE_ANIMATOR_TYPE getType() const { return ESNAT_UNKNOWN; }
    virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0) 
    { return new CTCBPositionAnim(numkeys, &pos[0] ); }
};
 
//********************************************************************************************
//********************************************************************************************
other approaches could involve hard baking/pre-calculating (at load or on export) the tangents into the keyframe data removing the need for tcb part. Anyway enjoy.

edit: correct some error on the start and end keys, also worth noting that bias and continuity should always be zero for start and end frames
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Re: TCB Position Key frame Animator

Post by christianclavet »

This look quite interesting, could you put a quick example on how to use the class?
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: TCB Position Key frame Animator

Post by Klunk »

sure, should replace the code in main.cpp in the helloworld examples project

Code: Select all

#include <irrlicht.h>
#include <vector>
 
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace std;
 
#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
 
#define MS_PER_SEC  1000
#define DEF_INTESITY        115.0f/255.0f
#define DFACTOR             0.75f
#define INTENSITY_TO_COL(i)  SColor(255,(u32)(255 * i * DFACTOR),(u32)(255 * i * DFACTOR),(u32)(255 * i * DFACTOR))
 
//*****************************************************************************
// a simple grid helper 
 
class CGridSceneHelper : public ISceneNode
{
    float               intensity;
    float               spacing;
    int                 extents;
    aabbox3df           bbox;
    video::SMaterial    material;
    bool        drawAxis:1;
 
public:
 
    CGridSceneHelper(scene::ISceneNode* parent, ISceneManager* mgr,int  id, float s = 10.0f, int ext = 7, bool drwaxs = false,
                            float intens = DEF_INTESITY) 
                        : scene::ISceneNode(parent, mgr, id), spacing(s), extents(ext), drawAxis(drwaxs),intensity(intens)
    { 
        material.DiffuseColor = INTENSITY_TO_COL(intensity);
        material.setFlag(EMF_LIGHTING, false);
        float temp = extents * spacing;
        bbox = aabbox3df(-temp, 0.0f, -temp,temp, spacing, temp);
        setAutomaticCulling(EAC_OFF);   // we always draw the grid
        material.EmissiveColor = SColor(255,255,255,255);
    }
    void setDrawAxis(const bool draw) { drawAxis = draw; }
    void setSpacing(const float s)          
    { 
        spacing = s; 
        float temp = extents * spacing;
        bbox = aabbox3df(-temp,0.0f,-temp,temp,spacing,temp);
    }
    void setExtents(const int ext)          
    { 
        extents = ext; 
        float temp = extents * spacing;
        bbox = aabbox3df(-temp,0.0f,-temp,temp,spacing,temp);
    }
    void setColour(const float intens) { material.DiffuseColor = INTENSITY_TO_COL(intens); }
    virtual void OnRegisterSceneNode()
    {
        if(IsVisible)
            SceneManager->registerNodeForRendering(this);
        ISceneNode::OnRegisterSceneNode();
    }
    virtual void render()
    { 
        float endpt = extents * spacing;
        IVideoDriver* driver = SceneManager->getVideoDriver();
        driver->setTransform(ETS_WORLD,AbsoluteTransformation);
        material.Thickness = 1;
        driver->setMaterial(material);
 
        SColor temp_c = material.DiffuseColor;
        for(int i = 1; i <= extents;i++)
        {
            float t = i * spacing;
            driver->draw3DLine(vector3df(t,0.0f,-endpt),vector3df(t,0.0f,endpt),temp_c);
            driver->draw3DLine(vector3df(-t,0.0f,-endpt),vector3df(-t,0.0f,endpt),temp_c);
            driver->draw3DLine(vector3df(-endpt,0.0f,t),vector3df(endpt,0.0f,t),temp_c);
            driver->draw3DLine(vector3df(-endpt,0.0f,-t),vector3df(endpt,0.0f,-t),temp_c);
        }
 
        driver->draw3DLine(vector3df(-endpt,0.0f,0.0f),vector3df(endpt,0.0f,0.0f),SColor(255,0,0,0));
        driver->draw3DLine(vector3df(0.0f,0.0f,-endpt),vector3df(0.0f,0.0f,endpt),SColor(255,0,0,0));
 
        if(drawAxis)
        {
            SMaterial mat;
            mat.Thickness = 3;
            mat.setFlag(EMF_LIGHTING, false);
            driver->setMaterial(mat);
            vector3df origin = vector3df(0.0f,0.0f,0.0f);
            driver->draw3DLine(origin,vector3df(spacing,0.0f,0.0f),SColor(255,255,0,0));
            driver->draw3DLine(origin,vector3df(0.0f,spacing,0.0f),SColor(255,0,255,0));
            driver->draw3DLine(origin,vector3df(0.0f,0.0f,spacing),SColor(255,0,0,255));
        }
    }
    virtual const aabbox3df& getBoundingBox() const { return bbox; }
    virtual u32 getMaterialCount() const { return 1; }
    virtual SMaterial& getMaterial(u32 i) { return material; }
};
 
 
//***********************************************************************
 
class CCamSpaceLightAnim : public ISceneNodeAnimator
{
public:
 
    CCamSpaceLightAnim(ICameraSceneNode* cam, float offset, bool left) 
        : camera(cam), csoffset(0.5f * sin(PI/6.0f), 0.5f * cos(PI/6.0f),-cos(PI/6.0f)), doffset(offset)
    { 
        if(left)  
            csoffset.X *= -1.0f;
    }
    virtual void animateNode(ISceneNode* node, u32 timeMs)
    {
        matrix4 camTM, lightTM;
        vector3df wsoffset;
        camera->getViewMatrix().getInverse(camTM);
        camTM.zeroTranslation();
        camTM.transformVect(wsoffset,csoffset);
        TransformFromDirVector(-wsoffset,  lightTM);
        node->setRotation(lightTM.getRotationDegrees());
        node->setPosition(wsoffset * doffset);
    }
    virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const {}
    virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) {}
    virtual ESCENE_NODE_ANIMATOR_TYPE getType() const { return ESNAT_UNKNOWN; }
    virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0) 
    { 
        return new CCamSpaceLightAnim(camera,doffset,false); 
    }
 
private:
 
    void TransformFromDirVector(const vector3df& dir, matrix4& mat)
    {
        vector3df up(0.0f,1.0f,0.0f), zaxis, xaxis, yaxis;
        zaxis = dir;
        zaxis.normalize();
        xaxis = up.crossProduct(zaxis);
        xaxis.normalize();
        yaxis = zaxis.crossProduct(xaxis);
        mat[0] = xaxis.X; mat[1] = xaxis.Y; mat[2] = xaxis.Z; mat[3] = 0;
        mat[4] = yaxis.X; mat[5] = yaxis.Y; mat[6] = yaxis.Z; mat[7] = 0;
        mat[8] = zaxis.X; mat[9] = zaxis.Y; mat[10] = zaxis.Z; mat[11] = 0;
        mat[12] = 0; mat[13] = 0; mat[14] = 0; mat[15] = 1;
    }
    vector3df           csoffset;
    float               doffset;
    ICameraSceneNode*   camera;
};
 
//********************************************************************************************
// keyframe classes
 
class TCBTanKey
{
public:
    int         time;
    int         flags;
    vector3df   inTan;
    vector3df   outTan;
    float       easeIn;
    float       easeOut;
};
 
class TCBKey
{
public:
 
    int     time;
    int     flags;
    float   tension;
    float   continuity;
    float   bias;
    float   easeIn;
    float   easeOut;
 
    float getContinuity()   { return continuity; }
    float getBias()         { return bias; }
    float getTension()      { return tension; }
};
 
class TCBVector3dKey : public TCBKey
{
public:
 
    vector3df value;
};
 
typedef vector<TCBVector3dKey>  tcbposkeybuf;
 
 
// temp struct for quick and dirty intialization, as my data comes from an export file
 
struct TCBVector3dKeyData 
{
    int     time;
    int     flags;
    float   tension;
    float   continuity;
    float   bias;
    float   easeIn;
    float   easeOut;
    float   value[3];
};
 
//********************************************************************************************
// The Math: I usual keep these in a mathlib but could be put easily into the class below if you wanted to
 
void Hermite(float t, float& h1, float& h2, float& h3, float& h4)
{
    float t2, t3;
 
    t2 = t * t;
    t3 = t * t2;
 
    h2 = 3.0f * t2 - t3 - t3;
    h1 = 1.0f - h2;
    h4 = t3 - t2;
    h3 = h4 - t2 + t;
}
 
float TCB_EaseFromEaseTo(float t, float easefrom, float easeto)
{
    float span = easefrom + easeto;
    if(span == 0.0f) 
        return t;       
    if(span > 1.0f)
    {
        easefrom /= span; 
        easeto /= span;
    }
    float c = 1.0f/(2.0f - easefrom - easeto);
    if( t < easefrom) 
        return c/easefrom * t * t;
    else if( t > 1.0 - easeto)
        return 1.0f - c/easeto * (t * (t - 2.0f) + 1.0f);
    else
        return c * (2.0f * t - easefrom);       
}
 
void TCBInTangent(TCBVector3dKey* key, TCBVector3dKey* prev, TCBVector3dKey* next, vector3df& tangent)
{
    vector3df a = (key->value - prev->value) * (1 - key->tension) * (1 - key->continuity) * (1 + key->bias); 
    vector3df b = (next->value - key->value) * (1 - key->tension) * (1 + key->continuity) * (1 - key->bias);
            
    float t = (float)(key->time - prev->time)/(float)(next->time - prev->time);
    tangent = (b + a) * lerp(t, 0.5f, fabs(key->continuity));   
}
 
void TCBOutTangent(TCBVector3dKey* key, TCBVector3dKey* prev, TCBVector3dKey* next, vector3df& tangent)
{
    vector3df a = (key->value - prev->value) * (1 - key->tension) * (1 + key->continuity) * (1 + key->bias);
    vector3df b = (next->value - key->value) * (1 - key->tension) * (1 - key->continuity) * (1 - key->bias);
            
    float t = (float)(next->time - key->time)/(float)(next->time - prev->time);
    tangent = (b + a) * lerp(t, 0.5f,fabs(key->continuity));
}
 
//********************************************************************************************
// the animator class
 
class CTCBPositionAnim : public ISceneNodeAnimator
{
    short           framerate;
    short           numkeys;
    short           frame;
    short           key;
    vector3df       outtangent;
    vector3df       intangent;  
    tcbposkeybuf    pos;
    int     oldtime;
    bool    play    ;
    bool    reverse ;
    bool    bounce  ;
    bool    loop    ;
    bool    new_segment;
 
public:
 
    CTCBPositionAnim(short nkeys, TCBVector3dKey* pkeys) 
        : numkeys(nkeys), pos(nkeys), oldtime(0), framerate(30), key(0),
            frame(-1), reverse(false), bounce(false), loop(false), play(true), new_segment(true)
    { 
        memmove(&pos[0], pkeys, nkeys * sizeof(TCBVector3dKey));
    }
    void Loop()     { loop = true; bounce = false;}
    void Bounce()   { loop = false; bounce = true;}
    void Play()     { play = true; }
    void Stop()     { play = false; }
    void Reverse()  
    { 
        reverse = true; 
        key = numkeys - 1;
        frame = pos[key].time + 1;
    }
    void Fowards()  
    { 
        reverse = false; 
        frame = -1;
        key = 0;
    }
    virtual void animateNode(ISceneNode* node, u32 timeMs)
    {   
        if(!play)
            return;
 
        if(numkeys == 1)
        {
            node->setPosition(pos[0].value);
            return;
        }   
 
        if((timeMs - oldtime) * framerate >= MS_PER_SEC)
        {
            oldtime = timeMs;
            if(reverse)
            {
                frame--;
                if(frame <= pos[key - 1].time)
                {
                    new_segment = true;
                    key--;
                    if(key <= 0)
                    {
                        if(bounce)
                        {
                            reverse = false;
                            frame++;
                            key++;
                        }
                        else if(loop)
                        {
                            key = numkeys - 1;
                            frame = pos[key].time;
                        }
                        else
                        {
                            frame = 0;
                            key = 0;
                            play = false;
                        }
                    }
                }
            }
            else
            {
                frame++;
                if(frame > pos[key].time)
                {
                    new_segment = true;
                    key++;
                    if(key > numkeys - 1)
                    {
                        if(bounce)
                        {
                            reverse = true;
                            frame--;
                            key--;
                            new_segment = false;
                        }
                        else if(loop)
                        {
                            key = 0;
                            frame = 0;
                        }
                        else
                        {
                            frame--;
                            key--;
                            play = false;
                        }
                    }
                }
            }
            if(frame == 0)
                node->setPosition(pos[0].value);
            else
            {
                short outkey = key - 1;
                if(new_segment)
                {
                    TCBVector3dKey* next, *prev;
                    prev = key == 0 ? &pos[0] : &pos[outkey];
                    next = key == numkeys - 1 ? &pos[numkeys - 1] : &pos[key + 1];
                    TCBInTangent(&pos[key], prev, next, intangent);
                    
                    prev = outkey == 0 ? &pos[0] : &pos[outkey - 1]; 
                    TCBOutTangent(&pos[outkey], prev, &pos[key], outtangent);
 
                    if(key == 1) 
                        outtangent = outtangent * 1.5 - intangent * 0.5 * (1.0f - pos[0].tension);
                    if(key == numkeys - 1) 
                        intangent = intangent * 1.5 - outtangent * 0.5 * (1.0f - pos[numkeys - 1].tension);
 
                    new_segment = false;
                }
                float h1, h2, h3, h4;
                float t = (float)(frame - pos[outkey].time)/(float)(pos[key].time - pos[outkey].time);
                t = TCB_EaseFromEaseTo(t, pos[outkey].easeOut, pos[key].easeIn);
                Hermite(t,h1,h2,h3,h4);
                node->setPosition(h1 * pos[outkey].value + h2 * pos[key].value  + h3 * outtangent +  h4 * intangent);
            }
        }
    }
    virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const {}
    virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) {}
    virtual ESCENE_NODE_ANIMATOR_TYPE getType() const { return ESNAT_UNKNOWN; }
    virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0) 
    { return new CTCBPositionAnim(numkeys, &pos[0] ); }
};
 
//***********************************************************************
// keyframe data this was exported from max using a simple script
 
TCBVector3dKeyData tcbkeys[] = {{0,0,1.0f,0.0f,0.0f,0.0f,0.0f,-104.221f,4.40779f,61.7011f},
                                {20,0,0.0f,0.0f,0.0f,0.0f,0.0f,-7.71352f,104.638f,61.7011f},
                                {34,0,-0.952f,0.116f,0.572f,0.0f,0.0f,147.733f,184.363f,182.394f},
                                {50,0,1.0f,0.0f,0.0f,0.0f,0.0f,108.372f,4.58077f,61.7011f},
                                {57,0,0.0f,0.0f,0.0f,0.0f,0.0f,81.5355f,50.7064f,19.1456f},
                                {65,0,0.928f,0.0f,-0.316f,0.0f,0.0f,14.1475f,4.30017f,-74.9152f},
                                {85,0,0.0f,0.0f,0.0f,0.0f,0.0f,-75.702f,55.2902f,-74.9152f},
                                {100,0,0.0f,0.0f,0.0f,0.0f,0.0f,-103.876f,6.24371f,58.0337f}};
 
//***********************************************************************
 
all the same file :)

Code: Select all

int main()
{
    IrrlichtDevice *device = createDevice( video::EDT_OPENGL, dimension2d<u32>(640, 480), 16, false, false, false, 0);
 
    if (!device)
        return 1;
 
    device->setWindowCaption(L"Test TCB Animator");
 
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
 
// add a grid helper so  as to gauge the motion as it were
 
    CGridSceneHelper* gridNode = new CGridSceneHelper(smgr->getRootSceneNode(), smgr, 666, 20, 7);
    gridNode->drop();
    gridNode = 0;
 
// add spheres with a tcb key frame animator
 
    IMeshSceneNode* sphereNode1 = smgr->addSphereSceneNode(10.f,16,smgr->getRootSceneNode(),999,vector3df(10.0f,0.0,0.0));
    CTCBPositionAnim* tcbanim = new CTCBPositionAnim((sizeof(tcbkeys)/sizeof(tcbkeys[0])),(TCBVector3dKey *)tcbkeys);
    if(tcbanim)
    {
        tcbanim->Loop();
        sphereNode1->addAnimator(tcbanim);
        tcbanim->drop();
    }
 
    ICameraSceneNode* cameraNode = smgr->addCameraSceneNodeMaya();
 
// add a light
 
    ILightSceneNode* lightNode = smgr->addLightSceneNode(smgr->getRootSceneNode(),vector3df(0,0,0));
    lightNode->setLightType(ELT_DIRECTIONAL);
 
// add camera space animator
 
    ISceneNodeAnimator* anim = new CCamSpaceLightAnim(cameraNode,100.0f,false);
    if(anim)
    {
        lightNode->addAnimator(anim);
        anim->drop();
    }
 
    while(device->run())
    {
        driver->beginScene(true, true, SColor(255,127,127,140));
        smgr->drawAll();
        driver->endScene();
    }
    device->drop();
    return 0;
}
Last edited by Klunk on Sun Oct 27, 2013 10:39 am, edited 2 times in total.
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: TCB Position Key frame Animator

Post by Klunk »

if you use max and want to play around this will generate the keyframe cpp code

Code: Select all

fn cp p =  (p * 0.04 - 1.0)
 
fn ToZero val tolerance = 
(   
    res =  val;
    if abs val < tolerance then res = 0.0
    res;
)
 
fn TCBPositionKeysToCpp obj = if obj != undefined and classof obj.position.controller == tcb_position then
(
    keys = obj.position.controller.keys;
    for k = 1 to keys.count do
    (
        key = keys[k];
        
        if k == 1 then
            format "TCBVector3dKeyData tcbkeys[] = {{%,0,%f,0.0f,0.0f,%f,%f,%f,%f,%f},\n"  (key.time as integer/160) \
                            (cp key.tension) (key.easeFrom * 0.02) (key.easeTo * 0.02) \
                        (ToZero key.value.x 0.00001)  (ToZero key.value.z 0.00001)  (ToZero key.value.y 0.00001);   
        else if k == keys.count then 
            format "{%,0,%f,0.0f,0.0f,%f,%f,%f,%f,%f}};\n"  (key.time as integer/160) (cp key.tension) \
                            (key.easeFrom * 0.02) (key.easeTo * 0.02) \
                        (ToZero key.value.x 0.00001)  (ToZero key.value.z 0.00001)  (ToZero key.value.y 0.00001);
        else
            format "{%,0,%f,%f,%f,%f,%f,%f,%f,%f},\n" (key.time as integer/160) (cp key.tension) (cp key.continuity) \
                        (cp key.bias) (key.easeFrom * 0.02) (key.easeTo * 0.02) \
                        (ToZero key.value.x 0.00001)  (ToZero key.value.z 0.00001)  (ToZero key.value.y 0.00001);   
    )   
)   
 
(
    clearlistener()
    TCBPositionKeysToCpp $
)
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: TCB Position Key frame Animator

Post by Klunk »

the snake

Code: Select all

TCBVector3dKeyData tcbkeys[] = {{0,0,0.0f,0.0f,0.0f,0.0f,0.0f,-0.637849f,10.14f,111.148f},
                            {5,0,0.0f,0.0f,0.0f,0.0f,0.0f,34.8608f,10.14f,87.736f},
                            {15,0,0.0f,0.0f,0.0f,0.0f,0.0f,-38.9052f,10.14f,45.0942f},
                            {25,0,0.0f,0.0f,0.0f,0.0f,0.0f,28.0183f,10.14f,13.0513f},
                            {35,0,0.0f,0.0f,0.0f,0.0f,0.0f,-31.2692f,10.14f,-20.1104f},
                            {45,0,0.0f,0.0f,0.0f,0.0f,0.0f,29.8344f,10.14f,-49.7389f},
                            {55,0,-1.0f,0.0f,0.268f,0.0f,0.0f,-48.4794f,10.14f,-84.3622f},
                            {65,0,0.0f,0.0f,0.0f,0.0f,0.0f,44.6347f,10.14f,-109.842f},
                            {70,0,0.78f,0.0f,0.0f,0.0f,0.0f,1.10813f,10.14f,-126.988f},
                            {80,0,0.0f,0.0f,0.0f,0.0f,1.0f,-1.16113f,10.14f,101.07f}};
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: TCB Position Key frame Animator

Post by Klunk »

btw also known as Kochanek-Bartels splines

http://en.wikipedia.org/wiki/Kochanek%E ... els_spline
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: TCB Position Key frame Animator

Post by Klunk »

a version of the animator with the in and out tangents cached with the keyframes, cleaner and probably a bit more efficient.

Code: Select all

class TCBTanVector3dKey
{
public:
 
    int         time;
    int         flags;
    vector3df   inTan;
    vector3df   outTan;
    float       easeIn;
    float       easeOut;
    vector3df   value;
};
 
typedef vector<TCBTanVector3dKey>   tcbtanposkeybuf;
 
class CTCBTanPositionAnim : public ISceneNodeAnimator
{
    short           framerate;
    short           numkeys;
    short           frame;
    short           key;
    tcbtanposkeybuf pos;
    int             oldtime;
    bool            play;
    bool            reverse;
    bool            bounce;
    bool            loop;
 
public:
 
    CTCBTanPositionAnim(short nkeys, TCBVector3dKey* pkeys) 
        : numkeys(nkeys), pos(nkeys), oldtime(0), framerate(30), key(0),
            frame(-1), reverse(false), bounce(false), loop(false), play(true)
    { 
        if(nkeys == 1)
            pos[0].value = pkeys[0].value;
        else
        {
            for(int k = 0; k < nkeys; k++)
            {
                TCBVector3dKey* key = &pkeys[k];
                TCBVector3dKey* prev  = k == 0 ? key : &pkeys[k - 1];
                TCBVector3dKey* next  = k == nkeys - 1 ? key : &pkeys[k+1];
                TCBOutTangent(key, prev, next, pos[k].outTan);
                TCBInTangent(key, prev, next, pos[k].inTan);    
                pos[k].time = pkeys[k].time;
                pos[k].flags = pkeys[k].flags;
                pos[k].easeIn = pkeys[k].easeIn;
                pos[k].easeOut = pkeys[k].easeOut;
                pos[k].value = pkeys[k].value;
            }
            pos[0].outTan = pos[0].outTan * 1.5 - pos[1].inTan * 0.5 * (1.0f - pkeys[1].tension);
            pos[nkeys - 1].inTan = pos[nkeys - 1].inTan * 1.5 -  pos[nkeys - 2].outTan * 0.5 * (1.0f - pkeys[nkeys - 1].tension);
        }
    }
    CTCBTanPositionAnim(short nkeys, TCBTanVector3dKey* pkeys) 
        : numkeys(nkeys), pos(nkeys), oldtime(0), framerate(30), key(0),
            frame(-1), reverse(false), bounce(false), loop(false), play(true)
    {
        memmove(&pos[0], pkeys, nkeys * sizeof(TCBTanVector3dKey)); 
    }
    void Loop()     { loop = true; bounce = false;}
    void Bounce()   { loop = false; bounce = true;}
    void Play()     { play = true; }
    void Stop()     { play = false; }
    void Reverse()  
    { 
        reverse = true; 
        key = numkeys - 1;
        frame = pos[key].time + 1;
    }
    void Fowards()  
    { 
        reverse = false; 
        frame = -1;
        key = 0;
    }
    virtual void animateNode(ISceneNode* node, u32 timeMs)
    {   
        if(!play)
            return;
 
        if(numkeys == 1)
        {
            node->setPosition(pos[0].value);
            return;
        }   
 
        if((timeMs - oldtime) * framerate >= MS_PER_SEC)
        {
            oldtime = timeMs;
            if(reverse)
            {
                frame--;
                if(frame <= pos[key - 1].time)
                {
                    key--;
                    if(key <= 0)
                    {
                        if(bounce)
                        {
                            reverse = false;
                            frame++;
                            key++;
                        }
                        else if(loop)
                        {
                            key = numkeys - 1;
                            frame = pos[key].time;
                        }
                        else
                        {
                            frame = 0;
                            key = 0;
                            play = false;
                        }
                    }
                }
            }
            else
            {
                frame++;
                if(frame > pos[key].time)
                {
                    key++;
                    if(key > numkeys - 1)
                    {
                        if(bounce)
                        {
                            reverse = true;
                            frame--;
                            key--;
                        }
                        else if(loop)
                        {
                            key = 0;
                            frame = 0;
                        }
                        else
                        {
                            frame--;
                            key--;
                            play = false;
                        }
                    }
                }
            }
            if(frame == 0)
                node->setPosition(pos[0].value);
            else
            {
                short outkey = key - 1;
                float h1, h2, h3, h4;
                float t = (float)(frame - pos[outkey].time)/(float)(pos[key].time - pos[outkey].time);
                t = TCB_EaseFromEaseTo(t, pos[outkey].easeOut, pos[key].easeIn);
                Hermite(t,h1,h2,h3,h4);
                node->setPosition(h1 * pos[outkey].value + h2 * pos[key].value  + h3 * pos[outkey].outTan +  h4 * pos[key].inTan);
            }
        }
    }
    virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const {}
    virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) {}
    virtual ESCENE_NODE_ANIMATOR_TYPE getType() const { return ESNAT_UNKNOWN; }
    virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0) 
    { return new CTCBTanPositionAnim(numkeys, &pos[0] ); }
};
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Re: TCB Position Key frame Animator

Post by christianclavet »

Thanks for posting. This will surely be useful, as I would like to look at animating objects directly from my own application. (Cutscene creation)
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: TCB Position Key frame Animator

Post by Klunk »

I wanted to compare performance, both visual and actual with a something more simple so I did a linear version, you don't lose too much either way you look at it :? Still working on the bezier version can't quite crack constant velocity, though animators I know said they prefer to use tcb for 95% of the time.

So here's a linear version

Code: Select all

class IKey
{
public:
    int time;
    int flags;
};
 
 
class LINVector3dKey : public IKey
{
public:
    vector3df   value;
};
 
typedef vector<LINVector3dKey>  linposkeybuf;
 
struct LINVector3dKeyData 
{
    int     time;
    int     flags;
    float   value[3];
};
 
 
class CLINPositionAnim : public ISceneNodeAnimator
{
    short           framerate;
    short           numkeys;
    short           frame;
    short           key;
    linposkeybuf    pos;
    int             oldtime;
    bool            play;
    bool            reverse;
    bool            bounce;
    bool            loop;
 
public:
 
    CLINPositionAnim(short nkeys, LINVector3dKey* pkeys) 
        : numkeys(nkeys), pos(nkeys), oldtime(0), framerate(30), key(0),
            frame(-1), reverse(false), bounce(false), loop(false), play(true)
    {
        memmove(&pos[0], pkeys, nkeys * sizeof(LINVector3dKey));    
    }
    void Loop()     { loop = true; bounce = false;}
    void Bounce()   { loop = false; bounce = true;}
    void Play()     { play = true; }
    void Stop()     { play = false; }
    void Reverse()  
    { 
        reverse = true; 
        key = numkeys - 1;
        frame = pos[key].time + 1;
    }
    void Fowards()  
    { 
        reverse = false; 
        frame = -1;
        key = 0;
    }
    virtual void animateNode(ISceneNode* node, u32 timeMs)
    {   
        if(!play)
            return;
 
        if(numkeys == 1)
        {
            node->setPosition(pos[0].value);
            return;
        }   
 
        if((timeMs - oldtime) * framerate >= MS_PER_SEC)
        {
            oldtime = timeMs;
            if(reverse)
            {
                frame--;
                if(frame <= pos[key - 1].time)
                {
                    key--;
                    if(key <= 0)
                    {
                        if(bounce)
                        {
                            reverse = false;
                            frame++;
                            key++;
                        }
                        else if(loop)
                        {
                            key = numkeys - 1;
                            frame = pos[key].time;
                        }
                        else
                        {
                            frame = 0;
                            key = 0;
                            play = false;
                        }
                    }
                }
            }
            else
            {
                frame++;
                if(frame > pos[key].time)
                {
                    key++;
                    if(key > numkeys - 1)
                    {
                        if(bounce)
                        {
                            reverse = true;
                            frame--;
                            key--;
                        }
                        else if(loop)
                        {
                            key = 0;
                            frame = 0;
                        }
                        else
                        {
                            frame--;
                            key--;
                            play = false;
                        }
                    }
                }
            }
            if(frame == 0)
                node->setPosition(pos[0].value);
            else
            {
                short outkey = key - 1;
                float t = (float)(frame - pos[outkey].time)/(float)(pos[key].time - pos[outkey].time);
                node->setPosition(lerp(pos[outkey].value,pos[key].value, t));
            }
        }
    }
    virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const {}
    virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) {}
    virtual ESCENE_NODE_ANIMATOR_TYPE getType() const { return ESNAT_UNKNOWN; }
    virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0) 
    { return new CLINPositionAnim(numkeys, &pos[0] ); }
};
 
// linear snake.
 
LINVector3dKeyData linkeys[] = {{0,0,-0.637849f,10.14f,111.148f},
                                {5,0,34.8608f,10.14f,87.736f},
                                {15,0,-38.9052f,10.14f,45.0942f},
                                {25,0,28.0183f,10.14f,13.0513f},
                                {35,0,-31.2692f,10.14f,-20.1104f},
                                {45,0,29.8344f,10.14f,-49.7389f},
                                {55,0,-48.4794f,10.14f,-84.3622f},
                                {65,0,44.6347f,10.14f,-109.842f},
                                {70,0,1.10813f,10.14f,-126.988f},
                                {80,0,-1.16113f,10.14f,101.07f}}; 
Post Reply