-
Notifications
You must be signed in to change notification settings - Fork 165
Physics
ALPHA status!
- Overview
- Demos
- Creating a Physics Body
- Box-shaped Body
- Sphere-shaped Body
- Plane-shaped Body
- Body Extents
- Physics Materials
- Configuring a Physics System
- Physics Primitives
- Teapot Primitive
- Box Primitive
- Sphere Primitive
- Plane Primitive
- Switching Physics Engines
- Roadmap
SceneJS supports rigid-body physics through custom nodes which may be inserted into scene graphs to define physical behaviours for their child nodes. Physical simulations may be run across multiple systems, with each system running within an engine on a separate worker thread. This means that when one physics system is overloaded it will affect the performance of the rendering thread and other physics systems. The physics engines are pluggable - they are JigLibJS by default, but may be easily replaced with alternatives such as AmmoJS, CannonJS, or even with a proxy to a high-performance server-based engine.
100 bodies in a single system - Run demo | 400 bodies in four systems - Run demo | 30 Falling boxes - Run demo |
A physics/body
node creates a body in a physics system and causes its child nodes to translate and rotate according to the body's movements and interactions with other bodies.
The shape
parameter on this node specifies the shape of the region occupied by the body. We'll describe the different shapes available, starting with the box
shape, with which we'll introduce the general parameters of the node. Then we'll describe the other shape types, describing only the parameters specific to each of those shape types.
Example:
someNode.addNode({
type: "physics/body",
// This optional "systemId" param selects which system to create body in,
// allowing multiple systems per scene. The system is created if not yet
// existing. The body is created in the nameless default system when this
// param is omitted.
systemId: "mySystem",
// Available shapes are "box", "sphere", "plane" etc.
shape: "box",
// Center of body
pos: [-10, 2, 100],
// Dimension parameters will be shape-specific
size: [3,1, 2],
// Mass
mass: 0.4,
// The coefficient of restitution (COR) of two colliding objects is a
// fractional value representing the ratio of speeds after and before
// an impact, taken along the line of the impact.
restitution: 0.9,
// Friction
friction: 0.3,
// Velocity vector
velocity: [0.2,0.3,-0.2],
// Specify whether or not this body is able to move
// Unmoving bodies would be things like walls and floors
movable: true,
// Child nodes
nodes: [
// Child nodes can be anything, but must fit within the dimensions
// of the physics body. In this case, we have a child box primitive,
// implemented by the plugin at
// http://scenejs.org/api/latest/plugins/node/prims/box.js
{
type: "prims/box",
size: [3, 1, 2]
}
]
});
- There is one default physics system per scene
- Each system has its own physics engine (eg. JigLibJS) that runs in its own Web worker
- Using an optional
systemId
property on thephysics/body
node, multiple physics systems may be created per scene - Note that vectors (size, pos, velocity) on the
physics/body
and ``prims/box``` are arrays - we're deprecating object formats for those sort of params. physics/body
node implementation
Example:
someNode.addNode({
type: "physics/body",
// Optional physics system ID - fall back on scene's default system
systemId: "mySystem",
// Spherical body shape
shape: "sphere",
// Center of body
pos: [-10, 2, 100],
// Sphere radius
radius: 1.0,
// Mass
mass: 0.4,
// The coefficient of restitution
restitution: 0.9,
// Friction
friction: 0.3,
// Velocity vector
velocity: [0.2,0.3,-0.2],
// Body is moving
movable: true,
// Child nodes
nodes: [
// Child sphere primitive, implemented by the plugin at
// http://scenejs.org/api/latest/plugins/node/prims/sphere.js
{
type: "prims/sphere",
radius: 1
}
]
});
Example:
someNode.addNode({
type: "physics/body",
// Optional physics system ID - fall back on scene's default system
systemId: "mySystem",
// Planar body shape
shape: "plane",
// Position
pos:[0, 0, 0],
// Points upwards
dir:[0, 1, 0],
// Let's make it stationary
velocity:[0, 0, 0],
// Mass not relevant for unmoving object
mass:0.0,
// The coefficient of restitution
restitution:0.9,
friction:0.3,
// Let's make it stationary
movable:false,
// Child nodes
nodes:[
// Blue color for ground plane
{
type:"material",
color:{ r:0.8, g:0.8, b:1.0 },
nodes:[
// Grid ground plane geometry, implemented by plugin at
// http://scenejs.org/api/latest/plugins/node/prims/grid.js
{
type:"prims/grid",
size:{ x:1000, z:1000 },
xSegments:200,
zSegments:200
}
]
}
]
});
More body types coming up.
You must ensure that the extents of the physics/body
enclose the space occupied by its child nodes. Ideally, we would do that automatically, expanding and contracting the extents as child nodes are added and removed.
However, the reasons we don't (re)calculated them automatically are:
- It's complex, where there are too many cases to consider (at least at this stage), and we need to keep the JavaScript simple
- The child nodes might be moving, and thus the boundary would be specified for the extents of their movement, (which again, could be hard to compute in advance, or synchronise the body extents with)
- Whatever creates the
physics/body
node (eg. a loader, or another custom node type) is likely to be in a position of calculate the extents of child nodes anyway
Although you can specify properties like restitution
, friction
and mass
on individual physics/body
nodes, sometimes you'll want to share those properties among a group of physics/body
nodes.
Just as we can have a parent material
node wrapping multiple child geometry
nodes, we can have a physics/material
node wrapping multiple child physics/body
nodes, so that the children inherit properties from the parent:
someNode.addNode({
type: "physics/material",
mass:0.0,
restitution:0.9,
friction:0.3,
nodes: [
{
type: "physics/body",
shape: "plane",
//...
},
{
type: "physics/body",
shape: "sphere",
//...
},
//...
]
});
Note that spatial properties like pos
, dir
, velocity
etc. are not supported on a physics/material
node, because those only make sense on an actual physical body.
The physics/system
node configures a physics system and may appear anywhere within the scene graph.
var physicsSystem = someNode.addNode({
type: "physics/system",
systemId: "mySystem", // Optional - selects scene's default system when absent
gravity: [0, -9.8, 0, 0],
solver:"ACCUMULATED" // (default) or "FAST", "NORMAL"
})
We can reconfigure the system any time:
physicsSystem.setConfigs({
gravity: [0, -4.0, 0, 0],
solver:"FAST"
});
- When the
systemId
is given, thephysics/system
node will configure that system, creating it first if not existing yet. WhensystemId
is absent, the node will configure the scene's default physics system. -
physics/system
nodes are optional - if you createphysics/body
nodes for asystemId
(or the scene's default system) for which nophysics/system
node exists in the scene, then that system will just use its default configuration. physics/system
node implementation
There is a collection of convenience nodes, each of which wraps a child physics/body
, which in turn wraps a child geometry primitive node, and configures the extents of the physics/body
to enclose the primitive. These allow you to just create, for example, a teapot with physics behaviours, in one shot:
someNode.addNode({
type: "physics/teapot",
pos: [-100.0, 30.4, 234.1],
velocity: [0.2,0.3,-0.2],
mass: 5,
//...
});
someNode.addNode({
type: "physics/box",
pos: [-100.0, 30.4, 234.1],
velocity: [0.2,0.3,-0.2],
size: [3.0, 4.0, 2.0],
mass: 5,
//...
});
someNode.addNode({
type: "physics/sphere",
pos: [-100.0, 30.4, 234.1],
velocity: [0.2,0.3,-0.2],
radius: 1,
latitudeBands: 30,
longitudeBands: 30,
wire: false,
mass: 5,
//...
});
someNode.addNode({
type: "physics/plane",
pos: [-100.0, 30.4, 234.1],
velocity: [0.2,0.3,-0.2],
direction: [0,1,0,
width: 10,
height: 10,
widthSegments: 10,
heightSegments: 10
wire: false,
mass: 5,
//...
});
[More to come..]
In development
JigLibJS is the default physics engine, however other supported engines may be selected with a physicsEngine
SceneJS global config. The config will be "jiglibjs" by default, but might also be "cannonjs" or "ammojs" if those are available:
SceneJS,setConfigs({
physicsEngine: "jiglibjs"
});
Each supported physics engine will be a RequireJS module within the plugin support library directory, and will export this interface:
{
/** Configure the engine. This can be done at any time.
* @param configs Key-value map of configs
*/
setConfigs: function(params) {
},
/** Creates a body in the engine and returns an ID for it
* @params params Body properties - possibly generic across all engines
* @params callback Callback which return an updated position and a rotation
* matrix each the body is updated
* @return ID of the new body
*/
createBody : function (params, callback) {
};
/** Updates physics properties an existing body in the engine. Note this
* does not change the shape of the phycics body, just things like mass,
* restitution, velocity, position etc.
* @params bodyId Body ID
* @params params Body properties
*/
updateBody : function (bodyid, params) {
},
/** Removes a body from the engine
*/
removeBody : function (bodyId) {
},
/**
* Integrates this physics system
*/
integrate : function () {
}
}
- Ability to specify physics attributes such as
restitution
orfriction
once for multiplephysics/body
nodes, using scene graph state inheritance in the same way as a parentmaterial
defines surface lighting properties for childgeometry
nodes. We'll probably have something like aphysics/material
node. - Ability to enable or disable physics systems so they don't keep running when their bodies are culled from the view (ie. by frustum culling etc)
- An engine (pluggable as shown above) that proxies a high-performance server-side physics system (ie. written in C/C++, not JavaScript)