1 /*
2  * $RCSfile: Shape3DRetained.java,v $
3  *
4  * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6  *
7  * This code is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 only, as
9  * published by the Free Software Foundation.  Sun designates this
10  * particular file as subject to the "Classpath" exception as provided
11  * by Sun in the LICENSE file that accompanied this code.
12  *
13  * This code is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16  * version 2 for more details (a copy is included in the LICENSE file that
17  * accompanied this code).
18  *
19  * You should have received a copy of the GNU General Public License version
20  * 2 along with this work; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
24  * CA 95054 USA or visit www.sun.com if you need additional information or
25  * have any questions.
26  *
27  * $Revision: 1.12 $
28  * $Date: 2008/02/28 20:17:30 $
29  * $State: Exp $
30  */
31 
32 package javax.media.j3d;
33 
34 import javax.vecmath.*;
35 import java.util.ArrayList;
36 import java.util.Enumeration;
37 import java.util.Vector;
38 
39 /**
40  * A shape leaf node consisting of geometry and appearance properties.
41  */
42 
43 class Shape3DRetained extends LeafRetained {
44 
45     static final int GEOMETRY_CHANGED		= 0x00001;
46     static final int APPEARANCE_CHANGED 	= 0x00002;
47     static final int COLLISION_CHANGED		= 0x00004;
48     static final int BOUNDS_CHANGED		= 0x00008;
49     static final int APPEARANCEOVERRIDE_CHANGED	= 0x00010;
50     static final int LAST_DEFINED_BIT        = 0x00010;
51 
52 
53     // Target threads to be notified when light changes
54     static final int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT |
55                                      J3dThread.UPDATE_RENDER;
56 
57     /**
58      * The appearance component of the shape node.
59      */
60     AppearanceRetained appearance = null;
61 
62     /**
63      * The arraylist of geometry component of the shape node.
64      */
65     ArrayList geometryList = null;
66 
67     /**
68      * A 2D storage of all geometry atoms associated with this shape node.
69      * There may be more than one geometry for a Shape3D node.
70      * Do not change the following private variables to public, its access need to synchronize
71      * via mirrorShape3DLock.
72      */
73 
74     // geomAtomArr should always be a 1 element array, unless S3D contains multiple Text3Ds.
75     private GeometryAtom geomAtom = null;
76 
77     /**
78      * To sychronize access of the mirrorShape3D's geomAtomArray*.
79      * A multiple read single write Lock to sychronize access into mirrorShape3D.
80      * To prevent deadlock a call to read/write lock must end with a read/write unlock
81      * respectively.
82      */
83     private MRSWLock mirrorShape3DLock = null;
84 
85     /**
86      * The mirror Shape3DRetained nodes for this object.  There is one
87      * mirror for each instance of this Shape3D node.  If it is not in
88      * a SharedGroup, only index 0 is valid.
89      * Do not change the following private variables to public, its access need to synchronize
90      * via mirrorShape3DLock.
91      */
92     ArrayList mirrorShape3D = new ArrayList(1);
93 
94     /**
95      * This field is used for mirror Shape3D nodes accessing their
96      * original nodes.  It is a NodeRetained because the original
97      * node may be a Shape3DRetained or a MorphRetained node.
98      */
99     NodeRetained sourceNode = null;
100 
101     /**
102      * The hashkey for this Shape3DRetained mirror object
103      */
104     HashKey key = null;
105 
106     // This is true when this geometry is referenced in an IMM mode context
107     boolean inImmCtx = false;
108 
109     // A bitmask to indicate when something has changed
110     int isDirty = 0xffff;
111 
112     // The list of lights that are scoped to this node
113     LightRetained[] lights =null;
114 
115     // The number of lights in the above array, may be less than lights.length
116     int numlights = 0;
117 
118     // The list of fogs that are scoped to this node
119     FogRetained[] fogs = null;
120 
121     // The number of fogs in the above array, may be less than fogs.length
122     int numfogs = 0;
123 
124     // The list of modelClips that are scoped to this node
125     ModelClipRetained[] modelClips = null;
126 
127     // The number of modelClips in the above array, may be less than modelClips.length
128     int numModelClips = 0;
129 
130     // The list of alt app that are scoped to this node
131     AlternateAppearanceRetained[] altApps = null;
132 
133     //The number of alt app in the above array, may be less than alt app.length
134     int numAltApps = 0;
135 
136     /**
137      * Reference to the  BranchGroup path of this mirror shape
138      * This is used for picking and GeometryStructure only.
139      */
140     BranchGroupRetained branchGroupPath[];
141 
142     // cache value for picking in mirror shape.
143     // True if all the node of the path from this to root are all pickable
144     boolean isPickable = true;
145 
146     // cache value for collidable in mirror shape.
147     // True if all the node of the path from this to root are all collidable
148     boolean isCollidable = true;
149 
150     // closest switch parent
151     SwitchRetained  closestSwitchParent = null;
152 
153     // the child index from the closest switch parent
154     int closestSwitchIndex = -1;
155 
156     // Is this S3D visible ? The default is true.
157     boolean visible = true;
158 
159     // Whether the normal appearance is overrided by the alternate app
160     boolean appearanceOverrideEnable = false;
161 
162     // AlternateAppearance retained that is applicable to this
163     // mirror shape when the override flag is true
164     AppearanceRetained otherAppearance = null;
165 
166     // geometry Bounds in local coordinate
167     Bounds bounds = null;
168 
169     // geometry Bounds in virtual world coordinate
170     BoundingBox vwcBounds = null;
171 
172     // collision Bounds in local coordinate
173     Bounds collisionBound = null;
174 
175     // collision Bounds in virtual world coordinate
176     Bounds collisionVwcBound = null;
177 
178     // a path of OrderedGroup, childrenId pairs which leads to this node
179     OrderedPath orderedPath = null;
180 
181     // List of views that a mirror object is scoped to
182     ArrayList viewList = null;
183 
184     int changedFrequent = 0;
185 
Shape3DRetained()186     Shape3DRetained() {
187 	super();
188         this.nodeType = NodeRetained.SHAPE;
189 	numlights = 0;
190 	numfogs = 0;
191 	numModelClips = 0;
192 	numAltApps = 0;
193 	localBounds = new BoundingBox((BoundingBox) null);
194 
195 	mirrorShape3DLock = new MRSWLock();
196 	geometryList = new ArrayList(1);
197 	geometryList.add(null);
198     }
199 
200     /**
201      * Sets the collision bounds of a node.
202      * @param bounds the bounding object for the node
203      */
setCollisionBounds(Bounds bounds)204     void setCollisionBounds(Bounds bounds) {
205         if (bounds == null) {
206             this.collisionBound = null;
207 	} else {
208 	    this.collisionBound = (Bounds)bounds.clone();
209 	}
210 
211 	if (source.isLive()) {
212 	    // Notify Geometry Structure to check for collision
213 	    J3dMessage message = new J3dMessage();
214 	    message.type = J3dMessage.COLLISION_BOUND_CHANGED;
215 	    message.threads = J3dThread.UPDATE_TRANSFORM;
216 	    message.universe = universe;
217 	    message.args[0] = getGeomAtomsArray(mirrorShape3D);
218 	    // no need to clone collisionBound
219 	    message.args[1] = collisionBound;
220 	    VirtualUniverse.mc.processMessage(message);
221 	}
222     }
223 
getLocalBounds(Bounds bounds)224     Bounds getLocalBounds(Bounds bounds) {
225 	if(localBounds != null) {
226 	    localBounds.set(bounds);
227 	}
228 	else {
229 	    localBounds = new BoundingBox(bounds);
230 	}
231 	return localBounds;
232     }
233 
234 
235     /**
236      * Sets the geometric bounds of a node.
237      * @param bounds the bounding object for the node
238      */
setBounds(Bounds bounds)239     void setBounds(Bounds bounds) {
240 	super.setBounds(bounds);
241 
242 	if (source.isLive() && !boundsAutoCompute) {
243 	    J3dMessage message = new J3dMessage();
244 	    message.type = J3dMessage.REGION_BOUND_CHANGED;
245             message.threads = J3dThread.UPDATE_TRANSFORM |
246                               J3dThread.UPDATE_GEOMETRY |
247                               J3dThread.UPDATE_RENDER;
248 
249 	    message.universe = universe;
250  	    message.args[0] = getGeomAtomsArray(mirrorShape3D);
251 	    // no need to clone localBounds
252             message.args[1] = localBounds;
253 	    VirtualUniverse.mc.processMessage(message);
254 	}
255     }
256 
257     /**
258      * Gets the collision bounds of a node.
259      * @return the node's bounding object
260      */
getCollisionBounds(int id)261     Bounds getCollisionBounds(int id) {
262       return (collisionBound == null ?
263 	      null: (Bounds)collisionBound.clone());
264     }
265 
266     /**
267      * Appends the specified geometry component to this Shape3D
268      * node's list of geometry components.
269      * If there are existing geometry components in the list, the new
270      * geometry component must be of the same equivalence class
271      * (point, line, polygon, CompressedGeometry, Raster, Text3D) as
272      * the others.
273      * @param geometry the geometry component to be appended.
274      * @exception IllegalArgumentException if the new geometry
275      * component is not of of the same equivalence class as the
276      * existing geometry components.
277      *
278      * @since Java 3D 1.2
279      */
addGeometry(Geometry geometry)280     void addGeometry(Geometry geometry) {
281 	int i;
282 	Shape3DRetained s;
283 	GeometryRetained newGeom = null;
284 
285 	checkEquivalenceClass(geometry, -1);
286 
287 	if(((Shape3D)this.source).isLive()) {
288 	    if (geometry != null) {
289 
290 		newGeom = ((GeometryRetained)geometry.retained);
291                 newGeom.setLive(inBackgroundGroup, refCount);
292 
293 		geometryList.add(newGeom);
294 
295 	    } else {
296 		geometryList.add(null);
297 		newGeom = null;
298 	    }
299 	    sendDataChangedMessage(newGeom);
300 
301         } else {
302 	    if (geometry != null) {
303                 geometryList.add((GeometryRetained) geometry.retained);
304 	    } else {
305 	        geometryList.add(null);
306 	    }
307 	}
308         dirtyBoundsCache();
309     }
310 
311     /**
312      * Replaces the geometry component at the specified index in this
313      * Shape3D node's list of geometry components with the specified
314      * geometry component.
315      * If there are existing geometry components in the list (besides
316      * the one being replaced), the new geometry component must be of
317      * the same equivalence class (point, line, polygon, CompressedGeometry,
318      * Raster, Text3D) as the others.
319      * @param geometry the geometry component to be stored at the
320      * specified index.
321      * @param index the index of the geometry component to be replaced.
322      * @exception IllegalArgumentException if the new geometry
323      * component is not of of the same equivalence class as the
324      * existing geometry components.
325      *
326      * @since Java 3D 1.2
327      */
setGeometry(Geometry geometry, int index)328     void setGeometry(Geometry geometry, int index) {
329 	int i;
330 	Shape3DRetained mShape;
331 	GeometryRetained newGeom = null;
332 	GeometryRetained oldGeom = null;
333 
334 	checkEquivalenceClass(geometry, index);
335 
336 	if (((Shape3D)this.source).isLive()) {
337 
338 	    oldGeom = (GeometryRetained) (geometryList.get(index));
339 	    if (oldGeom != null) {
340 		oldGeom.clearLive(refCount);
341 		for (i=0; i<mirrorShape3D.size(); i++) {
342 		    mShape = (Shape3DRetained)mirrorShape3D.get(i);
343 		    oldGeom.removeUser(mShape);
344 		}
345 		oldGeom.decRefCnt();
346 	    }
347 
348             if (geometry != null) {
349 		newGeom = (GeometryRetained) geometry.retained;
350 		newGeom.incRefCnt();
351                 newGeom.setLive(inBackgroundGroup, refCount);
352 		geometryList.set(index, newGeom);
353 		sendDataChangedMessage(newGeom);
354 	    } else {
355 		geometryList.set(index, null);
356 		sendDataChangedMessage(null);
357 	    }
358 
359         } else {
360 
361 	    oldGeom = (GeometryRetained) (geometryList.get(index));
362 	    if (oldGeom != null) {
363 		oldGeom.decRefCnt();
364 	    }
365 	    if (geometry != null) {
366                 geometryList.set(index,(GeometryRetained) geometry.retained);
367 		((GeometryRetained)geometry.retained).incRefCnt();
368 	    } else {
369 	        geometryList.set(index,null);
370 	    }
371 	}
372         dirtyBoundsCache();
373     }
374 
375     /**
376      * Inserts the specified geometry component into this Shape3D
377      * node's list of geometry components at the specified index.
378      * If there are existing geometry components in the list, the new
379      * geometry component must be of the same equivalence class
380      * (point, line, polygon, CompressedGeometry, Raster, Text3D) as
381      * the others.
382      * @param geometry the geometry component to be inserted at the
383      * specified index.
384      * @param index the index at which the geometry component is inserted.
385      *
386      * @since Java 3D 1.2
387      */
insertGeometry(Geometry geometry, int index)388     void insertGeometry(Geometry geometry, int index) {
389 	int i;
390 	Shape3DRetained mShape;
391 	GeometryRetained newGeom = null;
392 	GeometryRetained oldGeom = null;
393 
394 	checkEquivalenceClass(geometry, -1);
395 
396 	if (((Shape3D)this.source).isLive()) {
397 
398 	    if (geometry != null) {
399 		// Note : The order of the statements in important. Want ArrayList class to do index bounds
400 		// check before creating internal object.
401 		newGeom = (GeometryRetained) geometry.retained;
402 		newGeom.incRefCnt();
403 		geometryList.add(index, newGeom);
404 		newGeom.setLive(inBackgroundGroup, refCount);
405 		sendDataChangedMessage(newGeom);
406 	    } else {
407 		geometryList.add(index, null);
408 		sendDataChangedMessage(null);
409 	    }
410 
411         } else {
412 
413 	    if (geometry != null) {
414 		geometryList.add(index,(GeometryRetained) geometry.retained);
415 		((GeometryRetained)geometry.retained).incRefCnt();
416 	    } else {
417 		geometryList.add(index,null);
418 	    }
419 	}
420         dirtyBoundsCache();
421     }
422 
423     /**
424      * Removes the geometry component at the specified index from
425      * this Shape3D node's list of geometry components.
426      * @param index the index of the geometry component to be removed.
427      *
428      * @since Java 3D 1.2
429      */
removeGeometry(int index)430     void removeGeometry(int index) {
431 	int i;
432 	Shape3DRetained mShape;
433 	GeometryRetained oldGeom = null;
434 
435 	if (((Shape3D)this.source).isLive()) {
436 
437 	    oldGeom = (GeometryRetained) (geometryList.get(index));
438 	    if (oldGeom != null) {
439 		oldGeom.clearLive(refCount);
440 		oldGeom.decRefCnt();
441 		for (i=0; i<mirrorShape3D.size(); i++) {
442 		    mShape = (Shape3DRetained)mirrorShape3D.get(i);
443 		    oldGeom.removeUser(mShape);
444 
445 		}
446 	    }
447 
448 	    geometryList.remove(index);
449 	    sendDataChangedMessage(null);
450 
451 	} else {
452 	    oldGeom = (GeometryRetained) (geometryList.get(index));
453 	    if (oldGeom != null) {
454 		oldGeom.decRefCnt();
455 	    }
456 	    geometryList.remove(index);
457 	}
458 
459         dirtyBoundsCache();
460 
461     }
462 
463     /**
464      * Retrieves the geometry component of this Shape3D node.
465      * @return the geometry component of this shape node
466      *
467      * @since Java 3D 1.2
468      */
getGeometry(int index, int id)469     Geometry getGeometry(int index, int id) {
470 	GeometryRetained ga = (GeometryRetained) geometryList.get(index);
471         return (ga == null ? null : (Geometry)ga.source);
472     }
473 
474 
475     /**
476      * Returns an enumeration of this Shape3D node's list of geometry
477      * components.
478      * @return an Enumeration object containing all geometry components in
479      * this Shape3D node's list of geometry components.
480      *
481      * @since Java 3D 1.2
482      */
getAllGeometries(int id)483     Enumeration getAllGeometries(int id) {
484 	GeometryRetained ga = null;
485 	Vector geomList = new Vector(geometryList.size());
486 
487 	for(int i=0; i<geometryList.size(); i++) {
488 	    ga = (GeometryRetained) geometryList.get(i);
489 	    if(ga != null)
490 		geomList.add((Geometry)ga.source);
491 	    else
492 		geomList.add(null);
493 	}
494 
495 	return geomList.elements();
496     }
497 
498     /**
499      * Returns the number of geometry components in this Shape3D node's
500      * list of geometry components.
501      * @return the number of geometry components in this Shape3D node's
502      * list of geometry components.
503      *
504      * @since Java 3D 1.2
505      */
numGeometries(int id)506     int numGeometries(int id) {
507 
508 	return geometryList.size();
509     }
510 
511     /**
512      * Sets the appearance component of this Shape3D node.
513      * @param appearance the new apearance component for this shape node
514      */
setAppearance(Appearance newAppearance)515     void setAppearance(Appearance newAppearance) {
516 
517 	Shape3DRetained s;
518 	boolean visibleIsDirty = false;
519 
520 	if (((Shape3D)this.source).isLive()) {
521 	    if (appearance != null) {
522 	        appearance.clearLive(refCount);
523 		for (int i=0; i<mirrorShape3D.size(); i++) {
524 		    s = (Shape3DRetained)mirrorShape3D.get(i);
525 		    appearance.removeAMirrorUser(s);
526 		}
527 	    }
528 
529 	    if (newAppearance != null) {
530 	       ((AppearanceRetained)newAppearance.retained).setLive(inBackgroundGroup, refCount);
531 		appearance = ((AppearanceRetained)newAppearance.retained);
532 		for (int i=0; i<mirrorShape3D.size(); i++) {
533 		    s = (Shape3DRetained)mirrorShape3D.get(i);
534 		    appearance.addAMirrorUser(s);
535 		}
536 		if((appearance.renderingAttributes != null) &&
537 		   (visible !=  appearance.renderingAttributes.visible)) {
538 		    visible = appearance.renderingAttributes.visible;
539 		    visibleIsDirty = true;
540 		}
541 	    }
542 	    else {
543 		if(visible == false) {
544 		    visible = true;
545 		    visibleIsDirty = true;
546 	       }
547 	    }
548 	    int size = 0;
549 	    if (visibleIsDirty)
550 		size = 2;
551 	    else
552 		size = 1;
553 	    J3dMessage[] createMessage = new J3dMessage[size];
554 	    // Send a message
555 	    createMessage[0] = new J3dMessage();
556 	    createMessage[0].threads = targetThreads;
557 	    createMessage[0].type = J3dMessage.SHAPE3D_CHANGED;
558 	    createMessage[0].universe = universe;
559 	    createMessage[0].args[0] = this;
560 	    createMessage[0].args[1]= new Integer(APPEARANCE_CHANGED);
561 	    Shape3DRetained[] s3dArr = new Shape3DRetained[mirrorShape3D.size()];
562 	    mirrorShape3D.toArray(s3dArr);
563 	    createMessage[0].args[2] = s3dArr;
564 	    Object[] obj = new Object[2];
565 	    if (newAppearance == null) {
566 		obj[0] = null;
567 	    }
568 	    else {
569 		obj[0]  = appearance.mirror;
570 	    }
571 	    obj[1] = new Integer(changedFrequent);
572 	    createMessage[0].args[3] = obj;
573 	    createMessage[0].args[4] = getGeomAtomsArray(mirrorShape3D);
574 	    if(visibleIsDirty) {
575 		createMessage[1] = new J3dMessage();
576 		createMessage[1].threads = J3dThread.UPDATE_GEOMETRY;
577 		createMessage[1].type = J3dMessage.SHAPE3D_CHANGED;
578 		createMessage[1].universe = universe;
579 		createMessage[1].args[0] = this;
580 		createMessage[1].args[1]= new Integer(APPEARANCE_CHANGED);
581 		createMessage[1].args[2]= visible?Boolean.TRUE:Boolean.FALSE;
582 		createMessage[1].args[3]= createMessage[0].args[4];
583 	    }
584 	    VirtualUniverse.mc.processMessage(createMessage);
585 
586         }
587 	else { // not live.
588 	    if (newAppearance == null) {
589 		appearance = null;
590 	    } else {
591 		appearance = (AppearanceRetained) newAppearance.retained;
592 	    }
593 	}
594     }
595 
596     /**
597      * Retrieves the shape node's appearance component.
598      * @return the shape node's appearance
599      */
getAppearance()600     Appearance getAppearance() {
601         return (appearance == null ? null: (Appearance) appearance.source);
602     }
603 
setAppearanceOverrideEnable(boolean flag)604     void setAppearanceOverrideEnable(boolean flag) {
605 	if (((Shape3D)this.source).isLive()) {
606 
607 	    // Send a message
608 	    J3dMessage createMessage = new J3dMessage();
609 	    createMessage.threads = targetThreads;
610 	    createMessage.type = J3dMessage.SHAPE3D_CHANGED;
611 	    createMessage.universe = universe;
612 	    createMessage.args[0] = this;
613 	    createMessage.args[1]= new Integer(APPEARANCEOVERRIDE_CHANGED);
614 	    Shape3DRetained[] s3dArr = new Shape3DRetained[mirrorShape3D.size()];
615 	    mirrorShape3D.toArray(s3dArr);
616 	    createMessage.args[2] = s3dArr;
617 	    Object[] obj = new Object[2];
618 	    if (flag) {
619 		obj[0] = Boolean.TRUE;
620 	    }
621 	    else {
622 		obj[0]  = Boolean.FALSE;
623 	    }
624 	    obj[1] = new Integer(changedFrequent);
625 	    createMessage.args[3] = obj;
626 	    createMessage.args[4] = getGeomAtomsArray(mirrorShape3D);
627 
628 	    VirtualUniverse.mc.processMessage(createMessage);
629 	}
630 	appearanceOverrideEnable = flag;
631     }
632 
getAppearanceOverrideEnable()633     boolean getAppearanceOverrideEnable() {
634 	return appearanceOverrideEnable;
635     }
636 
intersect(PickInfo pickInfo, PickShape pickShape, int flags )637     boolean intersect(PickInfo pickInfo, PickShape pickShape, int flags ) {
638 
639         Transform3D localToVworld = pickInfo.getLocalToVWorldRef();
640 
641         // Support OrientedShape3D here.
642 	// Note - BugId : 4363899 - APIs issue : OrientedShape3D's intersect needs view
643 	//                          info. temp. fix use the primary view.
644 	if (this instanceof OrientedShape3DRetained) {
645 	    Transform3D orientedTransform = ((OrientedShape3DRetained)this).
646 		getOrientedTransform(getPrimaryViewIdx());
647 	    localToVworld.mul(orientedTransform);
648 	}
649 
650  	Transform3D t3d = new Transform3D();
651 	t3d.invert(localToVworld);
652 	PickShape newPS = pickShape.transform(t3d);
653 
654 	// Note: For optimization - Should do a geobounds check of
655 	// each geometry first. But this doesn't work for
656 	// OrientedShape3D case...
657 	int geomListSize = geometryList.size();
658 	GeometryRetained geometry;
659 
660         if (((flags & PickInfo.CLOSEST_INTERSECTION_POINT) == 0) &&
661                 ((flags & PickInfo.CLOSEST_DISTANCE) == 0) &&
662                 ((flags & PickInfo.CLOSEST_GEOM_INFO) == 0) &&
663                 ((flags & PickInfo.ALL_GEOM_INFO) == 0)) {
664 
665             for (int i=0; i < geomListSize; i++) {
666                 geometry =  (GeometryRetained) geometryList.get(i);
667                 if (geometry != null) {
668                     if (geometry.mirrorGeometry != null) {
669                         geometry = geometry.mirrorGeometry;
670                     }
671                     if (geometry.intersect(newPS, null, 0, null, null, 0)) {
672                         return true;
673                     }
674                 }
675             }
676         } else {
677             double distance;
678             double minDist = Double.POSITIVE_INFINITY;
679             Point3d closestIPnt = new Point3d();
680             Point3d iPnt = new Point3d();
681             Point3d iPntVW = new Point3d();
682 
683             for (int i=0; i < geomListSize; i++) {
684                 geometry =  (GeometryRetained) geometryList.get(i);
685                 if (geometry != null) {
686                     if (geometry.mirrorGeometry != null) {
687                         geometry = geometry.mirrorGeometry;
688                     }
689                     //if (geometry.intersect(newPS, intersectionInfo, flags, iPnt)) {
690                     if(geometry.intersect(newPS, pickInfo, flags, iPnt, geometry, i)) {
691 
692                         iPntVW.set(iPnt);
693                         localToVworld.transform(iPntVW);
694                         distance = pickShape.distance(iPntVW);
695 
696                         if (minDist > distance) {
697                             minDist = distance;
698                             closestIPnt.set(iPnt);
699                         }
700                     }
701                 }
702             }
703 
704             if (minDist < Double.POSITIVE_INFINITY) {
705                 if ((flags & PickInfo.CLOSEST_DISTANCE) != 0) {
706                     pickInfo.setClosestDistance(minDist);
707                 }
708                 if((flags & PickInfo.CLOSEST_INTERSECTION_POINT) != 0) {
709                     pickInfo.setClosestIntersectionPoint(closestIPnt);
710                 }
711                 return true;
712             }
713         }
714 
715 	return false;
716 
717     }
718 
719 
720     /**
721      * Check if the geometry component of this shape node under path
722      * intersects with the pickShape.
723      * This is an expensive method. It should only be called if and only
724      * if the path's bound intersects pickShape.
725      * @exception IllegalArgumentException if <code>path</code> is
726      * invalid.
727      */
728 
intersect(SceneGraphPath path, PickShape pickShape, double[] dist)729     boolean intersect(SceneGraphPath path,
730             PickShape pickShape, double[] dist) {
731 
732         int flags;
733         PickInfo pickInfo = new PickInfo();
734 
735         Transform3D localToVworld = path.getTransform();
736         if (localToVworld == null) {
737 	    throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3"));
738 	}
739         pickInfo.setLocalToVWorldRef( localToVworld);
740         //System.err.println("Shape3DRetained.intersect() : ");
741         if (dist == null) {
742             //System.err.println("      no dist request ....");
743             return intersect(pickInfo, pickShape, 0);
744         }
745 
746         flags = PickInfo.CLOSEST_DISTANCE;
747         if (intersect(pickInfo, pickShape, flags)) {
748             dist[0] = pickInfo.getClosestDistance();
749             return true;
750         }
751 
752         return false;
753 
754       }
755 
756     /**
757      * This sets the immedate mode context flag
758      */
setInImmCtx(boolean inCtx)759     void setInImmCtx(boolean inCtx) {
760         inImmCtx = inCtx;
761     }
762 
763     /**
764      * This gets the immedate mode context flag
765      */
getInImmCtx()766     boolean getInImmCtx() {
767         return (inImmCtx);
768     }
769 
770     /**
771      * This updates the mirror shape to reflect the state of the
772      * real shape3d.
773      */
initMirrorShape3D(SetLiveState s, Shape3DRetained ms, int index)774     private void initMirrorShape3D(SetLiveState s, Shape3DRetained ms, int index) {
775 
776 	// New 1.2.1 code
777 
778         ms.inBackgroundGroup = inBackgroundGroup;
779         ms.geometryBackground = geometryBackground;
780 	ms.source = source;
781 	ms.universe = universe;
782 	// Has to be false. We have a instance of mirror for every link to the shape3d.
783 	ms.inSharedGroup = false;
784 	ms.locale = locale;
785 	ms.parent = parent;
786 
787 	// New 1.3.2
788 	// Used when user supplied their own bounds for transparency sorting
789 	// GeometryAtom uses this to change how it computes the centroid
790 	ms.boundsAutoCompute = boundsAutoCompute;
791 	ms.localBounds = localBounds;
792 	// End new 1.3.2
793 
794         OrderedPath op = (OrderedPath)s.orderedPaths.get(index);
795         if (op.pathElements.size() == 0) {
796             ms.orderedPath = null;
797         } else {
798             ms.orderedPath = op;
799 /*
800             System.err.println("initMirrorShape3D ms.orderedPath ");
801             ms.orderedPath.printPath();
802 */
803         }
804 
805 	// all mirror shapes point to the same transformGroupRetained
806 	// for the static transform
807 	ms.staticTransform = staticTransform;
808 
809 
810 	ms.appearanceOverrideEnable = appearanceOverrideEnable;
811 
812 	ms.geometryList = geometryList;
813 
814 	// Assign the parent of this mirror shape node
815 	ms.sourceNode = this;
816 
817         if (this instanceof OrientedShape3DRetained) {
818             OrientedShape3DRetained os = (OrientedShape3DRetained)this;
819             OrientedShape3DRetained oms = (OrientedShape3DRetained)ms;
820             oms.initAlignmentMode(os.mode);
821             oms.initAlignmentAxis(os.axis);
822             oms.initRotationPoint(os.rotationPoint);
823 	    oms.initConstantScaleEnable(os.constantScale);
824 	    oms.initScale(os.scaleFactor);
825         }
826 
827     }
828 
updateImmediateMirrorObject(Object[] objs)829     void updateImmediateMirrorObject(Object[] objs) {
830 	int component = ((Integer)objs[1]).intValue();
831 	GeometryArrayRetained ga;
832 
833 	Shape3DRetained[] msArr = (Shape3DRetained[]) objs[2];
834 	int i, j;
835 	if ((component & APPEARANCE_CHANGED) != 0) {
836 	    Object[] arg = (Object[])objs[3];
837 	    int val = ((Integer)arg[1]).intValue();
838 	    for ( i = msArr.length-1; i >=0; i--) {
839 		msArr[i].appearance = (AppearanceRetained)arg[0];
840 		msArr[i].changedFrequent = val;
841 	    }
842 	}
843 	if ((component & APPEARANCEOVERRIDE_CHANGED) != 0) {
844 	    Object[] arg = (Object[])objs[3];
845 	    int val = ((Integer)arg[1]).intValue();
846 	    for ( i = msArr.length-1; i >=0; i--) {
847 		msArr[i].appearanceOverrideEnable = ((Boolean)arg[0]).booleanValue();
848 		msArr[i].changedFrequent = val;
849 	    }
850 	}
851     }
852 
853     /**
854      * Gets the bounding object of a node.
855      * @return the node's bounding object
856      */
857 
getBounds()858     Bounds getBounds() {
859 
860         if(boundsAutoCompute) {
861 	    // System.err.println("getBounds ---- localBounds is " + localBounds);
862             // Issue 514 : NPE in Wonderland : triggered in cached bounds computation
863 	    if (validCachedBounds) {
864                 return (Bounds) cachedBounds.clone();
865             }
866 
867 	    if(geometryList != null) {
868 		BoundingBox bbox = new BoundingBox((Bounds) null);
869 		GeometryRetained geometry;
870 		for(int i=0; i<geometryList.size(); i++) {
871 		    geometry = (GeometryRetained) geometryList.get(i);
872 		    if ((geometry != null) &&
873 			(geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) {
874 			geometry.computeBoundingBox();
875 			synchronized(geometry.geoBounds) {
876 			    bbox.combine(geometry.geoBounds);
877 			}
878 		    }
879 		}
880 		return (Bounds) bbox;
881 
882 	    } else {
883 		return null;
884             }
885 
886         } else {
887             return super.getBounds();
888         }
889     }
890 
getEffectiveBounds()891     Bounds getEffectiveBounds() {
892         if(boundsAutoCompute) {
893 	    return getBounds();
894 	}
895 	else {
896 	    return super.getEffectiveBounds();
897 	}
898     }
899 
900 
901     /**
902      * ONLY needed for SHAPE, MORPH, and LINK node type.
903      * Compute the combine bounds of bounds and its localBounds.
904      */
computeCombineBounds(Bounds bounds)905     void computeCombineBounds(Bounds bounds) {
906 
907         if(boundsAutoCompute) {
908             if(geometryList != null) {
909                 GeometryRetained geometry;
910                 BoundingBox bbox = null;
911 
912                 if (staticTransform != null) {
913                     bbox = new BoundingBox((BoundingBox) null);
914                 }
915 
916                 if (!VirtualUniverse.mc.cacheAutoComputedBounds) {
917                     for(int i=0; i<geometryList.size(); i++) {
918                         geometry = (GeometryRetained) geometryList.get(i);
919                         if ((geometry != null) &&
920                                 (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) {
921                             geometry.computeBoundingBox();
922                             // Should this be lock too ? ( MT safe  ? )
923                             synchronized(geometry.geoBounds) {
924                                 if (staticTransform != null) {
925                                     bbox.set(geometry.geoBounds);
926                                     bbox.transform(staticTransform.transform);
927                                     bounds.combine((Bounds)bbox);
928                                 } else {
929                                     bounds.combine((Bounds)geometry.geoBounds);
930                                 }
931                             }
932                         }
933                     }
934                 } else {
935                     // Issue 514 : NPE in Wonderland : triggered in cached bounds computation
936                     if (!validCachedBounds) {
937                         validCachedBounds = true;
938                         cachedBounds = new BoundingBox((BoundingBox) null);
939 
940                         for(int i=0; i<geometryList.size(); i++) {
941                             geometry = (GeometryRetained) geometryList.get(i);
942                             if ((geometry != null) &&
943                                     (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) {
944                                 geometry.computeBoundingBox();
945                                 // Should this be lock too ? ( MT safe  ? )
946                                 synchronized(geometry.geoBounds) {
947                                     if (staticTransform != null) {
948                                         bbox.set(geometry.geoBounds);
949                                         bbox.transform(staticTransform.transform);
950                                         cachedBounds.combine((Bounds)bbox);
951                                     } else {
952                                         cachedBounds.combine((Bounds)geometry.geoBounds);
953                                     }
954                                 }
955                             }
956                         }
957                     }
958                     bounds.combine(cachedBounds);
959                 }
960             }
961         } else {
962 
963             // Should this be lock too ? ( MT safe  ? )
964             synchronized(localBounds) {
965                 bounds.combine((Bounds) localBounds);
966             }
967         }
968     }
969 
970   /**
971    * assign a name to this node when it is made live.
972    */
973 
setLive(SetLiveState s)974     void setLive(SetLiveState s) {
975 	doSetLive(s);
976 	markAsLive();
977     }
978 
doSetLive(SetLiveState s)979     void doSetLive(SetLiveState s) {
980 	// System.err.println("S3DRetained : setLive " + s);
981 	Shape3DRetained shape;
982 	GeometryRetained geometry;
983 	int i, j, k, gaCnt;
984 	ArrayList msList = new ArrayList();
985 
986 	super.doSetLive(s);
987 
988 	nodeId = universe.getNodeId();
989 
990 
991 	if (inSharedGroup) {
992 	    for (i=0; i<s.keys.length; i++) {
993                 if (this instanceof OrientedShape3DRetained) {
994                     shape = new OrientedShape3DRetained();
995                 } else {
996                     shape = new Shape3DRetained();
997                 }
998 		shape.key = s.keys[i];
999 		shape.localToVworld = new Transform3D[1][];
1000 		shape.localToVworldIndex = new int[1][];
1001 
1002 		j = s.keys[i].equals(localToVworldKeys, 0,
1003 				     localToVworldKeys.length);
1004 		/*
1005 		    System.err.print("s.keys[i] = "+s.keys[i]+" j = "+j);
1006 		    if(j < 0) {
1007 		    System.err.println("Shape3dRetained : Can't find hashKey");
1008 		    }
1009 		*/
1010 		shape.localToVworld[0] = localToVworld[j];
1011 		shape.localToVworldIndex[0] = localToVworldIndex[j];
1012 		shape.branchGroupPath = (BranchGroupRetained []) branchGroupPaths.get(j);
1013 		shape.isPickable = s.pickable[i];
1014 		shape.isCollidable = s.collidable[i];
1015 
1016 		initMirrorShape3D(s, shape, j);
1017 
1018                 if (s.switchTargets != null &&
1019                         s.switchTargets[i] != null) {
1020 		    s.switchTargets[i].addNode(shape, Targets.GEO_TARGETS);
1021                     shape.closestSwitchParent = s.closestSwitchParents[i];
1022                     shape.closestSwitchIndex = s.closestSwitchIndices[i];
1023                 }
1024         	shape.switchState = (SwitchState)s.switchStates.get(j);
1025 
1026 
1027 		// Add any scoped lights to the mirror shape
1028 		if (s.lights != null) {
1029 		    ArrayList l = (ArrayList)s.lights.get(j);
1030 		    if (l != null) {
1031 			for (int m = 0; m < l.size(); m++) {
1032 			    shape.addLight((LightRetained)l.get(m));
1033 			}
1034 		    }
1035 		}
1036 
1037 		// Add any scoped fog
1038 		if (s.fogs != null) {
1039 		    ArrayList l = (ArrayList)s.fogs.get(j);
1040 		    if (l != null) {
1041 			for (int m = 0; m < l.size(); m++) {
1042 			    shape.addFog((FogRetained)l.get(m));
1043 			}
1044 		    }
1045 		}
1046 
1047 		// Add any scoped modelClip
1048 		if (s.modelClips != null) {
1049 		    ArrayList l = (ArrayList)s.modelClips.get(j);
1050 		    if (l != null) {
1051 			for (int m = 0; m < l.size(); m++) {
1052 			    shape.addModelClip((ModelClipRetained)l.get(m));
1053 			}
1054 		    }
1055 		}
1056 
1057 		// Add any scoped alt app
1058 		if (s.altAppearances != null) {
1059 		    ArrayList l = (ArrayList)s.altAppearances.get(j);
1060 		    if (l != null) {
1061 			for (int m = 0; m < l.size(); m++) {
1062 			    shape.addAltApp((AlternateAppearanceRetained)l.get(m));
1063 			}
1064 		    }
1065 		}
1066 		synchronized(mirrorShape3D) {
1067 		    mirrorShape3D.add(j,shape);
1068 		}
1069 
1070 		msList.add(shape);
1071 		if (s.viewLists != null) {
1072 		    shape.viewList = (ArrayList)s.viewLists.get(j);
1073 		} else {
1074 		    shape.viewList = null;
1075 		}
1076 	    }
1077 	} else {
1078             if (this instanceof OrientedShape3DRetained) {
1079                 shape = new OrientedShape3DRetained();
1080             } else {
1081                 shape = new Shape3DRetained();
1082             }
1083 
1084 	    shape.localToVworld = new Transform3D[1][];
1085 	    shape.localToVworldIndex = new int[1][];
1086 	    shape.localToVworld[0] = localToVworld[0];
1087 	    shape.localToVworldIndex[0] = localToVworldIndex[0];
1088 	    shape.branchGroupPath = (BranchGroupRetained []) branchGroupPaths.get(0);
1089 	    shape.isPickable = s.pickable[0];
1090 	    shape.isCollidable = s.collidable[0];
1091 	    initMirrorShape3D(s, shape, 0);
1092 
1093 	    // Add any scoped lights to the mirror shape
1094 	    if (s.lights != null) {
1095 		ArrayList l = (ArrayList)s.lights.get(0);
1096 		for (i = 0; i < l.size(); i++) {
1097 		    shape.addLight((LightRetained)l.get(i));
1098 		}
1099 	    }
1100 
1101 	    // Add any scoped fog
1102 	    if (s.fogs != null) {
1103 		ArrayList l = (ArrayList)s.fogs.get(0);
1104 		for (i = 0; i < l.size(); i++) {
1105 		    shape.addFog((FogRetained)l.get(i));
1106 		}
1107 	    }
1108 
1109 	    // Add any scoped modelClip
1110 	    if (s.modelClips != null) {
1111 		ArrayList l = (ArrayList)s.modelClips.get(0);
1112 		for (i = 0; i < l.size(); i++) {
1113 		    shape.addModelClip((ModelClipRetained)l.get(i));
1114 		}
1115 
1116 	    }
1117 
1118 	    // Add any scoped alt app
1119 	    if (s.altAppearances != null) {
1120 		ArrayList l = (ArrayList)s.altAppearances.get(0);
1121 		for (i = 0; i < l.size(); i++) {
1122 		    shape.addAltApp((AlternateAppearanceRetained)l.get(i));
1123 		}
1124 	    }
1125 	    synchronized(mirrorShape3D) {
1126 		mirrorShape3D.add(shape);
1127 	    }
1128 
1129 	    msList.add(shape);
1130 	    if (s.viewLists != null)
1131 		shape.viewList = (ArrayList)s.viewLists.get(0);
1132 	    else
1133 		shape.viewList = null;
1134 
1135             if (s.switchTargets != null &&
1136                         s.switchTargets[0] != null) {
1137 		s.switchTargets[0].addNode(shape, Targets.GEO_TARGETS);
1138                 shape.closestSwitchParent = s.closestSwitchParents[0];
1139                 shape.closestSwitchIndex = s.closestSwitchIndices[0];
1140             }
1141             shape.switchState = (SwitchState)s.switchStates.get(0);
1142 	}
1143 
1144 	for (k = 0; k < msList.size(); k++) {
1145 	    Shape3DRetained sh = (Shape3DRetained)msList.get(k);
1146 
1147 	    if (appearance != null) {
1148 		synchronized(appearance.liveStateLock) {
1149 		    if (k == 0) { // Do only first time
1150 			appearance.setLive(inBackgroundGroup, s.refCount);
1151 			appearance.initMirrorObject();
1152 			if (appearance.renderingAttributes != null)
1153 			    visible = appearance.renderingAttributes.visible;
1154 		    }
1155 		    sh.appearance = (AppearanceRetained)appearance.mirror;
1156 		    appearance.addAMirrorUser(sh);
1157 
1158 		}
1159 	    }
1160 	    else {
1161 		sh.appearance = null;
1162 	    }
1163 
1164 	    if (geometryList != null) {
1165 		for(gaCnt=0; gaCnt<geometryList.size(); gaCnt++) {
1166 		    geometry = (GeometryRetained) geometryList.get(gaCnt);
1167 		    if(geometry != null) {
1168 			synchronized(geometry.liveStateLock) {
1169 			    if (k == 0) { // Do only first time
1170 				geometry.setLive(inBackgroundGroup, s.refCount);
1171 			    }
1172 			    geometry.addUser(sh);
1173 			}
1174 		    }
1175 		}
1176 
1177 	    }
1178 
1179 	    // after the geometry has been setLived and bounds computed
1180 	    if (k== 0 && boundsAutoCompute) { // Do only once
1181 		// user may call setBounds with a bounds other than boundingBox
1182 		if (! (localBounds instanceof BoundingBox)) {
1183 		    localBounds = new BoundingBox((BoundingBox) null);
1184 		}
1185 		getCombineBounds((BoundingBox)localBounds);
1186 
1187 	    }
1188 	    // Assign GAtom and set the bounds if we are not using switch
1189 	    initializeGAtom(sh);
1190 
1191             GeometryAtom ga = getGeomAtom(sh);
1192 
1193 	    // Add the geometry atom for this shape to the nodeList
1194 	    s.nodeList.add(ga);
1195 
1196             if (s.transformTargets != null &&
1197             		s.transformTargets[k] != null) {
1198 		// Add the geometry atom for this shape to the transformTargets
1199 
1200 		s.transformTargets[k].addNode(ga, Targets.GEO_TARGETS);
1201 	    }
1202 	}
1203 
1204 	s.notifyThreads |= (J3dThread.UPDATE_GEOMETRY |
1205 			    J3dThread.UPDATE_TRANSFORM |
1206 			    J3dThread.UPDATE_RENDER |
1207 			    J3dThread.UPDATE_RENDERING_ENVIRONMENT);
1208 
1209     }
1210 
1211     /**
1212      * This clears all references in a mirror shape
1213      */
1214     // This is call in RenderingEnvironmentStructure.removeNode() because that is the
1215     // last point that will reference this ms.
1216     // called on the mirror shape ..
clearMirrorShape()1217     void clearMirrorShape() {
1218 	int i;
1219 
1220 	source = null;
1221 	sourceNode = null;
1222 	parent = null;
1223 
1224 	if (otherAppearance != null) {
1225 	    otherAppearance.sgApp.removeAMirrorUser(this);
1226 	    otherAppearance = null;
1227         }
1228 
1229 	appearance = null;
1230 
1231 	branchGroupPath = null;
1232 	isPickable = true;
1233 	isCollidable = true;
1234 	branchGroupPath = null;
1235 	// No locking needed. Owner, s3dR, has already been destory.
1236 	// DO NOT clear geometryList, ie. geometryList.clear().
1237 	// It is referred by the source s3DRetained.
1238 	 geometryList = null;
1239 
1240 	// Clear the mirror scoping info
1241 	// Remove all the fogs
1242 	for (i = 0; i <  numfogs; i++)
1243 	     fogs[i] = null;
1244 	 numfogs = 0;
1245 
1246 	// Remove all the modelClips
1247 	for (i = 0; i <  numModelClips; i++)
1248 	     modelClips[i] = null;
1249 	 numModelClips = 0;
1250 
1251 	// Remove all the lights
1252 	for (i = 0; i < numlights; i++)
1253 	     lights[i] = null;
1254 	 numlights = 0;
1255 
1256 	// Remove all the al app
1257 	for (i = 0; i <  numAltApps; i++)
1258 	     altApps[i] = null;
1259 	numAltApps = 0;
1260 
1261 	viewList = null;
1262 
1263     }
1264 
1265     /**
1266      * assign a name to this node when it is made live.
1267      */
clearLive(SetLiveState s)1268     void clearLive(SetLiveState s) {
1269 
1270 	//System.err.println("S3DRetained : clearLive " + s);
1271 
1272 	int i, j, gaCnt;
1273 	Shape3DRetained shape;
1274 	GeometryRetained geometry;
1275 	Object[] shapes;
1276 	ArrayList msList = new ArrayList();
1277 
1278 	super.clearLive(s);
1279 
1280 
1281 
1282 	if (inSharedGroup) {
1283 	    synchronized(mirrorShape3D) {
1284 		shapes = mirrorShape3D.toArray();
1285 		for (i=0; i<s.keys.length; i++) {
1286 		    for (j=0; j<shapes.length; j++) {
1287 			shape = (Shape3DRetained)shapes[j];
1288 			if (shape.key.equals(s.keys[i])) {
1289 			    mirrorShape3D.remove(mirrorShape3D.indexOf(shape));
1290             		    if (s.switchTargets != null &&
1291                         		s.switchTargets[i] != null) {
1292                 		s.switchTargets[i].addNode(
1293 						shape, Targets.GEO_TARGETS);
1294 			    }
1295 			    msList.add(shape);
1296                             GeometryAtom ga = getGeomAtom(shape);
1297 
1298 			    // Add the geometry atom for this shape to the nodeList
1299 			    s.nodeList.add(ga);
1300             		    if (s.transformTargets != null &&
1301                         		s.transformTargets[i] != null) {
1302                                 s.transformTargets[i].addNode(ga, Targets.GEO_TARGETS);
1303                             }
1304 			}
1305 		    }
1306 		}
1307 	    }
1308 	} else {
1309 	    // Only entry 0 is valid
1310 	    shape = (Shape3DRetained)mirrorShape3D.get(0);
1311 	    synchronized(mirrorShape3D) {
1312 		mirrorShape3D.remove(0);
1313 	    }
1314 
1315             if (s.switchTargets != null &&
1316                         s.switchTargets[0] != null) {
1317                 s.switchTargets[0].addNode(shape, Targets.GEO_TARGETS);
1318             }
1319 
1320 
1321 	    msList.add(shape);
1322 
1323             GeometryAtom ga = getGeomAtom(shape);
1324 
1325 	    // Add the geometry atom for this shape to the nodeList
1326 	    s.nodeList.add(ga);
1327             if (s.transformTargets != null &&
1328 		s.transformTargets[0] != null) {
1329                 s.transformTargets[0].addNode(ga, Targets.GEO_TARGETS);
1330             }
1331 	}
1332 
1333 
1334 	for (int k = 0; k < msList.size(); k++) {
1335 	    Shape3DRetained sh = (Shape3DRetained)msList.get(k);
1336 	    if (appearance != null) {
1337 		synchronized(appearance.liveStateLock) {
1338 		    if (k == 0) {
1339 			appearance.clearLive(s.refCount);
1340 		    }
1341 		    appearance.removeAMirrorUser(sh);
1342 		}
1343 	    }
1344 	    if (geometryList != null) {
1345 		for(gaCnt=0; gaCnt<geometryList.size(); gaCnt++) {
1346 		    geometry = (GeometryRetained) geometryList.get(gaCnt);
1347 		    if(geometry != null) {
1348 			synchronized(geometry.liveStateLock) {
1349 			    if (k == 0) {
1350 				geometry.clearLive(s.refCount);
1351 			    }
1352 			    geometry.removeUser(sh);
1353 			}
1354 		    }
1355 		}
1356 	    }
1357 	}
1358 
1359 	s.notifyThreads |= (J3dThread.UPDATE_GEOMETRY |
1360 			    J3dThread.UPDATE_TRANSFORM |
1361 			    // This is used to clear the scope info
1362 			    // of all the mirror shapes
1363 			    J3dThread.UPDATE_RENDERING_ENVIRONMENT |
1364 			    J3dThread.UPDATE_RENDER);
1365 
1366 	if (!source.isLive()) {
1367 	    // Clear the mirror scoping info
1368 	    // Remove all the fogs
1369 	    for (i = 0; i < numfogs; i++)
1370 		fogs[i] = null;
1371 	    numfogs = 0;
1372 
1373 	    // Remove all the modelClips
1374 	    for (i = 0; i < numModelClips; i++)
1375 		modelClips[i] = null;
1376 	    numModelClips = 0;
1377 
1378 	    // Remove all the lights
1379 	    for (i = 0; i < numlights; i++)
1380 		lights[i] = null;
1381 	    numlights = 0;
1382 
1383 	    // Remove all the al app
1384 	    for (i = 0; i < numAltApps; i++)
1385 		altApps[i] = null;
1386 	    numAltApps = 0;
1387 	}
1388     }
1389 
isStatic()1390     boolean isStatic() {
1391 	if (source.getCapability(Shape3D.ALLOW_APPEARANCE_WRITE) ||
1392 	    source.getCapability(Shape3D.ALLOW_GEOMETRY_WRITE) ||
1393 	    source.getCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE)) {
1394 	    return false;
1395 	} else {
1396 	    return true;
1397 	}
1398     }
1399 
staticXformCanBeApplied()1400     boolean staticXformCanBeApplied() {
1401 
1402 	// static xform can be applied if
1403 	// . shape is not pickable or collidable
1404 	// . geometry is not being shared by more than one shape nodes
1405 	// . geometry will be put in display list
1406 	// . geometry is not readable
1407 
1408         // no static xform if shape is pickable or collidable because
1409 	// otherwise the static xform will have to be applied to the
1410 	// currentLocalToVworld in the intersect test, it will then
1411 	// be more costly and really beat the purpose of eliminating
1412 	// the static transform group
1413         if (isPickable || isCollidable ||
1414 	    	source.getCapability(Shape3D.ALLOW_PICKABLE_WRITE) ||
1415 	    	source.getCapability(Shape3D.ALLOW_COLLIDABLE_WRITE)) {
1416 	    return false;
1417 	}
1418 
1419 	if (appearance != null &&
1420 	    (appearance.transparencyAttributes != null && appearance.transparencyAttributes.transparencyMode != TransparencyAttributes.NONE))
1421 	    return false;
1422 
1423 	GeometryRetained geo;
1424 	boolean alphaEditable;
1425 
1426 	for (int i=0; i<geometryList.size(); i++) {
1427 	    geo = (GeometryRetained) geometryList.get(i);
1428 	    if (geo != null) {
1429 		if (geo.refCnt > 1) {
1430 		    return false;
1431 		}
1432 		alphaEditable = isAlphaEditable(geo);
1433 		if (geo instanceof GeometryArrayRetained) {
1434 		    geo.isEditable = !((GeometryArrayRetained)geo).isWriteStatic();
1435 
1436 		    // TODO: for now if vertex data can be returned, then
1437 		    // don't apply static transform
1438 		    if (geo.source.getCapability(
1439 				GeometryArray.ALLOW_COORDINATE_READ) ||
1440 			geo.source.getCapability(
1441 				GeometryArray.ALLOW_NORMAL_READ))
1442 			return false;
1443 
1444 		}
1445 
1446 		if (!geo.canBeInDisplayList(alphaEditable)) {
1447 		    return false;
1448 		}
1449 	    }
1450  	}
1451 	return true;
1452     }
1453 
1454 
compile(CompileState compState)1455     void compile(CompileState compState) {
1456 	AppearanceRetained newApp;
1457 
1458 	super.compile(compState);
1459 
1460 	if (isStatic() && staticXformCanBeApplied()) {
1461 	    mergeFlag = SceneGraphObjectRetained.MERGE;
1462             if (J3dDebug.devPhase && J3dDebug.debug) {
1463 	        compState.numShapesWStaticTG++;
1464 	    }
1465 	} else
1466 	{
1467 	    mergeFlag = SceneGraphObjectRetained.DONT_MERGE;
1468 	    compState.keepTG = true;
1469 	}
1470 
1471         if (J3dDebug.devPhase && J3dDebug.debug) {
1472 	    compState.numShapes++;
1473 	}
1474 
1475 	if (appearance != null) {
1476 	    appearance.compile(compState);
1477 	    // Non-static apperanace can still be compiled, since in compile
1478 	    // state we will be grouping all shapes that have same appearance
1479 	    // so, when the appearance changes, all the shapes will be affected
1480 	    // For non-static appearances, we don't get an equivalent appearance
1481 	    // from the compile state
1482 	    if (appearance.isStatic()) {
1483 		newApp = compState.getAppearance(appearance);
1484 		appearance = newApp;
1485 	    }
1486 	}
1487 
1488 	for (int i = 0; i < geometryList.size(); i++) {
1489 	    GeometryRetained geo = (GeometryRetained)geometryList.get(i);
1490 	    if (geo != null)
1491 		geo.compile(compState);
1492 	}
1493 
1494     }
1495 
merge(CompileState compState)1496     void merge(CompileState compState) {
1497 
1498 
1499 	if (mergeFlag == SceneGraphObjectRetained.DONT_MERGE) {
1500 
1501 	    // no need to save the staticTransform here
1502 
1503 	    TransformGroupRetained saveStaticTransform =
1504 					compState.staticTransform;
1505 	    compState.staticTransform = null;
1506 	    super.merge(compState);
1507 	    compState.staticTransform = saveStaticTransform;
1508         } else {
1509 	    super.merge(compState);
1510 	}
1511 
1512 	if (shapeIsMergeable(compState)) {
1513 	    compState.addShape(this);
1514 	}
1515     }
1516 
1517 
shapeIsMergeable(CompileState compState)1518     boolean shapeIsMergeable(CompileState  compState) {
1519 	boolean mergeable = true;
1520 	AppearanceRetained newApp;
1521 	int i;
1522 
1523 	GeometryRetained geometry = null;
1524 	int index = 0;
1525 	i = 0;
1526 	/*
1527 	if (isPickable)
1528 	    return false;
1529 	*/
1530 
1531 	// For now, don't merge if the shape has static transform
1532 	if (staticTransform != null)
1533 	    return false;
1534 
1535 	// If this shape's to be immediate parent is orderedGroup or a switchNode
1536 	// this shape is not mergerable
1537 	if (parent instanceof OrderedGroupRetained ||
1538 	    parent instanceof SwitchRetained)
1539 	    return false;
1540 
1541 	// Get the first geometry that is non-null
1542 	while (geometry == null && index < geometryList.size()) {
1543 	    geometry = (GeometryRetained) geometryList.get(index);
1544 	    index++;
1545 	}
1546 
1547 	if (!(geometry  instanceof GeometryArrayRetained)) {
1548 	    return false;
1549 	}
1550 
1551 	GeometryArrayRetained firstGeo = (GeometryArrayRetained) geometry;
1552 
1553 	for(i=index; (i<geometryList.size() && mergeable); i++) {
1554 	    geometry = (GeometryRetained) geometryList.get(i);
1555 	    if (geometry != null) {
1556 		GeometryArrayRetained geo = (GeometryArrayRetained)geometry;
1557 
1558 		if (! geo.isWriteStatic())
1559 		    mergeable = false;
1560 
1561 		if (geo.vertexFormat != firstGeo.vertexFormat)
1562 		    mergeable = false;
1563 
1564 
1565 	    }
1566 	}
1567 
1568 	// For now, turn off lots of capability bits
1569 	if (source.getCapability(Shape3D.ALLOW_COLLISION_BOUNDS_WRITE) ||
1570 	    source.getCapability(Shape3D.ALLOW_APPEARANCE_WRITE) ||
1571 	    source.getCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) ||
1572 	    source.getCapability(Shape3D.ALLOW_AUTO_COMPUTE_BOUNDS_WRITE) ||
1573 	    source.getCapability(Shape3D.ALLOW_BOUNDS_WRITE) ||
1574 	    source.getCapability(Shape3D.ALLOW_COLLIDABLE_WRITE) ||
1575 	    source.getCapability(Shape3D.ALLOW_PICKABLE_WRITE) ||
1576 	    source.getCapability(Shape3D.ALLOW_GEOMETRY_WRITE)) {
1577 	    mergeable = false;
1578 	}
1579 
1580 	return mergeable;
1581 
1582     }
1583 
1584 
getMirrorObjects( ArrayList list, HashKey k)1585     void getMirrorObjects( ArrayList list, HashKey k) {
1586 	Shape3DRetained ms;
1587 	if (inSharedGroup) {
1588 	    if (k.count == 0) {
1589 		//		System.err.println("===> CAN NEVER BE TRUE");
1590 		return;
1591 	    }
1592 	    else {
1593 		ms = getMirrorShape(k);
1594 	    }
1595 	}
1596 	else {
1597 	    ms = (Shape3DRetained)mirrorShape3D.get(0);
1598 	}
1599 
1600 	list.add(getGeomAtom(ms));
1601 
1602     }
1603 
1604 
1605     // Called on the mirror Object
addLight(LightRetained light)1606     void addLight(LightRetained light) {
1607 	 LightRetained[] newlights;
1608 	 int i, n;
1609 	 Shape3DRetained ms;
1610 
1611 	 if (lights == null) {
1612 	     lights = new LightRetained[10];
1613 	 }
1614 	 else if (lights.length == numlights) {
1615 	     newlights = new LightRetained[numlights*2];
1616 	     for (i=0; i<numlights; i++) {
1617 		 newlights[i] = lights[i];
1618 	     }
1619 	     lights = newlights;
1620 	 }
1621 	 lights[numlights] = light;
1622 	 numlights++;
1623     }
1624 
1625     // called on the mirror object
removeLight(LightRetained light)1626     void removeLight(LightRetained light) {
1627 	 int i;
1628 
1629 	 for (i=0; i<numlights; i++) {
1630 	     if (lights[i] == light) {
1631 		 lights[i] = null;
1632 		 break;
1633 	     }
1634 	 }
1635 
1636 	 // Shift everyone down one.
1637 	 for (i++; i<numlights; i++) {
1638 	     lights[i-1] = lights[i];
1639 	 }
1640 	 numlights--;
1641     }
1642 
1643     // Called on the mirror object
addFog(FogRetained fog)1644     void addFog(FogRetained fog) {
1645 	 FogRetained[] newfogs;
1646 	 int i;
1647 
1648 	 if (fogs == null) {
1649 	     fogs = new FogRetained[10];
1650 	 }
1651 	 else if (fogs.length == numfogs) {
1652 	     newfogs = new FogRetained[numfogs*2];
1653 	     for (i=0; i<numfogs; i++) {
1654 		 newfogs[i] = fogs[i];
1655 	     }
1656 	     fogs = newfogs;
1657 	 }
1658 	 fogs[numfogs] = fog;
1659 	 numfogs++;
1660     }
1661 
1662     // called on the mirror object
removeFog(FogRetained fog)1663     void removeFog(FogRetained fog) {
1664 		 int i;
1665 
1666 	 for (i=0; i<numfogs; i++) {
1667 	     if (fogs[i] == fog) {
1668 		 fogs[i] = null;
1669 		 break;
1670 	     }
1671 	 }
1672 
1673 	 // Shift everyone down one.
1674 	 for (i++; i<numfogs; i++) {
1675 	     fogs[i-1] = fogs[i];
1676 	 }
1677 	 numfogs--;
1678 
1679     }
1680 
1681     // Called on the mirror object
addModelClip(ModelClipRetained modelClip)1682     void addModelClip(ModelClipRetained modelClip) {
1683 	 ModelClipRetained[] newModelClips;
1684 	 int i;
1685 
1686 
1687 	 if (modelClips == null) {
1688 	     modelClips = new ModelClipRetained[10];
1689 	 }
1690 	 else if (modelClips.length == numModelClips) {
1691 	     newModelClips = new ModelClipRetained[numModelClips*2];
1692 	     for (i=0; i<numModelClips; i++) {
1693 		 newModelClips[i] = modelClips[i];
1694 	     }
1695 	     modelClips = newModelClips;
1696 	 }
1697 	 modelClips[numModelClips] = modelClip;
1698 	 numModelClips++;
1699     }
1700 
1701     // called on the mirror object
removeModelClip(ModelClipRetained modelClip)1702     void removeModelClip(ModelClipRetained modelClip) {
1703 		 int i;
1704 
1705 	 for (i=0; i<numModelClips; i++) {
1706 	     if (modelClips[i] == modelClip) {
1707 		 modelClips[i] = null;
1708 		 break;
1709 	     }
1710 	 }
1711 
1712 	 // Shift everyone down one.
1713 	 for (i++; i<numModelClips; i++) {
1714 	     modelClips[i-1] = modelClips[i];
1715 	 }
1716 	 numModelClips--;
1717 
1718     }
1719 
1720     // Called on the mirror object
addAltApp(AlternateAppearanceRetained aApp)1721     void addAltApp(AlternateAppearanceRetained aApp) {
1722 	 AlternateAppearanceRetained[] newAltApps;
1723 	 int i;
1724 	 if (altApps == null) {
1725 	     altApps = new AlternateAppearanceRetained[10];
1726 	 }
1727 	 else if (altApps.length == numAltApps) {
1728 	     newAltApps = new AlternateAppearanceRetained[numAltApps*2];
1729 	     for (i=0; i<numAltApps; i++) {
1730 		 newAltApps[i] = altApps[i];
1731 	     }
1732 	     altApps = newAltApps;
1733 	 }
1734 	 altApps[numAltApps] = aApp;
1735 	 numAltApps++;
1736     }
1737 
1738     // called on the mirror object
removeAltApp(AlternateAppearanceRetained aApp)1739     void removeAltApp(AlternateAppearanceRetained aApp) {
1740 	int i;
1741 
1742 	 for (i=0; i<numAltApps; i++) {
1743 	     if (altApps[i] == aApp) {
1744 		 altApps[i] = null;
1745 		 break;
1746 	     }
1747 	 }
1748 
1749 	 // Shift everyone down one.
1750 	 for (i++; i<numAltApps; i++) {
1751 	     altApps[i-1] = altApps[i];
1752 	 }
1753 	 numAltApps--;
1754 
1755     }
1756 
1757 
1758 
updatePickable(HashKey keys[], boolean pick[])1759     void updatePickable(HashKey keys[], boolean pick[]) {
1760       super.updatePickable(keys, pick);
1761       Shape3DRetained shape;
1762 
1763       if (!inSharedGroup) {
1764 	  shape = (Shape3DRetained) mirrorShape3D.get(0);
1765 	  shape.isPickable = pick[0];
1766       } else {
1767 	  int size = mirrorShape3D.size();
1768 	  for (int j=0; j< keys.length; j++) {
1769 	      for (int i=0; i < size; i++) {
1770 		  shape = (Shape3DRetained) mirrorShape3D.get(i);
1771 		  if (keys[j].equals(shape.key)) {
1772 		      shape.isPickable = pick[j];
1773 		      break;
1774 		  }
1775 
1776 	      }
1777 	  }
1778       }
1779     }
1780 
1781 
updateCollidable(HashKey keys[], boolean collide[])1782     void updateCollidable(HashKey keys[], boolean collide[]) {
1783       super.updateCollidable(keys, collide);
1784       Shape3DRetained shape;
1785 
1786       if (!inSharedGroup) {
1787 	  shape = (Shape3DRetained) mirrorShape3D.get(0);
1788 	  shape.isCollidable = collide[0];
1789       } else {
1790 	  int size = mirrorShape3D.size();
1791 	  for (int j=0; j< keys.length; j++) {
1792 	      for (int i=0; i < size; i++) {
1793 		  shape = (Shape3DRetained) mirrorShape3D.get(i);
1794 		  if (keys[j].equals(shape.key)) {
1795 		      shape.isCollidable = collide[j];
1796 		      break;
1797 		  }
1798 
1799 	      }
1800 	  }
1801       }
1802     }
1803 
1804 
1805 
1806 
1807     // New 1.2.1 code ....
1808 
1809     // Remove the old geometry atoms and reInsert
1810     // the new geometry atoms and update the transform
1811     // target list
1812 
sendDataChangedMessage( GeometryRetained newGeom )1813     private void sendDataChangedMessage( GeometryRetained newGeom ) {
1814 
1815 	int i, j, gaCnt;
1816 	GeometryAtom[] newGAArray =  null;
1817 	GeometryAtom[] oldGAArray = null;
1818 	GeometryAtom[] newGeometryAtoms = null;
1819 	int geometryCnt = 0;
1820 	GeometryRetained geometry = null;
1821 
1822 	int s3dMSize = mirrorShape3D.size();
1823 
1824 	if(s3dMSize < 1)
1825 	    return;
1826 
1827 	Shape3DRetained mS3d = (Shape3DRetained) mirrorShape3D.get(0);
1828 
1829 	mS3d.mirrorShape3DLock.writeLock();
1830 
1831 	GeometryAtom oldGA = mS3d.geomAtom;
1832 
1833 	GeometryAtom newGA = new GeometryAtom();
1834 
1835 	if(newGeom != null) {
1836 	    newGeom.addUser(mS3d);
1837 	}
1838 
1839 	int gSize = geometryList.size();
1840 
1841 	for(i=0; i<gSize; i++) {
1842 	    geometry = (GeometryRetained) geometryList.get(i);
1843 	    if(geometry != null) {
1844 		newGA.geoType = geometry.geoType;
1845 		newGA.alphaEditable = mS3d.isAlphaEditable(geometry);
1846 		break;
1847 	    }
1848 	}
1849 
1850 	if((geometry != null) &&
1851 	   (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D)) {
1852 
1853 	    for(i = 0; i<gSize; i++) {
1854 		geometry = (GeometryRetained) geometryList.get(i);
1855 		if(geometry != null) {
1856 		    Text3DRetained tempT3d = (Text3DRetained)geometry;
1857 		    geometryCnt += tempT3d.numChars;
1858 		}
1859 		else {
1860 		    // This is slightly wasteful, but not quite worth to optimize yet.
1861 		    geometryCnt++;
1862 		}
1863 	    }
1864 	    newGA.geometryArray = new GeometryRetained[geometryCnt];
1865 	    newGA.lastLocalTransformArray = new Transform3D[geometryCnt];
1866 	    // Reset geometryCnt;
1867 	    geometryCnt = 0;
1868 
1869 	}
1870 	else {
1871 	    newGA.geometryArray = new GeometryRetained[gSize];
1872 	}
1873 
1874 	newGA.locale = mS3d.locale;
1875 	newGA.visible = visible;
1876 	newGA.source = mS3d;
1877 
1878 
1879 	for(gaCnt = 0; gaCnt<gSize; gaCnt++) {
1880 	    geometry = (GeometryRetained) geometryList.get(gaCnt);
1881 	    if(geometry == null) {
1882 		newGA.geometryArray[geometryCnt++] = null;
1883 	    }
1884 	    else {
1885 		if (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D) {
1886 		    Text3DRetained t = (Text3DRetained)geometry;
1887 		    GeometryRetained geo;
1888 		    for (i=0; i<t.numChars; i++, geometryCnt++) {
1889 			geo = t.geometryList[i];
1890 			if (geo!= null) {
1891 			    newGA.geometryArray[geometryCnt] = geo;
1892 			    newGA.lastLocalTransformArray[geometryCnt] =
1893 				t.charTransforms[i];
1894 
1895 			} else {
1896 			    newGA.geometryArray[geometryCnt] = null;
1897 			    newGA.lastLocalTransformArray[geometryCnt] = null;
1898 			}
1899 
1900 		    }
1901 
1902 		} else {
1903 		    newGA.geometryArray[geometryCnt++] = geometry;
1904 		}
1905 	    }
1906 	}
1907 
1908 	oldGAArray = new GeometryAtom[s3dMSize];
1909 	newGAArray = new GeometryAtom[s3dMSize];
1910 	oldGAArray[0] = oldGA;
1911 	newGAArray[0] = newGA;
1912 
1913 	mS3d.geomAtom = newGA;
1914 	mS3d.mirrorShape3DLock.writeUnlock();
1915 
1916 	// ..... clone the rest of mirrorS3D's GA with the above newGA, but modify
1917 	// its source.
1918 
1919 	for (i = 1; i < s3dMSize; i++) {
1920 	    mS3d = (Shape3DRetained) mirrorShape3D.get(i);
1921 	    mS3d.mirrorShape3DLock.writeLock();
1922 	    oldGA = mS3d.geomAtom;
1923 	    newGA = new GeometryAtom();
1924 
1925 	    if(newGeom != null) {
1926 		newGeom.addUser(mS3d);
1927 	    }
1928 
1929 	    newGA.geoType = newGAArray[0].geoType;
1930 	    newGA.locale = mS3d.locale;
1931 	    newGA.visible = visible;
1932 	    newGA.source = mS3d;
1933 	    newGA.alphaEditable = newGAArray[0].alphaEditable;
1934 
1935 	    newGA.geometryArray = new GeometryRetained[newGAArray[0].geometryArray.length];
1936 	    for(j=0; j<newGA.geometryArray.length; j++) {
1937 		newGA.geometryArray[j] = newGAArray[0].geometryArray[j];
1938 	    }
1939 
1940 	    oldGAArray[i] = oldGA;
1941 	    newGAArray[i] = newGA;
1942 
1943 	    mS3d.geomAtom = newGA;
1944 	    mS3d.mirrorShape3DLock.writeUnlock();
1945 	}
1946 
1947         TargetsInterface ti =
1948 		((GroupRetained)parent).getClosestTargetsInterface(
1949                                         TargetsInterface.TRANSFORM_TARGETS);
1950 	CachedTargets[] newCtArr = null;
1951 
1952         if (ti != null) {
1953 	    CachedTargets ct;
1954 	    newCtArr = new CachedTargets[s3dMSize];
1955 
1956             for (i=0; i<s3dMSize; i++) {
1957 
1958                 ct = ti.getCachedTargets(
1959                                 TargetsInterface.TRANSFORM_TARGETS, i, -1);
1960                 if (ct != null) {
1961 		    newCtArr[i] = new CachedTargets();
1962 		    newCtArr[i].copy(ct);
1963 		    newCtArr[i].replace(oldGAArray[i], newGAArray[i],
1964 					Targets.GEO_TARGETS);
1965                 } else {
1966 		    newCtArr[i] = null;
1967                 }
1968             }
1969             ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS,
1970 							newCtArr, -1);
1971 	}
1972 
1973 
1974 	J3dMessage changeMessage  = new J3dMessage();
1975 	changeMessage.type = J3dMessage.SHAPE3D_CHANGED;
1976 	// Who to send this message to ?
1977 	changeMessage.threads = J3dThread.UPDATE_RENDER |
1978 	    J3dThread.UPDATE_TRANSFORM |
1979 	    J3dThread.UPDATE_GEOMETRY;
1980 	changeMessage.universe = universe;
1981 	changeMessage.args[0] = this;
1982 	changeMessage.args[1] = new Integer(GEOMETRY_CHANGED);
1983 	changeMessage.args[2] = oldGAArray;
1984 	changeMessage.args[3] = newGAArray;
1985 	if (ti != null) {
1986 	    changeMessage.args[4] = ti;
1987 	    changeMessage.args[5] = newCtArr;
1988 	}
1989 	if (boundsAutoCompute) {
1990 	    getCombineBounds((BoundingBox)localBounds);
1991 	}
1992 	VirtualUniverse.mc.processMessage(changeMessage);
1993 
1994     }
1995 
1996 
1997     // ********** End of New 1.2.1 code ....
1998 
1999 
2000 
2001 
2002 
getMirrorShape(SceneGraphPath path)2003     Shape3DRetained getMirrorShape(SceneGraphPath path) {
2004 	if (!inSharedGroup) {
2005 	    return (Shape3DRetained) mirrorShape3D.get(0);
2006 	}
2007 	HashKey key = new HashKey("");
2008 	path.getHashKey(key);
2009 	return getMirrorShape(key);
2010     }
2011 
getMirrorShape(HashKey key)2012     Shape3DRetained getMirrorShape(HashKey key) {
2013 	if (key == null) {
2014 	    return (Shape3DRetained) mirrorShape3D.get(0);
2015 	} else {
2016 	    int i = key.equals(localToVworldKeys, 0, localToVworldKeys.length);
2017 
2018 	    if (i>=0) {
2019 		return (Shape3DRetained) mirrorShape3D.get(i);
2020 	    }
2021 	}
2022 	// Not possible
2023 	throw new RuntimeException("Shape3DRetained: MirrorShape Not found!");
2024     }
2025 
setBoundsAutoCompute(boolean autoCompute)2026     void setBoundsAutoCompute(boolean autoCompute) {
2027 	GeometryRetained geometry;
2028         if (autoCompute != boundsAutoCompute) {
2029             if (autoCompute) {
2030                 // localBounds may not have been set to bbox
2031                 localBounds = new BoundingBox((BoundingBox) null);
2032 		if (source.isLive() && geometryList != null) {
2033 		    int size = geometryList.size()*mirrorShape3D.size();
2034 		    for (int i=0; i<size; i++) {
2035 			geometry = (GeometryRetained) geometryList.get(i);
2036 			geometry.incrComputeGeoBounds();
2037 		    }
2038 		}
2039 
2040 		getCombineBounds((BoundingBox)localBounds);
2041             }
2042 	    else {
2043 		if (source.isLive() && geometryList != null) {
2044 		    int size = geometryList.size()*mirrorShape3D.size();
2045 		    for (int i=0; i<size; i++) {
2046 			geometry = (GeometryRetained) geometryList.get(i);
2047 			geometry.decrComputeGeoBounds();
2048 		    }
2049 
2050 		}
2051             }
2052             super.setBoundsAutoCompute(autoCompute);
2053             if (source.isLive()) {
2054                 J3dMessage message = new J3dMessage();
2055                 message.type = J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED;
2056                 message.threads = J3dThread.UPDATE_TRANSFORM |
2057 		    J3dThread.UPDATE_GEOMETRY |
2058 		    J3dThread.UPDATE_RENDER;
2059                 message.universe = universe;
2060                 message.args[0] = getGeomAtomsArray(mirrorShape3D);
2061 		// no need to clone localBounds
2062                 message.args[1] = localBounds;
2063 		VirtualUniverse.mc.processMessage(message);
2064 	    }
2065         }
2066     }
2067     // This method is called when coordinates of a geometry in the geometrylist
2068     // changed and autoBoundsCompute is true
2069 
updateBounds()2070     void updateBounds() {
2071 	localBounds = new BoundingBox((BoundingBox) null);
2072 	getCombineBounds((BoundingBox)localBounds);
2073 	synchronized(mirrorShape3D) {
2074 	    if (source.isLive()) {
2075 		J3dMessage message = new J3dMessage();
2076 		message.type = J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED;
2077 		message.threads = J3dThread.UPDATE_TRANSFORM |
2078 		    J3dThread.UPDATE_GEOMETRY |
2079 		    J3dThread.UPDATE_RENDER;
2080 		message.universe = universe;
2081 		message.args[0] = getGeomAtomsArray(mirrorShape3D);
2082 		// no need to clone localBounds
2083 		message.args[1] = localBounds;
2084 		VirtualUniverse.mc.processMessage(message);
2085 	    }
2086 	}
2087     }
2088 
allowIntersect()2089     boolean allowIntersect() {
2090 	GeometryRetained ga = null;
2091 
2092 	for(int i=0; i<geometryList.size(); i++) {
2093 	    ga = (GeometryRetained) geometryList.get(i);
2094 	    if(ga != null)
2095 		if (!ga.source.getCapability(Geometry.ALLOW_INTERSECT)) {
2096 		    return false;
2097 		}
2098 	}
2099 	return true;
2100     }
intersectGeometryList(Shape3DRetained otherShape)2101     boolean intersectGeometryList(Shape3DRetained otherShape) {
2102 	GeometryRetained geom1, geom2;
2103 	ArrayList gaList = otherShape.geometryList;
2104 	int gaSize =  gaList.size();
2105 	Transform3D otherLocalToVworld = otherShape.getCurrentLocalToVworld();
2106 	Transform3D thisLocalToVworld = getCurrentLocalToVworld();
2107 	View views = null;
2108 	int primaryViewIdx = -1;
2109 
2110 
2111 	if (this instanceof OrientedShape3DRetained) {
2112 	    primaryViewIdx = getPrimaryViewIdx();
2113 	    thisLocalToVworld.mul(((OrientedShape3DRetained)this).
2114 				  getOrientedTransform(primaryViewIdx));
2115 	}
2116 
2117 	if (otherShape instanceof OrientedShape3DRetained) {
2118 	    if (primaryViewIdx < 0) {
2119 		primaryViewIdx = getPrimaryViewIdx();
2120 	    }
2121 	    otherLocalToVworld.mul(((OrientedShape3DRetained)otherShape).
2122 				   getOrientedTransform(primaryViewIdx));
2123 	}
2124 
2125 	for (int i=geometryList.size()-1; i >=0; i--) {
2126 	    geom1 = (GeometryRetained) geometryList.get(i);
2127 	    if (geom1 != null) {
2128 		for (int j=gaSize-1; j >=0; j--) {
2129 		    geom2 = (GeometryRetained) gaList.get(j);
2130 		    if ((geom2 != null) &&
2131 			geom1.intersect(thisLocalToVworld,
2132 					otherLocalToVworld, geom2)) {
2133 			return true;
2134 		    }
2135 		}
2136 	    }
2137 	}
2138 
2139 	return false;
2140     }
2141 
intersectGeometryList(Transform3D thisLocalToVworld, Bounds targetBound)2142     boolean intersectGeometryList(Transform3D thisLocalToVworld, Bounds targetBound) {
2143 
2144 	GeometryRetained geometry;
2145 
2146 	if (this instanceof OrientedShape3DRetained) {
2147 	    Transform3D orientedTransform =
2148 		((OrientedShape3DRetained)this).
2149 		getOrientedTransform(getPrimaryViewIdx());
2150 	    thisLocalToVworld.mul(orientedTransform);
2151 	}
2152 
2153 	for (int i=geometryList.size() - 1; i >=0; i--) {
2154 	    geometry = (GeometryRetained) geometryList.get(i);
2155 	    if ((geometry != null) &&
2156 		geometry.intersect(thisLocalToVworld, targetBound)) {
2157 		return true;
2158 	    }
2159 	}
2160 
2161 	return false;
2162 
2163     }
2164 
2165 
2166     /**
2167      * This initialize the mirror shape to reflect the state of the
2168      * real Morph.
2169      */
initMirrorShape3D(SetLiveState s, MorphRetained morph, int index)2170     void initMirrorShape3D(SetLiveState s, MorphRetained morph, int index) {
2171 
2172 	GeometryRetained geometry;
2173 
2174 	GeometryAtom[] newGeometryAtoms = null;
2175 
2176 	universe = morph.universe;
2177 	inSharedGroup = morph.inSharedGroup;
2178         inBackgroundGroup = morph.inBackgroundGroup;
2179         geometryBackground = morph.geometryBackground;
2180         parent = morph.parent;
2181 	locale = morph.locale;
2182 
2183         OrderedPath op = (OrderedPath)s.orderedPaths.get(index);
2184         if (op.pathElements.size() == 0) {
2185             orderedPath = null;
2186         } else {
2187             orderedPath = op;
2188         }
2189 
2190 	staticTransform = morph.staticTransform;
2191         if (morph.boundsAutoCompute) {
2192             localBounds.set(morph.localBounds);
2193         }
2194         bounds = localBounds;
2195         vwcBounds = new BoundingBox((BoundingBox) null);
2196         vwcBounds.transform(bounds, getCurrentLocalToVworld(0));
2197 
2198         if (morph.collisionBound == null) {
2199             collisionBound = null;
2200             collisionVwcBound = vwcBounds;
2201         } else {
2202             collisionBound = morph.collisionBound;
2203             collisionVwcBound = (Bounds)collisionBound.clone();
2204             collisionVwcBound.transform(getCurrentLocalToVworld(0));
2205         }
2206 
2207 	appearanceOverrideEnable = morph.appearanceOverrideEnable;
2208 
2209 	// mga is the final geometry we're interested.
2210 	geometryList = new ArrayList(1);
2211 	geometryList.add((GeometryArrayRetained)morph.morphedGeometryArray.retained);
2212 
2213 	GeometryAtom gAtom = new GeometryAtom();
2214 	gAtom.geometryArray = new GeometryRetained[1];
2215 
2216 	gAtom.locale = locale;
2217 	gAtom.visible = morph.visible;
2218 	gAtom.source = this;
2219 
2220 	geometry = (GeometryRetained) geometryList.get(0);
2221 
2222 	if(geometry ==null) {
2223 	    gAtom.geometryArray[0] = null;
2224 	} else {
2225 	    gAtom.geometryArray[0] = (GeometryArrayRetained)morph.
2226 		morphedGeometryArray.retained;
2227 	    gAtom.geoType = gAtom.geometryArray[0].geoType;
2228 	}
2229 	geomAtom = gAtom;
2230 
2231 	// Assign the parent of this mirror shape node
2232 	sourceNode = morph;
2233     }
2234 
2235     // geometries in morph object is modified, update the geometry
2236     // list in the mirror shapes and the geometry array in the geometry atom
2237 
setMorphGeometry(Geometry geometry, ArrayList mirrorShapes)2238     void setMorphGeometry(Geometry geometry, ArrayList mirrorShapes) {
2239         GeometryAtom oldGA, newGA;
2240 	Shape3DRetained ms;
2241         TransformGroupRetained tg;
2242         int nMirrorShapes = mirrorShapes.size();
2243 	int i;
2244 
2245         GeometryAtom oldGAArray[] = new GeometryAtom[nMirrorShapes];
2246         GeometryAtom newGAArray[] = new GeometryAtom[nMirrorShapes];
2247 
2248 
2249 	for (i = 0; i < nMirrorShapes; i++) {
2250 	    ms = (Shape3DRetained) mirrorShapes.get(i);
2251 
2252 	    oldGA = Shape3DRetained.getGeomAtom(ms);
2253 
2254             ms.geometryList = new ArrayList(1);
2255             ms.geometryList.add((GeometryArrayRetained)geometry.retained);
2256 
2257             newGA = new GeometryAtom();
2258             newGA.geometryArray = new GeometryRetained[1];
2259 
2260             if (geometry ==null) {
2261                 newGA.geometryArray[0] = null;
2262             } else {
2263                 newGA.geometryArray[0] =
2264 			(GeometryArrayRetained)geometry.retained;
2265                 newGA.geoType = newGA.geometryArray[0].geoType;
2266             }
2267 
2268             newGA.locale = locale;
2269             newGA.visible = oldGA.visible;
2270             newGA.source = this;
2271 
2272             oldGAArray[i] = oldGA;
2273             newGAArray[i] = newGA;
2274 
2275 	    Shape3DRetained.setGeomAtom(ms, newGA);
2276 	}
2277 
2278         TargetsInterface ti =
2279 		((GroupRetained)parent).getClosestTargetsInterface(
2280                                         TargetsInterface.TRANSFORM_TARGETS);
2281 	CachedTargets[] newCtArr = null;
2282 
2283         if (ti != null) {
2284 	    CachedTargets ct;
2285 	    newCtArr = new CachedTargets[nMirrorShapes];
2286 
2287             for (i=0; i<nMirrorShapes; i++) {
2288 
2289                 ct = ti.getCachedTargets(
2290                                 TargetsInterface.TRANSFORM_TARGETS, i, -1);
2291                 if (ct != null) {
2292 		    newCtArr[i] = new CachedTargets();
2293 		    newCtArr[i].copy(ct);
2294 		    newCtArr[i].replace(oldGAArray[i], newGAArray[i],
2295 					Targets.GEO_TARGETS);
2296                 } else {
2297 		    newCtArr[i] = null;
2298                 }
2299             }
2300         }
2301 
2302 	// send a Shape GEOMETRY_CHANGED message for all geometry atoms
2303 
2304 	J3dMessage changeMessage  = new J3dMessage();
2305 	changeMessage.type = J3dMessage.SHAPE3D_CHANGED;
2306 	changeMessage.threads = J3dThread.UPDATE_RENDER |
2307 	    J3dThread.UPDATE_TRANSFORM |
2308 	    J3dThread.UPDATE_GEOMETRY;
2309 	changeMessage.universe = universe;
2310 	changeMessage.args[0] = this;
2311 	changeMessage.args[1] = new Integer(GEOMETRY_CHANGED);
2312 	changeMessage.args[2] = oldGAArray;
2313 	changeMessage.args[3] = newGAArray;
2314         if (ti != null) {
2315             changeMessage.args[4] = ti;
2316             changeMessage.args[5] = newCtArr;
2317         }
2318 	VirtualUniverse.mc.processMessage(changeMessage);
2319     }
2320 
2321 
2322     /**
2323      * Return an array of geometry atoms belongs to userList.
2324      * The input is an arraylist of Shape3DRetained type.
2325      * This is used to send a message of the snapshot of the
2326      * geometry atoms that are affected by this change.
2327      */
getGeomAtomsArray(ArrayList userList)2328     final static GeometryAtom[] getGeomAtomsArray(ArrayList userList) {
2329 	Shape3DRetained ms = null;
2330 	GeometryAtom[] gaArr = null;
2331 	int size, nullCnt=0, i, j;
2332 
2333 	synchronized(userList) {
2334 	    size = userList.size();
2335 	    gaArr = new GeometryAtom[size];
2336 	    for (i = 0; i < size; i++) {
2337 		ms = (Shape3DRetained) userList.get(i);
2338 		ms.mirrorShape3DLock.readLock();
2339 		if(ms.geomAtom == null) {
2340 		    nullCnt++;
2341 		}
2342 		gaArr[i] = ms.geomAtom;
2343 		ms.mirrorShape3DLock.readUnlock();
2344 	    }
2345 	}
2346 	if(nullCnt == 0) {
2347 	    return gaArr;
2348 	}
2349 	else if(nullCnt == size) {
2350 	    return null;
2351 	}
2352 	else {
2353 	    GeometryAtom[] newGaArr = new GeometryAtom[size - nullCnt];
2354 
2355 	    for (i=0, j=0; i < size; i++) {
2356 		if(gaArr[i] != null) {
2357 		    newGaArr[j++] = gaArr[i];
2358 		}
2359 	    }
2360 	    return newGaArr;
2361 	}
2362     }
2363 
2364     /**
2365      * Return a list of geometry atoms belongs to userList and places a list of
2366      * universe found in userList in univList.
2367      * The input is an array of Shape3DRetained type.
2368      * univList is assume to be empty.
2369      * This is used to send a message of the snapshot of the
2370      * geometry atoms that are affected by this change.
2371      */
getGeomAtomsList(ArrayList userList, ArrayList univList)2372     final static ArrayList getGeomAtomsList(ArrayList userList, ArrayList univList) {
2373 	ArrayList listPerUniverse = new ArrayList();
2374 	int index;
2375 	ArrayList gaList = null;
2376 	Shape3DRetained ms = null;
2377 	boolean moreThanOneUniv = false;
2378 	VirtualUniverse firstFndUniv = null;
2379 
2380 	synchronized(userList) {
2381 	    for (int i = userList.size()-1; i >=0; i--) {
2382 		ms = (Shape3DRetained) userList.get(i);
2383 
2384 		if(moreThanOneUniv == false) {
2385 		    if(firstFndUniv == null) {
2386 			firstFndUniv = ms.universe;
2387 			univList.add(ms.universe);
2388 
2389 			gaList = new ArrayList();
2390 			listPerUniverse.add(gaList);
2391 		    }
2392 		    else if(firstFndUniv != ms.universe) {
2393 			moreThanOneUniv = true;
2394 			univList.add(ms.universe);
2395 			gaList = new ArrayList();
2396 			listPerUniverse.add(gaList);
2397 		    }
2398 		}
2399 		else {
2400 		    index = univList.indexOf(ms.universe);
2401 		    if (index < 0) {
2402 			univList.add(ms.universe);
2403 			gaList = new ArrayList();
2404 			listPerUniverse.add(gaList);
2405 		    }
2406 		    else {
2407 			gaList = (ArrayList) listPerUniverse.get(index);
2408 		    }
2409 		}
2410 
2411 
2412 		ms.mirrorShape3DLock.readLock();
2413 
2414 		if(ms.geomAtom != null) {
2415 		    gaList.add(ms.geomAtom);
2416 		}
2417 		ms.mirrorShape3DLock.readUnlock();
2418 
2419 	    }
2420 	}
2421 	return listPerUniverse;
2422     }
2423 
getGeomAtom(Shape3DRetained shape)2424     final static GeometryAtom getGeomAtom(Shape3DRetained shape) {
2425 	GeometryAtom ga;
2426 
2427 	shape.mirrorShape3DLock.readLock();
2428 	ga = shape.geomAtom;
2429 	shape.mirrorShape3DLock.readUnlock();
2430 
2431 	return ga;
2432     }
2433 
setGeomAtom(Shape3DRetained shape, GeometryAtom ga)2434     final static void setGeomAtom(Shape3DRetained shape, GeometryAtom ga) {
2435 	shape.mirrorShape3DLock.writeLock();
2436 	shape.geomAtom = ga;
2437 	shape.mirrorShape3DLock.writeUnlock();
2438     }
2439 
2440 
2441     // Alpha is editable due to the appearance
isAlphaEditable(GeometryRetained geo)2442     boolean isAlphaEditable(GeometryRetained geo) {
2443 
2444         boolean alphaEditable = false;
2445 
2446 	if (appearanceOverrideEnable) {
2447 	    alphaEditable = true;
2448         } else if (geo != null &&
2449 		   appearance != null) {
2450 
2451             AppearanceRetained app = appearance;
2452 
2453             if (source.getCapability(
2454                         Shape3D.ALLOW_APPEARANCE_WRITE) ||
2455 		source.getCapability(
2456                         Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) ||
2457 
2458                 app.source.getCapability(
2459                         Appearance.ALLOW_RENDERING_ATTRIBUTES_WRITE) ||
2460 
2461                 app.source.getCapability(
2462                         Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE) ||
2463 
2464                 (app.renderingAttributes != null &&
2465                  (app.renderingAttributes.source.getCapability(
2466                         RenderingAttributes.ALLOW_ALPHA_TEST_FUNCTION_WRITE) ||
2467 		  app.renderingAttributes.source.getCapability(
2468                         RenderingAttributes.ALLOW_IGNORE_VERTEX_COLORS_WRITE))) ||
2469 
2470                 (app.transparencyAttributes != null &&
2471                  (app.transparencyAttributes.source.getCapability(
2472                         TransparencyAttributes.ALLOW_MODE_WRITE) ||
2473                   app.transparencyAttributes.source.getCapability(
2474                         TransparencyAttributes.ALLOW_VALUE_WRITE)))) {
2475 
2476                 alphaEditable = true;
2477 
2478             } else if (geo instanceof GeometryArrayRetained &&
2479                        (app.source.getCapability(
2480                         Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE) ||
2481 
2482                         (app.textureAttributes != null &&
2483                          app.textureAttributes.source.getCapability(
2484                         TextureAttributes.ALLOW_MODE_WRITE)))) {
2485 
2486                 alphaEditable = true;
2487 
2488             } else if (geo instanceof RasterRetained) {
2489                 if ((((RasterRetained)geo).type & Raster.RASTER_COLOR) !=
2490 0
2491                     && ((RasterRetained)geo).source.getCapability(
2492                         Raster.ALLOW_IMAGE_WRITE)) {
2493 
2494                     alphaEditable = true;
2495                 }
2496             }
2497         }
2498 	return alphaEditable;
2499     }
2500 
2501     // getCombineBounds is faster than computeCombineBounds since it
2502     // does not recompute the geometry.geoBounds
getCombineBounds(BoundingBox bounds)2503     void getCombineBounds(BoundingBox bounds) {
2504 
2505         if(geometryList != null) {
2506 	    BoundingBox bbox = null;
2507             GeometryRetained geometry;
2508 
2509 	    if (staticTransform != null) {
2510 		bbox = new BoundingBox((BoundingBox) null);
2511 	    }
2512 
2513             synchronized(bounds) {
2514                 bounds.setLower( 1.0, 1.0, 1.0);
2515                 bounds.setUpper(-1.0,-1.0,-1.0);
2516                 for(int i=0; i<geometryList.size(); i++) {
2517                     geometry = (GeometryRetained) geometryList.get(i);
2518                     if ((geometry != null) &&
2519                         (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) {
2520                         synchronized(geometry.geoBounds) {
2521                             if (staticTransform != null) {
2522                                 bbox.set(geometry.geoBounds);
2523                                 bbox.transform(staticTransform.transform);
2524                                 bounds.combine((Bounds)bbox);
2525                             } else {
2526                                 bounds.combine((Bounds)geometry.geoBounds);
2527                             }
2528                         }
2529                     }
2530                 }
2531             }
2532 
2533 	    // System.err.println("Shape3DRetained - getCombineBounds");
2534 	    // Enlarge boundingBox to the "minmium bounds" that encompasses all possible
2535 	    // orientation.
2536 	    if (this instanceof OrientedShape3DRetained) {
2537 		double maxVal = Math.abs(bounds.lower.x);
2538 		double tempVal = Math.abs(bounds.upper.x);
2539 		if(tempVal > maxVal)
2540 		    maxVal = tempVal;
2541 		tempVal = Math.abs(bounds.lower.y);
2542 		if(tempVal > maxVal)
2543 		    maxVal = tempVal;
2544 		tempVal = Math.abs(bounds.upper.y);
2545 		if(tempVal > maxVal)
2546 		    maxVal = tempVal;
2547 		tempVal = Math.abs(bounds.lower.z);
2548 		if(tempVal > maxVal)
2549 		    maxVal = tempVal;
2550 		tempVal = Math.abs(bounds.upper.z);
2551 		if(tempVal > maxVal)
2552 		    maxVal = tempVal;
2553 
2554 		// System.err.println("Shape3DRetained - bounds (Before) " + bounds);
2555 		bounds.setLower(-maxVal, -maxVal, -maxVal);
2556 		bounds.setUpper(maxVal, maxVal, maxVal);
2557 		// System.err.println("Shape3DRetained - bounds (After) " + bounds);
2558 	    }
2559 
2560         }
2561     }
2562 
2563 
isEquivalent(Shape3DRetained shape)2564     boolean isEquivalent(Shape3DRetained shape) {
2565 	if (this.appearance != shape.appearance ||
2566 	    // Scoping info should be same since they are under same group
2567 	    this.appearanceOverrideEnable != shape.appearanceOverrideEnable ||
2568 	    this.isPickable != shape.isPickable ||
2569 	    this.isCollidable != shape.isCollidable) {
2570 
2571 	    return false;
2572 	}
2573 	if (this.boundsAutoCompute) {
2574 	    if (!shape.boundsAutoCompute)
2575 		return false;
2576 	}
2577 	else {
2578 	    // If bounds autoCompute is false
2579 	    // Then check if both bounds are equal
2580 	    if (this.localBounds != null) {
2581 		if (shape.localBounds != null) {
2582 		    return this.localBounds.equals(shape.localBounds);
2583 		}
2584 	    }
2585 	    else if (shape.localBounds != null) {
2586 		return false;
2587 	    }
2588 	}
2589 	if (collisionBound != null) {
2590 	    if (shape.collisionBound == null)
2591 		return false;
2592 	    else
2593 		return collisionBound.equals(shape.collisionBound);
2594 	}
2595 	else if (shape.collisionBound != null)
2596 	    return false;
2597 
2598 	return true;
2599     }
2600 
2601     // Bounds can only be set after the geometry is setLived, so has to be done
2602     // here, if we are not using switchVwcBounds
initializeGAtom(Shape3DRetained ms)2603     void initializeGAtom(Shape3DRetained ms) {
2604 	int i, gaCnt;
2605 	int geometryCnt = 0;
2606 	int gSize = geometryList.size();
2607 	GeometryRetained geometry = null;
2608 
2609 	ms.bounds = localBounds;
2610 	ms.vwcBounds = new BoundingBox((BoundingBox) null);
2611 	ms.vwcBounds.transform(ms.bounds, ms.getCurrentLocalToVworld(0));
2612 
2613 	if (collisionBound == null) {
2614 	    ms.collisionBound = null;
2615 	    ms.collisionVwcBound = ms.vwcBounds;
2616 	} else {
2617 	    ms.collisionBound = collisionBound;
2618 	    ms.collisionVwcBound = (Bounds)ms.collisionBound.clone();
2619 	    ms.collisionVwcBound.transform(ms.getCurrentLocalToVworld(0));
2620 	}
2621 	GeometryAtom gAtom = new GeometryAtom();
2622 	for(gaCnt=0; gaCnt<gSize; gaCnt++) {
2623 	    geometry = (GeometryRetained) geometryList.get(gaCnt);
2624 	    if(geometry != null) {
2625 		gAtom.geoType = geometry.geoType;
2626 		gAtom.alphaEditable = ms.isAlphaEditable(geometry);
2627 		break;
2628 	    }
2629 	}
2630 	if((geometry != null) &&
2631 	   (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D)) {
2632 
2633 	    for(gaCnt = 0; gaCnt<gSize; gaCnt++) {
2634 		geometry = (GeometryRetained) geometryList.get(gaCnt);
2635 		if(geometry != null) {
2636 		    Text3DRetained tempT3d = (Text3DRetained)geometry;
2637 		    geometryCnt += tempT3d.numChars;
2638 		}
2639 		else {
2640 		    // This is slightly wasteful, but not quite worth to optimize yet.
2641 		    geometryCnt++;
2642 		}
2643 	    }
2644 	    gAtom.geometryArray = new GeometryRetained[geometryCnt];
2645 	    gAtom.lastLocalTransformArray = new Transform3D[geometryCnt];
2646 	    // Reset geometryCnt;
2647 	    geometryCnt = 0;
2648 
2649 	}
2650 	else {
2651 	    gAtom.geometryArray = new GeometryRetained[gSize];
2652 	}
2653 
2654 
2655 	for(gaCnt = 0; gaCnt<geometryList.size(); gaCnt++) {
2656 	    geometry = (GeometryRetained) geometryList.get(gaCnt);
2657 	    if(geometry == null) {
2658 		gAtom.geometryArray[gaCnt] = null;
2659 	    }
2660 	    else {
2661 		if (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D) {
2662 		    Text3DRetained t = (Text3DRetained)geometry;
2663 		    GeometryRetained geo;
2664 		    for (i=0; i<t.numChars; i++, geometryCnt++) {
2665 			geo = t.geometryList[i];
2666 			if (geo != null) {
2667 			    gAtom.geometryArray[geometryCnt] = geo;
2668 			    gAtom.lastLocalTransformArray[geometryCnt] =
2669 				t.charTransforms[i];
2670 			} else {
2671 			    gAtom.geometryArray[geometryCnt] = null;
2672 			    gAtom.lastLocalTransformArray[geometryCnt] = null;
2673 			}
2674 
2675 		    }
2676 
2677 		} else {
2678 		    gAtom.geometryArray[gaCnt] = geometry;
2679 		}
2680 	    }
2681 	}
2682 	gAtom.locale = ms.locale;
2683 	gAtom.visible = visible;
2684 	gAtom.source = ms;
2685 	ms.geomAtom = gAtom;
2686     }
2687 
2688     // Check if geomRetained's class is equivalence with the geometry class.
checkEquivalenceClass(Geometry geometry, int index)2689     void checkEquivalenceClass(Geometry geometry, int index) {
2690 
2691 	if (geometry != null) {
2692 	    for (int i=geometryList.size()-1; i >= 0; i--) {
2693 		GeometryRetained geomRetained = (GeometryRetained) geometryList.get(i);
2694 		if ((geomRetained != null) &&
2695 		    (index != i)) { // this geometry will replace
2696 		    // current one so there is no need to check
2697 		    if (!geomRetained.isEquivalenceClass((GeometryRetained)geometry.retained)) {
2698 			throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained5"));
2699 		    }
2700 		    break;
2701 		}
2702 	    }
2703 	}
2704     }
2705 
indexOfGeometry(Geometry geometry)2706     int indexOfGeometry(Geometry geometry) {
2707       if(geometry != null)
2708 	return geometryList.indexOf(geometry.retained);
2709       else
2710 	return geometryList.indexOf(null);
2711     }
2712 
2713 
2714   // Removes the specified geometry from this Shape3DRetained's list of geometries
removeGeometry(Geometry geometry)2715     void removeGeometry(Geometry geometry) {
2716       int ind = indexOfGeometry(geometry);
2717       if(ind >= 0)
2718 	removeGeometry(ind);
2719     }
2720 
2721   // Removes all the geometries from this node
removeAllGeometries()2722     void removeAllGeometries() {
2723       int n = geometryList.size();
2724 
2725       int i;
2726       Shape3DRetained mShape;
2727       GeometryRetained oldGeom = null;
2728 
2729       if (((Shape3D)this.source).isLive()) {
2730 	for(int index = n-1; index >= 0; index--) {
2731 	  oldGeom = (GeometryRetained) (geometryList.get(index));
2732 	  if (oldGeom != null) {
2733 	    oldGeom.clearLive(refCount);
2734 	    oldGeom.decRefCnt();
2735 	    for (i=0; i<mirrorShape3D.size(); i++) {
2736 	      mShape = (Shape3DRetained)mirrorShape3D.get(i);
2737 	      oldGeom.removeUser(mShape);
2738 	    }
2739 	  }
2740 	  geometryList.remove(index);
2741 	}
2742 	sendDataChangedMessage(null);
2743       } else {
2744 	for(int index = n-1; index >= 0; index--) {
2745 	  oldGeom = (GeometryRetained) (geometryList.get(index));
2746 	  if (oldGeom != null) {
2747 	    oldGeom.decRefCnt();
2748 	  }
2749 	  geometryList.remove(index);
2750 	}
2751       }
2752       dirtyBoundsCache();
2753     }
2754 
willRemainOpaque(int geoType)2755     boolean willRemainOpaque(int geoType) {
2756 	if (appearance == null ||
2757 	    (appearance.isStatic() &&
2758 	     appearance.isOpaque(geoType))) {
2759 	    return true;
2760 	}
2761 	else {
2762 	    return false;
2763 	}
2764 
2765     }
2766 
handleFrequencyChange(int bit)2767     void handleFrequencyChange(int bit) {
2768 	int mask = 0;
2769 	if (bit == Shape3D.ALLOW_GEOMETRY_WRITE) {
2770 	    mask = GEOMETRY_CHANGED;
2771 	}
2772 	else if (bit == Shape3D.ALLOW_APPEARANCE_WRITE) {
2773 	    mask = APPEARANCE_CHANGED;
2774 	}
2775 	else if (bit == Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) {
2776 	    mask = APPEARANCEOVERRIDE_CHANGED;
2777 	}
2778 	if (mask != 0) {
2779 	    if (source.getCapabilityIsFrequent(bit))
2780 		changedFrequent |= mask;
2781 	    else if (!source.isLive()) {
2782 		changedFrequent &= ~mask;
2783 	    }
2784 	}
2785     }
2786 
2787 
2788     // Alpha is editable due to the appearance(Called on the MirrorShape3D)
isAlphaFrequentlyEditable(GeometryRetained geo)2789     boolean isAlphaFrequentlyEditable(GeometryRetained geo) {
2790 
2791         boolean alphaFrequentlyEditable = false;
2792 	if (appearanceOverrideEnable) {
2793 	    alphaFrequentlyEditable = true;
2794         } else if (geo != null &&
2795 		   appearance != null) {
2796             AppearanceRetained app = appearance;
2797 
2798             if (((changedFrequent &(APPEARANCE_CHANGED|APPEARANCEOVERRIDE_CHANGED)) != 0)||
2799                 ((app.changedFrequent &(AppearanceRetained.RENDERING|AppearanceRetained.TRANSPARENCY)) != 0) ||
2800                 (app.renderingAttributes != null &&
2801                  (((app.renderingAttributes.changedFrequent & (RenderingAttributesRetained.IGNORE_VCOLOR |RenderingAttributesRetained.ALPHA_TEST_FUNC)) != 0))) ||
2802 
2803                 (app.transparencyAttributes != null &&
2804                  ((app.transparencyAttributes.changedFrequent != 0)))) {
2805 
2806                 alphaFrequentlyEditable = true;
2807 
2808 	    } else if (geo instanceof GeometryArrayRetained &&
2809 		       ((app.changedFrequent & AppearanceRetained.TEXTURE_ATTR)  != 0) ||
2810 		       (app.textureAttributes != null &&
2811 			((app.textureAttributes.changedFrequent & TextureAttributes.ALLOW_MODE_WRITE) != 0))) {
2812                 alphaFrequentlyEditable = true;
2813 
2814             } else if (geo instanceof RasterRetained) {
2815                 if (((((RasterRetained)geo).type & Raster.RASTER_COLOR) !=
2816 0)
2817                     && (((RasterRetained)geo).cachedChangedFrequent != 0)) {
2818 
2819                     alphaFrequentlyEditable = true;
2820                 }
2821             }
2822 	}
2823 	//	System.err.println("changedFrequent="+changedFrequent+" sourceNode = "+sourceNode+" isAlphaFrequentlyEditable, = "+alphaFrequentlyEditable);
2824 	return alphaFrequentlyEditable;
2825     }
2826 
2827 
getPrimaryViewIdx()2828     int getPrimaryViewIdx() {
2829 	// To avoid MT-safe issues when using View, just clone it.
2830 	UnorderList viewList  = VirtualUniverse.mc.cloneView();
2831 	View views[] = (View []) viewList.toArray(false);
2832 	int size = viewList.arraySize();
2833 
2834 	for (int i=0; i < size; i++) {
2835 	    if (views[i].primaryView) {
2836 		return views[i].viewIndex;
2837 	    }
2838 	}
2839 	return 0;
2840     }
2841 
searchGeometryAtoms(UnorderList list)2842     void searchGeometryAtoms(UnorderList list) {
2843 	list.add(getGeomAtom(getMirrorShape(key)));
2844     }
2845 }
2846 
2847