Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libglemu.js
4150 views
1
/**
2
* @license
3
* Copyright 2010 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
assert(LEGACY_GL_EMULATION, 'libglemu.js should only be included with LEGACY_GL_EMULATION set')
8
assert(!FULL_ES2, 'cannot emulate both ES2 and legacy GL');
9
assert(!FULL_ES3, 'cannot emulate both ES3 and legacy GL');
10
11
{{{
12
const copySigs = (func) => {
13
if (!RELOCATABLE) return '';
14
return ` _${func}.sig = _emscripten_${func}.sig = orig_${func}.sig;`;
15
};
16
const fromPtr = (arg) => {
17
if (CAN_ADDRESS_2GB) {
18
return `${arg} >>>= 0`;
19
} else if (MEMORY64) {
20
return `${arg} = Number(${arg})`;
21
}
22
return '';
23
};
24
}}}
25
26
var LibraryGLEmulation = {
27
// GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL
28
$GLEmulation__deps: ['$GLImmediateSetup', 'glEnable', 'glDisable',
29
'glIsEnabled', 'glGetBooleanv', 'glGetIntegerv', 'glGetString',
30
'glCreateShader', 'glShaderSource', 'glCompileShader', 'glAttachShader',
31
'glDetachShader', 'glUseProgram', 'glDeleteProgram', 'glBindAttribLocation',
32
'glLinkProgram', 'glBindBuffer', 'glGetFloatv', 'glHint',
33
'glEnableVertexAttribArray', 'glDisableVertexAttribArray',
34
'glVertexAttribPointer', 'glActiveTexture', '$stringToNewUTF8',
35
'$ptrToString', '$getEmscriptenSupportedExtensions',
36
],
37
$GLEmulation__postset:
38
#if MAYBE_CLOSURE_COMPILER
39
// Forward declare GL functions that are overridden by GLEmulation here to appease Closure compiler.
40
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDrawArrays;' +
41
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDrawElements;' +
42
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glActiveTexture;' +
43
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glEnable;' +
44
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDisable;' +
45
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvf;' +
46
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvi;' +
47
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glTexEnvfv;' +
48
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetIntegerv;' +
49
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glIsEnabled;' +
50
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetBooleanv;' +
51
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetString;' +
52
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glCreateShader;' +
53
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glShaderSource;' +
54
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glCompileShader;' +
55
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glAttachShader;' +
56
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDetachShader;' +
57
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glUseProgram;' +
58
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDeleteProgram;' +
59
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glBindAttribLocation;' +
60
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glLinkProgram;' +
61
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glBindBuffer;' +
62
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glGetFloatv;' +
63
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glHint;' +
64
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glEnableVertexAttribArray;' +
65
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glDisableVertexAttribArray;' +
66
'/**@suppress {duplicate, undefinedVars}*/var _emscripten_glVertexAttribPointer;' +
67
'/**@suppress {duplicate, undefinedVars}*/var _glTexEnvf;' +
68
'/**@suppress {duplicate, undefinedVars}*/var _glTexEnvi;' +
69
'/**@suppress {duplicate, undefinedVars}*/var _glTexEnvfv;' +
70
'/**@suppress {duplicate, undefinedVars}*/var _glGetTexEnviv;' +
71
'/**@suppress {duplicate, undefinedVars}*/var _glGetTexEnvfv;' +
72
#endif
73
'GLEmulation.init();',
74
$GLEmulation: {
75
// Fog support. Partial, we assume shaders are used that implement fog. We just pass them uniforms
76
fogStart: 0,
77
fogEnd: 1,
78
fogDensity: 1.0,
79
fogColor: null,
80
fogMode: 0x800, // GL_EXP
81
fogEnabled: false,
82
83
// GL_CLIP_PLANE support
84
MAX_CLIP_PLANES: 6,
85
clipPlaneEnabled: [false, false, false, false, false, false],
86
clipPlaneEquation: [],
87
88
// GL_LIGHTING support
89
lightingEnabled: false,
90
91
lightModelAmbient: null,
92
lightModelLocalViewer: false,
93
lightModelTwoSide: false,
94
95
materialAmbient: null,
96
materialDiffuse: null,
97
materialSpecular: null,
98
materialShininess: null,
99
materialEmission: null,
100
101
MAX_LIGHTS: 8,
102
lightEnabled: [false, false, false, false, false, false, false, false],
103
lightAmbient: [],
104
lightDiffuse: [],
105
lightSpecular: [],
106
lightPosition: [],
107
// TODO attenuation modes of lights
108
109
// GL_ALPHA_TEST support
110
alphaTestEnabled: false,
111
alphaTestFunc: 0x207, // GL_ALWAYS
112
alphaTestRef: 0.0,
113
114
// GL_POINTS support.
115
pointSize: 1.0,
116
117
// VAO support
118
vaos: [],
119
currentVao: null,
120
enabledVertexAttribArrays: {}, // helps with vao cleanups
121
122
hasRunInit: false,
123
124
// Find a token in a shader source string
125
findToken(source, token) {
126
function isIdentChar(ch) {
127
if (ch >= 48 && ch <= 57) // 0-9
128
return true;
129
if (ch >= 65 && ch <= 90) // A-Z
130
return true;
131
if (ch >= 97 && ch <= 122) // a-z
132
return true;
133
return false;
134
}
135
var i = -1;
136
do {
137
i = source.indexOf(token, i + 1);
138
if (i < 0) {
139
break;
140
}
141
if (i > 0 && isIdentChar(source[i - 1])) {
142
continue;
143
}
144
i += token.length;
145
if (i < source.length - 1 && isIdentChar(source[i + 1])) {
146
continue;
147
}
148
return true;
149
} while (true);
150
return false;
151
},
152
153
init() {
154
// Do not activate immediate/emulation code (e.g. replace glDrawElements)
155
// when in FULL_ES2 mode. We do not need full emulation, we instead
156
// emulate client-side arrays etc. in FULL_ES2 code in a straightforward
157
// manner, and avoid not having a bound buffer be ambiguous between es2
158
// emulation code and legacy gl emulation code.
159
#if FULL_ES2
160
return;
161
#endif
162
163
if (GLEmulation.hasRunInit) {
164
return;
165
}
166
GLEmulation.hasRunInit = true;
167
168
GLEmulation.fogColor = new Float32Array(4);
169
170
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
171
GLEmulation.clipPlaneEquation[clipPlaneId] = new Float32Array(4);
172
}
173
174
// set defaults for GL_LIGHTING
175
GLEmulation.lightModelAmbient = new Float32Array([0.2, 0.2, 0.2, 1.0]);
176
GLEmulation.materialAmbient = new Float32Array([0.2, 0.2, 0.2, 1.0]);
177
GLEmulation.materialDiffuse = new Float32Array([0.8, 0.8, 0.8, 1.0]);
178
GLEmulation.materialSpecular = new Float32Array([0.0, 0.0, 0.0, 1.0]);
179
GLEmulation.materialShininess = new Float32Array([0.0]);
180
GLEmulation.materialEmission = new Float32Array([0.0, 0.0, 0.0, 1.0]);
181
182
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
183
GLEmulation.lightAmbient[lightId] = new Float32Array([0.0, 0.0, 0.0, 1.0]);
184
GLEmulation.lightDiffuse[lightId] = lightId ? new Float32Array([0.0, 0.0, 0.0, 1.0]) : new Float32Array([1.0, 1.0, 1.0, 1.0]);
185
GLEmulation.lightSpecular[lightId] = lightId ? new Float32Array([0.0, 0.0, 0.0, 1.0]) : new Float32Array([1.0, 1.0, 1.0, 1.0]);
186
GLEmulation.lightPosition[lightId] = new Float32Array([0.0, 0.0, 1.0, 0.0]);
187
}
188
189
190
// Add some emulation workarounds
191
err('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work.');
192
#if GL_UNSAFE_OPTS == 1
193
err('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -sGL_UNSAFE_OPTS=0');
194
#endif
195
196
// XXX some of the capabilities we don't support may lead to incorrect rendering, if we do not emulate them in shaders
197
var validCapabilities = {
198
0xB44: 1, // GL_CULL_FACE
199
0xBE2: 1, // GL_BLEND
200
0xBD0: 1, // GL_DITHER,
201
0xB90: 1, // GL_STENCIL_TEST
202
0xB71: 1, // GL_DEPTH_TEST
203
0xC11: 1, // GL_SCISSOR_TEST
204
0x8037: 1, // GL_POLYGON_OFFSET_FILL
205
0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE
206
0x80A0: 1 // GL_SAMPLE_COVERAGE
207
};
208
209
var orig_glEnable = _glEnable;
210
_glEnable = _emscripten_glEnable = (cap) => {
211
// Clean up the renderer on any change to the rendering state. The optimization of
212
// skipping renderer setup is aimed at the case of multiple glDraw* right after each other
213
GLImmediate.lastRenderer?.cleanup();
214
if (cap == 0xB60 /* GL_FOG */) {
215
if (GLEmulation.fogEnabled != true) {
216
GLImmediate.currentRenderer = null; // Fog parameter is part of the FFP shader state, we must re-lookup the renderer to use.
217
GLEmulation.fogEnabled = true;
218
}
219
return;
220
} else if ((cap >= 0x3000) && (cap < 0x3006) /* GL_CLIP_PLANE0 to GL_CLIP_PLANE5 */) {
221
var clipPlaneId = cap - 0x3000;
222
if (GLEmulation.clipPlaneEnabled[clipPlaneId] != true) {
223
GLImmediate.currentRenderer = null; // clip plane parameter is part of the FFP shader state, we must re-lookup the renderer to use.
224
GLEmulation.clipPlaneEnabled[clipPlaneId] = true;
225
}
226
return;
227
} else if ((cap >= 0x4000) && (cap < 0x4008) /* GL_LIGHT0 to GL_LIGHT7 */) {
228
var lightId = cap - 0x4000;
229
if (GLEmulation.lightEnabled[lightId] != true) {
230
GLImmediate.currentRenderer = null; // light parameter is part of the FFP shader state, we must re-lookup the renderer to use.
231
GLEmulation.lightEnabled[lightId] = true;
232
}
233
return;
234
} else if (cap == 0xB50 /* GL_LIGHTING */) {
235
if (GLEmulation.lightingEnabled != true) {
236
GLImmediate.currentRenderer = null; // light parameter is part of the FFP shader state, we must re-lookup the renderer to use.
237
GLEmulation.lightingEnabled = true;
238
}
239
return;
240
} else if (cap == 0xBC0 /* GL_ALPHA_TEST */) {
241
if (GLEmulation.alphaTestEnabled != true) {
242
GLImmediate.currentRenderer = null; // alpha testing is part of the FFP shader state, we must re-lookup the renderer to use.
243
GLEmulation.alphaTestEnabled = true;
244
}
245
return;
246
} else if (cap == 0xDE1 /* GL_TEXTURE_2D */) {
247
// XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
248
// it by forwarding to glEnableClientState
249
/* Actually, let's not, for now. (This sounds exceedingly broken)
250
* This is in gl_ps_workaround2.c.
251
_glEnableClientState(cap);
252
*/
253
return;
254
} else if (!(cap in validCapabilities)) {
255
return;
256
}
257
orig_glEnable(cap);
258
};
259
{{{ copySigs('glEnable') }}}
260
261
var orig_glDisable = _glDisable;
262
_glDisable = _emscripten_glDisable = (cap) => {
263
GLImmediate.lastRenderer?.cleanup();
264
if (cap == 0xB60 /* GL_FOG */) {
265
if (GLEmulation.fogEnabled != false) {
266
GLImmediate.currentRenderer = null; // Fog parameter is part of the FFP shader state, we must re-lookup the renderer to use.
267
GLEmulation.fogEnabled = false;
268
}
269
return;
270
} else if ((cap >= 0x3000) && (cap < 0x3006) /* GL_CLIP_PLANE0 to GL_CLIP_PLANE5 */) {
271
var clipPlaneId = cap - 0x3000;
272
if (GLEmulation.clipPlaneEnabled[clipPlaneId] != false) {
273
GLImmediate.currentRenderer = null; // clip plane parameter is part of the FFP shader state, we must re-lookup the renderer to use.
274
GLEmulation.clipPlaneEnabled[clipPlaneId] = false;
275
}
276
return;
277
} else if ((cap >= 0x4000) && (cap < 0x4008) /* GL_LIGHT0 to GL_LIGHT7 */) {
278
var lightId = cap - 0x4000;
279
if (GLEmulation.lightEnabled[lightId] != false) {
280
GLImmediate.currentRenderer = null; // light parameter is part of the FFP shader state, we must re-lookup the renderer to use.
281
GLEmulation.lightEnabled[lightId] = false;
282
}
283
return;
284
} else if (cap == 0xB50 /* GL_LIGHTING */) {
285
if (GLEmulation.lightingEnabled != false) {
286
GLImmediate.currentRenderer = null; // light parameter is part of the FFP shader state, we must re-lookup the renderer to use.
287
GLEmulation.lightingEnabled = false;
288
}
289
return;
290
} else if (cap == 0xBC0 /* GL_ALPHA_TEST */) {
291
if (GLEmulation.alphaTestEnabled != false) {
292
GLImmediate.currentRenderer = null; // alpha testing is part of the FFP shader state, we must re-lookup the renderer to use.
293
GLEmulation.alphaTestEnabled = false;
294
}
295
return;
296
} else if (cap == 0xDE1 /* GL_TEXTURE_2D */) {
297
// XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support
298
// it by forwarding to glDisableClientState
299
/* Actually, let's not, for now. (This sounds exceedingly broken)
300
* This is in gl_ps_workaround2.c.
301
_glDisableClientState(cap);
302
*/
303
return;
304
} else if (!(cap in validCapabilities)) {
305
return;
306
}
307
orig_glDisable(cap);
308
};
309
{{{ copySigs('glDisable') }}}
310
311
var orig_glIsEnabled = _glIsEnabled;
312
_glIsEnabled = _emscripten_glIsEnabled = (cap) => {
313
if (cap == 0xB60 /* GL_FOG */) {
314
return GLEmulation.fogEnabled ? 1 : 0;
315
} else if ((cap >= 0x3000) && (cap < 0x3006) /* GL_CLIP_PLANE0 to GL_CLIP_PLANE5 */) {
316
var clipPlaneId = cap - 0x3000;
317
return GLEmulation.clipPlaneEnabled[clipPlaneId] ? 1 : 0;
318
} else if ((cap >= 0x4000) && (cap < 0x4008) /* GL_LIGHT0 to GL_LIGHT7 */) {
319
var lightId = cap - 0x4000;
320
return GLEmulation.lightEnabled[lightId] ? 1 : 0;
321
} else if (cap == 0xB50 /* GL_LIGHTING */) {
322
return GLEmulation.lightingEnabled ? 1 : 0;
323
} else if (cap == 0xBC0 /* GL_ALPHA_TEST */) {
324
return GLEmulation.alphaTestEnabled ? 1 : 0;
325
} else if (!(cap in validCapabilities)) {
326
return 0;
327
}
328
return GLctx.isEnabled(cap);
329
};
330
{{{ copySigs('glIsEnabled') }}}
331
332
var orig_glGetBooleanv = _glGetBooleanv;
333
_glGetBooleanv = _emscripten_glGetBooleanv = (pname, p) => {
334
var attrib = GLEmulation.getAttributeFromCapability(pname);
335
if (attrib !== null) {
336
{{{ fromPtr('p') }}}
337
var result = GLImmediate.enabledClientAttributes[attrib];
338
{{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}};
339
return;
340
}
341
orig_glGetBooleanv(pname, p);
342
};
343
{{{ copySigs('glGetBooleanv') }}}
344
345
var orig_glGetIntegerv = _glGetIntegerv;
346
_glGetIntegerv = _emscripten_glGetIntegerv = (pname, params) => {
347
{{{ fromPtr('params') }}}
348
switch (pname) {
349
case 0x84E2: pname = GLctx.MAX_TEXTURE_IMAGE_UNITS /* fake it */; break; // GL_MAX_TEXTURE_UNITS
350
case 0x8B4A: { // GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB
351
var result = GLctx.getParameter(GLctx.MAX_VERTEX_UNIFORM_VECTORS);
352
{{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
353
return;
354
}
355
case 0x8B49: { // GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB
356
var result = GLctx.getParameter(GLctx.MAX_FRAGMENT_UNIFORM_VECTORS);
357
{{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
358
return;
359
}
360
case 0x8B4B: { // GL_MAX_VARYING_FLOATS_ARB
361
var result = GLctx.getParameter(GLctx.MAX_VARYING_VECTORS);
362
{{{ makeSetValue('params', '0', 'result*4', 'i32') }}}; // GLES gives num of 4-element vectors, GL wants individual components, so multiply
363
return;
364
}
365
case 0x8871: pname = GLctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS
366
case 0x807A: { // GL_VERTEX_ARRAY_SIZE
367
var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
368
{{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
369
return;
370
}
371
case 0x807B: { // GL_VERTEX_ARRAY_TYPE
372
var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
373
{{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
374
return;
375
}
376
case 0x807C: { // GL_VERTEX_ARRAY_STRIDE
377
var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
378
{{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
379
return;
380
}
381
case 0x8081: { // GL_COLOR_ARRAY_SIZE
382
var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
383
{{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
384
return;
385
}
386
case 0x8082: { // GL_COLOR_ARRAY_TYPE
387
var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
388
{{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
389
return;
390
}
391
case 0x8083: { // GL_COLOR_ARRAY_STRIDE
392
var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
393
{{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
394
return;
395
}
396
case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE
397
var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
398
{{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
399
return;
400
}
401
case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE
402
var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
403
{{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
404
return;
405
}
406
case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE
407
var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture];
408
{{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
409
return;
410
}
411
case 0x0D32: { // GL_MAX_CLIP_PLANES
412
{{{ makeSetValue('params', '0', 'GLEmulation.MAX_CLIP_PLANES', 'i32') }}}; // all implementations need to support atleast 6
413
return;
414
}
415
case 0x0BA0: { // GL_MATRIX_MODE
416
{{{ makeSetValue('params', '0', 'GLImmediate.currentMatrix + 0x1700', 'i32') }}};
417
return;
418
}
419
case 0x0BC1: { // GL_ALPHA_TEST_FUNC
420
{{{ makeSetValue('params', '0', 'GLEmulation.alphaTestFunc', 'i32') }}};
421
return;
422
}
423
}
424
orig_glGetIntegerv(pname, params);
425
};
426
{{{ copySigs('glGetIntegerv') }}}
427
428
var orig_glGetString = _glGetString;
429
_glGetString = _emscripten_glGetString = (name_) => {
430
if (GL.stringCache[name_]) return GL.stringCache[name_];
431
switch (name_) {
432
case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support
433
var ret = stringToNewUTF8(getEmscriptenSupportedExtensions(GLctx).join(' ') +
434
' 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' +
435
(GL.currentContext.compressionExt ? ' GL_ARB_texture_compression GL_EXT_texture_compression_s3tc' : '') +
436
(GL.currentContext.anisotropicExt ? ' GL_EXT_texture_filter_anisotropic' : '')
437
);
438
return GL.stringCache[name_] = {{{ to64('ret') }}};
439
}
440
return orig_glGetString(name_);
441
};
442
{{{ copySigs('glGetString') }}}
443
444
// Do some automatic rewriting to work around GLSL differences. Note that this must be done in
445
// tandem with the rest of the program, by itself it cannot suffice.
446
// Note that we need to remember shader types for this rewriting, saving sources makes it easier to debug.
447
GL.shaderInfos = {};
448
#if GL_DEBUG
449
GL.shaderSources = {};
450
GL.shaderOriginalSources = {};
451
#endif
452
var orig_glCreateShader = _glCreateShader;
453
_glCreateShader = _emscripten_glCreateShader = (shaderType) => {
454
var id = orig_glCreateShader(shaderType);
455
GL.shaderInfos[id] = {
456
type: shaderType,
457
ftransform: false
458
};
459
return id;
460
};
461
{{{ copySigs('glCreateShader') }}}
462
463
function ensurePrecision(source) {
464
if (!/precision +(low|medium|high)p +float *;/.test(source)) {
465
source = '#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n' + source;
466
}
467
return source;
468
}
469
470
var orig_glShaderSource = _glShaderSource;
471
_glShaderSource = _emscripten_glShaderSource = (shader, count, string, length) => {
472
{{{ fromPtr('string') }}}
473
{{{ fromPtr('length') }}}
474
var source = GL.getSource(shader, count, string, length);
475
#if GL_DEBUG
476
dbg("glShaderSource: Input: \n" + source);
477
GL.shaderOriginalSources[shader] = source;
478
#endif
479
// XXX We add attributes and uniforms to shaders. The program can ask for the # of them, and see the
480
// ones we generated, potentially confusing it? Perhaps we should hide them.
481
if (GL.shaderInfos[shader].type == GLctx.VERTEX_SHADER) {
482
// Replace ftransform() with explicit project/modelview transforms, and add position and matrix info.
483
var has_pm = source.search(/u_projection/) >= 0;
484
var has_mm = source.search(/u_modelView/) >= 0;
485
var has_pv = source.search(/a_position/) >= 0;
486
var need_pm = 0, need_mm = 0, need_pv = 0;
487
var old = source;
488
source = source.replace(/ftransform\(\)/g, '(u_projection * u_modelView * a_position)');
489
if (old != source) need_pm = need_mm = need_pv = 1;
490
old = source;
491
source = source.replace(/gl_ProjectionMatrix/g, 'u_projection');
492
if (old != source) need_pm = 1;
493
old = source;
494
source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][2], u_modelView[1][2], u_modelView[2][2], u_modelView[3][2])'); // XXX extremely inefficient
495
if (old != source) need_mm = 1;
496
old = source;
497
source = source.replace(/gl_ModelViewMatrix/g, 'u_modelView');
498
if (old != source) need_mm = 1;
499
old = source;
500
source = source.replace(/gl_Vertex/g, 'a_position');
501
if (old != source) need_pv = 1;
502
old = source;
503
source = source.replace(/gl_ModelViewProjectionMatrix/g, '(u_projection * u_modelView)');
504
if (old != source) need_pm = need_mm = 1;
505
if (need_pv && !has_pv) source = 'attribute vec4 a_position; \n' + source;
506
if (need_mm && !has_mm) source = 'uniform mat4 u_modelView; \n' + source;
507
if (need_pm && !has_pm) source = 'uniform mat4 u_projection; \n' + source;
508
GL.shaderInfos[shader].ftransform = need_pm || need_mm || need_pv; // we will need to provide the fixed function stuff as attributes and uniforms
509
for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
510
// XXX To handle both regular texture mapping and cube mapping, we use vec4 for tex coordinates.
511
old = source;
512
var need_vtc = source.search(`v_texCoord${i}`) == -1;
513
source = source.replace(new RegExp(`gl_TexCoord\\[${i}\\]`, 'g'), `v_texCoord${i}`)
514
.replace(new RegExp(`gl_MultiTexCoord${i}`, 'g'), `a_texCoord${i}`);
515
if (source != old) {
516
source = `attribute vec4 a_texCoord${i}; \n${source}`;
517
if (need_vtc) {
518
source = `varying vec4 v_texCoord${i}; \n${source}`;
519
}
520
}
521
522
old = source;
523
source = source.replace(new RegExp(`gl_TextureMatrix\\[${i}\\]`, 'g'), `u_textureMatrix${i}`);
524
if (source != old) {
525
source = `uniform mat4 u_textureMatrix${i}; \n${source}`;
526
}
527
}
528
if (source.includes('gl_FrontColor')) {
529
source = 'varying vec4 v_color; \n' +
530
source.replace(/gl_FrontColor/g, 'v_color');
531
}
532
if (source.includes('gl_Color')) {
533
source = 'attribute vec4 a_color; \n' +
534
source.replace(/gl_Color/g, 'a_color');
535
}
536
if (source.includes('gl_Normal')) {
537
source = 'attribute vec3 a_normal; \n' +
538
source.replace(/gl_Normal/g, 'a_normal');
539
}
540
// fog
541
if (source.includes('gl_FogFragCoord')) {
542
source = 'varying float v_fogFragCoord; \n' +
543
source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord');
544
}
545
} else { // Fragment shader
546
for (i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
547
old = source;
548
source = source.replace(new RegExp(`gl_TexCoord\\[${i}\\]`, 'g'), `v_texCoord${i}`);
549
if (source != old) {
550
source = 'varying vec4 v_texCoord' + i + '; \n' + source;
551
}
552
}
553
if (source.includes('gl_Color')) {
554
source = 'varying vec4 v_color; \n' + source.replace(/gl_Color/g, 'v_color');
555
}
556
if (source.includes('gl_Fog.color')) {
557
source = 'uniform vec4 u_fogColor; \n' +
558
source.replace(/gl_Fog.color/g, 'u_fogColor');
559
}
560
if (source.includes('gl_Fog.end')) {
561
source = 'uniform float u_fogEnd; \n' +
562
source.replace(/gl_Fog.end/g, 'u_fogEnd');
563
}
564
if (source.includes('gl_Fog.scale')) {
565
source = 'uniform float u_fogScale; \n' +
566
source.replace(/gl_Fog.scale/g, 'u_fogScale');
567
}
568
if (source.includes('gl_Fog.density')) {
569
source = 'uniform float u_fogDensity; \n' +
570
source.replace(/gl_Fog.density/g, 'u_fogDensity');
571
}
572
if (source.includes('gl_FogFragCoord')) {
573
source = 'varying float v_fogFragCoord; \n' +
574
source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord');
575
}
576
source = ensurePrecision(source);
577
}
578
#if GL_DEBUG
579
GL.shaderSources[shader] = source;
580
dbg("glShaderSource: Output: \n" + source);
581
#endif
582
GLctx.shaderSource(GL.shaders[shader], source);
583
};
584
{{{ copySigs('glShaderSource') }}}
585
586
var orig_glCompileShader = _glCompileShader;
587
_glCompileShader = _emscripten_glCompileShader = (shader) => {
588
GLctx.compileShader(GL.shaders[shader]);
589
#if GL_DEBUG
590
if (!GLctx.getShaderParameter(GL.shaders[shader], GLctx.COMPILE_STATUS)) {
591
dbg(`Failed to compile shader: ${GLctx.getShaderInfoLog(GL.shaders[shader])}`);
592
dbg(`Info: ${JSON.stringify(GL.shaderInfos[shader])}`);
593
dbg(`Original source: ${GL.shaderOriginalSources[shader]}`);
594
dbg(`Source: ${GL.shaderSources[shader]}`);
595
throw 'Shader compilation halt';
596
}
597
#endif
598
};
599
{{{ copySigs('glCompileShader') }}}
600
601
GL.programShaders = {};
602
var orig_glAttachShader = _glAttachShader;
603
_glAttachShader = _emscripten_glAttachShader = (program, shader) => {
604
GL.programShaders[program] ||= [];
605
GL.programShaders[program].push(shader);
606
orig_glAttachShader(program, shader);
607
};
608
{{{ copySigs('glAttachShader') }}}
609
610
var orig_glDetachShader = _glDetachShader;
611
_glDetachShader = _emscripten_glDetachShader = (program, shader) => {
612
var programShader = GL.programShaders[program];
613
if (!programShader) {
614
err(`WARNING: _glDetachShader received invalid program: ${program}`);
615
return;
616
}
617
var index = programShader.indexOf(shader);
618
programShader.splice(index, 1);
619
orig_glDetachShader(program, shader);
620
};
621
{{{ copySigs('glDetachShader') }}}
622
623
var orig_glUseProgram = _glUseProgram;
624
_glUseProgram = _emscripten_glUseProgram = (program) => {
625
#if GL_DEBUG
626
if (GL.debug) {
627
dbg('[using program with shaders]');
628
if (program) {
629
GL.programShaders[program].forEach((shader) => {
630
dbg(` shader ${shader}, original source: ${GL.shaderOriginalSources[shader]}`);
631
dbg(` Source: ${GL.shaderSources[shader]}`);
632
});
633
}
634
}
635
#endif
636
if (GL.currProgram != program) {
637
GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
638
GL.currProgram = program;
639
GLImmediate.fixedFunctionProgram = 0;
640
orig_glUseProgram(program);
641
}
642
}
643
{{{ copySigs('glUseProgram') }}}
644
645
var orig_glDeleteProgram = _glDeleteProgram;
646
_glDeleteProgram = _emscripten_glDeleteProgram = (program) => {
647
orig_glDeleteProgram(program);
648
if (program == GL.currProgram) {
649
GLImmediate.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
650
GL.currProgram = 0;
651
}
652
};
653
{{{ copySigs('glDeleteProgram') }}}
654
655
// If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that.
656
var zeroUsedPrograms = {};
657
var orig_glBindAttribLocation = _glBindAttribLocation;
658
_glBindAttribLocation = _emscripten_glBindAttribLocation = (program, index, name) => {
659
if (index == 0) zeroUsedPrograms[program] = true;
660
orig_glBindAttribLocation(program, index, name);
661
};
662
{{{ copySigs('glBindAttribLocation') }}}
663
664
var orig_glLinkProgram = _glLinkProgram;
665
_glLinkProgram = _emscripten_glLinkProgram = (program) => {
666
if (!(program in zeroUsedPrograms)) {
667
GLctx.bindAttribLocation(GL.programs[program], 0, 'a_position');
668
}
669
orig_glLinkProgram(program);
670
};
671
{{{ copySigs('glLinkProgram') }}}
672
673
var orig_glBindBuffer = _glBindBuffer;
674
_glBindBuffer = _emscripten_glBindBuffer = (target, buffer) => {
675
orig_glBindBuffer(target, buffer);
676
if (target == GLctx.ARRAY_BUFFER) {
677
if (GLEmulation.currentVao) {
678
#if ASSERTIONS
679
assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao');
680
#endif
681
GLEmulation.currentVao.arrayBuffer = buffer;
682
}
683
} else if (target == GLctx.ELEMENT_ARRAY_BUFFER) {
684
if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer;
685
}
686
};
687
{{{ copySigs('glBindBuffer') }}}
688
689
var orig_glGetFloatv = _glGetFloatv;
690
_glGetFloatv = _emscripten_glGetFloatv = (pname, params) => {
691
{{{ fromPtr('params') }}}
692
if (pname == 0xBA6) { // GL_MODELVIEW_MATRIX
693
HEAPF32.set(GLImmediate.matrix[0/*m*/], {{{ getHeapOffset('params', 'float') }}});
694
} else if (pname == 0xBA7) { // GL_PROJECTION_MATRIX
695
HEAPF32.set(GLImmediate.matrix[1/*p*/], {{{ getHeapOffset('params', 'float') }}});
696
} else if (pname == 0xBA8) { // GL_TEXTURE_MATRIX
697
HEAPF32.set(GLImmediate.matrix[2/*t*/ + GLImmediate.clientActiveTexture], {{{ getHeapOffset('params', 'float') }}});
698
} else if (pname == 0xB66) { // GL_FOG_COLOR
699
HEAPF32.set(GLEmulation.fogColor, {{{ getHeapOffset('params', 'float') }}});
700
} else if (pname == 0xB63) { // GL_FOG_START
701
{{{ makeSetValue('params', '0', 'GLEmulation.fogStart', 'float') }}};
702
} else if (pname == 0xB64) { // GL_FOG_END
703
{{{ makeSetValue('params', '0', 'GLEmulation.fogEnd', 'float') }}};
704
} else if (pname == 0xB62) { // GL_FOG_DENSITY
705
{{{ makeSetValue('params', '0', 'GLEmulation.fogDensity', 'float') }}};
706
} else if (pname == 0xB65) { // GL_FOG_MODE
707
{{{ makeSetValue('params', '0', 'GLEmulation.fogMode', 'float') }}};
708
} else if (pname == 0xB53) { // GL_LIGHT_MODEL_AMBIENT
709
{{{ makeSetValue('params', '0', 'GLEmulation.lightModelAmbient[0]', 'float') }}};
710
{{{ makeSetValue('params', '4', 'GLEmulation.lightModelAmbient[1]', 'float') }}};
711
{{{ makeSetValue('params', '8', 'GLEmulation.lightModelAmbient[2]', 'float') }}};
712
{{{ makeSetValue('params', '12', 'GLEmulation.lightModelAmbient[3]', 'float') }}};
713
} else if (pname == 0xBC2) { // GL_ALPHA_TEST_REF
714
{{{ makeSetValue('params', '0', 'GLEmulation.alphaTestRef', 'float') }}};
715
} else {
716
orig_glGetFloatv(pname, params);
717
}
718
};
719
{{{ copySigs('glGetFloatv') }}}
720
721
var orig_glHint = _glHint;
722
_glHint = _emscripten_glHint = (target, mode) => {
723
if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT
724
return;
725
}
726
orig_glHint(target, mode);
727
};
728
{{{ copySigs('glHint') }}}
729
730
var orig_glEnableVertexAttribArray = _glEnableVertexAttribArray;
731
_glEnableVertexAttribArray = _emscripten_glEnableVertexAttribArray = (index) => {
732
orig_glEnableVertexAttribArray(index);
733
GLEmulation.enabledVertexAttribArrays[index] = 1;
734
if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
735
};
736
{{{ copySigs('glEnableVertexAttribArray') }}}
737
738
var orig_glDisableVertexAttribArray = _glDisableVertexAttribArray;
739
_glDisableVertexAttribArray = _emscripten_glDisableVertexAttribArray = (index) => {
740
orig_glDisableVertexAttribArray(index);
741
delete GLEmulation.enabledVertexAttribArrays[index];
742
if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
743
};
744
{{{ copySigs('glDisableVertexAttribArray') }}}
745
746
var orig_glVertexAttribPointer = _glVertexAttribPointer;
747
_glVertexAttribPointer = _emscripten_glVertexAttribPointer = (index, size, type, normalized, stride, pointer) => {
748
orig_glVertexAttribPointer(index, size, type, normalized, stride, pointer);
749
if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though
750
GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
751
}
752
};
753
{{{ copySigs('glVertexAttribPointer') }}}
754
},
755
756
getAttributeFromCapability(cap) {
757
var attrib = null;
758
switch (cap) {
759
case 0xDE1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it
760
#if ASSERTIONS
761
abort("GL_TEXTURE_2D is not a spec-defined capability for gl{Enable,Disable}ClientState.");
762
#endif
763
// Fall through:
764
case 0x8078: // GL_TEXTURE_COORD_ARRAY
765
attrib = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture; break;
766
case 0x8074: // GL_VERTEX_ARRAY
767
attrib = GLImmediate.VERTEX; break;
768
case 0x8075: // GL_NORMAL_ARRAY
769
attrib = GLImmediate.NORMAL; break;
770
case 0x8076: // GL_COLOR_ARRAY
771
attrib = GLImmediate.COLOR; break;
772
}
773
return attrib;
774
},
775
},
776
777
glDeleteObject__deps: ['glDeleteProgram', 'glDeleteShader'],
778
glDeleteObject: (id) => {
779
if (GL.programs[id]) {
780
_glDeleteProgram(id);
781
} else if (GL.shaders[id]) {
782
_glDeleteShader(id);
783
} else {
784
err(`WARNING: deleteObject received invalid id: ${id}`);
785
}
786
},
787
glDeleteObjectARB: 'glDeleteObject',
788
789
glGetObjectParameteriv__deps: ['glGetProgramiv', 'glGetShaderiv'],
790
glGetObjectParameteriv: (id, type, result) => {
791
if (GL.programs[id]) {
792
if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
793
var log = GLctx.getProgramInfoLog(GL.programs[id]);
794
if (log === null) log = '(unknown error)';
795
{{{ makeSetValue('result', '0', 'log.length', 'i32') }}};
796
return;
797
}
798
_glGetProgramiv(id, type, result);
799
} else if (GL.shaders[id]) {
800
if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
801
var log = GLctx.getShaderInfoLog(GL.shaders[id]);
802
if (log === null) log = '(unknown error)';
803
{{{ makeSetValue('result', '0', 'log.length', 'i32') }}};
804
return;
805
} else if (type == 0x8B88) { // GL_OBJECT_SHADER_SOURCE_LENGTH_ARB
806
var source = GLctx.getShaderSource(GL.shaders[id]);
807
if (source === null) return; // If an error occurs, nothing will be written to result
808
{{{ makeSetValue('result', '0', 'source.length', 'i32') }}};
809
return;
810
}
811
_glGetShaderiv(id, type, result);
812
} else {
813
err(`WARNING: getObjectParameteriv received invalid id: ${id}`);
814
}
815
},
816
glGetObjectParameterivARB: 'glGetObjectParameteriv',
817
818
glGetInfoLog__deps: ['glGetProgramInfoLog', 'glGetShaderInfoLog'],
819
glGetInfoLog: (id, maxLength, length, infoLog) => {
820
if (GL.programs[id]) {
821
_glGetProgramInfoLog(id, maxLength, length, infoLog);
822
} else if (GL.shaders[id]) {
823
_glGetShaderInfoLog(id, maxLength, length, infoLog);
824
} else {
825
err(`WARNING: glGetInfoLog received invalid id: ${id}`);
826
}
827
},
828
glGetInfoLogARB: 'glGetInfoLog',
829
830
glBindProgram: (type, id) => {
831
#if ASSERTIONS
832
assert(id == 0);
833
#endif
834
},
835
glBindProgramARB: 'glBindProgram',
836
837
glGetPointerv: (name, p) => {
838
var attribute;
839
switch (name) {
840
case 0x808E: // GL_VERTEX_ARRAY_POINTER
841
attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break;
842
case 0x8090: // GL_COLOR_ARRAY_POINTER
843
attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
844
case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
845
attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break;
846
default:
847
GL.recordError(0x500/*GL_INVALID_ENUM*/);
848
#if GL_ASSERTIONS
849
err(`GL_INVALID_ENUM in glGetPointerv: Unsupported name ${name}!`);
850
#endif
851
return;
852
}
853
{{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
854
},
855
856
// GL Immediate mode
857
858
// See comment in GLEmulation.init()
859
#if !FULL_ES2
860
$GLImmediate__postset: 'GLImmediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(() => GLImmediate.init());',
861
#endif
862
$GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'],
863
$GLImmediate: {
864
MapTreeLib: null,
865
spawnMapTreeLib: () => {
866
/**
867
* A naive implementation of a map backed by an array, and accessed by
868
* naive iteration along the array. (hashmap with only one bucket)
869
* @constructor
870
*/
871
function CNaiveListMap() {
872
var list = [];
873
874
this.insert = function CNaiveListMap_insert(key, val) {
875
if (this.contains(key|0)) return false;
876
list.push([key, val]);
877
return true;
878
};
879
880
var __contains_i;
881
this.contains = function CNaiveListMap_contains(key) {
882
for (__contains_i = 0; __contains_i < list.length; ++__contains_i) {
883
if (list[__contains_i][0] === key) return true;
884
}
885
return false;
886
};
887
888
var __get_i;
889
this.get = function CNaiveListMap_get(key) {
890
for (__get_i = 0; __get_i < list.length; ++__get_i) {
891
if (list[__get_i][0] === key) return list[__get_i][1];
892
}
893
return undefined;
894
};
895
};
896
897
/**
898
* A tree of map nodes.
899
* Uses `KeyView`s to allow descending the tree without garbage.
900
* Example: {
901
* // Create our map object.
902
* var map = new ObjTreeMap();
903
*
904
* // Grab the static keyView for the map.
905
* var keyView = map.GetStaticKeyView();
906
*
907
* // Let's make a map for:
908
* // root: <undefined>
909
* // 1: <undefined>
910
* // 2: <undefined>
911
* // 5: "Three, sir!"
912
* // 3: "Three!"
913
*
914
* // Note how we can chain together `Reset` and `Next` to
915
* // easily descend based on multiple key fragments.
916
* keyView.Reset().Next(1).Next(2).Next(5).Set("Three, sir!");
917
* keyView.Reset().Next(1).Next(2).Next(3).Set("Three!");
918
* }
919
* @constructor
920
*/
921
function CMapTree() {
922
/** @constructor */
923
function CNLNode() {
924
var map = new CNaiveListMap();
925
926
this.child = function CNLNode_child(keyFrag) {
927
if (!map.contains(keyFrag|0)) {
928
map.insert(keyFrag|0, new CNLNode());
929
}
930
return map.get(keyFrag|0);
931
};
932
933
this.value = undefined;
934
this.get = function CNLNode_get() {
935
return this.value;
936
};
937
938
this.set = function CNLNode_set(val) {
939
this.value = val;
940
};
941
}
942
943
/** @constructor */
944
function CKeyView(root) {
945
var cur;
946
947
this.reset = function CKeyView_reset() {
948
cur = root;
949
return this;
950
};
951
this.reset();
952
953
this.next = function CKeyView_next(keyFrag) {
954
cur = cur.child(keyFrag);
955
return this;
956
};
957
958
this.get = function CKeyView_get() {
959
return cur.get();
960
};
961
962
this.set = function CKeyView_set(val) {
963
cur.set(val);
964
};
965
};
966
967
var root;
968
var staticKeyView;
969
970
this.createKeyView = function CNLNode_createKeyView() {
971
return new CKeyView(root);
972
}
973
974
this.clear = function CNLNode_clear() {
975
root = new CNLNode();
976
staticKeyView = this.createKeyView();
977
};
978
this.clear();
979
980
this.getStaticKeyView = function CNLNode_getStaticKeyView() {
981
staticKeyView.reset();
982
return staticKeyView;
983
};
984
};
985
986
// Exports:
987
return {
988
create: () => new CMapTree(),
989
};
990
},
991
992
TexEnvJIT: null,
993
spawnTexEnvJIT: () => {
994
// GL defs:
995
var GL_TEXTURE0 = 0x84C0;
996
var GL_TEXTURE_1D = 0xDE0;
997
var GL_TEXTURE_2D = 0xDE1;
998
var GL_TEXTURE_3D = 0x806f;
999
var GL_TEXTURE_CUBE_MAP = 0x8513;
1000
var GL_TEXTURE_ENV = 0x2300;
1001
var GL_TEXTURE_ENV_MODE = 0x2200;
1002
var GL_TEXTURE_ENV_COLOR = 0x2201;
1003
var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
1004
var GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
1005
var GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
1006
var GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
1007
var GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
1008
var GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
1009
1010
var GL_SRC0_RGB = 0x8580;
1011
var GL_SRC1_RGB = 0x8581;
1012
var GL_SRC2_RGB = 0x8582;
1013
1014
var GL_SRC0_ALPHA = 0x8588;
1015
var GL_SRC1_ALPHA = 0x8589;
1016
var GL_SRC2_ALPHA = 0x858A;
1017
1018
var GL_OPERAND0_RGB = 0x8590;
1019
var GL_OPERAND1_RGB = 0x8591;
1020
var GL_OPERAND2_RGB = 0x8592;
1021
1022
var GL_OPERAND0_ALPHA = 0x8598;
1023
var GL_OPERAND1_ALPHA = 0x8599;
1024
var GL_OPERAND2_ALPHA = 0x859A;
1025
1026
var GL_COMBINE_RGB = 0x8571;
1027
var GL_COMBINE_ALPHA = 0x8572;
1028
1029
var GL_RGB_SCALE = 0x8573;
1030
var GL_ALPHA_SCALE = 0xD1C;
1031
1032
// env.mode
1033
var GL_ADD = 0x104;
1034
var GL_BLEND = 0xBE2;
1035
var GL_REPLACE = 0x1E01;
1036
var GL_MODULATE = 0x2100;
1037
var GL_DECAL = 0x2101;
1038
var GL_COMBINE = 0x8570;
1039
1040
// env.color/alphaCombiner
1041
//var GL_ADD = 0x104;
1042
//var GL_REPLACE = 0x1E01;
1043
//var GL_MODULATE = 0x2100;
1044
var GL_SUBTRACT = 0x84E7;
1045
var GL_INTERPOLATE = 0x8575;
1046
1047
// env.color/alphaSrc
1048
var GL_TEXTURE = 0x1702;
1049
var GL_CONSTANT = 0x8576;
1050
var GL_PRIMARY_COLOR = 0x8577;
1051
var GL_PREVIOUS = 0x8578;
1052
1053
// env.color/alphaOp
1054
var GL_SRC_COLOR = 0x300;
1055
var GL_ONE_MINUS_SRC_COLOR = 0x301;
1056
var GL_SRC_ALPHA = 0x302;
1057
var GL_ONE_MINUS_SRC_ALPHA = 0x303;
1058
1059
var GL_RGB = 0x1907;
1060
var GL_RGBA = 0x1908;
1061
1062
// Our defs:
1063
var TEXENVJIT_NAMESPACE_PREFIX = "tej_";
1064
// Not actually constant, as they can be changed between JIT passes:
1065
var TEX_UNIT_UNIFORM_PREFIX = "uTexUnit";
1066
var TEX_COORD_VARYING_PREFIX = "vTexCoord";
1067
var PRIM_COLOR_VARYING = "vPrimColor";
1068
var TEX_MATRIX_UNIFORM_PREFIX = "uTexMatrix";
1069
1070
// Static vars:
1071
var s_texUnits = null; //[];
1072
var s_activeTexture = 0;
1073
1074
var s_requiredTexUnitsForPass = [];
1075
1076
// Static funcs:
1077
function abort_noSupport(info) {
1078
abort("[TexEnvJIT] ABORT: No support: " + info);
1079
}
1080
1081
function abort_sanity(info) {
1082
abort("[TexEnvJIT] ABORT: Sanity failure: " + info);
1083
}
1084
1085
function genTexUnitSampleExpr(texUnitID) {
1086
var texUnit = s_texUnits[texUnitID];
1087
var texType = texUnit.getTexType();
1088
1089
var func = null;
1090
switch (texType) {
1091
case GL_TEXTURE_1D:
1092
func = "texture2D";
1093
break;
1094
case GL_TEXTURE_2D:
1095
func = "texture2D";
1096
break;
1097
case GL_TEXTURE_3D:
1098
return abort_noSupport("No support for 3D textures.");
1099
case GL_TEXTURE_CUBE_MAP:
1100
func = "textureCube";
1101
break;
1102
default:
1103
return abort_sanity(`Unknown texType: ${ptrToString(texType)}`);
1104
}
1105
1106
var texCoordExpr = TEX_COORD_VARYING_PREFIX + texUnitID;
1107
if (TEX_MATRIX_UNIFORM_PREFIX != null) {
1108
texCoordExpr = `(${TEX_MATRIX_UNIFORM_PREFIX}${texUnitID} * ${texCoordExpr})`;
1109
}
1110
return `${func}(${TEX_UNIT_UNIFORM_PREFIX}${texUnitID}, ${texCoordExpr}.xy)`;
1111
}
1112
1113
function getTypeFromCombineOp(op) {
1114
switch (op) {
1115
case GL_SRC_COLOR:
1116
case GL_ONE_MINUS_SRC_COLOR:
1117
return "vec3";
1118
case GL_SRC_ALPHA:
1119
case GL_ONE_MINUS_SRC_ALPHA:
1120
return "float";
1121
}
1122
1123
return abort_noSupport("Unsupported combiner op: " + ptrToString(op));
1124
}
1125
1126
function getCurTexUnit() {
1127
return s_texUnits[s_activeTexture];
1128
}
1129
1130
function genCombinerSourceExpr(texUnitID, constantExpr, previousVar,
1131
src, op)
1132
{
1133
var srcExpr = null;
1134
switch (src) {
1135
case GL_TEXTURE:
1136
srcExpr = genTexUnitSampleExpr(texUnitID);
1137
break;
1138
case GL_CONSTANT:
1139
srcExpr = constantExpr;
1140
break;
1141
case GL_PRIMARY_COLOR:
1142
srcExpr = PRIM_COLOR_VARYING;
1143
break;
1144
case GL_PREVIOUS:
1145
srcExpr = previousVar;
1146
break;
1147
default:
1148
return abort_noSupport("Unsupported combiner src: " + ptrToString(src));
1149
}
1150
1151
var expr = null;
1152
switch (op) {
1153
case GL_SRC_COLOR:
1154
expr = srcExpr + ".rgb";
1155
break;
1156
case GL_ONE_MINUS_SRC_COLOR:
1157
expr = "(vec3(1.0) - " + srcExpr + ".rgb)";
1158
break;
1159
case GL_SRC_ALPHA:
1160
expr = srcExpr + ".a";
1161
break;
1162
case GL_ONE_MINUS_SRC_ALPHA:
1163
expr = "(1.0 - " + srcExpr + ".a)";
1164
break;
1165
default:
1166
return abort_noSupport("Unsupported combiner op: " + ptrToString(op));
1167
}
1168
1169
return expr;
1170
}
1171
1172
function valToFloatLiteral(val) {
1173
if (val == Math.round(val)) return val + '.0';
1174
return val;
1175
}
1176
1177
1178
// Classes:
1179
/** @constructor */
1180
function CTexEnv() {
1181
this.mode = GL_MODULATE;
1182
this.colorCombiner = GL_MODULATE;
1183
this.alphaCombiner = GL_MODULATE;
1184
this.colorScale = 1;
1185
this.alphaScale = 1;
1186
this.envColor = [0, 0, 0, 0];
1187
1188
this.colorSrc = [
1189
GL_TEXTURE,
1190
GL_PREVIOUS,
1191
GL_CONSTANT
1192
];
1193
this.alphaSrc = [
1194
GL_TEXTURE,
1195
GL_PREVIOUS,
1196
GL_CONSTANT
1197
];
1198
this.colorOp = [
1199
GL_SRC_COLOR,
1200
GL_SRC_COLOR,
1201
GL_SRC_ALPHA
1202
];
1203
this.alphaOp = [
1204
GL_SRC_ALPHA,
1205
GL_SRC_ALPHA,
1206
GL_SRC_ALPHA
1207
];
1208
1209
// Map GLenums to small values to efficiently pack the enums to bits for tighter access.
1210
this.traverseKey = {
1211
// mode
1212
0x1E01 /* GL_REPLACE */: 0,
1213
0x2100 /* GL_MODULATE */: 1,
1214
0x104 /* GL_ADD */: 2,
1215
0xBE2 /* GL_BLEND */: 3,
1216
0x2101 /* GL_DECAL */: 4,
1217
0x8570 /* GL_COMBINE */: 5,
1218
1219
// additional color and alpha combiners
1220
0x84E7 /* GL_SUBTRACT */: 3,
1221
0x8575 /* GL_INTERPOLATE */: 4,
1222
1223
// color and alpha src
1224
0x1702 /* GL_TEXTURE */: 0,
1225
0x8576 /* GL_CONSTANT */: 1,
1226
0x8577 /* GL_PRIMARY_COLOR */: 2,
1227
0x8578 /* GL_PREVIOUS */: 3,
1228
1229
// color and alpha op
1230
0x300 /* GL_SRC_COLOR */: 0,
1231
0x301 /* GL_ONE_MINUS_SRC_COLOR */: 1,
1232
0x302 /* GL_SRC_ALPHA */: 2,
1233
0x303 /* GL_ONE_MINUS_SRC_ALPHA */: 3
1234
};
1235
1236
// The tuple (key0,key1,key2) uniquely identifies the state of the variables in CTexEnv.
1237
// -1 on key0 denotes 'the whole cached key is dirty'
1238
this.key0 = -1;
1239
this.key1 = 0;
1240
this.key2 = 0;
1241
1242
this.computeKey0 = function() {
1243
var k = this.traverseKey;
1244
var key = k[this.mode] * 1638400; // 6 distinct values.
1245
key += k[this.colorCombiner] * 327680; // 5 distinct values.
1246
key += k[this.alphaCombiner] * 65536; // 5 distinct values.
1247
// The above three fields have 6*5*5=150 distinct values -> 8 bits.
1248
key += (this.colorScale-1) * 16384; // 10 bits used.
1249
key += (this.alphaScale-1) * 4096; // 12 bits used.
1250
key += k[this.colorSrc[0]] * 1024; // 14
1251
key += k[this.colorSrc[1]] * 256; // 16
1252
key += k[this.colorSrc[2]] * 64; // 18
1253
key += k[this.alphaSrc[0]] * 16; // 20
1254
key += k[this.alphaSrc[1]] * 4; // 22
1255
key += k[this.alphaSrc[2]]; // 24 bits used total.
1256
return key;
1257
}
1258
this.computeKey1 = function() {
1259
var k = this.traverseKey;
1260
var key = k[this.colorOp[0]] * 4096;
1261
key += k[this.colorOp[1]] * 1024;
1262
key += k[this.colorOp[2]] * 256;
1263
key += k[this.alphaOp[0]] * 16;
1264
key += k[this.alphaOp[1]] * 4;
1265
key += k[this.alphaOp[2]];
1266
return key;
1267
}
1268
// TODO: remove this. The color should not be part of the key!
1269
this.computeKey2 = function() {
1270
return this.envColor[0] * 16777216 + this.envColor[1] * 65536 + this.envColor[2] * 256 + 1 + this.envColor[3];
1271
}
1272
this.recomputeKey = function() {
1273
this.key0 = this.computeKey0();
1274
this.key1 = this.computeKey1();
1275
this.key2 = this.computeKey2();
1276
}
1277
this.invalidateKey = function() {
1278
this.key0 = -1; // The key of this texture unit must be recomputed when rendering the next time.
1279
GLImmediate.currentRenderer = null; // The currently used renderer must be re-evaluated at next render.
1280
}
1281
}
1282
1283
/** @constructor */
1284
function CTexUnit() {
1285
this.env = new CTexEnv();
1286
this.enabled_tex1D = false;
1287
this.enabled_tex2D = false;
1288
this.enabled_tex3D = false;
1289
this.enabled_texCube = false;
1290
this.texTypesEnabled = 0; // A bitfield combination of the four flags above, used for fast access to operations.
1291
1292
this.traverseState = function CTexUnit_traverseState(keyView) {
1293
if (this.texTypesEnabled) {
1294
if (this.env.key0 == -1) {
1295
this.env.recomputeKey();
1296
}
1297
keyView.next(this.texTypesEnabled | (this.env.key0 << 4));
1298
keyView.next(this.env.key1);
1299
keyView.next(this.env.key2);
1300
} else {
1301
// For correctness, must traverse a zero value, theoretically a subsequent integer key could collide with this value otherwise.
1302
keyView.next(0);
1303
}
1304
};
1305
};
1306
1307
// Class impls:
1308
CTexUnit.prototype.enabled = function CTexUnit_enabled() {
1309
return this.texTypesEnabled;
1310
}
1311
1312
CTexUnit.prototype.genPassLines = function CTexUnit_genPassLines(passOutputVar, passInputVar, texUnitID) {
1313
if (!this.enabled()) {
1314
return ["vec4 " + passOutputVar + " = " + passInputVar + ";"];
1315
}
1316
var lines = this.env.genPassLines(passOutputVar, passInputVar, texUnitID).join('\n');
1317
1318
var texLoadLines = '';
1319
var texLoadRegex = /(texture.*?\(.*?\))/g;
1320
var loadCounter = 0;
1321
var load;
1322
1323
// As an optimization, merge duplicate identical texture loads to one var.
1324
while (load = texLoadRegex.exec(lines)) {
1325
var texLoadExpr = load[1];
1326
var secondOccurrence = lines.slice(load.index+1).indexOf(texLoadExpr);
1327
if (secondOccurrence != -1) { // And also has a second occurrence of same load expression..
1328
// Create new var to store the common load.
1329
var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
1330
var texLoadVar = prefix + 'texload' + loadCounter++;
1331
var texLoadLine = 'vec4 ' + texLoadVar + ' = ' + texLoadExpr + ';\n';
1332
texLoadLines += texLoadLine + '\n'; // Store the generated texture load statements in a temp string to not confuse regex search in progress.
1333
lines = lines.split(texLoadExpr).join(texLoadVar);
1334
// Reset regex search, since we modified the string.
1335
texLoadRegex = /(texture.*\(.*\))/g;
1336
}
1337
}
1338
return [texLoadLines + lines];
1339
}
1340
1341
CTexUnit.prototype.getTexType = function CTexUnit_getTexType() {
1342
if (this.enabled_texCube) {
1343
return GL_TEXTURE_CUBE_MAP;
1344
} else if (this.enabled_tex3D) {
1345
return GL_TEXTURE_3D;
1346
} else if (this.enabled_tex2D) {
1347
return GL_TEXTURE_2D;
1348
} else if (this.enabled_tex1D) {
1349
return GL_TEXTURE_1D;
1350
}
1351
return 0;
1352
}
1353
1354
CTexEnv.prototype.genPassLines = function CTexEnv_genPassLines(passOutputVar, passInputVar, texUnitID) {
1355
switch (this.mode) {
1356
case GL_REPLACE: {
1357
/* RGB:
1358
* Cv = Cs
1359
* Av = Ap // Note how this is different, and that we'll
1360
* need to track the bound texture internalFormat
1361
* to get this right.
1362
*
1363
* RGBA:
1364
* Cv = Cs
1365
* Av = As
1366
*/
1367
return [
1368
"vec4 " + passOutputVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
1369
];
1370
}
1371
case GL_ADD: {
1372
/* RGBA:
1373
* Cv = Cp + Cs
1374
* Av = ApAs
1375
*/
1376
var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
1377
var texVar = prefix + "tex";
1378
var colorVar = prefix + "color";
1379
var alphaVar = prefix + "alpha";
1380
1381
return [
1382
"vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
1383
"vec3 " + colorVar + " = " + passInputVar + ".rgb + " + texVar + ".rgb;",
1384
"float " + alphaVar + " = " + passInputVar + ".a * " + texVar + ".a;",
1385
"vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
1386
];
1387
}
1388
case GL_MODULATE: {
1389
/* RGBA:
1390
* Cv = CpCs
1391
* Av = ApAs
1392
*/
1393
var line = [
1394
"vec4 " + passOutputVar,
1395
" = ",
1396
passInputVar,
1397
" * ",
1398
genTexUnitSampleExpr(texUnitID),
1399
";",
1400
];
1401
return [line.join("")];
1402
}
1403
case GL_DECAL: {
1404
/* RGBA:
1405
* Cv = Cp(1 - As) + CsAs
1406
* Av = Ap
1407
*/
1408
var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
1409
var texVar = prefix + "tex";
1410
var colorVar = prefix + "color";
1411
var alphaVar = prefix + "alpha";
1412
1413
return [
1414
"vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
1415
[
1416
"vec3 " + colorVar + " = ",
1417
passInputVar + ".rgb * (1.0 - " + texVar + ".a)",
1418
" + ",
1419
texVar + ".rgb * " + texVar + ".a",
1420
";"
1421
].join(""),
1422
"float " + alphaVar + " = " + passInputVar + ".a;",
1423
"vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
1424
];
1425
}
1426
case GL_BLEND: {
1427
/* RGBA:
1428
* Cv = Cp(1 - Cs) + CcCs
1429
* Av = As
1430
*/
1431
var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
1432
var texVar = prefix + "tex";
1433
var colorVar = prefix + "color";
1434
var alphaVar = prefix + "alpha";
1435
1436
return [
1437
"vec4 " + texVar + " = " + genTexUnitSampleExpr(texUnitID) + ";",
1438
[
1439
"vec3 " + colorVar + " = ",
1440
passInputVar + ".rgb * (1.0 - " + texVar + ".rgb)",
1441
" + ",
1442
PRIM_COLOR_VARYING + ".rgb * " + texVar + ".rgb",
1443
";"
1444
].join(""),
1445
"float " + alphaVar + " = " + texVar + ".a;",
1446
"vec4 " + passOutputVar + " = vec4(" + colorVar + ", " + alphaVar + ");",
1447
];
1448
}
1449
case GL_COMBINE: {
1450
var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + texUnitID + "_";
1451
var colorVar = prefix + "color";
1452
var alphaVar = prefix + "alpha";
1453
var colorLines = this.genCombinerLines(true, colorVar,
1454
passInputVar, texUnitID,
1455
this.colorCombiner, this.colorSrc, this.colorOp);
1456
var alphaLines = this.genCombinerLines(false, alphaVar,
1457
passInputVar, texUnitID,
1458
this.alphaCombiner, this.alphaSrc, this.alphaOp);
1459
1460
// Generate scale, but avoid generating an identity op that multiplies by one.
1461
var scaledColor = (this.colorScale == 1) ? colorVar : (colorVar + " * " + valToFloatLiteral(this.colorScale));
1462
var scaledAlpha = (this.alphaScale == 1) ? alphaVar : (alphaVar + " * " + valToFloatLiteral(this.alphaScale));
1463
1464
var line = [
1465
"vec4 " + passOutputVar,
1466
" = ",
1467
"vec4(",
1468
scaledColor,
1469
", ",
1470
scaledAlpha,
1471
")",
1472
";",
1473
].join("");
1474
return [].concat(colorLines, alphaLines, [line]);
1475
}
1476
}
1477
1478
return abort_noSupport("Unsupported TexEnv mode: " + ptrToString(this.mode));
1479
}
1480
1481
CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar,
1482
passInputVar, texUnitID,
1483
combiner, srcArr, opArr)
1484
{
1485
var argsNeeded = null;
1486
switch (combiner) {
1487
case GL_REPLACE:
1488
argsNeeded = 1;
1489
break;
1490
1491
case GL_MODULATE:
1492
case GL_ADD:
1493
case GL_SUBTRACT:
1494
argsNeeded = 2;
1495
break;
1496
1497
case GL_INTERPOLATE:
1498
argsNeeded = 3;
1499
break;
1500
1501
default:
1502
return abort_noSupport("Unsupported combiner: " + ptrToString(combiner));
1503
}
1504
1505
var constantExpr = [
1506
"vec4(",
1507
valToFloatLiteral(this.envColor[0]),
1508
", ",
1509
valToFloatLiteral(this.envColor[1]),
1510
", ",
1511
valToFloatLiteral(this.envColor[2]),
1512
", ",
1513
valToFloatLiteral(this.envColor[3]),
1514
")",
1515
].join("");
1516
var src0Expr = (argsNeeded >= 1) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[0], opArr[0])
1517
: null;
1518
var src1Expr = (argsNeeded >= 2) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[1], opArr[1])
1519
: null;
1520
var src2Expr = (argsNeeded >= 3) ? genCombinerSourceExpr(texUnitID, constantExpr, passInputVar, srcArr[2], opArr[2])
1521
: null;
1522
1523
var outputType = isColor ? "vec3" : "float";
1524
var lines = null;
1525
switch (combiner) {
1526
case GL_REPLACE: {
1527
lines = [`${outputType} ${outputVar} = ${src0Expr};`]
1528
break;
1529
}
1530
case GL_MODULATE: {
1531
lines = [`${outputType} ${outputVar} = ${src0Expr} * ${src1Expr};`];
1532
break;
1533
}
1534
case GL_ADD: {
1535
lines = [`${outputType} ${outputVar} = ${src0Expr} + ${src1Expr};`]
1536
break;
1537
}
1538
case GL_SUBTRACT: {
1539
lines = [`${outputType} ${outputVar} = ${src0Expr} - ${src1Expr};`]
1540
break;
1541
}
1542
case GL_INTERPOLATE: {
1543
var prefix = `${TEXENVJIT_NAMESPACE_PREFIX}env${texUnitID}_`;
1544
var arg2Var = `${prefix}colorSrc2`;
1545
var arg2Type = getTypeFromCombineOp(this.colorOp[2]);
1546
1547
lines = [
1548
`${arg2Type} ${arg2Var} = ${src2Expr};`,
1549
`${outputType} ${outputVar} = ${src0Expr} * ${arg2Var} + ${src1Expr} * (1.0 - ${arg2Var});`,
1550
];
1551
break;
1552
}
1553
1554
default:
1555
return abort_sanity("Unmatched TexEnv.colorCombiner?");
1556
}
1557
1558
return lines;
1559
}
1560
1561
return {
1562
// Exports:
1563
init: (gl, specifiedMaxTextureImageUnits) => {
1564
var maxTexUnits = 0;
1565
if (specifiedMaxTextureImageUnits) {
1566
maxTexUnits = specifiedMaxTextureImageUnits;
1567
} else if (gl) {
1568
maxTexUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
1569
}
1570
#if ASSERTIONS
1571
assert(maxTexUnits > 0);
1572
#endif
1573
s_texUnits = [];
1574
for (var i = 0; i < maxTexUnits; i++) {
1575
s_texUnits.push(new CTexUnit());
1576
}
1577
},
1578
1579
setGLSLVars: (uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix) => {
1580
TEX_UNIT_UNIFORM_PREFIX = uTexUnitPrefix;
1581
TEX_COORD_VARYING_PREFIX = vTexCoordPrefix;
1582
PRIM_COLOR_VARYING = vPrimColor;
1583
TEX_MATRIX_UNIFORM_PREFIX = uTexMatrixPrefix;
1584
},
1585
1586
genAllPassLines: (resultDest, indentSize = 0) => {
1587
s_requiredTexUnitsForPass.length = 0; // Clear the list.
1588
var lines = [];
1589
var lastPassVar = PRIM_COLOR_VARYING;
1590
for (var i = 0; i < s_texUnits.length; i++) {
1591
if (!s_texUnits[i].enabled()) continue;
1592
1593
s_requiredTexUnitsForPass.push(i);
1594
1595
var prefix = TEXENVJIT_NAMESPACE_PREFIX + 'env' + i + "_";
1596
var passOutputVar = prefix + "result";
1597
1598
var newLines = s_texUnits[i].genPassLines(passOutputVar, lastPassVar, i);
1599
lines = lines.concat(newLines, [""]);
1600
1601
lastPassVar = passOutputVar;
1602
}
1603
lines.push(resultDest + " = " + lastPassVar + ";");
1604
1605
var indent = "";
1606
for (var i = 0; i < indentSize; i++) indent += " ";
1607
1608
var output = indent + lines.join("\n" + indent);
1609
1610
return output;
1611
},
1612
1613
getUsedTexUnitList: () => s_requiredTexUnitsForPass,
1614
1615
getActiveTexture: () => s_activeTexture,
1616
1617
traverseState: (keyView) => {
1618
for (var texUnit of s_texUnits) {
1619
texUnit.traverseState(keyView);
1620
}
1621
},
1622
1623
getTexUnitType: (texUnitID) => {
1624
#if ASSERTIONS
1625
assert(texUnitID >= 0 &&
1626
texUnitID < s_texUnits.length);
1627
#endif
1628
return s_texUnits[texUnitID].getTexType();
1629
},
1630
1631
// Hooks:
1632
hook_activeTexture: (texture) => {
1633
s_activeTexture = texture - GL_TEXTURE0;
1634
// Check if the current matrix mode is GL_TEXTURE.
1635
if (GLImmediate.currentMatrix >= 2) {
1636
// Switch to the corresponding texture matrix stack.
1637
GLImmediate.currentMatrix = 2 + s_activeTexture;
1638
}
1639
},
1640
1641
hook_enable: (cap) => {
1642
var cur = getCurTexUnit();
1643
switch (cap) {
1644
case GL_TEXTURE_1D:
1645
if (!cur.enabled_tex1D) {
1646
GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again.
1647
cur.enabled_tex1D = true;
1648
cur.texTypesEnabled |= 1;
1649
}
1650
break;
1651
case GL_TEXTURE_2D:
1652
if (!cur.enabled_tex2D) {
1653
GLImmediate.currentRenderer = null;
1654
cur.enabled_tex2D = true;
1655
cur.texTypesEnabled |= 2;
1656
}
1657
break;
1658
case GL_TEXTURE_3D:
1659
if (!cur.enabled_tex3D) {
1660
GLImmediate.currentRenderer = null;
1661
cur.enabled_tex3D = true;
1662
cur.texTypesEnabled |= 4;
1663
}
1664
break;
1665
case GL_TEXTURE_CUBE_MAP:
1666
if (!cur.enabled_texCube) {
1667
GLImmediate.currentRenderer = null;
1668
cur.enabled_texCube = true;
1669
cur.texTypesEnabled |= 8;
1670
}
1671
break;
1672
}
1673
},
1674
1675
hook_disable: (cap) => {
1676
var cur = getCurTexUnit();
1677
switch (cap) {
1678
case GL_TEXTURE_1D:
1679
if (cur.enabled_tex1D) {
1680
GLImmediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again.
1681
cur.enabled_tex1D = false;
1682
cur.texTypesEnabled &= ~1;
1683
}
1684
break;
1685
case GL_TEXTURE_2D:
1686
if (cur.enabled_tex2D) {
1687
GLImmediate.currentRenderer = null;
1688
cur.enabled_tex2D = false;
1689
cur.texTypesEnabled &= ~2;
1690
}
1691
break;
1692
case GL_TEXTURE_3D:
1693
if (cur.enabled_tex3D) {
1694
GLImmediate.currentRenderer = null;
1695
cur.enabled_tex3D = false;
1696
cur.texTypesEnabled &= ~4;
1697
}
1698
break;
1699
case GL_TEXTURE_CUBE_MAP:
1700
if (cur.enabled_texCube) {
1701
GLImmediate.currentRenderer = null;
1702
cur.enabled_texCube = false;
1703
cur.texTypesEnabled &= ~8;
1704
}
1705
break;
1706
}
1707
},
1708
1709
hook_texEnvf(target, pname, param) {
1710
if (target != GL_TEXTURE_ENV)
1711
return;
1712
1713
var env = getCurTexUnit().env;
1714
switch (pname) {
1715
case GL_RGB_SCALE:
1716
if (env.colorScale != param) {
1717
env.invalidateKey(); // We changed FFP emulation renderer state.
1718
env.colorScale = param;
1719
}
1720
break;
1721
case GL_ALPHA_SCALE:
1722
if (env.alphaScale != param) {
1723
env.invalidateKey();
1724
env.alphaScale = param;
1725
}
1726
break;
1727
1728
default:
1729
err('WARNING: Unhandled `pname` in call to `glTexEnvf`.');
1730
}
1731
},
1732
1733
hook_texEnvi(target, pname, param) {
1734
if (target != GL_TEXTURE_ENV)
1735
return;
1736
1737
var env = getCurTexUnit().env;
1738
switch (pname) {
1739
case GL_TEXTURE_ENV_MODE:
1740
if (env.mode != param) {
1741
env.invalidateKey(); // We changed FFP emulation renderer state.
1742
env.mode = param;
1743
}
1744
break;
1745
1746
case GL_COMBINE_RGB:
1747
if (env.colorCombiner != param) {
1748
env.invalidateKey();
1749
env.colorCombiner = param;
1750
}
1751
break;
1752
case GL_COMBINE_ALPHA:
1753
if (env.alphaCombiner != param) {
1754
env.invalidateKey();
1755
env.alphaCombiner = param;
1756
}
1757
break;
1758
1759
case GL_SRC0_RGB:
1760
if (env.colorSrc[0] != param) {
1761
env.invalidateKey();
1762
env.colorSrc[0] = param;
1763
}
1764
break;
1765
case GL_SRC1_RGB:
1766
if (env.colorSrc[1] != param) {
1767
env.invalidateKey();
1768
env.colorSrc[1] = param;
1769
}
1770
break;
1771
case GL_SRC2_RGB:
1772
if (env.colorSrc[2] != param) {
1773
env.invalidateKey();
1774
env.colorSrc[2] = param;
1775
}
1776
break;
1777
1778
case GL_SRC0_ALPHA:
1779
if (env.alphaSrc[0] != param) {
1780
env.invalidateKey();
1781
env.alphaSrc[0] = param;
1782
}
1783
break;
1784
case GL_SRC1_ALPHA:
1785
if (env.alphaSrc[1] != param) {
1786
env.invalidateKey();
1787
env.alphaSrc[1] = param;
1788
}
1789
break;
1790
case GL_SRC2_ALPHA:
1791
if (env.alphaSrc[2] != param) {
1792
env.invalidateKey();
1793
env.alphaSrc[2] = param;
1794
}
1795
break;
1796
1797
case GL_OPERAND0_RGB:
1798
if (env.colorOp[0] != param) {
1799
env.invalidateKey();
1800
env.colorOp[0] = param;
1801
}
1802
break;
1803
case GL_OPERAND1_RGB:
1804
if (env.colorOp[1] != param) {
1805
env.invalidateKey();
1806
env.colorOp[1] = param;
1807
}
1808
break;
1809
case GL_OPERAND2_RGB:
1810
if (env.colorOp[2] != param) {
1811
env.invalidateKey();
1812
env.colorOp[2] = param;
1813
}
1814
break;
1815
1816
case GL_OPERAND0_ALPHA:
1817
if (env.alphaOp[0] != param) {
1818
env.invalidateKey();
1819
env.alphaOp[0] = param;
1820
}
1821
break;
1822
case GL_OPERAND1_ALPHA:
1823
if (env.alphaOp[1] != param) {
1824
env.invalidateKey();
1825
env.alphaOp[1] = param;
1826
}
1827
break;
1828
case GL_OPERAND2_ALPHA:
1829
if (env.alphaOp[2] != param) {
1830
env.invalidateKey();
1831
env.alphaOp[2] = param;
1832
}
1833
break;
1834
1835
case GL_RGB_SCALE:
1836
if (env.colorScale != param) {
1837
env.invalidateKey();
1838
env.colorScale = param;
1839
}
1840
break;
1841
case GL_ALPHA_SCALE:
1842
if (env.alphaScale != param) {
1843
env.invalidateKey();
1844
env.alphaScale = param;
1845
}
1846
break;
1847
1848
default:
1849
err('WARNING: Unhandled `pname` in call to `glTexEnvi`.');
1850
}
1851
},
1852
1853
hook_texEnvfv(target, pname, params) {
1854
if (target != GL_TEXTURE_ENV) return;
1855
1856
var env = getCurTexUnit().env;
1857
switch (pname) {
1858
case GL_TEXTURE_ENV_COLOR: {
1859
for (var i = 0; i < 4; i++) {
1860
var param = {{{ makeGetValue('params', 'i*4', 'float') }}};
1861
if (env.envColor[i] != param) {
1862
env.invalidateKey(); // We changed FFP emulation renderer state.
1863
env.envColor[i] = param;
1864
}
1865
}
1866
break
1867
}
1868
default:
1869
err('WARNING: Unhandled `pname` in call to `glTexEnvfv`.');
1870
}
1871
},
1872
1873
hook_getTexEnviv(target, pname, param) {
1874
if (target != GL_TEXTURE_ENV)
1875
return;
1876
1877
var env = getCurTexUnit().env;
1878
switch (pname) {
1879
case GL_TEXTURE_ENV_MODE:
1880
{{{ makeSetValue('param', '0', 'env.mode', 'i32') }}};
1881
return;
1882
1883
case GL_TEXTURE_ENV_COLOR:
1884
{{{ makeSetValue('param', '0', 'Math.max(Math.min(env.envColor[0]*255, 255, -255))', 'i32') }}};
1885
{{{ makeSetValue('param', '1', 'Math.max(Math.min(env.envColor[1]*255, 255, -255))', 'i32') }}};
1886
{{{ makeSetValue('param', '2', 'Math.max(Math.min(env.envColor[2]*255, 255, -255))', 'i32') }}};
1887
{{{ makeSetValue('param', '3', 'Math.max(Math.min(env.envColor[3]*255, 255, -255))', 'i32') }}};
1888
return;
1889
1890
case GL_COMBINE_RGB:
1891
{{{ makeSetValue('param', '0', 'env.colorCombiner', 'i32') }}};
1892
return;
1893
1894
case GL_COMBINE_ALPHA:
1895
{{{ makeSetValue('param', '0', 'env.alphaCombiner', 'i32') }}};
1896
return;
1897
1898
case GL_SRC0_RGB:
1899
{{{ makeSetValue('param', '0', 'env.colorSrc[0]', 'i32') }}};
1900
return;
1901
1902
case GL_SRC1_RGB:
1903
{{{ makeSetValue('param', '0', 'env.colorSrc[1]', 'i32') }}};
1904
return;
1905
1906
case GL_SRC2_RGB:
1907
{{{ makeSetValue('param', '0', 'env.colorSrc[2]', 'i32') }}};
1908
return;
1909
1910
case GL_SRC0_ALPHA:
1911
{{{ makeSetValue('param', '0', 'env.alphaSrc[0]', 'i32') }}};
1912
return;
1913
1914
case GL_SRC1_ALPHA:
1915
{{{ makeSetValue('param', '0', 'env.alphaSrc[1]', 'i32') }}};
1916
return;
1917
1918
case GL_SRC2_ALPHA:
1919
{{{ makeSetValue('param', '0', 'env.alphaSrc[2]', 'i32') }}};
1920
return;
1921
1922
case GL_OPERAND0_RGB:
1923
{{{ makeSetValue('param', '0', 'env.colorOp[0]', 'i32') }}};
1924
return;
1925
1926
case GL_OPERAND1_RGB:
1927
{{{ makeSetValue('param', '0', 'env.colorOp[1]', 'i32') }}};
1928
return;
1929
1930
case GL_OPERAND2_RGB:
1931
{{{ makeSetValue('param', '0', 'env.colorOp[2]', 'i32') }}};
1932
return;
1933
1934
case GL_OPERAND0_ALPHA:
1935
{{{ makeSetValue('param', '0', 'env.alphaOp[0]', 'i32') }}};
1936
return;
1937
1938
case GL_OPERAND1_ALPHA:
1939
{{{ makeSetValue('param', '0', 'env.alphaOp[1]', 'i32') }}};
1940
return;
1941
1942
case GL_OPERAND2_ALPHA:
1943
{{{ makeSetValue('param', '0', 'env.alphaOp[2]', 'i32') }}};
1944
return;
1945
1946
case GL_RGB_SCALE:
1947
{{{ makeSetValue('param', '0', 'env.colorScale', 'i32') }}};
1948
return;
1949
1950
case GL_ALPHA_SCALE:
1951
{{{ makeSetValue('param', '0', 'env.alphaScale', 'i32') }}};
1952
return;
1953
1954
default:
1955
err('WARNING: Unhandled `pname` in call to `glGetTexEnvi`.');
1956
}
1957
},
1958
1959
hook_getTexEnvfv: (target, pname, param) => {
1960
if (target != GL_TEXTURE_ENV)
1961
return;
1962
1963
var env = getCurTexUnit().env;
1964
switch (pname) {
1965
case GL_TEXTURE_ENV_COLOR:
1966
{{{ makeSetValue('param', '0', 'env.envColor[0]', 'float') }}};
1967
{{{ makeSetValue('param', '4', 'env.envColor[1]', 'float') }}};
1968
{{{ makeSetValue('param', '8', 'env.envColor[2]', 'float') }}};
1969
{{{ makeSetValue('param', '12', 'env.envColor[3]', 'float') }}};
1970
return;
1971
}
1972
}
1973
};
1974
},
1975
1976
// Vertex and index data
1977
vertexData: null, // current vertex data. either tempData (glBegin etc.) or a view into the heap (gl*Pointer). Default view is F32
1978
vertexDataU8: null, // U8 view
1979
tempData: null,
1980
indexData: null,
1981
vertexCounter: 0,
1982
mode: -1,
1983
1984
rendererCache: null,
1985
rendererComponents: [], // small cache for calls inside glBegin/end. counts how many times the element was seen
1986
rendererComponentPointer: 0, // next place to start a glBegin/end component
1987
lastRenderer: null, // used to avoid cleaning up and re-preparing the same renderer
1988
lastArrayBuffer: null, // used in conjunction with lastRenderer
1989
lastProgram: null, // ""
1990
lastStride: -1, // ""
1991
1992
// The following data structures are used for OpenGL Immediate Mode matrix routines.
1993
matrix: [],
1994
matrixStack: [],
1995
currentMatrix: 0, // 0: modelview, 1: projection, 2+i, texture matrix i.
1996
tempMatrix: null,
1997
matricesModified: false,
1998
useTextureMatrix: false,
1999
2000
// Clientside attributes
2001
VERTEX: 0,
2002
NORMAL: 1,
2003
COLOR: 2,
2004
TEXTURE0: 3,
2005
NUM_ATTRIBUTES: -1, // Initialized in GL emulation init().
2006
MAX_TEXTURES: -1, // Initialized in GL emulation init().
2007
2008
totalEnabledClientAttributes: 0,
2009
enabledClientAttributes: [0, 0],
2010
clientAttributes: [], // raw data, including possible unneeded ones
2011
liveClientAttributes: [], // the ones actually alive in the current computation, sorted
2012
currentRenderer: null, // Caches the currently active FFP emulation renderer, so that it does not have to be re-looked up unless relevant state changes.
2013
modifiedClientAttributes: false,
2014
clientActiveTexture: 0,
2015
clientColor: null,
2016
usedTexUnitList: [],
2017
fixedFunctionProgram: null,
2018
2019
setClientAttribute(name, size, type, stride, pointer) {
2020
var attrib = GLImmediate.clientAttributes[name];
2021
if (!attrib) {
2022
for (var i = 0; i <= name; i++) { // keep flat
2023
GLImmediate.clientAttributes[i] ||= {
2024
name,
2025
size,
2026
type,
2027
stride,
2028
pointer,
2029
offset: 0
2030
};
2031
}
2032
} else {
2033
attrib.name = name;
2034
attrib.size = size;
2035
attrib.type = type;
2036
attrib.stride = stride;
2037
attrib.pointer = pointer;
2038
attrib.offset = 0;
2039
}
2040
GLImmediate.modifiedClientAttributes = true;
2041
},
2042
2043
// Renderers
2044
addRendererComponent(name, size, type) {
2045
if (!GLImmediate.rendererComponents[name]) {
2046
GLImmediate.rendererComponents[name] = 1;
2047
#if ASSERTIONS
2048
if (GLImmediate.enabledClientAttributes[name]) {
2049
warnOnce("Warning: glTexCoord used after EnableClientState for TEXTURE_COORD_ARRAY for TEXTURE0. Disabling TEXTURE_COORD_ARRAY...");
2050
}
2051
#endif
2052
GLImmediate.enabledClientAttributes[name] = true;
2053
GLImmediate.setClientAttribute(name, size, type, 0, GLImmediate.rendererComponentPointer);
2054
GLImmediate.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot];
2055
#if GL_FFP_ONLY
2056
// We can enable the correct attribute stream index immediately here, since the same attribute in each shader
2057
// will be bound to this same index.
2058
GL.enableVertexAttribArray(name);
2059
#endif
2060
} else {
2061
GLImmediate.rendererComponents[name]++;
2062
}
2063
},
2064
2065
disableBeginEndClientAttributes() {
2066
for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
2067
if (GLImmediate.rendererComponents[i]) GLImmediate.enabledClientAttributes[i] = false;
2068
}
2069
},
2070
2071
getRenderer() {
2072
// If no FFP state has changed that would have forced to re-evaluate which FFP emulation shader to use,
2073
// we have the currently used renderer in cache, and can immediately return that.
2074
if (GLImmediate.currentRenderer) {
2075
return GLImmediate.currentRenderer;
2076
}
2077
// return a renderer object given the liveClientAttributes
2078
// we maintain a cache of renderers, optimized to not generate garbage
2079
var attributes = GLImmediate.liveClientAttributes;
2080
var cacheMap = GLImmediate.rendererCache;
2081
var keyView = cacheMap.getStaticKeyView().reset();
2082
2083
// By attrib state:
2084
var enabledAttributesKey = 0;
2085
for (var attr of attributes) {
2086
enabledAttributesKey |= 1 << attr.name;
2087
}
2088
2089
// To prevent using more than 31 bits add another level to the maptree
2090
// and reset the enabledAttributesKey for the next glemulation state bits
2091
keyView.next(enabledAttributesKey);
2092
enabledAttributesKey = 0;
2093
2094
// By fog state:
2095
var fogParam = 0;
2096
if (GLEmulation.fogEnabled) {
2097
switch (GLEmulation.fogMode) {
2098
case 0x801: // GL_EXP2
2099
fogParam = 1;
2100
break;
2101
case 0x2601: // GL_LINEAR
2102
fogParam = 2;
2103
break;
2104
default: // default to GL_EXP
2105
fogParam = 3;
2106
break;
2107
}
2108
}
2109
enabledAttributesKey = (enabledAttributesKey << 2) | fogParam;
2110
2111
// By clip plane mode
2112
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
2113
enabledAttributesKey = (enabledAttributesKey << 1) | GLEmulation.clipPlaneEnabled[clipPlaneId];
2114
}
2115
2116
// By lighting mode and enabled lights
2117
enabledAttributesKey = (enabledAttributesKey << 1) | GLEmulation.lightingEnabled;
2118
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
2119
enabledAttributesKey = (enabledAttributesKey << 1) | (GLEmulation.lightingEnabled ? GLEmulation.lightEnabled[lightId] : 0);
2120
}
2121
2122
// By alpha testing mode
2123
enabledAttributesKey = (enabledAttributesKey << 3) | (GLEmulation.alphaTestEnabled ? (GLEmulation.alphaTestFunc - 0x200) : 0x7);
2124
2125
// By drawing mode:
2126
enabledAttributesKey = (enabledAttributesKey << 1) | (GLImmediate.mode == GLctx.POINTS ? 1 : 0);
2127
2128
keyView.next(enabledAttributesKey);
2129
2130
#if !GL_FFP_ONLY
2131
// By cur program:
2132
keyView.next(GL.currProgram);
2133
if (!GL.currProgram) {
2134
#endif
2135
GLImmediate.TexEnvJIT.traverseState(keyView);
2136
#if !GL_FFP_ONLY
2137
}
2138
#endif
2139
2140
// If we don't already have it, create it.
2141
var renderer = keyView.get();
2142
if (!renderer) {
2143
#if GL_DEBUG
2144
dbg(`generating renderer for ${JSON.stringify(attributes)}`);
2145
#endif
2146
renderer = GLImmediate.createRenderer();
2147
GLImmediate.currentRenderer = renderer;
2148
keyView.set(renderer);
2149
return renderer;
2150
}
2151
GLImmediate.currentRenderer = renderer; // Cache the currently used renderer, so later lookups without state changes can get this fast.
2152
return renderer;
2153
},
2154
2155
createRenderer(renderer) {
2156
var useCurrProgram = !!GL.currProgram;
2157
var hasTextures = false;
2158
for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
2159
var texAttribName = GLImmediate.TEXTURE0 + i;
2160
if (!GLImmediate.enabledClientAttributes[texAttribName])
2161
continue;
2162
2163
#if ASSERTIONS
2164
if (!useCurrProgram) {
2165
if (GLImmediate.TexEnvJIT.getTexUnitType(i) == 0) {
2166
warnOnce("GL_TEXTURE" + i + " coords are supplied, but that texture unit is disabled in the fixed-function pipeline.");
2167
}
2168
}
2169
#endif
2170
2171
hasTextures = true;
2172
}
2173
2174
/** @constructor */
2175
function Renderer() {
2176
this.init = function() {
2177
// For fixed-function shader generation.
2178
var uTexUnitPrefix = 'u_texUnit';
2179
var aTexCoordPrefix = 'a_texCoord';
2180
var vTexCoordPrefix = 'v_texCoord';
2181
var vPrimColor = 'v_color';
2182
var uTexMatrixPrefix = GLImmediate.useTextureMatrix ? 'u_textureMatrix' : null;
2183
2184
if (useCurrProgram) {
2185
if (GL.shaderInfos[GL.programShaders[GL.currProgram][0]].type == GLctx.VERTEX_SHADER) {
2186
this.vertexShader = GL.shaders[GL.programShaders[GL.currProgram][0]];
2187
this.fragmentShader = GL.shaders[GL.programShaders[GL.currProgram][1]];
2188
} else {
2189
this.vertexShader = GL.shaders[GL.programShaders[GL.currProgram][1]];
2190
this.fragmentShader = GL.shaders[GL.programShaders[GL.currProgram][0]];
2191
}
2192
this.program = GL.programs[GL.currProgram];
2193
this.usedTexUnitList = [];
2194
} else {
2195
// IMPORTANT NOTE: If you parameterize the shader source based on any runtime values
2196
// in order to create the least expensive shader possible based on the features being
2197
// used, you should also update the code in the beginning of getRenderer to make sure
2198
// that you cache the renderer based on the said parameters.
2199
if (GLEmulation.fogEnabled) {
2200
switch (GLEmulation.fogMode) {
2201
case 0x801: // GL_EXP2
2202
// fog = exp(-(gl_Fog.density * gl_FogFragCoord)^2)
2203
var fogFormula = ' float fog = exp(-u_fogDensity * u_fogDensity * ecDistance * ecDistance); \n';
2204
break;
2205
case 0x2601: // GL_LINEAR
2206
// fog = (gl_Fog.end - gl_FogFragCoord) * gl_fog.scale
2207
var fogFormula = ' float fog = (u_fogEnd - ecDistance) * u_fogScale; \n';
2208
break;
2209
default: // default to GL_EXP
2210
// fog = exp(-gl_Fog.density * gl_FogFragCoord)
2211
var fogFormula = ' float fog = exp(-u_fogDensity * ecDistance); \n';
2212
break;
2213
}
2214
}
2215
2216
GLImmediate.TexEnvJIT.setGLSLVars(uTexUnitPrefix, vTexCoordPrefix, vPrimColor, uTexMatrixPrefix);
2217
var fsTexEnvPass = GLImmediate.TexEnvJIT.genAllPassLines('gl_FragColor', 2);
2218
2219
var texUnitAttribList = '';
2220
var texUnitVaryingList = '';
2221
var texUnitUniformList = '';
2222
var vsTexCoordInits = '';
2223
this.usedTexUnitList = GLImmediate.TexEnvJIT.getUsedTexUnitList();
2224
for (var texUnit of this.usedTexUnitList) {
2225
texUnitAttribList += 'attribute vec4 ' + aTexCoordPrefix + texUnit + ';\n';
2226
texUnitVaryingList += 'varying vec4 ' + vTexCoordPrefix + texUnit + ';\n';
2227
texUnitUniformList += 'uniform sampler2D ' + uTexUnitPrefix + texUnit + ';\n';
2228
vsTexCoordInits += ' ' + vTexCoordPrefix + texUnit + ' = ' + aTexCoordPrefix + texUnit + ';\n';
2229
2230
if (GLImmediate.useTextureMatrix) {
2231
texUnitUniformList += 'uniform mat4 ' + uTexMatrixPrefix + texUnit + ';\n';
2232
}
2233
}
2234
2235
var vsFogVaryingInit = null;
2236
if (GLEmulation.fogEnabled) {
2237
vsFogVaryingInit = ' v_fogFragCoord = abs(ecPosition.z);\n';
2238
}
2239
2240
var vsPointSizeDefs = null;
2241
var vsPointSizeInit = null;
2242
if (GLImmediate.mode == GLctx.POINTS) {
2243
vsPointSizeDefs = 'uniform float u_pointSize;\n';
2244
vsPointSizeInit = ' gl_PointSize = u_pointSize;\n';
2245
}
2246
2247
var vsClipPlaneDefs = '';
2248
var vsClipPlaneInit = '';
2249
var fsClipPlaneDefs = '';
2250
var fsClipPlanePass = '';
2251
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
2252
if (GLEmulation.clipPlaneEnabled[clipPlaneId]) {
2253
vsClipPlaneDefs += 'uniform vec4 u_clipPlaneEquation' + clipPlaneId + ';';
2254
vsClipPlaneDefs += 'varying float v_clipDistance' + clipPlaneId + ';';
2255
vsClipPlaneInit += ' v_clipDistance' + clipPlaneId + ' = dot(ecPosition, u_clipPlaneEquation' + clipPlaneId + ');';
2256
fsClipPlaneDefs += 'varying float v_clipDistance' + clipPlaneId + ';';
2257
fsClipPlanePass += ' if (v_clipDistance' + clipPlaneId + ' < 0.0) discard;';
2258
}
2259
}
2260
2261
var vsLightingDefs = '';
2262
var vsLightingPass = '';
2263
if (GLEmulation.lightingEnabled) {
2264
vsLightingDefs += 'attribute vec3 a_normal;';
2265
vsLightingDefs += 'uniform mat3 u_normalMatrix;';
2266
vsLightingDefs += 'uniform vec4 u_lightModelAmbient;';
2267
vsLightingDefs += 'uniform vec4 u_materialAmbient;';
2268
vsLightingDefs += 'uniform vec4 u_materialDiffuse;';
2269
vsLightingDefs += 'uniform vec4 u_materialSpecular;';
2270
vsLightingDefs += 'uniform float u_materialShininess;';
2271
vsLightingDefs += 'uniform vec4 u_materialEmission;';
2272
2273
vsLightingPass += ' vec3 ecNormal = normalize(u_normalMatrix * a_normal);';
2274
vsLightingPass += ' v_color.w = u_materialDiffuse.w;';
2275
vsLightingPass += ' v_color.xyz = u_materialEmission.xyz;';
2276
vsLightingPass += ' v_color.xyz += u_lightModelAmbient.xyz * u_materialAmbient.xyz;';
2277
2278
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
2279
if (GLEmulation.lightEnabled[lightId]) {
2280
vsLightingDefs += 'uniform vec4 u_lightAmbient' + lightId + ';';
2281
vsLightingDefs += 'uniform vec4 u_lightDiffuse' + lightId + ';';
2282
vsLightingDefs += 'uniform vec4 u_lightSpecular' + lightId + ';';
2283
vsLightingDefs += 'uniform vec4 u_lightPosition' + lightId + ';';
2284
2285
vsLightingPass += ' {';
2286
vsLightingPass += ' vec3 lightDirection = normalize(u_lightPosition' + lightId + ').xyz;';
2287
vsLightingPass += ' vec3 halfVector = normalize(lightDirection + vec3(0,0,1));';
2288
vsLightingPass += ' vec3 ambient = u_lightAmbient' + lightId + '.xyz * u_materialAmbient.xyz;';
2289
vsLightingPass += ' float diffuseI = max(dot(ecNormal, lightDirection), 0.0);';
2290
vsLightingPass += ' float specularI = max(dot(ecNormal, halfVector), 0.0);';
2291
vsLightingPass += ' vec3 diffuse = diffuseI * u_lightDiffuse' + lightId + '.xyz * u_materialDiffuse.xyz;';
2292
vsLightingPass += ' specularI = (diffuseI > 0.0 && specularI > 0.0) ? exp(u_materialShininess * log(specularI)) : 0.0;';
2293
vsLightingPass += ' vec3 specular = specularI * u_lightSpecular' + lightId + '.xyz * u_materialSpecular.xyz;';
2294
vsLightingPass += ' v_color.xyz += ambient + diffuse + specular;';
2295
vsLightingPass += ' }';
2296
}
2297
}
2298
vsLightingPass += ' v_color = clamp(v_color, 0.0, 1.0);';
2299
}
2300
2301
var vsSource = [
2302
'attribute vec4 a_position;',
2303
'attribute vec4 a_color;',
2304
'varying vec4 v_color;',
2305
texUnitAttribList,
2306
texUnitVaryingList,
2307
(GLEmulation.fogEnabled ? 'varying float v_fogFragCoord;' : null),
2308
'uniform mat4 u_modelView;',
2309
'uniform mat4 u_projection;',
2310
vsPointSizeDefs,
2311
vsClipPlaneDefs,
2312
vsLightingDefs,
2313
'void main()',
2314
'{',
2315
' vec4 ecPosition = u_modelView * a_position;', // eye-coordinate position
2316
' gl_Position = u_projection * ecPosition;',
2317
' v_color = a_color;',
2318
vsTexCoordInits,
2319
vsFogVaryingInit,
2320
vsPointSizeInit,
2321
vsClipPlaneInit,
2322
vsLightingPass,
2323
'}',
2324
''
2325
].join('\n').replace(/\n\n+/g, '\n');
2326
2327
this.vertexShader = GLctx.createShader(GLctx.VERTEX_SHADER);
2328
GLctx.shaderSource(this.vertexShader, vsSource);
2329
GLctx.compileShader(this.vertexShader);
2330
2331
var fogHeaderIfNeeded = null;
2332
if (GLEmulation.fogEnabled) {
2333
fogHeaderIfNeeded = [
2334
'',
2335
'varying float v_fogFragCoord; ',
2336
'uniform vec4 u_fogColor; ',
2337
'uniform float u_fogEnd; ',
2338
'uniform float u_fogScale; ',
2339
'uniform float u_fogDensity; ',
2340
'float ffog(in float ecDistance) { ',
2341
fogFormula,
2342
' fog = clamp(fog, 0.0, 1.0); ',
2343
' return fog; ',
2344
'}',
2345
'',
2346
].join("\n");
2347
}
2348
2349
var fogPass = null;
2350
if (GLEmulation.fogEnabled) {
2351
fogPass = 'gl_FragColor = vec4(mix(u_fogColor.rgb, gl_FragColor.rgb, ffog(v_fogFragCoord)), gl_FragColor.a);\n';
2352
}
2353
2354
var fsAlphaTestDefs = '';
2355
var fsAlphaTestPass = '';
2356
if (GLEmulation.alphaTestEnabled) {
2357
fsAlphaTestDefs = 'uniform float u_alphaTestRef;';
2358
switch (GLEmulation.alphaTestFunc) {
2359
case 0x200: // GL_NEVER
2360
fsAlphaTestPass = 'discard;';
2361
break;
2362
case 0x201: // GL_LESS
2363
fsAlphaTestPass = 'if (!(gl_FragColor.a < u_alphaTestRef)) { discard; }';
2364
break;
2365
case 0x202: // GL_EQUAL
2366
fsAlphaTestPass = 'if (!(gl_FragColor.a == u_alphaTestRef)) { discard; }';
2367
break;
2368
case 0x203: // GL_LEQUAL
2369
fsAlphaTestPass = 'if (!(gl_FragColor.a <= u_alphaTestRef)) { discard; }';
2370
break;
2371
case 0x204: // GL_GREATER
2372
fsAlphaTestPass = 'if (!(gl_FragColor.a > u_alphaTestRef)) { discard; }';
2373
break;
2374
case 0x205: // GL_NOTEQUAL
2375
fsAlphaTestPass = 'if (!(gl_FragColor.a != u_alphaTestRef)) { discard; }';
2376
break;
2377
case 0x206: // GL_GEQUAL
2378
fsAlphaTestPass = 'if (!(gl_FragColor.a >= u_alphaTestRef)) { discard; }';
2379
break;
2380
case 0x207: // GL_ALWAYS
2381
fsAlphaTestPass = '';
2382
break;
2383
}
2384
}
2385
2386
var fsSource = [
2387
'precision mediump float;',
2388
texUnitVaryingList,
2389
texUnitUniformList,
2390
'varying vec4 v_color;',
2391
fogHeaderIfNeeded,
2392
fsClipPlaneDefs,
2393
fsAlphaTestDefs,
2394
'void main()',
2395
'{',
2396
fsClipPlanePass,
2397
fsTexEnvPass,
2398
fogPass,
2399
fsAlphaTestPass,
2400
'}',
2401
''
2402
].join("\n").replace(/\n\n+/g, '\n');
2403
2404
this.fragmentShader = GLctx.createShader(GLctx.FRAGMENT_SHADER);
2405
GLctx.shaderSource(this.fragmentShader, fsSource);
2406
GLctx.compileShader(this.fragmentShader);
2407
2408
this.program = GLctx.createProgram();
2409
GLctx.attachShader(this.program, this.vertexShader);
2410
GLctx.attachShader(this.program, this.fragmentShader);
2411
2412
// As optimization, bind all attributes to prespecified locations, so that the FFP emulation
2413
// code can submit attributes to any generated FFP shader without having to examine each shader in turn.
2414
// These prespecified locations are only assumed if GL_FFP_ONLY is specified, since user could also create their
2415
// own shaders that didn't have attributes in the same locations.
2416
GLctx.bindAttribLocation(this.program, GLImmediate.VERTEX, 'a_position');
2417
GLctx.bindAttribLocation(this.program, GLImmediate.COLOR, 'a_color');
2418
GLctx.bindAttribLocation(this.program, GLImmediate.NORMAL, 'a_normal');
2419
var maxVertexAttribs = GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS);
2420
for (var i = 0; i < GLImmediate.MAX_TEXTURES && GLImmediate.TEXTURE0 + i < maxVertexAttribs; i++) {
2421
GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, 'a_texCoord'+i);
2422
GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, aTexCoordPrefix+i);
2423
}
2424
GLctx.linkProgram(this.program);
2425
}
2426
2427
// Stores an array that remembers which matrix uniforms are up-to-date in this FFP renderer, so they don't need to be resubmitted
2428
// each time we render with this program.
2429
this.textureMatrixVersion = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
2430
2431
this.positionLocation = GLctx.getAttribLocation(this.program, 'a_position');
2432
2433
this.texCoordLocations = [];
2434
2435
for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
2436
if (!GLImmediate.enabledClientAttributes[GLImmediate.TEXTURE0 + i]) {
2437
this.texCoordLocations[i] = -1;
2438
continue;
2439
}
2440
2441
if (useCurrProgram) {
2442
this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, `a_texCoord${i}`);
2443
} else {
2444
this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, aTexCoordPrefix + i);
2445
}
2446
}
2447
this.colorLocation = GLctx.getAttribLocation(this.program, 'a_color');
2448
if (!useCurrProgram) {
2449
// Temporarily switch to the program so we can set our sampler uniforms early.
2450
var prevBoundProg = GLctx.getParameter(GLctx.CURRENT_PROGRAM);
2451
GLctx.useProgram(this.program);
2452
{
2453
for (var i = 0; i < this.usedTexUnitList.length; i++) {
2454
var texUnitID = this.usedTexUnitList[i];
2455
var texSamplerLoc = GLctx.getUniformLocation(this.program, uTexUnitPrefix + texUnitID);
2456
GLctx.uniform1i(texSamplerLoc, texUnitID);
2457
}
2458
}
2459
// The default color attribute value is not the same as the default for all other attribute streams (0,0,0,1) but (1,1,1,1),
2460
// so explicitly set it right at start.
2461
GLctx.vertexAttrib4fv(this.colorLocation, [1,1,1,1]);
2462
GLctx.useProgram(prevBoundProg);
2463
}
2464
2465
this.textureMatrixLocations = [];
2466
for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
2467
this.textureMatrixLocations[i] = GLctx.getUniformLocation(this.program, `u_textureMatrix${i}`);
2468
}
2469
this.normalLocation = GLctx.getAttribLocation(this.program, 'a_normal');
2470
2471
this.modelViewLocation = GLctx.getUniformLocation(this.program, 'u_modelView');
2472
this.projectionLocation = GLctx.getUniformLocation(this.program, 'u_projection');
2473
this.normalMatrixLocation = GLctx.getUniformLocation(this.program, 'u_normalMatrix');
2474
2475
this.hasTextures = hasTextures;
2476
this.hasNormal = GLImmediate.enabledClientAttributes[GLImmediate.NORMAL] &&
2477
GLImmediate.clientAttributes[GLImmediate.NORMAL].size > 0 &&
2478
this.normalLocation >= 0;
2479
this.hasColor = (this.colorLocation === 0) || this.colorLocation > 0;
2480
2481
this.floatType = GLctx.FLOAT; // minor optimization
2482
2483
this.fogColorLocation = GLctx.getUniformLocation(this.program, 'u_fogColor');
2484
this.fogEndLocation = GLctx.getUniformLocation(this.program, 'u_fogEnd');
2485
this.fogScaleLocation = GLctx.getUniformLocation(this.program, 'u_fogScale');
2486
this.fogDensityLocation = GLctx.getUniformLocation(this.program, 'u_fogDensity');
2487
this.hasFog = !!(this.fogColorLocation || this.fogEndLocation ||
2488
this.fogScaleLocation || this.fogDensityLocation);
2489
2490
this.pointSizeLocation = GLctx.getUniformLocation(this.program, 'u_pointSize');
2491
2492
this.hasClipPlane = false;
2493
this.clipPlaneEquationLocation = [];
2494
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
2495
this.clipPlaneEquationLocation[clipPlaneId] = GLctx.getUniformLocation(this.program, `u_clipPlaneEquation${clipPlaneId}`);
2496
this.hasClipPlane = (this.hasClipPlane || this.clipPlaneEquationLocation[clipPlaneId]);
2497
}
2498
2499
this.hasLighting = GLEmulation.lightingEnabled;
2500
this.lightModelAmbientLocation = GLctx.getUniformLocation(this.program, 'u_lightModelAmbient');
2501
this.materialAmbientLocation = GLctx.getUniformLocation(this.program, 'u_materialAmbient');
2502
this.materialDiffuseLocation = GLctx.getUniformLocation(this.program, 'u_materialDiffuse');
2503
this.materialSpecularLocation = GLctx.getUniformLocation(this.program, 'u_materialSpecular');
2504
this.materialShininessLocation = GLctx.getUniformLocation(this.program, 'u_materialShininess');
2505
this.materialEmissionLocation = GLctx.getUniformLocation(this.program, 'u_materialEmission');
2506
this.lightAmbientLocation = []
2507
this.lightDiffuseLocation = []
2508
this.lightSpecularLocation = []
2509
this.lightPositionLocation = []
2510
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
2511
this.lightAmbientLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightAmbient${lightId}`);
2512
this.lightDiffuseLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightDiffuse${lightId}`);
2513
this.lightSpecularLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightSpecular${lightId}`);
2514
this.lightPositionLocation[lightId] = GLctx.getUniformLocation(this.program, `u_lightPosition${lightId}`);
2515
}
2516
2517
this.hasAlphaTest = GLEmulation.alphaTestEnabled;
2518
this.alphaTestRefLocation = GLctx.getUniformLocation(this.program, 'u_alphaTestRef');
2519
2520
};
2521
2522
this.prepare = function() {
2523
// Calculate the array buffer
2524
var arrayBuffer;
2525
if (!GLctx.currentArrayBufferBinding) {
2526
var start = GLImmediate.firstVertex*GLImmediate.stride;
2527
var end = GLImmediate.lastVertex*GLImmediate.stride;
2528
#if ASSERTIONS
2529
assert(end <= GL.MAX_TEMP_BUFFER_SIZE, 'too much vertex data');
2530
#endif
2531
arrayBuffer = GL.getTempVertexBuffer(end);
2532
// TODO: consider using the last buffer we bound, if it was larger. downside is larger buffer, but we might avoid rebinding and preparing
2533
} else {
2534
arrayBuffer = GLctx.currentArrayBufferBinding;
2535
}
2536
2537
#if GL_UNSAFE_OPTS
2538
// If the array buffer is unchanged and the renderer as well, then we can avoid all the work here
2539
// XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you
2540
// have odd glitches
2541
var lastRenderer = GLImmediate.lastRenderer;
2542
var canSkip = this == lastRenderer &&
2543
arrayBuffer == GLImmediate.lastArrayBuffer &&
2544
(GL.currProgram || this.program) == GLImmediate.lastProgram &&
2545
GLImmediate.stride == GLImmediate.lastStride &&
2546
!GLImmediate.matricesModified;
2547
if (!canSkip && lastRenderer) lastRenderer.cleanup();
2548
#endif
2549
if (!GLctx.currentArrayBufferBinding) {
2550
// Bind the array buffer and upload data after cleaning up the previous renderer
2551
2552
if (arrayBuffer != GLImmediate.lastArrayBuffer) {
2553
GLctx.bindBuffer(GLctx.ARRAY_BUFFER, arrayBuffer);
2554
GLImmediate.lastArrayBuffer = arrayBuffer;
2555
}
2556
2557
GLctx.bufferSubData(GLctx.ARRAY_BUFFER, start, GLImmediate.vertexData.subarray(start >> 2, end >> 2));
2558
}
2559
#if GL_UNSAFE_OPTS
2560
if (canSkip) return;
2561
GLImmediate.lastRenderer = this;
2562
GLImmediate.lastProgram = GL.currProgram || this.program;
2563
GLImmediate.lastStride = GLImmediate.stride;
2564
GLImmediate.matricesModified = false;
2565
#endif
2566
2567
if (!GL.currProgram) {
2568
if (GLImmediate.fixedFunctionProgram != this.program) {
2569
GLctx.useProgram(this.program);
2570
GLImmediate.fixedFunctionProgram = this.program;
2571
}
2572
}
2573
2574
if (this.modelViewLocation && this.modelViewMatrixVersion != GLImmediate.matrixVersion[0/*m*/]) {
2575
this.modelViewMatrixVersion = GLImmediate.matrixVersion[0/*m*/];
2576
GLctx.uniformMatrix4fv(this.modelViewLocation, false, GLImmediate.matrix[0/*m*/]);
2577
2578
// set normal matrix to the upper 3x3 of the inverse transposed current modelview matrix
2579
if (GLEmulation.lightEnabled) {
2580
var tmpMVinv = GLImmediate.matrixLib.mat4.create(GLImmediate.matrix[0]);
2581
GLImmediate.matrixLib.mat4.inverse(tmpMVinv);
2582
GLImmediate.matrixLib.mat4.transpose(tmpMVinv);
2583
GLctx.uniformMatrix3fv(this.normalMatrixLocation, false, GLImmediate.matrixLib.mat4.toMat3(tmpMVinv));
2584
}
2585
}
2586
if (this.projectionLocation && this.projectionMatrixVersion != GLImmediate.matrixVersion[1/*p*/]) {
2587
this.projectionMatrixVersion = GLImmediate.matrixVersion[1/*p*/];
2588
GLctx.uniformMatrix4fv(this.projectionLocation, false, GLImmediate.matrix[1/*p*/]);
2589
}
2590
2591
var clientAttributes = GLImmediate.clientAttributes;
2592
var posAttr = clientAttributes[GLImmediate.VERTEX];
2593
2594
#if GL_ASSERTIONS
2595
GL.validateVertexAttribPointer(posAttr.size, posAttr.type, GLImmediate.stride, clientAttributes[GLImmediate.VERTEX].offset);
2596
#endif
2597
2598
#if GL_FFP_ONLY
2599
if (!GLctx.currentArrayBufferBinding) {
2600
GLctx.vertexAttribPointer(GLImmediate.VERTEX, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset);
2601
if (this.hasNormal) {
2602
var normalAttr = clientAttributes[GLImmediate.NORMAL];
2603
GLctx.vertexAttribPointer(GLImmediate.NORMAL, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset);
2604
}
2605
}
2606
#else
2607
GLctx.vertexAttribPointer(this.positionLocation, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset);
2608
GLctx.enableVertexAttribArray(this.positionLocation);
2609
if (this.hasNormal) {
2610
var normalAttr = clientAttributes[GLImmediate.NORMAL];
2611
#if GL_ASSERTIONS
2612
GL.validateVertexAttribPointer(normalAttr.size, normalAttr.type, GLImmediate.stride, normalAttr.offset);
2613
#endif
2614
GLctx.vertexAttribPointer(this.normalLocation, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset);
2615
GLctx.enableVertexAttribArray(this.normalLocation);
2616
}
2617
#endif
2618
if (this.hasTextures) {
2619
for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
2620
#if GL_FFP_ONLY
2621
if (!GLctx.currentArrayBufferBinding) {
2622
var attribLoc = GLImmediate.TEXTURE0+i;
2623
var texAttr = clientAttributes[attribLoc];
2624
if (texAttr.size) {
2625
GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset);
2626
} else {
2627
// These two might be dangerous, but let's try them.
2628
GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
2629
}
2630
}
2631
#else
2632
var attribLoc = this.texCoordLocations[i];
2633
if (attribLoc === undefined || attribLoc < 0) continue;
2634
var texAttr = clientAttributes[GLImmediate.TEXTURE0+i];
2635
2636
if (texAttr.size) {
2637
#if GL_ASSERTIONS
2638
GL.validateVertexAttribPointer(texAttr.size, texAttr.type, GLImmediate.stride, texAttr.offset);
2639
#endif
2640
GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset);
2641
GLctx.enableVertexAttribArray(attribLoc);
2642
} else {
2643
// These two might be dangerous, but let's try them.
2644
GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
2645
GLctx.disableVertexAttribArray(attribLoc);
2646
}
2647
#endif
2648
var t = 2/*t*/+i;
2649
if (this.textureMatrixLocations[i] && this.textureMatrixVersion[t] != GLImmediate.matrixVersion[t]) { // XXX might we need this even without the condition we are currently in?
2650
this.textureMatrixVersion[t] = GLImmediate.matrixVersion[t];
2651
GLctx.uniformMatrix4fv(this.textureMatrixLocations[i], false, GLImmediate.matrix[t]);
2652
}
2653
}
2654
}
2655
if (GLImmediate.enabledClientAttributes[GLImmediate.COLOR]) {
2656
var colorAttr = clientAttributes[GLImmediate.COLOR];
2657
#if GL_ASSERTIONS
2658
GL.validateVertexAttribPointer(colorAttr.size, colorAttr.type, GLImmediate.stride, colorAttr.offset);
2659
#endif
2660
#if GL_FFP_ONLY
2661
if (!GLctx.currentArrayBufferBinding) {
2662
GLctx.vertexAttribPointer(GLImmediate.COLOR, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset);
2663
}
2664
#else
2665
GLctx.vertexAttribPointer(this.colorLocation, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset);
2666
GLctx.enableVertexAttribArray(this.colorLocation);
2667
#endif
2668
}
2669
#if !GL_FFP_ONLY
2670
else if (this.hasColor) {
2671
GLctx.disableVertexAttribArray(this.colorLocation);
2672
GLctx.vertexAttrib4fv(this.colorLocation, GLImmediate.clientColor);
2673
}
2674
#endif
2675
if (this.hasFog) {
2676
if (this.fogColorLocation) GLctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor);
2677
if (this.fogEndLocation) GLctx.uniform1f(this.fogEndLocation, GLEmulation.fogEnd);
2678
if (this.fogScaleLocation) GLctx.uniform1f(this.fogScaleLocation, 1/(GLEmulation.fogEnd - GLEmulation.fogStart));
2679
if (this.fogDensityLocation) GLctx.uniform1f(this.fogDensityLocation, GLEmulation.fogDensity);
2680
}
2681
2682
if (this.hasClipPlane) {
2683
for (var clipPlaneId = 0; clipPlaneId < GLEmulation.MAX_CLIP_PLANES; clipPlaneId++) {
2684
if (this.clipPlaneEquationLocation[clipPlaneId]) GLctx.uniform4fv(this.clipPlaneEquationLocation[clipPlaneId], GLEmulation.clipPlaneEquation[clipPlaneId]);
2685
}
2686
}
2687
2688
if (this.hasLighting) {
2689
if (this.lightModelAmbientLocation) GLctx.uniform4fv(this.lightModelAmbientLocation, GLEmulation.lightModelAmbient);
2690
if (this.materialAmbientLocation) GLctx.uniform4fv(this.materialAmbientLocation, GLEmulation.materialAmbient);
2691
if (this.materialDiffuseLocation) GLctx.uniform4fv(this.materialDiffuseLocation, GLEmulation.materialDiffuse);
2692
if (this.materialSpecularLocation) GLctx.uniform4fv(this.materialSpecularLocation, GLEmulation.materialSpecular);
2693
if (this.materialShininessLocation) GLctx.uniform1f(this.materialShininessLocation, GLEmulation.materialShininess[0]);
2694
if (this.materialEmissionLocation) GLctx.uniform4fv(this.materialEmissionLocation, GLEmulation.materialEmission);
2695
for (var lightId = 0; lightId < GLEmulation.MAX_LIGHTS; lightId++) {
2696
if (this.lightAmbientLocation[lightId]) GLctx.uniform4fv(this.lightAmbientLocation[lightId], GLEmulation.lightAmbient[lightId]);
2697
if (this.lightDiffuseLocation[lightId]) GLctx.uniform4fv(this.lightDiffuseLocation[lightId], GLEmulation.lightDiffuse[lightId]);
2698
if (this.lightSpecularLocation[lightId]) GLctx.uniform4fv(this.lightSpecularLocation[lightId], GLEmulation.lightSpecular[lightId]);
2699
if (this.lightPositionLocation[lightId]) GLctx.uniform4fv(this.lightPositionLocation[lightId], GLEmulation.lightPosition[lightId]);
2700
}
2701
}
2702
2703
if (this.hasAlphaTest) {
2704
if (this.alphaTestRefLocation) GLctx.uniform1f(this.alphaTestRefLocation, GLEmulation.alphaTestRef);
2705
}
2706
2707
if (GLImmediate.mode == GLctx.POINTS) {
2708
if (this.pointSizeLocation) {
2709
GLctx.uniform1f(this.pointSizeLocation, GLEmulation.pointSize);
2710
}
2711
}
2712
};
2713
2714
this.cleanup = function() {
2715
#if !GL_FFP_ONLY
2716
GLctx.disableVertexAttribArray(this.positionLocation);
2717
if (this.hasTextures) {
2718
for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) {
2719
if (GLImmediate.enabledClientAttributes[GLImmediate.TEXTURE0+i] && this.texCoordLocations[i] >= 0) {
2720
GLctx.disableVertexAttribArray(this.texCoordLocations[i]);
2721
}
2722
}
2723
}
2724
if (this.hasColor) {
2725
GLctx.disableVertexAttribArray(this.colorLocation);
2726
}
2727
if (this.hasNormal) {
2728
GLctx.disableVertexAttribArray(this.normalLocation);
2729
}
2730
if (!GL.currProgram) {
2731
GLctx.useProgram(null);
2732
GLImmediate.fixedFunctionProgram = 0;
2733
}
2734
if (!GLctx.currentArrayBufferBinding) {
2735
GLctx.bindBuffer(GLctx.ARRAY_BUFFER, null);
2736
GLImmediate.lastArrayBuffer = null;
2737
}
2738
2739
#if GL_UNSAFE_OPTS
2740
GLImmediate.lastRenderer = null;
2741
GLImmediate.lastProgram = null;
2742
#endif
2743
GLImmediate.matricesModified = true;
2744
#endif
2745
}
2746
2747
this.init();
2748
}
2749
return new Renderer();
2750
},
2751
2752
setupFuncs() {
2753
// TexEnv stuff needs to be prepared early, so do it here.
2754
// init() is too late for -O2, since it freezes the GL functions
2755
// by that point.
2756
GLImmediate.MapTreeLib = GLImmediate.spawnMapTreeLib();
2757
GLImmediate.spawnMapTreeLib = null;
2758
2759
GLImmediate.TexEnvJIT = GLImmediate.spawnTexEnvJIT();
2760
GLImmediate.spawnTexEnvJIT = null;
2761
2762
GLImmediate.setupHooks();
2763
},
2764
2765
setupHooks() {
2766
if (!GLEmulation.hasRunInit) {
2767
GLEmulation.init();
2768
}
2769
2770
var glActiveTexture = _glActiveTexture;
2771
_glActiveTexture = _emscripten_glActiveTexture = (texture) => {
2772
GLImmediate.TexEnvJIT.hook_activeTexture(texture);
2773
glActiveTexture(texture);
2774
};
2775
2776
var glEnable = _glEnable;
2777
_glEnable = _emscripten_glEnable = (cap) => {
2778
GLImmediate.TexEnvJIT.hook_enable(cap);
2779
glEnable(cap);
2780
};
2781
2782
var glDisable = _glDisable;
2783
_glDisable = _emscripten_glDisable = (cap) => {
2784
GLImmediate.TexEnvJIT.hook_disable(cap);
2785
glDisable(cap);
2786
};
2787
2788
var glTexEnvf = (typeof _glTexEnvf != 'undefined') ? _glTexEnvf : () => {};
2789
/** @suppress {checkTypes} */
2790
_glTexEnvf = _emscripten_glTexEnvf = (target, pname, param) => {
2791
GLImmediate.TexEnvJIT.hook_texEnvf(target, pname, param);
2792
// Don't call old func, since we are the implementor.
2793
//glTexEnvf(target, pname, param);
2794
};
2795
2796
var glTexEnvi = (typeof _glTexEnvi != 'undefined') ? _glTexEnvi : () => {};
2797
/** @suppress {checkTypes} */
2798
_glTexEnvi = _emscripten_glTexEnvi = (target, pname, param) => {
2799
{{{ fromPtr('param') }}}
2800
GLImmediate.TexEnvJIT.hook_texEnvi(target, pname, param);
2801
// Don't call old func, since we are the implementor.
2802
//glTexEnvi(target, pname, param);
2803
};
2804
2805
var glTexEnvfv = (typeof _glTexEnvfv != 'undefined') ? _glTexEnvfv : () => {};
2806
/** @suppress {checkTypes} */
2807
_glTexEnvfv = _emscripten_glTexEnvfv = (target, pname, param) => {
2808
{{{ fromPtr('param') }}}
2809
GLImmediate.TexEnvJIT.hook_texEnvfv(target, pname, param);
2810
// Don't call old func, since we are the implementor.
2811
//glTexEnvfv(target, pname, param);
2812
};
2813
2814
_glGetTexEnviv = (target, pname, param) => {
2815
{{{ fromPtr('param') }}}
2816
GLImmediate.TexEnvJIT.hook_getTexEnviv(target, pname, param);
2817
};
2818
2819
_glGetTexEnvfv = (target, pname, param) => {
2820
{{{ fromPtr('param') }}}
2821
GLImmediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param);
2822
};
2823
2824
var glGetIntegerv = _glGetIntegerv;
2825
_glGetIntegerv = _emscripten_glGetIntegerv = (pname, params) => {
2826
switch (pname) {
2827
case 0x8B8D: { // GL_CURRENT_PROGRAM
2828
// Just query directly so we're working with WebGL objects.
2829
var cur = GLctx.getParameter(GLctx.CURRENT_PROGRAM);
2830
if (cur == GLImmediate.fixedFunctionProgram) {
2831
// Pretend we're not using a program.
2832
{{{ makeSetValue('params', '0', '0', 'i32') }}};
2833
return;
2834
}
2835
break;
2836
}
2837
}
2838
glGetIntegerv(pname, params);
2839
};
2840
},
2841
2842
// Main functions
2843
initted: false,
2844
init() {
2845
err('WARNING: using emscripten GL immediate mode emulation. This is very limited in what it supports');
2846
GLImmediate.initted = true;
2847
2848
if (!Browser.useWebGL) return; // a 2D canvas may be currently used TODO: make sure we are actually called in that case
2849
2850
// User can override the maximum number of texture units that we emulate. Using fewer texture units increases runtime performance
2851
// slightly, so it is advantageous to choose as small value as needed.
2852
// Limit to a maximum of 28 to not overflow the state bits used for renderer caching (31 bits = 3 attributes + 28 texture units).
2853
GLImmediate.MAX_TEXTURES = Math.min(Module['GL_MAX_TEXTURE_IMAGE_UNITS'] || GLctx.getParameter(GLctx.MAX_TEXTURE_IMAGE_UNITS), 28);
2854
2855
GLImmediate.TexEnvJIT.init(GLctx, GLImmediate.MAX_TEXTURES);
2856
2857
GLImmediate.NUM_ATTRIBUTES = 3 /*pos+normal+color attributes*/ + GLImmediate.MAX_TEXTURES;
2858
GLImmediate.clientAttributes = [];
2859
GLEmulation.enabledClientAttribIndices = [];
2860
for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
2861
GLImmediate.clientAttributes.push({});
2862
GLEmulation.enabledClientAttribIndices.push(false);
2863
}
2864
2865
// Initialize matrix library
2866
// When user sets a matrix, increment a 'version number' on the new data, and when rendering, submit
2867
// the matrices to the shader program only if they have an old version of the data.
2868
GLImmediate.matrix = [];
2869
GLImmediate.matrixStack = [];
2870
GLImmediate.matrixVersion = [];
2871
for (var i = 0; i < 2 + GLImmediate.MAX_TEXTURES; i++) { // Modelview, Projection, plus one matrix for each texture coordinate.
2872
GLImmediate.matrixStack.push([]);
2873
GLImmediate.matrixVersion.push(0);
2874
GLImmediate.matrix.push(GLImmediate.matrixLib.mat4.create());
2875
GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[i]);
2876
}
2877
2878
// Renderer cache
2879
GLImmediate.rendererCache = GLImmediate.MapTreeLib.create();
2880
2881
// Buffers for data
2882
GLImmediate.tempData = new Float32Array(GL.MAX_TEMP_BUFFER_SIZE >> 2);
2883
GLImmediate.indexData = new Uint16Array(GL.MAX_TEMP_BUFFER_SIZE >> 1);
2884
2885
GLImmediate.vertexDataU8 = new Uint8Array(GLImmediate.tempData.buffer);
2886
2887
GL.generateTempBuffers(true, GL.currentContext);
2888
2889
GLImmediate.clientColor = new Float32Array([1, 1, 1, 1]);
2890
},
2891
2892
// Prepares and analyzes client attributes.
2893
// Modifies liveClientAttributes, stride, vertexPointer, vertexCounter
2894
// count: number of elements we will draw
2895
// beginEnd: whether we are drawing the results of a begin/end block
2896
prepareClientAttributes(count, beginEnd) {
2897
// If no client attributes were modified since we were last called, do
2898
// nothing. Note that this does not work for glBegin/End, where we
2899
// generate renderer components dynamically and then disable them
2900
// ourselves, but it does help with glDrawElements/Arrays.
2901
if (!GLImmediate.modifiedClientAttributes) {
2902
#if GL_ASSERTIONS
2903
if ((GLImmediate.stride & 3) != 0) {
2904
warnOnce(`Warning: Rendering from client side vertex arrays where stride (${GLImmediate.stride}) is not a multiple of four! This is not currently supported!`);
2905
}
2906
#endif
2907
GLImmediate.vertexCounter = (GLImmediate.stride * count) / 4; // XXX assuming float
2908
return;
2909
}
2910
GLImmediate.modifiedClientAttributes = false;
2911
2912
// The role of prepareClientAttributes is to examine the set of
2913
// client-side vertex attribute buffers that user code has submitted, and
2914
// to prepare them to be uploaded to a VBO in GPU memory (since WebGL does
2915
// not support client-side rendering, i.e. rendering from vertex data in
2916
// CPU memory). User can submit vertex data generally in three different
2917
// configurations:
2918
// 1. Fully planar: all attributes are in their own separate
2919
// tightly-packed arrays in CPU memory.
2920
// 2. Fully interleaved: all attributes share a single array where data is
2921
// interleaved something like (pos,uv,normal),
2922
// (pos,uv,normal), ...
2923
// 3. Complex hybrid: Multiple separate arrays that either are sparsely
2924
// strided, and/or partially interleaves vertex
2925
// attributes.
2926
2927
// For simplicity, we support the case (2) as the fast case. For (1) and
2928
// (3), we do a memory copy of the vertex data here to prepare a
2929
// relayouted buffer that is of the structure in case (2). The reason
2930
// for this is that it allows the emulation code to get away with using
2931
// just one VBO buffer for rendering, and not have to maintain multiple
2932
// ones. Therefore cases (1) and (3) will be very slow, and case (2) is
2933
// fast.
2934
2935
// Detect which case we are in by using a quick heuristic by examining the
2936
// strides of the buffers. If all the buffers have identical stride, we
2937
// assume we have case (2), otherwise we have something more complex.
2938
var clientStartPointer = {{{ POINTER_MAX }}};
2939
var bytes = 0; // Total number of bytes taken up by a single vertex.
2940
var minStride = {{{ POINTER_MAX }}};
2941
var maxStride = 0;
2942
var attributes = GLImmediate.liveClientAttributes;
2943
attributes.length = 0;
2944
for (var i = 0; i < 3+GLImmediate.MAX_TEXTURES; i++) {
2945
if (GLImmediate.enabledClientAttributes[i]) {
2946
var attr = GLImmediate.clientAttributes[i];
2947
attributes.push(attr);
2948
clientStartPointer = Math.min(clientStartPointer, attr.pointer);
2949
attr.sizeBytes = attr.size * GL.byteSizeByType[attr.type - GL.byteSizeByTypeRoot];
2950
bytes += attr.sizeBytes;
2951
minStride = Math.min(minStride, attr.stride);
2952
maxStride = Math.max(maxStride, attr.stride);
2953
}
2954
}
2955
2956
if ((minStride != maxStride || maxStride < bytes) && !beginEnd) {
2957
// We are in cases (1) or (3): slow path, shuffle the data around into a
2958
// single interleaved vertex buffer.
2959
// The immediate-mode glBegin()/glEnd() vertex submission gets
2960
// automatically generated in appropriate layout, so never need to come
2961
// down this path if that was used.
2962
#if GL_ASSERTIONS
2963
warnOnce('Rendering from planar client-side vertex arrays. This is a very slow emulation path! Use interleaved vertex arrays for best performance.');
2964
#endif
2965
GLImmediate.restrideBuffer ||= _malloc(GL.MAX_TEMP_BUFFER_SIZE);
2966
var start = GLImmediate.restrideBuffer;
2967
bytes = 0;
2968
// calculate restrided offsets and total size
2969
for (var attr of attributes) {
2970
var size = attr.sizeBytes;
2971
if (size % 4 != 0) size += 4 - (size % 4); // align everything
2972
attr.offset = bytes;
2973
bytes += size;
2974
}
2975
// copy out the data (we need to know the stride for that, and define attr.pointer)
2976
for (var attr of attributes) {
2977
var srcStride = Math.max(attr.sizeBytes, attr.stride);
2978
if ((srcStride & 3) == 0 && (attr.sizeBytes & 3) == 0) {
2979
for (var j = 0; j < count; j++) {
2980
for (var k = 0; k < attr.sizeBytes; k+=4) { // copy in chunks of 4 bytes, our alignment makes this possible
2981
var val = {{{ makeGetValue('attr.pointer', 'j*srcStride + k', 'i32') }}};
2982
{{{ makeSetValue('start + attr.offset', 'bytes*j + k', 'val', 'i32') }}};
2983
}
2984
}
2985
} else {
2986
for (var j = 0; j < count; j++) {
2987
for (var k = 0; k < attr.sizeBytes; k++) { // source data was not aligned to multiples of 4, must copy byte by byte.
2988
HEAP8[start + attr.offset + bytes*j + k] = HEAP8[attr.pointer + j*srcStride + k];
2989
}
2990
}
2991
}
2992
attr.pointer = start + attr.offset;
2993
}
2994
GLImmediate.stride = bytes;
2995
GLImmediate.vertexPointer = start;
2996
} else {
2997
// case (2): fast path, all data is interleaved to a single vertex array so we can get away with a single VBO upload.
2998
if (GLctx.currentArrayBufferBinding) {
2999
GLImmediate.vertexPointer = 0;
3000
} else {
3001
GLImmediate.vertexPointer = clientStartPointer;
3002
}
3003
for (var attr of attributes) {
3004
attr.offset = attr.pointer - GLImmediate.vertexPointer; // Compute what will be the offset of this attribute in the VBO after we upload.
3005
}
3006
GLImmediate.stride = Math.max(maxStride, bytes);
3007
}
3008
if (!beginEnd) {
3009
#if GL_ASSERTIONS
3010
if ((GLImmediate.stride & 3) != 0) {
3011
warnOnce(`Warning: Rendering from client side vertex arrays where stride (${GLImmediate.stride}) is not a multiple of four! This is not currently supported!`);
3012
}
3013
#endif
3014
GLImmediate.vertexCounter = (GLImmediate.stride * count) / 4; // XXX assuming float
3015
}
3016
},
3017
3018
flush(numProvidedIndexes, startIndex = 0, ptr = 0) {
3019
#if ASSERTIONS
3020
assert(numProvidedIndexes >= 0 || !numProvidedIndexes);
3021
#endif
3022
var renderer = GLImmediate.getRenderer();
3023
3024
// Generate index data in a format suitable for GLES 2.0/WebGL
3025
var numVertices = 4 * GLImmediate.vertexCounter / GLImmediate.stride;
3026
if (!numVertices) return;
3027
#if ASSERTIONS
3028
assert(numVertices % 1 == 0, "`numVertices` must be an integer.");
3029
#endif
3030
var emulatedElementArrayBuffer = false;
3031
var numIndexes = 0;
3032
if (numProvidedIndexes) {
3033
numIndexes = numProvidedIndexes;
3034
if (!GLctx.currentArrayBufferBinding && GLImmediate.firstVertex > GLImmediate.lastVertex) {
3035
// Figure out the first and last vertex from the index data
3036
#if ASSERTIONS
3037
// If we are going to upload array buffer data, we need to find which range to
3038
// upload based on the indices. If they are in a buffer on the GPU, that is very
3039
// inconvenient! So if you do not have an array buffer, you should also not have
3040
// an element array buffer. But best is to use both buffers!
3041
assert(!GLctx.currentElementArrayBufferBinding);
3042
#endif
3043
for (var i = 0; i < numProvidedIndexes; i++) {
3044
var currIndex = {{{ makeGetValue('ptr', 'i*2', 'u16') }}};
3045
GLImmediate.firstVertex = Math.min(GLImmediate.firstVertex, currIndex);
3046
GLImmediate.lastVertex = Math.max(GLImmediate.lastVertex, currIndex+1);
3047
}
3048
}
3049
if (!GLctx.currentElementArrayBufferBinding) {
3050
// If no element array buffer is bound, then indices is a literal pointer to clientside data
3051
#if ASSERTIONS
3052
assert(numProvidedIndexes << 1 <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)');
3053
#endif
3054
var indexBuffer = GL.getTempIndexBuffer(numProvidedIndexes << 1);
3055
GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, indexBuffer);
3056
GLctx.bufferSubData(GLctx.ELEMENT_ARRAY_BUFFER, 0, {{{ makeHEAPView('U16', 'ptr', 'ptr + (numProvidedIndexes << 1)') }}});
3057
ptr = 0;
3058
emulatedElementArrayBuffer = true;
3059
}
3060
} else if (GLImmediate.mode > 6) { // above GL_TRIANGLE_FAN are the non-GL ES modes
3061
if (GLImmediate.mode != 7) throw 'unsupported immediate mode ' + GLImmediate.mode; // GL_QUADS
3062
// GLImmediate.firstVertex is the first vertex we want. Quad indexes are
3063
// in the pattern 0 1 2, 0 2 3, 4 5 6, 4 6 7, so we need to look at
3064
// index firstVertex * 1.5 to see it. Then since indexes are 2 bytes
3065
// each, that means 3
3066
#if ASSERTIONS
3067
assert(GLImmediate.firstVertex % 4 == 0);
3068
#endif
3069
ptr = GLImmediate.firstVertex * 3;
3070
var numQuads = numVertices / 4;
3071
numIndexes = numQuads * 6; // 0 1 2, 0 2 3 pattern
3072
#if ASSERTIONS
3073
assert(ptr + (numIndexes << 1) <= GL.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)');
3074
#endif
3075
GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.currentContext.tempQuadIndexBuffer);
3076
emulatedElementArrayBuffer = true;
3077
GLImmediate.mode = GLctx.TRIANGLES;
3078
}
3079
3080
renderer.prepare();
3081
3082
if (numIndexes) {
3083
GLctx.drawElements(GLImmediate.mode, numIndexes, GLctx.UNSIGNED_SHORT, ptr);
3084
} else {
3085
GLctx.drawArrays(GLImmediate.mode, startIndex, numVertices);
3086
}
3087
3088
if (emulatedElementArrayBuffer) {
3089
GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GLctx.currentElementArrayBufferBinding] || null);
3090
}
3091
3092
#if !GL_UNSAFE_OPTS
3093
#if !GL_FFP_ONLY
3094
renderer.cleanup();
3095
#endif
3096
#endif
3097
}
3098
},
3099
3100
$GLImmediateSetup__deps: ['$GLImmediate', () => 'GLImmediate.matrixLib = ' + read('gl-matrix.js') + ';\n'],
3101
$GLImmediateSetup: {},
3102
3103
glBegin__deps: ['$GLImmediateSetup'],
3104
glBegin: (mode) => {
3105
// Push the old state:
3106
GLImmediate.enabledClientAttributes_preBegin = GLImmediate.enabledClientAttributes;
3107
GLImmediate.enabledClientAttributes = [];
3108
3109
GLImmediate.clientAttributes_preBegin = GLImmediate.clientAttributes;
3110
GLImmediate.clientAttributes = []
3111
for (var i = 0; i < GLImmediate.clientAttributes_preBegin.length; i++) {
3112
GLImmediate.clientAttributes.push({});
3113
}
3114
3115
GLImmediate.mode = mode;
3116
GLImmediate.vertexCounter = 0;
3117
var components = GLImmediate.rendererComponents = [];
3118
for (var i = 0; i < GLImmediate.NUM_ATTRIBUTES; i++) {
3119
components[i] = 0;
3120
}
3121
GLImmediate.rendererComponentPointer = 0;
3122
GLImmediate.vertexData = GLImmediate.tempData;
3123
},
3124
3125
glEnd: () => {
3126
GLImmediate.prepareClientAttributes(GLImmediate.rendererComponents[GLImmediate.VERTEX], true);
3127
GLImmediate.firstVertex = 0;
3128
GLImmediate.lastVertex = GLImmediate.vertexCounter / (GLImmediate.stride >> 2);
3129
GLImmediate.flush();
3130
GLImmediate.disableBeginEndClientAttributes();
3131
GLImmediate.mode = -1;
3132
3133
// Pop the old state:
3134
GLImmediate.enabledClientAttributes = GLImmediate.enabledClientAttributes_preBegin;
3135
GLImmediate.clientAttributes = GLImmediate.clientAttributes_preBegin;
3136
GLImmediate.currentRenderer = null; // The set of active client attributes changed, we must re-lookup the renderer to use.
3137
GLImmediate.modifiedClientAttributes = true;
3138
},
3139
3140
glVertex2f: (x, y) => {
3141
#if ASSERTIONS
3142
assert(GLImmediate.mode >= 0); // must be in begin/end
3143
#endif
3144
GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
3145
GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
3146
GLImmediate.vertexData[GLImmediate.vertexCounter++] = 0;
3147
GLImmediate.vertexData[GLImmediate.vertexCounter++] = 1;
3148
#if ASSERTIONS
3149
assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
3150
#endif
3151
GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
3152
},
3153
3154
glVertex3f: (x, y, z) => {
3155
#if ASSERTIONS
3156
assert(GLImmediate.mode >= 0); // must be in begin/end
3157
#endif
3158
GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
3159
GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
3160
GLImmediate.vertexData[GLImmediate.vertexCounter++] = z;
3161
GLImmediate.vertexData[GLImmediate.vertexCounter++] = 1;
3162
#if ASSERTIONS
3163
assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
3164
#endif
3165
GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
3166
},
3167
3168
glVertex4f: (x, y, z, w) => {
3169
#if ASSERTIONS
3170
assert(GLImmediate.mode >= 0); // must be in begin/end
3171
#endif
3172
GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
3173
GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
3174
GLImmediate.vertexData[GLImmediate.vertexCounter++] = z;
3175
GLImmediate.vertexData[GLImmediate.vertexCounter++] = w;
3176
#if ASSERTIONS
3177
assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
3178
#endif
3179
GLImmediate.addRendererComponent(GLImmediate.VERTEX, 4, GLctx.FLOAT);
3180
},
3181
3182
glVertex2fv__deps: ['glVertex2f'],
3183
glVertex2fv: (p) => _glVertex2f({{{ makeGetValue('p', '0', 'float') }}},
3184
{{{ makeGetValue('p', '4', 'float') }}}),
3185
3186
glVertex3fv__deps: ['glVertex3f'],
3187
glVertex3fv: (p) => _glVertex3f({{{ makeGetValue('p', '0', 'float') }}},
3188
{{{ makeGetValue('p', '4', 'float') }}},
3189
{{{ makeGetValue('p', '8', 'float') }}}),
3190
3191
glVertex4fv__deps: ['glVertex4f'],
3192
glVertex4fv: (p) => _glVertex4f({{{ makeGetValue('p', '0', 'float') }}},
3193
{{{ makeGetValue('p', '4', 'float') }}},
3194
{{{ makeGetValue('p', '8', 'float') }}},
3195
{{{ makeGetValue('p', '12', 'float') }}}),
3196
3197
glVertex2i: 'glVertex2f',
3198
3199
glVertex3i: 'glVertex3f',
3200
3201
glVertex4i: 'glVertex4f',
3202
3203
glTexCoord2i: (u, v) => {
3204
#if ASSERTIONS
3205
assert(GLImmediate.mode >= 0); // must be in begin/end
3206
#endif
3207
GLImmediate.vertexData[GLImmediate.vertexCounter++] = u;
3208
GLImmediate.vertexData[GLImmediate.vertexCounter++] = v;
3209
GLImmediate.addRendererComponent(GLImmediate.TEXTURE0, 2, GLctx.FLOAT);
3210
},
3211
glTexCoord2f: 'glTexCoord2i',
3212
3213
glTexCoord2fv__deps: ['glTexCoord2i'],
3214
glTexCoord2fv: (v) =>
3215
_glTexCoord2i({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}}),
3216
3217
glTexCoord4f: () => { throw 'glTexCoord4f: TODO' },
3218
3219
glColor4f: (r, g, b, a) => {
3220
r = Math.max(Math.min(r, 1), 0);
3221
g = Math.max(Math.min(g, 1), 0);
3222
b = Math.max(Math.min(b, 1), 0);
3223
a = Math.max(Math.min(a, 1), 0);
3224
3225
// TODO: make ub the default, not f, save a few mathops
3226
if (GLImmediate.mode >= 0) {
3227
var start = GLImmediate.vertexCounter << 2;
3228
GLImmediate.vertexDataU8[start + 0] = r * 255;
3229
GLImmediate.vertexDataU8[start + 1] = g * 255;
3230
GLImmediate.vertexDataU8[start + 2] = b * 255;
3231
GLImmediate.vertexDataU8[start + 3] = a * 255;
3232
GLImmediate.vertexCounter++;
3233
GLImmediate.addRendererComponent(GLImmediate.COLOR, 4, GLctx.UNSIGNED_BYTE);
3234
} else {
3235
GLImmediate.clientColor[0] = r;
3236
GLImmediate.clientColor[1] = g;
3237
GLImmediate.clientColor[2] = b;
3238
GLImmediate.clientColor[3] = a;
3239
#if GL_FFP_ONLY
3240
GLctx.vertexAttrib4fv(GLImmediate.COLOR, GLImmediate.clientColor);
3241
#endif
3242
}
3243
},
3244
3245
glColor4d: 'glColor4f',
3246
3247
glColor4ub__deps: ['glColor4f'],
3248
glColor4ub: (r, g, b, a) => _glColor4f((r&255)/255, (g&255)/255, (b&255)/255, (a&255)/255),
3249
3250
glColor4us__deps: ['glColor4f'],
3251
glColor4us: (r, g, b, a) => _glColor4f((r&65535)/65535, (g&65535)/65535, (b&65535)/65535, (a&65535)/65535),
3252
3253
glColor4ui__deps: ['glColor4f'],
3254
glColor4ui: (r, g, b, a) => _glColor4f((r>>>0)/4294967295, (g>>>0)/4294967295, (b>>>0)/4294967295, (a>>>0)/4294967295),
3255
3256
glColor3f__deps: ['glColor4f'],
3257
glColor3f: (r, g, b) => _glColor4f(r, g, b, 1),
3258
3259
glColor3d: 'glColor3f',
3260
3261
glColor3ub__deps: ['glColor4ub'],
3262
glColor3ub: (r, g, b) => _glColor4ub(r, g, b, 255),
3263
3264
glColor3us__deps: ['glColor4us'],
3265
glColor3us: (r, g, b) => _glColor4us(r, g, b, 65535),
3266
3267
glColor3ui__deps: ['glColor4ui'],
3268
glColor3ui: (r, g, b) => _glColor4ui(r, g, b, 4294967295),
3269
3270
glColor3ubv__deps: ['glColor3ub'],
3271
glColor3ubv: (p) => _glColor3ub({{{ makeGetValue('p', '0', 'i8') }}},
3272
{{{ makeGetValue('p', '1', 'i8') }}},
3273
{{{ makeGetValue('p', '2', 'i8') }}}),
3274
3275
glColor3usv__deps: ['glColor3us'],
3276
glColor3usv: (p) => _glColor3us({{{ makeGetValue('p', '0', 'i16') }}},
3277
{{{ makeGetValue('p', '2', 'i16') }}},
3278
{{{ makeGetValue('p', '4', 'i16') }}}),
3279
3280
glColor3uiv__deps: ['glColor3ui'],
3281
glColor3uiv: (p) => _glColor3ui({{{ makeGetValue('p', '0', 'i32') }}},
3282
{{{ makeGetValue('p', '4', 'i32') }}},
3283
{{{ makeGetValue('p', '8', 'i32') }}}),
3284
3285
glColor3fv__deps: ['glColor3f'],
3286
glColor3fv: (p) => _glColor3f({{{ makeGetValue('p', '0', 'float') }}},
3287
{{{ makeGetValue('p', '4', 'float') }}},
3288
{{{ makeGetValue('p', '8', 'float') }}}),
3289
3290
glColor4fv__deps: ['glColor4f'],
3291
glColor4fv: (p) => _glColor4f({{{ makeGetValue('p', '0', 'float') }}},
3292
{{{ makeGetValue('p', '4', 'float') }}},
3293
{{{ makeGetValue('p', '8', 'float') }}},
3294
{{{ makeGetValue('p', '12', 'float') }}}),
3295
3296
glColor4ubv__deps: ['glColor4ub'],
3297
glColor4ubv: (p) => _glColor4ub({{{ makeGetValue('p', '0', 'i8') }}},
3298
{{{ makeGetValue('p', '1', 'i8') }}},
3299
{{{ makeGetValue('p', '2', 'i8') }}},
3300
{{{ makeGetValue('p', '3', 'i8') }}}),
3301
3302
glFogf: (pname, param) => { // partial support, TODO
3303
switch (pname) {
3304
case 0xB63: // GL_FOG_START
3305
GLEmulation.fogStart = param; break;
3306
case 0xB64: // GL_FOG_END
3307
GLEmulation.fogEnd = param; break;
3308
case 0xB62: // GL_FOG_DENSITY
3309
GLEmulation.fogDensity = param; break;
3310
case 0xB65: // GL_FOG_MODE
3311
switch (param) {
3312
case 0x801: // GL_EXP2
3313
case 0x2601: // GL_LINEAR
3314
if (GLEmulation.fogMode != param) {
3315
GLImmediate.currentRenderer = null; // Fog mode is part of the FFP shader state, we must re-lookup the renderer to use.
3316
GLEmulation.fogMode = param;
3317
}
3318
break;
3319
default: // default to GL_EXP
3320
if (GLEmulation.fogMode != 0x800 /* GL_EXP */) {
3321
GLImmediate.currentRenderer = null; // Fog mode is part of the FFP shader state, we must re-lookup the renderer to use.
3322
GLEmulation.fogMode = 0x800 /* GL_EXP */;
3323
}
3324
break;
3325
}
3326
break;
3327
}
3328
},
3329
glFogi__deps: ['glFogf'],
3330
glFogi: (pname, param) => {
3331
return _glFogf(pname, param);
3332
},
3333
glFogfv__deps: ['glFogf'],
3334
glFogfv: (pname, param) => { // partial support, TODO
3335
switch (pname) {
3336
case 0xB66: // GL_FOG_COLOR
3337
GLEmulation.fogColor[0] = {{{ makeGetValue('param', '0', 'float') }}};
3338
GLEmulation.fogColor[1] = {{{ makeGetValue('param', '4', 'float') }}};
3339
GLEmulation.fogColor[2] = {{{ makeGetValue('param', '8', 'float') }}};
3340
GLEmulation.fogColor[3] = {{{ makeGetValue('param', '12', 'float') }}};
3341
break;
3342
case 0xB63: // GL_FOG_START
3343
case 0xB64: // GL_FOG_END
3344
_glFogf(pname, {{{ makeGetValue('param', '0', 'float') }}}); break;
3345
}
3346
},
3347
glFogiv__deps: ['glFogf'],
3348
glFogiv: (pname, param) => {
3349
switch (pname) {
3350
case 0xB66: // GL_FOG_COLOR
3351
GLEmulation.fogColor[0] = ({{{ makeGetValue('param', '0', 'i32') }}}/2147483647)/2.0+0.5;
3352
GLEmulation.fogColor[1] = ({{{ makeGetValue('param', '4', 'i32') }}}/2147483647)/2.0+0.5;
3353
GLEmulation.fogColor[2] = ({{{ makeGetValue('param', '8', 'i32') }}}/2147483647)/2.0+0.5;
3354
GLEmulation.fogColor[3] = ({{{ makeGetValue('param', '12', 'i32') }}}/2147483647)/2.0+0.5;
3355
break;
3356
default:
3357
_glFogf(pname, {{{ makeGetValue('param', '0', 'i32') }}}); break;
3358
}
3359
},
3360
glFogx: 'glFogi',
3361
glFogxv: 'glFogiv',
3362
3363
glPointSize: (size) => {
3364
GLEmulation.pointSize = size;
3365
},
3366
3367
glPolygonMode: () => {}, // TODO
3368
3369
glAlphaFunc: (func, ref) => {
3370
switch(func) {
3371
case 0x200: // GL_NEVER
3372
case 0x201: // GL_LESS
3373
case 0x202: // GL_EQUAL
3374
case 0x203: // GL_LEQUAL
3375
case 0x204: // GL_GREATER
3376
case 0x205: // GL_NOTEQUAL
3377
case 0x206: // GL_GEQUAL
3378
case 0x207: // GL_ALWAYS
3379
GLEmulation.alphaTestRef = ref;
3380
if (GLEmulation.alphaTestFunc != func) {
3381
GLEmulation.alphaTestFunc = func;
3382
GLImmediate.currentRenderer = null; // alpha test mode is part of the FFP shader state, we must re-lookup the renderer to use.
3383
}
3384
break;
3385
default: // invalid value provided
3386
#if GL_ASSERTIONS
3387
err(`glAlphaFunc: Invalid alpha comparison function ${ptrToString(func)}!`);
3388
#endif
3389
break;
3390
}
3391
},
3392
3393
glNormal3f: (x, y, z) => {
3394
#if ASSERTIONS
3395
assert(GLImmediate.mode >= 0); // must be in begin/end
3396
#endif
3397
GLImmediate.vertexData[GLImmediate.vertexCounter++] = x;
3398
GLImmediate.vertexData[GLImmediate.vertexCounter++] = y;
3399
GLImmediate.vertexData[GLImmediate.vertexCounter++] = z;
3400
#if ASSERTIONS
3401
assert(GLImmediate.vertexCounter << 2 < GL.MAX_TEMP_BUFFER_SIZE);
3402
#endif
3403
GLImmediate.addRendererComponent(GLImmediate.NORMAL, 3, GLctx.FLOAT);
3404
},
3405
3406
glNormal3fv__deps: ['glNormal3f'],
3407
glNormal3fv: (p) => {
3408
_glNormal3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}});
3409
},
3410
3411
3412
// Additional non-GLES rendering calls
3413
3414
glDrawRangeElements__deps: ['glDrawElements'],
3415
glDrawRangeElements: (mode, start, end, count, type, indices) => {
3416
_glDrawElements(mode, count, type, indices, start, end);
3417
},
3418
3419
// ClientState/gl*Pointer
3420
3421
glEnableClientState: (cap) => {
3422
var attrib = GLEmulation.getAttributeFromCapability(cap);
3423
if (attrib === null) {
3424
#if ASSERTIONS
3425
err(`WARNING: unhandled clientstate: ${cap}`);
3426
#endif
3427
return;
3428
}
3429
if (!GLImmediate.enabledClientAttributes[attrib]) {
3430
GLImmediate.enabledClientAttributes[attrib] = true;
3431
GLImmediate.totalEnabledClientAttributes++;
3432
GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed.
3433
#if GL_FFP_ONLY
3434
// In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here.
3435
GL.enableVertexAttribArray(attrib);
3436
#endif
3437
if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1;
3438
GLImmediate.modifiedClientAttributes = true;
3439
}
3440
},
3441
glDisableClientState: (cap) => {
3442
var attrib = GLEmulation.getAttributeFromCapability(cap);
3443
if (attrib === null) {
3444
#if ASSERTIONS
3445
err(`WARNING: unhandled clientstate: ${cap}`);
3446
#endif
3447
return;
3448
}
3449
if (GLImmediate.enabledClientAttributes[attrib]) {
3450
GLImmediate.enabledClientAttributes[attrib] = false;
3451
GLImmediate.totalEnabledClientAttributes--;
3452
GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed.
3453
#if GL_FFP_ONLY
3454
// In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here.
3455
GL.disableVertexAttribArray(attrib);
3456
#endif
3457
if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap];
3458
GLImmediate.modifiedClientAttributes = true;
3459
}
3460
},
3461
3462
glVertexPointer: (size, type, stride, pointer) => {
3463
GLImmediate.setClientAttribute(GLImmediate.VERTEX, size, type, stride, pointer);
3464
#if GL_FFP_ONLY
3465
if (GLctx.currentArrayBufferBinding) {
3466
GLctx.vertexAttribPointer(GLImmediate.VERTEX, size, type, false, stride, pointer);
3467
}
3468
#endif
3469
},
3470
glTexCoordPointer: (size, type, stride, pointer) => {
3471
GLImmediate.setClientAttribute(GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture, size, type, stride, pointer);
3472
#if GL_FFP_ONLY
3473
if (GLctx.currentArrayBufferBinding) {
3474
var loc = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture;
3475
GLctx.vertexAttribPointer(loc, size, type, false, stride, pointer);
3476
}
3477
#endif
3478
},
3479
glNormalPointer: (type, stride, pointer) => {
3480
GLImmediate.setClientAttribute(GLImmediate.NORMAL, 3, type, stride, pointer);
3481
#if GL_FFP_ONLY
3482
if (GLctx.currentArrayBufferBinding) {
3483
GLctx.vertexAttribPointer(GLImmediate.NORMAL, 3, type, true, stride, pointer);
3484
}
3485
#endif
3486
},
3487
glColorPointer: (size, type, stride, pointer) => {
3488
GLImmediate.setClientAttribute(GLImmediate.COLOR, size, type, stride, pointer);
3489
#if GL_FFP_ONLY
3490
if (GLctx.currentArrayBufferBinding) {
3491
GLctx.vertexAttribPointer(GLImmediate.COLOR, size, type, true, stride, pointer);
3492
}
3493
#endif
3494
},
3495
3496
glClientActiveTexture: (texture) => {
3497
GLImmediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0
3498
},
3499
3500
// Replace some functions with immediate-mode aware versions. If there are no
3501
// client attributes enabled, and we use webgl-friendly modes (no GL_QUADS),
3502
// then no need for emulation
3503
glDrawArrays: (mode, first, count) => {
3504
if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6) {
3505
GLctx.drawArrays(mode, first, count);
3506
return;
3507
}
3508
GLImmediate.prepareClientAttributes(count, false);
3509
GLImmediate.mode = mode;
3510
if (!GLctx.currentArrayBufferBinding) {
3511
GLImmediate.vertexData = {{{ makeHEAPView('F32', 'GLImmediate.vertexPointer', 'GLImmediate.vertexPointer + (first+count)*GLImmediate.stride') }}}; // XXX assuming float
3512
GLImmediate.firstVertex = first;
3513
GLImmediate.lastVertex = first + count;
3514
}
3515
GLImmediate.flush(null, first);
3516
GLImmediate.mode = -1;
3517
},
3518
3519
// start, end are given if we come from glDrawRangeElements
3520
glDrawElements: (mode, count, type, indices, start, end) => {
3521
if (GLImmediate.totalEnabledClientAttributes == 0 && mode <= 6 && GLctx.currentElementArrayBufferBinding) {
3522
GLctx.drawElements(mode, count, type, indices);
3523
return;
3524
}
3525
#if ASSERTIONS
3526
if (!GLctx.currentElementArrayBufferBinding) {
3527
assert(type == GLctx.UNSIGNED_SHORT); // We can only emulate buffers of this kind, for now
3528
}
3529
warnOnce("DrawElements doesn't actually prepareClientAttributes properly.");
3530
#endif
3531
GLImmediate.prepareClientAttributes(count, false);
3532
GLImmediate.mode = mode;
3533
if (!GLctx.currentArrayBufferBinding) {
3534
GLImmediate.firstVertex = end ? start : HEAP8.length; // if we don't know the start, set an invalid value and we will calculate it later from the indices
3535
GLImmediate.lastVertex = end ? end + 1 : 0;
3536
start = GLImmediate.vertexPointer;
3537
// TODO(sbc): Combine these two subarray calls back into a single one if
3538
// we ever fix https://github.com/emscripten-core/emscripten/issues/21250.
3539
if (end) {
3540
end = GLImmediate.vertexPointer + (end +1 ) * GLImmediate.stride;
3541
GLImmediate.vertexData = HEAPF32.subarray({{{ getHeapOffset('start', 'float') }}}, {{{ getHeapOffset('end', 'float') }}});
3542
} else {
3543
GLImmediate.vertexData = HEAPF32.subarray({{{ getHeapOffset('start', 'float') }}});
3544
}
3545
}
3546
GLImmediate.flush(count, 0, indices);
3547
GLImmediate.mode = -1;
3548
},
3549
3550
// Vertex array object (VAO) support. TODO: when the WebGL extension is
3551
// popular, use that and remove this code and GL.vaos
3552
$emulGlGenVertexArrays__deps: ['$GLEmulation'],
3553
$emulGlGenVertexArrays: (n, vaos) => {
3554
for (var i = 0; i < n; i++) {
3555
var id = GL.getNewId(GLEmulation.vaos);
3556
GLEmulation.vaos[id] = {
3557
id,
3558
arrayBuffer: 0,
3559
elementArrayBuffer: 0,
3560
enabledVertexAttribArrays: {},
3561
vertexAttribPointers: {},
3562
enabledClientStates: {},
3563
};
3564
{{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}};
3565
}
3566
},
3567
$emulGlDeleteVertexArrays: (n, vaos) => {
3568
for (var i = 0; i < n; i++) {
3569
var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}};
3570
GLEmulation.vaos[id] = null;
3571
if (GLEmulation.currentVao?.id == id) GLEmulation.currentVao = null;
3572
}
3573
},
3574
$emulGlIsVertexArray: (array) => {
3575
var vao = GLEmulation.vaos[array];
3576
if (!vao) return 0;
3577
return 1;
3578
},
3579
$emulGlBindVertexArray__deps: ['glBindBuffer', 'glEnableVertexAttribArray', 'glVertexAttribPointer', 'glEnableClientState'],
3580
$emulGlBindVertexArray: (vao) => {
3581
// undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao
3582
GLEmulation.currentVao = null; // make sure the commands we run here are not recorded
3583
GLImmediate.lastRenderer?.cleanup();
3584
_glBindBuffer(GLctx.ARRAY_BUFFER, 0); // XXX if one was there before we were bound?
3585
_glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, 0);
3586
for (var vaa in GLEmulation.enabledVertexAttribArrays) {
3587
GLctx.disableVertexAttribArray(vaa);
3588
}
3589
GLEmulation.enabledVertexAttribArrays = {};
3590
GLImmediate.enabledClientAttributes = [0, 0];
3591
GLImmediate.totalEnabledClientAttributes = 0;
3592
GLImmediate.modifiedClientAttributes = true;
3593
if (vao) {
3594
// replay vao
3595
var info = GLEmulation.vaos[vao];
3596
_glBindBuffer(GLctx.ARRAY_BUFFER, info.arrayBuffer); // XXX overwrite current binding?
3597
_glBindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer);
3598
for (var vaa in info.enabledVertexAttribArrays) {
3599
_glEnableVertexAttribArray(vaa);
3600
}
3601
for (var vaa in info.vertexAttribPointers) {
3602
_glVertexAttribPointer(...info.vertexAttribPointers[vaa]);
3603
}
3604
for (var attrib in info.enabledClientStates) {
3605
_glEnableClientState(attrib|0);
3606
}
3607
GLEmulation.currentVao = info; // set currentVao last, so the commands we ran here were not recorded
3608
}
3609
},
3610
3611
// OpenGL Immediate Mode matrix routines.
3612
// Note that in the future we might make these available only in certain modes.
3613
glMatrixMode__deps: ['$GL', '$GLImmediateSetup'],
3614
glMatrixMode: (mode) => {
3615
if (mode == 0x1700 /* GL_MODELVIEW */) {
3616
GLImmediate.currentMatrix = 0/*m*/;
3617
} else if (mode == 0x1701 /* GL_PROJECTION */) {
3618
GLImmediate.currentMatrix = 1/*p*/;
3619
} else if (mode == 0x1702) { // GL_TEXTURE
3620
GLImmediate.useTextureMatrix = true;
3621
GLImmediate.currentMatrix = 2/*t*/ + GLImmediate.TexEnvJIT.getActiveTexture();
3622
} else {
3623
throw `Wrong mode ${mode} passed to glMatrixMode`;
3624
}
3625
},
3626
3627
glPushMatrix: () => {
3628
GLImmediate.matricesModified = true;
3629
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3630
GLImmediate.matrixStack[GLImmediate.currentMatrix].push(
3631
Array.prototype.slice.call(GLImmediate.matrix[GLImmediate.currentMatrix]));
3632
},
3633
3634
glPopMatrix: () => {
3635
if (GLImmediate.matrixStack[GLImmediate.currentMatrix].length == 0) {
3636
GL.recordError(0x504/*GL_STACK_UNDERFLOW*/);
3637
return;
3638
}
3639
GLImmediate.matricesModified = true;
3640
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3641
GLImmediate.matrix[GLImmediate.currentMatrix] = GLImmediate.matrixStack[GLImmediate.currentMatrix].pop();
3642
},
3643
3644
glLoadIdentity__deps: ['$GL', '$GLImmediateSetup'],
3645
glLoadIdentity: () => {
3646
GLImmediate.matricesModified = true;
3647
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3648
GLImmediate.matrixLib.mat4.identity(GLImmediate.matrix[GLImmediate.currentMatrix]);
3649
},
3650
3651
glLoadMatrixd: (matrix) => {
3652
GLImmediate.matricesModified = true;
3653
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3654
GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
3655
},
3656
3657
glLoadMatrixf: (matrix) => {
3658
#if GL_DEBUG
3659
if (GL.debug) dbg('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16)));
3660
#endif
3661
GLImmediate.matricesModified = true;
3662
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3663
GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
3664
},
3665
3666
glLoadTransposeMatrixd: (matrix) => {
3667
GLImmediate.matricesModified = true;
3668
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3669
GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
3670
GLImmediate.matrixLib.mat4.transpose(GLImmediate.matrix[GLImmediate.currentMatrix]);
3671
},
3672
3673
glLoadTransposeMatrixf: (matrix) => {
3674
GLImmediate.matricesModified = true;
3675
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3676
GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, GLImmediate.matrix[GLImmediate.currentMatrix]);
3677
GLImmediate.matrixLib.mat4.transpose(GLImmediate.matrix[GLImmediate.currentMatrix]);
3678
},
3679
3680
glMultMatrixd: (matrix) => {
3681
GLImmediate.matricesModified = true;
3682
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3683
GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
3684
{{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}});
3685
},
3686
3687
glMultMatrixf: (matrix) => {
3688
GLImmediate.matricesModified = true;
3689
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3690
GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
3691
{{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}});
3692
},
3693
3694
glMultTransposeMatrixd: (matrix) => {
3695
GLImmediate.matricesModified = true;
3696
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3697
var colMajor = GLImmediate.matrixLib.mat4.create();
3698
GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, colMajor);
3699
GLImmediate.matrixLib.mat4.transpose(colMajor);
3700
GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], colMajor);
3701
},
3702
3703
glMultTransposeMatrixf: (matrix) => {
3704
GLImmediate.matricesModified = true;
3705
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3706
var colMajor = GLImmediate.matrixLib.mat4.create();
3707
GLImmediate.matrixLib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+' + (16*4)) }}}, colMajor);
3708
GLImmediate.matrixLib.mat4.transpose(colMajor);
3709
GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix], colMajor);
3710
},
3711
3712
glFrustum: (left, right, bottom, top_, nearVal, farVal) => {
3713
GLImmediate.matricesModified = true;
3714
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3715
GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
3716
GLImmediate.matrixLib.mat4.frustum(left, right, bottom, top_, nearVal, farVal));
3717
},
3718
glFrustumf: 'glFrustum',
3719
3720
glOrtho: (left, right, bottom, top_, nearVal, farVal) => {
3721
GLImmediate.matricesModified = true;
3722
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3723
GLImmediate.matrixLib.mat4.multiply(GLImmediate.matrix[GLImmediate.currentMatrix],
3724
GLImmediate.matrixLib.mat4.ortho(left, right, bottom, top_, nearVal, farVal));
3725
},
3726
glOrthof: 'glOrtho',
3727
3728
glScaled: (x, y, z) => {
3729
GLImmediate.matricesModified = true;
3730
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3731
GLImmediate.matrixLib.mat4.scale(GLImmediate.matrix[GLImmediate.currentMatrix], [x, y, z]);
3732
},
3733
glScalef: 'glScaled',
3734
3735
glTranslated: (x, y, z) => {
3736
GLImmediate.matricesModified = true;
3737
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3738
GLImmediate.matrixLib.mat4.translate(GLImmediate.matrix[GLImmediate.currentMatrix], [x, y, z]);
3739
},
3740
glTranslatef: 'glTranslated',
3741
3742
glRotated: (angle, x, y, z) => {
3743
GLImmediate.matricesModified = true;
3744
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3745
GLImmediate.matrixLib.mat4.rotate(GLImmediate.matrix[GLImmediate.currentMatrix], angle*Math.PI/180, [x, y, z]);
3746
},
3747
glRotatef: 'glRotated',
3748
3749
glDrawBuffer: () => { throw 'glDrawBuffer: TODO' },
3750
#if MAX_WEBGL_VERSION < 2
3751
glReadBuffer: () => { throw 'glReadBuffer: TODO' },
3752
#endif
3753
3754
glClipPlane: (pname, param) => {
3755
if ((pname >= 0x3000) && (pname < 0x3006) /* GL_CLIP_PLANE0 to GL_CLIP_PLANE5 */) {
3756
var clipPlaneId = pname - 0x3000;
3757
3758
GLEmulation.clipPlaneEquation[clipPlaneId][0] = {{{ makeGetValue('param', '0', 'double') }}};
3759
GLEmulation.clipPlaneEquation[clipPlaneId][1] = {{{ makeGetValue('param', '8', 'double') }}};
3760
GLEmulation.clipPlaneEquation[clipPlaneId][2] = {{{ makeGetValue('param', '16', 'double') }}};
3761
GLEmulation.clipPlaneEquation[clipPlaneId][3] = {{{ makeGetValue('param', '24', 'double') }}};
3762
3763
// apply inverse transposed current modelview matrix when setting clip plane
3764
var tmpMV = GLImmediate.matrixLib.mat4.create(GLImmediate.matrix[0]);
3765
GLImmediate.matrixLib.mat4.inverse(tmpMV);
3766
GLImmediate.matrixLib.mat4.transpose(tmpMV);
3767
GLImmediate.matrixLib.mat4.multiplyVec4(tmpMV, GLEmulation.clipPlaneEquation[clipPlaneId]);
3768
}
3769
},
3770
3771
glLightfv: (light, pname, param) => {
3772
if ((light >= 0x4000) && (light < 0x4008) /* GL_LIGHT0 to GL_LIGHT7 */) {
3773
var lightId = light - 0x4000;
3774
3775
if (pname == 0x1200) { // GL_AMBIENT
3776
GLEmulation.lightAmbient[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}};
3777
GLEmulation.lightAmbient[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}};
3778
GLEmulation.lightAmbient[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}};
3779
GLEmulation.lightAmbient[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}};
3780
} else if (pname == 0x1201) { // GL_DIFFUSE
3781
GLEmulation.lightDiffuse[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}};
3782
GLEmulation.lightDiffuse[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}};
3783
GLEmulation.lightDiffuse[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}};
3784
GLEmulation.lightDiffuse[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}};
3785
} else if (pname == 0x1202) { // GL_SPECULAR
3786
GLEmulation.lightSpecular[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}};
3787
GLEmulation.lightSpecular[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}};
3788
GLEmulation.lightSpecular[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}};
3789
GLEmulation.lightSpecular[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}};
3790
} else if (pname == 0x1203) { // GL_POSITION
3791
GLEmulation.lightPosition[lightId][0] = {{{ makeGetValue('param', '0', 'float') }}};
3792
GLEmulation.lightPosition[lightId][1] = {{{ makeGetValue('param', '4', 'float') }}};
3793
GLEmulation.lightPosition[lightId][2] = {{{ makeGetValue('param', '8', 'float') }}};
3794
GLEmulation.lightPosition[lightId][3] = {{{ makeGetValue('param', '12', 'float') }}};
3795
3796
// multiply position with current modelviewmatrix
3797
GLImmediate.matrixLib.mat4.multiplyVec4(GLImmediate.matrix[0], GLEmulation.lightPosition[lightId]);
3798
} else {
3799
throw 'glLightfv: TODO: ' + pname;
3800
}
3801
}
3802
},
3803
3804
glLightModelf: (pname, param) => {
3805
if (pname == 0x0B52) { // GL_LIGHT_MODEL_TWO_SIDE
3806
GLEmulation.lightModelTwoSide = (param != 0) ? true : false;
3807
} else {
3808
throw 'glLightModelf: TODO: ' + pname;
3809
}
3810
},
3811
3812
glLightModelfv: (pname, param) => { // TODO: GL_LIGHT_MODEL_LOCAL_VIEWER
3813
if (pname == 0x0B53) { // GL_LIGHT_MODEL_AMBIENT
3814
GLEmulation.lightModelAmbient[0] = {{{ makeGetValue('param', '0', 'float') }}};
3815
GLEmulation.lightModelAmbient[1] = {{{ makeGetValue('param', '4', 'float') }}};
3816
GLEmulation.lightModelAmbient[2] = {{{ makeGetValue('param', '8', 'float') }}};
3817
GLEmulation.lightModelAmbient[3] = {{{ makeGetValue('param', '12', 'float') }}};
3818
} else {
3819
throw 'glLightModelfv: TODO: ' + pname;
3820
}
3821
},
3822
3823
glMaterialfv: (face, pname, param) => {
3824
if ((face != 0x0404) && (face != 0x0408)) { throw 'glMaterialfv: TODO' + face; } // only GL_FRONT and GL_FRONT_AND_BACK supported
3825
3826
if (pname == 0x1200) { // GL_AMBIENT
3827
GLEmulation.materialAmbient[0] = {{{ makeGetValue('param', '0', 'float') }}};
3828
GLEmulation.materialAmbient[1] = {{{ makeGetValue('param', '4', 'float') }}};
3829
GLEmulation.materialAmbient[2] = {{{ makeGetValue('param', '8', 'float') }}};
3830
GLEmulation.materialAmbient[3] = {{{ makeGetValue('param', '12', 'float') }}};
3831
} else if (pname == 0x1201) { // GL_DIFFUSE
3832
GLEmulation.materialDiffuse[0] = {{{ makeGetValue('param', '0', 'float') }}};
3833
GLEmulation.materialDiffuse[1] = {{{ makeGetValue('param', '4', 'float') }}};
3834
GLEmulation.materialDiffuse[2] = {{{ makeGetValue('param', '8', 'float') }}};
3835
GLEmulation.materialDiffuse[3] = {{{ makeGetValue('param', '12', 'float') }}};
3836
} else if (pname == 0x1202) { // GL_SPECULAR
3837
GLEmulation.materialSpecular[0] = {{{ makeGetValue('param', '0', 'float') }}};
3838
GLEmulation.materialSpecular[1] = {{{ makeGetValue('param', '4', 'float') }}};
3839
GLEmulation.materialSpecular[2] = {{{ makeGetValue('param', '8', 'float') }}};
3840
GLEmulation.materialSpecular[3] = {{{ makeGetValue('param', '12', 'float') }}};
3841
} else if (pname == 0x1601) { // GL_SHININESS
3842
GLEmulation.materialShininess[0] = {{{ makeGetValue('param', '0', 'float') }}};
3843
} else {
3844
throw 'glMaterialfv: TODO: ' + pname;
3845
}
3846
},
3847
3848
glTexGeni: (coord, pname, param) => { throw 'glTexGeni: TODO' },
3849
glTexGenfv: (coord, pname, param) => { throw 'glTexGenfv: TODO' },
3850
glTexEnvi: (target, pname, params) => warnOnce('glTexEnvi: TODO'),
3851
glTexEnvf: (target, pname, params) => warnOnce('glTexEnvf: TODO'),
3852
glTexEnvfv: (target, pname, params) => warnOnce('glTexEnvfv: TODO'),
3853
3854
glGetTexEnviv: (target, pname, param) => { throw 'GL emulation not initialized!'; },
3855
glGetTexEnvfv: (target, pname, param) => { throw 'GL emulation not initialized!'; },
3856
3857
glTexImage1D: (target, level, internalformat, width, border, format, type, data) => { throw 'glTexImage1D: TODO' },
3858
glTexCoord3f: (target, level, internalformat, width, border, format, type, data) => { throw 'glTexCoord3f: TODO' },
3859
glGetTexLevelParameteriv: (target, level, pname, params) => { throw 'glGetTexLevelParameteriv: TODO' },
3860
3861
glShadeModel: () => warnOnce('TODO: glShadeModel'),
3862
3863
// Open GLES1.1 compatibility
3864
3865
glGenFramebuffersOES: 'glGenFramebuffers',
3866
glGenRenderbuffersOES: 'glGenRenderbuffers',
3867
glBindFramebufferOES: 'glBindFramebuffer',
3868
glBindRenderbufferOES: 'glBindRenderbuffer',
3869
glGetRenderbufferParameterivOES: 'glGetRenderbufferParameteriv',
3870
glFramebufferRenderbufferOES: 'glFramebufferRenderbuffer',
3871
glRenderbufferStorageOES: 'glRenderbufferStorage',
3872
glCheckFramebufferStatusOES: 'glCheckFramebufferStatus',
3873
glDeleteFramebuffersOES: 'glDeleteFramebuffers',
3874
glDeleteRenderbuffersOES: 'glDeleteRenderbuffers',
3875
glFramebufferTexture2DOES: 'glFramebufferTexture2D',
3876
3877
// GLU
3878
3879
gluPerspective: (fov, aspect, near, far) => {
3880
GLImmediate.matricesModified = true;
3881
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3882
GLImmediate.matrix[GLImmediate.currentMatrix] =
3883
GLImmediate.matrixLib.mat4.perspective(fov, aspect, near, far,
3884
GLImmediate.matrix[GLImmediate.currentMatrix]);
3885
},
3886
3887
gluLookAt: (ex, ey, ez, cx, cy, cz, ux, uy, uz) => {
3888
GLImmediate.matricesModified = true;
3889
GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0;
3890
GLImmediate.matrixLib.mat4.lookAt(GLImmediate.matrix[GLImmediate.currentMatrix], [ex, ey, ez],
3891
[cx, cy, cz], [ux, uy, uz]);
3892
},
3893
3894
gluProject: (objX, objY, objZ, model, proj, view, winX, winY, winZ) => {
3895
// The algorithm for this functions comes from Mesa
3896
3897
var inVec = new Float32Array(4);
3898
var outVec = new Float32Array(4);
3899
GLImmediate.matrixLib.mat4.multiplyVec4({{{ makeHEAPView('F64', 'model', 'model+' + (16*8)) }}},
3900
[objX, objY, objZ, 1.0], outVec);
3901
GLImmediate.matrixLib.mat4.multiplyVec4({{{ makeHEAPView('F64', 'proj', 'proj+' + (16*8)) }}},
3902
outVec, inVec);
3903
if (inVec[3] == 0.0) {
3904
return 0 /* GL_FALSE */;
3905
}
3906
inVec[0] /= inVec[3];
3907
inVec[1] /= inVec[3];
3908
inVec[2] /= inVec[3];
3909
// Map x, y and z to range 0-1 */
3910
inVec[0] = inVec[0] * 0.5 + 0.5;
3911
inVec[1] = inVec[1] * 0.5 + 0.5;
3912
inVec[2] = inVec[2] * 0.5 + 0.5;
3913
// Map x, y to viewport
3914
inVec[0] = inVec[0] * {{{ makeGetValue('view', 2*4, 'i32') }}} + {{{ makeGetValue('view', 0*4, 'i32') }}};
3915
inVec[1] = inVec[1] * {{{ makeGetValue('view', 3*4, 'i32') }}} + {{{ makeGetValue('view', 1*4, 'i32') }}};
3916
3917
{{{ makeSetValue('winX', '0', 'inVec[0]', 'double') }}};
3918
{{{ makeSetValue('winY', '0', 'inVec[1]', 'double') }}};
3919
{{{ makeSetValue('winZ', '0', 'inVec[2]', 'double') }}};
3920
3921
return 1 /* GL_TRUE */;
3922
},
3923
3924
gluUnProject: (winX, winY, winZ, model, proj, view, objX, objY, objZ) => {
3925
var result = GLImmediate.matrixLib.vec3.unproject([winX, winY, winZ],
3926
{{{ makeHEAPView('F64', 'model', 'model+' + (16*8)) }}},
3927
{{{ makeHEAPView('F64', 'proj', 'proj+' + (16*8)) }}},
3928
{{{ makeHEAPView('32', 'view', 'view+' + (4*4)) }}});
3929
3930
if (result === null) {
3931
return 0 /* GL_FALSE */;
3932
}
3933
3934
{{{ makeSetValue('objX', '0', 'result[0]', 'double') }}};
3935
{{{ makeSetValue('objY', '0', 'result[1]', 'double') }}};
3936
{{{ makeSetValue('objZ', '0', 'result[2]', 'double') }}};
3937
3938
return 1 /* GL_TRUE */;
3939
},
3940
3941
gluOrtho2D__deps: ['glOrtho'],
3942
gluOrtho2D: (left, right, bottom, top) => _glOrtho(left, right, bottom, top, -1, 1),
3943
};
3944
3945
extraLibraryFuncs.push('$GLEmulation');
3946
3947
recordGLProcAddressGet(LibraryGLEmulation);
3948
3949
addToLibrary(LibraryGLEmulation);
3950
3951