UNPKG

@dimforge/rapier3d

Version:

3-dimensional physics engine in Rust - official JS bindings.

1,522 lines (1,385 loc) 58.9 kB
import {RawColliderSet} from "../raw"; import {Rotation, RotationOps, Vector, VectorOps} from "../math"; import { CoefficientCombineRule, RigidBody, RigidBodyHandle, RigidBodySet, } from "../dynamics"; import {ActiveHooks, ActiveEvents} from "../pipeline"; import {InteractionGroups} from "./interaction_groups"; import { Shape, Cuboid, Ball, ShapeType, Capsule, Voxels, TriMesh, Polyline, Heightfield, Segment, Triangle, RoundTriangle, RoundCuboid, HalfSpace, TriMeshFlags, // #if DIM3 Cylinder, RoundCylinder, Cone, RoundCone, ConvexPolyhedron, RoundConvexPolyhedron, HeightFieldFlags, // #endif } from "./shape"; import {Ray, RayIntersection} from "./ray"; import {PointProjection} from "./point"; import {ColliderShapeCastHit, ShapeCastHit} from "./toi"; import {ShapeContact} from "./contact"; import {ColliderSet} from "./collider_set"; /** * Flags affecting whether collision-detection happens between two colliders * depending on the type of rigid-bodies they are attached to. */ export enum ActiveCollisionTypes { /** * Enable collision-detection between a collider attached to a dynamic body * and another collider attached to a dynamic body. */ DYNAMIC_DYNAMIC = 0b0000_0000_0000_0001, /** * Enable collision-detection between a collider attached to a dynamic body * and another collider attached to a kinematic body. */ DYNAMIC_KINEMATIC = 0b0000_0000_0000_1100, /** * Enable collision-detection between a collider attached to a dynamic body * and another collider attached to a fixed body (or not attached to any body). */ DYNAMIC_FIXED = 0b0000_0000_0000_0010, /** * Enable collision-detection between a collider attached to a kinematic body * and another collider attached to a kinematic body. */ KINEMATIC_KINEMATIC = 0b1100_1100_0000_0000, /** * Enable collision-detection between a collider attached to a kinematic body * and another collider attached to a fixed body (or not attached to any body). */ KINEMATIC_FIXED = 0b0010_0010_0000_0000, /** * Enable collision-detection between a collider attached to a fixed body (or * not attached to any body) and another collider attached to a fixed body (or * not attached to any body). */ FIXED_FIXED = 0b0000_0000_0010_0000, /** * The default active collision types, enabling collisions between a dynamic body * and another body of any type, but not enabling collisions between two non-dynamic bodies. */ DEFAULT = DYNAMIC_KINEMATIC | DYNAMIC_DYNAMIC | DYNAMIC_FIXED, /** * Enable collisions between any kind of rigid-bodies (including between two non-dynamic bodies). */ ALL = DYNAMIC_KINEMATIC | DYNAMIC_DYNAMIC | DYNAMIC_FIXED | KINEMATIC_KINEMATIC | KINEMATIC_FIXED | KINEMATIC_KINEMATIC, } /** * The integer identifier of a collider added to a `ColliderSet`. */ export type ColliderHandle = number; /** * A geometric entity that can be attached to a body so it can be affected * by contacts and proximity queries. */ export class Collider { private colliderSet: ColliderSet; // The Collider won't need to free this. readonly handle: ColliderHandle; private _shape: Shape; // TODO: deprecate/remove this since it isn’t a reliable way of getting the latest shape properties. private _parent: RigidBody | null; constructor( colliderSet: ColliderSet, handle: ColliderHandle, parent: RigidBody | null, shape?: Shape, ) { this.colliderSet = colliderSet; this.handle = handle; this._parent = parent; this._shape = shape; } /** @internal */ public finalizeDeserialization(bodies: RigidBodySet) { if (this.handle != null) { this._parent = bodies.get( this.colliderSet.raw.coParent(this.handle), ); } } private ensureShapeIsCached() { if (!this._shape) this._shape = Shape.fromRaw(this.colliderSet.raw, this.handle); } /** * The shape of this collider. */ public get shape(): Shape { this.ensureShapeIsCached(); return this._shape; } /** * Set the internal cached JS shape to null. * * This can be useful if you want to free some memory (assuming you are not * holding any other references to the shape object), or in order to force * the recalculation of the JS shape (the next time the `shape` getter is * accessed) from the WASM source of truth. */ public clearShapeCache() { this._shape = null; } /** * Checks if this collider is still valid (i.e. that it has * not been deleted from the collider set yet). */ public isValid(): boolean { return this.colliderSet.raw.contains(this.handle); } /** * The world-space translation of this collider. */ public translation(): Vector { return VectorOps.fromRaw( this.colliderSet.raw.coTranslation(this.handle), ); } /** * The translation of this collider relative to its parent rigid-body. * * Returns `null` if the collider doesn’t have a parent rigid-body. */ public translationWrtParent(): Vector | null { return VectorOps.fromRaw( this.colliderSet.raw.coTranslationWrtParent(this.handle), ); } /** * The world-space orientation of this collider. */ public rotation(): Rotation { return RotationOps.fromRaw( this.colliderSet.raw.coRotation(this.handle), ); } /** * The orientation of this collider relative to its parent rigid-body. * * Returns `null` if the collider doesn’t have a parent rigid-body. */ public rotationWrtParent(): Rotation | null { return RotationOps.fromRaw( this.colliderSet.raw.coRotationWrtParent(this.handle), ); } /** * Is this collider a sensor? */ public isSensor(): boolean { return this.colliderSet.raw.coIsSensor(this.handle); } /** * Sets whether this collider is a sensor. * @param isSensor - If `true`, the collider will be a sensor. */ public setSensor(isSensor: boolean) { this.colliderSet.raw.coSetSensor(this.handle, isSensor); } /** * Sets the new shape of the collider. * @param shape - The collider’s new shape. */ public setShape(shape: Shape) { let rawShape = shape.intoRaw(); this.colliderSet.raw.coSetShape(this.handle, rawShape); rawShape.free(); this._shape = shape; } /** * Sets whether this collider is enabled or not. * * @param enabled - Set to `false` to disable this collider (its parent rigid-body won’t be disabled automatically by this). */ public setEnabled(enabled: boolean) { this.colliderSet.raw.coSetEnabled(this.handle, enabled); } /** * Is this collider enabled? */ public isEnabled(): boolean { return this.colliderSet.raw.coIsEnabled(this.handle); } /** * Sets the restitution coefficient of the collider to be created. * * @param restitution - The restitution coefficient in `[0, 1]`. A value of 0 (the default) means no bouncing behavior * while 1 means perfect bouncing (though energy may still be lost due to numerical errors of the * constraints solver). */ public setRestitution(restitution: number) { this.colliderSet.raw.coSetRestitution(this.handle, restitution); } /** * Sets the friction coefficient of the collider to be created. * * @param friction - The friction coefficient. Must be greater or equal to 0. This is generally smaller than 1. The * higher the coefficient, the stronger friction forces will be for contacts with the collider * being built. */ public setFriction(friction: number) { this.colliderSet.raw.coSetFriction(this.handle, friction); } /** * Gets the rule used to combine the friction coefficients of two colliders * colliders involved in a contact. */ public frictionCombineRule(): CoefficientCombineRule { return this.colliderSet.raw.coFrictionCombineRule(this.handle); } /** * Sets the rule used to combine the friction coefficients of two colliders * colliders involved in a contact. * * @param rule − The combine rule to apply. */ public setFrictionCombineRule(rule: CoefficientCombineRule) { this.colliderSet.raw.coSetFrictionCombineRule(this.handle, rule); } /** * Gets the rule used to combine the restitution coefficients of two colliders * colliders involved in a contact. */ public restitutionCombineRule(): CoefficientCombineRule { return this.colliderSet.raw.coRestitutionCombineRule(this.handle); } /** * Sets the rule used to combine the restitution coefficients of two colliders * colliders involved in a contact. * * @param rule − The combine rule to apply. */ public setRestitutionCombineRule(rule: CoefficientCombineRule) { this.colliderSet.raw.coSetRestitutionCombineRule(this.handle, rule); } /** * Sets the collision groups used by this collider. * * Two colliders will interact iff. their collision groups are compatible. * See the documentation of `InteractionGroups` for details on teh used bit pattern. * * @param groups - The collision groups used for the collider being built. */ public setCollisionGroups(groups: InteractionGroups) { this.colliderSet.raw.coSetCollisionGroups(this.handle, groups); } /** * Sets the solver groups used by this collider. * * Forces between two colliders in contact will be computed iff their solver * groups are compatible. * See the documentation of `InteractionGroups` for details on the used bit pattern. * * @param groups - The solver groups used for the collider being built. */ public setSolverGroups(groups: InteractionGroups) { this.colliderSet.raw.coSetSolverGroups(this.handle, groups); } /** * Sets the contact skin for this collider. * * See the documentation of `ColliderDesc.setContactSkin` for additional details. */ public contactSkin(): number { return this.colliderSet.raw.coContactSkin(this.handle); } /** * Sets the contact skin for this collider. * * See the documentation of `ColliderDesc.setContactSkin` for additional details. * * @param thickness - The contact skin thickness. */ public setContactSkin(thickness: number) { return this.colliderSet.raw.coSetContactSkin(this.handle, thickness); } /** * Get the physics hooks active for this collider. */ public activeHooks(): ActiveHooks { return this.colliderSet.raw.coActiveHooks(this.handle); } /** * Set the physics hooks active for this collider. * * Use this to enable custom filtering rules for contact/intersecstion pairs involving this collider. * * @param activeHooks - The hooks active for contact/intersection pairs involving this collider. */ public setActiveHooks(activeHooks: ActiveHooks) { this.colliderSet.raw.coSetActiveHooks(this.handle, activeHooks); } /** * The events active for this collider. */ public activeEvents(): ActiveEvents { return this.colliderSet.raw.coActiveEvents(this.handle); } /** * Set the events active for this collider. * * Use this to enable contact and/or intersection event reporting for this collider. * * @param activeEvents - The events active for contact/intersection pairs involving this collider. */ public setActiveEvents(activeEvents: ActiveEvents) { this.colliderSet.raw.coSetActiveEvents(this.handle, activeEvents); } /** * Gets the collision types active for this collider. */ public activeCollisionTypes(): ActiveCollisionTypes { return this.colliderSet.raw.coActiveCollisionTypes(this.handle); } /** * Sets the total force magnitude beyond which a contact force event can be emitted. * * @param threshold - The new force threshold. */ public setContactForceEventThreshold(threshold: number) { return this.colliderSet.raw.coSetContactForceEventThreshold( this.handle, threshold, ); } /** * The total force magnitude beyond which a contact force event can be emitted. */ public contactForceEventThreshold(): number { return this.colliderSet.raw.coContactForceEventThreshold(this.handle); } /** * Set the collision types active for this collider. * * @param activeCollisionTypes - The hooks active for contact/intersection pairs involving this collider. */ public setActiveCollisionTypes(activeCollisionTypes: ActiveCollisionTypes) { this.colliderSet.raw.coSetActiveCollisionTypes( this.handle, activeCollisionTypes, ); } /** * Sets the uniform density of this collider. * * This will override any previous mass-properties set by `this.setDensity`, * `this.setMass`, `this.setMassProperties`, `ColliderDesc.density`, * `ColliderDesc.mass`, or `ColliderDesc.massProperties` for this collider. * * The mass and angular inertia of this collider will be computed automatically based on its * shape. */ public setDensity(density: number) { this.colliderSet.raw.coSetDensity(this.handle, density); } /** * Sets the mass of this collider. * * This will override any previous mass-properties set by `this.setDensity`, * `this.setMass`, `this.setMassProperties`, `ColliderDesc.density`, * `ColliderDesc.mass`, or `ColliderDesc.massProperties` for this collider. * * The angular inertia of this collider will be computed automatically based on its shape * and this mass value. */ public setMass(mass: number) { this.colliderSet.raw.coSetMass(this.handle, mass); } // #if DIM3 /** * Sets the mass of this collider. * * This will override any previous mass-properties set by `this.setDensity`, * `this.setMass`, `this.setMassProperties`, `ColliderDesc.density`, * `ColliderDesc.mass`, or `ColliderDesc.massProperties` for this collider. */ public setMassProperties( mass: number, centerOfMass: Vector, principalAngularInertia: Vector, angularInertiaLocalFrame: Rotation, ) { let rawCom = VectorOps.intoRaw(centerOfMass); let rawPrincipalInertia = VectorOps.intoRaw(principalAngularInertia); let rawInertiaFrame = RotationOps.intoRaw(angularInertiaLocalFrame); this.colliderSet.raw.coSetMassProperties( this.handle, mass, rawCom, rawPrincipalInertia, rawInertiaFrame, ); rawCom.free(); rawPrincipalInertia.free(); rawInertiaFrame.free(); } // #endif /** * Sets the translation of this collider. * * @param tra - The world-space position of the collider. */ public setTranslation(tra: Vector) { // #if DIM3 this.colliderSet.raw.coSetTranslation(this.handle, tra.x, tra.y, tra.z); // #endif } /** * Sets the translation of this collider relative to its parent rigid-body. * * Does nothing if this collider isn't attached to a rigid-body. * * @param tra - The new translation of the collider relative to its parent. */ public setTranslationWrtParent(tra: Vector) { // #if DIM3 this.colliderSet.raw.coSetTranslationWrtParent( this.handle, tra.x, tra.y, tra.z, ); // #endif } // #if DIM3 /** * Sets the rotation quaternion of this collider. * * This does nothing if a zero quaternion is provided. * * @param rotation - The rotation to set. */ public setRotation(rot: Rotation) { this.colliderSet.raw.coSetRotation( this.handle, rot.x, rot.y, rot.z, rot.w, ); } /** * Sets the rotation quaternion of this collider relative to its parent rigid-body. * * This does nothing if a zero quaternion is provided or if this collider isn't * attached to a rigid-body. * * @param rotation - The rotation to set. */ public setRotationWrtParent(rot: Rotation) { this.colliderSet.raw.coSetRotationWrtParent( this.handle, rot.x, rot.y, rot.z, rot.w, ); } // #endif /** * The type of the shape of this collider. */ public shapeType(): ShapeType { return this.colliderSet.raw.coShapeType( this.handle, ) as number as ShapeType; } /** * The half-extents of this collider if it is a cuboid shape. */ public halfExtents(): Vector { return VectorOps.fromRaw( this.colliderSet.raw.coHalfExtents(this.handle), ); } /** * Sets the half-extents of this collider if it is a cuboid shape. * * @param newHalfExtents - desired half extents. */ public setHalfExtents(newHalfExtents: Vector) { const rawPoint = VectorOps.intoRaw(newHalfExtents); this.colliderSet.raw.coSetHalfExtents(this.handle, rawPoint); } /** * The radius of this collider if it is a ball, cylinder, capsule, or cone shape. */ public radius(): number { return this.colliderSet.raw.coRadius(this.handle); } /** * Sets the radius of this collider if it is a ball, cylinder, capsule, or cone shape. * * @param newRadius - desired radius. */ public setRadius(newRadius: number): void { this.colliderSet.raw.coSetRadius(this.handle, newRadius); } /** * The radius of the round edges of this collider if it is a round cylinder. */ public roundRadius(): number { return this.colliderSet.raw.coRoundRadius(this.handle); } /** * Sets the radius of the round edges of this collider if it has round edges. * * @param newBorderRadius - desired round edge radius. */ public setRoundRadius(newBorderRadius: number) { this.colliderSet.raw.coSetRoundRadius(this.handle, newBorderRadius); } /** * The half height of this collider if it is a cylinder, capsule, or cone shape. */ public halfHeight(): number { return this.colliderSet.raw.coHalfHeight(this.handle); } /** * Sets the half height of this collider if it is a cylinder, capsule, or cone shape. * * @param newHalfheight - desired half height. */ public setHalfHeight(newHalfheight: number) { this.colliderSet.raw.coSetHalfHeight(this.handle, newHalfheight); } /** * If this collider has a Voxels shape, this will mark the voxel at the * given grid coordinates as filled or empty (depending on the `filled` * argument). * * Each input value is assumed to be an integer. * * The operation is O(1), unless the provided coordinates are out of the * bounds of the currently allocated internal grid in which case the grid * will be grown automatically. */ public setVoxel( ix: number, iy: number, // #if DIM3 iz: number, // #endif filled: boolean, ) { this.colliderSet.raw.coSetVoxel( this.handle, ix, iy, // #if DIM3 iz, // #endif filled, ); // We modified the shape, invalidate it to keep our cache // up-to-date the next time the user requests the shape data. // PERF: this isn’t ideal for performances as this adds a // hidden, non-constant, cost. this._shape = null; } /** * If this and `voxels2` are voxel colliders, and a voxel from `this` was * modified with `setVoxel`, this will ensure that a * moving object transitioning across the boundaries of these colliders * won’t suffer from the "internal edges" artifact. * * The indices `ix, iy, iz` indicate the integer coordinates of the voxel in * the local coordinate frame of `this`. * * If the voxels in `voxels2` live in a different coordinate space from `this`, * then the `shift_*` argument indicate the distance, in voxel units, between * the origin of `this` to the origin of `voxels2`. * * This method is intended to be called between `this` and all the other * voxels colliders with a domain intersecting `this` or sharing a domain * boundary. This is an incremental maintenance of the effect of * `combineVoxelStates`. */ public propagateVoxelChange( voxels2: Collider, ix: number, iy: number, // #if DIM3 iz: number, // #endif shift_x: number, shift_y: number, // #if DIM3 shift_z: number, // #endif ) { this.colliderSet.raw.coPropagateVoxelChange( this.handle, voxels2.handle, ix, iy, // #if DIM3 iz, // #endif shift_x, shift_y, // #if DIM3 shift_z, // #endif ); // We modified the shape, invalidate it to keep our cache // up-to-date the next time the user requests the shape data. // PERF: this isn’t ideal for performances as this adds a // hidden, non-constant, cost. this._shape = null; } /** * If this and `voxels2` are voxel colliders, this will ensure that a * moving object transitioning across the boundaries of these colliders * won’t suffer from the "internal edges" artifact. * * If the voxels in `voxels2` live in a different coordinate space from `this`, * then the `shift_*` argument indicate the distance, in voxel units, between * the origin of `this` to the origin of `voxels2`. * * This method is intended to be called once between all pairs of voxels * colliders with intersecting domains or shared boundaries. * * If either voxels collider is then modified with `setVoxel`, the * `propagateVoxelChange` method must be called to maintain the coupling * between the voxels shapes after the modification. */ public combineVoxelStates( voxels2: Collider, shift_x: number, shift_y: number, // #if DIM3 shift_z: number, // #endif ) { this.colliderSet.raw.coCombineVoxelStates( this.handle, voxels2.handle, shift_x, shift_y, // #if DIM3 shift_z, // #endif ); // We modified the shape, invalidate it to keep our cache // up-to-date the next time the user requests the shape data. // PERF: this isn’t ideal for performances as this adds a // hidden, non-constant, cost. this._shape = null; } /** * If this collider has a triangle mesh, polyline, convex polygon, or convex polyhedron shape, * this returns the vertex buffer of said shape. */ public vertices(): Float32Array { return this.colliderSet.raw.coVertices(this.handle); } /** * If this collider has a triangle mesh, polyline, or convex polyhedron shape, * this returns the index buffer of said shape. */ public indices(): Uint32Array | undefined { return this.colliderSet.raw.coIndices(this.handle); } /** * If this collider has a heightfield shape, this returns the heights buffer of * the heightfield. * In 3D, the returned height matrix is provided in column-major order. */ public heightfieldHeights(): Float32Array { return this.colliderSet.raw.coHeightfieldHeights(this.handle); } /** * If this collider has a heightfield shape, this returns the scale * applied to it. */ public heightfieldScale(): Vector { let scale = this.colliderSet.raw.coHeightfieldScale(this.handle); return VectorOps.fromRaw(scale); } // #if DIM3 /** * If this collider has a heightfield shape, this returns the number of * rows of its height matrix. */ public heightfieldNRows(): number { return this.colliderSet.raw.coHeightfieldNRows(this.handle); } /** * If this collider has a heightfield shape, this returns the number of * columns of its height matrix. */ public heightfieldNCols(): number { return this.colliderSet.raw.coHeightfieldNCols(this.handle); } // #endif /** * The rigid-body this collider is attached to. */ public parent(): RigidBody | null { return this._parent; } /** * The friction coefficient of this collider. */ public friction(): number { return this.colliderSet.raw.coFriction(this.handle); } /** * The restitution coefficient of this collider. */ public restitution(): number { return this.colliderSet.raw.coRestitution(this.handle); } /** * The density of this collider. */ public density(): number { return this.colliderSet.raw.coDensity(this.handle); } /** * The mass of this collider. */ public mass(): number { return this.colliderSet.raw.coMass(this.handle); } /** * The volume of this collider. */ public volume(): number { return this.colliderSet.raw.coVolume(this.handle); } /** * The collision groups of this collider. */ public collisionGroups(): InteractionGroups { return this.colliderSet.raw.coCollisionGroups(this.handle); } /** * The solver groups of this collider. */ public solverGroups(): InteractionGroups { return this.colliderSet.raw.coSolverGroups(this.handle); } /** * Tests if this collider contains a point. * * @param point - The point to test. */ public containsPoint(point: Vector): boolean { let rawPoint = VectorOps.intoRaw(point); let result = this.colliderSet.raw.coContainsPoint( this.handle, rawPoint, ); rawPoint.free(); return result; } /** * Find the projection of a point on this collider. * * @param point - The point to project. * @param solid - If this is set to `true` then the collider shapes are considered to * be plain (if the point is located inside of a plain shape, its projection is the point * itself). If it is set to `false` the collider shapes are considered to be hollow * (if the point is located inside of an hollow shape, it is projected on the shape's * boundary). */ public projectPoint(point: Vector, solid: boolean): PointProjection | null { let rawPoint = VectorOps.intoRaw(point); let result = PointProjection.fromRaw( this.colliderSet.raw.coProjectPoint(this.handle, rawPoint, solid), ); rawPoint.free(); return result; } /** * Tests if this collider intersects the given ray. * * @param ray - The ray to cast. * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively * limits the length of the ray to `ray.dir.norm() * maxToi`. */ public intersectsRay(ray: Ray, maxToi: number): boolean { let rawOrig = VectorOps.intoRaw(ray.origin); let rawDir = VectorOps.intoRaw(ray.dir); let result = this.colliderSet.raw.coIntersectsRay( this.handle, rawOrig, rawDir, maxToi, ); rawOrig.free(); rawDir.free(); return result; } /* * Computes the smallest time between this and the given shape under translational movement are separated by a distance smaller or equal to distance. * * @param collider1Vel - The constant velocity of the current shape to cast (i.e. the cast direction). * @param shape2 - The shape to cast against. * @param shape2Pos - The position of the second shape. * @param shape2Rot - The rotation of the second shape. * @param shape2Vel - The constant velocity of the second shape. * @param targetDistance − If the shape moves closer to this distance from a collider, a hit * will be returned. * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively * limits the distance traveled by the shape to `collider1Vel.norm() * maxToi`. * @param stopAtPenetration - If set to `false`, the linear shape-cast won’t immediately stop if * the shape is penetrating another shape at its starting point **and** its trajectory is such * that it’s on a path to exit that penetration state. */ public castShape( collider1Vel: Vector, shape2: Shape, shape2Pos: Vector, shape2Rot: Rotation, shape2Vel: Vector, targetDistance: number, maxToi: number, stopAtPenetration: boolean, ): ShapeCastHit | null { let rawCollider1Vel = VectorOps.intoRaw(collider1Vel); let rawShape2Pos = VectorOps.intoRaw(shape2Pos); let rawShape2Rot = RotationOps.intoRaw(shape2Rot); let rawShape2Vel = VectorOps.intoRaw(shape2Vel); let rawShape2 = shape2.intoRaw(); let result = ShapeCastHit.fromRaw( this.colliderSet, this.colliderSet.raw.coCastShape( this.handle, rawCollider1Vel, rawShape2, rawShape2Pos, rawShape2Rot, rawShape2Vel, targetDistance, maxToi, stopAtPenetration, ), ); rawCollider1Vel.free(); rawShape2Pos.free(); rawShape2Rot.free(); rawShape2Vel.free(); rawShape2.free(); return result; } /* * Computes the smallest time between this and the given collider under translational movement are separated by a distance smaller or equal to distance. * * @param collider1Vel - The constant velocity of the current collider to cast (i.e. the cast direction). * @param collider2 - The collider to cast against. * @param collider2Vel - The constant velocity of the second collider. * @param targetDistance − If the shape moves closer to this distance from a collider, a hit * will be returned. * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively * limits the distance traveled by the shape to `shapeVel.norm() * maxToi`. * @param stopAtPenetration - If set to `false`, the linear shape-cast won’t immediately stop if * the shape is penetrating another shape at its starting point **and** its trajectory is such * that it’s on a path to exit that penetration state. */ public castCollider( collider1Vel: Vector, collider2: Collider, collider2Vel: Vector, targetDistance: number, maxToi: number, stopAtPenetration: boolean, ): ColliderShapeCastHit | null { let rawCollider1Vel = VectorOps.intoRaw(collider1Vel); let rawCollider2Vel = VectorOps.intoRaw(collider2Vel); let result = ColliderShapeCastHit.fromRaw( this.colliderSet, this.colliderSet.raw.coCastCollider( this.handle, rawCollider1Vel, collider2.handle, rawCollider2Vel, targetDistance, maxToi, stopAtPenetration, ), ); rawCollider1Vel.free(); rawCollider2Vel.free(); return result; } public intersectsShape( shape2: Shape, shapePos2: Vector, shapeRot2: Rotation, ): boolean { let rawPos2 = VectorOps.intoRaw(shapePos2); let rawRot2 = RotationOps.intoRaw(shapeRot2); let rawShape2 = shape2.intoRaw(); let result = this.colliderSet.raw.coIntersectsShape( this.handle, rawShape2, rawPos2, rawRot2, ); rawPos2.free(); rawRot2.free(); rawShape2.free(); return result; } /** * Computes one pair of contact points between the shape owned by this collider and the given shape. * * @param shape2 - The second shape. * @param shape2Pos - The initial position of the second shape. * @param shape2Rot - The rotation of the second shape. * @param prediction - The prediction value, if the shapes are separated by a distance greater than this value, test will fail. * @returns `null` if the shapes are separated by a distance greater than prediction, otherwise contact details. The result is given in world-space. */ contactShape( shape2: Shape, shape2Pos: Vector, shape2Rot: Rotation, prediction: number, ): ShapeContact | null { let rawPos2 = VectorOps.intoRaw(shape2Pos); let rawRot2 = RotationOps.intoRaw(shape2Rot); let rawShape2 = shape2.intoRaw(); let result = ShapeContact.fromRaw( this.colliderSet.raw.coContactShape( this.handle, rawShape2, rawPos2, rawRot2, prediction, ), ); rawPos2.free(); rawRot2.free(); rawShape2.free(); return result; } /** * Computes one pair of contact points between the collider and the given collider. * * @param collider2 - The second collider. * @param prediction - The prediction value, if the shapes are separated by a distance greater than this value, test will fail. * @returns `null` if the shapes are separated by a distance greater than prediction, otherwise contact details. The result is given in world-space. */ contactCollider( collider2: Collider, prediction: number, ): ShapeContact | null { let result = ShapeContact.fromRaw( this.colliderSet.raw.coContactCollider( this.handle, collider2.handle, prediction, ), ); return result; } /** * Find the closest intersection between a ray and this collider. * * This also computes the normal at the hit point. * @param ray - The ray to cast. * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively * limits the length of the ray to `ray.dir.norm() * maxToi`. * @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its * origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain, * whereas `false` implies that all shapes are hollow for this ray-cast. * @returns The time-of-impact between this collider and the ray, or `-1` if there is no intersection. */ public castRay(ray: Ray, maxToi: number, solid: boolean): number { let rawOrig = VectorOps.intoRaw(ray.origin); let rawDir = VectorOps.intoRaw(ray.dir); let result = this.colliderSet.raw.coCastRay( this.handle, rawOrig, rawDir, maxToi, solid, ); rawOrig.free(); rawDir.free(); return result; } /** * Find the closest intersection between a ray and this collider. * * This also computes the normal at the hit point. * @param ray - The ray to cast. * @param maxToi - The maximum time-of-impact that can be reported by this cast. This effectively * limits the length of the ray to `ray.dir.norm() * maxToi`. * @param solid - If `false` then the ray will attempt to hit the boundary of a shape, even if its * origin already lies inside of a shape. In other terms, `true` implies that all shapes are plain, * whereas `false` implies that all shapes are hollow for this ray-cast. */ public castRayAndGetNormal( ray: Ray, maxToi: number, solid: boolean, ): RayIntersection | null { let rawOrig = VectorOps.intoRaw(ray.origin); let rawDir = VectorOps.intoRaw(ray.dir); let result = RayIntersection.fromRaw( this.colliderSet.raw.coCastRayAndGetNormal( this.handle, rawOrig, rawDir, maxToi, solid, ), ); rawOrig.free(); rawDir.free(); return result; } } export enum MassPropsMode { Density, Mass, MassProps, } export class ColliderDesc { enabled: boolean; shape: Shape; massPropsMode: MassPropsMode; mass: number; centerOfMass: Vector; // #if DIM3 principalAngularInertia: Vector; angularInertiaLocalFrame: Rotation; // #endif density: number; friction: number; restitution: number; rotation: Rotation; translation: Vector; isSensor: boolean; collisionGroups: InteractionGroups; solverGroups: InteractionGroups; frictionCombineRule: CoefficientCombineRule; restitutionCombineRule: CoefficientCombineRule; activeEvents: ActiveEvents; activeHooks: ActiveHooks; activeCollisionTypes: ActiveCollisionTypes; contactForceEventThreshold: number; contactSkin: number; /** * Initializes a collider descriptor from the collision shape. * * @param shape - The shape of the collider being built. */ constructor(shape: Shape) { this.enabled = true; this.shape = shape; this.massPropsMode = MassPropsMode.Density; this.density = 1.0; this.friction = 0.5; this.restitution = 0.0; this.rotation = RotationOps.identity(); this.translation = VectorOps.zeros(); this.isSensor = false; this.collisionGroups = 0xffff_ffff; this.solverGroups = 0xffff_ffff; this.frictionCombineRule = CoefficientCombineRule.Average; this.restitutionCombineRule = CoefficientCombineRule.Average; this.activeCollisionTypes = ActiveCollisionTypes.DEFAULT; this.activeEvents = ActiveEvents.NONE; this.activeHooks = ActiveHooks.NONE; this.mass = 0.0; this.centerOfMass = VectorOps.zeros(); this.contactForceEventThreshold = 0.0; this.contactSkin = 0.0; // #if DIM3 this.principalAngularInertia = VectorOps.zeros(); this.angularInertiaLocalFrame = RotationOps.identity(); // #endif } /** * Create a new collider descriptor with a ball shape. * * @param radius - The radius of the ball. */ public static ball(radius: number): ColliderDesc { const shape = new Ball(radius); return new ColliderDesc(shape); } /** * Create a new collider descriptor with a capsule shape. * * @param halfHeight - The half-height of the capsule, along the `y` axis. * @param radius - The radius of the capsule basis. */ public static capsule(halfHeight: number, radius: number): ColliderDesc { const shape = new Capsule(halfHeight, radius); return new ColliderDesc(shape); } /** * Creates a new segment shape. * * @param a - The first point of the segment. * @param b - The second point of the segment. */ public static segment(a: Vector, b: Vector): ColliderDesc { const shape = new Segment(a, b); return new ColliderDesc(shape); } /** * Creates a new triangle shape. * * @param a - The first point of the triangle. * @param b - The second point of the triangle. * @param c - The third point of the triangle. */ public static triangle(a: Vector, b: Vector, c: Vector): ColliderDesc { const shape = new Triangle(a, b, c); return new ColliderDesc(shape); } /** * Creates a new triangle shape with round corners. * * @param a - The first point of the triangle. * @param b - The second point of the triangle. * @param c - The third point of the triangle. * @param borderRadius - The radius of the borders of this triangle. In 3D, * this is also equal to half the thickness of the triangle. */ public static roundTriangle( a: Vector, b: Vector, c: Vector, borderRadius: number, ): ColliderDesc { const shape = new RoundTriangle(a, b, c, borderRadius); return new ColliderDesc(shape); } /** * Creates a new collider descriptor with a polyline shape. * * @param vertices - The coordinates of the polyline's vertices. * @param indices - The indices of the polyline's segments. If this is `undefined` or `null`, * the vertices are assumed to describe a line strip. */ public static polyline( vertices: Float32Array, indices?: Uint32Array | null, ): ColliderDesc { const shape = new Polyline(vertices, indices); return new ColliderDesc(shape); } /** * Creates a new collider descriptor with a shape made of voxels. * * @param data - Defines the set of voxels. If this is a `Int32Array` then * each voxel is defined from its (signed) grid coordinates, * with 3 (resp 2) contiguous integers per voxel in 3D (resp 2D). * If this is a `Float32Array`, each voxel will be such that * they contain at least one point from this array (where each * point is defined from 3 (resp 2) contiguous numbers per point * in 3D (resp 2D). * @param voxelSize - The size of each voxel. */ public static voxels( voxels: Float32Array | Int32Array, voxelSize: Vector, ): ColliderDesc { const shape = new Voxels(voxels, voxelSize); return new ColliderDesc(shape); } /** * Creates a new collider descriptor with a triangle mesh shape. * * @param vertices - The coordinates of the triangle mesh's vertices. * @param indices - The indices of the triangle mesh's triangles. */ public static trimesh( vertices: Float32Array, indices: Uint32Array, flags?: TriMeshFlags, ): ColliderDesc { const shape = new TriMesh(vertices, indices, flags); return new ColliderDesc(shape); } // #if DIM3 /** * Creates a new collider descriptor with a cuboid shape. * * @param hx - The half-width of the rectangle along its local `x` axis. * @param hy - The half-width of the rectangle along its local `y` axis. * @param hz - The half-width of the rectangle along its local `z` axis. */ public static cuboid(hx: number, hy: number, hz: number): ColliderDesc { const shape = new Cuboid(hx, hy, hz); return new ColliderDesc(shape); } /** * Creates a new collider descriptor with a rectangular shape with round borders. * * @param hx - The half-width of the rectangle along its local `x` axis. * @param hy - The half-width of the rectangle along its local `y` axis. * @param hz - The half-width of the rectangle along its local `z` axis. * @param borderRadius - The radius of the cuboid's borders. */ public static roundCuboid( hx: number, hy: number, hz: number, borderRadius: number, ): ColliderDesc { const shape = new RoundCuboid(hx, hy, hz, borderRadius); return new ColliderDesc(shape); } /** * Creates a new collider descriptor with a heightfield shape. * * @param nrows − The number of rows in the heights matrix. * @param ncols - The number of columns in the heights matrix. * @param heights - The heights of the heightfield along its local `y` axis, * provided as a matrix stored in column-major order. * @param scale - The scale factor applied to the heightfield. */ public static heightfield( nrows: number, ncols: number, heights: Float32Array, scale: Vector, flags?: HeightFieldFlags, ): ColliderDesc { const shape = new Heightfield(nrows, ncols, heights, scale, flags); return new ColliderDesc(shape); } /** * Create a new collider descriptor with a cylinder shape. * * @param halfHeight - The half-height of the cylinder, along the `y` axis. * @param radius - The radius of the cylinder basis. */ public static cylinder(halfHeight: number, radius: number): ColliderDesc { const shape = new Cylinder(halfHeight, radius); return new ColliderDesc(shape); } /** * Create a new collider descriptor with a cylinder shape with rounded corners. * * @param halfHeight - The half-height of the cylinder, along the `y` axis. * @param radius - The radius of the cylinder basis. * @param borderRadius - The radius of the cylinder's rounded edges and vertices. */ public static roundCylinder( halfHeight: number, radius: number, borderRadius: number, ): ColliderDesc { const shape = new RoundCylinder(halfHeight, radius, borderRadius); return new ColliderDesc(shape); } /** * Create a new collider descriptor with a cone shape. * * @param halfHeight - The half-height of the cone, along the `y` axis. * @param radius - The radius of the cone basis. */ public static cone(halfHeight: number, radius: number): ColliderDesc { const shape = new Cone(halfHeight, radius); return new ColliderDesc(shape); } /** * Create a new collider descriptor with a cone shape with rounded corners. * * @param halfHeight - The half-height of the cone, along the `y` axis. * @param radius - The radius of the cone basis. * @param borderRadius - The radius of the cone's rounded edges and vertices. */ public static roundCone( halfHeight: number, radius: number, borderRadius: number, ): ColliderDesc { const shape = new RoundCone(halfHeight, radius, borderRadius); return new ColliderDesc(shape); } /** * Computes the convex-hull of the given points and use the resulting * convex polyhedron as the shape for this new collider descriptor. * * @param points - The point that will be used to compute the convex-hull. */ public static convexHull(points: Float32Array): ColliderDesc | null { const shape = new ConvexPolyhedron(points, null); return new ColliderDesc(shape); } /** * Creates a new collider descriptor that uses the given set of points assumed * to form a convex polyline (no convex-hull computation will be done). * * @param vertices - The vertices of the convex polyline. */ public static convexMesh( vertices: Float32Array, indices?: Uint32Array | null, ): ColliderDesc | null { const shape = new ConvexPolyhedron(vertices, indices); return new ColliderDesc(shape); } /** * Computes the convex-hull of the given points and use the resulting * convex polyhedron as the shape for this new collider descriptor. A * border is added to that convex polyhedron to give it round corners. * * @param points - The point that will be used to compute the convex-hull. * @param borderRadius - The radius of the round border added to the convex polyhedron. */ public static roundConvexHull( points: Float32Array, borderRadius: number, ): ColliderDesc | null { const shape = new RoundConvexPolyhedron(points, null, borderRadius); return new ColliderDesc(shape); } /** * Creates a new collider descriptor that uses the given set of points assumed * to form a round convex polyline (no convex-hull computation will be done). * * @param vertices - The vertices of the convex polyline. * @param borderRadius - The radius of the round border added to the convex polyline. */ public static roundConvexMesh( vertices: Float32Array, indices: Uint32Array | null, borderRadius: number, ): ColliderDesc | null { const shape = new RoundConvexPolyhedron( vertices, indices, borderRadius, ); return new ColliderDesc(shape); } // #endif // #if DIM3 /** * Sets the position of the collider to be created re