Move legacy GL emulation into its own file library_glemu.js. Restore glDrawArrays() and glDrawElements() to be closure minified.
diff --git a/emscripten-version.txt b/emscripten-version.txt
index ea9d262..cea72ce 100644
--- a/emscripten-version.txt
+++ b/emscripten-version.txt
@@ -1 +1 @@
-"1.38.22"
+"1.38.23"
diff --git a/src/closure-externs.js b/src/closure-externs.js
index f494c2d..e4882ed 100644
--- a/src/closure-externs.js
+++ b/src/closure-externs.js
@@ -1289,14 +1289,6 @@
/**
* @suppress {duplicate, undefinedVars}
*/
-var _glDrawArrays;
-/**
- * @suppress {duplicate, undefinedVars}
- */
-var _glDrawElements;
-/**
- * @suppress {duplicate, undefinedVars}
- */
var _glTexEnvf;
/**
* @suppress {duplicate, undefinedVars}
diff --git a/src/library_gl.js b/src/library_gl.js
index 601e2d5..1be7fd3 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -20,9 +20,6 @@
#if GL_DEBUG
debug: true,
#endif
-#if LEGACY_GL_EMULATION
- legacyGLEmulation: true,
-#endif
counter: 1, // 0 is reserved as 'null' in gl
#if GL_TRACK_ERRORS
@@ -251,37 +248,6 @@
},
#endif
-#if LEGACY_GL_EMULATION
- // Find a token in a shader source string
- findToken: function(source, token) {
- function isIdentChar(ch) {
- if (ch >= 48 && ch <= 57) // 0-9
- return true;
- if (ch >= 65 && ch <= 90) // A-Z
- return true;
- if (ch >= 97 && ch <= 122) // a-z
- return true;
- return false;
- }
- var i = -1;
- do {
- i = source.indexOf(token, i + 1);
- if (i < 0) {
- break;
- }
- if (i > 0 && isIdentChar(source[i - 1])) {
- continue;
- }
- i += token.length;
- if (i < source.length - 1 && isIdentChar(source[i + 1])) {
- continue;
- }
- return true;
- } while (true);
- return false;
- },
-#endif
-
getSource: function(shader, count, string, length) {
var source = '';
for (var i = 0; i < count; ++i) {
@@ -292,9 +258,9 @@
// Let's see if we need to enable the standard derivatives extension
var type = GLctx.getShaderParameter(GL.shaders[shader], 0x8B4F /* GL_SHADER_TYPE */);
if (type == 0x8B30 /* GL_FRAGMENT_SHADER */) {
- if (GL.findToken(source, "dFdx") ||
- GL.findToken(source, "dFdy") ||
- GL.findToken(source, "fwidth")) {
+ if (GLEmulation.findToken(source, "dFdx") ||
+ GLEmulation.findToken(source, "dFdy") ||
+ GLEmulation.findToken(source, "fwidth")) {
source = "#extension GL_OES_standard_derivatives : enable\n" + source;
var extension = GLctx.getExtension("OES_standard_derivatives");
#if GL_DEBUG
@@ -4238,3352 +4204,7 @@
#endif
},
-#if LEGACY_GL_EMULATION
-
- // GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL
-
- $GLEmulation__deps: ['$GLImmediateSetup', 'glEnable', 'glDisable', 'glIsEnabled', 'glGetBooleanv', 'glGetIntegerv', 'glGetString', 'glCreateShader', 'glShaderSource', 'glCompileShader', 'glAttachShader', 'glDetachShader', 'glUseProgram', 'glDeleteProgram', 'glBindAttribLocation', 'glLinkProgram', 'glBindBuffer', 'glGetFloatv', 'glHint', 'glEnableVertexAttribArray', 'glDisableVertexAttribArray', 'glVertexAttribPointer', 'glActiveTexture'],
- $GLEmulation__postset: 'GLEmulation.init();',
- $GLEmulation: {
- // 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,
-
- // VAO support
- vaos: [],
- currentVao: null,
- enabledVertexAttribArrays: {}, // helps with vao cleanups
-
- hasRunInit: false,
-
- init: function() {
- // Do not activate immediate/emulation code (e.g. replace glDrawElements) when in FULL_ES2 mode.
- // We do not need full emulation, we instead emulate client-side arrays etc. in FULL_ES2 code in
- // a straightforward manner, and avoid not having a bound buffer be ambiguous between es2 emulation
- // code and legacy gl emulation code.
-#if FULL_ES2
- return;
-#endif
-
- if (GLEmulation.hasRunInit) {
- return;
- }
- GLEmulation.hasRunInit = true;
-
- GLEmulation.fogColor = new Float32Array(4);
-
- // Add some emulation workarounds
- err('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work.');
-#if GL_UNSAFE_OPTS == 1
- err('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -s GL_UNSAFE_OPTS=0');
-#endif
-
- // 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
- };
-
-#if RELOCATABLE
-{{{
-(updateExport = function(name) {
- var name = '_' + name;
- var exported = 'Module["' + name + '"]';
- // make sure we write to an existing export, and are not repeating ourselves
- return 'assert(' + exported + ' !== ' + name + '); ' + exported + ' = ' + name + ';';
-}, '')
-}}}
-#else
-{{{ (updateExport = function(){ return '' }, '') }}}
-#endif
-
- var glEnable = _glEnable;
- _glEnable = _emscripten_glEnable = function _glEnable(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 (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup();
- if (cap == 0x0B60 /* GL_FOG */) {
- if (GLEmulation.fogEnabled != true) {
- GLImmediate.currentRenderer = null; // Fog parameter is part of the FFP shader state, we must re-lookup the renderer to use.
- GLEmulation.fogEnabled = true;
- }
- return;
- } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) {
- // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
- // it by forwarding to glEnableClientState
- /* Actually, let's not, for now. (This sounds exceedingly broken)
- * This is in gl_ps_workaround2.c.
- _glEnableClientState(cap);
- */
- return;
- } else if (!(cap in validCapabilities)) {
- return;
- }
- glEnable(cap);
- };
- {{{ updateExport('glEnable') }}}
-
- var glDisable = _glDisable;
- _glDisable = _emscripten_glDisable = function _glDisable(cap) {
- if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup();
- if (cap == 0x0B60 /* GL_FOG */) {
- if (GLEmulation.fogEnabled != false) {
- GLImmediate.currentRenderer = null; // Fog parameter is part of the FFP shader state, we must re-lookup the renderer to use.
- GLEmulation.fogEnabled = false;
- }
- return;
- } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) {
- // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
- // it by forwarding to glDisableClientState
- /* Actually, let's not, for now. (This sounds exceedingly broken)
- * This is in gl_ps_workaround2.c.
- _glDisableClientState(cap);
- */
- return;
- } else if (!(cap in validCapabilities)) {
- return;
- }
- glDisable(cap);
- };
- {{{ updateExport('glDisable') }}}
-
- _glIsEnabled = _emscripten_glIsEnabled = function _glIsEnabled(cap) {
- if (cap == 0x0B60 /* GL_FOG */) {
- return GLEmulation.fogEnabled ? 1 : 0;
- } else if (!(cap in validCapabilities)) {
- return 0;
- }
- return GLctx.isEnabled(cap);
- };
- {{{ updateExport('glIsEnabled') }}}
-
- var glGetBooleanv = _glGetBooleanv;
- _glGetBooleanv = _emscripten_glGetBooleanv = function _glGetBooleanv(pname, p) {
- var attrib = GLEmulation.getAttributeFromCapability(pname);
- if (attrib !== null) {
- var result = GLImmediate.enabledClientAttributes[attrib];
- {{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}};
- return;
- }
- glGetBooleanv(pname, p);
- };
- {{{ updateExport('glGetBooleanv') }}}
-
- var glGetIntegerv = _glGetIntegerv;
- _glGetIntegerv = _emscripten_glGetIntegerv = function _glGetIntegerv(pname, params) {
- switch (pname) {
- case 0x84E2: pname = GLctx.MAX_TEXTURE_IMAGE_UNITS /* fake it */; break; // GL_MAX_TEXTURE_UNITS
- case 0x8B4A: { // GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB
- var result = GLctx.getParameter(GLctx.MAX_VERTEX_UNIFORM_VECTORS);
- {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
- return;
- }
- case 0x8B49: { // GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB
- var result = GLctx.getParameter(GLctx.MAX_FRAGMENT_UNIFORM_VECTORS);
- {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
- return;
- }
- case 0x8B4B: { // GL_MAX_VARYING_FLOATS_ARB
- var result = GLctx.getParameter(GLctx.MAX_VARYING_VECTORS);
- {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
- return;
- }
- case 0x8871: pname = GLctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS
- case 0x807A: { // GL_VERTEX_ARRAY_SIZE
- var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
- return;
- }
- case 0x807B: { // GL_VERTEX_ARRAY_TYPE
- var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
- return;
- }
- case 0x807C: { // GL_VERTEX_ARRAY_STRIDE
- var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
- return;
- }
- case 0x8081: { // GL_COLOR_ARRAY_SIZE
- var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
- return;
- }
- case 0x8082: { // GL_COLOR_ARRAY_TYPE
- var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
- return;
- }
- case 0x8083: { // GL_COLOR_ARRAY_STRIDE
- var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
- return;
- }
- case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE
- var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
- return;
- }
- case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE
- var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
- return;
- }
- case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE
- var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
- {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
- return;
- }
- }
- glGetIntegerv(pname, params);
- };
- {{{ updateExport('glGetIntegerv') }}}
-
- var glGetString = _glGetString;
- _glGetString = _emscripten_glGetString = function _glGetString(name_) {
- if (GL.stringCache[name_]) return GL.stringCache[name_];
- switch(name_) {
- case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support
- var ret = allocate(intArrayFromString(GLctx.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.currentContext.compressionExt ? ' GL_ARB_texture_compression GL_EXT_texture_compression_s3tc' : '') +
- (GL.currentContext.anisotropicExt ? ' GL_EXT_texture_filter_anisotropic' : '')
- ), 'i8', ALLOC_NORMAL);
- GL.stringCache[name_] = ret;
- return ret;
- }
- return glGetString(name_);
- };
- {{{ updateExport('glGetString') }}}
-
- // 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.shaderInfos = {};
-#if GL_DEBUG
- GL.shaderSources = {};
- GL.shaderOriginalSources = {};
-#endif
- var glCreateShader = _glCreateShader;
- _glCreateShader = _emscripten_glCreateShader = function _glCreateShader(shaderType) {
- var id = glCreateShader(shaderType);
- GL.shaderInfos[id] = {
- type: shaderType,
- ftransform: false
- };
- return id;
- };
- {{{ updateExport('glCreateShader') }}}
-
- function ensurePrecision(source) {
- if (!/precision +(low|medium|high)p +float *;/.test(source)) {
- source = 'precision mediump float;\n' + source;
- }
- return source;
- }
-
- var glShaderSource = _glShaderSource;
- _glShaderSource = _emscripten_glShaderSource = function _glShaderSource(shader, count, string, length) {
- var source = GL.getSource(shader, count, string, length);
-#if GL_DEBUG
- console.log("glShaderSource: Input: \n" + source);
- GL.shaderOriginalSources[shader] = source;
-#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 == GLctx.VERTEX_SHADER) {
- // Replace ftransform() with explicit project/modelview transforms, and add position and matrix info.
- 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_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][2], u_modelView[1][2], u_modelView[2][2], u_modelView[3][2])'); // XXX extremely inefficient
- if (old != source) need_mm = 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;
- 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 < GLImmediate.MAX_TEXTURES; i++) {
- // XXX To handle both regular texture mapping and cube mapping, we use vec4 for tex coordinates.
- 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' + 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' +
- source.replace(/gl_Color/g, 'a_color');
- }
- if (source.indexOf('gl_Normal') >= 0) {
- source = 'attribute vec3 a_normal; \n' +
- source.replace(/gl_Normal/g, 'a_normal');
- }
- // fog
- if (source.indexOf('gl_FogFragCoord') >= 0) {
- source = 'varying float v_fogFragCoord; \n' +
- source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord');
- }
- source = ensurePrecision(source);
- } else { // Fragment shader
- for (i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
- 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;
- }
- }
- if (source.indexOf('gl_Color') >= 0) {
- source = 'varying vec4 v_color; \n' + source.replace(/gl_Color/g, 'v_color');
- }
- if (source.indexOf('gl_Fog.color') >= 0) {
- source = 'uniform vec4 u_fogColor; \n' +
- source.replace(/gl_Fog.color/g, 'u_fogColor');
- }
- if (source.indexOf('gl_Fog.end') >= 0) {
- source = 'uniform float u_fogEnd; \n' +
- source.replace(/gl_Fog.end/g, 'u_fogEnd');
- }
- if (source.indexOf('gl_Fog.scale') >= 0) {
- 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');
- }
- source = ensurePrecision(source);
- }
-#if GL_DEBUG
- GL.shaderSources[shader] = source;
- console.log("glShaderSource: Output: \n" + source);
-#endif
- GLctx.shaderSource(GL.shaders[shader], source);
- };
- {{{ updateExport('glShaderSource') }}}
-
- var glCompileShader = _glCompileShader;
- _glCompileShader = _emscripten_glCompileShader = function _glCompileShader(shader) {
- GLctx.compileShader(GL.shaders[shader]);
-#if GL_DEBUG
- if (!GLctx.getShaderParameter(GL.shaders[shader], GLctx.COMPILE_STATUS)) {
- err('Failed to compile shader: ' + GLctx.getShaderInfoLog(GL.shaders[shader]));
- err('Info: ' + JSON.stringify(GL.shaderInfos[shader]));
- err('Original source: ' + GL.shaderOriginalSources[shader]);
- err('Source: ' + GL.shaderSources[shader]);
- throw 'Shader compilation halt';
- }
-#endif
- };
- {{{ updateExport('glCompileShader') }}}
-
- GL.programShaders = {};
- var glAttachShader = _glAttachShader;
- _glAttachShader = _emscripten_glAttachShader = function _glAttachShader(program, shader) {
- if (!GL.programShaders[program]) GL.programShaders[program] = [];
- GL.programShaders[program].push(shader);
- glAttachShader(program, shader);
- };
- {{{ updateExport('glAttachShader') }}}
-
- var glDetachShader = _glDetachShader;
- _glDetachShader = _emscripten_glDetachShader = function _glDetachShader(program, shader) {
- var programShader = GL.programShaders[program];
- if (!programShader) {
- err('WARNING: _glDetachShader received invalid program: ' + program);
- return;
- }
- var index = programShader.indexOf(shader);
- programShader.splice(index, 1);
- glDetachShader(program, shader);
- };
- {{{ updateExport('glDetachShader') }}}
-
- var glUseProgram = _glUseProgram;
- _glUseProgram = _emscripten_glUseProgram = function _glUseProgram(program) {
-#if GL_DEBUG
- if (GL.debug) {
- err('[using program with shaders]');
- if (program) {
- GL.programShaders[program].forEach(function(shader) {
- err(' shader ' + shader + ', original source: ' + GL.shaderOriginalSources[shader]);
- err(' Source: ' + GL.shaderSources[shader]);
- });
- }
- }
-#endif
- if (GL.currProgram != program) {
- GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
- GL.currProgram = program;
- GLImmediate.fixedFunctionProgram = 0;
- glUseProgram(program);
- }
- }
- {{{ updateExport('glUseProgram') }}}
-
- var glDeleteProgram = _glDeleteProgram;
- _glDeleteProgram = _emscripten_glDeleteProgram = function _glDeleteProgram(program) {
- glDeleteProgram(program);
- if (program == GL.currProgram) {
- GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
- GL.currProgram = 0;
- }
- };
- {{{ updateExport('glDeleteProgram') }}}
-
- // If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that.
- var zeroUsedPrograms = {};
- var glBindAttribLocation = _glBindAttribLocation;
- _glBindAttribLocation = _emscripten_glBindAttribLocation = function _glBindAttribLocation(program, index, name) {
- if (index == 0) zeroUsedPrograms[program] = true;
- glBindAttribLocation(program, index, name);
- };
- {{{ updateExport('glBindAttribLocation') }}}
-
- var glLinkProgram = _glLinkProgram;
- _glLinkProgram = _emscripten_glLinkProgram = function _glLinkProgram(program) {
- if (!(program in zeroUsedPrograms)) {
- GLctx.bindAttribLocation(GL.programs[program], 0, 'a_position');
- }
- glLinkProgram(program);
- };
- {{{ updateExport('glLinkProgram') }}}
-
- var glBindBuffer = _glBindBuffer;
- _glBindBuffer = _emscripten_glBindBuffer = function _glBindBuffer(target, buffer) {
- glBindBuffer(target, buffer);
- if (target == GLctx.ARRAY_BUFFER) {
- if (GLEmulation.currentVao) {
-#if ASSERTIONS
- assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao');
-#endif
- GLEmulation.currentVao.arrayBuffer = buffer;
- }
- } else if (target == GLctx.ELEMENT_ARRAY_BUFFER) {
- if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer;
- }
- };
- {{{ updateExport('glBindBuffer') }}}
-
- var glGetFloatv = _glGetFloatv;
- _glGetFloatv = _emscripten_glGetFloatv = function _glGetFloatv(pname, params) {
- if (pname == 0x0BA6) { // GL_MODELVIEW_MATRIX
- HEAPF32.set(GLImmediate.matrix[0/*m*/], params >> 2);
- } else if (pname == 0x0BA7) { // GL_PROJECTION_MATRIX
- HEAPF32.set(GLImmediate.matrix[1/*p*/], params >> 2);
- } else if (pname == 0x0BA8) { // GL_TEXTURE_MATRIX
- HEAPF32.set(GLImmediate.matrix[2/*t*/ + GLImmediate.clientActiveTexture], params >> 2);
- } else if (pname == 0x0B66) { // GL_FOG_COLOR
- HEAPF32.set(GLEmulation.fogColor, params >> 2);
- } else if (pname == 0x0B63) { // GL_FOG_START
- {{{ 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);
- }
- };
- {{{ updateExport('glGetFloatv') }}}
-
- var glHint = _glHint;
- _glHint = _emscripten_glHint = function _glHint(target, mode) {
- if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT
- return;
- }
- glHint(target, mode);
- };
- {{{ updateExport('glHint') }}}
-
- var glEnableVertexAttribArray = _glEnableVertexAttribArray;
- _glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray = function _glEnableVertexAttribArray(index) {
- glEnableVertexAttribArray(index);
- GLEmulation.enabledVertexAttribArrays[index] = 1;
- if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
- };
- {{{ updateExport('glEnableVertexAttribArray') }}}
-
- var glDisableVertexAttribArray = _glDisableVertexAttribArray;
- _glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray = function _glDisableVertexAttribArray(index) {
- glDisableVertexAttribArray(index);
- delete GLEmulation.enabledVertexAttribArrays[index];
- if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
- };
- {{{ updateExport('glDisableVertexAttribArray') }}}
-
- var glVertexAttribPointer = _glVertexAttribPointer;
- _glVertexAttribPointer = _emscripten_glVertexAttribPointer = function _glVertexAttribPointer(index, size, type, normalized, stride, pointer) {
- glVertexAttribPointer(index, size, type, normalized, stride, pointer);
- if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though
- GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
- }
- };
- {{{ updateExport('glVertexAttribPointer') }}}
- },
-
- getAttributeFromCapability: function(cap) {
- var attrib = null;
- switch (cap) {
- case 0x0de1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it
-#if ASSERTIONS
- abort("GL_TEXTURE_2D is not a spec-defined capability for gl{Enable,Disable}ClientState.");
-#endif
- // Fall through:
- case 0x8078: // GL_TEXTURE_COORD_ARRAY
- attrib = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture; break;
- case 0x8074: // GL_VERTEX_ARRAY
- attrib = GLImmediate.VERTEX; break;
- case 0x8075: // GL_NORMAL_ARRAY
- attrib = GLImmediate.NORMAL; break;
- case 0x8076: // GL_COLOR_ARRAY
- attrib = GLImmediate.COLOR; break;
- }
- return attrib;
- },
- },
-
- glGetShaderPrecisionFormat__sig: 'v',
- glGetShaderPrecisionFormat: function() { throw 'glGetShaderPrecisionFormat: TODO' },
-
- glDeleteObject__deps: ['glDeleteProgram', 'glDeleteShader'],
- glDeleteObject__sig: 'vi',
- glDeleteObject: function(id) {
- if (GL.programs[id]) {
- _glDeleteProgram(id);
- } else if (GL.shaders[id]) {
- _glDeleteShader(id);
- } else {
- err('WARNING: deleteObject received invalid id: ' + id);
- }
- },
- glDeleteObjectARB: 'glDeleteObject',
-
- glGetObjectParameteriv__sig: 'viii',
- glGetObjectParameteriv__deps: ['glGetProgramiv', 'glGetShaderiv'],
- glGetObjectParameteriv: function(id, type, result) {
- if (GL.programs[id]) {
- if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
- var log = GLctx.getProgramInfoLog(GL.programs[id]);
- if (log === null) log = '(unknown error)';
- {{{ makeSetValue('result', '0', 'log.length', 'i32') }}};
- return;
- }
- _glGetProgramiv(id, type, result);
- } else if (GL.shaders[id]) {
- if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
- var log = GLctx.getShaderInfoLog(GL.shaders[id]);
- if (log === null) log = '(unknown error)';
- {{{ makeSetValue('result', '0', 'log.length', 'i32') }}};
- return;
- } else if (type == 0x8B88) { // GL_OBJECT_SHADER_SOURCE_LENGTH_ARB
- var source = GLctx.getShaderSource(GL.shaders[id]);
- if (source === null) return; // If an error occurs, nothing will be written to result
- {{{ makeSetValue('result', '0', 'source.length', 'i32') }}};
- return;
- }
- _glGetShaderiv(id, type, result);
- } else {
- err('WARNING: getObjectParameteriv received invalid id: ' + id);
- }
- },
- glGetObjectParameterivARB: 'glGetObjectParameteriv',
-
- glGetInfoLog__deps: ['glGetProgramInfoLog', 'glGetShaderInfoLog'],
- glGetInfoLog__sig: 'viiii',
- glGetInfoLog: 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 {
- err('WARNING: glGetInfoLog received invalid id: ' + id);
- }
- },
- glGetInfoLogARB: 'glGetInfoLog',
-
- glBindProgram__sig: 'vii',
- glBindProgram: function(type, id) {
-#if ASSERTIONS
- assert(id == 0);
-#endif
- },
- glBindProgramARB: 'glBindProgram',
-
- glGetPointerv: function(name, p) {
- var attribute;
- switch(name) {
- case 0x808E: // GL_VERTEX_ARRAY_POINTER
- attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break;
- case 0x8090: // GL_COLOR_ARRAY_POINTER
- attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
- case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
- attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break;
- default:
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- err('GL_INVALID_ENUM in glGetPointerv: Unsupported name ' + name + '!');
-#endif
- return;
- }
- {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
- },
-
- // GL Immediate mode
-
- // See comment in GLEmulation.init()
-#if !FULL_ES2
- $GLImmediate__postset: 'GLImmediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GLImmediate.init() });',
-#endif
- $GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'],
- $GLImmediate: {
- MapTreeLib: null,
- spawnMapTreeLib: function() {
- /* A naive implementation of a map backed by an array, and accessed by
- * naive iteration along the array. (hashmap with only one bucket)
- */
- function CNaiveListMap() {
- var list = [];
-
- this.insert = function CNaiveListMap_insert(key, val) {
- if (this.contains(key|0)) return false;
- list.push([key, val]);
- return true;
- };
-
- var __contains_i;
- this.contains = function CNaiveListMap_contains(key) {
- for (__contains_i = 0; __contains_i < list.length; ++__contains_i) {
- if (list[__contains_i][0] === key) return true;
- }
- return false;
- };
-
- var __get_i;
- this.get = function CNaiveListMap_get(key) {
- for (__get_i = 0; __get_i < list.length; ++__get_i) {
- if (list[__get_i][0] === key) return list[__get_i][1];
- }
- return undefined;
- };
- };
-
- /* A tree of map nodes.
- Uses `KeyView`s to allow descending the tree without garbage.
- Example: {
- // Create our map object.
- var map = new ObjTreeMap();
-
- // Grab the static keyView for the map.
- var keyView = map.GetStaticKeyView();
-
- // Let's make a map for:
- // root: <undefined>
- // 1: <undefined>
- // 2: <undefined>
- // 5: "Three, sir!"
- // 3: "Three!"
-
- // Note how we can chain together `Reset` and `Next` to
- // easily descend based on multiple key fragments.
- keyView.Reset().Next(1).Next(2).Next(5).Set("Three, sir!");
- keyView.Reset().Next(1).Next(2).Next(3).Set("Three!");
- }
- */
- function CMapTree() {
- function CNLNode() {
- var map = new CNaiveListMap();
-
- this.child = function CNLNode_child(keyFrag) {
- if (!map.contains(keyFrag|0)) {
- map.insert(keyFrag|0, new CNLNode());
- }
- return map.get(keyFrag|0);
- };
-
- this.value = undefined;
- this.get = function CNLNode_get() {
- return this.value;
- };
-
- this.set = function CNLNode_set(val) {
- this.value = val;
- };
- }
-
- function CKeyView(root) {
- var cur;
-
- this.reset = function CKeyView_reset() {
- cur = root;
- return this;
- };
- this.reset();
-
- this.next = function CKeyView_next(keyFrag) {
- cur = cur.child(keyFrag);
- return this;
- };
-
- this.get = function CKeyView_get() {
- return cur.get();
- };
-
- this.set = function CKeyView_set(val) {
- cur.set(val);
- };
- };
-
- var root;
- var staticKeyView;
-
- this.createKeyView = function CNLNode_createKeyView() {
- return new CKeyView(root);
- }
-
- this.clear = function CNLNode_clear() {
- root = new CNLNode();
- staticKeyView = this.createKeyView();
- };
- this.clear();
-
- this.getStaticKeyView = function CNLNode_getStaticKeyView() {
- staticKeyView.reset();
- return staticKeyView;
- };
- };
-
- // Exports:
- return {
- create: function() {
- return new CMapTree();
- },
- };
- },
-
- TexEnvJIT: null,
- spawnTexEnvJIT: function() {
- // GL defs:
- var GL_TEXTURE0 = 0x84C0;
- var GL_TEXTURE_1D = 0x0DE0;
- var GL_TEXTURE_2D = 0x0DE1;
- var GL_TEXTURE_3D = 0x806f;
- var GL_TEXTURE_CUBE_MAP = 0x8513;
- var GL_TEXTURE_ENV = 0x2300;
- var GL_TEXTURE_ENV_MODE = 0x2200;
- var GL_TEXTURE_ENV_COLOR = 0x2201;
- var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
- var GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
- var GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
- var GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
- var GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
- var GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
-
- var GL_SRC0_RGB = 0x8580;
- var GL_SRC1_RGB = 0x8581;
- var GL_SRC2_RGB = 0x8582;
-
- var GL_SRC0_ALPHA = 0x8588;
- var GL_SRC1_ALPHA = 0x8589;
- var GL_SRC2_ALPHA = 0x858A;
-
- var GL_OPERAND0_RGB = 0x8590;
- var GL_OPERAND1_RGB = 0x8591;
- var GL_OPERAND2_RGB = 0x8592;
-
- var GL_OPERAND0_ALPHA = 0x8598;
- var GL_OPERAND1_ALPHA = 0x8599;
- var GL_OPERAND2_ALPHA = 0x859A;
-
- var GL_COMBINE_RGB = 0x8571;
- var GL_COMBINE_ALPHA = 0x8572;
-
- var GL_RGB_SCALE = 0x8573;
- var GL_ALPHA_SCALE = 0x0D1C;
-
- // env.mode
- var GL_ADD = 0x0104;
- var GL_BLEND = 0x0BE2;
- var GL_REPLACE = 0x1E01;
- var GL_MODULATE = 0x2100;
- var GL_DECAL = 0x2101;
- var GL_COMBINE = 0x8570;
-
- // env.color/alphaCombiner
- //var GL_ADD = 0x0104;
- //var GL_REPLACE = 0x1E01;
- //var GL_MODULATE = 0x2100;
- var GL_SUBTRACT = 0x84E7;
- var GL_INTERPOLATE = 0x8575;
-
- // env.color/alphaSrc
- var GL_TEXTURE = 0x1702;
- var GL_CONSTANT = 0x8576;
- var GL_PRIMARY_COLOR = 0x8577;
- var GL_PREVIOUS = 0x8578;
-
- // env.color/alphaOp
- var GL_SRC_COLOR = 0x0300;
- var GL_ONE_MINUS_SRC_COLOR = 0x0301;
- var GL_SRC_ALPHA = 0x0302;
- var GL_ONE_MINUS_SRC_ALPHA = 0x0303;
-
- var GL_RGB = 0x1907;
- var GL_RGBA = 0x1908;
-
- // Our defs:
- var TEXENVJIT_NAMESPACE_PREFIX = "tej_";
- // Not actually constant, as they can be changed between JIT passes:
- var TEX_UNIT_UNIFORM_PREFIX = "uTexUnit";
- var TEX_COORD_VARYING_PREFIX = "vTexCoord";
- var PRIM_COLOR_VARYING = "vPrimColor";
- var TEX_MATRIX_UNIFORM_PREFIX = "uTexMatrix";
-
- // Static vars:
- var s_texUnits = null; //[];
- var s_activeTexture = 0;
-
- var s_requiredTexUnitsForPass = [];
-
- // Static funcs:
- function abort(info) {
- assert(false, "[TexEnvJIT] ABORT: " + info);
- }
-
- function abort_noSupport(info) {
- abort("No support: " + info);
- }
-
- function abort_sanity(info) {
- abort("Sanity failure: " + info);
- }
-
- function genTexUnitSampleExpr(texUnitID) {
- var texUnit = s_texUnits[texUnitID];
- var texType = texUnit.getTexType();
-
- var func = null;
- switch (texType) {
- case GL_TEXTURE_1D:
- func = "texture2D";
- break;
- case GL_TEXTURE_2D:
- func = "texture2D";
- break;
- case GL_TEXTURE_3D:
- return abort_noSupport("No support for 3D textures.");
- case GL_TEXTURE_CUBE_MAP:
- func = "textureCube";
- break;
- default:
- return abort_sanity("Unknown texType: 0x" + texType.toString(16));
- }
-
- var texCoordExpr = TEX_COORD_VARYING_PREFIX + texUnitID;
- if (TEX_MATRIX_UNIFORM_PREFIX != null) {
- texCoordExpr = "(" + TEX_MATRIX_UNIFORM_PREFIX + texUnitID + " * " + texCoordExpr + ")";
- }
- return func + "(" + TEX_UNIT_UNIFORM_PREFIX + texUnitID + ", " + texCoordExpr + ".xy)";
- }
-
- function getTypeFromCombineOp(op) {
- switch (op) {
- case GL_SRC_COLOR:
- case GL_ONE_MINUS_SRC_COLOR:
- return "vec3";
- case GL_SRC_ALPHA:
- case GL_ONE_MINUS_SRC_ALPHA:
- return "float";
- }
-
- return abort_noSupport("Unsupported combiner op: 0x" + op.toString(16));
- }
-
- function getCurTexUnit() {
- return s_texUnits[s_activeTexture];
- }
-
- function genCombinerSourceExpr(texUnitID, constantExpr, previousVar,
- src, op)
- {
- var srcExpr = null;
- switch (src) {
- case GL_TEXTURE:
- srcExpr = genTexUnitSampleExpr(texUnitID);
- break;
- case GL_CONSTANT:
- srcExpr = constantExpr;
- break;
- case GL_PRIMARY_COLOR:
- srcExpr = PRIM_COLOR_VARYING;
- break;
- case GL_PREVIOUS:
- srcExpr = previousVar;
- break;
- default:
- return abort_noSupport("Unsupported combiner src: 0x" + src.toString(16));
- }
-
- var expr = null;
- switch (op) {
- case GL_SRC_COLOR:
- expr = srcExpr + ".rgb";
- break;
- case GL_ONE_MINUS_SRC_COLOR:
- expr = "(vec3(1.0) - " + srcExpr + ".rgb)";
- break;
- case GL_SRC_ALPHA:
- expr = srcExpr + ".a";
- break;
- case GL_ONE_MINUS_SRC_ALPHA:
- expr = "(1.0 - " + srcExpr + ".a)";
- break;
- default:
- return abort_noSupport("Unsupported combiner op: 0x" + op.toString(16));
- }
-
- return expr;
- }
-
- function valToFloatLiteral(val) {
- if (val == Math.round(val)) return val + '.0';
- return val;
- }
-
-
- // Classes:
- function CTexEnv() {
- this.mode = GL_MODULATE;
- this.colorCombiner = GL_MODULATE;
- this.alphaCombiner = GL_MODULATE;
- this.colorScale = 1;
- this.alphaScale = 1;
- this.envColor = [0, 0, 0, 0];
-
- this.colorSrc = [
- GL_TEXTURE,
- GL_PREVIOUS,
- GL_CONSTANT
- ];
- this.alphaSrc = [
- GL_TEXTURE,
- GL_PREVIOUS,
- GL_CONSTANT
- ];
- this.colorOp = [
- GL_SRC_COLOR,
- GL_SRC_COLOR,
- GL_SRC_ALPHA
- ];
- this.alphaOp = [
- GL_SRC_ALPHA,
- GL_SRC_ALPHA,
- GL_SRC_ALPHA
- ];
-
- // Map GLenums to small values to efficiently pack the enums to bits for tighter access.
- this.traverseKey = {
- // mode
- 0x1E01 /* GL_REPLACE */: 0,
- 0x2100 /* GL_MODULATE */: 1,
- 0x0104 /* GL_ADD */: 2,
- 0x0BE2 /* GL_BLEND */: 3,
- 0x2101 /* GL_DECAL */: 4,
- 0x8570 /* GL_COMBINE */: 5,
-
- // additional color and alpha combiners
- 0x84E7 /* GL_SUBTRACT */: 3,
- 0x8575 /* GL_INTERPOLATE */: 4,
-
- // color and alpha src
- 0x1702 /* GL_TEXTURE */: 0,
- 0x8576 /* GL_CONSTANT */: 1,
- 0x8577 /* GL_PRIMARY_COLOR */: 2,
- 0x8578 /* GL_PREVIOUS */: 3,
-
- // color and alpha op
- 0x0300 /* GL_SRC_COLOR */: 0,
- 0x0301 /* GL_ONE_MINUS_SRC_COLOR */: 1,
- 0x0302 /* GL_SRC_ALPHA */: 2,
- 0x0303 /* GL_ONE_MINUS_SRC_ALPHA */: 3
- };
-
- // The tuple (key0,key1,key2) uniquely identifies the state of the variables in CTexEnv.
- // -1 on key0 denotes 'the whole cached key is dirty'
- this.key0 = -1;
- this.key1 = 0;
- this.key2 = 0;
-
- this.computeKey0 = function() {
- var k = this.traverseKey;
- var key = k[this.mode] * 1638400; // 6 distinct values.
- key += k[this.colorCombiner] * 327680; // 5 distinct values.
- key += k[this.alphaCombiner] * 65536; // 5 distinct values.
- // The above three fields have 6*5*5=150 distinct values -> 8 bits.
- key += (this.colorScale-1) * 16384; // 10 bits used.
- key += (this.alphaScale-1) * 4096; // 12 bits used.
- key += k[this.colorSrc[0]] * 1024; // 14
- key += k[this.colorSrc[1]] * 256; // 16
- key += k[this.colorSrc[2]] * 64; // 18
- key += k[this.alphaSrc[0]] * 16; // 20
- key += k[this.alphaSrc[1]] * 4; // 22
- key += k[this.alphaSrc[2]]; // 24 bits used total.
- return key;
- }
- this.computeKey1 = function() {
- var k = this.traverseKey;
- key = k[this.colorOp[0]] * 4096;
- key += k[this.colorOp[1]] * 1024;
- key += k[this.colorOp[2]] * 256;
- key += k[this.alphaOp[0]] * 16;
- key += k[this.alphaOp[1]] * 4;
- key += k[this.alphaOp[2]];
- return key;
- }
- // TODO: remove this. The color should not be part of the key!
- this.computeKey2 = function() {
- return this.envColor[0] * 16777216 + this.envColor[1] * 65536 + this.envColor[2] * 256 + 1 + this.envColor[3];
- }
- this.recomputeKey = function() {
- this.key0 = this.computeKey0();
- this.key1 = this.computeKey1();
- this.key2 = this.computeKey2();
- }
- this.invalidateKey = function() {
- this.key0 = -1; // The key of this texture unit must be recomputed when rendering the next time.
- GLImmediate.currentRenderer = null; // The currently used renderer must be re-evaluated at next render.
- }
- }
-
- function CTexUnit() {
- this.env = new CTexEnv();
- this.enabled_tex1D = false;
- this.enabled_tex2D = false;
- this.enabled_tex3D = false;
- this.enabled_texCube = false;
- this.texTypesEnabled = 0; // A bitfield combination of the four flags above, used for fast access to operations.
-
- this.traverseState = function CTexUnit_traverseState(keyView) {
- if (this.texTypesEnabled) {
- if (this.env.key0 == -1) {
- this.env.recomputeKey();
- }
- keyView.next(this.texTypesEnabled | (this.env.key0 << 4));
- keyView.next(this.env.key1);
- keyView.next(this.env.key2);
- } else {
- // For correctness, must traverse a zero value, theoretically a subsequent integer key could collide with this value otherwise.
- keyView.next(0);
- }
- };
- };
-
- // Class impls:
- CTexUnit.prototype.enabled = function CTexUnit_enabled() {
- return this.texTypesEnabled;
- }
-
- CTexUnit.prototype.genPassLines = function CTexUnit_genPassLines(passOutputVar, passInputVar, texUnitID) {
- if (!this.enabled()) {
- return ["vec4 " + passOutputVar + " = " + passInputVar + ";"];
- }
- var lines = this.env.genPassLines(passOutputVar, passInputVar, texUnitID).join('\n');
-
- var texLoadLines = '';
- var texLoadRegex = /(texture.*?\(.*?\))/g;
- var loadCounter = 0;
- var load;
-
- // As an optimization, merge duplicate identical texture loads to one var.
- while(load = texLoadRegex.exec(lines)) {
- var texLoadExpr = load[1];
- var secondOccurrence = lines.slice(load.index+1).indexOf(texLoadExpr);
- if (secondOccurrence != -1) { // And also has a second occurrence of same load expression..
- // Create new var to store the common load.
- var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
- var texLoadVar = prefix + 'texload' + loadCounter++;
- var texLoadLine = 'vec4 ' + texLoadVar + ' = ' + texLoadExpr + ';\n';
- texLoadLines += texLoadLine + '\n'; // Store the generated texture load statements in a temp string to not confuse regex search in progress.
- lines = lines.split(texLoadExpr).join(texLoadVar);
- // Reset regex search, since we modified the string.
- texLoadRegex = /(texture.*\(.*\))/g;
- }
- }
- return [texLoadLines + lines];
- }
-
- CTexUnit.prototype.getTexType = function CTexUnit_getTexType() {
- if (this.enabled_texCube) {
- return GL_TEXTURE_CUBE_MAP;
- } else if (this.enabled_tex3D) {
- return GL_TEXTURE_3D;
- } else if (this.enabled_tex2D) {
- return GL_TEXTURE_2D;
- } else if (this.enabled_tex1D) {
- return GL_TEXTURE_1D;
- }
- return 0;
- }
-
- CTexEnv.prototype.genPassLines = function CTexEnv_genPassLines(passOutputVar, passInputVar, texUnitID) {
- switch (this.mode) {
- case GL_REPLACE: {
- /* RGB:
- * Cv = Cs
- * Av = Ap // Note how this is different, and that we'll
- * need to track the bound texture internalFormat
- * to get this right.
- *
- * RGBA:
- * Cv = Cs
- * Av = As
- */
- return [
- "vec4 " + passOutputVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
- ];
- }
- case GL_ADD: {
- /* RGBA:
- * Cv = Cp + Cs
- * Av = ApAs
- */
- var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
- var texVar = prefix + "tex";
- var colorVar = prefix + "color";
- var alphaVar = prefix + "alpha";
-
- return [
- "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
- "vec3 " + colorVar + " = " + passInputVar + ".rgb + " + texVar + ".rgb;",
- "float " + alphaVar + " = " + passInputVar + ".a * " + texVar + ".a;",
- "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
- ];
- }
- case GL_MODULATE: {
- /* RGBA:
- * Cv = CpCs
- * Av = ApAs
- */
- var line = [
- "vec4 " + passOutputVar,
- " = ",
- passInputVar,
- " * ",
- genTexUnitSampleExpr(texUnitID),
- ";",
- ];
- return [line.join("")];
- }
- case GL_DECAL: {
- /* RGBA:
- * Cv = Cp(1 - As) + CsAs
- * Av = Ap
- */
- var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
- var texVar = prefix + "tex";
- var colorVar = prefix + "color";
- var alphaVar = prefix + "alpha";
-
- return [
- "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
- [
- "vec3 " + colorVar + " = ",
- passInputVar + ".rgb * (1.0 - " + texVar + ".a)",
- " + ",
- texVar + ".rgb * " + texVar + ".a",
- ";"
- ].join(""),
- "float " + alphaVar + " = " + passInputVar + ".a;",
- "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
- ];
- }
- case GL_BLEND: {
- /* RGBA:
- * Cv = Cp(1 - Cs) + CcCs
- * Av = As
- */
- var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
- var texVar = prefix + "tex";
- var colorVar = prefix + "color";
- var alphaVar = prefix + "alpha";
-
- return [
- "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
- [
- "vec3 " + colorVar + " = ",
- passInputVar + ".rgb * (1.0 - " + texVar + ".rgb)",
- " + ",
- PRIM_COLOR_VARYING + ".rgb * " + texVar + ".rgb",
- ";"
- ].join(""),
- "float " + alphaVar + " = " + texVar + ".a;",
- "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
- ];
- }
- case GL_COMBINE: {
- var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
- var colorVar = prefix + "color";
- var alphaVar = prefix + "alpha";
- var colorLines = this.genCombinerLines(true, colorVar,
- passInputVar, texUnitID,
- this.colorCombiner, this.colorSrc, this.colorOp);
- var alphaLines = this.genCombinerLines(false, alphaVar,
- passInputVar, texUnitID,
- this.alphaCombiner, this.alphaSrc, this.alphaOp);
-
- // Generate scale, but avoid generating an identity op that multiplies by one.
- var scaledColor = (this.colorScale == 1) ? colorVar : (colorVar + " * " + valToFloatLiteral(this.colorScale));
- var scaledAlpha = (this.alphaScale == 1) ? alphaVar : (alphaVar + " * " + valToFloatLiteral(this.alphaScale));
-
- var line = [
- "vec4 " + passOutputVar,
- " = ",
- "vec4(",
- scaledColor,
- ", ",
- scaledAlpha,
- ")",
- ";",
- ].join("");
- return [].concat(colorLines, alphaLines, [line]);
- }
- }
-
- return abort_noSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16));
- }
-
- CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar,
- passInputVar, texUnitID,
- combiner, srcArr, opArr)
- {
- var argsNeeded = null;
- switch (combiner) {
- case GL_REPLACE:
- argsNeeded = 1;
- break;
-
- case GL_MODULATE:
- case GL_ADD:
- case GL_SUBTRACT:
- argsNeeded = 2;
- break;
-
- case GL_INTERPOLATE:
- argsNeeded = 3;
- break;
-
- default:
- return abort_noSupport("Unsupported combiner: 0x" + combiner.toString(16));
- }
-
- var constantExpr = [
- "vec4(",
- valToFloatLiteral(this.envColor[0]),
- ", ",
- valToFloatLiteral(this.envColor[1]),
- ", ",
- valToFloatLiteral(this.envColor[2]),
- ", ",
- valToFloatLiteral(this.envColor[3]),
- ")",
- ].join("");
- var src0Expr = (argsNeeded >= 1) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[0], opArr[0])
- : null;
- var src1Expr = (argsNeeded >= 2) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[1], opArr[1])
- : null;
- var src2Expr = (argsNeeded >= 3) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[2], opArr[2])
- : null;
-
- var outputType = isColor ? "vec3" : "float";
- var lines = null;
- switch (combiner) {
- case GL_REPLACE: {
- var line = [
- outputType + " " + outputVar,
- " = ",
- src0Expr,
- ";",
- ];
- lines = [line.join("")];
- break;
- }
- case GL_MODULATE: {
- var line = [
- outputType + " " + outputVar + " = ",
- src0Expr + " * " + src1Expr,
- ";",
- ];
- lines = [line.join("")];
- break;
- }
- case GL_ADD: {
- var line = [
- outputType + " " + outputVar + " = ",
- src0Expr + " + " + src1Expr,
- ";",
- ];
- lines = [line.join("")];
- break;
- }
- case GL_SUBTRACT: {
- var line = [
- outputType + " " + outputVar + " = ",
- src0Expr + " - " + src1Expr,
- ";",
- ];
- lines = [line.join("")];
- break;
- }
- case GL_INTERPOLATE: {
- var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
- var arg2Var = prefix + "colorSrc2";
- var arg2Line = getTypeFromCombineOp(this.colorOp[2]) + " " + arg2Var + " = " + src2Expr + ";";
-
- var line = [
- outputType + " " + outputVar,
- " = ",
- src0Expr + " * " + arg2Var,
- " + ",
- src1Expr + " * (1.0 - " + arg2Var + ")",
- ";",
- ];
- lines = [
- arg2Line,
- line.join(""),
- ];
- break;
- }
-
- default:
- return abort_sanity("Unmatched TexEnv.colorCombiner?");
- }
-
- return lines;
- }
-
- return {
- // Exports:
- init: function(gl, specifiedMaxTextureImageUnits) {
- var maxTexUnits = 0;
- if (specifiedMaxTextureImageUnits) {
- maxTexUnits = specifiedMaxTextureImageUnits;
- } else if (gl) {
- maxTexUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
- }
-#if ASSERTIONS
- assert(maxTexUnits > 0);
-#endif
- s_texUnits = [];
- for (var i = 0; i < maxTexUnits; i++) {
- s_texUnits.push(new CTexUnit());
- }
- },
-
- setGLSLVars: function(uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix) {
- TEX_UNIT_UNIFORM_PREFIX = uTexUnitPrefix;
- TEX_COORD_VARYING_PREFIX = vTexCoordPrefix;
- PRIM_COLOR_VARYING = vPrimColor;
- TEX_MATRIX_UNIFORM_PREFIX = uTexMatrixPrefix;
- },
-
- genAllPassLines: function(resultDest, indentSize) {
- indentSize = indentSize || 0;
-
- s_requiredTexUnitsForPass.length = 0; // Clear the list.
- var lines = [];
- var lastPassVar = PRIM_COLOR_VARYING;
- for (var i = 0; i < s_texUnits.length; i++) {
- if (!s_texUnits[i].enabled()) continue;
-
- s_requiredTexUnitsForPass.push(i);
-
- var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + i + "_";
- var passOutputVar = prefix + "result";
-
- var newLines = s_texUnits[i].genPassLines(passOutputVar, lastPassVar, i);
- lines = lines.concat(newLines, [""]);
-
- lastPassVar = passOutputVar;
- }
- lines.push(resultDest + " = " + lastPassVar + ";");
-
- var indent = "";
- for (var i = 0; i < indentSize; i++) indent += " ";
-
- var output = indent + lines.join("\n" + indent);
-
- return output;
- },
-
- getUsedTexUnitList: function() {
- return s_requiredTexUnitsForPass;
- },
-
- traverseState: function(keyView) {
- for (var i = 0; i < s_texUnits.length; i++) {
- s_texUnits[i].traverseState(keyView);
- }
- },
-
- getTexUnitType: function(texUnitID) {
-#if ASSERTIONS
- assert(texUnitID >= 0 &&
- texUnitID < s_texUnits.length);
-#endif
- return s_texUnits[texUnitID].getTexType();
- },
-
- // Hooks:
- hook_activeTexture: function(texture) {
- s_activeTexture = texture - GL_TEXTURE0;
- },
-
- hook_enable: function(cap) {
- var cur = getCurTexUnit();
- switch (cap) {
- case GL_TEXTURE_1D:
- if (!cur.enabled_tex1D) {
- GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again.
- cur.enabled_tex1D = true;
- cur.texTypesEnabled |= 1;
- }
- break;
- case GL_TEXTURE_2D:
- if (!cur.enabled_tex2D) {
- GLImmediate.currentRenderer = null;
- cur.enabled_tex2D = true;
- cur.texTypesEnabled |= 2;
- }
- break;
- case GL_TEXTURE_3D:
- if (!cur.enabled_tex3D) {
- GLImmediate.currentRenderer = null;
- cur.enabled_tex3D = true;
- cur.texTypesEnabled |= 4;
- }
- break;
- case GL_TEXTURE_CUBE_MAP:
- if (!cur.enabled_texCube) {
- GLImmediate.currentRenderer = null;
- cur.enabled_texCube = true;
- cur.texTypesEnabled |= 8;
- }
- break;
- }
- },
-
- hook_disable: function(cap) {
- var cur = getCurTexUnit();
- switch (cap) {
- case GL_TEXTURE_1D:
- if (cur.enabled_tex1D) {
- GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again.
- cur.enabled_tex1D = false;
- cur.texTypesEnabled &= ~1;
- }
- break;
- case GL_TEXTURE_2D:
- if (cur.enabled_tex2D) {
- GLImmediate.currentRenderer = null;
- cur.enabled_tex2D = false;
- cur.texTypesEnabled &= ~2;
- }
- break;
- case GL_TEXTURE_3D:
- if (cur.enabled_tex3D) {
- GLImmediate.currentRenderer = null;
- cur.enabled_tex3D = false;
- cur.texTypesEnabled &= ~4;
- }
- break;
- case GL_TEXTURE_CUBE_MAP:
- if (cur.enabled_texCube) {
- GLImmediate.currentRenderer = null;
- cur.enabled_texCube = false;
- cur.texTypesEnabled &= ~8;
- }
- break;
- }
- },
-
- hook_texEnvf: function(target, pname, param) {
- if (target != GL_TEXTURE_ENV)
- return;
-
- var env = getCurTexUnit().env;
- switch (pname) {
- case GL_RGB_SCALE:
- if (env.colorScale != param) {
- env.invalidateKey(); // We changed FFP emulation renderer state.
- env.colorScale = param;
- }
- break;
- case GL_ALPHA_SCALE:
- if (env.alphaScale != param) {
- env.invalidateKey();
- env.alphaScale = param;
- }
- break;
-
- default:
- err('WARNING: Unhandled `pname` in call to `glTexEnvf`.');
- }
- },
-
- hook_texEnvi: function(target, pname, param) {
- if (target != GL_TEXTURE_ENV)
- return;
-
- var env = getCurTexUnit().env;
- switch (pname) {
- case GL_TEXTURE_ENV_MODE:
- if (env.mode != param) {
- env.invalidateKey(); // We changed FFP emulation renderer state.
- env.mode = param;
- }
- break;
-
- case GL_COMBINE_RGB:
- if (env.colorCombiner != param) {
- env.invalidateKey();
- env.colorCombiner = param;
- }
- break;
- case GL_COMBINE_ALPHA:
- if (env.alphaCombiner != param) {
- env.invalidateKey();
- env.alphaCombiner = param;
- }
- break;
-
- case GL_SRC0_RGB:
- if (env.colorSrc[0] != param) {
- env.invalidateKey();
- env.colorSrc[0] = param;
- }
- break;
- case GL_SRC1_RGB:
- if (env.colorSrc[1] != param) {
- env.invalidateKey();
- env.colorSrc[1] = param;
- }
- break;
- case GL_SRC2_RGB:
- if (env.colorSrc[2] != param) {
- env.invalidateKey();
- env.colorSrc[2] = param;
- }
- break;
-
- case GL_SRC0_ALPHA:
- if (env.alphaSrc[0] != param) {
- env.invalidateKey();
- env.alphaSrc[0] = param;
- }
- break;
- case GL_SRC1_ALPHA:
- if (env.alphaSrc[1] != param) {
- env.invalidateKey();
- env.alphaSrc[1] = param;
- }
- break;
- case GL_SRC2_ALPHA:
- if (env.alphaSrc[2] != param) {
- env.invalidateKey();
- env.alphaSrc[2] = param;
- }
- break;
-
- case GL_OPERAND0_RGB:
- if (env.colorOp[0] != param) {
- env.invalidateKey();
- env.colorOp[0] = param;
- }
- break;
- case GL_OPERAND1_RGB:
- if (env.colorOp[1] != param) {
- env.invalidateKey();
- env.colorOp[1] = param;
- }
- break;
- case GL_OPERAND2_RGB:
- if (env.colorOp[2] != param) {
- env.invalidateKey();
- env.colorOp[2] = param;
- }
- break;
-
- case GL_OPERAND0_ALPHA:
- if (env.alphaOp[0] != param) {
- env.invalidateKey();
- env.alphaOp[0] = param;
- }
- break;
- case GL_OPERAND1_ALPHA:
- if (env.alphaOp[1] != param) {
- env.invalidateKey();
- env.alphaOp[1] = param;
- }
- break;
- case GL_OPERAND2_ALPHA:
- if (env.alphaOp[2] != param) {
- env.invalidateKey();
- env.alphaOp[2] = param;
- }
- break;
-
- case GL_RGB_SCALE:
- if (env.colorScale != param) {
- env.invalidateKey();
- env.colorScale = param;
- }
- break;
- case GL_ALPHA_SCALE:
- if (env.alphaScale != param) {
- env.invalidateKey();
- env.alphaScale = param;
- }
- break;
-
- default:
- err('WARNING: Unhandled `pname` in call to `glTexEnvi`.');
- }
- },
-
- hook_texEnvfv: function(target, pname, params) {
- if (target != GL_TEXTURE_ENV) return;
-
- var env = getCurTexUnit().env;
- switch (pname) {
- case GL_TEXTURE_ENV_COLOR: {
- for (var i = 0; i < 4; i++) {
- var param = {{{ makeGetValue('params', 'i*4', 'float') }}};
- if (env.envColor[i] != param) {
- env.invalidateKey(); // We changed FFP emulation renderer state.
- env.envColor[i] = param;
- }
- }
- break
- }
- default:
- err('WARNING: Unhandled `pname` in call to `glTexEnvfv`.');
- }
- },
-
- hook_getTexEnviv: function(target, pname, param) {
- if (target != GL_TEXTURE_ENV)
- return;
-
- var env = getCurTexUnit().env;
- switch (pname) {
- case GL_TEXTURE_ENV_MODE:
- {{{ makeSetValue('param', '0', 'env.mode', 'i32') }}};
- return;
-
- case GL_TEXTURE_ENV_COLOR:
- {{{ makeSetValue('param', '0', 'Math.max(Math.min(env.envColor[0]*255, 255, -255))', 'i32') }}};
- {{{ makeSetValue('param', '1', 'Math.max(Math.min(env.envColor[1]*255, 255, -255))', 'i32') }}};
- {{{ makeSetValue('param', '2', 'Math.max(Math.min(env.envColor[2]*255, 255, -255))', 'i32') }}};
- {{{ makeSetValue('param', '3', 'Math.max(Math.min(env.envColor[3]*255, 255, -255))', 'i32') }}};
- return;
-
- case GL_COMBINE_RGB:
- {{{ makeSetValue('param', '0', 'env.colorCombiner', 'i32') }}};
- return;
-
- case GL_COMBINE_ALPHA:
- {{{ makeSetValue('param', '0', 'env.alphaCombiner', 'i32') }}};
- return;
-
- case GL_SRC0_RGB:
- {{{ makeSetValue('param', '0', 'env.colorSrc[0]', 'i32') }}};
- return;
-
- case GL_SRC1_RGB:
- {{{ makeSetValue('param', '0', 'env.colorSrc[1]', 'i32') }}};
- return;
-
- case GL_SRC2_RGB:
- {{{ makeSetValue('param', '0', 'env.colorSrc[2]', 'i32') }}};
- return;
-
- case GL_SRC0_ALPHA:
- {{{ makeSetValue('param', '0', 'env.alphaSrc[0]', 'i32') }}};
- return;
-
- case GL_SRC1_ALPHA:
- {{{ makeSetValue('param', '0', 'env.alphaSrc[1]', 'i32') }}};
- return;
-
- case GL_SRC2_ALPHA:
- {{{ makeSetValue('param', '0', 'env.alphaSrc[2]', 'i32') }}};
- return;
-
- case GL_OPERAND0_RGB:
- {{{ makeSetValue('param', '0', 'env.colorOp[0]', 'i32') }}};
- return;
-
- case GL_OPERAND1_RGB:
- {{{ makeSetValue('param', '0', 'env.colorOp[1]', 'i32') }}};
- return;
-
- case GL_OPERAND2_RGB:
- {{{ makeSetValue('param', '0', 'env.colorOp[2]', 'i32') }}};
- return;
-
- case GL_OPERAND0_ALPHA:
- {{{ makeSetValue('param', '0', 'env.alphaOp[0]', 'i32') }}};
- return;
-
- case GL_OPERAND1_ALPHA:
- {{{ makeSetValue('param', '0', 'env.alphaOp[1]', 'i32') }}};
- return;
-
- case GL_OPERAND2_ALPHA:
- {{{ makeSetValue('param', '0', 'env.alphaOp[2]', 'i32') }}};
- return;
-
- case GL_RGB_SCALE:
- {{{ makeSetValue('param', '0', 'env.colorScale', 'i32') }}};
- return;
-
- case GL_ALPHA_SCALE:
- {{{ makeSetValue('param', '0', 'env.alphaScale', 'i32') }}};
- return;
-
- default:
- err('WARNING: Unhandled `pname` in call to `glGetTexEnvi`.');
- }
- },
-
- hook_getTexEnvfv: function(target, pname, param) {
- if (target != GL_TEXTURE_ENV)
- return;
-
- var env = getCurTexUnit().env;
- switch (pname) {
- case GL_TEXTURE_ENV_COLOR:
- {{{ makeSetValue('param', '0', 'env.envColor[0]', 'float') }}};
- {{{ makeSetValue('param', '4', 'env.envColor[1]', 'float') }}};
- {{{ makeSetValue('param', '8', 'env.envColor[2]', 'float') }}};
- {{{ makeSetValue('param', '12', 'env.envColor[3]', 'float') }}};
- return;
- }
- }
- };
- },
-
- // Vertex and index data
- 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: -1,
-
- rendererCache: null,
- 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
- lastRenderer: null, // used to avoid cleaning up and re-preparing the same renderer
- lastArrayBuffer: null, // used in conjunction with lastRenderer
- lastProgram: null, // ""
- lastStride: -1, // ""
-
- // The following data structures are used for OpenGL Immediate Mode matrix routines.
- matrix: [],
- matrixStack: [],
- currentMatrix: 0, // 0: modelview, 1: projection, 2+i, texture matrix i.
- tempMatrix: null,
- matricesModified: false,
- useTextureMatrix: false,
-
- // Clientside attributes
- VERTEX: 0,
- NORMAL: 1,
- COLOR: 2,
- TEXTURE0: 3,
- NUM_ATTRIBUTES: -1, // Initialized in GL emulation init().
- MAX_TEXTURES: -1, // Initialized in GL emulation init().
-
- totalEnabledClientAttributes: 0,
- enabledClientAttributes: [0, 0],
- clientAttributes: [], // raw data, including possible unneeded ones
- liveClientAttributes: [], // the ones actually alive in the current computation, sorted
- currentRenderer: null, // Caches the currently active FFP emulation renderer, so that it does not have to be re-looked up unless relevant state changes.
- modifiedClientAttributes: false,
- clientActiveTexture: 0,
- clientColor: null,
- usedTexUnitList: [],
- fixedFunctionProgram: null,
-
- setClientAttribute: function setClientAttribute(name, size, type, stride, pointer) {
- var attrib = GLImmediate.clientAttributes[name];
- if (!attrib) {
- for (var i = 0; i <= name; i++) { // keep flat
- if (!GLImmediate.clientAttributes[i]) {
- GLImmediate.clientAttributes[i] = {
- name: name,
- size: size,
- type: type,
- stride: stride,
- pointer: pointer,
- offset: 0
- };
- }
- }
- } else {
- attrib.name = name;
- attrib.size = size;
- attrib.type = type;
- attrib.stride = stride;
- attrib.pointer = pointer;
- attrib.offset = 0;
- }
- GLImmediate.modifiedClientAttributes = true;
- },
-
- // Renderers
- addRendererComponent: function addRendererComponent(name, size, type) {
- if (!GLImmediate.rendererComponents[name]) {
- GLImmediate.rendererComponents[name] = 1;
-#if ASSERTIONS
- if (GLImmediate.enabledClientAttributes[name]) {
- console.log("Warning: glTexCoord used after EnableClientState for TEXTURE_COORD_ARRAY for TEXTURE0. Disabling TEXTURE_COORD_ARRAY...");
- }
-#endif
- GLImmediate.enabledClientAttributes[name] = true;
- GLImmediate.setClientAttribute(name, size, type, 0, GLImmediate.rendererComponentPointer);
- GLImmediate.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot];
-#if GL_FFP_ONLY
- // We can enable the correct attribute stream index immediately here, since the same attribute in each shader
- // will be bound to this same index.
- GL.enableVertexAttribArray(name);
-#endif
- } else {
- GLImmediate.rendererComponents[name]++;
- }
- },
-
- disableBeginEndClientAttributes: function disableBeginEndClientAttributes() {
- for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
- if (GLImmediate.rendererComponents[i]) GLImmediate.enabledClientAttributes[i] = false;
- }
- },
-
- getRenderer: function getRenderer() {
- // If no FFP state has changed that would have forced to re-evaluate which FFP emulation shader to use,
- // we have the currently used renderer in cache, and can immediately return that.
- if (GLImmediate.currentRenderer) {
- return GLImmediate.currentRenderer;
- }
- // return a renderer object given the liveClientAttributes
- // we maintain a cache of renderers, optimized to not generate garbage
- var attributes = GLImmediate.liveClientAttributes;
- var cacheMap = GLImmediate.rendererCache;
- var keyView = cacheMap.getStaticKeyView().reset();
-
- // By attrib state:
- var enabledAttributesKey = 0;
- for (var i = 0; i < attributes.length; i++) {
- enabledAttributesKey |= 1 << attributes[i].name;
- }
-
- // By fog state:
- var fogParam = 0;
- if (GLEmulation.fogEnabled) {
- switch (GLEmulation.fogMode) {
- case 0x0801: // GL_EXP2
- fogParam = 1;
- break;
- case 0x2601: // GL_LINEAR
- fogParam = 2;
- break;
- default: // default to GL_EXP
- fogParam = 3;
- break;
- }
- }
- keyView.next((enabledAttributesKey << 2) | fogParam);
-
-#if !GL_FFP_ONLY
- // By cur program:
- keyView.next(GL.currProgram);
- if (!GL.currProgram) {
-#endif
- GLImmediate.TexEnvJIT.traverseState(keyView);
-#if !GL_FFP_ONLY
- }
-#endif
-
- // If we don't already have it, create it.
- var renderer = keyView.get();
- if (!renderer) {
-#if GL_DEBUG
- err('generating renderer for ' + JSON.stringify(attributes));
-#endif
- renderer = GLImmediate.createRenderer();
- GLImmediate.currentRenderer = renderer;
- keyView.set(renderer);
- return renderer;
- }
- GLImmediate.currentRenderer = renderer; // Cache the currently used renderer, so later lookups without state changes can get this fast.
- return renderer;
- },
-
- createRenderer: function createRenderer(renderer) {
- var useCurrProgram = !!GL.currProgram;
- var hasTextures = false;
- for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
- var texAttribName = GLImmediate.TEXTURE0 + i;
- if (!GLImmediate.enabledClientAttributes[texAttribName])
- continue;
-
-#if ASSERTIONS
- if (!useCurrProgram) {
- if (GLImmediate.TexEnvJIT.getTexUnitType(i) == 0) {
- warnOnce("GL_TEXTURE" + i + " coords are supplied, but that texture unit is disabled in the fixed-function pipeline.");
- }
- }
-#endif
-
- hasTextures = true;
- }
-
- var ret = {
- init: function init() {
- // For fixed-function shader generation.
- var uTexUnitPrefix = 'u_texUnit';
- var aTexCoordPrefix = 'a_texCoord';
- var vTexCoordPrefix = 'v_texCoord';
- var vPrimColor = 'v_color';
- var uTexMatrixPrefix = GLImmediate.useTextureMatrix ? 'u_textureMatrix' : null;
-
- if (useCurrProgram) {
- if (GL.shaderInfos[GL.programShaders[GL.currProgram][0]].type == GLctx.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];
- this.usedTexUnitList = [];
- } 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.
- 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;
- }
- }
-
- GLImmediate.TexEnvJIT.setGLSLVars(uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix);
- var fsTexEnvPass = GLImmediate.TexEnvJIT.genAllPassLines('gl_FragColor', 2);
-
- var texUnitAttribList = '';
- var texUnitVaryingList = '';
- var texUnitUniformList = '';
- var vsTexCoordInits = '';
- this.usedTexUnitList = GLImmediate.TexEnvJIT.getUsedTexUnitList();
- for (var i = 0; i < this.usedTexUnitList.length; i++) {
- var texUnit = this.usedTexUnitList[i];
- texUnitAttribList += 'attribute vec4 ' + aTexCoordPrefix + texUnit + ';\n';
- texUnitVaryingList += 'varying vec4 ' + vTexCoordPrefix + texUnit + ';\n';
- texUnitUniformList += 'uniform sampler2D ' + uTexUnitPrefix + texUnit + ';\n';
- vsTexCoordInits += ' ' + vTexCoordPrefix + texUnit + ' = ' + aTexCoordPrefix + texUnit + ';\n';
-
- if (GLImmediate.useTextureMatrix) {
- texUnitUniformList += 'uniform mat4 ' + uTexMatrixPrefix + texUnit + ';\n';
- }
- }
-
- var vsFogVaryingInit = null;
- if (GLEmulation.fogEnabled) {
- vsFogVaryingInit = ' v_fogFragCoord = abs(ecPosition.z);\n';
- }
-
- var vsSource = [
- 'attribute vec4 a_position;',
- 'attribute vec4 a_color;',
- 'varying vec4 v_color;',
- texUnitAttribList,
- texUnitVaryingList,
- (GLEmulation.fogEnabled ? 'varying float v_fogFragCoord;' : null),
- 'uniform mat4 u_modelView;',
- 'uniform mat4 u_projection;',
- 'void main()',
- '{',
- ' vec4 ecPosition = u_modelView * a_position;', // eye-coordinate position
- ' gl_Position = u_projection * ecPosition;',
- ' v_color = a_color;',
- vsTexCoordInits,
- vsFogVaryingInit,
- '}',
- ''
- ].join('\n').replace(/\n\n+/g, '\n');
-
- this.vertexShader = GLctx.createShader(GLctx.VERTEX_SHADER);
- GLctx.shaderSource(this.vertexShader, vsSource);
- GLctx.compileShader(this.vertexShader);
-
- var fogHeaderIfNeeded = null;
- if (GLEmulation.fogEnabled) {
- fogHeaderIfNeeded = [
- '',
- 'varying float v_fogFragCoord; ',
- 'uniform vec4 u_fogColor; ',
- 'uniform float u_fogEnd; ',
- 'uniform float u_fogScale; ',
- 'uniform float u_fogDensity; ',
- 'float ffog(in float ecDistance) { ',
- fogFormula,
- ' fog = clamp(fog, 0.0, 1.0); ',
- ' return fog; ',
- '}',
- '',
- ].join("\n");
- }
-
- var fogPass = null;
- if (GLEmulation.fogEnabled) {
- fogPass = 'gl_FragColor = vec4(mix(u_fogColor.rgb, gl_FragColor.rgb, ffog(v_fogFragCoord)), gl_FragColor.a);\n';
- }
-
- var fsSource = [
- 'precision mediump float;',
- texUnitVaryingList,
- texUnitUniformList,
- 'varying vec4 v_color;',
- fogHeaderIfNeeded,
- 'void main()',
- '{',
- fsTexEnvPass,
- fogPass,
- '}',
- ''
- ].join("\n").replace(/\n\n+/g, '\n');
-
- this.fragmentShader = GLctx.createShader(GLctx.FRAGMENT_SHADER);
- GLctx.shaderSource(this.fragmentShader, fsSource);
- GLctx.compileShader(this.fragmentShader);
-
- this.program = GLctx.createProgram();
- GLctx.attachShader(this.program, this.vertexShader);
- GLctx.attachShader(this.program, this.fragmentShader);
-
- // As optimization, bind all attributes to prespecified locations, so that the FFP emulation
- // code can submit attributes to any generated FFP shader without having to examine each shader in turn.
- // These prespecified locations are only assumed if GL_FFP_ONLY is specified, since user could also create their
- // own shaders that didn't have attributes in the same locations.
- GLctx.bindAttribLocation(this.program, GLImmediate.VERTEX, 'a_position');
- GLctx.bindAttribLocation(this.program, GLImmediate.COLOR, 'a_color');
- GLctx.bindAttribLocation(this.program, GLImmediate.NORMAL, 'a_normal');
- var maxVertexAttribs = GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS);
- for (var i = 0; i < GLImmediate.MAX_TEXTURES && GLImmediate.TEXTURE0 + i < maxVertexAttribs; i++) {
- GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, 'a_texCoord'+i);
- GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, aTexCoordPrefix+i);
- }
- GLctx.linkProgram(this.program);
- }
-
- // Stores an array that remembers which matrix uniforms are up-to-date in this FFP renderer, so they don't need to be resubmitted
- // each time we render with this program.
- this.textureMatrixVersion = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
-
- this.positionLocation = GLctx.getAttribLocation(this.program, 'a_position');
-
- this.texCoordLocations = [];
-
- for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
- if (!GLImmediate.enabledClientAttributes[GLImmediate.TEXTURE0 + i]) {
- this.texCoordLocations[i] = -1;
- continue;
- }
-
- if (useCurrProgram) {
- this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, 'a_texCoord' + i);
- } else {
- this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, aTexCoordPrefix + i);
- }
- }
- this.colorLocation = GLctx.getAttribLocation(this.program, 'a_color');
- if (!useCurrProgram) {
- // Temporarily switch to the program so we can set our sampler uniforms early.
- var prevBoundProg = GLctx.getParameter(GLctx.CURRENT_PROGRAM);
- GLctx.useProgram(this.program);
- {
- for (var i = 0; i < this.usedTexUnitList.length; i++) {
- var texUnitID = this.usedTexUnitList[i];
- var texSamplerLoc = GLctx.getUniformLocation(this.program, uTexUnitPrefix + texUnitID);
- GLctx.uniform1i(texSamplerLoc, texUnitID);
- }
- }
- // The default color attribute value is not the same as the default for all other attribute streams (0,0,0,1) but (1,1,1,1),
- // so explicitly set it right at start.
- GLctx.vertexAttrib4fv(this.colorLocation, [1,1,1,1]);
- GLctx.useProgram(prevBoundProg);
- }
-
- this.textureMatrixLocations = [];
- for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
- this.textureMatrixLocations[i] = GLctx.getUniformLocation(this.program, 'u_textureMatrix' + i);
- }
- this.normalLocation = GLctx.getAttribLocation(this.program, 'a_normal');
-
- this.modelViewLocation = GLctx.getUniformLocation(this.program, 'u_modelView');
- this.projectionLocation = GLctx.getUniformLocation(this.program, 'u_projection');
-
- this.hasTextures = hasTextures;
- this.hasNormal = GLImmediate.enabledClientAttributes[GLImmediate.NORMAL] &&
- GLImmediate.clientAttributes[GLImmediate.NORMAL].size > 0 &&
- this.normalLocation >= 0;
- this.hasColor = (this.colorLocation === 0) || this.colorLocation > 0;
-
- this.floatType = GLctx.FLOAT; // minor optimization
-
- this.fogColorLocation = GLctx.getUniformLocation(this.program, 'u_fogColor');
- this.fogEndLocation = GLctx.getUniformLocation(this.program, 'u_fogEnd');
- this.fogScaleLocation = GLctx.getUniformLocation(this.program, 'u_fogScale');
- this.fogDensityLocation = GLctx.getUniformLocation(this.program, 'u_fogDensity');
- this.hasFog = !!(this.fogColorLocation || this.fogEndLocation ||
- this.fogScaleLocation || this.fogDensityLocation);
- },
-
- prepare: function prepare() {
- // Calculate the array buffer
- var arrayBuffer;
- if (!GL.currArrayBuffer) {
- var start = GLImmediate.firstVertex*GLImmediate.stride;
- var end = GLImmediate.lastVertex*GLImmediate.stride;
-#if ASSERTIONS
- assert(end <= GL.MAX_TEMP_BUFFER_SIZE, 'too much vertex data');
-#endif
- arrayBuffer = GL.getTempVertexBuffer(end);
- // TODO: consider using the last buffer we bound, if it was larger. downside is larger buffer, but we might avoid rebinding and preparing
- } else {
- arrayBuffer = GL.currArrayBuffer;
- }
-
-#if GL_UNSAFE_OPTS
- // If the array buffer is unchanged and the renderer as well, then we can avoid all the work here
- // XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you
- // have odd glitches
- var lastRenderer = GLImmediate.lastRenderer;
- var canSkip = this == lastRenderer &&
- arrayBuffer == GLImmediate.lastArrayBuffer &&
- (GL.currProgram || this.program) == GLImmediate.lastProgram &&
- GLImmediate.stride == GLImmediate.lastStride &&
- !GLImmediate.matricesModified;
- if (!canSkip && lastRenderer) lastRenderer.cleanup();
-#endif
- if (!GL.currArrayBuffer) {
- // Bind the array buffer and upload data after cleaning up the previous renderer
-
- if (arrayBuffer != GLImmediate.lastArrayBuffer) {
- GLctx.bindBuffer(GLctx.ARRAY_BUFFER, arrayBuffer);
- GLImmediate.lastArrayBuffer = arrayBuffer;
- }
-
- GLctx.bufferSubData(GLctx.ARRAY_BUFFER, start, GLImmediate.vertexData.subarray(start >> 2, end >> 2));
- }
-#if GL_UNSAFE_OPTS
- if (canSkip) return;
- GLImmediate.lastRenderer = this;
- GLImmediate.lastProgram = GL.currProgram || this.program;
- GLImmediate.lastStride == GLImmediate.stride;
- GLImmediate.matricesModified = false;
-#endif
-
- if (!GL.currProgram) {
- if (GLImmediate.fixedFunctionProgram != this.program) {
- GLctx.useProgram(this.program);
- GLImmediate.fixedFunctionProgram = this.program;
- }
- }
-
- if (this.modelViewLocation && this.modelViewMatrixVersion != GLImmediate.matrixVersion[0/*m*/]) {
- this.modelViewMatrixVersion = GLImmediate.matrixVersion[0/*m*/];
- GLctx.uniformMatrix4fv(this.modelViewLocation, false, GLImmediate.matrix[0/*m*/]);
- }
- if (this.projectionLocation && this.projectionMatrixVersion != GLImmediate.matrixVersion[1/*p*/]) {
- this.projectionMatrixVersion = GLImmediate.matrixVersion[1/*p*/];
- GLctx.uniformMatrix4fv(this.projectionLocation, false, GLImmediate.matrix[1/*p*/]);
- }
-
- var clientAttributes = GLImmediate.clientAttributes;
- var posAttr = clientAttributes[GLImmediate.VERTEX];
-
-#if GL_ASSERTIONS
- GL.validateVertexAttribPointer(posAttr.size, posAttr.type, GLImmediate.stride, clientAttributes[GLImmediate.VERTEX].offset);
-#endif
-
-#if GL_FFP_ONLY
- if (!GL.currArrayBuffer) {
- GLctx.vertexAttribPointer(GLImmediate.VERTEX, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset);
- if (this.hasNormal) {
- var normalAttr = clientAttributes[GLImmediate.NORMAL];
- GLctx.vertexAttribPointer(GLImmediate.NORMAL, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset);
- }
- }
-#else
- GLctx.vertexAttribPointer(this.positionLocation, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset);
- GLctx.enableVertexAttribArray(this.positionLocation);
- if (this.hasNormal) {
- var normalAttr = clientAttributes[GLImmediate.NORMAL];
-#if GL_ASSERTIONS
- GL.validateVertexAttribPointer(normalAttr.size, normalAttr.type, GLImmediate.stride, normalAttr.offset);
-#endif
- GLctx.vertexAttribPointer(this.normalLocation, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset);
- GLctx.enableVertexAttribArray(this.normalLocation);
- }
-#endif
- if (this.hasTextures) {
- for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
-#if GL_FFP_ONLY
- if (!GL.currArrayBuffer) {
- var attribLoc = GLImmediate.TEXTURE0+i;
- var texAttr = clientAttributes[attribLoc];
- if (texAttr.size) {
- GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset);
- } else {
- // These two might be dangerous, but let's try them.
- GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
- }
- }
-#else
- var attribLoc = this.texCoordLocations[i];
- if (attribLoc === undefined || attribLoc < 0) continue;
- var texAttr = clientAttributes[GLImmediate.TEXTURE0+i];
-
- if (texAttr.size) {
-#if GL_ASSERTIONS
- GL.validateVertexAttribPointer(texAttr.size, texAttr.type, GLImmediate.stride, texAttr.offset);
-#endif
- GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset);
- GLctx.enableVertexAttribArray(attribLoc);
- } else {
- // These two might be dangerous, but let's try them.
- GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
- GLctx.disableVertexAttribArray(attribLoc);
- }
-#endif
- var t = 2/*t*/+i;
- if (this.textureMatrixLocations[i] && this.textureMatrixVersion[t] != GLImmediate.matrixVersion[t]) { // XXX might we need this even without the condition we are currently in?
- this.textureMatrixVersion[t] = GLImmediate.matrixVersion[t];
- GLctx.uniformMatrix4fv(this.textureMatrixLocations[i], false, GLImmediate.matrix[t]);
- }
- }
- }
- if (GLImmediate.enabledClientAttributes[GLImmediate.COLOR]) {
- var colorAttr = clientAttributes[GLImmediate.COLOR];
-#if GL_ASSERTIONS
- GL.validateVertexAttribPointer(colorAttr.size, colorAttr.type, GLImmediate.stride, colorAttr.offset);
-#endif
-#if GL_FFP_ONLY
- if (!GL.currArrayBuffer) {
- GLctx.vertexAttribPointer(GLImmediate.COLOR, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset);
- }
-#else
- GLctx.vertexAttribPointer(this.colorLocation, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset);
- GLctx.enableVertexAttribArray(this.colorLocation);
-#endif
- }
-#if !GL_FFP_ONLY
- else if (this.hasColor) {
- GLctx.disableVertexAttribArray(this.colorLocation);
- GLctx.vertexAttrib4fv(this.colorLocation, GLImmediate.clientColor);
- }
-#endif
- if (this.hasFog) {
- if (this.fogColorLocation) GLctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor);
- if (this.fogEndLocation) GLctx.uniform1f(this.fogEndLocation, GLEmulation.fogEnd);
- if (this.fogScaleLocation) GLctx.uniform1f(this.fogScaleLocation, 1/(GLEmulation.fogEnd - GLEmulation.fogStart));
- if (this.fogDensityLocation) GLctx.uniform1f(this.fogDensityLocation, GLEmulation.fogDensity);
- }
- },
-
- cleanup: function cleanup() {
-#if !GL_FFP_ONLY
- GLctx.disableVertexAttribArray(this.positionLocation);
- if (this.hasTextures) {
- for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
- if (GLImmediate.enabledClientAttributes[GLImmediate.TEXTURE0+i] && this.texCoordLocations[i] >= 0) {
- GLctx.disableVertexAttribArray(this.texCoordLocations[i]);
- }
- }
- }
- if (this.hasColor) {
- GLctx.disableVertexAttribArray(this.colorLocation);
- }
- if (this.hasNormal) {
- GLctx.disableVertexAttribArray(this.normalLocation);
- }
- if (!GL.currProgram) {
- GLctx.useProgram(null);
- GLImmediate.fixedFunctionProgram = 0;
- }
- if (!GL.currArrayBuffer) {
- GLctx.bindBuffer(GLctx.ARRAY_BUFFER, null);
- GLImmediate.lastArrayBuffer = null;
- }
-
-#if GL_UNSAFE_OPTS
- GLImmediate.lastRenderer = null;
- GLImmediate.lastProgram = null;
-#endif
- GLImmediate.matricesModified = true;
-#endif
- }
- };
- ret.init();
- return ret;
- },
-
- setupFuncs: function() {
- // Replace some functions with immediate-mode aware versions. If there are no client
- // attributes enabled, and we use webgl-friendly modes (no GL_QUADS), then no need
- // for emulation
- _glDrawArrays = _emscripten_glDrawArrays = function _glDrawArrays(mode, first, count) {
- if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6) {
- GLctx.drawArrays(mode, first, count);
- return;
- }
- GLImmediate.prepareClientAttributes(count, false);
- GLImmediate.mode = mode;
- if (!GL.currArrayBuffer) {
- GLImmediate.vertexData = {{{ makeHEAPView('F32', 'GLImmediate.vertexPointer', 'GLImmediate.vertexPointer + (first+count)*GLImmediate.stride') }}}; // XXX assuming float
- GLImmediate.firstVertex = first;
- GLImmediate.lastVertex = first + count;
- }
- GLImmediate.flush(null, first);
- GLImmediate.mode = -1;
- };
- {{{ updateExport('glDrawArrays') }}}
-
- _glDrawElements = _emscripten_glDrawElements = function _glDrawElements(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements
- if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6 && GL.currElementArrayBuffer) {
- GLctx.drawElements(mode, count, type, indices);
- return;
- }
-#if ASSERTIONS
- if (!GL.currElementArrayBuffer) {
- assert(type == GLctx.UNSIGNED_SHORT); // We can only emulate buffers of this kind, for now
- }
- console.log("DrawElements doesn't actually prepareClientAttributes properly.");
-#endif
- GLImmediate.prepareClientAttributes(count, false);
- GLImmediate.mode = mode;
- if (!GL.currArrayBuffer) {
- GLImmediate.firstVertex = end ? start : TOTAL_MEMORY; // if we don't know the start, set an invalid value and we will calculate it later from the indices
- GLImmediate.lastVertex = end ? end+1 : 0;
- GLImmediate.vertexData = HEAPF32.subarray(GLImmediate.vertexPointer >> 2, end ? (GLImmediate.vertexPointer + (end+1)*GLImmediate.stride) >> 2 : undefined); // XXX assuming float
- }
- GLImmediate.flush(count, 0, indices);
- GLImmediate.mode = -1;
- };
- {{{ updateExport('glDrawElements') }}}
-
- // TexEnv stuff needs to be prepared early, so do it here.
- // init() is too late for -O2, since it freezes the GL functions
- // by that point.
- GLImmediate.MapTreeLib = GLImmediate.spawnMapTreeLib();
- GLImmediate.spawnMapTreeLib = null;
-
- GLImmediate.TexEnvJIT = GLImmediate.spawnTexEnvJIT();
- GLImmediate.spawnTexEnvJIT = null;
-
- GLImmediate.setupHooks();
- },
-
- setupHooks: function() {
- if (!GLEmulation.hasRunInit) {
- GLEmulation.init();
- }
-
- var glActiveTexture = _glActiveTexture;
- _glActiveTexture = _emscripten_glActiveTexture = function _glActiveTexture(texture) {
- GLImmediate.TexEnvJIT.hook_activeTexture(texture);
- glActiveTexture(texture);
- };
- {{{ updateExport('glActiveTexture') }}}
-
- var glEnable = _glEnable;
- _glEnable = _emscripten_glEnable = function _glEnable(cap) {
- GLImmediate.TexEnvJIT.hook_enable(cap);
- glEnable(cap);
- };
- {{{ updateExport('glEnable') }}}
-
- var glDisable = _glDisable;
- _glDisable = _emscripten_glDisable = function _glDisable(cap) {
- GLImmediate.TexEnvJIT.hook_disable(cap);
- glDisable(cap);
- };
- {{{ updateExport('glDisable') }}}
-
- var glTexEnvf = (typeof(_glTexEnvf) != 'undefined') ? _glTexEnvf : function(){};
- _glTexEnvf = _emscripten_glTexEnvf = function _glTexEnvf(target, pname, param) {
- GLImmediate.TexEnvJIT.hook_texEnvf(target, pname, param);
- // Don't call old func, since we are the implementor.
- //glTexEnvf(target, pname, param);
- };
- {{{ updateExport('glTexEnvf') }}}
-
- var glTexEnvi = (typeof(_glTexEnvi) != 'undefined') ? _glTexEnvi : function(){};
- _glTexEnvi = _emscripten_glTexEnvi = function _glTexEnvi(target, pname, param) {
- GLImmediate.TexEnvJIT.hook_texEnvi(target, pname, param);
- // Don't call old func, since we are the implementor.
- //glTexEnvi(target, pname, param);
- };
- {{{ updateExport('glTexEnvi') }}}
-
- var glTexEnvfv = (typeof(_glTexEnvfv) != 'undefined') ? _glTexEnvfv : function(){};
- _glTexEnvfv = _emscripten_glTexEnvfv = function _glTexEnvfv(target, pname, param) {
- GLImmediate.TexEnvJIT.hook_texEnvfv(target, pname, param);
- // Don't call old func, since we are the implementor.
- //glTexEnvfv(target, pname, param);
- };
- {{{ updateExport('glTexEnvfv') }}}
-
- _glGetTexEnviv = function _glGetTexEnviv(target, pname, param) {
- GLImmediate.TexEnvJIT.hook_getTexEnviv(target, pname, param);
- };
- {{{ updateExport('glGetTexEnviv') }}}
-
- _glGetTexEnvfv = function _glGetTexEnvfv(target, pname, param) {
- GLImmediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param);
- };
- {{{ updateExport('glGetTexEnvfv') }}}
-
- var glGetIntegerv = _glGetIntegerv;
- _glGetIntegerv = _emscripten_glGetIntegerv = function _glGetIntegerv(pname, params) {
- switch (pname) {
- case 0x8B8D: { // GL_CURRENT_PROGRAM
- // Just query directly so we're working with WebGL objects.
- var cur = GLctx.getParameter(GLctx.CURRENT_PROGRAM);
- if (cur == GLImmediate.fixedFunctionProgram) {
- // Pretend we're not using a program.
- {{{ makeSetValue('params', '0', '0', 'i32') }}};
- return;
- }
- break;
- }
- }
- glGetIntegerv(pname, params);
- };
- {{{ updateExport('glGetIntegerv') }}}
- },
-
- // Main functions
- initted: false,
- init: function() {
- err('WARNING: using emscripten GL immediate mode emulation. This is very limited in what it supports');
- GLImmediate.initted = true;
-
- if (!Module.useWebGL) return; // a 2D canvas may be currently used TODO: make sure we are actually called in that case
-
- // User can override the maximum number of texture units that we emulate. Using fewer texture units increases runtime performance
- // slightly, so it is advantageous to choose as small value as needed.
- GLImmediate.MAX_TEXTURES = Module['GL_MAX_TEXTURE_IMAGE_UNITS'] || GLctx.getParameter(GLctx.MAX_TEXTURE_IMAGE_UNITS);
-
- GLImmediate.TexEnvJIT.init(GLctx, GLImmediate.MAX_TEXTURES);
-
- GLImmediate.NUM_ATTRIBUTES = 3 /*pos+normal+color attributes*/ + GLImmediate.MAX_TEXTURES;
- GLImmediate.clientAttributes = [];
- GLEmulation.enabledClientAttribIndices = [];
- for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
- GLImmediate.clientAttributes.push({});
- GLEmulation.enabledClientAttribIndices.push(false);
- }
-
- // Initialize matrix library
- // When user sets a matrix, increment a 'version number' on the new data, and when rendering, submit
- // the matrices to the shader program only if they have an old version of the data.
- GLImmediate.matrix = [];
- GLImmediate.matrixStack = [];
- GLImmediate.matrixVersion = [];
- for (var i = 0; i < 2 + GLImmediate.MAX_TEXTURES; i++) { // Modelview, Projection, plus one matrix for each texture coordinate.
- GLImmediate.matrixStack.push([]);
- GLImmediate.matrixVersion.push(0);
- GLImmediate.matrix.push(GLImmediate.matrixLib.mat4.create());
- GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[i]);
- }
-
- // Renderer cache
- GLImmediate.rendererCache = GLImmediate.MapTreeLib.create();
-
- // Buffers for data
- GLImmediate.tempData = new Float32Array(GL.MAX_TEMP_BUFFER_SIZE >> 2);
- GLImmediate.indexData = new Uint16Array(GL.MAX_TEMP_BUFFER_SIZE >> 1);
-
- GLImmediate.vertexDataU8 = new Uint8Array(GLImmediate.tempData.buffer);
-
- GL.generateTempBuffers(true, GL.currentContext);
-
- GLImmediate.clientColor = new Float32Array([1, 1, 1, 1]);
- },
-
- // Prepares and analyzes client attributes.
- // Modifies liveClientAttributes, stride, vertexPointer, vertexCounter
- // count: number of elements we will draw
- // beginEnd: whether we are drawing the results of a begin/end block
- prepareClientAttributes: function prepareClientAttributes(count, beginEnd) {
- // If no client attributes were modified since we were last called, do nothing. Note that this
- // does not work for glBegin/End, where we generate renderer components dynamically and then
- // disable them ourselves, but it does help with glDrawElements/Arrays.
- if (!GLImmediate.modifiedClientAttributes) {
-#if GL_ASSERTIONS
- if ((GLImmediate.stride & 3) != 0) {
- warnOnce('Warning: Rendering from client side vertex arrays where stride (' + GLImmediate.stride + ') is not a multiple of four! This is not currently supported!');
- }
-#endif
- GLImmediate.vertexCounter = (GLImmediate.stride * count) / 4; // XXX assuming float
- return;
- }
- GLImmediate.modifiedClientAttributes = false;
-
- // The role of prepareClientAttributes is to examine the set of client-side vertex attribute buffers
- // that user code has submitted, and to prepare them to be uploaded to a VBO in GPU memory
- // (since WebGL does not support client-side rendering, i.e. rendering from vertex data in CPU memory)
- // User can submit vertex data generally in three different configurations:
- // 1. Fully planar: all attributes are in their own separate tightly-packed arrays in CPU memory.
- // 2. Fully interleaved: all attributes share a single array where data is interleaved something like (pos,uv,normal), (pos,uv,normal), ...
- // 3. Complex hybrid: Multiple separate arrays that either are sparsely strided, and/or partially interleave vertex attributes.
-
- // For simplicity, we support the case (2) as the fast case. For (1) and (3), we do a memory copy of the
- // vertex data here to prepare a relayouted buffer that is of the structure in case (2). The reason
- // for this is that it allows the emulation code to get away with using just one VBO buffer for rendering,
- // and not have to maintain multiple ones. Therefore cases (1) and (3) will be very slow, and case (2) is fast.
-
- // Detect which case we are in by using a quick heuristic by examining the strides of the buffers. If all the buffers have identical
- // stride, we assume we have case (2), otherwise we have something more complex.
- var clientStartPointer = 0x7FFFFFFF;
- var bytes = 0; // Total number of bytes taken up by a single vertex.
- var minStride = 0x7FFFFFFF;
- var maxStride = 0;
- var attributes = GLImmediate.liveClientAttributes;
- attributes.length = 0;
- for (var i = 0; i < 3+GLImmediate.MAX_TEXTURES; i++) {
- if (GLImmediate.enabledClientAttributes[i]) {
- var attr = GLImmediate.clientAttributes[i];
- attributes.push(attr);
- clientStartPointer = Math.min(clientStartPointer, attr.pointer);
- attr.sizeBytes = attr.size * GL.byteSizeByType[attr.type - GL.byteSizeByTypeRoot];
- bytes += attr.sizeBytes;
- minStride = Math.min(minStride, attr.stride);
- maxStride = Math.max(maxStride, attr.stride);
- }
- }
-
- if ((minStride != maxStride || maxStride < bytes) && !beginEnd) {
- // We are in cases (1) or (3): slow path, shuffle the data around into a single interleaved vertex buffer.
- // The immediate-mode glBegin()/glEnd() vertex submission gets automatically generated in appropriate layout,
- // so never need to come down this path if that was used.
-#if GL_ASSERTIONS
- warnOnce('Rendering from planar client-side vertex arrays. This is a very slow emulation path! Use interleaved vertex arrays for best performance.');
-#endif
- if (!GLImmediate.restrideBuffer) GLImmediate.restrideBuffer = _malloc(GL.MAX_TEMP_BUFFER_SIZE);
- var start = GLImmediate.restrideBuffer;
- bytes = 0;
- // calculate restrided offsets and total size
- for (var i = 0; i < attributes.length; i++) {
- var attr = attributes[i];
- var size = attr.sizeBytes;
- if (size % 4 != 0) size += 4 - (size % 4); // align everything
- attr.offset = bytes;
- bytes += size;
- }
- // copy out the data (we need to know the stride for that, and define attr.pointer)
- for (var i = 0; i < attributes.length; i++) {
- var attr = attributes[i];
- var srcStride = Math.max(attr.sizeBytes, attr.stride);
- if ((srcStride & 3) == 0 && (attr.sizeBytes & 3) == 0) {
- var size4 = attr.sizeBytes>>2;
- var srcStride4 = Math.max(attr.sizeBytes, attr.stride)>>2;
- for (var j = 0; j < count; j++) {
- for (var k = 0; k < size4; k++) { // copy in chunks of 4 bytes, our alignment makes this possible
- HEAP32[((start + attr.offset + bytes*j)>>2) + k] = HEAP32[(attr.pointer>>2) + j*srcStride4 + k];
- }
- }
- } else {
- for (var j = 0; j < count; j++) {
- for (var k = 0; k < attr.sizeBytes; k++) { // source data was not aligned to multiples of 4, must copy byte by byte.
- HEAP8[start + attr.offset + bytes*j + k] = HEAP8[attr.pointer + j*srcStride + k];
- }
- }
- }
- attr.pointer = start + attr.offset;
- }
- GLImmediate.stride = bytes;
- GLImmediate.vertexPointer = start;
- } else {
- // case (2): fast path, all data is interleaved to a single vertex array so we can get away with a single VBO upload.
- if (GL.currArrayBuffer) {
- GLImmediate.vertexPointer = 0;
- } else {
- GLImmediate.vertexPointer = clientStartPointer;
- }
- for (var i = 0; i < attributes.length; i++) {
- var attr = attributes[i];
- attr.offset = attr.pointer - GLImmediate.vertexPointer; // Compute what will be the offset of this attribute in the VBO after we upload.
- }
- GLImmediate.stride = Math.max(maxStride, bytes);
- }
- if (!beginEnd) {
-#if GL_ASSERTIONS
- if ((GLImmediate.stride & 3) != 0) {
- warnOnce('Warning: Rendering from client side vertex arrays where stride (' + GLImmediate.stride + ') is not a multiple of four! This is not currently supported!');
- }
-#endif
- GLImmediate.vertexCounter = (GLImmediate.stride * count) / 4; // XXX assuming float
- }
- },
-
- flush: function flush(numProvidedIndexes, startIndex, ptr) {
-#if ASSERTIONS
- assert(numProvidedIndexes >= 0 || !numProvidedIndexes);
-#endif
- startIndex = startIndex || 0;
- ptr = ptr || 0;
-
- var renderer = GLImmediate.getRenderer();
-
- // Generate index data in a format suitable for GLES 2.0/WebGL
- var numVertexes = 4 * GLImmediate.vertexCounter / GLImmediate.stride;
- if (!numVertexes) return;
-#if ASSERTIONS
- assert(numVertexes % 1 == 0, "`numVertexes` must be an integer.");
-#endif
- var emulatedElementArrayBuffer = false;
- var numIndexes = 0;
- if (numProvidedIndexes) {
- numIndexes = numProvidedIndexes;
- if (!GL.currArrayBuffer && GLImmediate.firstVertex > GLImmediate.lastVertex) {
- // Figure out the first and last vertex from the index data
-#if ASSERTIONS
- assert(!GL.currElementArrayBuffer); // If we are going to upload array buffer data, we need to find which range to
- // upload based on the indices. If they are in a buffer on the GPU, that is very
- // inconvenient! So if you do not have an array buffer, you should also not have
- // an element array buffer. But best is to use both buffers!
-#endif
- for (var i = 0; i < numProvidedIndexes; i++) {
- var currIndex = {{{ makeGetValue('ptr', 'i*2', 'i16', null, 1) }}};
- GLImmediate.firstVertex = Math.min(GLImmediate.firstVertex, currIndex);
- GLImmediate.lastVertex = Math.max(GLImmediate.lastVertex, currIndex+1);
- }
- }
- if (!GL.currElementArrayBuffer) {
- // If no element array buffer is bound, then indices is a literal pointer to clientside data
-#if ASSERTIONS
- assert(numProvidedIndexes << 1 <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)');
-#endif
- var indexBuffer = GL.getTempIndexBuffer(numProvidedIndexes << 1);
- GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, indexBuffer);
- GLctx.bufferSubData(GLctx.ELEMENT_ARRAY_BUFFER, 0, {{{ makeHEAPView('U16', 'ptr', 'ptr + (numProvidedIndexes << 1)') }}});
- ptr = 0;
- emulatedElementArrayBuffer = true;
- }
- } else if (GLImmediate.mode > 6) { // above GL_TRIANGLE_FAN are the non-GL ES modes
- if (GLImmediate.mode != 7) throw 'unsupported immediate mode ' + GLImmediate.mode; // GL_QUADS
- // GLImmediate.firstVertex is the first vertex we want. Quad indexes are in the pattern
- // 0 1 2, 0 2 3, 4 5 6, 4 6 7, so we need to look at index firstVertex * 1.5 to see it.
- // Then since indexes are 2 bytes each, that means 3
-#if ASSERTIONS
- assert(GLImmediate.firstVertex % 4 == 0);
-#endif
- ptr = GLImmediate.firstVertex*3;
- var numQuads = numVertexes / 4;
- numIndexes = numQuads * 6; // 0 1 2, 0 2 3 pattern
-#if ASSERTIONS
- assert(ptr + (numIndexes << 1) <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)');
-#endif
- GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.currentContext.tempQuadIndexBuffer);
- emulatedElementArrayBuffer = true;
- }
-
- renderer.prepare();
-
- if (numIndexes) {
- GLctx.drawElements(GLctx.TRIANGLES, numIndexes, GLctx.UNSIGNED_SHORT, ptr);
- } else {
- GLctx.drawArrays(GLImmediate.mode, startIndex, numVertexes);
- }
-
- if (emulatedElementArrayBuffer) {
- GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null);
- }
-
-#if !GL_UNSAFE_OPTS
-#if !GL_FFP_ONLY
- renderer.cleanup();
-#endif
-#endif
- }
- },
-
- $GLImmediateSetup: {},
- $GLImmediateSetup__deps: ['$GLImmediate', function() { return 'GLImmediate.matrixLib = ' + read('gl-matrix.js') + ';\n' }],
- $GLImmediateSetup: {},
-
- glBegin__deps: ['$GLImmediateSetup'],
- glBegin: function(mode) {
- // Push the old state:
- GLImmediate.enabledClientAttributes_preBegin = GLImmediate.enabledClientAttributes;
- GLImmediate.enabledClientAttributes = [];
-
- GLImmediate.clientAttributes_preBegin = GLImmediate.clientAttributes;
- GLImmediate.clientAttributes = []
- for (var i = 0; i < GLImmediate.clientAttributes_preBegin.length; i++) {
- GLImmediate.clientAttributes.push({});
- }
-
- GLImmediate.mode = mode;
- GLImmediate.vertexCounter = 0;
- var components = GLImmediate.rendererComponents = [];
- for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
- components[i] = 0;
- }
- GLImmediate.rendererComponentPointer = 0;
- GLImmediate.vertexData = GLImmediate.tempData;
- },
-
- glEnd: function() {
- GLImmediate.prepareClientAttributes(GLImmediate.rendererComponents[GLImmediate.VERTEX], true);
- GLImmediate.firstVertex = 0;
- GLImmediate.lastVertex = GLImmediate.vertexCounter / (GLImmediate.stride >> 2);
- GLImmediate.flush();
- GLImmediate.disableBeginEndClientAttributes();
- GLImmediate.mode = -1;
-
- // Pop the old state:
- GLImmediate.enabledClientAttributes = GLImmediate.enabledClientAttributes_preBegin;
- GLImmediate.clientAttributes = GLImmediate.clientAttributes_preBegin;
- GLImmediate.currentRenderer = null; // The set of active client attributes changed, we must re-lookup the renderer to use.
- GLImmediate.modifiedClientAttributes = true;
- },
-
- glVertex2f: function(x, y) {
-#if ASSERTIONS
- assert(GLImmediate.mode >= 0); // must be in begin/end
-#endif
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = 0;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = 1;
-#if ASSERTIONS
- assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
-#endif
- GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
- },
-
- glVertex3f: function(x, y, z) {
-#if ASSERTIONS
- assert(GLImmediate.mode >= 0); // must be in begin/end
-#endif
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = z;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = 1;
-#if ASSERTIONS
- assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
-#endif
- GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
- },
-
- glVertex4f: function(x, y, z, w) {
-#if ASSERTIONS
- assert(GLImmediate.mode >= 0); // must be in begin/end
-#endif
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = z;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = w;
-#if ASSERTIONS
- assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
-#endif
- GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
- },
-
- glVertex2fv__deps: ['glVertex2f'],
- glVertex2fv: function(p) {
- _glVertex2f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}});
- },
-
- glVertex3fv__deps: ['glVertex3f'],
- glVertex3fv: function(p) {
- _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}});
- },
-
- glVertex4fv__deps: ['glVertex4f'],
- glVertex4fv: function(p) {
- _glVertex4f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}, {{{ makeGetValue('p', '12', 'float') }}});
- },
-
- glVertex2i: 'glVertex2f',
-
- glVertex3i: 'glVertex3f',
-
- glVertex4i: 'glVertex4f',
-
- glTexCoord2i: function(u, v) {
-#if ASSERTIONS
- assert(GLImmediate.mode >= 0); // must be in begin/end
-#endif
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = u;
- GLImmediate.vertexData[GLImmediate.vertexCounter++] = v;
- GLImmediate.addRendererComponent(GLImmediate.TEXTURE0, 2, GLctx.FLOAT);
- },
- glTexCoord2f: 'glTexCoord2i',
-
- glTexCoord2fv__deps: ['glTexCoord2i'],
- glTexCoord2fv: function(v) {
- _glTexCoord2i({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}});
- },
-
- glTexCoord4f: function() { throw 'glTexCoord4f: TODO' },
-
- glColor4f: function(r, g, b, a) {
- r = Math.max(Math.min(r, 1), 0);
- g = Math.max(Math.min(g, 1), 0);
- b = Math.max(Math.min(b, 1), 0);
- a = Math.max(Math.min(a, 1), 0);
-
- // TODO: make ub the default, not f, save a few mathops
- if (GLImmediate.mode >= 0) {
- var start = GLImmediate.vertexCounter << 2;
- GLImmediate.vertexDataU8[start + 0] = r * 255;
- GLImmediate.vertexDataU8[start + 1] = g * 255;
- GLImmediate.vertexDataU8[start + 2] = b * 255;
- GLImmediate.vertexDataU8[start + 3] = a * 255;
- GLImmediate.vertexCounter++;
- GLImmediate.addRendererComponent(GLImmediate.COLOR, 4, GLctx.UNSIGNED_BYTE);
- } else {
- GLImmediate.clientColor[0] = r;
- GLImmediate.clientColor[1] = g;
- GLImmediate.clientColor[2] = b;
- GLImmediate.clientColor[3] = a;
-#if GL_FFP_ONLY
- GLctx.vertexAttrib4fv(GLImmediate.COLOR, GLImmediate.clientColor);
-#endif
- }
- },
- glColor4d: 'glColor4f',
- glColor4ub__deps: ['glColor4f'],
- glColor4ub: function(r, g, b, a) {
- _glColor4f((r&255)/255, (g&255)/255, (b&255)/255, (a&255)/255);
- },
- glColor4us__deps: ['glColor4f'],
- glColor4us: function(r, g, b, a) {
- _glColor4f((r&65535)/65535, (g&65535)/65535, (b&65535)/65535, (a&65535)/65535);
- },
- glColor4ui__deps: ['glColor4f'],
- glColor4ui: function(r, g, b, a) {
- _glColor4f((r>>>0)/4294967295, (g>>>0)/4294967295, (b>>>0)/4294967295, (a>>>0)/4294967295);
- },
- glColor3f__deps: ['glColor4f'],
- glColor3f: function(r, g, b) {
- _glColor4f(r, g, b, 1);
- },
- glColor3d: 'glColor3f',
- glColor3ub__deps: ['glColor4ub'],
- glColor3ub: function(r, g, b) {
- _glColor4ub(r, g, b, 255);
- },
- glColor3us__deps: ['glColor4us'],
- glColor3us: function(r, g, b) {
- _glColor4us(r, g, b, 65535);
- },
- glColor3ui__deps: ['glColor4ui'],
- glColor3ui: function(r, g, b) {
- _glColor4ui(r, g, b, 4294967295);
- },
-
- glColor3ubv__deps: ['glColor3ub'],
- glColor3ubv: function(p) {
- _glColor3ub({{{ makeGetValue('p', '0', 'i8') }}}, {{{ makeGetValue('p', '1', 'i8') }}}, {{{ makeGetValue('p', '2', 'i8') }}});
- },
- glColor3usv__deps: ['glColor3us'],
- glColor3usv: function(p) {
- _glColor3us({{{ makeGetValue('p', '0', 'i16') }}}, {{{ makeGetValue('p', '2', 'i16') }}}, {{{ makeGetValue('p', '4', 'i16') }}});
- },
- glColor3uiv__deps: ['glColor3ui'],
- glColor3uiv: function(p) {
- _glColor3ui({{{ makeGetValue('p', '0', 'i32') }}}, {{{ makeGetValue('p', '4', 'i32') }}}, {{{ makeGetValue('p', '8', 'i32') }}});
- },
- glColor3fv__deps: ['glColor3f'],
- glColor3fv: function(p) {
- _glColor3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}});
- },
- glColor4fv__deps: ['glColor4f'],
- glColor4fv: function(p) {
- _glColor4f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}, {{{ makeGetValue('p', '12', 'float') }}});
- },
-
- glColor4ubv__deps: ['glColor4ub'],
- glColor4ubv: function(p) {
- _glColor4ub({{{ makeGetValue('p', '0', 'i8') }}}, {{{ makeGetValue('p', '1', 'i8') }}}, {{{ makeGetValue('p', '2', 'i8') }}}, {{{ makeGetValue('p', '3', 'i8') }}});
- },
-
- glFogf: function(pname, param) { // partial support, TODO
- switch(pname) {
- case 0x0B63: // GL_FOG_START
- 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
- if (GLEmulation.fogMode != param) {
- GLImmediate.currentRenderer = null; // Fog mode is part of the FFP shader state, we must re-lookup the renderer to use.
- GLEmulation.fogMode = param;
- }
- break;
- default: // default to GL_EXP
- if (GLEmulation.fogMode != 0x0800 /* GL_EXP */) {
- GLImmediate.currentRenderer = null; // Fog mode is part of the FFP shader state, we must re-lookup the renderer to use.
- GLEmulation.fogMode = 0x0800 /* GL_EXP */;
- }
- break;
- }
- break;
- }
- },
- 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
- GLEmulation.fogColor[0] = {{{ makeGetValue('param', '0', 'float') }}};
- GLEmulation.fogColor[1] = {{{ makeGetValue('param', '4', 'float') }}};
- 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
-
- glAlphaFunc: function(){}, // TODO
-
- glNormal3f: function(){}, // TODO
-
- // Additional non-GLES rendering calls
-
- glDrawRangeElements__deps: ['glDrawElements'],
- glDrawRangeElements__sig: 'viiiiii',
- glDrawRangeElements: function(mode, start, end, count, type, indices) {
- _glDrawElements(mode, count, type, indices, start, end);
- },
-
- // ClientState/gl*Pointer
-
- glEnableClientState: function(cap) {
- var attrib = GLEmulation.getAttributeFromCapability(cap);
- if (attrib === null) {
-#if ASSERTIONS
- err('WARNING: unhandled clientstate: ' + cap);
-#endif
- return;
- }
- if (!GLImmediate.enabledClientAttributes[attrib]) {
- GLImmediate.enabledClientAttributes[attrib] = true;
- GLImmediate.totalEnabledClientAttributes++;
- GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed.
-#if GL_FFP_ONLY
- // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here.
- GL.enableVertexAttribArray(attrib);
-#endif
- if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1;
- GLImmediate.modifiedClientAttributes = true;
- }
- },
- glDisableClientState: function(cap) {
- var attrib = GLEmulation.getAttributeFromCapability(cap);
- if (attrib === null) {
-#if ASSERTIONS
- err('WARNING: unhandled clientstate: ' + cap);
-#endif
- return;
- }
- if (GLImmediate.enabledClientAttributes[attrib]) {
- GLImmediate.enabledClientAttributes[attrib] = false;
- GLImmediate.totalEnabledClientAttributes--;
- GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed.
-#if GL_FFP_ONLY
- // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here.
- GL.disableVertexAttribArray(attrib);
-#endif
- if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap];
- GLImmediate.modifiedClientAttributes = true;
- }
- },
-
- glVertexPointer__deps: ['$GLEmulation'], // if any pointers are used, glVertexPointer must be, and if it is, then we need emulation
- glVertexPointer: function(size, type, stride, pointer) {
- GLImmediate.setClientAttribute(GLImmediate.VERTEX, size, type, stride, pointer);
-#if GL_FFP_ONLY
- if (GL.currArrayBuffer) {
- GLctx.vertexAttribPointer(GLImmediate.VERTEX, size, type, false, stride, pointer);
- }
-#endif
- },
- glTexCoordPointer: function(size, type, stride, pointer) {
- GLImmediate.setClientAttribute(GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture, size, type, stride, pointer);
-#if GL_FFP_ONLY
- if (GL.currArrayBuffer) {
- var loc = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture;
- GLctx.vertexAttribPointer(loc, size, type, false, stride, pointer);
- }
-#endif
- },
- glNormalPointer: function(type, stride, pointer) {
- GLImmediate.setClientAttribute(GLImmediate.NORMAL, 3, type, stride, pointer);
-#if GL_FFP_ONLY
- if (GL.currArrayBuffer) {
- GLctx.vertexAttribPointer(GLImmediate.NORMAL, 3, type, true, stride, pointer);
- }
-#endif
- },
- glColorPointer: function(size, type, stride, pointer) {
- GLImmediate.setClientAttribute(GLImmediate.COLOR, size, type, stride, pointer);
-#if GL_FFP_ONLY
- if (GL.currArrayBuffer) {
- GLctx.vertexAttribPointer(GLImmediate.COLOR, size, type, true, stride, pointer);
- }
-#endif
- },
-
- glClientActiveTexture__sig: 'vi',
- glClientActiveTexture: function(texture) {
- GLImmediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0
- },
-
- // Vertex array object (VAO) support. TODO: when the WebGL extension is popular, use that and remove this code and GL.vaos
- emulGlGenVertexArrays__deps: ['$GLEmulation'],
- emulGlGenVertexArrays__sig: 'vii',
- emulGlGenVertexArrays: function(n, vaos) {
- for (var i = 0; i < n; i++) {
- var id = GL.getNewId(GLEmulation.vaos);
- GLEmulation.vaos[id] = {
- id: id,
- arrayBuffer: 0,
- elementArrayBuffer: 0,
- enabledVertexAttribArrays: {},
- vertexAttribPointers: {},
- enabledClientStates: {},
- };
- {{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}};
- }
- },
- emulGlDeleteVertexArrays__sig: 'vii',
- emulGlDeleteVertexArrays: function(n, vaos) {
- for (var i = 0; i < n; i++) {
- var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}};
- GLEmulation.vaos[id] = null;
- if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null;
- }
- },
- emulGlIsVertexArray__sig: 'vi',
- emulGlIsVertexArray: function(array) {
- var vao = GLEmulation.vaos[array];
- if (!vao) return 0;
- return 1;
- },
- emulGlBindVertexArray__deps: ['glBindBuffer', 'glEnableVertexAttribArray', 'glVertexAttribPointer', 'glEnableClientState'],
- emulGlBindVertexArray__sig: 'vi',
- emulGlBindVertexArray: function(vao) {
- // undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao
- GLEmulation.currentVao = null; // make sure the commands we run here are not recorded
- if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup();
- _glBindBuffer(GLctx.ARRAY_BUFFER, 0); // XXX if one was there before we were bound?
- _glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, 0);
- for (var vaa in GLEmulation.enabledVertexAttribArrays) {
- GLctx.disableVertexAttribArray(vaa);
- }
- GLEmulation.enabledVertexAttribArrays = {};
- GLImmediate.enabledClientAttributes = [0, 0];
- GLImmediate.totalEnabledClientAttributes = 0;
- GLImmediate.modifiedClientAttributes = true;
- if (vao) {
- // replay vao
- var info = GLEmulation.vaos[vao];
- _glBindBuffer(GLctx.ARRAY_BUFFER, info.arrayBuffer); // XXX overwrite current binding?
- _glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer);
- for (var vaa in info.enabledVertexAttribArrays) {
- _glEnableVertexAttribArray(vaa);
- }
- for (var vaa in info.vertexAttribPointers) {
- _glVertexAttribPointer.apply(null, info.vertexAttribPointers[vaa]);
- }
- for (var attrib in info.enabledClientStates) {
- _glEnableClientState(attrib|0);
- }
- GLEmulation.currentVao = info; // set currentVao last, so the commands we ran here were not recorded
- }
- },
-
- // OpenGL Immediate Mode matrix routines.
- // Note that in the future we might make these available only in certain modes.
- glMatrixMode__deps: ['$GL', '$GLImmediateSetup', '$GLEmulation'], // emulation is not strictly needed, this is a workaround
- glMatrixMode: function(mode) {
- if (mode == 0x1700 /* GL_MODELVIEW */) {
- GLImmediate.currentMatrix = 0/*m*/;
- } else if (mode == 0x1701 /* GL_PROJECTION */) {
- GLImmediate.currentMatrix = 1/*p*/;
- } else if (mode == 0x1702) { // GL_TEXTURE
- GLImmediate.useTextureMatrix = true;
- GLImmediate.currentMatrix = 2/*t*/ + GLImmediate.clientActiveTexture;
- } else {
- throw "Wrong mode " + mode + " passed to glMatrixMode";
- }
- },
-
- glPushMatrix: function() {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixStack[GLImmediate.currentMatrix].push(
- Array.prototype.slice.call(GLImmediate.matrix[GLImmediate.currentMatrix]));
- },
-
- glPopMatrix: function() {
- if (GLImmediate.matrixStack[GLImmediate.currentMatrix].length == 0) {
- GL.recordError(0x0504/*GL_STACK_UNDERFLOW*/);
- return;
- }
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrix[GLImmediate.currentMatrix] = GLImmediate.matrixStack[GLImmediate.currentMatrix].pop();
- },
-
- glLoadIdentity__deps: ['$GL', '$GLImmediateSetup'],
- glLoadIdentity: function() {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[GLImmediate.currentMatrix]);
- },
-
- glLoadMatrixd: function(matrix) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
- },
-
- glLoadMatrixf: function(matrix) {
-#if GL_DEBUG
- if (GL.debug) err('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16)));
-#endif
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
- },
-
- glLoadTransposeMatrixd: function(matrix) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
- GLImmediate.matrixLib.mat4.transpose(GLImmediate.matrix[GLImmediate.currentMatrix]);
- },
-
- glLoadTransposeMatrixf: function(matrix) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
- GLImmediate.matrixLib.mat4.transpose(GLImmediate.matrix[GLImmediate.currentMatrix]);
- },
-
- glMultMatrixd: function(matrix) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
- {{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}});
- },
-
- glMultMatrixf: function(matrix) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
- {{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}});
- },
-
- glMultTransposeMatrixd: function(matrix) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- var colMajor = GLImmediate.matrixLib.mat4.create();
- GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, colMajor);
- GLImmediate.matrixLib.mat4.transpose(colMajor);
- GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], colMajor);
- },
-
- glMultTransposeMatrixf: function(matrix) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- var colMajor = GLImmediate.matrixLib.mat4.create();
- GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, colMajor);
- GLImmediate.matrixLib.mat4.transpose(colMajor);
- GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], colMajor);
- },
-
- glFrustum: function(left, right, bottom, top_, nearVal, farVal) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
- GLImmediate.matrixLib.mat4.frustum(left, right, bottom, top_, nearVal, farVal));
- },
- glFrustumf: 'glFrustum',
-
- glOrtho: function(left, right, bottom, top_, nearVal, farVal) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
- GLImmediate.matrixLib.mat4.ortho(left, right, bottom, top_, nearVal, farVal));
- },
- glOrthof: 'glOrtho',
-
- glScaled: function(x, y, z) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.scale(GLImmediate.matrix[GLImmediate.currentMatrix], [x, y, z]);
- },
- glScalef: 'glScaled',
-
- glTranslated: function(x, y, z) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.translate(GLImmediate.matrix[GLImmediate.currentMatrix], [x, y, z]);
- },
- glTranslatef: 'glTranslated',
-
- glRotated: function(angle, x, y, z) {
- GLImmediate.matricesModified = true;
- GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
- GLImmediate.matrixLib.mat4.rotate(GLImmediate.matrix[GLImmediate.currentMatrix], angle*Math.PI/180, [x, y, z]);
- },
- glRotatef: 'glRotated',
-
- glDrawBuffer: function() { throw 'glDrawBuffer: TODO' },
-#if !USE_WEBGL2
- glReadBuffer: function() { throw 'glReadBuffer: TODO' },
-#endif
-
- glLightfv: function() { throw 'glLightfv: TODO' },
- glLightModelfv: function() { throw 'glLightModelfv: TODO' },
- glMaterialfv: function() { throw 'glMaterialfv: TODO' },
-
- glTexGeni: function() { throw 'glTexGeni: TODO' },
- glTexGenfv: function() { throw 'glTexGenfv: TODO' },
- glTexEnvi: function() { warnOnce('glTexEnvi: TODO') },
- glTexEnvf: function() { warnOnce('glTexEnvf: TODO') },
- glTexEnvfv: function() { warnOnce('glTexEnvfv: TODO') },
-
- glGetTexEnviv: function(target, pname, param) { throw 'GL emulation not initialized!'; },
- glGetTexEnvfv: function(target, pname, param) { throw 'GL emulation not initialized!'; },
-
- glTexImage1D: function() { throw 'glTexImage1D: TODO' },
- glTexCoord3f: function() { throw 'glTexCoord3f: TODO' },
- glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' },
-
- glShadeModel: function() { warnOnce('TODO: glShadeModel') },
-
- // Open GLES1.1 compatibility
-
- glGenFramebuffersOES : 'glGenFramebuffers',
- glGenRenderbuffersOES : 'glGenRenderbuffers',
- glBindFramebufferOES : 'glBindFramebuffer',
- glBindRenderbufferOES : 'glBindRenderbuffer',
- glGetRenderbufferParameterivOES : 'glGetRenderbufferParameteriv',
- glFramebufferRenderbufferOES : 'glFramebufferRenderbuffer',
- glRenderbufferStorageOES : 'glRenderbufferStorage',
- glCheckFramebufferStatusOES : 'glCheckFramebufferStatus',
- glDeleteFramebuffersOES : 'glDeleteFramebuffers',
- glDeleteRenderbuffersOES : 'glDeleteRenderbuffers',
- glFramebufferTexture2DOES: 'glFramebufferTexture2D',
-
-#else // LEGACY_GL_EMULATION
+#if !LEGACY_GL_EMULATION
glVertexPointer: function(){ throw 'Legacy GL function (glVertexPointer) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; },
glMatrixMode: function(){ throw 'Legacy GL function (glMatrixMode) called. If you want legacy GL emulation, you need to compile with -s LEGACY_GL_EMULATION=1 to enable legacy GL emulation.'; },
@@ -7751,6 +4372,7 @@
GLctx.disableVertexAttribArray(index);
},
+#if !LEGACY_GL_EMULATION
glDrawArrays__sig: 'viii',
glDrawArrays: function(mode, first, count) {
#if FULL_ES2
@@ -7794,6 +4416,7 @@
}
#endif
},
+#endif // ~#if !LEGACY_GL_EMULATION
#if USE_WEBGL2
glDrawRangeElements__sig: 'viiiiii',
@@ -8024,48 +4647,44 @@
autoAddDeps(LibraryGL, '$GL');
-// Legacy GL emulation
-if (LEGACY_GL_EMULATION) {
- DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push('$GLEmulation');
+function copyLibEntry(lib, a, b) {
+ lib[a] = lib[b];
+ lib[a + '__postset'] = lib[b + '__postset'];
+ lib[a + '__proxy'] = lib[b + '__proxy'];
+ lib[a + '__sig'] = lib[b + '__sig'];
+ lib[a + '__asm'] = lib[b + '__asm'];
+ lib[a + '__deps'] = (lib[b + '__deps'] || []).slice(0);
}
-function copyLibEntry(a, b) {
- LibraryGL[a] = LibraryGL[b];
- LibraryGL[a + '__postset'] = LibraryGL[b + '__postset'];
- LibraryGL[a + '__proxy'] = LibraryGL[b + '__proxy'];
- LibraryGL[a + '__sig'] = LibraryGL[b + '__sig'];
- LibraryGL[a + '__asm'] = LibraryGL[b + '__asm'];
- LibraryGL[a + '__deps'] = LibraryGL[b + '__deps'].slice(0);
-}
-
-// GL proc address retrieval - allow access through glX and emscripten_glX, to allow name collisions with user-implemented things having the same name (see gl.c)
-keys(LibraryGL).forEach(function(x) {
- if (x.substr(-7) == '__proxy' || x.substr(-6) == '__deps' || x.substr(-9) == '__postset' || x.substr(-5) == '__sig' || x.substr(-5) == '__asm' || x.substr(0, 2) != 'gl') return;
- while (typeof LibraryGL[x] === 'string') {
- // resolve aliases right here, simpler for fastcomp
- copyLibEntry(x, LibraryGL[x]);
- }
- var y = 'emscripten_' + x;
- LibraryGL[x + '__deps'] = LibraryGL[x + '__deps'].map(function(dep) {
- // prefix dependencies as well
- if (typeof dep === 'string' && dep[0] == 'g' && dep[1] == 'l' && LibraryGL[dep]) {
- var orig = dep;
- dep = 'emscripten_' + dep;
- var fixed = LibraryGL[x].toString().replace(new RegExp('_' + orig + '\\(', 'g'), '_' + dep + '(');
- // `function` is 8 characters, add space and an explicit name after if there isn't one already
- if (fixed.startsWith('function(') || fixed.startsWith('function (')) {
- fixed = fixed.substr(0, 8) + ' _' + y + fixed.substr(8);
- }
- LibraryGL[x] = eval('(function() { return ' + fixed + ' })()');
+function recordGLProcAddressGet(lib) {
+ // GL proc address retrieval - allow access through glX and emscripten_glX, to allow name collisions with user-implemented things having the same name (see gl.c)
+ keys(lib).forEach(function(x) {
+ if (x.substr(-7) == '__proxy' || x.substr(-6) == '__deps' || x.substr(-9) == '__postset' || x.substr(-5) == '__sig' || x.substr(-5) == '__asm' || x.substr(0, 2) != 'gl') return;
+ while (typeof lib[x] === 'string') {
+ // resolve aliases right here, simpler for fastcomp
+ copyLibEntry(lib, x, lib[x]);
}
- return dep;
+ var y = 'emscripten_' + x;
+ lib[x + '__deps'] = (lib[x + '__deps'] || []).map(function(dep) {
+ // prefix dependencies as well
+ if (typeof dep === 'string' && dep[0] == 'g' && dep[1] == 'l' && lib[dep]) {
+ var orig = dep;
+ dep = 'emscripten_' + dep;
+ var fixed = lib[x].toString().replace(new RegExp('_' + orig + '\\(', 'g'), '_' + dep + '(');
+ // `function` is 8 characters, add space and an explicit name after if there isn't one already
+ if (fixed.startsWith('function(') || fixed.startsWith('function (')) {
+ fixed = fixed.substr(0, 8) + ' _' + y + fixed.substr(8);
+ }
+ lib[x] = eval('(function() { return ' + fixed + ' })()');
+ }
+ return dep;
+ });
+ // copy it
+ copyLibEntry(lib, y, x);
});
- // copy it
- copyLibEntry(y, x);
-});
+}
+
+recordGLProcAddressGet(LibraryGL);
// Final merge
mergeInto(LibraryManager.library, LibraryGL);
-
-assert(!(FULL_ES2 && LEGACY_GL_EMULATION), 'cannot emulate both ES2 and legacy GL');
-assert(!(FULL_ES3 && LEGACY_GL_EMULATION), 'cannot emulate both ES3 and legacy GL');
diff --git a/src/library_glemu.js b/src/library_glemu.js
new file mode 100644
index 0000000..f2be401
--- /dev/null
+++ b/src/library_glemu.js
@@ -0,0 +1,3389 @@
+/*
+ * Copyright 2010 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.
+ *
+ * Desktop OpenGL emulation support. See http://kripken.github.io/emscripten-site/docs/porting/multimedia_and_graphics/OpenGL-support.html
+ * for current status.
+ */
+
+var LibraryGLEmulation = {
+ // GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL
+ $GLEmulation__deps: ['$GLImmediateSetup', 'glEnable', 'glDisable', 'glIsEnabled', 'glGetBooleanv', 'glGetIntegerv', 'glGetString', 'glCreateShader', 'glShaderSource', 'glCompileShader', 'glAttachShader', 'glDetachShader', 'glUseProgram', 'glDeleteProgram', 'glBindAttribLocation', 'glLinkProgram', 'glBindBuffer', 'glGetFloatv', 'glHint', 'glEnableVertexAttribArray', 'glDisableVertexAttribArray', 'glVertexAttribPointer', 'glActiveTexture'],
+ $GLEmulation__postset: 'GLEmulation.init();',
+ $GLEmulation: {
+ // 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,
+
+ // VAO support
+ vaos: [],
+ currentVao: null,
+ enabledVertexAttribArrays: {}, // helps with vao cleanups
+
+ hasRunInit: false,
+
+ // Find a token in a shader source string
+ findToken: function(source, token) {
+ function isIdentChar(ch) {
+ if (ch >= 48 && ch <= 57) // 0-9
+ return true;
+ if (ch >= 65 && ch <= 90) // A-Z
+ return true;
+ if (ch >= 97 && ch <= 122) // a-z
+ return true;
+ return false;
+ }
+ var i = -1;
+ do {
+ i = source.indexOf(token, i + 1);
+ if (i < 0) {
+ break;
+ }
+ if (i > 0 && isIdentChar(source[i - 1])) {
+ continue;
+ }
+ i += token.length;
+ if (i < source.length - 1 && isIdentChar(source[i + 1])) {
+ continue;
+ }
+ return true;
+ } while (true);
+ return false;
+ },
+
+ init: function() {
+ // Do not activate immediate/emulation code (e.g. replace glDrawElements) when in FULL_ES2 mode.
+ // We do not need full emulation, we instead emulate client-side arrays etc. in FULL_ES2 code in
+ // a straightforward manner, and avoid not having a bound buffer be ambiguous between es2 emulation
+ // code and legacy gl emulation code.
+#if FULL_ES2
+ return;
+#endif
+
+ if (GLEmulation.hasRunInit) {
+ return;
+ }
+ GLEmulation.hasRunInit = true;
+
+ GLEmulation.fogColor = new Float32Array(4);
+
+ // Add some emulation workarounds
+ err('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work.');
+#if GL_UNSAFE_OPTS == 1
+ err('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -s GL_UNSAFE_OPTS=0');
+#endif
+
+ // 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
+ };
+
+#if RELOCATABLE
+{{{
+(updateExport = function(name) {
+ var name = '_' + name;
+ var exported = 'Module["' + name + '"]';
+ // make sure we write to an existing export, and are not repeating ourselves
+ return 'assert(' + exported + ' !== ' + name + '); ' + exported + ' = ' + name + ';';
+}, '')
+}}}
+#else
+{{{ (updateExport = function(){ return '' }, '') }}}
+#endif
+
+ var glEnable = _glEnable;
+ _glEnable = _emscripten_glEnable = function _glEnable(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 (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup();
+ if (cap == 0x0B60 /* GL_FOG */) {
+ if (GLEmulation.fogEnabled != true) {
+ GLImmediate.currentRenderer = null; // Fog parameter is part of the FFP shader state, we must re-lookup the renderer to use.
+ GLEmulation.fogEnabled = true;
+ }
+ return;
+ } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) {
+ // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
+ // it by forwarding to glEnableClientState
+ /* Actually, let's not, for now. (This sounds exceedingly broken)
+ * This is in gl_ps_workaround2.c.
+ _glEnableClientState(cap);
+ */
+ return;
+ } else if (!(cap in validCapabilities)) {
+ return;
+ }
+ glEnable(cap);
+ };
+ {{{ updateExport('glEnable') }}}
+
+ var glDisable = _glDisable;
+ _glDisable = _emscripten_glDisable = function _glDisable(cap) {
+ if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup();
+ if (cap == 0x0B60 /* GL_FOG */) {
+ if (GLEmulation.fogEnabled != false) {
+ GLImmediate.currentRenderer = null; // Fog parameter is part of the FFP shader state, we must re-lookup the renderer to use.
+ GLEmulation.fogEnabled = false;
+ }
+ return;
+ } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) {
+ // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
+ // it by forwarding to glDisableClientState
+ /* Actually, let's not, for now. (This sounds exceedingly broken)
+ * This is in gl_ps_workaround2.c.
+ _glDisableClientState(cap);
+ */
+ return;
+ } else if (!(cap in validCapabilities)) {
+ return;
+ }
+ glDisable(cap);
+ };
+ {{{ updateExport('glDisable') }}}
+
+ _glIsEnabled = _emscripten_glIsEnabled = function _glIsEnabled(cap) {
+ if (cap == 0x0B60 /* GL_FOG */) {
+ return GLEmulation.fogEnabled ? 1 : 0;
+ } else if (!(cap in validCapabilities)) {
+ return 0;
+ }
+ return GLctx.isEnabled(cap);
+ };
+ {{{ updateExport('glIsEnabled') }}}
+
+ var glGetBooleanv = _glGetBooleanv;
+ _glGetBooleanv = _emscripten_glGetBooleanv = function _glGetBooleanv(pname, p) {
+ var attrib = GLEmulation.getAttributeFromCapability(pname);
+ if (attrib !== null) {
+ var result = GLImmediate.enabledClientAttributes[attrib];
+ {{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}};
+ return;
+ }
+ glGetBooleanv(pname, p);
+ };
+ {{{ updateExport('glGetBooleanv') }}}
+
+ var glGetIntegerv = _glGetIntegerv;
+ _glGetIntegerv = _emscripten_glGetIntegerv = function _glGetIntegerv(pname, params) {
+ switch (pname) {
+ case 0x84E2: pname = GLctx.MAX_TEXTURE_IMAGE_UNITS /* fake it */; break; // GL_MAX_TEXTURE_UNITS
+ case 0x8B4A: { // GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB
+ var result = GLctx.getParameter(GLctx.MAX_VERTEX_UNIFORM_VECTORS);
+ {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
+ return;
+ }
+ case 0x8B49: { // GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB
+ var result = GLctx.getParameter(GLctx.MAX_FRAGMENT_UNIFORM_VECTORS);
+ {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
+ return;
+ }
+ case 0x8B4B: { // GL_MAX_VARYING_FLOATS_ARB
+ var result = GLctx.getParameter(GLctx.MAX_VARYING_VECTORS);
+ {{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
+ return;
+ }
+ case 0x8871: pname = GLctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS
+ case 0x807A: { // GL_VERTEX_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x807B: { // GL_VERTEX_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x807C: { // GL_VERTEX_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ case 0x8081: { // GL_COLOR_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x8082: { // GL_COLOR_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x8083: { // GL_COLOR_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ }
+ glGetIntegerv(pname, params);
+ };
+ {{{ updateExport('glGetIntegerv') }}}
+
+ var glGetString = _glGetString;
+ _glGetString = _emscripten_glGetString = function _glGetString(name_) {
+ if (GL.stringCache[name_]) return GL.stringCache[name_];
+ switch(name_) {
+ case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support
+ var ret = allocate(intArrayFromString(GLctx.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.currentContext.compressionExt ? ' GL_ARB_texture_compression GL_EXT_texture_compression_s3tc' : '') +
+ (GL.currentContext.anisotropicExt ? ' GL_EXT_texture_filter_anisotropic' : '')
+ ), 'i8', ALLOC_NORMAL);
+ GL.stringCache[name_] = ret;
+ return ret;
+ }
+ return glGetString(name_);
+ };
+ {{{ updateExport('glGetString') }}}
+
+ // 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.shaderInfos = {};
+#if GL_DEBUG
+ GL.shaderSources = {};
+ GL.shaderOriginalSources = {};
+#endif
+ var glCreateShader = _glCreateShader;
+ _glCreateShader = _emscripten_glCreateShader = function _glCreateShader(shaderType) {
+ var id = glCreateShader(shaderType);
+ GL.shaderInfos[id] = {
+ type: shaderType,
+ ftransform: false
+ };
+ return id;
+ };
+ {{{ updateExport('glCreateShader') }}}
+
+ function ensurePrecision(source) {
+ if (!/precision +(low|medium|high)p +float *;/.test(source)) {
+ source = 'precision mediump float;\n' + source;
+ }
+ return source;
+ }
+
+ var glShaderSource = _glShaderSource;
+ _glShaderSource = _emscripten_glShaderSource = function _glShaderSource(shader, count, string, length) {
+ var source = GL.getSource(shader, count, string, length);
+#if GL_DEBUG
+ console.log("glShaderSource: Input: \n" + source);
+ GL.shaderOriginalSources[shader] = source;
+#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 == GLctx.VERTEX_SHADER) {
+ // Replace ftransform() with explicit project/modelview transforms, and add position and matrix info.
+ 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_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][2], u_modelView[1][2], u_modelView[2][2], u_modelView[3][2])'); // XXX extremely inefficient
+ if (old != source) need_mm = 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;
+ 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 < GLImmediate.MAX_TEXTURES; i++) {
+ // XXX To handle both regular texture mapping and cube mapping, we use vec4 for tex coordinates.
+ 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' + 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' +
+ source.replace(/gl_Color/g, 'a_color');
+ }
+ if (source.indexOf('gl_Normal') >= 0) {
+ source = 'attribute vec3 a_normal; \n' +
+ source.replace(/gl_Normal/g, 'a_normal');
+ }
+ // fog
+ if (source.indexOf('gl_FogFragCoord') >= 0) {
+ source = 'varying float v_fogFragCoord; \n' +
+ source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord');
+ }
+ source = ensurePrecision(source);
+ } else { // Fragment shader
+ for (i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
+ 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;
+ }
+ }
+ if (source.indexOf('gl_Color') >= 0) {
+ source = 'varying vec4 v_color; \n' + source.replace(/gl_Color/g, 'v_color');
+ }
+ if (source.indexOf('gl_Fog.color') >= 0) {
+ source = 'uniform vec4 u_fogColor; \n' +
+ source.replace(/gl_Fog.color/g, 'u_fogColor');
+ }
+ if (source.indexOf('gl_Fog.end') >= 0) {
+ source = 'uniform float u_fogEnd; \n' +
+ source.replace(/gl_Fog.end/g, 'u_fogEnd');
+ }
+ if (source.indexOf('gl_Fog.scale') >= 0) {
+ 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');
+ }
+ source = ensurePrecision(source);
+ }
+#if GL_DEBUG
+ GL.shaderSources[shader] = source;
+ console.log("glShaderSource: Output: \n" + source);
+#endif
+ GLctx.shaderSource(GL.shaders[shader], source);
+ };
+ {{{ updateExport('glShaderSource') }}}
+
+ var glCompileShader = _glCompileShader;
+ _glCompileShader = _emscripten_glCompileShader = function _glCompileShader(shader) {
+ GLctx.compileShader(GL.shaders[shader]);
+#if GL_DEBUG
+ if (!GLctx.getShaderParameter(GL.shaders[shader], GLctx.COMPILE_STATUS)) {
+ err('Failed to compile shader: ' + GLctx.getShaderInfoLog(GL.shaders[shader]));
+ err('Info: ' + JSON.stringify(GL.shaderInfos[shader]));
+ err('Original source: ' + GL.shaderOriginalSources[shader]);
+ err('Source: ' + GL.shaderSources[shader]);
+ throw 'Shader compilation halt';
+ }
+#endif
+ };
+ {{{ updateExport('glCompileShader') }}}
+
+ GL.programShaders = {};
+ var glAttachShader = _glAttachShader;
+ _glAttachShader = _emscripten_glAttachShader = function _glAttachShader(program, shader) {
+ if (!GL.programShaders[program]) GL.programShaders[program] = [];
+ GL.programShaders[program].push(shader);
+ glAttachShader(program, shader);
+ };
+ {{{ updateExport('glAttachShader') }}}
+
+ var glDetachShader = _glDetachShader;
+ _glDetachShader = _emscripten_glDetachShader = function _glDetachShader(program, shader) {
+ var programShader = GL.programShaders[program];
+ if (!programShader) {
+ err('WARNING: _glDetachShader received invalid program: ' + program);
+ return;
+ }
+ var index = programShader.indexOf(shader);
+ programShader.splice(index, 1);
+ glDetachShader(program, shader);
+ };
+ {{{ updateExport('glDetachShader') }}}
+
+ var glUseProgram = _glUseProgram;
+ _glUseProgram = _emscripten_glUseProgram = function _glUseProgram(program) {
+#if GL_DEBUG
+ if (GL.debug) {
+ err('[using program with shaders]');
+ if (program) {
+ GL.programShaders[program].forEach(function(shader) {
+ err(' shader ' + shader + ', original source: ' + GL.shaderOriginalSources[shader]);
+ err(' Source: ' + GL.shaderSources[shader]);
+ });
+ }
+ }
+#endif
+ if (GL.currProgram != program) {
+ GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
+ GL.currProgram = program;
+ GLImmediate.fixedFunctionProgram = 0;
+ glUseProgram(program);
+ }
+ }
+ {{{ updateExport('glUseProgram') }}}
+
+ var glDeleteProgram = _glDeleteProgram;
+ _glDeleteProgram = _emscripten_glDeleteProgram = function _glDeleteProgram(program) {
+ glDeleteProgram(program);
+ if (program == GL.currProgram) {
+ GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
+ GL.currProgram = 0;
+ }
+ };
+ {{{ updateExport('glDeleteProgram') }}}
+
+ // If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that.
+ var zeroUsedPrograms = {};
+ var glBindAttribLocation = _glBindAttribLocation;
+ _glBindAttribLocation = _emscripten_glBindAttribLocation = function _glBindAttribLocation(program, index, name) {
+ if (index == 0) zeroUsedPrograms[program] = true;
+ glBindAttribLocation(program, index, name);
+ };
+ {{{ updateExport('glBindAttribLocation') }}}
+
+ var glLinkProgram = _glLinkProgram;
+ _glLinkProgram = _emscripten_glLinkProgram = function _glLinkProgram(program) {
+ if (!(program in zeroUsedPrograms)) {
+ GLctx.bindAttribLocation(GL.programs[program], 0, 'a_position');
+ }
+ glLinkProgram(program);
+ };
+ {{{ updateExport('glLinkProgram') }}}
+
+ var glBindBuffer = _glBindBuffer;
+ _glBindBuffer = _emscripten_glBindBuffer = function _glBindBuffer(target, buffer) {
+ glBindBuffer(target, buffer);
+ if (target == GLctx.ARRAY_BUFFER) {
+ if (GLEmulation.currentVao) {
+#if ASSERTIONS
+ assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao');
+#endif
+ GLEmulation.currentVao.arrayBuffer = buffer;
+ }
+ } else if (target == GLctx.ELEMENT_ARRAY_BUFFER) {
+ if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer;
+ }
+ };
+ {{{ updateExport('glBindBuffer') }}}
+
+ var glGetFloatv = _glGetFloatv;
+ _glGetFloatv = _emscripten_glGetFloatv = function _glGetFloatv(pname, params) {
+ if (pname == 0x0BA6) { // GL_MODELVIEW_MATRIX
+ HEAPF32.set(GLImmediate.matrix[0/*m*/], params >> 2);
+ } else if (pname == 0x0BA7) { // GL_PROJECTION_MATRIX
+ HEAPF32.set(GLImmediate.matrix[1/*p*/], params >> 2);
+ } else if (pname == 0x0BA8) { // GL_TEXTURE_MATRIX
+ HEAPF32.set(GLImmediate.matrix[2/*t*/ + GLImmediate.clientActiveTexture], params >> 2);
+ } else if (pname == 0x0B66) { // GL_FOG_COLOR
+ HEAPF32.set(GLEmulation.fogColor, params >> 2);
+ } else if (pname == 0x0B63) { // GL_FOG_START
+ {{{ 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);
+ }
+ };
+ {{{ updateExport('glGetFloatv') }}}
+
+ var glHint = _glHint;
+ _glHint = _emscripten_glHint = function _glHint(target, mode) {
+ if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT
+ return;
+ }
+ glHint(target, mode);
+ };
+ {{{ updateExport('glHint') }}}
+
+ var glEnableVertexAttribArray = _glEnableVertexAttribArray;
+ _glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray = function _glEnableVertexAttribArray(index) {
+ glEnableVertexAttribArray(index);
+ GLEmulation.enabledVertexAttribArrays[index] = 1;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
+ };
+ {{{ updateExport('glEnableVertexAttribArray') }}}
+
+ var glDisableVertexAttribArray = _glDisableVertexAttribArray;
+ _glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray = function _glDisableVertexAttribArray(index) {
+ glDisableVertexAttribArray(index);
+ delete GLEmulation.enabledVertexAttribArrays[index];
+ if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
+ };
+ {{{ updateExport('glDisableVertexAttribArray') }}}
+
+ var glVertexAttribPointer = _glVertexAttribPointer;
+ _glVertexAttribPointer = _emscripten_glVertexAttribPointer = function _glVertexAttribPointer(index, size, type, normalized, stride, pointer) {
+ glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though
+ GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
+ }
+ };
+ {{{ updateExport('glVertexAttribPointer') }}}
+ },
+
+ getAttributeFromCapability: function(cap) {
+ var attrib = null;
+ switch (cap) {
+ case 0x0de1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it
+#if ASSERTIONS
+ abort("GL_TEXTURE_2D is not a spec-defined capability for gl{Enable,Disable}ClientState.");
+#endif
+ // Fall through:
+ case 0x8078: // GL_TEXTURE_COORD_ARRAY
+ attrib = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture; break;
+ case 0x8074: // GL_VERTEX_ARRAY
+ attrib = GLImmediate.VERTEX; break;
+ case 0x8075: // GL_NORMAL_ARRAY
+ attrib = GLImmediate.NORMAL; break;
+ case 0x8076: // GL_COLOR_ARRAY
+ attrib = GLImmediate.COLOR; break;
+ }
+ return attrib;
+ },
+ },
+
+ glDeleteObject__deps: ['glDeleteProgram', 'glDeleteShader'],
+ glDeleteObject__sig: 'vi',
+ glDeleteObject: function(id) {
+ if (GL.programs[id]) {
+ _glDeleteProgram(id);
+ } else if (GL.shaders[id]) {
+ _glDeleteShader(id);
+ } else {
+ err('WARNING: deleteObject received invalid id: ' + id);
+ }
+ },
+ glDeleteObjectARB: 'glDeleteObject',
+
+ glGetObjectParameteriv__sig: 'viii',
+ glGetObjectParameteriv__deps: ['glGetProgramiv', 'glGetShaderiv'],
+ glGetObjectParameteriv: function(id, type, result) {
+ if (GL.programs[id]) {
+ if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
+ var log = GLctx.getProgramInfoLog(GL.programs[id]);
+ if (log === null) log = '(unknown error)';
+ {{{ makeSetValue('result', '0', 'log.length', 'i32') }}};
+ return;
+ }
+ _glGetProgramiv(id, type, result);
+ } else if (GL.shaders[id]) {
+ if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
+ var log = GLctx.getShaderInfoLog(GL.shaders[id]);
+ if (log === null) log = '(unknown error)';
+ {{{ makeSetValue('result', '0', 'log.length', 'i32') }}};
+ return;
+ } else if (type == 0x8B88) { // GL_OBJECT_SHADER_SOURCE_LENGTH_ARB
+ var source = GLctx.getShaderSource(GL.shaders[id]);
+ if (source === null) return; // If an error occurs, nothing will be written to result
+ {{{ makeSetValue('result', '0', 'source.length', 'i32') }}};
+ return;
+ }
+ _glGetShaderiv(id, type, result);
+ } else {
+ err('WARNING: getObjectParameteriv received invalid id: ' + id);
+ }
+ },
+ glGetObjectParameterivARB: 'glGetObjectParameteriv',
+
+ glGetInfoLog__deps: ['glGetProgramInfoLog', 'glGetShaderInfoLog'],
+ glGetInfoLog__sig: 'viiii',
+ glGetInfoLog: 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 {
+ err('WARNING: glGetInfoLog received invalid id: ' + id);
+ }
+ },
+ glGetInfoLogARB: 'glGetInfoLog',
+
+ glBindProgram__sig: 'vii',
+ glBindProgram: function(type, id) {
+#if ASSERTIONS
+ assert(id == 0);
+#endif
+ },
+ glBindProgramARB: 'glBindProgram',
+
+ glGetPointerv: function(name, p) {
+ var attribute;
+ switch(name) {
+ case 0x808E: // GL_VERTEX_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break;
+ case 0x8090: // GL_COLOR_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
+ case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break;
+ default:
+ GL.recordError(0x0500/*GL_INVALID_ENUM*/);
+#if GL_ASSERTIONS
+ err('GL_INVALID_ENUM in glGetPointerv: Unsupported name ' + name + '!');
+#endif
+ return;
+ }
+ {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
+ },
+
+ // GL Immediate mode
+
+ // See comment in GLEmulation.init()
+#if !FULL_ES2
+ $GLImmediate__postset: 'GLImmediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GLImmediate.init() });',
+#endif
+ $GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'],
+ $GLImmediate: {
+ MapTreeLib: null,
+ spawnMapTreeLib: function() {
+ /* A naive implementation of a map backed by an array, and accessed by
+ * naive iteration along the array. (hashmap with only one bucket)
+ */
+ function CNaiveListMap() {
+ var list = [];
+
+ this.insert = function CNaiveListMap_insert(key, val) {
+ if (this.contains(key|0)) return false;
+ list.push([key, val]);
+ return true;
+ };
+
+ var __contains_i;
+ this.contains = function CNaiveListMap_contains(key) {
+ for (__contains_i = 0; __contains_i < list.length; ++__contains_i) {
+ if (list[__contains_i][0] === key) return true;
+ }
+ return false;
+ };
+
+ var __get_i;
+ this.get = function CNaiveListMap_get(key) {
+ for (__get_i = 0; __get_i < list.length; ++__get_i) {
+ if (list[__get_i][0] === key) return list[__get_i][1];
+ }
+ return undefined;
+ };
+ };
+
+ /* A tree of map nodes.
+ Uses `KeyView`s to allow descending the tree without garbage.
+ Example: {
+ // Create our map object.
+ var map = new ObjTreeMap();
+
+ // Grab the static keyView for the map.
+ var keyView = map.GetStaticKeyView();
+
+ // Let's make a map for:
+ // root: <undefined>
+ // 1: <undefined>
+ // 2: <undefined>
+ // 5: "Three, sir!"
+ // 3: "Three!"
+
+ // Note how we can chain together `Reset` and `Next` to
+ // easily descend based on multiple key fragments.
+ keyView.Reset().Next(1).Next(2).Next(5).Set("Three, sir!");
+ keyView.Reset().Next(1).Next(2).Next(3).Set("Three!");
+ }
+ */
+ function CMapTree() {
+ function CNLNode() {
+ var map = new CNaiveListMap();
+
+ this.child = function CNLNode_child(keyFrag) {
+ if (!map.contains(keyFrag|0)) {
+ map.insert(keyFrag|0, new CNLNode());
+ }
+ return map.get(keyFrag|0);
+ };
+
+ this.value = undefined;
+ this.get = function CNLNode_get() {
+ return this.value;
+ };
+
+ this.set = function CNLNode_set(val) {
+ this.value = val;
+ };
+ }
+
+ function CKeyView(root) {
+ var cur;
+
+ this.reset = function CKeyView_reset() {
+ cur = root;
+ return this;
+ };
+ this.reset();
+
+ this.next = function CKeyView_next(keyFrag) {
+ cur = cur.child(keyFrag);
+ return this;
+ };
+
+ this.get = function CKeyView_get() {
+ return cur.get();
+ };
+
+ this.set = function CKeyView_set(val) {
+ cur.set(val);
+ };
+ };
+
+ var root;
+ var staticKeyView;
+
+ this.createKeyView = function CNLNode_createKeyView() {
+ return new CKeyView(root);
+ }
+
+ this.clear = function CNLNode_clear() {
+ root = new CNLNode();
+ staticKeyView = this.createKeyView();
+ };
+ this.clear();
+
+ this.getStaticKeyView = function CNLNode_getStaticKeyView() {
+ staticKeyView.reset();
+ return staticKeyView;
+ };
+ };
+
+ // Exports:
+ return {
+ create: function() {
+ return new CMapTree();
+ },
+ };
+ },
+
+ TexEnvJIT: null,
+ spawnTexEnvJIT: function() {
+ // GL defs:
+ var GL_TEXTURE0 = 0x84C0;
+ var GL_TEXTURE_1D = 0x0DE0;
+ var GL_TEXTURE_2D = 0x0DE1;
+ var GL_TEXTURE_3D = 0x806f;
+ var GL_TEXTURE_CUBE_MAP = 0x8513;
+ var GL_TEXTURE_ENV = 0x2300;
+ var GL_TEXTURE_ENV_MODE = 0x2200;
+ var GL_TEXTURE_ENV_COLOR = 0x2201;
+ var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
+ var GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
+ var GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
+ var GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
+ var GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
+ var GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
+
+ var GL_SRC0_RGB = 0x8580;
+ var GL_SRC1_RGB = 0x8581;
+ var GL_SRC2_RGB = 0x8582;
+
+ var GL_SRC0_ALPHA = 0x8588;
+ var GL_SRC1_ALPHA = 0x8589;
+ var GL_SRC2_ALPHA = 0x858A;
+
+ var GL_OPERAND0_RGB = 0x8590;
+ var GL_OPERAND1_RGB = 0x8591;
+ var GL_OPERAND2_RGB = 0x8592;
+
+ var GL_OPERAND0_ALPHA = 0x8598;
+ var GL_OPERAND1_ALPHA = 0x8599;
+ var GL_OPERAND2_ALPHA = 0x859A;
+
+ var GL_COMBINE_RGB = 0x8571;
+ var GL_COMBINE_ALPHA = 0x8572;
+
+ var GL_RGB_SCALE = 0x8573;
+ var GL_ALPHA_SCALE = 0x0D1C;
+
+ // env.mode
+ var GL_ADD = 0x0104;
+ var GL_BLEND = 0x0BE2;
+ var GL_REPLACE = 0x1E01;
+ var GL_MODULATE = 0x2100;
+ var GL_DECAL = 0x2101;
+ var GL_COMBINE = 0x8570;
+
+ // env.color/alphaCombiner
+ //var GL_ADD = 0x0104;
+ //var GL_REPLACE = 0x1E01;
+ //var GL_MODULATE = 0x2100;
+ var GL_SUBTRACT = 0x84E7;
+ var GL_INTERPOLATE = 0x8575;
+
+ // env.color/alphaSrc
+ var GL_TEXTURE = 0x1702;
+ var GL_CONSTANT = 0x8576;
+ var GL_PRIMARY_COLOR = 0x8577;
+ var GL_PREVIOUS = 0x8578;
+
+ // env.color/alphaOp
+ var GL_SRC_COLOR = 0x0300;
+ var GL_ONE_MINUS_SRC_COLOR = 0x0301;
+ var GL_SRC_ALPHA = 0x0302;
+ var GL_ONE_MINUS_SRC_ALPHA = 0x0303;
+
+ var GL_RGB = 0x1907;
+ var GL_RGBA = 0x1908;
+
+ // Our defs:
+ var TEXENVJIT_NAMESPACE_PREFIX = "tej_";
+ // Not actually constant, as they can be changed between JIT passes:
+ var TEX_UNIT_UNIFORM_PREFIX = "uTexUnit";
+ var TEX_COORD_VARYING_PREFIX = "vTexCoord";
+ var PRIM_COLOR_VARYING = "vPrimColor";
+ var TEX_MATRIX_UNIFORM_PREFIX = "uTexMatrix";
+
+ // Static vars:
+ var s_texUnits = null; //[];
+ var s_activeTexture = 0;
+
+ var s_requiredTexUnitsForPass = [];
+
+ // Static funcs:
+ function abort(info) {
+ assert(false, "[TexEnvJIT] ABORT: " + info);
+ }
+
+ function abort_noSupport(info) {
+ abort("No support: " + info);
+ }
+
+ function abort_sanity(info) {
+ abort("Sanity failure: " + info);
+ }
+
+ function genTexUnitSampleExpr(texUnitID) {
+ var texUnit = s_texUnits[texUnitID];
+ var texType = texUnit.getTexType();
+
+ var func = null;
+ switch (texType) {
+ case GL_TEXTURE_1D:
+ func = "texture2D";
+ break;
+ case GL_TEXTURE_2D:
+ func = "texture2D";
+ break;
+ case GL_TEXTURE_3D:
+ return abort_noSupport("No support for 3D textures.");
+ case GL_TEXTURE_CUBE_MAP:
+ func = "textureCube";
+ break;
+ default:
+ return abort_sanity("Unknown texType: 0x" + texType.toString(16));
+ }
+
+ var texCoordExpr = TEX_COORD_VARYING_PREFIX + texUnitID;
+ if (TEX_MATRIX_UNIFORM_PREFIX != null) {
+ texCoordExpr = "(" + TEX_MATRIX_UNIFORM_PREFIX + texUnitID + " * " + texCoordExpr + ")";
+ }
+ return func + "(" + TEX_UNIT_UNIFORM_PREFIX + texUnitID + ", " + texCoordExpr + ".xy)";
+ }
+
+ function getTypeFromCombineOp(op) {
+ switch (op) {
+ case GL_SRC_COLOR:
+ case GL_ONE_MINUS_SRC_COLOR:
+ return "vec3";
+ case GL_SRC_ALPHA:
+ case GL_ONE_MINUS_SRC_ALPHA:
+ return "float";
+ }
+
+ return abort_noSupport("Unsupported combiner op: 0x" + op.toString(16));
+ }
+
+ function getCurTexUnit() {
+ return s_texUnits[s_activeTexture];
+ }
+
+ function genCombinerSourceExpr(texUnitID, constantExpr, previousVar,
+ src, op)
+ {
+ var srcExpr = null;
+ switch (src) {
+ case GL_TEXTURE:
+ srcExpr = genTexUnitSampleExpr(texUnitID);
+ break;
+ case GL_CONSTANT:
+ srcExpr = constantExpr;
+ break;
+ case GL_PRIMARY_COLOR:
+ srcExpr = PRIM_COLOR_VARYING;
+ break;
+ case GL_PREVIOUS:
+ srcExpr = previousVar;
+ break;
+ default:
+ return abort_noSupport("Unsupported combiner src: 0x" + src.toString(16));
+ }
+
+ var expr = null;
+ switch (op) {
+ case GL_SRC_COLOR:
+ expr = srcExpr + ".rgb";
+ break;
+ case GL_ONE_MINUS_SRC_COLOR:
+ expr = "(vec3(1.0) - " + srcExpr + ".rgb)";
+ break;
+ case GL_SRC_ALPHA:
+ expr = srcExpr + ".a";
+ break;
+ case GL_ONE_MINUS_SRC_ALPHA:
+ expr = "(1.0 - " + srcExpr + ".a)";
+ break;
+ default:
+ return abort_noSupport("Unsupported combiner op: 0x" + op.toString(16));
+ }
+
+ return expr;
+ }
+
+ function valToFloatLiteral(val) {
+ if (val == Math.round(val)) return val + '.0';
+ return val;
+ }
+
+
+ // Classes:
+ function CTexEnv() {
+ this.mode = GL_MODULATE;
+ this.colorCombiner = GL_MODULATE;
+ this.alphaCombiner = GL_MODULATE;
+ this.colorScale = 1;
+ this.alphaScale = 1;
+ this.envColor = [0, 0, 0, 0];
+
+ this.colorSrc = [
+ GL_TEXTURE,
+ GL_PREVIOUS,
+ GL_CONSTANT
+ ];
+ this.alphaSrc = [
+ GL_TEXTURE,
+ GL_PREVIOUS,
+ GL_CONSTANT
+ ];
+ this.colorOp = [
+ GL_SRC_COLOR,
+ GL_SRC_COLOR,
+ GL_SRC_ALPHA
+ ];
+ this.alphaOp = [
+ GL_SRC_ALPHA,
+ GL_SRC_ALPHA,
+ GL_SRC_ALPHA
+ ];
+
+ // Map GLenums to small values to efficiently pack the enums to bits for tighter access.
+ this.traverseKey = {
+ // mode
+ 0x1E01 /* GL_REPLACE */: 0,
+ 0x2100 /* GL_MODULATE */: 1,
+ 0x0104 /* GL_ADD */: 2,
+ 0x0BE2 /* GL_BLEND */: 3,
+ 0x2101 /* GL_DECAL */: 4,
+ 0x8570 /* GL_COMBINE */: 5,
+
+ // additional color and alpha combiners
+ 0x84E7 /* GL_SUBTRACT */: 3,
+ 0x8575 /* GL_INTERPOLATE */: 4,
+
+ // color and alpha src
+ 0x1702 /* GL_TEXTURE */: 0,
+ 0x8576 /* GL_CONSTANT */: 1,
+ 0x8577 /* GL_PRIMARY_COLOR */: 2,
+ 0x8578 /* GL_PREVIOUS */: 3,
+
+ // color and alpha op
+ 0x0300 /* GL_SRC_COLOR */: 0,
+ 0x0301 /* GL_ONE_MINUS_SRC_COLOR */: 1,
+ 0x0302 /* GL_SRC_ALPHA */: 2,
+ 0x0303 /* GL_ONE_MINUS_SRC_ALPHA */: 3
+ };
+
+ // The tuple (key0,key1,key2) uniquely identifies the state of the variables in CTexEnv.
+ // -1 on key0 denotes 'the whole cached key is dirty'
+ this.key0 = -1;
+ this.key1 = 0;
+ this.key2 = 0;
+
+ this.computeKey0 = function() {
+ var k = this.traverseKey;
+ var key = k[this.mode] * 1638400; // 6 distinct values.
+ key += k[this.colorCombiner] * 327680; // 5 distinct values.
+ key += k[this.alphaCombiner] * 65536; // 5 distinct values.
+ // The above three fields have 6*5*5=150 distinct values -> 8 bits.
+ key += (this.colorScale-1) * 16384; // 10 bits used.
+ key += (this.alphaScale-1) * 4096; // 12 bits used.
+ key += k[this.colorSrc[0]] * 1024; // 14
+ key += k[this.colorSrc[1]] * 256; // 16
+ key += k[this.colorSrc[2]] * 64; // 18
+ key += k[this.alphaSrc[0]] * 16; // 20
+ key += k[this.alphaSrc[1]] * 4; // 22
+ key += k[this.alphaSrc[2]]; // 24 bits used total.
+ return key;
+ }
+ this.computeKey1 = function() {
+ var k = this.traverseKey;
+ key = k[this.colorOp[0]] * 4096;
+ key += k[this.colorOp[1]] * 1024;
+ key += k[this.colorOp[2]] * 256;
+ key += k[this.alphaOp[0]] * 16;
+ key += k[this.alphaOp[1]] * 4;
+ key += k[this.alphaOp[2]];
+ return key;
+ }
+ // TODO: remove this. The color should not be part of the key!
+ this.computeKey2 = function() {
+ return this.envColor[0] * 16777216 + this.envColor[1] * 65536 + this.envColor[2] * 256 + 1 + this.envColor[3];
+ }
+ this.recomputeKey = function() {
+ this.key0 = this.computeKey0();
+ this.key1 = this.computeKey1();
+ this.key2 = this.computeKey2();
+ }
+ this.invalidateKey = function() {
+ this.key0 = -1; // The key of this texture unit must be recomputed when rendering the next time.
+ GLImmediate.currentRenderer = null; // The currently used renderer must be re-evaluated at next render.
+ }
+ }
+
+ function CTexUnit() {
+ this.env = new CTexEnv();
+ this.enabled_tex1D = false;
+ this.enabled_tex2D = false;
+ this.enabled_tex3D = false;
+ this.enabled_texCube = false;
+ this.texTypesEnabled = 0; // A bitfield combination of the four flags above, used for fast access to operations.
+
+ this.traverseState = function CTexUnit_traverseState(keyView) {
+ if (this.texTypesEnabled) {
+ if (this.env.key0 == -1) {
+ this.env.recomputeKey();
+ }
+ keyView.next(this.texTypesEnabled | (this.env.key0 << 4));
+ keyView.next(this.env.key1);
+ keyView.next(this.env.key2);
+ } else {
+ // For correctness, must traverse a zero value, theoretically a subsequent integer key could collide with this value otherwise.
+ keyView.next(0);
+ }
+ };
+ };
+
+ // Class impls:
+ CTexUnit.prototype.enabled = function CTexUnit_enabled() {
+ return this.texTypesEnabled;
+ }
+
+ CTexUnit.prototype.genPassLines = function CTexUnit_genPassLines(passOutputVar, passInputVar, texUnitID) {
+ if (!this.enabled()) {
+ return ["vec4 " + passOutputVar + " = " + passInputVar + ";"];
+ }
+ var lines = this.env.genPassLines(passOutputVar, passInputVar, texUnitID).join('\n');
+
+ var texLoadLines = '';
+ var texLoadRegex = /(texture.*?\(.*?\))/g;
+ var loadCounter = 0;
+ var load;
+
+ // As an optimization, merge duplicate identical texture loads to one var.
+ while(load = texLoadRegex.exec(lines)) {
+ var texLoadExpr = load[1];
+ var secondOccurrence = lines.slice(load.index+1).indexOf(texLoadExpr);
+ if (secondOccurrence != -1) { // And also has a second occurrence of same load expression..
+ // Create new var to store the common load.
+ var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
+ var texLoadVar = prefix + 'texload' + loadCounter++;
+ var texLoadLine = 'vec4 ' + texLoadVar + ' = ' + texLoadExpr + ';\n';
+ texLoadLines += texLoadLine + '\n'; // Store the generated texture load statements in a temp string to not confuse regex search in progress.
+ lines = lines.split(texLoadExpr).join(texLoadVar);
+ // Reset regex search, since we modified the string.
+ texLoadRegex = /(texture.*\(.*\))/g;
+ }
+ }
+ return [texLoadLines + lines];
+ }
+
+ CTexUnit.prototype.getTexType = function CTexUnit_getTexType() {
+ if (this.enabled_texCube) {
+ return GL_TEXTURE_CUBE_MAP;
+ } else if (this.enabled_tex3D) {
+ return GL_TEXTURE_3D;
+ } else if (this.enabled_tex2D) {
+ return GL_TEXTURE_2D;
+ } else if (this.enabled_tex1D) {
+ return GL_TEXTURE_1D;
+ }
+ return 0;
+ }
+
+ CTexEnv.prototype.genPassLines = function CTexEnv_genPassLines(passOutputVar, passInputVar, texUnitID) {
+ switch (this.mode) {
+ case GL_REPLACE: {
+ /* RGB:
+ * Cv = Cs
+ * Av = Ap // Note how this is different, and that we'll
+ * need to track the bound texture internalFormat
+ * to get this right.
+ *
+ * RGBA:
+ * Cv = Cs
+ * Av = As
+ */
+ return [
+ "vec4 " + passOutputVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
+ ];
+ }
+ case GL_ADD: {
+ /* RGBA:
+ * Cv = Cp + Cs
+ * Av = ApAs
+ */
+ var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
+ var texVar = prefix + "tex";
+ var colorVar = prefix + "color";
+ var alphaVar = prefix + "alpha";
+
+ return [
+ "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
+ "vec3 " + colorVar + " = " + passInputVar + ".rgb + " + texVar + ".rgb;",
+ "float " + alphaVar + " = " + passInputVar + ".a * " + texVar + ".a;",
+ "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
+ ];
+ }
+ case GL_MODULATE: {
+ /* RGBA:
+ * Cv = CpCs
+ * Av = ApAs
+ */
+ var line = [
+ "vec4 " + passOutputVar,
+ " = ",
+ passInputVar,
+ " * ",
+ genTexUnitSampleExpr(texUnitID),
+ ";",
+ ];
+ return [line.join("")];
+ }
+ case GL_DECAL: {
+ /* RGBA:
+ * Cv = Cp(1 - As) + CsAs
+ * Av = Ap
+ */
+ var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
+ var texVar = prefix + "tex";
+ var colorVar = prefix + "color";
+ var alphaVar = prefix + "alpha";
+
+ return [
+ "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
+ [
+ "vec3 " + colorVar + " = ",
+ passInputVar + ".rgb * (1.0 - " + texVar + ".a)",
+ " + ",
+ texVar + ".rgb * " + texVar + ".a",
+ ";"
+ ].join(""),
+ "float " + alphaVar + " = " + passInputVar + ".a;",
+ "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
+ ];
+ }
+ case GL_BLEND: {
+ /* RGBA:
+ * Cv = Cp(1 - Cs) + CcCs
+ * Av = As
+ */
+ var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
+ var texVar = prefix + "tex";
+ var colorVar = prefix + "color";
+ var alphaVar = prefix + "alpha";
+
+ return [
+ "vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
+ [
+ "vec3 " + colorVar + " = ",
+ passInputVar + ".rgb * (1.0 - " + texVar + ".rgb)",
+ " + ",
+ PRIM_COLOR_VARYING + ".rgb * " + texVar + ".rgb",
+ ";"
+ ].join(""),
+ "float " + alphaVar + " = " + texVar + ".a;",
+ "vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
+ ];
+ }
+ case GL_COMBINE: {
+ var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
+ var colorVar = prefix + "color";
+ var alphaVar = prefix + "alpha";
+ var colorLines = this.genCombinerLines(true, colorVar,
+ passInputVar, texUnitID,
+ this.colorCombiner, this.colorSrc, this.colorOp);
+ var alphaLines = this.genCombinerLines(false, alphaVar,
+ passInputVar, texUnitID,
+ this.alphaCombiner, this.alphaSrc, this.alphaOp);
+
+ // Generate scale, but avoid generating an identity op that multiplies by one.
+ var scaledColor = (this.colorScale == 1) ? colorVar : (colorVar + " * " + valToFloatLiteral(this.colorScale));
+ var scaledAlpha = (this.alphaScale == 1) ? alphaVar : (alphaVar + " * " + valToFloatLiteral(this.alphaScale));
+
+ var line = [
+ "vec4 " + passOutputVar,
+ " = ",
+ "vec4(",
+ scaledColor,
+ ", ",
+ scaledAlpha,
+ ")",
+ ";",
+ ].join("");
+ return [].concat(colorLines, alphaLines, [line]);
+ }
+ }
+
+ return abort_noSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16));
+ }
+
+ CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar,
+ passInputVar, texUnitID,
+ combiner, srcArr, opArr)
+ {
+ var argsNeeded = null;
+ switch (combiner) {
+ case GL_REPLACE:
+ argsNeeded = 1;
+ break;
+
+ case GL_MODULATE:
+ case GL_ADD:
+ case GL_SUBTRACT:
+ argsNeeded = 2;
+ break;
+
+ case GL_INTERPOLATE:
+ argsNeeded = 3;
+ break;
+
+ default:
+ return abort_noSupport("Unsupported combiner: 0x" + combiner.toString(16));
+ }
+
+ var constantExpr = [
+ "vec4(",
+ valToFloatLiteral(this.envColor[0]),
+ ", ",
+ valToFloatLiteral(this.envColor[1]),
+ ", ",
+ valToFloatLiteral(this.envColor[2]),
+ ", ",
+ valToFloatLiteral(this.envColor[3]),
+ ")",
+ ].join("");
+ var src0Expr = (argsNeeded >= 1) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[0], opArr[0])
+ : null;
+ var src1Expr = (argsNeeded >= 2) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[1], opArr[1])
+ : null;
+ var src2Expr = (argsNeeded >= 3) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[2], opArr[2])
+ : null;
+
+ var outputType = isColor ? "vec3" : "float";
+ var lines = null;
+ switch (combiner) {
+ case GL_REPLACE: {
+ var line = [
+ outputType + " " + outputVar,
+ " = ",
+ src0Expr,
+ ";",
+ ];
+ lines = [line.join("")];
+ break;
+ }
+ case GL_MODULATE: {
+ var line = [
+ outputType + " " + outputVar + " = ",
+ src0Expr + " * " + src1Expr,
+ ";",
+ ];
+ lines = [line.join("")];
+ break;
+ }
+ case GL_ADD: {
+ var line = [
+ outputType + " " + outputVar + " = ",
+ src0Expr + " + " + src1Expr,
+ ";",
+ ];
+ lines = [line.join("")];
+ break;
+ }
+ case GL_SUBTRACT: {
+ var line = [
+ outputType + " " + outputVar + " = ",
+ src0Expr + " - " + src1Expr,
+ ";",
+ ];
+ lines = [line.join("")];
+ break;
+ }
+ case GL_INTERPOLATE: {
+ var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
+ var arg2Var = prefix + "colorSrc2";
+ var arg2Line = getTypeFromCombineOp(this.colorOp[2]) + " " + arg2Var + " = " + src2Expr + ";";
+
+ var line = [
+ outputType + " " + outputVar,
+ " = ",
+ src0Expr + " * " + arg2Var,
+ " + ",
+ src1Expr + " * (1.0 - " + arg2Var + ")",
+ ";",
+ ];
+ lines = [
+ arg2Line,
+ line.join(""),
+ ];
+ break;
+ }
+
+ default:
+ return abort_sanity("Unmatched TexEnv.colorCombiner?");
+ }
+
+ return lines;
+ }
+
+ return {
+ // Exports:
+ init: function(gl, specifiedMaxTextureImageUnits) {
+ var maxTexUnits = 0;
+ if (specifiedMaxTextureImageUnits) {
+ maxTexUnits = specifiedMaxTextureImageUnits;
+ } else if (gl) {
+ maxTexUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ }
+#if ASSERTIONS
+ assert(maxTexUnits > 0);
+#endif
+ s_texUnits = [];
+ for (var i = 0; i < maxTexUnits; i++) {
+ s_texUnits.push(new CTexUnit());
+ }
+ },
+
+ setGLSLVars: function(uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix) {
+ TEX_UNIT_UNIFORM_PREFIX = uTexUnitPrefix;
+ TEX_COORD_VARYING_PREFIX = vTexCoordPrefix;
+ PRIM_COLOR_VARYING = vPrimColor;
+ TEX_MATRIX_UNIFORM_PREFIX = uTexMatrixPrefix;
+ },
+
+ genAllPassLines: function(resultDest, indentSize) {
+ indentSize = indentSize || 0;
+
+ s_requiredTexUnitsForPass.length = 0; // Clear the list.
+ var lines = [];
+ var lastPassVar = PRIM_COLOR_VARYING;
+ for (var i = 0; i < s_texUnits.length; i++) {
+ if (!s_texUnits[i].enabled()) continue;
+
+ s_requiredTexUnitsForPass.push(i);
+
+ var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + i + "_";
+ var passOutputVar = prefix + "result";
+
+ var newLines = s_texUnits[i].genPassLines(passOutputVar, lastPassVar, i);
+ lines = lines.concat(newLines, [""]);
+
+ lastPassVar = passOutputVar;
+ }
+ lines.push(resultDest + " = " + lastPassVar + ";");
+
+ var indent = "";
+ for (var i = 0; i < indentSize; i++) indent += " ";
+
+ var output = indent + lines.join("\n" + indent);
+
+ return output;
+ },
+
+ getUsedTexUnitList: function() {
+ return s_requiredTexUnitsForPass;
+ },
+
+ traverseState: function(keyView) {
+ for (var i = 0; i < s_texUnits.length; i++) {
+ s_texUnits[i].traverseState(keyView);
+ }
+ },
+
+ getTexUnitType: function(texUnitID) {
+#if ASSERTIONS
+ assert(texUnitID >= 0 &&
+ texUnitID < s_texUnits.length);
+#endif
+ return s_texUnits[texUnitID].getTexType();
+ },
+
+ // Hooks:
+ hook_activeTexture: function(texture) {
+ s_activeTexture = texture - GL_TEXTURE0;
+ },
+
+ hook_enable: function(cap) {
+ var cur = getCurTexUnit();
+ switch (cap) {
+ case GL_TEXTURE_1D:
+ if (!cur.enabled_tex1D) {
+ GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again.
+ cur.enabled_tex1D = true;
+ cur.texTypesEnabled |= 1;
+ }
+ break;
+ case GL_TEXTURE_2D:
+ if (!cur.enabled_tex2D) {
+ GLImmediate.currentRenderer = null;
+ cur.enabled_tex2D = true;
+ cur.texTypesEnabled |= 2;
+ }
+ break;
+ case GL_TEXTURE_3D:
+ if (!cur.enabled_tex3D) {
+ GLImmediate.currentRenderer = null;
+ cur.enabled_tex3D = true;
+ cur.texTypesEnabled |= 4;
+ }
+ break;
+ case GL_TEXTURE_CUBE_MAP:
+ if (!cur.enabled_texCube) {
+ GLImmediate.currentRenderer = null;
+ cur.enabled_texCube = true;
+ cur.texTypesEnabled |= 8;
+ }
+ break;
+ }
+ },
+
+ hook_disable: function(cap) {
+ var cur = getCurTexUnit();
+ switch (cap) {
+ case GL_TEXTURE_1D:
+ if (cur.enabled_tex1D) {
+ GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again.
+ cur.enabled_tex1D = false;
+ cur.texTypesEnabled &= ~1;
+ }
+ break;
+ case GL_TEXTURE_2D:
+ if (cur.enabled_tex2D) {
+ GLImmediate.currentRenderer = null;
+ cur.enabled_tex2D = false;
+ cur.texTypesEnabled &= ~2;
+ }
+ break;
+ case GL_TEXTURE_3D:
+ if (cur.enabled_tex3D) {
+ GLImmediate.currentRenderer = null;
+ cur.enabled_tex3D = false;
+ cur.texTypesEnabled &= ~4;
+ }
+ break;
+ case GL_TEXTURE_CUBE_MAP:
+ if (cur.enabled_texCube) {
+ GLImmediate.currentRenderer = null;
+ cur.enabled_texCube = false;
+ cur.texTypesEnabled &= ~8;
+ }
+ break;
+ }
+ },
+
+ hook_texEnvf: function(target, pname, param) {
+ if (target != GL_TEXTURE_ENV)
+ return;
+
+ var env = getCurTexUnit().env;
+ switch (pname) {
+ case GL_RGB_SCALE:
+ if (env.colorScale != param) {
+ env.invalidateKey(); // We changed FFP emulation renderer state.
+ env.colorScale = param;
+ }
+ break;
+ case GL_ALPHA_SCALE:
+ if (env.alphaScale != param) {
+ env.invalidateKey();
+ env.alphaScale = param;
+ }
+ break;
+
+ default:
+ err('WARNING: Unhandled `pname` in call to `glTexEnvf`.');
+ }
+ },
+
+ hook_texEnvi: function(target, pname, param) {
+ if (target != GL_TEXTURE_ENV)
+ return;
+
+ var env = getCurTexUnit().env;
+ switch (pname) {
+ case GL_TEXTURE_ENV_MODE:
+ if (env.mode != param) {
+ env.invalidateKey(); // We changed FFP emulation renderer state.
+ env.mode = param;
+ }
+ break;
+
+ case GL_COMBINE_RGB:
+ if (env.colorCombiner != param) {
+ env.invalidateKey();
+ env.colorCombiner = param;
+ }
+ break;
+ case GL_COMBINE_ALPHA:
+ if (env.alphaCombiner != param) {
+ env.invalidateKey();
+ env.alphaCombiner = param;
+ }
+ break;
+
+ case GL_SRC0_RGB:
+ if (env.colorSrc[0] != param) {
+ env.invalidateKey();
+ env.colorSrc[0] = param;
+ }
+ break;
+ case GL_SRC1_RGB:
+ if (env.colorSrc[1] != param) {
+ env.invalidateKey();
+ env.colorSrc[1] = param;
+ }
+ break;
+ case GL_SRC2_RGB:
+ if (env.colorSrc[2] != param) {
+ env.invalidateKey();
+ env.colorSrc[2] = param;
+ }
+ break;
+
+ case GL_SRC0_ALPHA:
+ if (env.alphaSrc[0] != param) {
+ env.invalidateKey();
+ env.alphaSrc[0] = param;
+ }
+ break;
+ case GL_SRC1_ALPHA:
+ if (env.alphaSrc[1] != param) {
+ env.invalidateKey();
+ env.alphaSrc[1] = param;
+ }
+ break;
+ case GL_SRC2_ALPHA:
+ if (env.alphaSrc[2] != param) {
+ env.invalidateKey();
+ env.alphaSrc[2] = param;
+ }
+ break;
+
+ case GL_OPERAND0_RGB:
+ if (env.colorOp[0] != param) {
+ env.invalidateKey();
+ env.colorOp[0] = param;
+ }
+ break;
+ case GL_OPERAND1_RGB:
+ if (env.colorOp[1] != param) {
+ env.invalidateKey();
+ env.colorOp[1] = param;
+ }
+ break;
+ case GL_OPERAND2_RGB:
+ if (env.colorOp[2] != param) {
+ env.invalidateKey();
+ env.colorOp[2] = param;
+ }
+ break;
+
+ case GL_OPERAND0_ALPHA:
+ if (env.alphaOp[0] != param) {
+ env.invalidateKey();
+ env.alphaOp[0] = param;
+ }
+ break;
+ case GL_OPERAND1_ALPHA:
+ if (env.alphaOp[1] != param) {
+ env.invalidateKey();
+ env.alphaOp[1] = param;
+ }
+ break;
+ case GL_OPERAND2_ALPHA:
+ if (env.alphaOp[2] != param) {
+ env.invalidateKey();
+ env.alphaOp[2] = param;
+ }
+ break;
+
+ case GL_RGB_SCALE:
+ if (env.colorScale != param) {
+ env.invalidateKey();
+ env.colorScale = param;
+ }
+ break;
+ case GL_ALPHA_SCALE:
+ if (env.alphaScale != param) {
+ env.invalidateKey();
+ env.alphaScale = param;
+ }
+ break;
+
+ default:
+ err('WARNING: Unhandled `pname` in call to `glTexEnvi`.');
+ }
+ },
+
+ hook_texEnvfv: function(target, pname, params) {
+ if (target != GL_TEXTURE_ENV) return;
+
+ var env = getCurTexUnit().env;
+ switch (pname) {
+ case GL_TEXTURE_ENV_COLOR: {
+ for (var i = 0; i < 4; i++) {
+ var param = {{{ makeGetValue('params', 'i*4', 'float') }}};
+ if (env.envColor[i] != param) {
+ env.invalidateKey(); // We changed FFP emulation renderer state.
+ env.envColor[i] = param;
+ }
+ }
+ break
+ }
+ default:
+ err('WARNING: Unhandled `pname` in call to `glTexEnvfv`.');
+ }
+ },
+
+ hook_getTexEnviv: function(target, pname, param) {
+ if (target != GL_TEXTURE_ENV)
+ return;
+
+ var env = getCurTexUnit().env;
+ switch (pname) {
+ case GL_TEXTURE_ENV_MODE:
+ {{{ makeSetValue('param', '0', 'env.mode', 'i32') }}};
+ return;
+
+ case GL_TEXTURE_ENV_COLOR:
+ {{{ makeSetValue('param', '0', 'Math.max(Math.min(env.envColor[0]*255, 255, -255))', 'i32') }}};
+ {{{ makeSetValue('param', '1', 'Math.max(Math.min(env.envColor[1]*255, 255, -255))', 'i32') }}};
+ {{{ makeSetValue('param', '2', 'Math.max(Math.min(env.envColor[2]*255, 255, -255))', 'i32') }}};
+ {{{ makeSetValue('param', '3', 'Math.max(Math.min(env.envColor[3]*255, 255, -255))', 'i32') }}};
+ return;
+
+ case GL_COMBINE_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorCombiner', 'i32') }}};
+ return;
+
+ case GL_COMBINE_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaCombiner', 'i32') }}};
+ return;
+
+ case GL_SRC0_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorSrc[0]', 'i32') }}};
+ return;
+
+ case GL_SRC1_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorSrc[1]', 'i32') }}};
+ return;
+
+ case GL_SRC2_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorSrc[2]', 'i32') }}};
+ return;
+
+ case GL_SRC0_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaSrc[0]', 'i32') }}};
+ return;
+
+ case GL_SRC1_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaSrc[1]', 'i32') }}};
+ return;
+
+ case GL_SRC2_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaSrc[2]', 'i32') }}};
+ return;
+
+ case GL_OPERAND0_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorOp[0]', 'i32') }}};
+ return;
+
+ case GL_OPERAND1_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorOp[1]', 'i32') }}};
+ return;
+
+ case GL_OPERAND2_RGB:
+ {{{ makeSetValue('param', '0', 'env.colorOp[2]', 'i32') }}};
+ return;
+
+ case GL_OPERAND0_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaOp[0]', 'i32') }}};
+ return;
+
+ case GL_OPERAND1_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaOp[1]', 'i32') }}};
+ return;
+
+ case GL_OPERAND2_ALPHA:
+ {{{ makeSetValue('param', '0', 'env.alphaOp[2]', 'i32') }}};
+ return;
+
+ case GL_RGB_SCALE:
+ {{{ makeSetValue('param', '0', 'env.colorScale', 'i32') }}};
+ return;
+
+ case GL_ALPHA_SCALE:
+ {{{ makeSetValue('param', '0', 'env.alphaScale', 'i32') }}};
+ return;
+
+ default:
+ err('WARNING: Unhandled `pname` in call to `glGetTexEnvi`.');
+ }
+ },
+
+ hook_getTexEnvfv: function(target, pname, param) {
+ if (target != GL_TEXTURE_ENV)
+ return;
+
+ var env = getCurTexUnit().env;
+ switch (pname) {
+ case GL_TEXTURE_ENV_COLOR:
+ {{{ makeSetValue('param', '0', 'env.envColor[0]', 'float') }}};
+ {{{ makeSetValue('param', '4', 'env.envColor[1]', 'float') }}};
+ {{{ makeSetValue('param', '8', 'env.envColor[2]', 'float') }}};
+ {{{ makeSetValue('param', '12', 'env.envColor[3]', 'float') }}};
+ return;
+ }
+ }
+ };
+ },
+
+ // Vertex and index data
+ 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: -1,
+
+ rendererCache: null,
+ 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
+ lastRenderer: null, // used to avoid cleaning up and re-preparing the same renderer
+ lastArrayBuffer: null, // used in conjunction with lastRenderer
+ lastProgram: null, // ""
+ lastStride: -1, // ""
+
+ // The following data structures are used for OpenGL Immediate Mode matrix routines.
+ matrix: [],
+ matrixStack: [],
+ currentMatrix: 0, // 0: modelview, 1: projection, 2+i, texture matrix i.
+ tempMatrix: null,
+ matricesModified: false,
+ useTextureMatrix: false,
+
+ // Clientside attributes
+ VERTEX: 0,
+ NORMAL: 1,
+ COLOR: 2,
+ TEXTURE0: 3,
+ NUM_ATTRIBUTES: -1, // Initialized in GL emulation init().
+ MAX_TEXTURES: -1, // Initialized in GL emulation init().
+
+ totalEnabledClientAttributes: 0,
+ enabledClientAttributes: [0, 0],
+ clientAttributes: [], // raw data, including possible unneeded ones
+ liveClientAttributes: [], // the ones actually alive in the current computation, sorted
+ currentRenderer: null, // Caches the currently active FFP emulation renderer, so that it does not have to be re-looked up unless relevant state changes.
+ modifiedClientAttributes: false,
+ clientActiveTexture: 0,
+ clientColor: null,
+ usedTexUnitList: [],
+ fixedFunctionProgram: null,
+
+ setClientAttribute: function setClientAttribute(name, size, type, stride, pointer) {
+ var attrib = GLImmediate.clientAttributes[name];
+ if (!attrib) {
+ for (var i = 0; i <= name; i++) { // keep flat
+ if (!GLImmediate.clientAttributes[i]) {
+ GLImmediate.clientAttributes[i] = {
+ name: name,
+ size: size,
+ type: type,
+ stride: stride,
+ pointer: pointer,
+ offset: 0
+ };
+ }
+ }
+ } else {
+ attrib.name = name;
+ attrib.size = size;
+ attrib.type = type;
+ attrib.stride = stride;
+ attrib.pointer = pointer;
+ attrib.offset = 0;
+ }
+ GLImmediate.modifiedClientAttributes = true;
+ },
+
+ // Renderers
+ addRendererComponent: function addRendererComponent(name, size, type) {
+ if (!GLImmediate.rendererComponents[name]) {
+ GLImmediate.rendererComponents[name] = 1;
+#if ASSERTIONS
+ if (GLImmediate.enabledClientAttributes[name]) {
+ console.log("Warning: glTexCoord used after EnableClientState for TEXTURE_COORD_ARRAY for TEXTURE0. Disabling TEXTURE_COORD_ARRAY...");
+ }
+#endif
+ GLImmediate.enabledClientAttributes[name] = true;
+ GLImmediate.setClientAttribute(name, size, type, 0, GLImmediate.rendererComponentPointer);
+ GLImmediate.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot];
+#if GL_FFP_ONLY
+ // We can enable the correct attribute stream index immediately here, since the same attribute in each shader
+ // will be bound to this same index.
+ GL.enableVertexAttribArray(name);
+#endif
+ } else {
+ GLImmediate.rendererComponents[name]++;
+ }
+ },
+
+ disableBeginEndClientAttributes: function disableBeginEndClientAttributes() {
+ for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
+ if (GLImmediate.rendererComponents[i]) GLImmediate.enabledClientAttributes[i] = false;
+ }
+ },
+
+ getRenderer: function getRenderer() {
+ // If no FFP state has changed that would have forced to re-evaluate which FFP emulation shader to use,
+ // we have the currently used renderer in cache, and can immediately return that.
+ if (GLImmediate.currentRenderer) {
+ return GLImmediate.currentRenderer;
+ }
+ // return a renderer object given the liveClientAttributes
+ // we maintain a cache of renderers, optimized to not generate garbage
+ var attributes = GLImmediate.liveClientAttributes;
+ var cacheMap = GLImmediate.rendererCache;
+ var keyView = cacheMap.getStaticKeyView().reset();
+
+ // By attrib state:
+ var enabledAttributesKey = 0;
+ for (var i = 0; i < attributes.length; i++) {
+ enabledAttributesKey |= 1 << attributes[i].name;
+ }
+
+ // By fog state:
+ var fogParam = 0;
+ if (GLEmulation.fogEnabled) {
+ switch (GLEmulation.fogMode) {
+ case 0x0801: // GL_EXP2
+ fogParam = 1;
+ break;
+ case 0x2601: // GL_LINEAR
+ fogParam = 2;
+ break;
+ default: // default to GL_EXP
+ fogParam = 3;
+ break;
+ }
+ }
+ keyView.next((enabledAttributesKey << 2) | fogParam);
+
+#if !GL_FFP_ONLY
+ // By cur program:
+ keyView.next(GL.currProgram);
+ if (!GL.currProgram) {
+#endif
+ GLImmediate.TexEnvJIT.traverseState(keyView);
+#if !GL_FFP_ONLY
+ }
+#endif
+
+ // If we don't already have it, create it.
+ var renderer = keyView.get();
+ if (!renderer) {
+#if GL_DEBUG
+ err('generating renderer for ' + JSON.stringify(attributes));
+#endif
+ renderer = GLImmediate.createRenderer();
+ GLImmediate.currentRenderer = renderer;
+ keyView.set(renderer);
+ return renderer;
+ }
+ GLImmediate.currentRenderer = renderer; // Cache the currently used renderer, so later lookups without state changes can get this fast.
+ return renderer;
+ },
+
+ createRenderer: function createRenderer(renderer) {
+ var useCurrProgram = !!GL.currProgram;
+ var hasTextures = false;
+ for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
+ var texAttribName = GLImmediate.TEXTURE0 + i;
+ if (!GLImmediate.enabledClientAttributes[texAttribName])
+ continue;
+
+#if ASSERTIONS
+ if (!useCurrProgram) {
+ if (GLImmediate.TexEnvJIT.getTexUnitType(i) == 0) {
+ warnOnce("GL_TEXTURE" + i + " coords are supplied, but that texture unit is disabled in the fixed-function pipeline.");
+ }
+ }
+#endif
+
+ hasTextures = true;
+ }
+
+ var ret = {
+ init: function init() {
+ // For fixed-function shader generation.
+ var uTexUnitPrefix = 'u_texUnit';
+ var aTexCoordPrefix = 'a_texCoord';
+ var vTexCoordPrefix = 'v_texCoord';
+ var vPrimColor = 'v_color';
+ var uTexMatrixPrefix = GLImmediate.useTextureMatrix ? 'u_textureMatrix' : null;
+
+ if (useCurrProgram) {
+ if (GL.shaderInfos[GL.programShaders[GL.currProgram][0]].type == GLctx.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];
+ this.usedTexUnitList = [];
+ } 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.
+ 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;
+ }
+ }
+
+ GLImmediate.TexEnvJIT.setGLSLVars(uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix);
+ var fsTexEnvPass = GLImmediate.TexEnvJIT.genAllPassLines('gl_FragColor', 2);
+
+ var texUnitAttribList = '';
+ var texUnitVaryingList = '';
+ var texUnitUniformList = '';
+ var vsTexCoordInits = '';
+ this.usedTexUnitList = GLImmediate.TexEnvJIT.getUsedTexUnitList();
+ for (var i = 0; i < this.usedTexUnitList.length; i++) {
+ var texUnit = this.usedTexUnitList[i];
+ texUnitAttribList += 'attribute vec4 ' + aTexCoordPrefix + texUnit + ';\n';
+ texUnitVaryingList += 'varying vec4 ' + vTexCoordPrefix + texUnit + ';\n';
+ texUnitUniformList += 'uniform sampler2D ' + uTexUnitPrefix + texUnit + ';\n';
+ vsTexCoordInits += ' ' + vTexCoordPrefix + texUnit + ' = ' + aTexCoordPrefix + texUnit + ';\n';
+
+ if (GLImmediate.useTextureMatrix) {
+ texUnitUniformList += 'uniform mat4 ' + uTexMatrixPrefix + texUnit + ';\n';
+ }
+ }
+
+ var vsFogVaryingInit = null;
+ if (GLEmulation.fogEnabled) {
+ vsFogVaryingInit = ' v_fogFragCoord = abs(ecPosition.z);\n';
+ }
+
+ var vsSource = [
+ 'attribute vec4 a_position;',
+ 'attribute vec4 a_color;',
+ 'varying vec4 v_color;',
+ texUnitAttribList,
+ texUnitVaryingList,
+ (GLEmulation.fogEnabled ? 'varying float v_fogFragCoord;' : null),
+ 'uniform mat4 u_modelView;',
+ 'uniform mat4 u_projection;',
+ 'void main()',
+ '{',
+ ' vec4 ecPosition = u_modelView * a_position;', // eye-coordinate position
+ ' gl_Position = u_projection * ecPosition;',
+ ' v_color = a_color;',
+ vsTexCoordInits,
+ vsFogVaryingInit,
+ '}',
+ ''
+ ].join('\n').replace(/\n\n+/g, '\n');
+
+ this.vertexShader = GLctx.createShader(GLctx.VERTEX_SHADER);
+ GLctx.shaderSource(this.vertexShader, vsSource);
+ GLctx.compileShader(this.vertexShader);
+
+ var fogHeaderIfNeeded = null;
+ if (GLEmulation.fogEnabled) {
+ fogHeaderIfNeeded = [
+ '',
+ 'varying float v_fogFragCoord; ',
+ 'uniform vec4 u_fogColor; ',
+ 'uniform float u_fogEnd; ',
+ 'uniform float u_fogScale; ',
+ 'uniform float u_fogDensity; ',
+ 'float ffog(in float ecDistance) { ',
+ fogFormula,
+ ' fog = clamp(fog, 0.0, 1.0); ',
+ ' return fog; ',
+ '}',
+ '',
+ ].join("\n");
+ }
+
+ var fogPass = null;
+ if (GLEmulation.fogEnabled) {
+ fogPass = 'gl_FragColor = vec4(mix(u_fogColor.rgb, gl_FragColor.rgb, ffog(v_fogFragCoord)), gl_FragColor.a);\n';
+ }
+
+ var fsSource = [
+ 'precision mediump float;',
+ texUnitVaryingList,
+ texUnitUniformList,
+ 'varying vec4 v_color;',
+ fogHeaderIfNeeded,
+ 'void main()',
+ '{',
+ fsTexEnvPass,
+ fogPass,
+ '}',
+ ''
+ ].join("\n").replace(/\n\n+/g, '\n');
+
+ this.fragmentShader = GLctx.createShader(GLctx.FRAGMENT_SHADER);
+ GLctx.shaderSource(this.fragmentShader, fsSource);
+ GLctx.compileShader(this.fragmentShader);
+
+ this.program = GLctx.createProgram();
+ GLctx.attachShader(this.program, this.vertexShader);
+ GLctx.attachShader(this.program, this.fragmentShader);
+
+ // As optimization, bind all attributes to prespecified locations, so that the FFP emulation
+ // code can submit attributes to any generated FFP shader without having to examine each shader in turn.
+ // These prespecified locations are only assumed if GL_FFP_ONLY is specified, since user could also create their
+ // own shaders that didn't have attributes in the same locations.
+ GLctx.bindAttribLocation(this.program, GLImmediate.VERTEX, 'a_position');
+ GLctx.bindAttribLocation(this.program, GLImmediate.COLOR, 'a_color');
+ GLctx.bindAttribLocation(this.program, GLImmediate.NORMAL, 'a_normal');
+ var maxVertexAttribs = GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS);
+ for (var i = 0; i < GLImmediate.MAX_TEXTURES && GLImmediate.TEXTURE0 + i < maxVertexAttribs; i++) {
+ GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, 'a_texCoord'+i);
+ GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, aTexCoordPrefix+i);
+ }
+ GLctx.linkProgram(this.program);
+ }
+
+ // Stores an array that remembers which matrix uniforms are up-to-date in this FFP renderer, so they don't need to be resubmitted
+ // each time we render with this program.
+ this.textureMatrixVersion = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+
+ this.positionLocation = GLctx.getAttribLocation(this.program, 'a_position');
+
+ this.texCoordLocations = [];
+
+ for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
+ if (!GLImmediate.enabledClientAttributes[GLImmediate.TEXTURE0 + i]) {
+ this.texCoordLocations[i] = -1;
+ continue;
+ }
+
+ if (useCurrProgram) {
+ this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, 'a_texCoord' + i);
+ } else {
+ this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, aTexCoordPrefix + i);
+ }
+ }
+ this.colorLocation = GLctx.getAttribLocation(this.program, 'a_color');
+ if (!useCurrProgram) {
+ // Temporarily switch to the program so we can set our sampler uniforms early.
+ var prevBoundProg = GLctx.getParameter(GLctx.CURRENT_PROGRAM);
+ GLctx.useProgram(this.program);
+ {
+ for (var i = 0; i < this.usedTexUnitList.length; i++) {
+ var texUnitID = this.usedTexUnitList[i];
+ var texSamplerLoc = GLctx.getUniformLocation(this.program, uTexUnitPrefix + texUnitID);
+ GLctx.uniform1i(texSamplerLoc, texUnitID);
+ }
+ }
+ // The default color attribute value is not the same as the default for all other attribute streams (0,0,0,1) but (1,1,1,1),
+ // so explicitly set it right at start.
+ GLctx.vertexAttrib4fv(this.colorLocation, [1,1,1,1]);
+ GLctx.useProgram(prevBoundProg);
+ }
+
+ this.textureMatrixLocations = [];
+ for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
+ this.textureMatrixLocations[i] = GLctx.getUniformLocation(this.program, 'u_textureMatrix' + i);
+ }
+ this.normalLocation = GLctx.getAttribLocation(this.program, 'a_normal');
+
+ this.modelViewLocation = GLctx.getUniformLocation(this.program, 'u_modelView');
+ this.projectionLocation = GLctx.getUniformLocation(this.program, 'u_projection');
+
+ this.hasTextures = hasTextures;
+ this.hasNormal = GLImmediate.enabledClientAttributes[GLImmediate.NORMAL] &&
+ GLImmediate.clientAttributes[GLImmediate.NORMAL].size > 0 &&
+ this.normalLocation >= 0;
+ this.hasColor = (this.colorLocation === 0) || this.colorLocation > 0;
+
+ this.floatType = GLctx.FLOAT; // minor optimization
+
+ this.fogColorLocation = GLctx.getUniformLocation(this.program, 'u_fogColor');
+ this.fogEndLocation = GLctx.getUniformLocation(this.program, 'u_fogEnd');
+ this.fogScaleLocation = GLctx.getUniformLocation(this.program, 'u_fogScale');
+ this.fogDensityLocation = GLctx.getUniformLocation(this.program, 'u_fogDensity');
+ this.hasFog = !!(this.fogColorLocation || this.fogEndLocation ||
+ this.fogScaleLocation || this.fogDensityLocation);
+ },
+
+ prepare: function prepare() {
+ // Calculate the array buffer
+ var arrayBuffer;
+ if (!GL.currArrayBuffer) {
+ var start = GLImmediate.firstVertex*GLImmediate.stride;
+ var end = GLImmediate.lastVertex*GLImmediate.stride;
+#if ASSERTIONS
+ assert(end <= GL.MAX_TEMP_BUFFER_SIZE, 'too much vertex data');
+#endif
+ arrayBuffer = GL.getTempVertexBuffer(end);
+ // TODO: consider using the last buffer we bound, if it was larger. downside is larger buffer, but we might avoid rebinding and preparing
+ } else {
+ arrayBuffer = GL.currArrayBuffer;
+ }
+
+#if GL_UNSAFE_OPTS
+ // If the array buffer is unchanged and the renderer as well, then we can avoid all the work here
+ // XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you
+ // have odd glitches
+ var lastRenderer = GLImmediate.lastRenderer;
+ var canSkip = this == lastRenderer &&
+ arrayBuffer == GLImmediate.lastArrayBuffer &&
+ (GL.currProgram || this.program) == GLImmediate.lastProgram &&
+ GLImmediate.stride == GLImmediate.lastStride &&
+ !GLImmediate.matricesModified;
+ if (!canSkip && lastRenderer) lastRenderer.cleanup();
+#endif
+ if (!GL.currArrayBuffer) {
+ // Bind the array buffer and upload data after cleaning up the previous renderer
+
+ if (arrayBuffer != GLImmediate.lastArrayBuffer) {
+ GLctx.bindBuffer(GLctx.ARRAY_BUFFER, arrayBuffer);
+ GLImmediate.lastArrayBuffer = arrayBuffer;
+ }
+
+ GLctx.bufferSubData(GLctx.ARRAY_BUFFER, start, GLImmediate.vertexData.subarray(start >> 2, end >> 2));
+ }
+#if GL_UNSAFE_OPTS
+ if (canSkip) return;
+ GLImmediate.lastRenderer = this;
+ GLImmediate.lastProgram = GL.currProgram || this.program;
+ GLImmediate.lastStride == GLImmediate.stride;
+ GLImmediate.matricesModified = false;
+#endif
+
+ if (!GL.currProgram) {
+ if (GLImmediate.fixedFunctionProgram != this.program) {
+ GLctx.useProgram(this.program);
+ GLImmediate.fixedFunctionProgram = this.program;
+ }
+ }
+
+ if (this.modelViewLocation && this.modelViewMatrixVersion != GLImmediate.matrixVersion[0/*m*/]) {
+ this.modelViewMatrixVersion = GLImmediate.matrixVersion[0/*m*/];
+ GLctx.uniformMatrix4fv(this.modelViewLocation, false, GLImmediate.matrix[0/*m*/]);
+ }
+ if (this.projectionLocation && this.projectionMatrixVersion != GLImmediate.matrixVersion[1/*p*/]) {
+ this.projectionMatrixVersion = GLImmediate.matrixVersion[1/*p*/];
+ GLctx.uniformMatrix4fv(this.projectionLocation, false, GLImmediate.matrix[1/*p*/]);
+ }
+
+ var clientAttributes = GLImmediate.clientAttributes;
+ var posAttr = clientAttributes[GLImmediate.VERTEX];
+
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(posAttr.size, posAttr.type, GLImmediate.stride, clientAttributes[GLImmediate.VERTEX].offset);
+#endif
+
+#if GL_FFP_ONLY
+ if (!GL.currArrayBuffer) {
+ GLctx.vertexAttribPointer(GLImmediate.VERTEX, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset);
+ if (this.hasNormal) {
+ var normalAttr = clientAttributes[GLImmediate.NORMAL];
+ GLctx.vertexAttribPointer(GLImmediate.NORMAL, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset);
+ }
+ }
+#else
+ GLctx.vertexAttribPointer(this.positionLocation, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset);
+ GLctx.enableVertexAttribArray(this.positionLocation);
+ if (this.hasNormal) {
+ var normalAttr = clientAttributes[GLImmediate.NORMAL];
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(normalAttr.size, normalAttr.type, GLImmediate.stride, normalAttr.offset);
+#endif
+ GLctx.vertexAttribPointer(this.normalLocation, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset);
+ GLctx.enableVertexAttribArray(this.normalLocation);
+ }
+#endif
+ if (this.hasTextures) {
+ for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
+#if GL_FFP_ONLY
+ if (!GL.currArrayBuffer) {
+ var attribLoc = GLImmediate.TEXTURE0+i;
+ var texAttr = clientAttributes[attribLoc];
+ if (texAttr.size) {
+ GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset);
+ } else {
+ // These two might be dangerous, but let's try them.
+ GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
+ }
+ }
+#else
+ var attribLoc = this.texCoordLocations[i];
+ if (attribLoc === undefined || attribLoc < 0) continue;
+ var texAttr = clientAttributes[GLImmediate.TEXTURE0+i];
+
+ if (texAttr.size) {
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(texAttr.size, texAttr.type, GLImmediate.stride, texAttr.offset);
+#endif
+ GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset);
+ GLctx.enableVertexAttribArray(attribLoc);
+ } else {
+ // These two might be dangerous, but let's try them.
+ GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
+ GLctx.disableVertexAttribArray(attribLoc);
+ }
+#endif
+ var t = 2/*t*/+i;
+ if (this.textureMatrixLocations[i] && this.textureMatrixVersion[t] != GLImmediate.matrixVersion[t]) { // XXX might we need this even without the condition we are currently in?
+ this.textureMatrixVersion[t] = GLImmediate.matrixVersion[t];
+ GLctx.uniformMatrix4fv(this.textureMatrixLocations[i], false, GLImmediate.matrix[t]);
+ }
+ }
+ }
+ if (GLImmediate.enabledClientAttributes[GLImmediate.COLOR]) {
+ var colorAttr = clientAttributes[GLImmediate.COLOR];
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(colorAttr.size, colorAttr.type, GLImmediate.stride, colorAttr.offset);
+#endif
+#if GL_FFP_ONLY
+ if (!GL.currArrayBuffer) {
+ GLctx.vertexAttribPointer(GLImmediate.COLOR, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset);
+ }
+#else
+ GLctx.vertexAttribPointer(this.colorLocation, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset);
+ GLctx.enableVertexAttribArray(this.colorLocation);
+#endif
+ }
+#if !GL_FFP_ONLY
+ else if (this.hasColor) {
+ GLctx.disableVertexAttribArray(this.colorLocation);
+ GLctx.vertexAttrib4fv(this.colorLocation, GLImmediate.clientColor);
+ }
+#endif
+ if (this.hasFog) {
+ if (this.fogColorLocation) GLctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor);
+ if (this.fogEndLocation) GLctx.uniform1f(this.fogEndLocation, GLEmulation.fogEnd);
+ if (this.fogScaleLocation) GLctx.uniform1f(this.fogScaleLocation, 1/(GLEmulation.fogEnd - GLEmulation.fogStart));
+ if (this.fogDensityLocation) GLctx.uniform1f(this.fogDensityLocation, GLEmulation.fogDensity);
+ }
+ },
+
+ cleanup: function cleanup() {
+#if !GL_FFP_ONLY
+ GLctx.disableVertexAttribArray(this.positionLocation);
+ if (this.hasTextures) {
+ for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
+ if (GLImmediate.enabledClientAttributes[GLImmediate.TEXTURE0+i] && this.texCoordLocations[i] >= 0) {
+ GLctx.disableVertexAttribArray(this.texCoordLocations[i]);
+ }
+ }
+ }
+ if (this.hasColor) {
+ GLctx.disableVertexAttribArray(this.colorLocation);
+ }
+ if (this.hasNormal) {
+ GLctx.disableVertexAttribArray(this.normalLocation);
+ }
+ if (!GL.currProgram) {
+ GLctx.useProgram(null);
+ GLImmediate.fixedFunctionProgram = 0;
+ }
+ if (!GL.currArrayBuffer) {
+ GLctx.bindBuffer(GLctx.ARRAY_BUFFER, null);
+ GLImmediate.lastArrayBuffer = null;
+ }
+
+#if GL_UNSAFE_OPTS
+ GLImmediate.lastRenderer = null;
+ GLImmediate.lastProgram = null;
+#endif
+ GLImmediate.matricesModified = true;
+#endif
+ }
+ };
+ ret.init();
+ return ret;
+ },
+
+ setupFuncs: function() {
+ // TexEnv stuff needs to be prepared early, so do it here.
+ // init() is too late for -O2, since it freezes the GL functions
+ // by that point.
+ GLImmediate.MapTreeLib = GLImmediate.spawnMapTreeLib();
+ GLImmediate.spawnMapTreeLib = null;
+
+ GLImmediate.TexEnvJIT = GLImmediate.spawnTexEnvJIT();
+ GLImmediate.spawnTexEnvJIT = null;
+
+ GLImmediate.setupHooks();
+ },
+
+ setupHooks: function() {
+ if (!GLEmulation.hasRunInit) {
+ GLEmulation.init();
+ }
+
+ var glActiveTexture = _glActiveTexture;
+ _glActiveTexture = _emscripten_glActiveTexture = function _glActiveTexture(texture) {
+ GLImmediate.TexEnvJIT.hook_activeTexture(texture);
+ glActiveTexture(texture);
+ };
+ {{{ updateExport('glActiveTexture') }}}
+
+ var glEnable = _glEnable;
+ _glEnable = _emscripten_glEnable = function _glEnable(cap) {
+ GLImmediate.TexEnvJIT.hook_enable(cap);
+ glEnable(cap);
+ };
+ {{{ updateExport('glEnable') }}}
+
+ var glDisable = _glDisable;
+ _glDisable = _emscripten_glDisable = function _glDisable(cap) {
+ GLImmediate.TexEnvJIT.hook_disable(cap);
+ glDisable(cap);
+ };
+ {{{ updateExport('glDisable') }}}
+
+ var glTexEnvf = (typeof(_glTexEnvf) != 'undefined') ? _glTexEnvf : function(){};
+ _glTexEnvf = _emscripten_glTexEnvf = function _glTexEnvf(target, pname, param) {
+ GLImmediate.TexEnvJIT.hook_texEnvf(target, pname, param);
+ // Don't call old func, since we are the implementor.
+ //glTexEnvf(target, pname, param);
+ };
+ {{{ updateExport('glTexEnvf') }}}
+
+ var glTexEnvi = (typeof(_glTexEnvi) != 'undefined') ? _glTexEnvi : function(){};
+ _glTexEnvi = _emscripten_glTexEnvi = function _glTexEnvi(target, pname, param) {
+ GLImmediate.TexEnvJIT.hook_texEnvi(target, pname, param);
+ // Don't call old func, since we are the implementor.
+ //glTexEnvi(target, pname, param);
+ };
+ {{{ updateExport('glTexEnvi') }}}
+
+ var glTexEnvfv = (typeof(_glTexEnvfv) != 'undefined') ? _glTexEnvfv : function(){};
+ _glTexEnvfv = _emscripten_glTexEnvfv = function _glTexEnvfv(target, pname, param) {
+ GLImmediate.TexEnvJIT.hook_texEnvfv(target, pname, param);
+ // Don't call old func, since we are the implementor.
+ //glTexEnvfv(target, pname, param);
+ };
+ {{{ updateExport('glTexEnvfv') }}}
+
+ _glGetTexEnviv = function _glGetTexEnviv(target, pname, param) {
+ GLImmediate.TexEnvJIT.hook_getTexEnviv(target, pname, param);
+ };
+ {{{ updateExport('glGetTexEnviv') }}}
+
+ _glGetTexEnvfv = function _glGetTexEnvfv(target, pname, param) {
+ GLImmediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param);
+ };
+ {{{ updateExport('glGetTexEnvfv') }}}
+
+ var glGetIntegerv = _glGetIntegerv;
+ _glGetIntegerv = _emscripten_glGetIntegerv = function _glGetIntegerv(pname, params) {
+ switch (pname) {
+ case 0x8B8D: { // GL_CURRENT_PROGRAM
+ // Just query directly so we're working with WebGL objects.
+ var cur = GLctx.getParameter(GLctx.CURRENT_PROGRAM);
+ if (cur == GLImmediate.fixedFunctionProgram) {
+ // Pretend we're not using a program.
+ {{{ makeSetValue('params', '0', '0', 'i32') }}};
+ return;
+ }
+ break;
+ }
+ }
+ glGetIntegerv(pname, params);
+ };
+ {{{ updateExport('glGetIntegerv') }}}
+ },
+
+ // Main functions
+ initted: false,
+ init: function() {
+ err('WARNING: using emscripten GL immediate mode emulation. This is very limited in what it supports');
+ GLImmediate.initted = true;
+
+ if (!Module.useWebGL) return; // a 2D canvas may be currently used TODO: make sure we are actually called in that case
+
+ // User can override the maximum number of texture units that we emulate. Using fewer texture units increases runtime performance
+ // slightly, so it is advantageous to choose as small value as needed.
+ GLImmediate.MAX_TEXTURES = Module['GL_MAX_TEXTURE_IMAGE_UNITS'] || GLctx.getParameter(GLctx.MAX_TEXTURE_IMAGE_UNITS);
+
+ GLImmediate.TexEnvJIT.init(GLctx, GLImmediate.MAX_TEXTURES);
+
+ GLImmediate.NUM_ATTRIBUTES = 3 /*pos+normal+color attributes*/ + GLImmediate.MAX_TEXTURES;
+ GLImmediate.clientAttributes = [];
+ GLEmulation.enabledClientAttribIndices = [];
+ for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
+ GLImmediate.clientAttributes.push({});
+ GLEmulation.enabledClientAttribIndices.push(false);
+ }
+
+ // Initialize matrix library
+ // When user sets a matrix, increment a 'version number' on the new data, and when rendering, submit
+ // the matrices to the shader program only if they have an old version of the data.
+ GLImmediate.matrix = [];
+ GLImmediate.matrixStack = [];
+ GLImmediate.matrixVersion = [];
+ for (var i = 0; i < 2 + GLImmediate.MAX_TEXTURES; i++) { // Modelview, Projection, plus one matrix for each texture coordinate.
+ GLImmediate.matrixStack.push([]);
+ GLImmediate.matrixVersion.push(0);
+ GLImmediate.matrix.push(GLImmediate.matrixLib.mat4.create());
+ GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[i]);
+ }
+
+ // Renderer cache
+ GLImmediate.rendererCache = GLImmediate.MapTreeLib.create();
+
+ // Buffers for data
+ GLImmediate.tempData = new Float32Array(GL.MAX_TEMP_BUFFER_SIZE >> 2);
+ GLImmediate.indexData = new Uint16Array(GL.MAX_TEMP_BUFFER_SIZE >> 1);
+
+ GLImmediate.vertexDataU8 = new Uint8Array(GLImmediate.tempData.buffer);
+
+ GL.generateTempBuffers(true, GL.currentContext);
+
+ GLImmediate.clientColor = new Float32Array([1, 1, 1, 1]);
+ },
+
+ // Prepares and analyzes client attributes.
+ // Modifies liveClientAttributes, stride, vertexPointer, vertexCounter
+ // count: number of elements we will draw
+ // beginEnd: whether we are drawing the results of a begin/end block
+ prepareClientAttributes: function prepareClientAttributes(count, beginEnd) {
+ // If no client attributes were modified since we were last called, do nothing. Note that this
+ // does not work for glBegin/End, where we generate renderer components dynamically and then
+ // disable them ourselves, but it does help with glDrawElements/Arrays.
+ if (!GLImmediate.modifiedClientAttributes) {
+#if GL_ASSERTIONS
+ if ((GLImmediate.stride & 3) != 0) {
+ warnOnce('Warning: Rendering from client side vertex arrays where stride (' + GLImmediate.stride + ') is not a multiple of four! This is not currently supported!');
+ }
+#endif
+ GLImmediate.vertexCounter = (GLImmediate.stride * count) / 4; // XXX assuming float
+ return;
+ }
+ GLImmediate.modifiedClientAttributes = false;
+
+ // The role of prepareClientAttributes is to examine the set of client-side vertex attribute buffers
+ // that user code has submitted, and to prepare them to be uploaded to a VBO in GPU memory
+ // (since WebGL does not support client-side rendering, i.e. rendering from vertex data in CPU memory)
+ // User can submit vertex data generally in three different configurations:
+ // 1. Fully planar: all attributes are in their own separate tightly-packed arrays in CPU memory.
+ // 2. Fully interleaved: all attributes share a single array where data is interleaved something like (pos,uv,normal), (pos,uv,normal), ...
+ // 3. Complex hybrid: Multiple separate arrays that either are sparsely strided, and/or partially interleave vertex attributes.
+
+ // For simplicity, we support the case (2) as the fast case. For (1) and (3), we do a memory copy of the
+ // vertex data here to prepare a relayouted buffer that is of the structure in case (2). The reason
+ // for this is that it allows the emulation code to get away with using just one VBO buffer for rendering,
+ // and not have to maintain multiple ones. Therefore cases (1) and (3) will be very slow, and case (2) is fast.
+
+ // Detect which case we are in by using a quick heuristic by examining the strides of the buffers. If all the buffers have identical
+ // stride, we assume we have case (2), otherwise we have something more complex.
+ var clientStartPointer = 0x7FFFFFFF;
+ var bytes = 0; // Total number of bytes taken up by a single vertex.
+ var minStride = 0x7FFFFFFF;
+ var maxStride = 0;
+ var attributes = GLImmediate.liveClientAttributes;
+ attributes.length = 0;
+ for (var i = 0; i < 3+GLImmediate.MAX_TEXTURES; i++) {
+ if (GLImmediate.enabledClientAttributes[i]) {
+ var attr = GLImmediate.clientAttributes[i];
+ attributes.push(attr);
+ clientStartPointer = Math.min(clientStartPointer, attr.pointer);
+ attr.sizeBytes = attr.size * GL.byteSizeByType[attr.type - GL.byteSizeByTypeRoot];
+ bytes += attr.sizeBytes;
+ minStride = Math.min(minStride, attr.stride);
+ maxStride = Math.max(maxStride, attr.stride);
+ }
+ }
+
+ if ((minStride != maxStride || maxStride < bytes) && !beginEnd) {
+ // We are in cases (1) or (3): slow path, shuffle the data around into a single interleaved vertex buffer.
+ // The immediate-mode glBegin()/glEnd() vertex submission gets automatically generated in appropriate layout,
+ // so never need to come down this path if that was used.
+#if GL_ASSERTIONS
+ warnOnce('Rendering from planar client-side vertex arrays. This is a very slow emulation path! Use interleaved vertex arrays for best performance.');
+#endif
+ if (!GLImmediate.restrideBuffer) GLImmediate.restrideBuffer = _malloc(GL.MAX_TEMP_BUFFER_SIZE);
+ var start = GLImmediate.restrideBuffer;
+ bytes = 0;
+ // calculate restrided offsets and total size
+ for (var i = 0; i < attributes.length; i++) {
+ var attr = attributes[i];
+ var size = attr.sizeBytes;
+ if (size % 4 != 0) size += 4 - (size % 4); // align everything
+ attr.offset = bytes;
+ bytes += size;
+ }
+ // copy out the data (we need to know the stride for that, and define attr.pointer)
+ for (var i = 0; i < attributes.length; i++) {
+ var attr = attributes[i];
+ var srcStride = Math.max(attr.sizeBytes, attr.stride);
+ if ((srcStride & 3) == 0 && (attr.sizeBytes & 3) == 0) {
+ var size4 = attr.sizeBytes>>2;
+ var srcStride4 = Math.max(attr.sizeBytes, attr.stride)>>2;
+ for (var j = 0; j < count; j++) {
+ for (var k = 0; k < size4; k++) { // copy in chunks of 4 bytes, our alignment makes this possible
+ HEAP32[((start + attr.offset + bytes*j)>>2) + k] = HEAP32[(attr.pointer>>2) + j*srcStride4 + k];
+ }
+ }
+ } else {
+ for (var j = 0; j < count; j++) {
+ for (var k = 0; k < attr.sizeBytes; k++) { // source data was not aligned to multiples of 4, must copy byte by byte.
+ HEAP8[start + attr.offset + bytes*j + k] = HEAP8[attr.pointer + j*srcStride + k];
+ }
+ }
+ }
+ attr.pointer = start + attr.offset;
+ }
+ GLImmediate.stride = bytes;
+ GLImmediate.vertexPointer = start;
+ } else {
+ // case (2): fast path, all data is interleaved to a single vertex array so we can get away with a single VBO upload.
+ if (GL.currArrayBuffer) {
+ GLImmediate.vertexPointer = 0;
+ } else {
+ GLImmediate.vertexPointer = clientStartPointer;
+ }
+ for (var i = 0; i < attributes.length; i++) {
+ var attr = attributes[i];
+ attr.offset = attr.pointer - GLImmediate.vertexPointer; // Compute what will be the offset of this attribute in the VBO after we upload.
+ }
+ GLImmediate.stride = Math.max(maxStride, bytes);
+ }
+ if (!beginEnd) {
+#if GL_ASSERTIONS
+ if ((GLImmediate.stride & 3) != 0) {
+ warnOnce('Warning: Rendering from client side vertex arrays where stride (' + GLImmediate.stride + ') is not a multiple of four! This is not currently supported!');
+ }
+#endif
+ GLImmediate.vertexCounter = (GLImmediate.stride * count) / 4; // XXX assuming float
+ }
+ },
+
+ flush: function flush(numProvidedIndexes, startIndex, ptr) {
+#if ASSERTIONS
+ assert(numProvidedIndexes >= 0 || !numProvidedIndexes);
+#endif
+ startIndex = startIndex || 0;
+ ptr = ptr || 0;
+
+ var renderer = GLImmediate.getRenderer();
+
+ // Generate index data in a format suitable for GLES 2.0/WebGL
+ var numVertexes = 4 * GLImmediate.vertexCounter / GLImmediate.stride;
+ if (!numVertexes) return;
+#if ASSERTIONS
+ assert(numVertexes % 1 == 0, "`numVertexes` must be an integer.");
+#endif
+ var emulatedElementArrayBuffer = false;
+ var numIndexes = 0;
+ if (numProvidedIndexes) {
+ numIndexes = numProvidedIndexes;
+ if (!GL.currArrayBuffer && GLImmediate.firstVertex > GLImmediate.lastVertex) {
+ // Figure out the first and last vertex from the index data
+#if ASSERTIONS
+ assert(!GL.currElementArrayBuffer); // If we are going to upload array buffer data, we need to find which range to
+ // upload based on the indices. If they are in a buffer on the GPU, that is very
+ // inconvenient! So if you do not have an array buffer, you should also not have
+ // an element array buffer. But best is to use both buffers!
+#endif
+ for (var i = 0; i < numProvidedIndexes; i++) {
+ var currIndex = {{{ makeGetValue('ptr', 'i*2', 'i16', null, 1) }}};
+ GLImmediate.firstVertex = Math.min(GLImmediate.firstVertex, currIndex);
+ GLImmediate.lastVertex = Math.max(GLImmediate.lastVertex, currIndex+1);
+ }
+ }
+ if (!GL.currElementArrayBuffer) {
+ // If no element array buffer is bound, then indices is a literal pointer to clientside data
+#if ASSERTIONS
+ assert(numProvidedIndexes << 1 <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)');
+#endif
+ var indexBuffer = GL.getTempIndexBuffer(numProvidedIndexes << 1);
+ GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, indexBuffer);
+ GLctx.bufferSubData(GLctx.ELEMENT_ARRAY_BUFFER, 0, {{{ makeHEAPView('U16', 'ptr', 'ptr + (numProvidedIndexes << 1)') }}});
+ ptr = 0;
+ emulatedElementArrayBuffer = true;
+ }
+ } else if (GLImmediate.mode > 6) { // above GL_TRIANGLE_FAN are the non-GL ES modes
+ if (GLImmediate.mode != 7) throw 'unsupported immediate mode ' + GLImmediate.mode; // GL_QUADS
+ // GLImmediate.firstVertex is the first vertex we want. Quad indexes are in the pattern
+ // 0 1 2, 0 2 3, 4 5 6, 4 6 7, so we need to look at index firstVertex * 1.5 to see it.
+ // Then since indexes are 2 bytes each, that means 3
+#if ASSERTIONS
+ assert(GLImmediate.firstVertex % 4 == 0);
+#endif
+ ptr = GLImmediate.firstVertex*3;
+ var numQuads = numVertexes / 4;
+ numIndexes = numQuads * 6; // 0 1 2, 0 2 3 pattern
+#if ASSERTIONS
+ assert(ptr + (numIndexes << 1) <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)');
+#endif
+ GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.currentContext.tempQuadIndexBuffer);
+ emulatedElementArrayBuffer = true;
+ }
+
+ renderer.prepare();
+
+ if (numIndexes) {
+ GLctx.drawElements(GLctx.TRIANGLES, numIndexes, GLctx.UNSIGNED_SHORT, ptr);
+ } else {
+ GLctx.drawArrays(GLImmediate.mode, startIndex, numVertexes);
+ }
+
+ if (emulatedElementArrayBuffer) {
+ GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null);
+ }
+
+#if !GL_UNSAFE_OPTS
+#if !GL_FFP_ONLY
+ renderer.cleanup();
+#endif
+#endif
+ }
+ },
+
+ $GLImmediateSetup: {},
+ $GLImmediateSetup__deps: ['$GLImmediate', function() { return 'GLImmediate.matrixLib = ' + read('gl-matrix.js') + ';\n' }],
+ $GLImmediateSetup: {},
+
+ glBegin__deps: ['$GLImmediateSetup'],
+ glBegin: function(mode) {
+ // Push the old state:
+ GLImmediate.enabledClientAttributes_preBegin = GLImmediate.enabledClientAttributes;
+ GLImmediate.enabledClientAttributes = [];
+
+ GLImmediate.clientAttributes_preBegin = GLImmediate.clientAttributes;
+ GLImmediate.clientAttributes = []
+ for (var i = 0; i < GLImmediate.clientAttributes_preBegin.length; i++) {
+ GLImmediate.clientAttributes.push({});
+ }
+
+ GLImmediate.mode = mode;
+ GLImmediate.vertexCounter = 0;
+ var components = GLImmediate.rendererComponents = [];
+ for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
+ components[i] = 0;
+ }
+ GLImmediate.rendererComponentPointer = 0;
+ GLImmediate.vertexData = GLImmediate.tempData;
+ },
+
+ glEnd: function() {
+ GLImmediate.prepareClientAttributes(GLImmediate.rendererComponents[GLImmediate.VERTEX], true);
+ GLImmediate.firstVertex = 0;
+ GLImmediate.lastVertex = GLImmediate.vertexCounter / (GLImmediate.stride >> 2);
+ GLImmediate.flush();
+ GLImmediate.disableBeginEndClientAttributes();
+ GLImmediate.mode = -1;
+
+ // Pop the old state:
+ GLImmediate.enabledClientAttributes = GLImmediate.enabledClientAttributes_preBegin;
+ GLImmediate.clientAttributes = GLImmediate.clientAttributes_preBegin;
+ GLImmediate.currentRenderer = null; // The set of active client attributes changed, we must re-lookup the renderer to use.
+ GLImmediate.modifiedClientAttributes = true;
+ },
+
+ glVertex2f: function(x, y) {
+#if ASSERTIONS
+ assert(GLImmediate.mode >= 0); // must be in begin/end
+#endif
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = 0;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = 1;
+#if ASSERTIONS
+ assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
+#endif
+ GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
+ },
+
+ glVertex3f: function(x, y, z) {
+#if ASSERTIONS
+ assert(GLImmediate.mode >= 0); // must be in begin/end
+#endif
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = z;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = 1;
+#if ASSERTIONS
+ assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
+#endif
+ GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
+ },
+
+ glVertex4f: function(x, y, z, w) {
+#if ASSERTIONS
+ assert(GLImmediate.mode >= 0); // must be in begin/end
+#endif
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = z;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = w;
+#if ASSERTIONS
+ assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
+#endif
+ GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
+ },
+
+ glVertex2fv__deps: ['glVertex2f'],
+ glVertex2fv: function(p) {
+ _glVertex2f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}});
+ },
+
+ glVertex3fv__deps: ['glVertex3f'],
+ glVertex3fv: function(p) {
+ _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}});
+ },
+
+ glVertex4fv__deps: ['glVertex4f'],
+ glVertex4fv: function(p) {
+ _glVertex4f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}, {{{ makeGetValue('p', '12', 'float') }}});
+ },
+
+ glVertex2i: 'glVertex2f',
+
+ glVertex3i: 'glVertex3f',
+
+ glVertex4i: 'glVertex4f',
+
+ glTexCoord2i: function(u, v) {
+#if ASSERTIONS
+ assert(GLImmediate.mode >= 0); // must be in begin/end
+#endif
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = u;
+ GLImmediate.vertexData[GLImmediate.vertexCounter++] = v;
+ GLImmediate.addRendererComponent(GLImmediate.TEXTURE0, 2, GLctx.FLOAT);
+ },
+ glTexCoord2f: 'glTexCoord2i',
+
+ glTexCoord2fv__deps: ['glTexCoord2i'],
+ glTexCoord2fv: function(v) {
+ _glTexCoord2i({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}});
+ },
+
+ glTexCoord4f: function() { throw 'glTexCoord4f: TODO' },
+
+ glColor4f: function(r, g, b, a) {
+ r = Math.max(Math.min(r, 1), 0);
+ g = Math.max(Math.min(g, 1), 0);
+ b = Math.max(Math.min(b, 1), 0);
+ a = Math.max(Math.min(a, 1), 0);
+
+ // TODO: make ub the default, not f, save a few mathops
+ if (GLImmediate.mode >= 0) {
+ var start = GLImmediate.vertexCounter << 2;
+ GLImmediate.vertexDataU8[start + 0] = r * 255;
+ GLImmediate.vertexDataU8[start + 1] = g * 255;
+ GLImmediate.vertexDataU8[start + 2] = b * 255;
+ GLImmediate.vertexDataU8[start + 3] = a * 255;
+ GLImmediate.vertexCounter++;
+ GLImmediate.addRendererComponent(GLImmediate.COLOR, 4, GLctx.UNSIGNED_BYTE);
+ } else {
+ GLImmediate.clientColor[0] = r;
+ GLImmediate.clientColor[1] = g;
+ GLImmediate.clientColor[2] = b;
+ GLImmediate.clientColor[3] = a;
+#if GL_FFP_ONLY
+ GLctx.vertexAttrib4fv(GLImmediate.COLOR, GLImmediate.clientColor);
+#endif
+ }
+ },
+ glColor4d: 'glColor4f',
+ glColor4ub__deps: ['glColor4f'],
+ glColor4ub: function(r, g, b, a) {
+ _glColor4f((r&255)/255, (g&255)/255, (b&255)/255, (a&255)/255);
+ },
+ glColor4us__deps: ['glColor4f'],
+ glColor4us: function(r, g, b, a) {
+ _glColor4f((r&65535)/65535, (g&65535)/65535, (b&65535)/65535, (a&65535)/65535);
+ },
+ glColor4ui__deps: ['glColor4f'],
+ glColor4ui: function(r, g, b, a) {
+ _glColor4f((r>>>0)/4294967295, (g>>>0)/4294967295, (b>>>0)/4294967295, (a>>>0)/4294967295);
+ },
+ glColor3f__deps: ['glColor4f'],
+ glColor3f: function(r, g, b) {
+ _glColor4f(r, g, b, 1);
+ },
+ glColor3d: 'glColor3f',
+ glColor3ub__deps: ['glColor4ub'],
+ glColor3ub: function(r, g, b) {
+ _glColor4ub(r, g, b, 255);
+ },
+ glColor3us__deps: ['glColor4us'],
+ glColor3us: function(r, g, b) {
+ _glColor4us(r, g, b, 65535);
+ },
+ glColor3ui__deps: ['glColor4ui'],
+ glColor3ui: function(r, g, b) {
+ _glColor4ui(r, g, b, 4294967295);
+ },
+
+ glColor3ubv__deps: ['glColor3ub'],
+ glColor3ubv: function(p) {
+ _glColor3ub({{{ makeGetValue('p', '0', 'i8') }}}, {{{ makeGetValue('p', '1', 'i8') }}}, {{{ makeGetValue('p', '2', 'i8') }}});
+ },
+ glColor3usv__deps: ['glColor3us'],
+ glColor3usv: function(p) {
+ _glColor3us({{{ makeGetValue('p', '0', 'i16') }}}, {{{ makeGetValue('p', '2', 'i16') }}}, {{{ makeGetValue('p', '4', 'i16') }}});
+ },
+ glColor3uiv__deps: ['glColor3ui'],
+ glColor3uiv: function(p) {
+ _glColor3ui({{{ makeGetValue('p', '0', 'i32') }}}, {{{ makeGetValue('p', '4', 'i32') }}}, {{{ makeGetValue('p', '8', 'i32') }}});
+ },
+ glColor3fv__deps: ['glColor3f'],
+ glColor3fv: function(p) {
+ _glColor3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}});
+ },
+ glColor4fv__deps: ['glColor4f'],
+ glColor4fv: function(p) {
+ _glColor4f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}, {{{ makeGetValue('p', '12', 'float') }}});
+ },
+
+ glColor4ubv__deps: ['glColor4ub'],
+ glColor4ubv: function(p) {
+ _glColor4ub({{{ makeGetValue('p', '0', 'i8') }}}, {{{ makeGetValue('p', '1', 'i8') }}}, {{{ makeGetValue('p', '2', 'i8') }}}, {{{ makeGetValue('p', '3', 'i8') }}});
+ },
+
+ glFogf: function(pname, param) { // partial support, TODO
+ switch(pname) {
+ case 0x0B63: // GL_FOG_START
+ 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
+ if (GLEmulation.fogMode != param) {
+ GLImmediate.currentRenderer = null; // Fog mode is part of the FFP shader state, we must re-lookup the renderer to use.
+ GLEmulation.fogMode = param;
+ }
+ break;
+ default: // default to GL_EXP
+ if (GLEmulation.fogMode != 0x0800 /* GL_EXP */) {
+ GLImmediate.currentRenderer = null; // Fog mode is part of the FFP shader state, we must re-lookup the renderer to use.
+ GLEmulation.fogMode = 0x0800 /* GL_EXP */;
+ }
+ break;
+ }
+ break;
+ }
+ },
+ 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
+ GLEmulation.fogColor[0] = {{{ makeGetValue('param', '0', 'float') }}};
+ GLEmulation.fogColor[1] = {{{ makeGetValue('param', '4', 'float') }}};
+ 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
+
+ glAlphaFunc: function(){}, // TODO
+
+ glNormal3f: function(){}, // TODO
+
+ // Additional non-GLES rendering calls
+
+ glDrawRangeElements__deps: ['glDrawElements'],
+ glDrawRangeElements__sig: 'viiiiii',
+ glDrawRangeElements: function(mode, start, end, count, type, indices) {
+ _glDrawElements(mode, count, type, indices, start, end);
+ },
+
+ // ClientState/gl*Pointer
+
+ glEnableClientState: function(cap) {
+ var attrib = GLEmulation.getAttributeFromCapability(cap);
+ if (attrib === null) {
+#if ASSERTIONS
+ err('WARNING: unhandled clientstate: ' + cap);
+#endif
+ return;
+ }
+ if (!GLImmediate.enabledClientAttributes[attrib]) {
+ GLImmediate.enabledClientAttributes[attrib] = true;
+ GLImmediate.totalEnabledClientAttributes++;
+ GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed.
+#if GL_FFP_ONLY
+ // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here.
+ GL.enableVertexAttribArray(attrib);
+#endif
+ if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1;
+ GLImmediate.modifiedClientAttributes = true;
+ }
+ },
+ glDisableClientState: function(cap) {
+ var attrib = GLEmulation.getAttributeFromCapability(cap);
+ if (attrib === null) {
+#if ASSERTIONS
+ err('WARNING: unhandled clientstate: ' + cap);
+#endif
+ return;
+ }
+ if (GLImmediate.enabledClientAttributes[attrib]) {
+ GLImmediate.enabledClientAttributes[attrib] = false;
+ GLImmediate.totalEnabledClientAttributes--;
+ GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed.
+#if GL_FFP_ONLY
+ // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here.
+ GL.disableVertexAttribArray(attrib);
+#endif
+ if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap];
+ GLImmediate.modifiedClientAttributes = true;
+ }
+ },
+
+ glVertexPointer__deps: ['$GLEmulation'], // if any pointers are used, glVertexPointer must be, and if it is, then we need emulation
+ glVertexPointer: function(size, type, stride, pointer) {
+ GLImmediate.setClientAttribute(GLImmediate.VERTEX, size, type, stride, pointer);
+#if GL_FFP_ONLY
+ if (GL.currArrayBuffer) {
+ GLctx.vertexAttribPointer(GLImmediate.VERTEX, size, type, false, stride, pointer);
+ }
+#endif
+ },
+ glTexCoordPointer: function(size, type, stride, pointer) {
+ GLImmediate.setClientAttribute(GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture, size, type, stride, pointer);
+#if GL_FFP_ONLY
+ if (GL.currArrayBuffer) {
+ var loc = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture;
+ GLctx.vertexAttribPointer(loc, size, type, false, stride, pointer);
+ }
+#endif
+ },
+ glNormalPointer: function(type, stride, pointer) {
+ GLImmediate.setClientAttribute(GLImmediate.NORMAL, 3, type, stride, pointer);
+#if GL_FFP_ONLY
+ if (GL.currArrayBuffer) {
+ GLctx.vertexAttribPointer(GLImmediate.NORMAL, 3, type, true, stride, pointer);
+ }
+#endif
+ },
+ glColorPointer: function(size, type, stride, pointer) {
+ GLImmediate.setClientAttribute(GLImmediate.COLOR, size, type, stride, pointer);
+#if GL_FFP_ONLY
+ if (GL.currArrayBuffer) {
+ GLctx.vertexAttribPointer(GLImmediate.COLOR, size, type, true, stride, pointer);
+ }
+#endif
+ },
+
+ glClientActiveTexture__sig: 'vi',
+ glClientActiveTexture: function(texture) {
+ GLImmediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0
+ },
+
+ // Replace some functions with immediate-mode aware versions. If there are no client
+ // attributes enabled, and we use webgl-friendly modes (no GL_QUADS), then no need
+ // for emulation
+ glDrawArrays: function(mode, first, count) {
+ if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6) {
+ GLctx.drawArrays(mode, first, count);
+ return;
+ }
+ GLImmediate.prepareClientAttributes(count, false);
+ GLImmediate.mode = mode;
+ if (!GL.currArrayBuffer) {
+ GLImmediate.vertexData = {{{ makeHEAPView('F32', 'GLImmediate.vertexPointer', 'GLImmediate.vertexPointer + (first+count)*GLImmediate.stride') }}}; // XXX assuming float
+ GLImmediate.firstVertex = first;
+ GLImmediate.lastVertex = first + count;
+ }
+ GLImmediate.flush(null, first);
+ GLImmediate.mode = -1;
+ },
+
+ glDrawElements: function(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements
+ if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6 && GL.currElementArrayBuffer) {
+ GLctx.drawElements(mode, count, type, indices);
+ return;
+ }
+#if ASSERTIONS
+ if (!GL.currElementArrayBuffer) {
+ assert(type == GLctx.UNSIGNED_SHORT); // We can only emulate buffers of this kind, for now
+ }
+ console.log("DrawElements doesn't actually prepareClientAttributes properly.");
+#endif
+ GLImmediate.prepareClientAttributes(count, false);
+ GLImmediate.mode = mode;
+ if (!GL.currArrayBuffer) {
+ GLImmediate.firstVertex = end ? start : TOTAL_MEMORY; // if we don't know the start, set an invalid value and we will calculate it later from the indices
+ GLImmediate.lastVertex = end ? end+1 : 0;
+ GLImmediate.vertexData = HEAPF32.subarray(GLImmediate.vertexPointer >> 2, end ? (GLImmediate.vertexPointer + (end+1)*GLImmediate.stride) >> 2 : undefined); // XXX assuming float
+ }
+ GLImmediate.flush(count, 0, indices);
+ GLImmediate.mode = -1;
+ },
+
+ // Vertex array object (VAO) support. TODO: when the WebGL extension is popular, use that and remove this code and GL.vaos
+ emulGlGenVertexArrays__deps: ['$GLEmulation'],
+ emulGlGenVertexArrays__sig: 'vii',
+ emulGlGenVertexArrays: function(n, vaos) {
+ for (var i = 0; i < n; i++) {
+ var id = GL.getNewId(GLEmulation.vaos);
+ GLEmulation.vaos[id] = {
+ id: id,
+ arrayBuffer: 0,
+ elementArrayBuffer: 0,
+ enabledVertexAttribArrays: {},
+ vertexAttribPointers: {},
+ enabledClientStates: {},
+ };
+ {{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}};
+ }
+ },
+ emulGlDeleteVertexArrays__sig: 'vii',
+ emulGlDeleteVertexArrays: function(n, vaos) {
+ for (var i = 0; i < n; i++) {
+ var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}};
+ GLEmulation.vaos[id] = null;
+ if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null;
+ }
+ },
+ emulGlIsVertexArray__sig: 'vi',
+ emulGlIsVertexArray: function(array) {
+ var vao = GLEmulation.vaos[array];
+ if (!vao) return 0;
+ return 1;
+ },
+ emulGlBindVertexArray__deps: ['glBindBuffer', 'glEnableVertexAttribArray', 'glVertexAttribPointer', 'glEnableClientState'],
+ emulGlBindVertexArray__sig: 'vi',
+ emulGlBindVertexArray: function(vao) {
+ // undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao
+ GLEmulation.currentVao = null; // make sure the commands we run here are not recorded
+ if (GLImmediate.lastRenderer) GLImmediate.lastRenderer.cleanup();
+ _glBindBuffer(GLctx.ARRAY_BUFFER, 0); // XXX if one was there before we were bound?
+ _glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, 0);
+ for (var vaa in GLEmulation.enabledVertexAttribArrays) {
+ GLctx.disableVertexAttribArray(vaa);
+ }
+ GLEmulation.enabledVertexAttribArrays = {};
+ GLImmediate.enabledClientAttributes = [0, 0];
+ GLImmediate.totalEnabledClientAttributes = 0;
+ GLImmediate.modifiedClientAttributes = true;
+ if (vao) {
+ // replay vao
+ var info = GLEmulation.vaos[vao];
+ _glBindBuffer(GLctx.ARRAY_BUFFER, info.arrayBuffer); // XXX overwrite current binding?
+ _glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer);
+ for (var vaa in info.enabledVertexAttribArrays) {
+ _glEnableVertexAttribArray(vaa);
+ }
+ for (var vaa in info.vertexAttribPointers) {
+ _glVertexAttribPointer.apply(null, info.vertexAttribPointers[vaa]);
+ }
+ for (var attrib in info.enabledClientStates) {
+ _glEnableClientState(attrib|0);
+ }
+ GLEmulation.currentVao = info; // set currentVao last, so the commands we ran here were not recorded
+ }
+ },
+
+ // OpenGL Immediate Mode matrix routines.
+ // Note that in the future we might make these available only in certain modes.
+ glMatrixMode__deps: ['$GL', '$GLImmediateSetup', '$GLEmulation'], // emulation is not strictly needed, this is a workaround
+ glMatrixMode: function(mode) {
+ if (mode == 0x1700 /* GL_MODELVIEW */) {
+ GLImmediate.currentMatrix = 0/*m*/;
+ } else if (mode == 0x1701 /* GL_PROJECTION */) {
+ GLImmediate.currentMatrix = 1/*p*/;
+ } else if (mode == 0x1702) { // GL_TEXTURE
+ GLImmediate.useTextureMatrix = true;
+ GLImmediate.currentMatrix = 2/*t*/ + GLImmediate.clientActiveTexture;
+ } else {
+ throw "Wrong mode " + mode + " passed to glMatrixMode";
+ }
+ },
+
+ glPushMatrix: function() {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixStack[GLImmediate.currentMatrix].push(
+ Array.prototype.slice.call(GLImmediate.matrix[GLImmediate.currentMatrix]));
+ },
+
+ glPopMatrix: function() {
+ if (GLImmediate.matrixStack[GLImmediate.currentMatrix].length == 0) {
+ GL.recordError(0x0504/*GL_STACK_UNDERFLOW*/);
+ return;
+ }
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrix[GLImmediate.currentMatrix] = GLImmediate.matrixStack[GLImmediate.currentMatrix].pop();
+ },
+
+ glLoadIdentity__deps: ['$GL', '$GLImmediateSetup'],
+ glLoadIdentity: function() {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[GLImmediate.currentMatrix]);
+ },
+
+ glLoadMatrixd: function(matrix) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
+ },
+
+ glLoadMatrixf: function(matrix) {
+#if GL_DEBUG
+ if (GL.debug) err('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16)));
+#endif
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
+ },
+
+ glLoadTransposeMatrixd: function(matrix) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
+ GLImmediate.matrixLib.mat4.transpose(GLImmediate.matrix[GLImmediate.currentMatrix]);
+ },
+
+ glLoadTransposeMatrixf: function(matrix) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
+ GLImmediate.matrixLib.mat4.transpose(GLImmediate.matrix[GLImmediate.currentMatrix]);
+ },
+
+ glMultMatrixd: function(matrix) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
+ {{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}});
+ },
+
+ glMultMatrixf: function(matrix) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
+ {{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}});
+ },
+
+ glMultTransposeMatrixd: function(matrix) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ var colMajor = GLImmediate.matrixLib.mat4.create();
+ GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, colMajor);
+ GLImmediate.matrixLib.mat4.transpose(colMajor);
+ GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], colMajor);
+ },
+
+ glMultTransposeMatrixf: function(matrix) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ var colMajor = GLImmediate.matrixLib.mat4.create();
+ GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, colMajor);
+ GLImmediate.matrixLib.mat4.transpose(colMajor);
+ GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], colMajor);
+ },
+
+ glFrustum: function(left, right, bottom, top_, nearVal, farVal) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
+ GLImmediate.matrixLib.mat4.frustum(left, right, bottom, top_, nearVal, farVal));
+ },
+ glFrustumf: 'glFrustum',
+
+ glOrtho: function(left, right, bottom, top_, nearVal, farVal) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
+ GLImmediate.matrixLib.mat4.ortho(left, right, bottom, top_, nearVal, farVal));
+ },
+ glOrthof: 'glOrtho',
+
+ glScaled: function(x, y, z) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.scale(GLImmediate.matrix[GLImmediate.currentMatrix], [x, y, z]);
+ },
+ glScalef: 'glScaled',
+
+ glTranslated: function(x, y, z) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.translate(GLImmediate.matrix[GLImmediate.currentMatrix], [x, y, z]);
+ },
+ glTranslatef: 'glTranslated',
+
+ glRotated: function(angle, x, y, z) {
+ GLImmediate.matricesModified = true;
+ GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
+ GLImmediate.matrixLib.mat4.rotate(GLImmediate.matrix[GLImmediate.currentMatrix], angle*Math.PI/180, [x, y, z]);
+ },
+ glRotatef: 'glRotated',
+
+ glDrawBuffer: function() { throw 'glDrawBuffer: TODO' },
+#if !USE_WEBGL2
+ glReadBuffer: function() { throw 'glReadBuffer: TODO' },
+#endif
+
+ glLightfv: function() { throw 'glLightfv: TODO' },
+ glLightModelfv: function() { throw 'glLightModelfv: TODO' },
+ glMaterialfv: function() { throw 'glMaterialfv: TODO' },
+
+ glTexGeni: function() { throw 'glTexGeni: TODO' },
+ glTexGenfv: function() { throw 'glTexGenfv: TODO' },
+ glTexEnvi: function() { warnOnce('glTexEnvi: TODO') },
+ glTexEnvf: function() { warnOnce('glTexEnvf: TODO') },
+ glTexEnvfv: function() { warnOnce('glTexEnvfv: TODO') },
+
+ glGetTexEnviv: function(target, pname, param) { throw 'GL emulation not initialized!'; },
+ glGetTexEnvfv: function(target, pname, param) { throw 'GL emulation not initialized!'; },
+
+ glTexImage1D: function() { throw 'glTexImage1D: TODO' },
+ glTexCoord3f: function() { throw 'glTexCoord3f: TODO' },
+ glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' },
+
+ glShadeModel: function() { warnOnce('TODO: glShadeModel') },
+
+ // Open GLES1.1 compatibility
+
+ glGenFramebuffersOES : 'glGenFramebuffers',
+ glGenRenderbuffersOES : 'glGenRenderbuffers',
+ glBindFramebufferOES : 'glBindFramebuffer',
+ glBindRenderbufferOES : 'glBindRenderbuffer',
+ glGetRenderbufferParameterivOES : 'glGetRenderbufferParameteriv',
+ glFramebufferRenderbufferOES : 'glFramebufferRenderbuffer',
+ glRenderbufferStorageOES : 'glRenderbufferStorage',
+ glCheckFramebufferStatusOES : 'glCheckFramebufferStatus',
+ glDeleteFramebuffersOES : 'glDeleteFramebuffers',
+ glDeleteRenderbuffersOES : 'glDeleteRenderbuffers',
+ glFramebufferTexture2DOES: 'glFramebufferTexture2D',
+};
+
+// Legacy GL emulation
+if (LEGACY_GL_EMULATION) {
+ DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push('$GLEmulation');
+}
+
+recordGLProcAddressGet(LibraryGLEmulation);
+
+mergeInto(LibraryManager.library, LibraryGLEmulation);
+
+assert(!(FULL_ES2 && LEGACY_GL_EMULATION), 'cannot emulate both ES2 and legacy GL');
+assert(!(FULL_ES3 && LEGACY_GL_EMULATION), 'cannot emulate both ES3 and legacy GL');
diff --git a/src/modules.js b/src/modules.js
index f2fff8f..2777e0a 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -168,6 +168,9 @@
]);
}
+ if (LEGACY_GL_EMULATION) {
+ libraries.push('library_glemu.js');
+ }
// If there are any explicitly specified system JS libraries to link to, add those to link.
if (SYSTEM_JS_LIBRARIES) {
libraries = libraries.concat(SYSTEM_JS_LIBRARIES.split(','));
diff --git a/system/lib/gl/gl.c b/system/lib/gl/gl.c
index 095b611..e6ae4ef 100644
--- a/system/lib/gl/gl.c
+++ b/system/lib/gl/gl.c
@@ -2397,175 +2397,177 @@
if (!strcmp(name, "glFramebufferTexture3D")) return emscripten_glFramebufferTexture3D;
if (!strcmp(name, "glTexImage2D")) return emscripten_glTexImage2D;
if (!strcmp(name, "glGetShaderPrecisionFormat")) return emscripten_glGetShaderPrecisionFormat;
+
// If LEGACY_GL_EMULATION is on, allow access to those functions too
- if (EM_ASM_INT({ return GL.legacyGLEmulation })) {
- if (!strcmp(name, "glDeleteObject")) return emscripten_glDeleteObjectARB;
- if (!strcmp(name, "glGetObjectParameteriv")) return emscripten_glGetObjectParameterivARB;
- if (!strcmp(name, "glGetInfoLog")) return emscripten_glGetInfoLogARB;
- if (!strcmp(name, "glBindProgram")) return emscripten_glBindProgramARB;
- if (!strcmp(name, "glGetPointerv")) return emscripten_glGetPointerv;
- if (!strcmp(name, "glBegin")) return emscripten_glBegin;
- if (!strcmp(name, "glEnd")) return emscripten_glEnd;
- if (!strcmp(name, "glVertex2d")) return emscripten_glVertex2d;
- if (!strcmp(name, "glVertex2f")) return emscripten_glVertex2f;
- if (!strcmp(name, "glVertex2i")) return emscripten_glVertex2i;
- if (!strcmp(name, "glVertex2s")) return emscripten_glVertex2s;
- if (!strcmp(name, "glVertex3d")) return emscripten_glVertex3d;
- if (!strcmp(name, "glVertex3f")) return emscripten_glVertex3f;
- if (!strcmp(name, "glVertex3i")) return emscripten_glVertex3i;
- if (!strcmp(name, "glVertex3s")) return emscripten_glVertex3s;
- if (!strcmp(name, "glVertex4d")) return emscripten_glVertex4d;
- if (!strcmp(name, "glVertex4f")) return emscripten_glVertex4f;
- if (!strcmp(name, "glVertex4i")) return emscripten_glVertex4i;
- if (!strcmp(name, "glVertex4s")) return emscripten_glVertex4s;
- if (!strcmp(name, "glVertex2dv")) return emscripten_glVertex2dv;
- if (!strcmp(name, "glVertex2fv")) return emscripten_glVertex2fv;
- if (!strcmp(name, "glVertex2iv")) return emscripten_glVertex2iv;
- if (!strcmp(name, "glVertex2sv")) return emscripten_glVertex2sv;
- if (!strcmp(name, "glVertex3dv")) return emscripten_glVertex3dv;
- if (!strcmp(name, "glVertex3fv")) return emscripten_glVertex3fv;
- if (!strcmp(name, "glVertex3iv")) return emscripten_glVertex3iv;
- if (!strcmp(name, "glVertex3sv")) return emscripten_glVertex3sv;
- if (!strcmp(name, "glVertex4dv")) return emscripten_glVertex4dv;
- if (!strcmp(name, "glVertex4fv")) return emscripten_glVertex4fv;
- if (!strcmp(name, "glVertex4iv")) return emscripten_glVertex4iv;
- if (!strcmp(name, "glVertex4sv")) return emscripten_glVertex4sv;
- if (!strcmp(name, "glColor3b")) return emscripten_glColor3b;
- if (!strcmp(name, "glColor3d")) return emscripten_glColor3d;
- if (!strcmp(name, "glColor3f")) return emscripten_glColor3f;
- if (!strcmp(name, "glColor3i")) return emscripten_glColor3i;
- if (!strcmp(name, "glColor3s")) return emscripten_glColor3s;
- if (!strcmp(name, "glColor3ub")) return emscripten_glColor3ub;
- if (!strcmp(name, "glColor3ui")) return emscripten_glColor3ui;
- if (!strcmp(name, "glColor3us")) return emscripten_glColor3us;
- if (!strcmp(name, "glColor4b")) return emscripten_glColor4b;
- if (!strcmp(name, "glColor4d")) return emscripten_glColor4d;
- if (!strcmp(name, "glColor4f")) return emscripten_glColor4f;
- if (!strcmp(name, "glColor4i")) return emscripten_glColor4i;
- if (!strcmp(name, "glColor4s")) return emscripten_glColor4s;
- if (!strcmp(name, "glColor4ub")) return emscripten_glColor4ub;
- if (!strcmp(name, "glColor4ui")) return emscripten_glColor4ui;
- if (!strcmp(name, "glColor4us")) return emscripten_glColor4us;
- if (!strcmp(name, "glColor3bv")) return emscripten_glColor3bv;
- if (!strcmp(name, "glColor3dv")) return emscripten_glColor3dv;
- if (!strcmp(name, "glColor3fv")) return emscripten_glColor3fv;
- if (!strcmp(name, "glColor3iv")) return emscripten_glColor3iv;
- if (!strcmp(name, "glColor3sv")) return emscripten_glColor3sv;
- if (!strcmp(name, "glColor3ubv")) return emscripten_glColor3ubv;
- if (!strcmp(name, "glColor3uiv")) return emscripten_glColor3uiv;
- if (!strcmp(name, "glColor3usv")) return emscripten_glColor3usv;
- if (!strcmp(name, "glColor4bv")) return emscripten_glColor4bv;
- if (!strcmp(name, "glColor4dv")) return emscripten_glColor4dv;
- if (!strcmp(name, "glColor4fv")) return emscripten_glColor4fv;
- if (!strcmp(name, "glColor4iv")) return emscripten_glColor4iv;
- if (!strcmp(name, "glColor4sv")) return emscripten_glColor4sv;
- if (!strcmp(name, "glColor4ubv")) return emscripten_glColor4ubv;
- if (!strcmp(name, "glColor4uiv")) return emscripten_glColor4uiv;
- if (!strcmp(name, "glColor4usv")) return emscripten_glColor4usv;
- if (!strcmp(name, "glFogCoordf")) return emscripten_glFogCoordf;
- if (!strcmp(name, "glFogCoordfv")) return emscripten_glFogCoordfv;
- if (!strcmp(name, "glFogCoordd")) return emscripten_glFogCoordd;
- if (!strcmp(name, "glFogCoorddv")) return emscripten_glFogCoorddv;
- if (!strcmp(name, "glFogCoordPointer")) return emscripten_glFogCoordPointer;
- if (!strcmp(name, "glPolygonMode")) return emscripten_glPolygonMode;
- if (!strcmp(name, "glAlphaFunc")) return emscripten_glAlphaFunc;
- if (!strcmp(name, "glNormal3b")) return emscripten_glNormal3b;
- if (!strcmp(name, "glNormal3d")) return emscripten_glNormal3d;
- if (!strcmp(name, "glNormal3f")) return emscripten_glNormal3f;
- if (!strcmp(name, "glNormal3i")) return emscripten_glNormal3i;
- if (!strcmp(name, "glNormal3s")) return emscripten_glNormal3s;
- if (!strcmp(name, "glNormal3bv")) return emscripten_glNormal3bv;
- if (!strcmp(name, "glNormal3dv")) return emscripten_glNormal3dv;
- if (!strcmp(name, "glNormal3fv")) return emscripten_glNormal3fv;
- if (!strcmp(name, "glNormal3iv")) return emscripten_glNormal3iv;
- if (!strcmp(name, "glNormal3sv")) return emscripten_glNormal3sv;
- if (!strcmp(name, "glDrawRangeElements")) return emscripten_glDrawRangeElements;
- if (!strcmp(name, "glEnableClientState")) return emscripten_glEnableClientState;
- if (!strcmp(name, "glDisableClientState")) return emscripten_glDisableClientState;
- if (!strcmp(name, "glVertexPointer")) return emscripten_glVertexPointer;
- if (!strcmp(name, "glNormalPointer")) return emscripten_glNormalPointer;
- if (!strcmp(name, "glColorPointer")) return emscripten_glColorPointer;
- if (!strcmp(name, "glTexCoordPointer")) return emscripten_glTexCoordPointer;
- if (!strcmp(name, "glClientActiveTexture")) return emscripten_glClientActiveTexture;
- if (!strcmp(name, "glMatrixMode")) return emscripten_glMatrixMode;
- if (!strcmp(name, "glPushMatrix")) return emscripten_glPushMatrix;
- if (!strcmp(name, "glPopMatrix")) return emscripten_glPopMatrix;
- if (!strcmp(name, "glLoadIdentity")) return emscripten_glLoadIdentity;
- if (!strcmp(name, "glLoadMatrixd")) return emscripten_glLoadMatrixd;
- if (!strcmp(name, "glLoadMatrixf")) return emscripten_glLoadMatrixf;
- if (!strcmp(name, "glLoadTransposeMatrixf")) return emscripten_glLoadTransposeMatrixfARB;
- if (!strcmp(name, "glLoadTransposeMatrixd")) return emscripten_glLoadTransposeMatrixdARB;
- if (!strcmp(name, "glMultTransposeMatrixf")) return emscripten_glMultTransposeMatrixfARB;
- if (!strcmp(name, "glMultTransposeMatrixd")) return emscripten_glMultTransposeMatrixdARB;
- if (!strcmp(name, "glMultMatrixd")) return emscripten_glMultMatrixd;
- if (!strcmp(name, "glMultMatrixf")) return emscripten_glMultMatrixf;
- if (!strcmp(name, "glOrtho")) return emscripten_glOrtho;
- if (!strcmp(name, "glFrustum")) return emscripten_glFrustum;
- if (!strcmp(name, "glRotated")) return emscripten_glRotated;
- if (!strcmp(name, "glRotatef")) return emscripten_glRotatef;
- if (!strcmp(name, "glScaled")) return emscripten_glScaled;
- if (!strcmp(name, "glScalef")) return emscripten_glScalef;
- if (!strcmp(name, "glTranslated")) return emscripten_glTranslated;
- if (!strcmp(name, "glTranslatef")) return emscripten_glTranslatef;
- if (!strcmp(name, "glDrawBuffer")) return emscripten_glDrawBuffer;
- if (!strcmp(name, "glReadBuffer")) return emscripten_glReadBuffer;
- if (!strcmp(name, "glLightf")) return emscripten_glLightf;
- if (!strcmp(name, "glLighti")) return emscripten_glLighti;
- if (!strcmp(name, "glLightfv")) return emscripten_glLightfv;
- if (!strcmp(name, "glLightiv")) return emscripten_glLightiv;
- if (!strcmp(name, "glLightModelf")) return emscripten_glLightModelf;
- if (!strcmp(name, "glLightModeli")) return emscripten_glLightModeli;
- if (!strcmp(name, "glLightModelfv")) return emscripten_glLightModelfv;
- if (!strcmp(name, "glLightModeliv")) return emscripten_glLightModeliv;
- if (!strcmp(name, "glMaterialf")) return emscripten_glMaterialf;
- if (!strcmp(name, "glMateriali")) return emscripten_glMateriali;
- if (!strcmp(name, "glMaterialfv")) return emscripten_glMaterialfv;
- if (!strcmp(name, "glMaterialiv")) return emscripten_glMaterialiv;
- if (!strcmp(name, "glTexGend")) return emscripten_glTexGend;
- if (!strcmp(name, "glTexGenf")) return emscripten_glTexGenf;
- if (!strcmp(name, "glTexGeni")) return emscripten_glTexGeni;
- if (!strcmp(name, "glTexGendv")) return emscripten_glTexGendv;
- if (!strcmp(name, "glTexGenfv")) return emscripten_glTexGenfv;
- if (!strcmp(name, "glTexGeniv")) return emscripten_glTexGeniv;
- if (!strcmp(name, "glGetTexEnvfv")) return emscripten_glGetTexEnvfv;
- if (!strcmp(name, "glGetTexEnviv")) return emscripten_glGetTexEnviv;
- if (!strcmp(name, "glTexImage1D")) return emscripten_glTexImage1D;
- if (!strcmp(name, "glTexCoord1d")) return emscripten_glTexCoord1d;
- if (!strcmp(name, "glTexCoord1f")) return emscripten_glTexCoord1f;
- if (!strcmp(name, "glTexCoord1i")) return emscripten_glTexCoord1i;
- if (!strcmp(name, "glTexCoord1s")) return emscripten_glTexCoord1s;
- if (!strcmp(name, "glTexCoord2d")) return emscripten_glTexCoord2d;
- if (!strcmp(name, "glTexCoord2f")) return emscripten_glTexCoord2f;
- if (!strcmp(name, "glTexCoord2i")) return emscripten_glTexCoord2i;
- if (!strcmp(name, "glTexCoord2s")) return emscripten_glTexCoord2s;
- if (!strcmp(name, "glTexCoord3d")) return emscripten_glTexCoord3d;
- if (!strcmp(name, "glTexCoord3f")) return emscripten_glTexCoord3f;
- if (!strcmp(name, "glTexCoord3i")) return emscripten_glTexCoord3i;
- if (!strcmp(name, "glTexCoord3s")) return emscripten_glTexCoord3s;
- if (!strcmp(name, "glTexCoord4d")) return emscripten_glTexCoord4d;
- if (!strcmp(name, "glTexCoord4f")) return emscripten_glTexCoord4f;
- if (!strcmp(name, "glTexCoord4i")) return emscripten_glTexCoord4i;
- if (!strcmp(name, "glTexCoord4s")) return emscripten_glTexCoord4s;
- if (!strcmp(name, "glTexCoord1dv")) return emscripten_glTexCoord1dv;
- if (!strcmp(name, "glTexCoord1fv")) return emscripten_glTexCoord1fv;
- if (!strcmp(name, "glTexCoord1iv")) return emscripten_glTexCoord1iv;
- if (!strcmp(name, "glTexCoord1sv")) return emscripten_glTexCoord1sv;
- if (!strcmp(name, "glTexCoord2dv")) return emscripten_glTexCoord2dv;
- if (!strcmp(name, "glTexCoord2fv")) return emscripten_glTexCoord2fv;
- if (!strcmp(name, "glTexCoord2iv")) return emscripten_glTexCoord2iv;
- if (!strcmp(name, "glTexCoord2sv")) return emscripten_glTexCoord2sv;
- if (!strcmp(name, "glTexCoord3dv")) return emscripten_glTexCoord3dv;
- if (!strcmp(name, "glTexCoord3fv")) return emscripten_glTexCoord3fv;
- if (!strcmp(name, "glTexCoord3iv")) return emscripten_glTexCoord3iv;
- if (!strcmp(name, "glTexCoord3sv")) return emscripten_glTexCoord3sv;
- if (!strcmp(name, "glTexCoord4dv")) return emscripten_glTexCoord4dv;
- if (!strcmp(name, "glTexCoord4fv")) return emscripten_glTexCoord4fv;
- if (!strcmp(name, "glTexCoord4iv")) return emscripten_glTexCoord4iv;
- if (!strcmp(name, "glTexCoord4sv")) return emscripten_glTexCoord4sv;
- if (!strcmp(name, "glGetTexLevelParameterfv")) return emscripten_glGetTexLevelParameterfv;
- if (!strcmp(name, "glGetTexLevelParameteriv")) return emscripten_glGetTexLevelParameteriv;
- if (!strcmp(name, "glShadeModel")) return emscripten_glShadeModel;
- }
+#ifdef LEGACY_GL_EMULATION
+ if (!strcmp(name, "glDeleteObject")) return emscripten_glDeleteObjectARB;
+ if (!strcmp(name, "glGetObjectParameteriv")) return emscripten_glGetObjectParameterivARB;
+ if (!strcmp(name, "glGetInfoLog")) return emscripten_glGetInfoLogARB;
+ if (!strcmp(name, "glBindProgram")) return emscripten_glBindProgramARB;
+ if (!strcmp(name, "glGetPointerv")) return emscripten_glGetPointerv;
+ if (!strcmp(name, "glBegin")) return emscripten_glBegin;
+ if (!strcmp(name, "glEnd")) return emscripten_glEnd;
+ if (!strcmp(name, "glVertex2d")) return emscripten_glVertex2d;
+ if (!strcmp(name, "glVertex2f")) return emscripten_glVertex2f;
+ if (!strcmp(name, "glVertex2i")) return emscripten_glVertex2i;
+ if (!strcmp(name, "glVertex2s")) return emscripten_glVertex2s;
+ if (!strcmp(name, "glVertex3d")) return emscripten_glVertex3d;
+ if (!strcmp(name, "glVertex3f")) return emscripten_glVertex3f;
+ if (!strcmp(name, "glVertex3i")) return emscripten_glVertex3i;
+ if (!strcmp(name, "glVertex3s")) return emscripten_glVertex3s;
+ if (!strcmp(name, "glVertex4d")) return emscripten_glVertex4d;
+ if (!strcmp(name, "glVertex4f")) return emscripten_glVertex4f;
+ if (!strcmp(name, "glVertex4i")) return emscripten_glVertex4i;
+ if (!strcmp(name, "glVertex4s")) return emscripten_glVertex4s;
+ if (!strcmp(name, "glVertex2dv")) return emscripten_glVertex2dv;
+ if (!strcmp(name, "glVertex2fv")) return emscripten_glVertex2fv;
+ if (!strcmp(name, "glVertex2iv")) return emscripten_glVertex2iv;
+ if (!strcmp(name, "glVertex2sv")) return emscripten_glVertex2sv;
+ if (!strcmp(name, "glVertex3dv")) return emscripten_glVertex3dv;
+ if (!strcmp(name, "glVertex3fv")) return emscripten_glVertex3fv;
+ if (!strcmp(name, "glVertex3iv")) return emscripten_glVertex3iv;
+ if (!strcmp(name, "glVertex3sv")) return emscripten_glVertex3sv;
+ if (!strcmp(name, "glVertex4dv")) return emscripten_glVertex4dv;
+ if (!strcmp(name, "glVertex4fv")) return emscripten_glVertex4fv;
+ if (!strcmp(name, "glVertex4iv")) return emscripten_glVertex4iv;
+ if (!strcmp(name, "glVertex4sv")) return emscripten_glVertex4sv;
+ if (!strcmp(name, "glColor3b")) return emscripten_glColor3b;
+ if (!strcmp(name, "glColor3d")) return emscripten_glColor3d;
+ if (!strcmp(name, "glColor3f")) return emscripten_glColor3f;
+ if (!strcmp(name, "glColor3i")) return emscripten_glColor3i;
+ if (!strcmp(name, "glColor3s")) return emscripten_glColor3s;
+ if (!strcmp(name, "glColor3ub")) return emscripten_glColor3ub;
+ if (!strcmp(name, "glColor3ui")) return emscripten_glColor3ui;
+ if (!strcmp(name, "glColor3us")) return emscripten_glColor3us;
+ if (!strcmp(name, "glColor4b")) return emscripten_glColor4b;
+ if (!strcmp(name, "glColor4d")) return emscripten_glColor4d;
+ if (!strcmp(name, "glColor4f")) return emscripten_glColor4f;
+ if (!strcmp(name, "glColor4i")) return emscripten_glColor4i;
+ if (!strcmp(name, "glColor4s")) return emscripten_glColor4s;
+ if (!strcmp(name, "glColor4ub")) return emscripten_glColor4ub;
+ if (!strcmp(name, "glColor4ui")) return emscripten_glColor4ui;
+ if (!strcmp(name, "glColor4us")) return emscripten_glColor4us;
+ if (!strcmp(name, "glColor3bv")) return emscripten_glColor3bv;
+ if (!strcmp(name, "glColor3dv")) return emscripten_glColor3dv;
+ if (!strcmp(name, "glColor3fv")) return emscripten_glColor3fv;
+ if (!strcmp(name, "glColor3iv")) return emscripten_glColor3iv;
+ if (!strcmp(name, "glColor3sv")) return emscripten_glColor3sv;
+ if (!strcmp(name, "glColor3ubv")) return emscripten_glColor3ubv;
+ if (!strcmp(name, "glColor3uiv")) return emscripten_glColor3uiv;
+ if (!strcmp(name, "glColor3usv")) return emscripten_glColor3usv;
+ if (!strcmp(name, "glColor4bv")) return emscripten_glColor4bv;
+ if (!strcmp(name, "glColor4dv")) return emscripten_glColor4dv;
+ if (!strcmp(name, "glColor4fv")) return emscripten_glColor4fv;
+ if (!strcmp(name, "glColor4iv")) return emscripten_glColor4iv;
+ if (!strcmp(name, "glColor4sv")) return emscripten_glColor4sv;
+ if (!strcmp(name, "glColor4ubv")) return emscripten_glColor4ubv;
+ if (!strcmp(name, "glColor4uiv")) return emscripten_glColor4uiv;
+ if (!strcmp(name, "glColor4usv")) return emscripten_glColor4usv;
+ if (!strcmp(name, "glFogCoordf")) return emscripten_glFogCoordf;
+ if (!strcmp(name, "glFogCoordfv")) return emscripten_glFogCoordfv;
+ if (!strcmp(name, "glFogCoordd")) return emscripten_glFogCoordd;
+ if (!strcmp(name, "glFogCoorddv")) return emscripten_glFogCoorddv;
+ if (!strcmp(name, "glFogCoordPointer")) return emscripten_glFogCoordPointer;
+ if (!strcmp(name, "glPolygonMode")) return emscripten_glPolygonMode;
+ if (!strcmp(name, "glAlphaFunc")) return emscripten_glAlphaFunc;
+ if (!strcmp(name, "glNormal3b")) return emscripten_glNormal3b;
+ if (!strcmp(name, "glNormal3d")) return emscripten_glNormal3d;
+ if (!strcmp(name, "glNormal3f")) return emscripten_glNormal3f;
+ if (!strcmp(name, "glNormal3i")) return emscripten_glNormal3i;
+ if (!strcmp(name, "glNormal3s")) return emscripten_glNormal3s;
+ if (!strcmp(name, "glNormal3bv")) return emscripten_glNormal3bv;
+ if (!strcmp(name, "glNormal3dv")) return emscripten_glNormal3dv;
+ if (!strcmp(name, "glNormal3fv")) return emscripten_glNormal3fv;
+ if (!strcmp(name, "glNormal3iv")) return emscripten_glNormal3iv;
+ if (!strcmp(name, "glNormal3sv")) return emscripten_glNormal3sv;
+ if (!strcmp(name, "glDrawRangeElements")) return emscripten_glDrawRangeElements;
+ if (!strcmp(name, "glEnableClientState")) return emscripten_glEnableClientState;
+ if (!strcmp(name, "glDisableClientState")) return emscripten_glDisableClientState;
+ if (!strcmp(name, "glVertexPointer")) return emscripten_glVertexPointer;
+ if (!strcmp(name, "glNormalPointer")) return emscripten_glNormalPointer;
+ if (!strcmp(name, "glColorPointer")) return emscripten_glColorPointer;
+ if (!strcmp(name, "glTexCoordPointer")) return emscripten_glTexCoordPointer;
+ if (!strcmp(name, "glClientActiveTexture")) return emscripten_glClientActiveTexture;
+ if (!strcmp(name, "glMatrixMode")) return emscripten_glMatrixMode;
+ if (!strcmp(name, "glPushMatrix")) return emscripten_glPushMatrix;
+ if (!strcmp(name, "glPopMatrix")) return emscripten_glPopMatrix;
+ if (!strcmp(name, "glLoadIdentity")) return emscripten_glLoadIdentity;
+ if (!strcmp(name, "glLoadMatrixd")) return emscripten_glLoadMatrixd;
+ if (!strcmp(name, "glLoadMatrixf")) return emscripten_glLoadMatrixf;
+ if (!strcmp(name, "glLoadTransposeMatrixf")) return emscripten_glLoadTransposeMatrixfARB;
+ if (!strcmp(name, "glLoadTransposeMatrixd")) return emscripten_glLoadTransposeMatrixdARB;
+ if (!strcmp(name, "glMultTransposeMatrixf")) return emscripten_glMultTransposeMatrixfARB;
+ if (!strcmp(name, "glMultTransposeMatrixd")) return emscripten_glMultTransposeMatrixdARB;
+ if (!strcmp(name, "glMultMatrixd")) return emscripten_glMultMatrixd;
+ if (!strcmp(name, "glMultMatrixf")) return emscripten_glMultMatrixf;
+ if (!strcmp(name, "glOrtho")) return emscripten_glOrtho;
+ if (!strcmp(name, "glFrustum")) return emscripten_glFrustum;
+ if (!strcmp(name, "glRotated")) return emscripten_glRotated;
+ if (!strcmp(name, "glRotatef")) return emscripten_glRotatef;
+ if (!strcmp(name, "glScaled")) return emscripten_glScaled;
+ if (!strcmp(name, "glScalef")) return emscripten_glScalef;
+ if (!strcmp(name, "glTranslated")) return emscripten_glTranslated;
+ if (!strcmp(name, "glTranslatef")) return emscripten_glTranslatef;
+ if (!strcmp(name, "glDrawBuffer")) return emscripten_glDrawBuffer;
+ if (!strcmp(name, "glReadBuffer")) return emscripten_glReadBuffer;
+ if (!strcmp(name, "glLightf")) return emscripten_glLightf;
+ if (!strcmp(name, "glLighti")) return emscripten_glLighti;
+ if (!strcmp(name, "glLightfv")) return emscripten_glLightfv;
+ if (!strcmp(name, "glLightiv")) return emscripten_glLightiv;
+ if (!strcmp(name, "glLightModelf")) return emscripten_glLightModelf;
+ if (!strcmp(name, "glLightModeli")) return emscripten_glLightModeli;
+ if (!strcmp(name, "glLightModelfv")) return emscripten_glLightModelfv;
+ if (!strcmp(name, "glLightModeliv")) return emscripten_glLightModeliv;
+ if (!strcmp(name, "glMaterialf")) return emscripten_glMaterialf;
+ if (!strcmp(name, "glMateriali")) return emscripten_glMateriali;
+ if (!strcmp(name, "glMaterialfv")) return emscripten_glMaterialfv;
+ if (!strcmp(name, "glMaterialiv")) return emscripten_glMaterialiv;
+ if (!strcmp(name, "glTexGend")) return emscripten_glTexGend;
+ if (!strcmp(name, "glTexGenf")) return emscripten_glTexGenf;
+ if (!strcmp(name, "glTexGeni")) return emscripten_glTexGeni;
+ if (!strcmp(name, "glTexGendv")) return emscripten_glTexGendv;
+ if (!strcmp(name, "glTexGenfv")) return emscripten_glTexGenfv;
+ if (!strcmp(name, "glTexGeniv")) return emscripten_glTexGeniv;
+ if (!strcmp(name, "glGetTexEnvfv")) return emscripten_glGetTexEnvfv;
+ if (!strcmp(name, "glGetTexEnviv")) return emscripten_glGetTexEnviv;
+ if (!strcmp(name, "glTexImage1D")) return emscripten_glTexImage1D;
+ if (!strcmp(name, "glTexCoord1d")) return emscripten_glTexCoord1d;
+ if (!strcmp(name, "glTexCoord1f")) return emscripten_glTexCoord1f;
+ if (!strcmp(name, "glTexCoord1i")) return emscripten_glTexCoord1i;
+ if (!strcmp(name, "glTexCoord1s")) return emscripten_glTexCoord1s;
+ if (!strcmp(name, "glTexCoord2d")) return emscripten_glTexCoord2d;
+ if (!strcmp(name, "glTexCoord2f")) return emscripten_glTexCoord2f;
+ if (!strcmp(name, "glTexCoord2i")) return emscripten_glTexCoord2i;
+ if (!strcmp(name, "glTexCoord2s")) return emscripten_glTexCoord2s;
+ if (!strcmp(name, "glTexCoord3d")) return emscripten_glTexCoord3d;
+ if (!strcmp(name, "glTexCoord3f")) return emscripten_glTexCoord3f;
+ if (!strcmp(name, "glTexCoord3i")) return emscripten_glTexCoord3i;
+ if (!strcmp(name, "glTexCoord3s")) return emscripten_glTexCoord3s;
+ if (!strcmp(name, "glTexCoord4d")) return emscripten_glTexCoord4d;
+ if (!strcmp(name, "glTexCoord4f")) return emscripten_glTexCoord4f;
+ if (!strcmp(name, "glTexCoord4i")) return emscripten_glTexCoord4i;
+ if (!strcmp(name, "glTexCoord4s")) return emscripten_glTexCoord4s;
+ if (!strcmp(name, "glTexCoord1dv")) return emscripten_glTexCoord1dv;
+ if (!strcmp(name, "glTexCoord1fv")) return emscripten_glTexCoord1fv;
+ if (!strcmp(name, "glTexCoord1iv")) return emscripten_glTexCoord1iv;
+ if (!strcmp(name, "glTexCoord1sv")) return emscripten_glTexCoord1sv;
+ if (!strcmp(name, "glTexCoord2dv")) return emscripten_glTexCoord2dv;
+ if (!strcmp(name, "glTexCoord2fv")) return emscripten_glTexCoord2fv;
+ if (!strcmp(name, "glTexCoord2iv")) return emscripten_glTexCoord2iv;
+ if (!strcmp(name, "glTexCoord2sv")) return emscripten_glTexCoord2sv;
+ if (!strcmp(name, "glTexCoord3dv")) return emscripten_glTexCoord3dv;
+ if (!strcmp(name, "glTexCoord3fv")) return emscripten_glTexCoord3fv;
+ if (!strcmp(name, "glTexCoord3iv")) return emscripten_glTexCoord3iv;
+ if (!strcmp(name, "glTexCoord3sv")) return emscripten_glTexCoord3sv;
+ if (!strcmp(name, "glTexCoord4dv")) return emscripten_glTexCoord4dv;
+ if (!strcmp(name, "glTexCoord4fv")) return emscripten_glTexCoord4fv;
+ if (!strcmp(name, "glTexCoord4iv")) return emscripten_glTexCoord4iv;
+ if (!strcmp(name, "glTexCoord4sv")) return emscripten_glTexCoord4sv;
+ if (!strcmp(name, "glGetTexLevelParameterfv")) return emscripten_glGetTexLevelParameterfv;
+ if (!strcmp(name, "glGetTexLevelParameteriv")) return emscripten_glGetTexLevelParameteriv;
+ if (!strcmp(name, "glShadeModel")) return emscripten_glShadeModel;
+#endif
+
return 0;
}
diff --git a/tools/system_libs.py b/tools/system_libs.py
index 03a32b2..9f21abf 100755
--- a/tools/system_libs.py
+++ b/tools/system_libs.py
@@ -156,6 +156,14 @@
assert '-mt' not in libname
return []
+ def legacy_gl_emulation_flags(libname):
+ if shared.Settings.LEGACY_GL_EMULATION:
+ assert '-emu' in libname
+ return ['-DLEGACY_GL_EMULATION=1']
+ else:
+ assert '-emu' not in libname
+ return []
+
# libc
def create_libc(libname):
logging.debug(' building libc for cache')
@@ -361,6 +369,7 @@
files += map(lambda f: os.path.join(src_dir, f), filenames)
flags = ['-Oz']
flags += threading_flags(libname)
+ flags += legacy_gl_emulation_flags(libname)
return build_libc(libname, files, flags)
# al
@@ -602,10 +611,16 @@
if shared.Settings.USE_PTHREADS:
system_libs += [Library('libpthreads', ext, create_pthreads, pthreads_symbols, [libc_name], False), # noqa
- Library('libpthreads_asmjs', ext, create_pthreads_asmjs, asmjs_pthreads_symbols, [libc_name], False), # noqa
- Library('libgl-mt', ext, create_gl, gl_symbols, [libc_name], False)] # noqa
+ Library('libpthreads_asmjs', ext, create_pthreads_asmjs, asmjs_pthreads_symbols, [libc_name], False)] # noqa
+ if shared.Settings.LEGACY_GL_EMULATION:
+ system_libs += [Library('libgl-emu-mt', ext, create_gl, gl_symbols, [libc_name], False)] # noqa
+ else:
+ system_libs += [Library('libgl-mt', ext, create_gl, gl_symbols, [libc_name], False)] # noqa
else:
- system_libs += [Library('libgl', ext, create_gl, gl_symbols, [libc_name], False)] # noqa
+ if shared.Settings.LEGACY_GL_EMULATION:
+ system_libs += [Library('libgl-emu', ext, create_gl, gl_symbols, [libc_name], False)] # noqa
+ else:
+ system_libs += [Library('libgl', ext, create_gl, gl_symbols, [libc_name], False)] # noqa
system_libs.append(Library(libc_name, ext, create_libc, libc_symbols, libc_deps, False))