1
0
Fork 0
mirror of https://github.com/luau-lang/luau.git synced 2025-03-05 03:31:41 +00:00
luau/bench/gc/test_SunSpider_3d-raytrace.lua
Vyacheslav Egorov aafea36235
Fixed the backwards compatible benchmark support library require ()
Previous benchmark require fix wasn't actually working correctly for the
old style require (or running in Lua).
2023-12-04 12:48:31 -08:00

503 lines
16 KiB
Lua

--[[
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
]]
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
function test()
local size = 30
function createVector(x,y,z)
return { x,y,z };
end
function sqrLengthVector(self)
return self[1] * self[1] + self[2] * self[2] + self[3] * self[3];
end
function lengthVector(self)
return math.sqrt(self[1] * self[1] + self[2] * self[2] + self[3] * self[3]);
end
function addVector(self, v)
self[1] = self[1] + v[1];
self[2] = self[2] + v[2];
self[3] = self[3] + v[3];
return self;
end
function subVector(self, v)
self[1] = self[1] - v[1];
self[2] = self[2] - v[2];
self[3] = self[3] - v[3];
return self;
end
function scaleVector(self, scale)
self[1] = self[1] * scale;
self[2] = self[2] * scale;
self[3] = self[3] * scale;
return self;
end
function normaliseVector(self)
local len = math.sqrt(self[1] * self[1] + self[2] * self[2] + self[3] * self[3]);
self[1] = self[1] / len;
self[2] = self[2] / len;
self[3] = self[3] / len;
return self;
end
function add(v1, v2)
return { v1[1] + v2[1], v1[2] + v2[2], v1[3] + v2[3] };
end
function sub(v1, v2)
return { v1[1] - v2[1], v1[2] - v2[2], v1[3] - v2[3] };
end
function scalev(v1, v2)
return { v1[1] * v2[1], v1[2] * v2[2], v1[3] * v2[3] };
end
function dot(v1, v2)
return v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3];
end
function scale(v, scale)
return { v[1] * scale, v[2] * scale, v[3] * scale };
end
function cross(v1, v2)
return { v1[2] * v2[3] - v1[3] * v2[2],
v1[3] * v2[1] - v1[1] * v2[3],
v1[1] * v2[2] - v1[2] * v2[1] };
end
function normalise(v)
local len = lengthVector(v);
return { v[1] / len, v[2] / len, v[3] / len };
end
function transformMatrix(self, v)
local vals = self;
local x = vals[1] * v[1] + vals[2] * v[2] + vals[3] * v[3] + vals[4];
local y = vals[5] * v[1] + vals[6] * v[2] + vals[7] * v[3] + vals[8];
local z = vals[9] * v[1] + vals[10] * v[2] + vals[11] * v[3] + vals[12];
return { x, y, z };
end
function invertMatrix(self)
local temp = {}
local tx = -self[4];
local ty = -self[8];
local tz = -self[12];
for h = 0,2 do
for v = 0,2 do
temp[h + v * 4 + 1] = self[v + h * 4 + 1];
end
end
for i = 0,10 do
self[i + 1] = temp[i + 1];
end
self[4] = tx * self[1] + ty * self[2] + tz * self[3];
self[8] = tx * self[5] + ty * self[6] + tz * self[7];
self[12] = tx * self[9] + ty * self[10] + tz * self[11];
return self;
end
-- Triangle intersection using barycentric coord method
function Triangle(p1, p2, p3)
local this = {}
local edge1 = sub(p3, p1);
local edge2 = sub(p2, p1);
local normal = cross(edge1, edge2);
if (math.abs(normal[1]) > math.abs(normal[2])) then
if (math.abs(normal[1]) > math.abs(normal[3])) then
this.axis = 0;
else
this.axis = 2;
end
else
if (math.abs(normal[2]) > math.abs(normal[3])) then
this.axis = 1;
else
this.axis = 2;
end
end
local u = (this.axis + 1) % 3;
local v = (this.axis + 2) % 3;
local u1 = edge1[u + 1];
local v1 = edge1[v + 1];
local u2 = edge2[u + 1];
local v2 = edge2[v + 1];
this.normal = normalise(normal);
this.nu = normal[u + 1] / normal[this.axis + 1];
this.nv = normal[v + 1] / normal[this.axis + 1];
this.nd = dot(normal, p1) / normal[this.axis + 1];
local det = u1 * v2 - v1 * u2;
this.eu = p1[u + 1];
this.ev = p1[v + 1];
this.nu1 = u1 / det;
this.nv1 = -v1 / det;
this.nu2 = v2 / det;
this.nv2 = -u2 / det;
this.material = { 0.7, 0.7, 0.7 };
this.intersect = function(self, orig, dir, near, far)
local u = (self.axis + 1) % 3;
local v = (self.axis + 2) % 3;
local d = dir[self.axis + 1] + self.nu * dir[u + 1] + self.nv * dir[v + 1];
local t = (self.nd - orig[self.axis + 1] - self.nu * orig[u + 1] - self.nv * orig[v + 1]) / d;
if (t < near or t > far) then
return nil;
end
local Pu = orig[u + 1] + t * dir[u + 1] - self.eu;
local Pv = orig[v + 1] + t * dir[v + 1] - self.ev;
local a2 = Pv * self.nu1 + Pu * self.nv1;
if (a2 < 0) then
return nil;
end
local a3 = Pu * self.nu2 + Pv * self.nv2;
if (a3 < 0) then
return nil;
end
if ((a2 + a3) > 1) then
return nil;
end
return t;
end
return this
end
function Scene(a_triangles)
local this = {}
this.triangles = a_triangles;
this.lights = {};
this.ambient = {0,0,0};
this.background = {0.8,0.8,1};
this.intersect = function(self, origin, dir, near, far)
local closest = nil;
for i = 0,#self.triangles-1 do
local triangle = self.triangles[i + 1];
local d = triangle:intersect(origin, dir, near, far);
if (d == nil or d > far or d < near) then
-- continue;
else
far = d;
closest = triangle;
end
end
if (not closest) then
return { self.background[1],self.background[2],self.background[3] };
end
local normal = closest.normal;
local hit = add(origin, scale(dir, far));
if (dot(dir, normal) > 0) then
normal = { -normal[1], -normal[2], -normal[3] };
end
local colour = nil;
if (closest.shader) then
colour = closest.shader(closest, hit, dir);
else
colour = closest.material;
end
-- do reflection
local reflected = nil;
if (colour.reflection or 0 > 0.001) then
local reflection = addVector(scale(normal, -2*dot(dir, normal)), dir);
reflected = self:intersect(hit, reflection, 0.0001, 1000000);
if (colour.reflection >= 0.999999) then
return reflected;
end
end
local l = { self.ambient[1], self.ambient[2], self.ambient[3] };
for i = 0,#self.lights-1 do
local light = self.lights[i + 1];
local toLight = sub(light, hit);
local distance = lengthVector(toLight);
scaleVector(toLight, 1.0/distance);
distance = distance - 0.0001;
if (self:blocked(hit, toLight, distance)) then
-- continue;
else
local nl = dot(normal, toLight);
if (nl > 0) then
addVector(l, scale(light.colour, nl));
end
end
end
l = scalev(l, colour);
if (reflected) then
l = addVector(scaleVector(l, 1 - colour.reflection), scaleVector(reflected, colour.reflection));
end
return l;
end
this.blocked = function(self, O, D, far)
local near = 0.0001;
local closest = nil;
for i = 0,#self.triangles-1 do
local triangle = self.triangles[i + 1];
local d = triangle:intersect(O, D, near, far);
if (d == nil or d > far or d < near) then
--continue;
else
return true;
end
end
return false;
end
return this
end
local zero = { 0,0,0 };
-- this camera code is from notes i made ages ago, it is from *somewhere* -- i cannot remember where
-- that somewhere is
function Camera(origin, lookat, up)
local this = {}
local zaxis = normaliseVector(subVector(lookat, origin));
local xaxis = normaliseVector(cross(up, zaxis));
local yaxis = normaliseVector(cross(xaxis, subVector({ 0,0,0 }, zaxis)));
local m = {};
m[1] = xaxis[1]; m[2] = xaxis[2]; m[3] = xaxis[3];
m[5] = yaxis[1]; m[6] = yaxis[2]; m[7] = yaxis[3];
m[9] = zaxis[1]; m[10] = zaxis[2]; m[11] = zaxis[3];
m[4] = 0; m[8] = 0; m[12] = 0;
invertMatrix(m);
m[4] = 0; m[8] = 0; m[12] = 0;
this.origin = origin;
this.directions = {};
this.directions[1] = normalise({ -0.7, 0.7, 1 });
this.directions[2] = normalise({ 0.7, 0.7, 1 });
this.directions[3] = normalise({ 0.7, -0.7, 1 });
this.directions[4] = normalise({ -0.7, -0.7, 1 });
this.directions[1] = transformMatrix(m, this.directions[1]);
this.directions[2] = transformMatrix(m, this.directions[2]);
this.directions[3] = transformMatrix(m, this.directions[3]);
this.directions[4] = transformMatrix(m, this.directions[4]);
this.generateRayPair = function(self, y)
rays = { {}, {} }
rays[1].origin = self.origin;
rays[2].origin = self.origin;
rays[1].dir = addVector(scale(self.directions[1], y), scale(self.directions[4], 1 - y));
rays[2].dir = addVector(scale(self.directions[2], y), scale(self.directions[3], 1 - y));
return rays;
end
function renderRows(camera, scene, pixels, width, height, starty, stopy)
for y = starty,stopy-1 do
local rays = camera:generateRayPair(y / height);
for x = 0,width-1 do
local xp = x / width;
local origin = addVector(scale(rays[1].origin, xp), scale(rays[2].origin, 1 - xp));
local dir = normaliseVector(addVector(scale(rays[1].dir, xp), scale(rays[2].dir, 1 - xp)));
local l = scene:intersect(origin, dir, 0, math.huge);
pixels[y + 1][x + 1] = l;
end
end
end
this.render = function(self, scene, pixels, width, height)
local cam = self;
local row = 0;
renderRows(cam, scene, pixels, width, height, 0, height);
end
return this
end
function raytraceScene()
local startDate = 13154863;
local numTriangles = 2 * 6;
local triangles = {}; -- numTriangles);
local tfl = createVector(-10, 10, -10);
local tfr = createVector( 10, 10, -10);
local tbl = createVector(-10, 10, 10);
local tbr = createVector( 10, 10, 10);
local bfl = createVector(-10, -10, -10);
local bfr = createVector( 10, -10, -10);
local bbl = createVector(-10, -10, 10);
local bbr = createVector( 10, -10, 10);
-- cube!!!
-- front
local i = 0;
triangles[i + 1] = Triangle(tfl, tfr, bfr); i = i + 1;
triangles[i + 1] = Triangle(tfl, bfr, bfl); i = i + 1;
-- back
triangles[i + 1] = Triangle(tbl, tbr, bbr); i = i + 1;
triangles[i + 1] = Triangle(tbl, bbr, bbl); i = i + 1;
-- triangles[i-1].material = [0.7,0.2,0.2];
-- triangles[i-1].material.reflection = 0.8;
-- left
triangles[i + 1] = Triangle(tbl, tfl, bbl); i = i + 1;
-- triangles[i-1].reflection = 0.6;
triangles[i + 1] = Triangle(tfl, bfl, bbl); i = i + 1;
-- triangles[i-1].reflection = 0.6;
-- right
triangles[i + 1] = Triangle(tbr, tfr, bbr); i = i + 1;
triangles[i + 1] = Triangle(tfr, bfr, bbr); i = i + 1;
-- top
triangles[i + 1] = Triangle(tbl, tbr, tfr); i = i + 1;
triangles[i + 1] = Triangle(tbl, tfr, tfl); i = i + 1;
-- bottom
triangles[i + 1] = Triangle(bbl, bbr, bfr); i = i + 1;
triangles[i + 1] = Triangle(bbl, bfr, bfl); i = i + 1;
-- Floor!!!!
local green = createVector(0.0, 0.4, 0.0);
green.reflection = 0; --
local grey = createVector(0.4, 0.4, 0.4);
grey.reflection = 1.0;
local floorShader = function(tri, pos, view)
local x = ((pos[1]/32) % 2 + 2) % 2;
local z = ((pos[3]/32 + 0.3) % 2 + 2) % 2;
if ((x < 1) ~= (z < 1)) then
--in the real world we use the fresnel term...
-- local angle = 1-dot(view, tri.normal);
-- angle *= angle;
-- angle *= angle;
-- angle *= angle;
--grey.reflection = angle;
return grey;
else
return green;
end
end
local ffl = createVector(-1000, -30, -1000);
local ffr = createVector( 1000, -30, -1000);
local fbl = createVector(-1000, -30, 1000);
local fbr = createVector( 1000, -30, 1000);
triangles[i + 1] = Triangle(fbl, fbr, ffr); i = i + 1;
triangles[i-1 + 1].shader = floorShader;
triangles[i + 1] = Triangle(fbl, ffr, ffl); i = i + 1;
triangles[i-1 + 1].shader = floorShader;
local _scene = Scene(triangles);
_scene.lights[1] = createVector(20, 38, -22);
_scene.lights[1].colour = createVector(0.7, 0.3, 0.3);
_scene.lights[2] = createVector(-23, 40, 17);
_scene.lights[2].colour = createVector(0.7, 0.3, 0.3);
_scene.lights[3] = createVector(23, 20, 17);
_scene.lights[3].colour = createVector(0.7, 0.7, 0.7);
_scene.ambient = createVector(0.1, 0.1, 0.1);
-- _scene.background = createVector(0.7, 0.7, 1.0);
local pixels = {};
for y = 0,size-1 do
pixels[y + 1] = {};
for x = 0,size-1 do
pixels[y + 1][x + 1] = 0;
end
end
local _camera = Camera(createVector(-40, 40, 40), createVector(0, 0, 0), createVector(0, 1, 0));
_camera:render(_scene, pixels, size, size);
return pixels;
end
function arrayToCanvasCommands(pixels)
local s = '<!DOCTYPE html><html><head><title>Test</title></head><body><canvas id="renderCanvas" width="' .. size .. 'px" height="' .. size .. 'px"></canvas><scr' .. 'ipt>\nvar pixels = [';
for y = 0,size-1 do
s = s .. "[";
for x = 0,size-1 do
s = s .. "[" .. math.floor(pixels[y + 1][x + 1][1] * 255) .. "," .. math.floor(pixels[y + 1][x + 1][2] * 255) .. "," .. math.floor(pixels[y + 1][x + 1][3] * 255) .. "],";
end
s = s .. "],";
end
s = s .. '];\n var canvas = document.getElementById("renderCanvas").getContext("2d");\n\
\n\
\n\
var size = ' .. size .. ';\n\
canvas.fillStyle = "red";\n\
canvas.fillRect(0, 0, size, size);\n\
canvas.scale(1, -1);\n\
canvas.translate(0, -size);\n\
\n\
if (!canvas.setFillColor)\n\
canvas.setFillColor = function(r, g, b, a) {\n\
this.fillStyle = "rgb("+[Math.floor(r), Math.floor(g), Math.floor(b)]+")";\n\
}\n\
\n\
for (var y = 0; y < size; y++) {\n\
for (var x = 0; x < size; x++) {\n\
var l = pixels[y][x];\n\
canvas.setFillColor(l[0], l[1], l[2], 1);\n\
canvas.fillRect(x, y, 1, 1);\n\
}\n\
}</scr' .. 'ipt></body></html>';
return s;
end
testOutput = arrayToCanvasCommands(raytraceScene());
--local f = io.output("output.html")
--f:write(testOutput)
--f:close()
local expectedLength = 11599;
local testLength = #testOutput
if (testLength ~= expectedLength) then
assert(false, "Error: bad result: expected length " .. expectedLength .. " but got " .. testLength);
end
end
bench.runCode(test, "3d-raytrace")