topical media & game development

talk show tell print

graphic-canvas-ie-examples-example2.htm / htm



  <!--
          Copyright 2006 Google Inc.
  
          Licensed under the Apache License, Version 2.0 (the "License");
          you may not use this file except in compliance with the License.
          You may obtain a copy of the License at
  
            http://www.apache.org/licenses/LICENSE-2.0
  
  	Unless required by applicable law or agreed to in writing, software
          distributed under the License is distributed on an "AS IS" BASIS,
          WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
          See the License for the specific language governing permissions and
          limitations under the License.
  -->
  <html>
  <head>
          <title>ExplorerCanvas Example 1</title>
          <!--[if IE]><script type="text/javascript" src="graphic-canvas-ie-excanvas.js"></script><![endif]-->
          <script type="text/javascript">
                  /* -------------------------------------------------------------------- */
  
                  var canvas, ctx;
                  var canvasWidth, halfCanvasWidth;
                  var canvasHeight, halfCanvasHeight;
  
                  var space;  // 3D Engine
                  var scene;  // 3D Scene
  
                  /* -------------------------------------------------------------------- */
  
                  
Space is a simple 3D system. Y+ = up Z+ = into screen X+ = right

  
                  function Space() {
                          this.m = this.createMatrixIdentity();
                          this.mStack = [];
                  }
  
                  Space.prototype.createMatrixIdentity = function() {
                          return [
                                  [1, 0, 0, 0],
                                  [0, 1, 0, 0],
                                  [0, 0, 1, 0],
                                  [0, 0, 0, 1]
                          ];
                  }
  
                  
Multiplies two 4x4 matricies together.

  
                  Space.prototype.matrixMultiply = function(m1, m2) {
                          var result = this.createMatrixIdentity();
  
                          var width = m1[0].length;
                          var height = m1.length;
  
                          if (width != m2.length) {
                                  // error
                          }
  
                          for (var x = 0; x < width; x++) {
                                  for (var y = 0; y < height; y++) {
                                          var sum = 0;
  
                                          for (var z = 0; z < width; z++) {
                                                  sum += m1[y][z] * m2[z][x];
                                          }
  
                                          result[y][x] = sum;
                                  }
                          }
  
                          return result;
                  }
  
                  
Transforms a coordinate using the current transformation matrix, then flattens it using the projection matrix.

  
                  Space.prototype.flatten = function(point) {
                          var p = [[point.x, point.y, point.z, 1]];
                          var pm = this.matrixMultiply(p, this.m);
  
                          point.tx = pm[0][0];
                          point.ty = pm[0][1];
                          point.tz = pm[0][2];
  
                          // lazy projection
                          point.fx = halfCanvasWidth + (canvasWidth * point.tx / point.tz);
                          point.fy = halfCanvasHeight -(canvasWidth * point.ty / point.tz);
                  }
  
                  
Translate (move) the current transformation matrix

  
                  Space.prototype.translate = function(x, y, z) {
                          var m = [
                                  [1, 0, 0, 0],
                                  [0, 1, 0, 0],
                                  [0, 0, 1, 0],
                                  [x, y, z, 1]
                          ];
  
                          this.m = this.matrixMultiply(m, this.m);
                  }
  
                  
Rotate the current transformation matrix. Rotations are world-oriented, and occur in y,x,z order.

  
                  Space.prototype.rotate = function(x, y, z) {
                          if (y) {
                                  var cosY = Math.cos(y);
                                  var sinY = Math.sin(y);
                                  var rotY = [
                                          [cosY, 0, sinY, 0],
                                          [0, 1, 0, 0],
                                          [-sinY, 0, cosY, 0],
                                          [0, 0, 0, 1]
                                  ];
  
                                  this.m = this.matrixMultiply(this.m, rotY);
                          }
  
                          if (x) {
                                  var cosX = Math.cos(x);
                                  var sinX = Math.sin(x);
                                  var rotX = [
                                          [1, 0, 0, 0],
                                          [0, cosX, -sinX, 0],
                                          [0, sinX, cosX,0],
                                          [0, 0, 0, 1]
                                  ];
                                  this.m = this.matrixMultiply(this.m, rotX);
                          }
  
                          if (z) {
                                  var cosZ = Math.cos(z);
                                  var sinZ = Math.sin(z);
                                  var rotZ = [
                                          [cosZ, -sinZ, 0, 0],
                                          [sinZ, cosZ, 0, 0],
                                          [0, 0, 1, 0],
                                          [0, 0, 0, 1]
                                  ];
  
                                  this.m = this.matrixMultiply(this.m, rotZ);
                          }
                  }
  
                  
Pushes the current transformation onto the stack

  
                  Space.prototype.push = function() {
                          this.mStack.push(this.m);
                          this.m = [
                                  [this.m[0][0], this.m[0][1], this.m[0][2], this.m[0][3]],
                                  [this.m[1][0], this.m[1][1], this.m[1][2], this.m[1][3]],
                                  [this.m[2][0], this.m[2][1], this.m[2][2], this.m[2][3]],
                                  [this.m[3][0], this.m[3][1], this.m[3][2], this.m[3][3]]
                          ];
                  }
  
                  
Pops the end off the transformation stack

  
                  Space.prototype.pop = function() {
                          this.m = this.mStack.pop();
                  }
  
                  /* -------------------------------------------------------------------- */
  
                  
A 3d coordinate

  
                  function Point(x, y, z) {
                          this.x = x;
                          this.y = y;
                          this.z = z;
  
                          // Relative to camera coordinates
                          this.tx;
                          this.ty;
                          this.tz;
  
                          // Flattened coordinates
                          this.fx;
                          this.fy;
                  }
  
                  
A Shape is made up of polygons

  
                  function Shape() {
                          this.points = [];
                          this.polygons = [];
                  }
  
                  
Draws the shape

  
                  Shape.prototype.draw = function(drawlist) {
                          for (var i = 0; i< this.points.length; i++) {
                                  space.flatten(this.points[i]);
                          }
  
                          for (var i = 0; i< this.polygons.length; i++) {
                                  var poly = this.polygons[i]; // convenience
  
                                  space.flatten(poly.origin);
  
                                  // lazy backface culling
                                  if (poly.normal && this.backface) {
                                          space.flatten(poly.normal);
  
                                          var originDist = Math.pow(poly.origin.tx, 2)
                                                                                                   + Math.pow(poly.origin.ty, 2)
                                                                                                   + Math.pow(poly.origin.tz, 2);
  
                                          var normalDist = Math.pow(poly.normal.tx, 2)
                                                                                                   + Math.pow(poly.normal.ty, 2)
                                                                                                   + Math.pow(poly.normal.tz, 2);
  
                                          if(originDist > normalDist) {
                                                  drawlist.push(poly);
                                          }
                                  } else {
                                          drawlist.push(poly);
                                  }
                          }
                  }
  
                  
A polygon is a connection of points in the shape object. You should probably try to make them coplanar.

  
                  function Polygon(points, normal, backface, type, color) {
                          this.points = points;
  
                          this.origin = new Point(0, 0, 0);
                          for(var i = 0; i < this.points.length; i++) {
                                  this.origin.x += this.points[i].x;
                                  this.origin.y += this.points[i].y;
                                  this.origin.z += this.points[i].z;
                          }
  
                          this.origin.x /= this.points.length;
                          this.origin.y /= this.points.length;
                          this.origin.z /= this.points.length;
  
                          if (normal) {
                                  this.normal = new Point(this.origin.x + normal.x,
                                                                                                                                  this.origin.y + normal.y,
                                                                                                                                  this.origin.z + normal.z);
                          } else {
                                  this.normal = null;
                          }
  
                          this.backface = backface;
                          this.type = type;
                          this.color = color;
                  }
  
                  Polygon.SOLID = 0;
                  Polygon.WIRE = 1;
  
                  
Draws the polygon. Assumes that the points have already been flattened.

  
                  Polygon.prototype.draw = function() {
                          ctx.beginPath();
                          ctx.moveTo(this.points[0].fx, this.points[0].fy);
  
                          for(var i = 0; i < this.points.length; i++) {
                                  ctx.lineTo(this.points[i].fx, this.points[i].fy);
                          }
  
                          ctx.closePath();
  
                          var color = this.color;
  
                          /*
                          // Do lighting here
                          lightvector = Math.abs(this.normal.x + this.normal.y);
                          if(lightvector > 1) {
                                  lightvector = 1;
                          }
  
                          color[0] = (color[0] * lightvector).toString();
                          color[1] = (color[1] * lightvector).toString();
                          color[2] = (color[2] * lightvector).toString();
  			*/
  
                          if (color.length > 3) {
                                  var style = ["rgba(",
                                               color[0], ",",
                                               color[1], ",",
                                               color[2], ",",
                                               color[3], ")"].join("");
                          } else {
                                  var style = ["rgb(",
                                               color[0], ",",
                                               color[1], ",",
                                               color[2], ")"].join("");
                          }
  
                          if (this.type == Polygon.SOLID) {
                                  ctx.fillStyle = style;
                                  ctx.fill();
                          } else if (this.type == Polygon.WIRE) {
                                  ctx.strokeStyle = style;
                                  ctx.stroke();
                          }
                  }
  
                  /* -------------------------------------------------------------------- */
  
                  
Scene describes the 3D environment

  
                  function Scene() {
                          this.shapes = {};
                          this.camera = new Point(0, 0, 0);
                          this.cameraTarget = new Point(0, 0, 0);
                          this.cameraRotation = 0;
  
                          this.drawlist = [];
                  }
  
                  
Draw the world

  
                  Scene.prototype.draw = function() {
                          space.push();
  
                          // Camera transformation
                          space.translate(
                                  -this.camera.x,
                                  -this.camera.y,
                                  -this.camera.z
                          );
  
                          // Camera rotation
                          var xdiff = this.cameraTarget.x - this.camera.x;
                          var ydiff = this.cameraTarget.y - this.camera.y;
                          var zdiff = this.cameraTarget.z - this.camera.z;
  
                          var xzdist = Math.sqrt(Math.pow(xdiff, 2) + Math.pow(zdiff, 2));
  
                          var xrot = -Math.atan2(ydiff, xzdist); // up/down rotation
                          var yrot =  Math.atan2(xdiff, zdiff);  // left/right rotation
  
                          space.rotate(xrot, yrot, this.cameraRotation);
  
                          // Drawing
                          this.drawlist = [];
  
                          for(var i in this.shapes) {
                                  this.shapes[i].draw(this.drawlist);
                          }
  
                          // Depth sorting (warning: this is only enough to drive this demo - feel
                          // free to contribute a better system).
                          this.drawlist.sort(function (poly1, poly2) {
                                  return poly2.origin.tz - poly1.origin.tz;
                          });
  
                          for (var i = 0; i < this.drawlist.length; i++) {
                                  this.drawlist[i].draw();
                          }
  
                          space.pop();
                  }
  
                  /* -------------------------------------------------------------------- */
  
                  var count = 0;
  
                  function loop() {
                          ctx.clearRect(0, 0, canvasWidth, canvasHeight);
  
                          scene.camera.x = 70*Math.sin(count);
                          scene.camera.y = 70;
                          scene.camera.z = 70*Math.cos(count);
                          scene.cameraRotation = count / 10;
  
                          count += 0.01;
                          scene.draw();
                  }
  
                  function load() {
                          // Init drawing system
                          canvas = document.getElementById("cv");
                          ctx = canvas.getContext("2d");
  
                          canvasWidth = canvas.width;
                          canvasHeight = canvas.height;
                          halfCanvasWidth = canvasWidth * 0.5;
                          halfCanvasHeight = canvasHeight * 0.5;
  
                          // Init 3D components
                          space = new Space();
                          scene = new Scene();
  
                          // Create a box shape and add it to the scene
                          scene.shapes['box'] = new Shape();
                          var p = scene.shapes['box'].points; // for convenience
  
                          p[0] = new Point(-10, -10, -10); // left  bottom front
                          p[1] = new Point(10, -10, -10);  // right bottom front
                          p[2] = new Point(10, 10, -10);   // right top    front
                          p[3] = new Point(-10, 10, -10);  // left  top    front
  
                          p[4] = new Point(-10, -10, 10);  // left  bottom back
                          p[5] = new Point(10, -10, 10);   // right bottom back
                          p[6] = new Point(10, 10, 10);    // right top    back
                          p[7] = new Point(-10, 10, 10);   // left  top    back
  
                          // Back
                          scene.shapes['box'].polygons.push(new Polygon(
                                  [ p[0], p[1], p[2], p[3] ],
                                  new Point(0, 0, -1),
                                  true /* double-sided */,
                                  Polygon.SOLID,
                                  [255, 0, 0]
                          ));
  
                          // Front
                          scene.shapes['box'].polygons.push(new Polygon(
                                  [ p[4], p[5], p[6], p[7] ],
                                  new Point(0, 0, 1),
                                  true /* double-sided */,
                                  Polygon.SOLID,
                                  [0, 0, 255]
                          ));
  
                          // Top
                          scene.shapes['box'].polygons.push(new Polygon(
                                  [ p[2], p[3], p[7], p[6] ],
                                  new Point(0, 1, 0),
                                  false /* single-sided */,
                                  Polygon.WIRE,
                                  [0, 255, 0]
                          ));
  
                          // Transparent Top
                          scene.shapes['box'].polygons.push(new Polygon(
                                  [ p[2], p[3], p[7], p[6] ],
                                  new Point(0, 1, 0),
                                  false /* single-sided */,
                                  Polygon.SOLID,
                                  [0, 255, 0, 0.4]
                          ));
  
                          // Left
                          scene.shapes['box'].polygons.push(new Polygon(
                                  [ p[0], p[4], p[7], p[3] ],
                                  new Point(-1, 0, 0),
                                  true /* double-sided */,
                                  Polygon.SOLID,
                                  [255, 255, 0]
                          ));
  
                          // Right
                          scene.shapes['box'].polygons.push(new Polygon(
                                  [ p[1], p[5], p[6], p[2] ],
                                  new Point(1, 0, 0),
                                  true /* double-sided */,
                                  Polygon.SOLID,
                                  [0, 255, 255]
                          ));
  
                          // Create a floor shape and add it to the scene
                          scene.shapes['floor'] = new Shape();
                          var p = scene.shapes['floor'].points; // for convenience
  
                          p[0]  = new Point(-40, -10, -40);
                          p[1]  = new Point(-40, -10,  40);
                          p[2] = new Point( 40, -10,  40);
                          p[3] = new Point( 40, -10, -40);
  
                          // Floor
                          scene.shapes['floor'].polygons.push(new Polygon(
                                  [ p[0], p[1], p[2], p[3] ],
                                  new Point(0, 1, 0),
                                  false /* single-sided */,
                                  Polygon.SOLID,
                                  [45, 45, 45]
                          ));
  
                          setInterval('loop()', 20);
                  }
  
                  /* -------------------------------------------------------------------- */
          </script>
          <style>
          body {
                  background-color:black;
                  margin:50px;
                  text-align:center;
          }
          </style>
  </head>
  <body onload="load();">
    <canvas id="cv" width="400" height="300"></canvas>
  </body>
  </html>
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.