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