Sunday, October 14, 2012

Three.js 3D in Blogger ~ Now with Animation

This is day two of 3D blogging and the 3D is improving. As you can see (I hope) data can now be animated.

The idea is to be able to display 3D animations created using Three.js in Blogger headers and posts and, well, anywhere on the page.

So let's see how it's done.

The first thing is to load the Three.js scripts and set any global variables.

A hand place to insert the tags is suggested by Netzpot.

In the Blogger Dashboard left menu, go to 'Template' > 'Edit HTML'.

Under the suggested line, add any of the needed code as shown below.

<b:include data='blog' name='all-head-content'/>

<script src='http://mrdoob.github.com/three.js/build/three.min.js'/>
<script src='http://mrdoob.github.com/three.js/examples/js/Detector.js'/>
<script src='http://mrdoob.github.com/three.js/examples/js/Stats.js'/>
<script>
<script>
  var renderer, scene, camera, controls, stats;
  var light, geometry, material, mesh;
  var clock = new THREE.Clock();
  var renderers = [];
</script>

The code for the header image could have gone here as well, but as explained in the previous post, a handy place to put that code is to embed it in a side menu widget. This enables a logged-in user to open the Blogger widget editor with a single click.

Here is all the code in the License widget as of this writing. Note that the code is likely to change because a spinning cube may well be 3D but it's still pretty lame.

<a rel="license" href="http://creativecommons.org/licenses/by-nc/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-nc/3.0/88x31.png" /></a><br />This <span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/InteractiveResource" rel="dct:type">work</span> is licensed under a <a rel="license" javascript:void(0)href="http://creativecommons.org/licenses/by-nc/3.0/">Creative Commons Attribution-NonCommercial 3.0 Unported License</a>.

<script>
  init();
  animate();
 
function init() {
  if ( !Detector.webgl ) Detector.addGetWebGLMessage();

  var geometry, material, mesh;
  camera = new THREE.PerspectiveCamera( 50, 1, 1, 1000 );
  camera.position.set( 50, 50, -50);

  scene = new THREE.Scene();
  geometry = new THREE.CubeGeometry( 10, 10, 10 );
  material = new THREE.MeshNormalMaterial();
  mesh= new THREE.Mesh( geometry, material );
  scene.add( mesh );
  
  renderer = new THREE.WebGLRenderer( {antialias: true } );
  renderer.setSize( 300, 300 );
  var d = document.getElementById('header-inner');
  d.appendChild( renderer.domElement );
  renderer.domElement.style.position = 'absolute';
  renderer.domElement.style.right = '0';
  renderer.domElement.style.top = '0';

  controls = new THREE.TrackballControls( camera, renderer.domElement );

  renderers.push( {renderer: renderer, scene: scene, camera: camera, controls: controls, stats: stats} );
}
 
function animate() {
  requestAnimationFrame( animate );
  render();
}

function render() { 
  var tim = clock.getElapsedTime() * 0.15;
  for ( var i = 0, l = renderers.length; i < l; i++ ) {
    var r = renderers[i];
    r.camera.position.x = 20 * Math.cos( tim  );
    r.camera.position.y = 20 * Math.cos( tim  );
    r.camera.position.z = 20 * Math.sin( tim );
    r.controls.update();
    r.renderer.render( r.scene, r.camera );
    if (r.stats) { r.stats.update(); }
  }
 }
</script>
If you are familiar with Three.js, you will recognize that all of this is ordinary Three.js coding practice. There are just a few things to notice. The header rendering should appear the header, so var d = document.getElementById('header-inner') is used to attach the renderer to the header <div> tag.

One of the goals of this project is to enable all renderers to be animated. In order for the animations not to step all over each other, a single call to requestAnimsationFrame is desirable. But this call must be able to handle rendering an as yet unknown number of renderers each of which might be using different variable names.

The method used here is to create a global array renderers in the header file. A single line is added to any of the init functions which assembles the current variable names into an object and pushes the object to the array.

renderers.push( {renderer: renderer, scene: scene, camera: camera, controls: controls, stats: stats} );

Finally the render function is modified so that a for loop processes the updates to all of the renderers.

As of this writing, every renderer produces the identical camera motion. I expect that in the not distant future, the capability for each renderer to have its own custom animation function will be available.

Each post is handled in a similar manner. Instead of attaching the code to the header, the code should look for the post it's in, so a new post is created and its unique ID is used. Here's the code for this post: var d = document.getElementById('post-body-1464980370977528245');

The only other addition is to add the push to the array: renderers.push( {renderer: renderer, scene: scene, camera: camera, controls: controls, stats: stats} ); to the end of the init() function.

Here is what is so cool: All of this is really entry-level eezy-peezy stuff. Doing something similar to a self-hosted WordPress site should be just as easy. I may even look into the possibility of adding 3D to a WordPress.com blog. Or even a 3D tumblr?

Link to demo code
http://jaanga.github.com/blode/#jaanga.githb.com/Blode/Now-with-Animation