Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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.

Expand Down
87 changes: 87 additions & 0 deletions src/world/Narrowphase.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -63,6 +64,92 @@ describe('Narrowphase', () => {

expect(result.length).toBe(1)
})

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

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() {
Expand Down
3 changes: 2 additions & 1 deletion src/world/Narrowphase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ export class Narrowphase {

if (friction > 0) {
// Create 2 tangent equations
const mug = friction * world.gravity.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
Expand Down
17 changes: 17 additions & 0 deletions src/world/World.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,21 @@ describe('World', () => {
test('using ObjectCollisionMatrix', () => {
testCollisionMatrix(ObjectCollisionMatrix)
})

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).toBeUndefined()
})

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

expect(world.gravity).toEqual(gravity)
expect(world.frictionGravity).toEqual(frictionGravity)
})
})
16 changes: 16 additions & 0 deletions src/world/World.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ 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.
* Use to enable friction in a World with a null gravity vector (no gravity).
*/
frictionGravity?: Vec3

/**
* The broadphase algorithm to use.
* @default NaiveBroadphase
Expand Down Expand Up @@ -170,6 +177,11 @@ export class World extends EventTarget {
* The gravity of the world.
*/
gravity?: Vec3
/**
* Gravity to use when approximating the friction max force (mu*mass*gravity).
* If undefined, global gravity will be used.
*/
frictionGravity?: Vec3
/**
* Makes bodies go to sleep when they've been inactive.
* @default false
Expand Down Expand Up @@ -215,6 +227,10 @@ export class World extends EventTarget {
if (options.gravity) {
this.gravity.copy(options.gravity)
}
if (options.frictionGravity) {
this.frictionGravity = new Vec3()
this.frictionGravity.copy(options.frictionGravity)
}

this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase()
this.bodies = []
Expand Down