
org.scijava.java3d.WakeupOnCollisionEntry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of j3dcore Show documentation
Show all versions of j3dcore Show documentation
3D Graphics API for the Java Platform
The newest version!
/*
* 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 org.scijava.java3d;
import java.util.Vector;
/**
* Class specifying a wakeup when the specified object
* collides with any other object in the scene graph.
*
*/
public final class WakeupOnCollisionEntry extends WakeupCriterion {
// different types of WakeupIndexedList that use in GeometryStructure
static final int COND_IN_GS_LIST = 0;
static final int COLLIDEENTRY_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 = 10;
/**
* Use geometric bounds as an approximation in computing collisions.
*/
public static final int USE_BOUNDS = 11;
static final int GROUP = NodeRetained.GROUP;
static final int BOUNDINGLEAF = NodeRetained.BOUNDINGLEAF;
static final int SHAPE = NodeRetained.SHAPE;
static final int MORPH = NodeRetained.MORPH;
static final int ORIENTEDSHAPE3D = NodeRetained.ORIENTEDSHAPE3D;
static final int BOUND = 0;
/**
* Accuracy mode one of USE_GEOMETRY or USE_BOUNDS
*/
int accuracyMode;
// Cached the arming Node being used when it is not BOUND
NodeRetained armingNode;
// A transformed Bounds of Group/Bounds, use by
// BOUND, GROUP
Bounds vwcBounds = null;
// 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
Bounds collidingBounds = null;
SceneGraphPath collidingPath = null;
/**
* Constructs a new WakeupOnCollisionEntry criterion with
* USE_BOUNDS for a speed hint.
* @param armingPath the path used to arm collision
* detection
* @exception IllegalArgumentException if object associated with the
* SceneGraphPath is other than a Group, Shape3D, Morph, or
* BoundingLeaf node.
*/
public WakeupOnCollisionEntry(SceneGraphPath armingPath) {
this(armingPath, USE_BOUNDS);
}
/**
* Constructs a new WakeupOnCollisionEntry criterion.
* @param armingPath the path used to arm 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 WakeupOnCollisionEntry(SceneGraphPath armingPath,
int speedHint) {
this(new SceneGraphPath(armingPath), speedHint, null);
}
/**
* Constructs a new WakeupOnCollisionEntry criterion.
* @param armingNode the Group, Shape, or Morph node used to
* arm collision detection
* @exception IllegalArgumentException if object is under a
* SharedGroup node or object is other than a Group, Shape3D,
* Morph or BoundingLeaf node.
*/
public WakeupOnCollisionEntry(Node armingNode) {
this(armingNode, USE_BOUNDS);
}
/**
* Constructs a new WakeupOnCollisionEntry criterion.
* @param armingNode the Group, Shape, or Morph node used to
* arm 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 WakeupOnCollisionEntry(Node armingNode, int speedHint) {
this(new SceneGraphPath(null, armingNode), speedHint, null);
}
/**
* Constructs a new WakeupOnCollisionEntry criterion.
* @param armingBounds the bounds object used to arm collision
* detection
*/
public WakeupOnCollisionEntry(Bounds armingBounds) {
this(null, USE_BOUNDS, (Bounds) armingBounds.clone());
}
/**
* Constructs a new WakeupOnCollisionEntry criterion.
* @param armingPath the path used to arm 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 arm 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.
*/
WakeupOnCollisionEntry(SceneGraphPath armingPath,
int speedHint, Bounds armingBounds) {
if (armingPath != null) {
this.armingNode = (NodeRetained) armingPath.getObject().retained;
nodeType = getNodeType(armingNode, armingPath,
"WakeupOnCollisionEntry");
this.armingPath = armingPath;
validateSpeedHint(speedHint, "WakeupOnCollisionEntry4");
} else {
this.armingBounds = armingBounds;
nodeType = 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("WakeupOnCollisionEntry5"));
}
synchronized (behav) {
if (!behav.inCallback) {
throw new IllegalStateException
(J3dI18N.getString("WakeupOnCollisionEntry5"));
}
}
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("WakeupOnCollisionEntry6"));
}
synchronized (behav) {
if (!behav.inCallback) {
throw new IllegalStateException
(J3dI18N.getString("WakeupOnCollisionEntry6"));
}
}
return (collidingBounds != null ?
(Bounds)(collidingBounds.clone()): null);
}
/**
* Node legality checker
* throw Exception if node is not legal.
* @return nodeType
*/
static int getNodeType(NodeRetained armingNode,
SceneGraphPath armingPath, String s)
throws IllegalArgumentException {
// check if SceneGraphPath is unique
// Note that graph may not live at this point so we
// can't use node.inSharedGroup.
if (!armingPath.validate()) {
throw new IllegalArgumentException(J3dI18N.getString(s + "7"));
}
if (armingNode.inBackgroundGroup) {
throw new IllegalArgumentException(J3dI18N.getString(s + "1"));
}
// This should come before Shape3DRetained check
if (armingNode instanceof OrientedShape3DRetained) {
return ORIENTEDSHAPE3D;
}
if (armingNode instanceof Shape3DRetained) {
return SHAPE;
}
if (armingNode instanceof MorphRetained) {
return MORPH;
}
if (armingNode instanceof GroupRetained) {
return GROUP;
}
if (armingNode instanceof BoundingLeafRetained) {
return BOUNDINGLEAF;
}
throw new IllegalArgumentException(J3dI18N.getString(s + "0"));
}
/**
* speedHint legality checker
* throw Exception if speedHint is not legal
*/
static void validateSpeedHint(int speedHint, String s)
throws IllegalArgumentException {
if ((speedHint != USE_GEOMETRY) && (speedHint != USE_BOUNDS)) {
throw new IllegalArgumentException(J3dI18N.getString(s));
}
}
/**
* This is a callback from BehaviorStructure. It is
* used to add wakeupCondition to behavior structure.
*/
@Override
void addBehaviorCondition(BehaviorStructure bs) {
switch (nodeType) {
case SHAPE: // Use geometryAtoms[].collisionBounds
case 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 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 BOUNDINGLEAF: // use BoundingLeaf.transformedRegion
if (!armingNode.source.isLive()) {
return;
}
this.boundingLeaf = ((BoundingLeafRetained) armingNode).mirrorBoundingLeaf;
break;
case BOUND: // use this.vwcBounds
vwcBounds = (Bounds) armingBounds.clone();
this.armingNode = behav;
break;
case 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 = getSceneGraphPath(shape.sourceNode,
shape.key,
shape.getCurrentLocalToVworld(0));
bound = getTriggeringBounds(shape);
} else {
// Find the triggered Path & Bounds for this alternative
// collision target
GroupRetained group = (GroupRetained) leaf;
path = getSceneGraphPath(group);
bound = 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 == 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 == BOUND) {
vwcBounds.transform(armingBounds, behav.getCurrentLocalToVworld());
}
if (reEvaluateGAs &&
(nodeType == GROUP) &&
(accuracyMode == USE_GEOMETRY)) {
geometryAtoms.clear();
((GroupRetained) armingNode).searchGeometryAtoms(geometryAtoms);
}
}
/**
* Return the TriggeringBounds for node
*/
static Bounds getTriggeringBounds(Shape3DRetained mirrorShape) {
NodeRetained node = mirrorShape.sourceNode;
if (node instanceof Shape3DRetained) {
Shape3DRetained shape = (Shape3DRetained) node;
if (shape.collisionBound == null) {
// TODO: get bounds by copy
return shape.getEffectiveBounds();
}
return shape.collisionBound;
}
MorphRetained morph = (MorphRetained) node;
if (morph.collisionBound == null) {
// TODO: get bounds by copy
return morph.getEffectiveBounds();
}
return morph.collisionBound;
}
/**
* Return the TriggeringBounds for node
*/
static Bounds getTriggeringBounds(GroupRetained group) {
if (group.collisionBound == null) {
// TODO: get bounds by copy
return group.getEffectiveBounds();
}
return group.collisionBound;
}
static SceneGraphPath getSceneGraphPath(GroupRetained group) {
// Find the transform base on the key
Transform3D transform = null;
GroupRetained srcGroup = group.sourceNode;
synchronized (srcGroup.universe.sceneGraphLock) {
if (group.key == null) {
transform = srcGroup.getCurrentLocalToVworld();
} else {
HashKey keys[] = srcGroup.localToVworldKeys;
if (keys == null) {
// the branch is already detach when
// Collision got this message
return null;
}
transform = srcGroup.getCurrentLocalToVworld(group.key);
}
return getSceneGraphPath(srcGroup, group.key, transform);
}
}
/**
* return the SceneGraphPath of the geomAtom.
* Find the alternative Collision target closest to the locale.
*/
static SceneGraphPath getSceneGraphPath(NodeRetained startNode,
HashKey key,
Transform3D transform) {
synchronized (startNode.universe.sceneGraphLock) {
NodeRetained target = startNode;
UnorderList path = new UnorderList(5, Node.class);
NodeRetained nodeR = target;
Locale locale = nodeR.locale;
String nodeId;
if (nodeR.inSharedGroup) {
// getlastNodeId() will destroy this key
if (key != null) {
key = new HashKey(key);
} else {
key = new HashKey(startNode.localToVworldKeys[0]);
}
}
do {
if (nodeR.source.getCapability(Node.ENABLE_COLLISION_REPORTING)){
path.add(nodeR.source);
}
if (nodeR instanceof SharedGroupRetained) {
// retrieve the last node ID
nodeId = key.getLastNodeId();
Vector parents = ((SharedGroupRetained)nodeR).parents;
NodeRetained prevNodeR = nodeR;
for(int i=parents.size()-1; i >=0; i--) {
NodeRetained linkR = parents.get(i);
if (linkR.nodeId.equals(nodeId)) {
nodeR = linkR;
break;
}
}
if (nodeR == prevNodeR) {
// the branch is already detach when
// Collision got this message
return null;
}
} else if ((nodeR instanceof GroupRetained) &&
((GroupRetained) nodeR).collisionTarget) {
// we need to find the collision target closest to the
// root of tree
target = nodeR;
if (key == null) {
transform = nodeR.getCurrentLocalToVworld(null);
} else {
transform = nodeR.getCurrentLocalToVworld(key);
}
}
nodeR = nodeR.parent;
} while (nodeR != null); // reach Locale
Node nodes[];
if (target == startNode) { // in most case
nodes = (Node []) path.toArray(false);
} else { // alternativeCollisionTarget is set
nodes = (Node []) path.toArray(target);
}
SceneGraphPath sgpath = new SceneGraphPath(locale,
nodes,
(Node) target.source);
sgpath.setTransform(transform);
return sgpath;
}
}
@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.
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy