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