smoothly rotate a node

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
diogoecomp
Posts: 30
Joined: Thu Jun 11, 2009 3:36 am

smoothly rotate a node

Post by diogoecomp »

I'm trying to rotate a moving node using the following methods that I found in the forum:

Code: Select all

void moveTo(IAnimatedMeshSceneNode* node, vector3df finalPosition){
vector3df actualPosition = node->getPosition();
core::vector3df posDiff = finalPosition - actualPosition;
f32 degree = actualPosition.Y;
posDiff.normalize();
if (posDiff.X != 0.0f || posDiff.Z != 0.0f)
	degree = atan2(posDiff.X,posDiff.Z) * core::RADTODEG;
node->setRotation(core::vector3df(0,degree,0));
...
}
with this method I got some reasonable values for the degree, like 56, 57, 36 and so on. the problem is that the node is "shaking", changing very rapidly its direction, I think because of the high number of setRotation that I apply.

The other way is this one:

Code: Select all

void moveTo(IAnimatedMeshSceneNode* node, vector3df finalPosition){
core::quaternion qt;
core::quaternion rot = qt.rotationFromTo(actualPosition, finalPosition);
vector3df degrees = rot.getMatrix().getRotationDegrees();
node->setRotation(degrees);
...
}
this method is giving me degrees around 0(zero), a little less or a little more, and the node almost doesn't change its rotation. I don't know what is wrong.

the positions are like this:
1.350000 0.000000 1.720000
1.340000 0.000000 1.760000
1.340000 0.000000 1.800000
1.370000 0.000000 1.820000
1.390000 0.000000 1.850000
1.390000 0.000000 1.900000
1.390000 0.000000 1.940000
1.420000 0.000000 1.960000
1.430000 0.000000 1.980000
1.440000 0.000000 2.020000
1.430000 0.000000 2.060000
1.450000 0.000000 2.100000
1.460000 0.000000 2.140000
1.490000 0.000000 2.170000
1.530000 0.000000 2.180000

Is there something to solve this problem with quaternions? And someone has a suggestion of how to rotate my node in a smooth way?

thanks in advance.
arras
Posts: 1622
Joined: Mon Apr 05, 2004 8:35 am
Location: Slovakia
Contact:

Post by arras »

Code: Select all

f32 degree = actualPosition.Y;
Do you really expect to get angle from this code? You should learn bit more about vectors and trigonometry.

To get Y rotation out of your vector you need to use:

Code: Select all

f32 degree = posDiff.getHorizontalAngle().Y
Edit: OK I see that you actually do not use that value ...but then why you assign it? Any way, getHorizontalAngle() should give you correct angle (it gives both X and Y rotation in case you need it).

Edit2:

Code: Select all

degree = atan2(posDiff.X,posDiff.Z) * core::RADTODEG;
This line seems to be correct so error is in some other part of your code probably.

Edit3: Also you dont need this:

Code: Select all

if (posDiff.X != 0.0f || posDiff.Z != 0.0f) 
atan2 will accept 0 values happily.
diogoecomp
Posts: 30
Joined: Thu Jun 11, 2009 3:36 am

Post by diogoecomp »

Like I said, I found both codes here in the forum. The code that you have correctly analised I've found here and here. Now about your analysis, you are right, there are some strange lines in that code. I did your suggestions and it seems to be working well. The final code is now like this:

Code: Select all

void moveTo(IAnimatedMeshSceneNode* node, vector3df finalPosition)
{
vector3df actualPosition = node->getPosition();
core::vector3df posDiff = finalPosition - actualPosition;
posDiff.normalize();
f32	degree = atan2(posDiff.X,posDiff.Z) * core::RADTODEG;
node->setRotation(core::vector3df(0,degree,0));
...
}
Thanks for that :wink: .
The code is working well, giving me the angles that I expect. The problem is that the node rotation changes at 24fps and because of that it seems to "vibrate" around the Y asis. Would you have any suggestion on how to deal with this so the node can rotate smoothly?
arras
Posts: 1622
Joined: Mon Apr 05, 2004 8:35 am
Location: Slovakia
Contact:

Post by arras »

There must be something wrong with rest of your code. I made test program and it runs fine for me. Check it yourself:

Code: Select all

// use mouse left click to position target
// use arrow keys to move camera around

#include <irrlicht.h>
using namespace irr;



class FollowAnimator : public scene::ISceneNodeAnimator
{
   u32 LastTime;
   f32 Speed;
   scene::ISceneNode *Target;

public:

   FollowAnimator(scene::ISceneNode *target, f32 speedPerSecond = 1.0f)
   {
      Target = target;
      Speed = speedPerSecond;
      LastTime = 0;
   }

   void animateNode(scene::ISceneNode* node, u32 timeMs)
   {
      if (!Target) return;

      if(LastTime == 0) LastTime = timeMs;
      u32 deltaTime = timeMs - LastTime;
      if(deltaTime > 0) LastTime = timeMs;

      core::vector3df pos = node->getPosition();
      core::vector3df tar = Target->getPosition();

      if(pos == tar) return;

      core::vector3df v = tar - pos;

      core::vector3df rot;
      rot.Y = v.getHorizontalAngle().Y;

      f32 speed = Speed * (f32)deltaTime;
      if(speed > v.getLength())
      {
         node->setPosition(tar);
         node->setRotation(rot);
         return;
      }

      v.normalize();

      v *= speed;

      pos += v;

      node->setPosition(pos);
      node->setRotation(rot);
   }

   scene::ISceneNodeAnimator* createClone(scene::ISceneNode* node,
      scene::ISceneManager* newManager = 0)
   {
      FollowAnimator* clone = new FollowAnimator(Target, Speed);
      return clone;
   }
};



class CameraAnimator : public scene::ISceneNodeAnimator
{
   bool Right;
   bool Left;
   bool Up;
   bool Down;

   u32 LastTime;

   f32 Speed;

public:

   CameraAnimator(f32 speedPerSecond = 1.0f)
   {
      Speed = speedPerSecond;

      LastTime = 0;

      Right = false;
      Left = false;
      Up = false;
      Down = false;
   }

   void animateNode(scene::ISceneNode* node, u32 timeMs)
   {
      if (node->getType() != scene::ESNT_CAMERA) return;

      scene::ICameraSceneNode* camera;
      camera = static_cast<scene::ICameraSceneNode*>(node);

      if(LastTime == 0) LastTime = timeMs;
      u32 deltaTime = timeMs - LastTime;
      if(deltaTime > 0) LastTime = timeMs;

      core::vector3df pos = camera->getPosition();
      core::vector3df tar = camera->getTarget();

      f32 speed = Speed * (f32)deltaTime;

      if(Right)
      {
         pos.X += speed;
         tar.X += speed;
      }
      if(Left)
      {
         pos.X -= speed;
         tar.X -= speed;
      }
      if(Up)
      {
         pos.Z += speed;
         tar.Z += speed;
      }
      if(Down)
      {
         pos.Z -= speed;
         tar.Z -= speed;
      }

      camera->setPosition(pos);
      camera->setTarget(tar);
   }

   scene::ISceneNodeAnimator* createClone(scene::ISceneNode* node,
      scene::ISceneManager* newManager = 0)
   {
      CameraAnimator* clone = new CameraAnimator(Speed);
      return clone;
   }

   bool isEventReceiverEnabled() const
   {
      return true;
   }


   virtual bool OnEvent(const SEvent& event)
   {
      if(event.EventType == EET_KEY_INPUT_EVENT)
      {
         switch(event.KeyInput.Key)
         {
            case KEY_RIGHT: Right = event.KeyInput.PressedDown; return true;
            case KEY_LEFT: Left = event.KeyInput.PressedDown; return true;

            case KEY_UP: Up = event.KeyInput.PressedDown; return true;
            case KEY_DOWN: Down = event.KeyInput.PressedDown; return true;

            default: break;
         }
		}
		return false;
   }
};



scene::ISceneNode *Target;
scene::ICameraSceneNode *Camera;

class EventReceiver : public IEventReceiver
{
public:

   virtual bool OnEvent(const SEvent &event)
	{
		if(event.EventType == EET_MOUSE_INPUT_EVENT )
      {
         if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
         {
            scene::SViewFrustum frustum(*Camera->getViewFrustum());

            core::vector3df xf = frustum.getFarRightUp() - frustum.getFarLeftUp();
            core::vector3df yf = frustum.getFarLeftDown() - frustum.getFarLeftUp();

            f32 xs = (f32)event.MouseInput.X / (f32)640;
            f32 ys = (f32)event.MouseInput.Y / (f32)480;

            xf *= xs;
            yf *= ys;

            core::vector3df v = xf + yf + frustum.getFarLeftUp();

            core::vector3df cpos = Camera->getPosition();
            core::vector3df cvec = v - cpos;

            core::vector3df pos;
            core::plane3df ground(0,0,0, 0,1,0);
            ground.getIntersectionWithLine(cpos, cvec, pos);

            Target->setPosition(pos);

            return true;
         }
		}

		return false;
	}

}Receiver;



int main()
{
   IrrlichtDevice *device = createDevice(video::EDT_OPENGL,
      core::dimension2d<s32>(640, 480), 16, false, false, false, &Receiver);

   video::IVideoDriver* driver = device->getVideoDriver();
   scene::ISceneManager* smgr = device->getSceneManager();

   scene::ISceneNode *node = smgr->addCubeSceneNode();
   node->getMaterial(0).setFlag(video::EMF_LIGHTING, false);
   node->getMaterial(0).setFlag(video::EMF_WIREFRAME, true);
   node->setScale(core::vector3df(1,0.6,2));

   Target = smgr->addSphereSceneNode(1.0f);
   Target->getMaterial(0).setFlag(video::EMF_LIGHTING, false);
   Target->getMaterial(0).setFlag(video::EMF_WIREFRAME, true);

   FollowAnimator *flwAnim = new FollowAnimator(Target, 0.1f);
   node->addAnimator(flwAnim);
   flwAnim->drop();

   Camera = smgr->addCameraSceneNode(0, core::vector3df(0,70,-70),
      core::vector3df(0,0,0));

   CameraAnimator *camAnim = new CameraAnimator(0.1f);
   Camera->addAnimator(camAnim);
   camAnim->drop();

   while(device->run())
   {
      driver->beginScene(true, true, video::SColor(0,200,200,200));

      smgr->drawAll();

      driver->endScene();
   }

   device->drop();

   return 0;
}
diogoecomp
Posts: 30
Joined: Thu Jun 11, 2009 3:36 am

Post by diogoecomp »

Thanks for your help.

I did some videos showing what I'm talking about.

version 1 - shows the problem of the node shaking around Y.
version 2 - a solution that I found, using the medium value from the next 5 angles as the rotation angle. I know that this is not a good solution but is the only thing that came to my mind.:roll:

the videos are here: http://www.megaupload.com/?d=XXZDXMOH

In my case, there is no interaction between the user and the animation, the nodes just follow a script, going from one point to another using those positions as I have shown you before. I think they shake because the positions are very close to each other. Using your code, if you try to click very fast in a circle shape you will have the results that I am having. The node will move like the Robocop. :)

PS: in the videos you can notice other problem: the nodes are crossing the floor. I think that is a problem with the b3d model, whose center should be moved to its feet, or something like that.
Post Reply