Repository: pyalot/webgl-deferred-irradiance-volumes Branch: master Commit: 72513c3cc927 Files: 121 Total size: 963.5 KB Directory structure: gitextract_c0t50r9u/ ├── README.md ├── build ├── build.sh ├── compile ├── extra/ │ ├── Stats.js │ ├── jquery.js │ ├── loader.coffee │ ├── loader.js │ ├── matrix.coffee │ ├── matrix.js │ ├── shims.coffee │ ├── shims.js │ ├── vector.coffee │ └── vector.js ├── lib/ │ ├── audio.coffee │ ├── audio.js │ ├── camera.coffee │ ├── camera.js │ ├── events.coffee │ ├── events.js │ ├── geometry.js │ ├── hdr_clear.shader │ ├── keys.coffee │ ├── keys.js │ ├── loading.coffee │ ├── loading.js │ ├── rendernode.coffee │ ├── rendernode.js │ ├── schedule.coffee │ ├── schedule.js │ ├── webgl/ │ │ ├── cube.coffee │ │ ├── cube.js │ │ ├── drawable.coffee │ │ ├── drawable.js │ │ ├── framebuffer.coffee │ │ ├── framebuffer.js │ │ ├── hexgrid.coffee │ │ ├── hexgrid.js │ │ ├── model.coffee │ │ ├── model.js │ │ ├── plane.coffee │ │ ├── plane.js │ │ ├── quad.coffee │ │ ├── quad.js │ │ ├── shader.coffee │ │ ├── shader.js │ │ ├── sphere.coffee │ │ ├── sphere.js │ │ ├── texture.coffee │ │ └── texture.js │ ├── webgl-nuke-vendor-prefix.coffee │ ├── webgl-nuke-vendor-prefix.js │ ├── webgl-texture-float-extension-shims.coffee │ └── webgl-texture-float-extension-shims.js ├── license/ │ ├── bsd-license │ ├── gplv2-license │ ├── gplv3-license │ ├── mit-license │ └── readme ├── pack ├── src/ │ ├── albedo.shader │ ├── antialias/ │ │ ├── fxaa2_0.shader │ │ ├── fxaa3_11.shader │ │ ├── fxaa3_11.shaderlib │ │ ├── fxaa3_11_preprocessed.shaderlib │ │ ├── fxaa3_11_stripped.shaderlib │ │ ├── module.coffee │ │ └── module.js │ ├── application.coffee │ ├── application.js │ ├── blur/ │ │ ├── blur.shader │ │ ├── module.coffee │ │ └── module.js │ ├── composit.shader │ ├── deferred_model.coffee │ ├── deferred_model.js │ ├── depth/ │ │ ├── deferred_shadow_map.shader │ │ ├── depth.shader │ │ ├── lightmap_shadow_map.shader │ │ ├── module.coffee │ │ ├── module.js │ │ └── variance.shaderlib │ ├── direct_illumination.shader │ ├── dist3d.coffee │ ├── dist3d.js │ ├── global_illumination.shader │ ├── harmonics.shaderlib │ ├── illumination/ │ │ ├── bounce.shader │ │ ├── bounce_model.coffee │ │ ├── bounce_model.js │ │ ├── cube_diffuse.shader │ │ ├── cubeprobe.shader │ │ ├── debug.shader │ │ ├── harmonics.shader │ │ ├── module.coffee │ │ ├── module.js │ │ ├── shadow.shader │ │ └── transfer.shader │ ├── main.coffee │ ├── main.js │ ├── model/ │ │ ├── lowres.vertices │ │ ├── materials.json │ │ ├── module.coffee │ │ ├── module.js │ │ ├── sponza.indices │ │ └── sponza.vertices │ ├── normaldepth.shader │ ├── presets/ │ │ ├── default.json │ │ └── new.json │ ├── ssao/ │ │ ├── module.coffee │ │ ├── module.js │ │ ├── moments.shader │ │ └── ssao.shader │ ├── sun.coffee │ ├── sun.js │ └── windows/ │ ├── module.coffee │ ├── module.js │ └── window.shader └── www/ ├── assets.pack ├── code.js └── index.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ WebGL Deferred Irradiance Volumes ================================= An implementation of global illumination using deferred application of irradiance probes. Documentation ------------- Blog entry outlining the basic principle: http://codeflow.org/entries/2012/aug/25/webgl-deferred-irradiance-volumes/ Demo ---- Live Demo at: http://codeflow.org/webgl/deferred-irradiance-volumes/www/ License ------- Copyright (c) 2012, Florian Boesch http://codeflow.org/ WebGL Deferred Irradiance Volumes is licensed under any of the following licenses at your choosing: * MIT: see mit-license * GPL: see gplv*-license * BSD: see bsd-license ================================================ FILE: build ================================================ #!/usr/bin/env python ''' Copy this into your bin directory for convenient building ''' import os, subprocess here = os.getcwd() folders = here.split('/') while folders: path = '/'.join(folders) scriptpath = os.path.join(path, 'build.sh') if os.path.exists(scriptpath): os.chdir(path) subprocess.call(['bash', 'build.sh']) break folders.pop() ================================================ FILE: build.sh ================================================ ./compile ./pack ================================================ FILE: compile ================================================ #!/usr/bin/env python import os, sys, stat, subprocess from datetime import datetime here = os.path.dirname(os.path.abspath(__file__)) message_count = 0 def message(text): global message_count now = datetime.now().strftime('%H:%M:%S') print '[%04i %s] %s' % (message_count, now, text) message_count+=1 def error(text): sys.stdout.write('\x1b[31m%s\x1b[39m' % text) sys.stdout.flush() def coffee_compile(path, bare): message('compiling: %s' % path) if bare: command = ['coffee', '--compile', '--bare', path] else: command = ['coffee', '--compile', path] process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = process.communicate() if process.returncode: error(err) def get_mtime(name): return os.stat(name)[stat.ST_MTIME] def listfiles(folder, *exts): for path, dirs, names in os.walk(os.path.join(here, folder)): for name in names: ext = os.path.splitext(name)[-1].lstrip('.') if ext in exts: yield path, name, os.path.join(path, name) def check_files(folder, bare): for path, name, loc in listfiles(folder, 'coffee'): js_path = os.path.join(path, os.path.splitext(name)[0] + '.js') coffee_mtime = get_mtime(loc) if os.path.exists(js_path): js_mtime = get_mtime(js_path) if coffee_mtime > js_mtime: coffee_compile(loc, bare=bare) else: coffee_compile(loc, bare=bare) if __name__ == '__main__': check_files('src', bare=True) check_files('lib', bare=True) check_files('extra', bare=False) ================================================ FILE: extra/Stats.js ================================================ /** * @author mrdoob / http://mrdoob.com/ */ var Stats = function () { var startTime = Date.now(), prevTime = startTime; var ms = 0, msMin = 1000, msMax = 0; var fps = 0, fpsMin = 1000, fpsMax = 0; var frames = 0, mode = 0; var container = document.createElement( 'div' ); container.id = 'stats'; container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; var fpsDiv = document.createElement( 'div' ); fpsDiv.id = 'fps'; fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; container.appendChild( fpsDiv ); var fpsText = document.createElement( 'div' ); fpsText.id = 'fpsText'; fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; fpsText.innerHTML = 'FPS'; fpsDiv.appendChild( fpsText ); var fpsGraph = document.createElement( 'div' ); fpsGraph.id = 'fpsGraph'; fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; fpsDiv.appendChild( fpsGraph ); while ( fpsGraph.children.length < 74 ) { var bar = document.createElement( 'span' ); bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; fpsGraph.appendChild( bar ); } var msDiv = document.createElement( 'div' ); msDiv.id = 'ms'; msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; container.appendChild( msDiv ); var msText = document.createElement( 'div' ); msText.id = 'msText'; msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; msText.innerHTML = 'MS'; msDiv.appendChild( msText ); var msGraph = document.createElement( 'div' ); msGraph.id = 'msGraph'; msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; msDiv.appendChild( msGraph ); while ( msGraph.children.length < 74 ) { var bar = document.createElement( 'span' ); bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; msGraph.appendChild( bar ); } var setMode = function ( value ) { mode = value; switch ( mode ) { case 0: fpsDiv.style.display = 'block'; msDiv.style.display = 'none'; break; case 1: fpsDiv.style.display = 'none'; msDiv.style.display = 'block'; break; } } var updateGraph = function ( dom, value ) { var child = dom.appendChild( dom.firstChild ); child.style.height = value + 'px'; } return { domElement: container, setMode: setMode, begin: function () { startTime = Date.now(); }, end: function () { var time = Date.now(); ms = time - startTime; msMin = Math.min( msMin, ms ); msMax = Math.max( msMax, ms ); msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); frames ++; if ( time > prevTime + 1000 ) { fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); fpsMin = Math.min( fpsMin, fps ); fpsMax = Math.max( fpsMax, fps ); fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); prevTime = time; frames = 0; } return time; }, update: function () { startTime = this.end(); } } }; ================================================ FILE: extra/jquery.js ================================================ /*! jQuery v1.7.2 jquery.com | jquery.org/license */ (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f .clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); ================================================ FILE: extra/loader.coffee ================================================ fs = {} makeURL = (blob) -> return URL.createObjectURL blob makeBlob = (data, type) -> #builder = new BlobBuilder() #builder.append(data) #blob = builder.getBlob(type) #is recommended, but doesn't work in either Firefox or Chrome o_O blob = new Blob([data], type:type) return blob window.getURL = (data, mime) -> blob = makeBlob data, mime return makeURL blob resolvePath = (base, path) -> if path[0] == '/' return path else path = path.split '/' if base == '/' base = [''] else base = base.split '/' while base.length > 0 and path.length > 0 and path[0] == '..' base.pop() path.shift() if base.length == 0 || path.length == 0 || base[0] != '' throw "Invalid path: #{base.join '/'}/#{path.join '/'}" return "#{base.join('/')}/#{path.join('/')}" getJSON = (url, callback) -> request = new XMLHttpRequest() request.open 'GET', url, true request.onload = -> callback(JSON.parse(request.response)) request.send() getBuffer = (url, progress, callback) -> request = new XMLHttpRequest() request.open 'GET', url, true request.responseType = 'arraybuffer' request.onload = -> callback(request.response) request.onprogress = (event) -> if event.lengthComputable progress event.loaded/event.total request.send() isImage = (path) -> return path.match('\.jpg$|\.jpeg|\.gif$|\.png') window.loader = resolvePath: resolvePath main: -> main = @require 'main' if main.main main.main() else throw 'Main function is not defined in main module.' define: (path, code) -> dirname = path.split '/' dirname.pop() dirname = dirname.join '/' require = (modpath) -> abspath = resolvePath dirname, modpath node = fs["#{abspath}.js"] if not node node = fs["#{abspath}/module.js"] if not node then throw "Module not found: #{abspath}" if !node.value then node.create() return node.value get = (respath) -> abspath = resolvePath dirname, respath node = fs[abspath] if not node then throw "Resource not found: #{abspath}" return node get.exists = (respath) -> abspath = resolvePath dirname, respath node = fs[abspath] return node != undefined folder = get.folder = (folderpath) -> folder_abs = resolvePath dirname, folderpath return { path: folder_abs, name: folder_abs.split('/')[folder_abs.split('/').length-1] get: (respath) -> nodepath = resolvePath folder_abs, respath node = fs[nodepath] if not node then throw "Resource not found: #{nodepath}" return node exists: (respath) -> nodepath = resolvePath folder_abs, respath return fs[nodepath] != undefined listdir: (respath) -> if respath then nodepath = resolvepath folder_abs, respath else nodepath = folder_abs result = [] for name of fs match = name.match "#{folder_abs}/[a-zA-Z0-9-\.]+" if match match = match[0] if result.indexOf(match) == -1 result.push match translated = [] for name in result if name.match /\.[a-z]+$/ translated.push name else translated.push folder name return translated } get.listdir = (respath, match) -> if respath abspath = resolvePath dirname, respath else abspath = dirname result = [] for name of fs if name.search(abspath) == 0 if match if name.match match result.push name else result.push name return result fs[path] = path: path type: 'code' data: code create: -> @value = {} retval = code @value, require, get if retval @value = retval require: (modpath) -> abspath = resolvePath '/', modpath node = fs["#{abspath}.js"] if not node node = fs["#{abspath}/module.js"] if not node then throw "Module not found: #{abspath}" if !node.value then node.create() return node.value loadPack: ({url, progress, loaded}) -> files = {} hooks = @hooks getBuffer url, ((factor) -> if progress then progress(factor*0.5, 'network')), (data) -> decoding = 0 decoded = 0 doLoad = (name, info) -> if typeof info == 'object' and info.offset != undefined and info.size != undefined storage = new ArrayBuffer info.size dst = new Uint8Array storage src = new Uint8Array data, 8+length+info.offset, info.size dst.set src dst = dst.buffer if hooks for matcher, decode of hooks if name.match(matcher) decoding += 1 decode name, dst, (result) -> decoded += 1 files[name] = result if progress then progress(0.5+(decoded/decoding)*0.5, 'decode') if decoding == decoded and loaded then loaded(files) return files[name] = dst else if hooks for matcher, decode of hooks if name.match(matcher) decode name, info, (result) -> files[name] = result return files[name] = info length = new Uint32Array(data, 4, 1)[0] metadata = new Uint8Array(data, 8, length) result = '' for i in [0...length] result += String.fromCharCode(metadata[i]) result = JSON.parse(result) for name, info of result doLoad name, info, data if decoding == decoded and loaded then loaded(files) hooks: (@hooks) -> return @ mount: ({url, mountpoint, progress, loaded}) -> mountpoint ?= '/' @loadPack url: url, progress: progress, loaded: (data) -> for name, value of data fs[name] = value loaded(data, fs) ================================================ FILE: extra/loader.js ================================================ // Generated by CoffeeScript 1.3.3 (function() { var fs, getBuffer, getJSON, isImage, makeBlob, makeURL, resolvePath; fs = {}; makeURL = function(blob) { return URL.createObjectURL(blob); }; makeBlob = function(data, type) { var blob; blob = new Blob([data], { type: type }); return blob; }; window.getURL = function(data, mime) { var blob; blob = makeBlob(data, mime); return makeURL(blob); }; resolvePath = function(base, path) { if (path[0] === '/') { return path; } else { path = path.split('/'); if (base === '/') { base = ['']; } else { base = base.split('/'); } while (base.length > 0 && path.length > 0 && path[0] === '..') { base.pop(); path.shift(); } if (base.length === 0 || path.length === 0 || base[0] !== '') { throw "Invalid path: " + (base.join('/')) + "/" + (path.join('/')); } return "" + (base.join('/')) + "/" + (path.join('/')); } }; getJSON = function(url, callback) { var request; request = new XMLHttpRequest(); request.open('GET', url, true); request.onload = function() { return callback(JSON.parse(request.response)); }; return request.send(); }; getBuffer = function(url, progress, callback) { var request; request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.onload = function() { return callback(request.response); }; request.onprogress = function(event) { if (event.lengthComputable) { return progress(event.loaded / event.total); } }; return request.send(); }; isImage = function(path) { return path.match('\.jpg$|\.jpeg|\.gif$|\.png'); }; window.loader = { resolvePath: resolvePath, main: function() { var main; main = this.require('main'); if (main.main) { return main.main(); } else { throw 'Main function is not defined in main module.'; } }, define: function(path, code) { var dirname, folder, get, require; dirname = path.split('/'); dirname.pop(); dirname = dirname.join('/'); require = function(modpath) { var abspath, node; abspath = resolvePath(dirname, modpath); node = fs["" + abspath + ".js"]; if (!node) { node = fs["" + abspath + "/module.js"]; } if (!node) { throw "Module not found: " + abspath; } if (!node.value) { node.create(); } return node.value; }; get = function(respath) { var abspath, node; abspath = resolvePath(dirname, respath); node = fs[abspath]; if (!node) { throw "Resource not found: " + abspath; } return node; }; get.exists = function(respath) { var abspath, node; abspath = resolvePath(dirname, respath); node = fs[abspath]; return node !== void 0; }; folder = get.folder = function(folderpath) { var folder_abs; folder_abs = resolvePath(dirname, folderpath); return { path: folder_abs, name: folder_abs.split('/')[folder_abs.split('/').length - 1], get: function(respath) { var node, nodepath; nodepath = resolvePath(folder_abs, respath); node = fs[nodepath]; if (!node) { throw "Resource not found: " + nodepath; } return node; }, exists: function(respath) { var nodepath; nodepath = resolvePath(folder_abs, respath); return fs[nodepath] !== void 0; }, listdir: function(respath) { var match, name, nodepath, result, translated, _i, _len; if (respath) { nodepath = resolvepath(folder_abs, respath); } else { nodepath = folder_abs; } result = []; for (name in fs) { match = name.match("" + folder_abs + "/[a-zA-Z0-9-\.]+"); if (match) { match = match[0]; if (result.indexOf(match) === -1) { result.push(match); } } } translated = []; for (_i = 0, _len = result.length; _i < _len; _i++) { name = result[_i]; if (name.match(/\.[a-z]+$/)) { translated.push(name); } else { translated.push(folder(name)); } } return translated; } }; }; get.listdir = function(respath, match) { var abspath, name, result; if (respath) { abspath = resolvePath(dirname, respath); } else { abspath = dirname; } result = []; for (name in fs) { if (name.search(abspath) === 0) { if (match) { if (name.match(match)) { result.push(name); } } else { result.push(name); } } } return result; }; return fs[path] = { path: path, type: 'code', data: code, create: function() { var retval; this.value = {}; retval = code(this.value, require, get); if (retval) { return this.value = retval; } } }; }, require: function(modpath) { var abspath, node; abspath = resolvePath('/', modpath); node = fs["" + abspath + ".js"]; if (!node) { node = fs["" + abspath + "/module.js"]; } if (!node) { throw "Module not found: " + abspath; } if (!node.value) { node.create(); } return node.value; }, loadPack: function(_arg) { var files, hooks, loaded, progress, url; url = _arg.url, progress = _arg.progress, loaded = _arg.loaded; files = {}; hooks = this.hooks; return getBuffer(url, (function(factor) { if (progress) { return progress(factor * 0.5, 'network'); } }), function(data) { var decoded, decoding, doLoad, i, info, length, metadata, name, result, _i; decoding = 0; decoded = 0; doLoad = function(name, info) { var decode, dst, matcher, src, storage; if (typeof info === 'object' && info.offset !== void 0 && info.size !== void 0) { storage = new ArrayBuffer(info.size); dst = new Uint8Array(storage); src = new Uint8Array(data, 8 + length + info.offset, info.size); dst.set(src); dst = dst.buffer; if (hooks) { for (matcher in hooks) { decode = hooks[matcher]; if (name.match(matcher)) { decoding += 1; decode(name, dst, function(result) { decoded += 1; files[name] = result; if (progress) { progress(0.5 + (decoded / decoding) * 0.5, 'decode'); } if (decoding === decoded && loaded) { return loaded(files); } }); return; } } } return files[name] = dst; } else { if (hooks) { for (matcher in hooks) { decode = hooks[matcher]; if (name.match(matcher)) { decode(name, info, function(result) { return files[name] = result; }); return; } } } return files[name] = info; } }; length = new Uint32Array(data, 4, 1)[0]; metadata = new Uint8Array(data, 8, length); result = ''; for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) { result += String.fromCharCode(metadata[i]); } result = JSON.parse(result); for (name in result) { info = result[name]; doLoad(name, info, data); } if (decoding === decoded && loaded) { return loaded(files); } }); }, hooks: function(hooks) { this.hooks = hooks; return this; }, mount: function(_arg) { var loaded, mountpoint, progress, url; url = _arg.url, mountpoint = _arg.mountpoint, progress = _arg.progress, loaded = _arg.loaded; if (mountpoint == null) { mountpoint = '/'; } return this.loadPack({ url: url, progress: progress, loaded: function(data) { var name, value; for (name in data) { value = data[name]; fs[name] = value; } return loaded(data, fs); } }); } }; }).call(this); ================================================ FILE: extra/matrix.coffee ================================================ pi = Math.PI tau = 2*pi deg = 360/tau arc = tau/360 window.Mat3 = class Mat3 constructor: (@data) -> @data ?= new Float32Array 9 @identity() identity: -> d = @data d[0] = 1; d[1] =0; d[2] = 0 d[3] = 0; d[4] =1; d[5] = 0 d[6] = 0; d[7] =0; d[8] = 1 return @ transpose: -> d = @data a01 = d[1]; a02 = d[2]; a12 = d[5] d[1] = d[3] d[2] = d[6] d[3] = a01 d[5] = d[7] d[6] = a02 d[7] = a12 return @ mulVec3: (vec, dst=vec) -> @mulVal3 vec.x, vec.y, vec.z, dst return dst mulVal3: (x, y, z, dst) -> dst = dst.data d = @data dst[0] = d[0]*x + d[3]*y + d[6]*z dst[1] = d[1]*x + d[4]*y + d[7]*z dst[2] = d[2]*x + d[5]*y + d[8]*z return @ rotatex: (angle) -> s = Math.sin angle*arc c = Math.cos angle*arc return @amul( 1, 0, 0, 0, c, s, 0, -s, c ) rotatey: (angle) -> s = Math.sin angle*arc c = Math.cos angle*arc return @amul( c, 0, -s, 0, 1, 0, s, 0, c ) rotatez: (angle) -> s = Math.sin angle*arc c = Math.cos angle*arc return @amul( c, s, 0, -s, c, 0, 0, 0, 1 ) amul: ( b00, b10, b20, b01, b11, b21, b02, b12, b22, b03, b13, b23 ) -> a = @data a00 = a[0] a10 = a[1] a20 = a[2] a01 = a[3] a11 = a[4] a21 = a[5] a02 = a[6] a12 = a[7] a22 = a[8] a[0] = a00*b00 + a01*b10 + a02*b20 a[1] = a10*b00 + a11*b10 + a12*b20 a[2] = a20*b00 + a21*b10 + a22*b20 a[3] = a00*b01 + a01*b11 + a02*b21 a[4] = a10*b01 + a11*b11 + a12*b21 a[5] = a20*b01 + a21*b11 + a22*b21 a[6] = a00*b02 + a01*b12 + a02*b22 a[7] = a10*b02 + a11*b12 + a12*b22 a[8] = a20*b02 + a21*b12 + a22*b22 return @ log: -> d = @data console.log '%f, %f, %f,\n%f, %f, %f, \n%f, %f, %f, ', d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8] window.Mat4 = class Mat4 constructor: (@data) -> @data ?= new Float32Array 16 @identity() identity: -> d = @data d[0] = 1; d[1] =0; d[2] = 0; d[3] = 0 d[4] = 0; d[5] =1; d[6] = 0; d[7] = 0 d[8] = 0; d[9] =0; d[10] = 1; d[11] = 0 d[12] = 0; d[13] =0; d[14] = 0; d[15] = 1 return @ zero: -> d = @data d[0] = 0; d[1] =0; d[2] = 0; d[3] = 0 d[4] = 0; d[5] =0; d[6] = 0; d[7] = 0 d[8] = 0; d[9] =0; d[10] = 0; d[11] = 0 d[12] = 0; d[13] =0; d[14] = 0; d[15] = 0 return @ copy: (dest) -> src = @data dst = dest.data dst[0] = src[0] dst[1] = src[1] dst[2] = src[2] dst[3] = src[3] dst[4] = src[4] dst[5] = src[5] dst[6] = src[6] dst[7] = src[7] dst[8] = src[8] dst[9] = src[9] dst[10] = src[10] dst[11] = src[11] dst[12] = src[12] dst[13] = src[13] dst[14] = src[14] dst[15] = src[15] return dest toMat3: (dest) -> src = @data dst = dest.data dst[0] = src[0] dst[1] = src[1] dst[2] = src[2] dst[3] = src[4] dst[4] = src[5] dst[5] = src[6] dst[6] = src[8] dst[7] = src[9] dst[8] = src[10] return dest toMat3Rot: (dest) -> dst = dest.data src = @data a00 = src[0]; a01 = src[1]; a02 = src[2] a10 = src[4]; a11 = src[5]; a12 = src[6] a20 = src[8]; a21 = src[9]; a22 = src[10] b01 = a22 * a11 - a12 * a21 b11 = -a22 * a10 + a12 * a20 b21 = a21 * a10 - a11 * a20 d = a00 * b01 + a01 * b11 + a02 * b21 id = 1 / d dst[0] = b01 * id dst[3] = (-a22 * a01 + a02 * a21) * id dst[6] = (a12 * a01 - a02 * a11) * id dst[1] = b11 * id dst[4] = (a22 * a00 - a02 * a20) * id dst[7] = (-a12 * a00 + a02 * a10) * id dst[2] = b21 * id dst[5] = (-a21 * a00 + a01 * a20) * id dst[8] = (a11 * a00 - a01 * a10) * id return dest perspective: (fov, aspect, near, far) -> @zero() d = @data top = near * Math.tan(fov*Math.PI/360) right = top*aspect left = -right bottom = -top d[0] = (2*near)/(right-left) d[5] = (2*near)/(top-bottom) d[8] = (right+left)/(right-left) d[9] = (top+bottom)/(top-bottom) d[10] = -(far+near)/(far-near) d[11] = -1 d[14] = -(2*far*near)/(far-near) return @ inversePerspective: (fov, aspect, near, far) -> @zero() dst = @data top = near * Math.tan(fov*Math.PI/360) right = top*aspect left = -right bottom = -top dst[0] = (right-left)/(2*near) dst[5] = (top-bottom)/(2*near) dst[11] = -(far-near)/(2*far*near) dst[12] = (right+left)/(2*near) dst[13] = (top+bottom)/(2*near) dst[14] = -1 dst[15] = (far+near)/(2*far*near) return @ ortho: (near=-1, far=1, top=-1, bottom=1, left=-1, right=1) -> rl = right-left tb = top - bottom fn = far - near return @set( 2/rl, 0, 0, -(left+right)/rl, 0, 2/tb, 0, -(top+bottom)/tb, 0, 0, -2/fn, -(far+near)/fn, 0, 0, 0, 1, ) inverseOrtho: (near=-1, far=1, top=-1, bottom=1, left=-1, right=1) -> a = (right-left)/2 b = (right+left)/2 c = (top-bottom)/2 d = (top+bottom)/2 e = (far-near)/-2 f = (near+far)/2 g = 1 return @set( a, 0, 0, b, 0, c, 0, d, 0, 0, e, f, 0, 0, 0, g ) fromRotationTranslation: (quat, vec) -> x = quat.x; y = quat.y; z = quat.z; w = quat.w x2 = x + x y2 = y + y z2 = z + z xx = x * x2 xy = x * y2 xz = x * z2 yy = y * y2 yz = y * z2 zz = z * z2 wx = w * x2 wy = w * y2 wz = w * z2 dest = @data ''' dest[0] = 1 - 2.0 * y * y - 2.0 * y * y dest[1] = 2 * x * y - 2.0 * w * z dest[3] = 2 * x * z + 2.0 * w * y dest[4] = 2 * x * y + 2.0 * w * z dest[5] = 1 - 2.0 * x * x - 2.0 * z * z dest[6] = 2 * y * z - 2.0 * w * x dest[8] = 2 * x * z - 2.0 * w * y dest[9] = 2 * y * z + 2.0 * w * x dest[10] = 1 - 2.0 * x * x - 2.0 * y * y ''' ''' dest[0] = 1 - 2.0 * y * y - 2.0 * y * y dest[1] = 2 * x * y + 2.0 * w * z dest[2] = 2 * x * z - 2.0 * w * y dest[4] = 2 * x * y - 2.0 * w * z dest[5] = 1 - 2.0 * x * x - 2.0 * z * z dest[6] = 2 * y * z + 2.0 * w * x dest[8] = 2 * x * z + 2.0 * w * y dest[9] = 2 * y * z - 2.0 * w * x dest[10] = 1 - 2.0 * x * x - 2.0 * y * y ''' dest[0] = 1 - (yy + zz) dest[1] = xy + wz dest[2] = xz - wy dest[3] = 0 dest[4] = xy - wz dest[5] = 1 - (xx + zz) dest[6] = yz + wx dest[7] = 0 dest[8] = xz + wy dest[9] = yz - wx dest[10] = 1 - (xx + yy) dest[11] = 0 dest[12] = vec.x dest[13] = vec.y dest[14] = vec.z dest[15] = 1 return @ translateVec3: (vec) -> return @translateVal3 vec.x, vec.y, vec.z translateVal3: (x, y, z) -> d = @data a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3] a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7] a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11] d[12] = a00 * x + a10 * y + a20 * z + d[12] d[13] = a01 * x + a11 * y + a21 * z + d[13] d[14] = a02 * x + a12 * y + a22 * z + d[14] d[15] = a03 * x + a13 * y + a23 * z + d[15] return @ rotatex: (angle) -> d = @data rad = tau*(angle/360) s = Math.sin rad c = Math.cos rad a10 = d[4] a11 = d[5] a12 = d[6] a13 = d[7] a20 = d[8] a21 = d[9] a22 = d[10] a23 = d[11] d[4] = a10 * c + a20 * s d[5] = a11 * c + a21 * s d[6] = a12 * c + a22 * s d[7] = a13 * c + a23 * s d[8] = a10 * -s + a20 * c d[9] = a11 * -s + a21 * c d[10] = a12 * -s + a22 * c d[11] = a13 * -s + a23 * c return @ rotatey: (angle) -> d = @data rad = tau*(angle/360) s = Math.sin rad c = Math.cos rad a00 = d[0] a01 = d[1] a02 = d[2] a03 = d[3] a20 = d[8] a21 = d[9] a22 = d[10] a23 = d[11] d[0] = a00 * c + a20 * -s d[1] = a01 * c + a21 * -s d[2] = a02 * c + a22 * -s d[3] = a03 * c + a23 * -s d[8] = a00 * s + a20 * c d[9] = a01 * s + a21 * c d[10] = a02 * s + a22 * c d[11] = a03 * s + a23 * c return @ rotatez: (angle) -> d = @data rad = tau*(angle/360) s = Math.sin rad c = Math.cos rad a00 = d[0] a01 = d[1] a02 = d[2] a03 = d[3] a10 = d[4] a11 = d[5] a12 = d[6] a13 = d[7] d[0] = a00 * c + a10 * s d[1] = a01 * c + a11 * s d[2] = a02 * c + a12 * s d[3] = a03 * c + a13 * s d[4] = a00 * -s + a10 * c d[5] = a01 * -s + a11 * c d[6] = a02 * -s + a12 * c d[7] = a03 * -s + a13 * c return @ scale: (scalar) -> d = @data a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3] a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7] a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11] d[0] = a00 * scalar d[1] = a01 * scalar d[2] = a02 * scalar d[3] = a03 * scalar d[4] = a10 * scalar d[5] = a11 * scalar d[6] = a12 * scalar d[7] = a13 * scalar d[8] = a20 * scalar d[9] = a21 * scalar d[10] = a22 * scalar d[11] = a23 * scalar return @ mulMat4: (other, dst=@) -> dest = dst.data mat = @data mat2 = other.data a00 = mat[ 0]; a01 = mat[ 1]; a02 = mat[ 2]; a03 = mat[3] a10 = mat[ 4]; a11 = mat[ 5]; a12 = mat[ 6]; a13 = mat[7] a20 = mat[ 8]; a21 = mat[ 9]; a22 = mat[10]; a23 = mat[11] a30 = mat[12]; a31 = mat[13]; a32 = mat[14]; a33 = mat[15] b0 = mat2[0]; b1 = mat2[1]; b2 = mat2[2]; b3 = mat2[3] dest[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30 dest[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31 dest[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32 dest[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33 b0 = mat2[4] b1 = mat2[5] b2 = mat2[6] b3 = mat2[7] dest[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30 dest[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31 dest[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32 dest[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33 b0 = mat2[8] b1 = mat2[9] b2 = mat2[10] b3 = mat2[11] dest[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30 dest[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31 dest[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32 dest[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33 b0 = mat2[12] b1 = mat2[13] b2 = mat2[14] b3 = mat2[15] dest[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30 dest[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31 dest[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32 dest[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33 return dst mulVec3: (vec, dst=vec) -> return @mulVal3 vec.x, vec.y, vec.z, dst mulVal3: (x, y, z, dst) -> dst = dst.data d = @data dst[0] = d[0]*x + d[4]*y + d[8] *z dst[1] = d[1]*x + d[5]*y + d[9] *z dst[2] = d[2]*x + d[6]*y + d[10]*z return dst mulVec4: (vec, dst) -> dst ?= vec return @mulVal4 vec.x, vec.y, vec.z, vec.w, dst mulVal4: (x, y, z, w, dst) -> dst = dst.data d = @data dst[0] = d[0]*x + d[4]*y + d[8] *z + d[12]*w dst[1] = d[1]*x + d[5]*y + d[9] *z + d[13]*w dst[2] = d[2]*x + d[6]*y + d[10]*z + d[14]*w dst[3] = d[3]*x + d[7]*y + d[11]*z + d[15]*w return dst invert: (dst=@) -> mat = @data dest = dst.data a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3] a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7] a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11] a30 = mat[12]; a31 = mat[13]; a32 = mat[14]; a33 = mat[15] b00 = a00 * a11 - a01 * a10 b01 = a00 * a12 - a02 * a10 b02 = a00 * a13 - a03 * a10 b03 = a01 * a12 - a02 * a11 b04 = a01 * a13 - a03 * a11 b05 = a02 * a13 - a03 * a12 b06 = a20 * a31 - a21 * a30 b07 = a20 * a32 - a22 * a30 b08 = a20 * a33 - a23 * a30 b09 = a21 * a32 - a22 * a31 b10 = a21 * a33 - a23 * a31 b11 = a22 * a33 - a23 * a32 d = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06) if d==0 then return invDet = 1 / d dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet return dst set: ( a00, a10, a20, a30, a01, a11, a21, a31, a02, a12, a22, a32, a03, a13, a23, a33, ) -> d = @data d[0]=a00; d[4]=a10; d[8]=a20; d[12]=a30 d[1]=a01; d[5]=a11; d[9]=a21; d[13]=a31 d[2]=a02; d[6]=a12; d[10]=a22; d[14]=a32 d[3]=a03; d[7]=a13; d[11]=a23; d[15]=a33 return @ ================================================ FILE: extra/matrix.js ================================================ // Generated by CoffeeScript 1.3.3 (function() { var Mat3, Mat4, arc, deg, pi, tau; pi = Math.PI; tau = 2 * pi; deg = 360 / tau; arc = tau / 360; window.Mat3 = Mat3 = (function() { function Mat3(data) { var _ref; this.data = data; if ((_ref = this.data) == null) { this.data = new Float32Array(9); } this.identity(); } Mat3.prototype.identity = function() { var d; d = this.data; d[0] = 1; d[1] = 0; d[2] = 0; d[3] = 0; d[4] = 1; d[5] = 0; d[6] = 0; d[7] = 0; d[8] = 1; return this; }; Mat3.prototype.transpose = function() { var a01, a02, a12, d; d = this.data; a01 = d[1]; a02 = d[2]; a12 = d[5]; d[1] = d[3]; d[2] = d[6]; d[3] = a01; d[5] = d[7]; d[6] = a02; d[7] = a12; return this; }; Mat3.prototype.mulVec3 = function(vec, dst) { if (dst == null) { dst = vec; } this.mulVal3(vec.x, vec.y, vec.z, dst); return dst; }; Mat3.prototype.mulVal3 = function(x, y, z, dst) { var d; dst = dst.data; d = this.data; dst[0] = d[0] * x + d[3] * y + d[6] * z; dst[1] = d[1] * x + d[4] * y + d[7] * z; dst[2] = d[2] * x + d[5] * y + d[8] * z; return this; }; Mat3.prototype.rotatex = function(angle) { var c, s; s = Math.sin(angle * arc); c = Math.cos(angle * arc); return this.amul(1, 0, 0, 0, c, s, 0, -s, c); }; Mat3.prototype.rotatey = function(angle) { var c, s; s = Math.sin(angle * arc); c = Math.cos(angle * arc); return this.amul(c, 0, -s, 0, 1, 0, s, 0, c); }; Mat3.prototype.rotatez = function(angle) { var c, s; s = Math.sin(angle * arc); c = Math.cos(angle * arc); return this.amul(c, s, 0, -s, c, 0, 0, 0, 1); }; Mat3.prototype.amul = function(b00, b10, b20, b01, b11, b21, b02, b12, b22, b03, b13, b23) { var a, a00, a01, a02, a10, a11, a12, a20, a21, a22; a = this.data; a00 = a[0]; a10 = a[1]; a20 = a[2]; a01 = a[3]; a11 = a[4]; a21 = a[5]; a02 = a[6]; a12 = a[7]; a22 = a[8]; a[0] = a00 * b00 + a01 * b10 + a02 * b20; a[1] = a10 * b00 + a11 * b10 + a12 * b20; a[2] = a20 * b00 + a21 * b10 + a22 * b20; a[3] = a00 * b01 + a01 * b11 + a02 * b21; a[4] = a10 * b01 + a11 * b11 + a12 * b21; a[5] = a20 * b01 + a21 * b11 + a22 * b21; a[6] = a00 * b02 + a01 * b12 + a02 * b22; a[7] = a10 * b02 + a11 * b12 + a12 * b22; a[8] = a20 * b02 + a21 * b12 + a22 * b22; return this; }; Mat3.prototype.log = function() { var d; d = this.data; return console.log('%f, %f, %f,\n%f, %f, %f, \n%f, %f, %f, ', d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8]); }; return Mat3; })(); window.Mat4 = Mat4 = (function() { function Mat4(data) { var _ref; this.data = data; if ((_ref = this.data) == null) { this.data = new Float32Array(16); } this.identity(); } Mat4.prototype.identity = function() { var d; d = this.data; d[0] = 1; d[1] = 0; d[2] = 0; d[3] = 0; d[4] = 0; d[5] = 1; d[6] = 0; d[7] = 0; d[8] = 0; d[9] = 0; d[10] = 1; d[11] = 0; d[12] = 0; d[13] = 0; d[14] = 0; d[15] = 1; return this; }; Mat4.prototype.zero = function() { var d; d = this.data; d[0] = 0; d[1] = 0; d[2] = 0; d[3] = 0; d[4] = 0; d[5] = 0; d[6] = 0; d[7] = 0; d[8] = 0; d[9] = 0; d[10] = 0; d[11] = 0; d[12] = 0; d[13] = 0; d[14] = 0; d[15] = 0; return this; }; Mat4.prototype.copy = function(dest) { var dst, src; src = this.data; dst = dest.data; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; dst[8] = src[8]; dst[9] = src[9]; dst[10] = src[10]; dst[11] = src[11]; dst[12] = src[12]; dst[13] = src[13]; dst[14] = src[14]; dst[15] = src[15]; return dest; }; Mat4.prototype.toMat3 = function(dest) { var dst, src; src = this.data; dst = dest.data; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6]; dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10]; return dest; }; Mat4.prototype.toMat3Rot = function(dest) { var a00, a01, a02, a10, a11, a12, a20, a21, a22, b01, b11, b21, d, dst, id, src; dst = dest.data; src = this.data; a00 = src[0]; a01 = src[1]; a02 = src[2]; a10 = src[4]; a11 = src[5]; a12 = src[6]; a20 = src[8]; a21 = src[9]; a22 = src[10]; b01 = a22 * a11 - a12 * a21; b11 = -a22 * a10 + a12 * a20; b21 = a21 * a10 - a11 * a20; d = a00 * b01 + a01 * b11 + a02 * b21; id = 1 / d; dst[0] = b01 * id; dst[3] = (-a22 * a01 + a02 * a21) * id; dst[6] = (a12 * a01 - a02 * a11) * id; dst[1] = b11 * id; dst[4] = (a22 * a00 - a02 * a20) * id; dst[7] = (-a12 * a00 + a02 * a10) * id; dst[2] = b21 * id; dst[5] = (-a21 * a00 + a01 * a20) * id; dst[8] = (a11 * a00 - a01 * a10) * id; return dest; }; Mat4.prototype.perspective = function(fov, aspect, near, far) { var bottom, d, left, right, top; this.zero(); d = this.data; top = near * Math.tan(fov * Math.PI / 360); right = top * aspect; left = -right; bottom = -top; d[0] = (2 * near) / (right - left); d[5] = (2 * near) / (top - bottom); d[8] = (right + left) / (right - left); d[9] = (top + bottom) / (top - bottom); d[10] = -(far + near) / (far - near); d[11] = -1; d[14] = -(2 * far * near) / (far - near); return this; }; Mat4.prototype.inversePerspective = function(fov, aspect, near, far) { var bottom, dst, left, right, top; this.zero(); dst = this.data; top = near * Math.tan(fov * Math.PI / 360); right = top * aspect; left = -right; bottom = -top; dst[0] = (right - left) / (2 * near); dst[5] = (top - bottom) / (2 * near); dst[11] = -(far - near) / (2 * far * near); dst[12] = (right + left) / (2 * near); dst[13] = (top + bottom) / (2 * near); dst[14] = -1; dst[15] = (far + near) / (2 * far * near); return this; }; Mat4.prototype.ortho = function(near, far, top, bottom, left, right) { var fn, rl, tb; if (near == null) { near = -1; } if (far == null) { far = 1; } if (top == null) { top = -1; } if (bottom == null) { bottom = 1; } if (left == null) { left = -1; } if (right == null) { right = 1; } rl = right - left; tb = top - bottom; fn = far - near; return this.set(2 / rl, 0, 0, -(left + right) / rl, 0, 2 / tb, 0, -(top + bottom) / tb, 0, 0, -2 / fn, -(far + near) / fn, 0, 0, 0, 1); }; Mat4.prototype.inverseOrtho = function(near, far, top, bottom, left, right) { var a, b, c, d, e, f, g; if (near == null) { near = -1; } if (far == null) { far = 1; } if (top == null) { top = -1; } if (bottom == null) { bottom = 1; } if (left == null) { left = -1; } if (right == null) { right = 1; } a = (right - left) / 2; b = (right + left) / 2; c = (top - bottom) / 2; d = (top + bottom) / 2; e = (far - near) / -2; f = (near + far) / 2; g = 1; return this.set(a, 0, 0, b, 0, c, 0, d, 0, 0, e, f, 0, 0, 0, g); }; Mat4.prototype.fromRotationTranslation = function(quat, vec) { var dest, w, wx, wy, wz, x, x2, xx, xy, xz, y, y2, yy, yz, z, z2, zz; x = quat.x; y = quat.y; z = quat.z; w = quat.w; x2 = x + x; y2 = y + y; z2 = z + z; xx = x * x2; xy = x * y2; xz = x * z2; yy = y * y2; yz = y * z2; zz = z * z2; wx = w * x2; wy = w * y2; wz = w * z2; dest = this.data; ' \ndest[0] = 1 - 2.0 * y * y - 2.0 * y * y\ndest[1] = 2 * x * y - 2.0 * w * z\ndest[3] = 2 * x * z + 2.0 * w * y\n\ndest[4] = 2 * x * y + 2.0 * w * z\ndest[5] = 1 - 2.0 * x * x - 2.0 * z * z\ndest[6] = 2 * y * z - 2.0 * w * x\n\ndest[8] = 2 * x * z - 2.0 * w * y\ndest[9] = 2 * y * z + 2.0 * w * x\ndest[10] = 1 - 2.0 * x * x - 2.0 * y * y'; 'dest[0] = 1 - 2.0 * y * y - 2.0 * y * y\ndest[1] = 2 * x * y + 2.0 * w * z\ndest[2] = 2 * x * z - 2.0 * w * y\n\ndest[4] = 2 * x * y - 2.0 * w * z\ndest[5] = 1 - 2.0 * x * x - 2.0 * z * z\ndest[6] = 2 * y * z + 2.0 * w * x\n\ndest[8] = 2 * x * z + 2.0 * w * y\ndest[9] = 2 * y * z - 2.0 * w * x\ndest[10] = 1 - 2.0 * x * x - 2.0 * y * y'; dest[0] = 1 - (yy + zz); dest[1] = xy + wz; dest[2] = xz - wy; dest[3] = 0; dest[4] = xy - wz; dest[5] = 1 - (xx + zz); dest[6] = yz + wx; dest[7] = 0; dest[8] = xz + wy; dest[9] = yz - wx; dest[10] = 1 - (xx + yy); dest[11] = 0; dest[12] = vec.x; dest[13] = vec.y; dest[14] = vec.z; dest[15] = 1; return this; }; Mat4.prototype.translateVec3 = function(vec) { return this.translateVal3(vec.x, vec.y, vec.z); }; Mat4.prototype.translateVal3 = function(x, y, z) { var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, d; d = this.data; a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3]; a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7]; a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11]; d[12] = a00 * x + a10 * y + a20 * z + d[12]; d[13] = a01 * x + a11 * y + a21 * z + d[13]; d[14] = a02 * x + a12 * y + a22 * z + d[14]; d[15] = a03 * x + a13 * y + a23 * z + d[15]; return this; }; Mat4.prototype.rotatex = function(angle) { var a10, a11, a12, a13, a20, a21, a22, a23, c, d, rad, s; d = this.data; rad = tau * (angle / 360); s = Math.sin(rad); c = Math.cos(rad); a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7]; a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11]; d[4] = a10 * c + a20 * s; d[5] = a11 * c + a21 * s; d[6] = a12 * c + a22 * s; d[7] = a13 * c + a23 * s; d[8] = a10 * -s + a20 * c; d[9] = a11 * -s + a21 * c; d[10] = a12 * -s + a22 * c; d[11] = a13 * -s + a23 * c; return this; }; Mat4.prototype.rotatey = function(angle) { var a00, a01, a02, a03, a20, a21, a22, a23, c, d, rad, s; d = this.data; rad = tau * (angle / 360); s = Math.sin(rad); c = Math.cos(rad); a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3]; a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11]; d[0] = a00 * c + a20 * -s; d[1] = a01 * c + a21 * -s; d[2] = a02 * c + a22 * -s; d[3] = a03 * c + a23 * -s; d[8] = a00 * s + a20 * c; d[9] = a01 * s + a21 * c; d[10] = a02 * s + a22 * c; d[11] = a03 * s + a23 * c; return this; }; Mat4.prototype.rotatez = function(angle) { var a00, a01, a02, a03, a10, a11, a12, a13, c, d, rad, s; d = this.data; rad = tau * (angle / 360); s = Math.sin(rad); c = Math.cos(rad); a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3]; a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7]; d[0] = a00 * c + a10 * s; d[1] = a01 * c + a11 * s; d[2] = a02 * c + a12 * s; d[3] = a03 * c + a13 * s; d[4] = a00 * -s + a10 * c; d[5] = a01 * -s + a11 * c; d[6] = a02 * -s + a12 * c; d[7] = a03 * -s + a13 * c; return this; }; Mat4.prototype.scale = function(scalar) { var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, d; d = this.data; a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3]; a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7]; a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11]; d[0] = a00 * scalar; d[1] = a01 * scalar; d[2] = a02 * scalar; d[3] = a03 * scalar; d[4] = a10 * scalar; d[5] = a11 * scalar; d[6] = a12 * scalar; d[7] = a13 * scalar; d[8] = a20 * scalar; d[9] = a21 * scalar; d[10] = a22 * scalar; d[11] = a23 * scalar; return this; }; Mat4.prototype.mulMat4 = function(other, dst) { var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33, b0, b1, b2, b3, dest, mat, mat2; if (dst == null) { dst = this; } dest = dst.data; mat = this.data; mat2 = other.data; a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3]; a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7]; a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11]; a30 = mat[12]; a31 = mat[13]; a32 = mat[14]; a33 = mat[15]; b0 = mat2[0]; b1 = mat2[1]; b2 = mat2[2]; b3 = mat2[3]; dest[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; dest[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; dest[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; dest[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = mat2[4]; b1 = mat2[5]; b2 = mat2[6]; b3 = mat2[7]; dest[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; dest[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; dest[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; dest[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = mat2[8]; b1 = mat2[9]; b2 = mat2[10]; b3 = mat2[11]; dest[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; dest[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; dest[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; dest[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = mat2[12]; b1 = mat2[13]; b2 = mat2[14]; b3 = mat2[15]; dest[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; dest[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; dest[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; dest[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; return dst; }; Mat4.prototype.mulVec3 = function(vec, dst) { if (dst == null) { dst = vec; } return this.mulVal3(vec.x, vec.y, vec.z, dst); }; Mat4.prototype.mulVal3 = function(x, y, z, dst) { var d; dst = dst.data; d = this.data; dst[0] = d[0] * x + d[4] * y + d[8] * z; dst[1] = d[1] * x + d[5] * y + d[9] * z; dst[2] = d[2] * x + d[6] * y + d[10] * z; return dst; }; Mat4.prototype.mulVec4 = function(vec, dst) { if (dst == null) { dst = vec; } return this.mulVal4(vec.x, vec.y, vec.z, vec.w, dst); }; Mat4.prototype.mulVal4 = function(x, y, z, w, dst) { var d; dst = dst.data; d = this.data; dst[0] = d[0] * x + d[4] * y + d[8] * z + d[12] * w; dst[1] = d[1] * x + d[5] * y + d[9] * z + d[13] * w; dst[2] = d[2] * x + d[6] * y + d[10] * z + d[14] * w; dst[3] = d[3] * x + d[7] * y + d[11] * z + d[15] * w; return dst; }; Mat4.prototype.invert = function(dst) { var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33, b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10, b11, d, dest, invDet, mat; if (dst == null) { dst = this; } mat = this.data; dest = dst.data; a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3]; a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7]; a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11]; a30 = mat[12]; a31 = mat[13]; a32 = mat[14]; a33 = mat[15]; b00 = a00 * a11 - a01 * a10; b01 = a00 * a12 - a02 * a10; b02 = a00 * a13 - a03 * a10; b03 = a01 * a12 - a02 * a11; b04 = a01 * a13 - a03 * a11; b05 = a02 * a13 - a03 * a12; b06 = a20 * a31 - a21 * a30; b07 = a20 * a32 - a22 * a30; b08 = a20 * a33 - a23 * a30; b09 = a21 * a32 - a22 * a31; b10 = a21 * a33 - a23 * a31; b11 = a22 * a33 - a23 * a32; d = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (d === 0) { return; } invDet = 1 / d; dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; return dst; }; Mat4.prototype.set = function(a00, a10, a20, a30, a01, a11, a21, a31, a02, a12, a22, a32, a03, a13, a23, a33) { var d; d = this.data; d[0] = a00; d[4] = a10; d[8] = a20; d[12] = a30; d[1] = a01; d[5] = a11; d[9] = a21; d[13] = a31; d[2] = a02; d[6] = a12; d[10] = a22; d[14] = a32; d[3] = a03; d[7] = a13; d[11] = a23; d[15] = a33; return this; }; return Mat4; })(); }).call(this); ================================================ FILE: extra/shims.coffee ================================================ if window.performance if window.performance.now now = -> window.performance.now() else if window.performance.webkitNow now = -> window.performance.webkitNow() else if window.performance.mozNow now = -> window.performance.mozNow() else if window.performance.oNow now = -> window.performance.oNow() else now = -> Date.now() else now = -> Date.now() start = now() window.gettime = -> (now() - start)/1000 if not window.requestAnimationFrame if window.webkitRequestAnimationFrame window.requestAnimationFrame = window.webkitRequestAnimationFrame else if window.mozRequestAnimationFrame window.requestAnimationFrame = window.mozRequestAnimationFrame else if window.oRequestAnimationFrame window.requestAnimationFrame = window.oRequestAnimationFrame else window.requestAnimationFrame = (fun) -> setTimeout(fun, 1000/30) window.URL = window.URL or window.mozURL or window.webkitURL or window.oURL window.BlobBuilder = window.BlobBuilder or window.MozBlobBuilder or window.WebKitBlobBuilder or window.OBlobBuilder log_count = 0 window.console.logN = (n) -> if log_count < n log_count += 1 args = [].slice.call(arguments, 1) console.log.apply console, args ================================================ FILE: extra/shims.js ================================================ // Generated by CoffeeScript 1.3.3 (function() { var log_count, now, start; if (window.performance) { if (window.performance.now) { now = function() { return window.performance.now(); }; } else if (window.performance.webkitNow) { now = function() { return window.performance.webkitNow(); }; } else if (window.performance.mozNow) { now = function() { return window.performance.mozNow(); }; } else if (window.performance.oNow) { now = function() { return window.performance.oNow(); }; } else { now = function() { return Date.now(); }; } } else { now = function() { return Date.now(); }; } start = now(); window.gettime = function() { return (now() - start) / 1000; }; if (!window.requestAnimationFrame) { if (window.webkitRequestAnimationFrame) { window.requestAnimationFrame = window.webkitRequestAnimationFrame; } else if (window.mozRequestAnimationFrame) { window.requestAnimationFrame = window.mozRequestAnimationFrame; } else if (window.oRequestAnimationFrame) { window.requestAnimationFrame = window.oRequestAnimationFrame; } else { window.requestAnimationFrame = function(fun) { return setTimeout(fun, 1000 / 30); }; } } window.URL = window.URL || window.mozURL || window.webkitURL || window.oURL; window.BlobBuilder = window.BlobBuilder || window.MozBlobBuilder || window.WebKitBlobBuilder || window.OBlobBuilder; log_count = 0; window.console.logN = function(n) { var args; if (log_count < n) { log_count += 1; args = [].slice.call(arguments, 1); return console.log.apply(console, args); } }; }).call(this); ================================================ FILE: extra/vector.coffee ================================================ window.Vec3 = class Vec3 @property 'x' get: -> @data[0] set: (val) -> @data[0] = val @property 'y' get: -> @data[1] set: (val) -> @data[1] = val @property 'z' get: -> @data[2] set: (val) -> @data[2] = val @property 'length' get: -> Math.sqrt(@x*@x + @y*@y + @z*@z) constructor: (@data) -> @data ?= new Float32Array 3 sub: (other, dst=@) -> dst.x = @x - other.x dst.y = @y - other.y dst.z = @z - other.z return dst add: (other, dst=@) -> dst.x = @x + other.x dst.y = @y + other.y dst.z = @z + other.z return dst addVal3: (x, y, z, dst=@) -> dst.x = @x + x dst.y = @y + y dst.z = @z + z return dst mul: (scalar, dst=@) -> dst.x = @x * scalar dst.y = @y * scalar dst.z = @z * scalar return dst div: (scalar, dst=@) -> dst.x = @x / scalar dst.y = @y / scalar dst.z = @z / scalar return dst divVal3: (x, y, z, dst=@) -> dst.x = @x/x dst.y = @y/y dst.z = @z/z return dst dot: (other) -> return @x*other.x + @y*other.y + @z*other.z normalize: (dst=@) -> l = @length if l > 0 then @mul 1/@length, dst return dst set: (x,y,z) -> @x = x @y = y @z = z return @ window.Vec4 = class Vec4 @property 'x' get: -> @data[0] set: (val) -> @data[0] = val @property 'y' get: -> @data[1] set: (val) -> @data[1] = val @property 'z' get: -> @data[2] set: (val) -> @data[2] = val @property 'w' get: -> @data[3] set: (val) -> @data[3] = val constructor: (@data) -> @data ?= new Float32Array 4 sub: (other, dst=@) -> dst.x = @x - other.x dst.y = @y - other.y dst.z = @z - other.z dst.w = @w - other.w return dst dot: (other) -> return @x*other.x + @y*other.y + @z*other.z + @w*other.w toVec3: (dst) -> dst ?= new Vec3() dst.x = @x dst.y = @y dst.z = @z return dst ================================================ FILE: extra/vector.js ================================================ // Generated by CoffeeScript 1.3.3 (function() { var Vec3, Vec4; window.Vec3 = Vec3 = (function() { Vec3.property('x', { get: function() { return this.data[0]; }, set: function(val) { return this.data[0] = val; } }); Vec3.property('y', { get: function() { return this.data[1]; }, set: function(val) { return this.data[1] = val; } }); Vec3.property('z', { get: function() { return this.data[2]; }, set: function(val) { return this.data[2] = val; } }); Vec3.property('length', { get: function() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } }); function Vec3(data) { var _ref; this.data = data; if ((_ref = this.data) == null) { this.data = new Float32Array(3); } } Vec3.prototype.sub = function(other, dst) { if (dst == null) { dst = this; } dst.x = this.x - other.x; dst.y = this.y - other.y; dst.z = this.z - other.z; return dst; }; Vec3.prototype.add = function(other, dst) { if (dst == null) { dst = this; } dst.x = this.x + other.x; dst.y = this.y + other.y; dst.z = this.z + other.z; return dst; }; Vec3.prototype.addVal3 = function(x, y, z, dst) { if (dst == null) { dst = this; } dst.x = this.x + x; dst.y = this.y + y; dst.z = this.z + z; return dst; }; Vec3.prototype.mul = function(scalar, dst) { if (dst == null) { dst = this; } dst.x = this.x * scalar; dst.y = this.y * scalar; dst.z = this.z * scalar; return dst; }; Vec3.prototype.div = function(scalar, dst) { if (dst == null) { dst = this; } dst.x = this.x / scalar; dst.y = this.y / scalar; dst.z = this.z / scalar; return dst; }; Vec3.prototype.divVal3 = function(x, y, z, dst) { if (dst == null) { dst = this; } dst.x = this.x / x; dst.y = this.y / y; dst.z = this.z / z; return dst; }; Vec3.prototype.dot = function(other) { return this.x * other.x + this.y * other.y + this.z * other.z; }; Vec3.prototype.normalize = function(dst) { var l; if (dst == null) { dst = this; } l = this.length; if (l > 0) { this.mul(1 / this.length, dst); } return dst; }; Vec3.prototype.set = function(x, y, z) { this.x = x; this.y = y; this.z = z; return this; }; return Vec3; })(); window.Vec4 = Vec4 = (function() { Vec4.property('x', { get: function() { return this.data[0]; }, set: function(val) { return this.data[0] = val; } }); Vec4.property('y', { get: function() { return this.data[1]; }, set: function(val) { return this.data[1] = val; } }); Vec4.property('z', { get: function() { return this.data[2]; }, set: function(val) { return this.data[2] = val; } }); Vec4.property('w', { get: function() { return this.data[3]; }, set: function(val) { return this.data[3] = val; } }); function Vec4(data) { var _ref; this.data = data; if ((_ref = this.data) == null) { this.data = new Float32Array(4); } } Vec4.prototype.sub = function(other, dst) { if (dst == null) { dst = this; } dst.x = this.x - other.x; dst.y = this.y - other.y; dst.z = this.z - other.z; dst.w = this.w - other.w; return dst; }; Vec4.prototype.dot = function(other) { return this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w; }; Vec4.prototype.toVec3 = function(dst) { if (dst == null) { dst = new Vec3(); } dst.x = this.x; dst.y = this.y; dst.z = this.z; return dst; }; return Vec4; })(); }).call(this); ================================================ FILE: lib/audio.coffee ================================================ class Backend constructor: -> @loading = 0 @handlers = [] loaded: -> if @loading == 0 for handler in @handlers if handler.event == 'loaded' then handler.callback.apply(handler) return bind: (event, callback) -> handler = event: event callback: callback @handlers.push handler return handler unbind: (handler) -> index = @handlers.indexOf handler if index >= 0 then @handlers.splice index, 1 class HTMLAudio extends Backend @available = ( (window.Audio != undefined) and (window.URL != undefined) and (window.BlobBuilder != undefined) ) class Sample constructor: (@backend, data) -> @backend.loading += 1 @url = blob.pack data, 'audio/ogg' play: (looping) -> voice = @backend.getFree() voice.play(@url, looping) if voice class Voice constructor: (backend, @id) -> self = @ @audio = new Audio() @audio.onended = -> backend.ended(self) play: (url) -> @audio.src = url @audio.play() constructor: -> @free = {} @playing = {} for id in [0...20] @free[id] = new Voice(@, id) setInterval @check, 100 check: => #for id, voice of @playing #console.log voice.audio.duration getFree: -> for id, voice of @free delete @free[id] @playing[id] = voice return voice ended: (voice) -> @free[voice.id] = voice delete @playing[voice.id] createSample: (data) -> @start_time = gettime() return new Sample @, data class WebAudio extends Backend @available = window.webkitAudioContext != undefined constructor: -> super() @ctx = new webkitAudioContext() play: (buffer, looping=false) -> source = @ctx.createBufferSource() source.buffer = buffer source.loop = looping source.connect(@ctx.destination) source.noteOn(@ctx.currentTime) decode: (data, callback) -> @ctx.decodeAudioData data, (buffer) -> callback(buffer) if WebAudio.available backend = new WebAudio() exports.decode = (data, callback) -> backend.decode data, callback exports.play = (buffer) -> backend.play buffer else exports.decode = (data, callback) -> exports.play = (buffer) -> ================================================ FILE: lib/audio.js ================================================ // Generated by CoffeeScript 1.3.3 var Backend, HTMLAudio, WebAudio, backend, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Backend = (function() { function Backend() { this.loading = 0; this.handlers = []; } Backend.prototype.loaded = function() { var handler, _i, _len, _ref; if (this.loading === 0) { _ref = this.handlers; for (_i = 0, _len = _ref.length; _i < _len; _i++) { handler = _ref[_i]; if (handler.event === 'loaded') { handler.callback.apply(handler); } } } }; Backend.prototype.bind = function(event, callback) { var handler; handler = { event: event, callback: callback }; this.handlers.push(handler); return handler; }; Backend.prototype.unbind = function(handler) { var index; index = this.handlers.indexOf(handler); if (index >= 0) { return this.handlers.splice(index, 1); } }; return Backend; })(); HTMLAudio = (function(_super) { var Sample, Voice; __extends(HTMLAudio, _super); HTMLAudio.available = (window.Audio !== void 0) && (window.URL !== void 0) && (window.BlobBuilder !== void 0); Sample = (function() { function Sample(backend, data) { this.backend = backend; this.backend.loading += 1; this.url = blob.pack(data, 'audio/ogg'); } Sample.prototype.play = function(looping) { var voice; voice = this.backend.getFree(); if (voice) { return voice.play(this.url, looping); } }; return Sample; })(); Voice = (function() { function Voice(backend, id) { var self; this.id = id; self = this; this.audio = new Audio(); this.audio.onended = function() { return backend.ended(self); }; } Voice.prototype.play = function(url) { this.audio.src = url; return this.audio.play(); }; return Voice; })(); function HTMLAudio() { this.check = __bind(this.check, this); var id, _i; this.free = {}; this.playing = {}; for (id = _i = 0; _i < 20; id = ++_i) { this.free[id] = new Voice(this, id); } setInterval(this.check, 100); } HTMLAudio.prototype.check = function() {}; HTMLAudio.prototype.getFree = function() { var id, voice, _ref; _ref = this.free; for (id in _ref) { voice = _ref[id]; delete this.free[id]; this.playing[id] = voice; return voice; } }; HTMLAudio.prototype.ended = function(voice) { this.free[voice.id] = voice; return delete this.playing[voice.id]; }; HTMLAudio.prototype.createSample = function(data) { this.start_time = gettime(); return new Sample(this, data); }; return HTMLAudio; })(Backend); WebAudio = (function(_super) { __extends(WebAudio, _super); WebAudio.available = window.webkitAudioContext !== void 0; function WebAudio() { WebAudio.__super__.constructor.call(this); this.ctx = new webkitAudioContext(); } WebAudio.prototype.play = function(buffer, looping) { var source; if (looping == null) { looping = false; } source = this.ctx.createBufferSource(); source.buffer = buffer; source.loop = looping; source.connect(this.ctx.destination); return source.noteOn(this.ctx.currentTime); }; WebAudio.prototype.decode = function(data, callback) { return this.ctx.decodeAudioData(data, function(buffer) { return callback(buffer); }); }; return WebAudio; })(Backend); if (WebAudio.available) { backend = new WebAudio(); exports.decode = function(data, callback) { return backend.decode(data, callback); }; exports.play = function(buffer) { return backend.play(buffer); }; } else { exports.decode = function(data, callback) {}; exports.play = function(buffer) {}; } ================================================ FILE: lib/camera.coffee ================================================ keys = require 'keys' class MouseDrag constructor: (@which) -> @x = 0 @y = 0 @lx = 0 @ly = 0 @pressed = false if navigator.appVersion.indexOf('Mac') != -1 $('#ui').bind 'mousewheel', (event) => event.preventDefault() event.stopImmediatePropagation() event.stopPropagation() @x += event.originalEvent.wheelDeltaX*0.25 @y += event.originalEvent.wheelDeltaY*0.25 return false $('#ui').mousedown (event) => if event.which == @which @lx = event.pageX @ly = event.pageY @pressed = true return undefined $(document).mouseup => @pressed = false return undefined $(document).mousemove (event) => if @pressed and event.which == @which x = event.pageX y = event.pageY @x += x - @lx @y += y - @ly @lx = x @ly = y return false return undefined reset: -> @x = 0 @y = 0 class Camera constructor: (@delta=1/180, @near=0.1, @far=1000) -> @last_gui_update = gettime() @time = gettime() @proj = new Mat4() @inv_proj = new Mat4() @view = new Mat4() @inv_view = new Mat4() @rot = new Mat3() @inv_rot = new Mat3() @acc = new Vec3() aspect: (width, height) -> @proj.perspective 75, width/height, @near, @far @inv_proj.inversePerspective 75, width/height, @near, @far step: -> @accelerate() @limit() @move() @limit() @time += @delta update: -> now = gettime() if now - @last_gui_update > 0.5 @guiUpdate() @last_gui_update = now if now - @time > @delta*30 @time = now - @delta*30 while @time < now @step() @finish() @view.invert @inv_view.identity() @view.toMat3 @rot.identity() @inv_view.toMat3 @inv_rot.identity() limit: -> guiUpdate: -> exports.GameCam = class GameCam extends Camera constructor: ({@sl, @sr, delta, x, y, z}={}) -> super(delta) @realpos = new Vec4() @sl ?= 200 @sr ?= 100 x ?= 0 y ?= 0 z ?= 0 @mouse = new MouseDrag(3) @target_height = 0 @height = 0 @x=x; @lx=x #@y=y; @ly=y @z=z; @lz=z @o=0; @lo=0 @d=0; @ld=0; @ad=0 $(document).bind 'mousewheel', (event) => event.preventDefault() event.stopImmediatePropagation() event.stopPropagation() @ad -= event.originalEvent.wheelDeltaY return false accelerate: -> sl = @delta*@delta*@sl sr = @delta*@delta*@sr ctrl_x = if keys.a then -1 else if keys.d then 1 else 0 ctrl_y = if keys.q then -1 else if keys.e then 1 else 0 ctrl_z = if keys.w then -1 else if keys.s then 1 else 0 ax = ctrl_x * sl #ay = ctrl_y * sl az = ctrl_z * sl @rot .identity() .rotatey(-@o) .mulVal3(ax, 0, az, @acc) @x += @acc.x #@y += @acc.y @z += @acc.z @o += @mouse.x * sr @d += @ad * @delta * @delta * 20 move = @delta*@delta*4000 if move > 1 then move = 1 @height = @height + (@target_height - @height) * move move: -> retl = 0.97 retr = 0.94 x = @x + (@x - @lx) * retl #y = @y + (@y - @ly) * retl z = @z + (@z - @lz) * retl d = @d + (@d - @ld) * retl o = @o + (@o - @lo) * retr @lx = @x; @x = x #@ly = @y; @y = y @lz = @z; @z = z @lo = @o; @o = o @ld = @d; @d = d limit: -> if @d < 0 then @d = 0 else if @d > 30 then @d = 30 high = 128+64 low = 128-64 if @x < low then @x = low else if @x > high then @x = high if @z < low then @z = low else if @z > high then @z = high finish: -> @mouse.reset() @ad = 0 @view .identity() .translateVal3(0, 0, -@d-5) .rotatex(25+(@d/30)*40) .rotatey(@o) .translateVal3(-@x, -@height, -@z) update: (picker) -> @view .identity() .translateVal3(0, 0, -@d-5) .rotatex(25+(@d/30)*40) .rotatey(@o) .translateVal3(-@x, 0, -@z) @view.invert @inv_view.identity() h1 = picker.getHeight @x, @z @inv_view.mulVal4 0, 0, 0, 1, @realpos h2 = picker.getHeight(@realpos.x, @realpos.z)+2 real_height = h1+@realpos.y if real_height < h2 diff = h2 - real_height @target_height = h1 + diff else @target_height = h1 super() exports.Orbit = class Orbit extends Camera constructor: ({@sr, delta, x, y, z, @dist}={}) -> super(delta) @sr ?= 100 @dist ?= 0.6 @mouse = new MouseDrag(1) @o=0; @lo=0 @p=0; @lp=0 accelerate: -> sr = @delta*@delta*@sr @o += @mouse.x * sr @p += @mouse.y * sr move: -> retr = 0.94 o = @o + (@o - @lo) * retr p = @p + (@p - @lp) * retr @lo = @o; @o = o @lp = @p; @p = p finish: -> @mouse.reset() update: () -> @view .identity() .translateVal3(0, 0, -@dist) .rotatex(@p) .rotatey(@o) @view.invert @inv_view.identity() super() exports.FlyCam = class FlyCam extends Camera constructor: ({@sl, @gui, @sr, delta, near, far, lookbutton, x, y, z, o, p}={}) -> super(delta, near, far) @sl ?= 50 @sr ?= 100 lookbutton ?= 1 x ?= 0 y ?= 0 z ?= 0 o ?= 0 p ?= 0 @mouse = new MouseDrag(lookbutton) @x=x; @lx=x @y=y; @ly=y @z=z; @lz=z @o=o; @lo=o @p=p; @lp=p folder = @gui.addFolder('Camera') @gui.remember @ @xgui = folder.add(@, 'x', -30.0, 30.0).onChange(@guiChanged) @ygui = folder.add(@, 'y', -30.0, 30.0).onChange(@guiChanged) @zgui = folder.add(@, 'z', -30.0, 30.0).onChange(@guiChanged) @go = @o @ogui = folder.add(@, 'go', 0.0, 360.0).name('Orientation').onChange(@guiChanged) @pgui = folder.add(@, 'p', -80.0, 80.0).name('Pitch').onChange(@guiChanged) @guiChanged() guiChanged: => @lx=@x @ly=@y @lz=@z @o=@go; @lo=@go @lp=@p guiUpdate: -> @go = @o % 360 @xgui.updateDisplay() @ygui.updateDisplay() @zgui.updateDisplay() @ogui.updateDisplay() @pgui.updateDisplay() accelerate: -> sl = @delta*@delta*@sl sr = @delta*@delta*@sr ctrl_x = if keys.a then -1 else if keys.d then 1 else 0 ctrl_y = if keys.q then -1 else if keys.e then 1 else 0 ctrl_z = if keys.w then -1 else if keys.s then 1 else 0 ax = ctrl_x * sl ay = ctrl_y * sl az = ctrl_z * sl @rot .identity() .rotatey(-@o) .rotatex(-@p) .mulVal3(ax, ay, az, @acc) @x += @acc.x @y += @acc.y @z += @acc.z @o += @mouse.x * sr @p += @mouse.y * sr move: -> retl = 0.97 retr = 0.94 x = @x + (@x - @lx) * retl y = @y + (@y - @ly) * retl z = @z + (@z - @lz) * retl o = @o + (@o - @lo) * retr p = @p + (@p - @lp) * retr if p > 80 then p = 80 else if p < -80 then p = -80 @lx = @x; @x = x @ly = @y; @y = y @lz = @z; @z = z @lo = @o; @o = o @lp = @p; @p = p finish: -> @mouse.reset() @view .identity() .rotatex(@p) .rotatey(@o) .translateVal3(-@x, -@y, -@z) ================================================ FILE: lib/camera.js ================================================ // Generated by CoffeeScript 1.3.3 var Camera, FlyCam, GameCam, MouseDrag, Orbit, keys, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; keys = require('keys'); MouseDrag = (function() { function MouseDrag(which) { var _this = this; this.which = which; this.x = 0; this.y = 0; this.lx = 0; this.ly = 0; this.pressed = false; if (navigator.appVersion.indexOf('Mac') !== -1) { $('#ui').bind('mousewheel', function(event) { event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); _this.x += event.originalEvent.wheelDeltaX * 0.25; _this.y += event.originalEvent.wheelDeltaY * 0.25; return false; }); } $('#ui').mousedown(function(event) { if (event.which === _this.which) { _this.lx = event.pageX; _this.ly = event.pageY; _this.pressed = true; } return void 0; }); $(document).mouseup(function() { _this.pressed = false; return void 0; }); $(document).mousemove(function(event) { var x, y; if (_this.pressed && event.which === _this.which) { x = event.pageX; y = event.pageY; _this.x += x - _this.lx; _this.y += y - _this.ly; _this.lx = x; _this.ly = y; return false; } return void 0; }); } MouseDrag.prototype.reset = function() { this.x = 0; return this.y = 0; }; return MouseDrag; })(); Camera = (function() { function Camera(delta, near, far) { this.delta = delta != null ? delta : 1 / 180; this.near = near != null ? near : 0.1; this.far = far != null ? far : 1000; this.last_gui_update = gettime(); this.time = gettime(); this.proj = new Mat4(); this.inv_proj = new Mat4(); this.view = new Mat4(); this.inv_view = new Mat4(); this.rot = new Mat3(); this.inv_rot = new Mat3(); this.acc = new Vec3(); } Camera.prototype.aspect = function(width, height) { this.proj.perspective(75, width / height, this.near, this.far); return this.inv_proj.inversePerspective(75, width / height, this.near, this.far); }; Camera.prototype.step = function() { this.accelerate(); this.limit(); this.move(); this.limit(); return this.time += this.delta; }; Camera.prototype.update = function() { var now; now = gettime(); if (now - this.last_gui_update > 0.5) { this.guiUpdate(); this.last_gui_update = now; } if (now - this.time > this.delta * 30) { this.time = now - this.delta * 30; } while (this.time < now) { this.step(); } this.finish(); this.view.invert(this.inv_view.identity()); this.view.toMat3(this.rot.identity()); return this.inv_view.toMat3(this.inv_rot.identity()); }; Camera.prototype.limit = function() {}; Camera.prototype.guiUpdate = function() {}; return Camera; })(); exports.GameCam = GameCam = (function(_super) { __extends(GameCam, _super); function GameCam(_arg) { var delta, x, y, z, _ref, _ref1, _ref2, _this = this; _ref = _arg != null ? _arg : {}, this.sl = _ref.sl, this.sr = _ref.sr, delta = _ref.delta, x = _ref.x, y = _ref.y, z = _ref.z; GameCam.__super__.constructor.call(this, delta); this.realpos = new Vec4(); if ((_ref1 = this.sl) == null) { this.sl = 200; } if ((_ref2 = this.sr) == null) { this.sr = 100; } if (x == null) { x = 0; } if (y == null) { y = 0; } if (z == null) { z = 0; } this.mouse = new MouseDrag(3); this.target_height = 0; this.height = 0; this.x = x; this.lx = x; this.z = z; this.lz = z; this.o = 0; this.lo = 0; this.d = 0; this.ld = 0; this.ad = 0; $(document).bind('mousewheel', function(event) { event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); _this.ad -= event.originalEvent.wheelDeltaY; return false; }); } GameCam.prototype.accelerate = function() { var ax, az, ctrl_x, ctrl_y, ctrl_z, move, sl, sr; sl = this.delta * this.delta * this.sl; sr = this.delta * this.delta * this.sr; ctrl_x = keys.a ? -1 : keys.d ? 1 : 0; ctrl_y = keys.q ? -1 : keys.e ? 1 : 0; ctrl_z = keys.w ? -1 : keys.s ? 1 : 0; ax = ctrl_x * sl; az = ctrl_z * sl; this.rot.identity().rotatey(-this.o).mulVal3(ax, 0, az, this.acc); this.x += this.acc.x; this.z += this.acc.z; this.o += this.mouse.x * sr; this.d += this.ad * this.delta * this.delta * 20; move = this.delta * this.delta * 4000; if (move > 1) { move = 1; } return this.height = this.height + (this.target_height - this.height) * move; }; GameCam.prototype.move = function() { var d, o, retl, retr, x, z; retl = 0.97; retr = 0.94; x = this.x + (this.x - this.lx) * retl; z = this.z + (this.z - this.lz) * retl; d = this.d + (this.d - this.ld) * retl; o = this.o + (this.o - this.lo) * retr; this.lx = this.x; this.x = x; this.lz = this.z; this.z = z; this.lo = this.o; this.o = o; this.ld = this.d; return this.d = d; }; GameCam.prototype.limit = function() { var high, low; if (this.d < 0) { this.d = 0; } else if (this.d > 30) { this.d = 30; } high = 128 + 64; low = 128 - 64; if (this.x < low) { this.x = low; } else if (this.x > high) { this.x = high; } if (this.z < low) { return this.z = low; } else if (this.z > high) { return this.z = high; } }; GameCam.prototype.finish = function() { this.mouse.reset(); this.ad = 0; return this.view.identity().translateVal3(0, 0, -this.d - 5).rotatex(25 + (this.d / 30) * 40).rotatey(this.o).translateVal3(-this.x, -this.height, -this.z); }; GameCam.prototype.update = function(picker) { var diff, h1, h2, real_height; this.view.identity().translateVal3(0, 0, -this.d - 5).rotatex(25 + (this.d / 30) * 40).rotatey(this.o).translateVal3(-this.x, 0, -this.z); this.view.invert(this.inv_view.identity()); h1 = picker.getHeight(this.x, this.z); this.inv_view.mulVal4(0, 0, 0, 1, this.realpos); h2 = picker.getHeight(this.realpos.x, this.realpos.z) + 2; real_height = h1 + this.realpos.y; if (real_height < h2) { diff = h2 - real_height; this.target_height = h1 + diff; } else { this.target_height = h1; } return GameCam.__super__.update.call(this); }; return GameCam; })(Camera); exports.Orbit = Orbit = (function(_super) { __extends(Orbit, _super); function Orbit(_arg) { var delta, x, y, z, _ref, _ref1, _ref2; _ref = _arg != null ? _arg : {}, this.sr = _ref.sr, delta = _ref.delta, x = _ref.x, y = _ref.y, z = _ref.z, this.dist = _ref.dist; Orbit.__super__.constructor.call(this, delta); if ((_ref1 = this.sr) == null) { this.sr = 100; } if ((_ref2 = this.dist) == null) { this.dist = 0.6; } this.mouse = new MouseDrag(1); this.o = 0; this.lo = 0; this.p = 0; this.lp = 0; } Orbit.prototype.accelerate = function() { var sr; sr = this.delta * this.delta * this.sr; this.o += this.mouse.x * sr; return this.p += this.mouse.y * sr; }; Orbit.prototype.move = function() { var o, p, retr; retr = 0.94; o = this.o + (this.o - this.lo) * retr; p = this.p + (this.p - this.lp) * retr; this.lo = this.o; this.o = o; this.lp = this.p; return this.p = p; }; Orbit.prototype.finish = function() { return this.mouse.reset(); }; Orbit.prototype.update = function() { this.view.identity().translateVal3(0, 0, -this.dist).rotatex(this.p).rotatey(this.o); this.view.invert(this.inv_view.identity()); return Orbit.__super__.update.call(this); }; return Orbit; })(Camera); exports.FlyCam = FlyCam = (function(_super) { __extends(FlyCam, _super); function FlyCam(_arg) { var delta, far, folder, lookbutton, near, o, p, x, y, z, _ref, _ref1, _ref2; _ref = _arg != null ? _arg : {}, this.sl = _ref.sl, this.gui = _ref.gui, this.sr = _ref.sr, delta = _ref.delta, near = _ref.near, far = _ref.far, lookbutton = _ref.lookbutton, x = _ref.x, y = _ref.y, z = _ref.z, o = _ref.o, p = _ref.p; this.guiChanged = __bind(this.guiChanged, this); FlyCam.__super__.constructor.call(this, delta, near, far); if ((_ref1 = this.sl) == null) { this.sl = 50; } if ((_ref2 = this.sr) == null) { this.sr = 100; } if (lookbutton == null) { lookbutton = 1; } if (x == null) { x = 0; } if (y == null) { y = 0; } if (z == null) { z = 0; } if (o == null) { o = 0; } if (p == null) { p = 0; } this.mouse = new MouseDrag(lookbutton); this.x = x; this.lx = x; this.y = y; this.ly = y; this.z = z; this.lz = z; this.o = o; this.lo = o; this.p = p; this.lp = p; folder = this.gui.addFolder('Camera'); this.gui.remember(this); this.xgui = folder.add(this, 'x', -30.0, 30.0).onChange(this.guiChanged); this.ygui = folder.add(this, 'y', -30.0, 30.0).onChange(this.guiChanged); this.zgui = folder.add(this, 'z', -30.0, 30.0).onChange(this.guiChanged); this.go = this.o; this.ogui = folder.add(this, 'go', 0.0, 360.0).name('Orientation').onChange(this.guiChanged); this.pgui = folder.add(this, 'p', -80.0, 80.0).name('Pitch').onChange(this.guiChanged); this.guiChanged(); } FlyCam.prototype.guiChanged = function() { this.lx = this.x; this.ly = this.y; this.lz = this.z; this.o = this.go; this.lo = this.go; return this.lp = this.p; }; FlyCam.prototype.guiUpdate = function() { this.go = this.o % 360; this.xgui.updateDisplay(); this.ygui.updateDisplay(); this.zgui.updateDisplay(); this.ogui.updateDisplay(); return this.pgui.updateDisplay(); }; FlyCam.prototype.accelerate = function() { var ax, ay, az, ctrl_x, ctrl_y, ctrl_z, sl, sr; sl = this.delta * this.delta * this.sl; sr = this.delta * this.delta * this.sr; ctrl_x = keys.a ? -1 : keys.d ? 1 : 0; ctrl_y = keys.q ? -1 : keys.e ? 1 : 0; ctrl_z = keys.w ? -1 : keys.s ? 1 : 0; ax = ctrl_x * sl; ay = ctrl_y * sl; az = ctrl_z * sl; this.rot.identity().rotatey(-this.o).rotatex(-this.p).mulVal3(ax, ay, az, this.acc); this.x += this.acc.x; this.y += this.acc.y; this.z += this.acc.z; this.o += this.mouse.x * sr; return this.p += this.mouse.y * sr; }; FlyCam.prototype.move = function() { var o, p, retl, retr, x, y, z; retl = 0.97; retr = 0.94; x = this.x + (this.x - this.lx) * retl; y = this.y + (this.y - this.ly) * retl; z = this.z + (this.z - this.lz) * retl; o = this.o + (this.o - this.lo) * retr; p = this.p + (this.p - this.lp) * retr; if (p > 80) { p = 80; } else if (p < -80) { p = -80; } this.lx = this.x; this.x = x; this.ly = this.y; this.y = y; this.lz = this.z; this.z = z; this.lo = this.o; this.o = o; this.lp = this.p; return this.p = p; }; FlyCam.prototype.finish = function() { this.mouse.reset(); return this.view.identity().rotatex(this.p).rotatey(this.o).translateVal3(-this.x, -this.y, -this.z); }; return FlyCam; })(Camera); ================================================ FILE: lib/events.coffee ================================================ return class Emitter constructor: -> @handlers = {} on: (name, callback) -> handlers = @handlers[name] if handlers == undefined handlers = @handlers[name] = [] handlers.push callback return @ trigger: (name, a1, a2, a3, a4, a5, a6) -> handlers = @handlers[name] if handlers != undefined for handler in handlers handler(a1, a2, a3, a4, a5, a6) return @ ================================================ FILE: lib/events.js ================================================ // Generated by CoffeeScript 1.3.3 var Emitter; return Emitter = (function() { function Emitter() { this.handlers = {}; } Emitter.prototype.on = function(name, callback) { var handlers; handlers = this.handlers[name]; if (handlers === void 0) { handlers = this.handlers[name] = []; } handlers.push(callback); return this; }; Emitter.prototype.trigger = function(name, a1, a2, a3, a4, a5, a6) { var handler, handlers, _i, _len; handlers = this.handlers[name]; if (handlers !== void 0) { for (_i = 0, _len = handlers.length; _i < _len; _i++) { handler = handlers[_i]; handler(a1, a2, a3, a4, a5, a6); } } return this; }; return Emitter; })(); ================================================ FILE: lib/geometry.js ================================================ // Generated by CoffeeScript 1.3.3 exports.AABB = (function() { function AABB(xmin, xmax, ymin, ymax, zmin, zmax) { this.xmin = xmin; this.xmax = xmax; this.ymin = ymin; this.ymax = ymax; this.zmin = zmin; this.zmax = zmax; } AABB.prototype.ray_intersect = function(ray) { var d, inv_x, inv_y, inv_z, o, tmax, tmin, ymax, ymin, zmax, zmin, _ref, _ref1, _ref2; o = ray.origin; d = ray.direction; inv_x = 1.0 / d.x; tmin = (this.xmin - o.x) * inv_x; tmax = (this.xmax - o.x) * inv_x; if (inv_x < 0) { _ref = [tmax, tmin], tmin = _ref[0], tmax = _ref[1]; } inv_y = 1.0 / d.y; ymin = (this.ymin - o.y) * inv_y; ymax = (this.ymax - o.y) * inv_y; if (inv_y < 0) { _ref1 = [ymax, ymin], ymin = _ref1[0], ymax = _ref1[1]; } if (tmin > ymax || ymin > tmax) { return null; } if (ymin > tmin) { tmin = ymin; } if (ymax < tmax) { tmax = ymax; } inv_z = 1.0 / d.z; zmin = (this.zmin - o.z) * inv_z; zmax = (this.zmax - o.z) * inv_z; if (inv_z < 0) { _ref2 = [zmax, zmin], zmin = _ref2[0], zmax = _ref2[1]; } if (tmin > zmax || zmin > tmax) { return null; } if (zmin > tmin) { tmin = zmin; } if (zmax < tmax) { tmax = zmax; } return [tmin, tmax]; }; return AABB; })(); exports.Ray = (function() { function Ray(origin, direction) { var _ref, _ref1; this.origin = origin; this.direction = direction; if ((_ref = this.origin) == null) { this.origin = new Vec4(); } if ((_ref1 = this.direction) == null) { this.direction = new Vec4(); } } Ray.prototype.interpolate = function(interval, vector) { var d, o, v; if (vector == null) { vector = new Vec4(); } o = this.origin; d = this.direction; v = vector; v.x = o.x + d.x * interval; v.y = o.y + d.y * interval; v.z = o.z + d.z * interval; v.w = o.w + d.w * interval; return vector; }; Ray.prototype.ray_nearest = function(ray) { var U, V, W, a, b, c, d, det, e, s, t; W = this.origin.sub(ray.origin, new Vec4); U = this.direction; V = ray.direction; a = U.dot(U); b = U.dot(V); c = V.dot(V); d = U.dot(W); e = V.dot(W); det = a * c - b * b; if (det === 0) { return null; } s = (b * e - c * d) / det; t = (a * e - b * d) / det; return [s, t]; }; Ray.prototype.point_distance = function(point) { var W, s; W = point.sub(this.origin, new Vec4); s = W.dot(this.direction) / this.direction.dot(this.direction); W.x -= this.direction.x * s; W.y -= this.direction.y * s; W.z -= this.direction.z * s; return Math.sqrt(W.dot(W)); }; return Ray; })(); exports.get_mouseray = function(x, y, inv_proj, inv_view, ray) { if (ray == null) { ray = new exports.Ray; } inv_proj.mulVal4(x, y, -1, 1, ray.direction); inv_view.mulVec3(ray.direction); ray.direction.w = 0; inv_view.mulVal4(0, 0, 0, 1, ray.origin); return ray; }; ================================================ FILE: lib/hdr_clear.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform vec4 clear_color; void main(){ gl_FragColor = clear_color; } ================================================ FILE: lib/keys.coffee ================================================ keymap = ({ 87: 'w', 65: 'a', 83: 's', 68: 'd', 81: 'q', 69: 'e', 37: 'left', 39: 'right', 38: 'up', 40: 'down', 13: 'enter', 27: 'esc', 32: 'space', 8: 'backspace', 16: 'shift', 17: 'ctrl', 18: 'alt', 91: 'start', 0: 'altc', 20: 'caps', 9: 'tab', 49: 'key1', 50: 'key2', 51: 'key3', 52: 'key4' }) key_handlers = {} keys = { press: (name, callback) -> handlers = key_handlers[name] = key_handlers[name] or [] handlers.push(callback) } $(document).keydown (event) -> if event.target == document.body name = keymap[event.which] keys[name] = true handlers = key_handlers[name] if handlers for handler in handlers handler() $(document).keyup (event) -> name = keymap[event.which] keys[name] = false return keys ================================================ FILE: lib/keys.js ================================================ // Generated by CoffeeScript 1.3.3 var key_handlers, keymap, keys; keymap = { 87: 'w', 65: 'a', 83: 's', 68: 'd', 81: 'q', 69: 'e', 37: 'left', 39: 'right', 38: 'up', 40: 'down', 13: 'enter', 27: 'esc', 32: 'space', 8: 'backspace', 16: 'shift', 17: 'ctrl', 18: 'alt', 91: 'start', 0: 'altc', 20: 'caps', 9: 'tab', 49: 'key1', 50: 'key2', 51: 'key3', 52: 'key4' }; key_handlers = {}; keys = { press: function(name, callback) { var handlers; handlers = key_handlers[name] = key_handlers[name] || []; return handlers.push(callback); } }; $(document).keydown(function(event) { var handler, handlers, name, _i, _len, _results; if (event.target === document.body) { name = keymap[event.which]; keys[name] = true; handlers = key_handlers[name]; if (handlers) { _results = []; for (_i = 0, _len = handlers.length; _i < _len; _i++) { handler = handlers[_i]; _results.push(handler()); } return _results; } } }); $(document).keyup(function(event) { var name; name = keymap[event.which]; return keys[name] = false; }); return keys; ================================================ FILE: lib/loading.coffee ================================================ ui = $('#ui') hidden = true container = $('
').css position: 'absolute' top: '50%' left: '50%' width: 200 height: 40 marginLeft: -100 marginTop: -20 label = $('
').appendTo(container).css position: 'absolute' top: 0 left: 0 width: 200 height: 20 textAlign: 'center' color: 'white' loading = $('
').appendTo(container).css position: 'absolute' top: 20 left: 0 width: 200 height: 20 border: '1px solid white' bar = null makeBar = -> loading.empty() bar = $('
').appendTo(loading).css position: 'absolute' top: 0 left: 0 width: 0 height: 20 backgroundColor: 'white' '-webkit-transition': 'width 0.7s' exports.show = (text) -> label.text text makeBar() container .fadeIn('slow') .appendTo(ui) exports.hide = -> container.fadeOut 'slow', -> container.detach() exports.progress = (factor) -> bar.width(factor*200) ================================================ FILE: lib/loading.js ================================================ // Generated by CoffeeScript 1.3.3 var bar, container, hidden, label, loading, makeBar, ui; ui = $('#ui'); hidden = true; container = $('
').css({ position: 'absolute', top: '50%', left: '50%', width: 200, height: 40, marginLeft: -100, marginTop: -20 }); label = $('
').appendTo(container).css({ position: 'absolute', top: 0, left: 0, width: 200, height: 20, textAlign: 'center', color: 'white' }); loading = $('
').appendTo(container).css({ position: 'absolute', top: 20, left: 0, width: 200, height: 20, border: '1px solid white' }); bar = null; makeBar = function() { loading.empty(); return bar = $('
').appendTo(loading).css({ position: 'absolute', top: 0, left: 0, width: 0, height: 20, backgroundColor: 'white', '-webkit-transition': 'width 0.7s' }); }; exports.show = function(text) { label.text(text); makeBar(); return container.fadeIn('slow').appendTo(ui); }; exports.hide = function() { return container.fadeOut('slow', function() { return container.detach(); }); }; exports.progress = function(factor) { return bar.width(factor * 200); }; ================================================ FILE: lib/rendernode.coffee ================================================ {Framebuffer, Depthbuffer} = require 'webgl/framebuffer' {Texture2D, Cubemap} = require 'webgl/texture' Quad = require 'webgl/quad' class State constructor: (@gl) -> @depthTest = false @depthWrite = false @cullFace = null @alphaToCoverage = false @blend = false setDefaults: -> @gl.disable @gl.DEPTH_TEST @gl.depthMask false @gl.cullFace @gl.BACK @gl.disable @gl.CULL_FACE @gl.disable @gl.SAMPLE_ALPHA_TO_COVERAGE @gl.disable @gl.BLEND return @ set: -> if @depthTest @gl.enable @gl.DEPTH_TEST mode = @gl[@depthTest] if mode @gl.depthFunc mode else @gl.depthFunc @gl.LEQUAL if @depthWrite then @gl.depthMask true if @cullFace @gl.enable @gl.CULL_FACE @gl.cullFace @gl[@cullFace] if @blend @gl.enable @gl.BLEND if @blend == 'additive' @gl.blendFunc @gl.ONE, @gl.ONE if @alphaToCoverage then @gl.enable @gl.SAMPLE_ALPHA_TO_COVERAGE return @ revert: -> if @depthTest @gl.disable @gl.DEPTH_TEST @gl.depthFunc @gl.LESS if @depthWrite then @gl.depthMask false if @cullFace @gl.disable @gl.CULL_FACE if @blend then @gl.disable @gl.BLEND if @alphaToCoverage then @gl.disable @gl.SAMPLE_ALPHA_TO_COVERAGE return @ default_state = null return class Rendernode @stateDefaults = (gl) -> default_state = new State(gl).setDefaults() constructor: (@gl, {@width, @height, @program, @drawable, @type, @front, depthTest, depthWrite, cullFace, @depthBuffer, blend, @filter, @channels, @format, @hdrClear}) -> @xoff = 0 @yoff = 0 @state = new State(@gl) @texunit_counter = 0 @texunits = {} @type ?= @gl.UNSIGNED_BYTE @front ?= false depthTest ?= false @depthTest depthTest depthWrite ?= false @depthWrite depthWrite cullFace ?= null @cullFace cullFace blend ?= false @state.blend = blend if not @front @createBuffers() if @hdrClear @clearShader = get 'hdr_clear.shader' createBuffers: -> @output = new Texture2D(@gl, channels:@channels, format:@format, type:@type).bind().clampToEdge() if @filter == 'nearest' @output.nearest() else @output.linear() if @width and @height @output.setSize(@width, @height) else @output.setSize(16, 16) @output.unbind() @fbo = new Framebuffer(@gl).bind().color(@output).unbind() if @depthBuffer then @addDepth() addDepth: (buffer=@depthBuffer) -> if not @depth and not @front if buffer instanceof Depthbuffer @depth = buffer else @depth = new Depthbuffer(@gl).setSize(@output.width, @output.height) @fbo.bind().depth(@depth).unbind() return @ cullFace: (side=null) -> @state.cullFace = side return @ depthWrite: (enabled=false) -> @state.depthWrite = enabled return @ depthTest: (enabled=false) -> @state.depthTest = enabled return @ alphaToCoverage: (enabled=false) -> @state.alphaToCoverage = enabled return @ blendAdditive: -> @state.blend = 'additive' return @ filterNearest: -> @output.bind().nearest().unbind() return @ start: -> @started = true @viewport() @state.set() if @program then @program.use() if not @front then @fbo.bind() if @drawable then @setPointers @drawable return @ setPointers: (drawable) -> if drawable != @current_drawable @current_drawable = drawable drawable.setPointersForShader @program end: -> @started = false @current_drawable = null @state.revert() if not @front then @fbo.unbind() return @ sampler: (name, source) -> if source.output texture = source.output else texture = source unit = @texunits[name] if unit == undefined unit = @texunits[name] = @texunit_counter++ texture.bind(unit) @program.i name, unit return @ mat4: (name, value) -> @program.mat4(name, value) return @ mat3: (name, value) -> @program.mat3(name, value) return @ val3: (name, x, y, z) -> @program.val3(name, x, y, z) return @ vec3: (name, value) -> @program.vec3(name, value) return @ f: (name, value) -> @program.f name, value return @ fv: (name, values) -> @program.fv name, values return @ val2: (name, x, y) -> @program.val2(name, x, y) return @ clear: (r=0, g=0, b=0, a=1) -> if @hdrClear if not @front then @fbo.bind() @clearShader.use().val4('clear_color', r, g, b, a).draw(quad) else @gl.clearColor r, g, b, a @gl.clear @gl.COLOR_BUFFER_BIT return @ clearBoth: (r=0, g=0, b=0, a=1, depth=1) -> @gl.clearColor r, g, b, a @gl.clearDepth depth @gl.clear @gl.COLOR_BUFFER_BIT | @gl.DEPTH_BUFFER_BIT return @ clearDepth: (depth=1) -> @gl.clearDepth depth @gl.clear @gl.DEPTH_BUFFER_BIT return @ draw: (drawable=@drawable) -> do_end = false if not @started do_end = true @start() @program.val2 'viewport', @width, @height if drawable != @current_drawable @setPointers drawable drawable.draw() if do_end @end() return @ drawModel: (texture_type, sampler_name=texture_type) -> if texture_type for material in @drawable.materials[texture_type] @f('specularity', material.specularity) c = material.diffuse_color @val3('diffuse_color', c.r, c.g, c.b) @sampler(sampler_name, material[texture_type]) @drawable.drawRange material.start, material.size else @draw() return @ resize: (width, height) -> @width = Math.floor(width) @height = Math.floor(height) if @output @output.bind() .setSize(@width, @height) .unbind() if @depth @depth.setSize(@width, @height) if @fbo @fbo.bind().check().unbind() viewport: (x=@xoff, y=@yoff, width=@width, height=@height) -> if width and height @xoff = x @yoff = y @width = width @height = height if @started @gl.viewport x, y, width, height return @ bind: (unit=0) -> @output.bind(unit) ================================================ FILE: lib/rendernode.js ================================================ // Generated by CoffeeScript 1.3.3 var Cubemap, Depthbuffer, Framebuffer, Quad, Rendernode, State, Texture2D, default_state, _ref, _ref1; _ref = require('webgl/framebuffer'), Framebuffer = _ref.Framebuffer, Depthbuffer = _ref.Depthbuffer; _ref1 = require('webgl/texture'), Texture2D = _ref1.Texture2D, Cubemap = _ref1.Cubemap; Quad = require('webgl/quad'); State = (function() { function State(gl) { this.gl = gl; this.depthTest = false; this.depthWrite = false; this.cullFace = null; this.alphaToCoverage = false; this.blend = false; } State.prototype.setDefaults = function() { this.gl.disable(this.gl.DEPTH_TEST); this.gl.depthMask(false); this.gl.cullFace(this.gl.BACK); this.gl.disable(this.gl.CULL_FACE); this.gl.disable(this.gl.SAMPLE_ALPHA_TO_COVERAGE); this.gl.disable(this.gl.BLEND); return this; }; State.prototype.set = function() { var mode; if (this.depthTest) { this.gl.enable(this.gl.DEPTH_TEST); mode = this.gl[this.depthTest]; if (mode) { this.gl.depthFunc(mode); } else { this.gl.depthFunc(this.gl.LEQUAL); } } if (this.depthWrite) { this.gl.depthMask(true); } if (this.cullFace) { this.gl.enable(this.gl.CULL_FACE); this.gl.cullFace(this.gl[this.cullFace]); } if (this.blend) { this.gl.enable(this.gl.BLEND); if (this.blend === 'additive') { this.gl.blendFunc(this.gl.ONE, this.gl.ONE); } } if (this.alphaToCoverage) { this.gl.enable(this.gl.SAMPLE_ALPHA_TO_COVERAGE); } return this; }; State.prototype.revert = function() { if (this.depthTest) { this.gl.disable(this.gl.DEPTH_TEST); this.gl.depthFunc(this.gl.LESS); } if (this.depthWrite) { this.gl.depthMask(false); } if (this.cullFace) { this.gl.disable(this.gl.CULL_FACE); } if (this.blend) { this.gl.disable(this.gl.BLEND); } if (this.alphaToCoverage) { this.gl.disable(this.gl.SAMPLE_ALPHA_TO_COVERAGE); } return this; }; return State; })(); default_state = null; return Rendernode = (function() { Rendernode.stateDefaults = function(gl) { return default_state = new State(gl).setDefaults(); }; function Rendernode(gl, _arg) { var blend, cullFace, depthTest, depthWrite, _ref2, _ref3; this.gl = gl; this.width = _arg.width, this.height = _arg.height, this.program = _arg.program, this.drawable = _arg.drawable, this.type = _arg.type, this.front = _arg.front, depthTest = _arg.depthTest, depthWrite = _arg.depthWrite, cullFace = _arg.cullFace, this.depthBuffer = _arg.depthBuffer, blend = _arg.blend, this.filter = _arg.filter, this.channels = _arg.channels, this.format = _arg.format, this.hdrClear = _arg.hdrClear; this.xoff = 0; this.yoff = 0; this.state = new State(this.gl); this.texunit_counter = 0; this.texunits = {}; if ((_ref2 = this.type) == null) { this.type = this.gl.UNSIGNED_BYTE; } if ((_ref3 = this.front) == null) { this.front = false; } if (depthTest == null) { depthTest = false; } this.depthTest(depthTest); if (depthWrite == null) { depthWrite = false; } this.depthWrite(depthWrite); if (cullFace == null) { cullFace = null; } this.cullFace(cullFace); if (blend == null) { blend = false; } this.state.blend = blend; if (!this.front) { this.createBuffers(); } if (this.hdrClear) { this.clearShader = get('hdr_clear.shader'); } } Rendernode.prototype.createBuffers = function() { this.output = new Texture2D(this.gl, { channels: this.channels, format: this.format, type: this.type }).bind().clampToEdge(); if (this.filter === 'nearest') { this.output.nearest(); } else { this.output.linear(); } if (this.width && this.height) { this.output.setSize(this.width, this.height); } else { this.output.setSize(16, 16); } this.output.unbind(); this.fbo = new Framebuffer(this.gl).bind().color(this.output).unbind(); if (this.depthBuffer) { return this.addDepth(); } }; Rendernode.prototype.addDepth = function(buffer) { if (buffer == null) { buffer = this.depthBuffer; } if (!this.depth && !this.front) { if (buffer instanceof Depthbuffer) { this.depth = buffer; } else { this.depth = new Depthbuffer(this.gl).setSize(this.output.width, this.output.height); } this.fbo.bind().depth(this.depth).unbind(); } return this; }; Rendernode.prototype.cullFace = function(side) { if (side == null) { side = null; } this.state.cullFace = side; return this; }; Rendernode.prototype.depthWrite = function(enabled) { if (enabled == null) { enabled = false; } this.state.depthWrite = enabled; return this; }; Rendernode.prototype.depthTest = function(enabled) { if (enabled == null) { enabled = false; } this.state.depthTest = enabled; return this; }; Rendernode.prototype.alphaToCoverage = function(enabled) { if (enabled == null) { enabled = false; } this.state.alphaToCoverage = enabled; return this; }; Rendernode.prototype.blendAdditive = function() { this.state.blend = 'additive'; return this; }; Rendernode.prototype.filterNearest = function() { this.output.bind().nearest().unbind(); return this; }; Rendernode.prototype.start = function() { this.started = true; this.viewport(); this.state.set(); if (this.program) { this.program.use(); } if (!this.front) { this.fbo.bind(); } if (this.drawable) { this.setPointers(this.drawable); } return this; }; Rendernode.prototype.setPointers = function(drawable) { if (drawable !== this.current_drawable) { this.current_drawable = drawable; return drawable.setPointersForShader(this.program); } }; Rendernode.prototype.end = function() { this.started = false; this.current_drawable = null; this.state.revert(); if (!this.front) { this.fbo.unbind(); } return this; }; Rendernode.prototype.sampler = function(name, source) { var texture, unit; if (source.output) { texture = source.output; } else { texture = source; } unit = this.texunits[name]; if (unit === void 0) { unit = this.texunits[name] = this.texunit_counter++; } texture.bind(unit); this.program.i(name, unit); return this; }; Rendernode.prototype.mat4 = function(name, value) { this.program.mat4(name, value); return this; }; Rendernode.prototype.mat3 = function(name, value) { this.program.mat3(name, value); return this; }; Rendernode.prototype.val3 = function(name, x, y, z) { this.program.val3(name, x, y, z); return this; }; Rendernode.prototype.vec3 = function(name, value) { this.program.vec3(name, value); return this; }; Rendernode.prototype.f = function(name, value) { this.program.f(name, value); return this; }; Rendernode.prototype.fv = function(name, values) { this.program.fv(name, values); return this; }; Rendernode.prototype.val2 = function(name, x, y) { this.program.val2(name, x, y); return this; }; Rendernode.prototype.clear = function(r, g, b, a) { if (r == null) { r = 0; } if (g == null) { g = 0; } if (b == null) { b = 0; } if (a == null) { a = 1; } if (this.hdrClear) { if (!this.front) { this.fbo.bind(); } this.clearShader.use().val4('clear_color', r, g, b, a).draw(quad); } else { this.gl.clearColor(r, g, b, a); this.gl.clear(this.gl.COLOR_BUFFER_BIT); } return this; }; Rendernode.prototype.clearBoth = function(r, g, b, a, depth) { if (r == null) { r = 0; } if (g == null) { g = 0; } if (b == null) { b = 0; } if (a == null) { a = 1; } if (depth == null) { depth = 1; } this.gl.clearColor(r, g, b, a); this.gl.clearDepth(depth); this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); return this; }; Rendernode.prototype.clearDepth = function(depth) { if (depth == null) { depth = 1; } this.gl.clearDepth(depth); this.gl.clear(this.gl.DEPTH_BUFFER_BIT); return this; }; Rendernode.prototype.draw = function(drawable) { var do_end; if (drawable == null) { drawable = this.drawable; } do_end = false; if (!this.started) { do_end = true; this.start(); } this.program.val2('viewport', this.width, this.height); if (drawable !== this.current_drawable) { this.setPointers(drawable); } drawable.draw(); if (do_end) { this.end(); } return this; }; Rendernode.prototype.drawModel = function(texture_type, sampler_name) { var c, material, _i, _len, _ref2; if (sampler_name == null) { sampler_name = texture_type; } if (texture_type) { _ref2 = this.drawable.materials[texture_type]; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { material = _ref2[_i]; this.f('specularity', material.specularity); c = material.diffuse_color; this.val3('diffuse_color', c.r, c.g, c.b); this.sampler(sampler_name, material[texture_type]); this.drawable.drawRange(material.start, material.size); } } else { this.draw(); } return this; }; Rendernode.prototype.resize = function(width, height) { this.width = Math.floor(width); this.height = Math.floor(height); if (this.output) { this.output.bind().setSize(this.width, this.height).unbind(); } if (this.depth) { this.depth.setSize(this.width, this.height); } if (this.fbo) { return this.fbo.bind().check().unbind(); } }; Rendernode.prototype.viewport = function(x, y, width, height) { if (x == null) { x = this.xoff; } if (y == null) { y = this.yoff; } if (width == null) { width = this.width; } if (height == null) { height = this.height; } if (width && height) { this.xoff = x; this.yoff = y; this.width = width; this.height = height; if (this.started) { this.gl.viewport(x, y, width, height); } return this; } }; Rendernode.prototype.bind = function(unit) { if (unit == null) { unit = 0; } return this.output.bind(unit); }; return Rendernode; })(); ================================================ FILE: lib/schedule.coffee ================================================ exports.run = (callback) -> last = gettime() step = -> current = gettime() delta = current-last last = current callback current, delta requestAnimationFrame step requestAnimationFrame step ================================================ FILE: lib/schedule.js ================================================ // Generated by CoffeeScript 1.3.3 exports.run = function(callback) { var last, step; last = gettime(); step = function() { var current, delta; current = gettime(); delta = current - last; last = current; callback(current, delta); return requestAnimationFrame(step); }; return requestAnimationFrame(step); }; ================================================ FILE: lib/webgl/cube.coffee ================================================ return class Cube extends require('drawable') attribs: ['position', 'normal', 'barycentric'] pointers: [ {name: 'position', size: 3, offset: 0, stride: 9}, {name: 'normal', size: 3, offset: 3, stride: 9}, {name: 'barycentric', size: 3, offset: 6, stride: 9}, ] constructor: (@gl, s=1) -> super() @size = 6 * 6 vertices = [ -s, -s, -s, 0, 0, -1, 1,0,0, -s, s, -s, 0, 0, -1, 0,1,0, s, s, -s, 0, 0, -1, 0,0,1, s, -s, -s, 0, 0, -1, 1,0,0, -s, -s, -s, 0, 0, -1, 0,1,0, s, s, -s, 0, 0, -1, 0,0,1, s, s, s, 0, 0, 1, 1,0,0, -s, s, s, 0, 0, 1, 0,1,0, -s, -s, s, 0, 0, 1, 0,0,1, s, s, s, 0, 0, 1, 1,0,0, -s, -s, s, 0, 0, 1, 0,1,0, s, -s, s, 0, 0, 1, 0,0,1, -s, s, -s, 0, 1, 0, 1,0,0, -s, s, s, 0, 1, 0, 0,1,0, s, s, s, 0, 1, 0, 0,0,1, s, s, -s, 0, 1, 0, 1,0,0, -s, s, -s, 0, 1, 0, 0,1,0, s, s, s, 0, 1, 0, 0,0,1, s, -s, s, 0, -1, 0, 1,0,0, -s, -s, s, 0, -1, 0, 0,1,0, -s, -s, -s, 0, -1, 0, 0,0,1, s, -s, s, 0, -1, 0, 1,0,0, -s, -s, -s, 0, -1, 0, 0,1,0, s, -s, -s, 0, -1, 0, 0,0,1, -s, -s, -s, -1, 0, 0, 1,0,0, -s, -s, s, -1, 0, 0, 0,1,0, -s, s, s, -1, 0, 0, 0,0,1, -s, s, -s, -1, 0, 0, 1,0,0, -s, -s, -s, -1, 0, 0, 0,1,0, -s, s, s, -1, 0, 0, 0,0,1, s, s, s, 1, 0, 0, 1,0,0, s, -s, s, 1, 0, 0, 0,1,0, s, -s, -s, 1, 0, 0, 0,0,1, s, s, s, 1, 0, 0, 1,0,0, s, -s, -s, 1, 0, 0, 0,1,0, s, s, -s, 1, 0, 0, 0,0,1, ] @uploadList vertices ================================================ FILE: lib/webgl/cube.js ================================================ // Generated by CoffeeScript 1.3.3 var Cube, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Cube = (function(_super) { __extends(Cube, _super); Cube.prototype.attribs = ['position', 'normal', 'barycentric']; Cube.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 9 }, { name: 'normal', size: 3, offset: 3, stride: 9 }, { name: 'barycentric', size: 3, offset: 6, stride: 9 } ]; function Cube(gl, s) { var vertices; this.gl = gl; if (s == null) { s = 1; } Cube.__super__.constructor.call(this); this.size = 6 * 6; vertices = [-s, -s, -s, 0, 0, -1, 1, 0, 0, -s, s, -s, 0, 0, -1, 0, 1, 0, s, s, -s, 0, 0, -1, 0, 0, 1, s, -s, -s, 0, 0, -1, 1, 0, 0, -s, -s, -s, 0, 0, -1, 0, 1, 0, s, s, -s, 0, 0, -1, 0, 0, 1, s, s, s, 0, 0, 1, 1, 0, 0, -s, s, s, 0, 0, 1, 0, 1, 0, -s, -s, s, 0, 0, 1, 0, 0, 1, s, s, s, 0, 0, 1, 1, 0, 0, -s, -s, s, 0, 0, 1, 0, 1, 0, s, -s, s, 0, 0, 1, 0, 0, 1, -s, s, -s, 0, 1, 0, 1, 0, 0, -s, s, s, 0, 1, 0, 0, 1, 0, s, s, s, 0, 1, 0, 0, 0, 1, s, s, -s, 0, 1, 0, 1, 0, 0, -s, s, -s, 0, 1, 0, 0, 1, 0, s, s, s, 0, 1, 0, 0, 0, 1, s, -s, s, 0, -1, 0, 1, 0, 0, -s, -s, s, 0, -1, 0, 0, 1, 0, -s, -s, -s, 0, -1, 0, 0, 0, 1, s, -s, s, 0, -1, 0, 1, 0, 0, -s, -s, -s, 0, -1, 0, 0, 1, 0, s, -s, -s, 0, -1, 0, 0, 0, 1, -s, -s, -s, -1, 0, 0, 1, 0, 0, -s, -s, s, -1, 0, 0, 0, 1, 0, -s, s, s, -1, 0, 0, 0, 0, 1, -s, s, -s, -1, 0, 0, 1, 0, 0, -s, -s, -s, -1, 0, 0, 0, 1, 0, -s, s, s, -1, 0, 0, 0, 0, 1, s, s, s, 1, 0, 0, 1, 0, 0, s, -s, s, 1, 0, 0, 0, 1, 0, s, -s, -s, 1, 0, 0, 0, 0, 1, s, s, s, 1, 0, 0, 1, 0, 0, s, -s, -s, 1, 0, 0, 0, 1, 0, s, s, -s, 1, 0, 0, 0, 0, 1]; this.uploadList(vertices); } return Cube; })(require('drawable')); ================================================ FILE: lib/webgl/drawable.coffee ================================================ return class Drawable float_size = Float32Array.BYTES_PER_ELEMENT constructor: () -> @first = 0 @size = 0 @buffer = @gl.createBuffer() @mode = @gl.TRIANGLES setPointer: (shader, name, size=3, start=0, stride=0) -> location = shader.attribLoc name if location >= 0 @gl.vertexAttribPointer location, size, @gl.FLOAT, false, stride*float_size, start*float_size return @ setPointersForShader: (shader) -> @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer for pointer in @pointers @setPointer shader, pointer.name, pointer.size, pointer.offset, pointer.stride return @ draw: (shader) -> if shader then @setPointersForShader shader @gl.drawArrays @mode, @first, @size if shader then @disableAttribs shader return @ drawRange: (start=@first, size=@size) -> @gl.drawArrays @mode, start, size disableAttribs: (shader) -> for name in @attribs location = shader.attribLoc name if location >= 0 then @gl.disableVertexAttribArray location return @ uploadList: (list) -> data = new Float32Array list @upload data upload: (data) -> @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer @gl.bufferData @gl.ARRAY_BUFFER, data, @gl.STATIC_DRAW @gl.bindBuffer @gl.ARRAY_BUFFER, null ================================================ FILE: lib/webgl/drawable.js ================================================ // Generated by CoffeeScript 1.3.3 var Drawable; return Drawable = (function() { var float_size; float_size = Float32Array.BYTES_PER_ELEMENT; function Drawable() { this.first = 0; this.size = 0; this.buffer = this.gl.createBuffer(); this.mode = this.gl.TRIANGLES; } Drawable.prototype.setPointer = function(shader, name, size, start, stride) { var location; if (size == null) { size = 3; } if (start == null) { start = 0; } if (stride == null) { stride = 0; } location = shader.attribLoc(name); if (location >= 0) { this.gl.vertexAttribPointer(location, size, this.gl.FLOAT, false, stride * float_size, start * float_size); } return this; }; Drawable.prototype.setPointersForShader = function(shader) { var pointer, _i, _len, _ref; this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); _ref = this.pointers; for (_i = 0, _len = _ref.length; _i < _len; _i++) { pointer = _ref[_i]; this.setPointer(shader, pointer.name, pointer.size, pointer.offset, pointer.stride); } return this; }; Drawable.prototype.draw = function(shader) { if (shader) { this.setPointersForShader(shader); } this.gl.drawArrays(this.mode, this.first, this.size); if (shader) { this.disableAttribs(shader); } return this; }; Drawable.prototype.drawRange = function(start, size) { if (start == null) { start = this.first; } if (size == null) { size = this.size; } return this.gl.drawArrays(this.mode, start, size); }; Drawable.prototype.disableAttribs = function(shader) { var location, name, _i, _len, _ref; _ref = this.attribs; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; location = shader.attribLoc(name); if (location >= 0) { this.gl.disableVertexAttribArray(location); } } return this; }; Drawable.prototype.uploadList = function(list) { var data; data = new Float32Array(list); return this.upload(data); }; Drawable.prototype.upload = function(data) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.gl.bufferData(this.gl.ARRAY_BUFFER, data, this.gl.STATIC_DRAW); return this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null); }; return Drawable; })(); ================================================ FILE: lib/webgl/framebuffer.coffee ================================================ framebufferBinding = null exports.Framebuffer = class Framebuffer constructor: (@gl) -> @buffer = @gl.createFramebuffer() bind: -> if framebufferBinding isnt @ @gl.bindFramebuffer @gl.FRAMEBUFFER, @buffer framebufferBinding = @ return @ unbind: -> if framebufferBinding isnt null @gl.bindFramebuffer @gl.FRAMEBUFFER, null framebufferBinding = null return @ check: -> result = @gl.checkFramebufferStatus @gl.FRAMEBUFFER switch result when @gl.FRAMEBUFFER_UNSUPPORTED throw 'Framebuffer is unsupported' when @gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT throw 'Framebuffer incomplete attachment' when @gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS throw 'Framebuffer incomplete dimensions' when @gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT throw 'Framebuffer incomplete missing attachment' return @ color: (texture, target=texture.target) -> @gl.framebufferTexture2D @gl.FRAMEBUFFER, @gl.COLOR_ATTACHMENT0, target, texture.handle, 0 @check() return @ depth: (buffer) -> @gl.framebufferRenderbuffer @gl.FRAMEBUFFER, @gl.DEPTH_ATTACHMENT, @gl.RENDERBUFFER, buffer.id @check() return @ destroy: -> @gl.deleteFramebuffer @buffer class Renderbuffer constructor: (@gl) -> @id = @gl.createRenderbuffer() bind: -> @gl.bindRenderbuffer @gl.RENDERBUFFER, @id return @ setSize: (@width, @height) -> @bind() @gl.renderbufferStorage @gl.RENDERBUFFER, @gl[@format], @width, @height @unbind() unbind: -> @gl.bindRenderbuffer @gl.RENDERBUFFER, null return @ exports.Depthbuffer = class extends Renderbuffer format: 'DEPTH_COMPONENT16' ================================================ FILE: lib/webgl/framebuffer.js ================================================ // Generated by CoffeeScript 1.3.3 var Framebuffer, Renderbuffer, framebufferBinding, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; framebufferBinding = null; exports.Framebuffer = Framebuffer = (function() { function Framebuffer(gl) { this.gl = gl; this.buffer = this.gl.createFramebuffer(); } Framebuffer.prototype.bind = function() { if (framebufferBinding !== this) { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffer); framebufferBinding = this; } return this; }; Framebuffer.prototype.unbind = function() { if (framebufferBinding !== null) { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); framebufferBinding = null; } return this; }; Framebuffer.prototype.check = function() { var result; result = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER); switch (result) { case this.gl.FRAMEBUFFER_UNSUPPORTED: throw 'Framebuffer is unsupported'; break; case this.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: throw 'Framebuffer incomplete attachment'; break; case this.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: throw 'Framebuffer incomplete dimensions'; break; case this.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: throw 'Framebuffer incomplete missing attachment'; } return this; }; Framebuffer.prototype.color = function(texture, target) { if (target == null) { target = texture.target; } this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, target, texture.handle, 0); this.check(); return this; }; Framebuffer.prototype.depth = function(buffer) { this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, buffer.id); this.check(); return this; }; Framebuffer.prototype.destroy = function() { return this.gl.deleteFramebuffer(this.buffer); }; return Framebuffer; })(); Renderbuffer = (function() { function Renderbuffer(gl) { this.gl = gl; this.id = this.gl.createRenderbuffer(); } Renderbuffer.prototype.bind = function() { this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.id); return this; }; Renderbuffer.prototype.setSize = function(width, height) { this.width = width; this.height = height; this.bind(); this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl[this.format], this.width, this.height); return this.unbind(); }; Renderbuffer.prototype.unbind = function() { this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null); return this; }; return Renderbuffer; })(); exports.Depthbuffer = (function(_super) { __extends(_Class, _super); function _Class() { return _Class.__super__.constructor.apply(this, arguments); } _Class.prototype.format = 'DEPTH_COMPONENT16'; return _Class; })(Renderbuffer); ================================================ FILE: lib/webgl/hexgrid.coffee ================================================ clamp = (value, left, right) -> return if value < left then left else if value > right then right else value return class Hexgrid extends require('drawable') attribs: ['position', 'texcoord', 'barycentric'] constructor: (@gl, xsize=16, ysize=16, width=1, height=1) -> super() vertices = [] for x in [0..xsize] x1 = clamp((x-0.5)/xsize, 0, 1) x2 = clamp((x+0.0)/xsize, 0, 1) x3 = clamp((x+0.5)/xsize, 0, 1) x4 = clamp((x+1.0)/xsize, 0, 1) for y in [0...ysize] by 2 t = (y+0)/ysize m = (y+1)/ysize b = (y+2)/ysize vertices.push( x2*width,0,m*height, x2,m, 0,0,1, x3*width,0,t*height, x3,t, 0,1,0, x1*width,0,t*height, x1,t, 1,0,0, x4*width,0,m*height, x4,m, 0,0,1, x3*width,0,t*height, x3,t, 0,1,0, x2*width,0,m*height, x2,m, 1,0,0, x3*width,0,b*height, x3,b, 0,0,1, x2*width,0,m*height, x2,m, 0,1,0, x1*width,0,b*height, x1,b, 1,0,0, x3*width,0,b*height, x3,b, 0,0,1 x4*width,0,m*height, x4,m, 0,1,0, x2*width,0,m*height, x2,m, 1,0,0, ) @size = vertices.length/8 @uploadList vertices setPointersForShader: (shader) -> @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer @setPointer shader, 'position', 3, 0, 8 @setPointer shader, 'texcoord', 2, 3, 8 @setPointer shader, 'barycentric', 3, 5, 8 return @ ================================================ FILE: lib/webgl/hexgrid.js ================================================ // Generated by CoffeeScript 1.3.3 var Hexgrid, clamp, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; clamp = function(value, left, right) { if (value < left) { return left; } else if (value > right) { return right; } else { return value; } }; return Hexgrid = (function(_super) { __extends(Hexgrid, _super); Hexgrid.prototype.attribs = ['position', 'texcoord', 'barycentric']; function Hexgrid(gl, xsize, ysize, width, height) { var b, m, t, vertices, x, x1, x2, x3, x4, y, _i, _j; this.gl = gl; if (xsize == null) { xsize = 16; } if (ysize == null) { ysize = 16; } if (width == null) { width = 1; } if (height == null) { height = 1; } Hexgrid.__super__.constructor.call(this); vertices = []; for (x = _i = 0; 0 <= xsize ? _i <= xsize : _i >= xsize; x = 0 <= xsize ? ++_i : --_i) { x1 = clamp((x - 0.5) / xsize, 0, 1); x2 = clamp((x + 0.0) / xsize, 0, 1); x3 = clamp((x + 0.5) / xsize, 0, 1); x4 = clamp((x + 1.0) / xsize, 0, 1); for (y = _j = 0; _j < ysize; y = _j += 2) { t = (y + 0) / ysize; m = (y + 1) / ysize; b = (y + 2) / ysize; vertices.push(x2 * width, 0, m * height, x2, m, 0, 0, 1, x3 * width, 0, t * height, x3, t, 0, 1, 0, x1 * width, 0, t * height, x1, t, 1, 0, 0, x4 * width, 0, m * height, x4, m, 0, 0, 1, x3 * width, 0, t * height, x3, t, 0, 1, 0, x2 * width, 0, m * height, x2, m, 1, 0, 0, x3 * width, 0, b * height, x3, b, 0, 0, 1, x2 * width, 0, m * height, x2, m, 0, 1, 0, x1 * width, 0, b * height, x1, b, 1, 0, 0, x3 * width, 0, b * height, x3, b, 0, 0, 1, x4 * width, 0, m * height, x4, m, 0, 1, 0, x2 * width, 0, m * height, x2, m, 1, 0, 0); } } this.size = vertices.length / 8; this.uploadList(vertices); } Hexgrid.prototype.setPointersForShader = function(shader) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.setPointer(shader, 'position', 3, 0, 8); this.setPointer(shader, 'texcoord', 2, 3, 8); this.setPointer(shader, 'barycentric', 3, 5, 8); return this; }; return Hexgrid; })(require('drawable')); ================================================ FILE: lib/webgl/model.coffee ================================================ return class Model extends require('drawable') attribs: ['position', 'normal', 'texcoord'] constructor: (@gl, data) -> super() @size = data.byteLength/(8*Float32Array.BYTES_PER_ELEMENT) @upload data setPointersForShader: (shader) -> @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer @setPointer shader, 'position', 3, 0, 8 @setPointer shader, 'normal', 3, 3, 8 @setPointer shader, 'texcoord', 2, 6, 8 return @ ================================================ FILE: lib/webgl/model.js ================================================ // Generated by CoffeeScript 1.3.3 var Model, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Model = (function(_super) { __extends(Model, _super); Model.prototype.attribs = ['position', 'normal', 'texcoord']; function Model(gl, data) { this.gl = gl; Model.__super__.constructor.call(this); this.size = data.byteLength / (8 * Float32Array.BYTES_PER_ELEMENT); this.upload(data); } Model.prototype.setPointersForShader = function(shader) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.setPointer(shader, 'position', 3, 0, 8); this.setPointer(shader, 'normal', 3, 3, 8); this.setPointer(shader, 'texcoord', 2, 6, 8); return this; }; return Model; })(require('drawable')); ================================================ FILE: lib/webgl/plane.coffee ================================================ return class Plane extends require('drawable') attribs: ['position', 'normal', 'texcoord'] constructor: (@gl, s=1) -> super() @size = 6 vertices = [ -s, 0, -s, 0, 1, 0, 0,0, -s, 0, s, 0, 1, 0, 0,1, s, 0, s, 0, 1, 0, 1,1, s, 0, -s, 0, 1, 0, 1,0, -s, 0, -s, 0, 1, 0, 0,0, s, 0, s, 0, 1, 0, 1,1, ] @uploadList vertices setPointersForShader: (shader) -> @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer @setPointer shader, 'position', 3, 0, 8 @setPointer shader, 'normal', 3, 3, 8 @setPointer shader, 'texcoord', 2, 6, 8 return @ ================================================ FILE: lib/webgl/plane.js ================================================ // Generated by CoffeeScript 1.3.3 var Plane, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Plane = (function(_super) { __extends(Plane, _super); Plane.prototype.attribs = ['position', 'normal', 'texcoord']; function Plane(gl, s) { var vertices; this.gl = gl; if (s == null) { s = 1; } Plane.__super__.constructor.call(this); this.size = 6; vertices = [-s, 0, -s, 0, 1, 0, 0, 0, -s, 0, s, 0, 1, 0, 0, 1, s, 0, s, 0, 1, 0, 1, 1, s, 0, -s, 0, 1, 0, 1, 0, -s, 0, -s, 0, 1, 0, 0, 0, s, 0, s, 0, 1, 0, 1, 1]; this.uploadList(vertices); } Plane.prototype.setPointersForShader = function(shader) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.setPointer(shader, 'position', 3, 0, 8); this.setPointer(shader, 'normal', 3, 3, 8); this.setPointer(shader, 'texcoord', 2, 6, 8); return this; }; return Plane; })(require('drawable')); ================================================ FILE: lib/webgl/quad.coffee ================================================ return class Quad extends require('drawable') attribs: ['position'] pointers: [ {name: 'position', size: 2, offset: 0, stride: 2}, ] constructor: (@gl) -> super() @size = 6 vertices = [ -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, ] @uploadList vertices ================================================ FILE: lib/webgl/quad.js ================================================ // Generated by CoffeeScript 1.3.3 var Quad, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Quad = (function(_super) { __extends(Quad, _super); Quad.prototype.attribs = ['position']; Quad.prototype.pointers = [ { name: 'position', size: 2, offset: 0, stride: 2 } ]; function Quad(gl) { var vertices; this.gl = gl; Quad.__super__.constructor.call(this); this.size = 6; vertices = [-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]; this.uploadList(vertices); } return Quad; })(require('drawable')); ================================================ FILE: lib/webgl/shader.coffee ================================================ directives = [ '#ifdef GL_FRAGMENT_PRECISION_HIGH', 'precision highp int;', 'precision highp float;', '#else', 'precision mediump int;', 'precision mediump float;', '#endif', ] in_use = null return class Shader @lastError = '' @splitLines = (path, source) -> result = [] for line, i in source.split '\n' result.push line: i text: line path: path return result @error = 'ShaderError' constructor: (@gl, @path, source) -> dirname = @path.split '/' dirname.pop() @dirname = dirname.join '/' @program = @gl.createProgram() @vs = @gl.createShader gl.VERTEX_SHADER @fs = @gl.createShader gl.FRAGMENT_SHADER @gl.attachShader @program, @vs @gl.attachShader @program, @fs @link source preprocess: (source) -> lines = source.split '\n' shaders = {'global': [], 'fragment': [], 'vertex': []} type = 'global' for line, i in lines match = line.match /^(\w+):$/ if match type = match[1] else shaders[type].push line: i text: line path: @path global = @resolveLines(shaders.global) shaders.fragment = global.concat(@resolveLines(shaders.fragment)) shaders.vertex = global.concat(@resolveLines(shaders.vertex)) return shaders resolveLines: (stage) -> result = [] for line in stage match = line.text.match /^\s+#require (\S+)\s*$/ if match path = "#{match[1]}.shaderlib" abspath = loader.resolvePath(@dirname, path) lib = get abspath for line in lib result.push line else result.push line return result concat: (stage) -> result = '' for line in directives result += line + '\n' result += '#line 0\n' for line in stage result += line.text + '\n' return result link: (source) -> shaders = @preprocess source @compile @vs, shaders.vertex @compile @fs, shaders.fragment @gl.linkProgram @program if not @gl.getProgramParameter @program, @gl.LINK_STATUS error = "Shader Link Error for file: #{@path}:\n#{@gl.getProgramInfoLog(@program)}" console.error error Shader.lastError = error throw Shader.error @attrib_cache = {} @uniform_cache = {} @value_cache = {} compile: (shader, lines) -> source = @concat lines @gl.shaderSource shader, source @gl.compileShader shader if not @gl.getShaderParameter shader, @gl.COMPILE_STATUS error = @gl.getShaderInfoLog(shader) group = "Shader Compile Error for file: #{@path}" translated = @translateError(error, lines) text = group + '\n' + translated Shader.lastError = text console.group group console.warn translated console.groupEnd() throw Shader.error return translateError: (error, sourcelines) -> result = [] for line, i in error.split('\n') match = line.match /ERROR: \d+:(\d+): (.*)/ if match lineno = parseFloat(match[1]) message = match[2] sourceline = sourcelines[lineno-1] result.push "ERROR: Line #{sourceline.line+1}: File #{sourceline.path}: #{message} SOURCE: #{sourceline.text}" else result.push line return result.join('\n') attribLoc: (name) -> location = @attrib_cache[name] if location is undefined location = @attrib_cache[name] = @gl.getAttribLocation @program, name @gl.enableVertexAttribArray location if location >= 0 return location use: -> if @ != in_use in_use = @ @gl.useProgram @program return @ unbind: -> if in_use in_use = null @gl.useProgram null return @ loc: (name) -> location = @uniform_cache[name] if location is undefined location = @uniform_cache[name] = @gl.getUniformLocation @program, name return location i: (name, value) -> cached = @value_cache[name] if cached != value @value_cache[name] = value loc = @loc name @gl.uniform1i loc, value if loc return @ f: (name, value) -> cached = @value_cache[name] if cached != value @value_cache[name] = value loc = @loc name @gl.uniform1f loc, value if loc return @ fv: (name, values) -> loc = @loc name @gl.uniform1fv loc, values if loc return @ val2: (name, a, b) -> cached = @value_cache[name] if cached if cached.a != a or cached.b != b cached.a = a; cached.b = b loc = @loc name @gl.uniform2f loc, a, b if loc else @value_cache[name] = {a:a, b:b} loc = @loc name @gl.uniform2f loc, a, b if loc return @ val3: (name, a, b, c) -> cached = @value_cache[name] if cached if cached.a != a or cached.b != b or cached.c != c cached.a = a; cached.b = b; cached.c = c loc = @loc name @gl.uniform3f loc, a, b, c if loc else @value_cache[name] = {a:a, b:b, c:c} loc = @loc name @gl.uniform3f loc, a, b, c if loc return @ vec2: (name, value) -> loc = @loc name @gl.uniform2fv loc, value if loc return @ vec3: (name, value) -> loc = @loc name @gl.uniform3fv loc, value if loc return @ val4: (name, a, b, c, d) -> loc = @loc name @gl.uniform4f loc, a, b, c, d if loc return @ vec4: (name, a, b, c, e) -> loc = @loc name @gl.uniform2f loc, a, b, c, e if loc return @ mat4: (name, value) -> loc = @loc name if loc if value instanceof Mat4 @gl.uniformMatrix4fv loc, @gl.FALSE, value.data else @gl.uniformMatrix4fv loc, @gl.FALSE, value return @ mat3: (name, value) -> loc = @loc name @gl.uniformMatrix3fv loc, @gl.FALSE, value.data if loc return @ draw: (drawable) -> drawable .setPointersForShader(@) .draw() .disableAttribs(@) return @ ================================================ FILE: lib/webgl/shader.js ================================================ // Generated by CoffeeScript 1.3.3 var Shader, directives, in_use; directives = ['#ifdef GL_FRAGMENT_PRECISION_HIGH', 'precision highp int;', 'precision highp float;', '#else', 'precision mediump int;', 'precision mediump float;', '#endif']; in_use = null; return Shader = (function() { Shader.lastError = ''; Shader.splitLines = function(path, source) { var i, line, result, _i, _len, _ref; result = []; _ref = source.split('\n'); for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { line = _ref[i]; result.push({ line: i, text: line, path: path }); } return result; }; Shader.error = 'ShaderError'; function Shader(gl, path, source) { var dirname; this.gl = gl; this.path = path; dirname = this.path.split('/'); dirname.pop(); this.dirname = dirname.join('/'); this.program = this.gl.createProgram(); this.vs = this.gl.createShader(gl.VERTEX_SHADER); this.fs = this.gl.createShader(gl.FRAGMENT_SHADER); this.gl.attachShader(this.program, this.vs); this.gl.attachShader(this.program, this.fs); this.link(source); } Shader.prototype.preprocess = function(source) { var global, i, line, lines, match, shaders, type, _i, _len; lines = source.split('\n'); shaders = { 'global': [], 'fragment': [], 'vertex': [] }; type = 'global'; for (i = _i = 0, _len = lines.length; _i < _len; i = ++_i) { line = lines[i]; match = line.match(/^(\w+):$/); if (match) { type = match[1]; } else { shaders[type].push({ line: i, text: line, path: this.path }); } } global = this.resolveLines(shaders.global); shaders.fragment = global.concat(this.resolveLines(shaders.fragment)); shaders.vertex = global.concat(this.resolveLines(shaders.vertex)); return shaders; }; Shader.prototype.resolveLines = function(stage) { var abspath, lib, line, match, path, result, _i, _j, _len, _len1; result = []; for (_i = 0, _len = stage.length; _i < _len; _i++) { line = stage[_i]; match = line.text.match(/^\s+#require (\S+)\s*$/); if (match) { path = "" + match[1] + ".shaderlib"; abspath = loader.resolvePath(this.dirname, path); lib = get(abspath); for (_j = 0, _len1 = lib.length; _j < _len1; _j++) { line = lib[_j]; result.push(line); } } else { result.push(line); } } return result; }; Shader.prototype.concat = function(stage) { var line, result, _i, _j, _len, _len1; result = ''; for (_i = 0, _len = directives.length; _i < _len; _i++) { line = directives[_i]; result += line + '\n'; } result += '#line 0\n'; for (_j = 0, _len1 = stage.length; _j < _len1; _j++) { line = stage[_j]; result += line.text + '\n'; } return result; }; Shader.prototype.link = function(source) { var error, shaders; shaders = this.preprocess(source); this.compile(this.vs, shaders.vertex); this.compile(this.fs, shaders.fragment); this.gl.linkProgram(this.program); if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) { error = "Shader Link Error for file: " + this.path + ":\n" + (this.gl.getProgramInfoLog(this.program)); console.error(error); Shader.lastError = error; throw Shader.error; } this.attrib_cache = {}; this.uniform_cache = {}; return this.value_cache = {}; }; Shader.prototype.compile = function(shader, lines) { var error, group, source, text, translated; source = this.concat(lines); this.gl.shaderSource(shader, source); this.gl.compileShader(shader); if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { error = this.gl.getShaderInfoLog(shader); group = "Shader Compile Error for file: " + this.path; translated = this.translateError(error, lines); text = group + '\n' + translated; Shader.lastError = text; console.group(group); console.warn(translated); console.groupEnd(); throw Shader.error; } }; Shader.prototype.translateError = function(error, sourcelines) { var i, line, lineno, match, message, result, sourceline, _i, _len, _ref; result = []; _ref = error.split('\n'); for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { line = _ref[i]; match = line.match(/ERROR: \d+:(\d+): (.*)/); if (match) { lineno = parseFloat(match[1]); message = match[2]; sourceline = sourcelines[lineno - 1]; result.push("ERROR: Line " + (sourceline.line + 1) + ": File " + sourceline.path + ": " + message + " SOURCE: " + sourceline.text); } else { result.push(line); } } return result.join('\n'); }; Shader.prototype.attribLoc = function(name) { var location; location = this.attrib_cache[name]; if (location === void 0) { location = this.attrib_cache[name] = this.gl.getAttribLocation(this.program, name); } if (location >= 0) { this.gl.enableVertexAttribArray(location); } return location; }; Shader.prototype.use = function() { if (this !== in_use) { in_use = this; this.gl.useProgram(this.program); } return this; }; Shader.prototype.unbind = function() { if (in_use) { in_use = null; this.gl.useProgram(null); } return this; }; Shader.prototype.loc = function(name) { var location; location = this.uniform_cache[name]; if (location === void 0) { location = this.uniform_cache[name] = this.gl.getUniformLocation(this.program, name); } return location; }; Shader.prototype.i = function(name, value) { var cached, loc; cached = this.value_cache[name]; if (cached !== value) { this.value_cache[name] = value; loc = this.loc(name); if (loc) { this.gl.uniform1i(loc, value); } } return this; }; Shader.prototype.f = function(name, value) { var cached, loc; cached = this.value_cache[name]; if (cached !== value) { this.value_cache[name] = value; loc = this.loc(name); if (loc) { this.gl.uniform1f(loc, value); } } return this; }; Shader.prototype.fv = function(name, values) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform1fv(loc, values); } return this; }; Shader.prototype.val2 = function(name, a, b) { var cached, loc; cached = this.value_cache[name]; if (cached) { if (cached.a !== a || cached.b !== b) { cached.a = a; cached.b = b; loc = this.loc(name); if (loc) { this.gl.uniform2f(loc, a, b); } } } else { this.value_cache[name] = { a: a, b: b }; loc = this.loc(name); if (loc) { this.gl.uniform2f(loc, a, b); } } return this; }; Shader.prototype.val3 = function(name, a, b, c) { var cached, loc; cached = this.value_cache[name]; if (cached) { if (cached.a !== a || cached.b !== b || cached.c !== c) { cached.a = a; cached.b = b; cached.c = c; loc = this.loc(name); if (loc) { this.gl.uniform3f(loc, a, b, c); } } } else { this.value_cache[name] = { a: a, b: b, c: c }; loc = this.loc(name); if (loc) { this.gl.uniform3f(loc, a, b, c); } } return this; }; Shader.prototype.vec2 = function(name, value) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform2fv(loc, value); } return this; }; Shader.prototype.vec3 = function(name, value) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform3fv(loc, value); } return this; }; Shader.prototype.val4 = function(name, a, b, c, d) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform4f(loc, a, b, c, d); } return this; }; Shader.prototype.vec4 = function(name, a, b, c, e) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform2f(loc, a, b, c, e); } return this; }; Shader.prototype.mat4 = function(name, value) { var loc; loc = this.loc(name); if (loc) { if (value instanceof Mat4) { this.gl.uniformMatrix4fv(loc, this.gl.FALSE, value.data); } else { this.gl.uniformMatrix4fv(loc, this.gl.FALSE, value); } } return this; }; Shader.prototype.mat3 = function(name, value) { var loc; loc = this.loc(name); if (loc) { this.gl.uniformMatrix3fv(loc, this.gl.FALSE, value.data); } return this; }; Shader.prototype.draw = function(drawable) { drawable.setPointersForShader(this).draw().disableAttribs(this); return this; }; return Shader; })(); ================================================ FILE: lib/webgl/sphere.coffee ================================================ phi = (1+Math.sqrt(5))/2 midp = (v1, v2) -> x1 = v1[0] y1 = v1[1] z1 = v1[2] x2 = v2[0] y2 = v2[1] z2 = v2[2] x3 = (x1+x2)/2 y3 = (y1+y2)/2 z3 = (z1+z2)/2 return [x3, y3, z3] normalize = (faces, r) -> r ?= 1 result = [] for face in faces new_face = [] result.push new_face for vertex in face x = vertex[0] y = vertex[1] z = vertex[2] l = Math.sqrt(x*x + y*y + z*z) new_face.push [(r*x)/l, (r*y)/l, (r*z)/l] return result subdivide = (faces) -> result = [] for face in faces v0 = face[0] v1 = face[1] v2 = face[2] va = midp v0, v1 vb = midp v1, v2 vc = midp v2, v0 result.push( [v0, va, vc], [va, v1, vb], [vc, vb, v2], [va, vb, vc], ) return result v1 = [ 1, phi, 0] v2 = [ -1, phi, 0] v3 = [ 0, 1, phi] v4 = [ 0, 1, -phi] v5 = [ phi, 0, 1] v6 = [-phi, 0, 1] v7 = [-phi, 0, -1] v8 = [ phi, 0, -1] v9 = [ 0, -1, phi] v10 = [ 0, -1, -phi] v11 = [ -1, -phi, 0] v12 = [ 1, -phi, 0] faces = [ [ v1, v2, v3], [ v2, v1, v4], [ v1, v3, v5], [ v2, v6, v3], [ v2, v7, v6], [ v2, v4, v7], [ v1, v5, v8], [ v1, v8, v4], [ v9, v3, v6], [ v3, v9, v5], [ v4, v10, v7], [ v4, v8, v10], [ v6, v7, v11], [ v6, v11, v9], [ v7, v10, v11], [ v5, v12, v8], [v12, v5, v9], [v12, v10, v8], [v11, v12, v9], [v12, v11, v10] ] icosahedron = normalize(faces) vertexlist = (faces) -> vertices = [] for face in faces for vertex in face x = vertex[0] y = vertex[1] z = vertex[2] vertices.push x, y, z return vertices return class Sphere extends require('drawable') attribs: ['position'] pointers: [ {name: 'position', size: 3, offset: 0, stride: 3}, ] @makeVertices = (radius=1, subdivisions=3) -> template = icosahedron for i in [0...subdivisions] template = subdivide template template = normalize template faces = normalize template, radius vertices = vertexlist faces return vertices constructor: (@gl, radius=1, subdivisions=3) -> super() vertices = Sphere.makeVertices radius, subdivisions @size = vertices.length/3 @uploadList vertices ================================================ FILE: lib/webgl/sphere.js ================================================ // Generated by CoffeeScript 1.3.3 var Sphere, faces, icosahedron, midp, normalize, phi, subdivide, v1, v10, v11, v12, v2, v3, v4, v5, v6, v7, v8, v9, vertexlist, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; phi = (1 + Math.sqrt(5)) / 2; midp = function(v1, v2) { var x1, x2, x3, y1, y2, y3, z1, z2, z3; x1 = v1[0]; y1 = v1[1]; z1 = v1[2]; x2 = v2[0]; y2 = v2[1]; z2 = v2[2]; x3 = (x1 + x2) / 2; y3 = (y1 + y2) / 2; z3 = (z1 + z2) / 2; return [x3, y3, z3]; }; normalize = function(faces, r) { var face, l, new_face, result, vertex, x, y, z, _i, _j, _len, _len1; if (r == null) { r = 1; } result = []; for (_i = 0, _len = faces.length; _i < _len; _i++) { face = faces[_i]; new_face = []; result.push(new_face); for (_j = 0, _len1 = face.length; _j < _len1; _j++) { vertex = face[_j]; x = vertex[0]; y = vertex[1]; z = vertex[2]; l = Math.sqrt(x * x + y * y + z * z); new_face.push([(r * x) / l, (r * y) / l, (r * z) / l]); } } return result; }; subdivide = function(faces) { var face, result, v0, v1, v2, va, vb, vc, _i, _len; result = []; for (_i = 0, _len = faces.length; _i < _len; _i++) { face = faces[_i]; v0 = face[0]; v1 = face[1]; v2 = face[2]; va = midp(v0, v1); vb = midp(v1, v2); vc = midp(v2, v0); result.push([v0, va, vc], [va, v1, vb], [vc, vb, v2], [va, vb, vc]); } return result; }; v1 = [1, phi, 0]; v2 = [-1, phi, 0]; v3 = [0, 1, phi]; v4 = [0, 1, -phi]; v5 = [phi, 0, 1]; v6 = [-phi, 0, 1]; v7 = [-phi, 0, -1]; v8 = [phi, 0, -1]; v9 = [0, -1, phi]; v10 = [0, -1, -phi]; v11 = [-1, -phi, 0]; v12 = [1, -phi, 0]; faces = [[v1, v2, v3], [v2, v1, v4], [v1, v3, v5], [v2, v6, v3], [v2, v7, v6], [v2, v4, v7], [v1, v5, v8], [v1, v8, v4], [v9, v3, v6], [v3, v9, v5], [v4, v10, v7], [v4, v8, v10], [v6, v7, v11], [v6, v11, v9], [v7, v10, v11], [v5, v12, v8], [v12, v5, v9], [v12, v10, v8], [v11, v12, v9], [v12, v11, v10]]; icosahedron = normalize(faces); vertexlist = function(faces) { var face, vertex, vertices, x, y, z, _i, _j, _len, _len1; vertices = []; for (_i = 0, _len = faces.length; _i < _len; _i++) { face = faces[_i]; for (_j = 0, _len1 = face.length; _j < _len1; _j++) { vertex = face[_j]; x = vertex[0]; y = vertex[1]; z = vertex[2]; vertices.push(x, y, z); } } return vertices; }; return Sphere = (function(_super) { __extends(Sphere, _super); Sphere.prototype.attribs = ['position']; Sphere.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 3 } ]; Sphere.makeVertices = function(radius, subdivisions) { var i, template, vertices, _i; if (radius == null) { radius = 1; } if (subdivisions == null) { subdivisions = 3; } template = icosahedron; for (i = _i = 0; 0 <= subdivisions ? _i < subdivisions : _i > subdivisions; i = 0 <= subdivisions ? ++_i : --_i) { template = subdivide(template); template = normalize(template); } faces = normalize(template, radius); vertices = vertexlist(faces); return vertices; }; function Sphere(gl, radius, subdivisions) { var vertices; this.gl = gl; if (radius == null) { radius = 1; } if (subdivisions == null) { subdivisions = 3; } Sphere.__super__.constructor.call(this); vertices = Sphere.makeVertices(radius, subdivisions); this.size = vertices.length / 3; this.uploadList(vertices); } return Sphere; })(require('drawable')); ================================================ FILE: lib/webgl/texture.coffee ================================================ {Framebuffer} = require 'framebuffer' class Texture bound = [] ids = 0 constructor: -> @handle = @gl.createTexture() @id = ids++ @unit = null bind: (unit=0) -> @unit = unit if bound[unit] != @id @gl.activeTexture @gl.TEXTURE0+unit @gl.bindTexture @target, @handle bound[unit] = @id return @ unbind: (unit=@unit) -> if unit and bound[unit] == @id @gl.activeTexture @gl.TEXTURE0+unit @gl.bindTexture @target, null bound[unit] = null return @ mipmap: -> @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.LINEAR @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR_MIPMAP_LINEAR @gl.generateMipmap @target return @ mipmapNearest: -> @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.NEAREST @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR_MIPMAP_LINEAR @gl.generateMipmap @target return @ linear: -> @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.LINEAR @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR return @ nearest: -> @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.NEAREST @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.NEAREST return @ clampToEdge: -> @gl.texParameteri @target, @gl.TEXTURE_WRAP_S, @gl.CLAMP_TO_EDGE @gl.texParameteri @target, @gl.TEXTURE_WRAP_T, @gl.CLAMP_TO_EDGE return @ repeat: -> @gl.texParameteri @target, @gl.TEXTURE_WRAP_S, @gl.REPEAT @gl.texParameteri @target, @gl.TEXTURE_WRAP_T, @gl.REPEAT return @ anisotropy: -> ext = @gl.getExtension 'WEBKIT_EXT_texture_filter_anisotropic' if ext max = @gl.getParameter ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT @gl.texParameterf @target, ext.TEXTURE_MAX_ANISOTROPY_EXT, max return @ exports.Texture2D = class Texture2D extends Texture constructor: (@gl, {@channels, @format, @type}={}) -> super() @channels ?= @gl.RGBA @format ?= @gl.RGBA @type ?= @gl.UNSIGNED_BYTE @target = @gl.TEXTURE_2D upload: (image) -> @uploadImage image return @ uploadImage: (image) -> @width = image.width @height = image.height @gl.texImage2D @target, 0, @channels, @format, @type, image return @ uploadData: (data, @width, @height) -> @gl.texImage2D @target, 0, @channels, width, height, 0, @format, @type, data return @ setSize: (@width, @height) -> @gl.texImage2D @target, 0, @channels, width, height, 0, @format, @type, null return @ read: (dst=new Uint8Array(@width*@height*4)) -> if @fbo @fbo.bind() else @fbo = new Framebuffer(@gl).bind().color(@) @gl.readPixels 0, 0, @width, @height, @gl.RGBA, @gl.UNSIGNED_BYTE, dst @fbo.unbind() return dst toPNG: -> canvas = document.createElement 'canvas' canvas.height = @height canvas.width = @width ctx = canvas.getContext '2d' imgdata = ctx.createImageData @width, @height imgdata.data.set @read(), 0 ctx.putImageData imgdata, 0, 0 url = canvas.toDataURL 'image/png' data = atob(url.split(',')[1]) result = new Uint8Array(data.length) for i in [0...data.length] result[i] = data.charCodeAt i return result exports.Cubemap = class Cubemap extends Texture constructor: (@gl) -> super() @target = @gl.TEXTURE_CUBE_MAP @up = @gl.TEXTURE_CUBE_MAP_POSITIVE_Y @down = @gl.TEXTURE_CUBE_MAP_NEGATIVE_Y @right = @gl.TEXTURE_CUBE_MAP_POSITIVE_X @left = @gl.TEXTURE_CUBE_MAP_NEGATIVE_X @back = @gl.TEXTURE_CUBE_MAP_NEGATIVE_Z @front = @gl.TEXTURE_CUBE_MAP_POSITIVE_Z uploadSide: (name, image) -> @gl.texImage2D @gl['TEXTURE_CUBE_MAP_' + name], 0, @gl.RGBA, @gl.RGBA, @gl.UNSIGNED_BYTE, image upload: (folder, ext='jpg') -> @uploadSide 'POSITIVE_Y', folder.get "up.#{ext}" @uploadSide 'NEGATIVE_Y', folder.get "down.#{ext}" @uploadSide 'POSITIVE_X', folder.get "right.#{ext}" @uploadSide 'NEGATIVE_X', folder.get "left.#{ext}" @uploadSide 'POSITIVE_Z', folder.get "front.#{ext}" @uploadSide 'NEGATIVE_Z', folder.get "back.#{ext}" return @ setSize: (@size) -> @gl.texImage2D @gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null @gl.texImage2D @gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null @gl.texImage2D @gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null @gl.texImage2D @gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null @gl.texImage2D @gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null @gl.texImage2D @gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null return @ ================================================ FILE: lib/webgl/texture.js ================================================ // Generated by CoffeeScript 1.3.3 var Cubemap, Framebuffer, Texture, Texture2D, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Framebuffer = require('framebuffer').Framebuffer; Texture = (function() { var bound, ids; bound = []; ids = 0; function Texture() { this.handle = this.gl.createTexture(); this.id = ids++; this.unit = null; } Texture.prototype.bind = function(unit) { if (unit == null) { unit = 0; } this.unit = unit; if (bound[unit] !== this.id) { this.gl.activeTexture(this.gl.TEXTURE0 + unit); this.gl.bindTexture(this.target, this.handle); bound[unit] = this.id; } return this; }; Texture.prototype.unbind = function(unit) { if (unit == null) { unit = this.unit; } if (unit && bound[unit] === this.id) { this.gl.activeTexture(this.gl.TEXTURE0 + unit); this.gl.bindTexture(this.target, null); bound[unit] = null; } return this; }; Texture.prototype.mipmap = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR); this.gl.generateMipmap(this.target); return this; }; Texture.prototype.mipmapNearest = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR); this.gl.generateMipmap(this.target); return this; }; Texture.prototype.linear = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); return this; }; Texture.prototype.nearest = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST); return this; }; Texture.prototype.clampToEdge = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); return this; }; Texture.prototype.repeat = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT); this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.REPEAT); return this; }; Texture.prototype.anisotropy = function() { var ext, max; ext = this.gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); if (ext) { max = this.gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT); this.gl.texParameterf(this.target, ext.TEXTURE_MAX_ANISOTROPY_EXT, max); } return this; }; return Texture; })(); exports.Texture2D = Texture2D = (function(_super) { __extends(Texture2D, _super); function Texture2D(gl, _arg) { var _ref, _ref1, _ref2, _ref3; this.gl = gl; _ref = _arg != null ? _arg : {}, this.channels = _ref.channels, this.format = _ref.format, this.type = _ref.type; Texture2D.__super__.constructor.call(this); if ((_ref1 = this.channels) == null) { this.channels = this.gl.RGBA; } if ((_ref2 = this.format) == null) { this.format = this.gl.RGBA; } if ((_ref3 = this.type) == null) { this.type = this.gl.UNSIGNED_BYTE; } this.target = this.gl.TEXTURE_2D; } Texture2D.prototype.upload = function(image) { this.uploadImage(image); return this; }; Texture2D.prototype.uploadImage = function(image) { this.width = image.width; this.height = image.height; this.gl.texImage2D(this.target, 0, this.channels, this.format, this.type, image); return this; }; Texture2D.prototype.uploadData = function(data, width, height) { this.width = width; this.height = height; this.gl.texImage2D(this.target, 0, this.channels, width, height, 0, this.format, this.type, data); return this; }; Texture2D.prototype.setSize = function(width, height) { this.width = width; this.height = height; this.gl.texImage2D(this.target, 0, this.channels, width, height, 0, this.format, this.type, null); return this; }; Texture2D.prototype.read = function(dst) { if (dst == null) { dst = new Uint8Array(this.width * this.height * 4); } if (this.fbo) { this.fbo.bind(); } else { this.fbo = new Framebuffer(this.gl).bind().color(this); } this.gl.readPixels(0, 0, this.width, this.height, this.gl.RGBA, this.gl.UNSIGNED_BYTE, dst); this.fbo.unbind(); return dst; }; Texture2D.prototype.toPNG = function() { var canvas, ctx, data, i, imgdata, result, url, _i, _ref; canvas = document.createElement('canvas'); canvas.height = this.height; canvas.width = this.width; ctx = canvas.getContext('2d'); imgdata = ctx.createImageData(this.width, this.height); imgdata.data.set(this.read(), 0); ctx.putImageData(imgdata, 0, 0); url = canvas.toDataURL('image/png'); data = atob(url.split(',')[1]); result = new Uint8Array(data.length); for (i = _i = 0, _ref = data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { result[i] = data.charCodeAt(i); } return result; }; return Texture2D; })(Texture); exports.Cubemap = Cubemap = (function(_super) { __extends(Cubemap, _super); function Cubemap(gl) { this.gl = gl; Cubemap.__super__.constructor.call(this); this.target = this.gl.TEXTURE_CUBE_MAP; this.up = this.gl.TEXTURE_CUBE_MAP_POSITIVE_Y; this.down = this.gl.TEXTURE_CUBE_MAP_NEGATIVE_Y; this.right = this.gl.TEXTURE_CUBE_MAP_POSITIVE_X; this.left = this.gl.TEXTURE_CUBE_MAP_NEGATIVE_X; this.back = this.gl.TEXTURE_CUBE_MAP_NEGATIVE_Z; this.front = this.gl.TEXTURE_CUBE_MAP_POSITIVE_Z; } Cubemap.prototype.uploadSide = function(name, image) { return this.gl.texImage2D(this.gl['TEXTURE_CUBE_MAP_' + name], 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image); }; Cubemap.prototype.upload = function(folder, ext) { if (ext == null) { ext = 'jpg'; } this.uploadSide('POSITIVE_Y', folder.get("up." + ext)); this.uploadSide('NEGATIVE_Y', folder.get("down." + ext)); this.uploadSide('POSITIVE_X', folder.get("right." + ext)); this.uploadSide('NEGATIVE_X', folder.get("left." + ext)); this.uploadSide('POSITIVE_Z', folder.get("front." + ext)); this.uploadSide('NEGATIVE_Z', folder.get("back." + ext)); return this; }; Cubemap.prototype.setSize = function(size) { this.size = size; this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); return this; }; return Cubemap; })(Texture); ================================================ FILE: lib/webgl-nuke-vendor-prefix.coffee ================================================ if window.WebGLRenderingContext? vendors = ['WEBKIT', 'MOZ', 'MS', 'O'] vendorRe = /^WEBKIT_(.*)|MOZ_(.*)|MS_(.*)|O_(.*)/ getExtension = WebGLRenderingContext.prototype.getExtension WebGLRenderingContext.prototype.getExtension = (name) -> match = name.match vendorRe if match != null name = match[1] extobj = getExtension.call @, name if extobj == null for vendor in vendors extobj = getExtension.call @, vendor + '_' + name if extobj != null return extobj return null else return extobj getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions WebGLRenderingContext.prototype.getSupportedExtensions = -> supported = getSupportedExtensions.call @ result = [] for extension in supported match = extension.match vendorRe if match != null extension = match[1] if extension not in result result.push extension return result ================================================ FILE: lib/webgl-nuke-vendor-prefix.js ================================================ // Generated by CoffeeScript 1.3.3 var getExtension, getSupportedExtensions, vendorRe, vendors, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; if (window.WebGLRenderingContext != null) { vendors = ['WEBKIT', 'MOZ', 'MS', 'O']; vendorRe = /^WEBKIT_(.*)|MOZ_(.*)|MS_(.*)|O_(.*)/; getExtension = WebGLRenderingContext.prototype.getExtension; WebGLRenderingContext.prototype.getExtension = function(name) { var extobj, match, vendor, _i, _len; match = name.match(vendorRe); if (match !== null) { name = match[1]; } extobj = getExtension.call(this, name); if (extobj === null) { for (_i = 0, _len = vendors.length; _i < _len; _i++) { vendor = vendors[_i]; extobj = getExtension.call(this, vendor + '_' + name); if (extobj !== null) { return extobj; } } return null; } else { return extobj; } }; getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions; WebGLRenderingContext.prototype.getSupportedExtensions = function() { var extension, match, result, supported, _i, _len; supported = getSupportedExtensions.call(this); result = []; for (_i = 0, _len = supported.length; _i < _len; _i++) { extension = supported[_i]; match = extension.match(vendorRe); if (match !== null) { extension = match[1]; } if (__indexOf.call(result, extension) < 0) { result.push(extension); } } return result; }; } ================================================ FILE: lib/webgl-texture-float-extension-shims.coffee ================================================ createSourceCanvas = -> canvas = document.createElement 'canvas' canvas.width = 2 canvas.height = 2 ctx = canvas.getContext '2d' imageData = ctx.getImageData(0, 0, 2, 2) imageData.data.set(new Uint8ClampedArray([ 0,0,0,0, 255,255,255,255, 0,0,0,0, 255,255,255,255, ])) ctx.putImageData(imageData, 0, 0) return canvas createSourceCanvas() checkFloatLinear = (gl, sourceType) -> ## drawing program ## program = gl.createProgram() vertexShader = gl.createShader(gl.VERTEX_SHADER) gl.attachShader(program, vertexShader) gl.shaderSource(vertexShader, ''' attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } ''') gl.compileShader(vertexShader) if not gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) throw gl.getShaderInfoLog(vertexShader) fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) gl.attachShader(program, fragmentShader) gl.shaderSource(fragmentShader, ''' uniform sampler2D source; void main(){ gl_FragColor = texture2D(source, vec2(1.0, 1.0)); } ''') gl.compileShader(fragmentShader) if not gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) throw gl.getShaderInfoLog(fragmentShader) gl.linkProgram(program) if not gl.getProgramParameter(program, gl.LINK_STATUS) throw gl.getProgramInfoLog(program) gl.useProgram(program) cleanup = -> gl.deleteShader(fragmentShader) gl.deleteShader(vertexShader) gl.deleteProgram(program) gl.deleteBuffer(buffer) gl.deleteTexture(source) gl.deleteTexture(target) gl.deleteFramebuffer(framebuffer) gl.bindBuffer(gl.ARRAY_BUFFER, null) gl.useProgram(null) gl.bindTexture(gl.TEXTURE_2D, null) gl.bindFramebuffer(gl.FRAMEBUFFER, null) ## target FBO ## target = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, target) gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null, ) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) framebuffer = gl.createFramebuffer() gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer) gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0 ) ## source texture ## sourceCanvas = createSourceCanvas() source = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, source) gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, sourceType, sourceCanvas, ) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) ## create VBO ## vertices = new Float32Array([ 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, ]) buffer = gl.createBuffer() gl.bindBuffer(gl.ARRAY_BUFFER, buffer) gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW) positionLoc = gl.getAttribLocation(program, 'position') sourceLoc = gl.getUniformLocation(program, 'source') gl.enableVertexAttribArray(positionLoc) gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0) gl.uniform1i(sourceLoc, 0) gl.drawArrays(gl.TRIANGLES, 0, 6) readBuffer = new Uint8Array(4*4) gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer) result = Math.abs(readBuffer[0] - 127) < 10 cleanup() return result checkTexture = (gl, targetType) -> target = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, target) gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null, ) if gl.getError() == 0 gl.deleteTexture(target) return true else gl.deleteTexture(target) return false checkColorBuffer = (gl, targetType) -> target = gl.createTexture() gl.bindTexture(gl.TEXTURE_2D, target) gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null, ) framebuffer = gl.createFramebuffer() gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer) gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0 ) check = gl.checkFramebufferStatus(gl.FRAMEBUFFER) gl.deleteTexture(target) gl.deleteFramebuffer(framebuffer) gl.bindTexture(gl.TEXTURE_2D, null) gl.bindFramebuffer(gl.FRAMEBUFFER, null) if check == gl.FRAMEBUFFER_COMPLETE return true else return false shimExtensions = [] shimLookup = {} unshimExtensions = [] checkSupport = -> canvas = document.createElement 'canvas' gl = null try gl = canvas.getContext 'experimental-webgl' if(gl == null) gl = canvas.getContext 'webgl' if gl? singleFloatExt = gl.getExtension 'OES_texture_float' if singleFloatExt == null if checkTexture gl, gl.FLOAT singleFloatTexturing = true shimExtensions.push 'OES_texture_float' shimLookup.OES_texture_float = {shim:true} else singleFloatTexturing = false unshimExtensions.push 'OES_texture_float' else if checkTexture gl, gl.FLOAT singleFloatTexturing = true shimExtensions.push 'OES_texture_float' else singleFloatTexturing = false unshimExtensions.push 'OES_texture_float' if singleFloatTexturing extobj = gl.getExtension 'WEBGL_color_buffer_float' if extobj == null if checkColorBuffer gl, gl.FLOAT shimExtensions.push 'WEBGL_color_buffer_float' shimLookup.WEBGL_color_buffer_float = { shim: true RGBA32F_EXT: 0x8814 RGB32F_EXT: 0x8815 FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211 UNSIGNED_NORMALIZED_EXT: 0x8C17 } else unshimExtensions.push 'WEBGL_color_buffer_float' else if checkColorBuffer gl, gl.FLOAT shimExtensions.push 'WEBGL_color_buffer_float' else unshimExtensions.push 'WEBGL_color_buffer_float' extobj = gl.getExtension 'OES_texture_float_linear' if extobj == null if checkFloatLinear gl, gl.FLOAT shimExtensions.push 'OES_texture_float_linear' shimLookup.OES_texture_float_linear = {shim:true} else unshimExtensions.push 'OES_texture_float_linear' else if checkFloatLinear gl, gl.FLOAT shimExtensions.push 'OES_texture_float_linear' else unshimExtensions.push 'OES_texture_float_linear' halfFloatExt = gl.getExtension 'OES_texture_half_float' if halfFloatExt == null if checkTexture(gl, 0x8D61) halfFloatTexturing = true shimExtensions.push 'OES_texture_half_float' halfFloatExt = shimLookup.OES_texture_half_float = { HALF_FLOAT_OES: 0x8D61 shim:true } else halfFloatTexturing = false unshimExtensions.push 'OES_texture_half_float' else if checkTexture(gl, halfFloatExt.HALF_FLOAT_OES) halfFloatTexturing = true shimExtensions.push 'OES_texture_half_float' else halfFloatTexturing = false unshimExtensions.push 'OES_texture_half_float' if halfFloatTexturing extobj = gl.getExtension 'EXT_color_buffer_half_float' if extobj == null if checkColorBuffer gl, halfFloatExt.HALF_FLOAT_OES shimExtensions.push 'EXT_color_buffer_half_float' shimLookup.EXT_color_buffer_half_float = { shim: true RGBA16F_EXT: 0x881A RGB16F_EXT: 0x881B FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211 UNSIGNED_NORMALIZED_EXT: 0x8C17 } else unshimExtensions.push 'EXT_color_buffer_half_float' else if checkColorBuffer gl, halfFloatExt.HALF_FLOAT_OES shimExtensions.push 'EXT_color_buffer_half_float' else unshimExtensions.push 'EXT_color_buffer_half_float' extobj = gl.getExtension 'OES_texture_half_float_linear' if extobj == null if checkFloatLinear gl, halfFloatExt.HALF_FLOAT_OES shimExtensions.push 'OES_texture_half_float_linear' shimLookup.OES_texture_half_float_linear = {shim:true} else unshimExtensions.push 'OES_texture_half_float_linear' else if checkFloatLinear gl, halfFloatExt.HALF_FLOAT_OES shimExtensions.push 'OES_texture_half_float_linear' else unshimExtensions.push 'OES_texture_half_float_linear' if window.WebGLRenderingContext? checkSupport() unshimLookup = {} for name in unshimExtensions unshimLookup[name] = true getExtension = WebGLRenderingContext.prototype.getExtension WebGLRenderingContext.prototype.getExtension = (name) -> extobj = shimLookup[name] if extobj == undefined if unshimLookup[name] return null else return getExtension.call @, name else return extobj getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions WebGLRenderingContext.prototype.getSupportedExtensions = -> supported = getSupportedExtensions.call(@) result = [] for extension in supported if unshimLookup[extension] == undefined result.push(extension) for extension in shimExtensions if extension not in result result.push extension return result WebGLRenderingContext.prototype.getFloatExtension = (spec) -> spec.prefer ?= ['half'] spec.require ?= [] spec.throws ?= true singleTexture = @getExtension 'OES_texture_float' halfTexture = @getExtension 'OES_texture_half_float' singleFramebuffer = @getExtension 'WEBGL_color_buffer_float' halfFramebuffer = @getExtension 'EXT_color_buffer_half_float' singleLinear = @getExtension 'OES_texture_float_linear' halfLinear = @getExtension 'OES_texture_half_float_linear' single = { texture: singleTexture != null filterable: singleLinear != null renderable: singleFramebuffer != null score: 0 precision: 'single' half: false single: true type: @FLOAT } half = { texture: halfTexture != null filterable: halfLinear != null renderable: halfFramebuffer != null score: 0 precision: 'half' half: true single: false type: halfTexture?.HALF_FLOAT_OES ? null } candidates = [] if single.texture candidates.push(single) if half.texture candidates.push(half) result = [] for candidate in candidates use = true for name in spec.require if candidate[name] == false use = false if use result.push candidate for candidate in result for preference, i in spec.prefer importance = Math.pow 2, spec.prefer.length - i - 1 if candidate[preference] candidate.score += importance result.sort (a, b) -> if a.score == b.score then 0 else if a.score < b.score then 1 else if a.score > b.score then -1 if result.length == 0 if spec.throws throw 'No floating point texture support that is ' + spec.require.join(', ') else return null else result = result[0] return { filterable: result.filterable renderable: result.renderable type: result.type precision: result.precision } ================================================ FILE: lib/webgl-texture-float-extension-shims.js ================================================ // Generated by CoffeeScript 1.3.3 var checkColorBuffer, checkFloatLinear, checkSupport, checkTexture, createSourceCanvas, getExtension, getSupportedExtensions, name, shimExtensions, shimLookup, unshimExtensions, unshimLookup, _i, _len, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; createSourceCanvas = function() { var canvas, ctx, imageData; canvas = document.createElement('canvas'); canvas.width = 2; canvas.height = 2; ctx = canvas.getContext('2d'); imageData = ctx.getImageData(0, 0, 2, 2); imageData.data.set(new Uint8ClampedArray([0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255])); ctx.putImageData(imageData, 0, 0); return canvas; }; createSourceCanvas(); checkFloatLinear = function(gl, sourceType) { var buffer, cleanup, fragmentShader, framebuffer, positionLoc, program, readBuffer, result, source, sourceCanvas, sourceLoc, target, vertexShader, vertices; program = gl.createProgram(); vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.attachShader(program, vertexShader); gl.shaderSource(vertexShader, 'attribute vec2 position;\nvoid main(){\n gl_Position = vec4(position, 0.0, 1.0);\n}'); gl.compileShader(vertexShader); if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { throw gl.getShaderInfoLog(vertexShader); } fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.attachShader(program, fragmentShader); gl.shaderSource(fragmentShader, 'uniform sampler2D source;\nvoid main(){\n gl_FragColor = texture2D(source, vec2(1.0, 1.0));\n}'); gl.compileShader(fragmentShader); if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { throw gl.getShaderInfoLog(fragmentShader); } gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { throw gl.getProgramInfoLog(program); } gl.useProgram(program); cleanup = function() { gl.deleteShader(fragmentShader); gl.deleteShader(vertexShader); gl.deleteProgram(program); gl.deleteBuffer(buffer); gl.deleteTexture(source); gl.deleteTexture(target); gl.deleteFramebuffer(framebuffer); gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.useProgram(null); gl.bindTexture(gl.TEXTURE_2D, null); return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }; target = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, target); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); framebuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0); sourceCanvas = createSourceCanvas(); source = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, source); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, sourceType, sourceCanvas); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); vertices = new Float32Array([1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1]); buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); positionLoc = gl.getAttribLocation(program, 'position'); sourceLoc = gl.getUniformLocation(program, 'source'); gl.enableVertexAttribArray(positionLoc); gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); gl.uniform1i(sourceLoc, 0); gl.drawArrays(gl.TRIANGLES, 0, 6); readBuffer = new Uint8Array(4 * 4); gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer); result = Math.abs(readBuffer[0] - 127) < 10; cleanup(); return result; }; checkTexture = function(gl, targetType) { var target; target = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, target); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null); if (gl.getError() === 0) { gl.deleteTexture(target); return true; } else { gl.deleteTexture(target); return false; } }; checkColorBuffer = function(gl, targetType) { var check, framebuffer, target; target = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, target); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null); framebuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0); check = gl.checkFramebufferStatus(gl.FRAMEBUFFER); gl.deleteTexture(target); gl.deleteFramebuffer(framebuffer); gl.bindTexture(gl.TEXTURE_2D, null); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (check === gl.FRAMEBUFFER_COMPLETE) { return true; } else { return false; } }; shimExtensions = []; shimLookup = {}; unshimExtensions = []; checkSupport = function() { var canvas, extobj, gl, halfFloatExt, halfFloatTexturing, singleFloatExt, singleFloatTexturing; canvas = document.createElement('canvas'); gl = null; try { gl = canvas.getContext('experimental-webgl'); if (gl === null) { gl = canvas.getContext('webgl'); } } catch (_error) {} if (gl != null) { singleFloatExt = gl.getExtension('OES_texture_float'); if (singleFloatExt === null) { if (checkTexture(gl, gl.FLOAT)) { singleFloatTexturing = true; shimExtensions.push('OES_texture_float'); shimLookup.OES_texture_float = { shim: true }; } else { singleFloatTexturing = false; unshimExtensions.push('OES_texture_float'); } } else { if (checkTexture(gl, gl.FLOAT)) { singleFloatTexturing = true; shimExtensions.push('OES_texture_float'); } else { singleFloatTexturing = false; unshimExtensions.push('OES_texture_float'); } } if (singleFloatTexturing) { extobj = gl.getExtension('WEBGL_color_buffer_float'); if (extobj === null) { if (checkColorBuffer(gl, gl.FLOAT)) { shimExtensions.push('WEBGL_color_buffer_float'); shimLookup.WEBGL_color_buffer_float = { shim: true, RGBA32F_EXT: 0x8814, RGB32F_EXT: 0x8815, FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211, UNSIGNED_NORMALIZED_EXT: 0x8C17 }; } else { unshimExtensions.push('WEBGL_color_buffer_float'); } } else { if (checkColorBuffer(gl, gl.FLOAT)) { shimExtensions.push('WEBGL_color_buffer_float'); } else { unshimExtensions.push('WEBGL_color_buffer_float'); } } extobj = gl.getExtension('OES_texture_float_linear'); if (extobj === null) { if (checkFloatLinear(gl, gl.FLOAT)) { shimExtensions.push('OES_texture_float_linear'); shimLookup.OES_texture_float_linear = { shim: true }; } else { unshimExtensions.push('OES_texture_float_linear'); } } else { if (checkFloatLinear(gl, gl.FLOAT)) { shimExtensions.push('OES_texture_float_linear'); } else { unshimExtensions.push('OES_texture_float_linear'); } } } halfFloatExt = gl.getExtension('OES_texture_half_float'); if (halfFloatExt === null) { if (checkTexture(gl, 0x8D61)) { halfFloatTexturing = true; shimExtensions.push('OES_texture_half_float'); halfFloatExt = shimLookup.OES_texture_half_float = { HALF_FLOAT_OES: 0x8D61, shim: true }; } else { halfFloatTexturing = false; unshimExtensions.push('OES_texture_half_float'); } } else { if (checkTexture(gl, halfFloatExt.HALF_FLOAT_OES)) { halfFloatTexturing = true; shimExtensions.push('OES_texture_half_float'); } else { halfFloatTexturing = false; unshimExtensions.push('OES_texture_half_float'); } } if (halfFloatTexturing) { extobj = gl.getExtension('EXT_color_buffer_half_float'); if (extobj === null) { if (checkColorBuffer(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('EXT_color_buffer_half_float'); shimLookup.EXT_color_buffer_half_float = { shim: true, RGBA16F_EXT: 0x881A, RGB16F_EXT: 0x881B, FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211, UNSIGNED_NORMALIZED_EXT: 0x8C17 }; } else { unshimExtensions.push('EXT_color_buffer_half_float'); } } else { if (checkColorBuffer(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('EXT_color_buffer_half_float'); } else { unshimExtensions.push('EXT_color_buffer_half_float'); } } extobj = gl.getExtension('OES_texture_half_float_linear'); if (extobj === null) { if (checkFloatLinear(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('OES_texture_half_float_linear'); return shimLookup.OES_texture_half_float_linear = { shim: true }; } else { return unshimExtensions.push('OES_texture_half_float_linear'); } } else { if (checkFloatLinear(gl, halfFloatExt.HALF_FLOAT_OES)) { return shimExtensions.push('OES_texture_half_float_linear'); } else { return unshimExtensions.push('OES_texture_half_float_linear'); } } } } }; if (window.WebGLRenderingContext != null) { checkSupport(); unshimLookup = {}; for (_i = 0, _len = unshimExtensions.length; _i < _len; _i++) { name = unshimExtensions[_i]; unshimLookup[name] = true; } getExtension = WebGLRenderingContext.prototype.getExtension; WebGLRenderingContext.prototype.getExtension = function(name) { var extobj; extobj = shimLookup[name]; if (extobj === void 0) { if (unshimLookup[name]) { return null; } else { return getExtension.call(this, name); } } else { return extobj; } }; getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions; WebGLRenderingContext.prototype.getSupportedExtensions = function() { var extension, result, supported, _j, _k, _len1, _len2; supported = getSupportedExtensions.call(this); result = []; for (_j = 0, _len1 = supported.length; _j < _len1; _j++) { extension = supported[_j]; if (unshimLookup[extension] === void 0) { result.push(extension); } } for (_k = 0, _len2 = shimExtensions.length; _k < _len2; _k++) { extension = shimExtensions[_k]; if (__indexOf.call(result, extension) < 0) { result.push(extension); } } return result; }; WebGLRenderingContext.prototype.getFloatExtension = function(spec) { var candidate, candidates, half, halfFramebuffer, halfLinear, halfTexture, i, importance, preference, result, single, singleFramebuffer, singleLinear, singleTexture, use, _j, _k, _l, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; if ((_ref = spec.prefer) == null) { spec.prefer = ['half']; } if ((_ref1 = spec.require) == null) { spec.require = []; } if ((_ref2 = spec.throws) == null) { spec.throws = true; } singleTexture = this.getExtension('OES_texture_float'); halfTexture = this.getExtension('OES_texture_half_float'); singleFramebuffer = this.getExtension('WEBGL_color_buffer_float'); halfFramebuffer = this.getExtension('EXT_color_buffer_half_float'); singleLinear = this.getExtension('OES_texture_float_linear'); halfLinear = this.getExtension('OES_texture_half_float_linear'); single = { texture: singleTexture !== null, filterable: singleLinear !== null, renderable: singleFramebuffer !== null, score: 0, precision: 'single', half: false, single: true, type: this.FLOAT }; half = { texture: halfTexture !== null, filterable: halfLinear !== null, renderable: halfFramebuffer !== null, score: 0, precision: 'half', half: true, single: false, type: (_ref3 = halfTexture != null ? halfTexture.HALF_FLOAT_OES : void 0) != null ? _ref3 : null }; candidates = []; if (single.texture) { candidates.push(single); } if (half.texture) { candidates.push(half); } result = []; for (_j = 0, _len1 = candidates.length; _j < _len1; _j++) { candidate = candidates[_j]; use = true; _ref4 = spec.require; for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { name = _ref4[_k]; if (candidate[name] === false) { use = false; } } if (use) { result.push(candidate); } } for (_l = 0, _len3 = result.length; _l < _len3; _l++) { candidate = result[_l]; _ref5 = spec.prefer; for (i = _m = 0, _len4 = _ref5.length; _m < _len4; i = ++_m) { preference = _ref5[i]; importance = Math.pow(2, spec.prefer.length - i - 1); if (candidate[preference]) { candidate.score += importance; } } } result.sort(function(a, b) { if (a.score === b.score) { return 0; } else if (a.score < b.score) { return 1; } else if (a.score > b.score) { return -1; } }); if (result.length === 0) { if (spec.throws) { throw 'No floating point texture support that is ' + spec.require.join(', '); } else { return null; } } else { result = result[0]; return { filterable: result.filterable, renderable: result.renderable, type: result.type, precision: result.precision }; } }; } ================================================ FILE: license/bsd-license ================================================ Copyright (c) 2012, Florian Boesch http://codeflow.org/ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 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. Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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. ================================================ FILE: license/gplv2-license ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ================================================ FILE: license/gplv3-license ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: license/mit-license ================================================ Copyright (c) 2012 Florian Boesch http://codeflow.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: license/readme ================================================ Copyright (c) 2012, Florian Boesch http://codeflow.org/ Deferred Irradiance Volumes is licensed under any of the following licenses at your choosing: MIT: see mit-license GPL: see gplv*-license BSD: see bsd-license ================================================ FILE: pack ================================================ #!/usr/bin/env python import os, sys, struct, cjson, stat from os.path import join here = os.path.dirname(os.path.abspath(__file__)) global_shunts = ''' Function.prototype.property = function(prop, desc) { Object.defineProperty(this.prototype, prop, desc); }; ''' def packModules(path): result = '' template = ''' loader.define('%s', function(exports, require, get){ %s}); ''' fs_base = join(here, path) for path, dirs, names in os.walk(fs_base): relpath = path[len(fs_base):] if not relpath.startswith('/'): relpath = '/' + relpath for name in names: if name.endswith('.js'): packpath = join(relpath, name) fspath = join(path, name) data = open(fspath).read() module = template % (packpath, data) result += module return result def packCode(app, target, targetname): if not requireCodePack([app, 'extra', 'lib'], target, targetname): return print 'packing code: %s' % target result = '' result += global_shunts for path, dirs, names in os.walk(join(here, 'extra')): for name in names: if name.endswith('.js'): data = open(join(path, name)).read() result += '\n%s;\n' % data result += packModules(app) result += packModules('lib') result += '\n$(function(){loader.main()});' open(join(here, target, targetname), 'w').write(result) def requireCodePack(apps, target, targetname): target_path = join(here, target, targetname) if not os.path.exists(target_path): return True for app in apps: target_mtime = os.stat(target_path)[stat.ST_MTIME] for path, dirs, names in os.walk(join(here, app)): for name in names: if name.endswith('.js'): code_mtime = os.stat(join(path, name))[stat.ST_MTIME] if code_mtime > target_mtime: return True return False binary_types = [ 'png', 'jpg', 'ogg', 'indices', 'vertices', 'model', 'mesh', 'bones', ] text_types = ['txt', 'shader', 'shaderlib'] packtypes = binary_types + text_types + ['json'] def requireAssetPack(app, target, targetname): for app in [app, 'lib']: fs_base = join(here, app) target_path = join(here, target, targetname) if not os.path.exists(target_path): return True target_mtime = os.stat(target_path)[stat.ST_MTIME] for path, dirs, names in os.walk(fs_base): for name in names: ext = os.path.splitext(name)[1].lstrip('.') fspath = join(path, name) if ext in packtypes: asset_mtime = os.stat(fspath)[stat.ST_MTIME] if asset_mtime > target_mtime: return True return False def packAssets(app, target, targetname): if not requireAssetPack(app, target, targetname): return print 'packing assets: %s' % target chunks = '' meta = {} for app in [app, 'lib']: fs_base = join(here, app) for path, dirs, names in os.walk(fs_base): relpath = path[len(fs_base):] if not relpath.startswith('/'): relpath = '/' + relpath for name in names: ext = os.path.splitext(name)[1].lstrip('.') packpath = join(relpath, name) fspath = join(path, name) if ext in binary_types: data = open(fspath, 'rb').read() offset = len(chunks) size = len(data) chunks += data meta[packpath] = { 'offset': offset, 'size': size, } elif ext in text_types: meta[packpath] = open(fspath, 'r').read() elif ext == 'json': meta[packpath] = cjson.decode(open(fspath, 'r').read()) metadata = cjson.encode(meta) metasize = struct.pack('I', len(metadata)) open(join(here, target, targetname), 'wb').write('PACK'+metasize+metadata+chunks) def pack(app, target, codename=None, assetname=None): if codename: packCode(app, target, codename) if assetname: packAssets(app, target, assetname) if __name__ == '__main__': pack('src', 'www', 'code.js', 'assets.pack') ================================================ FILE: src/albedo.shader ================================================ varying vec2 vTexcoord; varying vec3 vPosition, vViewPosition, vNormal; uniform mat4 proj, view; uniform mat3 view_rot; vertex: attribute vec3 position, normal; attribute vec2 texcoord; void main(){ vTexcoord = texcoord; vPosition = position; vNormal = normal; vViewPosition = (view * vec4(position, 1.0)).xyz; gl_Position = proj * view * vec4(position, 1.0); } fragment: uniform sampler2D diffuse_texture; uniform float specularity; uniform vec3 diffuse_color; uniform float gamma; void main(){ vec3 diffuse = pow(texture2D(diffuse_texture, vTexcoord).rgb, vec3(gamma)); gl_FragColor = vec4(diffuse*pow(diffuse_color, vec3(gamma)), 1.0); } ================================================ FILE: src/antialias/fxaa2_0.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform sampler2D source; uniform vec2 viewport; #define FXAA_REDUCE_MIN (1.0/ 128.0) #define FXAA_REDUCE_MUL (1.0 / 8.0) #define FXAA_SPAN_MAX 8.0 vec4 fxaa(vec2 fragCoord, sampler2D tex) { vec4 color; vec2 inverseVP = vec2(1.0 / viewport.x, 1.0 / viewport.y); vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz; vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz; vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz; vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz; vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz; vec3 luma = vec3(0.299, 0.587, 0.114); float lumaNW = dot(rgbNW, luma); float lumaNE = dot(rgbNE, luma); float lumaSW = dot(rgbSW, luma); float lumaSE = dot(rgbSE, luma); float lumaM = dot(rgbM, luma); float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); vec2 dir; dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP; vec3 rgbA = 0.5 * ( texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); vec3 rgbB = rgbA * 0.5 + 0.25 * ( texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz); float lumaB = dot(rgbB, luma); if ((lumaB < lumaMin) || (lumaB > lumaMax)) color = vec4(rgbA, 1.0); else color = vec4(rgbB, 1.0); return color; } void main(){ gl_FragColor = vec4(fxaa(gl_FragCoord.xy, source).rgb, 1.0); } ================================================ FILE: src/antialias/fxaa3_11.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: #define FXAA_WebGL 1 //#require fxaa3_11 //#require fxaa3_11_stripped #require fxaa3_11_preprocessed uniform sampler2D source; uniform vec2 viewport; uniform float subpixel_aa, contrast_treshold, edge_treshold; void main(){ vec4 color = FxaaPixelShader( gl_FragCoord.xy/viewport, source, vec2(1.0)/viewport, subpixel_aa, contrast_treshold, edge_treshold ); gl_FragColor = vec4(color.rgb, 1.0); } ================================================ FILE: src/antialias/fxaa3_11.shaderlib ================================================ /*============================================================================ NVIDIA FXAA 3.11 by TIMOTHY LOTTES ------------------------------------------------------------------------------ COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. ------------------------------------------------------------------------------ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ------------------------------------------------------------------------------ INTEGRATION CHECKLIST ------------------------------------------------------------------------------ (1.) In the shader source, setup defines for the desired configuration. When providing multiple shaders (for different presets), simply setup the defines differently in multiple files. Example, #define FXAA_PC 1 #define FXAA_HLSL_5 1 #define FXAA_QUALITY__PRESET 12 Or, #define FXAA_360 1 Or, #define FXAA_PS3 1 Etc. (2.) Then include this file, #include "Fxaa3_11.h" (3.) Then call the FXAA pixel shader from within your desired shader. Look at the FXAA Quality FxaaPixelShader() for docs on inputs. As for FXAA 3.11 all inputs for all shaders are the same to enable easy porting between platforms. return FxaaPixelShader(...); (4.) Insure pass prior to FXAA outputs RGBL (see next section). Or use, #define FXAA_GREEN_AS_LUMA 1 (5.) Setup engine to provide the following constants which are used in the FxaaPixelShader() inputs, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir Look at the FXAA Quality FxaaPixelShader() for docs on inputs. (6.) Have FXAA vertex shader run as a full screen triangle, and output "pos" and "fxaaConsolePosPos" such that inputs in the pixel shader provide, // {xy} = center of pixel FxaaFloat2 pos, // {xy__} = upper left of pixel // {__zw} = lower right of pixel FxaaFloat4 fxaaConsolePosPos, (7.) Insure the texture sampler(s) used by FXAA are set to bilinear filtering. ------------------------------------------------------------------------------ INTEGRATION - RGBL AND COLORSPACE ------------------------------------------------------------------------------ FXAA3 requires RGBL as input unless the following is set, #define FXAA_GREEN_AS_LUMA 1 In which case the engine uses green in place of luma, and requires RGB input is in a non-linear colorspace. RGB should be LDR (low dynamic range). Specifically do FXAA after tonemapping. RGB data as returned by a texture fetch can be non-linear, or linear when FXAA_GREEN_AS_LUMA is not set. Note an "sRGB format" texture counts as linear, because the result of a texture fetch is linear data. Regular "RGBA8" textures in the sRGB colorspace are non-linear. If FXAA_GREEN_AS_LUMA is not set, luma must be stored in the alpha channel prior to running FXAA. This luma should be in a perceptual space (could be gamma 2.0). Example pass before FXAA where output is gamma 2.0 encoded, color.rgb = ToneMap(color.rgb); // linear color output color.rgb = sqrt(color.rgb); // gamma 2.0 color output return color; To use FXAA, color.rgb = ToneMap(color.rgb); // linear color output color.rgb = sqrt(color.rgb); // gamma 2.0 color output color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma return color; Another example where output is linear encoded, say for instance writing to an sRGB formated render target, where the render target does the conversion back to sRGB after blending, color.rgb = ToneMap(color.rgb); // linear color output return color; To use FXAA, color.rgb = ToneMap(color.rgb); // linear color output color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma return color; Getting luma correct is required for the algorithm to work correctly. ------------------------------------------------------------------------------ BEING LINEARLY CORRECT? ------------------------------------------------------------------------------ Applying FXAA to a framebuffer with linear RGB color will look worse. This is very counter intuitive, but happends to be true in this case. The reason is because dithering artifacts will be more visiable in a linear colorspace. ------------------------------------------------------------------------------ COMPLEX INTEGRATION ------------------------------------------------------------------------------ Q. What if the engine is blending into RGB before wanting to run FXAA? A. In the last opaque pass prior to FXAA, have the pass write out luma into alpha. Then blend into RGB only. FXAA should be able to run ok assuming the blending pass did not any add aliasing. This should be the common case for particles and common blending passes. A. Or use FXAA_GREEN_AS_LUMA. ============================================================================*/ /*============================================================================ INTEGRATION KNOBS ============================================================================*/ // // FXAA_PS3 and FXAA_360 choose the console algorithm (FXAA3 CONSOLE). // FXAA_360_OPT is a prototype for the new optimized 360 version. // // 1 = Use API. // 0 = Don't use API. // /*--------------------------------------------------------------------------*/ #ifndef FXAA_PS3 #define FXAA_PS3 0 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_360 #define FXAA_360 0 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_360_OPT #define FXAA_360_OPT 0 #endif /*==========================================================================*/ #ifndef FXAA_PC // // FXAA Quality // The high quality PC algorithm. // #define FXAA_PC 1 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_PC_CONSOLE // // The console algorithm for PC is included // for developers targeting really low spec machines. // Likely better to just run FXAA_PC, and use a really low preset. // #define FXAA_PC_CONSOLE 0 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_GLSL_120 #define FXAA_GLSL_120 0 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_GLSL_130 #define FXAA_GLSL_130 0 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_HLSL_3 #define FXAA_HLSL_3 0 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_HLSL_4 #define FXAA_HLSL_4 0 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_HLSL_5 #define FXAA_HLSL_5 0 #endif /*==========================================================================*/ #ifndef FXAA_GREEN_AS_LUMA // // For those using non-linear color, // and either not able to get luma in alpha, or not wanting to, // this enables FXAA to run using green as a proxy for luma. // So with this enabled, no need to pack luma in alpha. // // This will turn off AA on anything which lacks some amount of green. // Pure red and blue or combination of only R and B, will get no AA. // // Might want to lower the settings for both, // fxaaConsoleEdgeThresholdMin // fxaaQualityEdgeThresholdMin // In order to insure AA does not get turned off on colors // which contain a minor amount of green. // // 1 = On. // 0 = Off. // #define FXAA_GREEN_AS_LUMA 1 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_EARLY_EXIT // // Controls algorithm's early exit path. // On PS3 turning this ON adds 2 cycles to the shader. // On 360 turning this OFF adds 10ths of a millisecond to the shader. // Turning this off on console will result in a more blurry image. // So this defaults to on. // // 1 = On. // 0 = Off. // #define FXAA_EARLY_EXIT 1 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_DISCARD // // Only valid for PC OpenGL currently. // Probably will not work when FXAA_GREEN_AS_LUMA = 1. // // 1 = Use discard on pixels which don't need AA. // For APIs which enable concurrent TEX+ROP from same surface. // 0 = Return unchanged color on pixels which don't need AA. // #define FXAA_DISCARD 0 #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_FAST_PIXEL_OFFSET // // Used for GLSL 120 only. // // 1 = GL API supports fast pixel offsets // 0 = do not use fast pixel offsets // #ifdef GL_EXT_gpu_shader4 #define FXAA_FAST_PIXEL_OFFSET 1 #endif #ifdef GL_NV_gpu_shader5 #define FXAA_FAST_PIXEL_OFFSET 1 #endif #ifdef GL_ARB_gpu_shader5 #define FXAA_FAST_PIXEL_OFFSET 1 #endif #ifndef FXAA_FAST_PIXEL_OFFSET #define FXAA_FAST_PIXEL_OFFSET 0 #endif #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_GATHER4_ALPHA // // 1 = API supports gather4 on alpha channel. // 0 = API does not support gather4 on alpha channel. // #if (FXAA_HLSL_5 == 1) #define FXAA_GATHER4_ALPHA 1 #endif #ifdef GL_ARB_gpu_shader5 #define FXAA_GATHER4_ALPHA 1 #endif #ifdef GL_NV_gpu_shader5 #define FXAA_GATHER4_ALPHA 1 #endif #ifndef FXAA_GATHER4_ALPHA #define FXAA_GATHER4_ALPHA 0 #endif #endif /*============================================================================ FXAA CONSOLE PS3 - TUNING KNOBS ============================================================================*/ #ifndef FXAA_CONSOLE__PS3_EDGE_SHARPNESS // // Consoles the sharpness of edges on PS3 only. // Non-PS3 tuning is done with shader input. // // Due to the PS3 being ALU bound, // there are only two safe values here: 4 and 8. // These options use the shaders ability to a free *|/ by 2|4|8. // // 8.0 is sharper // 4.0 is softer // 2.0 is really soft (good for vector graphics inputs) // #if 1 #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 8.0 #endif #if 0 #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 4.0 #endif #if 0 #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 2.0 #endif #endif /*--------------------------------------------------------------------------*/ #ifndef FXAA_CONSOLE__PS3_EDGE_THRESHOLD // // Only effects PS3. // Non-PS3 tuning is done with shader input. // // The minimum amount of local contrast required to apply algorithm. // The console setting has a different mapping than the quality setting. // // This only applies when FXAA_EARLY_EXIT is 1. // // Due to the PS3 being ALU bound, // there are only two safe values here: 0.25 and 0.125. // These options use the shaders ability to a free *|/ by 2|4|8. // // 0.125 leaves less aliasing, but is softer // 0.25 leaves more aliasing, and is sharper // #if 1 #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.125 #else #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.25 #endif #endif /*============================================================================ FXAA QUALITY - TUNING KNOBS ------------------------------------------------------------------------------ NOTE the other tuning knobs are now in the shader function inputs! ============================================================================*/ #ifndef FXAA_QUALITY__PRESET // // Choose the quality preset. // This needs to be compiled into the shader as it effects code. // Best option to include multiple presets is to // in each shader define the preset, then include this file. // // OPTIONS // ----------------------------------------------------------------------- // 10 to 15 - default medium dither (10=fastest, 15=highest quality) // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) // 39 - no dither, very expensive // // NOTES // ----------------------------------------------------------------------- // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) // 13 = about same speed as FXAA 3.9 and better than 12 // 23 = closest to FXAA 3.9 visually and performance wise // _ = the lowest digit is directly related to performance // _ = the highest digit is directly related to style // #define FXAA_QUALITY__PRESET 12 #endif /*============================================================================ FXAA QUALITY - PRESETS ============================================================================*/ /*============================================================================ FXAA QUALITY - MEDIUM DITHER PRESETS ============================================================================*/ #if (FXAA_QUALITY__PRESET == 10) #define FXAA_QUALITY__PS 3 #define FXAA_QUALITY__P0 1.5 #define FXAA_QUALITY__P1 3.0 #define FXAA_QUALITY__P2 12.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 11) #define FXAA_QUALITY__PS 4 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 3.0 #define FXAA_QUALITY__P3 12.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 12) #define FXAA_QUALITY__PS 5 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 4.0 #define FXAA_QUALITY__P4 12.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 13) #define FXAA_QUALITY__PS 6 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 4.0 #define FXAA_QUALITY__P5 12.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 14) #define FXAA_QUALITY__PS 7 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 4.0 #define FXAA_QUALITY__P6 12.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 15) #define FXAA_QUALITY__PS 8 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 4.0 #define FXAA_QUALITY__P7 12.0 #endif /*============================================================================ FXAA QUALITY - LOW DITHER PRESETS ============================================================================*/ #if (FXAA_QUALITY__PRESET == 20) #define FXAA_QUALITY__PS 3 #define FXAA_QUALITY__P0 1.5 #define FXAA_QUALITY__P1 2.0 #define FXAA_QUALITY__P2 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 21) #define FXAA_QUALITY__PS 4 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 22) #define FXAA_QUALITY__PS 5 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 23) #define FXAA_QUALITY__PS 6 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 24) #define FXAA_QUALITY__PS 7 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 3.0 #define FXAA_QUALITY__P6 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 25) #define FXAA_QUALITY__PS 8 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 4.0 #define FXAA_QUALITY__P7 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 26) #define FXAA_QUALITY__PS 9 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 4.0 #define FXAA_QUALITY__P8 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 27) #define FXAA_QUALITY__PS 10 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 2.0 #define FXAA_QUALITY__P8 4.0 #define FXAA_QUALITY__P9 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 28) #define FXAA_QUALITY__PS 11 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 2.0 #define FXAA_QUALITY__P8 2.0 #define FXAA_QUALITY__P9 4.0 #define FXAA_QUALITY__P10 8.0 #endif /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PRESET == 29) #define FXAA_QUALITY__PS 12 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 2.0 #define FXAA_QUALITY__P8 2.0 #define FXAA_QUALITY__P9 2.0 #define FXAA_QUALITY__P10 4.0 #define FXAA_QUALITY__P11 8.0 #endif /*============================================================================ FXAA QUALITY - EXTREME QUALITY ============================================================================*/ #if (FXAA_QUALITY__PRESET == 39) #define FXAA_QUALITY__PS 12 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.0 #define FXAA_QUALITY__P2 1.0 #define FXAA_QUALITY__P3 1.0 #define FXAA_QUALITY__P4 1.0 #define FXAA_QUALITY__P5 1.5 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 2.0 #define FXAA_QUALITY__P8 2.0 #define FXAA_QUALITY__P9 2.0 #define FXAA_QUALITY__P10 4.0 #define FXAA_QUALITY__P11 8.0 #endif /*============================================================================ API PORTING ============================================================================*/ #if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) || (FXAA_WebGL == 1) #define FxaaBool bool #define FxaaDiscard discard #define FxaaFloat float #define FxaaFloat2 vec2 #define FxaaFloat3 vec3 #define FxaaFloat4 vec4 #define FxaaHalf float #define FxaaHalf2 vec2 #define FxaaHalf3 vec3 #define FxaaHalf4 vec4 #define FxaaInt2 ivec2 #define FxaaSat(x) clamp(x, 0.0, 1.0) #define FxaaTex sampler2D #else #define FxaaBool bool #define FxaaDiscard clip(-1) #define FxaaFloat float #define FxaaFloat2 float2 #define FxaaFloat3 float3 #define FxaaFloat4 float4 #define FxaaHalf half #define FxaaHalf2 half2 #define FxaaHalf3 half3 #define FxaaHalf4 half4 #define FxaaSat(x) saturate(x) #endif /*--------------------------------------------------------------------------*/ #if (FXAA_WebGL == 1) #define FxaaTexTop(t, p) texture2D(t, p, 0.0) #define FxaaTexOff(t, p, o, r) texture2D(t, p + (vec2(o) * r), 0.0) #endif #if (FXAA_GLSL_120 == 1) // Requires, // #version 120 // And at least, // #extension GL_EXT_gpu_shader4 : enable // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) // #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) #if (FXAA_FAST_PIXEL_OFFSET == 1) #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) #else #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) #endif #if (FXAA_GATHER4_ALPHA == 1) // use #extension GL_ARB_gpu_shader5 : enable #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) #define FxaaTexGreen4(t, p) textureGather(t, p, 1) #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) #endif #endif /*--------------------------------------------------------------------------*/ #if (FXAA_GLSL_130 == 1) // Requires "#version 130" or better #define FxaaTexTop(t, p) textureLod(t, p, 0.0) #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) #if (FXAA_GATHER4_ALPHA == 1) // use #extension GL_ARB_gpu_shader5 : enable #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) #define FxaaTexGreen4(t, p) textureGather(t, p, 1) #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) #endif #endif /*--------------------------------------------------------------------------*/ #if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1) #define FxaaInt2 float2 #define FxaaTex sampler2D #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) #endif /*--------------------------------------------------------------------------*/ #if (FXAA_HLSL_4 == 1) #define FxaaInt2 int2 struct FxaaTex { SamplerState smpl; Texture2D tex; }; #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) #endif /*--------------------------------------------------------------------------*/ #if (FXAA_HLSL_5 == 1) #define FxaaInt2 int2 struct FxaaTex { SamplerState smpl; Texture2D tex; }; #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) #endif /*============================================================================ GREEN AS LUMA OPTION SUPPORT FUNCTION ============================================================================*/ #if (FXAA_GREEN_AS_LUMA == 0) FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } #else FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } #endif /*============================================================================ FXAA3 QUALITY - PC ============================================================================*/ #if (FXAA_PC == 1) /*--------------------------------------------------------------------------*/ FxaaFloat4 FxaaPixelShader( // Use noperspective interpolation here (turn off perspective interpolation). // {xy} = center of pixel FxaaFloat2 pos #if (FXAA_PS3 == 1) // Used only for FXAA Console, and not used on the 360 version. // Use noperspective interpolation here (turn off perspective interpolation). // {xy__} = upper left of pixel // {__zw} = lower right of pixel ,FxaaFloat4 fxaaConsolePosPos #endif // Input color texture. // {rgb_} = color in linear or perceptual color space // if (FXAA_GREEN_AS_LUMA == 0) // {___a} = luma in perceptual color space (not linear) ,FxaaTex tex #if (FXAA_360_OPT == 1) // Only used on the optimized 360 version of FXAA Console. // For everything but 360, just use the same input here as for "tex". // For 360, same texture, just alias with a 2nd sampler. // This sampler needs to have an exponent bias of -1. ,FxaaTex fxaaConsole360TexExpBiasNegOne // Only used on the optimized 360 version of FXAA Console. // For everything but 360, just use the same input here as for "tex". // For 360, same texture, just alias with a 3nd sampler. // This sampler needs to have an exponent bias of -2. ,FxaaTex fxaaConsole360TexExpBiasNegTwo #endif // // Only used on FXAA Quality. // This must be from a constant/uniform. // {x_} = 1.0/screenWidthInPixels // {_y} = 1.0/screenHeightInPixels ,FxaaFloat2 fxaaQualityRcpFrame #if (FXAA_PS3 == 1) || (FXAA_360 == 1) || (FXAA_360_OPT == 1) || (FXAA_PC_CONSOLE == 1) // // Only used on FXAA Console. // This must be from a constant/uniform. // This effects sub-pixel AA quality and inversely sharpness. // Where N ranges between, // N = 0.50 (default) // N = 0.33 (sharper) // {x___} = -N/screenWidthInPixels // {_y__} = -N/screenHeightInPixels // {__z_} = N/screenWidthInPixels // {___w} = N/screenHeightInPixels ,FxaaFloat4 fxaaConsoleRcpFrameOpt // // Only used on FXAA Console. // Not used on 360, but used on PS3 and PC. // This must be from a constant/uniform. // {x___} = -2.0/screenWidthInPixels // {_y__} = -2.0/screenHeightInPixels // {__z_} = 2.0/screenWidthInPixels // {___w} = 2.0/screenHeightInPixels ,FxaaFloat4 fxaaConsoleRcpFrameOpt2 // // Only used on FXAA Console. // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. // This must be from a constant/uniform. // {x___} = 8.0/screenWidthInPixels // {_y__} = 8.0/screenHeightInPixels // {__z_} = -4.0/screenWidthInPixels // {___w} = -4.0/screenHeightInPixels ,FxaaFloat4 fxaaConsole360RcpFrameOpt2 #endif // Only used on FXAA Quality. // This used to be the FXAA_QUALITY__SUBPIX define. // It is here now to allow easier tuning. // Choose the amount of sub-pixel aliasing removal. // This can effect sharpness. // 1.00 - upper limit (softer) // 0.75 - default amount of filtering // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) // 0.25 - almost off // 0.00 - completely off ,FxaaFloat fxaaQualitySubpix // // Only used on FXAA Quality. // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. // It is here now to allow easier tuning. // The minimum amount of local contrast required to apply algorithm. // 0.333 - too little (faster) // 0.250 - low quality // 0.166 - default // 0.125 - high quality // 0.063 - overkill (slower) ,FxaaFloat fxaaQualityEdgeThreshold // // Only used on FXAA Quality. // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. // It is here now to allow easier tuning. // Trims the algorithm from processing darks. // 0.0833 - upper limit (default, the start of visible unfiltered edges) // 0.0625 - high quality (faster) // 0.0312 - visible limit (slower) // Special notes when using FXAA_GREEN_AS_LUMA, // Likely want to set this to zero. // As colors that are mostly not-green // will appear very dark in the green channel! // Tune by looking at mostly non-green content, // then start at zero and increase until aliasing is a problem. ,FxaaFloat fxaaQualityEdgeThresholdMin #if (FXAA_PS3 == 1) || (FXAA_360 == 1) || (FXAA_360_OPT == 1) || (FXAA_PC_CONSOLE == 1) // Only used on FXAA Console. // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. // It is here now to allow easier tuning. // This does not effect PS3, as this needs to be compiled in. // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. // Due to the PS3 being ALU bound, // there are only three safe values here: 2 and 4 and 8. // These options use the shaders ability to a free *|/ by 2|4|8. // For all other platforms can be a non-power of two. // 8.0 is sharper (default!!!) // 4.0 is softer // 2.0 is really soft (good only for vector graphics inputs) ,FxaaFloat fxaaConsoleEdgeSharpness // // Only used on FXAA Console. // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. // It is here now to allow easier tuning. // This does not effect PS3, as this needs to be compiled in. // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. // Due to the PS3 being ALU bound, // there are only two safe values here: 1/4 and 1/8. // These options use the shaders ability to a free *|/ by 2|4|8. // The console setting has a different mapping than the quality setting. // Other platforms can use other values. // 0.125 leaves less aliasing, but is softer (default!!!) // 0.25 leaves more aliasing, and is sharper ,FxaaFloat fxaaConsoleEdgeThreshold // // Only used on FXAA Console. // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. // It is here now to allow easier tuning. // Trims the algorithm from processing darks. // The console setting has a different mapping than the quality setting. // This only applies when FXAA_EARLY_EXIT is 1. // This does not apply to PS3, // PS3 was simplified to avoid more shader instructions. // 0.06 - faster but more aliasing in darks // 0.05 - default // 0.04 - slower and less aliasing in darks // Special notes when using FXAA_GREEN_AS_LUMA, // Likely want to set this to zero. // As colors that are mostly not-green // will appear very dark in the green channel! // Tune by looking at mostly non-green content, // then start at zero and increase until aliasing is a problem. ,FxaaFloat fxaaConsoleEdgeThresholdMin #endif #if (FXAA_360 == 1) || (FXAA_360_OPT == 1) // Extra constants for 360 FXAA Console only. // Use zeros or anything else for other platforms. // These must be in physical constant registers and NOT immedates. // Immedates will result in compiler un-optimizing. // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) ,FxaaFloat4 fxaaConsole360ConstDir #endif ) { /*--------------------------------------------------------------------------*/ FxaaFloat2 posM; posM.x = pos.x; posM.y = pos.y; #if (FXAA_GATHER4_ALPHA == 1) #if (FXAA_DISCARD == 0) FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); #if (FXAA_GREEN_AS_LUMA == 0) #define lumaM rgbyM.w #else #define lumaM rgbyM.y #endif #endif #if (FXAA_GREEN_AS_LUMA == 0) FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); #else FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); #endif #if (FXAA_DISCARD == 1) #define lumaM luma4A.w #endif #define lumaE luma4A.z #define lumaS luma4A.x #define lumaSE luma4A.y #define lumaNW luma4B.w #define lumaN luma4B.z #define lumaW luma4B.x #else FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); #if (FXAA_GREEN_AS_LUMA == 0) #define lumaM rgbyM.w #else #define lumaM rgbyM.y #endif FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); #endif /*--------------------------------------------------------------------------*/ FxaaFloat maxSM = max(lumaS, lumaM); FxaaFloat minSM = min(lumaS, lumaM); FxaaFloat maxESM = max(lumaE, maxSM); FxaaFloat minESM = min(lumaE, minSM); FxaaFloat maxWN = max(lumaN, lumaW); FxaaFloat minWN = min(lumaN, lumaW); FxaaFloat rangeMax = max(maxWN, maxESM); FxaaFloat rangeMin = min(minWN, minESM); FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; FxaaFloat range = rangeMax - rangeMin; FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); FxaaBool earlyExit = range < rangeMaxClamped; /*--------------------------------------------------------------------------*/ if(earlyExit) #if (FXAA_DISCARD == 1) FxaaDiscard; #else return rgbyM; #endif /*--------------------------------------------------------------------------*/ #if (FXAA_GATHER4_ALPHA == 0) FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); #else FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); #endif /*--------------------------------------------------------------------------*/ FxaaFloat lumaNS = lumaN + lumaS; FxaaFloat lumaWE = lumaW + lumaE; FxaaFloat subpixRcpRange = 1.0/range; FxaaFloat subpixNSWE = lumaNS + lumaWE; FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; /*--------------------------------------------------------------------------*/ FxaaFloat lumaNESE = lumaNE + lumaSE; FxaaFloat lumaNWNE = lumaNW + lumaNE; FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; /*--------------------------------------------------------------------------*/ FxaaFloat lumaNWSW = lumaNW + lumaSW; FxaaFloat lumaSWSE = lumaSW + lumaSE; FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; /*--------------------------------------------------------------------------*/ FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; FxaaFloat lengthSign = fxaaQualityRcpFrame.x; FxaaBool horzSpan = edgeHorz >= edgeVert; FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; /*--------------------------------------------------------------------------*/ if(!horzSpan) lumaN = lumaW; if(!horzSpan) lumaS = lumaE; if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; /*--------------------------------------------------------------------------*/ FxaaFloat gradientN = lumaN - lumaM; FxaaFloat gradientS = lumaS - lumaM; FxaaFloat lumaNN = lumaN + lumaM; FxaaFloat lumaSS = lumaS + lumaM; FxaaBool pairN = abs(gradientN) >= abs(gradientS); FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); if(pairN) lengthSign = -lengthSign; FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); /*--------------------------------------------------------------------------*/ FxaaFloat2 posB; posB.x = posM.x; posB.y = posM.y; FxaaFloat2 offNP; offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; if(!horzSpan) posB.x += lengthSign * 0.5; if( horzSpan) posB.y += lengthSign * 0.5; /*--------------------------------------------------------------------------*/ FxaaFloat2 posN; posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; FxaaFloat2 posP; posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); FxaaFloat subpixE = subpixC * subpixC; FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); /*--------------------------------------------------------------------------*/ if(!pairN) lumaNN = lumaSS; FxaaFloat gradientScaled = gradient * 1.0/4.0; FxaaFloat lumaMM = lumaM - lumaNN * 0.5; FxaaFloat subpixF = subpixD * subpixE; FxaaBool lumaMLTZero = lumaMM < 0.0; /*--------------------------------------------------------------------------*/ lumaEndN -= lumaNN * 0.5; lumaEndP -= lumaNN * 0.5; FxaaBool doneN = abs(lumaEndN) >= gradientScaled; FxaaBool doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; FxaaBool doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; /*--------------------------------------------------------------------------*/ if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 3) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 4) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 5) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 6) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 7) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 8) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 9) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 10) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 11) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY__PS > 12) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } /*--------------------------------------------------------------------------*/ FxaaFloat dstN = posM.x - posN.x; FxaaFloat dstP = posP.x - posM.x; if(!horzSpan) dstN = posM.y - posN.y; if(!horzSpan) dstP = posP.y - posM.y; /*--------------------------------------------------------------------------*/ FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; FxaaFloat spanLength = (dstP + dstN); FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; FxaaFloat spanLengthRcp = 1.0/spanLength; /*--------------------------------------------------------------------------*/ FxaaBool directionN = dstN < dstP; FxaaFloat dst = min(dstN, dstP); FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; FxaaFloat subpixG = subpixF * subpixF; FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; FxaaFloat subpixH = subpixG * fxaaQualitySubpix; /*--------------------------------------------------------------------------*/ FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; #if (FXAA_DISCARD == 1) return FxaaTexTop(tex, posM); #else return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); #endif } /*==========================================================================*/ #endif /*============================================================================ FXAA3 CONSOLE - PC VERSION ------------------------------------------------------------------------------ Instead of using this on PC, I'd suggest just using FXAA Quality with #define FXAA_QUALITY__PRESET 10 Or #define FXAA_QUALITY__PRESET 20 Either are higher qualilty and almost as fast as this on modern PC GPUs. ============================================================================*/ #if (FXAA_PC_CONSOLE == 1) /*--------------------------------------------------------------------------*/ FxaaFloat4 FxaaPixelShader( // See FXAA Quality FxaaPixelShader() source for docs on Inputs! FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir ) { /*--------------------------------------------------------------------------*/ FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); /*--------------------------------------------------------------------------*/ FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); #if (FXAA_GREEN_AS_LUMA == 0) FxaaFloat lumaM = rgbyM.w; #else FxaaFloat lumaM = rgbyM.y; #endif /*--------------------------------------------------------------------------*/ FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); lumaNe += 1.0/384.0; FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); /*--------------------------------------------------------------------------*/ FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); /*--------------------------------------------------------------------------*/ FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); /*--------------------------------------------------------------------------*/ FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; /*--------------------------------------------------------------------------*/ FxaaFloat lumaMinM = min(lumaMin, lumaM); FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); FxaaFloat lumaMaxM = max(lumaMax, lumaM); FxaaFloat dirSwMinusNe = lumaSw - lumaNe; FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; FxaaFloat dirSeMinusNw = lumaSe - lumaNw; if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM; /*--------------------------------------------------------------------------*/ FxaaFloat2 dir; dir.x = dirSwMinusNe + dirSeMinusNw; dir.y = dirSwMinusNe - dirSeMinusNw; /*--------------------------------------------------------------------------*/ FxaaFloat2 dir1 = normalize(dir.xy); FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); /*--------------------------------------------------------------------------*/ FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); /*--------------------------------------------------------------------------*/ FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw); FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw); /*--------------------------------------------------------------------------*/ FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); /*--------------------------------------------------------------------------*/ #if (FXAA_GREEN_AS_LUMA == 0) FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax); #else FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax); #endif if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5; return rgbyB; } /*==========================================================================*/ #endif /*============================================================================ FXAA3 CONSOLE - 360 PIXEL SHADER ------------------------------------------------------------------------------ This optimized version thanks to suggestions from Andy Luedke. Should be fully tex bound in all cases. As of the FXAA 3.11 release, I have still not tested this code, however I fixed a bug which was in both FXAA 3.9 and FXAA 3.10. And note this is replacing the old unoptimized version. If it does not work, please let me know so I can fix it. ============================================================================*/ #if (FXAA_360 == 1) /*--------------------------------------------------------------------------*/ [reduceTempRegUsage(4)] float4 FxaaPixelShader( // See FXAA Quality FxaaPixelShader() source for docs on Inputs! FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir ) { /*--------------------------------------------------------------------------*/ float4 lumaNwNeSwSe; #if (FXAA_GREEN_AS_LUMA == 0) asm { tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false }; #else asm { tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false }; #endif /*--------------------------------------------------------------------------*/ lumaNwNeSwSe.y += 1.0/384.0; float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y); float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y); /*--------------------------------------------------------------------------*/ float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0)); #if (FXAA_GREEN_AS_LUMA == 0) float lumaMinM = min(lumaMin, rgbyM.w); float lumaMaxM = max(lumaMax, rgbyM.w); #else float lumaMinM = min(lumaMin, rgbyM.y); float lumaMaxM = max(lumaMax, rgbyM.y); #endif if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM; /*--------------------------------------------------------------------------*/ float2 dir; dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx); dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy); dir = normalize(dir); /*--------------------------------------------------------------------------*/ float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw; /*--------------------------------------------------------------------------*/ float4 dir2; float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness; dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5); dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw; /*--------------------------------------------------------------------------*/ float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0)); float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0)); float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0)); float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0)); /*--------------------------------------------------------------------------*/ float4 rgbyA = rgbyN1 + rgbyP1; float4 rgbyB = rgbyN2 + rgbyP2 + rgbyA * 0.5; /*--------------------------------------------------------------------------*/ float4 rgbyR = ((FxaaLuma(rgbyB) - lumaMax) > 0.0) ? rgbyA : rgbyB; rgbyR = ((FxaaLuma(rgbyB) - lumaMin) > 0.0) ? rgbyR : rgbyA; return rgbyR; } /*==========================================================================*/ #endif /*============================================================================ FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (NO EARLY EXIT) ============================================================================== The code below does not exactly match the assembly. I have a feeling that 12 cycles is possible, but was not able to get there. Might have to increase register count to get full performance. Note this shader does not use perspective interpolation. Use the following cgc options, --fenable-bx2 --fastmath --fastprecision --nofloatbindings ------------------------------------------------------------------------------ NVSHADERPERF OUTPUT ------------------------------------------------------------------------------ For reference and to aid in debug, output of NVShaderPerf should match this, Shader to schedule: 0: texpkb h0.w(TRUE), v5.zyxx, #0 2: addh h2.z(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x 4: texpkb h0.w(TRUE), v5.xwxx, #0 6: addh h0.z(TRUE), -h2, h0.w 7: texpkb h1.w(TRUE), v5, #0 9: addh h0.x(TRUE), h0.z, -h1.w 10: addh h3.w(TRUE), h0.z, h1 11: texpkb h2.w(TRUE), v5.zwzz, #0 13: addh h0.z(TRUE), h3.w, -h2.w 14: addh h0.x(TRUE), h2.w, h0 15: nrmh h1.xz(TRUE), h0_n 16: minh_m8 h0.x(TRUE), |h1|, |h1.z| 17: maxh h4.w(TRUE), h0, h1 18: divx h2.xy(TRUE), h1_n.xzzw, h0_n 19: movr r1.zw(TRUE), v4.xxxy 20: madr r2.xz(TRUE), -h1, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zzww, r1.zzww 22: minh h5.w(TRUE), h0, h1 23: texpkb h0(TRUE), r2.xzxx, #0 25: madr r0.zw(TRUE), h1.xzxz, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w), r1 27: maxh h4.x(TRUE), h2.z, h2.w 28: texpkb h1(TRUE), r0.zwzz, #0 30: addh_d2 h1(TRUE), h0, h1 31: madr r0.xy(TRUE), -h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz 33: texpkb h0(TRUE), r0, #0 35: minh h4.z(TRUE), h2, h2.w 36: fenct TRUE 37: madr r1.xy(TRUE), h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz 39: texpkb h2(TRUE), r1, #0 41: addh_d2 h0(TRUE), h0, h2 42: maxh h2.w(TRUE), h4, h4.x 43: minh h2.x(TRUE), h5.w, h4.z 44: addh_d2 h0(TRUE), h0, h1 45: slth h2.x(TRUE), h0.w, h2 46: sgth h2.w(TRUE), h0, h2 47: movh h0(TRUE), h0 48: addx.c0 rc(TRUE), h2, h2.w 49: movh h0(c0.NE.x), h1 IPU0 ------ Simplified schedule: -------- Pass | Unit | uOp | PC: Op -----+--------+------+------------------------- 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; | SCB1 | add | 2: ADDh h2.z, h0.--w-, const.--x-; | | | 2 | SCT0/1 | mov | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; | TEX | txl | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; | SCB1 | add | 6: ADDh h0.z,-h2, h0.--w-; | | | 3 | SCT0/1 | mov | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; | TEX | txl | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; | SCB0 | add | 9: ADDh h0.x, h0.z---,-h1.w---; | SCB1 | add | 10: ADDh h3.w, h0.---z, h1; | | | 4 | SCT0/1 | mov | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; | TEX | txl | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; | SCB0 | add | 14: ADDh h0.x, h2.w---, h0; | SCB1 | add | 13: ADDh h0.z, h3.--w-,-h2.--w-; | | | 5 | SCT1 | mov | 15: NRMh h1.xz, h0; | SRB | nrm | 15: NRMh h1.xz, h0; | SCB0 | min | 16: MINh*8 h0.x, |h1|, |h1.z---|; | SCB1 | max | 17: MAXh h4.w, h0, h1; | | | 6 | SCT0 | div | 18: DIVx h2.xy, h1.xz--, h0; | SCT1 | mov | 19: MOVr r1.zw, g[TEX0].--xy; | SCB0 | mad | 20: MADr r2.xz,-h1, const.z-w-, r1.z-w-; | SCB1 | min | 22: MINh h5.w, h0, h1; | | | 7 | SCT0/1 | mov | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; | TEX | txl | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; | SCB0 | max | 27: MAXh h4.x, h2.z---, h2.w---; | SCB1 | mad | 25: MADr r0.zw, h1.--xz, const, r1; | | | 8 | SCT0/1 | mov | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; | TEX | txl | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; | SCB0/1 | add | 30: ADDh/2 h1, h0, h1; | | | 9 | SCT0 | mad | 31: MADr r0.xy,-h2, const.xy--, r1.zw--; | SCT1 | mov | 33: TXLr h0, r0, const.zzzz, TEX0; | TEX | txl | 33: TXLr h0, r0, const.zzzz, TEX0; | SCB1 | min | 35: MINh h4.z, h2, h2.--w-; | | | 10 | SCT0 | mad | 37: MADr r1.xy, h2, const.xy--, r1.zw--; | SCT1 | mov | 39: TXLr h2, r1, const.zzzz, TEX0; | TEX | txl | 39: TXLr h2, r1, const.zzzz, TEX0; | SCB0/1 | add | 41: ADDh/2 h0, h0, h2; | | | 11 | SCT0 | min | 43: MINh h2.x, h5.w---, h4.z---; | SCT1 | max | 42: MAXh h2.w, h4, h4.---x; | SCB0/1 | add | 44: ADDh/2 h0, h0, h1; | | | 12 | SCT0 | set | 45: SLTh h2.x, h0.w---, h2; | SCT1 | set | 46: SGTh h2.w, h0, h2; | SCB0/1 | mul | 47: MOVh h0, h0; | | | 13 | SCT0 | mad | 48: ADDxc0_s rc, h2, h2.w---; | SCB0/1 | mul | 49: MOVh h0(NE0.xxxx), h1; Pass SCT TEX SCB 1: 0% 100% 25% 2: 0% 100% 25% 3: 0% 100% 50% 4: 0% 100% 50% 5: 0% 0% 50% 6: 100% 0% 75% 7: 0% 100% 75% 8: 0% 100% 100% 9: 0% 100% 25% 10: 0% 100% 100% 11: 50% 0% 100% 12: 50% 0% 100% 13: 25% 0% 100% MEAN: 17% 61% 67% Pass SCT0 SCT1 TEX SCB0 SCB1 1: 0% 0% 100% 0% 100% 2: 0% 0% 100% 0% 100% 3: 0% 0% 100% 100% 100% 4: 0% 0% 100% 100% 100% 5: 0% 0% 0% 100% 100% 6: 100% 100% 0% 100% 100% 7: 0% 0% 100% 100% 100% 8: 0% 0% 100% 100% 100% 9: 0% 0% 100% 0% 100% 10: 0% 0% 100% 100% 100% 11: 100% 100% 0% 100% 100% 12: 100% 100% 0% 100% 100% 13: 100% 0% 0% 100% 100% MEAN: 30% 23% 61% 76% 100% Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 Results 13 cycles, 3 r regs, 923,076,923 pixels/s ============================================================================*/ #if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0) /*--------------------------------------------------------------------------*/ #pragma regcount 7 #pragma disablepc all #pragma option O3 #pragma option OutColorPrec=fp16 #pragma texformat default RGBA8 /*==========================================================================*/ half4 FxaaPixelShader( // See FXAA Quality FxaaPixelShader() source for docs on Inputs! FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir ) { /*--------------------------------------------------------------------------*/ // (1) half4 dir; half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) lumaNe.w += half(1.0/512.0); dir.x = -lumaNe.w; dir.z = -lumaNe.w; #else lumaNe.y += half(1.0/512.0); dir.x = -lumaNe.y; dir.z = -lumaNe.y; #endif /*--------------------------------------------------------------------------*/ // (2) half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) dir.x += lumaSw.w; dir.z += lumaSw.w; #else dir.x += lumaSw.y; dir.z += lumaSw.y; #endif /*--------------------------------------------------------------------------*/ // (3) half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) dir.x -= lumaNw.w; dir.z += lumaNw.w; #else dir.x -= lumaNw.y; dir.z += lumaNw.y; #endif /*--------------------------------------------------------------------------*/ // (4) half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) dir.x += lumaSe.w; dir.z -= lumaSe.w; #else dir.x += lumaSe.y; dir.z -= lumaSe.y; #endif /*--------------------------------------------------------------------------*/ // (5) half4 dir1_pos; dir1_pos.xy = normalize(dir.xyz).xz; half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); /*--------------------------------------------------------------------------*/ // (6) half4 dir2_pos; dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0)); dir1_pos.zw = pos.xy; dir2_pos.zw = pos.xy; half4 temp1N; temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; /*--------------------------------------------------------------------------*/ // (7) temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); half4 rgby1; rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; /*--------------------------------------------------------------------------*/ // (8) rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); rgby1 = (temp1N + rgby1) * 0.5; /*--------------------------------------------------------------------------*/ // (9) half4 temp2N; temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); /*--------------------------------------------------------------------------*/ // (10) half4 rgby2; rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); rgby2 = (temp2N + rgby2) * 0.5; /*--------------------------------------------------------------------------*/ // (11) // compilier moves these scalar ops up to other cycles #if (FXAA_GREEN_AS_LUMA == 0) half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w)); half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w)); #else half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y)); half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y)); #endif rgby2 = (rgby2 + rgby1) * 0.5; /*--------------------------------------------------------------------------*/ // (12) #if (FXAA_GREEN_AS_LUMA == 0) bool twoTapLt = rgby2.w < lumaMin; bool twoTapGt = rgby2.w > lumaMax; #else bool twoTapLt = rgby2.y < lumaMin; bool twoTapGt = rgby2.y > lumaMax; #endif /*--------------------------------------------------------------------------*/ // (13) if(twoTapLt || twoTapGt) rgby2 = rgby1; /*--------------------------------------------------------------------------*/ return rgby2; } /*==========================================================================*/ #endif /*============================================================================ FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (WITH EARLY EXIT) ============================================================================== The code mostly matches the assembly. I have a feeling that 14 cycles is possible, but was not able to get there. Might have to increase register count to get full performance. Note this shader does not use perspective interpolation. Use the following cgc options, --fenable-bx2 --fastmath --fastprecision --nofloatbindings Use of FXAA_GREEN_AS_LUMA currently adds a cycle (16 clks). Will look at fixing this for FXAA 3.12. ------------------------------------------------------------------------------ NVSHADERPERF OUTPUT ------------------------------------------------------------------------------ For reference and to aid in debug, output of NVShaderPerf should match this, Shader to schedule: 0: texpkb h0.w(TRUE), v5.zyxx, #0 2: addh h2.y(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x 4: texpkb h1.w(TRUE), v5.xwxx, #0 6: addh h0.x(TRUE), h1.w, -h2.y 7: texpkb h2.w(TRUE), v5.zwzz, #0 9: minh h4.w(TRUE), h2.y, h2 10: maxh h5.x(TRUE), h2.y, h2.w 11: texpkb h0.w(TRUE), v5, #0 13: addh h3.w(TRUE), -h0, h0.x 14: addh h0.x(TRUE), h0.w, h0 15: addh h0.z(TRUE), -h2.w, h0.x 16: addh h0.x(TRUE), h2.w, h3.w 17: minh h5.y(TRUE), h0.w, h1.w 18: nrmh h2.xz(TRUE), h0_n 19: minh_m8 h2.w(TRUE), |h2.x|, |h2.z| 20: divx h4.xy(TRUE), h2_n.xzzw, h2_n.w 21: movr r1.zw(TRUE), v4.xxxy 22: maxh h2.w(TRUE), h0, h1 23: fenct TRUE 24: madr r0.xy(TRUE), -h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz 26: texpkb h0(TRUE), r0, #0 28: maxh h5.x(TRUE), h2.w, h5 29: minh h5.w(TRUE), h5.y, h4 30: madr r1.xy(TRUE), h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz 32: texpkb h2(TRUE), r1, #0 34: addh_d2 h2(TRUE), h0, h2 35: texpkb h1(TRUE), v4, #0 37: maxh h5.y(TRUE), h5.x, h1.w 38: minh h4.w(TRUE), h1, h5 39: madr r0.xy(TRUE), -h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz 41: texpkb h0(TRUE), r0, #0 43: addh_m8 h5.z(TRUE), h5.y, -h4.w 44: madr r2.xy(TRUE), h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz 46: texpkb h3(TRUE), r2, #0 48: addh_d2 h0(TRUE), h0, h3 49: addh_d2 h3(TRUE), h0, h2 50: movh h0(TRUE), h3 51: slth h3.x(TRUE), h3.w, h5.w 52: sgth h3.w(TRUE), h3, h5.x 53: addx.c0 rc(TRUE), h3.x, h3 54: slth.c0 rc(TRUE), h5.z, h5 55: movh h0(c0.NE.w), h2 56: movh h0(c0.NE.x), h1 IPU0 ------ Simplified schedule: -------- Pass | Unit | uOp | PC: Op -----+--------+------+------------------------- 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; | SCB0 | add | 2: ADDh h2.y, h0.-w--, const.-x--; | | | 2 | SCT0/1 | mov | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; | TEX | txl | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; | SCB0 | add | 6: ADDh h0.x, h1.w---,-h2.y---; | | | 3 | SCT0/1 | mov | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; | TEX | txl | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; | SCB0 | max | 10: MAXh h5.x, h2.y---, h2.w---; | SCB1 | min | 9: MINh h4.w, h2.---y, h2; | | | 4 | SCT0/1 | mov | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; | TEX | txl | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; | SCB0 | add | 14: ADDh h0.x, h0.w---, h0; | SCB1 | add | 13: ADDh h3.w,-h0, h0.---x; | | | 5 | SCT0 | mad | 16: ADDh h0.x, h2.w---, h3.w---; | SCT1 | mad | 15: ADDh h0.z,-h2.--w-, h0.--x-; | SCB0 | min | 17: MINh h5.y, h0.-w--, h1.-w--; | | | 6 | SCT1 | mov | 18: NRMh h2.xz, h0; | SRB | nrm | 18: NRMh h2.xz, h0; | SCB1 | min | 19: MINh*8 h2.w, |h2.---x|, |h2.---z|; | | | 7 | SCT0 | div | 20: DIVx h4.xy, h2.xz--, h2.ww--; | SCT1 | mov | 21: MOVr r1.zw, g[TEX0].--xy; | SCB1 | max | 22: MAXh h2.w, h0, h1; | | | 8 | SCT0 | mad | 24: MADr r0.xy,-h2.xz--, const.zw--, r1.zw--; | SCT1 | mov | 26: TXLr h0, r0, const.xxxx, TEX0; | TEX | txl | 26: TXLr h0, r0, const.xxxx, TEX0; | SCB0 | max | 28: MAXh h5.x, h2.w---, h5; | SCB1 | min | 29: MINh h5.w, h5.---y, h4; | | | 9 | SCT0 | mad | 30: MADr r1.xy, h2.xz--, const.zw--, r1.zw--; | SCT1 | mov | 32: TXLr h2, r1, const.xxxx, TEX0; | TEX | txl | 32: TXLr h2, r1, const.xxxx, TEX0; | SCB0/1 | add | 34: ADDh/2 h2, h0, h2; | | | 10 | SCT0/1 | mov | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; | TEX | txl | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; | SCB0 | max | 37: MAXh h5.y, h5.-x--, h1.-w--; | SCB1 | min | 38: MINh h4.w, h1, h5; | | | 11 | SCT0 | mad | 39: MADr r0.xy,-h4, const.xy--, r1.zw--; | SCT1 | mov | 41: TXLr h0, r0, const.zzzz, TEX0; | TEX | txl | 41: TXLr h0, r0, const.zzzz, TEX0; | SCB0 | mad | 44: MADr r2.xy, h4, const.xy--, r1.zw--; | SCB1 | add | 43: ADDh*8 h5.z, h5.--y-,-h4.--w-; | | | 12 | SCT0/1 | mov | 46: TXLr h3, r2, const.xxxx, TEX0; | TEX | txl | 46: TXLr h3, r2, const.xxxx, TEX0; | SCB0/1 | add | 48: ADDh/2 h0, h0, h3; | | | 13 | SCT0/1 | mad | 49: ADDh/2 h3, h0, h2; | SCB0/1 | mul | 50: MOVh h0, h3; | | | 14 | SCT0 | set | 51: SLTh h3.x, h3.w---, h5.w---; | SCT1 | set | 52: SGTh h3.w, h3, h5.---x; | SCB0 | set | 54: SLThc0 rc, h5.z---, h5; | SCB1 | add | 53: ADDxc0_s rc, h3.---x, h3; | | | 15 | SCT0/1 | mul | 55: MOVh h0(NE0.wwww), h2; | SCB0/1 | mul | 56: MOVh h0(NE0.xxxx), h1; Pass SCT TEX SCB 1: 0% 100% 25% 2: 0% 100% 25% 3: 0% 100% 50% 4: 0% 100% 50% 5: 50% 0% 25% 6: 0% 0% 25% 7: 100% 0% 25% 8: 0% 100% 50% 9: 0% 100% 100% 10: 0% 100% 50% 11: 0% 100% 75% 12: 0% 100% 100% 13: 100% 0% 100% 14: 50% 0% 50% 15: 100% 0% 100% MEAN: 26% 60% 56% Pass SCT0 SCT1 TEX SCB0 SCB1 1: 0% 0% 100% 100% 0% 2: 0% 0% 100% 100% 0% 3: 0% 0% 100% 100% 100% 4: 0% 0% 100% 100% 100% 5: 100% 100% 0% 100% 0% 6: 0% 0% 0% 0% 100% 7: 100% 100% 0% 0% 100% 8: 0% 0% 100% 100% 100% 9: 0% 0% 100% 100% 100% 10: 0% 0% 100% 100% 100% 11: 0% 0% 100% 100% 100% 12: 0% 0% 100% 100% 100% 13: 100% 100% 0% 100% 100% 14: 100% 100% 0% 100% 100% 15: 100% 100% 0% 100% 100% MEAN: 33% 33% 60% 86% 80% Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 Results 15 cycles, 3 r regs, 800,000,000 pixels/s ============================================================================*/ #if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1) /*--------------------------------------------------------------------------*/ #pragma regcount 7 #pragma disablepc all #pragma option O2 #pragma option OutColorPrec=fp16 #pragma texformat default RGBA8 /*==========================================================================*/ half4 FxaaPixelShader( // See FXAA Quality FxaaPixelShader() source for docs on Inputs! FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir ) { /*--------------------------------------------------------------------------*/ // (1) half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) half lumaNe = rgbyNe.w + half(1.0/512.0); #else half lumaNe = rgbyNe.y + half(1.0/512.0); #endif /*--------------------------------------------------------------------------*/ // (2) half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) half lumaSwNegNe = lumaSw.w - lumaNe; #else half lumaSwNegNe = lumaSw.y - lumaNe; #endif /*--------------------------------------------------------------------------*/ // (3) half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) half lumaMaxNwSw = max(lumaNw.w, lumaSw.w); half lumaMinNwSw = min(lumaNw.w, lumaSw.w); #else half lumaMaxNwSw = max(lumaNw.y, lumaSw.y); half lumaMinNwSw = min(lumaNw.y, lumaSw.y); #endif /*--------------------------------------------------------------------------*/ // (4) half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) half dirZ = lumaNw.w + lumaSwNegNe; half dirX = -lumaNw.w + lumaSwNegNe; #else half dirZ = lumaNw.y + lumaSwNegNe; half dirX = -lumaNw.y + lumaSwNegNe; #endif /*--------------------------------------------------------------------------*/ // (5) half3 dir; dir.y = 0.0; #if (FXAA_GREEN_AS_LUMA == 0) dir.x = lumaSe.w + dirX; dir.z = -lumaSe.w + dirZ; half lumaMinNeSe = min(lumaNe, lumaSe.w); #else dir.x = lumaSe.y + dirX; dir.z = -lumaSe.y + dirZ; half lumaMinNeSe = min(lumaNe, lumaSe.y); #endif /*--------------------------------------------------------------------------*/ // (6) half4 dir1_pos; dir1_pos.xy = normalize(dir).xz; half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); /*--------------------------------------------------------------------------*/ // (7) half4 dir2_pos; dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0)); dir1_pos.zw = pos.xy; dir2_pos.zw = pos.xy; #if (FXAA_GREEN_AS_LUMA == 0) half lumaMaxNeSe = max(lumaNe, lumaSe.w); #else half lumaMaxNeSe = max(lumaNe, lumaSe.y); #endif /*--------------------------------------------------------------------------*/ // (8) half4 temp1N; temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe); half lumaMin = min(lumaMinNwSw, lumaMinNeSe); /*--------------------------------------------------------------------------*/ // (9) half4 rgby1; rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); rgby1 = (temp1N + rgby1) * 0.5; /*--------------------------------------------------------------------------*/ // (10) half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0)); #if (FXAA_GREEN_AS_LUMA == 0) half lumaMaxM = max(lumaMax, rgbyM.w); half lumaMinM = min(lumaMin, rgbyM.w); #else half lumaMaxM = max(lumaMax, rgbyM.y); half lumaMinM = min(lumaMin, rgbyM.y); #endif /*--------------------------------------------------------------------------*/ // (11) half4 temp2N; temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); half4 rgby2; rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE__PS3_EDGE_THRESHOLD; /*--------------------------------------------------------------------------*/ // (12) rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); rgby2 = (temp2N + rgby2) * 0.5; /*--------------------------------------------------------------------------*/ // (13) rgby2 = (rgby2 + rgby1) * 0.5; /*--------------------------------------------------------------------------*/ // (14) #if (FXAA_GREEN_AS_LUMA == 0) bool twoTapLt = rgby2.w < lumaMin; bool twoTapGt = rgby2.w > lumaMax; #else bool twoTapLt = rgby2.y < lumaMin; bool twoTapGt = rgby2.y > lumaMax; #endif bool earlyExit = lumaRangeM < lumaMax; bool twoTap = twoTapLt || twoTapGt; /*--------------------------------------------------------------------------*/ // (15) if(twoTap) rgby2 = rgby1; if(earlyExit) rgby2 = rgbyM; /*--------------------------------------------------------------------------*/ return rgby2; } /*==========================================================================*/ #endif ================================================ FILE: src/antialias/fxaa3_11_preprocessed.shaderlib ================================================ float FxaaLuma(vec4 rgba) { return rgba.y; } vec4 FxaaPixelShader( vec2 pos ,sampler2D tex ,vec2 fxaaQualityRcpFrame ,float fxaaQualitySubpix ,float fxaaQualityEdgeThreshold ,float fxaaQualityEdgeThresholdMin ) { vec2 posM; posM.x = pos.x; posM.y = pos.y; vec4 rgbyM = texture2D(tex, posM, 0.0); float lumaS = FxaaLuma(texture2D(tex, posM + (vec2( 0.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)); float lumaE = FxaaLuma(texture2D(tex, posM + (vec2( 1.0, 0.0) * fxaaQualityRcpFrame.xy), 0.0)); float lumaN = FxaaLuma(texture2D(tex, posM + (vec2( 0.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)); float lumaW = FxaaLuma(texture2D(tex, posM + (vec2(-1.0, 0.0) * fxaaQualityRcpFrame.xy), 0.0)); float maxSM = max(lumaS, rgbyM.y); float minSM = min(lumaS, rgbyM.y); float maxESM = max(lumaE, maxSM); float minESM = min(lumaE, minSM); float maxWN = max(lumaN, lumaW); float minWN = min(lumaN, lumaW); float rangeMax = max(maxWN, maxESM); float rangeMin = min(minWN, minESM); float rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; float range = rangeMax - rangeMin; float rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); bool earlyExit = range < rangeMaxClamped; if(earlyExit) return rgbyM; float lumaNW = FxaaLuma(texture2D(tex, posM + (vec2(-1.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)); float lumaSE = FxaaLuma(texture2D(tex, posM + (vec2( 1.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)); float lumaNE = FxaaLuma(texture2D(tex, posM + (vec2( 1.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)); float lumaSW = FxaaLuma(texture2D(tex, posM + (vec2(-1.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)); float lumaNS = lumaN + lumaS; float lumaWE = lumaW + lumaE; float subpixRcpRange = 1.0/range; float subpixNSWE = lumaNS + lumaWE; float edgeHorz1 = (-2.0 * rgbyM.y) + lumaNS; float edgeVert1 = (-2.0 * rgbyM.y) + lumaWE; float lumaNESE = lumaNE + lumaSE; float lumaNWNE = lumaNW + lumaNE; float edgeHorz2 = (-2.0 * lumaE) + lumaNESE; float edgeVert2 = (-2.0 * lumaN) + lumaNWNE; float lumaNWSW = lumaNW + lumaSW; float lumaSWSE = lumaSW + lumaSE; float edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); float edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); float edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; float edgeVert3 = (-2.0 * lumaS) + lumaSWSE; float edgeHorz = abs(edgeHorz3) + edgeHorz4; float edgeVert = abs(edgeVert3) + edgeVert4; float subpixNWSWNESE = lumaNWSW + lumaNESE; float lengthSign = fxaaQualityRcpFrame.x; bool horzSpan = edgeHorz >= edgeVert; float subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; if(!horzSpan) lumaN = lumaW; if(!horzSpan) lumaS = lumaE; if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; float subpixB = (subpixA * (1.0/12.0)) - rgbyM.y; float gradientN = lumaN - rgbyM.y; float gradientS = lumaS - rgbyM.y; float lumaNN = lumaN + rgbyM.y; float lumaSS = lumaS + rgbyM.y; bool pairN = abs(gradientN) >= abs(gradientS); float gradient = max(abs(gradientN), abs(gradientS)); if(pairN) lengthSign = -lengthSign; float subpixC = clamp(abs(subpixB) * subpixRcpRange, 0.0, 1.0); vec2 posB; posB.x = posM.x; posB.y = posM.y; vec2 offNP; offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; if(!horzSpan) posB.x += lengthSign * 0.5; if( horzSpan) posB.y += lengthSign * 0.5; vec2 posN; posN.x = posB.x - offNP.x * 1.0; posN.y = posB.y - offNP.y * 1.0; vec2 posP; posP.x = posB.x + offNP.x * 1.0; posP.y = posB.y + offNP.y * 1.0; float subpixD = ((-2.0)*subpixC) + 3.0; float lumaEndN = FxaaLuma(texture2D(tex, posN, 0.0)); float subpixE = subpixC * subpixC; float lumaEndP = FxaaLuma(texture2D(tex, posP, 0.0)); if(!pairN) lumaNN = lumaSS; float gradientScaled = gradient * 1.0/4.0; float lumaMM = rgbyM.y - lumaNN * 0.5; float subpixF = subpixD * subpixE; bool lumaMLTZero = lumaMM < 0.0; lumaEndN -= lumaNN * 0.5; lumaEndP -= lumaNN * 0.5; bool doneN = abs(lumaEndN) >= gradientScaled; bool doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * 1.5; if(!doneN) posN.y -= offNP.y * 1.5; bool doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * 1.5; if(!doneP) posP.y += offNP.y * 1.5; if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(texture2D(tex, posN.xy, 0.0)); if(!doneP) lumaEndP = FxaaLuma(texture2D(tex, posP.xy, 0.0)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * 2.0; if(!doneN) posN.y -= offNP.y * 2.0; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * 2.0; if(!doneP) posP.y += offNP.y * 2.0; if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(texture2D(tex, posN.xy, 0.0)); if(!doneP) lumaEndP = FxaaLuma(texture2D(tex, posP.xy, 0.0)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * 4.0; if(!doneN) posN.y -= offNP.y * 4.0; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * 4.0; if(!doneP) posP.y += offNP.y * 4.0; if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(texture2D(tex, posN.xy, 0.0)); if(!doneP) lumaEndP = FxaaLuma(texture2D(tex, posP.xy, 0.0)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * 12.0; if(!doneN) posN.y -= offNP.y * 12.0; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * 12.0; if(!doneP) posP.y += offNP.y * 12.0; } } } float dstN = posM.x - posN.x; float dstP = posP.x - posM.x; if(!horzSpan) dstN = posM.y - posN.y; if(!horzSpan) dstP = posP.y - posM.y; bool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; float spanLength = (dstP + dstN); bool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; float spanLengthRcp = 1.0/spanLength; bool directionN = dstN < dstP; float dst = min(dstN, dstP); bool goodSpan = directionN ? goodSpanN : goodSpanP; float subpixG = subpixF * subpixF; float pixelOffset = (dst * (-spanLengthRcp)) + 0.5; float subpixH = subpixG * fxaaQualitySubpix; float pixelOffsetGood = goodSpan ? pixelOffset : 0.0; float pixelOffsetSubpix = max(pixelOffsetGood, subpixH); if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; return vec4(texture2D(tex, posM, 0.0).xyz, rgbyM.y); } ================================================ FILE: src/antialias/fxaa3_11_stripped.shaderlib ================================================ #ifndef FXAA_PS3 #define FXAA_PS3 0 #endif #ifndef FXAA_360 #define FXAA_360 0 #endif #ifndef FXAA_360_OPT #define FXAA_360_OPT 0 #endif #ifndef FXAA_PC #define FXAA_PC 1 #endif #ifndef FXAA_PC_CONSOLE #define FXAA_PC_CONSOLE 0 #endif #ifndef FXAA_GLSL_120 #define FXAA_GLSL_120 0 #endif #ifndef FXAA_GLSL_130 #define FXAA_GLSL_130 0 #endif #ifndef FXAA_HLSL_3 #define FXAA_HLSL_3 0 #endif #ifndef FXAA_HLSL_4 #define FXAA_HLSL_4 0 #endif #ifndef FXAA_HLSL_5 #define FXAA_HLSL_5 0 #endif #ifndef FXAA_GREEN_AS_LUMA #define FXAA_GREEN_AS_LUMA 1 #endif #ifndef FXAA_EARLY_EXIT #define FXAA_EARLY_EXIT 1 #endif #ifndef FXAA_DISCARD #define FXAA_DISCARD 0 #endif #ifndef FXAA_FAST_PIXEL_OFFSET #ifdef GL_EXT_gpu_shader4 #define FXAA_FAST_PIXEL_OFFSET 1 #endif #ifdef GL_NV_gpu_shader5 #define FXAA_FAST_PIXEL_OFFSET 1 #endif #ifdef GL_ARB_gpu_shader5 #define FXAA_FAST_PIXEL_OFFSET 1 #endif #ifndef FXAA_FAST_PIXEL_OFFSET #define FXAA_FAST_PIXEL_OFFSET 0 #endif #endif #ifndef FXAA_GATHER4_ALPHA #if (FXAA_HLSL_5 == 1) #define FXAA_GATHER4_ALPHA 1 #endif #ifdef GL_ARB_gpu_shader5 #define FXAA_GATHER4_ALPHA 1 #endif #ifdef GL_NV_gpu_shader5 #define FXAA_GATHER4_ALPHA 1 #endif #ifndef FXAA_GATHER4_ALPHA #define FXAA_GATHER4_ALPHA 0 #endif #endif #ifndef FXAA_CONSOLE__PS3_EDGE_SHARPNESS #if 1 #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 8.0 #endif #if 0 #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 4.0 #endif #if 0 #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 2.0 #endif #endif #ifndef FXAA_CONSOLE__PS3_EDGE_THRESHOLD #if 1 #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.125 #else #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.25 #endif #endif #ifndef FXAA_QUALITY__PRESET #define FXAA_QUALITY__PRESET 12 #endif #if (FXAA_QUALITY__PRESET == 10) #define FXAA_QUALITY__PS 3 #define FXAA_QUALITY__P0 1.5 #define FXAA_QUALITY__P1 3.0 #define FXAA_QUALITY__P2 12.0 #endif #if (FXAA_QUALITY__PRESET == 11) #define FXAA_QUALITY__PS 4 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 3.0 #define FXAA_QUALITY__P3 12.0 #endif #if (FXAA_QUALITY__PRESET == 12) #define FXAA_QUALITY__PS 5 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 4.0 #define FXAA_QUALITY__P4 12.0 #endif #if (FXAA_QUALITY__PRESET == 13) #define FXAA_QUALITY__PS 6 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 4.0 #define FXAA_QUALITY__P5 12.0 #endif #if (FXAA_QUALITY__PRESET == 14) #define FXAA_QUALITY__PS 7 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 4.0 #define FXAA_QUALITY__P6 12.0 #endif #if (FXAA_QUALITY__PRESET == 15) #define FXAA_QUALITY__PS 8 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 4.0 #define FXAA_QUALITY__P7 12.0 #endif #if (FXAA_QUALITY__PRESET == 20) #define FXAA_QUALITY__PS 3 #define FXAA_QUALITY__P0 1.5 #define FXAA_QUALITY__P1 2.0 #define FXAA_QUALITY__P2 8.0 #endif #if (FXAA_QUALITY__PRESET == 21) #define FXAA_QUALITY__PS 4 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 8.0 #endif #if (FXAA_QUALITY__PRESET == 22) #define FXAA_QUALITY__PS 5 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 8.0 #endif #if (FXAA_QUALITY__PRESET == 23) #define FXAA_QUALITY__PS 6 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 8.0 #endif #if (FXAA_QUALITY__PRESET == 24) #define FXAA_QUALITY__PS 7 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 3.0 #define FXAA_QUALITY__P6 8.0 #endif #if (FXAA_QUALITY__PRESET == 25) #define FXAA_QUALITY__PS 8 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 4.0 #define FXAA_QUALITY__P7 8.0 #endif #if (FXAA_QUALITY__PRESET == 26) #define FXAA_QUALITY__PS 9 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 4.0 #define FXAA_QUALITY__P8 8.0 #endif #if (FXAA_QUALITY__PRESET == 27) #define FXAA_QUALITY__PS 10 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 2.0 #define FXAA_QUALITY__P8 4.0 #define FXAA_QUALITY__P9 8.0 #endif #if (FXAA_QUALITY__PRESET == 28) #define FXAA_QUALITY__PS 11 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 2.0 #define FXAA_QUALITY__P8 2.0 #define FXAA_QUALITY__P9 4.0 #define FXAA_QUALITY__P10 8.0 #endif #if (FXAA_QUALITY__PRESET == 29) #define FXAA_QUALITY__PS 12 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.5 #define FXAA_QUALITY__P2 2.0 #define FXAA_QUALITY__P3 2.0 #define FXAA_QUALITY__P4 2.0 #define FXAA_QUALITY__P5 2.0 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 2.0 #define FXAA_QUALITY__P8 2.0 #define FXAA_QUALITY__P9 2.0 #define FXAA_QUALITY__P10 4.0 #define FXAA_QUALITY__P11 8.0 #endif #if (FXAA_QUALITY__PRESET == 39) #define FXAA_QUALITY__PS 12 #define FXAA_QUALITY__P0 1.0 #define FXAA_QUALITY__P1 1.0 #define FXAA_QUALITY__P2 1.0 #define FXAA_QUALITY__P3 1.0 #define FXAA_QUALITY__P4 1.0 #define FXAA_QUALITY__P5 1.5 #define FXAA_QUALITY__P6 2.0 #define FXAA_QUALITY__P7 2.0 #define FXAA_QUALITY__P8 2.0 #define FXAA_QUALITY__P9 2.0 #define FXAA_QUALITY__P10 4.0 #define FXAA_QUALITY__P11 8.0 #endif #if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) || (FXAA_WebGL == 1) #define FxaaBool bool #define FxaaDiscard discard #define FxaaFloat float #define FxaaFloat2 vec2 #define FxaaFloat3 vec3 #define FxaaFloat4 vec4 #define FxaaHalf float #define FxaaHalf2 vec2 #define FxaaHalf3 vec3 #define FxaaHalf4 vec4 #define FxaaInt2 ivec2 #define FxaaSat(x) clamp(x, 0.0, 1.0) #define FxaaTex sampler2D #else #define FxaaBool bool #define FxaaDiscard clip(-1) #define FxaaFloat float #define FxaaFloat2 float2 #define FxaaFloat3 float3 #define FxaaFloat4 float4 #define FxaaHalf half #define FxaaHalf2 half2 #define FxaaHalf3 half3 #define FxaaHalf4 half4 #define FxaaSat(x) saturate(x) #endif #if (FXAA_WebGL == 1) #define FxaaTexTop(t, p) texture2D(t, p, 0.0) #define FxaaTexOff(t, p, o, r) texture2D(t, p + (vec2(o) * r), 0.0) #endif #if (FXAA_GLSL_120 == 1) #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) #if (FXAA_FAST_PIXEL_OFFSET == 1) #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) #else #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) #endif #if (FXAA_GATHER4_ALPHA == 1) #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) #define FxaaTexGreen4(t, p) textureGather(t, p, 1) #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) #endif #endif #if (FXAA_GLSL_130 == 1) #define FxaaTexTop(t, p) textureLod(t, p, 0.0) #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) #if (FXAA_GATHER4_ALPHA == 1) #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) #define FxaaTexGreen4(t, p) textureGather(t, p, 1) #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) #endif #endif #if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1) #define FxaaInt2 float2 #define FxaaTex sampler2D #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) #endif #if (FXAA_HLSL_4 == 1) #define FxaaInt2 int2 struct FxaaTex { SamplerState smpl; Texture2D tex; }; #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) #endif #if (FXAA_HLSL_5 == 1) #define FxaaInt2 int2 struct FxaaTex { SamplerState smpl; Texture2D tex; }; #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) #endif #if (FXAA_GREEN_AS_LUMA == 0) FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } #else FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } #endif #if (FXAA_PC == 1) FxaaFloat4 FxaaPixelShader( FxaaFloat2 pos #if (FXAA_PS3 == 1) ,FxaaFloat4 fxaaConsolePosPos #endif ,FxaaTex tex #if (FXAA_360_OPT == 1) ,FxaaTex fxaaConsole360TexExpBiasNegOne ,FxaaTex fxaaConsole360TexExpBiasNegTwo #endif ,FxaaFloat2 fxaaQualityRcpFrame #if (FXAA_PS3 == 1) || (FXAA_360 == 1) || (FXAA_360_OPT == 1) || (FXAA_PC_CONSOLE == 1) ,FxaaFloat4 fxaaConsoleRcpFrameOpt ,FxaaFloat4 fxaaConsoleRcpFrameOpt2 ,FxaaFloat4 fxaaConsole360RcpFrameOpt2 #endif ,FxaaFloat fxaaQualitySubpix ,FxaaFloat fxaaQualityEdgeThreshold ,FxaaFloat fxaaQualityEdgeThresholdMin #if (FXAA_PS3 == 1) || (FXAA_360 == 1) || (FXAA_360_OPT == 1) || (FXAA_PC_CONSOLE == 1) ,FxaaFloat fxaaConsoleEdgeSharpness ,FxaaFloat fxaaConsoleEdgeThreshold ,FxaaFloat fxaaConsoleEdgeThresholdMin #endif #if (FXAA_360 == 1) || (FXAA_360_OPT == 1) ,FxaaFloat4 fxaaConsole360ConstDir #endif ) { FxaaFloat2 posM; posM.x = pos.x; posM.y = pos.y; #if (FXAA_GATHER4_ALPHA == 1) #if (FXAA_DISCARD == 0) FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); #if (FXAA_GREEN_AS_LUMA == 0) #define lumaM rgbyM.w #else #define lumaM rgbyM.y #endif #endif #if (FXAA_GREEN_AS_LUMA == 0) FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); #else FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); #endif #if (FXAA_DISCARD == 1) #define lumaM luma4A.w #endif #define lumaE luma4A.z #define lumaS luma4A.x #define lumaSE luma4A.y #define lumaNW luma4B.w #define lumaN luma4B.z #define lumaW luma4B.x #else FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); #if (FXAA_GREEN_AS_LUMA == 0) #define lumaM rgbyM.w #else #define lumaM rgbyM.y #endif FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); #endif FxaaFloat maxSM = max(lumaS, lumaM); FxaaFloat minSM = min(lumaS, lumaM); FxaaFloat maxESM = max(lumaE, maxSM); FxaaFloat minESM = min(lumaE, minSM); FxaaFloat maxWN = max(lumaN, lumaW); FxaaFloat minWN = min(lumaN, lumaW); FxaaFloat rangeMax = max(maxWN, maxESM); FxaaFloat rangeMin = min(minWN, minESM); FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; FxaaFloat range = rangeMax - rangeMin; FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); FxaaBool earlyExit = range < rangeMaxClamped; if(earlyExit) #if (FXAA_DISCARD == 1) FxaaDiscard; #else return rgbyM; #endif #if (FXAA_GATHER4_ALPHA == 0) FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); #else FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); #endif FxaaFloat lumaNS = lumaN + lumaS; FxaaFloat lumaWE = lumaW + lumaE; FxaaFloat subpixRcpRange = 1.0/range; FxaaFloat subpixNSWE = lumaNS + lumaWE; FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; FxaaFloat lumaNESE = lumaNE + lumaSE; FxaaFloat lumaNWNE = lumaNW + lumaNE; FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; FxaaFloat lumaNWSW = lumaNW + lumaSW; FxaaFloat lumaSWSE = lumaSW + lumaSE; FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; FxaaFloat lengthSign = fxaaQualityRcpFrame.x; FxaaBool horzSpan = edgeHorz >= edgeVert; FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; if(!horzSpan) lumaN = lumaW; if(!horzSpan) lumaS = lumaE; if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; FxaaFloat gradientN = lumaN - lumaM; FxaaFloat gradientS = lumaS - lumaM; FxaaFloat lumaNN = lumaN + lumaM; FxaaFloat lumaSS = lumaS + lumaM; FxaaBool pairN = abs(gradientN) >= abs(gradientS); FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); if(pairN) lengthSign = -lengthSign; FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); FxaaFloat2 posB; posB.x = posM.x; posB.y = posM.y; FxaaFloat2 offNP; offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; if(!horzSpan) posB.x += lengthSign * 0.5; if( horzSpan) posB.y += lengthSign * 0.5; FxaaFloat2 posN; posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; FxaaFloat2 posP; posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); FxaaFloat subpixE = subpixC * subpixC; FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); if(!pairN) lumaNN = lumaSS; FxaaFloat gradientScaled = gradient * 1.0/4.0; FxaaFloat lumaMM = lumaM - lumaNN * 0.5; FxaaFloat subpixF = subpixD * subpixE; FxaaBool lumaMLTZero = lumaMM < 0.0; lumaEndN -= lumaNN * 0.5; lumaEndP -= lumaNN * 0.5; FxaaBool doneN = abs(lumaEndN) >= gradientScaled; FxaaBool doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; FxaaBool doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; #if (FXAA_QUALITY__PS > 3) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; #if (FXAA_QUALITY__PS > 4) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; #if (FXAA_QUALITY__PS > 5) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; #if (FXAA_QUALITY__PS > 6) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; #if (FXAA_QUALITY__PS > 7) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; #if (FXAA_QUALITY__PS > 8) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; #if (FXAA_QUALITY__PS > 9) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; #if (FXAA_QUALITY__PS > 10) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; #if (FXAA_QUALITY__PS > 11) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; #if (FXAA_QUALITY__PS > 12) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; } #endif } #endif } #endif } #endif } #endif } #endif } #endif } #endif } #endif } #endif } FxaaFloat dstN = posM.x - posN.x; FxaaFloat dstP = posP.x - posM.x; if(!horzSpan) dstN = posM.y - posN.y; if(!horzSpan) dstP = posP.y - posM.y; FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; FxaaFloat spanLength = (dstP + dstN); FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; FxaaFloat spanLengthRcp = 1.0/spanLength; FxaaBool directionN = dstN < dstP; FxaaFloat dst = min(dstN, dstP); FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; FxaaFloat subpixG = subpixF * subpixF; FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; FxaaFloat subpixH = subpixG * fxaaQualitySubpix; FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; #if (FXAA_DISCARD == 1) return FxaaTexTop(tex, posM); #else return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); #endif } #endif #if (FXAA_PC_CONSOLE == 1) FxaaFloat4 FxaaPixelShader( FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir ) { FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); #if (FXAA_GREEN_AS_LUMA == 0) FxaaFloat lumaM = rgbyM.w; #else FxaaFloat lumaM = rgbyM.y; #endif FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); lumaNe += 1.0/384.0; FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; FxaaFloat lumaMinM = min(lumaMin, lumaM); FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); FxaaFloat lumaMaxM = max(lumaMax, lumaM); FxaaFloat dirSwMinusNe = lumaSw - lumaNe; FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; FxaaFloat dirSeMinusNw = lumaSe - lumaNw; if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM; FxaaFloat2 dir; dir.x = dirSwMinusNe + dirSeMinusNw; dir.y = dirSwMinusNe - dirSeMinusNw; FxaaFloat2 dir1 = normalize(dir.xy); FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw); FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw); FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); #if (FXAA_GREEN_AS_LUMA == 0) FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax); #else FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax); #endif if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5; return rgbyB; } #endif #if (FXAA_360 == 1) [reduceTempRegUsage(4)] float4 FxaaPixelShader( FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir ) { float4 lumaNwNeSwSe; #if (FXAA_GREEN_AS_LUMA == 0) asm { tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false }; #else asm { tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false }; #endif lumaNwNeSwSe.y += 1.0/384.0; float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y); float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y); float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0)); #if (FXAA_GREEN_AS_LUMA == 0) float lumaMinM = min(lumaMin, rgbyM.w); float lumaMaxM = max(lumaMax, rgbyM.w); #else float lumaMinM = min(lumaMin, rgbyM.y); float lumaMaxM = max(lumaMax, rgbyM.y); #endif if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM; float2 dir; dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx); dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy); dir = normalize(dir); float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw; float4 dir2; float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness; dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5); dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw; float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0)); float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0)); float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0)); float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0)); float4 rgbyA = rgbyN1 + rgbyP1; float4 rgbyB = rgbyN2 + rgbyP2 + rgbyA * 0.5; float4 rgbyR = ((FxaaLuma(rgbyB) - lumaMax) > 0.0) ? rgbyA : rgbyB; rgbyR = ((FxaaLuma(rgbyB) - lumaMin) > 0.0) ? rgbyR : rgbyA; return rgbyR; } #endif #if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0) #pragma regcount 7 #pragma disablepc all #pragma option O3 #pragma option OutColorPrec=fp16 #pragma texformat default RGBA8 half4 FxaaPixelShader( FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir ) { half4 dir; half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) lumaNe.w += half(1.0/512.0); dir.x = -lumaNe.w; dir.z = -lumaNe.w; #else lumaNe.y += half(1.0/512.0); dir.x = -lumaNe.y; dir.z = -lumaNe.y; #endif half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) dir.x += lumaSw.w; dir.z += lumaSw.w; #else dir.x += lumaSw.y; dir.z += lumaSw.y; #endif half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) dir.x -= lumaNw.w; dir.z += lumaNw.w; #else dir.x -= lumaNw.y; dir.z += lumaNw.y; #endif half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) dir.x += lumaSe.w; dir.z -= lumaSe.w; #else dir.x += lumaSe.y; dir.z -= lumaSe.y; #endif half4 dir1_pos; dir1_pos.xy = normalize(dir.xyz).xz; half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); half4 dir2_pos; dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0)); dir1_pos.zw = pos.xy; dir2_pos.zw = pos.xy; half4 temp1N; temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); half4 rgby1; rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); rgby1 = (temp1N + rgby1) * 0.5; half4 temp2N; temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); half4 rgby2; rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); rgby2 = (temp2N + rgby2) * 0.5; #if (FXAA_GREEN_AS_LUMA == 0) half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w)); half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w)); #else half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y)); half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y)); #endif rgby2 = (rgby2 + rgby1) * 0.5; #if (FXAA_GREEN_AS_LUMA == 0) bool twoTapLt = rgby2.w < lumaMin; bool twoTapGt = rgby2.w > lumaMax; #else bool twoTapLt = rgby2.y < lumaMin; bool twoTapGt = rgby2.y > lumaMax; #endif if(twoTapLt || twoTapGt) rgby2 = rgby1; return rgby2; } #endif #if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1) #pragma regcount 7 #pragma disablepc all #pragma option O2 #pragma option OutColorPrec=fp16 #pragma texformat default RGBA8 half4 FxaaPixelShader( FxaaFloat2 pos, FxaaFloat4 fxaaConsolePosPos, FxaaTex tex, FxaaTex fxaaConsole360TexExpBiasNegOne, FxaaTex fxaaConsole360TexExpBiasNegTwo, FxaaFloat2 fxaaQualityRcpFrame, FxaaFloat4 fxaaConsoleRcpFrameOpt, FxaaFloat4 fxaaConsoleRcpFrameOpt2, FxaaFloat4 fxaaConsole360RcpFrameOpt2, FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin, FxaaFloat fxaaConsoleEdgeSharpness, FxaaFloat fxaaConsoleEdgeThreshold, FxaaFloat fxaaConsoleEdgeThresholdMin, FxaaFloat4 fxaaConsole360ConstDir ) { half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) half lumaNe = rgbyNe.w + half(1.0/512.0); #else half lumaNe = rgbyNe.y + half(1.0/512.0); #endif half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) half lumaSwNegNe = lumaSw.w - lumaNe; #else half lumaSwNegNe = lumaSw.y - lumaNe; #endif half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) half lumaMaxNwSw = max(lumaNw.w, lumaSw.w); half lumaMinNwSw = min(lumaNw.w, lumaSw.w); #else half lumaMaxNwSw = max(lumaNw.y, lumaSw.y); half lumaMinNwSw = min(lumaNw.y, lumaSw.y); #endif half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); #if (FXAA_GREEN_AS_LUMA == 0) half dirZ = lumaNw.w + lumaSwNegNe; half dirX = -lumaNw.w + lumaSwNegNe; #else half dirZ = lumaNw.y + lumaSwNegNe; half dirX = -lumaNw.y + lumaSwNegNe; #endif half3 dir; dir.y = 0.0; #if (FXAA_GREEN_AS_LUMA == 0) dir.x = lumaSe.w + dirX; dir.z = -lumaSe.w + dirZ; half lumaMinNeSe = min(lumaNe, lumaSe.w); #else dir.x = lumaSe.y + dirX; dir.z = -lumaSe.y + dirZ; half lumaMinNeSe = min(lumaNe, lumaSe.y); #endif half4 dir1_pos; dir1_pos.xy = normalize(dir).xz; half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); half4 dir2_pos; dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0)); dir1_pos.zw = pos.xy; dir2_pos.zw = pos.xy; #if (FXAA_GREEN_AS_LUMA == 0) half lumaMaxNeSe = max(lumaNe, lumaSe.w); #else half lumaMaxNeSe = max(lumaNe, lumaSe.y); #endif half4 temp1N; temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe); half lumaMin = min(lumaMinNwSw, lumaMinNeSe); half4 rgby1; rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); rgby1 = (temp1N + rgby1) * 0.5; half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0)); #if (FXAA_GREEN_AS_LUMA == 0) half lumaMaxM = max(lumaMax, rgbyM.w); half lumaMinM = min(lumaMin, rgbyM.w); #else half lumaMaxM = max(lumaMax, rgbyM.y); half lumaMinM = min(lumaMin, rgbyM.y); #endif half4 temp2N; temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); half4 rgby2; rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE__PS3_EDGE_THRESHOLD; rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); rgby2 = (temp2N + rgby2) * 0.5; rgby2 = (rgby2 + rgby1) * 0.5; #if (FXAA_GREEN_AS_LUMA == 0) bool twoTapLt = rgby2.w < lumaMin; bool twoTapGt = rgby2.w > lumaMax; #else bool twoTapLt = rgby2.y < lumaMin; bool twoTapGt = rgby2.y > lumaMax; #endif bool earlyExit = lumaRangeM < lumaMax; bool twoTap = twoTapLt || twoTapGt; if(twoTap) rgby2 = rgby1; if(earlyExit) rgby2 = rgbyM; return rgby2; } #endif ================================================ FILE: src/antialias/module.coffee ================================================ Rendernode = require '/rendernode' Quad = require '/webgl/quad' return class AntiAlias constructor: (@gl, gui, @source) -> gui.remember @ @node = new Rendernode @gl, #program: get 'fxaa.shader' program: get 'fxaa3_11.shader' drawable: quad @subpixel_aa = 0.75 @contrast_treshold = 0.166 @edge_treshold = 0.0 folder = gui.addFolder('Antialias') folder.add(@, 'subpixel_aa', 0.0, 1.0).name('Subpixel aa') folder.add(@, 'contrast_treshold', 0.063, 0.333).name('Contrast Treshold') folder.add(@, 'edge_treshold', 0.0, 0.0833).name('Edge Treshold') apply: -> @node.start() .f('subpixel_aa', @subpixel_aa) .f('contrast_treshold', @contrast_treshold) .f('edge_treshold', @edge_treshold) .clear() .sampler('source', @source) .draw() .end() resize: (width, height) -> @node.resize width, height ================================================ FILE: src/antialias/module.js ================================================ // Generated by CoffeeScript 1.3.3 var AntiAlias, Quad, Rendernode; Rendernode = require('/rendernode'); Quad = require('/webgl/quad'); return AntiAlias = (function() { function AntiAlias(gl, gui, source) { var folder; this.gl = gl; this.source = source; gui.remember(this); this.node = new Rendernode(this.gl, { program: get('fxaa3_11.shader'), drawable: quad }); this.subpixel_aa = 0.75; this.contrast_treshold = 0.166; this.edge_treshold = 0.0; folder = gui.addFolder('Antialias'); folder.add(this, 'subpixel_aa', 0.0, 1.0).name('Subpixel aa'); folder.add(this, 'contrast_treshold', 0.063, 0.333).name('Contrast Treshold'); folder.add(this, 'edge_treshold', 0.0, 0.0833).name('Edge Treshold'); } AntiAlias.prototype.apply = function() { return this.node.start().f('subpixel_aa', this.subpixel_aa).f('contrast_treshold', this.contrast_treshold).f('edge_treshold', this.edge_treshold).clear().sampler('source', this.source).draw().end(); }; AntiAlias.prototype.resize = function(width, height) { return this.node.resize(width, height); }; return AntiAlias; })(); ================================================ FILE: src/application.coffee ================================================ schedule = require 'schedule' loading = require 'loading' camera = require 'camera' Quad = require '/webgl/quad' Cube = require '/webgl/cube' Antialias = require 'antialias' {LowresModel, Model} = require 'model' Illumination = require 'illumination' Rendernode = require '/rendernode' Windows = require '/windows' {DeferredShadowMap} = require '/depth' Sun = require 'sun' DeferredModel = require 'deferred_model' SSAO = require 'ssao' class CompositingControl constructor: (gui) -> gui.remember @ @draw_probes = false @gi = 1 @di = 1 @ao = 0.8 folder = gui.addFolder('Compositing') folder.add(@, 'draw_probes').name('Draw Probes') folder.add(@, 'gi', 0.0, 2.0).name('Glob. Illum.') folder.add(@, 'di', 0.0, 2.0).name('Direct. Illum.') folder.add(@, 'ao', 0.0, 1.0).name('SSAO') class Lighting extends require('events') constructor: (gui) -> gui.remember @ super() @sunRadiance = 1.0 @skyRadiance = 1.0 @giGain = 1.0 @bounces = 3 @sunColor = [255, 255, 255] @skyColor = [0x07, 0xcb, 0xf5] @sun_radiance = [0,0,0] @sky_radiance = [0,0,0] folder = gui.addFolder('Lighting') folder.addColor(@, 'sunColor').name('Sun Color').onChange @computeRadiance folder.add(@, 'sunRadiance', 0.0, 20.0).name('Sun Radiance').onChange @computeRadiance folder.addColor(@, 'skyColor').name('Sky Color').onChange @computeRadiance folder.add(@, 'skyRadiance', 0.0, 20.0).name('Sky Radiance').onChange @computeRadiance folder.add(@, 'giGain', 0.0, 20.0).name('GI-gain').onChange @update folder.add(@, 'bounces', 1.0, 10.0).step(1).name('Bounces').onChange @bouncesChanged @computeRadiance() @lastbounces = @bounces computeRadiance: => @sun_radiance[0] = @sunRadiance * (@sunColor[0]/255.0) @sun_radiance[1] = @sunRadiance * (@sunColor[1]/255.0) @sun_radiance[2] = @sunRadiance * (@sunColor[2]/255.0) @sky_radiance[0] = @skyRadiance * (@skyColor[0]/255.0) @sky_radiance[1] = @skyRadiance * (@skyColor[1]/255.0) @sky_radiance[2] = @skyRadiance * (@skyColor[2]/255.0) @update() bouncesChanged: => if @bounces != @lastBounces @lastBounces = @bounces @update() update: => @trigger('change') class PictureSettings constructor: (gui) -> gui.remember @ @inputGamma = 1.8 @outputGamma = 1.8 @brightness = 1.0 @saturation = 1.0 folder = gui.addFolder('Picture') folder.add(@, 'inputGamma', 0.25, 3.0).name('Input Gamma') folder.add(@, 'outputGamma', 0.25, 3.0).name('Output Gamma') folder.add(@, 'brightness', 0.0, 10.0).name('Exposure') folder.add(@, 'saturation', 0.0, 4.0).name('Saturation') class SHConstants constructor: (@app, gui) -> gui.remember @ @c1 = 0.43 @c2 = 0.66 @band3 = 1.0 @c3 = 0.9 @c4 = 0.34 @c5 = 0.43 @data = new Float32Array(5) folder = gui.addFolder('Harmonics') folder.add(@, 'c1', 0.0, 4.0).name('L0').onChange @change folder.add(@, 'c2', 0.0, 4.0).name('L1').onChange @change folder.add(@, 'band3', 0.0, 4.0).name('L2').onChange @change folder.add(@, 'c3', 0.0, 4.0).name('L2m2/L2m1/L21').onChange @change folder.add(@, 'c4', 0.0, 4.0).name('L20').onChange @change folder.add(@, 'c5', 0.0, 4.0).name('L22').onChange @change @updateData() updateData: -> @data[0] = @c1 @data[1] = @c2 @data[2] = @band3 * @c3 @data[3] = @band3 * @c4 @data[4] = @band3 * @c5 change: => @updateData() @app.lightChange() makeStat = (mode, offset) -> stats = new Stats() stats.setMode(mode) node = $(stats.domElement) node.css( position: 'absolute' left: offset top: 0 ).appendTo('body').hide() stats.hide = -> node.clearQueue().fadeOut() stats.show = -> node.clearQueue().fadeIn() return stats exports.Application = class constructor: (@canvas) -> $('
') .css('margin', 10) .appendTo('#ui') Rendernode.stateDefaults(gl) gui = @gui = new dat.GUI load: get 'presets/new.json' gui.remember @ @gui_width = gui.width = 370 @gui.closed = false @fps = makeStat(0, 0) @rtime = makeStat(1, 80) @gui_closed = gui.closed @resolution = 0.5 @resolution_label = '1:2 default' resmap = '2:1 very slow!': 2 '1:1 slow': 1 '1:2 default': 0.5 '1:4 ugly': 0.25 '1:8 worse': 0.125 folder = gui.addFolder('Performance') $('
  • WASD=move, space=overview, cursor keys=navigate
  • ').appendTo(folder.__ul) folder.add(@, 'resolution_label', ['2:1 very slow!', '1:1 slow', '1:2 default', '1:4 ugly', '1:8 worse']).name('Resolution').onChange => @resolution = resmap[@resolution_label] @resizeBuffers() @resolution = resmap[@resolution_label] @show_fps = false folder.add(@, 'show_fps').name('FPS').onChange => if @show_fps @fps.show() @rtime.show() else @fps.hide() @rtime.hide() if @show_fps @fps.show() @rtime.show() else @fps.hide() @rtime.hide() @picture = new PictureSettings(gui) @sun = new Sun(gui).on('change', @sunChanged) @compositing_control = new CompositingControl(gui) @lighting = new Lighting(gui).on('change', @lightChange) @shconst = new SHConstants @, gui loading.hide() @near = 0.1 @far = 42 @camera = new camera.FlyCam(gui: gui, near: @near, far: @far, x: -10, y:7, z:-1.5, o:100, p:20) @sponza = new Model gl @lowres = new LowresModel gl floatExt = gl.getFloatExtension require: ['renderable', 'filterable'] #highQualityExt = gl.getFloatExtension require: ['renderable', 'single'] @view_normaldepth = new Rendernode gl, program: get 'normaldepth.shader' drawable: @sponza depthBuffer: true depthTest: true depthWrite: true cullFace: 'BACK' #disabled because of mesh gaps #type: gl.FLOAT #float is required to avoid banding issues (tried packing, doesn't come out well) type: floatExt.type #type: highQualityExt.type filter: 'nearest' hdrClear: true @ssao = new SSAO gl, @view_normaldepth @direct_light = new DeferredShadowMap gl, drawable: @sponza depthWidth: 512 depthHeight: 512 eyeNormaldepth: @view_normaldepth light: @sun camera: @camera blurred: true @illumination = new Illumination gl, @sun, @lighting, @lowres, @sponza, @view_normaldepth, @sun.orientation, @sun.elevation, @shconst @albedo = new Rendernode gl, program: get 'albedo.shader' drawable: @sponza depthBuffer: true depthTest: true depthWrite: true #cullFace: 'BACK' #disabled because of mesh gaps #type: gl.FLOAT #not really required, adds little, the source is bytes anyway @global_illumination = new Rendernode gl, program: get 'global_illumination.shader' drawable: new DeferredModel gl, @illumination.probes cullFace: 'FRONT' blend: 'additive' #type: gl.FLOAT #float is required because of additive summation, does not yield a perf benefit type: floatExt.type, depthBuffer: @view_normaldepth.depth # early z gives some performance depthWrite: false depthTest: 'GEQUAL' @composit = new Rendernode gl, program: get 'composit.shader' drawable: quad @antialias = new Antialias gl, gui, @composit @windows = new Windows gl, gui, [ {label: 'Scene depth from sun', affine: [1, 0], gamma: false, tex: @direct_light.depth.output}, {label: 'Scene normal/depth', affine: [0.5, 0.5], gamma: false, tex: @view_normaldepth}, #{label: 'Scene normal/depth', affine: [0.1, 0.0], gamma: false, tex: @view_normaldepth}, {label: 'Scene depth moments', gamma: false, tex: @ssao.blur.output}, {label: 'Direct Illumination Lightmap', tex: @illumination.direct_light.output}, {label: 'Global Illumination Lightmap', diva: true, tex: @illumination.bounce}, {label: 'Lightmap Dictionary', tex: @illumination.texmap}, {label: 'Albedo Probe Values', tex: @illumination.diffusemap}, {label: 'Light Probes', tex: @illumination.lightprobes}, {label: 'Spherical Harmonics Coefficients', tex: @illumination.coefficients}, {label: 'Albedo', tex: @albedo}, {label: 'SSAO', gamma: false, tex: @ssao.output}, {label: 'Direct Illumination', tex: @direct_light.output}, {label: 'Global Illumination', diva: true, tex: @global_illumination}, {label: 'Composited', gamma: false, tex: @composit}, {label: 'Antialiased', gamma: false, tex: @antialias.node}, ] @target_width = @canvas.width() @current_width = @target_width $(window).resize @resize @resize() schedule.run @update @canvas.fadeIn(2000) $('div.dg > ul').css('margin-top', 0) sunChanged: => @direct_light.updateDepth() @illumination.updateDirectLight() @lightChange() lightChange: => @illumination.update() resizeBuffers: (width, height) -> w = @width*@resolution h = @height*@resolution @view_normaldepth.resize w, h @albedo.resize w, h @global_illumination.resize w, h @direct_light.resize w, h @composit.resize w, h @antialias.resize w, h @illumination.debug.resize w, h @ssao.resize w, h resize: => @width = @canvas.width() @height = @canvas.height() @camera.aspect @width, @height @canvas[0].width = @width @canvas[0].height = @height @resizeBuffers @width, @height @resizeWindows() update: => @fps.end() @fps.begin() @rtime.begin() @step() @draw() @rtime.end() resizeWindows: -> @windows.node.viewport 0, 0, @current_width, @height step: -> gui_closed = @gui.closed if gui_closed @target_width = @width else gui_width = @gui.width @target_width = @width - (gui_width+10) dw = @target_width - @current_width @current_width = @current_width+dw*0.1 dw = Math.abs(@target_width - @current_width) if dw > 1 @resizeWindows() else if dw <= 1 and dw > 0 @current_width = @target_width @resizeWindows() @camera.update() draw: -> #@illumination.update() #TODO @view_normaldepth .clear(0, 0, 0, 100) .start() .clearDepth() .mat4('proj', @camera.proj) .mat4('view', @camera.view) .mat3('view_rot', @camera.rot) .drawModel('bumpmap') .end() @ssao.update() @albedo.start() .f('gamma', @picture.inputGamma) .mat4('proj', @camera.proj) .mat4('view', @camera.view) .mat3('view_rot', @camera.rot) .clearBoth(0, 0, 0, 0) .drawModel('diffuse_texture') .end() @global_illumination.start() .f('gi_gain', @lighting.giGain) .fv('shconst', @shconst.data) .sampler('normaldepth', @view_normaldepth) .mat4('proj', @camera.proj) .mat4('view', @camera.view) .mat4('inv_view', @camera.inv_view) .sampler('coefficients', @illumination.coefficients) .val2('coefficients_size', @illumination.coefficients.width, @illumination.coefficients.height) .clear() .draw() .end() @direct_light.updateShadow() if @compositing_control.draw_probes probe_factor = 1 @illumination.drawDebug(@camera, @view_normaldepth) else probe_factor = 0 @composit.start() .clear() .f('gamma', @picture.outputGamma) .f('brightness', @picture.brightness) .f('saturation', @picture.saturation) .vec3('sun_radiance', @lighting.sun_radiance) .vec3('sky_radiance', @lighting.sky_radiance) .f('probe_factor', probe_factor) .f('gi_factor', @compositing_control.gi) .f('di_factor', @compositing_control.di) .f('ao_factor', @compositing_control.ao) .vec3('sky_color', @lighting.skyColor) .sampler('debug', @illumination.debug) .sampler('albedo', @albedo) .sampler('global', @global_illumination) .sampler('direct', @direct_light.output) .sampler('ssao', @ssao.output) .draw() .end() @antialias.apply() @windows.draw(@picture.outputGamma) ================================================ FILE: src/application.js ================================================ // Generated by CoffeeScript 1.3.3 var Antialias, CompositingControl, Cube, DeferredModel, DeferredShadowMap, Illumination, Lighting, LowresModel, Model, PictureSettings, Quad, Rendernode, SHConstants, SSAO, Sun, Windows, camera, loading, makeStat, schedule, _ref, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; schedule = require('schedule'); loading = require('loading'); camera = require('camera'); Quad = require('/webgl/quad'); Cube = require('/webgl/cube'); Antialias = require('antialias'); _ref = require('model'), LowresModel = _ref.LowresModel, Model = _ref.Model; Illumination = require('illumination'); Rendernode = require('/rendernode'); Windows = require('/windows'); DeferredShadowMap = require('/depth').DeferredShadowMap; Sun = require('sun'); DeferredModel = require('deferred_model'); SSAO = require('ssao'); CompositingControl = (function() { function CompositingControl(gui) { var folder; gui.remember(this); this.draw_probes = false; this.gi = 1; this.di = 1; this.ao = 0.8; folder = gui.addFolder('Compositing'); folder.add(this, 'draw_probes').name('Draw Probes'); folder.add(this, 'gi', 0.0, 2.0).name('Glob. Illum.'); folder.add(this, 'di', 0.0, 2.0).name('Direct. Illum.'); folder.add(this, 'ao', 0.0, 1.0).name('SSAO'); } return CompositingControl; })(); Lighting = (function(_super) { __extends(Lighting, _super); function Lighting(gui) { this.update = __bind(this.update, this); this.bouncesChanged = __bind(this.bouncesChanged, this); this.computeRadiance = __bind(this.computeRadiance, this); var folder; gui.remember(this); Lighting.__super__.constructor.call(this); this.sunRadiance = 1.0; this.skyRadiance = 1.0; this.giGain = 1.0; this.bounces = 3; this.sunColor = [255, 255, 255]; this.skyColor = [0x07, 0xcb, 0xf5]; this.sun_radiance = [0, 0, 0]; this.sky_radiance = [0, 0, 0]; folder = gui.addFolder('Lighting'); folder.addColor(this, 'sunColor').name('Sun Color').onChange(this.computeRadiance); folder.add(this, 'sunRadiance', 0.0, 20.0).name('Sun Radiance').onChange(this.computeRadiance); folder.addColor(this, 'skyColor').name('Sky Color').onChange(this.computeRadiance); folder.add(this, 'skyRadiance', 0.0, 20.0).name('Sky Radiance').onChange(this.computeRadiance); folder.add(this, 'giGain', 0.0, 20.0).name('GI-gain').onChange(this.update); folder.add(this, 'bounces', 1.0, 10.0).step(1).name('Bounces').onChange(this.bouncesChanged); this.computeRadiance(); this.lastbounces = this.bounces; } Lighting.prototype.computeRadiance = function() { this.sun_radiance[0] = this.sunRadiance * (this.sunColor[0] / 255.0); this.sun_radiance[1] = this.sunRadiance * (this.sunColor[1] / 255.0); this.sun_radiance[2] = this.sunRadiance * (this.sunColor[2] / 255.0); this.sky_radiance[0] = this.skyRadiance * (this.skyColor[0] / 255.0); this.sky_radiance[1] = this.skyRadiance * (this.skyColor[1] / 255.0); this.sky_radiance[2] = this.skyRadiance * (this.skyColor[2] / 255.0); return this.update(); }; Lighting.prototype.bouncesChanged = function() { if (this.bounces !== this.lastBounces) { this.lastBounces = this.bounces; return this.update(); } }; Lighting.prototype.update = function() { return this.trigger('change'); }; return Lighting; })(require('events')); PictureSettings = (function() { function PictureSettings(gui) { var folder; gui.remember(this); this.inputGamma = 1.8; this.outputGamma = 1.8; this.brightness = 1.0; this.saturation = 1.0; folder = gui.addFolder('Picture'); folder.add(this, 'inputGamma', 0.25, 3.0).name('Input Gamma'); folder.add(this, 'outputGamma', 0.25, 3.0).name('Output Gamma'); folder.add(this, 'brightness', 0.0, 10.0).name('Exposure'); folder.add(this, 'saturation', 0.0, 4.0).name('Saturation'); } return PictureSettings; })(); SHConstants = (function() { function SHConstants(app, gui) { var folder; this.app = app; this.change = __bind(this.change, this); gui.remember(this); this.c1 = 0.43; this.c2 = 0.66; this.band3 = 1.0; this.c3 = 0.9; this.c4 = 0.34; this.c5 = 0.43; this.data = new Float32Array(5); folder = gui.addFolder('Harmonics'); folder.add(this, 'c1', 0.0, 4.0).name('L0').onChange(this.change); folder.add(this, 'c2', 0.0, 4.0).name('L1').onChange(this.change); folder.add(this, 'band3', 0.0, 4.0).name('L2').onChange(this.change); folder.add(this, 'c3', 0.0, 4.0).name('L2m2/L2m1/L21').onChange(this.change); folder.add(this, 'c4', 0.0, 4.0).name('L20').onChange(this.change); folder.add(this, 'c5', 0.0, 4.0).name('L22').onChange(this.change); this.updateData(); } SHConstants.prototype.updateData = function() { this.data[0] = this.c1; this.data[1] = this.c2; this.data[2] = this.band3 * this.c3; this.data[3] = this.band3 * this.c4; return this.data[4] = this.band3 * this.c5; }; SHConstants.prototype.change = function() { this.updateData(); return this.app.lightChange(); }; return SHConstants; })(); makeStat = function(mode, offset) { var node, stats; stats = new Stats(); stats.setMode(mode); node = $(stats.domElement); node.css({ position: 'absolute', left: offset, top: 0 }).appendTo('body').hide(); stats.hide = function() { return node.clearQueue().fadeOut(); }; stats.show = function() { return node.clearQueue().fadeIn(); }; return stats; }; exports.Application = (function() { function _Class(canvas) { var floatExt, folder, gui, resmap, _this = this; this.canvas = canvas; this.update = __bind(this.update, this); this.resize = __bind(this.resize, this); this.lightChange = __bind(this.lightChange, this); this.sunChanged = __bind(this.sunChanged, this); $('
    ').css('margin', 10).appendTo('#ui'); Rendernode.stateDefaults(gl); gui = this.gui = new dat.GUI({ load: get('presets/new.json') }); gui.remember(this); this.gui_width = gui.width = 370; this.gui.closed = false; this.fps = makeStat(0, 0); this.rtime = makeStat(1, 80); this.gui_closed = gui.closed; this.resolution = 0.5; this.resolution_label = '1:2 default'; resmap = { '2:1 very slow!': 2, '1:1 slow': 1, '1:2 default': 0.5, '1:4 ugly': 0.25, '1:8 worse': 0.125 }; folder = gui.addFolder('Performance'); $('
  • WASD=move, space=overview, cursor keys=navigate
  • ').appendTo(folder.__ul); folder.add(this, 'resolution_label', ['2:1 very slow!', '1:1 slow', '1:2 default', '1:4 ugly', '1:8 worse']).name('Resolution').onChange(function() { _this.resolution = resmap[_this.resolution_label]; return _this.resizeBuffers(); }); this.resolution = resmap[this.resolution_label]; this.show_fps = false; folder.add(this, 'show_fps').name('FPS').onChange(function() { if (_this.show_fps) { _this.fps.show(); return _this.rtime.show(); } else { _this.fps.hide(); return _this.rtime.hide(); } }); if (this.show_fps) { this.fps.show(); this.rtime.show(); } else { this.fps.hide(); this.rtime.hide(); } this.picture = new PictureSettings(gui); this.sun = new Sun(gui).on('change', this.sunChanged); this.compositing_control = new CompositingControl(gui); this.lighting = new Lighting(gui).on('change', this.lightChange); this.shconst = new SHConstants(this, gui); loading.hide(); this.near = 0.1; this.far = 42; this.camera = new camera.FlyCam({ gui: gui, near: this.near, far: this.far, x: -10, y: 7, z: -1.5, o: 100, p: 20 }); this.sponza = new Model(gl); this.lowres = new LowresModel(gl); floatExt = gl.getFloatExtension({ require: ['renderable', 'filterable'] }); this.view_normaldepth = new Rendernode(gl, { program: get('normaldepth.shader'), drawable: this.sponza, depthBuffer: true, depthTest: true, depthWrite: true, cullFace: 'BACK', type: floatExt.type, filter: 'nearest', hdrClear: true }); this.ssao = new SSAO(gl, this.view_normaldepth); this.direct_light = new DeferredShadowMap(gl, { drawable: this.sponza, depthWidth: 512, depthHeight: 512, eyeNormaldepth: this.view_normaldepth, light: this.sun, camera: this.camera, blurred: true }); this.illumination = new Illumination(gl, this.sun, this.lighting, this.lowres, this.sponza, this.view_normaldepth, this.sun.orientation, this.sun.elevation, this.shconst); this.albedo = new Rendernode(gl, { program: get('albedo.shader'), drawable: this.sponza, depthBuffer: true, depthTest: true, depthWrite: true }); this.global_illumination = new Rendernode(gl, { program: get('global_illumination.shader'), drawable: new DeferredModel(gl, this.illumination.probes), cullFace: 'FRONT', blend: 'additive', type: floatExt.type, depthBuffer: this.view_normaldepth.depth, depthWrite: false, depthTest: 'GEQUAL' }); this.composit = new Rendernode(gl, { program: get('composit.shader'), drawable: quad }); this.antialias = new Antialias(gl, gui, this.composit); this.windows = new Windows(gl, gui, [ { label: 'Scene depth from sun', affine: [1, 0], gamma: false, tex: this.direct_light.depth.output }, { label: 'Scene normal/depth', affine: [0.5, 0.5], gamma: false, tex: this.view_normaldepth }, { label: 'Scene depth moments', gamma: false, tex: this.ssao.blur.output }, { label: 'Direct Illumination Lightmap', tex: this.illumination.direct_light.output }, { label: 'Global Illumination Lightmap', diva: true, tex: this.illumination.bounce }, { label: 'Lightmap Dictionary', tex: this.illumination.texmap }, { label: 'Albedo Probe Values', tex: this.illumination.diffusemap }, { label: 'Light Probes', tex: this.illumination.lightprobes }, { label: 'Spherical Harmonics Coefficients', tex: this.illumination.coefficients }, { label: 'Albedo', tex: this.albedo }, { label: 'SSAO', gamma: false, tex: this.ssao.output }, { label: 'Direct Illumination', tex: this.direct_light.output }, { label: 'Global Illumination', diva: true, tex: this.global_illumination }, { label: 'Composited', gamma: false, tex: this.composit }, { label: 'Antialiased', gamma: false, tex: this.antialias.node } ]); this.target_width = this.canvas.width(); this.current_width = this.target_width; $(window).resize(this.resize); this.resize(); schedule.run(this.update); this.canvas.fadeIn(2000); $('div.dg > ul').css('margin-top', 0); } _Class.prototype.sunChanged = function() { this.direct_light.updateDepth(); this.illumination.updateDirectLight(); return this.lightChange(); }; _Class.prototype.lightChange = function() { return this.illumination.update(); }; _Class.prototype.resizeBuffers = function(width, height) { var h, w; w = this.width * this.resolution; h = this.height * this.resolution; this.view_normaldepth.resize(w, h); this.albedo.resize(w, h); this.global_illumination.resize(w, h); this.direct_light.resize(w, h); this.composit.resize(w, h); this.antialias.resize(w, h); this.illumination.debug.resize(w, h); return this.ssao.resize(w, h); }; _Class.prototype.resize = function() { this.width = this.canvas.width(); this.height = this.canvas.height(); this.camera.aspect(this.width, this.height); this.canvas[0].width = this.width; this.canvas[0].height = this.height; this.resizeBuffers(this.width, this.height); return this.resizeWindows(); }; _Class.prototype.update = function() { this.fps.end(); this.fps.begin(); this.rtime.begin(); this.step(); this.draw(); return this.rtime.end(); }; _Class.prototype.resizeWindows = function() { return this.windows.node.viewport(0, 0, this.current_width, this.height); }; _Class.prototype.step = function() { var dw, gui_closed, gui_width; gui_closed = this.gui.closed; if (gui_closed) { this.target_width = this.width; } else { gui_width = this.gui.width; this.target_width = this.width - (gui_width + 10); } dw = this.target_width - this.current_width; this.current_width = this.current_width + dw * 0.1; dw = Math.abs(this.target_width - this.current_width); if (dw > 1) { this.resizeWindows(); } else if (dw <= 1 && dw > 0) { this.current_width = this.target_width; this.resizeWindows(); } return this.camera.update(); }; _Class.prototype.draw = function() { var probe_factor; this.view_normaldepth.clear(0, 0, 0, 100).start().clearDepth().mat4('proj', this.camera.proj).mat4('view', this.camera.view).mat3('view_rot', this.camera.rot).drawModel('bumpmap').end(); this.ssao.update(); this.albedo.start().f('gamma', this.picture.inputGamma).mat4('proj', this.camera.proj).mat4('view', this.camera.view).mat3('view_rot', this.camera.rot).clearBoth(0, 0, 0, 0).drawModel('diffuse_texture').end(); this.global_illumination.start().f('gi_gain', this.lighting.giGain).fv('shconst', this.shconst.data).sampler('normaldepth', this.view_normaldepth).mat4('proj', this.camera.proj).mat4('view', this.camera.view).mat4('inv_view', this.camera.inv_view).sampler('coefficients', this.illumination.coefficients).val2('coefficients_size', this.illumination.coefficients.width, this.illumination.coefficients.height).clear().draw().end(); this.direct_light.updateShadow(); if (this.compositing_control.draw_probes) { probe_factor = 1; this.illumination.drawDebug(this.camera, this.view_normaldepth); } else { probe_factor = 0; } this.composit.start().clear().f('gamma', this.picture.outputGamma).f('brightness', this.picture.brightness).f('saturation', this.picture.saturation).vec3('sun_radiance', this.lighting.sun_radiance).vec3('sky_radiance', this.lighting.sky_radiance).f('probe_factor', probe_factor).f('gi_factor', this.compositing_control.gi).f('di_factor', this.compositing_control.di).f('ao_factor', this.compositing_control.ao).vec3('sky_color', this.lighting.skyColor).sampler('debug', this.illumination.debug).sampler('albedo', this.albedo).sampler('global', this.global_illumination).sampler('direct', this.direct_light.output).sampler('ssao', this.ssao.output).draw().end(); this.antialias.apply(); return this.windows.draw(this.picture.outputGamma); }; return _Class; })(); ================================================ FILE: src/blur/blur.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform sampler2D source; uniform vec2 viewport; void main(){ vec4 sum = vec4(0.0); float divider = 0.0; for(float x=-2.0; x<=2.0; x++){ for(float y=-2.0; y<=2.0; y++){ vec2 coord = vec2(x,y); float l = length(coord)+1.0; float factor = 1.0/l; divider += factor; sum += texture2D(source, (gl_FragCoord.xy+vec2(x,y))/viewport) * factor; } } gl_FragColor = sum/divider; } ================================================ FILE: src/blur/module.coffee ================================================ Rendernode = require '/rendernode' return class Blur constructor: (gl, {width, height, type, filter}) -> type ?= gl.UNSIGNED_BYTE filter ?= 'linear' @output = new Rendernode gl, width: width height: height program: get 'blur.shader' drawable: quad filter: filter type: type update: (source) -> @output .start() .sampler('source', source) .draw() .end() resize: (width, height) -> @output.resize(width, height) ================================================ FILE: src/blur/module.js ================================================ // Generated by CoffeeScript 1.3.3 var Blur, Rendernode; Rendernode = require('/rendernode'); return Blur = (function() { function Blur(gl, _arg) { var filter, height, type, width; width = _arg.width, height = _arg.height, type = _arg.type, filter = _arg.filter; if (type == null) { type = gl.UNSIGNED_BYTE; } if (filter == null) { filter = 'linear'; } this.output = new Rendernode(gl, { width: width, height: height, program: get('blur.shader'), drawable: quad, filter: filter, type: type }); } Blur.prototype.update = function(source) { return this.output.start().sampler('source', source).draw().end(); }; Blur.prototype.resize = function(width, height) { return this.output.resize(width, height); }; return Blur; })(); ================================================ FILE: src/composit.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform sampler2D global, direct, albedo, debug, ssao; uniform vec2 viewport; uniform float gamma, brightness, saturation; uniform float probe_factor, di_factor, gi_factor, ao_factor; uniform vec3 sky_radiance, sun_radiance; void main(){ float occlusion = mix(1.0, texture2D(ssao, gl_FragCoord.xy/viewport).r, ao_factor); vec4 global_data = texture2D(global, gl_FragCoord.xy/viewport); vec3 global_irradiance = max(global_data.rgb/global_data.a, vec3(0.0)); vec3 direct_irradiance = texture2D(direct, gl_FragCoord.xy/viewport).rgb * sun_radiance; vec4 diffuse_color = texture2D(albedo, gl_FragCoord.xy/viewport); vec3 irradiance = (global_irradiance*gi_factor + direct_irradiance*di_factor)*occlusion; vec3 excident = mix(sky_radiance, diffuse_color.rgb*irradiance, diffuse_color.a); vec4 debug_data = texture2D(debug, gl_FragCoord.xy/viewport); vec3 color = mix(excident, debug_data.rgb, debug_data.a*probe_factor); color = brightness * color; vec3 luma_coeff = vec3(0.2125, 0.7154, 0.0721); vec3 intensity = vec3(dot(color, luma_coeff)); color = mix(intensity, color, saturation); gl_FragColor = vec4(clamp(pow(color, vec3(1.0/gamma)), vec3(0.0), vec3(1.0)), 1.0); } ================================================ FILE: src/deferred_model.coffee ================================================ Sphere = require '/webgl/sphere' return class DeferredModel extends require('webgl/drawable') attribs: ['position', 'lightprobe', 'center'] pointers: [ {name: 'position', size: 3, offset: 0, stride: 7}, {name: 'lightprobe', size: 4, offset: 3, stride: 7}, ] constructor: (@gl, probes) -> super() template = Sphere.makeVertices(5.1, 2) buffer = [] for probe, i in probes px = probe.x py = probe.y pz = probe.z for vi in [0...template.length] by 3 x = template[vi] y = template[vi+1] z = template[vi+2] buffer.push(x,y,z,px,py,pz,i) @size = buffer.length/7 @uploadList buffer ================================================ FILE: src/deferred_model.js ================================================ // Generated by CoffeeScript 1.3.3 var DeferredModel, Sphere, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Sphere = require('/webgl/sphere'); return DeferredModel = (function(_super) { __extends(DeferredModel, _super); DeferredModel.prototype.attribs = ['position', 'lightprobe', 'center']; DeferredModel.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 7 }, { name: 'lightprobe', size: 4, offset: 3, stride: 7 } ]; function DeferredModel(gl, probes) { var buffer, i, probe, px, py, pz, template, vi, x, y, z, _i, _j, _len, _ref; this.gl = gl; DeferredModel.__super__.constructor.call(this); template = Sphere.makeVertices(5.1, 2); buffer = []; for (i = _i = 0, _len = probes.length; _i < _len; i = ++_i) { probe = probes[i]; px = probe.x; py = probe.y; pz = probe.z; for (vi = _j = 0, _ref = template.length; _j < _ref; vi = _j += 3) { x = template[vi]; y = template[vi + 1]; z = template[vi + 2]; buffer.push(x, y, z, px, py, pz, i); } } this.size = buffer.length / 7; this.uploadList(buffer); } return DeferredModel; })(require('webgl/drawable')); ================================================ FILE: src/depth/deferred_shadow_map.shader ================================================ varying vec2 clip; vertex: attribute vec2 position; void main(){ clip = position; gl_Position = vec4(position, 0.0, 1.0); } fragment: #require variance uniform sampler2D eye_normaldepth; uniform mat4 inv_eye_proj, inv_eye_view; uniform vec2 viewport; void main(){ vec4 eye_data = texture2D(eye_normaldepth, gl_FragCoord.xy/viewport); vec3 normal = eye_data.xyz; float depth = eye_data.w; vec4 device_pos = inv_eye_proj * vec4(clip, 1.0, 1.0); vec3 eye_normal = normalize(device_pos.xyz); vec3 eye_pos = depth * eye_normal; vec3 position = (inv_eye_view * vec4(eye_pos, 1.0)).xyz; gl_FragColor = vec4(vec3(getIntensity(position, normal)), 1.0); } ================================================ FILE: src/depth/depth.shader ================================================ varying vec3 vViewPosition; uniform mat4 proj, view; vertex: attribute vec3 position; void main(){ vViewPosition = (view * vec4(position, 1.0)).xyz; gl_Position = proj * view * vec4(position, 1.0); } fragment: #extension GL_OES_standard_derivatives : enable uniform float range; void main(){ float z = -vViewPosition.z/range; float dx = dFdx(z); float dy = dFdy(z); gl_FragColor = vec4(z, z*z + 0.25*(dx*dx + dy*dy), 0.0, 1.0); } ================================================ FILE: src/depth/lightmap_shadow_map.shader ================================================ varying vec3 vPosition, vNormal; vertex: attribute vec3 position, normal; attribute vec2 texcoord; void main(){ vPosition = position; vNormal = normal; gl_Position = vec4(texcoord*2.0-1.0, 0.0, 1.0); } fragment: #require variance void main(){ gl_FragColor = vec4(vec3(getIntensity(vPosition, normalize(vNormal))), 1.0); } ================================================ FILE: src/depth/module.coffee ================================================ Rendernode = require '/rendernode' Blur = require '/blur' exports.DepthRender = class DepthRender constructor: (gl, width, height, drawable, {blurred}={}) -> blurred ?= false floatExt = gl.getFloatExtension require: ['renderable', 'filterable'] @direct = new Rendernode gl, width: width height: height program: get 'depth.shader' drawable: drawable depthBuffer: true depthTest: true depthWrite: true filter: if blurred then 'nearest' else 'linear' #type: gl.FLOAT #float is required because of depth precision type: floatExt.type cullFace: 'BACK' if blurred @blurred = new Blur gl, width: width height: height #type: gl.FLOAT type: floatExt.type @output = if @blurred then @blurred.output else @direct update: (proj, view) -> @direct.start() .clearBoth(0,0,0,1) .mat4('proj', proj) .mat4('view', view) .f('range', 42) #FIXME .draw() .end() if @blurred @blurred.update(@direct) exports.DeferredShadowMap = class DeferredShadowMap constructor: (gl, {drawable, depthWidth, depthHeight, @eyeNormaldepth, @light, @camera, blurred}) -> @depth = new DepthRender gl, depthWidth, depthHeight, drawable, blurred:blurred @output = new Rendernode gl, program: get 'deferred_shadow_map.shader' drawable: quad @updateDepth() resize: (width, height) -> @output.resize width, height updateDepth: -> @depth.update @light.proj, @light.view updateShadow: -> @output .start() .clear(1, 0, 1) .sampler('eye_normaldepth', @eyeNormaldepth) .sampler('light_depth', @depth.output) .mat4('inv_eye_proj', @camera.inv_proj) .mat4('inv_eye_view', @camera.inv_view) .mat4('light_view', @light.view) .mat4('light_proj', @light.proj) .mat3('light_rot', @light.rot) .draw() .end() exports.LightmapShadowMap = class LightmappedShadowMap constructor: (gl, {drawable, depthWidth, depthHeight, lightmapSize, @light, blurred}) -> @depth = new DepthRender gl, depthWidth, depthHeight, drawable, blurred:blurred lightmapSize ?= 256 @output = new Rendernode gl, width: lightmapSize height: lightmapSize program: get 'lightmap_shadow_map.shader' drawable: drawable @update() update: -> @depth.update @light.proj, @light.view @output .start() .sampler('light_depth', @depth.output) .mat4('light_view', @light.view) .mat4('light_proj', @light.proj) .mat3('light_rot', @light.rot) .draw() .end() ================================================ FILE: src/depth/module.js ================================================ // Generated by CoffeeScript 1.3.3 var Blur, DeferredShadowMap, DepthRender, LightmappedShadowMap, Rendernode; Rendernode = require('/rendernode'); Blur = require('/blur'); exports.DepthRender = DepthRender = (function() { function DepthRender(gl, width, height, drawable, _arg) { var blurred, floatExt; blurred = (_arg != null ? _arg : {}).blurred; if (blurred == null) { blurred = false; } floatExt = gl.getFloatExtension({ require: ['renderable', 'filterable'] }); this.direct = new Rendernode(gl, { width: width, height: height, program: get('depth.shader'), drawable: drawable, depthBuffer: true, depthTest: true, depthWrite: true, filter: blurred ? 'nearest' : 'linear', type: floatExt.type, cullFace: 'BACK' }); if (blurred) { this.blurred = new Blur(gl, { width: width, height: height, type: floatExt.type }); } this.output = this.blurred ? this.blurred.output : this.direct; } DepthRender.prototype.update = function(proj, view) { this.direct.start().clearBoth(0, 0, 0, 1).mat4('proj', proj).mat4('view', view).f('range', 42).draw().end(); if (this.blurred) { return this.blurred.update(this.direct); } }; return DepthRender; })(); exports.DeferredShadowMap = DeferredShadowMap = (function() { function DeferredShadowMap(gl, _arg) { var blurred, depthHeight, depthWidth, drawable; drawable = _arg.drawable, depthWidth = _arg.depthWidth, depthHeight = _arg.depthHeight, this.eyeNormaldepth = _arg.eyeNormaldepth, this.light = _arg.light, this.camera = _arg.camera, blurred = _arg.blurred; this.depth = new DepthRender(gl, depthWidth, depthHeight, drawable, { blurred: blurred }); this.output = new Rendernode(gl, { program: get('deferred_shadow_map.shader'), drawable: quad }); this.updateDepth(); } DeferredShadowMap.prototype.resize = function(width, height) { return this.output.resize(width, height); }; DeferredShadowMap.prototype.updateDepth = function() { return this.depth.update(this.light.proj, this.light.view); }; DeferredShadowMap.prototype.updateShadow = function() { return this.output.start().clear(1, 0, 1).sampler('eye_normaldepth', this.eyeNormaldepth).sampler('light_depth', this.depth.output).mat4('inv_eye_proj', this.camera.inv_proj).mat4('inv_eye_view', this.camera.inv_view).mat4('light_view', this.light.view).mat4('light_proj', this.light.proj).mat3('light_rot', this.light.rot).draw().end(); }; return DeferredShadowMap; })(); exports.LightmapShadowMap = LightmappedShadowMap = (function() { function LightmappedShadowMap(gl, _arg) { var blurred, depthHeight, depthWidth, drawable, lightmapSize; drawable = _arg.drawable, depthWidth = _arg.depthWidth, depthHeight = _arg.depthHeight, lightmapSize = _arg.lightmapSize, this.light = _arg.light, blurred = _arg.blurred; this.depth = new DepthRender(gl, depthWidth, depthHeight, drawable, { blurred: blurred }); if (lightmapSize == null) { lightmapSize = 256; } this.output = new Rendernode(gl, { width: lightmapSize, height: lightmapSize, program: get('lightmap_shadow_map.shader'), drawable: drawable }); this.update(); } LightmappedShadowMap.prototype.update = function() { this.depth.update(this.light.proj, this.light.view); return this.output.start().sampler('light_depth', this.depth.output).mat4('light_view', this.light.view).mat4('light_proj', this.light.proj).mat3('light_rot', this.light.rot).draw().end(); }; return LightmappedShadowMap; })(); ================================================ FILE: src/depth/variance.shaderlib ================================================ uniform sampler2D light_depth; uniform mat4 light_view, light_proj; uniform mat3 light_rot; float getOcclusion(vec2 uv, float z){ vec2 moments = texture2D(light_depth, uv).xy; float p = smoothstep(z-0.02, z-0.01, moments.x); float variance = moments.y - moments.x*moments.x; float d = z - moments.x; float p_max = variance/(variance+d*d); p_max = smoothstep(0.4, 1.0, p_max); return max(p, p_max); } float getIntensity(vec3 position, vec3 normal){ vec4 light_view_position = light_view * vec4(position, 1.0); vec4 light_device = light_proj * light_view_position; vec2 light_clip = light_device.xy/light_device.w; vec2 uv = light_clip*0.5+0.5; float z = -light_view_position.z/42.0; float occlusion = getOcclusion(uv, z); float lambert = clamp((light_rot * normal).z, 0.0, 1.0); return occlusion * lambert; } ================================================ FILE: src/direct_illumination.shader ================================================ varying vec2 clip; vertex: attribute vec2 position; void main(){ clip = position; gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform sampler2D sun_normaldepth, eye_normaldepth; uniform float eye_near, eye_far, sun_near, sun_far; uniform mat4 inv_eye_proj, inv_eye_view, sun_view, sun_proj; uniform mat3 sun_rot, inv_eye_rot; uniform vec2 sun_normaldepth_size; uniform vec2 viewport; uniform float epsilon, shadow_bias, shadow_distance; vec2 getMoments(vec2 uv, float x, float y){ vec4 sun_data = texture2D(sun_normaldepth, uv + vec2(x,y)/sun_normaldepth_size); return sun_data.rg; } float linstep(float low, float high, float v){ return clamp((v-low)/(high-low), 0.0, 1.0); } float occlusionFun(vec2 moments, float z){ float p = smoothstep(z-0.02, z-0.01, moments.x); float variance = moments.y - moments.x*moments.x; float d = z - moments.x; float p_max = variance/(variance+d*d); //p_max = linstep(0.4, 1.0, p_max); p_max = smoothstep(0.4, 1.0, p_max); return max(p, p_max); } float getOcclusion(vec2 uv, float z, float x, float y){ vec2 moments = getMoments(uv, x, y); return occlusionFun(moments, z); } void main(){ vec4 eye_data = texture2D(eye_normaldepth, gl_FragCoord.xy/viewport); vec3 normal = eye_data.xyz; float depth = eye_data.w; vec4 device_pos = inv_eye_proj * vec4(clip, 1.0, 1.0); vec3 eye_normal = normalize(device_pos.xyz); vec3 eye_pos = depth * eye_normal; vec3 position = (inv_eye_view * vec4(eye_pos, 1.0)).xyz; vec4 sun_view_position = sun_view * vec4(position, 1.0); vec4 sun_device = sun_proj * sun_view_position; vec2 sun_clip = sun_device.xy/sun_device.w; vec2 uv = sun_clip*0.5+0.5; float z = -sun_view_position.z/42.0; //float occlusion = getOcclusion(uv, z, 0.0, 0.0); float occlusion = getOcclusion(uv, z, 0.0, 0.0); float lambert = clamp((sun_rot * normal).z, 0.0, 1.0); //float intensity = clamp(min(occlusion, lambert), 0.0, 1.0); float intensity = occlusion * lambert; //float intensity = (1.0 - occlusion)*lambert; vec3 color = intensity * vec3(1.0, 0.95, 1.0); gl_FragColor = vec4(color, 1.0); //gl_FragColor = vec4(vec3(occlusion), 1.0); } ================================================ FILE: src/dist3d.coffee ================================================ vsub = (p1, p2) -> return [ p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2], ] vadd = (p1, p2) -> return [ p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2], ] sadd = (s, p) -> x=p[0]; y=p[1]; z=p[2] return [x+s, y+s, z+s] slength = (p) -> x=p[0]; y=p[1]; z=p[2] return x*x + y*y + z*z length = (p) -> return Math.sqrt(slength(p)) dot = (p1, p2) -> x1=p1[0]; y1=p1[1]; z1=p1[2] x2=p2[0]; y2=p2[1]; z2=p2[2] return x1*x2 + y1*y2 + z1*z2 smul = (s, p) -> x=p[0]; y=p[1]; z=p[2] return [x*s, y*s, z*s] cross = (p1, p2) -> x1=p1[0]; y1=p1[1]; z1=p1[2] x2=p2[0]; y2=p2[1]; z2=p2[2] return [ y1*z2 - z1*y2, z1*x2 - x1*z2, x1*y2 - y1*x2, ] exports.closestPointTriangle = (p, a, b, c) -> ab = vsub(b, a) ac = vsub(c, a) bc = vsub(c, b) snom = dot(vsub(p,a), ab); sdenom = dot(vsub(p,b), vsub(a,b)) tnom = dot(vsub(p, a), ac); tdenom = dot(vsub(p,c), vsub(a,c)) if snom <= 0 and tnom <= 0 then return a unom = dot(vsub(p,b), bc); udenom = dot(vsub(p,c), vsub(b,c)) if sdenom <= 0 and unom <= 0 then return b if tdenom <= 0 and udenom <= 0 then return c n = cross(vsub(b,a), vsub(c,a)) vc = dot(n, cross(vsub(a,p), vsub(b,p))) if vc <= 0 and snom >= 0 and sdenom >= 0 return vadd(a, smul(snom/(snom + sdenom), ab)) va = dot(n, cross(vsub(b,p), vsub(c,p))) if va <= 0 and unom >= 0 and udenom >= 0 return vadd(b, smul(unom/(unom + udenom), bc)) vb = dot(n, cross(vsub(c,p), vsub(a,p))) if vb <= 0 and tnom > 0 and tdenom >= 0 return vadd(a, smul(tnom/(tnom + tdenom), ac)) u = va/(va+vb+vc) v = vb/(va+vb+vc) w = 1-u-v return vadd(smul(u, a), vadd(smul(v,b), smul(w,c))) exports.pointTriangleDist = (p, v0, v1, v2) -> [cx, cy, cz] = exports.closestPointTriangle(p, v0, v1, v2) [px, py, pz] = p dx=cx-px; dy=cy-py; dz=cz-pz return Math.sqrt(dx*dx + dy*dy + dz*dz) ================================================ FILE: src/dist3d.js ================================================ // Generated by CoffeeScript 1.3.3 var cross, dot, length, sadd, slength, smul, vadd, vsub; vsub = function(p1, p2) { return [p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]]; }; vadd = function(p1, p2) { return [p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2]]; }; sadd = function(s, p) { var x, y, z; x = p[0]; y = p[1]; z = p[2]; return [x + s, y + s, z + s]; }; slength = function(p) { var x, y, z; x = p[0]; y = p[1]; z = p[2]; return x * x + y * y + z * z; }; length = function(p) { return Math.sqrt(slength(p)); }; dot = function(p1, p2) { var x1, x2, y1, y2, z1, z2; x1 = p1[0]; y1 = p1[1]; z1 = p1[2]; x2 = p2[0]; y2 = p2[1]; z2 = p2[2]; return x1 * x2 + y1 * y2 + z1 * z2; }; smul = function(s, p) { var x, y, z; x = p[0]; y = p[1]; z = p[2]; return [x * s, y * s, z * s]; }; cross = function(p1, p2) { var x1, x2, y1, y2, z1, z2; x1 = p1[0]; y1 = p1[1]; z1 = p1[2]; x2 = p2[0]; y2 = p2[1]; z2 = p2[2]; return [y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2]; }; exports.closestPointTriangle = function(p, a, b, c) { var ab, ac, bc, n, sdenom, snom, tdenom, tnom, u, udenom, unom, v, va, vb, vc, w; ab = vsub(b, a); ac = vsub(c, a); bc = vsub(c, b); snom = dot(vsub(p, a), ab); sdenom = dot(vsub(p, b), vsub(a, b)); tnom = dot(vsub(p, a), ac); tdenom = dot(vsub(p, c), vsub(a, c)); if (snom <= 0 && tnom <= 0) { return a; } unom = dot(vsub(p, b), bc); udenom = dot(vsub(p, c), vsub(b, c)); if (sdenom <= 0 && unom <= 0) { return b; } if (tdenom <= 0 && udenom <= 0) { return c; } n = cross(vsub(b, a), vsub(c, a)); vc = dot(n, cross(vsub(a, p), vsub(b, p))); if (vc <= 0 && snom >= 0 && sdenom >= 0) { return vadd(a, smul(snom / (snom + sdenom), ab)); } va = dot(n, cross(vsub(b, p), vsub(c, p))); if (va <= 0 && unom >= 0 && udenom >= 0) { return vadd(b, smul(unom / (unom + udenom), bc)); } vb = dot(n, cross(vsub(c, p), vsub(a, p))); if (vb <= 0 && tnom > 0 && tdenom >= 0) { return vadd(a, smul(tnom / (tnom + tdenom), ac)); } u = va / (va + vb + vc); v = vb / (va + vb + vc); w = 1 - u - v; return vadd(smul(u, a), vadd(smul(v, b), smul(w, c))); }; exports.pointTriangleDist = function(p, v0, v1, v2) { var cx, cy, cz, dx, dy, dz, px, py, pz, _ref; _ref = exports.closestPointTriangle(p, v0, v1, v2), cx = _ref[0], cy = _ref[1], cz = _ref[2]; px = p[0], py = p[1], pz = p[2]; dx = cx - px; dy = cy - py; dz = cz - pz; return Math.sqrt(dx * dx + dy * dy + dz * dz); }; ================================================ FILE: src/global_illumination.shader ================================================ /* Conclusion of is that the shader is fillrate bound, not lookup bound 42 fps without GI 42 fps when discarding in VS 21 fps with unified geometry 21 fps with early z and discard 21 fps with early z and gl_FragColor write 21 fps when discarding in FS 17 fps with GI 17 fps without discard 17 fps without light lookup 13 fps without conditional/discard */ varying vec3 vViewPosition; varying vec4 vLightprobe; uniform mat4 proj, view; uniform sampler2D normaldepth; vertex: attribute vec3 position, center; attribute vec4 lightprobe; void main(){ vec3 pos = lightprobe.xyz + position; vViewPosition = (view * vec4(pos, 1.0)).xyz; vLightprobe = lightprobe; gl_Position = proj * view * vec4(pos, 1.0); /* // gives some speed, difficult to get rid of artifacts, very uneven performance vec4 center_view = view * vec4(lightprobe.xyz+center, 1.0); vec4 center_proj = proj * center_view; clip = center_proj.xy/center_proj.w; float scene_depth = texture2D(normaldepth, clip*0.5+0.5).w; float center_depth = length(center_view.xyz); float dist = distance(normalize(center_view.xyz) * scene_depth, (view * vec4(lightprobe.xyz, 1.0)).xyz); if(abs(clip.x) < 1.0 && abs(clip.y) < 1.0 && dist < 5.0){ gl_Position = proj * view * vec4(pos, 1.0); } else{ gl_Position = vec4(2.0); } */ } fragment: #require harmonics uniform mat4 inv_view; uniform vec2 viewport; uniform float gi_gain; //uniform float index; void main(){ vec4 data = texture2D(normaldepth, gl_FragCoord.xy/viewport); vec3 normal = data.xyz; float depth = data.w; vec3 eye_pos = depth * normalize(vViewPosition); vec3 position = (inv_view * vec4(eye_pos, 1.0)).xyz; // apply the global illumination float dist = distance(vLightprobe.xyz, position); float lambert = dot(normal, normalize(vLightprobe.xyz-position)); if(dist < 5.0 && lambert > 0.0){ float falloff = 1.0 - clamp(dist/5.0, 0.0, 1.0); float intensity = clamp(pow(falloff, 1.5) * lambert, 0.0, 1.0); vec3 irradiance = sphericalHarmonics(vLightprobe.w, normal)*gi_gain; gl_FragColor = vec4(irradiance * intensity, intensity); } else{ discard; } } ================================================ FILE: src/harmonics.shaderlib ================================================ uniform vec2 coefficients_size; uniform sampler2D coefficients; uniform float shconst[5]; vec3 getCoefficient(float index, float m){ vec2 coord = vec2(m+0.5, index+0.5)/coefficients_size; return texture2D(coefficients, coord).rgb; } vec3 sphericalHarmonics(float index, vec3 normal){ float x = normal.x; float y = normal.y; float z = normal.z; vec3 l00 = getCoefficient(index, 0.0); vec3 l10 = getCoefficient(index, 1.0); vec3 l11 = getCoefficient(index, 2.0); vec3 l12 = getCoefficient(index, 3.0); vec3 l20 = getCoefficient(index, 4.0); vec3 l21 = getCoefficient(index, 5.0); vec3 l22 = getCoefficient(index, 6.0); vec3 l23 = getCoefficient(index, 7.0); vec3 l24 = getCoefficient(index, 8.0); vec3 result = ( l00 * shconst[0] + l12 * shconst[1] * x + l10 * shconst[1] * y + l11 * shconst[1] * z + l20 * shconst[2] * x*y + l21 * shconst[2] * y*z + l22 * shconst[3] * (3.0*z*z - 1.0) + l23 * shconst[2] * x*z + l24 * shconst[4] * (x*x - y*y) ); return max(result, vec3(0.0)); } ================================================ FILE: src/illumination/bounce.shader ================================================ varying vec3 vPosition, vNormal; varying vec4 vLightprobe; vertex: attribute vec3 position, normal; attribute vec2 texcoord; attribute vec4 lightprobe; void main(){ vPosition = position; vNormal = normal; vLightprobe = lightprobe; gl_Position = vec4(texcoord*2.0-1.0, 0.0, 1.0); } fragment: #require /harmonics uniform vec2 viewport; uniform float gi_gain; void main(){ // compute world normal vec3 normal = normalize(vNormal); // apply the global illumination float dist = distance(vLightprobe.xyz, vPosition); float lambert = dot(normal, normalize(vLightprobe.xyz-vPosition)); if(dist < 5.0 && lambert > 0.0){ float falloff = 1.0 - clamp(dist/5.0, 0.0, 1.0); float intensity = clamp(pow(falloff, 1.5) * lambert, 0.0, 1.0); vec3 irradiance = sphericalHarmonics(vLightprobe.w, normal)*gi_gain; gl_FragColor = vec4(irradiance * intensity, intensity); } else{ discard; } } ================================================ FILE: src/illumination/bounce_model.coffee ================================================ {pointTriangleDist} = require '/dist3d' return class BounceModel extends require('/webgl/drawable') attribs: ['position', 'texcoord', 'normal', 'lightprobe'] pointers: [ {name: 'position', size: 3, offset: 0, stride: 12}, {name: 'texcoord', size: 2, offset: 3, stride: 12}, {name: 'normal', size: 3, offset: 5, stride: 12}, {name: 'lightprobe', size: 4, offset: 8, stride: 12}, ] constructor: (@gl, model, probes) -> super() start = gettime() vertices = model.vertices vertex_count = vertices.length/8 face_count = vertex_count/3 result = [] for i in [0...face_count] verti = i*3 vali = verti*8 x1 = vertices[vali+0] y1 = vertices[vali+1] z1 = vertices[vali+2] u1 = vertices[vali+3] v1 = vertices[vali+4] nx1 = vertices[vali+5] ny1 = vertices[vali+6] nz1 = vertices[vali+7] vali = verti*8+8 x2 = vertices[vali+0] y2 = vertices[vali+1] z2 = vertices[vali+2] u2 = vertices[vali+3] v2 = vertices[vali+4] nx2 = vertices[vali+5] ny2 = vertices[vali+6] nz2 = vertices[vali+7] vali = verti*8+16 x3 = vertices[vali+0] y3 = vertices[vali+1] z3 = vertices[vali+2] u3 = vertices[vali+3] v3 = vertices[vali+4] nx3 = vertices[vali+5] ny3 = vertices[vali+6] nz3 = vertices[vali+7] for probe, i in probes px = probe.x; py = probe.y; pz = probe.z dx1 = px-x1; dy1 = py-y1; dz1 = pz-z1 l = Math.sqrt(dx1*dx1+dy1*dy1+dz1*dz1) dx1/=l; dy1/=l; dz1/=l dot1 = dx1*nx1 + dy1*ny1 + dz1*nz1 dx2 = px-x2; dy2 = py-y2; dz2 = pz-z2 l = Math.sqrt(dx2*dx2+dy2*dy2+dz2*dz2) dx2/=l; dy2/=l; dz2/=l dot2 = dx2*nx2 + dy2*ny2 + dz2*nz2 dx3 = px-x3; dy3 = py-y3; dz3 = pz-z3 l = Math.sqrt(dx3*dx3+dy3*dy3+dz3*dz3) dx3/=l; dy3/=l; dz3/=l dot3 = dx3*nx3 + dy3*ny3 + dz3*nz3 # triangle plane tx=x2-x1; ty=y2-y1; tz=z2-z1 btx=x3-x1; bty=y3-y1; btz=z3-z1 fnx = ty*btz - tz*bty fny = tz*btx - tx*btz fnz = tx*bty - ty*btx l = Math.sqrt(fnx*fnx + fny*fny + fnz*fnz) fnx/=l; fny/=l; fnz/=l det = fnx*x1 + fny*y1 + fnz*z1 dist = Math.abs((fnx*px + fny*py + fnz*pz)-det) if (dot1 >= 0 or dot2 >= 0 or dot3 >= 0) and dist <= 5.0 if pointTriangleDist([px, py, pz], [x1, y1, z1], [x2, y2, z2], [x3, y3, z3]) <= 5.0 result.push( x1, y1, z1, u1, v1, nx1, ny1, nz1, px, py, pz, i, x2, y2, z2, u2, v2, nx2, ny2, nz2, px, py, pz, i, x3, y3, z3, u3, v3, nx3, ny3, nz3, px, py, pz, i, ) #console.log (result.length/12)/((vertices.length/8)*probes.length) @size = result.length/12 @uploadList result #console.log gettime() - start ================================================ FILE: src/illumination/bounce_model.js ================================================ // Generated by CoffeeScript 1.3.3 var BounceModel, pointTriangleDist, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; pointTriangleDist = require('/dist3d').pointTriangleDist; return BounceModel = (function(_super) { __extends(BounceModel, _super); BounceModel.prototype.attribs = ['position', 'texcoord', 'normal', 'lightprobe']; BounceModel.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 12 }, { name: 'texcoord', size: 2, offset: 3, stride: 12 }, { name: 'normal', size: 3, offset: 5, stride: 12 }, { name: 'lightprobe', size: 4, offset: 8, stride: 12 } ]; function BounceModel(gl, model, probes) { var btx, bty, btz, det, dist, dot1, dot2, dot3, dx1, dx2, dx3, dy1, dy2, dy3, dz1, dz2, dz3, face_count, fnx, fny, fnz, i, l, nx1, nx2, nx3, ny1, ny2, ny3, nz1, nz2, nz3, probe, px, py, pz, result, start, tx, ty, tz, u1, u2, u3, v1, v2, v3, vali, vertex_count, verti, vertices, x1, x2, x3, y1, y2, y3, z1, z2, z3, _i, _j, _len; this.gl = gl; BounceModel.__super__.constructor.call(this); start = gettime(); vertices = model.vertices; vertex_count = vertices.length / 8; face_count = vertex_count / 3; result = []; for (i = _i = 0; 0 <= face_count ? _i < face_count : _i > face_count; i = 0 <= face_count ? ++_i : --_i) { verti = i * 3; vali = verti * 8; x1 = vertices[vali + 0]; y1 = vertices[vali + 1]; z1 = vertices[vali + 2]; u1 = vertices[vali + 3]; v1 = vertices[vali + 4]; nx1 = vertices[vali + 5]; ny1 = vertices[vali + 6]; nz1 = vertices[vali + 7]; vali = verti * 8 + 8; x2 = vertices[vali + 0]; y2 = vertices[vali + 1]; z2 = vertices[vali + 2]; u2 = vertices[vali + 3]; v2 = vertices[vali + 4]; nx2 = vertices[vali + 5]; ny2 = vertices[vali + 6]; nz2 = vertices[vali + 7]; vali = verti * 8 + 16; x3 = vertices[vali + 0]; y3 = vertices[vali + 1]; z3 = vertices[vali + 2]; u3 = vertices[vali + 3]; v3 = vertices[vali + 4]; nx3 = vertices[vali + 5]; ny3 = vertices[vali + 6]; nz3 = vertices[vali + 7]; for (i = _j = 0, _len = probes.length; _j < _len; i = ++_j) { probe = probes[i]; px = probe.x; py = probe.y; pz = probe.z; dx1 = px - x1; dy1 = py - y1; dz1 = pz - z1; l = Math.sqrt(dx1 * dx1 + dy1 * dy1 + dz1 * dz1); dx1 /= l; dy1 /= l; dz1 /= l; dot1 = dx1 * nx1 + dy1 * ny1 + dz1 * nz1; dx2 = px - x2; dy2 = py - y2; dz2 = pz - z2; l = Math.sqrt(dx2 * dx2 + dy2 * dy2 + dz2 * dz2); dx2 /= l; dy2 /= l; dz2 /= l; dot2 = dx2 * nx2 + dy2 * ny2 + dz2 * nz2; dx3 = px - x3; dy3 = py - y3; dz3 = pz - z3; l = Math.sqrt(dx3 * dx3 + dy3 * dy3 + dz3 * dz3); dx3 /= l; dy3 /= l; dz3 /= l; dot3 = dx3 * nx3 + dy3 * ny3 + dz3 * nz3; tx = x2 - x1; ty = y2 - y1; tz = z2 - z1; btx = x3 - x1; bty = y3 - y1; btz = z3 - z1; fnx = ty * btz - tz * bty; fny = tz * btx - tx * btz; fnz = tx * bty - ty * btx; l = Math.sqrt(fnx * fnx + fny * fny + fnz * fnz); fnx /= l; fny /= l; fnz /= l; det = fnx * x1 + fny * y1 + fnz * z1; dist = Math.abs((fnx * px + fny * py + fnz * pz) - det); if ((dot1 >= 0 || dot2 >= 0 || dot3 >= 0) && dist <= 5.0) { if (pointTriangleDist([px, py, pz], [x1, y1, z1], [x2, y2, z2], [x3, y3, z3]) <= 5.0) { result.push(x1, y1, z1, u1, v1, nx1, ny1, nz1, px, py, pz, i, x2, y2, z2, u2, v2, nx2, ny2, nz2, px, py, pz, i, x3, y3, z3, u3, v3, nx3, ny3, nz3, px, py, pz, i); } } } } this.size = result.length / 12; this.uploadList(result); } return BounceModel; })(require('/webgl/drawable')); ================================================ FILE: src/illumination/cube_diffuse.shader ================================================ varying vec2 vTexcoord; varying vec3 vPosition; uniform mat4 proj, view; vertex: attribute vec3 position; attribute vec2 texcoord; void main(){ vTexcoord = texcoord; vPosition = position; gl_Position = proj * view * vec4(position, 1.0); } fragment: uniform sampler2D diffuse_texture; uniform vec3 diffuse_color; void main(){ vec3 diffuse = pow(texture2D(diffuse_texture, vTexcoord).rgb, vec3(1.8)); gl_FragColor = vec4(diffuse*pow(diffuse_color, vec3(1.8)), 1.0); } ================================================ FILE: src/illumination/cubeprobe.shader ================================================ varying vec2 vTexcoord; varying vec3 vPosition; uniform mat4 proj, view; vertex: attribute vec3 position; attribute vec2 texcoord; void main(){ vTexcoord = texcoord; vPosition = position; gl_Position = proj * view * vec4(position, 1.0); } fragment: void main(){ gl_FragColor = vec4(vTexcoord, 0.0, 1.0); } ================================================ FILE: src/illumination/debug.shader ================================================ varying vec3 vPosition, vViewPosition; uniform mat4 proj, view; vertex: attribute vec3 position; uniform vec3 offset; void main(){ vPosition = position; vViewPosition = (view * vec4(position+offset, 1.0)).xyz; gl_Position = proj * view * vec4(position+offset, 1.0); } fragment: #require /harmonics uniform sampler2D normaldepth; uniform vec2 viewport; uniform float gi_gain; uniform float index; void main(){ vec4 data = texture2D(normaldepth, gl_FragCoord.xy/viewport); float depth = data.w; if(length(vViewPosition) < depth){ gl_FragColor = vec4(sphericalHarmonics(index, normalize(vPosition))*gi_gain, 1.0); } else{ discard; } } ================================================ FILE: src/illumination/harmonics.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform sampler2D lightprobes; uniform vec2 lightprobes_size; uniform float shconst[5]; float pi = 3.141592653589793; mat3 front = mat3( 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ); mat3 back = mat3( -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0 ); mat3 left = mat3( 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0 ); mat3 right = mat3( 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0 ); mat3 up = mat3( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0 ); mat3 down = mat3( 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 ); float harmonics(vec3 normal){ int index = int(gl_FragCoord.x); float x = normal.x; float y = normal.y; float z = normal.z; /* if(index==0){ return shconst[0]; } else if(index==1){ return shconst[1] * y; } else if(index==2){ return shconst[1] * z; } else if(index==3){ return shconst[1] * x; } else if(index==4){ return shconst[2]*x*y; } else if(index==5){ return shconst[2]*y*z; } else if(index==6){ return shconst[3] * (3.0*z*z - 1.0); } else if(index==7){ return shconst[2]*x*z; } else{ return shconst[4]*(x*x - y*y); } */ if(index==0){ return 1.0; } else if(index==1){ return y; } else if(index==2){ return z; } else if(index==3){ return x; } else if(index==4){ return x*y; } else if(index==5){ return y*z; } else if(index==6){ return 3.0*z*z - 1.0; } else if(index==7){ return x*z; } else{ return x*x - y*y; } } #define probe_size 16 vec3 sampleSide(float side, mat3 rot){ vec3 result = vec3(0.0); float divider = 0.0; //(140,6): error X3511: unable to unroll loop, loop does not appear to terminate in a timely manner (9 iterations), use the [unroll(n)] attribute to force an exact higher number /* for(int y=0; y @proj = new Mat4().perspective(90, 1, 0.01, 42) @view = new Mat4() @mapsize = 32 @probesize = 16 @generateProbes() floatExt = @gl.getFloatExtension require: ['renderable', 'filterable'] @debug = new Rendernode @gl, program: get 'debug.shader' drawable: new Sphere @gl, 0.6 depthBuffer: true depthTest: true depthWrite: true cullFace: 'BACK' #type: @gl.FLOAT #maybe, not really essential to scene quality type: floatExt.type @lightprobes = new Rendernode @gl, width: @probesize*6 height: @probesize*@probes.length program: get 'transfer.shader' drawable: quad filter: 'nearest' #type: @gl.FLOAT #float is required due to HDR, maybe could solve this with color packing type: floatExt.type @coefficients = new Rendernode @gl, width: 9 height: @probes.length program: get 'harmonics.shader' drawable: quad filter: 'nearest' #type: @gl.FLOAT #float is required, otherwise bad banding and wrong colors, maybe could solve this with color packing type: floatExt.type @direct_light = new LightmapShadowMap gl, drawable: model depthWidth: 128 depthHeight: 128 light: sun blurred: true @bounce = new Rendernode @gl, width: 256 height: 256 program: get 'bounce.shader' drawable: new BounceModel @gl, model, @probes #type: @gl.FLOAT #float is required due to additive blending type: floatExt.type blend: 'additive' @renderProbes(model, highresmodel) @update() generateProbes: () -> @probes = [] for i in [0...7] @probes.push x: i*4.5 - 3*4.5 y: 2.2 z: 0 @probes.push x: i*4.5 - 3*4.5 y: 2.2 z: 5.5 @probes.push x: i*4.5 - 3*4.5 y: 2.2 z: -5.5 @probes.push x: i*4.5 - 3*4.5 y: 7.0 z: 0 @probes.push x: i*4.5 - 3*4.5 y: 7.0 z: 5.5 @probes.push x: i*4.5 - 3*4.5 y: 7.0 z: -5.5 for i in [1...6] @probes.push x: i*4.5 - 3*4.5 y: 12.5 z: 0 updateDirectLight: -> @direct_light.update() update: () -> @bounce.start() .clear(0, 0, 0, 1) .f('gi_gain', @lighting.giGain) .val2('coefficients_size', @coefficients.width, @coefficients.height) .fv('shconst', @shconst.data) .end() @lightprobes .start() .vec3('sun_radiance', @lighting.sun_radiance) .vec3('sky_radiance', @lighting.sky_radiance) .sampler('texmap', @texmap) .sampler('diffusemap', @diffusemap) .sampler('bounce', @bounce) .sampler('lightmap', @direct_light.output) .draw() .end() @coefficients .start() .val2('lightprobes_size', @lightprobes.width, @lightprobes.height) .sampler('lightprobes', @lightprobes) .fv('shconst', @shconst.data) .draw() .end() for i in [0...@lighting.bounces-1] @bounce .start() .clear(0, 0, 0, 0) .sampler('coefficients', @coefficients) .draw() .end() @lightprobes .start() .sampler('texmap', @texmap) .sampler('bounce', @bounce) .sampler('lightmap', @direct_light.output) .draw() .end() @coefficients .start() .val2('lightprobes_size', @lightprobes.width, @lightprobes.height) .sampler('lightprobes', @lightprobes) .draw() .end() renderProbes: (model, highresmodel) -> if get.exists 'texmap.png' @texmap = new Texture2D(@gl) .bind() .upload(get 'texmap.png') .nearest() .clampToEdge() .unbind() else @texmap = new Rendernode @gl, width: @mapsize*6 height: @mapsize*@probes.length program: get 'cubeprobe.shader' drawable: model depthTest: true depthWrite: true cullFace: 'BACK' filter: 'nearest' depthBuffer: true @texmap.start().clear(0, 0, 1) @texmap.mat4('proj', @proj) for probe, i in @probes @renderProbe(i, @texmap, null, probe.x, probe.y, probe.z) @texmap.end() @texmap = @texmap.output if get.exists 'diffusemap.jpg' @diffusemap = new Texture2D(@gl) .bind() .upload(get 'diffusemap.jpg') .nearest() .clampToEdge() .unbind() else @diffusemap = new Rendernode @gl, width: @mapsize*6 height: @mapsize*@probes.length program: get 'cube_diffuse.shader' drawable: highresmodel depthTest: true depthWrite: true cullFace: 'BACK' filter: 'nearest' depthBuffer: true @diffusemap.start().clear(0, 0, 0) @diffusemap.mat4('proj', @proj) for probe, i in @probes @renderProbe(i, @diffusemap, 'diffuse_texture', probe.x, probe.y, probe.z) @diffusemap.end() @diffusemap = @diffusemap.output #url = getURL @texmap.output.read().buffer #$('texmap').appendTo('#ui')[0].href = url #url = getURL @diffusemap.output.read().buffer #$('texmap').appendTo('#ui')[0].href = url renderProbe: (i, node, texture_type, x, y, z) -> s = @mapsize offset = i*s @view.identity().translateVal3(-x, -y, -z) node.viewport(s*0, offset, s, s).mat4('view', @view).drawModel(texture_type) @view.identity().rotatey(180).translateVal3(-x, -y, -z) node.viewport(s*1, offset, s, s).mat4('view', @view).drawModel(texture_type) @view.identity().rotatey(-90).translateVal3(-x, -y, -z) node.viewport(s*2, offset, s, s).mat4('view', @view).drawModel(texture_type) @view.identity().rotatey(90).translateVal3(-x, -y, -z) node.viewport(s*3, offset, s, s).mat4('view', @view).drawModel(texture_type) @view.identity().rotatex(-90).translateVal3(-x, -y, -z) node.viewport(s*4, offset, s, s).mat4('view', @view).drawModel(texture_type) @view.identity().rotatex(90).translateVal3(-x, -y, -z) node.viewport(s*5, offset, s, s).mat4('view', @view).drawModel(texture_type) drawDebug: (camera, normaldepth) -> @debug.start() .clearBoth(0, 0, 0, 0) .f('gi_gain', @lighting.giGain) .sampler('normaldepth', normaldepth) .sampler('coefficients', @coefficients) .val2('coefficients_size', @coefficients.width, @coefficients.height) .fv('shconst', @shconst.data) .mat4('proj', camera.proj) .mat4('view', camera.view) for probe, i in @probes @debug .val3('offset', probe.x, probe.y, probe.z) .f('index', i) .draw() @debug.end() ================================================ FILE: src/illumination/module.js ================================================ // Generated by CoffeeScript 1.3.3 var BounceModel, DepthRender, Illumination, LightmapShadowMap, Quad, Rendernode, Sphere, Texture2D, _ref; Quad = require('/webgl/quad'); Sphere = require('/webgl/sphere'); Texture2D = require('/webgl/texture').Texture2D; Rendernode = require('/rendernode'); _ref = require('/depth'), DepthRender = _ref.DepthRender, LightmapShadowMap = _ref.LightmapShadowMap; BounceModel = require('bounce_model'); return Illumination = (function() { function Illumination(gl, sun, lighting, model, highresmodel, normaldepth, orientation, elevation, shconst) { var floatExt; this.gl = gl; this.lighting = lighting; this.shconst = shconst; this.proj = new Mat4().perspective(90, 1, 0.01, 42); this.view = new Mat4(); this.mapsize = 32; this.probesize = 16; this.generateProbes(); floatExt = this.gl.getFloatExtension({ require: ['renderable', 'filterable'] }); this.debug = new Rendernode(this.gl, { program: get('debug.shader'), drawable: new Sphere(this.gl, 0.6), depthBuffer: true, depthTest: true, depthWrite: true, cullFace: 'BACK', type: floatExt.type }); this.lightprobes = new Rendernode(this.gl, { width: this.probesize * 6, height: this.probesize * this.probes.length, program: get('transfer.shader'), drawable: quad, filter: 'nearest', type: floatExt.type }); this.coefficients = new Rendernode(this.gl, { width: 9, height: this.probes.length, program: get('harmonics.shader'), drawable: quad, filter: 'nearest', type: floatExt.type }); this.direct_light = new LightmapShadowMap(gl, { drawable: model, depthWidth: 128, depthHeight: 128, light: sun, blurred: true }); this.bounce = new Rendernode(this.gl, { width: 256, height: 256, program: get('bounce.shader'), drawable: new BounceModel(this.gl, model, this.probes), type: floatExt.type, blend: 'additive' }); this.renderProbes(model, highresmodel); this.update(); } Illumination.prototype.generateProbes = function() { var i, _i, _j, _results; this.probes = []; for (i = _i = 0; _i < 7; i = ++_i) { this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 2.2, z: 0 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 2.2, z: 5.5 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 2.2, z: -5.5 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 7.0, z: 0 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 7.0, z: 5.5 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 7.0, z: -5.5 }); } _results = []; for (i = _j = 1; _j < 6; i = ++_j) { _results.push(this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 12.5, z: 0 })); } return _results; }; Illumination.prototype.updateDirectLight = function() { return this.direct_light.update(); }; Illumination.prototype.update = function() { var i, _i, _ref1, _results; this.bounce.start().clear(0, 0, 0, 1).f('gi_gain', this.lighting.giGain).val2('coefficients_size', this.coefficients.width, this.coefficients.height).fv('shconst', this.shconst.data).end(); this.lightprobes.start().vec3('sun_radiance', this.lighting.sun_radiance).vec3('sky_radiance', this.lighting.sky_radiance).sampler('texmap', this.texmap).sampler('diffusemap', this.diffusemap).sampler('bounce', this.bounce).sampler('lightmap', this.direct_light.output).draw().end(); this.coefficients.start().val2('lightprobes_size', this.lightprobes.width, this.lightprobes.height).sampler('lightprobes', this.lightprobes).fv('shconst', this.shconst.data).draw().end(); _results = []; for (i = _i = 0, _ref1 = this.lighting.bounces - 1; 0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) { this.bounce.start().clear(0, 0, 0, 0).sampler('coefficients', this.coefficients).draw().end(); this.lightprobes.start().sampler('texmap', this.texmap).sampler('bounce', this.bounce).sampler('lightmap', this.direct_light.output).draw().end(); _results.push(this.coefficients.start().val2('lightprobes_size', this.lightprobes.width, this.lightprobes.height).sampler('lightprobes', this.lightprobes).draw().end()); } return _results; }; Illumination.prototype.renderProbes = function(model, highresmodel) { var i, probe, _i, _j, _len, _len1, _ref1, _ref2; if (get.exists('texmap.png')) { this.texmap = new Texture2D(this.gl).bind().upload(get('texmap.png')).nearest().clampToEdge().unbind(); } else { this.texmap = new Rendernode(this.gl, { width: this.mapsize * 6, height: this.mapsize * this.probes.length, program: get('cubeprobe.shader'), drawable: model, depthTest: true, depthWrite: true, cullFace: 'BACK', filter: 'nearest', depthBuffer: true }); this.texmap.start().clear(0, 0, 1); this.texmap.mat4('proj', this.proj); _ref1 = this.probes; for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) { probe = _ref1[i]; this.renderProbe(i, this.texmap, null, probe.x, probe.y, probe.z); } this.texmap.end(); this.texmap = this.texmap.output; } if (get.exists('diffusemap.jpg')) { return this.diffusemap = new Texture2D(this.gl).bind().upload(get('diffusemap.jpg')).nearest().clampToEdge().unbind(); } else { this.diffusemap = new Rendernode(this.gl, { width: this.mapsize * 6, height: this.mapsize * this.probes.length, program: get('cube_diffuse.shader'), drawable: highresmodel, depthTest: true, depthWrite: true, cullFace: 'BACK', filter: 'nearest', depthBuffer: true }); this.diffusemap.start().clear(0, 0, 0); this.diffusemap.mat4('proj', this.proj); _ref2 = this.probes; for (i = _j = 0, _len1 = _ref2.length; _j < _len1; i = ++_j) { probe = _ref2[i]; this.renderProbe(i, this.diffusemap, 'diffuse_texture', probe.x, probe.y, probe.z); } this.diffusemap.end(); return this.diffusemap = this.diffusemap.output; } }; Illumination.prototype.renderProbe = function(i, node, texture_type, x, y, z) { var offset, s; s = this.mapsize; offset = i * s; this.view.identity().translateVal3(-x, -y, -z); node.viewport(s * 0, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatey(180).translateVal3(-x, -y, -z); node.viewport(s * 1, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatey(-90).translateVal3(-x, -y, -z); node.viewport(s * 2, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatey(90).translateVal3(-x, -y, -z); node.viewport(s * 3, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatex(-90).translateVal3(-x, -y, -z); node.viewport(s * 4, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatex(90).translateVal3(-x, -y, -z); return node.viewport(s * 5, offset, s, s).mat4('view', this.view).drawModel(texture_type); }; Illumination.prototype.drawDebug = function(camera, normaldepth) { var i, probe, _i, _len, _ref1; this.debug.start().clearBoth(0, 0, 0, 0).f('gi_gain', this.lighting.giGain).sampler('normaldepth', normaldepth).sampler('coefficients', this.coefficients).val2('coefficients_size', this.coefficients.width, this.coefficients.height).fv('shconst', this.shconst.data).mat4('proj', camera.proj).mat4('view', camera.view); _ref1 = this.probes; for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) { probe = _ref1[i]; this.debug.val3('offset', probe.x, probe.y, probe.z).f('index', i).draw(); } return this.debug.end(); }; return Illumination; })(); ================================================ FILE: src/illumination/shadow.shader ================================================ varying vec2 vTexcoord; varying vec3 vPosition, vNormal; varying vec3 vViewPosition; vertex: attribute vec3 position, normal; attribute vec2 texcoord; void main(){ vTexcoord = texcoord; vPosition = position; vNormal = normal; gl_Position = vec4(texcoord*2.0-1.0, 0.0, 1.0); } fragment: uniform mat4 shadow_view, shadow_proj; uniform mat3 shadow_rot; uniform sampler2D depth; void main(){ vec3 normal = normalize(vNormal); vec4 shadow_view = shadow_view * vec4(vPosition, 1.0); vec4 shadow_device = shadow_proj * shadow_view; vec2 shadow_coord = (shadow_device.xy/shadow_device.w)*0.5+0.5; float occlusion = 0.0; for(int u=-2; u<=2; u++){ for(int v=-2; v<=2; v++){ vec4 shadow_value = texture2D(depth, shadow_coord+vec2(u,v)/2048.0); float shadow_depth = shadow_value.w; vec3 shadow_normal = normalize(shadow_value.xyz); float diff = shadow_view.z - shadow_value.w; float shadowed = smoothstep(-0.04, -0.02, diff); occlusion += shadowed; } } occlusion /= 25.0; //float lambert = step(0.01, (shadow_rot * normal).z); float lambert = clamp((shadow_rot * normal).z, 0.0, 1.0); float intensity = min(occlusion, lambert); //float intensity = occlusion*lambert; vec3 color = intensity * vec3(1.0, 0.95, 1.0); gl_FragColor = vec4(color, 1.0); } ================================================ FILE: src/illumination/transfer.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform sampler2D lightmap, bounce, texmap, diffusemap; uniform vec2 viewport; uniform vec3 sun_radiance, sky_radiance; vec3 get(float x, float y){ vec2 coord = (gl_FragCoord.xy+vec2(x,y))/viewport; vec3 uv = texture2D(texmap, coord).xyz; if(uv.b > 0.0){ return sky_radiance; } else{ vec3 diffuse_color = texture2D(diffusemap, coord).rgb; vec4 bounce_data = texture2D(bounce, uv.xy); vec3 direct_irradiance = texture2D(lightmap, uv.xy).rgb*sun_radiance; vec3 global_irradiance = bounce_data.rgb/bounce_data.a; return diffuse_color * (direct_irradiance + global_irradiance); } } void main(){ vec3 result = ( get(-0.5, -0.5) + get(0.5, 0.5) + get(-0.5, 0.5) + get(0.5, -0.5) )/4.0; gl_FragColor = vec4(max(result, vec3(0.0)), 1.0); } ================================================ FILE: src/main.coffee ================================================ audio = require 'audio' loading = require 'loading' Shader = require 'webgl/shader' Quad = require 'webgl/quad' require 'webgl-nuke-vendor-prefix' require 'webgl-texture-float-extension-shims' #crashes firefox #worker = new Worker('worker.js') #worker.postMessage('Ping') #worker.onmessage = (event) -> # console.log event load_hooks = '\.jpg$|\.jpeg$|\.gif$|\.png': (name, buffer, callback) -> ext =name.split('.').pop() switch ext when 'png' then mime = 'image/png' when 'gif' then mime = 'image/gif' when 'jpg', 'jpeg' then mime = 'image/jpeg' image = new Image() image.src = getURL(buffer, mime) image.onload = -> callback image '\.mpg$|\.ogg$|\.wav$': (name, buffer, callback) -> audio.decode buffer, (result) -> callback result errorContainer = (title) -> canvas.remove() $('#ui').empty() return $('
    ') .css( position: 'absolute', width: 300, left: '50%', top: 50, marginLeft: -100 ) .append($('

    ').text(title)) .appendTo('#ui') disableSelect = -> $('*').each -> $(@) .attr('unselectable', 'on') .css '-moz-user-select':'none', '-webkit-user-select':'none', 'user-select':'none', '-ms-user-select':'none' @onselectstart = -> false document.oncontextmenu = -> return false enableSelect = -> $('*').each -> $(@) .removeAttr('unselectable') .css '-moz-user-select':'text', '-webkit-user-select':'text', 'user-select':'text', '-ms-user-select':'text' @onselectstart = undefined document.oncontextmenu = undefined exports.main = -> disableSelect() window.canvas = $ 'canvas' window.onerror = (error) -> if error.search(Shader.error) > 0 return true try window.gl = canvas[0].getContext 'experimental-webgl' if not window.gl window.gl = canvas[0].getContext 'webgl' if window.gl window.quad = new Quad window.gl stddev = gl.getExtension 'OES_standard_derivatives' if not stddev return errorContainer('Missing Extension: Standard Derivatives') .append('''

    This application requires the WebGL Standard Derivatives extension which you do not have, sorry.

    ''') floatExt = gl.getFloatExtension require: ['renderable'], prefer:['filterable', 'half'], throws:false if not floatExt return errorContainer('Missing Extension: Floating Point Textures') .append('''

    This application requires the WebGL Floating Point Textures extension which you do not have, sorry.

    ''') Application = require('application').Application application = null loading.show 'Loading ...' loader.hooks(load_hooks).mount url: 'assets.pack', loaded: (files, fs) -> for name, value of files if name.match('\.shaderlib$') fs[name] = Shader.splitLines name, value try for name, value of files if name.match('\.shader$') fs[name] = new Shader gl, name, value application = new Application(window.canvas, window.gl) catch error if error == 'ShaderError' enableSelect() container = errorContainer('Shader Error').append('''

    An error occured when compiling a shader, you can paste me the error.

    ''') container.css width: 600 marginLeft: -300 $('
    ')
                                .text(Shader.lastError)
                                .css('overflow', 'auto')
                                .appendTo(container)
                        else
                            throw error
                        
                progress: loading.progress
        else
            container = errorContainer('You dont have WebGL')
            if $.browser.msie
                container.append('''
                    

    You have Internet Explorer, please install Google Chrome or Firefox

    ''') else if $.browser.webkit container.append('''

    If you use OSX Safari, please enable WebGL manually. If you use iOS Safari, you cannot use WebGL. If you use Android, please try Firefox Mobile or Opera Mobile

    ''') container.append('''

    Please consult the support pages on how to get WebGL for your machine.

    ''') ================================================ FILE: src/main.js ================================================ // Generated by CoffeeScript 1.3.3 var Quad, Shader, audio, disableSelect, enableSelect, errorContainer, load_hooks, loading; audio = require('audio'); loading = require('loading'); Shader = require('webgl/shader'); Quad = require('webgl/quad'); require('webgl-nuke-vendor-prefix'); require('webgl-texture-float-extension-shims'); load_hooks = { '\.jpg$|\.jpeg$|\.gif$|\.png': function(name, buffer, callback) { var ext, image, mime; ext = name.split('.').pop(); switch (ext) { case 'png': mime = 'image/png'; break; case 'gif': mime = 'image/gif'; break; case 'jpg': case 'jpeg': mime = 'image/jpeg'; } image = new Image(); image.src = getURL(buffer, mime); return image.onload = function() { return callback(image); }; }, '\.mpg$|\.ogg$|\.wav$': function(name, buffer, callback) { return audio.decode(buffer, function(result) { return callback(result); }); } }; errorContainer = function(title) { canvas.remove(); $('#ui').empty(); return $('
    ').css({ position: 'absolute', width: 300, left: '50%', top: 50, marginLeft: -100 }).append($('

    ').text(title)).appendTo('#ui'); }; disableSelect = function() { $('*').each(function() { $(this).attr('unselectable', 'on').css({ '-moz-user-select': 'none', '-webkit-user-select': 'none', 'user-select': 'none', '-ms-user-select': 'none' }); return this.onselectstart = function() { return false; }; }); return document.oncontextmenu = function() { return false; }; }; enableSelect = function() { $('*').each(function() { $(this).removeAttr('unselectable').css({ '-moz-user-select': 'text', '-webkit-user-select': 'text', 'user-select': 'text', '-ms-user-select': 'text' }); return this.onselectstart = void 0; }); return document.oncontextmenu = void 0; }; exports.main = function() { var Application, application, container, floatExt, stddev; disableSelect(); window.canvas = $('canvas'); window.onerror = function(error) { if (error.search(Shader.error) > 0) { return true; } }; try { window.gl = canvas[0].getContext('experimental-webgl'); if (!window.gl) { window.gl = canvas[0].getContext('webgl'); } } catch (_error) {} if (window.gl) { window.quad = new Quad(window.gl); stddev = gl.getExtension('OES_standard_derivatives'); if (!stddev) { return errorContainer('Missing Extension: Standard Derivatives').append('

    This application requires the WebGL Standard Derivatives extension which you do not have, sorry.

    '); } floatExt = gl.getFloatExtension({ require: ['renderable'], prefer: ['filterable', 'half'], throws: false }); if (!floatExt) { return errorContainer('Missing Extension: Floating Point Textures').append('

    This application requires the WebGL Floating Point Textures extension which you do not have, sorry.

    '); } Application = require('application').Application; application = null; loading.show('Loading ...'); return loader.hooks(load_hooks).mount({ url: 'assets.pack', loaded: function(files, fs) { var container, name, value; for (name in files) { value = files[name]; if (name.match('\.shaderlib$')) { fs[name] = Shader.splitLines(name, value); } } try { for (name in files) { value = files[name]; if (name.match('\.shader$')) { fs[name] = new Shader(gl, name, value); } } return application = new Application(window.canvas, window.gl); } catch (error) { if (error === 'ShaderError') { enableSelect(); container = errorContainer('Shader Error').append('

    \n An error occured when compiling a shader, you can paste me the error.\n

    '); container.css({ width: 600, marginLeft: -300 }); return $('
    ').text(Shader.lastError).css('overflow', 'auto').appendTo(container);
              } else {
                throw error;
              }
            }
          },
          progress: loading.progress
        });
      } else {
        container = errorContainer('You dont have WebGL');
        if ($.browser.msie) {
          container.append('

    \n You have Internet Explorer, please install\n Google Chrome or\n Firefox\n

    '); } else if ($.browser.webkit) { container.append('

    \n If you use OSX Safari, please enable WebGL manually.\n If you use iOS Safari, you cannot use WebGL.\n If you use Android, please try Firefox Mobile or\n Opera Mobile\n

    '); } return container.append('

    \n Please consult the support pages\n on how to get WebGL for your machine.\n

    '); } }; ================================================ FILE: src/model/materials.json ================================================ [{"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "00_skap.jpg", "diffuse_color": {"r": 0.713726, "b": 0.658824, "g": 0.705882}, "start": 0, "bumpmap": "00_skap.jpg", "size": 2304}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 19.999998, "diffuse_texture": "vrata_ko.jpg", "diffuse_color": {"r": 0.784314, "b": 0.784314, "g": 0.784314}, "start": 2304, "bumpmap": "vrata_ko.jpg", "size": 726}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_st_kp.jpg", "diffuse_color": {"r": 0.745098, "b": 0.67451, "g": 0.709804}, "start": 3030, "bumpmap": "01_st_kp-bump.jpg", "size": 16560}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "kamen.jpg", "diffuse_color": {"r": 0.627451, "b": 0.560784, "g": 0.572549}, "start": 19590, "bumpmap": "kamen-bump.jpg", "size": 144}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "reljef.jpg", "diffuse_color": {"r": 0.529412, "b": 0.490196, "g": 0.498039}, "start": 19734, "bumpmap": "reljef-bump.jpg", "size": 48}, {"specular_color": {"r": 0.034039, "b": 0.029333, "g": 0.032314}, "specularity": 1.0, "diffuse_texture": "kamen-stup.jpg", "diffuse_color": {"r": 0.941177, "b": 0.737255, "g": 0.866667}, "start": 19782, "bumpmap": "kamen-stup.jpg", "size": 33822}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "00_skap.jpg", "diffuse_color": {"r": 0.713726, "b": 0.658824, "g": 0.705882}, "start": 53604, "bumpmap": "00_skap.jpg", "size": 17748}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "kamen.jpg", "diffuse_color": {"r": 0.627451, "b": 0.560784, "g": 0.572549}, "start": 71352, "bumpmap": "kamen-bump.jpg", "size": 7503}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_stub.jpg", "diffuse_color": {"r": 0.737255, "b": 0.670588, "g": 0.709804}, "start": 78855, "bumpmap": "01_stub-bump.jpg", "size": 192}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_s_ba.jpg", "diffuse_color": {"r": 0.8, "b": 0.74902, "g": 0.784314}, "start": 79047, "bumpmap": "01_s_ba.jpg", "size": 2736}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "kamen.jpg", "diffuse_color": {"r": 0.627451, "b": 0.560784, "g": 0.572549}, "start": 81783, "bumpmap": "kamen-bump.png", "size": 210}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 19.999998, "diffuse_texture": "white.png", "diffuse_color": {"r": 0.784314, "b": 0.784314, "g": 0.784314}, "start": 81993, "bumpmap": "level.png", "size": 1440}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_stub.jpg", "diffuse_color": {"r": 0.737255, "b": 0.670588, "g": 0.709804}, "start": 83433, "bumpmap": "01_stub-bump.jpg", "size": 144}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_s_ba.jpg", "diffuse_color": {"r": 0.8, "b": 0.74902, "g": 0.784314}, "start": 83577, "bumpmap": "01_s_ba.jpg", "size": 14931}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "prozor1.jpg", "diffuse_color": {"r": 1.0, "b": 1.0, "g": 1.0}, "start": 98508, "bumpmap": "prozor1.jpg", "size": 882}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 19.999998, "diffuse_texture": "vrata_kr.jpg", "diffuse_color": {"r": 0.784314, "b": 0.784314, "g": 0.784314}, "start": 99390, "bumpmap": "vrata_kr.jpg", "size": 4878}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_stub.jpg", "diffuse_color": {"r": 0.737255, "b": 0.670588, "g": 0.709804}, "start": 104268, "bumpmap": "01_stub-bump.jpg", "size": 696}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "x01_st.jpg", "diffuse_color": {"r": 0.827451, "b": 0.768628, "g": 0.8}, "start": 104964, "bumpmap": "level.png", "size": 6756}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "sp_luk.jpg", "diffuse_color": {"r": 0.745098, "b": 0.67451, "g": 0.709804}, "start": 111720, "bumpmap": "sp_luk-bump.jpg", "size": 25056}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "sp_luk.jpg", "diffuse_color": {"r": 0.745098, "b": 0.67451, "g": 0.709804}, "start": 136776, "bumpmap": "sp_luk-bump.jpg", "size": 62574}] ================================================ FILE: src/model/module.coffee ================================================ {Texture2D} = require '/webgl/texture' Materials = class Materials createTexture: (path) -> texture = @texture_cache[path] if not texture image = get path texture = new Texture2D(@gl) .bind() .upload(image) .mipmap() .repeat() .unbind() @texture_cache[path] = texture return texture constructor: (@gl) -> @texture_cache = {} @definitions = get 'materials.json' @gl.pixelStorei @gl.UNPACK_FLIP_Y_WEBGL, true for definition in @definitions diffuse = "diffuse/#{definition.diffuse_texture}" jpgbump = "bump/#{definition.bumpmap}" pngbump = jpgbump.replace('.jpg', '.png') definition.diffuse_texture = @createTexture diffuse specular = definition.specular_color luma = (specular.r + specular.g + specular.b)/3 definition.specularity = luma*definition.specularity if get.exists pngbump definition.bumpmap = @createTexture pngbump else if get.exists jpgbump definition.bumpmap = @createTexture jpgbump else definition.bumpmap = definition.diffuse_texture @gl.pixelStorei @gl.UNPACK_FLIP_Y_WEBGL, false @diffuse_texture = @sortById('diffuse_texture') @bumpmap = @sortById('bumpmap') sortById: (type) -> result = for definition in @definitions definition result.sort (a,b) -> return a[type].id - b[type].id return result exports.LowresModel = class LowresModel extends require('/webgl/drawable') attribs: ['position', 'texcoord', 'normal'] pointers: [ {name: 'position', size: 3, offset: 0, stride: 8}, {name: 'texcoord', size: 2, offset: 3, stride: 8}, {name: 'normal', size: 3, offset: 5, stride: 8}, ] constructor: (@gl) -> super() @vertices = new Float32Array(get('lowres.vertices')) @size = @vertices.length/8 @upload @vertices exports.Model = class Model extends require('/webgl/drawable') attribs: ['position', 'texcoord', 'normal'] pointers: [ {name: 'position', size: 3, offset: 0, stride: 8}, {name: 'texcoord', size: 2, offset: 3, stride: 8}, {name: 'normal', size: 3, offset: 5, stride: 8}, ] constructor: (@gl) -> super() @materials = new Materials @gl indices = new Uint16Array(get('sponza.indices')) vertices = new Float32Array(get('sponza.vertices')) @size = indices.length @max_angle = Math.cos(Math.PI*2*(43/360)) @computeVertexFaces(indices, vertices) @computeFaceNormals(indices, vertices) buffer = @calculateVertices(indices, vertices) @upload buffer computeVertexFaces: (indices, vertices) -> start = gettime() vertex_count = vertices.length/5 #how much storage do I need? counts = new Uint8Array(vertex_count) for index in indices counts[index] += 1 max = 0 for count in counts if count > max max = count @max_count = max #store face indices vertex_faces = new Uint32Array(max*vertex_count) counts = new Uint8Array(vertex_count) face_count = indices.length/3 for face_index in [0...face_count] iv = face_index*3 idx1 = indices[iv] idx2 = indices[iv+1] idx3 = indices[iv+2] c1 = counts[idx1]++ c2 = counts[idx2]++ c3 = counts[idx3]++ vertex_faces[idx1*max+c1] = face_index vertex_faces[idx2*max+c2] = face_index vertex_faces[idx3*max+c3] = face_index end = gettime() #console.log 'compute vertex faces', (end-start)*1000 @vertex_face_count = counts @vertex_faces = vertex_faces computeFaceNormals: (indices, vertices) -> start = gettime() face_count = indices.length/3 normals = new Float32Array(face_count*3) for i in [0...face_count] iv = i*3 i1 = indices[iv] i2 = indices[iv+1] i3 = indices[iv+2] x1 = vertices[i1*5] y1 = vertices[i1*5+1] z1 = vertices[i1*5+2] x2 = vertices[i2*5] y2 = vertices[i2*5+1] z2 = vertices[i2*5+2] x3 = vertices[i3*5] y3 = vertices[i3*5+1] z3 = vertices[i3*5+2] tx=x2-x1; ty=y2-y1; tz=z2-z1 btx=x3-x1; bty=y3-y1; btz=z3-z1 nx = ty*btz - tz*bty ny = tz*btx - tx*btz nz = tx*bty - ty*btx l = Math.sqrt(nx*nx + ny*ny + nz*nz) nx/=l; ny/=l; nz/=l normals[iv+0] = nx normals[iv+1] = ny normals[iv+2] = nz @normals = normals end = gettime() #console.log 'compute face normals', (end-start)*1000 getNormal: (face_index, vertex_index) -> rx = @normals[face_index*3+0] ry = @normals[face_index*3+1] rz = @normals[face_index*3+2] #return [rx, ry, rz] nx = 0; ny=0; nz=0 for c in [0...@vertex_face_count[vertex_index]] vfidx = @vertex_faces[vertex_index*@max_count+c] x = @normals[vfidx*3+0] y = @normals[vfidx*3+1] z = @normals[vfidx*3+2] cos = rx*x + ry*y + rz*z if cos > @max_angle nx = x ny = y nz = z l = Math.sqrt(nx*nx + ny*ny + nz*nz) return [nx/l, ny/l, nz/l] calculateVertices: (indices, vertices, normals) -> start = gettime() result = new Float32Array(indices.length*8) for i in [0...indices.length/3] iv = i*3 i1 = indices[iv] i2 = indices[iv+1] i3 = indices[iv+2] x1 = vertices[i1*5] y1 = vertices[i1*5+1] z1 = vertices[i1*5+2] u1 = vertices[i1*5+3] v1 = vertices[i1*5+4] x2 = vertices[i2*5] y2 = vertices[i2*5+1] z2 = vertices[i2*5+2] u2 = vertices[i2*5+3] v2 = vertices[i2*5+4] x3 = vertices[i3*5] y3 = vertices[i3*5+1] z3 = vertices[i3*5+2] u3 = vertices[i3*5+3] v3 = vertices[i3*5+4] [nx,ny,nz] = @getNormal i, i1 result[(iv+0)*8 + 0] = x1 result[(iv+0)*8 + 1] = y1 result[(iv+0)*8 + 2] = z1 result[(iv+0)*8 + 3] = u1 result[(iv+0)*8 + 4] = v1 result[(iv+0)*8 + 5] = nx result[(iv+0)*8 + 6] = ny result[(iv+0)*8 + 7] = nz [nx,ny,nz] = @getNormal i, i2 result[(iv+1)*8 + 0] = x2 result[(iv+1)*8 + 1] = y2 result[(iv+1)*8 + 2] = z2 result[(iv+1)*8 + 3] = u2 result[(iv+1)*8 + 4] = v2 result[(iv+1)*8 + 5] = nx result[(iv+1)*8 + 6] = ny result[(iv+1)*8 + 7] = nz [nx,ny,nz] = @getNormal i, i3 result[(iv+2)*8 + 0] = x3 result[(iv+2)*8 + 1] = y3 result[(iv+2)*8 + 2] = z3 result[(iv+2)*8 + 3] = u3 result[(iv+2)*8 + 4] = v3 result[(iv+2)*8 + 5] = nx result[(iv+2)*8 + 6] = ny result[(iv+2)*8 + 7] = nz end = gettime() #console.log 'build vertices', (end - start)*1000 return result ''' draw: (shader) -> if shader then @setPointersForShader shader for material in @materials #@gl.drawArrays @mode, @first, @size @gl.drawArrays @mode, material.start, material.size if shader then @disableAttribs shader return @ ''' ================================================ FILE: src/model/module.js ================================================ // Generated by CoffeeScript 1.3.3 var LowresModel, Materials, Model, Texture2D, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Texture2D = require('/webgl/texture').Texture2D; Materials = Materials = (function() { Materials.prototype.createTexture = function(path) { var image, texture; texture = this.texture_cache[path]; if (!texture) { image = get(path); texture = new Texture2D(this.gl).bind().upload(image).mipmap().repeat().unbind(); this.texture_cache[path] = texture; } return texture; }; function Materials(gl) { var definition, diffuse, jpgbump, luma, pngbump, specular, _i, _len, _ref; this.gl = gl; this.texture_cache = {}; this.definitions = get('materials.json'); this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, true); _ref = this.definitions; for (_i = 0, _len = _ref.length; _i < _len; _i++) { definition = _ref[_i]; diffuse = "diffuse/" + definition.diffuse_texture; jpgbump = "bump/" + definition.bumpmap; pngbump = jpgbump.replace('.jpg', '.png'); definition.diffuse_texture = this.createTexture(diffuse); specular = definition.specular_color; luma = (specular.r + specular.g + specular.b) / 3; definition.specularity = luma * definition.specularity; if (get.exists(pngbump)) { definition.bumpmap = this.createTexture(pngbump); } else if (get.exists(jpgbump)) { definition.bumpmap = this.createTexture(jpgbump); } else { definition.bumpmap = definition.diffuse_texture; } } this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); this.diffuse_texture = this.sortById('diffuse_texture'); this.bumpmap = this.sortById('bumpmap'); } Materials.prototype.sortById = function(type) { var definition, result; result = (function() { var _i, _len, _ref, _results; _ref = this.definitions; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { definition = _ref[_i]; _results.push(definition); } return _results; }).call(this); result.sort(function(a, b) { return a[type].id - b[type].id; }); return result; }; return Materials; })(); exports.LowresModel = LowresModel = (function(_super) { __extends(LowresModel, _super); LowresModel.prototype.attribs = ['position', 'texcoord', 'normal']; LowresModel.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 8 }, { name: 'texcoord', size: 2, offset: 3, stride: 8 }, { name: 'normal', size: 3, offset: 5, stride: 8 } ]; function LowresModel(gl) { this.gl = gl; LowresModel.__super__.constructor.call(this); this.vertices = new Float32Array(get('lowres.vertices')); this.size = this.vertices.length / 8; this.upload(this.vertices); } return LowresModel; })(require('/webgl/drawable')); exports.Model = Model = (function(_super) { __extends(Model, _super); Model.prototype.attribs = ['position', 'texcoord', 'normal']; Model.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 8 }, { name: 'texcoord', size: 2, offset: 3, stride: 8 }, { name: 'normal', size: 3, offset: 5, stride: 8 } ]; function Model(gl) { var buffer, indices, vertices; this.gl = gl; Model.__super__.constructor.call(this); this.materials = new Materials(this.gl); indices = new Uint16Array(get('sponza.indices')); vertices = new Float32Array(get('sponza.vertices')); this.size = indices.length; this.max_angle = Math.cos(Math.PI * 2 * (43 / 360)); this.computeVertexFaces(indices, vertices); this.computeFaceNormals(indices, vertices); buffer = this.calculateVertices(indices, vertices); this.upload(buffer); } Model.prototype.computeVertexFaces = function(indices, vertices) { var c1, c2, c3, count, counts, end, face_count, face_index, idx1, idx2, idx3, index, iv, max, start, vertex_count, vertex_faces, _i, _j, _k, _len, _len1; start = gettime(); vertex_count = vertices.length / 5; counts = new Uint8Array(vertex_count); for (_i = 0, _len = indices.length; _i < _len; _i++) { index = indices[_i]; counts[index] += 1; } max = 0; for (_j = 0, _len1 = counts.length; _j < _len1; _j++) { count = counts[_j]; if (count > max) { max = count; } } this.max_count = max; vertex_faces = new Uint32Array(max * vertex_count); counts = new Uint8Array(vertex_count); face_count = indices.length / 3; for (face_index = _k = 0; 0 <= face_count ? _k < face_count : _k > face_count; face_index = 0 <= face_count ? ++_k : --_k) { iv = face_index * 3; idx1 = indices[iv]; idx2 = indices[iv + 1]; idx3 = indices[iv + 2]; c1 = counts[idx1]++; c2 = counts[idx2]++; c3 = counts[idx3]++; vertex_faces[idx1 * max + c1] = face_index; vertex_faces[idx2 * max + c2] = face_index; vertex_faces[idx3 * max + c3] = face_index; } end = gettime(); this.vertex_face_count = counts; return this.vertex_faces = vertex_faces; }; Model.prototype.computeFaceNormals = function(indices, vertices) { var btx, bty, btz, end, face_count, i, i1, i2, i3, iv, l, normals, nx, ny, nz, start, tx, ty, tz, x1, x2, x3, y1, y2, y3, z1, z2, z3, _i; start = gettime(); face_count = indices.length / 3; normals = new Float32Array(face_count * 3); for (i = _i = 0; 0 <= face_count ? _i < face_count : _i > face_count; i = 0 <= face_count ? ++_i : --_i) { iv = i * 3; i1 = indices[iv]; i2 = indices[iv + 1]; i3 = indices[iv + 2]; x1 = vertices[i1 * 5]; y1 = vertices[i1 * 5 + 1]; z1 = vertices[i1 * 5 + 2]; x2 = vertices[i2 * 5]; y2 = vertices[i2 * 5 + 1]; z2 = vertices[i2 * 5 + 2]; x3 = vertices[i3 * 5]; y3 = vertices[i3 * 5 + 1]; z3 = vertices[i3 * 5 + 2]; tx = x2 - x1; ty = y2 - y1; tz = z2 - z1; btx = x3 - x1; bty = y3 - y1; btz = z3 - z1; nx = ty * btz - tz * bty; ny = tz * btx - tx * btz; nz = tx * bty - ty * btx; l = Math.sqrt(nx * nx + ny * ny + nz * nz); nx /= l; ny /= l; nz /= l; normals[iv + 0] = nx; normals[iv + 1] = ny; normals[iv + 2] = nz; } this.normals = normals; return end = gettime(); }; Model.prototype.getNormal = function(face_index, vertex_index) { var c, cos, l, nx, ny, nz, rx, ry, rz, vfidx, x, y, z, _i, _ref; rx = this.normals[face_index * 3 + 0]; ry = this.normals[face_index * 3 + 1]; rz = this.normals[face_index * 3 + 2]; nx = 0; ny = 0; nz = 0; for (c = _i = 0, _ref = this.vertex_face_count[vertex_index]; 0 <= _ref ? _i < _ref : _i > _ref; c = 0 <= _ref ? ++_i : --_i) { vfidx = this.vertex_faces[vertex_index * this.max_count + c]; x = this.normals[vfidx * 3 + 0]; y = this.normals[vfidx * 3 + 1]; z = this.normals[vfidx * 3 + 2]; cos = rx * x + ry * y + rz * z; if (cos > this.max_angle) { nx = x; ny = y; nz = z; } } l = Math.sqrt(nx * nx + ny * ny + nz * nz); return [nx / l, ny / l, nz / l]; }; Model.prototype.calculateVertices = function(indices, vertices, normals) { var end, i, i1, i2, i3, iv, nx, ny, nz, result, start, u1, u2, u3, v1, v2, v3, x1, x2, x3, y1, y2, y3, z1, z2, z3, _i, _ref, _ref1, _ref2, _ref3; start = gettime(); result = new Float32Array(indices.length * 8); for (i = _i = 0, _ref = indices.length / 3; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { iv = i * 3; i1 = indices[iv]; i2 = indices[iv + 1]; i3 = indices[iv + 2]; x1 = vertices[i1 * 5]; y1 = vertices[i1 * 5 + 1]; z1 = vertices[i1 * 5 + 2]; u1 = vertices[i1 * 5 + 3]; v1 = vertices[i1 * 5 + 4]; x2 = vertices[i2 * 5]; y2 = vertices[i2 * 5 + 1]; z2 = vertices[i2 * 5 + 2]; u2 = vertices[i2 * 5 + 3]; v2 = vertices[i2 * 5 + 4]; x3 = vertices[i3 * 5]; y3 = vertices[i3 * 5 + 1]; z3 = vertices[i3 * 5 + 2]; u3 = vertices[i3 * 5 + 3]; v3 = vertices[i3 * 5 + 4]; _ref1 = this.getNormal(i, i1), nx = _ref1[0], ny = _ref1[1], nz = _ref1[2]; result[(iv + 0) * 8 + 0] = x1; result[(iv + 0) * 8 + 1] = y1; result[(iv + 0) * 8 + 2] = z1; result[(iv + 0) * 8 + 3] = u1; result[(iv + 0) * 8 + 4] = v1; result[(iv + 0) * 8 + 5] = nx; result[(iv + 0) * 8 + 6] = ny; result[(iv + 0) * 8 + 7] = nz; _ref2 = this.getNormal(i, i2), nx = _ref2[0], ny = _ref2[1], nz = _ref2[2]; result[(iv + 1) * 8 + 0] = x2; result[(iv + 1) * 8 + 1] = y2; result[(iv + 1) * 8 + 2] = z2; result[(iv + 1) * 8 + 3] = u2; result[(iv + 1) * 8 + 4] = v2; result[(iv + 1) * 8 + 5] = nx; result[(iv + 1) * 8 + 6] = ny; result[(iv + 1) * 8 + 7] = nz; _ref3 = this.getNormal(i, i3), nx = _ref3[0], ny = _ref3[1], nz = _ref3[2]; result[(iv + 2) * 8 + 0] = x3; result[(iv + 2) * 8 + 1] = y3; result[(iv + 2) * 8 + 2] = z3; result[(iv + 2) * 8 + 3] = u3; result[(iv + 2) * 8 + 4] = v3; result[(iv + 2) * 8 + 5] = nx; result[(iv + 2) * 8 + 6] = ny; result[(iv + 2) * 8 + 7] = nz; } end = gettime(); return result; }; 'draw: (shader) ->\n if shader then @setPointersForShader shader\n for material in @materials\n #@gl.drawArrays @mode, @first, @size\n @gl.drawArrays @mode, material.start, material.size\n if shader then @disableAttribs shader\n return @'; return Model; })(require('/webgl/drawable')); ================================================ FILE: src/normaldepth.shader ================================================ varying vec2 vTexcoord; varying vec3 vPosition, vViewPosition, vNormal, vViewNormal; uniform mat4 proj, view; uniform mat3 view_rot; vertex: attribute vec3 position, normal; attribute vec2 texcoord; void main(){ vTexcoord = texcoord; vPosition = position; vNormal = normal; vViewNormal = view_rot * normal; vViewPosition = (view * vec4(position, 1.0)).xyz; gl_Position = proj * view * vec4(position, 1.0); } fragment: #extension GL_OES_standard_derivatives : enable uniform sampler2D bumpmap; vec3 perturbedNormal(vec3 normal, float bumpheight){ vec3 vSigmaS = dFdx(vPosition); vec3 vSigmaT = dFdy(vPosition); vec3 vR1 = cross(vSigmaT, normal); vec3 vR2 = cross(normal, vSigmaS); float fDet = dot(vSigmaS, vR1); float dBs = dFdx(bumpheight); float dBt = dFdy(bumpheight); vec3 vSurfGrad = sign(fDet) * (dBs*vR1 + dBt*vR2); return normalize(abs(fDet)*normal-vSurfGrad); } void main(){ vec3 normal = normalize(vNormal); float bumpheight = texture2D(bumpmap, vTexcoord).r; vec3 perturbed_normal = perturbedNormal(normal, bumpheight/96.0); float depth = length(vViewPosition); vec3 eye_dir = normalize(vViewPosition); float displacement = dot(eye_dir, -(view_rot*normal)) * bumpheight*0.05; gl_FragColor = vec4(perturbed_normal, depth-displacement); } ================================================ FILE: src/presets/default.json ================================================ { "remembered": { "Default": { "0": { "resolution_label": "1:2 default", "show_fps": false }, "1": { "inputGamma": 1.8, "outputGamma": 1.99100719424, "brightness": 1.51079136691, "saturation": 1.0071942446 }, "2": { "orientation": 104, "elevation": 53.8686131387 }, "3": { "draw_probes": false, "gi": 0.762589928058, "di": 0.172661870504, "ao": 0.700729927007 }, "4": { "sunColor": [ 255, 248.823529412, 202.5 ], "sunRadiance": 9.4964028777, "skyColor": [ 9.90196078431, 66.9838523645, 252.5 ], "skyRadiance": 1.0071942446, "giGain": 0.7, "bounces": 3 }, "5": { "c1": 1.15107913669, "c2": 2.41726618705, "band3": 0.517985611511, "c3": 3.13669064748, "c4": 0.546762589928, "c5": 1.87050359712 }, "6": { "x": -9.38136276391469, "y": 3.176749303559157, "z": 0.09001503384880467, "go": 92.0781892939057, "p": 0.2106932539339603 }, "7": { "subpixel_aa": 0.75, "contrast_treshold": 0.166, "edge_treshold": 0 }, "8": { "show_all": false, "window_label": "Antialiased", "show_label": false } } }, "folders": { "Performance": { "folders": {}, "preset": "Default", "closed": false }, "Picture": { "folders": {}, "preset": "Default", "closed": false }, "Compositing": { "folders": {}, "preset": "Default", "closed": true }, "Lighting": { "folders": {}, "preset": "Default", "closed": true }, "Harmonics": { "folders": {}, "preset": "Default", "closed": true }, "Camera": { "folders": {}, "preset": "Default", "closed": true }, "Sun": { "folders": {}, "preset": "Default", "closed": false }, "Antialias": { "folders": {}, "preset": "Default", "closed": true }, "Views": { "folders": {}, "preset": "Default", "closed": false } }, "preset": "Default", "closed": true } ================================================ FILE: src/presets/new.json ================================================ { "remembered": { "Apple Gamma": { "1": { "outputGamma": 2.5, "brightness": 1.3218978102189782, "saturation": 0.95071942446 } }, "Default": { "0": { "resolution_label": "1:2 default", "show_fps": false }, "1": { "inputGamma": 1.8, "outputGamma": 2.1, "brightness": 1.0218978102189782, "saturation": 1.0071942446 }, "2": { "orientation": 102.48175182481751, "elevation": 51.240875912408754 }, "3": { "draw_probes": false, "gi": 0.9927007299270073, "di": 0.8321167883211679, "ao": 0.700729927007 }, "4": { "sunColor": [ 255, 248.823529412, 202.5 ], "sunRadiance": 5.5, "skyColor": [ 0, 35, 255 ], "skyRadiance": 0.8, "giGain": 1.6058394160583942, "bounces": 3 }, "5": { "c1": 1.0359712230215827, "c2": 1.841726618705036, "band3": 0.8345323741007195, "c3": 2.935251798561151, "c4": 0.5755395683453237, "c5": 1.064748201438849 }, "6": { "x": -9.904411786477144, "y": 1.864264505425676, "z": -0.5129541306187764, "go": 112.499999992655, "p": -4.316055717198253 }, "7": { "subpixel_aa": 0.75, "contrast_treshold": 0.166, "edge_treshold": 0 }, "8": { "show_all": false, "window_label": "Antialiased", "show_label": false } }, "Bright Day": { "1": { "brightness": 1.0218978102189782 }, "2": { "orientation": 102.48175182481751, "elevation": 51.240875912408754 }, "4": { "sunColor": [ 255, 248.823529412, 202.5 ], "sunRadiance": 5.5, "skyColor": [ 0, 35, 255 ], "skyRadiance": 0.8 } }, "Morning Mood": { "1": { "brightness": 1.7266187050359714 }, "2": { "orientation": 102.481751825, "elevation": 7.769784172661871 }, "4": { "sunColor": [ 252.5, 178.81776239907728, 138.62745098039215 ], "sunRadiance": 2.5899280575539567, "skyColor": [ 74.63235294117646, 94.24163783160323, 217.5 ], "skyRadiance": 0.8 } }, "Noon": { "1": { "brightness": 1.0218978102189782 }, "2": { "orientation": 160.57553956834533, "elevation": 78.34532374100719 }, "4": { "sunColor": [ 255, 255, 255 ], "sunRadiance": 6.9064748201438855, "skyColor": [ 140, 155.7843137254902, 255 ], "skyRadiance": 2.5899280575539567 } } }, "folders": { "Performance": { "folders": {}, "preset": "Default", "closed": false }, "Picture": { "folders": {}, "preset": "Default", "closed": false }, "Compositing": { "folders": {}, "preset": "Default", "closed": true }, "Lighting": { "folders": {}, "preset": "Default", "closed": true }, "Harmonics": { "folders": {}, "preset": "Default", "closed": true }, "Camera": { "folders": {}, "preset": "Default", "closed": true }, "Sun": { "folders": {}, "preset": "Default", "closed": false }, "Antialias": { "folders": {}, "preset": "Default", "closed": true }, "Views": { "folders": {}, "preset": "Default", "closed": false } }, "preset": "Default", "closed": true } ================================================ FILE: src/ssao/module.coffee ================================================ Rendernode = require '/rendernode' Blur = require '/blur' return class SSAO constructor: (gl, @normaldepth) -> floatExt = gl.getFloatExtension require: ['renderable', 'filterable'] @moments = new Rendernode gl, program: get 'moments.shader' type: floatExt.type drawable: quad @blur = new Blur gl, type: floatExt.type @output = new Rendernode gl, program: get 'ssao.shader' drawable: quad update: -> @moments.start() .sampler('normaldepth', @normaldepth) .f('range', 42) .clear() .draw() .end() @blur.update(@moments) @output.start() .sampler('normaldepth', @normaldepth) .sampler('momentsmap', @blur.output) .f('range', 42) .clear() .draw() .end() resize: (width, height) -> @moments.resize width/2, height/2 @blur.resize width/4, height/4 @output.resize width, height ================================================ FILE: src/ssao/module.js ================================================ // Generated by CoffeeScript 1.3.3 var Blur, Rendernode, SSAO; Rendernode = require('/rendernode'); Blur = require('/blur'); return SSAO = (function() { function SSAO(gl, normaldepth) { var floatExt; this.normaldepth = normaldepth; floatExt = gl.getFloatExtension({ require: ['renderable', 'filterable'] }); this.moments = new Rendernode(gl, { program: get('moments.shader'), type: floatExt.type, drawable: quad }); this.blur = new Blur(gl, { type: floatExt.type }); this.output = new Rendernode(gl, { program: get('ssao.shader'), drawable: quad }); } SSAO.prototype.update = function() { this.moments.start().sampler('normaldepth', this.normaldepth).f('range', 42).clear().draw().end(); this.blur.update(this.moments); return this.output.start().sampler('normaldepth', this.normaldepth).sampler('momentsmap', this.blur.output).f('range', 42).clear().draw().end(); }; SSAO.prototype.resize = function(width, height) { this.moments.resize(width / 2, height / 2); this.blur.resize(width / 4, height / 4); return this.output.resize(width, height); }; return SSAO; })(); ================================================ FILE: src/ssao/moments.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform sampler2D normaldepth; uniform vec2 viewport; uniform float range; void main(){ vec4 data = texture2D(normaldepth, gl_FragCoord.xy/viewport); float depth = data.w; float scaled = clamp(depth/range, 0.0, 1.0); gl_FragColor = vec4(scaled, scaled*scaled, 0.0, 1.0); //float dx = dFdx(scaled); //float dy = dFdy(scaled); //gl_FragColor = vec4(scaled, scaled*scaled + 0.25*(dx*dx + dy*dy), 0.0, 1.0); } ================================================ FILE: src/ssao/ssao.shader ================================================ vertex: attribute vec2 position; void main(){ gl_Position = vec4(position, 0.0, 1.0); } fragment: uniform sampler2D normaldepth, momentsmap; uniform vec2 viewport; uniform float range; float linstep(float low, float high, float v){ return clamp((v-low)/(high-low), 0.0, 1.0); } float getOcclusion(float depth, vec2 moments, float offset){ float p = smoothstep(depth-offset/range, depth, moments.x); //float p = step(depth-offset/range, moments.x); float variance = moments.y - moments.x*moments.x; float d = depth - moments.x; float p_max = variance/(variance+d*d); p_max = smoothstep(0.5, 1.0, p_max); return 1.0-clamp(max(p, p_max), 0.0, 1.0); } void main(){ float depth = clamp(texture2D(normaldepth, gl_FragCoord.xy/viewport).w/range, 0.0, 1.0); vec2 moments = texture2D(momentsmap, gl_FragCoord.xy/viewport).xy; float factor = smoothstep((depth-0.2/range), depth, moments.x); float result = getOcclusion(depth, moments, 0.025)*clamp(factor, 0.0, 1.0); gl_FragColor = vec4(1.0-pow(result, 0.5)); } ================================================ FILE: src/sun.coffee ================================================ return class Sun extends require('events') constructor: (gui, @orientation=104, @elevation=60) -> gui.remember @ super() folder = gui.addFolder('Sun') folder.add(@, 'orientation', 0, 360).onChange(@update) folder.add(@, 'elevation', 0, 90).onChange(@update) @near = -1 @far = 41 @proj = new Mat4().ortho(@near, @far, 21, -21, -21, 21) @view = new Mat4() @rot = new Mat3() @update() update: => @view.identity() .translateVal3(0, 0, -21) .rotatex(@elevation) .rotatey(@orientation) .translateVal3(0, -7.5, 0) .toMat3 @rot.identity() @trigger('change') return @ ================================================ FILE: src/sun.js ================================================ // Generated by CoffeeScript 1.3.3 var Sun, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Sun = (function(_super) { __extends(Sun, _super); function Sun(gui, orientation, elevation) { var folder; this.orientation = orientation != null ? orientation : 104; this.elevation = elevation != null ? elevation : 60; this.update = __bind(this.update, this); gui.remember(this); Sun.__super__.constructor.call(this); folder = gui.addFolder('Sun'); folder.add(this, 'orientation', 0, 360).onChange(this.update); folder.add(this, 'elevation', 0, 90).onChange(this.update); this.near = -1; this.far = 41; this.proj = new Mat4().ortho(this.near, this.far, 21, -21, -21, 21); this.view = new Mat4(); this.rot = new Mat3(); this.update(); } Sun.prototype.update = function() { this.view.identity().translateVal3(0, 0, -21).rotatex(this.elevation).rotatey(this.orientation).translateVal3(0, -7.5, 0).toMat3(this.rot.identity()); this.trigger('change'); return this; }; return Sun; })(require('events')); ================================================ FILE: src/windows/module.coffee ================================================ Quad = require '/webgl/quad' Rendernode = require '/rendernode' keys = require '/keys' class Window constructor: (@index, @texture, @node, @x, @y) -> @label = @texture.label if @texture.diva @diva = 1 else @diva = 0 if @texture.gamma == false @gamma = 0 else @gamma = 1 if @texture.affine @mul = @texture.affine[0] @add = @texture.affine[1] else @mul = 1 @add = 0 draw: (xscale, yscale, one2one, cx, cy, active) -> width = @texture.tex.width height = @texture.tex.height max = Math.max width, height w = width/max h = height/max s = 1/Math.max(w*xscale, h*yscale) w = w * (1-one2one) + s*w*one2one h = h * (1-one2one) + s*h*one2one @node .sampler('source', @texture.tex) .f('mixgamma', @gamma) .f('diva', @diva) .f('border_factor', active) .val2('affine', @mul, @add) .val2('size', w*xscale, h*yscale) .val2('offset', (@x-cx)*xscale, (@y-cy)*yscale) .draw() return class Windows constructor: (@gl, gui, @textures) -> gui.remember @ @label = $('
    test
    ').appendTo('#ui').hide() @show_all = false @show_label = false @needs_clear = $.browser.mozilla #console.log @needs_clear @node = new Rendernode @gl, front: true program: get 'window.shader' drawable: quad @windows = [] @labelmap = {} labels = [] gridsize = Math.ceil(Math.sqrt(@textures.length)) for texture, i in @textures x = i%gridsize y = gridsize - Math.floor(i/gridsize) - 1 window = new Window i, texture, @node, x*2.2, y*2.2 @labelmap[window.label] = window @windows.push window labels.push window.label minx = null maxx = null miny = null maxy = null for window in @windows minx = if minx!=null then Math.min(window.x, minx) else window.x maxx = if maxx!=null then Math.max(window.x, maxx) else window.x miny = if miny!=null then Math.min(window.y, miny) else window.y maxy = if maxy!=null then Math.max(window.y, maxy) else window.y @cx = (minx+maxx)/2 @cy = (miny+maxy)/2 @full_scale = 1.9/Math.min(maxx-minx+2, maxy-miny+2) @zoom = 0.0 @active = @windows.length - 1 keys.press 'right', @next keys.press 'left', @prev keys.press 'down', => new_value = @active + gridsize if new_value < @windows.length @active = new_value else @active = @active % gridsize @setActive() keys.press 'up', => new_value = @active - gridsize if new_value >= 0 @active = new_value else new_value = gridsize*gridsize + new_value while new_value >= @windows.length new_value -= gridsize @active = new_value @setActive() keys.press 'space', => @show_all = not @show_all @all_ctrl.setValue(@show_all) keys.press 'enter', => @show_all = not @show_all @all_ctrl.setValue(@show_all) active = @getActive() @x = active.x @y = active.y folder = gui.addFolder('Views') @all_ctrl = folder.add(@, 'show_all').name('Overview') folder.add(@, 'next').name('Next view') folder.add(@, 'prev').name('Prev view') @window_label = active.label @guiLabel = folder.add(@, 'window_label', labels).name('View').onChange(@guiLabelChange) folder.add(@, 'show_label').name('Labels').onChange @labelVisibilityChange @guiLabelChange() labelVisibilityChange: => if @show_label @label.clearQueue().fadeIn() else @label.clearQueue().fadeOut() guiLabelChange: => window = @labelmap[@window_label] @active = window.index @setActive() getActive: -> @windows[@active] setActive: -> text = @getActive().label @window_label = text @guiLabel.updateDisplay() @label.text(text) next: => @active = (@active+1) % @windows.length @setActive() prev: => if @active == 0 @active = @windows.length - 1 else @active -= 1 @setActive() step: -> active = @getActive() tx = active.x ty = active.y @x = @x+(tx-@x)*0.1 @y = @y+(ty-@y)*0.1 if @show_all @zoom = @zoom + (1-@zoom)*0.1 else @zoom = @zoom + (0-@zoom)*0.1 draw: (gamma) -> @step() if @needs_clear == true @gl.clearColor 0, 0, 0, 0 @gl.clear @gl.COLOR_BUFFER_BIT | @gl.DEPTH_BUFFER_BIT @node .start() .f('gamma', gamma) width = @node.width height = @node.height if width > height xscale = height/width yscale = 1 else xscale = 1 yscale = width/height factor = 1.0-@zoom + @zoom * @full_scale #factor = @full_scale xscale *= factor yscale *= factor active = @getActive() for window in @windows if window != active @drawWindow xscale, yscale, window, 0 @drawWindow xscale, yscale, active, 1 @node.end() drawWindow: (xscale, yscale, window, active) -> dx = window.x - @x dy = window.y - @y l = Math.sqrt(dx*dx+dy*dy) if l > 0 one2one = Math.min(1/(l*2), 1) else one2one = 1 x = @x*(1-@zoom) + @cx*@zoom y = @y*(1-@zoom) + @cy*@zoom active = Math.pow(one2one, 2.0) * @zoom window.draw(xscale, yscale, one2one*(1-@zoom), x, y, active) ================================================ FILE: src/windows/module.js ================================================ // Generated by CoffeeScript 1.3.3 var Quad, Rendernode, Window, Windows, keys, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; Quad = require('/webgl/quad'); Rendernode = require('/rendernode'); keys = require('/keys'); Window = (function() { function Window(index, texture, node, x, y) { this.index = index; this.texture = texture; this.node = node; this.x = x; this.y = y; this.label = this.texture.label; if (this.texture.diva) { this.diva = 1; } else { this.diva = 0; } if (this.texture.gamma === false) { this.gamma = 0; } else { this.gamma = 1; } if (this.texture.affine) { this.mul = this.texture.affine[0]; this.add = this.texture.affine[1]; } else { this.mul = 1; this.add = 0; } } Window.prototype.draw = function(xscale, yscale, one2one, cx, cy, active) { var h, height, max, s, w, width; width = this.texture.tex.width; height = this.texture.tex.height; max = Math.max(width, height); w = width / max; h = height / max; s = 1 / Math.max(w * xscale, h * yscale); w = w * (1 - one2one) + s * w * one2one; h = h * (1 - one2one) + s * h * one2one; return this.node.sampler('source', this.texture.tex).f('mixgamma', this.gamma).f('diva', this.diva).f('border_factor', active).val2('affine', this.mul, this.add).val2('size', w * xscale, h * yscale).val2('offset', (this.x - cx) * xscale, (this.y - cy) * yscale).draw(); }; return Window; })(); return Windows = (function() { function Windows(gl, gui, textures) { var active, folder, gridsize, i, labels, maxx, maxy, minx, miny, texture, window, x, y, _i, _j, _len, _len1, _ref, _ref1, _this = this; this.gl = gl; this.textures = textures; this.prev = __bind(this.prev, this); this.next = __bind(this.next, this); this.guiLabelChange = __bind(this.guiLabelChange, this); this.labelVisibilityChange = __bind(this.labelVisibilityChange, this); gui.remember(this); this.label = $('
    test
    ').appendTo('#ui').hide(); this.show_all = false; this.show_label = false; this.needs_clear = $.browser.mozilla; this.node = new Rendernode(this.gl, { front: true, program: get('window.shader'), drawable: quad }); this.windows = []; this.labelmap = {}; labels = []; gridsize = Math.ceil(Math.sqrt(this.textures.length)); _ref = this.textures; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { texture = _ref[i]; x = i % gridsize; y = gridsize - Math.floor(i / gridsize) - 1; window = new Window(i, texture, this.node, x * 2.2, y * 2.2); this.labelmap[window.label] = window; this.windows.push(window); labels.push(window.label); } minx = null; maxx = null; miny = null; maxy = null; _ref1 = this.windows; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { window = _ref1[_j]; minx = minx !== null ? Math.min(window.x, minx) : window.x; maxx = maxx !== null ? Math.max(window.x, maxx) : window.x; miny = miny !== null ? Math.min(window.y, miny) : window.y; maxy = maxy !== null ? Math.max(window.y, maxy) : window.y; } this.cx = (minx + maxx) / 2; this.cy = (miny + maxy) / 2; this.full_scale = 1.9 / Math.min(maxx - minx + 2, maxy - miny + 2); this.zoom = 0.0; this.active = this.windows.length - 1; keys.press('right', this.next); keys.press('left', this.prev); keys.press('down', function() { var new_value; new_value = _this.active + gridsize; if (new_value < _this.windows.length) { _this.active = new_value; } else { _this.active = _this.active % gridsize; } return _this.setActive(); }); keys.press('up', function() { var new_value; new_value = _this.active - gridsize; if (new_value >= 0) { _this.active = new_value; } else { new_value = gridsize * gridsize + new_value; while (new_value >= _this.windows.length) { new_value -= gridsize; } _this.active = new_value; } return _this.setActive(); }); keys.press('space', function() { _this.show_all = !_this.show_all; return _this.all_ctrl.setValue(_this.show_all); }); keys.press('enter', function() { _this.show_all = !_this.show_all; return _this.all_ctrl.setValue(_this.show_all); }); active = this.getActive(); this.x = active.x; this.y = active.y; folder = gui.addFolder('Views'); this.all_ctrl = folder.add(this, 'show_all').name('Overview'); folder.add(this, 'next').name('Next view'); folder.add(this, 'prev').name('Prev view'); this.window_label = active.label; this.guiLabel = folder.add(this, 'window_label', labels).name('View').onChange(this.guiLabelChange); folder.add(this, 'show_label').name('Labels').onChange(this.labelVisibilityChange); this.guiLabelChange(); } Windows.prototype.labelVisibilityChange = function() { if (this.show_label) { return this.label.clearQueue().fadeIn(); } else { return this.label.clearQueue().fadeOut(); } }; Windows.prototype.guiLabelChange = function() { var window; window = this.labelmap[this.window_label]; this.active = window.index; return this.setActive(); }; Windows.prototype.getActive = function() { return this.windows[this.active]; }; Windows.prototype.setActive = function() { var text; text = this.getActive().label; this.window_label = text; this.guiLabel.updateDisplay(); return this.label.text(text); }; Windows.prototype.next = function() { this.active = (this.active + 1) % this.windows.length; return this.setActive(); }; Windows.prototype.prev = function() { if (this.active === 0) { this.active = this.windows.length - 1; } else { this.active -= 1; } return this.setActive(); }; Windows.prototype.step = function() { var active, tx, ty; active = this.getActive(); tx = active.x; ty = active.y; this.x = this.x + (tx - this.x) * 0.1; this.y = this.y + (ty - this.y) * 0.1; if (this.show_all) { return this.zoom = this.zoom + (1 - this.zoom) * 0.1; } else { return this.zoom = this.zoom + (0 - this.zoom) * 0.1; } }; Windows.prototype.draw = function(gamma) { var active, factor, height, width, window, xscale, yscale, _i, _len, _ref; this.step(); if (this.needs_clear === true) { this.gl.clearColor(0, 0, 0, 0); this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); } this.node.start().f('gamma', gamma); width = this.node.width; height = this.node.height; if (width > height) { xscale = height / width; yscale = 1; } else { xscale = 1; yscale = width / height; } factor = 1.0 - this.zoom + this.zoom * this.full_scale; xscale *= factor; yscale *= factor; active = this.getActive(); _ref = this.windows; for (_i = 0, _len = _ref.length; _i < _len; _i++) { window = _ref[_i]; if (window !== active) { this.drawWindow(xscale, yscale, window, 0); } } this.drawWindow(xscale, yscale, active, 1); return this.node.end(); }; Windows.prototype.drawWindow = function(xscale, yscale, window, active) { var dx, dy, l, one2one, x, y; dx = window.x - this.x; dy = window.y - this.y; l = Math.sqrt(dx * dx + dy * dy); if (l > 0) { one2one = Math.min(1 / (l * 2), 1); } else { one2one = 1; } x = this.x * (1 - this.zoom) + this.cx * this.zoom; y = this.y * (1 - this.zoom) + this.cy * this.zoom; active = Math.pow(one2one, 2.0) * this.zoom; return window.draw(xscale, yscale, one2one * (1 - this.zoom), x, y, active); }; return Windows; })(); ================================================ FILE: src/windows/window.shader ================================================ varying vec2 vTexcoord, vPosition; vertex: attribute vec2 position; uniform vec2 size; uniform vec2 offset; void main(){ vPosition = position; vTexcoord = position * 0.5 + 0.5; gl_Position = vec4(position*size+offset, 0.0, 1.0); } fragment: #extension GL_OES_standard_derivatives : enable uniform sampler2D source; uniform float diva, gamma, mixgamma, border_factor; uniform vec2 affine; float edgeFactor(vec2 src){ vec2 d = fwidth(src); vec2 a3 = smoothstep(vec2(0.0), d*1.5, src); return min(a3.x, a3.y); } void main(){ vec4 data = texture2D(source, vTexcoord); vec3 transformed = data.rgb*affine.x + affine.y; vec3 color; if(data.a == 0.0){ color = transformed; } else{ color = mix(transformed, data.rgb/data.a, diva); } vec3 corrected = pow(color, vec3(1.0/gamma)); vec3 output_color = mix(color, corrected, mixgamma); vec3 clamped = clamp(output_color, vec3(0.0), vec3(1.0)); vec2 dpos = fwidth(vPosition); vec2 pos = smoothstep(1.0-dpos*4.0, vec2(1.0), abs(vPosition)); float near = border_factor * max(pos.x, pos.y); gl_FragColor = vec4(mix(clamped, vec3(1.0, 0.5, 0.0), near), 1.0); } ================================================ FILE: www/code.js ================================================ Function.prototype.property = function(prop, desc) { Object.defineProperty(this.prototype, prop, desc); }; /** * dat-gui JavaScript Controller Library * http://code.google.com/p/dat-gui * * Copyright 2011 Data Arts Team, Google Creative Lab * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 */ var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(e,a){var a=a||document,c=a.createElement("link");c.type="text/css";c.rel="stylesheet";c.href=e;a.getElementsByTagName("head")[0].appendChild(c)},inject:function(e,a){var a=a||document,c=document.createElement("style");c.type="text/css";c.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(c)}}}(); dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(c[f]=a[f])},this);return c},defaults:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(c[f])&&(c[f]=a[f])},this);return c},compose:function(){var c=a.call(arguments);return function(){for(var d=a.call(arguments),f=c.length-1;f>=0;f--)d=[c[f].apply(this,d)];return d[0]}}, each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var b=0,n=a.length;b-1?d.length-d.indexOf(".")-1:0};c.superclass=e;a.extend(c.prototype,e.prototype,{setValue:function(a){if(this.__min!==void 0&&athis.__max)a=this.__max;this.__step!==void 0&&a%this.__step!=0&&(a=Math.round(a/this.__step)*this.__step);return c.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__step=a;return this}});return c}(dat.controllers.Controller,dat.utils.common); dat.controllers.NumberControllerBox=function(e,a,c){var d=function(f,b,e){function h(){var a=parseFloat(l.__input.value);c.isNaN(a)||l.setValue(a)}function j(a){var b=o-a.clientY;l.setValue(l.getValue()+b*l.__impliedStep);o=a.clientY}function m(){a.unbind(window,"mousemove",j);a.unbind(window,"mouseup",m)}this.__truncationSuspended=false;d.superclass.call(this,f,b,e);var l=this,o;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",h); a.bind(this.__input,"blur",function(){h();l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",j);a.bind(window,"mouseup",m);o=b.clientY});a.bind(this.__input,"keydown",function(a){if(a.keyCode===13)l.__truncationSuspended=true,this.blur(),l.__truncationSuspended=false});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input, b;if(this.__truncationSuspended)b=this.getValue();else{b=this.getValue();var c=Math.pow(10,this.__precision);b=Math.round(b*c)/c}a.value=b;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common); dat.controllers.NumberControllerSlider=function(e,a,c,d,f){var b=function(d,c,f,e,l){function o(b){b.preventDefault();var d=a.getOffset(g.__background),c=a.getWidth(g.__background);g.setValue(g.__min+(g.__max-g.__min)*((b.clientX-d.left)/(d.left+c-d.left)));return false}function y(){a.unbind(window,"mousemove",o);a.unbind(window,"mouseup",y);g.__onFinishChange&&g.__onFinishChange.call(g,g.getValue())}b.superclass.call(this,d,c,{min:f,max:e,step:l});var g=this;this.__background=document.createElement("div"); this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",o);a.bind(window,"mouseup",y);o(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};b.superclass=e;b.useDefaultStyles=function(){c.inject(f)};d.extend(b.prototype,e.prototype,{updateDisplay:function(){this.__foreground.style.width= (this.getValue()-this.__min)/(this.__max-this.__min)*100+"%";return b.superclass.prototype.updateDisplay.call(this)}});return b}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,".slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); dat.controllers.FunctionController=function(e,a,c){var d=function(c,b,e){d.superclass.call(this,c,b);var h=this;this.__button=document.createElement("div");this.__button.innerHTML=e===void 0?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();h.fire();return false});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;c.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this, this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); dat.controllers.BooleanController=function(e,a,c){var d=function(c,b){d.superclass.call(this,c,b);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},false);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&& this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){this.getValue()===true?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=true):this.__checkbox.checked=false;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); dat.color.toString=function(e){return function(a){if(a.a==1||e.isUndefined(a.a)){for(a=a.hex.toString(16);a.length<6;)a="0"+a;return"#"+a}else return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common); dat.color.interpret=function(e,a){var c,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return a.length!= 3?false:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return a.length!=4?false:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:false},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&& a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:false},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:false},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:false},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d= false;var b=arguments.length>1?a.toArray(arguments):arguments[0];a.each(f,function(e){if(e.litmus(b))return a.each(e.conversions,function(e,f){c=e.read(b);if(d===false&&c!==false)return d=c,c.conversionName=f,c.conversion=e,a.BREAK}),a.BREAK});return d}}(dat.color.toString,dat.utils.common); dat.GUI=dat.gui.GUI=function(e,a,c,d,f,b,n,h,j,m,l,o,y,g,i){function q(a,b,r,c){if(b[r]===void 0)throw Error("Object "+b+' has no property "'+r+'"');c.color?b=new l(b,r):(b=[b,r].concat(c.factoryArgs),b=d.apply(a,b));if(c.before instanceof f)c.before=c.before.__li;t(a,b);g.addClass(b.domElement,"c");r=document.createElement("span");g.addClass(r,"property-name");r.innerHTML=b.property;var e=document.createElement("div");e.appendChild(r);e.appendChild(b.domElement);c=s(a,e,c.before);g.addClass(c,k.CLASS_CONTROLLER_ROW); g.addClass(c,typeof b.getValue());p(a,c,b);a.__controllers.push(b);return b}function s(a,b,d){var c=document.createElement("li");b&&c.appendChild(b);d?a.__ul.insertBefore(c,params.before):a.__ul.appendChild(c);a.onResize();return c}function p(a,d,c){c.__li=d;c.__gui=a;i.extend(c,{options:function(b){if(arguments.length>1)return c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[i.toArray(arguments)]});if(i.isArray(b)||i.isObject(b))return c.remove(),q(a,c.object,c.property, {before:c.__li.nextElementSibling,factoryArgs:[b]})},name:function(a){c.__li.firstElementChild.firstElementChild.innerHTML=a;return c},listen:function(){c.__gui.listen(c);return c},remove:function(){c.__gui.remove(c);return c}});if(c instanceof j){var e=new h(c.object,c.property,{min:c.__min,max:c.__max,step:c.__step});i.each(["updateDisplay","onChange","onFinishChange"],function(a){var b=c[a],H=e[a];c[a]=e[a]=function(){var a=Array.prototype.slice.call(arguments);b.apply(c,a);return H.apply(e,a)}}); g.addClass(d,"has-slider");c.domElement.insertBefore(e.domElement,c.domElement.firstElementChild)}else if(c instanceof h){var f=function(b){return i.isNumber(c.__min)&&i.isNumber(c.__max)?(c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[c.__min,c.__max,c.__step]})):b};c.min=i.compose(f,c.min);c.max=i.compose(f,c.max)}else if(c instanceof b)g.bind(d,"click",function(){g.fakeEvent(c.__checkbox,"click")}),g.bind(c.__checkbox,"click",function(a){a.stopPropagation()}); else if(c instanceof n)g.bind(d,"click",function(){g.fakeEvent(c.__button,"click")}),g.bind(d,"mouseover",function(){g.addClass(c.__button,"hover")}),g.bind(d,"mouseout",function(){g.removeClass(c.__button,"hover")});else if(c instanceof l)g.addClass(d,"color"),c.updateDisplay=i.compose(function(a){d.style.borderLeftColor=c.__color.toString();return a},c.updateDisplay),c.updateDisplay();c.setValue=i.compose(function(b){a.getRoot().__preset_select&&c.isModified()&&B(a.getRoot(),true);return b},c.setValue)} function t(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(d!=-1){var e=c.__rememberedObjectIndecesToControllers[d];e===void 0&&(e={},c.__rememberedObjectIndecesToControllers[d]=e);e[b.property]=b;if(c.load&&c.load.remembered){c=c.load.remembered;if(c[a.preset])c=c[a.preset];else if(c[w])c=c[w];else return;if(c[d]&&c[d][b.property]!==void 0)d=c[d][b.property],b.initialValue=d,b.setValue(d)}}}function I(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement, "has-save");a.__ul.insertBefore(b,a.__ul.firstChild);g.addClass(b,"save-row");var c=document.createElement("span");c.innerHTML=" ";g.addClass(c,"button gears");var d=document.createElement("span");d.innerHTML="Save";g.addClass(d,"button");g.addClass(d,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var m=a.__preset_select=document.createElement("select"); a.load&&a.load.remembered?i.each(a.load.remembered,function(b,c){C(a,c,c==a.preset)}):C(a,w,false);g.bind(m,"change",function(){for(var b=0;b0){a.preset=this.preset;if(!a.remembered)a.remembered={};a.remembered[this.preset]=z(this)}a.folders={};i.each(this.__folders,function(b, c){a.folders[c]=b.getSaveObject()});return a},save:function(){if(!this.load.remembered)this.load.remembered={};this.load.remembered[this.preset]=z(this);B(this,false)},saveAs:function(a){if(!this.load.remembered)this.load.remembered={},this.load.remembered[w]=z(this,true);this.load.remembered[a]=z(this);this.preset=a;C(this,a,true)},revert:function(a){i.each(this.__controllers,function(b){this.getRoot().load.remembered?t(a||this.getRoot(),b):b.setValue(b.initialValue)},this);i.each(this.__folders, function(a){a.revert(a)});a||B(this.getRoot(),false)},listen:function(a){var b=this.__listening.length==0;this.__listening.push(a);b&&E(this.__listening)}});return k}(dat.utils.css,'
    \n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
    \n\n Automatically save\n values to localStorage on exit.\n\n
    The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
    \n \n
    \n\n
    ', ".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear;border:0;position:absolute;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-x:hidden}.dg.a.has-save ul{margin-top:27px}.dg.a.has-save ul.closed{margin-top:0}.dg.a .save-row{position:fixed;top:0;z-index:1002}.dg li{-webkit-transition:height 0.1s ease-out;-o-transition:height 0.1s ease-out;-moz-transition:height 0.1s ease-out;transition:height 0.1s ease-out}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;overflow:hidden;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li > *{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:9px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2fa1d6}.dg .cr.number input[type=text]{color:#2fa1d6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2fa1d6}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n", dat.controllers.factory=function(e,a,c,d,f,b,n){return function(h,j,m,l){var o=h[j];if(n.isArray(m)||n.isObject(m))return new e(h,j,m);if(n.isNumber(o))return n.isNumber(m)&&n.isNumber(l)?new c(h,j,m,l):new a(h,j,{min:m,max:l});if(n.isString(o))return new d(h,j);if(n.isFunction(o))return new f(h,j,"");if(n.isBoolean(o))return new b(h,j)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,c){var d= function(c,b){function e(){h.setValue(h.__input.value)}d.superclass.call(this,c,b);var h=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())});a.bind(this.__input,"keydown",function(a){a.keyCode===13&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype, e.prototype,{updateDisplay:function(){if(!a.isActive(this.__input))this.__input.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController, dat.controllers.ColorController=function(e,a,c,d,f){function b(a,b,c,d){a.style.background="";f.each(j,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+c+" 0%, "+d+" 100%); "})}function n(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"; a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var h=function(e,l){function o(b){q(b);a.bind(window,"mousemove",q);a.bind(window, "mouseup",j)}function j(){a.unbind(window,"mousemove",q);a.unbind(window,"mouseup",j)}function g(){var a=d(this.value);a!==false?(p.__color.__state=a,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function i(){a.unbind(window,"mousemove",s);a.unbind(window,"mouseup",i)}function q(b){b.preventDefault();var c=a.getWidth(p.__saturation_field),d=a.getOffset(p.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c,b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b= 1:b<0&&(b=0);e>1?e=1:e<0&&(e=0);p.__color.v=b;p.__color.s=e;p.setValue(p.__color.toOriginal());return false}function s(b){b.preventDefault();var c=a.getHeight(p.__hue_field),d=a.getOffset(p.__hue_field),b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=1:b<0&&(b=0);p.__color.h=b*360;p.setValue(p.__color.toOriginal());return false}h.superclass.call(this,e,l);this.__color=new c(this.getValue());this.__temp=new c(0);var p=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement, false);this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input= document.createElement("input");this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){a.keyCode===13&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(){a.addClass(this,"drag").bind(window,"mouseup",function(){a.removeClass(p.__selector,"drag")})});var t=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}); f.extend(this.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(this.__color.v<0.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(t.style, {width:"100%",height:"100%",background:"none"});b(t,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});n(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",o);a.bind(this.__field_knob,"mousedown",o);a.bind(this.__hue_field,"mousedown", function(b){s(b);a.bind(window,"mousemove",s);a.bind(window,"mouseup",i)});this.__saturation_field.appendChild(t);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};h.superclass=e;f.extend(h.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue()); if(a!==false){var e=false;f.each(c.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=true,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var h=this.__color.v<0.5||this.__color.s>0.5?255:0,j=255-h;f.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+ "rgb("+h+","+h+","+h+")"});this.__hue_knob.style.marginTop=(1-this.__color.h/360)*100+"px";this.__temp.s=1;this.__temp.v=1;b(this.__saturation_field,"left","#fff",this.__temp.toString());f.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+h+","+h+","+h+")",textShadow:this.__input_textShadow+"rgba("+j+","+j+","+j+",.7)"})}});var j=["-moz-","-o-","-webkit-","-ms-",""];return h}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(e,a,c,d){function f(a, b,c){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="RGB")return this.__state[b];n(this,b,c);return this.__state[b]},set:function(a){if(this.__state.space!=="RGB")n(this,b,c),this.__state.space="RGB";this.__state[b]=a}})}function b(a,b){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="HSV")return this.__state[b];h(this);return this.__state[b]},set:function(a){if(this.__state.space!=="HSV")h(this),this.__state.space="HSV";this.__state[b]=a}})}function n(b,c,e){if(b.__state.space=== "HEX")b.__state[c]=a.component_from_hex(b.__state.hex,e);else if(b.__state.space==="HSV")d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});if(d.isNaN(c.h)){if(d.isUndefined(b.__state.h))b.__state.h=0}else b.__state.h=c.h}var j=function(){this.__state=e.apply(this,arguments);if(this.__state===false)throw"Failed to interpret color arguments";this.__state.a=this.__state.a|| 1};j.COMPONENTS="r,g,b,h,s,v,hex,a".split(",");d.extend(j.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(j.prototype,"r",2);f(j.prototype,"g",1);f(j.prototype,"b",0);b(j.prototype,"h");b(j.prototype,"s");b(j.prototype,"v");Object.defineProperty(j.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(j.prototype,"hex",{get:function(){if(!this.__state.space!=="HEX")this.__state.hex= a.rgb_to_hex(this.r,this.g,this.b);return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return j}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,c,d){var e=a/60-Math.floor(a/60),b=d*(1-c),n=d*(1-e*c),c=d*(1-(1-e)*c),a=[[d,c,b],[n,d,b],[b,d,c],[b,n,d],[c,b,d],[d,b,n]][Math.floor(a/60)%6];return{r:a[0]*255,g:a[1]*255,b:a[2]*255}},rgb_to_hsv:function(a,c,d){var e=Math.min(a,c,d),b=Math.max(a,c,d),e=b-e;if(b==0)return{h:NaN,s:0,v:0}; a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255< prevTime + 1000 ) { fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); fpsMin = Math.min( fpsMin, fps ); fpsMax = Math.max( fpsMax, fps ); fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); prevTime = time; frames = 0; } return time; }, update: function () { startTime = this.end(); } } }; ; // Generated by CoffeeScript 1.3.3 (function() { var fs, getBuffer, getJSON, isImage, makeBlob, makeURL, resolvePath; fs = {}; makeURL = function(blob) { return URL.createObjectURL(blob); }; makeBlob = function(data, type) { var blob; blob = new Blob([data], { type: type }); return blob; }; window.getURL = function(data, mime) { var blob; blob = makeBlob(data, mime); return makeURL(blob); }; resolvePath = function(base, path) { if (path[0] === '/') { return path; } else { path = path.split('/'); if (base === '/') { base = ['']; } else { base = base.split('/'); } while (base.length > 0 && path.length > 0 && path[0] === '..') { base.pop(); path.shift(); } if (base.length === 0 || path.length === 0 || base[0] !== '') { throw "Invalid path: " + (base.join('/')) + "/" + (path.join('/')); } return "" + (base.join('/')) + "/" + (path.join('/')); } }; getJSON = function(url, callback) { var request; request = new XMLHttpRequest(); request.open('GET', url, true); request.onload = function() { return callback(JSON.parse(request.response)); }; return request.send(); }; getBuffer = function(url, progress, callback) { var request; request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.onload = function() { return callback(request.response); }; request.onprogress = function(event) { if (event.lengthComputable) { return progress(event.loaded / event.total); } }; return request.send(); }; isImage = function(path) { return path.match('\.jpg$|\.jpeg|\.gif$|\.png'); }; window.loader = { resolvePath: resolvePath, main: function() { var main; main = this.require('main'); if (main.main) { return main.main(); } else { throw 'Main function is not defined in main module.'; } }, define: function(path, code) { var dirname, folder, get, require; dirname = path.split('/'); dirname.pop(); dirname = dirname.join('/'); require = function(modpath) { var abspath, node; abspath = resolvePath(dirname, modpath); node = fs["" + abspath + ".js"]; if (!node) { node = fs["" + abspath + "/module.js"]; } if (!node) { throw "Module not found: " + abspath; } if (!node.value) { node.create(); } return node.value; }; get = function(respath) { var abspath, node; abspath = resolvePath(dirname, respath); node = fs[abspath]; if (!node) { throw "Resource not found: " + abspath; } return node; }; get.exists = function(respath) { var abspath, node; abspath = resolvePath(dirname, respath); node = fs[abspath]; return node !== void 0; }; folder = get.folder = function(folderpath) { var folder_abs; folder_abs = resolvePath(dirname, folderpath); return { path: folder_abs, name: folder_abs.split('/')[folder_abs.split('/').length - 1], get: function(respath) { var node, nodepath; nodepath = resolvePath(folder_abs, respath); node = fs[nodepath]; if (!node) { throw "Resource not found: " + nodepath; } return node; }, exists: function(respath) { var nodepath; nodepath = resolvePath(folder_abs, respath); return fs[nodepath] !== void 0; }, listdir: function(respath) { var match, name, nodepath, result, translated, _i, _len; if (respath) { nodepath = resolvepath(folder_abs, respath); } else { nodepath = folder_abs; } result = []; for (name in fs) { match = name.match("" + folder_abs + "/[a-zA-Z0-9-\.]+"); if (match) { match = match[0]; if (result.indexOf(match) === -1) { result.push(match); } } } translated = []; for (_i = 0, _len = result.length; _i < _len; _i++) { name = result[_i]; if (name.match(/\.[a-z]+$/)) { translated.push(name); } else { translated.push(folder(name)); } } return translated; } }; }; get.listdir = function(respath, match) { var abspath, name, result; if (respath) { abspath = resolvePath(dirname, respath); } else { abspath = dirname; } result = []; for (name in fs) { if (name.search(abspath) === 0) { if (match) { if (name.match(match)) { result.push(name); } } else { result.push(name); } } } return result; }; return fs[path] = { path: path, type: 'code', data: code, create: function() { var retval; this.value = {}; retval = code(this.value, require, get); if (retval) { return this.value = retval; } } }; }, require: function(modpath) { var abspath, node; abspath = resolvePath('/', modpath); node = fs["" + abspath + ".js"]; if (!node) { node = fs["" + abspath + "/module.js"]; } if (!node) { throw "Module not found: " + abspath; } if (!node.value) { node.create(); } return node.value; }, loadPack: function(_arg) { var files, hooks, loaded, progress, url; url = _arg.url, progress = _arg.progress, loaded = _arg.loaded; files = {}; hooks = this.hooks; return getBuffer(url, (function(factor) { if (progress) { return progress(factor * 0.5, 'network'); } }), function(data) { var decoded, decoding, doLoad, i, info, length, metadata, name, result, _i; decoding = 0; decoded = 0; doLoad = function(name, info) { var decode, dst, matcher, src, storage; if (typeof info === 'object' && info.offset !== void 0 && info.size !== void 0) { storage = new ArrayBuffer(info.size); dst = new Uint8Array(storage); src = new Uint8Array(data, 8 + length + info.offset, info.size); dst.set(src); dst = dst.buffer; if (hooks) { for (matcher in hooks) { decode = hooks[matcher]; if (name.match(matcher)) { decoding += 1; decode(name, dst, function(result) { decoded += 1; files[name] = result; if (progress) { progress(0.5 + (decoded / decoding) * 0.5, 'decode'); } if (decoding === decoded && loaded) { return loaded(files); } }); return; } } } return files[name] = dst; } else { if (hooks) { for (matcher in hooks) { decode = hooks[matcher]; if (name.match(matcher)) { decode(name, info, function(result) { return files[name] = result; }); return; } } } return files[name] = info; } }; length = new Uint32Array(data, 4, 1)[0]; metadata = new Uint8Array(data, 8, length); result = ''; for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) { result += String.fromCharCode(metadata[i]); } result = JSON.parse(result); for (name in result) { info = result[name]; doLoad(name, info, data); } if (decoding === decoded && loaded) { return loaded(files); } }); }, hooks: function(hooks) { this.hooks = hooks; return this; }, mount: function(_arg) { var loaded, mountpoint, progress, url; url = _arg.url, mountpoint = _arg.mountpoint, progress = _arg.progress, loaded = _arg.loaded; if (mountpoint == null) { mountpoint = '/'; } return this.loadPack({ url: url, progress: progress, loaded: function(data) { var name, value; for (name in data) { value = data[name]; fs[name] = value; } return loaded(data, fs); } }); } }; }).call(this); ; // Generated by CoffeeScript 1.3.3 (function() { var Vec3, Vec4; window.Vec3 = Vec3 = (function() { Vec3.property('x', { get: function() { return this.data[0]; }, set: function(val) { return this.data[0] = val; } }); Vec3.property('y', { get: function() { return this.data[1]; }, set: function(val) { return this.data[1] = val; } }); Vec3.property('z', { get: function() { return this.data[2]; }, set: function(val) { return this.data[2] = val; } }); Vec3.property('length', { get: function() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } }); function Vec3(data) { var _ref; this.data = data; if ((_ref = this.data) == null) { this.data = new Float32Array(3); } } Vec3.prototype.sub = function(other, dst) { if (dst == null) { dst = this; } dst.x = this.x - other.x; dst.y = this.y - other.y; dst.z = this.z - other.z; return dst; }; Vec3.prototype.add = function(other, dst) { if (dst == null) { dst = this; } dst.x = this.x + other.x; dst.y = this.y + other.y; dst.z = this.z + other.z; return dst; }; Vec3.prototype.addVal3 = function(x, y, z, dst) { if (dst == null) { dst = this; } dst.x = this.x + x; dst.y = this.y + y; dst.z = this.z + z; return dst; }; Vec3.prototype.mul = function(scalar, dst) { if (dst == null) { dst = this; } dst.x = this.x * scalar; dst.y = this.y * scalar; dst.z = this.z * scalar; return dst; }; Vec3.prototype.div = function(scalar, dst) { if (dst == null) { dst = this; } dst.x = this.x / scalar; dst.y = this.y / scalar; dst.z = this.z / scalar; return dst; }; Vec3.prototype.divVal3 = function(x, y, z, dst) { if (dst == null) { dst = this; } dst.x = this.x / x; dst.y = this.y / y; dst.z = this.z / z; return dst; }; Vec3.prototype.dot = function(other) { return this.x * other.x + this.y * other.y + this.z * other.z; }; Vec3.prototype.normalize = function(dst) { var l; if (dst == null) { dst = this; } l = this.length; if (l > 0) { this.mul(1 / this.length, dst); } return dst; }; Vec3.prototype.set = function(x, y, z) { this.x = x; this.y = y; this.z = z; return this; }; return Vec3; })(); window.Vec4 = Vec4 = (function() { Vec4.property('x', { get: function() { return this.data[0]; }, set: function(val) { return this.data[0] = val; } }); Vec4.property('y', { get: function() { return this.data[1]; }, set: function(val) { return this.data[1] = val; } }); Vec4.property('z', { get: function() { return this.data[2]; }, set: function(val) { return this.data[2] = val; } }); Vec4.property('w', { get: function() { return this.data[3]; }, set: function(val) { return this.data[3] = val; } }); function Vec4(data) { var _ref; this.data = data; if ((_ref = this.data) == null) { this.data = new Float32Array(4); } } Vec4.prototype.sub = function(other, dst) { if (dst == null) { dst = this; } dst.x = this.x - other.x; dst.y = this.y - other.y; dst.z = this.z - other.z; dst.w = this.w - other.w; return dst; }; Vec4.prototype.dot = function(other) { return this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w; }; Vec4.prototype.toVec3 = function(dst) { if (dst == null) { dst = new Vec3(); } dst.x = this.x; dst.y = this.y; dst.z = this.z; return dst; }; return Vec4; })(); }).call(this); ; // Generated by CoffeeScript 1.3.3 (function() { var Mat3, Mat4, arc, deg, pi, tau; pi = Math.PI; tau = 2 * pi; deg = 360 / tau; arc = tau / 360; window.Mat3 = Mat3 = (function() { function Mat3(data) { var _ref; this.data = data; if ((_ref = this.data) == null) { this.data = new Float32Array(9); } this.identity(); } Mat3.prototype.identity = function() { var d; d = this.data; d[0] = 1; d[1] = 0; d[2] = 0; d[3] = 0; d[4] = 1; d[5] = 0; d[6] = 0; d[7] = 0; d[8] = 1; return this; }; Mat3.prototype.transpose = function() { var a01, a02, a12, d; d = this.data; a01 = d[1]; a02 = d[2]; a12 = d[5]; d[1] = d[3]; d[2] = d[6]; d[3] = a01; d[5] = d[7]; d[6] = a02; d[7] = a12; return this; }; Mat3.prototype.mulVec3 = function(vec, dst) { if (dst == null) { dst = vec; } this.mulVal3(vec.x, vec.y, vec.z, dst); return dst; }; Mat3.prototype.mulVal3 = function(x, y, z, dst) { var d; dst = dst.data; d = this.data; dst[0] = d[0] * x + d[3] * y + d[6] * z; dst[1] = d[1] * x + d[4] * y + d[7] * z; dst[2] = d[2] * x + d[5] * y + d[8] * z; return this; }; Mat3.prototype.rotatex = function(angle) { var c, s; s = Math.sin(angle * arc); c = Math.cos(angle * arc); return this.amul(1, 0, 0, 0, c, s, 0, -s, c); }; Mat3.prototype.rotatey = function(angle) { var c, s; s = Math.sin(angle * arc); c = Math.cos(angle * arc); return this.amul(c, 0, -s, 0, 1, 0, s, 0, c); }; Mat3.prototype.rotatez = function(angle) { var c, s; s = Math.sin(angle * arc); c = Math.cos(angle * arc); return this.amul(c, s, 0, -s, c, 0, 0, 0, 1); }; Mat3.prototype.amul = function(b00, b10, b20, b01, b11, b21, b02, b12, b22, b03, b13, b23) { var a, a00, a01, a02, a10, a11, a12, a20, a21, a22; a = this.data; a00 = a[0]; a10 = a[1]; a20 = a[2]; a01 = a[3]; a11 = a[4]; a21 = a[5]; a02 = a[6]; a12 = a[7]; a22 = a[8]; a[0] = a00 * b00 + a01 * b10 + a02 * b20; a[1] = a10 * b00 + a11 * b10 + a12 * b20; a[2] = a20 * b00 + a21 * b10 + a22 * b20; a[3] = a00 * b01 + a01 * b11 + a02 * b21; a[4] = a10 * b01 + a11 * b11 + a12 * b21; a[5] = a20 * b01 + a21 * b11 + a22 * b21; a[6] = a00 * b02 + a01 * b12 + a02 * b22; a[7] = a10 * b02 + a11 * b12 + a12 * b22; a[8] = a20 * b02 + a21 * b12 + a22 * b22; return this; }; Mat3.prototype.log = function() { var d; d = this.data; return console.log('%f, %f, %f,\n%f, %f, %f, \n%f, %f, %f, ', d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8]); }; return Mat3; })(); window.Mat4 = Mat4 = (function() { function Mat4(data) { var _ref; this.data = data; if ((_ref = this.data) == null) { this.data = new Float32Array(16); } this.identity(); } Mat4.prototype.identity = function() { var d; d = this.data; d[0] = 1; d[1] = 0; d[2] = 0; d[3] = 0; d[4] = 0; d[5] = 1; d[6] = 0; d[7] = 0; d[8] = 0; d[9] = 0; d[10] = 1; d[11] = 0; d[12] = 0; d[13] = 0; d[14] = 0; d[15] = 1; return this; }; Mat4.prototype.zero = function() { var d; d = this.data; d[0] = 0; d[1] = 0; d[2] = 0; d[3] = 0; d[4] = 0; d[5] = 0; d[6] = 0; d[7] = 0; d[8] = 0; d[9] = 0; d[10] = 0; d[11] = 0; d[12] = 0; d[13] = 0; d[14] = 0; d[15] = 0; return this; }; Mat4.prototype.copy = function(dest) { var dst, src; src = this.data; dst = dest.data; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; dst[8] = src[8]; dst[9] = src[9]; dst[10] = src[10]; dst[11] = src[11]; dst[12] = src[12]; dst[13] = src[13]; dst[14] = src[14]; dst[15] = src[15]; return dest; }; Mat4.prototype.toMat3 = function(dest) { var dst, src; src = this.data; dst = dest.data; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6]; dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10]; return dest; }; Mat4.prototype.toMat3Rot = function(dest) { var a00, a01, a02, a10, a11, a12, a20, a21, a22, b01, b11, b21, d, dst, id, src; dst = dest.data; src = this.data; a00 = src[0]; a01 = src[1]; a02 = src[2]; a10 = src[4]; a11 = src[5]; a12 = src[6]; a20 = src[8]; a21 = src[9]; a22 = src[10]; b01 = a22 * a11 - a12 * a21; b11 = -a22 * a10 + a12 * a20; b21 = a21 * a10 - a11 * a20; d = a00 * b01 + a01 * b11 + a02 * b21; id = 1 / d; dst[0] = b01 * id; dst[3] = (-a22 * a01 + a02 * a21) * id; dst[6] = (a12 * a01 - a02 * a11) * id; dst[1] = b11 * id; dst[4] = (a22 * a00 - a02 * a20) * id; dst[7] = (-a12 * a00 + a02 * a10) * id; dst[2] = b21 * id; dst[5] = (-a21 * a00 + a01 * a20) * id; dst[8] = (a11 * a00 - a01 * a10) * id; return dest; }; Mat4.prototype.perspective = function(fov, aspect, near, far) { var bottom, d, left, right, top; this.zero(); d = this.data; top = near * Math.tan(fov * Math.PI / 360); right = top * aspect; left = -right; bottom = -top; d[0] = (2 * near) / (right - left); d[5] = (2 * near) / (top - bottom); d[8] = (right + left) / (right - left); d[9] = (top + bottom) / (top - bottom); d[10] = -(far + near) / (far - near); d[11] = -1; d[14] = -(2 * far * near) / (far - near); return this; }; Mat4.prototype.inversePerspective = function(fov, aspect, near, far) { var bottom, dst, left, right, top; this.zero(); dst = this.data; top = near * Math.tan(fov * Math.PI / 360); right = top * aspect; left = -right; bottom = -top; dst[0] = (right - left) / (2 * near); dst[5] = (top - bottom) / (2 * near); dst[11] = -(far - near) / (2 * far * near); dst[12] = (right + left) / (2 * near); dst[13] = (top + bottom) / (2 * near); dst[14] = -1; dst[15] = (far + near) / (2 * far * near); return this; }; Mat4.prototype.ortho = function(near, far, top, bottom, left, right) { var fn, rl, tb; if (near == null) { near = -1; } if (far == null) { far = 1; } if (top == null) { top = -1; } if (bottom == null) { bottom = 1; } if (left == null) { left = -1; } if (right == null) { right = 1; } rl = right - left; tb = top - bottom; fn = far - near; return this.set(2 / rl, 0, 0, -(left + right) / rl, 0, 2 / tb, 0, -(top + bottom) / tb, 0, 0, -2 / fn, -(far + near) / fn, 0, 0, 0, 1); }; Mat4.prototype.inverseOrtho = function(near, far, top, bottom, left, right) { var a, b, c, d, e, f, g; if (near == null) { near = -1; } if (far == null) { far = 1; } if (top == null) { top = -1; } if (bottom == null) { bottom = 1; } if (left == null) { left = -1; } if (right == null) { right = 1; } a = (right - left) / 2; b = (right + left) / 2; c = (top - bottom) / 2; d = (top + bottom) / 2; e = (far - near) / -2; f = (near + far) / 2; g = 1; return this.set(a, 0, 0, b, 0, c, 0, d, 0, 0, e, f, 0, 0, 0, g); }; Mat4.prototype.fromRotationTranslation = function(quat, vec) { var dest, w, wx, wy, wz, x, x2, xx, xy, xz, y, y2, yy, yz, z, z2, zz; x = quat.x; y = quat.y; z = quat.z; w = quat.w; x2 = x + x; y2 = y + y; z2 = z + z; xx = x * x2; xy = x * y2; xz = x * z2; yy = y * y2; yz = y * z2; zz = z * z2; wx = w * x2; wy = w * y2; wz = w * z2; dest = this.data; ' \ndest[0] = 1 - 2.0 * y * y - 2.0 * y * y\ndest[1] = 2 * x * y - 2.0 * w * z\ndest[3] = 2 * x * z + 2.0 * w * y\n\ndest[4] = 2 * x * y + 2.0 * w * z\ndest[5] = 1 - 2.0 * x * x - 2.0 * z * z\ndest[6] = 2 * y * z - 2.0 * w * x\n\ndest[8] = 2 * x * z - 2.0 * w * y\ndest[9] = 2 * y * z + 2.0 * w * x\ndest[10] = 1 - 2.0 * x * x - 2.0 * y * y'; 'dest[0] = 1 - 2.0 * y * y - 2.0 * y * y\ndest[1] = 2 * x * y + 2.0 * w * z\ndest[2] = 2 * x * z - 2.0 * w * y\n\ndest[4] = 2 * x * y - 2.0 * w * z\ndest[5] = 1 - 2.0 * x * x - 2.0 * z * z\ndest[6] = 2 * y * z + 2.0 * w * x\n\ndest[8] = 2 * x * z + 2.0 * w * y\ndest[9] = 2 * y * z - 2.0 * w * x\ndest[10] = 1 - 2.0 * x * x - 2.0 * y * y'; dest[0] = 1 - (yy + zz); dest[1] = xy + wz; dest[2] = xz - wy; dest[3] = 0; dest[4] = xy - wz; dest[5] = 1 - (xx + zz); dest[6] = yz + wx; dest[7] = 0; dest[8] = xz + wy; dest[9] = yz - wx; dest[10] = 1 - (xx + yy); dest[11] = 0; dest[12] = vec.x; dest[13] = vec.y; dest[14] = vec.z; dest[15] = 1; return this; }; Mat4.prototype.translateVec3 = function(vec) { return this.translateVal3(vec.x, vec.y, vec.z); }; Mat4.prototype.translateVal3 = function(x, y, z) { var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, d; d = this.data; a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3]; a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7]; a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11]; d[12] = a00 * x + a10 * y + a20 * z + d[12]; d[13] = a01 * x + a11 * y + a21 * z + d[13]; d[14] = a02 * x + a12 * y + a22 * z + d[14]; d[15] = a03 * x + a13 * y + a23 * z + d[15]; return this; }; Mat4.prototype.rotatex = function(angle) { var a10, a11, a12, a13, a20, a21, a22, a23, c, d, rad, s; d = this.data; rad = tau * (angle / 360); s = Math.sin(rad); c = Math.cos(rad); a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7]; a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11]; d[4] = a10 * c + a20 * s; d[5] = a11 * c + a21 * s; d[6] = a12 * c + a22 * s; d[7] = a13 * c + a23 * s; d[8] = a10 * -s + a20 * c; d[9] = a11 * -s + a21 * c; d[10] = a12 * -s + a22 * c; d[11] = a13 * -s + a23 * c; return this; }; Mat4.prototype.rotatey = function(angle) { var a00, a01, a02, a03, a20, a21, a22, a23, c, d, rad, s; d = this.data; rad = tau * (angle / 360); s = Math.sin(rad); c = Math.cos(rad); a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3]; a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11]; d[0] = a00 * c + a20 * -s; d[1] = a01 * c + a21 * -s; d[2] = a02 * c + a22 * -s; d[3] = a03 * c + a23 * -s; d[8] = a00 * s + a20 * c; d[9] = a01 * s + a21 * c; d[10] = a02 * s + a22 * c; d[11] = a03 * s + a23 * c; return this; }; Mat4.prototype.rotatez = function(angle) { var a00, a01, a02, a03, a10, a11, a12, a13, c, d, rad, s; d = this.data; rad = tau * (angle / 360); s = Math.sin(rad); c = Math.cos(rad); a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3]; a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7]; d[0] = a00 * c + a10 * s; d[1] = a01 * c + a11 * s; d[2] = a02 * c + a12 * s; d[3] = a03 * c + a13 * s; d[4] = a00 * -s + a10 * c; d[5] = a01 * -s + a11 * c; d[6] = a02 * -s + a12 * c; d[7] = a03 * -s + a13 * c; return this; }; Mat4.prototype.scale = function(scalar) { var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, d; d = this.data; a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3]; a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7]; a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11]; d[0] = a00 * scalar; d[1] = a01 * scalar; d[2] = a02 * scalar; d[3] = a03 * scalar; d[4] = a10 * scalar; d[5] = a11 * scalar; d[6] = a12 * scalar; d[7] = a13 * scalar; d[8] = a20 * scalar; d[9] = a21 * scalar; d[10] = a22 * scalar; d[11] = a23 * scalar; return this; }; Mat4.prototype.mulMat4 = function(other, dst) { var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33, b0, b1, b2, b3, dest, mat, mat2; if (dst == null) { dst = this; } dest = dst.data; mat = this.data; mat2 = other.data; a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3]; a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7]; a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11]; a30 = mat[12]; a31 = mat[13]; a32 = mat[14]; a33 = mat[15]; b0 = mat2[0]; b1 = mat2[1]; b2 = mat2[2]; b3 = mat2[3]; dest[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; dest[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; dest[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; dest[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = mat2[4]; b1 = mat2[5]; b2 = mat2[6]; b3 = mat2[7]; dest[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; dest[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; dest[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; dest[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = mat2[8]; b1 = mat2[9]; b2 = mat2[10]; b3 = mat2[11]; dest[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; dest[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; dest[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; dest[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = mat2[12]; b1 = mat2[13]; b2 = mat2[14]; b3 = mat2[15]; dest[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; dest[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; dest[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; dest[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; return dst; }; Mat4.prototype.mulVec3 = function(vec, dst) { if (dst == null) { dst = vec; } return this.mulVal3(vec.x, vec.y, vec.z, dst); }; Mat4.prototype.mulVal3 = function(x, y, z, dst) { var d; dst = dst.data; d = this.data; dst[0] = d[0] * x + d[4] * y + d[8] * z; dst[1] = d[1] * x + d[5] * y + d[9] * z; dst[2] = d[2] * x + d[6] * y + d[10] * z; return dst; }; Mat4.prototype.mulVec4 = function(vec, dst) { if (dst == null) { dst = vec; } return this.mulVal4(vec.x, vec.y, vec.z, vec.w, dst); }; Mat4.prototype.mulVal4 = function(x, y, z, w, dst) { var d; dst = dst.data; d = this.data; dst[0] = d[0] * x + d[4] * y + d[8] * z + d[12] * w; dst[1] = d[1] * x + d[5] * y + d[9] * z + d[13] * w; dst[2] = d[2] * x + d[6] * y + d[10] * z + d[14] * w; dst[3] = d[3] * x + d[7] * y + d[11] * z + d[15] * w; return dst; }; Mat4.prototype.invert = function(dst) { var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33, b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10, b11, d, dest, invDet, mat; if (dst == null) { dst = this; } mat = this.data; dest = dst.data; a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3]; a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7]; a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11]; a30 = mat[12]; a31 = mat[13]; a32 = mat[14]; a33 = mat[15]; b00 = a00 * a11 - a01 * a10; b01 = a00 * a12 - a02 * a10; b02 = a00 * a13 - a03 * a10; b03 = a01 * a12 - a02 * a11; b04 = a01 * a13 - a03 * a11; b05 = a02 * a13 - a03 * a12; b06 = a20 * a31 - a21 * a30; b07 = a20 * a32 - a22 * a30; b08 = a20 * a33 - a23 * a30; b09 = a21 * a32 - a22 * a31; b10 = a21 * a33 - a23 * a31; b11 = a22 * a33 - a23 * a32; d = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (d === 0) { return; } invDet = 1 / d; dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; return dst; }; Mat4.prototype.set = function(a00, a10, a20, a30, a01, a11, a21, a31, a02, a12, a22, a32, a03, a13, a23, a33) { var d; d = this.data; d[0] = a00; d[4] = a10; d[8] = a20; d[12] = a30; d[1] = a01; d[5] = a11; d[9] = a21; d[13] = a31; d[2] = a02; d[6] = a12; d[10] = a22; d[14] = a32; d[3] = a03; d[7] = a13; d[11] = a23; d[15] = a33; return this; }; return Mat4; })(); }).call(this); ; /*! jQuery v1.7.2 jquery.com | jquery.org/license */ (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
    a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
    "+""+"
    ",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
    t
    ",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
    ",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
    ","
    "]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f .clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
    ").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);; loader.define('/application.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Antialias, CompositingControl, Cube, DeferredModel, DeferredShadowMap, Illumination, Lighting, LowresModel, Model, PictureSettings, Quad, Rendernode, SHConstants, SSAO, Sun, Windows, camera, loading, makeStat, schedule, _ref, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; schedule = require('schedule'); loading = require('loading'); camera = require('camera'); Quad = require('/webgl/quad'); Cube = require('/webgl/cube'); Antialias = require('antialias'); _ref = require('model'), LowresModel = _ref.LowresModel, Model = _ref.Model; Illumination = require('illumination'); Rendernode = require('/rendernode'); Windows = require('/windows'); DeferredShadowMap = require('/depth').DeferredShadowMap; Sun = require('sun'); DeferredModel = require('deferred_model'); SSAO = require('ssao'); CompositingControl = (function() { function CompositingControl(gui) { var folder; gui.remember(this); this.draw_probes = false; this.gi = 1; this.di = 1; this.ao = 0.8; folder = gui.addFolder('Compositing'); folder.add(this, 'draw_probes').name('Draw Probes'); folder.add(this, 'gi', 0.0, 2.0).name('Glob. Illum.'); folder.add(this, 'di', 0.0, 2.0).name('Direct. Illum.'); folder.add(this, 'ao', 0.0, 1.0).name('SSAO'); } return CompositingControl; })(); Lighting = (function(_super) { __extends(Lighting, _super); function Lighting(gui) { this.update = __bind(this.update, this); this.bouncesChanged = __bind(this.bouncesChanged, this); this.computeRadiance = __bind(this.computeRadiance, this); var folder; gui.remember(this); Lighting.__super__.constructor.call(this); this.sunRadiance = 1.0; this.skyRadiance = 1.0; this.giGain = 1.0; this.bounces = 3; this.sunColor = [255, 255, 255]; this.skyColor = [0x07, 0xcb, 0xf5]; this.sun_radiance = [0, 0, 0]; this.sky_radiance = [0, 0, 0]; folder = gui.addFolder('Lighting'); folder.addColor(this, 'sunColor').name('Sun Color').onChange(this.computeRadiance); folder.add(this, 'sunRadiance', 0.0, 20.0).name('Sun Radiance').onChange(this.computeRadiance); folder.addColor(this, 'skyColor').name('Sky Color').onChange(this.computeRadiance); folder.add(this, 'skyRadiance', 0.0, 20.0).name('Sky Radiance').onChange(this.computeRadiance); folder.add(this, 'giGain', 0.0, 20.0).name('GI-gain').onChange(this.update); folder.add(this, 'bounces', 1.0, 10.0).step(1).name('Bounces').onChange(this.bouncesChanged); this.computeRadiance(); this.lastbounces = this.bounces; } Lighting.prototype.computeRadiance = function() { this.sun_radiance[0] = this.sunRadiance * (this.sunColor[0] / 255.0); this.sun_radiance[1] = this.sunRadiance * (this.sunColor[1] / 255.0); this.sun_radiance[2] = this.sunRadiance * (this.sunColor[2] / 255.0); this.sky_radiance[0] = this.skyRadiance * (this.skyColor[0] / 255.0); this.sky_radiance[1] = this.skyRadiance * (this.skyColor[1] / 255.0); this.sky_radiance[2] = this.skyRadiance * (this.skyColor[2] / 255.0); return this.update(); }; Lighting.prototype.bouncesChanged = function() { if (this.bounces !== this.lastBounces) { this.lastBounces = this.bounces; return this.update(); } }; Lighting.prototype.update = function() { return this.trigger('change'); }; return Lighting; })(require('events')); PictureSettings = (function() { function PictureSettings(gui) { var folder; gui.remember(this); this.inputGamma = 1.8; this.outputGamma = 1.8; this.brightness = 1.0; this.saturation = 1.0; folder = gui.addFolder('Picture'); folder.add(this, 'inputGamma', 0.25, 3.0).name('Input Gamma'); folder.add(this, 'outputGamma', 0.25, 3.0).name('Output Gamma'); folder.add(this, 'brightness', 0.0, 10.0).name('Exposure'); folder.add(this, 'saturation', 0.0, 4.0).name('Saturation'); } return PictureSettings; })(); SHConstants = (function() { function SHConstants(app, gui) { var folder; this.app = app; this.change = __bind(this.change, this); gui.remember(this); this.c1 = 0.43; this.c2 = 0.66; this.band3 = 1.0; this.c3 = 0.9; this.c4 = 0.34; this.c5 = 0.43; this.data = new Float32Array(5); folder = gui.addFolder('Harmonics'); folder.add(this, 'c1', 0.0, 4.0).name('L0').onChange(this.change); folder.add(this, 'c2', 0.0, 4.0).name('L1').onChange(this.change); folder.add(this, 'band3', 0.0, 4.0).name('L2').onChange(this.change); folder.add(this, 'c3', 0.0, 4.0).name('L2m2/L2m1/L21').onChange(this.change); folder.add(this, 'c4', 0.0, 4.0).name('L20').onChange(this.change); folder.add(this, 'c5', 0.0, 4.0).name('L22').onChange(this.change); this.updateData(); } SHConstants.prototype.updateData = function() { this.data[0] = this.c1; this.data[1] = this.c2; this.data[2] = this.band3 * this.c3; this.data[3] = this.band3 * this.c4; return this.data[4] = this.band3 * this.c5; }; SHConstants.prototype.change = function() { this.updateData(); return this.app.lightChange(); }; return SHConstants; })(); makeStat = function(mode, offset) { var node, stats; stats = new Stats(); stats.setMode(mode); node = $(stats.domElement); node.css({ position: 'absolute', left: offset, top: 0 }).appendTo('body').hide(); stats.hide = function() { return node.clearQueue().fadeOut(); }; stats.show = function() { return node.clearQueue().fadeIn(); }; return stats; }; exports.Application = (function() { function _Class(canvas) { var floatExt, folder, gui, resmap, _this = this; this.canvas = canvas; this.update = __bind(this.update, this); this.resize = __bind(this.resize, this); this.lightChange = __bind(this.lightChange, this); this.sunChanged = __bind(this.sunChanged, this); $('
    ').css('margin', 10).appendTo('#ui'); Rendernode.stateDefaults(gl); gui = this.gui = new dat.GUI({ load: get('presets/new.json') }); gui.remember(this); this.gui_width = gui.width = 370; this.gui.closed = false; this.fps = makeStat(0, 0); this.rtime = makeStat(1, 80); this.gui_closed = gui.closed; this.resolution = 0.5; this.resolution_label = '1:2 default'; resmap = { '2:1 very slow!': 2, '1:1 slow': 1, '1:2 default': 0.5, '1:4 ugly': 0.25, '1:8 worse': 0.125 }; folder = gui.addFolder('Performance'); $('
  • WASD=move, space=overview, cursor keys=navigate
  • ').appendTo(folder.__ul); folder.add(this, 'resolution_label', ['2:1 very slow!', '1:1 slow', '1:2 default', '1:4 ugly', '1:8 worse']).name('Resolution').onChange(function() { _this.resolution = resmap[_this.resolution_label]; return _this.resizeBuffers(); }); this.resolution = resmap[this.resolution_label]; this.show_fps = false; folder.add(this, 'show_fps').name('FPS').onChange(function() { if (_this.show_fps) { _this.fps.show(); return _this.rtime.show(); } else { _this.fps.hide(); return _this.rtime.hide(); } }); if (this.show_fps) { this.fps.show(); this.rtime.show(); } else { this.fps.hide(); this.rtime.hide(); } this.picture = new PictureSettings(gui); this.sun = new Sun(gui).on('change', this.sunChanged); this.compositing_control = new CompositingControl(gui); this.lighting = new Lighting(gui).on('change', this.lightChange); this.shconst = new SHConstants(this, gui); loading.hide(); this.near = 0.1; this.far = 42; this.camera = new camera.FlyCam({ gui: gui, near: this.near, far: this.far, x: -10, y: 7, z: -1.5, o: 100, p: 20 }); this.sponza = new Model(gl); this.lowres = new LowresModel(gl); floatExt = gl.getFloatExtension({ require: ['renderable', 'filterable'] }); this.view_normaldepth = new Rendernode(gl, { program: get('normaldepth.shader'), drawable: this.sponza, depthBuffer: true, depthTest: true, depthWrite: true, cullFace: 'BACK', type: floatExt.type, filter: 'nearest', hdrClear: true }); this.ssao = new SSAO(gl, this.view_normaldepth); this.direct_light = new DeferredShadowMap(gl, { drawable: this.sponza, depthWidth: 512, depthHeight: 512, eyeNormaldepth: this.view_normaldepth, light: this.sun, camera: this.camera, blurred: true }); this.illumination = new Illumination(gl, this.sun, this.lighting, this.lowres, this.sponza, this.view_normaldepth, this.sun.orientation, this.sun.elevation, this.shconst); this.albedo = new Rendernode(gl, { program: get('albedo.shader'), drawable: this.sponza, depthBuffer: true, depthTest: true, depthWrite: true }); this.global_illumination = new Rendernode(gl, { program: get('global_illumination.shader'), drawable: new DeferredModel(gl, this.illumination.probes), cullFace: 'FRONT', blend: 'additive', type: floatExt.type, depthBuffer: this.view_normaldepth.depth, depthWrite: false, depthTest: 'GEQUAL' }); this.composit = new Rendernode(gl, { program: get('composit.shader'), drawable: quad }); this.antialias = new Antialias(gl, gui, this.composit); this.windows = new Windows(gl, gui, [ { label: 'Scene depth from sun', affine: [1, 0], gamma: false, tex: this.direct_light.depth.output }, { label: 'Scene normal/depth', affine: [0.5, 0.5], gamma: false, tex: this.view_normaldepth }, { label: 'Scene depth moments', gamma: false, tex: this.ssao.blur.output }, { label: 'Direct Illumination Lightmap', tex: this.illumination.direct_light.output }, { label: 'Global Illumination Lightmap', diva: true, tex: this.illumination.bounce }, { label: 'Lightmap Dictionary', tex: this.illumination.texmap }, { label: 'Albedo Probe Values', tex: this.illumination.diffusemap }, { label: 'Light Probes', tex: this.illumination.lightprobes }, { label: 'Spherical Harmonics Coefficients', tex: this.illumination.coefficients }, { label: 'Albedo', tex: this.albedo }, { label: 'SSAO', gamma: false, tex: this.ssao.output }, { label: 'Direct Illumination', tex: this.direct_light.output }, { label: 'Global Illumination', diva: true, tex: this.global_illumination }, { label: 'Composited', gamma: false, tex: this.composit }, { label: 'Antialiased', gamma: false, tex: this.antialias.node } ]); this.target_width = this.canvas.width(); this.current_width = this.target_width; $(window).resize(this.resize); this.resize(); schedule.run(this.update); this.canvas.fadeIn(2000); $('div.dg > ul').css('margin-top', 0); } _Class.prototype.sunChanged = function() { this.direct_light.updateDepth(); this.illumination.updateDirectLight(); return this.lightChange(); }; _Class.prototype.lightChange = function() { return this.illumination.update(); }; _Class.prototype.resizeBuffers = function(width, height) { var h, w; w = this.width * this.resolution; h = this.height * this.resolution; this.view_normaldepth.resize(w, h); this.albedo.resize(w, h); this.global_illumination.resize(w, h); this.direct_light.resize(w, h); this.composit.resize(w, h); this.antialias.resize(w, h); this.illumination.debug.resize(w, h); return this.ssao.resize(w, h); }; _Class.prototype.resize = function() { this.width = this.canvas.width(); this.height = this.canvas.height(); this.camera.aspect(this.width, this.height); this.canvas[0].width = this.width; this.canvas[0].height = this.height; this.resizeBuffers(this.width, this.height); return this.resizeWindows(); }; _Class.prototype.update = function() { this.fps.end(); this.fps.begin(); this.rtime.begin(); this.step(); this.draw(); return this.rtime.end(); }; _Class.prototype.resizeWindows = function() { return this.windows.node.viewport(0, 0, this.current_width, this.height); }; _Class.prototype.step = function() { var dw, gui_closed, gui_width; gui_closed = this.gui.closed; if (gui_closed) { this.target_width = this.width; } else { gui_width = this.gui.width; this.target_width = this.width - (gui_width + 10); } dw = this.target_width - this.current_width; this.current_width = this.current_width + dw * 0.1; dw = Math.abs(this.target_width - this.current_width); if (dw > 1) { this.resizeWindows(); } else if (dw <= 1 && dw > 0) { this.current_width = this.target_width; this.resizeWindows(); } return this.camera.update(); }; _Class.prototype.draw = function() { var probe_factor; this.view_normaldepth.clear(0, 0, 0, 100).start().clearDepth().mat4('proj', this.camera.proj).mat4('view', this.camera.view).mat3('view_rot', this.camera.rot).drawModel('bumpmap').end(); this.ssao.update(); this.albedo.start().f('gamma', this.picture.inputGamma).mat4('proj', this.camera.proj).mat4('view', this.camera.view).mat3('view_rot', this.camera.rot).clearBoth(0, 0, 0, 0).drawModel('diffuse_texture').end(); this.global_illumination.start().f('gi_gain', this.lighting.giGain).fv('shconst', this.shconst.data).sampler('normaldepth', this.view_normaldepth).mat4('proj', this.camera.proj).mat4('view', this.camera.view).mat4('inv_view', this.camera.inv_view).sampler('coefficients', this.illumination.coefficients).val2('coefficients_size', this.illumination.coefficients.width, this.illumination.coefficients.height).clear().draw().end(); this.direct_light.updateShadow(); if (this.compositing_control.draw_probes) { probe_factor = 1; this.illumination.drawDebug(this.camera, this.view_normaldepth); } else { probe_factor = 0; } this.composit.start().clear().f('gamma', this.picture.outputGamma).f('brightness', this.picture.brightness).f('saturation', this.picture.saturation).vec3('sun_radiance', this.lighting.sun_radiance).vec3('sky_radiance', this.lighting.sky_radiance).f('probe_factor', probe_factor).f('gi_factor', this.compositing_control.gi).f('di_factor', this.compositing_control.di).f('ao_factor', this.compositing_control.ao).vec3('sky_color', this.lighting.skyColor).sampler('debug', this.illumination.debug).sampler('albedo', this.albedo).sampler('global', this.global_illumination).sampler('direct', this.direct_light.output).sampler('ssao', this.ssao.output).draw().end(); this.antialias.apply(); return this.windows.draw(this.picture.outputGamma); }; return _Class; })(); }); loader.define('/deferred_model.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var DeferredModel, Sphere, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Sphere = require('/webgl/sphere'); return DeferredModel = (function(_super) { __extends(DeferredModel, _super); DeferredModel.prototype.attribs = ['position', 'lightprobe', 'center']; DeferredModel.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 7 }, { name: 'lightprobe', size: 4, offset: 3, stride: 7 } ]; function DeferredModel(gl, probes) { var buffer, i, probe, px, py, pz, template, vi, x, y, z, _i, _j, _len, _ref; this.gl = gl; DeferredModel.__super__.constructor.call(this); template = Sphere.makeVertices(5.1, 2); buffer = []; for (i = _i = 0, _len = probes.length; _i < _len; i = ++_i) { probe = probes[i]; px = probe.x; py = probe.y; pz = probe.z; for (vi = _j = 0, _ref = template.length; _j < _ref; vi = _j += 3) { x = template[vi]; y = template[vi + 1]; z = template[vi + 2]; buffer.push(x, y, z, px, py, pz, i); } } this.size = buffer.length / 7; this.uploadList(buffer); } return DeferredModel; })(require('webgl/drawable')); }); loader.define('/main.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Quad, Shader, audio, disableSelect, enableSelect, errorContainer, load_hooks, loading; audio = require('audio'); loading = require('loading'); Shader = require('webgl/shader'); Quad = require('webgl/quad'); require('webgl-nuke-vendor-prefix'); require('webgl-texture-float-extension-shims'); load_hooks = { '\.jpg$|\.jpeg$|\.gif$|\.png': function(name, buffer, callback) { var ext, image, mime; ext = name.split('.').pop(); switch (ext) { case 'png': mime = 'image/png'; break; case 'gif': mime = 'image/gif'; break; case 'jpg': case 'jpeg': mime = 'image/jpeg'; } image = new Image(); image.src = getURL(buffer, mime); return image.onload = function() { return callback(image); }; }, '\.mpg$|\.ogg$|\.wav$': function(name, buffer, callback) { return audio.decode(buffer, function(result) { return callback(result); }); } }; errorContainer = function(title) { canvas.remove(); $('#ui').empty(); return $('
    ').css({ position: 'absolute', width: 300, left: '50%', top: 50, marginLeft: -100 }).append($('

    ').text(title)).appendTo('#ui'); }; disableSelect = function() { $('*').each(function() { $(this).attr('unselectable', 'on').css({ '-moz-user-select': 'none', '-webkit-user-select': 'none', 'user-select': 'none', '-ms-user-select': 'none' }); return this.onselectstart = function() { return false; }; }); return document.oncontextmenu = function() { return false; }; }; enableSelect = function() { $('*').each(function() { $(this).removeAttr('unselectable').css({ '-moz-user-select': 'text', '-webkit-user-select': 'text', 'user-select': 'text', '-ms-user-select': 'text' }); return this.onselectstart = void 0; }); return document.oncontextmenu = void 0; }; exports.main = function() { var Application, application, container, floatExt, stddev; disableSelect(); window.canvas = $('canvas'); window.onerror = function(error) { if (error.search(Shader.error) > 0) { return true; } }; try { window.gl = canvas[0].getContext('experimental-webgl'); if (!window.gl) { window.gl = canvas[0].getContext('webgl'); } } catch (_error) {} if (window.gl) { window.quad = new Quad(window.gl); stddev = gl.getExtension('OES_standard_derivatives'); if (!stddev) { return errorContainer('Missing Extension: Standard Derivatives').append('

    This application requires the WebGL Standard Derivatives extension which you do not have, sorry.

    '); } floatExt = gl.getFloatExtension({ require: ['renderable'], prefer: ['filterable', 'half'], throws: false }); if (!floatExt) { return errorContainer('Missing Extension: Floating Point Textures').append('

    This application requires the WebGL Floating Point Textures extension which you do not have, sorry.

    '); } Application = require('application').Application; application = null; loading.show('Loading ...'); return loader.hooks(load_hooks).mount({ url: 'assets.pack', loaded: function(files, fs) { var container, name, value; for (name in files) { value = files[name]; if (name.match('\.shaderlib$')) { fs[name] = Shader.splitLines(name, value); } } try { for (name in files) { value = files[name]; if (name.match('\.shader$')) { fs[name] = new Shader(gl, name, value); } } return application = new Application(window.canvas, window.gl); } catch (error) { if (error === 'ShaderError') { enableSelect(); container = errorContainer('Shader Error').append('

    \n An error occured when compiling a shader, you can paste me the error.\n

    '); container.css({ width: 600, marginLeft: -300 }); return $('
    ').text(Shader.lastError).css('overflow', 'auto').appendTo(container);
              } else {
                throw error;
              }
            }
          },
          progress: loading.progress
        });
      } else {
        container = errorContainer('You dont have WebGL');
        if ($.browser.msie) {
          container.append('

    \n You have Internet Explorer, please install\n Google Chrome or\n Firefox\n

    '); } else if ($.browser.webkit) { container.append('

    \n If you use OSX Safari, please enable WebGL manually.\n If you use iOS Safari, you cannot use WebGL.\n If you use Android, please try Firefox Mobile or\n Opera Mobile\n

    '); } return container.append('

    \n Please consult the support pages\n on how to get WebGL for your machine.\n

    '); } }; }); loader.define('/sun.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Sun, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Sun = (function(_super) { __extends(Sun, _super); function Sun(gui, orientation, elevation) { var folder; this.orientation = orientation != null ? orientation : 104; this.elevation = elevation != null ? elevation : 60; this.update = __bind(this.update, this); gui.remember(this); Sun.__super__.constructor.call(this); folder = gui.addFolder('Sun'); folder.add(this, 'orientation', 0, 360).onChange(this.update); folder.add(this, 'elevation', 0, 90).onChange(this.update); this.near = -1; this.far = 41; this.proj = new Mat4().ortho(this.near, this.far, 21, -21, -21, 21); this.view = new Mat4(); this.rot = new Mat3(); this.update(); } Sun.prototype.update = function() { this.view.identity().translateVal3(0, 0, -21).rotatex(this.elevation).rotatey(this.orientation).translateVal3(0, -7.5, 0).toMat3(this.rot.identity()); this.trigger('change'); return this; }; return Sun; })(require('events')); }); loader.define('/dist3d.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var cross, dot, length, sadd, slength, smul, vadd, vsub; vsub = function(p1, p2) { return [p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]]; }; vadd = function(p1, p2) { return [p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2]]; }; sadd = function(s, p) { var x, y, z; x = p[0]; y = p[1]; z = p[2]; return [x + s, y + s, z + s]; }; slength = function(p) { var x, y, z; x = p[0]; y = p[1]; z = p[2]; return x * x + y * y + z * z; }; length = function(p) { return Math.sqrt(slength(p)); }; dot = function(p1, p2) { var x1, x2, y1, y2, z1, z2; x1 = p1[0]; y1 = p1[1]; z1 = p1[2]; x2 = p2[0]; y2 = p2[1]; z2 = p2[2]; return x1 * x2 + y1 * y2 + z1 * z2; }; smul = function(s, p) { var x, y, z; x = p[0]; y = p[1]; z = p[2]; return [x * s, y * s, z * s]; }; cross = function(p1, p2) { var x1, x2, y1, y2, z1, z2; x1 = p1[0]; y1 = p1[1]; z1 = p1[2]; x2 = p2[0]; y2 = p2[1]; z2 = p2[2]; return [y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2]; }; exports.closestPointTriangle = function(p, a, b, c) { var ab, ac, bc, n, sdenom, snom, tdenom, tnom, u, udenom, unom, v, va, vb, vc, w; ab = vsub(b, a); ac = vsub(c, a); bc = vsub(c, b); snom = dot(vsub(p, a), ab); sdenom = dot(vsub(p, b), vsub(a, b)); tnom = dot(vsub(p, a), ac); tdenom = dot(vsub(p, c), vsub(a, c)); if (snom <= 0 && tnom <= 0) { return a; } unom = dot(vsub(p, b), bc); udenom = dot(vsub(p, c), vsub(b, c)); if (sdenom <= 0 && unom <= 0) { return b; } if (tdenom <= 0 && udenom <= 0) { return c; } n = cross(vsub(b, a), vsub(c, a)); vc = dot(n, cross(vsub(a, p), vsub(b, p))); if (vc <= 0 && snom >= 0 && sdenom >= 0) { return vadd(a, smul(snom / (snom + sdenom), ab)); } va = dot(n, cross(vsub(b, p), vsub(c, p))); if (va <= 0 && unom >= 0 && udenom >= 0) { return vadd(b, smul(unom / (unom + udenom), bc)); } vb = dot(n, cross(vsub(c, p), vsub(a, p))); if (vb <= 0 && tnom > 0 && tdenom >= 0) { return vadd(a, smul(tnom / (tnom + tdenom), ac)); } u = va / (va + vb + vc); v = vb / (va + vb + vc); w = 1 - u - v; return vadd(smul(u, a), vadd(smul(v, b), smul(w, c))); }; exports.pointTriangleDist = function(p, v0, v1, v2) { var cx, cy, cz, dx, dy, dz, px, py, pz, _ref; _ref = exports.closestPointTriangle(p, v0, v1, v2), cx = _ref[0], cy = _ref[1], cz = _ref[2]; px = p[0], py = p[1], pz = p[2]; dx = cx - px; dy = cy - py; dz = cz - pz; return Math.sqrt(dx * dx + dy * dy + dz * dz); }; }); loader.define('/windows/module.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Quad, Rendernode, Window, Windows, keys, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; Quad = require('/webgl/quad'); Rendernode = require('/rendernode'); keys = require('/keys'); Window = (function() { function Window(index, texture, node, x, y) { this.index = index; this.texture = texture; this.node = node; this.x = x; this.y = y; this.label = this.texture.label; if (this.texture.diva) { this.diva = 1; } else { this.diva = 0; } if (this.texture.gamma === false) { this.gamma = 0; } else { this.gamma = 1; } if (this.texture.affine) { this.mul = this.texture.affine[0]; this.add = this.texture.affine[1]; } else { this.mul = 1; this.add = 0; } } Window.prototype.draw = function(xscale, yscale, one2one, cx, cy, active) { var h, height, max, s, w, width; width = this.texture.tex.width; height = this.texture.tex.height; max = Math.max(width, height); w = width / max; h = height / max; s = 1 / Math.max(w * xscale, h * yscale); w = w * (1 - one2one) + s * w * one2one; h = h * (1 - one2one) + s * h * one2one; return this.node.sampler('source', this.texture.tex).f('mixgamma', this.gamma).f('diva', this.diva).f('border_factor', active).val2('affine', this.mul, this.add).val2('size', w * xscale, h * yscale).val2('offset', (this.x - cx) * xscale, (this.y - cy) * yscale).draw(); }; return Window; })(); return Windows = (function() { function Windows(gl, gui, textures) { var active, folder, gridsize, i, labels, maxx, maxy, minx, miny, texture, window, x, y, _i, _j, _len, _len1, _ref, _ref1, _this = this; this.gl = gl; this.textures = textures; this.prev = __bind(this.prev, this); this.next = __bind(this.next, this); this.guiLabelChange = __bind(this.guiLabelChange, this); this.labelVisibilityChange = __bind(this.labelVisibilityChange, this); gui.remember(this); this.label = $('
    test
    ').appendTo('#ui').hide(); this.show_all = false; this.show_label = false; this.needs_clear = $.browser.mozilla; this.node = new Rendernode(this.gl, { front: true, program: get('window.shader'), drawable: quad }); this.windows = []; this.labelmap = {}; labels = []; gridsize = Math.ceil(Math.sqrt(this.textures.length)); _ref = this.textures; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { texture = _ref[i]; x = i % gridsize; y = gridsize - Math.floor(i / gridsize) - 1; window = new Window(i, texture, this.node, x * 2.2, y * 2.2); this.labelmap[window.label] = window; this.windows.push(window); labels.push(window.label); } minx = null; maxx = null; miny = null; maxy = null; _ref1 = this.windows; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { window = _ref1[_j]; minx = minx !== null ? Math.min(window.x, minx) : window.x; maxx = maxx !== null ? Math.max(window.x, maxx) : window.x; miny = miny !== null ? Math.min(window.y, miny) : window.y; maxy = maxy !== null ? Math.max(window.y, maxy) : window.y; } this.cx = (minx + maxx) / 2; this.cy = (miny + maxy) / 2; this.full_scale = 1.9 / Math.min(maxx - minx + 2, maxy - miny + 2); this.zoom = 0.0; this.active = this.windows.length - 1; keys.press('right', this.next); keys.press('left', this.prev); keys.press('down', function() { var new_value; new_value = _this.active + gridsize; if (new_value < _this.windows.length) { _this.active = new_value; } else { _this.active = _this.active % gridsize; } return _this.setActive(); }); keys.press('up', function() { var new_value; new_value = _this.active - gridsize; if (new_value >= 0) { _this.active = new_value; } else { new_value = gridsize * gridsize + new_value; while (new_value >= _this.windows.length) { new_value -= gridsize; } _this.active = new_value; } return _this.setActive(); }); keys.press('space', function() { _this.show_all = !_this.show_all; return _this.all_ctrl.setValue(_this.show_all); }); keys.press('enter', function() { _this.show_all = !_this.show_all; return _this.all_ctrl.setValue(_this.show_all); }); active = this.getActive(); this.x = active.x; this.y = active.y; folder = gui.addFolder('Views'); this.all_ctrl = folder.add(this, 'show_all').name('Overview'); folder.add(this, 'next').name('Next view'); folder.add(this, 'prev').name('Prev view'); this.window_label = active.label; this.guiLabel = folder.add(this, 'window_label', labels).name('View').onChange(this.guiLabelChange); folder.add(this, 'show_label').name('Labels').onChange(this.labelVisibilityChange); this.guiLabelChange(); } Windows.prototype.labelVisibilityChange = function() { if (this.show_label) { return this.label.clearQueue().fadeIn(); } else { return this.label.clearQueue().fadeOut(); } }; Windows.prototype.guiLabelChange = function() { var window; window = this.labelmap[this.window_label]; this.active = window.index; return this.setActive(); }; Windows.prototype.getActive = function() { return this.windows[this.active]; }; Windows.prototype.setActive = function() { var text; text = this.getActive().label; this.window_label = text; this.guiLabel.updateDisplay(); return this.label.text(text); }; Windows.prototype.next = function() { this.active = (this.active + 1) % this.windows.length; return this.setActive(); }; Windows.prototype.prev = function() { if (this.active === 0) { this.active = this.windows.length - 1; } else { this.active -= 1; } return this.setActive(); }; Windows.prototype.step = function() { var active, tx, ty; active = this.getActive(); tx = active.x; ty = active.y; this.x = this.x + (tx - this.x) * 0.1; this.y = this.y + (ty - this.y) * 0.1; if (this.show_all) { return this.zoom = this.zoom + (1 - this.zoom) * 0.1; } else { return this.zoom = this.zoom + (0 - this.zoom) * 0.1; } }; Windows.prototype.draw = function(gamma) { var active, factor, height, width, window, xscale, yscale, _i, _len, _ref; this.step(); if (this.needs_clear === true) { this.gl.clearColor(0, 0, 0, 0); this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); } this.node.start().f('gamma', gamma); width = this.node.width; height = this.node.height; if (width > height) { xscale = height / width; yscale = 1; } else { xscale = 1; yscale = width / height; } factor = 1.0 - this.zoom + this.zoom * this.full_scale; xscale *= factor; yscale *= factor; active = this.getActive(); _ref = this.windows; for (_i = 0, _len = _ref.length; _i < _len; _i++) { window = _ref[_i]; if (window !== active) { this.drawWindow(xscale, yscale, window, 0); } } this.drawWindow(xscale, yscale, active, 1); return this.node.end(); }; Windows.prototype.drawWindow = function(xscale, yscale, window, active) { var dx, dy, l, one2one, x, y; dx = window.x - this.x; dy = window.y - this.y; l = Math.sqrt(dx * dx + dy * dy); if (l > 0) { one2one = Math.min(1 / (l * 2), 1); } else { one2one = 1; } x = this.x * (1 - this.zoom) + this.cx * this.zoom; y = this.y * (1 - this.zoom) + this.cy * this.zoom; active = Math.pow(one2one, 2.0) * this.zoom; return window.draw(xscale, yscale, one2one * (1 - this.zoom), x, y, active); }; return Windows; })(); }); loader.define('/depth/module.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Blur, DeferredShadowMap, DepthRender, LightmappedShadowMap, Rendernode; Rendernode = require('/rendernode'); Blur = require('/blur'); exports.DepthRender = DepthRender = (function() { function DepthRender(gl, width, height, drawable, _arg) { var blurred, floatExt; blurred = (_arg != null ? _arg : {}).blurred; if (blurred == null) { blurred = false; } floatExt = gl.getFloatExtension({ require: ['renderable', 'filterable'] }); this.direct = new Rendernode(gl, { width: width, height: height, program: get('depth.shader'), drawable: drawable, depthBuffer: true, depthTest: true, depthWrite: true, filter: blurred ? 'nearest' : 'linear', type: floatExt.type, cullFace: 'BACK' }); if (blurred) { this.blurred = new Blur(gl, { width: width, height: height, type: floatExt.type }); } this.output = this.blurred ? this.blurred.output : this.direct; } DepthRender.prototype.update = function(proj, view) { this.direct.start().clearBoth(0, 0, 0, 1).mat4('proj', proj).mat4('view', view).f('range', 42).draw().end(); if (this.blurred) { return this.blurred.update(this.direct); } }; return DepthRender; })(); exports.DeferredShadowMap = DeferredShadowMap = (function() { function DeferredShadowMap(gl, _arg) { var blurred, depthHeight, depthWidth, drawable; drawable = _arg.drawable, depthWidth = _arg.depthWidth, depthHeight = _arg.depthHeight, this.eyeNormaldepth = _arg.eyeNormaldepth, this.light = _arg.light, this.camera = _arg.camera, blurred = _arg.blurred; this.depth = new DepthRender(gl, depthWidth, depthHeight, drawable, { blurred: blurred }); this.output = new Rendernode(gl, { program: get('deferred_shadow_map.shader'), drawable: quad }); this.updateDepth(); } DeferredShadowMap.prototype.resize = function(width, height) { return this.output.resize(width, height); }; DeferredShadowMap.prototype.updateDepth = function() { return this.depth.update(this.light.proj, this.light.view); }; DeferredShadowMap.prototype.updateShadow = function() { return this.output.start().clear(1, 0, 1).sampler('eye_normaldepth', this.eyeNormaldepth).sampler('light_depth', this.depth.output).mat4('inv_eye_proj', this.camera.inv_proj).mat4('inv_eye_view', this.camera.inv_view).mat4('light_view', this.light.view).mat4('light_proj', this.light.proj).mat3('light_rot', this.light.rot).draw().end(); }; return DeferredShadowMap; })(); exports.LightmapShadowMap = LightmappedShadowMap = (function() { function LightmappedShadowMap(gl, _arg) { var blurred, depthHeight, depthWidth, drawable, lightmapSize; drawable = _arg.drawable, depthWidth = _arg.depthWidth, depthHeight = _arg.depthHeight, lightmapSize = _arg.lightmapSize, this.light = _arg.light, blurred = _arg.blurred; this.depth = new DepthRender(gl, depthWidth, depthHeight, drawable, { blurred: blurred }); if (lightmapSize == null) { lightmapSize = 256; } this.output = new Rendernode(gl, { width: lightmapSize, height: lightmapSize, program: get('lightmap_shadow_map.shader'), drawable: drawable }); this.update(); } LightmappedShadowMap.prototype.update = function() { this.depth.update(this.light.proj, this.light.view); return this.output.start().sampler('light_depth', this.depth.output).mat4('light_view', this.light.view).mat4('light_proj', this.light.proj).mat3('light_rot', this.light.rot).draw().end(); }; return LightmappedShadowMap; })(); }); loader.define('/antialias/module.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var AntiAlias, Quad, Rendernode; Rendernode = require('/rendernode'); Quad = require('/webgl/quad'); return AntiAlias = (function() { function AntiAlias(gl, gui, source) { var folder; this.gl = gl; this.source = source; gui.remember(this); this.node = new Rendernode(this.gl, { program: get('fxaa3_11.shader'), drawable: quad }); this.subpixel_aa = 0.75; this.contrast_treshold = 0.166; this.edge_treshold = 0.0; folder = gui.addFolder('Antialias'); folder.add(this, 'subpixel_aa', 0.0, 1.0).name('Subpixel aa'); folder.add(this, 'contrast_treshold', 0.063, 0.333).name('Contrast Treshold'); folder.add(this, 'edge_treshold', 0.0, 0.0833).name('Edge Treshold'); } AntiAlias.prototype.apply = function() { return this.node.start().f('subpixel_aa', this.subpixel_aa).f('contrast_treshold', this.contrast_treshold).f('edge_treshold', this.edge_treshold).clear().sampler('source', this.source).draw().end(); }; AntiAlias.prototype.resize = function(width, height) { return this.node.resize(width, height); }; return AntiAlias; })(); }); loader.define('/illumination/module.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var BounceModel, DepthRender, Illumination, LightmapShadowMap, Quad, Rendernode, Sphere, Texture2D, _ref; Quad = require('/webgl/quad'); Sphere = require('/webgl/sphere'); Texture2D = require('/webgl/texture').Texture2D; Rendernode = require('/rendernode'); _ref = require('/depth'), DepthRender = _ref.DepthRender, LightmapShadowMap = _ref.LightmapShadowMap; BounceModel = require('bounce_model'); return Illumination = (function() { function Illumination(gl, sun, lighting, model, highresmodel, normaldepth, orientation, elevation, shconst) { var floatExt; this.gl = gl; this.lighting = lighting; this.shconst = shconst; this.proj = new Mat4().perspective(90, 1, 0.01, 42); this.view = new Mat4(); this.mapsize = 32; this.probesize = 16; this.generateProbes(); floatExt = this.gl.getFloatExtension({ require: ['renderable', 'filterable'] }); this.debug = new Rendernode(this.gl, { program: get('debug.shader'), drawable: new Sphere(this.gl, 0.6), depthBuffer: true, depthTest: true, depthWrite: true, cullFace: 'BACK', type: floatExt.type }); this.lightprobes = new Rendernode(this.gl, { width: this.probesize * 6, height: this.probesize * this.probes.length, program: get('transfer.shader'), drawable: quad, filter: 'nearest', type: floatExt.type }); this.coefficients = new Rendernode(this.gl, { width: 9, height: this.probes.length, program: get('harmonics.shader'), drawable: quad, filter: 'nearest', type: floatExt.type }); this.direct_light = new LightmapShadowMap(gl, { drawable: model, depthWidth: 128, depthHeight: 128, light: sun, blurred: true }); this.bounce = new Rendernode(this.gl, { width: 256, height: 256, program: get('bounce.shader'), drawable: new BounceModel(this.gl, model, this.probes), type: floatExt.type, blend: 'additive' }); this.renderProbes(model, highresmodel); this.update(); } Illumination.prototype.generateProbes = function() { var i, _i, _j, _results; this.probes = []; for (i = _i = 0; _i < 7; i = ++_i) { this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 2.2, z: 0 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 2.2, z: 5.5 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 2.2, z: -5.5 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 7.0, z: 0 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 7.0, z: 5.5 }); this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 7.0, z: -5.5 }); } _results = []; for (i = _j = 1; _j < 6; i = ++_j) { _results.push(this.probes.push({ x: i * 4.5 - 3 * 4.5, y: 12.5, z: 0 })); } return _results; }; Illumination.prototype.updateDirectLight = function() { return this.direct_light.update(); }; Illumination.prototype.update = function() { var i, _i, _ref1, _results; this.bounce.start().clear(0, 0, 0, 1).f('gi_gain', this.lighting.giGain).val2('coefficients_size', this.coefficients.width, this.coefficients.height).fv('shconst', this.shconst.data).end(); this.lightprobes.start().vec3('sun_radiance', this.lighting.sun_radiance).vec3('sky_radiance', this.lighting.sky_radiance).sampler('texmap', this.texmap).sampler('diffusemap', this.diffusemap).sampler('bounce', this.bounce).sampler('lightmap', this.direct_light.output).draw().end(); this.coefficients.start().val2('lightprobes_size', this.lightprobes.width, this.lightprobes.height).sampler('lightprobes', this.lightprobes).fv('shconst', this.shconst.data).draw().end(); _results = []; for (i = _i = 0, _ref1 = this.lighting.bounces - 1; 0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) { this.bounce.start().clear(0, 0, 0, 0).sampler('coefficients', this.coefficients).draw().end(); this.lightprobes.start().sampler('texmap', this.texmap).sampler('bounce', this.bounce).sampler('lightmap', this.direct_light.output).draw().end(); _results.push(this.coefficients.start().val2('lightprobes_size', this.lightprobes.width, this.lightprobes.height).sampler('lightprobes', this.lightprobes).draw().end()); } return _results; }; Illumination.prototype.renderProbes = function(model, highresmodel) { var i, probe, _i, _j, _len, _len1, _ref1, _ref2; if (get.exists('texmap.png')) { this.texmap = new Texture2D(this.gl).bind().upload(get('texmap.png')).nearest().clampToEdge().unbind(); } else { this.texmap = new Rendernode(this.gl, { width: this.mapsize * 6, height: this.mapsize * this.probes.length, program: get('cubeprobe.shader'), drawable: model, depthTest: true, depthWrite: true, cullFace: 'BACK', filter: 'nearest', depthBuffer: true }); this.texmap.start().clear(0, 0, 1); this.texmap.mat4('proj', this.proj); _ref1 = this.probes; for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) { probe = _ref1[i]; this.renderProbe(i, this.texmap, null, probe.x, probe.y, probe.z); } this.texmap.end(); this.texmap = this.texmap.output; } if (get.exists('diffusemap.jpg')) { return this.diffusemap = new Texture2D(this.gl).bind().upload(get('diffusemap.jpg')).nearest().clampToEdge().unbind(); } else { this.diffusemap = new Rendernode(this.gl, { width: this.mapsize * 6, height: this.mapsize * this.probes.length, program: get('cube_diffuse.shader'), drawable: highresmodel, depthTest: true, depthWrite: true, cullFace: 'BACK', filter: 'nearest', depthBuffer: true }); this.diffusemap.start().clear(0, 0, 0); this.diffusemap.mat4('proj', this.proj); _ref2 = this.probes; for (i = _j = 0, _len1 = _ref2.length; _j < _len1; i = ++_j) { probe = _ref2[i]; this.renderProbe(i, this.diffusemap, 'diffuse_texture', probe.x, probe.y, probe.z); } this.diffusemap.end(); return this.diffusemap = this.diffusemap.output; } }; Illumination.prototype.renderProbe = function(i, node, texture_type, x, y, z) { var offset, s; s = this.mapsize; offset = i * s; this.view.identity().translateVal3(-x, -y, -z); node.viewport(s * 0, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatey(180).translateVal3(-x, -y, -z); node.viewport(s * 1, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatey(-90).translateVal3(-x, -y, -z); node.viewport(s * 2, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatey(90).translateVal3(-x, -y, -z); node.viewport(s * 3, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatex(-90).translateVal3(-x, -y, -z); node.viewport(s * 4, offset, s, s).mat4('view', this.view).drawModel(texture_type); this.view.identity().rotatex(90).translateVal3(-x, -y, -z); return node.viewport(s * 5, offset, s, s).mat4('view', this.view).drawModel(texture_type); }; Illumination.prototype.drawDebug = function(camera, normaldepth) { var i, probe, _i, _len, _ref1; this.debug.start().clearBoth(0, 0, 0, 0).f('gi_gain', this.lighting.giGain).sampler('normaldepth', normaldepth).sampler('coefficients', this.coefficients).val2('coefficients_size', this.coefficients.width, this.coefficients.height).fv('shconst', this.shconst.data).mat4('proj', camera.proj).mat4('view', camera.view); _ref1 = this.probes; for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) { probe = _ref1[i]; this.debug.val3('offset', probe.x, probe.y, probe.z).f('index', i).draw(); } return this.debug.end(); }; return Illumination; })(); }); loader.define('/illumination/bounce_model.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var BounceModel, pointTriangleDist, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; pointTriangleDist = require('/dist3d').pointTriangleDist; return BounceModel = (function(_super) { __extends(BounceModel, _super); BounceModel.prototype.attribs = ['position', 'texcoord', 'normal', 'lightprobe']; BounceModel.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 12 }, { name: 'texcoord', size: 2, offset: 3, stride: 12 }, { name: 'normal', size: 3, offset: 5, stride: 12 }, { name: 'lightprobe', size: 4, offset: 8, stride: 12 } ]; function BounceModel(gl, model, probes) { var btx, bty, btz, det, dist, dot1, dot2, dot3, dx1, dx2, dx3, dy1, dy2, dy3, dz1, dz2, dz3, face_count, fnx, fny, fnz, i, l, nx1, nx2, nx3, ny1, ny2, ny3, nz1, nz2, nz3, probe, px, py, pz, result, start, tx, ty, tz, u1, u2, u3, v1, v2, v3, vali, vertex_count, verti, vertices, x1, x2, x3, y1, y2, y3, z1, z2, z3, _i, _j, _len; this.gl = gl; BounceModel.__super__.constructor.call(this); start = gettime(); vertices = model.vertices; vertex_count = vertices.length / 8; face_count = vertex_count / 3; result = []; for (i = _i = 0; 0 <= face_count ? _i < face_count : _i > face_count; i = 0 <= face_count ? ++_i : --_i) { verti = i * 3; vali = verti * 8; x1 = vertices[vali + 0]; y1 = vertices[vali + 1]; z1 = vertices[vali + 2]; u1 = vertices[vali + 3]; v1 = vertices[vali + 4]; nx1 = vertices[vali + 5]; ny1 = vertices[vali + 6]; nz1 = vertices[vali + 7]; vali = verti * 8 + 8; x2 = vertices[vali + 0]; y2 = vertices[vali + 1]; z2 = vertices[vali + 2]; u2 = vertices[vali + 3]; v2 = vertices[vali + 4]; nx2 = vertices[vali + 5]; ny2 = vertices[vali + 6]; nz2 = vertices[vali + 7]; vali = verti * 8 + 16; x3 = vertices[vali + 0]; y3 = vertices[vali + 1]; z3 = vertices[vali + 2]; u3 = vertices[vali + 3]; v3 = vertices[vali + 4]; nx3 = vertices[vali + 5]; ny3 = vertices[vali + 6]; nz3 = vertices[vali + 7]; for (i = _j = 0, _len = probes.length; _j < _len; i = ++_j) { probe = probes[i]; px = probe.x; py = probe.y; pz = probe.z; dx1 = px - x1; dy1 = py - y1; dz1 = pz - z1; l = Math.sqrt(dx1 * dx1 + dy1 * dy1 + dz1 * dz1); dx1 /= l; dy1 /= l; dz1 /= l; dot1 = dx1 * nx1 + dy1 * ny1 + dz1 * nz1; dx2 = px - x2; dy2 = py - y2; dz2 = pz - z2; l = Math.sqrt(dx2 * dx2 + dy2 * dy2 + dz2 * dz2); dx2 /= l; dy2 /= l; dz2 /= l; dot2 = dx2 * nx2 + dy2 * ny2 + dz2 * nz2; dx3 = px - x3; dy3 = py - y3; dz3 = pz - z3; l = Math.sqrt(dx3 * dx3 + dy3 * dy3 + dz3 * dz3); dx3 /= l; dy3 /= l; dz3 /= l; dot3 = dx3 * nx3 + dy3 * ny3 + dz3 * nz3; tx = x2 - x1; ty = y2 - y1; tz = z2 - z1; btx = x3 - x1; bty = y3 - y1; btz = z3 - z1; fnx = ty * btz - tz * bty; fny = tz * btx - tx * btz; fnz = tx * bty - ty * btx; l = Math.sqrt(fnx * fnx + fny * fny + fnz * fnz); fnx /= l; fny /= l; fnz /= l; det = fnx * x1 + fny * y1 + fnz * z1; dist = Math.abs((fnx * px + fny * py + fnz * pz) - det); if ((dot1 >= 0 || dot2 >= 0 || dot3 >= 0) && dist <= 5.0) { if (pointTriangleDist([px, py, pz], [x1, y1, z1], [x2, y2, z2], [x3, y3, z3]) <= 5.0) { result.push(x1, y1, z1, u1, v1, nx1, ny1, nz1, px, py, pz, i, x2, y2, z2, u2, v2, nx2, ny2, nz2, px, py, pz, i, x3, y3, z3, u3, v3, nx3, ny3, nz3, px, py, pz, i); } } } } this.size = result.length / 12; this.uploadList(result); } return BounceModel; })(require('/webgl/drawable')); }); loader.define('/ssao/module.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Blur, Rendernode, SSAO; Rendernode = require('/rendernode'); Blur = require('/blur'); return SSAO = (function() { function SSAO(gl, normaldepth) { var floatExt; this.normaldepth = normaldepth; floatExt = gl.getFloatExtension({ require: ['renderable', 'filterable'] }); this.moments = new Rendernode(gl, { program: get('moments.shader'), type: floatExt.type, drawable: quad }); this.blur = new Blur(gl, { type: floatExt.type }); this.output = new Rendernode(gl, { program: get('ssao.shader'), drawable: quad }); } SSAO.prototype.update = function() { this.moments.start().sampler('normaldepth', this.normaldepth).f('range', 42).clear().draw().end(); this.blur.update(this.moments); return this.output.start().sampler('normaldepth', this.normaldepth).sampler('momentsmap', this.blur.output).f('range', 42).clear().draw().end(); }; SSAO.prototype.resize = function(width, height) { this.moments.resize(width / 2, height / 2); this.blur.resize(width / 4, height / 4); return this.output.resize(width, height); }; return SSAO; })(); }); loader.define('/blur/module.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Blur, Rendernode; Rendernode = require('/rendernode'); return Blur = (function() { function Blur(gl, _arg) { var filter, height, type, width; width = _arg.width, height = _arg.height, type = _arg.type, filter = _arg.filter; if (type == null) { type = gl.UNSIGNED_BYTE; } if (filter == null) { filter = 'linear'; } this.output = new Rendernode(gl, { width: width, height: height, program: get('blur.shader'), drawable: quad, filter: filter, type: type }); } Blur.prototype.update = function(source) { return this.output.start().sampler('source', source).draw().end(); }; Blur.prototype.resize = function(width, height) { return this.output.resize(width, height); }; return Blur; })(); }); loader.define('/model/module.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var LowresModel, Materials, Model, Texture2D, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Texture2D = require('/webgl/texture').Texture2D; Materials = Materials = (function() { Materials.prototype.createTexture = function(path) { var image, texture; texture = this.texture_cache[path]; if (!texture) { image = get(path); texture = new Texture2D(this.gl).bind().upload(image).mipmap().repeat().unbind(); this.texture_cache[path] = texture; } return texture; }; function Materials(gl) { var definition, diffuse, jpgbump, luma, pngbump, specular, _i, _len, _ref; this.gl = gl; this.texture_cache = {}; this.definitions = get('materials.json'); this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, true); _ref = this.definitions; for (_i = 0, _len = _ref.length; _i < _len; _i++) { definition = _ref[_i]; diffuse = "diffuse/" + definition.diffuse_texture; jpgbump = "bump/" + definition.bumpmap; pngbump = jpgbump.replace('.jpg', '.png'); definition.diffuse_texture = this.createTexture(diffuse); specular = definition.specular_color; luma = (specular.r + specular.g + specular.b) / 3; definition.specularity = luma * definition.specularity; if (get.exists(pngbump)) { definition.bumpmap = this.createTexture(pngbump); } else if (get.exists(jpgbump)) { definition.bumpmap = this.createTexture(jpgbump); } else { definition.bumpmap = definition.diffuse_texture; } } this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); this.diffuse_texture = this.sortById('diffuse_texture'); this.bumpmap = this.sortById('bumpmap'); } Materials.prototype.sortById = function(type) { var definition, result; result = (function() { var _i, _len, _ref, _results; _ref = this.definitions; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { definition = _ref[_i]; _results.push(definition); } return _results; }).call(this); result.sort(function(a, b) { return a[type].id - b[type].id; }); return result; }; return Materials; })(); exports.LowresModel = LowresModel = (function(_super) { __extends(LowresModel, _super); LowresModel.prototype.attribs = ['position', 'texcoord', 'normal']; LowresModel.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 8 }, { name: 'texcoord', size: 2, offset: 3, stride: 8 }, { name: 'normal', size: 3, offset: 5, stride: 8 } ]; function LowresModel(gl) { this.gl = gl; LowresModel.__super__.constructor.call(this); this.vertices = new Float32Array(get('lowres.vertices')); this.size = this.vertices.length / 8; this.upload(this.vertices); } return LowresModel; })(require('/webgl/drawable')); exports.Model = Model = (function(_super) { __extends(Model, _super); Model.prototype.attribs = ['position', 'texcoord', 'normal']; Model.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 8 }, { name: 'texcoord', size: 2, offset: 3, stride: 8 }, { name: 'normal', size: 3, offset: 5, stride: 8 } ]; function Model(gl) { var buffer, indices, vertices; this.gl = gl; Model.__super__.constructor.call(this); this.materials = new Materials(this.gl); indices = new Uint16Array(get('sponza.indices')); vertices = new Float32Array(get('sponza.vertices')); this.size = indices.length; this.max_angle = Math.cos(Math.PI * 2 * (43 / 360)); this.computeVertexFaces(indices, vertices); this.computeFaceNormals(indices, vertices); buffer = this.calculateVertices(indices, vertices); this.upload(buffer); } Model.prototype.computeVertexFaces = function(indices, vertices) { var c1, c2, c3, count, counts, end, face_count, face_index, idx1, idx2, idx3, index, iv, max, start, vertex_count, vertex_faces, _i, _j, _k, _len, _len1; start = gettime(); vertex_count = vertices.length / 5; counts = new Uint8Array(vertex_count); for (_i = 0, _len = indices.length; _i < _len; _i++) { index = indices[_i]; counts[index] += 1; } max = 0; for (_j = 0, _len1 = counts.length; _j < _len1; _j++) { count = counts[_j]; if (count > max) { max = count; } } this.max_count = max; vertex_faces = new Uint32Array(max * vertex_count); counts = new Uint8Array(vertex_count); face_count = indices.length / 3; for (face_index = _k = 0; 0 <= face_count ? _k < face_count : _k > face_count; face_index = 0 <= face_count ? ++_k : --_k) { iv = face_index * 3; idx1 = indices[iv]; idx2 = indices[iv + 1]; idx3 = indices[iv + 2]; c1 = counts[idx1]++; c2 = counts[idx2]++; c3 = counts[idx3]++; vertex_faces[idx1 * max + c1] = face_index; vertex_faces[idx2 * max + c2] = face_index; vertex_faces[idx3 * max + c3] = face_index; } end = gettime(); this.vertex_face_count = counts; return this.vertex_faces = vertex_faces; }; Model.prototype.computeFaceNormals = function(indices, vertices) { var btx, bty, btz, end, face_count, i, i1, i2, i3, iv, l, normals, nx, ny, nz, start, tx, ty, tz, x1, x2, x3, y1, y2, y3, z1, z2, z3, _i; start = gettime(); face_count = indices.length / 3; normals = new Float32Array(face_count * 3); for (i = _i = 0; 0 <= face_count ? _i < face_count : _i > face_count; i = 0 <= face_count ? ++_i : --_i) { iv = i * 3; i1 = indices[iv]; i2 = indices[iv + 1]; i3 = indices[iv + 2]; x1 = vertices[i1 * 5]; y1 = vertices[i1 * 5 + 1]; z1 = vertices[i1 * 5 + 2]; x2 = vertices[i2 * 5]; y2 = vertices[i2 * 5 + 1]; z2 = vertices[i2 * 5 + 2]; x3 = vertices[i3 * 5]; y3 = vertices[i3 * 5 + 1]; z3 = vertices[i3 * 5 + 2]; tx = x2 - x1; ty = y2 - y1; tz = z2 - z1; btx = x3 - x1; bty = y3 - y1; btz = z3 - z1; nx = ty * btz - tz * bty; ny = tz * btx - tx * btz; nz = tx * bty - ty * btx; l = Math.sqrt(nx * nx + ny * ny + nz * nz); nx /= l; ny /= l; nz /= l; normals[iv + 0] = nx; normals[iv + 1] = ny; normals[iv + 2] = nz; } this.normals = normals; return end = gettime(); }; Model.prototype.getNormal = function(face_index, vertex_index) { var c, cos, l, nx, ny, nz, rx, ry, rz, vfidx, x, y, z, _i, _ref; rx = this.normals[face_index * 3 + 0]; ry = this.normals[face_index * 3 + 1]; rz = this.normals[face_index * 3 + 2]; nx = 0; ny = 0; nz = 0; for (c = _i = 0, _ref = this.vertex_face_count[vertex_index]; 0 <= _ref ? _i < _ref : _i > _ref; c = 0 <= _ref ? ++_i : --_i) { vfidx = this.vertex_faces[vertex_index * this.max_count + c]; x = this.normals[vfidx * 3 + 0]; y = this.normals[vfidx * 3 + 1]; z = this.normals[vfidx * 3 + 2]; cos = rx * x + ry * y + rz * z; if (cos > this.max_angle) { nx = x; ny = y; nz = z; } } l = Math.sqrt(nx * nx + ny * ny + nz * nz); return [nx / l, ny / l, nz / l]; }; Model.prototype.calculateVertices = function(indices, vertices, normals) { var end, i, i1, i2, i3, iv, nx, ny, nz, result, start, u1, u2, u3, v1, v2, v3, x1, x2, x3, y1, y2, y3, z1, z2, z3, _i, _ref, _ref1, _ref2, _ref3; start = gettime(); result = new Float32Array(indices.length * 8); for (i = _i = 0, _ref = indices.length / 3; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { iv = i * 3; i1 = indices[iv]; i2 = indices[iv + 1]; i3 = indices[iv + 2]; x1 = vertices[i1 * 5]; y1 = vertices[i1 * 5 + 1]; z1 = vertices[i1 * 5 + 2]; u1 = vertices[i1 * 5 + 3]; v1 = vertices[i1 * 5 + 4]; x2 = vertices[i2 * 5]; y2 = vertices[i2 * 5 + 1]; z2 = vertices[i2 * 5 + 2]; u2 = vertices[i2 * 5 + 3]; v2 = vertices[i2 * 5 + 4]; x3 = vertices[i3 * 5]; y3 = vertices[i3 * 5 + 1]; z3 = vertices[i3 * 5 + 2]; u3 = vertices[i3 * 5 + 3]; v3 = vertices[i3 * 5 + 4]; _ref1 = this.getNormal(i, i1), nx = _ref1[0], ny = _ref1[1], nz = _ref1[2]; result[(iv + 0) * 8 + 0] = x1; result[(iv + 0) * 8 + 1] = y1; result[(iv + 0) * 8 + 2] = z1; result[(iv + 0) * 8 + 3] = u1; result[(iv + 0) * 8 + 4] = v1; result[(iv + 0) * 8 + 5] = nx; result[(iv + 0) * 8 + 6] = ny; result[(iv + 0) * 8 + 7] = nz; _ref2 = this.getNormal(i, i2), nx = _ref2[0], ny = _ref2[1], nz = _ref2[2]; result[(iv + 1) * 8 + 0] = x2; result[(iv + 1) * 8 + 1] = y2; result[(iv + 1) * 8 + 2] = z2; result[(iv + 1) * 8 + 3] = u2; result[(iv + 1) * 8 + 4] = v2; result[(iv + 1) * 8 + 5] = nx; result[(iv + 1) * 8 + 6] = ny; result[(iv + 1) * 8 + 7] = nz; _ref3 = this.getNormal(i, i3), nx = _ref3[0], ny = _ref3[1], nz = _ref3[2]; result[(iv + 2) * 8 + 0] = x3; result[(iv + 2) * 8 + 1] = y3; result[(iv + 2) * 8 + 2] = z3; result[(iv + 2) * 8 + 3] = u3; result[(iv + 2) * 8 + 4] = v3; result[(iv + 2) * 8 + 5] = nx; result[(iv + 2) * 8 + 6] = ny; result[(iv + 2) * 8 + 7] = nz; } end = gettime(); return result; }; 'draw: (shader) ->\n if shader then @setPointersForShader shader\n for material in @materials\n #@gl.drawArrays @mode, @first, @size\n @gl.drawArrays @mode, material.start, material.size\n if shader then @disableAttribs shader\n return @'; return Model; })(require('/webgl/drawable')); }); loader.define('/webgl-nuke-vendor-prefix.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var getExtension, getSupportedExtensions, vendorRe, vendors, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; if (window.WebGLRenderingContext != null) { vendors = ['WEBKIT', 'MOZ', 'MS', 'O']; vendorRe = /^WEBKIT_(.*)|MOZ_(.*)|MS_(.*)|O_(.*)/; getExtension = WebGLRenderingContext.prototype.getExtension; WebGLRenderingContext.prototype.getExtension = function(name) { var extobj, match, vendor, _i, _len; match = name.match(vendorRe); if (match !== null) { name = match[1]; } extobj = getExtension.call(this, name); if (extobj === null) { for (_i = 0, _len = vendors.length; _i < _len; _i++) { vendor = vendors[_i]; extobj = getExtension.call(this, vendor + '_' + name); if (extobj !== null) { return extobj; } } return null; } else { return extobj; } }; getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions; WebGLRenderingContext.prototype.getSupportedExtensions = function() { var extension, match, result, supported, _i, _len; supported = getSupportedExtensions.call(this); result = []; for (_i = 0, _len = supported.length; _i < _len; _i++) { extension = supported[_i]; match = extension.match(vendorRe); if (match !== null) { extension = match[1]; } if (__indexOf.call(result, extension) < 0) { result.push(extension); } } return result; }; } }); loader.define('/loading.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var bar, container, hidden, label, loading, makeBar, ui; ui = $('#ui'); hidden = true; container = $('
    ').css({ position: 'absolute', top: '50%', left: '50%', width: 200, height: 40, marginLeft: -100, marginTop: -20 }); label = $('
    ').appendTo(container).css({ position: 'absolute', top: 0, left: 0, width: 200, height: 20, textAlign: 'center', color: 'white' }); loading = $('
    ').appendTo(container).css({ position: 'absolute', top: 20, left: 0, width: 200, height: 20, border: '1px solid white' }); bar = null; makeBar = function() { loading.empty(); return bar = $('
    ').appendTo(loading).css({ position: 'absolute', top: 0, left: 0, width: 0, height: 20, backgroundColor: 'white', '-webkit-transition': 'width 0.7s' }); }; exports.show = function(text) { label.text(text); makeBar(); return container.fadeIn('slow').appendTo(ui); }; exports.hide = function() { return container.fadeOut('slow', function() { return container.detach(); }); }; exports.progress = function(factor) { return bar.width(factor * 200); }; }); loader.define('/geometry.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 exports.AABB = (function() { function AABB(xmin, xmax, ymin, ymax, zmin, zmax) { this.xmin = xmin; this.xmax = xmax; this.ymin = ymin; this.ymax = ymax; this.zmin = zmin; this.zmax = zmax; } AABB.prototype.ray_intersect = function(ray) { var d, inv_x, inv_y, inv_z, o, tmax, tmin, ymax, ymin, zmax, zmin, _ref, _ref1, _ref2; o = ray.origin; d = ray.direction; inv_x = 1.0 / d.x; tmin = (this.xmin - o.x) * inv_x; tmax = (this.xmax - o.x) * inv_x; if (inv_x < 0) { _ref = [tmax, tmin], tmin = _ref[0], tmax = _ref[1]; } inv_y = 1.0 / d.y; ymin = (this.ymin - o.y) * inv_y; ymax = (this.ymax - o.y) * inv_y; if (inv_y < 0) { _ref1 = [ymax, ymin], ymin = _ref1[0], ymax = _ref1[1]; } if (tmin > ymax || ymin > tmax) { return null; } if (ymin > tmin) { tmin = ymin; } if (ymax < tmax) { tmax = ymax; } inv_z = 1.0 / d.z; zmin = (this.zmin - o.z) * inv_z; zmax = (this.zmax - o.z) * inv_z; if (inv_z < 0) { _ref2 = [zmax, zmin], zmin = _ref2[0], zmax = _ref2[1]; } if (tmin > zmax || zmin > tmax) { return null; } if (zmin > tmin) { tmin = zmin; } if (zmax < tmax) { tmax = zmax; } return [tmin, tmax]; }; return AABB; })(); exports.Ray = (function() { function Ray(origin, direction) { var _ref, _ref1; this.origin = origin; this.direction = direction; if ((_ref = this.origin) == null) { this.origin = new Vec4(); } if ((_ref1 = this.direction) == null) { this.direction = new Vec4(); } } Ray.prototype.interpolate = function(interval, vector) { var d, o, v; if (vector == null) { vector = new Vec4(); } o = this.origin; d = this.direction; v = vector; v.x = o.x + d.x * interval; v.y = o.y + d.y * interval; v.z = o.z + d.z * interval; v.w = o.w + d.w * interval; return vector; }; Ray.prototype.ray_nearest = function(ray) { var U, V, W, a, b, c, d, det, e, s, t; W = this.origin.sub(ray.origin, new Vec4); U = this.direction; V = ray.direction; a = U.dot(U); b = U.dot(V); c = V.dot(V); d = U.dot(W); e = V.dot(W); det = a * c - b * b; if (det === 0) { return null; } s = (b * e - c * d) / det; t = (a * e - b * d) / det; return [s, t]; }; Ray.prototype.point_distance = function(point) { var W, s; W = point.sub(this.origin, new Vec4); s = W.dot(this.direction) / this.direction.dot(this.direction); W.x -= this.direction.x * s; W.y -= this.direction.y * s; W.z -= this.direction.z * s; return Math.sqrt(W.dot(W)); }; return Ray; })(); exports.get_mouseray = function(x, y, inv_proj, inv_view, ray) { if (ray == null) { ray = new exports.Ray; } inv_proj.mulVal4(x, y, -1, 1, ray.direction); inv_view.mulVec3(ray.direction); ray.direction.w = 0; inv_view.mulVal4(0, 0, 0, 1, ray.origin); return ray; }; }); loader.define('/keys.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var key_handlers, keymap, keys; keymap = { 87: 'w', 65: 'a', 83: 's', 68: 'd', 81: 'q', 69: 'e', 37: 'left', 39: 'right', 38: 'up', 40: 'down', 13: 'enter', 27: 'esc', 32: 'space', 8: 'backspace', 16: 'shift', 17: 'ctrl', 18: 'alt', 91: 'start', 0: 'altc', 20: 'caps', 9: 'tab', 49: 'key1', 50: 'key2', 51: 'key3', 52: 'key4' }; key_handlers = {}; keys = { press: function(name, callback) { var handlers; handlers = key_handlers[name] = key_handlers[name] || []; return handlers.push(callback); } }; $(document).keydown(function(event) { var handler, handlers, name, _i, _len, _results; if (event.target === document.body) { name = keymap[event.which]; keys[name] = true; handlers = key_handlers[name]; if (handlers) { _results = []; for (_i = 0, _len = handlers.length; _i < _len; _i++) { handler = handlers[_i]; _results.push(handler()); } return _results; } } }); $(document).keyup(function(event) { var name; name = keymap[event.which]; return keys[name] = false; }); return keys; }); loader.define('/camera.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Camera, FlyCam, GameCam, MouseDrag, Orbit, keys, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; keys = require('keys'); MouseDrag = (function() { function MouseDrag(which) { var _this = this; this.which = which; this.x = 0; this.y = 0; this.lx = 0; this.ly = 0; this.pressed = false; if (navigator.appVersion.indexOf('Mac') !== -1) { $('#ui').bind('mousewheel', function(event) { event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); _this.x += event.originalEvent.wheelDeltaX * 0.25; _this.y += event.originalEvent.wheelDeltaY * 0.25; return false; }); } $('#ui').mousedown(function(event) { if (event.which === _this.which) { _this.lx = event.pageX; _this.ly = event.pageY; _this.pressed = true; } return void 0; }); $(document).mouseup(function() { _this.pressed = false; return void 0; }); $(document).mousemove(function(event) { var x, y; if (_this.pressed && event.which === _this.which) { x = event.pageX; y = event.pageY; _this.x += x - _this.lx; _this.y += y - _this.ly; _this.lx = x; _this.ly = y; return false; } return void 0; }); } MouseDrag.prototype.reset = function() { this.x = 0; return this.y = 0; }; return MouseDrag; })(); Camera = (function() { function Camera(delta, near, far) { this.delta = delta != null ? delta : 1 / 180; this.near = near != null ? near : 0.1; this.far = far != null ? far : 1000; this.last_gui_update = gettime(); this.time = gettime(); this.proj = new Mat4(); this.inv_proj = new Mat4(); this.view = new Mat4(); this.inv_view = new Mat4(); this.rot = new Mat3(); this.inv_rot = new Mat3(); this.acc = new Vec3(); } Camera.prototype.aspect = function(width, height) { this.proj.perspective(75, width / height, this.near, this.far); return this.inv_proj.inversePerspective(75, width / height, this.near, this.far); }; Camera.prototype.step = function() { this.accelerate(); this.limit(); this.move(); this.limit(); return this.time += this.delta; }; Camera.prototype.update = function() { var now; now = gettime(); if (now - this.last_gui_update > 0.5) { this.guiUpdate(); this.last_gui_update = now; } if (now - this.time > this.delta * 30) { this.time = now - this.delta * 30; } while (this.time < now) { this.step(); } this.finish(); this.view.invert(this.inv_view.identity()); this.view.toMat3(this.rot.identity()); return this.inv_view.toMat3(this.inv_rot.identity()); }; Camera.prototype.limit = function() {}; Camera.prototype.guiUpdate = function() {}; return Camera; })(); exports.GameCam = GameCam = (function(_super) { __extends(GameCam, _super); function GameCam(_arg) { var delta, x, y, z, _ref, _ref1, _ref2, _this = this; _ref = _arg != null ? _arg : {}, this.sl = _ref.sl, this.sr = _ref.sr, delta = _ref.delta, x = _ref.x, y = _ref.y, z = _ref.z; GameCam.__super__.constructor.call(this, delta); this.realpos = new Vec4(); if ((_ref1 = this.sl) == null) { this.sl = 200; } if ((_ref2 = this.sr) == null) { this.sr = 100; } if (x == null) { x = 0; } if (y == null) { y = 0; } if (z == null) { z = 0; } this.mouse = new MouseDrag(3); this.target_height = 0; this.height = 0; this.x = x; this.lx = x; this.z = z; this.lz = z; this.o = 0; this.lo = 0; this.d = 0; this.ld = 0; this.ad = 0; $(document).bind('mousewheel', function(event) { event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); _this.ad -= event.originalEvent.wheelDeltaY; return false; }); } GameCam.prototype.accelerate = function() { var ax, az, ctrl_x, ctrl_y, ctrl_z, move, sl, sr; sl = this.delta * this.delta * this.sl; sr = this.delta * this.delta * this.sr; ctrl_x = keys.a ? -1 : keys.d ? 1 : 0; ctrl_y = keys.q ? -1 : keys.e ? 1 : 0; ctrl_z = keys.w ? -1 : keys.s ? 1 : 0; ax = ctrl_x * sl; az = ctrl_z * sl; this.rot.identity().rotatey(-this.o).mulVal3(ax, 0, az, this.acc); this.x += this.acc.x; this.z += this.acc.z; this.o += this.mouse.x * sr; this.d += this.ad * this.delta * this.delta * 20; move = this.delta * this.delta * 4000; if (move > 1) { move = 1; } return this.height = this.height + (this.target_height - this.height) * move; }; GameCam.prototype.move = function() { var d, o, retl, retr, x, z; retl = 0.97; retr = 0.94; x = this.x + (this.x - this.lx) * retl; z = this.z + (this.z - this.lz) * retl; d = this.d + (this.d - this.ld) * retl; o = this.o + (this.o - this.lo) * retr; this.lx = this.x; this.x = x; this.lz = this.z; this.z = z; this.lo = this.o; this.o = o; this.ld = this.d; return this.d = d; }; GameCam.prototype.limit = function() { var high, low; if (this.d < 0) { this.d = 0; } else if (this.d > 30) { this.d = 30; } high = 128 + 64; low = 128 - 64; if (this.x < low) { this.x = low; } else if (this.x > high) { this.x = high; } if (this.z < low) { return this.z = low; } else if (this.z > high) { return this.z = high; } }; GameCam.prototype.finish = function() { this.mouse.reset(); this.ad = 0; return this.view.identity().translateVal3(0, 0, -this.d - 5).rotatex(25 + (this.d / 30) * 40).rotatey(this.o).translateVal3(-this.x, -this.height, -this.z); }; GameCam.prototype.update = function(picker) { var diff, h1, h2, real_height; this.view.identity().translateVal3(0, 0, -this.d - 5).rotatex(25 + (this.d / 30) * 40).rotatey(this.o).translateVal3(-this.x, 0, -this.z); this.view.invert(this.inv_view.identity()); h1 = picker.getHeight(this.x, this.z); this.inv_view.mulVal4(0, 0, 0, 1, this.realpos); h2 = picker.getHeight(this.realpos.x, this.realpos.z) + 2; real_height = h1 + this.realpos.y; if (real_height < h2) { diff = h2 - real_height; this.target_height = h1 + diff; } else { this.target_height = h1; } return GameCam.__super__.update.call(this); }; return GameCam; })(Camera); exports.Orbit = Orbit = (function(_super) { __extends(Orbit, _super); function Orbit(_arg) { var delta, x, y, z, _ref, _ref1, _ref2; _ref = _arg != null ? _arg : {}, this.sr = _ref.sr, delta = _ref.delta, x = _ref.x, y = _ref.y, z = _ref.z, this.dist = _ref.dist; Orbit.__super__.constructor.call(this, delta); if ((_ref1 = this.sr) == null) { this.sr = 100; } if ((_ref2 = this.dist) == null) { this.dist = 0.6; } this.mouse = new MouseDrag(1); this.o = 0; this.lo = 0; this.p = 0; this.lp = 0; } Orbit.prototype.accelerate = function() { var sr; sr = this.delta * this.delta * this.sr; this.o += this.mouse.x * sr; return this.p += this.mouse.y * sr; }; Orbit.prototype.move = function() { var o, p, retr; retr = 0.94; o = this.o + (this.o - this.lo) * retr; p = this.p + (this.p - this.lp) * retr; this.lo = this.o; this.o = o; this.lp = this.p; return this.p = p; }; Orbit.prototype.finish = function() { return this.mouse.reset(); }; Orbit.prototype.update = function() { this.view.identity().translateVal3(0, 0, -this.dist).rotatex(this.p).rotatey(this.o); this.view.invert(this.inv_view.identity()); return Orbit.__super__.update.call(this); }; return Orbit; })(Camera); exports.FlyCam = FlyCam = (function(_super) { __extends(FlyCam, _super); function FlyCam(_arg) { var delta, far, folder, lookbutton, near, o, p, x, y, z, _ref, _ref1, _ref2; _ref = _arg != null ? _arg : {}, this.sl = _ref.sl, this.gui = _ref.gui, this.sr = _ref.sr, delta = _ref.delta, near = _ref.near, far = _ref.far, lookbutton = _ref.lookbutton, x = _ref.x, y = _ref.y, z = _ref.z, o = _ref.o, p = _ref.p; this.guiChanged = __bind(this.guiChanged, this); FlyCam.__super__.constructor.call(this, delta, near, far); if ((_ref1 = this.sl) == null) { this.sl = 50; } if ((_ref2 = this.sr) == null) { this.sr = 100; } if (lookbutton == null) { lookbutton = 1; } if (x == null) { x = 0; } if (y == null) { y = 0; } if (z == null) { z = 0; } if (o == null) { o = 0; } if (p == null) { p = 0; } this.mouse = new MouseDrag(lookbutton); this.x = x; this.lx = x; this.y = y; this.ly = y; this.z = z; this.lz = z; this.o = o; this.lo = o; this.p = p; this.lp = p; folder = this.gui.addFolder('Camera'); this.gui.remember(this); this.xgui = folder.add(this, 'x', -30.0, 30.0).onChange(this.guiChanged); this.ygui = folder.add(this, 'y', -30.0, 30.0).onChange(this.guiChanged); this.zgui = folder.add(this, 'z', -30.0, 30.0).onChange(this.guiChanged); this.go = this.o; this.ogui = folder.add(this, 'go', 0.0, 360.0).name('Orientation').onChange(this.guiChanged); this.pgui = folder.add(this, 'p', -80.0, 80.0).name('Pitch').onChange(this.guiChanged); this.guiChanged(); } FlyCam.prototype.guiChanged = function() { this.lx = this.x; this.ly = this.y; this.lz = this.z; this.o = this.go; this.lo = this.go; return this.lp = this.p; }; FlyCam.prototype.guiUpdate = function() { this.go = this.o % 360; this.xgui.updateDisplay(); this.ygui.updateDisplay(); this.zgui.updateDisplay(); this.ogui.updateDisplay(); return this.pgui.updateDisplay(); }; FlyCam.prototype.accelerate = function() { var ax, ay, az, ctrl_x, ctrl_y, ctrl_z, sl, sr; sl = this.delta * this.delta * this.sl; sr = this.delta * this.delta * this.sr; ctrl_x = keys.a ? -1 : keys.d ? 1 : 0; ctrl_y = keys.q ? -1 : keys.e ? 1 : 0; ctrl_z = keys.w ? -1 : keys.s ? 1 : 0; ax = ctrl_x * sl; ay = ctrl_y * sl; az = ctrl_z * sl; this.rot.identity().rotatey(-this.o).rotatex(-this.p).mulVal3(ax, ay, az, this.acc); this.x += this.acc.x; this.y += this.acc.y; this.z += this.acc.z; this.o += this.mouse.x * sr; return this.p += this.mouse.y * sr; }; FlyCam.prototype.move = function() { var o, p, retl, retr, x, y, z; retl = 0.97; retr = 0.94; x = this.x + (this.x - this.lx) * retl; y = this.y + (this.y - this.ly) * retl; z = this.z + (this.z - this.lz) * retl; o = this.o + (this.o - this.lo) * retr; p = this.p + (this.p - this.lp) * retr; if (p > 80) { p = 80; } else if (p < -80) { p = -80; } this.lx = this.x; this.x = x; this.ly = this.y; this.y = y; this.lz = this.z; this.z = z; this.lo = this.o; this.o = o; this.lp = this.p; return this.p = p; }; FlyCam.prototype.finish = function() { this.mouse.reset(); return this.view.identity().rotatex(this.p).rotatey(this.o).translateVal3(-this.x, -this.y, -this.z); }; return FlyCam; })(Camera); }); loader.define('/webgl-texture-float-extension-shims.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var checkColorBuffer, checkFloatLinear, checkSupport, checkTexture, createSourceCanvas, getExtension, getSupportedExtensions, name, shimExtensions, shimLookup, unshimExtensions, unshimLookup, _i, _len, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; createSourceCanvas = function() { var canvas, ctx, imageData; canvas = document.createElement('canvas'); canvas.width = 2; canvas.height = 2; ctx = canvas.getContext('2d'); imageData = ctx.getImageData(0, 0, 2, 2); imageData.data.set(new Uint8ClampedArray([0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255])); ctx.putImageData(imageData, 0, 0); return canvas; }; createSourceCanvas(); checkFloatLinear = function(gl, sourceType) { var buffer, cleanup, fragmentShader, framebuffer, positionLoc, program, readBuffer, result, source, sourceCanvas, sourceLoc, target, vertexShader, vertices; program = gl.createProgram(); vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.attachShader(program, vertexShader); gl.shaderSource(vertexShader, 'attribute vec2 position;\nvoid main(){\n gl_Position = vec4(position, 0.0, 1.0);\n}'); gl.compileShader(vertexShader); if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { throw gl.getShaderInfoLog(vertexShader); } fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.attachShader(program, fragmentShader); gl.shaderSource(fragmentShader, 'uniform sampler2D source;\nvoid main(){\n gl_FragColor = texture2D(source, vec2(1.0, 1.0));\n}'); gl.compileShader(fragmentShader); if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { throw gl.getShaderInfoLog(fragmentShader); } gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { throw gl.getProgramInfoLog(program); } gl.useProgram(program); cleanup = function() { gl.deleteShader(fragmentShader); gl.deleteShader(vertexShader); gl.deleteProgram(program); gl.deleteBuffer(buffer); gl.deleteTexture(source); gl.deleteTexture(target); gl.deleteFramebuffer(framebuffer); gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.useProgram(null); gl.bindTexture(gl.TEXTURE_2D, null); return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }; target = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, target); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); framebuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0); sourceCanvas = createSourceCanvas(); source = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, source); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, sourceType, sourceCanvas); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); vertices = new Float32Array([1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1]); buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); positionLoc = gl.getAttribLocation(program, 'position'); sourceLoc = gl.getUniformLocation(program, 'source'); gl.enableVertexAttribArray(positionLoc); gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); gl.uniform1i(sourceLoc, 0); gl.drawArrays(gl.TRIANGLES, 0, 6); readBuffer = new Uint8Array(4 * 4); gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer); result = Math.abs(readBuffer[0] - 127) < 10; cleanup(); return result; }; checkTexture = function(gl, targetType) { var target; target = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, target); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null); if (gl.getError() === 0) { gl.deleteTexture(target); return true; } else { gl.deleteTexture(target); return false; } }; checkColorBuffer = function(gl, targetType) { var check, framebuffer, target; target = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, target); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null); framebuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0); check = gl.checkFramebufferStatus(gl.FRAMEBUFFER); gl.deleteTexture(target); gl.deleteFramebuffer(framebuffer); gl.bindTexture(gl.TEXTURE_2D, null); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (check === gl.FRAMEBUFFER_COMPLETE) { return true; } else { return false; } }; shimExtensions = []; shimLookup = {}; unshimExtensions = []; checkSupport = function() { var canvas, extobj, gl, halfFloatExt, halfFloatTexturing, singleFloatExt, singleFloatTexturing; canvas = document.createElement('canvas'); gl = null; try { gl = canvas.getContext('experimental-webgl'); if (gl === null) { gl = canvas.getContext('webgl'); } } catch (_error) {} if (gl != null) { singleFloatExt = gl.getExtension('OES_texture_float'); if (singleFloatExt === null) { if (checkTexture(gl, gl.FLOAT)) { singleFloatTexturing = true; shimExtensions.push('OES_texture_float'); shimLookup.OES_texture_float = { shim: true }; } else { singleFloatTexturing = false; unshimExtensions.push('OES_texture_float'); } } else { if (checkTexture(gl, gl.FLOAT)) { singleFloatTexturing = true; shimExtensions.push('OES_texture_float'); } else { singleFloatTexturing = false; unshimExtensions.push('OES_texture_float'); } } if (singleFloatTexturing) { extobj = gl.getExtension('WEBGL_color_buffer_float'); if (extobj === null) { if (checkColorBuffer(gl, gl.FLOAT)) { shimExtensions.push('WEBGL_color_buffer_float'); shimLookup.WEBGL_color_buffer_float = { shim: true, RGBA32F_EXT: 0x8814, RGB32F_EXT: 0x8815, FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211, UNSIGNED_NORMALIZED_EXT: 0x8C17 }; } else { unshimExtensions.push('WEBGL_color_buffer_float'); } } else { if (checkColorBuffer(gl, gl.FLOAT)) { shimExtensions.push('WEBGL_color_buffer_float'); } else { unshimExtensions.push('WEBGL_color_buffer_float'); } } extobj = gl.getExtension('OES_texture_float_linear'); if (extobj === null) { if (checkFloatLinear(gl, gl.FLOAT)) { shimExtensions.push('OES_texture_float_linear'); shimLookup.OES_texture_float_linear = { shim: true }; } else { unshimExtensions.push('OES_texture_float_linear'); } } else { if (checkFloatLinear(gl, gl.FLOAT)) { shimExtensions.push('OES_texture_float_linear'); } else { unshimExtensions.push('OES_texture_float_linear'); } } } halfFloatExt = gl.getExtension('OES_texture_half_float'); if (halfFloatExt === null) { if (checkTexture(gl, 0x8D61)) { halfFloatTexturing = true; shimExtensions.push('OES_texture_half_float'); halfFloatExt = shimLookup.OES_texture_half_float = { HALF_FLOAT_OES: 0x8D61, shim: true }; } else { halfFloatTexturing = false; unshimExtensions.push('OES_texture_half_float'); } } else { if (checkTexture(gl, halfFloatExt.HALF_FLOAT_OES)) { halfFloatTexturing = true; shimExtensions.push('OES_texture_half_float'); } else { halfFloatTexturing = false; unshimExtensions.push('OES_texture_half_float'); } } if (halfFloatTexturing) { extobj = gl.getExtension('EXT_color_buffer_half_float'); if (extobj === null) { if (checkColorBuffer(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('EXT_color_buffer_half_float'); shimLookup.EXT_color_buffer_half_float = { shim: true, RGBA16F_EXT: 0x881A, RGB16F_EXT: 0x881B, FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211, UNSIGNED_NORMALIZED_EXT: 0x8C17 }; } else { unshimExtensions.push('EXT_color_buffer_half_float'); } } else { if (checkColorBuffer(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('EXT_color_buffer_half_float'); } else { unshimExtensions.push('EXT_color_buffer_half_float'); } } extobj = gl.getExtension('OES_texture_half_float_linear'); if (extobj === null) { if (checkFloatLinear(gl, halfFloatExt.HALF_FLOAT_OES)) { shimExtensions.push('OES_texture_half_float_linear'); return shimLookup.OES_texture_half_float_linear = { shim: true }; } else { return unshimExtensions.push('OES_texture_half_float_linear'); } } else { if (checkFloatLinear(gl, halfFloatExt.HALF_FLOAT_OES)) { return shimExtensions.push('OES_texture_half_float_linear'); } else { return unshimExtensions.push('OES_texture_half_float_linear'); } } } } }; if (window.WebGLRenderingContext != null) { checkSupport(); unshimLookup = {}; for (_i = 0, _len = unshimExtensions.length; _i < _len; _i++) { name = unshimExtensions[_i]; unshimLookup[name] = true; } getExtension = WebGLRenderingContext.prototype.getExtension; WebGLRenderingContext.prototype.getExtension = function(name) { var extobj; extobj = shimLookup[name]; if (extobj === void 0) { if (unshimLookup[name]) { return null; } else { return getExtension.call(this, name); } } else { return extobj; } }; getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions; WebGLRenderingContext.prototype.getSupportedExtensions = function() { var extension, result, supported, _j, _k, _len1, _len2; supported = getSupportedExtensions.call(this); result = []; for (_j = 0, _len1 = supported.length; _j < _len1; _j++) { extension = supported[_j]; if (unshimLookup[extension] === void 0) { result.push(extension); } } for (_k = 0, _len2 = shimExtensions.length; _k < _len2; _k++) { extension = shimExtensions[_k]; if (__indexOf.call(result, extension) < 0) { result.push(extension); } } return result; }; WebGLRenderingContext.prototype.getFloatExtension = function(spec) { var candidate, candidates, half, halfFramebuffer, halfLinear, halfTexture, i, importance, preference, result, single, singleFramebuffer, singleLinear, singleTexture, use, _j, _k, _l, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; if ((_ref = spec.prefer) == null) { spec.prefer = ['half']; } if ((_ref1 = spec.require) == null) { spec.require = []; } if ((_ref2 = spec.throws) == null) { spec.throws = true; } singleTexture = this.getExtension('OES_texture_float'); halfTexture = this.getExtension('OES_texture_half_float'); singleFramebuffer = this.getExtension('WEBGL_color_buffer_float'); halfFramebuffer = this.getExtension('EXT_color_buffer_half_float'); singleLinear = this.getExtension('OES_texture_float_linear'); halfLinear = this.getExtension('OES_texture_half_float_linear'); single = { texture: singleTexture !== null, filterable: singleLinear !== null, renderable: singleFramebuffer !== null, score: 0, precision: 'single', half: false, single: true, type: this.FLOAT }; half = { texture: halfTexture !== null, filterable: halfLinear !== null, renderable: halfFramebuffer !== null, score: 0, precision: 'half', half: true, single: false, type: (_ref3 = halfTexture != null ? halfTexture.HALF_FLOAT_OES : void 0) != null ? _ref3 : null }; candidates = []; if (single.texture) { candidates.push(single); } if (half.texture) { candidates.push(half); } result = []; for (_j = 0, _len1 = candidates.length; _j < _len1; _j++) { candidate = candidates[_j]; use = true; _ref4 = spec.require; for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { name = _ref4[_k]; if (candidate[name] === false) { use = false; } } if (use) { result.push(candidate); } } for (_l = 0, _len3 = result.length; _l < _len3; _l++) { candidate = result[_l]; _ref5 = spec.prefer; for (i = _m = 0, _len4 = _ref5.length; _m < _len4; i = ++_m) { preference = _ref5[i]; importance = Math.pow(2, spec.prefer.length - i - 1); if (candidate[preference]) { candidate.score += importance; } } } result.sort(function(a, b) { if (a.score === b.score) { return 0; } else if (a.score < b.score) { return 1; } else if (a.score > b.score) { return -1; } }); if (result.length === 0) { if (spec.throws) { throw 'No floating point texture support that is ' + spec.require.join(', '); } else { return null; } } else { result = result[0]; return { filterable: result.filterable, renderable: result.renderable, type: result.type, precision: result.precision }; } }; } }); loader.define('/audio.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Backend, HTMLAudio, WebAudio, backend, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Backend = (function() { function Backend() { this.loading = 0; this.handlers = []; } Backend.prototype.loaded = function() { var handler, _i, _len, _ref; if (this.loading === 0) { _ref = this.handlers; for (_i = 0, _len = _ref.length; _i < _len; _i++) { handler = _ref[_i]; if (handler.event === 'loaded') { handler.callback.apply(handler); } } } }; Backend.prototype.bind = function(event, callback) { var handler; handler = { event: event, callback: callback }; this.handlers.push(handler); return handler; }; Backend.prototype.unbind = function(handler) { var index; index = this.handlers.indexOf(handler); if (index >= 0) { return this.handlers.splice(index, 1); } }; return Backend; })(); HTMLAudio = (function(_super) { var Sample, Voice; __extends(HTMLAudio, _super); HTMLAudio.available = (window.Audio !== void 0) && (window.URL !== void 0) && (window.BlobBuilder !== void 0); Sample = (function() { function Sample(backend, data) { this.backend = backend; this.backend.loading += 1; this.url = blob.pack(data, 'audio/ogg'); } Sample.prototype.play = function(looping) { var voice; voice = this.backend.getFree(); if (voice) { return voice.play(this.url, looping); } }; return Sample; })(); Voice = (function() { function Voice(backend, id) { var self; this.id = id; self = this; this.audio = new Audio(); this.audio.onended = function() { return backend.ended(self); }; } Voice.prototype.play = function(url) { this.audio.src = url; return this.audio.play(); }; return Voice; })(); function HTMLAudio() { this.check = __bind(this.check, this); var id, _i; this.free = {}; this.playing = {}; for (id = _i = 0; _i < 20; id = ++_i) { this.free[id] = new Voice(this, id); } setInterval(this.check, 100); } HTMLAudio.prototype.check = function() {}; HTMLAudio.prototype.getFree = function() { var id, voice, _ref; _ref = this.free; for (id in _ref) { voice = _ref[id]; delete this.free[id]; this.playing[id] = voice; return voice; } }; HTMLAudio.prototype.ended = function(voice) { this.free[voice.id] = voice; return delete this.playing[voice.id]; }; HTMLAudio.prototype.createSample = function(data) { this.start_time = gettime(); return new Sample(this, data); }; return HTMLAudio; })(Backend); WebAudio = (function(_super) { __extends(WebAudio, _super); WebAudio.available = window.webkitAudioContext !== void 0; function WebAudio() { WebAudio.__super__.constructor.call(this); this.ctx = new webkitAudioContext(); } WebAudio.prototype.play = function(buffer, looping) { var source; if (looping == null) { looping = false; } source = this.ctx.createBufferSource(); source.buffer = buffer; source.loop = looping; source.connect(this.ctx.destination); return source.noteOn(this.ctx.currentTime); }; WebAudio.prototype.decode = function(data, callback) { return this.ctx.decodeAudioData(data, function(buffer) { return callback(buffer); }); }; return WebAudio; })(Backend); if (WebAudio.available) { backend = new WebAudio(); exports.decode = function(data, callback) { return backend.decode(data, callback); }; exports.play = function(buffer) { return backend.play(buffer); }; } else { exports.decode = function(data, callback) {}; exports.play = function(buffer) {}; } }); loader.define('/events.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Emitter; return Emitter = (function() { function Emitter() { this.handlers = {}; } Emitter.prototype.on = function(name, callback) { var handlers; handlers = this.handlers[name]; if (handlers === void 0) { handlers = this.handlers[name] = []; } handlers.push(callback); return this; }; Emitter.prototype.trigger = function(name, a1, a2, a3, a4, a5, a6) { var handler, handlers, _i, _len; handlers = this.handlers[name]; if (handlers !== void 0) { for (_i = 0, _len = handlers.length; _i < _len; _i++) { handler = handlers[_i]; handler(a1, a2, a3, a4, a5, a6); } } return this; }; return Emitter; })(); }); loader.define('/rendernode.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Cubemap, Depthbuffer, Framebuffer, Quad, Rendernode, State, Texture2D, default_state, _ref, _ref1; _ref = require('webgl/framebuffer'), Framebuffer = _ref.Framebuffer, Depthbuffer = _ref.Depthbuffer; _ref1 = require('webgl/texture'), Texture2D = _ref1.Texture2D, Cubemap = _ref1.Cubemap; Quad = require('webgl/quad'); State = (function() { function State(gl) { this.gl = gl; this.depthTest = false; this.depthWrite = false; this.cullFace = null; this.alphaToCoverage = false; this.blend = false; } State.prototype.setDefaults = function() { this.gl.disable(this.gl.DEPTH_TEST); this.gl.depthMask(false); this.gl.cullFace(this.gl.BACK); this.gl.disable(this.gl.CULL_FACE); this.gl.disable(this.gl.SAMPLE_ALPHA_TO_COVERAGE); this.gl.disable(this.gl.BLEND); return this; }; State.prototype.set = function() { var mode; if (this.depthTest) { this.gl.enable(this.gl.DEPTH_TEST); mode = this.gl[this.depthTest]; if (mode) { this.gl.depthFunc(mode); } else { this.gl.depthFunc(this.gl.LEQUAL); } } if (this.depthWrite) { this.gl.depthMask(true); } if (this.cullFace) { this.gl.enable(this.gl.CULL_FACE); this.gl.cullFace(this.gl[this.cullFace]); } if (this.blend) { this.gl.enable(this.gl.BLEND); if (this.blend === 'additive') { this.gl.blendFunc(this.gl.ONE, this.gl.ONE); } } if (this.alphaToCoverage) { this.gl.enable(this.gl.SAMPLE_ALPHA_TO_COVERAGE); } return this; }; State.prototype.revert = function() { if (this.depthTest) { this.gl.disable(this.gl.DEPTH_TEST); this.gl.depthFunc(this.gl.LESS); } if (this.depthWrite) { this.gl.depthMask(false); } if (this.cullFace) { this.gl.disable(this.gl.CULL_FACE); } if (this.blend) { this.gl.disable(this.gl.BLEND); } if (this.alphaToCoverage) { this.gl.disable(this.gl.SAMPLE_ALPHA_TO_COVERAGE); } return this; }; return State; })(); default_state = null; return Rendernode = (function() { Rendernode.stateDefaults = function(gl) { return default_state = new State(gl).setDefaults(); }; function Rendernode(gl, _arg) { var blend, cullFace, depthTest, depthWrite, _ref2, _ref3; this.gl = gl; this.width = _arg.width, this.height = _arg.height, this.program = _arg.program, this.drawable = _arg.drawable, this.type = _arg.type, this.front = _arg.front, depthTest = _arg.depthTest, depthWrite = _arg.depthWrite, cullFace = _arg.cullFace, this.depthBuffer = _arg.depthBuffer, blend = _arg.blend, this.filter = _arg.filter, this.channels = _arg.channels, this.format = _arg.format, this.hdrClear = _arg.hdrClear; this.xoff = 0; this.yoff = 0; this.state = new State(this.gl); this.texunit_counter = 0; this.texunits = {}; if ((_ref2 = this.type) == null) { this.type = this.gl.UNSIGNED_BYTE; } if ((_ref3 = this.front) == null) { this.front = false; } if (depthTest == null) { depthTest = false; } this.depthTest(depthTest); if (depthWrite == null) { depthWrite = false; } this.depthWrite(depthWrite); if (cullFace == null) { cullFace = null; } this.cullFace(cullFace); if (blend == null) { blend = false; } this.state.blend = blend; if (!this.front) { this.createBuffers(); } if (this.hdrClear) { this.clearShader = get('hdr_clear.shader'); } } Rendernode.prototype.createBuffers = function() { this.output = new Texture2D(this.gl, { channels: this.channels, format: this.format, type: this.type }).bind().clampToEdge(); if (this.filter === 'nearest') { this.output.nearest(); } else { this.output.linear(); } if (this.width && this.height) { this.output.setSize(this.width, this.height); } else { this.output.setSize(16, 16); } this.output.unbind(); this.fbo = new Framebuffer(this.gl).bind().color(this.output).unbind(); if (this.depthBuffer) { return this.addDepth(); } }; Rendernode.prototype.addDepth = function(buffer) { if (buffer == null) { buffer = this.depthBuffer; } if (!this.depth && !this.front) { if (buffer instanceof Depthbuffer) { this.depth = buffer; } else { this.depth = new Depthbuffer(this.gl).setSize(this.output.width, this.output.height); } this.fbo.bind().depth(this.depth).unbind(); } return this; }; Rendernode.prototype.cullFace = function(side) { if (side == null) { side = null; } this.state.cullFace = side; return this; }; Rendernode.prototype.depthWrite = function(enabled) { if (enabled == null) { enabled = false; } this.state.depthWrite = enabled; return this; }; Rendernode.prototype.depthTest = function(enabled) { if (enabled == null) { enabled = false; } this.state.depthTest = enabled; return this; }; Rendernode.prototype.alphaToCoverage = function(enabled) { if (enabled == null) { enabled = false; } this.state.alphaToCoverage = enabled; return this; }; Rendernode.prototype.blendAdditive = function() { this.state.blend = 'additive'; return this; }; Rendernode.prototype.filterNearest = function() { this.output.bind().nearest().unbind(); return this; }; Rendernode.prototype.start = function() { this.started = true; this.viewport(); this.state.set(); if (this.program) { this.program.use(); } if (!this.front) { this.fbo.bind(); } if (this.drawable) { this.setPointers(this.drawable); } return this; }; Rendernode.prototype.setPointers = function(drawable) { if (drawable !== this.current_drawable) { this.current_drawable = drawable; return drawable.setPointersForShader(this.program); } }; Rendernode.prototype.end = function() { this.started = false; this.current_drawable = null; this.state.revert(); if (!this.front) { this.fbo.unbind(); } return this; }; Rendernode.prototype.sampler = function(name, source) { var texture, unit; if (source.output) { texture = source.output; } else { texture = source; } unit = this.texunits[name]; if (unit === void 0) { unit = this.texunits[name] = this.texunit_counter++; } texture.bind(unit); this.program.i(name, unit); return this; }; Rendernode.prototype.mat4 = function(name, value) { this.program.mat4(name, value); return this; }; Rendernode.prototype.mat3 = function(name, value) { this.program.mat3(name, value); return this; }; Rendernode.prototype.val3 = function(name, x, y, z) { this.program.val3(name, x, y, z); return this; }; Rendernode.prototype.vec3 = function(name, value) { this.program.vec3(name, value); return this; }; Rendernode.prototype.f = function(name, value) { this.program.f(name, value); return this; }; Rendernode.prototype.fv = function(name, values) { this.program.fv(name, values); return this; }; Rendernode.prototype.val2 = function(name, x, y) { this.program.val2(name, x, y); return this; }; Rendernode.prototype.clear = function(r, g, b, a) { if (r == null) { r = 0; } if (g == null) { g = 0; } if (b == null) { b = 0; } if (a == null) { a = 1; } if (this.hdrClear) { if (!this.front) { this.fbo.bind(); } this.clearShader.use().val4('clear_color', r, g, b, a).draw(quad); } else { this.gl.clearColor(r, g, b, a); this.gl.clear(this.gl.COLOR_BUFFER_BIT); } return this; }; Rendernode.prototype.clearBoth = function(r, g, b, a, depth) { if (r == null) { r = 0; } if (g == null) { g = 0; } if (b == null) { b = 0; } if (a == null) { a = 1; } if (depth == null) { depth = 1; } this.gl.clearColor(r, g, b, a); this.gl.clearDepth(depth); this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); return this; }; Rendernode.prototype.clearDepth = function(depth) { if (depth == null) { depth = 1; } this.gl.clearDepth(depth); this.gl.clear(this.gl.DEPTH_BUFFER_BIT); return this; }; Rendernode.prototype.draw = function(drawable) { var do_end; if (drawable == null) { drawable = this.drawable; } do_end = false; if (!this.started) { do_end = true; this.start(); } this.program.val2('viewport', this.width, this.height); if (drawable !== this.current_drawable) { this.setPointers(drawable); } drawable.draw(); if (do_end) { this.end(); } return this; }; Rendernode.prototype.drawModel = function(texture_type, sampler_name) { var c, material, _i, _len, _ref2; if (sampler_name == null) { sampler_name = texture_type; } if (texture_type) { _ref2 = this.drawable.materials[texture_type]; for (_i = 0, _len = _ref2.length; _i < _len; _i++) { material = _ref2[_i]; this.f('specularity', material.specularity); c = material.diffuse_color; this.val3('diffuse_color', c.r, c.g, c.b); this.sampler(sampler_name, material[texture_type]); this.drawable.drawRange(material.start, material.size); } } else { this.draw(); } return this; }; Rendernode.prototype.resize = function(width, height) { this.width = Math.floor(width); this.height = Math.floor(height); if (this.output) { this.output.bind().setSize(this.width, this.height).unbind(); } if (this.depth) { this.depth.setSize(this.width, this.height); } if (this.fbo) { return this.fbo.bind().check().unbind(); } }; Rendernode.prototype.viewport = function(x, y, width, height) { if (x == null) { x = this.xoff; } if (y == null) { y = this.yoff; } if (width == null) { width = this.width; } if (height == null) { height = this.height; } if (width && height) { this.xoff = x; this.yoff = y; this.width = width; this.height = height; if (this.started) { this.gl.viewport(x, y, width, height); } return this; } }; Rendernode.prototype.bind = function(unit) { if (unit == null) { unit = 0; } return this.output.bind(unit); }; return Rendernode; })(); }); loader.define('/schedule.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 exports.run = function(callback) { var last, step; last = gettime(); step = function() { var current, delta; current = gettime(); delta = current - last; last = current; callback(current, delta); return requestAnimationFrame(step); }; return requestAnimationFrame(step); }; }); loader.define('/webgl/plane.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Plane, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Plane = (function(_super) { __extends(Plane, _super); Plane.prototype.attribs = ['position', 'normal', 'texcoord']; function Plane(gl, s) { var vertices; this.gl = gl; if (s == null) { s = 1; } Plane.__super__.constructor.call(this); this.size = 6; vertices = [-s, 0, -s, 0, 1, 0, 0, 0, -s, 0, s, 0, 1, 0, 0, 1, s, 0, s, 0, 1, 0, 1, 1, s, 0, -s, 0, 1, 0, 1, 0, -s, 0, -s, 0, 1, 0, 0, 0, s, 0, s, 0, 1, 0, 1, 1]; this.uploadList(vertices); } Plane.prototype.setPointersForShader = function(shader) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.setPointer(shader, 'position', 3, 0, 8); this.setPointer(shader, 'normal', 3, 3, 8); this.setPointer(shader, 'texcoord', 2, 6, 8); return this; }; return Plane; })(require('drawable')); }); loader.define('/webgl/model.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Model, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Model = (function(_super) { __extends(Model, _super); Model.prototype.attribs = ['position', 'normal', 'texcoord']; function Model(gl, data) { this.gl = gl; Model.__super__.constructor.call(this); this.size = data.byteLength / (8 * Float32Array.BYTES_PER_ELEMENT); this.upload(data); } Model.prototype.setPointersForShader = function(shader) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.setPointer(shader, 'position', 3, 0, 8); this.setPointer(shader, 'normal', 3, 3, 8); this.setPointer(shader, 'texcoord', 2, 6, 8); return this; }; return Model; })(require('drawable')); }); loader.define('/webgl/cube.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Cube, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Cube = (function(_super) { __extends(Cube, _super); Cube.prototype.attribs = ['position', 'normal', 'barycentric']; Cube.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 9 }, { name: 'normal', size: 3, offset: 3, stride: 9 }, { name: 'barycentric', size: 3, offset: 6, stride: 9 } ]; function Cube(gl, s) { var vertices; this.gl = gl; if (s == null) { s = 1; } Cube.__super__.constructor.call(this); this.size = 6 * 6; vertices = [-s, -s, -s, 0, 0, -1, 1, 0, 0, -s, s, -s, 0, 0, -1, 0, 1, 0, s, s, -s, 0, 0, -1, 0, 0, 1, s, -s, -s, 0, 0, -1, 1, 0, 0, -s, -s, -s, 0, 0, -1, 0, 1, 0, s, s, -s, 0, 0, -1, 0, 0, 1, s, s, s, 0, 0, 1, 1, 0, 0, -s, s, s, 0, 0, 1, 0, 1, 0, -s, -s, s, 0, 0, 1, 0, 0, 1, s, s, s, 0, 0, 1, 1, 0, 0, -s, -s, s, 0, 0, 1, 0, 1, 0, s, -s, s, 0, 0, 1, 0, 0, 1, -s, s, -s, 0, 1, 0, 1, 0, 0, -s, s, s, 0, 1, 0, 0, 1, 0, s, s, s, 0, 1, 0, 0, 0, 1, s, s, -s, 0, 1, 0, 1, 0, 0, -s, s, -s, 0, 1, 0, 0, 1, 0, s, s, s, 0, 1, 0, 0, 0, 1, s, -s, s, 0, -1, 0, 1, 0, 0, -s, -s, s, 0, -1, 0, 0, 1, 0, -s, -s, -s, 0, -1, 0, 0, 0, 1, s, -s, s, 0, -1, 0, 1, 0, 0, -s, -s, -s, 0, -1, 0, 0, 1, 0, s, -s, -s, 0, -1, 0, 0, 0, 1, -s, -s, -s, -1, 0, 0, 1, 0, 0, -s, -s, s, -1, 0, 0, 0, 1, 0, -s, s, s, -1, 0, 0, 0, 0, 1, -s, s, -s, -1, 0, 0, 1, 0, 0, -s, -s, -s, -1, 0, 0, 0, 1, 0, -s, s, s, -1, 0, 0, 0, 0, 1, s, s, s, 1, 0, 0, 1, 0, 0, s, -s, s, 1, 0, 0, 0, 1, 0, s, -s, -s, 1, 0, 0, 0, 0, 1, s, s, s, 1, 0, 0, 1, 0, 0, s, -s, -s, 1, 0, 0, 0, 1, 0, s, s, -s, 1, 0, 0, 0, 0, 1]; this.uploadList(vertices); } return Cube; })(require('drawable')); }); loader.define('/webgl/framebuffer.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Framebuffer, Renderbuffer, framebufferBinding, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; framebufferBinding = null; exports.Framebuffer = Framebuffer = (function() { function Framebuffer(gl) { this.gl = gl; this.buffer = this.gl.createFramebuffer(); } Framebuffer.prototype.bind = function() { if (framebufferBinding !== this) { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffer); framebufferBinding = this; } return this; }; Framebuffer.prototype.unbind = function() { if (framebufferBinding !== null) { this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); framebufferBinding = null; } return this; }; Framebuffer.prototype.check = function() { var result; result = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER); switch (result) { case this.gl.FRAMEBUFFER_UNSUPPORTED: throw 'Framebuffer is unsupported'; break; case this.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: throw 'Framebuffer incomplete attachment'; break; case this.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: throw 'Framebuffer incomplete dimensions'; break; case this.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: throw 'Framebuffer incomplete missing attachment'; } return this; }; Framebuffer.prototype.color = function(texture, target) { if (target == null) { target = texture.target; } this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, target, texture.handle, 0); this.check(); return this; }; Framebuffer.prototype.depth = function(buffer) { this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, buffer.id); this.check(); return this; }; Framebuffer.prototype.destroy = function() { return this.gl.deleteFramebuffer(this.buffer); }; return Framebuffer; })(); Renderbuffer = (function() { function Renderbuffer(gl) { this.gl = gl; this.id = this.gl.createRenderbuffer(); } Renderbuffer.prototype.bind = function() { this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.id); return this; }; Renderbuffer.prototype.setSize = function(width, height) { this.width = width; this.height = height; this.bind(); this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl[this.format], this.width, this.height); return this.unbind(); }; Renderbuffer.prototype.unbind = function() { this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null); return this; }; return Renderbuffer; })(); exports.Depthbuffer = (function(_super) { __extends(_Class, _super); function _Class() { return _Class.__super__.constructor.apply(this, arguments); } _Class.prototype.format = 'DEPTH_COMPONENT16'; return _Class; })(Renderbuffer); }); loader.define('/webgl/quad.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Quad, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; return Quad = (function(_super) { __extends(Quad, _super); Quad.prototype.attribs = ['position']; Quad.prototype.pointers = [ { name: 'position', size: 2, offset: 0, stride: 2 } ]; function Quad(gl) { var vertices; this.gl = gl; Quad.__super__.constructor.call(this); this.size = 6; vertices = [-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]; this.uploadList(vertices); } return Quad; })(require('drawable')); }); loader.define('/webgl/hexgrid.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Hexgrid, clamp, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; clamp = function(value, left, right) { if (value < left) { return left; } else if (value > right) { return right; } else { return value; } }; return Hexgrid = (function(_super) { __extends(Hexgrid, _super); Hexgrid.prototype.attribs = ['position', 'texcoord', 'barycentric']; function Hexgrid(gl, xsize, ysize, width, height) { var b, m, t, vertices, x, x1, x2, x3, x4, y, _i, _j; this.gl = gl; if (xsize == null) { xsize = 16; } if (ysize == null) { ysize = 16; } if (width == null) { width = 1; } if (height == null) { height = 1; } Hexgrid.__super__.constructor.call(this); vertices = []; for (x = _i = 0; 0 <= xsize ? _i <= xsize : _i >= xsize; x = 0 <= xsize ? ++_i : --_i) { x1 = clamp((x - 0.5) / xsize, 0, 1); x2 = clamp((x + 0.0) / xsize, 0, 1); x3 = clamp((x + 0.5) / xsize, 0, 1); x4 = clamp((x + 1.0) / xsize, 0, 1); for (y = _j = 0; _j < ysize; y = _j += 2) { t = (y + 0) / ysize; m = (y + 1) / ysize; b = (y + 2) / ysize; vertices.push(x2 * width, 0, m * height, x2, m, 0, 0, 1, x3 * width, 0, t * height, x3, t, 0, 1, 0, x1 * width, 0, t * height, x1, t, 1, 0, 0, x4 * width, 0, m * height, x4, m, 0, 0, 1, x3 * width, 0, t * height, x3, t, 0, 1, 0, x2 * width, 0, m * height, x2, m, 1, 0, 0, x3 * width, 0, b * height, x3, b, 0, 0, 1, x2 * width, 0, m * height, x2, m, 0, 1, 0, x1 * width, 0, b * height, x1, b, 1, 0, 0, x3 * width, 0, b * height, x3, b, 0, 0, 1, x4 * width, 0, m * height, x4, m, 0, 1, 0, x2 * width, 0, m * height, x2, m, 1, 0, 0); } } this.size = vertices.length / 8; this.uploadList(vertices); } Hexgrid.prototype.setPointersForShader = function(shader) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.setPointer(shader, 'position', 3, 0, 8); this.setPointer(shader, 'texcoord', 2, 3, 8); this.setPointer(shader, 'barycentric', 3, 5, 8); return this; }; return Hexgrid; })(require('drawable')); }); loader.define('/webgl/drawable.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Drawable; return Drawable = (function() { var float_size; float_size = Float32Array.BYTES_PER_ELEMENT; function Drawable() { this.first = 0; this.size = 0; this.buffer = this.gl.createBuffer(); this.mode = this.gl.TRIANGLES; } Drawable.prototype.setPointer = function(shader, name, size, start, stride) { var location; if (size == null) { size = 3; } if (start == null) { start = 0; } if (stride == null) { stride = 0; } location = shader.attribLoc(name); if (location >= 0) { this.gl.vertexAttribPointer(location, size, this.gl.FLOAT, false, stride * float_size, start * float_size); } return this; }; Drawable.prototype.setPointersForShader = function(shader) { var pointer, _i, _len, _ref; this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); _ref = this.pointers; for (_i = 0, _len = _ref.length; _i < _len; _i++) { pointer = _ref[_i]; this.setPointer(shader, pointer.name, pointer.size, pointer.offset, pointer.stride); } return this; }; Drawable.prototype.draw = function(shader) { if (shader) { this.setPointersForShader(shader); } this.gl.drawArrays(this.mode, this.first, this.size); if (shader) { this.disableAttribs(shader); } return this; }; Drawable.prototype.drawRange = function(start, size) { if (start == null) { start = this.first; } if (size == null) { size = this.size; } return this.gl.drawArrays(this.mode, start, size); }; Drawable.prototype.disableAttribs = function(shader) { var location, name, _i, _len, _ref; _ref = this.attribs; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; location = shader.attribLoc(name); if (location >= 0) { this.gl.disableVertexAttribArray(location); } } return this; }; Drawable.prototype.uploadList = function(list) { var data; data = new Float32Array(list); return this.upload(data); }; Drawable.prototype.upload = function(data) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.gl.bufferData(this.gl.ARRAY_BUFFER, data, this.gl.STATIC_DRAW); return this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null); }; return Drawable; })(); }); loader.define('/webgl/shader.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Shader, directives, in_use; directives = ['#ifdef GL_FRAGMENT_PRECISION_HIGH', 'precision highp int;', 'precision highp float;', '#else', 'precision mediump int;', 'precision mediump float;', '#endif']; in_use = null; return Shader = (function() { Shader.lastError = ''; Shader.splitLines = function(path, source) { var i, line, result, _i, _len, _ref; result = []; _ref = source.split('\n'); for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { line = _ref[i]; result.push({ line: i, text: line, path: path }); } return result; }; Shader.error = 'ShaderError'; function Shader(gl, path, source) { var dirname; this.gl = gl; this.path = path; dirname = this.path.split('/'); dirname.pop(); this.dirname = dirname.join('/'); this.program = this.gl.createProgram(); this.vs = this.gl.createShader(gl.VERTEX_SHADER); this.fs = this.gl.createShader(gl.FRAGMENT_SHADER); this.gl.attachShader(this.program, this.vs); this.gl.attachShader(this.program, this.fs); this.link(source); } Shader.prototype.preprocess = function(source) { var global, i, line, lines, match, shaders, type, _i, _len; lines = source.split('\n'); shaders = { 'global': [], 'fragment': [], 'vertex': [] }; type = 'global'; for (i = _i = 0, _len = lines.length; _i < _len; i = ++_i) { line = lines[i]; match = line.match(/^(\w+):$/); if (match) { type = match[1]; } else { shaders[type].push({ line: i, text: line, path: this.path }); } } global = this.resolveLines(shaders.global); shaders.fragment = global.concat(this.resolveLines(shaders.fragment)); shaders.vertex = global.concat(this.resolveLines(shaders.vertex)); return shaders; }; Shader.prototype.resolveLines = function(stage) { var abspath, lib, line, match, path, result, _i, _j, _len, _len1; result = []; for (_i = 0, _len = stage.length; _i < _len; _i++) { line = stage[_i]; match = line.text.match(/^\s+#require (\S+)\s*$/); if (match) { path = "" + match[1] + ".shaderlib"; abspath = loader.resolvePath(this.dirname, path); lib = get(abspath); for (_j = 0, _len1 = lib.length; _j < _len1; _j++) { line = lib[_j]; result.push(line); } } else { result.push(line); } } return result; }; Shader.prototype.concat = function(stage) { var line, result, _i, _j, _len, _len1; result = ''; for (_i = 0, _len = directives.length; _i < _len; _i++) { line = directives[_i]; result += line + '\n'; } result += '#line 0\n'; for (_j = 0, _len1 = stage.length; _j < _len1; _j++) { line = stage[_j]; result += line.text + '\n'; } return result; }; Shader.prototype.link = function(source) { var error, shaders; shaders = this.preprocess(source); this.compile(this.vs, shaders.vertex); this.compile(this.fs, shaders.fragment); this.gl.linkProgram(this.program); if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) { error = "Shader Link Error for file: " + this.path + ":\n" + (this.gl.getProgramInfoLog(this.program)); console.error(error); Shader.lastError = error; throw Shader.error; } this.attrib_cache = {}; this.uniform_cache = {}; return this.value_cache = {}; }; Shader.prototype.compile = function(shader, lines) { var error, group, source, text, translated; source = this.concat(lines); this.gl.shaderSource(shader, source); this.gl.compileShader(shader); if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { error = this.gl.getShaderInfoLog(shader); group = "Shader Compile Error for file: " + this.path; translated = this.translateError(error, lines); text = group + '\n' + translated; Shader.lastError = text; console.group(group); console.warn(translated); console.groupEnd(); throw Shader.error; } }; Shader.prototype.translateError = function(error, sourcelines) { var i, line, lineno, match, message, result, sourceline, _i, _len, _ref; result = []; _ref = error.split('\n'); for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { line = _ref[i]; match = line.match(/ERROR: \d+:(\d+): (.*)/); if (match) { lineno = parseFloat(match[1]); message = match[2]; sourceline = sourcelines[lineno - 1]; result.push("ERROR: Line " + (sourceline.line + 1) + ": File " + sourceline.path + ": " + message + " SOURCE: " + sourceline.text); } else { result.push(line); } } return result.join('\n'); }; Shader.prototype.attribLoc = function(name) { var location; location = this.attrib_cache[name]; if (location === void 0) { location = this.attrib_cache[name] = this.gl.getAttribLocation(this.program, name); } if (location >= 0) { this.gl.enableVertexAttribArray(location); } return location; }; Shader.prototype.use = function() { if (this !== in_use) { in_use = this; this.gl.useProgram(this.program); } return this; }; Shader.prototype.unbind = function() { if (in_use) { in_use = null; this.gl.useProgram(null); } return this; }; Shader.prototype.loc = function(name) { var location; location = this.uniform_cache[name]; if (location === void 0) { location = this.uniform_cache[name] = this.gl.getUniformLocation(this.program, name); } return location; }; Shader.prototype.i = function(name, value) { var cached, loc; cached = this.value_cache[name]; if (cached !== value) { this.value_cache[name] = value; loc = this.loc(name); if (loc) { this.gl.uniform1i(loc, value); } } return this; }; Shader.prototype.f = function(name, value) { var cached, loc; cached = this.value_cache[name]; if (cached !== value) { this.value_cache[name] = value; loc = this.loc(name); if (loc) { this.gl.uniform1f(loc, value); } } return this; }; Shader.prototype.fv = function(name, values) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform1fv(loc, values); } return this; }; Shader.prototype.val2 = function(name, a, b) { var cached, loc; cached = this.value_cache[name]; if (cached) { if (cached.a !== a || cached.b !== b) { cached.a = a; cached.b = b; loc = this.loc(name); if (loc) { this.gl.uniform2f(loc, a, b); } } } else { this.value_cache[name] = { a: a, b: b }; loc = this.loc(name); if (loc) { this.gl.uniform2f(loc, a, b); } } return this; }; Shader.prototype.val3 = function(name, a, b, c) { var cached, loc; cached = this.value_cache[name]; if (cached) { if (cached.a !== a || cached.b !== b || cached.c !== c) { cached.a = a; cached.b = b; cached.c = c; loc = this.loc(name); if (loc) { this.gl.uniform3f(loc, a, b, c); } } } else { this.value_cache[name] = { a: a, b: b, c: c }; loc = this.loc(name); if (loc) { this.gl.uniform3f(loc, a, b, c); } } return this; }; Shader.prototype.vec2 = function(name, value) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform2fv(loc, value); } return this; }; Shader.prototype.vec3 = function(name, value) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform3fv(loc, value); } return this; }; Shader.prototype.val4 = function(name, a, b, c, d) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform4f(loc, a, b, c, d); } return this; }; Shader.prototype.vec4 = function(name, a, b, c, e) { var loc; loc = this.loc(name); if (loc) { this.gl.uniform2f(loc, a, b, c, e); } return this; }; Shader.prototype.mat4 = function(name, value) { var loc; loc = this.loc(name); if (loc) { if (value instanceof Mat4) { this.gl.uniformMatrix4fv(loc, this.gl.FALSE, value.data); } else { this.gl.uniformMatrix4fv(loc, this.gl.FALSE, value); } } return this; }; Shader.prototype.mat3 = function(name, value) { var loc; loc = this.loc(name); if (loc) { this.gl.uniformMatrix3fv(loc, this.gl.FALSE, value.data); } return this; }; Shader.prototype.draw = function(drawable) { drawable.setPointersForShader(this).draw().disableAttribs(this); return this; }; return Shader; })(); }); loader.define('/webgl/sphere.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Sphere, faces, icosahedron, midp, normalize, phi, subdivide, v1, v10, v11, v12, v2, v3, v4, v5, v6, v7, v8, v9, vertexlist, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; phi = (1 + Math.sqrt(5)) / 2; midp = function(v1, v2) { var x1, x2, x3, y1, y2, y3, z1, z2, z3; x1 = v1[0]; y1 = v1[1]; z1 = v1[2]; x2 = v2[0]; y2 = v2[1]; z2 = v2[2]; x3 = (x1 + x2) / 2; y3 = (y1 + y2) / 2; z3 = (z1 + z2) / 2; return [x3, y3, z3]; }; normalize = function(faces, r) { var face, l, new_face, result, vertex, x, y, z, _i, _j, _len, _len1; if (r == null) { r = 1; } result = []; for (_i = 0, _len = faces.length; _i < _len; _i++) { face = faces[_i]; new_face = []; result.push(new_face); for (_j = 0, _len1 = face.length; _j < _len1; _j++) { vertex = face[_j]; x = vertex[0]; y = vertex[1]; z = vertex[2]; l = Math.sqrt(x * x + y * y + z * z); new_face.push([(r * x) / l, (r * y) / l, (r * z) / l]); } } return result; }; subdivide = function(faces) { var face, result, v0, v1, v2, va, vb, vc, _i, _len; result = []; for (_i = 0, _len = faces.length; _i < _len; _i++) { face = faces[_i]; v0 = face[0]; v1 = face[1]; v2 = face[2]; va = midp(v0, v1); vb = midp(v1, v2); vc = midp(v2, v0); result.push([v0, va, vc], [va, v1, vb], [vc, vb, v2], [va, vb, vc]); } return result; }; v1 = [1, phi, 0]; v2 = [-1, phi, 0]; v3 = [0, 1, phi]; v4 = [0, 1, -phi]; v5 = [phi, 0, 1]; v6 = [-phi, 0, 1]; v7 = [-phi, 0, -1]; v8 = [phi, 0, -1]; v9 = [0, -1, phi]; v10 = [0, -1, -phi]; v11 = [-1, -phi, 0]; v12 = [1, -phi, 0]; faces = [[v1, v2, v3], [v2, v1, v4], [v1, v3, v5], [v2, v6, v3], [v2, v7, v6], [v2, v4, v7], [v1, v5, v8], [v1, v8, v4], [v9, v3, v6], [v3, v9, v5], [v4, v10, v7], [v4, v8, v10], [v6, v7, v11], [v6, v11, v9], [v7, v10, v11], [v5, v12, v8], [v12, v5, v9], [v12, v10, v8], [v11, v12, v9], [v12, v11, v10]]; icosahedron = normalize(faces); vertexlist = function(faces) { var face, vertex, vertices, x, y, z, _i, _j, _len, _len1; vertices = []; for (_i = 0, _len = faces.length; _i < _len; _i++) { face = faces[_i]; for (_j = 0, _len1 = face.length; _j < _len1; _j++) { vertex = face[_j]; x = vertex[0]; y = vertex[1]; z = vertex[2]; vertices.push(x, y, z); } } return vertices; }; return Sphere = (function(_super) { __extends(Sphere, _super); Sphere.prototype.attribs = ['position']; Sphere.prototype.pointers = [ { name: 'position', size: 3, offset: 0, stride: 3 } ]; Sphere.makeVertices = function(radius, subdivisions) { var i, template, vertices, _i; if (radius == null) { radius = 1; } if (subdivisions == null) { subdivisions = 3; } template = icosahedron; for (i = _i = 0; 0 <= subdivisions ? _i < subdivisions : _i > subdivisions; i = 0 <= subdivisions ? ++_i : --_i) { template = subdivide(template); template = normalize(template); } faces = normalize(template, radius); vertices = vertexlist(faces); return vertices; }; function Sphere(gl, radius, subdivisions) { var vertices; this.gl = gl; if (radius == null) { radius = 1; } if (subdivisions == null) { subdivisions = 3; } Sphere.__super__.constructor.call(this); vertices = Sphere.makeVertices(radius, subdivisions); this.size = vertices.length / 3; this.uploadList(vertices); } return Sphere; })(require('drawable')); }); loader.define('/webgl/texture.js', function(exports, require, get){ // Generated by CoffeeScript 1.3.3 var Cubemap, Framebuffer, Texture, Texture2D, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; Framebuffer = require('framebuffer').Framebuffer; Texture = (function() { var bound, ids; bound = []; ids = 0; function Texture() { this.handle = this.gl.createTexture(); this.id = ids++; this.unit = null; } Texture.prototype.bind = function(unit) { if (unit == null) { unit = 0; } this.unit = unit; if (bound[unit] !== this.id) { this.gl.activeTexture(this.gl.TEXTURE0 + unit); this.gl.bindTexture(this.target, this.handle); bound[unit] = this.id; } return this; }; Texture.prototype.unbind = function(unit) { if (unit == null) { unit = this.unit; } if (unit && bound[unit] === this.id) { this.gl.activeTexture(this.gl.TEXTURE0 + unit); this.gl.bindTexture(this.target, null); bound[unit] = null; } return this; }; Texture.prototype.mipmap = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR); this.gl.generateMipmap(this.target); return this; }; Texture.prototype.mipmapNearest = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR); this.gl.generateMipmap(this.target); return this; }; Texture.prototype.linear = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); return this; }; Texture.prototype.nearest = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST); return this; }; Texture.prototype.clampToEdge = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); return this; }; Texture.prototype.repeat = function() { this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT); this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.REPEAT); return this; }; Texture.prototype.anisotropy = function() { var ext, max; ext = this.gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); if (ext) { max = this.gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT); this.gl.texParameterf(this.target, ext.TEXTURE_MAX_ANISOTROPY_EXT, max); } return this; }; return Texture; })(); exports.Texture2D = Texture2D = (function(_super) { __extends(Texture2D, _super); function Texture2D(gl, _arg) { var _ref, _ref1, _ref2, _ref3; this.gl = gl; _ref = _arg != null ? _arg : {}, this.channels = _ref.channels, this.format = _ref.format, this.type = _ref.type; Texture2D.__super__.constructor.call(this); if ((_ref1 = this.channels) == null) { this.channels = this.gl.RGBA; } if ((_ref2 = this.format) == null) { this.format = this.gl.RGBA; } if ((_ref3 = this.type) == null) { this.type = this.gl.UNSIGNED_BYTE; } this.target = this.gl.TEXTURE_2D; } Texture2D.prototype.upload = function(image) { this.uploadImage(image); return this; }; Texture2D.prototype.uploadImage = function(image) { this.width = image.width; this.height = image.height; this.gl.texImage2D(this.target, 0, this.channels, this.format, this.type, image); return this; }; Texture2D.prototype.uploadData = function(data, width, height) { this.width = width; this.height = height; this.gl.texImage2D(this.target, 0, this.channels, width, height, 0, this.format, this.type, data); return this; }; Texture2D.prototype.setSize = function(width, height) { this.width = width; this.height = height; this.gl.texImage2D(this.target, 0, this.channels, width, height, 0, this.format, this.type, null); return this; }; Texture2D.prototype.read = function(dst) { if (dst == null) { dst = new Uint8Array(this.width * this.height * 4); } if (this.fbo) { this.fbo.bind(); } else { this.fbo = new Framebuffer(this.gl).bind().color(this); } this.gl.readPixels(0, 0, this.width, this.height, this.gl.RGBA, this.gl.UNSIGNED_BYTE, dst); this.fbo.unbind(); return dst; }; Texture2D.prototype.toPNG = function() { var canvas, ctx, data, i, imgdata, result, url, _i, _ref; canvas = document.createElement('canvas'); canvas.height = this.height; canvas.width = this.width; ctx = canvas.getContext('2d'); imgdata = ctx.createImageData(this.width, this.height); imgdata.data.set(this.read(), 0); ctx.putImageData(imgdata, 0, 0); url = canvas.toDataURL('image/png'); data = atob(url.split(',')[1]); result = new Uint8Array(data.length); for (i = _i = 0, _ref = data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { result[i] = data.charCodeAt(i); } return result; }; return Texture2D; })(Texture); exports.Cubemap = Cubemap = (function(_super) { __extends(Cubemap, _super); function Cubemap(gl) { this.gl = gl; Cubemap.__super__.constructor.call(this); this.target = this.gl.TEXTURE_CUBE_MAP; this.up = this.gl.TEXTURE_CUBE_MAP_POSITIVE_Y; this.down = this.gl.TEXTURE_CUBE_MAP_NEGATIVE_Y; this.right = this.gl.TEXTURE_CUBE_MAP_POSITIVE_X; this.left = this.gl.TEXTURE_CUBE_MAP_NEGATIVE_X; this.back = this.gl.TEXTURE_CUBE_MAP_NEGATIVE_Z; this.front = this.gl.TEXTURE_CUBE_MAP_POSITIVE_Z; } Cubemap.prototype.uploadSide = function(name, image) { return this.gl.texImage2D(this.gl['TEXTURE_CUBE_MAP_' + name], 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image); }; Cubemap.prototype.upload = function(folder, ext) { if (ext == null) { ext = 'jpg'; } this.uploadSide('POSITIVE_Y', folder.get("up." + ext)); this.uploadSide('NEGATIVE_Y', folder.get("down." + ext)); this.uploadSide('POSITIVE_X', folder.get("right." + ext)); this.uploadSide('NEGATIVE_X', folder.get("left." + ext)); this.uploadSide('POSITIVE_Z', folder.get("front." + ext)); this.uploadSide('NEGATIVE_Z', folder.get("back." + ext)); return this; }; Cubemap.prototype.setSize = function(size) { this.size = size; this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, this.gl.RGBA, this.size, this.size, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null); return this; }; return Cubemap; })(Texture); }); $(function(){loader.main()}); ================================================ FILE: www/index.html ================================================ Deferred Irradiance Volumes
    Blog Entry