All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.media.j3d.GroupRetained Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 1996-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.Vector;

/**
 * Group node.
 */

class GroupRetained extends NodeRetained implements BHLeafInterface {
/**
 * The Group Node's children vector.
 */
ArrayList children = new ArrayList(1);

    /**
     * The Group node's collision bounds in local coordinates.
     */
    Bounds collisionBound = null;

    // The locale that this node is decended from
    Locale locale = null;

// The list of lights that are scoped to this node
// One such arraylist per path. If not in sharedGroup
// then only index 0 is valid
ArrayList> lights = null;

// The list of fogs that are scoped to this node
// One such arraylist per path. If not in sharedGroup
// then only index 0 is valid
ArrayList> fogs = null;

// The list of model clips that are scoped to this node
// One such arraylist per path. If not in sharedGroup
// then only index 0 is valid
ArrayList> modelClips = null;

// The list of alternateappearance that are scoped to this node
// One such arraylist per path. If not in sharedGroup
// then only index 0 is valid
ArrayList> altAppearances = null;


    // indicates whether this Group node can be the target of a collision
    boolean collisionTarget = false;

// per child switchLinks
ArrayList> childrenSwitchLinks = null;

    // the immediate childIndex of a parentSwitchLink
    int parentSwitchLinkChildIndex = -1;

// per shared path ordered path data
ArrayList orderedPaths = null;

    /**
     * If collisionBound is set, this is equal to the
     * transformed collisionBounds, otherwise it is equal
     * to the transformed localBounds.
     * This variable is set to null unless collisionTarget = true.
     * This bound is only used by mirror Group.
     */
    BoundingBox collisionVwcBounds;

/**
 * Mirror group of this node, it is only used when
 * collisionTarget = true. Otherwise it is set to null.
 * If not in shared group,
 * only entry 0 is used.
 *
 */
ArrayList mirrorGroup;

    /**
     * key of mirror GroupRetained.
     */
    HashKey key;

    /**
     * sourceNode of this mirror Group
     */
    GroupRetained sourceNode;

    /**
     * The BHLeafNode for this GeometryAtom.
     */
    BHLeafNode bhLeafNode = null;

    //
    // The following variables are used during compile
    //

    // true if this is the root of the scenegraph tree
    boolean isRoot = false;

    boolean allocatedLights = false;

    boolean allocatedFogs = false;

    boolean allocatedMclips = false;

    boolean allocatedAltApps = false;

    // > 0 if this group is being used in scoping
    int scopingRefCount = 0;


ArrayList compiledChildrenList = null;

    boolean isInClearLive = false;

// List of viewes scoped to this Group, for all subclasses
// of group, except ViewSpecificGroup its a pointer to closest
// ViewSpecificGroup parent
// viewList for this node, if inSharedGroup is
// false then only viewList(0) is valid
// For VSGs, this list is an intersection of
// higher level VSGs
ArrayList> viewLists = null;

    // True if this Node is descendent of ViewSpecificGroup;
    boolean inViewSpecificGroup = false;

    GroupRetained() {
        this.nodeType = NodeRetained.GROUP;
        // issue 544
        if (VirtualUniverse.mc.useBoxForGroupBounds) {
            localBounds = new BoundingBox((Bounds) null);
        } else {
		localBounds = new BoundingSphere((Bounds)null);
        }
    }

    /**
     * Sets the collision bounds of a node.
     * @param bounds the bounding object for the node
     */
    void setCollisionBounds(Bounds bounds) {
        if (bounds == null) {
	    this.collisionBound = null;
	} else {
	    this.collisionBound = (Bounds)bounds.clone();
	}

	if (source.isLive()) {
	    J3dMessage message = new J3dMessage();
	    message.type = J3dMessage.COLLISION_BOUND_CHANGED;
            message.threads = J3dThread.UPDATE_TRANSFORM |
		J3dThread.UPDATE_GEOMETRY;
	    message.universe = universe;
	    message.args[0] = this;
	    VirtualUniverse.mc.processMessage(message);
	}

    }


    /**
     * Gets the collision bounds of a node.
     * @return the node's bounding object
     */
    Bounds getCollisionBounds() {
	return (collisionBound == null ? null : (Bounds)collisionBound.clone());
    }

    /**
     * Replaces the specified child with the child provided.
     * @param child the new child
     * @param index which child to replace
     */
    void setChild(Node child, int index) {

	checkValidChild(child, "GroupRetained0");
	if (this.source.isLive()) {
	    universe.resetWaitMCFlag();
	    synchronized (universe.sceneGraphLock) {
	        doSetChild(child, index);
		universe.setLiveState.clear();
	    }
	    universe.waitForMC();

	} else {
	    doSetChild(child, index);
	    if (universe != null) {
		synchronized (universe.sceneGraphLock) {
		    universe.setLiveState.clear();
		}
	    }
	}
        dirtyBoundsCache();
    }

    // The method that does the work once the lock is acquired.
    void doSetChild(Node child, int index) {
	J3dMessage[] messages = null;
	int numMessages = 0;
	int attachStartIndex = 0;


	// since we want to make sure the replacement of the child
        // including removal of the oldChild and insertion of the newChild
	// all happen in the same frame, we'll send all the necessary
	// messages to masterControl for processing in one call.
	// So let's first find out how many messages will be sent

	NodeRetained oldchildr = children.get(index);

	if (this.source.isLive()) {
	    if (oldchildr != null) {
	        numMessages+=3;		// REMOVE_NODES, ORDERED_GROUP_REMOVED
		                        // VIEWSPECIFICGROUP_CLEAR
	        attachStartIndex = 3;
	    }

	    if (child != null) {
	        numMessages+=4;	// INSERT_NODES,BEHAVIOR_ACTIVATE,ORDERED_GROUP_INSERTED,
		                // VIEWSPECIFICGROUP_INIT
	    }

	    messages = new J3dMessage[numMessages];
	    for (int i = 0; i < numMessages; i++) {
	         messages[i] = new J3dMessage();
	    }
	}

	if(oldchildr != null) {
	    oldchildr.setParent(null);
	    checkClearLive(oldchildr, messages, 0, index, null);
            if (this.source.isLive()) {
                universe.notifyStructureChangeListeners(false, this.source, (BranchGroup)oldchildr.source);
            }
	}
	removeChildrenData(index);

	if(child == null) {
	    children.set(index, null);
	    if (messages != null) {
	        VirtualUniverse.mc.processMessage(messages);
	    }
	    return;
	}

        if (this.source.isLive()) {
            universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child);
        }
	NodeRetained childr = (NodeRetained) child.retained;
	childr.setParent(this);
	children.set(index, childr);


	insertChildrenData(index);
	checkSetLive(childr, index, messages, attachStartIndex, null);
	if (this.source.isLive()) {
	    ((BranchGroupRetained)childr).isNew = true;
	}

	if (messages != null) {
	    VirtualUniverse.mc.processMessage(messages);
	}
    }

    /**
     * Inserts the specified child at specified index.
     * @param child the new child
     * @param index position to insert new child at
     */
    void insertChild(Node child, int index) {

	checkValidChild(child, "GroupRetained1");
	if (this.source.isLive()) {
	    universe.resetWaitMCFlag();
	    synchronized (universe.sceneGraphLock) {
                universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child);
	        doInsertChild(child, index);
		universe.setLiveState.clear();
	    }
	    universe.waitForMC();
	} else {
	    doInsertChild(child, index);
	    if (universe != null) {
		synchronized (universe.sceneGraphLock) {
		    universe.setLiveState.clear();
		}
	    }
	}
        dirtyBoundsCache();
    }

    // The method that does the work once the lock is acquired.
    void doInsertChild(Node child, int index) {
	int i;

	insertChildrenData(index);
	for (i=index; i= 0)
	    removeChild(i);
    }

    void removeAllChildren() {
	int n = children.size();
	for(int i = n-1; i >= 0; i--) {
	    removeChild(i);
	}
    }


    // The method that does the work once the lock is acquired.
    void doRemoveChild(int index, J3dMessage messages[], int messageIndex) {
	NodeRetained oldchildr = children.get(index);

	int size = children.size();
	for (int i = index; i < size; i++) {
		NodeRetained child = children.get(i);
	    if(child != null)
		child.childIndex--;
	}

	if(oldchildr != null) {
	    oldchildr.setParent(null);
	    checkClearLive(oldchildr, messages, messageIndex, index, null);
	}

	children.remove(index);
	removeChildrenData(index);

        if (nodeType == NodeRetained.SWITCH) {
	    // force reEvaluation of switch children
	    SwitchRetained sg = (SwitchRetained)this;
	    sg.setWhichChild(sg.whichChild, true);
        }

    }

	/**
	 * Returns the child specified by the index.
	 * @param index which child to return
	 * @return the children at location index
	 */
	Node getChild(int index) {
		NodeRetained sgo = children.get(index);
		if (sgo == null)
			return null;
		else
			return (Node)sgo.source;
	}

	/**
	 * Returns an enumeration object of the children.
	 * @return an enumeration object of the children
	 */
	Enumeration getAllChildren() {
		Vector userChildren = new Vector(children.size());

		for (int i = 0; i < children.size(); i++) {
			NodeRetained sgo = children.get(i);
			if (sgo != null)
				userChildren.add((Node)sgo.source);
			else
				userChildren.add(null);
		}

		return userChildren.elements();
	}

    void checkValidChild(Node child, String s) {

	if ((child != null) &&
            (((child instanceof BranchGroup) &&
	      (((BranchGroupRetained) child.retained).attachedToLocale)) ||
	     (((NodeRetained)child.retained).parent != null))) {
		throw new MultipleParentException(J3dI18N.getString(s));
	}
    }

    /**
     * Appends the specified child to this node's list of children.
     * @param child the child to add to this node's list of children
     */
    void addChild(Node child) {
	checkValidChild(child, "GroupRetained2");

	if (this.source.isLive()) {
	    universe.resetWaitMCFlag();
	    synchronized (universe.sceneGraphLock) {
                universe.notifyStructureChangeListeners(true, this.source, (BranchGroup)child);
	        doAddChild(child, null, 0);
		universe.setLiveState.clear();
	    }
	    universe.waitForMC();
	} else {
	    doAddChild(child, null, 0);
	    if (universe != null) {
		synchronized (universe.sceneGraphLock) {
		    universe.setLiveState.clear();
		}
	    }
	}
        dirtyBoundsCache();
    }

    // The method that does the work once the lock is acquired.
    void doAddChild(Node child, J3dMessage messages[], int messageIndex) {

	appendChildrenData();

	if(child == null) {
	    children.add(null);
	    return;
	}

	NodeRetained childr = (NodeRetained) child.retained;
	childr.setParent(this);
	children.add(childr);
	checkSetLive(childr, children.size()-1, messages, messageIndex, null);
	if (this.source.isLive()) {
	    ((BranchGroupRetained)childr).isNew = true;
	}

    }

    void moveTo(BranchGroup bg) {
        if (bg != null) {
            ((GroupRetained)bg.retained).dirtyBoundsCache();
        }
	if (this.source.isLive()) {
	    universe.resetWaitMCFlag();
	    synchronized (universe.sceneGraphLock) {
                GroupRetained oldParent = (GroupRetained)((BranchGroupRetained)bg.retained).parent;
	        doMoveTo(bg);
		universe.setLiveState.clear();
                if (oldParent==null)
                    universe.notifyStructureChangeListeners(((BranchGroupRetained)bg.retained).locale, this.source, bg);
                else
                    universe.notifyStructureChangeListeners(oldParent.source, this.source, bg);
	    }
	    universe.waitForMC();
	} else {
	    doMoveTo(bg);
	    if (universe != null) {
		synchronized (universe.sceneGraphLock) {
		    universe.setLiveState.clear();
		}
	    }
	}
        dirtyBoundsCache();
    }

    // The method that does the work once the lock is acquired.
    void doMoveTo(BranchGroup branchGroup) {
	J3dMessage messages[] = null;
	int numMessages = 0;
	int detachStartIndex = 0;
	int attachStartIndex = 0;
	if(branchGroup != null) {
	    BranchGroupRetained bg = (BranchGroupRetained) branchGroup.retained;
	    GroupRetained g = (GroupRetained)bg.parent;

	    // Find out how many messages to be created
	    // Note that g can be NULL if branchGroup parent is
	    // a Locale, in this case the following condition
	    // will fail.
	    // Figure out the number of messages based on whether the group
	    // from which its moving from is live and group to which its
	    // moving to is live
	    if (g != null) {
		if (g.source.isLive()) {
		    numMessages = 3; // REMOVE_NODES, ORDERED_GROUP_REMOVED,VIEWSPECIFICGROUP_CLEAR
		    attachStartIndex = 3;
		}
		else {
		    numMessages = 0;
		    attachStartIndex = 0;
		}

	    }
	    else { // Attached to locale
		numMessages = 3; // REMOVE_NODES, ORDERED_GROUP_REMOVED, VIEWSPECIFICGROUP_CLEAR
		attachStartIndex = 3;
	    }
	    // Now, do the evaluation for the group that its going to be
	    // attached to ..
	    if (this.source.isLive()) {
		numMessages+=4;		// INSERT_NODES, BEHAVIOR_ACTIVATE
		                        // ORDERED_GROUP_INSERTED, VIEWSPECIFICGROUP_INIT

	    }
	    messages = new J3dMessage[numMessages];
	    for (int i=0; i 0) {
	    int count = 0;
	    for (int i=0; i < numMessages; i++) {
		if (messages[i].type != J3dMessage.INVALID_TYPE) {
		    count++;
		}
	    }
	    if (count == numMessages) {
		// in most cases
		VirtualUniverse.mc.processMessage(messages);
	    } else {
		J3dMessage ms[] = null;

		if (count > 0) {
		    ms = new J3dMessage[count];
		}

		int k=0;
		for (int i=0; i < numMessages; i++) {
		    if (messages[i].type != J3dMessage.INVALID_TYPE) {
			ms[k++] = messages[i];
		    }
		}
		if (ms != null) {
		    VirtualUniverse.mc.processMessage(ms);
		}
	    }
	}
    }


    /**
     * Returns a count of this nodes' children.
     * @return the number of children descendant from this node
     */
    int numChildren() {
	return children.size();
    }

    // Remove a light from the list of lights
    void removeLight(int numLgt, LightRetained[] removelight, HashKey key) {
	int index;
	if (inSharedGroup) {
	    int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
		ArrayList l = lights.get(hkIndex);
	    if (l != null) {
		for (int i = 0; i < numLgt; i++) {
		    index = l.indexOf(removelight[i]);
		    l.remove(index);
		}
	    }
	}
	else {
		ArrayList l = lights.get(0);
	    for (int i = 0; i < numLgt; i++) {
		index = l.indexOf(removelight[i]);
		l.remove(index);
	    }
	}

	/*
	// XXXX: lights may remove twice or more during clearLive(),
	// one from itself and one call from every LightRetained
	// reference this.  So there is case that this procedure get
	// called when light already removed.
	if (i >= 0)
	    lights.remove(i);
	*/
    }


    void addAllNodesForScopedLight(int numLgts,
				   LightRetained[] ml,
				   ArrayList list,
				   HashKey k) {
	if (inSharedGroup) {
	    for (int i = 0; i < localToVworldKeys.length; i++) {
		k.set(localToVworldKeys[i]);
		processAllNodesForScopedLight(numLgts,  ml, list,  k);
	    }
	}
	else {
	    processAllNodesForScopedLight(numLgts, ml,  list,  k);
	}
    }

    void processAllNodesForScopedLight(int numLgts, LightRetained[] ml, ArrayList list, HashKey k) {
	if (allocatedLights) {
	    addLight(ml, numLgts, k);
	}
	if (this.source.isLive() || this.isInSetLive()) {
	    for (int i = children.size()-1; i >=0; i--) {
			NodeRetained child = children.get(i);
		if(child != null) {
		    if (child instanceof GroupRetained  && (child.source.isLive() || child.isInSetLive()))
			((GroupRetained)child).processAllNodesForScopedLight(numLgts, ml, list, k);
		    else if (child instanceof LinkRetained && (child.source.isLive()|| child.isInSetLive())) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			((GroupRetained)(ln.sharedGroup)).
			    processAllNodesForScopedLight(numLgts, ml, list, k.append("+").
						       append(ln.nodeId));
			k.count = lastCount;
		    } else if (child instanceof Shape3DRetained && child.source.isLive()) {
			((Shape3DRetained)child).getMirrorObjects(list, k);
		    } else if (child instanceof MorphRetained && child.source.isLive()) {
			((MorphRetained)child).getMirrorObjects(list, k);
		    }
		}
	    }
	}
    }

    // If its a group, then add the scope to the group, if
    // its a shape, then keep a list to be added during
    // updateMirrorObject
    void removeAllNodesForScopedLight(int numLgts, LightRetained[] ml, ArrayList list, HashKey k) {
	if (inSharedGroup) {
	    for (int i = 0; i < localToVworldKeys.length; i++) {
		k.set(localToVworldKeys[i]);
		processRemoveAllNodesForScopedLight(numLgts, ml,  list,  k);
	    }
	}
	else {
	    processRemoveAllNodesForScopedLight(numLgts, ml,  list,  k);
	}
    }

    void processRemoveAllNodesForScopedLight(int numLgts, LightRetained[] ml, ArrayList list, HashKey k) {
	if (allocatedLights) {
	    removeLight(numLgts,ml, k);
	}
	// If the source is live, then notify the children
	if (this.source.isLive() && !isInClearLive) {
	    for (int i = children.size()-1; i >=0; i--) {
			NodeRetained child = children.get(i);
		if(child != null) {
		    if (child instanceof GroupRetained &&(child.source.isLive() &&
							    ! ((GroupRetained)child).isInClearLive))
			((GroupRetained)child).processRemoveAllNodesForScopedLight(numLgts, ml,list, k);
		    else if (child instanceof LinkRetained && child.source.isLive()) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			((GroupRetained)(ln.sharedGroup)).
			    processRemoveAllNodesForScopedLight(numLgts, ml, list, k.append("+").
							append(ln.nodeId));
			k.count = lastCount;
		    } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
			((Shape3DRetained)child).getMirrorObjects(list, k);
		    } else if (child instanceof MorphRetained && child.source.isLive()) {
			((MorphRetained)child).getMirrorObjects(list,  k);
		    }
		}
	    }
	}
    }


    void addAllNodesForScopedFog(FogRetained mfog, ArrayList list, HashKey k) {
	if (inSharedGroup) {
	    for (int i = 0; i < localToVworldKeys.length; i++) {
		k.set(localToVworldKeys[i]);
		processAddNodesForScopedFog(mfog,  list,  k);
	    }
	}
	else {
	    processAddNodesForScopedFog(mfog,  list,  k);
	}
    }

    void processAddNodesForScopedFog(FogRetained mfog, ArrayList list, HashKey k) {
	// If this group has it own scoping list then add ..
	if (allocatedFogs)
	    addFog(mfog, k);
	// If the source is live, then notify the children
	if (this.source.isLive() || this.isInSetLive()) {
	    for (int i = children.size()-1; i >=0; i--) {
			NodeRetained child = children.get(i);
		if(child != null) {
		    if (child instanceof GroupRetained && (child.source.isLive()|| child.isInSetLive()))
			((GroupRetained)child).processAddNodesForScopedFog(mfog, list, k);
		    else if (child instanceof LinkRetained && (child.source.isLive()||child.isInSetLive() )) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			((GroupRetained)(ln.sharedGroup)).
			    processAddNodesForScopedFog(mfog, list,  k.append("+").
						     append(ln.nodeId));
			k.count = lastCount;
		    } else if (child instanceof Shape3DRetained && child.source.isLive()) {
			((Shape3DRetained)child).getMirrorObjects(list, k);
		    } else if (child instanceof MorphRetained && child.source.isLive()) {
			((MorphRetained)child).getMirrorObjects(list, k);
		    }
		}
	    }
	}
    }

    // If its a group, then add the scope to the group, if
    // its a shape, then keep a list to be added during
    // updateMirrorObject
    void removeAllNodesForScopedFog(FogRetained mfog, ArrayList list, HashKey k) {
	if (inSharedGroup) {
	    for (int i = 0; i < localToVworldKeys.length; i++) {
		k.set(localToVworldKeys[i]);
		processRemoveAllNodesForScopedFog(mfog,  list,  k);
	    }
	}
	else {
	    processRemoveAllNodesForScopedFog(mfog,  list,  k);
	}
    }
    void processRemoveAllNodesForScopedFog(FogRetained mfog, ArrayList list, HashKey k) {
	// If the source is live, then notify the children
	if (allocatedFogs)
	    removeFog(mfog, k);
	if (this.source.isLive() && !isInClearLive) {
	    for (int i = children.size()-1; i >=0; i--) {
			NodeRetained child = children.get(i);
		if(child != null) {
		    if (child instanceof GroupRetained &&(child.source.isLive() &&
							    ! ((GroupRetained)child).isInClearLive))
			((GroupRetained)child).processRemoveAllNodesForScopedFog(mfog, list, k);
		    else if (child instanceof LinkRetained && child.source.isLive()) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			((GroupRetained)(ln.sharedGroup)).
			    processRemoveAllNodesForScopedFog(mfog, list, k.append("+").
							append(ln.nodeId));
			k.count = lastCount;
		    } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
			((Shape3DRetained)child).getMirrorObjects(list, k);
		    } else if (child instanceof MorphRetained && child.source.isLive()) {
			((MorphRetained)child).getMirrorObjects(list,  k);
		    }
		}
	    }
	}
    }

    void addAllNodesForScopedModelClip(ModelClipRetained mModelClip, ArrayList list, HashKey k) {
	if (inSharedGroup) {
	    for (int i = 0; i < localToVworldKeys.length; i++) {
		k.set(localToVworldKeys[i]);
		processAddNodesForScopedModelClip(mModelClip,  list,  k);
	    }
	}
	else {
	    processAddNodesForScopedModelClip(mModelClip,  list,  k);
	}
    }

    void processAddNodesForScopedModelClip(ModelClipRetained mModelClip,
				       ArrayList list,
					HashKey k) {
	if (allocatedMclips)
	    addModelClip(mModelClip, k);
	// If the source is live, then notify the children
	if (this.source.isLive() || this.isInSetLive()) {
	    for (int i = children.size()-1; i >=0; i--) {
			NodeRetained child = children.get(i);
		if(child != null) {
		    if (child instanceof GroupRetained && (child.source.isLive()||child.isInSetLive() ))
			((GroupRetained)child).processAddNodesForScopedModelClip(
									      mModelClip, list,  k);
		    else if (child instanceof LinkRetained && (child.source.isLive()||child.isInSetLive() )) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			((GroupRetained)(ln.sharedGroup)).
			    processAddNodesForScopedModelClip(mModelClip, list,
							   k.append("+").append(ln.nodeId));
			k.count = lastCount;
		    } else if (child instanceof Shape3DRetained && child.source.isLive()) {
			((Shape3DRetained)child).getMirrorObjects(list,  k);
		    } else if (child instanceof MorphRetained && child.source.isLive()) {
			((MorphRetained)child).getMirrorObjects(list, k);
		    }
		}
	    }
	}
    }
    void removeAllNodesForScopedModelClip(ModelClipRetained mModelClip, ArrayList list, HashKey k) {
	if (inSharedGroup) {
	    for (int i = 0; i < localToVworldKeys.length; i++) {
		k.set(localToVworldKeys[i]);
		processRemoveAllNodesForScopedModelClip(mModelClip,  list,  k);
	    }
	}
	else {
	    processRemoveAllNodesForScopedModelClip(mModelClip,  list,  k);
	}

    }

    // If its a group, then add the scope to the group, if
    // its a shape, then keep a list to be added during
    // updateMirrorObject
    void processRemoveAllNodesForScopedModelClip(ModelClipRetained mModelClip, ArrayList list, HashKey k) {
	// If the source is live, then notify the children
	if (allocatedMclips)
	    removeModelClip(mModelClip, k);
	if (this.source.isLive() && !isInClearLive) {
	    for (int i = children.size()-1; i >=0; i--) {
			NodeRetained child = children.get(i);
		if(child != null) {
		    if (child instanceof GroupRetained &&(child.source.isLive() &&
							    ! ((GroupRetained)child).isInClearLive))
			((GroupRetained)child).processRemoveAllNodesForScopedModelClip(mModelClip, list, k);
		    else if (child instanceof LinkRetained && child.source.isLive()) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			((GroupRetained)(ln.sharedGroup)).
			    processRemoveAllNodesForScopedModelClip(mModelClip, list, k.append("+").
							append(ln.nodeId));
			k.count = lastCount;
		    } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
			((Shape3DRetained)child).getMirrorObjects(list, k);
		    } else if (child instanceof MorphRetained && child.source.isLive()) {
			((MorphRetained)child).getMirrorObjects(list,  k);
		    }
		}
	    }
	}
    }

    void addAllNodesForScopedAltApp(AlternateAppearanceRetained mAltApp, ArrayList list, HashKey k) {
	if (inSharedGroup) {
	    for (int i = 0; i < localToVworldKeys.length; i++) {
		k.set(localToVworldKeys[i]);
		processAddNodesForScopedAltApp(mAltApp,  list,  k);
	    }
	}
	else {
	    processAddNodesForScopedAltApp(mAltApp,  list,  k);
	}
    }

    // If its a group, then add the scope to the group, if
    // its a shape, then keep a list to be added during
    // updateMirrorObject
    void processAddNodesForScopedAltApp(AlternateAppearanceRetained mAltApp, ArrayList list, HashKey k) {
	// If the source is live, then notify the children
	if (allocatedAltApps)
	    addAltApp(mAltApp, k);
	if (this.source.isLive() || this.isInSetLive()) {
	    for (int i = children.size()-1; i >=0; i--) {
			NodeRetained child = children.get(i);
		if(child != null) {
		    if (child instanceof GroupRetained && (child.source.isLive() || child.isInSetLive()))
			((GroupRetained)child).processAddNodesForScopedAltApp(mAltApp, list, k);
		    else if (child instanceof LinkRetained && child.source.isLive()) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			((GroupRetained)(ln.sharedGroup)).
			    processAddNodesForScopedAltApp(mAltApp, list, k.append("+").
							append(ln.nodeId));
			k.count = lastCount;
		    } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
			((Shape3DRetained)child).getMirrorObjects(list, k);
		    } else if (child instanceof MorphRetained && child.source.isLive()) {
			((MorphRetained)child).getMirrorObjects(list,  k);
		    }
		}
	    }
	}
    }

    void removeAllNodesForScopedAltApp(AlternateAppearanceRetained mAltApp, ArrayList list, HashKey k) {
	if (inSharedGroup) {
	    for (int i = 0; i < localToVworldKeys.length; i++) {
		k.set(localToVworldKeys[i]);
		processRemoveNodesForScopedAltApp(mAltApp,  list,  k);
	    }
	}
	else {
	    processAddNodesForScopedAltApp(mAltApp,  list,  k);
	}
    }

    // If its a group, then add the scope to the group, if
    // its a shape, then keep a list to be added during
    // updateMirrorObject
    void processRemoveNodesForScopedAltApp(AlternateAppearanceRetained mAltApp, ArrayList list, HashKey k) {
	// If the source is live, then notify the children
	if (allocatedAltApps)
	    removeAltApp(mAltApp, k);
	if (this.source.isLive() && !isInClearLive) {
	    for (int i = children.size()-1; i >=0; i--) {
			NodeRetained child = children.get(i);
		if(child != null) {
		    if (child instanceof GroupRetained &&(child.source.isLive() &&
							    ! ((GroupRetained)child).isInClearLive))
			((GroupRetained)child).processRemoveNodesForScopedAltApp(mAltApp, list, k);
		    else if (child instanceof LinkRetained && child.source.isLive()) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			((GroupRetained)(ln.sharedGroup)).
			    processRemoveNodesForScopedAltApp(mAltApp, list, k.append("+").
							append(ln.nodeId));
			k.count = lastCount;
		    } else if (child instanceof Shape3DRetained && child.source.isLive() ) {
			((Shape3DRetained)child).getMirrorObjects(list, k);
		    } else if (child instanceof MorphRetained && child.source.isLive()) {
			((MorphRetained)child).getMirrorObjects(list,  k);
		    }
		}
	    }
	}
    }

synchronized void setLightScope() {
	// Make group's own copy
	ArrayList> newLights;
	if (!allocatedLights) {
		allocatedLights = true;
		if (lights != null) {
			newLights = new ArrayList>(lights.size());
			int size = lights.size();
			for (int i = 0; i < size; i++) {
				ArrayList l = lights.get(i);
				if (l != null) {
					newLights.add(new ArrayList(l));
				}
				else {
					newLights.add(null);
				}
			}
		}
		else {
			if (inSharedGroup) {
				newLights = new ArrayList>();
				for (int i = 0; i < localToVworldKeys.length; i++) {
					newLights.add(new ArrayList());
				}
			}
			else {
				newLights = new ArrayList>();
				newLights.add(new ArrayList());
			}
		}
		lights = newLights;

	}
	scopingRefCount++;
}
    synchronized void removeLightScope() {
	scopingRefCount--;
    }


synchronized void setFogScope() {
	// Make group's own copy
	ArrayList> newFogs;
	if (!allocatedFogs) {
		allocatedFogs = true;
		if (fogs != null) {
			newFogs = new ArrayList>(fogs.size());
			int size = fogs.size();
			for (int i = 0; i < size; i++) {
				ArrayList l = fogs.get(i);
				if (l != null) {
					newFogs.add(new ArrayList(l));
				}
				else {
					newFogs.add(null);
				}
			}
		}
		else {
			if (inSharedGroup) {
				newFogs = new ArrayList>();
				for (int i = 0; i < localToVworldKeys.length; i++) {
					newFogs.add(new ArrayList());
				}
			}
			else {
				newFogs = new ArrayList>();
				newFogs.add(new ArrayList());
			}
		}
		fogs = newFogs;

	}
	scopingRefCount++;
}
    synchronized void removeFogScope() {
	scopingRefCount--;
    }


synchronized void setMclipScope() {
	// Make group's own copy
	ArrayList> newMclips;
	if (!allocatedMclips) {
		allocatedMclips = true;
		if (modelClips != null) {
			newMclips = new ArrayList>(modelClips.size());
			int size = modelClips.size();
			for (int i = 0; i < size; i++) {
				ArrayList l = modelClips.get(i);
				if (l != null) {
					newMclips.add(new ArrayList(l));
				}
				else {
					newMclips.add(null);
				}
			}
		}
		else {
			if (inSharedGroup) {
				newMclips = new ArrayList>();
				for (int i = 0; i < localToVworldKeys.length; i++) {
					newMclips.add(new ArrayList());
				}
			}
			else {
				newMclips = new ArrayList>();
				newMclips.add(new ArrayList());
			}
		}
		modelClips = newMclips;

	}
	scopingRefCount++;
}
    synchronized void removeMclipScope() {
	scopingRefCount--;
    }


synchronized void setAltAppScope() {
	// Make group's own copy
	ArrayList> newAltApps;
	if (!allocatedAltApps) {
		allocatedAltApps = true;
		if (altAppearances != null) {
			newAltApps = new ArrayList>(altAppearances.size());
			int size = altAppearances.size();
			for (int i = 0; i < size; i++) {
				ArrayList l = altAppearances.get(i);
				if (l != null) {
					newAltApps.add(new ArrayList(l));
				}
				else {
					newAltApps.add(null);
				}
			}
		}
		else {
			if (inSharedGroup) {
				newAltApps = new ArrayList>();
				for (int i = 0; i < localToVworldKeys.length; i++) {
					newAltApps.add(new ArrayList());
				}
			}
			else {
				newAltApps = new ArrayList>();
				newAltApps.add(new ArrayList());
			}
		}
		altAppearances = newAltApps;

	}
	scopingRefCount++;
}

    synchronized void removeAltAppScope() {
	scopingRefCount--;
    }


    synchronized boolean usedInScoping() {
	return (scopingRefCount > 0);
    }

     // Add a light to the list of lights
     void addLight(LightRetained[] addlight, int numLgts, HashKey key) {
	 if (inSharedGroup) {
	     int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
		ArrayList l = lights.get(hkIndex);
	     if (l != null) {
		 for (int i = 0; i < numLgts; i++) {
		     l.add(addlight[i]);
		 }
	     }
	 }
	 else {
		ArrayList l = lights.get(0);
	     for (int i = 0; i < numLgts; i++) {
		 l.add(addlight[i]);
	     }
	 }

     }
      // Add a fog to the list of fogs
      void addFog(FogRetained fog, HashKey key) {
	 if (inSharedGroup) {
	     int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
		ArrayList l = fogs.get(hkIndex);
	     if (l != null) {
		 l.add(fog);
	     }
	 }
	 else {
		ArrayList l = fogs.get(0);
	     l.add(fog);
	 }

      }

      // Add a ModelClip to the list of ModelClip
      void addModelClip(ModelClipRetained modelClip, HashKey key) {
	 if (inSharedGroup) {
	     int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
		ArrayList l = modelClips.get(hkIndex);
	     if (l != null) {
		 l.add(modelClip);
	     }
	 }
	 else {
		ArrayList l = modelClips.get(0);
	     l.add(modelClip);
	 }

      }
      // Add a alt appearance to the list of alt appearance
      void addAltApp(AlternateAppearanceRetained altApp, HashKey key) {
	 if (inSharedGroup) {
	     int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
		ArrayList l = altAppearances.get(hkIndex);
	     if (l != null) {
		 l.add(altApp);
	     }
	 }
	 else {
		ArrayList l = altAppearances.get(0);
	     l.add(altApp);
	 }

      }


    // Remove a fog from the list of fogs
    void removeFog(FogRetained fog, HashKey key) {
	 int index;
	 if (inSharedGroup) {
	     int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
		ArrayList l = fogs.get(hkIndex);
	     if (l != null) {
		 index = l.indexOf(fog);
		 l.remove(index);
	     }
	 }
	 else {
		ArrayList l = fogs.get(0);
	     index = l.indexOf(fog);
	     l.remove(index);
	 }

     }


    // Remove a ModelClip from the list of ModelClip
    void removeModelClip(ModelClipRetained modelClip, HashKey key) {
	 int index;
	 if (inSharedGroup) {
	     int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
		ArrayList l = modelClips.get(hkIndex);
	     if (l != null) {
		 index = l.indexOf(modelClip);
		 l.remove(index);
	     }
	 }
	 else {
		ArrayList l = modelClips.get(0);
	     index = l.indexOf(modelClip);
	     l.remove(index);
	 }
     }



    // Remove a fog from the list of alt appearance
    void removeAltApp(AlternateAppearanceRetained altApp, HashKey key) {
	 int index;
	 if (inSharedGroup) {
	     int hkIndex = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
		ArrayList l = altAppearances.get(hkIndex);
	     if (l != null) {
		 index = l.indexOf(altApp);
		 l.remove(index);
	     }
	 }
	 else {
		ArrayList l = altAppearances.get(0);
	     index = l.indexOf(altApp);
	     l.remove(index);
	 }

     }


    @Override
    void updatePickable(HashKey keys[], boolean pick[]) {
	int numChildLessOne = children.size() - 1;
	super.updatePickable(keys, pick);
	int i=0;

        // Fix for issue 540
        if (numChildLessOne < 0) {
            return;
        }
        // End fix for issue 540

	for (i = 0; i < numChildLessOne; i++) {
		NodeRetained child = children.get(i);
	    if(child != null)
		child.updatePickable(keys, (boolean []) pick.clone());
	}
	// No need to clone for the last value

	NodeRetained child = children.get(i);
	if(child != null)
	    child.updatePickable(keys, pick);

    }


    @Override
    void updateCollidable(HashKey keys[], boolean collide[]) {
	int numChildLessOne = children.size() - 1;
	super.updateCollidable(keys, collide);
	int i=0;

        // Fix for issue 540
        if (numChildLessOne < 0) {
            return;
        }
        // End fix for issue 540

	for (i = 0; i < numChildLessOne; i++) {
		NodeRetained child = children.get(i);
		if (child != null)
			child.updateCollidable(keys, (boolean[]) collide.clone());
	}
	// No need to clone for the last value
	NodeRetained child = children.get(i);
	if(child != null)
	    child.updateCollidable(keys, collide);
    }

    void setAlternateCollisionTarget(boolean target) {
	if (collisionTarget == target)
	    return;

	collisionTarget = target;

	if (source.isLive()) {
	    // Notify parent TransformGroup to add itself
	    // Since we want to update collisionVwcBounds when
	    // transform change in TransformStructure.
	    TransformGroupRetained tg;
	    J3dMessage message = new J3dMessage();
	    message.threads = J3dThread.UPDATE_GEOMETRY;
	    message.universe = universe;
	    // send message to GeometryStructure to add/remove this
	    // group node in BHTree as AlternateCollisionTarget

	    int numPath;
	    CachedTargets newCtArr[] = null;

	    if (target) {
		createMirrorGroup();

                TargetsInterface ti = getClosestTargetsInterface(
                                        TargetsInterface.TRANSFORM_TARGETS);
                if (ti != null) {

                    // update targets
	            CachedTargets ct;
		    Targets targets = new Targets();
		    numPath = mirrorGroup.size();
		    newCtArr = 	new CachedTargets[numPath];
		    for (int i=0; i(branchGroupPaths);
		s.orderedPaths = orderedPaths;

	    // Make the scoped fogs and lights of the child to include, the
	    // the scoped fog of this group
	    s.lights = lights;
	    s.altAppearances = altAppearances;
	    s.fogs = fogs;
	    s.modelClips = modelClips;

	    boolean pick[];
	    boolean collide[];

	    if (!inSharedGroup) {
		pick = new boolean[1];
		collide = new boolean[1];
	    } else {
		pick = new boolean[localToVworldKeys.length];
		collide = new boolean[localToVworldKeys.length];
	    }
	    findPickableFlags(pick);
	    super.updatePickable(null, pick);
	    s.pickable = pick;

	    findCollidableFlags(collide);
	    super.updateCollidable(null, collide);
	    s.collidable = collide;

            TargetsInterface transformInterface, switchInterface;
            transformInterface = initTransformStates(s, true);
            switchInterface = initSwitchStates(s, this, child, linkNode, true);


	    if (s.inViewSpecificGroup &&
		(s.changedViewGroup == null)) {
		s.changedViewGroup = new ArrayList();
		s.changedViewList = new ArrayList>();
		s.keyList = new int[10];
		s.viewScopedNodeList = new ArrayList();
		s.scopedNodesViewList = new ArrayList>();
	    }

            childCheckSetLive(child, childIndex, s, linkNode);

	    CachedTargets[] newCtArr = null;
            newCtArr = updateTransformStates(s, transformInterface, true);
            updateSwitchStates(s, switchInterface, true);

	    // We're sending multiple messages in the call, inorder to
	    // have all these messages to be process as an atomic operation.
	    // We need to create an array of messages to MasterControl, this
	    // will ensure that all these messages will get the same time stamp.

	    // If it is called from "moveTo",  messages is not null.
	    if (messages == null) {
		int numMessages = 2;
		if(s.ogList.size() > 0) {
		    numMessages++;
		}
		else {
		    sendOGMessage = false;
		}
		if(s.changedViewGroup != null) {
		    numMessages++;
		}
		else {
		    sendVSGMessage = false;
		}

		messages = new J3dMessage[numMessages];
		messageIndex = 0;
		for(int mIndex=0; mIndex < numMessages; mIndex++) {
		    messages[mIndex] = new J3dMessage();
		}
		sendMessages = true;
	    }

	    if(sendOGMessage) {
		createMessage = messages[messageIndex++];
		createMessage.threads = J3dThread.UPDATE_RENDER |
		    J3dThread.UPDATE_RENDERING_ENVIRONMENT;
		createMessage.type = J3dMessage.ORDERED_GROUP_INSERTED;
		createMessage.universe = universe;
		createMessage.args[0] = s.ogList.toArray();
		createMessage.args[1] = s.ogChildIdList.toArray();
		createMessage.args[2] = s.ogOrderedIdList.toArray();
		createMessage.args[3] = s.ogCIOList.toArray();
		createMessage.args[4] = s.ogCIOTableList.toArray();
	    }


	    if(sendVSGMessage) {
		createMessage = messages[messageIndex++];
		createMessage.threads = J3dThread.UPDATE_RENDERING_ENVIRONMENT;
		createMessage.type = J3dMessage.VIEWSPECIFICGROUP_INIT;
		createMessage.universe = universe;
		createMessage.args[0] = s.changedViewGroup;
		createMessage.args[1] = s.changedViewList;
		createMessage.args[2] = s.keyList;
	    }

	    createMessage = messages[messageIndex++];
	    createMessage.threads = s.notifyThreads;
	    createMessage.type = J3dMessage.INSERT_NODES;
	    createMessage.universe = universe;
	    createMessage.args[0] = s.nodeList.toArray();
	    if (newCtArr != null) {
                createMessage.args[1] = transformInterface;
                createMessage.args[2] = newCtArr;
	    } else {
                createMessage.args[1] = null;
                createMessage.args[2] = null;
	    }

	    if (s.viewScopedNodeList != null) {
		createMessage.args[3] = s.viewScopedNodeList;
		createMessage.args[4] = s.scopedNodesViewList;
	    }

            // execute user behavior's initialize methods
	    int sz = s.behaviorNodes.size();

            for (int i=0; i < sz; i++) {
                BehaviorRetained b = s.behaviorNodes.get(i);
                b.executeInitialize();
            }

            s.behaviorNodes.clear();

	    createMessage = messages[messageIndex++];

	    createMessage.threads = J3dThread.UPDATE_BEHAVIOR;
	    createMessage.type = J3dMessage.BEHAVIOR_ACTIVATE;
	    createMessage.universe = universe;

	    if (sendMessages == true) {
		VirtualUniverse.mc.processMessage(messages);
	    }

            if (nodeType == NodeRetained.SWITCH) {
	        // force reEvaluation of switch children
		SwitchRetained sw = (SwitchRetained)this;
	        sw.setWhichChild(sw.whichChild, true);
            }

	    //Reset SetLiveState to free up memory.
	    s.reset(null);
        }
    }



    void checkClearLive(NodeRetained child,
                        J3dMessage messages[], int messageIndex,
			int childIndex, NodeRetained linkNode) {
	checkClearLive(child, localToVworldKeys, inSharedGroup,
		       messages, messageIndex, childIndex, linkNode);
    }



    /**
     * This checks if clearLive needs to be called.  If it does, it gets the
     * needed info and calls it.
     */
    void checkClearLive(NodeRetained child, HashKey keys[],
			boolean isShared,
                        J3dMessage messages[], int messageIndex,
			int childIndex, NodeRetained linkNode) {

        SceneGraphObject me = this.source;
	J3dMessage destroyMessage;
	boolean sendMessages = false;
	boolean sendOGMessage = true;
	boolean sendVSGMessage = true;

  	int i, j;
	TransformGroupRetained tg;

        if (me.isLive()) {
            SetLiveState s = universe.setLiveState;

            s.reset(locale);
	    s.refCount = refCount;
            s.inSharedGroup = isShared;
            s.inBackgroundGroup = inBackgroundGroup;
            s.inViewSpecificGroup = inViewSpecificGroup;
            s.keys = keys;
            s.fogs = fogs;
            s.lights = lights;
	    s.altAppearances = altAppearances;
	    s.modelClips = modelClips;

            // Issue 312: Allocate data structures if we are in a ViewSpecificGroup
	    if (s.inViewSpecificGroup &&
		(s.changedViewGroup == null)) {
		s.changedViewGroup = new ArrayList();
		s.changedViewList = new ArrayList>();
		s.keyList = new int[10];
		s.viewScopedNodeList = new ArrayList();
		s.scopedNodesViewList = new ArrayList>();
	    }

	    if (this instanceof OrderedGroupRetained && linkNode == null) {
                // set this regardless of refCount
		OrderedGroupRetained og = (OrderedGroupRetained)this;
		s.ogList.add(og);
		s.ogChildIdList.add(new Integer(childIndex));
		s.ogCIOList.add(og);
		int[] newArr = null;
		if(og.userChildIndexOrder != null) {
		    newArr = new int[og.userChildIndexOrder.length];
		    System.arraycopy(og.userChildIndexOrder, 0, newArr,
				     0, og.userChildIndexOrder.length);
		}
		s.ogCIOTableList.add(newArr);

	    }

            // Issue 312: always initialize s.viewLists
            s.viewLists = viewLists;

            TargetsInterface transformInterface, switchInterface;
            transformInterface = initTransformStates(s, false);
            switchInterface = initSwitchStates(s, this, child, linkNode, false);

            child.clearLive(s);

            CachedTargets[] newCtArr = null;
            newCtArr = updateTransformStates(s, transformInterface, false);
            updateSwitchStates(s, switchInterface, false);

	    // We're sending multiple messages in the call, inorder to
	    // have all these messages to be process as an atomic operation.
	    // We need to create an array of messages to MasterControl, this
	    // will ensure that all these messages will get the same time stamp.

	    // If it is called from "moveTo",  messages is not null.
	    if (messages == null) {
		int numMessages = 1;
		if(s.ogList.size() > 0) {
		    numMessages++;
		}
		else {
		    sendOGMessage = false;
		}

		if(s.changedViewGroup != null) {
		    numMessages++;
		}
		else {
		    sendVSGMessage = false;
		}

		messages = new J3dMessage[numMessages];
		messageIndex = 0;
		for(int mIndex=0; mIndex < numMessages; mIndex++) {
		    messages[mIndex] = new J3dMessage();
		}
		sendMessages = true;
	    }

	    if(sendOGMessage) {
		destroyMessage = messages[messageIndex++];
		destroyMessage.threads = J3dThread.UPDATE_RENDER |
		    J3dThread.UPDATE_RENDERING_ENVIRONMENT;
		destroyMessage.type = J3dMessage.ORDERED_GROUP_REMOVED;
		destroyMessage.universe = universe;
		destroyMessage.args[0] = s.ogList.toArray();
		destroyMessage.args[1] = s.ogChildIdList.toArray();
		destroyMessage.args[3] = s.ogCIOList.toArray();
		destroyMessage.args[4] = s.ogCIOTableList.toArray();
	    }

            // Issue 312: We need to send the REMOVE_NODES message to the
            // RenderingEnvironmentStructure before we send VIEWSPECIFICGROUP_CLEAR,
            // since the latter clears the list of views that is referred to by
            // scopedNodesViewList and used by removeNodes.
	    destroyMessage = messages[messageIndex++];
            destroyMessage.threads = s.notifyThreads;
            destroyMessage.type = J3dMessage.REMOVE_NODES;
            destroyMessage.universe = universe;
            destroyMessage.args[0] = s.nodeList.toArray();

	    if (newCtArr != null) {
                destroyMessage.args[1] = transformInterface;
                destroyMessage.args[2] = newCtArr;
	    } else {
                destroyMessage.args[1] = null;
                destroyMessage.args[2] = null;
	    }
	    if (s.viewScopedNodeList != null) {
		destroyMessage.args[3] = s.viewScopedNodeList;
		destroyMessage.args[4] = s.scopedNodesViewList;
	    }

	    if(sendVSGMessage) {
		destroyMessage = messages[messageIndex++];
		destroyMessage.threads =  J3dThread.UPDATE_RENDERING_ENVIRONMENT;
		destroyMessage.type = J3dMessage.VIEWSPECIFICGROUP_CLEAR;
		destroyMessage.universe = universe;
		destroyMessage.args[0] = s.changedViewGroup;
		destroyMessage.args[1] = s.keyList;
	    }

            if (sendMessages == true) {
		VirtualUniverse.mc.processMessage(messages);
	    }

	    s.reset(null); // for GC
        }
    }

    TargetsInterface initTransformStates(SetLiveState s, boolean isSetLive) {

        int numPaths = (inSharedGroup)? s.keys.length : 1;
        TargetsInterface ti = getClosestTargetsInterface(
                                        TargetsInterface.TRANSFORM_TARGETS);


	if (isSetLive) {
            s.currentTransforms = localToVworld;
            s.currentTransformsIndex = localToVworldIndex;
            s.localToVworldKeys = localToVworldKeys;
            s.localToVworld = s.currentTransforms;
            s.localToVworldIndex = s.currentTransformsIndex;

            s.parentTransformLink = parentTransformLink;
            if (parentTransformLink != null) {
                if (parentTransformLink instanceof TransformGroupRetained) {
		    TransformGroupRetained tg;
                    tg = (TransformGroupRetained) parentTransformLink;
                    s.childTransformLinks = tg.childTransformLinks;
                } else {
		    SharedGroupRetained sg;
                    sg = (SharedGroupRetained) parentTransformLink;
                    s.childTransformLinks = sg.childTransformLinks;
                }
            }
	}

	int transformLevels[] = new int[numPaths];
	findTransformLevels(transformLevels);
	s.transformLevels = transformLevels;

	if (ti != null) {
	    Targets[] newTargets = new Targets[numPaths];
	    for(int i=0; i= 0) {
		    newTargets[i] = new Targets();
		} else {
		    newTargets[i] = null;
		}
	    }
	    s.transformTargets = newTargets;

	    // XXXX: optimization for targetThreads computation, require
	    // cleanup in GroupRetained.doSetLive()
	    //s.transformTargetThreads = 0;
	}

	return ti;
    }

    CachedTargets[] updateTransformStates(SetLiveState s,
				TargetsInterface ti, boolean isSetLive) {
        CachedTargets[] newCtArr = null;

        if (ti != null) {
	    if (isSetLive) {
                CachedTargets ct;
                int newTargetThreads = 0;
                int hkIndex;

                newCtArr = new CachedTargets[localToVworld.length];

                // update targets
                if (! inSharedGroup) {
                    if (s.transformTargets[0] != null) {
                        ct = ti.getCachedTargets(
                                    TargetsInterface.TRANSFORM_TARGETS, 0, -1);
                        if (ct != null) {
                            newCtArr[0] = s.transformTargets[0].snapShotAdd(ct);
                        }
                    } else {
                        newCtArr[0] = null;
                    }
                } else {
                    for (int i=0; i= 0) {
                    newTargets[i] = new Targets();
                } else {
                    newTargets[i] = null;
                }
            }
            s.switchTargets = newTargets;
        }

	if (isSetLive) {
	    // set switch states
            if (nodeType == NodeRetained.SWITCH) {
                i = parentSwitchLinkChildIndex;
                s.childSwitchLinks = childrenSwitchLinks.get(i);
                s.parentSwitchLink = this;

            } else {
                if (nodeType == NodeRetained.SHAREDGROUP) {
                    i = parentSwitchLinkChildIndex;
                    s.childSwitchLinks = childrenSwitchLinks.get(i);
                    s.parentSwitchLink = this;

                } else {
		    s.parentSwitchLink = parentSwitchLink;
                    if (parentSwitchLink != null) {
                        i = parentSwitchLinkChildIndex;
                        s.childSwitchLinks = parentSwitchLink.childrenSwitchLinks.get(i);
                    }
                }
            }
            if (ti != null) {
                s.switchStates = ti.getTargetsData(
					TargetsInterface.SWITCH_TARGETS,
					parentSwitchLinkChildIndex);
            } else {
			s.switchStates = new ArrayList(1);
                s.switchStates.add(new SwitchState(false));
            }
	}
        return ti;
    }

    void updateSwitchStates(SetLiveState s, TargetsInterface ti,
				boolean isSetLive) {

        // update switch leaves's compositeSwitchMask for ancestors
        // and update switch leaves' switchOn flag if at top level switch

        if (ti != null) {
	    if (isSetLive) {
                CachedTargets[] newCtArr = null;
                CachedTargets ct;

                newCtArr = new CachedTargets[localToVworld.length];

                // update targets
                if (! inSharedGroup) {

                    if (s.switchTargets[0] != null) {
                        ct = ti.getCachedTargets(
                                TargetsInterface.SWITCH_TARGETS, 0,
                                        parentSwitchLinkChildIndex);
                        if (ct != null) {
                            newCtArr[0] = s.switchTargets[0].snapShotAdd(ct);
                        } else {
                            newCtArr[0] = s.switchTargets[0].snapShotInit();
                        }
		    } else {
                        newCtArr[0] = null;
		    }
                } else {
                    for (int i=0; i=0; i--) {
		NodeRetained child = children.get(i);
	    if(child != null)
		child.updateLocalToVworld();
	}
    }

    @Override
    void setNodeData(SetLiveState s) {
	super.setNodeData(s);
        orderedPaths = s.orderedPaths;
    }

    @Override
    void removeNodeData(SetLiveState s) {

        if((!inSharedGroup) || (s.keys.length == localToVworld.length)) {
            orderedPaths = null;
        }
        else {
            // Set it back to its parent localToVworld data. This is b/c the
            // parent has changed it localToVworld data arrays.
            orderedPaths = s.orderedPaths;
        }
        super.removeNodeData(s);
    }



    @Override
    void setLive(SetLiveState s) {
	doSetLive(s);
	super.markAsLive();
    }

    // Note that SwitchRetained, OrderedGroupRetained and SharedGroupRetained
    // override this method
    void childDoSetLive(NodeRetained child, int childIndex, SetLiveState s) {
	if(child!=null)
            child.setLive(s);
    }

    // Note that BranchRetained, OrderedGroupRetained and SharedGroupRetained
    // TransformGroupRetained override this method
    void childCheckSetLive(NodeRetained child, int childIndex,
                                SetLiveState s, NodeRetained linkNode) {
        child.setLive(s);
    }

    /**
     * This version of setLive calls setLive on all of its chidren.
     */
    @Override
    void doSetLive(SetLiveState s) {
      	int i, nchildren;
        super.doSetLive(s);
	locale = s.locale;

	inViewSpecificGroup = s.inViewSpecificGroup;
	nchildren = children.size();
	ArrayList> savedScopedLights = s.lights;
	ArrayList> savedScopedFogs = s.fogs;
	ArrayList> savedScopedAltApps = s.altAppearances;
	ArrayList> savedScopedMclips = s.modelClips;

	boolean oldpickableArray[] = (boolean []) s.pickable.clone();
	boolean oldcollidableArray[] = (boolean []) s.collidable.clone();
	boolean workingpickableArray[] = new boolean[oldpickableArray.length];
	boolean workingcollidableArray[] = new boolean[oldcollidableArray.length];
	ArrayList oldBranchGroupPaths = s.branchGroupPaths;
	setScopingInfo(s);


	if (!(this instanceof ViewSpecificGroupRetained)) {
	    viewLists = s.viewLists;
	}

	for (i=0; i(oldBranchGroupPaths);
	    s.inViewSpecificGroup = inViewSpecificGroup;
            childDoSetLive(child, i, s);
	}



	if (collisionTarget) {
	    processCollisionTarget(s);
	}

	s.lights = savedScopedLights;
	s.fogs = savedScopedFogs;
	s.altAppearances = savedScopedAltApps;
	s.modelClips = savedScopedMclips;
    }

    void setScopingInfo(SetLiveState s) {

	int i, k, hkIndex;
	// If this is a scoped group , then copy the parent's
	// scoping info
	if (allocatedLights) {
	    if (s.lights != null) {
		// Add the parent's scoping info to this group
		if (inSharedGroup) {
		    for (i=0; i < s.keys.length; i++) {
			hkIndex = s.keys[i].equals(localToVworldKeys, 0,
						   localToVworldKeys.length);
					ArrayList l = lights.get(hkIndex);
					ArrayList src = s.lights.get(i);
			if (src != null) {
			    int size = src.size();
			    for (k = 0; k < size; k++) {
				l.add(src.get(k));
			    }
			}

		    }
		}
		else {
				ArrayList l = lights.get(0);
				ArrayList src = s.lights.get(0);
		    int size = src.size();
		    for (i = 0; i < size; i++) {
			l.add(src.get(i));
		    }
		}
	    }
	    s.lights = lights;
	}
	else {
	    lights = s.lights;
	}

	if (allocatedFogs) {
	    if (s.fogs != null) {
		// Add the parent's scoping info to this group
		if (inSharedGroup) {
		    for (i=0; i < s.keys.length; i++) {
			hkIndex = s.keys[i].equals(localToVworldKeys, 0,
						   localToVworldKeys.length);
					ArrayList l = fogs.get(hkIndex);
					ArrayList src = s.fogs.get(i);
			if (src != null) {
			    int size = src.size();
			    for (k = 0; k < size; k++) {
				l.add(src.get(k));
			    }
			}

		    }
		}
		else {
				ArrayList l = fogs.get(0);
				ArrayList src = s.fogs.get(0);
		    int size = src.size();
		    for (i = 0; i < size; i++) {
			l.add(src.get(i));
		    }
		}
	    }
	    s.fogs = fogs;
	}
	else {
	    fogs = s.fogs;
	}

	if (allocatedMclips) {
	    if (s.modelClips != null) {
		// Add the parent's scoping info to this group
		if (inSharedGroup) {
		    for (i=0; i < s.keys.length; i++) {
			hkIndex = s.keys[i].equals(localToVworldKeys, 0,
						   localToVworldKeys.length);
					ArrayList l = modelClips.get(hkIndex);
					ArrayList src = s.modelClips.get(i);
			if (src != null) {
			    int size = src.size();
			    for (k = 0; k < size; k++) {
				l.add(src.get(k));
			    }
			}

		    }
		}
		else {
				ArrayList l = modelClips.get(0);
				ArrayList src = s.modelClips.get(0);
		    int size = src.size();
		    for (i = 0; i < size; i++) {
			l.add(src.get(i));
		    }
		}
	    }
	    s.modelClips = modelClips;
	}
	else {
	    modelClips = s.modelClips;
	}

	if (allocatedAltApps) {
	    if (s.altAppearances != null) {
		// Add the parent's scoping info to this group
		if (inSharedGroup) {
		    for (i=0; i < s.keys.length; i++) {
			hkIndex = s.keys[i].equals(localToVworldKeys, 0,
						   localToVworldKeys.length);
					ArrayList l = altAppearances.get(hkIndex);
					ArrayList src = s.altAppearances.get(i);
			if (src != null) {
			    int size = src.size();
			    for (k = 0; k < size; k++) {
				l.add(src.get(k));
			    }
			}

		    }
		}
		else {
				ArrayList l = altAppearances.get(0);
				ArrayList src = s.altAppearances.get(0);
		    int size = src.size();
		    for (i = 0; i < size; i++) {
			l.add(src.get(i));
		    }
		}
	    }
	    s.altAppearances = altAppearances;
	}
	else {
	    altAppearances = s.altAppearances;
	}
    }

    void processCollisionTarget(SetLiveState s) {

	    GroupRetained g;
	if (mirrorGroup == null) {
		mirrorGroup = new ArrayList();
	}
	    Bounds bound = (collisionBound != null ?
			    collisionBound : getEffectiveBounds());
	    if (inSharedGroup) {
		for (int i=0; i < s.keys.length; i++) {
		    int j;
		    g = new GroupRetained();
		    g.key = s.keys[i];
		    g.localToVworld = new Transform3D[1][];
		    g.localToVworldIndex = new int[1][];

		    j = s.keys[i].equals(localToVworldKeys, 0,
					 localToVworldKeys.length);
		    if(j < 0) {
			System.err.println("GroupRetained : Can't find hashKey");
		    }

		    g.localToVworld[0] = localToVworld[j];
		    g.localToVworldIndex[0] = localToVworldIndex[j];
		    g.collisionVwcBounds = new BoundingBox();
		    g.collisionVwcBounds.transform(bound, g.getCurrentLocalToVworld(0));
		    g.sourceNode = this;
		    g.locale = locale; // need by getVisibleGeometryAtom()
		    mirrorGroup.add(g);
		    /*
		      System.err.println("processCollisionTarget mirrorGroup.add() : " +
		      g.getId() + " mirrorGroup.size() "
		      + mirrorGroup.size());
		    */
		    if (s.transformTargets != null &&
				s.transformTargets[i] != null) {
		        s.transformTargets[i].addNode(g, Targets.GRP_TARGETS);
		    }
		    s.nodeList.add(g);
		}
	    } else {
		g = new GroupRetained();
		g.localToVworld = new Transform3D[1][];
		g.localToVworldIndex = new int[1][];
		g.localToVworld[0] = localToVworld[0];
		g.localToVworldIndex[0] = localToVworldIndex[0];
		g.collisionVwcBounds = new BoundingBox();
		g.collisionVwcBounds.transform(bound, g.getCurrentLocalToVworld(0));
		g.sourceNode = this;
		g.locale = locale; // need by getVisibleGeometryAtom()
		mirrorGroup.add(g);
                if (s.transformTargets != null &&
			s.transformTargets[0] != null) {
		    s.transformTargets[0].addNode(g, Targets.GRP_TARGETS);
		}
		s.nodeList.add(g);
	    }
    }

    @Override
    void computeCombineBounds(Bounds bounds) {
        if (!VirtualUniverse.mc.cacheAutoComputedBounds) {
            if (boundsAutoCompute) {
                for (int i=children.size()-1; i>=0; i--) {
				NodeRetained child = children.get(i);
                    if(child != null)
                        child.computeCombineBounds(bounds);
                }
            } else {
                // Should this be lock too ? ( MT safe  ? )
                synchronized(localBounds) {
                    bounds.combine(localBounds);
                }
            }
        } else {
            // Issue 514 : NPE in Wonderland : triggered in cached bounds computation
            if (validCachedBounds && boundsAutoCompute) {
                bounds.combine(cachedBounds);
                return;
            }

            if (boundsAutoCompute) {
                // issue 544
                if (VirtualUniverse.mc.useBoxForGroupBounds) {
                    cachedBounds = new BoundingBox((Bounds) null);
                } else {
				cachedBounds = new BoundingSphere((Bounds)null);
                }
                for (int i = children.size() - 1; i >= 0; i--) {
				NodeRetained child = children.get(i);
                    if (child != null) {
                        child.computeCombineBounds(cachedBounds);
                    }
                }
                bounds.combine(cachedBounds);
            } else {
                // Should this be lock too ? ( MT safe  ? )
                synchronized(localBounds) {
                    bounds.combine(localBounds);
                }
            }
        }

    }


    /**
     * Gets the bounding object of a node.
     * @return the node's bounding object
     */
    @Override
    Bounds getBounds() {

	if ( boundsAutoCompute) {
            // Issue 514 : NPE in Wonderland : triggered in cached bounds computation
            if (validCachedBounds) {
                return (Bounds) cachedBounds.clone();
            }
            // issue 544
            Bounds boundingObject = null;
            if (VirtualUniverse.mc.useBoxForGroupBounds) {
                boundingObject = new BoundingBox((Bounds) null);
            } else {
			boundingObject = new BoundingSphere((Bounds)null);
            }
            for (int i = children.size() - 1; i >= 0; i--) {
			NodeRetained child = children.get(i);
                if (child != null) {
                    child.computeCombineBounds(boundingObject);
                }
            }

            return boundingObject;
        }
        return super.getBounds();
    }

    /**
     * Gets the bounding object of a node.
     * @return the node's bounding object
     */
    @Override
    Bounds getEffectiveBounds() {
	if ( boundsAutoCompute) {
	    return getBounds();
	}
	return super.getEffectiveBounds();
    }

    // returns true if children cannot be read/written and none of the
    // children can read their parent (i.e., "this") group node
    boolean isStaticChildren() {
	if (source.getCapability(Group.ALLOW_CHILDREN_READ) ||
	    source.getCapability(Group.ALLOW_CHILDREN_WRITE)) {
	    return false;
	}

	for (int i = children.size() - 1; i >= 0; i--) {
		NodeRetained nodeR = children.get(i);
	    if (nodeR != null && nodeR.source.getCapability(Node.ALLOW_PARENT_READ)) {
		return false;
	    }
	}

	return true;
    }


    @Override
    boolean isStatic() {
	return (super.isStatic() && isStaticChildren());
    }

    /**
     * This compiles() a group
     */
    @Override
    void setCompiled() {
	super.setCompiled();
	for (int i=children.size()-1; i>=0; i--) {
		NodeRetained node = children.get(i);
	    if (node != null)
		node.setCompiled();
	}
    }

    @Override
    void traverse(boolean sameLevel, int level) {
	if (!sameLevel) {
	    super.traverse(true, level);

	    if (source.getCapability(Group.ALLOW_CHILDREN_READ)) {
		System.err.print(" (r)");
	    } else if (isStatic()) {
		System.err.print(" (s)");
	    } else if (source.getCapability(Group.ALLOW_CHILDREN_WRITE)) {
		System.err.print(" (w)");
	    }
	}

	level++;
	for (int i = 0; i < children.size(); i++) {
		NodeRetained node = children.get(i);
	    if (node != null) {
		node.traverse(false, level);
	    }
	}
    }

    @Override
    void compile(CompileState compState) {
	super.compile(compState);

	mergeFlag = SceneGraphObjectRetained.MERGE;

	if (!isStatic()) {
	    compState.keepTG = true;
	    mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
        }

	if (isRoot || this.usedInScoping() ||
	    (parent instanceof SwitchRetained)) {
	    mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
	}

	compiledChildrenList = new ArrayList(5);

	for (int i = 0; i < children.size(); i++) {
		NodeRetained node = children.get(i);
	    if (node != null) {
		node.compile(compState);
	    }
	}

        if (J3dDebug.devPhase && J3dDebug.debug) {
            compState.numGroups++;
	}
    }

    @Override
    void merge(CompileState compState) {

	GroupRetained saveParentGroup = null;

	if (mergeFlag != SceneGraphObjectRetained.MERGE_DONE) {
            if (mergeFlag == SceneGraphObjectRetained.DONT_MERGE) {

	 	// don't merge/eliminate this node
		super.merge(compState);

		saveParentGroup = compState.parentGroup;
		compState.parentGroup = this;
	    }

	    for (int i = 0; i < children.size(); i++) {
			NodeRetained node = children.get(i);
	        if (node != null) {
		    node.merge(compState);
		}
	    }

	    if (compState.parentGroup == this) {
	        this.children = compiledChildrenList;
		compState.doShapeMerge();
	        compiledChildrenList = null;
		compState.parentGroup = saveParentGroup;
	    } else {
		// this group node can be eliminated
		this.children.clear();

                if (J3dDebug.devPhase && J3dDebug.debug) {
                    compState.numMergedGroups++;
	        }
	    }

	    mergeFlag = SceneGraphObjectRetained.MERGE_DONE;

	} else {
            if (compState.parentGroup != null) {
                compState.parentGroup.compiledChildrenList.add(this);
                parent = compState.parentGroup;
            }
	}
    }

    /**
     * This version of clearLive calls clearLive on all of its chidren.
     */
    @Override
    void clearLive(SetLiveState s) {
	int i, k, hkIndex, nchildren;
	int parentScopedLtSize = 0;
	int parentScopedFogSize = 0;
	int parentScopedMcSize = 0;
	int parentScopedAltAppSize = 0;
	int groupScopedLtSize = 0;
	int groupScopedFogSize = 0;
	int groupScopedMcSize = 0;
	int groupScopedAltAppSize = 0;
	int size;

	isInClearLive = true;

	// Save this for later use in this method. Temporary. to be removed when OG cleanup.
	HashKey[] savedLocalToVworldKeys = localToVworldKeys;

	super.clearLive(s);


	nchildren = this.children.size();

	if (!(this instanceof ViewSpecificGroupRetained)) {
	    viewLists = s.viewLists;
	}

	ArrayList> savedParentLights = s.lights;
	if (allocatedLights) {
		s.lights = lights;
	}

	ArrayList> savedParentFogs = s.fogs;
	if (allocatedFogs) {
		s.fogs = fogs;
	}

	ArrayList> savedParentMclips = s.modelClips;
	if (allocatedMclips) {
		s.modelClips = modelClips;
	}

	ArrayList> savedParentAltApps = s.altAppearances;
	if (allocatedAltApps) {
		s.altAppearances = altAppearances;
	}


	for (i=nchildren-1; i >=0 ; i--) {
		NodeRetained child = children.get(i);
	    if (this instanceof OrderedGroupRetained) {
	        OrderedGroupRetained og = (OrderedGroupRetained)this;

		// adjust refCount, which has been decremented
	        //in super.clearLive
		if ((refCount+1) == s.refCount) {
		    //only need to do it once if in shared group. Add
		    //all the children to the list of OG_REMOVED message
		    s.ogList.add(og);
		    s.ogChildIdList.add(new Integer(i));
		}
			s.orderedPaths = og.childrenOrderedPaths.get(i);
	    }

	    if (child != null) {
	        child.clearLive(s);
	    }
	}
	// Has its own copy
	// XXXX: Handle the case of
	// was non-zero, gone to zero?
	if (savedParentLights != null) {
	    if (allocatedLights) {
		if (inSharedGroup) {

		    for (i=0; i < s.keys.length; i++) {
			hkIndex = s.keys[i].equals(localToVworldKeys, 0,
						   localToVworldKeys.length);
					ArrayList l = savedParentLights.get(hkIndex);
					ArrayList gl = lights.get(hkIndex);
			if (l != null) {
			    size = l.size();
			    for (k = 0; k < size; k++) {
				gl.remove(l.get(k));
			    }
			}

		    }
		}
		else {
				ArrayList l = savedParentLights.get(0);
				ArrayList gl = lights.get(0);
		    size = l.size();
		    for (int m = 0; m < size; m++) {
			gl.remove(l.get(m));
		    }
		}
	    }
	}

	if (savedParentFogs != null) {
	    if (allocatedFogs) {
		if (inSharedGroup) {
		    for (i=0; i < s.keys.length; i++) {
			hkIndex = s.keys[i].equals(localToVworldKeys, 0,
						   localToVworldKeys.length);
					ArrayList l = savedParentFogs.get(hkIndex);
					ArrayList gl = fogs.get(hkIndex);
			if (l != null) {
			    size = l.size();
			    for (k = 0; k < size; k++) {
				gl.remove(l.get(k));
			    }
			}

		    }
		}
		else {
				ArrayList l = savedParentFogs.get(0);
		    size = l.size();
		    for (int m = 0; m < size; m++) {
			fogs.remove(l.get(m));
		    }
		}
	    }
	}

	if (savedParentMclips != null) {
	    if (allocatedMclips) {
		if (inSharedGroup) {
		    for (i=0; i < s.keys.length; i++) {
			hkIndex = s.keys[i].equals(localToVworldKeys, 0,
						   localToVworldKeys.length);
					ArrayList l = savedParentMclips.get(hkIndex);
					ArrayList gl = modelClips.get(hkIndex);
			if (l != null) {
			    size = l.size();
			    for (k = 0; k < size; k++) {
				gl.remove(l.get(k));
			    }
			}

		    }
		}
		else {
				ArrayList l = savedParentMclips.get(0);
		    size = l.size();
		    for (int m = 0; m < size; m++) {
			modelClips.remove(l.get(m));
		    }
		}
	    }
	}

	if (savedParentAltApps != null) {
	    if (allocatedAltApps) {
		if (inSharedGroup) {
		    for (i=0; i < s.keys.length; i++) {
			hkIndex = s.keys[i].equals(localToVworldKeys, 0,
						   localToVworldKeys.length);
					ArrayList l = savedParentAltApps.get(hkIndex);
					ArrayList gl = altAppearances.get(hkIndex);
			if (l != null) {
			    size = l.size();
			    for (k = 0; k < size; k++) {
				gl.remove(l.get(k));
			    }
			}

		    }
		}
		else {
				ArrayList l = savedParentAltApps.get(0);
		    size = l.size();
		    for (int m = 0; m < size; m++) {
			altAppearances.remove(l.get(m));
		    }
		}
	    }
	}

	if (collisionTarget) {
	    GroupRetained g;
	    if (inSharedGroup) {
		for (i=s.keys.length-1; i >=0; i--) {
		    HashKey hkey = s.keys[i];
		    for (int j = mirrorGroup.size()-1; j >=0 ; j--) {
			g = mirrorGroup.get(j);
			if (g.key.equals(hkey)) {
			    s.nodeList.add(mirrorGroup.remove(j));
			    if (s.transformTargets != null &&
				s.transformTargets[j] != null) {
				s.transformTargets[j].addNode(g, Targets.GRP_TARGETS);
			    }
			    break;
			}

		    }
		}
	    } else {
                g = mirrorGroup.get(0);
                if (s.transformTargets != null &&
			s.transformTargets[0] != null) {
                    s.transformTargets[0].addNode(g, Targets.GRP_TARGETS);
		}
                s.nodeList.add(mirrorGroup.remove(0));
	    }
	}
	s.lights = savedParentLights;
	s.modelClips = savedParentMclips;
	s.fogs = savedParentFogs;
	s.altAppearances = savedParentAltApps;
	isInClearLive = false;
    }

    // This is only used by alternateCollisionTarget
    @Override
    public BoundingBox computeBoundingHull() {
	return collisionVwcBounds;
    }

    // If isSwitchOn cached here, we don't need to traverse up the tree
    @Override
    public boolean isEnable() {
	return isNodeSwitchOn(this.sourceNode, key);
    }

    // If isSwitchOn cached here, we don't need to traverse up the tree
    // This method does nothing with vis.
    @Override
    public boolean isEnable(int vis) {
	return isNodeSwitchOn(this.sourceNode, key);
    }

    // Can't use getLocale, it is used by BranchGroupRetained
    @Override
    public Locale getLocale2() {
	return locale;
    }

    /**
     * Return true of nodeR is not under a switch group or
     * nodeR is enable under a switch group.
     */
    static boolean isNodeSwitchOn(NodeRetained node, HashKey key) {
	NodeRetained prevNode = null;
	if (key != null) {
	    key = new HashKey(key);
	}

	synchronized (node.universe.sceneGraphLock) {
	    do {
		if ((node instanceof SwitchRetained) &&
		    (prevNode != null) &&
		    !validSwitchChild((SwitchRetained) node, prevNode)) {
		    return false;
		}
		prevNode = node;
		if (node instanceof SharedGroupRetained) {
		    // retrieve the last node ID
		    String nodeId = key.getLastNodeId();
			Vector parents = ((SharedGroupRetained)node).parents;
		    // find the matching link
		    for(int i=parents.size()-1; i >=0; i--) {
			NodeRetained link = parents.get(i);
			if (link.nodeId.equals(nodeId)) {
			    node = link;
			    break;
			}
		    }
		    if (node == prevNode) {
			// Fail to found a matching link, this is
			// probably cause by BHTree not yet updated
			// because message not yet arrive
			// when collision so it return current node as target.
			return false;
		    }
		} else {
		    node = node.parent;
		}
	    } while (node != null);
	    // reach locale
	}
	return true;
    }



    /**
     * Determinte if nodeR is a valid child to render for
     * Switch Node swR.
     */
    static boolean validSwitchChild(SwitchRetained sw,
				    NodeRetained node) {

	int whichChild = sw.whichChild;

	if (whichChild == Switch.CHILD_NONE) {
	    return false;
	}

	if (whichChild == Switch.CHILD_ALL) {
	    return true;
	}

	ArrayList children = sw.children;

	if (whichChild >= 0) { // most common case
	    return (children.get(whichChild) == node);
	}

	// Switch.CHILD_MASK
	for (int i=children.size()-1; i >=0; i--) {
	    if (sw.childMask.get(i) &&
		(children.get(i) == node)) {
		return true;
	    }
	}
	return false;
    }


    /**
     * Create mirror group when this Group AlternateCollisionTarget
     * is set to true while live.
     */
    void createMirrorGroup() {
	GroupRetained g;

	mirrorGroup = new ArrayList();

	Bounds bound = (collisionBound != null ?
			collisionBound : getEffectiveBounds());

	if (inSharedGroup) {
	    for (int i=0; i < localToVworldKeys.length; i++) {
		g = new GroupRetained();
		g.key = localToVworldKeys[i];
		g.localToVworld = new Transform3D[1][];
		g.localToVworldIndex = new int[1][];
		g.localToVworld[0] = localToVworld[i];
		g.localToVworldIndex[0] = localToVworldIndex[i];
		g.collisionVwcBounds = new BoundingBox();
		g.collisionVwcBounds.transform(bound, g.getCurrentLocalToVworld());
		g.sourceNode = this;
		g.locale = locale; // need by getVisibleGeometryAtom()
		mirrorGroup.add(g);
	    }
	} else {
	    g = new GroupRetained();
	    g.localToVworld = new Transform3D[1][];
	    g.localToVworldIndex = new int[1][];
	    g.localToVworld[0] = localToVworld[0];
	    g.localToVworldIndex[0] = localToVworldIndex[0];
	    g.collisionVwcBounds = new BoundingBox();
	    g.collisionVwcBounds.transform(bound, g.getCurrentLocalToVworld());
	    g.sourceNode = this;
	    g.locale = locale; // need by getVisibleGeometryAtom()
	    mirrorGroup.add(g);
	}
    }

    @Override
    void setBoundsAutoCompute(boolean autoCompute) {
        if (autoCompute != boundsAutoCompute) {
            super.setBoundsAutoCompute(autoCompute);
            if (!autoCompute) {
                localBounds = getEffectiveBounds();
            }
            if (source.isLive() && collisionBound == null && autoCompute
                && mirrorGroup != null) {

                J3dMessage message = new J3dMessage();
                message.type = J3dMessage.COLLISION_BOUND_CHANGED;
                message.threads = J3dThread.UPDATE_TRANSFORM |
		    J3dThread.UPDATE_GEOMETRY;
                message.universe = universe;
                message.args[0] = this;
                VirtualUniverse.mc.processMessage(message);
            }
        }
    }

    @Override
    void setBounds(Bounds bounds) {
        super.setBounds(bounds);
        if (source.isLive() && !boundsAutoCompute &&
	    collisionBound == null && mirrorGroup != null) {

            J3dMessage message = new J3dMessage();
            message.type = J3dMessage.COLLISION_BOUND_CHANGED;
            message.threads = J3dThread.UPDATE_TRANSFORM |
		J3dThread.UPDATE_GEOMETRY;
            message.universe = universe;
            message.args[0] = this;
            VirtualUniverse.mc.processMessage(message);
        }
    }


    @Override
    int[] processViewSpecificInfo(int mode, HashKey k, View v, ArrayList vsgList, int[] keyList,
				  ArrayList leafList) {
	int nchildren = children.size();
	if (source.isLive()) {
	    for (int i = 0; i < nchildren; i++) {
			NodeRetained child = children.get(i);
		if (child instanceof LeafRetained) {
		    if (child instanceof LinkRetained) {
			int lastCount = k.count;
			LinkRetained ln = (LinkRetained) child;
			if (k.count == 0) {
			    k.append(locale.nodeId);
			}
			keyList =  ((GroupRetained)(ln.sharedGroup)).
			    processViewSpecificInfo(mode, k.append("+").append(ln.nodeId), v, vsgList,
						    keyList, leafList);
			k.count = lastCount;
		    }
		    else {
			((LeafRetained)child).getMirrorObjects(leafList, k);
		    }
		} else {
		    keyList =  child.processViewSpecificInfo(mode, k, v, vsgList, keyList, leafList);
		}
	    }
	}
	return keyList;
    }

    void findSwitchInfo(SetLiveState s, NodeRetained parentNode,
                                NodeRetained childNode, NodeRetained linkNode) {

        NodeRetained child;
        NodeRetained parent;

	parentSwitchLinkChildIndex = -1;

	// traverse up scene graph to find switch parent information
        if (!inSharedGroup) {
            child = (linkNode == null)? childNode: linkNode;
	    parent = parentNode;
            while (parent != null) {
		if (parent instanceof SwitchRetained) {
		    s.switchLevels[0]++;
		    if (s.closestSwitchParents[0] == null) {
		        s.closestSwitchParents[0] = (SwitchRetained)parent;
		        s.closestSwitchIndices[0] =
				((SwitchRetained)parent).switchIndexCount++;
		    }
		    if (parentSwitchLinkChildIndex == -1) {
		        parentSwitchLinkChildIndex =
                            	((GroupRetained)parent).children.indexOf(child);
		    }
		} else if (parent instanceof SharedGroupRetained) {
		    if (parentSwitchLinkChildIndex == -1) {
		        parentSwitchLinkChildIndex =
                            	((GroupRetained)parent).children.indexOf(child);
		    }
		}
		child = parent;
		parent = child.parent;
            }
        } else {
            HashKey key;
	    int i,j;

            s.switchLevels = new int[localToVworldKeys.length];
            s.closestSwitchParents =
                                new SwitchRetained[localToVworldKeys.length];
            s.closestSwitchIndices = new int[localToVworldKeys.length];
            for (i=0; i parents = ((SharedGroupRetained)parent).parents;

		        if (parentSwitchLinkChildIndex == -1) {
		            parentSwitchLinkChildIndex =
                            	((GroupRetained)parent).children.indexOf(child);
		        }

                        for(j=0; j< parents.size(); j++) {
                        	NodeRetained ln = parents.get(j);
                            if (ln.nodeId.equals(nodeId)) {
                                parent = ln;
                                break;
                            }
                        }
		    }
		    child = parent;
		    parent = child.parent;
		}
            }
        }
    }

    static void gatherBlUsers(ArrayList blUsers, Object[] blArr) {
        ArrayList users;

        for (int i=0; i=0; i--) {
		NodeRetained child = children.get(i);
	    child.searchGeometryAtoms(list);
	}
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy