I have a problem with nodes while rotation of model in some cases. When I was rotating a model, I can saw that the side nodes was incorrect rotating in some cases.
Code for test this cases:
import com.jme3.app.SimpleApplication;
import com.jme3.asset.AssetManager;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
import java.util.concurrent.ThreadLocalRandom;
import static java.lang.Math.abs;
public class TestRotation extends SimpleApplication {
private final Quaternion startRotation;
private final Quaternion endRotation;
private final Quaternion resultRotation;
private final Quaternion bufferRotation;
private final Quaternion bodyEndRotation;
private final Quaternion bodyStartRotation;
private final Quaternion bodyResultRotation;
private final Quaternion bodyBufferRotation;
private final Vector3f prevBodyPosition;
private final Quaternion prevCameraRotation;
private Node body;
private int frame;
private float cameraDone;
private float cameraStep;
private float bodyDone;
private float bodyStep;
private long lastCamUpdate;
private long lastBodyUpdate;
private long lastBodyRotate;
public TestRotation() {
this.startRotation = new Quaternion();
this.endRotation = new Quaternion();
this.resultRotation = new Quaternion();
this.bufferRotation = new Quaternion();
this.prevBodyPosition = new Vector3f();
this.prevCameraRotation = new Quaternion();
this.bodyBufferRotation = new Quaternion();
this.bodyEndRotation = new Quaternion();
this.bodyStartRotation = new Quaternion();
this.bodyResultRotation = new Quaternion();
}
public static void main(String[] args) {
final AppSettings settings = new AppSettings(true);
settings.setResolution(1024, 768);
TestRotation app = new TestRotation();
app.setSettings(settings);
app.setShowSettings(false);
app.start();
}
@Override
public void simpleInitApp() {
viewPort.setBackgroundColor(ColorRGBA.Gray);
cam.setFrustumPerspective(55F, (float) cam.getWidth() / cam.getHeight(), 1f, 1000F);
final Geometry geometry1 = new Geometry("Box", new Box(1, 1, 1));
geometry1.setMaterial(createMaterial(assetManager));
geometry1.setLocalScale(1, 0.2F, 3.8F);
final Geometry geometry2 = new Geometry("Box", new Box(1, 1, 1));
geometry2.setMaterial(createMaterial(assetManager));
geometry2.setLocalScale(3.8F, 0.2F, 1F);
geometry2.setLocalTranslation(new Vector3f(0F, 0.43930158F, -2.800108F));
final Geometry geometry3 = new Geometry("Box", new Box(1, 1, 1));
geometry3.setMaterial(createMaterial(assetManager));
geometry3.setLocalScale(0.6F, 0.6F, 0.6F);
final Geometry geometry4 = new Geometry("Box", new Box(1, 1, 1));
geometry4.setMaterial(createMaterial(assetManager));
geometry4.setLocalScale(0.6F, 0.6F, 0.6F);
final Node node2 = new Node();
node2.attachChild(geometry3);
node2.setLocalTranslation(new Vector3f(4.421318F, -0.35402215F, -3.193687F));
final Node node3 = new Node();
node3.attachChild(geometry4);
node3.setLocalTranslation(new Vector3f(-4.421318F, -0.35402215F, -3.193687F));
body = new Node();
body.attachChild(geometry1);
body.attachChild(geometry2);
body.attachChild(node2);
body.attachChild(node3);
rootNode.attachChild(body);
final DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1, 0, 1).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);
getFlyByCamera().setDragToRotate(true);
this.bodyDone = 2;
}
private Material createMaterial(final AssetManager assetManager) {
return new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
}
@Override
public void simpleUpdate(final float tpf) {
frame++;
if(bodyDone >= 1F && System.currentTimeMillis() - lastBodyRotate > 1000) {
final ThreadLocalRandom random = ThreadLocalRandom.current();
final Quaternion target = new Quaternion();
target.set(random.nextFloat(), random.nextFloat(), random.nextFloat(), random.nextFloat());
target.normalizeLocal();
rotateBodyTo(target);
lastBodyRotate = System.currentTimeMillis();
}
updateBodyRotation();
if(System.currentTimeMillis() - lastCamUpdate > 15) {
updateCamRotation();
lastCamUpdate = System.currentTimeMillis();
}
updateCamPosition();
}
public void rotateBodyTo(final Quaternion target) {
final Quaternion start = body.getLocalRotation();
final float dot = Math.abs(start.dot(target));
if (dot >= 1F) {
this.bodyEndRotation.set(target);
this.bodyDone = 1F;
return;
}
final float radians = (float) Math.acos(dot);
final float speed = 0.5F;
final float count = radians / speed;
final float step = 1 / count;
this.bodyStartRotation.set(start);
this.bodyEndRotation.set(target);
this.lastBodyUpdate = System.currentTimeMillis();
this.bodyStep = step;
this.bodyDone = 0F;
}
public void updateBodyRotation() {
float done = this.bodyDone;
try {
if (done >= 1F) return;
final long diff = System.currentTimeMillis() - lastBodyUpdate;
if (diff < 1) return;
done += this.bodyStep * diff / 1000F;
Quaternion result = bodyResultRotation;
if (done >= 1F) {
result = bodyEndRotation;
}
if (result != bodyEndRotation) {
bodyBufferRotation.set(bodyEndRotation);
result = bodyResultRotation.slerp(bodyStartRotation, bodyBufferRotation, done);
}
body.setLocalRotation(result);
} finally {
lastBodyUpdate = System.currentTimeMillis();
this.bodyDone = done;
}
}
private void updateCamRotation() {
final Camera camera = getCamera();
final Quaternion bodyRotation = body.getLocalRotation();
final Quaternion cameraRotation = camera.getRotation();
if (!isNeedRotation(bodyRotation, cameraRotation)) return;
if (!endRotation.equals(bodyRotation)) {
startRotation.set(cameraRotation);
endRotation.set(bodyRotation);
final float dot = abs(cameraRotation.dot(bodyRotation));
final float step = (float) Math.pow(1F / (1 * dot), 10);
this.cameraStep = step;
this.cameraDone = 0;
}
float done = this.cameraDone + 0.03F;
try {
if (done >= 1D) {
camera.setRotation(endRotation);
endRotation.set(0, 0, 0, 1);
done = 0;
return;
}
bufferRotation.set(endRotation);
resultRotation.slerp(startRotation, bufferRotation, done);
camera.setRotation(resultRotation);
if (bodyRotation.equals(resultRotation)) {
camera.setRotation(bodyRotation);
endRotation.set(0, 0, 0, 1);
done = 0;
}
} finally {
this.cameraDone = done;
}
}
private void updateCamPosition() {
final Camera camera = getCamera();
final Vector3f currentBodyPosition = body.getLocalTranslation();
final Quaternion currentCamRotation = camera.getRotation();
if (frame > 2 && prevBodyPosition.equals(currentBodyPosition) && currentCamRotation.equals(prevCameraRotation)) {
return;
}
try {
float offset = 15;
Vector3f pos = camera.getDirection();
pos.multLocal(-offset);
pos.addLocal(currentBodyPosition);
final float firstX = pos.getX();
final float firstY = pos.getY();
final float firstZ = pos.getZ();
pos = camera.getUp(pos);
pos.multLocal(3);
pos.addLocal(firstX, firstY, firstZ);
camera.setLocation(pos);
} finally {
prevBodyPosition.set(currentBodyPosition);
prevCameraRotation.set(currentCamRotation);
}
}
protected boolean isNeedRotation(final Quaternion shipRotation, final Quaternion cameraRotation) {
return !shipRotation.equals(cameraRotation);
}
}