assert(LEGACY_GL_EMULATION, 'libglemu.js should only be included with LEGACY_GL_EMULATION set')
assert(!FULL_ES2, 'cannot emulate both ES2 and legacy GL');
assert(!FULL_ES3, 'cannot emulate both ES3 and legacy GL');
{{{
const copySigs = (func) => {
if (!RELOCATABLE) return '';
return ` _${func}.sig = _emscripten_${func}.sig = orig_${func}.sig;`;
};
const fromPtr = (arg) => {
if (CAN_ADDRESS_2GB) {
return `${arg} >>>= 0`;
} else if (MEMORY64) {
return `${arg} = Number(${arg})`;
}
return '';
};
}}}
var LibraryGLEmulation = {
$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', '$stringToNewUTF8',
'$ptrToString', '$getEmscriptenSupportedExtensions',
],
$GLEmulation__postset:
#if MAYBE_CLOSURE_COMPILER
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDrawArrays;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDrawElements;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glActiveTexture;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glEnable;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDisable;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvf;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvi;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvfv;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetIntegerv;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glIsEnabled;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetBooleanv;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetString;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glCreateShader;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glShaderSource;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glCompileShader;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glAttachShader;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDetachShader;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glUseProgram;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDeleteProgram;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glBindAttribLocation;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glLinkProgram;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glBindBuffer;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetFloatv;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glHint;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glEnableVertexAttribArray;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDisableVertexAttribArray;' +
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glVertexAttribPointer;' +
'/**@suppress {duplicate, undefinedVars}*/var _glTexEnvf;' +
'/**@suppress {duplicate, undefinedVars}*/var _glTexEnvi;' +
'/**@suppress {duplicate, undefinedVars}*/var _glTexEnvfv;' +
'/**@suppress {duplicate, undefinedVars}*/var _glGetTexEnviv;' +
'/**@suppress {duplicate, undefinedVars}*/var _glGetTexEnvfv;' +
#endif
'GLEmulation.init();',
$GLEmulation: {
fogStart: 0,
fogEnd: 1,
fogDensity: 1.0,
fogColor: null,
fogMode: 0x800,
fogEnabled: false,
MAX_CLIP_PLANES: 6,
clipPlaneEnabled: [false, false, false, false, false, false],
clipPlaneEquation: [],
lightingEnabled: false,
lightModelAmbient: null,
lightModelLocalViewer: false,
lightModelTwoSide: false,
materialAmbient: null,
materialDiffuse: null,
materialSpecular: null,
materialShininess: null,
materialEmission: null,
MAX_LIGHTS: 8,
lightEnabled: [false, false, false, false, false, false, false, false],
lightAmbient: [],
lightDiffuse: [],
lightSpecular: [],
lightPosition: [],
alphaTestEnabled: false,
alphaTestFunc: 0x207,
alphaTestRef: 0.0,
pointSize: 1.0,
vaos: [],
currentVao: null,
enabledVertexAttribArrays: {},
hasRunInit: false,
findToken(source, token) {
function isIdentChar(ch) {
if (ch >= 48 && ch <= 57)
return true;
if (ch >= 65 && ch <= 90)
return true;
if (ch >= 97 && ch <= 122)
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() {
#if FULL_ES2
return;
#endif
if (GLEmulation.hasRunInit) {
return;
}
GLEmulation.hasRunInit = true;
GLEmulation.fogColor = new Float32Array(4);
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
GLEmulation.clipPlaneEquation[clipPlaneId] = new Float32Array(4);
}
GLEmulation.lightModelAmbient = new Float32Array([0.2, 0.2, 0.2, 1.0]);
GLEmulation.materialAmbient = new Float32Array([0.2, 0.2, 0.2, 1.0]);
GLEmulation.materialDiffuse = new Float32Array([0.8, 0.8, 0.8, 1.0]);
GLEmulation.materialSpecular = new Float32Array([0.0, 0.0, 0.0, 1.0]);
GLEmulation.materialShininess = new Float32Array([0.0]);
GLEmulation.materialEmission = new Float32Array([0.0, 0.0, 0.0, 1.0]);
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
GLEmulation.lightAmbient[lightId] = new Float32Array([0.0, 0.0, 0.0, 1.0]);
GLEmulation.lightDiffuse[lightId] = lightId ? new Float32Array([0.0, 0.0, 0.0, 1.0]) : new Float32Array([1.0, 1.0, 1.0, 1.0]);
GLEmulation.lightSpecular[lightId] = lightId ? new Float32Array([0.0, 0.0, 0.0, 1.0]) : new Float32Array([1.0, 1.0, 1.0, 1.0]);
GLEmulation.lightPosition[lightId] = new Float32Array([0.0, 0.0, 1.0, 0.0]);
}
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 -sGL_UNSAFE_OPTS=0');
#endif
var validCapabilities = {
0xB44: 1,
0xBE2: 1,
0xBD0: 1,
0xB90: 1,
0xB71: 1,
0xC11: 1,
0x8037: 1,
0x809E: 1,
0x80A0: 1
};
var orig_glEnable = _glEnable;
_glEnable = _emscripten_glEnable = (cap) => {
GLImmediate.lastRenderer?.cleanup();
if (cap == 0xB60 ) {
if (GLEmulation.fogEnabled != true) {
GLImmediate.currentRenderer = null;
GLEmulation.fogEnabled = true;
}
return;
} else if ((cap >= 0x3000) && (cap < 0x3006) ) {
var clipPlaneId = cap - 0x3000;
if (GLEmulation.clipPlaneEnabled[clipPlaneId] != true) {
GLImmediate.currentRenderer = null;
GLEmulation.clipPlaneEnabled[clipPlaneId] = true;
}
return;
} else if ((cap >= 0x4000) && (cap < 0x4008) ) {
var lightId = cap - 0x4000;
if (GLEmulation.lightEnabled[lightId] != true) {
GLImmediate.currentRenderer = null;
GLEmulation.lightEnabled[lightId] = true;
}
return;
} else if (cap == 0xB50 ) {
if (GLEmulation.lightingEnabled != true) {
GLImmediate.currentRenderer = null;
GLEmulation.lightingEnabled = true;
}
return;
} else if (cap == 0xBC0 ) {
if (GLEmulation.alphaTestEnabled != true) {
GLImmediate.currentRenderer = null;
GLEmulation.alphaTestEnabled = true;
}
return;
} else if (cap == 0xDE1 ) {
return;
} else if (!(cap in validCapabilities)) {
return;
}
orig_glEnable(cap);
};
{{{ copySigs('glEnable') }}}
var orig_glDisable = _glDisable;
_glDisable = _emscripten_glDisable = (cap) => {
GLImmediate.lastRenderer?.cleanup();
if (cap == 0xB60 ) {
if (GLEmulation.fogEnabled != false) {
GLImmediate.currentRenderer = null;
GLEmulation.fogEnabled = false;
}
return;
} else if ((cap >= 0x3000) && (cap < 0x3006) ) {
var clipPlaneId = cap - 0x3000;
if (GLEmulation.clipPlaneEnabled[clipPlaneId] != false) {
GLImmediate.currentRenderer = null;
GLEmulation.clipPlaneEnabled[clipPlaneId] = false;
}
return;
} else if ((cap >= 0x4000) && (cap < 0x4008) ) {
var lightId = cap - 0x4000;
if (GLEmulation.lightEnabled[lightId] != false) {
GLImmediate.currentRenderer = null;
GLEmulation.lightEnabled[lightId] = false;
}
return;
} else if (cap == 0xB50 ) {
if (GLEmulation.lightingEnabled != false) {
GLImmediate.currentRenderer = null;
GLEmulation.lightingEnabled = false;
}
return;
} else if (cap == 0xBC0 ) {
if (GLEmulation.alphaTestEnabled != false) {
GLImmediate.currentRenderer = null;
GLEmulation.alphaTestEnabled = false;
}
return;
} else if (cap == 0xDE1 ) {
return;
} else if (!(cap in validCapabilities)) {
return;
}
orig_glDisable(cap);
};
{{{ copySigs('glDisable') }}}
var orig_glIsEnabled = _glIsEnabled;
_glIsEnabled = _emscripten_glIsEnabled = (cap) => {
if (cap == 0xB60 ) {
return GLEmulation.fogEnabled ? 1 : 0;
} else if ((cap >= 0x3000) && (cap < 0x3006) ) {
var clipPlaneId = cap - 0x3000;
return GLEmulation.clipPlaneEnabled[clipPlaneId] ? 1 : 0;
} else if ((cap >= 0x4000) && (cap < 0x4008) ) {
var lightId = cap - 0x4000;
return GLEmulation.lightEnabled[lightId] ? 1 : 0;
} else if (cap == 0xB50 ) {
return GLEmulation.lightingEnabled ? 1 : 0;
} else if (cap == 0xBC0 ) {
return GLEmulation.alphaTestEnabled ? 1 : 0;
} else if (!(cap in validCapabilities)) {
return 0;
}
return GLctx.isEnabled(cap);
};
{{{ copySigs('glIsEnabled') }}}
var orig_glGetBooleanv = _glGetBooleanv;
_glGetBooleanv = _emscripten_glGetBooleanv = (pname, p) => {
var attrib = GLEmulation.getAttributeFromCapability(pname);
if (attrib !== null) {
{{{ fromPtr('p') }}}
var result = GLImmediate.enabledClientAttributes[attrib];
{{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}};
return;
}
orig_glGetBooleanv(pname, p);
};
{{{ copySigs('glGetBooleanv') }}}
var orig_glGetIntegerv = _glGetIntegerv;
_glGetIntegerv = _emscripten_glGetIntegerv = (pname, params) => {
{{{ fromPtr('params') }}}
switch (pname) {
case 0x84E2: pname = GLctx.MAX_TEXTURE_IMAGE_UNITS ; break;
case 0x8B4A: {
var result = GLctx.getParameter(GLctx.MAX_VERTEX_UNIFORM_VECTORS);
{{{ makeSetValue('params', '0', 'result*4', 'i32') }}};
return;
}
case 0x8B49: {
var result = GLctx.getParameter(GLctx.MAX_FRAGMENT_UNIFORM_VECTORS);
{{{ makeSetValue('params', '0', 'result*4', 'i32') }}};
return;
}
case 0x8B4B: {
var result = GLctx.getParameter(GLctx.MAX_VARYING_VECTORS);
{{{ makeSetValue('params', '0', 'result*4', 'i32') }}};
return;
}
case 0x8871: pname = GLctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS ; break;
case 0x807A: {
var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
{{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
return;
}
case 0x807B: {
var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
{{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
return;
}
case 0x807C: {
var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
{{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
return;
}
case 0x8081: {
var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
{{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
return;
}
case 0x8082: {
var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
{{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
return;
}
case 0x8083: {
var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
{{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
return;
}
case 0x8088: {
var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
{{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
return;
}
case 0x8089: {
var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
{{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
return;
}
case 0x808A: {
var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
{{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
return;
}
case 0x0D32: {
{{{ makeSetValue('params', '0', 'GLEmulation.MAX_CLIP_PLANES', 'i32') }}};
return;
}
case 0x0BA0: {
{{{ makeSetValue('params', '0', 'GLImmediate.currentMatrix + 0x1700', 'i32') }}};
return;
}
case 0x0BC1: {
{{{ makeSetValue('params', '0', 'GLEmulation.alphaTestFunc', 'i32') }}};
return;
}
}
orig_glGetIntegerv(pname, params);
};
{{{ copySigs('glGetIntegerv') }}}
var orig_glGetString = _glGetString;
_glGetString = _emscripten_glGetString = (name_) => {
if (GL.stringCache[name_]) return GL.stringCache[name_];
switch (name_) {
case 0x1F03 :
var ret = stringToNewUTF8(getEmscriptenSupportedExtensions(GLctx).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' : '')
);
return GL.stringCache[name_] = {{{ to64('ret') }}};
}
return orig_glGetString(name_);
};
{{{ copySigs('glGetString') }}}
GL.shaderInfos = {};
#if GL_DEBUG
GL.shaderSources = {};
GL.shaderOriginalSources = {};
#endif
var orig_glCreateShader = _glCreateShader;
_glCreateShader = _emscripten_glCreateShader = (shaderType) => {
var id = orig_glCreateShader(shaderType);
GL.shaderInfos[id] = {
type: shaderType,
ftransform: false
};
return id;
};
{{{ copySigs('glCreateShader') }}}
function ensurePrecision(source) {
if (!/precision +(low|medium|high)p +float *;/.test(source)) {
source = '#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n' + source;
}
return source;
}
var orig_glShaderSource = _glShaderSource;
_glShaderSource = _emscripten_glShaderSource = (shader, count, string, length) => {
{{{ fromPtr('string') }}}
{{{ fromPtr('length') }}}
var source = GL.getSource(shader, count, string, length);
#if GL_DEBUG
dbg("glShaderSource: Input: \n" + source);
GL.shaderOriginalSources[shader] = source;
#endif
if (GL.shaderInfos[shader].type == GLctx.VERTEX_SHADER) {
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])');
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;
for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
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.includes('gl_FrontColor')) {
source = 'varying vec4 v_color; \n' +
source.replace(/gl_FrontColor/g, 'v_color');
}
if (source.includes('gl_Color')) {
source = 'attribute vec4 a_color; \n' +
source.replace(/gl_Color/g, 'a_color');
}
if (source.includes('gl_Normal')) {
source = 'attribute vec3 a_normal; \n' +
source.replace(/gl_Normal/g, 'a_normal');
}
if (source.includes('gl_FogFragCoord')) {
source = 'varying float v_fogFragCoord; \n' +
source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord');
}
} else {
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.includes('gl_Color')) {
source = 'varying vec4 v_color; \n' + source.replace(/gl_Color/g, 'v_color');
}
if (source.includes('gl_Fog.color')) {
source = 'uniform vec4 u_fogColor; \n' +
source.replace(/gl_Fog.color/g, 'u_fogColor');
}
if (source.includes('gl_Fog.end')) {
source = 'uniform float u_fogEnd; \n' +
source.replace(/gl_Fog.end/g, 'u_fogEnd');
}
if (source.includes('gl_Fog.scale')) {
source = 'uniform float u_fogScale; \n' +
source.replace(/gl_Fog.scale/g, 'u_fogScale');
}
if (source.includes('gl_Fog.density')) {
source = 'uniform float u_fogDensity; \n' +
source.replace(/gl_Fog.density/g, 'u_fogDensity');
}
if (source.includes('gl_FogFragCoord')) {
source = 'varying float v_fogFragCoord; \n' +
source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord');
}
source = ensurePrecision(source);
}
#if GL_DEBUG
GL.shaderSources[shader] = source;
dbg("glShaderSource: Output: \n" + source);
#endif
GLctx.shaderSource(GL.shaders[shader], source);
};
{{{ copySigs('glShaderSource') }}}
var orig_glCompileShader = _glCompileShader;
_glCompileShader = _emscripten_glCompileShader = (shader) => {
GLctx.compileShader(GL.shaders[shader]);
#if GL_DEBUG
if (!GLctx.getShaderParameter(GL.shaders[shader], GLctx.COMPILE_STATUS)) {
dbg(`Failed to compile shader: ${GLctx.getShaderInfoLog(GL.shaders[shader])}`);
dbg(`Info: ${JSON.stringify(GL.shaderInfos[shader])}`);
dbg(`Original source: ${GL.shaderOriginalSources[shader]}`);
dbg(`Source: ${GL.shaderSources[shader]}`);
throw 'Shader compilation halt';
}
#endif
};
{{{ copySigs('glCompileShader') }}}
GL.programShaders = {};
var orig_glAttachShader = _glAttachShader;
_glAttachShader = _emscripten_glAttachShader = (program, shader) => {
GL.programShaders[program] ||= [];
GL.programShaders[program].push(shader);
orig_glAttachShader(program, shader);
};
{{{ copySigs('glAttachShader') }}}
var orig_glDetachShader = _glDetachShader;
_glDetachShader = _emscripten_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);
orig_glDetachShader(program, shader);
};
{{{ copySigs('glDetachShader') }}}
var orig_glUseProgram = _glUseProgram;
_glUseProgram = _emscripten_glUseProgram = (program) => {
#if GL_DEBUG
if (GL.debug) {
dbg('[using program with shaders]');
if (program) {
GL.programShaders[program].forEach((shader) => {
dbg(` shader ${shader}, original source: ${GL.shaderOriginalSources[shader]}`);
dbg(` Source: ${GL.shaderSources[shader]}`);
});
}
}
#endif
if (GL.currProgram != program) {
GLImmediate.currentRenderer = null;
GL.currProgram = program;
GLImmediate.fixedFunctionProgram = 0;
orig_glUseProgram(program);
}
}
{{{ copySigs('glUseProgram') }}}
var orig_glDeleteProgram = _glDeleteProgram;
_glDeleteProgram = _emscripten_glDeleteProgram = (program) => {
orig_glDeleteProgram(program);
if (program == GL.currProgram) {
GLImmediate.currentRenderer = null;
GL.currProgram = 0;
}
};
{{{ copySigs('glDeleteProgram') }}}
var zeroUsedPrograms = {};
var orig_glBindAttribLocation = _glBindAttribLocation;
_glBindAttribLocation = _emscripten_glBindAttribLocation = (program, index, name) => {
if (index == 0) zeroUsedPrograms[program] = true;
orig_glBindAttribLocation(program, index, name);
};
{{{ copySigs('glBindAttribLocation') }}}
var orig_glLinkProgram = _glLinkProgram;
_glLinkProgram = _emscripten_glLinkProgram = (program) => {
if (!(program in zeroUsedPrograms)) {
GLctx.bindAttribLocation(GL.programs[program], 0, 'a_position');
}
orig_glLinkProgram(program);
};
{{{ copySigs('glLinkProgram') }}}
var orig_glBindBuffer = _glBindBuffer;
_glBindBuffer = _emscripten_glBindBuffer = (target, buffer) => {
orig_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;
}
};
{{{ copySigs('glBindBuffer') }}}
var orig_glGetFloatv = _glGetFloatv;
_glGetFloatv = _emscripten_glGetFloatv = (pname, params) => {
{{{ fromPtr('params') }}}
if (pname == 0xBA6) {
HEAPF32.set(GLImmediate.matrix[0], {{{ getHeapOffset('params', 'float') }}});
} else if (pname == 0xBA7) {
HEAPF32.set(GLImmediate.matrix[1], {{{ getHeapOffset('params', 'float') }}});
} else if (pname == 0xBA8) {
HEAPF32.set(GLImmediate.matrix[2 + GLImmediate.clientActiveTexture], {{{ getHeapOffset('params', 'float') }}});
} else if (pname == 0xB66) {
HEAPF32.set(GLEmulation.fogColor, {{{ getHeapOffset('params', 'float') }}});
} else if (pname == 0xB63) {
{{{ makeSetValue('params', '0', 'GLEmulation.fogStart', 'float') }}};
} else if (pname == 0xB64) {
{{{ makeSetValue('params', '0', 'GLEmulation.fogEnd', 'float') }}};
} else if (pname == 0xB62) {
{{{ makeSetValue('params', '0', 'GLEmulation.fogDensity', 'float') }}};
} else if (pname == 0xB65) {
{{{ makeSetValue('params', '0', 'GLEmulation.fogMode', 'float') }}};
} else if (pname == 0xB53) {
{{{ makeSetValue('params', '0', 'GLEmulation.lightModelAmbient[0]', 'float') }}};
{{{ makeSetValue('params', '4', 'GLEmulation.lightModelAmbient[1]', 'float') }}};
{{{ makeSetValue('params', '8', 'GLEmulation.lightModelAmbient[2]', 'float') }}};
{{{ makeSetValue('params', '12', 'GLEmulation.lightModelAmbient[3]', 'float') }}};
} else if (pname == 0xBC2) {
{{{ makeSetValue('params', '0', 'GLEmulation.alphaTestRef', 'float') }}};
} else {
orig_glGetFloatv(pname, params);
}
};
{{{ copySigs('glGetFloatv') }}}
var orig_glHint = _glHint;
_glHint = _emscripten_glHint = (target, mode) => {
if (target == 0x84EF) {
return;
}
orig_glHint(target, mode);
};
{{{ copySigs('glHint') }}}
var orig_glEnableVertexAttribArray = _glEnableVertexAttribArray;
_glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray = (index) => {
orig_glEnableVertexAttribArray(index);
GLEmulation.enabledVertexAttribArrays[index] = 1;
if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
};
{{{ copySigs('glEnableVertexAttribArray') }}}
var orig_glDisableVertexAttribArray = _glDisableVertexAttribArray;
_glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray = (index) => {
orig_glDisableVertexAttribArray(index);
delete GLEmulation.enabledVertexAttribArrays[index];
if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
};
{{{ copySigs('glDisableVertexAttribArray') }}}
var orig_glVertexAttribPointer = _glVertexAttribPointer;
_glVertexAttribPointer = _emscripten_glVertexAttribPointer = (index, size, type, normalized, stride, pointer) => {
orig_glVertexAttribPointer(index, size, type, normalized, stride, pointer);
if (GLEmulation.currentVao) {
GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
}
};
{{{ copySigs('glVertexAttribPointer') }}}
},
getAttributeFromCapability(cap) {
var attrib = null;
switch (cap) {
case 0xDE1:
#if ASSERTIONS
abort("GL_TEXTURE_2D is not a spec-defined capability for gl{Enable,Disable}ClientState.");
#endif
case 0x8078:
attrib = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture; break;
case 0x8074:
attrib = GLImmediate.VERTEX; break;
case 0x8075:
attrib = GLImmediate.NORMAL; break;
case 0x8076:
attrib = GLImmediate.COLOR; break;
}
return attrib;
},
},
glDeleteObject__deps: ['glDeleteProgram', 'glDeleteShader'],
glDeleteObject: (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__deps: ['glGetProgramiv', 'glGetShaderiv'],
glGetObjectParameteriv: (id, type, result) => {
if (GL.programs[id]) {
if (type == 0x8B84) {
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) {
var log = GLctx.getShaderInfoLog(GL.shaders[id]);
if (log === null) log = '(unknown error)';
{{{ makeSetValue('result', '0', 'log.length', 'i32') }}};
return;
} else if (type == 0x8B88) {
var source = GLctx.getShaderSource(GL.shaders[id]);
if (source === null) return;
{{{ 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: (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: (type, id) => {
#if ASSERTIONS
assert(id == 0);
#endif
},
glBindProgramARB: 'glBindProgram',
glGetPointerv: (name, p) => {
var attribute;
switch (name) {
case 0x808E:
attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break;
case 0x8090:
attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
case 0x8092:
attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break;
default:
GL.recordError(0x500);
#if GL_ASSERTIONS
err(`GL_INVALID_ENUM in glGetPointerv: Unsupported name ${name}!`);
#endif
return;
}
{{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
},
#if !FULL_ES2
$GLImmediate__postset: 'GLImmediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(() => GLImmediate.init());',
#endif
$GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'],
$GLImmediate: {
MapTreeLib: null,
spawnMapTreeLib: () => {
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;
};
};
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;
};
};
return {
create: () => new CMapTree(),
};
},
TexEnvJIT: null,
spawnTexEnvJIT: () => {
var GL_TEXTURE0 = 0x84C0;
var GL_TEXTURE_1D = 0xDE0;
var GL_TEXTURE_2D = 0xDE1;
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 = 0xD1C;
var GL_ADD = 0x104;
var GL_BLEND = 0xBE2;
var GL_REPLACE = 0x1E01;
var GL_MODULATE = 0x2100;
var GL_DECAL = 0x2101;
var GL_COMBINE = 0x8570;
var GL_SUBTRACT = 0x84E7;
var GL_INTERPOLATE = 0x8575;
var GL_TEXTURE = 0x1702;
var GL_CONSTANT = 0x8576;
var GL_PRIMARY_COLOR = 0x8577;
var GL_PREVIOUS = 0x8578;
var GL_SRC_COLOR = 0x300;
var GL_ONE_MINUS_SRC_COLOR = 0x301;
var GL_SRC_ALPHA = 0x302;
var GL_ONE_MINUS_SRC_ALPHA = 0x303;
var GL_RGB = 0x1907;
var GL_RGBA = 0x1908;
var TEXENVJIT_NAMESPACE_PREFIX = "tej_";
var TEX_UNIT_UNIFORM_PREFIX = "uTexUnit";
var TEX_COORD_VARYING_PREFIX = "vTexCoord";
var PRIM_COLOR_VARYING = "vPrimColor";
var TEX_MATRIX_UNIFORM_PREFIX = "uTexMatrix";
var s_texUnits = null;
var s_activeTexture = 0;
var s_requiredTexUnitsForPass = [];
function abort_noSupport(info) {
abort("[TexEnvJIT] ABORT: No support: " + info);
}
function abort_sanity(info) {
abort("[TexEnvJIT] 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: ${ptrToString(texType)}`);
}
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: " + ptrToString(op));
}
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: " + ptrToString(src));
}
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: " + ptrToString(op));
}
return expr;
}
function valToFloatLiteral(val) {
if (val == Math.round(val)) return val + '.0';
return val;
}
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
];
this.traverseKey = {
0x1E01 : 0,
0x2100 : 1,
0x104 : 2,
0xBE2 : 3,
0x2101 : 4,
0x8570 : 5,
0x84E7 : 3,
0x8575 : 4,
0x1702 : 0,
0x8576 : 1,
0x8577 : 2,
0x8578 : 3,
0x300 : 0,
0x301 : 1,
0x302 : 2,
0x303 : 3
};
this.key0 = -1;
this.key1 = 0;
this.key2 = 0;
this.computeKey0 = function() {
var k = this.traverseKey;
var key = k[this.mode] * 1638400;
key += k[this.colorCombiner] * 327680;
key += k[this.alphaCombiner] * 65536;
key += (this.colorScale-1) * 16384;
key += (this.alphaScale-1) * 4096;
key += k[this.colorSrc[0]] * 1024;
key += k[this.colorSrc[1]] * 256;
key += k[this.colorSrc[2]] * 64;
key += k[this.alphaSrc[0]] * 16;
key += k[this.alphaSrc[1]] * 4;
key += k[this.alphaSrc[2]];
return key;
}
this.computeKey1 = function() {
var k = this.traverseKey;
var 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;
}
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;
GLImmediate.currentRenderer = null;
}
}
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;
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 {
keyView.next(0);
}
};
};
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;
while (load = texLoadRegex.exec(lines)) {
var texLoadExpr = load[1];
var secondOccurrence = lines.slice(load.index+1).indexOf(texLoadExpr);
if (secondOccurrence != -1) {
var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
var texLoadVar = prefix + 'texload' + loadCounter++;
var texLoadLine = 'vec4 ' + texLoadVar + ' = ' + texLoadExpr + ';\n';
texLoadLines += texLoadLine + '\n';
lines = lines.split(texLoadExpr).join(texLoadVar);
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: {
return [
"vec4 " + passOutputVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
];
}
case GL_ADD: {
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: {
var line = [
"vec4 " + passOutputVar,
" = ",
passInputVar,
" * ",
genTexUnitSampleExpr(texUnitID),
";",
];
return [line.join("")];
}
case GL_DECAL: {
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: {
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);
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: " + ptrToString(this.mode));
}
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: " + ptrToString(combiner));
}
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: {
lines = [`${outputType} ${outputVar} = ${src0Expr};`]
break;
}
case GL_MODULATE: {
lines = [`${outputType} ${outputVar} = ${src0Expr} * ${src1Expr};`];
break;
}
case GL_ADD: {
lines = [`${outputType} ${outputVar} = ${src0Expr} + ${src1Expr};`]
break;
}
case GL_SUBTRACT: {
lines = [`${outputType} ${outputVar} = ${src0Expr} - ${src1Expr};`]
break;
}
case GL_INTERPOLATE: {
var prefix = `${TEXENVJIT_NAMESPACE_PREFIX}env${texUnitID}_`;
var arg2Var = `${prefix}colorSrc2`;
var arg2Type = getTypeFromCombineOp(this.colorOp[2]);
lines = [
`${arg2Type} ${arg2Var} = ${src2Expr};`,
`${outputType} ${outputVar} = ${src0Expr} * ${arg2Var} + ${src1Expr} * (1.0 - ${arg2Var});`,
];
break;
}
default:
return abort_sanity("Unmatched TexEnv.colorCombiner?");
}
return lines;
}
return {
init: (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: (uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix) => {
TEX_UNIT_UNIFORM_PREFIX = uTexUnitPrefix;
TEX_COORD_VARYING_PREFIX = vTexCoordPrefix;
PRIM_COLOR_VARYING = vPrimColor;
TEX_MATRIX_UNIFORM_PREFIX = uTexMatrixPrefix;
},
genAllPassLines: (resultDest, indentSize = 0) => {
s_requiredTexUnitsForPass.length = 0;
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: () => s_requiredTexUnitsForPass,
getActiveTexture: () => s_activeTexture,
traverseState: (keyView) => {
for (var texUnit of s_texUnits) {
texUnit.traverseState(keyView);
}
},
getTexUnitType: (texUnitID) => {
#if ASSERTIONS
assert(texUnitID >= 0 &&
texUnitID < s_texUnits.length);
#endif
return s_texUnits[texUnitID].getTexType();
},
hook_activeTexture: (texture) => {
s_activeTexture = texture - GL_TEXTURE0;
if (GLImmediate.currentMatrix >= 2) {
GLImmediate.currentMatrix = 2 + s_activeTexture;
}
},
hook_enable: (cap) => {
var cur = getCurTexUnit();
switch (cap) {
case GL_TEXTURE_1D:
if (!cur.enabled_tex1D) {
GLImmediate.currentRenderer = null;
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: (cap) => {
var cur = getCurTexUnit();
switch (cap) {
case GL_TEXTURE_1D:
if (cur.enabled_tex1D) {
GLImmediate.currentRenderer = null;
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(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();
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(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();
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(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();
env.envColor[i] = param;
}
}
break
}
default:
err('WARNING: Unhandled `pname` in call to `glTexEnvfv`.');
}
},
hook_getTexEnviv(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: (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;
}
}
};
},
vertexData: null,
vertexDataU8: null,
tempData: null,
indexData: null,
vertexCounter: 0,
mode: -1,
rendererCache: null,
rendererComponents: [],
rendererComponentPointer: 0,
lastRenderer: null,
lastArrayBuffer: null,
lastProgram: null,
lastStride: -1,
matrix: [],
matrixStack: [],
currentMatrix: 0,
tempMatrix: null,
matricesModified: false,
useTextureMatrix: false,
VERTEX: 0,
NORMAL: 1,
COLOR: 2,
TEXTURE0: 3,
NUM_ATTRIBUTES: -1,
MAX_TEXTURES: -1,
totalEnabledClientAttributes: 0,
enabledClientAttributes: [0, 0],
clientAttributes: [],
liveClientAttributes: [],
currentRenderer: null,
modifiedClientAttributes: false,
clientActiveTexture: 0,
clientColor: null,
usedTexUnitList: [],
fixedFunctionProgram: null,
setClientAttribute(name, size, type, stride, pointer) {
var attrib = GLImmediate.clientAttributes[name];
if (!attrib) {
for (var i = 0; i <= name; i++) {
GLImmediate.clientAttributes[i] ||= {
name,
size,
type,
stride,
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;
},
addRendererComponent(name, size, type) {
if (!GLImmediate.rendererComponents[name]) {
GLImmediate.rendererComponents[name] = 1;
#if ASSERTIONS
if (GLImmediate.enabledClientAttributes[name]) {
warnOnce("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
GL.enableVertexAttribArray(name);
#endif
} else {
GLImmediate.rendererComponents[name]++;
}
},
disableBeginEndClientAttributes() {
for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
if (GLImmediate.rendererComponents[i]) GLImmediate.enabledClientAttributes[i] = false;
}
},
getRenderer() {
if (GLImmediate.currentRenderer) {
return GLImmediate.currentRenderer;
}
var attributes = GLImmediate.liveClientAttributes;
var cacheMap = GLImmediate.rendererCache;
var keyView = cacheMap.getStaticKeyView().reset();
var enabledAttributesKey = 0;
for (var attr of attributes) {
enabledAttributesKey |= 1 << attr.name;
}
keyView.next(enabledAttributesKey);
enabledAttributesKey = 0;
var fogParam = 0;
if (GLEmulation.fogEnabled) {
switch (GLEmulation.fogMode) {
case 0x801:
fogParam = 1;
break;
case 0x2601:
fogParam = 2;
break;
default:
fogParam = 3;
break;
}
}
enabledAttributesKey = (enabledAttributesKey << 2) | fogParam;
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
enabledAttributesKey = (enabledAttributesKey << 1) | GLEmulation.clipPlaneEnabled[clipPlaneId];
}
enabledAttributesKey = (enabledAttributesKey << 1) | GLEmulation.lightingEnabled;
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
enabledAttributesKey = (enabledAttributesKey << 1) | (GLEmulation.lightingEnabled ? GLEmulation.lightEnabled[lightId] : 0);
}
enabledAttributesKey = (enabledAttributesKey << 3) | (GLEmulation.alphaTestEnabled ? (GLEmulation.alphaTestFunc - 0x200) : 0x7);
enabledAttributesKey = (enabledAttributesKey << 1) | (GLImmediate.mode == GLctx.POINTS ? 1 : 0);
keyView.next(enabledAttributesKey);
#if !GL_FFP_ONLY
keyView.next(GL.currProgram);
if (!GL.currProgram) {
#endif
GLImmediate.TexEnvJIT.traverseState(keyView);
#if !GL_FFP_ONLY
}
#endif
var renderer = keyView.get();
if (!renderer) {
#if GL_DEBUG
dbg(`generating renderer for ${JSON.stringify(attributes)}`);
#endif
renderer = GLImmediate.createRenderer();
GLImmediate.currentRenderer = renderer;
keyView.set(renderer);
return renderer;
}
GLImmediate.currentRenderer = renderer;
return renderer;
},
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;
}
function Renderer() {
this.init = function() {
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 {
if (GLEmulation.fogEnabled) {
switch (GLEmulation.fogMode) {
case 0x801:
var fogFormula = ' float fog = exp(-u_fogDensity * u_fogDensity * ecDistance * ecDistance); \n';
break;
case 0x2601:
var fogFormula = ' float fog = (u_fogEnd - ecDistance) * u_fogScale; \n';
break;
default:
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 texUnit of this.usedTexUnitList) {
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 vsPointSizeDefs = null;
var vsPointSizeInit = null;
if (GLImmediate.mode == GLctx.POINTS) {
vsPointSizeDefs = 'uniform float u_pointSize;\n';
vsPointSizeInit = ' gl_PointSize = u_pointSize;\n';
}
var vsClipPlaneDefs = '';
var vsClipPlaneInit = '';
var fsClipPlaneDefs = '';
var fsClipPlanePass = '';
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
if (GLEmulation.clipPlaneEnabled[clipPlaneId]) {
vsClipPlaneDefs += 'uniform vec4 u_clipPlaneEquation' + clipPlaneId + ';';
vsClipPlaneDefs += 'varying float v_clipDistance' + clipPlaneId + ';';
vsClipPlaneInit += ' v_clipDistance' + clipPlaneId + ' = dot(ecPosition, u_clipPlaneEquation' + clipPlaneId + ');';
fsClipPlaneDefs += 'varying float v_clipDistance' + clipPlaneId + ';';
fsClipPlanePass += ' if (v_clipDistance' + clipPlaneId + ' < 0.0) discard;';
}
}
var vsLightingDefs = '';
var vsLightingPass = '';
if (GLEmulation.lightingEnabled) {
vsLightingDefs += 'attribute vec3 a_normal;';
vsLightingDefs += 'uniform mat3 u_normalMatrix;';
vsLightingDefs += 'uniform vec4 u_lightModelAmbient;';
vsLightingDefs += 'uniform vec4 u_materialAmbient;';
vsLightingDefs += 'uniform vec4 u_materialDiffuse;';
vsLightingDefs += 'uniform vec4 u_materialSpecular;';
vsLightingDefs += 'uniform float u_materialShininess;';
vsLightingDefs += 'uniform vec4 u_materialEmission;';
vsLightingPass += ' vec3 ecNormal = normalize(u_normalMatrix * a_normal);';
vsLightingPass += ' v_color.w = u_materialDiffuse.w;';
vsLightingPass += ' v_color.xyz = u_materialEmission.xyz;';
vsLightingPass += ' v_color.xyz += u_lightModelAmbient.xyz * u_materialAmbient.xyz;';
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
if (GLEmulation.lightEnabled[lightId]) {
vsLightingDefs += 'uniform vec4 u_lightAmbient' + lightId + ';';
vsLightingDefs += 'uniform vec4 u_lightDiffuse' + lightId + ';';
vsLightingDefs += 'uniform vec4 u_lightSpecular' + lightId + ';';
vsLightingDefs += 'uniform vec4 u_lightPosition' + lightId + ';';
vsLightingPass += ' {';
vsLightingPass += ' vec3 lightDirection = normalize(u_lightPosition' + lightId + ').xyz;';
vsLightingPass += ' vec3 halfVector = normalize(lightDirection + vec3(0,0,1));';
vsLightingPass += ' vec3 ambient = u_lightAmbient' + lightId + '.xyz * u_materialAmbient.xyz;';
vsLightingPass += ' float diffuseI = max(dot(ecNormal, lightDirection), 0.0);';
vsLightingPass += ' float specularI = max(dot(ecNormal, halfVector), 0.0);';
vsLightingPass += ' vec3 diffuse = diffuseI * u_lightDiffuse' + lightId + '.xyz * u_materialDiffuse.xyz;';
vsLightingPass += ' specularI = (diffuseI > 0.0 && specularI > 0.0) ? exp(u_materialShininess * log(specularI)) : 0.0;';
vsLightingPass += ' vec3 specular = specularI * u_lightSpecular' + lightId + '.xyz * u_materialSpecular.xyz;';
vsLightingPass += ' v_color.xyz += ambient + diffuse + specular;';
vsLightingPass += ' }';
}
}
vsLightingPass += ' v_color = clamp(v_color, 0.0, 1.0);';
}
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;',
vsPointSizeDefs,
vsClipPlaneDefs,
vsLightingDefs,
'void main()',
'{',
' vec4 ecPosition = u_modelView * a_position;',
' gl_Position = u_projection * ecPosition;',
' v_color = a_color;',
vsTexCoordInits,
vsFogVaryingInit,
vsPointSizeInit,
vsClipPlaneInit,
vsLightingPass,
'}',
''
].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 fsAlphaTestDefs = '';
var fsAlphaTestPass = '';
if (GLEmulation.alphaTestEnabled) {
fsAlphaTestDefs = 'uniform float u_alphaTestRef;';
switch (GLEmulation.alphaTestFunc) {
case 0x200:
fsAlphaTestPass = 'discard;';
break;
case 0x201:
fsAlphaTestPass = 'if (!(gl_FragColor.a < u_alphaTestRef)) { discard; }';
break;
case 0x202:
fsAlphaTestPass = 'if (!(gl_FragColor.a == u_alphaTestRef)) { discard; }';
break;
case 0x203:
fsAlphaTestPass = 'if (!(gl_FragColor.a <= u_alphaTestRef)) { discard; }';
break;
case 0x204:
fsAlphaTestPass = 'if (!(gl_FragColor.a > u_alphaTestRef)) { discard; }';
break;
case 0x205:
fsAlphaTestPass = 'if (!(gl_FragColor.a != u_alphaTestRef)) { discard; }';
break;
case 0x206:
fsAlphaTestPass = 'if (!(gl_FragColor.a >= u_alphaTestRef)) { discard; }';
break;
case 0x207:
fsAlphaTestPass = '';
break;
}
}
var fsSource = [
'precision mediump float;',
texUnitVaryingList,
texUnitUniformList,
'varying vec4 v_color;',
fogHeaderIfNeeded,
fsClipPlaneDefs,
fsAlphaTestDefs,
'void main()',
'{',
fsClipPlanePass,
fsTexEnvPass,
fogPass,
fsAlphaTestPass,
'}',
''
].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);
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);
}
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) {
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);
}
}
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.normalMatrixLocation = GLctx.getUniformLocation(this.program, 'u_normalMatrix');
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;
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);
this.pointSizeLocation = GLctx.getUniformLocation(this.program, 'u_pointSize');
this.hasClipPlane = false;
this.clipPlaneEquationLocation = [];
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
this.clipPlaneEquationLocation[clipPlaneId] = GLctx.getUniformLocation(this.program, `u_clipPlaneEquation${clipPlaneId}`);
this.hasClipPlane = (this.hasClipPlane || this.clipPlaneEquationLocation[clipPlaneId]);
}
this.hasLighting = GLEmulation.lightingEnabled;
this.lightModelAmbientLocation = GLctx.getUniformLocation(this.program, 'u_lightModelAmbient');
this.materialAmbientLocation = GLctx.getUniformLocation(this.program, 'u_materialAmbient');
this.materialDiffuseLocation = GLctx.getUniformLocation(this.program, 'u_materialDiffuse');
this.materialSpecularLocation = GLctx.getUniformLocation(this.program, 'u_materialSpecular');
this.materialShininessLocation = GLctx.getUniformLocation(this.program, 'u_materialShininess');
this.materialEmissionLocation = GLctx.getUniformLocation(this.program, 'u_materialEmission');
this.lightAmbientLocation = []
this.lightDiffuseLocation = []
this.lightSpecularLocation = []
this.lightPositionLocation = []
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
this.lightAmbientLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightAmbient${lightId}`);
this.lightDiffuseLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightDiffuse${lightId}`);
this.lightSpecularLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightSpecular${lightId}`);
this.lightPositionLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightPosition${lightId}`);
}
this.hasAlphaTest = GLEmulation.alphaTestEnabled;
this.alphaTestRefLocation = GLctx.getUniformLocation(this.program, 'u_alphaTestRef');
};
this.prepare = function() {
var arrayBuffer;
if (!GLctx.currentArrayBufferBinding) {
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);
} else {
arrayBuffer = GLctx.currentArrayBufferBinding;
}
#if GL_UNSAFE_OPTS
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 (!GLctx.currentArrayBufferBinding) {
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]) {
this.modelViewMatrixVersion = GLImmediate.matrixVersion[0];
GLctx.uniformMatrix4fv(this.modelViewLocation, false, GLImmediate.matrix[0]);
if (GLEmulation.lightEnabled) {
var tmpMVinv = GLImmediate.matrixLib.mat4.create(GLImmediate.matrix[0]);
GLImmediate.matrixLib.mat4.inverse(tmpMVinv);
GLImmediate.matrixLib.mat4.transpose(tmpMVinv);
GLctx.uniformMatrix3fv(this.normalMatrixLocation, false, GLImmediate.matrixLib.mat4.toMat3(tmpMVinv));
}
}
if (this.projectionLocation && this.projectionMatrixVersion != GLImmediate.matrixVersion[1]) {
this.projectionMatrixVersion = GLImmediate.matrixVersion[1];
GLctx.uniformMatrix4fv(this.projectionLocation, false, GLImmediate.matrix[1]);
}
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 (!GLctx.currentArrayBufferBinding) {
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 (!GLctx.currentArrayBufferBinding) {
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 {
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 {
GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
GLctx.disableVertexAttribArray(attribLoc);
}
#endif
var t = 2+i;
if (this.textureMatrixLocations[i] && this.textureMatrixVersion[t] != GLImmediate.matrixVersion[t]) {
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 (!GLctx.currentArrayBufferBinding) {
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);
}
if (this.hasClipPlane) {
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
if (this.clipPlaneEquationLocation[clipPlaneId]) GLctx.uniform4fv(this.clipPlaneEquationLocation[clipPlaneId], GLEmulation.clipPlaneEquation[clipPlaneId]);
}
}
if (this.hasLighting) {
if (this.lightModelAmbientLocation) GLctx.uniform4fv(this.lightModelAmbientLocation, GLEmulation.lightModelAmbient);
if (this.materialAmbientLocation) GLctx.uniform4fv(this.materialAmbientLocation, GLEmulation.materialAmbient);
if (this.materialDiffuseLocation) GLctx.uniform4fv(this.materialDiffuseLocation, GLEmulation.materialDiffuse);
if (this.materialSpecularLocation) GLctx.uniform4fv(this.materialSpecularLocation, GLEmulation.materialSpecular);
if (this.materialShininessLocation) GLctx.uniform1f(this.materialShininessLocation, GLEmulation.materialShininess[0]);
if (this.materialEmissionLocation) GLctx.uniform4fv(this.materialEmissionLocation, GLEmulation.materialEmission);
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
if (this.lightAmbientLocation[lightId]) GLctx.uniform4fv(this.lightAmbientLocation[lightId], GLEmulation.lightAmbient[lightId]);
if (this.lightDiffuseLocation[lightId]) GLctx.uniform4fv(this.lightDiffuseLocation[lightId], GLEmulation.lightDiffuse[lightId]);
if (this.lightSpecularLocation[lightId]) GLctx.uniform4fv(this.lightSpecularLocation[lightId], GLEmulation.lightSpecular[lightId]);
if (this.lightPositionLocation[lightId]) GLctx.uniform4fv(this.lightPositionLocation[lightId], GLEmulation.lightPosition[lightId]);
}
}
if (this.hasAlphaTest) {
if (this.alphaTestRefLocation) GLctx.uniform1f(this.alphaTestRefLocation, GLEmulation.alphaTestRef);
}
if (GLImmediate.mode == GLctx.POINTS) {
if (this.pointSizeLocation) {
GLctx.uniform1f(this.pointSizeLocation, GLEmulation.pointSize);
}
}
};
this.cleanup = function() {
#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 (!GLctx.currentArrayBufferBinding) {
GLctx.bindBuffer(GLctx.ARRAY_BUFFER, null);
GLImmediate.lastArrayBuffer = null;
}
#if GL_UNSAFE_OPTS
GLImmediate.lastRenderer = null;
GLImmediate.lastProgram = null;
#endif
GLImmediate.matricesModified = true;
#endif
}
this.init();
}
return new Renderer();
},
setupFuncs() {
GLImmediate.MapTreeLib = GLImmediate.spawnMapTreeLib();
GLImmediate.spawnMapTreeLib = null;
GLImmediate.TexEnvJIT = GLImmediate.spawnTexEnvJIT();
GLImmediate.spawnTexEnvJIT = null;
GLImmediate.setupHooks();
},
setupHooks() {
if (!GLEmulation.hasRunInit) {
GLEmulation.init();
}
var glActiveTexture = _glActiveTexture;
_glActiveTexture = _emscripten_glActiveTexture = (texture) => {
GLImmediate.TexEnvJIT.hook_activeTexture(texture);
glActiveTexture(texture);
};
var glEnable = _glEnable;
_glEnable = _emscripten_glEnable = (cap) => {
GLImmediate.TexEnvJIT.hook_enable(cap);
glEnable(cap);
};
var glDisable = _glDisable;
_glDisable = _emscripten_glDisable = (cap) => {
GLImmediate.TexEnvJIT.hook_disable(cap);
glDisable(cap);
};
var glTexEnvf = (typeof _glTexEnvf != 'undefined') ? _glTexEnvf : () => {};
_glTexEnvf = _emscripten_glTexEnvf = (target, pname, param) => {
GLImmediate.TexEnvJIT.hook_texEnvf(target, pname, param);
};
var glTexEnvi = (typeof _glTexEnvi != 'undefined') ? _glTexEnvi : () => {};
_glTexEnvi = _emscripten_glTexEnvi = (target, pname, param) => {
{{{ fromPtr('param') }}}
GLImmediate.TexEnvJIT.hook_texEnvi(target, pname, param);
};
var glTexEnvfv = (typeof _glTexEnvfv != 'undefined') ? _glTexEnvfv : () => {};
_glTexEnvfv = _emscripten_glTexEnvfv = (target, pname, param) => {
{{{ fromPtr('param') }}}
GLImmediate.TexEnvJIT.hook_texEnvfv(target, pname, param);
};
_glGetTexEnviv = (target, pname, param) => {
{{{ fromPtr('param') }}}
GLImmediate.TexEnvJIT.hook_getTexEnviv(target, pname, param);
};
_glGetTexEnvfv = (target, pname, param) => {
{{{ fromPtr('param') }}}
GLImmediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param);
};
var glGetIntegerv = _glGetIntegerv;
_glGetIntegerv = _emscripten_glGetIntegerv = (pname, params) => {
switch (pname) {
case 0x8B8D: {
var cur = GLctx.getParameter(GLctx.CURRENT_PROGRAM);
if (cur == GLImmediate.fixedFunctionProgram) {
{{{ makeSetValue('params', '0', '0', 'i32') }}};
return;
}
break;
}
}
glGetIntegerv(pname, params);
};
},
initted: false,
init() {
err('WARNING: using emscripten GL immediate mode emulation. This is very limited in what it supports');
GLImmediate.initted = true;
if (!Browser.useWebGL) return;
GLImmediate.MAX_TEXTURES = Math.min(Module['GL_MAX_TEXTURE_IMAGE_UNITS'] || GLctx.getParameter(GLctx.MAX_TEXTURE_IMAGE_UNITS), 28);
GLImmediate.TexEnvJIT.init(GLctx, GLImmediate.MAX_TEXTURES);
GLImmediate.NUM_ATTRIBUTES = 3 + GLImmediate.MAX_TEXTURES;
GLImmediate.clientAttributes = [];
GLEmulation.enabledClientAttribIndices = [];
for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
GLImmediate.clientAttributes.push({});
GLEmulation.enabledClientAttribIndices.push(false);
}
GLImmediate.matrix = [];
GLImmediate.matrixStack = [];
GLImmediate.matrixVersion = [];
for (var i = 0; i < 2 + GLImmediate.MAX_TEXTURES; i++) {
GLImmediate.matrixStack.push([]);
GLImmediate.matrixVersion.push(0);
GLImmediate.matrix.push(GLImmediate.matrixLib.mat4.create());
GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[i]);
}
GLImmediate.rendererCache = GLImmediate.MapTreeLib.create();
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]);
},
prepareClientAttributes(count, beginEnd) {
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;
return;
}
GLImmediate.modifiedClientAttributes = false;
var clientStartPointer = {{{ POINTER_MAX }}};
var bytes = 0;
var minStride = {{{ POINTER_MAX }}};
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) {
#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
GLImmediate.restrideBuffer ||= _malloc(GL.MAX_TEMP_BUFFER_SIZE);
var start = GLImmediate.restrideBuffer;
bytes = 0;
for (var attr of attributes) {
var size = attr.sizeBytes;
if (size % 4 != 0) size += 4 - (size % 4);
attr.offset = bytes;
bytes += size;
}
for (var attr of attributes) {
var srcStride = Math.max(attr.sizeBytes, attr.stride);
if ((srcStride & 3) == 0 && (attr.sizeBytes & 3) == 0) {
for (var j = 0; j < count; j++) {
for (var k = 0; k < attr.sizeBytes; k+=4) {
var val = {{{ makeGetValue('attr.pointer', 'j*srcStride + k', 'i32') }}};
{{{ makeSetValue('start + attr.offset', 'bytes*j + k', 'val', 'i32') }}};
}
}
} else {
for (var j = 0; j < count; j++) {
for (var k = 0; k < attr.sizeBytes; k++) {
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 {
if (GLctx.currentArrayBufferBinding) {
GLImmediate.vertexPointer = 0;
} else {
GLImmediate.vertexPointer = clientStartPointer;
}
for (var attr of attributes) {
attr.offset = attr.pointer - GLImmediate.vertexPointer;
}
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;
}
},
flush(numProvidedIndexes, startIndex = 0, ptr = 0) {
#if ASSERTIONS
assert(numProvidedIndexes >= 0 || !numProvidedIndexes);
#endif
var renderer = GLImmediate.getRenderer();
var numVertices = 4 * GLImmediate.vertexCounter / GLImmediate.stride;
if (!numVertices) return;
#if ASSERTIONS
assert(numVertices % 1 == 0, "`numVertices` must be an integer.");
#endif
var emulatedElementArrayBuffer = false;
var numIndexes = 0;
if (numProvidedIndexes) {
numIndexes = numProvidedIndexes;
if (!GLctx.currentArrayBufferBinding && GLImmediate.firstVertex > GLImmediate.lastVertex) {
#if ASSERTIONS
assert(!GLctx.currentElementArrayBufferBinding);
#endif
for (var i = 0; i < numProvidedIndexes; i++) {
var currIndex = {{{ makeGetValue('ptr', 'i*2', 'u16') }}};
GLImmediate.firstVertex = Math.min(GLImmediate.firstVertex, currIndex);
GLImmediate.lastVertex = Math.max(GLImmediate.lastVertex, currIndex+1);
}
}
if (!GLctx.currentElementArrayBufferBinding) {
#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) {
if (GLImmediate.mode != 7) throw 'unsupported immediate mode ' + GLImmediate.mode;
#if ASSERTIONS
assert(GLImmediate.firstVertex % 4 == 0);
#endif
ptr = GLImmediate.firstVertex * 3;
var numQuads = numVertices / 4;
numIndexes = numQuads * 6;
#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;
GLImmediate.mode = GLctx.TRIANGLES;
}
renderer.prepare();
if (numIndexes) {
GLctx.drawElements(GLImmediate.mode, numIndexes, GLctx.UNSIGNED_SHORT, ptr);
} else {
GLctx.drawArrays(GLImmediate.mode, startIndex, numVertices);
}
if (emulatedElementArrayBuffer) {
GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GLctx.currentElementArrayBufferBinding] || null);
}
#if !GL_UNSAFE_OPTS
#if !GL_FFP_ONLY
renderer.cleanup();
#endif
#endif
}
},
$GLImmediateSetup__deps: ['$GLImmediate', () => 'GLImmediate.matrixLib = ' + read('gl-matrix.js') + ';\n'],
$GLImmediateSetup: {},
glBegin__deps: ['$GLImmediateSetup'],
glBegin: (mode) => {
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: () => {
GLImmediate.prepareClientAttributes(GLImmediate.rendererComponents[GLImmediate.VERTEX], true);
GLImmediate.firstVertex = 0;
GLImmediate.lastVertex = GLImmediate.vertexCounter / (GLImmediate.stride >> 2);
GLImmediate.flush();
GLImmediate.disableBeginEndClientAttributes();
GLImmediate.mode = -1;
GLImmediate.enabledClientAttributes = GLImmediate.enabledClientAttributes_preBegin;
GLImmediate.clientAttributes = GLImmediate.clientAttributes_preBegin;
GLImmediate.currentRenderer = null;
GLImmediate.modifiedClientAttributes = true;
},
glVertex2f: (x, y) => {
#if ASSERTIONS
assert(GLImmediate.mode >= 0);
#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: (x, y, z) => {
#if ASSERTIONS
assert(GLImmediate.mode >= 0);
#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: (x, y, z, w) => {
#if ASSERTIONS
assert(GLImmediate.mode >= 0);
#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: (p) => _glVertex2f({{{ makeGetValue('p', '0', 'float') }}},
{{{ makeGetValue('p', '4', 'float') }}}),
glVertex3fv__deps: ['glVertex3f'],
glVertex3fv: (p) => _glVertex3f({{{ makeGetValue('p', '0', 'float') }}},
{{{ makeGetValue('p', '4', 'float') }}},
{{{ makeGetValue('p', '8', 'float') }}}),
glVertex4fv__deps: ['glVertex4f'],
glVertex4fv: (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: (u, v) => {
#if ASSERTIONS
assert(GLImmediate.mode >= 0);
#endif
GLImmediate.vertexData[GLImmediate.vertexCounter++] = u;
GLImmediate.vertexData[GLImmediate.vertexCounter++] = v;
GLImmediate.addRendererComponent(GLImmediate.TEXTURE0, 2, GLctx.FLOAT);
},
glTexCoord2f: 'glTexCoord2i',
glTexCoord2fv__deps: ['glTexCoord2i'],
glTexCoord2fv: (v) =>
_glTexCoord2i({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}}),
glTexCoord4f: () => { throw 'glTexCoord4f: TODO' },
glColor4f: (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);
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: (r, g, b, a) => _glColor4f((r&255)/255, (g&255)/255, (b&255)/255, (a&255)/255),
glColor4us__deps: ['glColor4f'],
glColor4us: (r, g, b, a) => _glColor4f((r&65535)/65535, (g&65535)/65535, (b&65535)/65535, (a&65535)/65535),
glColor4ui__deps: ['glColor4f'],
glColor4ui: (r, g, b, a) => _glColor4f((r>>>0)/4294967295, (g>>>0)/4294967295, (b>>>0)/4294967295, (a>>>0)/4294967295),
glColor3f__deps: ['glColor4f'],
glColor3f: (r, g, b) => _glColor4f(r, g, b, 1),
glColor3d: 'glColor3f',
glColor3ub__deps: ['glColor4ub'],
glColor3ub: (r, g, b) => _glColor4ub(r, g, b, 255),
glColor3us__deps: ['glColor4us'],
glColor3us: (r, g, b) => _glColor4us(r, g, b, 65535),
glColor3ui__deps: ['glColor4ui'],
glColor3ui: (r, g, b) => _glColor4ui(r, g, b, 4294967295),
glColor3ubv__deps: ['glColor3ub'],
glColor3ubv: (p) => _glColor3ub({{{ makeGetValue('p', '0', 'i8') }}},
{{{ makeGetValue('p', '1', 'i8') }}},
{{{ makeGetValue('p', '2', 'i8') }}}),
glColor3usv__deps: ['glColor3us'],
glColor3usv: (p) => _glColor3us({{{ makeGetValue('p', '0', 'i16') }}},
{{{ makeGetValue('p', '2', 'i16') }}},
{{{ makeGetValue('p', '4', 'i16') }}}),
glColor3uiv__deps: ['glColor3ui'],
glColor3uiv: (p) => _glColor3ui({{{ makeGetValue('p', '0', 'i32') }}},
{{{ makeGetValue('p', '4', 'i32') }}},
{{{ makeGetValue('p', '8', 'i32') }}}),
glColor3fv__deps: ['glColor3f'],
glColor3fv: (p) => _glColor3f({{{ makeGetValue('p', '0', 'float') }}},
{{{ makeGetValue('p', '4', 'float') }}},
{{{ makeGetValue('p', '8', 'float') }}}),
glColor4fv__deps: ['glColor4f'],
glColor4fv: (p) => _glColor4f({{{ makeGetValue('p', '0', 'float') }}},
{{{ makeGetValue('p', '4', 'float') }}},
{{{ makeGetValue('p', '8', 'float') }}},
{{{ makeGetValue('p', '12', 'float') }}}),
glColor4ubv__deps: ['glColor4ub'],
glColor4ubv: (p) => _glColor4ub({{{ makeGetValue('p', '0', 'i8') }}},
{{{ makeGetValue('p', '1', 'i8') }}},
{{{ makeGetValue('p', '2', 'i8') }}},
{{{ makeGetValue('p', '3', 'i8') }}}),
glFogf: (pname, param) => {
switch (pname) {
case 0xB63:
GLEmulation.fogStart = param; break;
case 0xB64:
GLEmulation.fogEnd = param; break;
case 0xB62:
GLEmulation.fogDensity = param; break;
case 0xB65:
switch (param) {
case 0x801:
case 0x2601:
if (GLEmulation.fogMode != param) {
GLImmediate.currentRenderer = null;
GLEmulation.fogMode = param;
}
break;
default:
if (GLEmulation.fogMode != 0x800 ) {
GLImmediate.currentRenderer = null;
GLEmulation.fogMode = 0x800 ;
}
break;
}
break;
}
},
glFogi__deps: ['glFogf'],
glFogi: (pname, param) => {
return _glFogf(pname, param);
},
glFogfv__deps: ['glFogf'],
glFogfv: (pname, param) => {
switch (pname) {
case 0xB66:
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 0xB63:
case 0xB64:
_glFogf(pname, {{{ makeGetValue('param', '0', 'float') }}}); break;
}
},
glFogiv__deps: ['glFogf'],
glFogiv: (pname, param) => {
switch (pname) {
case 0xB66:
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',
glPointSize: (size) => {
GLEmulation.pointSize = size;
},
glPolygonMode: () => {},
glAlphaFunc: (func, ref) => {
switch(func) {
case 0x200:
case 0x201:
case 0x202:
case 0x203:
case 0x204:
case 0x205:
case 0x206:
case 0x207:
GLEmulation.alphaTestRef = ref;
if (GLEmulation.alphaTestFunc != func) {
GLEmulation.alphaTestFunc = func;
GLImmediate.currentRenderer = null;
}
break;
default:
#if GL_ASSERTIONS
err(`glAlphaFunc: Invalid alpha comparison function ${ptrToString(func)}!`);
#endif
break;
}
},
glNormal3f: (x, y, z) => {
#if ASSERTIONS
assert(GLImmediate.mode >= 0);
#endif
GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
GLImmediate.vertexData[GLImmediate.vertexCounter++] = z;
#if ASSERTIONS
assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
#endif
GLImmediate.addRendererComponent(GLImmediate.NORMAL, 3, GLctx.FLOAT);
},
glNormal3fv__deps: ['glNormal3f'],
glNormal3fv: (p) => {
_glNormal3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}});
},
glDrawRangeElements__deps: ['glDrawElements'],
glDrawRangeElements: (mode, start, end, count, type, indices) => {
_glDrawElements(mode, count, type, indices, start, end);
},
glEnableClientState: (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;
#if GL_FFP_ONLY
GL.enableVertexAttribArray(attrib);
#endif
if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1;
GLImmediate.modifiedClientAttributes = true;
}
},
glDisableClientState: (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;
#if GL_FFP_ONLY
GL.disableVertexAttribArray(attrib);
#endif
if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap];
GLImmediate.modifiedClientAttributes = true;
}
},
glVertexPointer: (size, type, stride, pointer) => {
GLImmediate.setClientAttribute(GLImmediate.VERTEX, size, type, stride, pointer);
#if GL_FFP_ONLY
if (GLctx.currentArrayBufferBinding) {
GLctx.vertexAttribPointer(GLImmediate.VERTEX, size, type, false, stride, pointer);
}
#endif
},
glTexCoordPointer: (size, type, stride, pointer) => {
GLImmediate.setClientAttribute(GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture, size, type, stride, pointer);
#if GL_FFP_ONLY
if (GLctx.currentArrayBufferBinding) {
var loc = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture;
GLctx.vertexAttribPointer(loc, size, type, false, stride, pointer);
}
#endif
},
glNormalPointer: (type, stride, pointer) => {
GLImmediate.setClientAttribute(GLImmediate.NORMAL, 3, type, stride, pointer);
#if GL_FFP_ONLY
if (GLctx.currentArrayBufferBinding) {
GLctx.vertexAttribPointer(GLImmediate.NORMAL, 3, type, true, stride, pointer);
}
#endif
},
glColorPointer: (size, type, stride, pointer) => {
GLImmediate.setClientAttribute(GLImmediate.COLOR, size, type, stride, pointer);
#if GL_FFP_ONLY
if (GLctx.currentArrayBufferBinding) {
GLctx.vertexAttribPointer(GLImmediate.COLOR, size, type, true, stride, pointer);
}
#endif
},
glClientActiveTexture: (texture) => {
GLImmediate.clientActiveTexture = texture - 0x84C0;
},
glDrawArrays: (mode, first, count) => {
if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6) {
GLctx.drawArrays(mode, first, count);
return;
}
GLImmediate.prepareClientAttributes(count, false);
GLImmediate.mode = mode;
if (!GLctx.currentArrayBufferBinding) {
GLImmediate.vertexData = {{{ makeHEAPView('F32', 'GLImmediate.vertexPointer', 'GLImmediate.vertexPointer + (first+count)*GLImmediate.stride') }}};
GLImmediate.firstVertex = first;
GLImmediate.lastVertex = first + count;
}
GLImmediate.flush(null, first);
GLImmediate.mode = -1;
},
glDrawElements: (mode, count, type, indices, start, end) => {
if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6 && GLctx.currentElementArrayBufferBinding) {
GLctx.drawElements(mode, count, type, indices);
return;
}
#if ASSERTIONS
if (!GLctx.currentElementArrayBufferBinding) {
assert(type == GLctx.UNSIGNED_SHORT);
}
warnOnce("DrawElements doesn't actually prepareClientAttributes properly.");
#endif
GLImmediate.prepareClientAttributes(count, false);
GLImmediate.mode = mode;
if (!GLctx.currentArrayBufferBinding) {
GLImmediate.firstVertex = end ? start : HEAP8.length;
GLImmediate.lastVertex = end ? end + 1 : 0;
start = GLImmediate.vertexPointer;
if (end) {
end = GLImmediate.vertexPointer + (end +1 ) * GLImmediate.stride;
GLImmediate.vertexData = HEAPF32.subarray({{{ getHeapOffset('start', 'float') }}}, {{{ getHeapOffset('end', 'float') }}});
} else {
GLImmediate.vertexData = HEAPF32.subarray({{{ getHeapOffset('start', 'float') }}});
}
}
GLImmediate.flush(count, 0, indices);
GLImmediate.mode = -1;
},
$emulGlGenVertexArrays__deps: ['$GLEmulation'],
$emulGlGenVertexArrays: (n, vaos) => {
for (var i = 0; i < n; i++) {
var id = GL.getNewId(GLEmulation.vaos);
GLEmulation.vaos[id] = {
id,
arrayBuffer: 0,
elementArrayBuffer: 0,
enabledVertexAttribArrays: {},
vertexAttribPointers: {},
enabledClientStates: {},
};
{{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}};
}
},
$emulGlDeleteVertexArrays: (n, vaos) => {
for (var i = 0; i < n; i++) {
var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}};
GLEmulation.vaos[id] = null;
if (GLEmulation.currentVao?.id == id) GLEmulation.currentVao = null;
}
},
$emulGlIsVertexArray: (array) => {
var vao = GLEmulation.vaos[array];
if (!vao) return 0;
return 1;
},
$emulGlBindVertexArray__deps: ['glBindBuffer', 'glEnableVertexAttribArray', 'glVertexAttribPointer', 'glEnableClientState'],
$emulGlBindVertexArray: (vao) => {
GLEmulation.currentVao = null;
GLImmediate.lastRenderer?.cleanup();
_glBindBuffer(GLctx.ARRAY_BUFFER, 0);
_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) {
var info = GLEmulation.vaos[vao];
_glBindBuffer(GLctx.ARRAY_BUFFER, info.arrayBuffer);
_glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer);
for (var vaa in info.enabledVertexAttribArrays) {
_glEnableVertexAttribArray(vaa);
}
for (var vaa in info.vertexAttribPointers) {
_glVertexAttribPointer(...info.vertexAttribPointers[vaa]);
}
for (var attrib in info.enabledClientStates) {
_glEnableClientState(attrib|0);
}
GLEmulation.currentVao = info;
}
},
glMatrixMode__deps: ['$GL', '$GLImmediateSetup'],
glMatrixMode: (mode) => {
if (mode == 0x1700 ) {
GLImmediate.currentMatrix = 0;
} else if (mode == 0x1701 ) {
GLImmediate.currentMatrix = 1;
} else if (mode == 0x1702) {
GLImmediate.useTextureMatrix = true;
GLImmediate.currentMatrix = 2 + GLImmediate.TexEnvJIT.getActiveTexture();
} else {
throw `Wrong mode ${mode} passed to glMatrixMode`;
}
},
glPushMatrix: () => {
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: () => {
if (GLImmediate.matrixStack[GLImmediate.currentMatrix].length == 0) {
GL.recordError(0x504);
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: () => {
GLImmediate.matricesModified = true;
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[GLImmediate.currentMatrix]);
},
glLoadMatrixd: (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: (matrix) => {
#if GL_DEBUG
if (GL.debug) dbg('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: (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: (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: (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: (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: (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: (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: (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: (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: (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: (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: (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: () => { throw 'glDrawBuffer: TODO' },
#if MAX_WEBGL_VERSION < 2
glReadBuffer: () => { throw 'glReadBuffer: TODO' },
#endif
glClipPlane: (pname, param) => {
if ((pname >= 0x3000) && (pname < 0x3006) ) {
var clipPlaneId = pname - 0x3000;
GLEmulation.clipPlaneEquation[clipPlaneId][0] = {{{ makeGetValue('param', '0', 'double') }}};
GLEmulation.clipPlaneEquation[clipPlaneId][1] = {{{ makeGetValue('param', '8', 'double') }}};
GLEmulation.clipPlaneEquation[clipPlaneId][2] = {{{ makeGetValue('param', '16', 'double') }}};
GLEmulation.clipPlaneEquation[clipPlaneId][3] = {{{ makeGetValue('param', '24', 'double') }}};
var tmpMV = GLImmediate.matrixLib.mat4.create(GLImmediate.matrix[0]);
GLImmediate.matrixLib.mat4.inverse(tmpMV);
GLImmediate.matrixLib.mat4.transpose(tmpMV);
GLImmediate.matrixLib.mat4.multiplyVec4(tmpMV, GLEmulation.clipPlaneEquation[clipPlaneId]);
}
},
glLightfv: (light, pname, param) => {
if ((light >= 0x4000) && (light < 0x4008) ) {
var lightId = light - 0x4000;
if (pname == 0x1200) {
GLEmulation.lightAmbient[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}};
GLEmulation.lightAmbient[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}};
GLEmulation.lightAmbient[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}};
GLEmulation.lightAmbient[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}};
} else if (pname == 0x1201) {
GLEmulation.lightDiffuse[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}};
GLEmulation.lightDiffuse[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}};
GLEmulation.lightDiffuse[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}};
GLEmulation.lightDiffuse[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}};
} else if (pname == 0x1202) {
GLEmulation.lightSpecular[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}};
GLEmulation.lightSpecular[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}};
GLEmulation.lightSpecular[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}};
GLEmulation.lightSpecular[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}};
} else if (pname == 0x1203) {
GLEmulation.lightPosition[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}};
GLEmulation.lightPosition[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}};
GLEmulation.lightPosition[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}};
GLEmulation.lightPosition[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}};
GLImmediate.matrixLib.mat4.multiplyVec4(GLImmediate.matrix[0], GLEmulation.lightPosition[lightId]);
} else {
throw 'glLightfv: TODO: ' + pname;
}
}
},
glLightModelf: (pname, param) => {
if (pname == 0x0B52) {
GLEmulation.lightModelTwoSide = (param != 0) ? true : false;
} else {
throw 'glLightModelf: TODO: ' + pname;
}
},
glLightModelfv: (pname, param) => {
if (pname == 0x0B53) {
GLEmulation.lightModelAmbient[0] = {{{ makeGetValue('param', '0', 'float') }}};
GLEmulation.lightModelAmbient[1] = {{{ makeGetValue('param', '4', 'float') }}};
GLEmulation.lightModelAmbient[2] = {{{ makeGetValue('param', '8', 'float') }}};
GLEmulation.lightModelAmbient[3] = {{{ makeGetValue('param', '12', 'float') }}};
} else {
throw 'glLightModelfv: TODO: ' + pname;
}
},
glMaterialfv: (face, pname, param) => {
if ((face != 0x0404) && (face != 0x0408)) { throw 'glMaterialfv: TODO' + face; }
if (pname == 0x1200) {
GLEmulation.materialAmbient[0] = {{{ makeGetValue('param', '0', 'float') }}};
GLEmulation.materialAmbient[1] = {{{ makeGetValue('param', '4', 'float') }}};
GLEmulation.materialAmbient[2] = {{{ makeGetValue('param', '8', 'float') }}};
GLEmulation.materialAmbient[3] = {{{ makeGetValue('param', '12', 'float') }}};
} else if (pname == 0x1201) {
GLEmulation.materialDiffuse[0] = {{{ makeGetValue('param', '0', 'float') }}};
GLEmulation.materialDiffuse[1] = {{{ makeGetValue('param', '4', 'float') }}};
GLEmulation.materialDiffuse[2] = {{{ makeGetValue('param', '8', 'float') }}};
GLEmulation.materialDiffuse[3] = {{{ makeGetValue('param', '12', 'float') }}};
} else if (pname == 0x1202) {
GLEmulation.materialSpecular[0] = {{{ makeGetValue('param', '0', 'float') }}};
GLEmulation.materialSpecular[1] = {{{ makeGetValue('param', '4', 'float') }}};
GLEmulation.materialSpecular[2] = {{{ makeGetValue('param', '8', 'float') }}};
GLEmulation.materialSpecular[3] = {{{ makeGetValue('param', '12', 'float') }}};
} else if (pname == 0x1601) {
GLEmulation.materialShininess[0] = {{{ makeGetValue('param', '0', 'float') }}};
} else {
throw 'glMaterialfv: TODO: ' + pname;
}
},
glTexGeni: (coord, pname, param) => { throw 'glTexGeni: TODO' },
glTexGenfv: (coord, pname, param) => { throw 'glTexGenfv: TODO' },
glTexEnvi: (target, pname, params) => warnOnce('glTexEnvi: TODO'),
glTexEnvf: (target, pname, params) => warnOnce('glTexEnvf: TODO'),
glTexEnvfv: (target, pname, params) => warnOnce('glTexEnvfv: TODO'),
glGetTexEnviv: (target, pname, param) => { throw 'GL emulation not initialized!'; },
glGetTexEnvfv: (target, pname, param) => { throw 'GL emulation not initialized!'; },
glTexImage1D: (target, level, internalformat, width, border, format, type, data) => { throw 'glTexImage1D: TODO' },
glTexCoord3f: (target, level, internalformat, width, border, format, type, data) => { throw 'glTexCoord3f: TODO' },
glGetTexLevelParameteriv: (target, level, pname, params) => { throw 'glGetTexLevelParameteriv: TODO' },
glShadeModel: () => warnOnce('TODO: glShadeModel'),
glGenFramebuffersOES: 'glGenFramebuffers',
glGenRenderbuffersOES: 'glGenRenderbuffers',
glBindFramebufferOES: 'glBindFramebuffer',
glBindRenderbufferOES: 'glBindRenderbuffer',
glGetRenderbufferParameterivOES: 'glGetRenderbufferParameteriv',
glFramebufferRenderbufferOES: 'glFramebufferRenderbuffer',
glRenderbufferStorageOES: 'glRenderbufferStorage',
glCheckFramebufferStatusOES: 'glCheckFramebufferStatus',
glDeleteFramebuffersOES: 'glDeleteFramebuffers',
glDeleteRenderbuffersOES: 'glDeleteRenderbuffers',
glFramebufferTexture2DOES: 'glFramebufferTexture2D',
gluPerspective: (fov, aspect, near, far) => {
GLImmediate.matricesModified = true;
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
GLImmediate.matrix[GLImmediate.currentMatrix] =
GLImmediate.matrixLib.mat4.perspective(fov, aspect, near, far,
GLImmediate.matrix[GLImmediate.currentMatrix]);
},
gluLookAt: (ex, ey, ez, cx, cy, cz, ux, uy, uz) => {
GLImmediate.matricesModified = true;
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
GLImmediate.matrixLib.mat4.lookAt(GLImmediate.matrix[GLImmediate.currentMatrix], [ex, ey, ez],
[cx, cy, cz], [ux, uy, uz]);
},
gluProject: (objX, objY, objZ, model, proj, view, winX, winY, winZ) => {
var inVec = new Float32Array(4);
var outVec = new Float32Array(4);
GLImmediate.matrixLib.mat4.multiplyVec4({{{ makeHEAPView('F64', 'model', 'model+' + (16*8)) }}},
[objX, objY, objZ, 1.0], outVec);
GLImmediate.matrixLib.mat4.multiplyVec4({{{ makeHEAPView('F64', 'proj', 'proj+' + (16*8)) }}},
outVec, inVec);
if (inVec[3] == 0.0) {
return 0 ;
}
inVec[0] /= inVec[3];
inVec[1] /= inVec[3];
inVec[2] /= inVec[3];
inVec[0] = inVec[0] * 0.5 + 0.5;
inVec[1] = inVec[1] * 0.5 + 0.5;
inVec[2] = inVec[2] * 0.5 + 0.5;
inVec[0] = inVec[0] * {{{ makeGetValue('view', 2*4, 'i32') }}} + {{{ makeGetValue('view', 0*4, 'i32') }}};
inVec[1] = inVec[1] * {{{ makeGetValue('view', 3*4, 'i32') }}} + {{{ makeGetValue('view', 1*4, 'i32') }}};
{{{ makeSetValue('winX', '0', 'inVec[0]', 'double') }}};
{{{ makeSetValue('winY', '0', 'inVec[1]', 'double') }}};
{{{ makeSetValue('winZ', '0', 'inVec[2]', 'double') }}};
return 1 ;
},
gluUnProject: (winX, winY, winZ, model, proj, view, objX, objY, objZ) => {
var result = GLImmediate.matrixLib.vec3.unproject([winX, winY, winZ],
{{{ makeHEAPView('F64', 'model', 'model+' + (16*8)) }}},
{{{ makeHEAPView('F64', 'proj', 'proj+' + (16*8)) }}},
{{{ makeHEAPView('32', 'view', 'view+' + (4*4)) }}});
if (result === null) {
return 0 ;
}
{{{ makeSetValue('objX', '0', 'result[0]', 'double') }}};
{{{ makeSetValue('objY', '0', 'result[1]', 'double') }}};
{{{ makeSetValue('objZ', '0', 'result[2]', 'double') }}};
return 1 ;
},
gluOrtho2D__deps: ['glOrtho'],
gluOrtho2D: (left, right, bottom, top) => _glOrtho(left, right, bottom, top, -1, 1),
};
extraLibraryFuncs.push('$GLEmulation');
recordGLProcAddressGet(LibraryGLEmulation);
addToLibrary(LibraryGLEmulation);