/* * Copyright 1998-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.Arrays; import javax.vecmath.Vector3d; /** * A geometry structure is a object that organizes geometries * and bounds. */ class GeometryStructure extends J3dStructure { /** * used during Transform Processing */ UpdateTargets targets = null; /** * A multiple read single write Lock to sychronize access into this * GeometryStructure. * To prevent deadlock a call to read/write lock must end with a read/write * unlock respectively. */ private MRSWLock lock = null; /** * A lock object to prevent concurrent getVisibleBHTree query. */ private Object visLock = new Object(); /** * A lock object to prevent concurrent collideEntryList, * collideExitList using toArray() in BehaviorStructure buildTree() * while clearMirror() is invoked in GeometryStructure removeNode() */ private Object collideListLock = new Object(); /** * Binary Hull Tree structure for handling geometry atoms. * Do not change the following private variables to public, their access * need to synchronize via lock. */ private BHTree[] bhTreeArr = null; private int bhTreeCount; private int bhTreeMax; private int bhTreeBlockSize = 5; /** * The array of BHNode, a data pool, for passing data between GS and BHTrees. * Do not change the following private variables to public, their access * need to synchronize via lock. */ private BHNode[] bhNodeArr = null; private int bhNodeCount, bhNodeMax; private int bhNodeBlockSize = 50; // Support for multi-locale. private Vector3d localeTrans = new Vector3d(); //The lists of wakeupCriterion object currently in collision. WakeupIndexedList collideEntryList; WakeupIndexedList collideExitList; WakeupIndexedList collideMovementList; // The lists of wakeupCriterion objects that GeometryStructure keeps WakeupIndexedList wakeupOnCollisionEntry; WakeupIndexedList wakeupOnCollisionExit; WakeupIndexedList wakeupOnCollisionMovement; // When Shape insert/remove for WakeupOnCollisionxxx() using // Group node and USE_GEOMETRY, we need to reevaluate the // cache geometryAtoms list. boolean reEvaluateWakeupCollisionGAs; private boolean transformMsg = false; /** * Constructor. */ GeometryStructure(VirtualUniverse u) { super(u, J3dThread.UPDATE_GEOMETRY); bhNodeCount = 0; bhNodeMax = bhNodeBlockSize; bhNodeArr = new BHNode[bhNodeMax]; bhTreeMax = 1; bhTreeArr = new BHTree[bhTreeMax]; bhTreeCount=0; lock = new MRSWLock(); collideEntryList = new WakeupIndexedList(WakeupOnCollisionEntry.class, WakeupOnCollisionEntry.COLLIDEENTRY_IN_BS_LIST, u); collideExitList = new WakeupIndexedList(WakeupOnCollisionExit.class, WakeupOnCollisionExit.COLLIDEEXIT_IN_BS_LIST, u); collideMovementList = new WakeupIndexedList(WakeupOnCollisionMovement.class, WakeupOnCollisionMovement.COLLIDEMOVE_IN_BS_LIST, u); wakeupOnCollisionEntry = new WakeupIndexedList(WakeupOnCollisionEntry.class, WakeupOnCollisionEntry.COND_IN_GS_LIST, u); wakeupOnCollisionExit = new WakeupIndexedList(WakeupOnCollisionExit.class, WakeupOnCollisionExit.COND_IN_GS_LIST, u); wakeupOnCollisionMovement = new WakeupIndexedList(WakeupOnCollisionMovement.class, WakeupOnCollisionMovement.COND_IN_GS_LIST, u); } @Override void processMessages(long referenceTime) { J3dMessage m; J3dMessage[] messages = getMessages(referenceTime); int nMsg = getNumMessage(); if (nMsg > 0) { reEvaluateWakeupCollisionGAs = false; for (int i=0; i < nMsg; i++) { lock.writeLock(); m = messages[i]; switch (m.type) { case J3dMessage.TRANSFORM_CHANGED: transformMsg = true; break; case J3dMessage.SWITCH_CHANGED: processSwitchChanged(m); // may need to process dirty switched-on transform if (universe.transformStructure.getLazyUpdate()) { transformMsg = true; } break; case J3dMessage.INSERT_NODES: insertNodes((Object[])m.args[0]); reEvaluateWakeupCollisionGAs = true; break; case J3dMessage.REMOVE_NODES: removeNodes(m); reEvaluateWakeupCollisionGAs = true; break; case J3dMessage.SHAPE3D_CHANGED: { int comp = ((Integer)m.args[1]).intValue(); if (comp == Shape3DRetained.GEOMETRY_CHANGED) { m.args[0] = m.args[2]; removeNodes(m); insertNodes((Object[])m.args[3]); reEvaluateWakeupCollisionGAs = true; } else if (comp == Shape3DRetained.APPEARANCE_CHANGED) { processVisibleChanged(m.args[2], ((GeometryAtom[]) m.args[3])); } break; } case J3dMessage.TEXT3D_DATA_CHANGED: removeNodes(m); insertNodes((Object[])m.args[1]); break; case J3dMessage.TEXT3D_TRANSFORM_CHANGED: processBoundsChanged((Object []) m.args[0], false); break; case J3dMessage.MORPH_CHANGED: { int comp = ((Integer)m.args[1]).intValue(); if (comp == MorphRetained.GEOMETRY_CHANGED) { processBoundsChanged((Object []) m.args[3], false); } else if (comp == MorphRetained.APPEARANCE_CHANGED) { processVisibleChanged(m.args[2], ((GeometryAtom[]) m.args[3])); } break; } case J3dMessage.REGION_BOUND_CHANGED: case J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED: // Only set this flag, when bounds might be empty. processBoundsChanged((Object [])m.args[0], false); break; case J3dMessage.GEOMETRY_CHANGED: // System.err.println("J3dMessage.GEOMETRY_CHANGED"); processBoundsChanged((Object []) m.args[0], false); break; case J3dMessage.RENDERINGATTRIBUTES_CHANGED: processVisibleChanged(m.args[2], ((GeometryAtom[]) m.args[3])); break; } lock.writeUnlock(); m.decRefcount(); } if (transformMsg) { targets = universe.transformStructure.getTargetList(); lock.writeLock(); processTransformChanged(targets); lock.writeUnlock(); transformMsg = false; targets = null; } Arrays.fill(messages, 0, nMsg, null); } processCollisionDetection(); } private int getBHTreeIndex(Locale locale) { int i; for (i=0; i< bhTreeCount; i++) { if (bhTreeArr[i].locale == locale) return i; } // Can't find will return -1 so that other // program know this return -1; } private int getOrAddBHTreeIndex(Locale locale) { int i; for (i=0; i< bhTreeCount; i++) { if (bhTreeArr[i].locale == locale) return i; } if (bhTreeCount >= bhTreeMax) { // allocate a bigger array here.... if (J3dDebug.devPhase) J3dDebug.doDebug(J3dDebug.geometryStructure, J3dDebug.LEVEL_2, "Expanding bhTreeArr array ...\n"); bhTreeMax += bhTreeBlockSize; BHTree[] oldBhTreeArr = bhTreeArr; bhTreeArr = new BHTree[bhTreeMax]; System.arraycopy(oldBhTreeArr, 0, bhTreeArr, 0, oldBhTreeArr.length); } bhTreeArr[bhTreeCount] = new BHTree(locale); bhTreeCount++; return i; } private void clearBhNodeArr() { // Issue 353: set all elements to null so we don't leak // NOTE: we really should change this to be an ArrayList, but that // would be a less localized change. Consider for 1.6.0. for (int i = 0; i < bhNodeCount; i++) { bhNodeArr[i] = null; } bhNodeCount = 0; } private void addToBhNodeArr(BHNode bhNode) { // Add to bhNodeArr. if (bhNodeCount >= bhNodeMax) { bhNodeMax += bhNodeBlockSize; BHNode[] oldbhNodeArr = bhNodeArr; bhNodeArr = new BHNode[bhNodeMax]; System.arraycopy(oldbhNodeArr, 0, bhNodeArr, 0, oldbhNodeArr.length); } bhNodeArr[bhNodeCount] = bhNode; bhNodeCount++; } private void processVisibleChanged(Object valueObj, GeometryAtom[] gaArr) { boolean visible = true; // Default is true. int i, treeIndex; if ((gaArr == null) || (gaArr.length < 1)) return; treeIndex = getBHTreeIndex(gaArr[0].locale); visible = ((Boolean)valueObj).booleanValue(); for ( i=gaArr.length-1; i>=0; i--) { gaArr[i].visible = visible; } } private void insertNodes(Object[] nodes) { Object node; GeometryAtom geomAtom; clearBhNodeArr(); // System.err.println("GS : nodes.length is " + nodes.length); for (int i=0; i=0; j--) { wentry = wentryArr[j]; if (wentry.behav == behav) { collideEntryList.remove(wentry); } } WakeupOnCollisionExit wexit; WakeupOnCollisionExit wexitArr[] = (WakeupOnCollisionExit []) collideExitList.toArray(); for (int j=collideExitList.arraySize()-1; j>=0; j--) { wexit = wexitArr[j]; if (wexit.behav == behav) { collideExitList.remove(wexit); } } } } } if (bhNodeCount < 1) { return; } int index = getBHTreeIndex(((BHLeafNode) bhNodeArr[0]).getLocale()); if (index < 0) { // Issue 353: must clear array after we are done with it clearBhNodeArr(); return; } BHTree currTree = bhTreeArr[index]; currTree.delete(bhNodeArr, bhNodeCount); // Issue 353: must clear array after we are done with it clearBhNodeArr(); // It is safe to do it here since only GeometryStructure // thread invoke wakeupOnCollisionEntry/Exit .toArray() wakeupOnCollisionEntry.clearMirror(); wakeupOnCollisionMovement.clearMirror(); wakeupOnCollisionExit.clearMirror(); synchronized (collideListLock) { collideEntryList.clearMirror(); collideExitList.clearMirror(); } } private void processBoundsChanged(Object[] nodes, boolean transformChanged) { int index; Object node; clearBhNodeArr(); for (int i = 0; i < nodes.length; i++) { node = nodes[i]; if (node instanceof GeometryAtom) { synchronized (node) { GeometryAtom geomAtom = (GeometryAtom) node; if (geomAtom.bhLeafNode != null) { addToBhNodeArr(geomAtom.bhLeafNode); } } } else if (node instanceof GroupRetained) { GroupRetained group = (GroupRetained) node; if (group.nodeType != NodeRetained.SWITCH) { synchronized (node) { if (group.bhLeafNode != null) { addToBhNodeArr(group.bhLeafNode); } } } } } if (bhNodeCount < 1) { return; } index = getBHTreeIndex(((BHLeafNode)bhNodeArr[0]).getLocale()); if (index >= 0) { bhTreeArr[index].boundsChanged(bhNodeArr, bhNodeCount); } // Issue 353: must clear array after we are done with it clearBhNodeArr(); } private void processTransformChanged(UpdateTargets targets) { int i, j, index; Object[] nodes, nodesArr; UnorderList arrList; int size; clearBhNodeArr(); arrList = targets.targetList[Targets.GEO_TARGETS]; if (arrList != null) { size = arrList.size(); nodesArr = arrList.toArray(false); for (j = 0; j < size; j++) { nodes = (Object[])nodesArr[j]; for (i = 0; i < nodes.length; i++) { GeometryAtom geomAtom = (GeometryAtom) nodes[i]; synchronized (geomAtom) { if (geomAtom.bhLeafNode != null) { addToBhNodeArr(geomAtom.bhLeafNode); } } } } } arrList = targets.targetList[Targets.GRP_TARGETS]; if (arrList != null) { size = arrList.size(); nodesArr = arrList.toArray(false); for ( j = 0; j < size; j++) { nodes = (Object[])nodesArr[j]; for ( i = 0; i < nodes.length; i++) { GroupRetained group = (GroupRetained) nodes[i]; if (group.nodeType != NodeRetained.SWITCH) { synchronized (group) { if (group.bhLeafNode != null) { addToBhNodeArr(group.bhLeafNode); } } } } } } if (bhNodeCount < 1) { return; } index = getBHTreeIndex(((BHLeafNode)bhNodeArr[0]).getLocale()); if (index >= 0) { bhTreeArr[index].boundsChanged(bhNodeArr, bhNodeCount); } // Issue 353: must clear array after we are done with it clearBhNodeArr(); } // This method is called by RenderBin to get a array of possibly visible // sub-trees. // bhTrees mustn't be null. // Return true if bhTree's root in encompass by frustumBBox. boolean getVisibleBHTrees(RenderBin rBin, BoundingBox frustumBBox, Locale locale, long referenceTime, boolean stateChanged, int visibilityPolicy) { int i, j; boolean unviInFB = true; // System.err.println("GeometryStructure : view's locale is " + locale); lock.readLock(); // Issue 353: create a new array list each time rather than passing it // in. This will not generate too much garbage, since we only call // this once per frame and it is very short-lived. ArrayList bhTrees = new ArrayList(); if (bhTreeCount == 1) { // For debugging only. if (J3dDebug.devPhase) { if (J3dDebug.doDebug(J3dDebug.geometryStructure, J3dDebug.LEVEL_2)) { System.err.println("GeometryStructure : In simple case"); System.err.println("GeometryStructure : view's locale is " + locale); System.err.println("GeometryStructure : bhTreeArr[0].locale is " + bhTreeArr[0].locale); } } // One locale case - Lets make the simple case fast. synchronized(visLock) { unviInFB = bhTreeArr[0].getVisibleBHTrees(rBin, bhTrees, frustumBBox, referenceTime, stateChanged, visibilityPolicy, true); } } else { // Multiple locale case. // For debugging only. if (J3dDebug.devPhase) J3dDebug.doDebug(J3dDebug.geometryStructure, J3dDebug.LEVEL_2, "GeometryStructure : bhTreeCount is " + universe.geometryStructure.bhTreeCount + " view's locale is " + locale + "\n"); BoundingBox localeFrustumBBox = new BoundingBox(); synchronized(visLock) { for (j=0; j=0; i--) { wentry = collideEntryArr[i]; if ((wentry.behav == w.behav) && (wentry.geometryAtoms == w.geometryAtoms)) { collideEntryList.remove(i); needTrigger = false; break; } } } // add to wakeup list wakeupOnCollisionEntry.add(w); w.updateCollisionBounds(false); // check for collision and triggered event BHLeafInterface target = collide(w.behav.locale, w.accuracyMode, w.geometryAtoms, w.vwcBounds, w.boundingLeaf, w.armingNode, null); if (target != null) { collideEntryList.add(w); w.setTarget(target); } if ((target != null) && (needTrigger)) { w.setTriggered(); } } void addWakeupOnCollision(WakeupOnCollisionExit w) { // Cleanup, since collideExitList did not remove // its condition in removeWakeupOnCollision boolean needTrigger = true; synchronized (collideListLock) { WakeupOnCollisionExit collideExitArr[] = (WakeupOnCollisionExit []) collideExitList.toArray(); WakeupOnCollisionExit wexit; for (int i=collideExitList.arraySize()-1; i>=0; i--) { wexit = collideExitArr[i]; if ((wexit.behav == w.behav) && (wexit.geometryAtoms == w.geometryAtoms)) { collideExitList.remove(i); needTrigger = false; break; } } } // add condition wakeupOnCollisionExit.add(w); w.updateCollisionBounds(false); BHLeafInterface target = collide(w.behav.locale, w.accuracyMode, w.geometryAtoms, w.vwcBounds, w.boundingLeaf, w.armingNode, null); if (target != null) { // store the target that cause this condition to collide // this is used when this condition is triggered. w.setTarget(target); collideExitList.add(w); } if (!needTrigger) { return; } // see if the matching wakeupOnCollisionEntry // condition exists synchronized (collideListLock) { WakeupOnCollisionEntry collideEntryArr[] = (WakeupOnCollisionEntry []) collideEntryList.toArray(); WakeupOnCollisionEntry wentry; for (int i=collideEntryList.arraySize()-1; i>=0; i--) { wentry = collideEntryArr[i]; if ((wentry.behav == w.behav) && (wentry.geometryAtoms == w.geometryAtoms)) { // Should not call collideEntryList.remove(i); // Otherwise wakeupOn for Entry case may call several // time at when initialize if collide if (target == null) { w.setTriggered(); } break; } } } } void addWakeupOnCollision(WakeupOnCollisionMovement w) { wakeupOnCollisionMovement.add(w); w.updateCollisionBounds(false); BHLeafInterface target = collide(w.behav.locale, w.accuracyMode, w.geometryAtoms, w.vwcBounds, w.boundingLeaf, w.armingNode, w); if (target != null) { w.setTarget(target); collideMovementList.add(w); } } void removeWakeupOnCollision(WakeupOnCollisionEntry wentry) { wakeupOnCollisionEntry.remove(wentry); // No need to remove collideEntry, it is used next time // when WakeupOnExitCollision is added to determine // whether to trigger it. } void removeWakeupOnCollision(WakeupOnCollisionExit wexit) { wakeupOnCollisionExit.remove(wexit); // No need to remove collideExit, it is used next time // when WakeupOnExitCollision is added to determine // whether to trigger it. } void removeWakeupOnCollision(WakeupOnCollisionMovement wmovement) { wakeupOnCollisionMovement.remove(wmovement); collideMovementList.remove(wmovement); // remove if exists } /** * This method test all wakeupOnCollision list and trigger the * condition if collision occurs. */ void processCollisionDetection() { int i, idx; BHLeafInterface target; // handle WakeupOnCollisionEntry WakeupOnCollisionEntry wentry; WakeupOnCollisionEntry wentryArr[] = (WakeupOnCollisionEntry []) wakeupOnCollisionEntry.toArray(); for (i = wakeupOnCollisionEntry.arraySize()-1; i >=0; i--) { wentry = wentryArr[i]; wentry.updateCollisionBounds(reEvaluateWakeupCollisionGAs); target = collide(wentry.behav.locale, wentry.accuracyMode, wentry.geometryAtoms, wentry.vwcBounds, wentry.boundingLeaf, wentry.armingNode, null); idx = collideEntryList.indexOf(wentry); if (target != null) { if (idx < 0) { collideEntryList.add(wentry); wentry.setTarget(target); wentry.setTriggered(); } } else { if (idx >= 0) { collideEntryList.remove(idx); } } } // handle WakeupOnCollisionMovement WakeupOnCollisionMovement wmove; WakeupOnCollisionMovement wmoveArr[] = (WakeupOnCollisionMovement []) wakeupOnCollisionMovement.toArray(); for (i = wakeupOnCollisionMovement.arraySize()-1; i >=0; i--) { wmove = wmoveArr[i]; wmove.updateCollisionBounds(reEvaluateWakeupCollisionGAs); target = collide(wmove.behav.locale, wmove.accuracyMode, wmove.geometryAtoms, wmove.vwcBounds, wmove.boundingLeaf, wmove.armingNode, wmove); idx = collideMovementList.indexOf(wmove); if (target != null) { if (idx < 0) { collideMovementList.add(wmove); wmove.setTarget(target); } else { if (!wmove.duplicateEvent) { wmove.setTriggered(); } } } else { if (idx >= 0) { collideMovementList.remove(idx); wmove.lastSrcBounds = null; wmove.lastDstBounds = null; } } } // Finally, handle WakeupOnCollisionExit WakeupOnCollisionExit wexit; WakeupOnCollisionExit wexitArr[] = (WakeupOnCollisionExit []) wakeupOnCollisionExit.toArray(); for (i = wakeupOnCollisionExit.arraySize()-1; i >=0; i--) { wexit = wexitArr[i]; wexit.updateCollisionBounds(reEvaluateWakeupCollisionGAs); target = collide(wexit.behav.locale, wexit.accuracyMode, wexit.geometryAtoms, wexit.vwcBounds, wexit.boundingLeaf, wexit.armingNode, null); idx = collideExitList.indexOf(wexit); if (target != null) { if (idx < 0) { collideExitList.add(wexit); wexit.setTarget(target); } } else { if (idx >= 0) { collideExitList.remove(idx); wexit.setTriggered(); } } } } /** * Check for duplicate WakeupOnCollisionMovement event. * We don't want to continue deliver event even though the * two colliding object did not move but this Geometry update * thread continue to run due to transform change in others * shape not in collision. */ void checkDuplicateEvent(WakeupOnCollisionMovement wmove, Bounds bound, BHLeafInterface hitNode) { Bounds hitBound; if ((wmove.lastSrcBounds != null) && wmove.lastSrcBounds.equals(bound)) { if (hitNode instanceof GeometryAtom) { hitBound = ((GeometryAtom) hitNode).source.vwcBounds; } else { hitBound = ((GroupRetained) hitNode).collisionVwcBounds; } if ((wmove.lastDstBounds != null) && wmove.lastDstBounds.equals(hitBound)) { wmove.duplicateEvent = true; } else { wmove.duplicateEvent = false; wmove.lastDstBounds = (Bounds) hitBound.clone(); } } else { wmove.duplicateEvent = false; wmove.lastSrcBounds = (Bounds) bound.clone(); } } /** * check if either the geomAtoms[] or * bound or boundingLeaf collide with BHTree. * Only one of geomAtoms, bound, boundingLeaf is non-null. * If accurancyMode is USE_GEOMETRY, object geometry is used, * otherwise object bounding box is used for collision * detection. * In case of GROUP & BOUND, the armingNode is used * to tell whether the colliding Group is itself or not. * Also in case GROUP, geomAtoms is non-null if USE_GEOMETRY. * If cond != null, it must be instanceof WakeupOnCollisionMovement */ BHLeafInterface collide(Locale locale, int accurancyMode, UnorderList geomAtoms, Bounds bound, BoundingLeafRetained boundingLeaf, NodeRetained armingNode, WakeupCriterion cond) { lock.readLock(); int idx = getBHTreeIndex(locale); if (idx < 0) { lock.readUnlock(); return null; } BHLeafInterface hitNode; if (geomAtoms != null) { synchronized (bhTreeArr[idx]) { if ((bound != null) && (armingNode instanceof GroupRetained)) { // Check Bound intersect first before process // to individual Shape3D geometryAtoms hitNode = bhTreeArr[idx].selectAny(bound, accurancyMode, (GroupRetained) armingNode); if (hitNode == null) { lock.readUnlock(); return null; } GeometryAtom galist[] = (GeometryAtom []) geomAtoms.toArray(false); hitNode = bhTreeArr[idx].selectAny(galist, geomAtoms.arraySize(), accurancyMode); if (hitNode != null) { lock.readUnlock(); if (cond != null) { checkDuplicateEvent((WakeupOnCollisionMovement) cond, bound, hitNode); } return hitNode; } } else { GeometryAtom ga = (GeometryAtom) geomAtoms.get(0); hitNode = bhTreeArr[idx].selectAny(ga, accurancyMode); if (hitNode != null) { lock.readUnlock(); if (cond != null) { checkDuplicateEvent((WakeupOnCollisionMovement) cond, ga.source.vwcBounds, hitNode); } return hitNode; } } } } else { if (bound == null) { if (boundingLeaf == null) { lock.readUnlock(); return null; } bound = boundingLeaf.transformedRegion; } if (bound == null) { lock.readUnlock(); return null; } if (armingNode instanceof GroupRetained) { synchronized (bhTreeArr[idx]) { hitNode = bhTreeArr[idx].selectAny(bound, accurancyMode, (GroupRetained) armingNode); lock.readUnlock(); if ((hitNode != null) && (cond != null)) { checkDuplicateEvent((WakeupOnCollisionMovement) cond, bound, hitNode); } return hitNode; } } else { synchronized (bhTreeArr[idx]) { hitNode = bhTreeArr[idx].selectAny(bound, accurancyMode, armingNode); lock.readUnlock(); if ((hitNode != null) && (cond != null)) { checkDuplicateEvent((WakeupOnCollisionMovement) cond, bound, hitNode); } return hitNode; } } } lock.readUnlock(); return null; } /** * This prevents wakeupCondition sent out message and set * conditionMet to true but the * BehaviorStructure/BehaviorScheduler is not fast enough to * process the message and reset conditionMet to false * when view deactivate/unregister. */ void resetConditionMet() { BehaviorStructure.resetConditionMet(wakeupOnCollisionEntry); BehaviorStructure.resetConditionMet(wakeupOnCollisionExit); BehaviorStructure.resetConditionMet(wakeupOnCollisionMovement); } /** * This processes a switch change. */ private void processSwitchChanged(J3dMessage m) { // int i; // UnorderList arrList; // int size, treeIndex; // Object[] nodes; // LeafRetained leaf; /* is now a NOOP UpdateTargets targets = (UpdateTargets)m.args[0]; arrList = targets.targetList[Targets.GEO_TARGETS]; if (arrList != null) { size = arrList.size(); nodes = arrList.toArray(false); treeIndex = getBHTreeIndex(((LeafRetained)nodes[0]).locale); for (i=0; i