/* * 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); } } }