UNPKG

@awayfl/awayfl-player

Version:

Flash Player emulator for executing SWF files (published for FP versions 6 and up) in javascript

1,267 lines (1,015 loc) 38.1 kB
/* * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ import { ASArray, ASClass } from '@awayfl/avm2'; import { b2AABB } from '../Collision/b2AABB'; import { b2Vec2, b2XForm, b2Math, b2Mat22 } from '../Common/Math'; import { b2ContactFilter } from './b2ContactFilter'; import { b2BodyDef } from './b2BodyDef'; import { b2BroadPhase } from '../Collision/b2BroadPhase'; import { b2DestructionListener } from './b2DestructionListener'; import { b2BoundaryListener } from './b2BoundaryListener'; import { b2ContactListener } from './b2ContactListener'; import { b2DebugDraw } from './b2DebugDraw'; import { b2Body } from './b2Body'; import { b2JointEdge } from './Joints'; import { b2Shape } from '../Collision/Shapes/b2Shape'; import { b2JointDef } from './Joints'; import { b2Joint } from './Joints'; import { b2TimeStep } from './b2TimeStep'; import { b2Island } from './b2Island'; import { b2Contact } from './Contacts/b2Contact'; import { b2ContactEdge } from './Contacts/b2ContactEdge'; import { b2Settings } from '../Common/b2Settings'; import { b2TimeOfImpact } from '../Collision/b2TimeOfImpact'; import { b2Color } from '../Common/b2Color'; import { b2PulleyJoint } from './Joints'; import { b2CircleShape } from '../Collision/Shapes/b2CircleShape'; import { b2PolygonShape } from '../Collision/Shapes/b2PolygonShape'; import { b2Pair } from '../Collision/b2Pair'; import { b2Proxy } from '../Collision/b2Proxy'; import { b2OBB } from '../Collision/b2OBB'; import { b2ContactManager } from './b2ContactManager'; import { b2Distance } from '../Collision/b2Distance'; import { b2ShapeDef } from '../Collision/Shapes/b2ShapeDef'; import { b2ConcaveArcShape } from '../Collision/Shapes/b2ConcaveArcShape'; import { b2StaticEdgeShape } from '../Collision/Shapes/b2StaticEdgeShape'; import { b2StaticEdgeChain } from '../Collision/Shapes/b2StaticEdgeChain'; export class b2World { readonly __fast__ = true; // Construct a world object. /// @param worldAABB a bounding box that completely encompasses all your shapes. /// @param gravity the world gravity vector. /// @param doSleep improve performance by not simulating inactive bodies. constructor(worldAABB: b2AABB, gravity: b2Vec2, doSleep: boolean) { this.m_destructionListener = null; this.m_boundaryListener = null; this.m_contactFilter = b2ContactFilter.b2_defaultFilter; this.m_contactListener = null; this.m_debugDraw = null; this.m_bodyList = null; this.m_contactList = null; this.m_jointList = null; this.m_bodyCount = 0; this.m_contactCount = 0; this.m_jointCount = 0; b2World.m_positionCorrection = true; b2World.m_warmStarting = true; b2World.m_continuousPhysics = true; this.m_allowSleep = doSleep; this.m_gravity = gravity; this.m_lock = false; this.m_inv_dt0 = 0.0; this.m_contactManager.m_world = this; //void* mem = b2Alloc(sizeof(b2BroadPhase)); this.m_broadPhase = new b2BroadPhase(worldAABB, this.m_contactManager); const bd: b2BodyDef = new b2BodyDef(); this.m_groundBody = this.CreateBody(bd); var b2Dis:b2Distance =new b2Distance(); b2Distance.InitializeRegisters(); } /// Destruct the world. All physics entities are destroyed and all heap memory is released. //~b2World(); /// Register a destruction listener. public SetDestructionListener(listener: b2DestructionListener): void { this.m_destructionListener = listener; } /// Register a broad-phase boundary listener. public SetBoundaryListener(listener: b2BoundaryListener): void { this.m_boundaryListener = listener; } /// Register a contact filter to provide specific control over collision. /// Otherwise the default filter is used (b2_defaultFilter). public SetContactFilter(filter: b2ContactFilter): void { this.m_contactFilter = filter; } /// Register a contact event listener public SetContactListener(listener: b2ContactListener | ASClass | null): void { // maybe nulled for reset if (!listener) { this.m_contactListener = null; return; } const v_listener = <b2ContactListener>listener; // ASClass, cool! Inject real class names, because box2D should call it by real name if (typeof listener['traits'] !== 'undefined' && this.__fast__) { // unwrapp to real class; const names = Object.getOwnPropertyNames(b2ContactListener.prototype); const mangle = '$Bg'; for (const name of names) { if (!v_listener[name] && v_listener[mangle + name]) { v_listener[name] = v_listener[mangle + name]; } } } this.m_contactListener = v_listener; } /// Register a routine for debug drawing. The debug draw functions are called /// inside the b2World::Step method, so make sure your renderer is ready to /// consume draw commands when you call Step(). public SetDebugDraw(debugDraw: b2DebugDraw): void { this.m_debugDraw = debugDraw; } /// Perform validation of internal data structures. public Validate(): void { this.m_broadPhase.Validate(); } /// Get the number of broad-phase proxies. public GetProxyCount(): number /** int */ { return this.m_broadPhase.m_proxyCount; } /// Get the number of broad-phase pairs. public GetPairCount(): number /** int */ { return this.m_broadPhase.m_pairManager.m_pairCount; } /// Create a rigid body given a definition. No reference to the definition /// is retained. /// @warning This function is locked during callbacks. public CreateBody(def: b2BodyDef): b2Body { //b2Settings.b2Assert(this.m_lock == false); if (this.m_lock == true) { return null; } //void* mem = this.m_blockAllocator.Allocate(sizeof(b2Body)); const b: b2Body = new b2Body(def, this); // Add to world doubly linked list. b.m_prev = null; b.m_next = this.m_bodyList; if (this.m_bodyList) { this.m_bodyList.m_prev = b; } this.m_bodyList = b; ++this.m_bodyCount; return b; } /// Destroy a rigid body given a definition. No reference to the definition /// is retained. This function is locked during callbacks. /// @warning This automatically deletes all associated shapes and joints. /// @warning This function is locked during callbacks. public DestroyBody(b: b2Body): void { //b2Settings.b2Assert(this.m_bodyCount > 0); //b2Settings.b2Assert(this.m_lock == false); if (this.m_lock == true) { return; } // Delete the attached joints. let jn: b2JointEdge = b.m_jointList; while (jn) { const jn0: b2JointEdge = jn; jn = jn.next; if (this.m_destructionListener) { this.m_destructionListener.SayGoodbyeJoint(jn0.joint); } this.DestroyJoint(jn0.joint); } // Delete the attached shapes. This destroys broad-phase // proxies and pairs, leading to the destruction of contacts. let s: b2Shape = b.m_shapeList; while (s) { const s0: b2Shape = s; s = s.m_next; if (this.m_destructionListener) { this.m_destructionListener.SayGoodbyeShape(s0); } s0.DestroyProxy(this.m_broadPhase); b2Shape.Destroy(s0, this.m_blockAllocator); } // Remove world body list. if (b.m_prev) { b.m_prev.m_next = b.m_next; } if (b.m_next) { b.m_next.m_prev = b.m_prev; } if (b == this.m_bodyList) { this.m_bodyList = b.m_next; } --this.m_bodyCount; //b->~b2Body(); //this.m_blockAllocator.Free(b, sizeof(b2Body)); } /// Create a joint to constrain bodies together. No reference to the definition /// is retained. This may cause the connected bodies to cease colliding. /// @warning This function is locked during callbacks. public CreateJoint(def: b2JointDef): b2Joint { //b2Settings.b2Assert(this.m_lock == false); const j: b2Joint = b2Joint.Create(def, this.m_blockAllocator); // Connect to the world list. j.m_prev = null; j.m_next = this.m_jointList; if (this.m_jointList) { this.m_jointList.m_prev = j; } this.m_jointList = j; ++this.m_jointCount; // Connect to the bodies' doubly linked lists. j.m_node1.joint = j; j.m_node1.other = j.m_body2; j.m_node1.prev = null; j.m_node1.next = j.m_body1.m_jointList; if (j.m_body1.m_jointList) j.m_body1.m_jointList.prev = j.m_node1; j.m_body1.m_jointList = j.m_node1; j.m_node2.joint = j; j.m_node2.other = j.m_body1; j.m_node2.prev = null; j.m_node2.next = j.m_body2.m_jointList; if (j.m_body2.m_jointList) j.m_body2.m_jointList.prev = j.m_node2; j.m_body2.m_jointList = j.m_node2; // If the joint prevents collisions, then reset collision filtering. if (def.collideConnected == false) { // Reset the proxies on the body with the minimum number of shapes. const b: b2Body = def.body1.m_shapeCount < def.body2.m_shapeCount ? def.body1 : def.body2; for (let s: b2Shape = b.m_shapeList; s; s = s.m_next) { s.RefilterProxy(this.m_broadPhase, b.m_xf); } } return j; } /// Destroy a joint. This may cause the connected bodies to begin colliding. /// @warning This function is locked during callbacks. public DestroyJoint(j: b2Joint): void { //b2Settings.b2Assert(this.m_lock == false); const collideConnected: boolean = j.m_collideConnected; // Remove from the doubly linked list. if (j.m_prev) { j.m_prev.m_next = j.m_next; } if (j.m_next) { j.m_next.m_prev = j.m_prev; } if (j == this.m_jointList) { this.m_jointList = j.m_next; } // Disconnect from island graph. const body1: b2Body = j.m_body1; const body2: b2Body = j.m_body2; // Wake up connected bodies. body1.WakeUp(); body2.WakeUp(); // Remove from body 1. if (j.m_node1.prev) { j.m_node1.prev.next = j.m_node1.next; } if (j.m_node1.next) { j.m_node1.next.prev = j.m_node1.prev; } if (j.m_node1 == body1.m_jointList) { body1.m_jointList = j.m_node1.next; } j.m_node1.prev = null; j.m_node1.next = null; // Remove from body 2 if (j.m_node2.prev) { j.m_node2.prev.next = j.m_node2.next; } if (j.m_node2.next) { j.m_node2.next.prev = j.m_node2.prev; } if (j.m_node2 == body2.m_jointList) { body2.m_jointList = j.m_node2.next; } j.m_node2.prev = null; j.m_node2.next = null; b2Joint.Destroy(j, this.m_blockAllocator); //b2Settings.b2Assert(this.m_jointCount > 0); --this.m_jointCount; // If the joint prevents collisions, then reset collision filtering. if (collideConnected == false) { // Reset the proxies on the body with the minimum number of shapes. const b: b2Body = body1.m_shapeCount < body2.m_shapeCount ? body1 : body2; for (let s: b2Shape = b.m_shapeList; s; s = s.m_next) { s.RefilterProxy(this.m_broadPhase, b.m_xf); } } } public CreateGroundShape(def:b2ShapeDef) : any { //b2Settings.b2Assert(m_lock == false); if (this.m_lock == true) { return null; } switch (def.type) { case b2Shape.e_staticEdgeShape: return new b2StaticEdgeChain(def, this); default: return this.m_groundBody.CreateShape(def); } } /// Re-filter a shape. This re-runs contact filtering on a shape. public Refilter(shape: b2Shape): void { shape.RefilterProxy(this.m_broadPhase, shape.m_body.m_xf); } /// Enable/disable warm starting. For testing. public SetWarmStarting(flag: boolean): void { b2World.m_warmStarting = flag; } /// Enable/disable position correction. For testing. public SetPositionCorrection(flag: boolean): void { b2World.m_positionCorrection = flag; } /// Enable/disable continuous physics. For testing. public SetContinuousPhysics(flag: boolean): void { b2World.m_continuousPhysics = flag; } /// Get the number of bodies. public GetBodyCount(): number /** int */ { return this.m_bodyCount; } /// Get the number joints. public GetJointCount(): number /** int */ { return this.m_jointCount; } /// Get the number of contacts (each may have 0 or more contact points). public GetContactCount(): number /** int */ { return this.m_contactCount; } /// Change the global gravity vector. public SetGravity(gravity: b2Vec2): void { this.m_gravity = gravity; } /// The world provides a single static ground body with no collision shapes. /// You can use this to simplify the creation of joints and static shapes. public GetGroundBody(): b2Body { return this.m_groundBody; } /// Take a time step. This performs collision detection, integration, /// and constraint solution. /// @param timeStep the amount of time to simulate, this should not vary. /// @param iterations the number of iterations to be used by the constraint solver. public Step(dt: number, iterations: number /** int */): void { this.m_lock = true; const step: b2TimeStep = new b2TimeStep(); step.dt = dt; step.maxIterations = iterations; if (dt > 0.0) { step.inv_dt = 1.0 / dt; } else { step.inv_dt = 0.0; } step.dtRatio = this.m_inv_dt0 * dt; step.positionCorrection = b2World.m_positionCorrection; step.warmStarting = b2World.m_warmStarting; // Update contacts. this.m_contactManager.Collide(); // Integrate velocities, solve velocity constraints, and integrate positions. if (step.dt > 0.0) { this.Solve(step); } // Handle TOI events. if (b2World.m_continuousPhysics && step.dt > 0.0) { this.SolveTOI(step); } // Draw debug information. this.DrawDebugData(); this.m_inv_dt0 = step.inv_dt; this.m_lock = false; } /// Query the world for all shapes that potentially overlap the /// provided AABB. You provide a shape pointer buffer of specified /// size. The number of shapes found is returned. /// @param aabb the query box. /// @param shapes a user allocated shape pointer array of size maxCount (or greater). /// @param maxCount the capacity of the shapes array. /// @return the number of shapes found in aabb. public Query(aabb: b2AABB, shapes: any[] | ASArray, maxCount: number /** int */): number /** int */{ //void** results = (void**)this.m_stackAllocator.Allocate(maxCount * sizeof(void*)); const results: any[] = new Array(maxCount); const count: number /** int */ = this.m_broadPhase.QueryAABB(aabb, results, maxCount); let v_arr = shapes; // ASArray if (typeof (v_arr['traits']) !== 'undefined') { v_arr = v_arr['value']; } for (let i = 0; i < count; ++i) { v_arr[i] = results[i]; } //this.m_stackAllocator.Free(results); return count; } /// Get the world body list. With the returned body, use b2Body::GetNext to get /// the next body in the world list. A NULL body indicates the end of the list. /// @return the head of the world body list. public GetBodyList(): b2Body { return this.m_bodyList; } /// Get the world joint list. With the returned joint, use b2Joint::GetNext to get /// the next joint in the world list. A NULL joint indicates the end of the list. /// @return the head of the world joint list. public GetJointList(): b2Joint { return this.m_jointList; } //--------------- Internals Below ------------------- // Internal yet public to make life easier. // Find islands, integrate and solve constraints, solve position constraints public Solve(step: b2TimeStep): void { let b: b2Body; this.m_positionIterationCount = 0; // Size the island for the worst case. const island: b2Island = new b2Island(this.m_bodyCount, this.m_contactCount, this.m_jointCount, this.m_stackAllocator, this.m_contactListener); // Clear all the island flags. for (b = this.m_bodyList; b; b = b.m_next) { b.m_flags &= ~b2Body.e_islandFlag; } for (let c: b2Contact = this.m_contactList; c; c = c.m_next) { c.m_flags &= ~b2Contact.e_islandFlag; } for (let j: b2Joint = this.m_jointList; j; j = j.m_next) { j.m_islandFlag = false; } // Build and simulate all awake islands. const stackSize: number /** int */ = this.m_bodyCount; //b2Body** stack = (b2Body**)this.m_stackAllocator.Allocate(stackSize * sizeof(b2Body*)); const stack: b2Body[] = new Array(stackSize); for (let seed: b2Body = this.m_bodyList; seed; seed = seed.m_next) { if (seed.m_flags & (b2Body.e_islandFlag | b2Body.e_sleepFlag | b2Body.e_frozenFlag)) { continue; } if (seed.IsStatic()) { continue; } // Reset island and stack. island.Clear(); let stackCount: number /** int */ = 0; stack[stackCount++] = seed; seed.m_flags |= b2Body.e_islandFlag; // Perform a depth first search (DFS) on the constraint graph. while (stackCount > 0) { // Grab the next body off the stack and add it to the island. b = stack[--stackCount]; island.AddBody(b); // Make sure the body is awake. b.m_flags &= ~b2Body.e_sleepFlag; // To keep islands as small as possible, we don't // propagate islands across static bodies. if (b.IsStatic()) { continue; } var other: b2Body; // Search all contacts connected to this body. for (let cn: b2ContactEdge = b.m_contactList; cn; cn = cn.next) { // Has this contact already been added to an island? if (cn.contact.m_flags & (b2Contact.e_islandFlag | b2Contact.e_nonSolidFlag)) { continue; } // Is this contact touching? if (cn.contact.m_manifoldCount == 0) { continue; } island.AddContact(cn.contact); cn.contact.m_flags |= b2Contact.e_islandFlag; //var other:b2Body = cn.other; other = cn.other; // Was the other body already added to this island? if (other.m_flags & b2Body.e_islandFlag) { continue; } //b2Settings.b2Assert(stackCount < stackSize); stack[stackCount++] = other; other.m_flags |= b2Body.e_islandFlag; } // Search all joints connect to this body. for (let jn: b2JointEdge = b.m_jointList; jn; jn = jn.next) { if (jn.joint.m_islandFlag == true) { continue; } island.AddJoint(jn.joint); jn.joint.m_islandFlag = true; //var other:b2Body = jn.other; other = jn.other; if (other.m_flags & b2Body.e_islandFlag) { continue; } //b2Settings.b2Assert(stackCount < stackSize); stack[stackCount++] = other; other.m_flags |= b2Body.e_islandFlag; } } island.Solve(step, this.m_gravity, b2World.m_positionCorrection, this.m_allowSleep); //this.m_positionIterationCount = Math.max(this.m_positionIterationCount, island.m_positionIterationCount); if (island.m_positionIterationCount > this.m_positionIterationCount) { this.m_positionIterationCount = island.m_positionIterationCount; } // Post solve cleanup. for (let i: number /** int */ = 0; i < island.m_bodyCount; ++i) { // Allow static bodies to participate in other islands. b = island.m_bodies[i]; if (b.IsStatic()) { b.m_flags &= ~b2Body.e_islandFlag; } } } //this.m_stackAllocator.Free(stack); // Synchronize shapes, check for out of range bodies. for (b = this.m_bodyList; b; b = b.m_next) { if (b.m_flags & (b2Body.e_sleepFlag | b2Body.e_frozenFlag)) { continue; } if (b.IsStatic()) { continue; } // Update shapes (for broad-phase). If the shapes go out of // the world AABB then shapes and contacts may be destroyed, // including contacts that are const inRange: boolean = b.SynchronizeShapes(); // Did the body's shapes leave the world? if (inRange == false && this.m_boundaryListener != null) { this.m_boundaryListener.Violation(b); } } // Commit shape proxy movements to the broad-phase so that new contacts are created. // Also, some contacts can be destroyed. this.m_broadPhase.Commit(); } // Find TOI contacts and solve them. public SolveTOI(step: b2TimeStep): void { let b: b2Body; let s1: b2Shape; let s2: b2Shape; let b1: b2Body; let b2: b2Body; let cn: b2ContactEdge; // Reserve an island and a stack for TOI island solution. const island: b2Island = new b2Island(this.m_bodyCount, b2Settings.b2_maxTOIContactsPerIsland, 0, this.m_stackAllocator, this.m_contactListener); const stackSize: number /** int */ = this.m_bodyCount; //b2Body** stack = (b2Body**)this.m_stackAllocator.Allocate(stackSize * sizeof(b2Body*)); const stack: b2Body[] = new Array(stackSize); for (b = this.m_bodyList; b; b = b.m_next) { b.m_flags &= ~b2Body.e_islandFlag; b.m_sweep.t0 = 0.0; } let c: b2Contact; for (c = this.m_contactList; c; c = c.m_next) { // Invalidate TOI c.m_flags &= ~(b2Contact.e_toiFlag | b2Contact.e_islandFlag); } // Find TOI events and solve them. for (;;) { // Find the first TOI. let minContact: b2Contact = null; let minTOI: number = 1.0; for (c = this.m_contactList; c; c = c.m_next) { if (c.m_flags & (b2Contact.e_slowFlag | b2Contact.e_nonSolidFlag)) { continue; } // TODO_ERIN keep a counter on the contact, only respond to M TOIs per contact. let toi: number = 1.0; if (c.m_flags & b2Contact.e_toiFlag) { // This contact has a valid cached TOI. toi = c.m_toi; } else { // Compute the TOI for this contact. s1 = c.m_shape1; s2 = c.m_shape2; b1 = s1.m_body; b2 = s2.m_body; if ((b1.IsStatic() || b1.IsSleeping()) && (b2.IsStatic() || b2.IsSleeping())) { continue; } // Put the sweeps onto the same time interval. let t0: number = b1.m_sweep.t0; if (b1.m_sweep.t0 < b2.m_sweep.t0) { t0 = b2.m_sweep.t0; b1.m_sweep.Advance(t0); } else if (b2.m_sweep.t0 < b1.m_sweep.t0) { t0 = b1.m_sweep.t0; b2.m_sweep.Advance(t0); } //b2Settings.b2Assert(t0 < 1.0f); // Compute the time of impact. toi = b2TimeOfImpact.TimeOfImpact(c.m_shape1, b1.m_sweep, c.m_shape2, b2.m_sweep); //b2Settings.b2Assert(0.0 <= toi && toi <= 1.0); if (toi > 0.0 && toi < 1.0) { //toi = Math.min((1.0 - toi) * t0 + toi, 1.0); toi = (1.0 - toi) * t0 + toi; if (toi > 1) toi = 1; } c.m_toi = toi; c.m_flags |= b2Contact.e_toiFlag; } if (Number.MIN_VALUE < toi && toi < minTOI) { // This is the minimum TOI found so far. minContact = c; minTOI = toi; } } if (minContact == null || 1.0 - 100.0 * Number.MIN_VALUE < minTOI) { // No more TOI events. Done! break; } // Advance the bodies to the TOI. s1 = minContact.m_shape1; s2 = minContact.m_shape2; b1 = s1.m_body; b2 = s2.m_body; b1.Advance(minTOI); b2.Advance(minTOI); // The TOI contact likely has some new contact points. minContact.Update(this.m_contactListener); minContact.m_flags &= ~b2Contact.e_toiFlag; if (minContact.m_manifoldCount == 0) { // This shouldn't happen. Numerical error? //b2Assert(false); continue; } // Build the TOI island. We need a dynamic seed. let seed: b2Body = b1; if (seed.IsStatic()) { seed = b2; } // Reset island and stack. island.Clear(); let stackCount: number /** int */ = 0; stack[stackCount++] = seed; seed.m_flags |= b2Body.e_islandFlag; // Perform a depth first search (DFS) on the contact graph. while (stackCount > 0) { // Grab the next body off the stack and add it to the island. b = stack[--stackCount]; island.AddBody(b); // Make sure the body is awake. b.m_flags &= ~b2Body.e_sleepFlag; // To keep islands as small as possible, we don't // propagate islands across static bodies. if (b.IsStatic()) { continue; } // Search all contacts connected to this body. for (cn = b.m_contactList; cn; cn = cn.next) { // Does the TOI island still have space for contacts? if (island.m_contactCount == island.m_contactCapacity) { continue; } // Has this contact already been added to an island? Skip slow or non-solid contacts. if (cn.contact.m_flags & (b2Contact.e_islandFlag | b2Contact.e_slowFlag | b2Contact.e_nonSolidFlag)) { continue; } // Is this contact touching? For performance we are not updating this contact. if (cn.contact.m_manifoldCount == 0) { continue; } island.AddContact(cn.contact); cn.contact.m_flags |= b2Contact.e_islandFlag; // Update other body. const other: b2Body = cn.other; // Was the other body already added to this island? if (other.m_flags & b2Body.e_islandFlag) { continue; } // March forward, this can do no harm since this is the min TOI. if (other.IsStatic() == false) { other.Advance(minTOI); other.WakeUp(); } //b2Settings.b2Assert(stackCount < stackSize); stack[stackCount++] = other; other.m_flags |= b2Body.e_islandFlag; } } const subStep: b2TimeStep = new b2TimeStep(); subStep.dt = (1.0 - minTOI) * step.dt; //b2Settings.b2Assert(subStep.dt > Number.MIN_VALUE); subStep.inv_dt = 1.0 / subStep.dt; subStep.maxIterations = step.maxIterations; island.SolveTOI(subStep); var i: number /** int */; // Post solve cleanup. for (i = 0; i < island.m_bodyCount; ++i) { // Allow bodies to participate in future TOI islands. b = island.m_bodies[i]; b.m_flags &= ~b2Body.e_islandFlag; if (b.m_flags & (b2Body.e_sleepFlag | b2Body.e_frozenFlag)) { continue; } if (b.IsStatic()) { continue; } // Update shapes (for broad-phase). If the shapes go out of // the world AABB then shapes and contacts may be destroyed, // including contacts that are const inRange: boolean = b.SynchronizeShapes(); // Did the body's shapes leave the world? if (inRange == false && this.m_boundaryListener != null) { this.m_boundaryListener.Violation(b); } // Invalidate all contact TOIs associated with this body. Some of these // may not be in the island because they were not touching. for (cn = b.m_contactList; cn; cn = cn.next) { cn.contact.m_flags &= ~b2Contact.e_toiFlag; } } for (i = 0; i < island.m_contactCount; ++i) { // Allow contacts to participate in future TOI islands. c = island.m_contacts[i]; c.m_flags &= ~(b2Contact.e_toiFlag | b2Contact.e_islandFlag); } // Commit shape proxy movements to the broad-phase so that new contacts are created. // Also, some contacts can be destroyed. this.m_broadPhase.Commit(); } //this.m_stackAllocator.Free(stack); } private static s_jointColor: b2Color = new b2Color(0.5, 0.8, 0.8); // public DrawJoint(joint: b2Joint): void { const b1: b2Body = joint.m_body1; const b2: b2Body = joint.m_body2; const xf1: b2XForm = b1.m_xf; const xf2: b2XForm = b2.m_xf; const x1: b2Vec2 = xf1.position; const x2: b2Vec2 = xf2.position; const p1: b2Vec2 = joint.GetAnchor1(); const p2: b2Vec2 = joint.GetAnchor2(); //b2Color color(0.5f, 0.8f, 0.8f); const color: b2Color = b2World.s_jointColor; switch (joint.m_type) { case b2Joint.e_distanceJoint: this.m_debugDraw.DrawSegment(p1, p2, color); break; case b2Joint.e_pulleyJoint: { const pulley: b2PulleyJoint = (joint as b2PulleyJoint); const s1: b2Vec2 = pulley.GetGroundAnchor1(); const s2: b2Vec2 = pulley.GetGroundAnchor2(); this.m_debugDraw.DrawSegment(s1, p1, color); this.m_debugDraw.DrawSegment(s2, p2, color); this.m_debugDraw.DrawSegment(s1, s2, color); } break; case b2Joint.e_mouseJoint: this.m_debugDraw.DrawSegment(p1, p2, color); break; default: if (b1 != this.m_groundBody) this.m_debugDraw.DrawSegment(x1, p1, color); this.m_debugDraw.DrawSegment(p1, p2, color); if (b2 != this.m_groundBody) this.m_debugDraw.DrawSegment(x2, p2, color); } } private static s_coreColor: b2Color = new b2Color(0.9, 0.6, 0.6); public DrawShape(shape: b2Shape, xf: b2XForm, color: b2Color, core: boolean): void { const coreColor: b2Color = b2World.s_coreColor; switch (shape.m_type) { case b2Shape.e_circleShape: { const circle: b2CircleShape = (shape as b2CircleShape); const center: b2Vec2 = b2Math.b2MulX(xf, circle.m_localPosition); const radius: number = circle.m_radius; const axis: b2Vec2 = xf.R.col1; this.m_debugDraw.DrawSolidCircle(center, radius, axis, color); if (core) { this.m_debugDraw.DrawCircle(center, radius - b2Settings.b2_toiSlop, coreColor); } } break; case b2Shape.e_polygonShape: { let i: number /** int */; const poly: b2PolygonShape = (shape as b2PolygonShape); const vertexCount: number /** int */ = poly.GetVertexCount(); const localVertices: b2Vec2[] = poly.GetVertices(); //b2Assert(vertexCount <= b2_maxPolygonVertices); const vertices: b2Vec2[] = new Array(b2Settings.b2_maxPolygonVertices); for (i = 0; i < vertexCount; ++i) { vertices[i] = b2Math.b2MulX(xf, localVertices[i]); } this.m_debugDraw.DrawSolidPolygon(vertices, vertexCount, color); if (core) { const localCoreVertices: b2Vec2[] = poly.GetCoreVertices(); for (i = 0; i < vertexCount; ++i) { vertices[i] = b2Math.b2MulX(xf, localCoreVertices[i]); } this.m_debugDraw.DrawPolygon(vertices, vertexCount, coreColor); } } break; case b2Shape.e_concaveArcShape: { var arc:b2ConcaveArcShape = (shape as b2ConcaveArcShape); const vertexCount = arc.GetVertexCount(); const localVertices = arc.GetVertices(); const center = b2Math.b2MulX(xf, arc.m_arcCenter); //b2Assert(vertexCount <= b2_maxPolygonVertices); const vertices = new Array(b2Settings.b2_maxPolygonVertices); for (let i = 0; i < vertexCount; ++i) { vertices[i] = b2Math.b2MulX(xf, localVertices[i]); } this.m_debugDraw.DrawSolidConcaveArc(vertices, vertexCount, center, color); if (core) { const localCoreVertices = arc.GetCoreVertices(); for (let i = 0; i < vertexCount; ++i) { vertices[i] = b2Math.b2MulX(xf, localCoreVertices[i]); } this.m_debugDraw.DrawConcaveArc(vertices, vertexCount, center, coreColor); } } break; case b2Shape.e_staticEdgeShape: { var edge:b2StaticEdgeShape = (shape as b2StaticEdgeShape); this.m_debugDraw.DrawSegment(edge.m_v1, edge.m_v2, color); if (core) { this.m_debugDraw.DrawSegment(edge.m_coreV1, edge.m_coreV2, coreColor); } } break; } } private static s_xf: b2XForm = new b2XForm(); public DrawDebugData(): void { if (this.m_debugDraw == null) { return; } this.m_debugDraw.m_sprite.graphics.clear(); const flags: number /** uint */ = this.m_debugDraw.GetFlags(); let i: number /** int */; let b: b2Body; let s: b2Shape; let j: b2Joint; let bp: b2BroadPhase; const invQ: b2Vec2 = new b2Vec2; const x1: b2Vec2 = new b2Vec2; const x2: b2Vec2 = new b2Vec2; const color: b2Color = new b2Color(0,0,0); let xf: b2XForm; const b1: b2AABB = new b2AABB(); const b2: b2AABB = new b2AABB(); const vs: b2Vec2[] = [new b2Vec2(), new b2Vec2(), new b2Vec2(), new b2Vec2()]; if (flags & b2DebugDraw.e_shapeBit) { const core: boolean = (flags & b2DebugDraw.e_coreShapeBit) == b2DebugDraw.e_coreShapeBit; for (b = this.m_bodyList; b; b = b.m_next) { xf = b.m_xf; for (s = b.GetShapeList(); s; s = s.m_next) { if (b.IsStatic()) { this.DrawShape(s, xf, new b2Color(0.5, 0.9, 0.5), core); } else if (b.IsSleeping()) { this.DrawShape(s, xf, new b2Color(0.5, 0.5, 0.9), core); } else { this.DrawShape(s, xf, new b2Color(0.9, 0.9, 0.9), core); } } } } if (flags & b2DebugDraw.e_jointBit) { for (j = this.m_jointList; j; j = j.m_next) { //if (j.m_type != b2Joint.e_mouseJoint) //{ this.DrawJoint(j); //} } } if (flags & b2DebugDraw.e_pairBit) { bp = this.m_broadPhase; //b2Vec2 invQ; invQ.Set(1.0 / bp.m_quantizationFactor.x, 1.0 / bp.m_quantizationFactor.y); //b2Color color(0.9f, 0.9f, 0.3f); color.Set(0.9, 0.9, 0.3); for (i = 0; i < b2Pair.b2_tableCapacity; ++i) { let index: number /** uint */ = bp.m_pairManager.m_hashTable[i]; while (index != b2Pair.b2_nullPair) { const pair: b2Pair = bp.m_pairManager.m_pairs[ index ]; const p1: b2Proxy = bp.m_proxyPool[ pair.proxyId1 ]; const p2: b2Proxy = bp.m_proxyPool[ pair.proxyId2 ]; //b2AABB b1, b2; b1.lowerBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p1.lowerBounds[0]].value; b1.lowerBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p1.lowerBounds[1]].value; b1.upperBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p1.upperBounds[0]].value; b1.upperBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p1.upperBounds[1]].value; b2.lowerBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p2.lowerBounds[0]].value; b2.lowerBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p2.lowerBounds[1]].value; b2.upperBound.x = bp.m_worldAABB.lowerBound.x + invQ.x * bp.m_bounds[0][p2.upperBounds[0]].value; b2.upperBound.y = bp.m_worldAABB.lowerBound.y + invQ.y * bp.m_bounds[1][p2.upperBounds[1]].value; //b2Vec2 x1 = 0.5f * (b1.lowerBound + b1.upperBound); x1.x = 0.5 * (b1.lowerBound.x + b1.upperBound.x); x1.y = 0.5 * (b1.lowerBound.y + b1.upperBound.y); //b2Vec2 x2 = 0.5f * (b2.lowerBound + b2.upperBound); x2.x = 0.5 * (b2.lowerBound.x + b2.upperBound.x); x2.y = 0.5 * (b2.lowerBound.y + b2.upperBound.y); this.m_debugDraw.DrawSegment(x1, x2, color); index = pair.next; } } } if (flags & b2DebugDraw.e_aabbBit) { bp = this.m_broadPhase; const worldLower: b2Vec2 = bp.m_worldAABB.lowerBound; const worldUpper: b2Vec2 = bp.m_worldAABB.upperBound; //b2Vec2 invQ; invQ.Set(1.0 / bp.m_quantizationFactor.x, 1.0 / bp.m_quantizationFactor.y); //b2Color color(0.9f, 0.3f, 0.9f); color.Set(0.9, 0.3, 0.9); for (i = 0; i < b2Settings.b2_maxProxies; ++i) { const p: b2Proxy = bp.m_proxyPool[ i]; if (p.IsValid() == false) { continue; } //b2AABB b1; b1.lowerBound.x = worldLower.x + invQ.x * bp.m_bounds[0][p.lowerBounds[0]].value; b1.lowerBound.y = worldLower.y + invQ.y * bp.m_bounds[1][p.lowerBounds[1]].value; b1.upperBound.x = worldLower.x + invQ.x * bp.m_bounds[0][p.upperBounds[0]].value; b1.upperBound.y = worldLower.y + invQ.y * bp.m_bounds[1][p.upperBounds[1]].value; //b2Vec2 vs[4]; vs[0].Set(b1.lowerBound.x, b1.lowerBound.y); vs[1].Set(b1.upperBound.x, b1.lowerBound.y); vs[2].Set(b1.upperBound.x, b1.upperBound.y); vs[3].Set(b1.lowerBound.x, b1.upperBound.y); this.m_debugDraw.DrawPolygon(vs, 4, color); } //b2Vec2 vs[4]; vs[0].Set(worldLower.x, worldLower.y); vs[1].Set(worldUpper.x, worldLower.y); vs[2].Set(worldUpper.x, worldUpper.y); vs[3].Set(worldLower.x, worldUpper.y); this.m_debugDraw.DrawPolygon(vs, 4, new b2Color(0.3, 0.9, 0.9)); } if (flags & b2DebugDraw.e_obbBit) { //b2Color color(0.5f, 0.3f, 0.5f); color.Set(0.5, 0.3, 0.5); for (b = this.m_bodyList; b; b = b.m_next) { xf = b.m_xf; for (s = b.GetShapeList(); s; s = s.m_next) { if (s.m_type != b2Shape.e_polygonShape) { continue; } const poly: b2PolygonShape = (s as b2PolygonShape); const obb: b2OBB = poly.GetOBB(); const h: b2Vec2 = obb.extents; //b2Vec2 vs[4]; vs[0].Set(-h.x, -h.y); vs[1].Set(h.x, -h.y); vs[2].Set(h.x, h.y); vs[3].Set(-h.x, h.y); for (i = 0; i < 4; ++i) { //vs[i] = obb.center + b2Mul(obb.R, vs[i]); let tMat: b2Mat22 = obb.R; const tVec: b2Vec2 = vs[i]; var tX: number; tX = obb.center.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); vs[i].y = obb.center.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); vs[i].x = tX; //vs[i] = b2Mul(xf, vs[i]); tMat = xf.R; tX = xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); vs[i].y = xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); vs[i].x = tX; } this.m_debugDraw.DrawPolygon(vs, 4, color); } } } if (flags & b2DebugDraw.e_centerOfMassBit) { for (b = this.m_bodyList; b; b = b.m_next) { xf = b2World.s_xf; xf.R = b.m_xf.R; xf.position = b.GetWorldCenter(); this.m_debugDraw.DrawXForm(xf); } } } public m_blockAllocator: any; public m_stackAllocator: any; public m_lock: boolean; public m_broadPhase: b2BroadPhase; public m_contactManager: b2ContactManager = new b2ContactManager(); public m_bodyList: b2Body; public m_jointList: b2Joint; // Do not access public m_contactList: b2Contact; public m_bodyCount: number /** int */; public m_contactCount: number /** int */; public m_jointCount: number /** int */; public m_gravity: b2Vec2; public m_allowSleep: boolean; public m_groundBody: b2Body; public m_destructionListener: b2DestructionListener; public m_boundaryListener: b2BoundaryListener; public m_contactFilter: b2ContactFilter; public m_contactListener: b2ContactListener; public m_debugDraw: b2DebugDraw; public m_inv_dt0: number; public m_positionIterationCount: number /** int */; // This is for debugging the solver. public static m_positionCorrection: boolean; // This is for debugging the solver. public static m_warmStarting: boolean; // This is for debugging the solver. public static m_continuousPhysics: boolean; }