001: /*
002: * $RCSfile: NodeRetained.java,v $
003: *
004: * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
006: *
007: * This code is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License version 2 only, as
009: * published by the Free Software Foundation. Sun designates this
010: * particular file as subject to the "Classpath" exception as provided
011: * by Sun in the LICENSE file that accompanied this code.
012: *
013: * This code is distributed in the hope that it will be useful, but WITHOUT
014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: * version 2 for more details (a copy is included in the LICENSE file that
017: * accompanied this code).
018: *
019: * You should have received a copy of the GNU General Public License version
020: * 2 along with this work; if not, write to the Free Software Foundation,
021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
024: * CA 95054 USA or visit www.sun.com if you need additional information or
025: * have any questions.
026: *
027: * $Revision: 1.10 $
028: * $Date: 2008/02/28 20:17:26 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import java.util.Vector;
035: import java.util.Hashtable;
036: import java.util.ArrayList;
037:
038: /**
039: * The Node class provides an abstract class for all Group and Leaf
040: * Nodes. It provides a common framework for constructing a Java 3D
041: * scene graph, including bounding volumes and parent pointers.
042: */
043: abstract class NodeRetained extends SceneGraphObjectRetained implements
044: NnuId {
045:
046: // All the node types in the scene graph
047: static final int BACKGROUND = 1;
048: static final int CLIP = 2;
049: static final int LINEARFOG = 3;
050: static final int EXPONENTIALFOG = 4;
051: static final int AMBIENTLIGHT = 5;
052: static final int DIRECTIONALLIGHT = 6;
053: static final int POINTLIGHT = 7;
054: static final int SPOTLIGHT = 8;
055: static final int LINK = 9;
056: static final int MORPH = 10;
057: static final int SHAPE = 11;
058: static final int BACKGROUNDSOUND = 12;
059: static final int POINTSOUND = 13;
060: static final int CONESOUND = 14;
061: static final int SOUNDSCAPE = 15;
062: static final int VIEWPLATFORM = 16;
063: static final int BEHAVIOR = 17;
064:
065: static final int SWITCH = 18;
066: static final int BRANCHGROUP = 19;
067: static final int ORDEREDGROUP = 20;
068: static final int DECALGROUP = 21;
069: static final int SHAREDGROUP = 22;
070: static final int GROUP = 23;
071: static final int TRANSFORMGROUP = 24;
072: static final int BOUNDINGLEAF = 25;
073: static final int MODELCLIP = 26;
074: static final int ALTERNATEAPPEARANCE = 27;
075: static final int ORIENTEDSHAPE3D = 28;
076: static final int VIEWSPECIFICGROUP = 29;
077: static final int NUMNODES = 29;
078:
079: // traverse flags
080: static final int CONTAINS_VIEWPLATFORM = 0x1;
081:
082: /**
083: * The universe that we are in
084: */
085: VirtualUniverse universe = null;
086:
087: /**
088: * The locale that this node is attatched to. This is only non-null
089: * if this instance is directly linked into a locale.
090: */
091: Locale locale = null;
092:
093: /**
094: * The node's parent.
095: */
096: NodeRetained parent = null;
097:
098: /**
099: * The node's internal identifier.
100: */
101: String nodeId = null;
102:
103: /**
104: * An int that represents the nodes type. Used for quick if tests
105: * in the traverser.
106: */
107: int nodeType;
108:
109: // This keeps track of how many times this Node is refernced, refCount > 1
110: // if node is in a shared group
111: int refCount = 0;
112:
113: /**
114: * This is the index for the child, as seen by its parent.
115: */
116: int childIndex = -1;
117:
118: /**
119: * This boolean is true when the node is in a sharedGroup
120: */
121: boolean inSharedGroup = false;
122:
123: /**
124: * This indicates if the node is pickable. If this node is not
125: * pickable then neither are any children
126: */
127: boolean pickable = true;
128:
129: /**
130: * The collidable setting; see getCollidable and setCollidable.
131: */
132: boolean collidable = true;
133:
134: // A list of localToVworld transforms. If inSharedGroup is false,
135: // then only localToVworld[0][] is valid.
136: // Note: this contains reference to the actual transforms in the
137: // TransformGroupRetained
138: Transform3D localToVworld[][] = null;
139: int localToVworldIndex[][] = null;
140:
141: static final int LAST_LOCAL_TO_VWORLD = 0;
142: static final int CURRENT_LOCAL_TO_VWORLD = 1;
143:
144: // A parallel array to localToVworld. This is the keys for
145: // localToVworld transforms in shared groups.
146: HashKey localToVworldKeys[] = null;
147:
148: /**
149: * This boolean is true when the geometric bounds for the node is
150: * automatically updated
151: */
152: boolean boundsAutoCompute = true;
153:
154: // "effective" bounds in local coordinate if boundsAutoCompute == F,
155: // used for internal operations, not used if boundsAutoCompute == T
156: Bounds localBounds;
157:
158: // Bounds set by the API
159: Bounds apiBounds;
160:
161: protected Bounds cachedBounds = null; // Cached auto compute bounds, could we use localBounds ?
162: protected boolean validCachedBounds = false; // Fix to Issue 514
163: /**
164: * Each element, p, of branchGroupPaths is a list of BranchGroup from
165: * root of the tree to this.
166: * For BranchGroup under a non-shared group this size of
167: * branchGroupPaths is always 1. Otherwise, the size is equal to
168: * the number of possible paths to reach this node.
169: * This variable is used to cached BranchGroup for fast picking.
170: * For non BranchGroupRetained class this is a reference to
171: * the previous BranchGroupRetained branchGroupPaths.
172: */
173: ArrayList branchGroupPaths = new ArrayList(1);
174:
175: // background node whose geometry branch contains this node
176: BackgroundRetained geometryBackground = null;
177:
178: // closest parent which is a TransformGroupRetained or sharedGroupRetained
179: GroupRetained parentTransformLink = null;
180:
181: // closest parent which is a SwitchRetained or sharedGroupRetained
182: GroupRetained parentSwitchLink = null;
183:
184: // static transform if a parent transform group is merged during compile.
185: TransformGroupRetained staticTransform = null;
186:
187: // orderedId assigned by OrderedGroup parent
188: Integer orderedId = null;
189:
190: // Id use for quick search.
191: int nnuId;
192:
193: NodeRetained() {
194: // Get a not necessary unique Id.
195: nnuId = NnuIdManager.getId();
196:
197: localBounds = new BoundingBox();
198: ((BoundingBox) localBounds).setUpper(-1.0, -1.0, -1.0);
199: ((BoundingBox) localBounds).setLower(1.0, 1.0, 1.0);
200: }
201:
202: public int getId() {
203: return nnuId;
204: }
205:
206: public int equal(NnuId obj) {
207: int keyId = obj.getId();
208: if (nnuId < keyId) {
209: return -1;
210: } else if (nnuId > keyId) {
211: return 1;
212: } else { // Found it!
213: return 0;
214: }
215: }
216:
217: Bounds getLocalBounds(Bounds bounds) {
218: return (Bounds) bounds.clone();
219: }
220:
221: /**
222: * Sets the geometric bounds of a node.
223: * @param bounds the bounding object for the node
224: */
225: void setBounds(Bounds bounds) {
226: apiBounds = bounds;
227: if (source.isLive()) {
228: if (!boundsAutoCompute) {
229: if (bounds != null) {
230: localBounds = getLocalBounds(bounds);
231: if (staticTransform != null) {
232: localBounds
233: .transform(staticTransform.transform);
234: }
235: } else {
236: if (localBounds != null) {
237: localBounds.set((Bounds) null);
238: } else {
239: localBounds = new BoundingBox((Bounds) null);
240: }
241: }
242: }
243: } else {
244: if (bounds != null) {
245: localBounds = getLocalBounds(bounds);
246: if (staticTransform != null) {
247: localBounds.transform(staticTransform.transform);
248: }
249: } else {
250: if (localBounds != null) {
251: localBounds.set((Bounds) null);
252: } else {
253: localBounds = new BoundingBox((Bounds) null);
254: }
255: }
256: }
257: }
258:
259: /**
260: * Gets the bounding object of a node.
261: * @return the node's bounding object
262: */
263: Bounds getEffectiveBounds() {
264: Bounds b = null;
265: if (localBounds != null && !localBounds.isEmpty()) {
266: b = (Bounds) localBounds.clone();
267: if (staticTransform != null) {
268: Transform3D invTransform = staticTransform
269: .getInvTransform();
270: b.transform(invTransform);
271: }
272: }
273: return b;
274: }
275:
276: Bounds getBounds() {
277: return apiBounds;
278: }
279:
280: /**
281: * ONLY needed for SHAPE, MORPH, and LINK node type.
282: * Compute the combine bounds of bounds and its localBounds.
283: */
284: void computeCombineBounds(Bounds bounds) {
285: // Do nothing except for Group, Shape3D, Morph, and Link node.
286: }
287:
288: /**
289: * Sets the automatic calcuation of geometric bounds of a node.
290: * @param autoCompute is a boolean value indicating if automatic calcuation
291: * of bounds
292: */
293: void setBoundsAutoCompute(boolean autoCompute) {
294: if (this .boundsAutoCompute == autoCompute) {
295: return;
296: }
297:
298: this .boundsAutoCompute = autoCompute;
299: dirtyBoundsCache();
300: }
301:
302: /**
303: * Gets the auto Compute flag for the geometric bounds.
304: * @return the node's auto Compute flag for the geometric bounding object
305: */
306: boolean getBoundsAutoCompute() {
307: return boundsAutoCompute;
308: }
309:
310: /**
311: * Replaces the specified parent by a new parent.
312: * @param parent the new parent
313: */
314: void setParent(NodeRetained parent) {
315: this .parent = parent;
316: }
317:
318: /**
319: * Returns the parent of the node.
320: * @return the parent.
321: */
322: NodeRetained getParent() {
323: return (NodeRetained) parent;
324: }
325:
326: // Transform the input bound by the current LocalToVWorld
327: void transformBounds(SceneGraphPath path, Bounds bound) {
328: if (!((NodeRetained) path.item.retained).inSharedGroup) {
329: bound.transform(getCurrentLocalToVworld());
330: } else {
331: HashKey key = new HashKey("");
332: path.getHashKey(key);
333: bound.transform(getCurrentLocalToVworld(key));
334: }
335: }
336:
337: // Note : key will get modified in this method.
338: private void computeLocalToVworld(NodeRetained caller,
339: NodeRetained nodeR, HashKey key, Transform3D l2Vw) {
340: int i;
341:
342: // To handle localToVworld under a SG.
343: if (nodeR instanceof SharedGroupRetained) {
344: // Get the immediate parent's id and remove last id from key.
345: String nodeId = key.getLastNodeId();
346:
347: SharedGroupRetained sgRetained = (SharedGroupRetained) nodeR;
348:
349: // Search for the right parent.
350: for (i = 0; i < sgRetained.parents.size(); i++) {
351:
352: if (nodeId
353: .equals((String) (((NodeRetained) (sgRetained.parents
354: .elementAt(i))).nodeId))) {
355: // Found the right link. Now traverse upward.
356:
357: computeLocalToVworld(caller,
358: (NodeRetained) (sgRetained.parents
359: .elementAt(i)), key, l2Vw);
360: return;
361: }
362: }
363: // Problem !
364: throw new RuntimeException(J3dI18N
365: .getString("NodeRetained4"));
366: } else {
367:
368: NodeRetained nodeParentR = (NodeRetained) nodeR.getParent();
369:
370: if (nodeParentR == null) {
371: // Base case. It has to be a BG attached to a locale.
372: if (((BranchGroupRetained) (nodeR)).locale != null) {
373: l2Vw.setIdentity();
374: } else {
375: throw new RuntimeException(J3dI18N
376: .getString("NodeRetained5"));
377: }
378: } else {
379: computeLocalToVworld(caller,
380: (NodeRetained) nodeParentR, key, l2Vw);
381:
382: }
383:
384: }
385:
386: if ((nodeR instanceof TransformGroupRetained)
387: && (nodeR != caller)) {
388: Transform3D t1 = new Transform3D();
389: ((TransformGroupRetained) (nodeR)).transform
390: .getWithLock(t1);
391: l2Vw.mul(t1);
392: } else if ((nodeR == caller) && (staticTransform != null)) {
393: l2Vw.mul(staticTransform.transform);
394: }
395:
396: return;
397: }
398:
399: /**
400: * Compute the LocalToVworld of this node even though it is not live. We
401: * assume the graph is attached at the origin of a locale
402: */
403: void computeNonLiveLocalToVworld(Transform3D t, Node caller) {
404: NodeRetained n = getParent();
405:
406: if (n == null)
407: t.setIdentity();
408: else
409: n.computeNonLiveLocalToVworld(t, caller);
410:
411: if (this instanceof TransformGroupRetained
412: && this .source != caller) {
413: Transform3D trans = new Transform3D();
414: ((TransformGroupRetained) this ).getTransform(trans);
415: t.mul(trans);
416: }
417:
418: }
419:
420: /**
421: * Get the localToVworld transform for a node.
422: */
423: void getLocalToVworld(Transform3D t) {
424: if (inSharedGroup) {
425: throw new IllegalSharingException(J3dI18N
426: .getString("NodeRetained0"));
427: }
428:
429: // Lock the object while writing into t.
430: if (localToVworld == null) {
431: t.setIdentity();
432: } else {
433: computeLocalToVworld(this , this , null, t);
434: }
435: }
436:
437: /**
438: * Get the localToVworld transform for a node.
439: */
440: void getLocalToVworld(SceneGraphPath path, Transform3D t) {
441: HashKey key = new HashKey("");
442:
443: if (inSharedGroup == false) {
444: throw new IllegalSharingException(J3dI18N
445: .getString("NodeRetained1"));
446: }
447: path.validate(key);
448: computeLocalToVworld(this , this , key, t);
449:
450: }
451:
452: /**
453: * Get the localToVworld transform for a node
454: */
455: void getLocalToVworld(Transform3D t, HashKey key) {
456: HashKey newKey = new HashKey(key);
457: computeLocalToVworld(this , this , newKey, t);
458: }
459:
460: /**
461: * Get the Locale to which the node is attached
462: */
463: Locale getLocale() {
464: if (inSharedGroup) {
465: throw new IllegalSharingException(J3dI18N
466: .getString("NodeRetained0"));
467: }
468:
469: return locale;
470: }
471:
472: /**
473: * Get the current localToVworld transform for a node
474: */
475: Transform3D getCurrentLocalToVworld() {
476:
477: if (localToVworld != null) {
478: return localToVworld[0][localToVworldIndex[0][CURRENT_LOCAL_TO_VWORLD]];
479: } else {
480: return new Transform3D();
481: }
482: }
483:
484: // this method expects that localToVworld is not null
485:
486: Transform3D getCurrentLocalToVworld(int index) {
487: return localToVworld[index][localToVworldIndex[index][CURRENT_LOCAL_TO_VWORLD]];
488: }
489:
490: Transform3D getCurrentLocalToVworld(HashKey key) {
491:
492: if (localToVworld != null) {
493: if (!inSharedGroup) {
494: return localToVworld[0][localToVworldIndex[0][CURRENT_LOCAL_TO_VWORLD]];
495: } else {
496: int i = key.equals(localToVworldKeys, 0,
497: localToVworldKeys.length);
498: if (i >= 0) {
499: return localToVworld[i][localToVworldIndex[i][CURRENT_LOCAL_TO_VWORLD]];
500: }
501: }
502: }
503: return new Transform3D();
504: }
505:
506: /**
507: * Get the last localToVworld transform for a node
508: */
509: Transform3D getLastLocalToVworld() {
510:
511: if (localToVworld != null) {
512: return localToVworld[0][localToVworldIndex[0][LAST_LOCAL_TO_VWORLD]];
513: } else {
514: return new Transform3D();
515: }
516: }
517:
518: Transform3D getLastLocalToVworld(int index) {
519: return localToVworld[index][localToVworldIndex[index][LAST_LOCAL_TO_VWORLD]];
520: }
521:
522: Transform3D getLastLocalToVworld(HashKey key) {
523:
524: if (localToVworld != null) {
525: if (!inSharedGroup) {
526: return localToVworld[0][localToVworldIndex[0][LAST_LOCAL_TO_VWORLD]];
527: } else {
528: int i = key.equals(localToVworldKeys, 0,
529: localToVworldKeys.length);
530: if (i >= 0) {
531: return localToVworld[i][localToVworldIndex[i][LAST_LOCAL_TO_VWORLD]];
532: }
533: }
534: }
535: return new Transform3D();
536: }
537:
538: // Do nothing for NodeRetained.
539: void setAuxData(SetLiveState s, int index, int hkIndex) {
540:
541: }
542:
543: void setNodeData(SetLiveState s) {
544: localToVworld = s.localToVworld;
545: localToVworldIndex = s.localToVworldIndex;
546: localToVworldKeys = s.localToVworldKeys;
547:
548: // reference to the last branchGroupPaths
549: branchGroupPaths = s.parentBranchGroupPaths;
550:
551: parentTransformLink = s.parentTransformLink;
552: parentSwitchLink = s.parentSwitchLink;
553: }
554:
555: // set pickable, recursively update cache result
556: void setPickable(boolean pickable) {
557: if (this .pickable == pickable)
558: return;
559:
560: this .pickable = pickable;
561:
562: if (source.isLive()) {
563: synchronized (universe.sceneGraphLock) {
564: boolean pick[];
565: if (!inSharedGroup) {
566: pick = new boolean[1];
567: } else {
568: pick = new boolean[localToVworldKeys.length];
569: }
570:
571: findPickableFlags(pick);
572: updatePickable(localToVworldKeys, pick);
573: }
574: }
575: }
576:
577: void updatePickable(HashKey pickKeys[], boolean pick[]) {
578: for (int i = 0; i < pick.length; i++) {
579: if (!pickable) {
580: pick[i] = false;
581: }
582: }
583: }
584:
585: // get pickable
586: boolean getPickable() {
587: return pickable;
588: }
589:
590: // set collidable, recursively update cache result
591: void setCollidable(boolean collidable) {
592: if (this .collidable == collidable)
593: return;
594:
595: this .collidable = collidable;
596:
597: if (source.isLive()) {
598: synchronized (universe.sceneGraphLock) {
599: boolean collide[];
600: if (!inSharedGroup) {
601: collide = new boolean[1];
602: } else {
603: collide = new boolean[localToVworldKeys.length];
604: }
605:
606: findCollidableFlags(collide);
607: updateCollidable(localToVworldKeys, collide);
608: }
609: }
610: }
611:
612: // get collidable
613: boolean getCollidable() {
614: return collidable;
615: }
616:
617: void updateCollidable(HashKey keys[], boolean collide[]) {
618: for (int i = 0; i < collide.length; i++) {
619: if (!collidable) {
620: collide[i] = false;
621: }
622: }
623: }
624:
625: /**
626: * For the default, just pass up to parent
627: */
628: void notifySceneGraphChanged(boolean globalTraverse) {
629: }
630:
631: void recombineAbove() {
632: }
633:
634: synchronized void updateLocalToVworld() {
635: }
636:
637: void setLive(SetLiveState s) {
638: int oldrefCount = refCount;
639:
640: doSetLive(s);
641: if (oldrefCount <= 0)
642: super .markAsLive();
643: }
644:
645: // The default set of setLive actions.
646: void doSetLive(SetLiveState s) {
647: int i;
648: int oldrefCount = refCount;
649:
650: refCount += s.refCount;
651: if (!(locale == null || universe == s.universe))
652: throw new IllegalSharingException(J3dI18N
653: .getString("NodeRetained3"));
654: if (s.locale == null)
655: System.err.println("NodeRetained.setLive() locale is null");
656:
657: locale = s.locale;
658: inSharedGroup = s.inSharedGroup;
659:
660: if (oldrefCount <= 0) {
661: if (listIdx == null) {
662: universe = s.universe;
663: } else {
664: // sync with getIdxUsed()
665: if (s.universe != universe) {
666: synchronized (this ) {
667: universe = s.universe;
668: incIdxUsed();
669: }
670: }
671: }
672: }
673: s.universe.numNodes++;
674:
675: // pickable & collidable array have the same length
676: for (i = 0; i < s.pickable.length; i++) {
677: if (!pickable) {
678: s.pickable[i] = false;
679: }
680: if (!collidable) {
681: s.collidable[i] = false;
682: }
683: }
684:
685: if (oldrefCount <= 0)
686: super .doSetLive(s);
687:
688: if (inBackgroundGroup) {
689: geometryBackground = s.geometryBackground;
690: }
691:
692: setNodeData(s);
693: }
694:
695: /**
696: * remove the localToVworld transform for this node.
697: */
698: void removeNodeData(SetLiveState s) {
699:
700: if (refCount <= 0) {
701: localToVworld = null;
702: localToVworldIndex = null;
703: localToVworldKeys = null;
704: // restore to default and avoid calling clear()
705: // that may clear parent reference branchGroupPaths
706: branchGroupPaths = new ArrayList(1);
707: parentTransformLink = null;
708: parentSwitchLink = null;
709: } else {
710: // Set it back to its parent localToVworld data. This is b/c the parent has
711: // changed it localToVworld data arrays.
712: localToVworld = s.localToVworld;
713: localToVworldIndex = s.localToVworldIndex;
714: localToVworldKeys = s.localToVworldKeys;
715:
716: // Reference of parent branchGroupPaths will not change
717:
718: // no need to reset parentSwitchLink or parentTransformLink
719: // because there are not per path data
720: }
721:
722: }
723:
724: // The default set of clearLive actions
725: void clearLive(SetLiveState s) {
726:
727: refCount -= s.refCount;
728:
729: if (refCount <= 0) {
730: super .clearLive();
731:
732: // don't remove the nodeId unless there are no more references
733: if (nodeId != null) {
734: universe.nodeIdFreeList.addElement(nodeId);
735: nodeId = null;
736: }
737: }
738:
739: universe.numNodes--;
740:
741: removeNodeData(s);
742:
743: if (refCount <= 0) {
744: locale = null;
745: geometryBackground = null;
746: }
747: }
748:
749: // search up the parent to determine if this node is pickable
750: void findPickableFlags(boolean pick[]) {
751: NodeRetained nodeR = this ;
752:
753: if (!inSharedGroup) {
754: pick[0] = true;
755: nodeR = nodeR.parent;
756: while (nodeR != null) {
757: if (!nodeR.pickable) {
758: pick[0] = false;
759: break;
760: }
761: nodeR = nodeR.parent;
762: }
763: } else {
764: HashKey key;
765: for (int i = 0; i < pick.length; i++) {
766: nodeR = this ;
767: pick[i] = true;
768: key = new HashKey(localToVworldKeys[i]);
769:
770: do {
771: if (nodeR instanceof SharedGroupRetained) {
772: String nodeId = key.getLastNodeId();
773: Vector parents = ((SharedGroupRetained) nodeR).parents;
774: int sz = parents.size();
775: NodeRetained prevNodeR = nodeR;
776: for (int j = 0; j < sz; j++) {
777: NodeRetained linkR = (NodeRetained) parents
778: .elementAt(j);
779: if (linkR.nodeId.equals(nodeId)) {
780: nodeR = linkR;
781: break;
782: }
783: }
784: if (prevNodeR == nodeR) {
785: // branch is already detach
786: return;
787: }
788: } else {
789: nodeR = nodeR.parent;
790: }
791: if (nodeR == null)
792: break;
793: if (!nodeR.pickable) {
794: pick[i] = false;
795: break;
796: }
797: } while (true);
798: }
799: }
800: }
801:
802: // search up the parent to determine if this node is collidable
803: void findCollidableFlags(boolean collide[]) {
804: NodeRetained nodeR = this ;
805:
806: if (!inSharedGroup) {
807: collide[0] = true;
808: nodeR = nodeR.parent;
809: while (nodeR != null) {
810: if (!nodeR.collidable) {
811: collide[0] = false;
812: break;
813: }
814: nodeR = nodeR.parent;
815: }
816: } else {
817: HashKey key;
818: for (int i = 0; i < collide.length; i++) {
819: nodeR = this ;
820: collide[i] = true;
821: key = new HashKey(localToVworldKeys[i]);
822:
823: do {
824: if (nodeR instanceof SharedGroupRetained) {
825: String nodeId = key.getLastNodeId();
826: Vector parents = ((SharedGroupRetained) nodeR).parents;
827: int sz = parents.size();
828: NodeRetained prevNodeR = nodeR;
829: for (int j = 0; j < sz; j++) {
830: NodeRetained linkR = (NodeRetained) parents
831: .elementAt(j);
832: if (linkR.nodeId.equals(nodeId)) {
833: nodeR = linkR;
834: break;
835: }
836: }
837: if (nodeR == prevNodeR) {
838: return;
839: }
840: } else {
841: nodeR = nodeR.parent;
842: }
843: if (nodeR == null)
844: break;
845: if (!nodeR.collidable) {
846: collide[i] = false;
847: break;
848: }
849: } while (true);
850: }
851: }
852: }
853:
854: void findTransformLevels(int transformLevels[]) {
855: NodeRetained nodeR = this ;
856: TransformGroupRetained tg;
857:
858: if (!inSharedGroup) {
859: transformLevels[0] = -1;
860: while (nodeR != null) {
861: if (nodeR.nodeType == NodeRetained.TRANSFORMGROUP) {
862: tg = (TransformGroupRetained) nodeR;
863: transformLevels[0] = tg.transformLevels[0];
864: break;
865: }
866: nodeR = nodeR.parent;
867: }
868: } else {
869: HashKey key;
870: int i, j;
871: for (i = 0; i < transformLevels.length; i++) {
872: nodeR = this ;
873: transformLevels[i] = -1;
874: key = new HashKey(localToVworldKeys[i]);
875:
876: do {
877: if (nodeR == null)
878: break;
879: else if (nodeR instanceof SharedGroupRetained) {
880: // note that key is truncated after getLastNodeId
881: String nodeId = key.getLastNodeId();
882: Vector parents = ((SharedGroupRetained) nodeR).parents;
883: int sz = parents.size();
884: NodeRetained prevNodeR = nodeR;
885: for (j = 0; j < sz; j++) {
886: NodeRetained linkR = (NodeRetained) parents
887: .elementAt(j);
888: if (linkR.nodeId.equals(nodeId)) {
889: nodeR = linkR;
890: break;
891: }
892: }
893: if (prevNodeR == nodeR) {
894: // branch is already detach
895: return;
896: }
897: } else if (nodeR.nodeType == NodeRetained.TRANSFORMGROUP) {
898: tg = (TransformGroupRetained) nodeR;
899: if (tg.inSharedGroup) {
900:
901: j = key.equals(tg.localToVworldKeys, 0,
902: tg.localToVworldKeys.length);
903:
904: transformLevels[i] = tg.transformLevels[j];
905: } else {
906: transformLevels[i] = tg.transformLevels[0];
907: }
908: break;
909: }
910:
911: nodeR = nodeR.parent;
912: } while (true);
913: }
914: }
915: }
916:
917: boolean isStatic() {
918: if (source.getCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ)
919: || source.getCapability(Node.ALLOW_PARENT_READ)
920: || source.getCapability(Node.ENABLE_PICK_REPORTING)
921: || source
922: .getCapability(Node.ENABLE_COLLISION_REPORTING)
923: || source.getCapability(Node.ALLOW_BOUNDS_READ)
924: || source.getCapability(Node.ALLOW_BOUNDS_WRITE)
925: || source.getCapability(Node.ALLOW_PICKABLE_READ)
926: || source.getCapability(Node.ALLOW_PICKABLE_WRITE)
927: || source.getCapability(Node.ALLOW_COLLIDABLE_READ)
928: || source.getCapability(Node.ALLOW_COLLIDABLE_WRITE)
929: || source
930: .getCapability(Node.ALLOW_AUTO_COMPUTE_BOUNDS_READ)
931: || source
932: .getCapability(Node.ALLOW_AUTO_COMPUTE_BOUNDS_WRITE)) {
933: return false;
934: }
935: return true;
936: }
937:
938: void merge(CompileState compState) {
939: staticTransform = compState.staticTransform;
940: if (compState.parentGroup != null) {
941: compState.parentGroup.compiledChildrenList.add(this );
942: }
943: parent = compState.parentGroup;
944: if (staticTransform != null) {
945: mergeTransform(staticTransform);
946: }
947: }
948:
949: void mergeTransform(TransformGroupRetained xform) {
950: if (localBounds != null) {
951: localBounds.transform(xform.transform);
952: }
953: }
954:
955: int[] processViewSpecificInfo(int mode, HashKey k, View v,
956: ArrayList vsgList, int[] keyList, ArrayList leafList) {
957: return keyList;
958:
959: }
960:
961: VirtualUniverse getVirtualUniverse() {
962: return universe;
963: }
964:
965: void searchGeometryAtoms(UnorderList list) {
966: }
967:
968: /**
969: * Make the boundsCache of this node and all its parents dirty
970: */
971: void dirtyBoundsCache() {
972: // Possible optimisation is to not traverse up the tree
973: // if the cachedBounds==null. However this is not the case
974: // if the node is the child of a SharedGroup
975: if (VirtualUniverse.mc.cacheAutoComputedBounds) {
976: // Issue 514 : NPE in Wonderland : triggered in cached bounds computation
977: validCachedBounds = false;
978: if (parent != null) {
979: parent.dirtyBoundsCache();
980: }
981: }
982: }
983: }
|