Documentation

Getting Started

The project enable3d offers 4 different ways. As a Standalone 3D Framework, a Physics Plugin for three.js, as a 3D Objects and Physics extension for Phaser or as a library to run Ammo.js on Node.js

In this guide, you will learn how to use the Standalone 3D Framework. Most of what you learn, will be applicable to all packages. Just take a look at the examples to see what Enable3d can do and how to use the different packages.

Note: Not everything is documented yet, the examples page helps a lot!

Help: Do you know something that is not documented yet? Help improving the documentation on GitHub!

Introduction Video

Watch a great introduction video on YouTube

Basic Setup

// import the UMD bundle enable3d.framework.min.js
// or from npm enable3d
import { Project, Scene3D, PhysicsLoader } from 'enable3d'

class MainScene extends Scene3D {
  constructor() {
    super('MainScene')
  }

  async init() {
    this.renderer.setPixelRatio(1)
    this.renderer.setSize(window.innerWidth, window.innerHeight)
  }

  async preload() {
    // preload your assets here
  }

  async create() {
    // set up scene (light, ground, grid, sky, orbitControls)
    this.warpSpeed()

    // enable physics debug
    this.physics.debug.enable()

    // position camera
    this.camera.position.set(10, 10, 20)

    // blue box (without physics)
    this.add.box({ y: 2 }, { lambert: { color: 'deepskyblue' } })

    // pink box (with physics)
    this.physics.add.box({ y: 10 }, { lambert: { color: 'hotpink' } })
  }

  update() {
    this.box.rotation.x += 0.01
    this.box.rotation.y += 0.01
  }
}

// set your project configs
const config = { scenes: [MainScene] }

// load the ammo.js file from the /lib folder and start the project
PhysicsLoader('/lib', () => new Project(config))

Native Three.js Objects

You can use native Three.js objects if you include THREE

import { THREE } from 'enable3d'

// green sphere
const geometry = new THREE.SphereGeometry(0.8, 16, 16)
const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 })
const cube = new THREE.Mesh(geometry, material)
cube.position.set(0.2, 3, 0)
this.scene.add(cube)
// add physics to an existing object
this.physics.add.existing(cube)

Extended Three.js Objects

To have more functionalities and a better compatibility, use new ExtendedMesh() and new ExtendedObject3D() instead of new THREE.Mesh() and new THREE.Object3D()

import { THREE, ExtendedMesh, ExtendedObject3D } from 'enable3d'

const cube = new ExtendedMesh(geometry, material)
// instead of
const cube = new THREE.Mesh(geometry, material)

const object = new ExtendedObject3D()
// instead of
const object = new THREE.Object3D()

Warp Speed

When building your projects, you need to set up the third-dimension most of the time. For instance, you need a basic setup with at least one camera, lights, orbit controls, and so on. To design all these things takes a long time.

You can shorten that time by using Enable3d warpSpeed() function. The function supports the following features:

  • light
  • camera
  • lookAtCenter
  • ground
  • grid
  • orbitControls
  • sky
  • fog

By default, the function enables all the features.

class MainScene extends Scene3D {

  async create() {
    // set up scene (light, ground, grid, sky, orbitControls)
    await this.warpSpeed()

    // additionally warpSpeed() returns the camera, ground, lights, orbitControls.
    const { camera, ground, lights, orbitControls } = await this.warpSpeed()

    // now modify the features (if needed)
    // example:
    camera.position.set(10, 10, 10)
    camera.lookAt(0, 5, 0)

    const { hemisphereLight, ambientLight, directionalLight } = lights
    hemisphereLight.intensity = 0.65

    orbitControls.target.set(0, 5, 0)
  }

If you only want to set up particular features, you can call the function passing the feature name.

this.warpSpeed('light', 'ground', 'sky')

Also, if you want to enable all the features but some, you can specify the feature name with the minus sign.

// enable all the features but the orbit controls
this.warpSpeed('-orbitControls')

The following sections provide some details on all features.

Light

Including 'light' among the features in warpSpeed(), Enable3D will place three lights in the scene:

  • a hemisphere light
  • an ambient light
  • a directional light

The directional light is positioned at (100, 200, 50).

Camera

Including 'camera' among the features in warpSpeed(), Enable3D will place a camera in position (0, 6, 12)

Look at Center

Including 'lookAtCenter' among the features in warpSpeed(), Enable3D will make the camera looking at the scene's initial position.

Ground

Including 'ground' among the features in warpSpeed(), Enable3D will create a ground platform in the scene. The ground platform measures 21x21x1, and it is positioned 0.5 under the origin. By default, the ground platform is not texturized.

Grid

The 'grid' feature of warpSpeed() is available only when the 'ground' feature is included in the feature list. When included, Enable3D will texturize the ground platform with a grid composed of white 1x1 squares with a black border.

Orbit Controls

Including 'orbitControls' among the features in warpSpeed(), Enable3D will include a set of controllers to allow the camera to orbit around a target.

The Orbit Controller is documented in this example from ThreeJS

Sky

Including 'orbitControls' among the features in warpSpeed(), Enable3D will include a Mesh and a set of shaders to simulate the effect of an azure sky. Shaders derive from the example documented in this example from ThreeJS

Fog

Not available in the current release.

Factory

Enable3d provides a simple factory for creating physical objects.

// will add a 5x3x1 red box
const box = physics.add.box(
  { x: 1, y: 2, z: 10, width: 5, height: 3, depth: 1, mass: 2, collisionFlags: 0 },
  { lambert: { color: 'red', transparent: true, opacity: 0.5 } }
)

If you want to use the factory without adding physics to the object, extract the factory from physics.

// get the objects factory
const { factory } = physics

// add the same box as above, but without physics
const box = factory.add.box({ x: 1, y: 2, z: 10, width: 5, height: 3, depth: 1 }, { lambert: { color: 'red', transparent: true, opacity: 0.5 } })

// you can later add physics to it
physics.add.existing(box, { mass: 2, collisionFlags: 0 })

You can also add custom materials like so:

physics.add.box({}, { custom: THREE.MeshLambertMaterial({ color: 0x2194ce }) })

Basic Shapes

Enable3d supports these shapes:

  • Plane
  • Box
  • Sphere
  • Cylinder
  • Cone
  • Capsule
  • Compound
  • Hull
  • HACD (Hierarchical Approximate Convex Decomposition)
  • ConvexMesh
  • ConcaveMesh
  • Heightfield (not yet)

Enable3d does automatically recognize the shape of simple three.js object when adding it via physics.add.existing(object). But you can also change the shape like so physics.add.existing(object, { shape: 'hacd' })

Take a look at the compare-physics-body-shapes and the play-with-physics-bodies examples to see creating & adding shapes in action.

Compound shapes

There are 3 ways to use compound shapes. Automatically generated (child or group) and manually generated. The automatically generated follow three.js objects structure. To manually generate a compound shape, simply add multiple shapes to the second parameter of physics.add.existing()

Automatically

// example: https://enable3d.io/examples/play-with-physics-bodies.html

// compound shape (child based)
// (the center of mass is the center of the box)
let box1 = this.add.box({ x: 0, y: 2 })
let sphere3 = this.add.sphere({ radius: 0.5, x: 0.25, y: 0.5 }) // relative position to box1
box1.add(sphere3)
this.physics.add.existing(box1)

// compound shape (group based)
// (the center of mass is 0,0,0)
let group = new THREE.Group()
const body = this.add.box({ height: 0.8, y: 1, width: 0.4, depth: 0.4 }, { lambert: { color: 0xffff00 } })
const head = this.add.sphere({ radius: 0.25, y: 1.7, z: 0.05 }, { lambert: { color: 0xffff00 } })
group.add(body, head)
group.position.setX(3)
this.add.existing(group)
this.physics.add.existing(group)

Manually

// example: https://enable3d.io/examples/play-with-physics-bodies.html

// custom compound shape
const box = this.add.box({ x: 9 })
const compound = [
  { shape: 'box', width: 0.5, height: 1, depth: 0.4, y: -0.5, z: 0.5 },
  { shape: 'box', width: 2.4, height: 0.6, depth: 0.4, z: -0.4, y: 0.2 },
  { shape: 'sphere', radius: 0.65, z: -0.25, y: 0.35 },
  { shape: 'box', width: 1.5, height: 0.8, depth: 1, y: 0.2, z: 0.2 }
]
this.physics.add.existing(box, { compound })

Physics Body

Body Types

There are 4 different body types you can choose from by setting object.body.setCollisionFlags(number) accordingly.

  • 0 - Dynamic
  • 1 - Static
  • 2 - Kinematic
  • 4 - Ghost (aka Sensor or NO_CONTACT_RESPONSE).

Dynamic

The dynamic bodies react to force and gravity and you can apply velocity and torque to them.

// example: https://enable3d.io/examples/first-phaser-game-3d-version.html

jump() {
  dynamicObject.body.applyForceY(16)
}

Static

These object are just there but do not move at all.

Kinematic

These bodies do NOT react to force or gravity. You can only move them by adjusting its position or rotation.

// example: https://enable3d.io/examples/kinematic-body-orbiting-around-sun.html

update() {
  kinematicObject.position.set(0,5,0)
  kinematicObject.rotation.x += 0.01
  // set needUpdate to true, every time you want
  // to adjust the position or rotation of a kinematic body
  kinematicObject.body.needUpdate = true
}

Ghost

These bodies do never interact with other bodies. But they fire collision events. Use them as sensory in your game. Ghost can be dynamic (4), static (5) or kinematic (6), by settings the collisionFlags accordingly.

Headless Physics

In headless mode, make sure you call transform and refresh on the body when updating the position or rotation. See #242.

Body Methods

You can use a lot of method on the body (object.body.something()). It does not make sense listing them all here.

Just take a look the the physicsBody.ts source code.

Physics Configuration

When we add Enable3d to the scene, there are a few choices we can make.

new Project({ gravity: { x: 0, y: -9.81, z: 0 }, maxSubSteps: 4, fixedTimeStep: 1 / 60 })
  • gravity default { x: 0, y: -9.81, z: 0 } Sets the amount and direction of gravitational forces

  • maxSubSteps default 2 Set the max sub steps allowed.

  • fixedTimeStep default 1 / 60 This number determines how much time one simulation step is to simulate. The smaller the number, the more accurate (and slower) the simulation.

You must always satisfy the equation timeStep(fps) < maxSubSteps * fixedTimeStep

Position, Rotation and Scale

Position

You can only set the position (and rotation) at any moment if it the body is of type kinematic. See Body Types. For all other types, you have to set the position and the rotation before you add a physics body to it.

Example of a non kinematic box:

const { factory } = physics

// add a box without physics
const box = factory.add.box()
// set position and rotation
box.position.set(10, 10, 0)
box.rotation.set(0, Math.PI / 2, 0)
// add physics to the box
physics.add.existing(box)

But if you really need to set a new position or rotation to any other type than kinematic, see the Teleport Example.

Rotation

Same as Position.

Scale

You can't scale a physics body after added to the world. But you can scale your object before adding physics to it or remove the physics body and add a new one.

Example of a Torus:

const { factory } = physics

// add torus (without physics)
let torus = factory.add.torus()
// scale the torus
torus.scale.set(2, 2, 2)
// add a rigid body to it
physics.add.existing(torus, { shape: 'hacd' })

// add torus (with physics)
let torus = physics.add.torus()
// remove the rigid body
physics.destroy(torus.body)
// scale the torus
torus.scale.set(2, 2, 2)
// add a new rigid body to it
physics.add.existing(torus, { shape: 'hacd' })

Collisions

You can check all collisions on a single body or check for collision between 2 specific bodies or check all collisions on bodies with checkCollisions set to true. The event will be start, colliding or end.

// all collision for blueBox (will set body.checkCollisions = true, on the blueBox)
blueBox.body.on.collision((otherObject, event) => {
  if (otherObject.name !== 'ground') {
    console.log('blueBox collided with another object than the ground')
  }
})
// collision between blueBox and redBox (will set body.checkCollisions = true, on the blueBox and the redBox)
physics.add.collider(blueBox, redBox, event => {
  console.log(`blueBox and redBox: ${event}`)
})
// show all collision for object that have { checkCollisions: true }
physics.collisionEvents.on('collision', data => {
  const { bodies, event } = data
  console.log(bodies[0].name, bodies[1].name, event)
})

const box = physics.add.box({ y: 10 })
box.body.checkCollisions = true // set checkCollisions to true

See the collisions example for details on implementing and using this event.

Motion Clamping

When an object has a high velocity, collisions can be missed if it moves through and past other objects between simulation steps. To fix this, enable CCD motion clamping. For a cube of size 1 try:

// Enable CCD if the object moves more than 1 meter in one simulation frame
object.body.setCcdMotionThreshold(1)

// Set the radius of the embedded sphere such that it is smaller than the object
object.body.setCcdSweptSphereRadius(0.2)

Constraints

The following constraints are available, but I have not yet written the documentation.

  • Lock
  • Fixed
  • Spring
  • Slider
  • Hinge
  • Cone Twist
  • Point To Point

You will find an example of all constraints here.

Lights

Not documented yet.

Loaders

Not documented yet.

Tweens

Want to use Tweens? Checkout https://github.com/tweenjs/tween.js

Controls

JoyStick

Not documented yet.

FirstPersonControls

Not documented yet.

ThirdPersonControls

Not documented yet.

PointerDrag

Not documented yet.

PointerLock

Audio, Keyboard and Mouse

Take a look at the following packages:

Package Description
audio 🎵 Audio library for the Web Audio API.
keyboard ⌨️ Handling of keyboard events.
tap 🖱️ Handling of user interactions such as mouse, touch and pointer events.

Raycasting

Not documented yet.

Objects Raycasting

Physics Raycasting

WebXR

Not documented yet.

Virtual Reality

Augmented Reality

Misc

Not documented yet.

Water