
Click the screenshot to launch the demo. You will need to use the latest version of Chrome, Firefox or Safari if you want to see it running.
This is a re-creation of a Flash experiment from several years ago. Back before I started playing around with 3d, I created a series of experiments exploring cellular automata, the majority of which were variations on the Langton’s Ant algorithm. The most advanced version was one in pseudo-3d, and that was the last for over three years. Now with the advent of easy-to-use 3d libraries (like Three.js, used here), and the option of hardware acceleration to allow for complex animations, I felt it was time to re-visit some of the old experiments.
Here is the source code for the 3d ant:
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas,
stage,
container,
scene,
camera,
renderer,
projector,
light;
var WIDTH = 640,
HEIGHT = 640;
VIEW_ANGLE = 45,
ASPECT = WIDTH/HEIGHT,
NEAR = .1,
FAR = 10000,
centerX = 0,
centerY = 0,
centerZ = 0,
cameraX = 0,
cameraY = 200,
cameraZ = 400;
var antX = 32,
antY = 32,
antZ = 32,
nextX,
nextY,
nextZ,
cellsX = 64,
cellsY = 64,
cellsZ = 64,
cellWidth = 8,
cellHeight = 8,
cellDepth = 8,
antSize = 7,
maxDirections = 8,
colorMultiplier = Math.round(256/cellsX),
xOff = cellsX/2*cellWidth,
yOff = cellsY/2*cellHeight,
zOff = cellsZ/2*cellDepth,
base,
objects,
antDirection = 1,
filledCells=0,
isRotating=true,
isPaused=false;
function init() {
canvas = document.getElementById("myCanvas");
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR );
camera.position.x = cameraX;
camera.position.y = cameraY;
camera.position.z = cameraZ;
camera.lookAt(scene.position);
scene.add( camera );
projector = new THREE.Projector();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( WIDTH, HEIGHT );
light = new THREE.SpotLight();
light.position.set( cameraX, cameraY, cameraZ );
scene.add(light);
canvas.appendChild( renderer.domElement );
renderer.render(scene,camera);
base = new THREE.Object3D();
base.position.x = centerX;
base.position.y = centerY;
base.position.x = centerZ;
base.rotation.y = 45;
scene.add(base);
camera.lookAt(base.position);
objects = new Array(cellsX);
for(var i=0;i<objects.length;i++) {
objects[i] = new Array(cellsY);
for(var j=0;j<objects[i].length;j++) {
objects[i][j] = new Array(cellsZ);
}
}
onEnterFrame();
}
function onEnterFrame() {
requestAnimFrame(onEnterFrame);
renderer.render(scene,camera);
if(isPaused==true) return;
if(!objects[antX][antY][antZ]) {
antDirection++;
if(antDirection == maxDirections) antDirection = 0;
addObject(antX,antY,antZ);
} else {
removeObject(antX,antY,antZ);
antDirection--;
if(antDirection == -1) antDirection = maxDirections-1;
}
switch(antDirection) {
case 0:
antZ--;
break;
case 1:
antX++;
break;
case 2:
antY++;
break;
case 3:
antX--;
break;
case 4:
antZ++;
break;
case 5:
antX++;
break;
case 6:
antY--;
break;
case 7:
antX--;
break;
default:
break;
}
if(antY < 0) antY += cellsY;
if(antY >= cellsY) antY -= cellsY;
if(antX < 0) antX += cellsX;
if(antX >= cellsX) antX -= cellsX;
if(antZ < 0) antZ += cellsZ;
if(antZ >= cellsZ) antZ -= cellsZ;
if(isRotating==true) base.rotation.y +=.01;
document.getElementById("count").value = filledCells;
}
function addObject(x,y,z) {
var sphereMaterial = new THREE.MeshPhongMaterial({
color: getRGB(x,y,z),
opacity:1
});
var obj = new THREE.Mesh(
new THREE.CubeGeometry(antSize,antSize,antSize),
sphereMaterial);
obj.position.x = x*cellWidth-xOff;
obj.position.y = y*cellHeight-yOff;
obj.position.z = z*cellDepth-zOff;
obj.overdraw = true;
base.add(obj);
objects[x][y][z] = obj;
filledCells++;
}
function removeObject(x,y,z) {
base.remove(objects[x][y][z]);
objects[x][y][z] = null;
filledCells--;
}
function getRGB(r,g,b) {
var rgb = parseInt((r*colorMultiplier).toString(16) + (g*colorMultiplier).toString(16) + (b*colorMultiplier).toString(16),16);
return rgb;
}
onload=init;
The above code assumes you have downloaded a copy of Three.js and have a HTML page with appropriate elements set up. To see the entirety of the code I used, right-click on the screenshot and open it in a new tab or window, then view source. It’s all one file, other than the Three.js library. Feel free to copy it in its entirety and play around.