diff --git a/Cargo.lock b/Cargo.lock index 0d2a5111..cb0c6667 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -338,12 +338,13 @@ dependencies = [ [[package]] name = "dimforge_rapier2d" -version = "0.19.2" +version = "0.19.3" dependencies = [ "bincode", "js-sys", "nalgebra", "palette", + "parry2d", "rapier2d", "ref-cast", "serde", @@ -352,12 +353,13 @@ dependencies = [ [[package]] name = "dimforge_rapier2d-deterministic" -version = "0.19.2" +version = "0.19.3" dependencies = [ "bincode", "js-sys", "nalgebra", "palette", + "parry2d", "rapier2d", "ref-cast", "serde", @@ -366,12 +368,13 @@ dependencies = [ [[package]] name = "dimforge_rapier2d-simd" -version = "0.19.2" +version = "0.19.3" dependencies = [ "bincode", "js-sys", "nalgebra", "palette", + "parry2d", "rapier2d", "ref-cast", "serde", @@ -380,12 +383,13 @@ dependencies = [ [[package]] name = "dimforge_rapier3d" -version = "0.19.2" +version = "0.19.3" dependencies = [ "bincode", "js-sys", "nalgebra", "palette", + "parry3d", "rapier3d", "ref-cast", "serde", @@ -394,12 +398,13 @@ dependencies = [ [[package]] name = "dimforge_rapier3d-deterministic" -version = "0.19.2" +version = "0.19.3" dependencies = [ "bincode", "js-sys", "nalgebra", "palette", + "parry3d", "rapier3d", "ref-cast", "serde", @@ -408,12 +413,13 @@ dependencies = [ [[package]] name = "dimforge_rapier3d-simd" -version = "0.19.2" +version = "0.19.3" dependencies = [ "bincode", "js-sys", "nalgebra", "palette", + "parry3d", "rapier3d", "ref-cast", "serde", @@ -465,6 +471,30 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -726,10 +756,12 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -926,9 +958,9 @@ dependencies = [ [[package]] name = "parry2d" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ea16e5cdf52dd91b2a98ef781a2121f48dfb0880a92ae1ec6bc9e78097fceb" +checksum = "ef681740349cec3ab9b5996b03b459b383b6998e1ffcb2804e8b57eb1e8491d9" dependencies = [ "approx", "arrayvec", @@ -955,9 +987,9 @@ dependencies = [ [[package]] name = "parry3d" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2b4291e3c8fcba5f514ed627228c92fa4c94c38bb202c35be7b3b021090193" +checksum = "e99471b7b6870f7fe406d5611dd4b4c9b07aa3e5436b1d27e1515f9832bb0c6b" dependencies = [ "approx", "arrayvec", @@ -1102,6 +1134,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1639,35 +1677,22 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1675,22 +1700,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] diff --git a/builds/prepare_builds/templates/package.json.tera b/builds/prepare_builds/templates/package.json.tera index 95cc5f79..d596c196 100644 --- a/builds/prepare_builds/templates/package.json.tera +++ b/builds/prepare_builds/templates/package.json.tera @@ -14,7 +14,8 @@ "//": "Better keep rimraf version in sync with wasm-pack, see https://github.com/rustwasm/wasm-pack/issues/1444", "devDependencies": { "rimraf": "^3.0.2", - "typedoc": "^0.25.13" + "typedoc": "^0.25.13", + "typescript": "~5.4.5" }, "dependencies": { "wasm-pack": "^0.12.1" diff --git a/builds/prepare_builds/templates/tsconfig.json b/builds/prepare_builds/templates/tsconfig.json index ecdfc4ce..1614ac78 100644 --- a/builds/prepare_builds/templates/tsconfig.json +++ b/builds/prepare_builds/templates/tsconfig.json @@ -3,7 +3,7 @@ "outDir": "./pkg", "module": "ES6", "target": "es6", - "lib": ["es6"], + "lib": ["es6", "ESNext.Disposable"], "moduleResolution": "node", "sourceMap": true, "declaration": true, diff --git a/builds/prepare_builds/templates/tsconfig_typedoc.json b/builds/prepare_builds/templates/tsconfig_typedoc.json index eea45699..f6958d5c 100644 --- a/builds/prepare_builds/templates/tsconfig_typedoc.json +++ b/builds/prepare_builds/templates/tsconfig_typedoc.json @@ -4,7 +4,7 @@ "moduleResolution": "node", "sourceMap": true, "declaration": true, - "lib": ["es6"] + "lib": ["es6", "ESNext.Disposable"] }, "files": ["./pkg/rapier.d.ts"] } diff --git a/src.ts/control/character_controller.ts b/src.ts/control/character_controller.ts index 8e536392..4c10f82c 100644 --- a/src.ts/control/character_controller.ts +++ b/src.ts/control/character_controller.ts @@ -1,5 +1,5 @@ import {RawKinematicCharacterController, RawCharacterCollision} from "../raw"; -import {Rotation, Vector, VectorOps} from "../math"; +import {Rotation, Vector, VectorOps, scratchBuffer} from "../math"; import { BroadPhase, Collider, @@ -333,8 +333,9 @@ export class KinematicCharacterController { /** * The movement computed by the last call to `this.computeColliderMovement`. */ - public computedMovement(): Vector { - return VectorOps.fromRaw(this.raw.computedMovement()); + public computedMovement(target?: Vector): Vector { + this.raw.computedMovement(scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** @@ -368,17 +369,25 @@ export class KinematicCharacterController { } else { let c = this.rawCharacterCollision; out = out ?? new CharacterCollision(); - out.translationDeltaApplied = VectorOps.fromRaw( - c.translationDeltaApplied(), + c.translationDeltaApplied(scratchBuffer); + out.translationDeltaApplied = VectorOps.fromBuffer( + scratchBuffer, + out.translationDeltaApplied, ); - out.translationDeltaRemaining = VectorOps.fromRaw( - c.translationDeltaRemaining(), + c.translationDeltaRemaining(scratchBuffer); + out.translationDeltaRemaining = VectorOps.fromBuffer( + scratchBuffer, + out.translationDeltaRemaining, ); out.toi = c.toi(); - out.witness1 = VectorOps.fromRaw(c.worldWitness1()); - out.witness2 = VectorOps.fromRaw(c.worldWitness2()); - out.normal1 = VectorOps.fromRaw(c.worldNormal1()); - out.normal2 = VectorOps.fromRaw(c.worldNormal2()); + c.worldWitness1(scratchBuffer); + out.witness1 = VectorOps.fromBuffer(scratchBuffer, out.witness1); + c.worldWitness2(scratchBuffer); + out.witness2 = VectorOps.fromBuffer(scratchBuffer, out.witness2); + c.worldNormal1(scratchBuffer); + out.normal1 = VectorOps.fromBuffer(scratchBuffer, out.normal1); + c.worldNormal2(scratchBuffer); + out.normal2 = VectorOps.fromBuffer(scratchBuffer, out.normal2); out.collider = this.colliders.get(c.handle()); return out; } diff --git a/src.ts/control/pid_controller.ts b/src.ts/control/pid_controller.ts index 52b2f006..a8f9bdac 100644 --- a/src.ts/control/pid_controller.ts +++ b/src.ts/control/pid_controller.ts @@ -1,5 +1,5 @@ import {RawPidController} from "../raw"; -import {Rotation, RotationOps, Vector, VectorOps} from "../math"; +import {Rotation, RotationOps, scratchBuffer, Vector, VectorOps} from "../math"; import {Collider, ColliderSet, InteractionGroups, Shape} from "../geometry"; import {QueryFilterFlags, World} from "../pipeline"; import {IntegrationParameters, RigidBody, RigidBodySet} from "../dynamics"; @@ -151,20 +151,22 @@ export class PidController { body: RigidBody, targetPosition: Vector, targetLinvel: Vector, + target?: Vector, ): Vector { let rawPos = VectorOps.intoRaw(targetPosition); let rawVel = VectorOps.intoRaw(targetLinvel); - let correction = this.raw.linear_correction( + this.raw.linear_correction( this.params.dt, this.bodies.raw, body.handle, rawPos, rawVel, + scratchBuffer, ); rawPos.free(); rawVel.free(); - return VectorOps.fromRaw(correction); + return VectorOps.fromBuffer(scratchBuffer, target); } // #if DIM2 @@ -188,20 +190,22 @@ export class PidController { body: RigidBody, targetRotation: Rotation, targetAngVel: Vector, + target?: Vector, ): Vector { let rawPos = RotationOps.intoRaw(targetRotation); let rawVel = VectorOps.intoRaw(targetAngVel); - let correction = this.raw.angular_correction( + this.raw.angular_correction( this.params.dt, this.bodies.raw, body.handle, rawPos, rawVel, + scratchBuffer, ); rawPos.free(); rawVel.free(); - return VectorOps.fromRaw(correction); + return VectorOps.fromBuffer(scratchBuffer, target); } // #endif } diff --git a/src.ts/control/ray_cast_vehicle_controller.ts b/src.ts/control/ray_cast_vehicle_controller.ts index fa65b3e3..55e4b52f 100644 --- a/src.ts/control/ray_cast_vehicle_controller.ts +++ b/src.ts/control/ray_cast_vehicle_controller.ts @@ -1,5 +1,5 @@ import {RawDynamicRayCastVehicleController} from "../raw"; -import {Vector, VectorOps} from "../math"; +import {scratchBuffer, Vector, VectorOps} from "../math"; import { BroadPhase, Collider, @@ -163,11 +163,23 @@ export class DynamicRayCastVehicleController { /* * Getters + setters */ + /** * The position of the i-th wheel, relative to the chassis. - */ - public wheelChassisConnectionPointCs(i: number): Vector | null { - return VectorOps.fromRaw(this.raw.wheel_chassis_connection_point_cs(i)); + * + * @param {number} i + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public wheelChassisConnectionPointCs( + i: number, + target?: Vector, + ): Vector | null { + const exists = this.raw.wheel_chassis_connection_point_cs( + i, + scratchBuffer, + ); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } /** @@ -331,9 +343,14 @@ export class DynamicRayCastVehicleController { * The direction of the i-th wheel’s suspension, relative to the chassis. * * The ray-casting will happen following this direction to detect the ground. + * + * @param {number} i + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public wheelDirectionCs(i: number): Vector | null { - return VectorOps.fromRaw(this.raw.wheel_direction_cs(i)); + public wheelDirectionCs(i: number, target?: Vector): Vector | null { + const exists = this.raw.wheel_direction_cs(i, scratchBuffer); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } /** @@ -351,9 +368,14 @@ export class DynamicRayCastVehicleController { * The i-th wheel’s axle axis, relative to the chassis. * * The axis index defined as 0 = X, 1 = Y, 2 = Z. + * + * @param {number} i + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public wheelAxleCs(i: number): Vector | null { - return VectorOps.fromRaw(this.raw.wheel_axle_cs(i)); + public wheelAxleCs(i: number, target?: Vector): Vector | null { + const exists = this.raw.wheel_axle_cs(i, scratchBuffer); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } /** @@ -439,16 +461,26 @@ export class DynamicRayCastVehicleController { /** * The (world-space) contact normal between the i-th wheel and the floor. + * + * @param {number} i + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public wheelContactNormal(i: number): Vector | null { - return VectorOps.fromRaw(this.raw.wheel_contact_normal_ws(i)); + public wheelContactNormal(i: number, target?: Vector): Vector | null { + const exists = this.raw.wheel_contact_normal_ws(i, scratchBuffer); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } /** * The (world-space) point hit by the wheel’s ray-cast for the i-th wheel. + * + * @param {number} i + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public wheelContactPoint(i: number): Vector | null { - return VectorOps.fromRaw(this.raw.wheel_contact_point_ws(i)); + public wheelContactPoint(i: number, target?: Vector): Vector | null { + const exists = this.raw.wheel_contact_point_ws(i, scratchBuffer); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } /** @@ -460,9 +492,14 @@ export class DynamicRayCastVehicleController { /** * The (world-space) starting point of the ray-cast for the i-th wheel. + * + * @param {number} i + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public wheelHardPoint(i: number): Vector | null { - return VectorOps.fromRaw(this.raw.wheel_hard_point_ws(i)); + public wheelHardPoint(i: number, target?: Vector): Vector | null { + const exists = this.raw.wheel_hard_point_ws(i, scratchBuffer); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } /** diff --git a/src.ts/dynamics/impulse_joint.ts b/src.ts/dynamics/impulse_joint.ts index ab4ab411..68185efe 100644 --- a/src.ts/dynamics/impulse_joint.ts +++ b/src.ts/dynamics/impulse_joint.ts @@ -1,4 +1,4 @@ -import {Rotation, Vector, VectorOps, RotationOps} from "../math"; +import {Rotation, Vector, VectorOps, RotationOps, scratchBuffer} from "../math"; import { RawGenericJoint, RawImpulseJointSet, @@ -149,9 +149,13 @@ export class ImpulseJoint { // #if DIM3 /** * The rotation quaternion that aligns this joint's first local axis to the `x` axis. + * + * @param {Rotation?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public frameX1(): Rotation { - return RotationOps.fromRaw(this.rawSet.jointFrameX1(this.handle)); + public frameX1(target?: Rotation): Rotation { + this.rawSet.jointFrameX1(this.handle, scratchBuffer); + return RotationOps.fromBuffer(scratchBuffer, target); } // #endif @@ -159,9 +163,13 @@ export class ImpulseJoint { // #if DIM3 /** * The rotation matrix that aligns this joint's second local axis to the `x` axis. + * + * @param {Rotation?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public frameX2(): Rotation { - return RotationOps.fromRaw(this.rawSet.jointFrameX2(this.handle)); + public frameX2(target?: Rotation): Rotation { + this.rawSet.jointFrameX2(this.handle, scratchBuffer); + return RotationOps.fromBuffer(scratchBuffer, target); } // #endif @@ -171,9 +179,13 @@ export class ImpulseJoint { * * The first anchor gives the position of the application point on the * local frame of the first rigid-body it is attached to. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public anchor1(): Vector { - return VectorOps.fromRaw(this.rawSet.jointAnchor1(this.handle)); + public anchor1(target?: Vector): Vector { + this.rawSet.jointAnchor1(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** @@ -181,9 +193,13 @@ export class ImpulseJoint { * * The second anchor gives the position of the application point on the * local frame of the second rigid-body it is attached to. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. */ - public anchor2(): Vector { - return VectorOps.fromRaw(this.rawSet.jointAnchor2(this.handle)); + public anchor2(target?: Vector): Vector { + this.rawSet.jointAnchor2(this.handle, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** diff --git a/src.ts/dynamics/rigid_body.ts b/src.ts/dynamics/rigid_body.ts index 8f52dbe5..a4e07cb1 100644 --- a/src.ts/dynamics/rigid_body.ts +++ b/src.ts/dynamics/rigid_body.ts @@ -1,5 +1,5 @@ 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 @@ -293,19 +293,36 @@ export class RigidBody { /** * 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(): Vector { - let res = this.rawSet.rbTranslation(this.handle); - return VectorOps.fromRaw(res); + 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(): Rotation { - let res = this.rawSet.rbRotation(this.handle); - return RotationOps.fromRaw(res); + 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. @@ -313,23 +330,44 @@ export class RigidBody { * 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(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. + * + * 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. */ - public nextTranslation(): Vector { - let res = this.rawSet.rbNextTranslation(this.handle); - return VectorOps.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(): Rotation { - let res = this.rawSet.rbNextRotation(this.handle); - return RotationOps.fromRaw(res); + 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 +540,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 +582,6 @@ export class RigidBody { public angvel(): number { return this.rawSet.rbAngvel(this.handle); } - // #endif /** @@ -548,9 +593,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 +613,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 +642,6 @@ export class RigidBody { public invPrincipalInertia(): number { return this.rawSet.rbInvPrincipalInertia(this.handle); } - // #endif // #if DIM3 @@ -593,13 +649,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 +666,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 +702,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, * 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 +734,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 +1147,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 +1170,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..7101ec3f 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"; @@ -111,10 +111,11 @@ export class BroadPhase { filterExcludeCollider?: ColliderHandle, filterExcludeRigidBody?: RigidBodyHandle, filterPredicate?: (collider: ColliderHandle) => boolean, + target?: RayColliderIntersection, ): RayColliderIntersection | null { let rawOrig = VectorOps.intoRaw(ray.origin); let rawDir = VectorOps.intoRaw(ray.dir); - let result = RayColliderIntersection.fromRaw( + let result = RayColliderIntersection.fromBuffer( colliders, this.raw.castRayAndGetNormal( narrowPhase.raw, @@ -130,6 +131,7 @@ export class BroadPhase { filterExcludeRigidBody, filterPredicate, ), + target, ); rawOrig.free(); @@ -170,7 +172,7 @@ export class BroadPhase { let rawDir = VectorOps.intoRaw(ray.dir); let rawCallback = (rawInter: RawRayColliderIntersection) => { return callback( - RayColliderIntersection.fromRaw(colliders, rawInter), + RayColliderIntersection.fromBuffer(colliders, rawInter), ); }; @@ -265,9 +267,10 @@ export class BroadPhase { filterExcludeCollider?: ColliderHandle, filterExcludeRigidBody?: RigidBodyHandle, filterPredicate?: (collider: ColliderHandle) => boolean, + target?: PointColliderProjection, ): PointColliderProjection | null { let rawPoint = VectorOps.intoRaw(point); - let result = PointColliderProjection.fromRaw( + let result = PointColliderProjection.fromBuffer( colliders, this.raw.projectPoint( narrowPhase.raw, @@ -281,6 +284,7 @@ export class BroadPhase { filterExcludeRigidBody, filterPredicate, ), + target, ); rawPoint.free(); @@ -306,9 +310,10 @@ export class BroadPhase { filterExcludeCollider?: ColliderHandle, filterExcludeRigidBody?: RigidBodyHandle, filterPredicate?: (collider: ColliderHandle) => boolean, + target?: PointColliderProjection, ): PointColliderProjection | null { let rawPoint = VectorOps.intoRaw(point); - let result = PointColliderProjection.fromRaw( + let result = PointColliderProjection.fromBuffer( colliders, this.raw.projectPointAndGetFeature( narrowPhase.raw, @@ -321,6 +326,7 @@ export class BroadPhase { filterExcludeRigidBody, filterPredicate, ), + target, ); rawPoint.free(); @@ -387,6 +393,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 +412,44 @@ 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, ); + let result = null; + if (rawColliderShapeCastHit) { + const colliderHandle: number = + rawColliderShapeCastHit.colliderHandle(); + rawColliderShapeCastHit.getComponents(scratchBuffer); + 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..cc6e9034 100644 --- a/src.ts/geometry/collider.ts +++ b/src.ts/geometry/collider.ts @@ -1,11 +1,5 @@ -import {RawColliderSet} from "../raw"; -import {Rotation, RotationOps, Vector, VectorOps} from "../math"; -import { - CoefficientCombineRule, - RigidBody, - RigidBodyHandle, - RigidBodySet, -} from "../dynamics"; +import {Rotation, RotationOps, Vector, VectorOps, scratchBuffer} from "../math"; +import {CoefficientCombineRule, RigidBody, RigidBodySet} from "../dynamics"; import {ActiveHooks, ActiveEvents} from "../pipeline"; import {InteractionGroups} from "./interaction_groups"; import { @@ -169,43 +163,82 @@ 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 +655,16 @@ 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 +880,18 @@ 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 @@ -950,10 +996,15 @@ export class Collider { * (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 { + public projectPoint( + point: Vector, + solid: boolean, + target?: PointProjection, + ): PointProjection | null { let rawPoint = VectorOps.intoRaw(point); - let result = PointProjection.fromRaw( + let result = PointProjection.fromBuffer( this.colliderSet.raw.coProjectPoint(this.handle, rawPoint, solid), + target, ); rawPoint.free(); @@ -984,7 +1035,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 +1050,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 +1062,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 +1070,25 @@ 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, ); + let result = null; + if (rawShapeCastHit) { + rawShapeCastHit.getComponents(scratchBuffer); + result = ShapeCastHit.fromBuffer(null, scratchBuffer, target); + rawShapeCastHit.free(); + } + rawCollider1Vel.free(); rawShape2Pos.free(); rawShape2Rot.free(); @@ -1040,7 +1098,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 +1111,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 +1121,34 @@ 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, ); + let result = null; + if (rawColliderShapeCastHit) { + const colliderHandle: number = + rawColliderShapeCastHit.colliderHandle(); + rawColliderShapeCastHit.getComponents(scratchBuffer); + result = ColliderShapeCastHit.fromBuffer( + this.colliderSet.get(colliderHandle), + scratchBuffer, + target, + ); + rawColliderShapeCastHit.free(); + } + rawCollider1Vel.free(); rawCollider2Vel.free(); @@ -1121,12 +1192,13 @@ export class Collider { shape2Pos: Vector, shape2Rot: Rotation, prediction: number, + target?: ShapeContact, ): ShapeContact | null { let rawPos2 = VectorOps.intoRaw(shape2Pos); let rawRot2 = RotationOps.intoRaw(shape2Rot); let rawShape2 = shape2.intoRaw(); - let result = ShapeContact.fromRaw( + let result = ShapeContact.fromBuffer( this.colliderSet.raw.coContactShape( this.handle, rawShape2, @@ -1134,6 +1206,7 @@ export class Collider { rawRot2, prediction, ), + target, ); rawPos2.free(); @@ -1153,13 +1226,15 @@ export class Collider { contactCollider( collider2: Collider, prediction: number, + target?: ShapeContact, ): ShapeContact | null { - let result = ShapeContact.fromRaw( + let result = ShapeContact.fromBuffer( this.colliderSet.raw.coContactCollider( this.handle, collider2.handle, prediction, ), + target, ); return result; @@ -1209,10 +1284,11 @@ export class Collider { ray: Ray, maxToi: number, solid: boolean, + target?: RayIntersection, ): RayIntersection | null { let rawOrig = VectorOps.intoRaw(ray.origin); let rawDir = VectorOps.intoRaw(ray.dir); - let result = RayIntersection.fromRaw( + let result = RayIntersection.fromBuffer( this.colliderSet.raw.coCastRayAndGetNormal( this.handle, rawOrig, @@ -1220,6 +1296,7 @@ export class Collider { maxToi, solid, ), + target, ); rawOrig.free(); diff --git a/src.ts/geometry/contact.ts b/src.ts/geometry/contact.ts index 802f57f4..dc76b557 100644 --- a/src.ts/geometry/contact.ts +++ b/src.ts/geometry/contact.ts @@ -1,4 +1,4 @@ -import {Vector, VectorOps} from "../math"; +import {Vector, VectorOps, scratchBuffer} from "../math"; import {RawShapeContact} from "../raw"; /** @@ -46,17 +46,56 @@ export class ShapeContact { this.normal2 = normal2; } - public static fromRaw(raw: RawShapeContact): ShapeContact { + /** + * @param raw - The raw contact returned by the WASM query. It is freed by this method. + * @param target - If provided, this object is populated and returned instead of + * allocating a new one. + */ + public static fromBuffer( + raw: RawShapeContact, + target?: ShapeContact, + ): ShapeContact { if (!raw) return null; - const result = new ShapeContact( - raw.distance(), - VectorOps.fromRaw(raw.point1()), - VectorOps.fromRaw(raw.point2()), - VectorOps.fromRaw(raw.normal1()), - VectorOps.fromRaw(raw.normal2()), - ); + raw.getComponents(scratchBuffer); raw.free(); - return result; + + target ??= new ShapeContact( + 0, + VectorOps.zeros(), + VectorOps.zeros(), + VectorOps.zeros(), + VectorOps.zeros(), + ); + + target.distance = scratchBuffer[0]; + + // #if DIM2 + target.point1.x = scratchBuffer[1]; + target.point1.y = scratchBuffer[2]; + target.point2.x = scratchBuffer[3]; + target.point2.y = scratchBuffer[4]; + target.normal1.x = scratchBuffer[5]; + target.normal1.y = scratchBuffer[6]; + target.normal2.x = scratchBuffer[7]; + target.normal2.y = scratchBuffer[8]; + // #endif + + // #if DIM3 + target.point1.x = scratchBuffer[1]; + target.point1.y = scratchBuffer[2]; + target.point1.z = scratchBuffer[3]; + target.point2.x = scratchBuffer[4]; + target.point2.y = scratchBuffer[5]; + target.point2.z = scratchBuffer[6]; + target.normal1.x = scratchBuffer[7]; + target.normal1.y = scratchBuffer[8]; + target.normal1.z = scratchBuffer[9]; + target.normal2.x = scratchBuffer[10]; + target.normal2.y = scratchBuffer[11]; + target.normal2.z = scratchBuffer[12]; + // #endif + + return target; } } diff --git a/src.ts/geometry/narrow_phase.ts b/src.ts/geometry/narrow_phase.ts index af04a227..c5d4974c 100644 --- a/src.ts/geometry/narrow_phase.ts +++ b/src.ts/geometry/narrow_phase.ts @@ -1,6 +1,6 @@ import {RawNarrowPhase, RawContactManifold} from "../raw"; import {ColliderHandle} from "./collider"; -import {Vector, VectorOps} from "../math"; +import {Vector, VectorOps, scratchBuffer} from "../math"; /** * The narrow-phase used for precise collision-detection. @@ -113,16 +113,37 @@ export class TempContactManifold { this.raw = raw; } - public normal(): Vector { - return VectorOps.fromRaw(this.raw.normal()); + /** + * The contact normal of the manifold, expressed in world-space. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public normal(target?: Vector): Vector { + this.raw.normal(scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } - public localNormal1(): Vector { - return VectorOps.fromRaw(this.raw.local_n1()); + /** + * The contact normal of the manifold, expressed in the local-space of the first shape. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public localNormal1(target?: Vector): Vector { + this.raw.local_n1(scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } - public localNormal2(): Vector { - return VectorOps.fromRaw(this.raw.local_n2()); + /** + * The contact normal of the manifold, expressed in the local-space of the second shape. + * + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public localNormal2(target?: Vector): Vector { + this.raw.local_n2(scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } public subshape1(): number { @@ -137,12 +158,28 @@ export class TempContactManifold { return this.raw.num_contacts(); } - public localContactPoint1(i: number): Vector | null { - return VectorOps.fromRaw(this.raw.contact_local_p1(i)); + /** + * The local-space contact point on the first shape, for the `i`-th contact. + * + * @param {number} i - The index of the contact to read. + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public localContactPoint1(i: number, target?: Vector): Vector | null { + const exists = this.raw.contact_local_p1(i, scratchBuffer); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } - public localContactPoint2(i: number): Vector | null { - return VectorOps.fromRaw(this.raw.contact_local_p2(i)); + /** + * The local-space contact point on the second shape, for the `i`-th contact. + * + * @param {number} i - The index of the contact to read. + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public localContactPoint2(i: number, target?: Vector): Vector | null { + const exists = this.raw.contact_local_p2(i, scratchBuffer); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } public contactDist(i: number): number { @@ -181,8 +218,16 @@ export class TempContactManifold { return this.raw.num_solver_contacts(); } - public solverContactPoint(i: number): Vector { - return VectorOps.fromRaw(this.raw.solver_contact_point(i)); + /** + * The world-space position of the `i`-th solver contact point. + * + * @param {number} i - The index of the solver contact to read. + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public solverContactPoint(i: number, target?: Vector): Vector | null { + const exists = this.raw.solver_contact_point(i, scratchBuffer); + return exists ? VectorOps.fromBuffer(scratchBuffer, target) : null; } public solverContactDist(i: number): number { @@ -197,7 +242,15 @@ export class TempContactManifold { return this.raw.solver_contact_restitution(i); } - public solverContactTangentVelocity(i: number): Vector { - return VectorOps.fromRaw(this.raw.solver_contact_tangent_velocity(i)); + /** + * The tangent (surface) velocity of the `i`-th solver contact point. + * + * @param {number} i - The index of the solver contact to read. + * @param {Vector?} target - The object to be populated. If provided, + * the function returns this object instead of creating a new one. + */ + public solverContactTangentVelocity(i: number, target?: Vector): Vector { + this.raw.solver_contact_tangent_velocity(i, scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } } diff --git a/src.ts/geometry/point.ts b/src.ts/geometry/point.ts index d272b7e8..ec1335c1 100644 --- a/src.ts/geometry/point.ts +++ b/src.ts/geometry/point.ts @@ -1,5 +1,5 @@ import {Collider, ColliderHandle} from "./collider"; -import {Vector, VectorOps} from "../math"; +import {Vector, VectorOps, scratchBuffer} from "../math"; import { RawFeatureType, RawPointColliderProjection, @@ -26,15 +26,25 @@ export class PointProjection { this.isInside = isInside; } - public static fromRaw(raw: RawPointProjection): PointProjection { + /** + * @param raw - The raw projection returned by the WASM query. It is freed by this method. + * @param target - If provided, this object is populated and returned instead of + * allocating a new one. + */ + public static fromBuffer( + raw: RawPointProjection, + target?: PointProjection, + ): PointProjection { if (!raw) return null; - const result = new PointProjection( - VectorOps.fromRaw(raw.point()), - raw.isInside(), - ); + raw.point(scratchBuffer); + + target ??= new PointProjection(VectorOps.zeros(), false); + target.point = VectorOps.fromBuffer(scratchBuffer, target.point); + target.isInside = raw.isInside(); + raw.free(); - return result; + return target; } } @@ -79,20 +89,29 @@ export class PointColliderProjection { if (featureType !== undefined) this.featureType = featureType; } - public static fromRaw( + /** + * @param colliderSet - The set the projected-on collider belongs to. + * @param raw - The raw projection returned by the WASM query. It is freed by this method. + * @param target - If provided, this object is populated and returned instead of + * allocating a new one. + */ + public static fromBuffer( colliderSet: ColliderSet, raw: RawPointColliderProjection, + target?: PointColliderProjection, ): PointColliderProjection { if (!raw) return null; - const result = new PointColliderProjection( - colliderSet.get(raw.colliderHandle()), - VectorOps.fromRaw(raw.point()), - raw.isInside(), - raw.featureType() as number as FeatureType, - raw.featureId(), - ); + raw.point(scratchBuffer); + + target ??= new PointColliderProjection(null, VectorOps.zeros(), false); + target.collider = colliderSet.get(raw.colliderHandle()); + target.point = VectorOps.fromBuffer(scratchBuffer, target.point); + target.isInside = raw.isInside(); + target.featureType = raw.featureType() as number as FeatureType; + target.featureId = raw.featureId(); + raw.free(); - return result; + return target; } } diff --git a/src.ts/geometry/ray.ts b/src.ts/geometry/ray.ts index 36df5ee3..cd035609 100644 --- a/src.ts/geometry/ray.ts +++ b/src.ts/geometry/ray.ts @@ -1,4 +1,4 @@ -import {Vector, VectorOps} from "../math"; +import {Vector, VectorOps, scratchBuffer} from "../math"; import { RawFeatureType, RawRayColliderIntersection, @@ -81,17 +81,27 @@ export class RayIntersection { if (featureType !== undefined) this.featureType = featureType; } - public static fromRaw(raw: RawRayIntersection): RayIntersection { + /** + * @param raw - The raw intersection returned by the WASM query. It is freed by this method. + * @param target - If provided, this object is populated and returned instead of + * allocating a new one. + */ + public static fromBuffer( + raw: RawRayIntersection, + target?: RayIntersection, + ): RayIntersection { if (!raw) return null; - const result = new RayIntersection( - raw.time_of_impact(), - VectorOps.fromRaw(raw.normal()), - raw.featureType() as number as FeatureType, - raw.featureId(), - ); + raw.normal(scratchBuffer); + + target ??= new RayIntersection(0, VectorOps.zeros()); + target.timeOfImpact = raw.time_of_impact(); + target.normal = VectorOps.fromBuffer(scratchBuffer, target.normal); + target.featureType = raw.featureType() as number as FeatureType; + target.featureId = raw.featureId(); + raw.free(); - return result; + return target; } } @@ -138,21 +148,30 @@ export class RayColliderIntersection { if (featureType !== undefined) this.featureType = featureType; } - public static fromRaw( + /** + * @param colliderSet - The set the hit collider belongs to. + * @param raw - The raw intersection returned by the WASM query. It is freed by this method. + * @param target - If provided, this object is populated and returned instead of + * allocating a new one. + */ + public static fromBuffer( colliderSet: ColliderSet, raw: RawRayColliderIntersection, + target?: RayColliderIntersection, ): RayColliderIntersection { if (!raw) return null; - const result = new RayColliderIntersection( - colliderSet.get(raw.colliderHandle()), - raw.time_of_impact(), - VectorOps.fromRaw(raw.normal()), - raw.featureType() as number as FeatureType, - raw.featureId(), - ); + raw.normal(scratchBuffer); + + target ??= new RayColliderIntersection(null, 0, VectorOps.zeros()); + target.collider = colliderSet.get(raw.colliderHandle()); + target.timeOfImpact = raw.time_of_impact(); + target.normal = VectorOps.fromBuffer(scratchBuffer, target.normal); + target.featureType = raw.featureType() as number as FeatureType; + target.featureId = raw.featureId(); + raw.free(); - return result; + return target; } } diff --git a/src.ts/geometry/shape.ts b/src.ts/geometry/shape.ts index f3546982..bf4809ff 100644 --- a/src.ts/geometry/shape.ts +++ b/src.ts/geometry/shape.ts @@ -1,4 +1,4 @@ -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 +23,6 @@ export abstract class Shape { ): Shape { const rawType = rawSet.coShapeType(handle); - let extents: Vector; let borderRadius: number; let vs: Float32Array; let indices: Uint32Array; @@ -35,28 +34,37 @@ 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); + 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 @@ -128,7 +136,8 @@ export abstract class Shape { // #endif case RawShapeType.HalfSpace: - normal = VectorOps.fromRaw(rawSet.coHalfspaceNormal(handle)); + rawSet.coHalfspaceNormal(handle, scratchBuffer); + normal = VectorOps.fromBuffer(scratchBuffer); return new HalfSpace(normal); case RawShapeType.Voxels: @@ -143,14 +152,23 @@ 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); @@ -204,7 +222,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 +235,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 +252,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 +264,26 @@ export abstract class Shape { let rawShape1 = this.intoRaw(); let rawShape2 = shape2.intoRaw(); - let result = ShapeCastHit.fromRaw( - null, - rawShape1.castShape( - rawPos1, - rawRot1, - rawVel1, - rawShape2, - rawPos2, - rawRot2, - rawVel2, - targetDistance, - maxToi, - stopAtPenetration, - ), + const rawShapeCastHit = rawShape1.castShape( + rawPos1, + rawRot1, + rawVel1, + rawShape2, + rawPos2, + rawRot2, + rawVel2, + targetDistance, + maxToi, + stopAtPenetration, ); + let result = null; + if (rawShapeCastHit) { + rawShapeCastHit.getComponents(scratchBuffer); + result = ShapeCastHit.fromBuffer(null, scratchBuffer, target); + rawShapeCastHit.free(); + } + rawPos1.free(); rawRot1.free(); rawVel1.free(); @@ -334,6 +359,7 @@ export abstract class Shape { shapePos2: Vector, shapeRot2: Rotation, prediction: number, + target?: ShapeContact, ): ShapeContact | null { let rawPos1 = VectorOps.intoRaw(shapePos1); let rawRot1 = RotationOps.intoRaw(shapeRot1); @@ -343,7 +369,7 @@ export abstract class Shape { let rawShape1 = this.intoRaw(); let rawShape2 = shape2.intoRaw(); - let result = ShapeContact.fromRaw( + let result = ShapeContact.fromBuffer( rawShape1.contactShape( rawPos1, rawRot1, @@ -352,6 +378,7 @@ export abstract class Shape { rawRot2, prediction, ), + target, ); rawPos1.free(); @@ -390,14 +417,16 @@ export abstract class Shape { shapeRot: Rotation, point: Vector, solid: boolean, + target?: PointProjection, ): PointProjection { let rawPos = VectorOps.intoRaw(shapePos); let rawRot = RotationOps.intoRaw(shapeRot); let rawPoint = VectorOps.intoRaw(point); let rawShape = this.intoRaw(); - let result = PointProjection.fromRaw( + let result = PointProjection.fromBuffer( rawShape.projectPoint(rawPos, rawRot, rawPoint, solid), + target, ); rawPos.free(); @@ -474,6 +503,7 @@ export abstract class Shape { shapeRot: Rotation, maxToi: number, solid: boolean, + target?: RayIntersection, ): RayIntersection { let rawPos = VectorOps.intoRaw(shapePos); let rawRot = RotationOps.intoRaw(shapeRot); @@ -481,7 +511,7 @@ export abstract class Shape { let rawRayDir = VectorOps.intoRaw(ray.dir); let rawShape = this.intoRaw(); - let result = RayIntersection.fromRaw( + let result = RayIntersection.fromBuffer( rawShape.castRayAndGetNormal( rawPos, rawRot, @@ -490,6 +520,7 @@ export abstract class Shape { maxToi, solid, ), + target, ); rawPos.free(); diff --git a/src.ts/geometry/toi.ts b/src.ts/geometry/toi.ts index ade13f17..013c4be3 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..86eb9e5a 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,20 @@ 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 +289,22 @@ 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.ts/pipeline/event_queue.ts b/src.ts/pipeline/event_queue.ts index e39c1582..1269b7a8 100644 --- a/src.ts/pipeline/event_queue.ts +++ b/src.ts/pipeline/event_queue.ts @@ -1,7 +1,7 @@ import {RawContactForceEvent, RawEventQueue} from "../raw"; import {RigidBodyHandle} from "../dynamics"; import {Collider, ColliderHandle} from "../geometry"; -import {Vector, VectorOps} from "../math"; +import {Vector, VectorOps, scratchBuffer} from "../math"; /** * Flags indicating what events are enabled for colliders. @@ -52,8 +52,9 @@ export class TempContactForceEvent { /** * The sum of all the forces between the two colliders. */ - public totalForce(): Vector { - return VectorOps.fromRaw(this.raw.total_force()); + public totalForce(target?: Vector): Vector { + this.raw.total_force(scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** @@ -70,8 +71,9 @@ export class TempContactForceEvent { /** * The world-space (unit) direction of the force with strongest magnitude. */ - public maxForceDirection(): Vector { - return VectorOps.fromRaw(this.raw.max_force_direction()); + public maxForceDirection(target?: Vector): Vector { + this.raw.max_force_direction(scratchBuffer); + return VectorOps.fromBuffer(scratchBuffer, target); } /** diff --git a/src/control/character_controller.rs b/src/control/character_controller.rs index 03cbdc5d..3a7e7589 100644 --- a/src/control/character_controller.rs +++ b/src/control/character_controller.rs @@ -212,8 +212,19 @@ impl RawKinematicCharacterController { } } - pub fn computedMovement(&self) -> RawVector { - self.result.translation.into() + #[cfg(feature = "dim2")] + pub fn computedMovement(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.result.translation; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + #[cfg(feature = "dim3")] + pub fn computedMovement(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.result.translation; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } pub fn computedGrounded(&self) -> bool { @@ -260,31 +271,97 @@ impl RawCharacterCollision { utils::flat_handle(self.0.handle.0) } - pub fn translationDeltaApplied(&self) -> RawVector { - self.0.translation_applied.into() + #[cfg(feature = "dim2")] + pub fn translationDeltaApplied(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.translation_applied; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + #[cfg(feature = "dim3")] + pub fn translationDeltaApplied(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.translation_applied; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } - pub fn translationDeltaRemaining(&self) -> RawVector { - self.0.translation_remaining.into() + #[cfg(feature = "dim2")] + pub fn translationDeltaRemaining(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.translation_remaining; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + #[cfg(feature = "dim3")] + pub fn translationDeltaRemaining(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.translation_remaining; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } pub fn toi(&self) -> Real { self.0.hit.time_of_impact } - pub fn worldWitness1(&self) -> RawVector { - self.0.hit.witness1.coords.into() // Already in world-space. + #[cfg(feature = "dim2")] + pub fn worldWitness1(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.hit.witness1.coords; // Already in world-space. + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + #[cfg(feature = "dim3")] + pub fn worldWitness1(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.hit.witness1.coords; // Already in world-space. + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + } + + #[cfg(feature = "dim2")] + pub fn worldWitness2(&self, scratch_buffer: &js_sys::Float32Array) { + let u = (self.0.character_pos * self.0.hit.witness2).coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + #[cfg(feature = "dim3")] + pub fn worldWitness2(&self, scratch_buffer: &js_sys::Float32Array) { + let u = (self.0.character_pos * self.0.hit.witness2).coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + } + + #[cfg(feature = "dim2")] + pub fn worldNormal1(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.hit.normal1.into_inner(); // Already in world-space. + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); } - pub fn worldWitness2(&self) -> RawVector { - (self.0.character_pos * self.0.hit.witness2).coords.into() + #[cfg(feature = "dim3")] + pub fn worldNormal1(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.hit.normal1.into_inner(); // Already in world-space. + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } - pub fn worldNormal1(&self) -> RawVector { - self.0.hit.normal1.into_inner().into() // Already in world-space. + #[cfg(feature = "dim2")] + pub fn worldNormal2(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.character_pos * self.0.hit.normal2.into_inner(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); } - pub fn worldNormal2(&self) -> RawVector { - (self.0.character_pos * self.0.hit.normal2.into_inner()).into() + #[cfg(feature = "dim3")] + pub fn worldNormal2(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.character_pos * self.0.hit.normal2.into_inner(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } } diff --git a/src/control/pid_controller.rs b/src/control/pid_controller.rs index 890e3391..e60f3331 100644 --- a/src/control/pid_controller.rs +++ b/src/control/pid_controller.rs @@ -210,15 +210,27 @@ impl RawPidController { rb_handle: FlatHandle, target_translation: &RawVector, target_linvel: &RawVector, - ) -> RawVector { + scratch_buffer: &js_sys::Float32Array, + ) { let rb_handle = crate::utils::body_handle(rb_handle); let Some(rb) = bodies.0.get(rb_handle) else { - return RawVector(Vector::zeros()); + scratch_buffer.set_index(0, 0.0); + scratch_buffer.set_index(1, 0.0); + #[cfg(feature = "dim3")] + scratch_buffer.set_index(2, 0.0); + return; }; - self.controller - .linear_rigid_body_correction(dt, rb, target_translation.0.into(), target_linvel.0) - .into() + let u: Vector = self.controller.linear_rigid_body_correction( + dt, + rb, + target_translation.0.into(), + target_linvel.0, + ); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + #[cfg(feature = "dim3")] + scratch_buffer.set_index(2, u.z); } #[cfg(feature = "dim2")] @@ -251,14 +263,24 @@ impl RawPidController { rb_handle: FlatHandle, target_rotation: &RawRotation, target_angvel: &RawVector, - ) -> RawVector { + scratch_buffer: &js_sys::Float32Array, + ) { let rb_handle = crate::utils::body_handle(rb_handle); let Some(rb) = bodies.0.get(rb_handle) else { - return RawVector(Vector::zeros()); + scratch_buffer.set_index(0, 0.0); + scratch_buffer.set_index(1, 0.0); + scratch_buffer.set_index(2, 0.0); + return; }; - self.controller - .angular_rigid_body_correction(dt, rb, target_rotation.0, target_angvel.0) - .into() + let u: Vector = self.controller.angular_rigid_body_correction( + dt, + rb, + target_rotation.0, + target_angvel.0, + ); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } } diff --git a/src/control/ray_cast_vehicle_controller.rs b/src/control/ray_cast_vehicle_controller.rs index 36bd7f61..445037ce 100644 --- a/src/control/ray_cast_vehicle_controller.rs +++ b/src/control/ray_cast_vehicle_controller.rs @@ -105,12 +105,35 @@ impl RawDynamicRayCastVehicleController { /* * Getters + setters */ - pub fn wheel_chassis_connection_point_cs(&self, i: usize) -> Option { - self.controller - .wheels() - .get(i) - .map(|w| w.chassis_connection_point_cs.into()) + #[cfg(feature = "dim2")] + pub fn wheel_chassis_connection_point_cs( + &self, + i: usize, + scratch_buffer: &js_sys::Float32Array, + ) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.chassis_connection_point_cs; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } + + #[cfg(feature = "dim3")] + pub fn wheel_chassis_connection_point_cs( + &self, + i: usize, + scratch_buffer: &js_sys::Float32Array, + ) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.chassis_connection_point_cs; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) } + pub fn set_wheel_chassis_connection_point_cs(&mut self, i: usize, value: &RawVector) { if let Some(wheel) = self.controller.wheels_mut().get_mut(i) { wheel.chassis_connection_point_cs = value.0.into(); @@ -225,21 +248,54 @@ impl RawDynamicRayCastVehicleController { } } - pub fn wheel_direction_cs(&self, i: usize) -> Option { - self.controller - .wheels() - .get(i) - .map(|w| w.direction_cs.into()) + #[cfg(feature = "dim2")] + pub fn wheel_direction_cs(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.direction_cs; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } + + #[cfg(feature = "dim3")] + pub fn wheel_direction_cs(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.direction_cs; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) } + pub fn set_wheel_direction_cs(&mut self, i: usize, value: &RawVector) { if let Some(wheel) = self.controller.wheels_mut().get_mut(i) { wheel.direction_cs = value.0; } } - pub fn wheel_axle_cs(&self, i: usize) -> Option { - self.controller.wheels().get(i).map(|w| w.axle_cs.into()) + #[cfg(feature = "dim2")] + pub fn wheel_axle_cs(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.axle_cs; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } + + #[cfg(feature = "dim3")] + pub fn wheel_axle_cs(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.axle_cs; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) } + pub fn set_wheel_axle_cs(&mut self, i: usize, value: &RawVector) { if let Some(wheel) = self.controller.wheels_mut().get_mut(i) { wheel.axle_cs = value.0; @@ -290,18 +346,46 @@ impl RawDynamicRayCastVehicleController { .map(|w| w.wheel_suspension_force) } - pub fn wheel_contact_normal_ws(&self, i: usize) -> Option { - self.controller - .wheels() - .get(i) - .map(|w| w.raycast_info().contact_normal_ws.into()) - } - - pub fn wheel_contact_point_ws(&self, i: usize) -> Option { - self.controller - .wheels() - .get(i) - .map(|w| w.raycast_info().contact_point_ws.into()) + #[cfg(feature = "dim2")] + pub fn wheel_contact_normal_ws(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.raycast_info().contact_normal_ws; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } + + #[cfg(feature = "dim3")] + pub fn wheel_contact_normal_ws(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.raycast_info().contact_normal_ws; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) + } + + #[cfg(feature = "dim2")] + pub fn wheel_contact_point_ws(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.raycast_info().contact_point_ws; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } + + #[cfg(feature = "dim3")] + pub fn wheel_contact_point_ws(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.raycast_info().contact_point_ws; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) } pub fn wheel_suspension_length(&self, i: usize) -> Option { @@ -311,11 +395,25 @@ impl RawDynamicRayCastVehicleController { .map(|w| w.raycast_info().suspension_length) } - pub fn wheel_hard_point_ws(&self, i: usize) -> Option { - self.controller - .wheels() - .get(i) - .map(|w| w.raycast_info().hard_point_ws.into()) + #[cfg(feature = "dim2")] + pub fn wheel_hard_point_ws(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.raycast_info().hard_point_ws; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } + + #[cfg(feature = "dim3")] + pub fn wheel_hard_point_ws(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + self.controller.wheels().get(i).map_or(false, |w| { + let u = w.raycast_info().hard_point_ws; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) } pub fn wheel_is_in_contact(&self, i: usize) -> bool { diff --git a/src/dynamics/impulse_joint.rs b/src/dynamics/impulse_joint.rs index 8e4eec51..2d2ef8c2 100644 --- a/src/dynamics/impulse_joint.rs +++ b/src/dynamics/impulse_joint.rs @@ -23,29 +23,81 @@ impl RawImpulseJointSet { } /// The angular part of the joint’s local frame relative to the first rigid-body it is attached to. - pub fn jointFrameX1(&self, handle: FlatHandle) -> RawRotation { - self.map(handle, |j| j.data.local_frame1.rotation.into()) + #[cfg(feature = "dim3")] + pub fn jointFrameX1(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |j| { + let u = j.data.local_frame1.rotation.into_inner(); + scratch_buffer.set_index(0, u.i); + scratch_buffer.set_index(1, u.j); + scratch_buffer.set_index(2, u.k); + scratch_buffer.set_index(3, u.w); + }); } /// The angular part of the joint’s local frame relative to the second rigid-body it is attached to. - pub fn jointFrameX2(&self, handle: FlatHandle) -> RawRotation { - self.map(handle, |j| j.data.local_frame2.rotation.into()) + #[cfg(feature = "dim3")] + pub fn jointFrameX2(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |j| { + let u = j.data.local_frame2.rotation.into_inner(); + scratch_buffer.set_index(0, u.i); + scratch_buffer.set_index(1, u.j); + scratch_buffer.set_index(2, u.k); + scratch_buffer.set_index(3, u.w); + }); } /// The position of the first anchor of this joint. /// /// The first anchor gives the position of the points application point on the /// local frame of the first rigid-body it is attached to. - pub fn jointAnchor1(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |j| j.data.local_frame1.translation.vector.into()) + #[cfg(feature = "dim2")] + pub fn jointAnchor1(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |j| { + let u = j.data.local_frame1.translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + }); + } + + /// The position of the second anchor of this joint. + /// + /// The second anchor gives the position of the points application point on the + /// local frame of the second rigid-body it is attached to. + #[cfg(feature = "dim3")] + pub fn jointAnchor1(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |j| { + let u = j.data.local_frame1.translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); + } + + /// The position of the second anchor of this joint. + /// + /// The second anchor gives the position of the points application point on the + /// local frame of the second rigid-body it is attached to. + #[cfg(feature = "dim2")] + pub fn jointAnchor2(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |j| { + let u = j.data.local_frame2.translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + }); } /// The position of the second anchor of this joint. /// /// The second anchor gives the position of the points application point on the /// local frame of the second rigid-body it is attached to. - pub fn jointAnchor2(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |j| j.data.local_frame2.translation.vector.into()) + #[cfg(feature = "dim3")] + pub fn jointAnchor2(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |j| { + let u = j.data.local_frame2.translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); } /// Sets the position of the first local anchor @@ -77,16 +129,28 @@ impl RawImpulseJointSet { } /// Sets the full local frame (anchor + rotation) for the first rigid-body attachment. - pub fn jointSetLocalFrame1(&mut self, handle: FlatHandle, anchor: &RawVector, rot: &RawRotation) { + pub fn jointSetLocalFrame1( + &mut self, + handle: FlatHandle, + anchor: &RawVector, + rot: &RawRotation, + ) { self.map_mut(handle, |j| { - j.data.set_local_frame1(Isometry::from_parts(anchor.0.into(), rot.0)); + j.data + .set_local_frame1(Isometry::from_parts(anchor.0.into(), rot.0)); }); } /// Sets the full local frame (anchor + rotation) for the second rigid-body attachment. - pub fn jointSetLocalFrame2(&mut self, handle: FlatHandle, anchor: &RawVector, rot: &RawRotation) { + pub fn jointSetLocalFrame2( + &mut self, + handle: FlatHandle, + anchor: &RawVector, + rot: &RawRotation, + ) { self.map_mut(handle, |j| { - j.data.set_local_frame2(Isometry::from_parts(anchor.0.into(), rot.0)); + j.data + .set_local_frame2(Isometry::from_parts(anchor.0.into(), rot.0)); }); } diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index d7174d77..88f4e36f 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -1,8 +1,8 @@ use crate::dynamics::{RawRigidBodySet, RawRigidBodyType}; use crate::geometry::RawColliderSet; #[cfg(feature = "dim3")] -use crate::math::RawSdpMatrix3; -use crate::math::{RawRotation, RawVector}; +use crate::math::RawRotation; +use crate::math::RawVector; use crate::utils::{self, FlatHandle}; use na::Point; use rapier::dynamics::MassProperties; @@ -11,13 +11,52 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] impl RawRigidBodySet { /// The world-space translation of this rigid-body. - pub fn rbTranslation(&self, handle: FlatHandle) -> RawVector { - self.map(handle, |rb| RawVector(rb.position().translation.vector)) + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbTranslation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.position().translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + }); + } + + /// The world-space translation of this rigid-body. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbTranslation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.position().translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.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. - pub fn rbRotation(&self, handle: FlatHandle) -> RawRotation { - self.map(handle, |rb| RawRotation(rb.position().rotation)) + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbRotation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.position().rotation; + let inner = u.into_inner(); + scratch_buffer.set_index(0, inner.i); + scratch_buffer.set_index(1, inner.j); + scratch_buffer.set_index(2, inner.k); + scratch_buffer.set_index(3, inner.w); + }); } /// Put the given rigid-body to sleep. @@ -40,10 +79,44 @@ 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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbNextTranslation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { self.map(handle, |rb| { - RawVector(rb.next_position().translation.vector) - }) + let u = rb.next_position().translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbNextTranslation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.next_position().translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); + } + + /// 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. + #[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. @@ -51,8 +124,19 @@ 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)) + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbNextRotation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.next_position().rotation; + let inner = u.into_inner(); + scratch_buffer.set_index(0, inner.i); + scratch_buffer.set_index(1, inner.j); + scratch_buffer.set_index(2, inner.k); + scratch_buffer.set_index(3, inner.w); + }); } /// Sets the translation of this rigid-body. @@ -282,8 +366,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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbLinvel(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.linvel(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + }); + } + + /// The linear velocity of this rigid-body. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbLinvel(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.linvel(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); } /// The angular velocity of this rigid-body. @@ -293,16 +399,54 @@ impl RawRigidBodySet { } /// The angular velocity of this rigid-body. + /// + /// # Parameters + /// - `scratch_buffer`: 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, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.angvel(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); } /// The velocity of the given world-space point on this rigid-body. - pub fn rbVelocityAtPoint(&self, handle: FlatHandle, point: &RawVector) -> RawVector { + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbVelocityAtPoint( + &self, + handle: FlatHandle, + point: &RawVector, + scratch_buffer: &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)); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + }); + } + + /// The velocity of the given world-space point on this rigid-body. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbVelocityAtPoint( + &self, + handle: FlatHandle, + point: &RawVector, + scratch_buffer: &js_sys::Float32Array, + ) { + self.map(handle, |rb| { + let u = rb.velocity_at_point(&Point::from(point.0)); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); } pub fn rbLockTranslations(&mut self, handle: FlatHandle, locked: bool, wake_up: bool) { @@ -383,20 +527,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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbEffectiveInvMass(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().effective_inv_mass; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + }); + } + + /// The inverse mass taking into account translation locking. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbEffectiveInvMass(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().effective_inv_mass; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbLocalCom(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { self.map(handle, |rb| { - rb.mass_properties().local_mprops.local_com.into() - }) + let u = rb.mass_properties().local_mprops.local_com; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + }); + } + + /// The center of mass of a rigid-body expressed in its local-space. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbLocalCom(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().local_mprops.local_com; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); + } + + /// The world-space center of mass of the rigid-body. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbWorldCom(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().world_com; + scratch_buffer.set_index(0, u.x); + scratch_buffer.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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbWorldCom(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.mass_properties().world_com; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); } /// The inverse of the principal angular inertia of the rigid-body. @@ -405,52 +613,69 @@ impl RawRigidBodySet { #[cfg(feature = "dim2")] pub fn rbInvPrincipalInertia(&self, handle: FlatHandle) -> f32 { self.map(handle, |rb| { - rb.mass_properties() - .local_mprops - .inv_principal_inertia - .into() + 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 + /// - `scratch_buffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbInvPrincipalInertia(&self, handle: FlatHandle) -> RawVector { + pub fn rbInvPrincipalInertia(&self, handle: FlatHandle, scratch_buffer: &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; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbPrincipalInertiaLocalFrame( + &self, + handle: FlatHandle, + scratch_buffer: &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(); + scratch_buffer.set_index(0, inner.i); + scratch_buffer.set_index(1, inner.j); + scratch_buffer.set_index(2, inner.k); + scratch_buffer.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() + rb.mass_properties().local_mprops.principal_inertia() }) } /// The angular inertia along the principal inertia axes of the rigid-body. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbPrincipalInertia(&self, handle: FlatHandle) -> RawVector { + pub fn rbPrincipalInertia(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { self.map(handle, |rb| { - rb.mass_properties().local_mprops.principal_inertia().into() - }) + let u = rb.mass_properties().local_mprops.principal_inertia(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); } /// The world-space inverse angular inertia tensor of the rigid-body, @@ -458,17 +683,30 @@ impl RawRigidBodySet { #[cfg(feature = "dim2")] pub fn rbEffectiveWorldInvInertia(&self, handle: FlatHandle) -> f32 { self.map(handle, |rb| { - rb.mass_properties().effective_world_inv_inertia.into() + rb.mass_properties().effective_world_inv_inertia }) } /// The world-space inverse angular inertia tensor of the rigid-body, /// taking into account rotation locking. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbEffectiveWorldInvInertia(&self, handle: FlatHandle) -> RawSdpMatrix3 { + pub fn rbEffectiveWorldInvInertia( + &self, + handle: FlatHandle, + scratch_buffer: &js_sys::Float32Array, + ) { self.map(handle, |rb| { - rb.mass_properties().effective_world_inv_inertia.into() - }) + let u = rb.mass_properties().effective_world_inv_inertia; + scratch_buffer.set_index(0, u.m11); + scratch_buffer.set_index(1, u.m12); + scratch_buffer.set_index(2, u.m13); + scratch_buffer.set_index(3, u.m22); + scratch_buffer.set_index(4, u.m23); + scratch_buffer.set_index(5, u.m33); + }); } /// The effective world-space angular inertia (that takes the potential rotation locking into account) of @@ -476,17 +714,30 @@ impl RawRigidBodySet { #[cfg(feature = "dim2")] pub fn rbEffectiveAngularInertia(&self, handle: FlatHandle) -> f32 { self.map(handle, |rb| { - rb.mass_properties().effective_angular_inertia().into() + 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 + /// - `scratch_buffer`: The array to be populated. #[cfg(feature = "dim3")] - pub fn rbEffectiveAngularInertia(&self, handle: FlatHandle) -> RawSdpMatrix3 { + pub fn rbEffectiveAngularInertia( + &self, + handle: FlatHandle, + scratch_buffer: &js_sys::Float32Array, + ) { self.map(handle, |rb| { - rb.mass_properties().effective_angular_inertia().into() - }) + let u = rb.mass_properties().effective_angular_inertia(); + scratch_buffer.set_index(0, u.m11); + scratch_buffer.set_index(1, u.m12); + scratch_buffer.set_index(2, u.m13); + scratch_buffer.set_index(3, u.m22); + scratch_buffer.set_index(4, u.m23); + scratch_buffer.set_index(5, u.m33); + }); } /// Wakes this rigid-body up. @@ -733,8 +984,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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn rbUserForce(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.user_force(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn rbUserForce(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.user_force(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); } /// Retrieves the constant torque(s) the user added to this rigid-body. @@ -746,8 +1020,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 + /// - `scratch_buffer`: 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, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |rb| { + let u = rb.user_torque(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + }); } } diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index e8019044..1005d40b 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -16,31 +16,128 @@ 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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn coTranslation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |co| { + let u = co.position().translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + }); + } + /// The world-space translation of this collider. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coTranslation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |co| { + let u = co.position().translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coRotation(&self, handle: FlatHandle, scratch_buffer: &js_sys::Float32Array) { + self.map(handle, |co| { + let u = co.position().rotation; + let inner = u.into_inner(); + scratch_buffer.set_index(0, inner.i); + scratch_buffer.set_index(1, inner.j); + scratch_buffer.set_index(2, inner.k); + scratch_buffer.set_index(3, inner.w); + }); } /// 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. + #[cfg(feature = "dim2")] + pub fn coTranslationWrtParent( + &self, + handle: FlatHandle, + scratch_buffer: &js_sys::Float32Array, + ) -> bool { + self.map(handle, |co| { + co.position_wrt_parent().map_or(false, |pose| { + let u = pose.translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + }) + } + + /// The translation of this collider relative to its parent rigid-body. + /// + /// Returns `false` if it doesn’t have a parent. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coTranslationWrtParent( + &self, + handle: FlatHandle, + scratch_buffer: &js_sys::Float32Array, + ) -> bool { + self.map(handle, |co| { + co.position_wrt_parent().map_or(false, |pose| { + let u = pose.translation.vector; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.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()) + .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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coRotationWrtParent( + &self, + handle: FlatHandle, + scratch_buffer: &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(); + scratch_buffer.set_index(0, inner.i); + scratch_buffer.set_index(1, inner.j); + scratch_buffer.set_index(2, inner.k); + scratch_buffer.set_index(3, inner.w); + true + }) }) } @@ -169,25 +266,100 @@ impl RawColliderSet { }) } - pub fn coHalfspaceNormal(&self, handle: FlatHandle) -> Option { + /// The outward normal of this collider if it has a half-space shape. + /// + /// Returns `false` if it doesn’t have a half-space shape. + #[cfg(feature = "dim2")] + pub fn coHalfspaceNormal( + &self, + handle: FlatHandle, + scratch_buffer: &js_sys::Float32Array, + ) -> bool { self.map(handle, |co| { - co.shape() - .as_halfspace() - .map(|h| h.normal.into_inner().into()) + co.shape().as_halfspace().map_or(false, |h| { + let u = h.normal.into_inner(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) }) } - /// The half-extents of this collider if it is has a cuboid shape. - pub fn coHalfExtents(&self, handle: FlatHandle) -> Option { + /// The outward normal of this collider if it has a half-space shape. + /// + /// Returns `false` if it doesn’t have a half-space shape. + #[cfg(feature = "dim3")] + pub fn coHalfspaceNormal( + &self, + handle: FlatHandle, + scratch_buffer: &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_halfspace().map_or(false, |h| { + let u = h.normal.into_inner(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) + }) + } + + /// The half-extents of this collider if it has a cuboid shape. + /// + /// Returns `false` if it doesn’t have a cuboid shape. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn coHalfExtents(&self, handle: FlatHandle, scratch_buffer: &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; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + }, + |c| { + let u = c.half_extents; + scratch_buffer.set_index(0, u.x); + scratch_buffer.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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coHalfExtents(&self, handle: FlatHandle, scratch_buffer: &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; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) + }, + |c| { + let u = c.half_extents; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }, + ) }) } @@ -580,11 +752,46 @@ 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 + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim2")] + pub fn coHeightfieldScale( + &self, + handle: FlatHandle, + scratch_buffer: &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(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }), + _ => false, + }) + } + + /// The scaling factor applied to this heightfield if it is one. + /// + /// # Parameters + /// - `scratch_buffer`: The array to be populated. + #[cfg(feature = "dim3")] + pub fn coHeightfieldScale( + &self, + handle: FlatHandle, + scratch_buffer: &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(); + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }), + _ => false, }) } diff --git a/src/geometry/contact.rs b/src/geometry/contact.rs index 47043a27..6697853a 100644 --- a/src/geometry/contact.rs +++ b/src/geometry/contact.rs @@ -1,4 +1,3 @@ -use crate::math::RawVector; use rapier::parry::query; use wasm_bindgen::prelude::*; @@ -9,23 +8,47 @@ pub struct RawShapeContact { #[wasm_bindgen] impl RawShapeContact { - pub fn distance(&self) -> f32 { - self.contact.dist + /// Writes the contact components into the given scratch buffer. + /// + /// Layout: `[distance, point1, point2, normal1, normal2]`. + #[cfg(feature = "dim2")] + pub fn getComponents(&self, scratch_buffer: &js_sys::Float32Array) { + scratch_buffer.set_index(0, self.contact.dist); + let p1 = self.contact.point1.coords; + scratch_buffer.set_index(1, p1.x); + scratch_buffer.set_index(2, p1.y); + let p2 = self.contact.point2.coords; + scratch_buffer.set_index(3, p2.x); + scratch_buffer.set_index(4, p2.y); + let n1 = self.contact.normal1.into_inner(); + scratch_buffer.set_index(5, n1.x); + scratch_buffer.set_index(6, n1.y); + let n2 = self.contact.normal2.into_inner(); + scratch_buffer.set_index(7, n2.x); + scratch_buffer.set_index(8, n2.y); } - pub fn point1(&self) -> RawVector { - self.contact.point1.coords.into() - } - - pub fn point2(&self) -> RawVector { - self.contact.point2.coords.into() - } - - pub fn normal1(&self) -> RawVector { - self.contact.normal1.into_inner().into() - } - - pub fn normal2(&self) -> RawVector { - self.contact.normal2.into_inner().into() + /// Writes the contact components into the given scratch buffer. + /// + /// Layout: `[distance, point1, point2, normal1, normal2]`. + #[cfg(feature = "dim3")] + pub fn getComponents(&self, scratch_buffer: &js_sys::Float32Array) { + scratch_buffer.set_index(0, self.contact.dist); + let p1 = self.contact.point1.coords; + scratch_buffer.set_index(1, p1.x); + scratch_buffer.set_index(2, p1.y); + scratch_buffer.set_index(3, p1.z); + let p2 = self.contact.point2.coords; + scratch_buffer.set_index(4, p2.x); + scratch_buffer.set_index(5, p2.y); + scratch_buffer.set_index(6, p2.z); + let n1 = self.contact.normal1.into_inner(); + scratch_buffer.set_index(7, n1.x); + scratch_buffer.set_index(8, n1.y); + scratch_buffer.set_index(9, n1.z); + let n2 = self.contact.normal2.into_inner(); + scratch_buffer.set_index(10, n2.x); + scratch_buffer.set_index(11, n2.y); + scratch_buffer.set_index(12, n2.z); } } diff --git a/src/geometry/narrow_phase.rs b/src/geometry/narrow_phase.rs index be3fee25..b89a21e7 100644 --- a/src/geometry/narrow_phase.rs +++ b/src/geometry/narrow_phase.rs @@ -1,4 +1,3 @@ -use crate::math::RawVector; use crate::utils::{self, FlatHandle}; use rapier::geometry::{ContactManifold, ContactPair, NarrowPhase}; use rapier::math::Real; @@ -93,20 +92,65 @@ impl RawContactPair { #[wasm_bindgen] impl RawContactManifold { - pub fn normal(&self) -> RawVector { - unsafe { RawVector((*self.0).data.normal) } + #[cfg(feature = "dim2")] + pub fn normal(&self, scratch_buffer: &js_sys::Float32Array) { + unsafe { + let u = (*self.0).data.normal; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + } + + #[cfg(feature = "dim3")] + pub fn normal(&self, scratch_buffer: &js_sys::Float32Array) { + unsafe { + let u = (*self.0).data.normal; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + } } // pub fn user_data(&self) -> u32 { // unsafe { (*self.0).data.user_data } // } - pub fn local_n1(&self) -> RawVector { - unsafe { (*self.0).local_n1.into() } + #[cfg(feature = "dim2")] + pub fn local_n1(&self, scratch_buffer: &js_sys::Float32Array) { + unsafe { + let u = (*self.0).local_n1; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } } - pub fn local_n2(&self) -> RawVector { - unsafe { (*self.0).local_n2.into() } + #[cfg(feature = "dim3")] + pub fn local_n1(&self, scratch_buffer: &js_sys::Float32Array) { + unsafe { + let u = (*self.0).local_n1; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + } + } + + #[cfg(feature = "dim2")] + pub fn local_n2(&self, scratch_buffer: &js_sys::Float32Array) { + unsafe { + let u = (*self.0).local_n2; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + } + + #[cfg(feature = "dim3")] + pub fn local_n2(&self, scratch_buffer: &js_sys::Float32Array) { + unsafe { + let u = (*self.0).local_n2; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + } } pub fn subshape1(&self) -> u32 { @@ -121,12 +165,54 @@ impl RawContactManifold { unsafe { (*self.0).points.len() } } - pub fn contact_local_p1(&self, i: usize) -> Option { - unsafe { (&(*self.0).points).get(i).map(|c| c.local_p1.coords.into()) } + #[cfg(feature = "dim2")] + pub fn contact_local_p1(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + unsafe { + (&(*self.0).points).get(i).map_or(false, |c| { + let u = c.local_p1.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } } - pub fn contact_local_p2(&self, i: usize) -> Option { - unsafe { (&(*self.0).points).get(i).map(|c| c.local_p2.coords.into()) } + #[cfg(feature = "dim3")] + pub fn contact_local_p1(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + unsafe { + (&(*self.0).points).get(i).map_or(false, |c| { + let u = c.local_p1.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) + } + } + + #[cfg(feature = "dim2")] + pub fn contact_local_p2(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + unsafe { + (&(*self.0).points).get(i).map_or(false, |c| { + let u = c.local_p2.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } + } + + #[cfg(feature = "dim3")] + pub fn contact_local_p2(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + unsafe { + (&(*self.0).points).get(i).map_or(false, |c| { + let u = c.local_p2.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) + } } pub fn contact_dist(&self, i: usize) -> Real { @@ -184,12 +270,28 @@ impl RawContactManifold { unsafe { (*self.0).data.solver_contacts.len() } } - pub fn solver_contact_point(&self, i: usize) -> Option { + #[cfg(feature = "dim2")] + pub fn solver_contact_point(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { unsafe { - (&(*self.0).data) - .solver_contacts - .get(i) - .map(|c| c.point.coords.into()) + (&(*self.0).data).solver_contacts.get(i).map_or(false, |c| { + let u = c.point.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + true + }) + } + } + + #[cfg(feature = "dim3")] + pub fn solver_contact_point(&self, i: usize, scratch_buffer: &js_sys::Float32Array) -> bool { + unsafe { + (&(*self.0).data).solver_contacts.get(i).map_or(false, |c| { + let u = c.point.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + true + }) } } @@ -211,7 +313,22 @@ impl RawContactManifold { unsafe { (&(*self.0).data).solver_contacts[i].restitution } } - pub fn solver_contact_tangent_velocity(&self, i: usize) -> RawVector { - unsafe { (&(*self.0).data).solver_contacts[i].tangent_velocity.into() } + #[cfg(feature = "dim2")] + pub fn solver_contact_tangent_velocity(&self, i: usize, scratch_buffer: &js_sys::Float32Array) { + unsafe { + let u = (&(*self.0).data).solver_contacts[i].tangent_velocity; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + } + + #[cfg(feature = "dim3")] + pub fn solver_contact_tangent_velocity(&self, i: usize, scratch_buffer: &js_sys::Float32Array) { + unsafe { + let u = (&(*self.0).data).solver_contacts[i].tangent_velocity; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); + } } } diff --git a/src/geometry/point.rs b/src/geometry/point.rs index e7bb4a93..bf58d40b 100644 --- a/src/geometry/point.rs +++ b/src/geometry/point.rs @@ -1,6 +1,5 @@ use crate::geometry::feature::IntoTypeValue; use crate::geometry::RawFeatureType; -use crate::math::RawVector; use crate::utils::{self, FlatHandle}; use rapier::{ geometry::{ColliderHandle, PointProjection}, @@ -13,8 +12,21 @@ pub struct RawPointProjection(pub(crate) PointProjection); #[wasm_bindgen] impl RawPointProjection { - pub fn point(&self) -> RawVector { - self.0.point.coords.into() + /// Writes the projected point components into the given scratch buffer. + #[cfg(feature = "dim2")] + pub fn point(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.point.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + /// Writes the projected point components into the given scratch buffer. + #[cfg(feature = "dim3")] + pub fn point(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.point.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } pub fn isInside(&self) -> bool { @@ -35,8 +47,21 @@ impl RawPointColliderProjection { utils::flat_handle(self.handle.0) } - pub fn point(&self) -> RawVector { - self.proj.point.coords.into() + /// Writes the projected point components into the given scratch buffer. + #[cfg(feature = "dim2")] + pub fn point(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.proj.point.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + /// Writes the projected point components into the given scratch buffer. + #[cfg(feature = "dim3")] + pub fn point(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.proj.point.coords; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } pub fn isInside(&self) -> bool { diff --git a/src/geometry/ray.rs b/src/geometry/ray.rs index 5285d5da..a99723cc 100644 --- a/src/geometry/ray.rs +++ b/src/geometry/ray.rs @@ -1,6 +1,5 @@ use crate::geometry::feature::IntoTypeValue; use crate::geometry::RawFeatureType; -use crate::math::RawVector; use crate::utils::{self, FlatHandle}; use rapier::geometry::{ColliderHandle, RayIntersection}; use wasm_bindgen::prelude::*; @@ -10,8 +9,21 @@ pub struct RawRayIntersection(pub(crate) RayIntersection); #[wasm_bindgen] impl RawRayIntersection { - pub fn normal(&self) -> RawVector { - self.0.normal.into() + /// Writes the hit normal components into the given scratch buffer. + #[cfg(feature = "dim2")] + pub fn normal(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.normal; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + /// Writes the hit normal components into the given scratch buffer. + #[cfg(feature = "dim3")] + pub fn normal(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.normal; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } pub fn time_of_impact(&self) -> f32 { @@ -39,8 +51,21 @@ impl RawRayColliderIntersection { utils::flat_handle(self.handle.0) } - pub fn normal(&self) -> RawVector { - self.inter.normal.into() + /// Writes the hit normal components into the given scratch buffer. + #[cfg(feature = "dim2")] + pub fn normal(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.inter.normal; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + } + + /// Writes the hit normal components into the given scratch buffer. + #[cfg(feature = "dim3")] + pub fn normal(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.inter.normal; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + scratch_buffer.set_index(2, u.z); } pub fn time_of_impact(&self) -> f32 { diff --git a/src/geometry/toi.rs b/src/geometry/toi.rs index 1e00675d..909b0138 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, scratch_buffer: &js_sys::Float32Array) { + scratch_buffer.set_index(0, self.hit.time_of_impact); + let mut u = self.hit.witness1.coords; + scratch_buffer.set_index(1, u.x); + scratch_buffer.set_index(2, u.y); + u = self.hit.witness2.coords; + scratch_buffer.set_index(3, u.x); + scratch_buffer.set_index(4, u.y); + u = self.hit.normal1.into_inner(); + scratch_buffer.set_index(5, u.x); + scratch_buffer.set_index(6, u.y); + u = self.hit.normal2.into_inner(); + scratch_buffer.set_index(7, u.x); + scratch_buffer.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, scratch_buffer: &js_sys::Float32Array) { + scratch_buffer.set_index(0, self.hit.time_of_impact); + let mut u = self.hit.witness1.coords; + scratch_buffer.set_index(1, u.x); + scratch_buffer.set_index(2, u.y); + scratch_buffer.set_index(3, u.z); + u = self.hit.witness2.coords; + scratch_buffer.set_index(4, u.x); + scratch_buffer.set_index(5, u.y); + scratch_buffer.set_index(6, u.z); + u = self.hit.normal1.into_inner(); + scratch_buffer.set_index(7, u.x); + scratch_buffer.set_index(8, u.y); + scratch_buffer.set_index(9, u.z); + u = self.hit.normal2.into_inner(); + scratch_buffer.set_index(10, u.x); + scratch_buffer.set_index(11, u.y); + scratch_buffer.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, scratch_buffer: &js_sys::Float32Array) { + scratch_buffer.set_index(0, self.hit.time_of_impact); + let mut u = self.hit.witness1.coords; + scratch_buffer.set_index(1, u.x); + scratch_buffer.set_index(2, u.y); + u = self.hit.witness2.coords; + scratch_buffer.set_index(3, u.x); + scratch_buffer.set_index(4, u.y); + u = self.hit.normal1.into_inner(); + scratch_buffer.set_index(5, u.x); + scratch_buffer.set_index(6, u.y); + u = self.hit.normal2.into_inner(); + scratch_buffer.set_index(7, u.x); + scratch_buffer.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, scratch_buffer: &js_sys::Float32Array) { + scratch_buffer.set_index(0, self.hit.time_of_impact); + let mut u = self.hit.witness1.coords; + scratch_buffer.set_index(1, u.x); + scratch_buffer.set_index(2, u.y); + scratch_buffer.set_index(3, u.z); + u = self.hit.witness2.coords; + scratch_buffer.set_index(4, u.x); + scratch_buffer.set_index(5, u.y); + scratch_buffer.set_index(6, u.z); + u = self.hit.normal1.into_inner(); + scratch_buffer.set_index(7, u.x); + scratch_buffer.set_index(8, u.y); + scratch_buffer.set_index(9, u.z); + u = self.hit.normal2.into_inner(); + scratch_buffer.set_index(10, u.x); + scratch_buffer.set_index(11, u.y); + scratch_buffer.set_index(12, u.z); } } diff --git a/src/pipeline/event_queue.rs b/src/pipeline/event_queue.rs index fb6034d9..af61b627 100644 --- a/src/pipeline/event_queue.rs +++ b/src/pipeline/event_queue.rs @@ -1,4 +1,3 @@ -use crate::math::RawVector; use crate::utils; use crate::utils::FlatHandle; use rapier::geometry::{CollisionEvent, ContactForceEvent}; @@ -32,8 +31,12 @@ impl RawContactForceEvent { } /// The sum of all the forces between the two colliders. - pub fn total_force(&self) -> RawVector { - RawVector(self.0.total_force) + pub fn total_force(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.total_force; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + #[cfg(feature = "dim3")] + scratch_buffer.set_index(2, u.z); } /// The sum of the magnitudes of each force between the two colliders. @@ -46,8 +49,12 @@ impl RawContactForceEvent { } /// The world-space (unit) direction of the force with strongest magnitude. - pub fn max_force_direction(&self) -> RawVector { - RawVector(self.0.max_force_direction) + pub fn max_force_direction(&self, scratch_buffer: &js_sys::Float32Array) { + let u = self.0.max_force_direction; + scratch_buffer.set_index(0, u.x); + scratch_buffer.set_index(1, u.y); + #[cfg(feature = "dim3")] + scratch_buffer.set_index(2, u.z); } /// The magnitude of the largest force at a contact point of this contact pair.