├── .gitignore ├── build ├── build.sh ├── compile ├── extra ├── 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 ├── geometry.js ├── keys.coffee ├── keys.js ├── loading.coffee ├── loading.js ├── processingNode.coffee ├── processingNode.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 ├── pack ├── src ├── application.coffee ├── application.js ├── bunny.mesh ├── display.shader ├── main.coffee ├── main.js ├── overlay.shader └── transparent.shader └── www ├── assets.pack ├── code.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | debug 2 | packed/main.js 3 | *.pyc 4 | .DS_Store 5 | *.swp 6 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | Copy this into your bin directory for convenient building 5 | ''' 6 | 7 | import os, subprocess 8 | 9 | here = os.getcwd() 10 | folders = here.split('/') 11 | 12 | while folders: 13 | path = '/'.join(folders) 14 | scriptpath = os.path.join(path, 'build.sh') 15 | if os.path.exists(scriptpath): 16 | os.chdir(path) 17 | subprocess.call(['bash', 'build.sh']) 18 | break 19 | folders.pop() 20 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | ./compile 2 | ./pack 3 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys, stat, subprocess 4 | from datetime import datetime 5 | 6 | here = os.path.dirname(os.path.abspath(__file__)) 7 | 8 | message_count = 0 9 | def message(text): 10 | global message_count 11 | now = datetime.now().strftime('%H:%M:%S') 12 | print '[%04i %s] %s' % (message_count, now, text) 13 | message_count+=1 14 | 15 | def error(text): 16 | sys.stdout.write('\x1b[31m%s\x1b[39m' % text) 17 | sys.stdout.flush() 18 | 19 | def coffee_compile(path, bare): 20 | message('compiling: %s' % path) 21 | if bare: 22 | command = ['coffee', '--compile', '--bare', path] 23 | else: 24 | command = ['coffee', '--compile', path] 25 | 26 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 27 | out, err = process.communicate() 28 | if process.returncode: 29 | error(err) 30 | 31 | def get_mtime(name): 32 | return os.stat(name)[stat.ST_MTIME] 33 | 34 | def listfiles(folder, *exts): 35 | for path, dirs, names in os.walk(os.path.join(here, folder)): 36 | for name in names: 37 | ext = os.path.splitext(name)[-1].lstrip('.') 38 | if ext in exts: 39 | yield path, name, os.path.join(path, name) 40 | 41 | def check_files(folder, bare): 42 | for path, name, loc in listfiles(folder, 'coffee'): 43 | js_path = os.path.join(path, os.path.splitext(name)[0] + '.js') 44 | coffee_mtime = get_mtime(loc) 45 | if os.path.exists(js_path): 46 | js_mtime = get_mtime(js_path) 47 | if coffee_mtime > js_mtime: 48 | coffee_compile(loc, bare=bare) 49 | else: 50 | coffee_compile(loc, bare=bare) 51 | 52 | if __name__ == '__main__': 53 | check_files('src', bare=True) 54 | check_files('lib', bare=True) 55 | check_files('extra', bare=False) 56 | -------------------------------------------------------------------------------- /extra/loader.coffee: -------------------------------------------------------------------------------- 1 | fs = {} 2 | 3 | makeURL = (blob) -> 4 | return URL.createObjectURL blob 5 | 6 | makeBlob = (data, type) -> 7 | builder = new BlobBuilder() 8 | builder.append(data) 9 | return builder.getBlob(type) 10 | 11 | window.getURL = (data) -> 12 | blob = makeBlob data 13 | return makeURL blob 14 | 15 | resolvePath = (base, path) -> 16 | if path[0] == '/' 17 | return path 18 | else 19 | path = path.split '/' 20 | if base == '/' 21 | base = [''] 22 | else 23 | base = base.split '/' 24 | 25 | while base.length > 0 and path.length > 0 and path[0] == '..' 26 | base.pop() 27 | path.shift() 28 | 29 | if base.length == 0 || path.length == 0 || base[0] != '' 30 | throw "Invalid path: #{base.join '/'}/#{path.join '/'}" 31 | return "#{base.join('/')}/#{path.join('/')}" 32 | 33 | getJSON = (url, callback) -> 34 | request = new XMLHttpRequest() 35 | request.open 'GET', url, true 36 | request.onload = -> 37 | callback(JSON.parse(request.response)) 38 | request.send() 39 | 40 | getBuffer = (url, progress, callback) -> 41 | request = new XMLHttpRequest() 42 | request.open 'GET', url, true 43 | request.responseType = 'arraybuffer' 44 | request.onload = -> 45 | callback(request.response) 46 | request.onprogress = (event) -> 47 | if event.lengthComputable 48 | progress event.loaded/event.total 49 | request.send() 50 | 51 | isImage = (path) -> 52 | return path.match('\.jpg$|\.jpeg|\.gif$|\.png') 53 | 54 | window.loader = 55 | main: -> 56 | main = @require 'main' 57 | if main.main 58 | main.main() 59 | else 60 | throw 'Main function is not defined in main module.' 61 | 62 | define: (path, code) -> 63 | dirname = path.split '/' 64 | dirname.pop() 65 | dirname = dirname.join '/' 66 | 67 | require = (modpath) -> 68 | abspath = resolvePath dirname, modpath 69 | node = fs["#{abspath}.js"] 70 | if not node 71 | node = fs["#{abspath}/module.js"] 72 | if not node then throw "Module not found: #{abspath}" 73 | if !node.value then node.create() 74 | return node.value 75 | 76 | get = (respath) -> 77 | abspath = resolvePath dirname, respath 78 | node = fs[abspath] 79 | if not node then throw "Resource not found: #{abspath}" 80 | return node 81 | 82 | folder = get.folder = (folderpath) -> 83 | folder_abs = resolvePath dirname, folderpath 84 | return { 85 | path: folder_abs, 86 | name: folder_abs.split('/')[folder_abs.split('/').length-1] 87 | get: (respath) -> 88 | nodepath = resolvePath folder_abs, respath 89 | node = fs[nodepath] 90 | if not node then throw "Resource not found: #{nodepath}" 91 | return node 92 | listdir: (respath) -> 93 | if respath then nodepath = resolvepath folder_abs, respath 94 | else nodepath = folder_abs 95 | 96 | result = [] 97 | for name of fs 98 | match = name.match "#{folder_abs}/[a-zA-Z0-9-\.]+" 99 | if match 100 | match = match[0] 101 | if result.indexOf(match) == -1 102 | result.push match 103 | 104 | translated = [] 105 | for name in result 106 | if name.match /\.[a-z]+$/ 107 | translated.push name 108 | else 109 | translated.push folder name 110 | return translated 111 | } 112 | 113 | get.listdir = (respath, match) -> 114 | if respath 115 | abspath = resolvePath dirname, respath 116 | else 117 | abspath = dirname 118 | 119 | result = [] 120 | for name of fs 121 | if name.search(abspath) == 0 122 | if match 123 | if name.match match 124 | result.push name 125 | else 126 | result.push name 127 | return result 128 | 129 | fs[path] = 130 | path: path 131 | type: 'code' 132 | data: code 133 | create: -> 134 | @value = {} 135 | retval = code @value, require, get 136 | if retval 137 | @value = retval 138 | 139 | require: (modpath) -> 140 | abspath = resolvePath '/', modpath 141 | node = fs["#{abspath}.js"] 142 | if not node 143 | node = fs["#{abspath}/module.js"] 144 | 145 | if not node then throw "Module not found: #{abspath}" 146 | if !node.value then node.create() 147 | return node.value 148 | 149 | loadPack: ({url, progress, loaded}) -> 150 | files = {} 151 | hooks = @hooks 152 | getBuffer url, ((factor) -> if progress then progress(factor*0.5, 'network')), (data) -> 153 | decoding = 0 154 | decoded = 0 155 | 156 | 157 | doLoad = (name, info) -> 158 | if typeof info == 'object' and info.offset != undefined and info.size != undefined 159 | storage = new ArrayBuffer info.size 160 | dst = new Uint8Array storage 161 | src = new Uint8Array data, 8+length+info.offset, info.size 162 | dst.set src 163 | dst = dst.buffer 164 | 165 | if hooks 166 | for matcher, decode of hooks 167 | if name.match(matcher) 168 | decoding += 1 169 | decode dst, (result) -> 170 | decoded += 1 171 | files[name] = result 172 | if progress then progress(0.5+(decoded/decoding)*0.5, 'decode') 173 | if decoding == decoded and loaded then loaded(files) 174 | return 175 | files[name] = dst 176 | else 177 | if hooks 178 | for matcher, decode of hooks 179 | if name.match(matcher) 180 | decode info, (result) -> 181 | files[name] = result 182 | return 183 | files[name] = info 184 | 185 | length = new Uint32Array(data, 4, 1)[0] 186 | metadata = new Uint8Array(data, 8, length) 187 | result = '' 188 | for i in [0...length] 189 | result += String.fromCharCode(metadata[i]) 190 | result = JSON.parse(result) 191 | 192 | for name, info of result 193 | doLoad name, info, data 194 | 195 | if decoding == decoded and loaded then loaded(files) 196 | 197 | hooks: (@hooks) -> return @ 198 | 199 | mount: ({url, mountpoint, progress, loaded}) -> 200 | mountpoint ?= '/' 201 | @loadPack url: url, progress: progress, loaded: (data) -> 202 | for name, value of data 203 | fs[name] = value 204 | loaded() 205 | -------------------------------------------------------------------------------- /extra/loader.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | (function() { 3 | var fs, getBuffer, getJSON, isImage, makeBlob, makeURL, resolvePath; 4 | 5 | fs = {}; 6 | 7 | makeURL = function(blob) { 8 | return URL.createObjectURL(blob); 9 | }; 10 | 11 | makeBlob = function(data, type) { 12 | var builder; 13 | builder = new BlobBuilder(); 14 | builder.append(data); 15 | return builder.getBlob(type); 16 | }; 17 | 18 | window.getURL = function(data) { 19 | var blob; 20 | blob = makeBlob(data); 21 | return makeURL(blob); 22 | }; 23 | 24 | resolvePath = function(base, path) { 25 | if (path[0] === '/') { 26 | return path; 27 | } else { 28 | path = path.split('/'); 29 | if (base === '/') { 30 | base = ['']; 31 | } else { 32 | base = base.split('/'); 33 | } 34 | while (base.length > 0 && path.length > 0 && path[0] === '..') { 35 | base.pop(); 36 | path.shift(); 37 | } 38 | if (base.length === 0 || path.length === 0 || base[0] !== '') { 39 | throw "Invalid path: " + (base.join('/')) + "/" + (path.join('/')); 40 | } 41 | return "" + (base.join('/')) + "/" + (path.join('/')); 42 | } 43 | }; 44 | 45 | getJSON = function(url, callback) { 46 | var request; 47 | request = new XMLHttpRequest(); 48 | request.open('GET', url, true); 49 | request.onload = function() { 50 | return callback(JSON.parse(request.response)); 51 | }; 52 | return request.send(); 53 | }; 54 | 55 | getBuffer = function(url, progress, callback) { 56 | var request; 57 | request = new XMLHttpRequest(); 58 | request.open('GET', url, true); 59 | request.responseType = 'arraybuffer'; 60 | request.onload = function() { 61 | return callback(request.response); 62 | }; 63 | request.onprogress = function(event) { 64 | if (event.lengthComputable) { 65 | return progress(event.loaded / event.total); 66 | } 67 | }; 68 | return request.send(); 69 | }; 70 | 71 | isImage = function(path) { 72 | return path.match('\.jpg$|\.jpeg|\.gif$|\.png'); 73 | }; 74 | 75 | window.loader = { 76 | main: function() { 77 | var main; 78 | main = this.require('main'); 79 | if (main.main) { 80 | return main.main(); 81 | } else { 82 | throw 'Main function is not defined in main module.'; 83 | } 84 | }, 85 | define: function(path, code) { 86 | var dirname, folder, get, require; 87 | dirname = path.split('/'); 88 | dirname.pop(); 89 | dirname = dirname.join('/'); 90 | require = function(modpath) { 91 | var abspath, node; 92 | abspath = resolvePath(dirname, modpath); 93 | node = fs["" + abspath + ".js"]; 94 | if (!node) { 95 | node = fs["" + abspath + "/module.js"]; 96 | } 97 | if (!node) { 98 | throw "Module not found: " + abspath; 99 | } 100 | if (!node.value) { 101 | node.create(); 102 | } 103 | return node.value; 104 | }; 105 | get = function(respath) { 106 | var abspath, node; 107 | abspath = resolvePath(dirname, respath); 108 | node = fs[abspath]; 109 | if (!node) { 110 | throw "Resource not found: " + abspath; 111 | } 112 | return node; 113 | }; 114 | folder = get.folder = function(folderpath) { 115 | var folder_abs; 116 | folder_abs = resolvePath(dirname, folderpath); 117 | return { 118 | path: folder_abs, 119 | name: folder_abs.split('/')[folder_abs.split('/').length - 1], 120 | get: function(respath) { 121 | var node, nodepath; 122 | nodepath = resolvePath(folder_abs, respath); 123 | node = fs[nodepath]; 124 | if (!node) { 125 | throw "Resource not found: " + nodepath; 126 | } 127 | return node; 128 | }, 129 | listdir: function(respath) { 130 | var match, name, nodepath, result, translated, _i, _len; 131 | if (respath) { 132 | nodepath = resolvepath(folder_abs, respath); 133 | } else { 134 | nodepath = folder_abs; 135 | } 136 | result = []; 137 | for (name in fs) { 138 | match = name.match("" + folder_abs + "/[a-zA-Z0-9-\.]+"); 139 | if (match) { 140 | match = match[0]; 141 | if (result.indexOf(match) === -1) { 142 | result.push(match); 143 | } 144 | } 145 | } 146 | translated = []; 147 | for (_i = 0, _len = result.length; _i < _len; _i++) { 148 | name = result[_i]; 149 | if (name.match(/\.[a-z]+$/)) { 150 | translated.push(name); 151 | } else { 152 | translated.push(folder(name)); 153 | } 154 | } 155 | return translated; 156 | } 157 | }; 158 | }; 159 | get.listdir = function(respath, match) { 160 | var abspath, name, result; 161 | if (respath) { 162 | abspath = resolvePath(dirname, respath); 163 | } else { 164 | abspath = dirname; 165 | } 166 | result = []; 167 | for (name in fs) { 168 | if (name.search(abspath) === 0) { 169 | if (match) { 170 | if (name.match(match)) { 171 | result.push(name); 172 | } 173 | } else { 174 | result.push(name); 175 | } 176 | } 177 | } 178 | return result; 179 | }; 180 | return fs[path] = { 181 | path: path, 182 | type: 'code', 183 | data: code, 184 | create: function() { 185 | var retval; 186 | this.value = {}; 187 | retval = code(this.value, require, get); 188 | if (retval) { 189 | return this.value = retval; 190 | } 191 | } 192 | }; 193 | }, 194 | require: function(modpath) { 195 | var abspath, node; 196 | abspath = resolvePath('/', modpath); 197 | node = fs["" + abspath + ".js"]; 198 | if (!node) { 199 | node = fs["" + abspath + "/module.js"]; 200 | } 201 | if (!node) { 202 | throw "Module not found: " + abspath; 203 | } 204 | if (!node.value) { 205 | node.create(); 206 | } 207 | return node.value; 208 | }, 209 | loadPack: function(_arg) { 210 | var files, hooks, loaded, progress, url; 211 | url = _arg.url, progress = _arg.progress, loaded = _arg.loaded; 212 | files = {}; 213 | hooks = this.hooks; 214 | return getBuffer(url, (function(factor) { 215 | if (progress) { 216 | return progress(factor * 0.5, 'network'); 217 | } 218 | }), function(data) { 219 | var decoded, decoding, doLoad, i, info, length, metadata, name, result, _i; 220 | decoding = 0; 221 | decoded = 0; 222 | doLoad = function(name, info) { 223 | var decode, dst, matcher, src, storage; 224 | if (typeof info === 'object' && info.offset !== void 0 && info.size !== void 0) { 225 | storage = new ArrayBuffer(info.size); 226 | dst = new Uint8Array(storage); 227 | src = new Uint8Array(data, 8 + length + info.offset, info.size); 228 | dst.set(src); 229 | dst = dst.buffer; 230 | if (hooks) { 231 | for (matcher in hooks) { 232 | decode = hooks[matcher]; 233 | if (name.match(matcher)) { 234 | decoding += 1; 235 | decode(dst, function(result) { 236 | decoded += 1; 237 | files[name] = result; 238 | if (progress) { 239 | progress(0.5 + (decoded / decoding) * 0.5, 'decode'); 240 | } 241 | if (decoding === decoded && loaded) { 242 | return loaded(files); 243 | } 244 | }); 245 | return; 246 | } 247 | } 248 | } 249 | return files[name] = dst; 250 | } else { 251 | if (hooks) { 252 | for (matcher in hooks) { 253 | decode = hooks[matcher]; 254 | if (name.match(matcher)) { 255 | decode(info, function(result) { 256 | return files[name] = result; 257 | }); 258 | return; 259 | } 260 | } 261 | } 262 | return files[name] = info; 263 | } 264 | }; 265 | length = new Uint32Array(data, 4, 1)[0]; 266 | metadata = new Uint8Array(data, 8, length); 267 | result = ''; 268 | for (i = _i = 0; 0 <= length ? _i < length : _i > length; i = 0 <= length ? ++_i : --_i) { 269 | result += String.fromCharCode(metadata[i]); 270 | } 271 | result = JSON.parse(result); 272 | for (name in result) { 273 | info = result[name]; 274 | doLoad(name, info, data); 275 | } 276 | if (decoding === decoded && loaded) { 277 | return loaded(files); 278 | } 279 | }); 280 | }, 281 | hooks: function(hooks) { 282 | this.hooks = hooks; 283 | return this; 284 | }, 285 | mount: function(_arg) { 286 | var loaded, mountpoint, progress, url; 287 | url = _arg.url, mountpoint = _arg.mountpoint, progress = _arg.progress, loaded = _arg.loaded; 288 | if (mountpoint == null) { 289 | mountpoint = '/'; 290 | } 291 | return this.loadPack({ 292 | url: url, 293 | progress: progress, 294 | loaded: function(data) { 295 | var name, value; 296 | for (name in data) { 297 | value = data[name]; 298 | fs[name] = value; 299 | } 300 | return loaded(); 301 | } 302 | }); 303 | } 304 | }; 305 | 306 | }).call(this); 307 | -------------------------------------------------------------------------------- /extra/matrix.coffee: -------------------------------------------------------------------------------- 1 | pi = Math.PI 2 | tau = 2*pi 3 | deg = 360/tau 4 | arc = tau/360 5 | 6 | window.Mat3 = class Mat3 7 | constructor: (@data) -> 8 | @data ?= new Float32Array 9 9 | @identity() 10 | 11 | identity: -> 12 | d = @data 13 | d[0] = 1; d[1] =0; d[2] = 0 14 | d[3] = 0; d[4] =1; d[5] = 0 15 | d[6] = 0; d[7] =0; d[8] = 1 16 | return @ 17 | 18 | transpose: -> 19 | d = @data 20 | a01 = d[1]; a02 = d[2]; a12 = d[5] 21 | 22 | d[1] = d[3] 23 | d[2] = d[6] 24 | d[3] = a01 25 | d[5] = d[7] 26 | d[6] = a02 27 | d[7] = a12 28 | return @ 29 | 30 | mulVec3: (vec, dst=vec) -> 31 | @mulVal3 vec.x, vec.y, vec.z, dst 32 | return dst 33 | 34 | mulVal3: (x, y, z, dst) -> 35 | dst = dst.data 36 | d = @data 37 | dst[0] = d[0]*x + d[3]*y + d[6]*z 38 | dst[1] = d[1]*x + d[4]*y + d[7]*z 39 | dst[2] = d[2]*x + d[5]*y + d[8]*z 40 | 41 | return @ 42 | 43 | rotatex: (angle) -> 44 | s = Math.sin angle*arc 45 | c = Math.cos angle*arc 46 | return @amul( 47 | 1, 0, 0, 48 | 0, c, s, 49 | 0, -s, c 50 | ) 51 | 52 | rotatey: (angle) -> 53 | s = Math.sin angle*arc 54 | c = Math.cos angle*arc 55 | return @amul( 56 | c, 0, -s, 57 | 0, 1, 0, 58 | s, 0, c 59 | ) 60 | 61 | rotatez: (angle) -> 62 | s = Math.sin angle*arc 63 | c = Math.cos angle*arc 64 | return @amul( 65 | c, s, 0, 66 | -s, c, 0, 67 | 0, 0, 1 68 | ) 69 | 70 | amul: ( 71 | b00, b10, b20, 72 | b01, b11, b21, 73 | b02, b12, b22, 74 | b03, b13, b23 75 | ) -> 76 | a = @data 77 | 78 | a00 = a[0] 79 | a10 = a[1] 80 | a20 = a[2] 81 | 82 | a01 = a[3] 83 | a11 = a[4] 84 | a21 = a[5] 85 | 86 | a02 = a[6] 87 | a12 = a[7] 88 | a22 = a[8] 89 | 90 | a[0] = a00*b00 + a01*b10 + a02*b20 91 | a[1] = a10*b00 + a11*b10 + a12*b20 92 | a[2] = a20*b00 + a21*b10 + a22*b20 93 | 94 | a[3] = a00*b01 + a01*b11 + a02*b21 95 | a[4] = a10*b01 + a11*b11 + a12*b21 96 | a[5] = a20*b01 + a21*b11 + a22*b21 97 | 98 | a[6] = a00*b02 + a01*b12 + a02*b22 99 | a[7] = a10*b02 + a11*b12 + a12*b22 100 | a[8] = a20*b02 + a21*b12 + a22*b22 101 | 102 | return @ 103 | 104 | window.Mat4 = class Mat4 105 | constructor: (@data) -> 106 | @data ?= new Float32Array 16 107 | @identity() 108 | 109 | identity: -> 110 | d = @data 111 | d[0] = 1; d[1] =0; d[2] = 0; d[3] = 0 112 | d[4] = 0; d[5] =1; d[6] = 0; d[7] = 0 113 | d[8] = 0; d[9] =0; d[10] = 1; d[11] = 0 114 | d[12] = 0; d[13] =0; d[14] = 0; d[15] = 1 115 | return @ 116 | 117 | zero: -> 118 | d = @data 119 | d[0] = 0; d[1] =0; d[2] = 0; d[3] = 0 120 | d[4] = 0; d[5] =0; d[6] = 0; d[7] = 0 121 | d[8] = 0; d[9] =0; d[10] = 0; d[11] = 0 122 | d[12] = 0; d[13] =0; d[14] = 0; d[15] = 0 123 | return @ 124 | 125 | copy: (dest) -> 126 | src = @data 127 | dst = dest.data 128 | dst[0] = src[0] 129 | dst[1] = src[1] 130 | dst[2] = src[2] 131 | dst[3] = src[3] 132 | dst[4] = src[4] 133 | dst[5] = src[5] 134 | dst[6] = src[6] 135 | dst[7] = src[7] 136 | dst[8] = src[8] 137 | dst[9] = src[9] 138 | dst[10] = src[10] 139 | dst[11] = src[11] 140 | dst[12] = src[12] 141 | dst[13] = src[13] 142 | dst[14] = src[14] 143 | dst[15] = src[15] 144 | return dest 145 | 146 | toMat3: (dest) -> 147 | src = @data 148 | dst = dest.data 149 | dst[0] = src[0] 150 | dst[1] = src[1] 151 | dst[2] = src[2] 152 | dst[3] = src[4] 153 | dst[4] = src[5] 154 | dst[5] = src[6] 155 | dst[6] = src[8] 156 | dst[7] = src[9] 157 | dst[8] = src[10] 158 | 159 | return dest 160 | 161 | toMat3Rot: (dest) -> 162 | dst = dest.data 163 | src = @data 164 | a00 = src[0]; a01 = src[1]; a02 = src[2] 165 | a10 = src[4]; a11 = src[5]; a12 = src[6] 166 | a20 = src[8]; a21 = src[9]; a22 = src[10] 167 | 168 | b01 = a22 * a11 - a12 * a21 169 | b11 = -a22 * a10 + a12 * a20 170 | b21 = a21 * a10 - a11 * a20 171 | 172 | d = a00 * b01 + a01 * b11 + a02 * b21 173 | id = 1 / d 174 | 175 | dst[0] = b01 * id 176 | dst[3] = (-a22 * a01 + a02 * a21) * id 177 | dst[6] = (a12 * a01 - a02 * a11) * id 178 | dst[1] = b11 * id 179 | dst[4] = (a22 * a00 - a02 * a20) * id 180 | dst[7] = (-a12 * a00 + a02 * a10) * id 181 | dst[2] = b21 * id 182 | dst[5] = (-a21 * a00 + a01 * a20) * id 183 | dst[8] = (a11 * a00 - a01 * a10) * id 184 | 185 | return dest 186 | 187 | perspective: (fov, aspect, near, far) -> 188 | @zero() 189 | d = @data 190 | top = near * Math.tan(fov*Math.PI/360) 191 | right = top*aspect 192 | left = -right 193 | bottom = -top 194 | 195 | d[0] = (2*near)/(right-left) 196 | d[5] = (2*near)/(top-bottom) 197 | d[8] = (right+left)/(right-left) 198 | d[9] = (top+bottom)/(top-bottom) 199 | d[10] = -(far+near)/(far-near) 200 | d[11] = -1 201 | d[14] = -(2*far*near)/(far-near) 202 | 203 | return @ 204 | 205 | inversePerspective: (fov, aspect, near, far) -> 206 | @zero() 207 | dst = @data 208 | top = near * Math.tan(fov*Math.PI/360) 209 | right = top*aspect 210 | left = -right 211 | bottom = -top 212 | 213 | dst[0] = (right-left)/(2*near) 214 | dst[5] = (top-bottom)/(2*near) 215 | dst[11] = -(far-near)/(2*far*near) 216 | dst[12] = (right+left)/(2*near) 217 | dst[13] = (top+bottom)/(2*near) 218 | dst[14] = -1 219 | dst[15] = (far+near)/(2*far*near) 220 | 221 | return @ 222 | 223 | ortho: (near=-1, far=1, top=-1, bottom=1, left=-1, right=1) -> 224 | a = 2/(right-left) 225 | b = -((right+left)/(right-left)) 226 | c = 2/(top-bottom) 227 | d = -((top+bottom)/(top-bottom)) 228 | e = -2/(far-near) 229 | f = -((far+near)/(far-near)) 230 | g = 1 231 | 232 | return @set( 233 | a, 0, 0, b, 234 | 0, c, 0, d, 235 | 0, 0, e, f, 236 | 0, 0, 0, g 237 | ) 238 | 239 | inverseOrtho: (near=-1, far=1, top=-1, bottom=1, left=-1, right=1) -> 240 | a = (right-left)/2 241 | b = (right+left)/2 242 | c = (top-bottom)/2 243 | d = (top+bottom)/2 244 | e = (far-near)/-2 245 | f = (near+far)/2 246 | g = 1 247 | 248 | return @set( 249 | a, 0, 0, b, 250 | 0, c, 0, d, 251 | 0, 0, e, f, 252 | 0, 0, 0, g 253 | ) 254 | 255 | fromRotationTranslation: (quat, vec) -> 256 | x = quat.x; y = quat.y; z = quat.z; w = quat.w 257 | x2 = x + x 258 | y2 = y + y 259 | z2 = z + z 260 | 261 | xx = x * x2 262 | xy = x * y2 263 | xz = x * z2 264 | yy = y * y2 265 | yz = y * z2 266 | zz = z * z2 267 | wx = w * x2 268 | wy = w * y2 269 | wz = w * z2 270 | 271 | dest = @data 272 | 273 | ''' 274 | dest[0] = 1 - 2.0 * y * y - 2.0 * y * y 275 | dest[1] = 2 * x * y - 2.0 * w * z 276 | dest[3] = 2 * x * z + 2.0 * w * y 277 | 278 | dest[4] = 2 * x * y + 2.0 * w * z 279 | dest[5] = 1 - 2.0 * x * x - 2.0 * z * z 280 | dest[6] = 2 * y * z - 2.0 * w * x 281 | 282 | dest[8] = 2 * x * z - 2.0 * w * y 283 | dest[9] = 2 * y * z + 2.0 * w * x 284 | dest[10] = 1 - 2.0 * x * x - 2.0 * y * y 285 | ''' 286 | 287 | ''' 288 | dest[0] = 1 - 2.0 * y * y - 2.0 * y * y 289 | dest[1] = 2 * x * y + 2.0 * w * z 290 | dest[2] = 2 * x * z - 2.0 * w * y 291 | 292 | dest[4] = 2 * x * y - 2.0 * w * z 293 | dest[5] = 1 - 2.0 * x * x - 2.0 * z * z 294 | dest[6] = 2 * y * z + 2.0 * w * x 295 | 296 | dest[8] = 2 * x * z + 2.0 * w * y 297 | dest[9] = 2 * y * z - 2.0 * w * x 298 | dest[10] = 1 - 2.0 * x * x - 2.0 * y * y 299 | ''' 300 | 301 | dest[0] = 1 - (yy + zz) 302 | dest[1] = xy + wz 303 | dest[2] = xz - wy 304 | dest[3] = 0 305 | dest[4] = xy - wz 306 | dest[5] = 1 - (xx + zz) 307 | dest[6] = yz + wx 308 | dest[7] = 0 309 | dest[8] = xz + wy 310 | dest[9] = yz - wx 311 | dest[10] = 1 - (xx + yy) 312 | dest[11] = 0 313 | 314 | dest[12] = vec.x 315 | dest[13] = vec.y 316 | dest[14] = vec.z 317 | dest[15] = 1 318 | 319 | return @ 320 | 321 | translateVec3: (vec) -> 322 | return @translateVal3 vec.x, vec.y, vec.z 323 | 324 | translateVal3: (x, y, z) -> 325 | d = @data 326 | a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3] 327 | a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7] 328 | a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11] 329 | 330 | d[12] = a00 * x + a10 * y + a20 * z + d[12] 331 | d[13] = a01 * x + a11 * y + a21 * z + d[13] 332 | d[14] = a02 * x + a12 * y + a22 * z + d[14] 333 | d[15] = a03 * x + a13 * y + a23 * z + d[15] 334 | 335 | return @ 336 | 337 | rotatex: (angle) -> 338 | d = @data 339 | rad = tau*(angle/360) 340 | s = Math.sin rad 341 | c = Math.cos rad 342 | 343 | a10 = d[4] 344 | a11 = d[5] 345 | a12 = d[6] 346 | a13 = d[7] 347 | a20 = d[8] 348 | a21 = d[9] 349 | a22 = d[10] 350 | a23 = d[11] 351 | 352 | d[4] = a10 * c + a20 * s 353 | d[5] = a11 * c + a21 * s 354 | d[6] = a12 * c + a22 * s 355 | d[7] = a13 * c + a23 * s 356 | 357 | d[8] = a10 * -s + a20 * c 358 | d[9] = a11 * -s + a21 * c 359 | d[10] = a12 * -s + a22 * c 360 | d[11] = a13 * -s + a23 * c 361 | 362 | return @ 363 | 364 | rotatey: (angle) -> 365 | d = @data 366 | rad = tau*(angle/360) 367 | s = Math.sin rad 368 | c = Math.cos rad 369 | 370 | a00 = d[0] 371 | a01 = d[1] 372 | a02 = d[2] 373 | a03 = d[3] 374 | a20 = d[8] 375 | a21 = d[9] 376 | a22 = d[10] 377 | a23 = d[11] 378 | 379 | d[0] = a00 * c + a20 * -s 380 | d[1] = a01 * c + a21 * -s 381 | d[2] = a02 * c + a22 * -s 382 | d[3] = a03 * c + a23 * -s 383 | 384 | d[8] = a00 * s + a20 * c 385 | d[9] = a01 * s + a21 * c 386 | d[10] = a02 * s + a22 * c 387 | d[11] = a03 * s + a23 * c 388 | 389 | return @ 390 | 391 | rotatez: (angle) -> 392 | d = @data 393 | rad = tau*(angle/360) 394 | s = Math.sin rad 395 | c = Math.cos rad 396 | 397 | a00 = d[0] 398 | a01 = d[1] 399 | a02 = d[2] 400 | a03 = d[3] 401 | a10 = d[4] 402 | a11 = d[5] 403 | a12 = d[6] 404 | a13 = d[7] 405 | 406 | d[0] = a00 * c + a10 * s 407 | d[1] = a01 * c + a11 * s 408 | d[2] = a02 * c + a12 * s 409 | d[3] = a03 * c + a13 * s 410 | d[4] = a00 * -s + a10 * c 411 | d[5] = a01 * -s + a11 * c 412 | d[6] = a02 * -s + a12 * c 413 | d[7] = a03 * -s + a13 * c 414 | 415 | return @ 416 | 417 | scale: (scalar) -> 418 | d = @data 419 | 420 | a00 = d[0]; a01 = d[1]; a02 = d[2]; a03 = d[3] 421 | a10 = d[4]; a11 = d[5]; a12 = d[6]; a13 = d[7] 422 | a20 = d[8]; a21 = d[9]; a22 = d[10]; a23 = d[11] 423 | 424 | d[0] = a00 * scalar 425 | d[1] = a01 * scalar 426 | d[2] = a02 * scalar 427 | d[3] = a03 * scalar 428 | 429 | d[4] = a10 * scalar 430 | d[5] = a11 * scalar 431 | d[6] = a12 * scalar 432 | d[7] = a13 * scalar 433 | 434 | d[8] = a20 * scalar 435 | d[9] = a21 * scalar 436 | d[10] = a22 * scalar 437 | d[11] = a23 * scalar 438 | 439 | return @ 440 | 441 | mulMat4: (other, dst=@) -> 442 | dest = dst.data 443 | mat = @data 444 | mat2 = other.data 445 | 446 | a00 = mat[ 0]; a01 = mat[ 1]; a02 = mat[ 2]; a03 = mat[3] 447 | a10 = mat[ 4]; a11 = mat[ 5]; a12 = mat[ 6]; a13 = mat[7] 448 | a20 = mat[ 8]; a21 = mat[ 9]; a22 = mat[10]; a23 = mat[11] 449 | a30 = mat[12]; a31 = mat[13]; a32 = mat[14]; a33 = mat[15] 450 | 451 | b0 = mat2[0]; b1 = mat2[1]; b2 = mat2[2]; b3 = mat2[3] 452 | dest[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30 453 | dest[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31 454 | dest[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32 455 | dest[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33 456 | 457 | b0 = mat2[4] 458 | b1 = mat2[5] 459 | b2 = mat2[6] 460 | b3 = mat2[7] 461 | dest[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30 462 | dest[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31 463 | dest[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32 464 | dest[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33 465 | 466 | b0 = mat2[8] 467 | b1 = mat2[9] 468 | b2 = mat2[10] 469 | b3 = mat2[11] 470 | dest[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30 471 | dest[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31 472 | dest[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32 473 | dest[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33 474 | 475 | b0 = mat2[12] 476 | b1 = mat2[13] 477 | b2 = mat2[14] 478 | b3 = mat2[15] 479 | dest[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30 480 | dest[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31 481 | dest[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32 482 | dest[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33 483 | 484 | return dst 485 | 486 | mulVec3: (vec, dst=vec) -> 487 | return @mulVal3 vec.x, vec.y, vec.z, dst 488 | 489 | mulVal3: (x, y, z, dst) -> 490 | dst = dst.data 491 | d = @data 492 | dst[0] = d[0]*x + d[4]*y + d[8] *z 493 | dst[1] = d[1]*x + d[5]*y + d[9] *z 494 | dst[2] = d[2]*x + d[6]*y + d[10]*z 495 | 496 | return dst 497 | 498 | mulVec4: (vec, dst) -> 499 | dst ?= vec 500 | return @mulVal4 vec.x, vec.y, vec.z, vec.w, dst 501 | 502 | mulVal4: (x, y, z, w, dst) -> 503 | dst = dst.data 504 | d = @data 505 | dst[0] = d[0]*x + d[4]*y + d[8] *z + d[12]*w 506 | dst[1] = d[1]*x + d[5]*y + d[9] *z + d[13]*w 507 | dst[2] = d[2]*x + d[6]*y + d[10]*z + d[14]*w 508 | dst[3] = d[3]*x + d[7]*y + d[11]*z + d[15]*w 509 | 510 | return dst 511 | 512 | invert: (dst=@) -> 513 | mat = @data 514 | dest = dst.data 515 | 516 | a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3] 517 | a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7] 518 | a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11] 519 | a30 = mat[12]; a31 = mat[13]; a32 = mat[14]; a33 = mat[15] 520 | 521 | b00 = a00 * a11 - a01 * a10 522 | b01 = a00 * a12 - a02 * a10 523 | b02 = a00 * a13 - a03 * a10 524 | b03 = a01 * a12 - a02 * a11 525 | b04 = a01 * a13 - a03 * a11 526 | b05 = a02 * a13 - a03 * a12 527 | b06 = a20 * a31 - a21 * a30 528 | b07 = a20 * a32 - a22 * a30 529 | b08 = a20 * a33 - a23 * a30 530 | b09 = a21 * a32 - a22 * a31 531 | b10 = a21 * a33 - a23 * a31 532 | b11 = a22 * a33 - a23 * a32 533 | 534 | d = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06) 535 | 536 | if d==0 then return 537 | invDet = 1 / d 538 | 539 | dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet 540 | dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet 541 | dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet 542 | dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet 543 | dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet 544 | dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet 545 | dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet 546 | dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet 547 | dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet 548 | dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet 549 | dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet 550 | dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet 551 | dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet 552 | dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet 553 | dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet 554 | dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet 555 | 556 | return dst 557 | 558 | set: ( 559 | a00, a10, a20, a30, 560 | a01, a11, a21, a31, 561 | a02, a12, a22, a32, 562 | a03, a13, a23, a33, 563 | ) -> 564 | d = @data 565 | d[0]=a00; d[4]=a10; d[8]=a20; d[12]=a30 566 | d[1]=a01; d[5]=a11; d[9]=a21; d[13]=a31 567 | d[2]=a02; d[6]=a12; d[10]=a22; d[14]=a32 568 | d[3]=a03; d[7]=a13; d[11]=a23; d[15]=a33 569 | 570 | return @ 571 | -------------------------------------------------------------------------------- /extra/matrix.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | (function() { 3 | var Mat3, Mat4, arc, deg, pi, tau; 4 | 5 | pi = Math.PI; 6 | 7 | tau = 2 * pi; 8 | 9 | deg = 360 / tau; 10 | 11 | arc = tau / 360; 12 | 13 | window.Mat3 = Mat3 = (function() { 14 | 15 | function Mat3(data) { 16 | var _ref; 17 | this.data = data; 18 | if ((_ref = this.data) == null) { 19 | this.data = new Float32Array(9); 20 | } 21 | this.identity(); 22 | } 23 | 24 | Mat3.prototype.identity = function() { 25 | var d; 26 | d = this.data; 27 | d[0] = 1; 28 | d[1] = 0; 29 | d[2] = 0; 30 | d[3] = 0; 31 | d[4] = 1; 32 | d[5] = 0; 33 | d[6] = 0; 34 | d[7] = 0; 35 | d[8] = 1; 36 | return this; 37 | }; 38 | 39 | Mat3.prototype.transpose = function() { 40 | var a01, a02, a12, d; 41 | d = this.data; 42 | a01 = d[1]; 43 | a02 = d[2]; 44 | a12 = d[5]; 45 | d[1] = d[3]; 46 | d[2] = d[6]; 47 | d[3] = a01; 48 | d[5] = d[7]; 49 | d[6] = a02; 50 | d[7] = a12; 51 | return this; 52 | }; 53 | 54 | Mat3.prototype.mulVec3 = function(vec, dst) { 55 | if (dst == null) { 56 | dst = vec; 57 | } 58 | this.mulVal3(vec.x, vec.y, vec.z, dst); 59 | return dst; 60 | }; 61 | 62 | Mat3.prototype.mulVal3 = function(x, y, z, dst) { 63 | var d; 64 | dst = dst.data; 65 | d = this.data; 66 | dst[0] = d[0] * x + d[3] * y + d[6] * z; 67 | dst[1] = d[1] * x + d[4] * y + d[7] * z; 68 | dst[2] = d[2] * x + d[5] * y + d[8] * z; 69 | return this; 70 | }; 71 | 72 | Mat3.prototype.rotatex = function(angle) { 73 | var c, s; 74 | s = Math.sin(angle * arc); 75 | c = Math.cos(angle * arc); 76 | return this.amul(1, 0, 0, 0, c, s, 0, -s, c); 77 | }; 78 | 79 | Mat3.prototype.rotatey = function(angle) { 80 | var c, s; 81 | s = Math.sin(angle * arc); 82 | c = Math.cos(angle * arc); 83 | return this.amul(c, 0, -s, 0, 1, 0, s, 0, c); 84 | }; 85 | 86 | Mat3.prototype.rotatez = function(angle) { 87 | var c, s; 88 | s = Math.sin(angle * arc); 89 | c = Math.cos(angle * arc); 90 | return this.amul(c, s, 0, -s, c, 0, 0, 0, 1); 91 | }; 92 | 93 | Mat3.prototype.amul = function(b00, b10, b20, b01, b11, b21, b02, b12, b22, b03, b13, b23) { 94 | var a, a00, a01, a02, a10, a11, a12, a20, a21, a22; 95 | a = this.data; 96 | a00 = a[0]; 97 | a10 = a[1]; 98 | a20 = a[2]; 99 | a01 = a[3]; 100 | a11 = a[4]; 101 | a21 = a[5]; 102 | a02 = a[6]; 103 | a12 = a[7]; 104 | a22 = a[8]; 105 | a[0] = a00 * b00 + a01 * b10 + a02 * b20; 106 | a[1] = a10 * b00 + a11 * b10 + a12 * b20; 107 | a[2] = a20 * b00 + a21 * b10 + a22 * b20; 108 | a[3] = a00 * b01 + a01 * b11 + a02 * b21; 109 | a[4] = a10 * b01 + a11 * b11 + a12 * b21; 110 | a[5] = a20 * b01 + a21 * b11 + a22 * b21; 111 | a[6] = a00 * b02 + a01 * b12 + a02 * b22; 112 | a[7] = a10 * b02 + a11 * b12 + a12 * b22; 113 | a[8] = a20 * b02 + a21 * b12 + a22 * b22; 114 | return this; 115 | }; 116 | 117 | return Mat3; 118 | 119 | })(); 120 | 121 | window.Mat4 = Mat4 = (function() { 122 | 123 | function Mat4(data) { 124 | var _ref; 125 | this.data = data; 126 | if ((_ref = this.data) == null) { 127 | this.data = new Float32Array(16); 128 | } 129 | this.identity(); 130 | } 131 | 132 | Mat4.prototype.identity = function() { 133 | var d; 134 | d = this.data; 135 | d[0] = 1; 136 | d[1] = 0; 137 | d[2] = 0; 138 | d[3] = 0; 139 | d[4] = 0; 140 | d[5] = 1; 141 | d[6] = 0; 142 | d[7] = 0; 143 | d[8] = 0; 144 | d[9] = 0; 145 | d[10] = 1; 146 | d[11] = 0; 147 | d[12] = 0; 148 | d[13] = 0; 149 | d[14] = 0; 150 | d[15] = 1; 151 | return this; 152 | }; 153 | 154 | Mat4.prototype.zero = function() { 155 | var d; 156 | d = this.data; 157 | d[0] = 0; 158 | d[1] = 0; 159 | d[2] = 0; 160 | d[3] = 0; 161 | d[4] = 0; 162 | d[5] = 0; 163 | d[6] = 0; 164 | d[7] = 0; 165 | d[8] = 0; 166 | d[9] = 0; 167 | d[10] = 0; 168 | d[11] = 0; 169 | d[12] = 0; 170 | d[13] = 0; 171 | d[14] = 0; 172 | d[15] = 0; 173 | return this; 174 | }; 175 | 176 | Mat4.prototype.copy = function(dest) { 177 | var dst, src; 178 | src = this.data; 179 | dst = dest.data; 180 | dst[0] = src[0]; 181 | dst[1] = src[1]; 182 | dst[2] = src[2]; 183 | dst[3] = src[3]; 184 | dst[4] = src[4]; 185 | dst[5] = src[5]; 186 | dst[6] = src[6]; 187 | dst[7] = src[7]; 188 | dst[8] = src[8]; 189 | dst[9] = src[9]; 190 | dst[10] = src[10]; 191 | dst[11] = src[11]; 192 | dst[12] = src[12]; 193 | dst[13] = src[13]; 194 | dst[14] = src[14]; 195 | dst[15] = src[15]; 196 | return dest; 197 | }; 198 | 199 | Mat4.prototype.toMat3 = function(dest) { 200 | var dst, src; 201 | src = this.data; 202 | dst = dest.data; 203 | dst[0] = src[0]; 204 | dst[1] = src[1]; 205 | dst[2] = src[2]; 206 | dst[3] = src[4]; 207 | dst[4] = src[5]; 208 | dst[5] = src[6]; 209 | dst[6] = src[8]; 210 | dst[7] = src[9]; 211 | dst[8] = src[10]; 212 | return dest; 213 | }; 214 | 215 | Mat4.prototype.toMat3Rot = function(dest) { 216 | var a00, a01, a02, a10, a11, a12, a20, a21, a22, b01, b11, b21, d, dst, id, src; 217 | dst = dest.data; 218 | src = this.data; 219 | a00 = src[0]; 220 | a01 = src[1]; 221 | a02 = src[2]; 222 | a10 = src[4]; 223 | a11 = src[5]; 224 | a12 = src[6]; 225 | a20 = src[8]; 226 | a21 = src[9]; 227 | a22 = src[10]; 228 | b01 = a22 * a11 - a12 * a21; 229 | b11 = -a22 * a10 + a12 * a20; 230 | b21 = a21 * a10 - a11 * a20; 231 | d = a00 * b01 + a01 * b11 + a02 * b21; 232 | id = 1 / d; 233 | dst[0] = b01 * id; 234 | dst[3] = (-a22 * a01 + a02 * a21) * id; 235 | dst[6] = (a12 * a01 - a02 * a11) * id; 236 | dst[1] = b11 * id; 237 | dst[4] = (a22 * a00 - a02 * a20) * id; 238 | dst[7] = (-a12 * a00 + a02 * a10) * id; 239 | dst[2] = b21 * id; 240 | dst[5] = (-a21 * a00 + a01 * a20) * id; 241 | dst[8] = (a11 * a00 - a01 * a10) * id; 242 | return dest; 243 | }; 244 | 245 | Mat4.prototype.perspective = function(fov, aspect, near, far) { 246 | var bottom, d, left, right, top; 247 | this.zero(); 248 | d = this.data; 249 | top = near * Math.tan(fov * Math.PI / 360); 250 | right = top * aspect; 251 | left = -right; 252 | bottom = -top; 253 | d[0] = (2 * near) / (right - left); 254 | d[5] = (2 * near) / (top - bottom); 255 | d[8] = (right + left) / (right - left); 256 | d[9] = (top + bottom) / (top - bottom); 257 | d[10] = -(far + near) / (far - near); 258 | d[11] = -1; 259 | d[14] = -(2 * far * near) / (far - near); 260 | return this; 261 | }; 262 | 263 | Mat4.prototype.inversePerspective = function(fov, aspect, near, far) { 264 | var bottom, dst, left, right, top; 265 | this.zero(); 266 | dst = this.data; 267 | top = near * Math.tan(fov * Math.PI / 360); 268 | right = top * aspect; 269 | left = -right; 270 | bottom = -top; 271 | dst[0] = (right - left) / (2 * near); 272 | dst[5] = (top - bottom) / (2 * near); 273 | dst[11] = -(far - near) / (2 * far * near); 274 | dst[12] = (right + left) / (2 * near); 275 | dst[13] = (top + bottom) / (2 * near); 276 | dst[14] = -1; 277 | dst[15] = (far + near) / (2 * far * near); 278 | return this; 279 | }; 280 | 281 | Mat4.prototype.ortho = function(near, far, top, bottom, left, right) { 282 | var a, b, c, d, e, f, g; 283 | if (near == null) { 284 | near = -1; 285 | } 286 | if (far == null) { 287 | far = 1; 288 | } 289 | if (top == null) { 290 | top = -1; 291 | } 292 | if (bottom == null) { 293 | bottom = 1; 294 | } 295 | if (left == null) { 296 | left = -1; 297 | } 298 | if (right == null) { 299 | right = 1; 300 | } 301 | a = 2 / (right - left); 302 | b = -((right + left) / (right - left)); 303 | c = 2 / (top - bottom); 304 | d = -((top + bottom) / (top - bottom)); 305 | e = -2 / (far - near); 306 | f = -((far + near) / (far - near)); 307 | g = 1; 308 | return this.set(a, 0, 0, b, 0, c, 0, d, 0, 0, e, f, 0, 0, 0, g); 309 | }; 310 | 311 | Mat4.prototype.inverseOrtho = function(near, far, top, bottom, left, right) { 312 | var a, b, c, d, e, f, g; 313 | if (near == null) { 314 | near = -1; 315 | } 316 | if (far == null) { 317 | far = 1; 318 | } 319 | if (top == null) { 320 | top = -1; 321 | } 322 | if (bottom == null) { 323 | bottom = 1; 324 | } 325 | if (left == null) { 326 | left = -1; 327 | } 328 | if (right == null) { 329 | right = 1; 330 | } 331 | a = (right - left) / 2; 332 | b = (right + left) / 2; 333 | c = (top - bottom) / 2; 334 | d = (top + bottom) / 2; 335 | e = (far - near) / -2; 336 | f = (near + far) / 2; 337 | g = 1; 338 | return this.set(a, 0, 0, b, 0, c, 0, d, 0, 0, e, f, 0, 0, 0, g); 339 | }; 340 | 341 | Mat4.prototype.fromRotationTranslation = function(quat, vec) { 342 | var dest, w, wx, wy, wz, x, x2, xx, xy, xz, y, y2, yy, yz, z, z2, zz; 343 | x = quat.x; 344 | y = quat.y; 345 | z = quat.z; 346 | w = quat.w; 347 | x2 = x + x; 348 | y2 = y + y; 349 | z2 = z + z; 350 | xx = x * x2; 351 | xy = x * y2; 352 | xz = x * z2; 353 | yy = y * y2; 354 | yz = y * z2; 355 | zz = z * z2; 356 | wx = w * x2; 357 | wy = w * y2; 358 | wz = w * z2; 359 | dest = this.data; 360 | ' \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'; 361 | 362 | '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'; 363 | 364 | dest[0] = 1 - (yy + zz); 365 | dest[1] = xy + wz; 366 | dest[2] = xz - wy; 367 | dest[3] = 0; 368 | dest[4] = xy - wz; 369 | dest[5] = 1 - (xx + zz); 370 | dest[6] = yz + wx; 371 | dest[7] = 0; 372 | dest[8] = xz + wy; 373 | dest[9] = yz - wx; 374 | dest[10] = 1 - (xx + yy); 375 | dest[11] = 0; 376 | dest[12] = vec.x; 377 | dest[13] = vec.y; 378 | dest[14] = vec.z; 379 | dest[15] = 1; 380 | return this; 381 | }; 382 | 383 | Mat4.prototype.translateVec3 = function(vec) { 384 | return this.translateVal3(vec.x, vec.y, vec.z); 385 | }; 386 | 387 | Mat4.prototype.translateVal3 = function(x, y, z) { 388 | var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, d; 389 | d = this.data; 390 | a00 = d[0]; 391 | a01 = d[1]; 392 | a02 = d[2]; 393 | a03 = d[3]; 394 | a10 = d[4]; 395 | a11 = d[5]; 396 | a12 = d[6]; 397 | a13 = d[7]; 398 | a20 = d[8]; 399 | a21 = d[9]; 400 | a22 = d[10]; 401 | a23 = d[11]; 402 | d[12] = a00 * x + a10 * y + a20 * z + d[12]; 403 | d[13] = a01 * x + a11 * y + a21 * z + d[13]; 404 | d[14] = a02 * x + a12 * y + a22 * z + d[14]; 405 | d[15] = a03 * x + a13 * y + a23 * z + d[15]; 406 | return this; 407 | }; 408 | 409 | Mat4.prototype.rotatex = function(angle) { 410 | var a10, a11, a12, a13, a20, a21, a22, a23, c, d, rad, s; 411 | d = this.data; 412 | rad = tau * (angle / 360); 413 | s = Math.sin(rad); 414 | c = Math.cos(rad); 415 | a10 = d[4]; 416 | a11 = d[5]; 417 | a12 = d[6]; 418 | a13 = d[7]; 419 | a20 = d[8]; 420 | a21 = d[9]; 421 | a22 = d[10]; 422 | a23 = d[11]; 423 | d[4] = a10 * c + a20 * s; 424 | d[5] = a11 * c + a21 * s; 425 | d[6] = a12 * c + a22 * s; 426 | d[7] = a13 * c + a23 * s; 427 | d[8] = a10 * -s + a20 * c; 428 | d[9] = a11 * -s + a21 * c; 429 | d[10] = a12 * -s + a22 * c; 430 | d[11] = a13 * -s + a23 * c; 431 | return this; 432 | }; 433 | 434 | Mat4.prototype.rotatey = function(angle) { 435 | var a00, a01, a02, a03, a20, a21, a22, a23, c, d, rad, s; 436 | d = this.data; 437 | rad = tau * (angle / 360); 438 | s = Math.sin(rad); 439 | c = Math.cos(rad); 440 | a00 = d[0]; 441 | a01 = d[1]; 442 | a02 = d[2]; 443 | a03 = d[3]; 444 | a20 = d[8]; 445 | a21 = d[9]; 446 | a22 = d[10]; 447 | a23 = d[11]; 448 | d[0] = a00 * c + a20 * -s; 449 | d[1] = a01 * c + a21 * -s; 450 | d[2] = a02 * c + a22 * -s; 451 | d[3] = a03 * c + a23 * -s; 452 | d[8] = a00 * s + a20 * c; 453 | d[9] = a01 * s + a21 * c; 454 | d[10] = a02 * s + a22 * c; 455 | d[11] = a03 * s + a23 * c; 456 | return this; 457 | }; 458 | 459 | Mat4.prototype.rotatez = function(angle) { 460 | var a00, a01, a02, a03, a10, a11, a12, a13, c, d, rad, s; 461 | d = this.data; 462 | rad = tau * (angle / 360); 463 | s = Math.sin(rad); 464 | c = Math.cos(rad); 465 | a00 = d[0]; 466 | a01 = d[1]; 467 | a02 = d[2]; 468 | a03 = d[3]; 469 | a10 = d[4]; 470 | a11 = d[5]; 471 | a12 = d[6]; 472 | a13 = d[7]; 473 | d[0] = a00 * c + a10 * s; 474 | d[1] = a01 * c + a11 * s; 475 | d[2] = a02 * c + a12 * s; 476 | d[3] = a03 * c + a13 * s; 477 | d[4] = a00 * -s + a10 * c; 478 | d[5] = a01 * -s + a11 * c; 479 | d[6] = a02 * -s + a12 * c; 480 | d[7] = a03 * -s + a13 * c; 481 | return this; 482 | }; 483 | 484 | Mat4.prototype.scale = function(scalar) { 485 | var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, d; 486 | d = this.data; 487 | a00 = d[0]; 488 | a01 = d[1]; 489 | a02 = d[2]; 490 | a03 = d[3]; 491 | a10 = d[4]; 492 | a11 = d[5]; 493 | a12 = d[6]; 494 | a13 = d[7]; 495 | a20 = d[8]; 496 | a21 = d[9]; 497 | a22 = d[10]; 498 | a23 = d[11]; 499 | d[0] = a00 * scalar; 500 | d[1] = a01 * scalar; 501 | d[2] = a02 * scalar; 502 | d[3] = a03 * scalar; 503 | d[4] = a10 * scalar; 504 | d[5] = a11 * scalar; 505 | d[6] = a12 * scalar; 506 | d[7] = a13 * scalar; 507 | d[8] = a20 * scalar; 508 | d[9] = a21 * scalar; 509 | d[10] = a22 * scalar; 510 | d[11] = a23 * scalar; 511 | return this; 512 | }; 513 | 514 | Mat4.prototype.mulMat4 = function(other, dst) { 515 | var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33, b0, b1, b2, b3, dest, mat, mat2; 516 | if (dst == null) { 517 | dst = this; 518 | } 519 | dest = dst.data; 520 | mat = this.data; 521 | mat2 = other.data; 522 | a00 = mat[0]; 523 | a01 = mat[1]; 524 | a02 = mat[2]; 525 | a03 = mat[3]; 526 | a10 = mat[4]; 527 | a11 = mat[5]; 528 | a12 = mat[6]; 529 | a13 = mat[7]; 530 | a20 = mat[8]; 531 | a21 = mat[9]; 532 | a22 = mat[10]; 533 | a23 = mat[11]; 534 | a30 = mat[12]; 535 | a31 = mat[13]; 536 | a32 = mat[14]; 537 | a33 = mat[15]; 538 | b0 = mat2[0]; 539 | b1 = mat2[1]; 540 | b2 = mat2[2]; 541 | b3 = mat2[3]; 542 | dest[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; 543 | dest[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; 544 | dest[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; 545 | dest[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; 546 | b0 = mat2[4]; 547 | b1 = mat2[5]; 548 | b2 = mat2[6]; 549 | b3 = mat2[7]; 550 | dest[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; 551 | dest[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; 552 | dest[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; 553 | dest[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; 554 | b0 = mat2[8]; 555 | b1 = mat2[9]; 556 | b2 = mat2[10]; 557 | b3 = mat2[11]; 558 | dest[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; 559 | dest[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; 560 | dest[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; 561 | dest[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; 562 | b0 = mat2[12]; 563 | b1 = mat2[13]; 564 | b2 = mat2[14]; 565 | b3 = mat2[15]; 566 | dest[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; 567 | dest[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; 568 | dest[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; 569 | dest[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; 570 | return dst; 571 | }; 572 | 573 | Mat4.prototype.mulVec3 = function(vec, dst) { 574 | if (dst == null) { 575 | dst = vec; 576 | } 577 | return this.mulVal3(vec.x, vec.y, vec.z, dst); 578 | }; 579 | 580 | Mat4.prototype.mulVal3 = function(x, y, z, dst) { 581 | var d; 582 | dst = dst.data; 583 | d = this.data; 584 | dst[0] = d[0] * x + d[4] * y + d[8] * z; 585 | dst[1] = d[1] * x + d[5] * y + d[9] * z; 586 | dst[2] = d[2] * x + d[6] * y + d[10] * z; 587 | return dst; 588 | }; 589 | 590 | Mat4.prototype.mulVec4 = function(vec, dst) { 591 | if (dst == null) { 592 | dst = vec; 593 | } 594 | return this.mulVal4(vec.x, vec.y, vec.z, vec.w, dst); 595 | }; 596 | 597 | Mat4.prototype.mulVal4 = function(x, y, z, w, dst) { 598 | var d; 599 | dst = dst.data; 600 | d = this.data; 601 | dst[0] = d[0] * x + d[4] * y + d[8] * z + d[12] * w; 602 | dst[1] = d[1] * x + d[5] * y + d[9] * z + d[13] * w; 603 | dst[2] = d[2] * x + d[6] * y + d[10] * z + d[14] * w; 604 | dst[3] = d[3] * x + d[7] * y + d[11] * z + d[15] * w; 605 | return dst; 606 | }; 607 | 608 | Mat4.prototype.invert = function(dst) { 609 | 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; 610 | if (dst == null) { 611 | dst = this; 612 | } 613 | mat = this.data; 614 | dest = dst.data; 615 | a00 = mat[0]; 616 | a01 = mat[1]; 617 | a02 = mat[2]; 618 | a03 = mat[3]; 619 | a10 = mat[4]; 620 | a11 = mat[5]; 621 | a12 = mat[6]; 622 | a13 = mat[7]; 623 | a20 = mat[8]; 624 | a21 = mat[9]; 625 | a22 = mat[10]; 626 | a23 = mat[11]; 627 | a30 = mat[12]; 628 | a31 = mat[13]; 629 | a32 = mat[14]; 630 | a33 = mat[15]; 631 | b00 = a00 * a11 - a01 * a10; 632 | b01 = a00 * a12 - a02 * a10; 633 | b02 = a00 * a13 - a03 * a10; 634 | b03 = a01 * a12 - a02 * a11; 635 | b04 = a01 * a13 - a03 * a11; 636 | b05 = a02 * a13 - a03 * a12; 637 | b06 = a20 * a31 - a21 * a30; 638 | b07 = a20 * a32 - a22 * a30; 639 | b08 = a20 * a33 - a23 * a30; 640 | b09 = a21 * a32 - a22 * a31; 641 | b10 = a21 * a33 - a23 * a31; 642 | b11 = a22 * a33 - a23 * a32; 643 | d = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; 644 | if (d === 0) { 645 | return; 646 | } 647 | invDet = 1 / d; 648 | dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; 649 | dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; 650 | dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; 651 | dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; 652 | dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; 653 | dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; 654 | dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; 655 | dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; 656 | dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; 657 | dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; 658 | dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; 659 | dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; 660 | dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; 661 | dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; 662 | dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; 663 | dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; 664 | return dst; 665 | }; 666 | 667 | Mat4.prototype.set = function(a00, a10, a20, a30, a01, a11, a21, a31, a02, a12, a22, a32, a03, a13, a23, a33) { 668 | var d; 669 | d = this.data; 670 | d[0] = a00; 671 | d[4] = a10; 672 | d[8] = a20; 673 | d[12] = a30; 674 | d[1] = a01; 675 | d[5] = a11; 676 | d[9] = a21; 677 | d[13] = a31; 678 | d[2] = a02; 679 | d[6] = a12; 680 | d[10] = a22; 681 | d[14] = a32; 682 | d[3] = a03; 683 | d[7] = a13; 684 | d[11] = a23; 685 | d[15] = a33; 686 | return this; 687 | }; 688 | 689 | return Mat4; 690 | 691 | })(); 692 | 693 | }).call(this); 694 | -------------------------------------------------------------------------------- /extra/shims.coffee: -------------------------------------------------------------------------------- 1 | if window.performance 2 | if window.performance.now 3 | now = -> window.performance.now() 4 | else if window.performance.webkitNow 5 | now = -> window.performance.webkitNow() 6 | else if window.performance.mozNow 7 | now = -> window.performance.mozNow() 8 | else if window.performance.oNow 9 | now = -> window.performance.oNow() 10 | else 11 | now = -> Date.now() 12 | else 13 | now = -> Date.now() 14 | 15 | start = now() 16 | window.gettime = -> (now() - start)/1000 17 | 18 | if not window.requestAnimationFrame 19 | if window.webkitRequestAnimationFrame 20 | window.requestAnimationFrame = window.webkitRequestAnimationFrame 21 | else if window.mozRequestAnimationFrame 22 | window.requestAnimationFrame = window.mozRequestAnimationFrame 23 | else if window.oRequestAnimationFrame 24 | window.requestAnimationFrame = window.oRequestAnimationFrame 25 | else 26 | window.requestAnimationFrame = (fun) -> 27 | setTimeout(fun, 1000/30) 28 | 29 | window.URL = window.URL or window.mozURL or window.webkitURL or window.oURL 30 | window.BlobBuilder = window.BlobBuilder or window.MozBlobBuilder or window.WebKitBlobBuilder or window.OBlobBuilder 31 | 32 | log_count = 0 33 | window.console.logN = (n) -> 34 | if log_count < n 35 | log_count += 1 36 | args = [].slice.call(arguments, 1) 37 | console.log.apply console, args 38 | -------------------------------------------------------------------------------- /extra/shims.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | (function() { 3 | var log_count, now, start; 4 | 5 | if (window.performance) { 6 | if (window.performance.now) { 7 | now = function() { 8 | return window.performance.now(); 9 | }; 10 | } else if (window.performance.webkitNow) { 11 | now = function() { 12 | return window.performance.webkitNow(); 13 | }; 14 | } else if (window.performance.mozNow) { 15 | now = function() { 16 | return window.performance.mozNow(); 17 | }; 18 | } else if (window.performance.oNow) { 19 | now = function() { 20 | return window.performance.oNow(); 21 | }; 22 | } else { 23 | now = function() { 24 | return Date.now(); 25 | }; 26 | } 27 | } else { 28 | now = function() { 29 | return Date.now(); 30 | }; 31 | } 32 | 33 | start = now(); 34 | 35 | window.gettime = function() { 36 | return (now() - start) / 1000; 37 | }; 38 | 39 | if (!window.requestAnimationFrame) { 40 | if (window.webkitRequestAnimationFrame) { 41 | window.requestAnimationFrame = window.webkitRequestAnimationFrame; 42 | } else if (window.mozRequestAnimationFrame) { 43 | window.requestAnimationFrame = window.mozRequestAnimationFrame; 44 | } else if (window.oRequestAnimationFrame) { 45 | window.requestAnimationFrame = window.oRequestAnimationFrame; 46 | } else { 47 | window.requestAnimationFrame = function(fun) { 48 | return setTimeout(fun, 1000 / 30); 49 | }; 50 | } 51 | } 52 | 53 | window.URL = window.URL || window.mozURL || window.webkitURL || window.oURL; 54 | 55 | window.BlobBuilder = window.BlobBuilder || window.MozBlobBuilder || window.WebKitBlobBuilder || window.OBlobBuilder; 56 | 57 | log_count = 0; 58 | 59 | window.console.logN = function(n) { 60 | var args; 61 | if (log_count < n) { 62 | log_count += 1; 63 | args = [].slice.call(arguments, 1); 64 | return console.log.apply(console, args); 65 | } 66 | }; 67 | 68 | }).call(this); 69 | -------------------------------------------------------------------------------- /extra/vector.coffee: -------------------------------------------------------------------------------- 1 | window.Vec3 = class Vec3 2 | @property 'x' 3 | get: -> @data[0] 4 | set: (val) -> @data[0] = val 5 | @property 'y' 6 | get: -> @data[1] 7 | set: (val) -> @data[1] = val 8 | @property 'z' 9 | get: -> @data[2] 10 | set: (val) -> @data[2] = val 11 | 12 | @property 'length' 13 | get: -> Math.sqrt(@x*@x + @y*@y + @z*@z) 14 | 15 | constructor: (@data) -> 16 | @data ?= new Float32Array 3 17 | 18 | sub: (other, dst=@) -> 19 | dst.x = @x - other.x 20 | dst.y = @y - other.y 21 | dst.z = @z - other.z 22 | return dst 23 | 24 | add: (other, dst=@) -> 25 | dst.x = @x + other.x 26 | dst.y = @y + other.y 27 | dst.z = @z + other.z 28 | return dst 29 | 30 | addVal3: (x, y, z, dst=@) -> 31 | dst.x = @x + x 32 | dst.y = @y + y 33 | dst.z = @z + z 34 | return dst 35 | 36 | mul: (scalar, dst=@) -> 37 | dst.x = @x * scalar 38 | dst.y = @y * scalar 39 | dst.z = @z * scalar 40 | return dst 41 | 42 | div: (scalar, dst=@) -> 43 | dst.x = @x / scalar 44 | dst.y = @y / scalar 45 | dst.z = @z / scalar 46 | return dst 47 | 48 | divVal3: (x, y, z, dst=@) -> 49 | dst.x = @x/x 50 | dst.y = @y/y 51 | dst.z = @z/z 52 | return dst 53 | 54 | dot: (other) -> 55 | return @x*other.x + @y*other.y + @z*other.z 56 | 57 | normalize: (dst=@) -> 58 | l = @length 59 | if l > 0 then @mul 1/@length, dst 60 | return dst 61 | 62 | set: (x,y,z) -> 63 | @x = x 64 | @y = y 65 | @z = z 66 | return @ 67 | 68 | window.Vec4 = class Vec4 69 | @property 'x' 70 | get: -> @data[0] 71 | set: (val) -> @data[0] = val 72 | @property 'y' 73 | get: -> @data[1] 74 | set: (val) -> @data[1] = val 75 | @property 'z' 76 | get: -> @data[2] 77 | set: (val) -> @data[2] = val 78 | @property 'w' 79 | get: -> @data[3] 80 | set: (val) -> @data[3] = val 81 | 82 | constructor: (@data) -> 83 | @data ?= new Float32Array 4 84 | 85 | sub: (other, dst=@) -> 86 | dst.x = @x - other.x 87 | dst.y = @y - other.y 88 | dst.z = @z - other.z 89 | dst.w = @w - other.w 90 | 91 | return dst 92 | 93 | dot: (other) -> 94 | return @x*other.x + @y*other.y + @z*other.z + @w*other.w 95 | 96 | toVec3: (dst) -> 97 | dst ?= new Vec3() 98 | dst.x = @x 99 | dst.y = @y 100 | dst.z = @z 101 | 102 | return dst 103 | -------------------------------------------------------------------------------- /extra/vector.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | (function() { 3 | var Vec3, Vec4; 4 | 5 | window.Vec3 = Vec3 = (function() { 6 | 7 | Vec3.property('x', { 8 | get: function() { 9 | return this.data[0]; 10 | }, 11 | set: function(val) { 12 | return this.data[0] = val; 13 | } 14 | }); 15 | 16 | Vec3.property('y', { 17 | get: function() { 18 | return this.data[1]; 19 | }, 20 | set: function(val) { 21 | return this.data[1] = val; 22 | } 23 | }); 24 | 25 | Vec3.property('z', { 26 | get: function() { 27 | return this.data[2]; 28 | }, 29 | set: function(val) { 30 | return this.data[2] = val; 31 | } 32 | }); 33 | 34 | Vec3.property('length', { 35 | get: function() { 36 | return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 37 | } 38 | }); 39 | 40 | function Vec3(data) { 41 | var _ref; 42 | this.data = data; 43 | if ((_ref = this.data) == null) { 44 | this.data = new Float32Array(3); 45 | } 46 | } 47 | 48 | Vec3.prototype.sub = function(other, dst) { 49 | if (dst == null) { 50 | dst = this; 51 | } 52 | dst.x = this.x - other.x; 53 | dst.y = this.y - other.y; 54 | dst.z = this.z - other.z; 55 | return dst; 56 | }; 57 | 58 | Vec3.prototype.add = function(other, dst) { 59 | if (dst == null) { 60 | dst = this; 61 | } 62 | dst.x = this.x + other.x; 63 | dst.y = this.y + other.y; 64 | dst.z = this.z + other.z; 65 | return dst; 66 | }; 67 | 68 | Vec3.prototype.addVal3 = function(x, y, z, dst) { 69 | if (dst == null) { 70 | dst = this; 71 | } 72 | dst.x = this.x + x; 73 | dst.y = this.y + y; 74 | dst.z = this.z + z; 75 | return dst; 76 | }; 77 | 78 | Vec3.prototype.mul = function(scalar, dst) { 79 | if (dst == null) { 80 | dst = this; 81 | } 82 | dst.x = this.x * scalar; 83 | dst.y = this.y * scalar; 84 | dst.z = this.z * scalar; 85 | return dst; 86 | }; 87 | 88 | Vec3.prototype.div = function(scalar, dst) { 89 | if (dst == null) { 90 | dst = this; 91 | } 92 | dst.x = this.x / scalar; 93 | dst.y = this.y / scalar; 94 | dst.z = this.z / scalar; 95 | return dst; 96 | }; 97 | 98 | Vec3.prototype.divVal3 = function(x, y, z, dst) { 99 | if (dst == null) { 100 | dst = this; 101 | } 102 | dst.x = this.x / x; 103 | dst.y = this.y / y; 104 | dst.z = this.z / z; 105 | return dst; 106 | }; 107 | 108 | Vec3.prototype.dot = function(other) { 109 | return this.x * other.x + this.y * other.y + this.z * other.z; 110 | }; 111 | 112 | Vec3.prototype.normalize = function(dst) { 113 | var l; 114 | if (dst == null) { 115 | dst = this; 116 | } 117 | l = this.length; 118 | if (l > 0) { 119 | this.mul(1 / this.length, dst); 120 | } 121 | return dst; 122 | }; 123 | 124 | Vec3.prototype.set = function(x, y, z) { 125 | this.x = x; 126 | this.y = y; 127 | this.z = z; 128 | return this; 129 | }; 130 | 131 | return Vec3; 132 | 133 | })(); 134 | 135 | window.Vec4 = Vec4 = (function() { 136 | 137 | Vec4.property('x', { 138 | get: function() { 139 | return this.data[0]; 140 | }, 141 | set: function(val) { 142 | return this.data[0] = val; 143 | } 144 | }); 145 | 146 | Vec4.property('y', { 147 | get: function() { 148 | return this.data[1]; 149 | }, 150 | set: function(val) { 151 | return this.data[1] = val; 152 | } 153 | }); 154 | 155 | Vec4.property('z', { 156 | get: function() { 157 | return this.data[2]; 158 | }, 159 | set: function(val) { 160 | return this.data[2] = val; 161 | } 162 | }); 163 | 164 | Vec4.property('w', { 165 | get: function() { 166 | return this.data[3]; 167 | }, 168 | set: function(val) { 169 | return this.data[3] = val; 170 | } 171 | }); 172 | 173 | function Vec4(data) { 174 | var _ref; 175 | this.data = data; 176 | if ((_ref = this.data) == null) { 177 | this.data = new Float32Array(4); 178 | } 179 | } 180 | 181 | Vec4.prototype.sub = function(other, dst) { 182 | if (dst == null) { 183 | dst = this; 184 | } 185 | dst.x = this.x - other.x; 186 | dst.y = this.y - other.y; 187 | dst.z = this.z - other.z; 188 | dst.w = this.w - other.w; 189 | return dst; 190 | }; 191 | 192 | Vec4.prototype.dot = function(other) { 193 | return this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w; 194 | }; 195 | 196 | Vec4.prototype.toVec3 = function(dst) { 197 | if (dst == null) { 198 | dst = new Vec3(); 199 | } 200 | dst.x = this.x; 201 | dst.y = this.y; 202 | dst.z = this.z; 203 | return dst; 204 | }; 205 | 206 | return Vec4; 207 | 208 | })(); 209 | 210 | }).call(this); 211 | -------------------------------------------------------------------------------- /lib/audio.coffee: -------------------------------------------------------------------------------- 1 | class Backend 2 | constructor: -> 3 | @loading = 0 4 | @handlers = [] 5 | 6 | loaded: -> 7 | if @loading == 0 8 | for handler in @handlers 9 | if handler.event == 'loaded' then handler.callback.apply(handler) 10 | return 11 | 12 | bind: (event, callback) -> 13 | handler = 14 | event: event 15 | callback: callback 16 | @handlers.push handler 17 | return handler 18 | 19 | unbind: (handler) -> 20 | index = @handlers.indexOf handler 21 | if index >= 0 then @handlers.splice index, 1 22 | 23 | class HTMLAudio extends Backend 24 | @available = ( 25 | (window.Audio != undefined) and 26 | (window.URL != undefined) and 27 | (window.BlobBuilder != undefined) 28 | ) 29 | 30 | class Sample 31 | constructor: (@backend, data) -> 32 | @backend.loading += 1 33 | @url = blob.pack data, 'audio/ogg' 34 | 35 | play: (looping) -> 36 | voice = @backend.getFree() 37 | voice.play(@url, looping) if voice 38 | 39 | class Voice 40 | constructor: (backend, @id) -> 41 | self = @ 42 | @audio = new Audio() 43 | @audio.onended = -> 44 | backend.ended(self) 45 | 46 | play: (url) -> 47 | @audio.src = url 48 | @audio.play() 49 | 50 | constructor: -> 51 | @free = {} 52 | @playing = {} 53 | 54 | for id in [0...20] 55 | @free[id] = new Voice(@, id) 56 | 57 | setInterval @check, 100 58 | 59 | check: => 60 | #for id, voice of @playing 61 | #console.log voice.audio.duration 62 | 63 | getFree: -> 64 | for id, voice of @free 65 | delete @free[id] 66 | @playing[id] = voice 67 | return voice 68 | 69 | ended: (voice) -> 70 | @free[voice.id] = voice 71 | delete @playing[voice.id] 72 | 73 | createSample: (data) -> 74 | @start_time = gettime() 75 | return new Sample @, data 76 | 77 | class WebAudio extends Backend 78 | @available = window.webkitAudioContext != undefined 79 | 80 | constructor: -> 81 | super() 82 | @ctx = new webkitAudioContext() 83 | 84 | play: (buffer, looping=false) -> 85 | source = @ctx.createBufferSource() 86 | source.buffer = buffer 87 | source.loop = looping 88 | source.connect(@ctx.destination) 89 | source.noteOn(@ctx.currentTime) 90 | 91 | decode: (data, callback) -> 92 | @ctx.decodeAudioData data, (buffer) -> 93 | callback(buffer) 94 | 95 | if WebAudio.available 96 | backend = new WebAudio() 97 | 98 | exports.decode = (data, callback) -> 99 | backend.decode data, callback 100 | 101 | exports.play = (buffer) -> 102 | backend.play buffer 103 | else 104 | exports.decode = (data, callback) -> 105 | exports.play = (buffer) -> 106 | -------------------------------------------------------------------------------- /lib/audio.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Backend, HTMLAudio, WebAudio, backend, 3 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 4 | __hasProp = {}.hasOwnProperty, 5 | __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; }; 6 | 7 | Backend = (function() { 8 | 9 | function Backend() { 10 | this.loading = 0; 11 | this.handlers = []; 12 | } 13 | 14 | Backend.prototype.loaded = function() { 15 | var handler, _i, _len, _ref; 16 | if (this.loading === 0) { 17 | _ref = this.handlers; 18 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 19 | handler = _ref[_i]; 20 | if (handler.event === 'loaded') { 21 | handler.callback.apply(handler); 22 | } 23 | } 24 | } 25 | }; 26 | 27 | Backend.prototype.bind = function(event, callback) { 28 | var handler; 29 | handler = { 30 | event: event, 31 | callback: callback 32 | }; 33 | this.handlers.push(handler); 34 | return handler; 35 | }; 36 | 37 | Backend.prototype.unbind = function(handler) { 38 | var index; 39 | index = this.handlers.indexOf(handler); 40 | if (index >= 0) { 41 | return this.handlers.splice(index, 1); 42 | } 43 | }; 44 | 45 | return Backend; 46 | 47 | })(); 48 | 49 | HTMLAudio = (function(_super) { 50 | var Sample, Voice; 51 | 52 | __extends(HTMLAudio, _super); 53 | 54 | HTMLAudio.available = (window.Audio !== void 0) && (window.URL !== void 0) && (window.BlobBuilder !== void 0); 55 | 56 | Sample = (function() { 57 | 58 | function Sample(backend, data) { 59 | this.backend = backend; 60 | this.backend.loading += 1; 61 | this.url = blob.pack(data, 'audio/ogg'); 62 | } 63 | 64 | Sample.prototype.play = function(looping) { 65 | var voice; 66 | voice = this.backend.getFree(); 67 | if (voice) { 68 | return voice.play(this.url, looping); 69 | } 70 | }; 71 | 72 | return Sample; 73 | 74 | })(); 75 | 76 | Voice = (function() { 77 | 78 | function Voice(backend, id) { 79 | var self; 80 | this.id = id; 81 | self = this; 82 | this.audio = new Audio(); 83 | this.audio.onended = function() { 84 | return backend.ended(self); 85 | }; 86 | } 87 | 88 | Voice.prototype.play = function(url) { 89 | this.audio.src = url; 90 | return this.audio.play(); 91 | }; 92 | 93 | return Voice; 94 | 95 | })(); 96 | 97 | function HTMLAudio() { 98 | this.check = __bind(this.check, this); 99 | 100 | var id, _i; 101 | this.free = {}; 102 | this.playing = {}; 103 | for (id = _i = 0; _i < 20; id = ++_i) { 104 | this.free[id] = new Voice(this, id); 105 | } 106 | setInterval(this.check, 100); 107 | } 108 | 109 | HTMLAudio.prototype.check = function() {}; 110 | 111 | HTMLAudio.prototype.getFree = function() { 112 | var id, voice, _ref; 113 | _ref = this.free; 114 | for (id in _ref) { 115 | voice = _ref[id]; 116 | delete this.free[id]; 117 | this.playing[id] = voice; 118 | return voice; 119 | } 120 | }; 121 | 122 | HTMLAudio.prototype.ended = function(voice) { 123 | this.free[voice.id] = voice; 124 | return delete this.playing[voice.id]; 125 | }; 126 | 127 | HTMLAudio.prototype.createSample = function(data) { 128 | this.start_time = gettime(); 129 | return new Sample(this, data); 130 | }; 131 | 132 | return HTMLAudio; 133 | 134 | })(Backend); 135 | 136 | WebAudio = (function(_super) { 137 | 138 | __extends(WebAudio, _super); 139 | 140 | WebAudio.available = window.webkitAudioContext !== void 0; 141 | 142 | function WebAudio() { 143 | WebAudio.__super__.constructor.call(this); 144 | this.ctx = new webkitAudioContext(); 145 | } 146 | 147 | WebAudio.prototype.play = function(buffer, looping) { 148 | var source; 149 | if (looping == null) { 150 | looping = false; 151 | } 152 | source = this.ctx.createBufferSource(); 153 | source.buffer = buffer; 154 | source.loop = looping; 155 | source.connect(this.ctx.destination); 156 | return source.noteOn(this.ctx.currentTime); 157 | }; 158 | 159 | WebAudio.prototype.decode = function(data, callback) { 160 | return this.ctx.decodeAudioData(data, function(buffer) { 161 | return callback(buffer); 162 | }); 163 | }; 164 | 165 | return WebAudio; 166 | 167 | })(Backend); 168 | 169 | if (WebAudio.available) { 170 | backend = new WebAudio(); 171 | exports.decode = function(data, callback) { 172 | return backend.decode(data, callback); 173 | }; 174 | exports.play = function(buffer) { 175 | return backend.play(buffer); 176 | }; 177 | } else { 178 | exports.decode = function(data, callback) {}; 179 | exports.play = function(buffer) {}; 180 | } 181 | -------------------------------------------------------------------------------- /lib/camera.coffee: -------------------------------------------------------------------------------- 1 | keys = require 'keys' 2 | 3 | class MouseDrag 4 | constructor: (@which) -> 5 | @x = 0 6 | @y = 0 7 | 8 | @lx = 0 9 | @ly = 0 10 | @pressed = false 11 | 12 | if navigator.appVersion.indexOf('Mac') != -1 13 | $(document).bind 'mousewheel', (event) => 14 | event.preventDefault() 15 | event.stopImmediatePropagation() 16 | event.stopPropagation() 17 | 18 | @x += event.originalEvent.wheelDeltaX*0.25 19 | @y += event.originalEvent.wheelDeltaY*0.25 20 | return false 21 | 22 | $(document).mousedown (event) => 23 | if event.which == @which 24 | @lx = event.pageX 25 | @ly = event.pageY 26 | @pressed = true 27 | return undefined 28 | 29 | $(document).mouseup => 30 | @pressed = false 31 | return undefined 32 | 33 | $(document).mousemove (event) => 34 | if @pressed and event.which == @which 35 | x = event.pageX 36 | y = event.pageY 37 | @x += x - @lx 38 | @y += y - @ly 39 | @lx = x 40 | @ly = y 41 | return undefined 42 | 43 | $(document).bind 'touchstart', (event) => 44 | touch = event.originalEvent.touches[0] 45 | @pressed = true 46 | @lx = touch.pageX 47 | @ly = touch.pageY 48 | 49 | return undefined 50 | 51 | $(document).bind 'touchend', (event) => 52 | @pressed = false 53 | 54 | return undefined 55 | 56 | $(document).bind 'touchmove', (event) => 57 | event.preventDefault() 58 | if @pressed 59 | touch = event.originalEvent.touches[0] 60 | x = touch.pageX 61 | y = touch.pageY 62 | @x += x - @lx 63 | @y += y - @ly 64 | @lx = x 65 | @ly = y 66 | 67 | return undefined 68 | 69 | reset: -> 70 | @x = 0 71 | @y = 0 72 | 73 | class Camera 74 | constructor: (@delta=1/180, @near=0.1, @far=1000) -> 75 | @time = gettime() 76 | @proj = new Mat4() 77 | @inv_proj = new Mat4() 78 | @view = new Mat4() 79 | @inv_view = new Mat4() 80 | @rot = new Mat3() 81 | @inv_rot = new Mat3() 82 | @acc = new Vec3() 83 | 84 | aspect: (width, height) -> 85 | @proj.perspective 75, width/height, @near, @far 86 | @inv_proj.inversePerspective 75, width/height, @near, @far 87 | 88 | step: -> 89 | @accelerate() 90 | @limit() 91 | @move() 92 | @limit() 93 | @time += @delta 94 | 95 | update: -> 96 | now = gettime() 97 | if now - @time > @delta*30 98 | @time = now - @delta*30 99 | 100 | while @time < now 101 | @step() 102 | @finish() 103 | 104 | @view.invert @inv_view.identity() 105 | @view.toMat3 @rot.identity() 106 | @inv_view.toMat3 @inv_rot.identity() 107 | 108 | limit: -> 109 | 110 | exports.GameCam = class GameCam extends Camera 111 | constructor: ({@sl, @sr, delta, x, y, z}={}) -> 112 | super(delta) 113 | @realpos = new Vec4() 114 | @sl ?= 200 115 | @sr ?= 100 116 | x ?= 0 117 | y ?= 0 118 | z ?= 0 119 | 120 | @mouse = new MouseDrag(3) 121 | 122 | @target_height = 0 123 | @height = 0 124 | 125 | @x=x; @lx=x 126 | #@y=y; @ly=y 127 | @z=z; @lz=z 128 | @o=0; @lo=0 129 | @d=0; @ld=0; @ad=0 130 | 131 | $(document).bind 'mousewheel', (event) => 132 | event.preventDefault() 133 | event.stopImmediatePropagation() 134 | event.stopPropagation() 135 | 136 | @ad -= event.originalEvent.wheelDeltaY 137 | return false 138 | 139 | accelerate: -> 140 | sl = @delta*@delta*@sl 141 | sr = @delta*@delta*@sr 142 | 143 | ctrl_x = if keys.a then -1 else if keys.d then 1 else 0 144 | ctrl_y = if keys.q then -1 else if keys.e then 1 else 0 145 | ctrl_z = if keys.w then -1 else if keys.s then 1 else 0 146 | 147 | ax = ctrl_x * sl 148 | #ay = ctrl_y * sl 149 | az = ctrl_z * sl 150 | 151 | @rot 152 | .identity() 153 | .rotatey(-@o) 154 | .mulVal3(ax, 0, az, @acc) 155 | 156 | @x += @acc.x 157 | #@y += @acc.y 158 | @z += @acc.z 159 | @o += @mouse.x * sr 160 | @d += @ad * @delta * @delta * 20 161 | 162 | move = @delta*@delta*4000 163 | if move > 1 then move = 1 164 | @height = @height + (@target_height - @height) * move 165 | 166 | move: -> 167 | retl = 0.97 168 | retr = 0.94 169 | x = @x + (@x - @lx) * retl 170 | #y = @y + (@y - @ly) * retl 171 | z = @z + (@z - @lz) * retl 172 | d = @d + (@d - @ld) * retl 173 | o = @o + (@o - @lo) * retr 174 | 175 | @lx = @x; @x = x 176 | #@ly = @y; @y = y 177 | @lz = @z; @z = z 178 | @lo = @o; @o = o 179 | @ld = @d; @d = d 180 | 181 | 182 | limit: -> 183 | if @d < 0 then @d = 0 184 | else if @d > 30 then @d = 30 185 | 186 | high = 128+64 187 | low = 128-64 188 | if @x < low then @x = low 189 | else if @x > high then @x = high 190 | 191 | if @z < low then @z = low 192 | else if @z > high then @z = high 193 | 194 | finish: -> 195 | @mouse.reset() 196 | @ad = 0 197 | @view 198 | .identity() 199 | .translateVal3(0, 0, -@d-5) 200 | .rotatex(25+(@d/30)*40) 201 | .rotatey(@o) 202 | .translateVal3(-@x, -@height, -@z) 203 | 204 | update: (picker) -> 205 | @view 206 | .identity() 207 | .translateVal3(0, 0, -@d-5) 208 | .rotatex(25+(@d/30)*40) 209 | .rotatey(@o) 210 | .translateVal3(-@x, 0, -@z) 211 | @view.invert @inv_view.identity() 212 | 213 | h1 = picker.getHeight @x, @z 214 | @inv_view.mulVal4 0, 0, 0, 1, @realpos 215 | h2 = picker.getHeight(@realpos.x, @realpos.z)+2 216 | real_height = h1+@realpos.y 217 | if real_height < h2 218 | diff = h2 - real_height 219 | @target_height = h1 + diff 220 | else 221 | @target_height = h1 222 | 223 | super() 224 | 225 | exports.Orbit = class Orbit extends Camera 226 | constructor: ({@sr, delta, x, y, z}={}) -> 227 | super(delta) 228 | @sr ?= 100 229 | 230 | @mouse = new MouseDrag(1) 231 | 232 | @o=0; @lo=0 233 | @p=0; @lp=0 234 | 235 | accelerate: -> 236 | sr = @delta*@delta*@sr 237 | @o += @mouse.x * sr 238 | @p += @mouse.y * sr 239 | 240 | move: -> 241 | retr = 0.94 242 | o = @o + (@o - @lo) * retr 243 | p = @p + (@p - @lp) * retr 244 | 245 | @lo = @o; @o = o 246 | @lp = @p; @p = p 247 | 248 | finish: -> 249 | @mouse.reset() 250 | 251 | update: () -> 252 | @view 253 | .identity() 254 | .translateVal3(0, 0, -0.6) 255 | .rotatex(@p) 256 | .rotatey(@o) 257 | .translateVal3(0.1, 0.1, 0) 258 | @view.invert @inv_view.identity() 259 | 260 | super() 261 | 262 | exports.FlyCam = class FlyCam extends Camera 263 | constructor: ({@sl, @sr, delta, near, far, lookbutton, x, y, z}={}) -> 264 | super(delta, near, far) 265 | @sl ?= 50 266 | @sr ?= 100 267 | lookbutton ?= 1 268 | x ?= 0 269 | y ?= 0 270 | z ?= 0 271 | 272 | @mouse = new MouseDrag(lookbutton) 273 | 274 | @x=x; @lx=x 275 | @y=y; @ly=y 276 | @z=z; @lz=z 277 | @o=0; @lo=0 278 | @p=0; @lp=0 279 | 280 | accelerate: -> 281 | sl = @delta*@delta*@sl 282 | sr = @delta*@delta*@sr 283 | 284 | ctrl_x = if keys.a then -1 else if keys.d then 1 else 0 285 | ctrl_y = if keys.q then -1 else if keys.e then 1 else 0 286 | ctrl_z = if keys.w then -1 else if keys.s then 1 else 0 287 | 288 | ax = ctrl_x * sl 289 | ay = ctrl_y * sl 290 | az = ctrl_z * sl 291 | 292 | @rot 293 | .identity() 294 | .rotatey(-@o) 295 | .rotatex(-@p) 296 | .mulVal3(ax, ay, az, @acc) 297 | 298 | @x += @acc.x 299 | @y += @acc.y 300 | @z += @acc.z 301 | @o += @mouse.x * sr 302 | @p += @mouse.y * sr 303 | 304 | move: -> 305 | retl = 0.97 306 | retr = 0.94 307 | x = @x + (@x - @lx) * retl 308 | y = @y + (@y - @ly) * retl 309 | z = @z + (@z - @lz) * retl 310 | o = @o + (@o - @lo) * retr 311 | p = @p + (@p - @lp) * retr 312 | 313 | if p > 80 then p = 80 314 | else if p < -80 then p = -80 315 | 316 | @lx = @x; @x = x 317 | @ly = @y; @y = y 318 | @lz = @z; @z = z 319 | @lo = @o; @o = o 320 | @lp = @p; @p = p 321 | 322 | finish: -> 323 | @mouse.reset() 324 | @view 325 | .identity() 326 | .rotatex(@p) 327 | .rotatey(@o) 328 | .translateVal3(-@x, -@y, -@z) 329 | -------------------------------------------------------------------------------- /lib/camera.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Camera, FlyCam, GameCam, MouseDrag, Orbit, keys, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | keys = require('keys'); 7 | 8 | MouseDrag = (function() { 9 | 10 | function MouseDrag(which) { 11 | var _this = this; 12 | this.which = which; 13 | this.x = 0; 14 | this.y = 0; 15 | this.lx = 0; 16 | this.ly = 0; 17 | this.pressed = false; 18 | if (navigator.appVersion.indexOf('Mac') !== -1) { 19 | $(document).bind('mousewheel', function(event) { 20 | event.preventDefault(); 21 | event.stopImmediatePropagation(); 22 | event.stopPropagation(); 23 | _this.x += event.originalEvent.wheelDeltaX * 0.25; 24 | _this.y += event.originalEvent.wheelDeltaY * 0.25; 25 | return false; 26 | }); 27 | } 28 | $(document).mousedown(function(event) { 29 | if (event.which === _this.which) { 30 | _this.lx = event.pageX; 31 | _this.ly = event.pageY; 32 | _this.pressed = true; 33 | } 34 | return void 0; 35 | }); 36 | $(document).mouseup(function() { 37 | _this.pressed = false; 38 | return void 0; 39 | }); 40 | $(document).mousemove(function(event) { 41 | var x, y; 42 | if (_this.pressed && event.which === _this.which) { 43 | x = event.pageX; 44 | y = event.pageY; 45 | _this.x += x - _this.lx; 46 | _this.y += y - _this.ly; 47 | _this.lx = x; 48 | _this.ly = y; 49 | } 50 | return void 0; 51 | }); 52 | $(document).bind('touchstart', function(event) { 53 | var target, touch; 54 | target = event.originalEvent.target; 55 | console.log(target.id); 56 | touch = event.originalEvent.touches[0]; 57 | _this.pressed = true; 58 | _this.lx = touch.pageX; 59 | _this.ly = touch.pageY; 60 | return void 0; 61 | }); 62 | $(document).bind('touchend', function(event) { 63 | _this.pressed = false; 64 | return void 0; 65 | }); 66 | $(document).bind('touchmove', function(event) { 67 | var touch, x, y; 68 | event.preventDefault(); 69 | if (_this.pressed) { 70 | touch = event.originalEvent.touches[0]; 71 | x = touch.pageX; 72 | y = touch.pageY; 73 | _this.x += x - _this.lx; 74 | _this.y += y - _this.ly; 75 | _this.lx = x; 76 | _this.ly = y; 77 | } 78 | return void 0; 79 | }); 80 | } 81 | 82 | MouseDrag.prototype.reset = function() { 83 | this.x = 0; 84 | return this.y = 0; 85 | }; 86 | 87 | return MouseDrag; 88 | 89 | })(); 90 | 91 | Camera = (function() { 92 | 93 | function Camera(delta, near, far) { 94 | this.delta = delta != null ? delta : 1 / 180; 95 | this.near = near != null ? near : 0.1; 96 | this.far = far != null ? far : 1000; 97 | this.time = gettime(); 98 | this.proj = new Mat4(); 99 | this.inv_proj = new Mat4(); 100 | this.view = new Mat4(); 101 | this.inv_view = new Mat4(); 102 | this.rot = new Mat3(); 103 | this.inv_rot = new Mat3(); 104 | this.acc = new Vec3(); 105 | } 106 | 107 | Camera.prototype.aspect = function(width, height) { 108 | this.proj.perspective(75, width / height, this.near, this.far); 109 | return this.inv_proj.inversePerspective(75, width / height, this.near, this.far); 110 | }; 111 | 112 | Camera.prototype.step = function() { 113 | this.accelerate(); 114 | this.limit(); 115 | this.move(); 116 | this.limit(); 117 | return this.time += this.delta; 118 | }; 119 | 120 | Camera.prototype.update = function() { 121 | var now; 122 | now = gettime(); 123 | if (now - this.time > this.delta * 30) { 124 | this.time = now - this.delta * 30; 125 | } 126 | while (this.time < now) { 127 | this.step(); 128 | } 129 | this.finish(); 130 | this.view.invert(this.inv_view.identity()); 131 | this.view.toMat3(this.rot.identity()); 132 | return this.inv_view.toMat3(this.inv_rot.identity()); 133 | }; 134 | 135 | Camera.prototype.limit = function() {}; 136 | 137 | return Camera; 138 | 139 | })(); 140 | 141 | exports.GameCam = GameCam = (function(_super) { 142 | 143 | __extends(GameCam, _super); 144 | 145 | function GameCam(_arg) { 146 | var delta, x, y, z, _ref, _ref1, _ref2, 147 | _this = this; 148 | _ref = _arg != null ? _arg : {}, this.sl = _ref.sl, this.sr = _ref.sr, delta = _ref.delta, x = _ref.x, y = _ref.y, z = _ref.z; 149 | GameCam.__super__.constructor.call(this, delta); 150 | this.realpos = new Vec4(); 151 | if ((_ref1 = this.sl) == null) { 152 | this.sl = 200; 153 | } 154 | if ((_ref2 = this.sr) == null) { 155 | this.sr = 100; 156 | } 157 | if (x == null) { 158 | x = 0; 159 | } 160 | if (y == null) { 161 | y = 0; 162 | } 163 | if (z == null) { 164 | z = 0; 165 | } 166 | this.mouse = new MouseDrag(3); 167 | this.target_height = 0; 168 | this.height = 0; 169 | this.x = x; 170 | this.lx = x; 171 | this.z = z; 172 | this.lz = z; 173 | this.o = 0; 174 | this.lo = 0; 175 | this.d = 0; 176 | this.ld = 0; 177 | this.ad = 0; 178 | $(document).bind('mousewheel', function(event) { 179 | event.preventDefault(); 180 | event.stopImmediatePropagation(); 181 | event.stopPropagation(); 182 | _this.ad -= event.originalEvent.wheelDeltaY; 183 | return false; 184 | }); 185 | } 186 | 187 | GameCam.prototype.accelerate = function() { 188 | var ax, az, ctrl_x, ctrl_y, ctrl_z, move, sl, sr; 189 | sl = this.delta * this.delta * this.sl; 190 | sr = this.delta * this.delta * this.sr; 191 | ctrl_x = keys.a ? -1 : keys.d ? 1 : 0; 192 | ctrl_y = keys.q ? -1 : keys.e ? 1 : 0; 193 | ctrl_z = keys.w ? -1 : keys.s ? 1 : 0; 194 | ax = ctrl_x * sl; 195 | az = ctrl_z * sl; 196 | this.rot.identity().rotatey(-this.o).mulVal3(ax, 0, az, this.acc); 197 | this.x += this.acc.x; 198 | this.z += this.acc.z; 199 | this.o += this.mouse.x * sr; 200 | this.d += this.ad * this.delta * this.delta * 20; 201 | move = this.delta * this.delta * 4000; 202 | if (move > 1) { 203 | move = 1; 204 | } 205 | return this.height = this.height + (this.target_height - this.height) * move; 206 | }; 207 | 208 | GameCam.prototype.move = function() { 209 | var d, o, retl, retr, x, z; 210 | retl = 0.97; 211 | retr = 0.94; 212 | x = this.x + (this.x - this.lx) * retl; 213 | z = this.z + (this.z - this.lz) * retl; 214 | d = this.d + (this.d - this.ld) * retl; 215 | o = this.o + (this.o - this.lo) * retr; 216 | this.lx = this.x; 217 | this.x = x; 218 | this.lz = this.z; 219 | this.z = z; 220 | this.lo = this.o; 221 | this.o = o; 222 | this.ld = this.d; 223 | return this.d = d; 224 | }; 225 | 226 | GameCam.prototype.limit = function() { 227 | var high, low; 228 | if (this.d < 0) { 229 | this.d = 0; 230 | } else if (this.d > 30) { 231 | this.d = 30; 232 | } 233 | high = 128 + 64; 234 | low = 128 - 64; 235 | if (this.x < low) { 236 | this.x = low; 237 | } else if (this.x > high) { 238 | this.x = high; 239 | } 240 | if (this.z < low) { 241 | return this.z = low; 242 | } else if (this.z > high) { 243 | return this.z = high; 244 | } 245 | }; 246 | 247 | GameCam.prototype.finish = function() { 248 | this.mouse.reset(); 249 | this.ad = 0; 250 | 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); 251 | }; 252 | 253 | GameCam.prototype.update = function(picker) { 254 | var diff, h1, h2, real_height; 255 | this.view.identity().translateVal3(0, 0, -this.d - 5).rotatex(25 + (this.d / 30) * 40).rotatey(this.o).translateVal3(-this.x, 0, -this.z); 256 | this.view.invert(this.inv_view.identity()); 257 | h1 = picker.getHeight(this.x, this.z); 258 | this.inv_view.mulVal4(0, 0, 0, 1, this.realpos); 259 | h2 = picker.getHeight(this.realpos.x, this.realpos.z) + 2; 260 | real_height = h1 + this.realpos.y; 261 | if (real_height < h2) { 262 | diff = h2 - real_height; 263 | this.target_height = h1 + diff; 264 | } else { 265 | this.target_height = h1; 266 | } 267 | return GameCam.__super__.update.call(this); 268 | }; 269 | 270 | return GameCam; 271 | 272 | })(Camera); 273 | 274 | exports.Orbit = Orbit = (function(_super) { 275 | 276 | __extends(Orbit, _super); 277 | 278 | function Orbit(_arg) { 279 | var delta, x, y, z, _ref, _ref1; 280 | _ref = _arg != null ? _arg : {}, this.sr = _ref.sr, delta = _ref.delta, x = _ref.x, y = _ref.y, z = _ref.z; 281 | Orbit.__super__.constructor.call(this, delta); 282 | if ((_ref1 = this.sr) == null) { 283 | this.sr = 100; 284 | } 285 | this.mouse = new MouseDrag(1); 286 | this.o = 0; 287 | this.lo = 0; 288 | this.p = 0; 289 | this.lp = 0; 290 | } 291 | 292 | Orbit.prototype.accelerate = function() { 293 | var sr; 294 | sr = this.delta * this.delta * this.sr; 295 | this.o += this.mouse.x * sr; 296 | return this.p += this.mouse.y * sr; 297 | }; 298 | 299 | Orbit.prototype.move = function() { 300 | var o, p, retr; 301 | retr = 0.94; 302 | o = this.o + (this.o - this.lo) * retr; 303 | p = this.p + (this.p - this.lp) * retr; 304 | this.lo = this.o; 305 | this.o = o; 306 | this.lp = this.p; 307 | return this.p = p; 308 | }; 309 | 310 | Orbit.prototype.finish = function() { 311 | return this.mouse.reset(); 312 | }; 313 | 314 | Orbit.prototype.update = function() { 315 | this.view.identity().translateVal3(0, 0, -0.6).rotatex(this.p).rotatey(this.o).translateVal3(0.1, 0.1, 0); 316 | this.view.invert(this.inv_view.identity()); 317 | return Orbit.__super__.update.call(this); 318 | }; 319 | 320 | return Orbit; 321 | 322 | })(Camera); 323 | 324 | exports.FlyCam = FlyCam = (function(_super) { 325 | 326 | __extends(FlyCam, _super); 327 | 328 | function FlyCam(_arg) { 329 | var delta, far, lookbutton, near, x, y, z, _ref, _ref1, _ref2; 330 | _ref = _arg != null ? _arg : {}, this.sl = _ref.sl, 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; 331 | FlyCam.__super__.constructor.call(this, delta, near, far); 332 | if ((_ref1 = this.sl) == null) { 333 | this.sl = 50; 334 | } 335 | if ((_ref2 = this.sr) == null) { 336 | this.sr = 100; 337 | } 338 | if (lookbutton == null) { 339 | lookbutton = 1; 340 | } 341 | if (x == null) { 342 | x = 0; 343 | } 344 | if (y == null) { 345 | y = 0; 346 | } 347 | if (z == null) { 348 | z = 0; 349 | } 350 | this.mouse = new MouseDrag(lookbutton); 351 | this.x = x; 352 | this.lx = x; 353 | this.y = y; 354 | this.ly = y; 355 | this.z = z; 356 | this.lz = z; 357 | this.o = 0; 358 | this.lo = 0; 359 | this.p = 0; 360 | this.lp = 0; 361 | } 362 | 363 | FlyCam.prototype.accelerate = function() { 364 | var ax, ay, az, ctrl_x, ctrl_y, ctrl_z, sl, sr; 365 | sl = this.delta * this.delta * this.sl; 366 | sr = this.delta * this.delta * this.sr; 367 | ctrl_x = keys.a ? -1 : keys.d ? 1 : 0; 368 | ctrl_y = keys.q ? -1 : keys.e ? 1 : 0; 369 | ctrl_z = keys.w ? -1 : keys.s ? 1 : 0; 370 | ax = ctrl_x * sl; 371 | ay = ctrl_y * sl; 372 | az = ctrl_z * sl; 373 | this.rot.identity().rotatey(-this.o).rotatex(-this.p).mulVal3(ax, ay, az, this.acc); 374 | this.x += this.acc.x; 375 | this.y += this.acc.y; 376 | this.z += this.acc.z; 377 | this.o += this.mouse.x * sr; 378 | return this.p += this.mouse.y * sr; 379 | }; 380 | 381 | FlyCam.prototype.move = function() { 382 | var o, p, retl, retr, x, y, z; 383 | retl = 0.97; 384 | retr = 0.94; 385 | x = this.x + (this.x - this.lx) * retl; 386 | y = this.y + (this.y - this.ly) * retl; 387 | z = this.z + (this.z - this.lz) * retl; 388 | o = this.o + (this.o - this.lo) * retr; 389 | p = this.p + (this.p - this.lp) * retr; 390 | if (p > 80) { 391 | p = 80; 392 | } else if (p < -80) { 393 | p = -80; 394 | } 395 | this.lx = this.x; 396 | this.x = x; 397 | this.ly = this.y; 398 | this.y = y; 399 | this.lz = this.z; 400 | this.z = z; 401 | this.lo = this.o; 402 | this.o = o; 403 | this.lp = this.p; 404 | return this.p = p; 405 | }; 406 | 407 | FlyCam.prototype.finish = function() { 408 | this.mouse.reset(); 409 | return this.view.identity().rotatex(this.p).rotatey(this.o).translateVal3(-this.x, -this.y, -this.z); 410 | }; 411 | 412 | return FlyCam; 413 | 414 | })(Camera); 415 | -------------------------------------------------------------------------------- /lib/geometry.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 3 | exports.AABB = (function() { 4 | 5 | function AABB(xmin, xmax, ymin, ymax, zmin, zmax) { 6 | this.xmin = xmin; 7 | this.xmax = xmax; 8 | this.ymin = ymin; 9 | this.ymax = ymax; 10 | this.zmin = zmin; 11 | this.zmax = zmax; 12 | } 13 | 14 | AABB.prototype.ray_intersect = function(ray) { 15 | var d, inv_x, inv_y, inv_z, o, tmax, tmin, ymax, ymin, zmax, zmin, _ref, _ref1, _ref2; 16 | o = ray.origin; 17 | d = ray.direction; 18 | inv_x = 1.0 / d.x; 19 | tmin = (this.xmin - o.x) * inv_x; 20 | tmax = (this.xmax - o.x) * inv_x; 21 | if (inv_x < 0) { 22 | _ref = [tmax, tmin], tmin = _ref[0], tmax = _ref[1]; 23 | } 24 | inv_y = 1.0 / d.y; 25 | ymin = (this.ymin - o.y) * inv_y; 26 | ymax = (this.ymax - o.y) * inv_y; 27 | if (inv_y < 0) { 28 | _ref1 = [ymax, ymin], ymin = _ref1[0], ymax = _ref1[1]; 29 | } 30 | if (tmin > ymax || ymin > tmax) { 31 | return null; 32 | } 33 | if (ymin > tmin) { 34 | tmin = ymin; 35 | } 36 | if (ymax < tmax) { 37 | tmax = ymax; 38 | } 39 | inv_z = 1.0 / d.z; 40 | zmin = (this.zmin - o.z) * inv_z; 41 | zmax = (this.zmax - o.z) * inv_z; 42 | if (inv_z < 0) { 43 | _ref2 = [zmax, zmin], zmin = _ref2[0], zmax = _ref2[1]; 44 | } 45 | if (tmin > zmax || zmin > tmax) { 46 | return null; 47 | } 48 | if (zmin > tmin) { 49 | tmin = zmin; 50 | } 51 | if (zmax < tmax) { 52 | tmax = zmax; 53 | } 54 | return [tmin, tmax]; 55 | }; 56 | 57 | return AABB; 58 | 59 | })(); 60 | 61 | exports.Ray = (function() { 62 | 63 | function Ray(origin, direction) { 64 | var _ref, _ref1; 65 | this.origin = origin; 66 | this.direction = direction; 67 | if ((_ref = this.origin) == null) { 68 | this.origin = new Vec4(); 69 | } 70 | if ((_ref1 = this.direction) == null) { 71 | this.direction = new Vec4(); 72 | } 73 | } 74 | 75 | Ray.prototype.interpolate = function(interval, vector) { 76 | var d, o, v; 77 | if (vector == null) { 78 | vector = new Vec4(); 79 | } 80 | o = this.origin; 81 | d = this.direction; 82 | v = vector; 83 | v.x = o.x + d.x * interval; 84 | v.y = o.y + d.y * interval; 85 | v.z = o.z + d.z * interval; 86 | v.w = o.w + d.w * interval; 87 | return vector; 88 | }; 89 | 90 | Ray.prototype.ray_nearest = function(ray) { 91 | var U, V, W, a, b, c, d, det, e, s, t; 92 | W = this.origin.sub(ray.origin, new Vec4); 93 | U = this.direction; 94 | V = ray.direction; 95 | a = U.dot(U); 96 | b = U.dot(V); 97 | c = V.dot(V); 98 | d = U.dot(W); 99 | e = V.dot(W); 100 | det = a * c - b * b; 101 | if (det === 0) { 102 | return null; 103 | } 104 | s = (b * e - c * d) / det; 105 | t = (a * e - b * d) / det; 106 | return [s, t]; 107 | }; 108 | 109 | Ray.prototype.point_distance = function(point) { 110 | var W, s; 111 | W = point.sub(this.origin, new Vec4); 112 | s = W.dot(this.direction) / this.direction.dot(this.direction); 113 | W.x -= this.direction.x * s; 114 | W.y -= this.direction.y * s; 115 | W.z -= this.direction.z * s; 116 | return Math.sqrt(W.dot(W)); 117 | }; 118 | 119 | return Ray; 120 | 121 | })(); 122 | 123 | exports.get_mouseray = function(x, y, inv_proj, inv_view, ray) { 124 | if (ray == null) { 125 | ray = new exports.Ray; 126 | } 127 | inv_proj.mulVal4(x, y, -1, 1, ray.direction); 128 | inv_view.mulVec3(ray.direction); 129 | ray.direction.w = 0; 130 | inv_view.mulVal4(0, 0, 0, 1, ray.origin); 131 | return ray; 132 | }; 133 | -------------------------------------------------------------------------------- /lib/keys.coffee: -------------------------------------------------------------------------------- 1 | keymap = ({ 2 | 87: 'w', 3 | 65: 'a', 4 | 83: 's', 5 | 68: 'd', 6 | 81: 'q', 7 | 69: 'e', 8 | 37: 'left', 9 | 39: 'right', 10 | 38: 'up', 11 | 40: 'down', 12 | 13: 'enter', 13 | 27: 'esc', 14 | 32: 'space', 15 | 8: 'backspace', 16 | 16: 'shift', 17 | 17: 'ctrl', 18 | 18: 'alt', 19 | 91: 'start', 20 | 0: 'altc', 21 | 20: 'caps', 22 | 9: 'tab', 23 | 49: 'key1', 24 | 50: 'key2', 25 | 51: 'key3', 26 | 52: 'key4' 27 | }) 28 | 29 | keys = {} 30 | 31 | $(document).keydown (event) -> 32 | name = keymap[event.which] 33 | keys[name] = true 34 | 35 | $(document).keyup (event) -> 36 | name = keymap[event.which] 37 | keys[name] = false 38 | 39 | return keys 40 | -------------------------------------------------------------------------------- /lib/keys.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var keymap, keys; 3 | 4 | keymap = { 5 | 87: 'w', 6 | 65: 'a', 7 | 83: 's', 8 | 68: 'd', 9 | 81: 'q', 10 | 69: 'e', 11 | 37: 'left', 12 | 39: 'right', 13 | 38: 'up', 14 | 40: 'down', 15 | 13: 'enter', 16 | 27: 'esc', 17 | 32: 'space', 18 | 8: 'backspace', 19 | 16: 'shift', 20 | 17: 'ctrl', 21 | 18: 'alt', 22 | 91: 'start', 23 | 0: 'altc', 24 | 20: 'caps', 25 | 9: 'tab', 26 | 49: 'key1', 27 | 50: 'key2', 28 | 51: 'key3', 29 | 52: 'key4' 30 | }; 31 | 32 | keys = {}; 33 | 34 | $(document).keydown(function(event) { 35 | var name; 36 | name = keymap[event.which]; 37 | return keys[name] = true; 38 | }); 39 | 40 | $(document).keyup(function(event) { 41 | var name; 42 | name = keymap[event.which]; 43 | return keys[name] = false; 44 | }); 45 | 46 | return keys; 47 | -------------------------------------------------------------------------------- /lib/loading.coffee: -------------------------------------------------------------------------------- 1 | ui = $('#ui') 2 | hidden = true 3 | 4 | container = $('
').css 5 | position: 'absolute' 6 | top: '50%' 7 | left: '50%' 8 | width: 200 9 | height: 40 10 | marginLeft: -100 11 | marginTop: -20 12 | 13 | label = $('').appendTo(container).css 14 | position: 'absolute' 15 | top: 0 16 | left: 0 17 | width: 200 18 | height: 20 19 | textAlign: 'center' 20 | color: 'white' 21 | 22 | loading = $('').appendTo(container).css 23 | position: 'absolute' 24 | top: 20 25 | left: 0 26 | width: 200 27 | height: 20 28 | border: '1px solid white' 29 | 30 | bar = null 31 | makeBar = -> 32 | loading.empty() 33 | bar = $('').appendTo(loading).css 34 | position: 'absolute' 35 | top: 0 36 | left: 0 37 | width: 0 38 | height: 20 39 | backgroundColor: 'white' 40 | '-webkit-transition': 'width 0.7s' 41 | 42 | exports.show = (text) -> 43 | label.text text 44 | makeBar() 45 | container 46 | .fadeIn('slow') 47 | .appendTo(ui) 48 | 49 | exports.hide = -> 50 | container.fadeOut 'slow', -> 51 | container.detach() 52 | 53 | exports.progress = (factor) -> 54 | bar.width(factor*200) 55 | -------------------------------------------------------------------------------- /lib/loading.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var bar, container, hidden, label, loading, makeBar, ui; 3 | 4 | ui = $('#ui'); 5 | 6 | hidden = true; 7 | 8 | container = $('').css({ 9 | position: 'absolute', 10 | top: '50%', 11 | left: '50%', 12 | width: 200, 13 | height: 40, 14 | marginLeft: -100, 15 | marginTop: -20 16 | }); 17 | 18 | label = $('').appendTo(container).css({ 19 | position: 'absolute', 20 | top: 0, 21 | left: 0, 22 | width: 200, 23 | height: 20, 24 | textAlign: 'center', 25 | color: 'white' 26 | }); 27 | 28 | loading = $('').appendTo(container).css({ 29 | position: 'absolute', 30 | top: 20, 31 | left: 0, 32 | width: 200, 33 | height: 20, 34 | border: '1px solid white' 35 | }); 36 | 37 | bar = null; 38 | 39 | makeBar = function() { 40 | loading.empty(); 41 | return bar = $('').appendTo(loading).css({ 42 | position: 'absolute', 43 | top: 0, 44 | left: 0, 45 | width: 0, 46 | height: 20, 47 | backgroundColor: 'white', 48 | '-webkit-transition': 'width 0.7s' 49 | }); 50 | }; 51 | 52 | exports.show = function(text) { 53 | label.text(text); 54 | makeBar(); 55 | return container.fadeIn('slow').appendTo(ui); 56 | }; 57 | 58 | exports.hide = function() { 59 | return container.fadeOut('slow', function() { 60 | return container.detach(); 61 | }); 62 | }; 63 | 64 | exports.progress = function(factor) { 65 | return bar.width(factor * 200); 66 | }; 67 | -------------------------------------------------------------------------------- /lib/processingNode.coffee: -------------------------------------------------------------------------------- 1 | {Framebuffer} = require 'webgl/framebuffer' 2 | Quad = require 'webgl/quad' 3 | 4 | return class ProcessingNode 5 | constructor: (@gl) -> 6 | @fbo = new Framebuffer @gl 7 | @quad = new Quad @gl 8 | @sources = [] 9 | @params = [] 10 | 11 | program: (program) -> 12 | @_program = program 13 | return @ 14 | 15 | addSource: (name, texture) -> 16 | @sources.push 17 | name: name, 18 | texture: texture 19 | return @ 20 | 21 | target: (texture) -> 22 | @width = texture.width 23 | @height = texture.width 24 | @fbo.bind().color(texture).unbind() 25 | return @ 26 | 27 | setUniform: (call, name, value) -> 28 | for obj in @params 29 | if obj.name == name 30 | obj.value = value 31 | obj.call = call 32 | return @ 33 | 34 | @params.push 35 | name: name 36 | value: value 37 | call: call 38 | return @ 39 | 40 | compute: -> 41 | viewport = @gl.getParameter @gl.VIEWPORT 42 | @gl.disable @gl.DEPTH_TEST 43 | @gl.depthMask false 44 | @gl.disable @gl.CULL_FACE 45 | @gl.viewport 0, 0, @width, @height 46 | 47 | @fbo.bind() 48 | @_program 49 | .use() 50 | .val2('viewport', @width, @height) 51 | 52 | for obj, i in @sources 53 | obj.texture.bind(i) 54 | @_program.i(obj.name, i) 55 | 56 | for param in @params 57 | @_program[param.call](param.name, param.value) 58 | 59 | @_program.draw(@quad) 60 | @fbo.unbind() 61 | @gl.viewport.apply @gl, viewport 62 | return @ 63 | -------------------------------------------------------------------------------- /lib/processingNode.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Framebuffer, ProcessingNode, Quad; 3 | 4 | Framebuffer = require('webgl/framebuffer').Framebuffer; 5 | 6 | Quad = require('webgl/quad'); 7 | 8 | return ProcessingNode = (function() { 9 | 10 | function ProcessingNode(gl) { 11 | this.gl = gl; 12 | this.fbo = new Framebuffer(this.gl); 13 | this.quad = new Quad(this.gl); 14 | this.sources = []; 15 | this.params = []; 16 | } 17 | 18 | ProcessingNode.prototype.program = function(program) { 19 | this._program = program; 20 | return this; 21 | }; 22 | 23 | ProcessingNode.prototype.addSource = function(name, texture) { 24 | this.sources.push({ 25 | name: name, 26 | texture: texture 27 | }); 28 | return this; 29 | }; 30 | 31 | ProcessingNode.prototype.target = function(texture) { 32 | this.width = texture.width; 33 | this.height = texture.width; 34 | this.fbo.bind().color(texture).unbind(); 35 | return this; 36 | }; 37 | 38 | ProcessingNode.prototype.setUniform = function(call, name, value) { 39 | var obj, _i, _len, _ref; 40 | _ref = this.params; 41 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 42 | obj = _ref[_i]; 43 | if (obj.name === name) { 44 | obj.value = value; 45 | obj.call = call; 46 | return this; 47 | } 48 | } 49 | this.params.push({ 50 | name: name, 51 | value: value, 52 | call: call 53 | }); 54 | return this; 55 | }; 56 | 57 | ProcessingNode.prototype.compute = function() { 58 | var i, obj, param, viewport, _i, _j, _len, _len1, _ref, _ref1; 59 | viewport = this.gl.getParameter(this.gl.VIEWPORT); 60 | this.gl.disable(this.gl.DEPTH_TEST); 61 | this.gl.depthMask(false); 62 | this.gl.disable(this.gl.CULL_FACE); 63 | this.gl.viewport(0, 0, this.width, this.height); 64 | this.fbo.bind(); 65 | this._program.use().val2('viewport', this.width, this.height); 66 | _ref = this.sources; 67 | for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { 68 | obj = _ref[i]; 69 | obj.texture.bind(i); 70 | this._program.i(obj.name, i); 71 | } 72 | _ref1 = this.params; 73 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 74 | param = _ref1[_j]; 75 | this._program[param.call](param.name, param.value); 76 | } 77 | this._program.draw(this.quad); 78 | this.fbo.unbind(); 79 | this.gl.viewport.apply(this.gl, viewport); 80 | return this; 81 | }; 82 | 83 | return ProcessingNode; 84 | 85 | })(); 86 | -------------------------------------------------------------------------------- /lib/schedule.coffee: -------------------------------------------------------------------------------- 1 | exports.run = (callback) -> 2 | last = gettime() 3 | 4 | step = -> 5 | current = gettime() 6 | delta = current-last 7 | last = current 8 | callback current, delta 9 | requestAnimationFrame step 10 | 11 | requestAnimationFrame step 12 | -------------------------------------------------------------------------------- /lib/schedule.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 3 | exports.run = function(callback) { 4 | var last, step; 5 | last = gettime(); 6 | step = function() { 7 | var current, delta; 8 | current = gettime(); 9 | delta = current - last; 10 | last = current; 11 | callback(current, delta); 12 | return requestAnimationFrame(step); 13 | }; 14 | return requestAnimationFrame(step); 15 | }; 16 | -------------------------------------------------------------------------------- /lib/webgl/cube.coffee: -------------------------------------------------------------------------------- 1 | return class Cube extends require('drawable') 2 | attribs: ['position', 'normal', 'barycentric'] 3 | 4 | constructor: (@gl, s=1) -> 5 | super() 6 | @size = 6 * 6 7 | vertices = [ 8 | -s, -s, -s, 0, 0, -1, 1,0,0, 9 | -s, s, -s, 0, 0, -1, 0,1,0, 10 | s, s, -s, 0, 0, -1, 0,0,1, 11 | s, -s, -s, 0, 0, -1, 1,0,0, 12 | -s, -s, -s, 0, 0, -1, 0,1,0, 13 | s, s, -s, 0, 0, -1, 0,0,1, 14 | 15 | s, s, s, 0, 0, 1, 1,0,0, 16 | -s, s, s, 0, 0, 1, 0,1,0, 17 | -s, -s, s, 0, 0, 1, 0,0,1, 18 | s, s, s, 0, 0, 1, 1,0,0, 19 | -s, -s, s, 0, 0, 1, 0,1,0, 20 | s, -s, s, 0, 0, 1, 0,0,1, 21 | 22 | -s, s, -s, 0, 1, 0, 1,0,0, 23 | -s, s, s, 0, 1, 0, 0,1,0, 24 | s, s, s, 0, 1, 0, 0,0,1, 25 | s, s, -s, 0, 1, 0, 1,0,0, 26 | -s, s, -s, 0, 1, 0, 0,1,0, 27 | s, s, s, 0, 1, 0, 0,0,1, 28 | 29 | s, -s, s, 0, -1, 0, 1,0,0, 30 | -s, -s, s, 0, -1, 0, 0,1,0, 31 | -s, -s, -s, 0, -1, 0, 0,0,1, 32 | s, -s, s, 0, -1, 0, 1,0,0, 33 | -s, -s, -s, 0, -1, 0, 0,1,0, 34 | s, -s, -s, 0, -1, 0, 0,0,1, 35 | 36 | -s, -s, -s, -1, 0, 0, 1,0,0, 37 | -s, -s, s, -1, 0, 0, 0,1,0, 38 | -s, s, s, -1, 0, 0, 0,0,1, 39 | -s, s, -s, -1, 0, 0, 1,0,0, 40 | -s, -s, -s, -1, 0, 0, 0,1,0, 41 | -s, s, s, -1, 0, 0, 0,0,1, 42 | 43 | s, s, s, 1, 0, 0, 1,0,0, 44 | s, -s, s, 1, 0, 0, 0,1,0, 45 | s, -s, -s, 1, 0, 0, 0,0,1, 46 | s, s, s, 1, 0, 0, 1,0,0, 47 | s, -s, -s, 1, 0, 0, 0,1,0, 48 | s, s, -s, 1, 0, 0, 0,0,1, 49 | ] 50 | 51 | @uploadList vertices 52 | 53 | setPointersForShader: (shader) -> 54 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 55 | @setPointer shader, 'position', 3, 0, 9 56 | @setPointer shader, 'normal', 3, 3, 9 57 | @setPointer shader, 'barycentric', 3, 6, 9 58 | 59 | return @ 60 | -------------------------------------------------------------------------------- /lib/webgl/cube.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Cube, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | return Cube = (function(_super) { 7 | 8 | __extends(Cube, _super); 9 | 10 | Cube.prototype.attribs = ['position', 'normal', 'barycentric']; 11 | 12 | function Cube(gl, s) { 13 | var vertices; 14 | this.gl = gl; 15 | if (s == null) { 16 | s = 1; 17 | } 18 | Cube.__super__.constructor.call(this); 19 | this.size = 6 * 6; 20 | 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]; 21 | this.uploadList(vertices); 22 | } 23 | 24 | Cube.prototype.setPointersForShader = function(shader) { 25 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 26 | this.setPointer(shader, 'position', 3, 0, 9); 27 | this.setPointer(shader, 'normal', 3, 3, 9); 28 | this.setPointer(shader, 'barycentric', 3, 6, 9); 29 | return this; 30 | }; 31 | 32 | return Cube; 33 | 34 | })(require('drawable')); 35 | -------------------------------------------------------------------------------- /lib/webgl/drawable.coffee: -------------------------------------------------------------------------------- 1 | return class Drawable 2 | float_size = Float32Array.BYTES_PER_ELEMENT 3 | 4 | constructor: () -> 5 | @first = 0 6 | @size = 0 7 | @buffer = @gl.createBuffer() 8 | @mode = @gl.TRIANGLES 9 | 10 | setPointer: (shader, name, size=3, start=0, stride=0) -> 11 | location = shader.attribLoc name 12 | if location >= 0 13 | @gl.vertexAttribPointer location, size, @gl.FLOAT, false, stride*float_size, start*float_size 14 | return @ 15 | 16 | draw: (shader) -> 17 | if shader then @setPointersForShader shader 18 | @gl.drawArrays @mode, @first, @size 19 | if shader then @disableAttribs shader 20 | return @ 21 | 22 | disableAttribs: (shader) -> 23 | for name in @attribs 24 | location = shader.attribLoc name 25 | if location >= 0 then @gl.disableVertexAttribArray location 26 | return @ 27 | 28 | uploadList: (list) -> 29 | data = new Float32Array list 30 | @upload data 31 | 32 | upload: (data) -> 33 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 34 | @gl.bufferData @gl.ARRAY_BUFFER, data, @gl.STATIC_DRAW 35 | @gl.bindBuffer @gl.ARRAY_BUFFER, null 36 | -------------------------------------------------------------------------------- /lib/webgl/drawable.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Drawable; 3 | 4 | return Drawable = (function() { 5 | var float_size; 6 | 7 | float_size = Float32Array.BYTES_PER_ELEMENT; 8 | 9 | function Drawable() { 10 | this.first = 0; 11 | this.size = 0; 12 | this.buffer = this.gl.createBuffer(); 13 | this.mode = this.gl.TRIANGLES; 14 | } 15 | 16 | Drawable.prototype.setPointer = function(shader, name, size, start, stride) { 17 | var location; 18 | if (size == null) { 19 | size = 3; 20 | } 21 | if (start == null) { 22 | start = 0; 23 | } 24 | if (stride == null) { 25 | stride = 0; 26 | } 27 | location = shader.attribLoc(name); 28 | if (location >= 0) { 29 | this.gl.vertexAttribPointer(location, size, this.gl.FLOAT, false, stride * float_size, start * float_size); 30 | } 31 | return this; 32 | }; 33 | 34 | Drawable.prototype.draw = function(shader) { 35 | if (shader) { 36 | this.setPointersForShader(shader); 37 | } 38 | this.gl.drawArrays(this.mode, this.first, this.size); 39 | if (shader) { 40 | this.disableAttribs(shader); 41 | } 42 | return this; 43 | }; 44 | 45 | Drawable.prototype.disableAttribs = function(shader) { 46 | var location, name, _i, _len, _ref; 47 | _ref = this.attribs; 48 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 49 | name = _ref[_i]; 50 | location = shader.attribLoc(name); 51 | if (location >= 0) { 52 | this.gl.disableVertexAttribArray(location); 53 | } 54 | } 55 | return this; 56 | }; 57 | 58 | Drawable.prototype.uploadList = function(list) { 59 | var data; 60 | data = new Float32Array(list); 61 | return this.upload(data); 62 | }; 63 | 64 | Drawable.prototype.upload = function(data) { 65 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 66 | this.gl.bufferData(this.gl.ARRAY_BUFFER, data, this.gl.STATIC_DRAW); 67 | return this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null); 68 | }; 69 | 70 | return Drawable; 71 | 72 | })(); 73 | -------------------------------------------------------------------------------- /lib/webgl/framebuffer.coffee: -------------------------------------------------------------------------------- 1 | framebufferBinding = null 2 | 3 | exports.Framebuffer = class Framebuffer 4 | constructor: (@gl) -> 5 | @buffer = @gl.createFramebuffer() 6 | 7 | bind: -> 8 | if framebufferBinding isnt @ 9 | @gl.bindFramebuffer @gl.FRAMEBUFFER, @buffer 10 | framebufferBinding = @ 11 | 12 | return @ 13 | 14 | unbind: -> 15 | if framebufferBinding isnt null 16 | @gl.bindFramebuffer @gl.FRAMEBUFFER, null 17 | framebufferBinding = null 18 | 19 | return @ 20 | 21 | check: -> 22 | result = @gl.checkFramebufferStatus @gl.FRAMEBUFFER 23 | switch result 24 | when @gl.FRAMEBUFFER_UNSUPPORTED 25 | throw 'Framebuffer is unsupported' 26 | when @gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT 27 | throw 'Framebuffer incomplete attachment' 28 | when @gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS 29 | throw 'Framebuffer incomplete dimensions' 30 | when @gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 31 | throw 'Framebuffer incomplete missing attachment' 32 | return @ 33 | 34 | color: (texture) -> 35 | @gl.framebufferTexture2D @gl.FRAMEBUFFER, @gl.COLOR_ATTACHMENT0, texture.target, texture.handle, 0 36 | @check() 37 | return @ 38 | 39 | depth: (buffer) -> 40 | @gl.framebufferRenderbuffer @gl.FRAMEBUFFER, @gl.DEPTH_ATTACHMENT, @gl.RENDERBUFFER, buffer.id 41 | @check() 42 | return @ 43 | 44 | destroy: -> 45 | @gl.deleteFramebuffer @buffer 46 | 47 | class Renderbuffer 48 | constructor: (@gl) -> 49 | @id = @gl.createRenderbuffer() 50 | 51 | setSize: (width, height) -> 52 | @gl.bindRenderbuffer @gl.RENDERBUFFER, @id 53 | @gl.renderbufferStorage @gl.RENDERBUFFER, @gl[@format], width, height 54 | @gl.bindRenderbuffer @gl.RENDERBUFFER, null 55 | return @ 56 | 57 | exports.Depthbuffer = class extends Renderbuffer 58 | format: 'DEPTH_COMPONENT16' 59 | -------------------------------------------------------------------------------- /lib/webgl/framebuffer.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Framebuffer, Renderbuffer, framebufferBinding, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | framebufferBinding = null; 7 | 8 | exports.Framebuffer = Framebuffer = (function() { 9 | 10 | function Framebuffer(gl) { 11 | this.gl = gl; 12 | this.buffer = this.gl.createFramebuffer(); 13 | } 14 | 15 | Framebuffer.prototype.bind = function() { 16 | if (framebufferBinding !== this) { 17 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffer); 18 | framebufferBinding = this; 19 | } 20 | return this; 21 | }; 22 | 23 | Framebuffer.prototype.unbind = function() { 24 | if (framebufferBinding !== null) { 25 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); 26 | framebufferBinding = null; 27 | } 28 | return this; 29 | }; 30 | 31 | Framebuffer.prototype.check = function() { 32 | var result; 33 | result = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER); 34 | switch (result) { 35 | case this.gl.FRAMEBUFFER_UNSUPPORTED: 36 | throw 'Framebuffer is unsupported'; 37 | break; 38 | case this.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 39 | throw 'Framebuffer incomplete attachment'; 40 | break; 41 | case this.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 42 | throw 'Framebuffer incomplete dimensions'; 43 | break; 44 | case this.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 45 | throw 'Framebuffer incomplete missing attachment'; 46 | } 47 | return this; 48 | }; 49 | 50 | Framebuffer.prototype.color = function(texture) { 51 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, texture.target, texture.handle, 0); 52 | this.check(); 53 | return this; 54 | }; 55 | 56 | Framebuffer.prototype.depth = function(buffer) { 57 | this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, buffer.id); 58 | this.check(); 59 | return this; 60 | }; 61 | 62 | Framebuffer.prototype.destroy = function() { 63 | return this.gl.deleteFramebuffer(this.buffer); 64 | }; 65 | 66 | return Framebuffer; 67 | 68 | })(); 69 | 70 | Renderbuffer = (function() { 71 | 72 | function Renderbuffer(gl) { 73 | this.gl = gl; 74 | this.id = this.gl.createRenderbuffer(); 75 | } 76 | 77 | Renderbuffer.prototype.setSize = function(width, height) { 78 | this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.id); 79 | this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl[this.format], width, height); 80 | this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null); 81 | return this; 82 | }; 83 | 84 | return Renderbuffer; 85 | 86 | })(); 87 | 88 | exports.Depthbuffer = (function(_super) { 89 | 90 | __extends(_Class, _super); 91 | 92 | function _Class() { 93 | return _Class.__super__.constructor.apply(this, arguments); 94 | } 95 | 96 | _Class.prototype.format = 'DEPTH_COMPONENT16'; 97 | 98 | return _Class; 99 | 100 | })(Renderbuffer); 101 | -------------------------------------------------------------------------------- /lib/webgl/hexgrid.coffee: -------------------------------------------------------------------------------- 1 | clamp = (value, left, right) -> 2 | return if value < left then left else if value > right then right else value 3 | 4 | return class Hexgrid extends require('drawable') 5 | attribs: ['position', 'texcoord', 'barycentric'] 6 | 7 | constructor: (@gl, xsize=16, ysize=16, width=1, height=1) -> 8 | super() 9 | vertices = [] 10 | 11 | for x in [0..xsize] 12 | x1 = clamp((x-0.5)/xsize, 0, 1) 13 | x2 = clamp((x+0.0)/xsize, 0, 1) 14 | x3 = clamp((x+0.5)/xsize, 0, 1) 15 | x4 = clamp((x+1.0)/xsize, 0, 1) 16 | for y in [0...ysize] by 2 17 | t = (y+0)/ysize 18 | m = (y+1)/ysize 19 | b = (y+2)/ysize 20 | 21 | vertices.push( 22 | x2*width,0,m*height, x2,m, 0,0,1, 23 | x3*width,0,t*height, x3,t, 0,1,0, 24 | x1*width,0,t*height, x1,t, 1,0,0, 25 | 26 | x4*width,0,m*height, x4,m, 0,0,1, 27 | x3*width,0,t*height, x3,t, 0,1,0, 28 | x2*width,0,m*height, x2,m, 1,0,0, 29 | 30 | x3*width,0,b*height, x3,b, 0,0,1, 31 | x2*width,0,m*height, x2,m, 0,1,0, 32 | x1*width,0,b*height, x1,b, 1,0,0, 33 | 34 | x3*width,0,b*height, x3,b, 0,0,1 35 | x4*width,0,m*height, x4,m, 0,1,0, 36 | x2*width,0,m*height, x2,m, 1,0,0, 37 | ) 38 | @size = vertices.length/8 39 | @uploadList vertices 40 | 41 | setPointersForShader: (shader) -> 42 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 43 | @setPointer shader, 'position', 3, 0, 8 44 | @setPointer shader, 'texcoord', 2, 3, 8 45 | @setPointer shader, 'barycentric', 3, 5, 8 46 | 47 | return @ 48 | -------------------------------------------------------------------------------- /lib/webgl/hexgrid.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Hexgrid, clamp, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | clamp = function(value, left, right) { 7 | if (value < left) { 8 | return left; 9 | } else if (value > right) { 10 | return right; 11 | } else { 12 | return value; 13 | } 14 | }; 15 | 16 | return Hexgrid = (function(_super) { 17 | 18 | __extends(Hexgrid, _super); 19 | 20 | Hexgrid.prototype.attribs = ['position', 'texcoord', 'barycentric']; 21 | 22 | function Hexgrid(gl, xsize, ysize, width, height) { 23 | var b, m, t, vertices, x, x1, x2, x3, x4, y, _i, _j; 24 | this.gl = gl; 25 | if (xsize == null) { 26 | xsize = 16; 27 | } 28 | if (ysize == null) { 29 | ysize = 16; 30 | } 31 | if (width == null) { 32 | width = 1; 33 | } 34 | if (height == null) { 35 | height = 1; 36 | } 37 | Hexgrid.__super__.constructor.call(this); 38 | vertices = []; 39 | for (x = _i = 0; 0 <= xsize ? _i <= xsize : _i >= xsize; x = 0 <= xsize ? ++_i : --_i) { 40 | x1 = clamp((x - 0.5) / xsize, 0, 1); 41 | x2 = clamp((x + 0.0) / xsize, 0, 1); 42 | x3 = clamp((x + 0.5) / xsize, 0, 1); 43 | x4 = clamp((x + 1.0) / xsize, 0, 1); 44 | for (y = _j = 0; _j < ysize; y = _j += 2) { 45 | t = (y + 0) / ysize; 46 | m = (y + 1) / ysize; 47 | b = (y + 2) / ysize; 48 | 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); 49 | } 50 | } 51 | this.size = vertices.length / 8; 52 | this.uploadList(vertices); 53 | } 54 | 55 | Hexgrid.prototype.setPointersForShader = function(shader) { 56 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 57 | this.setPointer(shader, 'position', 3, 0, 8); 58 | this.setPointer(shader, 'texcoord', 2, 3, 8); 59 | this.setPointer(shader, 'barycentric', 3, 5, 8); 60 | return this; 61 | }; 62 | 63 | return Hexgrid; 64 | 65 | })(require('drawable')); 66 | -------------------------------------------------------------------------------- /lib/webgl/model.coffee: -------------------------------------------------------------------------------- 1 | return class Model extends require('drawable') 2 | attribs: ['position', 'normal', 'texcoord'] 3 | 4 | constructor: (@gl, data) -> 5 | super() 6 | @size = data.byteLength/(8*Float32Array.BYTES_PER_ELEMENT) 7 | @upload data 8 | 9 | setPointersForShader: (shader) -> 10 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 11 | @setPointer shader, 'position', 3, 0, 8 12 | @setPointer shader, 'normal', 3, 3, 8 13 | @setPointer shader, 'texcoord', 2, 6, 8 14 | 15 | return @ 16 | -------------------------------------------------------------------------------- /lib/webgl/model.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Model, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | return Model = (function(_super) { 7 | 8 | __extends(Model, _super); 9 | 10 | Model.prototype.attribs = ['position', 'normal', 'texcoord']; 11 | 12 | function Model(gl, data) { 13 | this.gl = gl; 14 | Model.__super__.constructor.call(this); 15 | this.size = data.byteLength / (8 * Float32Array.BYTES_PER_ELEMENT); 16 | this.upload(data); 17 | } 18 | 19 | Model.prototype.setPointersForShader = function(shader) { 20 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 21 | this.setPointer(shader, 'position', 3, 0, 8); 22 | this.setPointer(shader, 'normal', 3, 3, 8); 23 | this.setPointer(shader, 'texcoord', 2, 6, 8); 24 | return this; 25 | }; 26 | 27 | return Model; 28 | 29 | })(require('drawable')); 30 | -------------------------------------------------------------------------------- /lib/webgl/plane.coffee: -------------------------------------------------------------------------------- 1 | return class Plane extends require('drawable') 2 | attribs: ['position', 'normal', 'texcoord'] 3 | 4 | constructor: (@gl, s=1) -> 5 | super() 6 | @size = 6 7 | 8 | vertices = [ 9 | -s, 0, -s, 0, 1, 0, 0,0, 10 | -s, 0, s, 0, 1, 0, 0,1, 11 | s, 0, s, 0, 1, 0, 1,1, 12 | 13 | s, 0, -s, 0, 1, 0, 1,0, 14 | -s, 0, -s, 0, 1, 0, 0,0, 15 | s, 0, s, 0, 1, 0, 1,1, 16 | ] 17 | 18 | @uploadList vertices 19 | 20 | setPointersForShader: (shader) -> 21 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 22 | @setPointer shader, 'position', 3, 0, 8 23 | @setPointer shader, 'normal', 3, 3, 8 24 | @setPointer shader, 'texcoord', 2, 6, 8 25 | 26 | return @ 27 | -------------------------------------------------------------------------------- /lib/webgl/plane.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Plane, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | return Plane = (function(_super) { 7 | 8 | __extends(Plane, _super); 9 | 10 | Plane.prototype.attribs = ['position', 'normal', 'texcoord']; 11 | 12 | function Plane(gl, s) { 13 | var vertices; 14 | this.gl = gl; 15 | if (s == null) { 16 | s = 1; 17 | } 18 | Plane.__super__.constructor.call(this); 19 | this.size = 6; 20 | 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]; 21 | this.uploadList(vertices); 22 | } 23 | 24 | Plane.prototype.setPointersForShader = function(shader) { 25 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 26 | this.setPointer(shader, 'position', 3, 0, 8); 27 | this.setPointer(shader, 'normal', 3, 3, 8); 28 | this.setPointer(shader, 'texcoord', 2, 6, 8); 29 | return this; 30 | }; 31 | 32 | return Plane; 33 | 34 | })(require('drawable')); 35 | -------------------------------------------------------------------------------- /lib/webgl/quad.coffee: -------------------------------------------------------------------------------- 1 | return class Quad extends require('drawable') 2 | attribs: ['position'] 3 | 4 | constructor: (@gl) -> 5 | super() 6 | @size = 6 7 | 8 | vertices = [ 9 | -1, -1, 1, -1, 1, 1, 10 | -1, -1, 1, 1, -1, 1, 11 | ] 12 | 13 | @uploadList vertices 14 | 15 | setPointersForShader: (shader) -> 16 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 17 | @setPointer shader, 'position', 2 18 | 19 | return @ 20 | -------------------------------------------------------------------------------- /lib/webgl/quad.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Quad, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | return Quad = (function(_super) { 7 | 8 | __extends(Quad, _super); 9 | 10 | Quad.prototype.attribs = ['position']; 11 | 12 | function Quad(gl) { 13 | var vertices; 14 | this.gl = gl; 15 | Quad.__super__.constructor.call(this); 16 | this.size = 6; 17 | vertices = [-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]; 18 | this.uploadList(vertices); 19 | } 20 | 21 | Quad.prototype.setPointersForShader = function(shader) { 22 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 23 | this.setPointer(shader, 'position', 2); 24 | return this; 25 | }; 26 | 27 | return Quad; 28 | 29 | })(require('drawable')); 30 | -------------------------------------------------------------------------------- /lib/webgl/shader.coffee: -------------------------------------------------------------------------------- 1 | current = null 2 | 3 | return class Shader 4 | constructor: (@gl, source) -> 5 | @program = @gl.createProgram() 6 | @vs = @gl.createShader gl.VERTEX_SHADER 7 | @fs = @gl.createShader gl.FRAGMENT_SHADER 8 | 9 | @gl.attachShader @program, @vs 10 | @gl.attachShader @program, @fs 11 | 12 | @link source 13 | 14 | preprocess: (source) -> 15 | lines = source.split '\n' 16 | shaders = {'global': '', 'fragment': '', 'vertex': ''} 17 | type = 'global' 18 | for line, i in lines 19 | match = line.match /^(\w+):$/ 20 | if match 21 | type = match[1] 22 | else 23 | shaders[type] += '#line ' + i + '\n' + line + '\n' 24 | 25 | directives = [ 26 | '#ifdef GL_FRAGMENT_PRECISION_HIGH', 27 | 'precision highp int;', 28 | 'precision highp float;', 29 | '#else', 30 | 'precision mediump int;', 31 | 'precision mediump float;', 32 | '#endif', 33 | ].join('\n') + '\n' 34 | 35 | shaders.fragment = directives + shaders.global + shaders.fragment 36 | shaders.vertex = directives + shaders.global + shaders.vertex 37 | return shaders 38 | 39 | link: (source) -> 40 | shaders = @preprocess source 41 | @compile @vs, shaders.vertex 42 | @compile @fs, shaders.fragment 43 | @gl.linkProgram @program 44 | 45 | if not @gl.getProgramParameter @program, @gl.LINK_STATUS 46 | throw "Shader Link Error: #{@gl.getProgramInfoLog(@program)}" 47 | 48 | @attrib_cache = {} 49 | @uniform_cache = {} 50 | 51 | compile: (shader, source) -> 52 | @gl.shaderSource shader, source 53 | @gl.compileShader shader 54 | 55 | if not @gl.getShaderParameter shader, @gl.COMPILE_STATUS 56 | throw "Shader Compile Error: #{@gl.getShaderInfoLog(shader)}" 57 | return 58 | 59 | attribLoc: (name) -> 60 | location = @attrib_cache[name] 61 | if location is undefined 62 | location = @attrib_cache[name] = @gl.getAttribLocation @program, name 63 | @gl.enableVertexAttribArray location if location >= 0 64 | return location 65 | 66 | use: -> 67 | @gl.useProgram @program 68 | return @ 69 | 70 | loc: (name) -> 71 | location = @uniform_cache[name] 72 | if location is undefined 73 | location = @uniform_cache[name] = @gl.getUniformLocation @program, name 74 | return location 75 | 76 | i: (name, value) -> 77 | loc = @loc name 78 | @gl.uniform1i loc, value if loc 79 | return @ 80 | 81 | f: (name, value) -> 82 | loc = @loc name 83 | @gl.uniform1f loc, value if loc 84 | return @ 85 | 86 | val2: (name, a, b) -> 87 | loc = @loc name 88 | @gl.uniform2f loc, a, b if loc 89 | return @ 90 | 91 | val3: (name, a, b, c) -> 92 | loc = @loc name 93 | @gl.uniform3f loc, a, b, c if loc 94 | return @ 95 | 96 | vec3: (name, value) -> 97 | loc = @loc name 98 | @gl.uniform3fv loc, value if loc 99 | return @ 100 | 101 | val4: (name, a, b, c, d) -> 102 | loc = @loc name 103 | @gl.uniform4f loc, a, b, c, d if loc 104 | return @ 105 | 106 | vec4: (name, a, b, c, e) -> 107 | loc = @loc name 108 | @gl.uniform2f loc, a, b, c, e if loc 109 | return @ 110 | 111 | mat4: (name, value) -> 112 | loc = @loc name 113 | if loc 114 | if value instanceof Mat4 115 | @gl.uniformMatrix4fv loc, @gl.FALSE, value.data 116 | else 117 | @gl.uniformMatrix4fv loc, @gl.FALSE, value 118 | return @ 119 | 120 | mat3: (name, value) -> 121 | loc = @loc name 122 | @gl.uniformMatrix3fv loc, @gl.FALSE, value.data if loc 123 | return @ 124 | 125 | draw: (drawable) -> 126 | drawable 127 | .setPointersForShader(@) 128 | .draw() 129 | .disableAttribs(@) 130 | return @ 131 | -------------------------------------------------------------------------------- /lib/webgl/shader.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Shader, current; 3 | 4 | current = null; 5 | 6 | return Shader = (function() { 7 | 8 | function Shader(gl, source) { 9 | this.gl = gl; 10 | this.program = this.gl.createProgram(); 11 | this.vs = this.gl.createShader(gl.VERTEX_SHADER); 12 | this.fs = this.gl.createShader(gl.FRAGMENT_SHADER); 13 | this.gl.attachShader(this.program, this.vs); 14 | this.gl.attachShader(this.program, this.fs); 15 | this.link(source); 16 | } 17 | 18 | Shader.prototype.preprocess = function(source) { 19 | var directives, i, line, lines, match, shaders, type, _i, _len; 20 | lines = source.split('\n'); 21 | shaders = { 22 | 'global': '', 23 | 'fragment': '', 24 | 'vertex': '' 25 | }; 26 | type = 'global'; 27 | for (i = _i = 0, _len = lines.length; _i < _len; i = ++_i) { 28 | line = lines[i]; 29 | match = line.match(/^(\w+):$/); 30 | if (match) { 31 | type = match[1]; 32 | } else { 33 | shaders[type] += '#line ' + i + '\n' + line + '\n'; 34 | } 35 | } 36 | directives = ['#ifdef GL_FRAGMENT_PRECISION_HIGH', 'precision highp int;', 'precision highp float;', '#else', 'precision mediump int;', 'precision mediump float;', '#endif'].join('\n') + '\n'; 37 | shaders.fragment = directives + shaders.global + shaders.fragment; 38 | shaders.vertex = directives + shaders.global + shaders.vertex; 39 | return shaders; 40 | }; 41 | 42 | Shader.prototype.link = function(source) { 43 | var shaders; 44 | shaders = this.preprocess(source); 45 | this.compile(this.vs, shaders.vertex); 46 | this.compile(this.fs, shaders.fragment); 47 | this.gl.linkProgram(this.program); 48 | if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) { 49 | throw "Shader Link Error: " + (this.gl.getProgramInfoLog(this.program)); 50 | } 51 | this.attrib_cache = {}; 52 | return this.uniform_cache = {}; 53 | }; 54 | 55 | Shader.prototype.compile = function(shader, source) { 56 | this.gl.shaderSource(shader, source); 57 | this.gl.compileShader(shader); 58 | if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { 59 | throw "Shader Compile Error: " + (this.gl.getShaderInfoLog(shader)); 60 | } 61 | }; 62 | 63 | Shader.prototype.attribLoc = function(name) { 64 | var location; 65 | location = this.attrib_cache[name]; 66 | if (location === void 0) { 67 | location = this.attrib_cache[name] = this.gl.getAttribLocation(this.program, name); 68 | } 69 | if (location >= 0) { 70 | this.gl.enableVertexAttribArray(location); 71 | } 72 | return location; 73 | }; 74 | 75 | Shader.prototype.use = function() { 76 | this.gl.useProgram(this.program); 77 | return this; 78 | }; 79 | 80 | Shader.prototype.loc = function(name) { 81 | var location; 82 | location = this.uniform_cache[name]; 83 | if (location === void 0) { 84 | location = this.uniform_cache[name] = this.gl.getUniformLocation(this.program, name); 85 | } 86 | return location; 87 | }; 88 | 89 | Shader.prototype.i = function(name, value) { 90 | var loc; 91 | loc = this.loc(name); 92 | if (loc) { 93 | this.gl.uniform1i(loc, value); 94 | } 95 | return this; 96 | }; 97 | 98 | Shader.prototype.f = function(name, value) { 99 | var loc; 100 | loc = this.loc(name); 101 | if (loc) { 102 | this.gl.uniform1f(loc, value); 103 | } 104 | return this; 105 | }; 106 | 107 | Shader.prototype.val2 = function(name, a, b) { 108 | var loc; 109 | loc = this.loc(name); 110 | if (loc) { 111 | this.gl.uniform2f(loc, a, b); 112 | } 113 | return this; 114 | }; 115 | 116 | Shader.prototype.val3 = function(name, a, b, c) { 117 | var loc; 118 | loc = this.loc(name); 119 | if (loc) { 120 | this.gl.uniform3f(loc, a, b, c); 121 | } 122 | return this; 123 | }; 124 | 125 | Shader.prototype.vec3 = function(name, value) { 126 | var loc; 127 | loc = this.loc(name); 128 | if (loc) { 129 | this.gl.uniform3fv(loc, value); 130 | } 131 | return this; 132 | }; 133 | 134 | Shader.prototype.val4 = function(name, a, b, c, d) { 135 | var loc; 136 | loc = this.loc(name); 137 | if (loc) { 138 | this.gl.uniform4f(loc, a, b, c, d); 139 | } 140 | return this; 141 | }; 142 | 143 | Shader.prototype.vec4 = function(name, a, b, c, e) { 144 | var loc; 145 | loc = this.loc(name); 146 | if (loc) { 147 | this.gl.uniform2f(loc, a, b, c, e); 148 | } 149 | return this; 150 | }; 151 | 152 | Shader.prototype.mat4 = function(name, value) { 153 | var loc; 154 | loc = this.loc(name); 155 | if (loc) { 156 | if (value instanceof Mat4) { 157 | this.gl.uniformMatrix4fv(loc, this.gl.FALSE, value.data); 158 | } else { 159 | this.gl.uniformMatrix4fv(loc, this.gl.FALSE, value); 160 | } 161 | } 162 | return this; 163 | }; 164 | 165 | Shader.prototype.mat3 = function(name, value) { 166 | var loc; 167 | loc = this.loc(name); 168 | if (loc) { 169 | this.gl.uniformMatrix3fv(loc, this.gl.FALSE, value.data); 170 | } 171 | return this; 172 | }; 173 | 174 | Shader.prototype.draw = function(drawable) { 175 | drawable.setPointersForShader(this).draw().disableAttribs(this); 176 | return this; 177 | }; 178 | 179 | return Shader; 180 | 181 | })(); 182 | -------------------------------------------------------------------------------- /lib/webgl/sphere.coffee: -------------------------------------------------------------------------------- 1 | phi = (1+Math.sqrt(5))/2 2 | 3 | midp = (v1, v2) -> 4 | x1 = v1[0] 5 | y1 = v1[1] 6 | z1 = v1[2] 7 | 8 | x2 = v2[0] 9 | y2 = v2[1] 10 | z2 = v2[2] 11 | 12 | x3 = (x1+x2)/2 13 | y3 = (y1+y2)/2 14 | z3 = (z1+z2)/2 15 | 16 | return [x3, y3, z3] 17 | 18 | normalize = (faces, r) -> 19 | r ?= 1 20 | result = [] 21 | for face in faces 22 | new_face = [] 23 | result.push new_face 24 | for vertex in face 25 | x = vertex[0] 26 | y = vertex[1] 27 | z = vertex[2] 28 | l = Math.sqrt(x*x + y*y + z*z) 29 | new_face.push [(r*x)/l, (r*y)/l, (r*z)/l] 30 | return result 31 | 32 | subdivide = (faces) -> 33 | result = [] 34 | for face in faces 35 | v0 = face[0] 36 | v1 = face[1] 37 | v2 = face[2] 38 | 39 | va = midp v0, v1 40 | vb = midp v1, v2 41 | vc = midp v2, v0 42 | 43 | result.push( 44 | [v0, va, vc], 45 | [va, v1, vb], 46 | [vc, vb, v2], 47 | [va, vb, vc], 48 | ) 49 | 50 | return result 51 | 52 | v1 = [ 1, phi, 0] 53 | v2 = [ -1, phi, 0] 54 | v3 = [ 0, 1, phi] 55 | v4 = [ 0, 1, -phi] 56 | v5 = [ phi, 0, 1] 57 | v6 = [-phi, 0, 1] 58 | v7 = [-phi, 0, -1] 59 | v8 = [ phi, 0, -1] 60 | v9 = [ 0, -1, phi] 61 | v10 = [ 0, -1, -phi] 62 | v11 = [ -1, -phi, 0] 63 | v12 = [ 1, -phi, 0] 64 | 65 | faces = [ 66 | [ v1, v2, v3], 67 | [ v2, v1, v4], 68 | [ v1, v3, v5], 69 | [ v2, v6, v3], 70 | [ v2, v7, v6], 71 | [ v2, v4, v7], 72 | [ v1, v5, v8], 73 | [ v1, v8, v4], 74 | [ v9, v3, v6], 75 | [ v3, v9, v5], 76 | [ v4, v10, v7], 77 | [ v4, v8, v10], 78 | [ v6, v7, v11], 79 | [ v6, v11, v9], 80 | [ v7, v10, v11], 81 | [ v5, v12, v8], 82 | [v12, v5, v9], 83 | [v12, v10, v8], 84 | [v11, v12, v9], 85 | [v12, v11, v10] 86 | ] 87 | 88 | icosahedron = normalize(faces) 89 | 90 | vertexlist = (faces) -> 91 | vertices = [] 92 | for face in faces 93 | for vertex in face 94 | x = vertex[0] 95 | y = vertex[1] 96 | z = vertex[2] 97 | vertices.push x, y, z 98 | return vertices 99 | 100 | return class Sphere extends require('drawable') 101 | attribs: ['position'] 102 | 103 | constructor: (@gl, radius=1, subdivisions=3) -> 104 | super() 105 | template = icosahedron 106 | for i in [0...subdivisions] 107 | template = subdivide template 108 | template = normalize template 109 | faces = normalize template, radius 110 | vertices = vertexlist faces 111 | 112 | @size = vertices.length/3 113 | @uploadList vertices 114 | 115 | setPointersForShader: (shader) -> 116 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 117 | @setPointer shader, 'position', 3 118 | 119 | return @ 120 | -------------------------------------------------------------------------------- /lib/webgl/sphere.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Sphere, faces, icosahedron, midp, normalize, phi, subdivide, v1, v10, v11, v12, v2, v3, v4, v5, v6, v7, v8, v9, vertexlist, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | phi = (1 + Math.sqrt(5)) / 2; 7 | 8 | midp = function(v1, v2) { 9 | var x1, x2, x3, y1, y2, y3, z1, z2, z3; 10 | x1 = v1[0]; 11 | y1 = v1[1]; 12 | z1 = v1[2]; 13 | x2 = v2[0]; 14 | y2 = v2[1]; 15 | z2 = v2[2]; 16 | x3 = (x1 + x2) / 2; 17 | y3 = (y1 + y2) / 2; 18 | z3 = (z1 + z2) / 2; 19 | return [x3, y3, z3]; 20 | }; 21 | 22 | normalize = function(faces, r) { 23 | var face, l, new_face, result, vertex, x, y, z, _i, _j, _len, _len1; 24 | if (r == null) { 25 | r = 1; 26 | } 27 | result = []; 28 | for (_i = 0, _len = faces.length; _i < _len; _i++) { 29 | face = faces[_i]; 30 | new_face = []; 31 | result.push(new_face); 32 | for (_j = 0, _len1 = face.length; _j < _len1; _j++) { 33 | vertex = face[_j]; 34 | x = vertex[0]; 35 | y = vertex[1]; 36 | z = vertex[2]; 37 | l = Math.sqrt(x * x + y * y + z * z); 38 | new_face.push([(r * x) / l, (r * y) / l, (r * z) / l]); 39 | } 40 | } 41 | return result; 42 | }; 43 | 44 | subdivide = function(faces) { 45 | var face, result, v0, v1, v2, va, vb, vc, _i, _len; 46 | result = []; 47 | for (_i = 0, _len = faces.length; _i < _len; _i++) { 48 | face = faces[_i]; 49 | v0 = face[0]; 50 | v1 = face[1]; 51 | v2 = face[2]; 52 | va = midp(v0, v1); 53 | vb = midp(v1, v2); 54 | vc = midp(v2, v0); 55 | result.push([v0, va, vc], [va, v1, vb], [vc, vb, v2], [va, vb, vc]); 56 | } 57 | return result; 58 | }; 59 | 60 | v1 = [1, phi, 0]; 61 | 62 | v2 = [-1, phi, 0]; 63 | 64 | v3 = [0, 1, phi]; 65 | 66 | v4 = [0, 1, -phi]; 67 | 68 | v5 = [phi, 0, 1]; 69 | 70 | v6 = [-phi, 0, 1]; 71 | 72 | v7 = [-phi, 0, -1]; 73 | 74 | v8 = [phi, 0, -1]; 75 | 76 | v9 = [0, -1, phi]; 77 | 78 | v10 = [0, -1, -phi]; 79 | 80 | v11 = [-1, -phi, 0]; 81 | 82 | v12 = [1, -phi, 0]; 83 | 84 | 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]]; 85 | 86 | icosahedron = normalize(faces); 87 | 88 | vertexlist = function(faces) { 89 | var face, vertex, vertices, x, y, z, _i, _j, _len, _len1; 90 | vertices = []; 91 | for (_i = 0, _len = faces.length; _i < _len; _i++) { 92 | face = faces[_i]; 93 | for (_j = 0, _len1 = face.length; _j < _len1; _j++) { 94 | vertex = face[_j]; 95 | x = vertex[0]; 96 | y = vertex[1]; 97 | z = vertex[2]; 98 | vertices.push(x, y, z); 99 | } 100 | } 101 | return vertices; 102 | }; 103 | 104 | return Sphere = (function(_super) { 105 | 106 | __extends(Sphere, _super); 107 | 108 | Sphere.prototype.attribs = ['position']; 109 | 110 | function Sphere(gl, radius, subdivisions) { 111 | var i, template, vertices, _i; 112 | this.gl = gl; 113 | if (radius == null) { 114 | radius = 1; 115 | } 116 | if (subdivisions == null) { 117 | subdivisions = 3; 118 | } 119 | Sphere.__super__.constructor.call(this); 120 | template = icosahedron; 121 | for (i = _i = 0; 0 <= subdivisions ? _i < subdivisions : _i > subdivisions; i = 0 <= subdivisions ? ++_i : --_i) { 122 | template = subdivide(template); 123 | template = normalize(template); 124 | } 125 | faces = normalize(template, radius); 126 | vertices = vertexlist(faces); 127 | this.size = vertices.length / 3; 128 | this.uploadList(vertices); 129 | } 130 | 131 | Sphere.prototype.setPointersForShader = function(shader) { 132 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 133 | this.setPointer(shader, 'position', 3); 134 | return this; 135 | }; 136 | 137 | return Sphere; 138 | 139 | })(require('drawable')); 140 | -------------------------------------------------------------------------------- /lib/webgl/texture.coffee: -------------------------------------------------------------------------------- 1 | {Framebuffer} = require 'framebuffer' 2 | 3 | class Texture 4 | bound = [] 5 | ids = 0 6 | 7 | constructor: -> 8 | @handle = @gl.createTexture() 9 | @id = ids++ 10 | 11 | bind: (unit=0) -> 12 | if bound[unit] != @id 13 | @gl.activeTexture @gl.TEXTURE0+unit 14 | @gl.bindTexture @target, @handle 15 | bound[unit] = @id 16 | return @ 17 | 18 | mipmap: -> 19 | @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.LINEAR 20 | @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR_MIPMAP_LINEAR 21 | @gl.generateMipmap @target 22 | return @ 23 | 24 | mipmapNearest: -> 25 | @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.NEAREST 26 | @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR_MIPMAP_LINEAR 27 | @gl.generateMipmap @target 28 | return @ 29 | 30 | linear: -> 31 | @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.LINEAR 32 | @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR 33 | return @ 34 | 35 | nearest: -> 36 | @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.NEAREST 37 | @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.NEAREST 38 | return @ 39 | 40 | clampToEdge: -> 41 | @gl.texParameteri @target, @gl.TEXTURE_WRAP_S, @gl.CLAMP_TO_EDGE 42 | @gl.texParameteri @target, @gl.TEXTURE_WRAP_T, @gl.CLAMP_TO_EDGE 43 | return @ 44 | 45 | repeat: -> 46 | @gl.texParameteri @target, @gl.TEXTURE_WRAP_S, @gl.REPEAT 47 | @gl.texParameteri @target, @gl.TEXTURE_WRAP_T, @gl.REPEAT 48 | return @ 49 | 50 | anisotropy: -> 51 | ext = @gl.getExtension 'WEBKIT_EXT_texture_filter_anisotropic' 52 | if ext 53 | max = @gl.getParameter ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT 54 | @gl.texParameterf @target, ext.TEXTURE_MAX_ANISOTROPY_EXT, max 55 | return @ 56 | 57 | exports.Texture2D = class Texture2D extends Texture 58 | constructor: (@gl, {@channels, @format}={}) -> 59 | super() 60 | @channels ?= @gl.RGBA 61 | @format ?= @gl.RGBA 62 | @target = @gl.TEXTURE_2D 63 | 64 | upload: (image) -> 65 | @uploadImage image 66 | return @ 67 | 68 | uploadImage: (image) -> 69 | @width = image.width 70 | @height = image.height 71 | @gl.texImage2D @target, 0, @channels, @format, @gl.UNSIGNED_BYTE, image 72 | return @ 73 | 74 | uploadData: (data, @width, @height, type=@gl.UNSIGNED_BYTE) -> 75 | @gl.texImage2D @target, 0, @channels, width, height, 0, @format, type, data 76 | return @ 77 | 78 | setSize: (@width, @height, type=@gl.UNSIGNED_BYTE) -> 79 | @gl.texImage2D @target, 0, @channels, width, height, 0, @format, type, null 80 | return @ 81 | 82 | read: (dst=new Uint8Array(@width*@height*4)) -> 83 | if @fbo 84 | @fbo.bind() 85 | else 86 | @fbo = new Framebuffer(@gl).bind().color(@) 87 | 88 | @gl.readPixels 0, 0, @width, @height, @gl.RGBA, @gl.UNSIGNED_BYTE, dst 89 | @fbo.unbind() 90 | 91 | return dst 92 | 93 | toPNG: -> 94 | canvas = document.createElement 'canvas' 95 | canvas.height = @height 96 | canvas.width = @width 97 | ctx = canvas.getContext '2d' 98 | imgdata = ctx.createImageData @width, @height 99 | imgdata.data.set @read(), 0 100 | ctx.putImageData imgdata, 0, 0 101 | url = canvas.toDataURL 'image/png' 102 | data = atob(url.split(',')[1]) 103 | result = new Uint8Array(data.length) 104 | for i in [0...data.length] 105 | result[i] = data.charCodeAt i 106 | return result 107 | 108 | exports.Cubemap = class Cubemap extends Texture 109 | constructor: (@gl) -> 110 | super() 111 | @target = @gl.TEXTURE_CUBE_MAP 112 | 113 | uploadSide: (name, image) -> 114 | @gl.texImage2D @gl['TEXTURE_CUBE_MAP_' + name], 0, @gl.RGBA, @gl.RGBA, @gl.UNSIGNED_BYTE, image 115 | 116 | upload: (folder, ext='jpg') -> 117 | @uploadSide 'POSITIVE_Y', folder.get "up.#{ext}" 118 | @uploadSide 'NEGATIVE_Y', folder.get "down.#{ext}" 119 | @uploadSide 'POSITIVE_X', folder.get "right.#{ext}" 120 | @uploadSide 'NEGATIVE_X', folder.get "left.#{ext}" 121 | @uploadSide 'POSITIVE_Z', folder.get "front.#{ext}" 122 | @uploadSide 'NEGATIVE_Z', folder.get "back.#{ext}" 123 | return @ 124 | -------------------------------------------------------------------------------- /lib/webgl/texture.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Cubemap, Framebuffer, Texture, Texture2D, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }; 5 | 6 | Framebuffer = require('framebuffer').Framebuffer; 7 | 8 | Texture = (function() { 9 | var bound, ids; 10 | 11 | bound = []; 12 | 13 | ids = 0; 14 | 15 | function Texture() { 16 | this.handle = this.gl.createTexture(); 17 | this.id = ids++; 18 | } 19 | 20 | Texture.prototype.bind = function(unit) { 21 | if (unit == null) { 22 | unit = 0; 23 | } 24 | if (bound[unit] !== this.id) { 25 | this.gl.activeTexture(this.gl.TEXTURE0 + unit); 26 | this.gl.bindTexture(this.target, this.handle); 27 | bound[unit] = this.id; 28 | } 29 | return this; 30 | }; 31 | 32 | Texture.prototype.mipmap = function() { 33 | this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); 34 | this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR); 35 | this.gl.generateMipmap(this.target); 36 | return this; 37 | }; 38 | 39 | Texture.prototype.mipmapNearest = function() { 40 | this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); 41 | this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR); 42 | this.gl.generateMipmap(this.target); 43 | return this; 44 | }; 45 | 46 | Texture.prototype.linear = function() { 47 | this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); 48 | this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); 49 | return this; 50 | }; 51 | 52 | Texture.prototype.nearest = function() { 53 | this.gl.texParameteri(this.target, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST); 54 | this.gl.texParameteri(this.target, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST); 55 | return this; 56 | }; 57 | 58 | Texture.prototype.clampToEdge = function() { 59 | this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); 60 | this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); 61 | return this; 62 | }; 63 | 64 | Texture.prototype.repeat = function() { 65 | this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT); 66 | this.gl.texParameteri(this.target, this.gl.TEXTURE_WRAP_T, this.gl.REPEAT); 67 | return this; 68 | }; 69 | 70 | Texture.prototype.anisotropy = function() { 71 | var ext, max; 72 | ext = this.gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); 73 | if (ext) { 74 | max = this.gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT); 75 | this.gl.texParameterf(this.target, ext.TEXTURE_MAX_ANISOTROPY_EXT, max); 76 | } 77 | return this; 78 | }; 79 | 80 | return Texture; 81 | 82 | })(); 83 | 84 | exports.Texture2D = Texture2D = (function(_super) { 85 | 86 | __extends(Texture2D, _super); 87 | 88 | function Texture2D(gl, _arg) { 89 | var _ref, _ref1, _ref2; 90 | this.gl = gl; 91 | _ref = _arg != null ? _arg : {}, this.channels = _ref.channels, this.format = _ref.format; 92 | Texture2D.__super__.constructor.call(this); 93 | if ((_ref1 = this.channels) == null) { 94 | this.channels = this.gl.RGBA; 95 | } 96 | if ((_ref2 = this.format) == null) { 97 | this.format = this.gl.RGBA; 98 | } 99 | this.target = this.gl.TEXTURE_2D; 100 | } 101 | 102 | Texture2D.prototype.upload = function(image) { 103 | this.uploadImage(image); 104 | return this; 105 | }; 106 | 107 | Texture2D.prototype.uploadImage = function(image) { 108 | this.width = image.width; 109 | this.height = image.height; 110 | this.gl.texImage2D(this.target, 0, this.channels, this.format, this.gl.UNSIGNED_BYTE, image); 111 | return this; 112 | }; 113 | 114 | Texture2D.prototype.uploadData = function(data, width, height, type) { 115 | this.width = width; 116 | this.height = height; 117 | if (type == null) { 118 | type = this.gl.UNSIGNED_BYTE; 119 | } 120 | this.gl.texImage2D(this.target, 0, this.channels, width, height, 0, this.format, type, data); 121 | return this; 122 | }; 123 | 124 | Texture2D.prototype.setSize = function(width, height, type) { 125 | this.width = width; 126 | this.height = height; 127 | if (type == null) { 128 | type = this.gl.UNSIGNED_BYTE; 129 | } 130 | this.gl.texImage2D(this.target, 0, this.channels, width, height, 0, this.format, type, null); 131 | return this; 132 | }; 133 | 134 | Texture2D.prototype.read = function(dst) { 135 | if (dst == null) { 136 | dst = new Uint8Array(this.width * this.height * 4); 137 | } 138 | if (this.fbo) { 139 | this.fbo.bind(); 140 | } else { 141 | this.fbo = new Framebuffer(this.gl).bind().color(this); 142 | } 143 | this.gl.readPixels(0, 0, this.width, this.height, this.gl.RGBA, this.gl.UNSIGNED_BYTE, dst); 144 | this.fbo.unbind(); 145 | return dst; 146 | }; 147 | 148 | Texture2D.prototype.toPNG = function() { 149 | var canvas, ctx, data, i, imgdata, result, url, _i, _ref; 150 | canvas = document.createElement('canvas'); 151 | canvas.height = this.height; 152 | canvas.width = this.width; 153 | ctx = canvas.getContext('2d'); 154 | imgdata = ctx.createImageData(this.width, this.height); 155 | imgdata.data.set(this.read(), 0); 156 | ctx.putImageData(imgdata, 0, 0); 157 | url = canvas.toDataURL('image/png'); 158 | data = atob(url.split(',')[1]); 159 | result = new Uint8Array(data.length); 160 | for (i = _i = 0, _ref = data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { 161 | result[i] = data.charCodeAt(i); 162 | } 163 | return result; 164 | }; 165 | 166 | return Texture2D; 167 | 168 | })(Texture); 169 | 170 | exports.Cubemap = Cubemap = (function(_super) { 171 | 172 | __extends(Cubemap, _super); 173 | 174 | function Cubemap(gl) { 175 | this.gl = gl; 176 | Cubemap.__super__.constructor.call(this); 177 | this.target = this.gl.TEXTURE_CUBE_MAP; 178 | } 179 | 180 | Cubemap.prototype.uploadSide = function(name, image) { 181 | return this.gl.texImage2D(this.gl['TEXTURE_CUBE_MAP_' + name], 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image); 182 | }; 183 | 184 | Cubemap.prototype.upload = function(folder, ext) { 185 | if (ext == null) { 186 | ext = 'jpg'; 187 | } 188 | this.uploadSide('POSITIVE_Y', folder.get("up." + ext)); 189 | this.uploadSide('NEGATIVE_Y', folder.get("down." + ext)); 190 | this.uploadSide('POSITIVE_X', folder.get("right." + ext)); 191 | this.uploadSide('NEGATIVE_X', folder.get("left." + ext)); 192 | this.uploadSide('POSITIVE_Z', folder.get("front." + ext)); 193 | this.uploadSide('NEGATIVE_Z', folder.get("back." + ext)); 194 | return this; 195 | }; 196 | 197 | return Cubemap; 198 | 199 | })(Texture); 200 | -------------------------------------------------------------------------------- /pack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys, struct, cjson, stat 4 | from os.path import join 5 | 6 | here = os.path.dirname(os.path.abspath(__file__)) 7 | 8 | global_shunts = ''' 9 | Function.prototype.property = function(prop, desc) { 10 | Object.defineProperty(this.prototype, prop, desc); 11 | }; 12 | ''' 13 | 14 | def packModules(path): 15 | result = '' 16 | template = ''' 17 | loader.define('%s', function(exports, require, get){ 18 | %s}); 19 | ''' 20 | fs_base = join(here, path) 21 | for path, dirs, names in os.walk(fs_base): 22 | relpath = path[len(fs_base):] 23 | if not relpath.startswith('/'): 24 | relpath = '/' + relpath 25 | 26 | for name in names: 27 | if name.endswith('.js'): 28 | packpath = join(relpath, name) 29 | fspath = join(path, name) 30 | data = open(fspath).read() 31 | module = template % (packpath, data) 32 | result += module 33 | return result 34 | 35 | def packCode(app, target): 36 | if not requireCodePack([app, 'extra', 'lib'], target): 37 | return 38 | 39 | print 'packing code: %s' % target 40 | result = '' 41 | result += global_shunts 42 | for path, dirs, names in os.walk(join(here, 'extra')): 43 | for name in names: 44 | if name.endswith('.js'): 45 | data = open(join(path, name)).read() 46 | result += '\n%s;\n' % data 47 | 48 | result += packModules(app) 49 | result += packModules('lib') 50 | 51 | result += '\n$(function(){loader.main()});' 52 | open(join(here, target, 'code.js'), 'w').write(result) 53 | 54 | def requireCodePack(apps, target): 55 | target_path = join(here, target, 'code.js') 56 | if not os.path.exists(target_path): 57 | return True 58 | 59 | for app in apps: 60 | target_mtime = os.stat(target_path)[stat.ST_MTIME] 61 | for path, dirs, names in os.walk(join(here, app)): 62 | for name in names: 63 | if name.endswith('.js'): 64 | code_mtime = os.stat(join(path, name))[stat.ST_MTIME] 65 | if code_mtime > target_mtime: 66 | return True 67 | return False 68 | 69 | def requireAssetPack(app, target): 70 | for app in [app, 'lib']: 71 | fs_base = join(here, app) 72 | target_path = join(here, target, 'assets.pack') 73 | if not os.path.exists(target_path): 74 | return True 75 | target_mtime = os.stat(target_path)[stat.ST_MTIME] 76 | 77 | for path, dirs, names in os.walk(fs_base): 78 | for name in names: 79 | ext = os.path.splitext(name)[1].lstrip('.') 80 | fspath = join(path, name) 81 | if ext in ('png', 'jpg', 'ogg', 'model', 'mesh', 'bones', 'txt', 'shader', 'json'): 82 | asset_mtime = os.stat(fspath)[stat.ST_MTIME] 83 | if asset_mtime > target_mtime: 84 | return True 85 | return False 86 | 87 | def packAssets(app, target): 88 | if not requireAssetPack(app, target): 89 | return 90 | 91 | print 'packing assets: %s' % target 92 | chunks = '' 93 | meta = {} 94 | for app in [app, 'lib']: 95 | fs_base = join(here, app) 96 | for path, dirs, names in os.walk(fs_base): 97 | relpath = path[len(fs_base):] 98 | if not relpath.startswith('/'): 99 | relpath = '/' + relpath 100 | for name in names: 101 | ext = os.path.splitext(name)[1].lstrip('.') 102 | packpath = join(relpath, name) 103 | fspath = join(path, name) 104 | if ext in ('png', 'jpg', 'ogg', 'mesh', 'bones', 'model'): 105 | data = open(fspath, 'rb').read() 106 | offset = len(chunks) 107 | size = len(data) 108 | chunks += data 109 | meta[packpath] = { 110 | 'offset': offset, 111 | 'size': size, 112 | } 113 | elif ext in ('txt', 'shader'): 114 | meta[packpath] = open(fspath, 'r').read() 115 | elif ext == 'json': 116 | meta[packpath] = cjson.decode(open(fspath, 'r').read()) 117 | 118 | metadata = cjson.encode(meta) 119 | metasize = struct.pack('I', len(metadata)) 120 | open(join(here, target, 'assets.pack'), 'wb').write('PACK'+metasize+metadata+chunks) 121 | 122 | def pack(app, target): 123 | packCode(app, target) 124 | packAssets(app, target) 125 | 126 | if __name__ == '__main__': 127 | pack('src', 'www') 128 | -------------------------------------------------------------------------------- /src/application.coffee: -------------------------------------------------------------------------------- 1 | schedule = require 'schedule' 2 | loading = require 'loading' 3 | camera = require 'camera' 4 | Cube = require 'webgl/cube' 5 | 6 | $('*').each -> 7 | $(@) 8 | .attr('unselectable', 'on') 9 | .css 10 | '-moz-user-select':'none', 11 | '-webkit-user-select':'none', 12 | 'user-select':'none', 13 | '-ms-user-select':'none' 14 | @onselectstart = -> false 15 | 16 | class Model extends require('webgl/drawable') 17 | attribs: ['position'] 18 | 19 | constructor: (@gl, data) -> 20 | super() 21 | @size = data.byteLength/(3*4) 22 | buffer = new Float32Array(@size*3*2) 23 | buffer.set new Float32Array(data), 0 24 | 25 | for i in [@size*3...@size*2*3] by 9 26 | buffer[i+0] = 1; buffer[i+1] = 0; ; buffer[i+2] = 0 27 | buffer[i+3] = 0; buffer[i+4] = 1; ; buffer[i+5] = 0 28 | buffer[i+6] = 0; buffer[i+7] = 0; ; buffer[i+8] = 1 29 | 30 | @upload buffer 31 | 32 | setPointersForShader: (shader) -> 33 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 34 | @setPointer shader, 'position', 3, 0, 3 35 | @setPointer shader, 'barycentric', 3, @size*3, 3 36 | 37 | return @ 38 | 39 | exports.Application = class 40 | constructor: (@canvas) -> 41 | loading.hide() 42 | @camera = new camera.Orbit(near: 0.001, far: 100) 43 | @transparent = get 'transparent.shader' 44 | @overlay = get 'overlay.shader' 45 | @geom = new Model gl, get('bunny.mesh') 46 | $(window).resize @resize 47 | @resize() 48 | schedule.run @update 49 | gl.enable gl.DEPTH_TEST 50 | 51 | container = $('') 52 | .css('margin', 10) 53 | .appendTo('#ui') 54 | 55 | $('Transparent') 56 | .appendTo(container) 57 | input = $('') 58 | .appendTo(container) 59 | .change => 60 | if input[0].checked 61 | @show_transparent = true 62 | else 63 | @show_transparent = false 64 | 65 | resize: => 66 | @width = @canvas.width() 67 | @height = @canvas.height() 68 | 69 | @canvas[0].width = @width 70 | @canvas[0].height = @height 71 | gl.viewport 0, 0, @width, @height 72 | @camera.aspect @width, @height 73 | 74 | update: => 75 | @step() 76 | if @show_transparent 77 | @drawTransparent() 78 | else 79 | @drawOverlay() 80 | 81 | step: -> 82 | @camera.update() 83 | 84 | drawOverlay: -> 85 | gl.disable gl.SAMPLE_ALPHA_TO_COVERAGE 86 | gl.disable gl.BLEND 87 | @overlay.use() 88 | .mat4('proj', @camera.proj) 89 | .mat4('view', @camera.view) 90 | .draw(@geom) 91 | 92 | drawTransparent: -> 93 | gl.enable gl.SAMPLE_ALPHA_TO_COVERAGE 94 | gl.enable gl.BLEND 95 | gl.blendFunc gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA 96 | @transparent.use() 97 | .mat4('proj', @camera.proj) 98 | .mat4('view', @camera.view) 99 | .draw(@geom) 100 | 101 | -------------------------------------------------------------------------------- /src/application.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Cube, Model, camera, loading, schedule, 3 | __hasProp = {}.hasOwnProperty, 4 | __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; }, 5 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; 6 | 7 | schedule = require('schedule'); 8 | 9 | loading = require('loading'); 10 | 11 | camera = require('camera'); 12 | 13 | Cube = require('webgl/cube'); 14 | 15 | $('*').each(function() { 16 | $(this).attr('unselectable', 'on').css({ 17 | '-moz-user-select': 'none', 18 | '-webkit-user-select': 'none', 19 | 'user-select': 'none', 20 | '-ms-user-select': 'none' 21 | }); 22 | return this.onselectstart = function() { 23 | return false; 24 | }; 25 | }); 26 | 27 | Model = (function(_super) { 28 | 29 | __extends(Model, _super); 30 | 31 | Model.prototype.attribs = ['position']; 32 | 33 | function Model(gl, data) { 34 | var buffer, i, _i, _ref, _ref1; 35 | this.gl = gl; 36 | Model.__super__.constructor.call(this); 37 | this.size = data.byteLength / (3 * 4); 38 | buffer = new Float32Array(this.size * 3 * 2); 39 | buffer.set(new Float32Array(data), 0); 40 | for (i = _i = _ref = this.size * 3, _ref1 = this.size * 2 * 3; _i < _ref1; i = _i += 9) { 41 | buffer[i + 0] = 1; 42 | buffer[i + 1] = 0; 43 | buffer[i + 2] = 0; 44 | buffer[i + 3] = 0; 45 | buffer[i + 4] = 1; 46 | buffer[i + 5] = 0; 47 | buffer[i + 6] = 0; 48 | buffer[i + 7] = 0; 49 | buffer[i + 8] = 1; 50 | } 51 | this.upload(buffer); 52 | } 53 | 54 | Model.prototype.setPointersForShader = function(shader) { 55 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 56 | this.setPointer(shader, 'position', 3, 0, 3); 57 | this.setPointer(shader, 'barycentric', 3, this.size * 3, 3); 58 | return this; 59 | }; 60 | 61 | return Model; 62 | 63 | })(require('webgl/drawable')); 64 | 65 | exports.Application = (function() { 66 | 67 | function _Class(canvas) { 68 | var container, input, 69 | _this = this; 70 | this.canvas = canvas; 71 | this.update = __bind(this.update, this); 72 | 73 | this.resize = __bind(this.resize, this); 74 | 75 | loading.hide(); 76 | this.camera = new camera.Orbit({ 77 | near: 0.001, 78 | far: 100 79 | }); 80 | this.transparent = get('transparent.shader'); 81 | this.overlay = get('overlay.shader'); 82 | this.geom = new Model(gl, get('bunny.mesh')); 83 | $(window).resize(this.resize); 84 | this.resize(); 85 | schedule.run(this.update); 86 | gl.enable(gl.DEPTH_TEST); 87 | container = $('').css('margin', 10).appendTo('#ui'); 88 | $('Transparent').appendTo(container); 89 | input = $('').appendTo(container).change(function() { 90 | if (input[0].checked) { 91 | return _this.show_transparent = true; 92 | } else { 93 | return _this.show_transparent = false; 94 | } 95 | }); 96 | } 97 | 98 | _Class.prototype.resize = function() { 99 | this.width = this.canvas.width(); 100 | this.height = this.canvas.height(); 101 | this.canvas[0].width = this.width; 102 | this.canvas[0].height = this.height; 103 | gl.viewport(0, 0, this.width, this.height); 104 | return this.camera.aspect(this.width, this.height); 105 | }; 106 | 107 | _Class.prototype.update = function() { 108 | this.step(); 109 | if (this.show_transparent) { 110 | return this.drawTransparent(); 111 | } else { 112 | return this.drawOverlay(); 113 | } 114 | }; 115 | 116 | _Class.prototype.step = function() { 117 | return this.camera.update(); 118 | }; 119 | 120 | _Class.prototype.drawOverlay = function() { 121 | gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); 122 | gl.disable(gl.BLEND); 123 | return this.overlay.use().mat4('proj', this.camera.proj).mat4('view', this.camera.view).draw(this.geom); 124 | }; 125 | 126 | _Class.prototype.drawTransparent = function() { 127 | gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE); 128 | gl.enable(gl.BLEND); 129 | gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 130 | return this.transparent.use().mat4('proj', this.camera.proj).mat4('view', this.camera.view).draw(this.geom); 131 | }; 132 | 133 | return _Class; 134 | 135 | })(); 136 | -------------------------------------------------------------------------------- /src/bunny.mesh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-easy-wireframe/c1c99a0bde6dbf0d0bd78428e48bf8f692919fb7/src/bunny.mesh -------------------------------------------------------------------------------- /src/display.shader: -------------------------------------------------------------------------------- 1 | varying vec3 vBC; 2 | 3 | vertex: 4 | attribute vec3 position, barycentric; 5 | uniform mat4 proj, view; 6 | void main(){ 7 | vBC = barycentric; 8 | gl_Position = proj * view * vec4(position, 1.0); 9 | } 10 | 11 | fragment: 12 | #extension GL_OES_standard_derivatives : enable 13 | float edgeFactor(){ 14 | vec3 d = fwidth(vBC); 15 | vec3 a3 = smoothstep(vec3(0.0), d*1.5, vBC); 16 | return min(min(a3.x, a3.y), a3.z); 17 | } 18 | 19 | void main(){ 20 | 21 | // coloring by edge 22 | gl_FragColor.rgb = mix(vec3(0.0), vec3(0.5), edgeFactor()); 23 | gl_FragColor.a = 1.0; 24 | 25 | // alpha by edge 26 | if(gl_FrontFacing){ 27 | gl_FragColor = vec4(0.0, 0.0, 0.0, (1.0-edgeFactor())*0.95); 28 | } 29 | else{ 30 | gl_FragColor = vec4(0.0, 0.0, 0.0, (1.0-edgeFactor())*0.7); 31 | } 32 | 33 | 34 | // aliased boolean decision 35 | /* 36 | if(any(lessThan(vBC, vec3(0.02)))){ 37 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 38 | } 39 | else{ 40 | gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0); 41 | } 42 | */ 43 | } 44 | -------------------------------------------------------------------------------- /src/main.coffee: -------------------------------------------------------------------------------- 1 | audio = require 'audio' 2 | loading = require 'loading' 3 | Shader = require 'webgl/shader' 4 | 5 | load_hooks = 6 | '\.jpg$|\.jpeg$|\.gif$|\.png': (buffer, callback) -> 7 | image = new Image() 8 | image.src = getURL(buffer) 9 | image.onload = -> 10 | callback image 11 | '\.mpg$|\.ogg$|\.wav$': (buffer, callback) -> 12 | audio.decode buffer, (result) -> 13 | callback result 14 | '\.shader$': (source, callback) -> 15 | callback new Shader(gl, source) 16 | 17 | exports.main = -> 18 | document.oncontextmenu = -> 19 | return false 20 | window.canvas = $ 'canvas' 21 | try 22 | window.gl = canvas[0].getContext 'experimental-webgl' 23 | if not window.gl 24 | window.gl = canvas[0].getContext 'webgl' 25 | 26 | if window.gl 27 | gl.getExtension 'OES_standard_derivatives' 28 | 29 | Application = require('application').Application 30 | application = null 31 | 32 | loading.show 'Loading ...' 33 | 34 | loader.hooks(load_hooks).mount 35 | url: 'assets.pack', 36 | loaded: -> 37 | application = new Application(window.canvas, window.gl) 38 | progress: loading.progress 39 | else 40 | canvas.remove() 41 | $('#ui').empty() 42 | container = $('') 43 | .css( 44 | position: 'absolute', 45 | width: 300, 46 | left: '50%', 47 | top: 50, 48 | marginLeft: -100 49 | ) 50 | .appendTo('#ui') 51 | 52 | container.append('You have Internet Explorer, please install Google Chrome or Firefox
') 55 | else if $.browser.webkit 56 | 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
') 57 | 58 | container.append('Please consult the support pages on how to get WebGL for your machine.
') 59 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Shader, audio, load_hooks, loading; 3 | 4 | audio = require('audio'); 5 | 6 | loading = require('loading'); 7 | 8 | Shader = require('webgl/shader'); 9 | 10 | load_hooks = { 11 | '\.jpg$|\.jpeg$|\.gif$|\.png': function(buffer, callback) { 12 | var image; 13 | image = new Image(); 14 | image.src = getURL(buffer); 15 | return image.onload = function() { 16 | return callback(image); 17 | }; 18 | }, 19 | '\.mpg$|\.ogg$|\.wav$': function(buffer, callback) { 20 | return audio.decode(buffer, function(result) { 21 | return callback(result); 22 | }); 23 | }, 24 | '\.shader$': function(source, callback) { 25 | return callback(new Shader(gl, source)); 26 | } 27 | }; 28 | 29 | exports.main = function() { 30 | var Application, application, container; 31 | document.oncontextmenu = function() { 32 | return false; 33 | }; 34 | window.canvas = $('canvas'); 35 | try { 36 | window.gl = canvas[0].getContext('experimental-webgl'); 37 | if (!window.gl) { 38 | window.gl = canvas[0].getContext('webgl'); 39 | } 40 | } catch (_error) {} 41 | if (window.gl) { 42 | gl.getExtension('OES_standard_derivatives'); 43 | Application = require('application').Application; 44 | application = null; 45 | loading.show('Loading ...'); 46 | return loader.hooks(load_hooks).mount({ 47 | url: 'assets.pack', 48 | loaded: function() { 49 | return application = new Application(window.canvas, window.gl); 50 | }, 51 | progress: loading.progress 52 | }); 53 | } else { 54 | canvas.remove(); 55 | $('#ui').empty(); 56 | container = $('').css({ 57 | position: 'absolute', 58 | width: 300, 59 | left: '50%', 60 | top: 50, 61 | marginLeft: -100 62 | }).appendTo('#ui'); 63 | container.append('You have Internet Explorer, please install Google Chrome or Firefox
'); 66 | } else if ($.browser.webkit) { 67 | 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
'); 68 | } 69 | return container.append('Please consult the support pages on how to get WebGL for your machine.
'); 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /src/overlay.shader: -------------------------------------------------------------------------------- 1 | varying vec3 vBC; 2 | 3 | vertex: 4 | attribute vec3 position, barycentric; 5 | uniform mat4 proj, view; 6 | void main(){ 7 | vBC = barycentric; 8 | gl_Position = proj * view * vec4(position, 1.0); 9 | } 10 | 11 | fragment: 12 | #extension GL_OES_standard_derivatives : enable 13 | float edgeFactor(){ 14 | vec3 d = fwidth(vBC); 15 | vec3 a3 = smoothstep(vec3(0.0), d*0.95, vBC); 16 | return min(min(a3.x, a3.y), a3.z); 17 | } 18 | 19 | void main(){ 20 | gl_FragColor.rgb = mix(vec3(0.0), vec3(0.5), edgeFactor()); 21 | gl_FragColor.a = 1.0; 22 | } 23 | -------------------------------------------------------------------------------- /src/transparent.shader: -------------------------------------------------------------------------------- 1 | varying vec3 vBC; 2 | 3 | vertex: 4 | attribute vec3 position, barycentric; 5 | uniform mat4 proj, view; 6 | void main(){ 7 | vBC = barycentric; 8 | gl_Position = proj * view * vec4(position, 1.0); 9 | } 10 | 11 | fragment: 12 | #extension GL_OES_standard_derivatives : enable 13 | float edgeFactor(){ 14 | vec3 d = fwidth(vBC); 15 | vec3 a3 = smoothstep(vec3(0.0), d*1.5, vBC); 16 | return min(min(a3.x, a3.y), a3.z); 17 | } 18 | 19 | void main(){ 20 | if(gl_FrontFacing){ 21 | gl_FragColor = vec4(0.0, 0.0, 0.0, (1.0-edgeFactor())*0.95); 22 | } 23 | else{ 24 | gl_FragColor = vec4(0.0, 0.0, 0.0, (1.0-edgeFactor())*0.7); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /www/assets.pack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-easy-wireframe/c1c99a0bde6dbf0d0bd78428e48bf8f692919fb7/www/assets.pack -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 36 | 37 | 38 | 39 | 40 | 41 | --------------------------------------------------------------------------------