I am translating Math For Dummies slideshow into a printable format and when going through the slides I am having a problem with understanding some things.
Hopefully this is clear.
If what I am showing here is correct for slide 40 where
quatA = Vector3f(1, 0, 0)
quatB = Vector3f(-1, 0, 0)
quatC = Vector3f(0, 0, 1)
Slide 40
Quaternion quatA = new Quaternion();
quatA.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Z);
-90 deg around Z axis
(Y) Y
| -z | -z
| / | /
-x ------+------ X -x ------+------ (X)
/ | / |
Z | Z |
-y -y
Before After
quatA = Vector3f(1, 0, 0)
Quaternion quatB = new Quaternion();
quatB.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y);
-90 deg around Y axis
Y Y
| -z | -z
| / | /
-x ------+------ X (-x) ------+------ X
/ | / |
(Z) | Z |
-y -y
Before After
quatB = Vector3f(-1, 0, 0)
Quaternion quatC = quatA.mult(quatB);
-90 deg around Z axis & -90 deg around Y axis
(Y) Y Y
| -z | -z | -z
| / | / | /
-x ------+------ X -x ------+------ (X) -x ------+------ X
/ | / | / |
Z | Z | (Z) |
-y -y -y
Before Step 1 Step 2
quatC = Vector3f(0, 0, 1)
Then is this correct where Vector3f vectorB = quatC.mult(vectorA);?
vectorB = Vector3f(0, -1, 0)
=======================================================
Slide 41
Vector3f vectorA = new Vector3f(0, 1, 0);
(Y)
| -z
| /
-x ------+------ X
/ |
Z |
-y
Before
vectorA = Vector3f(0, 1, 0)
Vector3f vectorB = quatC.mult(vectorA);
90 deg around X axis
Y Y
| -z | -z
| / | /
-x ------+------ X -x ------+------ X
/ | / |
(Z) | Z |
-y (-y)
Before After
vectorB = Vector3f(0, -1, 0)
Slide 42 and 45 shows it as vectorB = Vecotr3f(0, 0, 1).
=======================================================
Slide 42 & 45
(Y) Y
| -z | -z
| / | /
-x ------+------ X -x ------+------ X
/ | / |
Z | ( Z) |
-y -y
Before After
Vector3f(0, 0, 1)
That looks fishy already… aren’t they supposed to be Quaternions and no Vector3f? I see a lot of that in your post and looks like something is wrong in your understanding .
It’s a bit confusing. What is your question exactly?
Yeah, I found it very confusing also. I couldn’t even follow the post.
I do kind of feel like the slides are wrong, though.
quatC is quatA.mult(quatB) and then it describes that first it’s rotated by quatA and then by quatB which sounds backwards to me. I’d have to stop any think about it for longer than I have at the moment.
…but if you think of it in scene graph terms, the vector first rotates in the child space and then the parent space.
Its hard to describe images in text and keep it short.
The vector stuff is not meant to say there are vector changes, its meant to show rotation of a point as a description.
This is the scenegraph in text.
Y
| -z
| /
-x ------+------ X
/ |
Z |
-y
Slide 40 shows these 3 variables.
Quaternion quatA = new Quaternion();
quatA.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Z);
Quaternion quatB = new Quaternion();
quatB.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y);
Quaternion quatC = quatA.mult(quatB);
This is a textual representation of the rotation of quatA and quatB in slide 40. Positive Axis depicts orientation and rotation.
Quaternion quatA = new Quaternion();
quatA.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Z);
-90 deg around Z axis
(Y) -x
| -z | -z
| / | /
-x ------+------ X -y ------+------ (Y)
/ | / |
(Z) | (Z) |
-y X
Before After
Quaternion quatB = new Quaternion();
quatB.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y);
-90 deg around Y axis
(Y) (Y)
| -z | -x
| / | /
-x ------+------ X (z) ------+------ -Z
/ | / |
(Z) | X |
-y -y
Before After
This is where the first error shows for Slide 40, quatC.
Slide 43 and Slide 44 say this is what happens. I will stop here in case I am wrong. It depicts rotation around Z axis correctly in Slide 43 but then shows rotation around X axis in Slide 44.
Edit: Whats depicted in slide 44 would be +90 deg rotation(Step 2 below) because rotation happens from the perspective of +X looking down to -X with +Y as top as in Step 1. The rotation is counter clockwise which means it is POSITIVE PI.
Quaternion quatC = quatA.mult(quatB);
-90 deg around Z axis & +90 deg around X axis
(Y) -X -X
| -z | -z | -y
| / | / | /
-x ------+------ X -y ------+------ (Y) (z) ------+------ -Z
/ | / | / |
(Z) | (Z) | (Y) |
-y x x
Before Step 1 Step 2
Slide 43 Slide 44
I say this is what happens. Just as what is described, rotate around Z axis, followed by rotation around Y axis.
Quaternion quatC = quatA.mult(quatB);
-90 deg around Z axis & -90 deg around Y axis
(Y) -X (Z)
| -z | -z | -x
| / | / | /
-x ------+------ X -y ------+------ (Y) -y ------+------ (Y)
/ | / | / |
(Z) | (Z) | X |
-y x -z
Before Step 1 Step 2
Slide 43 Slide 44 should be this
I hope this clarifies this better. What I say happens is what shows when running these variables in game.
But wouldn’t you get the same result if you rotated around Y first (essentially a no-op) and then around X… I think the description of quatC is what is incorrect.
I’d have to see actual vectors in and out to confirm. The naming of the vectors after quaternions really threw me in your first post so I couldn’t follow very easily.
If I have time to investigate further I will do more than just these fly-by comments.
Yes and no. The description and illustration is what I think is wrong.
As I understand what’s happening, I could be wrong but here goes.
Yes as in exactly what you describe is done in Slide 46 with this Quanternion. quatC = quatB.mult(quatA);
No as in quatB.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y);
is rotating around Y in both local and world space. Then the final roatation of quatA.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Z);
is applied which rotates around local Z axis. World axis is X.
The slide descriptions for that Quaternion are also wrong.
I was trying to establish that my thinking is correct before moving onto the latter slides because after slide 40, everything falls apart if I am correct.
So for what I am describing with slide 40, quatC = quatA.mult(quatB);
Slide 42 says its rotating right around Z axis local and world space, then slide 43 says towards you. Which is local space X axis and world space Y axis. This is not what is happening.
It’s rotated to your right around Z axis local and world space, then rotates face up. Which is local space Y axis and world space X axis.
Please correct me if I am wrong on this.
I am skipping the vector stuff because its also wrong and would be a distraction at this point.
it seems like each quat is a relative rotation.
vector results of each rotation step in the process should represent the principle vector next to the resultant vector to gauge the difference.
for example:
quatA is -90 about Z and begins at (0,1,0) and ends at (1,0,0) as noted…
quatB is -90 about Y, so a result of (-1,0,0) would mean a start at (0,0,1)?
…if quatB begins from quatA’s unitY rotated to unitX, it will result in a forward facing unitZ vector?
quatC is -90 about Z then Y and begins at (0,1,0) and ends at (0,0,1) as it applies both rotations to the same original vector.
also, i could be wrong here, but my assumption is that each quat rotation is applied using world coords, so even if you rotate the up vector to be the left vector, subsequent rotations still use the world’s up vector.
I rewrote the app from above to use key input to show what I am saying.
package TestRotation;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.debug.Arrow;
public class TestRotation extends SimpleApplication implements ActionListener {
private static final Quaternion ROTATION = new Quaternion().fromAngleAxis(FastMath.PI * 225/180, new Vector3f(0,1,0));
private static final Quaternion PITCH045 = new Quaternion().fromAngleAxis(FastMath.PI/4, new Vector3f(1,0,0));
private Node pivot;
Quaternion quatA, quatB, quatC, quatD, reset;
private String[] actionInputs;
public static void main(String[] args) {
TestRotation app = new TestRotation();
app.start();
}
@Override
public void simpleInitApp() {
flyCam.setEnabled(false);
cam.setLocation(new Vector3f(10, 10, 10));
Quaternion quat = ROTATION.mult(PITCH045);
cam.setRotation(quat);
actionInputs = new String[]{
"one", "two", "three", "four", "five", "six","seven", "eight", "nine", "ten"
};
inputManager.addListener(this, actionInputs);
//-90 around Z Axis
quatA = new Quaternion();
quatA.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Z);
//-90 around Y Axis
quatB = new Quaternion();
quatB.fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y);
//-90 around Z Axis, -90 around Y Axis
quatC = quatA.mult(quatB);
//-90 around Y axis, -90 around Z axis
quatD = quatB.mult(quatA);
//reset
reset = new Quaternion(0,0,0,1);
setupKeys();
pivot = new Node("pivot");
Vector3f center = new Vector3f(Vector3f.ZERO);
attachCoordinateAxes(center);
rootNode.attachChild(pivot);
}
@Override
public void simpleUpdate(float tpf) {
//TODO: add update code
}
@Override
public void simpleRender(RenderManager rm) {
//TODO: add render code
}
private void attachCoordinateAxes(Vector3f pos) {
Arrow arrow = new Arrow(Vector3f.UNIT_X);
putShape(arrow, ColorRGBA.Red).setLocalTranslation(pos);
arrow = new Arrow(Vector3f.UNIT_Y);
putShape(arrow, ColorRGBA.Green).setLocalTranslation(pos);
arrow = new Arrow(Vector3f.UNIT_Z);
putShape(arrow, ColorRGBA.Blue).setLocalTranslation(pos);
}
private Geometry putShape(Mesh shape, ColorRGBA color) {
Geometry g = new Geometry("coordinate axis", shape);
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mat.getAdditionalRenderState().setWireframe(true);
mat.getAdditionalRenderState().setLineWidth(4);
mat.setColor("Color", color);
g.setMaterial(mat);
pivot.attachChild(g);
return g;
}
private void setupKeys() {
inputManager.addMapping("one", new KeyTrigger(KeyInput.KEY_1));
inputManager.addMapping("two", new KeyTrigger(KeyInput.KEY_2));
inputManager.addMapping("three", new KeyTrigger(KeyInput.KEY_3));
inputManager.addMapping("four", new KeyTrigger(KeyInput.KEY_4));
inputManager.addMapping("five", new KeyTrigger(KeyInput.KEY_5));
inputManager.addMapping("six", new KeyTrigger(KeyInput.KEY_6));
inputManager.addMapping("seven", new KeyTrigger(KeyInput.KEY_7));
inputManager.addMapping("eight", new KeyTrigger(KeyInput.KEY_8));
inputManager.addMapping("nine", new KeyTrigger(KeyInput.KEY_9));
inputManager.addMapping("ten", new KeyTrigger(KeyInput.KEY_0));
}
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (name.equals("one") && !isPressed) {
//-90 around Z Axis
pivot.rotate(quatA);
}
if (name.equals("two") && !isPressed) {
//-90 around Y Axis
pivot.rotate(quatB);
}
if (name.equals("three") && !isPressed) {
//-90 around Z Axis, -90 around Y Axis
pivot.rotate(quatC);
}
if (name.equals("four") && !isPressed) {
//-90 around Y axis, -90 around Z axis
pivot.rotate(quatD);
}
if (name.equals("five") && !isPressed) {
pivot.setLocalRotation(quatA);
}
if (name.equals("six") && !isPressed) {
pivot.setLocalRotation(quatB);
}
if (name.equals("seven") && !isPressed) {
pivot.setLocalRotation(quatC);
}
if (name.equals("eight") && !isPressed) {
pivot.setLocalRotation(quatD);
}
if (name.equals("ten") && !isPressed) {
pivot.setLocalRotation(reset);
}
}
}
Relative as in from the start rotation at the time the Quaternion is applied in local space.
Yes and no.
No as in there are no vector changes. just a rotation. If this were a spatial, it would still be located at its original location. The vectors of (-1,0,0) and (0,0,1) you mention are not vectors but highlights of the axis for demonstration purposes.
As nehon pointed out,
A bad choice for a demonstration on my part.
Back to your question.
Quaternion rotation is applied based on current rotation of the local space. 0 degrees for Y starts at forward (Z), 0 degrees for Z is Up (Y), zero degree for X is Up (Y).
So yes, as in, the (Y) axis is rotated -90 (clockwise), i.e. Z (zero degree), is rotated towards what you say is (-1,0,0).
The quaternion quatA rotated local (Y) to world (X) around local (Z) so local space is now on its side.
Your spatial is facing forward.
Next, quatB rotates -90 around local (Y), ie clockwise, using the forward Vector (Z) which, is zero degrees. This moves local (Z) to world space (Y). i.e your spatial is face up.
If you next apply another rotation Quaternion, it will rotate based off the current rotation of local space.
It first applies the rotaion of quatA, then applies the rotaion of quatB to wherever quatA roatation ended. Not to the same starting rotation both times.
Original rotation both times implies where quatA rotation started from is where quatB also rotates from. This is exactly what slide 44 says is happening.
This is what my original question is about and what I think have demonstrated is wrong with the slides after slide 40.
This is exactly what the whole purpose of this thread is about in a nutshell.
My answer is no, they are based on local space, otherwise things like this would never make sense,
This is quatA so we are rotating clockwise around the (Z) axis. You rotated Y once to WORLD SPACE X. It is then rotated face up with Z moving to the WORLD SPACE Y.
If the LOCAL SPACE is on its side with local (Y) at world (X), and local (Z) is at world (Y), and you then used a Quaternion to rotate around WORLD SPACE (Z), the rotation would be expected to rotate clockwise like so.
Y (Z)
| |
| |
+------ X +------ (Y) +------ (Z)
/ / / |
Z X X |
(y)
World Space Local Space Start Local Space End
This is not what happens.
It is rotated around LOCAL SPACE (Z) and the rotation happens like so,
Y (Z) (Z)
| | |
| | |
+------ X +------ (Y) x ------+
/ / /
Z X (Y)
World Space Local Space Start Local Space End
You can test this in my app by hitting key 3, which will do quatC rotation, then key 1, which is quatA rotation. Key 0 will reset the rotation.
You can also test it by typing key 1 (quatA), key 2 (quatB), then key 1 (quatA).
BTW, key 1 followed by key 2 demonstrates the movement which shows slide 44 is wrong. The vector stuff is also wrong.
So either you are correct and Quaternions rotate based on world space always and everything I have written here is wrong, or I am correct and Quaternions rotate based on local space always.
What does “world space” and “local space” mean to a Quaternion?
quatA.mult(quatB) is of course going to give you some combination of those rotations such that if you applied them both in a particular order individually to a vector then you’d get the same result. There is no “world space” or “local space”… just where you start and where you end.
The idea of “world space” and “local space” only make sense in a scene graph.
Here is my take… without having time to write code to check but the scene graph would be broken if this weren’t true:
If quatA rotates 90 degrees around the Z-axis.
If quatB rotates 45 degrees around the Y-axis.
If quatC is quatA.mult(quatB)
Then I think (signs could be backwards but you will get the point):
quatA.mult(Vector3f.UNIT_Y) = 1, 0, 0
quatB.mult(Vecotr3f.UNIT_Z) = 0.707, 0, 0.707 (approximately)
Vector3f v = Vector3f.UNIT_Y;
v = quatC.mult(v);
…should be the same as:
Vector3f v = Vector3f.UNIT_Y;
v = quatB.mult(v);
v = quatA.mult(v);
Which I think would be something like: 0, 0.707, 0.707
…again I may have my signs flipped somewhere but the point should be the same.
In this particular case, it is being used as a visual to represent the rotation and orientation of axis.
Don’t take it literal.
This is how its visually depicted in math for dummies. This is what this is about. The visuals are not correct.
To try once more and get through, I will remove all references to axis or anything else, the visual shows the rotation is first to the right in slide 43. Then in slide 44, the visual shows the rotation is forward, towards you. This is not so… it rotates in place, ie face up…
The vector visual in slide 42 is also wrong…its says the rotation is from up then down towards you, around the X axis…in reality, it is from up then down to the right, around the Z axis…
Everything after slide 40 is either wrong about Quaternions or my demo app that uses the exact Quaternions and I am wrong. It’s pretty straight forward, compare the results of the app to the slides. They do not match.
and run the app side by side to compare. Anyone can do it. No calculating, brain crunching required. Enter 1, 2, 1. or to save time, 3, 1. 0 to reset. That’s it.
I was trying to tell you how it should work. I don’t know if the slides are right or wrong and I don’t have time to look into it. Unless you can get my kids up for school and take my wife to get her cast removed.
But you know what… I’m done with this thread. Good luck.