diff --git a/tools/sundae/ref-test-runner.html b/tools/sundae/ref-test-runner.html new file mode 100755 index 0000000..01acf38 --- /dev/null +++ b/tools/sundae/ref-test-runner.html @@ -0,0 +1,42 @@ + + + + + Sundae + + + +

Sundae Ref Test Runner

+

Settings

+
Run Tests: +   + Blur Radius:   + Epsilon (0-1.0):   +
+ + + \ No newline at end of file diff --git a/tools/sundae/resources/acorn_unlit.js b/tools/sundae/resources/acorn_unlit.js new file mode 100644 index 0000000..25fd2a1 --- /dev/null +++ b/tools/sundae/resources/acorn_unlit.js @@ -0,0 +1,16 @@ +function start(cvs){ + var acorn; + var ps = new PointStream(); + ps.setup(cvs); + ps.pointSize(5); + ps.onRender = function(){ + ps.background([1, 1, 1, 1]); + ps.clear(); + ps.translate(0, 0, -25); + ps.render(acorn); + if(acorn.status === 3){ + ps.onRender = function(){}; + } + }; + acorn = ps.load('../../clouds/acorn.asc'); +} \ No newline at end of file diff --git a/tools/sundae/resources/acorn_unlit.png b/tools/sundae/resources/acorn_unlit.png new file mode 100644 index 0000000..2f004be Binary files /dev/null and b/tools/sundae/resources/acorn_unlit.png differ diff --git a/tools/sundae/resources/only_verts.js b/tools/sundae/resources/only_verts.js new file mode 100644 index 0000000..39d35e7 --- /dev/null +++ b/tools/sundae/resources/only_verts.js @@ -0,0 +1,16 @@ +function start(cvs){ + var pointCloud; + var ps = new PointStream(); + ps.setup(cvs); + ps.pointSize(5); + ps.onRender = function(){ + ps.background([1,1,1,1]); + ps.clear(); + ps.translate(0, 0, -25); + ps.render(pointCloud); + if(pointCloud.status === 3){ + ps.onRender = function(){}; + } + }; + pointCloud = ps.load('../../clouds/acorn_only_verts.asc'); +} \ No newline at end of file diff --git a/tools/sundae/resources/only_verts.png b/tools/sundae/resources/only_verts.png new file mode 100644 index 0000000..0c9becf Binary files /dev/null and b/tools/sundae/resources/only_verts.png differ diff --git a/tools/sundae/resources/tests.js b/tools/sundae/resources/tests.js new file mode 100755 index 0000000..133da82 --- /dev/null +++ b/tools/sundae/resources/tests.js @@ -0,0 +1,42 @@ +{ + "testSuite": [{ + "test": + [ + { + "name": "user shader", + "dependancyURL": ["../../mjs.js", + "../../psapi.js", + "../../parsers/asc.js", + "resources/user_shader/user_shader.js"], + "referenceImageURL": "resources/user_shader/mickey_lit.png", + "run": {"src": "resources/user_shader/test.js", "func": "start" } + }, + + { + "name": "only verts", + "dependancyURL": ["../../mjs.js", + "../../psapi.js", + "../../parsers/asc.js"], + "referenceImageURL": "resources/only_verts.png", + "run": {"src": "resources/only_verts.js", "func": "start" } + }, + { + "name": "1", + "dependancyURL": ["../../mjs.js", + "../../psapi.js", + "../../parsers/asc.js"], + "referenceImageURL": "resources/acorn_unlit.png", + "run": {"src": "resources/acorn_unlit.js", "func": "start" } + }, + { + "name": "2", + "dependancyURL": ["../../mjs.js", + "../../psapi.js", + "../../parsers/asc.js"], + "referenceImageURL": "resources/acorn_unlit.png", + "run": {"src": "resources/acorn_unlit.js", "func": "start" } + } + ] + }] +} + \ No newline at end of file diff --git a/tools/sundae/resources/user_shader/mickey_lit.png b/tools/sundae/resources/user_shader/mickey_lit.png new file mode 100644 index 0000000..7a7332c Binary files /dev/null and b/tools/sundae/resources/user_shader/mickey_lit.png differ diff --git a/tools/sundae/resources/user_shader/test.js b/tools/sundae/resources/user_shader/test.js new file mode 100644 index 0000000..f4cdcd6 --- /dev/null +++ b/tools/sundae/resources/user_shader/test.js @@ -0,0 +1,20 @@ +function start(cvs){ + var ps, pointCloud; + ps = new PointStream(); + ps.setup(cvs); + ps.onRender = function(){ + ps.clear(); + ps.uniformi("reflection", false); + ps.uniformf("lightPos", [0, 50, 10]); + ps.uniformf("uReflection", [1, 1, 1, 1]); + ps.translate(0, 10, -80); + ps.render(pointCloud); + if(pointCloud.status === 3){ + ps.onRender = function(){}; + } + }; + var progObj = ps.createProgram(vertShader, fragShader); + ps.useProgram(progObj); + ps.pointSize(10); + pointCloud = ps.load("../../clouds/mickey.asc"); +} diff --git a/tools/sundae/resources/user_shader/user_shader.js b/tools/sundae/resources/user_shader/user_shader.js new file mode 100644 index 0000000..4203bdc --- /dev/null +++ b/tools/sundae/resources/user_shader/user_shader.js @@ -0,0 +1,62 @@ +var vertShader = +"varying vec4 frontColor;" + + +"attribute vec3 ps_Vertex;" + +"attribute vec3 ps_Normal;" + +"attribute vec4 ps_Color;" + + +"uniform float ps_PointSize;" + +"uniform vec3 ps_Attenuation;" + + +"uniform vec3 lightPos;" + +"uniform bool reflection;"+ + +"uniform mat4 ps_ModelViewMatrix;" + +"uniform mat4 ps_ProjectionMatrix;" + +"uniform mat4 ps_NormalMatrix;" + + +"void PointLight(inout vec3 col, in vec3 ecPos, in vec3 vertNormal) {" + +" vec3 VP = lightPos - ecPos;" + +" VP = normalize( VP );" + +" float nDotVP = max( 0.0, dot( vertNormal, VP ));" + +" col += vec3(1.0, 1.0, 1.0) * nDotVP;" + +"}" + + +"void main(void) {" + +" vec3 transNorm = vec3(ps_NormalMatrix * vec4(ps_Normal, 0.0));" + + +" vec4 ecPos4 = ps_ModelViewMatrix * vec4(ps_Vertex, 1.0);" + +" vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" + + +" vec3 col = vec3(0.0, 0.0, 0.0);" + +" PointLight(col, ecPos, transNorm);" + + +" frontColor = ps_Color * vec4(col, 1.0);" + + +" if(reflection){" + +" float l = length(ps_Vertex - vec3(0.0, -20.0, 0.0))/20.0;" + + // magic number that super saturates to white +" float col = l * 5.0;" + +" frontColor += vec4(col, col, col, 1.0);" + +" }" + + +" float dist = length( ecPos4 );" + +" float attn = ps_Attenuation[0] + " + +" (ps_Attenuation[1] * dist) + " + +" (ps_Attenuation[2] * dist * dist);" + + +" gl_PointSize = ps_PointSize * sqrt(1.0/attn);" + +" gl_Position = ps_ProjectionMatrix * ecPos4;" + +"}"; + +var fragShader = +"#ifdef GL_ES\n" + +" precision highp float;\n" + +"#endif\n" + + +"uniform vec4 uReflection;" + + +"varying vec4 frontColor;" + +"void main(void){" + +" gl_FragColor = frontColor * uReflection;" + +"}"; diff --git a/tools/sundae/sundae.js b/tools/sundae/sundae.js new file mode 100755 index 0000000..ab9ec9f --- /dev/null +++ b/tools/sundae/sundae.js @@ -0,0 +1,314 @@ +/*! + * Sundae Javascript Library v0.4 + * http://sundae.lighthouseapp.com/dashboard + * + * The MIT License + * Copyright (c) 2011 Carlin Desautels + * http://www.opensource.org/licenses/mit-license.php +*/ +var sundae = {}; +(function (window, undef) { + //Enviroment variables + var _kernel, _kernelSize, _kernelSum; + var _tag = "a"; + var _sigma = 2; + var _epsilon = 0.05; + var _w = window; + var _testSuite = []; + + sundae.setBlurRadius = function(s){ + if(s) + _sigma = s; + }; + sundae.setTolerance = function(e){ + if(e) + _epsilon = e; + }; + sundae.setTestTag = function(t){ + if(t) + _tag = t; + }; + sundae.init = function(){ + setupKernel(); + setupBody(); + getTests(); + }; + function reportResult(r,t){ + r.innerHTML = (" [" + t + "ms]"); + } + function setupTest(test){ + var name = test.name || "default"; + var d = createDiv(_w.document.getElementById("sundae"), name); + var r = createDiv(d, name + "-title"); + var a = createCanvas(d, name + "-orig", 100, 100); + var b = createCanvas(d, name + "-curr", 100, 100); + var c = createCanvas(d, name + "-diff", 100, 100); + var t = 0; + function runTest(func, aCanvas){ + var startTime = (new Date).getTime(); + func(aCanvas); + t = (new Date).getTime() - startTime; + } + var isDone = {"orig" : false, "curr" : false}; + var whenDone = function(who, func, aCanvas){ + isDone[who] = true; + if(who == "curr" && func && aCanvas){ + runTest(func, aCanvas); + reportResult(r, t, e); + } + if(isDone.curr === true && isDone.orig === true){ + //if aPix == null error + //about:config + //security.fileuri.strict_origin_policy == false + compare(a, b, c); + } + }; + injectOrig(a, test.referenceImageURL, whenDone); + injectCurr(b, test.run, whenDone); + } + function injectOrig(aCanvas, url, callback){ + var ctx = aCanvas.getContext("2d"); + var img = new Image(); + img.onload = function(){ + ctx.drawImage(img, 0, 0, img.width, img.height); + callback("orig"); + } + img.src = url; + } + function injectCurr(aCanvas, run, callback){ + var testObj = eval(run); + if(typeof(testObj) === "function"){ + callback("curr", testObj, aCanvas); + } + else if(typeof(testObj) === "object"){ + getScript(testObj.src, + function(){ + callback("curr", _w[testObj.func], aCanvas); + } + ); + } + else if(typeof(testObj) === "string"){ + callback("curr", _w[testObj], aCanvas); + } + } + function createDiv(parent, id){ + var d = _w.document.createElement("div"); + d.id = id; + parent.appendChild(d); + return d; + } + function createCanvas(parent, id, h, w){ + var c = _w.document.createElement("canvas"); + c.id = id; + c.width = w; + c.height = h; + parent.appendChild(c); + return c; + } + function setupBody(){ + createDiv(_w.document.body, "sundae"); + } + function getTests(){ + var setupTests = function(data){ + var loadDeps = function(deps, test){ + if(typeof(deps) === 'object'){ + if(deps.length > 0){ + getScript(deps.pop(), + function(){ + loadDeps(deps, test); + } + ); + } + else{ + setupTest(test); + } + } + else if(typeof(deps) === 'string'){ + getScript(deps, + function(){ + setupTest(test); + } + ); + } + }; + _testSuite = data.testSuite || undef; + if(_testSuite){ + for(var i = 0, sl = _testSuite.length; i < sl; i++){ + if(_testSuite[i].test){ + for(var j = 0, tl = _testSuite[i].test.length; j < tl; j++){ + if(_testSuite[i].test[j].dependancyURL){ + loadDeps(_testSuite[i].test[j].dependancyURL, _testSuite[i].test[j]); + } + else{ + setupTest(_testSuite[i].test[j]); + } + } + } + } + } + }; + getJSON("resources/tests.js", setupTests); + } + function getJSON(src,callback){ + get(src,callback,true); + } + function getScript(src,callback){ + get(src,callback); + } + function get(src, success, isJSON){ + if(isJSON){ + var r = new XMLHttpRequest(); + r.open("GET", src, true); + r.overrideMimeType("application/json"); + r.onload = function(){ + try{ + success(JSON.parse(r.responseText)); + } + catch(e){ + //Not valid JSON + success(eval("(" + r.responseText + ")")); + } + }; + r.send(null); + } + else{ + var s = _w.document.createElement('script'); + s.type = 'text/javascript'; + s.onload = function(){ + success(); + _w.document.head.removeChild(s); + }; + s.src = src; + _w.document.head.appendChild(s); + } + } + //Global Utility Functions + function getPixels(aCanvas, isWebGL) { + try { + if (isWebGL) { + var context = aCanvas.getContext("experimental-webgl"); + var data = null; + try{ + // try deprecated way first + data = context.readPixels(0, 0, aCanvas.width, aCanvas.height, context.RGBA, context.UNSIGNED_BYTE); + // Chrome posts an error + if(context.getError()){ + throw new Error("API has changed"); + } + } + catch(e){ + // if that failed, try new way + if(!data){ + data = new Uint8Array(aCanvas.width * aCanvas.height * 4); + context.readPixels(0, 0, aCanvas.width, aCanvas.height, context.RGBA, context.UNSIGNED_BYTE, data); + } + } + return data; + } + else { + return aCanvas.getContext('2d').getImageData(0, 0, aCanvas.width, aCanvas.height).data; + } + } + catch (e) { + return null; + } + } + function compare(a, b, c){ + var failed = false; + var valueEpsilon = _epsilon * 255; + //Get pixel arrays from canvases + var aPix = getPixels(a, false); + var bPix = getPixels(b, true); + //Blur pixel arrays + //aPix = blur(aPix, aPix.width, aPix.height); + //bPix = blur(bPix, bPix.width, bPix.height); + if(aPix.length === bPix.length){ + //Compare pixel arrays + var cCtx = c.getContext('2d'); + var cPix = cCtx.createImageData(c.width, c.height); + var len = bPix.length; + for (var j=0; j < len; j+=4){ + if (Math.abs(bPix[j] - aPix[j]) < valueEpsilon && + Math.abs(bPix[j + 1] - aPix[j + 1]) < valueEpsilon && + Math.abs(bPix[j + 2] - aPix[j + 2]) < valueEpsilon && + Math.abs(bPix[j + 3] - aPix[j + 3]) < valueEpsilon){ + cPix.data[j] = cPix.data[j+1] = cPix.data[j+2] = cPix.data[j+3] = 0; + } //Pixel difference in c + else{ + cPix.data[j] = 255; + cPix.data[j+1] = cPix.data[j+2] = 0; + cPix.data[j+3] = 255; + failed = true; + } + } + //Display pixel difference in _c + if(failed){ + cCtx.putImageData(cPix, 0, 0); + } + else{ + cCtx.fillStyle = "rgb(0,255,0)"; + cCtx.fillRect (0, 0, c.width, c.height); + } + } + else{ + failed = true; + } + } + function setupKernel() { + var ss = _sigma * _sigma; + var factor = 2 * Math.PI * ss; + _kernel = new Array(); + _kernel.push(new Array()); + var i = 0, j; + do { + var g = Math.exp(-(i * i) / (2 * ss)) / factor; + if (g < 1e-3) break; + _kernel[0].push(g); + ++i; + } while (i < 7); + _kernelSize = i; + for (j = 1; j < _kernelSize; ++j) { + _kernel.push(new Array()); + for (i = 0; i < _kernelSize; ++i) { + var g = Math.exp(-(i * i + j * j) / (2 * ss)) / factor; + _kernel[j].push(g); + } + } + _kernelSum = 0; + for (j = 1 - _kernelSize; j < _kernelSize; ++j) { + for (i = 1 - _kernelSize; i < _kernelSize; ++i) { + _kernelSum += _kernel[Math.abs(j)][Math.abs(i)]; + } + } + } + function blur(data, width, height) { + var len = data.length; + var newData = new Array(len); + for (var y = 0; y < height; ++y) { + for (var x = 0; x < width; ++x) { + var r = 0, g = 0, b = 0, a = 0; + for (j = 1 - _kernelSize; j < _kernelSize; ++j) { + if (y + j < 0 || y + j >= height) continue; + for (i = 1 - _kernelSize; i < _kernelSize; ++i) { + if (x + i < 0 || x + i >= width) continue; + r += data[4 * ((y + j) * width + (x + i)) + 0] * _kernel[Math.abs(j)][Math.abs(i)]; + g += data[4 * ((y + j) * width + (x + i)) + 1] * _kernel[Math.abs(j)][Math.abs(i)]; + b += data[4 * ((y + j) * width + (x + i)) + 2] * _kernel[Math.abs(j)][Math.abs(i)]; + a += data[4 * ((y + j) * width + (x + i)) + 3] * _kernel[Math.abs(j)][Math.abs(i)]; + } + } + newData[4 * (y * width + x) + 0] = r / _kernelSum; + newData[4 * (y * width + x) + 1] = g / _kernelSum; + newData[4 * (y * width + x) + 2] = b / _kernelSum; + newData[4 * (y * width + x) + 3] = a / _kernelSum; + } + } + return newData; + } + // Opera createImageData fix + try { + if (!("createImageData" in CanvasRenderingContext2D.prototype)) { + CanvasRenderingContext2D.prototype.createImageData = function(sw,sh) { return this.getImageData(0,0,sw,sh); } + } + } catch(e) {} +})(window);