Hi there,
Apparently setLocalScale only affects visual mesh, what about collision? Any ways to scale/update it without reloading model? Thanks
Hi there,
Apparently setLocalScale only affects visual mesh, what about collision? Any ways to scale/update it without reloading model? Thanks
Iāve not tried this myself but you could try:
That should remove and re-add the physics body to the world
No effect unfortunately
scale = otherEntity.getLocalScale();
otherEntity.collision.enabled = false;
otherEntity.rigidbody.enabled = false;
otherEntity.setLocalScale(scale.x + 0.05, scale.y + 0.05, scale.z + 0.05);
otherEntity.collision.enabled = true;
otherEntity.rigidbody.enabled = true;
You probably need to remove the cashed mesh in order for the engine to generate a new one with a new scale. So, after you disabled the components, and before you enable them, something like:
// when using render
for (const mesh of entity.collision.render.meshes) {
delete this.app.systems.collision._triMeshCache[mesh.id];
}
// when using model
for (const instance of entity.collision.model.meshInstances) {
delete this.app.systems.collision._triMeshCache[instance.mesh.id];
}
Hi, no effect either
scale = otherEntity.getLocalScale();
otherEntity.collision.enabled = false;
otherEntity.rigidbody.enabled = false;
otherEntity.setLocalScale(scale.x + 0.05, scale.y + 0.05, scale.z + 0.05);
for (const instance of otherEntity.collision.model.meshInstances) {
delete this.app.systems.collision._triMeshCache[instance.mesh.id];
}
otherEntity.collision.enabled = true;
otherEntity.rigidbody.enabled = true;
Using debug draw
It should work. You need to debug and see why it doesnāt, if it doesnāt.
First, Iād check if it really doesnāt work by changing the scale to something much larger, e.g. multiply the current by 10 scale.x * 10
. Perhaps 0.05 is not a noticeable change. I would also try to reset the cache completely, instead of only the affected mesh.
Couldnāt get it to work, tried deleting this.app.systems.collision._triMeshCache - no luck either
Hereās repro PlayCanvas 3D HTML5 Game Engine
KEY_PAGE_UP
to increase scale/clear cache
You may need in addition to destroy the shape in Ammo:
for (const mesh of entity.collision.render.meshes) {
const shape = this.app.systems.collision._triMeshCache[mesh.id];
Ammo.destroy(shape);
delete this.app.systems.collision._triMeshCache[mesh.id];
}
Have you tried this on render? Model throws errors
Isnāt it that Ammo.destroy(this._triMeshCache[key]);
requires meshInstance key? As these are actually stored
Nevertheless, using ammo destroy with valid keys throwing āError: Cannot destroy object. (Did you create it yourself?)ā
No itās being actually used in the PlayCanvas engine, Iāve shared the link above.
The logic is itās not enough to remove the JS reference, Ammo.js occupies a different memory chunk than the one used by regular JS data. So that Ammo.destroy() call is required to clean up that memory.
Now if that solves your problem or not Iām not quite sure. But definitely it shouldnāt give any error if the object is valid. Try using the JS debugger or the console to check that you are using it as expected.
for (const instance of otherEntity.collision.model.meshInstances) {
const shape = this.app.systems.collision._triMeshCache[instance.mesh.id];
Ammo.destroy(shape);
delete this.app.systems.collision._triMeshCache[instance.mesh.id];
}
RuntimeError: indirect call to null
for (const instance of otherEntity.collision.model.meshInstances) {
const shape = this.app.systems.collision._triMeshCache[instance.key];
Ammo.destroy(shape);
delete this.app.systems.collision._triMeshCache[instance.mesh.id];
}
TypeError: a is undefined
It looks like it is not easy to change a scale of a collision mesh at runtime at the moment.
Before clearing the cache, you also want to destroy the reference stored in the collision component:
Ammo.destroy(otherEntity.collision.shape);
otherEntity.collision.shape = null;
Related issue:
I can confirm it works as expected
scale = otherEntity.getLocalScale();
otherEntity.enabled = false;
Ammo.destroy(otherEntity.collision.shape);
otherEntity.collision.shape = null;
otherEntity.setLocalScale(scale.x + 0.05, scale.y + 0.05, scale.z + 0.05);
for (const instance of otherEntity.collision.model.meshInstances) {
delete this.app.systems.collision._triMeshCache[instance.mesh.id];
}
console.log(this.app.systems.collision._triMeshCache);
otherEntity.enabled = true;
}
Thank you folks
Hi, Iām having issues with this solution. When I manually delete cached collision data in
for (const instance of otherEntity.collision.model.meshInstances) {
delete this.app.systems.collision._triMeshCache[instance.mesh.id];
}
Ammo.destroy(otherEntity.collision.shape);
Ammo will randomly (!) throw errors, like this:
Uncaught TypeError: ha[L[((L[(e >> 2)] + 8) >> 2)]] is not a function
at lh (ammo.js:32:24329)
at Array.HA (ammo.js:36:38107)
at Array.EA (ammo.js:32:119491)
at Uj (ammo.js:34:38987)
at x.addRigidBody (ammo.js:611:426)
at RigidBodyComponentSystem.addBody (playcanvas.js:56507:27)
at RigidBodyComponent.enableSimulation (playcanvas.js:56148:22)
at set mask (playcanvas.js:56020:15)
at template.js:117:24
at Array.forEach (<anonymous>)
I believe whatās happening is other pieces of my game rely on the entity in question, and when its mesh cache is destroyed, sometimes those other pieces ask Ammo for information that I destroyed using this manual call. However, this should not happen if the Ammo deletion and re-creation of the mesh happens instantly ā yet it does happen sometimes which leads me to believe that Playcanvas<>Ammo is calculating physics asynchronously (and non-deterministically) to save on performance.
The result is that this manual solution is a non-starter for my use case. I would need to disconnect each reference to that collider and pause the expectation for it to exist while this operation completes during an unknown time.
Is there any other way to scale a mesh collider without directly accessing Ammo mesh caches?
UPDATE: Iāve started a new thread here, and came up with an alternative solution which does not rely on calling Ammo.destroy (which was causing my bug) - https://forum.playcanvas.com/t/setlocalscale-collision-mesh-ammo-js-errors/40022/2
Update2: My above solution still breaks Ammo nondeterministically (out of memory or null ref, āonly sometimesā). This solution is working: Multiple Mesh Colliders of Different Scales don't behave properly Ā· Issue #3062 Ā· playcanvas/engine Ā· GitHub
Hereās a snippet to solve the scaling issue for mesh colliders, no warranties/use-at-is:
(function () {
const app = (pc.AppBase || pc.Application).getApplication();
app.once('start', () => {
pc.getScaleUniqueMeshId = (meshId, scale) => {
const rawMeshId = Math.floor(meshId);
const precision = 2; //decimal places, 1 means rounding to 0.1, 2 to ~0.01, 3 to ~0.001 etc.
const roundingPower = Math.pow(10, precision);
let x = Math.abs(scale.x * roundingPower);
let y = Math.abs(scale.y * roundingPower);
let z = Math.abs(scale.z * roundingPower);
return Number(`${rawMeshId}.${x}${y}${z}`);
};
if (typeof Ammo === 'undefined' || !app.systems.collision.implementations.mesh) return console.log('[MeshCollisionSystemImpl] could not detect Ammo');
const originalCreateAmmoMeshFunction = app.systems.collision.implementations.mesh.createAmmoMesh.toString();
if (originalCreateAmmoMeshFunction.includes('mesh.id')) {
const patchedCreateAmmoMeshFunction = originalCreateAmmoMeshFunction.replaceAll('mesh.id', 'pc.getScaleUniqueMeshId(mesh.id, scale)').replaceAll('SEMANTIC_POSITION', 'pc.SEMANTIC_POSITION');
console.log('[PATCH] MeshCollisionSystemImpl has been patched to support mesh collider scaling');
app.systems.collision.implementations.mesh.createAmmoMesh = new Function('return ' + patchedCreateAmmoMeshFunction)();
} else {
console.warn('[PATCH] Could not patch MeshCollisionSystemImpl: mesh.id is not used in this version of the engine');
}
})
})();