Skip to content

Commit

Permalink
📖 Add docs and usage of fixedStep
Browse files Browse the repository at this point in the history
  • Loading branch information
marcofugaro committed Jan 6, 2022
1 parent 7a1ff67 commit b56a4b3
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 68 deletions.
20 changes: 2 additions & 18 deletions examples/threejs.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@
// cannon.js variables
let world
let body
const timeStep = 1 / 60
let lastCallTime

initThree()
initCannon()
Expand Down Expand Up @@ -87,27 +85,13 @@
requestAnimationFrame(animate)

// Step the physics world
updatePhysics()
world.fixedStep()

// Copy coordinates from cannon.js to three.js
mesh.position.copy(body.position)
mesh.quaternion.copy(body.quaternion)

render()
}

function updatePhysics() {
const time = performance.now() / 1000
if (!lastCallTime) {
world.step(timeStep)
} else {
const dt = time - lastCallTime
world.step(timeStep, dt)
}
lastCallTime = time
}

function render() {
// Render three.js
renderer.render(scene, camera)
}
</script>
Expand Down
32 changes: 11 additions & 21 deletions examples/threejs_cloth.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@
* https://viscomp.alexandra.dk/?p=147
*/

// Specify the simulation constants
const timeStep = 1 / 60
let lastCallTime

const clothMass = 1 // 1 kg in total
const clothSize = 1 // 1 meter
const Nx = 12 // number of horizontal particles in the cloth
Expand Down Expand Up @@ -210,24 +206,20 @@
function animate() {
requestAnimationFrame(animate)
controls.update()
updatePhysics()
render()
stats.update()
}

// Step the physics world
function updatePhysics() {
const time = performance.now() / 1000
if (!lastCallTime) {
world.step(timeStep)
} else {
const dt = time - lastCallTime
world.step(timeStep, dt)
}
lastCallTime = time
// Step the physics world
world.fixedStep()

// Sync the three.js meshes with the bodies
updateMeshes()

// Render three.js
renderer.render(scene, camera)

stats.update()
}

function render() {
function updateMeshes() {
// Make the three.js cloth follow the cannon.js particles
for (let i = 0; i < Nx + 1; i++) {
for (let j = 0; j < Ny + 1; j++) {
Expand All @@ -246,8 +238,6 @@
// Make the three.js ball follow the cannon.js one
// Copying quaternion is not needed since it's a sphere
sphereMesh.position.copy(sphereBody.position)

renderer.render(scene, camera)
}
</script>
</body>
Expand Down
17 changes: 3 additions & 14 deletions examples/threejs_mousepick.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@

// cannon.js variables
let world
const timeStep = 1 / 60
let lastCallTime
let jointBody
let jointConstraint
let cubeBody
Expand Down Expand Up @@ -296,27 +294,18 @@
requestAnimationFrame(animate)

// Step the physics world
updatePhysics()
world.fixedStep()

// Sync the three.js meshes with the bodies
for (let i = 0; i !== meshes.length; i++) {
meshes[i].position.copy(bodies[i].position)
meshes[i].quaternion.copy(bodies[i].quaternion)
}

// Render three.js
renderer.render(scene, camera)
stats.update()
}

function updatePhysics() {
const time = performance.now() / 1000
if (!lastCallTime) {
world.step(timeStep)
} else {
const dt = time - lastCallTime
world.step(timeStep, dt)
}
lastCallTime = time
stats.update()
}
</script>
</body>
Expand Down
2 changes: 1 addition & 1 deletion examples/worker.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
const { positions, quaternions, timeStep } = event.data

// Step the world
world.step(timeStep)
world.fixedStep(timeStep)

// Copy the cannon.js data into the buffers
for (let i = 0; i < bodies.length; i++) {
Expand Down
2 changes: 1 addition & 1 deletion examples/worker_sharedarraybuffer.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@

function update() {
// Step the world
world.step(timeStep)
world.fixedStep(timeStep)

// Copy the cannon.js data into the buffers
for (let i = 0; i < bodies.length; i++) {
Expand Down
35 changes: 22 additions & 13 deletions getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,25 @@ const world = new CANNON.World({
})
```

To step the simulation forward, we have to call **`world.step()`** each frame.
As a first argument we pass the fixed timestep at which we want the simulation to run, `1 / 60` means 60fps.
As a second argument, we pass the elapsed time since the last `.step()` call. This is used to keep the simulation at the same speed independently of the framerate, since `requestAnimationFrame` calls may vary on different devices or there might be performance issues. [Read more about fixed simulation stepping here](https://gafferongames.com/post/fix_your_timestep/).
To step the simulation forward, we have to call **`world.fixedStep()`** each frame.
As a first argument, we can pass the fixed timestep at which we want the simulation to run, the default value is `1 / 60` meaning `60fps`.
**`world.fixedStep()`** keeps track of the last time it was called to keep the simulation at the same speed independently of the framerate, since `requestAnimationFrame` calls may vary on different devices or if there are performance issues. [Read more about fixed simulation stepping here](https://gafferongames.com/post/fix_your_timestep/).

```js
function animate() {
requestAnimationFrame(animate)

// Run the simulation independently of framerate every 1 / 60 ms
world.fixedStep()
}
// Start the simulation loop
animate()
```

If you wish to pass the time since last call by hand (`dt` in the game world) you can use the more advanced **`world.step()`**.

<details>
<summary>See advanced world stepping example</summary>

```js
const timeStep = 1 / 60 // seconds
Expand All @@ -36,6 +52,8 @@ function animate() {
animate()
```

</details>

Rigid Bodies are the entities which will be simulated in the world, they can be simple shapes such as [Sphere](classes/sphere), [Box](classes/box), [Plane](classes/plane), [Cylinder](classes/cylinder), or more complex shapes such as [ConvexPolyhedron](classes/convexpolyhedron), [Particle](classes/particle), [Heightfield](classes/heightfield), [Trimesh](classes/trimesh).

Let's create a basic sphere body.
Expand Down Expand Up @@ -93,19 +111,10 @@ groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0) // make it face up
world.addBody(groundBody)

// Start the simulation loop
const timeStep = 1 / 60 // seconds
let lastCallTime
function animate() {
requestAnimationFrame(animate)

const time = performance.now() / 1000 // seconds
if (!lastCallTime) {
world.step(timeStep)
} else {
const dt = time - lastCallTime
world.step(timeStep, dt)
}
lastCallTime = time
world.fixedStep()

// the sphere y position shows the sphere falling
console.log(`Sphere y position: ${sphereBody.position.y}`)
Expand Down

0 comments on commit b56a4b3

Please sign in to comment.