Following the above line of reasoning, heres my patch. To do it this way, four classes have to be modified.
The plan is this: In TriMesh.findTriangleCollisions(), detect when two shared meshes with the same target are colliding. If that ever happens, instantiate a new private field: Map<TriangleBatch,TriangleBatch> triangleMap.
Manually construct a new collision tree, and add it to the CollisionTreeManager, but keyed not to the original triangle batch that it was produced from, but from a new, empty TriangleBatch that will serve as a key. Finally, put that new key batch into the triangleMap created earlier, so that it can be recovered if needed again.
Heres the patch for TriMesh that accomplishes that:
Index: TriMesh.java
===================================================================
RCS file: /cvs/jme/src/com/jme/scene/TriMesh.java,v
retrieving revision 1.69
diff -u -r1.69 TriMesh.java
--- TriMesh.java 2 Aug 2007 21:54:36 -0000 1.69
+++ TriMesh.java 6 Jun 2008 05:19:24 -0000
@@ -36,6 +36,8 @@
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.logging.Logger;
import com.jme.bounding.CollisionTree;
@@ -382,6 +384,8 @@
return thisCT.intersect(checkCT);
}
+ private transient Map<TriangleBatch,TriangleBatch> triangleMap = null;
+
/**
* This function finds all intersections between this trimesh and the
* checking one. The intersections are stored as Integer objects of Triangle
@@ -397,9 +401,44 @@
public void findTriangleCollision(TriMesh toCheck, int batchIndex1, int batchIndex2,
ArrayList<Integer> thisIndex, ArrayList<Integer> otherIndex) {
- CollisionTree myTree = CollisionTreeManager.getInstance().getCollisionTree(getBatch(batchIndex1));
- CollisionTree otherTree = CollisionTreeManager.getInstance().getCollisionTree(toCheck.getBatch(batchIndex2));
+ CollisionTree otherTree = CollisionTreeManager.getInstance().getCollisionTree(toCheck.getBatch(batchIndex2));
+
+ CollisionTree myTree = null;
+ if (toCheck instanceof SharedMesh && (((SharedMesh)toCheck).getTarget() == this))
+ {
+ if (triangleMap == null)
+ triangleMap = new HashMap<TriangleBatch, TriangleBatch>();
+ TriangleBatch original = getBatch(batchIndex1);
+ //this is an intersection of the target with a mesh that shares it.
+ //First check if we already have a secondary collision tree for this
+ TriangleBatch keyBatch = triangleMap.get(original);
+ if (keyBatch != null)
+ {
+ boolean generatesTrees = CollisionTreeManager.getInstance().isGenerateTrees();
+ CollisionTreeManager.getInstance().setGenerateTrees(false);//disable tree generating for this call (because it won't work properly, as this is a fake triangle batch)
+ myTree = CollisionTreeManager.getInstance().getCollisionTree(keyBatch);
+ CollisionTreeManager.getInstance().setGenerateTrees(generatesTrees);//return tree generation to its old value
+ }
+
+ if (myTree == null)
+ {
+ myTree = new CollisionTree(CollisionTreeManager.getInstance().getTreeType());
+ myTree.construct(getBatch(batchIndex1), this, CollisionTreeManager.getInstance().isDoSort());
+ keyBatch = new TriangleBatch();//create a fake triangle batch to use as a key
+ CollisionTreeManager.getInstance().putTree(keyBatch, myTree);//cache the tree so we can find it later
+ triangleMap.put(original, keyBatch);
+ }else if (otherTree.isChanged())//if the other tree has been updated since the last collision, then we may need to update this tree also.
+ {
+ otherTree.setChanged(false);
+ myTree.construct(original, (TriMesh) original.getParentGeom(), CollisionTreeManager.getInstance().isDoSort());
+
+ }
+ }else
+ myTree= CollisionTreeManager.getInstance().getCollisionTree(getBatch(batchIndex1));
+
+
+
if (myTree == null || otherTree == null) {
return;
}
To facilitate this, CollisionTreeManager needs a new method that allows for trees to be put directly into its cache, so that we can map the tree to an arbitrary triangle batch.
Secondly, CollisionTrees need to keep track of whether they have been modified. This way we know to update the collision tree we created here anytime the original collision tree was updated (because CollisionTreeManager will not know to update our secondary tree when the original gets updated).
Index: CollisionTreeManager.java
===================================================================
RCS file: /cvs/jme/src/com/jme/bounding/CollisionTreeManager.java,v
retrieving revision 1.4
diff -u -r1.4 CollisionTreeManager.java
--- CollisionTreeManager.java 22 Sep 2007 19:56:57 -0000 1.4
+++ CollisionTreeManager.java 6 Jun 2008 05:13:49 -0000
@@ -336,7 +336,7 @@
for (int i = n.getQuantity() - 1; i >= 0; i--) {
updateCollisionTree(n.getChild(i));
}
- } else if (object instanceof TriMesh) {
+ }else if (object instanceof TriMesh) {
TriMesh t = (TriMesh) object;
for (int i = 0; i < t.getBatchCount(); i++) {
updateCollisionTree(t.getBatch(i));
@@ -415,4 +415,9 @@
this.maxTrisPerLeaf = maxTrisPerLeaf;
}
+ public void putTree(TriangleBatch key, CollisionTree tree)
+ {
+ cache.put(key, tree);
+ }
+
}
Index: CollisionTree.java
===================================================================
RCS file: /cvs/jme/src/com/jme/bounding/CollisionTree.java,v
retrieving revision 1.4
diff -u -r1.4 CollisionTree.java
--- CollisionTree.java 23 Dec 2007 03:57:49 -0000 1.4
+++ CollisionTree.java 6 Jun 2008 05:13:49 -0000
@@ -141,7 +141,7 @@
public CollisionTree(int type) {
this.type = type;
}
-
+ boolean changed = false;
/**
* Recreate this Collision Tree for the given TriMesh and batch
* index.
@@ -152,7 +152,6 @@
* @param doSort true to sort triangles during creation, false otherwise
*/
public void construct(int batchIndex, Geometry parent, boolean doSort) {
-
GeomBatch gb = parent.getBatch(batchIndex);
if(gb instanceof TriangleBatch) {
batch = (TriangleBatch)gb;
@@ -190,6 +189,7 @@
* false otherwise.
*/
public void createTree(int start, int end, boolean doSort) {
+ changed = true;
this.start = start;
this.end = end;
@@ -581,4 +581,13 @@
comparator.setBatch(batch);
SortUtil.qsort(triIndex, start, end - 1, comparator);
}
+
+ public boolean isChanged() {
+ return changed;
+ }
+
+ public void setChanged(boolean changed)
+ {
+ this.changed = changed;
+ }
}
(edit: I had listed a minor patch to SharedMesh.findTriangleCollision(), but it turned out not to be neccesary so I've removed it).