1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/devtools/jint/sunspider/3d-raytrace.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,466 @@ 1.4 +/* 1.5 + * Copyright (C) 2007 Apple Inc. All rights reserved. 1.6 + * 1.7 + * Redistribution and use in source and binary forms, with or without 1.8 + * modification, are permitted provided that the following conditions 1.9 + * are met: 1.10 + * 1. Redistributions of source code must retain the above copyright 1.11 + * notice, this list of conditions and the following disclaimer. 1.12 + * 2. Redistributions in binary form must reproduce the above copyright 1.13 + * notice, this list of conditions and the following disclaimer in the 1.14 + * documentation and/or other materials provided with the distribution. 1.15 + * 1.16 + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 1.17 + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.18 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 1.19 + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 1.20 + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 1.21 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 1.22 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 1.23 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 1.24 + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.25 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.26 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.27 + */ 1.28 + 1.29 +function createVector(x,y,z) { 1.30 + return new Array(x,y,z); 1.31 +} 1.32 + 1.33 +function sqrLengthVector(self) { 1.34 + return self[0] * self[0] + self[1] * self[1] + self[2] * self[2]; 1.35 +} 1.36 + 1.37 +function lengthVector(self) { 1.38 + return Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]); 1.39 +} 1.40 + 1.41 +function addVector(self, v) { 1.42 + self[0] += v[0]; 1.43 + self[1] += v[1]; 1.44 + self[2] += v[2]; 1.45 + return self; 1.46 +} 1.47 + 1.48 +function subVector(self, v) { 1.49 + self[0] -= v[0]; 1.50 + self[1] -= v[1]; 1.51 + self[2] -= v[2]; 1.52 + return self; 1.53 +} 1.54 + 1.55 +function scaleVector(self, scale) { 1.56 + self[0] *= scale; 1.57 + self[1] *= scale; 1.58 + self[2] *= scale; 1.59 + return self; 1.60 +} 1.61 + 1.62 +function normaliseVector(self) { 1.63 + var len = Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]); 1.64 + self[0] /= len; 1.65 + self[1] /= len; 1.66 + self[2] /= len; 1.67 + return self; 1.68 +} 1.69 + 1.70 +function add(v1, v2) { 1.71 + return new Array(v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]); 1.72 +} 1.73 + 1.74 +function sub(v1, v2) { 1.75 + return new Array(v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]); 1.76 +} 1.77 + 1.78 +function scalev(v1, v2) { 1.79 + return new Array(v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2]); 1.80 +} 1.81 + 1.82 +function dot(v1, v2) { 1.83 + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; 1.84 +} 1.85 + 1.86 +function scale(v, scale) { 1.87 + return [v[0] * scale, v[1] * scale, v[2] * scale]; 1.88 +} 1.89 + 1.90 +function cross(v1, v2) { 1.91 + return [v1[1] * v2[2] - v1[2] * v2[1], 1.92 + v1[2] * v2[0] - v1[0] * v2[2], 1.93 + v1[0] * v2[1] - v1[1] * v2[0]]; 1.94 + 1.95 +} 1.96 + 1.97 +function normalise(v) { 1.98 + var len = lengthVector(v); 1.99 + return [v[0] / len, v[1] / len, v[2] / len]; 1.100 +} 1.101 + 1.102 +function transformMatrix(self, v) { 1.103 + var vals = self; 1.104 + var x = vals[0] * v[0] + vals[1] * v[1] + vals[2] * v[2] + vals[3]; 1.105 + var y = vals[4] * v[0] + vals[5] * v[1] + vals[6] * v[2] + vals[7]; 1.106 + var z = vals[8] * v[0] + vals[9] * v[1] + vals[10] * v[2] + vals[11]; 1.107 + return [x, y, z]; 1.108 +} 1.109 + 1.110 +function invertMatrix(self) { 1.111 + var temp = new Array(16); 1.112 + var tx = -self[3]; 1.113 + var ty = -self[7]; 1.114 + var tz = -self[11]; 1.115 + /* BEGIN LOOP */ 1.116 + for (h = 0; h < 3; h++) { 1.117 + /* BEGIN LOOP */ 1.118 + for (v = 0; v < 3; v++) 1.119 + temp[h + v * 4] = self[v + h * 4]; 1.120 + /* END LOOP */ 1.121 + } 1.122 + /* END LOOP */ 1.123 + /* BEGIN LOOP */ 1.124 + for (i = 0; i < 11; i++) 1.125 + self[i] = temp[i]; 1.126 + /* END LOOP */ 1.127 + self[3] = tx * self[0] + ty * self[1] + tz * self[2]; 1.128 + self[7] = tx * self[4] + ty * self[5] + tz * self[6]; 1.129 + self[11] = tx * self[8] + ty * self[9] + tz * self[10]; 1.130 + return self; 1.131 +} 1.132 + 1.133 + 1.134 +// Triangle intersection using barycentric coord method 1.135 +function Triangle(p1, p2, p3) { 1.136 + var edge1 = sub(p3, p1); 1.137 + var edge2 = sub(p2, p1); 1.138 + var normal = cross(edge1, edge2); 1.139 + if (Math.abs(normal[0]) > Math.abs(normal[1])) 1.140 + if (Math.abs(normal[0]) > Math.abs(normal[2])) 1.141 + this.axis = 0; 1.142 + else 1.143 + this.axis = 2; 1.144 + else 1.145 + if (Math.abs(normal[1]) > Math.abs(normal[2])) 1.146 + this.axis = 1; 1.147 + else 1.148 + this.axis = 2; 1.149 + var u = (this.axis + 1) % 3; 1.150 + var v = (this.axis + 2) % 3; 1.151 + var u1 = edge1[u]; 1.152 + var v1 = edge1[v]; 1.153 + 1.154 + var u2 = edge2[u]; 1.155 + var v2 = edge2[v]; 1.156 + this.normal = normalise(normal); 1.157 + this.nu = normal[u] / normal[this.axis]; 1.158 + this.nv = normal[v] / normal[this.axis]; 1.159 + this.nd = dot(normal, p1) / normal[this.axis]; 1.160 + var det = u1 * v2 - v1 * u2; 1.161 + this.eu = p1[u]; 1.162 + this.ev = p1[v]; 1.163 + this.nu1 = u1 / det; 1.164 + this.nv1 = -v1 / det; 1.165 + this.nu2 = v2 / det; 1.166 + this.nv2 = -u2 / det; 1.167 + this.material = [0.7, 0.7, 0.7]; 1.168 +} 1.169 + 1.170 +Triangle.prototype.intersect = function(orig, dir, near, far) { 1.171 + var u = (this.axis + 1) % 3; 1.172 + var v = (this.axis + 2) % 3; 1.173 + var d = dir[this.axis] + this.nu * dir[u] + this.nv * dir[v]; 1.174 + var t = (this.nd - orig[this.axis] - this.nu * orig[u] - this.nv * orig[v]) / d; 1.175 + if (t < near || t > far) 1.176 + return null; 1.177 + var Pu = orig[u] + t * dir[u] - this.eu; 1.178 + var Pv = orig[v] + t * dir[v] - this.ev; 1.179 + var a2 = Pv * this.nu1 + Pu * this.nv1; 1.180 + if (a2 < 0) 1.181 + return null; 1.182 + var a3 = Pu * this.nu2 + Pv * this.nv2; 1.183 + if (a3 < 0) 1.184 + return null; 1.185 + 1.186 + if ((a2 + a3) > 1) 1.187 + return null; 1.188 + return t; 1.189 +} 1.190 + 1.191 +function Scene(a_triangles) { 1.192 + this.triangles = a_triangles; 1.193 + this.lights = []; 1.194 + this.ambient = [0,0,0]; 1.195 + this.background = [0.8,0.8,1]; 1.196 +} 1.197 +var zero = new Array(0,0,0); 1.198 + 1.199 +Scene.prototype.intersect = function(origin, dir, near, far) { 1.200 + var closest = null; 1.201 + /* BEGIN LOOP */ 1.202 + for (i = 0; i < this.triangles.length; i++) { 1.203 + var triangle = this.triangles[i]; 1.204 + var d = triangle.intersect(origin, dir, near, far); 1.205 + if (d == null || d > far || d < near) 1.206 + continue; 1.207 + far = d; 1.208 + closest = triangle; 1.209 + } 1.210 + /* END LOOP */ 1.211 + 1.212 + if (!closest) 1.213 + return [this.background[0],this.background[1],this.background[2]]; 1.214 + 1.215 + var normal = closest.normal; 1.216 + var hit = add(origin, scale(dir, far)); 1.217 + if (dot(dir, normal) > 0) 1.218 + normal = [-normal[0], -normal[1], -normal[2]]; 1.219 + 1.220 + var colour = null; 1.221 + if (closest.shader) { 1.222 + colour = closest.shader(closest, hit, dir); 1.223 + } else { 1.224 + colour = closest.material; 1.225 + } 1.226 + 1.227 + // do reflection 1.228 + var reflected = null; 1.229 + if (colour.reflection > 0.001) { 1.230 + var reflection = addVector(scale(normal, -2*dot(dir, normal)), dir); 1.231 + reflected = this.intersect(hit, reflection, 0.0001, 1000000); 1.232 + if (colour.reflection >= 0.999999) 1.233 + return reflected; 1.234 + } 1.235 + 1.236 + var l = [this.ambient[0], this.ambient[1], this.ambient[2]]; 1.237 + /* BEGIN LOOP */ 1.238 + for (var i = 0; i < this.lights.length; i++) { 1.239 + var light = this.lights[i]; 1.240 + var toLight = sub(light, hit); 1.241 + var distance = lengthVector(toLight); 1.242 + scaleVector(toLight, 1.0/distance); 1.243 + distance -= 0.0001; 1.244 + if (this.blocked(hit, toLight, distance)) 1.245 + continue; 1.246 + var nl = dot(normal, toLight); 1.247 + if (nl > 0) 1.248 + addVector(l, scale(light.colour, nl)); 1.249 + } 1.250 + /* END LOOP */ 1.251 + l = scalev(l, colour); 1.252 + if (reflected) { 1.253 + l = addVector(scaleVector(l, 1 - colour.reflection), scaleVector(reflected, colour.reflection)); 1.254 + } 1.255 + return l; 1.256 +} 1.257 + 1.258 +Scene.prototype.blocked = function(O, D, far) { 1.259 + var near = 0.0001; 1.260 + var closest = null; 1.261 + /* BEGIN LOOP */ 1.262 + for (i = 0; i < this.triangles.length; i++) { 1.263 + var triangle = this.triangles[i]; 1.264 + var d = triangle.intersect(O, D, near, far); 1.265 + if (d == null || d > far || d < near) 1.266 + continue; 1.267 + return true; 1.268 + } 1.269 + /* END LOOP */ 1.270 + 1.271 + return false; 1.272 +} 1.273 + 1.274 + 1.275 +// this camera code is from notes i made ages ago, it is from *somewhere* -- i cannot remember where 1.276 +// that somewhere is 1.277 +function Camera(origin, lookat, up) { 1.278 + var zaxis = normaliseVector(subVector(lookat, origin)); 1.279 + var xaxis = normaliseVector(cross(up, zaxis)); 1.280 + var yaxis = normaliseVector(cross(xaxis, subVector([0,0,0], zaxis))); 1.281 + var m = new Array(16); 1.282 + m[0] = xaxis[0]; m[1] = xaxis[1]; m[2] = xaxis[2]; 1.283 + m[4] = yaxis[0]; m[5] = yaxis[1]; m[6] = yaxis[2]; 1.284 + m[8] = zaxis[0]; m[9] = zaxis[1]; m[10] = zaxis[2]; 1.285 + invertMatrix(m); 1.286 + m[3] = 0; m[7] = 0; m[11] = 0; 1.287 + this.origin = origin; 1.288 + this.directions = new Array(4); 1.289 + this.directions[0] = normalise([-0.7, 0.7, 1]); 1.290 + this.directions[1] = normalise([ 0.7, 0.7, 1]); 1.291 + this.directions[2] = normalise([ 0.7, -0.7, 1]); 1.292 + this.directions[3] = normalise([-0.7, -0.7, 1]); 1.293 + this.directions[0] = transformMatrix(m, this.directions[0]); 1.294 + this.directions[1] = transformMatrix(m, this.directions[1]); 1.295 + this.directions[2] = transformMatrix(m, this.directions[2]); 1.296 + this.directions[3] = transformMatrix(m, this.directions[3]); 1.297 +} 1.298 + 1.299 +Camera.prototype.generateRayPair = function(y) { 1.300 + rays = new Array(new Object(), new Object()); 1.301 + rays[0].origin = this.origin; 1.302 + rays[1].origin = this.origin; 1.303 + rays[0].dir = addVector(scale(this.directions[0], y), scale(this.directions[3], 1 - y)); 1.304 + rays[1].dir = addVector(scale(this.directions[1], y), scale(this.directions[2], 1 - y)); 1.305 + return rays; 1.306 +} 1.307 + 1.308 +function renderRows(camera, scene, pixels, width, height, starty, stopy) { 1.309 + /* BEGIN LOOP */ 1.310 + for (var y = starty; y < stopy; y++) { 1.311 + var rays = camera.generateRayPair(y / height); 1.312 + /* BEGIN LOOP */ 1.313 + for (var x = 0; x < width; x++) { 1.314 + var xp = x / width; 1.315 + var origin = addVector(scale(rays[0].origin, xp), scale(rays[1].origin, 1 - xp)); 1.316 + var dir = normaliseVector(addVector(scale(rays[0].dir, xp), scale(rays[1].dir, 1 - xp))); 1.317 + var l = scene.intersect(origin, dir); 1.318 + pixels[y][x] = l; 1.319 + } 1.320 + /* END LOOP */ 1.321 + } 1.322 + /* END LOOP */ 1.323 +} 1.324 + 1.325 +Camera.prototype.render = function(scene, pixels, width, height) { 1.326 + var cam = this; 1.327 + var row = 0; 1.328 + renderRows(cam, scene, pixels, width, height, 0, height); 1.329 +} 1.330 + 1.331 + 1.332 + 1.333 +function raytraceScene() 1.334 +{ 1.335 + var startDate = new Date().getTime(); 1.336 + var numTriangles = 2 * 6; 1.337 + var triangles = new Array();//numTriangles); 1.338 + var tfl = createVector(-10, 10, -10); 1.339 + var tfr = createVector( 10, 10, -10); 1.340 + var tbl = createVector(-10, 10, 10); 1.341 + var tbr = createVector( 10, 10, 10); 1.342 + var bfl = createVector(-10, -10, -10); 1.343 + var bfr = createVector( 10, -10, -10); 1.344 + var bbl = createVector(-10, -10, 10); 1.345 + var bbr = createVector( 10, -10, 10); 1.346 + 1.347 + // cube!!! 1.348 + // front 1.349 + var i = 0; 1.350 + 1.351 + triangles[i++] = new Triangle(tfl, tfr, bfr); 1.352 + triangles[i++] = new Triangle(tfl, bfr, bfl); 1.353 + // back 1.354 + triangles[i++] = new Triangle(tbl, tbr, bbr); 1.355 + triangles[i++] = new Triangle(tbl, bbr, bbl); 1.356 + // triangles[i-1].material = [0.7,0.2,0.2]; 1.357 + // triangles[i-1].material.reflection = 0.8; 1.358 + // left 1.359 + triangles[i++] = new Triangle(tbl, tfl, bbl); 1.360 + // triangles[i-1].reflection = 0.6; 1.361 + triangles[i++] = new Triangle(tfl, bfl, bbl); 1.362 + // triangles[i-1].reflection = 0.6; 1.363 + // right 1.364 + triangles[i++] = new Triangle(tbr, tfr, bbr); 1.365 + triangles[i++] = new Triangle(tfr, bfr, bbr); 1.366 + // top 1.367 + triangles[i++] = new Triangle(tbl, tbr, tfr); 1.368 + triangles[i++] = new Triangle(tbl, tfr, tfl); 1.369 + // bottom 1.370 + triangles[i++] = new Triangle(bbl, bbr, bfr); 1.371 + triangles[i++] = new Triangle(bbl, bfr, bfl); 1.372 + 1.373 + //Floor!!!! 1.374 + var green = createVector(0.0, 0.4, 0.0); 1.375 + var grey = createVector(0.4, 0.4, 0.4); 1.376 + grey.reflection = 1.0; 1.377 + var floorShader = function(tri, pos, view) { 1.378 + var x = ((pos[0]/32) % 2 + 2) % 2; 1.379 + var z = ((pos[2]/32 + 0.3) % 2 + 2) % 2; 1.380 + if (x < 1 != z < 1) { 1.381 + //in the real world we use the fresnel term... 1.382 + // var angle = 1-dot(view, tri.normal); 1.383 + // angle *= angle; 1.384 + // angle *= angle; 1.385 + // angle *= angle; 1.386 + //grey.reflection = angle; 1.387 + return grey; 1.388 + } else 1.389 + return green; 1.390 + } 1.391 + var ffl = createVector(-1000, -30, -1000); 1.392 + var ffr = createVector( 1000, -30, -1000); 1.393 + var fbl = createVector(-1000, -30, 1000); 1.394 + var fbr = createVector( 1000, -30, 1000); 1.395 + triangles[i++] = new Triangle(fbl, fbr, ffr); 1.396 + triangles[i-1].shader = floorShader; 1.397 + triangles[i++] = new Triangle(fbl, ffr, ffl); 1.398 + triangles[i-1].shader = floorShader; 1.399 + 1.400 + var _scene = new Scene(triangles); 1.401 + _scene.lights[0] = createVector(20, 38, -22); 1.402 + _scene.lights[0].colour = createVector(0.7, 0.3, 0.3); 1.403 + _scene.lights[1] = createVector(-23, 40, 17); 1.404 + _scene.lights[1].colour = createVector(0.7, 0.3, 0.3); 1.405 + _scene.lights[2] = createVector(23, 20, 17); 1.406 + _scene.lights[2].colour = createVector(0.7, 0.7, 0.7); 1.407 + _scene.ambient = createVector(0.1, 0.1, 0.1); 1.408 + // _scene.background = createVector(0.7, 0.7, 1.0); 1.409 + 1.410 + var size = 30; 1.411 + var pixels = new Array(); 1.412 + /* BEGIN LOOP */ 1.413 + for (var y = 0; y < size; y++) { 1.414 + pixels[y] = new Array(); 1.415 + /* BEGIN LOOP */ 1.416 + for (var x = 0; x < size; x++) { 1.417 + pixels[y][x] = 0; 1.418 + } 1.419 + /* END LOOP */ 1.420 + } 1.421 + /* END LOOP */ 1.422 + 1.423 + var _camera = new Camera(createVector(-40, 40, 40), createVector(0, 0, 0), createVector(0, 1, 0)); 1.424 + _camera.render(_scene, pixels, size, size); 1.425 + 1.426 + return pixels; 1.427 +} 1.428 + 1.429 +function arrayToCanvasCommands(pixels) 1.430 +{ 1.431 + var s = '<canvas id="renderCanvas" width="30px" height="30px"></canvas><scr' + 'ipt>\nvar pixels = ['; 1.432 + var size = 30; 1.433 + /* BEGIN LOOP */ 1.434 + for (var y = 0; y < size; y++) { 1.435 + s += "["; 1.436 + /* BEGIN LOOP */ 1.437 + for (var x = 0; x < size; x++) { 1.438 + s += "[" + pixels[y][x] + "],"; 1.439 + } 1.440 + /* END LOOP */ 1.441 + s+= "],"; 1.442 + } 1.443 + /* END LOOP */ 1.444 + s += '];\n var canvas = document.getElementById("renderCanvas").getContext("2d");\n\ 1.445 +\n\ 1.446 +\n\ 1.447 + var size = 30;\n\ 1.448 + canvas.fillStyle = "red";\n\ 1.449 + canvas.fillRect(0, 0, size, size);\n\ 1.450 + canvas.scale(1, -1);\n\ 1.451 + canvas.translate(0, -size);\n\ 1.452 +\n\ 1.453 + if (!canvas.setFillColor)\n\ 1.454 + canvas.setFillColor = function(r, g, b, a) {\n\ 1.455 + this.fillStyle = "rgb("+[Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]+")";\n\ 1.456 + }\n\ 1.457 +\n\ 1.458 +for (var y = 0; y < size; y++) {\n\ 1.459 + for (var x = 0; x < size; x++) {\n\ 1.460 + var l = pixels[y][x];\n\ 1.461 + canvas.setFillColor(l[0], l[1], l[2], 1);\n\ 1.462 + canvas.fillRect(x, y, 1, 1);\n\ 1.463 + }\n\ 1.464 +}</scr' + 'ipt>'; 1.465 + 1.466 + return s; 1.467 +} 1.468 + 1.469 +testOutput = arrayToCanvasCommands(raytraceScene());