diff options
Diffstat (limited to 'src/library_gl.js')
-rw-r--r-- | src/library_gl.js | 1114 |
1 files changed, 806 insertions, 308 deletions
diff --git a/src/library_gl.js b/src/library_gl.js index 052226cf..b5d65ee6 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -5,6 +5,10 @@ var LibraryGL = { $GL: { +#if GL_DEBUG + debug: true, +#endif + counter: 1, buffers: {}, programs: {}, @@ -116,6 +120,8 @@ var LibraryGL = { return allocate(intArrayFromString(Module.ctx.getParameter(name_)), 'i8', ALLOC_NORMAL); case 0x1F03 /* GL_EXTENSIONS */: return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ')), 'i8', ALLOC_NORMAL); + case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */: + return allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL); default: throw 'Failure: Invalid glGetString value: ' + name_; } @@ -360,7 +366,7 @@ var LibraryGL = { }, glBindTexture: function(target, texture) { - Module.ctx.bindTexture(target, GL.textures[texture]); + Module.ctx.bindTexture(target, texture ? GL.textures[texture] : null); }, glGetTexParameterfv: function(target, pname, params) { @@ -433,7 +439,7 @@ var LibraryGL = { }, glBindRenderbuffer: function(target, renderbuffer) { - Module.ctx.bindRenderbuffer(target, GL.renderbuffers[renderbuffer]); + Module.ctx.bindRenderbuffer(target, renderbuffer ? GL.renderbuffers[renderbuffer] : null); }, glGetRenderbufferParameteriv: function(target, pname, params) { @@ -509,7 +515,7 @@ var LibraryGL = { program = GL.programs[program]; var info = Module.ctx.getActiveUniform(program, index); - var infoname = info.name.slice(0, bufsize - 1); + var infoname = info.name.slice(0, bufSize - 1); writeStringToMemory(infoname, name); if (length) { @@ -639,7 +645,7 @@ var LibraryGL = { }, glBindBuffer: function(target, buffer) { - Module.ctx.bindBuffer(target, GL.buffers[buffer]); + Module.ctx.bindBuffer(target, buffer ? GL.buffers[buffer] : null); }, glVertexAttrib1fv: function(index, v) { @@ -672,7 +678,7 @@ var LibraryGL = { program = GL.programs[program]; var info = Module.ctx.getActiveAttrib(program, index); - var infoname = info.name.slice(0, bufsize - 1); + var infoname = info.name.slice(0, bufSize - 1); writeStringToMemory(infoname, name); if (length) { @@ -719,9 +725,9 @@ var LibraryGL = { Module.ctx.shaderSource(GL.shaders[shader], source); }, - glGetShaderSource: function(shader, bufsize, length, source) { + glGetShaderSource: function(shader, bufSize, length, source) { var result = Module.ctx.getShaderSource(GL.shaders[shader]); - result.slice(0, bufsize - 1); + result.slice(0, bufSize - 1); writeStringToMemory(result, source); if (length) { {{{ makeSetValue('length', '0', 'result.length', 'i32') }}}; @@ -746,11 +752,19 @@ var LibraryGL = { }, glGetShaderiv : function(shader, pname, p) { - {{{ makeSetValue('p', '0', 'Module.ctx.getShaderParameter(GL.shaders[shader], pname)', 'i32') }}}; + if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH + {{{ makeSetValue('p', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[shader]).length + 1', 'i32') }}}; + } else { + {{{ makeSetValue('p', '0', 'Module.ctx.getShaderParameter(GL.shaders[shader], pname)', 'i32') }}}; + } }, glGetProgramiv : function(program, pname, p) { - {{{ makeSetValue('p', '0', 'Module.ctx.getProgramParameter(GL.programs[program], pname)', 'i32') }}}; + if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH + {{{ makeSetValue('p', '0', 'Module.ctx.getProgramInfoLog(GL.programs[program]).length + 1', 'i32') }}}; + } else { + {{{ makeSetValue('p', '0', 'Module.ctx.getProgramParameter(GL.programs[program], pname)', 'i32') }}}; + } }, glIsShader: function(shader) { @@ -823,7 +837,7 @@ var LibraryGL = { }, glBindFramebuffer: function(target, framebuffer) { - Module.ctx.bindFramebuffer(target, GL.framebuffers[framebuffer]); + Module.ctx.bindFramebuffer(target, framebuffer ? GL.framebuffers[framebuffer] : null); }, glGenFramebuffers: function(n, ids) { @@ -867,24 +881,31 @@ var LibraryGL = { // GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL - $GLEmulation__deps: ['glCreateShader', 'glShaderSource', 'glCompileShader', 'glCreateProgram', 'glDeleteShader', 'glDeleteProgram', 'glAttachShader', 'glActiveTexture', 'glGetShaderiv', 'glGetProgramiv', 'glLinkProgram', 'glGetProgramInfoLog', 'glGetShaderInfoLog'], $GLEmulation__postset: 'GLEmulation.init();', $GLEmulation: { init: function() { // 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 = { + 0x0DE1: 1, // GL_TEXTURE_2D + 0x0B20: 1, // GL_LINE_SMOOTH + 0x0B60: 1, // GL_FOG + 0x8513: 1, // GL_TEXTURE_CUBE_MAP + 0x0BA1: 1, // GL_NORMALIZE + 0x0C60: 1, // GL_TEXTURE_GEN_S + 0x0C61: 1 // GL_TEXTURE_GEN_T + }; _glEnable = function(cap) { - if (cap == 0x0DE1) return; // GL_TEXTURE_2D - if (cap == 0x0B20) return; // GL_LINE_SMOOTH - if (cap == 0x0B60) return; // GL_FOG + if (cap in ignoredCapabilities) return; Module.ctx.enable(cap); }; _glDisable = function(cap) { - if (cap == 0x0DE1) return; // GL_TEXTURE_2D - if (cap == 0x0B20) return; // GL_LINE_SMOOTH - if (cap == 0x0B60) return; // GL_FOG + if (cap in ignoredCapabilities) return; Module.ctx.disable(cap); }; + var glGetIntegerv = _glGetIntegerv; _glGetIntegerv = function(pname, params) { switch (pname) { @@ -904,58 +925,105 @@ var LibraryGL = { {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply return; } + case 0x8871: pname = Module.ctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS } glGetIntegerv(pname, params); }; + // Do some automatic rewriting to work around GLSL differences. Note that this must be done in // tandem with the rest of the program, by itself it cannot suffice. // Note that we need to remember shader types for this rewriting, saving sources makes it easier to debug. - GL.shaderTypes = {}; + GL.shaderInfos = {}; +#if GL_DEBUG GL.shaderSources = {}; GL.shaderOriginalSources = {}; +#endif var glCreateShader = _glCreateShader; _glCreateShader = function(shaderType) { var id = glCreateShader(shaderType); - GL.shaderTypes[id] = shaderType; + GL.shaderInfos[id] = { + type: shaderType, + ftransform: false + }; return id; }; + var glShaderSource = _glShaderSource; _glShaderSource = function(shader, count, string, length) { var source = GL.getSource(shader, count, string, length); +#if GL_DEBUG GL.shaderOriginalSources[shader] = source; - if (GL.shaderTypes[shader] == Module.ctx.VERTEX_SHADER) { +#endif + // XXX We add attributes and uniforms to shaders. The program can ask for the # of them, and see the + // ones we generated, potentially confusing it? Perhaps we should hide them. + if (GL.shaderInfos[shader].type == Module.ctx.VERTEX_SHADER) { // Replace ftransform() with explicit project/modelview transforms, and add position and matrix info. - source = 'attribute vec4 a_position; \n\ - uniform mat4 u_modelView; \n\ - uniform mat4 u_projection; \n' + - source.replace(/ftransform\(\)/g, 'u_projection * u_modelView * a_position') - .replace(/gl_Vertex/g, 'a_position') - .replace(/gl_ModelViewMatrix/g, 'u_modelView') - .replace(/gl_ProjectionMatrix/g, 'u_projection') - .replace(/gl_ModelViewProjectionMatrix/g, 'u_modelView * u_projection') - .replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec3(u_modelView[0][0], u_modelView[1][0], u_modelView[2][0])'); // XXX extremely inefficient - for (var i = 0; i <= 6; i++) { + var has_pm = source.search(/u_projection/) >= 0; + var has_mm = source.search(/u_modelView/) >= 0; + var has_pv = source.search(/a_position/) >= 0; + var need_pm = 0, need_mm = 0, need_pv = 0; + var old = source; + source = source.replace(/ftransform\(\)/g, '(u_projection * u_modelView * a_position)'); + if (old != source) need_pm = need_mm = need_pv = 1; + old = source; + source = source.replace(/gl_ProjectionMatrix/g, 'u_projection'); + if (old != source) need_pm = 1; + old = source; + source = source.replace(/gl_ModelViewMatrix/g, 'u_modelView'); + if (old != source) need_mm = 1; + old = source; + source = source.replace(/gl_Vertex/g, 'a_position'); + if (old != source) need_pv = 1; + old = source; + source = source.replace(/gl_ModelViewProjectionMatrix/g, '(u_projection * u_modelView)'); + if (old != source) need_pm = need_mm = 1; + old = source; + source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec3(u_modelView[0][0], u_modelView[1][0], u_modelView[2][0])'); // XXX extremely inefficient + if (old != source) need_mm = 1; + if (need_pv && !has_pv) source = 'attribute vec4 a_position; \n' + source; + if (need_mm && !has_mm) source = 'uniform mat4 u_modelView; \n' + source; + if (need_pm && !has_pm) source = 'uniform mat4 u_projection; \n' + source; + GL.shaderInfos[shader].ftransform = need_pm || need_mm || need_pv; // we will need to provide the fixed function stuff as attributes and uniforms + for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { // XXX To handle both regular texture mapping and cube mapping, we use vec4 for tex coordinates. var old = source; + var need_vtc = source.search('v_texCoord' + i) == -1; source = source.replace(new RegExp('gl_TexCoord\\[' + i + '\\]', 'g'), 'v_texCoord' + i) .replace(new RegExp('gl_MultiTexCoord' + i, 'g'), 'a_texCoord' + i); if (source != old) { - source = 'attribute vec4 a_texCoord' + i + '; \n\ - varying vec4 v_texCoord' + i + '; \n' + source; + source = 'attribute vec4 a_texCoord' + i + '; \n' + source; + if (need_vtc) { + source = 'varying vec4 v_texCoord' + i + '; \n' + source; + } } + + old = source; + source = source.replace(new RegExp('gl_TextureMatrix\\[' + i + '\\]', 'g'), 'u_textureMatrix' + i); + if (source != old) { + source = 'uniform mat4 u_textureMatrix' + i + '; \n' + source; + } + } + if (source.indexOf('gl_FrontColor') >= 0) { + source = 'varying vec4 v_color; \n' + + source.replace(/gl_FrontColor/g, 'v_color'); } if (source.indexOf('gl_Color') >= 0) { - source = 'attribute vec4 a_color; \n\ - varying vec4 v_color; \n' + - source.replace(/gl_Color/g, 'a_color').replace(/gl_FrontColor/g, 'v_color'); + source = 'attribute vec4 a_color; \n' + + 'uniform vec4 u_color; \n' + + 'uniform int u_hasColorAttrib; \n' + + source.replace(/gl_Color/g, '(u_hasColorAttrib > 0 ? a_color : u_color)'); + } + if (source.indexOf('gl_Normal') >= 0) { + source = 'attribute vec3 a_normal; \n' + + source.replace(/gl_Normal/g, 'a_normal'); } if (source.indexOf('gl_FogFragCoord') >= 0) { source = 'varying float v_fogCoord; \n' + source.replace(/gl_FogFragCoord/g, 'v_fogCoord'); } } else { // Fragment shader - for (var i = 0; i <= 6; i++) { - var old = 0; + for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { + var old = source; source = source.replace(new RegExp('gl_TexCoord\\[' + i + '\\]', 'g'), 'v_texCoord' + i); if (source != old) { source = 'varying vec4 v_texCoord' + i + '; \n' + source; @@ -967,82 +1035,194 @@ var LibraryGL = { source = source.replace(/gl_Fog.color/g, 'vec4(0.0)'); // XXX TODO source = 'precision mediump float;\n' + source; } +#if GL_DEBUG GL.shaderSources[shader] = source; +#endif Module.ctx.shaderSource(GL.shaders[shader], source); }; + var glCompileShader = _glCompileShader; _glCompileShader = function(shader) { Module.ctx.compileShader(GL.shaders[shader]); if (!Module.ctx.getShaderParameter(GL.shaders[shader], Module.ctx.COMPILE_STATUS)) { console.log('Failed to compile shader: ' + Module.ctx.getShaderInfoLog(GL.shaders[shader])); - console.log('Type: ' + GL.shaderTypes[shader]); + console.log('Info: ' + JSON.stringify(GL.shaderInfos[shader])); +#if GL_DEBUG console.log('Original source: ' + GL.shaderOriginalSources[shader]); console.log('Source: ' + GL.shaderSources[shader]); throw 'Shader compilation halt'; +#else + console.log('Enable GL_DEBUG to see shader source'); +#endif } }; - }, - procReplacements: { - 'glCreateShaderObjectARB': 'glCreateShader', - 'glShaderSourceARB': 'glShaderSource', - 'glCompileShaderARB': 'glCompileShader', - 'glCreateProgramObjectARB': 'glCreateProgram', - 'glAttachObjectARB': 'glAttachShader', - 'glLinkProgramARB': 'glLinkProgram', - 'glActiveTextureARB': 'glActiveTexture' - }, + GL.programShaders = {}; + var glAttachShader = _glAttachShader; + _glAttachShader = function(program, shader) { + if (!GL.programShaders[program]) GL.programShaders[program] = []; + GL.programShaders[program].push(shader); + glAttachShader(program, shader); + }; - procs: { - glDeleteObjectARB: function(id) { - if (GL.programs[id]) { - _glDeleteProgram(id); - } else if (GL.shaders[id]) { - _glDeleteShader(id); - } else { - console.log('WARNING: deleteObjectARB received invalid id: ' + id); + var glUseProgram = _glUseProgram; + _glUseProgram = function(program) { +#if GL_DEBUG + if (GL.debug) { + console.log('[using program with shaders:]'); + GL.programShaders[program].forEach(function(shader) { + console.log(' shader ' + shader + ', original source: ' + GL.shaderOriginalSources[shader]); + console.log(' Source: ' + GL.shaderSources[shader]); + }); } - }, +#endif + GL.currProgram = program; + glUseProgram(program); + } - glGetObjectParameterivARB: function(id, type, result) { - if (GL.programs[id]) { - if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB - {{{ makeSetValue('result', '0', 'Module.ctx.getProgramInfoLog(GL.programs[id]).length', 'i32') }}}; - return; - } - _glGetProgramiv(id, type, result); - } else if (GL.shaders[id]) { - if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB - {{{ makeSetValue('result', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[id]).length', 'i32') }}}; - return; - } - _glGetShaderiv(id, type, result); - } else { - console.log('WARNING: getObjectParameterivARB received invalid id: ' + id); + var glDeleteProgram = _glDeleteProgram; + _glDeleteProgram = function(program) { + glDeleteProgram(program); + if (program == GL.currProgram) GL.currProgram = 0; + }; + + var glBindBuffer = _glBindBuffer; + _glBindBuffer = function(target, buffer) { + glBindBuffer(target, buffer); + if (target == Module.ctx.ARRAY_BUFFER) { + GL.currArrayBuffer = buffer; + } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) { + GL.currElementArrayBuffer = buffer; + } + }; + + var glDeleteBuffers = _glDeleteBuffers; + _glDeleteBuffers = function(n, buffers) { + glDeleteBuffers(n, buffers); + for (var i = 0; i < n; i++) { + var buffer = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; + if (buffer == GL.currArrayBuffer) GL.currArrayBuffer = 0; + if (buffer == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0; } - }, + }; - glGetInfoLogARB: function(id, maxLength, length, infoLog) { - if (GL.programs[id]) { - _glGetProgramInfoLog(id, maxLength, length, infoLog); - } else if (GL.shaders[id]) { - _glGetShaderInfoLog(id, maxLength, length, infoLog); + var glGetFloatv = _glGetFloatv; + _glGetFloatv = function(pname, params) { + if (pname == 0x0BA6) { // GL_MODELVIEW_MATRIX + HEAPF32.set(GL.immediate.matrix['m'], params >> 2); + } else if (pname == 0x0BA7) { // GL_PROJECTION_MATRIX + HEAPF32.set(GL.immediate.matrix['p'], params >> 2); + } else if (pname == 0x0BA8) { // GL_TEXTURE_MATRIX + HEAPF32.set(GL.immediate.matrix['t' + GL.immediate.clientActiveTexture], params >> 2); + } else if (pname == 0x0B66) { // GL_FOG_COLOR + {{{ makeSetValue('params', '0', '0', 'float') }}}; + } else if (pname == 0x0B63) { // GL_FOG_START + {{{ makeSetValue('params', '0', '0', 'float') }}}; + } else if (pname == 0x0B64) { // GL_FOG_END + {{{ makeSetValue('params', '0', '0', 'float') }}}; } else { - console.log('WARNING: getObjectParameterivARB received invalid id: ' + id); + glGetFloatv(pname, params); } - } + }; }, - getProcAddress: function(name_) { - name_ = GLEmulation.procReplacements[name_] || name_; - var func = GLEmulation.procs[name_]; - if (!func) { - try { - func = eval('_' + name_); // XXX closure, need Module. and for them to be exported - } catch(e) { - console.log('WARNING: getProcAddress failed for ' + name_); + getProcAddress: function(name) { + name = name.replace('EXT', '').replace('ARB', ''); + // Do the translation carefully because of closure + switch (name) { + case 'glCreateShaderObject': case 'glCreateShader': func = _glCreateShader; break; + case 'glCreateProgramObject': case 'glCreateProgram': func = _glCreateProgram; break; + case 'glAttachObject': case 'glAttachShader': func = _glAttachShader; break; + case 'glUseProgramObject': case 'glUseProgram': func = _glUseProgram; break; + case 'glDeleteObject': func = function(id) { + if (GL.programs[id]) { + _glDeleteProgram(id); + } else if (GL.shaders[id]) { + _glDeleteShader(id); + } else { + console.log('WARNING: deleteObject received invalid id: ' + id); + } + }; break; + case 'glGetObjectParameteriv': func = function(id, type, result) { + if (GL.programs[id]) { + if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB + {{{ makeSetValue('result', '0', 'Module.ctx.getProgramInfoLog(GL.programs[id]).length', 'i32') }}}; + return; + } + _glGetProgramiv(id, type, result); + } else if (GL.shaders[id]) { + if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB + {{{ makeSetValue('result', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[id]).length', 'i32') }}}; + return; + } + _glGetShaderiv(id, type, result); + } else { + console.log('WARNING: getObjectParameteriv received invalid id: ' + id); + } + }; break; + case 'glGetInfoLog': func = function(id, maxLength, length, infoLog) { + if (GL.programs[id]) { + _glGetProgramInfoLog(id, maxLength, length, infoLog); + } else if (GL.shaders[id]) { + _glGetShaderInfoLog(id, maxLength, length, infoLog); + } else { + console.log('WARNING: getObjectParameteriv received invalid id: ' + id); + } + }; break; + case 'glBindProgram': func = function(type, id) { + assert(id == 0); + }; break; + case 'glShaderSource': func = _glShaderSource; break; + case 'glCompileShader': func = _glCompileShader; break; + case 'glLinkProgram': func = _glLinkProgram; break; + case 'glGetUniformLocation': func = _glGetUniformLocation; break; + case 'glUniform1f': func = _glUniform1f; break; + case 'glUniform2f': func = _glUniform2f; break; + case 'glUniform3f': func = _glUniform3f; break; + case 'glUniform4f': func = _glUniform4f; break; + case 'glUniform1fv': func = _glUniform1fv; break; + case 'glUniform2fv': func = _glUniform2fv; break; + case 'glUniform3fv': func = _glUniform3fv; break; + case 'glUniform4fv': func = _glUniform4fv; break; + case 'glUniform1i': func = _glUniform1i; break; + case 'glUniform2i': func = _glUniform2i; break; + case 'glUniform3i': func = _glUniform3i; break; + case 'glUniform4i': func = _glUniform4i; break; + case 'glUniform1iv': func = _glUniform1iv; break; + case 'glUniform2iv': func = _glUniform2iv; break; + case 'glUniform3iv': func = _glUniform3iv; break; + case 'glUniform4iv': func = _glUniform4iv; break; + case 'glBindAttribLocation': func = _glBindAttribLocation; break; + case 'glGetActiveUniform': func = _glGetActiveUniform; break; + case 'glGenBuffers': func = _glGenBuffers; break; + case 'glBindBuffer': func = _glBindBuffer; break; + case 'glBufferData': func = _glBufferData; break; + case 'glBufferSubData': func = _glBufferSubData; break; + case 'glDeleteBuffers': func = _glDeleteBuffers; break; + case 'glActiveTexture': func = _glActiveTexture; break; + case 'glClientActiveTexture': func = _glClientActiveTexture; break; + case 'glGetProgramiv': func = _glGetProgramiv; break; + case 'glEnableVertexAttribArray': func = _glEnableVertexAttribArray; break; + case 'glDisableVertexAttribArray': func = _glDisableVertexAttribArray; break; + case 'glVertexAttribPointer': func = _glVertexAttribPointer; break; + case 'glBindRenderbuffer': func = _glBindRenderbuffer; break; + case 'glDeleteRenderbuffers': func = _glDeleteRenderbuffers; break; + case 'glGenRenderbuffers': func = _glGenRenderbuffers; break; + case 'glCompressedTexImage2D': func = _glCompressedTexImage2D; break; + case 'glCompressedTexSubImage2D': func = _glCompressedTexSubImage2D; break; + case 'glBindFramebuffer': func = _glBindFramebuffer; break; + case 'glGenFramebuffers': func = _glGenFramebuffers; break; + case 'glDeleteFramebuffers': func = _glDeleteFramebuffers; break; + case 'glFramebufferRenderbuffer': func = _glFramebufferRenderbuffer; break; + case 'glFramebufferTexture2D': func = _glFramebufferTexture2D; break; + case 'glGetFramebufferAttachmentParameteriv': func = _glGetFramebufferAttachmentParameteriv; break; + case 'glIsFramebuffer': func = _glIsFramebuffer; break; + case 'glCheckFramebufferStatus': func = _glCheckFramebufferStatus; break; + case 'glRenderbufferStorage': func = _glRenderbufferStorage; break; + default: { + console.log('WARNING: getProcAddress failed for ' + name); func = function() { - console.log('WARNING: empty replacement for ' + name_ + ' called, no-op'); + console.log('WARNING: empty replacement for ' + name + ' called, no-op'); return 0; }; } @@ -1053,134 +1233,282 @@ var LibraryGL = { // GL Immediate mode + $GLImmediate__postset: 'Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });', + $GLImmediate__deps: ['$Browser'], $GLImmediate: { // Vertex and index data - maxElements: 1024, - vertexData: null, // current vertex data. either tempData (glBegin etc.) or a view into the heap (gl*Pointer) + maxElements: 10240, + MAX_TEXTURES: 7, + vertexData: null, // current vertex data. either tempData (glBegin etc.) or a view into the heap (gl*Pointer). Default view is F32 + vertexDataU8: null, // U8 view tempData: null, indexData: null, vertexCounter: 0, mode: 0, - renderers: {}, - renderer: null, + rendererCache: {}, + rendererComponents: {}, // small cache for calls inside glBegin/end. counts how many times the element was seen + rendererComponentPointer: 0, // next place to start a glBegin/end component // The following data structures are used for OpenGL Immediate Mode matrix routines. - matrix: { - 'm': null, // modelview - 'p': null // projection - }, - matrixStack: { - 'm': [], // modelview - 'p': [] // projection - }, + matrix: {}, + matrixStack: {}, currentMatrix: 'm', // default is modelview tempMatrix: null, - initMatrixLibrary: function() { - GL.immediate.matrix['m'] = GL.immediate.matrix.lib.mat4.create(); - GL.immediate.matrix['p'] = GL.immediate.matrix.lib.mat4.create(); - GL.immediate.currentMatrix = GL.immediate.matrix.lib.mat4.create(); - }, // Clientside attributes - TEXTURE: 0, - VERTEX: 1, - NUM_ATTRIBUTES: 2, - ATTRIBUTE_BY_NAME: { - 'T': 0, - 'V': 1 - }, + VERTEX: 0, + NORMAL: 1, + COLOR: 2, + TEXTURE0: 3, + TEXTURE1: 4, + TEXTURE2: 5, + TEXTURE3: 6, + TEXTURE4: 7, + TEXTURE5: 8, + TEXTURE6: 9, + NUM_ATTRIBUTES: 10, + NUM_TEXTURES: 7, + ATTRIBUTE_BY_NAME: {}, totalEnabledClientAttributes: 0, enabledClientAttributes: [0, 0], - clientAttributes: [null, null], + clientAttributes: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}], // raw data, including possible unneeded ones + liveClientAttributes: [], // the ones actually alive in the current computation, sorted + clientActiveTexture: 0, + clientColor: null, + + byteSizeByType: { + 0x1400: 1, // GL_BYTE + 0x1401: 1, // GL_UNSIGNED_BYTE + 0x1402: 2, // GL_SHORT + 0x1403: 2, // GL_UNSIGNED_SHORT + 0x1404: 4, // GL_INT + 0x1405: 4, // GL_UNSIGNED_INT + 0x1406: 4 // GL_FLOAT + }, - setClientAttribute: function(which, name, size, type, stride, pointer) { - this.clientAttributes[which] = { - size: size, type: type, stride: stride, pointer: pointer, name: name + size - }; + setClientAttribute: function(name, size, type, stride, pointer) { + var attrib = this.clientAttributes[GL.immediate.ATTRIBUTE_BY_NAME[name]]; + attrib.name = name; + attrib.size = size; + attrib.type = type; + attrib.stride = stride; + attrib.pointer = pointer; }, // Renderers - setRenderer: function(renderer) { - this.renderer = renderer; - if (this.renderers[renderer]) return; - - // Create renderer - var vertexSize = 0; - for (var i = 0; i < renderer.length; i+=2) { - var which = renderer[i]; - var size = parseInt(renderer[i+1]); - if (which == 'V') { - positionSize = size; - positionOffset = vertexSize; - } else if (which == 'T') { - textureSize = size; - textureOffset = vertexSize; + addRendererComponent: function(name, size, type) { + if (!this.rendererComponents[name]) { + this.rendererComponents[name] = 1; +#if ASSERTIONS + assert(!this.enabledClientAttributes[this.ATTRIBUTE_BY_NAME[name]]); // cannot get mixed up with this, for example we will disable this later +#endif + this.enabledClientAttributes[this.ATTRIBUTE_BY_NAME[name]] = true; + this.setClientAttribute(name, size, type, 0, this.rendererComponentPointer); + this.rendererComponentPointer += size * this.byteSizeByType[type]; + } else { + this.rendererComponents[name]++; + } + }, + + disableBeginEndClientAttributes: function() { + for (var name in this.rendererComponents) { + this.enabledClientAttributes[this.ATTRIBUTE_BY_NAME[name]] = false; + } + }, + + getRenderer: function() { + // return a renderer object given the liveClientAttributes + // we maintain a cache of renderers, optimized to not generate garbage + var attributes = GL.immediate.liveClientAttributes; + var cacheItem = GL.immediate.rendererCache; + for (var i = 0; i < attributes.length; i++) { + var attribute = attributes[i]; + if (!cacheItem[attribute.name]) cacheItem[attribute.name] = {}; + cacheItem = cacheItem[attribute.name]; + if (!cacheItem[attribute.size]) cacheItem[attribute.size] = {}; + cacheItem = cacheItem[attribute.size]; + if (!cacheItem[attribute.type]) cacheItem[attribute.type] = {}; + cacheItem = cacheItem[attribute.type]; + } + if (GL.currProgram) { + if (!cacheItem[GL.currProgram]) cacheItem[GL.currProgram] = {}; + cacheItem = cacheItem[GL.currProgram]; + } + if (!cacheItem.renderer) { +#if GL_DEBUG + console.log('generating renderer for ' + JSON.stringify(attributes)); +#endif + cacheItem.renderer = this.createRenderer(); + } + return cacheItem.renderer; + }, + + createRenderer: function(renderer) { + var useCurrProgram = !!GL.currProgram; + var hasTextures = false, textureSizes = [], textureTypes = [], textureOffsets = []; + for (var i = 0; i < GL.immediate.NUM_TEXTURES; i++) { + if (GL.immediate.enabledClientAttributes[GL.immediate.TEXTURE0 + i]) { + textureSizes[i] = GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + i].size; + textureTypes[i] = GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + i].type; + textureOffsets[i] = GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + i].offset; + hasTextures = true; } - vertexSize += size * 4; // XXX assuming float } - // TODO: verify vertexSize is equal to the stride in enabled client arrays - // TODO: assert that we can create the renderer type we were asked - // TODO: use bufferSubData to prevent reallocation of new buffers? Or all on GPU and doesn't matter? Anyhow, use DYNAMIC as hint - this.renderers[renderer] = { - vertexSize: vertexSize, + var stride = GL.immediate.stride; + var positionSize = GL.immediate.clientAttributes[GL.immediate.VERTEX].size; + var positionType = GL.immediate.clientAttributes[GL.immediate.VERTEX].type; + var positionOffset = GL.immediate.clientAttributes[GL.immediate.VERTEX].offset; + var colorSize = 0, colorType, colorOffset; + if (GL.immediate.enabledClientAttributes[GL.immediate.COLOR]) { + colorSize = GL.immediate.clientAttributes[GL.immediate.COLOR].size; + colorType = GL.immediate.clientAttributes[GL.immediate.COLOR].type; + colorOffset = GL.immediate.clientAttributes[GL.immediate.COLOR].offset; + } + var normalSize = 0, normalType, normalOffset; + if (GL.immediate.enabledClientAttributes[GL.immediate.NORMAL]) { + normalSize = GL.immediate.clientAttributes[GL.immediate.NORMAL].size; + normalType = GL.immediate.clientAttributes[GL.immediate.NORMAL].type; + normalOffset = GL.immediate.clientAttributes[GL.immediate.NORMAL].offset; + } + var ret = { init: function() { - this.vertexShader = Module.ctx.createShader(Module.ctx.VERTEX_SHADER); - var zero = positionSize == 2 ? '0, ' : ''; - Module.ctx.shaderSource(this.vertexShader, 'attribute vec' + positionSize + ' a_position; \n\ - attribute vec2 a_texCoord; \n\ - varying vec2 v_texCoord; \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\ - v_texCoord = a_texCoord; \n\ - } \n'); - Module.ctx.compileShader(this.vertexShader); - - this.fragmentShader = Module.ctx.createShader(Module.ctx.FRAGMENT_SHADER); - Module.ctx.shaderSource(this.fragmentShader, 'precision mediump float; \n\ - varying vec2 v_texCoord; \n\ - uniform sampler2D s_texture; \n\ - void main() \n\ - { \n\ - gl_FragColor = texture2D( s_texture, v_texCoord );\n\ - } \n'); - Module.ctx.compileShader(this.fragmentShader); - - this.program = Module.ctx.createProgram(); - Module.ctx.attachShader(this.program, this.vertexShader); - Module.ctx.attachShader(this.program, this.fragmentShader); - Module.ctx.linkProgram(this.program); + if (useCurrProgram) { + if (GL.shaderInfos[GL.programShaders[GL.currProgram][0]].type == Module.ctx.VERTEX_SHADER) { + this.vertexShader = GL.shaders[GL.programShaders[GL.currProgram][0]]; + this.fragmentShader = GL.shaders[GL.programShaders[GL.currProgram][1]]; + } else { + this.vertexShader = GL.shaders[GL.programShaders[GL.currProgram][1]]; + this.fragmentShader = GL.shaders[GL.programShaders[GL.currProgram][0]]; + } + this.program = GL.programs[GL.currProgram]; + } else { + this.vertexShader = Module.ctx.createShader(Module.ctx.VERTEX_SHADER); + var zero = positionSize == 2 ? '0, ' : ''; + 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') + + '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' + + (hasTextures ? 'v_texCoord = a_texCoord0; \n' : '') + + (colorSize ? 'v_color = a_color; \n' : 'v_color = u_color; \n') + + '} \n'); + Module.ctx.compileShader(this.vertexShader); + + this.fragmentShader = Module.ctx.createShader(Module.ctx.FRAGMENT_SHADER); + Module.ctx.shaderSource(this.fragmentShader, 'precision mediump float; \n' + + 'varying vec2 v_texCoord; \n' + + 'uniform sampler2D u_texture; \n' + + 'varying vec4 v_color; \n' + + 'void main() \n' + + '{ \n' + + (hasTextures ? 'gl_FragColor = v_color * texture2D( u_texture, v_texCoord );\n' : + 'gl_FragColor = v_color;\n') + + '} \n'); + Module.ctx.compileShader(this.fragmentShader); + + this.program = Module.ctx.createProgram(); + Module.ctx.attachShader(this.program, this.vertexShader); + Module.ctx.attachShader(this.program, this.fragmentShader); + Module.ctx.linkProgram(this.program); + } this.positionLocation = Module.ctx.getAttribLocation(this.program, 'a_position'); - this.texCoordLocation = Module.ctx.getAttribLocation(this.program, 'a_texCoord'); - this.textureLocation = Module.ctx.getUniformLocation(this.program, 's_texture'); + this.texCoordLocations = []; + for (var i = 0; i < textureSizes.length; i++) { + if (textureSizes[i]) { + this.texCoordLocations[i] = Module.ctx.getAttribLocation(this.program, 'a_texCoord' + i); + } + } + this.textureMatrixLocations = []; + for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { + this.textureMatrixLocations[i] = Module.ctx.getUniformLocation(this.program, 'u_textureMatrix' + i); + } + this.colorLocation = Module.ctx.getAttribLocation(this.program, 'a_color'); + this.normalLocation = Module.ctx.getAttribLocation(this.program, 'a_normal'); + + this.textureLocation = Module.ctx.getUniformLocation(this.program, 'u_texture'); // only for immediate mode with no shaders, so only one is enough this.modelViewLocation = Module.ctx.getUniformLocation(this.program, 'u_modelView'); this.projectionLocation = Module.ctx.getUniformLocation(this.program, 'u_projection'); + this.hasColorAttribLocation = Module.ctx.getUniformLocation(this.program, 'u_hasColorAttrib'); + this.colorUniformLocation = Module.ctx.getUniformLocation(this.program, 'u_color'); + + this.hasTextures = hasTextures; + this.hasColorAttrib = colorSize > 0 && this.colorLocation >= 0; + this.hasColorUniform = !!this.colorUniformLocation; + this.hasNormal = normalSize > 0 && this.normalLocation >= 0; + + this.floatType = Module.ctx.FLOAT; // minor optimization }, prepare: function() { - Module.ctx.vertexAttribPointer(this.texCoordLocation, textureSize, Module.ctx.FLOAT, false, - vertexSize, textureOffset); - Module.ctx.vertexAttribPointer(this.positionLocation, positionSize, Module.ctx.FLOAT, false, - vertexSize, positionOffset); + if (this.modelViewLocation) Module.ctx.uniformMatrix4fv(this.modelViewLocation, false, GL.immediate.matrix['m']); + if (this.projectionLocation) Module.ctx.uniformMatrix4fv(this.projectionLocation, false, GL.immediate.matrix['p']); - Module.ctx.enableVertexAttribArray(this.texCoordLocation); + Module.ctx.vertexAttribPointer(this.positionLocation, positionSize, positionType, false, + stride, positionOffset); Module.ctx.enableVertexAttribArray(this.positionLocation); + if (this.hasTextures) { + for (var i = 0; i < textureSizes.length; i++) { + if (textureSizes[i] && this.texCoordLocations[i] >= 0) { + Module.ctx.vertexAttribPointer(this.texCoordLocations[i], textureSizes[i], textureTypes[i], false, + |