Box2d Wasm Versions Save

Box2D physics engine compiled to WebAssembly. Supports TypeScript and ES modules.

liquidfun-v7.0.0

2 years ago

v7.0.0

2 years ago

Closes https://github.com/Birch-san/box2d-wasm/issues/38
Closes https://github.com/Birch-san/box2d-wasm/issues/36
Closes https://github.com/Birch-san/box2d-wasm/issues/35

Exposed new Box2D functionality:

Added LeakMitigator for freeing retained references from Emscripten's JS object caches.
See documentation.

Built with Emscripten 3.0.0.

  • upgrades musl libc from v1.1.15 to v1.2.2; bundle size may change.

Breaking changes:

liquidfun-v6.0.4-lf.1

2 years ago

Iterates on liquidfun-v6.0.4, exposing Liquidfun-specific functionality.

====

Install via:

npm i "box2d-wasm@npm:[email protected]"

This will install liquidfun-wasm under a package alias, box2d-wasm:

"dependencies": {
    "box2d-wasm": "npm:[email protected]",

Using this package alias enables you to invoke liquidfun-wasm the same way box2d-wasm is invoked in the documentation (liquidfun-wasm is a distribution of box2d-wasm):

import Box2DFactory from 'box2d-wasm';

Box2DFactory().then((box2D) => {
  console.log(box2D);
});

====

Adds new methods to existing callback classes:

// these methods have also been added to JSContactListenerWithoutSolveCallbacks,
// but the performance reasons to prefer that class aren't relevant nowadays;
// WASM->JS calls are fast, so it's cheap to implement a no-op.
// https://hacks.mozilla.org/2018/10/calls-between-javascript-and-webassembly-are-finally-fast-%F0%9F%8E%89/
interface JSContactListener {
  /**
   * Called when a fixture and particle start touching if the
   * {@link b2_fixtureContactFilterParticle} flag is set on the particle.
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   * @param particleBodyContact_p pointer to {@link b2ParticleBodyContact}
   */
  BeginContactParticleSystemParticleBodyContact(particleSystem_p: number, particleBodyContact_p: number): void;

  /**
   * Called when a fixture and particle stop touching if the
   * {@link b2_fixtureContactFilterParticle} flag is set on either particle.
   * @param fixture_p pointer to {@link b2Fixture}
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   * @param index particle's index in its {@link b2ParticleSystem}
   */
  EndContactFixtureParticleSystemIndex(fixture_p: number, particleSystem_p: number, index: number): void;

  /**
   * Called when two particles start touching if
   * {@link b2_particleContactFilterParticle} flag is set on either particle.
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   * @param particleContact_p pointer to {@link b2ParticleContact}
   */
  BeginContactParticleSystemParticleContact(particleSystem_p: number, particleContact_p: number): void;

  /**
   * Called when two particles start touching if
   * {@link b2_particleContactFilterParticle} flag is set on either particle.
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   * @param indexA particle's index in its {@link b2ParticleSystem}
   * @param indexB particle's index in its {@link b2ParticleSystem}
   */
  EndContactParticleSystemIndexIndex(particleSystem_p: number, indexA: number, indexB: number): void;
}

interface JSContactFilter {
  /**
   * Return true if contact calculations should be performed between a
   * fixture and particle.  This is only called if the
   * {@link b2_fixtureContactListenerParticle} flag is set on the particle.
   * @param fixture_p pointer to {@link b2Fixture}
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   * @param particleIndex particle's index in its {@link b2ParticleSystem}
   */
  ShouldCollideFixtureParticleSystemIndex(fixture_p: number, particleSystem: number, particleIndex: number): boolean;

  /**
   * Return true if contact calculations should be performed between a
   * fixture and particle.  This is only called if the
   * {@link b2_fixtureContactListenerParticle} flag is set on the particle.
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   * @param particleIndexA particle's index in its {@link b2ParticleSystem}
   * @param particleIndexB particle's index in its {@link b2ParticleSystem}
   */
  ShouldCollideParticleSystemIndexIndex(particleSystem_p: number, particleIndexA: number, particleIndexB: number): boolean;
}

interface JSQueryCallback {
  /**
   * Called for each particle found in the query AABB.
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   * @param index particle's index in its {@link b2ParticleSystem}
   * @return false to terminate the query.
   */
  ReportParticle(particleSystem_p: number, index: number): boolean;

  /**
   * Cull an entire particle system from {@link b2World.QueryAABB}. Ignored for
   * {@link b2ParticleSystem.QueryAABB}.
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   * @return true if you want to include particleSystem in the AABB query,
   * or false to cull particleSystem from the AABB query.
   */
  ShouldQueryParticleSystem(particleSystem_p: number): boolean;
}

interface JSRayCastCallback {
  /**
   * Called for each particle found in the query. You control how the ray
   * cast proceeds by returning a float:
   * return <=0: ignore the remaining particles in this particle system
   * return fraction: ignore particles that are 'fraction' percent farther
   *   along the line from 'point1' to 'point2'. Note that 'point1' and
   *   'point2' are parameters to b2World::RayCast.
   * @param particleSystem_p pointer to the {@link b2ParticleSystem} containing the particle
   * @param index particle's index in its {@link b2ParticleSystem}
   * @param point_p pointer to {@link b2Vec2}; the point of intersection bt the ray and the particle
   * @param normal_p pointer to {@link b2Vec2}; the normal vector at the point of intersection
   * @param fraction percent (0.0~1.0) from 'point0' to 'point1' along the
   *   ray. Note that 'point1' and 'point2' are parameters to
   *   {@link b2World.RayCast}.
   * @return <=0 to ignore rest of particle system, fraction to ignore
   * particles that are farther away.
   */
  ReportParticle(particleSystem_p: number, index: number, point_p: number, normal_p: number, fraction: number): number;

  /**
   * Cull an entire particle system from {@link b2World.RayCast}. Ignored in
   * {@link b2ParticleSystem.RayCast}.
   * @return true if you want to include particleSystem in the RayCast, or
   * false to cull particleSystem from the RayCast.
   * @param particleSystem_p pointer to {@link b2ParticleSystem}
   */
  ShouldQueryParticleSystem(particleSystem_p: number): boolean;
}

Example invocation of JSQueryCallback#ReportParticle:

const {
  b2AABB,
  b2Vec2,
  b2ParticleSystem,
  destroy,
  JSQueryCallback,
  getPointer,
  wrapPointer
} = this.box2D;
const center: Box2D.Point = {
  x: -1,
  y: 1
}
const radius = 1
const { x, y } = center
const aabb = new b2AABB()
const bound = new b2Vec2(x - radius, y - radius)
aabb.set_lowerBound(bound)
bound.Set(x + radius, y + radius)
aabb.set_upperBound(bound)
destroy(bound)
const impulse = new b2Vec2(0.25, 0);
const queryCallback: Box2D.JSQueryCallback = Object.assign<
  Box2D.JSQueryCallback,
  Partial<Box2D.JSQueryCallback>
>(new JSQueryCallback(), {
  ReportFixture: (_fixture_p: number) => false,
  ReportParticle(particleSystem_p: number, index: number): boolean {
    const particleSystem: Box2D.b2ParticleSystem = wrapPointer(particleSystem_p, b2ParticleSystem)
    // you can use the index to lookup the particle via pointer arithmetic; each position is 8 bytes
    const position: Box2D.b2Vec2 = wrapPointer(getPointer(particleSystem.GetPositionBuffer()) + index * 8, b2Vec2)
    const velocity: Box2D.b2Vec2 = wrapPointer(getPointer(particleSystem.GetVelocityBuffer()) + index * 8, b2Vec2)
    // since indices can change on each world step, you can grab a handle to more permanently identify the particle
    const particleHandle: Box2D.b2ParticleHandle = particleSystem.GetParticleHandleFromIndex(index);
    particleSystem.ParticleApplyLinearImpulse(index, impulse);
    return false
  },
  ShouldQueryParticleSystem: (_particleSystem_p: number) => true
})

Exposes Voronoi diagrams. See b2_voronoi_diagram.h.

const {
  b2Vec2,
  b2VoronoiDiagram,
  b2StackAllocator,
  destroy,
  JSNodeCallback
} = this.box2D
const stackAllocator = new b2StackAllocator();
const voronoiDiagram = new b2VoronoiDiagram(
  stackAllocator,
  100
);
const generatorPos = new b2Vec2(0, 0);
enum Generator {
  Origin,
  TopRight,
  BottomRight
}
const generators: Record<Generator, [x: number, y: number]> = {
  [Generator.Origin]:
  [0, 0],
  [Generator.TopRight]:
  [1, 0],
  [Generator.BottomRight]:
  [1, 1]
};
// eslint-disable-next-line no-inner-declarations
function isNonNumeric(x: string | number): x is string {
  return isNaN(Number(x));
}
for (const generator of Object.values(Generator)) {
  if (isNonNumeric(generator)) {
    continue;
  }
  const [x, y] = generators[generator];
  generatorPos.Set(x, y);
  voronoiDiagram.AddGenerator(generatorPos, generator, true);
}
destroy(generatorPos);
voronoiDiagram.Generate(1, 0.1);
const nodes: Array<[
  a: keyof typeof Generator,
  b: keyof typeof Generator,
  c: keyof typeof Generator
]> = [];
const nodeCallback = Object.assign(new JSNodeCallback(), {
  op_call(a: number, b: number, c: number): void {
    // prints 'BottomRight', 'TopRight', 'Origin'
    console.log(
      Generator[a] as keyof typeof Generator,
      Generator[b] as keyof typeof Generator,
      Generator[c] as keyof typeof Generator
    );
  }
});
voronoiDiagram.GetNodes(nodeCallback);

liquidfun-v6.0.2

2 years ago

liquidfun.0 rebased onto v6.0.2.

v6.0.4

2 years ago

Added .d.ts declarations accompanying entry.js, Box2D.js and Box2D.simd.js (in case anybody wants to bypass the Node module specifier or the entrypoint and import an asset directly).

v6.0.3

2 years ago

Updated UMD entrypoint to be better statically-analysable by Parcel.

liquidfun-v6.0.4

2 years ago

liquidfun.0 rebased onto v6.0.4.

v6.0.2

2 years ago

Fixed substantial performance & size regression introduced in v5.0.0. We now correctly set optimization level.

Box2D.js is still 319kB. Box2D.simd.js is still 319kB. Box2D.wasm is 161kB (down from 226kB). Box2D.simd.wasm is 162kB (down from 226kB).

Performance is back to normal now, so it's now possible to try out SIMD properly.

liquidfun-v6.0.1

2 years ago

Do not use: introduces performance/size regression (due to rebasing onto v5.0.0). Prefer liquidfun-v6.0.2

liquidfun.0 rebased onto v6.0.1.

v6.0.1

2 years ago

Do not use: suffers from performance/size regression introduced in v5.0.0. Prefer v6.0.2

Added a mechanism to the 'browser global' loader in the UMD entrypoint that enables you to specify the directory from which you serve Box2D.js, via the data-box2d-dir attribute on its <script> tag:

<script data-box2d-dir="Box2D" src='Box2D/entry.js'></script>

This tells entry.js that Box2D.js can be found at Box2D/Box2D.js.