javax.media.j3d.Shape3DRetained Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java3d-core Show documentation
Show all versions of java3d-core Show documentation
Java3D Core And Java3D Util Libraries
The newest version!
/*
* 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)));
}
}