diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/library.js | 2 | ||||
-rw-r--r-- | src/library_browser.js | 46 | ||||
-rw-r--r-- | src/library_gl.js | 175 | ||||
-rw-r--r-- | src/library_sdl.js | 50 |
4 files changed, 229 insertions, 44 deletions
diff --git a/src/library.js b/src/library.js index cb94fccb..b9c13055 100644 --- a/src/library.js +++ b/src/library.js @@ -1495,7 +1495,7 @@ LibraryManager.library = { lseek: function(fildes, offset, whence) { // off_t lseek(int fildes, off_t offset, int whence); // http://pubs.opengroup.org/onlinepubs/000095399/functions/lseek.html - if (FS.streams[fildes] && !FS.streams[fildes].isDevice) { + if (FS.streams[fildes] && !FS.streams[fildes].object.isDevice) { var stream = FS.streams[fildes]; var position = offset; if (whence === 1) { // SEEK_CUR. diff --git a/src/library_browser.js b/src/library_browser.js index 9283913f..6015168f 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -86,6 +86,7 @@ mergeInto(LibraryManager.library, { requestFullScreen: function() { var canvas = Module.canvas; function fullScreenChange() { + if (Module['onFullScreen']) Module['onFullScreen'](); if (document['webkitFullScreenElement'] === canvas || document['mozFullScreenElement'] === canvas || document['fullScreenElement'] === canvas) { @@ -142,24 +143,55 @@ mergeInto(LibraryManager.library, { 0; }, - asyncLoad: function(url, callback) { + xhrLoad: function(url, onload, onerror) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; xhr.onload = function() { - var arrayBuffer = xhr.response; + if (xhr.status == 200) { + onload(xhr.response); + } else { + onerror(); + } + }; + xhr.onerror = onerror; + xhr.send(null); + }, + + asyncLoad: function(url, callback) { + Browser.xhrLoad(url, function(arrayBuffer) { assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); callback(new Uint8Array(arrayBuffer)); removeRunDependency(); - }; - xhr.onerror = function(event) { - assert(arrayBuffer, 'Loading data file "' + url + '" failed.'); - }; - xhr.send(null); + }, function(event) { + throw 'Loading data file "' + url + '" failed.'; + }); addRunDependency(); } }, + emscripten_async_wget: function(url, file, onload, onerror) { + url = Pointer_stringify(url); + + Browser.xhrLoad(url, function(response) { + var absolute = Pointer_stringify(file); + var index = absolute.lastIndexOf('/'); + FS.createDataFile( + absolute.substr(0, index), + absolute.substr(index +1), + new Uint8Array(response), + true, true); + + if (onload) { + FUNCTION_TABLE[onload](file); + } + }, function(event) { + if (onerror) { + FUNCTION_TABLE[onerror](file); + } + }); + }, + emscripten_async_run_script__deps: ['emscripten_run_script'], emscripten_async_run_script: function(script, millis) { Module['noExitRuntime'] = true; diff --git a/src/library_gl.js b/src/library_gl.js index 68f8248b..53e587de 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -23,7 +23,7 @@ var LibraryGL = { unpackAlignment: 4, // default alignment is 4 bytes init: function() { - Browser.moduleContextCreatedCallbacks.push(GL.initCompression); + Browser.moduleContextCreatedCallbacks.push(GL.initExtensions); }, // Linear lookup in one of the tables (buffers, programs, etc.). TODO: consider using a weakmap to make this faster, if it matters @@ -107,14 +107,17 @@ var LibraryGL = { ((height - 1) * alignedRowSize + plainRowSize); }, - initCompression: function() { - if (GL.initCompression.done) return; - GL.initCompression.done = true; + initExtensions: function() { + if (GL.initExtensions.done) return; + GL.initExtensions.done = true; - var ext = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') || - Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || - Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); - if (!ext) Module.printErr('Failed to get texture compression WebGL extension, if compressed textures are used they will fail'); + GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') || + Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || + Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); + + GL.anisotropicExt = Module.ctx.getExtension('EXT_texture_filter_anisotropic') || + Module.ctx.getExtension('MOZ_EXT_texture_filter_anisotropic') || + Module.ctx.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); } }, @@ -289,21 +292,23 @@ var LibraryGL = { }, glCompressedTexImage2D: function(target, level, internalformat, width, height, border, imageSize, data) { + assert(GL.compressionExt); if (data) { data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}}; } else { data = null; } - Module.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, data); + Module.ctx['compressedTexImage2D'](target, level, internalformat, width, height, border, data); }, glCompressedTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, imageSize, data) { + assert(GL.compressionExt); if (data) { data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}}; } else { data = null; } - Module.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, data); + Module.ctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, data); }, glTexImage2D: function(target, level, internalformat, width, height, border, format, type, pixels) { @@ -909,7 +914,10 @@ var LibraryGL = { // Fog support. Partial, we assume shaders are used that implement fog. We just pass them uniforms fogStart: 0, fogEnd: 1, + fogDensity: 1.0, fogColor: null, + fogMode: 0x0800, // GL_EXP + fogEnabled: false, init: function() { GLEmulation.fogColor = new Float32Array(4); @@ -917,29 +925,48 @@ var LibraryGL = { // Add some emulation workarounds Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work'); - // XXX some of these ignored capabilities may lead to incorrect rendering, if we do not emulate them in shaders - var ignoredCapabilities = { - 0x0B20: 1, // GL_LINE_SMOOTH - 0x0B60: 1, // GL_FOG - 0x0BA1: 1, // GL_NORMALIZE - 0x0C60: 1, // GL_TEXTURE_GEN_S - 0x0C61: 1, // GL_TEXTURE_GEN_T - 0x0DE1: 1, // GL_TEXTURE_2D - 0x8513: 1, // GL_TEXTURE_CUBE_MAP - 0x2A02: 1 // GL_POLYGON_OFFSET_LINE + // XXX some of the capabilities we don't support may lead to incorrect rendering, if we do not emulate them in shaders + var validCapabilities = { + 0x0B44: 1, // GL_CULL_FACE + 0x0BE2: 1, // GL_BLEND + 0x0BD0: 1, // GL_DITHER, + 0x0B90: 1, // GL_STENCIL_TEST + 0x0B71: 1, // GL_DEPTH_TEST + 0x0C11: 1, // GL_SCISSOR_TEST + 0x8037: 1, // GL_POLYGON_OFFSET_FILL + 0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE + 0x80A0: 1 // GL_SAMPLE_COVERAGE }; _glEnable = function(cap) { // Clean up the renderer on any change to the rendering state. The optimization of // skipping renderer setup is aimed at the case of multiple glDraw* right after each other if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup(); - if (cap in ignoredCapabilities) return; + if (cap == 0x0B60 /* GL_FOG */) { + GLEmulation.fogEnabled = true; + return; + } else if (!(cap in validCapabilities)) { + return; + } Module.ctx.enable(cap); }; _glDisable = function(cap) { if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup(); - if (cap in ignoredCapabilities) return; + if (cap == 0x0B60 /* GL_FOG */) { + GLEmulation.fogEnabled = false; + return; + } else if (!(cap in validCapabilities)) { + return; + } Module.ctx.disable(cap); }; + _glIsEnabled = function(cap) { + if (cap == 0x0B60 /* GL_FOG */) { + return GLEmulation.fogEnabled ? 1 : 0; + } else if (!(cap in validCapabilities)) { + return 0; + } + return Module.ctx.isEnabled(cap); + }; var glGetIntegerv = _glGetIntegerv; _glGetIntegerv = function(pname, params) { @@ -969,7 +996,11 @@ var LibraryGL = { _glGetString = function(name_) { switch(name_) { case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support - return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') + ' GL_EXT_texture_env_combine GL_ARB_texture_env_crossbar GL_ATI_texture_env_combine3 GL_NV_texture_env_combine4 GL_EXT_texture_env_dot3 GL_ARB_multitexture GL_ARB_vertex_buffer_object GL_EXT_framebuffer_object GL_ARB_vertex_program GL_ARB_fragment_program GL_ARB_shading_language_100 GL_ARB_shader_objects GL_ARB_vertex_shader GL_ARB_fragment_shader GL_ARB_texture_cube_map GL_EXT_draw_range_elements GL_ARB_texture_compression GL_EXT_texture_compression_s3tc'), 'i8', ALLOC_NORMAL); + return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') + + ' GL_EXT_texture_env_combine GL_ARB_texture_env_crossbar GL_ATI_texture_env_combine3 GL_NV_texture_env_combine4 GL_EXT_texture_env_dot3 GL_ARB_multitexture GL_ARB_vertex_buffer_object GL_EXT_framebuffer_object GL_ARB_vertex_program GL_ARB_fragment_program GL_ARB_shading_language_100 GL_ARB_shader_objects GL_ARB_vertex_shader GL_ARB_fragment_shader GL_ARB_texture_cube_map GL_EXT_draw_range_elements' + + (GL.compressionExt ? ' GL_ARB_texture_compression GL_EXT_texture_compression_s3tc' : '') + + (GL.anisotropicExt ? ' GL_EXT_texture_filter_anisotropic' : '') + ), 'i8', ALLOC_NORMAL); } return glGetString(name_); }; @@ -1089,6 +1120,10 @@ var LibraryGL = { source = 'uniform float u_fogScale; \n' + source.replace(/gl_Fog.scale/g, 'u_fogScale'); } + if (source.indexOf('gl_Fog.density') >= 0) { + source = 'uniform float u_fogDensity; \n' + + source.replace(/gl_Fog.density/g, 'u_fogDensity'); + } if (source.indexOf('gl_FogFragCoord') >= 0) { source = 'varying float v_fogFragCoord; \n' + source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord'); @@ -1182,6 +1217,10 @@ var LibraryGL = { {{{ makeSetValue('params', '0', 'GLEmulation.fogStart', 'float') }}}; } else if (pname == 0x0B64) { // GL_FOG_END {{{ makeSetValue('params', '0', 'GLEmulation.fogEnd', 'float') }}}; + } else if (pname == 0x0B62) { // GL_FOG_DENSITY + {{{ makeSetValue('params', '0', 'GLEmulation.fogDensity', 'float') }}}; + } else if (pname == 0x0B65) { // GL_FOG_MODE + {{{ makeSetValue('params', '0', 'GLEmulation.fogMode', 'float') }}}; } else { glGetFloatv(pname, params); } @@ -1474,7 +1513,14 @@ var LibraryGL = { if (!cacheItem[attribute.type]) cacheItem[attribute.type] = {}; cacheItem = cacheItem[attribute.type]; } - if (GL.currProgram) { + if (GLEmulation.fogEnabled) { + var fogParam = GLEmulation.fogMode; + } else { + var fogParam = 0; // all valid modes are non-zero + } + if (!cacheItem[fogParam]) cacheItem[fogParam] = {}; + cacheItem = cacheItem[fogParam]; + if (GL.currProgram) { // Note the order here; this one is last, and optional if (!cacheItem[GL.currProgram]) cacheItem[GL.currProgram] = {}; cacheItem = cacheItem[GL.currProgram]; } @@ -1526,20 +1572,43 @@ var LibraryGL = { } this.program = GL.programs[GL.currProgram]; } else { + // IMPORTANT NOTE: If you parameterize the shader source based on any runtime values + // in order to create the least expensive shader possible based on the features being + // used, you should also update the code in the beginning of getRenderer to make sure + // that you cache the renderer based on the said parameters. this.vertexShader = Module.ctx.createShader(Module.ctx.VERTEX_SHADER); var zero = positionSize == 2 ? '0, ' : ''; + if (GLEmulation.fogEnabled) { + switch (GLEmulation.fogMode) { + case 0x0801: // GL_EXP2 + // fog = exp(-(gl_Fog.density * gl_FogFragCoord)^2) + var fogFormula = ' float fog = exp(-u_fogDensity * u_fogDensity * ecDistance * ecDistance); \n'; + break; + case 0x2601: // GL_LINEAR + // fog = (gl_Fog.end - gl_FogFragCoord) * gl_fog.scale + var fogFormula = ' float fog = (u_fogEnd - ecDistance) * u_fogScale; \n'; + break; + default: // default to GL_EXP + // fog = exp(-gl_Fog.density * gl_FogFragCoord) + var fogFormula = ' float fog = exp(-u_fogDensity * ecDistance); \n'; + break; + } + } Module.ctx.shaderSource(this.vertexShader, 'attribute vec' + positionSize + ' a_position; \n' + 'attribute vec2 a_texCoord0; \n' + (hasTextures ? 'varying vec2 v_texCoord; \n' : '') + 'varying vec4 v_color; \n' + (colorSize ? 'attribute vec4 a_color; \n': 'uniform vec4 u_color; \n') + + (GLEmulation.fogEnabled ? 'varying float v_fogFragCoord; \n' : '') + 'uniform mat4 u_modelView; \n' + 'uniform mat4 u_projection; \n' + 'void main() \n' + '{ \n' + - ' gl_Position = u_projection * (u_modelView * vec4(a_position, ' + zero + '1.0)); \n' + + ' vec4 ecPosition = (u_modelView * vec4(a_position, ' + zero + '1.0)); \n' + // eye-coordinate position + ' gl_Position = u_projection * ecPosition; \n' + (hasTextures ? 'v_texCoord = a_texCoord0; \n' : '') + (colorSize ? 'v_color = a_color; \n' : 'v_color = u_color; \n') + + (GLEmulation.fogEnabled ? 'v_fogFragCoord = abs(ecPosition.z);\n' : '') + '} \n'); Module.ctx.compileShader(this.vertexShader); @@ -1548,10 +1617,23 @@ var LibraryGL = { 'varying vec2 v_texCoord; \n' + 'uniform sampler2D u_texture; \n' + 'varying vec4 v_color; \n' + + (GLEmulation.fogEnabled ? ( + 'varying float v_fogFragCoord; \n' + + 'uniform vec4 u_fogColor; \n' + + 'uniform float u_fogEnd; \n' + + 'uniform float u_fogScale; \n' + + 'uniform float u_fogDensity; \n' + + 'float ffog(in float ecDistance) { \n' + + fogFormula + + ' fog = clamp(fog, 0.0, 1.0); \n' + + ' return fog; \n' + + '} \n' + ) : '') + 'void main() \n' + '{ \n' + (hasTextures ? 'gl_FragColor = v_color * texture2D( u_texture, v_texCoord );\n' : 'gl_FragColor = v_color;\n') + + (GLEmulation.fogEnabled ? 'gl_FragColor = vec4(mix(u_fogColor.rgb, gl_FragColor.rgb, ffog(v_fogFragCoord)), gl_FragColor.a); \n' : '') + '} \n'); Module.ctx.compileShader(this.fragmentShader); @@ -1591,7 +1673,9 @@ var LibraryGL = { this.fogColorLocation = Module.ctx.getUniformLocation(this.program, 'u_fogColor'); this.fogEndLocation = Module.ctx.getUniformLocation(this.program, 'u_fogEnd'); this.fogScaleLocation = Module.ctx.getUniformLocation(this.program, 'u_fogScale'); - this.hasFog = !!(this.fogColorLocation || this.fogEndLocation || this.fogScaleLocation); + this.fogDensityLocation = Module.ctx.getUniformLocation(this.program, 'u_fogDensity'); + this.hasFog = !!(this.fogColorLocation || this.fogEndLocation || + this.fogScaleLocation || this.fogDensityLocation); }, prepare: function() { @@ -1678,6 +1762,7 @@ var LibraryGL = { if (this.fogColorLocation) Module.ctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor); if (this.fogEndLocation) Module.ctx.uniform1f(this.fogEndLocation, GLEmulation.fogEnd); if (this.fogScaleLocation) Module.ctx.uniform1f(this.fogScaleLocation, 1/(GLEmulation.fogEnd - GLEmulation.fogStart)); + if (this.fogDensityLocation) Module.ctx.uniform1f(this.fogDensityLocation, GLEmulation.fogDensity); } }, @@ -1996,7 +2081,7 @@ var LibraryGL = { }, glColor4us__deps: ['glColor4f'], glColor4us: function(r, g, b, a) { - _glColor4f((r&65525)/65535, (g&65525)/65535, (b&65525)/65535, (a&65525)/65535); + _glColor4f((r&65535)/65535, (g&65535)/65535, (b&65535)/65535, (a&65535)/65535); }, glColor4ui__deps: ['glColor4f'], glColor4ui: function(r, g, b, a) { @@ -2047,10 +2132,24 @@ var LibraryGL = { GLEmulation.fogStart = param; break; case 0x0B64: // GL_FOG_END GLEmulation.fogEnd = param; break; + case 0x0B62: // GL_FOG_DENSITY + GLEmulation.fogDensity = param; break; + case 0x0B65: // GL_FOG_MODE + switch (param) { + case 0x0801: // GL_EXP2 + case 0x2601: // GL_LINEAR + GLEmulation.fogMode = param; break; + default: // default to GL_EXP + GLEmulation.fogMode = 0x0800 /* GL_EXP */; break; + } + break; } }, - glFogi: function(){}, // TODO - glFogx: function(){}, // TODO + glFogi__deps: ['glFogf'], + glFogi: function(pname, param) { + return _glFogf(pname, param); + }, + glFogfv__deps: ['glFogf'], glFogfv: function(pname, param) { // partial support, TODO switch(pname) { case 0x0B66: // GL_FOG_COLOR @@ -2059,8 +2158,26 @@ var LibraryGL = { GLEmulation.fogColor[2] = {{{ makeGetValue('param', '8', 'float') }}}; GLEmulation.fogColor[3] = {{{ makeGetValue('param', '12', 'float') }}}; break; + case 0x0B63: // GL_FOG_START + case 0x0B64: // GL_FOG_END + _glFogf(pname, {{{ makeGetValue('param', '0', 'float') }}}); break; + } + }, + glFogiv__deps: ['glFogf'], + glFogiv: function(pname, param) { + switch(pname) { + case 0x0B66: // GL_FOG_COLOR + GLEmulation.fogColor[0] = ({{{ makeGetValue('param', '0', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[1] = ({{{ makeGetValue('param', '4', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[2] = ({{{ makeGetValue('param', '8', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[3] = ({{{ makeGetValue('param', '12', 'i32') }}}/2147483647)/2.0+0.5; + break; + default: + _glFogf(pname, {{{ makeGetValue('param', '0', 'i32') }}}); break; } }, + glFogx: 'glFogi', + glFogxv: 'glFogiv', glPolygonMode: function(){}, // TODO diff --git a/src/library_sdl.js b/src/library_sdl.js index 41898056..88649c38 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -23,6 +23,10 @@ var LibrarySDL = { audio: null, volume: 1.0 }, + mixerFrequency: 22050, + mixerFormat: 0x8010, // AUDIO_S16LSB + mixerNumChannels: 2, + mixerChunkSize: 1024, keyboardState: null, shiftKey: false, @@ -1065,6 +1069,10 @@ var LibrarySDL = { Mix_OpenAudio: function(frequency, format, channels, chunksize) { SDL.allocateChannels(32); + SDL.mixerFrequency = frequency; + SDL.mixerFormat = format; + SDL.mixerNumChannels = channels; + SDL.mixerChunkSize = chunksize; return 0; }, @@ -1108,13 +1116,32 @@ var LibrarySDL = { return id; }, + Mix_QuickLoad_RAW: function(mem, len) { + var audio = new Audio(); + audio['mozSetup'](SDL.mixerNumChannels, SDL.mixerFrequency); + var numSamples = (len / (SDL.mixerNumChannels * 2)) | 0; + var buffer = new Float32Array(numSamples); + for (var i = 0; i < numSamples; ++i) { + buffer[i] = ({{{ makeGetValue('mem', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?) + } + var id = SDL.audios.length; + SDL.audios.push({ + source: '', + audio: audio, + buffer: buffer + }); + return id; + }, + Mix_FreeChunk: function(id) { SDL.audios[id] = null; }, Mix_PlayChannel: function(channel, id, loops) { // TODO: handle loops - var audio = SDL.audios[id].audio; + var info = SDL.audios[id]; + if (!info) return 0; + var audio = info.audio; if (!audio) return 0; if (channel == -1) { channel = 0; @@ -1125,15 +1152,20 @@ var LibrarySDL = { } } } - var info = SDL.channels[channel]; - info.audio = audio.cloneNode(true); + var channelInfo = SDL.channels[channel]; + channelInfo.audio = audio = audio.cloneNode(true); if (SDL.channelFinished) { - info.audio['onended'] = function() { // TODO: cache these + audio['onended'] = function() { // TODO: cache these Runtime.getFuncWrapper(SDL.channelFinished)(channel); } } - info.audio.play(); - info.audio.volume = info.volume; + if (info.buffer) { + audio['mozSetup'](SDL.mixerNumChannels, SDL.mixerFrequency); + audio["mozWriteAudio"](info.buffer); + } else { + audio.play(); + } + audio.volume = channelInfo.volume; return channel; }, Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing @@ -1176,7 +1208,11 @@ var LibrarySDL = { var audio = SDL.audios[id].audio; if (!audio) return 0; audio.loop = loops != 1; // TODO: handle N loops for finite N - audio.play(); + if (SDL.audios[id].buffer) { + audio["mozWriteAudio"](SDL.audios[id].buffer); + } else { + audio.play(); + } audio.volume = SDL.music.volume; audio['onended'] = _Mix_HaltMusic; // will send callback SDL.music.audio = audio; |