Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for shapeCast and merged raycastFirst and raycastAll into raycast #5039

Open
wants to merge 100 commits into
base: main
Choose a base branch
from
Open
Changes from 2 commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
ac625fc
Added shape casts
MushAsterion Feb 3, 2023
4fd6099
Fixed ESLint
MushAsterion Feb 3, 2023
c5f8fc8
Fixed halfExtends into halfExtents
MushAsterion Feb 3, 2023
36d500a
Added shapecast shape destroying
MushAsterion Feb 3, 2023
624c8da
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 3, 2023
0e51d63
Reduced debug assets into one
MushAsterion Feb 3, 2023
2ace6a3
Removed variable type change from shapeCasts
MushAsterion Feb 3, 2023
2b1e039
Fixed JSDoc for optional parameters
MushAsterion Feb 3, 2023
0cdc17a
Added default position and rotation for _shapecast
MushAsterion Feb 3, 2023
052e8f8
Removed shapecast body world addition
MushAsterion Feb 3, 2023
7eff8db
Shape casts now return Entity array
MushAsterion Feb 3, 2023
5de3fef
Removed unused variables
MushAsterion Feb 3, 2023
9133182
Added Entity import
MushAsterion Feb 3, 2023
1e4415e
Fixed Entity JSDoc
MushAsterion Feb 3, 2023
53cb43f
Updated shape.type eval to switch
MushAsterion Feb 3, 2023
b4f005b
Fixed docs
MushAsterion Feb 3, 2023
502994c
Fix shape on boxCast
MushAsterion Feb 4, 2023
c8adb46
Added HitResult to shapecast
MushAsterion Feb 4, 2023
fd907d5
Fixed inconsistent documentation
MushAsterion Feb 4, 2023
9566b60
Fixed ESLint
MushAsterion Feb 4, 2023
6c2d55b
Changed naming from cast to test
MushAsterion Feb 4, 2023
9027bf3
Added Quat rotation for shape tests
MushAsterion Feb 4, 2023
c294abb
Fixed ESLint
MushAsterion Feb 4, 2023
845fb0a
Added sphereCast and boxCast
MushAsterion Feb 4, 2023
34d83b3
Fixed typo
MushAsterion Feb 4, 2023
a448e62
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 4, 2023
fcb70ea
Fixed sphereCast length
MushAsterion Feb 4, 2023
6213685
Added vector for shapecast position
MushAsterion Feb 5, 2023
a444069
Fixed boxCast shape halfExtents
MushAsterion Feb 5, 2023
3ae5522
Fixed typo
MushAsterion Feb 5, 2023
dc69842
Renamed boxCastAll and sphereCastAll
MushAsterion Feb 5, 2023
2d8149a
Fixed sphereCast length
MushAsterion Feb 5, 2023
8134d3f
Removed boxCastAll
MushAsterion Feb 5, 2023
87451da
Removed shapetest body deactivation
MushAsterion Feb 6, 2023
d66eaaf
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 6, 2023
bf5c384
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 8, 2023
73b10a7
Rename shape casts into shapeCastAll
MushAsterion Feb 8, 2023
2fc5fa8
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 10, 2023
3df7a28
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 11, 2023
d6d4539
Changed RaycastResult into HitResult
MushAsterion Feb 11, 2023
a9ea632
Changed RaycastResult into HitResult
MushAsterion Feb 11, 2023
db8274b
Changed RaycastResult into HitResult
MushAsterion Feb 11, 2023
faf1f00
Deprecated RaycastResult
MushAsterion Feb 11, 2023
6474880
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 12, 2023
e084be1
Added shapeCastFirst functions
MushAsterion Feb 22, 2023
c344241
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 22, 2023
2759da4
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 22, 2023
cf21ed8
Changed T and C to lowercase from Test and Cast
MushAsterion Feb 22, 2023
f5d91b6
Reverted previous commit
MushAsterion Feb 22, 2023
8ca124e
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 22, 2023
00d2d67
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 24, 2023
1ccc26a
Reduced code length
MushAsterion Feb 24, 2023
a4a8305
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Mar 4, 2023
54518b1
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Mar 10, 2023
73b751c
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Mar 11, 2023
77b3ed0
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Mar 17, 2023
956fd3e
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Mar 18, 2023
377f5ae
Added support for `shapeTestFirst`
MushAsterion Mar 19, 2023
383e9ad
Resolve conflicts from source
MushAsterion Mar 21, 2023
390bac6
Merge remote-tracking branch 'upstream/main' into shapecasts
MushAsterion Mar 21, 2023
e5c7a09
Match latest features from raycasting
MushAsterion Mar 21, 2023
9802d4c
Added sorting documentation.
MushAsterion Mar 21, 2023
6bd474b
Merge remote-tracking branch 'upstream/main' into shapecasts
MushAsterion Mar 22, 2023
ea47f72
Merge remote-tracking branch 'upstream/main' into shapecasts
MushAsterion Mar 22, 2023
969ab5b
Added filtering to shape testing and casting
MushAsterion Mar 22, 2023
290a35d
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Mar 25, 2023
7c1b2c1
Merge remote-tracking branch 'upstream/main' into shapecasts
MushAsterion Apr 2, 2023
20f6377
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Apr 8, 2023
6fc799a
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Apr 13, 2023
4c70791
Fixed tags filtering issue
MushAsterion Apr 17, 2023
e6a47a3
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Apr 17, 2023
230e1fa
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Apr 18, 2023
f0ab87b
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Apr 26, 2023
79a67ee
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Apr 29, 2023
ecf1583
Merge branch 'playcanvas:main' into shapecasts
MushAsterion May 10, 2023
74f509a
Merge branch 'playcanvas:main' into shapecasts
MushAsterion May 12, 2023
f23587a
Merge branch 'playcanvas:main' into shapecasts
MushAsterion May 28, 2023
3c3fa50
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Jun 20, 2023
13a7c28
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Jun 28, 2023
fa155c4
Merge remote-tracking branch 'upstream/main' into shapecasts
MushAsterion Jul 24, 2023
f373ac7
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Jul 28, 2023
f955f64
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Jul 31, 2023
ff3906b
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Aug 10, 2023
b96694a
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Aug 17, 2023
ece932e
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Oct 16, 2023
d442376
Merge remote-tracking branch 'upstream/main' into shapecasts
MushAsterion Oct 31, 2023
a1a5473
Merge branch 'playcanvas:main' into shapecasts
MushAsterion Feb 14, 2024
8c7a842
Merge remote-tracking branch 'upstream/main' into shapecasts
MushAsterion Mar 29, 2024
2b2b643
Fixed trailing space
MushAsterion Mar 29, 2024
654794e
Merge branch 'playcanvas:main' into shapecasts
MushAsterion May 11, 2024
02e33bf
Merge branch 'playcanvas:main' into shapecasts
MushAsterion May 29, 2024
7bae7dc
Merge branch 'main' into shapecasts
MushAsterion May 30, 2024
c5e35d6
Reduced public API functions
MushAsterion May 31, 2024
ba29b58
Used Object assign instead of ...
MushAsterion May 31, 2024
8c67c9f
Fixed documentation disclaimers
MushAsterion May 31, 2024
b3f213d
Changed raycastFirst/raycastAll occurrences
MushAsterion May 31, 2024
7a642a8
Removed extra Ammo.destroy
MushAsterion May 31, 2024
972c1f7
Fixed documentation and line width
MushAsterion May 31, 2024
ec7aa86
Merge branch 'main' into shapecasts
MushAsterion May 31, 2024
bae27b7
Merge branch 'main' into shapecasts
MushAsterion Jun 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
246 changes: 244 additions & 2 deletions src/framework/components/rigid-body/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import { Vec3 } from '../../../core/math/vec3.js';
import { Component } from '../component.js';
import { ComponentSystem } from '../system.js';

import { BODYFLAG_NORESPONSE_OBJECT } from './constants.js';
import { BODYFLAG_NORESPONSE_OBJECT, BODYGROUP_TRIGGER, BODYMASK_ALL, BODYSTATE_ACTIVE_TAG, BODYSTATE_DISABLE_DEACTIVATION } from './constants.js';
import { RigidBodyComponent } from './component.js';
import { RigidBodyComponentData } from './data.js';

let ammoRayStart, ammoRayEnd;
// Ammo.js variable for performance saving.
let ammoRayStart, ammoRayEnd, ammoVec3, ammoQuat, ammoTransform;

// RigidBody for shape casts. Permanent to save performance.
let shapecastBody;

/**
* Object holding the result of a successful raycast hit.
Expand Down Expand Up @@ -347,6 +351,10 @@ class RigidBodyComponentSystem extends ComponentSystem {
// Lazily create temp vars
ammoRayStart = new Ammo.btVector3();
ammoRayEnd = new Ammo.btVector3();
ammoVec3 = new Ammo.btVector3();
ammoQuat = new Ammo.btQuaternion();
ammoTransform = new Ammo.btTransform();

RigidBodyComponent.onLibraryLoaded();

this.contactPointPool = new ObjectPool(ContactPoint, 1);
Expand Down Expand Up @@ -552,6 +560,240 @@ class RigidBodyComponentSystem extends ComponentSystem {
return results;
}

/**
* Perform a collision check on the world and return all entities the shape hits.
* It returns an array of {@link RaycastResult}, one for each hit. If no hits are
* detected, the returned array will be of length 0.
*
* @param {object} shape - The shape to use for collision.
* @param {number} shape.axis - The local space axis with which the capsule, cylinder or cone shape's length is aligned. 0 for X, 1 for Y and 2 for Z. Defaults to 1 (Y-axis).
* @param {Vec3} shape.halfExtends - The half-extents of the box in the x, y and z axes.
MushAsterion marked this conversation as resolved.
Show resolved Hide resolved
* @param {number} shape.height - The total height of the capsule, cylinder or cone from tip to tip.
* @param {string} shape.type - The type of shape to use. Available options are "box", "capsule", "cone", "cylinder" or "sphere". Defaults to "box".
* @param {number} shape.radius - The radius of the sphere, capsule, cylinder or cone.
* @param {Vec3} position - The world space position for the shape to be.
* @param {Vec3} rotation - The world space rotation for the shape to have.
*
* @returns {RaycastResult[]} An array of shapecast hit results (0 length if there were no hits).
MushAsterion marked this conversation as resolved.
Show resolved Hide resolved
*/
shapeCast(shape, position, rotation) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is a utility function that basically calls the shape-specific functions. For the purpose of maintaining a nice and accessible API, should we:

  1. just retain this 'all-in-one' function as public
  2. just retain the shape-specific function as public
  3. keep all public

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The advantage of "all-in-one" function is that you can actually use a JSON format for all the shape config meaning it can easily be assigned from a script attribute to edit it within the editor directly and avoid having to be only code-based. In another hand the shape-specific function allow users used to other engines to find the function easily (especially sphereCast which is really common).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that's a great point. OK, fair enough. 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to unresolve this discussion and talk about it some more, @MushAsterion. I do still feel that this PR introduces a lot of additions to the API. 18 methods at the moment. And if shapeCastAll flavors get added, it takes it to 24. Plus the two existing raycasting functions, that'd be 26 functions for testing and casting. I think that's just too much. We could just have:

raycast (we could deprecate raycastFirst and raycastAll - note we don't _have_ to do that in this PR)
shapeCast
shapeTest

The all versus first could just be an option (maybe defaulting to first).

It just feels to me that the JSON approach is interesting, but it's very niche and, in reality, the vast majority of developers won't call the API like that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could potentially also drop shapeTest if start and end pos/rot of shapeCast where the same too maybe?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shapeTestFirst is doing more work due to how Bullet/Ammo work and completely different from raycasting as raycasting is only a question of point while the closest point in a shape's position is not always the first to be detected since it depends on how the physics engine perform the test.

Once again the exposure of First/All methods is to match with what the engine has already implemented. I'm an external contributor so I don't want to step or change core project features. I personally find it handy for beginners to have the support for sure but as @willeastcott mentioned it's a very long list of new public methods which might also bring more confusion. I think it's a good solution to merge as much as possible to reduce public methods but I'm concerned about how easy it would be for people that are just looking to make a quick project without trying to get a deep understanding of the engine's API... I'll surely update to fit whatever they think is best anyway, the core features of the PR are already done and updating them is not a big deal

Copy link
Contributor

@LeXXik LeXXik May 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff! Many useful features are being added. Although, I do agree that the API is too verbose. We can simplify those, and move most of the customization stuff to options.

My vote goes for dropping -all/-first from the method name, and use an option setting.

shapeTestFirst seems a bit superfluous. We can just return everything and let user decide what to do with the results. If future Ammo adds a feature to allow collecting only the first intersection (which sounds strange to me), we can add it via an option. Shape and point collision tests are usually useful for getting all colliders they intersect, otherwise a sweep test is used.

About shape target rotation during sweep. Personally, I've never met a need to use it. Maybe it is useful in robotics, where Bullet engine is popular? No idea. Most of the other other physics engines don't allow it (Rapier, PhysX, Jolt). As such, I would actually move it to options as well.

Whatever the names are, but signatures could look something like:

raycast(start, end, opts = {});

shapecast(shape, pos, rot, dir = Vec3.ZERO, opts = {});

If dir is a zero vector, we do shape test. Otherwise it is a non-normalized cast direction (its normal for direction, its magnitude as cast distance). Shape properties can be part of the options, e.g. radius for radius-based shapes. We could also use the end position instead and check if it is same as start position, as Will suggested. Perhaps would be a better match to raycast then.

About destroying a shape used in sweep. If a shape is not auto-destroyed (e.g. via some option), then there should be a method to destroy it later. Never mind, it is for a user created one, which he is in control of.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to understand the point of changing endPosition into dir while raycast has a end param, is there any particular reason to make it different?

Something like

shapecast(shape, start, end, opts = {})

Where options could have something like

{
    startRotation: Vec3|Quat,
    endRotation: Vec3|Quat,
    findAll: boolean, // Defaults to false, not possible if end transform !== start transform
    // ... other options
}

Would be more fitting with the suggested raycast signature.

Copy link
Contributor

@LeXXik LeXXik May 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, something like that. I think starting rotation is probably not in options, as it is often used for non-spheres. Target rotation can be.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright so based on feedback I just rewrote the PR. Let me know if it better fits now 🙃

Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#shapeCast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.');
MushAsterion marked this conversation as resolved.
Show resolved Hide resolved

if (shape.type === 'capsule') {
return this.capsuleCast(shape.radius, shape.height, shape.axis, position, rotation);
} else if (shape.type === 'cone') {
return this.coneCast(shape.radius, shape.height, shape.axis, position, rotation);
} else if (shape.type === 'cylinder') {
return this.cylinderCast(shape.radius, shape.height, shape.axis, position, rotation);
} else if (shape.type === 'sphere') {
return this.sphereCast(shape.radius, position, rotation);
}

return this.boxCast(shape.halfExtends, position, rotation);
}

/**
* Perform a collision check on the world and return all entities the box hits.
* It returns an array of {@link RaycastResult}, one for each hit. If no hits are
* detected, the returned array will be of length 0.
*
* @param {Vec3} halfExtends - The half-extents of the box in the x, y and z axes.
* @param {Vec3} position - The world space position for the box to be.
* @param {Vec3} rotation - The world space rotation for the box to have.
*
* @returns {RaycastResult[]} An array of boxcast hit results (0 length if there were no hits).
*/
boxCast(halfExtends, position, rotation) {
Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#boxCast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.');

ammoVec3.setValue(halfExtends.x, halfExtends.y, halfExtends.z);
return this._shapecast(new Ammo.btSphereShape(ammoVec3), position, rotation);
MushAsterion marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Perform a collision check on the world and return all entities the capsule hits.
* It returns an array of {@link RaycastResult}, one for each hit. If no hits are
* detected, the returned array will be of length 0.
*
* @param {number} radius - The radius of the capsule.
* @param {number} height - The total height of the capsule from tip to tip.
* @param {number} axis - The local space axis with which the capsule's length is aligned. 0 for X, 1 for Y and 2 for Z. Defaults to 1 (Y-axis).
* @param {Vec3} position - The world space position for the capsule to be.
* @param {Vec3} rotation - The world space rotation for the capsule to have.
*
* @returns {RaycastResult[]} An array of capsulecast hit results (0 length if there were no hits).
*/
capsuleCast(radius, height, axis, position, rotation) {
Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#capsuleCast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.');

if (axis === 0) {
MushAsterion marked this conversation as resolved.
Show resolved Hide resolved
axis = 'btCapsuleShapeX';
} else if (axis === 2) {
axis = 'btCapsuleShapeZ';
} else {
axis = 'btCapsuleShape';
}

return this._shapecast(new Ammo[axis](radius, height), position, rotation);
mvaligursky marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Perform a collision check on the world and return all entities the cone hits.
* It returns an array of {@link RaycastResult}, one for each hit. If no hits are
* detected, the returned array will be of length 0.
*
* @param {number} radius - The radius of the cone.
* @param {number} height - The total height of the cone from tip to tip.
* @param {number} axis - The local space axis with which the cone's length is aligned. 0 for X, 1 for Y and 2 for Z. Defaults to 1 (Y-axis).
* @param {Vec3} position - The world space position for the cone to be.
* @param {Vec3} rotation - The world space rotation for the cone to have.
*
* @returns {RaycastResult[]} An array of conecast hit results (0 length if there were no hits).
*/
coneCast(radius, height, axis, position, rotation) {
Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#coneCast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.');

if (axis === 0) {
axis = 'btConeShapeX';
} else if (axis === 2) {
axis = 'btConeShapeZ';
} else {
axis = 'btConeShape';
}

return this._shapecast(new Ammo[axis](radius, height), position, rotation);
}

/**
* Perform a collision check on the world and return all entities the cylinder hits.
* It returns an array of {@link RaycastResult}, one for each hit. If no hits are
* detected, the returned array will be of length 0.
*
* @param {number} radius - The radius of the cylinder.
* @param {number} height - The total height of the cylinder from tip to tip.
* @param {number} axis - The local space axis with which the cylinder's length is aligned. 0 for X, 1 for Y and 2 for Z. Defaults to 1 (Y-axis).
* @param {Vec3} position - The world space position for the cylinder to be.
MushAsterion marked this conversation as resolved.
Show resolved Hide resolved
* @param {Vec3} rotation - The world space rotation for the cylinder to have.
*
* @returns {RaycastResult[]} An array of cylindercast hit results (0 length if there were no hits).
*/
cylinderCast(radius, height, axis, position, rotation) {
Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#cylinderCast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.');

if (axis === 0) {
axis = 'btCylinderShapeX';
} else if (axis === 2) {
axis = 'btCylinderShapeZ';
} else {
axis = 'btCylinderShape';
}

return this._shapecast(new Ammo[axis](radius, height), position, rotation);
}

/**
* Perform a collision check on the world and return all entities the sphere hits.
* It returns an array of {@link RaycastResult}, one for each hit. If no hits are
* detected, the returned array will be of length 0.
*
* @param {number} radius - The radius of the sphere.
* @param {Vec3} position - The world space position for the sphere to be.
* @param {Vec3} rotation - The world space rotation for the sphere to have.
*
* @returns {RaycastResult[]} An array of spherecast hit results (0 length if there were no hits).
*/
sphereCast(radius, position, rotation) {
Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#sphereCast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.');

return this._shapecast(new Ammo.btSphereShape(radius), position, rotation);
}

/**
* Perform a collision check on the world and return all entities the shape hits.
* It returns an array of {@link RaycastResult}, one for each hit. If no hits are
* detected, the returned array will be of length 0.
*
* @param {Ammo.btCollisionShape} shape - The Ammo.btCollisionShape to use for collision check.
* @param {Vec3} position - The world space position for the shape to be.
* @param {Vec3} rotation - The world space rotation for the shape to have.
*
* @returns {RaycastResult[]} An array of spherecast hit results (0 length if there were no hits).
* @private
*/
_shapecast(shape, position, rotation) {
willeastcott marked this conversation as resolved.
Show resolved Hide resolved
MushAsterion marked this conversation as resolved.
Show resolved Hide resolved
const results = [];

// Set proper position
if (position) {
ammoVec3.setValue(position.x, position.y, position.z);
} else {
ammoVec3.setValue(0, 0, 0);
}

// Set proper rotation.
if (rotation) {
ammoQuat.setEulerZYX(rotation.z, rotation.y, rotation.x);
} else {
ammoQuat.setEulerZYX(0, 0, 0);
}

// Assign position and rotation to transform.
ammoTransform.setIdentity();
ammoTransform.setOrigin(ammoVec3);
ammoTransform.setRotation(ammoQuat);

// We only initialize the shapecast body here so we don't have an extra body unless the user uses this function
if (!shapecastBody) {
shapecastBody = this.createBody(0, shape, ammoTransform);
}

// Add the body to the world.
this.addBody(shapecastBody, BODYGROUP_TRIGGER, BODYMASK_ALL);
MushAsterion marked this conversation as resolved.
Show resolved Hide resolved

// Make sure the body has proper shape, transform and is active.
shapecastBody.setCollisionShape(shape);
shapecastBody.setWorldTransform(ammoTransform);
shapecastBody.forceActivationState(BODYSTATE_ACTIVE_TAG);

// Callback for the contactTest results.
const resultCallback = new Ammo.ConcreteContactResultCallback();
resultCallback.addSingleResult = function (cp, colObj0Wrap, partId0, index0, colObj1Wrap, p1, index1) {
// Retrieve collided entity.
const body1 = Ammo.castObject(Ammo.wrapPointer(colObj1Wrap, Ammo.btCollisionObjectWrapper).getCollisionObject(), Ammo.btRigidBody);

// Make sure there is an existing entity.
if (body1.entity) {
// Retrieve manifold point.
const manifold = Ammo.wrapPointer(cp, Ammo.btManifoldPoint);

// Make sure there is a collision
if (manifold.getDistance() < 0) {
const point = manifold.get_m_positionWorldOnB();
const normal = manifold.get_m_normalWorldOnB();

// Push the result.
results.push(new RaycastResult(
body1.entity,
new Vec3(point.x(), point.y(), point.z()),
new Vec3(normal.x(), normal.y(), normal.z())
));
}
}
};

// Check for contacts.
this.dynamicsWorld.contactTest(shapecastBody, resultCallback);

// Remove body from world and disable it.
this.removeBody(shapecastBody);
shapecastBody.forceActivationState(BODYSTATE_DISABLE_DEACTIVATION);

// Destroy unused variables for performance.
Ammo.destroy(resultCallback);

return results;
}

/**
* Stores a collision between the entity and other in the contacts map and returns true if it
* is a new collision.
Expand Down