I have a pretty cool walking avatar implemented in Gladius. I still have several challenges remaining to me as I get ready for the alpha release of Gaming JavaScript. Perhaps the biggest is the amount of syntax that I want the kids to have to write in order to make an avatar.
So tonight, I take a step back from Gladius and take a look at Three.js. Like CubicVR.js, Three.js is a 3D rendering engine. Unlike CubicVR.js, Three.js aims to make things as simple as possible. That sounds like it might be useful to me.
I start by downloading Three.js into my app:
$ cd scripts $ wget http://mrdoob.github.com/three.js/build/Three.jsI start with a very simple HTML page that links both Three.js and an avatar.js file that will define my avatar:
<!DOCTYPE html> <html> <head> <script src="/scripts/Three.js"></script> <script src="/scripts/avatar.js"></script> <title>Avatar</title> </head> <body> <h1>Three.js Avatar</h1> </body> </html>As for the
avatar.js
file, I mostly stick to the example on the Three.js site. I start by declaring some globals and initializing the view after the DOM has loaded:var camera, scene, renderer, avatar; document.addEventListener( "DOMContentLoaded", function( e ) { init(); animate(); });The latter function called when the DOM is loaded,
animate()
, is an exact copy from the Three.js example:function animate() { // note: three.js includes requestAnimationFrame shim requestAnimationFrame(animate); render(); } function render() { renderer.render(scene, camera); }The
init()
function also retains most of the example. It defines a scene, adds a camera, and creates a canvas renderer that is added to the page's DOM. Instead of the cube example, however, I add a call to a new buildAvatar()
function:function init() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000); camera.position.z = 1000; scene.add(camera); avatar = buildAvatar(); scene.add(avatar); renderer = new THREE.CanvasRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); }That
buildAvatar()
function is where I get started in earnest. Unlike in CubicVR/Gladius where I set entities as parents of each other, I have to group objects together in Three.js. That is accomplished by creating an empty Object3D
object so that I can use its add()
method to add the head and the body:function buildAvatar() { var avatar = new THREE.Object3D(); var material = new THREE.MeshNormalMaterial(); var body_geometry = new THREE.CylinderGeometry(1, 300, 300); var body = new THREE.Mesh(body_geometry, material); body.position.z = -150; avatar.add(body); var head_geometry = new THREE.SphereGeometry(200); var head = new THREE.Mesh(head_geometry, material); head.position.y = 200; avatar.add(head); return avatar; }The result is a somewhat blocky looking avatar:
I am not too fussed about the blockiness of the avatar; a quick inspection of the source code shows that additional constructor arguments can help with that.
As for the limbs, I start using my favorite thing: frame-of-reference. Specifically, I create a limb, add the arm and the hand to the limb with simple geometry:
var right_limb = new THREE.Object3D();
var right_arm_geometry = new THREE.CylinderGeometry(25, 25, 500);
var right_arm = new THREE.Mesh(right_arm_geometry, material);
right_limb.add(right_arm);
var right_hand_geometry = new THREE.SphereGeometry(75);
var right_hand = new THREE.Mesh(right_hand_geometry, material);
right_hand.position.y = 250;
right_limb.add(right_hand);
I can then position the arm and the hand relative to the rest of the avatar by rotating just the limb: right_limb.position.x = 150;
right_limb.position.z = -50;
right_limb.rotation.z = -Math.PI/3;
avatar.add(right_limb);
I convert that into a factory function for generating limbs:function limb(material) {
var limb = new THREE.Object3D();
var arm_geometry = new THREE.CylinderGeometry(25, 25, 500);
var arm = new THREE.Mesh(arm_geometry, material);
limb.add(arm);
var hand_geometry = new THREE.SphereGeometry(75);
var hand = new THREE.Mesh(hand_geometry, material);
hand.position.y = 250;
limb.add(hand);
return limb;
}
And then add the remaining legs and arms. The result is:That is not bad for a night's work. I am unsure if the Three.js syntax or my familiarity with the topic is more deserving of credit the quickness with which I assembled this. As usual, the answer is probably somewhere in the middle.
I do like the grouping approach to frame of reference. I think that might make more sense to kids. Luckily I know some on which I can experiment. Up tomorrow: animating this Three.js avatar.
Day #450
No comments:
Post a Comment