Rotate a Node, face another node

A forum to store posts deemed exceptionally wise and useful

Rotate a Node, face another node

Postby Gorgon Zola » Mon Feb 09, 2004 3:17 pm

Rotate a node to face another node, LookAt
Walk forward
Walk sideways
==================================

Hi

I've seen in the forum quiet a few questions and answers about
turning scenenodes to walk forward or to face some other scenenode.

I have here some code example that should clarify things a bit for newbies.
This is certainly not the only way to do things, so feel free to post more ways
and/or corrections/enhancements that deal with this sort of problems.
I think this will help every one to find things faster.

The code should work with irrlicht 0.4.2
I assume you have something like this in your code:

Code: Select all
// the initial up direction of the hero
vector3df up(0,1,0);
// the up direction during gameplay
vector3df curup(up);
// the initial forward direction
vector3df vtHeroInitDir(0,0,1);
// the forward direction during gameplay
vector3df vtHeroCurrentDir(vtHeroInitDir);

// the hero of the game
ISceneNode* myHero;

// the bad guy
ISceneNode* myMonster;


Now, every time you want the hero to <b>turn towards<b> the monster,
you just call the following function
(note that this function can also be modified to turn to a point!):

Code: Select all
void faceTarget(irr::scene::ISceneNode& hero, irr::scene::ISceneNode& target){
   using namespace irr;
   using namespace core;
   vector3df dir=vtHeroInitDir;
   vector3df targetdir=target.getPosition()-hero.getPosition();
   
   // now build the rotation matrix
   vector3df z(targetdir-dir);
   z.normalize();
   vector3df x( up.crossProduct(z) );
   x.normalize();
   vector3df y( z.crossProduct(x) );
   y.normalize();
   
   transform(0,0) = x.X;
   transform(0,1) = x.Y;
   transform(0,2) = x.Z;
   transform(0,3) = 0;
   
   transform(1,0) = y.X;
   transform(1,1) = y.Y;
   transform(1,2) = y.Z;
   transform(1,3) = 0;
   
   transform(2,0) = z.X;
   transform(2,1) = z.Y;
   transform(2,2) = z.Z;
   transform(2,3) = 0;
   
   transform(3,0) = 0;
   transform(3,1) = 0;
   transform(3,2) = 0;
   transform(3,3) = 1;
   
   irr::core::vector3df rot=MatrixToEulerAngles(transform);
   transform.transformVect(vtHeroInitDir,vtHeroCurrentDir);
   transform.transformVect(up,curup);
   hero.setRotation(rot);
   vtHeroCurrentDir.normalize();
   curup.normalize();
}



For matrix to eulerangles conversion I've taken the function that Maureen
[url]http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=1288[\url]
has posted. The aproach for the 'faceTarget' function mentioned there doesn't work though.
Also, You have to keep in mind that this function rotates the hero completely, that is,
it doesn't only turn the node in the xz-plane,
which is what you normaly would want in a game where your object's can't fly.

<b>Walking forward</b> is really easy if you update the vector 'vtHeroCurrentDir' every time the direction
changes. just call 'goForward(myHero, vtHeroCurrentDir , MillisSinceLastFrame)'

Code: Select all
void goForward(ISceneNode& hero, vector3df& direction, long MillisSinceLastFrame){
   // scale the direction to match different framerates
   vector3df newPos=hero.getPosition()+ (direction * MillisSinceLastFrame/1000.0);
   hero->setPosition(newPos);
}


<b>Going sideways</b> is also a minor problem.

Code: Select all
void goSide(ISceneNode& hero, vector3df& forward, vector3df& updir, bool left){
   vector3df s=forward.crossProduct(updir);
   s.normalize();
   vector3df newPos=hero.getPosition() + ( s * (left?1:-1) );
}


cheers
Gorgon Zola
Gorgon Zola
 
Posts: 118
Joined: Thu Sep 18, 2003 10:05 pm
Location: switzerland

Postby jox » Sat Apr 24, 2004 8:30 pm

The functionality for faceTarget() is now part of the engine (at least since version 0.6). It's all in core::matrix4.

To get the rotation matrix use:

void matrix4::buildCameraLookAtMatrixLH(position, target, upVector);

(there's also buildCameraLookAtMatrixRH() (right handed))

To achieve the same as in the example above, you could use it somehow like this:
Code: Select all
...
translation.buildCameraLookAtMatrixLH(vector3df(0,0,0), targetdir, up);
...
(so the position doesn't get affected)

The MatrixToEulerAngles() is also there:

vector3df matrix4::getRotationDegrees();
jox
Bug Slayer
 
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Postby jox » Thu Apr 29, 2004 1:56 pm

To make Gorgons helpful faceTarget code work with Irrlicht 0.6 I had to mirror the matrix assignments. (Maybe this is because of matrix4.h has changed in some way? It seems to have some inconsistensies anyway, but I'm not sure yet, still investigating).

Also the targetdir-dir is actually a bug. You don't need to substract dir from targetdir. It adds a position offset which makes it behave not quite correctly. dir is not needed in the calculation at all, only up. (you still can update dir at the end for further use.)

Here is the updated faceTarget function:

Code: Select all
void faceTarget(irr::scene::ISceneNode& hero, irr::scene::ISceneNode& target){
   using namespace irr;
   using namespace core;
   vector3df dir=vtHeroInitDir;
   vector3df targetdir=target.getPosition()-hero.getPosition();
   
   // now build the rotation matrix
   vector3df z(targetdir);
   z.normalize();
   vector3df x( up.crossProduct(z) );
   x.normalize();
   vector3df y( z.crossProduct(x) );
   y.normalize();
   
   transform(0,0) = x.X;
   transform(0,1) = y.X;
   transform(0,2) = z.X;
   transform(0,3) = 0;
   
   transform(1,0) = x.Y;
   transform(1,1) = y.Y;
   transform(1,2) = z.Y;
   transform(1,3) = 0;
   
   transform(2,0) = x.Z;
   transform(2,1) = y.Z;
   transform(2,2) = z.Z;
   transform(2,3) = 0;
   
   transform(3,0) = 0;
   transform(3,1) = 0;
   transform(3,2) = 0;
   transform(3,3) = 1;
   
   irr::core::vector3df rot=MatrixToEulerAngles(transform);
   transform.transformVect(vtHeroInitDir,vtHeroCurrentDir);
   transform.transformVect(up,curup);
   hero.setRotation(rot);
   vtHeroCurrentDir.normalize();
   curup.normalize();
}


hope it's correct now!

cheese, :)
jox
jox
Bug Slayer
 
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Postby jox » Thu Apr 29, 2004 2:06 pm

Oh, and the matrix4::getRotationDegrees(); (matrix to eulerangles) has a bug in Irrlicht v0.6 which I fixed:

http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=2234

(btw. the code from Maureen that Gorgon were refering to is not working properly neither)
jox
Bug Slayer
 
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Postby Gorgon Zola » Thu Apr 29, 2004 4:10 pm

:) Cool thanks jox
now it's helpfull and right :D
Gorgon Zola
 
Posts: 118
Joined: Thu Sep 18, 2003 10:05 pm
Location: switzerland

Postby smartwhiz » Fri Jun 11, 2004 8:35 pm

ok.... wht if i am designing a RPG type game where the target position if found and i want my model to look at that point when moving towards it...? plz help!!!
smartwhiz
 
Posts: 120
Joined: Wed Jan 14, 2004 6:03 pm
Location: India

Postby Tyn » Fri Jun 11, 2004 8:53 pm

Put it in the drawing loop. Best way I can think of is to have the functionality always there and to have some kind of flag to say that the character is staring at something. Then store the look at target in an entity class ( you will have to create one sooner or later ).

I think I need to get a little more aquanted with matricies, I don't really know much about them. One thing: When a node rotates, it appears to rotate on it's origin rather than the centre of the model. Is this correct?
Tyn
 
Posts: 932
Joined: Thu Nov 20, 2003 7:53 pm
Location: England

Postby Gorgon Zola » Fri Jun 11, 2004 9:13 pm

@smartwhiz: I agree with Tyn. exept that my flag is a pointer to the node i want to follow if its valid(!=NULL) a scenenode animator turns the node to face that node.

@Tyn :
Yes, rotation is alway around the local origin and not the center of the model
I have made all my models such that the center of the model will be the nodes origin. This way I don't have problems with physic libraries etc. that sometimes tend to define the center of mass to be the origin (at least ODE bbehaves like that :wink: )
Gorgon Zola
 
Posts: 118
Joined: Thu Sep 18, 2003 10:05 pm
Location: switzerland

Postby Tyn » Fri Jun 11, 2004 9:18 pm

Hmm, I prefer to model in the +ve. Makes things simple for me when I position things, something I have always done. Pretty sure this is what most games do from viewing model files, I'll have to research it a little. I wonder if there is a way to offset the rotation?

I also wonder if the FlyCircle animator could be of use with a 0 radius?
Tyn
 
Posts: 932
Joined: Thu Nov 20, 2003 7:53 pm
Location: England

Postby Gorgon Zola » Fri Jun 11, 2004 9:23 pm

You could always use a dummy scenenode (I thinks there's one already availabel through the scenemanager) as parent which you use as rotation offset. I doubt though that this will be easier .
Gorgon Zola
 
Posts: 118
Joined: Thu Sep 18, 2003 10:05 pm
Location: switzerland

Postby smartwhiz » Sat Jun 12, 2004 5:17 pm

think it in this way i am having two point vector3df's ok..........
one -> the curr position of the model
2nd->the target position....
now how do i turn the model at the current position to point at target point
smartwhiz
 
Posts: 120
Joined: Wed Jan 14, 2004 6:03 pm
Location: India

Postby BableOff » Mon Jan 09, 2006 7:50 am

I'm having a problem with this code.

The target(my goblin charector with attached camera) and my node are looking at each other, if I straft the target to the right the node should turn left and still be looking at me.

In this instance if I straft right the node turns right also and does not face me.

example:

Image
Image
Image

Here is my code:

Code: Select all
void faceTarget(irr::core::vector3df targetPos) {

  irr::core::vector3df up(0,1,0);
  irr::core::vector3df curup(up);
  irr::core::vector3df forward(0,0,1);
  irr::core::vector3df curforward(forward);

   vector3df dir=forward;
   vector3df targetdir = targetPos - mynode->getPosition();

   // now build the rotation matrix
   vector3df z(targetdir);
   z.normalize();
   vector3df x( up.crossProduct(z) );
   x.normalize();
   vector3df y( z.crossProduct(x) );
   y.normalize();

   irr::core::matrix4 transform;
   transform(0,0) = x.X;
   transform(0,1) = y.X;
   transform(0,2) = z.X;
   transform(0,3) = 0;

   transform(1,0) = x.Y;
   transform(1,1) = y.Y;
   transform(1,2) = z.Y;
   transform(1,3) = 0;

   transform(2,0) = x.Z;
   transform(2,1) = y.Z;
   transform(2,2) = z.Z;
   transform(2,3) = 0;

   transform(3,0) = 0;
   transform(3,1) = 0;
   transform(3,2) = 0;
   transform(3,3) = 1;

   irr::core::vector3df rot = myMatrixToEulerAngles(transform);
   transform.transformVect(forward,curforward);
   transform.transformVect(up,curup);
   mynode->setRotation(rot);
   curforward.normalize();
   curup.normalize();
}

irr::core::vector3df myMatrixToEulerAngles(irr::core::matrix4& mat)
{
  irr::f32 angle_y,D,C,angle_x,angle_z,frx,fry;
  angle_y = D = -asin( mat(0,2) );
  C           =  cos( angle_y );
  angle_y    *= (f32)irr::core::GRAD_PI;
  if ( fabs( C ) > 0.005 )             /* Gimball lock? */
  {
    frx      =  mat(2,2) / C;
    fry      = -mat(1,2)  / C;
    angle_x  = atan2( fry, frx ) * (f32)irr::core::GRAD_PI;
    frx      =  mat(0,0) / C;
    fry      = -mat(0,1) / C;
    angle_z  = atan2( fry, frx ) * (f32)irr::core::GRAD_PI;
  }
  else                                 /* Gimball lock has occurred */
  {
    angle_x  = 0;
    frx      = mat(1,1);
    fry      = mat(1,0);
    angle_z  = atan2( fry, frx ) * (f32)irr::core::GRAD_PI;
  }
  angle_x = angle_x<0 ? angle_x+360.0f : angle_x;
  angle_y = angle_y<0 ? angle_y+360.0f : angle_y;
  angle_z = angle_z<0 ? angle_z+360.0f : angle_z;
  return irr::core::vector3df(angle_x,angle_y,angle_z);
}

And I execute this from the rendering loop as such:
Code: Select all
  irr::core::vector3df target = myself->getPosition();
  faceTarget(target);

Any ideas why he is turing the wrong direction?
BableOff
 

Postby BableOff » Mon Jan 09, 2006 10:05 am

My solution
mynode is global

Code: Select all
void faceTarget(irr::core::vector3df targetPos) {

  core::vector3df nodePos = targetPos - mynode->getPosition();
  float degree = atan(nodePos.Z/nodePos.X) * (180.0f / irr::core::PI);
  if((targetPos.X - mynode->getPosition().X) > 0) {
   degree = 90 - degree;
  } else {
    degree = -90 - degree;
  }
  degree -= 90;
  mynode->setRotation(vector3df(0,(float) degree, 0));
}
BableOff
 

Postby BableOff » Fri Jan 13, 2006 2:31 am

Updated solution.

Fixing a bug when targetPos.X == myNode->getPosition().X the rotation was off 180 degrees;

I have not had this issue with the Z axis, only the X.

myNode is global

myRotation is global but you can use "float degree" insted of "myRotation.Y" and set myNode->setRotation(vector3df(0,(float) degree, 0));

Code: Select all
void faceTarget(irr::core::vector3df targetPos) {
  core::vector3df nodePos = targetPos - myNode->getPosition();
  myRotation.Y = atan(nodePos.Z/nodePos.X) * (180.0f / irr::core::PI);
  if((targetPos.X - myNode->getPosition().X) > 0) {
    myRotation.Y = 90 - myRotation.Y;
  } else if((targetPos.X - myNode->getPosition().X) < 0) {
    myRotation.Y = -90 - myRotation.Y;
  }
  myRotation.Y -= 90;
  myNode->setRotation(myRotation);
}
BableOff
 

Postby Nick_Japan » Mon Mar 27, 2006 11:58 pm

I second BableOff's solution - works for me, only I found that I didn't need the last myRotation.Y -= 90; commenting that out gives me the correct angle.
Nick_Japan
 
Posts: 98
Joined: Mon Dec 13, 2004 11:47 am
Location: Japan

Next

Return to FAQs, Tutorials, Howtos, and external tool lists

Who is online

Users browsing this forum: No registered users and 0 guests