schteppe / cannon.js

A lightweight 3D physics engine written in JavaScript.
http://schteppe.github.com/cannon.js
MIT License
4.69k stars 712 forks source link

Nearphase is slow #80

Open mmorrisontx opened 11 years ago

mmorrisontx commented 11 years ago

I'm trying to use cannon as part of what is essentially an FPS engine, but am running into a bit of a performance snag in regards to nearphase.

All collisions are Box against Convex, and it seems that the nearphase calculations take about 1ms per collision. This becomes an issue at about 10-15 collisions, because there is no longer enough time to run a full 60 physics frames per second.

Any ideas on ways to optimize this? Most of my convex objects are very simple (8 vertices at most).

schteppe commented 11 years ago

Nearphase is indeed an expensive operation, especially convex/convex, since they use a geometry intersection algorithm (box/convex is really just convex/convex). Box/plane and box/sphere are examples of special cases where this intersection computation can be made simpler and fast. What you can do:

Can't think of more things now, maybe after the morning coffee...

How does these convex objects look? Are you using the "dev" branch of Cannon.js?

mmorrisontx commented 11 years ago

Yep, I'm on a slightly modified version of the dev branch. The physics geometries are being generated dynamically based on the polyhedrons loaded from our map files, which means that potentially they can be of any shape (although, most of them are "walls," meaning they are essentially boxes).

Is box-box more efficient than box-convex when the convex polyhedron is simply a box? I was under the impression that cannonjs internally treated all box-box collisions as convex-convex anyways.

Regardless, the speed is pretty much "good enough" at the moment -- I might even be able to get away with running the physics simulation at 30fps, and tweening between frames, or running in a web worker.

You can see a demo of the engine at https://demo.sites.team9000.net/ (warning: sound)

schteppe commented 11 years ago

Nice demo! I'm impressed. I can see why you want to keep those 60Hz, the game runs very smooth.

You are right about box/box, it is indeed just convex/convex.

Are you using bounding boxes in the broadphase? It may help to reduce the number of nearphase checks, especially when the walls etc are axis aligned. You see, bounding spheres are used by default. To enable bounding boxes, set world.broadphase.useBoundingBoxes = true;

Are the players boxes too? I guess the players are the only dynamic physics objects that triggers the nearphase when getting close to environment objects? By using physics spheres for the players you could gain a lot of performance, though it may not give the physics you want.

It's a shame Cannon.js only runs in one thread, the nearphase is an embarrassingly parallel task... :(

mmorrisontx commented 11 years ago

Yes, the players are boxes as well. Originally they were spheres, but it ran into some unbearable issues (distance to walls too far, sliding up unusual surfaces, etc). Yes, bounding boxes are being used.

Rather than going with the Physijs method (push all of cannon.js into a web worker), I may try to hack some sort of web worker pool into the nearphase calculations -- simply sending off the vertex lists of both objects and receiving back the collision lists. This way, I could potentially run a pool of 3-4 workers and cut the time down to a fraction.

schteppe commented 11 years ago

Sounds great! I've done some experiments with web workers and in your case I think you will do great. Use Transferable Objects for fast thread communication. You may find relevant stuff to read in my blog entry about parallel javascript physics.

mmorrisontx commented 11 years ago

Another related question: at the moment, I'm using a couple meta objects to test areas around the player for collisions. For example, I have a meta object 0.5 units above the player, used to detect if the player can "step" up onto something, or if they're simply pressing against a wall.

Are the coordinates in a collision meaningful in any way? What do they identify? Why are there approximately four per contact? I'm thinking that I may be able to detect a "step" vs "wall" directly from the location of the collision on the player box, rather than needing the meta objects at all.

schteppe commented 11 years ago

The "collide" event fires on each contact. This is a bit confusing, yes. Should rename it to "contact".

A contact is a point in which two bodies touch. If you have a box on a plane, you will get four contacts, one from each corner that intersects the plane. A sphere on a plane will generate one contact. The ContactGenerator decides how many contact points (instances of ContactEquation) are needed for each intersecting body pair.

Since a box on a plane makes up to four contacts, you get a varying number of "collide" events on each step, which will cause some frustration to you. A solution would be to add an event that fires on each new intersecting body pair instead. This is a todo for now though. Perhaps you'd like to contribute? This event could be fired when the contacts are computed.

The coordinates in the ContactEquation are the positions of the contact points, world oriented, with position relative to the respective body. The reason why there are two coordinates/points per contact point is because there is always a small overlap (which the contact solver will try to fix). I hope this was the information you asked for :/

mmorrisontx commented 11 years ago

Actually, I'm already using some of the hook points you suggested (see: https://github.com/sonicsnes/cannon.js/ -- certainly not contrib quality ). The "collide" event is quite inconvenient as it doesn't let you see the whole state at once, so instead I use a global hook immediately after nearphase which lets me view / modify the contacts as I see fit prior to the solver.