Has anyone completed or started on a ThirdPersonHandler (or ThirdPersonCamera) for jme3? It would be much like the ThirdPersonHandler in jme2.
There were already people working on this, but I think no one has ever finished it.
I thought tim8dev and nehon were working on it, but I'm not sure. Maybe Norman knows who were it.
yeah i was working on it and i've been sidetracked whith shadows and AO…
anyway i've got a working but drafty chaseCam, if you want to use it :
import com.jme3.input.InputManager;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
public class ChaseCam{
private Spatial target = null;
private float distance = 20;
private float minHeight = 0.05f;
private float maxHeight = FastMath.PI / 2;
private float minDistance = 1.0f;
private float maxDistance = 40.0f;
private float zoomSpeed = 2.0f;
private float rotationSpeed = 1.0f;
/**
* the camera.
*/
private Camera cam = null;
private InputManager inputManager;
private Vector3f initialUpVec;
private boolean canRotate;
private float rotation = FastMath.PI / 2;
private float vRotation = 0;
public ChaseCam(Camera cam, final Spatial target) {
this.target = target;
initialUpVec = cam.getUp().clone();
this.cam = cam;
updateCamera();
}
public void registerWithInput(InputManager inputManager) {
String[] ActionInputs = {"toggleRotate"};
String[] AnalogInputs = {"Down", "Up", "mouseLeft", "mouseRight", "ZoomIn", "ZoomOut"};
this.inputManager = inputManager;
inputManager.addMapping("Down", new MouseAxisTrigger(1, true));
inputManager.addMapping("Up", new MouseAxisTrigger(1, false));
inputManager.addMapping("ZoomIn", new MouseAxisTrigger(2, true));
inputManager.addMapping("ZoomOut", new MouseAxisTrigger(2, false));
inputManager.addMapping("mouseLeft", new MouseAxisTrigger(0, true));
inputManager.addMapping("mouseRight", new MouseAxisTrigger(0, false));
inputManager.addMapping("toggleRotate", new MouseButtonTrigger(0));
inputManager.addMapping("toggleRotate", new MouseButtonTrigger(1));
ActionListener al = new ActionListener() {
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals("toggleRotate")) {
if (keyPressed) {
canRotate = true;
} else {
canRotate = false;
}
}
}
};
AnalogListener anl = new AnalogListener() {
public void onAnalog(String name, float value, float tpf) {
if (name.equals("mouseLeft")) {
rotateCamera(-value);
} else if (name.equals("mouseRight")) {
rotateCamera(value);
} else if (name.equals("Up")) {
vRotateCamera(value);
} else if (name.equals("Down")) {
vRotateCamera(-value);
} else if (name.equals("ZoomIn")) {
zoomCamera(value);
} else if (name.equals("ZoomOut")) {
zoomCamera(-value);
}
}
};
inputManager.addListener(al, ActionInputs);
inputManager.addListener(anl, AnalogInputs);
}
private void rotateCamera(float value) {
if (!canRotate) {
return;
}
rotation += value * rotationSpeed;
updateCamera();
}
private void zoomCamera(float value) {
((Node)target).getChild(0).setCullHint(Spatial.CullHint.Dynamic);
((Node)target).getChild(0).setShadowMode(ShadowMode.CastAndRecieve);
distance += value * zoomSpeed;
if (distance > maxDistance) {
distance = maxDistance;
}
if (distance < minDistance) {
distance = minDistance;
}
if ((vRotation < minHeight) && (distance > (minDistance + 1.0f))) {
vRotation = minHeight;
}
if (distance <= (minDistance + 1.0f)) {
((Node)target).getChild(0).setCullHint(Spatial.CullHint.Always);
((Node)target).getChild(0).setShadowMode(ShadowMode.Cast);
}
updateCamera();
}
private void vRotateCamera(float value) {
if (!canRotate) {
return;
}
vRotation += value * rotationSpeed;
if (vRotation > maxHeight) {
vRotation = maxHeight;
}
if ((vRotation < minHeight) && (distance > (minDistance + 1.0f))) {
vRotation = minHeight;
}
updateCamera();
}
public void updateCamera() {
float hDistance = distance * FastMath.sin((FastMath.PI / 2) - vRotation);
Vector3f pos = new Vector3f(hDistance * FastMath.cos(rotation), distance * FastMath.sin(vRotation), hDistance * FastMath.sin(rotation));
pos = pos.add(target.getLocalTranslation());
cam.setLocation(pos);
cam.lookAt(target.getLocalTranslation(), initialUpVec);
}
public void onPreUpdate(float tpf) {
canRotate = false;
}
public void onPostUpdate(float tpf) {
inputManager.setCursorVisible(!canRotate);
}
public float getMaxDistance() {
return maxDistance;
}
public void setMaxDistance(float maxDistance) {
this.maxDistance = maxDistance;
}
public float getMinDistance() {
return minDistance;
}
public void setMinDistance(float minDistance) {
this.minDistance = minDistance;
}
}
be aware that it's assuming that the target is a node and that the first child is the model you want to chase.
to initialize it you have to do that
flyCam.setEnabled(false);
ChaseCam chaser = new ChaseCam(viewPort.getCamera(),myTargetNode);
chaser.registerWithInput(inputManager);
and in the simpleUpdate method of your app (you'll need to override it)
@Override
public void simpleUpdate(float tpf) {
chaser.updateCamera();
}
That's not great to have to call for an update but that is the simpliest way i found to do it...
The cam works like this :
- you can zoom in and out with the mouse wheel (default max zoomout distance is 40 but you can change it with setMaxDistance method)
- "grab" with left/right click moves the camera around the target
- if you completely zoom into the target, it culls it, set it to not receive shadow, and act like a first person camera
that's it... hope that helps
Nice, thanks Nehon. I'll take a look at this and see how well it works for me.