[SOLVED] (yet another) Camera Question

Hi all,



I have some trouble wrapping my head around the CameraNode…



Basically I have a vehicle that’s driving along a pre-determine track. I want to create a chase camera that follows the vehicle, but looks at the next point on the track (the vehicle should remain in the bottom half of the screen). I looked into the ChaseCamera class, but the problem is that the camera always moves to the side as the vehicle turns corners (the camera changes from a view that’s behind the vehicle to one that’s looking at the side of the vehicle).



My camera:



_chaseCam = new CameraNode(“Chase Camera”, _app.getCamera());

_chaseCam.setControlDir(ControlDirection.SpatialToCamera);

_vehicle.attachChild(_chaseCam);



This works on its own, however as the vehicle turns corners, the camera keeps looking straight ahead / into the wrong direction (it doesn’t look at the vehicle). Therefore

I tried the following:


  1. Created a getLookAtLocation() method for my vehicle which returns the next point on the track. That way, as the vehicle moves, I call:

    _chaseCam.lookAt(_vehicle.getLookAtLocation().clone(), Vector3f.UNIT_Y); where getLookAtLocation() is the world coordinate of the next point on the track.


  2. Alternative to _chaseCam.lookAt() I updated the camera’s local translation everytime that the vehicle moved:



    Vector3f store = new Vector3f();

    _vehicle.worldToLocal(_vehicle.getLookAtLocation(), store);

    _chaseCam.setLocalTranslation(store);


  3. Tutorials and samples at: https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:making_the_camera_follow_a_character



    Thanks.
@javatar said:
This works on its own, however as the vehicle turns corners, the camera keeps looking straight ahead

What does turn exactly? your_vehicle node? or something attached to it?
Because the camNode will only copy the rotation of its parent.

yes, the vehicle node. :slight_smile:

Could you make a test case or post your code?

Hi Nehon,



Here a test case that encapsulates the essence of my logic:

[java]

package test;



import java.util.ArrayList;

import java.util.concurrent.Callable;



import com.jme3.app.SimpleApplication;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.light.DirectionalLight;

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.CameraNode;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.Spatial;

import com.jme3.scene.control.CameraControl.ControlDirection;

import com.jme3.scene.shape.Box;

import com.jme3.util.SkyFactory;

import com.sun.tools.javac.util.List;



public class TestCamera extends SimpleApplication {

private ArrayList<Vector3f> track = new ArrayList();



private Vector3f lightDir = new Vector3f(-4.9236743f, -1.27054665f,

5.896916f);

Node vehicle = new Node("vehicle");

CameraNode chaseCam;



public static void main(String[] args) {

TestCamera app = new TestCamera();

app.start();

}



public void initTrack() {

track.add(new Vector3f(1, -1, 100));

track.add(new Vector3f(1, -1, 200));

track.add(new Vector3f(100, -1, 200));

track.add(new Vector3f(200, -1, 200));

track.add(new Vector3f(300, -1, 200));

track.add(new Vector3f(400, -1, 200));

track.add(new Vector3f(400, -1, 300));

}



@Override

public void simpleInitApp() {

initTrack();

setDisplayFps(false);

setDisplayStatView(false);

Node mainScene = new Node("Main Scene");

rootNode.attachChild(mainScene);

DirectionalLight sun = new DirectionalLight();

sun.setDirection(lightDir);

sun.setColor(ColorRGBA.White.clone().multLocal(1.7f));

mainScene.addLight(sun);

flyCam.setMoveSpeed(50);

cam.setLocation(new Vector3f(-327.21957f, 61.6459f, 126.884346f));

cam.setRotation(new Quaternion(0.052168474f, 0.9443102f, -0.18395276f,

0.2678024f));

cam.setRotation(new Quaternion().fromAngles(new float[] {

FastMath.PI * 0.06f, FastMath.PI * 0.65f, 0 }));

Spatial sky = SkyFactory.createSky(assetManager,

"Scenes/FullskiesSunset0068.dds", false);

sky.setLocalScale(350);

mainScene.attachChild(sky);

cam.setFrustumFar(4000);



Box box1 = new Box(new Vector3f(1, -1, 1), 10, 10, 10);

Geometry blue = new Geometry("Box", box1);

Material mat1 = new Material(assetManager,

"Common/MatDefs/Misc/Unshaded.j3md");

mat1.setColor("Color", ColorRGBA.Blue);

blue.setMaterial(mat1);

rootNode.attachChild(vehicle); // put this node in the scene

vehicle.attachChild(blue);



chaseCam = new CameraNode("Chase Camera", getCamera());

chaseCam.setControlDir(ControlDirection.SpatialToCamera);

vehicle.attachChild(chaseCam);

// Vector3f vTranslation = _vehicle.getLocalTranslation();

Vector3f localTrans = new Vector3f(0, 25, -55);



chaseCam.setLocalTranslation(localTrans);



inputManager.addListener(new ActionListener() {



public void onAction(String name, boolean isPressed, float tpf) {

if (isPressed) {

if (name.equals("cam")) {

chaseCam.removeFromParent();

}



}

}

}, "cam");

inputManager.addMapping("cam", new KeyTrigger(keyInput.KEY_1));

createWaypoints();

startEngine();

}



public void createWaypoints() {

Node wp = new Node("waypoints");



for (Vector3f waypoint : track) {

Box box1 = new Box(waypoint, 5, 5, 5);

Geometry red = new Geometry("Box2", box1);

Material mat1 = new Material(assetManager,

"Common/MatDefs/Misc/Unshaded.j3md");

mat1.setColor("Color", ColorRGBA.Red);

red.setMaterial(mat1);

wp.attachChild(red);

}



rootNode.attachChild(wp);

}



int i = 0;



public void startEngine() {

Thread t = new Thread(new Runnable() {



@Override

public void run() {

try {

Thread.sleep(2000);

while (true) {

if (i >= track.size()) {

break;

}

enqueue(new Callable<Object>() {

public Object call() throws Exception {

vehicle.setLocalTranslation(track.get(i));

i++;

return null;

}

});



Thread.sleep(3000);

}

} catch (Exception e) {

e.printStackTrace();

}



}

});

t.start();



}



@Override

public void simpleUpdate(float tpf) {

super.simpleUpdate(tpf);

if (i + 1 < track.size()) {

chaseCam.lookAt(track.get(i + 1), Vector3f.UNIT_Y);

}

}

}

[/java]

ok, you have to disable the flyCam, it may interfere with the other control

Thanks - however that makes no difference (the problem remains. I had disabled the flycam in the original code. flyCam.setEnabled(false)).

Hey man, I’m worried the camera is used on a node that’s not being rotated. Probably the one that this is attached to:

[java]

getChild(“U48”).lookAt(lookAtLocation, Vector3f.UNIT_Y);

[/java]

ref: https://bitbucket.org/ianmayo/geomonkeyengine/src/260f4802c949/GME_NB_Eclipse/src/org/geojme/generators/vehicles/U48.java

EDIT: Sent you an e-mail since this is quite gme specific.

2 Likes

Hey, cheers - I’ll apply that patch. It does answer one question that had boggled my mind, but unfortunately the test case produces the same output than GME, so the problem must lie with my logic too… :frowning:

Ah, who am I kidding - it works. Makeshift, see email - thank you!



To those encountering similar problems in the future: watch your camera rotation. As makeshift noted, our camera was attached to a node that wasn’t being rotated.