diff --git a/src.ts/dynamics/rigid_body.ts b/src.ts/dynamics/rigid_body.ts index 8f52dbe5..603b0bd1 100644 --- a/src.ts/dynamics/rigid_body.ts +++ b/src.ts/dynamics/rigid_body.ts @@ -1,5 +1,11 @@ import {RawRigidBodySet, RawRigidBodyType} from "../raw"; -import {Rotation, RotationOps, Vector, VectorOps} from "../math"; +import { + Rotation, + RotationOps, + Vector, + VectorOps, + scratchBuffer +} from "../math"; // #if DIM3 import {SdpMatrix3, SdpMatrix3Ops} from "../math"; // #endif @@ -294,31 +300,68 @@ export class RigidBody { /** * The world-space translation of this rigid-body. */ - public translation(): Vector { - let res = this.rawSet.rbTranslation(this.handle); + public translationOriginal(): Vector { + let res = this.rawSet.rbTranslationOriginal(this.handle); return VectorOps.fromRaw(res); } /** * The world-space orientation of this rigid-body. */ - public rotation(): Rotation { - let res = this.rawSet.rbRotation(this.handle); + public rotationOriginal(): Rotation { + let res = this.rawSet.rbRotationOriginal(this.handle); return RotationOps.fromRaw(res); } + /** + * The world-space translation of this rigid-body. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public translation(target?: Vector): Vector { + this.rawSet.rbTranslation(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); + } + + // #if DIM2 + /** + * The world-space orientation of this rigid-body. + */ + public rotation(): number { + return this.rawSet.rbRotation(this.handle); + } + // #endif + + // #if DIM3 + /** + * The world-space orientation of this rigid-body. + * + * @param {Rotation?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public rotation(target?: Rotation): Rotation { + this.rawSet.rbRotation(this.handle, scratchBuffer); + return RotationOps.fromBuffer(scratchBuffer, target); + } + // #endif + /** * The world-space next translation of this rigid-body. * * If this rigid-body is kinematic this value is set by the `setNextKinematicTranslation` * method and is used for estimating the kinematic body velocity at the next timestep. * For non-kinematic bodies, this value is currently unspecified. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public nextTranslation(): Vector { - let res = this.rawSet.rbNextTranslation(this.handle); - return VectorOps.fromRaw(res); + public nextTranslation(target?: Vector): Vector { + this.rawSet.rbNextTranslation(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } + // #if DIM2 /** * The world-space next orientation of this rigid-body. * @@ -326,10 +369,27 @@ export class RigidBody { * method and is used for estimating the kinematic body velocity at the next timestep. * For non-kinematic bodies, this value is currently unspecified. */ - public nextRotation(): Rotation { - let res = this.rawSet.rbNextRotation(this.handle); - return RotationOps.fromRaw(res); + public nextRotation(): number { + return this.rawSet.rbNextRotation(this.handle); } + // #endif + + // #if DIM3 + /** + * The world-space next orientation of this rigid-body. + * + * If this rigid-body is kinematic this value is set by the `setNextKinematicRotation` + * method and is used for estimating the kinematic body velocity at the next timestep. + * For non-kinematic bodies, this value is currently unspecified. + * + * @param {Rotation?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public nextRotation(target?: Rotation): Rotation { + this.rawSet.rbNextRotation(this.handle, scratchBuffer); + return RotationOps.fromBuffer(scratchBuffer, target); + } + // #endif /** * Sets the translation of this rigid-body. @@ -502,31 +562,39 @@ export class RigidBody { /** * The linear velocity of this rigid-body. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public linvel(): Vector { - return VectorOps.fromRaw(this.rawSet.rbLinvel(this.handle)); + public linvel(target?: Vector): Vector { + this.rawSet.rbLinvel(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** * The velocity of the given world-space point on this rigid-body. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public velocityAtPoint(point: Vector): Vector { + public velocityAtPoint(point: Vector, target?: Vector): Vector { const rawPoint = VectorOps.intoRaw(point); - let result = VectorOps.fromRaw( - this.rawSet.rbVelocityAtPoint(this.handle, rawPoint), - ); + this.rawSet.rbVelocityAtPoint(this.handle, rawPoint, scratchBuffer); rawPoint.free(); - return result; + return VectorOps.fromBuffer(scratchBuffer, target); } // #if DIM3 /** * The angular velocity of this rigid-body. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public angvel(): Vector { - return VectorOps.fromRaw(this.rawSet.rbAngvel(this.handle)); + public angvel(target?: Vector): Vector { + this.rawSet.rbAngvel(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } - // #endif // #if DIM2 @@ -536,7 +604,6 @@ export class RigidBody { public angvel(): number { return this.rawSet.rbAngvel(this.handle); } - // #endif /** @@ -548,9 +615,13 @@ export class RigidBody { /** * The inverse mass taking into account translation locking. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public effectiveInvMass(): Vector { - return VectorOps.fromRaw(this.rawSet.rbEffectiveInvMass(this.handle)); + public effectiveInvMass(target?: Vector): Vector { + this.rawSet.rbEffectiveInvMass(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** @@ -564,16 +635,24 @@ export class RigidBody { /** * The center of mass of a rigid-body expressed in its local-space. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public localCom(): Vector { - return VectorOps.fromRaw(this.rawSet.rbLocalCom(this.handle)); + public localCom(target?: Vector): Vector { + this.rawSet.rbLocalCom(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** * The world-space center of mass of the rigid-body. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public worldCom(): Vector { - return VectorOps.fromRaw(this.rawSet.rbWorldCom(this.handle)); + public worldCom(target?: Vector): Vector { + this.rawSet.rbWorldCom(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } // #if DIM2 @@ -585,7 +664,6 @@ export class RigidBody { public invPrincipalInertia(): number { return this.rawSet.rbInvPrincipalInertia(this.handle); } - // #endif // #if DIM3 @@ -593,13 +671,14 @@ export class RigidBody { * The inverse of the principal angular inertia of the rigid-body. * * Components set to zero are assumed to be infinite along the corresponding principal axis. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public invPrincipalInertia(): Vector { - return VectorOps.fromRaw( - this.rawSet.rbInvPrincipalInertia(this.handle), - ); + public invPrincipalInertia(target?: Vector): Vector { + this.rawSet.rbInvPrincipalInertia(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } - // #endif // #if DIM2 @@ -609,29 +688,32 @@ export class RigidBody { public principalInertia(): number { return this.rawSet.rbPrincipalInertia(this.handle); } - // #endif // #if DIM3 /** * The angular inertia along the principal inertia axes of the rigid-body. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public principalInertia(): Vector { - return VectorOps.fromRaw(this.rawSet.rbPrincipalInertia(this.handle)); + public principalInertia(target?: Vector): Vector { + this.rawSet.rbPrincipalInertia(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } - // #endif // #if DIM3 /** * The principal vectors of the local angular inertia tensor of the rigid-body. + * + * @param {Rotation?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public principalInertiaLocalFrame(): Rotation { - return RotationOps.fromRaw( - this.rawSet.rbPrincipalInertiaLocalFrame(this.handle), - ); + public principalInertiaLocalFrame(target?: Rotation): Rotation { + this.rawSet.rbPrincipalInertiaLocalFrame(this.handle, scratchBuffer); + return RotationOps.fromBuffer(scratchBuffer, target); } - // #endif // #if DIM2 @@ -642,18 +724,19 @@ export class RigidBody { public effectiveWorldInvInertia(): number { return this.rawSet.rbEffectiveWorldInvInertia(this.handle); } - // #endif // #if DIM3 /** - * The world-space inverse angular inertia tensor of the rigid-body, + * The square-root of the world-space inverse angular inertia tensor of the rigid-body, * taking into account rotation locking. + * + * @param {SdpMatrix3?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public effectiveWorldInvInertia(): SdpMatrix3 { - return SdpMatrix3Ops.fromRaw( - this.rawSet.rbEffectiveWorldInvInertia(this.handle), - ); + public effectiveWorldInvInertia(target?: SdpMatrix3): SdpMatrix3 { + this.rawSet.rbEffectiveWorldInvInertia(this.handle, scratchBuffer); + return SdpMatrix3Ops.fromBuffer(scratchBuffer, target); } // #endif @@ -673,11 +756,13 @@ export class RigidBody { /** * The effective world-space angular inertia (that takes the potential rotation locking into account) of * this rigid-body. + * + * @param {SdpMatrix3?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public effectiveAngularInertia(): SdpMatrix3 { - return SdpMatrix3Ops.fromRaw( - this.rawSet.rbEffectiveAngularInertia(this.handle), - ); + public effectiveAngularInertia(target?: SdpMatrix3): SdpMatrix3 { + this.rawSet.rbEffectiveAngularInertia(this.handle, scratchBuffer); + return SdpMatrix3Ops.fromBuffer(scratchBuffer, target); } // #endif @@ -1084,9 +1169,13 @@ export class RigidBody { /** * Retrieves the constant force(s) the user added to this rigid-body * Returns zero if the rigid-body is not dynamic. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public userForce(): Vector { - return VectorOps.fromRaw(this.rawSet.rbUserForce(this.handle)); + public userForce(target?: Vector): Vector { + this.rawSet.rbUserForce(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } // #if DIM2 @@ -1103,9 +1192,13 @@ export class RigidBody { /** * Retrieves the constant torque(s) the user added to this rigid-body * Returns zero if the rigid-body is not dynamic. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public userTorque(): Vector { - return VectorOps.fromRaw(this.rawSet.rbUserTorque(this.handle)); + public userTorque(target?: Vector): Vector { + this.rawSet.rbUserTorque(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } // #endif } diff --git a/src.ts/geometry/broad_phase.ts b/src.ts/geometry/broad_phase.ts index addaee92..de4f4f30 100644 --- a/src.ts/geometry/broad_phase.ts +++ b/src.ts/geometry/broad_phase.ts @@ -4,7 +4,7 @@ import {ColliderSet} from "./collider_set"; import {Ray, RayColliderHit, RayColliderIntersection} from "./ray"; import {InteractionGroups} from "./interaction_groups"; import {ColliderHandle} from "./collider"; -import {Rotation, RotationOps, Vector, VectorOps} from "../math"; +import {Rotation, RotationOps, Vector, VectorOps, scratchBuffer} from "../math"; import {Shape} from "./shape"; import {PointColliderProjection} from "./point"; import {ColliderShapeCastHit} from "./toi"; @@ -387,6 +387,8 @@ export class BroadPhase { * that it’s on a path to exit that penetration state. * @param groups - The bit groups and filter associated to the shape to cast, in order to only * test on colliders with collision groups compatible with this group. + * @param {ColliderShapeCastHit?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ public castShape( narrowPhase: NarrowPhase, @@ -404,33 +406,42 @@ export class BroadPhase { filterExcludeCollider?: ColliderHandle, filterExcludeRigidBody?: RigidBodyHandle, filterPredicate?: (collider: ColliderHandle) => boolean, + target?: ColliderShapeCastHit ): ColliderShapeCastHit | null { let rawPos = VectorOps.intoRaw(shapePos); let rawRot = RotationOps.intoRaw(shapeRot); let rawVel = VectorOps.intoRaw(shapeVel); let rawShape = shape.intoRaw(); - let result = ColliderShapeCastHit.fromRaw( - colliders, - this.raw.castShape( - narrowPhase.raw, - bodies.raw, - colliders.raw, - rawPos, - rawRot, - rawVel, - rawShape, - targetDistance, - maxToi, - stopAtPenetration, - filterFlags, - filterGroups, - filterExcludeCollider, - filterExcludeRigidBody, - filterPredicate, - ), + const rawColliderShapeCastHit = this.raw.castShape( + narrowPhase.raw, + bodies.raw, + colliders.raw, + rawPos, + rawRot, + rawVel, + rawShape, + targetDistance, + maxToi, + stopAtPenetration, + filterFlags, + filterGroups, + filterExcludeCollider, + filterExcludeRigidBody, + filterPredicate, + ); + + const colliderHandle: number = rawColliderShapeCastHit.colliderHandle(); + + rawColliderShapeCastHit.getComponents(scratchBuffer); + + let result = ColliderShapeCastHit.fromBuffer( + colliders.get(colliderHandle), + scratchBuffer, + target ); + rawColliderShapeCastHit.free(); rawPos.free(); rawRot.free(); rawVel.free(); diff --git a/src.ts/geometry/collider.ts b/src.ts/geometry/collider.ts index 25817362..d28d4532 100644 --- a/src.ts/geometry/collider.ts +++ b/src.ts/geometry/collider.ts @@ -1,9 +1,13 @@ -import {RawColliderSet} from "../raw"; -import {Rotation, RotationOps, Vector, VectorOps} from "../math"; +import { + Rotation, + RotationOps, + Vector, + VectorOps, + scratchBuffer +} from "../math"; import { CoefficientCombineRule, RigidBody, - RigidBodyHandle, RigidBodySet, } from "../dynamics"; import {ActiveHooks, ActiveEvents} from "../pipeline"; @@ -169,43 +173,76 @@ export class Collider { /** * The world-space translation of this collider. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public translation(): Vector { - return VectorOps.fromRaw( - this.colliderSet.raw.coTranslation(this.handle), - ); + public translation(target?: Vector): Vector { + this.colliderSet.raw.coTranslation(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** * The translation of this collider relative to its parent rigid-body. * * Returns `null` if the collider doesn’t have a parent rigid-body. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public translationWrtParent(): Vector | null { - return VectorOps.fromRaw( - this.colliderSet.raw.coTranslationWrtParent(this.handle), - ); + public translationWrtParent(target?: Vector): Vector | null { + const hasParent = this.colliderSet.raw.coTranslationWrtParent(this.handle, scratchBuffer); + return hasParent ? VectorOps.fromBuffer(scratchBuffer, target) : null; } + // #if DIM2 /** * The world-space orientation of this collider. */ - public rotation(): Rotation { - return RotationOps.fromRaw( - this.colliderSet.raw.coRotation(this.handle), - ); + public rotation(): number { + return this.colliderSet.raw.coRotation(this.handle); } + // #endif + // #if DIM3 + /** + * The world-space orientation of this collider. + * + * @param {Rotation?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public rotation(target?: Rotation): Rotation { + this.colliderSet.raw.coRotation(this.handle, scratchBuffer); + return RotationOps.fromBuffer(scratchBuffer, target); + } + // #endif + + // #if DIM2 /** * 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), - ); + const val = this.colliderSet.raw.coRotationWrtParent(this.handle); + return isNaN(val) ? null : val; + } + // #endif + + // #if DIM3 + /** + * The orientation of this collider relative to its parent rigid-body. + * + * Returns `null` if the collider doesn’t have a parent rigid-body. + * + * @param {Rotation?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public rotationWrtParent(target?: Rotation): Rotation | null { + const hasParent = this.colliderSet.raw.coRotationWrtParent(this.handle, scratchBuffer); + return hasParent ? RotationOps.fromBuffer(scratchBuffer, target) : null; } + // #endif /** * Is this collider a sensor? @@ -622,11 +659,13 @@ export class Collider { /** * The half-extents of this collider if it is a cuboid shape. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public halfExtents(): Vector { - return VectorOps.fromRaw( - this.colliderSet.raw.coHalfExtents(this.handle), - ); + public halfExtents(target?: Vector): Vector | null { + const isCuboid = this.colliderSet.raw.coHalfExtents(this.handle, scratchBuffer); + return isCuboid ? VectorOps.fromBuffer(scratchBuffer, target) : null; } /** @@ -842,10 +881,13 @@ export class Collider { /** * If this collider has a heightfield shape, this returns the scale * applied to it. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public heightfieldScale(): Vector { - let scale = this.colliderSet.raw.coHeightfieldScale(this.handle); - return VectorOps.fromRaw(scale); + public heightfieldScale(target?: Vector): Vector | null { + const isHeightfield = this.colliderSet.raw.coHeightfieldScale(this.handle, scratchBuffer); + return isHeightfield ? VectorOps.fromBuffer(scratchBuffer, target) : null; } // #if DIM3 @@ -984,7 +1026,7 @@ export class Collider { 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). @@ -999,6 +1041,8 @@ export class Collider { * @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. + * @param {ShapeCastHit?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ public castShape( collider1Vel: Vector, @@ -1009,6 +1053,7 @@ export class Collider { targetDistance: number, maxToi: number, stopAtPenetration: boolean, + target?: ShapeCastHit ): ShapeCastHit | null { let rawCollider1Vel = VectorOps.intoRaw(collider1Vel); let rawShape2Pos = VectorOps.intoRaw(shape2Pos); @@ -1016,21 +1061,27 @@ export class Collider { 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, - ), + const rawShapeCastHit = this.colliderSet.raw.coCastShape( + this.handle, + rawCollider1Vel, + rawShape2, + rawShape2Pos, + rawShape2Rot, + rawShape2Vel, + targetDistance, + maxToi, + stopAtPenetration, ); + rawShapeCastHit.getComponents(scratchBuffer); + + let result = ShapeCastHit.fromBuffer( + null, + scratchBuffer, + target, + ); + + rawShapeCastHit.free(); rawCollider1Vel.free(); rawShape2Pos.free(); rawShape2Rot.free(); @@ -1040,7 +1091,7 @@ export class Collider { 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). @@ -1053,6 +1104,8 @@ export class Collider { * @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. + * @param {ColliderShapeCastHit?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ public castCollider( collider1Vel: Vector, @@ -1061,23 +1114,32 @@ export class Collider { targetDistance: number, maxToi: number, stopAtPenetration: boolean, + target?: ColliderShapeCastHit ): 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, - ), + const rawColliderShapeCastHit = this.colliderSet.raw.coCastCollider( + this.handle, + rawCollider1Vel, + collider2.handle, + rawCollider2Vel, + targetDistance, + maxToi, + stopAtPenetration, + ); + + const colliderHandle: number = rawColliderShapeCastHit.colliderHandle(); + + rawColliderShapeCastHit.getComponents(scratchBuffer); + + let result = ColliderShapeCastHit.fromBuffer( + this.colliderSet.get(colliderHandle), + scratchBuffer, + target ); + rawColliderShapeCastHit.free(); rawCollider1Vel.free(); rawCollider2Vel.free(); diff --git a/src.ts/geometry/shape.ts b/src.ts/geometry/shape.ts index f3546982..cc1fe0cd 100644 --- a/src.ts/geometry/shape.ts +++ b/src.ts/geometry/shape.ts @@ -1,4 +1,10 @@ -import {Vector, VectorOps, Rotation, RotationOps} from "../math"; +import { + Vector, + VectorOps, + Rotation, + RotationOps, + scratchBuffer +} from "../math"; import {RawColliderSet, RawShape, RawShapeType} from "../raw"; import {ShapeContact} from "./contact"; import {PointProjection} from "./point"; @@ -23,7 +29,6 @@ export abstract class Shape { ): Shape { const rawType = rawSet.coShapeType(handle); - let extents: Vector; let borderRadius: number; let vs: Float32Array; let indices: Uint32Array; @@ -35,31 +40,32 @@ export abstract class Shape { case RawShapeType.Ball: return new Ball(rawSet.coRadius(handle)); case RawShapeType.Cuboid: - extents = rawSet.coHalfExtents(handle); + rawSet.coHalfExtents(handle, scratchBuffer); + // #if DIM2 - return new Cuboid(extents.x, extents.y); + return new Cuboid(scratchBuffer[0], scratchBuffer[1]); // #endif // #if DIM3 - return new Cuboid(extents.x, extents.y, extents.z); - // #endif + return new Cuboid(scratchBuffer[0], scratchBuffer[1], scratchBuffer[2]); + // #endif case RawShapeType.RoundCuboid: - extents = rawSet.coHalfExtents(handle); borderRadius = rawSet.coRoundRadius(handle); + rawSet.coHalfExtents(handle, scratchBuffer); // #if DIM2 - return new RoundCuboid(extents.x, extents.y, borderRadius); + return new RoundCuboid(scratchBuffer[0], scratchBuffer[1], borderRadius); // #endif // #if DIM3 return new RoundCuboid( - extents.x, - extents.y, - extents.z, + scratchBuffer[0], + scratchBuffer[1], + scratchBuffer[2], borderRadius, ); - // #endif + // #endif case RawShapeType.Capsule: halfHeight = rawSet.coHalfHeight(handle); @@ -143,19 +149,28 @@ export abstract class Shape { return new TriMesh(vs, indices, tri_flags); case RawShapeType.HeightField: - const scale = rawSet.coHeightfieldScale(handle); const heights = rawSet.coHeightfieldHeights(handle); + rawSet.coHeightfieldScale(handle, scratchBuffer); // #if DIM2 + const scale = { + x: scratchBuffer[0], + y: scratchBuffer[1] + }; return new Heightfield(heights, scale); // #endif // #if DIM3 + const scale = { + x: scratchBuffer[0], + y: scratchBuffer[1], + z: scratchBuffer[2] + }; const nrows = rawSet.coHeightfieldNRows(handle); const ncols = rawSet.coHeightfieldNCols(handle); const hf_flags = rawSet.coHeightFieldFlags(handle); return new Heightfield(nrows, ncols, heights, scale, hf_flags); - // #endif + // #endif // #if DIM2 case RawShapeType.ConvexPolygon: @@ -204,7 +219,7 @@ export abstract class Shape { /** * Computes the time of impact between two moving shapes. - * @param shapePos1 - The initial position of this sahpe. + * @param shapePos1 - The initial position of this shape. * @param shapeRot1 - The rotation of this shape. * @param shapeVel1 - The velocity of this shape. * @param shape2 - The second moving shape. @@ -217,9 +232,11 @@ export abstract class Shape { * @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. + * @param {ShapeCastHit?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. * @returns If the two moving shapes collider at some point along their trajectories, this returns the * time at which the two shape collider as well as the contact information during the impact. Returns - * `null`if the two shapes never collide along their paths. + * `null` if the two shapes never collide along their paths. */ public castShape( shapePos1: Vector, @@ -232,6 +249,7 @@ export abstract class Shape { targetDistance: number, maxToi: number, stopAtPenetration: boolean, + target?: ShapeCastHit ): ShapeCastHit | null { let rawPos1 = VectorOps.intoRaw(shapePos1); let rawRot1 = RotationOps.intoRaw(shapeRot1); @@ -243,22 +261,28 @@ export abstract class Shape { let rawShape1 = this.intoRaw(); let rawShape2 = shape2.intoRaw(); - let result = ShapeCastHit.fromRaw( + const rawShapeCastHit = rawShape1.castShape( + rawPos1, + rawRot1, + rawVel1, + rawShape2, + rawPos2, + rawRot2, + rawVel2, + targetDistance, + maxToi, + stopAtPenetration, + ); + + rawShapeCastHit.getComponents(scratchBuffer); + + let result = ShapeCastHit.fromBuffer( null, - rawShape1.castShape( - rawPos1, - rawRot1, - rawVel1, - rawShape2, - rawPos2, - rawRot2, - rawVel2, - targetDistance, - maxToi, - stopAtPenetration, - ), + scratchBuffer, + target, ); + rawShapeCastHit.free(); rawPos1.free(); rawRot1.free(); rawVel1.free(); diff --git a/src.ts/geometry/toi.ts b/src.ts/geometry/toi.ts index ade13f17..a72a1791 100644 --- a/src.ts/geometry/toi.ts +++ b/src.ts/geometry/toi.ts @@ -1,7 +1,5 @@ import {Collider} from "./collider"; import {Vector, VectorOps} from "../math"; -import {RawShapeCastHit, RawColliderShapeCastHit} from "../raw"; -import {ColliderSet} from "./collider_set"; /** * The intersection between a ray and a collider. @@ -46,21 +44,50 @@ export class ShapeCastHit { this.normal2 = normal2; } - public static fromRaw( - colliderSet: ColliderSet, - raw: RawShapeCastHit, + public static fromBuffer( + collider: Collider, + buffer: Float32Array, + target?: ShapeCastHit ): ShapeCastHit { - if (!raw) return null; - - const result = new ShapeCastHit( - raw.time_of_impact(), - VectorOps.fromRaw(raw.witness1()), - VectorOps.fromRaw(raw.witness2()), - VectorOps.fromRaw(raw.normal1()), - VectorOps.fromRaw(raw.normal2()), + if (!buffer) return null; + + target ??= new ShapeCastHit( + 0, + VectorOps.zeros(), + VectorOps.zeros(), + VectorOps.zeros(), + VectorOps.zeros() ); - raw.free(); - return result; + + target.time_of_impact = buffer[0]; + + // #if DIM2 + target.witness1.x = buffer[1]; + target.witness1.y = buffer[2]; + target.witness2.x = buffer[3]; + target.witness2.y = buffer[4]; + target.normal1.x = buffer[5]; + target.normal1.y = buffer[6]; + target.normal2.x = buffer[7]; + target.normal2.y = buffer[8]; + // #endif + + // #if DIM3 + target.witness1.x = buffer[1]; + target.witness1.y = buffer[2]; + target.witness1.z = buffer[3]; + target.witness2.x = buffer[4]; + target.witness2.y = buffer[5]; + target.witness2.z = buffer[6]; + target.normal1.x = buffer[7]; + target.normal1.y = buffer[8] + target.normal1.z = buffer[9]; + target.normal2.x = buffer[10]; + target.normal2.y = buffer[11]; + target.normal2.z = buffer[12]; + // #endif + + return target; } } @@ -85,21 +112,52 @@ export class ColliderShapeCastHit extends ShapeCastHit { this.collider = collider; } - public static fromRaw( - colliderSet: ColliderSet, - raw: RawColliderShapeCastHit, + public static fromBuffer( + collider: Collider, + buffer: Float32Array, + target?: ColliderShapeCastHit ): ColliderShapeCastHit { - if (!raw) return null; - - const result = new ColliderShapeCastHit( - colliderSet.get(raw.colliderHandle()), - raw.time_of_impact(), - VectorOps.fromRaw(raw.witness1()), - VectorOps.fromRaw(raw.witness2()), - VectorOps.fromRaw(raw.normal1()), - VectorOps.fromRaw(raw.normal2()), + if (!buffer) return null; + + target ??= new ColliderShapeCastHit( + null, + 0, + VectorOps.zeros(), + VectorOps.zeros(), + VectorOps.zeros(), + VectorOps.zeros() ); - raw.free(); - return result; + + target.collider = collider; + + target.time_of_impact = buffer[0]; + + // #if DIM2 + target.witness1.x = buffer[1]; + target.witness1.y = buffer[2]; + target.witness2.x = buffer[3]; + target.witness2.y = buffer[4]; + target.normal1.x = buffer[5]; + target.normal1.y = buffer[6]; + target.normal2.x = buffer[7]; + target.normal2.y = buffer[8]; + // #endif + + // #if DIM3 + target.witness1.x = buffer[1]; + target.witness1.y = buffer[2]; + target.witness1.z = buffer[3]; + target.witness2.x = buffer[4]; + target.witness2.y = buffer[5]; + target.witness2.z = buffer[6]; + target.normal1.x = buffer[7]; + target.normal1.y = buffer[8] + target.normal1.z = buffer[9]; + target.normal2.x = buffer[10]; + target.normal2.y = buffer[11]; + target.normal2.z = buffer[12]; + // #endif + + return target; } } diff --git a/src.ts/math.ts b/src.ts/math.ts index ebca392b..1db152b4 100644 --- a/src.ts/math.ts +++ b/src.ts/math.ts @@ -3,6 +3,9 @@ import {RawVector, RawRotation} from "./raw"; import {RawSdpMatrix3} from "./raw"; // #endif +// scratchBuffer should be as big as the biggest index Rust tries to set on it. +export const scratchBuffer = new Float32Array(16); + // #if DIM2 export interface Vector { x: number; @@ -31,6 +34,15 @@ export class VectorOps { return VectorOps.new(0.0, 0.0); } + public static fromBuffer(buffer: Float32Array, target?: Vector): Vector { + if (!buffer) return null; + + target ??= VectorOps.zeros(); + target.x = buffer[0]; + target.y = buffer[1]; + return target; + } + // FIXME: type ram: RawVector? public static fromRaw(raw: RawVector): Vector { if (!raw) return null; @@ -110,6 +122,16 @@ export class VectorOps { return VectorOps.new(0.0, 0.0, 0.0); } + public static fromBuffer(buffer: Float32Array, target?: Vector): Vector { + if (!buffer) return null; + + target ??= VectorOps.zeros(); + target.x = buffer[0]; + target.y = buffer[1]; + target.z = buffer[2]; + return target; + } + // FIXME: type ram: RawVector? public static fromRaw(raw: RawVector): Vector { if (!raw) return null; @@ -155,6 +177,17 @@ export class RotationOps { return new Quaternion(0.0, 0.0, 0.0, 1.0); } + public static fromBuffer(buffer: Float32Array, target?: Rotation): Rotation { + if (!buffer) return null; + + target ??= RotationOps.identity(); + target.x = buffer[0]; + target.y = buffer[1]; + target.z = buffer[2]; + target.w = buffer[3]; + return target; + } + public static fromRaw(raw: RawRotation): Rotation { if (!raw) return null; @@ -253,6 +286,20 @@ export class SdpMatrix3 { } export class SdpMatrix3Ops { + + public static fromBuffer(buffer: Float32Array, target?: SdpMatrix3): SdpMatrix3 { + if (!buffer) return null; + + target ??= new SdpMatrix3(buffer); + target.elements[0] = buffer[0]; + target.elements[1] = buffer[1]; + target.elements[2] = buffer[2]; + target.elements[3] = buffer[3]; + target.elements[4] = buffer[4]; + target.elements[5] = buffer[5]; + return target; + } + public static fromRaw(raw: RawSdpMatrix3): SdpMatrix3 { const sdpMatrix3 = new SdpMatrix3(raw.elements()); raw.free(); diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index d7174d77..b4be7076 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -1,7 +1,5 @@ use crate::dynamics::{RawRigidBodySet, RawRigidBodyType}; use crate::geometry::RawColliderSet; -#[cfg(feature = "dim3")] -use crate::math::RawSdpMatrix3; use crate::math::{RawRotation, RawVector}; use crate::utils::{self, FlatHandle}; use na::Point; @@ -11,15 +9,64 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] impl RawRigidBodySet { /// The world-space translation of this rigid-body. - pub fn rbTranslation(&self, handle: FlatHandle) -> RawVector { + pub fn rbTranslationOriginal(&self, handle: FlatHandle) -> RawVector { self.map(handle, |rb| RawVector(rb.position().translation.vector)) } /// The world-space orientation of this rigid-body. - pub fn rbRotation(&self, handle: FlatHandle) -> RawRotation { + pub fn rbRotationOriginal(&self, handle: FlatHandle) -> RawRotation { self.map(handle, |rb| RawRotation(rb.position().rotation)) } + /// The world-space translation of this rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbTranslation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.position().translation.vector; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); + } + + /// The world-space translation of this rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbTranslation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.position().translation.vector; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); + } + + /// The world-space orientation of this rigid-body. + #[cfg(feature = "dim2")] + pub fn rbRotation(&self, handle: FlatHandle) -> f32 { + self.map(handle, |rb| rb.position().rotation.angle()) + } + + /// The world-space orientation of this rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbRotation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.position().rotation; + let inner = u.into_inner(); + scratchBuffer.set_index(0, inner.i); + scratchBuffer.set_index(1, inner.j); + scratchBuffer.set_index(2, inner.k); + scratchBuffer.set_index(3, inner.w); + }); + } + /// Put the given rigid-body to sleep. pub fn rbSleep(&mut self, handle: FlatHandle) { self.map_mut(handle, |rb| rb.sleep()); @@ -40,10 +87,34 @@ impl RawRigidBodySet { /// If this rigid-body is kinematic this value is set by the `setNextKinematicTranslation` /// method and is used for estimating the kinematic body velocity at the next timestep. /// For non-kinematic bodies, this value is currently unspecified. - pub fn rbNextTranslation(&self, handle: FlatHandle) -> RawVector { + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbNextTranslation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { self.map(handle, |rb| { - RawVector(rb.next_position().translation.vector) - }) + let u = rb.next_position().translation.vector; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); + } + + /// The world-space predicted translation of this rigid-body. + /// + /// If this rigid-body is kinematic this value is set by the `setNextKinematicTranslation` + /// method and is used for estimating the kinematic body velocity at the next timestep. + /// For non-kinematic bodies, this value is currently unspecified. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbNextTranslation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.next_position().translation.vector; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); } /// The world-space predicted orientation of this rigid-body. @@ -51,8 +122,29 @@ impl RawRigidBodySet { /// If this rigid-body is kinematic this value is set by the `setNextKinematicRotation` /// method and is used for estimating the kinematic body velocity at the next timestep. /// For non-kinematic bodies, this value is currently unspecified. - pub fn rbNextRotation(&self, handle: FlatHandle) -> RawRotation { - self.map(handle, |rb| RawRotation(rb.next_position().rotation)) + #[cfg(feature = "dim2")] + pub fn rbNextRotation(&self, handle: FlatHandle) -> f32 { + self.map(handle, |rb| rb.next_position().rotation.angle()) + } + + /// The world-space predicted orientation of this rigid-body. + /// + /// If this rigid-body is kinematic this value is set by the `setNextKinematicRotation` + /// method and is used for estimating the kinematic body velocity at the next timestep. + /// For non-kinematic bodies, this value is currently unspecified. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbNextRotation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.next_position().rotation; + let inner = u.into_inner(); + scratchBuffer.set_index(0, inner.i); + scratchBuffer.set_index(1, inner.j); + scratchBuffer.set_index(2, inner.k); + scratchBuffer.set_index(3, inner.w); + }); } /// Sets the translation of this rigid-body. @@ -282,8 +374,30 @@ impl RawRigidBodySet { } /// The linear velocity of this rigid-body. - pub fn rbLinvel(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |rb| RawVector(*rb.linvel())) + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbLinvel(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.linvel(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); + } + + /// The linear velocity of this rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbLinvel(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.linvel(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); } /// The angular velocity of this rigid-body. @@ -293,16 +407,44 @@ impl RawRigidBodySet { } /// The angular velocity of this rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbAngvel(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |rb| RawVector(*rb.angvel())) + pub fn rbAngvel(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.angvel(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); + } + + /// The velocity of the given world-space point on this rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbVelocityAtPoint(&self, handle: FlatHandle, point: &RawVector, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.velocity_at_point(&Point::from(point.0)); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); } /// The velocity of the given world-space point on this rigid-body. - pub fn rbVelocityAtPoint(&self, handle: FlatHandle, point: &RawVector) -> RawVector { + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbVelocityAtPoint(&self, handle: FlatHandle, point: &RawVector, scratchBuffer: &js_sys::Float32Array) { self.map(handle, |rb| { - rb.velocity_at_point(&Point::from(point.0)).into() - }) + let u = rb.velocity_at_point(&Point::from(point.0)); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); } pub fn rbLockTranslations(&mut self, handle: FlatHandle, locked: bool, wake_up: bool) { @@ -383,20 +525,84 @@ impl RawRigidBodySet { } /// The inverse mass taking into account translation locking. - pub fn rbEffectiveInvMass(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |rb| rb.mass_properties().effective_inv_mass.into()) + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbEffectiveInvMass(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().effective_inv_mass; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); + } + + /// The inverse mass taking into account translation locking. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbEffectiveInvMass(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().effective_inv_mass; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); } /// The center of mass of a rigid-body expressed in its local-space. - pub fn rbLocalCom(&self, handle: FlatHandle) -> RawVector { + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbLocalCom(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { self.map(handle, |rb| { - rb.mass_properties().local_mprops.local_com.into() - }) + let u = rb.mass_properties().local_mprops.local_com; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); + } + + /// The center of mass of a rigid-body expressed in its local-space. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbLocalCom(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().local_mprops.local_com; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); + } + + /// The world-space center of mass of the rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbWorldCom(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().world_com; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); } /// The world-space center of mass of the rigid-body. - pub fn rbWorldCom(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |rb| rb.mass_properties().world_com.into()) + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbWorldCom(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().world_com; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); } /// The inverse of the principal angular inertia of the rigid-body. @@ -404,89 +610,110 @@ impl RawRigidBodySet { /// Components set to zero are assumed to be infinite along the corresponding principal axis. #[cfg(feature = "dim2")] pub fn rbInvPrincipalInertia(&self, handle: FlatHandle) -> f32 { - self.map(handle, |rb| { - rb.mass_properties() - .local_mprops - .inv_principal_inertia - .into() - }) + self.map(handle, |rb| rb.mass_properties().local_mprops.inv_principal_inertia) } /// The inverse of the principal angular inertia of the rigid-body. /// /// Components set to zero are assumed to be infinite along the corresponding principal axis. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbInvPrincipalInertia(&self, handle: FlatHandle) -> RawVector { + pub fn rbInvPrincipalInertia(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { self.map(handle, |rb| { - rb.mass_properties() - .local_mprops - .inv_principal_inertia - .into() - }) + let u = rb.mass_properties().local_mprops.inv_principal_inertia; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); } - #[cfg(feature = "dim3")] /// The principal vectors of the local angular inertia tensor of the rigid-body. - pub fn rbPrincipalInertiaLocalFrame(&self, handle: FlatHandle) -> RawRotation { + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbPrincipalInertiaLocalFrame(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { self.map(handle, |rb| { - RawRotation::from( - rb.mass_properties() - .local_mprops - .principal_inertia_local_frame, - ) - }) + let u = rb.mass_properties().local_mprops.principal_inertia_local_frame; + let inner = u.into_inner(); + scratchBuffer.set_index(0, inner.i); + scratchBuffer.set_index(1, inner.j); + scratchBuffer.set_index(2, inner.k); + scratchBuffer.set_index(3, inner.w); + }); } /// The angular inertia along the principal inertia axes of the rigid-body. #[cfg(feature = "dim2")] pub fn rbPrincipalInertia(&self, handle: FlatHandle) -> f32 { - self.map(handle, |rb| { - rb.mass_properties().local_mprops.principal_inertia().into() - }) + self.map(handle, |rb| rb.mass_properties().local_mprops.principal_inertia()) } /// The angular inertia along the principal inertia axes of the rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbPrincipalInertia(&self, handle: FlatHandle) -> RawVector { + pub fn rbPrincipalInertia(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { self.map(handle, |rb| { - rb.mass_properties().local_mprops.principal_inertia().into() - }) + let u = rb.mass_properties().local_mprops.principal_inertia(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); + } /// The world-space inverse angular inertia tensor of the rigid-body, /// taking into account rotation locking. #[cfg(feature = "dim2")] pub fn rbEffectiveWorldInvInertia(&self, handle: FlatHandle) -> f32 { - self.map(handle, |rb| { - rb.mass_properties().effective_world_inv_inertia.into() - }) + self.map(handle, |rb| rb.mass_properties().effective_world_inv_inertia) } /// The world-space inverse angular inertia tensor of the rigid-body, /// taking into account rotation locking. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbEffectiveWorldInvInertia(&self, handle: FlatHandle) -> RawSdpMatrix3 { + pub fn rbEffectiveWorldInvInertia(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { self.map(handle, |rb| { - rb.mass_properties().effective_world_inv_inertia.into() - }) + let u = rb.mass_properties().effective_world_inv_inertia; + scratchBuffer.set_index(0, u.m11); + scratchBuffer.set_index(1, u.m12); + scratchBuffer.set_index(2, u.m13); + scratchBuffer.set_index(3, u.m22); + scratchBuffer.set_index(4, u.m23); + scratchBuffer.set_index(5, u.m33); + }); } /// The effective world-space angular inertia (that takes the potential rotation locking into account) of /// this rigid-body. #[cfg(feature = "dim2")] pub fn rbEffectiveAngularInertia(&self, handle: FlatHandle) -> f32 { - self.map(handle, |rb| { - rb.mass_properties().effective_angular_inertia().into() - }) + self.map(handle, |rb| rb.mass_properties().effective_angular_inertia()) } /// The effective world-space angular inertia (that takes the potential rotation locking into account) of /// this rigid-body. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbEffectiveAngularInertia(&self, handle: FlatHandle) -> RawSdpMatrix3 { + pub fn rbEffectiveAngularInertia(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { self.map(handle, |rb| { - rb.mass_properties().effective_angular_inertia().into() - }) + let u = rb.mass_properties().effective_angular_inertia(); + scratchBuffer.set_index(0, u.m11); + scratchBuffer.set_index(1, u.m12); + scratchBuffer.set_index(2, u.m13); + scratchBuffer.set_index(3, u.m22); + scratchBuffer.set_index(4, u.m23); + scratchBuffer.set_index(5, u.m33); + }); } /// Wakes this rigid-body up. @@ -733,8 +960,31 @@ impl RawRigidBodySet { /// Retrieves the constant force(s) the user added to this rigid-body. /// Returns zero if the rigid-body is not dynamic. - pub fn rbUserForce(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |rb| rb.user_force().into()) + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbUserForce(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.user_force(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); + } + + /// Retrieves the constant force(s) the user added to this rigid-body. + /// Returns zero if the rigid-body is not dynamic. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbUserForce(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.user_force(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); } /// Retrieves the constant torque(s) the user added to this rigid-body. @@ -746,8 +996,16 @@ impl RawRigidBodySet { /// Retrieves the constant torque(s) the user added to this rigid-body. /// Returns zero if the rigid-body is not dynamic. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbUserTorque(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |rb| rb.user_torque().into()) + pub fn rbUserTorque(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.user_torque(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); } } diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index e8019044..023dafdc 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -16,31 +16,113 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] impl RawColliderSet { /// The world-space translation of this collider. - pub fn coTranslation(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |co| co.position().translation.vector.into()) + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn coTranslation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |co| { + let u = co.position().translation.vector; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + }); + } + /// The world-space translation of this collider. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coTranslation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |co| { + let u = co.position().translation.vector; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + }); + } + + /// The world-space orientation of this collider. + #[cfg(feature = "dim2")] + pub fn coRotation(&self, handle: FlatHandle) -> f32 { + self.map(handle, |co| co.position().rotation.angle()) } /// The world-space orientation of this collider. - pub fn coRotation(&self, handle: FlatHandle) -> RawRotation { - self.map(handle, |co| co.position().rotation.into()) + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coRotation(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) { + self.map(handle, |co| { + let u = co.position().rotation; + let inner = u.into_inner(); + scratchBuffer.set_index(0, inner.i); + scratchBuffer.set_index(1, inner.j); + scratchBuffer.set_index(2, inner.k); + scratchBuffer.set_index(3, inner.w); + }); + } + + /// The translation of this collider relative to its parent rigid-body. + /// + /// Returns `false` if it doesn’t have a parent. + #[cfg(feature = "dim2")] + pub fn coTranslationWrtParent(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) -> bool { + self.map(handle, |co| co.position_wrt_parent().map_or(false, |pose| { + let u = pose.translation.vector; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + true + })) } /// The translation of this collider relative to its parent rigid-body. /// - /// Returns the `None` if it doesn’t have a parent. - pub fn coTranslationWrtParent(&self, handle: FlatHandle) -> Option { + /// Returns `false` if it doesn’t have a parent. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coTranslationWrtParent(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) -> bool { + self.map(handle, |co| co.position_wrt_parent().map_or(false, |pose| { + let u = pose.translation.vector; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + true + })) + } + + /// The orientation of this collider relative to its parent rigid-body. + /// + /// Returns `NAN` if it doesn’t have a parent. + #[cfg(feature = "dim2")] + pub fn coRotationWrtParent(&self, handle: FlatHandle) -> f32 { self.map(handle, |co| { - co.position_wrt_parent() - .map(|pose| pose.translation.vector.into()) + co.position_wrt_parent().map_or(f32::NAN, |pose| { + pose.rotation.angle() + }) }) } /// The orientation of this collider relative to its parent rigid-body. /// - /// Returns the `None` if it doesn’t have a parent. - pub fn coRotationWrtParent(&self, handle: FlatHandle) -> Option { + /// Returns `false` if it doesn’t have a parent. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coRotationWrtParent(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) -> bool { self.map(handle, |co| { - co.position_wrt_parent().map(|pose| pose.rotation.into()) + co.position_wrt_parent().map_or(false, |pose| { + let u = pose.rotation; + let inner = u.into_inner(); + scratchBuffer.set_index(0, inner.i); + scratchBuffer.set_index(1, inner.j); + scratchBuffer.set_index(2, inner.k); + scratchBuffer.set_index(3, inner.w); + true + }) }) } @@ -177,17 +259,55 @@ impl RawColliderSet { }) } - /// The half-extents of this collider if it is has a cuboid shape. - pub fn coHalfExtents(&self, handle: FlatHandle) -> Option { + /// The half-extents of this collider if it has a cuboid shape. + /// + /// Returns `false` if it doesn’t have a cuboid shape. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn coHalfExtents(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) -> bool { self.map(handle, |co| { - co.shape() - .as_cuboid() - .map(|c| c.half_extents.into()) - .or_else(|| { - co.shape() - .as_round_cuboid() - .map(|c| c.inner_shape.half_extents.into()) + co.shape().as_cuboid().map_or_else(|| { + co.shape().as_round_cuboid().map_or(false, |c| { + let u = c.inner_shape.half_extents; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + true + }) + },|c| { + let u = c.half_extents; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + true + }) + }) + } + + /// The half-extents of this collider if it has a cuboid shape. + /// + /// Returns `false` if it doesn’t have a cuboid shape. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coHalfExtents(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) -> bool { + self.map(handle, |co| { + co.shape().as_cuboid().map_or_else(|| { + co.shape().as_round_cuboid().map_or(false,|c| { + let u = c.inner_shape.half_extents; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + true }) + },|c| { + let u = c.half_extents; + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + true + }) }) } @@ -580,11 +700,38 @@ impl RawColliderSet { }) } - /// The scaling factor applied of this heightfield if it is one. - pub fn coHeightfieldScale(&self, handle: FlatHandle) -> Option { + /// The scaling factor applied to this heightfield if it is one. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn coHeightfieldScale(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) -> bool { self.map(handle, |co| match co.shape().shape_type() { - ShapeType::HeightField => co.shape().as_heightfield().map(|h| RawVector(*h.scale())), - _ => None, + ShapeType::HeightField => co.shape().as_heightfield().map_or(false, |h| { + let u = h.scale(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + true + }), + _ => false, + }) + } + + /// The scaling factor applied to this heightfield if it is one. + /// + /// # Parameters + /// - `scratchBuffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coHeightfieldScale(&self, handle: FlatHandle, scratchBuffer: &js_sys::Float32Array) -> bool { + self.map(handle, |co| match co.shape().shape_type() { + ShapeType::HeightField => co.shape().as_heightfield().map_or(false, |h| { + let u = h.scale(); + scratchBuffer.set_index(0, u.x); + scratchBuffer.set_index(1, u.y); + scratchBuffer.set_index(2, u.z); + true + }), + _ => false, }) } diff --git a/src/geometry/toi.rs b/src/geometry/toi.rs index 1e00675d..3d306f62 100644 --- a/src/geometry/toi.rs +++ b/src/geometry/toi.rs @@ -1,4 +1,3 @@ -use crate::math::RawVector; use crate::utils::{self, FlatHandle}; use rapier::geometry::{ColliderHandle, ShapeCastHit}; use wasm_bindgen::prelude::*; @@ -10,24 +9,41 @@ pub struct RawShapeCastHit { #[wasm_bindgen] impl RawShapeCastHit { - pub fn time_of_impact(&self) -> f32 { - self.hit.time_of_impact + #[cfg(feature = "dim2")] + pub fn getComponents(&self, scratchBuffer: &js_sys::Float32Array) { + scratchBuffer.set_index(0, self.hit.time_of_impact); + let mut u = self.hit.witness1.coords; + scratchBuffer.set_index(1, u.x); + scratchBuffer.set_index(2, u.y); + u = self.hit.witness2.coords; + scratchBuffer.set_index(3, u.x); + scratchBuffer.set_index(4, u.y); + u = self.hit.normal1.into_inner(); + scratchBuffer.set_index(5, u.x); + scratchBuffer.set_index(6, u.y); + u = self.hit.normal2.into_inner(); + scratchBuffer.set_index(7, u.x); + scratchBuffer.set_index(8, u.y); } - - pub fn witness1(&self) -> RawVector { - self.hit.witness1.coords.into() - } - - pub fn witness2(&self) -> RawVector { - self.hit.witness2.coords.into() - } - - pub fn normal1(&self) -> RawVector { - self.hit.normal1.into_inner().into() - } - - pub fn normal2(&self) -> RawVector { - self.hit.normal2.into_inner().into() + #[cfg(feature = "dim3")] + pub fn getComponents(&self, scratchBuffer: &js_sys::Float32Array) { + scratchBuffer.set_index(0, self.hit.time_of_impact); + let mut u = self.hit.witness1.coords; + scratchBuffer.set_index(1, u.x); + scratchBuffer.set_index(2, u.y); + scratchBuffer.set_index(3, u.z); + u = self.hit.witness2.coords; + scratchBuffer.set_index(4, u.x); + scratchBuffer.set_index(5, u.y); + scratchBuffer.set_index(6, u.z); + u = self.hit.normal1.into_inner(); + scratchBuffer.set_index(7, u.x); + scratchBuffer.set_index(8, u.y); + scratchBuffer.set_index(9, u.z); + u = self.hit.normal2.into_inner(); + scratchBuffer.set_index(10, u.x); + scratchBuffer.set_index(11, u.y); + scratchBuffer.set_index(12, u.z); } } @@ -42,24 +58,40 @@ impl RawColliderShapeCastHit { pub fn colliderHandle(&self) -> FlatHandle { utils::flat_handle(self.handle.0) } - - pub fn time_of_impact(&self) -> f32 { - self.hit.time_of_impact + #[cfg(feature = "dim2")] + pub fn getComponents(&self, scratchBuffer: &js_sys::Float32Array) { + scratchBuffer.set_index(0, self.hit.time_of_impact); + let mut u = self.hit.witness1.coords; + scratchBuffer.set_index(1, u.x); + scratchBuffer.set_index(2, u.y); + u = self.hit.witness2.coords; + scratchBuffer.set_index(3, u.x); + scratchBuffer.set_index(4, u.y); + u = self.hit.normal1.into_inner(); + scratchBuffer.set_index(5, u.x); + scratchBuffer.set_index(6, u.y); + u = self.hit.normal2.into_inner(); + scratchBuffer.set_index(7, u.x); + scratchBuffer.set_index(8, u.y); } - - pub fn witness1(&self) -> RawVector { - self.hit.witness1.coords.into() - } - - pub fn witness2(&self) -> RawVector { - self.hit.witness2.coords.into() - } - - pub fn normal1(&self) -> RawVector { - self.hit.normal1.into_inner().into() - } - - pub fn normal2(&self) -> RawVector { - self.hit.normal2.into_inner().into() + #[cfg(feature = "dim3")] + pub fn getComponents(&self, scratchBuffer: &js_sys::Float32Array) { + scratchBuffer.set_index(0, self.hit.time_of_impact); + let mut u = self.hit.witness1.coords; + scratchBuffer.set_index(1, u.x); + scratchBuffer.set_index(2, u.y); + scratchBuffer.set_index(3, u.z); + u = self.hit.witness2.coords; + scratchBuffer.set_index(4, u.x); + scratchBuffer.set_index(5, u.y); + scratchBuffer.set_index(6, u.z); + u = self.hit.normal1.into_inner(); + scratchBuffer.set_index(7, u.x); + scratchBuffer.set_index(8, u.y); + scratchBuffer.set_index(9, u.z); + u = self.hit.normal2.into_inner(); + scratchBuffer.set_index(10, u.x); + scratchBuffer.set_index(11, u.y); + scratchBuffer.set_index(12, u.z); } }