annotate js/bt/other_libs/excanvas_0002/examples/example2.html @ 18:0d557e6e73f7

Added beautytips and some additional event handling code to the library.
author David Eads <eads@chicagotech.org>
date Fri, 06 Mar 2009 14:11:46 -0600
parents
children
rev   line source
eads@18 1 <!--
eads@18 2 Copyright 2006 Google Inc.
eads@18 3
eads@18 4 Licensed under the Apache License, Version 2.0 (the "License");
eads@18 5 you may not use this file except in compliance with the License.
eads@18 6 You may obtain a copy of the License at
eads@18 7
eads@18 8 http://www.apache.org/licenses/LICENSE-2.0
eads@18 9
eads@18 10 Unless required by applicable law or agreed to in writing, software
eads@18 11 distributed under the License is distributed on an "AS IS" BASIS,
eads@18 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
eads@18 13 See the License for the specific language governing permissions and
eads@18 14 limitations under the License.
eads@18 15 -->
eads@18 16 <html>
eads@18 17 <head>
eads@18 18 <title>ExplorerCanvas Example 1</title>
eads@18 19 <!--[if IE]><script type="text/javascript" src="../excanvas.js"></script><![endif]-->
eads@18 20 <script type="text/javascript">
eads@18 21 /* -------------------------------------------------------------------- */
eads@18 22
eads@18 23 var canvas, ctx;
eads@18 24 var canvasWidth, halfCanvasWidth;
eads@18 25 var canvasHeight, halfCanvasHeight;
eads@18 26
eads@18 27 var space; // 3D Engine
eads@18 28 var scene; // 3D Scene
eads@18 29
eads@18 30 /* -------------------------------------------------------------------- */
eads@18 31
eads@18 32 /**
eads@18 33 * Space is a simple 3D system.
eads@18 34 *
eads@18 35 * Y+ = up
eads@18 36 * Z+ = into screen
eads@18 37 * X+ = right
eads@18 38 */
eads@18 39 function Space() {
eads@18 40 this.m = this.createMatrixIdentity();
eads@18 41 this.mStack = [];
eads@18 42 }
eads@18 43
eads@18 44 Space.prototype.createMatrixIdentity = function() {
eads@18 45 return [
eads@18 46 [1, 0, 0, 0],
eads@18 47 [0, 1, 0, 0],
eads@18 48 [0, 0, 1, 0],
eads@18 49 [0, 0, 0, 1]
eads@18 50 ];
eads@18 51 }
eads@18 52
eads@18 53 /**
eads@18 54 * Multiplies two 4x4 matricies together.
eads@18 55 */
eads@18 56 Space.prototype.matrixMultiply = function(m1, m2) {
eads@18 57 var result = this.createMatrixIdentity();
eads@18 58
eads@18 59 var width = m1[0].length;
eads@18 60 var height = m1.length;
eads@18 61
eads@18 62 if (width != m2.length) {
eads@18 63 // error
eads@18 64 }
eads@18 65
eads@18 66 for (var x = 0; x < width; x++) {
eads@18 67 for (var y = 0; y < height; y++) {
eads@18 68 var sum = 0;
eads@18 69
eads@18 70 for (var z = 0; z < width; z++) {
eads@18 71 sum += m1[y][z] * m2[z][x];
eads@18 72 }
eads@18 73
eads@18 74 result[y][x] = sum;
eads@18 75 }
eads@18 76 }
eads@18 77
eads@18 78 return result;
eads@18 79 }
eads@18 80
eads@18 81 /**
eads@18 82 * Transforms a coordinate using the current transformation
eads@18 83 * matrix, then flattens it using the projection matrix.
eads@18 84 */
eads@18 85 Space.prototype.flatten = function(point) {
eads@18 86 var p = [[point.x, point.y, point.z, 1]];
eads@18 87 var pm = this.matrixMultiply(p, this.m);
eads@18 88
eads@18 89 point.tx = pm[0][0];
eads@18 90 point.ty = pm[0][1];
eads@18 91 point.tz = pm[0][2];
eads@18 92
eads@18 93 // lazy projection
eads@18 94 point.fx = halfCanvasWidth + (canvasWidth * point.tx / point.tz);
eads@18 95 point.fy = halfCanvasHeight -(canvasWidth * point.ty / point.tz);
eads@18 96 }
eads@18 97
eads@18 98 /**
eads@18 99 * Translate (move) the current transformation matrix
eads@18 100 */
eads@18 101 Space.prototype.translate = function(x, y, z) {
eads@18 102 var m = [
eads@18 103 [1, 0, 0, 0],
eads@18 104 [0, 1, 0, 0],
eads@18 105 [0, 0, 1, 0],
eads@18 106 [x, y, z, 1]
eads@18 107 ];
eads@18 108
eads@18 109 this.m = this.matrixMultiply(m, this.m);
eads@18 110 }
eads@18 111
eads@18 112 /**
eads@18 113 * Rotate the current transformation matrix. Rotations are
eads@18 114 * world-oriented, and occur in y,x,z order.
eads@18 115 */
eads@18 116 Space.prototype.rotate = function(x, y, z) {
eads@18 117 if (y) {
eads@18 118 var cosY = Math.cos(y);
eads@18 119 var sinY = Math.sin(y);
eads@18 120 var rotY = [
eads@18 121 [cosY, 0, sinY, 0],
eads@18 122 [0, 1, 0, 0],
eads@18 123 [-sinY, 0, cosY, 0],
eads@18 124 [0, 0, 0, 1]
eads@18 125 ];
eads@18 126
eads@18 127 this.m = this.matrixMultiply(this.m, rotY);
eads@18 128 }
eads@18 129
eads@18 130 if (x) {
eads@18 131 var cosX = Math.cos(x);
eads@18 132 var sinX = Math.sin(x);
eads@18 133 var rotX = [
eads@18 134 [1, 0, 0, 0],
eads@18 135 [0, cosX, -sinX, 0],
eads@18 136 [0, sinX, cosX,0],
eads@18 137 [0, 0, 0, 1]
eads@18 138 ];
eads@18 139 this.m = this.matrixMultiply(this.m, rotX);
eads@18 140 }
eads@18 141
eads@18 142 if (z) {
eads@18 143 var cosZ = Math.cos(z);
eads@18 144 var sinZ = Math.sin(z);
eads@18 145 var rotZ = [
eads@18 146 [cosZ, -sinZ, 0, 0],
eads@18 147 [sinZ, cosZ, 0, 0],
eads@18 148 [0, 0, 1, 0],
eads@18 149 [0, 0, 0, 1]
eads@18 150 ];
eads@18 151
eads@18 152 this.m = this.matrixMultiply(this.m, rotZ);
eads@18 153 }
eads@18 154 }
eads@18 155
eads@18 156 /**
eads@18 157 * Pushes the current transformation onto the stack
eads@18 158 */
eads@18 159 Space.prototype.push = function() {
eads@18 160 this.mStack.push(this.m);
eads@18 161 this.m = [
eads@18 162 [this.m[0][0], this.m[0][1], this.m[0][2], this.m[0][3]],
eads@18 163 [this.m[1][0], this.m[1][1], this.m[1][2], this.m[1][3]],
eads@18 164 [this.m[2][0], this.m[2][1], this.m[2][2], this.m[2][3]],
eads@18 165 [this.m[3][0], this.m[3][1], this.m[3][2], this.m[3][3]]
eads@18 166 ];
eads@18 167 }
eads@18 168
eads@18 169 /**
eads@18 170 * Pops the end off the transformation stack
eads@18 171 */
eads@18 172 Space.prototype.pop = function() {
eads@18 173 this.m = this.mStack.pop();
eads@18 174 }
eads@18 175
eads@18 176 /* -------------------------------------------------------------------- */
eads@18 177
eads@18 178 /**
eads@18 179 * A 3d coordinate
eads@18 180 */
eads@18 181 function Point(x, y, z) {
eads@18 182 this.x = x;
eads@18 183 this.y = y;
eads@18 184 this.z = z;
eads@18 185
eads@18 186 // Relative to camera coordinates
eads@18 187 this.tx;
eads@18 188 this.ty;
eads@18 189 this.tz;
eads@18 190
eads@18 191 // Flattened coordinates
eads@18 192 this.fx;
eads@18 193 this.fy;
eads@18 194 }
eads@18 195
eads@18 196 /**
eads@18 197 * A Shape is made up of polygons
eads@18 198 */
eads@18 199 function Shape() {
eads@18 200 this.points = [];
eads@18 201 this.polygons = [];
eads@18 202 }
eads@18 203
eads@18 204 /**
eads@18 205 * Draws the shape
eads@18 206 */
eads@18 207 Shape.prototype.draw = function(drawlist) {
eads@18 208 for (var i = 0; i< this.points.length; i++) {
eads@18 209 space.flatten(this.points[i]);
eads@18 210 }
eads@18 211
eads@18 212 for (var i = 0; i< this.polygons.length; i++) {
eads@18 213 var poly = this.polygons[i]; // convenience
eads@18 214
eads@18 215 space.flatten(poly.origin);
eads@18 216
eads@18 217 // lazy backface culling
eads@18 218 if (poly.normal && this.backface) {
eads@18 219 space.flatten(poly.normal);
eads@18 220
eads@18 221 var originDist = Math.pow(poly.origin.tx, 2)
eads@18 222 + Math.pow(poly.origin.ty, 2)
eads@18 223 + Math.pow(poly.origin.tz, 2);
eads@18 224
eads@18 225 var normalDist = Math.pow(poly.normal.tx, 2)
eads@18 226 + Math.pow(poly.normal.ty, 2)
eads@18 227 + Math.pow(poly.normal.tz, 2);
eads@18 228
eads@18 229 if(originDist > normalDist) {
eads@18 230 drawlist.push(poly);
eads@18 231 }
eads@18 232 } else {
eads@18 233 drawlist.push(poly);
eads@18 234 }
eads@18 235 }
eads@18 236 }
eads@18 237
eads@18 238 /**
eads@18 239 * A polygon is a connection of points in the shape object. You
eads@18 240 * should probably try to make them coplanar.
eads@18 241 */
eads@18 242 function Polygon(points, normal, backface, type, color) {
eads@18 243 this.points = points;
eads@18 244
eads@18 245 this.origin = new Point(0, 0, 0);
eads@18 246 for(var i = 0; i < this.points.length; i++) {
eads@18 247 this.origin.x += this.points[i].x;
eads@18 248 this.origin.y += this.points[i].y;
eads@18 249 this.origin.z += this.points[i].z;
eads@18 250 }
eads@18 251
eads@18 252 this.origin.x /= this.points.length;
eads@18 253 this.origin.y /= this.points.length;
eads@18 254 this.origin.z /= this.points.length;
eads@18 255
eads@18 256 if (normal) {
eads@18 257 this.normal = new Point(this.origin.x + normal.x,
eads@18 258 this.origin.y + normal.y,
eads@18 259 this.origin.z + normal.z);
eads@18 260 } else {
eads@18 261 this.normal = null;
eads@18 262 }
eads@18 263
eads@18 264 this.backface = backface;
eads@18 265 this.type = type;
eads@18 266 this.color = color;
eads@18 267 }
eads@18 268
eads@18 269 Polygon.SOLID = 0;
eads@18 270 Polygon.WIRE = 1;
eads@18 271
eads@18 272 /**
eads@18 273 * Draws the polygon. Assumes that the points have already been
eads@18 274 * flattened.
eads@18 275 */
eads@18 276 Polygon.prototype.draw = function() {
eads@18 277 ctx.beginPath();
eads@18 278 ctx.moveTo(this.points[0].fx, this.points[0].fy);
eads@18 279
eads@18 280 for(var i = 0; i < this.points.length; i++) {
eads@18 281 ctx.lineTo(this.points[i].fx, this.points[i].fy);
eads@18 282 }
eads@18 283
eads@18 284 ctx.closePath();
eads@18 285
eads@18 286 var color = this.color;
eads@18 287
eads@18 288 /*
eads@18 289 // Do lighting here
eads@18 290 lightvector = Math.abs(this.normal.x + this.normal.y);
eads@18 291 if(lightvector > 1) {
eads@18 292 lightvector = 1;
eads@18 293 }
eads@18 294
eads@18 295 color[0] = (color[0] * lightvector).toString();
eads@18 296 color[1] = (color[1] * lightvector).toString();
eads@18 297 color[2] = (color[2] * lightvector).toString();
eads@18 298 */
eads@18 299
eads@18 300 if (color.length > 3) {
eads@18 301 var style = ["rgba(",
eads@18 302 color[0], ",",
eads@18 303 color[1], ",",
eads@18 304 color[2], ",",
eads@18 305 color[3], ")"].join("");
eads@18 306 } else {
eads@18 307 var style = ["rgb(",
eads@18 308 color[0], ",",
eads@18 309 color[1], ",",
eads@18 310 color[2], ")"].join("");
eads@18 311 }
eads@18 312
eads@18 313 if (this.type == Polygon.SOLID) {
eads@18 314 ctx.fillStyle = style;
eads@18 315 ctx.fill();
eads@18 316 } else if (this.type == Polygon.WIRE) {
eads@18 317 ctx.strokeStyle = style;
eads@18 318 ctx.stroke();
eads@18 319 }
eads@18 320 }
eads@18 321
eads@18 322 /* -------------------------------------------------------------------- */
eads@18 323
eads@18 324 /**
eads@18 325 * Scene describes the 3D environment
eads@18 326 */
eads@18 327 function Scene() {
eads@18 328 this.shapes = {};
eads@18 329 this.camera = new Point(0, 0, 0);
eads@18 330 this.cameraTarget = new Point(0, 0, 0);
eads@18 331 this.cameraRotation = 0;
eads@18 332
eads@18 333 this.drawlist = [];
eads@18 334 }
eads@18 335
eads@18 336 /**
eads@18 337 * Draw the world
eads@18 338 */
eads@18 339 Scene.prototype.draw = function() {
eads@18 340 space.push();
eads@18 341
eads@18 342 // Camera transformation
eads@18 343 space.translate(
eads@18 344 -this.camera.x,
eads@18 345 -this.camera.y,
eads@18 346 -this.camera.z
eads@18 347 );
eads@18 348
eads@18 349 // Camera rotation
eads@18 350 var xdiff = this.cameraTarget.x - this.camera.x;
eads@18 351 var ydiff = this.cameraTarget.y - this.camera.y;
eads@18 352 var zdiff = this.cameraTarget.z - this.camera.z;
eads@18 353
eads@18 354 var xzdist = Math.sqrt(Math.pow(xdiff, 2) + Math.pow(zdiff, 2));
eads@18 355
eads@18 356 var xrot = -Math.atan2(ydiff, xzdist); // up/down rotation
eads@18 357 var yrot = Math.atan2(xdiff, zdiff); // left/right rotation
eads@18 358
eads@18 359 space.rotate(xrot, yrot, this.cameraRotation);
eads@18 360
eads@18 361 // Drawing
eads@18 362 this.drawlist = [];
eads@18 363
eads@18 364 for(var i in this.shapes) {
eads@18 365 this.shapes[i].draw(this.drawlist);
eads@18 366 }
eads@18 367
eads@18 368 // Depth sorting (warning: this is only enough to drive this demo - feel
eads@18 369 // free to contribute a better system).
eads@18 370 this.drawlist.sort(function (poly1, poly2) {
eads@18 371 return poly2.origin.tz - poly1.origin.tz;
eads@18 372 });
eads@18 373
eads@18 374 for (var i = 0; i < this.drawlist.length; i++) {
eads@18 375 this.drawlist[i].draw();
eads@18 376 }
eads@18 377
eads@18 378 space.pop();
eads@18 379 }
eads@18 380
eads@18 381 /* -------------------------------------------------------------------- */
eads@18 382
eads@18 383 var count = 0;
eads@18 384
eads@18 385 function loop() {
eads@18 386 ctx.clearRect(0, 0, canvasWidth, canvasHeight);
eads@18 387
eads@18 388 scene.camera.x = 70*Math.sin(count);
eads@18 389 scene.camera.y = 70;
eads@18 390 scene.camera.z = 70*Math.cos(count);
eads@18 391 scene.cameraRotation = count / 10;
eads@18 392
eads@18 393 count += 0.01;
eads@18 394 scene.draw();
eads@18 395 }
eads@18 396
eads@18 397 function load() {
eads@18 398 // Init drawing system
eads@18 399 canvas = document.getElementById("cv");
eads@18 400 ctx = canvas.getContext("2d");
eads@18 401
eads@18 402 canvasWidth = canvas.width;
eads@18 403 canvasHeight = canvas.height;
eads@18 404 halfCanvasWidth = canvasWidth * 0.5;
eads@18 405 halfCanvasHeight = canvasHeight * 0.5;
eads@18 406
eads@18 407 // Init 3D components
eads@18 408 space = new Space();
eads@18 409 scene = new Scene();
eads@18 410
eads@18 411 // Create a box shape and add it to the scene
eads@18 412 scene.shapes['box'] = new Shape();
eads@18 413 var p = scene.shapes['box'].points; // for convenience
eads@18 414
eads@18 415 p[0] = new Point(-10, -10, -10); // left bottom front
eads@18 416 p[1] = new Point(10, -10, -10); // right bottom front
eads@18 417 p[2] = new Point(10, 10, -10); // right top front
eads@18 418 p[3] = new Point(-10, 10, -10); // left top front
eads@18 419
eads@18 420 p[4] = new Point(-10, -10, 10); // left bottom back
eads@18 421 p[5] = new Point(10, -10, 10); // right bottom back
eads@18 422 p[6] = new Point(10, 10, 10); // right top back
eads@18 423 p[7] = new Point(-10, 10, 10); // left top back
eads@18 424
eads@18 425 // Back
eads@18 426 scene.shapes['box'].polygons.push(new Polygon(
eads@18 427 [ p[0], p[1], p[2], p[3] ],
eads@18 428 new Point(0, 0, -1),
eads@18 429 true /* double-sided */,
eads@18 430 Polygon.SOLID,
eads@18 431 [255, 0, 0]
eads@18 432 ));
eads@18 433
eads@18 434 // Front
eads@18 435 scene.shapes['box'].polygons.push(new Polygon(
eads@18 436 [ p[4], p[5], p[6], p[7] ],
eads@18 437 new Point(0, 0, 1),
eads@18 438 true /* double-sided */,
eads@18 439 Polygon.SOLID,
eads@18 440 [0, 0, 255]
eads@18 441 ));
eads@18 442
eads@18 443 // Top
eads@18 444 scene.shapes['box'].polygons.push(new Polygon(
eads@18 445 [ p[2], p[3], p[7], p[6] ],
eads@18 446 new Point(0, 1, 0),
eads@18 447 false /* single-sided */,
eads@18 448 Polygon.WIRE,
eads@18 449 [0, 255, 0]
eads@18 450 ));
eads@18 451
eads@18 452 // Transparent Top
eads@18 453 scene.shapes['box'].polygons.push(new Polygon(
eads@18 454 [ p[2], p[3], p[7], p[6] ],
eads@18 455 new Point(0, 1, 0),
eads@18 456 false /* single-sided */,
eads@18 457 Polygon.SOLID,
eads@18 458 [0, 255, 0, 0.4]
eads@18 459 ));
eads@18 460
eads@18 461 // Left
eads@18 462 scene.shapes['box'].polygons.push(new Polygon(
eads@18 463 [ p[0], p[4], p[7], p[3] ],
eads@18 464 new Point(-1, 0, 0),
eads@18 465 true /* double-sided */,
eads@18 466 Polygon.SOLID,
eads@18 467 [255, 255, 0]
eads@18 468 ));
eads@18 469
eads@18 470 // Right
eads@18 471 scene.shapes['box'].polygons.push(new Polygon(
eads@18 472 [ p[1], p[5], p[6], p[2] ],
eads@18 473 new Point(1, 0, 0),
eads@18 474 true /* double-sided */,
eads@18 475 Polygon.SOLID,
eads@18 476 [0, 255, 255]
eads@18 477 ));
eads@18 478
eads@18 479 // Create a floor shape and add it to the scene
eads@18 480 scene.shapes['floor'] = new Shape();
eads@18 481 var p = scene.shapes['floor'].points; // for convenience
eads@18 482
eads@18 483 p[0] = new Point(-40, -10, -40);
eads@18 484 p[1] = new Point(-40, -10, 40);
eads@18 485 p[2] = new Point( 40, -10, 40);
eads@18 486 p[3] = new Point( 40, -10, -40);
eads@18 487
eads@18 488 // Floor
eads@18 489 scene.shapes['floor'].polygons.push(new Polygon(
eads@18 490 [ p[0], p[1], p[2], p[3] ],
eads@18 491 new Point(0, 1, 0),
eads@18 492 false /* single-sided */,
eads@18 493 Polygon.SOLID,
eads@18 494 [45, 45, 45]
eads@18 495 ));
eads@18 496
eads@18 497 setInterval('loop()', 20);
eads@18 498 }
eads@18 499
eads@18 500 /* -------------------------------------------------------------------- */
eads@18 501 </script>
eads@18 502 <style>
eads@18 503 body {
eads@18 504 background-color:black;
eads@18 505 margin:50px;
eads@18 506 text-align:center;
eads@18 507 }
eads@18 508 </style>
eads@18 509 </head>
eads@18 510 <body onload="load();">
eads@18 511 <canvas id="cv" width="400" height="300"></canvas>
eads@18 512 </body>
eads@18 513 </html>