Skip to content

Commit

Permalink
Merge pull request #40 from react-spring/performance
Browse files Browse the repository at this point in the history
Fix performance issues and introduce Performance example
  • Loading branch information
codynova authored Jul 6, 2020
2 parents 9703433 + 22b4448 commit b60b86c
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 21 deletions.
25 changes: 18 additions & 7 deletions dist/cannon-es.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -9127,7 +9127,7 @@ class Trimesh extends Shape {
const n = this.vertices.length / 3,
verts = this.vertices;
const minx,miny,minz,maxx,maxy,maxz;
const v = tempWorldVertex;
const v = tempWorldVertex;
for(let i=0; i<n; i++){
this.getVertex(i, v);
quat.vmult(v, v);
Expand All @@ -9137,12 +9137,12 @@ class Trimesh extends Shape {
} else if(v.x > maxx || maxx===undefined){
maxx = v.x;
}
if (v.y < miny || miny===undefined){
if (v.y < miny || miny===undefined){
miny = v.y;
} else if(v.y > maxy || maxy===undefined){
maxy = v.y;
}
if (v.z < minz || minz===undefined){
if (v.z < minz || minz===undefined){
minz = v.z;
} else if(v.z > maxz || maxz===undefined){
maxz = v.z;
Expand Down Expand Up @@ -11803,24 +11803,35 @@ class World extends EventTarget {
*/


step(dt, timeSinceLastCalled = 0, maxSubSteps = 10) {
if (timeSinceLastCalled === 0) {
step(dt, timeSinceLastCalled, maxSubSteps = 10) {
if (timeSinceLastCalled === undefined) {
// Fixed, simple stepping
this.internalStep(dt); // Increment time

this.time += dt;
} else {
this.accumulator += timeSinceLastCalled;
const t0 = performance.now();
let substeps = 0;

while (this.accumulator >= dt && substeps < maxSubSteps) {
// Do fixed steps to catch up
this.internalStep(dt);
this.accumulator -= dt;
substeps++;
}

const t = this.accumulator % dt / dt;
if (performance.now() - t0 > dt * 2 * 1000) {
// The framerate is not interactive anymore.
// We are at half of the target framerate.
// Better bail out.
break;
}
} // Remove the excess accumulator, since we may not
// have had enough substeps available to catch up


this.accumulator = this.accumulator % dt;
const t = this.accumulator / dt;

for (let j = 0; j !== this.bodies.length; j++) {
const b = this.bodies[j];
Expand Down
25 changes: 18 additions & 7 deletions dist/cannon-es.js
Original file line number Diff line number Diff line change
Expand Up @@ -9123,7 +9123,7 @@ class Trimesh extends Shape {
const n = this.vertices.length / 3,
verts = this.vertices;
const minx,miny,minz,maxx,maxy,maxz;
const v = tempWorldVertex;
const v = tempWorldVertex;
for(let i=0; i<n; i++){
this.getVertex(i, v);
quat.vmult(v, v);
Expand All @@ -9133,12 +9133,12 @@ class Trimesh extends Shape {
} else if(v.x > maxx || maxx===undefined){
maxx = v.x;
}
if (v.y < miny || miny===undefined){
if (v.y < miny || miny===undefined){
miny = v.y;
} else if(v.y > maxy || maxy===undefined){
maxy = v.y;
}
if (v.z < minz || minz===undefined){
if (v.z < minz || minz===undefined){
minz = v.z;
} else if(v.z > maxz || maxz===undefined){
maxz = v.z;
Expand Down Expand Up @@ -11799,24 +11799,35 @@ class World extends EventTarget {
*/


step(dt, timeSinceLastCalled = 0, maxSubSteps = 10) {
if (timeSinceLastCalled === 0) {
step(dt, timeSinceLastCalled, maxSubSteps = 10) {
if (timeSinceLastCalled === undefined) {
// Fixed, simple stepping
this.internalStep(dt); // Increment time

this.time += dt;
} else {
this.accumulator += timeSinceLastCalled;
const t0 = performance.now();
let substeps = 0;

while (this.accumulator >= dt && substeps < maxSubSteps) {
// Do fixed steps to catch up
this.internalStep(dt);
this.accumulator -= dt;
substeps++;
}

const t = this.accumulator % dt / dt;
if (performance.now() - t0 > dt * 2 * 1000) {
// The framerate is not interactive anymore.
// We are at half of the target framerate.
// Better bail out.
break;
}
} // Remove the excess accumulator, since we may not
// have had enough substeps available to catch up


this.accumulator = this.accumulator % dt;
const t = this.accumulator / dt;

for (let j = 0; j !== this.bodies.length; j++) {
const b = this.bodies[j];
Expand Down
5 changes: 5 additions & 0 deletions examples/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ body {
overflow: hidden;
font-family: Monospace;
}

/* more space for the Stats.js button name */
.dg .property-name {
width: 80% !important;
}
46 changes: 43 additions & 3 deletions examples/js/Demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ var Demo = function (options) {
maxSubSteps: 20,
})

var dummy = new THREE.Object3D()

// Extend settings with options
options = options || {}
for (var key in options) {
Expand Down Expand Up @@ -274,9 +276,21 @@ var Demo = function (options) {
bodyQuat = b.quaternion
}

visual.position.copy(bodyPos)
if (b.quaternion) {
visual.quaternion.copy(bodyQuat)
if (visual.isInstancedMesh) {
dummy.position.copy(bodyPos)
if (b.quaternion) {
dummy.quaternion.copy(bodyQuat)
}

dummy.updateMatrix()

visual.setMatrixAt(b.instanceIndex, dummy.matrix)
visual.instanceMatrix.needsUpdate = true
} else {
visual.position.copy(bodyPos)
if (b.quaternion) {
visual.quaternion.copy(bodyQuat)
}
}
}

Expand Down Expand Up @@ -980,6 +994,32 @@ Demo.prototype.addVisual = function (body) {
}
}

Demo.prototype.addVisualInstanced = function (bodies) {
if (!Array.isArray(bodies) || !bodies.every((body) => body instanceof CANNON.Body)) {
throw new Error('The argument passed to addVisualInstanced() is not an array of bodies')
}

// What geometry should be used?
const mesh = this.shape2mesh(bodies[0]).children[0]

const instancedMesh = new THREE.InstancedMesh(mesh.geometry.clone(), mesh.material.clone(), bodies.length)
instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage) // will be updated every frame
// Add bodies

instancedMesh.receiveShadow = true
instancedMesh.castShadow = true

bodies.forEach((body, i) => {
this.bodies.push(body)
this.visuals.push(instancedMesh)
body.instanceIndex = i
body.visualref = instancedMesh
body.visualref.visualId = this.bodies.length - 1
})

this.scene.add(instancedMesh)
}

Demo.prototype.addVisuals = function (bodies) {
for (var i = 0; i < bodies.length; i++) {
this.addVisual(bodies[i])
Expand Down
125 changes: 125 additions & 0 deletions examples/performance.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html>
<head>
<title>cannon.js - performance tests</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="css/style.css" type="text/css" />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
</head>
<body>
<script type="module">
import * as CANNON from '../dist/cannon-es.js'
import { Demo } from './js/Demo.js'

/**
* Test the performance limits with a lot of stuff happening all at the same time.
* The simulation should not slow down but rather skip ahead when introducing jank.
* When there are too many object to simulate, the simulation will slow down but still run at a stable framerate.
*/
const demo = new Demo()

let rafId

demo.addScene('200 boxes', () => {
setupFallingBoxes({ N: 200 })
})

demo.addScene('500 boxes', () => {
setupFallingBoxes({ N: 500 })
})

demo.addScene('100 boxes + 32ms jank', () => {
setupFallingBoxes({ N: 100, JANK: 32 })
})

demo.addScene('100 boxes + 48ms jank', () => {
setupFallingBoxes({ N: 100, JANK: 48 })
})

demo.addScene('100 boxes + 64ms jank', () => {
setupFallingBoxes({ N: 100, JANK: 64 })
})

demo.addScene('100 boxes + 128ms jank', () => {
setupFallingBoxes({ N: 100, JANK: 128 })
})

function setupFallingBoxes({ N, JANK }) {
if (rafId) cancelAnimationFrame(rafId)

const world = setupWorld(demo)

const size = 0.25
const mass = 1

const boxShape = new CANNON.Box(new CANNON.Vec3(size, size, size))

const boxes = []
for (let i = 0; i < N; i++) {
// start with random positions
const position = new CANNON.Vec3(
(Math.random() * 2 - 1) * 2.5,
Math.random() * 10,
(Math.random() * 2 - 1) * 2.5
)

const boxBody = new CANNON.Body({
position,
mass,
})
boxBody.addShape(boxShape)
world.addBody(boxBody)
// demo.addVisual(boxBody)
boxes.push(boxBody)
}

// Use instancing so three.js doesn't get in the way
// of performance measuring
demo.addVisualInstanced(boxes)

function animate(ms) {
rafId = requestAnimationFrame(animate)

const time = ms / 1000

const index = Math.floor(Math.random() * N)

boxes[index].position.set(0, Math.random() * 10, 0)

if (JANK) {
blockThread(JANK)
}
}
rafId = requestAnimationFrame(animate)
}

demo.start()

function setupWorld(demo) {
const world = demo.getWorld()
world.gravity.set(0, -50, 0)
// world.broadphase = new CANNON.SAPBroadphase(world)

// world.solver.iterations = 20;
// world.solver.tolerance = 0.001;
// world.allowSleep = true;

// Static ground plane
const groundShape = new CANNON.Plane()
const groundBody = new CANNON.Body({ mass: 0 })
groundBody.addShape(groundShape)
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
world.addBody(groundBody)
demo.addVisual(groundBody)

return world
}

// Block the javascript thread for N milliseconds
function blockThread(milliseconds = 0) {
const start = performance.now()
while (performance.now() < start + milliseconds) {}
}
</script>
</body>
</html>
Binary file added images/performance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,19 @@ <h2>Examples</h2>
/></a>
</div>
</div>

<div class="example">
<a href="examples/performance.html" class="image"><img src="images/performance.png" alt="performance" /></a>
<div class="caption">
<a href="examples/performance.html" class="name">performance</a>
<a
href="https://github.com/react-spring/cannon-es/blob/master/examples/performance.html"
target="_blank"
class="github"
><img src="images/github.svg" alt="Github"
/></a>
</div>
</div>
</div>
</div>
</body>
Expand Down
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ It's a type-safe flatbundle (esm and cjs) which allows for **tree shaking** and

These minor changes and improvements were also made:

- These PRs from the original repo were merged: [schteppe/cannon.js#433](https://github.com/schteppe/cannon.js/pull/433), [schteppe/cannon.js#430](https://github.com/schteppe/cannon.js/pull/430), [schteppe/cannon.js#418](https://github.com/schteppe/cannon.js/pull/418), [schteppe/cannon.js#360](https://github.com/schteppe/cannon.js/pull/360), [schteppe/cannon.js#265](https://github.com/schteppe/cannon.js/pull/265)
- These PRs from the original repo were merged: [schteppe/cannon.js#433](https://github.com/schteppe/cannon.js/pull/433), [schteppe/cannon.js#430](https://github.com/schteppe/cannon.js/pull/430), [schteppe/cannon.js#418](https://github.com/schteppe/cannon.js/pull/418), [schteppe/cannon.js#360](https://github.com/schteppe/cannon.js/pull/360), [schteppe/cannon.js#265](https://github.com/schteppe/cannon.js/pull/265), [schteppe/cannon.js#392](https://github.com/schteppe/cannon.js/pull/392)
- The `ConvexPolyhedron` constructor now accepts an object instead of a list of arguments. [#6](https://github.com/react-spring/cannon-es/pull/6)
- The `Cylinder` is now oriented on the Y axis. [#30](https://github.com/react-spring/cannon-es/pull/30)
- `Body.applyImpulse()` and `Body.applyForce()` are now relative to the center of the body instead of the center of the world [86b0444](https://github.com/schteppe/cannon.js/commit/86b0444c93356aeaa25dd1af795fa162574c6f4b)
- Added a property `World.hasActiveBodies: boolean` which will be false when all physics bodies are sleeping. This allows for invalidating frames when physics aren't active for increased performance.
- 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/react-spring/cannon-es-debugger).
Expand Down
Loading

0 comments on commit b60b86c

Please sign in to comment.