From 232c96cc5465b2f8afd802c526d2a83b2a8341b5 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 14 Jun 2013 03:20:47 +0100 Subject: [PATCH] Space and Joints interface added. --- Phaser/Phaser.csproj | 9 +- Phaser/physics/advanced/Body.ts | 2 + Phaser/physics/advanced/Manager.ts | 4 + Phaser/physics/advanced/Space.ts | 831 +++++++++++ Phaser/physics/advanced/joints/IJoint.ts | 43 + Tests/phaser.js | 1624 ++++++++++++++-------- build/phaser.d.ts | 277 ++-- build/phaser.js | 1624 ++++++++++++++-------- 8 files changed, 3215 insertions(+), 1199 deletions(-) create mode 100644 Phaser/physics/advanced/Space.ts create mode 100644 Phaser/physics/advanced/joints/IJoint.ts diff --git a/Phaser/Phaser.csproj b/Phaser/Phaser.csproj index 4890ae97..2cd314bf 100644 --- a/Phaser/Phaser.csproj +++ b/Phaser/Phaser.csproj @@ -57,7 +57,6 @@ true - @@ -200,6 +199,10 @@ Joint.ts + + + IJoint.ts + Manager.ts @@ -227,6 +230,10 @@ ShapeTriangle.ts + + + Space.ts + ArcadePhysics.ts diff --git a/Phaser/physics/advanced/Body.ts b/Phaser/physics/advanced/Body.ts index b7348e52..f974005d 100644 --- a/Phaser/physics/advanced/Body.ts +++ b/Phaser/physics/advanced/Body.ts @@ -5,6 +5,7 @@ /// /// /// +/// /** * Phaser - Advanced Physics - Body @@ -133,6 +134,7 @@ module Phaser.Physics.Advanced { public categoryBits = 0x0001; public maskBits = 0xFFFF; public stepCount = 0; + public space: Space; // duplicate = Util function // serialize = Util function diff --git a/Phaser/physics/advanced/Manager.ts b/Phaser/physics/advanced/Manager.ts index 57b86601..7aa5f976 100644 --- a/Phaser/physics/advanced/Manager.ts +++ b/Phaser/physics/advanced/Manager.ts @@ -17,6 +17,8 @@ module Phaser.Physics.Advanced { this.game = game; + Manager.collision = new Collision(); + } /** @@ -24,6 +26,8 @@ module Phaser.Physics.Advanced { */ public game: Game; + public static collision: Collision; + public static SHAPE_TYPE_CIRCLE: number = 0; public static SHAPE_TYPE_SEGMENT: number = 1; public static SHAPE_TYPE_POLY: number = 2; diff --git a/Phaser/physics/advanced/Space.ts b/Phaser/physics/advanced/Space.ts new file mode 100644 index 00000000..340b204e --- /dev/null +++ b/Phaser/physics/advanced/Space.ts @@ -0,0 +1,831 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + +/** +* Phaser - Advanced Physics - Space +* +* Based on the work Ju Hyung Lee started in JS PhyRus. +*/ + +module Phaser.Physics.Advanced { + + export class Space { + + constructor() { + + this.bodyArr = []; + this.bodyHash = {}; + + this.jointArr = []; + this.jointHash = {}; + + this.numContacts = 0; + this.contactSolvers = []; + + //this.postSolve(arb) { }; + + this.gravity = new Phaser.Vec2; + this.damping = 0; + + } + + public static TIME_TO_SLEEP = 0.5; + public static SLEEP_LINEAR_TOLERANCE = 0.5; + public static SLEEP_ANGULAR_TOLERANCE = 2 * Phaser.GameMath.DEG_TO_RAD; + + public bodyArr: Body[]; + public bodyHash; + public jointArr: IJoint[]; + public jointHash; + public numContacts: number; + public contactSolvers: ContactSolver[]; + public postSolve; + public gravity: Phaser.Vec2; + public damping: number; + public stepCount: number = 0; + + public clear() { + + Manager.shapeCounter = 0; + Manager.bodyCounter = 0; + Manager.jointCounter = 0; + + for (var i = 0; i < this.bodyArr.length; i++) + { + if (this.bodyArr[i]) + { + this.removeBody(this.bodyArr[i]); + } + } + + this.bodyArr = []; + this.bodyHash = {}; + + this.jointArr = []; + this.jointHash = {}; + + this.contactSolvers = []; + + this.stepCount = 0; + + } + + public addBody(body: Body) { + + if (this.bodyHash[body.id] != undefined) + { + return; + } + + var index = this.bodyArr.push(body) - 1; + this.bodyHash[body.id] = index; + + body.awake(true); + body.space = this; + body.cacheData(); + + } + + public removeBody(body: Body) { + + if (this.bodyHash[body.id] == undefined) + { + return; + } + + // Remove linked joint + for (var i = 0; i < body.joints.length; i++) + { + if (body.joints[i]) + { + this.removeJoint(body.joints[i]); + } + } + + body.space = null; + + var index = this.bodyHash[body.id]; + delete this.bodyHash[body.id]; + delete this.bodyArr[index]; + + } + + public addJoint(joint: IJoint) { + + if (this.jointHash[joint.id] != undefined) + { + return; + } + + joint.body1.awake(true); + joint.body2.awake(true); + + var index = this.jointArr.push(joint) - 1; + this.jointHash[joint.id] = index; + + var index = joint.body1.joints.push(joint) - 1; + joint.body1.jointHash[joint.id] = index; + + var index = joint.body2.joints.push(joint) - 1; + joint.body2.jointHash[joint.id] = index; + + } + + public removeJoint(joint: IJoint) { + + if (this.jointHash[joint.id] == undefined) + { + return; + } + + joint.body1.awake(true); + joint.body2.awake(true); + + var index = joint.body1.jointHash[joint.id]; + delete joint.body1.jointHash[joint.id]; + delete joint.body1.joints[index]; + + var index = joint.body2.jointHash[joint.id]; + delete joint.body2.jointHash[joint.id]; + delete joint.body2.joints[index]; + + var index = this.jointHash[joint.id]; + delete this.jointHash[joint.id]; + delete this.jointArr[index]; + + } + + public findShapeByPoint(p, refShape) { + + var firstShape; + + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue; + } + + for (var j = 0; j < body.shapes.length; j++) + { + var shape = body.shapes[j]; + + if (shape.pointQuery(p)) + { + if (!refShape) + { + return shape; + } + + if (!firstShape) + { + firstShape = shape; + } + + if (shape == refShape) + { + refShape = null; + } + } + } + } + + return firstShape; + } + + public findBodyByPoint(p, refBody: Body) { + + var firstBody; + + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue; + } + + for (var j = 0; j < body.shapes.length; j++) + { + var shape = body.shapes[j]; + + if (shape.pointQuery(p)) + { + if (!refBody) + { + return shape.body; + } + + if (!firstBody) + { + firstBody = shape.body; + } + + if (shape.body == refBody) + { + refBody = null; + } + + break; + } + } + } + + return firstBody; + + } + + // TODO: Replace this function to shape hashing + public shapeById(id) { + + var shape; + + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + if (!body) + { + continue; + } + + for (var j = 0; j < body.shapes.length; j++) + { + if (body.shapes[j].id == id) + { + return body.shapes[j]; + } + } + } + + return null; + } + + public jointById(id) { + + var index = this.jointHash[id]; + + if (index != undefined) + { + return this.jointArr[index]; + } + + return null; + } + + public findVertexByPoint(p, minDist, refVertexId) { + + var firstVertexId = -1; + + refVertexId = refVertexId || -1; + + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue; + } + + for (var j = 0; j < body.shapes.length; j++) + { + var shape = body.shapes[j]; + var index = shape.findVertexByPoint(p, minDist); + + if (index != -1) + { + var vertex = (shape.id << 16) | index; + + if (refVertexId == -1) + { + return vertex; + } + + if (firstVertexId == -1) + { + firstVertexId = vertex; + } + + if (vertex == refVertexId) + { + refVertexId = -1; + } + } + } + } + + return firstVertexId; + + } + + public findEdgeByPoint(p, minDist, refEdgeId) { + + var firstEdgeId = -1; + + refEdgeId = refEdgeId || -1; + + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue; + } + + for (var j = 0; j < body.shapes.length; j++) + { + var shape = body.shapes[j]; + + if (shape.type != Manager.SHAPE_TYPE_POLY) + { + continue; + } + + var index = shape.findEdgeByPoint(p, minDist); + + if (index != -1) + { + var edge = (shape.id << 16) | index; + + if (refEdgeId == -1) + { + return edge; + } + + if (firstEdgeId == -1) + { + firstEdgeId = edge; + } + + if (edge == refEdgeId) + { + refEdgeId = -1; + } + } + } + } + + return firstEdgeId; + } + + public findJointByPoint(p, minDist, refJointId) { + + var firstJointId = -1; + + var dsq = minDist * minDist; + + refJointId = refJointId || -1; + + for (var i = 0; i < this.jointArr.length; i++) + { + var joint = this.jointArr[i]; + + if (!joint) + { + continue; + } + + var jointId = -1; + + if (Phaser.Vec2Utils.distanceSq(p, joint.getWorldAnchor1()) < dsq) + { + jointId = (joint.id << 16 | 0); + } + else if (Phaser.Vec2Utils.distanceSq(p, joint.getWorldAnchor2()) < dsq) + { + jointId = (joint.id << 16 | 1); + } + + if (jointId != -1) + { + if (refJointId == -1) + { + return jointId; + } + + if (firstJointId == -1) + { + firstJointId = jointId; + } + + if (jointId == refJointId) + { + refJointId = -1; + } + } + } + + return firstJointId; + } + + public findContactSolver(shape1, shape2) { + + for (var i = 0; i < this.contactSolvers.length; i++) + { + var contactSolver = this.contactSolvers[i]; + + if (shape1 == contactSolver.shape1 && shape2 == contactSolver.shape2) + { + return contactSolver; + } + } + + return null; + } + + public genTemporalContactSolvers() { + + //var t0 = Date.now(); + + var newContactSolverArr = []; + + this.numContacts = 0; + + for (var body1_index = 0; body1_index < this.bodyArr.length; body1_index++) + { + var body1 = this.bodyArr[body1_index]; + + if (!body1) + { + continue; + } + + body1.stepCount = this.stepCount; + + for (var body2_index = 0; body2_index < this.bodyArr.length; body2_index++) + { + var body2 = this.bodyArr[body2_index]; + if (!body2) + { + continue; + } + + if (body1.stepCount == body2.stepCount) + { + continue; + } + + var active1 = body1.isAwake && !body1.isStatic; + var active2 = body2.isAwake && !body2.isStatic; + + if (!active1 && !active2) + { + continue; + } + + if (!body1.isCollidable(body2)) + { + continue; + } + + if (!body1.bounds.intersectsBounds(body2.bounds)) + { + continue; + } + + for (var i = 0; i < body1.shapes.length; i++) + { + for (var j = 0; j < body2.shapes.length; j++) + { + var shape1 = body1.shapes[i]; + var shape2 = body2.shapes[j]; + + var contactArr = []; + + if (!Manager.collision.collide(shape1, shape2, contactArr)) + { + continue; + } + + if (shape1.type > shape2.type) + { + var temp = shape1; + shape1 = shape2; + shape2 = temp; + } + + this.numContacts += contactArr.length; + + var contactSolver = this.findContactSolver(shape1, shape2); + + if (contactSolver) + { + contactSolver.update(contactArr); + newContactSolverArr.push(contactSolver); + } + else + { + body1.awake(true); + body2.awake(true); + + var newContactSolver = new ContactSolver(shape1, shape2); + newContactSolver.contacts = contactArr; + newContactSolver.elasticity = Math.max(shape1.e, shape2.e); + newContactSolver.friction = Math.sqrt(shape1.u * shape2.u); + newContactSolverArr.push(newContactSolver); + } + } + } + } + } + + //stats.timeCollision = Date.now() - t0; + + return newContactSolverArr; + } + + public initSolver(dt, dt_inv, warmStarting) { + + //var t0 = Date.now(); + + // Initialize contact solvers + for (var i = 0; i < this.contactSolvers.length; i++) + { + this.contactSolvers[i].initSolver(dt_inv); + } + + // Initialize joint solver + for (var i = 0; i < this.jointArr.length; i++) + { + if (this.jointArr[i]) + { + this.jointArr[i].initSolver(dt, warmStarting); + } + } + + // Warm starting (apply cached impulse) + if (warmStarting) + { + for (var i = 0; i < this.contactSolvers.length; i++) + { + this.contactSolvers[i].warmStart(); + } + } + + //stats.timeInitSolver = Date.now() - t0; + } + + public velocitySolver(iteration) { + + //var t0 = Date.now(); + + for (var i = 0; i < iteration; i++) + { + for (var j = 0; j < this.jointArr.length; j++) + { + if (this.jointArr[j]) + { + this.jointArr[j].solveVelocityConstraints(); + } + } + + for (var j = 0; j < this.contactSolvers.length; j++) + { + this.contactSolvers[j].solveVelocityConstraints(); + } + } + + //stats.timeVelocitySolver = Date.now() - t0; + } + + public positionSolver(iteration) { + + //var t0 = Date.now(); + + var positionSolved = false; + + //stats.positionIterations = 0; + + for (var i = 0; i < iteration; i++) + { + var contactsOk = true; + var jointsOk = true; + + for (var j = 0; j < this.contactSolvers.length; j++) + { + var contactOk = this.contactSolvers[j].solvePositionConstraints(); + contactsOk = contactOk && contactsOk; + } + + for (var j = 0; j < this.jointArr.length; j++) + { + if (this.jointArr[j]) + { + var jointOk = this.jointArr[j].solvePositionConstraints(); + jointsOk = jointOk && jointsOk; + } + } + + if (contactsOk && jointsOk) + { + // exit early if the position errors are small + positionSolved = true; + break; + } + + //stats.positionIterations++; + } + + //stats.timePositionSolver = Date.now() - t0; + + return positionSolved; + + } + + public step(dt, vel_iteration, pos_iteration, warmStarting, allowSleep) { + + var dt_inv = 1 / dt; + + this.stepCount++; + + // Generate contact & contactSolver + this.contactSolvers = this.genTemporalContactSolvers(); + + // Initialize contacts & joints solver + this.initSolver(dt, dt_inv, warmStarting); + + // Intergrate velocity + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + if (!body) + { + continue; + } + + if (body.isDynamic && body.isAwake) + { + body.updateVelocity(this.gravity, dt, this.damping); + } + } + + for (var i = 0; i < this.jointArr.length; i++) + { + var joint = this.jointArr[i]; + + if (!joint) + { + continue; + } + + var body1 = joint.body1; + var body2 = joint.body2; + + var awake1 = body1.isAwake && !body1.isStatic; + var awake2 = body2.isAwake && !body2.isStatic; + + if (awake1 ^ awake2) + { + if (!awake1) + { + body1.awake(true); + } + + if (!awake2) + { + body2.awake(true); + } + } + } + + // Iterative velocity constraints solver + this.velocitySolver(vel_iteration); + + // Intergrate position + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue + } + + if (body.isDynamic && body.isAwake) + { + body.updatePosition(dt); + } + } + + // Process breakable joint + for (var i = 0; i < this.jointArr.length; i++) + { + var joint = this.jointArr[i]; + + if (!joint) + { + continue; + } + + if (joint.breakable) + { + if (joint.getReactionForce(dt_inv).lengthsq() >= joint.maxForce * joint.maxForce) + { + this.removeJoint(joint); + } + } + } + + // Iterative position constraints solver + var positionSolved = this.positionSolver(pos_iteration); + + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue; + } + + body.syncTransform(); + } + + // Post solve collision callback + for (var i = 0; i < this.contactSolvers.length; i++) + { + var arb = this.contactSolvers[i]; + this.postSolve(arb); + } + + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue; + } + + if (body.isDynamic && body.isAwake) + { + body.cacheData(); + } + } + + // Process sleeping + if (allowSleep) + { + var minSleepTime = 999999; + + var linTolSqr = Space.SLEEP_LINEAR_TOLERANCE * Space.SLEEP_LINEAR_TOLERANCE; + var angTolSqr = Space.SLEEP_ANGULAR_TOLERANCE * Space.SLEEP_ANGULAR_TOLERANCE; + + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue; + } + + if (!body.isDynamic) + { + continue; + } + + if (body.angularVelocity * body.angularVelocity > angTolSqr || body.velocity.dot(body.velocity) > linTolSqr) + { + body.sleepTime = 0; + minSleepTime = 0; + } + else + { + body.sleepTime += dt; + minSleepTime = Math.min(minSleepTime, body.sleepTime); + } + } + + if (positionSolved && minSleepTime >= Space.TIME_TO_SLEEP) + { + for (var i = 0; i < this.bodyArr.length; i++) + { + var body = this.bodyArr[i]; + + if (!body) + { + continue; + } + + body.awake(false); + } + } + } + } + + } + +} \ No newline at end of file diff --git a/Phaser/physics/advanced/joints/IJoint.ts b/Phaser/physics/advanced/joints/IJoint.ts new file mode 100644 index 00000000..1f0f1fb9 --- /dev/null +++ b/Phaser/physics/advanced/joints/IJoint.ts @@ -0,0 +1,43 @@ +/// +/// +/// +/// +/// + +/** +* Phaser - Advanced Physics - Joint +* +* Based on the work Ju Hyung Lee started in JS PhyRus. +*/ + +module Phaser.Physics.Advanced { + + export interface IJoint { + + id: number; + type: number; + + body1: Phaser.Physics.Advanced.Body; + body2: Phaser.Physics.Advanced.Body; + + collideConnected; // bool? + maxForce: number; + breakable: bool; + + anchor1: Phaser.Vec2; + anchor2: Phaser.Vec2; + + getWorldAnchor1(); + getWorldAnchor2(); + setWorldAnchor1(anchor1); + setWorldAnchor2(anchor2); + + initSolver(dt, warmStarting); + solveVelocityConstraints(); + solvePositionConstraints(); + getReactionForce(dt_inv); + + } + +} + diff --git a/Tests/phaser.js b/Tests/phaser.js index e2525f58..fb07542c 100644 --- a/Tests/phaser.js +++ b/Tests/phaser.js @@ -19113,6 +19113,7 @@ var Phaser; var Manager = (function () { function Manager(game) { this.game = game; + Manager.collision = new Advanced.Collision(); } Manager.SHAPE_TYPE_CIRCLE = 0; Manager.SHAPE_TYPE_SEGMENT = 1; @@ -19424,337 +19425,6 @@ var Phaser; var Physics = Phaser.Physics; })(Phaser || (Phaser = {})); var Phaser; -(function (Phaser) { - (function (Physics) { - /// - /// - /// - /// - /// - /// - /// - /** - * Phaser - Advanced Physics - Body - * - * Based on the work Ju Hyung Lee started in JS PhyRus. - */ - (function (Advanced) { - var Body = (function () { - function Body(sprite, type) { - // Shapes - this.shapes = []; - // Joints - this.joints = []; - this.jointHash = { - }; - this.fixedRotation = false; - this.categoryBits = 0x0001; - this.maskBits = 0xFFFF; - this.stepCount = 0; - this.sprite = sprite; - this.game = sprite.game; - this.id = Phaser.Physics.Advanced.Manager.bodyCounter++; - this.name = 'body' + this.id; - this.type = type; - this.position = new Phaser.Vec2(sprite.x, sprite.y); - this.angle = sprite.rotation; - this.transform = new Phaser.Transform(this.position, this.angle); - this.centroid = new Phaser.Vec2(); - this.velocity = new Phaser.Vec2(); - this.force = new Phaser.Vec2(); - this.angularVelocity = 0; - this.torque = 0; - this.linearDamping = 0; - this.angularDamping = 0; - this.sleepTime = 0; - this.awaked = false; - this.shapes = []; - this.joints = []; - this.jointHash = { - }; - this.bounds = new Advanced.Bounds(); - this.fixedRotation = false; - this.categoryBits = 0x0001; - this.maskBits = 0xFFFF; - this.stepCount = 0; - } - Object.defineProperty(Body.prototype, "isDisabled", { - get: // duplicate = Util function - // serialize = Util function - function () { - return this.type == Phaser.Types.BODY_DISABLED ? true : false; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(Body.prototype, "isStatic", { - get: function () { - return this.type == Phaser.Types.BODY_STATIC ? true : false; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(Body.prototype, "isKinetic", { - get: function () { - return this.type == Phaser.Types.BODY_KINETIC ? true : false; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(Body.prototype, "isDynamic", { - get: function () { - return this.type == Phaser.Types.BODY_DYNAMIC ? true : false; - }, - enumerable: true, - configurable: true - }); - Body.prototype.setType = function (type) { - if(type == this.type) { - return; - } - this.force.setTo(0, 0); - this.velocity.setTo(0, 0); - this.torque = 0; - this.angularVelocity = 0; - this.type = type; - this.awake(true); - }; - Body.prototype.addShape = function (shape) { - // Check not already part of this body - shape.body = this; - this.shapes.push(shape); - }; - Body.prototype.removeShape = function (shape) { - var index = this.shapes.indexOf(shape); - if(index != -1) { - this.shapes.splice(index, 1); - shape.body = undefined; - } - }; - Body.prototype.setMass = function (mass) { - this.mass = mass; - this.massInverted = mass > 0 ? 1 / mass : 0; - }; - Body.prototype.setInertia = function (inertia) { - this.inertia = inertia; - this.inertiaInverted = inertia > 0 ? 1 / inertia : 0; - }; - Body.prototype.setTransform = function (pos, angle) { - this.transform.setTo(pos, angle); - this.position = this.transform.transform(this.centroid); - this.angle = angle; - }; - Body.prototype.syncTransform = function () { - this.transform.setRotation(this.angle); - // this.transform.setPosition(vec2.sub(this.position, this.transform.rotate(this.centroid))); - Phaser.Vec2Utils.subtract(this.position, this.transform.rotate(this.centroid), this.transform.t); - }; - Body.prototype.getWorldPoint = function (p) { - // This is returning a new vector - check it's actually used in that way - return this.transform.transform(p); - }; - Body.prototype.getWorldVector = function (v) { - return this.transform.rotate(v); - }; - Body.prototype.getLocalPoint = function (p) { - return this.transform.untransform(p); - }; - Body.prototype.getLocalVector = function (v) { - return this.transform.unrotate(v); - }; - Body.prototype.setFixedRotation = function (flag) { - this.fixedRotation = flag; - this.resetMassData(); - }; - Body.prototype.resetMassData = function () { - this.centroid.setTo(0, 0); - this.mass = 0; - this.massInverted = 0; - this.inertia = 0; - this.inertiaInverted = 0; - if(this.isDynamic == false) { - this.position.copyFrom(this.transform.transform(this.centroid)); - return; - } - var totalMassCentroid = new Phaser.Vec2(0, 0); - var totalMass = 0; - var totalInertia = 0; - for(var i = 0; i < this.shapes.length; i++) { - var shape = this.shapes[i]; - var centroid = shape.centroid(); - var mass = shape.area() * shape.density; - var inertia = shape.inertia(mass); - totalMassCentroid.multiplyAddByScalar(centroid, mass); - totalMass += mass; - totalInertia += inertia; - } - //this.centroid.copy(vec2.scale(totalMassCentroid, 1 / totalMass)); - Phaser.Vec2Utils.scale(totalMassCentroid, 1 / totalMass, this.centroid); - this.setMass(totalMass); - if(!this.fixedRotation) { - //this.setInertia(totalInertia - totalMass * vec2.dot(this.centroid, this.centroid)); - this.setInertia(totalInertia - totalMass * Phaser.Vec2Utils.dot(this.centroid, this.centroid)); - } - //console.log("mass = " + this.m + " inertia = " + this.i); - // Move center of mass - var oldPosition = Phaser.Vec2Utils.clone(this.position); - this.position = this.transform.transform(this.centroid); - // Update center of mass velocity - //this.velocity.mad(vec2.perp(vec2.sub(this.position, old_p)), this.angularVelocity); - oldPosition.subtract(this.position); - this.velocity.multiplyAddByScalar(Phaser.Vec2Utils.perp(oldPosition, oldPosition), this.angularVelocity); - }; - Body.prototype.resetJointAnchors = function () { - for(var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - if(!joint) { - continue; - } - var anchor1 = joint.getWorldAnchor1(); - var anchor2 = joint.getWorldAnchor2(); - joint.setWorldAnchor1(anchor1); - joint.setWorldAnchor2(anchor2); - } - }; - Body.prototype.cacheData = function () { - this.bounds.clear(); - for(var i = 0; i < this.shapes.length; i++) { - var shape = this.shapes[i]; - shape.cacheData(this.transform); - this.bounds.addBounds(shape.bounds); - } - }; - Body.prototype.updateVelocity = function (gravity, dt, damping) { - // this.velocity = vec2.mad(this.velocity, vec2.mad(gravity, this.force, this.massInverted), dt); - Phaser.Vec2Utils.multiplyAdd(gravity, this.force, this.massInverted, this._tempVec2); - Phaser.Vec2Utils.multiplyAdd(this.velocity, this._tempVec2, dt, this.velocity); - this.angularVelocity = this.angularVelocity + this.torque * this.inertiaInverted * dt; - // Apply damping. - // ODE: dv/dt + c * v = 0 - // Solution: v(t) = v0 * exp(-c * t) - // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt) - // v2 = exp(-c * dt) * v1 - // Taylor expansion: - // v2 = (1.0f - c * dt) * v1 - this.velocity.scale(this.game.math.clamp(1 - dt * (damping + this.linearDamping), 0, 1)); - this.angularVelocity *= this.game.math.clamp(1 - dt * (damping + this.angularDamping), 0, 1); - this.force.setTo(0, 0); - this.torque = 0; - }; - Body.prototype.updatePosition = function (dt) { - //this.position.addself(vec2.scale(this.velocity, dt)); - this.position.add(Phaser.Vec2Utils.scale(this.velocity, dt, this._tempVec2)); - this.angle += this.angularVelocity * dt; - }; - Body.prototype.resetForce = function () { - this.force.setTo(0, 0); - this.torque = 0; - }; - Body.prototype.applyForce = function (force, p) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.force.add(force); - // this.f.addself(force); - // this.torque += vec2.cross(vec2.sub(p, this.p), force); - Phaser.Vec2Utils.subtract(p, this.position, this._tempVec2); - this.torque += Phaser.Vec2Utils.cross(this._tempVec2, force); - }; - Body.prototype.applyForceToCenter = function (force) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.force.add(force); - }; - Body.prototype.applyTorque = function (torque) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.torque += torque; - }; - Body.prototype.applyLinearImpulse = function (impulse, p) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.velocity.multiplyAddByScalar(impulse, this.massInverted); - // this.angularVelocity += vec2.cross(vec2.sub(p, this.position), impulse) * this.inertiaInverted; - Phaser.Vec2Utils.subtract(p, this.position, this._tempVec2); - this.angularVelocity += Phaser.Vec2Utils.cross(this._tempVec2, impulse) * this.inertiaInverted; - }; - Body.prototype.applyAngularImpulse = function (impulse) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.angularVelocity += impulse * this.inertiaInverted; - }; - Body.prototype.kineticEnergy = function () { - var vsq = this.velocity.dot(this.velocity); - var wsq = this.angularVelocity * this.angularVelocity; - return 0.5 * (this.mass * vsq + this.inertia * wsq); - }; - Object.defineProperty(Body.prototype, "isAwake", { - get: function () { - return this.awaked; - }, - enumerable: true, - configurable: true - }); - Body.prototype.awake = function (flag) { - this.awaked = flag; - if(flag) { - this.sleepTime = 0; - } else { - this.velocity.setTo(0, 0); - this.angularVelocity = 0; - this.force.setTo(0, 0); - this.torque = 0; - } - }; - Body.prototype.isCollidable = function (other) { - if(this == other) { - return false; - } - if(this.isDynamic == false && other.isDynamic == false) { - return false; - } - if(!(this.maskBits & other.categoryBits) || !(other.maskBits & this.categoryBits)) { - return false; - } - for(var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - if(!joint) { - continue; - } - if(!joint.collideConnected && other.jointHash[joint.id] != undefined) { - return false; - } - } - return true; - }; - return Body; - })(); - Advanced.Body = Body; - })(Physics.Advanced || (Physics.Advanced = {})); - var Advanced = Physics.Advanced; - })(Phaser.Physics || (Phaser.Physics = {})); - var Physics = Phaser.Physics; -})(Phaser || (Phaser = {})); -var Phaser; (function (Phaser) { (function (Physics) { /// @@ -19818,6 +19488,254 @@ var Phaser; var Physics = Phaser.Physics; })(Phaser || (Phaser = {})); var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /// + /// + /// + /// + /// + /// + /** + * Phaser - Advanced Physics - ContactSolver + * + * Based on the work Ju Hyung Lee started in JS PhyRus. + */ + //------------------------------------------------------------------------------------------------- + // Contact Constraint + // + // Non-penetration constraint: + // C = dot(p2 - p1, n) + // Cdot = dot(v2 - v1, n) + // J = [ -n, -cross(r1, n), n, cross(r2, n) ] + // + // impulse = JT * lambda = [ -n * lambda, -cross(r1, n) * lambda, n * lambda, cross(r1, n) * lambda ] + // + // Friction constraint: + // C = dot(p2 - p1, t) + // Cdot = dot(v2 - v1, t) + // J = [ -t, -cross(r1, t), t, cross(r2, t) ] + // + // impulse = JT * lambda = [ -t * lambda, -cross(r1, t) * lambda, t * lambda, cross(r1, t) * lambda ] + // + // NOTE: lambda is an impulse in constraint space. + //------------------------------------------------------------------------------------------------- + (function (Advanced) { + var ContactSolver = (function () { + function ContactSolver(shape1, shape2) { + this.shape1 = shape1; + this.shape2 = shape2; + this.contacts = []; + this.elasticity = 1; + this.friction = 1; + } + ContactSolver.prototype.update = function (newContactArr) { + for(var i = 0; i < newContactArr.length; i++) { + var newContact = newContactArr[i]; + var k = -1; + for(var j = 0; j < this.contacts.length; j++) { + if(newContact.hash == this.contacts[j].hash) { + k = j; + break; + } + } + if(k > -1) { + newContact.lambdaNormal = this.contacts[k].lambdaNormal; + newContact.lambdaTangential = this.contacts[k].lambdaTangential; + } + } + this.contacts = newContactArr; + }; + ContactSolver.prototype.initSolver = function (dt_inv) { + var body1 = this.shape1.body; + var body2 = this.shape2.body; + var sum_m_inv = body1.massInverted + body2.massInverted; + for(var i = 0; i < this.contacts.length; i++) { + var con = this.contacts[i]; + // Transformed r1, r2 + Phaser.Vec2Utils.subtract(con.point, body1.position, con.r1); + Phaser.Vec2Utils.subtract(con.point, body2.position, con.r2); + //con.r1 = vec2.sub(con.point, body1.p); + //con.r2 = vec2.sub(con.point, body2.p); + // Local r1, r2 + con.r1_local = body1.transform.unrotate(con.r1); + con.r2_local = body2.transform.unrotate(con.r2); + var n = con.normal; + var t = Phaser.Vec2Utils.perp(con.normal); + // invEMn = J * invM * JT + // J = [ -n, -cross(r1, n), n, cross(r2, n) ] + var sn1 = Phaser.Vec2Utils.cross(con.r1, n); + var sn2 = Phaser.Vec2Utils.cross(con.r2, n); + var emn_inv = sum_m_inv + body1.inertiaInverted * sn1 * sn1 + body2.inertiaInverted * sn2 * sn2; + con.emn = emn_inv == 0 ? 0 : 1 / emn_inv; + // invEMt = J * invM * JT + // J = [ -t, -cross(r1, t), t, cross(r2, t) ] + var st1 = Phaser.Vec2Utils.cross(con.r1, t); + var st2 = Phaser.Vec2Utils.cross(con.r2, t); + var emt_inv = sum_m_inv + body1.inertiaInverted * st1 * st1 + body2.inertiaInverted * st2 * st2; + con.emt = emt_inv == 0 ? 0 : 1 / emt_inv; + // Linear velocities at contact point + // in 2D: cross(w, r) = perp(r) * w + var v1 = new Phaser.Vec2(); + var v2 = new Phaser.Vec2(); + Phaser.Vec2Utils.multiplyAdd(body1.velocity, Phaser.Vec2Utils.perp(con.r1), body1.angularVelocity, v1); + Phaser.Vec2Utils.multiplyAdd(body2.velocity, Phaser.Vec2Utils.perp(con.r2), body2.angularVelocity, v2); + //var v1 = vec2.mad(body1.v, vec2.perp(con.r1), body1.w); + //var v2 = vec2.mad(body2.v, vec2.perp(con.r2), body2.w); + // relative velocity at contact point + var rv = new Phaser.Vec2(); + Phaser.Vec2Utils.subtract(v2, v1, rv); + //var rv = vec2.sub(v2, v1); + // bounce velocity dot n + con.bounce = Phaser.Vec2Utils.dot(rv, con.normal) * this.elasticity; + } + }; + ContactSolver.prototype.warmStart = function () { + var body1 = this.shape1.body; + var body2 = this.shape2.body; + for(var i = 0; i < this.contacts.length; i++) { + var con = this.contacts[i]; + var n = con.normal; + var lambda_n = con.lambdaNormal; + var lambda_t = con.lambdaTangential; + // Apply accumulated impulses + //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n); + //var impulse = new vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); + var impulse = new Phaser.Vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); + body1.velocity.multiplyAddByScalar(impulse, -body1.massInverted); + //body1.v.mad(impulse, -body1.m_inv); + body1.angularVelocity -= Phaser.Vec2Utils.cross(con.r1, impulse) * body1.inertiaInverted; + //body1.w -= vec2.cross(con.r1, impulse) * body1.i_inv; + body2.velocity.multiplyAddByScalar(impulse, -body2.massInverted); + //body2.v.mad(impulse, body2.m_inv); + body2.angularVelocity -= Phaser.Vec2Utils.cross(con.r2, impulse) * body2.inertiaInverted; + //body2.w += vec2.cross(con.r2, impulse) * body2.i_inv; + } + }; + ContactSolver.prototype.solveVelocityConstraints = function () { + var body1 = this.shape1.body; + var body2 = this.shape2.body; + var m1_inv = body1.massInverted; + var i1_inv = body1.inertiaInverted; + var m2_inv = body2.massInverted; + var i2_inv = body2.inertiaInverted; + for(var i = 0; i < this.contacts.length; i++) { + var con = this.contacts[i]; + var n = con.normal; + var t = Phaser.Vec2Utils.perp(n); + var r1 = con.r1; + var r2 = con.r2; + // Linear velocities at contact point + // in 2D: cross(w, r) = perp(r) * w + var v1 = new Phaser.Vec2(); + var v2 = new Phaser.Vec2(); + Phaser.Vec2Utils.multiplyAdd(body1.velocity, Phaser.Vec2Utils.perp(r1), body1.angularVelocity, v1); + //var v1 = vec2.mad(body1.v, vec2.perp(r1), body1.w); + Phaser.Vec2Utils.multiplyAdd(body2.velocity, Phaser.Vec2Utils.perp(r2), body2.angularVelocity, v2); + //var v2 = vec2.mad(body2.v, vec2.perp(r2), body2.w); + // Relative velocity at contact point + var rv = new Phaser.Vec2(); + Phaser.Vec2Utils.subtract(v2, v1, rv); + //var rv = vec2.sub(v2, v1); + // Compute normal constraint impulse + adding bounce as a velocity bias + // lambda_n = -EMn * J * V + var lambda_n = -con.emn * (Phaser.Vec2Utils.dot(n, rv) + con.bounce); + // Accumulate and clamp + var lambda_n_old = con.lambdaNormal; + con.lambdaNormal = Math.max(lambda_n_old + lambda_n, 0); + lambda_n = con.lambdaNormal - lambda_n_old; + // Compute frictional constraint impulse + // lambda_t = -EMt * J * V + var lambda_t = -con.emt * Phaser.Vec2Utils.dot(t, rv); + // Max friction constraint impulse (Coulomb's Law) + var lambda_t_max = con.lambdaNormal * this.friction; + // Accumulate and clamp + var lambda_t_old = con.lambdaTangential; + con.lambdaTangential = this.clamp(lambda_t_old + lambda_t, -lambda_t_max, lambda_t_max); + lambda_t = con.lambdaTangential - lambda_t_old; + // Apply the final impulses + //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n); + var impulse = new Phaser.Vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); + body1.velocity.multiplyAddByScalar(impulse, -m1_inv); + //body1.v.mad(impulse, -m1_inv); + body1.angularVelocity -= Phaser.Vec2Utils.cross(r1, impulse) * i1_inv; + //body1.w -= vec2.cross(r1, impulse) * i1_inv; + body2.velocity.multiplyAddByScalar(impulse, m2_inv); + //body2.v.mad(impulse, m2_inv); + body1.angularVelocity += Phaser.Vec2Utils.cross(r2, impulse) * i2_inv; + //body2.w += vec2.cross(r2, impulse) * i2_inv; + } + }; + ContactSolver.prototype.solvePositionConstraints = function () { + var body1 = this.shape1.body; + var body2 = this.shape2.body; + var m1_inv = body1.massInverted; + var i1_inv = body1.inertiaInverted; + var m2_inv = body2.massInverted; + var i2_inv = body2.inertiaInverted; + var sum_m_inv = m1_inv + m2_inv; + var max_penetration = 0; + for(var i = 0; i < this.contacts.length; i++) { + var con = this.contacts[i]; + var n = con.normal; + var r1 = new Phaser.Vec2(); + var r2 = new Phaser.Vec2(); + // Transformed r1, r2 + Phaser.Vec2Utils.rotate(con.r1_local, body1.angle, r1); + //var r1 = vec2.rotate(con.r1_local, body1.a); + Phaser.Vec2Utils.rotate(con.r2_local, body2.angle, r2); + //var r2 = vec2.rotate(con.r2_local, body2.a); + // Contact points (corrected) + var p1 = new Phaser.Vec2(); + var p2 = new Phaser.Vec2(); + Phaser.Vec2Utils.add(body1.position, r1, p1); + //var p1 = vec2.add(body1.p, r1); + Phaser.Vec2Utils.add(body2.position, r2, p2); + //var p2 = vec2.add(body2.p, r2); + // Corrected delta vector + var dp = new Phaser.Vec2(); + Phaser.Vec2Utils.subtract(p2, p1); + //var dp = vec2.sub(p2, p1); + // Position constraint + var c = Phaser.Vec2Utils.dot(dp, n) + con.depth; + var correction = this.clamp(Advanced.Manager.CONTACT_SOLVER_BAUMGARTE * (c + Advanced.Manager.CONTACT_SOLVER_COLLISION_SLOP), -Advanced.Manager.CONTACT_SOLVER_MAX_LINEAR_CORRECTION, 0); + if(correction == 0) { + continue; + } + // We don't need max_penetration less than or equal slop + max_penetration = Math.max(max_penetration, -c); + // Compute lambda for position constraint + // Solve (J * invM * JT) * lambda = -C / dt + var sn1 = Phaser.Vec2Utils.cross(r1, n); + var sn2 = Phaser.Vec2Utils.cross(r2, n); + var em_inv = sum_m_inv + body1.inertiaInverted * sn1 * sn1 + body2.inertiaInverted * sn2 * sn2; + var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv; + // Apply correction impulses + var impulse_dt = new Phaser.Vec2(); + Phaser.Vec2Utils.scale(n, lambda_dt, impulse_dt); + //var impulse_dt = vec2.scale(n, lambda_dt); + body1.position.multiplyAddByScalar(impulse_dt, -m1_inv); + //body1.p.mad(impulse_dt, -m1_inv); + body1.angle -= sn1 * lambda_dt * i1_inv; + body2.position.multiplyAddByScalar(impulse_dt, m2_inv); + //body2.p.mad(impulse_dt, m2_inv); + body2.angle += sn2 * lambda_dt * i2_inv; + } + return max_penetration <= Advanced.Manager.CONTACT_SOLVER_COLLISION_SLOP * 3; + }; + ContactSolver.prototype.clamp = function (v, min, max) { + return v < min ? min : (v > max ? max : v); + }; + return ContactSolver; + })(); + Advanced.ContactSolver = ContactSolver; + })(Physics.Advanced || (Physics.Advanced = {})); + var Advanced = Physics.Advanced; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; (function (Phaser) { (function (Physics) { /// @@ -20268,6 +20186,12 @@ var Phaser; var Physics = Phaser.Physics; })(Phaser || (Phaser = {})); var Phaser; +(function (Phaser) { + (function (Physics) { + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; (function (Phaser) { (function (Physics) { /// @@ -20276,240 +20200,846 @@ var Phaser; /// /// /// + /// /// + /// + /// /** - * Phaser - Advanced Physics - ContactSolver + * Phaser - Advanced Physics - Space * * Based on the work Ju Hyung Lee started in JS PhyRus. */ - //------------------------------------------------------------------------------------------------- - // Contact Constraint - // - // Non-penetration constraint: - // C = dot(p2 - p1, n) - // Cdot = dot(v2 - v1, n) - // J = [ -n, -cross(r1, n), n, cross(r2, n) ] - // - // impulse = JT * lambda = [ -n * lambda, -cross(r1, n) * lambda, n * lambda, cross(r1, n) * lambda ] - // - // Friction constraint: - // C = dot(p2 - p1, t) - // Cdot = dot(v2 - v1, t) - // J = [ -t, -cross(r1, t), t, cross(r2, t) ] - // - // impulse = JT * lambda = [ -t * lambda, -cross(r1, t) * lambda, t * lambda, cross(r1, t) * lambda ] - // - // NOTE: lambda is an impulse in constraint space. - //------------------------------------------------------------------------------------------------- (function (Advanced) { - var ContactSolver = (function () { - function ContactSolver(shape1, shape2) { - this.shape1 = shape1; - this.shape2 = shape2; - this.contacts = []; - this.elasticity = 1; - this.friction = 1; + var Space = (function () { + function Space() { + this.stepCount = 0; + this.bodyArr = []; + this.bodyHash = { + }; + this.jointArr = []; + this.jointHash = { + }; + this.numContacts = 0; + this.contactSolvers = []; + //this.postSolve(arb) { }; + this.gravity = new Phaser.Vec2(); + this.damping = 0; } - ContactSolver.prototype.update = function (newContactArr) { - for(var i = 0; i < newContactArr.length; i++) { - var newContact = newContactArr[i]; - var k = -1; - for(var j = 0; j < this.contacts.length; j++) { - if(newContact.hash == this.contacts[j].hash) { - k = j; + Space.TIME_TO_SLEEP = 0.5; + Space.SLEEP_LINEAR_TOLERANCE = 0.5; + Space.SLEEP_ANGULAR_TOLERANCE = 2 * Phaser.GameMath.DEG_TO_RAD; + Space.prototype.clear = function () { + Advanced.Manager.shapeCounter = 0; + Advanced.Manager.bodyCounter = 0; + Advanced.Manager.jointCounter = 0; + for(var i = 0; i < this.bodyArr.length; i++) { + if(this.bodyArr[i]) { + this.removeBody(this.bodyArr[i]); + } + } + this.bodyArr = []; + this.bodyHash = { + }; + this.jointArr = []; + this.jointHash = { + }; + this.contactSolvers = []; + this.stepCount = 0; + }; + Space.prototype.addBody = function (body) { + if(this.bodyHash[body.id] != undefined) { + return; + } + var index = this.bodyArr.push(body) - 1; + this.bodyHash[body.id] = index; + body.awake(true); + body.space = this; + body.cacheData(); + }; + Space.prototype.removeBody = function (body) { + if(this.bodyHash[body.id] == undefined) { + return; + } + // Remove linked joint + for(var i = 0; i < body.joints.length; i++) { + if(body.joints[i]) { + this.removeJoint(body.joints[i]); + } + } + body.space = null; + var index = this.bodyHash[body.id]; + delete this.bodyHash[body.id]; + delete this.bodyArr[index]; + }; + Space.prototype.addJoint = function (joint) { + if(this.jointHash[joint.id] != undefined) { + return; + } + joint.body1.awake(true); + joint.body2.awake(true); + var index = this.jointArr.push(joint) - 1; + this.jointHash[joint.id] = index; + var index = joint.body1.joints.push(joint) - 1; + joint.body1.jointHash[joint.id] = index; + var index = joint.body2.joints.push(joint) - 1; + joint.body2.jointHash[joint.id] = index; + }; + Space.prototype.removeJoint = function (joint) { + if(this.jointHash[joint.id] == undefined) { + return; + } + joint.body1.awake(true); + joint.body2.awake(true); + var index = joint.body1.jointHash[joint.id]; + delete joint.body1.jointHash[joint.id]; + delete joint.body1.joints[index]; + var index = joint.body2.jointHash[joint.id]; + delete joint.body2.jointHash[joint.id]; + delete joint.body2.joints[index]; + var index = this.jointHash[joint.id]; + delete this.jointHash[joint.id]; + delete this.jointArr[index]; + }; + Space.prototype.findShapeByPoint = function (p, refShape) { + var firstShape; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + for(var j = 0; j < body.shapes.length; j++) { + var shape = body.shapes[j]; + if(shape.pointQuery(p)) { + if(!refShape) { + return shape; + } + if(!firstShape) { + firstShape = shape; + } + if(shape == refShape) { + refShape = null; + } + } + } + } + return firstShape; + }; + Space.prototype.findBodyByPoint = function (p, refBody) { + var firstBody; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + for(var j = 0; j < body.shapes.length; j++) { + var shape = body.shapes[j]; + if(shape.pointQuery(p)) { + if(!refBody) { + return shape.body; + } + if(!firstBody) { + firstBody = shape.body; + } + if(shape.body == refBody) { + refBody = null; + } break; } } - if(k > -1) { - newContact.lambdaNormal = this.contacts[k].lambdaNormal; - newContact.lambdaTangential = this.contacts[k].lambdaTangential; - } } - this.contacts = newContactArr; + return firstBody; }; - ContactSolver.prototype.initSolver = function (dt_inv) { - var body1 = this.shape1.body; - var body2 = this.shape2.body; - var sum_m_inv = body1.massInverted + body2.massInverted; - for(var i = 0; i < this.contacts.length; i++) { - var con = this.contacts[i]; - // Transformed r1, r2 - Phaser.Vec2Utils.subtract(con.point, body1.position, con.r1); - Phaser.Vec2Utils.subtract(con.point, body2.position, con.r2); - //con.r1 = vec2.sub(con.point, body1.p); - //con.r2 = vec2.sub(con.point, body2.p); - // Local r1, r2 - con.r1_local = body1.transform.unrotate(con.r1); - con.r2_local = body2.transform.unrotate(con.r2); - var n = con.normal; - var t = Phaser.Vec2Utils.perp(con.normal); - // invEMn = J * invM * JT - // J = [ -n, -cross(r1, n), n, cross(r2, n) ] - var sn1 = Phaser.Vec2Utils.cross(con.r1, n); - var sn2 = Phaser.Vec2Utils.cross(con.r2, n); - var emn_inv = sum_m_inv + body1.inertiaInverted * sn1 * sn1 + body2.inertiaInverted * sn2 * sn2; - con.emn = emn_inv == 0 ? 0 : 1 / emn_inv; - // invEMt = J * invM * JT - // J = [ -t, -cross(r1, t), t, cross(r2, t) ] - var st1 = Phaser.Vec2Utils.cross(con.r1, t); - var st2 = Phaser.Vec2Utils.cross(con.r2, t); - var emt_inv = sum_m_inv + body1.inertiaInverted * st1 * st1 + body2.inertiaInverted * st2 * st2; - con.emt = emt_inv == 0 ? 0 : 1 / emt_inv; - // Linear velocities at contact point - // in 2D: cross(w, r) = perp(r) * w - var v1 = new Phaser.Vec2(); - var v2 = new Phaser.Vec2(); - Phaser.Vec2Utils.multiplyAdd(body1.velocity, Phaser.Vec2Utils.perp(con.r1), body1.angularVelocity, v1); - Phaser.Vec2Utils.multiplyAdd(body2.velocity, Phaser.Vec2Utils.perp(con.r2), body2.angularVelocity, v2); - //var v1 = vec2.mad(body1.v, vec2.perp(con.r1), body1.w); - //var v2 = vec2.mad(body2.v, vec2.perp(con.r2), body2.w); - // relative velocity at contact point - var rv = new Phaser.Vec2(); - Phaser.Vec2Utils.subtract(v2, v1, rv); - //var rv = vec2.sub(v2, v1); - // bounce velocity dot n - con.bounce = Phaser.Vec2Utils.dot(rv, con.normal) * this.elasticity; - } - }; - ContactSolver.prototype.warmStart = function () { - var body1 = this.shape1.body; - var body2 = this.shape2.body; - for(var i = 0; i < this.contacts.length; i++) { - var con = this.contacts[i]; - var n = con.normal; - var lambda_n = con.lambdaNormal; - var lambda_t = con.lambdaTangential; - // Apply accumulated impulses - //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n); - //var impulse = new vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); - var impulse = new Phaser.Vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); - body1.velocity.multiplyAddByScalar(impulse, -body1.massInverted); - //body1.v.mad(impulse, -body1.m_inv); - body1.angularVelocity -= Phaser.Vec2Utils.cross(con.r1, impulse) * body1.inertiaInverted; - //body1.w -= vec2.cross(con.r1, impulse) * body1.i_inv; - body2.velocity.multiplyAddByScalar(impulse, -body2.massInverted); - //body2.v.mad(impulse, body2.m_inv); - body2.angularVelocity -= Phaser.Vec2Utils.cross(con.r2, impulse) * body2.inertiaInverted; - //body2.w += vec2.cross(con.r2, impulse) * body2.i_inv; - } - }; - ContactSolver.prototype.solveVelocityConstraints = function () { - var body1 = this.shape1.body; - var body2 = this.shape2.body; - var m1_inv = body1.massInverted; - var i1_inv = body1.inertiaInverted; - var m2_inv = body2.massInverted; - var i2_inv = body2.inertiaInverted; - for(var i = 0; i < this.contacts.length; i++) { - var con = this.contacts[i]; - var n = con.normal; - var t = Phaser.Vec2Utils.perp(n); - var r1 = con.r1; - var r2 = con.r2; - // Linear velocities at contact point - // in 2D: cross(w, r) = perp(r) * w - var v1 = new Phaser.Vec2(); - var v2 = new Phaser.Vec2(); - Phaser.Vec2Utils.multiplyAdd(body1.velocity, Phaser.Vec2Utils.perp(r1), body1.angularVelocity, v1); - //var v1 = vec2.mad(body1.v, vec2.perp(r1), body1.w); - Phaser.Vec2Utils.multiplyAdd(body2.velocity, Phaser.Vec2Utils.perp(r2), body2.angularVelocity, v2); - //var v2 = vec2.mad(body2.v, vec2.perp(r2), body2.w); - // Relative velocity at contact point - var rv = new Phaser.Vec2(); - Phaser.Vec2Utils.subtract(v2, v1, rv); - //var rv = vec2.sub(v2, v1); - // Compute normal constraint impulse + adding bounce as a velocity bias - // lambda_n = -EMn * J * V - var lambda_n = -con.emn * (Phaser.Vec2Utils.dot(n, rv) + con.bounce); - // Accumulate and clamp - var lambda_n_old = con.lambdaNormal; - con.lambdaNormal = Math.max(lambda_n_old + lambda_n, 0); - lambda_n = con.lambdaNormal - lambda_n_old; - // Compute frictional constraint impulse - // lambda_t = -EMt * J * V - var lambda_t = -con.emt * Phaser.Vec2Utils.dot(t, rv); - // Max friction constraint impulse (Coulomb's Law) - var lambda_t_max = con.lambdaNormal * this.friction; - // Accumulate and clamp - var lambda_t_old = con.lambdaTangential; - con.lambdaTangential = this.clamp(lambda_t_old + lambda_t, -lambda_t_max, lambda_t_max); - lambda_t = con.lambdaTangential - lambda_t_old; - // Apply the final impulses - //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n); - var impulse = new Phaser.Vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); - body1.velocity.multiplyAddByScalar(impulse, -m1_inv); - //body1.v.mad(impulse, -m1_inv); - body1.angularVelocity -= Phaser.Vec2Utils.cross(r1, impulse) * i1_inv; - //body1.w -= vec2.cross(r1, impulse) * i1_inv; - body2.velocity.multiplyAddByScalar(impulse, m2_inv); - //body2.v.mad(impulse, m2_inv); - body1.angularVelocity += Phaser.Vec2Utils.cross(r2, impulse) * i2_inv; - //body2.w += vec2.cross(r2, impulse) * i2_inv; - } - }; - ContactSolver.prototype.solvePositionConstraints = function () { - var body1 = this.shape1.body; - var body2 = this.shape2.body; - var m1_inv = body1.massInverted; - var i1_inv = body1.inertiaInverted; - var m2_inv = body2.massInverted; - var i2_inv = body2.inertiaInverted; - var sum_m_inv = m1_inv + m2_inv; - var max_penetration = 0; - for(var i = 0; i < this.contacts.length; i++) { - var con = this.contacts[i]; - var n = con.normal; - var r1 = new Phaser.Vec2(); - var r2 = new Phaser.Vec2(); - // Transformed r1, r2 - Phaser.Vec2Utils.rotate(con.r1_local, body1.angle, r1); - //var r1 = vec2.rotate(con.r1_local, body1.a); - Phaser.Vec2Utils.rotate(con.r2_local, body2.angle, r2); - //var r2 = vec2.rotate(con.r2_local, body2.a); - // Contact points (corrected) - var p1 = new Phaser.Vec2(); - var p2 = new Phaser.Vec2(); - Phaser.Vec2Utils.add(body1.position, r1, p1); - //var p1 = vec2.add(body1.p, r1); - Phaser.Vec2Utils.add(body2.position, r2, p2); - //var p2 = vec2.add(body2.p, r2); - // Corrected delta vector - var dp = new Phaser.Vec2(); - Phaser.Vec2Utils.subtract(p2, p1); - //var dp = vec2.sub(p2, p1); - // Position constraint - var c = Phaser.Vec2Utils.dot(dp, n) + con.depth; - var correction = this.clamp(Advanced.Manager.CONTACT_SOLVER_BAUMGARTE * (c + Advanced.Manager.CONTACT_SOLVER_COLLISION_SLOP), -Advanced.Manager.CONTACT_SOLVER_MAX_LINEAR_CORRECTION, 0); - if(correction == 0) { + Space.prototype.shapeById = // TODO: Replace this function to shape hashing + function (id) { + var shape; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { continue; } - // We don't need max_penetration less than or equal slop - max_penetration = Math.max(max_penetration, -c); - // Compute lambda for position constraint - // Solve (J * invM * JT) * lambda = -C / dt - var sn1 = Phaser.Vec2Utils.cross(r1, n); - var sn2 = Phaser.Vec2Utils.cross(r2, n); - var em_inv = sum_m_inv + body1.inertiaInverted * sn1 * sn1 + body2.inertiaInverted * sn2 * sn2; - var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv; - // Apply correction impulses - var impulse_dt = new Phaser.Vec2(); - Phaser.Vec2Utils.scale(n, lambda_dt, impulse_dt); - //var impulse_dt = vec2.scale(n, lambda_dt); - body1.position.multiplyAddByScalar(impulse_dt, -m1_inv); - //body1.p.mad(impulse_dt, -m1_inv); - body1.angle -= sn1 * lambda_dt * i1_inv; - body2.position.multiplyAddByScalar(impulse_dt, m2_inv); - //body2.p.mad(impulse_dt, m2_inv); - body2.angle += sn2 * lambda_dt * i2_inv; + for(var j = 0; j < body.shapes.length; j++) { + if(body.shapes[j].id == id) { + return body.shapes[j]; + } + } } - return max_penetration <= Advanced.Manager.CONTACT_SOLVER_COLLISION_SLOP * 3; + return null; }; - ContactSolver.prototype.clamp = function (v, min, max) { - return v < min ? min : (v > max ? max : v); + Space.prototype.jointById = function (id) { + var index = this.jointHash[id]; + if(index != undefined) { + return this.jointArr[index]; + } + return null; }; - return ContactSolver; + Space.prototype.findVertexByPoint = function (p, minDist, refVertexId) { + var firstVertexId = -1; + refVertexId = refVertexId || -1; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + for(var j = 0; j < body.shapes.length; j++) { + var shape = body.shapes[j]; + var index = shape.findVertexByPoint(p, minDist); + if(index != -1) { + var vertex = (shape.id << 16) | index; + if(refVertexId == -1) { + return vertex; + } + if(firstVertexId == -1) { + firstVertexId = vertex; + } + if(vertex == refVertexId) { + refVertexId = -1; + } + } + } + } + return firstVertexId; + }; + Space.prototype.findEdgeByPoint = function (p, minDist, refEdgeId) { + var firstEdgeId = -1; + refEdgeId = refEdgeId || -1; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + for(var j = 0; j < body.shapes.length; j++) { + var shape = body.shapes[j]; + if(shape.type != Advanced.Manager.SHAPE_TYPE_POLY) { + continue; + } + var index = shape.findEdgeByPoint(p, minDist); + if(index != -1) { + var edge = (shape.id << 16) | index; + if(refEdgeId == -1) { + return edge; + } + if(firstEdgeId == -1) { + firstEdgeId = edge; + } + if(edge == refEdgeId) { + refEdgeId = -1; + } + } + } + } + return firstEdgeId; + }; + Space.prototype.findJointByPoint = function (p, minDist, refJointId) { + var firstJointId = -1; + var dsq = minDist * minDist; + refJointId = refJointId || -1; + for(var i = 0; i < this.jointArr.length; i++) { + var joint = this.jointArr[i]; + if(!joint) { + continue; + } + var jointId = -1; + if(Phaser.Vec2Utils.distanceSq(p, joint.getWorldAnchor1()) < dsq) { + jointId = (joint.id << 16 | 0); + } else if(Phaser.Vec2Utils.distanceSq(p, joint.getWorldAnchor2()) < dsq) { + jointId = (joint.id << 16 | 1); + } + if(jointId != -1) { + if(refJointId == -1) { + return jointId; + } + if(firstJointId == -1) { + firstJointId = jointId; + } + if(jointId == refJointId) { + refJointId = -1; + } + } + } + return firstJointId; + }; + Space.prototype.findContactSolver = function (shape1, shape2) { + for(var i = 0; i < this.contactSolvers.length; i++) { + var contactSolver = this.contactSolvers[i]; + if(shape1 == contactSolver.shape1 && shape2 == contactSolver.shape2) { + return contactSolver; + } + } + return null; + }; + Space.prototype.genTemporalContactSolvers = function () { + //var t0 = Date.now(); + var newContactSolverArr = []; + this.numContacts = 0; + for(var body1_index = 0; body1_index < this.bodyArr.length; body1_index++) { + var body1 = this.bodyArr[body1_index]; + if(!body1) { + continue; + } + body1.stepCount = this.stepCount; + for(var body2_index = 0; body2_index < this.bodyArr.length; body2_index++) { + var body2 = this.bodyArr[body2_index]; + if(!body2) { + continue; + } + if(body1.stepCount == body2.stepCount) { + continue; + } + var active1 = body1.isAwake && !body1.isStatic; + var active2 = body2.isAwake && !body2.isStatic; + if(!active1 && !active2) { + continue; + } + if(!body1.isCollidable(body2)) { + continue; + } + if(!body1.bounds.intersectsBounds(body2.bounds)) { + continue; + } + for(var i = 0; i < body1.shapes.length; i++) { + for(var j = 0; j < body2.shapes.length; j++) { + var shape1 = body1.shapes[i]; + var shape2 = body2.shapes[j]; + var contactArr = []; + if(!Advanced.Manager.collision.collide(shape1, shape2, contactArr)) { + continue; + } + if(shape1.type > shape2.type) { + var temp = shape1; + shape1 = shape2; + shape2 = temp; + } + this.numContacts += contactArr.length; + var contactSolver = this.findContactSolver(shape1, shape2); + if(contactSolver) { + contactSolver.update(contactArr); + newContactSolverArr.push(contactSolver); + } else { + body1.awake(true); + body2.awake(true); + var newContactSolver = new Advanced.ContactSolver(shape1, shape2); + newContactSolver.contacts = contactArr; + newContactSolver.elasticity = Math.max(shape1.e, shape2.e); + newContactSolver.friction = Math.sqrt(shape1.u * shape2.u); + newContactSolverArr.push(newContactSolver); + } + } + } + } + } + //stats.timeCollision = Date.now() - t0; + return newContactSolverArr; + }; + Space.prototype.initSolver = function (dt, dt_inv, warmStarting) { + //var t0 = Date.now(); + // Initialize contact solvers + for(var i = 0; i < this.contactSolvers.length; i++) { + this.contactSolvers[i].initSolver(dt_inv); + } + // Initialize joint solver + for(var i = 0; i < this.jointArr.length; i++) { + if(this.jointArr[i]) { + this.jointArr[i].initSolver(dt, warmStarting); + } + } + // Warm starting (apply cached impulse) + if(warmStarting) { + for(var i = 0; i < this.contactSolvers.length; i++) { + this.contactSolvers[i].warmStart(); + } + } + //stats.timeInitSolver = Date.now() - t0; + }; + Space.prototype.velocitySolver = function (iteration) { + //var t0 = Date.now(); + for(var i = 0; i < iteration; i++) { + for(var j = 0; j < this.jointArr.length; j++) { + if(this.jointArr[j]) { + this.jointArr[j].solveVelocityConstraints(); + } + } + for(var j = 0; j < this.contactSolvers.length; j++) { + this.contactSolvers[j].solveVelocityConstraints(); + } + } + //stats.timeVelocitySolver = Date.now() - t0; + }; + Space.prototype.positionSolver = function (iteration) { + //var t0 = Date.now(); + var positionSolved = false; + //stats.positionIterations = 0; + for(var i = 0; i < iteration; i++) { + var contactsOk = true; + var jointsOk = true; + for(var j = 0; j < this.contactSolvers.length; j++) { + var contactOk = this.contactSolvers[j].solvePositionConstraints(); + contactsOk = contactOk && contactsOk; + } + for(var j = 0; j < this.jointArr.length; j++) { + if(this.jointArr[j]) { + var jointOk = this.jointArr[j].solvePositionConstraints(); + jointsOk = jointOk && jointsOk; + } + } + if(contactsOk && jointsOk) { + // exit early if the position errors are small + positionSolved = true; + break; + } + //stats.positionIterations++; + } + //stats.timePositionSolver = Date.now() - t0; + return positionSolved; + }; + Space.prototype.step = function (dt, vel_iteration, pos_iteration, warmStarting, allowSleep) { + var dt_inv = 1 / dt; + this.stepCount++; + // Generate contact & contactSolver + this.contactSolvers = this.genTemporalContactSolvers(); + // Initialize contacts & joints solver + this.initSolver(dt, dt_inv, warmStarting); + // Intergrate velocity + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + if(body.isDynamic && body.isAwake) { + body.updateVelocity(this.gravity, dt, this.damping); + } + } + for(var i = 0; i < this.jointArr.length; i++) { + var joint = this.jointArr[i]; + if(!joint) { + continue; + } + var body1 = joint.body1; + var body2 = joint.body2; + var awake1 = body1.isAwake && !body1.isStatic; + var awake2 = body2.isAwake && !body2.isStatic; + if(awake1 ^ awake2) { + if(!awake1) { + body1.awake(true); + } + if(!awake2) { + body2.awake(true); + } + } + } + // Iterative velocity constraints solver + this.velocitySolver(vel_iteration); + // Intergrate position + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + if(body.isDynamic && body.isAwake) { + body.updatePosition(dt); + } + } + // Process breakable joint + for(var i = 0; i < this.jointArr.length; i++) { + var joint = this.jointArr[i]; + if(!joint) { + continue; + } + if(joint.breakable) { + if(joint.getReactionForce(dt_inv).lengthsq() >= joint.maxForce * joint.maxForce) { + this.removeJoint(joint); + } + } + } + // Iterative position constraints solver + var positionSolved = this.positionSolver(pos_iteration); + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + body.syncTransform(); + } + // Post solve collision callback + for(var i = 0; i < this.contactSolvers.length; i++) { + var arb = this.contactSolvers[i]; + this.postSolve(arb); + } + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + if(body.isDynamic && body.isAwake) { + body.cacheData(); + } + } + // Process sleeping + if(allowSleep) { + var minSleepTime = 999999; + var linTolSqr = Space.SLEEP_LINEAR_TOLERANCE * Space.SLEEP_LINEAR_TOLERANCE; + var angTolSqr = Space.SLEEP_ANGULAR_TOLERANCE * Space.SLEEP_ANGULAR_TOLERANCE; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + if(!body.isDynamic) { + continue; + } + if(body.angularVelocity * body.angularVelocity > angTolSqr || body.velocity.dot(body.velocity) > linTolSqr) { + body.sleepTime = 0; + minSleepTime = 0; + } else { + body.sleepTime += dt; + minSleepTime = Math.min(minSleepTime, body.sleepTime); + } + } + if(positionSolved && minSleepTime >= Space.TIME_TO_SLEEP) { + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + body.awake(false); + } + } + } + }; + return Space; })(); - Advanced.ContactSolver = ContactSolver; + Advanced.Space = Space; + })(Physics.Advanced || (Physics.Advanced = {})); + var Advanced = Physics.Advanced; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /// + /// + /// + /// + /// + /// + /// + /** + * Phaser - Advanced Physics - Body + * + * Based on the work Ju Hyung Lee started in JS PhyRus. + */ + (function (Advanced) { + var Body = (function () { + function Body(sprite, type) { + // Shapes + this.shapes = []; + // Joints + this.joints = []; + this.jointHash = { + }; + this.fixedRotation = false; + this.categoryBits = 0x0001; + this.maskBits = 0xFFFF; + this.stepCount = 0; + this.sprite = sprite; + this.game = sprite.game; + this.id = Phaser.Physics.Advanced.Manager.bodyCounter++; + this.name = 'body' + this.id; + this.type = type; + this.position = new Phaser.Vec2(sprite.x, sprite.y); + this.angle = sprite.rotation; + this.transform = new Phaser.Transform(this.position, this.angle); + this.centroid = new Phaser.Vec2(); + this.velocity = new Phaser.Vec2(); + this.force = new Phaser.Vec2(); + this.angularVelocity = 0; + this.torque = 0; + this.linearDamping = 0; + this.angularDamping = 0; + this.sleepTime = 0; + this.awaked = false; + this.shapes = []; + this.joints = []; + this.jointHash = { + }; + this.bounds = new Advanced.Bounds(); + this.fixedRotation = false; + this.categoryBits = 0x0001; + this.maskBits = 0xFFFF; + this.stepCount = 0; + } + Object.defineProperty(Body.prototype, "isDisabled", { + get: // duplicate = Util function + // serialize = Util function + function () { + return this.type == Phaser.Types.BODY_DISABLED ? true : false; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Body.prototype, "isStatic", { + get: function () { + return this.type == Phaser.Types.BODY_STATIC ? true : false; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Body.prototype, "isKinetic", { + get: function () { + return this.type == Phaser.Types.BODY_KINETIC ? true : false; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Body.prototype, "isDynamic", { + get: function () { + return this.type == Phaser.Types.BODY_DYNAMIC ? true : false; + }, + enumerable: true, + configurable: true + }); + Body.prototype.setType = function (type) { + if(type == this.type) { + return; + } + this.force.setTo(0, 0); + this.velocity.setTo(0, 0); + this.torque = 0; + this.angularVelocity = 0; + this.type = type; + this.awake(true); + }; + Body.prototype.addShape = function (shape) { + // Check not already part of this body + shape.body = this; + this.shapes.push(shape); + }; + Body.prototype.removeShape = function (shape) { + var index = this.shapes.indexOf(shape); + if(index != -1) { + this.shapes.splice(index, 1); + shape.body = undefined; + } + }; + Body.prototype.setMass = function (mass) { + this.mass = mass; + this.massInverted = mass > 0 ? 1 / mass : 0; + }; + Body.prototype.setInertia = function (inertia) { + this.inertia = inertia; + this.inertiaInverted = inertia > 0 ? 1 / inertia : 0; + }; + Body.prototype.setTransform = function (pos, angle) { + this.transform.setTo(pos, angle); + this.position = this.transform.transform(this.centroid); + this.angle = angle; + }; + Body.prototype.syncTransform = function () { + this.transform.setRotation(this.angle); + // this.transform.setPosition(vec2.sub(this.position, this.transform.rotate(this.centroid))); + Phaser.Vec2Utils.subtract(this.position, this.transform.rotate(this.centroid), this.transform.t); + }; + Body.prototype.getWorldPoint = function (p) { + // This is returning a new vector - check it's actually used in that way + return this.transform.transform(p); + }; + Body.prototype.getWorldVector = function (v) { + return this.transform.rotate(v); + }; + Body.prototype.getLocalPoint = function (p) { + return this.transform.untransform(p); + }; + Body.prototype.getLocalVector = function (v) { + return this.transform.unrotate(v); + }; + Body.prototype.setFixedRotation = function (flag) { + this.fixedRotation = flag; + this.resetMassData(); + }; + Body.prototype.resetMassData = function () { + this.centroid.setTo(0, 0); + this.mass = 0; + this.massInverted = 0; + this.inertia = 0; + this.inertiaInverted = 0; + if(this.isDynamic == false) { + this.position.copyFrom(this.transform.transform(this.centroid)); + return; + } + var totalMassCentroid = new Phaser.Vec2(0, 0); + var totalMass = 0; + var totalInertia = 0; + for(var i = 0; i < this.shapes.length; i++) { + var shape = this.shapes[i]; + var centroid = shape.centroid(); + var mass = shape.area() * shape.density; + var inertia = shape.inertia(mass); + totalMassCentroid.multiplyAddByScalar(centroid, mass); + totalMass += mass; + totalInertia += inertia; + } + //this.centroid.copy(vec2.scale(totalMassCentroid, 1 / totalMass)); + Phaser.Vec2Utils.scale(totalMassCentroid, 1 / totalMass, this.centroid); + this.setMass(totalMass); + if(!this.fixedRotation) { + //this.setInertia(totalInertia - totalMass * vec2.dot(this.centroid, this.centroid)); + this.setInertia(totalInertia - totalMass * Phaser.Vec2Utils.dot(this.centroid, this.centroid)); + } + //console.log("mass = " + this.m + " inertia = " + this.i); + // Move center of mass + var oldPosition = Phaser.Vec2Utils.clone(this.position); + this.position = this.transform.transform(this.centroid); + // Update center of mass velocity + //this.velocity.mad(vec2.perp(vec2.sub(this.position, old_p)), this.angularVelocity); + oldPosition.subtract(this.position); + this.velocity.multiplyAddByScalar(Phaser.Vec2Utils.perp(oldPosition, oldPosition), this.angularVelocity); + }; + Body.prototype.resetJointAnchors = function () { + for(var i = 0; i < this.joints.length; i++) { + var joint = this.joints[i]; + if(!joint) { + continue; + } + var anchor1 = joint.getWorldAnchor1(); + var anchor2 = joint.getWorldAnchor2(); + joint.setWorldAnchor1(anchor1); + joint.setWorldAnchor2(anchor2); + } + }; + Body.prototype.cacheData = function () { + this.bounds.clear(); + for(var i = 0; i < this.shapes.length; i++) { + var shape = this.shapes[i]; + shape.cacheData(this.transform); + this.bounds.addBounds(shape.bounds); + } + }; + Body.prototype.updateVelocity = function (gravity, dt, damping) { + // this.velocity = vec2.mad(this.velocity, vec2.mad(gravity, this.force, this.massInverted), dt); + Phaser.Vec2Utils.multiplyAdd(gravity, this.force, this.massInverted, this._tempVec2); + Phaser.Vec2Utils.multiplyAdd(this.velocity, this._tempVec2, dt, this.velocity); + this.angularVelocity = this.angularVelocity + this.torque * this.inertiaInverted * dt; + // Apply damping. + // ODE: dv/dt + c * v = 0 + // Solution: v(t) = v0 * exp(-c * t) + // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt) + // v2 = exp(-c * dt) * v1 + // Taylor expansion: + // v2 = (1.0f - c * dt) * v1 + this.velocity.scale(this.game.math.clamp(1 - dt * (damping + this.linearDamping), 0, 1)); + this.angularVelocity *= this.game.math.clamp(1 - dt * (damping + this.angularDamping), 0, 1); + this.force.setTo(0, 0); + this.torque = 0; + }; + Body.prototype.updatePosition = function (dt) { + //this.position.addself(vec2.scale(this.velocity, dt)); + this.position.add(Phaser.Vec2Utils.scale(this.velocity, dt, this._tempVec2)); + this.angle += this.angularVelocity * dt; + }; + Body.prototype.resetForce = function () { + this.force.setTo(0, 0); + this.torque = 0; + }; + Body.prototype.applyForce = function (force, p) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.force.add(force); + // this.f.addself(force); + // this.torque += vec2.cross(vec2.sub(p, this.p), force); + Phaser.Vec2Utils.subtract(p, this.position, this._tempVec2); + this.torque += Phaser.Vec2Utils.cross(this._tempVec2, force); + }; + Body.prototype.applyForceToCenter = function (force) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.force.add(force); + }; + Body.prototype.applyTorque = function (torque) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.torque += torque; + }; + Body.prototype.applyLinearImpulse = function (impulse, p) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.velocity.multiplyAddByScalar(impulse, this.massInverted); + // this.angularVelocity += vec2.cross(vec2.sub(p, this.position), impulse) * this.inertiaInverted; + Phaser.Vec2Utils.subtract(p, this.position, this._tempVec2); + this.angularVelocity += Phaser.Vec2Utils.cross(this._tempVec2, impulse) * this.inertiaInverted; + }; + Body.prototype.applyAngularImpulse = function (impulse) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.angularVelocity += impulse * this.inertiaInverted; + }; + Body.prototype.kineticEnergy = function () { + var vsq = this.velocity.dot(this.velocity); + var wsq = this.angularVelocity * this.angularVelocity; + return 0.5 * (this.mass * vsq + this.inertia * wsq); + }; + Object.defineProperty(Body.prototype, "isAwake", { + get: function () { + return this.awaked; + }, + enumerable: true, + configurable: true + }); + Body.prototype.awake = function (flag) { + this.awaked = flag; + if(flag) { + this.sleepTime = 0; + } else { + this.velocity.setTo(0, 0); + this.angularVelocity = 0; + this.force.setTo(0, 0); + this.torque = 0; + } + }; + Body.prototype.isCollidable = function (other) { + if(this == other) { + return false; + } + if(this.isDynamic == false && other.isDynamic == false) { + return false; + } + if(!(this.maskBits & other.categoryBits) || !(other.maskBits & this.categoryBits)) { + return false; + } + for(var i = 0; i < this.joints.length; i++) { + var joint = this.joints[i]; + if(!joint) { + continue; + } + if(!joint.collideConnected && other.jointHash[joint.id] != undefined) { + return false; + } + } + return true; + }; + return Body; + })(); + Advanced.Body = Body; })(Physics.Advanced || (Physics.Advanced = {})); var Advanced = Physics.Advanced; })(Phaser.Physics || (Phaser.Physics = {})); diff --git a/build/phaser.d.ts b/build/phaser.d.ts index f0037541..722e1bce 100644 --- a/build/phaser.d.ts +++ b/build/phaser.d.ts @@ -9583,6 +9583,7 @@ module Phaser.Physics.Advanced { * Local reference to Game. */ public game: Game; + static collision: Collision; static SHAPE_TYPE_CIRCLE: number; static SHAPE_TYPE_SEGMENT: number; static SHAPE_TYPE_POLY: number; @@ -9658,97 +9659,6 @@ module Phaser.Physics.Advanced { } } /** -* Phaser - Advanced Physics - Body -* -* Based on the work Ju Hyung Lee started in JS PhyRus. -*/ -module Phaser.Physics.Advanced { - class Body { - constructor(sprite: Sprite, type: number); - /** - * Reference to Phaser.Game - */ - public game: Game; - /** - * Reference to the parent Sprite - */ - public sprite: Sprite; - /** - * The Body ID - */ - public id: number; - /** - * The Body name - */ - public name: string; - /** - * The type of Body (disabled, dynamic, static or kinematic) - * Disabled = skips all physics operations / tests (default) - * Dynamic = gives and receives impacts - * Static = gives but doesn't receive impacts, cannot be moved by physics - * Kinematic = gives impacts, but never receives, can be moved by physics - * @type {number} - */ - public type: number; - public angle: number; - public transform: Transform; - public centroid: Vec2; - public position: Vec2; - public velocity: Vec2; - public force: Vec2; - public angularVelocity: number; - public torque: number; - public linearDamping: number; - public angularDamping: number; - public sleepTime: number; - public awaked: bool; - public shapes: any[]; - public joints: any[]; - public jointHash: {}; - public bounds; - public fixedRotation: bool; - public categoryBits: number; - public maskBits: number; - public stepCount: number; - public isDisabled : bool; - public isStatic : bool; - public isKinetic : bool; - public isDynamic : bool; - public setType(type: number): void; - public addShape(shape): void; - public removeShape(shape): void; - public mass: number; - public massInverted: number; - public inertia: number; - public inertiaInverted: number; - private setMass(mass); - private setInertia(inertia); - public setTransform(pos, angle): void; - public syncTransform(): void; - public getWorldPoint(p: Vec2): Vec2; - public getWorldVector(v): Vec2; - public getLocalPoint(p): Vec2; - public getLocalVector(v): Vec2; - public setFixedRotation(flag): void; - public resetMassData(): void; - public resetJointAnchors(): void; - public cacheData(): void; - private _tempVec2; - public updateVelocity(gravity, dt, damping): void; - public updatePosition(dt): void; - public resetForce(): void; - public applyForce(force, p): void; - public applyForceToCenter(force): void; - public applyTorque(torque): void; - public applyLinearImpulse(impulse, p): void; - public applyAngularImpulse(impulse): void; - public kineticEnergy(): number; - public isAwake : bool; - public awake(flag): void; - public isCollidable(other): bool; - } -} -/** * Phaser - Advanced Physics - Shape * * Based on the work Ju Hyung Lee started in JS PhyRus. @@ -9787,6 +9697,22 @@ module Phaser.Physics.Advanced { public lambdaTangential; } } +module Phaser.Physics.Advanced { + class ContactSolver { + constructor(shape1, shape2); + public shape1; + public shape2; + public contacts: Contact[]; + public elasticity: number; + public friction: number; + public update(newContactArr: Contact[]): void; + public initSolver(dt_inv): void; + public warmStart(): void; + public solveVelocityConstraints(): void; + public solvePositionConstraints(): bool; + public clamp(v, min, max); + } +} /** * Phaser - Advanced Physics - Shape * @@ -9838,20 +9764,163 @@ module Phaser.Physics.Advanced { public poly2Poly(poly1, poly2, contactArr): number; } } +/** +* Phaser - Advanced Physics - Joint +* +* Based on the work Ju Hyung Lee started in JS PhyRus. +*/ module Phaser.Physics.Advanced { - class ContactSolver { - constructor(shape1, shape2); - public shape1; - public shape2; - public contacts: Contact[]; - public elasticity: number; - public friction: number; - public update(newContactArr: Contact[]): void; - public initSolver(dt_inv): void; - public warmStart(): void; - public solveVelocityConstraints(): void; - public solvePositionConstraints(): bool; - public clamp(v, min, max); + interface IJoint { + id: number; + type: number; + body1: Body; + body2: Body; + collideConnected; + maxForce: number; + breakable: bool; + anchor1: Vec2; + anchor2: Vec2; + getWorldAnchor1(); + getWorldAnchor2(); + setWorldAnchor1(anchor1); + setWorldAnchor2(anchor2); + initSolver(dt, warmStarting); + solveVelocityConstraints(); + solvePositionConstraints(); + getReactionForce(dt_inv); + } +} +/** +* Phaser - Advanced Physics - Space +* +* Based on the work Ju Hyung Lee started in JS PhyRus. +*/ +module Phaser.Physics.Advanced { + class Space { + constructor(); + static TIME_TO_SLEEP: number; + static SLEEP_LINEAR_TOLERANCE: number; + static SLEEP_ANGULAR_TOLERANCE: number; + public bodyArr: Body[]; + public bodyHash; + public jointArr: IJoint[]; + public jointHash; + public numContacts: number; + public contactSolvers: ContactSolver[]; + public postSolve; + public gravity: Vec2; + public damping: number; + public stepCount: number; + public clear(): void; + public addBody(body: Body): void; + public removeBody(body: Body): void; + public addJoint(joint: IJoint): void; + public removeJoint(joint: IJoint): void; + public findShapeByPoint(p, refShape); + public findBodyByPoint(p, refBody: Body); + public shapeById(id); + public jointById(id): IJoint; + public findVertexByPoint(p, minDist, refVertexId): number; + public findEdgeByPoint(p, minDist, refEdgeId): number; + public findJointByPoint(p, minDist, refJointId): number; + public findContactSolver(shape1, shape2): ContactSolver; + public genTemporalContactSolvers(): any[]; + public initSolver(dt, dt_inv, warmStarting): void; + public velocitySolver(iteration): void; + public positionSolver(iteration): bool; + public step(dt, vel_iteration, pos_iteration, warmStarting, allowSleep): void; + } +} +/** +* Phaser - Advanced Physics - Body +* +* Based on the work Ju Hyung Lee started in JS PhyRus. +*/ +module Phaser.Physics.Advanced { + class Body { + constructor(sprite: Sprite, type: number); + /** + * Reference to Phaser.Game + */ + public game: Game; + /** + * Reference to the parent Sprite + */ + public sprite: Sprite; + /** + * The Body ID + */ + public id: number; + /** + * The Body name + */ + public name: string; + /** + * The type of Body (disabled, dynamic, static or kinematic) + * Disabled = skips all physics operations / tests (default) + * Dynamic = gives and receives impacts + * Static = gives but doesn't receive impacts, cannot be moved by physics + * Kinematic = gives impacts, but never receives, can be moved by physics + * @type {number} + */ + public type: number; + public angle: number; + public transform: Transform; + public centroid: Vec2; + public position: Vec2; + public velocity: Vec2; + public force: Vec2; + public angularVelocity: number; + public torque: number; + public linearDamping: number; + public angularDamping: number; + public sleepTime: number; + public awaked: bool; + public shapes: any[]; + public joints: any[]; + public jointHash: {}; + public bounds; + public fixedRotation: bool; + public categoryBits: number; + public maskBits: number; + public stepCount: number; + public space: Space; + public isDisabled : bool; + public isStatic : bool; + public isKinetic : bool; + public isDynamic : bool; + public setType(type: number): void; + public addShape(shape): void; + public removeShape(shape): void; + public mass: number; + public massInverted: number; + public inertia: number; + public inertiaInverted: number; + private setMass(mass); + private setInertia(inertia); + public setTransform(pos, angle): void; + public syncTransform(): void; + public getWorldPoint(p: Vec2): Vec2; + public getWorldVector(v): Vec2; + public getLocalPoint(p): Vec2; + public getLocalVector(v): Vec2; + public setFixedRotation(flag): void; + public resetMassData(): void; + public resetJointAnchors(): void; + public cacheData(): void; + private _tempVec2; + public updateVelocity(gravity, dt, damping): void; + public updatePosition(dt): void; + public resetForce(): void; + public applyForce(force, p): void; + public applyForceToCenter(force): void; + public applyTorque(torque): void; + public applyLinearImpulse(impulse, p): void; + public applyAngularImpulse(impulse): void; + public kineticEnergy(): number; + public isAwake : bool; + public awake(flag): void; + public isCollidable(other): bool; } } /** diff --git a/build/phaser.js b/build/phaser.js index e2525f58..fb07542c 100644 --- a/build/phaser.js +++ b/build/phaser.js @@ -19113,6 +19113,7 @@ var Phaser; var Manager = (function () { function Manager(game) { this.game = game; + Manager.collision = new Advanced.Collision(); } Manager.SHAPE_TYPE_CIRCLE = 0; Manager.SHAPE_TYPE_SEGMENT = 1; @@ -19424,337 +19425,6 @@ var Phaser; var Physics = Phaser.Physics; })(Phaser || (Phaser = {})); var Phaser; -(function (Phaser) { - (function (Physics) { - /// - /// - /// - /// - /// - /// - /// - /** - * Phaser - Advanced Physics - Body - * - * Based on the work Ju Hyung Lee started in JS PhyRus. - */ - (function (Advanced) { - var Body = (function () { - function Body(sprite, type) { - // Shapes - this.shapes = []; - // Joints - this.joints = []; - this.jointHash = { - }; - this.fixedRotation = false; - this.categoryBits = 0x0001; - this.maskBits = 0xFFFF; - this.stepCount = 0; - this.sprite = sprite; - this.game = sprite.game; - this.id = Phaser.Physics.Advanced.Manager.bodyCounter++; - this.name = 'body' + this.id; - this.type = type; - this.position = new Phaser.Vec2(sprite.x, sprite.y); - this.angle = sprite.rotation; - this.transform = new Phaser.Transform(this.position, this.angle); - this.centroid = new Phaser.Vec2(); - this.velocity = new Phaser.Vec2(); - this.force = new Phaser.Vec2(); - this.angularVelocity = 0; - this.torque = 0; - this.linearDamping = 0; - this.angularDamping = 0; - this.sleepTime = 0; - this.awaked = false; - this.shapes = []; - this.joints = []; - this.jointHash = { - }; - this.bounds = new Advanced.Bounds(); - this.fixedRotation = false; - this.categoryBits = 0x0001; - this.maskBits = 0xFFFF; - this.stepCount = 0; - } - Object.defineProperty(Body.prototype, "isDisabled", { - get: // duplicate = Util function - // serialize = Util function - function () { - return this.type == Phaser.Types.BODY_DISABLED ? true : false; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(Body.prototype, "isStatic", { - get: function () { - return this.type == Phaser.Types.BODY_STATIC ? true : false; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(Body.prototype, "isKinetic", { - get: function () { - return this.type == Phaser.Types.BODY_KINETIC ? true : false; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(Body.prototype, "isDynamic", { - get: function () { - return this.type == Phaser.Types.BODY_DYNAMIC ? true : false; - }, - enumerable: true, - configurable: true - }); - Body.prototype.setType = function (type) { - if(type == this.type) { - return; - } - this.force.setTo(0, 0); - this.velocity.setTo(0, 0); - this.torque = 0; - this.angularVelocity = 0; - this.type = type; - this.awake(true); - }; - Body.prototype.addShape = function (shape) { - // Check not already part of this body - shape.body = this; - this.shapes.push(shape); - }; - Body.prototype.removeShape = function (shape) { - var index = this.shapes.indexOf(shape); - if(index != -1) { - this.shapes.splice(index, 1); - shape.body = undefined; - } - }; - Body.prototype.setMass = function (mass) { - this.mass = mass; - this.massInverted = mass > 0 ? 1 / mass : 0; - }; - Body.prototype.setInertia = function (inertia) { - this.inertia = inertia; - this.inertiaInverted = inertia > 0 ? 1 / inertia : 0; - }; - Body.prototype.setTransform = function (pos, angle) { - this.transform.setTo(pos, angle); - this.position = this.transform.transform(this.centroid); - this.angle = angle; - }; - Body.prototype.syncTransform = function () { - this.transform.setRotation(this.angle); - // this.transform.setPosition(vec2.sub(this.position, this.transform.rotate(this.centroid))); - Phaser.Vec2Utils.subtract(this.position, this.transform.rotate(this.centroid), this.transform.t); - }; - Body.prototype.getWorldPoint = function (p) { - // This is returning a new vector - check it's actually used in that way - return this.transform.transform(p); - }; - Body.prototype.getWorldVector = function (v) { - return this.transform.rotate(v); - }; - Body.prototype.getLocalPoint = function (p) { - return this.transform.untransform(p); - }; - Body.prototype.getLocalVector = function (v) { - return this.transform.unrotate(v); - }; - Body.prototype.setFixedRotation = function (flag) { - this.fixedRotation = flag; - this.resetMassData(); - }; - Body.prototype.resetMassData = function () { - this.centroid.setTo(0, 0); - this.mass = 0; - this.massInverted = 0; - this.inertia = 0; - this.inertiaInverted = 0; - if(this.isDynamic == false) { - this.position.copyFrom(this.transform.transform(this.centroid)); - return; - } - var totalMassCentroid = new Phaser.Vec2(0, 0); - var totalMass = 0; - var totalInertia = 0; - for(var i = 0; i < this.shapes.length; i++) { - var shape = this.shapes[i]; - var centroid = shape.centroid(); - var mass = shape.area() * shape.density; - var inertia = shape.inertia(mass); - totalMassCentroid.multiplyAddByScalar(centroid, mass); - totalMass += mass; - totalInertia += inertia; - } - //this.centroid.copy(vec2.scale(totalMassCentroid, 1 / totalMass)); - Phaser.Vec2Utils.scale(totalMassCentroid, 1 / totalMass, this.centroid); - this.setMass(totalMass); - if(!this.fixedRotation) { - //this.setInertia(totalInertia - totalMass * vec2.dot(this.centroid, this.centroid)); - this.setInertia(totalInertia - totalMass * Phaser.Vec2Utils.dot(this.centroid, this.centroid)); - } - //console.log("mass = " + this.m + " inertia = " + this.i); - // Move center of mass - var oldPosition = Phaser.Vec2Utils.clone(this.position); - this.position = this.transform.transform(this.centroid); - // Update center of mass velocity - //this.velocity.mad(vec2.perp(vec2.sub(this.position, old_p)), this.angularVelocity); - oldPosition.subtract(this.position); - this.velocity.multiplyAddByScalar(Phaser.Vec2Utils.perp(oldPosition, oldPosition), this.angularVelocity); - }; - Body.prototype.resetJointAnchors = function () { - for(var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - if(!joint) { - continue; - } - var anchor1 = joint.getWorldAnchor1(); - var anchor2 = joint.getWorldAnchor2(); - joint.setWorldAnchor1(anchor1); - joint.setWorldAnchor2(anchor2); - } - }; - Body.prototype.cacheData = function () { - this.bounds.clear(); - for(var i = 0; i < this.shapes.length; i++) { - var shape = this.shapes[i]; - shape.cacheData(this.transform); - this.bounds.addBounds(shape.bounds); - } - }; - Body.prototype.updateVelocity = function (gravity, dt, damping) { - // this.velocity = vec2.mad(this.velocity, vec2.mad(gravity, this.force, this.massInverted), dt); - Phaser.Vec2Utils.multiplyAdd(gravity, this.force, this.massInverted, this._tempVec2); - Phaser.Vec2Utils.multiplyAdd(this.velocity, this._tempVec2, dt, this.velocity); - this.angularVelocity = this.angularVelocity + this.torque * this.inertiaInverted * dt; - // Apply damping. - // ODE: dv/dt + c * v = 0 - // Solution: v(t) = v0 * exp(-c * t) - // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt) - // v2 = exp(-c * dt) * v1 - // Taylor expansion: - // v2 = (1.0f - c * dt) * v1 - this.velocity.scale(this.game.math.clamp(1 - dt * (damping + this.linearDamping), 0, 1)); - this.angularVelocity *= this.game.math.clamp(1 - dt * (damping + this.angularDamping), 0, 1); - this.force.setTo(0, 0); - this.torque = 0; - }; - Body.prototype.updatePosition = function (dt) { - //this.position.addself(vec2.scale(this.velocity, dt)); - this.position.add(Phaser.Vec2Utils.scale(this.velocity, dt, this._tempVec2)); - this.angle += this.angularVelocity * dt; - }; - Body.prototype.resetForce = function () { - this.force.setTo(0, 0); - this.torque = 0; - }; - Body.prototype.applyForce = function (force, p) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.force.add(force); - // this.f.addself(force); - // this.torque += vec2.cross(vec2.sub(p, this.p), force); - Phaser.Vec2Utils.subtract(p, this.position, this._tempVec2); - this.torque += Phaser.Vec2Utils.cross(this._tempVec2, force); - }; - Body.prototype.applyForceToCenter = function (force) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.force.add(force); - }; - Body.prototype.applyTorque = function (torque) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.torque += torque; - }; - Body.prototype.applyLinearImpulse = function (impulse, p) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.velocity.multiplyAddByScalar(impulse, this.massInverted); - // this.angularVelocity += vec2.cross(vec2.sub(p, this.position), impulse) * this.inertiaInverted; - Phaser.Vec2Utils.subtract(p, this.position, this._tempVec2); - this.angularVelocity += Phaser.Vec2Utils.cross(this._tempVec2, impulse) * this.inertiaInverted; - }; - Body.prototype.applyAngularImpulse = function (impulse) { - if(this.isDynamic == false) { - return; - } - if(this.isAwake == false) { - this.awake(true); - } - this.angularVelocity += impulse * this.inertiaInverted; - }; - Body.prototype.kineticEnergy = function () { - var vsq = this.velocity.dot(this.velocity); - var wsq = this.angularVelocity * this.angularVelocity; - return 0.5 * (this.mass * vsq + this.inertia * wsq); - }; - Object.defineProperty(Body.prototype, "isAwake", { - get: function () { - return this.awaked; - }, - enumerable: true, - configurable: true - }); - Body.prototype.awake = function (flag) { - this.awaked = flag; - if(flag) { - this.sleepTime = 0; - } else { - this.velocity.setTo(0, 0); - this.angularVelocity = 0; - this.force.setTo(0, 0); - this.torque = 0; - } - }; - Body.prototype.isCollidable = function (other) { - if(this == other) { - return false; - } - if(this.isDynamic == false && other.isDynamic == false) { - return false; - } - if(!(this.maskBits & other.categoryBits) || !(other.maskBits & this.categoryBits)) { - return false; - } - for(var i = 0; i < this.joints.length; i++) { - var joint = this.joints[i]; - if(!joint) { - continue; - } - if(!joint.collideConnected && other.jointHash[joint.id] != undefined) { - return false; - } - } - return true; - }; - return Body; - })(); - Advanced.Body = Body; - })(Physics.Advanced || (Physics.Advanced = {})); - var Advanced = Physics.Advanced; - })(Phaser.Physics || (Phaser.Physics = {})); - var Physics = Phaser.Physics; -})(Phaser || (Phaser = {})); -var Phaser; (function (Phaser) { (function (Physics) { /// @@ -19818,6 +19488,254 @@ var Phaser; var Physics = Phaser.Physics; })(Phaser || (Phaser = {})); var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /// + /// + /// + /// + /// + /// + /** + * Phaser - Advanced Physics - ContactSolver + * + * Based on the work Ju Hyung Lee started in JS PhyRus. + */ + //------------------------------------------------------------------------------------------------- + // Contact Constraint + // + // Non-penetration constraint: + // C = dot(p2 - p1, n) + // Cdot = dot(v2 - v1, n) + // J = [ -n, -cross(r1, n), n, cross(r2, n) ] + // + // impulse = JT * lambda = [ -n * lambda, -cross(r1, n) * lambda, n * lambda, cross(r1, n) * lambda ] + // + // Friction constraint: + // C = dot(p2 - p1, t) + // Cdot = dot(v2 - v1, t) + // J = [ -t, -cross(r1, t), t, cross(r2, t) ] + // + // impulse = JT * lambda = [ -t * lambda, -cross(r1, t) * lambda, t * lambda, cross(r1, t) * lambda ] + // + // NOTE: lambda is an impulse in constraint space. + //------------------------------------------------------------------------------------------------- + (function (Advanced) { + var ContactSolver = (function () { + function ContactSolver(shape1, shape2) { + this.shape1 = shape1; + this.shape2 = shape2; + this.contacts = []; + this.elasticity = 1; + this.friction = 1; + } + ContactSolver.prototype.update = function (newContactArr) { + for(var i = 0; i < newContactArr.length; i++) { + var newContact = newContactArr[i]; + var k = -1; + for(var j = 0; j < this.contacts.length; j++) { + if(newContact.hash == this.contacts[j].hash) { + k = j; + break; + } + } + if(k > -1) { + newContact.lambdaNormal = this.contacts[k].lambdaNormal; + newContact.lambdaTangential = this.contacts[k].lambdaTangential; + } + } + this.contacts = newContactArr; + }; + ContactSolver.prototype.initSolver = function (dt_inv) { + var body1 = this.shape1.body; + var body2 = this.shape2.body; + var sum_m_inv = body1.massInverted + body2.massInverted; + for(var i = 0; i < this.contacts.length; i++) { + var con = this.contacts[i]; + // Transformed r1, r2 + Phaser.Vec2Utils.subtract(con.point, body1.position, con.r1); + Phaser.Vec2Utils.subtract(con.point, body2.position, con.r2); + //con.r1 = vec2.sub(con.point, body1.p); + //con.r2 = vec2.sub(con.point, body2.p); + // Local r1, r2 + con.r1_local = body1.transform.unrotate(con.r1); + con.r2_local = body2.transform.unrotate(con.r2); + var n = con.normal; + var t = Phaser.Vec2Utils.perp(con.normal); + // invEMn = J * invM * JT + // J = [ -n, -cross(r1, n), n, cross(r2, n) ] + var sn1 = Phaser.Vec2Utils.cross(con.r1, n); + var sn2 = Phaser.Vec2Utils.cross(con.r2, n); + var emn_inv = sum_m_inv + body1.inertiaInverted * sn1 * sn1 + body2.inertiaInverted * sn2 * sn2; + con.emn = emn_inv == 0 ? 0 : 1 / emn_inv; + // invEMt = J * invM * JT + // J = [ -t, -cross(r1, t), t, cross(r2, t) ] + var st1 = Phaser.Vec2Utils.cross(con.r1, t); + var st2 = Phaser.Vec2Utils.cross(con.r2, t); + var emt_inv = sum_m_inv + body1.inertiaInverted * st1 * st1 + body2.inertiaInverted * st2 * st2; + con.emt = emt_inv == 0 ? 0 : 1 / emt_inv; + // Linear velocities at contact point + // in 2D: cross(w, r) = perp(r) * w + var v1 = new Phaser.Vec2(); + var v2 = new Phaser.Vec2(); + Phaser.Vec2Utils.multiplyAdd(body1.velocity, Phaser.Vec2Utils.perp(con.r1), body1.angularVelocity, v1); + Phaser.Vec2Utils.multiplyAdd(body2.velocity, Phaser.Vec2Utils.perp(con.r2), body2.angularVelocity, v2); + //var v1 = vec2.mad(body1.v, vec2.perp(con.r1), body1.w); + //var v2 = vec2.mad(body2.v, vec2.perp(con.r2), body2.w); + // relative velocity at contact point + var rv = new Phaser.Vec2(); + Phaser.Vec2Utils.subtract(v2, v1, rv); + //var rv = vec2.sub(v2, v1); + // bounce velocity dot n + con.bounce = Phaser.Vec2Utils.dot(rv, con.normal) * this.elasticity; + } + }; + ContactSolver.prototype.warmStart = function () { + var body1 = this.shape1.body; + var body2 = this.shape2.body; + for(var i = 0; i < this.contacts.length; i++) { + var con = this.contacts[i]; + var n = con.normal; + var lambda_n = con.lambdaNormal; + var lambda_t = con.lambdaTangential; + // Apply accumulated impulses + //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n); + //var impulse = new vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); + var impulse = new Phaser.Vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); + body1.velocity.multiplyAddByScalar(impulse, -body1.massInverted); + //body1.v.mad(impulse, -body1.m_inv); + body1.angularVelocity -= Phaser.Vec2Utils.cross(con.r1, impulse) * body1.inertiaInverted; + //body1.w -= vec2.cross(con.r1, impulse) * body1.i_inv; + body2.velocity.multiplyAddByScalar(impulse, -body2.massInverted); + //body2.v.mad(impulse, body2.m_inv); + body2.angularVelocity -= Phaser.Vec2Utils.cross(con.r2, impulse) * body2.inertiaInverted; + //body2.w += vec2.cross(con.r2, impulse) * body2.i_inv; + } + }; + ContactSolver.prototype.solveVelocityConstraints = function () { + var body1 = this.shape1.body; + var body2 = this.shape2.body; + var m1_inv = body1.massInverted; + var i1_inv = body1.inertiaInverted; + var m2_inv = body2.massInverted; + var i2_inv = body2.inertiaInverted; + for(var i = 0; i < this.contacts.length; i++) { + var con = this.contacts[i]; + var n = con.normal; + var t = Phaser.Vec2Utils.perp(n); + var r1 = con.r1; + var r2 = con.r2; + // Linear velocities at contact point + // in 2D: cross(w, r) = perp(r) * w + var v1 = new Phaser.Vec2(); + var v2 = new Phaser.Vec2(); + Phaser.Vec2Utils.multiplyAdd(body1.velocity, Phaser.Vec2Utils.perp(r1), body1.angularVelocity, v1); + //var v1 = vec2.mad(body1.v, vec2.perp(r1), body1.w); + Phaser.Vec2Utils.multiplyAdd(body2.velocity, Phaser.Vec2Utils.perp(r2), body2.angularVelocity, v2); + //var v2 = vec2.mad(body2.v, vec2.perp(r2), body2.w); + // Relative velocity at contact point + var rv = new Phaser.Vec2(); + Phaser.Vec2Utils.subtract(v2, v1, rv); + //var rv = vec2.sub(v2, v1); + // Compute normal constraint impulse + adding bounce as a velocity bias + // lambda_n = -EMn * J * V + var lambda_n = -con.emn * (Phaser.Vec2Utils.dot(n, rv) + con.bounce); + // Accumulate and clamp + var lambda_n_old = con.lambdaNormal; + con.lambdaNormal = Math.max(lambda_n_old + lambda_n, 0); + lambda_n = con.lambdaNormal - lambda_n_old; + // Compute frictional constraint impulse + // lambda_t = -EMt * J * V + var lambda_t = -con.emt * Phaser.Vec2Utils.dot(t, rv); + // Max friction constraint impulse (Coulomb's Law) + var lambda_t_max = con.lambdaNormal * this.friction; + // Accumulate and clamp + var lambda_t_old = con.lambdaTangential; + con.lambdaTangential = this.clamp(lambda_t_old + lambda_t, -lambda_t_max, lambda_t_max); + lambda_t = con.lambdaTangential - lambda_t_old; + // Apply the final impulses + //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n); + var impulse = new Phaser.Vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); + body1.velocity.multiplyAddByScalar(impulse, -m1_inv); + //body1.v.mad(impulse, -m1_inv); + body1.angularVelocity -= Phaser.Vec2Utils.cross(r1, impulse) * i1_inv; + //body1.w -= vec2.cross(r1, impulse) * i1_inv; + body2.velocity.multiplyAddByScalar(impulse, m2_inv); + //body2.v.mad(impulse, m2_inv); + body1.angularVelocity += Phaser.Vec2Utils.cross(r2, impulse) * i2_inv; + //body2.w += vec2.cross(r2, impulse) * i2_inv; + } + }; + ContactSolver.prototype.solvePositionConstraints = function () { + var body1 = this.shape1.body; + var body2 = this.shape2.body; + var m1_inv = body1.massInverted; + var i1_inv = body1.inertiaInverted; + var m2_inv = body2.massInverted; + var i2_inv = body2.inertiaInverted; + var sum_m_inv = m1_inv + m2_inv; + var max_penetration = 0; + for(var i = 0; i < this.contacts.length; i++) { + var con = this.contacts[i]; + var n = con.normal; + var r1 = new Phaser.Vec2(); + var r2 = new Phaser.Vec2(); + // Transformed r1, r2 + Phaser.Vec2Utils.rotate(con.r1_local, body1.angle, r1); + //var r1 = vec2.rotate(con.r1_local, body1.a); + Phaser.Vec2Utils.rotate(con.r2_local, body2.angle, r2); + //var r2 = vec2.rotate(con.r2_local, body2.a); + // Contact points (corrected) + var p1 = new Phaser.Vec2(); + var p2 = new Phaser.Vec2(); + Phaser.Vec2Utils.add(body1.position, r1, p1); + //var p1 = vec2.add(body1.p, r1); + Phaser.Vec2Utils.add(body2.position, r2, p2); + //var p2 = vec2.add(body2.p, r2); + // Corrected delta vector + var dp = new Phaser.Vec2(); + Phaser.Vec2Utils.subtract(p2, p1); + //var dp = vec2.sub(p2, p1); + // Position constraint + var c = Phaser.Vec2Utils.dot(dp, n) + con.depth; + var correction = this.clamp(Advanced.Manager.CONTACT_SOLVER_BAUMGARTE * (c + Advanced.Manager.CONTACT_SOLVER_COLLISION_SLOP), -Advanced.Manager.CONTACT_SOLVER_MAX_LINEAR_CORRECTION, 0); + if(correction == 0) { + continue; + } + // We don't need max_penetration less than or equal slop + max_penetration = Math.max(max_penetration, -c); + // Compute lambda for position constraint + // Solve (J * invM * JT) * lambda = -C / dt + var sn1 = Phaser.Vec2Utils.cross(r1, n); + var sn2 = Phaser.Vec2Utils.cross(r2, n); + var em_inv = sum_m_inv + body1.inertiaInverted * sn1 * sn1 + body2.inertiaInverted * sn2 * sn2; + var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv; + // Apply correction impulses + var impulse_dt = new Phaser.Vec2(); + Phaser.Vec2Utils.scale(n, lambda_dt, impulse_dt); + //var impulse_dt = vec2.scale(n, lambda_dt); + body1.position.multiplyAddByScalar(impulse_dt, -m1_inv); + //body1.p.mad(impulse_dt, -m1_inv); + body1.angle -= sn1 * lambda_dt * i1_inv; + body2.position.multiplyAddByScalar(impulse_dt, m2_inv); + //body2.p.mad(impulse_dt, m2_inv); + body2.angle += sn2 * lambda_dt * i2_inv; + } + return max_penetration <= Advanced.Manager.CONTACT_SOLVER_COLLISION_SLOP * 3; + }; + ContactSolver.prototype.clamp = function (v, min, max) { + return v < min ? min : (v > max ? max : v); + }; + return ContactSolver; + })(); + Advanced.ContactSolver = ContactSolver; + })(Physics.Advanced || (Physics.Advanced = {})); + var Advanced = Physics.Advanced; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; (function (Phaser) { (function (Physics) { /// @@ -20268,6 +20186,12 @@ var Phaser; var Physics = Phaser.Physics; })(Phaser || (Phaser = {})); var Phaser; +(function (Phaser) { + (function (Physics) { + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; (function (Phaser) { (function (Physics) { /// @@ -20276,240 +20200,846 @@ var Phaser; /// /// /// + /// /// + /// + /// /** - * Phaser - Advanced Physics - ContactSolver + * Phaser - Advanced Physics - Space * * Based on the work Ju Hyung Lee started in JS PhyRus. */ - //------------------------------------------------------------------------------------------------- - // Contact Constraint - // - // Non-penetration constraint: - // C = dot(p2 - p1, n) - // Cdot = dot(v2 - v1, n) - // J = [ -n, -cross(r1, n), n, cross(r2, n) ] - // - // impulse = JT * lambda = [ -n * lambda, -cross(r1, n) * lambda, n * lambda, cross(r1, n) * lambda ] - // - // Friction constraint: - // C = dot(p2 - p1, t) - // Cdot = dot(v2 - v1, t) - // J = [ -t, -cross(r1, t), t, cross(r2, t) ] - // - // impulse = JT * lambda = [ -t * lambda, -cross(r1, t) * lambda, t * lambda, cross(r1, t) * lambda ] - // - // NOTE: lambda is an impulse in constraint space. - //------------------------------------------------------------------------------------------------- (function (Advanced) { - var ContactSolver = (function () { - function ContactSolver(shape1, shape2) { - this.shape1 = shape1; - this.shape2 = shape2; - this.contacts = []; - this.elasticity = 1; - this.friction = 1; + var Space = (function () { + function Space() { + this.stepCount = 0; + this.bodyArr = []; + this.bodyHash = { + }; + this.jointArr = []; + this.jointHash = { + }; + this.numContacts = 0; + this.contactSolvers = []; + //this.postSolve(arb) { }; + this.gravity = new Phaser.Vec2(); + this.damping = 0; } - ContactSolver.prototype.update = function (newContactArr) { - for(var i = 0; i < newContactArr.length; i++) { - var newContact = newContactArr[i]; - var k = -1; - for(var j = 0; j < this.contacts.length; j++) { - if(newContact.hash == this.contacts[j].hash) { - k = j; + Space.TIME_TO_SLEEP = 0.5; + Space.SLEEP_LINEAR_TOLERANCE = 0.5; + Space.SLEEP_ANGULAR_TOLERANCE = 2 * Phaser.GameMath.DEG_TO_RAD; + Space.prototype.clear = function () { + Advanced.Manager.shapeCounter = 0; + Advanced.Manager.bodyCounter = 0; + Advanced.Manager.jointCounter = 0; + for(var i = 0; i < this.bodyArr.length; i++) { + if(this.bodyArr[i]) { + this.removeBody(this.bodyArr[i]); + } + } + this.bodyArr = []; + this.bodyHash = { + }; + this.jointArr = []; + this.jointHash = { + }; + this.contactSolvers = []; + this.stepCount = 0; + }; + Space.prototype.addBody = function (body) { + if(this.bodyHash[body.id] != undefined) { + return; + } + var index = this.bodyArr.push(body) - 1; + this.bodyHash[body.id] = index; + body.awake(true); + body.space = this; + body.cacheData(); + }; + Space.prototype.removeBody = function (body) { + if(this.bodyHash[body.id] == undefined) { + return; + } + // Remove linked joint + for(var i = 0; i < body.joints.length; i++) { + if(body.joints[i]) { + this.removeJoint(body.joints[i]); + } + } + body.space = null; + var index = this.bodyHash[body.id]; + delete this.bodyHash[body.id]; + delete this.bodyArr[index]; + }; + Space.prototype.addJoint = function (joint) { + if(this.jointHash[joint.id] != undefined) { + return; + } + joint.body1.awake(true); + joint.body2.awake(true); + var index = this.jointArr.push(joint) - 1; + this.jointHash[joint.id] = index; + var index = joint.body1.joints.push(joint) - 1; + joint.body1.jointHash[joint.id] = index; + var index = joint.body2.joints.push(joint) - 1; + joint.body2.jointHash[joint.id] = index; + }; + Space.prototype.removeJoint = function (joint) { + if(this.jointHash[joint.id] == undefined) { + return; + } + joint.body1.awake(true); + joint.body2.awake(true); + var index = joint.body1.jointHash[joint.id]; + delete joint.body1.jointHash[joint.id]; + delete joint.body1.joints[index]; + var index = joint.body2.jointHash[joint.id]; + delete joint.body2.jointHash[joint.id]; + delete joint.body2.joints[index]; + var index = this.jointHash[joint.id]; + delete this.jointHash[joint.id]; + delete this.jointArr[index]; + }; + Space.prototype.findShapeByPoint = function (p, refShape) { + var firstShape; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + for(var j = 0; j < body.shapes.length; j++) { + var shape = body.shapes[j]; + if(shape.pointQuery(p)) { + if(!refShape) { + return shape; + } + if(!firstShape) { + firstShape = shape; + } + if(shape == refShape) { + refShape = null; + } + } + } + } + return firstShape; + }; + Space.prototype.findBodyByPoint = function (p, refBody) { + var firstBody; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + for(var j = 0; j < body.shapes.length; j++) { + var shape = body.shapes[j]; + if(shape.pointQuery(p)) { + if(!refBody) { + return shape.body; + } + if(!firstBody) { + firstBody = shape.body; + } + if(shape.body == refBody) { + refBody = null; + } break; } } - if(k > -1) { - newContact.lambdaNormal = this.contacts[k].lambdaNormal; - newContact.lambdaTangential = this.contacts[k].lambdaTangential; - } } - this.contacts = newContactArr; + return firstBody; }; - ContactSolver.prototype.initSolver = function (dt_inv) { - var body1 = this.shape1.body; - var body2 = this.shape2.body; - var sum_m_inv = body1.massInverted + body2.massInverted; - for(var i = 0; i < this.contacts.length; i++) { - var con = this.contacts[i]; - // Transformed r1, r2 - Phaser.Vec2Utils.subtract(con.point, body1.position, con.r1); - Phaser.Vec2Utils.subtract(con.point, body2.position, con.r2); - //con.r1 = vec2.sub(con.point, body1.p); - //con.r2 = vec2.sub(con.point, body2.p); - // Local r1, r2 - con.r1_local = body1.transform.unrotate(con.r1); - con.r2_local = body2.transform.unrotate(con.r2); - var n = con.normal; - var t = Phaser.Vec2Utils.perp(con.normal); - // invEMn = J * invM * JT - // J = [ -n, -cross(r1, n), n, cross(r2, n) ] - var sn1 = Phaser.Vec2Utils.cross(con.r1, n); - var sn2 = Phaser.Vec2Utils.cross(con.r2, n); - var emn_inv = sum_m_inv + body1.inertiaInverted * sn1 * sn1 + body2.inertiaInverted * sn2 * sn2; - con.emn = emn_inv == 0 ? 0 : 1 / emn_inv; - // invEMt = J * invM * JT - // J = [ -t, -cross(r1, t), t, cross(r2, t) ] - var st1 = Phaser.Vec2Utils.cross(con.r1, t); - var st2 = Phaser.Vec2Utils.cross(con.r2, t); - var emt_inv = sum_m_inv + body1.inertiaInverted * st1 * st1 + body2.inertiaInverted * st2 * st2; - con.emt = emt_inv == 0 ? 0 : 1 / emt_inv; - // Linear velocities at contact point - // in 2D: cross(w, r) = perp(r) * w - var v1 = new Phaser.Vec2(); - var v2 = new Phaser.Vec2(); - Phaser.Vec2Utils.multiplyAdd(body1.velocity, Phaser.Vec2Utils.perp(con.r1), body1.angularVelocity, v1); - Phaser.Vec2Utils.multiplyAdd(body2.velocity, Phaser.Vec2Utils.perp(con.r2), body2.angularVelocity, v2); - //var v1 = vec2.mad(body1.v, vec2.perp(con.r1), body1.w); - //var v2 = vec2.mad(body2.v, vec2.perp(con.r2), body2.w); - // relative velocity at contact point - var rv = new Phaser.Vec2(); - Phaser.Vec2Utils.subtract(v2, v1, rv); - //var rv = vec2.sub(v2, v1); - // bounce velocity dot n - con.bounce = Phaser.Vec2Utils.dot(rv, con.normal) * this.elasticity; - } - }; - ContactSolver.prototype.warmStart = function () { - var body1 = this.shape1.body; - var body2 = this.shape2.body; - for(var i = 0; i < this.contacts.length; i++) { - var con = this.contacts[i]; - var n = con.normal; - var lambda_n = con.lambdaNormal; - var lambda_t = con.lambdaTangential; - // Apply accumulated impulses - //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n); - //var impulse = new vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); - var impulse = new Phaser.Vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); - body1.velocity.multiplyAddByScalar(impulse, -body1.massInverted); - //body1.v.mad(impulse, -body1.m_inv); - body1.angularVelocity -= Phaser.Vec2Utils.cross(con.r1, impulse) * body1.inertiaInverted; - //body1.w -= vec2.cross(con.r1, impulse) * body1.i_inv; - body2.velocity.multiplyAddByScalar(impulse, -body2.massInverted); - //body2.v.mad(impulse, body2.m_inv); - body2.angularVelocity -= Phaser.Vec2Utils.cross(con.r2, impulse) * body2.inertiaInverted; - //body2.w += vec2.cross(con.r2, impulse) * body2.i_inv; - } - }; - ContactSolver.prototype.solveVelocityConstraints = function () { - var body1 = this.shape1.body; - var body2 = this.shape2.body; - var m1_inv = body1.massInverted; - var i1_inv = body1.inertiaInverted; - var m2_inv = body2.massInverted; - var i2_inv = body2.inertiaInverted; - for(var i = 0; i < this.contacts.length; i++) { - var con = this.contacts[i]; - var n = con.normal; - var t = Phaser.Vec2Utils.perp(n); - var r1 = con.r1; - var r2 = con.r2; - // Linear velocities at contact point - // in 2D: cross(w, r) = perp(r) * w - var v1 = new Phaser.Vec2(); - var v2 = new Phaser.Vec2(); - Phaser.Vec2Utils.multiplyAdd(body1.velocity, Phaser.Vec2Utils.perp(r1), body1.angularVelocity, v1); - //var v1 = vec2.mad(body1.v, vec2.perp(r1), body1.w); - Phaser.Vec2Utils.multiplyAdd(body2.velocity, Phaser.Vec2Utils.perp(r2), body2.angularVelocity, v2); - //var v2 = vec2.mad(body2.v, vec2.perp(r2), body2.w); - // Relative velocity at contact point - var rv = new Phaser.Vec2(); - Phaser.Vec2Utils.subtract(v2, v1, rv); - //var rv = vec2.sub(v2, v1); - // Compute normal constraint impulse + adding bounce as a velocity bias - // lambda_n = -EMn * J * V - var lambda_n = -con.emn * (Phaser.Vec2Utils.dot(n, rv) + con.bounce); - // Accumulate and clamp - var lambda_n_old = con.lambdaNormal; - con.lambdaNormal = Math.max(lambda_n_old + lambda_n, 0); - lambda_n = con.lambdaNormal - lambda_n_old; - // Compute frictional constraint impulse - // lambda_t = -EMt * J * V - var lambda_t = -con.emt * Phaser.Vec2Utils.dot(t, rv); - // Max friction constraint impulse (Coulomb's Law) - var lambda_t_max = con.lambdaNormal * this.friction; - // Accumulate and clamp - var lambda_t_old = con.lambdaTangential; - con.lambdaTangential = this.clamp(lambda_t_old + lambda_t, -lambda_t_max, lambda_t_max); - lambda_t = con.lambdaTangential - lambda_t_old; - // Apply the final impulses - //var impulse = vec2.rotate_vec(new vec2(lambda_n, lambda_t), n); - var impulse = new Phaser.Vec2(lambda_n * n.x - lambda_t * n.y, lambda_t * n.x + lambda_n * n.y); - body1.velocity.multiplyAddByScalar(impulse, -m1_inv); - //body1.v.mad(impulse, -m1_inv); - body1.angularVelocity -= Phaser.Vec2Utils.cross(r1, impulse) * i1_inv; - //body1.w -= vec2.cross(r1, impulse) * i1_inv; - body2.velocity.multiplyAddByScalar(impulse, m2_inv); - //body2.v.mad(impulse, m2_inv); - body1.angularVelocity += Phaser.Vec2Utils.cross(r2, impulse) * i2_inv; - //body2.w += vec2.cross(r2, impulse) * i2_inv; - } - }; - ContactSolver.prototype.solvePositionConstraints = function () { - var body1 = this.shape1.body; - var body2 = this.shape2.body; - var m1_inv = body1.massInverted; - var i1_inv = body1.inertiaInverted; - var m2_inv = body2.massInverted; - var i2_inv = body2.inertiaInverted; - var sum_m_inv = m1_inv + m2_inv; - var max_penetration = 0; - for(var i = 0; i < this.contacts.length; i++) { - var con = this.contacts[i]; - var n = con.normal; - var r1 = new Phaser.Vec2(); - var r2 = new Phaser.Vec2(); - // Transformed r1, r2 - Phaser.Vec2Utils.rotate(con.r1_local, body1.angle, r1); - //var r1 = vec2.rotate(con.r1_local, body1.a); - Phaser.Vec2Utils.rotate(con.r2_local, body2.angle, r2); - //var r2 = vec2.rotate(con.r2_local, body2.a); - // Contact points (corrected) - var p1 = new Phaser.Vec2(); - var p2 = new Phaser.Vec2(); - Phaser.Vec2Utils.add(body1.position, r1, p1); - //var p1 = vec2.add(body1.p, r1); - Phaser.Vec2Utils.add(body2.position, r2, p2); - //var p2 = vec2.add(body2.p, r2); - // Corrected delta vector - var dp = new Phaser.Vec2(); - Phaser.Vec2Utils.subtract(p2, p1); - //var dp = vec2.sub(p2, p1); - // Position constraint - var c = Phaser.Vec2Utils.dot(dp, n) + con.depth; - var correction = this.clamp(Advanced.Manager.CONTACT_SOLVER_BAUMGARTE * (c + Advanced.Manager.CONTACT_SOLVER_COLLISION_SLOP), -Advanced.Manager.CONTACT_SOLVER_MAX_LINEAR_CORRECTION, 0); - if(correction == 0) { + Space.prototype.shapeById = // TODO: Replace this function to shape hashing + function (id) { + var shape; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { continue; } - // We don't need max_penetration less than or equal slop - max_penetration = Math.max(max_penetration, -c); - // Compute lambda for position constraint - // Solve (J * invM * JT) * lambda = -C / dt - var sn1 = Phaser.Vec2Utils.cross(r1, n); - var sn2 = Phaser.Vec2Utils.cross(r2, n); - var em_inv = sum_m_inv + body1.inertiaInverted * sn1 * sn1 + body2.inertiaInverted * sn2 * sn2; - var lambda_dt = em_inv == 0 ? 0 : -correction / em_inv; - // Apply correction impulses - var impulse_dt = new Phaser.Vec2(); - Phaser.Vec2Utils.scale(n, lambda_dt, impulse_dt); - //var impulse_dt = vec2.scale(n, lambda_dt); - body1.position.multiplyAddByScalar(impulse_dt, -m1_inv); - //body1.p.mad(impulse_dt, -m1_inv); - body1.angle -= sn1 * lambda_dt * i1_inv; - body2.position.multiplyAddByScalar(impulse_dt, m2_inv); - //body2.p.mad(impulse_dt, m2_inv); - body2.angle += sn2 * lambda_dt * i2_inv; + for(var j = 0; j < body.shapes.length; j++) { + if(body.shapes[j].id == id) { + return body.shapes[j]; + } + } } - return max_penetration <= Advanced.Manager.CONTACT_SOLVER_COLLISION_SLOP * 3; + return null; }; - ContactSolver.prototype.clamp = function (v, min, max) { - return v < min ? min : (v > max ? max : v); + Space.prototype.jointById = function (id) { + var index = this.jointHash[id]; + if(index != undefined) { + return this.jointArr[index]; + } + return null; }; - return ContactSolver; + Space.prototype.findVertexByPoint = function (p, minDist, refVertexId) { + var firstVertexId = -1; + refVertexId = refVertexId || -1; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + for(var j = 0; j < body.shapes.length; j++) { + var shape = body.shapes[j]; + var index = shape.findVertexByPoint(p, minDist); + if(index != -1) { + var vertex = (shape.id << 16) | index; + if(refVertexId == -1) { + return vertex; + } + if(firstVertexId == -1) { + firstVertexId = vertex; + } + if(vertex == refVertexId) { + refVertexId = -1; + } + } + } + } + return firstVertexId; + }; + Space.prototype.findEdgeByPoint = function (p, minDist, refEdgeId) { + var firstEdgeId = -1; + refEdgeId = refEdgeId || -1; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + for(var j = 0; j < body.shapes.length; j++) { + var shape = body.shapes[j]; + if(shape.type != Advanced.Manager.SHAPE_TYPE_POLY) { + continue; + } + var index = shape.findEdgeByPoint(p, minDist); + if(index != -1) { + var edge = (shape.id << 16) | index; + if(refEdgeId == -1) { + return edge; + } + if(firstEdgeId == -1) { + firstEdgeId = edge; + } + if(edge == refEdgeId) { + refEdgeId = -1; + } + } + } + } + return firstEdgeId; + }; + Space.prototype.findJointByPoint = function (p, minDist, refJointId) { + var firstJointId = -1; + var dsq = minDist * minDist; + refJointId = refJointId || -1; + for(var i = 0; i < this.jointArr.length; i++) { + var joint = this.jointArr[i]; + if(!joint) { + continue; + } + var jointId = -1; + if(Phaser.Vec2Utils.distanceSq(p, joint.getWorldAnchor1()) < dsq) { + jointId = (joint.id << 16 | 0); + } else if(Phaser.Vec2Utils.distanceSq(p, joint.getWorldAnchor2()) < dsq) { + jointId = (joint.id << 16 | 1); + } + if(jointId != -1) { + if(refJointId == -1) { + return jointId; + } + if(firstJointId == -1) { + firstJointId = jointId; + } + if(jointId == refJointId) { + refJointId = -1; + } + } + } + return firstJointId; + }; + Space.prototype.findContactSolver = function (shape1, shape2) { + for(var i = 0; i < this.contactSolvers.length; i++) { + var contactSolver = this.contactSolvers[i]; + if(shape1 == contactSolver.shape1 && shape2 == contactSolver.shape2) { + return contactSolver; + } + } + return null; + }; + Space.prototype.genTemporalContactSolvers = function () { + //var t0 = Date.now(); + var newContactSolverArr = []; + this.numContacts = 0; + for(var body1_index = 0; body1_index < this.bodyArr.length; body1_index++) { + var body1 = this.bodyArr[body1_index]; + if(!body1) { + continue; + } + body1.stepCount = this.stepCount; + for(var body2_index = 0; body2_index < this.bodyArr.length; body2_index++) { + var body2 = this.bodyArr[body2_index]; + if(!body2) { + continue; + } + if(body1.stepCount == body2.stepCount) { + continue; + } + var active1 = body1.isAwake && !body1.isStatic; + var active2 = body2.isAwake && !body2.isStatic; + if(!active1 && !active2) { + continue; + } + if(!body1.isCollidable(body2)) { + continue; + } + if(!body1.bounds.intersectsBounds(body2.bounds)) { + continue; + } + for(var i = 0; i < body1.shapes.length; i++) { + for(var j = 0; j < body2.shapes.length; j++) { + var shape1 = body1.shapes[i]; + var shape2 = body2.shapes[j]; + var contactArr = []; + if(!Advanced.Manager.collision.collide(shape1, shape2, contactArr)) { + continue; + } + if(shape1.type > shape2.type) { + var temp = shape1; + shape1 = shape2; + shape2 = temp; + } + this.numContacts += contactArr.length; + var contactSolver = this.findContactSolver(shape1, shape2); + if(contactSolver) { + contactSolver.update(contactArr); + newContactSolverArr.push(contactSolver); + } else { + body1.awake(true); + body2.awake(true); + var newContactSolver = new Advanced.ContactSolver(shape1, shape2); + newContactSolver.contacts = contactArr; + newContactSolver.elasticity = Math.max(shape1.e, shape2.e); + newContactSolver.friction = Math.sqrt(shape1.u * shape2.u); + newContactSolverArr.push(newContactSolver); + } + } + } + } + } + //stats.timeCollision = Date.now() - t0; + return newContactSolverArr; + }; + Space.prototype.initSolver = function (dt, dt_inv, warmStarting) { + //var t0 = Date.now(); + // Initialize contact solvers + for(var i = 0; i < this.contactSolvers.length; i++) { + this.contactSolvers[i].initSolver(dt_inv); + } + // Initialize joint solver + for(var i = 0; i < this.jointArr.length; i++) { + if(this.jointArr[i]) { + this.jointArr[i].initSolver(dt, warmStarting); + } + } + // Warm starting (apply cached impulse) + if(warmStarting) { + for(var i = 0; i < this.contactSolvers.length; i++) { + this.contactSolvers[i].warmStart(); + } + } + //stats.timeInitSolver = Date.now() - t0; + }; + Space.prototype.velocitySolver = function (iteration) { + //var t0 = Date.now(); + for(var i = 0; i < iteration; i++) { + for(var j = 0; j < this.jointArr.length; j++) { + if(this.jointArr[j]) { + this.jointArr[j].solveVelocityConstraints(); + } + } + for(var j = 0; j < this.contactSolvers.length; j++) { + this.contactSolvers[j].solveVelocityConstraints(); + } + } + //stats.timeVelocitySolver = Date.now() - t0; + }; + Space.prototype.positionSolver = function (iteration) { + //var t0 = Date.now(); + var positionSolved = false; + //stats.positionIterations = 0; + for(var i = 0; i < iteration; i++) { + var contactsOk = true; + var jointsOk = true; + for(var j = 0; j < this.contactSolvers.length; j++) { + var contactOk = this.contactSolvers[j].solvePositionConstraints(); + contactsOk = contactOk && contactsOk; + } + for(var j = 0; j < this.jointArr.length; j++) { + if(this.jointArr[j]) { + var jointOk = this.jointArr[j].solvePositionConstraints(); + jointsOk = jointOk && jointsOk; + } + } + if(contactsOk && jointsOk) { + // exit early if the position errors are small + positionSolved = true; + break; + } + //stats.positionIterations++; + } + //stats.timePositionSolver = Date.now() - t0; + return positionSolved; + }; + Space.prototype.step = function (dt, vel_iteration, pos_iteration, warmStarting, allowSleep) { + var dt_inv = 1 / dt; + this.stepCount++; + // Generate contact & contactSolver + this.contactSolvers = this.genTemporalContactSolvers(); + // Initialize contacts & joints solver + this.initSolver(dt, dt_inv, warmStarting); + // Intergrate velocity + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + if(body.isDynamic && body.isAwake) { + body.updateVelocity(this.gravity, dt, this.damping); + } + } + for(var i = 0; i < this.jointArr.length; i++) { + var joint = this.jointArr[i]; + if(!joint) { + continue; + } + var body1 = joint.body1; + var body2 = joint.body2; + var awake1 = body1.isAwake && !body1.isStatic; + var awake2 = body2.isAwake && !body2.isStatic; + if(awake1 ^ awake2) { + if(!awake1) { + body1.awake(true); + } + if(!awake2) { + body2.awake(true); + } + } + } + // Iterative velocity constraints solver + this.velocitySolver(vel_iteration); + // Intergrate position + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + if(body.isDynamic && body.isAwake) { + body.updatePosition(dt); + } + } + // Process breakable joint + for(var i = 0; i < this.jointArr.length; i++) { + var joint = this.jointArr[i]; + if(!joint) { + continue; + } + if(joint.breakable) { + if(joint.getReactionForce(dt_inv).lengthsq() >= joint.maxForce * joint.maxForce) { + this.removeJoint(joint); + } + } + } + // Iterative position constraints solver + var positionSolved = this.positionSolver(pos_iteration); + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + body.syncTransform(); + } + // Post solve collision callback + for(var i = 0; i < this.contactSolvers.length; i++) { + var arb = this.contactSolvers[i]; + this.postSolve(arb); + } + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + if(body.isDynamic && body.isAwake) { + body.cacheData(); + } + } + // Process sleeping + if(allowSleep) { + var minSleepTime = 999999; + var linTolSqr = Space.SLEEP_LINEAR_TOLERANCE * Space.SLEEP_LINEAR_TOLERANCE; + var angTolSqr = Space.SLEEP_ANGULAR_TOLERANCE * Space.SLEEP_ANGULAR_TOLERANCE; + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + if(!body.isDynamic) { + continue; + } + if(body.angularVelocity * body.angularVelocity > angTolSqr || body.velocity.dot(body.velocity) > linTolSqr) { + body.sleepTime = 0; + minSleepTime = 0; + } else { + body.sleepTime += dt; + minSleepTime = Math.min(minSleepTime, body.sleepTime); + } + } + if(positionSolved && minSleepTime >= Space.TIME_TO_SLEEP) { + for(var i = 0; i < this.bodyArr.length; i++) { + var body = this.bodyArr[i]; + if(!body) { + continue; + } + body.awake(false); + } + } + } + }; + return Space; })(); - Advanced.ContactSolver = ContactSolver; + Advanced.Space = Space; + })(Physics.Advanced || (Physics.Advanced = {})); + var Advanced = Physics.Advanced; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /// + /// + /// + /// + /// + /// + /// + /** + * Phaser - Advanced Physics - Body + * + * Based on the work Ju Hyung Lee started in JS PhyRus. + */ + (function (Advanced) { + var Body = (function () { + function Body(sprite, type) { + // Shapes + this.shapes = []; + // Joints + this.joints = []; + this.jointHash = { + }; + this.fixedRotation = false; + this.categoryBits = 0x0001; + this.maskBits = 0xFFFF; + this.stepCount = 0; + this.sprite = sprite; + this.game = sprite.game; + this.id = Phaser.Physics.Advanced.Manager.bodyCounter++; + this.name = 'body' + this.id; + this.type = type; + this.position = new Phaser.Vec2(sprite.x, sprite.y); + this.angle = sprite.rotation; + this.transform = new Phaser.Transform(this.position, this.angle); + this.centroid = new Phaser.Vec2(); + this.velocity = new Phaser.Vec2(); + this.force = new Phaser.Vec2(); + this.angularVelocity = 0; + this.torque = 0; + this.linearDamping = 0; + this.angularDamping = 0; + this.sleepTime = 0; + this.awaked = false; + this.shapes = []; + this.joints = []; + this.jointHash = { + }; + this.bounds = new Advanced.Bounds(); + this.fixedRotation = false; + this.categoryBits = 0x0001; + this.maskBits = 0xFFFF; + this.stepCount = 0; + } + Object.defineProperty(Body.prototype, "isDisabled", { + get: // duplicate = Util function + // serialize = Util function + function () { + return this.type == Phaser.Types.BODY_DISABLED ? true : false; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Body.prototype, "isStatic", { + get: function () { + return this.type == Phaser.Types.BODY_STATIC ? true : false; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Body.prototype, "isKinetic", { + get: function () { + return this.type == Phaser.Types.BODY_KINETIC ? true : false; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Body.prototype, "isDynamic", { + get: function () { + return this.type == Phaser.Types.BODY_DYNAMIC ? true : false; + }, + enumerable: true, + configurable: true + }); + Body.prototype.setType = function (type) { + if(type == this.type) { + return; + } + this.force.setTo(0, 0); + this.velocity.setTo(0, 0); + this.torque = 0; + this.angularVelocity = 0; + this.type = type; + this.awake(true); + }; + Body.prototype.addShape = function (shape) { + // Check not already part of this body + shape.body = this; + this.shapes.push(shape); + }; + Body.prototype.removeShape = function (shape) { + var index = this.shapes.indexOf(shape); + if(index != -1) { + this.shapes.splice(index, 1); + shape.body = undefined; + } + }; + Body.prototype.setMass = function (mass) { + this.mass = mass; + this.massInverted = mass > 0 ? 1 / mass : 0; + }; + Body.prototype.setInertia = function (inertia) { + this.inertia = inertia; + this.inertiaInverted = inertia > 0 ? 1 / inertia : 0; + }; + Body.prototype.setTransform = function (pos, angle) { + this.transform.setTo(pos, angle); + this.position = this.transform.transform(this.centroid); + this.angle = angle; + }; + Body.prototype.syncTransform = function () { + this.transform.setRotation(this.angle); + // this.transform.setPosition(vec2.sub(this.position, this.transform.rotate(this.centroid))); + Phaser.Vec2Utils.subtract(this.position, this.transform.rotate(this.centroid), this.transform.t); + }; + Body.prototype.getWorldPoint = function (p) { + // This is returning a new vector - check it's actually used in that way + return this.transform.transform(p); + }; + Body.prototype.getWorldVector = function (v) { + return this.transform.rotate(v); + }; + Body.prototype.getLocalPoint = function (p) { + return this.transform.untransform(p); + }; + Body.prototype.getLocalVector = function (v) { + return this.transform.unrotate(v); + }; + Body.prototype.setFixedRotation = function (flag) { + this.fixedRotation = flag; + this.resetMassData(); + }; + Body.prototype.resetMassData = function () { + this.centroid.setTo(0, 0); + this.mass = 0; + this.massInverted = 0; + this.inertia = 0; + this.inertiaInverted = 0; + if(this.isDynamic == false) { + this.position.copyFrom(this.transform.transform(this.centroid)); + return; + } + var totalMassCentroid = new Phaser.Vec2(0, 0); + var totalMass = 0; + var totalInertia = 0; + for(var i = 0; i < this.shapes.length; i++) { + var shape = this.shapes[i]; + var centroid = shape.centroid(); + var mass = shape.area() * shape.density; + var inertia = shape.inertia(mass); + totalMassCentroid.multiplyAddByScalar(centroid, mass); + totalMass += mass; + totalInertia += inertia; + } + //this.centroid.copy(vec2.scale(totalMassCentroid, 1 / totalMass)); + Phaser.Vec2Utils.scale(totalMassCentroid, 1 / totalMass, this.centroid); + this.setMass(totalMass); + if(!this.fixedRotation) { + //this.setInertia(totalInertia - totalMass * vec2.dot(this.centroid, this.centroid)); + this.setInertia(totalInertia - totalMass * Phaser.Vec2Utils.dot(this.centroid, this.centroid)); + } + //console.log("mass = " + this.m + " inertia = " + this.i); + // Move center of mass + var oldPosition = Phaser.Vec2Utils.clone(this.position); + this.position = this.transform.transform(this.centroid); + // Update center of mass velocity + //this.velocity.mad(vec2.perp(vec2.sub(this.position, old_p)), this.angularVelocity); + oldPosition.subtract(this.position); + this.velocity.multiplyAddByScalar(Phaser.Vec2Utils.perp(oldPosition, oldPosition), this.angularVelocity); + }; + Body.prototype.resetJointAnchors = function () { + for(var i = 0; i < this.joints.length; i++) { + var joint = this.joints[i]; + if(!joint) { + continue; + } + var anchor1 = joint.getWorldAnchor1(); + var anchor2 = joint.getWorldAnchor2(); + joint.setWorldAnchor1(anchor1); + joint.setWorldAnchor2(anchor2); + } + }; + Body.prototype.cacheData = function () { + this.bounds.clear(); + for(var i = 0; i < this.shapes.length; i++) { + var shape = this.shapes[i]; + shape.cacheData(this.transform); + this.bounds.addBounds(shape.bounds); + } + }; + Body.prototype.updateVelocity = function (gravity, dt, damping) { + // this.velocity = vec2.mad(this.velocity, vec2.mad(gravity, this.force, this.massInverted), dt); + Phaser.Vec2Utils.multiplyAdd(gravity, this.force, this.massInverted, this._tempVec2); + Phaser.Vec2Utils.multiplyAdd(this.velocity, this._tempVec2, dt, this.velocity); + this.angularVelocity = this.angularVelocity + this.torque * this.inertiaInverted * dt; + // Apply damping. + // ODE: dv/dt + c * v = 0 + // Solution: v(t) = v0 * exp(-c * t) + // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt) + // v2 = exp(-c * dt) * v1 + // Taylor expansion: + // v2 = (1.0f - c * dt) * v1 + this.velocity.scale(this.game.math.clamp(1 - dt * (damping + this.linearDamping), 0, 1)); + this.angularVelocity *= this.game.math.clamp(1 - dt * (damping + this.angularDamping), 0, 1); + this.force.setTo(0, 0); + this.torque = 0; + }; + Body.prototype.updatePosition = function (dt) { + //this.position.addself(vec2.scale(this.velocity, dt)); + this.position.add(Phaser.Vec2Utils.scale(this.velocity, dt, this._tempVec2)); + this.angle += this.angularVelocity * dt; + }; + Body.prototype.resetForce = function () { + this.force.setTo(0, 0); + this.torque = 0; + }; + Body.prototype.applyForce = function (force, p) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.force.add(force); + // this.f.addself(force); + // this.torque += vec2.cross(vec2.sub(p, this.p), force); + Phaser.Vec2Utils.subtract(p, this.position, this._tempVec2); + this.torque += Phaser.Vec2Utils.cross(this._tempVec2, force); + }; + Body.prototype.applyForceToCenter = function (force) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.force.add(force); + }; + Body.prototype.applyTorque = function (torque) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.torque += torque; + }; + Body.prototype.applyLinearImpulse = function (impulse, p) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.velocity.multiplyAddByScalar(impulse, this.massInverted); + // this.angularVelocity += vec2.cross(vec2.sub(p, this.position), impulse) * this.inertiaInverted; + Phaser.Vec2Utils.subtract(p, this.position, this._tempVec2); + this.angularVelocity += Phaser.Vec2Utils.cross(this._tempVec2, impulse) * this.inertiaInverted; + }; + Body.prototype.applyAngularImpulse = function (impulse) { + if(this.isDynamic == false) { + return; + } + if(this.isAwake == false) { + this.awake(true); + } + this.angularVelocity += impulse * this.inertiaInverted; + }; + Body.prototype.kineticEnergy = function () { + var vsq = this.velocity.dot(this.velocity); + var wsq = this.angularVelocity * this.angularVelocity; + return 0.5 * (this.mass * vsq + this.inertia * wsq); + }; + Object.defineProperty(Body.prototype, "isAwake", { + get: function () { + return this.awaked; + }, + enumerable: true, + configurable: true + }); + Body.prototype.awake = function (flag) { + this.awaked = flag; + if(flag) { + this.sleepTime = 0; + } else { + this.velocity.setTo(0, 0); + this.angularVelocity = 0; + this.force.setTo(0, 0); + this.torque = 0; + } + }; + Body.prototype.isCollidable = function (other) { + if(this == other) { + return false; + } + if(this.isDynamic == false && other.isDynamic == false) { + return false; + } + if(!(this.maskBits & other.categoryBits) || !(other.maskBits & this.categoryBits)) { + return false; + } + for(var i = 0; i < this.joints.length; i++) { + var joint = this.joints[i]; + if(!joint) { + continue; + } + if(!joint.collideConnected && other.jointHash[joint.id] != undefined) { + return false; + } + } + return true; + }; + return Body; + })(); + Advanced.Body = Body; })(Physics.Advanced || (Physics.Advanced = {})); var Advanced = Physics.Advanced; })(Phaser.Physics || (Phaser.Physics = {}));