UNPKG

the-world-engine

Version:

three.js based, unity like game engine for browser

1,359 lines (1,329 loc) 130 kB
import { b2_linearSlop, b2_maxFloat, b2_invalidParticleIndex, b2_minParticleSystemBufferCapacity, b2_maxTriadDistanceSquared, b2_barrierCollisionTime, b2MakeArray, b2Maybe } from "../common/b2_settings.js"; import { b2_maxParticlePressure, b2_minParticleWeight, b2_maxParticleForce, b2_particleStride } from "../common/b2_settings.js"; import { b2Min, b2Max, b2Abs, b2Clamp, b2Sqrt, b2InvSqrt, b2Vec2, b2TypedVec2, b2Rot, b2Transform } from "../common/b2_math.js"; import { b2Color, b2TypedColor } from "../common/b2_draw.js"; import { b2AABB, b2RayCastInput, b2RayCastOutput } from "../collision/b2_collision.js"; import { b2ShapeType, b2Shape } from "../collision/b2_shape.js"; import { b2EdgeShape } from "../collision/b2_edge_shape.js"; import { b2TimeStep } from "../dynamics/b2_time_step.js"; import { b2QueryCallback } from "../dynamics/b2_world_callbacks.js"; import { b2ParticleFlag, b2ParticleDef, b2ParticleHandle } from "./b2_particle.js"; import { b2ParticleGroupFlag, b2ParticleGroupDef, b2ParticleGroup } from "./b2_particle_group.js"; import { b2VoronoiDiagram } from "./b2_voronoi_diagram.js"; function std_iter_swap(t, s, i) { const e = t[s]; t[s] = t[i]; t[i] = e; } function default_compare(t, s) { return t < s; } function std_sort(t, s = 0, i = t.length - s, e = default_compare) { let c = s; const r = []; let n = 0; for (;;) { for (;c + 1 < i; i++) { const s = t[c + Math.floor(Math.random() * (i - c))]; r[n++] = i; for (let r = c - 1; ;) { while (e(t[++r], s)) {} while (e(s, t[--i])) {} if (r >= i) { break; } std_iter_swap(t, r, i); } } if (n === 0) { break; } c = i; i = r[--n]; } return t; } function std_stable_sort(t, s = 0, i = t.length - s, e = default_compare) { return std_sort(t, s, i, e); } function std_remove_if(t, s, i = t.length) { let e = 0; for (let c = 0; c < i; ++c) { if (s(t[c])) { continue; } if (c === e) { ++e; continue; } std_iter_swap(t, e++, c); } return e; } function std_lower_bound(t, s, i, e, c) { let r = i - s; while (r > 0) { const i = Math.floor(r / 2); let n = s + i; if (c(t[n], e)) { s = ++n; r -= i + 1; } else { r = i; } } return s; } function std_upper_bound(t, s, i, e, c) { let r = i - s; while (r > 0) { const i = Math.floor(r / 2); let n = s + i; if (!c(e, t[n])) { s = ++n; r -= i + 1; } else { r = i; } } return s; } function std_rotate(t, s, i, e) { let c = i; while (s !== c) { std_iter_swap(t, s++, c++); if (c === e) { c = i; } else if (s === i) { i = c; } } } function std_unique(t, s, i, e) { if (s === i) { return i; } let c = s; while (++s !== i) { if (!e(t[c], t[s])) { std_iter_swap(t, ++c, s); } } return ++c; } export class b2GrowableBuffer { constructor(t) { this.data = []; this.count = 0; this.capacity = 0; this.allocator = t; } Append() { if (this.count >= this.capacity) { this.Grow(); } return this.count++; } Reserve(t) { if (this.capacity >= t) { return; } for (let s = this.capacity; s < t; ++s) { this.data[s] = this.allocator(); } this.capacity = t; } Grow() { const t = this.capacity ? 2 * this.capacity : b2_minParticleSystemBufferCapacity; this.Reserve(t); } Free() { if (this.data.length === 0) { return; } this.data = []; this.capacity = 0; this.count = 0; } Shorten(t) {} Data() { return this.data; } GetCount() { return this.count; } SetCount(t) { this.count = t; } GetCapacity() { return this.capacity; } RemoveIf(t) { this.count = std_remove_if(this.data, t, this.count); } Unique(t) { this.count = std_unique(this.data, 0, this.count, t); } } export class b2FixtureParticleQueryCallback extends b2QueryCallback { constructor(t) { super(); this.m_system = t; } ShouldQueryParticleSystem(t) { return false; } ReportFixture(t) { if (t.IsSensor()) { return true; } const s = t.GetShape(); const i = s.GetChildCount(); for (let s = 0; s < i; s++) { const i = t.GetAABB(s); const e = this.m_system.GetInsideBoundsEnumerator(i); let c; while ((c = e.GetNext()) >= 0) { this.ReportFixtureAndParticle(t, s, c); } } return true; } ReportParticle(t, s) { return false; } ReportFixtureAndParticle(t, s, i) {} } export class b2ParticleContact { constructor() { this.indexA = 0; this.indexB = 0; this.weight = 0; this.normal = new b2Vec2; this.flags = 0; } SetIndices(t, s) { this.indexA = t; this.indexB = s; } SetWeight(t) { this.weight = t; } SetNormal(t) { this.normal.Copy(t); } SetFlags(t) { this.flags = t; } GetIndexA() { return this.indexA; } GetIndexB() { return this.indexB; } GetWeight() { return this.weight; } GetNormal() { return this.normal; } GetFlags() { return this.flags; } IsEqual(t) { return this.indexA === t.indexA && this.indexB === t.indexB && this.flags === t.flags && this.weight === t.weight && this.normal.x === t.normal.x && this.normal.y === t.normal.y; } IsNotEqual(t) { return !this.IsEqual(t); } ApproximatelyEqual(t) { const s = .01; const i = .01 * .01; return this.indexA === t.indexA && this.indexB === t.indexB && this.flags === t.flags && b2Abs(this.weight - t.weight) < s && b2Vec2.DistanceSquaredVV(this.normal, t.normal) < i; } } export class b2ParticleBodyContact { constructor() { this.index = 0; this.weight = 0; this.normal = new b2Vec2; this.mass = 0; } } export class b2ParticlePair { constructor() { this.indexA = 0; this.indexB = 0; this.flags = 0; this.strength = 0; this.distance = 0; } } export class b2ParticleTriad { constructor() { this.indexA = 0; this.indexB = 0; this.indexC = 0; this.flags = 0; this.strength = 0; this.pa = new b2Vec2(0, 0); this.pb = new b2Vec2(0, 0); this.pc = new b2Vec2(0, 0); this.ka = 0; this.kb = 0; this.kc = 0; this.s = 0; } } export class b2ParticleSystemDef { constructor() { this.strictContactCheck = false; this.density = 1; this.gravityScale = 1; this.radius = 1; this.maxCount = 0; this.pressureStrength = .005; this.dampingStrength = 1; this.elasticStrength = .25; this.springStrength = .25; this.viscousStrength = .25; this.surfaceTensionPressureStrength = .2; this.surfaceTensionNormalStrength = .2; this.repulsiveStrength = 1; this.powderStrength = .5; this.ejectionStrength = .5; this.staticPressureStrength = .2; this.staticPressureRelaxation = .2; this.staticPressureIterations = 8; this.colorMixingStrength = .5; this.destroyByAge = true; this.lifetimeGranularity = 1 / 60; } Copy(t) { this.strictContactCheck = t.strictContactCheck; this.density = t.density; this.gravityScale = t.gravityScale; this.radius = t.radius; this.maxCount = t.maxCount; this.pressureStrength = t.pressureStrength; this.dampingStrength = t.dampingStrength; this.elasticStrength = t.elasticStrength; this.springStrength = t.springStrength; this.viscousStrength = t.viscousStrength; this.surfaceTensionPressureStrength = t.surfaceTensionPressureStrength; this.surfaceTensionNormalStrength = t.surfaceTensionNormalStrength; this.repulsiveStrength = t.repulsiveStrength; this.powderStrength = t.powderStrength; this.ejectionStrength = t.ejectionStrength; this.staticPressureStrength = t.staticPressureStrength; this.staticPressureRelaxation = t.staticPressureRelaxation; this.staticPressureIterations = t.staticPressureIterations; this.colorMixingStrength = t.colorMixingStrength; this.destroyByAge = t.destroyByAge; this.lifetimeGranularity = t.lifetimeGranularity; return this; } Clone() { return (new b2ParticleSystemDef).Copy(this); } } export class b2ParticleSystem { constructor(t, s) { this.m_paused = false; this.m_timestamp = 0; this.m_allParticleFlags = 0; this.m_needsUpdateAllParticleFlags = false; this.m_allGroupFlags = 0; this.m_needsUpdateAllGroupFlags = false; this.m_hasForce = false; this.m_iterationIndex = 0; this.m_inverseDensity = 0; this.m_particleDiameter = 0; this.m_inverseDiameter = 0; this.m_squaredDiameter = 0; this.m_count = 0; this.m_internalAllocatedCapacity = 0; this.m_handleIndexBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_flagsBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_positionBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_velocityBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_forceBuffer = []; this.m_weightBuffer = []; this.m_staticPressureBuffer = []; this.m_accumulationBuffer = []; this.m_accumulation2Buffer = []; this.m_depthBuffer = []; this.m_colorBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_groupBuffer = []; this.m_userDataBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_stuckThreshold = 0; this.m_lastBodyContactStepBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_bodyContactCountBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_consecutiveContactStepsBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_stuckParticleBuffer = new b2GrowableBuffer((() => 0)); this.m_proxyBuffer = new b2GrowableBuffer((() => new b2ParticleSystem_Proxy)); this.m_contactBuffer = new b2GrowableBuffer((() => new b2ParticleContact)); this.m_bodyContactBuffer = new b2GrowableBuffer((() => new b2ParticleBodyContact)); this.m_pairBuffer = new b2GrowableBuffer((() => new b2ParticlePair)); this.m_triadBuffer = new b2GrowableBuffer((() => new b2ParticleTriad)); this.m_expirationTimeBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_indexByExpirationTimeBuffer = new b2ParticleSystem_UserOverridableBuffer; this.m_timeElapsed = 0; this.m_expirationTimeBufferRequiresSorting = false; this.m_groupCount = 0; this.m_groupList = null; this.m_def = new b2ParticleSystemDef; this.m_prev = null; this.m_next = null; this.UpdateBodyContacts_callback = null; this.SolveCollision_callback = null; this.SetStrictContactCheck(t.strictContactCheck); this.SetDensity(t.density); this.SetGravityScale(t.gravityScale); this.SetRadius(t.radius); this.SetMaxParticleCount(t.maxCount); this.m_def = t.Clone(); this.m_world = s; this.SetDestructionByAge(this.m_def.destroyByAge); } static computeTag(t, s) { return (s + b2ParticleSystem.yOffset >>> 0 << b2ParticleSystem.yShift) + (b2ParticleSystem.xScale * t + b2ParticleSystem.xOffset >>> 0) >>> 0; } static computeRelativeTag(t, s, i) { return t + (i << b2ParticleSystem.yShift) + (s << b2ParticleSystem.xShift) >>> 0; } Drop() { while (this.m_groupList) { this.DestroyParticleGroup(this.m_groupList); } this.FreeUserOverridableBuffer(this.m_handleIndexBuffer); this.FreeUserOverridableBuffer(this.m_flagsBuffer); this.FreeUserOverridableBuffer(this.m_lastBodyContactStepBuffer); this.FreeUserOverridableBuffer(this.m_bodyContactCountBuffer); this.FreeUserOverridableBuffer(this.m_consecutiveContactStepsBuffer); this.FreeUserOverridableBuffer(this.m_positionBuffer); this.FreeUserOverridableBuffer(this.m_velocityBuffer); this.FreeUserOverridableBuffer(this.m_colorBuffer); this.FreeUserOverridableBuffer(this.m_userDataBuffer); this.FreeUserOverridableBuffer(this.m_expirationTimeBuffer); this.FreeUserOverridableBuffer(this.m_indexByExpirationTimeBuffer); this.FreeBuffer(this.m_forceBuffer, this.m_internalAllocatedCapacity); this.FreeBuffer(this.m_weightBuffer, this.m_internalAllocatedCapacity); this.FreeBuffer(this.m_staticPressureBuffer, this.m_internalAllocatedCapacity); this.FreeBuffer(this.m_accumulationBuffer, this.m_internalAllocatedCapacity); this.FreeBuffer(this.m_accumulation2Buffer, this.m_internalAllocatedCapacity); this.FreeBuffer(this.m_depthBuffer, this.m_internalAllocatedCapacity); this.FreeBuffer(this.m_groupBuffer, this.m_internalAllocatedCapacity); } CreateParticle(t) { if (this.m_world.IsLocked()) { throw new Error; } if (this.m_count >= this.m_internalAllocatedCapacity) { const t = this.m_count ? 2 * this.m_count : b2_minParticleSystemBufferCapacity; this.ReallocateInternalAllocatedBuffers(t); } if (this.m_count >= this.m_internalAllocatedCapacity) { if (this.m_def.destroyByAge) { this.DestroyOldestParticle(0, false); this.SolveZombie(); } else { return b2_invalidParticleIndex; } } const s = this.m_count++; this.m_flagsBuffer.data[s] = 0; if (this.m_lastBodyContactStepBuffer.data) { this.m_lastBodyContactStepBuffer.data[s] = 0; } if (this.m_bodyContactCountBuffer.data) { this.m_bodyContactCountBuffer.data[s] = 0; } if (this.m_consecutiveContactStepsBuffer.data) { this.m_consecutiveContactStepsBuffer.data[s] = 0; } this.m_positionBuffer.data[s] = (this.m_positionBuffer.data[s] || new b2Vec2).Copy(b2Maybe(t.position, b2Vec2.ZERO)); this.m_velocityBuffer.data[s] = (this.m_velocityBuffer.data[s] || new b2Vec2).Copy(b2Maybe(t.velocity, b2Vec2.ZERO)); this.m_weightBuffer[s] = 0; this.m_forceBuffer[s] = (this.m_forceBuffer[s] || new b2Vec2).SetZero(); if (this.m_staticPressureBuffer) { this.m_staticPressureBuffer[s] = 0; } if (this.m_depthBuffer) { this.m_depthBuffer[s] = 0; } const i = (new b2Color).Copy(b2Maybe(t.color, b2Color.ZERO)); if (this.m_colorBuffer.data || !i.IsZero()) { this.m_colorBuffer.data = this.RequestBuffer(this.m_colorBuffer.data); this.m_colorBuffer.data[s] = (this.m_colorBuffer.data[s] || new b2Color).Copy(i); } if (this.m_userDataBuffer.data || t.userData) { this.m_userDataBuffer.data = this.RequestBuffer(this.m_userDataBuffer.data); this.m_userDataBuffer.data[s] = t.userData; } if (this.m_handleIndexBuffer.data) { this.m_handleIndexBuffer.data[s] = null; } const e = this.m_proxyBuffer.data[this.m_proxyBuffer.Append()]; const c = b2Maybe(t.lifetime, 0); const r = c > 0; if (this.m_expirationTimeBuffer.data || r) { this.SetParticleLifetime(s, r ? c : this.ExpirationTimeToLifetime(-this.GetQuantizedTimeElapsed())); this.m_indexByExpirationTimeBuffer.data[s] = s; } e.index = s; const n = b2Maybe(t.group, null); this.m_groupBuffer[s] = n; if (n) { if (n.m_firstIndex < n.m_lastIndex) { this.RotateBuffer(n.m_firstIndex, n.m_lastIndex, s); n.m_lastIndex = s + 1; } else { n.m_firstIndex = s; n.m_lastIndex = s + 1; } } this.SetParticleFlags(s, b2Maybe(t.flags, 0)); return s; } GetParticleHandleFromIndex(t) { this.m_handleIndexBuffer.data = this.RequestBuffer(this.m_handleIndexBuffer.data); let s = this.m_handleIndexBuffer.data[t]; if (s) { return s; } s = new b2ParticleHandle; s.SetIndex(t); this.m_handleIndexBuffer.data[t] = s; return s; } DestroyParticle(t, s = false) { let i = b2ParticleFlag.b2_zombieParticle; if (s) { i |= b2ParticleFlag.b2_destructionListenerParticle; } this.SetParticleFlags(t, this.m_flagsBuffer.data[t] | i); } DestroyOldestParticle(t, s = false) { const i = this.GetParticleCount(); const e = this.m_indexByExpirationTimeBuffer.data[i - (t + 1)]; const c = this.m_indexByExpirationTimeBuffer.data[t]; this.DestroyParticle(this.m_expirationTimeBuffer.data[e] > 0 ? e : c, s); } DestroyParticlesInShape(t, s, i = false) { const e = b2ParticleSystem.DestroyParticlesInShape_s_aabb; if (this.m_world.IsLocked()) { throw new Error; } const c = new b2ParticleSystem_DestroyParticlesInShapeCallback(this, t, s, i); const r = e; t.ComputeAABB(r, s, 0); this.m_world.QueryAABB(c, r); return c.Destroyed(); } CreateParticleGroup(t) { const s = b2ParticleSystem.CreateParticleGroup_s_transform; if (this.m_world.IsLocked()) { throw new Error; } const i = s; i.SetPositionAngle(b2Maybe(t.position, b2Vec2.ZERO), b2Maybe(t.angle, 0)); const e = this.m_count; if (t.shape) { this.CreateParticlesWithShapeForGroup(t.shape, t, i); } if (t.shapes) { this.CreateParticlesWithShapesForGroup(t.shapes, b2Maybe(t.shapeCount, t.shapes.length), t, i); } if (t.positionData) { const s = b2Maybe(t.particleCount, t.positionData.length); for (let e = 0; e < s; e++) { const s = t.positionData[e]; this.CreateParticleForGroup(t, i, s); } } const c = this.m_count; let r = new b2ParticleGroup(this); r.m_firstIndex = e; r.m_lastIndex = c; r.m_strength = b2Maybe(t.strength, 1); r.m_userData = t.userData; r.m_transform.Copy(i); r.m_prev = null; r.m_next = this.m_groupList; if (this.m_groupList) { this.m_groupList.m_prev = r; } this.m_groupList = r; ++this.m_groupCount; for (let t = e; t < c; t++) { this.m_groupBuffer[t] = r; } this.SetGroupFlags(r, b2Maybe(t.groupFlags, 0)); const n = new b2ParticleSystem_ConnectionFilter; this.UpdateContacts(true); this.UpdatePairsAndTriads(e, c, n); if (t.group) { this.JoinParticleGroups(t.group, r); r = t.group; } return r; } JoinParticleGroups(t, s) { if (this.m_world.IsLocked()) { throw new Error; } this.RotateBuffer(s.m_firstIndex, s.m_lastIndex, this.m_count); this.RotateBuffer(t.m_firstIndex, t.m_lastIndex, s.m_firstIndex); const i = new b2ParticleSystem_JoinParticleGroupsFilter(s.m_firstIndex); this.UpdateContacts(true); this.UpdatePairsAndTriads(t.m_firstIndex, s.m_lastIndex, i); for (let i = s.m_firstIndex; i < s.m_lastIndex; i++) { this.m_groupBuffer[i] = t; } const e = t.m_groupFlags | s.m_groupFlags; this.SetGroupFlags(t, e); t.m_lastIndex = s.m_lastIndex; s.m_firstIndex = s.m_lastIndex; this.DestroyParticleGroup(s); } SplitParticleGroup(t) { this.UpdateContacts(true); const s = t.GetParticleCount(); const i = b2MakeArray(s, (t => new b2ParticleSystem_ParticleListNode)); b2ParticleSystem.InitializeParticleLists(t, i); this.MergeParticleListsInContact(t, i); const e = b2ParticleSystem.FindLongestParticleList(t, i); this.MergeZombieParticleListNodes(t, i, e); this.CreateParticleGroupsFromParticleList(t, i, e); this.UpdatePairsAndTriadsWithParticleList(t, i); } GetParticleGroupList() { return this.m_groupList; } GetParticleGroupCount() { return this.m_groupCount; } GetParticleCount() { return this.m_count; } GetMaxParticleCount() { return this.m_def.maxCount; } SetMaxParticleCount(t) { this.m_def.maxCount = t; } GetAllParticleFlags() { return this.m_allParticleFlags; } GetAllGroupFlags() { return this.m_allGroupFlags; } SetPaused(t) { this.m_paused = t; } GetPaused() { return this.m_paused; } SetDensity(t) { this.m_def.density = t; this.m_inverseDensity = 1 / this.m_def.density; } GetDensity() { return this.m_def.density; } SetGravityScale(t) { this.m_def.gravityScale = t; } GetGravityScale() { return this.m_def.gravityScale; } SetDamping(t) { this.m_def.dampingStrength = t; } GetDamping() { return this.m_def.dampingStrength; } SetStaticPressureIterations(t) { this.m_def.staticPressureIterations = t; } GetStaticPressureIterations() { return this.m_def.staticPressureIterations; } SetRadius(t) { this.m_particleDiameter = 2 * t; this.m_squaredDiameter = this.m_particleDiameter * this.m_particleDiameter; this.m_inverseDiameter = 1 / this.m_particleDiameter; } GetRadius() { return this.m_particleDiameter / 2; } GetPositionBuffer() { return this.m_positionBuffer.data; } GetVelocityBuffer() { return this.m_velocityBuffer.data; } GetColorBuffer() { this.m_colorBuffer.data = this.RequestBuffer(this.m_colorBuffer.data); return this.m_colorBuffer.data; } GetGroupBuffer() { return this.m_groupBuffer; } GetWeightBuffer() { return this.m_weightBuffer; } GetUserDataBuffer() { this.m_userDataBuffer.data = this.RequestBuffer(this.m_userDataBuffer.data); return this.m_userDataBuffer.data; } GetFlagsBuffer() { return this.m_flagsBuffer.data; } SetParticleFlags(t, s) { const i = this.m_flagsBuffer.data[t]; if (i & ~s) { this.m_needsUpdateAllParticleFlags = true; } if (~this.m_allParticleFlags & s) { if (s & b2ParticleFlag.b2_tensileParticle) { this.m_accumulation2Buffer = this.RequestBuffer(this.m_accumulation2Buffer); } if (s & b2ParticleFlag.b2_colorMixingParticle) { this.m_colorBuffer.data = this.RequestBuffer(this.m_colorBuffer.data); } this.m_allParticleFlags |= s; } this.m_flagsBuffer.data[t] = s; } GetParticleFlags(t) { return this.m_flagsBuffer.data[t]; } SetFlagsBuffer(t) { this.SetUserOverridableBuffer(this.m_flagsBuffer, t); } SetPositionBuffer(t) { if (t instanceof Float32Array) { if (t.length % 2 !== 0) { throw new Error; } const s = t.length / 2; const i = new Array(s); for (let e = 0; e < s; ++e) { i[e] = new b2TypedVec2(t.subarray(e * 2, e * 2 + 2)); } t = i; } this.SetUserOverridableBuffer(this.m_positionBuffer, t); } SetVelocityBuffer(t) { if (t instanceof Float32Array) { if (t.length % 2 !== 0) { throw new Error; } const s = t.length / 2; const i = new Array(s); for (let e = 0; e < s; ++e) { i[e] = new b2TypedVec2(t.subarray(e * 2, e * 2 + 2)); } t = i; } this.SetUserOverridableBuffer(this.m_velocityBuffer, t); } SetColorBuffer(t) { if (t instanceof Float32Array) { if (t.length % 4 !== 0) { throw new Error; } const s = t.length / 4; const i = new Array(s); for (let e = 0; e < s; ++e) { i[e] = new b2TypedColor(t.subarray(e * 4, e * 4 + 4)); } t = i; } this.SetUserOverridableBuffer(this.m_colorBuffer, t); } SetUserDataBuffer(t) { this.SetUserOverridableBuffer(this.m_userDataBuffer, t); } GetContacts() { return this.m_contactBuffer.data; } GetContactCount() { return this.m_contactBuffer.count; } GetBodyContacts() { return this.m_bodyContactBuffer.data; } GetBodyContactCount() { return this.m_bodyContactBuffer.count; } GetPairs() { return this.m_pairBuffer.data; } GetPairCount() { return this.m_pairBuffer.count; } GetTriads() { return this.m_triadBuffer.data; } GetTriadCount() { return this.m_triadBuffer.count; } SetStuckThreshold(t) { this.m_stuckThreshold = t; if (t > 0) { this.m_lastBodyContactStepBuffer.data = this.RequestBuffer(this.m_lastBodyContactStepBuffer.data); this.m_bodyContactCountBuffer.data = this.RequestBuffer(this.m_bodyContactCountBuffer.data); this.m_consecutiveContactStepsBuffer.data = this.RequestBuffer(this.m_consecutiveContactStepsBuffer.data); } } GetStuckCandidates() { return this.m_stuckParticleBuffer.Data(); } GetStuckCandidateCount() { return this.m_stuckParticleBuffer.GetCount(); } ComputeCollisionEnergy() { const t = b2ParticleSystem.ComputeCollisionEnergy_s_v; const s = this.m_velocityBuffer.data; let i = 0; for (let e = 0; e < this.m_contactBuffer.count; e++) { const c = this.m_contactBuffer.data[e]; const r = c.indexA; const n = c.indexB; const o = c.normal; const h = b2Vec2.SubVV(s[n], s[r], t); const l = b2Vec2.DotVV(h, o); if (l < 0) { i += l * l; } } return .5 * this.GetParticleMass() * i; } SetStrictContactCheck(t) { this.m_def.strictContactCheck = t; } GetStrictContactCheck() { return this.m_def.strictContactCheck; } SetParticleLifetime(t, s) { const i = this.m_indexByExpirationTimeBuffer.data === null; this.m_expirationTimeBuffer.data = this.RequestBuffer(this.m_expirationTimeBuffer.data); this.m_indexByExpirationTimeBuffer.data = this.RequestBuffer(this.m_indexByExpirationTimeBuffer.data); if (i) { const t = this.GetParticleCount(); for (let s = 0; s < t; ++s) { this.m_indexByExpirationTimeBuffer.data[s] = s; } } const e = s / this.m_def.lifetimeGranularity; const c = e > 0 ? this.GetQuantizedTimeElapsed() + e : e; if (c !== this.m_expirationTimeBuffer.data[t]) { this.m_expirationTimeBuffer.data[t] = c; this.m_expirationTimeBufferRequiresSorting = true; } } GetParticleLifetime(t) { return this.ExpirationTimeToLifetime(this.GetExpirationTimeBuffer()[t]); } SetDestructionByAge(t) { if (t) { this.GetExpirationTimeBuffer(); } this.m_def.destroyByAge = t; } GetDestructionByAge() { return this.m_def.destroyByAge; } GetExpirationTimeBuffer() { this.m_expirationTimeBuffer.data = this.RequestBuffer(this.m_expirationTimeBuffer.data); return this.m_expirationTimeBuffer.data; } ExpirationTimeToLifetime(t) { return (t > 0 ? t - this.GetQuantizedTimeElapsed() : t) * this.m_def.lifetimeGranularity; } GetIndexByExpirationTimeBuffer() { if (this.GetParticleCount()) { this.SetParticleLifetime(0, this.GetParticleLifetime(0)); } else { this.m_indexByExpirationTimeBuffer.data = this.RequestBuffer(this.m_indexByExpirationTimeBuffer.data); } return this.m_indexByExpirationTimeBuffer.data; } ParticleApplyLinearImpulse(t, s) { this.ApplyLinearImpulse(t, t + 1, s); } ApplyLinearImpulse(t, s, i) { const e = this.m_velocityBuffer.data; const c = s - t; const r = c * this.GetParticleMass(); const n = (new b2Vec2).Copy(i).SelfMul(1 / r); for (let i = t; i < s; i++) { e[i].SelfAdd(n); } } static IsSignificantForce(t) { return t.x !== 0 || t.y !== 0; } ParticleApplyForce(t, s) { if (b2ParticleSystem.IsSignificantForce(s) && this.ForceCanBeApplied(this.m_flagsBuffer.data[t])) { this.PrepareForceBuffer(); this.m_forceBuffer[t].SelfAdd(s); } } ApplyForce(t, s, i) { const e = (new b2Vec2).Copy(i).SelfMul(1 / (s - t)); if (b2ParticleSystem.IsSignificantForce(e)) { this.PrepareForceBuffer(); for (let i = t; i < s; i++) { this.m_forceBuffer[i].SelfAdd(e); } } } GetNext() { return this.m_next; } QueryAABB(t, s) { if (this.m_proxyBuffer.count === 0) { return; } const i = 0; const e = this.m_proxyBuffer.count; const c = std_lower_bound(this.m_proxyBuffer.data, i, e, b2ParticleSystem.computeTag(this.m_inverseDiameter * s.lowerBound.x, this.m_inverseDiameter * s.lowerBound.y), b2ParticleSystem_Proxy.CompareProxyTag); const r = std_upper_bound(this.m_proxyBuffer.data, c, e, b2ParticleSystem.computeTag(this.m_inverseDiameter * s.upperBound.x, this.m_inverseDiameter * s.upperBound.y), b2ParticleSystem_Proxy.CompareTagProxy); const n = this.m_positionBuffer.data; for (let i = c; i < r; ++i) { const e = this.m_proxyBuffer.data[i]; const c = e.index; const r = n[c]; if (s.lowerBound.x < r.x && r.x < s.upperBound.x && s.lowerBound.y < r.y && r.y < s.upperBound.y) { if (!t.ReportParticle(this, c)) { break; } } } } QueryShapeAABB(t, s, i, e = 0) { const c = b2ParticleSystem.QueryShapeAABB_s_aabb; const r = c; s.ComputeAABB(r, i, e); this.QueryAABB(t, r); } QueryPointAABB(t, s, i = b2_linearSlop) { const e = b2ParticleSystem.QueryPointAABB_s_aabb; const c = e; c.lowerBound.Set(s.x - i, s.y - i); c.upperBound.Set(s.x + i, s.y + i); this.QueryAABB(t, c); } RayCast(t, s, i) { const e = b2ParticleSystem.RayCast_s_aabb; const c = b2ParticleSystem.RayCast_s_p; const r = b2ParticleSystem.RayCast_s_v; const n = b2ParticleSystem.RayCast_s_n; const o = b2ParticleSystem.RayCast_s_point; if (this.m_proxyBuffer.count === 0) { return; } const h = this.m_positionBuffer.data; const l = e; b2Vec2.MinV(s, i, l.lowerBound); b2Vec2.MaxV(s, i, l.upperBound); let a = 1; const b = b2Vec2.SubVV(i, s, r); const f = b2Vec2.DotVV(b, b); const P = this.GetInsideBoundsEnumerator(l); let u; while ((u = P.GetNext()) >= 0) { const i = b2Vec2.SubVV(s, h[u], c); const e = b2Vec2.DotVV(i, b); const r = b2Vec2.DotVV(i, i); const l = e * e - f * (r - this.m_squaredDiameter); if (l >= 0) { const c = b2Sqrt(l); let r = (-e - c) / f; if (r > a) { continue; } if (r < 0) { r = (-e + c) / f; if (r < 0 || r > a) { continue; } } const h = b2Vec2.AddVMulSV(i, r, b, n); h.Normalize(); const P = t.ReportParticle(this, u, b2Vec2.AddVMulSV(s, r, b, o), h, r); a = b2Min(a, P); if (a <= 0) { break; } } } } ComputeAABB(t) { const s = this.GetParticleCount(); t.lowerBound.x = +b2_maxFloat; t.lowerBound.y = +b2_maxFloat; t.upperBound.x = -b2_maxFloat; t.upperBound.y = -b2_maxFloat; const i = this.m_positionBuffer.data; for (let e = 0; e < s; e++) { const s = i[e]; b2Vec2.MinV(t.lowerBound, s, t.lowerBound); b2Vec2.MaxV(t.upperBound, s, t.upperBound); } t.lowerBound.x -= this.m_particleDiameter; t.lowerBound.y -= this.m_particleDiameter; t.upperBound.x += this.m_particleDiameter; t.upperBound.y += this.m_particleDiameter; } FreeBuffer(t, s) { if (t === null) { return; } t.length = 0; } FreeUserOverridableBuffer(t) { if (t.userSuppliedCapacity === 0) { this.FreeBuffer(t.data, this.m_internalAllocatedCapacity); } } ReallocateBuffer3(t, s, i) { if (i <= s) { throw new Error; } const e = t ? t.slice() : []; e.length = i; return e; } ReallocateBuffer5(t, s, i, e, c) { if (e <= i) { throw new Error; } if (!(!s || e <= s)) { throw new Error; } if ((!c || t) && !s) { t = this.ReallocateBuffer3(t, i, e); } return t; } ReallocateBuffer4(t, s, i, e) { return this.ReallocateBuffer5(t.data, t.userSuppliedCapacity, s, i, e); } RequestBuffer(t) { if (!t) { if (this.m_internalAllocatedCapacity === 0) { this.ReallocateInternalAllocatedBuffers(b2_minParticleSystemBufferCapacity); } t = []; t.length = this.m_internalAllocatedCapacity; } return t; } ReallocateHandleBuffers(t) { this.m_handleIndexBuffer.data = this.ReallocateBuffer4(this.m_handleIndexBuffer, this.m_internalAllocatedCapacity, t, true); } ReallocateInternalAllocatedBuffers(t) { function LimitCapacity(t, s) { return s && t > s ? s : t; } t = LimitCapacity(t, this.m_def.maxCount); t = LimitCapacity(t, this.m_flagsBuffer.userSuppliedCapacity); t = LimitCapacity(t, this.m_positionBuffer.userSuppliedCapacity); t = LimitCapacity(t, this.m_velocityBuffer.userSuppliedCapacity); t = LimitCapacity(t, this.m_colorBuffer.userSuppliedCapacity); t = LimitCapacity(t, this.m_userDataBuffer.userSuppliedCapacity); if (this.m_internalAllocatedCapacity < t) { this.ReallocateHandleBuffers(t); this.m_flagsBuffer.data = this.ReallocateBuffer4(this.m_flagsBuffer, this.m_internalAllocatedCapacity, t, false); const s = this.m_stuckThreshold > 0; this.m_lastBodyContactStepBuffer.data = this.ReallocateBuffer4(this.m_lastBodyContactStepBuffer, this.m_internalAllocatedCapacity, t, s); this.m_bodyContactCountBuffer.data = this.ReallocateBuffer4(this.m_bodyContactCountBuffer, this.m_internalAllocatedCapacity, t, s); this.m_consecutiveContactStepsBuffer.data = this.ReallocateBuffer4(this.m_consecutiveContactStepsBuffer, this.m_internalAllocatedCapacity, t, s); this.m_positionBuffer.data = this.ReallocateBuffer4(this.m_positionBuffer, this.m_internalAllocatedCapacity, t, false); this.m_velocityBuffer.data = this.ReallocateBuffer4(this.m_velocityBuffer, this.m_internalAllocatedCapacity, t, false); this.m_forceBuffer = this.ReallocateBuffer5(this.m_forceBuffer, 0, this.m_internalAllocatedCapacity, t, false); this.m_weightBuffer = this.ReallocateBuffer5(this.m_weightBuffer, 0, this.m_internalAllocatedCapacity, t, false); this.m_staticPressureBuffer = this.ReallocateBuffer5(this.m_staticPressureBuffer, 0, this.m_internalAllocatedCapacity, t, true); this.m_accumulationBuffer = this.ReallocateBuffer5(this.m_accumulationBuffer, 0, this.m_internalAllocatedCapacity, t, false); this.m_accumulation2Buffer = this.ReallocateBuffer5(this.m_accumulation2Buffer, 0, this.m_internalAllocatedCapacity, t, true); this.m_depthBuffer = this.ReallocateBuffer5(this.m_depthBuffer, 0, this.m_internalAllocatedCapacity, t, true); this.m_colorBuffer.data = this.ReallocateBuffer4(this.m_colorBuffer, this.m_internalAllocatedCapacity, t, true); this.m_groupBuffer = this.ReallocateBuffer5(this.m_groupBuffer, 0, this.m_internalAllocatedCapacity, t, false); this.m_userDataBuffer.data = this.ReallocateBuffer4(this.m_userDataBuffer, this.m_internalAllocatedCapacity, t, true); this.m_expirationTimeBuffer.data = this.ReallocateBuffer4(this.m_expirationTimeBuffer, this.m_internalAllocatedCapacity, t, true); this.m_indexByExpirationTimeBuffer.data = this.ReallocateBuffer4(this.m_indexByExpirationTimeBuffer, this.m_internalAllocatedCapacity, t, false); this.m_internalAllocatedCapacity = t; } } CreateParticleForGroup(t, s, i) { const e = new b2ParticleDef; e.flags = b2Maybe(t.flags, 0); b2Transform.MulXV(s, i, e.position); b2Vec2.AddVV(b2Maybe(t.linearVelocity, b2Vec2.ZERO), b2Vec2.CrossSV(b2Maybe(t.angularVelocity, 0), b2Vec2.SubVV(e.position, b2Maybe(t.position, b2Vec2.ZERO), b2Vec2.s_t0), b2Vec2.s_t0), e.velocity); e.color.Copy(b2Maybe(t.color, b2Color.ZERO)); e.lifetime = b2Maybe(t.lifetime, 0); e.userData = t.userData; this.CreateParticle(e); } CreateParticlesStrokeShapeForGroup(t, s, i) { const e = b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_edge; const c = b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_d; const r = b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_p; let n = b2Maybe(s.stride, 0); if (n === 0) { n = this.GetParticleStride(); } let o = 0; const h = t.GetChildCount(); for (let l = 0; l < h; l++) { let h = null; if (t.GetType() === b2ShapeType.e_edgeShape) { h = t; } else { h = e; t.GetChildEdge(h, l); } const a = b2Vec2.SubVV(h.m_vertex2, h.m_vertex1, c); const b = a.Length(); while (o < b) { const t = b2Vec2.AddVMulSV(h.m_vertex1, o / b, a, r); this.CreateParticleForGroup(s, i, t); o += n; } o -= b; } } CreateParticlesFillShapeForGroup(t, s, i) { const e = b2ParticleSystem.CreateParticlesFillShapeForGroup_s_aabb; const c = b2ParticleSystem.CreateParticlesFillShapeForGroup_s_p; let r = b2Maybe(s.stride, 0); if (r === 0) { r = this.GetParticleStride(); } const n = b2Transform.IDENTITY; const o = e; t.ComputeAABB(o, n, 0); for (let e = Math.floor(o.lowerBound.y / r) * r; e < o.upperBound.y; e += r) { for (let h = Math.floor(o.lowerBound.x / r) * r; h < o.upperBound.x; h += r) { const r = c.Set(h, e); if (t.TestPoint(n, r)) { this.CreateParticleForGroup(s, i, r); } } } } CreateParticlesWithShapeForGroup(t, s, i) { switch (t.GetType()) { case b2ShapeType.e_edgeShape: case b2ShapeType.e_chainShape: this.CreateParticlesStrokeShapeForGroup(t, s, i); break; case b2ShapeType.e_polygonShape: case b2ShapeType.e_circleShape: this.CreateParticlesFillShapeForGroup(t, s, i); break; default: break; } } CreateParticlesWithShapesForGroup(t, s, i, e) { const c = new b2ParticleSystem_CompositeShape(t, s); this.CreateParticlesFillShapeForGroup(c, i, e); } CloneParticle(t, s) { const i = new b2ParticleDef; i.flags = this.m_flagsBuffer.data[t]; i.position.Copy(this.m_positionBuffer.data[t]); i.velocity.Copy(this.m_velocityBuffer.data[t]); if (this.m_colorBuffer.data) { i.color.Copy(this.m_colorBuffer.data[t]); } if (this.m_userDataBuffer.data) { i.userData = this.m_userDataBuffer.data[t]; } i.group = s; const e = this.CreateParticle(i); if (this.m_handleIndexBuffer.data) { const s = this.m_handleIndexBuffer.data[t]; if (s) { s.SetIndex(e); } this.m_handleIndexBuffer.data[e] = s; this.m_handleIndexBuffer.data[t] = null; } if (this.m_lastBodyContactStepBuffer.data) { this.m_lastBodyContactStepBuffer.data[e] = this.m_lastBodyContactStepBuffer.data[t]; } if (this.m_bodyContactCountBuffer.data) { this.m_bodyContactCountBuffer.data[e] = this.m_bodyContactCountBuffer.data[t]; } if (this.m_consecutiveContactStepsBuffer.data) { this.m_consecutiveContactStepsBuffer.data[e] = this.m_consecutiveContactStepsBuffer.data[t]; } if (this.m_hasForce) { this.m_forceBuffer[e].Copy(this.m_forceBuffer[t]); } if (this.m_staticPressureBuffer) { this.m_staticPressureBuffer[e] = this.m_staticPressureBuffer[t]; } if (this.m_depthBuffer) { this.m_depthBuffer[e] = this.m_depthBuffer[t]; } if (this.m_expirationTimeBuffer.data) { this.m_expirationTimeBuffer.data[e] = this.m_expirationTimeBuffer.data[t]; } return e; } DestroyParticlesInGroup(t, s = false) { for (let i = t.m_firstIndex; i < t.m_lastIndex; i++) { this.DestroyParticle(i, s); } } DestroyParticleGroup(t) { if (this.m_world.m_destructionListener) { this.m_world.m_destructionListener.SayGoodbyeParticleGroup(t); } this.SetGroupFlags(t, 0); for (let s = t.m_firstIndex; s < t.m_lastIndex; s++) { this.m_groupBuffer[s] = null; } if (t.m_prev) { t.m_prev.m_next = t.m_next; } if (t.m_next) { t.m_next.m_prev = t.m_prev; } if (t === this.m_groupList) { this.m_groupList = t.m_next; } --this.m_groupCount; } static ParticleCanBeConnected(t, s) { return (t & (b2ParticleFlag.b2_wallParticle | b2ParticleFlag.b2_springParticle | b2ParticleFlag.b2_elasticParticle)) !== 0 || s !== null && (s.GetGroupFlags() & b2ParticleGroupFlag.b2_rigidParticleGroup) !== 0; } UpdatePairsAndTriads(t, s, i) { const e = b2ParticleSystem.UpdatePairsAndTriads_s_dab; const c = b2ParticleSystem.UpdatePairsAndTriads_s_dbc; const r = b2ParticleSystem.UpdatePairsAndTriads_s_dca; const n = this.m_positionBuffer.data; let o = 0; for (let i = t; i < s; i++) { o |= this.m_flagsBuffer.data[i]; } if (o & b2ParticleSystem.k_pairFlags) { for (let e = 0; e < this.m_contactBuffer.count; e++) { const c = this.m_contactBuffer.data[e]; const r = c.indexA; const o = c.indexB; const h = this.m_flagsBuffer.data[r]; const l = this.m_flagsBuffer.data[o]; const a = this.m_groupBuffer[r]; const b = this.m_groupBuffer[o]; if (r >= t && r < s && o >= t && o < s && !((h | l) & b2ParticleFlag.b2_zombieParticle) && (h | l) & b2ParticleSystem.k_pairFlags && (i.IsNecessary(r) || i.IsNecessary(o)) && b2ParticleSystem.ParticleCanBeConnected(h, a) && b2ParticleSystem.ParticleCanBeConnected(l, b) && i.ShouldCreatePair(r, o)) { const t = this.m_pairBuffer.data[this.m_pairBuffer.Append()]; t.indexA = r; t.indexB = o; t.flags = c.flags; t.strength = b2Min(a ? a.m_strength : 1, b ? b.m_strength : 1); t.distance = b2Vec2.DistanceVV(n[r], n[o]); } std_stable_sort(this.m_pairBuffer.data, 0, this.m_pairBuffer.count, b2ParticleSystem.ComparePairIndices); this.m_pairBuffer.Unique(b2ParticleSystem.MatchPairIndices); } } if (o & b2ParticleSystem.k_triadFlags) { const o = new b2VoronoiDiagram(s - t); for (let e = t; e < s; e++) { const t = this.m_flagsBuffer.data[e]; const s = this.m_groupBuffer[e]; if (!(t & b2ParticleFlag.b2_zombieParticle) && b2ParticleSystem.ParticleCanBeConnected(t, s)) { o.AddGenerator(n[e], e, i.IsNecessary(e)); } } const h = this.GetParticleStride(); o.Generate(h / 2, h * 2); const l = this; const callback = (t, s, o) => { const h = l.m_flagsBuffer.data[t]; const a = l.m_flagsBuffer.data[s]; const b = l.m_flagsBuffer.data[o]; if ((h | a | b) & b2ParticleSystem.k_triadFlags && i.ShouldCreateTriad(t, s, o)) { const i = n[t]; const f = n[s]; const P = n[o]; const u = b2Vec2.SubVV(i, f, e); const m = b2Vec2.SubVV(f, P, c); const S = b2Vec2.SubVV(P, i, r); const y = b2_maxTriadDistanceSquared * l.m_squaredDiameter; if (b2Vec2.DotVV(u, u) > y || b2Vec2.DotVV(m, m) > y || b2Vec2.DotVV(S, S) > y) { return; } const d = l.m_groupBuffer[t]; const p = l.m_groupBuffer[s]; const V = l.m_groupBuffer[o]; const w = l.m_triadBuffer.data[l.m_triadBuffer.Append()]; w.indexA = t; w.indexB = s; w.indexC = o; w.flags = h | a | b; w.strength = b2Min(b2Min(d ? d.m_strength : 1, p ? p.m_strength : 1), V ? V.m_strength : 1); const _ = (i.x + f.x + P.x) / 3; const C = (i.y + f.y + P.y) / 3; w.pa.x = i.x - _; w.pa.y = i.y - C; w.pb.x = f.x - _; w.pb.y = f.y - C; w.pc.x = P.x - _; w.pc.y = P.y - C; w.ka = -b2Vec2.DotVV(S, u); w.kb = -b2Vec2.DotVV(u, m); w.kc = -b2Vec2.DotVV(m, S); w.s = b2Vec2.CrossVV(i, f) + b2Vec2.CrossVV(f, P) + b2Vec2.CrossVV(P, i); } }; o.GetNodes(callback); std_stable_sort(this.m_triadBuffer.data, 0, this.m_triadBuffer.count, b2ParticleSystem.CompareTriadIndices); this.m_triadBuffer.Unique(b2ParticleSystem.MatchTriadIndices); } } UpdatePairsAndTriadsWithReactiveParticles() { const t = new b2ParticleSystem_ReactiveFilter(this.m_flagsBuffer); this.UpdatePairsAndTriads(0, this.m_count, t); for (let t = 0; t < this.m_count; t++) { this.m_flagsBuffer.data[t] &= ~b2ParticleFlag.b2_reactiveParticle; } this.m_allParticleFlags &= ~b2ParticleFlag.b2_reactiveParticle; } static ComparePairIndices(t, s) { const i = t.indexA - s.indexA; if (i !== 0) { return i < 0; } return t.indexB < s.indexB; } static MatchPairIndices(t, s) { return t.indexA === s.indexA && t.indexB === s.indexB; } static CompareTriadIndices(t, s) { const i = t.indexA - s.indexA; if (i !== 0) { return i < 0; } const e = t.indexB - s.indexB; if (e !== 0) { return e < 0; } return t.indexC < s.indexC; } static MatchTriadIndices(t, s) { return t.indexA === s.indexA && t.indexB === s.indexB && t.indexC === s.indexC; } static InitializeParticleLists(t, s) { const i = t.GetBufferIndex(); const e = t.GetP