aboutsummaryrefslogtreecommitdiffstats
path: root/src/javax/media/j3d/GroupRetained.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/javax/media/j3d/GroupRetained.java')
-rw-r--r--src/javax/media/j3d/GroupRetained.java3207
1 files changed, 3207 insertions, 0 deletions
diff --git a/src/javax/media/j3d/GroupRetained.java b/src/javax/media/j3d/GroupRetained.java
new file mode 100644
index 0000000..84a6bc6
--- /dev/null
+++ b/src/javax/media/j3d/GroupRetained.java
@@ -0,0 +1,3207 @@
+/*
+ * 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<NodeRetained> children = new ArrayList<NodeRetained>(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<ArrayList<LightRetained>> 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<ArrayList<FogRetained>> 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<ArrayList<ModelClipRetained>> 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<ArrayList<AlternateAppearanceRetained>> altAppearances = null;
+
+
+ // indicates whether this Group node can be the target of a collision
+ boolean collisionTarget = false;
+
+// per child switchLinks
+ArrayList<ArrayList<NodeRetained>> childrenSwitchLinks = null;
+
+ // the immediate childIndex of a parentSwitchLink
+ int parentSwitchLinkChildIndex = -1;
+
+// per shared path ordered path data
+ArrayList<OrderedPath> 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<GroupRetained> 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<NodeRetained> 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<ArrayList<View>> 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<children.size(); i++) {
+ NodeRetained childi = children.get(i);
+ if(childi != null)
+ childi.childIndex++;
+ }
+ if(child==null) {
+ children.add(index, null);
+ return;
+ }
+
+ NodeRetained childr = (NodeRetained) child.retained;
+ childr.setParent(this);
+ children.add(index, childr);
+ checkSetLive(childr, index, null, 0, null);
+ if (this.source.isLive()) {
+ ((BranchGroupRetained)childr).isNew = true;
+ }
+ }
+
+ /**
+ * Removes the child at specified index.
+ * @param index which child to remove
+ */
+ void removeChild(int index) {
+
+ if (this.source.isLive()) {
+ universe.resetWaitMCFlag();
+ synchronized (universe.sceneGraphLock) {
+ NodeRetained childr = children.get(index);
+ doRemoveChild(index, null, 0);
+ universe.setLiveState.clear();
+ universe.notifyStructureChangeListeners(false, this.source, (BranchGroup)childr.source);
+ }
+ universe.waitForMC();
+ } else {
+ doRemoveChild(index, null, 0);
+ if (universe != null) {
+ synchronized (universe.sceneGraphLock) {
+ universe.setLiveState.clear();
+ }
+ }
+ }
+ dirtyBoundsCache();
+ }
+
+ /**
+ * Returns the index of the specified Node in this Group's list of Nodes
+ * @param Node whose index is desired
+ * @return index of the Node
+ */
+ int indexOfChild(Node child) {
+ if(child != null)
+ return children.indexOf((NodeRetained)child.retained);
+ else
+ return children.indexOf(null);
+ }
+
+ /**
+ * Removes the specified child from this Group's list of
+ * children. If the specified child is not found, the method returns
+ * quietly
+ *
+ * @param child to be removed
+ */
+ void removeChild(Node child) {
+ int i = indexOfChild(child);
+ if(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<Node> getAllChildren() {
+ Vector<Node> userChildren = new Vector<Node>(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<numMessages; i++) {
+ messages[i] = new J3dMessage();
+ messages[i].type = J3dMessage.INVALID_TYPE;
+ }
+
+ // Remove it from it's parents state
+ if (g == null) {
+ if (bg.locale != null) {
+ bg.locale.doRemoveBranchGraph(branchGroup,
+ messages, detachStartIndex);
+ }
+ } else {
+ g.doRemoveChild(g.children.indexOf(bg),
+ messages,
+ detachStartIndex);
+ }
+ }
+
+
+ // Add it to it's new parent
+ doAddChild(branchGroup, messages, attachStartIndex);
+
+ if (numMessages > 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<LightRetained> l = lights.get(hkIndex);
+ if (l != null) {
+ for (int i = 0; i < numLgt; i++) {
+ index = l.indexOf(removelight[i]);
+ l.remove(index);
+ }
+ }
+ }
+ else {
+ ArrayList<LightRetained> 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<ArrayList<LightRetained>> newLights;
+ if (!allocatedLights) {
+ allocatedLights = true;
+ if (lights != null) {
+ newLights = new ArrayList<ArrayList<LightRetained>>(lights.size());
+ int size = lights.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList<LightRetained> l = lights.get(i);
+ if (l != null) {
+ newLights.add(new ArrayList<LightRetained>(l));
+ }
+ else {
+ newLights.add(null);
+ }
+ }
+ }
+ else {
+ if (inSharedGroup) {
+ newLights = new ArrayList<ArrayList<LightRetained>>();
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ newLights.add(new ArrayList<LightRetained>());
+ }
+ }
+ else {
+ newLights = new ArrayList<ArrayList<LightRetained>>();
+ newLights.add(new ArrayList<LightRetained>());
+ }
+ }
+ lights = newLights;
+
+ }
+ scopingRefCount++;
+}
+ synchronized void removeLightScope() {
+ scopingRefCount--;
+ }
+
+
+synchronized void setFogScope() {
+ // Make group's own copy
+ ArrayList<ArrayList<FogRetained>> newFogs;
+ if (!allocatedFogs) {
+ allocatedFogs = true;
+ if (fogs != null) {
+ newFogs = new ArrayList<ArrayList<FogRetained>>(fogs.size());
+ int size = fogs.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList<FogRetained> l = fogs.get(i);
+ if (l != null) {
+ newFogs.add(new ArrayList<FogRetained>(l));
+ }
+ else {
+ newFogs.add(null);
+ }
+ }
+ }
+ else {
+ if (inSharedGroup) {
+ newFogs = new ArrayList<ArrayList<FogRetained>>();
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ newFogs.add(new ArrayList<FogRetained>());
+ }
+ }
+ else {
+ newFogs = new ArrayList<ArrayList<FogRetained>>();
+ newFogs.add(new ArrayList<FogRetained>());
+ }
+ }
+ fogs = newFogs;
+
+ }
+ scopingRefCount++;
+}
+ synchronized void removeFogScope() {
+ scopingRefCount--;
+ }
+
+
+synchronized void setMclipScope() {
+ // Make group's own copy
+ ArrayList<ArrayList<ModelClipRetained>> newMclips;
+ if (!allocatedMclips) {
+ allocatedMclips = true;
+ if (modelClips != null) {
+ newMclips = new ArrayList<ArrayList<ModelClipRetained>>(modelClips.size());
+ int size = modelClips.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList<ModelClipRetained> l = modelClips.get(i);
+ if (l != null) {
+ newMclips.add(new ArrayList<ModelClipRetained>(l));
+ }
+ else {
+ newMclips.add(null);
+ }
+ }
+ }
+ else {
+ if (inSharedGroup) {
+ newMclips = new ArrayList<ArrayList<ModelClipRetained>>();
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ newMclips.add(new ArrayList<ModelClipRetained>());
+ }
+ }
+ else {
+ newMclips = new ArrayList<ArrayList<ModelClipRetained>>();
+ newMclips.add(new ArrayList<ModelClipRetained>());
+ }
+ }
+ modelClips = newMclips;
+
+ }
+ scopingRefCount++;
+}
+ synchronized void removeMclipScope() {
+ scopingRefCount--;
+ }
+
+
+synchronized void setAltAppScope() {
+ // Make group's own copy
+ ArrayList<ArrayList<AlternateAppearanceRetained>> newAltApps;
+ if (!allocatedAltApps) {
+ allocatedAltApps = true;
+ if (altAppearances != null) {
+ newAltApps = new ArrayList<ArrayList<AlternateAppearanceRetained>>(altAppearances.size());
+ int size = altAppearances.size();
+ for (int i = 0; i < size; i++) {
+ ArrayList<AlternateAppearanceRetained> l = altAppearances.get(i);
+ if (l != null) {
+ newAltApps.add(new ArrayList<AlternateAppearanceRetained>(l));
+ }
+ else {
+ newAltApps.add(null);
+ }
+ }
+ }
+ else {
+ if (inSharedGroup) {
+ newAltApps = new ArrayList<ArrayList<AlternateAppearanceRetained>>();
+ for (int i = 0; i < localToVworldKeys.length; i++) {
+ newAltApps.add(new ArrayList<AlternateAppearanceRetained>());
+ }
+ }
+ else {
+ newAltApps = new ArrayList<ArrayList<AlternateAppearanceRetained>>();
+ newAltApps.add(new ArrayList<AlternateAppearanceRetained>());
+ }
+ }
+ 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<LightRetained> l = lights.get(hkIndex);
+ if (l != null) {
+ for (int i = 0; i < numLgts; i++) {
+ l.add(addlight[i]);
+ }
+ }
+ }
+ else {
+ ArrayList<LightRetained> 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<FogRetained> l = fogs.get(hkIndex);
+ if (l != null) {
+ l.add(fog);
+ }
+ }
+ else {
+ ArrayList<FogRetained> 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<ModelClipRetained> l = modelClips.get(hkIndex);
+ if (l != null) {
+ l.add(modelClip);
+ }
+ }
+ else {
+ ArrayList<ModelClipRetained> 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<AlternateAppearanceRetained> l = altAppearances.get(hkIndex);
+ if (l != null) {
+ l.add(altApp);
+ }
+ }
+ else {
+ ArrayList<AlternateAppearanceRetained> 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<FogRetained> l = fogs.get(hkIndex);
+ if (l != null) {
+ index = l.indexOf(fog);
+ l.remove(index);
+ }
+ }
+ else {
+ ArrayList<FogRetained> 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<ModelClipRetained> l = modelClips.get(hkIndex);
+ if (l != null) {
+ index = l.indexOf(modelClip);
+ l.remove(index);
+ }
+ }
+ else {
+ ArrayList<ModelClipRetained> 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<AlternateAppearanceRetained> l = altAppearances.get(hkIndex);
+ if (l != null) {
+ index = l.indexOf(altApp);
+ l.remove(index);
+ }
+ }
+ else {
+ ArrayList<AlternateAppearanceRetained> 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<numPath; i++) {
+ ct = ti.getCachedTargets(TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ targets.addNode(mirrorGroup.get(i),
+ Targets.GRP_TARGETS);
+ newCtArr[i] = targets.snapShotAdd(ct);
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+
+
+ // update target threads and propagate change to above
+ // nodes in scene graph
+ ti.updateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr);
+ ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr, -1);
+ }
+
+ message.type = J3dMessage.INSERT_NODES;
+ message.args[0] = mirrorGroup.toArray();
+ message.args[1] = ti;
+ message.args[2] = newCtArr;
+
+ } else {
+ TargetsInterface ti =
+ getClosestTargetsInterface(TargetsInterface.TRANSFORM_TARGETS);
+ if (ti != null) {
+
+ // update targets
+ Targets targets = new Targets();
+ CachedTargets ct;
+ numPath = mirrorGroup.size();
+ newCtArr = new CachedTargets[numPath];
+ for (int i=0; i<numPath; i++) {
+ ct = ti.getCachedTargets(TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ targets.addNode(mirrorGroup.get(i),
+ Targets.GRP_TARGETS);
+ //Note snapShotRemove calls targets.clearNode()
+ newCtArr[i] = targets.snapShotRemove(ct);
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ // update target threads and propagate change to above
+ // nodes in scene graph
+ ti.updateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr);
+ ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr, -1);
+ }
+
+ message.type = J3dMessage.REMOVE_NODES;
+ message.args[0] = mirrorGroup.toArray();
+ message.args[1] = ti;
+ message.args[2] = newCtArr;
+ mirrorGroup = null; // for gc
+ }
+ VirtualUniverse.mc.processMessage(message);
+ }
+ }
+
+ boolean getAlternateCollisionTarget() {
+ return collisionTarget;
+ }
+
+
+
+ /**
+ * This checks is setLive needs to be called. If it does, it gets the
+ * needed info and calls it.
+ */
+ void checkSetLive(NodeRetained child, int childIndex, J3dMessage messages[],
+ int messageIndex, NodeRetained linkNode) {
+ checkSetLive(child, childIndex, localToVworldKeys, inSharedGroup,
+ messages, messageIndex, linkNode);
+ }
+
+
+ /**
+ * This checks is setLive needs to be called. If it does, it gets the
+ * needed info and calls it.
+ */
+ void checkSetLive(NodeRetained child, int childIndex, HashKey keys[],
+ boolean isShared, J3dMessage messages[],
+ int messageIndex, NodeRetained linkNode) {
+
+ SceneGraphObject me = this.source;
+ SetLiveState s;
+ J3dMessage createMessage;
+ boolean sendMessages = false;
+ boolean sendOGMessage = true;
+ boolean sendVSGMessage = true;
+
+ if (me.isLive()) {
+
+ s = universe.setLiveState;
+ s.reset(locale);
+ s.refCount = refCount;
+ s.inSharedGroup = isShared;
+ s.inBackgroundGroup = inBackgroundGroup;
+ s.inViewSpecificGroup = inViewSpecificGroup;
+ s.geometryBackground = geometryBackground;
+ s.keys = keys;
+ s.viewLists = viewLists;
+ s.parentBranchGroupPaths = branchGroupPaths;
+ // Note that there is no need to clone individual
+ // branchGroupArray since they will get replace (not append)
+ // by creating a new reference in child's group.
+ s.branchGroupPaths = new ArrayList<BranchGroupRetained[]>(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<ViewSpecificGroupRetained>();
+ s.changedViewList = new ArrayList<ArrayList<View>>();
+ s.keyList = new int[10];
+ s.viewScopedNodeList = new ArrayList<NodeRetained>();
+ s.scopedNodesViewList = new ArrayList<ArrayList<View>>();
+ }
+
+ 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<ViewSpecificGroupRetained>();
+ s.changedViewList = new ArrayList<ArrayList<View>>();
+ s.keyList = new int[10];
+ s.viewScopedNodeList = new ArrayList<NodeRetained>();
+ s.scopedNodesViewList = new ArrayList<ArrayList<View>>();
+ }
+
+ 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<numPaths; i++) {
+ if (s.transformLevels[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<s.keys.length; i++) {
+
+ if (s.transformTargets[i] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ newCtArr[i] =
+ s.transformTargets[i].snapShotAdd(ct);
+ }
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+ } else {
+
+ CachedTargets ct;
+ int hkIndex;
+
+ newCtArr = new CachedTargets[localToVworld.length];
+
+ if (! inSharedGroup) {
+ if (s.transformTargets[0] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, 0, -1);
+ if (ct != null) {
+ newCtArr[0] =
+ s.transformTargets[0].snapShotRemove(ct);
+ }
+ } else {
+ newCtArr[0] = null;
+ }
+ } else {
+ for (int i=0; i<s.keys.length; i++) {
+ if (s.transformTargets[i] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.TRANSFORM_TARGETS, i, -1);
+ if (ct != null) {
+ newCtArr[i] =
+ s.transformTargets[i].snapShotRemove(ct);
+ }
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+ }
+ // update target threads and propagate change to above
+ // nodes in scene graph
+
+ ti.updateTargetThreads(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr);
+ ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS,
+ newCtArr, -1);
+
+ }
+ return newCtArr;
+ }
+
+ TargetsInterface initSwitchStates(SetLiveState s,
+ NodeRetained parentNode, NodeRetained childNode,
+ NodeRetained linkNode, boolean isSetLive) {
+ NodeRetained child;
+ NodeRetained parent;
+ int i,j;
+
+ findSwitchInfo(s, parentNode, childNode, linkNode);
+ TargetsInterface ti = getClosestTargetsInterface(
+ TargetsInterface.SWITCH_TARGETS);
+ if (ti != null) {
+ Targets[] newTargets = null;
+ int numPaths = (inSharedGroup)? s.keys.length : 1;
+ newTargets = new Targets[numPaths];
+ for(i=0; i<numPaths; i++) {
+ if (s.switchLevels[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<SwitchState>(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<s.keys.length; i++) {
+ if (s.switchTargets[i] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.SWITCH_TARGETS, i,
+ parentSwitchLinkChildIndex);
+ if (ct != null) {
+ newCtArr[i] =
+ s.switchTargets[i].snapShotAdd(ct);
+ } else {
+ newCtArr[i] =
+ s.switchTargets[i].snapShotInit();
+ }
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+ ti.resetCachedTargets(TargetsInterface.SWITCH_TARGETS,
+ newCtArr, parentSwitchLinkChildIndex);
+ if (ti instanceof SwitchRetained) {
+ ((SwitchRetained)ti).traverseSwitchParent();
+ } else if (ti instanceof SharedGroupRetained) {
+ ((SharedGroupRetained)ti).traverseSwitchParent();
+ }
+ } else {
+ CachedTargets ct;
+
+ CachedTargets[] newCtArr =
+ new CachedTargets[localToVworld.length];
+
+ if (! inSharedGroup) {
+ if (s.switchTargets[0] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.SWITCH_TARGETS, 0,
+ parentSwitchLinkChildIndex);
+ if (ct != null) {
+ newCtArr[0] =
+ s.switchTargets[0].snapShotRemove(ct);
+ }
+ } else {
+ newCtArr[0] = null;
+ }
+ } else {
+ for (int i=0; i<s.keys.length; i++) {
+ if (s.switchTargets[i] != null) {
+ ct = ti.getCachedTargets(
+ TargetsInterface.SWITCH_TARGETS, i,
+ parentSwitchLinkChildIndex);
+
+ if (ct != null) {
+ newCtArr[i] =
+ s.switchTargets[i].snapShotRemove(ct);
+ }
+ } else {
+ newCtArr[i] = null;
+ }
+ }
+ }
+ ti.resetCachedTargets(TargetsInterface.SWITCH_TARGETS,
+ newCtArr, parentSwitchLinkChildIndex);
+ }
+ }
+ }
+
+ void appendChildrenData() {
+ }
+ void insertChildrenData(int index) {
+ }
+ void removeChildrenData(int index) {
+ }
+
+ TargetsInterface getClosestTargetsInterface(int type) {
+ return (type == TargetsInterface.TRANSFORM_TARGETS)?
+ (TargetsInterface)parentTransformLink:
+ (TargetsInterface)parentSwitchLink;
+ }
+
+
+ @Override
+ synchronized void updateLocalToVworld() {
+ // For each children call .....
+ for (int i=children.size()-1; 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<ArrayList<LightRetained>> savedScopedLights = s.lights;
+ ArrayList<ArrayList<FogRetained>> savedScopedFogs = s.fogs;
+ ArrayList<ArrayList<AlternateAppearanceRetained>> savedScopedAltApps = s.altAppearances;
+ ArrayList<ArrayList<ModelClipRetained>> 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<BranchGroupRetained[]> oldBranchGroupPaths = s.branchGroupPaths;
+ setScopingInfo(s);
+
+
+ if (!(this instanceof ViewSpecificGroupRetained)) {
+ viewLists = s.viewLists;
+ }
+
+ for (i=0; i<nchildren; i++) {
+ NodeRetained child = children.get(i);
+
+ // Restore old values before child.setLive(s)
+ System.arraycopy(oldpickableArray, 0, workingpickableArray, 0,
+ oldpickableArray.length);
+ System.arraycopy(oldcollidableArray, 0, workingcollidableArray, 0,
+ oldcollidableArray.length);
+ s.pickable = workingpickableArray;
+ s.collidable = workingcollidableArray;
+ // s.branchGroupPaths will be modified by child setLive()
+ // so we have to restore it every time.
+ s.parentBranchGroupPaths = branchGroupPaths;
+ s.branchGroupPaths = new ArrayList<BranchGroupRetained[]>(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<LightRetained> l = lights.get(hkIndex);
+ ArrayList<LightRetained> 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<LightRetained> l = lights.get(0);
+ ArrayList<LightRetained> 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<FogRetained> l = fogs.get(hkIndex);
+ ArrayList<FogRetained> 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<FogRetained> l = fogs.get(0);
+ ArrayList<FogRetained> 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<ModelClipRetained> l = modelClips.get(hkIndex);
+ ArrayList<ModelClipRetained> 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<ModelClipRetained> l = modelClips.get(0);
+ ArrayList<ModelClipRetained> 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<AlternateAppearanceRetained> l = altAppearances.get(hkIndex);
+ ArrayList<AlternateAppearanceRetained> 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<AlternateAppearanceRetained> l = altAppearances.get(0);
+ ArrayList<AlternateAppearanceRetained> 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<GroupRetained>();
+ }
+ 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<NodeRetained>(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<ArrayList<LightRetained>> savedParentLights = s.lights;
+ if (allocatedLights) {
+ s.lights = lights;
+ }
+
+ ArrayList<ArrayList<FogRetained>> savedParentFogs = s.fogs;
+ if (allocatedFogs) {
+ s.fogs = fogs;
+ }
+
+ ArrayList<ArrayList<ModelClipRetained>> savedParentMclips = s.modelClips;
+ if (allocatedMclips) {
+ s.modelClips = modelClips;
+ }
+
+ ArrayList<ArrayList<AlternateAppearanceRetained>> 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<LightRetained> l = savedParentLights.get(hkIndex);
+ ArrayList<LightRetained> gl = lights.get(hkIndex);
+ if (l != null) {
+ size = l.size();
+ for (k = 0; k < size; k++) {
+ gl.remove(l.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList<LightRetained> l = savedParentLights.get(0);
+ ArrayList<LightRetained> 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<FogRetained> l = savedParentFogs.get(hkIndex);
+ ArrayList<FogRetained> gl = fogs.get(hkIndex);
+ if (l != null) {
+ size = l.size();
+ for (k = 0; k < size; k++) {
+ gl.remove(l.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList<FogRetained> 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<ModelClipRetained> l = savedParentMclips.get(hkIndex);
+ ArrayList<ModelClipRetained> gl = modelClips.get(hkIndex);
+ if (l != null) {
+ size = l.size();
+ for (k = 0; k < size; k++) {
+ gl.remove(l.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList<ModelClipRetained> 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<AlternateAppearanceRetained> l = savedParentAltApps.get(hkIndex);
+ ArrayList<AlternateAppearanceRetained> gl = altAppearances.get(hkIndex);
+ if (l != null) {
+ size = l.size();
+ for (k = 0; k < size; k++) {
+ gl.remove(l.get(k));
+ }
+ }
+
+ }
+ }
+ else {
+ ArrayList<AlternateAppearanceRetained> 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<NodeRetained> 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<NodeRetained> 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<GroupRetained>();
+
+ 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<localToVworldKeys.length; i++) {
+ s.switchLevels[i] = -1;
+ s.closestSwitchParents[i] = null;
+ s.closestSwitchIndices[i] = -1;
+ }
+
+ for (i=0; i < localToVworldKeys.length; i++) {
+ child = (linkNode == null)? childNode: linkNode;
+ parent = parentNode;
+ key = new HashKey(localToVworldKeys[i]);
+
+ while (parent != null) {
+
+ if (parent instanceof SwitchRetained) {
+ s.switchLevels[i]++;
+ if (s.closestSwitchParents[i] == null) {
+ s.closestSwitchParents[i] = (SwitchRetained)parent;
+ s.closestSwitchIndices[i] =
+ ((SwitchRetained)parent).switchIndexCount++;
+
+ }
+ if (parentSwitchLinkChildIndex == -1) {
+ parentSwitchLinkChildIndex =
+ ((GroupRetained)parent).children.indexOf(child);
+ }
+ } else if (parent instanceof SharedGroupRetained) {
+ String nodeId = key.getLastNodeId();
+ Vector<NodeRetained> 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<blArr.length; i++) {
+ users = ((BoundingLeafRetained)blArr[i]).users;
+ synchronized(users) {
+ blUsers.addAll(users);
+ }
+ }
+ }
+
+ // recursively found all geometryAtoms under this Group
+ @Override
+ void searchGeometryAtoms(UnorderList list) {
+ for (int i = children.size()-1; i >=0; i--) {
+ NodeRetained child = children.get(i);
+ child.searchGeometryAtoms(list);
+ }
+ }
+}