Creating a 3D engine in Javascript - Part 1

Submitted by m_sabal on Mon, 01/07/2013 - 22:40

The world of 3d programming, creating interactive worlds and scenes that resemble our own life experience, to many is a land of mystery. Until recently, rendering 3d models or games required either a desktop client or some kind of pre-processing. With HTML5 and browser canvases, it is now possible to develop 3-dimensionally cross-platform via the web. While there are many Javascript toolkits already published for 3d programming, such as Cottage (http://anemonesoft.com/os/cottage/) or jsc3d (http://code.google.com/p/jsc3d/), they are often focused on specific niches.

This new series of blog articles will share my own adventures in developing a 3d engine in Javascript. The goal is to be able to render and explore multifloor buildings both inside and out, and even entire neighborhoods. There's also the thrill of learning a whole new group of skills.

To start, we'll need three files. Index.html sets up the basic template to call the Javascript.

<!DOCTYPE HTML>
<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<TITLE>Canvas Javascript 3D Demo</TITLE>
<SCRIPT type="text/javascript" src="resman.js"></SCRIPT>
<SCRIPT type="text/javascript" src="wireframe.js"></SCRIPT>
</HEAD>
<BODY onLoad="wireframe_start('canvas');">
<DIV id="screen">
<CANVAS id="canvas" width="10" height="10" style="border:3px solid green;">
You need a browser that supports canvas.
</CANVAS>
</DIV>
</BODY>
</HTML>

The DIV and CANVAS tags in the body allow us to make application specific adjustments without affecting the core of the library. It also gives a place to check for errors by commenting out the script lines to make sure the rest of the template looks the way it should. The onLoad event handler calls the application initialization, not the resource manager initialization. The second file will grow as we add utility functions and additional resource management routines. Resman.js looks like this:


function resman_start(cantag, border, backcolor) {
can = document.getElementById(cantag);
w = self.innerWidth-32;
h = self.innerHeight-32;
can.width = w;
can.height = h;
if (border) {
can.style.border = "3px solid green";
}
can.style.background = backcolor;
context = can.getContext('2d');
return context;
}

The resource manager initialization examines the size of the client area of the browser and expands the canvas element to fit. It also adds an optional border and background, returning the graphics context to the application. The third file will be customized for every different application. Since our first actual 3d rendering will be in wireframe, we'll call this one wireframe.js:


function wireframe_start(cantag) {
var context;
context = resman_start(cantag,true,"#000000");
can = document.getElementById(cantag);
h = can.height;
w = can.width;
context.fillStyle="rgb(0,255,0)";
context.fillRect(0,Math.floor(h*.68),w,1);
}

In this basic example, a green line is drawn as a horizon. The fillRect method does not use upper left / lower right coordinates. Instead, it uses upper left / width and height. The code will be better organized and easier to maintain by using object oriented concepts; but since the development of both the library and applications will be done in such small steps with an emphasis on learning and understanding, procedural style makes it easier to explain the concepts.