From cb419cce911edbf63ca782ef83969f60ce11d832 Mon Sep 17 00:00:00 2001 From: Christian Nicoloso Date: Sun, 31 Jul 2022 18:55:24 -0700 Subject: [PATCH 1/6] add frictionGravity --- src/world/Narrowphase.test.ts | 48 +++++++++++++++++++++++++++++++++++ src/world/Narrowphase.ts | 2 +- src/world/World.test.ts | 17 +++++++++++++ src/world/World.ts | 16 ++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/world/Narrowphase.test.ts b/src/world/Narrowphase.test.ts index 99c1125d9..7d95725ac 100644 --- a/src/world/Narrowphase.test.ts +++ b/src/world/Narrowphase.test.ts @@ -6,6 +6,7 @@ import { Sphere } from '../shapes/Sphere' import { Body } from '../objects/Body' import { World } from '../world/World' import { ContactEquation } from '../equations/ContactEquation' +import { FrictionEquation } from '../equations/FrictionEquation' describe('Narrowphase', () => { test('sphere + sphere contact', () => { @@ -63,6 +64,53 @@ describe('Narrowphase', () => { expect(result.length).toBe(1) }) + + test('should use frictionGravity to create friction equations', () => { + const gravity = new Vec3(0, 0, 0) + const frictionGravity = new Vec3(0, -9.81, 0) + const world = new World({ gravity, frictionGravity }) + + const narrowPhase = new Narrowphase(world) + const contacts: ContactEquation[] = [] + const sphereShape = new Sphere(1) + + const bodyA = new Body({ mass: 1 }) + const bodyB = new Body({ mass: 1 }) + bodyA.addShape(sphereShape) + bodyB.addShape(sphereShape) + + narrowPhase.result = contacts + narrowPhase.sphereSphere( + sphereShape, + sphereShape, + new Vec3(0.5, 0, 0), + new Vec3(-0.5, 0, 0), + new Quaternion(), + new Quaternion(), + bodyA, + bodyB + ) + + expect(contacts.length).toBe(1) + const [contact] = contacts + const result: FrictionEquation[] = [] + + // No gravity, frictionGravity defined, friction. + narrowPhase.createFrictionEquationsFromContact(contact, result) + expect(result.length).toBe(2) + result.forEach((result) => { + expect(result.maxForce > 0).toBe(true) + }) + + // No gravity, no frictionGravity, no friction. + result.length = 0 + world.frictionGravity = new Vec3(0, 0, 0) + narrowPhase.createFrictionEquationsFromContact(contact, result) + expect(result.length).toBe(2) + result.forEach((result) => { + expect(result.maxForce).toBe(0) + }) + }) }) function createHeightfield() { diff --git a/src/world/Narrowphase.ts b/src/world/Narrowphase.ts index ca6c2f2ae..7f08448cb 100644 --- a/src/world/Narrowphase.ts +++ b/src/world/Narrowphase.ts @@ -224,7 +224,7 @@ export class Narrowphase { if (friction > 0) { // Create 2 tangent equations - const mug = friction * world.gravity.length() + const mug = friction * world.frictionGravity.length() let reducedMass = bodyA.invMass + bodyB.invMass if (reducedMass > 0) { reducedMass = 1 / reducedMass diff --git a/src/world/World.test.ts b/src/world/World.test.ts index cc5229c5f..b097136fa 100644 --- a/src/world/World.test.ts +++ b/src/world/World.test.ts @@ -259,4 +259,21 @@ describe('World', () => { test('using ObjectCollisionMatrix', () => { testCollisionMatrix(ObjectCollisionMatrix) }) + + test('frictionGravity: should default to world gravity', () => { + const gravity = new Vec3(0, -9.81, 0) + const world = new World({ gravity }) + + expect(world.gravity).toEqual(gravity) + expect(world.frictionGravity).toEqual(gravity) + }) + + test('frictionGravity: should allow overrides', () => { + const gravity = new Vec3(0, 0, 0) + const frictionGravity = new Vec3(0, -9.81, 0) + const world = new World({ gravity, frictionGravity }) + + expect(world.gravity).toEqual(gravity) + expect(world.frictionGravity).toEqual(frictionGravity) + }) }) diff --git a/src/world/World.ts b/src/world/World.ts index 9acd4e22e..cd497736d 100644 --- a/src/world/World.ts +++ b/src/world/World.ts @@ -79,6 +79,12 @@ export class World extends EventTarget { */ gravity: Vec3 + /** + * Gravity to use when approximating the friction max force (mu*mass*gravity). + * Use to enable friction in a World with a null gravity vector (no gravity). + */ + frictionGravity: Vec3 + /** * The broadphase algorithm to use. * @default NaiveBroadphase @@ -170,6 +176,10 @@ export class World extends EventTarget { * The gravity of the world. */ gravity?: Vec3 + /** + * Gravity to use when approximating the friction max force (mu*mass*gravity). + */ + frictionGravity?: Vec3 /** * Makes bodies go to sleep when they've been inactive. * @default false @@ -211,10 +221,16 @@ export class World extends EventTarget { this.default_dt = 1 / 60 this.nextId = 0 this.gravity = new Vec3() + this.frictionGravity = new Vec3() if (options.gravity) { this.gravity.copy(options.gravity) } + if (options.frictionGravity) { + this.frictionGravity.copy(options.frictionGravity) + } else if (options.gravity) { + this.frictionGravity.copy(options.gravity) + } this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase() this.bodies = [] From b55b33caac17c5300c33d58ebd1f2f2eb1b42ae2 Mon Sep 17 00:00:00 2001 From: Christian Nicoloso Date: Sun, 31 Jul 2022 19:17:36 -0700 Subject: [PATCH 2/6] update README --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index ea55c6cef..9cfe7e617 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,7 @@ These minor changes and improvements were also made: - Add support for [Trigger bodies](https://pmndrs.github.io/cannon-es/examples/trigger). [#83](https://github.com/pmndrs/cannon-es/pull/83) - Deprecated properties and methods have been removed. - The [original cannon.js debugger](https://github.com/schteppe/cannon.js/blob/master/tools/threejs/CannonDebugRenderer.js), which shows the wireframes of each body, has been moved to its own repo [cannon-es-debugger](https://github.com/pmndrs/cannon-es-debugger). +- Added a property `World.frictionGravity`, which will be used when computing the friction on two colliding bodies. The value defaults to `World.gravity` and allows for enabling friction in zero gravity. This addresses issue [#224](https://github.com/schteppe/cannon.js/issues/224) and follows the [pattern established for p2.js](https://github.com/schteppe/p2.js/blob/master/src/world/World.js#L88-L92). If instead you're using three.js in a **React** environment with [react-three-fiber](https://github.com/pmndrs/react-three-fiber), check out [use-cannon](https://github.com/pmndrs/use-cannon)! It's a wrapper around cannon-es. From c7f6d0f7b9bd1b2df672f7a6bfe797d8d3d16dd8 Mon Sep 17 00:00:00 2001 From: Christian Nicoloso Date: Tue, 2 Aug 2022 20:33:50 -0700 Subject: [PATCH 3/6] update dist --- dist/cannon-es.cjs.js | 16 +++++++++++++++- dist/cannon-es.d.ts | 2 ++ dist/cannon-es.js | 16 +++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/dist/cannon-es.cjs.js b/dist/cannon-es.cjs.js index 90cedfabe..c03b0c3a2 100644 --- a/dist/cannon-es.cjs.js +++ b/dist/cannon-es.cjs.js @@ -1801,6 +1801,8 @@ class Quaternion { target.x = bank; } /** + * Set the quaternion components given Euler angle representation. + * * @param order The order to apply angles: 'XYZ' or 'YXZ' or any other combination. * * See {@link https://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors MathWorks} reference @@ -10457,7 +10459,7 @@ class Narrowphase { if (friction > 0) { // Create 2 tangent equations - const mug = friction * world.gravity.length(); + const mug = friction * world.frictionGravity.length(); let reducedMass = bodyA.invMass + bodyB.invMass; if (reducedMass > 0) { @@ -12115,6 +12117,11 @@ class World extends EventTarget { * The gravity of the world. */ + /** + * Gravity to use when approximating the friction max force (mu*mass*gravity). + * Use to enable friction in a World with a null gravity vector (no gravity). + */ + /** * The broadphase algorithm to use. * @default NaiveBroadphase @@ -12186,11 +12193,18 @@ class World extends EventTarget { this.default_dt = 1 / 60; this.nextId = 0; this.gravity = new Vec3(); + this.frictionGravity = new Vec3(); if (options.gravity) { this.gravity.copy(options.gravity); } + if (options.frictionGravity) { + this.frictionGravity.copy(options.frictionGravity); + } else if (options.gravity) { + this.frictionGravity.copy(options.gravity); + } + this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase(); this.bodies = []; this.hasActiveBodies = false; diff --git a/dist/cannon-es.d.ts b/dist/cannon-es.d.ts index 70687b9d8..599a5dbf4 100644 --- a/dist/cannon-es.d.ts +++ b/dist/cannon-es.d.ts @@ -799,6 +799,7 @@ declare module "world/World" { default_dt: number; nextId: number; gravity: Vec3; + frictionGravity: Vec3; broadphase: Broadphase; bodies: Body[]; hasActiveBodies: boolean; @@ -837,6 +838,7 @@ declare module "world/World" { lastCallTime?: number; constructor(options?: { gravity?: Vec3; + frictionGravity?: Vec3; allowSleep?: boolean; broadphase?: Broadphase; solver?: Solver; diff --git a/dist/cannon-es.js b/dist/cannon-es.js index 7e050371d..48afb20e8 100644 --- a/dist/cannon-es.js +++ b/dist/cannon-es.js @@ -1797,6 +1797,8 @@ class Quaternion { target.x = bank; } /** + * Set the quaternion components given Euler angle representation. + * * @param order The order to apply angles: 'XYZ' or 'YXZ' or any other combination. * * See {@link https://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors MathWorks} reference @@ -10453,7 +10455,7 @@ class Narrowphase { if (friction > 0) { // Create 2 tangent equations - const mug = friction * world.gravity.length(); + const mug = friction * world.frictionGravity.length(); let reducedMass = bodyA.invMass + bodyB.invMass; if (reducedMass > 0) { @@ -12111,6 +12113,11 @@ class World extends EventTarget { * The gravity of the world. */ + /** + * Gravity to use when approximating the friction max force (mu*mass*gravity). + * Use to enable friction in a World with a null gravity vector (no gravity). + */ + /** * The broadphase algorithm to use. * @default NaiveBroadphase @@ -12182,11 +12189,18 @@ class World extends EventTarget { this.default_dt = 1 / 60; this.nextId = 0; this.gravity = new Vec3(); + this.frictionGravity = new Vec3(); if (options.gravity) { this.gravity.copy(options.gravity); } + if (options.frictionGravity) { + this.frictionGravity.copy(options.frictionGravity); + } else if (options.gravity) { + this.frictionGravity.copy(options.gravity); + } + this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase(); this.bodies = []; this.hasActiveBodies = false; From b405f6d3d2b0652d9911a8ad3aed6a28e2ee5d1c Mon Sep 17 00:00:00 2001 From: Christian Nicoloso Date: Sat, 6 Aug 2022 13:37:22 -0700 Subject: [PATCH 4/6] keep global gravity as default friction gravity --- src/world/Narrowphase.test.ts | 41 ++++++++++++++++++++++++++++++++++- src/world/Narrowphase.ts | 3 ++- src/world/World.test.ts | 6 ++--- src/world/World.ts | 8 +++---- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/world/Narrowphase.test.ts b/src/world/Narrowphase.test.ts index 7d95725ac..3b937c336 100644 --- a/src/world/Narrowphase.test.ts +++ b/src/world/Narrowphase.test.ts @@ -65,7 +65,46 @@ describe('Narrowphase', () => { expect(result.length).toBe(1) }) - test('should use frictionGravity to create friction equations', () => { + test('should default to using global gravity to create friction equations', () => { + const gravity = new Vec3(0, -9.81, 0) + const world = new World({ gravity }) + // No frictionGravity override. + expect(world.frictionGravity).toBeUndefined() + + const narrowPhase = new Narrowphase(world) + const contacts: ContactEquation[] = [] + const sphereShape = new Sphere(1) + + const bodyA = new Body({ mass: 1 }) + const bodyB = new Body({ mass: 1 }) + bodyA.addShape(sphereShape) + bodyB.addShape(sphereShape) + + narrowPhase.result = contacts + narrowPhase.sphereSphere( + sphereShape, + sphereShape, + new Vec3(0.5, 0, 0), + new Vec3(-0.5, 0, 0), + new Quaternion(), + new Quaternion(), + bodyA, + bodyB + ) + + expect(contacts.length).toBe(1) + const [contact] = contacts + const result: FrictionEquation[] = [] + + // No frictionGravity, should use global gravity. + narrowPhase.createFrictionEquationsFromContact(contact, result) + expect(result.length).toBe(2) + result.forEach((result) => { + expect(result.maxForce > 0).toBe(true) + }) + }) + + test('if provided, should use frictionGravity to create friction equations', () => { const gravity = new Vec3(0, 0, 0) const frictionGravity = new Vec3(0, -9.81, 0) const world = new World({ gravity, frictionGravity }) diff --git a/src/world/Narrowphase.ts b/src/world/Narrowphase.ts index 7f08448cb..9c7c978ea 100644 --- a/src/world/Narrowphase.ts +++ b/src/world/Narrowphase.ts @@ -224,7 +224,8 @@ export class Narrowphase { if (friction > 0) { // Create 2 tangent equations - const mug = friction * world.frictionGravity.length() + // Users may provide a force different from global gravity to use when computing contact friction. + const mug = friction * (world.frictionGravity || world.gravity).length() let reducedMass = bodyA.invMass + bodyB.invMass if (reducedMass > 0) { reducedMass = 1 / reducedMass diff --git a/src/world/World.test.ts b/src/world/World.test.ts index b097136fa..f657f410a 100644 --- a/src/world/World.test.ts +++ b/src/world/World.test.ts @@ -260,15 +260,15 @@ describe('World', () => { testCollisionMatrix(ObjectCollisionMatrix) }) - test('frictionGravity: should default to world gravity', () => { + test('frictionGravity: should be undefined by default', () => { const gravity = new Vec3(0, -9.81, 0) const world = new World({ gravity }) expect(world.gravity).toEqual(gravity) - expect(world.frictionGravity).toEqual(gravity) + expect(world.frictionGravity).toBeUndefined() }) - test('frictionGravity: should allow overrides', () => { + test('frictionGravity: should be configurable', () => { const gravity = new Vec3(0, 0, 0) const frictionGravity = new Vec3(0, -9.81, 0) const world = new World({ gravity, frictionGravity }) diff --git a/src/world/World.ts b/src/world/World.ts index cd497736d..9d917cb80 100644 --- a/src/world/World.ts +++ b/src/world/World.ts @@ -81,9 +81,10 @@ export class World extends EventTarget { /** * Gravity to use when approximating the friction max force (mu*mass*gravity). + * If undefined, global gravity will be used. * Use to enable friction in a World with a null gravity vector (no gravity). */ - frictionGravity: Vec3 + frictionGravity?: Vec3 /** * The broadphase algorithm to use. @@ -178,6 +179,7 @@ export class World extends EventTarget { gravity?: Vec3 /** * Gravity to use when approximating the friction max force (mu*mass*gravity). + * If undefined, global gravity will be used. */ frictionGravity?: Vec3 /** @@ -221,15 +223,13 @@ export class World extends EventTarget { this.default_dt = 1 / 60 this.nextId = 0 this.gravity = new Vec3() - this.frictionGravity = new Vec3() if (options.gravity) { this.gravity.copy(options.gravity) } if (options.frictionGravity) { + this.frictionGravity = new Vec3() this.frictionGravity.copy(options.frictionGravity) - } else if (options.gravity) { - this.frictionGravity.copy(options.gravity) } this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase() From e43146ebf47375cea8b7ee72ab14bc8aabea9123 Mon Sep 17 00:00:00 2001 From: Christian Nicoloso Date: Sat, 6 Aug 2022 13:44:25 -0700 Subject: [PATCH 5/6] revert dist to origin --- dist/cannon-es.cjs.js | 16 +--------------- dist/cannon-es.d.ts | 2 -- dist/cannon-es.js | 16 +--------------- 3 files changed, 2 insertions(+), 32 deletions(-) diff --git a/dist/cannon-es.cjs.js b/dist/cannon-es.cjs.js index c03b0c3a2..90cedfabe 100644 --- a/dist/cannon-es.cjs.js +++ b/dist/cannon-es.cjs.js @@ -1801,8 +1801,6 @@ class Quaternion { target.x = bank; } /** - * Set the quaternion components given Euler angle representation. - * * @param order The order to apply angles: 'XYZ' or 'YXZ' or any other combination. * * See {@link https://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors MathWorks} reference @@ -10459,7 +10457,7 @@ class Narrowphase { if (friction > 0) { // Create 2 tangent equations - const mug = friction * world.frictionGravity.length(); + const mug = friction * world.gravity.length(); let reducedMass = bodyA.invMass + bodyB.invMass; if (reducedMass > 0) { @@ -12117,11 +12115,6 @@ class World extends EventTarget { * The gravity of the world. */ - /** - * Gravity to use when approximating the friction max force (mu*mass*gravity). - * Use to enable friction in a World with a null gravity vector (no gravity). - */ - /** * The broadphase algorithm to use. * @default NaiveBroadphase @@ -12193,18 +12186,11 @@ class World extends EventTarget { this.default_dt = 1 / 60; this.nextId = 0; this.gravity = new Vec3(); - this.frictionGravity = new Vec3(); if (options.gravity) { this.gravity.copy(options.gravity); } - if (options.frictionGravity) { - this.frictionGravity.copy(options.frictionGravity); - } else if (options.gravity) { - this.frictionGravity.copy(options.gravity); - } - this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase(); this.bodies = []; this.hasActiveBodies = false; diff --git a/dist/cannon-es.d.ts b/dist/cannon-es.d.ts index 599a5dbf4..70687b9d8 100644 --- a/dist/cannon-es.d.ts +++ b/dist/cannon-es.d.ts @@ -799,7 +799,6 @@ declare module "world/World" { default_dt: number; nextId: number; gravity: Vec3; - frictionGravity: Vec3; broadphase: Broadphase; bodies: Body[]; hasActiveBodies: boolean; @@ -838,7 +837,6 @@ declare module "world/World" { lastCallTime?: number; constructor(options?: { gravity?: Vec3; - frictionGravity?: Vec3; allowSleep?: boolean; broadphase?: Broadphase; solver?: Solver; diff --git a/dist/cannon-es.js b/dist/cannon-es.js index 48afb20e8..7e050371d 100644 --- a/dist/cannon-es.js +++ b/dist/cannon-es.js @@ -1797,8 +1797,6 @@ class Quaternion { target.x = bank; } /** - * Set the quaternion components given Euler angle representation. - * * @param order The order to apply angles: 'XYZ' or 'YXZ' or any other combination. * * See {@link https://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors MathWorks} reference @@ -10455,7 +10453,7 @@ class Narrowphase { if (friction > 0) { // Create 2 tangent equations - const mug = friction * world.frictionGravity.length(); + const mug = friction * world.gravity.length(); let reducedMass = bodyA.invMass + bodyB.invMass; if (reducedMass > 0) { @@ -12113,11 +12111,6 @@ class World extends EventTarget { * The gravity of the world. */ - /** - * Gravity to use when approximating the friction max force (mu*mass*gravity). - * Use to enable friction in a World with a null gravity vector (no gravity). - */ - /** * The broadphase algorithm to use. * @default NaiveBroadphase @@ -12189,18 +12182,11 @@ class World extends EventTarget { this.default_dt = 1 / 60; this.nextId = 0; this.gravity = new Vec3(); - this.frictionGravity = new Vec3(); if (options.gravity) { this.gravity.copy(options.gravity); } - if (options.frictionGravity) { - this.frictionGravity.copy(options.frictionGravity); - } else if (options.gravity) { - this.frictionGravity.copy(options.gravity); - } - this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase(); this.bodies = []; this.hasActiveBodies = false; From 4f7cb4de054bcd74c23987e5dc0683576e96f1b7 Mon Sep 17 00:00:00 2001 From: Christian Nicoloso Date: Tue, 9 Aug 2022 18:57:45 -0700 Subject: [PATCH 6/6] update readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 9cfe7e617..658c2d610 100644 --- a/readme.md +++ b/readme.md @@ -16,7 +16,7 @@ These minor changes and improvements were also made: - Add support for [Trigger bodies](https://pmndrs.github.io/cannon-es/examples/trigger). [#83](https://github.com/pmndrs/cannon-es/pull/83) - Deprecated properties and methods have been removed. - The [original cannon.js debugger](https://github.com/schteppe/cannon.js/blob/master/tools/threejs/CannonDebugRenderer.js), which shows the wireframes of each body, has been moved to its own repo [cannon-es-debugger](https://github.com/pmndrs/cannon-es-debugger). -- Added a property `World.frictionGravity`, which will be used when computing the friction on two colliding bodies. The value defaults to `World.gravity` and allows for enabling friction in zero gravity. This addresses issue [#224](https://github.com/schteppe/cannon.js/issues/224) and follows the [pattern established for p2.js](https://github.com/schteppe/p2.js/blob/master/src/world/World.js#L88-L92). +- Added optional property `World.frictionGravity: Vec3` which can be set to customize the force used when computing the friction between two colliding bodies. If `undefined`, `World.gravity` will be used. This property is useful to enable friction in zero gravity. This addresses issue [#224](https://github.com/schteppe/cannon.js/issues/224) and follows the [pattern established for p2.js](https://github.com/schteppe/p2.js/blob/master/src/world/World.js#L88-L92). If instead you're using three.js in a **React** environment with [react-three-fiber](https://github.com/pmndrs/react-three-fiber), check out [use-cannon](https://github.com/pmndrs/use-cannon)! It's a wrapper around cannon-es.