--- /dev/null
+package com.jme.scene;
+
+import com.jme.scene.shape.Sphere;
+import com.jme.bounding.BoundingSphere;
+import static org.junit.Assert.*;
+
+/**
+ * @author Blaine Simpson (blaine dot simpson at admc dot com)
+ */
+public class CollisionMaskTest {
+ public CollisionMaskTest() {}
+
+ protected Node n;
+ protected Sphere a, b, c;
+
+ @org.junit.Before
+ public void setupScene() {
+ a = new Sphere("a", 8, 8, 1f);
+ /*
+ * Disabling this because the Bounding volumes never get initialized
+ * correctly.
+ * I must be missing something very obvious, because the
+ * updateGeometricState call below should do everything that's needed
+ * (since I previously allocated the Bounding* objects).
+ b = new Sphere("b", 8, 8, 1f);
+ c = new Sphere("c", 8, 8, 1f);
+ //a.setModelBound(new BoundingSphere());
+ //b.setModelBound(new BoundingSphere());
+ //c.setModelBound(new BoundingSphere());
+ b.getLocalTranslation().setX(.1f);
+ c.getLocalTranslation().setX(20f);
+ n = new Node("n");
+ n.attachChild(a);
+ n.attachChild(b);
+ n.attachChild(c);
+ n.setModelBound(new BoundingSphere());
+ //a.updateWorldBound();
+ //b.updateWorldBound();
+ //c.updateWorldBound();
+ n.updateGeometricState(0, true);
+ System.err.println("BOunds of a = " + a.getWorldBound());
+ }
+
+ /*
+ @org.junit.After
+ public void cleanupScene() {
+ a.removeFromParent();
+ b.removeFromParent();
+ c.removeFromParent();
+ a = b = c = null;
+ n = null;
+ */
+ }
+
+ @org.junit.Test
+ public void bitOps() {
+ a.setCollisionMask(-1);
+ assertTrue(a.isCollidable(0));
+ a.setCollisionMask(0);
+ assertTrue(a.isCollidable(0));
+ assertFalse(a.isCollidable(-1));
+ a.setCollisionMask(9999);
+ assertFalse(a.isCollidable(-1));
+ a.setCollisionMask(-9999);
+ assertFalse(a.isCollidable(-1));
+ a.setCollisionMask(10);
+ assertTrue(a.isCollidable(10));
+ a.setCollisionMask(11);
+ assertTrue(a.isCollidable(10));
+ a.setCollisionMask(10);
+ assertFalse(a.isCollidable(11));
+ }
+
+ /*
+ See comment in the 'setupScene' method above.
+ @org.junit.Test
+ public void geoGeoCollisions() {
+ // Use requiredOnBits of 0 to just test whether the Spatials touch
+ assertTrue(a.hasCollision(b, false, 0));
+ assertTrue(b.hasCollision(a, false, 0));
+ assertFalse(a.hasCollision(c, false, 0));
+ assertFalse(c.hasCollision(b, false, 0));
+ assertFalse(b.hasCollision(c, false, 0));
+ assertFalse(c.hasCollision(a, false, 0));
+
+ // These should all fail because a does not have 2 in its bit mask.
+ b.setCollisionMask(3);
+ c.setCollisionMask(-1);
+ assertFalse(a.hasCollision(b, false, 2));
+ assertFalse(b.hasCollision(a, false, 2));
+ assertFalse(a.hasCollision(c, false, 2));
+ assertFalse(c.hasCollision(b, false, 2));
+ assertFalse(b.hasCollision(c, false, 2));
+ assertFalse(c.hasCollision(a, false, 2));
+
+ // Flip on the needed 2 bit for a.
+ a.setCollisionMask(2);
+ assertTrue(a.hasCollision(b, false, 2));
+ assertTrue(b.hasCollision(a, false, 2));
+ assertFalse(a.hasCollision(c, false, 2));
+ assertFalse(c.hasCollision(b, false, 2));
+ assertFalse(b.hasCollision(c, false, 2));
+ assertFalse(c.hasCollision(a, false, 2));
+
+ // Turn up a couple more bits too.
+ a.setCollisionMask(10);
+ assertTrue(a.hasCollision(b, false, 2));
+ assertTrue(b.hasCollision(a, false, 2));
+ assertFalse(a.hasCollision(c, false, 2));
+ assertFalse(c.hasCollision(b, false, 2));
+ assertFalse(b.hasCollision(c, false, 2));
+ assertFalse(c.hasCollision(a, false, 2));
+ }
+ */
+}
* @see com.jme.scene.Spatial#hasCollision(com.jme.scene.Spatial,
* com.jme.intersection.CollisionResults)
*/
- public void findCollisions(Spatial scene, CollisionResults results) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
// TODO Auto-generated method stub
}
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
return false;
}
/* (non-Javadoc)
* @see com.jme.scene.Spatial#doPick(com.jme.math.Ray, com.jme.intersection.PickResults)
*/
- public void findPick(Ray toTest, PickResults results) {
+ public void findPick(Ray toTest, PickResults results, int requiredOnBits) {
// TODO Auto-generated method stub
}
* com.jme.intersection.CollisionResults)
*/
@Override
- public void findCollisions(Spatial scene, CollisionResults results) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
} // findCollisions
* @see com.jme.scene.Spatial#hasCollision(com.jme.scene.Spatial, boolean)
*/
@Override
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
return false;
}
* com.jme.intersection.PickResults)
*/
@Override
- public void findPick(Ray toTest, PickResults results) {
+ public void findPick(
+ Ray toTest, PickResults results, int requiredOnBits) {
} // findPick
} // CatmullRomCurve
@Override
- public void findCollisions(Spatial scene, CollisionResults results) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
// TODO Auto-generated method stub
}
@Override
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
// TODO Auto-generated method stub
return false;
}
*
* @see com.jme.scene.Spatial#doPick(com.jme.math.Ray, com.jme.intersection.PickResults)
*/
- public void findPick(Ray toTest, PickResults results) {
+ public void findPick(
+ Ray toTest, PickResults results, int requiredOnBits) {
// TODO Auto-generated method stub
}
/**
* Check if this geometry intersects the ray if yes add it to the results.
*
- * @param ray
- * ray to check intersection with. The direction of the ray must
+ * @param ray ray to check intersection with. The direction of the ray must
* be normalized (length 1).
+ * @param requiredOnBits Collision will only be considered if 'this'
+ * has these bits of its collision mask set.
* @param results
* result list
*/
@Override
- public void findPick(Ray ray, PickResults results) {
- if (getWorldBound() == null || !isCollidable()) {
+ public void findPick(Ray ray, PickResults results, int requiredOnBits) {
+ if (getWorldBound() == null || !isCollidable(requiredOnBits)) {
return;
}
if (getWorldBound().intersects(ray)) {
* @see com.jme.scene.Spatial#hasCollision(com.jme.scene.Spatial,
* com.jme.intersection.CollisionResults)
*/
- public void findCollisions(Spatial scene, CollisionResults results) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
// unsupported
}
*
* @return always false for lines
*/
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
return false;
}
setLightCombineMode(base.getLocalLightCombineMode());
setRenderQueueMode(base.getRenderQueueMode());
setNormalsMode(base.getLocalNormalsMode());
- setIsCollidable(base.isCollidable());
+ setCollisionMask(base.getCollisionMask());
setRenderQueueMode(base.getLocalRenderQueueMode());
for (RenderState.StateType rsType : RenderState.StateType.values()) {
clearRenderState(rsType);
*/
public Node(String name) {
super(name);
+ setCollisionMask(-1);
+ // Newly constitued (not reconsituted) Nodes should allow for maximum
+ // collision nesting. If anything other than -1, the node would block
+ // collisions for all descendant Spatials.
logger.info("Node created.");
}
}
@Override
- public void findCollisions(Spatial scene, CollisionResults results) {
- if (getWorldBound() != null && isCollidable() && scene.isCollidable()) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
+ if (getWorldBound() != null && isCollidable(requiredOnBits)
+ && scene.isCollidable(requiredOnBits)) {
if (getWorldBound().intersects(scene.getWorldBound())) {
// further checking needed.
for (int i = 0; i < getQuantity(); i++) {
- getChild(i).findCollisions(scene, results);
+ getChild(i).findCollisions(scene, results, requiredOnBits);
}
}
}
}
@Override
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
if (this == scene) return false; // No Collision with "self"
- if (getWorldBound() != null && isCollidable() && scene.isCollidable()) {
+ if (getWorldBound() != null && isCollidable(requiredOnBits)
+ && scene.isCollidable(requiredOnBits)) {
if (getWorldBound().intersects(scene.getWorldBound())) {
if(children == null && !checkTriangles) {
return true;
}
// further checking needed.
for (int i = 0; i < getQuantity(); i++) {
- if (getChild(i).hasCollision(scene, checkTriangles)) {
+ if (getChild(i).hasCollision(
+ scene, checkTriangles, requiredOnBits)) {
return true;
}
}
}
@Override
- public void findPick(Ray toTest, PickResults results) {
+ public void findPick(Ray toTest, PickResults results, int requiredOnBits) {
if(children == null) {
return;
}
- if (getWorldBound() != null && isCollidable()) {
+ if (getWorldBound() != null && isCollidable(requiredOnBits)) {
if (getWorldBound().intersects(toTest)) {
// further checking needed.
for (int i = 0; i < getQuantity(); i++) {
* @see com.jme.scene.Spatial#hasCollision(com.jme.scene.Spatial,
* com.jme.intersection.CollisionResults)
*/
- public void findCollisions(Spatial scene, CollisionResults results) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
; // unsupported
}
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
return false;
}
}
@Override
- public void findCollisions(Spatial scene, CollisionResults results) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
; // unsupported
}
@Override
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
return false;
}
}
* This function checks for intersection between the target trimesh and the
* given one. On the first intersection, true is returned.
*
- * @param toCheck
- * The intersection testing mesh.
+ * @param toCheck The intersection testing mesh.
+ * @param requiredOnBits Collision will only be considered if both 'this'
+ * and 'toCheck' have these bits of their collision masks set.
* @return True if they intersect.
*/
@Override
- public boolean hasTriangleCollision(TriMesh toCheck) {
+ public boolean hasTriangleCollision(TriMesh toCheck, int requiredOnBits) {
target.setLocalTranslation(worldTranslation);
target.setLocalRotation(worldRotation);
target.setLocalScale(worldScale);
target.updateWorldBound();
- return target.hasTriangleCollision(toCheck);
+ return target.hasTriangleCollision(toCheck, requiredOnBits);
}
/**
}
/**
+ * Convenience wrapper for
+ * calculateCollisions(Spatial, CollisionResults, int) using default
+ * collidability (first bit of the collidable bit mask).
+ *
+ * @see #calculateCollisions(Spatial, CollisionResults, int)
+ */
+ final public void calculateCollisions(
+ Spatial scene, CollisionResults results) {
+ calculateCollisions(scene, results, 1);
+ }
+
+ /**
* <code>calculateCollisions</code> calls findCollisions to populate the
* CollisionResults object then processes the collision results.
*
- * @param scene
- * the scene to test against.
- * @param results
- * the results object.
+ * @param scene the scene to test against.
+ * @param results the results object.
+ * @param requiredOnBits considered a collision only if these bits are 'on'
+ * in both 'this' and the 'scene' spatial.
*/
- public void calculateCollisions(Spatial scene, CollisionResults results) {
- findCollisions(scene, results);
+ final public void calculateCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
+ findCollisions(scene, results, requiredOnBits);
results.processCollisions();
}
public abstract void setModelBound(BoundingVolume modelBound);
/**
+ * Convenience wrapper for
+ * findCollisions(Spatial, CollisionResults, int) using default
+ * collidability (first bit of the collidable bit mask).
+ *
+ * @see #findCollisions(Spatial, CollisionResults, int)
+ */
+ final public void findCollisions(Spatial scene, CollisionResults results) {
+ findCollisions(scene, results, 1);
+ }
+
+ /**
* checks this spatial against a second spatial, any collisions are stored
* in the results object.
*
- * @param scene
- * the scene to test against.
- * @param results
- * the results of the collisions.
+ * @param scene the scene to test against.
+ * @param results the results of the collisions.
+ * @param requiredOnBits considered a collision only if these bits are 'on'
+ * in both 'this' and the 'scene' spatial.
*/
- public abstract void findCollisions(Spatial scene, CollisionResults results);
+ public abstract void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits);
+
+ /**
+ * Convenience wrapper for
+ * hasCollision(Spatial, CollisionResults, int) using default
+ * collidability (first bit of the collidable bit mask).
+ *
+ * @see #hasCollisionsSpatial, CollisionResults, boolean)
+ */
+ final public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ return hasCollision(scene, checkTriangles, 1);
+ }
/**
* Checks this spatial against a second spatial for collisions.
*
- * @param scene
- * the scene to test against.
- * @param checkTriangles
- * check for collisions on triangle accuracy level
+ * @param scene the scene to test against.
+ * @param checkTriangles check for collisions on triangle accuracy level
+ * @param requiredOnBits considered a collision only if these bits are 'on'
+ * in both 'this' and the 'scene' spatial.
* @return true if any collision were found
*/
- public abstract boolean hasCollision(Spatial scene, boolean checkTriangles);
+ public abstract boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits);
- public void calculatePick(Ray ray, PickResults results) {
- findPick(ray, results);
+ /**
+ * Convenience wrapper for
+ * calculatePick(Ray, PickResults, int) using default
+ * collidability (first bit of the collidable bit mask).
+ *
+ * @see #calculatePick(Ray, PickResults, int)
+ */
+ final public void calculatePick(Ray ray, PickResults results) {
+ calculatePick(ray, results, 1);
+ }
+
+ /**
+ * @param requiredOnBits considered a collision only if these bits are 'on'
+ * in 'this' spatial.
+ */
+ final public void calculatePick(
+ Ray ray, PickResults results, int requiredOnBits) {
+ findPick(ray, results, requiredOnBits);
results.processPick();
}
/**
+ * Convenience wrapper for
+ * findPick(Ray, PickResults, int) using default
+ * collidability (first bit of the collidable bit mask).
+ *
+ * @see #findPick(Ray, PickResults, int)
+ */
+ final public void findPick(Ray toTest, PickResults results) {
+ findPick(toTest, results, 1);
+ }
+
+ /**
* Tests a ray against this spatial, and stores the results in the result
* object.
*
- * @param toTest
- * ray to test picking against
- * @param results
- * the results of the picking
+ * @param toTest ray to test picking against
+ * @param results the results of the picking
+ * @param requiredOnBits considered a collision only if these bits are 'on'
+ * in 'this' spatial.
*/
- public abstract void findPick(Ray toTest, PickResults results);
+ public abstract void findPick(
+ Ray toTest, PickResults results, int requiredOnBits);
/**
* Stores user define data for this Spatial.
return name;
}
+ public void setCollisionMask(int collisionBits) {
+ this.collisionBits = collisionBits;
+ }
+
+ public int getCollisionMask() {
+ return collisionBits;
+ }
+
/**
* Sets if this Spatial is to be used in intersection (collision and
* picking) calculations. By default this is true.
* false otherwise.
*/
public boolean isCollidable() {
- return (collisionBits & 1) != 0;
+ return isCollidable(1);
+ }
+
+ /**
+ * @returns true if this Spatial matches all set bits in the specified value
+ */
+ public boolean isCollidable(int requiredOnBits) {
+ return ((requiredOnBits & collisionBits) ^ requiredOnBits) == 0;
}
/**
activeChild = SN_INVALID_CHILD;
} else {
if(activeChildData != null) {
- activeChildData.setIsCollidable(false);
+ activeChildData.setCollisionMask(0);
}
this.activeChild = child;
activeChildData = getChild(activeChild);
- activeChildData.setIsCollidable(true);
+ activeChildData.setCollisionMask(getCollisionMask());
}
}
*/
@Override
public int attachChild(Spatial child) {
- child.setIsCollidable(false);
+ child.setCollisionMask(0);
return super.attachChild(child);
}
*/
@Override
public int attachChildAt(Spatial child, int index) {
- child.setIsCollidable(false);
+ child.setCollisionMask(0);
return super.attachChildAt(child, index);
}
* collisions are checked for the currently active child.
*/
@Override
- public void findCollisions(Spatial scene, CollisionResults results) {
- if (this == scene || !isCollidable() || !scene.isCollidable()) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
+ if (this == scene || !isCollidable(requiredOnBits)
+ || !scene.isCollidable(requiredOnBits)) {
return;
}
if (activeChild != SN_INVALID_CHILD) {
if (activeChildData != null) {
- activeChildData.findCollisions(scene, results);
+ activeChildData.findCollisions(scene, results, requiredOnBits);
}
}
}
* collisions are checked for the currently active child.
*/
@Override
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
- if (this == scene || !isCollidable() || !scene.isCollidable()) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
+ if (this == scene || !isCollidable(requiredOnBits)
+ || !scene.isCollidable(requiredOnBits)) {
return false;
}
if (activeChild != SN_INVALID_CHILD) {
if (activeChildData != null) {
- return activeChildData.hasCollision(scene, checkTriangles);
+ return activeChildData.hasCollision(
+ scene, checkTriangles, requiredOnBits);
}
}
return false;
}
- public void findPick(Ray toTest, PickResults results) {
+ public void findPick(Ray toTest, PickResults results, int requiredOnBits) {
if (activeChild != SN_INVALID_CHILD) {
if (activeChildData != null) {
- activeChildData.findPick(toTest, results);
+ activeChildData.findPick(toTest, results, requiredOnBits);
}
}
}
* @see com.jme.scene.Spatial#hasCollision(com.jme.scene.Spatial,
* com.jme.intersection.CollisionResults)
*/
- public void findCollisions(Spatial scene, CollisionResults results) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
//Do nothing.
}
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
return false;
}
* determines if a collision between this trimesh and a given spatial occurs
* if it has true is returned, otherwise false is returned.
*/
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
- if (this == scene || !isCollidable() || !scene.isCollidable()) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
+ if (this == scene || !isCollidable(requiredOnBits)
+ || !scene.isCollidable(requiredOnBits)) {
return false;
}
if (getWorldBound().intersects(scene.getWorldBound())) {
if (scene instanceof Node) {
Node parent = (Node) scene;
for (int i = 0; i < parent.getQuantity(); i++) {
- if (hasCollision(parent.getChild(i), checkTriangles)) {
+ if (hasCollision(parent.getChild(i),
+ checkTriangles, requiredOnBits)) {
return true;
}
}
return true;
}
- return hasTriangleCollision((TriMesh) scene);
+ return hasTriangleCollision((TriMesh) scene, requiredOnBits);
}
return false;
* the two trimesh OBBTrees are then compared to find the triangles that
* hit.
*/
- public void findCollisions(Spatial scene, CollisionResults results) {
- if (this == scene || !isCollidable() || !scene.isCollidable()) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
+ if (this == scene || !isCollidable(requiredOnBits)
+ || !scene.isCollidable(requiredOnBits)) {
return;
}
if (scene instanceof Node) {
Node parent = (Node) scene;
for (int i = 0; i < parent.getQuantity(); i++) {
- findCollisions(parent.getChild(i), results);
+ findCollisions(parent.getChild(i), results, requiredOnBits);
}
} else {
results.addCollision(this, (Geometry) scene);
}
/**
+ * Convenience wrapper for hasTriangleCollision(TriMesh, int) using default
+ * collision mask (bit 1 set).
+ */
+ final public boolean hasTriangleCollision(TriMesh toCheck) {
+ return hasTriangleCollision(toCheck, 1);
+ }
+
+ /**
* This function checks for intersection between this trimesh and the given
* one. On the first intersection, true is returned.
*
- * @param toCheck
- * The intersection testing mesh.
+ * @param toCheck The intersection testing mesh.
+ * @param requiredOnBits Collision will only be considered if both 'this'
+ * and 'toCheck' have these bits of their collision masks set.
* @return True if they intersect.
*/
- public boolean hasTriangleCollision(TriMesh toCheck) {
+ public boolean hasTriangleCollision(TriMesh toCheck, int requiredOnBits) {
CollisionTree thisCT = CollisionTreeManager.getInstance()
.getCollisionTree(this);
CollisionTree checkCT = CollisionTreeManager.getInstance()
.getCollisionTree(toCheck);
- if (thisCT == null || checkCT == null || !isCollidable()
- || !toCheck.isCollidable()) {
+ if (thisCT == null || checkCT == null || !isCollidable(requiredOnBits)
+ || !toCheck.isCollidable(requiredOnBits)) {
return false;
}
thisCT.getBounds().transform(worldRotation, worldTranslation,
}
@Override
- public void findCollisions(Spatial scene, CollisionResults results) {
+ public void findCollisions(
+ Spatial scene, CollisionResults results, int requiredOnBits) {
; // ignore
}
@Override
- public boolean hasCollision(Spatial scene, boolean checkTriangles) {
+ public boolean hasCollision(
+ Spatial scene, boolean checkTriangles, int requiredOnBits) {
return false;
}
target.setRenderState(source.getRenderState(i));
}
- target.setIsCollidable(target.isCollidable());
+ target.setCollisionMask(target.getCollisionMask());
target.setLocalTranslation(source.getLocalTranslation().clone());
target.setLocalRotation(source.getLocalRotation().clone());