Null check in traverseNodesForSerialization
[WebKit-https.git] / v8-v4 / v8-raytrace.js
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();