API Question How to use GlobalRot function (or how to direct Vessel towards Sun)?

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
Hello forum.

From the API Reference:
GlobalRot
Performs a rotation of a direction from the local vessel frame to the global frame.

Synopsis:
void GlobalRot (
const VECTOR3 &rloc,
VECTOR3 &rrot) const


Parameters:
rloc point in local vessel coordinates (input)
rrot rotated point (output)

What does it mean by "local coordinates"?
If my vessel is orbiting Sun (its rbody is Sun), so relative direction to Sun is local coordinates?

I need to turn my Vessel directly to Sun, and I've tried with this function (converting [0 0 0] direction to Global Rotation) but it doesn't help (converted rotation wasn't to Sun... and error wasn't in 90 or 180 degrees)
 
Last edited:

Zatnikitelman

Addon Developer
Addon Developer
Joined
Jan 13, 2008
Messages
2,302
Reaction score
6
Points
38
Location
Atlanta, GA, USA, North America
It means the local cartesian coordinates of the vessel. To get the location, you would need the location of the sun in vessel-local coordinates (inverse of the vessel's global position?) and get the angles from those coordinates through a rotation matrix. It's been a while since I did much with global and local coordinate systems so I might be mis-remembering something.
 

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
So, rloc is XYZ cartesian point, but not [a b g] Euler angles?

There is a note in Reference:
Should be used to transform directions. To transform points, use
Local2Global, which additionally adds the vessel’s global position to the
rotated point.

So, If I have a point xyz (relative to rbody) and I want to get global position (x,y,z) I call Local2Global.
But with GlobalRot then I still have misunderstanding
 

Hielor

Defender of Truth
Donator
Beta Tester
Joined
May 30, 2008
Messages
5,580
Reaction score
2
Points
0
Local coordinates are with respect to the vessel: Z axis is "forward", y axis is "up," x axis is "right."

Coordinates relative to the rbody are not local coordinates...they're relative coordinates.

In order to point at the sun, you need to get the coordinate of the sun in local coordinates, and make it be (0,0,x).
 

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
Hielor
I've understood difference between local and relative, thank you.

GlobalRot used to transform directions.
But direction it's meant 3d point (xyz) on what object is pointing (but not three euler angles of object).

How to get current direction of Vessel?
I can get Rotation Matrix (transform from Local to Global frames)... but how to get current direction?
 

Hielor

Defender of Truth
Donator
Beta Tester
Joined
May 30, 2008
Messages
5,580
Reaction score
2
Points
0
The current direction of the vessel in the local frame is always (0,0,1).

The current direction of the sun in the local frame is the coordinates of the sun in the local frame, converted to a unit vector.
 
Last edited:

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
I can get direction of Sun. But Orbiter SDK has 2 ways of setting Vessel orientation:
setting vessel status field arot in Euler Angles
and
setting Rotation Matrix by function setRotationMatrix.

But why then I need 'Directions' (pointing vectors) and GlobalRot function, if I could change orientation only by setting Euler Angles or Rotation matrix but not Direction vectors.

So I need to convert Direction Vector to EulerAngles or Rotation Matrix.

I've tried such a code:
Code:
// Getting Direction to Sun
OBJHANDLE Sun = oapiGetGbodyByName("Sun");
VECTOR3 sunpos, localsunpos, sundir;
oapiGetGlobalPos(Sun, &sunpos);
pVessel->Global2Local(sunpos, localsunpos);
sundir = norm(localsunpos);

// The current vessel direction is [0 0 1]
VECTOR3 V0 = _V(0,0,1);
// Getting Rotation matrix for V0 to sundir
MATRIX3 Rot = getRotationMatrix(V0, sundir);

pVessel->setRotationMatrix(Rot);

Is that correct approach? (I haven't got good results with such an example)
So, I must write some extra function, such as getRotationMatrix. Just Orbiter API functions are not enough to solve my task (pointing vessel to Sun)
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
I can get direction of Sun. But Orbiter SDK has 2 ways of setting Vessel orientation:
setting vessel status field arot in Euler Angles
and
setting Rotation Matrix by function setRotationMatrix.

But why then I need 'Directions' (pointing vectors) and GlobalRot function, if I could change orientation only by setting Euler Angles or Rotation matrix but not Direction vectors.

So I need to convert Direction Vector to EulerAngles or Rotation Matrix.

This is a misunderstanding of what a direction vector and a rotation matrix (or Euler angle set) is.

A direction vector is NOT a sufficient way to represent a vessel's rotation in Orbiter's global coordinate system. It lacks the third rotation. There is no homoisomorphism for such a transformation, thus no generic function.

In other words: the direction vector alone is only determining where your nose is pointing, but not how much you rolled around your nose. You have to decide the amount of roll on your own, therefore the need for additional code.
 
Last edited:

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
This is a misunderstanding of what a direction vector and a rotation matrix (or Euler angle set) is.

A direction vector is NOT a sufficient way to represent a vessel's rotation in Orbiter's global coordinate system. It lacks the third rotation. There is no homoisomorphism for such a transformation, thus no generic function.

In other words: the direction vector alone is only determining where your nose is pointing, but not how much you rolled around your nose. You have to decide the amount of roll on your own, therefore the need for additional code.

Ok. I got it. Yeap, direction is only direction of nose. But we can rotate this nose about it's own z-axis.
But even then - I should got nose (or back, if it's error in sign) directed to Sun, and any roll angle. (with my code). But I haven't got nose directed to Sun.

Can you please advice me approach to direct vessel to the Sun?

---------- Post added at 11:52 AM ---------- Previous post was at 11:50 AM ----------

Direction cosines maybe?

do you mean direction cosine matrices? It's the same as rotation matrix, isn't it?
 

martins

Orbiter Founder
Orbiter Founder
Joined
Mar 31, 2008
Messages
2,448
Reaction score
462
Points
83
Website
orbit.medphys.ucl.ac.uk
Once you have the target direction, r_s = (x_s, y_s, z_s), you can construct a rotation matrix to point the nose in that direction, composed of a pitch and yaw rotation (leaving the roll angle undefined, as Face pointed out):

Pitch angle \phi and yaw angle \theta are defined by

\cos \phi = y_s
\tan \theta = z_s/x_s

from which you can construct

R_\phi = \left[\begin{array}{ccc}
1 & 0 & 0 \\
0 & \cos\phi & -\sin\phi \\
0 & \sin\phi & \cos\phi
\end{array}\right]

and

R_\theta = \left[\begin{array}{ccc}
\cos\theta & 0 & -\sin\theta \\
0 & 1 & 0 \\
\sin\theta & 0 & \cos\theta
\end{array}\right]

from which you can update the vessel's rotation matrix:

R \rightarrow R R_\theta R_\phi

(sorry about the latex code. I can't seem to get the math mode to work. Parse it through a latex interpreter if it looks incomprehensible).

You may need to flip the signs of the sines if the rotation goes the wrong direction.
 

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
Thank you very much. I understand the approach.

Tomorrow I'll try and then post the code if it works (It should work)
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
In addition to Martin's elegant solution I'd like to give another approach to the problem:

The goal is to create a rotation matrix for SetRotationMatrix(), where
vGlobal = Matrix * vLocal with the assumption that dSun = [x,y,z] = Matrix * [0,0,1]. I.e.: a matrix that rotates the Z axis into the direction of the sun.

You could then simply choose a set of orthogonal base vectors with the third one being the direction vector so that Matrix = [d1, d2, dSun]. Of course d1 and d2 must build an orthogonal system, so you have to use cross products to get it from dSun. The hardest part here would be to choose a non-collinear vector dNC to dSun for the first step:
Code:
if (dSun.x!=dSun.y) dNC=_V(dSun.y, dSun.x, dSun.z);
else if (dSun.x!=dSun.z) dNC=_V(dSun.z, dSun.y, dSun.x);
else if (dSun.y!=dSun.z) dNC=_V(dSun.x, dSun.z, dSun.y);
else dNC=_V(-dSun.x, dSun.y, dSun.z);
Once you have that, you can get d1 and d2 like so:
Code:
d1 = crossp(dSun, dNC);
normalise(d1);
d2 = crossp(d1, dSun);
vessel->SetRotationMatrix(_M(d1.x,d2.x,dSun.x,d1.y,d2.y,dSun.y,d1.z,d2.z,dSun.z));
This approach looks more complex at first, but if you go further and consider an up vector you'd like to achieve, you can just use that as dNC and should end up with a matrix doing the right roll, too.
 

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
I've just tried Martin's solution, and I haven't succeeded. Maybe I'm doing something wrong?

Here is my code:
Code:
DLLCLBK void opcOpenRenderViewport (HWND hWnd, DWORD w, DWORD h, BOOL fullscreen)
{
	/**
	 * Celestial bodies that we need
	 */
	OBJHANDLE Sun = oapiGetGbodyByName("Sun");
	OBJHANDLE Earth = oapiGetGbodyByName("Earth");
	// positions
	VECTOR3 sunpos, earthpos;
	oapiGetGlobalPos(Sun, &sunpos);
	oapiGetGlobalPos(Earth, &earthpos);

	/**
	 * Getting Vessel
	 */

	// Handle of Vessel
	OBJHANDLE hVessel = oapiGetVesselByName("UAV");
	// Instance of Vessel
	VESSEL2 *pVessel;
	pVessel = (VESSEL2*)oapiGetVesselInterface(hVessel);
	// Status instance of Vessel
	VESSELSTATUS vs;
	pVessel->GetStatus(vs);

	/**
	 * Positioning the Vessel
	 */

        // Place on Sun Orbit
	vs.rbody = Sun;
	// kill rotations and movings
	vs.vrot = V3NULL;
	vs.rvel = V3NULL;
	// Put it in conjuction with Earth (cross the Sun)
	vs.rpos = -earthpos+sunpos; 

	// saving state
	pVessel->DefSetState(&vs);

	/**
	 * Prepare to rotate the Object
	 * We need it to point to Sun
	 */

	// initial rotation matrix
	MATRIX3 Rot0;
	pVessel->GetRotationMatrix(Rot0);
	// direction is Sun
	VECTOR3 direction;
	pVessel->Global2Local(sunpos, direction);
	// normalise
	direction = direction/length(direction);

	// Calc cosines and sinuses for Rotation matrices
	double cos_phi   = direction.y;
	double sin_phi   = sqrt(1-cos_phi*cos_phi);
	
	double tan_theta = direction.z/direction.x;
	double cos_theta = sqrt(1/(tan_theta*tan_theta+1));
	double sin_theta = sqrt(1-cos_theta*cos_theta);

	// Making Rotation matrices for phi and theta
	MATRIX3 Rot_phi = _M( 
						  1, 0      , 0,
						  0, cos_phi, -sin_phi,
						  0, sin_phi, cos_phi
						 );
	MATRIX3 Rot_theta = _M(
						    cos_theta, 0, -sin_theta,
						    0,		    1,      0,
						    sin_theta,  0,  cos_theta
				               );

	// Complex rotation - multiplying rotation matrices
	MATRIX3 Rotation = mul(mul(Rot0, Rot_theta), Rot_phi);

	// Apply Rotation to Vessel
	pVessel->SetRotationMatrix(Rotation);

	/**
	 * Some Camera Preparation
	 */

	// place camera to exact distance
	double Distance = 125;
	oapiCameraScaleDist(Distance/oapiCameraTargetDist());

	// Reset Polar and azimuth
	oapiCameraRotPolar(0);
	oapiCameraRotAzimuth(0);
												
}

I'll show you screenshots of my results.

First, these are screenshots of placing object in conjuction to Earth (without rotation, setRotationMatrix is commented):


(1) After starting Orbiter. (2) Setting Camera track mode "From Sun"
If I set Camera track view "Movable global frame" view remains as in (2)

Than I enabled rotation (setting direction to Sun) (setRotationMatrix isn't commented)



(1) After starting Orbiter. (2) Setting Camera track mode "From Sun" (3) Setting camera track view "Movable global frame"

Results are not as I expected. And advices?
Where have I mistaken?
Thanks in advance.

---------- Post added at 08:30 AM ---------- Previous post was at 08:03 AM ----------

Face,
Thanks for an approach.

I used this code:
Code:
        // direction is Sun
	VECTOR3 direction;
	pVessel->Global2Local(sunpos, direction);
	// normalise
	direction = direction/length(direction);

	VECTOR3 dNC, d1, d2;

	if (direction.x != direction.y) dNC = _V(direction.y, direction.x, direction.z);
	else if (direction.x != direction.z) dNC = _V(direction.z, direction.y, direction.x);
	else if (direction.y != direction.z) dNC = _V(direction.x, direction.z, direction.y);
	else dNC = _V(-direction.x, direction.y, direction.z);

	d1 = crossp(direction, dNC);
	d1 = d1/length(d1);
	d2 = crossp(d1, direction);
	pVessel->SetRotationMatrix(_M(
			d1.x , d2.x , direction.x,
			d1.y , d2.y , direction.y,
			d1.z , d2.z , direction.z
		));

Here are results:
(1) After Orbiter starts

(2) Setting Camera track mode "From Sun"
orbiter32.png

(3) Setting Camera track view "Movable Global Frame"
orbiter33.png
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
Thanks for an approach.

I used this code:
Code:
        // direction is Sun
    VECTOR3 direction;
    pVessel->Global2Local(sunpos, direction);
    // normalise
    direction = direction/length(direction);

    VECTOR3 dNC, d1, d2;

    if (direction.x != direction.y) dNC = _V(direction.y, direction.x, direction.z);
    else if (direction.x != direction.z) dNC = _V(direction.z, direction.y, direction.x);
    else if (direction.y != direction.z) dNC = _V(direction.x, direction.z, direction.y);
    else dNC = _V(-direction.x, direction.y, direction.z);

    d1 = crossp(direction, dNC);
    d1 = d1/length(d1);
    d2 = crossp(d1, direction);
    pVessel->SetRotationMatrix(_M(
            d1.x , d2.x , direction.x,
            d1.y , d2.y , direction.y,
            d1.z , d2.z , direction.z
        ));

Your direction vector is wrong, because you've expressed it in local coordinates in the vessel's type 3 system. Just use it in global coordinates, like so:
Code:
     // direction is Sun, assuming sunpos is global coordinates of sun
    VECTOR3 direction=sunpos-oapiGetGlobalPos(pVessel->GetHandle());
    // normalise
    normalise(direction);
 

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
Your direction vector is wrong, because you've expressed it in local coordinates in the vessel's type 3 system. Just use it in global coordinates, like so:
Code:
     // direction is Sun, assuming sunpos is global coordinates of sun
    VECTOR3 direction=sunpos-oapiGetGlobalPos(pVessel->GetHandle());
    // normalise
    normalise(direction);

I've tried:
Code:
	// global sunpos
        OBJHANDLE Sun = oapiGetGbodyByName("Sun");
	VECTOR3 sunpos;
	oapiGetGlobalPos(Sun, &sunpos);
       
       // ...
       VECTOR3 objpos;
       oapiGetGlobalPos(hVessel, &objpos);
      VECTOR3 direction = sunpos-objpos;
      // normalise
      direction = direction/length(direction);

and I got the same results... it's weird ;)

weird is that fact, that I've got the same direction. Global2Local(sunpos) is the same as global sunpos minus global objpos.
May be it coincidence cause of vessel coordinate system is rotated like global
 
Last edited:

martins

Orbiter Founder
Orbiter Founder
Joined
Mar 31, 2008
Messages
2,448
Reaction score
462
Points
83
Website
orbit.medphys.ucl.ac.uk
Results are not as I expected. And advices?
Where have I mistaken?
Thanks in advance.


Firstly, I made a mistake. The yaw angle should be taken from the z-axis, not the x-axis, so tan theta = -x/z.

Secondly, you need to pick a method to compute the yaw angle which gives you the full 2pi range and doesn't have singularities, i.e.

Code:
theta = atan2(-x,z);
sin_theta = sin(theta);
cos_theta = cos(theta);

Thirdly, there is still a possibility that the rotations are going the wrong way and you have to flip the sines (independently for yaw and pitch). The best way to test this is one angle at a time. Set up a scenario where the yaw angle is already correct, comment the yaw component of the code, and see if pitch rotation works correctly. Then the other way round for yaw.
(Edit: an even better alternative is to sit down with pen and paper and work out the correct rotation matrices rather than trial and error, but that's up to your preference.)
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
I've tried:
Code:
    // global sunpos
        OBJHANDLE Sun = oapiGetGbodyByName("Sun");
    VECTOR3 sunpos;
    oapiGetGlobalPos(Sun, &sunpos);
       
       // ...
       VECTOR3 objpos;
       oapiGetGlobalPos(hVessel, &objpos);
      VECTOR3 direction = sunpos-objpos;
      // normalise
      direction = direction/length(direction);
and I got the same results... it's weird ;)

weird is that fact, that I've got the same direction. Global2Local(sunpos) is the same as global sunpos minus global objpos.
May be it coincidence cause of vessel coordinate system is rotated like global

Hm. Did you also check if that change PLUS my approach is giving you a sun pointing nose? Because it could well be that your results of simply checking the direction vector are based on the last state. And if your vessel already points approximately into the sun direction, of course Global2Local and vector sum is giving the same results.

I'd be a bit surprised if the vector sum PLUS my approach would not give a nose pointing to the sun. After all the calculation M * [0,0,1] must yield the direction vector, thus rendering all nose points in that direction. Strange.
 

Evgheny

New member
Joined
Oct 18, 2011
Messages
29
Reaction score
0
Points
0
Location
Moscow
Thanks.
I've corrected code and got almost good result.

Here is corrected code for Martin's approach:
Code:
// initial rotation matrix
MATRIX3 Rot0;
pVessel->GetRotationMatrix(Rot0);
// direction is Sun
VECTOR3 direction;
pVessel->Global2Local(sunpos, direction);
// normalise
direction = direction/length(direction);

// Calc cosines and sinuses for Rotation matrices
double cos_phi   = direction.y;
double sin_phi   = sqrt(1-cos_phi*cos_phi);

double theta     = atan2(-direction.x, direction.z);
double sin_theta = sin(theta);
double cos_theta = cos(theta);

// Making Rotation matrices for phi and theta
MATRIX3 Rot_phi = _M( 
					  1, 0      , 0,
					  0, cos_phi, -sin_phi,
					  0, sin_phi, cos_phi
       				 );
MATRIX3 Rot_theta = _M(
					cos_theta, 0, -sin_theta,
					0,		   1,      0,
					sin_theta, 0,  cos_theta
					   );

// Complex rotation - multiplying rotation matrices
MATRIX3 Rotation = mul(mul(Rot0, Rot_phi), Rot_theta);
// Apply Rotation to Vessel
pVessel->SetRotationMatrix(Rotation);

And I got such result:
pointedtosun.png


In the bottom of image you can see some debug information: polar and azimuth of camera.

So, roll doesn't matter in this solution (cause we lack roll)... but we see Vessel pointing not directly to the Sun. If I set "Target From .. Sun" mode (withot clicking Movable global frame) in Camera menu, I see polar : -5.74 and azimuth: -174.26
So, error is 5.74 degrees in both directions. But why?


Also, I've realized some another approach of pointing vessel to Sun (with roll):
Code:
	VECTOR3 objpos;
	oapiGetGlobalPos(hVessel, &objpos);
	MATRIX3 R = getRotationMatrix(_V(0,0,1), sunpos-objpos);
	pVessel->SetRotationMatrix(R);

For this code getRotationMatrix function is needed. Here it is:
Code:
/**
 * Getting rotation matrix from 2 vectors
 *
 * Theory:
 *
 * w = norm(cross(u,v))
 * U = [u w cross(u,w)]
 * V = [v w cross(v,w)]
 * R = VU'
 */
inline MATRIX3 getRotationMatrix(const VECTOR3 &a, const VECTOR3 &b)
{
	VECTOR3 u = norm(a);
	VECTOR3 v = norm(b);
	VECTOR3 w = norm(crossp(v,u));

	return mul( 
			concat3(v,w,crossp(v,w)),		/*   V   */
			transpose(concat3(u,w,crossp(u,w)))  /*   U'  */ 
		);
}

This code got such result:
azimuth.png


The screenshot was taken in "Movable global frame" mode. And we see Polar = 0, Azimuth = 95.74.
Again this 5.74 degrees. Where did it come from?

P.S. Here, this angle = 5.74 it's the angle between global z-axis and line Sun-Earth. It depends on current epoch.
 
Last edited:

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
Ok, the wrong result with my approach made me curious, so I've made a little test with Orbiter 2006 P1 plus Orbitersdk installation and the stock MeshDebugger module.

I added a button to point the focus vessel at the sun, and used my code snippet. And indeed, the vessel points to the right instead of directly at the sun.

Turns out that the reason for this is that a right-handed matrix (like the one my code generates) has this effect. So I simply swapped the order in one of the cross-product calls, and now it works:

Code:
VESSEL *vessel=oapiGetFocusInterface();
            if (vessel!=NULL)
            {
                OBJHANDLE sun=oapiGetObjectByName("Sun");
                if (sun!=NULL)
                {
                    VECTOR3 vesselPos, sunPos, dNC, d1, d2, d3;
                    oapiGetGlobalPos(vessel->GetHandle(), &vesselPos);
                    oapiGetGlobalPos(sun, &sunPos);
                    d3=sunPos-vesselPos;
                    d3=d3/length(d3);//normalise(d3);
                    
                    //choose ANY non-collinear vector to get a base plane orientation
                    /*if (d3.x!=d3.y) dNC=_V(d3.y, d3.x, d3.z);
                    else if (d3.x!=d3.z) dNC=_V(d3.z, d3.y, d3.x);
                    else if (d3.y!=d3.z) dNC=_V(d3.x, d3.z, d3.y);
                    else dNC=_V(-d3.x, d3.y, d3.z);*/

                    //choose the global up vector if not currently pointing straight up or down,
                    //otherwise use global right vector
                    if (abs(d3.x)>0.001 || abs(d3.z)>0.001) dNC=_V(0,1,0);
                    else dNC=_V(1,0,0);
                    
                    d1 = crossp(dNC, d3);
                    d1=d1/length(d1);//normalise(d1);
                    d2 = crossp(d3, d1);
                    vessel->SetRotationMatrix(_M(d1.x,d2.x,d3.x,d1.y,d2.y,d3.y,d1.z,d2.z,d3.z));
                }
            }

In addition to the fixed part, you can also see there an example for an up vector choice (instead of the arbitrary one).
 
Top