Jump to content

Recommended Posts

Over the course of the last few hours I've familiarized myself with, experimented on, taken apart, and learned the nature of Bohemia's 'vector' object orientation system and how to manipulate it to do exactly what you want it to do. I had searched around before for this information but had found only speculation or rough guesses and no concrete inner workings, and so after discovering them I wish to share them with the internet so nobody has to go through what I did to figure this stuff out.

 

setVectorDir and setVectorUp are script commands you can use to set an object's orientation, particularly useful because they don't overwrite each other. Using setDir resets an object's pitch to 0, so if you want to keep an object's pitch while changing its heading, setVectorDir is your only option. setVectorUp is essentially setVectorDir, but for roll and pitch. You can use BIS_fnc_setPitchBank to set roll and pitch, but I'd wager that setVectorUp is more efficient.

 

This post is about how the arguments to those commands work, exactly what the commands do, and how to manipulate them to do what you want them to do.

 

Go back to high school trigonometry for a moment. Remember right triangles and vectors? Well, those are at the core of the vector system. I suspected after a while and some fruitless experiments that the name may have literally meant that the system uses vectors, and that turned out to be the case.

 

For those that don't know, a vector is a distance and a direction. Take a line segment, put an arrow on one end, and you (basically) have a vector. Vectors consist of only two numbers: an angle, which defines their direction, and a magnitude, which defines their distance. They do not have a fixed start or end point, they are just an angle and distance. You can put a vector wherever you like and however you like. Vectors can be the hypotenuse of a right triangle:

5IWKa0D.jpg

 

at any rate, onward to the explanations.

 

VectorDir

VectorDir is the azimuth direction of an object, although it's represented much differently in the game's logic.

12bCu2V.jpg

VectorDir is an array of three values, [x, y, z].

x is the length of the x leg of that triangle, on the east/west axis. Positive x denotes east, negative x denotes west.

y is the length of the y leg, on the north/south axis. Positive y denotes north, negative south.

z I'm not sure about. It hasn't been necessary in any of my testing, and doesn't seem to do anything when I try to use it.

 

When you setVectorDir [x, y, z], you create a vector that is the hypotenuse of a triangle with legs of length x and y. The vehicle orients along that vector, using its direction angle:

JkCwY9q.jpg

I've changed the triangle and the references in this picture to reflect how the angle is actually measured. Technically, you can use either triangle in your calculations because angle X on the green triangle is equal to angle Y on the gold one, but the algebra is easier with the gold one. Speaking of algebra, let's start applying these principles.

 

Because that's a right triangle, trig ratios say that tan Y = x/y. In order to change the vehicle's direction, we have to create a vector. Let's say we want to put the vehicle at 35 degrees azimuth--we can do that by defining the direction angle, Y. In the equation, that looks like: tan 35 = x/y. The next step would be to define y and x, but how do we know what to set them as? Well, there are an infinite number of triangles we could make that have Y as 35 degrees; the only thing that's constant through them all is the ratio between y and x. So if we make one of the legs (y because it's easy) a constant (1 because it's easy), our equation looks like this: tan 35 = x/1. Simplified: tan 35 = x. So now we get our y-value. So, to recap, y=1, and x=tan 35. Let's put that back into the command:


object setVectorDir [tan 35, 1, 0];

This will set our vehicle's direction to 35 degrees azimuth. But that's not all there is to it. Recall the quadrants of a graph: 1st quadrant, both x and y are positive. 2nd quadrant, y is negative. 3rd, x and y are negative. 4th, x is negative. Using that formula above, we'll be okay for the first quadrant, but after that things start getting wonky. That's because tangent returns the slope of the vector; tan 35 is the slope of the vector we created that had direction angle 35. With direction angles between 90 and 180, the slope becomes negative. So tan 89 =~ 57, but tan 91 =~ -57. If we put tan 91 into the command, we end up with:

object setVectorDir [tan 91, 1, 0]; (approximately object setVectorDir [-57, 1, 0])

which puts us in the fourth quadrant. 91 goes out toward the second quadrant, which is positive x, negative y. So we have to modify our statement a little by multiplying both numbers by -1, or simply making them opposites. So, to get to the second quadrant correctly, the command should look like:

object setVectorDir [-tan 91, -1, 0];

which will set the vehicle to a direction of 91 degrees.

The third quadrant will use angles between 180 and 270, but we don't have to change anything here. Third quadrant is negative-negative, and tangent goes back to positive, so

object setVectorDir [-tan 181, -1, 0];

will work just fine.

The fourth quadrant uses angles between 270 and 360, and for this we have to go back to our first setup because tangent goes back to negative, so

object setVectorDir [tan 271, 1, 0];

will work.

 

We're not done yet. There are special cases, too. At 90 and 270, tangent is undefined, and if we try to pass something that's undefined we'll probably get an error (haven't actually tried). So we have to handle 90 and 270 separately, by just setting the vehicle to those directions.

object setVectorDir [1, 0, 0];

object setVectorDir [-1, 0, 0];

for 90 and 270, respectively.

 

I've written some code to convert an angle from traditional azimuth degrees to a vectorDir. Here it is:

// Converts azimuth angle to BIS vectorDir array
// Author: Ruger392, a.k.a. Lt. Col. Ruger of Air Combat Command

_return = [0, 1, 0]; // North

_angle = _this select 0;

_xlen = tan _angle;

// Determine quadrant and special cases and return

if ((_angle > 0) && (_angle < 90)) then {_return = [_xlen, 1, 0]};

if ((_angle > 90) && (_angle < 180)) then {_return = [-_xlen, -1, 0]};

if ((_angle > 180) && (_angle < 270)) then {_return = [-_xlen, -1, 0]};

if ((_angle > 270) && (_angle < 360)) then {_return = [_xlen, 1, 0]};

if (_angle == 90) then {_return = [1, 0, 0]};

if (_angle == 180) then {_return = [0, -1, 0]};

if (_angle == 270) then {_return = [-1, 0, 0]};

_return

Onward!

 

VectorUp

VectorDir was easy. VectorUp is where things get complicated.

 

With VectorUp, you have essentially the same thing as VectorDir, except the vehicle's top side is oriented along the vector instead of its front and z does stuff. Let me elaborate:

 

setVectorUp's axes are the same as those for setVectorDir, with one exception. The z axis is functional and represents up/down, positive = up and negative = down.

Doesn't seem so difficult, until you realize that setVectorDir and setVectorUp use absolute axes instead of object-relative ones. Which makes sense, it just makes setVectorUp a lot harder to use, because if we define x and z to pitch up at 45 degrees like so:

object setVectorUp [1, 0, 1];

it will only work when we're facing directly east. Any deviation from east and we get roll as well, because the top of the plane wants to point along a vector that is facing east when the plane itself is not. So how do we solve this? Well, recall the vector and the triangle. The two legs of the triangle defined where the vector pointed. Using some trigonometric magic, we can make the vector point along the axis of our plane by changing the lengths of x and y. Recall the functions sine and cosine; sine takes an angle and returns how far up it goes on the y-axis. Cosine does the same, but for the x-axis.

hB6x9dP.jpg

So if we change angle A to the azimuth of our object, we can take the sine and cosine of it (for north/south and east/west, respectively) and have the vector pointing in the same direction. But what about controlling the pitch? We can do that the same way we controlled azimuth with setVectorDir. If we consider just pitch and the vector's overall magnitude, we can think of it in two dimensions. There is one slight problem, though: setVectorUp orients the object's top, not its nose. This will invert our desired pitch, but we can correct for that by inverting our formula. Previously, we had: tan Y = x/y. Instead of using Y, we can use X, so we instead have: tan X = y/x. Thanks to the golden rule of trig ((sin x)^2 + (cos x)^2 = 1), we don't have to set a constant because the vector's magnitude (x) will always be 1. So we have: tan X = 1/y. Solve for y: y tan X = 1 ... y = 1 / (tan X). Now remember that y in this case is actually our z axis, our height.

 

So, to recap, we find x with cosine of the object's direction and y with sine of the object's direction, then z with our formula there. We input our desired pitch as X, so the (almost!) final command looks like:

object setVectorUp [cos (getDir object), sin (getDir object), 1 / (tan X)];

 

There's one last little thing we haven't accounted for. Sine, cosine, and tangent were all created for use in the math world, where angles start at what would be direct east in ArmA, and go counter-clockwise. So when you take sin 90, you're actually getting sine at direct north, instead of direct east. To fix that, we need to make sine measure clockwise by making it negative. We also need to have it start measuring at north, so we add 90 degrees. Cosine should measure counter-clockwise, because its sign changes are opposite that of sine's, but it should still start at north. +90. So the final command looks like:

object setVectorUp [cos (getDir object + 90), -sin (getDir object + 90), 1 / (tan X)];

 

... for pitching up. For pitching down, we have to make a few changes, because if we just make our angle negative, we make the entire z axis negative, which flips the object over. Thankfully, this fix is easy; we just have to reverse the signs of sine and cosine. So we get:

object setVectorUp [-cos (getDir object +90), sin (getDir object + 90), 1 / (tan X)];

and this works because by reversing the direction of the vector, we reverse which way the top points.

 

What about setVectorDirAndUp that everyone uses?

Personally, I haven't been able to successfully use setVectorDirAndUp. It has weird working conditions (most times I get no orientation change, if I do it's way off from what I'd get using the commands separately), and I suspect it has to do with intricacies of when exactly vectorDir and vectorUp are applied and that my method requires the object's azimuth be known and fixed before setting its vectorUp. I'm sure there's a way to use it, I just haven't been able to figure it out and I'm content to use both commands individually.

 

Hope this helped! If anything's too confusing, let me know so I can clear it up.

  • Like 6
  • Thanks 4

Share this post


Link to post
Share on other sites

@ruger392 that was beautifully presented and much appreciated.  I've never understood trig and have wasted a lot of time just experimenting with the setVectorDirAndUp numbers until I eventually get what I'm looking for.  I'm curious if you are a math professor...

 

Let's see if I have this right.  If I want to orient an object in 3D space using vectors (direction 300, pitch down 20), I first use your function to convert dir 300 to vectorDir, I then use your last vectorUp statement (with the negative cos) to pitch down 20 degrees?

 

What about yaw?  If I want to roll the object left 15 degrees?

 

Ultimately I would love to see a function where you input:

  • object
  • azimuth (0-360)
  • pitch degrees (positive or negative)
  • yaw degrees (positive or negative)

 

And the object is oriented as desired.  This would be awesome for the math challenged folks, of whom I am king!

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, johnnyboy said:

@ruger392 that was beautifully presented and much appreciated.  I've never understood trig and have wasted a lot of time just experimenting with the setVectorDirAndUp numbers until I eventually get what I'm looking for.  I'm curious if you are a math professor...

 

Let's see if I have this right.  If I want to orient an object in 3D space using vectors (direction 300, pitch down 20), I first use your function to convert dir 300 to vectorDir, I then use your last vectorUp statement (with the negative cos) to pitch down 20 degrees?

 

What about yaw?  If I want to roll the object left 15 degrees?

 

Ultimately I would love to see a function where you input:

  • object
  • azimuth (0-360)
  • pitch degrees (positive or negative)
  • yaw degrees (positive or negative)

 

And the object is oriented as desired.  This would be awesome for the math challenged folks, of whom I am king!

Exactly right.

I'm working on roll (it's a bit more complicated), and eventually I'll write a function that does just that.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

Hard topic!. If you want to find the equations, you can't skip the matrix products. Have a look  a look at WIKi for step by step reading and finding all calculations you want. You need also to dig at matrices multiplication if you want to check the given equations. I didn't find a simple example. Perhaps here in exercise 4.

 

Something interesting for yaw, pitch, bank with 2 axes as BI does in setVectorDIRandUP, is the intrinsic rotations. You can rotate in any positions you need with a succession of z, x, z rotations in the good order.

 

So, there is no easy way to "translate" a 3D-rotation without digging mathematics. The fnc_SetPitchBankYaw you'll find at the last remarks in BIKI setVectorDirandUp is not so bad!

 

NB: don't forget: "When attaching object to an object the axes are relative to the object that gets the attachment." That's the very good news.

Share this post


Link to post
Share on other sites
On 7.1.2018 at 4:28 AM, pierremgi said:

So, there is no easy way to "translate" a 3D-rotation without digging mathematics. The fnc_SetPitchBankYaw you'll find at the last remarks in BIKI setVectorDirandUp is not so bad!

 

NB: don't forget: "When attaching object to an object the axes are relative to the object that gets the attachment." That's the very good news.


You just saved hours of my life! Thank you :D

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×