Advanced CPT (Java)

advertisement
Animation and Games Development
242-515, Semester 1, 2014-2015
8. Rotations
• Objective
o explain the main types of object
rotation, with the help of jME
examples
242-515 AGD: 8. Rotations
1
Overview
1.
2.
3.
4.
5.
6.
7.
Rotation Forms
Matricies
Euler Angles
Multiple Rotations
Axis-angle Rotation
Quaternions
Why use Quaternions?
242-515 AGD: 8. Rotations
2
1. Rotations Forms
• There are four main ways of rotating a 3D object:
o
o
o
o
matricies
euler angles
axis-angles
quaternions
• jME supports all of these, which I'll demonstrate with
variations of the Sinbad.java application, which
displays a 3D monster model.
242-515 AGD: 8. Rotations
3
Sinbad.java
• The model is facing
along the +z axis,
towards the
camera. It is
centered at the
origin.
+y
+x
+z
242-515 AGD: 8. Rotations
4
Partial Code
public void simpleInitApp()
{
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
Spatial player = assetManager.loadModel(
"Models/Sinbad/Sinbad.mesh.xml");
player.setLocalScale(0.5f);
rootNode.attachChild(player);
}
// end of simpleInitApp()
I'll be explaining directional
light and mesh models later.
242-515 AGD: 8. Rotations
5
2. Matricies
• I described the 4x4 homogenous matrices for
rotation about the x-, y-, and z- axes in part 7.
M z -rotate
 cos 

 sin 

 0

 0
 sin 
0
0
cos 
0
0
0
1
0
0
0
1







Rotates around the z-axis through the angle θ
242-515 AGD: 8. Rotations
6
• Similar matrices for rotations about x-, y- axes
M x -rotate
M y -rotate
242-515 AGD: 8. Rotations







1
0
0
0
0
cos 
 sin 
0
0
sin 
cos 
0
0
0
0
1
0
sin 
0
1
0
0
0
cos 
0
0
0
1
 cos 

 0

  sin 

 0














7
• The Spatial class has numerous rotation methods.
• The class includes:
o Spatial.setLocalRotation(Matrix3f rotation)
o It sets the local rotation of this node using a Matrix3f object.
o The contents of the Matrix3f object should be the top left
3x3 part of the homogenous 4x4 matrix.
242-515 AGD: 8. Rotations
8
Rotating 45 degs around the z-axis
• The rotation uses the cosine and sine of 45 degrees
(in radians):
M z -rotate
242-515 AGD: 8. Rotations
 cos 45  sin 45

 sin 45 cos 45

 0
0

 0
0
0
0
0
0
1
0
0
1







9
RotMatrix.java Result
45 degrees
around
the z-axis
242-515 AGD: 8. Rotations
10
RotMatrix.java
(partial)
public void simpleInitApp()
{
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
Spatial player =
assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
player.setLocalScale(0.5f);
rootNode.attachChild(player);
// create rotation matrix for 45 degs around z-axis
float cos45 = (float) Math.cos(Math.toRadians(45));
float sin45 = (float) Math.sin(Math.toRadians(45));
Matrix3f mat = new Matrix3f(Matrix3f.IDENTITY);
mat.setColumn(0, new Vector3f(cos45, sin45, 0));
mat.setColumn(1, new Vector3f(-sin45, cos45, 0));
the new part
player.setLocalRotation(mat);
} // end of simpleInitApp()
242-515 AGD: 8. Rotations
11
• jME has a FastMath class with faster (but less
accurate) trigonometry constants and methods
than those in Java's Math class.
• The matrix code can be rewritten using FastMath:
// create rotation matrix for 45 degs around z-axis
float cos45 = FastMath.cos(FastMath.QUARTER_PI);
float sin45 = FastMath.sin(FastMath.QUARTER_PI);
the new part
Matrix3f mat = new Matrix3f(Matrix3f.IDENTITY);
mat.setColumn(0, new Vector3f(cos45, sin45, 0));
mat.setColumn(1, new Vector3f(-sin45, cos45, 0));
:
242-515 AGD: 8. Rotations
12
3. Euler Angles
• Euler angles express rotations in terms of angles
around the object's local x-, y-, and z- axes.
• There's no need to build a matrix, just specify the
three angles.
• Traditionally, these angles are called:
o yaw – a rotation around the y-axis
+y
o pitch – a rotation around x-axis
o roll – a rotation around the z-axis
+z
242-515 AGD: 8. Rotations
+x
13
Rotating 45 degs around the z-axis (again)
• The relevant Spatial method for Euler angle
rotations is:
o Spatial.rotate(float x, float y, float z)
o it rotates the spatial by the x-, y- and z- axis angles (in
radians), in the local coordinate space
o the arguments are not (yaw, roll, pitch) even though
that's what the documentation says
• The RotRoll.java application applies a 45 degree
roll (rotation around the z-axis) using euler angles
instead of a matrix.
242-515 AGD: 8. Rotations
14
RotRoll.java Result
• The same result
as RotMatrix.java
242-515 AGD: 8. Rotations
15
Partial Code
RotRoll.java
public void simpleInitApp()
{
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
Spatial player = assetManager.loadModel(
"Models/Sinbad/Sinbad.mesh.xml");
player.setLocalScale(0.5f);
No
rootNode.attachChild(player);
matrix
code required.
player.rotate(0, 0, (float)Math.toRadians(45));
// player.rotate(0, 0, FastMath.QUARTER_PI);
// roll: rotate around z-axis
} // end of simpleInitApp()
242-515 AGD: 8. Rotations
16
Rotating the Local Coord. Space
• An euler angle rotation rotates the local coordinate
space (and everything inside it).
• This means that the x-, y-, and z- axes of the local
space will no longer match up with the x-, y-, and zaxes of the global space.
local coord. space
y
x
global
coord.
space
y
z
x
z
242-515 AGD: 8. Rotations
global y
spatial.rotate() coord.
space
z
local coord. space
x
17
Turning Around the y-axis
• Make the model turn constantly around its y-axis.
RotLoop.java (very similar to part 6's HelloLoop.java)
242-515 AGD: 8. Rotations
18
Partial Code
RotLoop.java
• The constant change requires the call to rotate() to
be moved to the simpleUpdate() method inherited
from Simple Application.
private Spatial player;
// now global
public void simpleInitApp()
{ DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
player = assetManager.loadModel(
"Models/Sinbad/Sinbad.mesh.xml");
player.setLocalScale(0.5f);
rootNode.attachChild(player);
} // end of simpleInitApp()
242-515 AGD: 8. Rotations
19
public void simpleUpdate(float tpf)
// update action, called from game loop automatically
// the player is rotated around the y-axis
{
player.rotate(0, 2*tpf, 0);
}
242-515 AGD: 8. Rotations
20
4. Multiple Rotations
• An euler angle rotation rotates the local coordinate
space.
• This can become confusing when multiple euler
rotations are applied to a model.
• For example:
player.rotate(0, FastMath.HALF_PI, 0);
player.rotate(0, 0, FastMath.HALF_PI);
player.rotate(FastMath.HALF_PI, 0, 0);
// y-axis rotation
// z
// x
• It's confusing since the x-, y-, and z-axes of the local
coordinate space do not match the global space
axes after the first rotation.
242-515 AGD: 8. Rotations
21
rotate(0, FastMath.HALF_PI, 0);
90 degree y-axis rotation
y
y
x
x
z
z
rotate(0, 0, FastMath.HALF_PI);
90 degree z-axis rotation
x
y
x
z
z
y
rotate(FastMath.HALF_PI,0,0);
90 degree x-axis rotation
242-515 AGD: 8. Rotations
22
RotationTest1 Result
242-515 AGD: 8. Rotations
23
Partial Code
RotationTest1.java
public void simpleInitApp()
{
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
Spatial player = assetManager.loadModel(
"Models/Sinbad/Sinbad.mesh.xml");
player.setLocalScale(0.5f);
rootNode.attachChild(player);
player.rotate(0, FastMath.HALF_PI, 0); // y-axis rotation
player.rotate(0, 0, FastMath.HALF_PI); // z
player.rotate(FastMath.HALF_PI, 0, 0); // x
} // end of simpleInitApp()
242-515 AGD: 8. Rotations
24
More Complex rotate() Calls
• The complexity of multiple rotate() calls is made
worse, if several euler angle rotations are combined
into a single rotate() call.
• For example:
player.rotate(0, FastMath.HALF_PI, 0);
player.rotate(0, 0, FastMath.HALF_PI);
player.rotate(FastMath.HALF_PI, 0, 0);
// y-axis rotation
// z
// x
• is the same as
player.rotate(FastMath.HALF_PI, FastMath.HALF_PI, FastMath.HALF_PI);
• You must remember the hidden order: y, z, x.
o this is not the same as (x, y, z) or (z, y, x) or others since
rotation is non-commutative.
242-515 AGD: 8. Rotations
25
• e.g: reorder the rotate() lines in RotationsTest1.java:
player.rotate(FastMath.HALF_PI, 0, 0); // x was last, now first
player.rotate(0, FastMath.HALF_PI, 0); // y
player.rotate(0, 0, FastMath.HALF_PI); // z
• Result:
z
x
y
242-515 AGD: 8. Rotations
26
5. Axis-angle Rotation
• An axis-angle rotation is a rotation around a userspecified vector (a rotation axis)
o this is more flexible than an euler angle rotation which fixes
the vector to be the x-, y-, or z- axis
242-515 AGD: 8. Rotations
27
RotAxis.java
• Rotate the model 90 degrees around the y=x line
y
y
z
242-515 AGD: 8. Rotations
x
x
z
28
Partial Code
RotAxis.java
public void simpleInitApp()
{
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
Spatial player = assetManager.loadModel(
"Models/Sinbad/Sinbad.mesh.xml");
player.setLocalScale(0.5f);
rootNode.attachChild(player);
angle
axis
// rotate 90 degrees around y=x line
Vector3f xyVec = new Vector3f(1.0f, 1.0f, 0).normalize();
Quaternion q1 = new Quaternion().fromAngleAxis(FastMath.HALF_PI, xyVec);
player.rotate(q1);
}
In jME an axis-angle must
be executed as a quaternion
242-515 AGD: 8. Rotations
29
Euler's Rotation Theorem
• Euler's theorem: any rotation can be written as a
single rotation about an axis.
• This means that we can rotate a model to any
position with one axis-angle rotation
o but working out the necessary axis and angle can be
tricky!
242-515 AGD: 8. Rotations
30
6. Quaternions
• Quaternions are an extension to complex numbers.
A quaternion is made up of 4 values:
o one is a scalar (called w), and the other three are a vector
in 3D complex number space (i, j, k)
• A quaternion (i, j, k, w) can be viewed as a point on
the surface of a 4D sphere
o which I'll draw as a 3D sphere
(i, j, k, w)
242-515 AGD: 8. Rotations
31
Quaternions and Axis-Angles
• There's a simple mapping between an axis-angle
and a quaternion.
• If the axis-angle is [x, y, z, θ] then the corresponding
quaternion (i, j, k, w) is equal to:
o ( sin(θ/2)*x, sin(θ/2)*y, sin(θ/2)*z, cos(θ/2) )
• Example: a rotation of 90 degrees around the y-axis
o axis-angle = [0, 1, 0, π/2]
o quaternion = (0, sqrt(2)/2, 0, sqrt(2)/2 )
• since cos(45) = sin(45) = sqrt(2)/2
242-515 AGD: 8. Rotations
32
Quaternions in jME
public void simpleInitApp()
{
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
Spatial player = assetManager.loadModel(
"Models/Sinbad/Sinbad.mesh.xml");
player.setLocalScale(0.5f);
rootNode.attachChild(player);
// rotate 90 degs around y-axis
float trig = FastMath.sqrt(2)/2;
Quaternion q1 = new Quaternion(0, trig, 0, trig);
// x,
y, z, w
// carry out y-axis rotation of 90 degrees
player.rotate(q1);
} // end of simpleInitApp()
242-515 AGD: 8. Rotations
new part
33
Combining Rotations
• Two quaternion rotations are combined ('added
together') by multiplication
o e.g. carry out two 90 degree rotations around the y-axis:
// rotate 90 degs around y-axis
float trig = FastMath.sqrt(2)/2;
Quaternion q1 = new Quaternion(0, trig, 0, trig);
// x,
y, z, w
// carry out two rotations of 90 degrees
Quaternion q2 = q1.mult(q1);
player.rotate(q2);
RotQuats.java
242-515 AGD: 8. Rotations
34
7. Why Use Quaternions?
• We often want to smoothly rotate a model from
one position to another
o called rotational interpolation
• Calculating a smooth rotational interpolation
between two matrix rotations, two euler angles, or
two axis-angles is difficult
o but rotational interpolation is easy between quaternions
242-515 AGD: 8. Rotations
35
Slerping
• Rotating between two quaternions is equivalent to
following the shortest path of the great circle arc
between them
o a great circle arc is the distance over the sphere's surface
between two points
o called slerping (spherical linear interpolatation)
242-515 AGD: 8. Rotations
36
Slerping in jME
• We want to smoothly rotate the model between
the following two positions:
rotation of -90 degrees
around the z-axis
242-515 AGD: 8. Rotations
rotation of 180 degrees
around the y = -x line
37
rotate -90 degree around z-axis
y
x
z
z
y
x
z
y
x
z
242-515 AGD: 8. Rotations
x
rotate 180 degrees around
the y= -x line
y
38
Implementation
RotSlerp.java
• The simpleInitApp() method creates the two
positions as quaternion rotations.
• The simpleUpdate() method uses jME's
Quaternion.slerp() to smoothly interpolate between
the two quaternions
o this will cause the model to slowly rotate between the two
positions, updated with each call to simpleUpdate()
242-515 AGD: 8. Rotations
39
Execution
242-515 AGD: 8. Rotations
RotSlerp.java
40
RotSlerp Code
// globals
private static final float SLERP_STEP = 0.025f;
private Spatial player;
private Quaternion rightFrontQ, leftBackQ, slerpQ;
private float currSlerp = 0f;
private boolean isForward = true;
public void simpleInitApp()
{
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
player = assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
player.setLocalScale(0.5f);
rootNode.attachChild(player);
:
242-515 AGD: 8. Rotations
41
:
// rotated -90 degs around z-axis
Vector3f zVec = new Vector3f(0, 0, 1.0f);
rightFrontQ =
new Quaternion().fromAngleAxis(-FastMath.HALF_PI, zVec);
// rotated 180 degs around y=-x line
Vector3f negxyVec = new Vector3f(-1.0f, 1.0f, 0).normalize();
leftBackQ =
new Quaternion().fromAngleAxis(FastMath.PI, negxyVec);
slerpQ = new Quaternion();
player.rotate(rightFrontQ);
} // end of simpleInitApp()
242-515 AGD: 8. Rotations
// player starts at rightFront
42
public void simpleUpdate(float tpf)
// update action, called from game loop automatically
// the player is slerped between the two quaternions
{
if (isForward)
slerpQ is a rotation
currSlerp += SLERP_STEP;
between the other two
else
currSlerp -= SLERP_STEP;
slerpQ.slerp(rightFrontQ, leftBackQ, currSlerp);
player.setLocalRotation(slerpQ);
if ((currSlerp >= 1f) || (currSlerp <= 0f))
isForward = !isForward;
}
// end of simpleUpdate()
242-515 AGD: 8. Rotations
varies from 0 to 1
and back to 0
43
Download