> From: Juergen Neubauer <[EMAIL PROTECTED]>
> using the Vrml97Viewer from the examples that came with the
> vj3d0.90.2.src [...]
>
> My question is about the navigation in the examine-mode.
> Is it somehow possible to separate the navigation from the object view ?
>
> The problem:
> The zoom is always along the z-axis of the object, not along the actual
> view-axis.
> (So, if the object is rotated or looked at from the x-axis, the zoom is
> still along the z-axis...)

The problem here is that MouseZoom is really MouseTranslateZ.  That is,
MouseTranslate does XY translation and MouseZoom does Z translation, which makes
the object larger or smaller in a perspective view aligned with the Z axis.

The fix is to replace the MouseZoom behavior with a MouseScale behavior.
Attached below is a start.  It still isn't perfect, since the object is not
scaled/rotated around the origin, but its better than the MouseZoom behavior.

I'll see if I can improve on this sometime soon,


Doug Gehringer
Sun Microsystems
/*
 *      %Z%%M% %I% %E% %U%
 *
 * Copyright (c) 1996-1999 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 *
 * @Author: Doug Gehringer
 * @Author: Rick Goldberg
 */


// A Frame which creates a Canvas3D and a VrmlLoader and passes a URL to
// the loader

// TODO: make into an applet

import java.io.File;
import java.io.IOException;
import java.awt.*;
import java.awt.event.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.net.URL;

import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.loaders.vrml97.VrmlLoader;
import com.sun.j3d.loaders.vrml97.VrmlScene;
import vrml.BaseNode;
import com.sun.j3d.loaders.vrml97.node.Viewpoint;
//import TreePrinter;


public class Vrml97Viewer extends Frame implements ActionListener,
        ItemListener, MotionNotifierInterface {

    Canvas3D    canvas;
    VirtualUniverse     universe;
    Locale              locale;
    View                view;
    String              urlString;
    VrmlLoader          loader;
    Viewpoint[]         fileViewpoints = null;
    TransformGroup[]    fileVpWorldTrans;
    TransformGroup[]    fileVpOrientTrans;
    TransformGroup[]    fileVpTrans;
    ViewPlatform[]      fileVp;
    BranchGroup         objRoot;
    BoundingSphere      objBounds = null;
    BranchGroup         vpRoot;
    BranchGroup         browserGroup;
    DirectionalLight    headLight;
    AmbientLight        ambLight;
    ViewPlatform        vp ;
    TransformGroup      vpTrans, prevVpTrans;
    TransformGroup      vpWorldTrans, vpOrientTrans;
    TransformGroup      objVpObjectTrans;
    TransformGroup      objVpWorldTrans[] = new TransformGroup[NUM_OBJ_VIEWS];
    TransformGroup      objVpOrientTrans[] = new TransformGroup[NUM_OBJ_VIEWS];
    TransformGroup      objVpTrans[] = new TransformGroup[NUM_OBJ_VIEWS];
    ViewPlatform        objVp[] = new ViewPlatform[NUM_OBJ_VIEWS];
    BoundingSphere      browserBounds;
    float               vpFieldOfView, vpFrontClip, vpBackClip;
    float               objVpFieldOfView, objVpFrontClip;
    float               objVpBackClip;
    int                 viewMode = EXAMINE;
    TextField           gotoUrl;
    Panel               panel;
    Label               label;
    FileDialog          fd;
    String              filename;
    String              pathname;
    MenuBar             mb;
    Menu                m;
    Menu                fileVpMenu;
    Menu                objVpMenu;
    MenuItem            mi;
    CheckboxMenuItem    hlCheck;
    File                file;
    WindowListener      wl;
    int                 initVp = OBJ_VIEW_PLUS_Z;
    String              fileVpAction = "setFileVp ";
    String              objVpAction = "setObjVp ";
    String              modeAction = "setMode ";
    String              resetViewpoint = "Reset Viewpoint";
    Transform3D         identity = new Transform3D();
    NumFormat           numFormat = new NumFormat();
    boolean             fileLoaded = false;
    boolean             debug;
    boolean             timing;
    boolean             loadOnly;
    int                 numTris;
    boolean             startupTiming;
    int                 startupCount;
    int                 startupFrames = 5;
    int                 numFrames = 0;
    long                postTime;
    long                baseTime = System.currentTimeMillis();
    //TreePrinter               tp = new TreePrinter();

    static final int    AMB_LIGHT = 0;
    static final int    DIR_LIGHT = 1;
    static final int    BEHAVIOR  = 2;
    static final int    NUM_SLOTS = 3;

    static final int    EXAMINE = 1;
    static final int    FLY = 2;

    static final int    FILE_VIEW = -1;
    static final int    OBJ_VIEW_PLUS_X = 0;
    static final int    OBJ_VIEW_PLUS_Y = 1;
    static final int    OBJ_VIEW_PLUS_Z = 2;
    static final int    NUM_OBJ_VIEWS = 3;

    Vrml97Viewer(String initURL) {
        debug = Boolean.getBoolean("debug");
        timing = Boolean.getBoolean("timing");
        loadOnly = Boolean.getBoolean("loadOnly");
        urlString = initURL;

        if (!urlString.endsWith(".wrl") ) {
                pathname = urlString;
                urlString +="interp.wrl";
        } else {
                int lastSlash = urlString.lastIndexOf('/');
                if (lastSlash > 0) {
                    pathname =  urlString.substring(0, lastSlash+1);
                }
        }
        //System.out.println("pathname = " + pathname);

        // menus
        mb = new MenuBar();
        m = new Menu("File");
        mi = new MenuItem("Open");
        mi.addActionListener(this);
        m.add(mi);
        mi = new MenuItem("Exit");
        mi.addActionListener(this);
        m.add(mi);
        mb.add(m);

        m = new Menu("View");
        fileVpMenu = new Menu("File Viewpoint");
        m.add(fileVpMenu);
        objVpMenu = new Menu("Object Viewpoint");
        mi = new MenuItem("Object from +X");
        mi.addActionListener(this);
        mi.setActionCommand(objVpAction + OBJ_VIEW_PLUS_X);
        //objVpMenu.add(mi);
        mi = new MenuItem("Object from +Y");
        mi.addActionListener(this);
        mi.setActionCommand(objVpAction + OBJ_VIEW_PLUS_Y);
        //objVpMenu.add(mi);
        mi = new MenuItem("Object from +Z");
        mi.addActionListener(this);
        mi.setActionCommand(objVpAction + OBJ_VIEW_PLUS_Z);
        objVpMenu.add(mi);
        m.add(objVpMenu);
        mi = new MenuItem(resetViewpoint);
        mi.addActionListener(this);
        m.add(mi);
        mi = new MenuItem("WorldInfo");
        mi.addActionListener(this);
        m.add(mi);
        mb.add(m);

        m = new Menu("Mode");
        mi = new MenuItem("Examine");
        mi.addActionListener(this);
        mi.setActionCommand(modeAction + EXAMINE);
        m.add(mi);
        mi = new MenuItem("Fly");
        mi.setActionCommand(modeAction + FLY);
        mi.addActionListener(this);
        m.add(mi);
        mb.add(m);

        m = new Menu("Options");
        hlCheck = new CheckboxMenuItem("Headlight", true);
        hlCheck.addItemListener(this);
        m.add(hlCheck);
        mb.add(m);

        m = new Menu("Info");
        mi = new MenuItem("About");
        mi.addActionListener(this);
        m.add(mi);
        mb.add(m);

        fd = new FileDialog(this,"Visit WRL",FileDialog.LOAD);
        fd.setDirectory("."+File.separator+"parts");
        setMenuBar(mb);

        loader = new VrmlLoader();

    }

    public void actionPerformed(ActionEvent evt) {
        if(evt.getSource().equals(gotoUrl)){
            urlString = gotoUrl.getText();
            gotoUrl(urlString);
        } else if (evt.getSource() instanceof MenuItem) {
            String arg = evt.getActionCommand();
            if (arg.equals("Open")) {
                fd.show();
                gotoUrl.setText(pathname+fd.getFile());
                gotoUrl(pathname+fd.getFile());
            } else if (arg.equals("Exit")) {
                if (timing) {
                    outputTiming();
                }
                System.exit(0);
            } else if (arg.startsWith(fileVpAction)) {
                String indexString = arg.substring(fileVpAction.length());
                int index = Integer.valueOf(indexString).intValue();
                setFileViewpoint(index);
            } else if (arg.startsWith(objVpAction)) {
                String indexString = arg.substring(objVpAction.length());
                int index = Integer.valueOf(indexString).intValue();
                setObjViewpoint(index);
            } else if (arg.equals(resetViewpoint)) {
                resetViewpoint();
            } else if (arg.startsWith(modeAction)) {
                String indexString = arg.substring(modeAction.length());
                int index = Integer.valueOf(indexString).intValue();
                setMode(index);
            } else {
                System.out.println("Unknown action: " + arg);
            }
        }
    }

    public void itemStateChanged(ItemEvent evt) {
        if (evt.getItem().equals("Headlight")) {
            setHeadlight(hlCheck.getState());
        } else {
            System.out.println("Unknown item changed: " + evt);
        }
    }

    // This is the normal UniverseBuilder code, except that the ViewPlatform
    // will only be used if VRML doesn't define one
    void setupJ3D(Canvas3D canvas) {

        // Establish an unnamed virtual universe, with a single hi-res Locale
        universe = new VirtualUniverse();
        locale = new Locale(universe);

        // Create a PhysicalBody and Physical Environment object
        PhysicalBody body = new PhysicalBody();
        PhysicalEnvironment environment = new PhysicalEnvironment();

        // Create a View and attach the Canvas3D and the physical
        // body and environment to the view.
        view = new View();
        view.addCanvas3D(canvas);
        view.setPhysicalBody(body);
        view.setPhysicalEnvironment(environment);

        // Create a branch group node for the default view platform
        vpRoot = new BranchGroup();
        vpRoot.setCapability(BranchGroup.ALLOW_CHILDREN_READ);

        // This is the stuff that the browser needs to have in the same
        // place as the view platform.  It will get added to the view platform
        // group when a view is selected
        browserGroup = new BranchGroup();
        browserGroup.setCapability(BranchGroup.ALLOW_DETACH);
        browserGroup.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        browserGroup.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        setSize(browserGroup, NUM_SLOTS);
        BoundingSphere lightBounds =
                new BoundingSphere(new Point3d(), Double.MAX_VALUE);
        ambLight = new AmbientLight(true, new Color3f(0.2f, 0.2f, 0.2f));
        ambLight.setInfluencingBounds(lightBounds);
        ambLight.setCapability(Light.ALLOW_STATE_WRITE);
        browserGroup.setChild(ambLight, AMB_LIGHT);
        headLight = new DirectionalLight();
        headLight.setColor(new Color3f(0.8f, 0.8f, 0.8f));
        headLight.setCapability(Light.ALLOW_STATE_WRITE);
        headLight.setInfluencingBounds(lightBounds);
        browserGroup.setChild(headLight, DIR_LIGHT);


        // initialize the default view tree.  These are predefined views
        // which display the whole object along each of the axes.
        // The Object transform is used to scale and translate the object so
        // it's BoundingSphere is at the origin with radius=1 in the View
        // space.  The World transform is used to tranform the "world"
        // relative to the view (examine mode). The Orient tranform is used
        // to set the initial placement of the view (set by the VRML fields
        // for VRML Viewpoints and initialzed here for obj views) and the
        // VpTrans is the transform from the initial view platform orientation
        // made by the browser
        objVpFieldOfView = .785398f;
        objVpFrontClip = 0.01f;
        objVpBackClip = 30.0f;
        double eyeDist = 1.1 / Math.tan(objVpFieldOfView/ 2.0);
        Point3d origin = new Point3d();
        Point3d offset = new Point3d();
        Vector3d up = new Vector3d();
        Transform3D eyeTrans = new Transform3D();

        objVpObjectTrans =  new TransformGroup();
        objVpObjectTrans.setCapability(vpTrans.ALLOW_TRANSFORM_READ);
        objVpObjectTrans.setCapability(vpTrans.ALLOW_TRANSFORM_WRITE);
        objVpObjectTrans.setCapability(Group.ALLOW_CHILDREN_READ);
        objVpObjectTrans.setCapability(Group.ALLOW_CHILDREN_WRITE);
        objVpObjectTrans.setCapability(Group.ALLOW_CHILDREN_EXTEND);
        for (int i = 0; i < NUM_OBJ_VIEWS; i++) {
            objVpWorldTrans[i] =  new TransformGroup();
            objVpWorldTrans[i].setCapability(vpTrans.ALLOW_TRANSFORM_READ);
            objVpWorldTrans[i].setCapability(vpTrans.ALLOW_TRANSFORM_WRITE);
            objVpWorldTrans[i].setCapability(Group.ALLOW_CHILDREN_READ);
            objVpWorldTrans[i].setCapability(Group.ALLOW_CHILDREN_WRITE);
            objVpWorldTrans[i].setCapability(Group.ALLOW_CHILDREN_EXTEND);
            objVpObjectTrans.addChild(objVpWorldTrans[i]);
            objVpOrientTrans[i] =  new TransformGroup();
            objVpOrientTrans[i].setCapability(vpTrans.ALLOW_TRANSFORM_READ);
            objVpOrientTrans[i].setCapability(vpTrans.ALLOW_TRANSFORM_WRITE);
            objVpOrientTrans[i].setCapability(Group.ALLOW_CHILDREN_READ);
            objVpOrientTrans[i].setCapability(Group.ALLOW_CHILDREN_WRITE);
            objVpOrientTrans[i].setCapability(Group.ALLOW_CHILDREN_EXTEND);
            // set orient trans to view sphere of radius 1 at origin
            switch (i) {
              case OBJ_VIEW_PLUS_X:
                offset.x = eyeDist;
                offset.y = 0.0;
                offset.z = 0.0;
                up.x = 0.0;
                up.y = 0.0;
                up.z = 1.0;
                eyeTrans.lookAt(offset, origin, up);
                //System.out.println("+X eye xform = \n" + eyeTrans);
                break;
              case OBJ_VIEW_PLUS_Y:
                offset.x = 0.0;
                offset.y = eyeDist;
                offset.z = 0.0;
                up.x = 0.0;
                up.y = 0.0;
                up.z = 1.0;
                eyeTrans.lookAt(offset, origin, up);
                // System.out.println("+Y eye xform = \n" + eyeTrans);
                break;
              case OBJ_VIEW_PLUS_Z:
                offset.x = 0.0;
                offset.y = 0.0;
                offset.z = eyeDist;
                up.x = 0.0;
                up.y = 1.0;
                up.z = 0.0;
                eyeTrans.lookAt(offset, origin, up);
                // System.out.println("+Z eye xform = \n" + eyeTrans);
                break;
            }
            eyeTrans.invert(); // need to invert because on view side of tree
            objVpOrientTrans[i].setTransform(eyeTrans);
            objVpWorldTrans[i].addChild(objVpOrientTrans[i]);
            objVpTrans[i] = new TransformGroup();
            objVpTrans[i].setCapability(vpTrans.ALLOW_TRANSFORM_READ);
            objVpTrans[i].setCapability(vpTrans.ALLOW_TRANSFORM_WRITE);
            objVpTrans[i].setCapability(Group.ALLOW_CHILDREN_READ);
            objVpTrans[i].setCapability(Group.ALLOW_CHILDREN_WRITE);
            objVpTrans[i].setCapability(Group.ALLOW_CHILDREN_EXTEND);
            objVpOrientTrans[i].addChild(objVpTrans[i]);
            objVp[i] = new ViewPlatform();
            objVpTrans[i].addChild(objVp[i]);
        }

        browserBounds = new BoundingSphere(new Point3d(), Double.MAX_VALUE);

        // currently don't have a file version for these, so they will always
        // be the obj versions
        vpFieldOfView = objVpFieldOfView;
        vpFrontClip = objVpFrontClip;
        vpBackClip = objVpBackClip;

        updateBehavior();

        vpRoot.addChild(objVpObjectTrans);
        objVpTrans[0].addChild(browserGroup);
        prevVpTrans = objVpTrans[0];

        // setup the initial obj viewpoints and use one
        setupObjViewpoints();
        setObjViewpoint(0);


        // Attach the vpRoot to the universe, via the Locale.
        locale.addBranchGraph(vpRoot);

        objRoot = new BranchGroup();
        objRoot.setCapability(BranchGroup.ALLOW_BOUNDS_READ);
        objRoot.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        objRoot.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        objRoot.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
        objRoot.setCapability(BranchGroup.ALLOW_DETACH);

    }

    private void setSize(javax.media.j3d.Group g, int size) {
        for (int i = 0; i < size; i++) {
            g.addChild(new NullNode());
        }
    }

    void setupCanvas() {
        if (timing) {
            canvas = new TimingCanvas3D(null, this);
        } else {
            canvas = new Canvas3D(null);
        }
        add("Center",canvas);
        panel = new Panel();
        panel.setLayout(new FlowLayout(FlowLayout.LEFT));
        gotoUrl = new GotoWRLTextField(this,urlString,60);
        label = new Label("WRL:");
        panel.add(label);
        panel.add(gotoUrl);
        add("North",panel);
        canvas.setCursor(new Cursor(Cursor.WAIT_CURSOR));
        setupJ3D(canvas);
        show();
    }

    void removeOldURL() {
        objRoot.detach();
        int numToRemove = objRoot.numChildren();
        for (int i = 0; i < numToRemove; i++) {
            objRoot.removeChild(0);
        }
        fileVpMenu.removeAll();
        fileViewpoints = null;
        objBounds = null;
    }

    void resetViewpoint() {
        vpWorldTrans.setTransform(identity);
        vpTrans.setTransform(identity);
    }

    private void removeBgFromPrev() {
        if (debug) System.out.println("removeBgFromPrev()");
        int numChildren = prevVpTrans.numChildren();
        boolean found = false;
        for (int i = numChildren-1; i >= 0; i--) {
            Node child = prevVpTrans.getChild(i);
            if (child.equals(browserGroup)) {
                if (debug) System.out.println("removeBgFromPrev():" +
                        " removing child " + i + " from group " + prevVpTrans);
                prevVpTrans.removeChild(i);
                found = true;
            }
        }
        if (!found) {
            System.out.println("Can'f find browserGroup on previous vpTrans");
        }
    }


    private void updateView() {
        if (debug) System.out.println("updateView()");
        resetViewpoint();
        removeBgFromPrev();
        vpTrans.addChild(browserGroup);
        if (debug) System.out.println("updateView(): updating view");
        view.setFieldOfView(vpFieldOfView);
        view.setFrontClipDistance(vpFrontClip);
        view.setBackClipDistance(vpBackClip);
        view.attachViewPlatform(vp);
        updateBehavior();

        prevVpTrans = vpTrans;
    }

    void setFileViewpoint(int index) {
        if (debug) System.out.println("setFileViewpoint(" + index + ")");
        Viewpoint viewpoint = fileViewpoints[index];
        vpWorldTrans = fileVpWorldTrans[index];
        vpOrientTrans = fileVpOrientTrans[index];
        vpTrans = fileVpTrans[index];
        vp = fileVp[index];

        updateView();
    }

    void setObjViewpoint(int index) {
        if (debug) System.out.println("setObjViewpoint(" + index + ")");
        vp = objVp[index];
        vpTrans = objVpTrans[index];
        vpWorldTrans = objVpWorldTrans[index];
        vpOrientTrans = objVpOrientTrans[index];

        updateView();
    }

    void setupObjViewpoints() {
        if (debug) System.out.println("setupObjViewpoints()");

        Vector3d offset = new Vector3d();
        Transform3D objTrans = new Transform3D();

        if (objBounds == null) {
            objVpObjectTrans.setTransform(identity);
        } else {

            // Scale the obj trans so that the bounding sphere has radius 1
            // in view space
            double radius = objBounds.getRadius();

            // Set the Obj trans so the bounding sphere is at the origin
            Point3d center = new Point3d();
            objBounds.getCenter(center);
            offset.x = center.x;
            offset.y = center.y;
            offset.z = center.z;
            if (debug) {
                System.out.println("Default view center:" + center +
                        " distance " + radius);
            }
            objTrans.set(radius, offset);
            objVpObjectTrans.setTransform(objTrans);

        }


    }

    void setupFileViewpoints() {
        if (fileViewpoints != null) {
            MenuItem mi;
            String desc;
            fileVpWorldTrans = new TransformGroup[fileViewpoints.length];
            fileVpOrientTrans = new TransformGroup[fileViewpoints.length];
            fileVpTrans = new TransformGroup[fileViewpoints.length];
            fileVp = new ViewPlatform[fileViewpoints.length];
            for (int i = 0; i < fileViewpoints.length; i++) {
                // insert a transform above and below the Viewpoint's
                // TransformGroup
                TransformGroup viewOri = fileViewpoints[i].getTransformGroup();
                viewOri.setCapability(BranchGroup.ALLOW_CHILDREN_READ);

                Group parent = (Group)viewOri.getParent();
                TransformGroup viewWorld = new TransformGroup();
                viewWorld.setCapability(viewWorld.ALLOW_TRANSFORM_READ);
                viewWorld.setCapability(viewWorld.ALLOW_TRANSFORM_WRITE);

                // Replace the TG by a new TG which will hold the ViewTG
                int numChildren = parent.numChildren();
                boolean found = false;
                for (int j = 0; (j < numChildren) && (!found); j++) {
                    if (parent.getChild(j) == viewOri) {
                        found = true;
                        parent.setChild(viewWorld, j);
                    }
                }
                if (!found) {
                    System.err.println("Internal error, can't find viewOri");
                }
                viewWorld.addChild(viewOri);

                TransformGroup viewBrowser = new TransformGroup();
                viewBrowser.setCapability(viewBrowser.ALLOW_TRANSFORM_READ);
                viewBrowser.setCapability(viewBrowser.ALLOW_TRANSFORM_WRITE);
                viewBrowser.setCapability(viewBrowser.ALLOW_CHILDREN_READ);
                viewBrowser.setCapability(viewBrowser.ALLOW_CHILDREN_WRITE);
                viewBrowser.setCapability(viewBrowser.ALLOW_CHILDREN_EXTEND);
                viewOri.setChild(viewBrowser, 0);
                viewBrowser.addChild(fileViewpoints[i].getViewPlatform());

                fileVpWorldTrans[i] = viewWorld;
                fileVpOrientTrans[i] = viewOri;
                fileVpTrans[i] = viewBrowser;
                fileVp[i] = fileViewpoints[i].getViewPlatform();

                if (((desc = fileViewpoints[i].getDescription()) == null) ||
                                                            desc.equals("")) {
                    desc = "Viewpoint " + i;
                }
                mi = new MenuItem(desc);
                mi.addActionListener(this);
                mi.setActionCommand(fileVpAction + i);
                fileVpMenu.add(mi);
            }
        }
    }

    void setHeadlight(boolean state) {
        if (debug) System.out.println("setHeadlight(" + state + ")");
        ambLight.setEnable(state);
        headLight.setEnable(state);
    }

    private void updateBehavior() {
        BranchGroup bg = new BranchGroup();
        bg.setCapability(BranchGroup.ALLOW_DETACH);
        bg.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        KeyBehavior kb = new KeyBehavior(this);
        kb.setSchedulingBounds(browserBounds);
        bg.addChild(kb);
        switch (viewMode) {
          case EXAMINE:
            MouseRotate mr = new MouseRotate(MouseRotate.INVERT_INPUT);
            mr.setTransformGroup(vpWorldTrans);
            mr.setSchedulingBounds(browserBounds);
            bg.addChild(mr);
            MouseTranslate mt = new MouseTranslate(MouseTranslate.INVERT_INPUT);
            mt.setTransformGroup(vpWorldTrans);
            mt.setSchedulingBounds(browserBounds);
            bg.addChild(mt);
            MouseScale ms = new MouseScale(MouseScale.INVERT_INPUT);
            ms.setTransformGroup(vpWorldTrans);
            ms.setSchedulingBounds(browserBounds);
            bg.addChild(ms);
            break;
          case FLY:
            FlightBehavior fb = new FlightBehavior(0.0, 0.0, 0.0,
                0.0, 0.0, 0.0, vpTrans, canvas);
            bg.addChild(fb);
            break;
        }
        browserGroup.setChild(bg, BEHAVIOR);
    }

    void setMode(int mode) {
        viewMode = mode;
        updateBehavior();
    }

    synchronized void gotoUrl(String u) {
        try {
            Node        j3dNode;
            VrmlScene   scene;  // local var so VRML stuff will get gc'd
            canvas.setCursor(new Cursor(Cursor.WAIT_CURSOR));
            removeOldURL();
            fileLoaded = false;
            long startFree = 0;
            scene = (VrmlScene)loader.load(new URL(u));
            Hashtable named = scene.getNamedObjects();
            if (debug || timing || loadOnly) {
                System.gc();
                System.out.println("After parse, freeMemory = " +
                    numFormat.format(
                        Runtime.getRuntime().freeMemory()/(1024*1024.0),2) +
                    " totalMemory = " +
                    numFormat.format(
                        Runtime.getRuntime().totalMemory()/(1024*1024.0),2));
            }
            if (loadOnly) {
                System.exit(0);
            }


            BranchGroup sceneGroup = scene.getSceneGroup();

            // Clean the scene to prepare to compile it.  This will keep the
            // scene from being pickable or collidable, but we don't need
            // either of those for this app.
            scene.cleanForCompile(sceneGroup);
            if (debug) {
               System.out.println("Adding subtree:");
               System.out.println(sceneGroup);
               //tp.print(sceneGroup);
               //System.out.println("");
            }

            // setting up file viewpoints modifies nodes in the Scene graph,
            // so we need to do this before we compile/add
            fileViewpoints = scene.getViewpoints();
            if (fileViewpoints.length > 0) {
                setupFileViewpoints();
            }

            // compile the branchgraph to optimize it
            sceneGroup.compile();

            // add the brachgraph to our object tree
            objRoot.addChild(sceneGroup);

            locale.addBranchGraph(objRoot);
            objBounds = (BoundingSphere)objRoot.getBounds();
            if (debug) {
                System.out.println("Object Bounds = " + objBounds);
                TransparencyAttributes sphereTransp =
                        new TransparencyAttributes(
                                TransparencyAttributes.FASTEST, 0.95f);
                Appearance sphereApp = new Appearance();
                sphereApp.setTransparencyAttributes(sphereTransp);
                Sphere sphere = new Sphere((float)objBounds.getRadius(),
                                sphereApp);
                TransformGroup sphereTG = new TransformGroup();
                Point3d center = new Point3d();
                objBounds.getCenter(center);
                Vector3d sphereOffset = new Vector3d(center);
                Transform3D sphereXform = new Transform3D();
                sphereXform.setTranslation(sphereOffset);
                sphereTG.setTransform(sphereXform);
                sphereTG.addChild(sphere);
                BranchGroup sphereBG = new BranchGroup();
                sphereBG.addChild(sphereTG);
                objRoot.addChild(sphereBG);
            }
            if (timing) {
                startupTiming = true;
                startupCount = 0;
                postTime = System.currentTimeMillis();
            }
            fileLoaded = true;
            setupObjViewpoints();

            if ((fileViewpoints.length > 0) && (initVp == FILE_VIEW)) {
                setFileViewpoint(0);
            } else {
                setObjViewpoint(OBJ_VIEW_PLUS_Z);
            }

            numTris = scene.getNumTris();
            if (debug || timing) {
                System.out.println("Scene contains " + numTris + " triangles");
            }
            String desc = scene.getDescription();
            if ((desc == null) || desc.equals("")) {
                desc = u.substring(u.lastIndexOf('/')+1);
            }
            setTitle("Vrml97 Viewer : " + desc);
        } catch (java.io.IOException e) {
            System.err.println("IO exception reading URL");
        } catch (vrml.InvalidVRMLSyntaxException e) {
            System.err.println("VRML parse error");
            e.printStackTrace(System.err);
        }

        canvas.setCursor(new Cursor(Cursor.HAND_CURSOR));
    }

    void outputTiming() {
        // TODO: remove TimingCanvas3D and switch these to use view instead of
        // tc when these methods are implemented
        TimingCanvas3D tc = (TimingCanvas3D) canvas;
        long now = System.currentTimeMillis();
        long[] startTimes = new long[10];
        tc.getFrameStartTimes(startTimes);
        long lastFrameDuration = tc.getLastFrameDuration();
        int numFrames = (int)tc.getFrameNumber();

        if (numFrames > 10) numFrames = 10;
        double elapsed = (now - startTimes[numFrames-1]) / 1000.0;
        System.out.println("Last " + numFrames + " frames rendered in " +
                numFormat.format(elapsed, 1) + " seconds, " +
                numFormat.format(numFrames / elapsed, 2) + " frames/sec");
        System.out.println(numTris + " triangles/frame ");
        System.out.println("Overall rate: " +
                numFormat.format((numTris * numFrames) / (1000.0 * elapsed),0) +
                "K triangles/sec");
        System.out.println("Last frame rendered in " +
                numFormat.format(lastFrameDuration / 1000.0, 3) + " seconds " +
                numFormat.format(1.0 * numTris / lastFrameDuration, 0) +
                "K triangles/sec ");
    }

    public void postRender() {
        if (startupTiming) {
            if ((startupCount++) == startupFrames) {
                TimingCanvas3D tc = (TimingCanvas3D) canvas;
                long[] startTimes = new long[startupFrames+1];
                long[] durations = new long[startupFrames+1];
                tc.getFrameStartTimes(startTimes);
                tc.getFrameDurations(durations);
                int first = startupFrames;
                System.out.println("Post to first start: " +
                    numFormat.format((startTimes[first] - postTime)/1000.0,2));
                for (int i = 0; i < startupFrames; i++) {
                    double overall =
                        (startTimes[first-i-1] - startTimes[first-i]) / 1000.0;
                    double render = durations[first - i] / 1000.0;
                    double other = overall - render;
                    System.out.println("Frame " + i +
                        " overall time " + numFormat.format(overall, 3) +
                        " render time "  + numFormat.format(render,  3) +
                        " other time "   + numFormat.format(other,   3));
                }
                startupTiming = false;
            }
        }
        if (fileLoaded && ((numFrames++ % 10) == 0)) {
            outputTiming();
        }
    }

    // MotionNotifierInteface
    public void viewMoved( Matrix4d position ) {}
    public void velocityChanged(double velocity) {}
    public void buttonPressed(int buttonId) {};

    public void processEvent(AWTEvent e) {
        if(e.getID() == WindowEvent.WINDOW_CLOSING){
            dispose();
            System.exit(0);
        }
    }

    public void setGotoString(String s) {
        gotoUrl.setText(s);
        validate();
    }


    public static void main(String[] args) {

        String urlString = null;
        if (args.length == 0) {
            urlString = "file:" +
                System.getProperties().getProperty("user.dir") +
                "/parts/simple.wrl";
        } else {
            if (args.length != 1) {
                System.out.println("Usage: Vrml97Viewer [<URL>]");
                System.exit(0);
            } else {
                urlString = args[0];
            }
        }

        Vrml97Viewer v97v = new Vrml97Viewer(urlString);
        v97v.setSize(780, 780);
        v97v.setupCanvas();
        v97v.gotoUrl(urlString);
    }

}

/*
 *      @(#)MouseScale.java 1.22 99/09/15 13:46:25
 *
 * Copyright (c) 1996-1999 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

// package com.sun.j3d.utils.behaviors.mouse;

import com.sun.j3d.utils.behaviors.mouse.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;


/**
 * MouseScale is a Java3D behavior object that lets users control the
 * scaling of an object via a mouse drag motion with the second
 * mouse button for movement along the y axis. See MouseRotate for similar usage info.
 */

public class MouseScale extends MouseBehavior {
    double scale_factor = 1.01;

  private MouseBehaviorCallback callback = null;

  /**
   * Creates a scale behavior given the transform group.
   * @param transformGroup The transformGroup to operate on.
   */
  public MouseScale(TransformGroup transformGroup) {
    super(transformGroup);
  }

  /**
   * Creates a default mouse scale behavior.
   **/
  public MouseScale(){
    super(0);
  }

  /**
   * Creates a scale behavior.
   * Note that this behavior still needs a transform
   * group to work on (use setTransformGroup(tg)) and
   * the transform group must add this behavior.
   * @param flags
   */
  public MouseScale(int flags) {
      super(flags);
   }

  public void initialize() {
    super.initialize();
    if ((flags & INVERT_INPUT) == INVERT_INPUT) {
       invert = true;
    }
  }

  /**
   * Return the scale movement multipler.
   **/

  public double getFactor() {
    return scale_factor;
  }

  /**
   * Set the scale movement multipler with factor.
   **/

  public void setFactor( double factor) {
    scale_factor = factor;
  }

  public void processStimulus (Enumeration criteria) {
    WakeupCriterion wakeup;
    AWTEvent[] event;
    int id;
    int dx, dy;
    double scale = 1.0;

    while (criteria.hasMoreElements()) {
      wakeup = (WakeupCriterion) criteria.nextElement();
      if (wakeup instanceof WakeupOnAWTEvent) {
        event = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
        for (int i=0; i<event.length; i++) {
          processMouseEvent((MouseEvent) event[i]);

          if (((buttonPress)&&((flags & MANUAL_WAKEUP) == 0)) ||
              ((wakeUp)&&((flags & MANUAL_WAKEUP) != 0))){
            id = event[i].getID();
            if ((id == MouseEvent.MOUSE_DRAGGED) &&
                ((MouseEvent)event[i]).isAltDown() &&
                !((MouseEvent)event[i]).isMetaDown()){


              x = ((MouseEvent)event[i]).getX();
              y = ((MouseEvent)event[i]).getY();

              dx = x - x_last;
              dy = y - y_last;

              if (!reset){
                transformGroup.getTransform(currXform);

                //System.out.println("dy = " + dy);
                scale = 1.0;
                if (dy > 0) {
                    for (int iy = 0; iy < dy; iy++) {
                        scale *= scale_factor;
                    }
                } else {
                    for (int iy = 0; iy < -dy; iy++) {
                        scale /= scale_factor;
                    }
                }
                //System.out.println("scale = " + scale);

                transformX.set(scale);

                if (invert) {
                  currXform.mul(currXform, transformX);
                } else {
                  currXform.mul(transformX, currXform);
                }

                transformGroup.setTransform(currXform);

                transformChanged( currXform );

                if (callback!=null)
                    callback.transformChanged( MouseBehaviorCallback.ZOOM,
                                               currXform );

              }
              else {
                reset = false;
              }

              x_last = x;
              y_last = y;
            }
            else if (id == MouseEvent.MOUSE_PRESSED) {
              x_last = ((MouseEvent)event[i]).getX();
              y_last = ((MouseEvent)event[i]).getY();
            }
          }
        }
      }
    }

    wakeupOn (mouseCriterion);
  }
  /**
    * Users can overload this method  which is called every time
    * the Behavior updates the transform
    *
    * Default implementation does nothing
    */
  public void transformChanged( Transform3D transform ) {
  }

  /**
    * The transformChanged method in the callback class will
    * be called every time the transform is updated
    */
  public void setupCallback( MouseBehaviorCallback callback ) {
      this.callback = callback;
  }
}

Reply via email to