Hi,
I was unhappy about the translation which changed with the rotation, which is logically right, but feels wrong.
So I tested a bit to find a solution. Additionally it felt wrong, that I moved the camera, although I didn’t want it.
So here’s the “new” turntable, that works like motion in blender.
Of cause, situation can be “saved” and restored (programmatically)
Well only key support, no mouse.
Anyway - if someone is interested:
package de.schwarzrot.jme3;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
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.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.debug.Arrow;
import com.jme3.scene.debug.WireBox;
import com.jme3.scene.shape.Cylinder;
import com.jme3.scene.shape.Quad;
import com.jme3.system.AppSettings;
import com.jme3.util.JmeFormatter;
/**
* Test class to show the motion separation for a turntable, that can nick
* toward the user and where the arrow keys always work in native direction of
* the user.
* .
* Control of the sample works with cursor keys like this:
* - cursor left/right: move the table left or right
* - cursor up/down: moves the table towards the user or from the user away
* - cursor left/right while holding shift: rotates the table clockwise or
* counterclockwise
* - cursor up/down while holding shift: rotates the table toward the user or
* from the user away
* + key from numpad zooms in
* - key from numpad zooms out
* - press T key: tell the values of the current scene through the logger
* - press R key: restore the saved scene (have to code the values)
*
* @author Django Reinhard
*/
public class TestTurnTable extends SimpleApplication implements AnalogListener, ActionListener {
/**
* constructor that takes dimension of workarea. Dimension is taken from
* world of cnc-machines, which means, that X-Axis is from left to right,
* Y-Axis is from Operator to Monitor and Z-Axis is from Top to Bottom. That
* does not match JME3 axis and axis-direction, so we have to do a mapping
*
* @param limits
* dimension of workarea. Array of floats with the sequence: xMin,
* xMax, yMin, yMax, zMin, zMax - but axis and axis
* direction/limits are taken in the sense of cnc-machines, not in
* jme-notion. That means we have to map axis and change direction.
*/
public TestTurnTable(float[] limits) {
size = new Vector3f(limits[1] - limits[0], limits[5] - limits[4], limits[3] - limits[2]);
baseLoc = new Vector3f();
rotation = new Quaternion();
nick = new Quaternion();
l.log(Level.INFO, "workarea size is: " + size);
// here's the right place to change AppSettings the way we want it to be
AppSettings settings = new AppSettings(true);
settings.setAudioRenderer(null);
settings.setWidth(1000);
settings.setWidth(1000);
setSettings(settings);
setPauseOnLostFocus(false);
}
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (Restore.equals(name)) {
restoreScene();
} else if (TellPos.equals(name)) {
tellScene();
} else if (RotateTrigger.equals(name)) {
rotate = isPressed;
} else if (MoveModelTrigger.equals(name)) {
moveModel = isPressed;
}
}
@Override
public void onAnalog(String name, float value, float tpf) {
if (ZoomIN.equals(name)) {
zoomFactor -= 0.5f * zoomFactor * tpf;
resizeView();
} else if (ZoomOUT.equals(name)) {
zoomFactor += 0.5f * zoomFactor * tpf;
resizeView();
}
if (moveModel) {
// model will only move up and down from workplace
// This motion is only to compensate different workpiece height
if (KeyUp.equals(name)) {
modelOffset += 0.6 * zoomFactor * tpf;
moveModel();
} else if (KeyDown.equals(name)) {
modelOffset -= 0.6 * zoomFactor * tpf;
moveModel();
}
} else if (rotate) {
// standard rotation is rotating the workplace around the Y-Axis
if (KeyLeft.equals(name)) {
rotation.fromAngleAxis(-0.001f * zoomFactor * tpf, Vector3f.UNIT_Y);
rotateDesk();
} else if (KeyRight.equals(name)) {
rotation.fromAngleAxis(0.001f * zoomFactor * tpf, Vector3f.UNIT_Y);
rotateDesk();
} else if (KeyUp.equals(name)) {
nick.fromAngleAxis(-0.001f * zoomFactor * tpf, Vector3f.UNIT_X);
nickDesk();
} else if (KeyDown.equals(name)) {
nick.fromAngleAxis(0.001f * zoomFactor * tpf, Vector3f.UNIT_X);
nickDesk();
}
} else {
if (KeyLeft.equals(name)) {
baseLoc.x -= 0.8f * zoomFactor * tpf;
shiftBase();
} else if (KeyRight.equals(name)) {
baseLoc.x += 0.8f * zoomFactor * tpf;
shiftBase();
} else if (KeyUp.equals(name)) {
baseLoc.z -= 0.8f * zoomFactor * tpf;
shiftBase();
} else if (KeyDown.equals(name)) {
baseLoc.z += 0.8f * zoomFactor * tpf;
shiftBase();
}
}
}
@Override
public void simpleInitApp() {
flyCam.setEnabled(false);
cam.setParallelProjection(true);
Vector3f camLoc = new Vector3f(0, 2f * size.y, size.z);
l.log(Level.INFO, "camera location: " + camLoc);
cam.setLocation(camLoc);
cam.lookAt(camLoc.negate(), camDir);
l.log(Level.INFO, "camera direction: " + cam.getDirection());
createDesk();
createTool();
registerInputs();
resizeView();
}
protected void createDesk() {
shiftBase = new Node("ShiftBase");
nickBase = new Node("NickBase");
turnTable = new Node("TurnTable");
modelBase = new Node("ModelBase");
rootNode.attachChild(shiftBase);
shiftBase.attachChild(nickBase);
nickBase.attachChild(turnTable);
turnTable.attachChild(modelBase);
// the working area
Geometry box = new Geometry("Box", new WireBox(size.x, size.y, size.z));
// its our desktop, so create a nice looking surface
Geometry ground = new Geometry("Ground", new Quad(size.x * 2, size.z * 2));
Material m = new Material(assetManager, MATUnshaded);
Quaternion initialRotation = new Quaternion();
m.getAdditionalRenderState().setWireframe(true);
m.setColor(MATColor, ColorRGBA.Green);
box.setMaterial(m);
box.setLocalTranslation(0, size.y, 0);
initialRotation.fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y);
turnTable.rotate(initialRotation);
turnTable.attachChild(box);
m = new Material(assetManager, MATUnshaded);
m.setTexture(MATColorMap, assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
ground.setMaterial(m);
ground.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
ground.setLocalTranslation(-size.x, 0, size.z);
turnTable.attachChild(ground);
createOrigin(modelBase);
}
protected void createOrigin(Node parent) {
// visual use of axis does not follow axis from jme3,
// so create some arrows to show usage and direction of each axis
// follow color scheme of blender (x is red, y is green and z is blue)
Mesh mesh = new Arrow(new Vector3f(100f, 0, 0));
Geometry geo = new Geometry(PrimArrow, mesh);
Material m = new Material(assetManager, MATUnshaded);
m.setColor(MATColor, ColorRGBA.Red);
m.getAdditionalRenderState().setLineWidth(3);
geo.setMaterial(m);
parent.attachChild(geo);
mesh = new Arrow(new Vector3f(0, 0, -100f));
geo = new Geometry(PrimArrow, mesh);
m = new Material(assetManager, MATUnshaded);
m.setColor(MATColor, ColorRGBA.Green);
m.getAdditionalRenderState().setLineWidth(3);
geo.setMaterial(m);
parent.attachChild(geo);
mesh = new Arrow(new Vector3f(0, 100f, 0));
geo = new Geometry(PrimArrow, mesh);
m = new Material(assetManager, MATUnshaded);
m.setColor(MATColor, ColorRGBA.Blue);
m.getAdditionalRenderState().setLineWidth(3);
geo.setMaterial(m);
parent.attachChild(geo);
}
protected void createTool() {
Geometry geo = new Geometry("Tool", new Cylinder(2, 16, 10, 100, true));
Material m = new Material(assetManager, MATShowNormals);
geo.setMaterial(m);
geo.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
geo.setLocalTranslation(0, 100, 0);
modelBase.attachChild(geo);
}
protected void moveModel() {
modelBase.setLocalTranslation(0, modelOffset, 0);
}
protected void nickDesk() {
nickBase.rotate(nick);
}
protected void registerInputs() {
inputManager.addMapping(ZoomIN, new KeyTrigger(KeyInput.KEY_ADD));
inputManager.addMapping(ZoomOUT, new KeyTrigger(KeyInput.KEY_SUBTRACT));
inputManager.addMapping(Restore, new KeyTrigger(KeyInput.KEY_R));
inputManager.addMapping(TellPos, new KeyTrigger(KeyInput.KEY_T));
inputManager.addMapping(KeyLeft, new KeyTrigger(KeyInput.KEY_LEFT));
inputManager.addMapping(KeyRight, new KeyTrigger(KeyInput.KEY_RIGHT));
inputManager.addMapping(KeyUp, new KeyTrigger(KeyInput.KEY_UP));
inputManager.addMapping(KeyDown, new KeyTrigger(KeyInput.KEY_DOWN));
inputManager.addMapping(MoveModelTrigger, new KeyTrigger(KeyInput.KEY_LMENU),
new KeyTrigger(KeyInput.KEY_RMENU));
inputManager.addMapping(RotateTrigger, new KeyTrigger(KeyInput.KEY_LSHIFT),
new KeyTrigger(KeyInput.KEY_RSHIFT));
inputManager.addListener(this, Restore, ZoomIN, ZoomOUT, KeyLeft, KeyRight, KeyUp, KeyDown, KeyForward,
KeyBack, RotateTrigger, MoveModelTrigger, TellPos);
}
protected void resizeView() {
float aspect = (float) cam.getWidth() / (float) cam.getHeight();
// calculate Viewport (use big limits to avoid clipping)
cam.setFrustum(-2000, 6000, -aspect * zoomFactor, aspect * zoomFactor, zoomFactor, -zoomFactor);
}
/*
* put values in, that tellScene() printed
*/
protected void restoreScene() {
// key trigger is too fast, so have to limit usage to one call
if (restored) {
return;
}
restored = true;
// INFORMATION TestJMEMotion 19:48:29 location: (0.0, 0.0, 879.12665)
baseLoc.x = 0;
baseLoc.y = 0;
baseLoc.z = 879.12665f;
shiftBase();
// INFORMATION TestJMEMotion 19:48:29 nick: (-0.0530407, 0.0, 0.0, 0.9985713)
nick = new Quaternion(-0.0530407f, 0, 0, 0.9985713f);
nickDesk();
// INFORMATION TestJMEMotion 19:48:29 rotation: (0.0, 0.38741437, 0.0, -0.92179006)
rotation = new Quaternion(0, 0.38741437f, 0, -0.92179006f);
rotateDesk();
// INFORMATION TestJMEMotion 19:48:29 elevation: 733.17413
modelOffset = 733.17413f;
moveModel();
// INFORMATION TestJMEMotion 19:48:29 zoom: 1343.7764
zoomFactor = 1343f;
resizeView();
}
protected void rotateDesk() {
turnTable.rotate(rotation);
}
protected void shiftBase() {
shiftBase.setLocalTranslation(baseLoc);
}
protected void tellScene() {
l.log(Level.INFO, " location: " + shiftBase.getLocalTranslation());
l.log(Level.INFO, " nick: " + nickBase.getLocalRotation());
l.log(Level.INFO, " rotation: " + turnTable.getLocalRotation());
l.log(Level.INFO, "elevation: " + modelOffset);
l.log(Level.INFO, " zoom: " + zoomFactor);
}
public static void main(String[] args) {
JmeFormatter formatter = new JmeFormatter();
Handler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(formatter);
Logger.getLogger("").removeHandler(Logger.getLogger("").getHandlers()[0]);
Logger.getLogger("").addHandler(consoleHandler);
TestTurnTable app = new TestTurnTable(
// machine limits given from outside
new float[] { 0, 900, 0, 1800, -500, 0 });
app.start();
}
private Vector3f size;
private Node shiftBase;
private Node nickBase;
private Node turnTable;
private Node modelBase;
private Vector3f baseLoc;
private Quaternion rotation;
private Quaternion nick;
private boolean rotate;
private boolean restored;
private boolean moveModel;
private float modelOffset;
private float zoomFactor = 1800f;
private static final Logger l;
private static final String ZoomIN = "zoomIN";
private static final String ZoomOUT = "zoomOUT";
private static final String Restore = "Restore";
private static final String TellPos = "TellPos";
private static final String RotateTrigger = "trigRotation";
private static final String MoveModelTrigger = "trigModelMove";
private static final String KeyLeft = "kLeft";
private static final String KeyRight = "kRight";
private static final String KeyUp = "kUp";
private static final String KeyDown = "kDown";
private static final String KeyForward = "kForward";
private static final String KeyBack = "kBack";
private static final String MATUnshaded = "Common/MatDefs/Misc/Unshaded.j3md";
private static final String MATShowNormals = "Common/MatDefs/Misc/ShowNormals.j3md";
private static final String MATColor = "Color";
private static final String MATColorMap = "ColorMap";
private static final String PrimArrow = "Arrow";
private static final Vector3f camDir = new Vector3f(0, 1, 0);
static {
l = Logger.getLogger("Test");
}
}
I would like to know, how can I limit the nick rotation?