Skip to content

Commit 1d3d09c

Browse files
authored
Merge pull request #156 from chnicoloso/frictionGravity
Fix: Friction disappears when manually applying gravity schteppe#224
2 parents 5b51c3a + 4f7cb4d commit 1d3d09c

File tree

5 files changed

+123
-1
lines changed

5 files changed

+123
-1
lines changed

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ These minor changes and improvements were also made:
1616
- Add support for [Trigger bodies](https://pmndrs.github.io/cannon-es/examples/trigger). [#83](https://github.com/pmndrs/cannon-es/pull/83)
1717
- Deprecated properties and methods have been removed.
1818
- 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).
19+
- 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).
1920

2021
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.
2122

src/world/Narrowphase.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Sphere } from '../shapes/Sphere'
66
import { Body } from '../objects/Body'
77
import { World } from '../world/World'
88
import { ContactEquation } from '../equations/ContactEquation'
9+
import { FrictionEquation } from '../equations/FrictionEquation'
910

1011
describe('Narrowphase', () => {
1112
test('sphere + sphere contact', () => {
@@ -63,6 +64,92 @@ describe('Narrowphase', () => {
6364

6465
expect(result.length).toBe(1)
6566
})
67+
68+
test('should default to using global gravity to create friction equations', () => {
69+
const gravity = new Vec3(0, -9.81, 0)
70+
const world = new World({ gravity })
71+
// No frictionGravity override.
72+
expect(world.frictionGravity).toBeUndefined()
73+
74+
const narrowPhase = new Narrowphase(world)
75+
const contacts: ContactEquation[] = []
76+
const sphereShape = new Sphere(1)
77+
78+
const bodyA = new Body({ mass: 1 })
79+
const bodyB = new Body({ mass: 1 })
80+
bodyA.addShape(sphereShape)
81+
bodyB.addShape(sphereShape)
82+
83+
narrowPhase.result = contacts
84+
narrowPhase.sphereSphere(
85+
sphereShape,
86+
sphereShape,
87+
new Vec3(0.5, 0, 0),
88+
new Vec3(-0.5, 0, 0),
89+
new Quaternion(),
90+
new Quaternion(),
91+
bodyA,
92+
bodyB
93+
)
94+
95+
expect(contacts.length).toBe(1)
96+
const [contact] = contacts
97+
const result: FrictionEquation[] = []
98+
99+
// No frictionGravity, should use global gravity.
100+
narrowPhase.createFrictionEquationsFromContact(contact, result)
101+
expect(result.length).toBe(2)
102+
result.forEach((result) => {
103+
expect(result.maxForce > 0).toBe(true)
104+
})
105+
})
106+
107+
test('if provided, should use frictionGravity to create friction equations', () => {
108+
const gravity = new Vec3(0, 0, 0)
109+
const frictionGravity = new Vec3(0, -9.81, 0)
110+
const world = new World({ gravity, frictionGravity })
111+
112+
const narrowPhase = new Narrowphase(world)
113+
const contacts: ContactEquation[] = []
114+
const sphereShape = new Sphere(1)
115+
116+
const bodyA = new Body({ mass: 1 })
117+
const bodyB = new Body({ mass: 1 })
118+
bodyA.addShape(sphereShape)
119+
bodyB.addShape(sphereShape)
120+
121+
narrowPhase.result = contacts
122+
narrowPhase.sphereSphere(
123+
sphereShape,
124+
sphereShape,
125+
new Vec3(0.5, 0, 0),
126+
new Vec3(-0.5, 0, 0),
127+
new Quaternion(),
128+
new Quaternion(),
129+
bodyA,
130+
bodyB
131+
)
132+
133+
expect(contacts.length).toBe(1)
134+
const [contact] = contacts
135+
const result: FrictionEquation[] = []
136+
137+
// No gravity, frictionGravity defined, friction.
138+
narrowPhase.createFrictionEquationsFromContact(contact, result)
139+
expect(result.length).toBe(2)
140+
result.forEach((result) => {
141+
expect(result.maxForce > 0).toBe(true)
142+
})
143+
144+
// No gravity, no frictionGravity, no friction.
145+
result.length = 0
146+
world.frictionGravity = new Vec3(0, 0, 0)
147+
narrowPhase.createFrictionEquationsFromContact(contact, result)
148+
expect(result.length).toBe(2)
149+
result.forEach((result) => {
150+
expect(result.maxForce).toBe(0)
151+
})
152+
})
66153
})
67154

68155
function createHeightfield() {

src/world/Narrowphase.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ export class Narrowphase {
224224

225225
if (friction > 0) {
226226
// Create 2 tangent equations
227-
const mug = friction * world.gravity.length()
227+
// Users may provide a force different from global gravity to use when computing contact friction.
228+
const mug = friction * (world.frictionGravity || world.gravity).length()
228229
let reducedMass = bodyA.invMass + bodyB.invMass
229230
if (reducedMass > 0) {
230231
reducedMass = 1 / reducedMass

src/world/World.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,21 @@ describe('World', () => {
259259
test('using ObjectCollisionMatrix', () => {
260260
testCollisionMatrix(ObjectCollisionMatrix)
261261
})
262+
263+
test('frictionGravity: should be undefined by default', () => {
264+
const gravity = new Vec3(0, -9.81, 0)
265+
const world = new World({ gravity })
266+
267+
expect(world.gravity).toEqual(gravity)
268+
expect(world.frictionGravity).toBeUndefined()
269+
})
270+
271+
test('frictionGravity: should be configurable', () => {
272+
const gravity = new Vec3(0, 0, 0)
273+
const frictionGravity = new Vec3(0, -9.81, 0)
274+
const world = new World({ gravity, frictionGravity })
275+
276+
expect(world.gravity).toEqual(gravity)
277+
expect(world.frictionGravity).toEqual(frictionGravity)
278+
})
262279
})

src/world/World.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ export class World extends EventTarget {
7979
*/
8080
gravity: Vec3
8181

82+
/**
83+
* Gravity to use when approximating the friction max force (mu*mass*gravity).
84+
* If undefined, global gravity will be used.
85+
* Use to enable friction in a World with a null gravity vector (no gravity).
86+
*/
87+
frictionGravity?: Vec3
88+
8289
/**
8390
* The broadphase algorithm to use.
8491
* @default NaiveBroadphase
@@ -170,6 +177,11 @@ export class World extends EventTarget {
170177
* The gravity of the world.
171178
*/
172179
gravity?: Vec3
180+
/**
181+
* Gravity to use when approximating the friction max force (mu*mass*gravity).
182+
* If undefined, global gravity will be used.
183+
*/
184+
frictionGravity?: Vec3
173185
/**
174186
* Makes bodies go to sleep when they've been inactive.
175187
* @default false
@@ -215,6 +227,10 @@ export class World extends EventTarget {
215227
if (options.gravity) {
216228
this.gravity.copy(options.gravity)
217229
}
230+
if (options.frictionGravity) {
231+
this.frictionGravity = new Vec3()
232+
this.frictionGravity.copy(options.frictionGravity)
233+
}
218234

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

0 commit comments

Comments
 (0)