SunSpider/tests/v8-v4/v8-raytrace.js
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 // The ray tracer code in this file is written by Adam Burmister. It
       
     2 // is available in its original form from:
       
     3 //
       
     4 //   http://labs.flog.nz.co/raytracer/
       
     5 //
       
     6 // It has been modified slightly by Google to work as a standalone
       
     7 // benchmark, but the all the computational code remains
       
     8 // untouched. This file also contains a copy of parts of the Prototype
       
     9 // JavaScript framework which is used by the ray tracer.
       
    10 
       
    11 // Variable used to hold a number that can be used to verify that
       
    12 // the scene was ray traced correctly.
       
    13 var checkNumber;
       
    14 
       
    15 
       
    16 // ------------------------------------------------------------------------
       
    17 // ------------------------------------------------------------------------
       
    18 
       
    19 // The following is a copy of parts of the Prototype JavaScript library:
       
    20 
       
    21 // Prototype JavaScript framework, version 1.5.0
       
    22 // (c) 2005-2007 Sam Stephenson
       
    23 //
       
    24 // Prototype is freely distributable under the terms of an MIT-style license.
       
    25 // For details, see the Prototype web site: http://prototype.conio.net/
       
    26 
       
    27 
       
    28 var Class = {
       
    29   create: function() {
       
    30     return function() {
       
    31       this.initialize.apply(this, arguments);
       
    32     }
       
    33   }
       
    34 };
       
    35 
       
    36 
       
    37 Object.extend = function(destination, source) {
       
    38   for (var property in source) {
       
    39     destination[property] = source[property];
       
    40   }
       
    41   return destination;
       
    42 };
       
    43 
       
    44 
       
    45 // ------------------------------------------------------------------------
       
    46 // ------------------------------------------------------------------------
       
    47 
       
    48 // The rest of this file is the actual ray tracer written by Adam
       
    49 // Burmister. It's a concatenation of the following files:
       
    50 //
       
    51 //   flog/color.js
       
    52 //   flog/light.js
       
    53 //   flog/vector.js
       
    54 //   flog/ray.js
       
    55 //   flog/scene.js
       
    56 //   flog/material/basematerial.js
       
    57 //   flog/material/solid.js
       
    58 //   flog/material/chessboard.js
       
    59 //   flog/shape/baseshape.js
       
    60 //   flog/shape/sphere.js
       
    61 //   flog/shape/plane.js
       
    62 //   flog/intersectioninfo.js
       
    63 //   flog/camera.js
       
    64 //   flog/background.js
       
    65 //   flog/engine.js
       
    66 
       
    67 
       
    68 /* Fake a Flog.* namespace */
       
    69 if(typeof(Flog) == 'undefined') var Flog = {};
       
    70 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
    71 
       
    72 Flog.RayTracer.Color = Class.create();
       
    73 
       
    74 Flog.RayTracer.Color.prototype = {
       
    75     red : 0.0,
       
    76     green : 0.0,
       
    77     blue : 0.0,
       
    78 
       
    79     initialize : function(r, g, b) {
       
    80         if(!r) r = 0.0;
       
    81         if(!g) g = 0.0;
       
    82         if(!b) b = 0.0;
       
    83 
       
    84         this.red = r;
       
    85         this.green = g;
       
    86         this.blue = b;
       
    87     },
       
    88 
       
    89     add : function(c1, c2){
       
    90         var result = new Flog.RayTracer.Color(0,0,0);
       
    91 
       
    92         result.red = c1.red + c2.red;
       
    93         result.green = c1.green + c2.green;
       
    94         result.blue = c1.blue + c2.blue;
       
    95 
       
    96         return result;
       
    97     },
       
    98 
       
    99     addScalar: function(c1, s){
       
   100         var result = new Flog.RayTracer.Color(0,0,0);
       
   101 
       
   102         result.red = c1.red + s;
       
   103         result.green = c1.green + s;
       
   104         result.blue = c1.blue + s;
       
   105 
       
   106         result.limit();
       
   107 
       
   108         return result;
       
   109     },
       
   110 
       
   111     subtract: function(c1, c2){
       
   112         var result = new Flog.RayTracer.Color(0,0,0);
       
   113 
       
   114         result.red = c1.red - c2.red;
       
   115         result.green = c1.green - c2.green;
       
   116         result.blue = c1.blue - c2.blue;
       
   117 
       
   118         return result;
       
   119     },
       
   120 
       
   121     multiply : function(c1, c2) {
       
   122         var result = new Flog.RayTracer.Color(0,0,0);
       
   123 
       
   124         result.red = c1.red * c2.red;
       
   125         result.green = c1.green * c2.green;
       
   126         result.blue = c1.blue * c2.blue;
       
   127 
       
   128         return result;
       
   129     },
       
   130 
       
   131     multiplyScalar : function(c1, f) {
       
   132         var result = new Flog.RayTracer.Color(0,0,0);
       
   133 
       
   134         result.red = c1.red * f;
       
   135         result.green = c1.green * f;
       
   136         result.blue = c1.blue * f;
       
   137 
       
   138         return result;
       
   139     },
       
   140 
       
   141     divideFactor : function(c1, f) {
       
   142         var result = new Flog.RayTracer.Color(0,0,0);
       
   143 
       
   144         result.red = c1.red / f;
       
   145         result.green = c1.green / f;
       
   146         result.blue = c1.blue / f;
       
   147 
       
   148         return result;
       
   149     },
       
   150 
       
   151     limit: function(){
       
   152         this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
       
   153         this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
       
   154         this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
       
   155     },
       
   156 
       
   157     distance : function(color) {
       
   158         var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
       
   159         return d;
       
   160     },
       
   161 
       
   162     blend: function(c1, c2, w){
       
   163         var result = new Flog.RayTracer.Color(0,0,0);
       
   164         result = Flog.RayTracer.Color.prototype.add(
       
   165                     Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
       
   166                     Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
       
   167                   );
       
   168         return result;
       
   169     },
       
   170 
       
   171     brightness : function() {
       
   172         var r = Math.floor(this.red*255);
       
   173         var g = Math.floor(this.green*255);
       
   174         var b = Math.floor(this.blue*255);
       
   175         return (r * 77 + g * 150 + b * 29) >> 8;
       
   176     },
       
   177 
       
   178     toString : function () {
       
   179         var r = Math.floor(this.red*255);
       
   180         var g = Math.floor(this.green*255);
       
   181         var b = Math.floor(this.blue*255);
       
   182 
       
   183         return "rgb("+ r +","+ g +","+ b +")";
       
   184     }
       
   185 }
       
   186 /* Fake a Flog.* namespace */
       
   187 if(typeof(Flog) == 'undefined') var Flog = {};
       
   188 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   189 
       
   190 Flog.RayTracer.Light = Class.create();
       
   191 
       
   192 Flog.RayTracer.Light.prototype = {
       
   193     position: null,
       
   194     color: null,
       
   195     intensity: 10.0,
       
   196 
       
   197     initialize : function(pos, color, intensity) {
       
   198         this.position = pos;
       
   199         this.color = color;
       
   200         this.intensity = (intensity ? intensity : 10.0);
       
   201     },
       
   202 
       
   203     getIntensity: function(distance){
       
   204         if(distance >= intensity) return 0;
       
   205 
       
   206         return Math.pow((intensity - distance) / strength, 0.2);
       
   207     },
       
   208 
       
   209     toString : function () {
       
   210         return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
       
   211     }
       
   212 }
       
   213 /* Fake a Flog.* namespace */
       
   214 if(typeof(Flog) == 'undefined') var Flog = {};
       
   215 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   216 
       
   217 Flog.RayTracer.Vector = Class.create();
       
   218 
       
   219 Flog.RayTracer.Vector.prototype = {
       
   220     x : 0.0,
       
   221     y : 0.0,
       
   222     z : 0.0,
       
   223 
       
   224     initialize : function(x, y, z) {
       
   225         this.x = (x ? x : 0);
       
   226         this.y = (y ? y : 0);
       
   227         this.z = (z ? z : 0);
       
   228     },
       
   229 
       
   230     copy: function(vector){
       
   231         this.x = vector.x;
       
   232         this.y = vector.y;
       
   233         this.z = vector.z;
       
   234     },
       
   235 
       
   236     normalize : function() {
       
   237         var m = this.magnitude();
       
   238         return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
       
   239     },
       
   240 
       
   241     magnitude : function() {
       
   242         return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
       
   243     },
       
   244 
       
   245     cross : function(w) {
       
   246         return new Flog.RayTracer.Vector(
       
   247                                             -this.z * w.y + this.y * w.z,
       
   248                                            this.z * w.x - this.x * w.z,
       
   249                                           -this.y * w.x + this.x * w.y);
       
   250     },
       
   251 
       
   252     dot : function(w) {
       
   253         return this.x * w.x + this.y * w.y + this.z * w.z;
       
   254     },
       
   255 
       
   256     add : function(v, w) {
       
   257         return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
       
   258     },
       
   259 
       
   260     subtract : function(v, w) {
       
   261         if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
       
   262         return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
       
   263     },
       
   264 
       
   265     multiplyVector : function(v, w) {
       
   266         return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
       
   267     },
       
   268 
       
   269     multiplyScalar : function(v, w) {
       
   270         return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
       
   271     },
       
   272 
       
   273     toString : function () {
       
   274         return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
       
   275     }
       
   276 }
       
   277 /* Fake a Flog.* namespace */
       
   278 if(typeof(Flog) == 'undefined') var Flog = {};
       
   279 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   280 
       
   281 Flog.RayTracer.Ray = Class.create();
       
   282 
       
   283 Flog.RayTracer.Ray.prototype = {
       
   284     position : null,
       
   285     direction : null,
       
   286     initialize : function(pos, dir) {
       
   287         this.position = pos;
       
   288         this.direction = dir;
       
   289     },
       
   290 
       
   291     toString : function () {
       
   292         return 'Ray [' + this.position + ',' + this.direction + ']';
       
   293     }
       
   294 }
       
   295 /* Fake a Flog.* namespace */
       
   296 if(typeof(Flog) == 'undefined') var Flog = {};
       
   297 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   298 
       
   299 Flog.RayTracer.Scene = Class.create();
       
   300 
       
   301 Flog.RayTracer.Scene.prototype = {
       
   302     camera : null,
       
   303     shapes : [],
       
   304     lights : [],
       
   305     background : null,
       
   306 
       
   307     initialize : function() {
       
   308         this.camera = new Flog.RayTracer.Camera(
       
   309             new Flog.RayTracer.Vector(0,0,-5),
       
   310             new Flog.RayTracer.Vector(0,0,1),
       
   311             new Flog.RayTracer.Vector(0,1,0)
       
   312         );
       
   313         this.shapes = new Array();
       
   314         this.lights = new Array();
       
   315         this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
       
   316     }
       
   317 }
       
   318 /* Fake a Flog.* namespace */
       
   319 if(typeof(Flog) == 'undefined') var Flog = {};
       
   320 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   321 if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
       
   322 
       
   323 Flog.RayTracer.Material.BaseMaterial = Class.create();
       
   324 
       
   325 Flog.RayTracer.Material.BaseMaterial.prototype = {
       
   326 
       
   327     gloss: 2.0,             // [0...infinity] 0 = matt
       
   328     transparency: 0.0,      // 0=opaque
       
   329     reflection: 0.0,        // [0...infinity] 0 = no reflection
       
   330     refraction: 0.50,
       
   331     hasTexture: false,
       
   332 
       
   333     initialize : function() {
       
   334 
       
   335     },
       
   336 
       
   337     getColor: function(u, v){
       
   338 
       
   339     },
       
   340 
       
   341     wrapUp: function(t){
       
   342         t = t % 2.0;
       
   343         if(t < -1) t += 2.0;
       
   344         if(t >= 1) t -= 2.0;
       
   345         return t;
       
   346     },
       
   347 
       
   348     toString : function () {
       
   349         return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
       
   350     }
       
   351 }
       
   352 /* Fake a Flog.* namespace */
       
   353 if(typeof(Flog) == 'undefined') var Flog = {};
       
   354 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   355 
       
   356 Flog.RayTracer.Material.Solid = Class.create();
       
   357 
       
   358 Flog.RayTracer.Material.Solid.prototype = Object.extend(
       
   359     new Flog.RayTracer.Material.BaseMaterial(), {
       
   360         initialize : function(color, reflection, refraction, transparency, gloss) {
       
   361             this.color = color;
       
   362             this.reflection = reflection;
       
   363             this.transparency = transparency;
       
   364             this.gloss = gloss;
       
   365             this.hasTexture = false;
       
   366         },
       
   367 
       
   368         getColor: function(u, v){
       
   369             return this.color;
       
   370         },
       
   371 
       
   372         toString : function () {
       
   373             return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
       
   374         }
       
   375     }
       
   376 );
       
   377 /* Fake a Flog.* namespace */
       
   378 if(typeof(Flog) == 'undefined') var Flog = {};
       
   379 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   380 
       
   381 Flog.RayTracer.Material.Chessboard = Class.create();
       
   382 
       
   383 Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
       
   384     new Flog.RayTracer.Material.BaseMaterial(), {
       
   385         colorEven: null,
       
   386         colorOdd: null,
       
   387         density: 0.5,
       
   388 
       
   389         initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
       
   390             this.colorEven = colorEven;
       
   391             this.colorOdd = colorOdd;
       
   392             this.reflection = reflection;
       
   393             this.transparency = transparency;
       
   394             this.gloss = gloss;
       
   395             this.density = density;
       
   396             this.hasTexture = true;
       
   397         },
       
   398 
       
   399         getColor: function(u, v){
       
   400             var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
       
   401 
       
   402             if(t < 0.0)
       
   403                 return this.colorEven;
       
   404             else
       
   405                 return this.colorOdd;
       
   406         },
       
   407 
       
   408         toString : function () {
       
   409             return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
       
   410         }
       
   411     }
       
   412 );
       
   413 /* Fake a Flog.* namespace */
       
   414 if(typeof(Flog) == 'undefined') var Flog = {};
       
   415 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   416 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
       
   417 
       
   418 Flog.RayTracer.Shape.BaseShape = Class.create();
       
   419 
       
   420 Flog.RayTracer.Shape.BaseShape.prototype = {
       
   421     position: null,
       
   422     material: null,
       
   423 
       
   424     initialize : function() {
       
   425         this.position = new Vector(0,0,0);
       
   426         this.material = new Flog.RayTracer.Material.SolidMaterial(
       
   427             new Flog.RayTracer.Color(1,0,1),
       
   428             0,
       
   429             0,
       
   430             0
       
   431         );
       
   432     },
       
   433 
       
   434     toString : function () {
       
   435         return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
       
   436     }
       
   437 }
       
   438 /* Fake a Flog.* namespace */
       
   439 if(typeof(Flog) == 'undefined') var Flog = {};
       
   440 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   441 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
       
   442 
       
   443 Flog.RayTracer.Shape.Sphere = Class.create();
       
   444 
       
   445 Flog.RayTracer.Shape.Sphere.prototype = {
       
   446     initialize : function(pos, radius, material) {
       
   447         this.radius = radius;
       
   448         this.position = pos;
       
   449         this.material = material;
       
   450     },
       
   451 
       
   452     intersect: function(ray){
       
   453         var info = new Flog.RayTracer.IntersectionInfo();
       
   454         info.shape = this;
       
   455 
       
   456         var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
       
   457 
       
   458         var B = dst.dot(ray.direction);
       
   459         var C = dst.dot(dst) - (this.radius * this.radius);
       
   460         var D = (B * B) - C;
       
   461 
       
   462         if(D > 0){ // intersection!
       
   463             info.isHit = true;
       
   464             info.distance = (-B) - Math.sqrt(D);
       
   465             info.position = Flog.RayTracer.Vector.prototype.add(
       
   466                                                 ray.position,
       
   467                                                 Flog.RayTracer.Vector.prototype.multiplyScalar(
       
   468                                                     ray.direction,
       
   469                                                     info.distance
       
   470                                                 )
       
   471                                             );
       
   472             info.normal = Flog.RayTracer.Vector.prototype.subtract(
       
   473                                             info.position,
       
   474                                             this.position
       
   475                                         ).normalize();
       
   476 
       
   477             info.color = this.material.getColor(0,0);
       
   478         } else {
       
   479             info.isHit = false;
       
   480         }
       
   481         return info;
       
   482     },
       
   483 
       
   484     toString : function () {
       
   485         return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
       
   486     }
       
   487 }
       
   488 /* Fake a Flog.* namespace */
       
   489 if(typeof(Flog) == 'undefined') var Flog = {};
       
   490 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   491 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
       
   492 
       
   493 Flog.RayTracer.Shape.Plane = Class.create();
       
   494 
       
   495 Flog.RayTracer.Shape.Plane.prototype = {
       
   496     d: 0.0,
       
   497 
       
   498     initialize : function(pos, d, material) {
       
   499         this.position = pos;
       
   500         this.d = d;
       
   501         this.material = material;
       
   502     },
       
   503 
       
   504     intersect: function(ray){
       
   505         var info = new Flog.RayTracer.IntersectionInfo();
       
   506 
       
   507         var Vd = this.position.dot(ray.direction);
       
   508         if(Vd == 0) return info; // no intersection
       
   509 
       
   510         var t = -(this.position.dot(ray.position) + this.d) / Vd;
       
   511         if(t <= 0) return info;
       
   512 
       
   513         info.shape = this;
       
   514         info.isHit = true;
       
   515         info.position = Flog.RayTracer.Vector.prototype.add(
       
   516                                             ray.position,
       
   517                                             Flog.RayTracer.Vector.prototype.multiplyScalar(
       
   518                                                 ray.direction,
       
   519                                                 t
       
   520                                             )
       
   521                                         );
       
   522         info.normal = this.position;
       
   523         info.distance = t;
       
   524 
       
   525         if(this.material.hasTexture){
       
   526             var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
       
   527             var vV = vU.cross(this.position);
       
   528             var u = info.position.dot(vU);
       
   529             var v = info.position.dot(vV);
       
   530             info.color = this.material.getColor(u,v);
       
   531         } else {
       
   532             info.color = this.material.getColor(0,0);
       
   533         }
       
   534 
       
   535         return info;
       
   536     },
       
   537 
       
   538     toString : function () {
       
   539         return 'Plane [' + this.position + ', d=' + this.d + ']';
       
   540     }
       
   541 }
       
   542 /* Fake a Flog.* namespace */
       
   543 if(typeof(Flog) == 'undefined') var Flog = {};
       
   544 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   545 
       
   546 Flog.RayTracer.IntersectionInfo = Class.create();
       
   547 
       
   548 Flog.RayTracer.IntersectionInfo.prototype = {
       
   549     isHit: false,
       
   550     hitCount: 0,
       
   551     shape: null,
       
   552     position: null,
       
   553     normal: null,
       
   554     color: null,
       
   555     distance: null,
       
   556 
       
   557     initialize : function() {
       
   558         this.color = new Flog.RayTracer.Color(0,0,0);
       
   559     },
       
   560 
       
   561     toString : function () {
       
   562         return 'Intersection [' + this.position + ']';
       
   563     }
       
   564 }
       
   565 /* Fake a Flog.* namespace */
       
   566 if(typeof(Flog) == 'undefined') var Flog = {};
       
   567 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   568 
       
   569 Flog.RayTracer.Camera = Class.create();
       
   570 
       
   571 Flog.RayTracer.Camera.prototype = {
       
   572     position: null,
       
   573     lookAt: null,
       
   574     equator: null,
       
   575     up: null,
       
   576     screen: null,
       
   577 
       
   578     initialize : function(pos, lookAt, up) {
       
   579         this.position = pos;
       
   580         this.lookAt = lookAt;
       
   581         this.up = up;
       
   582         this.equator = lookAt.normalize().cross(this.up);
       
   583         this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
       
   584     },
       
   585 
       
   586     getRay: function(vx, vy){
       
   587         var pos = Flog.RayTracer.Vector.prototype.subtract(
       
   588             this.screen,
       
   589             Flog.RayTracer.Vector.prototype.subtract(
       
   590                 Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
       
   591                 Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
       
   592             )
       
   593         );
       
   594         pos.y = pos.y * -1;
       
   595         var dir = Flog.RayTracer.Vector.prototype.subtract(
       
   596             pos,
       
   597             this.position
       
   598         );
       
   599 
       
   600         var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
       
   601 
       
   602         return ray;
       
   603     },
       
   604 
       
   605     toString : function () {
       
   606         return 'Ray []';
       
   607     }
       
   608 }
       
   609 /* Fake a Flog.* namespace */
       
   610 if(typeof(Flog) == 'undefined') var Flog = {};
       
   611 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   612 
       
   613 Flog.RayTracer.Background = Class.create();
       
   614 
       
   615 Flog.RayTracer.Background.prototype = {
       
   616     color : null,
       
   617     ambience : 0.0,
       
   618 
       
   619     initialize : function(color, ambience) {
       
   620         this.color = color;
       
   621         this.ambience = ambience;
       
   622     }
       
   623 }
       
   624 /* Fake a Flog.* namespace */
       
   625 if(typeof(Flog) == 'undefined') var Flog = {};
       
   626 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
       
   627 
       
   628 Flog.RayTracer.Engine = Class.create();
       
   629 
       
   630 Flog.RayTracer.Engine.prototype = {
       
   631     canvas: null, /* 2d context we can render to */
       
   632 
       
   633     initialize: function(options){
       
   634         this.options = Object.extend({
       
   635                 canvasHeight: 100,
       
   636                 canvasWidth: 100,
       
   637                 pixelWidth: 2,
       
   638                 pixelHeight: 2,
       
   639                 renderDiffuse: false,
       
   640                 renderShadows: false,
       
   641                 renderHighlights: false,
       
   642                 renderReflections: false,
       
   643                 rayDepth: 2
       
   644             }, options || {});
       
   645 
       
   646         this.options.canvasHeight /= this.options.pixelHeight;
       
   647         this.options.canvasWidth /= this.options.pixelWidth;
       
   648 
       
   649         /* TODO: dynamically include other scripts */
       
   650     },
       
   651 
       
   652     setPixel: function(x, y, color){
       
   653         var pxW, pxH;
       
   654         pxW = this.options.pixelWidth;
       
   655         pxH = this.options.pixelHeight;
       
   656 
       
   657         if (this.canvas) {
       
   658           this.canvas.fillStyle = color.toString();
       
   659           this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
       
   660         } else {
       
   661           if (x ===  y) {
       
   662             checkNumber += color.brightness();
       
   663           }
       
   664           // print(x * pxW, y * pxH, pxW, pxH);
       
   665         }
       
   666     },
       
   667 
       
   668     renderScene: function(scene, canvas){
       
   669         checkNumber = 0;
       
   670         /* Get canvas */
       
   671         if (canvas) {
       
   672           this.canvas = canvas.getContext("2d");
       
   673         } else {
       
   674           this.canvas = null;
       
   675         }
       
   676 
       
   677         var canvasHeight = this.options.canvasHeight;
       
   678         var canvasWidth = this.options.canvasWidth;
       
   679 
       
   680         for(var y=0; y < canvasHeight; y++){
       
   681             for(var x=0; x < canvasWidth; x++){
       
   682                 var yp = y * 1.0 / canvasHeight * 2 - 1;
       
   683           		var xp = x * 1.0 / canvasWidth * 2 - 1;
       
   684 
       
   685           		var ray = scene.camera.getRay(xp, yp);
       
   686 
       
   687           		var color = this.getPixelColor(ray, scene);
       
   688 
       
   689             	this.setPixel(x, y, color);
       
   690             }
       
   691         }
       
   692         if (checkNumber !== 2321) {
       
   693           throw new Error("Scene rendered incorrectly");
       
   694         }
       
   695     },
       
   696 
       
   697     getPixelColor: function(ray, scene){
       
   698         var info = this.testIntersection(ray, scene, null);
       
   699         if(info.isHit){
       
   700             var color = this.rayTrace(info, ray, scene, 0);
       
   701             return color;
       
   702         }
       
   703         return scene.background.color;
       
   704     },
       
   705 
       
   706     testIntersection: function(ray, scene, exclude){
       
   707         var hits = 0;
       
   708         var best = new Flog.RayTracer.IntersectionInfo();
       
   709         best.distance = 2000;
       
   710 
       
   711         for(var i=0; i<scene.shapes.length; i++){
       
   712             var shape = scene.shapes[i];
       
   713 
       
   714             if(shape != exclude){
       
   715                 var info = shape.intersect(ray);
       
   716                 if(info.isHit && info.distance >= 0 && info.distance < best.distance){
       
   717                     best = info;
       
   718                     hits++;
       
   719                 }
       
   720             }
       
   721         }
       
   722         best.hitCount = hits;
       
   723         return best;
       
   724     },
       
   725 
       
   726     getReflectionRay: function(P,N,V){
       
   727         var c1 = -N.dot(V);
       
   728         var R1 = Flog.RayTracer.Vector.prototype.add(
       
   729             Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
       
   730             V
       
   731         );
       
   732         return new Flog.RayTracer.Ray(P, R1);
       
   733     },
       
   734 
       
   735     rayTrace: function(info, ray, scene, depth){
       
   736         // Calc ambient
       
   737         var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
       
   738         var oldColor = color;
       
   739         var shininess = Math.pow(10, info.shape.material.gloss + 1);
       
   740 
       
   741         for(var i=0; i<scene.lights.length; i++){
       
   742             var light = scene.lights[i];
       
   743 
       
   744             // Calc diffuse lighting
       
   745             var v = Flog.RayTracer.Vector.prototype.subtract(
       
   746                                 light.position,
       
   747                                 info.position
       
   748                             ).normalize();
       
   749 
       
   750             if(this.options.renderDiffuse){
       
   751                 var L = v.dot(info.normal);
       
   752                 if(L > 0.0){
       
   753                     color = Flog.RayTracer.Color.prototype.add(
       
   754                                         color,
       
   755                                         Flog.RayTracer.Color.prototype.multiply(
       
   756                                             info.color,
       
   757                                             Flog.RayTracer.Color.prototype.multiplyScalar(
       
   758                                                 light.color,
       
   759                                                 L
       
   760                                             )
       
   761                                         )
       
   762                                     );
       
   763                 }
       
   764             }
       
   765 
       
   766             // The greater the depth the more accurate the colours, but
       
   767             // this is exponentially (!) expensive
       
   768             if(depth <= this.options.rayDepth){
       
   769           // calculate reflection ray
       
   770           if(this.options.renderReflections && info.shape.material.reflection > 0)
       
   771           {
       
   772               var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
       
   773               var refl = this.testIntersection(reflectionRay, scene, info.shape);
       
   774 
       
   775               if (refl.isHit && refl.distance > 0){
       
   776                   refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
       
   777               } else {
       
   778                   refl.color = scene.background.color;
       
   779                         }
       
   780 
       
   781                   color = Flog.RayTracer.Color.prototype.blend(
       
   782                     color,
       
   783                     refl.color,
       
   784                     info.shape.material.reflection
       
   785                   );
       
   786           }
       
   787 
       
   788                 // Refraction
       
   789                 /* TODO */
       
   790             }
       
   791 
       
   792             /* Render shadows and highlights */
       
   793 
       
   794             var shadowInfo = new Flog.RayTracer.IntersectionInfo();
       
   795 
       
   796             if(this.options.renderShadows){
       
   797                 var shadowRay = new Flog.RayTracer.Ray(info.position, v);
       
   798 
       
   799                 shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
       
   800                 if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
       
   801                     var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
       
   802                     var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
       
   803                     color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
       
   804                 }
       
   805             }
       
   806 
       
   807       // Phong specular highlights
       
   808       if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
       
   809         var Lv = Flog.RayTracer.Vector.prototype.subtract(
       
   810                             info.shape.position,
       
   811                             light.position
       
   812                         ).normalize();
       
   813 
       
   814         var E = Flog.RayTracer.Vector.prototype.subtract(
       
   815                             scene.camera.position,
       
   816                             info.shape.position
       
   817                         ).normalize();
       
   818 
       
   819         var H = Flog.RayTracer.Vector.prototype.subtract(
       
   820                             E,
       
   821                             Lv
       
   822                         ).normalize();
       
   823 
       
   824         var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
       
   825         color = Flog.RayTracer.Color.prototype.add(
       
   826                             Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
       
   827                             color
       
   828                         );
       
   829       }
       
   830         }
       
   831         color.limit();
       
   832         return color;
       
   833     }
       
   834 };
       
   835 
       
   836 
       
   837 function renderScene(){
       
   838     var scene = new Flog.RayTracer.Scene();
       
   839 
       
   840     scene.camera = new Flog.RayTracer.Camera(
       
   841                         new Flog.RayTracer.Vector(0, 0, -15),
       
   842                         new Flog.RayTracer.Vector(-0.2, 0, 5),
       
   843                         new Flog.RayTracer.Vector(0, 1, 0)
       
   844                     );
       
   845 
       
   846     scene.background = new Flog.RayTracer.Background(
       
   847                                 new Flog.RayTracer.Color(0.5, 0.5, 0.5),
       
   848                                 0.4
       
   849                             );
       
   850 
       
   851     var sphere = new Flog.RayTracer.Shape.Sphere(
       
   852         new Flog.RayTracer.Vector(-1.5, 1.5, 2),
       
   853         1.5,
       
   854         new Flog.RayTracer.Material.Solid(
       
   855             new Flog.RayTracer.Color(0,0.5,0.5),
       
   856             0.3,
       
   857             0.0,
       
   858             0.0,
       
   859             2.0
       
   860         )
       
   861     );
       
   862 
       
   863     var sphere1 = new Flog.RayTracer.Shape.Sphere(
       
   864         new Flog.RayTracer.Vector(1, 0.25, 1),
       
   865         0.5,
       
   866         new Flog.RayTracer.Material.Solid(
       
   867             new Flog.RayTracer.Color(0.9,0.9,0.9),
       
   868             0.1,
       
   869             0.0,
       
   870             0.0,
       
   871             1.5
       
   872         )
       
   873     );
       
   874 
       
   875     var plane = new Flog.RayTracer.Shape.Plane(
       
   876                                 new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
       
   877                                 1.2,
       
   878                                 new Flog.RayTracer.Material.Chessboard(
       
   879                                     new Flog.RayTracer.Color(1,1,1),
       
   880                                     new Flog.RayTracer.Color(0,0,0),
       
   881                                     0.2,
       
   882                                     0.0,
       
   883                                     1.0,
       
   884                                     0.7
       
   885                                 )
       
   886                             );
       
   887 
       
   888     scene.shapes.push(plane);
       
   889     scene.shapes.push(sphere);
       
   890     scene.shapes.push(sphere1);
       
   891 
       
   892     var light = new Flog.RayTracer.Light(
       
   893         new Flog.RayTracer.Vector(5, 10, -1),
       
   894         new Flog.RayTracer.Color(0.8, 0.8, 0.8)
       
   895     );
       
   896 
       
   897     var light1 = new Flog.RayTracer.Light(
       
   898         new Flog.RayTracer.Vector(-3, 5, -15),
       
   899         new Flog.RayTracer.Color(0.8, 0.8, 0.8),
       
   900         100
       
   901     );
       
   902 
       
   903     scene.lights.push(light);
       
   904     scene.lights.push(light1);
       
   905 
       
   906     var imageWidth = 100; // $F('imageWidth');
       
   907     var imageHeight = 100; // $F('imageHeight');
       
   908     var pixelSize = "5,5".split(','); //  $F('pixelSize').split(',');
       
   909     var renderDiffuse = true; // $F('renderDiffuse');
       
   910     var renderShadows = true; // $F('renderShadows');
       
   911     var renderHighlights = true; // $F('renderHighlights');
       
   912     var renderReflections = true; // $F('renderReflections');
       
   913     var rayDepth = 2;//$F('rayDepth');
       
   914 
       
   915     var raytracer = new Flog.RayTracer.Engine(
       
   916         {
       
   917             canvasWidth: imageWidth,
       
   918             canvasHeight: imageHeight,
       
   919             pixelWidth: pixelSize[0],
       
   920             pixelHeight: pixelSize[1],
       
   921             "renderDiffuse": renderDiffuse,
       
   922             "renderHighlights": renderHighlights,
       
   923             "renderShadows": renderShadows,
       
   924             "renderReflections": renderReflections,
       
   925             "rayDepth": rayDepth
       
   926         }
       
   927     );
       
   928 
       
   929     raytracer.renderScene(scene, null, 0);
       
   930 }
       
   931 
       
   932 for (var i = 0; i < 6; ++i)
       
   933   renderScene();