Rotating the cube by euler engles, carries the other axes together.
#1
Apost 
I implemented some tricks in the "Tutorial #02: Color Cube" and I noticed something unexpected.

My body for void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state) is now:

Code:
void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state)
{
static Double angleX = 0;
static Double angleY = Math::HALF_PI;
static Double spinSpeed = 0.007;
angleX += spinSpeed;
angleX = Math::Fmod(angleX, Math::TWO_PI);
angleY = Math::Fmod(angleY, Math::TWO_PI);
mCubeTransform->SetRotation(angleX, angleY, 0);
state->SetUserDebugMessage(Util::DoubleToString(spinSpeed) + " -> " + Util::DoubleToString(angleX) + " / " + Util::DoubleToString(angleY) + "\n");
}
I noticed that my cube is spinning in the axis-Z unlike axis-X.
This only happens when the angleY is in (HALF_PI) or (PI + HALF_PI).
If this is true so, how I do to not carry in rotation the other axes together?
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#2
Apost 
(23 Nov 2015, 6:04)Flávio Rebouças Santos Wrote: ...
I noticed that my cube is spinning in the axis-Z unlike axis-X.
This only happens when the angleY is in (HALF_PI) or (PI + HALF_PI).
I suspect that I'm rotating the cube axis and not changing the cube angle.
If this is true so, how I do to, instead of rotating the axes, change the angle of the form?

Hi Flávio,

The SetRotation method directly sets the Euler angles of the transformation matrix of the cube.
The rotation order is ZYX. See also SetRotation

Alternatively you can set the rotation by specifying an axis vector and an angle to rotate the cube around the given axis vector with SetRotation(const Vector & axis, Real angle) or use quaternions to specify the rotation.

You can also nest multiple Transform nodes to sequentially apply transform operations to a node or a set of nodes. The execution order for nested Transform nodes is from inner to outer. Within one Transform node the transformation order is Scale, Rotate, Translate.

If you want to rotate the camera instead, just set the rotation of the CameraTransform node. You can also nest the CameraTransform node within other ordinary Transform nodes.

HTH
Reply
#3
Apost 
Here is an example how to add an additional rotation transform:
void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state)
{
   static Double angleX = 0;
   static Double angleY = Math::HALF_PI;
   static Double spinSpeed = 0.007;
   angleX += spinSpeed;
   angleX = Math::Fmod(angleX, Math::TWO_PI);
   angleY = Math::Fmod(angleY, Math::TWO_PI);
   mCubeTransform->SetRotation(angleX, angleY, 0);  

   Graph::Matrix transform(Matrix::IDENTITY);
   transform.SetRotationComponentX(angleX);
   Graph::Matrix& cubeTransform = mCubeTransform->GetTransform();
   cubeTransform = transform * cubeTransform;


and here is an example how to rotate the cube with the mouse:
void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state)
{
   Logic::IDeviceHandler* deviceHandler = state->GetDeviceHandler();
   
   static Real posX, posY;
   Real oldX = posX;
   Real oldY = posY;
   
   deviceHandler->GetMousePosition(posX, posY);
   Real dx = oldX - posX;
   Real dy = oldY - posY;
   Graph::Matrix transform(Matrix::IDENTITY);
   if (deviceHandler->IsMouseButtonPressed(IEnums::MOUSE_BUTTON_LEFT))
   {
       transform.SetRotationComponentXYZ(2*dy,-2*dx,0,IEnums::ROTATION_ORDER_ZYX);
   }
   Graph::Matrix& cubeTransform = mCubeTransform->GetTransform();
   cubeTransform = transform * cubeTransform;
Reply
#4
Apost 
Hi Ketschak,

I had suspected that I would have to use the transformation matrix.
Now I will study new possibilities with these instruments you showed me.

Thanks again!
Best Regards.

(23 Nov 2015, 12:26)Ketschak Wrote: Here is an example how to add an additional rotation transform:
void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state)
{
   static Double angleX = 0;
   static Double angleY = Math::HALF_PI;
   static Double spinSpeed = 0.007;
   angleX += spinSpeed;
   angleX = Math::Fmod(angleX, Math::TWO_PI);
   angleY = Math::Fmod(angleY, Math::TWO_PI);
   mCubeTransform->SetRotation(angleX, angleY, 0);  

   Graph::Matrix transform(Matrix::IDENTITY);
   transform.SetRotationComponentX(angleX);
   Graph::Matrix& cubeTransform = mCubeTransform->GetTransform();
   cubeTransform = transform * cubeTransform;


and here is an example how to rotate the cube with the mouse:
void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state)
{
   Logic::IDeviceHandler* deviceHandler = state->GetDeviceHandler();
   
   static Real posX, posY;
   Real oldX = posX;
   Real oldY = posY;
   
   deviceHandler->GetMousePosition(posX, posY);
   Real dx = oldX - posX;
   Real dy = oldY - posY;
   Graph::Matrix transform(Matrix::IDENTITY);
   if (deviceHandler->IsMouseButtonPressed(IEnums::MOUSE_BUTTON_LEFT))
   {
       transform.SetRotationComponentXYZ(2*dy,-2*dx,0,IEnums::ROTATION_ORDER_ZYX);
   }
   Graph::Matrix& cubeTransform = mCubeTransform->GetTransform();
   cubeTransform = transform * cubeTransform;
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#5
Tool 
I am trying to use the signature: SetRotation(const Vector & axis, Real angle)

Could you help-me with it?
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#6
Apost 
(23 Nov 2015, 18:41)Flávio Rebouças Santos Wrote: I am trying to use the signature: SetRotation(const Vector & axis, Real angle)

Could you help-me with it?

void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state)
{
   static Double angleX = 0;
   static Double spinSpeed = 0.007;
   angleX += spinSpeed;
   angleX = Math::Fmod(angleX, Math::TWO_PI);
   
   Graph::Vector axis(Vector::ZERO_DIRECTION);
   axis.x = 1;
   axis.y = 1;
   axis.NormalizeAxisSelf();
   mCubeTransform->SetRotation(axis, angleX);


The vector axis defines the rotation axis (45° between x and y).
The vector needs to have unit length.
This can be accomplished with axis.NormalizeAxisSelf();
Reply
#7
Apost 
It's Amazing!
What means use of SetPosition (const Vector &pos) ?

(23 Nov 2015, 19:33)Ketschak Wrote:
(23 Nov 2015, 18:41)Flávio Rebouças Santos Wrote: I am trying to use the signature: SetRotation(const Vector & axis, Real angle)

Could you help-me with it?

void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state)
{
   static Double angleX = 0;
   static Double spinSpeed = 0.007;
   angleX += spinSpeed;
   angleX = Math::Fmod(angleX, Math::TWO_PI);
   
   Graph::Vector axis(Vector::ZERO_DIRECTION);
   axis.x = 1;
   axis.y = 1;
   axis.NormalizeAxisSelf();
   mCubeTransform->SetRotation(axis, angleX);


The vector axis defines the rotation axis (45° between x and y).
The vector needs to have unit length.
This can be accomplished with axis.NormalizeAxisSelf();
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#8
Apost 
I need to execute at the same Tick:
transform.SetRotationComponentX(angleX);
transform.SetRotationComponentY(angleY);


Because I need to Set the rotation component of the transformation matrix to only rotate around the X axis and around the Y axis.

The problem is: When I executing SetRotationComponentY in the sequence after SetRotationComponentX, the rotation around the X axis is not happening.
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#9
Apost 
(23 Nov 2015, 20:41)Flávio San Wrote: I need to execute at the same Tick:
transform.SetRotationComponentX(angleX);
transform.SetRotationComponentY(angleY);


Because I need to Set the rotation component of the transformation matrix to only rotate around the X axis and around the Y axis.

The problem is: When I executing SetRotationComponentY in the sequence after SetRotationComponentX, the rotation around the X axis is not happening.

You need to use the method SetRotationComponentXYZ.
The method SetRotationComponentY automatically sets the other two rotation components X and Z to 0.
Reply
#10
Apost 
I forgot to say that I am using mCubeTransform->SetTransform(transform)

I can't use the SetRotationComponentXYZ because it carries the other axes together.


(23 Nov 2015, 21:30)Ketschak Wrote:
(23 Nov 2015, 20:41)Flávio San Wrote: I need to execute at the same Tick:
transform.SetRotationComponentX(angleX);
transform.SetRotationComponentY(angleY);


Because I need to Set the rotation component of the transformation matrix to only rotate around the X axis and around the Y axis.

The problem is: When I executing SetRotationComponentY in the sequence after SetRotationComponentX, the rotation around the X axis is not happening.

You need to use the method SetRotationComponentXYZ.
The method SetRotationComponentY automatically sets the other two rotation components X and Z to 0.
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#11
Apost 
I saw one source code and I have one suggestion to the solution of this problem.
void SetRotationComponent(DataType angleX, DataType angleY, DataType angleZ)
{
   SetRotationComponentXYZ(angleX, angleY, angleZ, IEnums::ROTATION_ORDER_ZYX);
}


The SetRotationComponent(DataType angleX, DataType angleY, DataType angleZ) automatically uses the SetRotationComponentXYZ(DataType angleX, DataType angleY, DataType angleZ,   IEnums::RotationOrder rotationOrder) because it wants to carries the other axes together. passing by default the axis rotation order IEnums::ROTATION_ORDER_ZYX

My suggestion is that you change the behavior of the method SetRotationComponent(DataType angleX, DataType angleY, DataType angleZ) to NOT carries the other axes together. like the behavior of SetRotationComponentX(DataType angleX) with Y and Z.
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#12
Apost 
I will try to give you a short summary about how the concept of transformation matrices is working. If you understand this concept, the answer to your question should be clear.

In computer graphics vertices are usually represented as a vector and transformations of vertices as matrices. In 3D a 3-vector and a 3x3-matrix can be used to transform a vector by a linear transformation.

   

Using this concept you can perform linear transformations like rotation or scale.

The problem is that you cannot perform an affine transformation like a translation with this concept. To be able to also represent affine transformations with a general matrix*vector operation, homogenous coordinates are used. This means representing a 3-vector (x, y, z) as a 4-vector (x, y, z, 1) instead.

   

Using homogenous coordinates, we can represent linear and affine transformations by simple 4x4 transformation matrices.

   

We can combine transformations by simply multiplying these transformation matrixes.

   

The order of transformations (order of matrix multiplications) is important! You cannot switch the order of the transformations and expect to end up with the same result. Use for example one Matrix T1 that translates in x direction and one matrix T2 that rotates around the z-axis.

With T'=T2*T1 you will get a completely different result as with T'=T1*T2

   

In the Murl Engine the Transform interface represents a transformation matrix with methods to directly set the rotation and translation components.

You can compose a combined transformation from individual transformations by nesting separate Transform nodes:
<Transform id="t2" …>
   <Transform id="t1" …>
       <PlaneGeometry ../>
   </Transform> 
</Transform>


In this example the PlaneGeometry will first apply its own Transform then Transform t1 and then Transform t2.

You can also compose a transformation by calculating the transformation matrix in code:
Graph::Matrix T1(Matrix::IDENTITY);
T1.SetTranslationComponentX(200);
Graph::Matrix T2(Matrix::IDENTITY);
T2.SetRotationComponentZ(45*Math::DEG_TO_RAD);
   
Graph::Matrix& m = mCubeTransform->GetTransform();
m = T2*T1;


If you directly set the rotation and translation components in one transformation matrix, you must be aware of the following restrictions:
  • Rotation is applied before translation is applied.
  • SetRotationComponentX or SetRotationX
    SetRotationComponentY or SetRotationY
    SetRotationComponentZ or SetRotationZ
    overwrites the upper 3x3 matrix. Therefore you get a rotation around the specified axis. Existing values will be overwritten. Calling SetRotationComponentX first and SetRotationComponentY second has the same effect as if you would call SetRotationComponentY only.
  • SetRotationComponent(angleX,angleY,angleZ) or SetRotation(angleX,angleY,angleZ)
    overwrites the upper 3x3 matrix with the given rotations in a fixed rotation order. Currently only one rotation order is implemented: ROTATION_ORDER_ZYX. Unfortunately the name is badly chosen, because it reflects the nesting order of the hypothetical Transform nodes and not the actual order of the rotations. The actual order of rotations is rotate X first, then rotate Y, then rotate Z.
  • The SetTranslationComponent/SetPosition methods overwrite the respective translation component value(s) in the transformation matrix and can be used independently.

What does this all mean for your example:
static Double angleX = 0;
static Double angleY = Math::HALF_PI;
static Double spinSpeed = 0.007;
angleX += spinSpeed;
angleX = Math::Fmod(angleX, Math::TWO_PI);
angleY = Math::Fmod(angleY, Math::TWO_PI);
mCubeTransform->SetRotation(angleX, angleY, 0);


In your example you rotate the cube around X first and then rotate the cube around Y by HALF_PI. The result is a spinning cube which is spinning around the axis-Z. That is the expected behaviour.

If you want to rotate the cube first around Y and then around X, a second transformation matrix needs to be used. E.g.

Graph::Matrix T1(Matrix::IDENTITY);
T1.SetRotationComponentY(Math::TWO_PI);

Matrix& m = mCubeTransform->GetTransform();
m.SetRotationComponentX(angleX);
m = m*T1;


I hope that helps to better understand transformation matrices.

Best regards, Ketschak
Reply
#13
Apost 
Hello Ketschak! Smile

Your explanation is very good for me.
The theory of 4x4 matrix I already knew but is very useful remember it!
It's all very well understood on my part. I'll apply the rotation of each axis in each matrix and use the multiplication of the two matrix, but the problem still remains, but by half.

The SetRotationComponent by the Graph::Matrix T1(Matrix::IDENTITY) is performing it carrying the other axes together.
You are using
Graph::Matrix T1(Matrix::IDENTITY);
T1.SetRotationComponentY(Math::TWO_PI);


Try to use to see
T1.SetRotationComponentY(Math::HALF_PI);


The rotation in the Cube Matrix is ok!
Matrix& m = mCubeTransform->GetTransform();
m.SetRotationComponentX(angleX);


Best Regards!
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#14
Apost 
Try to use
m = m*T1;
Reply
#15
Apost 
The problem was inverted, now the matrix that rotates "carrying the other axes together" is the matrix of the cube
Matrix& m = mCubeTransform->GetTransform();
m.SetRotationComponentX(angleX);

Tip: It would be nice to have these algorithms resolved directly by the engine with the call of an API method. Bring to the user the need for matrix multiplication is completely out of hand and running it directly from the engine certainly result in performance gains.

Smile

(25 Nov 2015, 19:17)Ketschak Wrote: Try to use
m = m*T1;
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#16
Apost 
Could you describe in detail what you are trying to achieve?
Reply
#17
Apost 
Hello Ketschak,

I just want a method to execute the rotation of the Cube in two or three axis but NOT carrying the other axes together.
That's it.

I will skip this problem with the cube.
Don't worry!

Very thanks.
(25 Nov 2015, 20:48)Ketschak Wrote: Could you describe in detail what you are trying to achieve?
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#18
Apost 
I think we're having trouble with names.

Some API methods perform the rotation carrying the other axes together.
Some methods do not.
The methods that has the description "by euler angles" carries in rotation the other axes together.
The methods that has the description "axis angle" do not carry in rotation the other axes together.

The problem is that if you rotate the cube with "by euler angles", you move the other axes along with the cube rotation. For example! Imagine the cube spinning a horizontal rotation, to the left or to the right with the method "by euler angles". You must realize it by the Y axis because it is the axis that we will spin horizontally for our camera.

Then the cube rotate horizontally carrying the X and Z axis, because that's how the method "by euler angle" perform rotation, carrying the other axes. So, for example, if you stop the cube 1 turn angle (HALF_PI) rotation Y axis, using the method "by euler angle", the X axis becomes to the position of the Z axis in the camera we are using.

If you rotate the X axis, for our same camera, we have a rotation identical to the Z axis. That's the problem of rotating using the methods "by euler angles". We carry the other axes.

What I'm talking about is that missing a method that can I perform, in the same tick, the rotation of two or more angles without carry any of the angles at their spatial position.

(25 Nov 2015, 20:48)Ketschak Wrote: Could you describe in detail what you are trying to achieve?
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply
#19
Apost 
Hi Flávio,

It looks like you would carry the axis but in fact it is the order of transformations that is the important factor. Keep in mind that the order of transformations is not associative. Let me explain it with a simple example using the example code you have provided.

I’m pressing 8 times the UP ARROW key to rotate the cube by 90° around the X axis (in world space).

Next I am pressing 8 times the RIGHT ARROW key to rotate the cube by 90° around the Y axis (in world space).

Now I’m again pressing two times the UP ARROW key.

What you probably want is that the cube now should again rotate around the X axis (in world space) but what actually happens, is that the cube is rotating around the Z axis (in world space). It looks like the cube has carried its X axis and is rotating around its local model space X axis which is the same direction as world space Z axis.

Now why does this happen? Let’s take a look at your code. You are using two transformation matrices and apply Mx first and My second. Mx rotates the cube around the X axis first and My rotates the cube around the y axis second. If you press the UP ARROW key you just increase the first x axis rotation.

What you want is:
  • ROTATE X 90°
  • ROTATE Y 90°
  • ROTATE X 22.5°

What you do is:
  • ROTATE X 112.5°
  • ROTATE Y 90°

No method can guess that you want to rotate 90x, 90y, 22.5x if you just give two angles 112.5x and 90y. The rotation method you are expecting cannot exist.

To solve the problem you have to apply the delta rotation and not the absolute angle. Here is the needed code change:

void App::ColorCubeLogic::TransformCube(const Logic::IState* state)
{
Graph::Matrix& cubeTransform = mCubeTransform->GetTransform();
//cubeTransform.SetIdentity();

mAngleX = Math::Fmod(mAngleX, Math::TWO_PI);
mAngleY = Math::Fmod(mAngleY, Math::TWO_PI);

Graph::Matrix Mx(Matrix::IDENTITY);
Mx.SetRotationComponentX(mAngleX);
cubeTransform = Mx * cubeTransform;

Graph::Matrix My(Matrix::IDENTITY);
My.SetRotationComponentY(mAngleY);
cubeTransform = My * cubeTransform;

debugMsg += Util::DoubleToString(mAngleX) + " / " +
Util::DoubleToString(mAngleY);
state->SetUserDebugMessage(debugMsg);
}

void App::ColorCubeLogic::OnProcessTick(const Logic::IState* state)
{ 

Logic::IDeviceHandler* deviceHandler = state->GetDeviceHandler();
   mAngleX = 0;
   mAngleY = 0;
if (deviceHandler->WasRawKeyPressed(RAWKEY_UP_ARROW)) {
debugMsg = "Angle X +" + Util::DoubleToString(mSpinSpeed) + "-> ";
mAngleX += mSpinSpeed;
TransformCube(state);
}
else if (deviceHandler->WasRawKeyPressed(RAWKEY_DOWN_ARROW)) {
debugMsg = "Angle X -" + Util::DoubleToString(mSpinSpeed) + "-> ";
mAngleX -= mSpinSpeed;
TransformCube(state);
}
else if (deviceHandler->WasRawKeyPressed(RAWKEY_LEFT_ARROW)) {
debugMsg = "Angle Y -" + Util::DoubleToString(mSpinSpeed) + "-> ";
mAngleY -= mSpinSpeed;
TransformCube(state);
}
else if (deviceHandler->WasRawKeyPressed(RAWKEY_RIGHT_ARROW)) {
debugMsg = "Angle Y +" + Util::DoubleToString(mSpinSpeed) + "-> ";
mAngleY += mSpinSpeed;
TransformCube(state);
}
}


I hope that addresses your problem.
Best regard, Ketschak
Reply
#20
Apost 
I wrote the code to finish my Cube program.

The idea from last week was to make the cube spin transition from one direction to another without the cube lose alignment with the camera.

I had to rewrite the whole program because of the rotation Delta novelty, then using the Spacebar you can change by 4 types of rotation.

The only problem that still remains is in the rotation Delta, but I do not think it has solution.

In my program, the rotational method, "Delta & Merge" the cube make the transition from one direction to another, merging (it turns both axis X and Y at same tick), but what happens is that the cube finish the transition "Delta & Merge" not perfectly aligned to the camera. I do not know exactly what happens, only that using the rotation with absolute angles "Not Delta & Merge" the cube is always aligned to the camera, despite the previus problem of the axis.

Finally this is it!
I go to another project now, and it was a pleasure to receive your help!

My Cube program is attached.

Best Regards!

(30 Nov 2015, 12:09)Ketschak Wrote: Hi Flávio,

It looks like you would carry the axis but in fact it is the order of transformations that is the important ...
...
Best regard, Ketschak


Attached Files
.zip   v4.zip (Size: 2.83 KB / Downloads: 1)
.zip   v4_cube_studies.zip (Size: 3.08 KB / Downloads: 0)
✠ nnDnn ✠ The peace of Christ! ✠ nnDnn ✠
Reply


Forum Jump:


Copyright © 2011-2017 Spraylight GmbH.