Archived

This topic is now archived and is closed to further replies.

neurokaotix

Rotate mesh to face camera..

Recommended Posts

Guest Anonymous Poster
What you need is billboarding. Why dont you check the SDK? It has a pretty good example of it.

Share this post


Link to post
Share on other sites
mmm I don''t think billboarding is the technique I''m looking for. Think of the meshes as enemies in a first-person shooter; when you are spotted the meshes face you and persue you. My problem is that I''m not sure as to how I can make the meshes face the camera.

Share this post


Link to post
Share on other sites
Here's what you need to do:

Get a vector from the mesh to the camera's current position. Dot-product that with the vector representing the mesh's current "look-at" position. If the dot product is less than 1.0, you know that the mesh must rotate. The question is: which direction to rotate?

To get the proper direction, cross-product the two vectors mentioned above. This will give you a new vector, let's call it crossVect. crossVect will point either up or down, depending on whether your look-at vector is to the left or to the right of the vector pointing at the camera (also depending on what order you set up the input vectors in the cross-product operation).

So, all you need to do is check the sign (+/-) of the y-value of crossVect, and that will tell you whether you need to rotate left or rotate right. Create an appropriate rotation matrix with some arbitrary rotation amount, and update your world matrix.

Run this algorithm every round, and eventually, your mesh will be facing the camera.

Two cautions: this method assumes your game is operating in a land-based game (i.e. this will not work in a 3D space game-type environment). Secondly, this method may produce a slight amount of "jitter" as the mesh adjusts and re-adjusts its heading to get that dot-product down to a perfect 1.0, which it will never quite do.

If anyone else can throw in their two cents regarding cleaner ways of solving this problem, I'd love to hear your suggestions, too, because I'm using this technique in my own game demo.

Good luck!
--Hoozit.

(edit: made my text a little clearer...

[edited by - HoozitWhatzit on July 16, 2002 6:51:48 PM]

Share this post


Link to post
Share on other sites
I dont'' think it''s nearly that hard. All you have to do is create a world matrix and rotate it the negative rotation of the camera. It''s as simple as that. You could do it the long way that HoozitWhatzit said but if you know the rotation values for the camera you don''t need to.

---
My Site
Come join us on IRC in #directxdev @ irc.afternet.org

Share this post


Link to post
Share on other sites
A little code to help you out...

    
// Assumes D3DXVECTOR3 position, direction; that are initialized


D3DXMATRIX matInv, matWorld;
D3DXVECTOR3 look ( position.x + direction.x,
position.y + direction.y,
position.z + direction.z);

D3DXMatrixLookAtLH(&matInv, &position, &look, &D3DXVECTOR3(0,1,0));
D3DXMatrixInverse(&matWorld, Null, &matInv);
d3ddevice->SetTransform(D3DTS_WORLD, &matWorld);

... And then draw the model...


This will let the model face whatever direction you want. (Given that the vertices are relative to the model center.) If you want to make a model face the camera, replace the look-initialization with the camera position.

Edit: Missed a line...

[edited by - nystagmus on July 17, 2002 6:34:26 AM]

Share this post


Link to post
Share on other sites
I''d like to dig a bit deeper into this topic if I could--this problem has vexed me, too.

It sounds like what neurokaotix is trying to do is have monsters in a FPS determine the player''s current position and run toward it (in order to kill the player, I''d guess!)

nyStagmus''s approach would allow you to make the monster _immediately_ face the player/camera, but what if you wanted the monster instead to make a smooth rotation that lasted until he was facing the player? Is there a better way of doing this than the approach I''ve outlined above?

As to RapidStunna''s approach, it would produce an immediate direction for the monster as well, but (and please correct me if I''m wrong) wouldn''t this only work if the monster was in the center of the camera view? I mean, the approach makes the monster face in the inverse direction that the camera is facing, but this would only make the monster actually point toward the camera if the monster was in the camera''s center-of-view. Otherwise the angle would be off by a little bit.

Or maybe I''m full of shit. Again, correct me if I''m wrong.
--Hoozit.

Share this post


Link to post
Share on other sites
hoozit: The neat part with my code is that the model can face any way you would like it to. What neurokaotix needs to do is calculate the angle between the direction it is currently facing and the line to the player. If this is greater than the max rotation value of the model/creature you rotate the max value, else you just rotate directly. Or am I mistaken?

Share this post


Link to post
Share on other sites
nyStagmus: Hmmm...I''m not sure I follow you. I understood the part where you said that you can make the monster face any direction you''d like to (just substitute any look-at point for direction.[x|y|z] in your sample code, right?) So far, you and I have similar approaches: we have a vector to the player, and a vector representing the monster''s current direction.

The next thing you said was to calculate the angle between these two vectors. Which, I gather, would be done using dot-product (well, giving you the cosine of the angle, at least).

After this point you lost me. What did you mean by the max rotation value? Also, what does it mean to ''rotate the max value'' versus ''rotating directly?''

I''m probably coming off pretty dense, apologies...

--Hoozit.

Share this post


Link to post
Share on other sites
Hoozit: I am sorry, I formulated that a bit awkwardly. What I mean is, a creature is only supposed to rotate a maximum angle per unit time (for example 1 degree), right? If the angle it wants to rotate is greater than 1 degree we rotate it by 1 degree, wait for the next frame and begin anew. But if the angle it wants to rotate is less than 1 degree, just let the creature face the player immediately.

Am I making more sense?

Share this post


Link to post
Share on other sites
If your enemies have functions to determine their physics, then it gets more complicated. In my game, the enemy space ships have the same physics and thrusters that the player has. The AI must operate those thrust and turn controls much like a player would.

This makes the opponents move realistically, and lets the player find ways to outmaneuver them or lose them (though it can be tough).

I handle it by getting the angular difference in yaw and pitch between the AI''s direction he''s facing, and the direction to his target or victim. You have to be careful though when using angles due to the transition from 2 pi radians to 0 radians.

I use the angle differences as inputs to the vehicle''s rotation thrusters.

I actually use a bit of feedback control theory (proportional/derivative) to prevent overshoot and improve aim, but that''s getting pretty deep. I can elaborate if you think this is the way you want to go.

Value of good ideas: 10 cents per dozen.
Implementation of the good ideas: Priceless.

Proxima Rebellion - A 3D action sim with a hint of strategy

Share this post


Link to post
Share on other sites
nyStagmus: Ah, now I see what you meant. You were talking about the "last mile" (err...degree) of the rotation--whether to continue rotating the monster or to simply "snap" the monster''s orientation to the destination vector.

This would solve the problem of "shake" that I mentioned before--instead of constantly adjusting and readjusting the monster''s orientation, once he''s snapped to face the camera, he stays there.

However, it still doesn''t address the question of how the monster knows what _direction_ to rotate each round. You could assume that he only rotates in one direction always, but it may look a bit silly sometimes--like John Travolta doing the disco dance...

BS-er: I''m interested in hearing more about your approach. My game is a hovertank-type game where, like you, I use space-style thrust physics for both the player tank and the enemy tanks.

Specifically, how do you calculate the angle between the two vectors and decide which direction to turn? Also, I would be interested in learning about your use of feedback control theory. I may not understand everything, but what I don''t I''ll look up on Google.

--Hoozit.

Share this post


Link to post
Share on other sites
Here's my AI function for making the hover-fighters chase the player (or their intended target as the case may be). Hopefully you can adapt parts of it to your needs, even if you can't drop my function in as a whole. Keep in mind that for my fightercraft, mouse motion controls the strength of the thrust, so fast mouse motion = faster turning. Anyway, for what it's worth:


    

// ______________________________________________________________________________________

void CAerialCombatAgent::Control()
{
// Temporary hack: All units chase you, the player for now.

CGameObject* pTargetObject = g_ObjectManager.GetUserObjectPtr();

bool bUp = false;
bool bDown = false;
bool bFwd = false;
bool bRev = false;
bool bLeft = false;
bool bRight = false;
float PitchDifference = 0;
float YawDifference = 0;
float Distance = 0;

if (pTargetObject)
{
// Difference vector between you the target, and me, the AI.

Vector3 Difference = pTargetObject->m_WorldPosition - m_Object.m_WorldPosition;
Distance = D3DXVec3Length(&Difference);

// I'll ignore you if you are too far away.

if (Distance > 150.0f)
{
// Since I'm idle, I should just right myself.

PitchDifference = m_Object.m_WorldPitch * -0.5;
m_PreviousDistance = 0;
}
else //You are close enough for me to notice

{
// I want to keep within 100 meters of you, so I will apply forward thrust

if (Distance > 100.0f)
{
bFwd = true;
}

// I don't want to be too close to you

if (Distance < 30.0f)
{
bRev = true;
}
// Check to see if you are edging closer to me and I should maneuver.

else if ((m_PreviousDistance - Distance) > 0.01f)
{
// Time-dependent value repeatedly counts from 0 to 120, at the world

// update rate of 20 per second. So it repeats every 6 seconds. the

// value of m_Randomize is different for every AI instance, making

// their maneuver patterns appear different.

int Motion = (g_WorldUpdateCount + m_Randomize) % 120;

// perform maneuver pattern based on the value of the Motion variable.

if (Motion > 90)
{

bLeft = true;
}
else if (Motion > 60)
{
// Move up if not too high

if (m_Object.m_Altitude < 120.0f)
{
bUp = true;
}
else
{
bDown = true;
}
}
else if (Motion > 30)
{
bRight = true;
}
else
{
// Move down if not too close to ground

if (m_Object.m_Altitude > 20.0f)
{
bDown = true;
}
else
{
bUp = true;
}
}
}

// Make the vector from you to me into a unit vector

Difference = Difference / Distance;

// get the vertical angle from my position to your position

float Pitch = asin(-Difference.y);

// Get the horizontal angle from my position to your position

float Yaw = atan2(Difference.x, Difference.z);

// Compute the difference between angle from me to you, and angle I am facing

YawDifference = Yaw - m_Object.m_WorldYaw;

// Correct large angle differences to avoid turning the long way around

// (needs work)

if (YawDifference > Pi)
{
YawDifference = YawDifference - TwoPi;
}
else if (YawDifference < - Pi)
{
YawDifference = YawDifference + TwoPi;
}

PitchDifference = Pitch - m_Object.m_WorldPitch;

// I'll only fire at you if I'm looking your way.

if ((YawDifference < FireThreshold) && (YawDifference > - FireThreshold) &&
(PitchDifference < FireThreshold) && (PitchDifference > - FireThreshold))
{
m_Object.m_bFire = TRUE;
}
else
{
m_Object.m_bFire = FALSE;
}
}
}

// Applying the thrusters

m_Object.ThrustControl(bFwd, bRev, bUp, bDown, bLeft, bRight);

// My yaw effort should be based on the angle difference to you, but also based

// on the speed at which I'm zeroing in on you (YawDifference - m_YawDifference).

// This is basically a simple proportional-derivative control equation, with

// 5.0 and 60.0 determined through trial and experimentation.

float YawEffort = 5.0f * YawDifference + 60.0f * (YawDifference - m_YawDifference);

// Compute my pitch effort similar to my yaw effort.

float PitchEffort = 5.0f * PitchDifference + 60.0f * (PitchDifference - m_PitchDifference);

// Apply the turn controls to the craft

m_Object.TurnControl(YawEffort, PitchEffort);

// Save the results of this pass for use in the next pass through this function.

m_YawDifference = YawDifference;
m_PitchDifference = PitchDifference;
m_PreviousDistance = Distance;

return;
}



Value of good ideas: 10 cents per dozen.
Implementation of the good ideas: Priceless.

Proxima Rebellion - A 3D action sim with a hint of strategy

[edited by - BS-er on July 19, 2002 12:39:46 AM]

Share this post


Link to post
Share on other sites
HoozitWhatzit is closest on track. I''ll post some code later but I''ll describe how I did it in a game I''m working on right now. The technique is the same as billboarding btw, so you can call it billboarding if you want although usually refers to the technique of making a flat rectangle (a quad) face the camera at all times to give the illusion that the texture on that object is actually 3D. I used my billboarding code to both face a quad at the camera and a 3d object at another 3d object, it''s "target".

The basic idea though is to get a vector from the source to the destination, the vector just being a direction (so it is normalized). We then take the cross product of the direction the source is facing, and the direction vector we just derived a sec ago. This is the up vector. Now get get the dot product between the the vectors we just crossed, the lookat vectors. We then take the arc cosine of this angle which gives us a valid angle in radians. Finally, the hard part (unless you use a Direct3D or OpenGL function to help you), you must now rotate the up vector by the angle we just got and store this as a rotation matrix. Now just just multiply this matrix by our objects transform and walla! It now turns towards the target.

If you wanted a gradual turn, you could have just used the up vector to decide which direction to turn and the cross product would have determined how MUCH to turn to get there, which we can just gradually step by the objects turning rate.

Cool, huh?

"Love all, trust a few. Do wrong to none." - Shakespeare

Dirge - Aurelio Reis
www.CodeFortress.com
Current Causes:
Nissan sues Nissan

Share this post


Link to post
Share on other sites
Yes, very cool indeed. Thanks, Dirge. Thanks too, BS-er, your code was very helpful. I think with a combination of these suggestions I should be able to make "pursuit code" that is free of the ''shakedy-shake''.

Out of curiosity, do game programmers still precompute sin, cos, and tan lookup tables for such tasks? Or is hardware fast enough these days that it''s acceptable to use the math.h trigonometric functions as they are in one''s code?

--Hoozit.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I wish somebody would come along and speak English on this topic. I''m reading Jim Adams'' book, Programming RPG''s with DirectX, and I''m about to throw it out the window. The code in his samples work perfectly, but as soon as I port them to my app, KABLAMMO!

The scenario: I''m writing a first-person RPG. I''ve got a mesh at 63, 4, -95. I can move the mesh toward 63,18,56, but I can''t make it face its destination point no matter what. It rotates, but it turns every direction except the one I want.

I''ve tried Adams'' "magic formula" of atan2(x2-x1, z2-z1) to get the angle, and it doesn''t do squat.

Share this post


Link to post
Share on other sites
Not to be rude, but...

Per your example, that ''magic formula'' gives you an angle of 0, which is correct. That tells you the mesh is pointing in the position z-axis.

Another example, let''s say facing in the positive X-axis:

x1 = -10
z1 = 0
x2 = 10
z2 = 0
atan2(x2-x1, z2-z1) = +1.570796 radians (positive rotation clock-wise)

Hmm, the ''magic formula'' works yet again. Once more, let''s try facing in the positive z-axis and neg. x-axis:

x1 = 10
z1 = -10
x2 = -10
z2 = 10
atan2(x2-x1, z2-z1) = -0.785398 radians (neg. rotation)

Again, it works.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
But I don''t need to know which direction my mesh is currently facing -- I want to rotate the mesh to face the destination point I''m trying to move it to....

Source X-Y-Z: 63,4,-95
Dest X-Y-Z: 63,18,56

If I take the atan2(X1-X2, Z1-Z2) of these, I get -- as you said -- 0. According to your "Chars" example in your book, the resulting angle of your "magic formula" tells us how much to rotate our Mesh along the Y axis (at least that''s what you do with it.. the comment says "turn to face the character" and you rotate your character object along the Y axis by Character->Direction). Whatever you''re doing, it works fine. Your characters turn and face the Player at all times. Not sure if that''s because your examples are third-person and mine''s first or what.

But, as you might guess, rotating a Mesh by 0 will do absolutely nothing. Hence, my mesh never turns to face 63,18,56.

Am I making any sense?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Specifically, here''s the code snippet that I was thinking would simply turn my Character object to face the next Route Point. I verified the Route Point and Object coordinates, they all checked out. The snippet is outside of the Frame() loop, if that makes any difference...


// rotate NPC to face its target point

float Direction = atan2(g_NPCObject[1].GetXPos() - Route[RoutePoint].XPos, g_NPCObject[1].GetZPos() - Route[RoutePoint].ZPos);

g_NPCObject[1].Rotate (0.0f, Direction, 0.0f);


(Direction in this case is 3.14159.)

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Sorry, Jim. I''m a moron.

As it turns out, I was using your Yodan.x from your "Chars" example, which is apparently rotated. When I imported it into Milkshape and exported my own .x file, it was facing the route points properly.

Alas, you are da man... *bows reverently*

Share this post


Link to post
Share on other sites