## News:

*NEW* Current Version on STEAM16.793

Webchat:
Visit the chat

## Rotate a 3D object to align with a vector

#### Slydog

I'm very new to 3D programming, and therefore 3D math.

I'm creating a path system where you create a bunch of path points and have an object (model, camera) follow that path.
Its working fairly well and follows the path, interpolating the position between the current two points based on those points time duration.  (It's only a linear interpolating for now, no smoothing).

I'm trying to implement an 'Orient To Path' option that will rotate the object to face the direction of the current path.
(Similar to the camera's 'LookAt' parameters).
So the numbers I have are the path start point and end point, and they are stored in a 3D vector TYPE (x,y,z).
I can take the difference of the two (end point - start point) to end up with one vector (named 'v') starting at (0,0,0).

Now, I want to rotate my object to match that vector's angle/direction.
I have the 'Y' rotation working with this:
v_rotate.y = ATAN(v.z, v.x)
But even that acts weird and I had to add 180 or something to get it working as expected.
I'm not even sure of the range of numbers ATAN returns?  (-180 to 180?)  So then just add 180?
Or I've seen this?:  v_rotate.y = MOD(ATAN(v.z, v.x), 360)

But, I can't even come close to getting the 'X' rotation working.  (And I figured out that the 'Z' rotation is needed too?)
I had thought that if I rotate on the 'Y' axis first so I'm heading in the desired direction, then I could just figure out my X angle (relative to the direction I'm facing, ie. how much to tilt up or down).  It would be the same formula somewhat.  But I think I've come to learn that the 'X' rotation is always 'World' aligned?  And I can't make it relative to the new 'Y' rotation?  I hope that makes sense.

I think I have to somehow figure out this 'Angle' for the 'X' rotation, and split that value between 'X' and 'Z' somehow?

I've tried this many times and have failed.  (I find so many 2D examples, but that third dimension is eluding me!)
Any ideas?

Thanks,
-Doug
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

#### Slydog

#1
Would using the Entity System make rotating easier?
ie: is there a built in 'Look At' option or something?
Or can I change an object's X axis relative to that object, and not the world?
So therefore I would only need to set its Y and X (and not Z)?

Or, is there a fancy way to use 'X_ROTATION' to do this?
I don't understand the parameters, besides using it the standard way such as:
X_ROTATION 90, 0, 1, 0 (would rotate on object 90 degrees on its Y axis).

But I've seem people use numbers other than 1 for the axis, such as "0, -1, 0", or "3, 4, 5", what would that do?

If I tried:
X_ROTATION 45, 1, 0, 1 - would that just rotate an object 45 degrees on both X and Z each?  Like the following:

X_ROTATION 45, 1, 0, 0
X_ROTATION 45, 0, 0, 1

Or does it split the value between the axis, or something else?
(ie: creates a new axis to rotate around, somehow combing X and Z?)

I hope I don't have to get into matrix math!!

Thanks again.
- Doug
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

#### Kitty Hello

if you have 3 dimensional direction you want to align to, it's pretty easy.
Just normalize that direction vector to the lenght of 1 (divide all components by SQR(x*x+y*y+z*z) )
Then (the vector is x,y,z) make a matrix from that:
local mat[]
dimdata mat[], x,0,0,0,  0,y,0,0, 0,0,z,1
and use X_MULTMATRIX mat[]

#### Hemlos

You can use 2 atans, but you need to add angles also.

Matrix is definetly the way to go, producing same results with less math.
Bing ChatGpt is pretty smart

#### Slydog

#4
Thanks for the ideas.

I've been playing with the X_MULTMATRIX a bit this weekend, but so far no luck.
I've never used this command before, and I'm not sure exactly how to use it, or where to put what values in the mat[] array.
I'm not sure if I have to push/pop the matrix, if so, when?
I've tried different values in different matrix array positions.
Is the last set of 4 values supposed to be zero?  Or can I put the position in there and skip the X_MOVEMENT command?

What I see (depending of what 'tweaks' I try) is either:
- the player disappears while this code runs [When I put the position into the matrix maybe, or maybe that causes the next problem]
- resets to the [0,0,0] position
- stretches and flickers beyond recognition [most common]

Here's what I've tried (using some code from the forums):
(my code looks very different, just simplified for the forum)

Code (glbasic) Select
`CONSTANT kTollerance = 0.0001// Subtract v2 from v1FUNCTION Vector_Subtract AS TVector: v1 AS TVector, v2 AS TVector   LOCAL rv AS TVector   rv.x = v1.x - v2.x   rv.y = v1.y - v2.y   rv.z = v1.z - v2.z   RETURN rvENDFUNCTION// Normalize a vector so it's length = 1 and apply toleranceFUNCTION Vector_Normalize: v AS TVector LOCAL m m = SQR(v.x*v.x + v.y*v.y + v.z*v.z) IF m <= kTollerance THEN m = 1 v.x = v.x / m v.y = v.y / m v.z = v.z / m IF ABS(v.x) < kTollerance THEN v.x = 0 IF ABS(v.y) < kTollerance THEN v.y = 0 IF ABS(v.z) < kTollerance THEN v.z = 0ENDFUNCTION// p_start - starting point vector// p_end   - end point vectorFUNCTION Player_Draw:  LOCAL rotation as TVector  rotation = Vector_Subtract(p_end, p_start)  Vector_Normalize(rotation)  DIM mat[16]  mat[ 0] = rotation.x  mat[ 1] = 0  mat[ 2] = 0  mat[ 3] = 0  mat[ 4] = 0  mat[ 5] = rotation.y  mat[ 6] = 0  mat[ 7] = 0  mat[ 8] = 0  mat[ 9] = 0  mat[10] = rotation.z  mat[11] = 1  mat[12] = 0  mat[13] = 0  mat[14] = 0  mat[15] = 0  X_MOVEMENT position.x, position.y, position.z// X_PUSHMATRIX here?  X_MULTMATRIX mat[]  X_DRAWOBJ player_id, 0END FUNCTION`

Just a thought.
I had the rotation on the Y axis working before the X_MULTMATRIX code was added.
Can I just use the X_PUSHMATRIX after I rotate on the Y, then rotate on the X to look up or down (determined using the ATAN command again) to rotate relative to the player and not the world?

My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

#### Kitty Hello

Try these fixes:
Code (glbasic) Select
`  mat[12]=position.x  mat[13]=position.y  mat[14]=position.z  mat[15] = 1 // ONE HERE - important!!  // if you have 12,13,14 empty, the commented lines would apply  // X_MOVEMENT position.x, position.y, position.z  // X_PUSHMATRIX  X_MULTMATRIX mat[]  X_DRAWOBJ player_id, 0  // X_POPMATRIX`

The matrix you provide in X_MULTMARIX is simply an OpenGL matrix, which is a vector [0,1,2,3] for the direction and scaling of the X-axis, [4,5,6,7] for the y axis and [8,9,10,11] for the z axis. [12,13,14,15] is the x_movement, so you can put that in one matrix call.

#### Slydog

Getting close.  I found a good explanation of how to do this online at:
http://www.opengl.org/resources/faq/technical/lookat.cpp

I created a 'Look At' function, that takes the id of the Entity to rotate, and a vector (v) as a point to look at.

Code (glbasic) Select
`FUNCTION Entity_LookAt: id%, v AS TVector LOCAL ex% LOCAL v_at AS TVector LOCAL v_up AS TVector LOCAL v_xaxis AS TVector ex = Entity_Find(id) IF ex <= 0 THEN RETURN v_up = Vector_New(0.0, 1.0, 0.0) v_at = Vector_Subtract(v, _Entity[ex].position) Vector_Normalize(v_at) v_xaxis = Vector_CrossProduct(v_at, v_up) Vector_Normalize(v_xaxis) v_up = Vector_CrossProduct(v_xaxis, v_at) Vector_Normalize(v_up) DIM _Entity[ex].matrix[16] _Entity[ex].matrix[ 0] = v_xaxis.x _Entity[ex].matrix[ 1] = v_xaxis.y _Entity[ex].matrix[ 2] = v_xaxis.z _Entity[ex].matrix[ 3] = 0 _Entity[ex].matrix[ 4] = v_up.x _Entity[ex].matrix[ 5] = v_up.y _Entity[ex].matrix[ 6] = v_up.z _Entity[ex].matrix[ 7] = 0 _Entity[ex].matrix[ 8] = v_at.x _Entity[ex].matrix[ 9] = v_at.y _Entity[ex].matrix[10] = v_at.z _Entity[ex].matrix[11] = 0 _Entity[ex].matrix[12] = _Entity[ex].position.x _Entity[ex].matrix[13] = _Entity[ex].position.y _Entity[ex].matrix[14] = _Entity[ex].position.z _Entity[ex].matrix[15] = 1ENDFUNCTION// Subtract v2 from v1FUNCTION Vector_Subtract AS TVector: v1 AS TVector, v2 AS TVector   LOCAL rv AS TVector   rv.x = v1.x - v2.x   rv.y = v1.y - v2.y   rv.z = v1.z - v2.z   RETURN rvENDFUNCTION// Normalize a vector so it's length = 1FUNCTION Vector_Normalize: v AS TVector LOCAL m m = SQR(v.x*v.x + v.y*v.y + v.z*v.z) IF m <= kTollerance THEN m = 1 v.x = v.x / m v.y = v.y / m v.z = v.z / m IF ABS(v.x) < kTollerance THEN v.x = 0 IF ABS(v.y) < kTollerance THEN v.y = 0 IF ABS(v.z) < kTollerance THEN v.z = 0ENDFUNCTION// Takes v1 AND v2 AND returns the cross product v1 X v2.// The cross product is a vector perpendicular TO both v1 AND v2// This is the normal of 2 vectorsFUNCTION Vector_CrossProduct AS TVector: v1 AS TVector, v2 AS TVector   LOCAL rv AS TVector   rv.x = (v1.y * v2.z) - (v1.z * v2.y)   rv.y = (v1.z * v2.x) - (v1.x * v2.z)   rv.z = (v1.x * v2.y) - (v1.y * v2.x)   RETURN rvENDFUNCTION`

And I use it like:
Code (glbasic) Select
`X_MOVEMENT 0, 0, 0X_ROTATION 0, 1, 0, 0X_ROTATION 0, 0, 1, 0X_ROTATION 0, 0, 0, 1X_MULTMATRIX _Entity[ex].matrix[]X_DRAWOBJ  _Entity[ex].id_object, 0`

(Is there a better way to reset the matrix?  (Identity Matrix?))
Now, it rotates and faces properly towards a point.

BUT now it displays the player object as black (no colours) while its using this code, but fine otherwise.
Is it something to do with the normals?
I've tried using 'X_AUTONORMALS' just before the X_MOVEMENT command, but with no luck.
Any ideas?
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

#### Slydog

I'm not sure why, but if I change the Cull Mode back to '0' (both sides) it works.

So I'm guessing that when the matrix is multiplied onto the stack, either:
- my normals get altered (but then why would I see them with Cull Mode '0'?)
- my normals get flipped inside out (but I would think you would see nothing, and not black)

I'm not sure of the performance hit setting Cull Mode to '0' (both sides), but it works, so I'm happy.

Oh, and the Entity Type and functions my code refers to is my own Entity system (very basic), and not the main one from the forums.
It should be very simple to convert the 'Entity_LookAt()' function to work for any vectors/points.

Thanks for all the help.
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

#### Slydog

#8
Ok, here it is converted to a Generic 'LookAt' function:

Code (glbasic) Select
`TYPE TVector x y zENDTYPE// matrix[]    - matrix array to hold the results, for use later at rendering// v_look_from - Point to look from// v_look_to   - Point to look atFUNCTION Vector_LookAt: matrix[], v_look_from AS TVector, v_look_to AS TVector LOCAL v_at AS TVector LOCAL v_up AS TVector LOCAL v_xaxis AS TVector v_at = Vector_Subtract(v_look_to, v_look_from) Vector_Normalize(v_at) v_up = Vector_New(0.0, 1.0, 0.0) v_xaxis = Vector_CrossProduct(v_at, v_up) Vector_Normalize(v_xaxis) v_up = Vector_CrossProduct(v_xaxis, v_at) Vector_Normalize(v_up) DIM matrix[16] matrix[ 0] = v_xaxis.x matrix[ 1] = v_xaxis.y matrix[ 2] = v_xaxis.z matrix[ 3] = 0 matrix[ 4] = v_up.x matrix[ 5] = v_up.y matrix[ 6] = v_up.z matrix[ 7] = 0 matrix[ 8] = v_at.x matrix[ 9] = v_at.y matrix[10] = v_at.z matrix[11] = 0 matrix[12] = v_look_from.x matrix[13] = v_look_from.y matrix[14] = v_look_from.z matrix[15] = 1ENDFUNCTIONFUNCTION Vector_New AS TVector: x, y, z LOCAL v AS TVector v.x = x v.y = y v.z = z RETURN vENDFUNCTION// Subtract v2 from v1FUNCTION Vector_Subtract AS TVector: v1 AS TVector, v2 AS TVector   LOCAL v AS TVector   v.x = v1.x - v2.x   v.y = v1.y - v2.y   v.z = v1.z - v2.z   RETURN vENDFUNCTION// Normalize a vector so it's length = 1FUNCTION Vector_Normalize: v AS TVector   LOCAL m   m = SQR(v.x*v.x + v.y*v.y + v.z*v.z)   IF m <= kTollerance THEN m = 1   v.x = v.x / m   v.y = v.y / m   v.z = v.z / m   IF ABS(v.x) < kTollerance THEN v.x = 0   IF ABS(v.y) < kTollerance THEN v.y = 0   IF ABS(v.z) < kTollerance THEN v.z = 0ENDFUNCTION// Takes v1 AND v2 AND returns the cross product v1 X v2.// The cross product is a vector perpendicular TO both v1 AND v2// This is the normal of 2 vectorsFUNCTION Vector_CrossProduct AS TVector: v1 AS TVector, v2 AS TVector   LOCAL rv AS TVector   rv.x = (v1.y * v2.z) - (v1.z * v2.y)   rv.y = (v1.z * v2.x) - (v1.x * v2.z)   rv.z = (v1.x * v2.y) - (v1.y * v2.x)   RETURN rvENDFUNCTION`

To use it:
Code (glbasic) Select
`LOCAL mat[]LOCAL v_from AS TVectorLOCAL v_to AS TVectorv_from = Vector_New(1.0, 1.0, 1.0)v_to = Vector_New(20.0, 5.0, 3.0)Vector_LookAt(mat[], v_from, v_to)X_MOVEMENT 0, 0, 0X_ROTATION 0, 1, 0, 0X_ROTATION 0, 0, 1, 0X_ROTATION 0, 0, 0, 1// Is there a shorter way to do the above? (I find if I don't, the current matrix affects it)X_MULTMATRIX mat[]X_DRAWOBJ  object_id, 0`
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

#### Kitty Hello

Code (glbasic) Select
`X_MOVEMENT 0, 0, 0X_ROTATION 0, 1, 0, 0X_ROTATION 0, 0, 1, 0X_ROTATION 0, 0, 0, 1`

You don't need that. An X_MAKE3D will put you in that state. If you push/pop matrix, you always are there. If you really want to reset it, X_MOVEMENT 0,0,0 is enough (it clears all rotations).