import { PointToPointConstraint } from '../constraints/PointToPointConstraint' import { ConeEquation } from '../equations/ConeEquation' import { RotationalEquation } from '../equations/RotationalEquation' import { Vec3 } from '../math/Vec3' import type { Body } from '../objects/Body' export type ConeTwistConstraintOptions = ConstructorParameters[2] /** * A Cone Twist constraint, useful for ragdolls. */ export class ConeTwistConstraint extends PointToPointConstraint { /** * The axis direction for the constraint of the body A. */ axisA: Vec3 /** * The axis direction for the constraint of the body B. */ axisB: Vec3 /** * The aperture angle of the cone. */ angle: number /** * The twist angle of the joint. */ twistAngle: number coneEquation: ConeEquation twistEquation: RotationalEquation constructor( bodyA: Body, bodyB: Body, options: { /** * The pivot point for bodyA. */ pivotA?: Vec3 /** * The pivot point for bodyB. */ pivotB?: Vec3 /** * The axis direction for the constraint of the body A. */ axisA?: Vec3 /** * The axis direction for the constraint of the body B. */ axisB?: Vec3 /** * The aperture angle of the cone. * @default 0 */ angle?: number /** * The twist angle of the joint. * @default 0 */ twistAngle?: number /** * The maximum force that should be applied to constrain the bodies. * @default 1e6 */ maxForce?: number /** * Wether to collide the connected bodies or not. * @default false */ collideConnected?: boolean } = {} ) { const maxForce = typeof options.maxForce !== 'undefined' ? options.maxForce : 1e6 // Set pivot point in between const pivotA = options.pivotA ? options.pivotA.clone() : new Vec3() const pivotB = options.pivotB ? options.pivotB.clone() : new Vec3() super(bodyA, pivotA, bodyB, pivotB, maxForce) this.axisA = options.axisA ? options.axisA.clone() : new Vec3() this.axisB = options.axisB ? options.axisB.clone() : new Vec3() this.collideConnected = !!options.collideConnected this.angle = typeof options.angle !== 'undefined' ? options.angle : 0 const c = (this.coneEquation = new ConeEquation(bodyA, bodyB, options)) const t = (this.twistEquation = new RotationalEquation(bodyA, bodyB, options)) this.twistAngle = typeof options.twistAngle !== 'undefined' ? options.twistAngle : 0 // Make the cone equation push the bodies toward the cone axis, not outward c.maxForce = 0 c.minForce = -maxForce // Make the twist equation add torque toward the initial position t.maxForce = 0 t.minForce = -maxForce this.equations.push(c, t) } update(): void { const bodyA = this.bodyA const bodyB = this.bodyB const cone = this.coneEquation const twist = this.twistEquation super.update() // Update the axes to the cone constraint bodyA.vectorToWorldFrame(this.axisA, cone.axisA) bodyB.vectorToWorldFrame(this.axisB, cone.axisB) // Update the world axes in the twist constraint this.axisA.tangents(twist.axisA, twist.axisA) bodyA.vectorToWorldFrame(twist.axisA, twist.axisA) this.axisB.tangents(twist.axisB, twist.axisB) bodyB.vectorToWorldFrame(twist.axisB, twist.axisB) cone.angle = this.angle twist.maxAngle = this.twistAngle } } const ConeTwistConstraint_update_tmpVec1 = new Vec3() const ConeTwistConstraint_update_tmpVec2 = new Vec3()