javax.media.j3d.WakeupOnCollisionMovement.java Source code

Java tutorial

Introduction

Here is the source code for javax.media.j3d.WakeupOnCollisionMovement.java

Source

/*
 * Copyright 1997-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;

/**
 * Class specifying a wakeup when the specified object
 * moves while in collision with any other object in the scene graph.
 */
public final class WakeupOnCollisionMovement extends WakeupCriterion {

    // different types of WakeupIndexedList that use in GeometryStructure
    static final int COND_IN_GS_LIST = 0;
    static final int COLLIDEMOVE_IN_BS_LIST = 1;

    // total number of different IndexedUnorderedSet types
    static final int TOTAL_INDEXED_UNORDER_SET_TYPES = 2;

    /**
     * Use geometry in computing collisions.
     */
    public static final int USE_GEOMETRY = WakeupOnCollisionEntry.USE_GEOMETRY;

    /**
     * Use geometric bounds as an approximation in computing collisions.
     */
    public static final int USE_BOUNDS = WakeupOnCollisionEntry.USE_BOUNDS;

    /**
     * Accuracy mode one of USE_GEOMETRY or USE_BOUNDS
     */
    int accuracyMode;

    // Cached the arming Node being used when it is not BOUND
    NodeRetained armingNode;

    // transformed Bounds of Group/Bounds, use by
    // BOUND, BOUNDINGLEAF, GROUP
    Bounds vwcBounds;

    // use by GROUP to cache local bounds
    Bounds localBounds = null;

    // source bound when collision occur last time
    // These three variables are used to keep track of duplicate
    // wakupOnMovement event
    Bounds lastSrcBounds = null;
    Bounds lastDstBounds = null;
    boolean duplicateEvent = false;

    // Use by BoundingLeaf, point to mirror BoundingLeaf
    // transformedRegion under this leaf is used.
    BoundingLeafRetained boundingLeaf = null;

    /**
     * Geometry atoms that this wakeup condition refer to.
     * Only use by SHAPE, MORPH, GROUP, ORIENTEDSHAPE
     */
    UnorderList geometryAtoms = null;

    // one of GROUP, BOUNDINGLEAF, SHAPE, MORPH, BOUND
    int nodeType;

    SceneGraphPath armingPath = null;
    Bounds armingBounds = null;

    // the following two references are set only after a collision
    // has occurred
    SceneGraphPath collidingPath = null;
    Bounds collidingBounds = null;

    /**
     * Constructs a new WakeupOnCollisionMovement criterion.
     * @param armingPath the path used to <em>arm</em> collision
     * detection
     * @exception IllegalArgumentException if object associated with the
     * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
     */
    public WakeupOnCollisionMovement(SceneGraphPath armingPath) {
        this(armingPath, USE_BOUNDS);
    }

    /**
     * Constructs a new WakeupOnCollisionMovement criterion.
     * @param armingPath the path used to <em>arm</em> collision
     * detection
     * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
     * accurately Java 3D will perform collision detection
     * @exception IllegalArgumentException if hint is not one of
     * USE_GEOMETRY or USE_BOUNDS.
     * @exception IllegalArgumentException if object associated with the
     * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
     */
    public WakeupOnCollisionMovement(SceneGraphPath armingPath, int speedHint) {
        this(new SceneGraphPath(armingPath), speedHint, null);
    }

    /**
     * Constructs a new WakeupOnCollisionMovement criterion.
     * @param armingNode the Group, Shape, or Morph node used to
     * <em>arm</em> collision detection
     * @exception IllegalArgumentException if object is under a
     * SharedGroup node or object is other than a Group, Shape3D,
     * Morph or BoundingLeaf node.
     */
    public WakeupOnCollisionMovement(Node armingNode) {
        this(armingNode, USE_BOUNDS);
    }

    /**
     * Constructs a new WakeupOnCollisionMovement criterion.
     * @param armingNode the Group, Shape, or Morph node used to
     * <em>arm</em> collision detection
     * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
     * accurately Java 3D will perform collision detection
     * @exception IllegalArgumentException if hint is not one of
     * USE_GEOMETRY or USE_BOUNDS.
     * @exception IllegalArgumentException if object is under a
     * SharedGroup node or object is other than a Group, Shape3D,
     * Morph or BoundingLeaf node.
     */
    public WakeupOnCollisionMovement(Node armingNode, int speedHint) {
        this(new SceneGraphPath(null, armingNode), speedHint, null);
    }

    /**
     * Constructs a new WakeupOnCollisionMovement criterion.
     * @param armingBounds the bounds object used to <em>arm</em> collision
     * detection
     */
    public WakeupOnCollisionMovement(Bounds armingBounds) {
        this(null, USE_BOUNDS, (Bounds) armingBounds.clone());
    }

    /**
     * Constructs a new WakeupOnCollisionMovement criterion.
     * @param armingPath the path used to <em>arm</em> collision
     * detection
     * @param speedHint one of USE_GEOMETRY or USE_BOUNDS, specifies how
     * accurately Java 3D will perform collision detection
     * @param armingBounds the bounds object used to <em>arm</em> collision
     * detection
     * @exception IllegalArgumentException if hint is not one of
     * USE_GEOMETRY or USE_BOUNDS.
     * @exception IllegalArgumentException if object associated with the
     * SceneGraphPath is other than a Group, Shape3D, Morph, or BoundingLeaf node.
     */
    WakeupOnCollisionMovement(SceneGraphPath armingPath, int speedHint, Bounds armingBounds) {
        if (armingPath != null) {
            this.armingNode = (NodeRetained) armingPath.getObject().retained;
            nodeType = WakeupOnCollisionEntry.getNodeType(armingNode, armingPath, "WakeupOnCollisionMovement");
            this.armingPath = armingPath;
            WakeupOnCollisionEntry.validateSpeedHint(speedHint, "WakeupOnCollisionMovement4");
        } else {
            this.armingBounds = armingBounds;
            nodeType = WakeupOnCollisionEntry.BOUND;
        }
        accuracyMode = speedHint;
        WakeupIndexedList.init(this, TOTAL_INDEXED_UNORDER_SET_TYPES);
    }

    /**
     * Returns the path used in specifying the collision condition.
     * @return the SceneGraphPath object generated when arming this
     * criterion---null implies that a bounds object armed this criteria
     */
    public SceneGraphPath getArmingPath() {
        return (armingPath != null ? new SceneGraphPath(armingPath) : null);
    }

    /**
     * Returns the bounds object used in specifying the collision condition.
     * @return the Bounds object generated when arming this
     * criterion---null implies that a SceneGraphPath armed this criteria
     */
    public Bounds getArmingBounds() {
        return (armingBounds != null ? (Bounds) armingBounds.clone() : null);
    }

    /**
     * Retrieves the path describing the object causing the collision.
     * @return the SceneGraphPath that describes the triggering object.
     * @exception IllegalStateException if not called from within the
     * a behavior's processStimulus method which was awoken by a collision.
     */
    public SceneGraphPath getTriggeringPath() {
        if (behav == null) {
            throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionMovement5"));
        }

        synchronized (behav) {
            if (!behav.inCallback) {
                throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionMovement5"));
            }
        }
        return (collidingPath != null ? new SceneGraphPath(collidingPath) : null);
    }

    /**
     * Retrieves the Bounds object that caused the collision
     * @return the colliding Bounds object.
     * @exception IllegalStateException if not called from within the
     * a behavior's processStimulus method which was awoken by a collision.
     */
    public Bounds getTriggeringBounds() {
        if (behav == null) {
            throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionMovement6"));
        }

        synchronized (behav) {
            if (!behav.inCallback) {
                throw new IllegalStateException(J3dI18N.getString("WakeupOnCollisionMovement6"));
            }
        }
        return (collidingBounds != null ? (Bounds) (collidingBounds.clone()) : null);
    }

    /**
     * This is a callback from BehaviorStructure. It is
     * used to add wakeupCondition to behavior structure.
     */
    @Override
    void addBehaviorCondition(BehaviorStructure bs) {

        switch (nodeType) {
        case WakeupOnCollisionEntry.SHAPE: // Use geometryAtoms[].collisionBounds
        case WakeupOnCollisionEntry.ORIENTEDSHAPE3D:
            if (!armingNode.source.isLive()) {
                return;
            }
            if (geometryAtoms == null) {
                geometryAtoms = new UnorderList(1, GeometryAtom.class);
            }
            Shape3DRetained shape = (Shape3DRetained) armingNode;
            geometryAtoms.add(Shape3DRetained.getGeomAtom(shape.getMirrorShape(armingPath)));
            break;
        case WakeupOnCollisionEntry.MORPH: // Use geometryAtoms[].collisionBounds
            if (!armingNode.source.isLive()) {
                return;
            }
            if (geometryAtoms == null) {
                geometryAtoms = new UnorderList(1, GeometryAtom.class);
            }
            MorphRetained morph = (MorphRetained) armingNode;
            geometryAtoms.add(Shape3DRetained.getGeomAtom(morph.getMirrorShape(armingPath)));
            break;
        case WakeupOnCollisionEntry.BOUNDINGLEAF: // use BoundingLeaf.transformedRegion
            if (!armingNode.source.isLive()) {
                return;
            }
            this.boundingLeaf = ((BoundingLeafRetained) armingNode).mirrorBoundingLeaf;
            break;
        case WakeupOnCollisionEntry.BOUND: // use this.vwcBounds
            vwcBounds = (Bounds) armingBounds.clone();
            this.armingNode = behav;
            break;
        case WakeupOnCollisionEntry.GROUP:
            if (!armingNode.source.isLive()) {
                return;
            }
            if (accuracyMode == USE_GEOMETRY) {
                if (geometryAtoms == null) {
                    geometryAtoms = new UnorderList(1, GeometryAtom.class);
                }
                ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
            }
            // else use this.vwcBounds
        default:
        }

        behav.universe.geometryStructure.addWakeupOnCollision(this);
    }

    /**
     * This is a callback from BehaviorStructure. It is
     * used to remove wakeupCondition from behavior structure.
     */
    @Override
    void removeBehaviorCondition(BehaviorStructure bs) {
        vwcBounds = null;
        if (geometryAtoms != null) {
            geometryAtoms.clear();
        }
        boundingLeaf = null;
        behav.universe.geometryStructure.removeWakeupOnCollision(this);
    }

    // Set collidingPath & collidingBounds
    void setTarget(BHLeafInterface leaf) {
        SceneGraphPath path;
        Bounds bound;

        if (leaf instanceof GeometryAtom) {
            // Find the triggered Path & Bounds for this geometry Atom
            GeometryAtom geomAtom = (GeometryAtom) leaf;
            Shape3DRetained shape = geomAtom.source;
            path = WakeupOnCollisionEntry.getSceneGraphPath(shape.sourceNode, shape.key,
                    shape.getCurrentLocalToVworld(0));
            bound = WakeupOnCollisionEntry.getTriggeringBounds(shape);

        } else {
            // Find the triggered Path & Bounds for this alternative
            // collision target
            GroupRetained group = (GroupRetained) leaf;
            path = WakeupOnCollisionEntry.getSceneGraphPath(group);
            bound = WakeupOnCollisionEntry.getTriggeringBounds(group);
        }

        if (path != null) {
            // colliding path may be null when branch detach before
            // user behavior retrieve the previous colliding path
            collidingPath = path;
            collidingBounds = bound;
        }
    }

    // Invoke from GeometryStructure  to update vwcBounds of GROUP
    void updateCollisionBounds(boolean reEvaluateGAs) {
        if (nodeType == WakeupOnCollisionEntry.GROUP) {
            GroupRetained group = (GroupRetained) armingNode;
            if (group.collisionBound != null) {
                vwcBounds = (Bounds) group.collisionBound.clone();
            } else {
                // this may involve recursive tree traverse if
                // BoundsAutoCompute is true, we can't avoid
                // since the bound under it may change by transform
                vwcBounds = group.getEffectiveBounds();
            }
            group.transformBounds(armingPath, vwcBounds);
        } else if (nodeType == WakeupOnCollisionEntry.BOUND) {
            vwcBounds.transform(armingBounds, behav.getCurrentLocalToVworld());
        }

        if (reEvaluateGAs && (nodeType == WakeupOnCollisionEntry.GROUP) && (accuracyMode == USE_GEOMETRY)) {
            geometryAtoms.clear();
            ((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
        }
    }

    @Override
    void setTriggered() {
        // if path not set, probably the branch is just detach.
        if (collidingPath != null) {
            super.setTriggered();
        }
    }

    /**
     * Perform task in addBehaviorCondition() that has to be
     * set every time the condition met.
     */
    @Override
    void resetBehaviorCondition(BehaviorStructure bs) {
        // The reference geometryAtom will not change once
        // Shape3D create so there is no need to set this.
    }

}