diff options
Diffstat (limited to 'src/javax/media/j3d/Shape3DRetained.java')
-rw-r--r-- | src/javax/media/j3d/Shape3DRetained.java | 2844 |
1 files changed, 2844 insertions, 0 deletions
diff --git a/src/javax/media/j3d/Shape3DRetained.java b/src/javax/media/j3d/Shape3DRetained.java new file mode 100644 index 0000000..7af82a7 --- /dev/null +++ b/src/javax/media/j3d/Shape3DRetained.java @@ -0,0 +1,2844 @@ +/* + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package javax.media.j3d; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; + +import javax.vecmath.Point3d; + +/** + * A shape leaf node consisting of geometry and appearance properties. + */ + +class Shape3DRetained extends LeafRetained { + + static final int GEOMETRY_CHANGED = 0x00001; + static final int APPEARANCE_CHANGED = 0x00002; + static final int COLLISION_CHANGED = 0x00004; + static final int BOUNDS_CHANGED = 0x00008; + static final int APPEARANCEOVERRIDE_CHANGED = 0x00010; + static final int LAST_DEFINED_BIT = 0x00010; + + + // Target threads to be notified when light changes + static final int targetThreads = J3dThread.UPDATE_RENDERING_ENVIRONMENT | + J3dThread.UPDATE_RENDER; + + /** + * The appearance component of the shape node. + */ + AppearanceRetained appearance = null; + +/** + * The arraylist of geometry component of the shape node. + */ +ArrayList<GeometryRetained> geometryList = null; + + /** + * A 2D storage of all geometry atoms associated with this shape node. + * There may be more than one geometry for a Shape3D node. + * Do not change the following private variables to public, its access need to synchronize + * via mirrorShape3DLock. + */ + + // geomAtomArr should always be a 1 element array, unless S3D contains multiple Text3Ds. + private GeometryAtom geomAtom = null; + + /** + * To sychronize access of the mirrorShape3D's geomAtomArray*. + * A multiple read single write Lock to sychronize access into mirrorShape3D. + * To prevent deadlock a call to read/write lock must end with a read/write unlock + * respectively. + */ + private MRSWLock mirrorShape3DLock = null; + + /** + * The mirror Shape3DRetained nodes for this object. There is one + * mirror for each instance of this Shape3D node. If it is not in + * a SharedGroup, only index 0 is valid. + * Do not change the following private variables to public, its access need to synchronize + * via mirrorShape3DLock. + */ + ArrayList<Shape3DRetained> mirrorShape3D = new ArrayList<Shape3DRetained>(1); + + /** + * This field is used for mirror Shape3D nodes accessing their + * original nodes. It is a NodeRetained because the original + * node may be a Shape3DRetained or a MorphRetained node. + */ + NodeRetained sourceNode = null; + + /** + * The hashkey for this Shape3DRetained mirror object + */ + HashKey key = null; + + // This is true when this geometry is referenced in an IMM mode context + boolean inImmCtx = false; + + // A bitmask to indicate when something has changed + int isDirty = 0xffff; + + // The list of lights that are scoped to this node + LightRetained[] lights =null; + + // The number of lights in the above array, may be less than lights.length + int numlights = 0; + + // The list of fogs that are scoped to this node + FogRetained[] fogs = null; + + // The number of fogs in the above array, may be less than fogs.length + int numfogs = 0; + + // The list of modelClips that are scoped to this node + ModelClipRetained[] modelClips = null; + + // The number of modelClips in the above array, may be less than modelClips.length + int numModelClips = 0; + + // The list of alt app that are scoped to this node + AlternateAppearanceRetained[] altApps = null; + + //The number of alt app in the above array, may be less than alt app.length + int numAltApps = 0; + + /** + * Reference to the BranchGroup path of this mirror shape + * This is used for picking and GeometryStructure only. + */ + BranchGroupRetained branchGroupPath[]; + + // cache value for picking in mirror shape. + // True if all the node of the path from this to root are all pickable + boolean isPickable = true; + + // cache value for collidable in mirror shape. + // True if all the node of the path from this to root are all collidable + boolean isCollidable = true; + + // closest switch parent + SwitchRetained closestSwitchParent = null; + + // the child index from the closest switch parent + int closestSwitchIndex = -1; + + // Is this S3D visible ? The default is true. + boolean visible = true; + + // Whether the normal appearance is overrided by the alternate app + boolean appearanceOverrideEnable = false; + + // AlternateAppearance retained that is applicable to this + // mirror shape when the override flag is true + AppearanceRetained otherAppearance = null; + + // geometry Bounds in local coordinate + Bounds bounds = null; + + // geometry Bounds in virtual world coordinate + BoundingBox vwcBounds = null; + + // collision Bounds in local coordinate + Bounds collisionBound = null; + + // collision Bounds in virtual world coordinate + Bounds collisionVwcBound = null; + + // a path of OrderedGroup, childrenId pairs which leads to this node + OrderedPath orderedPath = null; + +// List of views that a mirror object is scoped to +ArrayList<View> viewList = null; + + int changedFrequent = 0; + + Shape3DRetained() { + super(); + this.nodeType = NodeRetained.SHAPE; + numlights = 0; + numfogs = 0; + numModelClips = 0; + numAltApps = 0; + localBounds = new BoundingBox((BoundingBox) null); + + mirrorShape3DLock = new MRSWLock(); + geometryList = new ArrayList<GeometryRetained>(1); + geometryList.add(null); + } + + /** + * Sets the collision bounds of a node. + * @param bounds the bounding object for the node + */ + void setCollisionBounds(Bounds bounds) { + if (bounds == null) { + this.collisionBound = null; + } else { + this.collisionBound = (Bounds)bounds.clone(); + } + + if (source.isLive()) { + // Notify Geometry Structure to check for collision + J3dMessage message = new J3dMessage(); + message.type = J3dMessage.COLLISION_BOUND_CHANGED; + message.threads = J3dThread.UPDATE_TRANSFORM; + message.universe = universe; + message.args[0] = getGeomAtomsArray(mirrorShape3D); + // no need to clone collisionBound + message.args[1] = collisionBound; + VirtualUniverse.mc.processMessage(message); + } + } + + @Override + Bounds getLocalBounds(Bounds bounds) { + if(localBounds != null) { + localBounds.set(bounds); + } + else { + localBounds = new BoundingBox(bounds); + } + return localBounds; + } + + + /** + * Sets the geometric bounds of a node. + * @param bounds the bounding object for the node + */ + @Override + void setBounds(Bounds bounds) { + super.setBounds(bounds); + + if (source.isLive() && !boundsAutoCompute) { + J3dMessage message = new J3dMessage(); + message.type = J3dMessage.REGION_BOUND_CHANGED; + message.threads = J3dThread.UPDATE_TRANSFORM | + J3dThread.UPDATE_GEOMETRY | + J3dThread.UPDATE_RENDER; + + message.universe = universe; + message.args[0] = getGeomAtomsArray(mirrorShape3D); + // no need to clone localBounds + message.args[1] = localBounds; + VirtualUniverse.mc.processMessage(message); + } + } + + /** + * Gets the collision bounds of a node. + * @return the node's bounding object + */ + Bounds getCollisionBounds(int id) { + return (collisionBound == null ? + null: (Bounds)collisionBound.clone()); + } + + /** + * Appends the specified geometry component to this Shape3D + * node's list of geometry components. + * If there are existing geometry components in the list, the new + * geometry component must be of the same equivalence class + * (point, line, polygon, CompressedGeometry, Raster, Text3D) as + * the others. + * @param geometry the geometry component to be appended. + * @exception IllegalArgumentException if the new geometry + * component is not of of the same equivalence class as the + * existing geometry components. + * + * @since Java 3D 1.2 + */ + void addGeometry(Geometry geometry) { + GeometryRetained newGeom = null; + + checkEquivalenceClass(geometry, -1); + + if(((Shape3D)this.source).isLive()) { + if (geometry != null) { + + newGeom = ((GeometryRetained)geometry.retained); + newGeom.setLive(inBackgroundGroup, refCount); + + geometryList.add(newGeom); + + } else { + geometryList.add(null); + newGeom = null; + } + sendDataChangedMessage(newGeom); + + } else { + if (geometry != null) { + geometryList.add((GeometryRetained) geometry.retained); + } else { + geometryList.add(null); + } + } + dirtyBoundsCache(); + } + + /** + * Replaces the geometry component at the specified index in this + * Shape3D node's list of geometry components with the specified + * geometry component. + * If there are existing geometry components in the list (besides + * the one being replaced), the new geometry component must be of + * the same equivalence class (point, line, polygon, CompressedGeometry, + * Raster, Text3D) as the others. + * @param geometry the geometry component to be stored at the + * specified index. + * @param index the index of the geometry component to be replaced. + * @exception IllegalArgumentException if the new geometry + * component is not of of the same equivalence class as the + * existing geometry components. + * + * @since Java 3D 1.2 + */ + void setGeometry(Geometry geometry, int index) { + int i; + Shape3DRetained mShape; + GeometryRetained newGeom = null; + GeometryRetained oldGeom = null; + + checkEquivalenceClass(geometry, index); + + if (((Shape3D)this.source).isLive()) { + + oldGeom = geometryList.get(index); + if (oldGeom != null) { + oldGeom.clearLive(refCount); + for (i=0; i<mirrorShape3D.size(); i++) { + mShape = mirrorShape3D.get(i); + oldGeom.removeUser(mShape); + } + oldGeom.decRefCnt(); + } + + if (geometry != null) { + newGeom = (GeometryRetained) geometry.retained; + newGeom.incRefCnt(); + newGeom.setLive(inBackgroundGroup, refCount); + geometryList.set(index, newGeom); + sendDataChangedMessage(newGeom); + } else { + geometryList.set(index, null); + sendDataChangedMessage(null); + } + + } else { + + oldGeom = geometryList.get(index); + if (oldGeom != null) { + oldGeom.decRefCnt(); + } + if (geometry != null) { + geometryList.set(index,(GeometryRetained) geometry.retained); + ((GeometryRetained)geometry.retained).incRefCnt(); + } else { + geometryList.set(index,null); + } + } + dirtyBoundsCache(); + } + + /** + * Inserts the specified geometry component into this Shape3D + * node's list of geometry components at the specified index. + * If there are existing geometry components in the list, the new + * geometry component must be of the same equivalence class + * (point, line, polygon, CompressedGeometry, Raster, Text3D) as + * the others. + * @param geometry the geometry component to be inserted at the + * specified index. + * @param index the index at which the geometry component is inserted. + * + * @since Java 3D 1.2 + */ + void insertGeometry(Geometry geometry, int index) { + GeometryRetained newGeom = null; + + checkEquivalenceClass(geometry, -1); + + if (((Shape3D)this.source).isLive()) { + + if (geometry != null) { + // Note : The order of the statements in important. Want ArrayList class to do index bounds + // check before creating internal object. + newGeom = (GeometryRetained) geometry.retained; + newGeom.incRefCnt(); + geometryList.add(index, newGeom); + newGeom.setLive(inBackgroundGroup, refCount); + sendDataChangedMessage(newGeom); + } else { + geometryList.add(index, null); + sendDataChangedMessage(null); + } + + } else { + + if (geometry != null) { + geometryList.add(index,(GeometryRetained) geometry.retained); + ((GeometryRetained)geometry.retained).incRefCnt(); + } else { + geometryList.add(index,null); + } + } + dirtyBoundsCache(); + } + + /** + * Removes the geometry component at the specified index from + * this Shape3D node's list of geometry components. + * @param index the index of the geometry component to be removed. + * + * @since Java 3D 1.2 + */ + void removeGeometry(int index) { + int i; + Shape3DRetained mShape; + GeometryRetained oldGeom = null; + + if (((Shape3D)this.source).isLive()) { + + oldGeom = geometryList.get(index); + if (oldGeom != null) { + oldGeom.clearLive(refCount); + oldGeom.decRefCnt(); + for (i=0; i<mirrorShape3D.size(); i++) { + mShape = mirrorShape3D.get(i); + oldGeom.removeUser(mShape); + + } + } + + geometryList.remove(index); + sendDataChangedMessage(null); + + } else { + oldGeom = geometryList.get(index); + if (oldGeom != null) { + oldGeom.decRefCnt(); + } + geometryList.remove(index); + } + + dirtyBoundsCache(); + + } + +/** + * Retrieves the geometry component of this Shape3D node. + * @return the geometry component of this shape node + * @since Java 3D 1.2 + */ +Geometry getGeometry(int index, int id) { + GeometryRetained ga = geometryList.get(index); + if (ga == null) + return null; + else + return (Geometry)ga.source; +} + + /** + * Returns an enumeration of this Shape3D node's list of geometry + * components. + * @return an Enumeration object containing all geometry components in + * this Shape3D node's list of geometry components. + * + * @since Java 3D 1.2 + */ +Enumeration getAllGeometries(int id) { + Vector<Geometry> geomList = new Vector<Geometry>(geometryList.size()); + + for (int i = 0; i < geometryList.size(); i++) { + GeometryRetained ga = geometryList.get(i); + if (ga != null) + geomList.add((Geometry) ga.source); + else + geomList.add(null); + } + + return geomList.elements(); +} + + /** + * Returns the number of geometry components in this Shape3D node's + * list of geometry components. + * @return the number of geometry components in this Shape3D node's + * list of geometry components. + * + * @since Java 3D 1.2 + */ + int numGeometries(int id) { + + return geometryList.size(); + } + + /** + * Sets the appearance component of this Shape3D node. + * @param appearance the new apearance component for this shape node + */ + void setAppearance(Appearance newAppearance) { + + Shape3DRetained s; + boolean visibleIsDirty = false; + + if (((Shape3D)this.source).isLive()) { + if (appearance != null) { + appearance.clearLive(refCount); + for (int i=0; i<mirrorShape3D.size(); i++) { + s = mirrorShape3D.get(i); + appearance.removeAMirrorUser(s); + } + } + + if (newAppearance != null) { + ((AppearanceRetained)newAppearance.retained).setLive(inBackgroundGroup, refCount); + appearance = ((AppearanceRetained)newAppearance.retained); + for (int i=0; i<mirrorShape3D.size(); i++) { + s = mirrorShape3D.get(i); + appearance.addAMirrorUser(s); + } + if((appearance.renderingAttributes != null) && + (visible != appearance.renderingAttributes.visible)) { + visible = appearance.renderingAttributes.visible; + visibleIsDirty = true; + } + } + else { + if(visible == false) { + visible = true; + visibleIsDirty = true; + } + } + int size = 0; + if (visibleIsDirty) + size = 2; + else + size = 1; + J3dMessage[] createMessage = new J3dMessage[size]; + // Send a message + createMessage[0] = new J3dMessage(); + createMessage[0].threads = targetThreads; + createMessage[0].type = J3dMessage.SHAPE3D_CHANGED; + createMessage[0].universe = universe; + createMessage[0].args[0] = this; + createMessage[0].args[1]= new Integer(APPEARANCE_CHANGED); + Shape3DRetained[] s3dArr = new Shape3DRetained[mirrorShape3D.size()]; + mirrorShape3D.toArray(s3dArr); + createMessage[0].args[2] = s3dArr; + Object[] obj = new Object[2]; + if (newAppearance == null) { + obj[0] = null; + } + else { + obj[0] = appearance.mirror; + } + obj[1] = new Integer(changedFrequent); + createMessage[0].args[3] = obj; + createMessage[0].args[4] = getGeomAtomsArray(mirrorShape3D); + if(visibleIsDirty) { + createMessage[1] = new J3dMessage(); + createMessage[1].threads = J3dThread.UPDATE_GEOMETRY; + createMessage[1].type = J3dMessage.SHAPE3D_CHANGED; + createMessage[1].universe = universe; + createMessage[1].args[0] = this; + createMessage[1].args[1]= new Integer(APPEARANCE_CHANGED); + createMessage[1].args[2]= visible?Boolean.TRUE:Boolean.FALSE; + createMessage[1].args[3]= createMessage[0].args[4]; + } + VirtualUniverse.mc.processMessage(createMessage); + + } + else { // not live. + if (newAppearance == null) { + appearance = null; + } else { + appearance = (AppearanceRetained) newAppearance.retained; + } + } + } + + /** + * Retrieves the shape node's appearance component. + * @return the shape node's appearance + */ + Appearance getAppearance() { + return (appearance == null ? null: (Appearance) appearance.source); + } + + void setAppearanceOverrideEnable(boolean flag) { + if (((Shape3D)this.source).isLive()) { + + // Send a message + J3dMessage createMessage = new J3dMessage(); + createMessage.threads = targetThreads; + createMessage.type = J3dMessage.SHAPE3D_CHANGED; + createMessage.universe = universe; + createMessage.args[0] = this; + createMessage.args[1]= new Integer(APPEARANCEOVERRIDE_CHANGED); + Shape3DRetained[] s3dArr = new Shape3DRetained[mirrorShape3D.size()]; + mirrorShape3D.toArray(s3dArr); + createMessage.args[2] = s3dArr; + Object[] obj = new Object[2]; + if (flag) { + obj[0] = Boolean.TRUE; + } + else { + obj[0] = Boolean.FALSE; + } + obj[1] = new Integer(changedFrequent); + createMessage.args[3] = obj; + createMessage.args[4] = getGeomAtomsArray(mirrorShape3D); + + VirtualUniverse.mc.processMessage(createMessage); + } + appearanceOverrideEnable = flag; + } + + boolean getAppearanceOverrideEnable() { + return appearanceOverrideEnable; + } + + boolean intersect(PickInfo pickInfo, PickShape pickShape, int flags ) { + + Transform3D localToVworld = pickInfo.getLocalToVWorldRef(); + + // Support OrientedShape3D here. + // Note - BugId : 4363899 - APIs issue : OrientedShape3D's intersect needs view + // info. temp. fix use the primary view. + if (this instanceof OrientedShape3DRetained) { + Transform3D orientedTransform = ((OrientedShape3DRetained)this). + getOrientedTransform(getPrimaryViewIdx()); + localToVworld.mul(orientedTransform); + } + + Transform3D t3d = new Transform3D(); + t3d.invert(localToVworld); + PickShape newPS = pickShape.transform(t3d); + + // Note: For optimization - Should do a geobounds check of + // each geometry first. But this doesn't work for + // OrientedShape3D case... + int geomListSize = geometryList.size(); + GeometryRetained geometry; + + if (((flags & PickInfo.CLOSEST_INTERSECTION_POINT) == 0) && + ((flags & PickInfo.CLOSEST_DISTANCE) == 0) && + ((flags & PickInfo.CLOSEST_GEOM_INFO) == 0) && + ((flags & PickInfo.ALL_GEOM_INFO) == 0)) { + + for (int i=0; i < geomListSize; i++) { + geometry = geometryList.get(i); + if (geometry != null) { + if (geometry.mirrorGeometry != null) { + geometry = geometry.mirrorGeometry; + } + if (geometry.intersect(newPS, null, 0, null, null, 0)) { + return true; + } + } + } + } else { + double distance; + double minDist = Double.POSITIVE_INFINITY; + Point3d closestIPnt = new Point3d(); + Point3d iPnt = new Point3d(); + Point3d iPntVW = new Point3d(); + + for (int i=0; i < geomListSize; i++) { + geometry = geometryList.get(i); + if (geometry != null) { + if (geometry.mirrorGeometry != null) { + geometry = geometry.mirrorGeometry; + } + //if (geometry.intersect(newPS, intersectionInfo, flags, iPnt)) { + if(geometry.intersect(newPS, pickInfo, flags, iPnt, geometry, i)) { + + iPntVW.set(iPnt); + localToVworld.transform(iPntVW); + distance = pickShape.distance(iPntVW); + + if (minDist > distance) { + minDist = distance; + closestIPnt.set(iPnt); + } + } + } + } + + if (minDist < Double.POSITIVE_INFINITY) { + if ((flags & PickInfo.CLOSEST_DISTANCE) != 0) { + pickInfo.setClosestDistance(minDist); + } + if((flags & PickInfo.CLOSEST_INTERSECTION_POINT) != 0) { + pickInfo.setClosestIntersectionPoint(closestIPnt); + } + return true; + } + } + + return false; + + } + + + /** + * Check if the geometry component of this shape node under path + * intersects with the pickShape. + * This is an expensive method. It should only be called if and only + * if the path's bound intersects pickShape. + * @exception IllegalArgumentException if <code>path</code> is + * invalid. + */ + + boolean intersect(SceneGraphPath path, + PickShape pickShape, double[] dist) { + + int flags; + PickInfo pickInfo = new PickInfo(); + + Transform3D localToVworld = path.getTransform(); + if (localToVworld == null) { + throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained3")); + } + pickInfo.setLocalToVWorldRef( localToVworld); + //System.err.println("Shape3DRetained.intersect() : "); + if (dist == null) { + //System.err.println(" no dist request ...."); + return intersect(pickInfo, pickShape, 0); + } + + flags = PickInfo.CLOSEST_DISTANCE; + if (intersect(pickInfo, pickShape, flags)) { + dist[0] = pickInfo.getClosestDistance(); + return true; + } + + return false; + + } + + /** + * This sets the immedate mode context flag + */ + void setInImmCtx(boolean inCtx) { + inImmCtx = inCtx; + } + + /** + * This gets the immedate mode context flag + */ + boolean getInImmCtx() { + return (inImmCtx); + } + + /** + * This updates the mirror shape to reflect the state of the + * real shape3d. + */ + private void initMirrorShape3D(SetLiveState s, Shape3DRetained ms, int index) { + + // New 1.2.1 code + + ms.inBackgroundGroup = inBackgroundGroup; + ms.geometryBackground = geometryBackground; + ms.source = source; + ms.universe = universe; + // Has to be false. We have a instance of mirror for every link to the shape3d. + ms.inSharedGroup = false; + ms.locale = locale; + ms.parent = parent; + + // New 1.3.2 + // Used when user supplied their own bounds for transparency sorting + // GeometryAtom uses this to change how it computes the centroid + ms.boundsAutoCompute = boundsAutoCompute; + ms.localBounds = localBounds; + // End new 1.3.2 + + OrderedPath op = s.orderedPaths.get(index); + if (op.pathElements.size() == 0) { + ms.orderedPath = null; + } else { + ms.orderedPath = op; +/* + System.err.println("initMirrorShape3D ms.orderedPath "); + ms.orderedPath.printPath(); +*/ + } + + // all mirror shapes point to the same transformGroupRetained + // for the static transform + ms.staticTransform = staticTransform; + + + ms.appearanceOverrideEnable = appearanceOverrideEnable; + + ms.geometryList = geometryList; + + // Assign the parent of this mirror shape node + ms.sourceNode = this; + + if (this instanceof OrientedShape3DRetained) { + OrientedShape3DRetained os = (OrientedShape3DRetained)this; + OrientedShape3DRetained oms = (OrientedShape3DRetained)ms; + oms.initAlignmentMode(os.mode); + oms.initAlignmentAxis(os.axis); + oms.initRotationPoint(os.rotationPoint); + oms.initConstantScaleEnable(os.constantScale); + oms.initScale(os.scaleFactor); + } + + } + + void updateImmediateMirrorObject(Object[] objs) { + int component = ((Integer)objs[1]).intValue(); + + Shape3DRetained[] msArr = (Shape3DRetained[]) objs[2]; + int i; + if ((component & APPEARANCE_CHANGED) != 0) { + Object[] arg = (Object[])objs[3]; + int val = ((Integer)arg[1]).intValue(); + for ( i = msArr.length-1; i >=0; i--) { + msArr[i].appearance = (AppearanceRetained)arg[0]; + msArr[i].changedFrequent = val; + } + } + if ((component & APPEARANCEOVERRIDE_CHANGED) != 0) { + Object[] arg = (Object[])objs[3]; + int val = ((Integer)arg[1]).intValue(); + for ( i = msArr.length-1; i >=0; i--) { + msArr[i].appearanceOverrideEnable = ((Boolean)arg[0]).booleanValue(); + msArr[i].changedFrequent = val; + } + } + } + + /** + * Gets the bounding object of a node. + * @return the node's bounding object + */ + + @Override + Bounds getBounds() { + + if(boundsAutoCompute) { + // System.err.println("getBounds ---- localBounds is " + localBounds); + // Issue 514 : NPE in Wonderland : triggered in cached bounds computation + if (validCachedBounds) { + return (Bounds) cachedBounds.clone(); + } + + if(geometryList != null) { + BoundingBox bbox = new BoundingBox((Bounds) null); + GeometryRetained geometry; + for(int i=0; i<geometryList.size(); i++) { + geometry = geometryList.get(i); + if ((geometry != null) && + (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) { + geometry.computeBoundingBox(); + synchronized(geometry.geoBounds) { + bbox.combine(geometry.geoBounds); + } + } + } + return bbox; + + } else { + return null; + } + + } else { + return super.getBounds(); + } + } + + @Override + Bounds getEffectiveBounds() { + if(boundsAutoCompute) { + return getBounds(); + } + else { + return super.getEffectiveBounds(); + } + } + + + /** + * ONLY needed for SHAPE, MORPH, and LINK node type. + * Compute the combine bounds of bounds and its localBounds. + */ + @Override + void computeCombineBounds(Bounds bounds) { + + if(boundsAutoCompute) { + if(geometryList != null) { + GeometryRetained geometry; + BoundingBox bbox = null; + + if (staticTransform != null) { + bbox = new BoundingBox((BoundingBox) null); + } + + if (!VirtualUniverse.mc.cacheAutoComputedBounds) { + for(int i=0; i<geometryList.size(); i++) { + geometry = geometryList.get(i); + if ((geometry != null) && + (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) { + geometry.computeBoundingBox(); + // Should this be lock too ? ( MT safe ? ) + synchronized(geometry.geoBounds) { + if (staticTransform != null) { + bbox.set(geometry.geoBounds); + bbox.transform(staticTransform.transform); + bounds.combine(bbox); + } else { + bounds.combine(geometry.geoBounds); + } + } + } + } + } else { + // Issue 514 : NPE in Wonderland : triggered in cached bounds computation + if (!validCachedBounds) { + validCachedBounds = true; + cachedBounds = new BoundingBox((BoundingBox) null); + + for(int i=0; i<geometryList.size(); i++) { + geometry = geometryList.get(i); + if ((geometry != null) && + (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) { + geometry.computeBoundingBox(); + // Should this be lock too ? ( MT safe ? ) + synchronized(geometry.geoBounds) { + if (staticTransform != null) { + bbox.set(geometry.geoBounds); + bbox.transform(staticTransform.transform); + cachedBounds.combine(bbox); + } else { + cachedBounds.combine(geometry.geoBounds); + } + } + } + } + } + bounds.combine(cachedBounds); + } + } + } else { + + // Should this be lock too ? ( MT safe ? ) + synchronized(localBounds) { + bounds.combine(localBounds); + } + } + } + + /** + * assign a name to this node when it is made live. + */ + + @Override + void setLive(SetLiveState s) { + doSetLive(s); + markAsLive(); + } + + @Override + void doSetLive(SetLiveState s) { + // System.err.println("S3DRetained : setLive " + s); + Shape3DRetained shape; + GeometryRetained geometry; + int i, j, k, gaCnt; + ArrayList<Shape3DRetained> msList = new ArrayList<Shape3DRetained>(); + + super.doSetLive(s); + + nodeId = universe.getNodeId(); + + + if (inSharedGroup) { + for (i=0; i<s.keys.length; i++) { + if (this instanceof OrientedShape3DRetained) { + shape = new OrientedShape3DRetained(); + } else { + shape = new Shape3DRetained(); + } + shape.key = s.keys[i]; + shape.localToVworld = new Transform3D[1][]; + shape.localToVworldIndex = new int[1][]; + + j = s.keys[i].equals(localToVworldKeys, 0, + localToVworldKeys.length); + /* + System.err.print("s.keys[i] = "+s.keys[i]+" j = "+j); + if(j < 0) { + System.err.println("Shape3dRetained : Can't find hashKey"); + } + */ + shape.localToVworld[0] = localToVworld[j]; + shape.localToVworldIndex[0] = localToVworldIndex[j]; + shape.branchGroupPath = branchGroupPaths.get(j); + shape.isPickable = s.pickable[i]; + shape.isCollidable = s.collidable[i]; + + initMirrorShape3D(s, shape, j); + + if (s.switchTargets != null && + s.switchTargets[i] != null) { + s.switchTargets[i].addNode(shape, Targets.GEO_TARGETS); + shape.closestSwitchParent = s.closestSwitchParents[i]; + shape.closestSwitchIndex = s.closestSwitchIndices[i]; + } + shape.switchState = s.switchStates.get(j); + + // Add any scoped lights to the mirror shape + if (s.lights != null) { + ArrayList<LightRetained> l = s.lights.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addLight(l.get(m)); + } + } + } + + // Add any scoped fog + if (s.fogs != null) { + ArrayList<FogRetained> l = s.fogs.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addFog(l.get(m)); + } + } + } + + // Add any scoped modelClip + if (s.modelClips != null) { + ArrayList<ModelClipRetained> l = s.modelClips.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addModelClip(l.get(m)); + } + } + } + + // Add any scoped alt app + if (s.altAppearances != null) { + ArrayList<AlternateAppearanceRetained> l = s.altAppearances.get(j); + if (l != null) { + for (int m = 0; m < l.size(); m++) { + shape.addAltApp(l.get(m)); + } + } + } + synchronized(mirrorShape3D) { + mirrorShape3D.add(j,shape); + } + + msList.add(shape); + if (s.viewLists != null) { + shape.viewList = s.viewLists.get(j); + } else { + shape.viewList = null; + } + } + } else { + if (this instanceof OrientedShape3DRetained) { + shape = new OrientedShape3DRetained(); + } else { + shape = new Shape3DRetained(); + } + + shape.localToVworld = new Transform3D[1][]; + shape.localToVworldIndex = new int[1][]; + shape.localToVworld[0] = localToVworld[0]; + shape.localToVworldIndex[0] = localToVworldIndex[0]; + shape.branchGroupPath = branchGroupPaths.get(0); + shape.isPickable = s.pickable[0]; + shape.isCollidable = s.collidable[0]; + initMirrorShape3D(s, shape, 0); + + // Add any scoped lights to the mirror shape + if (s.lights != null) { + ArrayList<LightRetained> l = s.lights.get(0); + for (i = 0; i < l.size(); i++) { + shape.addLight(l.get(i)); + } + } + + // Add any scoped fog + if (s.fogs != null) { + ArrayList<FogRetained> l = s.fogs.get(0); + for (i = 0; i < l.size(); i++) { + shape.addFog(l.get(i)); + } + } + + // Add any scoped modelClip + if (s.modelClips != null) { + ArrayList<ModelClipRetained> l = s.modelClips.get(0); + for (i = 0; i < l.size(); i++) { + shape.addModelClip(l.get(i)); + } + + } + + // Add any scoped alt app + if (s.altAppearances != null) { + ArrayList<AlternateAppearanceRetained> l = s.altAppearances.get(0); + for (i = 0; i < l.size(); i++) { + shape.addAltApp(l.get(i)); + } + } + synchronized(mirrorShape3D) { + mirrorShape3D.add(shape); + } + + msList.add(shape); + if (s.viewLists != null) + shape.viewList = s.viewLists.get(0); + else + shape.viewList = null; + + if (s.switchTargets != null && + s.switchTargets[0] != null) { + s.switchTargets[0].addNode(shape, Targets.GEO_TARGETS); + shape.closestSwitchParent = s.closestSwitchParents[0]; + shape.closestSwitchIndex = s.closestSwitchIndices[0]; + } + shape.switchState = s.switchStates.get(0); + } + + for (k = 0; k < msList.size(); k++) { + Shape3DRetained sh = msList.get(k); + + if (appearance != null) { + synchronized(appearance.liveStateLock) { + if (k == 0) { // Do only first time + appearance.setLive(inBackgroundGroup, s.refCount); + appearance.initMirrorObject(); + if (appearance.renderingAttributes != null) + visible = appearance.renderingAttributes.visible; + } + sh.appearance = (AppearanceRetained)appearance.mirror; + appearance.addAMirrorUser(sh); + + } + } + else { + sh.appearance = null; + } + + if (geometryList != null) { + for(gaCnt=0; gaCnt<geometryList.size(); gaCnt++) { + geometry = geometryList.get(gaCnt); + if(geometry != null) { + synchronized(geometry.liveStateLock) { + if (k == 0) { // Do only first time + geometry.setLive(inBackgroundGroup, s.refCount); + } + geometry.addUser(sh); + } + } + } + + } + + // after the geometry has been setLived and bounds computed + if (k== 0 && boundsAutoCompute) { // Do only once + // user may call setBounds with a bounds other than boundingBox + if (! (localBounds instanceof BoundingBox)) { + localBounds = new BoundingBox((BoundingBox) null); + } + getCombineBounds((BoundingBox)localBounds); + + } + // Assign GAtom and set the bounds if we are not using switch + initializeGAtom(sh); + + GeometryAtom ga = getGeomAtom(sh); + + // Add the geometry atom for this shape to the nodeList + s.nodeList.add(ga); + + if (s.transformTargets != null && + s.transformTargets[k] != null) { + // Add the geometry atom for this shape to the transformTargets + + s.transformTargets[k].addNode(ga, Targets.GEO_TARGETS); + } + } + + s.notifyThreads |= (J3dThread.UPDATE_GEOMETRY | + J3dThread.UPDATE_TRANSFORM | + J3dThread.UPDATE_RENDER | + J3dThread.UPDATE_RENDERING_ENVIRONMENT); + + } + + /** + * This clears all references in a mirror shape + */ + // This is call in RenderingEnvironmentStructure.removeNode() because that is the + // last point that will reference this ms. + // called on the mirror shape .. + void clearMirrorShape() { + int i; + + source = null; + sourceNode = null; + parent = null; + + if (otherAppearance != null) { + otherAppearance.sgApp.removeAMirrorUser(this); + otherAppearance = null; + } + + appearance = null; + + branchGroupPath = null; + isPickable = true; + isCollidable = true; + branchGroupPath = null; + // No locking needed. Owner, s3dR, has already been destory. + // DO NOT clear geometryList, ie. geometryList.clear(). + // It is referred by the source s3DRetained. + geometryList = null; + + // Clear the mirror scoping info + // Remove all the fogs + for (i = 0; i < numfogs; i++) + fogs[i] = null; + numfogs = 0; + + // Remove all the modelClips + for (i = 0; i < numModelClips; i++) + modelClips[i] = null; + numModelClips = 0; + + // Remove all the lights + for (i = 0; i < numlights; i++) + lights[i] = null; + numlights = 0; + + // Remove all the al app + for (i = 0; i < numAltApps; i++) + altApps[i] = null; + numAltApps = 0; + + viewList = null; + + } + + /** + * assign a name to this node when it is made live. + */ + @Override + void clearLive(SetLiveState s) { + + //System.err.println("S3DRetained : clearLive " + s); + + int i, j, gaCnt; + GeometryRetained geometry; + ArrayList<Shape3DRetained> msList = new ArrayList<Shape3DRetained>(); + + super.clearLive(s); + + + + if (inSharedGroup) { + synchronized(mirrorShape3D) { + Shape3DRetained[] shapes = mirrorShape3D.toArray(new Shape3DRetained[mirrorShape3D.size()]); + for (i=0; i<s.keys.length; i++) { + for (j=0; j<shapes.length; j++) { + Shape3DRetained shape = shapes[j]; + if (shape.key.equals(s.keys[i])) { + mirrorShape3D.remove(mirrorShape3D.indexOf(shape)); + if (s.switchTargets != null && + s.switchTargets[i] != null) { + s.switchTargets[i].addNode( + shape, Targets.GEO_TARGETS); + } + msList.add(shape); + GeometryAtom ga = getGeomAtom(shape); + + // Add the geometry atom for this shape to the nodeList + s.nodeList.add(ga); + if (s.transformTargets != null && + s.transformTargets[i] != null) { + s.transformTargets[i].addNode(ga, Targets.GEO_TARGETS); + } + } + } + } + } + } else { + // Only entry 0 is valid + Shape3DRetained shape = mirrorShape3D.get(0); + synchronized(mirrorShape3D) { + mirrorShape3D.remove(0); + } + + if (s.switchTargets != null && + s.switchTargets[0] != null) { + s.switchTargets[0].addNode(shape, Targets.GEO_TARGETS); + } + + + msList.add(shape); + + GeometryAtom ga = getGeomAtom(shape); + + // Add the geometry atom for this shape to the nodeList + s.nodeList.add(ga); + if (s.transformTargets != null && + s.transformTargets[0] != null) { + s.transformTargets[0].addNode(ga, Targets.GEO_TARGETS); + } + } + + + for (int k = 0; k < msList.size(); k++) { + Shape3DRetained sh = msList.get(k); + if (appearance != null) { + synchronized(appearance.liveStateLock) { + if (k == 0) { + appearance.clearLive(s.refCount); + } + appearance.removeAMirrorUser(sh); + } + } + if (geometryList != null) { + for(gaCnt=0; gaCnt<geometryList.size(); gaCnt++) { + geometry = geometryList.get(gaCnt); + if(geometry != null) { + synchronized(geometry.liveStateLock) { + if (k == 0) { + geometry.clearLive(s.refCount); + } + geometry.removeUser(sh); + } + } + } + } + } + + s.notifyThreads |= (J3dThread.UPDATE_GEOMETRY | + J3dThread.UPDATE_TRANSFORM | + // This is used to clear the scope info + // of all the mirror shapes + J3dThread.UPDATE_RENDERING_ENVIRONMENT | + J3dThread.UPDATE_RENDER); + + if (!source.isLive()) { + // Clear the mirror scoping info + // Remove all the fogs + for (i = 0; i < numfogs; i++) + fogs[i] = null; + numfogs = 0; + + // Remove all the modelClips + for (i = 0; i < numModelClips; i++) + modelClips[i] = null; + numModelClips = 0; + + // Remove all the lights + for (i = 0; i < numlights; i++) + lights[i] = null; + numlights = 0; + + // Remove all the al app + for (i = 0; i < numAltApps; i++) + altApps[i] = null; + numAltApps = 0; + } + } + + @Override + boolean isStatic() { + if (source.getCapability(Shape3D.ALLOW_APPEARANCE_WRITE) || + source.getCapability(Shape3D.ALLOW_GEOMETRY_WRITE) || + source.getCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE)) { + return false; + } else { + return true; + } + } + + boolean staticXformCanBeApplied() { + + // static xform can be applied if + // . shape is not pickable or collidable + // . geometry is not being shared by more than one shape nodes + // . geometry will be put in display list + // . geometry is not readable + + // no static xform if shape is pickable or collidable because + // otherwise the static xform will have to be applied to the + // currentLocalToVworld in the intersect test, it will then + // be more costly and really beat the purpose of eliminating + // the static transform group + if (isPickable || isCollidable || + source.getCapability(Shape3D.ALLOW_PICKABLE_WRITE) || + source.getCapability(Shape3D.ALLOW_COLLIDABLE_WRITE)) { + return false; + } + + if (appearance != null && + (appearance.transparencyAttributes != null && appearance.transparencyAttributes.transparencyMode != TransparencyAttributes.NONE)) + return false; + + GeometryRetained geo; + boolean alphaEditable; + + for (int i=0; i<geometryList.size(); i++) { + geo = geometryList.get(i); + if (geo != null) { + if (geo.refCnt > 1) { + return false; + } + alphaEditable = isAlphaEditable(geo); + if (geo instanceof GeometryArrayRetained) { + geo.isEditable = !((GeometryArrayRetained)geo).isWriteStatic(); + + // TODO: for now if vertex data can be returned, then + // don't apply static transform + if (geo.source.getCapability( + GeometryArray.ALLOW_COORDINATE_READ) || + geo.source.getCapability( + GeometryArray.ALLOW_NORMAL_READ)) + return false; + + } + + if (!geo.canBeInDisplayList(alphaEditable)) { + return false; + } + } + } + return true; + } + + + @Override + void compile(CompileState compState) { + AppearanceRetained newApp; + + super.compile(compState); + + if (isStatic() && staticXformCanBeApplied()) { + mergeFlag = SceneGraphObjectRetained.MERGE; + if (J3dDebug.devPhase && J3dDebug.debug) { + compState.numShapesWStaticTG++; + } + } else + { + mergeFlag = SceneGraphObjectRetained.DONT_MERGE; + compState.keepTG = true; + } + + if (J3dDebug.devPhase && J3dDebug.debug) { + compState.numShapes++; + } + + if (appearance != null) { + appearance.compile(compState); + // Non-static apperanace can still be compiled, since in compile + // state we will be grouping all shapes that have same appearance + // so, when the appearance changes, all the shapes will be affected + // For non-static appearances, we don't get an equivalent appearance + // from the compile state + if (appearance.isStatic()) { + newApp = compState.getAppearance(appearance); + appearance = newApp; + } + } + + for (int i = 0; i < geometryList.size(); i++) { + GeometryRetained geo = geometryList.get(i); + if (geo != null) + geo.compile(compState); + } + + } + + @Override + void merge(CompileState compState) { + + + if (mergeFlag == SceneGraphObjectRetained.DONT_MERGE) { + + // no need to save the staticTransform here + + TransformGroupRetained saveStaticTransform = + compState.staticTransform; + compState.staticTransform = null; + super.merge(compState); + compState.staticTransform = saveStaticTransform; + } else { + super.merge(compState); + } + + if (shapeIsMergeable(compState)) { + compState.addShape(this); + } + } + + + boolean shapeIsMergeable(CompileState compState) { + boolean mergeable = true; + int i; + + GeometryRetained geometry = null; + int index = 0; + i = 0; + /* + if (isPickable) + return false; + */ + + // For now, don't merge if the shape has static transform + if (staticTransform != null) + return false; + + // If this shape's to be immediate parent is orderedGroup or a switchNode + // this shape is not mergerable + if (parent instanceof OrderedGroupRetained || + parent instanceof SwitchRetained) + return false; + + // Get the first geometry that is non-null + while (geometry == null && index < geometryList.size()) { + geometry = geometryList.get(index); + index++; + } + + if (!(geometry instanceof GeometryArrayRetained)) { + return false; + } + + GeometryArrayRetained firstGeo = (GeometryArrayRetained) geometry; + + for(i=index; (i<geometryList.size() && mergeable); i++) { + geometry = geometryList.get(i); + if (geometry != null) { + GeometryArrayRetained geo = (GeometryArrayRetained)geometry; + + if (! geo.isWriteStatic()) + mergeable = false; + + if (geo.vertexFormat != firstGeo.vertexFormat) + mergeable = false; + + + } + } + + // For now, turn off lots of capability bits + if (source.getCapability(Shape3D.ALLOW_COLLISION_BOUNDS_WRITE) || + source.getCapability(Shape3D.ALLOW_APPEARANCE_WRITE) || + source.getCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) || + source.getCapability(Shape3D.ALLOW_AUTO_COMPUTE_BOUNDS_WRITE) || + source.getCapability(Shape3D.ALLOW_BOUNDS_WRITE) || + source.getCapability(Shape3D.ALLOW_COLLIDABLE_WRITE) || + source.getCapability(Shape3D.ALLOW_PICKABLE_WRITE) || + source.getCapability(Shape3D.ALLOW_GEOMETRY_WRITE)) { + mergeable = false; + } + + return mergeable; + + } + + + @Override + void getMirrorObjects( ArrayList list, HashKey k) { + Shape3DRetained ms; + if (inSharedGroup) { + if (k.count == 0) { + // System.err.println("===> CAN NEVER BE TRUE"); + return; + } + else { + ms = getMirrorShape(k); + } + } + else { + ms = mirrorShape3D.get(0); + } + + list.add(getGeomAtom(ms)); + + } + + + // Called on the mirror Object + void addLight(LightRetained light) { + LightRetained[] newlights; + int i; + + if (lights == null) { + lights = new LightRetained[10]; + } + else if (lights.length == numlights) { + newlights = new LightRetained[numlights*2]; + for (i=0; i<numlights; i++) { + newlights[i] = lights[i]; + } + lights = newlights; + } + lights[numlights] = light; + numlights++; + } + + // called on the mirror object + void removeLight(LightRetained light) { + int i; + + for (i=0; i<numlights; i++) { + if (lights[i] == light) { + lights[i] = null; + break; + } + } + + // Shift everyone down one. + for (i++; i<numlights; i++) { + lights[i-1] = lights[i]; + } + numlights--; + } + + // Called on the mirror object + void addFog(FogRetained fog) { + FogRetained[] newfogs; + int i; + + if (fogs == null) { + fogs = new FogRetained[10]; + } + else if (fogs.length == numfogs) { + newfogs = new FogRetained[numfogs*2]; + for (i=0; i<numfogs; i++) { + newfogs[i] = fogs[i]; + } + fogs = newfogs; + } + fogs[numfogs] = fog; + numfogs++; + } + + // called on the mirror object + void removeFog(FogRetained fog) { + int i; + + for (i=0; i<numfogs; i++) { + if (fogs[i] == fog) { + fogs[i] = null; + break; + } + } + + // Shift everyone down one. + for (i++; i<numfogs; i++) { + fogs[i-1] = fogs[i]; + } + numfogs--; + + } + + // Called on the mirror object + void addModelClip(ModelClipRetained modelClip) { + ModelClipRetained[] newModelClips; + int i; + + + if (modelClips == null) { + modelClips = new ModelClipRetained[10]; + } + else if (modelClips.length == numModelClips) { + newModelClips = new ModelClipRetained[numModelClips*2]; + for (i=0; i<numModelClips; i++) { + newModelClips[i] = modelClips[i]; + } + modelClips = newModelClips; + } + modelClips[numModelClips] = modelClip; + numModelClips++; + } + + // called on the mirror object + void removeModelClip(ModelClipRetained modelClip) { + int i; + + for (i=0; i<numModelClips; i++) { + if (modelClips[i] == modelClip) { + modelClips[i] = null; + break; + } + } + + // Shift everyone down one. + for (i++; i<numModelClips; i++) { + modelClips[i-1] = modelClips[i]; + } + numModelClips--; + + } + + // Called on the mirror object + void addAltApp(AlternateAppearanceRetained aApp) { + AlternateAppearanceRetained[] newAltApps; + int i; + if (altApps == null) { + altApps = new AlternateAppearanceRetained[10]; + } + else if (altApps.length == numAltApps) { + newAltApps = new AlternateAppearanceRetained[numAltApps*2]; + for (i=0; i<numAltApps; i++) { + newAltApps[i] = altApps[i]; + } + altApps = newAltApps; + } + altApps[numAltApps] = aApp; + numAltApps++; + } + + // called on the mirror object + void removeAltApp(AlternateAppearanceRetained aApp) { + int i; + + for (i=0; i<numAltApps; i++) { + if (altApps[i] == aApp) { + altApps[i] = null; + break; + } + } + + // Shift everyone down one. + for (i++; i<numAltApps; i++) { + altApps[i-1] = altApps[i]; + } + numAltApps--; + + } + + + + @Override + void updatePickable(HashKey keys[], boolean pick[]) { + super.updatePickable(keys, pick); + Shape3DRetained shape; + + if (!inSharedGroup) { + shape = mirrorShape3D.get(0); + shape.isPickable = pick[0]; + } else { + int size = mirrorShape3D.size(); + for (int j=0; j< keys.length; j++) { + for (int i=0; i < size; i++) { + shape = mirrorShape3D.get(i); + if (keys[j].equals(shape.key)) { + shape.isPickable = pick[j]; + break; + } + + } + } + } + } + + + @Override + void updateCollidable(HashKey keys[], boolean collide[]) { + super.updateCollidable(keys, collide); + Shape3DRetained shape; + + if (!inSharedGroup) { + shape = mirrorShape3D.get(0); + shape.isCollidable = collide[0]; + } else { + int size = mirrorShape3D.size(); + for (int j=0; j< keys.length; j++) { + for (int i=0; i < size; i++) { + shape = mirrorShape3D.get(i); + if (keys[j].equals(shape.key)) { + shape.isCollidable = collide[j]; + break; + } + + } + } + } + } + + + + + // New 1.2.1 code .... + + // Remove the old geometry atoms and reInsert + // the new geometry atoms and update the transform + // target list + + private void sendDataChangedMessage( GeometryRetained newGeom ) { + + int i, j, gaCnt; + GeometryAtom[] newGAArray = null; + GeometryAtom[] oldGAArray = null; + int geometryCnt = 0; + GeometryRetained geometry = null; + + int s3dMSize = mirrorShape3D.size(); + + if(s3dMSize < 1) + return; + + Shape3DRetained mS3d = mirrorShape3D.get(0); + + mS3d.mirrorShape3DLock.writeLock(); + + GeometryAtom oldGA = mS3d.geomAtom; + + GeometryAtom newGA = new GeometryAtom(); + + if(newGeom != null) { + newGeom.addUser(mS3d); + } + + int gSize = geometryList.size(); + + for(i=0; i<gSize; i++) { + geometry = geometryList.get(i); + if(geometry != null) { + newGA.geoType = geometry.geoType; + newGA.alphaEditable = mS3d.isAlphaEditable(geometry); + break; + } + } + + if((geometry != null) && + (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D)) { + + for(i = 0; i<gSize; i++) { + geometry = geometryList.get(i); + if(geometry != null) { + Text3DRetained tempT3d = (Text3DRetained)geometry; + geometryCnt += tempT3d.numChars; + } + else { + // This is slightly wasteful, but not quite worth to optimize yet. + geometryCnt++; + } + } + newGA.geometryArray = new GeometryRetained[geometryCnt]; + newGA.lastLocalTransformArray = new Transform3D[geometryCnt]; + // Reset geometryCnt; + geometryCnt = 0; + + } + else { + newGA.geometryArray = new GeometryRetained[gSize]; + } + + newGA.locale = mS3d.locale; + newGA.visible = visible; + newGA.source = mS3d; + + + for(gaCnt = 0; gaCnt<gSize; gaCnt++) { + geometry = geometryList.get(gaCnt); + if(geometry == null) { + newGA.geometryArray[geometryCnt++] = null; + } + else { + if (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D) { + Text3DRetained t = (Text3DRetained)geometry; + GeometryRetained geo; + for (i=0; i<t.numChars; i++, geometryCnt++) { + geo = t.geometryList[i]; + if (geo!= null) { + newGA.geometryArray[geometryCnt] = geo; + newGA.lastLocalTransformArray[geometryCnt] = + t.charTransforms[i]; + + } else { + newGA.geometryArray[geometryCnt] = null; + newGA.lastLocalTransformArray[geometryCnt] = null; + } + + } + + } else { + newGA.geometryArray[geometryCnt++] = geometry; + } + } + } + + oldGAArray = new GeometryAtom[s3dMSize]; + newGAArray = new GeometryAtom[s3dMSize]; + oldGAArray[0] = oldGA; + newGAArray[0] = newGA; + + mS3d.geomAtom = newGA; + mS3d.mirrorShape3DLock.writeUnlock(); + + // ..... clone the rest of mirrorS3D's GA with the above newGA, but modify + // its source. + + for (i = 1; i < s3dMSize; i++) { + mS3d = mirrorShape3D.get(i); + mS3d.mirrorShape3DLock.writeLock(); + oldGA = mS3d.geomAtom; + newGA = new GeometryAtom(); + + if(newGeom != null) { + newGeom.addUser(mS3d); + } + + newGA.geoType = newGAArray[0].geoType; + newGA.locale = mS3d.locale; + newGA.visible = visible; + newGA.source = mS3d; + newGA.alphaEditable = newGAArray[0].alphaEditable; + + newGA.geometryArray = new GeometryRetained[newGAArray[0].geometryArray.length]; + for(j=0; j<newGA.geometryArray.length; j++) { + newGA.geometryArray[j] = newGAArray[0].geometryArray[j]; + } + + oldGAArray[i] = oldGA; + newGAArray[i] = newGA; + + mS3d.geomAtom = newGA; + mS3d.mirrorShape3DLock.writeUnlock(); + } + + TargetsInterface ti = + ((GroupRetained)parent).getClosestTargetsInterface( + TargetsInterface.TRANSFORM_TARGETS); + CachedTargets[] newCtArr = null; + + if (ti != null) { + CachedTargets ct; + newCtArr = new CachedTargets[s3dMSize]; + + for (i=0; i<s3dMSize; i++) { + + ct = ti.getCachedTargets( + TargetsInterface.TRANSFORM_TARGETS, i, -1); + if (ct != null) { + newCtArr[i] = new CachedTargets(); + newCtArr[i].copy(ct); + newCtArr[i].replace(oldGAArray[i], newGAArray[i], + Targets.GEO_TARGETS); + } else { + newCtArr[i] = null; + } + } + ti.resetCachedTargets(TargetsInterface.TRANSFORM_TARGETS, + newCtArr, -1); + } + + + J3dMessage changeMessage = new J3dMessage(); + changeMessage.type = J3dMessage.SHAPE3D_CHANGED; + // Who to send this message to ? + changeMessage.threads = J3dThread.UPDATE_RENDER | + J3dThread.UPDATE_TRANSFORM | + J3dThread.UPDATE_GEOMETRY; + changeMessage.universe = universe; + changeMessage.args[0] = this; + changeMessage.args[1] = new Integer(GEOMETRY_CHANGED); + changeMessage.args[2] = oldGAArray; + changeMessage.args[3] = newGAArray; + if (ti != null) { + changeMessage.args[4] = ti; + changeMessage.args[5] = newCtArr; + } + if (boundsAutoCompute) { + getCombineBounds((BoundingBox)localBounds); + } + VirtualUniverse.mc.processMessage(changeMessage); + + } + + + // ********** End of New 1.2.1 code .... + + + + + + Shape3DRetained getMirrorShape(SceneGraphPath path) { + if (!inSharedGroup) { + return mirrorShape3D.get(0); + } + HashKey key = new HashKey(""); + path.getHashKey(key); + return getMirrorShape(key); + } + + Shape3DRetained getMirrorShape(HashKey key) { + if (key == null) { + return mirrorShape3D.get(0); + } else { + int i = key.equals(localToVworldKeys, 0, localToVworldKeys.length); + + if (i>=0) { + return mirrorShape3D.get(i); + } + } + // Not possible + throw new RuntimeException("Shape3DRetained: MirrorShape Not found!"); + } + + @Override + void setBoundsAutoCompute(boolean autoCompute) { + GeometryRetained geometry; + if (autoCompute != boundsAutoCompute) { + if (autoCompute) { + // localBounds may not have been set to bbox + localBounds = new BoundingBox((BoundingBox) null); + if (source.isLive() && geometryList != null) { + int size = geometryList.size()*mirrorShape3D.size(); + for (int i=0; i<size; i++) { + geometry = geometryList.get(i); + geometry.incrComputeGeoBounds(); + } + } + + getCombineBounds((BoundingBox)localBounds); + } + else { + if (source.isLive() && geometryList != null) { + int size = geometryList.size()*mirrorShape3D.size(); + for (int i=0; i<size; i++) { + geometry = geometryList.get(i); + geometry.decrComputeGeoBounds(); + } + + } + } + super.setBoundsAutoCompute(autoCompute); + if (source.isLive()) { + J3dMessage message = new J3dMessage(); + message.type = J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED; + message.threads = J3dThread.UPDATE_TRANSFORM | + J3dThread.UPDATE_GEOMETRY | + J3dThread.UPDATE_RENDER; + message.universe = universe; + message.args[0] = getGeomAtomsArray(mirrorShape3D); + // no need to clone localBounds + message.args[1] = localBounds; + VirtualUniverse.mc.processMessage(message); + } + } + } + // This method is called when coordinates of a geometry in the geometrylist + // changed and autoBoundsCompute is true + + @Override + void updateBounds() { + localBounds = new BoundingBox((BoundingBox) null); + getCombineBounds((BoundingBox)localBounds); + synchronized(mirrorShape3D) { + if (source.isLive()) { + J3dMessage message = new J3dMessage(); + message.type = J3dMessage.BOUNDS_AUTO_COMPUTE_CHANGED; + message.threads = J3dThread.UPDATE_TRANSFORM | + J3dThread.UPDATE_GEOMETRY | + J3dThread.UPDATE_RENDER; + message.universe = universe; + message.args[0] = getGeomAtomsArray(mirrorShape3D); + // no need to clone localBounds + message.args[1] = localBounds; + VirtualUniverse.mc.processMessage(message); + } + } + } + + boolean allowIntersect() { + GeometryRetained ga = null; + + for(int i=0; i<geometryList.size(); i++) { + ga = geometryList.get(i); + if(ga != null) + if (!ga.source.getCapability(Geometry.ALLOW_INTERSECT)) { + return false; + } + } + return true; + } + boolean intersectGeometryList(Shape3DRetained otherShape) { + GeometryRetained geom1, geom2; + ArrayList<GeometryRetained> gaList = otherShape.geometryList; + int gaSize = gaList.size(); + Transform3D otherLocalToVworld = otherShape.getCurrentLocalToVworld(); + Transform3D thisLocalToVworld = getCurrentLocalToVworld(); + int primaryViewIdx = -1; + + + if (this instanceof OrientedShape3DRetained) { + primaryViewIdx = getPrimaryViewIdx(); + thisLocalToVworld.mul(((OrientedShape3DRetained)this). + getOrientedTransform(primaryViewIdx)); + } + + if (otherShape instanceof OrientedShape3DRetained) { + if (primaryViewIdx < 0) { + primaryViewIdx = getPrimaryViewIdx(); + } + otherLocalToVworld.mul(((OrientedShape3DRetained)otherShape). + getOrientedTransform(primaryViewIdx)); + } + + for (int i=geometryList.size()-1; i >=0; i--) { + geom1 = geometryList.get(i); + if (geom1 != null) { + for (int j=gaSize-1; j >=0; j--) { + geom2 = gaList.get(j); + if ((geom2 != null) && + geom1.intersect(thisLocalToVworld, + otherLocalToVworld, geom2)) { + return true; + } + } + } + } + + return false; + } + + boolean intersectGeometryList(Transform3D thisLocalToVworld, Bounds targetBound) { + + GeometryRetained geometry; + + if (this instanceof OrientedShape3DRetained) { + Transform3D orientedTransform = + ((OrientedShape3DRetained)this). + getOrientedTransform(getPrimaryViewIdx()); + thisLocalToVworld.mul(orientedTransform); + } + + for (int i=geometryList.size() - 1; i >=0; i--) { + geometry = geometryList.get(i); + if ((geometry != null) && + geometry.intersect(thisLocalToVworld, targetBound)) { + return true; + } + } + + return false; + + } + + + /** + * This initialize the mirror shape to reflect the state of the + * real Morph. + */ + void initMirrorShape3D(SetLiveState s, MorphRetained morph, int index) { + + GeometryRetained geometry; + + universe = morph.universe; + inSharedGroup = morph.inSharedGroup; + inBackgroundGroup = morph.inBackgroundGroup; + geometryBackground = morph.geometryBackground; + parent = morph.parent; + locale = morph.locale; + + OrderedPath op = s.orderedPaths.get(index); + if (op.pathElements.size() == 0) { + orderedPath = null; + } else { + orderedPath = op; + } + + staticTransform = morph.staticTransform; + if (morph.boundsAutoCompute) { + localBounds.set(morph.localBounds); + } + bounds = localBounds; + vwcBounds = new BoundingBox((BoundingBox) null); + vwcBounds.transform(bounds, getCurrentLocalToVworld(0)); + + if (morph.collisionBound == null) { + collisionBound = null; + collisionVwcBound = vwcBounds; + } else { + collisionBound = morph.collisionBound; + collisionVwcBound = (Bounds)collisionBound.clone(); + collisionVwcBound.transform(getCurrentLocalToVworld(0)); + } + + appearanceOverrideEnable = morph.appearanceOverrideEnable; + + // mga is the final geometry we're interested. + geometryList = new ArrayList<GeometryRetained>(1); + geometryList.add((GeometryArrayRetained)morph.morphedGeometryArray.retained); + + GeometryAtom gAtom = new GeometryAtom(); + gAtom.geometryArray = new GeometryRetained[1]; + + gAtom.locale = locale; + gAtom.visible = morph.visible; + gAtom.source = this; + + geometry = geometryList.get(0); + + if(geometry ==null) { + gAtom.geometryArray[0] = null; + } else { + gAtom.geometryArray[0] = (GeometryArrayRetained)morph. + morphedGeometryArray.retained; + gAtom.geoType = gAtom.geometryArray[0].geoType; + } + geomAtom = gAtom; + + // Assign the parent of this mirror shape node + sourceNode = morph; + } + + // geometries in morph object is modified, update the geometry + // list in the mirror shapes and the geometry array in the geometry atom + + void setMorphGeometry(Geometry geometry, ArrayList mirrorShapes) { + GeometryAtom oldGA, newGA; + Shape3DRetained ms; + int nMirrorShapes = mirrorShapes.size(); + int i; + + GeometryAtom oldGAArray[] = new GeometryAtom[nMirrorShapes]; + GeometryAtom newGAArray[] = new GeometryAtom[nMirrorShapes]; + + + for (i = 0; i < nMirrorShapes; i++) { + ms = (Shape3DRetained) mirrorShapes.get(i); + + oldGA = Shape3DRetained.getGeomAtom(ms); + + ms.geometryList = new ArrayList<GeometryRetained>(1); + ms.geometryList.add((GeometryArrayRetained)geometry.retained); + + newGA = new GeometryAtom(); + newGA.geometryArray = new GeometryRetained[1]; + + if (geometry ==null) { + newGA.geometryArray[0] = null; + } else { + newGA.geometryArray[0] = + (GeometryArrayRetained)geometry.retained; + newGA.geoType = newGA.geometryArray[0].geoType; + } + + newGA.locale = locale; + newGA.visible = oldGA.visible; + newGA.source = this; + + oldGAArray[i] = oldGA; + newGAArray[i] = newGA; + + Shape3DRetained.setGeomAtom(ms, newGA); + } + + TargetsInterface ti = + ((GroupRetained)parent).getClosestTargetsInterface( + TargetsInterface.TRANSFORM_TARGETS); + CachedTargets[] newCtArr = null; + + if (ti != null) { + CachedTargets ct; + newCtArr = new CachedTargets[nMirrorShapes]; + + for (i=0; i<nMirrorShapes; i++) { + + ct = ti.getCachedTargets( + TargetsInterface.TRANSFORM_TARGETS, i, -1); + if (ct != null) { + newCtArr[i] = new CachedTargets(); + newCtArr[i].copy(ct); + newCtArr[i].replace(oldGAArray[i], newGAArray[i], + Targets.GEO_TARGETS); + } else { + newCtArr[i] = null; + } + } + } + + // send a Shape GEOMETRY_CHANGED message for all geometry atoms + + J3dMessage changeMessage = new J3dMessage(); + changeMessage.type = J3dMessage.SHAPE3D_CHANGED; + changeMessage.threads = J3dThread.UPDATE_RENDER | + J3dThread.UPDATE_TRANSFORM | + J3dThread.UPDATE_GEOMETRY; + changeMessage.universe = universe; + changeMessage.args[0] = this; + changeMessage.args[1] = new Integer(GEOMETRY_CHANGED); + changeMessage.args[2] = oldGAArray; + changeMessage.args[3] = newGAArray; + if (ti != null) { + changeMessage.args[4] = ti; + changeMessage.args[5] = newCtArr; + } + VirtualUniverse.mc.processMessage(changeMessage); + } + + + /** + * Return an array of geometry atoms belongs to userList. + * The input is an arraylist of Shape3DRetained type. + * This is used to send a message of the snapshot of the + * geometry atoms that are affected by this change. + */ + final static GeometryAtom[] getGeomAtomsArray(ArrayList<Shape3DRetained> userList) { + Shape3DRetained ms = null; + GeometryAtom[] gaArr = null; + int size, nullCnt=0, i, j; + + synchronized(userList) { + size = userList.size(); + gaArr = new GeometryAtom[size]; + for (i = 0; i < size; i++) { + ms = userList.get(i); + ms.mirrorShape3DLock.readLock(); + if(ms.geomAtom == null) { + nullCnt++; + } + gaArr[i] = ms.geomAtom; + ms.mirrorShape3DLock.readUnlock(); + } + } + if(nullCnt == 0) { + return gaArr; + } + else if(nullCnt == size) { + return null; + } + else { + GeometryAtom[] newGaArr = new GeometryAtom[size - nullCnt]; + + for (i=0, j=0; i < size; i++) { + if(gaArr[i] != null) { + newGaArr[j++] = gaArr[i]; + } + } + return newGaArr; + } + } + + /** + * Return a list of geometry atoms belongs to userList and places a list of + * universe found in userList in univList. + * The input is an array of Shape3DRetained type. + * univList is assume to be empty. + * This is used to send a message of the snapshot of the + * geometry atoms that are affected by this change. + */ +final static ArrayList<ArrayList<GeometryAtom>> getGeomAtomsList(ArrayList userList, ArrayList<VirtualUniverse> univList) { + ArrayList<ArrayList<GeometryAtom>> listPerUniverse = new ArrayList<ArrayList<GeometryAtom>>(); + int index; + ArrayList<GeometryAtom> gaList = null; + Shape3DRetained ms = null; + boolean moreThanOneUniv = false; + VirtualUniverse firstFndUniv = null; + + synchronized(userList) { + for (int i = userList.size()-1; i >=0; i--) { + ms = (Shape3DRetained) userList.get(i); + + if(moreThanOneUniv == false) { + if(firstFndUniv == null) { + firstFndUniv = ms.universe; + univList.add(ms.universe); + + gaList = new ArrayList<GeometryAtom>(); + listPerUniverse.add(gaList); + } + else if(firstFndUniv != ms.universe) { + moreThanOneUniv = true; + univList.add(ms.universe); + gaList = new ArrayList<GeometryAtom>(); + listPerUniverse.add(gaList); + } + } + else { + index = univList.indexOf(ms.universe); + if (index < 0) { + univList.add(ms.universe); + gaList = new ArrayList<GeometryAtom>(); + listPerUniverse.add(gaList); + } + else { + gaList = listPerUniverse.get(index); + } + } + + + ms.mirrorShape3DLock.readLock(); + + if(ms.geomAtom != null) { + gaList.add(ms.geomAtom); + } + ms.mirrorShape3DLock.readUnlock(); + + } + } + return listPerUniverse; + } + + final static GeometryAtom getGeomAtom(Shape3DRetained shape) { + GeometryAtom ga; + + shape.mirrorShape3DLock.readLock(); + ga = shape.geomAtom; + shape.mirrorShape3DLock.readUnlock(); + + return ga; + } + + final static void setGeomAtom(Shape3DRetained shape, GeometryAtom ga) { + shape.mirrorShape3DLock.writeLock(); + shape.geomAtom = ga; + shape.mirrorShape3DLock.writeUnlock(); + } + + + // Alpha is editable due to the appearance + boolean isAlphaEditable(GeometryRetained geo) { + + boolean alphaEditable = false; + + if (appearanceOverrideEnable) { + alphaEditable = true; + } else if (geo != null && + appearance != null) { + + AppearanceRetained app = appearance; + + if (source.getCapability( + Shape3D.ALLOW_APPEARANCE_WRITE) || + source.getCapability( + Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) || + + app.source.getCapability( + Appearance.ALLOW_RENDERING_ATTRIBUTES_WRITE) || + + app.source.getCapability( + Appearance.ALLOW_TRANSPARENCY_ATTRIBUTES_WRITE) || + + (app.renderingAttributes != null && + (app.renderingAttributes.source.getCapability( + RenderingAttributes.ALLOW_ALPHA_TEST_FUNCTION_WRITE) || + app.renderingAttributes.source.getCapability( + RenderingAttributes.ALLOW_IGNORE_VERTEX_COLORS_WRITE))) || + + (app.transparencyAttributes != null && + (app.transparencyAttributes.source.getCapability( + TransparencyAttributes.ALLOW_MODE_WRITE) || + app.transparencyAttributes.source.getCapability( + TransparencyAttributes.ALLOW_VALUE_WRITE)))) { + + alphaEditable = true; + + } else if (geo instanceof GeometryArrayRetained && + (app.source.getCapability( + Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE) || + + (app.textureAttributes != null && + app.textureAttributes.source.getCapability( + TextureAttributes.ALLOW_MODE_WRITE)))) { + + alphaEditable = true; + + } else if (geo instanceof RasterRetained) { + if ((((RasterRetained)geo).type & Raster.RASTER_COLOR) != +0 + && ((RasterRetained)geo).source.getCapability( + Raster.ALLOW_IMAGE_WRITE)) { + + alphaEditable = true; + } + } + } + return alphaEditable; + } + + // getCombineBounds is faster than computeCombineBounds since it + // does not recompute the geometry.geoBounds + void getCombineBounds(BoundingBox bounds) { + + if(geometryList != null) { + BoundingBox bbox = null; + GeometryRetained geometry; + + if (staticTransform != null) { + bbox = new BoundingBox((BoundingBox) null); + } + + synchronized(bounds) { + bounds.setLower( 1.0, 1.0, 1.0); + bounds.setUpper(-1.0,-1.0,-1.0); + for(int i=0; i<geometryList.size(); i++) { + geometry = geometryList.get(i); + if ((geometry != null) && + (geometry.geoType != GeometryRetained.GEO_TYPE_NONE)) { + synchronized(geometry.geoBounds) { + if (staticTransform != null) { + bbox.set(geometry.geoBounds); + bbox.transform(staticTransform.transform); + bounds.combine(bbox); + } else { + bounds.combine(geometry.geoBounds); + } + } + } + } + } + + // System.err.println("Shape3DRetained - getCombineBounds"); + // Enlarge boundingBox to the "minmium bounds" that encompasses all possible + // orientation. + if (this instanceof OrientedShape3DRetained) { + double maxVal = Math.abs(bounds.lower.x); + double tempVal = Math.abs(bounds.upper.x); + if(tempVal > maxVal) + maxVal = tempVal; + tempVal = Math.abs(bounds.lower.y); + if(tempVal > maxVal) + maxVal = tempVal; + tempVal = Math.abs(bounds.upper.y); + if(tempVal > maxVal) + maxVal = tempVal; + tempVal = Math.abs(bounds.lower.z); + if(tempVal > maxVal) + maxVal = tempVal; + tempVal = Math.abs(bounds.upper.z); + if(tempVal > maxVal) + maxVal = tempVal; + + // System.err.println("Shape3DRetained - bounds (Before) " + bounds); + bounds.setLower(-maxVal, -maxVal, -maxVal); + bounds.setUpper(maxVal, maxVal, maxVal); + // System.err.println("Shape3DRetained - bounds (After) " + bounds); + } + + } + } + + + boolean isEquivalent(Shape3DRetained shape) { + if (this.appearance != shape.appearance || + // Scoping info should be same since they are under same group + this.appearanceOverrideEnable != shape.appearanceOverrideEnable || + this.isPickable != shape.isPickable || + this.isCollidable != shape.isCollidable) { + + return false; + } + if (this.boundsAutoCompute) { + if (!shape.boundsAutoCompute) + return false; + } + else { + // If bounds autoCompute is false + // Then check if both bounds are equal + if (this.localBounds != null) { + if (shape.localBounds != null) { + return this.localBounds.equals(shape.localBounds); + } + } + else if (shape.localBounds != null) { + return false; + } + } + if (collisionBound != null) { + if (shape.collisionBound == null) + return false; + else + return collisionBound.equals(shape.collisionBound); + } + else if (shape.collisionBound != null) + return false; + + return true; + } + + // Bounds can only be set after the geometry is setLived, so has to be done + // here, if we are not using switchVwcBounds + void initializeGAtom(Shape3DRetained ms) { + int i, gaCnt; + int geometryCnt = 0; + int gSize = geometryList.size(); + GeometryRetained geometry = null; + + ms.bounds = localBounds; + ms.vwcBounds = new BoundingBox((BoundingBox) null); + ms.vwcBounds.transform(ms.bounds, ms.getCurrentLocalToVworld(0)); + + if (collisionBound == null) { + ms.collisionBound = null; + ms.collisionVwcBound = ms.vwcBounds; + } else { + ms.collisionBound = collisionBound; + ms.collisionVwcBound = (Bounds)ms.collisionBound.clone(); + ms.collisionVwcBound.transform(ms.getCurrentLocalToVworld(0)); + } + GeometryAtom gAtom = new GeometryAtom(); + for(gaCnt=0; gaCnt<gSize; gaCnt++) { + geometry = geometryList.get(gaCnt); + if(geometry != null) { + gAtom.geoType = geometry.geoType; + gAtom.alphaEditable = ms.isAlphaEditable(geometry); + break; + } + } + if((geometry != null) && + (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D)) { + + for(gaCnt = 0; gaCnt<gSize; gaCnt++) { + geometry = geometryList.get(gaCnt); + if(geometry != null) { + Text3DRetained tempT3d = (Text3DRetained)geometry; + geometryCnt += tempT3d.numChars; + } + else { + // This is slightly wasteful, but not quite worth to optimize yet. + geometryCnt++; + } + } + gAtom.geometryArray = new GeometryRetained[geometryCnt]; + gAtom.lastLocalTransformArray = new Transform3D[geometryCnt]; + // Reset geometryCnt; + geometryCnt = 0; + + } + else { + gAtom.geometryArray = new GeometryRetained[gSize]; + } + + + for(gaCnt = 0; gaCnt<geometryList.size(); gaCnt++) { + geometry = geometryList.get(gaCnt); + if(geometry == null) { + gAtom.geometryArray[gaCnt] = null; + } + else { + if (geometry.geoType == GeometryRetained.GEO_TYPE_TEXT3D) { + Text3DRetained t = (Text3DRetained)geometry; + GeometryRetained geo; + for (i=0; i<t.numChars; i++, geometryCnt++) { + geo = t.geometryList[i]; + if (geo != null) { + gAtom.geometryArray[geometryCnt] = geo; + gAtom.lastLocalTransformArray[geometryCnt] = + t.charTransforms[i]; + } else { + gAtom.geometryArray[geometryCnt] = null; + gAtom.lastLocalTransformArray[geometryCnt] = null; + } + + } + + } else { + gAtom.geometryArray[gaCnt] = geometry; + } + } + } + gAtom.locale = ms.locale; + gAtom.visible = visible; + gAtom.source = ms; + ms.geomAtom = gAtom; + } + + // Check if geomRetained's class is equivalence with the geometry class. + void checkEquivalenceClass(Geometry geometry, int index) { + + if (geometry != null) { + for (int i=geometryList.size()-1; i >= 0; i--) { + GeometryRetained geomRetained = geometryList.get(i); + if ((geomRetained != null) && + (index != i)) { // this geometry will replace + // current one so there is no need to check + if (!geomRetained.isEquivalenceClass((GeometryRetained)geometry.retained)) { + throw new IllegalArgumentException(J3dI18N.getString("Shape3DRetained5")); + } + break; + } + } + } + } + + int indexOfGeometry(Geometry geometry) { + if(geometry != null) + return geometryList.indexOf(geometry.retained); + else + return geometryList.indexOf(null); + } + + + // Removes the specified geometry from this Shape3DRetained's list of geometries + void removeGeometry(Geometry geometry) { + int ind = indexOfGeometry(geometry); + if(ind >= 0) + removeGeometry(ind); + } + + // Removes all the geometries from this node + void removeAllGeometries() { + int n = geometryList.size(); + + int i; + Shape3DRetained mShape; + GeometryRetained oldGeom = null; + + if (((Shape3D)this.source).isLive()) { + for(int index = n-1; index >= 0; index--) { + oldGeom = geometryList.get(index); + if (oldGeom != null) { + oldGeom.clearLive(refCount); + oldGeom.decRefCnt(); + for (i=0; i<mirrorShape3D.size(); i++) { + mShape = mirrorShape3D.get(i); + oldGeom.removeUser(mShape); + } + } + geometryList.remove(index); + } + sendDataChangedMessage(null); + } else { + for(int index = n-1; index >= 0; index--) { + oldGeom = geometryList.get(index); + if (oldGeom != null) { + oldGeom.decRefCnt(); + } + geometryList.remove(index); + } + } + dirtyBoundsCache(); + } + + boolean willRemainOpaque(int geoType) { + if (appearance == null || + (appearance.isStatic() && + appearance.isOpaque(geoType))) { + return true; + } + else { + return false; + } + + } + + @Override + void handleFrequencyChange(int bit) { + int mask = 0; + if (bit == Shape3D.ALLOW_GEOMETRY_WRITE) { + mask = GEOMETRY_CHANGED; + } + else if (bit == Shape3D.ALLOW_APPEARANCE_WRITE) { + mask = APPEARANCE_CHANGED; + } + else if (bit == Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE) { + mask = APPEARANCEOVERRIDE_CHANGED; + } + if (mask != 0) { + if (source.getCapabilityIsFrequent(bit)) + changedFrequent |= mask; + else if (!source.isLive()) { + changedFrequent &= ~mask; + } + } + } + + + // Alpha is editable due to the appearance(Called on the MirrorShape3D) + boolean isAlphaFrequentlyEditable(GeometryRetained geo) { + + boolean alphaFrequentlyEditable = false; + if (appearanceOverrideEnable) { + alphaFrequentlyEditable = true; + } else if (geo != null && + appearance != null) { + AppearanceRetained app = appearance; + + if (((changedFrequent &(APPEARANCE_CHANGED|APPEARANCEOVERRIDE_CHANGED)) != 0)|| + ((app.changedFrequent &(AppearanceRetained.RENDERING|AppearanceRetained.TRANSPARENCY)) != 0) || + (app.renderingAttributes != null && + (((app.renderingAttributes.changedFrequent & (RenderingAttributesRetained.IGNORE_VCOLOR |RenderingAttributesRetained.ALPHA_TEST_FUNC)) != 0))) || + + (app.transparencyAttributes != null && + ((app.transparencyAttributes.changedFrequent != 0)))) { + + alphaFrequentlyEditable = true; + + } else if (geo instanceof GeometryArrayRetained && + ((app.changedFrequent & AppearanceRetained.TEXTURE_ATTR) != 0) || + (app.textureAttributes != null && + ((app.textureAttributes.changedFrequent & TextureAttributes.ALLOW_MODE_WRITE) != 0))) { + alphaFrequentlyEditable = true; + + } else if (geo instanceof RasterRetained) { + if (((((RasterRetained)geo).type & Raster.RASTER_COLOR) != +0) + && (((RasterRetained)geo).cachedChangedFrequent != 0)) { + + alphaFrequentlyEditable = true; + } + } + } + // System.err.println("changedFrequent="+changedFrequent+" sourceNode = "+sourceNode+" isAlphaFrequentlyEditable, = "+alphaFrequentlyEditable); + return alphaFrequentlyEditable; + } + + + int getPrimaryViewIdx() { + // To avoid MT-safe issues when using View, just clone it. + UnorderList viewList = VirtualUniverse.mc.cloneView(); + View views[] = (View []) viewList.toArray(false); + int size = viewList.arraySize(); + + for (int i=0; i < size; i++) { + if (views[i].primaryView) { + return views[i].viewIndex; + } + } + return 0; + } + + @Override + void searchGeometryAtoms(UnorderList list) { + list.add(getGeomAtom(getMirrorShape(key))); + } +} + |