Skip to content

Releases: Birch-san/box2d-wasm

v7.0.0

29 Nov 00:33
c04514c
Compare
Choose a tag to compare

Closes #38
Closes #36
Closes #35

Exposed new Box2D functionality:

  • b2BodyDef#enabled attribute (resolves #38)
  • b2Body#ApplyLinearImpulseToCenter method (resolves #36)

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:

  • Deleted (unreachable) type JSContactListenerWithoutSolveCallbacks (resolves #35); the performance problem it was designed to solve (eliminating WASM->JS calls) is not a problem nowadays. Prefer JSContactListener.

liquidfun-v7.0.0

29 Nov 00:48
2ea8ed4
Compare
Choose a tag to compare

liquidfun-v6.0.4-lf.1

22 Jul 17:25
c3bef36
Compare
Choose a tag to compare

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

====

Install via:

npm i "box2d-wasm@npm:liquidfun-wasm@6.0.4-lf.1"

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

"dependencies": {
    "box2d-wasm": "npm:liquidfun-wasm@6.0.4-lf.1",

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);

v6.0.4

21 Jul 18:39
8901550
Compare
Choose a tag to compare

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

21 Jul 18:38
44082d1
Compare
Choose a tag to compare

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

liquidfun-v6.0.4

08 Jul 18:55
bc32702
Compare
Choose a tag to compare

v6.0.2

08 Jul 18:39
6d98d6b
Compare
Choose a tag to compare

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.

v6.0.1

08 Jul 16:06
8193fd1
Compare
Choose a tag to compare
v6.0.1 Pre-release
Pre-release

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.

liquidfun-v6.0.2

21 Jul 18:40
8b9be8f
Compare
Choose a tag to compare

liquidfun-v6.0.1

08 Jul 16:13
43f6cf9
Compare
Choose a tag to compare
liquidfun-v6.0.1 Pre-release
Pre-release

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.