All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.media.j3d.Shape3DRetained Maven / Gradle / Ivy

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)));
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy