Hello,

I am implementing the moving functions für my units and have a few problem with the rotation.

I am using two functions to calculate the rotation and translation values for the next position. I need to be able to keep the maxRotationSpeed and maxTranslationSpeed for each unit variable. The basic code (ugly and expensive) works already, waypoints are reached and processed. However, i still cannot figure out the correct usage of quaternions. I have already looked a few times in the math for dummies section but i still cannot figure out how to make following things: (The modeld rotates every time strangely when i try to modify one of the axis manually)

a) I still need to disallow all axis rotations beside the up axis.

b) introduce the maxRotationSpeed in the calculations.

Here is the code i am using:

[java]

public void calculateNextRotation(float tpf){

if(this.waypoint.size()>0){

Quaternion current=this.movingNode.getLocalRotation().clone();

Quaternion lookAt=current.clone();

Vector3f toLookAt = this.waypoint.get(0).clone();

toLookAt.subtractLocal(this.movingNode.getWorldTranslation());

lookAt.lookAt(toLookAt, new Vector3f(0,1,0));

lookAt.subtractLocal(current);

// lookAt.set(this.movingNode.getLocalRotation().getX(), lookAt.getY(),this.movingNode.getLocalRotation().getZ(), this.movingNode.getLocalRotation().getW());

lookAt.multLocal(tpf);

current.addLocal(lookAt);

this.nextRotation=current;

}

}

public void calculateNextTranslation(float tpf){

if(this.nextTranslation!=null){

if(this.waypoint.size()>0){

Vector3f pos=this.movingNode.getLocalTranslation().clone();

float distance=pos.distance(this.waypoint.get(0));

Vector3f moveVector=new Vector3f(0,0,((Math.min(distance, this.maxMovingSpeed*tpf))));

this.nextRotation.multLocal(moveVector);

Vector3f posTmp=pos.clone();

posTmp.addLocal(moveVector);

float distance2=posTmp.distance(this.waypoint.get(0));

if(distance2>distance){

posTmp=pos.clone();

posTmp.addLocal(moveVector.negateLocal());

}

posTmp.setY(0);

this.nextTranslation=posTmp;

if(distance<=5){

this.waypoint.remove(0);

}

}

}

}

[/java]

Any advise would be extremely helpful.

Do not use rotations but use direction vectors and then convert them to quaternions using lookAt, its much easier than handling rotations.

Also, re-read the math for dummies: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:math_for_dummies

normen said:

Do not use rotations but use direction vectors and then convert them to quaternions using lookAt, its much easier than handling rotations.

Also, re-read the math for dummies: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:math_for_dummies

I cannot find the in math for dummies a single word about the 'W' in the quaterion. Why is it needed to rotate correctly around the Y axis? And how is it correlated with the Y roation value?

Hello, i got the rotation speed working with the use of slerp. Don’t know if its the correct way, but it works

There are still some issues, the code generates anomalies where the unit sinks below 0 (Should not be possible since the translation code sets Y to 0) and the unit rotates ‘randomly’.

I have made a small video showing the issue. At 0:08 the unit rotates strangely.

http://www.youtube.com/watch?v=t9MFyo1RoiQ

This is the code i use:

[java]

public void calculateNextRotation(float tpf){

if(this.waypoint.size()>0){

Quaternion current=this.movingNode.getLocalRotation().clone();

Quaternion lookAt=current.clone();

Vector3f toLookAt = this.waypoint.get(0).clone();

toLookAt.subtractLocal(this.movingNode.getWorldTranslation());

lookAt.lookAt(toLookAt, new Vector3f(0,1,0));

lookAt.subtractLocal(current);

Quaternion mod=new Quaternion(0,0,0,0);

mod.slerp(lookAt, 1f*tpf);
mod.set(0, mod.getY(), 0, mod.getW());
current.addLocal(mod);
this.nextRotation=current;
}
}
public void calculateNextTranslation(float tpf){
if(this.nextTranslation!=null){
if(this.waypoint.size()>0){
Vector3f pos=this.movingNode.getLocalTranslation().clone();
float distance=pos.distance(this.waypoint.get(0));
Vector3f moveVector=new Vector3f(0,0,((Math.min(distance, this.maxMovingSpeed*tpf))));

this.nextRotation.multLocal(moveVector);

Vector3f posTmp=pos.clone();

posTmp.addLocal(moveVector);

float distance2=posTmp.distance(this.waypoint.get(0));

if(distance2>distance){

posTmp=pos.clone();

posTmp.addLocal(moveVector.negateLocal());

}

posTmp.setY(0);

this.nextTranslation=posTmp;

if(distance<=5){

this.waypoint.remove(0);

}

}

}

}

[/java]

The four components of a quaternion and how they describe a rotation is a big mystery to me and I guess most other people using them too. Its storing the information in like 4 dimensions and generally is a pretty slick mathematical solution to matrix calculations… You don’t really need to know that to use quaternions with the provided methods for accessing euler angles etc. Also, just use Quaternion.ZERO.clone() to get a new zero Quaternion.

Hello,

I am sorry to bother you all again. I have changed the rotation code to a degree based calculation method.

The function to get the needed angles is following:

[java]

Quaternion currentDirection=this.movingNode.getWorldRotation().clone();

Quaternion directionToLook = currentDirection.clone();

Vector3f target=this.waypoint.get(0).clone();

target.subtractLocal(this.movingNode.getWorldTranslation());

directionToLook.lookAt(target, new Vector3f(0,1,0));

float currentDegree=currentDirection.toAngleAxis(new Vector3f(0,1,0))*FastMath.RAD_TO_DEG;

float targetDegree=directionToLook.toAngleAxis(new Vector3f(0,1,0))*FastMath.RAD_TO_DEG;
[/java]
This code works very nice as long the target(x) value is smaller then the node(x) value. However, if the x value of the target is larger then the current point there are two different and not understandable option. See graphic below:
The whole rotation function if following:
(i know that the if(difference!=0) part might not work in that way, but the current problem is that it seems i cannot get the angles reliable)
[java]
public void calculateNextRotationDegBased(float tpf){
if(this.waypoint.size()>0){
Quaternion currentDirection=this.movingNode.getWorldRotation().clone();
Quaternion directionToLook = currentDirection.clone();
Vector3f target=this.waypoint.get(0).clone();
target.subtractLocal(this.movingNode.getWorldTranslation());
directionToLook.lookAt(target, new Vector3f(0,1,0));
float currentDegree=currentDirection.toAngleAxis(new Vector3f(0,1,0))*FastMath.DEG_TO_RAD;

*FastMath.RAD_TO_DEG; float targetDegree=directionToLook.toAngleAxis(new Vector3f(0,1,0))*(-1)

*FastMath.RAD_TO_DEG; float difference=targetDegree-currentDegree; float newAngle=0; if(difference!=0){ if(difference>0){ newAngle=Math.min(difference, this.maxRotationSpeed*tpf); }else{ newAngle=Math.max(difference, this.maxRotationSpeed*tpf); } newAngle+=currentDegree; //newAngle=newAngle%360; //newAngle=newAngle*FastMath.DEG_TO_RAD; float rot=newAngle

System.out.println(currentDegree+" / “+targetDegree+” / "+newAngle+ " / “+rot+” / "+difference);

Quaternion newDirection=Quaternion.ZERO;

newDirection.fromAngleAxis(rot, new Vector3f(0,1,0));

this.nextRotation=newDirection;

}

}

}

[/java]

Sorry, but since I’m the only one answering until now I’ll declare my retreat, I find this rotation stuff too confusing.

@normen : i don’t know who is the writer/maintainer of jm3.math.Quaternion so i am refering to you There might be a bug in the Quaternion.toAngleAxis() function.

From what i understand from the doc, the following two parts should give the same result:

[java]

//first:

float targetDegree=directionToLook.toAngleAxis(new Vector3f(0,1,0))*FastMath.RAD_TO_DEG;

//second:

float targetDegreeHolder[]=directionToLook.toAngles(null);

float targetDegree=targetDegreeHolder[1]*FastMath.RAD_TO_DEG;

[/java]

However, the toAngleAxis call returns the absolute value, of the angle, while the second one works fine.

Basically:

[java]

first.targetDegree==Math.abs(second.targedDegree) && first.targetDegree!=second.targedDegree

[/java]

can be true or false. Depending if the second.targedDegree is < 180° or not.

Could also be a understanding problem on my side, but i wanted to report so one of the geniuses here can check on that.

Anyway, the second method works for me and solved all the issues i had with this damn rotation

Euler angles are hard to understand but I can assure you that the much used toAngleAxis method definitely has no issues after over 8 years of usage.

When you perform some random euler angles and turn them into a quaternion, you get a *UNIQUE* point on a 4 dimensional sphere. There are an infinite number of euler angle combinations that could give you that unique point on the 4 dimension sphere… but it’s still one minimal rotation from some base rotation… but the random twists and turns you took to get there are no longer available.

In other words:

euler set X → quaternion Q

Does not imply that:

quaternion Q → euler set X