/t3

Create ThreeJS demos with little code

Primary LanguageJavaScript

t3

t3 is a template to build three.js demos without dealing with the common set up process, this project is inspired by Jeromme's three.js boilerplate

Features

  • Integration with dat.gui and Stats
  • Micro scene handler for your multistaged demo :)
  • Micro camera manager
  • Themes for the default scene
  • Fully integrated with game-shell

Playground

view on requirebin

Fork it and have fun!

Installation

NPM

npm install --save-dev t3-boilerplate

Usage:

var t3 = require('t3-boilerplate')

Bower

Install the package with bower (recommended):

bower install t3

Or save the dist/ files on you project library (the old way)

Use with RequireJS

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 :)
});

Explicitly importing the file on your project

As an alternative if you don't use RequireJS, add the source file (t3.js which can be loaded with the source map for debugging purposes) to your page, t3 will be available through the global t3

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>t3</title>
	<script src="path/to/bower_components/dist/t3.js"></script>
</head>
<!-- ... -->
</html>
var t3 = window.t3;

Getting Started

Basic example

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 a css selector to an element to render the app into:

// examples/scripts/01.js
define(['t3'], function (t3) {
  t3({ target: '#canvas' });
});
<iframe id="t301" class="t3-example" src="../examples/?k=01"> </iframe>

Fullscreen > Source Code >

This basic "world" has:

  • A default scene
  • A default camera with OrbitControls
  • A set of axes positioned at the center of the scene (Source)
  • A gui control to see the info stored on game-shell (Source)
  • A gui control to hide/show helpers on the main scene (Source)

init, tick and render

The game loop is fully controlled by the awesome game-shell, besides checking when the dom is ready to start the application it also emits multiple events to notify when the game state needs to be updated it's not tied to the rendering rate, t3 provides hooks to access the following stages (taken from game-shell)

  • init

This event is fired once the document is loaded and it is safe to access the DOM

  • tick

Called each time the game state needs to be updated. This is not tied to rendering rate.

  • render ([frame_time])

Called when a frame is redrawn. The optional parameter frame_time is a floating point value between 0 and 1 which measures the fractional amount of frames since the last time the game was ticked. This can be used to create smoother sub-tick animations if desired.

When the init event is fired t3 will create the following elements for you

  • scenes: a pool of scenes
  • activeScene: the active scene accessible during any stage through this.scenes['default']
  • cameras: a pool of cameras
  • activeCamera: the active scene accessible during any stage through this.cameras['default'], t3 creates a perspective camera by default
  • renderer: a reference to an instance of THREE.WebGLRenderer
  • datgui: a reference to an instance of dat.gui
  • theme: the theme of the application
  • t3cache: the internal cache if injectCache is passed as a configuration option

NOTE: the game shell instance can be accessed through this.shell

A rotating cube

We're used to create an object and add it to the scene, well, now the scene is this.activeScene and the object has to be added to it:

// examples/scripts/02.js
define(['t3'], function (t3) {
  return t3({
    target: '#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 points to this.scenes.default
      this.activeScene.add(this.cube);
    },
    tick: function (delta) {
      this.cube.rotation.x += 0.01;
      this.cube.rotation.y += 0.01;
    }
  });
});
<iframe id="t302" class="t3-example" src="../examples/?k=02"> </iframe>

Fullscreen > Source Code >

Pause

We can pause the continuous calls to tick by calling this.shell.paused = true, set it to false to unpause it, you can achieve the same with the gui helper

// examples/scripts/03.js
define(['t3'], function (t3) {
  return t3({
    target: '#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.add(this.cube);
    },
    tick: function () {
      this.cube.rotation.x += 0.01;
      this.cube.rotation.y += 0.01;
    }
  });
});
<iframe id="t303" class="t3-example" src="../examples/?k=03"> </iframe> Toggle paused state >

Code for the toggle example:

// instance is the value returned from calling `t3`

Fullscreen > Source Code >

Disabling some objects in the default scene

There are 5 groups on the default scene:

  • A set of axes (RGB = XYZ)
  • Ground
  • 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 helpersOptions option

// examples/scripts/04.js
define(['t3'], function (t3) {
  return t3({
    target: '#canvas',
    helperOptions: {
      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.add(this.cube);
    },
    tick: function () {
      this.cube.rotation.x += 0.01;
      this.cube.rotation.y += 0.01;
    }
  });
});
<iframe id="t304" class="t3-example" src="../examples/?k=04"> </iframe>

Fullscreen > Source Code >

Changing Scenes

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:

// examples/scripts/05.js
define(['t3'], function (t3) {
  return t3({
    target: '#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);
    },
    tick: function (delta) {
      var me = this;
      ['cube', 'cylinder', 'sphere'].forEach(function (v) {
        me[v].rotation.x += 0.01;
        me[v].rotation.y += 0.01;
      });
    }
  });
});
<iframe id="t305" class="t3-example" src="../examples/?k=05"> </iframe> Change scene >

Code to change the scene:

var scenes = ['default', 'cone', 'sphere'];
current = current + 1;
current %= scenes.length;
instance.setActiveScene(scenes[current]);

Fullscreen > Source Code >

Changing Cameras

Since the application has a pool of cameras, adding more cameras is as easy as registering it with a name just like changing scenes:

// examples/scripts/06.js
define(['t3'], function (t3) {
  return t3({
    target: '#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);
    },
    tick: function () {
      this.cube.rotation.x += 0.01;
      this.cube.rotation.y += 0.01;
    }
  });
});
<iframe id="t306" class="t3-example" src="../examples/?k=06"> </iframe> Change camera >

Code to change the camera:

var cameras = ['default', 'origin', 'orthographic'];
current = current + 1;
current %= cameras.length;
instance.setActiveCamera(cameras[current]);

Fullscreen > Source Code >

Themes

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:

// examples/scripts/07.js
define(['t3'], function (t3) {
  return t3({
    target: '#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);
    },
    tick: function () {
      this.cube.rotation.x += 0.01;
      this.cube.rotation.y += 0.01;
    }
  });
});
[Demo >](http://maurizzzio.github.io/t3/examples/?k=07) [Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/07.js) Wanna roll your own? Just add an object to the object `t3.themes` and you're done! ```javascript // examples/scripts/071.js define(['t3'], function (t3) { t3.themes.sandyStone = { clearColor: 0xE6E2AF, fogColor: 0xE6E2AF, groundColor: 0xA7A37E }; return t3({ target: '#canvas', theme: 'sandyStone', 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); }, tick: function () { this.cube.rotation.x += 0.01; this.cube.rotation.y += 0.01; } }); }); ``` The available configuration options are: - clearColor (used in the `THREE.WebGLRenderer` instance) - fogColor (used in the `THREE.Fog` instance) - groundColor (ground color of the helper)

Demo > Source Code >

Caching objects

Another useful utility that the template has is the possibility of caching elements, three.js defines the field name for any THREE.Object3D which allows to make lookups (Source), the template extends this functionality and makes it possible to save the last added/removed object from any instance of THREE.Object3D (Source)

// examples/scripts/08.js
define(['t3'], function (t3) {
  return t3({
    target: '#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 `__t3cache__`
      this.activeScene
        .add(cube)
        // unique identifier = cube
        .cache('cube');

      // removal example
      // this.activeScene
      //   .remove(cube)
      //   .cache();
    },
    tick: function () {
      var cube = this.getFromCache('cube');
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;
    }
  });
});

Fullscreen > Source Code >

Development

After cloning the repo install the required node dependencies with:

npm install

Preview the landing page with:

gulp

Build the dist files with:

gulp build

Project structure

Generated by tree:

src
├── Application.js
├── index.js
├── lib
│   └── OrbitControls.js
├── model
│   └── Coordinates.js
└── themes
    ├── dark.js
    ├── index.js
    └── light.js
5 directories, 13 files

Changelog

0.3.0

game loop runs on top of `game-shell`

0.2.5

`browserify-shim` is no longer a dependency

0.2.4

`extend` is used to merge objects

0.2.3

`ambientConfig` renamed to `helpersOptions`
`Coordinates` now uses `THREE.Grid`

0.2.2

available as `t3-boilerplate` in npm

0.2.0

config.id replaced with config.selector in the configuration options of t3

0.1.3

PojoViz example embedded
Functions now have the form `function Name() {` to be analyzed with pojoviz
Fixed minor typos in the Readme file

0.1.2

Fixed some broken links [thanks to mscdex]
Additional instructions for development
Custom themes

0.1.1

Fixed some typos in the documentation
`t3` is now an alias for `t3.Application.run`

Acknowledgments

Special thanks to @mrdoob the author of three.js

Also this project wouldn't have been possible without the help from the following libraries:

Last but not least, thanks to Udacity and their awesome Interactive 3D graphics course, you should definitely check out their courses :)