engine icon indicating copy to clipboard operation
engine copied to clipboard

Implement simple API to allow Meshes / Renders to be used as a Collider

Open mvaligursky opened this issue 3 years ago • 10 comments

  • currently the Collider can accept Render / Model assets, or an instance of the model, which is not convenient for dynamically generated meshes
  • Ammo tri-meshes are cached in the _triMeshCache and don't get removed, causing a leak. This should be implemented using ref counting similarly to how MeshInstance references a Mesh - when there are no references to tri-mesh, it needs to be properly released from the memory

the complete story: https://forum.playcanvas.com/t/procedural-mesh-collision-generation-and-updates/25113

mvaligursky avatar Apr 07 '22 14:04 mvaligursky

To add to this:

At some point I had a need to use a convex hull for the shape. It can be generated via a convex decomposition by Ammo, where the input would still be a btTriangleMesh, but instead of using it to generate btBvhTriangleMeshShape like the collision system does, it would be used for btConvexHullShape:

const tmpConvexShape = new Ammo.btConvexTriangleMeshShape(triMesh);
const btConvexHullShape = new Ammo.btConvexHullShape(tmpConvexShape);
const btHull = new Ammo.btShapeHull(btConvexHullShape);
const margin = tmpConvexShape.getMargin();
btHull.buildHull(margin);

for (let i = 0; i < numTriangles; i++) {
    i1 = indices[base + i * 3] * stride;
    i2 = indices[base + i * 3 + 1] * stride;
    i3 = indices[base + i * 3 + 2] * stride;
    v1.setValue(positions[i1], positions[i1 + 1], positions[i1 + 2]);
    v2.setValue(positions[i2], positions[i2 + 1], positions[i2 + 2]);
    v3.setValue(positions[i3], positions[i3 + 1], positions[i3 + 2]);

    btConvexHullShape.addPoint(v1, false);
    btConvexHullShape.addPoint(v2, false);
    btConvexHullShape.addPoint(v3, false);
}

btConvexHullShape.recalcLocalAabb();

Ammo.destroy(tmpConvexShape);
Ammo.destroy(btHull);

At the time, I remember that I had a thought it would be great if the Collision Component would have another type. Apart from primitive types, like box or sphere, it has only one option - "mesh". Which then uses btBvhTriangleMeshShape, that is used for static meshes. Perhaps, some kind of "user" shape type would be great, that would require the developer to provide the collision shape. There are quite a few that are a better fit for different purposes:

https://pybullet.org/Bullet/BulletFull/dir_0c2384ea07a1f9c912f061d6c891619f.html

LeXXik avatar Apr 07 '22 15:04 LeXXik

  • Ammo tri-meshes are cached in the _triMeshCache and don't get removed, causing a leak. This should be implemented using ref counting similarly to how MeshInstance references a Mesh - when there are no references to tri-mesh, it needs to be properly released from the memory

That is actually intended. And clearing a cache should be thought through, and a call by a developer. Developers can have a template of a car model with tri-mesh let's say, and they can instantiate them, then destroy, then instantiate again. And the only first time it will generate tri-mesh. But we cannot guarantee that there will be always at least one instance of it. So if application logic will have times with zero instances, this will lead to cache clearing, but that will lead to stalls next time we will instantiate the car model again.

So it should be a developer call to clear cache based on application logic, not automatic.

Maksims avatar Apr 08 '22 12:04 Maksims

Somewhat related, as it is a conceptional decision whether it's the responsibility of the engine vs. the developer: https://github.com/playcanvas/engine/issues/1751

dexterdeluxe88 avatar Apr 08 '22 16:04 dexterdeluxe88

That is actually intended. So it should be a developer call to clear cache based on application logic, not automatic.

Not sure, if I agree here - the _triMeshCache is a variable private to the implementation system, indicated by the underscore. It is not supposed to be accessed outside of it. I doubt a developer is aware of it, unless they study the source code. If it is up to the developer to clear it, there should be a public API. Otherwise, the engine should handle it.

LeXXik avatar May 22 '22 12:05 LeXXik

Would it be helpful to split this issue into 2 separate ones? It would be super useful to use a mesh/mesh instance as a collider. The cache seems to be a separate issue.

jbromberg avatar Feb 06 '23 19:02 jbromberg

It's likely these would be implemented at the same time. But if somebody wants to develop just one part of it, they can create a PR just for that, there is no problem.

mvaligursky avatar Feb 06 '23 20:02 mvaligursky

Related: #6265

LeXXik avatar Apr 29 '24 08:04 LeXXik

I would also like to see better support for procedurally generated geometry. I'm porting my mesh fracturing library over to PlayCanvas and it's been very difficult figuring out how to massage the mesh data into a form that the CollisionComponent will accept.

dgreenheck avatar Feb 10 '25 15:02 dgreenheck

I would also like to see better support for procedurally generated geometry. I'm porting my mesh fracturing library over to PlayCanvas and it's been very difficult figuring out how to massage the mesh data into a form that the CollisionComponent will accept.

This is very much needed! In the meantime sharing some pseudo-code on how to pass a custom mesh to the collision component the way we've been using it in our games (tested in PC v1 releases):

const mesh = new pc.Mesh(this.app.graphicsDevice);
mesh.setPositions(positions);
mesh.setNormals(normals);
mesh.setUvs(0, uvs);
mesh.setIndices(indices);
mesh.update();

this.entity.collision.render = {
   meshes: [mesh]
};

leonidaspir avatar Feb 10 '25 16:02 leonidaspir

This is very much needed! In the meantime sharing some pseudo-code on how to pass a custom mesh to the collision component the way we've been using it in our games (tested in PC v1 releases):

const mesh = new pc.Mesh(this.app.graphicsDevice); mesh.setPositions(positions); mesh.setNormals(normals); mesh.setUvs(0, uvs); mesh.setIndices(indices); mesh.update();

this.entity.collision.render = { meshes: [mesh] };

Thanks for this! Much cleaner approach than what was shared in the linked forum thread since you don't need to manually create a Render asset.

dgreenheck avatar Feb 10 '25 16:02 dgreenheck