t3 is a template to build three.js applications without worrying about the common set up process and allowing multiple applications per page, this project is inspired by Jeromme's three.js boilerplate
- Module defined to work anywhere (follows the UMD pattern)
- Integration with dat.gui and Stats
- Micro scene handler for your multistaged app/game :)
- Micro camera manager
- Keyboard manager utility to catch keyboard events
- Themes for the default scene
- Frame rate control
- Object caching
Install the package with bower (recommended):
bower install t3
Or save the files in the dist
folder on you project library (the old way)
By default bower will install the package on bower_components/t3
, you might modify your requirejs configuration file adding a path to the source files
requirejs.config({
...
paths: {
t3: 'path/to/bower_components/dist/t3' // concatenated script
// or
t3: 'path/to/bower_components/dist/t3.min' // minified script
}
...
});
And require it on a module using:
define(['t3'], function (t3) {
// module t3 is loaded :)
});
As an alternative if you don't use RequireJS, add the source files (t3.js
or t3.min.js
which can be loaded with the source map for debugging purposes) to your page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>t3</title>
<script src="path/to/bower_components/dist/t3.min.js"></script>
</head>
<!-- ... -->
</html>
It's required that you create a DOM element for the application which needs to be identified with an id
<div id="canvas"></div>
The minimal example requires only the application's wrapper id
:
define(['t3'], function (t3) {
return t3.Application.run({
id: 'canvas'
});
});
Demo in Full Screen > Source Code >
This basic "world" has:
- A default scene
- A default camera with OrbitControls
- A world with a grid in the XZ plane (defined in the Coordinates model)
- A set of axes positioned at the center of the scene (Source)
- A dat-gui folder to enable/disable some options (Source)
- A gui control for the visibility of the axes, ground and grids (Source)
- A gui control to pause the animation (Source)
- A gui control for the maximum frame rate (Source)
Additionally we can set the init
and update
methods in the object passed to
t3.Application.run
.
The init
method is called after t3.Application
's constructor has been called, within this method this
has the following properties:
- scenes: a pool of scenes
- activeScene: the current scene which is by default
this.scenes.default
- cameras: a pool of cameras
- currentCamera: the current camera which is a perspective camera with Orbit controls
- renderer: a reference to an instance of THREE.WebGL renderer
- keyboard: a reference to the keyboard instance
- datgui: a reference to an instance of dat.gui
- loopManager: this instance controls the application frame rate and paused state
- theme: the theme of the application
- t3cache: the internal cache if
injectCache
is passed as a configuration option
The update
method is called from this.loopManager
and receives the time elapsed since the last frame a.k.a. delta
We're used to create an object and add it to the scene, well, now the scene is this.currentScene
and the object has to be added to it:
define(['t3'], function (t3) {
return t3.Application.run({
id: 'canvas',
init: function () {
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshNormalMaterial();
this.cube = new THREE.Mesh(geometry, material);
this.cube.position.set(100, 100, 100);
// this.activeScene equals this.scenes.default
this.activeScene
.add(this.cube);
},
update: function (delta) {
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
}
});
});
Demo in Full Screen > Source Code >
Perhaps we might need to call the game loop at a later time, to do so we can pass the option renderImmediately: false
define(['t3'], function (t3) {
var instance = t3.Application.run({
id: 'canvas',
renderImmediately: false,
init: function () {
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshNormalMaterial();
this.cube = new THREE.Mesh(geometry, material);
this.cube.position.set(100, 100, 100);
// this.activeScene equals this.scenes.default
this.activeScene
.add(this.cube);
},
update: function (delta) {
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
}
});
});
To make it renderable again at some point call instance.loopManager.start()
where the instance is the value returned from calling t3.Application.run
Code for the toggle example:
// instance is the value returned from calling `t3.Application.run`
var paused = instance.loopManager.pause;
instance.loopManager[paused ? 'start' : 'stop']();
Demo in Full Screen > Source Code >
There are 5 groups on the default scene:
- (visible) A set of axes (RGB = XYZ)
- (visible) Ground
- (visible) A grid on the XZ plane
- A grid on the YZ plane
- A grid on the XY plane
The visibility of these elements can be defined in the ambientConfig
option
define(['t3'], function (t3) {
return t3.Application.run({
id: 'canvas',
ambientConfig: {
axes: false,
ground: false,
gridX: true,
gridY: true,
gridZ: true
},
init: function () {
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshNormalMaterial();
this.cube = new THREE.Mesh(geometry, material);
this.cube.position.set(100, 100, 100);
// this.activeScene equals this.scenes.default
this.activeScene
.add(this.cube);
},
update: function (delta) {
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
}
});
});
Demo in Full Screen > Source Code >
Since the application has a pool of scenes, we can create as many scenes as we want and change it in runtime, to do so we can call this.setActiveScene
with the name of the scene:
define(['t3'], function (t3) {
return t3.Application.run({
id: 'canvas',
init: function () {
var scene,
geometry,
material;
// scene setup
this.addScene(new THREE.Scene(), 'cone');
this.addScene(new THREE.Scene(), 'sphere');
// default scene
scene = this.scenes['default'];
geometry = new THREE.BoxGeometry(20, 20, 20);
material = new THREE.MeshNormalMaterial();
this.cube = new THREE.Mesh(geometry, material);
this.cube.position.set(100, 100, 100);
scene.add(this.cube);
// cone scene
scene = this.scenes.cone;
geometry = new THREE.CylinderGeometry(10, 0, 20, 64, 64);
material = new THREE.MeshNormalMaterial();
this.cylinder = new THREE.Mesh(geometry, material);
this.cylinder.position.set(100, 100, 100);
scene.add(this.cylinder);
// sphere scene
scene = this.scenes.sphere;
geometry = new THREE.SphereGeometry(10, 32, 32);
material = new THREE.MeshNormalMaterial();
this.sphere = new THREE.Mesh(geometry, material);
this.sphere.position.set(100, 100, 100);
scene.add(this.sphere);
},
update: function (delta) {
var me = this;
['cube', 'cylinder', 'sphere'].forEach(function (v) {
me[v].rotation.x += 0.01;
me[v].rotation.y += 0.01;
});
}
});
});
Code to change the scene:
var scenes = ['default', 'cone', 'sphere'];
current = current + 1;
current %= scenes.length;
instance.setActiveScene(scenes[current]);
Demo in Full Screen > Source Code >
Since the application has a pool of cameras, adding more cameras is as easy as registering it with a name just like the changing the scene:
define(['t3'], function (t3) {
return t3.Application.run({
id: 'canvas',
init: function () {
var camera,
width = window.innerWidth,
height = window.innerHeight,
fov, ratio, near, far;
// origin camera
fov = 45;
ratio = width / height;
near = 1;
far = 1000;
camera = new THREE.PerspectiveCamera(fov, ratio, near, far);
camera.position.set(30, 30, 30);
camera.lookAt(new THREE.Vector3(100, 100, 100));
this.addCamera(camera, 'origin');
// orthographic camera
camera = new THREE.OrthographicCamera(
width / -2, width / 2, height / 2, height / -2, near, far
);
camera.position.set(200, 300, 200);
camera.lookAt(new THREE.Vector3(100, 100, 100));
this
.addCamera(camera, 'orthographic')
// adds orbit controls to the camera
.createCameraControls(camera);
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshNormalMaterial();
this.cube = new THREE.Mesh(geometry, material);
this.cube.position.set(100, 100, 100);
this.activeScene
.add(this.cube);
},
update: function (delta) {
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
}
});
});
Code to change the camera:
var cameras = ['default', 'origin', 'orthographic'];
current = current + 1;
current %= cameras.length;
instance.setActiveCamera(cameras[current]);
Demo in Full Screen > Source Code >
If you don't like the dark theme you can switch to the light theme on the default scene by passing theme: 'light'
in the configuration options:
define(['t3'], function (t3) {
return t3.Application.run({
id: 'canvas',
theme: 'light',
init: function () {
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshNormalMaterial();
this.cube = new THREE.Mesh(geometry, material);
this.cube.position.set(100, 100, 100);
this.activeScene
.add(this.cube);
},
update: function (delta) {
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
}
});
});
Demo in Full Screen > Source Code >
Another useful utility that the template has is the possibility of caching elements under a name, three.js
is able to look up for a child using a recursive search, the template extends this functionality and makes it possible to save the last added/removed object from any instance of THREE.Object3D
and additionally it makes it possible to call the injected method cache
to cache the object:
define(['t3'], function (t3) {
return t3.Application.run({
id: 'canvas',
injectCache: true,
init: function () {
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh(geometry, material);
cube.position.set(100, 100, 100);
// since THREE.Object3D.prototype was injected with the method
// `cache` we can call it to save the object under this
// instance's `__t3cache__`
this.activeScene
.add(cube)
// unique identifier = cube
.cache('cube');
// removal example
// this.activeScene
// .remove(cube)
// .cache();
},
update: function (delta) {
var cube = this.getFromCache('cube');
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
}
});
});
Demo in Full Screen > Source Code >
t3.Application.run
creates in fact a subclass of t3.Application
allowing an application to be quickly started, you can the functionality of the t3.Application
by creating a subclass just like t3.Application.run
does:
Application.run = function (options) {
options = _.merge({
init: function () {},
update: function () {},
}, options);
assert(options.id, 'canvas id required');
var QuickLaunch = function (options) {
Application.call(this, options);
options.init.call(this, options);
};
QuickLaunch.prototype = Object.create(Application.prototype);
QuickLaunch.prototype.update = function (delta) {
Application.prototype.update.call(this, delta);
options.update.call(this, delta);
};
return new QuickLaunch(options);
};
This project wouldn't have been possible without the help from the following libraries