Panning with the mouse

I appologize if this is a newbie question, but I am trying to find where in xith3d I enable the ability to use the mouse to pan around the origin or another specified point? This seems like it would be a fairly standard feature, but I haven’t been able to find it anywhere.

Thanks,
RDL

Do you mean mouselook?

However I do my mouselook like this:

I add a mouselistener and this code:


public void mouseMoved(MouseEvent mouseEvent) {
    if (ignoreNextMouseMove) {
        ignoreNextMouseMove = false;
    } else {
        mousePosition = mouseEvent.getPoint();
    }
}


Then, before I do renderOnce() I run this code:


public void processMouse() {
    if (mousePosition != null && !ignoreNextMouseMove) {
        ignoreNextMouseMove = true;
        robot.mouseMove(screenMiddleX, screenMiddleY);
        int xDifference = screenMiddleX - mousePosition.x;
        int yDifference = screenMiddleY - mousePosition.y;
        float xRot = yDifference / 1000.0f;
        float yRot = xDifference / 1000.0f;
        Transform3D xTransform3D = new Transform3D();
        xTransform3D.rotX(xRot);
        Transform3D yTransform3D = new Transform3D();
        yTransform3D.rotY(yRot);
        yTransform3D.mul(xTransform3D);
        transform3D.mul(yTransform3D);
    }
}


transform3D is the view transform.
robot is a java.awt.Robot which moves the mouse to a specified point.

I hope this helps :slight_smile:

Don,

Thanks for the code – this seems to be doing some of the job but it has some weird behaviour – maybe I haven’t implemented this correctly – my work has mostly been scientific and I haven’t had much experience using AWT so I am probably doing something wrong.

Running this I get some mouselook behaviour but it is limited to the transform group rotating around my camera view in a clockwhise manner, moving down, around behind the camera, and then showing up again at the top of the screen. I am unable to pan, or zoom around the root transform group. Is this what the behaviour is suppose to be like?

I had kind of assumed that moving the mouse would have moved various properties of the camera view .

I set the middleScreenX and middleScreenY to 320x240 as I am using a 640x480 screen. I am assuming this is correct. As well, other than initializingthe robot, is there anything else I need to do to it?

Finally, I was playing around with the various case statements: do I need more than just the following code in the mouse portion of the event listener?



case MouseEvent.MOUSE_DRAGGED :                  mouseMoved(e);
break;


Thanks
Rob Leclerc

P.S. I just came accros the MouseInteraction tutorial:
http://xith.org/tutes/GettingStarted/html/interaction.html

This was basically what I have in mind, but do you know if this is moving the object or is this moving the camera view while the objects position is maintained?

Thanks,
RDL

Hi again!

Yep, this is moving the camera view.

You initializes the Robot like this:


try {
    robot = new Robot();
} catch (Exception e) {
    e.printStackTrace();
}


And when you processes the mouse, you must do this with the Robot:


robot.mouseMove(screenMiddleX, screenMiddleY);


This will make the rotation unlimited.

Then edit this:


case MouseEvent.MOUSE_DRAGGED:
mouseMoved(e); 
break;

To this:


case MouseEvent.MOUSE_MOVED:
case MouseEvent.MOUSE_DRAGGED:
mouseMoved(e);
break;

Hope this helps :slight_smile:

Don,

Thanks, I will take a closer look at the robot tomorrow. I have been spending time with the MouseInteraction.java, which moves the cube. This bascally does exactly what I want from a camera perspective but it moves the location of the box and not the location of the camera, which is not what I want. Playing with it I figured out that I could pan and zoom with the camera view simply by changing the code in the example to:


 private void performTranslation()
 {
        view.getTransform().setTranslation(new Vector3f(transXTmp, transYTmp, transZTmp));

}

Thus I am moving the translation vector of the view object. However, when I tried to rotate view in a similar way (see below) I wasn’t able to get the desired behaviour and I just end up resetting my camera position to (0,0,0) whenever I do a left mouse drag. I had hoped I could just do the following, or something like it but no luck.


 private void performRotation()
{            view.getTransform().rotXYZ(rotXTmp, rotYTmp, 0);
}

Anyone have any idea why this isn’t working for me?

Thanks again
Rob Leclerc

orbit here:

http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=xith3d;action=display;num=1077631615

may help.

Rdlecler,

I had the same problem before…

Do this when you rotate:


private void performRotation()  {
    Transform3D tempTransform = new Transform3D();
    tempTransform.rotXYZ(rotXTmp, rotYTmp, 0);
    view.getTransform().mul(tempTransform);
}

I think it would solve the problem :slight_smile:

Hi all,

I seem to be getting closer to getting a working demo of a cameralook. I pulled some code from another post which was doing something similar and added it into a modified version of MouseInteraction.java which I am basing the demo onhttp://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=xith3d;action=display;num=1077631615.

The code more or less does what I am asking except it has two bugs still which I can’t figure out.

  1. It seems to reset the camera perspective when you move to a different kind of camera movement. I tried updating the “trans” values with the new location but this didn’t work for me.

  2. If you look carefully I think I am getting gimbel lock (I think this is what it is called) which is causing some problems on the rotations around 90degrees.

The code below has a main method and can be run without modification. Anyone have any suggestions for how to fix these two problems? I think this would be a valuable demo for the xith3d project since it seems other people have both A) had similar problems B) are interested in some demo code which addresses this.



import java.awt.*;
import java.awt.event.*;

import javax.vecmath.*;

// Xith3D
import com.xith3d.scenegraph.*;
import com.xith3d.test.*;

// use Jogl
import com.xith3d.loaders.texture.TextureLoader;
import com.xith3d.render.*;
import com.xith3d.render.jogl.*;

public class MouseInteraction
{
    // current rotation
    private float rotX = 0;
    private float rotY = 0;
    private float rotZ = 0;

    // temporary rotation (used while dragging)
    private float rotXTmp = 0;
    private float rotYTmp = 0;
    private float rotZTmp = 0;

    // rotation part of our scenegraph
    private TransformGroup objRotate;
    private Transform3D rotate;

    // current translation
    private float transX = 0;
    private float transY = 0;
    private float transZ = 0;

    // temporary translation (used while dragging)
    private float transXTmp = 0;
    private float transYTmp = 0;
    private float transZTmp = 0;

    // translation part of our scenegraph
    private TransformGroup objTranslate;
    private Transform3D translate;
    
    View view = null;
    VirtualUniverse universe = null;
    
    // saves the cursor position when user starts to drag
    private int startDragX;
    private int startDragY;

    // schedule actions
    private boolean isRotationScheduled = false;
    private boolean isTranslationScheduled = false;

    BranchGroup scene = null;
    
    class EventListener implements AWTEventListener
    {
        public void eventDispatched(AWTEvent event)
        {
            if(event instanceof MouseEvent)
            {
                MouseEvent e = (MouseEvent) event;
                switch(e.getID())
                {
                    case MouseEvent.MOUSE_PRESSED: mousePressed(e); break;
                    case MouseEvent.MOUSE_RELEASED: mouseReleased(e); break;
                    case MouseEvent.MOUSE_DRAGGED: mouseDragged(e); break;
                }
            }
            else if(event instanceof KeyEvent)
            {
                KeyEvent e = (KeyEvent) event;
                switch(e.getID())
                {
                    case KeyEvent.KEY_TYPED: keyTyped(e); break;
                }
            }
        }
    }
    
    public static void main(String[] args)
    {
        new MouseInteraction();
    }

    public MouseInteraction()
    {      
        universe = new VirtualUniverse();
        view = new View();
        universe.addView(view);
        Locale locale = new Locale();
        universe.addLocale(locale);
        scene = new BranchGroup();
        locale.addBranchGraph(scene);
        
        translate = new Transform3D();
        objTranslate = new TransformGroup(translate);
        scene.addChild(objTranslate);

        rotate = new Transform3D();
        objRotate = new TransformGroup(rotate);
        objTranslate.addChild(objRotate);
        
        // create Cube & plane
        Geometry g = Cube.createCubeViaTriangles(0, 0, 0, 1, true);
        Shape3D cube = new Shape3D(g, new Appearance());
        objRotate.addChild(cube);
        addPlane();
       
        scene.compile();

        RenderPeer rp = new RenderPeerImpl();
        CanvasPeer cp = rp.makeCanvas(null, 640, 480, 32, false);
        Canvas3D canvas = new Canvas3D();
        canvas.set3DPeer(cp);
        
        Toolkit.getDefaultToolkit().addAWTEventListener(
            new EventListener(),   AWTEvent.KEY_EVENT_MASK
                                 | AWTEvent.MOUSE_EVENT_MASK
                                 | AWTEvent.MOUSE_MOTION_EVENT_MASK);

        // modify our view so we can see the cube
        view.addCanvas3D(canvas);

        transX = 0;
        transY = 0;
        transZ = 10;
        view.getTransform().lookAt(new Vector3f( transXTmp, transYTmp, transZ),   // location of eye
                                  new Vector3f( 0, 0, 0),   // center of view
                                  new Vector3f( 0, 1, 0));  // vector pointing up
       while(true)
        {
            view.renderOnce();
            
            if(isRotationScheduled)
            {
                performRotation();
                isRotationScheduled = false;
            }
            if(isTranslationScheduled)
            {
                performTranslation();
                isTranslationScheduled = false;
            }
        }


    }

    private void keyTyped(KeyEvent e)
    {
        switch(e.getKeyChar())
        {
            case 27: System.exit(0); break;
        }
    }

    public void addPlane(){
          Appearance groundApp = new Appearance();
            groundApp.setPolygonAttributes(
                  new PolygonAttributes(
                        PolygonAttributes.POLYGON_FILL,
                        PolygonAttributes.CULL_NONE,
                        0));
        float k = 2f; 
            float w = 2f; 
            float x1 = -2f;
            float x2 = 2f;
            float y = 0f;
            float z1 = -w;
            float z2 = w;
            float tsx = k * 1f;
            float tsy = k * 1f;
            Shape3D shape = new Shape3D();
            Geometry geom =
                  TestUtils.createQuad(
                        new Point3f(x1, z1, y),
                        new Point3f(x1, z2, y),
                        new Point3f(x2, z2, y),
                        new Point3f(x2, z1, y),
                        tsx,
                        tsy);
            shape.setAppearance(groundApp);
            shape.setGeometry(geom);
            objRotate.addChild(shape);
    }
    
    public void mouseDragged(MouseEvent e)
    {
        switch(e.getModifiers())
        {
            case MouseEvent.BUTTON1_MASK: leftDrag(e); break;
            case MouseEvent.BUTTON2_MASK: middleDrag(e); break;
            case MouseEvent.BUTTON3_MASK: rightDrag(e); break;
        }
    }

    private void mousePressed(MouseEvent e)
    {      
        startDragX = e.getX();
        startDragY = e.getY();
    }

    private void mouseReleased(MouseEvent e)
    {      rotX = rotXTmp;
        rotY = rotYTmp;
        rotZ = rotZTmp;
        transX = transXTmp;
        transY = transYTmp;
        transZ = transZTmp;
    }

    private void leftDrag(MouseEvent e)
    {      
        rotXTmp = rotY + (e.getY() - startDragY)/100f;
        rotYTmp = rotX + (e.getX() - startDragX)/100f;
        isRotationScheduled = true;
        
    }

    private void middleDrag(MouseEvent e)
    {      transZTmp = (transZ + (e.getY() - startDragY)/100f);
        isTranslationScheduled = true;
    }

    private void rightDrag(MouseEvent e)
    {      
        transXTmp = -(transX + (e.getX() - startDragX)/100f);
        transYTmp = -(transY - (e.getY() - startDragY)/100f);
        isTranslationScheduled = true;
    }


    private void performRotation()
    {      float cos = (float)Math.cos(rotXTmp); 
          float x = cos*(float)Math.cos(rotYTmp) * transZTmp; 
          float y =   (float)Math.sin(rotXTmp) * transZTmp; 
          float z = cos*(float)Math.sin(rotYTmp) * transZTmp;
          
          view.getTransform().lookAt(new Vector3f(x,y,z),  // location of eye
                new Vector3f( 0, 0, 0),   // center of view
                new Vector3f( 0, 1, 0));  // vector pointing up      
    }

    private void performTranslation()
      {      view.getTransform().setTranslation(new Vector3f(transXTmp, transYTmp, transZTmp));
    }
  
}

P.S. Don: One of the only problems with thre rotation the way you suggested is it doesn’t maintain a camera perspective based on the origin.

Thanks,
Rob Leclerc

Hi again

I think you want to do like this:


private void performRotation()  { 
    Transform3D tempXTransform = new Transform3D();
    Transform3D tempYTransform = new Transform3D();
    tempXTransform.rotX(rotXTmp);
    tempYTransform.rotY(rotYTmp);
    tempYTransform.mul(tempXTransform);
    view.getTransform().mul(tempYTransform); 
}


Now I hope this helps :wink:

Don,

I am not sure about you, but implementing the function the way you suggest is giving me really strange behavior and it gets hard to control the camera as the object moves to the edge of the field of view.

In addition, it still suffers from the problem that when you move from a zoom to a pan that it resets the camera position.

Am I the only one who finds it strange that this should be so hard? Previously when I was using Vortex and ODE (physics engines written in C) this kind of mouselook was already implemente into the system. Not wanting to indulge in masochism anymore, I am moving to Java, but in ode4java none of the example code has the mouselook implementation. I had assumed this would have been a fairly standard tool in a 3d library?

Best
RDL

P.S. If you are interested, you my website has movies of some of my previous work.

www.cpsc.ucalgary.ca/~leclerc

Hey I am jsut implementing the same thing at the moment. I shall try and post my code when its ready. I have written another post about being careful about the synchronization

http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=xith3d;action=display;num=1088210496;start=0

All right, here’s my OrbitView class, that works quite well for me. The math is a little raw probably, but I’m no genius. Seriously! ;D

Warning: I didn’t bother with render loop synchronization, but it’s a question of using one additional Transform3D as a buffer and copying its contents to the view’s transform after a call from the render loop.

SEE BELLOW}

That could be a valuable addition to the Xith toolkit.

All right, here it is, because I care. I hope I won’t get banned for flooding. This one’s much more universal and cleaner. Works great even at 1fps. Try it! :wink:

For changes read the header.


import com.xith3d.render.CanvasPeer;
import com.xith3d.scenegraph.Canvas3D;
import com.xith3d.scenegraph.Transform3D;
import com.xith3d.scenegraph.View;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.vecmath.Vector3f;

/**
 * A simple Orbit view manipulator. Left mouse rotates the view, right mouse
 * translates the view horizontaly, Shift + right mouse translates it verticaly.
 * Use mouse wheel for zoom. Version 1.1 fixes unintentional zooming behind your 
 * center of interest and inverted translation on the screen's Y axis when 
 * looking up. The usage changed also slightly, to accomodate render loop 
 * synchronization. See usage.
 *
 * @author Otelo
 * @version 1.1
 * @usage 
 *    OrbitView orbit = new OrbitView( yourCanvas3D );
 *    OrbitView orbit = new OrbitView( yourCanvas3D, centerOfInterestDistance );
 *    orbit.updateView();   //Call from your render loop.
 */
public class OrbitView {
    
    private View view;
    private CanvasPeer peer;
    //some vectors
    private Vector3f originToInterest = new Vector3f();
    private Vector3f interestToPivot  = new Vector3f();
    private Vector3f originToPivot    = new Vector3f();
    private Vector3f cameraFocusOld   = new Vector3f();
    private Vector3f cameraFocusNew   = new Vector3f();
    private Vector3f translateXYZ     = new Vector3f();
    private Vector3f rotateXYZ        = new Vector3f();
    private Vector3f helperVector     = new Vector3f();
    //some transforms
    private Transform3D t3d = new Transform3D();
    private Transform3D tmp = new Transform3D();
    private Transform3D tmq = new Transform3D();
    //some constants
    private static final float zoomStep = 0.1f;
    private static final float moveStep = 0.01f;
    private static final float minmDist = 0.5f;
    //mouse cursor locators
    private Point oldXY = new Point();
    private Point newXY = new Point();
    //valid action?
    private boolean action = false;
    private boolean hasChanged = false;
    
    /** Creates a new instance of OrbitView */
    public OrbitView(Canvas3D owner) {
        this(owner, 1f);
    }
    
    /** Creates a new instance of OrbitView */
    public OrbitView(Canvas3D owner, float centerOfInterestDistance) {
        
        view = owner.getView();
        peer = owner.get3DPeer();
        
        if(centerOfInterestDistance < minmDist) centerOfInterestDistance = minmDist;
        cameraFocusOld.set(0f, 0f, centerOfInterestDistance);
        cameraFocusNew.set(cameraFocusOld);
        
        peer.getComponent().addMouseListener(new MouseAdapter(){
            public void mousePressed(MouseEvent e){
                int mask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK;
                if((e.getModifiersEx() & mask) != 0){
                    action = true;
                    oldXY = e.getPoint();
                }
            }
            public void mouseReleased(MouseEvent e){
                action = false;
            }
        });
        
        peer.getComponent().addMouseMotionListener(new MouseMotionAdapter(){
            public void mouseDragged(MouseEvent e){
                int mask = MouseEvent.BUTTON1_DOWN_MASK;
                if(action){
                    newXY = e.getPoint();
                    if((e.getModifiersEx() & mask) == mask){ //rotateXY
                        rotate(
                                (float)(oldXY.y - newXY.y),
                                (float)(oldXY.x - newXY.x));
                    } else
                        if(e.isShiftDown())                  //moveY
                            translateY((float)(oldXY.y - newXY.y));
                        else                                 //moveXZ
                            translateXZ(
                                    (float)(oldXY.x - newXY.x),
                                    (float)(oldXY.y - newXY.y));
                    oldXY.setLocation(newXY);
                    hasChanged = true;
                }
            }
        });
        
        peer.getComponent().addMouseWheelListener(new MouseWheelListener(){
            public void mouseWheelMoved(MouseWheelEvent e){
                cameraFocusNew.z -= e.getWheelRotation() * zoomStep;
                hasChanged = true;
            }
        });
    }
    
    private void translateXZ(float dx, float dz){
        helperVector.set(1, 0, 0);
        view.getTransform().transform(helperVector);
        helperVector.normalize();
        helperVector.set(
                dx * helperVector.x * moveStep,
                0f,
                dx * helperVector.z * moveStep);
        translateXYZ.add(helperVector);
        //
        view.getTransform().getTranslation(helperVector);
        dz *= (helperVector.y < 0) ? -1f : 1f;
        //
        helperVector.set(0, 0, 1);
        view.getTransform().transform(helperVector);
        helperVector.normalize();
        helperVector.set(
                dz * helperVector.x * moveStep,
                0f,
                dz * helperVector.z * moveStep);
        translateXYZ.add(helperVector);
    }
    
    private void translateY(float dy){
        translateXYZ.y -= dy * moveStep;
    }
    
    private void rotate(float rx, float ry){
        rotateXYZ.x += rx;
        rotateXYZ.y += ry;        
    }
    
    public void updateView(){
        if(hasChanged) hasChanged = !hasChanged;
        else return;
        view.getTransform(tmq);
        //get camera's position which becomes the origin-to-pivot vector
        tmq.get(originToPivot);
        //find initial point of interest {from its distance...cameraFocusOld}
        interestToPivot.set(cameraFocusOld);
        tmq.transform(interestToPivot);
        //calculate origint-to-interest vector from the two vectors above
        originToInterest.sub(originToPivot, interestToPivot);
        //now we can update the interest-to-pivot vector for the new zoom distance
        if(cameraFocusNew.z != cameraFocusOld.z){
            interestToPivot.set(cameraFocusNew);
            tmq.transform(interestToPivot);
            //push our center of interest forward in case of excessive zoom
            if(cameraFocusNew.z < minmDist) cameraFocusNew.z = minmDist;
            cameraFocusOld.set(cameraFocusNew);
        }
        //apply the rotations
        t3d.rotY(rotateXYZ.y * moveStep);
        helperVector.set(1, 0, 0);
        tmq.transform(helperVector);
        tmp.rotAxis(helperVector, rotateXYZ.x * moveStep);
        t3d.mul(tmp);
        //apply the translations
        originToInterest.add(translateXYZ);
        //flatten the transforms
        tmp.set(originToInterest);
        tmp.mul(t3d);
        tmq.setTranslation(interestToPivot);
        tmp.mul(tmq);
        view.setTransform(tmp);
        //clean-up
        translateXYZ.set(0f, 0f, 0f);
        rotateXYZ.set(0f, 0f, 0f);
    }
}