UNPKG

doubleray

Version:
938 lines (858 loc) 30 kB
var ASSERTIONS = 1; // Copyright 2017 The Emscripten Authors. All rights reserved. // Emscripten is available under two separate licenses, the MIT license and the // University of Illinois/NCSA Open Source License. Both these licenses can be // found in the LICENSE file. // Prefix of data URIs emitted by SINGLE_FILE and related options. var dataURIPrefix = 'data:application/octet-stream;base64,'; // Indicates whether filename is a base64 data URI. function isDataURI(filename) { return String.prototype.startsWith ? filename.startsWith(dataURIPrefix) : filename.indexOf(dataURIPrefix) === 0; } // Copied from https://github.com/strophe/strophejs/blob/e06d027/src/polyfills.js#L149 // This code was written by Tyler Akins and has been placed in the // public domain. It would be nice if you left this header intact. // Base64 code from Tyler Akins -- http://rumkin.com /** * Decodes a base64 string. * @param {string} input The string to decode. */ var decodeBase64 = typeof atob === 'function' ? atob : function (input) { var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var output = ''; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; // remove all characters that are not A-Z, a-z, 0-9, +, /, or = input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ''); do { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 !== 64) { output = output + String.fromCharCode(chr2); } if (enc4 !== 64) { output = output + String.fromCharCode(chr3); } } while (i < input.length); return output; }; // Converts a string of base64 into a byte array. // Throws error on invalid input. function intArrayFromBase64(s) { if (typeof ENVIRONMENT_IS_NODE === 'boolean' && ENVIRONMENT_IS_NODE) { var buf; try { // TODO: Update Node.js externs, Closure does not recognize the following Buffer.from() /**@suppress{checkTypes}*/ buf = Buffer.from(s, 'base64'); } catch (_) { buf = new Buffer(s, 'base64'); } return new Uint8Array(buf['buffer'], buf['byteOffset'], buf['byteLength']); } try { var decoded = decodeBase64(s); var bytes = new Uint8Array(decoded.length); for (var i = 0 ; i < decoded.length ; ++i) { bytes[i] = decoded.charCodeAt(i); } return bytes; } catch (_) { throw new Error('Converting base64 string to bytes failed.'); } } // If filename is a base64 data URI, parses and returns data (Buffer on node, // Uint8Array otherwise). If filename is not a base64 data URI, returns undefined. function tryParseAsDataURI(filename) { if (!isDataURI(filename)) { return; } return intArrayFromBase64(filename.slice(dataURIPrefix.length)); } // Copyright 2017 The Emscripten Authors. All rights reserved. // Emscripten is available under two separate licenses, the MIT license and the // University of Illinois/NCSA Open Source License. Both these licenses can be // found in the LICENSE file. /** @type {function(string, boolean=, number=)} */ function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy)+1; var u8array = new Array(len); var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); if (dontAddNull) u8array.length = numBytesWritten; return u8array; } function intArrayToString(array) { var ret = []; for (var i = 0; i < array.length; i++) { var chr = array[i]; if (chr > 0xFF) { if (ASSERTIONS) { assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.'); } chr &= 0xFF; } ret.push(String.fromCharCode(chr)); } return ret.join(''); } var filename = 'ray.js'; if ((',' + window.location.search.substr(1) + ',').indexOf(',noProxy,') < 0) { console.log('running code in a web worker'); // Copyright 2014 The Emscripten Authors. All rights reserved. // Emscripten is available under two separate licenses, the MIT license and the // University of Illinois/NCSA Open Source License. Both these licenses can be // found in the LICENSE file. // WebGLWorker client code function assert(x) { if (!x) throw 'failed assert'; } function WebGLClient() { var objects = {}; var ctx = null; var buffer = null; var i = 0; var skippable = false; var currFrameBuffer = null; function func0(name) { ctx[name](); } function func1(name) { ctx[name](buffer[i]); i++; } function func2(name) { ctx[name](buffer[i], buffer[i+1]); i += 2; } function func3(name) { ctx[name](buffer[i], buffer[i+1], buffer[i+2]); i += 3; } function func4(name) { ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3]); i += 4; } function func5(name) { ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4]); i += 5; } function func6(name) { ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5]); i += 6; } function func7(name) { ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5], buffer[i+6]); i += 7; } function func9(name) { ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7], buffer[i+8]); i += 9; } // lookuppers, convert integer ids to cached objects for some args function func1L0(name) { ctx[name](objects[buffer[i]]); i++; } function func2L0(name) { ctx[name](objects[buffer[i]], buffer[i+1]); i += 2; } function func2L0L1(name) { ctx[name](objects[buffer[i]], objects[buffer[i+1]]); i += 2; } function func2L1_(name) { ctx[name](buffer[i], buffer[i+1] ? objects[buffer[i+1]] : null); i += 2; } function func3L0(name) { ctx[name](objects[buffer[i]], buffer[i+1], buffer[i+2]); i += 3; } function func4L3_(name) { ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3] ? objects[buffer[i+3]] : null); i += 4; } function func5L3_(name) { ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3] ? objects[buffer[i+3]] : null, buffer[i+4]); i += 5; } // constructors, last argument is the id to save as function funcC0(name) { var object = ctx[name](); var id = buffer[i++]; objects[id] = object; } function funcC1(name) { var object = ctx[name](buffer[i++]); var id = buffer[i++]; objects[id] = object; } function funcC2(name) { var object = ctx[name](buffer[i++], buffer[i++]); var id = buffer[i++]; objects[id] = object; } function funcC2L0(name) { var object = ctx[name](objects[buffer[i++]], buffer[i++]); var id = buffer[i++]; objects[id] = object; } // destructors, stop holding on to the object in the cache function funcD0(name) { var id = buffer[i++]; var object = objects[id]; objects[id] = null; ctx[name](object); } // special cases/optimizations function bindFramebuffer() { currFrameBuffer = buffer[i+1] ? objects[buffer[i+1]] : null; ctx.bindFramebuffer(buffer[i], currFrameBuffer); i += 2; } function drawArrays(name) { if (!skippable || currFrameBuffer !== null) { ctx.drawArrays(buffer[i], buffer[i+1], buffer[i+2]); } i += 3; } function drawElements(name) { if (!skippable || currFrameBuffer !== null) { ctx.drawElements(buffer[i], buffer[i+1], buffer[i+2], buffer[i+3]); } i += 4; } function enable() { ctx.enable(buffer[i++]); } function disable() { ctx.disable(buffer[i++]); } function enableVertexAttribArray() { ctx.enableVertexAttribArray(buffer[i++]); } function disableVertexAttribArray() { ctx.disableVertexAttribArray(buffer[i++]); } function activeTexture() { ctx.activeTexture(buffer[i++]); } function uniform1i() { ctx.uniform1i(objects[buffer[i]], buffer[i+1]); i += 2; } function uniform1f() { ctx.uniform1f(objects[buffer[i]], buffer[i+1]); i += 2; } function uniform1fv() { ctx.uniform1fv(objects[buffer[i]], buffer[i+1]); i += 2; } function uniform2fv() { ctx.uniform2fv(objects[buffer[i]], buffer[i+1]); i += 2; } function uniform1iv() { ctx.uniform1iv(objects[buffer[i]], buffer[i+1]); i += 2; } function uniform3f() { ctx.uniform3f(objects[buffer[i]], buffer[i+1], buffer[i+2], buffer[i+3]); i += 4; } function uniform3fv() { ctx.uniform3fv(objects[buffer[i]], buffer[i+1]); i += 2; } function uniform4fv() { ctx.uniform4fv(objects[buffer[i]], buffer[i+1]); i += 2; } function vertexAttribPointer() { ctx.vertexAttribPointer(buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5]); i += 6; } var calls = { 0: { name: 'NULL', func: func0 }, 1: { name: 'getExtension', func: func1 }, 2: { name: 'enable', func: enable }, 3: { name: 'disable', func: disable }, 4: { name: 'clear', func: func1 }, 5: { name: 'clearColor', func: func4 }, 6: { name: 'createShader', func: funcC1 }, 7: { name: 'deleteShader', func: funcD0 }, 8: { name: 'shaderSource', func: func2L0 }, 9: { name: 'compileShader', func: func1L0 }, 10: { name: 'createProgram', func: funcC0 }, 11: { name: 'deleteProgram', func: funcD0 }, 12: { name: 'attachShader', func: func2L0L1 }, 13: { name: 'bindAttribLocation', func: func3L0 }, 14: { name: 'linkProgram', func: func1L0 }, 15: { name: 'getProgramParameter', func: function() { assert(ctx.getProgramParameter(objects[buffer[i++]], buffer[i++]), 'we cannot handle errors, we are async proxied WebGL'); } }, 16: { name: 'getUniformLocation', func: funcC2L0 }, 17: { name: 'useProgram', func: func1L0 }, 18: { name: 'uniform1i', func: uniform1i }, 19: { name: 'uniform1f', func: uniform1f }, 20: { name: 'uniform3fv', func: uniform3fv }, 21: { name: 'uniform4fv', func: uniform4fv }, 22: { name: 'uniformMatrix4fv', func: func3L0 }, 23: { name: 'vertexAttrib4fv', func: func2 }, 24: { name: 'createBuffer', func: funcC0 }, 25: { name: 'deleteBuffer', func: funcD0 }, 26: { name: 'bindBuffer', func: func2L1_ }, 27: { name: 'bufferData', func: func3 }, 28: { name: 'bufferSubData', func: func3 }, 29: { name: 'viewport', func: func4 }, 30: { name: 'vertexAttribPointer', func: vertexAttribPointer }, 31: { name: 'enableVertexAttribArray', func: enableVertexAttribArray }, 32: { name: 'disableVertexAttribArray', func: disableVertexAttribArray }, 33: { name: 'drawArrays', func: drawArrays }, 34: { name: 'drawElements', func: drawElements }, 35: { name: 'getError', func: function() { assert(ctx.getError() === ctx.NO_ERROR, 'we cannot handle errors, we are async proxied WebGL') } }, 36: { name: 'createTexture', func: funcC0 }, 37: { name: 'deleteTexture', func: funcD0 }, 38: { name: 'bindTexture', func: func2L1_ }, 39: { name: 'texParameteri', func: func3 }, 40: { name: 'texImage2D', func: func9 }, 41: { name: 'compressedTexImage2D', func: func7 }, 42: { name: 'activeTexture', func: activeTexture }, 43: { name: 'getShaderParameter', func: function() { assert(ctx.getShaderParameter(objects[buffer[i++]], buffer[i++]), 'we cannot handle errors, we are async proxied WebGL'); } }, 44: { name: 'clearDepth', func: func1 }, 45: { name: 'depthFunc', func: func1 }, 46: { name: 'frontFace', func: func1 }, 47: { name: 'cullFace', func: func1 }, 48: { name: 'pixelStorei', func: func2 }, 49: { name: 'depthMask', func: func1 }, 50: { name: 'depthRange', func: func2 }, 51: { name: 'blendFunc', func: func2 }, 52: { name: 'scissor', func: func4 }, 53: { name: 'colorMask', func: func4 }, 54: { name: 'lineWidth', func: func1 }, 55: { name: 'createFramebuffer', func: funcC0 }, 56: { name: 'deleteFramebuffer', func: funcD0 }, 57: { name: 'bindFramebuffer', func: bindFramebuffer }, 58: { name: 'framebufferTexture2D', func: func5L3_ }, 59: { name: 'createRenderbuffer', func: funcC0 }, 60: { name: 'deleteRenderbuffer', func: funcD0 }, 61: { name: 'bindRenderbuffer', func: func2L1_ }, 62: { name: 'renderbufferStorage', func: func4 }, 63: { name: 'framebufferRenderbuffer', func: func4L3_ }, 64: { name: 'debugPrint', func: func1 }, 65: { name: 'hint', func: func2 }, 66: { name: 'blendEquation', func: func1 }, 67: { name: 'generateMipmap', func: func1 }, 68: { name: 'uniformMatrix3fv', func: func3L0 }, 69: { name: 'stencilMask', func: func1 }, 70: { name: 'clearStencil', func: func1 }, 71: { name: 'texSubImage2D', func: func9 }, 72: { name: 'uniform3f', func: uniform3f }, 73: { name: 'blendFuncSeparate', func: func4 }, 74: { name: 'uniform2fv', func: uniform2fv }, 75: { name: 'texParameterf', func: func3 }, 76: { name: 'isContextLost', func: function() { assert(!ctx.isContextLost(), 'context lost which we cannot handle, we are async proxied WebGL') } }, 77: { name: 'blendEquationSeparate', func: func2 }, 78: { name: 'stencilFuncSeparate', func: func4 }, 79: { name: 'stencilOpSeparate', func: func4 }, 80: { name: 'drawBuffersWEBGL', func: func1 }, 81: { name: 'uniform1iv', func: uniform1iv }, 82: { name: 'uniform1fv', func: uniform1fv }, }; function renderCommands(buf) { ctx = Module.ctx; i = 0; buffer = buf; var len = buffer.length; //dump('issuing commands, buffer len: ' + len + '\n'); while (i < len) { var info = calls[buffer[i++]]; var name = info.name; info.func(name); //var err; //while ((err = ctx.getError()) !== ctx.NO_ERROR) { // dump('warning: GL error ' + err + ', after ' + [command, numArgs] + '\n'); //} assert(i <= len); } } var commandBuffers = []; function renderAllCommands() { // we can skip parts of the frames before the last, as we just need their side effects skippable = true; for (var i = 0; i < commandBuffers.length-1; i++) { renderCommands(commandBuffers[i]); } skippable = false; renderCommands(commandBuffers[commandBuffers.length-1]); commandBuffers.length = 0; } this.onmessage = function(msg) { //dump('client GL got ' + JSON.stringify(msg) + '\n'); switch(msg.op) { case 'render': { if (commandBuffers.length === 0) { // requestion a new frame, we will clear the buffers after rendering them window.requestAnimationFrame(renderAllCommands); } commandBuffers.push(msg.commandBuffer); break; } default: throw 'weird gl onmessage ' + JSON.stringify(msg); } }; } WebGLClient.prefetch = function() { // Create a fake temporary GL context var canvas = document.createElement('canvas'); var ctx = canvas.getContext('webgl-experimental') || canvas.getContext('webgl'); if (!ctx) { // If we have no webGL support, we still notify that prefetching is done, as the app blocks on that worker.postMessage({ target: 'gl', op: 'setPrefetched', preMain: true }); return; } // Fetch the parameters and proxy them var parameters = {}; ['MAX_VERTEX_ATTRIBS', 'MAX_TEXTURE_IMAGE_UNITS', 'MAX_TEXTURE_SIZE', 'MAX_CUBE_MAP_TEXTURE_SIZE', 'MAX_VERTEX_UNIFORM_VECTORS', 'MAX_FRAGMENT_UNIFORM_VECTORS', 'MAX_VARYING_VECTORS', 'MAX_COMBINED_TEXTURE_IMAGE_UNITS', 'MAX_VERTEX_TEXTURE_IMAGE_UNITS', 'VENDOR', 'RENDERER', 'VERSION', 'SHADING_LANGUAGE_VERSION', 'COMPRESSED_TEXTURE_FORMATS', 'RED_BITS', 'GREEN_BITS', 'BLUE_BITS', 'ALPHA_BITS', 'DEPTH_BITS', 'STENCIL_BITS', 'MAX_RENDERBUFFER_SIZE'].forEach(function(name) { var id = ctx[name]; parameters[id] = ctx.getParameter(id); }); // Try to enable some extensions, so we can access their parameters [{ extName: 'EXT_texture_filter_anisotropic', paramName: 'MAX_TEXTURE_MAX_ANISOTROPY_EXT' }, { extName: 'WEBGL_draw_buffers', paramName: 'MAX_COLOR_ATTACHMENTS_WEBGL' }].forEach(function(pair) { var ext = ctx.getExtension(pair.extName); if (ext) { var id = ext[pair.paramName]; parameters[id] = ctx.getParameter(id); } }); // Fetch shader precisions var precisions = {}; ['FRAGMENT_SHADER', 'VERTEX_SHADER'].forEach(function(shaderType) { shaderType = ctx[shaderType]; precisions[shaderType] = {}; ['LOW_FLOAT', 'MEDIUM_FLOAT', 'HIGH_FLOAT', 'LOW_INT', 'MEDIUM_INT', 'HIGH_INT'].forEach(function(precisionType) { precisionType = ctx[precisionType]; var info = ctx.getShaderPrecisionFormat(shaderType, precisionType); precisions[shaderType][precisionType] = info ? { rangeMin: info.rangeMin, rangeMax: info.rangeMax, precision: info.precision } : info; }); }); worker.postMessage({ target: 'gl', op: 'setPrefetched', parameters: parameters, extensions: ctx.getSupportedExtensions(), precisions: precisions, preMain: true }); }; // Copyright 2013 The Emscripten Authors. All rights reserved. // Emscripten is available under two separate licenses, the MIT license and the // University of Illinois/NCSA Open Source License. Both these licenses can be // found in the LICENSE file. // proxy to/from worker if (typeof Module === 'undefined') { console.warn('no Module object defined - cannot proxy canvas rendering and input events, etc.'); Module = { print: function(x) { console.log(x); }, printErr: function(x) { console.log(x); }, canvas: { addEventListener: function() {}, getBoundingClientRect: function() { return { bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 } }, }, }; } // utils function FPSTracker(text) { var last = 0; var mean = 0; var counter = 0; this.tick = function() { var now = Date.now(); if (last > 0) { var diff = now - last; mean = 0.99*mean + 0.01*diff; if (counter++ === 60) { counter = 0; dump(text + ' fps: ' + (1000/mean).toFixed(2) + '\n'); } } last = now; }; } /* function GenericTracker(text) { var mean = 0; var counter = 0; this.tick = function(value) { mean = 0.99*mean + 0.01*value; if (counter++ === 60) { counter = 0; dump(text + ': ' + (mean).toFixed(2) + '\n'); } }; } */ // render var renderFrameData = null; function renderFrame() { var dst = Module.canvasData.data; if (dst.set) { dst.set(renderFrameData); } else { for (var i = 0; i < renderFrameData.length; i++) { dst[i] = renderFrameData[i]; } } Module.ctx.putImageData(Module.canvasData, 0, 0); renderFrameData = null; } window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || renderFrame; /* (function() { var trueRAF = window.requestAnimationFrame; var tracker = new FPSTracker('client'); window.requestAnimationFrame = function(func) { trueRAF(function() { tracker.tick(); func(); }); } })(); */ // end render // IDBStore var IDBStore = // Copyright 2015 The Emscripten Authors. All rights reserved. // Emscripten is available under two separate licenses, the MIT license and the // University of Illinois/NCSA Open Source License. Both these licenses can be // found in the LICENSE file. { indexedDB: function() { if (typeof indexedDB !== 'undefined') return indexedDB; var ret = null; if (typeof window === 'object') ret = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; assert(ret, 'IDBStore used, but indexedDB not supported'); return ret; }, DB_VERSION: 22, DB_STORE_NAME: 'FILE_DATA', dbs: {}, blobs: [0], getDB: function(name, callback) { // check the cache first var db = IDBStore.dbs[name]; if (db) { return callback(null, db); } var req; try { req = IDBStore.indexedDB().open(name, IDBStore.DB_VERSION); } catch (e) { return callback(e); } req.onupgradeneeded = function(e) { var db = e.target.result; var transaction = e.target.transaction; var fileStore; if (db.objectStoreNames.contains(IDBStore.DB_STORE_NAME)) { fileStore = transaction.objectStore(IDBStore.DB_STORE_NAME); } else { fileStore = db.createObjectStore(IDBStore.DB_STORE_NAME); } }; req.onsuccess = function() { db = req.result; // add to the cache IDBStore.dbs[name] = db; callback(null, db); }; req.onerror = /** @this{IDBOpenDBRequest} */ function(e) { callback(this.error); e.preventDefault(); }; }, getStore: function(dbName, type, callback) { IDBStore.getDB(dbName, function(error, db) { if (error) return callback(error); var transaction = db.transaction([IDBStore.DB_STORE_NAME], type); transaction.onerror = function(e) { callback(this.error || 'unknown error'); e.preventDefault(); }; var store = transaction.objectStore(IDBStore.DB_STORE_NAME); callback(null, store); }); }, // External API getFile: function(dbName, id, callback) { IDBStore.getStore(dbName, 'readonly', function(err, store) { if (err) return callback(err); var req = store.get(id); req.onsuccess = function(event) { var result = event.target.result; if (!result) { return callback('file ' + id + ' not found'); } else { return callback(null, result); } }; req.onerror = function(error) { callback(error); }; }); }, setFile: function(dbName, id, data, callback) { IDBStore.getStore(dbName, 'readwrite', function(err, store) { if (err) return callback(err); var req = store.put(data, id); req.onsuccess = function(event) { callback(); }; req.onerror = function(error) { callback(error); }; }); }, deleteFile: function(dbName, id, callback) { IDBStore.getStore(dbName, 'readwrite', function(err, store) { if (err) return callback(err); var req = store.delete(id); req.onsuccess = function(event) { callback(); }; req.onerror = function(error) { callback(error); }; }); }, existsFile: function(dbName, id, callback) { IDBStore.getStore(dbName, 'readonly', function(err, store) { if (err) return callback(err); var req = store.count(id); req.onsuccess = function(event) { callback(null, event.target.result > 0); }; req.onerror = function(error) { callback(error); }; }); }, } ; // Frame throttling var frameId = 0; // Temporarily handling this at run-time pending Python preprocessor support var SUPPORT_BASE64_EMBEDDING; // Worker var filename; if (!filename) { filename = 'ray.js'; } var workerURL = filename; if (SUPPORT_BASE64_EMBEDDING) { var fileBytes = tryParseAsDataURI(filename); if (fileBytes) { workerURL = URL.createObjectURL(new Blob([fileBytes], {type: 'application/javascript'})); } } var worker = new Worker(workerURL); WebGLClient.prefetch(); setTimeout(function() { worker.postMessage({ target: 'worker-init', width: Module.canvas.width, height: Module.canvas.height, boundingClientRect: cloneObject(Module.canvas.getBoundingClientRect()), URL: document.URL, currentScriptUrl: filename, preMain: true }); }, 0); // delay til next frame, to make sure html is ready var workerResponded = false; worker.onmessage = function worker_onmessage(event) { //dump('\nclient got ' + JSON.stringify(event.data).substr(0, 150) + '\n'); if (!workerResponded) { workerResponded = true; if (Module.setStatus) Module.setStatus(''); if (SUPPORT_BASE64_EMBEDDING && workerURL !== filename) URL.revokeObjectURL(workerURL); } var data = event.data; switch (data.target) { case 'stdout': { Module['print'](data.content); break; } case 'stderr': { Module['printErr'](data.content); break; } case 'window': { window[data.method](); break; } case 'canvas': { switch (data.op) { case 'getContext': { Module.ctx = Module.canvas.getContext(data.type, data.attributes); if (data.type !== '2d') { // possible GL_DEBUG entry point: Module.ctx = wrapDebugGL(Module.ctx); Module.glClient = new WebGLClient(); } break; } case 'resize': { Module.canvas.width = data.width; Module.canvas.height = data.height; if (Module.ctx && Module.ctx.getImageData) Module.canvasData = Module.ctx.getImageData(0, 0, data.width, data.height); worker.postMessage({ target: 'canvas', boundingClientRect: cloneObject(Module.canvas.getBoundingClientRect()) }); break; } case 'render': { if (renderFrameData) { // previous image was not rendered yet, just update image renderFrameData = data.image.data; } else { // previous image was rendered so update image and request another frame renderFrameData = data.image.data; window.requestAnimationFrame(renderFrame); } break; } case 'setObjectProperty': { Module.canvas[data.object][data.property] = data.value; break; } default: throw 'eh?'; } break; } case 'gl': { Module.glClient.onmessage(data); break; } case 'tick': { frameId = data.id; worker.postMessage({ target: 'tock', id: frameId }); break; } case 'Image': { assert(data.method === 'src'); var img = new Image(); img.onload = function() { assert(img.complete); var canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); var imageData = ctx.getImageData(0, 0, img.width, img.height); worker.postMessage({ target: 'Image', method: 'onload', id: data.id, width: img.width, height: img.height, data: imageData.data, preMain: true }); }; img.onerror = function() { worker.postMessage({ target: 'Image', method: 'onerror', id: data.id, preMain: true }); }; img.src = data.src; break; } case 'IDBStore': { switch (data.method) { case 'loadBlob': { IDBStore.getFile(data.db, data.id, function(error, blob) { worker.postMessage({ target: 'IDBStore', method: 'response', blob: error ? null : blob }); }); break; } case 'storeBlob': { IDBStore.setFile(data.db, data.id, data.blob, function(error) { worker.postMessage({ target: 'IDBStore', method: 'response', error: !!error }); }); break; } } break; } case 'custom': { if (Module['onCustomMessage']) { Module['onCustomMessage'](event); } else { throw 'Custom message received but client Module.onCustomMessage not implemented.'; } break; } case 'setimmediate': { worker.postMessage({target: 'setimmediate'}); break; } default: throw 'what? ' + data.target; } }; function postCustomMessage(data, options) { options = options || {}; worker.postMessage({ target: 'custom', userData: data, preMain: options.preMain }); } function cloneObject(event) { var ret = {}; for (var x in event) { if (x == x.toUpperCase()) continue; var prop = event[x]; if (typeof prop === 'number' || typeof prop === 'string') ret[x] = prop; } return ret; }; // Only prevent default on backspace/tab because we don't want unexpected navigation. // Do not prevent default on the rest as we need the keypress event. function shouldPreventDefault(event) { if (event.type === 'keydown' && event.keyCode !== 8 /* backspace */ && event.keyCode !== 9 /* tab */) { return false; // keypress, back navigation } else { return true; // NO keypress, NO back navigation } }; ['keydown', 'keyup', 'keypress', 'blur', 'visibilitychange'].forEach(function(event) { document.addEventListener(event, function(event) { worker.postMessage({ target: 'document', event: cloneObject(event) }); if (shouldPreventDefault(event)) { event.preventDefault(); } }); }); ['unload'].forEach(function(event) { window.addEventListener(event, function(event) { worker.postMessage({ target: 'window', event: cloneObject(event) }); }); }); ['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) { Module.canvas.addEventListener(event, function(event) { worker.postMessage({ target: 'canvas', event: cloneObject(event) }); event.preventDefault(); }, true); }); } else { // note: no support for code mods (PRECISE_F32==2) console.log('running code on the main thread'); var fileBytes = tryParseAsDataURI(filename); var script = document.createElement('script'); if (fileBytes) { script.innerHTML = intArrayToString(fileBytes); } else { script.src = filename; } document.body.appendChild(script); }