UNPKG

blaze-2d

Version:

A fast and simple WebGL 2 2D game engine written in TypeScript

167 lines 6.25 kB
import { mat2, vec2 } from "gl-matrix"; import { cross2D, cross2DWithScalar } from "../../utils/vectors"; import CollisionObject from "../collisionObject"; /** * Represents a distance constraint between two bodies or a body and a point. * * The constraint points are in local space of their body. * * If the constraint is between a body and a point then the given point is in world space. */ export default class Constraint { constructor(a, b) { this.anchorA = vec2.create(); this.rotA = 0; this.anchorB = vec2.create(); this.rotB = 0; this.errorBias = Math.pow(0.9, 60); this.maxBias = Infinity; this.maxForce = Infinity; if (a instanceof CollisionObject && b instanceof CollisionObject) { // constraint between two bodies this.a = a; this.b = b; } else if (a instanceof CollisionObject) { // constraint between body and point this.a = a; this.point = b; } else { // constraint from options this.a = a.a; this.b = a.b; this.anchorA = a.anchorA; this.anchorB = a.anchorB; } this.rotA = this.a.getRotation(); if (this.b) this.rotB = this.b.getRotation(); } /** * Determines wether or not the constraint is between a body and a point. * * @returns Wether or not the constraint is between a body and a point. */ isBodyToPoint() { return !this.b; } applyImpulses(impulse, direction) { const anchorA = this.anchorA; const anchorB = this.isBodyToPoint() ? this.point : this.anchorB; if (typeof impulse === "number") { if (!this.a.isStatic) { this.a.applyImpulse(vec2.scale(vec2.create(), direction, -impulse), anchorA); } if (!this.isBodyToPoint() && !this.b.isStatic) { this.b.applyImpulse(vec2.scale(vec2.create(), direction, impulse), anchorB); } } else { if (!this.a.isStatic) { this.a.applyImpulse(vec2.negate(vec2.create(), impulse), anchorA); } if (!this.isBodyToPoint() && !this.b.isStatic) { this.b.applyImpulse(impulse, anchorB); } } } /** * Updates the constraint's anchor points to match the rotations of their bodies. */ updateAnchors() { const anchorA = this.anchorA; const anchorB = this.isBodyToPoint() ? this.point : this.anchorB; // rotate points with bodies if (!this.a.isStatic) { vec2.rotate(anchorA, anchorA, vec2.create(), this.a.getRotation() - this.rotA); this.rotA = this.a.getRotation(); } if (!this.isBodyToPoint() && !this.b.isStatic) { vec2.rotate(anchorB, anchorB, vec2.create(), this.b.getRotation() - this.rotB); this.rotB = this.b.getRotation(); } } /** * Calculate the relative velocity of the bodies in the constraint. * * @param pointA The anchor point on body a * @param pointB The anchor point on body b * @returns The relative velocity between body a and b or body a and the anchor point */ calculateRelativeVelocity(pointA, pointB) { const angularCrossPointA = cross2DWithScalar(vec2.create(), pointA, this.a.angularVelocity); const angularCrossPointB = cross2DWithScalar(vec2.create(), pointB, this.isBodyToPoint() ? 0 : -this.b.angularVelocity); const velA = vec2.sub(vec2.create(), this.a.velocity, angularCrossPointA); const velB = vec2.add(vec2.create(), this.isBodyToPoint() ? vec2.create() : this.b.velocity, angularCrossPointB); return vec2.sub(vec2.create(), velB, velA); } /** * Calculates the velocity bias coefficient. * * @param dt The time since the last update */ biasCoef(dt) { return 1 - Math.pow(this.errorBias, dt); } /** * Computes the k scalar value for the given body. * * @param body The body to calculate for * @param r The anchor point on the body * @param n The constraint's delta normal * @returns The k scalar value for the given body */ kScalarBody(body, r, n) { const rcn = cross2D(r, n); return body.getInverseMass() + body.getInverseInertia() * rcn * rcn; } /** * Computes the k scalar value for the constraint. * * @param n The constraint's delta normal * @returns The k scalar value for the constraint */ kScalar(n) { const val = this.kScalarBody(this.a, this.anchorA, n) + (this.isBodyToPoint() ? 0 : this.kScalarBody(this.b, this.anchorB, n)); return val; } /** * Computes the k tensor matrix for the constraint. * * @see [Chipmunk2D kTensor](https://github.com/slembcke/Chipmunk2D/blob/master/include/chipmunk/chipmunk_private.h#L229) * * @returns k tensor matrix */ kTensor() { const r1 = this.anchorA; const r2 = this.isBodyToPoint() ? this.point : this.anchorB; const invMass = this.a.getInverseMass() + (this.isBodyToPoint() ? 0 : this.b.getInverseMass()); // start with Identity * invMass const k = mat2.identity(mat2.create()); mat2.multiplyScalar(k, k, invMass); // add the influence from r1 const aInvInertia = this.a.getInverseInertia(); const r1xsq = r1[0] * r1[0] * aInvInertia; const r1ysq = r1[1] * r1[1] * aInvInertia; const r1nxy = -r1[0] * r1[1] * aInvInertia; k[0] += r1ysq; k[2] += r1nxy; k[1] += r1nxy; k[3] += r1xsq; // add the influence from r2 const bInvInertia = this.isBodyToPoint() ? 0 : this.b.getInverseInertia(); const r2xsq = r2[0] * r2[0] * bInvInertia; const r2ysq = r2[1] * r2[1] * bInvInertia; const r2nxy = -r2[0] * r2[1] * bInvInertia; k[0] += r2ysq; k[2] += r2nxy; k[1] += r2nxy; k[3] += r2xsq; // invert mat2.invert(k, k); return k; } } //# sourceMappingURL=constraint.js.map