/* * 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 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 mirrorShape3D = new ArrayList(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 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(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 geomList = new Vector(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 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 path 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 msList = new ArrayList(); super.doSetLive(s); nodeId = universe.getNodeId(); if (inSharedGroup) { for (i=0; i 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 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 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 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 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 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 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 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 msList = new ArrayList(); super.clearLive(s); if (inSharedGroup) { synchronized(mirrorShape3D) { Shape3DRetained[] shapes = mirrorShape3D.toArray(new Shape3DRetained[mirrorShape3D.size()]); for (i=0; i 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 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=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 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(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(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 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> getGeomAtomsList(ArrayList userList, ArrayList univList) { ArrayList> listPerUniverse = new ArrayList>(); int index; ArrayList 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(); listPerUniverse.add(gaList); } else if(firstFndUniv != ms.universe) { moreThanOneUniv = true; univList.add(ms.universe); gaList = new ArrayList(); listPerUniverse.add(gaList); } } else { index = univList.indexOf(ms.universe); if (index < 0) { univList.add(ms.universe); gaList = new ArrayList(); 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 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= 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= 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))); } }