javax.media.j3d.VirtualUniverse.java Source code

Java tutorial

Introduction

Here is the source code for javax.media.j3d.VirtualUniverse.java

Source

/*
 * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *
 */

package javax.media.j3d;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A VirtualUniverse object is the top-level container for all scene
 * graphs.  A virtual universe consists of a set of Locale objects,
 * each of which has a high-resolution position within the virtual
 * universe.  An application or applet may have more than one
 * VirtualUniverse objects, but many applications will need only one.
 * Virtual universes are separate entities in that no node object may
 * exist in more than one virtual universe at any one time. Likewise,
 * the objects in one virtual universe are not visible in, nor do they
 * interact with objects in, any other virtual universe.
 * <p>
 * A VirtualUniverse object defines methods to enumerate its Locale
 * objects and to remove them from the virtual universe.
 *
 * <p>
 * For more information, see the
 * <a href="doc-files/intro.html">Introduction to the Java 3D API</a> and
 * <a href="doc-files/VirtualUniverse.html">Scene Graph Superstructure</a>
 * documents.
 *
 * @see Locale
 */

public class VirtualUniverse extends Object {
    // NOTE TO DEVELOPERS:
    //
    // Developers who modify Java 3D in any way should modify
    // the auxiliary implementation vendor string in VersionInfo.java.
    // See that file for instructions.

    // The global MasterControl object.  There is only one of these
    // for all of Java 3D.
    static MasterControl mc = null;

    // The lock to acquire before traversing the scene graph
    Object sceneGraphLock = new Object();
    Object behaviorLock = new Object();

    // A list of locales that are contained within this universe
    Vector<Locale> listOfLocales = new Vector<Locale>();

    // The list of view platforms, a changed flag and a cached array
    private ArrayList<ViewPlatformRetained> viewPlatforms = new ArrayList<ViewPlatformRetained>();
    private boolean vpChanged = false;
    private ViewPlatformRetained[] viewPlatformList = new ViewPlatformRetained[0];

    // The Behavior Scheduler Thread for this Virtual Universe.
    BehaviorScheduler behaviorScheduler = null;

    // The geometry structure for this Universe
    GeometryStructure geometryStructure = null;

    // The transform structure for this Universe
    TransformStructure transformStructure = null;

    // The behavior structure for this Universe
    BehaviorStructure behaviorStructure = null;

    // The sound structure for this Universe
    SoundStructure soundStructure = null;

    // The rendering attributes structure for this Universe
    RenderingEnvironmentStructure renderingEnvironmentStructure = null;

    // Reference count of users of the RenderingEnvironmentStructure
    int renderingEnvironmentStructureRefCount = 0;

    // This is a global counter for node id's.
    long nodeIdCount = 0;

    // This is a global counter for view id's.
    int viewIdCount = 0;

    // This is a vector of free nodeid's
    Vector<String> nodeIdFreeList = new Vector<String>();

    // This is a vector of free viewid's
    ArrayList<Integer> viewIdFreeList = new ArrayList<Integer>();

    // The number of nodes in this universe
    int numNodes = 0;

    // The State object used when branch graphs are added
    SetLiveState setLiveState;

    // This is an array of references to objects that need their mirror
    // copies updated.  It is updated by the traverser and emptied by
    // the view thread.
    ObjectUpdate[] updateObjects = new ObjectUpdate[16];

    // The number of valid entries in updateObjects
    int updateObjectsLen = 0;

    // The current primary view for this universe
    View currentView;

    // A flag to indicate that we are in a behavior routine
    boolean inBehavior = false;

    // Flags to indicate if events need to be delivered
    boolean enableComponent = false;
    boolean enableFocus = false;
    boolean enableKey = false;
    boolean enableMouse = false;
    boolean enableMouseMotion = false;
    boolean enableMouseWheel = false;

    // Keep track of how many active View use this universe
    int activeViewCount = 0;

    // Root ThreadGroup for creating Java 3D threads
    static ThreadGroup rootThreadGroup;

    // Properties object for getProperties
    private static J3dQueryProps properties = null;

    // Flag to indicate that user thread has to
    // stop until MC completely register/unregister View.
    View regViewWaiting = null;
    View unRegViewWaiting = null;
    boolean isSceneGraphLock = false;

    private Object waitLock = new Object();

    // Set of scene graph structure change listeners
    private HashSet<GraphStructureChangeListener> structureChangeListenerSet = null;

    // Set of shader error listeners
    private HashSet<ShaderErrorListener> shaderErrorListenerSet = null;
    private ShaderErrorListener defaultShaderErrorListener = ShaderProgram.getDefaultErrorListener();

    // Set of rendering error listeners
    private static HashSet<RenderingErrorListener> renderingErrorListenerSet = null;
    private static RenderingErrorListener defaultRenderingErrorListener = Renderer.getDefaultErrorListener();

    /**
     * Constructs a new VirtualUniverse.
     */
    public VirtualUniverse() {
        setLiveState = new SetLiveState(this);
        initMCStructure();
    }

    void initMCStructure() {
        if (geometryStructure != null) {
            geometryStructure.cleanup();
        }
        geometryStructure = new GeometryStructure(this);
        if (transformStructure != null) {
            transformStructure.cleanup();
        }
        transformStructure = new TransformStructure(this);
        if (behaviorStructure != null) {
            behaviorStructure.cleanup();
        }
        behaviorStructure = new BehaviorStructure(this);
        if (soundStructure != null) {
            soundStructure.cleanup();
        }
        soundStructure = new SoundStructure(this);
        if (renderingEnvironmentStructure != null) {
            renderingEnvironmentStructure.cleanup();
        }
        renderingEnvironmentStructure = new RenderingEnvironmentStructure(this);

    }

    /**
     * Initialize the native interface and anything else that needs
     * to be initialized.
     */
    static void loadLibraries() {
        // No need to do anything.  The act of calling any method in this
        // class is sufficient to cause the static MasterControl object
        // to be created which, in turn, loads the native libraries.
    }

    static {
        boolean isLoggableConfig = MasterControl.isCoreLoggable(Level.CONFIG);
        Logger logger = MasterControl.getCoreLogger();

        // Print out version information unless this is a
        // non-debuggable, release (fcs) build
        if (isLoggableConfig || J3dDebug.devPhase || VersionInfo.isDebug) {
            StringBuffer strBuf = new StringBuffer("3D ");
            if (J3dDebug.devPhase) {
                strBuf.append("[dev] ");
            }
            strBuf.append(VersionInfo.getVersion());
            String str = strBuf.toString();
            if (isLoggableConfig) {
                logger.config(str);
            } else {
                System.err.println(str);
                System.err.println();
            }
        }

        // Print out debugging information for debug builds
        if (isLoggableConfig || VersionInfo.isDebug) {
            StringBuffer strBuf = new StringBuffer();
            strBuf.append("Initializing 3D runtime system:\n").append("    version = ")
                    .append(VersionInfo.getVersion()).append("\n").append("    vendor = ")
                    .append(VersionInfo.getVendor()).append("\n").append("    specification.version = ")
                    .append(VersionInfo.getSpecificationVersion()).append("\n")
                    .append("    specification.vendor = ").append(VersionInfo.getSpecificationVendor());
            String str = strBuf.toString();
            if (isLoggableConfig) {
                logger.config(str);
            } else {
                System.err.println(str);
                System.err.println();
            }
        }

        // Load the native libraries and create the static
        // MasterControl object
        MasterControl.loadLibraries();
        mc = new MasterControl();

        // Print out debugging information for debug builds
        if (isLoggableConfig || VersionInfo.isDebug) {
            StringBuffer strBuf = new StringBuffer();
            strBuf.append("3D system initialized\n").append("    rendering pipeline = ")
                    .append(Pipeline.getPipeline().getPipelineName());
            String str = strBuf.toString();
            if (isLoggableConfig) {
                logger.config(str);
            } else {
                System.err.println(str);
                System.err.println();
            }
        }
    }

    /**
     * Adds a locale at the end of list of locales
     * @param locale the locale to be added
     */
    void addLocale(Locale locale) {
        listOfLocales.addElement(locale);
    }

    /**
     * Removes a Locale and its associates branch graphs from this
     * universe.  All branch graphs within the specified Locale are
     * detached, regardless of whether their ALLOW_DETACH capability
     * bits are set.  The Locale is then marked as being dead: no
     * branch graphs may subsequently be attached.
     *
     * @param locale the Locale to be removed.
     *
     * @exception IllegalArgumentException if the specified Locale is not
     * attached to this VirtualUniverse.
     *
     * @since Java 3D 1.2
     */
    public void removeLocale(Locale locale) {
        if (locale.getVirtualUniverse() != this) {
            throw new IllegalArgumentException(J3dI18N.getString("VirtualUniverse0"));
        }

        listOfLocales.removeElement(locale);
        locale.removeFromUniverse();
        if (isEmpty()) {
            VirtualUniverse.mc.postRequest(MasterControl.EMPTY_UNIVERSE, this);
        }
        setLiveState.reset(null);
    }

    /**
     * Removes all Locales and their associates branch graphs from
     * this universe.  All branch graphs within each Locale are
     * detached, regardless of whether their ALLOW_DETACH capability
     * bits are set.  Each Locale is then marked as being dead: no
     * branch graphs may subsequently be attached.  This method
     * should be called by applications and applets to allow
     * Java 3D to cleanup its resources.
     *
     * @since Java 3D 1.2
     */
    public void removeAllLocales() {
        // NOTE: this is safe because Locale.removeFromUniverse does not
        // remove the Locale from the listOfLocales
        int i;

        for (i = listOfLocales.size() - 1; i > 0; i--) {
            listOfLocales.get(i).removeFromUniverse();
        }

        if (i >= 0) {
            // We have to clear() the listOfLocales first before
            // invoke the last removeFromUniverse() so that isEmpty()
            // (call from View.deactivate() ) will return true and
            // threads can destroy from MC.
            Locale loc = listOfLocales.get(0);
            listOfLocales.clear();
            loc.removeFromUniverse();
        }
        VirtualUniverse.mc.postRequest(MasterControl.EMPTY_UNIVERSE, this);

        setLiveState.reset(null);
    }

    /**
     * Returns the enumeration object of all locales in this virtual universe.
     *
     * @return the enumeration object
     */
    public Enumeration<Locale> getAllLocales() {
        return this.listOfLocales.elements();
    }

    /**
     * Returns the number of locales.
     * @return the count of locales
     */
    public int numLocales() {
        return this.listOfLocales.size();
    }

    /**
     * Sets the priority of all Java 3D threads to the specified
     * value.  The default value is the priority of the thread that
     * started Java 3D.
     *
     * @param priority the new thread priority
     *
     * @exception IllegalArgumentException if the priority is not in
     * the range MIN_PRIORITY to MAX_PRIORITY
     *
     * @exception SecurityException if the priority is greater than
     * that of the calling thread
     *
     * @since Java 3D 1.2
     */
    public static void setJ3DThreadPriority(int priority) {
        if (priority > Thread.MAX_PRIORITY) {
            priority = Thread.MAX_PRIORITY;
        } else if (priority < Thread.MIN_PRIORITY) {
            priority = Thread.MIN_PRIORITY;
        }
        VirtualUniverse.mc.setThreadPriority(priority);
    }

    /**
     * Retrieves that priority of Java 3D's threads.
     *
     * @return the current priority of Java 3D's threads
     *
     * @since Java 3D 1.2
     */
    public static int getJ3DThreadPriority() {
        return VirtualUniverse.mc.getThreadPriority();
    }

    /**
     * Returns a read-only Map object containing key-value pairs that
     * define various global properties for Java 3D.  All of the keys
     * are String objects.  The values are key-specific, but most will
     * be String objects.
     *
     * <p>
     * The set of global Java 3D properties always includes values for
     * the following keys:
     *
     * <p>
     * <ul>
     * <table BORDER=1 CELLSPACING=1 CELLPADDING=1>
     * <tr>
     * <td><b>Key (String)</b></td>
     * <td><b>Value Type</b></td>
     * </tr>
     * <tr>
     * <td><code>j3d.version</code></td>
     * <td>String</td>
     * </tr>
     * <tr>
     * <td><code>j3d.vendor</code></td>
     * <td>String</td>
     * </tr>
     * <tr>
     * <td><code>j3d.specification.version</code></td>
     * <td>String</td>
     * </tr>
     * <tr>
     * <td><code>j3d.specification.vendor</code></td>
     * <td>String</td>
     * </tr>
     * <tr>
     * <td><code>j3d.pipeline</code></td>
     * <td>String</td>
     * </tr>
     * <tr>
     * <td><code>j3d.renderer</code></td>
     * <td>String</td>
     * </tr>
     * </table>
     * </ul>
     *
     * <p>
     * The descriptions of the values returned for each key are as follows:
     *
     * <p>
     * <ul>
     *
     * <li>
     * <code>j3d.version</code>
     * <ul>
     * A String that defines the Java 3D implementation version.
     * The portion of the implementation version string before the first
     * space must adhere to one of the the following three formats
     * (anything after the first space is an optional free-form addendum
     * to the version):
     * <ul>
     * <i>x</i>.<i>y</i>.<i>z</i><br>
     * <i>x</i>.<i>y</i>.<i>z</i>_<i>p</i><br>
     * <i>x</i>.<i>y</i>.<i>z</i>-<i>ssss</i><br>
     * </ul>
     * where:
     * <ul>
     * <i>x</i> is the major version number<br>
     * <i>y</i> is the minor version number<br>
     * <i>z</i> is the sub-minor version number<br>
     * <i>p</i> is the patch revision number <br>
     * <i>ssss</i> is a string, identifying a non-release build
     * (e.g., beta1, build47, rc1, etc.).  It may only
     * contain letters, numbers, periods, dashes, or
     * underscores.
     * </ul>
     * </ul>
     * </li>
     * <p>
     *
     * <li>
     * <code>j3d.vendor</code>
     * <ul>
     * String that specifies the Java 3D implementation vendor.
     * </ul>
     * </li>
     * <p>
     *
     * <li>
     * <code>j3d.specification.version</code>
     * <ul>
     * A String that defines the Java 3D specification version.
     * This string must be of the following form:
     * <ul>
     * <i>x</i>.<i>y</i>
     * </ul>
     * where:
     * <ul>
     * <i>x</i> is the major version number<br>
     * <i>y</i> is the minor version number<br>
     * </ul>
     * No other characters are allowed in the specification version string.
     * </ul>
     * </li>
     * <p>
     *
     * <li>
     * <code>j3d.specification.vendor</code>
     * <ul>
     * String that specifies the Java 3D specification vendor.
     * </ul>
     * </li>
     * <p>
     *
     * <li>
     * <code>j3d.pipeline</code>
     * <ul>
     * String that specifies the Java 3D rendering pipeline. This could
     * be one of: "NATIVE_OGL", "NATIVE_D3D", or "JOGL". Others could be
     * added in the future.
     * </ul>
     * </li>
     * <p>
     *
     * <li>
     * <code>j3d.renderer</code>
     * <ul>
     * String that specifies the underlying rendering library.  This could
     * be one of: "OpenGL" or "DirectX". Others could be added in the future.
     * </ul>
     * </li>
     * <p>
     *
     * </ul>
     *
     * @return the global Java 3D properties
     *
     * @since Java 3D 1.3
     */
    public static final Map getProperties() {
        if (properties == null) {
            // Create lists of keys and values
            ArrayList<String> keys = new ArrayList<String>();
            ArrayList<Object> values = new ArrayList<Object>();

            // Implementation version string is obtained from the
            // ImplementationVersion class.
            keys.add("j3d.version");
            values.add(VersionInfo.getVersion());

            keys.add("j3d.vendor");
            values.add(VersionInfo.getVendor());

            keys.add("j3d.specification.version");
            values.add(VersionInfo.getSpecificationVersion());

            keys.add("j3d.specification.vendor");
            values.add(VersionInfo.getSpecificationVendor());

            keys.add("j3d.renderer");
            values.add(Pipeline.getPipeline().getRendererName());

            keys.add("j3d.pipeline");
            values.add(Pipeline.getPipeline().getPipelineName());

            // Now Create read-only properties object
            properties = new J3dQueryProps(keys, values);
        }
        return properties;
    }

    /**
     * This returns the next available nodeId as a string.
     */
    // XXXX: reuse of id's imply a slight collision problem in the
    // render queue's.
    // BUG 4181362
    String getNodeId() {
        String str;

        if (nodeIdFreeList.size() == 0) {
            str = Long.toString(nodeIdCount);
            nodeIdCount++;
        } else {
            // Issue 496: Remove last object using index to avoid performance
            // hit of a needless linear search.
            int idx = nodeIdFreeList.size() - 1;
            str = nodeIdFreeList.remove(idx);
        }
        return (str);
    }

    /**
     * This returns the next available viewId
     */
    Integer getViewId() {
        Integer id;
        int size;

        synchronized (viewIdFreeList) {
            size = viewIdFreeList.size();
            if (size == 0) {
                id = new Integer(viewIdCount++);
            } else {
                id = viewIdFreeList.remove(size - 1);
            }
        }
        return (id);
    }

    /**
     * This returns a viewId to the freelist
     */
    void addViewIdToFreeList(Integer viewId) {
        synchronized (viewIdFreeList) {
            viewIdFreeList.add(viewId);
        }
    }

    void addViewPlatform(ViewPlatformRetained vp) {
        synchronized (viewPlatforms) {
            vpChanged = true;
            viewPlatforms.add(vp);
        }
    }

    void removeViewPlatform(ViewPlatformRetained vp) {
        synchronized (viewPlatforms) {
            if (viewPlatforms.remove(vp))
                vpChanged = true;
        }
    }

    ViewPlatformRetained[] getViewPlatformList() {
        synchronized (viewPlatforms) {
            if (vpChanged) {
                viewPlatformList = viewPlatforms.toArray(new ViewPlatformRetained[viewPlatforms.size()]);
                vpChanged = false;
            }

            return viewPlatformList;
        }
    }

    void checkForEnableEvents() {
        if (enableFocus) {
            enableFocusEvents();
        }
        if (enableKey) {
            enableKeyEvents();
        }
        if (enableMouse) {
            enableMouseEvents();
        }
        if (enableMouseMotion) {
            enableMouseMotionEvents();
        }
        if (enableMouseWheel) {
            enableMouseWheelEvents();
        }

    }

    void disableFocusEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableFocus = false;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.disableFocusEvents();
                }
            }
        }
    }

    void enableFocusEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableFocus = true;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.enableFocusEvents();
                }
            }
        }
    }

    void disableKeyEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableKey = false;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.disableKeyEvents();
                }
            }
        }
    }

    void enableKeyEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableKey = true;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.enableKeyEvents();
                }
            }
        }
    }

    void disableMouseEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableMouse = false;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.disableMouseEvents();
                }
            }
        }
    }

    void enableMouseEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableMouse = true;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.enableMouseEvents();
                }
            }
        }
    }

    void disableMouseMotionEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableMouseMotion = false;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.disableMouseMotionEvents();
                }
            }
        }
    }

    void enableMouseMotionEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableMouseMotion = true;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.enableMouseMotionEvents();
                }
            }
        }
    }

    void disableMouseWheelEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableMouseWheel = false;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.disableMouseWheelEvents();
                }
            }
        }
    }

    void enableMouseWheelEvents() {
        ViewPlatformRetained[] vps = getViewPlatformList();
        enableMouseWheel = true;

        for (int i = 0; i < vps.length; i++) {
            View[] views = vps[i].getViewList();
            for (int j = views.length - 1; j >= 0; j--) {
                Enumeration<Canvas3D> cvs = views[j].getAllCanvas3Ds();
                while (cvs.hasMoreElements()) {
                    Canvas3D cv = cvs.nextElement();
                    // offscreen canvas does not have event catcher
                    if (cv.eventCatcher != null)
                        cv.eventCatcher.enableMouseWheelEvents();
                }
            }
        }
    }

    /**
     * Sets the "current" view (during view activation) for this virtual
     * universe.
     * @param last activated view
     */
    final void setCurrentView(View view) {
        this.currentView = view;
    }

    /**
     * Returns the "current" view (the last view activated for this virtual
     * universe.
     * @return last activated view
     */
    final View getCurrentView() {
        return this.currentView;
    }

    /**
     * Method to return the root thread group.  This must be called from
     * within a doPrivileged block.
     */
    static ThreadGroup getRootThreadGroup() {
        return rootThreadGroup;
    }

    /**
     * return true if all Locales under it don't have branchGroup
     * attach to it.
     */
    boolean isEmpty() {
        Enumeration<Locale> elm = listOfLocales.elements();

        while (elm.hasMoreElements()) {
            Locale loc = elm.nextElement();
            if (!loc.branchGroups.isEmpty()) {
                return false;
            }
        }
        return true;
    }

    void resetWaitMCFlag() {
        synchronized (waitLock) {
            regViewWaiting = null;
            unRegViewWaiting = null;
            isSceneGraphLock = true;
        }
    }

    void waitForMC() {
        synchronized (waitLock) {
            if (unRegViewWaiting != null) {
                if ((regViewWaiting == null) || (regViewWaiting != unRegViewWaiting)) {
                    while (!unRegViewWaiting.doneUnregister) {
                        MasterControl.threadYield();
                    }
                    unRegViewWaiting.doneUnregister = false;
                    unRegViewWaiting = null;
                }
            }

            if (regViewWaiting != null) {
                while (!VirtualUniverse.mc.isRegistered(regViewWaiting)) {
                    MasterControl.threadYield();
                }
                regViewWaiting = null;
            }
            isSceneGraphLock = false;
        }
    }

    /**
     * Adds the specified GraphStructureChangeListener to the set of listeners
     * that will be notified when the graph structure is changed on a live
     * scene graph. If the specifed listener is null no action is taken and no
     * exception is thrown.
     *
     * @param listener the listener to add to the set.
     *
     * @since Java 3D 1.4
     */
    public void addGraphStructureChangeListener(GraphStructureChangeListener listener) {
        if (listener == null) {
            return;
        }

        if (structureChangeListenerSet == null) {
            structureChangeListenerSet = new HashSet<GraphStructureChangeListener>();
        }

        synchronized (structureChangeListenerSet) {
            structureChangeListenerSet.add(listener);
        }
    }

    /**
     * Removes the specified GraphStructureChangeListener from the set of listeners. This
     * method performs no function, nor does it throw an exception if the specified listener
     * is not currently in the set or is null.
     *
     * @param listener the listener to remove from the set.
     *
     * @since Java 3D 1.4
     */
    public void removeGraphStructureChangeListener(GraphStructureChangeListener listener) {
        if (structureChangeListenerSet == null) {
            return;
        }

        synchronized (structureChangeListenerSet) {
            structureChangeListenerSet.remove(listener);
        }
    }

    /**
     * Processes all live BranchGroup add and removes and notifies
     * any registered listeners. Used for add and remove
     */
    void notifyStructureChangeListeners(boolean add, Object parent, BranchGroup child) {
        if (structureChangeListenerSet == null) {
            return;
        }

        synchronized (structureChangeListenerSet) {
            Iterator<GraphStructureChangeListener> it = structureChangeListenerSet.iterator();
            while (it.hasNext()) {
                GraphStructureChangeListener listener = it.next();
                try {
                    if (add) {
                        listener.branchGroupAdded(parent, child);
                    } else {
                        listener.branchGroupRemoved(parent, child);
                    }
                } catch (RuntimeException e) {
                    System.err.println("Exception occurred in GraphStructureChangeListener:");
                    e.printStackTrace();
                } catch (Error e) {
                    // Issue 264 - catch Error
                    System.err.println("Error occurred in GraphStructureChangeListener:");
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Processes all live BranchGroup moves and notifies
     * any registered listeners. Used for moveTo
     */
    void notifyStructureChangeListeners(Object oldParent, Object newParent, BranchGroup child) {
        if (structureChangeListenerSet == null) {
            return;
        }

        synchronized (structureChangeListenerSet) {
            Iterator<GraphStructureChangeListener> it = structureChangeListenerSet.iterator();
            while (it.hasNext()) {
                GraphStructureChangeListener listener = it.next();
                try {
                    listener.branchGroupMoved(oldParent, newParent, child);
                } catch (RuntimeException e) {
                    System.err.println("Exception occurred in GraphStructureChangeListener:");
                    e.printStackTrace();
                } catch (Error e) {
                    // Issue 264 - catch Error
                    System.err.println("Error occurred in GraphStructureChangeListener:");
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Adds the specified ShaderErrorListener to the set of listeners
     * that will be notified when a programmable shader error is
     * detected on a live scene graph. If the specifed listener is
     * null no action is taken and no exception is thrown.
     * If a shader error occurs, the listeners will be called
     * asynchronously from a separate notification thread. The Java 3D
     * renderer and behavior scheduler will continue to run as if the
     * error had not occurred, except that shading will be disabled
     * for the objects in error. If applications desire to detach or
     * modify the scene graph as a result of the error, they should
     * use a behavior post if they want that change to be
     * synchronous with the renderer.
     *
     * @param listener the listener to add to the set.
     *
     * @since Java 3D 1.4
     */
    public void addShaderErrorListener(ShaderErrorListener listener) {
        if (listener == null) {
            return;
        }

        if (shaderErrorListenerSet == null) {
            shaderErrorListenerSet = new HashSet<ShaderErrorListener>();
        }

        synchronized (shaderErrorListenerSet) {
            shaderErrorListenerSet.add(listener);
        }
    }

    /**
     * Removes the specified ShaderErrorListener from the set of
     * listeners. This method performs no function, nor does it throw
     * an exception if the specified listener is not currently in the
     * set or is null.
     *
     * @param listener the listener to remove from the set.
     *
     * @since Java 3D 1.4
     */
    public void removeShaderErrorListener(ShaderErrorListener listener) {
        if (shaderErrorListenerSet == null) {
            return;
        }

        synchronized (shaderErrorListenerSet) {
            shaderErrorListenerSet.remove(listener);
        }
    }

    /**
     * Notifies all listeners of a shader error. If no listeners exist, a default
     * listener is notified.
     */
    void notifyShaderErrorListeners(ShaderError error) {
        boolean errorReported = false;

        // Notify all error listeners in the set
        if (shaderErrorListenerSet != null) {
            synchronized (shaderErrorListenerSet) {
                Iterator<ShaderErrorListener> it = shaderErrorListenerSet.iterator();
                while (it.hasNext()) {
                    ShaderErrorListener listener = it.next();
                    try {
                        listener.errorOccurred(error);
                    } catch (RuntimeException e) {
                        System.err.println("Exception occurred in ShaderErrorListener:");
                        e.printStackTrace();
                    } catch (Error e) {
                        // Issue 264 - catch Error
                        System.err.println("Error occurred in ShaderErrorListener:");
                        e.printStackTrace();
                    }
                    errorReported = true;
                }
            }
        }

        // Notify the default error listener if the set is null or empty
        if (!errorReported) {
            defaultShaderErrorListener.errorOccurred(error);
        }
    }

    // Issue 260 : rendering error listeners.

    /**
     * Adds the specified RenderingErrorListener to the set of listeners
     * that will be notified when a rendering error is detected.
     * If the specifed listener is null no action is taken and no exception
     * is thrown.
     * If a rendering error occurs, the listeners will be called
     * asynchronously from a separate notification thread.  If the set
     * of listeners is empty, a default listener is notified. The
     * default listener prints the error information to System.err and
     * then calls System.exit().
     *
     * @param listener the listener to add to the set.
     *
     * @since Java 3D 1.5
     */
    public static void addRenderingErrorListener(RenderingErrorListener listener) {
        if (listener == null) {
            return;
        }

        if (renderingErrorListenerSet == null) {
            renderingErrorListenerSet = new HashSet<RenderingErrorListener>();
        }

        synchronized (renderingErrorListenerSet) {
            renderingErrorListenerSet.add(listener);
        }
    }

    /**
     * Removes the specified RenderingErrorListener from the set of
     * listeners. This method performs no function, nor does it throw
     * an exception if the specified listener is not currently in the
     * set or is null.
     *
     * @param listener the listener to remove from the set.
     *
     * @since Java 3D 1.5
     */
    public static void removeRenderingErrorListener(RenderingErrorListener listener) {
        if (renderingErrorListenerSet == null) {
            return;
        }

        synchronized (renderingErrorListenerSet) {
            renderingErrorListenerSet.remove(listener);
        }
    }

    /**
     * Notifies all listeners of a rendering error. If no listeners exist,
     * a default listener is notified.
     */
    static void notifyRenderingErrorListeners(RenderingError error) {
        boolean errorReported = false;

        // Notify all error listeners in the set
        if (renderingErrorListenerSet != null) {
            synchronized (renderingErrorListenerSet) {
                Iterator<RenderingErrorListener> it = renderingErrorListenerSet.iterator();
                while (it.hasNext()) {
                    RenderingErrorListener listener = it.next();
                    try {
                        listener.errorOccurred(error);
                    } catch (RuntimeException e) {
                        System.err.println("Exception occurred in RenderingErrorListener:");
                        e.printStackTrace();
                    } catch (Error e) {
                        // Issue 264 - catch Error
                        System.err.println("Error occurred in RenderingErrorListener:");
                        e.printStackTrace();
                    }
                    errorReported = true;
                }
            }
        }

        // Notify the default error listener if the set is null or empty
        if (!errorReported) {
            defaultRenderingErrorListener.errorOccurred(error);
        }
    }

}