From ac625fcfbff69004831abd9cd555b65cd320a418 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 15:45:44 +0000 Subject: [PATCH 01/54] Added shape casts Added cast for shapes using `shapeCast` and by configuring the shape. It also provide API for each primitive shapes: - `boxCast(halfExtends, position, rotation)` - `capsuleCast(radius, heigh, axis, position, rotation)` - `coneCast(radius, heigh, axis, position, rotation)` - `cylinderCast(radius, heigh, axis, position, rotation)` - `sphereCast(radius, position, rotation)` There is also a private function `_shapecast(shape, collision, rotation)` if you wish to use your own shape (Ammo.btCollisionShape). Include full comments and docs. Include assertion of `Ammo.ConcreteContactResultCallback` for every public function. No assertion on private function to reduce function calls. --- src/framework/components/rigid-body/system.js | 258 +++++++++++++++++- 1 file changed, 256 insertions(+), 2 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index d809ea9a229..ec174380e33 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -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. @@ -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); @@ -552,6 +560,252 @@ 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. + * @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). + */ + shapeCast(shape, position, rotation) { + Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#shapeCast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); + + 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); + } else { + 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); + } + + /** + * 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) { + axis = 'btCapsuleShapeX'; + } else if (axis === 2) { + axis = 'btCapsuleShapeZ'; + } else { + axis = 'btCapsuleShape'; + } + + return this._shapecast(new Ammo[axis](radius, height), position, rotation); + } + + /** + * 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. + * @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[]} + * @private + */ + _shapecast(shape, position, rotation) { + 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); + + // 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. + let resultCallback = new Ammo.ConcreteContactResultCallback(); + + /** + * Add a contact collision result. + * + * @param {number} cp Pointer for Contact Point + * @param {number} colObj0Wrap Pointer for first collision object's wrapper. + * @param {number} partId0 + * @param {number} index0 + * @param {number} colObj1Wrap Pointer for second collision object's wrapper + * @param {number} partId1 + * @param {number} index1 + */ + 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. From 4fd6099676c5927a19ebb33a24f6bf2edd7ec465 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 15:59:43 +0000 Subject: [PATCH 02/54] Fixed ESLint --- src/framework/components/rigid-body/system.js | 54 ++++++++----------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index ec174380e33..d64f28fd63f 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -564,8 +564,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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 {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. * @param {number} shape.height - The total height of the capsule, cylinder or cone from tip to tip. @@ -573,7 +573,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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). */ shapeCast(shape, position, rotation) { @@ -587,20 +587,20 @@ class RigidBodyComponentSystem extends ComponentSystem { return this.cylinderCast(shape.radius, shape.height, shape.axis, position, rotation); } else if (shape.type === 'sphere') { return this.sphereCast(shape.radius, position, rotation); - } else { - return this.boxCast(shape.halfExtends, 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) { @@ -614,13 +614,13 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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) { @@ -641,13 +641,13 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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) { @@ -668,13 +668,13 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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. * @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) { @@ -695,11 +695,11 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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) { @@ -712,12 +712,12 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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[]} + * + * @returns {RaycastResult[]} An array of spherecast hit results (0 length if there were no hits). * @private */ _shapecast(shape, position, rotation) { @@ -756,20 +756,8 @@ class RigidBodyComponentSystem extends ComponentSystem { shapecastBody.forceActivationState(BODYSTATE_ACTIVE_TAG); // Callback for the contactTest results. - let resultCallback = new Ammo.ConcreteContactResultCallback(); - - /** - * Add a contact collision result. - * - * @param {number} cp Pointer for Contact Point - * @param {number} colObj0Wrap Pointer for first collision object's wrapper. - * @param {number} partId0 - * @param {number} index0 - * @param {number} colObj1Wrap Pointer for second collision object's wrapper - * @param {number} partId1 - * @param {number} index1 - */ - resultCallback.addSingleResult = function(cp, colObj0Wrap, partId0, index0, colObj1Wrap, p1, index1) { + 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); @@ -787,7 +775,7 @@ class RigidBodyComponentSystem extends ComponentSystem { results.push(new RaycastResult( body1.entity, new Vec3(point.x(), point.y(), point.z()), - new Vec3(normal.x(), normal.y(), normal.z()), + new Vec3(normal.x(), normal.y(), normal.z()) )); } } From c5f8fc87bbc8c154dc7edbea2389430f8f445939 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 16:20:50 +0000 Subject: [PATCH 03/54] Fixed halfExtends into halfExtents --- src/framework/components/rigid-body/system.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index d64f28fd63f..6dbbbf5f635 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -567,7 +567,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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. + * @param {Vec3} shape.halfExtents - The half-extents of the box in the x, y and z axes. * @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. @@ -589,7 +589,7 @@ class RigidBodyComponentSystem extends ComponentSystem { return this.sphereCast(shape.radius, position, rotation); } - return this.boxCast(shape.halfExtends, position, rotation); + return this.boxCast(shape.halfExtents, position, rotation); } /** @@ -597,16 +597,16 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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} halfExtents - 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) { + boxCast(halfExtents, 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); + ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); return this._shapecast(new Ammo.btSphereShape(ammoVec3), position, rotation); } From 36d500a8859b4153a9b9805483f69267200b84a6 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 16:31:11 +0000 Subject: [PATCH 04/54] Added shapecast shape destroying --- src/framework/components/rigid-body/system.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 6dbbbf5f635..86ba8348e74 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -787,9 +787,11 @@ class RigidBodyComponentSystem extends ComponentSystem { // Remove body from world and disable it. this.removeBody(shapecastBody); shapecastBody.forceActivationState(BODYSTATE_DISABLE_DEACTIVATION); + shapecastBody.setCollisionShape(null); // Destroy unused variables for performance. Ammo.destroy(resultCallback); + Ammo.destroy(shape); return results; } From 0e51d63ef9b8db5e8e806f905d72dfbd9bd6f4cc Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:54:26 +0100 Subject: [PATCH 05/54] Reduced debug assets into one --- src/framework/components/rigid-body/system.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 86ba8348e74..8a5278624cf 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -577,8 +577,6 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {RaycastResult[]} An array of shapecast hit results (0 length if there were no hits). */ shapeCast(shape, position, rotation) { - Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#shapeCast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); - if (shape.type === 'capsule') { return this.capsuleCast(shape.radius, shape.height, shape.axis, position, rotation); } else if (shape.type === 'cone') { @@ -604,8 +602,6 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {RaycastResult[]} An array of boxcast hit results (0 length if there were no hits). */ boxCast(halfExtents, 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(halfExtents.x, halfExtents.y, halfExtents.z); return this._shapecast(new Ammo.btSphereShape(ammoVec3), position, rotation); } @@ -624,8 +620,6 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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) { axis = 'btCapsuleShapeX'; } else if (axis === 2) { @@ -651,8 +645,6 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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) { @@ -678,8 +670,6 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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) { @@ -703,8 +693,6 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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); } @@ -721,6 +709,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * @private */ _shapecast(shape, position, rotation) { + Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapecast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); + const results = []; // Set proper position From 2ace6a355aff86b2ddce440b8668b562cf095d5c Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:57:03 +0100 Subject: [PATCH 06/54] Removed variable type change from shapeCasts --- src/framework/components/rigid-body/system.js | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 8a5278624cf..712e99c77bb 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -620,15 +620,15 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {RaycastResult[]} An array of capsulecast hit results (0 length if there were no hits). */ capsuleCast(radius, height, axis, position, rotation) { + let fn = 'btCapsuleShape'; + if (axis === 0) { - axis = 'btCapsuleShapeX'; + fn = 'btCapsuleShapeX'; } else if (axis === 2) { - axis = 'btCapsuleShapeZ'; - } else { - axis = 'btCapsuleShape'; + fn = 'btCapsuleShapeZ'; } - return this._shapecast(new Ammo[axis](radius, height), position, rotation); + return this._shapecast(new Ammo[fn](radius, height), position, rotation); } /** @@ -645,15 +645,15 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {RaycastResult[]} An array of conecast hit results (0 length if there were no hits). */ coneCast(radius, height, axis, position, rotation) { + let fn = 'btConeShape'; + if (axis === 0) { - axis = 'btConeShapeX'; + fn = 'btConeShapeX'; } else if (axis === 2) { - axis = 'btConeShapeZ'; - } else { - axis = 'btConeShape'; + fn = 'btConeShapeZ'; } - return this._shapecast(new Ammo[axis](radius, height), position, rotation); + return this._shapecast(new Ammo[fn](radius, height), position, rotation); } /** @@ -670,15 +670,15 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {RaycastResult[]} An array of cylindercast hit results (0 length if there were no hits). */ cylinderCast(radius, height, axis, position, rotation) { + let fn = 'btCylinderShape'; + if (axis === 0) { - axis = 'btCylinderShapeX'; + fn = 'btCylinderShapeX'; } else if (axis === 2) { - axis = 'btCylinderShapeZ'; - } else { - axis = 'btCylinderShape'; + fn = 'btCylinderShapeZ'; } - return this._shapecast(new Ammo[axis](radius, height), position, rotation); + return this._shapecast(new Ammo[fn](radius, height), position, rotation); } /** From 2b1e039b4fdad1723deae2b496b9603a51c0368b Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 20:01:23 +0100 Subject: [PATCH 07/54] Fixed JSDoc for optional parameters --- src/framework/components/rigid-body/system.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 712e99c77bb..cda14e3df0c 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -571,8 +571,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. + * @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). */ @@ -596,8 +596,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * detected, the returned array will be of length 0. * * @param {Vec3} halfExtents - 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. + * @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). */ @@ -614,8 +614,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. + * @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). */ @@ -639,8 +639,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. + * @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). */ @@ -664,8 +664,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. - * @param {Vec3} rotation - The world space rotation for the cylinder to have. + * @param {Vec3} [position] - The world space position for the cylinder to be. + * @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). */ @@ -687,8 +687,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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. + * @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). */ @@ -702,8 +702,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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. + * @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 From 0cdc17abfb25622b0de3a1cbabe9676c570c79f0 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 20:06:22 +0100 Subject: [PATCH 08/54] Added default position and rotation for _shapecast --- src/framework/components/rigid-body/system.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index cda14e3df0c..53614eaf0ad 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -708,24 +708,14 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {RaycastResult[]} An array of spherecast hit results (0 length if there were no hits). * @private */ - _shapecast(shape, position, rotation) { + _shapecast(shape, position = Vec3.ZERO, rotation = Vec3.ZERO) { Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapecast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); 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); - } + // Set proper position and rotation + ammoVec3.setValue(position.x, position.y, position.z); + ammoQuat.setEulerZYX(rotation.z, rotation.y, rotation.x); // Assign position and rotation to transform. ammoTransform.setIdentity(); From 052e8f8cfc9b7e660c71b929659d7aeda597f988 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 21:49:18 +0100 Subject: [PATCH 09/54] Removed shapecast body world addition --- src/framework/components/rigid-body/system.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 53614eaf0ad..9bf5ba7f42a 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -727,9 +727,6 @@ class RigidBodyComponentSystem extends ComponentSystem { shapecastBody = this.createBody(0, shape, ammoTransform); } - // Add the body to the world. - this.addBody(shapecastBody, BODYGROUP_TRIGGER, BODYMASK_ALL); - // Make sure the body has proper shape, transform and is active. shapecastBody.setCollisionShape(shape); shapecastBody.setWorldTransform(ammoTransform); @@ -764,8 +761,7 @@ class RigidBodyComponentSystem extends ComponentSystem { // Check for contacts. this.dynamicsWorld.contactTest(shapecastBody, resultCallback); - // Remove body from world and disable it. - this.removeBody(shapecastBody); + // Disable body and remove shape. shapecastBody.forceActivationState(BODYSTATE_DISABLE_DEACTIVATION); shapecastBody.setCollisionShape(null); From 7eff8dbc605e6c5f9e28662878723ef1b23cb873 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 21:57:39 +0100 Subject: [PATCH 10/54] Shape casts now return Entity array --- src/framework/components/rigid-body/system.js | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 9bf5ba7f42a..6a70a1cc9ee 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -574,7 +574,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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). + * @returns {Entity[]} An array of shapecast hit results (0 length if there were no hits). */ shapeCast(shape, position, rotation) { if (shape.type === 'capsule') { @@ -599,7 +599,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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). + * @returns {Entity[]} An array of boxcast hit results (0 length if there were no hits). */ boxCast(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); @@ -617,7 +617,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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). + * @returns {Entity[]} An array of capsulecast hit results (0 length if there were no hits). */ capsuleCast(radius, height, axis, position, rotation) { let fn = 'btCapsuleShape'; @@ -642,7 +642,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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). + * @returns {Entity[]} An array of conecast hit results (0 length if there were no hits). */ coneCast(radius, height, axis, position, rotation) { let fn = 'btConeShape'; @@ -667,7 +667,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} [position] - The world space position for the cylinder to be. * @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). + * @returns {Entity[]} An array of cylindercast hit results (0 length if there were no hits). */ cylinderCast(radius, height, axis, position, rotation) { let fn = 'btCylinderShape'; @@ -690,7 +690,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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). + * @returns {Entity[]} An array of spherecast hit results (0 length if there were no hits). */ sphereCast(radius, position, rotation) { return this._shapecast(new Ammo.btSphereShape(radius), position, rotation); @@ -705,7 +705,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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). + * @returns {Entity[]} An array of spherecast hit results (0 length if there were no hits). * @private */ _shapecast(shape, position = Vec3.ZERO, rotation = Vec3.ZERO) { @@ -745,15 +745,8 @@ class RigidBodyComponentSystem extends ComponentSystem { // 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()) - )); + // Push the entity. + results.push(body1.entity); } } }; From 5de3fef45a55e0edbcbfbcbc9ac8dea491596e5e Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 21:59:17 +0100 Subject: [PATCH 11/54] Removed unused variables --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 6a70a1cc9ee..e3eecdb37f7 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -7,7 +7,7 @@ import { Vec3 } from '../../../core/math/vec3.js'; import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; -import { BODYFLAG_NORESPONSE_OBJECT, BODYGROUP_TRIGGER, BODYMASK_ALL, BODYSTATE_ACTIVE_TAG, BODYSTATE_DISABLE_DEACTIVATION } from './constants.js'; +import { BODYFLAG_NORESPONSE_OBJECT, BODYSTATE_ACTIVE_TAG, BODYSTATE_DISABLE_DEACTIVATION } from './constants.js'; import { RigidBodyComponent } from './component.js'; import { RigidBodyComponentData } from './data.js'; From 9133182cad6f17eb55e7010babc1feb59817b9b2 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 22:02:40 +0100 Subject: [PATCH 12/54] Added Entity import --- src/framework/components/rigid-body/system.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index e3eecdb37f7..d40870f4e8b 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -3,6 +3,7 @@ import { ObjectPool } from '../../../core/object-pool.js'; import { Debug } from '../../../core/debug.js'; import { Vec3 } from '../../../core/math/vec3.js'; +import { Entity } from '../../entity.js'; import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; From 1e4415efaf767b2e4f86b05678962da0c53b9ccc Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 22:06:07 +0100 Subject: [PATCH 13/54] Fixed Entity JSDoc --- src/framework/components/rigid-body/system.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index d40870f4e8b..a99c6f910eb 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -3,7 +3,6 @@ import { ObjectPool } from '../../../core/object-pool.js'; import { Debug } from '../../../core/debug.js'; import { Vec3 } from '../../../core/math/vec3.js'; -import { Entity } from '../../entity.js'; import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; @@ -575,7 +574,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {Entity[]} An array of shapecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of shapecast hit results (0 length if there were no hits). */ shapeCast(shape, position, rotation) { if (shape.type === 'capsule') { @@ -600,7 +599,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {Entity[]} An array of boxcast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of boxcast hit results (0 length if there were no hits). */ boxCast(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); @@ -618,7 +617,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {Entity[]} An array of capsulecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of capsulecast hit results (0 length if there were no hits). */ capsuleCast(radius, height, axis, position, rotation) { let fn = 'btCapsuleShape'; @@ -643,7 +642,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {Entity[]} An array of conecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of conecast hit results (0 length if there were no hits). */ coneCast(radius, height, axis, position, rotation) { let fn = 'btConeShape'; @@ -668,7 +667,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} [position] - The world space position for the cylinder to be. * @param {Vec3} [rotation] - The world space rotation for the cylinder to have. * - * @returns {Entity[]} An array of cylindercast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of cylindercast hit results (0 length if there were no hits). */ cylinderCast(radius, height, axis, position, rotation) { let fn = 'btCylinderShape'; @@ -691,7 +690,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {Entity[]} An array of spherecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of spherecast hit results (0 length if there were no hits). */ sphereCast(radius, position, rotation) { return this._shapecast(new Ammo.btSphereShape(radius), position, rotation); @@ -706,7 +705,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {Entity[]} An array of spherecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of spherecast hit results (0 length if there were no hits). * @private */ _shapecast(shape, position = Vec3.ZERO, rotation = Vec3.ZERO) { From 53cb43f8077bd3e6715e9ad3ecdf7a3ac84e4b14 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 22:35:17 +0100 Subject: [PATCH 14/54] Updated shape.type eval to switch --- src/framework/components/rigid-body/system.js | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index a99c6f910eb..e374e304ef2 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -577,17 +577,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {import('../../entity.js').Entity[]} An array of shapecast hit results (0 length if there were no hits). */ shapeCast(shape, position, rotation) { - 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); + switch (shape.type) { + case 'capsule': + return this.capsuleCast(shape.radius, shape.height, shape.axis, position, rotation); + case 'cone': + return this.coneCast(shape.radius, shape.height, shape.axis, position, rotation); + case 'cylinder': + return this.cylinderCast(shape.radius, shape.height, shape.axis, position, rotation); + case 'sphere': + return this.sphereCast(shape.radius, position, rotation); + default: + return this.boxCast(shape.halfExtents, position, rotation); } - - return this.boxCast(shape.halfExtents, position, rotation); } /** From b4f005b8ed2c4fd7c0028fa71a193c01dca94c94 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 3 Feb 2023 22:51:59 +0100 Subject: [PATCH 15/54] Fixed docs --- src/framework/components/rigid-body/system.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index e374e304ef2..859c91d323d 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -562,7 +562,7 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * 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 + * It returns an array of {@link import('../../entity.js').Entity}. If no hits are * detected, the returned array will be of length 0. * * @param {object} shape - The shape to use for collision. @@ -574,7 +574,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {import('../../entity.js').Entity[]} An array of shapecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of entities hit by this shapecast (0 length if there were no hits). */ shapeCast(shape, position, rotation) { switch (shape.type) { @@ -593,14 +593,14 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * 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 + * It returns an array of {@link import('../../entity.js').Entity}. If no hits are * detected, the returned array will be of length 0. * * @param {Vec3} halfExtents - 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 {import('../../entity.js').Entity[]} An array of boxcast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of entities hit by this boxcast (0 length if there were no hits). */ boxCast(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); @@ -609,7 +609,7 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * 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 + * It returns an array of {@link import('../../entity.js').Entity}. If no hits are * detected, the returned array will be of length 0. * * @param {number} radius - The radius of the capsule. @@ -618,7 +618,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {import('../../entity.js').Entity[]} An array of capsulecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of entities hit by this capsulecast (0 length if there were no hits). */ capsuleCast(radius, height, axis, position, rotation) { let fn = 'btCapsuleShape'; @@ -634,7 +634,7 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * 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 + * It returns an array of {@link import('../../entity.js').Entity}. If no hits are * detected, the returned array will be of length 0. * * @param {number} radius - The radius of the cone. @@ -643,7 +643,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {import('../../entity.js').Entity[]} An array of conecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of entities hit by this conecast (0 length if there were no hits). */ coneCast(radius, height, axis, position, rotation) { let fn = 'btConeShape'; @@ -659,7 +659,7 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * 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 + * It returns an array of {@link import('../../entity.js').Entity}. If no hits are * detected, the returned array will be of length 0. * * @param {number} radius - The radius of the cylinder. @@ -668,7 +668,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} [position] - The world space position for the cylinder to be. * @param {Vec3} [rotation] - The world space rotation for the cylinder to have. * - * @returns {import('../../entity.js').Entity[]} An array of cylindercast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of entities hit by this cylindercast (0 length if there were no hits). */ cylinderCast(radius, height, axis, position, rotation) { let fn = 'btCylinderShape'; @@ -684,14 +684,14 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * 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 + * It returns an array of {@link import('../../entity.js').Entity}. 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 {import('../../entity.js').Entity[]} An array of spherecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of entities hit by this spherecast (0 length if there were no hits). */ sphereCast(radius, position, rotation) { return this._shapecast(new Ammo.btSphereShape(radius), position, rotation); @@ -699,14 +699,14 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * 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 + * It returns an array of {@link import('../../entity.js').Entity}. 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 {import('../../entity.js').Entity[]} An array of spherecast hit results (0 length if there were no hits). + * @returns {import('../../entity.js').Entity[]} An array of entities hit by this shapecast (0 length if there were no hits). * @private */ _shapecast(shape, position = Vec3.ZERO, rotation = Vec3.ZERO) { From 502994cdf0e2bd5d7d12699268a28b7e10d9e74c Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 10:15:41 +0000 Subject: [PATCH 16/54] Fix shape on boxCast Co-authored-by: Hermann Rolfes --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 859c91d323d..80e456eb092 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -604,7 +604,7 @@ class RigidBodyComponentSystem extends ComponentSystem { */ boxCast(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapecast(new Ammo.btSphereShape(ammoVec3), position, rotation); + return this._shapecast(new Ammo.btBoxShape(ammoVec3), position, rotation); } /** From c8adb464913290c6982a14105f42820e146b066c Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 11:31:58 +0000 Subject: [PATCH 17/54] Added HitResult to shapecast --- src/framework/components/rigid-body/system.js | 75 +++++++++++++++---- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 80e456eb092..3646018ea9d 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -231,6 +231,42 @@ class ContactResult { } } +/** + * Object holding the result of a hit on an Entity. + */ +class HitResult { + /** + * Create a new HitResult instance. + * + * @param {import('../../entity.js').Entity} entity - The entity that was hit. + * @param {Vec3} point - The point on the entity where the hit occurred, in world space. + * @param {Vec3} normal - The normal vector of the hit on the entity, in world space. + * @hideconstructor + */ + constructor(entity, point, normal) { + /** + * The entity that was hit. + * + * @type {import('../../entity.js').Entity} + */ + this.entity = entity; + + /** + * The point on the entity where the hit occurred, in world space. When returned by a shapecast it is the first point found to collide, it does not have to be the closest. + * + * @type {Vec3} + */ + this.point = point; + + /** + * The normal vector of the hit on the entity, in world space. + * + * @type {Vec3} + */ + this.normal = normal; + } +} + const _schema = ['enabled']; /** @@ -562,7 +598,7 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Perform a collision check on the world and return all entities the shape hits. - * It returns an array of {@link import('../../entity.js').Entity}. If no hits are + * It returns an array of {@link HitResult}. If no hits are * detected, the returned array will be of length 0. * * @param {object} shape - The shape to use for collision. @@ -574,7 +610,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {import('../../entity.js').Entity[]} An array of entities hit by this shapecast (0 length if there were no hits). + * @returns {HitResult[]} An array of shapecast hit results (0 length if there were no hits). */ shapeCast(shape, position, rotation) { switch (shape.type) { @@ -593,14 +629,14 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Perform a collision check on the world and return all entities the box hits. - * It returns an array of {@link import('../../entity.js').Entity}. If no hits are + * It returns an array of {@link HitResult}. If no hits are * detected, the returned array will be of length 0. * * @param {Vec3} halfExtents - 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 {import('../../entity.js').Entity[]} An array of entities hit by this boxcast (0 length if there were no hits). + * @returns {HitResult[]} An array of boxcast hit results (0 length if there were no hits). */ boxCast(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); @@ -609,7 +645,7 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Perform a collision check on the world and return all entities the capsule hits. - * It returns an array of {@link import('../../entity.js').Entity}. If no hits are + * It returns an array of {@link HitResult}. If no hits are * detected, the returned array will be of length 0. * * @param {number} radius - The radius of the capsule. @@ -618,7 +654,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {import('../../entity.js').Entity[]} An array of entities hit by this capsulecast (0 length if there were no hits). + * @returns {HitResult[]} An array of capsulecast hit results (0 length if there were no hits). */ capsuleCast(radius, height, axis, position, rotation) { let fn = 'btCapsuleShape'; @@ -634,7 +670,7 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Perform a collision check on the world and return all entities the cone hits. - * It returns an array of {@link import('../../entity.js').Entity}. If no hits are + * It returns an array of {@link HitResult}. If no hits are * detected, the returned array will be of length 0. * * @param {number} radius - The radius of the cone. @@ -643,7 +679,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {import('../../entity.js').Entity[]} An array of entities hit by this conecast (0 length if there were no hits). + * @returns {HitResult[]} An array of conecast hit results (0 length if there were no hits). */ coneCast(radius, height, axis, position, rotation) { let fn = 'btConeShape'; @@ -659,7 +695,7 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Perform a collision check on the world and return all entities the cylinder hits. - * It returns an array of {@link import('../../entity.js').Entity}. If no hits are + * It returns an array of {@link HitResult}. If no hits are * detected, the returned array will be of length 0. * * @param {number} radius - The radius of the cylinder. @@ -668,7 +704,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} [position] - The world space position for the cylinder to be. * @param {Vec3} [rotation] - The world space rotation for the cylinder to have. * - * @returns {import('../../entity.js').Entity[]} An array of entities hit by this cylindercast (0 length if there were no hits). + * @returns {HitResult[]} An array of cylindercast hit results (0 length if there were no hits). */ cylinderCast(radius, height, axis, position, rotation) { let fn = 'btCylinderShape'; @@ -684,14 +720,14 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Perform a collision check on the world and return all entities the sphere hits. - * It returns an array of {@link import('../../entity.js').Entity}. If no hits are + * It returns an array of {@link HitResult}. 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 {import('../../entity.js').Entity[]} An array of entities hit by this spherecast (0 length if there were no hits). + * @returns {HitResult[]} An array of spherecast hit results (0 length if there were no hits). */ sphereCast(radius, position, rotation) { return this._shapecast(new Ammo.btSphereShape(radius), position, rotation); @@ -699,14 +735,14 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Perform a collision check on the world and return all entities the shape hits. - * It returns an array of {@link import('../../entity.js').Entity}. If no hits are + * It returns an array of {@link HitResult}. 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 {import('../../entity.js').Entity[]} An array of entities hit by this shapecast (0 length if there were no hits). + * @returns {HitResult[]} An array of shapecast hit results (0 length if there were no hits). * @private */ _shapecast(shape, position = Vec3.ZERO, rotation = Vec3.ZERO) { @@ -746,8 +782,15 @@ class RigidBodyComponentSystem extends ComponentSystem { // Make sure there is a collision if (manifold.getDistance() < 0) { - // Push the entity. - results.push(body1.entity); + const point = manifold.get_m_positionWorldOnB(); + const normal = manifold.get_m_normalWorldOnB(); + + // Push the result. + results.push(new HitResult( + body1.entity, + new Vec3(point.x(), point.y(), point.z()), + new Vec3(normal.x(), normal.y(), normal.z()), + )); } } }; From fd907d5d198ee2e40c961cc12b56906de62c4b9f Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 11:34:31 +0000 Subject: [PATCH 18/54] Fixed inconsistent documentation --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 3646018ea9d..5ebb6f9e5cb 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -239,7 +239,7 @@ class HitResult { * Create a new HitResult instance. * * @param {import('../../entity.js').Entity} entity - The entity that was hit. - * @param {Vec3} point - The point on the entity where the hit occurred, in world space. + * @param {Vec3} point - The point on the entity where the hit occurred, in world space. When returned by a shapecast it is the first point found to collide, it does not have to be the closest. * @param {Vec3} normal - The normal vector of the hit on the entity, in world space. * @hideconstructor */ From 9566b602290afcb333aa98d2f12827a0a228afe3 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 11:40:07 +0000 Subject: [PATCH 19/54] Fixed ESLint --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 5ebb6f9e5cb..9cfce6f455d 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -789,7 +789,7 @@ class RigidBodyComponentSystem extends ComponentSystem { results.push(new HitResult( body1.entity, new Vec3(point.x(), point.y(), point.z()), - new Vec3(normal.x(), normal.y(), normal.z()), + new Vec3(normal.x(), normal.y(), normal.z()) )); } } From 6c2d55b955ec2a38f0a93fff2988cc227bcc4e78 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 12:47:03 +0000 Subject: [PATCH 20/54] Changed naming from cast to test --- src/framework/components/rigid-body/system.js | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 9cfce6f455d..221b08c7bfa 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -14,8 +14,8 @@ import { RigidBodyComponentData } from './data.js'; // Ammo.js variable for performance saving. let ammoRayStart, ammoRayEnd, ammoVec3, ammoQuat, ammoTransform; -// RigidBody for shape casts. Permanent to save performance. -let shapecastBody; +// RigidBody for shape tests. Permanent to save performance. +let shapeTestBody; /** * Object holding the result of a successful raycast hit. @@ -610,20 +610,20 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {HitResult[]} An array of shapecast hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). */ - shapeCast(shape, position, rotation) { + shapeTest(shape, position, rotation) { switch (shape.type) { case 'capsule': - return this.capsuleCast(shape.radius, shape.height, shape.axis, position, rotation); + return this.capsuleTest(shape.radius, shape.height, shape.axis, position, rotation); case 'cone': - return this.coneCast(shape.radius, shape.height, shape.axis, position, rotation); + return this.coneTest(shape.radius, shape.height, shape.axis, position, rotation); case 'cylinder': - return this.cylinderCast(shape.radius, shape.height, shape.axis, position, rotation); + return this.cylinderTest(shape.radius, shape.height, shape.axis, position, rotation); case 'sphere': - return this.sphereCast(shape.radius, position, rotation); + return this.sphereTest(shape.radius, position, rotation); default: - return this.boxCast(shape.halfExtents, position, rotation); + return this.boxTest(shape.halfExtents, position, rotation); } } @@ -636,11 +636,11 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {HitResult[]} An array of boxcast hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). */ - boxCast(halfExtents, position, rotation) { + boxTest(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapecast(new Ammo.btBoxShape(ammoVec3), position, rotation); + return this._shapeTest(new Ammo.btBoxShape(ammoVec3), position, rotation); } /** @@ -654,9 +654,9 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {HitResult[]} An array of capsulecast hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of capsuletest hit results (0 length if there were no hits). */ - capsuleCast(radius, height, axis, position, rotation) { + capsuleTest(radius, height, axis, position, rotation) { let fn = 'btCapsuleShape'; if (axis === 0) { @@ -665,7 +665,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCapsuleShapeZ'; } - return this._shapecast(new Ammo[fn](radius, height), position, rotation); + return this._shapeTest(new Ammo[fn](radius, height), position, rotation); } /** @@ -679,9 +679,9 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {HitResult[]} An array of conecast hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of conetest hit results (0 length if there were no hits). */ - coneCast(radius, height, axis, position, rotation) { + coneTest(radius, height, axis, position, rotation) { let fn = 'btConeShape'; if (axis === 0) { @@ -690,7 +690,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btConeShapeZ'; } - return this._shapecast(new Ammo[fn](radius, height), position, rotation); + return this._shapeTest(new Ammo[fn](radius, height), position, rotation); } /** @@ -704,9 +704,9 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} [position] - The world space position for the cylinder to be. * @param {Vec3} [rotation] - The world space rotation for the cylinder to have. * - * @returns {HitResult[]} An array of cylindercast hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). */ - cylinderCast(radius, height, axis, position, rotation) { + cylinderTest(radius, height, axis, position, rotation) { let fn = 'btCylinderShape'; if (axis === 0) { @@ -715,7 +715,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCylinderShapeZ'; } - return this._shapecast(new Ammo[fn](radius, height), position, rotation); + return this._shapeTest(new Ammo[fn](radius, height), position, rotation); } /** @@ -727,10 +727,10 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {HitResult[]} An array of spherecast hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). */ - sphereCast(radius, position, rotation) { - return this._shapecast(new Ammo.btSphereShape(radius), position, rotation); + sphereTest(radius, position, rotation) { + return this._shapeTest(new Ammo.btSphereShape(radius), position, rotation); } /** @@ -742,10 +742,10 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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 {HitResult[]} An array of shapecast hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). * @private */ - _shapecast(shape, position = Vec3.ZERO, rotation = Vec3.ZERO) { + _shapeTest(shape, position = Vec3.ZERO, rotation = Vec3.ZERO) { Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapecast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); const results = []; @@ -759,15 +759,15 @@ class RigidBodyComponentSystem extends ComponentSystem { 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); + // We only initialize the shapeTast body here so we don't have an extra body unless the user uses this function + if (!shapeTestBody) { + shapeTestBody = this.createBody(0, shape, ammoTransform); } // Make sure the body has proper shape, transform and is active. - shapecastBody.setCollisionShape(shape); - shapecastBody.setWorldTransform(ammoTransform); - shapecastBody.forceActivationState(BODYSTATE_ACTIVE_TAG); + shapeTestBody.setCollisionShape(shape); + shapeTestBody.setWorldTransform(ammoTransform); + shapeTestBody.forceActivationState(BODYSTATE_ACTIVE_TAG); // Callback for the contactTest results. const resultCallback = new Ammo.ConcreteContactResultCallback(); @@ -796,11 +796,11 @@ class RigidBodyComponentSystem extends ComponentSystem { }; // Check for contacts. - this.dynamicsWorld.contactTest(shapecastBody, resultCallback); + this.dynamicsWorld.contactTest(shapeTestBody, resultCallback); // Disable body and remove shape. - shapecastBody.forceActivationState(BODYSTATE_DISABLE_DEACTIVATION); - shapecastBody.setCollisionShape(null); + shapeTestBody.forceActivationState(BODYSTATE_DISABLE_DEACTIVATION); + shapeTestBody.setCollisionShape(null); // Destroy unused variables for performance. Ammo.destroy(resultCallback); From 9027bf3c061490fcb24b6bd4cadf42b5229fed5d Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 12:53:00 +0000 Subject: [PATCH 21/54] Added Quat rotation for shape tests --- src/framework/components/rigid-body/system.js | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 221b08c7bfa..79ccb8f069f 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -3,6 +3,7 @@ import { ObjectPool } from '../../../core/object-pool.js'; import { Debug } from '../../../core/debug.js'; import { Vec3 } from '../../../core/math/vec3.js'; +import { Quat } from '../../../core/math/quat.js'; import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; @@ -608,7 +609,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. + * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). */ @@ -634,7 +635,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @param {Vec3} halfExtents - 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. + * @param {Vec3|Quat} [rotation] - The world space rotation for the box to have. * * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). */ @@ -652,7 +653,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. + * @param {Vec3|Quat} [rotation] - The world space rotation for the capsule to have. * * @returns {HitResult[]} An array of capsuletest hit results (0 length if there were no hits). */ @@ -677,7 +678,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. + * @param {Vec3|Quat} [rotation] - The world space rotation for the cone to have. * * @returns {HitResult[]} An array of conetest hit results (0 length if there were no hits). */ @@ -702,7 +703,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. - * @param {Vec3} [rotation] - The world space rotation for the cylinder to have. + * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. * * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). */ @@ -725,7 +726,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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. + * @param {Vec3|Quat} [rotation] - The world space rotation for the sphere to have. * * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). */ @@ -740,7 +741,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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. + * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). * @private @@ -750,9 +751,15 @@ class RigidBodyComponentSystem extends ComponentSystem { const results = []; - // Set proper position and rotation + // Set proper position ammoVec3.setValue(position.x, position.y, position.z); - ammoQuat.setEulerZYX(rotation.z, rotation.y, rotation.x); + + // Set proper rotation + if (rotation instanceof Quat) { + ammoQuat.setValue(rotation.x, rotation.y, rotation.z, rotation.w); + } else { + ammoQuat.setEulerZYX(rotation.z, rotation.y, rotation.x); + } // Assign position and rotation to transform. ammoTransform.setIdentity(); From c294abbeb974161b2c4fd7846328440bf197a488 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 12:55:15 +0000 Subject: [PATCH 22/54] Fixed ESLint --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 79ccb8f069f..7319dc8d3e0 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -753,7 +753,7 @@ class RigidBodyComponentSystem extends ComponentSystem { // Set proper position ammoVec3.setValue(position.x, position.y, position.z); - + // Set proper rotation if (rotation instanceof Quat) { ammoQuat.setValue(rotation.x, rotation.y, rotation.z, rotation.w); From 845fb0a9f161a56726c1bb1c46f3245ffe85341d Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 17:11:20 +0000 Subject: [PATCH 23/54] Added sphereCast and boxCast --- src/framework/components/rigid-body/system.js | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 7319dc8d3e0..e311fd46d5e 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -4,6 +4,7 @@ import { Debug } from '../../../core/debug.js'; import { Vec3 } from '../../../core/math/vec3.js'; import { Quat } from '../../../core/math/quat.js'; +import { Mat4 } from '../../../core/math/mat4'; import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; @@ -17,6 +18,8 @@ let ammoRayStart, ammoRayEnd, ammoVec3, ammoQuat, ammoTransform; // RigidBody for shape tests. Permanent to save performance. let shapeTestBody; +const shapecastRotation = new Quat(); +const shapecastRotationMatrix = new Mat4(); /** * Object holding the result of a successful raycast hit. @@ -597,6 +600,56 @@ class RigidBodyComponentSystem extends ComponentSystem { return results; } + /** + * Perform a collision check on the world and return all entities the box hits. + * It returns an array of {@link HitResult}. If no hits are + * detected, the returned array will be of length 0. + * + * @param {Vec3} halfExtents - The half-extents of the box in the x, y and z axes. Cast distance will be added to Z-Axis. + * @param {Vec3} start - The world space point where the box starts. + * @param {Vec3} end - The world space point where the box ends. + * + * @returns {HitResult[]} An array of boxCast hit results (0 length if there were no hits). + */ + boxCast(halfExtents, start, end) { + // Sweeping + halfExtents.z += start.distance(end) / 2; + + // Find rotation + shapecastRotationMatrix.setLookAt(start, end, Vec3.UP); + shapecastRotation.setFromMat4(shapecastRotationMatrix); + + // Transform start vector to make it beween initial start and end. + start.add(end).divScalar(2); + + return this._shapeTest(new Ammo.btBoxShape(halfExtents), start, shapecastRotation); + } + + /** + * Perform a collision check on the world and return all entities the sphere hits. + * It returns an array of {@link HitResult}. If no hits are + * detected, the returned array will be of length 0. + * + * @param {number} radius - The radius for the sphere. + * @param {Vec3} start - The world space point for the center of the sphere at the beginning of the cast. + * @param {Vec3} end - The world space point for the center of the sphere at the end of the cast. + * + * @returns {HitResult[]} An array of sphereCast hit results (0 length if there were no hits). + */ + sphereCast(radius, start, end) { + // Sweeping + const height = start.distance(end); + + // Find rotation + shapecastRotationMatrix.setLookAt(start, end, Vec3.UP); + shapecastRotation.setFromMat4(shapecastRotationMatrix); + + // Transform start vector to make it beween initial start and end. + start.add(end).divScalar(2); + + return this._shapeTest(new Ammo.btCapsuleShapeZ(radius, height), start, shapecastRotation); + } + /** * Perform a collision check on the world and return all entities the shape hits. * It returns an array of {@link HitResult}. If no hits are From 34d83b3bd8277e074d1f8647fa2262a1433eec3e Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 17:13:40 +0000 Subject: [PATCH 24/54] Fixed typo --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index e311fd46d5e..650d462f42e 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -4,7 +4,7 @@ import { Debug } from '../../../core/debug.js'; import { Vec3 } from '../../../core/math/vec3.js'; import { Quat } from '../../../core/math/quat.js'; -import { Mat4 } from '../../../core/math/mat4'; +import { Mat4 } from '../../../core/math/mat4.js'; import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; From fcb70ea5706f81d4c7bb9f9e939c11919ae00c52 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 4 Feb 2023 20:18:34 +0100 Subject: [PATCH 25/54] Fixed sphereCast length --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 650d462f42e..eddbfd1576d 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -638,7 +638,7 @@ class RigidBodyComponentSystem extends ComponentSystem { */ sphereCast(radius, start, end) { // Sweeping - const height = start.distance(end); + const height = start.distance(end) + radius; // Find rotation shapecastRotationMatrix.setLookAt(start, end, Vec3.UP); From 62136857b02ff8f6ee09396b7293b7a23b1a3eb8 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sun, 5 Feb 2023 10:18:58 +0000 Subject: [PATCH 26/54] Added vector for shapecast position --- src/framework/components/rigid-body/system.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index eddbfd1576d..54c471527f6 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -18,6 +18,7 @@ let ammoRayStart, ammoRayEnd, ammoVec3, ammoQuat, ammoTransform; // RigidBody for shape tests. Permanent to save performance. let shapeTestBody; +const shapecastPosition = new Vec3(); const shapecastRotation = new Quat(); const shapecastRotationMatrix = new Mat4(); @@ -620,9 +621,9 @@ class RigidBodyComponentSystem extends ComponentSystem { shapecastRotation.setFromMat4(shapecastRotationMatrix); // Transform start vector to make it beween initial start and end. - start.add(end).divScalar(2); + shapecastPosition.lerp(start, end, 0.5); - return this._shapeTest(new Ammo.btBoxShape(halfExtents), start, shapecastRotation); + return this._shapeTest(new Ammo.btBoxShape(halfExtents), shapecast, shapecastRotation); } /** @@ -645,9 +646,9 @@ class RigidBodyComponentSystem extends ComponentSystem { shapecastRotation.setFromMat4(shapecastRotationMatrix); // Transform start vector to make it beween initial start and end. - start.add(end).divScalar(2); + shapecastPosition.lerp(start, end, 0.5); - return this._shapeTest(new Ammo.btCapsuleShapeZ(radius, height), start, shapecastRotation); + return this._shapeTest(new Ammo.btCapsuleShapeZ(radius, height), shapecastPosition, shapecastRotation); } /** From a4440693c28a6720ee506a3220487969e16c59fd Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sun, 5 Feb 2023 10:20:58 +0000 Subject: [PATCH 27/54] Fixed boxCast shape halfExtents --- src/framework/components/rigid-body/system.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 54c471527f6..bb35789576b 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -614,7 +614,7 @@ class RigidBodyComponentSystem extends ComponentSystem { */ boxCast(halfExtents, start, end) { // Sweeping - halfExtents.z += start.distance(end) / 2; + ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z + start.distance(end) / 2); // Find rotation shapecastRotationMatrix.setLookAt(start, end, Vec3.UP); @@ -623,7 +623,7 @@ class RigidBodyComponentSystem extends ComponentSystem { // Transform start vector to make it beween initial start and end. shapecastPosition.lerp(start, end, 0.5); - return this._shapeTest(new Ammo.btBoxShape(halfExtents), shapecast, shapecastRotation); + return this._shapeTest(new Ammo.btBoxShape(ammoVec3), shapecast, shapecastRotation); } /** From 3ae5522f43788deb955ae64c065b1bb8d40caf8c Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sun, 5 Feb 2023 10:22:35 +0000 Subject: [PATCH 28/54] Fixed typo --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index bb35789576b..3a91e9bb54a 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -623,7 +623,7 @@ class RigidBodyComponentSystem extends ComponentSystem { // Transform start vector to make it beween initial start and end. shapecastPosition.lerp(start, end, 0.5); - return this._shapeTest(new Ammo.btBoxShape(ammoVec3), shapecast, shapecastRotation); + return this._shapeTest(new Ammo.btBoxShape(ammoVec3), shapecastPosition, shapecastRotation); } /** From dc69842d0ae6d8157b1af86c58d7e92fed57ee47 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sun, 5 Feb 2023 12:11:04 +0000 Subject: [PATCH 29/54] Renamed boxCastAll and sphereCastAll --- src/framework/components/rigid-body/system.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 3a91e9bb54a..a537bd5e897 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -612,7 +612,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of boxCast hit results (0 length if there were no hits). */ - boxCast(halfExtents, start, end) { + boxCastAll(halfExtents, start, end) { // Sweeping ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z + start.distance(end) / 2); @@ -637,7 +637,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of sphereCast hit results (0 length if there were no hits). */ - sphereCast(radius, start, end) { + sphereCastAll(radius, start, end) { // Sweeping const height = start.distance(end) + radius; From 2d8149a85cef5befc0808574c79dd4f1ca1dc752 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sun, 5 Feb 2023 19:56:49 +0100 Subject: [PATCH 30/54] Fixed sphereCast length --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index a537bd5e897..e899b4b65d0 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -639,7 +639,7 @@ class RigidBodyComponentSystem extends ComponentSystem { */ sphereCastAll(radius, start, end) { // Sweeping - const height = start.distance(end) + radius; + const height = start.distance(end) + radius * 2; // Find rotation shapecastRotationMatrix.setLookAt(start, end, Vec3.UP); From 8134d3fceedb35227ffdb1c3c339d8aab437331d Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sun, 5 Feb 2023 19:57:28 +0100 Subject: [PATCH 31/54] Removed boxCastAll --- src/framework/components/rigid-body/system.js | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index e899b4b65d0..0dad8c62c5a 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -601,31 +601,6 @@ class RigidBodyComponentSystem extends ComponentSystem { return results; } - /** - * Perform a collision check on the world and return all entities the box hits. - * It returns an array of {@link HitResult}. If no hits are - * detected, the returned array will be of length 0. - * - * @param {Vec3} halfExtents - The half-extents of the box in the x, y and z axes. Cast distance will be added to Z-Axis. - * @param {Vec3} start - The world space point where the box starts. - * @param {Vec3} end - The world space point where the box ends. - * - * @returns {HitResult[]} An array of boxCast hit results (0 length if there were no hits). - */ - boxCastAll(halfExtents, start, end) { - // Sweeping - ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z + start.distance(end) / 2); - - // Find rotation - shapecastRotationMatrix.setLookAt(start, end, Vec3.UP); - shapecastRotation.setFromMat4(shapecastRotationMatrix); - - // Transform start vector to make it beween initial start and end. - shapecastPosition.lerp(start, end, 0.5); - - return this._shapeTest(new Ammo.btBoxShape(ammoVec3), shapecastPosition, shapecastRotation); - } - /** * Perform a collision check on the world and return all entities the sphere hits. * It returns an array of {@link HitResult}. If no hits are From 87451da0ffd4adc5d129427c287c5edd43004773 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Mon, 6 Feb 2023 08:13:43 +0100 Subject: [PATCH 32/54] Removed shapetest body deactivation --- src/framework/components/rigid-body/system.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 0dad8c62c5a..d834faabc7b 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -9,7 +9,7 @@ import { Mat4 } from '../../../core/math/mat4.js'; import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; -import { BODYFLAG_NORESPONSE_OBJECT, BODYSTATE_ACTIVE_TAG, BODYSTATE_DISABLE_DEACTIVATION } from './constants.js'; +import { BODYFLAG_NORESPONSE_OBJECT, BODYSTATE_ACTIVE_TAG } from './constants.js'; import { RigidBodyComponent } from './component.js'; import { RigidBodyComponentData } from './data.js'; @@ -834,8 +834,7 @@ class RigidBodyComponentSystem extends ComponentSystem { // Check for contacts. this.dynamicsWorld.contactTest(shapeTestBody, resultCallback); - // Disable body and remove shape. - shapeTestBody.forceActivationState(BODYSTATE_DISABLE_DEACTIVATION); + // Remove body shape. shapeTestBody.setCollisionShape(null); // Destroy unused variables for performance. From 73b10a7422c1262550ae75e52dcc9d28a3c772a5 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Wed, 8 Feb 2023 08:48:45 +0100 Subject: [PATCH 33/54] Rename shape casts into shapeCastAll --- src/framework/components/rigid-body/system.js | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index d834faabc7b..2068becbdb8 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -642,18 +642,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). */ - shapeTest(shape, position, rotation) { + shapeTestAll(shape, position, rotation) { switch (shape.type) { case 'capsule': - return this.capsuleTest(shape.radius, shape.height, shape.axis, position, rotation); + return this.capsuleTestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'cone': - return this.coneTest(shape.radius, shape.height, shape.axis, position, rotation); + return this.coneTestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'cylinder': - return this.cylinderTest(shape.radius, shape.height, shape.axis, position, rotation); + return this.cylinderTestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'sphere': - return this.sphereTest(shape.radius, position, rotation); + return this.sphereTestAll(shape.radius, position, rotation); default: - return this.boxTest(shape.halfExtents, position, rotation); + return this.boxTestAll(shape.halfExtents, position, rotation); } } @@ -668,9 +668,9 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). */ - boxTest(halfExtents, position, rotation) { + boxTestAll(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapeTest(new Ammo.btBoxShape(ammoVec3), position, rotation); + return this._shapeTestAll(new Ammo.btBoxShape(ammoVec3), position, rotation); } /** @@ -686,7 +686,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of capsuletest hit results (0 length if there were no hits). */ - capsuleTest(radius, height, axis, position, rotation) { + capsuleTestAll(radius, height, axis, position, rotation) { let fn = 'btCapsuleShape'; if (axis === 0) { @@ -695,7 +695,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCapsuleShapeZ'; } - return this._shapeTest(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -711,7 +711,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of conetest hit results (0 length if there were no hits). */ - coneTest(radius, height, axis, position, rotation) { + coneTestAll(radius, height, axis, position, rotation) { let fn = 'btConeShape'; if (axis === 0) { @@ -720,7 +720,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btConeShapeZ'; } - return this._shapeTest(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -736,7 +736,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). */ - cylinderTest(radius, height, axis, position, rotation) { + cylinderTestAll(radius, height, axis, position, rotation) { let fn = 'btCylinderShape'; if (axis === 0) { @@ -745,7 +745,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCylinderShapeZ'; } - return this._shapeTest(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -759,8 +759,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). */ - sphereTest(radius, position, rotation) { - return this._shapeTest(new Ammo.btSphereShape(radius), position, rotation); + sphereTestAll(radius, position, rotation) { + return this._shapeTestAll(new Ammo.btSphereShape(radius), position, rotation); } /** From d6d4539aab1596e63a467a86a576c2d43207d599 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 11 Feb 2023 01:30:28 +0100 Subject: [PATCH 34/54] Changed RaycastResult into HitResult --- src/framework/components/rigid-body/system.js | 56 ++++--------------- 1 file changed, 10 insertions(+), 46 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 2068becbdb8..40da007539c 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -23,11 +23,11 @@ const shapecastRotation = new Quat(); const shapecastRotationMatrix = new Mat4(); /** - * Object holding the result of a successful raycast hit. + * Object holding the result of a successful hit. */ -class RaycastResult { +class HitResult { /** - * Create a new RaycastResult instance. + * Create a new HitResult instance. * * @param {import('../../entity.js').Entity} entity - The entity that was hit. * @param {Vec3} point - The point at which the ray hit the entity in world space. @@ -236,42 +236,6 @@ class ContactResult { } } -/** - * Object holding the result of a hit on an Entity. - */ -class HitResult { - /** - * Create a new HitResult instance. - * - * @param {import('../../entity.js').Entity} entity - The entity that was hit. - * @param {Vec3} point - The point on the entity where the hit occurred, in world space. When returned by a shapecast it is the first point found to collide, it does not have to be the closest. - * @param {Vec3} normal - The normal vector of the hit on the entity, in world space. - * @hideconstructor - */ - constructor(entity, point, normal) { - /** - * The entity that was hit. - * - * @type {import('../../entity.js').Entity} - */ - this.entity = entity; - - /** - * The point on the entity where the hit occurred, in world space. When returned by a shapecast it is the first point found to collide, it does not have to be the closest. - * - * @type {Vec3} - */ - this.point = point; - - /** - * The normal vector of the hit on the entity, in world space. - * - * @type {Vec3} - */ - this.normal = normal; - } -} - const _schema = ['enabled']; /** @@ -514,11 +478,11 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Raycast the world and return the first entity the ray hits. Fire a ray into the world from * start to end, if the ray hits an entity with a collision component, it returns a - * {@link RaycastResult}, otherwise returns null. + * {@link HitResult}, otherwise returns null. * * @param {Vec3} start - The world space point where the ray starts. * @param {Vec3} end - The world space point where the ray ends. - * @returns {RaycastResult} The result of the raycasting or null if there was no hit. + * @returns {HitResult} The result of the raycasting or null if there was no hit. */ raycastFirst(start, end) { let result = null; @@ -535,7 +499,7 @@ class RigidBodyComponentSystem extends ComponentSystem { const point = rayCallback.get_m_hitPointWorld(); const normal = rayCallback.get_m_hitNormalWorld(); - result = new RaycastResult( + result = new HitResult( body.entity, new Vec3(point.x(), point.y(), point.z()), new Vec3(normal.x(), normal.y(), normal.z()) @@ -558,12 +522,12 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Raycast the world and return all entities the ray hits. It returns an array of - * {@link RaycastResult}, one for each hit. If no hits are detected, the returned array will be + * {@link HitResult}, one for each hit. If no hits are detected, the returned array will be * of length 0. * * @param {Vec3} start - The world space point where the ray starts. * @param {Vec3} end - The world space point where the ray ends. - * @returns {RaycastResult[]} An array of raycast hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of raycast hit results (0 length if there were no hits). */ raycastAll(start, end) { Debug.assert(Ammo.AllHitsRayResultCallback, 'pc.RigidBodyComponentSystem#raycastAll: Your version of ammo.js does not expose Ammo.AllHitsRayResultCallback. Update it to latest.'); @@ -586,7 +550,7 @@ class RigidBodyComponentSystem extends ComponentSystem { if (body) { const point = points.at(i); const normal = normals.at(i); - const result = new RaycastResult( + const result = new HitResult( body.entity, new Vec3(point.x(), point.y(), point.z()), new Vec3(normal.x(), normal.y(), normal.z()) @@ -1233,4 +1197,4 @@ class RigidBodyComponentSystem extends ComponentSystem { Component._buildAccessors(RigidBodyComponent.prototype, _schema); -export { ContactPoint, ContactResult, RaycastResult, RigidBodyComponentSystem, SingleContactResult }; +export { ContactPoint, ContactResult, HitResult, RigidBodyComponentSystem, SingleContactResult }; From a9ea632ea96f6117dcacc913f21e16f8da3322fd Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 11 Feb 2023 01:31:50 +0100 Subject: [PATCH 35/54] Changed RaycastResult into HitResult --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 113756ae514..44df4f26b02 100644 --- a/src/index.js +++ b/src/index.js @@ -200,7 +200,7 @@ export { RenderComponent } from './framework/components/render/component.js'; export { RenderComponentSystem } from './framework/components/render/system.js'; export * from './framework/components/rigid-body/constants.js'; export { RigidBodyComponent } from './framework/components/rigid-body/component.js'; -export { RigidBodyComponentSystem, ContactPoint, ContactResult, RaycastResult, SingleContactResult } from './framework/components/rigid-body/system.js'; +export { RigidBodyComponentSystem, ContactPoint, ContactResult, HitResult, SingleContactResult } from './framework/components/rigid-body/system.js'; export { SceneRegistry } from './framework/scene-registry.js'; export { SceneRegistryItem } from './framework/scene-registry-item.js'; export * from './framework/components/screen/constants.js'; From db8274b233770f45f9a1d5d99ba94446a191d11d Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 11 Feb 2023 01:33:12 +0100 Subject: [PATCH 36/54] Changed RaycastResult into HitResult --- examples/src/examples/physics/raycast.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/examples/physics/raycast.tsx b/examples/src/examples/physics/raycast.tsx index e30020c5f54..669f61852f3 100644 --- a/examples/src/examples/physics/raycast.tsx +++ b/examples/src/examples/physics/raycast.tsx @@ -137,7 +137,7 @@ class RaycastExample { app.drawLine(start, end, white); const results = app.systems.rigidbody.raycastAll(start, end); - results.forEach(function (result: pc.RaycastResult) { + results.forEach(function (result: pc.HitResult) { result.entity.render.material = red; // Render the normal on the surface from the hit point From faf1f0075759c440bfce3ed5d66f5599fb85a672 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sat, 11 Feb 2023 01:36:08 +0100 Subject: [PATCH 37/54] Deprecated RaycastResult --- src/deprecated/deprecated.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/deprecated/deprecated.js b/src/deprecated/deprecated.js index ba4a99d8d6b..763f8edb7ff 100644 --- a/src/deprecated/deprecated.js +++ b/src/deprecated/deprecated.js @@ -112,7 +112,7 @@ import { BODYTYPE_DYNAMIC, BODYTYPE_KINEMATIC, BODYTYPE_STATIC } from '../framework/components/rigid-body/constants.js'; import { RigidBodyComponent } from '../framework/components/rigid-body/component.js'; -import { RigidBodyComponentSystem } from '../framework/components/rigid-body/system.js'; +import { RigidBodyComponentSystem, HitResult } from '../framework/components/rigid-body/system.js'; import { basisInitialize } from '../framework/handlers/basis.js'; // CORE @@ -1047,6 +1047,7 @@ Object.defineProperty(MouseEvent.prototype, 'wheel', { // FRAMEWORK +export const RaycastResult = HitResult; export const RIGIDBODY_TYPE_STATIC = BODYTYPE_STATIC; export const RIGIDBODY_TYPE_DYNAMIC = BODYTYPE_DYNAMIC; export const RIGIDBODY_TYPE_KINEMATIC = BODYTYPE_KINEMATIC; From e084be1813a4bc347a0361e4e3b8ed64fd15c242 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Wed, 22 Feb 2023 09:48:38 +0000 Subject: [PATCH 38/54] Added shapeCastFirst functions --- src/framework/components/rigid-body/system.js | 260 +++++++++++++++--- 1 file changed, 225 insertions(+), 35 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 40da007539c..bc9f077f151 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -4,7 +4,6 @@ import { Debug } from '../../../core/debug.js'; import { Vec3 } from '../../../core/math/vec3.js'; import { Quat } from '../../../core/math/quat.js'; -import { Mat4 } from '../../../core/math/mat4.js'; import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; @@ -14,13 +13,10 @@ import { RigidBodyComponent } from './component.js'; import { RigidBodyComponentData } from './data.js'; // Ammo.js variable for performance saving. -let ammoRayStart, ammoRayEnd, ammoVec3, ammoQuat, ammoTransform; +let ammoRayStart, ammoRayEnd, ammoVec3, ammoQuat, ammoTransform, ammoTransform2; // RigidBody for shape tests. Permanent to save performance. let shapeTestBody; -const shapecastPosition = new Vec3(); -const shapecastRotation = new Quat(); -const shapecastRotationMatrix = new Mat4(); /** * Object holding the result of a successful hit. @@ -359,6 +355,7 @@ class RigidBodyComponentSystem extends ComponentSystem { ammoVec3 = new Ammo.btVector3(); ammoQuat = new Ammo.btQuaternion(); ammoTransform = new Ammo.btTransform(); + ammoTransform2 = new Ammo.btTransform(); RigidBodyComponent.onLibraryLoaded(); @@ -566,28 +563,218 @@ class RigidBodyComponentSystem extends ComponentSystem { } /** - * Perform a collision check on the world and return all entities the sphere hits. - * It returns an array of {@link HitResult}. If no hits are - * detected, the returned array will be of length 0. + * Perform a shape casting on the world and return the first entity the shape hits. + * It returns a {@link HitResult}. If no hits are detected, returned value will be null. + * + * @param {object} shape - The shape to use for sweep test. + * @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.halfExtents] - The half-extents of the box in the x, y and z axes. + * @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} startPosition - The world space starting position for the shape to be. + * @param {Vec3} endPosition - The world space ending position for the shape to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * + * @returns {HitResult} The first hit result (null if there were no hits). + */ + shapeCastFirst(shape, startPosition, endPosition, startRotation, endRotation) { + switch (shape.type) { + case 'capsule': + return this.capsuleCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + case 'cone': + return this.coneCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + case 'cylinder': + return this.cylinderCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + case 'sphere': + return this.sphereCastFirst(shape.radius, startPosition, endPosition); + default: + return this.boxCastFirst(shape.halfExtents, startPosition, endPosition, startRotation, endRotation); + } + } + + /** + * Perform a shape casting on the world and return the first entity the box hits. + * It returns a {@link HitResult}. If no hits are detected, returned value will be null. + * + * @param {Vec3} halfExtents - The half-extents of the box in the x, y and z axes. + * @param {Vec3} startPosition - The world space starting position for the shape to be. + * @param {Vec3} endPosition - The world space ending position for the shape to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * + * @returns {HitResult} The first hit result (null if there were no hits). + */ + boxCastFirst(halfExtents, startPosition, endPosition, startRotation, endRotation) { + ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); + return this._shapeCastFirst(new Ammo.btBoxShape(ammoVec3), startPosition, endPosition, startRotation, endRotation); + } + + /** + * Perform a shape casting on the world and return the first entity the capsule hits. + * It returns a {@link HitResult}. If no hits are detected, returned value will be null. + * + * @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} startPosition - The world space starting position for the shape to be. + * @param {Vec3} endPosition - The world space ending position for the shape to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * + * @returns {HitResult} The first hit result (null if there were no hits). + */ + capsuleCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + let fn = 'btCapsuleShape'; + + if (axis === 0) { + fn = 'btCapsuleShapeX'; + } else if (axis === 2) { + fn = 'btCapsuleShapeZ'; + } + + return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + } + + /** + * Perform a shape casting on the world and return the first entity the cone hits. + * It returns a {@link HitResult}. If no hits are detected, returned value will be null. + * + * @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} startPosition - The world space starting position for the shape to be. + * @param {Vec3} endPosition - The world space ending position for the shape to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * + * @returns {HitResult} The first hit result (null if there were no hits). + */ + coneCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + let fn = 'btConeShape'; + + if (axis === 0) { + fn = 'btConeShapeX'; + } else if (axis === 2) { + fn = 'btConeShapeZ'; + } + + return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + } + + /** + * Perform a shape casting on the world and return the first entity the cylinder hits. + * It returns a {@link HitResult}. If no hits are detected, returned value will be null. + * + * @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} startPosition - The world space starting position for the shape to be. + * @param {Vec3} endPosition - The world space ending position for the shape to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * + * @returns {HitResult} The first hit result (null if there were no hits). + */ + cylinderCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + let fn = 'btCylinderShape'; + + if (axis === 0) { + fn = 'btCylinderShapeX'; + } else if (axis === 2) { + fn = 'btCylinderShapeZ'; + } + + return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + } + + /** + * Perform a shape casting on the world and return the first entity the sphere hits. + * It returns a {@link HitResult}. If no hits are detected, returned value will be null. * * @param {number} radius - The radius for the sphere. - * @param {Vec3} start - The world space point for the center of the sphere at the beginning of the cast. - * @param {Vec3} end - The world space point for the center of the sphere at the end of the cast. + * @param {Vec3} startPosition - The world space starting position for the shape to be. + * @param {Vec3} endPosition - The world space ending position for the shape to be. + * + * @returns {HitResult} The first hit result (null if there were no hits). + */ + sphereCastFirst(radius, startPosition, endPosition) { + return this._shapeCastFirst(new Ammo.btSphereShape(radius), startPosition, endPosition); + } + + /** + * Perform a shape casting on the world and return the first entity the shape hits. + * It returns a {@link HitResult}. If no hits are detected, returned value will be null. + * + * @param {object} shape - The Ammo.btCollisionShape to use for shape casting check. + * @param {Vec3} startPosition - The world space starting position for the shape to be. + * @param {Vec3} endPosition - The world space ending position for the shape to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * @param {boolean} [destroyShape] - Whether to destroy the shape after the cast. Defaults to true. * - * @returns {HitResult[]} An array of sphereCast hit results (0 length if there were no hits). + * @returns {HitResult} The first hit result (null if there were no hits). + * @private */ - sphereCastAll(radius, start, end) { - // Sweeping - const height = start.distance(end) + radius * 2; + _shapeCastFirst(shape, startPosition, endPosition, startRotation = Vec3.ZERO, endRotation = undefined, destroyShape = true) { + Debug.assert(Ammo.ClosestConvexResultCallback && Ammo.ClosestConvexResultCallback.get_m_hitCollisionObject, 'pc.RigidBodyComponentSystem#_shapeCastFirst: Your version of ammo.js does not expose Ammo.ClosestConvexResultCallback or Ammo.ClosestConvexResultCallback#get_m_hitCollisionObject. Update it to latest.'); - // Find rotation - shapecastRotationMatrix.setLookAt(start, end, Vec3.UP); - shapecastRotation.setFromMat4(shapecastRotationMatrix); + let result = null; - // Transform start vector to make it beween initial start and end. - shapecastPosition.lerp(start, end, 0.5); + ammoVec3.setValue(startPosition.x, startPosition.y, startPosition.z); + if (startRotation instanceof Quat) { + ammoQuat.setValue(startRotation.x, startRotation.y, startRotation.z, startRotation.w); + } else { + ammoQuat.setEulerZYX(startRotation.z, startRotation.y, startRotation.x); + } - return this._shapeTest(new Ammo.btCapsuleShapeZ(radius, height), shapecastPosition, shapecastRotation); + // Assign position and rotation to origin transform. + ammoTransform.setIdentity(); + ammoTransform.setOrigin(ammoVec3); + ammoTransform.setRotation(ammoQuat); + + ammoVec3.setValue(endPosition.x, endPosition.y, endPosition.z); + if (endRotation) { + if (endRotation instanceof Quat) { + ammoQuat.setValue(endRotation.x, endRotation.y, endRotation.z, endRotation.w); + } else { + ammoQuat.setEulerZYX(endRotation.z, endRotation.y, endRotation.x); + } + } + + // Assign position and rotation to destination transform. + ammoTransform2.setIdentity(); + ammoTransform2.setOrigin(ammoVec3); + ammoTransform2.setRotation(ammoQuat); + + // Callback for the contactTest results. + const resultCallback = new Ammo.ClosestConvexResultCallback(); + + // Check for contacts. + this.app.systems.rigidbody.dynamicsWorld.convexSweepTest(shape, ammoTransform, ammoTransform2, resultCallback); + + if (resultCallback.hasHit()) { + const body = Ammo.castObject(resultCallback.get_m_hitCollisionObject(), Ammo.btRigidBody); + if (body) { + const point = resultCallback.get_m_hitPointWorld(); + const normal = resultCallback.get_m_hitNormalWorld(); + + result = new HitResult( + body.entity, + new Vec3(point.x(), point.y(), point.z()), + new Vec3(normal.x(), normal.y(), normal.z()) + ); + } + } + + // Destroy unused variables for performance. + Ammo.destroy(resultCallback); + if (destroyShape) { + Ammo.destroy(shape); + } + + return result; } /** @@ -596,12 +783,12 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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.halfExtents - The half-extents of the box in the x, y and z axes. - * @param {number} shape.height - The total height of the capsule, cylinder or cone from tip to tip. + * @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.halfExtents] - The half-extents of the box in the x, y and z axes. + * @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 {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|Quat} [rotation] - The world space rotation for the shape to have. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). @@ -627,7 +814,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * detected, the returned array will be of length 0. * * @param {Vec3} halfExtents - 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} position - The world space position for the box to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the box to have. * * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). @@ -645,7 +832,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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} position - The world space position for the capsule to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the capsule to have. * * @returns {HitResult[]} An array of capsuletest hit results (0 length if there were no hits). @@ -670,7 +857,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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} position - The world space position for the cone to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the cone to have. * * @returns {HitResult[]} An array of conetest hit results (0 length if there were no hits). @@ -695,7 +882,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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. + * @param {Vec3} position - The world space position for the cylinder to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. * * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). @@ -718,7 +905,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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} position - The world space position for the sphere to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the sphere to have. * * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). @@ -732,15 +919,16 @@ class RigidBodyComponentSystem extends ComponentSystem { * It returns an array of {@link HitResult}. 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 {object} shape - The Ammo.btCollisionShape to use for collision check. + * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. + * @param {boolean} destroyShape - Whether to destroy the shape once done. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). * @private */ - _shapeTest(shape, position = Vec3.ZERO, rotation = Vec3.ZERO) { - Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapecast: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); + _shapeTestAll(shape, position, rotation = Vec3.ZERO, destroyShape = true) { + Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapeTestAll: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); const results = []; @@ -803,7 +991,9 @@ class RigidBodyComponentSystem extends ComponentSystem { // Destroy unused variables for performance. Ammo.destroy(resultCallback); - Ammo.destroy(shape); + if (destroyShape) { + Ammo.destroy(shape); + } return results; } From cf21ed85c2c8af659cb32442a71646d559f4f061 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:41:30 +0000 Subject: [PATCH 39/54] Changed T and C to lowercase from Test and Cast --- src/framework/components/rigid-body/system.js | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index bc9f077f151..78e0fa1d76c 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -16,7 +16,7 @@ import { RigidBodyComponentData } from './data.js'; let ammoRayStart, ammoRayEnd, ammoVec3, ammoQuat, ammoTransform, ammoTransform2; // RigidBody for shape tests. Permanent to save performance. -let shapeTestBody; +let shapetestBody; /** * Object holding the result of a successful hit. @@ -579,18 +579,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - shapeCastFirst(shape, startPosition, endPosition, startRotation, endRotation) { + shapecastFirst(shape, startPosition, endPosition, startRotation, endRotation) { switch (shape.type) { case 'capsule': - return this.capsuleCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.capsulecastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); case 'cone': - return this.coneCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.conecastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); case 'cylinder': - return this.cylinderCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.cylindercastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); case 'sphere': - return this.sphereCastFirst(shape.radius, startPosition, endPosition); + return this.spherecastFirst(shape.radius, startPosition, endPosition); default: - return this.boxCastFirst(shape.halfExtents, startPosition, endPosition, startRotation, endRotation); + return this.boxcastFirst(shape.halfExtents, startPosition, endPosition, startRotation, endRotation); } } @@ -606,9 +606,9 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - boxCastFirst(halfExtents, startPosition, endPosition, startRotation, endRotation) { + boxcastFirst(halfExtents, startPosition, endPosition, startRotation, endRotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapeCastFirst(new Ammo.btBoxShape(ammoVec3), startPosition, endPosition, startRotation, endRotation); + return this._shapecastFirst(new Ammo.btBoxShape(ammoVec3), startPosition, endPosition, startRotation, endRotation); } /** @@ -625,7 +625,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - capsuleCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + capsulecastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { let fn = 'btCapsuleShape'; if (axis === 0) { @@ -634,7 +634,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCapsuleShapeZ'; } - return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapecastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -651,7 +651,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - coneCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + conecastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { let fn = 'btConeShape'; if (axis === 0) { @@ -660,7 +660,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btConeShapeZ'; } - return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapecastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -677,7 +677,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - cylinderCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + cylindercastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { let fn = 'btCylinderShape'; if (axis === 0) { @@ -686,7 +686,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCylinderShapeZ'; } - return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapecastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -699,8 +699,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - sphereCastFirst(radius, startPosition, endPosition) { - return this._shapeCastFirst(new Ammo.btSphereShape(radius), startPosition, endPosition); + spherecastFirst(radius, startPosition, endPosition) { + return this._shapecastFirst(new Ammo.btSphereShape(radius), startPosition, endPosition); } /** @@ -717,8 +717,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {HitResult} The first hit result (null if there were no hits). * @private */ - _shapeCastFirst(shape, startPosition, endPosition, startRotation = Vec3.ZERO, endRotation = undefined, destroyShape = true) { - Debug.assert(Ammo.ClosestConvexResultCallback && Ammo.ClosestConvexResultCallback.get_m_hitCollisionObject, 'pc.RigidBodyComponentSystem#_shapeCastFirst: Your version of ammo.js does not expose Ammo.ClosestConvexResultCallback or Ammo.ClosestConvexResultCallback#get_m_hitCollisionObject. Update it to latest.'); + _shapecastFirst(shape, startPosition, endPosition, startRotation = Vec3.ZERO, endRotation = undefined, destroyShape = true) { + Debug.assert(Ammo.ClosestConvexResultCallback && Ammo.ClosestConvexResultCallback.get_m_hitCollisionObject, 'pc.RigidBodyComponentSystem#_shapecastFirst: Your version of ammo.js does not expose Ammo.ClosestConvexResultCallback or Ammo.ClosestConvexResultCallback#get_m_hitCollisionObject. Update it to latest.'); let result = null; @@ -791,20 +791,20 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. * - * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of shapetest hit results (0 length if there were no hits). */ - shapeTestAll(shape, position, rotation) { + shapetestAll(shape, position, rotation) { switch (shape.type) { case 'capsule': - return this.capsuleTestAll(shape.radius, shape.height, shape.axis, position, rotation); + return this.capsuletestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'cone': - return this.coneTestAll(shape.radius, shape.height, shape.axis, position, rotation); + return this.conetestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'cylinder': - return this.cylinderTestAll(shape.radius, shape.height, shape.axis, position, rotation); + return this.cylindertestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'sphere': - return this.sphereTestAll(shape.radius, position, rotation); + return this.spheretestAll(shape.radius, position, rotation); default: - return this.boxTestAll(shape.halfExtents, position, rotation); + return this.boxtestAll(shape.halfExtents, position, rotation); } } @@ -819,9 +819,9 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). */ - boxTestAll(halfExtents, position, rotation) { + boxtestAll(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapeTestAll(new Ammo.btBoxShape(ammoVec3), position, rotation); + return this._shapetestAll(new Ammo.btBoxShape(ammoVec3), position, rotation); } /** @@ -837,7 +837,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of capsuletest hit results (0 length if there were no hits). */ - capsuleTestAll(radius, height, axis, position, rotation) { + capsuletestAll(radius, height, axis, position, rotation) { let fn = 'btCapsuleShape'; if (axis === 0) { @@ -846,7 +846,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCapsuleShapeZ'; } - return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapetestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -862,7 +862,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of conetest hit results (0 length if there were no hits). */ - coneTestAll(radius, height, axis, position, rotation) { + conetestAll(radius, height, axis, position, rotation) { let fn = 'btConeShape'; if (axis === 0) { @@ -871,7 +871,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btConeShapeZ'; } - return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapetestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -887,7 +887,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). */ - cylinderTestAll(radius, height, axis, position, rotation) { + cylindertestAll(radius, height, axis, position, rotation) { let fn = 'btCylinderShape'; if (axis === 0) { @@ -896,7 +896,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCylinderShapeZ'; } - return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapetestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -910,8 +910,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). */ - sphereTestAll(radius, position, rotation) { - return this._shapeTestAll(new Ammo.btSphereShape(radius), position, rotation); + spheretestAll(radius, position, rotation) { + return this._shapetestAll(new Ammo.btSphereShape(radius), position, rotation); } /** @@ -924,11 +924,11 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. * @param {boolean} destroyShape - Whether to destroy the shape once done. * - * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of shapetest hit results (0 length if there were no hits). * @private */ - _shapeTestAll(shape, position, rotation = Vec3.ZERO, destroyShape = true) { - Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapeTestAll: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); + _shapetestAll(shape, position, rotation = Vec3.ZERO, destroyShape = true) { + Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapetestAll: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); const results = []; @@ -948,14 +948,14 @@ class RigidBodyComponentSystem extends ComponentSystem { ammoTransform.setRotation(ammoQuat); // We only initialize the shapeTast body here so we don't have an extra body unless the user uses this function - if (!shapeTestBody) { - shapeTestBody = this.createBody(0, shape, ammoTransform); + if (!shapetestBody) { + shapetestBody = this.createBody(0, shape, ammoTransform); } // Make sure the body has proper shape, transform and is active. - shapeTestBody.setCollisionShape(shape); - shapeTestBody.setWorldTransform(ammoTransform); - shapeTestBody.forceActivationState(BODYSTATE_ACTIVE_TAG); + shapetestBody.setCollisionShape(shape); + shapetestBody.setWorldTransform(ammoTransform); + shapetestBody.forceActivationState(BODYSTATE_ACTIVE_TAG); // Callback for the contactTest results. const resultCallback = new Ammo.ConcreteContactResultCallback(); @@ -984,10 +984,10 @@ class RigidBodyComponentSystem extends ComponentSystem { }; // Check for contacts. - this.dynamicsWorld.contactTest(shapeTestBody, resultCallback); + this.dynamicsWorld.contactTest(shapetestBody, resultCallback); // Remove body shape. - shapeTestBody.setCollisionShape(null); + shapetestBody.setCollisionShape(null); // Destroy unused variables for performance. Ammo.destroy(resultCallback); From f5d91b6a4070525acec77d02efa6fcf6435909cd Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:30:03 +0000 Subject: [PATCH 40/54] Reverted previous commit --- src/framework/components/rigid-body/system.js | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 78e0fa1d76c..bc9f077f151 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -16,7 +16,7 @@ import { RigidBodyComponentData } from './data.js'; let ammoRayStart, ammoRayEnd, ammoVec3, ammoQuat, ammoTransform, ammoTransform2; // RigidBody for shape tests. Permanent to save performance. -let shapetestBody; +let shapeTestBody; /** * Object holding the result of a successful hit. @@ -579,18 +579,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - shapecastFirst(shape, startPosition, endPosition, startRotation, endRotation) { + shapeCastFirst(shape, startPosition, endPosition, startRotation, endRotation) { switch (shape.type) { case 'capsule': - return this.capsulecastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.capsuleCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); case 'cone': - return this.conecastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.coneCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); case 'cylinder': - return this.cylindercastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.cylinderCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); case 'sphere': - return this.spherecastFirst(shape.radius, startPosition, endPosition); + return this.sphereCastFirst(shape.radius, startPosition, endPosition); default: - return this.boxcastFirst(shape.halfExtents, startPosition, endPosition, startRotation, endRotation); + return this.boxCastFirst(shape.halfExtents, startPosition, endPosition, startRotation, endRotation); } } @@ -606,9 +606,9 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - boxcastFirst(halfExtents, startPosition, endPosition, startRotation, endRotation) { + boxCastFirst(halfExtents, startPosition, endPosition, startRotation, endRotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapecastFirst(new Ammo.btBoxShape(ammoVec3), startPosition, endPosition, startRotation, endRotation); + return this._shapeCastFirst(new Ammo.btBoxShape(ammoVec3), startPosition, endPosition, startRotation, endRotation); } /** @@ -625,7 +625,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - capsulecastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + capsuleCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { let fn = 'btCapsuleShape'; if (axis === 0) { @@ -634,7 +634,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCapsuleShapeZ'; } - return this._shapecastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -651,7 +651,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - conecastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + coneCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { let fn = 'btConeShape'; if (axis === 0) { @@ -660,7 +660,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btConeShapeZ'; } - return this._shapecastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -677,7 +677,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - cylindercastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { + cylinderCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { let fn = 'btCylinderShape'; if (axis === 0) { @@ -686,7 +686,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCylinderShapeZ'; } - return this._shapecastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -699,8 +699,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult} The first hit result (null if there were no hits). */ - spherecastFirst(radius, startPosition, endPosition) { - return this._shapecastFirst(new Ammo.btSphereShape(radius), startPosition, endPosition); + sphereCastFirst(radius, startPosition, endPosition) { + return this._shapeCastFirst(new Ammo.btSphereShape(radius), startPosition, endPosition); } /** @@ -717,8 +717,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {HitResult} The first hit result (null if there were no hits). * @private */ - _shapecastFirst(shape, startPosition, endPosition, startRotation = Vec3.ZERO, endRotation = undefined, destroyShape = true) { - Debug.assert(Ammo.ClosestConvexResultCallback && Ammo.ClosestConvexResultCallback.get_m_hitCollisionObject, 'pc.RigidBodyComponentSystem#_shapecastFirst: Your version of ammo.js does not expose Ammo.ClosestConvexResultCallback or Ammo.ClosestConvexResultCallback#get_m_hitCollisionObject. Update it to latest.'); + _shapeCastFirst(shape, startPosition, endPosition, startRotation = Vec3.ZERO, endRotation = undefined, destroyShape = true) { + Debug.assert(Ammo.ClosestConvexResultCallback && Ammo.ClosestConvexResultCallback.get_m_hitCollisionObject, 'pc.RigidBodyComponentSystem#_shapeCastFirst: Your version of ammo.js does not expose Ammo.ClosestConvexResultCallback or Ammo.ClosestConvexResultCallback#get_m_hitCollisionObject. Update it to latest.'); let result = null; @@ -791,20 +791,20 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. * - * @returns {HitResult[]} An array of shapetest hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). */ - shapetestAll(shape, position, rotation) { + shapeTestAll(shape, position, rotation) { switch (shape.type) { case 'capsule': - return this.capsuletestAll(shape.radius, shape.height, shape.axis, position, rotation); + return this.capsuleTestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'cone': - return this.conetestAll(shape.radius, shape.height, shape.axis, position, rotation); + return this.coneTestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'cylinder': - return this.cylindertestAll(shape.radius, shape.height, shape.axis, position, rotation); + return this.cylinderTestAll(shape.radius, shape.height, shape.axis, position, rotation); case 'sphere': - return this.spheretestAll(shape.radius, position, rotation); + return this.sphereTestAll(shape.radius, position, rotation); default: - return this.boxtestAll(shape.halfExtents, position, rotation); + return this.boxTestAll(shape.halfExtents, position, rotation); } } @@ -819,9 +819,9 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). */ - boxtestAll(halfExtents, position, rotation) { + boxTestAll(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapetestAll(new Ammo.btBoxShape(ammoVec3), position, rotation); + return this._shapeTestAll(new Ammo.btBoxShape(ammoVec3), position, rotation); } /** @@ -837,7 +837,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of capsuletest hit results (0 length if there were no hits). */ - capsuletestAll(radius, height, axis, position, rotation) { + capsuleTestAll(radius, height, axis, position, rotation) { let fn = 'btCapsuleShape'; if (axis === 0) { @@ -846,7 +846,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCapsuleShapeZ'; } - return this._shapetestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -862,7 +862,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of conetest hit results (0 length if there were no hits). */ - conetestAll(radius, height, axis, position, rotation) { + coneTestAll(radius, height, axis, position, rotation) { let fn = 'btConeShape'; if (axis === 0) { @@ -871,7 +871,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btConeShapeZ'; } - return this._shapetestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -887,7 +887,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). */ - cylindertestAll(radius, height, axis, position, rotation) { + cylinderTestAll(radius, height, axis, position, rotation) { let fn = 'btCylinderShape'; if (axis === 0) { @@ -896,7 +896,7 @@ class RigidBodyComponentSystem extends ComponentSystem { fn = 'btCylinderShapeZ'; } - return this._shapetestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); } /** @@ -910,8 +910,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). */ - spheretestAll(radius, position, rotation) { - return this._shapetestAll(new Ammo.btSphereShape(radius), position, rotation); + sphereTestAll(radius, position, rotation) { + return this._shapeTestAll(new Ammo.btSphereShape(radius), position, rotation); } /** @@ -924,11 +924,11 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. * @param {boolean} destroyShape - Whether to destroy the shape once done. * - * @returns {HitResult[]} An array of shapetest hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). * @private */ - _shapetestAll(shape, position, rotation = Vec3.ZERO, destroyShape = true) { - Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapetestAll: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); + _shapeTestAll(shape, position, rotation = Vec3.ZERO, destroyShape = true) { + Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapeTestAll: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); const results = []; @@ -948,14 +948,14 @@ class RigidBodyComponentSystem extends ComponentSystem { ammoTransform.setRotation(ammoQuat); // We only initialize the shapeTast body here so we don't have an extra body unless the user uses this function - if (!shapetestBody) { - shapetestBody = this.createBody(0, shape, ammoTransform); + if (!shapeTestBody) { + shapeTestBody = this.createBody(0, shape, ammoTransform); } // Make sure the body has proper shape, transform and is active. - shapetestBody.setCollisionShape(shape); - shapetestBody.setWorldTransform(ammoTransform); - shapetestBody.forceActivationState(BODYSTATE_ACTIVE_TAG); + shapeTestBody.setCollisionShape(shape); + shapeTestBody.setWorldTransform(ammoTransform); + shapeTestBody.forceActivationState(BODYSTATE_ACTIVE_TAG); // Callback for the contactTest results. const resultCallback = new Ammo.ConcreteContactResultCallback(); @@ -984,10 +984,10 @@ class RigidBodyComponentSystem extends ComponentSystem { }; // Check for contacts. - this.dynamicsWorld.contactTest(shapetestBody, resultCallback); + this.dynamicsWorld.contactTest(shapeTestBody, resultCallback); // Remove body shape. - shapetestBody.setCollisionShape(null); + shapeTestBody.setCollisionShape(null); // Destroy unused variables for performance. Ammo.destroy(resultCallback); From 1ccc26a7a127be751d766724d1fa39f4feb1d34c Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:33:01 +0100 Subject: [PATCH 41/54] Reduced code length --- src/framework/components/rigid-body/system.js | 81 +++++++------------ 1 file changed, 27 insertions(+), 54 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index bc9f077f151..9408eb4a3ac 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -234,6 +234,27 @@ class ContactResult { const _schema = ['enabled']; +/** + * Creates a new shape. + * + * @param {string} name - Name of the shape. Must start with capital letter. + * @param {number} [axis] - The local space axis with which the shape's length is aligned. 0 for X, 1 for Y and 2 for Z. Defaults to 1 (Y-axis). + * @param {...any} [args] - Arguments to pass to creation. + * @returns {object} Created Ammo.btCollisionShape. + * @ignore + */ +function createShape(name, axis, ...args) { + let fn = `bt${name}Shape`; + + if (axis === 0) { + fn += 'X'; + } else if (axis === 2) { + fn += 'Z'; + } + + return new Ammo[fn](...args); +} + /** * The RigidBodyComponentSystem maintains the dynamics world for simulating rigid bodies, it also * controls global values for the world such as gravity. Note: The RigidBodyComponentSystem is only @@ -626,15 +647,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {HitResult} The first hit result (null if there were no hits). */ capsuleCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { - let fn = 'btCapsuleShape'; - - if (axis === 0) { - fn = 'btCapsuleShapeX'; - } else if (axis === 2) { - fn = 'btCapsuleShapeZ'; - } - - return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapeCastFirst(createShape('Capsule', axis, radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -652,15 +665,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {HitResult} The first hit result (null if there were no hits). */ coneCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { - let fn = 'btConeShape'; - - if (axis === 0) { - fn = 'btConeShapeX'; - } else if (axis === 2) { - fn = 'btConeShapeZ'; - } - - return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapeCastFirst(createShape('Cone', axis, radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -678,15 +683,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {HitResult} The first hit result (null if there were no hits). */ cylinderCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { - let fn = 'btCylinderShape'; - - if (axis === 0) { - fn = 'btCylinderShapeX'; - } else if (axis === 2) { - fn = 'btCylinderShapeZ'; - } - - return this._shapeCastFirst(new Ammo[fn](radius, height), startPosition, endPosition, startRotation, endRotation); + return this._shapeCastFirst(createShape('Cylinder', axis, radius, height), startPosition, endPosition, startRotation, endRotation); } /** @@ -838,15 +835,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {HitResult[]} An array of capsuletest hit results (0 length if there were no hits). */ capsuleTestAll(radius, height, axis, position, rotation) { - let fn = 'btCapsuleShape'; - - if (axis === 0) { - fn = 'btCapsuleShapeX'; - } else if (axis === 2) { - fn = 'btCapsuleShapeZ'; - } - - return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(createShape('Capsule', axis, radius, height), position, rotation); } /** @@ -863,15 +852,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {HitResult[]} An array of conetest hit results (0 length if there were no hits). */ coneTestAll(radius, height, axis, position, rotation) { - let fn = 'btConeShape'; - - if (axis === 0) { - fn = 'btConeShapeX'; - } else if (axis === 2) { - fn = 'btConeShapeZ'; - } - - return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(createShape('Cone', axis, radius, height), position, rotation); } /** @@ -888,15 +869,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). */ cylinderTestAll(radius, height, axis, position, rotation) { - let fn = 'btCylinderShape'; - - if (axis === 0) { - fn = 'btCylinderShapeX'; - } else if (axis === 2) { - fn = 'btCylinderShapeZ'; - } - - return this._shapeTestAll(new Ammo[fn](radius, height), position, rotation); + return this._shapeTestAll(createShape('Cylinder', axis, radius, height), position, rotation); } /** From 377f5aec8eba162de2590a040e3581bbc5dc11dd Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Sun, 19 Mar 2023 10:27:09 +0000 Subject: [PATCH 42/54] Added support for `shapeTestFirst` --- src/framework/components/rigid-body/system.js | 159 +++++++++++++++++- 1 file changed, 154 insertions(+), 5 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 9408eb4a3ac..11338ecc55d 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -832,7 +832,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the capsule to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the capsule to have. * - * @returns {HitResult[]} An array of capsuletest hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of capsuleTest hit results (0 length if there were no hits). */ capsuleTestAll(radius, height, axis, position, rotation) { return this._shapeTestAll(createShape('Capsule', axis, radius, height), position, rotation); @@ -849,7 +849,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the cone to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the cone to have. * - * @returns {HitResult[]} An array of conetest hit results (0 length if there were no hits). + * @returns {HitResult[]} An array of coneTest hit results (0 length if there were no hits). */ coneTestAll(radius, height, axis, position, rotation) { return this._shapeTestAll(createShape('Cone', axis, radius, height), position, rotation); @@ -895,13 +895,154 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {object} shape - The Ammo.btCollisionShape to use for collision check. * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {boolean} destroyShape - Whether to destroy the shape once done. + * @param {boolean} [destroyShape] - Whether to destroy the shape once done. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). - * @private + * @ignore */ _shapeTestAll(shape, position, rotation = Vec3.ZERO, destroyShape = true) { - Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapeTestAll: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); + return this._shapeTest(shape, position, rotation, false, destroyShape); + } + + /** + * Perform a collision check on the world and return the first entity the shape hits. + * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. + * + * @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.halfExtents] - The half-extents of the box in the x, y and z axes. + * @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|Quat} [rotation] - The world space rotation for the shape to have. + * + * @returns {HitResult|null} A shapeTest hit result (null if there were no hits). + */ + shapeTestFirst(shape, position, rotation) { + switch (shape.type) { + case 'capsule': + return this.capsuleTestFirst(shape.radius, shape.height, shape.axis, position, rotation); + case 'cone': + return this.coneTestFirst(shape.radius, shape.height, shape.axis, position, rotation); + case 'cylinder': + return this.cylinderTestFirst(shape.radius, shape.height, shape.axis, position, rotation); + case 'sphere': + return this.sphereTestFirst(shape.radius, position, rotation); + default: + return this.boxTestFirst(shape.halfExtents, position, rotation); + } + } + + /** + * Perform a collision check on the world and return the first entity the box hits. + * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. + * + * @param {Vec3} halfExtents - 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|Quat} [rotation] - The world space rotation for the box to have. + * + * @returns {HitResult|null} A boxTest hit result (null if there were no hits). + */ + boxTestFirst(halfExtents, position, rotation) { + ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); + return this._shapeTestFirst(new Ammo.btBoxShape(ammoVec3), position, rotation); + } + + /** + * Perform a collision check on the world and return the first entity the capsule hits. + * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. + * + * @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|Quat} [rotation] - The world space rotation for the capsule to have. + * + * @returns {HitResult|null} A capsuleTest hit result (null if there were no hits). + */ + capsuleTestFirst(radius, height, axis, position, rotation) { + return this._shapeTestFirst(createShape('Capsule', axis, radius, height), position, rotation); + } + + /** + * Perform a collision check on the world and return the first entity the cone hits. + * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. + * + * @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|Quat} [rotation] - The world space rotation for the cone to have. + * + * @returns {HitResult|null} A coneTest hit result (null if there were no hits). + */ + coneTestFirst(radius, height, axis, position, rotation) { + return this._shapeTestFirst(createShape('Cone', axis, radius, height), position, rotation); + } + + /** + * Perform a collision check on the world and return the first entity the cylinder hits. + * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. + * + * @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. + * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. + * + * @returns {HitResult|null} A cylinderTest hit result (null if there were no hits). + */ + cylinderTestFirst(radius, height, axis, position, rotation) { + return this._shapeTestFirst(createShape('Cylinder', axis, radius, height), position, rotation); + } + + /** + * Perform a collision check on the world and return the first entity the sphere hits. + * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. + * + * @param {number} radius - The radius of the sphere. + * @param {Vec3} position - The world space position for the sphere to be. + * @param {Vec3|Quat} [rotation] - The world space rotation for the sphere to have. + * + * @returns {HitResult|null} A sphereTest hit result (null if there were no hits). + */ + sphereTestFirst(radius, position, rotation) { + return this._shapeTestFirst(new Ammo.btSphereShape(radius), position, rotation); + } + + /** + * Perform a collision check on the world and return the first entity the shape hits. + * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. + * + * @param {object} shape - The Ammo.btCollisionShape to use for collision check. + * @param {Vec3} position - The world space position for the shape to be. + * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. + * @param {boolean} [destroyShape] - Whether to destroy the shape once done. + * + * @returns {HitResult|null} The shapeTest hit result (null if there were no hits). + * @ignore + */ + _shapeTestFirst(shape, position, rotation = Vec3.ZERO, destroyShape = true) { + return this._shapeTest(shape, position, rotation, true, destroyShape)[0] || null; + } + + /** + * Perform a collision check on the world and return all entities the shape hits. + * It returns an array of {@link HitResult}. If no hits are + * detected, the returned array will be of length 0. + * + * @param {object} shape - The Ammo.btCollisionShape to use for collision check. + * @param {Vec3} position - The world space position for the shape to be. + * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. + * @param {boolean} [firstOnly] - Whether only the first hit should be saved. + * @param {boolean} [destroyShape] - Whether to destroy the shape once done. + * + * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). + * @private + */ + _shapeTest(shape, position, rotation = Vec3.ZERO, firstOnly = true, destroyShape = true) { + Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapeTest: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); const results = []; @@ -933,6 +1074,10 @@ class RigidBodyComponentSystem extends ComponentSystem { // Callback for the contactTest results. const resultCallback = new Ammo.ConcreteContactResultCallback(); resultCallback.addSingleResult = function (cp, colObj0Wrap, partId0, index0, colObj1Wrap, p1, index1) { + if (firstOnly && results.length === 1) { + return 0; + } + // Retrieve collided entity. const body1 = Ammo.castObject(Ammo.wrapPointer(colObj1Wrap, Ammo.btCollisionObjectWrapper).getCollisionObject(), Ammo.btRigidBody); @@ -952,8 +1097,12 @@ class RigidBodyComponentSystem extends ComponentSystem { new Vec3(point.x(), point.y(), point.z()), new Vec3(normal.x(), normal.y(), normal.z()) )); + + return 1; } } + + return 0; }; // Check for contacts. From 383e9ad6fdba195605a128f8605aac41d2125402 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Tue, 21 Mar 2023 22:32:56 +0100 Subject: [PATCH 43/54] Resolve conflicts from source --- src/framework/components/rigid-body/system.js | 82 +++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 11338ecc55d..34ded95920b 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -28,9 +28,11 @@ class HitResult { * @param {import('../../entity.js').Entity} entity - The entity that was hit. * @param {Vec3} point - The point at which the ray hit the entity in world space. * @param {Vec3} normal - The normal vector of the surface where the ray hit in world space. + * @param {number} hitFraction - The normalized distance (between 0 and 1) at which the hit + * occurred from the starting point. * @hideconstructor */ - constructor(entity, point, normal) { + constructor(entity, point, normal, hitFraction) { /** * The entity that was hit. * @@ -51,6 +53,14 @@ class HitResult { * @type {Vec3} */ this.normal = normal; + + /** + * The normalized distance (between 0 and 1) at which the ray hit occurred from the + * starting point. + * + * @type {number} + */ + this.hitFraction = hitFraction; } } @@ -513,6 +523,7 @@ class RigidBodyComponentSystem extends ComponentSystem { if (rayCallback.hasHit()) { const collisionObj = rayCallback.get_m_collisionObject(); const body = Ammo.castObject(collisionObj, Ammo.btRigidBody); + if (body) { const point = rayCallback.get_m_hitPointWorld(); const normal = rayCallback.get_m_hitNormalWorld(); @@ -520,7 +531,8 @@ class RigidBodyComponentSystem extends ComponentSystem { result = new HitResult( body.entity, new Vec3(point.x(), point.y(), point.z()), - new Vec3(normal.x(), normal.y(), normal.z()) + new Vec3(normal.x(), normal.y(), normal.z()), + rayCallback.get_m_closestHitFraction() ); // keeping for backwards compatibility @@ -541,11 +553,12 @@ class RigidBodyComponentSystem extends ComponentSystem { /** * Raycast the world and return all entities the ray hits. It returns an array of * {@link HitResult}, one for each hit. If no hits are detected, the returned array will be - * of length 0. + * of length 0. Results are sorted by distance with closest first. * * @param {Vec3} start - The world space point where the ray starts. * @param {Vec3} end - The world space point where the ray ends. * @returns {HitResult[]} An array of raycast hit results (0 length if there were no hits). + * Results are sorted by distance with closest first. */ raycastAll(start, end) { Debug.assert(Ammo.AllHitsRayResultCallback, 'pc.RigidBodyComponentSystem#raycastAll: Your version of ammo.js does not expose Ammo.AllHitsRayResultCallback. Update it to latest.'); @@ -561,6 +574,7 @@ class RigidBodyComponentSystem extends ComponentSystem { const collisionObjs = rayCallback.get_m_collisionObjects(); const points = rayCallback.get_m_hitPointWorld(); const normals = rayCallback.get_m_hitNormalWorld(); + const hitFractions = rayCallback.get_m_hitFractions(); const numHits = collisionObjs.size(); for (let i = 0; i < numHits; i++) { @@ -571,11 +585,15 @@ class RigidBodyComponentSystem extends ComponentSystem { const result = new HitResult( body.entity, new Vec3(point.x(), point.y(), point.z()), - new Vec3(normal.x(), normal.y(), normal.z()) + new Vec3(normal.x(), normal.y(), normal.z()), + hitFractions.at(i) ); + results.push(result); } } + + results.sort((a, b) => a.hitFraction - b.hitFraction); } Ammo.destroy(rayCallback); @@ -588,17 +606,19 @@ class RigidBodyComponentSystem extends ComponentSystem { * It returns a {@link HitResult}. If no hits are detected, returned value will be null. * * @param {object} shape - The shape to use for sweep test. - * @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 {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.halfExtents] - The half-extents of the box in the x, y and z axes. * @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 {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} startPosition - The world space starting position for the shape to be. * @param {Vec3} endPosition - The world space ending position for the shape to be. * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. * - * @returns {HitResult} The first hit result (null if there were no hits). + * @returns {HitResult|null} The first hit result (null if there were no hits). */ shapeCastFirst(shape, startPosition, endPosition, startRotation, endRotation) { switch (shape.type) { @@ -638,7 +658,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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} startPosition - The world space starting position for the shape to be. * @param {Vec3} endPosition - The world space ending position for the shape to be. * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. @@ -656,7 +677,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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} startPosition - The world space starting position for the shape to be. * @param {Vec3} endPosition - The world space ending position for the shape to be. * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. @@ -674,7 +696,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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} startPosition - The world space starting position for the shape to be. * @param {Vec3} endPosition - The world space ending position for the shape to be. * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. @@ -760,7 +783,8 @@ class RigidBodyComponentSystem extends ComponentSystem { result = new HitResult( body.entity, new Vec3(point.x(), point.y(), point.z()), - new Vec3(normal.x(), normal.y(), normal.z()) + new Vec3(normal.x(), normal.y(), normal.z()), + 0 ); } } @@ -780,10 +804,12 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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 {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.halfExtents] - The half-extents of the box in the x, y and z axes. * @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 {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|Quat} [rotation] - The world space rotation for the shape to have. @@ -828,7 +854,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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|Quat} [rotation] - The world space rotation for the capsule to have. * @@ -845,7 +872,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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|Quat} [rotation] - The world space rotation for the cone to have. * @@ -862,7 +890,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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. * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. * @@ -909,10 +938,12 @@ class RigidBodyComponentSystem extends ComponentSystem { * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. * * @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 {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.halfExtents] - The half-extents of the box in the x, y and z axes. * @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 {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|Quat} [rotation] - The world space rotation for the shape to have. @@ -955,7 +986,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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|Quat} [rotation] - The world space rotation for the capsule to have. * @@ -971,7 +1003,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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|Quat} [rotation] - The world space rotation for the cone to have. * @@ -987,7 +1020,8 @@ class RigidBodyComponentSystem extends ComponentSystem { * * @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 {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. * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. * @@ -1087,7 +1121,8 @@ class RigidBodyComponentSystem extends ComponentSystem { const manifold = Ammo.wrapPointer(cp, Ammo.btManifoldPoint); // Make sure there is a collision - if (manifold.getDistance() < 0) { + const distance = manifold.getDistance(); + if (distance < 0) { const point = manifold.get_m_positionWorldOnB(); const normal = manifold.get_m_normalWorldOnB(); @@ -1095,7 +1130,8 @@ class RigidBodyComponentSystem extends ComponentSystem { results.push(new HitResult( body1.entity, new Vec3(point.x(), point.y(), point.z()), - new Vec3(normal.x(), normal.y(), normal.z()) + new Vec3(normal.x(), normal.y(), normal.z()), + 0 )); return 1; From e5c7a09f4c1732914b3ffce470139e7191de8ee9 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Tue, 21 Mar 2023 23:25:15 +0100 Subject: [PATCH 44/54] Match latest features from raycasting --- src/framework/components/rigid-body/system.js | 78 ++++++++++++------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 34ded95920b..b13ca6b55d4 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -30,9 +30,10 @@ class HitResult { * @param {Vec3} normal - The normal vector of the surface where the ray hit in world space. * @param {number} hitFraction - The normalized distance (between 0 and 1) at which the hit * occurred from the starting point. + * @param {number} distance - The distance at which the hit occurred from the starting point. * @hideconstructor */ - constructor(entity, point, normal, hitFraction) { + constructor(entity, point, normal, hitFraction, distance) { /** * The entity that was hit. * @@ -55,12 +56,19 @@ class HitResult { this.normal = normal; /** - * The normalized distance (between 0 and 1) at which the ray hit occurred from the - * starting point. + * The normalized distance (between 0 and 1) at which the hit occurred from the + * starting point toward the end. Prefer `distance` for shapes. * * @type {number} */ this.hitFraction = hitFraction; + + /** + * The distance at which the hit occurred from the starting point. + * + * @type {number} + */ + this.distance = distance; } } @@ -521,18 +529,22 @@ class RigidBodyComponentSystem extends ComponentSystem { this.dynamicsWorld.rayTest(ammoRayStart, ammoRayEnd, rayCallback); if (rayCallback.hasHit()) { + const rayDistance = start.distance(end); + const collisionObj = rayCallback.get_m_collisionObject(); const body = Ammo.castObject(collisionObj, Ammo.btRigidBody); if (body) { const point = rayCallback.get_m_hitPointWorld(); const normal = rayCallback.get_m_hitNormalWorld(); + const hitFraction = rayCallback.get_m_closestHitFraction(); result = new HitResult( body.entity, new Vec3(point.x(), point.y(), point.z()), new Vec3(normal.x(), normal.y(), normal.z()), - rayCallback.get_m_closestHitFraction() + hitFraction, + rayDistance * hitFraction ); // keeping for backwards compatibility @@ -571,6 +583,8 @@ class RigidBodyComponentSystem extends ComponentSystem { this.dynamicsWorld.rayTest(ammoRayStart, ammoRayEnd, rayCallback); if (rayCallback.hasHit()) { + const rayDistance = start.distance(end); + const collisionObjs = rayCallback.get_m_collisionObjects(); const points = rayCallback.get_m_hitPointWorld(); const normals = rayCallback.get_m_hitNormalWorld(); @@ -579,14 +593,18 @@ class RigidBodyComponentSystem extends ComponentSystem { const numHits = collisionObjs.size(); for (let i = 0; i < numHits; i++) { const body = Ammo.castObject(collisionObjs.at(i), Ammo.btRigidBody); + if (body) { const point = points.at(i); const normal = normals.at(i); + const hitFraction = hitFractions.at(i); + const result = new HitResult( body.entity, new Vec3(point.x(), point.y(), point.z()), new Vec3(normal.x(), normal.y(), normal.z()), - hitFractions.at(i) + hitFraction, + rayDistance * hitFraction ); results.push(result); @@ -732,12 +750,13 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} endPosition - The world space ending position for the shape to be. * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. - * @param {boolean} [destroyShape] - Whether to destroy the shape after the cast. Defaults to true. + * @param {object} [options] - Options to use when casting the shape. + * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the cast. Defaults to true. * * @returns {HitResult} The first hit result (null if there were no hits). * @private */ - _shapeCastFirst(shape, startPosition, endPosition, startRotation = Vec3.ZERO, endRotation = undefined, destroyShape = true) { + _shapeCastFirst(shape, startPosition, endPosition, startRotation = Vec3.ZERO, endRotation = undefined, options = {}) { Debug.assert(Ammo.ClosestConvexResultCallback && Ammo.ClosestConvexResultCallback.get_m_hitCollisionObject, 'pc.RigidBodyComponentSystem#_shapeCastFirst: Your version of ammo.js does not expose Ammo.ClosestConvexResultCallback or Ammo.ClosestConvexResultCallback#get_m_hitCollisionObject. Update it to latest.'); let result = null; @@ -780,18 +799,21 @@ class RigidBodyComponentSystem extends ComponentSystem { const point = resultCallback.get_m_hitPointWorld(); const normal = resultCallback.get_m_hitNormalWorld(); + const pointVec = new Vec3(point.x(), point.y(), point.z()); + result = new HitResult( body.entity, - new Vec3(point.x(), point.y(), point.z()), + pointVec, new Vec3(normal.x(), normal.y(), normal.z()), - 0 + resultCallback.get_m_closestHitFraction(), + startPosition.distance(pointVec) ); } } // Destroy unused variables for performance. Ammo.destroy(resultCallback); - if (destroyShape) { + if (options.destroyShape !== false) { Ammo.destroy(shape); } @@ -924,13 +946,14 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {object} shape - The Ammo.btCollisionShape to use for collision check. * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {boolean} [destroyShape] - Whether to destroy the shape once done. + * @param {object} [options] - Options to use when testing the shape. + * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to true. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). * @ignore */ - _shapeTestAll(shape, position, rotation = Vec3.ZERO, destroyShape = true) { - return this._shapeTest(shape, position, rotation, false, destroyShape); + _shapeTestAll(shape, position, rotation = Vec3.ZERO, options) { + return this._shapeTest(shape, position, rotation, options); } /** @@ -1052,13 +1075,14 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {object} shape - The Ammo.btCollisionShape to use for collision check. * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {boolean} [destroyShape] - Whether to destroy the shape once done. + * @param {object} [options] - Options to use when testing the shape. + * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the tets. Defaults to true. * * @returns {HitResult|null} The shapeTest hit result (null if there were no hits). * @ignore */ - _shapeTestFirst(shape, position, rotation = Vec3.ZERO, destroyShape = true) { - return this._shapeTest(shape, position, rotation, true, destroyShape)[0] || null; + _shapeTestFirst(shape, position, rotation = Vec3.ZERO, options) { + return this._shapeTest(shape, position, rotation, options)[0] || null; } /** @@ -1069,13 +1093,13 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {object} shape - The Ammo.btCollisionShape to use for collision check. * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {boolean} [firstOnly] - Whether only the first hit should be saved. - * @param {boolean} [destroyShape] - Whether to destroy the shape once done. + * @param {object} [options] - Options to use when testing the shape. + * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to true. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). * @private */ - _shapeTest(shape, position, rotation = Vec3.ZERO, firstOnly = true, destroyShape = true) { + _shapeTest(shape, position, rotation = Vec3.ZERO, options = {}) { Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapeTest: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); const results = []; @@ -1108,10 +1132,6 @@ class RigidBodyComponentSystem extends ComponentSystem { // Callback for the contactTest results. const resultCallback = new Ammo.ConcreteContactResultCallback(); resultCallback.addSingleResult = function (cp, colObj0Wrap, partId0, index0, colObj1Wrap, p1, index1) { - if (firstOnly && results.length === 1) { - return 0; - } - // Retrieve collided entity. const body1 = Ammo.castObject(Ammo.wrapPointer(colObj1Wrap, Ammo.btCollisionObjectWrapper).getCollisionObject(), Ammo.btRigidBody); @@ -1126,12 +1146,16 @@ class RigidBodyComponentSystem extends ComponentSystem { const point = manifold.get_m_positionWorldOnB(); const normal = manifold.get_m_normalWorldOnB(); + const pointVec = new Vec3(point.x(), point.y(), point.z()); + const startDistance = position.distance(pointVec); + // Push the result. results.push(new HitResult( body1.entity, - new Vec3(point.x(), point.y(), point.z()), + pointVec, new Vec3(normal.x(), normal.y(), normal.z()), - 0 + startDistance / (startDistance - distance), // Minus distance as it's negative. + startDistance )); return 1; @@ -1149,11 +1173,11 @@ class RigidBodyComponentSystem extends ComponentSystem { // Destroy unused variables for performance. Ammo.destroy(resultCallback); - if (destroyShape) { + if (options.destroyShape !== false) { Ammo.destroy(shape); } - return results; + return results.sort((a, b) => a.distance - b.distance); } /** From 9802d4cea50b38982ad72572af79e0888458569f Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Tue, 21 Mar 2023 23:33:54 +0100 Subject: [PATCH 45/54] Added sorting documentation. --- src/framework/components/rigid-body/system.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index b13ca6b55d4..86f69738c93 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -837,6 +837,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). + * Results are ordered based on distance from starting position with closest first. */ shapeTestAll(shape, position, rotation) { switch (shape.type) { @@ -863,6 +864,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3|Quat} [rotation] - The world space rotation for the box to have. * * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). + * Results are ordered based on distance from starting position with closest first. */ boxTestAll(halfExtents, position, rotation) { ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); @@ -882,6 +884,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3|Quat} [rotation] - The world space rotation for the capsule to have. * * @returns {HitResult[]} An array of capsuleTest hit results (0 length if there were no hits). + * Results are ordered based on distance from starting position with closest first. */ capsuleTestAll(radius, height, axis, position, rotation) { return this._shapeTestAll(createShape('Capsule', axis, radius, height), position, rotation); @@ -900,6 +903,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3|Quat} [rotation] - The world space rotation for the cone to have. * * @returns {HitResult[]} An array of coneTest hit results (0 length if there were no hits). + * Results are ordered based on distance from starting position with closest first. */ coneTestAll(radius, height, axis, position, rotation) { return this._shapeTestAll(createShape('Cone', axis, radius, height), position, rotation); @@ -918,6 +922,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. * * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). + * Results are ordered based on distance from starting position with closest first. */ cylinderTestAll(radius, height, axis, position, rotation) { return this._shapeTestAll(createShape('Cylinder', axis, radius, height), position, rotation); @@ -933,6 +938,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3|Quat} [rotation] - The world space rotation for the sphere to have. * * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). + * Results are ordered based on distance from starting position with closest first. */ sphereTestAll(radius, position, rotation) { return this._shapeTestAll(new Ammo.btSphereShape(radius), position, rotation); @@ -950,6 +956,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to true. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). + * Results are ordered based on distance from starting position with closest first. * @ignore */ _shapeTestAll(shape, position, rotation = Vec3.ZERO, options) { @@ -1097,6 +1104,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to true. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). + * Results are ordered based on distance from starting position with closest first. * @private */ _shapeTest(shape, position, rotation = Vec3.ZERO, options = {}) { From 969ab5b4df0cfc731dfb3dba4aedb22e647166fe Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:31:31 +0100 Subject: [PATCH 46/54] Added filtering to shape testing and casting --- src/framework/components/rigid-body/system.js | 281 +++++++++++++----- 1 file changed, 212 insertions(+), 69 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 64a73bfe5b9..2479ee6c69e 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -707,114 +707,137 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} endPosition - The world space ending position for the shape to be. * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * @param {object} [options] - The additional options for the shape casting. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape cast. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape cast. * * @returns {HitResult|null} The first hit result (null if there were no hits). */ - shapeCastFirst(shape, startPosition, endPosition, startRotation, endRotation) { + shapeCastFirst(shape, startPosition, endPosition, startRotation, endRotation, options) { switch (shape.type) { case 'capsule': - return this.capsuleCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.capsuleCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation, options); case 'cone': - return this.coneCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.coneCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation, options); case 'cylinder': - return this.cylinderCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation); + return this.cylinderCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation, options); case 'sphere': - return this.sphereCastFirst(shape.radius, startPosition, endPosition); + return this.sphereCastFirst(shape.radius, startPosition, endPosition, options); default: - return this.boxCastFirst(shape.halfExtents, startPosition, endPosition, startRotation, endRotation); + return this.boxCastFirst(shape.halfExtents, startPosition, endPosition, startRotation, endRotation, options); } } /** - * Perform a shape casting on the world and return the first entity the box hits. + * Perform a box casting on the world and return the first entity the box hits. * It returns a {@link HitResult}. If no hits are detected, returned value will be null. * * @param {Vec3} halfExtents - The half-extents of the box in the x, y and z axes. - * @param {Vec3} startPosition - The world space starting position for the shape to be. - * @param {Vec3} endPosition - The world space ending position for the shape to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * @param {Vec3} startPosition - The world space starting position for the box to be. + * @param {Vec3} endPosition - The world space ending position for the box to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the box to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the box to have. + * @param {object} [options] - The additional options for the box casting. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the box cast. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the box cast. * * @returns {HitResult} The first hit result (null if there were no hits). */ - boxCastFirst(halfExtents, startPosition, endPosition, startRotation, endRotation) { + boxCastFirst(halfExtents, startPosition, endPosition, startRotation, endRotation, options = {}) { + options.destroyShape = true; + ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - const options = { destroyShape: true }; return this._shapeCastFirst(new Ammo.btBoxShape(ammoVec3), startPosition, endPosition, startRotation, endRotation, options); } /** - * Perform a shape casting on the world and return the first entity the capsule hits. + * Perform a capsule casting on the world and return the first entity the capsule hits. * It returns a {@link HitResult}. If no hits are detected, returned value will be null. * * @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} startPosition - The world space starting position for the shape to be. - * @param {Vec3} endPosition - The world space ending position for the shape to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * @param {Vec3} startPosition - The world space starting position for the capsule to be. + * @param {Vec3} endPosition - The world space ending position for the capsule to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the capsule to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the capsule to have. + * @param {object} [options] - The additional options for the capsule casting. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the capsule cast. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the capsule cast. * * @returns {HitResult} The first hit result (null if there were no hits). */ - capsuleCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { - const options = { destroyShape: true }; + capsuleCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation, options = {}) { + options.destroyShape = true; + return this._shapeCastFirst(createShape('Capsule', axis, radius, height), startPosition, endPosition, startRotation, endRotation, options); } /** - * Perform a shape casting on the world and return the first entity the cone hits. + * Perform a cone casting on the world and return the first entity the cone hits. * It returns a {@link HitResult}. If no hits are detected, returned value will be null. * * @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} startPosition - The world space starting position for the shape to be. - * @param {Vec3} endPosition - The world space ending position for the shape to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * @param {Vec3} startPosition - The world space starting position for the cone to be. + * @param {Vec3} endPosition - The world space ending position for the cone to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the cone to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the cone to have. + * @param {object} [options] - The additional options for the cone casting. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cone cast. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cone cast. * * @returns {HitResult} The first hit result (null if there were no hits). */ - coneCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { - const options = { destroyShape: true }; + coneCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation, options = {}) { + options.destroyShape = true; + return this._shapeCastFirst(createShape('Cone', axis, radius, height), startPosition, endPosition, startRotation, endRotation, options); } /** - * Perform a shape casting on the world and return the first entity the cylinder hits. + * Perform a cylinder casting on the world and return the first entity the cylinder hits. * It returns a {@link HitResult}. If no hits are detected, returned value will be null. * * @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} startPosition - The world space starting position for the shape to be. - * @param {Vec3} endPosition - The world space ending position for the shape to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * @param {Vec3} startPosition - The world space starting position for the cylinder to be. + * @param {Vec3} endPosition - The world space ending position for the cylinder to be. + * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the cylinder to have. + * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the cylinder to have. + * @param {object} [options] - The additional options for the cylinder casting. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cylinder cast. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cylinder cast. * * @returns {HitResult} The first hit result (null if there were no hits). */ - cylinderCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation) { - const options = { destroyShape: true }; + cylinderCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation, options = {}) { + options.destroyShape = true; + return this._shapeCastFirst(createShape('Cylinder', axis, radius, height), startPosition, endPosition, startRotation, endRotation, options); } /** - * Perform a shape casting on the world and return the first entity the sphere hits. + * Perform a sphere casting on the world and return the first entity the sphere hits. * It returns a {@link HitResult}. If no hits are detected, returned value will be null. * * @param {number} radius - The radius for the sphere. - * @param {Vec3} startPosition - The world space starting position for the shape to be. - * @param {Vec3} endPosition - The world space ending position for the shape to be. + * @param {Vec3} startPosition - The world space starting position for the sphere to be. + * @param {Vec3} endPosition - The world space ending position for the sphere to be. + * @param {object} [options] - The additional options for the sphere casting. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the sphere cast. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the sphere cast. * * @returns {HitResult} The first hit result (null if there were no hits). */ - sphereCastFirst(radius, startPosition, endPosition) { - const options = { destroyShape: true }; + sphereCastFirst(radius, startPosition, endPosition, options = {}) { + options.destroyShape = true; + return this._shapeCastFirst(new Ammo.btSphereShape(radius), startPosition, endPosition, options); } @@ -827,8 +850,10 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} endPosition - The world space ending position for the shape to be. * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. - * @param {object} [options] - Options to use when casting the shape. + * @param {object} [options] - The additional options for the shape casting. * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the cast. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape cast. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape cast. * * @returns {HitResult} The first hit result (null if there were no hits). * @private @@ -867,6 +892,14 @@ class RigidBodyComponentSystem extends ComponentSystem { // Callback for the contactTest results. const resultCallback = new Ammo.ClosestConvexResultCallback(); + if (typeof options.filterCollisionGroup === 'number') { + resultCallback.set_m_collisionFilterGroup(options.filterCollisionGroup); + } + + if (typeof options.filterCollisionMask === 'number') { + resultCallback.set_m_collisionFilterMask(options.filterCollisionMask); + } + // Check for contacts. this.app.systems.rigidbody.dynamicsWorld.convexSweepTest(shape, ammoTransform, ammoTransform2, resultCallback); @@ -913,8 +946,14 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. * @param {object} [options] - The additional options for the shape testing. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest + * @param {boolean} [options.sort] - Whether to sort shape test results based on distance with closest * first. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). * Results are ordered based on distance from starting position with closest first. @@ -943,8 +982,14 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the box to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the box to have. * @param {object} [options] - The additional options for the box testing. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest + * @param {boolean} [options.sort] - Whether to sort box test results based on distance with closest * first. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the box test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the box test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). * Results are ordered based on distance from starting position with closest first. @@ -968,8 +1013,14 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the capsule to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the capsule to have. * @param {object} [options] - The additional options for the capsule testing. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest + * @param {boolean} [options.sort] - Whether to sort capsule test results based on distance with closest * first. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the capsule test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the capsule test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult[]} An array of capsuleTest hit results (0 length if there were no hits). * Results are ordered based on distance from starting position with closest first. @@ -992,8 +1043,14 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the cone to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the cone to have. * @param {object} [options] - The additional options for the cone testing. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest + * @param {boolean} [options.sort] - Whether to sort cone test results based on distance with closest * first. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cone test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cone test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult[]} An array of coneTest hit results (0 length if there were no hits). * Results are ordered based on distance from starting position with closest first. @@ -1015,9 +1072,15 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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. * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. - * @param {object} [options] - The additional options for the shape testing. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest + * @param {object} [options] - The additional options for the cylinder testing. + * @param {boolean} [options.sort] - Whether to sort cylinder test results based on distance with closest * first. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cylinder test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cylinder test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). * Results are ordered based on distance from starting position with closest first. @@ -1037,8 +1100,14 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} position - The world space position for the sphere to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the sphere to have. * @param {object} [options] - The additional options for the sphere testing. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest + * @param {boolean} [options.sort] - Whether to sort sphere test results based on distance with closest * first. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the sphere test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the sphere test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). * Results are ordered based on distance from starting position with closest first. @@ -1057,7 +1126,15 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {object} shape - The Ammo.btCollisionShape to use for collision check. * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {object} [options] - Options to use when testing the shape. + * @param {object} [options] - The additional options for the shape testing. + * @param {boolean} [options.sort] - Whether to sort shape test results based on distance with closest + * first. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to false. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). @@ -1065,8 +1142,6 @@ class RigidBodyComponentSystem extends ComponentSystem { * @ignore */ _shapeTestAll(shape, position, rotation = Vec3.ZERO, options = {}) { - options.destroyShape = true; - return this._shapeTest(shape, position, rotation, options); } @@ -1084,21 +1159,28 @@ class RigidBodyComponentSystem extends ComponentSystem { * @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|Quat} [rotation] - The world space rotation for the shape to have. + * @param {object} [options] - The additional options for the shape testing. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult|null} A shapeTest hit result (null if there were no hits). */ - shapeTestFirst(shape, position, rotation) { + shapeTestFirst(shape, position, rotation, options = {}) { switch (shape.type) { case 'capsule': - return this.capsuleTestFirst(shape.radius, shape.height, shape.axis, position, rotation); + return this.capsuleTestFirst(shape.radius, shape.height, shape.axis, position, rotation, options); case 'cone': - return this.coneTestFirst(shape.radius, shape.height, shape.axis, position, rotation); + return this.coneTestFirst(shape.radius, shape.height, shape.axis, position, rotation, options); case 'cylinder': - return this.cylinderTestFirst(shape.radius, shape.height, shape.axis, position, rotation); + return this.cylinderTestFirst(shape.radius, shape.height, shape.axis, position, rotation, options); case 'sphere': - return this.sphereTestFirst(shape.radius, position, rotation); + return this.sphereTestFirst(shape.radius, position, rotation, options); default: - return this.boxTestFirst(shape.halfExtents, position, rotation); + return this.boxTestFirst(shape.halfExtents, position, rotation, options); } } @@ -1109,11 +1191,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} halfExtents - 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|Quat} [rotation] - The world space rotation for the box to have. + * @param {object} [options] - The additional options for the box testing. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the box test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the box test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult|null} A boxTest hit result (null if there were no hits). */ - boxTestFirst(halfExtents, position, rotation) { - const options = { destroyShape: true }; + boxTestFirst(halfExtents, position, rotation, options = {}) { + options.destroyShape = true; ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); return this._shapeTestFirst(new Ammo.btBoxShape(ammoVec3), position, rotation, options); @@ -1129,11 +1218,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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|Quat} [rotation] - The world space rotation for the capsule to have. + * @param {object} [options] - The additional options for the capsule testing. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the capsule test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the capsule test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult|null} A capsuleTest hit result (null if there were no hits). */ - capsuleTestFirst(radius, height, axis, position, rotation) { - const options = { destroyShape: true }; + capsuleTestFirst(radius, height, axis, position, rotation, options = {}) { + options.destroyShape = true; return this._shapeTestFirst(createShape('Capsule', axis, radius, height), position, rotation, options); } @@ -1148,11 +1244,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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|Quat} [rotation] - The world space rotation for the cone to have. + * @param {object} [options] - The additional options for the cone testing. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cone test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cone test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult|null} A coneTest hit result (null if there were no hits). */ - coneTestFirst(radius, height, axis, position, rotation) { - const options = { destroyShape: true }; + coneTestFirst(radius, height, axis, position, rotation, options) { + options.destroyShape = true; return this._shapeTestFirst(createShape('Cone', axis, radius, height), position, rotation, options); } @@ -1167,11 +1270,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * 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. * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. + * @param {object} [options] - The additional options for the cylinder testing. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cylinder test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cylinder test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult|null} A cylinderTest hit result (null if there were no hits). */ - cylinderTestFirst(radius, height, axis, position, rotation) { - const options = { destroyShape: true }; + cylinderTestFirst(radius, height, axis, position, rotation, options) { + options.destroyShape = true; return this._shapeTestFirst(createShape('Cylinder', axis, radius, height), position, rotation, options); } @@ -1183,11 +1293,18 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {number} radius - The radius of the sphere. * @param {Vec3} position - The world space position for the sphere to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the sphere to have. + * @param {object} [options] - The additional options for the sphere testing. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the sphere test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the sphere test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * * @returns {HitResult|null} A sphereTest hit result (null if there were no hits). */ - sphereTestFirst(radius, position, rotation) { - const options = { destroyShape: true }; + sphereTestFirst(radius, position, rotation, options) { + options.destroyShape = true; return this._shapeTestFirst(new Ammo.btSphereShape(radius), position, rotation, options); } @@ -1199,13 +1316,21 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {object} shape - The Ammo.btCollisionShape to use for collision check. * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {object} [options] - Options to use when testing the shape. + * @param {object} [options] - The additional options for the shape testing. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the tests. Defaults to false. * * @returns {HitResult|null} The shapeTest hit result (null if there were no hits). * @ignore */ _shapeTestFirst(shape, position, rotation = Vec3.ZERO, options) { + options.sort = true; + return this._shapeTest(shape, position, rotation, options)[0] || null; } @@ -1217,10 +1342,16 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {object} shape - The Ammo.btCollisionShape to use for collision check. * @param {Vec3} position - The world space position for the shape to be. * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {object} [options] - Options to use when testing the shape. - * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to false. + * @param {object} [options] - The additional options for the shape testing. * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest * first. Defaults to false. + * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. + * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. + * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to false. * * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). * Results are ordered based on distance from starting position with closest first. @@ -1264,6 +1395,10 @@ class RigidBodyComponentSystem extends ComponentSystem { // Make sure there is an existing entity. if (body1.entity) { + if (options.filterTags && !body1.entity.has(...options.filterTags) || options.filterCallback && !options.filterCallback(body1.entity)) { + return 0; + } + // Retrieve manifold point. const manifold = Ammo.wrapPointer(cp, Ammo.btManifoldPoint); @@ -1292,6 +1427,14 @@ class RigidBodyComponentSystem extends ComponentSystem { return 0; }; + if (typeof options.filterCollisionGroup === 'number') { + resultCallback.set_m_collisionFilterGroup(options.filterCollisionGroup); + } + + if (typeof options.filterCollisionMask === 'number') { + resultCallback.set_m_collisionFilterMask(options.filterCollisionMask); + } + // Check for contacts. this.dynamicsWorld.contactTest(shapeTestBody, resultCallback); From 4c707913806f9121104fa2c673b234c4960e4901 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Mon, 17 Apr 2023 20:01:40 +0200 Subject: [PATCH 47/54] Fixed tags filtering issue --- src/framework/components/rigid-body/system.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 2479ee6c69e..9beaf6c18da 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -661,7 +661,7 @@ class RigidBodyComponentSystem extends ComponentSystem { const body = Ammo.castObject(collisionObjs.at(i), Ammo.btRigidBody); if (body && body.entity) { - if (options.filterTags && !body.entity.has(...options.filterTags) || options.filterCallback && !options.filterCallback(body.entity)) { + if (options.filterTags && !body.entity.tags.has(...options.filterTags) || options.filterCallback && !options.filterCallback(body.entity)) { continue; } @@ -1395,7 +1395,7 @@ class RigidBodyComponentSystem extends ComponentSystem { // Make sure there is an existing entity. if (body1.entity) { - if (options.filterTags && !body1.entity.has(...options.filterTags) || options.filterCallback && !options.filterCallback(body1.entity)) { + if (options.filterTags && !body1.entity.tags.has(...options.filterTags) || options.filterCallback && !options.filterCallback(body1.entity)) { return 0; } From 2b2b64397cc348ec68d99706165e222b66a4d22c Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 29 Mar 2024 10:45:48 +0000 Subject: [PATCH 48/54] Fixed trailing space --- src/framework/components/rigid-body/system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 90231e05da0..7bdafeee478 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -52,7 +52,7 @@ class HitResult { * @type {number} */ hitFraction; - + /** * The distance at which the hit occurred from the starting point. * From c5e35d6f5399fc4132a1eddd09b99a6cf3944ba9 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 31 May 2024 02:37:21 +0100 Subject: [PATCH 49/54] Reduced public API functions --- src/deprecated/deprecated.js | 9 + src/framework/components/rigid-body/system.js | 938 ++++-------------- 2 files changed, 184 insertions(+), 763 deletions(-) diff --git a/src/deprecated/deprecated.js b/src/deprecated/deprecated.js index 16e358e6033..bce5271739f 100644 --- a/src/deprecated/deprecated.js +++ b/src/deprecated/deprecated.js @@ -1661,6 +1661,15 @@ RigidBodyComponentSystem.prototype.setGravity = function () { } }; +RigidBodyComponentSystem.prototype.raycastFirst = function (start, end, options) { + Debug.deprecated('pc.RigidBodyComponentSystem#raycastFirst is deprecated. Use pc.RigidBodyComponentSystem#raycast instead.'); + return this.raycast(start, end, options)[0] ?? null; +}; + +RigidBodyComponentSystem.prototype.raycastAll = function (start, end, options = {}) { + Debug.deprecated('pc.RigidBodyComponentSystem#raycastAll is deprecated. Use pc.RigidBodyComponentSystem#raycast instead.'); + return this.raycast(start, end, { ...options, findAll: true }); +}; export function basisSetDownloadConfig(glueUrl, wasmUrl, fallbackUrl) { Debug.deprecated('pc.basisSetDownloadConfig is deprecated. Use pc.basisInitialize instead.'); diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 7bdafeee478..8c8ba4ce0d9 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -544,74 +544,9 @@ class RigidBodyComponentSystem extends ComponentSystem { } /** - * Raycast the world and return the first entity the ray hits. Fire a ray into the world from - * start to end, if the ray hits an entity with a collision component, it returns a - * {@link HitResult}, otherwise returns null. - * - * @param {Vec3} start - The world space point where the ray starts. - * @param {Vec3} end - The world space point where the ray ends. - * @param {object} [options] - The additional options for the raycasting. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the raycast. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the raycast. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes one argument: the entity to evaluate. - * - * @returns {HitResult|null} The result of the raycasting or null if there was no hit. - */ - raycastFirst(start, end, options = {}) { - // Tags and custom callback can only be performed by looking at all results. - if (options.filterTags || options.filterCallback) { - options.sort = true; - return this.raycastAll(start, end, options)[0] || null; - } - - let result = null; - - ammoRayStart.setValue(start.x, start.y, start.z); - ammoRayEnd.setValue(end.x, end.y, end.z); - const rayCallback = new Ammo.ClosestRayResultCallback(ammoRayStart, ammoRayEnd); - - if (typeof options.filterCollisionGroup === 'number') { - rayCallback.set_m_collisionFilterGroup(options.filterCollisionGroup); - } - - if (typeof options.filterCollisionMask === 'number') { - rayCallback.set_m_collisionFilterMask(options.filterCollisionMask); - } - - this.dynamicsWorld.rayTest(ammoRayStart, ammoRayEnd, rayCallback); - if (rayCallback.hasHit()) { - const rayDistance = start.distance(end); - - const collisionObj = rayCallback.get_m_collisionObject(); - const body = Ammo.castObject(collisionObj, Ammo.btRigidBody); - - if (body) { - const point = rayCallback.get_m_hitPointWorld(); - const normal = rayCallback.get_m_hitNormalWorld(); - const hitFraction = rayCallback.get_m_closestHitFraction(); - - result = new HitResult( - body.entity, - new Vec3(point.x(), point.y(), point.z()), - new Vec3(normal.x(), normal.y(), normal.z()), - hitFraction, - rayDistance * hitFraction - ); - } - } - - Ammo.destroy(rayCallback); - - return result; - } - - /** - * Raycast the world and return all entities the ray hits. It returns an array of + * Raycast the world and return the entities the ray hits. It returns an array of * {@link HitResult}, one for each hit. If no hits are detected, the returned array will be - * of length 0. Results are sorted by distance with closest first. + * of length 0. * * @param {Vec3} start - The world space point where the ray starts. * @param {Vec3} end - The world space point where the ray ends. @@ -624,29 +559,33 @@ class RigidBodyComponentSystem extends ComponentSystem { * query but within an array. * @param {Function} [options.filterCallback] - Custom function to use to filter entities. * Must return true to proceed with result. Takes the entity to evaluate as argument. + * @param {boolean} [options.findAll] - Whether to return all results. When false will return only + * the closest result. Defaults to false. * * @returns {HitResult[]} An array of raycast hit results (0 length if there were no hits). * * @example * // Return all results of a raycast between 0, 2, 2 and 0, -2, -2 - * const hits = this.app.systems.rigidbody.raycastAll(new Vec3(0, 2, 2), new Vec3(0, -2, -2)); + * const hits = this.app.systems.rigidbody.raycast(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { findAll: true }); * @example * // Return all results of a raycast between 0, 2, 2 and 0, -2, -2 * // where hit entity is tagged with `bird` OR `mammal` - * const hits = this.app.systems.rigidbody.raycastAll(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { - * filterTags: [ "bird", "mammal" ] + * const hits = this.app.systems.rigidbody.raycast(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { + * filterTags: [ "bird", "mammal" ], + * findAll: true * }); * @example * // Return all results of a raycast between 0, 2, 2 and 0, -2, -2 * // where hit entity has a `camera` component - * const hits = this.app.systems.rigidbody.raycastAll(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { - * filterCallback: (entity) => entity && entity.camera + * const hits = this.app.systems.rigidbody.raycast(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { + * filterCallback: (entity) => entity && entity.camera, + * findAll: true * }); * @example - * // Return all results of a raycast between 0, 2, 2 and 0, -2, -2 + * // Return the closest result of a raycast between 0, 2, 2 and 0, -2, -2 * // where hit entity is tagged with (`carnivore` AND `mammal`) OR (`carnivore` AND `reptile`) * // and the entity has an `anim` component - * const hits = this.app.systems.rigidbody.raycastAll(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { + * const hit = this.app.systems.rigidbody.raycast(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { * filterTags: [ * [ "carnivore", "mammal" ], * [ "carnivore", "reptile" ] @@ -654,14 +593,15 @@ class RigidBodyComponentSystem extends ComponentSystem { * filterCallback: (entity) => entity && entity.anim * }); */ - raycastAll(start, end, options = {}) { - Debug.assert(Ammo.AllHitsRayResultCallback, 'pc.RigidBodyComponentSystem#raycastAll: Your version of ammo.js does not expose Ammo.AllHitsRayResultCallback. Update it to latest.'); - + raycast(start, end, options = {}) { + Debug.assert(Ammo.AllHitsRayResultCallback, 'pc.RigidBodyComponentSystem#raycast: Your version of ammo.js does not expose Ammo.AllHitsRayResultCallback. Update it to latest.'); const results = []; ammoRayStart.setValue(start.x, start.y, start.z); ammoRayEnd.setValue(end.x, end.y, end.z); - const rayCallback = new Ammo.AllHitsRayResultCallback(ammoRayStart, ammoRayEnd); + + const findAll = options.findAll || options.filterTags || options.filterCallback; + const rayCallback = findAll ? new Ammo.AllHitsRayResultCallback(ammoRayStart, ammoRayEnd) : new Ammo.ClosestRayResultCallback(ammoRayStart, ammoRayEnd); if (typeof options.filterCollisionGroup === 'number') { rayCallback.set_m_collisionFilterGroup(options.filterCollisionGroup); @@ -672,742 +612,211 @@ class RigidBodyComponentSystem extends ComponentSystem { } this.dynamicsWorld.rayTest(ammoRayStart, ammoRayEnd, rayCallback); + if (rayCallback.hasHit()) { const rayDistance = start.distance(end); - const collisionObjs = rayCallback.get_m_collisionObjects(); - const points = rayCallback.get_m_hitPointWorld(); - const normals = rayCallback.get_m_hitNormalWorld(); - const hitFractions = rayCallback.get_m_hitFractions(); + if (findAll) { + const collisionObjs = rayCallback.get_m_collisionObjects(); + const points = rayCallback.get_m_hitPointWorld(); + const normals = rayCallback.get_m_hitNormalWorld(); + const hitFractions = rayCallback.get_m_hitFractions(); + + const numHits = collisionObjs.size(); + for (let i = 0; i < numHits; i++) { + const body = Ammo.castObject(collisionObjs.at(i), Ammo.btRigidBody); - const numHits = collisionObjs.size(); - for (let i = 0; i < numHits; i++) { - const body = Ammo.castObject(collisionObjs.at(i), Ammo.btRigidBody); + if (body && body.entity) { + if ((options.filterTags && !body.entity.tags.has(...options.filterTags)) || (options.filterCallback && !options.filterCallback(body.entity))) { + continue; + } - if (body && body.entity) { - if (options.filterTags && !body.entity.tags.has(...options.filterTags) || options.filterCallback && !options.filterCallback(body.entity)) { - continue; + const point = points.at(i); + const normal = normals.at(i); + const hitFraction = hitFractions.at(i); + + results.push(new HitResult(body.entity, new Vec3(point.x(), point.y(), point.z()), new Vec3(normal.x(), normal.y(), normal.z()), hitFraction, rayDistance * hitFraction)); } + } - const point = points.at(i); - const normal = normals.at(i); - const hitFraction = hitFractions.at(i); + if (options.sort || !options.findAll) { + results.sort((a, b) => a.hitFraction - b.hitFraction); - const result = new HitResult( - body.entity, - new Vec3(point.x(), point.y(), point.z()), - new Vec3(normal.x(), normal.y(), normal.z()), - hitFraction, - rayDistance * hitFraction + if (!options.findAll && results.length > 1) { + results.length = 1; + } + } + } else { + const collisionObj = rayCallback.get_m_collisionObject(); + const body = Ammo.castObject(collisionObj, Ammo.btRigidBody); + + if (body) { + const point = rayCallback.get_m_hitPointWorld(); + const normal = rayCallback.get_m_hitNormalWorld(); + const hitFraction = rayCallback.get_m_closestHitFraction(); + + results.push( + new HitResult( + body.entity, + new Vec3(point.x(), point.y(), point.z()), + new Vec3(normal.x(), normal.y(), normal.z()), + hitFraction, + rayDistance * hitFraction + ) ); - - results.push(result); } } - - if (options.sort) { - results.sort((a, b) => a.hitFraction - b.hitFraction); - } } + Ammo.destroy(ammoRayStart); + Ammo.destroy(ammoRayEnd); Ammo.destroy(rayCallback); - return results; } /** - * Perform a shape casting on the world and return the first entity the shape hits. - * It returns a {@link HitResult}. If no hits are detected, returned value will be null. + * Perform a collision check on the world and return the entities the shape hits. + * It returns an array of {@link HitResult}. If no hits are + * detected, the returned array will be of length 0. * - * @param {object} shape - The shape to use for sweep test. - * @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 {object} shape - The shape to use for collision. Can be a btCollisionShape or shape config. + * @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.halfExtents] - The half-extents of the box in the x, y and z axes. * @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". + * "cone", "cylinder" or "sphere". * @param {number} [shape.radius] - The radius of the sphere, capsule, cylinder or cone. - * @param {Vec3} startPosition - The world space starting position for the shape to be. - * @param {Vec3} endPosition - The world space ending position for the shape to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. + * @param {Vec3} startPosition - The world space position for the shape to be at start. + * @param {Vec3|Quat} [startRotation] - The world space rotation for the shape to have at start. + * @param {Vec3} [endPosition] - The world space position for the shape to be at end. * @param {object} [options] - The additional options for the shape casting. + * @param {Vec3|Quat} [options.endRotation] - The world space rotation for the shape to have at end. + * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest + * first. Defaults to false. * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape cast. * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape cast. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} + * query but within an array. + * @param {Function} [options.filterCallback] - Custom function to use to filter entities. + * Must return true to proceed with result. Takes the entity to evaluate as argument. + * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the cast. + * Defaults to false, forced true when shape is not a btCollisionShape. + * @param {boolean} [options.findAll] - Whether to return all results. When false will return only + * the closest result. Defaults to false. * - * @returns {HitResult|null} The first hit result (null if there were no hits). + * @returns {HitResult[]} An array of shapeCast hit results (0 length if there were no hits). */ - shapeCastFirst(shape, startPosition, endPosition, startRotation, endRotation, options) { + shapeCast(shape, startPosition, startRotation, endPosition, options = {}) { + const results = []; + + let btShape; switch (shape.type) { + case 'box': + ammoVec3.setValue(shape.halfExtents?.x ?? 0.5, shape.halfExtents?.y ?? 0.5, shape.halfExtents?.z ?? 0.5); + btShape = new Ammo.btBoxShape(ammoVec3); + options.destroyShape = true; + break; case 'capsule': - return this.capsuleCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation, options); + btShape = createShape('Capsule', shape.axis, shape.radius ?? 0.5, shape.height ?? 1); + options.destroyShape = true; + break; case 'cone': - return this.coneCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation, options); + btShape = createShape('Cone', shape.axis, shape.radius ?? 0.5, shape.height ?? 1); + options.destroyShape = true; + break; case 'cylinder': - return this.cylinderCastFirst(shape.radius, shape.height, shape.axis, startPosition, endPosition, startRotation, endRotation, options); + btShape = createShape('Cylinder', shape.axis, shape.radius ?? 0.5, shape.height ?? 1); + options.destroyShape = true; + break; case 'sphere': - return this.sphereCastFirst(shape.radius, startPosition, endPosition, options); + btShape = new Ammo.btSphereShape(shape.radius ?? 0.5); + options.destroyShape = true; + break; default: - return this.boxCastFirst(shape.halfExtents, startPosition, endPosition, startRotation, endRotation, options); + btShape = shape; + break; } - } - - /** - * Perform a box casting on the world and return the first entity the box hits. - * It returns a {@link HitResult}. If no hits are detected, returned value will be null. - * - * @param {Vec3} halfExtents - The half-extents of the box in the x, y and z axes. - * @param {Vec3} startPosition - The world space starting position for the box to be. - * @param {Vec3} endPosition - The world space ending position for the box to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the box to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the box to have. - * @param {object} [options] - The additional options for the box casting. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the box cast. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the box cast. - * - * @returns {HitResult} The first hit result (null if there were no hits). - */ - boxCastFirst(halfExtents, startPosition, endPosition, startRotation, endRotation, options = {}) { - options.destroyShape = true; - - ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapeCastFirst(new Ammo.btBoxShape(ammoVec3), startPosition, endPosition, startRotation, endRotation, options); - } - - /** - * Perform a capsule casting on the world and return the first entity the capsule hits. - * It returns a {@link HitResult}. If no hits are detected, returned value will be null. - * - * @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} startPosition - The world space starting position for the capsule to be. - * @param {Vec3} endPosition - The world space ending position for the capsule to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the capsule to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the capsule to have. - * @param {object} [options] - The additional options for the capsule casting. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the capsule cast. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the capsule cast. - * - * @returns {HitResult} The first hit result (null if there were no hits). - */ - capsuleCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation, options = {}) { - options.destroyShape = true; - - return this._shapeCastFirst(createShape('Capsule', axis, radius, height), startPosition, endPosition, startRotation, endRotation, options); - } - - /** - * Perform a cone casting on the world and return the first entity the cone hits. - * It returns a {@link HitResult}. If no hits are detected, returned value will be null. - * - * @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} startPosition - The world space starting position for the cone to be. - * @param {Vec3} endPosition - The world space ending position for the cone to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the cone to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the cone to have. - * @param {object} [options] - The additional options for the cone casting. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cone cast. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cone cast. - * - * @returns {HitResult} The first hit result (null if there were no hits). - */ - coneCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation, options = {}) { - options.destroyShape = true; - - return this._shapeCastFirst(createShape('Cone', axis, radius, height), startPosition, endPosition, startRotation, endRotation, options); - } - - /** - * Perform a cylinder casting on the world and return the first entity the cylinder hits. - * It returns a {@link HitResult}. If no hits are detected, returned value will be null. - * - * @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} startPosition - The world space starting position for the cylinder to be. - * @param {Vec3} endPosition - The world space ending position for the cylinder to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the cylinder to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the cylinder to have. - * @param {object} [options] - The additional options for the cylinder casting. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cylinder cast. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cylinder cast. - * - * @returns {HitResult} The first hit result (null if there were no hits). - */ - cylinderCastFirst(radius, height, axis, startPosition, endPosition, startRotation, endRotation, options = {}) { - options.destroyShape = true; - - return this._shapeCastFirst(createShape('Cylinder', axis, radius, height), startPosition, endPosition, startRotation, endRotation, options); - } - - /** - * Perform a sphere casting on the world and return the first entity the sphere hits. - * It returns a {@link HitResult}. If no hits are detected, returned value will be null. - * - * @param {number} radius - The radius for the sphere. - * @param {Vec3} startPosition - The world space starting position for the sphere to be. - * @param {Vec3} endPosition - The world space ending position for the sphere to be. - * @param {object} [options] - The additional options for the sphere casting. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the sphere cast. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the sphere cast. - * - * @returns {HitResult} The first hit result (null if there were no hits). - */ - sphereCastFirst(radius, startPosition, endPosition, options = {}) { - options.destroyShape = true; - - return this._shapeCastFirst(new Ammo.btSphereShape(radius), startPosition, endPosition, options); - } - - /** - * Perform a shape casting on the world and return the first entity the shape hits. - * It returns a {@link HitResult}. If no hits are detected, returned value will be null. - * - * @param {object} shape - The Ammo.btCollisionShape to use for shape casting check. - * @param {Vec3} startPosition - The world space starting position for the shape to be. - * @param {Vec3} endPosition - The world space ending position for the shape to be. - * @param {Vec3|Quat} [startRotation] - The world space starting rotation for the shape to have. - * @param {Vec3|Quat} [endRotation] - The world space ending rotation for the shape to have. - * @param {object} [options] - The additional options for the shape casting. - * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the cast. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape cast. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape cast. - * - * @returns {HitResult} The first hit result (null if there were no hits). - * @private - */ - _shapeCastFirst(shape, startPosition, endPosition, startRotation = Vec3.ZERO, endRotation = undefined, options = {}) { - Debug.assert(Ammo.ClosestConvexResultCallback && Ammo.ClosestConvexResultCallback.get_m_hitCollisionObject, 'pc.RigidBodyComponentSystem#_shapeCastFirst: Your version of ammo.js does not expose Ammo.ClosestConvexResultCallback or Ammo.ClosestConvexResultCallback#get_m_hitCollisionObject. Update it to latest.'); - - let result = null; ammoVec3.setValue(startPosition.x, startPosition.y, startPosition.z); if (startRotation instanceof Quat) { ammoQuat.setValue(startRotation.x, startRotation.y, startRotation.z, startRotation.w); - } else { + } else if (startRotation instanceof Vec3) { ammoQuat.setEulerZYX(startRotation.z, startRotation.y, startRotation.x); + } else { + ammoQuat.setEulerZYX(0, 0, 0); } - // Assign position and rotation to origin transform. ammoTransform.setIdentity(); ammoTransform.setOrigin(ammoVec3); ammoTransform.setRotation(ammoQuat); - ammoVec3.setValue(endPosition.x, endPosition.y, endPosition.z); - if (endRotation) { + const endRotation = options.endRotation; + if ((endPosition && !endPosition.equals(startPosition)) || endRotation) { + Debug.assert(Ammo.ClosestConvexResultCallback && Ammo.ClosestConvexResultCallback.get_m_hitCollisionObject, 'pc.RigidBodyComponentSystem#shapeCast: Your version of ammo.js does not expose Ammo.ClosestConvexResultCallback or Ammo.ClosestConvexResultCallback#get_m_hitCollisionObject. Update it to latest.'); + + ammoVec3.setValue(endPosition.x, endPosition.y, endPosition.z); if (endRotation instanceof Quat) { ammoQuat.setValue(endRotation.x, endRotation.y, endRotation.z, endRotation.w); - } else { + } else if (endRotation instanceof Vec3) { ammoQuat.setEulerZYX(endRotation.z, endRotation.y, endRotation.x); + } else { + ammoQuat.setEulerZYX(0, 0, 0); } - } - - // Assign position and rotation to destination transform. - ammoTransform2.setIdentity(); - ammoTransform2.setOrigin(ammoVec3); - ammoTransform2.setRotation(ammoQuat); - - // Callback for the contactTest results. - const resultCallback = new Ammo.ClosestConvexResultCallback(); - if (typeof options.filterCollisionGroup === 'number') { - resultCallback.set_m_collisionFilterGroup(options.filterCollisionGroup); - } + ammoTransform2.setIdentity(); + ammoTransform2.setOrigin(ammoVec3); + ammoTransform2.setRotation(ammoQuat); - if (typeof options.filterCollisionMask === 'number') { - resultCallback.set_m_collisionFilterMask(options.filterCollisionMask); - } + const resultCallback = new Ammo.ClosestConvexResultCallback(); - // Check for contacts. - this.app.systems.rigidbody.dynamicsWorld.convexSweepTest(shape, ammoTransform, ammoTransform2, resultCallback); - - if (resultCallback.hasHit()) { - const body = Ammo.castObject(resultCallback.get_m_hitCollisionObject(), Ammo.btRigidBody); - if (body) { - const point = resultCallback.get_m_hitPointWorld(); - const normal = resultCallback.get_m_hitNormalWorld(); - - const pointVec = new Vec3(point.x(), point.y(), point.z()); - - result = new HitResult( - body.entity, - pointVec, - new Vec3(normal.x(), normal.y(), normal.z()), - resultCallback.get_m_closestHitFraction(), - startPosition.distance(pointVec) - ); + if (typeof options.filterCollisionGroup === 'number') { + resultCallback.set_m_collisionFilterGroup(options.filterCollisionGroup); } - } - - // Destroy unused variables for performance. - Ammo.destroy(resultCallback); - if (options.destroyShape) { - Ammo.destroy(shape); - } - - return result; - } - /** - * Perform a collision check on the world and return all entities the shape hits. - * It returns an array of {@link HitResult}. 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.halfExtents] - The half-extents of the box in the x, y and z axes. - * @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|Quat} [rotation] - The world space rotation for the shape to have. - * @param {object} [options] - The additional options for the shape testing. - * @param {boolean} [options.sort] - Whether to sort shape test results based on distance with closest - * first. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). - * Results are ordered based on distance from starting position with closest first. - */ - shapeTestAll(shape, position, rotation, options) { - switch (shape.type) { - case 'capsule': - return this.capsuleTestAll(shape.radius, shape.height, shape.axis, position, rotation, options); - case 'cone': - return this.coneTestAll(shape.radius, shape.height, shape.axis, position, rotation, options); - case 'cylinder': - return this.cylinderTestAll(shape.radius, shape.height, shape.axis, position, rotation, options); - case 'sphere': - return this.sphereTestAll(shape.radius, position, rotation, options); - default: - return this.boxTestAll(shape.halfExtents, position, rotation, options); - } - } - - /** - * Perform a collision check on the world and return all entities the box hits. - * It returns an array of {@link HitResult}. If no hits are - * detected, the returned array will be of length 0. - * - * @param {Vec3} halfExtents - 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|Quat} [rotation] - The world space rotation for the box to have. - * @param {object} [options] - The additional options for the box testing. - * @param {boolean} [options.sort] - Whether to sort box test results based on distance with closest - * first. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the box test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the box test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult[]} An array of boxTest hit results (0 length if there were no hits). - * Results are ordered based on distance from starting position with closest first. - */ - boxTestAll(halfExtents, position, rotation, options = {}) { - options.destroyShape = true; - - ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapeTestAll(new Ammo.btBoxShape(ammoVec3), position, rotation, options); - } - - /** - * Perform a collision check on the world and return all entities the capsule hits. - * It returns an array of {@link HitResult}. 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|Quat} [rotation] - The world space rotation for the capsule to have. - * @param {object} [options] - The additional options for the capsule testing. - * @param {boolean} [options.sort] - Whether to sort capsule test results based on distance with closest - * first. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the capsule test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the capsule test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult[]} An array of capsuleTest hit results (0 length if there were no hits). - * Results are ordered based on distance from starting position with closest first. - */ - capsuleTestAll(radius, height, axis, position, rotation, options = {}) { - options.destroyShape = true; - - return this._shapeTestAll(createShape('Capsule', axis, radius, height), position, rotation, options); - } - - /** - * Perform a collision check on the world and return all entities the cone hits. - * It returns an array of {@link HitResult}. 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|Quat} [rotation] - The world space rotation for the cone to have. - * @param {object} [options] - The additional options for the cone testing. - * @param {boolean} [options.sort] - Whether to sort cone test results based on distance with closest - * first. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cone test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cone test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult[]} An array of coneTest hit results (0 length if there were no hits). - * Results are ordered based on distance from starting position with closest first. - */ - coneTestAll(radius, height, axis, position, rotation, options = {}) { - options.destroyShape = true; - - return this._shapeTestAll(createShape('Cone', axis, radius, height), position, rotation, options); - } - - /** - * Perform a collision check on the world and return all entities the cylinder hits. - * It returns an array of {@link HitResult}. 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. - * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. - * @param {object} [options] - The additional options for the cylinder testing. - * @param {boolean} [options.sort] - Whether to sort cylinder test results based on distance with closest - * first. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cylinder test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cylinder test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult[]} An array of cylinderTest hit results (0 length if there were no hits). - * Results are ordered based on distance from starting position with closest first. - */ - cylinderTestAll(radius, height, axis, position, rotation, options = {}) { - options.destroyShape = true; - - return this._shapeTestAll(createShape('Cylinder', axis, radius, height), position, rotation, options); - } - - /** - * Perform a collision check on the world and return all entities the sphere hits. - * It returns an array of {@link HitResult}. 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|Quat} [rotation] - The world space rotation for the sphere to have. - * @param {object} [options] - The additional options for the sphere testing. - * @param {boolean} [options.sort] - Whether to sort sphere test results based on distance with closest - * first. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the sphere test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the sphere test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult[]} An array of sphereTest hit results (0 length if there were no hits). - * Results are ordered based on distance from starting position with closest first. - */ - sphereTestAll(radius, position, rotation, options = {}) { - options.destroyShape = true; - - return this._shapeTestAll(new Ammo.btSphereShape(radius), position, rotation, options); - } - - /** - * Perform a collision check on the world and return all entities the shape hits. - * It returns an array of {@link HitResult}. If no hits are - * detected, the returned array will be of length 0. - * - * @param {object} shape - The Ammo.btCollisionShape to use for collision check. - * @param {Vec3} position - The world space position for the shape to be. - * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {object} [options] - The additional options for the shape testing. - * @param {boolean} [options.sort] - Whether to sort shape test results based on distance with closest - * first. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to false. - * - * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). - * Results are ordered based on distance from starting position with closest first. - * @ignore - */ - _shapeTestAll(shape, position, rotation = Vec3.ZERO, options = {}) { - return this._shapeTest(shape, position, rotation, options); - } - - /** - * Perform a collision check on the world and return the first entity the shape hits. - * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. - * - * @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.halfExtents] - The half-extents of the box in the x, y and z axes. - * @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|Quat} [rotation] - The world space rotation for the shape to have. - * @param {object} [options] - The additional options for the shape testing. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult|null} A shapeTest hit result (null if there were no hits). - */ - shapeTestFirst(shape, position, rotation, options = {}) { - switch (shape.type) { - case 'capsule': - return this.capsuleTestFirst(shape.radius, shape.height, shape.axis, position, rotation, options); - case 'cone': - return this.coneTestFirst(shape.radius, shape.height, shape.axis, position, rotation, options); - case 'cylinder': - return this.cylinderTestFirst(shape.radius, shape.height, shape.axis, position, rotation, options); - case 'sphere': - return this.sphereTestFirst(shape.radius, position, rotation, options); - default: - return this.boxTestFirst(shape.halfExtents, position, rotation, options); - } - } - - /** - * Perform a collision check on the world and return the first entity the box hits. - * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. - * - * @param {Vec3} halfExtents - 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|Quat} [rotation] - The world space rotation for the box to have. - * @param {object} [options] - The additional options for the box testing. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the box test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the box test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult|null} A boxTest hit result (null if there were no hits). - */ - boxTestFirst(halfExtents, position, rotation, options = {}) { - options.destroyShape = true; - - ammoVec3.setValue(halfExtents.x, halfExtents.y, halfExtents.z); - return this._shapeTestFirst(new Ammo.btBoxShape(ammoVec3), position, rotation, options); - } - - /** - * Perform a collision check on the world and return the first entity the capsule hits. - * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. - * - * @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|Quat} [rotation] - The world space rotation for the capsule to have. - * @param {object} [options] - The additional options for the capsule testing. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the capsule test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the capsule test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult|null} A capsuleTest hit result (null if there were no hits). - */ - capsuleTestFirst(radius, height, axis, position, rotation, options = {}) { - options.destroyShape = true; - - return this._shapeTestFirst(createShape('Capsule', axis, radius, height), position, rotation, options); - } - - /** - * Perform a collision check on the world and return the first entity the cone hits. - * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. - * - * @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|Quat} [rotation] - The world space rotation for the cone to have. - * @param {object} [options] - The additional options for the cone testing. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cone test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cone test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult|null} A coneTest hit result (null if there were no hits). - */ - coneTestFirst(radius, height, axis, position, rotation, options) { - options.destroyShape = true; - - return this._shapeTestFirst(createShape('Cone', axis, radius, height), position, rotation, options); - } - - /** - * Perform a collision check on the world and return the first entity the cylinder hits. - * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. - * - * @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. - * @param {Vec3|Quat} [rotation] - The world space rotation for the cylinder to have. - * @param {object} [options] - The additional options for the cylinder testing. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the cylinder test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the cylinder test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult|null} A cylinderTest hit result (null if there were no hits). - */ - cylinderTestFirst(radius, height, axis, position, rotation, options) { - options.destroyShape = true; - - return this._shapeTestFirst(createShape('Cylinder', axis, radius, height), position, rotation, options); - } - - /** - * Perform a collision check on the world and return the first entity the sphere hits. - * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. - * - * @param {number} radius - The radius of the sphere. - * @param {Vec3} position - The world space position for the sphere to be. - * @param {Vec3|Quat} [rotation] - The world space rotation for the sphere to have. - * @param {object} [options] - The additional options for the sphere testing. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the sphere test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the sphere test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * - * @returns {HitResult|null} A sphereTest hit result (null if there were no hits). - */ - sphereTestFirst(radius, position, rotation, options) { - options.destroyShape = true; + if (typeof options.filterCollisionMask === 'number') { + resultCallback.set_m_collisionFilterMask(options.filterCollisionMask); + } - return this._shapeTestFirst(new Ammo.btSphereShape(radius), position, rotation, options); - } + this.app.systems.rigidbody.dynamicsWorld.convexSweepTest(btShape, ammoTransform, ammoTransform2, resultCallback); + if (resultCallback.hasHit()) { + const body = Ammo.castObject(resultCallback.get_m_hitCollisionObject(), Ammo.btRigidBody); - /** - * Perform a collision check on the world and return the first entity the shape hits. - * It returns a {@link HitResult}. If no hits are detected, the returned value will be null. - * - * @param {object} shape - The Ammo.btCollisionShape to use for collision check. - * @param {Vec3} position - The world space position for the shape to be. - * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {object} [options] - The additional options for the shape testing. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the tests. Defaults to false. - * - * @returns {HitResult|null} The shapeTest hit result (null if there were no hits). - * @ignore - */ - _shapeTestFirst(shape, position, rotation = Vec3.ZERO, options) { - options.sort = true; + if (body) { + const point = resultCallback.get_m_hitPointWorld(); + const normal = resultCallback.get_m_hitNormalWorld(); - return this._shapeTest(shape, position, rotation, options)[0] || null; - } - - /** - * Perform a collision check on the world and return all entities the shape hits. - * It returns an array of {@link HitResult}. If no hits are - * detected, the returned array will be of length 0. - * - * @param {object} shape - The Ammo.btCollisionShape to use for collision check. - * @param {Vec3} position - The world space position for the shape to be. - * @param {Vec3|Quat} [rotation] - The world space rotation for the shape to have. - * @param {object} [options] - The additional options for the shape testing. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest - * first. Defaults to false. - * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape test. - * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape test. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. - * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. - * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the test. Defaults to false. - * - * @returns {HitResult[]} An array of shapeTest hit results (0 length if there were no hits). - * Results are ordered based on distance from starting position with closest first. - * @private - */ - _shapeTest(shape, position, rotation = Vec3.ZERO, options = {}) { - Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#_shapeTest: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); - - const results = []; + const pointVec = new Vec3(point.x(), point.y(), point.z()); + results.push( + new HitResult( + body.entity, + pointVec, + new Vec3(normal.x(), normal.y(), normal.z()), + resultCallback.get_m_closestHitFraction(), + startPosition.distance(pointVec) + ) + ); + } + } - // Set proper position - ammoVec3.setValue(position.x, position.y, position.z); + // Destroy Ammo variables. + Ammo.destroy(resultCallback); + if (options.destroyShape) { + Ammo.destroy(btShape); + } - // Set proper rotation - if (rotation instanceof Quat) { - ammoQuat.setValue(rotation.x, rotation.y, rotation.z, rotation.w); - } else { - ammoQuat.setEulerZYX(rotation.z, rotation.y, rotation.x); + return results; } - // Assign position and rotation to transform. - ammoTransform.setIdentity(); - ammoTransform.setOrigin(ammoVec3); - ammoTransform.setRotation(ammoQuat); + Debug.assert(Ammo.ConcreteContactResultCallback, 'pc.RigidBodyComponentSystem#shapeCest: Your version of ammo.js does not expose Ammo.ConcreteContactResultCallback. Update it to latest.'); - // We only initialize the shapeTast body here so we don't have an extra body unless the user uses this function + // We only initialize the shapeTest body here so we don't have an extra body unless the user uses this function if (!shapeTestBody) { - shapeTestBody = this.createBody(0, shape, ammoTransform); + shapeTestBody = this.createBody(0, btShape, ammoTransform); } // Make sure the body has proper shape, transform and is active. - shapeTestBody.setCollisionShape(shape); + shapeTestBody.setCollisionShape(btShape); shapeTestBody.setWorldTransform(ammoTransform); shapeTestBody.forceActivationState(BODYSTATE_ACTIVE_TAG); @@ -1419,7 +828,7 @@ class RigidBodyComponentSystem extends ComponentSystem { // Make sure there is an existing entity. if (body1.entity) { - if (options.filterTags && !body1.entity.tags.has(...options.filterTags) || options.filterCallback && !options.filterCallback(body1.entity)) { + if ((options.filterTags && !body1.entity.tags.has(...options.filterTags)) || (options.filterCallback && !options.filterCallback(body1.entity))) { return 0; } @@ -1433,16 +842,18 @@ class RigidBodyComponentSystem extends ComponentSystem { const normal = manifold.get_m_normalWorldOnB(); const pointVec = new Vec3(point.x(), point.y(), point.z()); - const startDistance = position.distance(pointVec); + const startDistance = startPosition.distance(pointVec); // Push the result. - results.push(new HitResult( - body1.entity, - pointVec, - new Vec3(normal.x(), normal.y(), normal.z()), - startDistance / (startDistance - distance), // Minus distance as it's negative. - startDistance - )); + results.push( + new HitResult( + body1.entity, + pointVec, + new Vec3(normal.x(), normal.y(), normal.z()), + startDistance / (startDistance - distance), // Minus distance as it's negative. + startDistance + ) + ); return 1; } @@ -1459,20 +870,21 @@ class RigidBodyComponentSystem extends ComponentSystem { resultCallback.set_m_collisionFilterMask(options.filterCollisionMask); } - // Check for contacts. this.dynamicsWorld.contactTest(shapeTestBody, resultCallback); - // Remove body shape. + // Destroy Ammo variables. shapeTestBody.setCollisionShape(null); - - // Destroy unused variables for performance. Ammo.destroy(resultCallback); if (options.destroyShape) { - Ammo.destroy(shape); + Ammo.destroy(btShape); } - if (options.sort) { - return results.sort((a, b) => a.distance - b.distance); + if (options.sort || !options.findAll) { + results.sort((a, b) => a.distance - b.distance); + + if (!options.findAll && results.length > 1) { + results.length = 1; + } } return results; From ba29b58c29f9768005886763d2ce47e7f348b2c7 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 31 May 2024 02:51:28 +0100 Subject: [PATCH 50/54] Used Object assign instead of ... --- src/deprecated/deprecated.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deprecated/deprecated.js b/src/deprecated/deprecated.js index bce5271739f..8d2efb0f0f0 100644 --- a/src/deprecated/deprecated.js +++ b/src/deprecated/deprecated.js @@ -1668,7 +1668,7 @@ RigidBodyComponentSystem.prototype.raycastFirst = function (start, end, options) RigidBodyComponentSystem.prototype.raycastAll = function (start, end, options = {}) { Debug.deprecated('pc.RigidBodyComponentSystem#raycastAll is deprecated. Use pc.RigidBodyComponentSystem#raycast instead.'); - return this.raycast(start, end, { ...options, findAll: true }); + return this.raycast(start, end, Object.assign(options, { findAll: true })); }; export function basisSetDownloadConfig(glueUrl, wasmUrl, fallbackUrl) { From 8c67c9f0c38557d3e758570358fc5475006335b9 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 31 May 2024 03:14:48 +0100 Subject: [PATCH 51/54] Fixed documentation disclaimers --- src/framework/components/rigid-body/system.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 8c8ba4ce0d9..c505acbbe78 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -697,13 +697,16 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape cast. * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape cast. * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. + * query but within an array. Only available if shape is not having different ending transform from + * starting one. * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. + * Must return true to proceed with result. Takes the entity to evaluate as argument. Only available + * if shape is not having different ending transform from starting one. * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the cast. * Defaults to false, forced true when shape is not a btCollisionShape. * @param {boolean} [options.findAll] - Whether to return all results. When false will return only - * the closest result. Defaults to false. + * the closest result. Defaults to false. Only available if shape is not having different ending + * transform from starting one. * * @returns {HitResult[]} An array of shapeCast hit results (0 length if there were no hits). */ From b3f213de9eff4e483a1d896871a9246a72b2d11b Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 31 May 2024 09:11:01 +0100 Subject: [PATCH 52/54] Changed raycastFirst/raycastAll occurrences --- examples/src/examples/animation/locomotion.example.mjs | 2 +- examples/src/examples/physics/raycast.example.mjs | 8 ++++---- scripts/camera/first-person-camera.js | 2 +- src/deprecated/deprecated.js | 2 +- src/framework/components/camera/component.js | 5 +++-- src/framework/components/rigid-body/system.js | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/src/examples/animation/locomotion.example.mjs b/examples/src/examples/animation/locomotion.example.mjs index 75528b2708d..8ab9c6ddf31 100644 --- a/examples/src/examples/animation/locomotion.example.mjs +++ b/examples/src/examples/animation/locomotion.example.mjs @@ -324,7 +324,7 @@ assetListLoader.load(() => { const cameraEntity = app.root.findByName('Camera'); const near = cameraEntity.camera.screenToWorld(event.x, event.y, cameraEntity.camera.nearClip); const far = cameraEntity.camera.screenToWorld(event.x, event.y, cameraEntity.camera.farClip); - const result = app.systems.rigidbody.raycastFirst(far, near); + const result = app.systems.rigidbody.raycast(far, near)[0]; if (result) { targetPosition = new pc.Vec3(result.point.x, 0, result.point.z); characterEntity.anim.setInteger('speed', data.get('jogToggle') ? 2 : 1); diff --git a/examples/src/examples/physics/raycast.example.mjs b/examples/src/examples/physics/raycast.example.mjs index 7ad4f593c33..1e0b6292447 100644 --- a/examples/src/examples/physics/raycast.example.mjs +++ b/examples/src/examples/physics/raycast.example.mjs @@ -167,7 +167,7 @@ assetListLoader.load(() => { // Render the ray used in the raycast app.drawLine(start, end, white); - const result = app.systems.rigidbody.raycastFirst(start, end); + const result = app.systems.rigidbody.raycast(start, end)[0]; if (result) { result.entity.render.material = red; @@ -183,7 +183,7 @@ assetListLoader.load(() => { // Render the ray used in the raycast app.drawLine(start, end, white); - const results = app.systems.rigidbody.raycastAll(start, end); + const results = app.systems.rigidbody.raycast(start, end, { findAll: true }); results.forEach(function (result) { result.entity.render.material = red; @@ -217,8 +217,8 @@ assetListLoader.load(() => { app.root.addChild(text); }; - createText(assets.font, 'raycastFirst', 0.5, 3.75, 0, 0); - createText(assets.font, 'raycastAll', 0.5, -0.25, 0, 0); + createText(assets.font, 'raycast', 0.5, 3.75, 0, 0); + createText(assets.font, 'raycast (with findAll)', 0.5, -0.25, 0, 0); }); export { app }; diff --git a/scripts/camera/first-person-camera.js b/scripts/camera/first-person-camera.js index 7a472885b37..80c1aa92edf 100644 --- a/scripts/camera/first-person-camera.js +++ b/scripts/camera/first-person-camera.js @@ -715,7 +715,7 @@ const start = this.entity.getPosition(); const end = tmpV1.copy(start).add(Vec3.DOWN); end.y -= 0.1; - this._grounded = !!this._rigidbody.system.raycastFirst(start, end); + this._grounded = !!this._rigidbody.system.raycast(start, end)[0]; } /** diff --git a/src/deprecated/deprecated.js b/src/deprecated/deprecated.js index 8d2efb0f0f0..6915d69e21e 100644 --- a/src/deprecated/deprecated.js +++ b/src/deprecated/deprecated.js @@ -1667,7 +1667,7 @@ RigidBodyComponentSystem.prototype.raycastFirst = function (start, end, options) }; RigidBodyComponentSystem.prototype.raycastAll = function (start, end, options = {}) { - Debug.deprecated('pc.RigidBodyComponentSystem#raycastAll is deprecated. Use pc.RigidBodyComponentSystem#raycast instead.'); + Debug.deprecated('pc.RigidBodyComponentSystem#raycastAll is deprecated. Use pc.RigidBodyComponentSystem#raycast with findAll option instead.'); return this.raycast(start, end, Object.assign(options, { findAll: true })); }; diff --git a/src/framework/components/camera/component.js b/src/framework/components/camera/component.js index 6949ba5a669..1806e56a168 100644 --- a/src/framework/components/camera/component.js +++ b/src/framework/components/camera/component.js @@ -782,9 +782,10 @@ class CameraComponent extends Component { * const end = entity.camera.screenToWorld(clickX, clickY, entity.camera.farClip); * * // Use the ray coordinates to perform a raycast - * app.systems.rigidbody.raycastFirst(start, end, function (result) { + * const result = app.systems.rigidbody.raycast(start, end)[0]; + * if (result) { * console.log("Entity " + result.entity.name + " was selected"); - * }); + * } * @returns {import('../../../core/math/vec3.js').Vec3} The world space coordinate. */ screenToWorld(screenx, screeny, cameraz, worldCoord) { diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index c505acbbe78..2b243e8d2b0 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -591,7 +591,7 @@ class RigidBodyComponentSystem extends ComponentSystem { * [ "carnivore", "reptile" ] * ], * filterCallback: (entity) => entity && entity.anim - * }); + * })[0]; */ raycast(start, end, options = {}) { Debug.assert(Ammo.AllHitsRayResultCallback, 'pc.RigidBodyComponentSystem#raycast: Your version of ammo.js does not expose Ammo.AllHitsRayResultCallback. Update it to latest.'); From 7a642a8f471b44f689be6e09fd3f16bdd42eb0d0 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 31 May 2024 09:26:30 +0100 Subject: [PATCH 53/54] Removed extra Ammo.destroy --- src/framework/components/rigid-body/system.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 2b243e8d2b0..651a27cbb88 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -668,8 +668,6 @@ class RigidBodyComponentSystem extends ComponentSystem { } } - Ammo.destroy(ammoRayStart); - Ammo.destroy(ammoRayEnd); Ammo.destroy(rayCallback); return results; } From 972c1f772fae1c03b66170b6a06c86fc2d71f4d6 Mon Sep 17 00:00:00 2001 From: Lucas Petitjean <28061629+MushAsterion@users.noreply.github.com> Date: Fri, 31 May 2024 09:52:25 +0100 Subject: [PATCH 54/54] Fixed documentation and line width --- src/framework/components/rigid-body/system.js | 88 ++++++++++++------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/src/framework/components/rigid-body/system.js b/src/framework/components/rigid-body/system.js index 651a27cbb88..09a52c186ff 100644 --- a/src/framework/components/rigid-body/system.js +++ b/src/framework/components/rigid-body/system.js @@ -289,7 +289,8 @@ const _schema = ['enabled']; * Creates a new shape. * * @param {string} name - Name of the shape. Must start with capital letter. - * @param {number} [axis] - The local space axis with which the shape's length is aligned. 0 for X, 1 for Y and 2 for Z. Defaults to 1 (Y-axis). + * @param {number} [axis] - The local space axis with which the shape's length is aligned. + * 0 for X, 1 for Y and 2 for Z. Defaults to 1 (Y-axis). * @param {...any} [args] - Arguments to pass to creation. * @returns {object} Created Ammo.btCollisionShape. * @ignore @@ -551,38 +552,42 @@ class RigidBodyComponentSystem extends ComponentSystem { * @param {Vec3} start - The world space point where the ray starts. * @param {Vec3} end - The world space point where the ray ends. * @param {object} [options] - The additional options for the raycasting. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest - * first. Defaults to false. + * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with + * closest first. Defaults to false. * @param {number} [options.filterCollisionGroup] - Collision group to apply to the raycast. * @param {number} [options.filterCollisionMask] - Collision mask to apply to the raycast. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a + * {@link Tags#has} query but within an array. * @param {Function} [options.filterCallback] - Custom function to use to filter entities. * Must return true to proceed with result. Takes the entity to evaluate as argument. - * @param {boolean} [options.findAll] - Whether to return all results. When false will return only - * the closest result. Defaults to false. + * @param {boolean} [options.findAll] - Whether to return all results. When false will return + * only the closest result. Defaults to false. * * @returns {HitResult[]} An array of raycast hit results (0 length if there were no hits). * * @example - * // Return all results of a raycast between 0, 2, 2 and 0, -2, -2 - * const hits = this.app.systems.rigidbody.raycast(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { findAll: true }); + * // Return all results of a raycast between (0, 2, 2) and (0, -2, -2) + * const hits = this.app.systems.rigidbody.raycast( + * new Vec3(0, 2, 2), + * new Vec3(0, -2, -2), + * { findAll: true } + * ); * @example - * // Return all results of a raycast between 0, 2, 2 and 0, -2, -2 + * // Return all results of a raycast between (0, 2, 2) and (0, -2, -2) * // where hit entity is tagged with `bird` OR `mammal` * const hits = this.app.systems.rigidbody.raycast(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { * filterTags: [ "bird", "mammal" ], * findAll: true * }); * @example - * // Return all results of a raycast between 0, 2, 2 and 0, -2, -2 + * // Return all results of a raycast between (0, 2, 2) and (0, -2, -2) * // where hit entity has a `camera` component * const hits = this.app.systems.rigidbody.raycast(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { * filterCallback: (entity) => entity && entity.camera, * findAll: true * }); * @example - * // Return the closest result of a raycast between 0, 2, 2 and 0, -2, -2 + * // Return the closest result of a raycast between (0, 2, 2) and (0, -2, -2) * // where hit entity is tagged with (`carnivore` AND `mammal`) OR (`carnivore` AND `reptile`) * // and the entity has an `anim` component * const hit = this.app.systems.rigidbody.raycast(new Vec3(0, 2, 2), new Vec3(0, -2, -2), { @@ -677,34 +682,38 @@ class RigidBodyComponentSystem extends ComponentSystem { * It returns an array of {@link HitResult}. If no hits are * detected, the returned array will be of length 0. * - * @param {object} shape - The shape to use for collision. Can be a btCollisionShape or shape config. + * @param {object} shape - The shape to use for collision. Can be a btCollisionShape + * or shape config. * @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.halfExtents] - The half-extents of the box in the x, y and z axes. - * @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". + * @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". * @param {number} [shape.radius] - The radius of the sphere, capsule, cylinder or cone. * @param {Vec3} startPosition - The world space position for the shape to be at start. - * @param {Vec3|Quat} [startRotation] - The world space rotation for the shape to have at start. + * @param {Vec3|Quat} [startRotation] - The world space rotation for the shape to have + * at start. * @param {Vec3} [endPosition] - The world space position for the shape to be at end. * @param {object} [options] - The additional options for the shape casting. - * @param {Vec3|Quat} [options.endRotation] - The world space rotation for the shape to have at end. - * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with closest - * first. Defaults to false. + * @param {Vec3|Quat} [options.endRotation] - The world space rotation for the shape to have + * at end. + * @param {boolean} [options.sort] - Whether to sort raycast results based on distance with + * closest first. Defaults to false. * @param {number} [options.filterCollisionGroup] - Collision group to apply to the shape cast. * @param {number} [options.filterCollisionMask] - Collision mask to apply to the shape cast. - * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a {@link Tags#has} - * query but within an array. Only available if shape is not having different ending transform from - * starting one. + * @param {any[]} [options.filterTags] - Tags filters. Defined the same way as a + * {@link Tags#has} query but within an array. Only available if shape is not having different + * ending transform from starting one. * @param {Function} [options.filterCallback] - Custom function to use to filter entities. - * Must return true to proceed with result. Takes the entity to evaluate as argument. Only available - * if shape is not having different ending transform from starting one. + * Must return true to proceed with result. Takes the entity to evaluate as argument. Only + * available if shape is not having different ending transform from starting one. * @param {boolean} [options.destroyShape] - Whether to destroy the shape after the cast. * Defaults to false, forced true when shape is not a btCollisionShape. - * @param {boolean} [options.findAll] - Whether to return all results. When false will return only - * the closest result. Defaults to false. Only available if shape is not having different ending - * transform from starting one. + * @param {boolean} [options.findAll] - Whether to return all results. When false will return + * only the closest result. Defaults to false. Only available if shape is not having different + * ending transform from starting one. * * @returns {HitResult[]} An array of shapeCast hit results (0 length if there were no hits). */ @@ -714,7 +723,12 @@ class RigidBodyComponentSystem extends ComponentSystem { let btShape; switch (shape.type) { case 'box': - ammoVec3.setValue(shape.halfExtents?.x ?? 0.5, shape.halfExtents?.y ?? 0.5, shape.halfExtents?.z ?? 0.5); + ammoVec3.setValue( + shape.halfExtents?.x ?? 0.5, + shape.halfExtents?.y ?? 0.5, + shape.halfExtents?.z ?? 0.5 + ); + btShape = new Ammo.btBoxShape(ammoVec3); options.destroyShape = true; break; @@ -779,7 +793,13 @@ class RigidBodyComponentSystem extends ComponentSystem { resultCallback.set_m_collisionFilterMask(options.filterCollisionMask); } - this.app.systems.rigidbody.dynamicsWorld.convexSweepTest(btShape, ammoTransform, ammoTransform2, resultCallback); + this.app.systems.rigidbody.dynamicsWorld.convexSweepTest( + btShape, + ammoTransform, + ammoTransform2, + resultCallback + ); + if (resultCallback.hasHit()) { const body = Ammo.castObject(resultCallback.get_m_hitCollisionObject(), Ammo.btRigidBody); @@ -825,7 +845,10 @@ class RigidBodyComponentSystem extends ComponentSystem { 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); + const body1 = Ammo.castObject( + Ammo.wrapPointer(colObj1Wrap, Ammo.btCollisionObjectWrapper).getCollisionObject(), + Ammo.btRigidBody + ); // Make sure there is an existing entity. if (body1.entity) { @@ -851,7 +874,8 @@ class RigidBodyComponentSystem extends ComponentSystem { body1.entity, pointVec, new Vec3(normal.x(), normal.y(), normal.z()), - startDistance / (startDistance - distance), // Minus distance as it's negative. + // Minus distance as it's negative. + startDistance / (startDistance - distance), startDistance ) );