Making a mesh face a direction/point

Started by
3 comments, last by KulSeran 13 years, 4 months ago
I want my meshs to automaticaly face its objective( position to reach ), so each time a new objective is set I do this:

- calculate the desired direction( aimed position - current position )
- calculate the angle between current direction and desired direction
- update direction to desired direction
- rotate mesh by the angle computed on step 2

Since the aimed position and position are always on floor( y = 0.0 ), the rotation is on y only(xz plane), so I though it would be ok, but it doesnt work, the meshs face weird directions, I cant understand what is going on..Should it be working( theres a logical problem Im not seeing)?

And I also very curious on how Id do that if directions can face any way(not only xz plane)..My guess is to calculate the ortogonal vector between them, the angle between them, and rotate arround the vector by this angle..would it work?


Advertisement
I actually just figured out how to do this myself. Assuming you want the z-axis to face the new point (which makes z rotation always 0), and that you have each axis in vector form (like (0,1,0) for the default up axis) you can do this (Look is the z axis, Up is the Y axis, and Right is the x axis in this function).

void PointObject(D3DXVECTOR3 TempPos)
{
Look=TempPos-Position;
D3DXVec3Normalize(&Look, &Look);
D3DXVECTOR3 Tempy=D3DXVECTOR3(0.0f,1.0f,0.0f);
D3DXVECTOR3 Tempx=D3DXVECTOR3(1.0f,0.0f,0.0f);
D3DXVec3Cross(&Right, &Tempy, &Look);
D3DXVec3Cross(&Up, &Look, &Right);
Yaw=acos(D3DXVec3Dot(&Tempx, &Right));
Pitch=acos(D3DXVec3Dot(&Tempy, &Up));
Roll=0.0f;
}

Basically, it subtracts Position (the object's position) from TempPos (the position you want to face) to get the directional vector, normalizes it and that makes the new z-axis. Then it uses a cross product of the default up axis and the new z-axis to get the new x-axis. Then it gets a cross product of the new z and x axis to get the new y axis.

Since z rotation is always zero, and the rotation of the axis you are calculating doesn't affect it's value (rotating around the default up axis would still be (0,1,0)), that leaves one rotation that could affect the axis you are calculating, either the x or y axis (depending on which one you are calculating).

To get the new Yaw, you get a dot product of the default x axis and the new x axis, which gives you the cosine of the angle which shows how much you are rotating around the y axis, because y rotation is the only thing that would affect the x axis. You get the acos of the dot product I just talked about (which is a cosine) and you have the new Yaw. To get Pitch, you do the same thing, you just get the dot product of the default y and new y axis and get the acos of.

Sorry if my explanation doesn't make sense, but the function I gave you should work. If you don't use direct x, any vector math library will work, so just modify the code to what you use if necessary.

In case you don't have the axes in vector form, you can get them in two ways. One, you can get them from the World Transformation matrix of your object. Say that m is a world transformation matrix. m[0][0 1 and 2] is the x, y, and z values of the x axis vector. m[1][0 1 and 2] for the y axis, and m[2][0 1 and 2] for the z axis, and m[3][0 1 and 2] has the x, y, and z position of the object itself. Or, you can use D3DXMatrixRotationAxis to get the axes when you add rotation to the object and use the axes to make the transformation matrix without having to do matrix multiplication (unless your scaling the matrix or something). You just make a matrix that rotates around the axis and apply it to the other two vectors with D3DXVec3TransformCoord or a similar command, like this

void SetYaw(float y)
{
Yaw+=y;
D3DXMATRIX m;
D3DXMatrixRotationAxis(&m, &Up, y);
D3DXVec3TransformCoord(&Look, &Look, &m);
D3DXVec3TransformCoord(&Right, &Right, &m);
//Do the same thing for the Pitch and Roll, just change the axis rotation matrix accordingly
}

Just make sure your axis vectors are set to the default axes when you first make the object, (1,0,0) for the x axis, etc. Since you have the axes in vector form and the position of your object, you can make a matrix and just fill it in like I showed above to make the world transformation matrix for the object. There is some other stuff you need to fill in, I'll edit this post and put it in when I get back to my programming computer.

That was a longer post then I thought it would be, my bad :) I hope this helps.

Edit Here's the code for it where matWorld is a D3DXMATRIX(or any 4 by 4 matrix)

matWorld._11=Right.x;
matWorld._12=Right.y;
matWorld._13=Right.z;
matWorld._14=0;
matWorld._21=Up.x;
matWorld._22=Up.y;
matWorld._23=Up.z;
matWorld._24=0;
matWorld._31=Look.x;
matWorld._32=Look.y;
matWorld._33=Look.z;
matWorld._34=0;
matWorld._41=Position.x;
matWorld._42=Position.y;
matWorld._43=Position.z;
matWorld._44=1;

[Edited by - Kryogenik on November 30, 2010 12:34:30 PM]
Im using xnamath, I kind didnt like your approach of editing the world matrix directly, since with xna math acessing things is a pain..

Did you post the way you would do that or you think I cant do this the way I was thinking?

I did a test scene to try figure out something, so I have a ball centered at origin and another ball that I can move with the keyboard, so I did what I explained in the first post to make the center ball face the moving ball..
It works =D..but just for clockwise rotations D=..Im guessing the function XMVector3AngleBetweenVectors returns the smaller angle(always positive) between vectors..I think that would explain the behavior..

If is not quite clear is like that: If I move the ball arround the center ball in clockwise, the ball face just right( you can say that because if the moving ball is closer, it rotates faster, and if u move away on a straigh line it doesnt move, etc., its clear it is working), but if you starting move in the other direction, the ball keeps rotating always clockwise..

Heres the code Im using:
//updates moving ball world matrix:							//translate:							litblock.dxMesh.mWorld *= XMMatrixTranslationFromVector( vVelocity );							//back to origin:							litblock.dxMesh.mWorld *= XMMatrixTranslationFromVector( -vPosition );							//rotate(reorient):							litblock.dxMesh.mWorld *= XMMatrixRotationY( /*fangletest*/fAngleOrient*(FLOAT)sec );							//back to actual position:							litblock.dxMesh.mWorld *= XMMatrixTranslationFromVector( vPosition );//face center ball:							XMVECTOR aimpos = XMVector3Normalize( vPosition );							aimpos = XMVectorSetW( aimpos, 1.0f );//also direction, cause ball is at origin							static XMVECTOR directiontest = XMVectorSet( 0.0f, 0.0f, 1.0f, 1.0f );							FLOAT fangletest = XMVectorGetX( XMVector3AngleBetweenVectors( aimpos, directiontest ));							directiontest = aimpos;//update direction							//world matrix start as identity							mfacingballWorld *= XMMatrixRotationY( fangletest );
Yeah, so I just puted my brain to remember basic maths..>_> you just keep away from it for a month and they go away from your mind..

So Im getting the angle as I said, and then I compute the cross product checking for the y value, if y is < 0(left turn), then I negate the angle..works like a charm...

But I dont know how I would do that for any direction(not only on xz, again..)I mean, this just works for 2D right? Well, i know 2 vectors are always on a plane..@_@ that is how far I can go..

How I would do that?( find a way to rotate the plane so it align to an axis plane(xy or xz), and then compute the cross? thats looks too overheaded )
The easiest way to "face" a location is just build a look-at matrix.
You set the Z (at) to the normalized direction to the target. Then create a X (left) and Y (up) vector that are perpendicular.

pseudo:
z = normalize( their_pos - my_pos );
y = (0,1,0);
x = y cross z;
y = z cross x;

You do have to check one edge case, if z dot (0,1,0) ~= 1, then you have an issue. You have to pick y = (1,0,0) or something to compensate.

If you want to perform a smooth rotation to look there, use the Slerp operation. The easiest way to do this is create a quaternion out of your current and target rotations. Then Slerp the quaternions by some factor. Then convert back to a matrix.

This topic is closed to new replies.

Advertisement