PlayCanvas physics extension

Sometimes there comes a need to detect a collision, when simple raycast is not suitable. For example, when you want to check if camera sphere can fit into that new location, or a car can pass between two trees without hitting them.

To manage such cases, you would want to cast a sphere along the path or some sort of a shape, that would detect the collision. I’ve made a physics extension to do just that:

Example project. (Click to change shapes. An invisible shape is a custom shape - convex triangle. Check example sources for details.)

How to install
No intallation needed. Just download rigidbody-extensions.js from the example project and drop it anywhere in your project.

How to use
You can refer to example.js to see how to cast a shape for collision detection.

// Example sphere cast:
// radius - sphere radius
// start - position from where to start the cast
// end - position where the cast should end
var result = this.app.systems.rigidbody.sphereCast(radius, start, end);
// result will contain the sweep test result or null if no collision

Supported shapes

Sphere:       .sphereCast(radius, start, end)
Cone:         .coneCast(radius, height, start, end)
Box:          .boxCast(halfExtents, start, end)
Cylinder:     .cylinderCast(halfExtents, start, end)
Capsule:      .capsuleCast(radius, height, start, end)
Custom shape: .shapeCast(vertices, start, end)

Advanced use
The .shapeCast() method shows how to create your own custom convex shape and use it for the sweep test. Here, I provide 3 consecutive vertices, which comprise a triangle shape:

var triangle = new Float32Array([ 1, -1, 0, -1, -1, 0, 0, 1, 0 ]);
result = this.app.systems.rigidbody.shapeCast(triangle, start, end);

All of the mentioned methods for shape casting are simply convenience methods that use one method only.

this.app.systems.rigidbody.convexCast();

Their responsibility is to generate a desired shape and pass it to the .convexCast() method for sweep test. It requires to be passed a shape, starting position of the sweep and the end position. All other arguments are optional:

@param {Ammo shape} shape - Convex shape used for sweep test.
@param {pc.Vec3} startPos - The world space point where the hit test starts.
@param {pc.Vec3} endPos - The world space point where the test ends.
@param {pc.Quat} [startRot] - Initial rotation of the shape.
@param {pc.Quat} [endRot] - Final rotation of the shape.
@param {number} [allowedPenetration] - CCD allowance margin.
@param {boolean} [findEntity] - Flag to do a separate raycast to return an entity.

Notes

  • Some shape methods take an optional margin attribute. It is for adjusting the collision shape margin, for example, you can make rounded corners in a box shape. The engine sets a default safe margin value (something like 0.03-0.04). If your shape is very small, you would want to adjust it.
  • You can optionally specify the initial and final rotations of the shape by providing the related pc.Quat quaternions. The extesion will default the rotations of the shape to face towards the end position of the sweep.
  • The collision detection is performed using the CCD method, which allows you to provide a collision penetration allowance via allowedPenetration attribute. If your object is moving at high speed, you would want to adjust this value to avoid passing through an obstacle. Defaults to 0.
  • By default the result will provide you the point in world space where collision occured, the normal of the surface that was hit and the hit fraction. The hit fraction is basically a normalized value of the sweep path location, where the collision occured. For example, it will be 0.5 if the collision occured in the middle of the sweep path.
  • The method allows you to make a separate rraycast test to find the entity at the hit point. You can pass findEntity as true to include the hit entity into the cast result. Deafults to false, because it is expensive.
  • Some shape generation methods accept an optional orientation axis. You can provide pc.Vec3.RIGHT to generate the shape around the X axis, or pc.Vec3.BACK for Z axis. Defaults to pc.Vec3.UP.
12 Likes

Pretty cool.

There is a known issue with my extension that Ammo will occasionally throw an OOM assertion when you do a convex sweep test. I am not sure what is causing it. I will probably need to build Ammo with debug enabled flags for that and see if this is something I can fix. Also, if you notice a bug in my convexCast() method, please, let me know.

Just be aware and use with caution untill then.

Edit:
I think I found the cause. Most probably OOM is thrown because I need to re-use the shape objects I am generating, instead of creating new ones. Fix will come.

Edit2:
Fixed.

3 Likes

Added PR for ammo.idl to include the collision object in the result of the convex sweep test. This will allow to easily access the hit objects. I’ve updated the original example with a custom Ammo version that exposes the binding.

Edit: The PR has been merged to ammo.js, so this extension will work with Playcanvas, after the next physics engine update.

4 Likes

Forked and edited project:
https://playcanvas.com/editor/scene/1051938

Clean project with Shape Cast implemented: (With the error)
https://playcanvas.com/editor/scene/1054155

I forked your project and it worked fine! Hats off and thanks for the addition!
But I encounterd a problem when I implemented it into an existing project.

I cannot link the original project because that is currently under NDA.
But I have made a new clean project with the Shape Cast implemented to showcase the error.

[ammo.wasm.js?id=39880806&branchId=f821a1b4-92cc-4a5e-a0e6-60ddeb1a94e4&t=5f1ca9b26b6b35810ab6da57160924cf:75]: Uncaught TypeError: Cannot read property 'apply' of undefined

TypeError: Cannot read property 'apply' of undefined
    at b._emscripten_bind_btVector3_btVector3_0 (https://launch.playcanvas.com/api/assets/files/Ammo/ammo.wasm.js?id=39880806&branchId=f821a1b4-92cc-4a5e-a0e6-60ddeb1a94e4&t=5f1ca9b26b6b35810ab6da57160924cf:75:73)
    at new p (https://launch.playcanvas.com/api/assets/files/Ammo/ammo.wasm.js?id=39880806&branchId=f821a1b4-92cc-4a5e-a0e6-60ddeb1a94e4&t=5f1ca9b26b6b35810ab6da57160924cf:500:137)
    at https://launch.playcanvas.com/api/assets/files/convex-cast.js?id=39880631&branchId=f821a1b4-92cc-4a5e-a0e6-60ddeb1a94e4:28:69
    at https://launch.playcanvas.com/api/assets/files/convex-cast.js?id=39880631&branchId=f821a1b4-92cc-4a5e-a0e6-60ddeb1a94e4:247:3

And I encounterd this error my original game project:

[ammo.wasm.js?id=39875743&branchId=eabadd1b-ade9-47ce-88c7-1b55ed37c503&t=5f1ca9b26b6b35810ab6da57160924cf:500]: Uncaught TypeError: Xd is not a function

TypeError: Xd is not a function
    at new p (https://launch.playcanvas.com/api/assets/files/Ammo/ammo.wasm.js?id=39875743&branchId=eabadd1b-ade9-47ce-88c7-1b55ed37c503&t=5f1ca9b26b6b35810ab6da57160924cf:500:137)
    at https://launch.playcanvas.com/api/assets/files/scripts/bekhoUtils/extensions.js?id=38035335&branchId=eabadd1b-ade9-47ce-88c7-1b55ed37c503:2:24
    at https://launch.playcanvas.com/api/assets/files/scripts/bekhoUtils/extensions.js?id=38035335&branchId=eabadd1b-ade9-47ce-88c7-1b55ed37c503:35:3

These errors came up when I pressed the “Import Ammo” button in the project settings and copied the ammo files from the Shape Cast project, then overwrited the Ammo files in my original game project.

You do refer to a “rigidbody-extensions.js” file, is that required for the project to run?
I cannot find it anywhere in the example project!

Hopefully explained the problem in an understandable way (This is my first ever post on a forum)
There is time pressure behind it so I hope to hear from you soon!

Hi and welcome to Playcanvas forums!

Hmm, the rigidbody-extensions.js was an old name of the extension file. I no longer can edit the original post, unfortunately. The filename of the extension is convex-cast.js and should be used together with the Ammo build, found in the example project.

I forked your project, copied Ammo folder and convex-cast.js from my project:
https://playcanvas.com/editor/scene/1054547
I cannot reproduce the error from your project. Please, check.

Hi. I tried downloading the 2 js files in the Ammo folder and the convex-cast.js file and dragging them into my project - overwriting the existing Ammo js files, but when I launch my project I get the following error:

[ammo.wasm.js?id=40069571&branchId=40bacc64-68d3-4329-ba4d-dd927e1b3b29&t=b40390ef531c12c1a0343200ae0b5a0b:75]: Uncaught TypeError: Cannot read property 'apply' of undefined

TypeError: Cannot read property 'apply' of undefined
    at b._emscripten_bind_btVector3_btVector3_0 (https://launch.playcanvas.com/api/assets/files/Ammo/ammo.wasm.js?id=40069571&branchId=40bacc64-68d3-4329-ba4d-dd927e1b3b29&t=b40390ef531c12c1a0343200ae0b5a0b:75:73)
    at new p (https://launch.playcanvas.com/api/assets/files/Ammo/ammo.wasm.js?id=40069571&branchId=40bacc64-68d3-4329-ba4d-dd927e1b3b29&t=b40390ef531c12c1a0343200ae0b5a0b:500:137)
    at https://launch.playcanvas.com/api/assets/files/Scripts/convex-cast.js?id=40068938&branchId=40bacc64-68d3-4329-ba4d-dd927e1b3b29:28:69
    at https://launch.playcanvas.com/api/assets/files/Scripts/convex-cast.js?id=40068938&branchId=40bacc64-68d3-4329-ba4d-dd927e1b3b29:247:3

is there a proper way of exporting the Ammo files and the extension into my project that I’m missing? I also get errors if I use the existing Ammo build from the standard import, or if I also copy the wasm file and attempt to relink the glue and fallback script.

Am I missing a step?

  1. Open your project and delete following:
  • Ammo folder
  • rigidbody-extensions.js file
  1. Open my project and holding CTRL select Ammo folder and convex-cast.js file
  2. Press CTR+C to copy
  3. Open your project, and press CTRL+V
  4. Launch
2 Likes

Perfect! Thanks!

1 Like

Does anyone know if this is still working with the current version of Playcanvas?

Getting a error with get_m_hitCollisionObject not being defined.

These are the only get_ functions on the convexCallback object.
image

Thanks in advance!

1 Like

The current version of Ammo that PlayCanvas is using is not recent enough for this to work. Until the team updates it, you can grab a newer Ammo version from my example project.

2 Likes

Thanks, worked perfect!

Would you happen to know if convexCallback.get_m_convexToWorld() returns the center origin of the primitive when it hits?

Edit: nvm, that is the end position of the cast. I am trying to solve the center point of the casted sphere. I am sure I could just take the hit point and find the intersection of the ray direction. But I am wondering if there is a value already calculated.

The ConvexCastResult object that you receive has one property called hitFraction. That is a normalized distance along the ray, where the sphere hit something. Say, your ray start is 0, and your ray end is 1, then hit fraction of 0.5 means the sphere hit something when it was in the middle of the ray.

In my example, I use that value to draw a yellow line from the ray to the hit point:

Where that line starts is your sphere center. You can check the code in the example.js, but pseudo code would be:

var direction  = rayStart.sub( rayEnd ).normalize();
var distance   = rayStart.distance( rayEnd );
distance       *= hitFraction;
direction.scale( distance );

var point      = rayStart.add( direction );
3 Likes

Thanks again, works perfect.
This helped me make a smooth camera collision system, performance is near identical to raycastFirst for me at 0.02 ms per calculation.

// get raycast forward dir
const dir = this.cameraRaycastForward
    .copy(end)
    .sub(start)
    .normalize();

// get distance to hit shape center origin 
const distance = start.distance(end) * result.hitFraction;

// get point from direction scaled by origin distance
const point = dir
    .scale(distance)
    .add(start);

this.entity.setPosition( point ); 
1 Like

hello Mr @LeXXik

Your extension is really useful and I am trying to get it to work for jumping. I have a character rigidbody with a box collider and I want the player to jump even if the player edge is off the side so I wanted to use your extension. A simple raycast is not enough for a box collider so this was very useful for me however I can not get it to work as it always jumps indefinitely.

Below is a sample code I wrote which works for raycastFirst and raycastAll but does not work when using boxCast because it jumps all the time even though there is no collision, I am wondering if I did something wrong.

PlayerJumping.prototype.jump = function() {

    var halfExtents = this.playerCollision.collision.halfExtents;
    var start = this.playerCollision.getPosition();
    var end = this.entity.getPosition();

    // setup raycaster underneath player
    this.jumpRaycastEnd.copy(end);
    this.jumpRaycastEnd.y += 0;

    var result = this.app.systems.rigidbody.boxCast(halfExtents, start, this.jumpRaycastEnd);

    if (result) {
        this.canJump = true;
    }
    else {
        this.canJump = false;
    }

    if (this.canJump) {
        this.entity.rigidbody.applyImpulse(0, 1000, 0);
    }

    this.app.drawLine(start, end, pc.Color.GREEN);
};

that is the case because the ray cast counts ALL rigid-bodies including the player so you got to check the name of the entity and if it doesn’t equal the player name

1 Like

Thanks I updated the result condition with:

if (result && result.entity.rigidbody && result.entity !== this.entity)

Now it does not multi jump and only jumps once but sometimes it does not jump at all when pressing space

1 Like

You want to debug the issue. Output to console the contents of result and inspect its contents, then compare it with your if logic and see where it breaks. You might want to compare against entity name, e.g. (pseudo code):

if (entity.name !== 'Player') do something
1 Like

Sometimes when I load my project and launch this error message happens but it is fixed when I refresh the project. Why does this happen sometimes? Does the ammo.wasm not load properly?

I already have the updated ammo version.

The error is not helpful, unfortunately. If you could create a simply project to reproduce, I could take a look.