t3
===
t3 is a template to build [three.js](http://threejs.org) demos without worrying about the common set up process and allowing multiple applications per page, this project is inspired by [Jeromme's three.js boilerplate](https://github.com/jeromeetienne/threejsboilerplate)

Features
--------
- Module defined to work anywhere (follows the UMD pattern)
- Integration with [dat.gui](http://workshop.chromeexperiments.com/examples/gui/) and [Stats](https://github.com/mrdoob/stats.js/)
- Micro scene handler for your multistaged demo :)
- Micro camera manager
- Keyboard manager utility to catch keyboard events
- Themes for the default scene
- Frame rate control

Playground
----------

Powered by <img src="http://requirebin.com/img/logo-black.png"></img>


Installation
------------

### NPM

```bash
npm install --save-dev t3-boilerplate
```

Usage:

```
var t3 = require('t3-boilerplate')
```

### Bower

Install the package with bower (recommended):

```bash
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

```javascript
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:

```javascript
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 files (`t3.js` or `t3.min.js` which can be loaded with the source map for debugging purposes) to your page

```html
<!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>
```

Getting Started
---------------
### Basic example

It's required that you create a DOM element for the application which needs to be identified with an `id`

```html
<div id="canvas"></div>
```

The minimal example requires only the application's wrapper selector:

```javascript
define(['t3'], function (t3) {
  return t3.run({
    selector: '#canvas'
  });
});
```

<iframe class="t3-example" src="../examples/?k=01">
</iframe>

[Fullscreen >](http://maurizzzio.github.io/t3/examples/?k=01)
[Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/01.js)

This basic "world" has:

- A default scene
- A default camera with [OrbitControls](https://github.com/mrdoob/three.js/blob/master/examples/js/controls/OrbitControls.js)
- A a grid in the XZ plane (defined in the [Coordinates model](https://github.com/maurizzzio/t3/blob/master/src/model/Coordinates.js#L22))
- A set of axes positioned at the center of the scene ([Source](https://github.com/maurizzzio/t3/blob/master/src/model/Coordinates.js#L10))
- A dat-gui folder to enable/disable some options ([Source](https://github.com/maurizzzio/t3/blob/master/src/controller/LoopManager.js#L36-38))
 - A gui control for the visibility of the axes, ground and grids ([Source](https://github.com/maurizzzio/t3/blob/master/src/model/Coordinates.js#L58-67))
 - A gui control to pause the animation ([Source](https://github.com/maurizzzio/t3/blob/master/src/controller/LoopManager.js#L40-48))
 - A gui control for the *maximum frame rate* ([Source](https://github.com/maurizzzio/t3/blob/master/src/controller/LoopManager.js#L33-50))

### Init & Update

Let's introduce the `init` and `update` methods in the object passed to `t3.run`.

The `init` method is called after `t3`'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
- **activeCamera**: the current camera which is a perspective camera with Orbit controls and by default is `this.cameras.default`
- **renderer**: a reference to an instance of **THREE.WebGLRenderer**
- **keyboard**: a reference to the keyboard instance (a utility to store keyboard events)
- **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`

### 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:

```javascript
define(['t3'], function (t3) {
  return t3.run({
    selector: '#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;
    }
  });
});
```

<iframe class="t3-example" src="../examples/?k=02">
</iframe>

[Fullscreen >](http://maurizzzio.github.io/t3/examples/?k=02)
[Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/02.js)

### Delaying the game loop

Perhaps we might need to call the game loop at a later time, to do so we can add the option `renderImmediately: false`

```javascript
define(['t3'], function (t3) {
  var instance = t3.run({
    selector: '#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.run`

<iframe id="t303" class="t3-example" src="../examples/?k=03">
</iframe>

<a class="button" href="javascript:;" id="tp03">
  Toggle paused state >
</a>

Code for the toggle example:

```javascript
// instance is the value returned from calling `t3.run`
var paused = instance.loopManager.pause;
instance.loopManager[paused ? 'start' : 'stop']();
```

[Fullscreen >](http://maurizzzio.github.io/t3/examples/?k=03)
[Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/03.js)

### Disabling some objects in the default scene
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

```javascript
define(['t3'], function (t3) {
  return t3.run({
    selector: '#canvas',
    helpersConfig: {
      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;
    }
  });
});
```

<iframe id="t304" class="t3-example" src="../examples/?k=04">
</iframe>

[Fullscreen >](http://maurizzzio.github.io/t3/examples/?k=04)
[Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/04.js)

### 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:

```javascript
define(['t3'], function (t3) {
  return t3.run({
    selector: '#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;
      });
    }
  });
});
```

<iframe id="t305" class="t3-example" src="../examples/?k=05">
</iframe>

<a class="button" href="javascript:;" id="tp05">
  Change scene >
</a>

Code to change the scene:

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

[Fullscreen >](http://maurizzzio.github.io/t3/examples/?k=05)
[Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/05.js)


### 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:

```javascript
define(['t3'], function (t3) {
  return t3.run({
    selector: '#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')
        // add 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;
    }
  });
});
```

<iframe id="t306" class="t3-example" src="../examples/?k=06">
</iframe>

<a class="button" href="javascript:;" id="tp06">
  Change camera >
</a>

Code to change the camera:

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

[Fullscreen >](http://maurizzzio.github.io/t3/examples/?k=06)
[Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/06.js)

### 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:

```javascript
define(['t3'], function (t3) {
  return t3.run({
    selector: '#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;
    }
  });
});
```

<iframe id="t307" class="t3-example" src="../examples/?k=07">
</iframe>

[Fullscreen >](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
define(['t3'], function (t3) {

  t3.themes.sandyStone = {
    clearColor: 0xE6E2AF,
    fogColor: 0xE6E2AF,
    groundColor: 0xA7A37E
  };

  return t3.run({
    selector: '#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);
    },
    update: function (delta) {
      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)

<iframe id="t3071" class="t3-example" src="../examples/?k=071">
</iframe>

[Fullscreen >](http://maurizzzio.github.io/t3/examples/?k=071)
[Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/071.js)

### 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](https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L457-485)), the template extends this functionality and makes it possible to save the last added/removed object from any instance of `THREE.Object3D` ([Source](https://github.com/maurizzzio/t3/blob/master/src/controller/Application.js#L556-596))

```javascript
define(['t3'], function (t3) {
  return t3.run({
    selector: '#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 last added/removed object
      // under `this.__t3cache__` passing a name
      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;
    }
  });
});
```

<iframe id="t308" class="t3-example" src="../examples/?k=08">
</iframe>

[Fullscreen >](http://maurizzzio.github.io/t3/examples/?k=08)
[Source Code >](http://github.com/maurizzzio/t3/blob/master/examples/scripts/08.js)

### Adding custom functionality by extending t3.Application

`t3.run` creates in fact a subclass of `t3.Application` allowing an application to be quickly started, you can extend the functionality of the `t3.Application` class by creating a subclass:

```javascript
Application.run = function (options) {
  options = _.merge({
    init: function () {},
    update: function () {},
  }, options);
  assert(options.selector, 'canvas selector 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);
};
```

### TODO list

- Render only on mouseover
- Pause if not visible

Development
-----------
After cloning the repo install the required node dependencies with:

```javascript
npm install
```

Generate the bundle with:

```javascript
gulp
```

This will generate an unminified file since it creates the bundle with [watchify](https://github.com/substack/watchify), if you want to build the minified file including its source map execute:

```javascript
gulp build
```

Project structure
-----------------
Generated by [tree](http://mama.indstate.edu/users/ice/tree/):
```
src
├── controller
│   ├── Application.js
│   ├── Keyboard.js
│   └── LoopManager.js
├── index.js
├── lib
│   ├── OrbitControls.js
│   ├── THREEx
│   │   ├── FullScreen.js
│   │   ├── WindowResize.js
│   │   └── index.js
│   ├── dat.gui.min.js
│   └── stats.min.js
├── model
│   └── Coordinates.js
└── themes
    ├── dark.js
    └── light.js

5 directories, 13 files
```

Powered by <a href="http://maurizzzio.github.io/PojoViz/public/vulcanize.html#readme">PojoViz</a>:

<a class="button" href="javascript:;" id="show-pojoviz">
  View the structure
</a>

<iframe height="0" id="pojoviz-frame" src=""></iframe>

Changelog
---------

0.2.5
```plain
`browserify-shim` is no longer a dependency
```

0.2.4
```plain
`extend` is used to merge objects
```

0.2.3
```plain
`ambientConfig` renamed to `helpersConfig`
`Coordinates` now uses `THREE.Grid`
```

0.2.2
```plain
available as `t3-boilerplate` in npm
```

0.2.0
```plain
config.id replaced with config.selector in the configuration options of t3.run
```

0.1.3

```plain
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

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

0.1.1

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

Acknowledgments
--------

Special thanks to [@mrdoob](https://twitter.com/mrdoob) the author of [three.js](http://threejs.org/)

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

- [Browserify](http://browserify.org/)
- [Gulp](http://gulpjs.com/)
- [THREEx](http://www.threejsgames.com/extensions/)
- [Flatdoc](http://ricostacruz.com/flatdoc/)
- [JSDoc](http://usejsdoc.org)

Last but not least, thanks to Udacity and their awesome [Interactive 3D graphics course](https://www.udacity.com/course/cs291), you should definitely check out their courses :)