we are currently trying to implement procedural mesh generation including collisions for our current project.
We have managed to implement a working solution, however we had to resort to some workarounds to get everything to work as expected. Since these workarounds seem quite fragile, we wanted to consult with you, whether this is the actual best way to implement this.
For our project we want to be able to generate a custom mesh from code, which will be used both for rendering as well as for physics collisions. We also want to be able to dynamically update this mesh at runtime, incuding its collision shape.
Current Implementation and encountered Problems
In order to achieve this we manually calculate the vertex positions, indices, uvs and normals and create a new pc.Mesh object via
pc.createMesh. In order to render the mesh, we are using
pc.RenderComponent and in order to have physics collisions we also add a
When we update our mesh, we first recalculate positions, indices, uvs and normals accordingly and reset them with
setNormals, etc. and finally call
1. CollisionComponent in combination with RenderComponent
Now comes the tricky part: we can directly assign a
CollisionComponent will only either accept a
ModelComponent (which is legacy according to the editor) or a
renderAsset. Since our mesh is procedurally generated, we don’t have an existing asset, but only a mesh.
We wanted to avoid using the legacy
ModelComponent, thus we decided to create a new
pc.Asset from code when the mesh is generated for the first time and assign it to
RenderComponent.asset as well as
I.e. something like this:
let asset = new pc.Asset('Custom Name', 'render'); // Some mesh initialization code here (see next code sample) pc.Application.getApplication().assets.add(asset); this.renderComponent.asset = asset; this.collisionComponent.renderAsset = asset;
Do we really have to create a Render Asset by hand in order to be able to use the
CollisionComponent with a custom mesh without having to use the legacy
2. Creating a Render Asset from Code: can’t create Render object
In order to be able to set our custom mesh to the created RenderAsset, we have to set
asset.resource to a new
Render object and set its
meshes field accordingly. However, for some reason we are not able to access the
Render class in order to create a new instance of it ourselves. Writing
new pc.Render() will yield an error. We had to dig into the engine code quite a while in order to find a way to create a new
pc.Render instance and this was the best we could come up with (and this is where it gets really hacky):
let render = pc.app.loader.getHandler('render').open(undefined, undefined); render.meshes = [this.mesh]; asset.resource = render;
Is there any better way for us to create a new
Render instance, or to set the mesh of the RenderAsset?
3. Asset Loading of Procedural Asset: have to manually set loaded
We noticed some other strange behaviour when setting the
RenderComponent.asset to our
asset we just created. This led to
render.meshes being reset to
null for some reason. In order to avoid this, we had to manually set
asset.loaded to true first. I.e.:
// Asset creation as shown earlier asset.loaded = true; this.renderComponent.asset = asset; this.collisionComponent.renderAsset = asset;
Is this behaviour expected?
4. Updating Collision Mesh causes Ammo Memory Leak
When changing the mesh, we update its data as described earlier and then also call
pc.CollisionComponentSystem in order to have the
CollisionComponent update its collision shape accordingly. Unfortunately, this will not update the collision mesh and the CollisionComponent will still have its initial collision shape. When we tried to implement the same approach with
ModelComponent instead and resetting the mesh each time we update it, the collision shape actually updated, but an OOM Exception would be thrown by Ammo after a couple of hundred updates.
After digging into the engine code, we noticed that PlayCanvas’
CollisionComponentSystem creates an internal cache of all
_triMeshCache), which is actually never cleared (except
onDestroy of the entire system). Thus, when creating a lot of collision meshes, we will eventually run OOM, even when destroying the ‘old’ meshes using
pc.Mesh.destroy() everytime. This
_triMeshCache also prevents us from updating the collision mesh, since PlayCanvas will perform a lookup using
Mesh.id, which didn’t change when updating the mesh using
pc.Mesh.update() and thus will continue using the old collision mesh from the cache.
In order to workaround this problem, we had to manually destroy the old Ammo collision mesh from the
_triMeshCache and delete the entry from the cache dictionary:
let collisionSystem = this.collisionComponent.system; Ammo.destroy(collisionSystem._triMeshCache[this.mesh.id]); delete collisionSystem._triMeshCache[this.mesh.id]; collisionSystem.recreatePhysicalShapes(this.collisionComponent);
Did we miss anything here, or is this currently the only way to procedurally update a collision mesh without leaving memory leaks?
We would really appreciate your input on these questions. We would like to avoid having our code break in the future due to our workarounds relying on private APIs / hidden engine features.
Thank you for your time and efforts!