0001: /*
0002: * $RCSfile: GeometryStructure.java,v $
0003: *
0004: * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0006: *
0007: * This code is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU General Public License version 2 only, as
0009: * published by the Free Software Foundation. Sun designates this
0010: * particular file as subject to the "Classpath" exception as provided
0011: * by Sun in the LICENSE file that accompanied this code.
0012: *
0013: * This code is distributed in the hope that it will be useful, but WITHOUT
0014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0016: * version 2 for more details (a copy is included in the LICENSE file that
0017: * accompanied this code).
0018: *
0019: * You should have received a copy of the GNU General Public License version
0020: * 2 along with this work; if not, write to the Free Software Foundation,
0021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0022: *
0023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0024: * CA 95054 USA or visit www.sun.com if you need additional information or
0025: * have any questions.
0026: *
0027: * $Revision: 1.9 $
0028: * $Date: 2008/02/28 20:17:22 $
0029: * $State: Exp $
0030: */
0031:
0032: package javax.media.j3d;
0033:
0034: import javax.vecmath.*;
0035: import java.util.ArrayList;
0036: import java.util.Arrays;
0037:
0038: /**
0039: * A geometry structure is a object that organizes geometries
0040: * and bounds.
0041: */
0042:
0043: class GeometryStructure extends J3dStructure {
0044: /**
0045: * used during Transform Processing
0046: */
0047: UpdateTargets targets = null;
0048:
0049: /**
0050: * A multiple read single write Lock to sychronize access into this
0051: * GeometryStructure.
0052: * To prevent deadlock a call to read/write lock must end with a read/write
0053: * unlock respectively.
0054: */
0055: private MRSWLock lock = null;
0056:
0057: /**
0058: * A lock object to prevent concurrent getVisibleBHTree query.
0059: */
0060: private Object visLock = new Object();
0061:
0062: /**
0063: * A lock object to prevent concurrent collideEntryList,
0064: * collideExitList using toArray() in BehaviorStructure buildTree()
0065: * while clearMirror() is invoked in GeometryStructure removeNode()
0066: */
0067: private Object collideListLock = new Object();
0068:
0069: /**
0070: * Binary Hull Tree structure for handling geometry atoms.
0071: * Do not change the following private variables to public, their access
0072: * need to synchronize via lock.
0073: */
0074:
0075: private BHTree[] bhTreeArr = null;
0076: private int bhTreeCount;
0077: private int bhTreeMax;
0078: private int bhTreeBlockSize = 5;
0079:
0080: /**
0081: * The array of BHNode, a data pool, for passing data between GS and BHTrees.
0082: * Do not change the following private variables to public, their access
0083: * need to synchronize via lock.
0084: */
0085: private BHNode[] bhNodeArr = null;
0086: private int bhNodeCount, bhNodeMax;
0087: private int bhNodeBlockSize = 50;
0088:
0089: // Support for multi-locale.
0090: private Vector3d localeTrans = new Vector3d();
0091:
0092: //The lists of wakeupCriterion object currently in collision.
0093: WakeupIndexedList collideEntryList;
0094: WakeupIndexedList collideExitList;
0095: WakeupIndexedList collideMovementList;
0096:
0097: // The lists of wakeupCriterion objects that GeometryStructure keeps
0098: WakeupIndexedList wakeupOnCollisionEntry;
0099: WakeupIndexedList wakeupOnCollisionExit;
0100: WakeupIndexedList wakeupOnCollisionMovement;
0101:
0102: // When Shape insert/remove for WakeupOnCollisionxxx() using
0103: // Group node and USE_GEOMETRY, we need to reevaluate the
0104: // cache geometryAtoms list.
0105: boolean reEvaluateWakeupCollisionGAs;
0106:
0107: private boolean transformMsg = false;
0108:
0109: /**
0110: * Constructor.
0111: */
0112: GeometryStructure(VirtualUniverse u) {
0113: super (u, J3dThread.UPDATE_GEOMETRY);
0114: bhNodeCount = 0;
0115: bhNodeMax = bhNodeBlockSize;
0116: bhNodeArr = new BHNode[bhNodeMax];
0117: bhTreeMax = 1;
0118: bhTreeArr = new BHTree[bhTreeMax];
0119: bhTreeCount = 0;
0120: lock = new MRSWLock();
0121: collideEntryList = new WakeupIndexedList(
0122: WakeupOnCollisionEntry.class,
0123: WakeupOnCollisionEntry.COLLIDEENTRY_IN_BS_LIST, u);
0124: collideExitList = new WakeupIndexedList(
0125: WakeupOnCollisionExit.class,
0126: WakeupOnCollisionExit.COLLIDEEXIT_IN_BS_LIST, u);
0127: collideMovementList = new WakeupIndexedList(
0128: WakeupOnCollisionMovement.class,
0129: WakeupOnCollisionMovement.COLLIDEMOVE_IN_BS_LIST, u);
0130: wakeupOnCollisionEntry = new WakeupIndexedList(
0131: WakeupOnCollisionEntry.class,
0132: WakeupOnCollisionEntry.COND_IN_GS_LIST, u);
0133: wakeupOnCollisionExit = new WakeupIndexedList(
0134: WakeupOnCollisionExit.class,
0135: WakeupOnCollisionExit.COND_IN_GS_LIST, u);
0136: wakeupOnCollisionMovement = new WakeupIndexedList(
0137: WakeupOnCollisionMovement.class,
0138: WakeupOnCollisionMovement.COND_IN_GS_LIST, u);
0139: }
0140:
0141: void processMessages(long referenceTime) {
0142: J3dMessage m;
0143: J3dMessage[] messages = getMessages(referenceTime);
0144: int nMsg = getNumMessage();
0145:
0146: if (nMsg > 0) {
0147: reEvaluateWakeupCollisionGAs = false;
0148: for (int i = 0; i < nMsg; i++) {
0149: lock.writeLock();
0150: m = messages[i];
0151: switch (m.type) {
0152: case J3dMessage.TRANSFORM_CHANGED:
0153: transformMsg = true;
0154: break;
0155: case J3dMessage.SWITCH_CHANGED:
0156: processSwitchChanged(m);
0157: // may need to process dirty switched-on transform
0158: if (universe.transformStructure.getLazyUpdate()) {
0159: transformMsg = true;
0160: }
0161: break;
0162: case J3dMessage.INSERT_NODES:
0163: insertNodes((Object[]) m.args[0]);
0164: reEvaluateWakeupCollisionGAs = true;
0165: break;
0166: case J3dMessage.REMOVE_NODES:
0167: removeNodes(m);
0168: reEvaluateWakeupCollisionGAs = true;
0169: break;
0170: case J3dMessage.SHAPE3D_CHANGED: {
0171: int comp = ((Integer) m.args[1]).intValue();
0172: if (comp == Shape3DRetained.GEOMETRY_CHANGED) {
0173: m.args[0] = m.args[2];
0174: removeNodes(m);
0175: insertNodes((Object[]) m.args[3]);
0176: reEvaluateWakeupCollisionGAs = true;
0177: } else if (comp == Shape3DRetained.APPEARANCE_CHANGED) {
0178: processVisibleChanged(m.args[2],
0179: ((GeometryAtom[]) m.args[3]));
0180: }
0181: break;
0182: }
0183: case J3dMessage.TEXT3D_DATA_CHANGED:
0184: removeNodes(m);
0185: insertNodes((Object[]) m.args[1]);
0186: break;
0187: case J3dMessage.TEXT3D_TRANSFORM_CHANGED:
0188: processBoundsChanged((Object[]) m.args[0], false);
0189: break;
0190: case J3dMessage.MORPH_CHANGED: {
0191: int comp = ((Integer) m.args[1]).intValue();
0192: if (comp == MorphRetained.GEOMETRY_CHANGED) {
0193: processBoundsChanged((Object[]) m.args[3],
0194: false);
0195: } else if (comp == MorphRetained.APPEARANCE_CHANGED) {
0196: processVisibleChanged(m.args[2],
0197: ((GeometryAtom[]) m.args[3]));
0198: }
0199: break;
0200: }
0201: case J3dMessage.REGION_BOUND_CHANGED:
0202: case J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED:
0203: // Only set this flag, when bounds might be empty.
0204: processBoundsChanged((Object[]) m.args[0], false);
0205: break;
0206: case J3dMessage.GEOMETRY_CHANGED:
0207: // System.err.println("J3dMessage.GEOMETRY_CHANGED");
0208: processBoundsChanged((Object[]) m.args[0], false);
0209: break;
0210: case J3dMessage.RENDERINGATTRIBUTES_CHANGED:
0211: processVisibleChanged(m.args[2],
0212: ((GeometryAtom[]) m.args[3]));
0213: break;
0214: }
0215:
0216: lock.writeUnlock();
0217: m.decRefcount();
0218: }
0219:
0220: if (transformMsg) {
0221: targets = universe.transformStructure.getTargetList();
0222: lock.writeLock();
0223:
0224: processTransformChanged(targets);
0225:
0226: lock.writeUnlock();
0227:
0228: transformMsg = false;
0229: targets = null;
0230: }
0231:
0232: Arrays.fill(messages, 0, nMsg, null);
0233: }
0234:
0235: processCollisionDetection();
0236: }
0237:
0238: private int getBHTreeIndex(Locale locale) {
0239: int i;
0240:
0241: for (i = 0; i < bhTreeCount; i++) {
0242: if (bhTreeArr[i].locale == locale)
0243: return i;
0244: }
0245: // Can't find will return -1 so that other
0246: // program know this
0247: return -1;
0248: }
0249:
0250: private int getOrAddBHTreeIndex(Locale locale) {
0251: int i;
0252:
0253: for (i = 0; i < bhTreeCount; i++) {
0254: if (bhTreeArr[i].locale == locale)
0255: return i;
0256: }
0257:
0258: if (bhTreeCount >= bhTreeMax) {
0259: // allocate a bigger array here....
0260: if (J3dDebug.devPhase)
0261: J3dDebug.doDebug(J3dDebug.geometryStructure,
0262: J3dDebug.LEVEL_2,
0263: "Expanding bhTreeArr array ...\n");
0264: bhTreeMax += bhTreeBlockSize;
0265: BHTree[] oldBhTreeArr = bhTreeArr;
0266:
0267: bhTreeArr = new BHTree[bhTreeMax];
0268: System.arraycopy(oldBhTreeArr, 0, bhTreeArr, 0,
0269: oldBhTreeArr.length);
0270: }
0271:
0272: bhTreeArr[bhTreeCount] = new BHTree(locale);
0273: bhTreeCount++;
0274: return i;
0275: }
0276:
0277: private void clearBhNodeArr() {
0278: // Issue 353: set all elements to null so we don't leak
0279: // NOTE: we really should change this to be an ArrayList, but that
0280: // would be a less localized change. Consider for 1.6.0.
0281: for (int i = 0; i < bhNodeCount; i++) {
0282: bhNodeArr[i] = null;
0283: }
0284:
0285: bhNodeCount = 0;
0286: }
0287:
0288: private void addToBhNodeArr(BHNode bhNode) {
0289:
0290: // Add to bhNodeArr.
0291: if (bhNodeCount >= bhNodeMax) {
0292: bhNodeMax += bhNodeBlockSize;
0293: BHNode[] oldbhNodeArr = bhNodeArr;
0294:
0295: bhNodeArr = new BHNode[bhNodeMax];
0296: System.arraycopy(oldbhNodeArr, 0, bhNodeArr, 0,
0297: oldbhNodeArr.length);
0298: }
0299:
0300: bhNodeArr[bhNodeCount] = bhNode;
0301: bhNodeCount++;
0302: }
0303:
0304: private void processVisibleChanged(Object valueObj,
0305: GeometryAtom[] gaArr) {
0306: boolean visible = true; // Default is true.
0307: int i, treeIndex;
0308:
0309: if ((gaArr == null) || (gaArr.length < 1))
0310: return;
0311:
0312: treeIndex = getBHTreeIndex(gaArr[0].locale);
0313:
0314: visible = ((Boolean) valueObj).booleanValue();
0315:
0316: for (i = gaArr.length - 1; i >= 0; i--) {
0317: gaArr[i].visible = visible;
0318: }
0319:
0320: }
0321:
0322: private void insertNodes(Object[] nodes) {
0323: Object node;
0324: GeometryAtom geomAtom;
0325: BHTree currTree = null;
0326:
0327: clearBhNodeArr();
0328:
0329: // System.err.println("GS : nodes.length is " + nodes.length);
0330:
0331: for (int i = 0; i < nodes.length; i++) {
0332: node = nodes[i];
0333: if (node instanceof GeometryAtom) {
0334: synchronized (node) {
0335: geomAtom = (GeometryAtom) node;
0336: if (geomAtom.source.inBackgroundGroup) {
0337: geomAtom.source.geometryBackground
0338: .addBgGeometryAtomList(geomAtom);
0339: continue;
0340: }
0341: BHLeafNode bhLeafNode = new BHLeafNode();
0342: bhLeafNode.leafIF = geomAtom;
0343: geomAtom.bhLeafNode = bhLeafNode;
0344: bhLeafNode.computeBoundingHull();
0345: // System.err.println("bhLeafNode.bHull is " + bhLeafNode.bHull);
0346: addToBhNodeArr(bhLeafNode);
0347: }
0348: } else if (node instanceof GroupRetained) {
0349: synchronized (node) {
0350: GroupRetained group = (GroupRetained) node;
0351: BHLeafNode bhLeafNode = new BHLeafNode();
0352: bhLeafNode.leafIF = group;
0353: group.bhLeafNode = bhLeafNode;
0354: bhLeafNode.computeBoundingHull();
0355: addToBhNodeArr(bhLeafNode);
0356: }
0357: }
0358: }
0359:
0360: if (bhNodeCount < 1) {
0361: return;
0362: }
0363:
0364: // Look for the right BHTree to insert to.
0365: if (currTree == null) {
0366: // We must separate the following two calls
0367: // since the first Call will allocate storage bhTreeArr
0368: // for the second index operation. (see bug 4361998)
0369: int idx = getOrAddBHTreeIndex(((BHLeafNode) bhNodeArr[0])
0370: .getLocale());
0371: currTree = bhTreeArr[idx];
0372:
0373: }
0374:
0375: currTree.insert(bhNodeArr, bhNodeCount);
0376:
0377: // Issue 353: must clear array after we are done with it
0378: clearBhNodeArr();
0379:
0380: // currTree.gatherTreeStatistics();
0381: }
0382:
0383: void removeNodes(J3dMessage m) {
0384: Object[] nodes = (Object[]) m.args[0];
0385: BHTree currTree = null;
0386: Object node;
0387: int index;
0388:
0389: clearBhNodeArr();
0390:
0391: for (int i = 0; i < nodes.length; i++) {
0392: node = nodes[i];
0393: if (node instanceof GeometryAtom) {
0394: synchronized (node) {
0395: GeometryAtom geomAtom = (GeometryAtom) node;
0396: if ((geomAtom.source != null)
0397: && (geomAtom.source.inBackgroundGroup)) {
0398: geomAtom.source.geometryBackground
0399: .removeBgGeometryAtomList(geomAtom);
0400: continue;
0401: }
0402: if (geomAtom.bhLeafNode != null) {
0403: addToBhNodeArr(geomAtom.bhLeafNode);
0404: // Dereference BHLeafNode in GeometryAtom.
0405: geomAtom.bhLeafNode = null;
0406: }
0407:
0408: }
0409: } else if (node instanceof GroupRetained) {
0410: if (((NodeRetained) node).nodeType != NodeRetained.ORDEREDGROUP) {
0411: synchronized (node) {
0412: GroupRetained group = (GroupRetained) node;
0413: if (group.bhLeafNode != null) {
0414: addToBhNodeArr(group.bhLeafNode);
0415: // Dereference BHLeafNode in GroupRetained
0416: group.bhLeafNode = null;
0417: }
0418: }
0419: }
0420: } else if (node instanceof BehaviorRetained) {
0421: synchronized (node) {
0422: BehaviorRetained behav = (BehaviorRetained) node;
0423: // cleanup collideEntryList & collideExitList
0424: // since we didn't remove
0425: // it on remove in removeWakeupOnCollision()
0426:
0427: // Note that GeometryStructure may run in
0428: // parallel with BehaviorStructure when
0429: // BS invoke activateBehaviors() to buildTree()
0430: // which in turn call addWakeupOnCollision()
0431: // to modify collideEntryList at the same time.
0432:
0433: WakeupOnCollisionEntry wentry;
0434: WakeupOnCollisionEntry wentryArr[] = (WakeupOnCollisionEntry[]) collideEntryList
0435: .toArray();
0436: for (int j = collideEntryList.arraySize() - 1; j >= 0; j--) {
0437: wentry = wentryArr[j];
0438: if (wentry.behav == behav) {
0439: collideEntryList.remove(wentry);
0440: }
0441: }
0442: WakeupOnCollisionExit wexit;
0443: WakeupOnCollisionExit wexitArr[] = (WakeupOnCollisionExit[]) collideExitList
0444: .toArray();
0445: for (int j = collideExitList.arraySize() - 1; j >= 0; j--) {
0446: wexit = wexitArr[j];
0447: if (wexit.behav == behav) {
0448: collideExitList.remove(wexit);
0449: }
0450: }
0451: }
0452: }
0453: }
0454:
0455: if (bhNodeCount < 1) {
0456: return;
0457: }
0458:
0459: if (currTree == null) {
0460: index = getBHTreeIndex(((BHLeafNode) bhNodeArr[0])
0461: .getLocale());
0462: if (index < 0) {
0463: // Issue 353: must clear array after we are done with it
0464: clearBhNodeArr();
0465:
0466: return;
0467: }
0468: currTree = bhTreeArr[index];
0469: }
0470: currTree.delete(bhNodeArr, bhNodeCount);
0471:
0472: // Issue 353: must clear array after we are done with it
0473: clearBhNodeArr();
0474:
0475: // It is safe to do it here since only GeometryStructure
0476: // thread invoke wakeupOnCollisionEntry/Exit .toArray()
0477:
0478: wakeupOnCollisionEntry.clearMirror();
0479: wakeupOnCollisionMovement.clearMirror();
0480: wakeupOnCollisionExit.clearMirror();
0481:
0482: synchronized (collideListLock) {
0483: collideEntryList.clearMirror();
0484: collideExitList.clearMirror();
0485: }
0486: }
0487:
0488: private void processBoundsChanged(Object[] nodes,
0489: boolean transformChanged) {
0490:
0491: int index;
0492: Object node;
0493:
0494: clearBhNodeArr();
0495:
0496: for (int i = 0; i < nodes.length; i++) {
0497: node = nodes[i];
0498: if (node instanceof GeometryAtom) {
0499: synchronized (node) {
0500:
0501: GeometryAtom geomAtom = (GeometryAtom) node;
0502: if (geomAtom.bhLeafNode != null) {
0503: addToBhNodeArr(geomAtom.bhLeafNode);
0504: }
0505: }
0506: } else if (node instanceof GroupRetained) {
0507:
0508: GroupRetained group = (GroupRetained) node;
0509: if (group.nodeType != NodeRetained.SWITCH) {
0510: synchronized (node) {
0511: if (group.bhLeafNode != null) {
0512: addToBhNodeArr(group.bhLeafNode);
0513: }
0514: }
0515: }
0516: }
0517: }
0518:
0519: if (bhNodeCount < 1) {
0520: return;
0521: }
0522:
0523: index = getBHTreeIndex(((BHLeafNode) bhNodeArr[0]).getLocale());
0524:
0525: if (index >= 0) {
0526: bhTreeArr[index].boundsChanged(bhNodeArr, bhNodeCount);
0527: }
0528:
0529: // Issue 353: must clear array after we are done with it
0530: clearBhNodeArr();
0531:
0532: }
0533:
0534: private void processTransformChanged(UpdateTargets targets) {
0535:
0536: int i, j, index;
0537: Object[] nodes, nodesArr;
0538: UnorderList arrList;
0539: int size;
0540:
0541: clearBhNodeArr();
0542:
0543: arrList = targets.targetList[Targets.GEO_TARGETS];
0544:
0545: if (arrList != null) {
0546: size = arrList.size();
0547: nodesArr = arrList.toArray(false);
0548:
0549: for (j = 0; j < size; j++) {
0550: nodes = (Object[]) nodesArr[j];
0551: for (i = 0; i < nodes.length; i++) {
0552: GeometryAtom geomAtom = (GeometryAtom) nodes[i];
0553: synchronized (geomAtom) {
0554: if (geomAtom.bhLeafNode != null) {
0555: addToBhNodeArr(geomAtom.bhLeafNode);
0556: }
0557: }
0558: }
0559: }
0560: }
0561:
0562: arrList = targets.targetList[Targets.GRP_TARGETS];
0563: if (arrList != null) {
0564: size = arrList.size();
0565: nodesArr = arrList.toArray(false);
0566: for (j = 0; j < size; j++) {
0567: nodes = (Object[]) nodesArr[j];
0568: for (i = 0; i < nodes.length; i++) {
0569: GroupRetained group = (GroupRetained) nodes[i];
0570: if (group.nodeType != NodeRetained.SWITCH) {
0571: synchronized (group) {
0572: if (group.bhLeafNode != null) {
0573: addToBhNodeArr(group.bhLeafNode);
0574: }
0575: }
0576: }
0577: }
0578: }
0579: }
0580:
0581: if (bhNodeCount < 1) {
0582: return;
0583: }
0584:
0585: index = getBHTreeIndex(((BHLeafNode) bhNodeArr[0]).getLocale());
0586:
0587: if (index >= 0) {
0588: bhTreeArr[index].boundsChanged(bhNodeArr, bhNodeCount);
0589:
0590: }
0591:
0592: // Issue 353: must clear array after we are done with it
0593: clearBhNodeArr();
0594:
0595: }
0596:
0597: // This method is called by RenderBin to get a array of possibly visible
0598: // sub-trees.
0599: // bhTrees mustn't be null.
0600: // Return true if bhTree's root in encompass by frustumBBox.
0601:
0602: boolean getVisibleBHTrees(RenderBin rBin, BoundingBox frustumBBox,
0603: Locale locale, long referenceTime, boolean stateChanged,
0604: int visibilityPolicy) {
0605:
0606: int i, j;
0607: boolean unviInFB = true;
0608:
0609: // System.err.println("GeometryStructure : view's locale is " + locale);
0610: lock.readLock();
0611:
0612: // Issue 353: create a new array list each time rather than passing it
0613: // in. This will not generate too much garbage, since we only call
0614: // this once per frame and it is very short-lived.
0615: ArrayList bhTrees = new ArrayList();
0616: if (bhTreeCount == 1) {
0617: // For debugging only.
0618: if (J3dDebug.devPhase) {
0619: if (J3dDebug.doDebug(J3dDebug.geometryStructure,
0620: J3dDebug.LEVEL_2)) {
0621: System.err
0622: .println("GeometryStructure : In simple case");
0623: System.err
0624: .println("GeometryStructure : view's locale is "
0625: + locale);
0626: System.err
0627: .println("GeometryStructure : bhTreeArr[0].locale is "
0628: + bhTreeArr[0].locale);
0629: }
0630: }
0631: // One locale case - Lets make the simple case fast.
0632: synchronized (visLock) {
0633: unviInFB = bhTreeArr[0].getVisibleBHTrees(rBin,
0634: bhTrees, frustumBBox, referenceTime,
0635: stateChanged, visibilityPolicy, true);
0636: }
0637: } else {
0638: // Multiple locale case.
0639:
0640: // For debugging only.
0641: if (J3dDebug.devPhase)
0642: J3dDebug
0643: .doDebug(
0644: J3dDebug.geometryStructure,
0645: J3dDebug.LEVEL_2,
0646: "GeometryStructure : bhTreeCount is "
0647: + universe.geometryStructure.bhTreeCount
0648: + " view's locale is " + locale
0649: + "\n");
0650:
0651: BoundingBox localeFrustumBBox = new BoundingBox();
0652:
0653: synchronized (visLock) {
0654:
0655: for (j = 0; j < bhTreeCount; j++) {
0656: if (J3dDebug.devPhase) {
0657: J3dDebug.doDebug(J3dDebug.geometryStructure,
0658: J3dDebug.LEVEL_2,
0659: "GeometryStructure : bhTreeArr[" + j
0660: + "] is " + bhTreeArr[j].locale
0661: + "\n");
0662: }
0663: if (!locale.hiRes.equals(bhTreeArr[j].locale.hiRes)) {
0664: bhTreeArr[j].locale.hiRes.difference(
0665: locale.hiRes, localeTrans);
0666:
0667: if (J3dDebug.devPhase) {
0668: J3dDebug
0669: .doDebug(
0670: J3dDebug.geometryStructure,
0671: J3dDebug.LEVEL_2,
0672: "localeTrans is "
0673: + localeTrans
0674: + "GeometryStructure : localeFrustumBBox "
0675: + localeFrustumBBox
0676: + "\n");
0677: }
0678:
0679: // Need to translate view frustumBBox here.
0680: localeFrustumBBox.lower.x = frustumBBox.lower.x
0681: + localeTrans.x;
0682: localeFrustumBBox.lower.y = frustumBBox.lower.y
0683: + localeTrans.y;
0684: localeFrustumBBox.lower.z = frustumBBox.lower.z
0685: + localeTrans.z;
0686: localeFrustumBBox.upper.x = frustumBBox.upper.x
0687: + localeTrans.x;
0688: localeFrustumBBox.upper.y = frustumBBox.upper.y
0689: + localeTrans.y;
0690: localeFrustumBBox.upper.z = frustumBBox.upper.z
0691: + localeTrans.z;
0692: } else {
0693: frustumBBox.copy(localeFrustumBBox);
0694: }
0695:
0696: if (!(bhTreeArr[j].getVisibleBHTrees(rBin, bhTrees,
0697: localeFrustumBBox, referenceTime,
0698: stateChanged, visibilityPolicy, false))) {
0699: unviInFB = false;
0700: }
0701: }
0702: }
0703: }
0704:
0705: lock.readUnlock();
0706: return unviInFB;
0707: }
0708:
0709: GeometryAtom[] pickAll(Locale locale, PickShape shape) {
0710:
0711: int i;
0712: UnorderList hitList = new UnorderList(BHNode.class);
0713: hitList.clear();
0714:
0715: lock.readLock();
0716:
0717: i = getBHTreeIndex(locale);
0718: if (i < 0) {
0719: lock.readUnlock();
0720: return null;
0721: }
0722:
0723: bhTreeArr[i].select(shape, hitList);
0724: lock.readUnlock();
0725:
0726: int size = hitList.size();
0727:
0728: if (size < 1)
0729: return null;
0730:
0731: BHNode[] hitArr = (BHNode[]) hitList.toArray(false);
0732:
0733: GeometryAtom[] geometryAtoms = new GeometryAtom[size];
0734: for (i = 0; i < size; i++) {
0735: geometryAtoms[i] = (GeometryAtom) (((BHLeafNode) hitArr[i]).leafIF);
0736: }
0737:
0738: return geometryAtoms;
0739: }
0740:
0741: GeometryAtom pickAny(Locale locale, PickShape shape) {
0742:
0743: int i;
0744:
0745: BHNode hitNode = null;
0746:
0747: lock.readLock();
0748:
0749: i = getBHTreeIndex(locale);
0750: if (i < 0) {
0751: lock.readUnlock();
0752: return null;
0753: }
0754:
0755: hitNode = bhTreeArr[i].selectAny(shape);
0756:
0757: lock.readUnlock();
0758:
0759: if (hitNode == null)
0760: return null;
0761:
0762: return (GeometryAtom) (((BHLeafNode) hitNode).leafIF);
0763:
0764: }
0765:
0766: void addWakeupOnCollision(WakeupOnCollisionEntry w) {
0767:
0768: boolean needTrigger = true;
0769:
0770: // Cleanup, since collideEntryList did not remove
0771: // its condition in removeWakeupOnCollision
0772: synchronized (collideListLock) {
0773: WakeupOnCollisionEntry collideEntryArr[] = (WakeupOnCollisionEntry[]) collideEntryList
0774: .toArray();
0775: WakeupOnCollisionEntry wentry;
0776: for (int i = collideEntryList.arraySize() - 1; i >= 0; i--) {
0777: wentry = collideEntryArr[i];
0778: if ((wentry.behav == w.behav)
0779: && (wentry.geometryAtoms == w.geometryAtoms)) {
0780: collideEntryList.remove(i);
0781: needTrigger = false;
0782: break;
0783: }
0784: }
0785: }
0786:
0787: // add to wakeup list
0788: wakeupOnCollisionEntry.add(w);
0789: w.updateCollisionBounds(false);
0790: // check for collision and triggered event
0791: BHLeafInterface target = collide(w.behav.locale,
0792: w.accuracyMode, w.geometryAtoms, w.vwcBounds,
0793: w.boundingLeaf, w.armingNode, null);
0794:
0795: if (target != null) {
0796: collideEntryList.add(w);
0797: w.setTarget(target);
0798: }
0799:
0800: if ((target != null) && (needTrigger)) {
0801: w.setTriggered();
0802: }
0803: }
0804:
0805: void addWakeupOnCollision(WakeupOnCollisionExit w) {
0806:
0807: // Cleanup, since collideExitList did not remove
0808: // its condition in removeWakeupOnCollision
0809: boolean needTrigger = true;
0810:
0811: synchronized (collideListLock) {
0812: WakeupOnCollisionExit collideExitArr[] = (WakeupOnCollisionExit[]) collideExitList
0813: .toArray();
0814: WakeupOnCollisionExit wexit;
0815: for (int i = collideExitList.arraySize() - 1; i >= 0; i--) {
0816: wexit = collideExitArr[i];
0817: if ((wexit.behav == w.behav)
0818: && (wexit.geometryAtoms == w.geometryAtoms)) {
0819: collideExitList.remove(i);
0820: needTrigger = false;
0821: break;
0822: }
0823: }
0824: }
0825:
0826: // add condition
0827: wakeupOnCollisionExit.add(w);
0828: w.updateCollisionBounds(false);
0829: BHLeafInterface target = collide(w.behav.locale,
0830: w.accuracyMode, w.geometryAtoms, w.vwcBounds,
0831: w.boundingLeaf, w.armingNode, null);
0832:
0833: if (target != null) {
0834: // store the target that cause this condition to collide
0835: // this is used when this condition is triggered.
0836: w.setTarget(target);
0837: collideExitList.add(w);
0838: }
0839:
0840: if (!needTrigger) {
0841: return;
0842: }
0843: // see if the matching wakeupOnCollisionEntry
0844: // condition exists
0845:
0846: synchronized (collideListLock) {
0847: WakeupOnCollisionEntry collideEntryArr[] = (WakeupOnCollisionEntry[]) collideEntryList
0848: .toArray();
0849: WakeupOnCollisionEntry wentry;
0850:
0851: for (int i = collideEntryList.arraySize() - 1; i >= 0; i--) {
0852: wentry = collideEntryArr[i];
0853: if ((wentry.behav == w.behav)
0854: && (wentry.geometryAtoms == w.geometryAtoms)) {
0855: // Should not call collideEntryList.remove(i);
0856: // Otherwise wakeupOn for Entry case may call several
0857: // time at when initialize if collide
0858: if (target == null) {
0859: w.setTriggered();
0860: }
0861: break;
0862: }
0863: }
0864: }
0865: }
0866:
0867: void addWakeupOnCollision(WakeupOnCollisionMovement w) {
0868: wakeupOnCollisionMovement.add(w);
0869: w.updateCollisionBounds(false);
0870: BHLeafInterface target = collide(w.behav.locale,
0871: w.accuracyMode, w.geometryAtoms, w.vwcBounds,
0872: w.boundingLeaf, w.armingNode, w);
0873: if (target != null) {
0874: w.setTarget(target);
0875: collideMovementList.add(w);
0876: }
0877: }
0878:
0879: void removeWakeupOnCollision(WakeupOnCollisionEntry wentry) {
0880: wakeupOnCollisionEntry.remove(wentry);
0881: // No need to remove collideEntry, it is used next time
0882: // when WakeupOnExitCollision is added to determine
0883: // whether to trigger it.
0884: }
0885:
0886: void removeWakeupOnCollision(WakeupOnCollisionExit wexit) {
0887: wakeupOnCollisionExit.remove(wexit);
0888: // No need to remove collideExit, it is used next time
0889: // when WakeupOnExitCollision is added to determine
0890: // whether to trigger it.
0891: }
0892:
0893: void removeWakeupOnCollision(WakeupOnCollisionMovement wmovement) {
0894: wakeupOnCollisionMovement.remove(wmovement);
0895: collideMovementList.remove(wmovement); // remove if exists
0896: }
0897:
0898: /**
0899: * This method test all wakeupOnCollision list and trigger the
0900: * condition if collision occurs.
0901: */
0902: void processCollisionDetection() {
0903: int i, idx;
0904: BHLeafInterface target;
0905:
0906: // handle WakeupOnCollisionEntry
0907: WakeupOnCollisionEntry wentry;
0908: WakeupOnCollisionEntry wentryArr[] = (WakeupOnCollisionEntry[]) wakeupOnCollisionEntry
0909: .toArray();
0910:
0911: for (i = wakeupOnCollisionEntry.arraySize() - 1; i >= 0; i--) {
0912: wentry = wentryArr[i];
0913: wentry.updateCollisionBounds(reEvaluateWakeupCollisionGAs);
0914: target = collide(wentry.behav.locale, wentry.accuracyMode,
0915: wentry.geometryAtoms, wentry.vwcBounds,
0916: wentry.boundingLeaf, wentry.armingNode, null);
0917: idx = collideEntryList.indexOf(wentry);
0918:
0919: if (target != null) {
0920: if (idx < 0) {
0921: collideEntryList.add(wentry);
0922: wentry.setTarget(target);
0923: wentry.setTriggered();
0924: }
0925: } else {
0926: if (idx >= 0) {
0927: collideEntryList.remove(idx);
0928: }
0929: }
0930: }
0931:
0932: // handle WakeupOnCollisionMovement
0933:
0934: WakeupOnCollisionMovement wmove;
0935: WakeupOnCollisionMovement wmoveArr[] = (WakeupOnCollisionMovement[]) wakeupOnCollisionMovement
0936: .toArray();
0937:
0938: for (i = wakeupOnCollisionMovement.arraySize() - 1; i >= 0; i--) {
0939: wmove = wmoveArr[i];
0940: wmove.updateCollisionBounds(reEvaluateWakeupCollisionGAs);
0941: target = collide(wmove.behav.locale, wmove.accuracyMode,
0942: wmove.geometryAtoms, wmove.vwcBounds,
0943: wmove.boundingLeaf, wmove.armingNode, wmove);
0944: idx = collideMovementList.indexOf(wmove);
0945: if (target != null) {
0946: if (idx < 0) {
0947: collideMovementList.add(wmove);
0948: wmove.setTarget(target);
0949: } else {
0950: if (!wmove.duplicateEvent) {
0951: wmove.setTriggered();
0952: }
0953: }
0954: } else {
0955: if (idx >= 0) {
0956: collideMovementList.remove(idx);
0957: wmove.lastSrcBounds = null;
0958: wmove.lastDstBounds = null;
0959: }
0960: }
0961: }
0962:
0963: // Finally, handle WakeupOnCollisionExit
0964:
0965: WakeupOnCollisionExit wexit;
0966: WakeupOnCollisionExit wexitArr[] = (WakeupOnCollisionExit[]) wakeupOnCollisionExit
0967: .toArray();
0968:
0969: for (i = wakeupOnCollisionExit.arraySize() - 1; i >= 0; i--) {
0970: wexit = wexitArr[i];
0971: wexit.updateCollisionBounds(reEvaluateWakeupCollisionGAs);
0972: target = collide(wexit.behav.locale, wexit.accuracyMode,
0973: wexit.geometryAtoms, wexit.vwcBounds,
0974: wexit.boundingLeaf, wexit.armingNode, null);
0975: idx = collideExitList.indexOf(wexit);
0976: if (target != null) {
0977: if (idx < 0) {
0978: collideExitList.add(wexit);
0979: wexit.setTarget(target);
0980: }
0981: } else {
0982: if (idx >= 0) {
0983: collideExitList.remove(idx);
0984: wexit.setTriggered();
0985: }
0986: }
0987: }
0988:
0989: }
0990:
0991: /**
0992: * Check for duplicate WakeupOnCollisionMovement event.
0993: * We don't want to continue deliver event even though the
0994: * two colliding object did not move but this Geometry update
0995: * thread continue to run due to transform change in others
0996: * shape not in collision.
0997: */
0998: void checkDuplicateEvent(WakeupOnCollisionMovement wmove,
0999: Bounds bound, BHLeafInterface hitNode) {
1000: Bounds hitBound;
1001:
1002: if ((wmove.lastSrcBounds != null)
1003: && wmove.lastSrcBounds.equals(bound)) {
1004: if (hitNode instanceof GeometryAtom) {
1005: hitBound = ((GeometryAtom) hitNode).source.vwcBounds;
1006: } else {
1007: hitBound = ((GroupRetained) hitNode).collisionVwcBounds;
1008: }
1009: if ((wmove.lastDstBounds != null)
1010: && wmove.lastDstBounds.equals(hitBound)) {
1011: wmove.duplicateEvent = true;
1012: } else {
1013: wmove.duplicateEvent = false;
1014: wmove.lastDstBounds = (Bounds) hitBound.clone();
1015: }
1016: } else {
1017: wmove.duplicateEvent = false;
1018: wmove.lastSrcBounds = (Bounds) bound.clone();
1019: }
1020: }
1021:
1022: /**
1023: * check if either the geomAtoms[] or
1024: * bound or boundingLeaf collide with BHTree.
1025: * Only one of geomAtoms, bound, boundingLeaf is non-null.
1026: * If accurancyMode is USE_GEOMETRY, object geometry is used,
1027: * otherwise object bounding box is used for collision
1028: * detection.
1029: * In case of GROUP & BOUND, the armingNode is used
1030: * to tell whether the colliding Group is itself or not.
1031: * Also in case GROUP, geomAtoms is non-null if USE_GEOMETRY.
1032: * If cond != null, it must be instanceof WakeupOnCollisionMovement
1033: */
1034: BHLeafInterface collide(Locale locale, int accurancyMode,
1035: UnorderList geomAtoms, Bounds bound,
1036: BoundingLeafRetained boundingLeaf, NodeRetained armingNode,
1037: WakeupCriterion cond) {
1038:
1039: lock.readLock();
1040: int idx = getBHTreeIndex(locale);
1041:
1042: if (idx < 0) {
1043: lock.readUnlock();
1044: return null;
1045: }
1046: BHLeafInterface hitNode;
1047:
1048: if (geomAtoms != null) {
1049: synchronized (bhTreeArr[idx]) {
1050: if ((bound != null)
1051: && (armingNode instanceof GroupRetained)) {
1052: // Check Bound intersect first before process
1053: // to individual Shape3D geometryAtoms
1054: hitNode = bhTreeArr[idx].selectAny(bound,
1055: accurancyMode, (GroupRetained) armingNode);
1056: if (hitNode == null) {
1057: lock.readUnlock();
1058: return null;
1059: }
1060: GeometryAtom galist[] = (GeometryAtom[]) geomAtoms
1061: .toArray(false);
1062:
1063: hitNode = bhTreeArr[idx].selectAny(galist,
1064: geomAtoms.arraySize(), accurancyMode);
1065:
1066: if (hitNode != null) {
1067: lock.readUnlock();
1068: if (cond != null) {
1069: checkDuplicateEvent(
1070: (WakeupOnCollisionMovement) cond,
1071: bound, hitNode);
1072: }
1073: return hitNode;
1074: }
1075: } else {
1076: GeometryAtom ga = (GeometryAtom) geomAtoms.get(0);
1077: hitNode = bhTreeArr[idx].selectAny(ga,
1078: accurancyMode);
1079:
1080: if (hitNode != null) {
1081: lock.readUnlock();
1082: if (cond != null) {
1083: checkDuplicateEvent(
1084: (WakeupOnCollisionMovement) cond,
1085: ga.source.vwcBounds, hitNode);
1086: }
1087: return hitNode;
1088: }
1089: }
1090: }
1091: } else {
1092: if (bound == null) {
1093: if (boundingLeaf == null) {
1094: lock.readUnlock();
1095: return null;
1096: }
1097: bound = boundingLeaf.transformedRegion;
1098: }
1099: if (bound == null) {
1100: lock.readUnlock();
1101: return null;
1102: }
1103: if (armingNode instanceof GroupRetained) {
1104: synchronized (bhTreeArr[idx]) {
1105: hitNode = bhTreeArr[idx].selectAny(bound,
1106: accurancyMode, (GroupRetained) armingNode);
1107: lock.readUnlock();
1108: if ((hitNode != null) && (cond != null)) {
1109: checkDuplicateEvent(
1110: (WakeupOnCollisionMovement) cond,
1111: bound, hitNode);
1112: }
1113: return hitNode;
1114: }
1115: } else {
1116: synchronized (bhTreeArr[idx]) {
1117: hitNode = bhTreeArr[idx].selectAny(bound,
1118: accurancyMode, armingNode);
1119: lock.readUnlock();
1120: if ((hitNode != null) && (cond != null)) {
1121: checkDuplicateEvent(
1122: (WakeupOnCollisionMovement) cond,
1123: bound, hitNode);
1124: }
1125: return hitNode;
1126: }
1127: }
1128: }
1129: lock.readUnlock();
1130: return null;
1131: }
1132:
1133: /**
1134: * This prevents wakeupCondition sent out message and set
1135: * conditionMet to true but the
1136: * BehaviorStructure/BehaviorScheduler is not fast enough to
1137: * process the message and reset conditionMet to false
1138: * when view deactivate/unregister.
1139: */
1140: void resetConditionMet() {
1141: BehaviorStructure.resetConditionMet(wakeupOnCollisionEntry);
1142: BehaviorStructure.resetConditionMet(wakeupOnCollisionExit);
1143: BehaviorStructure.resetConditionMet(wakeupOnCollisionMovement);
1144: }
1145:
1146: /**
1147: * This processes a switch change.
1148: */
1149: private void processSwitchChanged(J3dMessage m) {
1150:
1151: int i;
1152: UnorderList arrList;
1153: int size, treeIndex;
1154: Object[] nodes;
1155: LeafRetained leaf;
1156:
1157: /* is now a NOOP
1158:
1159: UpdateTargets targets = (UpdateTargets)m.args[0];
1160:
1161: arrList = targets.targetList[Targets.GEO_TARGETS];
1162:
1163: if (arrList != null) {
1164: size = arrList.size();
1165: nodes = arrList.toArray(false);
1166:
1167: treeIndex = getBHTreeIndex(((LeafRetained)nodes[0]).locale);
1168:
1169: for (i=0; i<size; i++) {
1170: leaf = (LeafRetained)nodes[i];
1171: }
1172: }
1173: */
1174: }
1175:
1176: void cleanup() {
1177: collideEntryList.clear();
1178: collideExitList.clear();
1179: collideMovementList.clear();
1180: wakeupOnCollisionEntry.clear();
1181: wakeupOnCollisionExit.clear();
1182: wakeupOnCollisionMovement.clear();
1183: }
1184: }
|