Much faster Collision Mesh building without 'removeDuplicateVertices'

Hi,

Patching the CollisionSystemImpl.createAmmoMesh function by just switching the removeDuplicateVertices parameter at addTriangle to false makes our collision baking 10x or more faster. A 50k triangle mesh now gets generated in 15ms instead of 2000ms.

Since createAmmoMesh is essentially freezing the rendering, this improves user the experience a lot when Meshes get loaded dynamically.

The easiest (and dirtiest ) way to patch the engine behavior would be to patch addTriangle directly by adding the following lines of code anywhere before loading collision meshes:

pc.app.addTriangleOverride = Ammo.btTriangleMesh.prototype.addTriangle;
Ammo.btTriangleMesh.prototype.addTriangle = function(v1,v2,v3,check)
{
	pc.app.addTriangleOverride.call(this,v1,v2,v3,false);
}

If triangles would be added by index within createAmmoMesh (by findOrAddVertex and addIndex) the loading should be even faster.

Best,
Tom

2 Likes

Preferably, you want to remove the duplicate vertices in the modelling software. In Blender its simply a single command to remove them.

If you keep the duplicates and generate the collision mesh without removing them, as you do in your example, it will introduce degenerate triangles to the collision solver and some collisions will not be correct, e.g. dynamic objects clipping though, bouncing in the wrong direction, teleporting to the other side of the surface, jittering in place, etc.

That’s a great find!
Do you have some standalone repro for this? If so, could you create an issue here with it, I’m sure we can find some good solution for this.

@LeXXik - engine calls

                triMesh.addTriangle(v1, v2, v3, true);

and I assume it’s just as slow with mesh without duplicates. We need a way to pass false here, or de-duplicate triangles ourselves quicker.

1 Like

Yes, true is the correct option. We want them to be removed. Do you mean it should not be hardcoded? I agree, it would be a good feature request, although, I don’t know if it will be more performant without dupes. This would need some benchmarking.

I’ve added a feature request. @TRT feel free to add to it.

2 Likes

The Ammo examples use false for removeDuplicateVertices when generating collisions from gltf files, what is what we essentially also do, see

For now we don’t experience any issue, it loads just much faster, but its not battle tested of course.

I think the best way to handle this in the engine is to use the mesh index buffer instead (see Ammo addIndex)

Well, it doesn’t matter, if it uses an index buffer or not. The reason they use false when adding a triangle, is because they know exactly that their mesh has no duplicates. The same would hold true if you remove them in Blender prior to generating the mesh.

You should never use false with the meshes that could potentially have duplicate vertices, e.g. directly using models downloaded from somewhere.

Maybe I am wrong, but when you take a look at the Ammo sample you will see that for every connected mesh there will be duplicate vertices generated, in fact it would lead to exactly the same inner structure with or without removed duplicates since 3 new vertices are added for each triangle:

 /* vertices, faces */
  let ammoMesh = new Ammo.btTriangleMesh();

  for (let i = 0, l = faces.length; i < l; i++) {
      let a = faces[i].a;
      let b = faces[i].b;
      let c = faces[i].c;
      ammoMesh.addTriangle(
          new Ammo.btVector3(vertices[a].x, vertices[a].y, vertices[a].z),
          new Ammo.btVector3(vertices[b].x, vertices[b].y, vertices[b].z),
          new Ammo.btVector3(vertices[c].x, vertices[c].y, vertices[c].z),
          false
      );
  }

The only way to generate a collision mesh without duplicate vertices (without the flag) is by using indices. In this case, vertex duplicates should be removed before hand to be 100% safe as you said.

Well, firstly, under the hood addTriangle method is using addIndex and findOrAddVertex, so there is no difference if you use them directly or using addTriangle. And secondly, the only way to make findOrAddVertex to find an existing vertex is via the mentioned flag. If you pass false to it, it will always create a new vertex in the storage. So, in the end it doesn’t matter if you use indices or not - the result will be the same.

The only way to truly use indices, is to use .addTriangleIndices(i1, i2, i3). However, it is not available in Ammo, so we can only create new vertices and skip/no-skip the check for existing ones.

You can use findOrAddVertex by just adding each vertex once (assuming there are no duplicates in the mesh itself) and then adding the 3 indices per triangle, hence no duplicates.

I implemented the approach here:
https://playcanvas.com/project/1153025

You can see the timings in the logs, indexed is the fastest and I guess use the same amount of memory as with the removeDuplicate.

So the only way to properly create physical meshes without vertex search is:

  • Have a mesh with no vertex duplicate
  • Use the indexed collision creation as I showed in the demo

Otherwise I agree, you need the flags.

But one last thing:
I don’t think its a problem to have duplicate vertices for static collision meshes, because

  1. The Ammo examples do not care
  2. Static collision detection has no inertia and does collsiion detection by triangle
  3. For dynamic (convex) meshes, duplicate vertices may be a problem
2 Likes

Ah, I see what you mean now :slight_smile: Thank you for the example! Yes, I agree with you.

I will add it to the feature request, as an implementation option.

As for the duplicate vertices - the issue will be visible in games, like golf, when you try to roll a dynamic ball on a surface with degenerate triangles. A raycast vehicle would probably be fine, as the ray should always hit the healthy triangle? Not sure. For kinematic controllers and some dynamic debris it should be fine or at least not obviously visible to player.