pcbjam/lib/gl_immediate_shim.js
Viktor Vaczi 3287ec7616 Add GL immediate mode shim for Emscripten compatibility
Create custom JS library (lib/gl_immediate_shim.js) that fixes two
Emscripten LEGACY_GL_EMULATION issues without modifying KiCad source:

1. Color-per-vertex requirement: Emscripten requires glColor before
   each glVertex, but standard OpenGL uses persistent "current color".
   The shim tracks color state and auto-injects before each vertex.

2. Missing double-precision functions: glVertex2d, glVertex3d,
   glColor3d, glColor4d are unimplemented in Emscripten (wontfix).
   The shim provides these as wrappers to float variants.

Update test code to use standard OpenGL patterns (color set once,
multiple vertices) to verify the shim works correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 21:22:09 +01:00

178 lines
5.3 KiB
JavaScript

/**
* gl_immediate_shim.js
*
* Custom OpenGL immediate mode shims for KiCad WASM port.
* Fixes Emscripten LEGACY_GL_EMULATION issues:
* 1. Color-per-vertex requirement - injects color before each vertex automatically
* 2. Missing double-precision functions (glVertex2d, glVertex3d, glColor3d, glColor4d)
*
* Usage: emcc ... --js-library=lib/gl_immediate_shim.js
*/
addToLibrary({
// ==================================================================
// GLImmediateShim - State tracking and function wrapping
// ==================================================================
$GLImmediateShim__deps: ['$GLImmediate'],
$GLImmediateShim__postset: 'GLImmediateShim.init();',
$GLImmediateShim: {
// Current color state (persistent across vertices)
currentColor: null,
// Track if we're inside glBegin/glEnd block
inBeginEnd: false,
// Track if color was called since the last vertex
// This prevents double-injection when code already calls color per vertex
colorCalledSinceLastVertex: false,
// Original functions we're wrapping
origFns: {},
// Initialization flag
initialized: false,
init: function() {
if (GLImmediateShim.initialized) return;
if (typeof GLImmediate === 'undefined') {
// GLImmediate not ready yet, will be called again
console.log('[GLImmediateShim] Waiting for GLImmediate...');
return;
}
console.log('[GLImmediateShim] Initializing OpenGL immediate mode shims');
// Initialize current color to white (OpenGL default)
GLImmediateShim.currentColor = new Float32Array([1.0, 1.0, 1.0, 1.0]);
// Store original functions
GLImmediateShim.origFns = {
glBegin: _glBegin,
glEnd: _glEnd,
glVertex2f: _glVertex2f,
glVertex3f: _glVertex3f,
glColor3f: _glColor3f,
glColor4f: _glColor4f,
};
// Install shims
_glBegin = GLImmediateShim.shimBegin;
_glEnd = GLImmediateShim.shimEnd;
_glVertex2f = GLImmediateShim.shimVertex2f;
_glVertex3f = GLImmediateShim.shimVertex3f;
_glColor3f = GLImmediateShim.shimColor3f;
_glColor4f = GLImmediateShim.shimColor4f;
GLImmediateShim.initialized = true;
console.log('[GLImmediateShim] Initialized successfully');
},
// ==================================================================
// Shim implementations
// ==================================================================
shimBegin: function(mode) {
GLImmediateShim.inBeginEnd = true;
GLImmediateShim.colorCalledSinceLastVertex = false;
GLImmediateShim.origFns.glBegin(mode);
},
shimEnd: function() {
GLImmediateShim.inBeginEnd = false;
GLImmediateShim.origFns.glEnd();
},
shimColor3f: function(r, g, b) {
var c = GLImmediateShim.currentColor;
c[0] = r;
c[1] = g;
c[2] = b;
c[3] = 1.0;
if (GLImmediateShim.inBeginEnd) {
// Mark that color was explicitly called for this vertex
GLImmediateShim.colorCalledSinceLastVertex = true;
}
GLImmediateShim.origFns.glColor3f(r, g, b);
},
shimColor4f: function(r, g, b, a) {
var c = GLImmediateShim.currentColor;
c[0] = r;
c[1] = g;
c[2] = b;
c[3] = a;
if (GLImmediateShim.inBeginEnd) {
// Mark that color was explicitly called for this vertex
GLImmediateShim.colorCalledSinceLastVertex = true;
}
GLImmediateShim.origFns.glColor4f(r, g, b, a);
},
// Inject current color before each vertex (only if not already called)
injectColor: function() {
if (!GLImmediateShim.inBeginEnd) return;
// Only inject color if it wasn't already called for this vertex
if (!GLImmediateShim.colorCalledSinceLastVertex) {
var c = GLImmediateShim.currentColor;
GLImmediateShim.origFns.glColor4f(c[0], c[1], c[2], c[3]);
}
// Reset the flag for the next vertex
GLImmediateShim.colorCalledSinceLastVertex = false;
},
shimVertex2f: function(x, y) {
GLImmediateShim.injectColor();
GLImmediateShim.origFns.glVertex2f(x, y);
},
shimVertex3f: function(x, y, z) {
GLImmediateShim.injectColor();
GLImmediateShim.origFns.glVertex3f(x, y, z);
},
},
// ==================================================================
// Double-precision vertex functions (missing in Emscripten)
// ==================================================================
glVertex2d__deps: ['glVertex2f'],
glVertex2d: function(x, y) {
_glVertex2f(x, y);
},
glVertex3d__deps: ['glVertex3f'],
glVertex3d: function(x, y, z) {
_glVertex3f(x, y, z);
},
glVertex4d__deps: ['glVertex4f'],
glVertex4d: function(x, y, z, w) {
_glVertex4f(x, y, z, w);
},
// ==================================================================
// Double-precision color functions
// ==================================================================
glColor3d__deps: ['glColor3f'],
glColor3d: function(r, g, b) {
_glColor3f(r, g, b);
},
glColor4d__deps: ['glColor4f'],
glColor4d: function(r, g, b, a) {
_glColor4f(r, g, b, a);
},
});
// Ensure GLImmediateShim is included in the build
if (typeof extraLibraryFuncs !== 'undefined') {
extraLibraryFuncs.push('$GLImmediateShim');
}