How to rotate a spatial to the direction of the camera?

Hi, i have a problem:



I need to rotate a gun model to the to the direction where my chasecamera is looking. With chasecamera I can get the direction by cam.getDirection() and when i shoot bullets in that direction, the bullets go in that direction (HelloIntersection).



But I need to make the gun model to be rotated to that direction also. Spatials or nodes dont have methods like, setDirection.



I have tried variations of



gun.setLocalRotation(new Matrix3f(0,0,cam.getDirection().x,0,0,cam.getDirection().y,0,0,cam.getDirection().z));



in the update method.



Since what i understand gun.getLocalRotation().getRotationColumn(2) should give the direction, but it still doesent work properly.



The chasecamera is from FlagrushTutorial and its currently chasing the guns origin. I actually need some different kind of camera also since its not comfortable to aim with this chasecamera.



I hope that somebody can help me:)

The simplest would be to attach the camera (with a cameranode) and the spatial you want to rotate with the camera, to the same node.



directionNode (which gets rotated)
    |
    |--- Camera
    |
    |--- Weapon
 

Tx, for the reply.



With this approach, do I have to apply the rotation like this:



directionNode.setLocalRotation(new Matrix3f(0,0,cam.getDirection().x,0,0,cam.getDirection().y,0,0,cam.getDirection().z));



And when creating a new chaseCamera chaser = new ChaseCamera(cam, directionNode, props); should the target node be directoinNode, around which origin the camera will be rotating, since i cant use directionNode.attachChild(chaser); since ChaseCamera extends InputHandler.



Well I tried it the way I described above, didnt work properly.



The rotation was right only along directionNode parents (playerNode) x axis. Around other axes it was wrong.



I quess the issue could be that cam.getDirection() give the world direction and in update mehtod i set



player.directionNode.setLocalRotation(new Matrix3f(0,0,cam.getDirection().x,0,0,cam.getDirection().y,0,0,cam.getDirection().z));



Which is localrotation not world rotation.



I have no idea how to make it work.

Ah i missed the part with the chasecamera.



I would suggest creating your own ChaseCamera which does exactly what you want.

Below is a small TestCase with a simple ChaseCam i made for another project.



The keys Home/ End PgUp/PgDn and MouseWheel move the ChaseCam relative to the attached object (in your case the weapon). Thats useful mostly for debug reasons.



In the Testcase, the Array (weapon) is moved with a NodeHandler, the Camera follows this Weapon.

If you want to move the Camera more smoothly, try to use the slerp() (instead camNode.getLocalRotation().set(q)) which is commented out currently.



package roatation;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.NodeHandler;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.scene.CameraNode;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Arrow;
import com.jme.scene.shape.Box;

public class TestChaseCam extends SimpleGame {

   private ChaseCam chaser;
   private NodeHandler nodeHandler;
   private Node playerNode;

   @Override
   protected void simpleInitGame() {
      // destroy SimpleGames Inputhandling
      input = new InputHandler();
      
      Arrow weapon = new Arrow("weapon", 2, 0.1f);
      weapon.getLocalRotation().fromAngleAxis(FastMath.DEG_TO_RAD*90, Vector3f.UNIT_X);
      weapon.setModelBound(new BoundingBox());
      weapon.updateModelBound();
      
      playerNode = new Node("playerNode");
      playerNode.attachChild(weapon);
      rootNode.attachChild(playerNode);
      
      // The ChaseCamera attaches the camera to a CameraNode and updates the location of the cameraNode
      // The location of the camera itself is updated by the CameraNode which is attached to the rootNode
      chaser = new ChaseCam(playerNode, cam, 0.5f, 0.7f, -1.5f);
      rootNode.attachChild(chaser.getCamNode());
      
      nodeHandler = new NodeHandler(playerNode, 15, 0.5f);
      
      Node scene = new Node("scene");
      for (int i = 0; i < 15; i++) {
         for (int j = 0; j < 15; j++) {
            for (int k = 0; k < 15; k++) {
               Box c = new Box("", new Vector3f(), 0.05f, 0.05f, 0.05f);
               c.setModelBound(new BoundingBox());
               c.updateModelBound();
               c.setLocalTranslation(i*5, j*5, k*5);
               scene.attachChild(c);
            }
         }
      }
      scene.setLocalTranslation(-11, -10, 0);
      scene.lock();
      rootNode.attachChild(scene);
   }
   
   @Override
   protected void simpleUpdate() {
      
      nodeHandler.update(tpf);
      chaser.update(tpf);
   }
   
   public static void main(String[] args) {
      TestChaseCam game = new TestChaseCam();
      game.setConfigShowMode(ConfigShowMode.AlwaysShow);
      game.start();
   }
}


class ChaseCam extends InputHandler {
    /**
     * the target.
     */
    private Spatial target = null;

    /**
     * distance in the X-Axis to the target.
     */
    private float xDistance = 0;
   
    /**
     * distance in the Y-Axis to the target.
     */
    private float yDistance = 0;

    /**
     * distance in the Z-Axis to the target.
     */
    private float zDistance = 0;

    /**
     * Zoom factor.
     */
    private float zoomFactor = 0.03f;

    /**
     * last mousewheel position.
     */
    private float lastPos = 0;

    /**
     * the camera.
     */
    private CameraNode camNode = null;

    /**
     * slerp factor.
     */
    private float slerpFactor = 10f;
   
    /**
     * Attaches the ChaseCam with an Y- and Z-Axis offset to the target.
     * @param target target to follow
     * @param y distance to the target on the Y-Axis
     * @param z distance to the target on the Z-Axis
     */
    public ChaseCam(final Spatial target, Camera cam, final float x, final float y, final float z) {
       this.xDistance = x;
        this.yDistance = y;
        this.zDistance = z;
        this.target = target;

        camNode = new CameraNode("chaseCamNode", cam);

        InputAction keyZoom = new InputAction() {
            public void performAction(final InputActionEvent evt) {
                if (evt.getTriggerName().equals("cam up") ) {
                        yDistance += zoomFactor;
                        return;
                }
                if (evt.getTriggerName().equals("cam down") ) {
                        yDistance -= zoomFactor;
                        return;
                }
               
                if (evt.getTriggerDevice().equals("mouse")) {
                    zDistance += (zoomFactor * (lastPos < evt
                            .getTriggerPosition() ? -1 : 1));
                    lastPos = evt.getTriggerPosition();
                } else if (evt.getTriggerName().equals("zoom in")) {
                    zDistance += zoomFactor;
                } else {
                    zDistance -= zoomFactor;
                }
            }
        };

        addAction(keyZoom, InputHandler.DEVICE_MOUSE, InputHandler.BUTTON_NONE,
                2, false);
        addAction(keyZoom, "zoom in", KeyInput.KEY_PGUP, true);
        addAction(keyZoom, "zoom out", KeyInput.KEY_PGDN, true);
       
        addAction(keyZoom, "cam up", KeyInput.KEY_HOME, true);
        addAction(keyZoom, "cam down", KeyInput.KEY_END, true);
    }

    /**
     * Update location and rotation of the ChaseCam to stay behind the target.
     * @param time time since last frame.
     */
    public final void update(final float time) {
        super.update(time);
        target.updateWorldVectors();
        Vector3f targetVec = target.localToWorld(new Vector3f(xDistance, yDistance,
                zDistance), null);
        camNode.getLocalTranslation().set(targetVec);
//        camNode.getLocalRotation().slerp(target.getLocalRotation(), slerpFactor*time);
        camNode.getLocalRotation().set(target.getLocalRotation());
    }

    /**
     * returns the camera node.
     * @return reference to camera node.
     */
    public final CameraNode getCamNode() {
        return camNode;
    }
}

Thank you very much for this great example, this is excactly what I need in this phase. Tomorrow I'll  work it into my game. I dont really need slerp, more difficult to aim with it.



One more question, if I have a third person game and I want to make aiming like it is in GTA3+ for example. Then how to point gun exactly to the point of target? Is it accomplished by picking?



Ok, I sort of worked it into my game, but somewhy I cant move the camera up and down with mouse, something is blocking it, I dont know what. Camera tries to move up and down when i move mouse up and down but it wont and the picture just twitches a little. I think I have to check the update method for some constraints. Other movements work.



I just replaced the flagrush lesson9's chasecamera with your camera.

One more question, if I have a third person game and I want to make aiming like it is in GTA3+ for example. Then how to point gun exactly to the point of target? Is it accomplished by picking?


I think in GTA you fire pretty much into the direction you character is facing currently.
Core-Dump said:

One more question, if I have a third person game and I want to make aiming like it is in GTA3+ for example. Then how to point gun exactly to the point of target? Is it accomplished by picking?


I think in GTA you fire pretty much into the direction you character is facing currently.



If I recall in GTA3 they did have an aiming reticle that you could move around that the player and his guns followed. Ignoring any player animations, it would be very similar to the example core dump - but in the third person where the gun and the entire player rotates as you aim.

Fire a ray from the camera through the reticle to find the closest point of intersection in the scene. Then use the gun rotations' lookAt() method to point to that location. No?



It will take a little math to get a direction vector from the intersection location, but that should be trivial.

Tx for the replies, but since I'm new to JME and (3D) game programming…


  1. What is reticle?


  2. What is ray and how to cast it to get the intersection point? Any jmetest you can point me to?



    Yes the math should be trivial once i know the intersection point :slight_smile:
Henri said:

1. What is reticle?

Nothing to do with jME or 3D, just think of it as another name for aiming crosshairs.

Henri said:

2. What is ray and how to cast it to get the intersection point? Any jmetest you can point me to?

Look at MousePick.java for an example of using a ray shot from the camera and finding the intersection.

If you are not familiar with Rays, then read up.