(c++) newton scene node 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
JonLT
Posts: 152
Joined: Thu Mar 15, 2007 5:47 pm
Location: Denmark

(c++) newton scene node animator!

Post by JonLT »

When trying to improve my irrlicht-newton-framework I came up with the idear to create a scene node animator that handled newton. So in stead of having some big clumsy framework, just add an animator to the scene node, and that should be it. I don't know if such a thing allready exists but here's my go at it!
the .h file

Code: Select all

#ifndef __C_SCENE_NODE_ANIMATOR_NEWTON_H_INCLUDED__
#define __C_SCENE_NODE_ANIMATOR_NEWTON_H_INCLUDED__

#include "irrList.h"
#include "ISceneNode.h"
#include "ISceneNodeAnimator.h"
#include <newton.h>

namespace irr
{
namespace scene
{

    //! Body types
    enum E_NEWTON_BODY_TYPE
    {
        ENBT_CUBE,
        ENBT_SPHERE
    };


    //! An animator that animates the scene node according to the Newton Game Dynamics
    class CSceneNodeAnimatorNewton : public ISceneNodeAnimator
    {
        public:
            //! Constructor
            CSceneNodeAnimatorNewton(E_NEWTON_BODY_TYPE type = ENBT_CUBE,
                                     const core::vector3df& size = core::vector3df(10,10,10), f32 mass = 10);

            //! destructor
            virtual ~CSceneNodeAnimatorNewton();

            //! animates a scene node
            virtual void animateNode(ISceneNode* node, u32 timeMs);

            //! Gets the newton body
            /** \return Returns the newton body */
            NewtonBody* getNewtonBody()
            {
                return Body;
            }

            //! Gets the newton world
            /** \return Returns the newton world. */
            static NewtonWorld* getNewtonWorld()
            {
                return nWorld;
            }

            //! Sets the gravity
            /** \param newGravity: the new gravity to be used */
            static void setGravity(f32 newGravity)
            {
                Gravity = newGravity;
            }

            //! Sets the update interval
            /** \param ms: the update inerval in milli seconds */
            static void setUpdateInterval(f32 ms)
            {
                dt = ms;
            }

        private:

            //the newton force and torque callback
            static void forceAndTorqueCallback(const NewtonBody* body);
            //the newton transform callback
            static void transformCallback(const NewtonBody* body, const f32* matrix);

            //the mass of the newton body
            f32 Mass;
            //the size of the newton collision
            core::vector3df Size;
            //the type of the newton collision (box or sphere)
            E_NEWTON_BODY_TYPE Type;

            //the newton world
            static NewtonWorld* nWorld;
            //the newton body
            NewtonBody* Body;
            //the gravity of the world
            static f32 Gravity;

            //bool used to check if an animateNode call is the first or not
            bool FirstAnimate;

            /* a list of created animators of this type, this is used to keep track
            of who's updating the newton world, when an animator is deleted the job is
            passed on to the next in the list */
            static core::list<CSceneNodeAnimatorNewton*> CreatedAnimators;
            //bool to check if this animator is updating the newton world
            bool IAmUpdating;

            //current time, gets updated on every animateNode call
            static f32 CurrentTime;
            //the time accumulator is making sure that the world is updated right
            static f32 TimeAccumulator;
            //the time step
            static f32 dt;
    };

}//end namespace scene
}//end namespace irr

#endif
the .cpp file

Code: Select all

#include "CSceneNodeAnimatorNewton.h"

namespace irr
{
namespace scene
{

//declare static data members
NewtonWorld* CSceneNodeAnimatorNewton::nWorld = NewtonCreate(NULL, NULL);
core::list<CSceneNodeAnimatorNewton*> CSceneNodeAnimatorNewton::CreatedAnimators;
f32 CSceneNodeAnimatorNewton::CurrentTime = 0;
f32 CSceneNodeAnimatorNewton::TimeAccumulator = 0;
f32 CSceneNodeAnimatorNewton::dt = 10;
f32 CSceneNodeAnimatorNewton::Gravity = -98.f;

//! Constructor
CSceneNodeAnimatorNewton::CSceneNodeAnimatorNewton(E_NEWTON_BODY_TYPE type, const core::vector3df& size, f32 mass)
    : Mass(mass), Size(size), Type(type), Body(0), FirstAnimate(true), IAmUpdating(false)
{

    //search though the other created animators to see if anyone is updating the newton world
    bool anotherIsUpdating = false;
    core::list<CSceneNodeAnimatorNewton*>::Iterator it = CreatedAnimators.begin();
    for (; it != CreatedAnimators.end(); ++it)
    {
        //if an object is found
        if((*it))
        {
            //is it updating
            if((*it)->IAmUpdating)
                anotherIsUpdating = true;
        }
    }

    //if no other animator is updating the newton world - make this one do it
    if(!anotherIsUpdating)
    {
        IAmUpdating = true;
    }

    //put this animator in the static list
    CreatedAnimators.push_back(this);

    /* the newton world could have been deleted, if at some time in the code no animators where active.
    Because of this the animator needs to check if the newton world is there, and create a new one if it isn't */
    if(!nWorld)
        nWorld = NewtonCreate(NULL, NULL);

}

//! destructor
CSceneNodeAnimatorNewton::~CSceneNodeAnimatorNewton()
{

    //a bool to check if other animators is still there
    bool foundOther = false;
    //bool to check if another animator is set to update
    bool anotherIsUpdating = false;

    //iterate though the list to remove the pointer to this animator, and check if other animators are still active
    core::list<CSceneNodeAnimatorNewton*>::Iterator it = CreatedAnimators.begin();
    for (; it != CreatedAnimators.end(); ++it)
    {
        //remove the pointer to this, from the list
        if((*it) == this)
            (*it) = 0;

        //if an animator is found
        if((*it))
            foundOther = true;
    }

    //if this animator was the one updating the newton world - make another one update
    if(IAmUpdating)
    {
        it = CreatedAnimators.begin();
        for(; it!= CreatedAnimators.end(); ++it)
        {
            //if an animator is found
            if((*it))
            {
                //and it is the first one found
                if(!anotherIsUpdating)
                {
                    //make it update
                    (*it)->IAmUpdating = true;
                    anotherIsUpdating = true;
                }
            }
        }
    }

    //delete the newton body
    if(Body)
        NewtonDestroyBody(nWorld, Body);

    //if this is the last animator - delete the newton world
    if(!foundOther)
    {
        if(nWorld)
        {
            NewtonDestroy(nWorld);
            nWorld = 0;
        }
    }

}

//! animates a scene node
void CSceneNodeAnimatorNewton::animateNode(ISceneNode* node, u32 timeMs)
{
    //if this is the first update - create the newton body
    if(FirstAnimate)
    {
        NewtonCollision* collision = 0;
        float I[3];

        if(Type == ENBT_CUBE)
        {
            //calculate the inertia
            I[0] = 0.0833f*Mass*(Size.Z*Size.Z + Size.Y*Size.Y);
            I[1] = 0.0833f*Mass*(Size.Z*Size.Z + Size.X*Size.X);
            I[2] = 0.0833f*Mass*(Size.X*Size.X + Size.Y*Size.Y);
            //create the collision
            collision = NewtonCreateBox(nWorld, Size.X, Size.Y, Size.Z, NULL);
        }

        if(Type == ENBT_SPHERE)
        {
            //calculate inertia
            I[0] = 0.2f*Mass*(Size.Z*Size.Z + Size.Y*Size.Y);
            I[1] = 0.2f*Mass*(Size.Z*Size.Z + Size.X*Size.X);
            I[2] = 0.2f*Mass*(Size.X*Size.X + Size.Y*Size.Y);

            //create the collision
            collision = NewtonCreateSphere(nWorld, Size.X, Size.Y, Size.Z, NULL);
        }

        if(collision)
        {
            //create the body
            Body = NewtonCreateBody(nWorld, collision);
            //release the collision
            NewtonReleaseCollision(nWorld, collision);
        }

        if(Body)
        {
            //set the mass and inertia of the body
            NewtonBodySetMassMatrix(Body, Mass, I[0], I[1], I[2]);
            //apply the node's matrix to the body
            NewtonBodySetMatrix(Body, node->getRelativeTransformation().pointer());
            //set the node as userdata of the body
            NewtonBodySetUserData(Body, node);
            //set the callbacks
            NewtonBodySetForceAndTorqueCallback(Body, forceAndTorqueCallback);
            NewtonBodySetTransformCallback(Body, transformCallback);
        }
        FirstAnimate = false;
    }

    //if this animator is taking care of updating the newton world
    if(IAmUpdating)
    {
            //update the newton world
            if(TimeAccumulator < dt)
            {
                //get the time from the device
                f32 newTime = timeMs;
                //calculate the time step
                f32 deltaTime = newTime - CurrentTime;
                //update CurrentTime
                CurrentTime = newTime;
                //add the time step to the time accumulator
                TimeAccumulator += deltaTime;
            }
            //update if the TimeAccumulator is big enough
            if(TimeAccumulator >= dt)
            {
                //update
                NewtonUpdate(nWorld, dt/1000); //convert to seconds
                TimeAccumulator -= dt;
            }
    }
}

void CSceneNodeAnimatorNewton::forceAndTorqueCallback(const NewtonBody* body)
{
   //some floats to hold the information of the body
   float Ixx;
   float Iyy;
   float Izz;
   float mass;
   //get the info and put it in the above floats
   NewtonBodyGetMassMatrix (body, &mass, &Ixx, &Iyy, &Izz);
   //create a force vector with gravity
   float force[3] = {0.0f, mass*Gravity, 0.0f};
   //apply the force
   NewtonBodySetForce(body, force);
}

void CSceneNodeAnimatorNewton::transformCallback(const NewtonBody* body, const f32* matrix)
{
    //get the scene node from the newton body
    ISceneNode* node = (ISceneNode*)NewtonBodyGetUserData(body);
    //make a matrix to hold the body matrix
    core::matrix4 irrMat;
    //copy the body matrix to the matrix4
    memcpy(irrMat.pointer(), matrix, sizeof(float)*16);
    //move and rotate the node according to the matrix
    if(node)
    {
        node->setPosition(irrMat.getTranslation());
        node->setRotation(irrMat.getRotationDegrees());
    }
}

}//end namespace scene
}//end namespace irr
and how I use it

Code: Select all

#include <irrlicht.h>
#include <newton.h>
#include "CSceneNodeAnimatorNewton.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;

int main()
{
    IrrlichtDevice* device = createDevice();
    ISceneManager* smgr = device->getSceneManager();
    IVideoDriver* driver = device->getVideoDriver();

    ISceneNode* node = smgr->addCubeSceneNode();
    ISceneNodeAnimator* anim = new CSceneNodeAnimatorNewton();
    node->addAnimator(anim);
    anim->drop();

    ISceneNode* node2 = smgr->addSphereSceneNode();
    node2->setPosition(vector3df(0,-30,5));
    ISceneNodeAnimator* anim2 = new CSceneNodeAnimatorNewton(ENBT_SPHERE, vector3df(5,5,5), 0);
    node2->addAnimator(anim2);
    anim2->drop();

    smgr->addCameraSceneNodeMaya();

    while(device->run())
    {
        driver->beginScene(true, true, SColor(100,100,100,100));
        smgr->drawAll();
        driver->endScene();
    }

    device->drop();

    return 0;

}
I'm sure there's a better way of doing this. I'm especially concerned with the creation and deletiion of the newton world, and I'm not 100% sure that I'm updating the world right.
Any comments would be appreciated! :)
Last edited by JonLT on Sun Jul 08, 2007 5:55 pm, edited 1 time in total.
Atraides
Posts: 18
Joined: Wed Jun 06, 2007 1:09 am
Location: Australia

Post by Atraides »

This sounds really cool, i haven't tested it yet but i will soon and let you know how i find it.

Just recently i have been programming using Irrlicht and Newton and the amount to work to make the two work together nicely is painful sometimes but hopefully if we can just add an animator it will make it all the easier for everyone

thanks
Cheers
Courtney
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

This is pretty cool, nice use of static..
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Post by bitplane »

I haven't tested this one either, but would it be okay if I also added it to irrlicht plugins svn? Again, you can have svn write access if you want to keep it up-to-date and add any other pluggable things that you make
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
JonLT
Posts: 152
Joined: Thu Mar 15, 2007 5:47 pm
Location: Denmark

Post by JonLT »

Thanks guys!
And just a reminder: because the animator is updating the newton world, an animator should only be added to one node. A new animator should be created for every new scene node. Also.. the newton body is created with the animator.
There might be some way of fixing this, but I havn't come up with one (yet).

bitplane
sure you can add it!
JonLT
Posts: 152
Joined: Thu Mar 15, 2007 5:47 pm
Location: Denmark

Post by JonLT »

changed the initial post.
Improved the code a bit, the changes should make the code more safe.
Last edited by JonLT on Tue Jul 10, 2007 1:49 pm, edited 1 time in total.
trivtn
Posts: 132
Joined: Tue Jan 17, 2006 12:30 pm
Location: Viet Nam
Contact:

Post by trivtn »

I've tested this code. It's fine. Thanks JonLT !
There's something is fantastic, there's nothing is absolute.
Post Reply