aboutsummaryrefslogtreecommitdiffstats
path: root/src/javax/media/j3d/Shape3DRetained.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/javax/media/j3d/Shape3DRetained.java')
-rw-r--r--src/javax/media/j3d/Shape3DRetained.java2844
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)));
+ }
+}
+