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