@easywasm/gl 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -2
- package/web/gl.js +1068 -0
- package/web/glfw.js +931 -0
- package/src/example.c +0 -41
- package/src/gl.h +0 -569
- package/src/glfw3.h +0 -6577
- package/src/platform.h +0 -28
package/web/gl.js
ADDED
|
@@ -0,0 +1,1068 @@
|
|
|
1
|
+
// OpenGL → WebGL2 host implementation for wasm modules compiled with wasi-sdk.
|
|
2
|
+
// Covers modern GL (ES 3.0), immediate mode emulation, and fixed-function matrices.
|
|
3
|
+
//
|
|
4
|
+
// Usage:
|
|
5
|
+
// const gl = new GL({ memory })
|
|
6
|
+
// // after _start() runs (window + context exist):
|
|
7
|
+
// gl.setGL(glfw.getContextGL())
|
|
8
|
+
// // include in env imports:
|
|
9
|
+
// env: { ...glfw, ...gl, memory }
|
|
10
|
+
|
|
11
|
+
const dec = new TextDecoder()
|
|
12
|
+
const enc = new TextEncoder()
|
|
13
|
+
|
|
14
|
+
// ── Matrix math (column-major, matches OpenGL) ───────────────────────────────
|
|
15
|
+
|
|
16
|
+
function m4id() {
|
|
17
|
+
return new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1])
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function m4mul(a, b) {
|
|
21
|
+
const o = new Float32Array(16)
|
|
22
|
+
for (let c = 0; c < 4; c++)
|
|
23
|
+
for (let r = 0; r < 4; r++) {
|
|
24
|
+
let s = 0
|
|
25
|
+
for (let k = 0; k < 4; k++) s += a[r + k*4] * b[k + c*4]
|
|
26
|
+
o[r + c*4] = s
|
|
27
|
+
}
|
|
28
|
+
return o
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function m4ortho(l, r, b, t, n, f) {
|
|
32
|
+
return new Float32Array([
|
|
33
|
+
2/(r-l), 0, 0, 0,
|
|
34
|
+
0, 2/(t-b), 0, 0,
|
|
35
|
+
0, 0, -2/(f-n), 0,
|
|
36
|
+
-(r+l)/(r-l), -(t+b)/(t-b), -(f+n)/(f-n), 1
|
|
37
|
+
])
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function m4frustum(l, r, b, t, n, f) {
|
|
41
|
+
return new Float32Array([
|
|
42
|
+
2*n/(r-l), 0, 0, 0,
|
|
43
|
+
0, 2*n/(t-b), 0, 0,
|
|
44
|
+
(r+l)/(r-l), (t+b)/(t-b), -(f+n)/(f-n), -1,
|
|
45
|
+
0, 0, -2*f*n/(f-n), 0
|
|
46
|
+
])
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function m4translate(x, y, z) {
|
|
50
|
+
const m = m4id()
|
|
51
|
+
m[12] = x; m[13] = y; m[14] = z
|
|
52
|
+
return m
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function m4scale(x, y, z) {
|
|
56
|
+
return new Float32Array([x,0,0,0, 0,y,0,0, 0,0,z,0, 0,0,0,1])
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function m4rotate(angleDeg, x, y, z) {
|
|
60
|
+
const a = angleDeg * Math.PI / 180
|
|
61
|
+
const c = Math.cos(a), s = Math.sin(a)
|
|
62
|
+
const len = Math.sqrt(x*x + y*y + z*z)
|
|
63
|
+
if (len === 0) return m4id()
|
|
64
|
+
x /= len; y /= len; z /= len
|
|
65
|
+
const t = 1 - c
|
|
66
|
+
return new Float32Array([
|
|
67
|
+
t*x*x+c, t*x*y+s*z, t*x*z-s*y, 0,
|
|
68
|
+
t*x*y-s*z, t*y*y+c, t*y*z+s*x, 0,
|
|
69
|
+
t*x*z+s*y, t*y*z-s*x, t*z*z+c, 0,
|
|
70
|
+
0, 0, 0, 1
|
|
71
|
+
])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Immediate mode: stride in floats per vertex: pos(3)+color(4)+uv(2)+normal(3)
|
|
75
|
+
const IM_STRIDE = 12
|
|
76
|
+
|
|
77
|
+
const IM_VERT_SRC = `#version 300 es
|
|
78
|
+
in vec3 a_pos; in vec4 a_col; in vec2 a_uv; in vec3 a_nrm;
|
|
79
|
+
uniform mat4 u_mv; uniform mat4 u_proj;
|
|
80
|
+
out vec4 v_col; out vec2 v_uv;
|
|
81
|
+
void main() {
|
|
82
|
+
gl_Position = u_proj * u_mv * vec4(a_pos, 1.0);
|
|
83
|
+
v_col = a_col; v_uv = a_uv;
|
|
84
|
+
}`
|
|
85
|
+
|
|
86
|
+
const IM_FRAG_SRC = `#version 300 es
|
|
87
|
+
precision mediump float;
|
|
88
|
+
in vec4 v_col; in vec2 v_uv;
|
|
89
|
+
uniform sampler2D u_tex; uniform int u_use_tex;
|
|
90
|
+
out vec4 fragColor;
|
|
91
|
+
void main() {
|
|
92
|
+
fragColor = u_use_tex != 0 ? v_col * texture(u_tex, v_uv) : v_col;
|
|
93
|
+
}`
|
|
94
|
+
|
|
95
|
+
// GL_QUADS → triangles: every 4 verts → 2 tris
|
|
96
|
+
function quadsToTris(verts, stride) {
|
|
97
|
+
const out = []
|
|
98
|
+
for (let i = 0; i < verts.length; i += stride * 4) {
|
|
99
|
+
const v = (j) => verts.slice(i + j*stride, i + (j+1)*stride)
|
|
100
|
+
out.push(...v(0), ...v(1), ...v(2))
|
|
101
|
+
out.push(...v(0), ...v(2), ...v(3))
|
|
102
|
+
}
|
|
103
|
+
return out
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export class GL {
|
|
107
|
+
constructor({ memory } = {}) {
|
|
108
|
+
this._memory = memory || null
|
|
109
|
+
this._gl = null
|
|
110
|
+
this._instance = null
|
|
111
|
+
|
|
112
|
+
// Object registries: integer ID → WebGL object
|
|
113
|
+
this._buffers = new Map()
|
|
114
|
+
this._textures = new Map()
|
|
115
|
+
this._vaos = new Map()
|
|
116
|
+
this._shaders = new Map()
|
|
117
|
+
this._programs = new Map()
|
|
118
|
+
this._fbos = new Map()
|
|
119
|
+
this._rbos = new Map()
|
|
120
|
+
this._ulocs = new Map() // uniform location ID → WebGLUniformLocation
|
|
121
|
+
this._nextId = 1
|
|
122
|
+
|
|
123
|
+
// String allocation cache (same pattern as Glfw)
|
|
124
|
+
this._strCache = new Map()
|
|
125
|
+
|
|
126
|
+
// Fixed-function matrix stacks
|
|
127
|
+
this._matMode = 0x1700 // GL_MODELVIEW
|
|
128
|
+
this._mvStack = [m4id()]
|
|
129
|
+
this._projStack = [m4id()]
|
|
130
|
+
this._texStack = [m4id()]
|
|
131
|
+
|
|
132
|
+
// Immediate mode state
|
|
133
|
+
this._imMode = -1
|
|
134
|
+
this._imVerts = []
|
|
135
|
+
this._imColor = [1, 1, 1, 1]
|
|
136
|
+
this._imNormal = [0, 0, 1]
|
|
137
|
+
this._imUV = [0, 0]
|
|
138
|
+
this._imVao = null
|
|
139
|
+
this._imVbo = null
|
|
140
|
+
this._imProg = null
|
|
141
|
+
this._imLocs = null // uniform locations for im program
|
|
142
|
+
this._imTex = false
|
|
143
|
+
|
|
144
|
+
// Bind all methods for use as wasm imports
|
|
145
|
+
const proto = Object.getPrototypeOf(this)
|
|
146
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
147
|
+
if (key === 'constructor') continue
|
|
148
|
+
const desc = Object.getOwnPropertyDescriptor(proto, key)
|
|
149
|
+
if (typeof desc.value === 'function') this[key] = desc.value.bind(this)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Call after instantiation with the wasm exports object (mirrors WasiPreview1.start).
|
|
154
|
+
// Call setGL(glfw.getContextGL()) after exports._start() to wire the WebGL2 context.
|
|
155
|
+
start(exports) {
|
|
156
|
+
this._instance = { exports }
|
|
157
|
+
if (!this._memory && exports.memory) this._memory = exports.memory
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Legacy: call with the full WebAssembly.Instance object.
|
|
161
|
+
setInstance(instance) {
|
|
162
|
+
this._instance = instance
|
|
163
|
+
if (!this._memory && instance.exports.memory) {
|
|
164
|
+
this._memory = instance.exports.memory
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
setGL(gl) {
|
|
169
|
+
this._gl = gl
|
|
170
|
+
this._initImmediate()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── Memory helpers ──────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
get _view() { return new DataView(this._memory.buffer) }
|
|
176
|
+
|
|
177
|
+
_readStr(ptr) {
|
|
178
|
+
if (!ptr) return ''
|
|
179
|
+
const buf = new Uint8Array(this._memory.buffer)
|
|
180
|
+
let end = ptr; while (buf[end]) end++
|
|
181
|
+
return dec.decode(buf.subarray(ptr, end))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
_wi32(ptr, v) { if (ptr) this._view.setInt32(ptr, v | 0, true) }
|
|
185
|
+
_wf32(ptr, v) { if (ptr) this._view.setFloat32(ptr, v, true) }
|
|
186
|
+
|
|
187
|
+
_allocStr(str) {
|
|
188
|
+
if (!str) return 0
|
|
189
|
+
if (this._strCache.has(str)) return this._strCache.get(str)
|
|
190
|
+
const fn = this._instance?.exports?.malloc
|
|
191
|
+
if (!fn) return 0
|
|
192
|
+
const bytes = enc.encode(str + '\0')
|
|
193
|
+
const ptr = fn(bytes.length)
|
|
194
|
+
new Uint8Array(this._memory.buffer, ptr, bytes.length).set(bytes)
|
|
195
|
+
this._strCache.set(str, ptr)
|
|
196
|
+
return ptr
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_f32s(ptr, n) {
|
|
200
|
+
// Return Float32Array view into wasm memory (ptr must be 4-byte aligned)
|
|
201
|
+
if (!ptr) return null
|
|
202
|
+
return new Float32Array(this._memory.buffer, ptr, n)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_i32s(ptr, n) {
|
|
206
|
+
if (!ptr) return null
|
|
207
|
+
return new Int32Array(this._memory.buffer, ptr, n)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
_bytes(ptr, n) {
|
|
211
|
+
if (!ptr) return null
|
|
212
|
+
return new Uint8Array(this._memory.buffer, ptr, n)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Immediate mode setup ────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
_initImmediate() {
|
|
218
|
+
const g = this._gl
|
|
219
|
+
const vs = g.createShader(g.VERTEX_SHADER)
|
|
220
|
+
g.shaderSource(vs, IM_VERT_SRC)
|
|
221
|
+
g.compileShader(vs)
|
|
222
|
+
const fs = g.createShader(g.FRAGMENT_SHADER)
|
|
223
|
+
g.shaderSource(fs, IM_FRAG_SRC)
|
|
224
|
+
g.compileShader(fs)
|
|
225
|
+
const prog = g.createProgram()
|
|
226
|
+
g.attachShader(prog, vs); g.attachShader(prog, fs)
|
|
227
|
+
g.linkProgram(prog)
|
|
228
|
+
g.deleteShader(vs); g.deleteShader(fs)
|
|
229
|
+
|
|
230
|
+
this._imProg = prog
|
|
231
|
+
this._imLocs = {
|
|
232
|
+
mv: g.getUniformLocation(prog, 'u_mv'),
|
|
233
|
+
proj: g.getUniformLocation(prog, 'u_proj'),
|
|
234
|
+
tex: g.getUniformLocation(prog, 'u_tex'),
|
|
235
|
+
useTex: g.getUniformLocation(prog, 'u_use_tex'),
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this._imVao = g.createVertexArray()
|
|
239
|
+
g.bindVertexArray(this._imVao)
|
|
240
|
+
this._imVbo = g.createBuffer()
|
|
241
|
+
g.bindBuffer(g.ARRAY_BUFFER, this._imVbo)
|
|
242
|
+
|
|
243
|
+
const stride = IM_STRIDE * 4
|
|
244
|
+
const al = (name, size, off) => {
|
|
245
|
+
const loc = g.getAttribLocation(prog, name)
|
|
246
|
+
g.vertexAttribPointer(loc, size, g.FLOAT, false, stride, off * 4)
|
|
247
|
+
g.enableVertexAttribArray(loc)
|
|
248
|
+
}
|
|
249
|
+
al('a_pos', 3, 0)
|
|
250
|
+
al('a_col', 4, 3)
|
|
251
|
+
al('a_uv', 2, 7)
|
|
252
|
+
al('a_nrm', 3, 9)
|
|
253
|
+
|
|
254
|
+
g.bindVertexArray(null)
|
|
255
|
+
g.bindBuffer(g.ARRAY_BUFFER, null)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
_flushImmediate() {
|
|
259
|
+
if (!this._imVerts.length) return
|
|
260
|
+
const g = this._gl
|
|
261
|
+
|
|
262
|
+
let mode = this._imMode
|
|
263
|
+
let verts = this._imVerts
|
|
264
|
+
|
|
265
|
+
// Emulate GL_QUADS → GL_TRIANGLES
|
|
266
|
+
if (mode === 7 /*GL_QUADS*/) {
|
|
267
|
+
verts = quadsToTris(verts, IM_STRIDE)
|
|
268
|
+
mode = 4 /*GL_TRIANGLES*/
|
|
269
|
+
}
|
|
270
|
+
// GL_POLYGON treated as triangle fan
|
|
271
|
+
if (mode === 9 /*GL_POLYGON*/) mode = 6 /*GL_TRIANGLE_FAN*/
|
|
272
|
+
|
|
273
|
+
const data = new Float32Array(verts)
|
|
274
|
+
|
|
275
|
+
// Save state
|
|
276
|
+
const prevProg = g.getParameter(g.CURRENT_PROGRAM)
|
|
277
|
+
const prevVao = g.getParameter(g.VERTEX_ARRAY_BINDING)
|
|
278
|
+
const prevVbo = g.getParameter(g.ARRAY_BUFFER_BINDING)
|
|
279
|
+
|
|
280
|
+
g.useProgram(this._imProg)
|
|
281
|
+
g.uniformMatrix4fv(this._imLocs.mv, false, this._mvStack[this._mvStack.length-1])
|
|
282
|
+
g.uniformMatrix4fv(this._imLocs.proj, false, this._projStack[this._projStack.length-1])
|
|
283
|
+
g.uniform1i(this._imLocs.useTex, this._imTex ? 1 : 0)
|
|
284
|
+
|
|
285
|
+
g.bindVertexArray(this._imVao)
|
|
286
|
+
g.bindBuffer(g.ARRAY_BUFFER, this._imVbo)
|
|
287
|
+
g.bufferData(g.ARRAY_BUFFER, data, g.STREAM_DRAW)
|
|
288
|
+
g.drawArrays(mode, 0, verts.length / IM_STRIDE)
|
|
289
|
+
|
|
290
|
+
// Restore state
|
|
291
|
+
g.bindVertexArray(prevVao)
|
|
292
|
+
g.bindBuffer(g.ARRAY_BUFFER, prevVbo)
|
|
293
|
+
g.useProgram(prevProg)
|
|
294
|
+
|
|
295
|
+
this._imVerts = []
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
_curMatrix() {
|
|
299
|
+
if (this._matMode === 0x1701) return this._projStack
|
|
300
|
+
if (this._matMode === 0x1702) return this._texStack
|
|
301
|
+
return this._mvStack
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ── Clear ───────────────────────────────────────────────────────────────────
|
|
305
|
+
|
|
306
|
+
glClearColor(r, g, b, a) { this._gl.clearColor(r, g, b, a) }
|
|
307
|
+
glClearDepth(d) { this._gl.clearDepth(d) }
|
|
308
|
+
glClearDepthf(d) { this._gl.clearDepth(d) }
|
|
309
|
+
glClearStencil(s) { this._gl.clearStencil(s) }
|
|
310
|
+
glClear(mask) { this._gl.clear(mask) }
|
|
311
|
+
|
|
312
|
+
// ── State ───────────────────────────────────────────────────────────────────
|
|
313
|
+
|
|
314
|
+
glEnable(cap) { this._gl.enable(cap) }
|
|
315
|
+
glDisable(cap) { this._gl.disable(cap) }
|
|
316
|
+
glIsEnabled(cap) { return this._gl.isEnabled(cap) ? 1 : 0 }
|
|
317
|
+
glBlendFunc(sfactor, dfactor) { this._gl.blendFunc(sfactor, dfactor) }
|
|
318
|
+
glBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA) { this._gl.blendFuncSeparate(srcRGB, dstRGB, srcA, dstA) }
|
|
319
|
+
glBlendEquation(mode) { this._gl.blendEquation(mode) }
|
|
320
|
+
glBlendEquationSeparate(modeRGB, modeA) { this._gl.blendEquationSeparate(modeRGB, modeA) }
|
|
321
|
+
glBlendColor(r, g, b, a) { this._gl.blendColor(r, g, b, a) }
|
|
322
|
+
glDepthFunc(func) { this._gl.depthFunc(func) }
|
|
323
|
+
glDepthMask(flag) { this._gl.depthMask(!!flag) }
|
|
324
|
+
glDepthRange(near, far) { this._gl.depthRange(near, far) }
|
|
325
|
+
glDepthRangef(near, far) { this._gl.depthRange(near, far) }
|
|
326
|
+
glColorMask(r, g, b, a) { this._gl.colorMask(!!r, !!g, !!b, !!a) }
|
|
327
|
+
glCullFace(mode) { this._gl.cullFace(mode) }
|
|
328
|
+
glFrontFace(mode) { this._gl.frontFace(mode) }
|
|
329
|
+
glLineWidth(w) { this._gl.lineWidth(w) }
|
|
330
|
+
glPolygonOffset(factor, units) { this._gl.polygonOffset(factor, units) }
|
|
331
|
+
glScissor(x, y, w, h) { this._gl.scissor(x, y, w, h) }
|
|
332
|
+
glViewport(x, y, w, h) { this._gl.viewport(x, y, w, h) }
|
|
333
|
+
glSampleCoverage(value, invert) { this._gl.sampleCoverage(value, !!invert) }
|
|
334
|
+
glStencilFunc(func, ref, mask) { this._gl.stencilFunc(func, ref, mask) }
|
|
335
|
+
glStencilFuncSeparate(face, f, r, m) { this._gl.stencilFuncSeparate(face, f, r, m) }
|
|
336
|
+
glStencilOp(fail, zfail, zpass) { this._gl.stencilOp(fail, zfail, zpass) }
|
|
337
|
+
glStencilOpSeparate(face, f, zf, zp) { this._gl.stencilOpSeparate(face, f, zf, zp) }
|
|
338
|
+
glStencilMask(mask) { this._gl.stencilMask(mask) }
|
|
339
|
+
glStencilMaskSeparate(face, mask) { this._gl.stencilMaskSeparate(face, mask) }
|
|
340
|
+
glPixelStorei(pname, param) { this._gl.pixelStorei(pname, param) }
|
|
341
|
+
glFinish() { this._gl.finish() }
|
|
342
|
+
glFlush() { this._gl.flush() }
|
|
343
|
+
|
|
344
|
+
// ── Buffers ─────────────────────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
glGenBuffers(n, ptr) {
|
|
347
|
+
for (let i = 0; i < n; i++) {
|
|
348
|
+
const id = this._nextId++
|
|
349
|
+
this._buffers.set(id, this._gl.createBuffer())
|
|
350
|
+
this._wi32(ptr + i * 4, id)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
glDeleteBuffers(n, ptr) {
|
|
355
|
+
for (let i = 0; i < n; i++) {
|
|
356
|
+
const id = this._view.getInt32(ptr + i * 4, true)
|
|
357
|
+
const buf = this._buffers.get(id)
|
|
358
|
+
if (buf) { this._gl.deleteBuffer(buf); this._buffers.delete(id) }
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
glBindBuffer(target, id) {
|
|
363
|
+
this._gl.bindBuffer(target, this._buffers.get(id) ?? null)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
glBufferData(target, size, dataPtr, usage) {
|
|
367
|
+
const data = dataPtr ? new Uint8Array(this._memory.buffer, dataPtr, size) : null
|
|
368
|
+
this._gl.bufferData(target, data ?? size, usage)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
glBufferSubData(target, offset, size, dataPtr) {
|
|
372
|
+
const data = new Uint8Array(this._memory.buffer, dataPtr, size)
|
|
373
|
+
this._gl.bufferSubData(target, offset, data)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
glIsBuffer(id) { return this._buffers.has(id) ? 1 : 0 }
|
|
377
|
+
|
|
378
|
+
// ── Vertex Arrays ────────────────────────────────────────────────────────────
|
|
379
|
+
|
|
380
|
+
glGenVertexArrays(n, ptr) {
|
|
381
|
+
for (let i = 0; i < n; i++) {
|
|
382
|
+
const id = this._nextId++
|
|
383
|
+
this._vaos.set(id, this._gl.createVertexArray())
|
|
384
|
+
this._wi32(ptr + i * 4, id)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
glDeleteVertexArrays(n, ptr) {
|
|
389
|
+
for (let i = 0; i < n; i++) {
|
|
390
|
+
const id = this._view.getInt32(ptr + i * 4, true)
|
|
391
|
+
const vao = this._vaos.get(id)
|
|
392
|
+
if (vao) { this._gl.deleteVertexArray(vao); this._vaos.delete(id) }
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
glBindVertexArray(id) {
|
|
397
|
+
this._gl.bindVertexArray(this._vaos.get(id) ?? null)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
glIsVertexArray(id) { return this._vaos.has(id) ? 1 : 0 }
|
|
401
|
+
|
|
402
|
+
glEnableVertexAttribArray(index) { this._gl.enableVertexAttribArray(index) }
|
|
403
|
+
glDisableVertexAttribArray(index) { this._gl.disableVertexAttribArray(index) }
|
|
404
|
+
|
|
405
|
+
glVertexAttribPointer(index, size, type, normalized, stride, offset) {
|
|
406
|
+
this._gl.vertexAttribPointer(index, size, type, !!normalized, stride, offset)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
glVertexAttribIPointer(index, size, type, stride, offset) {
|
|
410
|
+
this._gl.vertexAttribIPointer(index, size, type, stride, offset)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
glVertexAttrib1f(i, x) { this._gl.vertexAttrib1f(i, x) }
|
|
414
|
+
glVertexAttrib2f(i, x, y) { this._gl.vertexAttrib2f(i, x, y) }
|
|
415
|
+
glVertexAttrib3f(i, x, y, z) { this._gl.vertexAttrib3f(i, x, y, z) }
|
|
416
|
+
glVertexAttrib4f(i, x, y, z, w) { this._gl.vertexAttrib4f(i, x, y, z, w) }
|
|
417
|
+
|
|
418
|
+
glVertexAttrib1fv(i, ptr) { this._gl.vertexAttrib1fv(i, this._f32s(ptr, 1)) }
|
|
419
|
+
glVertexAttrib2fv(i, ptr) { this._gl.vertexAttrib2fv(i, this._f32s(ptr, 2)) }
|
|
420
|
+
glVertexAttrib3fv(i, ptr) { this._gl.vertexAttrib3fv(i, this._f32s(ptr, 3)) }
|
|
421
|
+
glVertexAttrib4fv(i, ptr) { this._gl.vertexAttrib4fv(i, this._f32s(ptr, 4)) }
|
|
422
|
+
|
|
423
|
+
glVertexAttribDivisor(index, divisor) { this._gl.vertexAttribDivisor(index, divisor) }
|
|
424
|
+
|
|
425
|
+
// ── Shaders ──────────────────────────────────────────────────────────────────
|
|
426
|
+
|
|
427
|
+
glCreateShader(type) {
|
|
428
|
+
const id = this._nextId++
|
|
429
|
+
this._shaders.set(id, this._gl.createShader(type))
|
|
430
|
+
return id
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
glDeleteShader(id) {
|
|
434
|
+
const s = this._shaders.get(id)
|
|
435
|
+
if (s) { this._gl.deleteShader(s); this._shaders.delete(id) }
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
glShaderSource(id, count, stringsPtrPtr, lengthsPtrPtr) {
|
|
439
|
+
const v = this._view
|
|
440
|
+
let src = ''
|
|
441
|
+
for (let i = 0; i < count; i++) {
|
|
442
|
+
const strPtr = v.getInt32(stringsPtrPtr + i * 4, true)
|
|
443
|
+
const len = lengthsPtrPtr ? v.getInt32(lengthsPtrPtr + i * 4, true) : -1
|
|
444
|
+
if (len >= 0) {
|
|
445
|
+
src += dec.decode(new Uint8Array(this._memory.buffer, strPtr, len))
|
|
446
|
+
} else {
|
|
447
|
+
src += this._readStr(strPtr)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
this._gl.shaderSource(this._shaders.get(id), src)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
glCompileShader(id) {
|
|
454
|
+
this._gl.compileShader(this._shaders.get(id))
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
glGetShaderiv(id, pname, paramsPtr) {
|
|
458
|
+
const g = this._gl
|
|
459
|
+
const s = this._shaders.get(id)
|
|
460
|
+
let val
|
|
461
|
+
if (pname === 0x8B81 /*COMPILE_STATUS*/) val = g.getShaderParameter(s, pname) ? 1 : 0
|
|
462
|
+
else if (pname === 0x8B84 /*INFO_LOG_LENGTH*/) val = (g.getShaderInfoLog(s) || '').length + 1
|
|
463
|
+
else if (pname === 0x8B4F /*SHADER_TYPE*/) val = g.getShaderParameter(s, pname)
|
|
464
|
+
else if (pname === 0x8B80 /*DELETE_STATUS*/) val = g.getShaderParameter(s, pname) ? 1 : 0
|
|
465
|
+
else val = 0
|
|
466
|
+
this._wi32(paramsPtr, val)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
glGetShaderInfoLog(id, maxLen, lengthPtr, logPtr) {
|
|
470
|
+
const log = this._gl.getShaderInfoLog(this._shaders.get(id)) || ''
|
|
471
|
+
const bytes = enc.encode(log.substring(0, maxLen - 1) + '\0')
|
|
472
|
+
new Uint8Array(this._memory.buffer, logPtr, bytes.length).set(bytes)
|
|
473
|
+
this._wi32(lengthPtr, bytes.length - 1)
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
glIsShader(id) { return this._shaders.has(id) ? 1 : 0 }
|
|
477
|
+
|
|
478
|
+
// ── Programs ─────────────────────────────────────────────────────────────────
|
|
479
|
+
|
|
480
|
+
glCreateProgram() {
|
|
481
|
+
const id = this._nextId++
|
|
482
|
+
this._programs.set(id, this._gl.createProgram())
|
|
483
|
+
return id
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
glDeleteProgram(id) {
|
|
487
|
+
const p = this._programs.get(id)
|
|
488
|
+
if (p) { this._gl.deleteProgram(p); this._programs.delete(id) }
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
glAttachShader(progId, shaderId) {
|
|
492
|
+
this._gl.attachShader(this._programs.get(progId), this._shaders.get(shaderId))
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
glDetachShader(progId, shaderId) {
|
|
496
|
+
this._gl.detachShader(this._programs.get(progId), this._shaders.get(shaderId))
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
glBindAttribLocation(progId, index, namePtr) {
|
|
500
|
+
this._gl.bindAttribLocation(this._programs.get(progId), index, this._readStr(namePtr))
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
glLinkProgram(id) {
|
|
504
|
+
this._gl.linkProgram(this._programs.get(id))
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
glValidateProgram(id) {
|
|
508
|
+
this._gl.validateProgram(this._programs.get(id))
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
glUseProgram(id) {
|
|
512
|
+
this._gl.useProgram(id ? this._programs.get(id) : null)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
glGetProgramiv(id, pname, paramsPtr) {
|
|
516
|
+
const g = this._gl
|
|
517
|
+
const p = this._programs.get(id)
|
|
518
|
+
let val
|
|
519
|
+
switch (pname) {
|
|
520
|
+
case 0x8B82: val = g.getProgramParameter(p, pname) ? 1 : 0; break // LINK_STATUS
|
|
521
|
+
case 0x8B80: val = g.getProgramParameter(p, pname) ? 1 : 0; break // DELETE_STATUS
|
|
522
|
+
case 0x8B83: val = g.getProgramParameter(p, pname) ? 1 : 0; break // VALIDATE_STATUS
|
|
523
|
+
case 0x8B85: val = (g.getProgramInfoLog(p) || '').length + 1; break // INFO_LOG_LENGTH
|
|
524
|
+
default: val = g.getProgramParameter(p, pname) ?? 0
|
|
525
|
+
}
|
|
526
|
+
this._wi32(paramsPtr, typeof val === 'boolean' ? (val ? 1 : 0) : val)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
glGetProgramInfoLog(id, maxLen, lengthPtr, logPtr) {
|
|
530
|
+
const log = this._gl.getProgramInfoLog(this._programs.get(id)) || ''
|
|
531
|
+
const bytes = enc.encode(log.substring(0, maxLen - 1) + '\0')
|
|
532
|
+
new Uint8Array(this._memory.buffer, logPtr, bytes.length).set(bytes)
|
|
533
|
+
this._wi32(lengthPtr, bytes.length - 1)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
glGetAttribLocation(progId, namePtr) {
|
|
537
|
+
return this._gl.getAttribLocation(this._programs.get(progId), this._readStr(namePtr))
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
glIsProgram(id) { return this._programs.has(id) ? 1 : 0 }
|
|
541
|
+
|
|
542
|
+
// ── Uniforms ─────────────────────────────────────────────────────────────────
|
|
543
|
+
|
|
544
|
+
glGetUniformLocation(progId, namePtr) {
|
|
545
|
+
const loc = this._gl.getUniformLocation(this._programs.get(progId), this._readStr(namePtr))
|
|
546
|
+
if (loc == null) return -1
|
|
547
|
+
const id = this._nextId++
|
|
548
|
+
this._ulocs.set(id, loc)
|
|
549
|
+
return id
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
_ul(id) { return id >= 0 ? this._ulocs.get(id) : null }
|
|
553
|
+
|
|
554
|
+
glUniform1f(l, x) { this._gl.uniform1f(this._ul(l), x) }
|
|
555
|
+
glUniform2f(l, x, y) { this._gl.uniform2f(this._ul(l), x, y) }
|
|
556
|
+
glUniform3f(l, x, y, z) { this._gl.uniform3f(this._ul(l), x, y, z) }
|
|
557
|
+
glUniform4f(l, x, y, z, w) { this._gl.uniform4f(this._ul(l), x, y, z, w) }
|
|
558
|
+
glUniform1i(l, x) { this._gl.uniform1i(this._ul(l), x) }
|
|
559
|
+
glUniform2i(l, x, y) { this._gl.uniform2i(this._ul(l), x, y) }
|
|
560
|
+
glUniform3i(l, x, y, z) { this._gl.uniform3i(this._ul(l), x, y, z) }
|
|
561
|
+
glUniform4i(l, x, y, z, w) { this._gl.uniform4i(this._ul(l), x, y, z, w) }
|
|
562
|
+
glUniform1ui(l, x) { this._gl.uniform1ui(this._ul(l), x) }
|
|
563
|
+
glUniform2ui(l, x, y) { this._gl.uniform2ui(this._ul(l), x, y) }
|
|
564
|
+
glUniform3ui(l, x, y, z) { this._gl.uniform3ui(this._ul(l), x, y, z) }
|
|
565
|
+
glUniform4ui(l, x, y, z, w) { this._gl.uniform4ui(this._ul(l), x, y, z, w) }
|
|
566
|
+
|
|
567
|
+
glUniform1fv(l, n, ptr) { this._gl.uniform1fv(this._ul(l), this._f32s(ptr, n*1)) }
|
|
568
|
+
glUniform2fv(l, n, ptr) { this._gl.uniform2fv(this._ul(l), this._f32s(ptr, n*2)) }
|
|
569
|
+
glUniform3fv(l, n, ptr) { this._gl.uniform3fv(this._ul(l), this._f32s(ptr, n*3)) }
|
|
570
|
+
glUniform4fv(l, n, ptr) { this._gl.uniform4fv(this._ul(l), this._f32s(ptr, n*4)) }
|
|
571
|
+
glUniform1iv(l, n, ptr) { this._gl.uniform1iv(this._ul(l), this._i32s(ptr, n*1)) }
|
|
572
|
+
glUniform2iv(l, n, ptr) { this._gl.uniform2iv(this._ul(l), this._i32s(ptr, n*2)) }
|
|
573
|
+
glUniform3iv(l, n, ptr) { this._gl.uniform3iv(this._ul(l), this._i32s(ptr, n*3)) }
|
|
574
|
+
glUniform4iv(l, n, ptr) { this._gl.uniform4iv(this._ul(l), this._i32s(ptr, n*4)) }
|
|
575
|
+
glUniform1uiv(l, n, ptr) { this._gl.uniform1uiv(this._ul(l), new Uint32Array(this._memory.buffer, ptr, n)) }
|
|
576
|
+
glUniform2uiv(l, n, ptr) { this._gl.uniform2uiv(this._ul(l), new Uint32Array(this._memory.buffer, ptr, n*2)) }
|
|
577
|
+
glUniform3uiv(l, n, ptr) { this._gl.uniform3uiv(this._ul(l), new Uint32Array(this._memory.buffer, ptr, n*3)) }
|
|
578
|
+
glUniform4uiv(l, n, ptr) { this._gl.uniform4uiv(this._ul(l), new Uint32Array(this._memory.buffer, ptr, n*4)) }
|
|
579
|
+
|
|
580
|
+
glUniformMatrix2fv(l, n, t, ptr) { this._gl.uniformMatrix2fv(this._ul(l), !!t, this._f32s(ptr, n*4)) }
|
|
581
|
+
glUniformMatrix3fv(l, n, t, ptr) { this._gl.uniformMatrix3fv(this._ul(l), !!t, this._f32s(ptr, n*9)) }
|
|
582
|
+
glUniformMatrix4fv(l, n, t, ptr) { this._gl.uniformMatrix4fv(this._ul(l), !!t, this._f32s(ptr, n*16)) }
|
|
583
|
+
glUniformMatrix2x3fv(l,n,t,ptr) { this._gl.uniformMatrix2x3fv(this._ul(l), !!t, this._f32s(ptr, n*6)) }
|
|
584
|
+
glUniformMatrix3x2fv(l,n,t,ptr) { this._gl.uniformMatrix3x2fv(this._ul(l), !!t, this._f32s(ptr, n*6)) }
|
|
585
|
+
glUniformMatrix2x4fv(l,n,t,ptr) { this._gl.uniformMatrix2x4fv(this._ul(l), !!t, this._f32s(ptr, n*8)) }
|
|
586
|
+
glUniformMatrix4x2fv(l,n,t,ptr) { this._gl.uniformMatrix4x2fv(this._ul(l), !!t, this._f32s(ptr, n*8)) }
|
|
587
|
+
glUniformMatrix3x4fv(l,n,t,ptr) { this._gl.uniformMatrix3x4fv(this._ul(l), !!t, this._f32s(ptr, n*12)) }
|
|
588
|
+
glUniformMatrix4x3fv(l,n,t,ptr) { this._gl.uniformMatrix4x3fv(this._ul(l), !!t, this._f32s(ptr, n*12)) }
|
|
589
|
+
|
|
590
|
+
// ── Textures ─────────────────────────────────────────────────────────────────
|
|
591
|
+
|
|
592
|
+
glGenTextures(n, ptr) {
|
|
593
|
+
for (let i = 0; i < n; i++) {
|
|
594
|
+
const id = this._nextId++
|
|
595
|
+
this._textures.set(id, this._gl.createTexture())
|
|
596
|
+
this._wi32(ptr + i * 4, id)
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
glDeleteTextures(n, ptr) {
|
|
601
|
+
for (let i = 0; i < n; i++) {
|
|
602
|
+
const id = this._view.getInt32(ptr + i * 4, true)
|
|
603
|
+
const t = this._textures.get(id)
|
|
604
|
+
if (t) { this._gl.deleteTexture(t); this._textures.delete(id) }
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
glBindTexture(target, id) {
|
|
609
|
+
this._gl.bindTexture(target, id ? this._textures.get(id) : null)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
glActiveTexture(unit) { this._gl.activeTexture(unit) }
|
|
613
|
+
|
|
614
|
+
glTexImage2D(target, level, internalFormat, width, height, border, format, type, dataPtr) {
|
|
615
|
+
const g = this._gl
|
|
616
|
+
if (!dataPtr) {
|
|
617
|
+
g.texImage2D(target, level, internalFormat, width, height, border, format, type, null)
|
|
618
|
+
return
|
|
619
|
+
}
|
|
620
|
+
const bytesPerPixel = this._glBytesPerPixel(format, type)
|
|
621
|
+
const size = width * height * bytesPerPixel
|
|
622
|
+
const src = this._typedView(type, dataPtr, size / this._glTypeSize(type))
|
|
623
|
+
g.texImage2D(target, level, internalFormat, width, height, border, format, type, src)
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
glTexImage3D(target, level, internalFmt, w, h, depth, border, fmt, type, dataPtr) {
|
|
627
|
+
const g = this._gl
|
|
628
|
+
if (!dataPtr) {
|
|
629
|
+
g.texImage3D(target, level, internalFmt, w, h, depth, border, fmt, type, null)
|
|
630
|
+
return
|
|
631
|
+
}
|
|
632
|
+
const bytesPerPixel = this._glBytesPerPixel(fmt, type)
|
|
633
|
+
const size = w * h * depth * bytesPerPixel
|
|
634
|
+
const src = this._typedView(type, dataPtr, size / this._glTypeSize(type))
|
|
635
|
+
g.texImage3D(target, level, internalFmt, w, h, depth, border, fmt, type, src)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
glTexSubImage2D(target, level, xoff, yoff, w, h, format, type, dataPtr) {
|
|
639
|
+
const size = w * h * this._glBytesPerPixel(format, type)
|
|
640
|
+
const src = this._typedView(type, dataPtr, size / this._glTypeSize(type))
|
|
641
|
+
this._gl.texSubImage2D(target, level, xoff, yoff, w, h, format, type, src)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
glCompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, dataPtr) {
|
|
645
|
+
this._gl.compressedTexImage2D(target, level, internalFormat, width, height, border, this._bytes(dataPtr, imageSize))
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
glCompressedTexSubImage2D(target, level, xoff, yoff, width, height, format, imageSize, dataPtr) {
|
|
649
|
+
this._gl.compressedTexSubImage2D(target, level, xoff, yoff, width, height, format, this._bytes(dataPtr, imageSize))
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
glTexParameteri(target, pname, param) { this._gl.texParameteri(target, pname, param) }
|
|
653
|
+
glTexParameterf(target, pname, param) { this._gl.texParameterf(target, pname, param) }
|
|
654
|
+
glGenerateMipmap(target) { this._gl.generateMipmap(target) }
|
|
655
|
+
glIsTexture(id) { return this._textures.has(id) ? 1 : 0 }
|
|
656
|
+
|
|
657
|
+
_glTypeSize(type) {
|
|
658
|
+
const map = { 0x1400:1, 0x1401:1, 0x1402:2, 0x1403:2, 0x1404:4, 0x1405:4, 0x1406:4 }
|
|
659
|
+
return map[type] ?? 1
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
_glBytesPerPixel(format, type) {
|
|
663
|
+
const channels = { 0x1903:1, 0x1902:1, 0x1904:3, 0x1908:4, 0x8227:2, 0x8228:4, 0x8D94:1, 0x8D95:2, 0x8D96:3, 0x8D97:4 }
|
|
664
|
+
return (channels[format] ?? 4) * this._glTypeSize(type)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
_typedView(type, ptr, count) {
|
|
668
|
+
switch (type) {
|
|
669
|
+
case 0x1406: return new Float32Array(this._memory.buffer, ptr, count) // FLOAT
|
|
670
|
+
case 0x1405: return new Uint32Array(this._memory.buffer, ptr, count) // UNSIGNED_INT
|
|
671
|
+
case 0x1404: return new Int32Array(this._memory.buffer, ptr, count) // INT
|
|
672
|
+
case 0x1403: return new Uint16Array(this._memory.buffer, ptr, count) // UNSIGNED_SHORT
|
|
673
|
+
case 0x1402: return new Int16Array(this._memory.buffer, ptr, count) // SHORT
|
|
674
|
+
case 0x1401: return new Uint8Array(this._memory.buffer, ptr, count) // UNSIGNED_BYTE
|
|
675
|
+
case 0x1400: return new Int8Array(this._memory.buffer, ptr, count) // BYTE
|
|
676
|
+
default: return new Uint8Array(this._memory.buffer, ptr, count)
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// ── Framebuffers ─────────────────────────────────────────────────────────────
|
|
681
|
+
|
|
682
|
+
glGenFramebuffers(n, ptr) {
|
|
683
|
+
for (let i = 0; i < n; i++) {
|
|
684
|
+
const id = this._nextId++
|
|
685
|
+
this._fbos.set(id, this._gl.createFramebuffer())
|
|
686
|
+
this._wi32(ptr + i * 4, id)
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
glDeleteFramebuffers(n, ptr) {
|
|
691
|
+
for (let i = 0; i < n; i++) {
|
|
692
|
+
const id = this._view.getInt32(ptr + i * 4, true)
|
|
693
|
+
const f = this._fbos.get(id)
|
|
694
|
+
if (f) { this._gl.deleteFramebuffer(f); this._fbos.delete(id) }
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
glBindFramebuffer(target, id) {
|
|
699
|
+
this._gl.bindFramebuffer(target, id ? this._fbos.get(id) : null)
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
glFramebufferTexture2D(target, attachment, texTarget, texId, level) {
|
|
703
|
+
this._gl.framebufferTexture2D(target, attachment, texTarget, this._textures.get(texId) ?? null, level)
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
glFramebufferRenderbuffer(target, attachment, rbTarget, rbId) {
|
|
707
|
+
this._gl.framebufferRenderbuffer(target, attachment, rbTarget, this._rbos.get(rbId) ?? null)
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
glCheckFramebufferStatus(target) { return this._gl.checkFramebufferStatus(target) }
|
|
711
|
+
glIsFramebuffer(id) { return this._fbos.has(id) ? 1 : 0 }
|
|
712
|
+
|
|
713
|
+
glGenRenderbuffers(n, ptr) {
|
|
714
|
+
for (let i = 0; i < n; i++) {
|
|
715
|
+
const id = this._nextId++
|
|
716
|
+
this._rbos.set(id, this._gl.createRenderbuffer())
|
|
717
|
+
this._wi32(ptr + i * 4, id)
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
glDeleteRenderbuffers(n, ptr) {
|
|
722
|
+
for (let i = 0; i < n; i++) {
|
|
723
|
+
const id = this._view.getInt32(ptr + i * 4, true)
|
|
724
|
+
const r = this._rbos.get(id)
|
|
725
|
+
if (r) { this._gl.deleteRenderbuffer(r); this._rbos.delete(id) }
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
glBindRenderbuffer(target, id) {
|
|
730
|
+
this._gl.bindRenderbuffer(target, id ? this._rbos.get(id) : null)
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
glRenderbufferStorage(target, internalFormat, w, h) {
|
|
734
|
+
this._gl.renderbufferStorage(target, internalFormat, w, h)
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
glRenderbufferStorageMultisample(target, samples, internalFormat, w, h) {
|
|
738
|
+
this._gl.renderbufferStorageMultisample(target, samples, internalFormat, w, h)
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
glIsRenderbuffer(id) { return this._rbos.has(id) ? 1 : 0 }
|
|
742
|
+
|
|
743
|
+
glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, mask, filter) {
|
|
744
|
+
this._gl.blitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, mask, filter)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// ── Drawing ──────────────────────────────────────────────────────────────────
|
|
748
|
+
|
|
749
|
+
glDrawArrays(mode, first, count) { this._gl.drawArrays(mode, first, count) }
|
|
750
|
+
|
|
751
|
+
glDrawElements(mode, count, type, offset) {
|
|
752
|
+
this._gl.drawElements(mode, count, type, offset)
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
glDrawArraysInstanced(mode, first, count, instanceCount) {
|
|
756
|
+
this._gl.drawArraysInstanced(mode, first, count, instanceCount)
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
glDrawElementsInstanced(mode, count, type, offset, instanceCount) {
|
|
760
|
+
this._gl.drawElementsInstanced(mode, count, type, offset, instanceCount)
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
glDrawRangeElements(mode, start, end, count, type, offset) {
|
|
764
|
+
this._gl.drawRangeElements(mode, start, end, count, type, offset)
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
glMultiDrawArrays(mode, firstsPtr, countsPtr, drawCount) {
|
|
768
|
+
const v = this._view
|
|
769
|
+
for (let i = 0; i < drawCount; i++) {
|
|
770
|
+
const first = v.getInt32(firstsPtr + i * 4, true)
|
|
771
|
+
const count = v.getInt32(countsPtr + i * 4, true)
|
|
772
|
+
this._gl.drawArrays(mode, first, count)
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// ── Read ─────────────────────────────────────────────────────────────────────
|
|
777
|
+
|
|
778
|
+
glReadPixels(x, y, w, h, format, type, dataPtr) {
|
|
779
|
+
const size = w * h * this._glBytesPerPixel(format, type)
|
|
780
|
+
const dst = this._typedView(type, dataPtr, size / this._glTypeSize(type))
|
|
781
|
+
this._gl.readPixels(x, y, w, h, format, type, dst)
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// ── Queries / getters ────────────────────────────────────────────────────────
|
|
785
|
+
|
|
786
|
+
glGetError() { return this._gl.getError() }
|
|
787
|
+
|
|
788
|
+
glGetIntegerv(pname, ptr) {
|
|
789
|
+
const val = this._gl.getParameter(pname)
|
|
790
|
+
if (val == null) return
|
|
791
|
+
const v = this._view
|
|
792
|
+
if (typeof val === 'number' || typeof val === 'boolean') {
|
|
793
|
+
v.setInt32(ptr, (typeof val === 'boolean' ? (val ? 1 : 0) : val) | 0, true)
|
|
794
|
+
} else if (val instanceof Int32Array || val instanceof Uint32Array) {
|
|
795
|
+
for (let i = 0; i < val.length; i++) v.setInt32(ptr + i * 4, val[i], true)
|
|
796
|
+
} else if (val instanceof Float32Array) {
|
|
797
|
+
for (let i = 0; i < val.length; i++) v.setInt32(ptr + i * 4, val[i] | 0, true)
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
glGetFloatv(pname, ptr) {
|
|
802
|
+
const val = this._gl.getParameter(pname)
|
|
803
|
+
if (val == null) return
|
|
804
|
+
const v = this._view
|
|
805
|
+
if (typeof val === 'number') {
|
|
806
|
+
v.setFloat32(ptr, val, true)
|
|
807
|
+
} else if (val instanceof Float32Array || val instanceof Int32Array) {
|
|
808
|
+
for (let i = 0; i < val.length; i++) v.setFloat32(ptr + i * 4, val[i], true)
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
glGetBooleanv(pname, ptr) {
|
|
813
|
+
const val = this._gl.getParameter(pname)
|
|
814
|
+
new Uint8Array(this._memory.buffer)[ptr] = val ? 1 : 0
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
glGetString(name) {
|
|
818
|
+
const g = this._gl
|
|
819
|
+
const str = {
|
|
820
|
+
0x1F00: 'WebGL', // GL_VENDOR
|
|
821
|
+
0x1F01: g.getParameter(g.RENDERER), // GL_RENDERER
|
|
822
|
+
0x1F02: '3.0.0 WebGL2', // GL_VERSION
|
|
823
|
+
0x1F03: '', // GL_EXTENSIONS
|
|
824
|
+
0x8B8C: '3.00 ES', // GL_SHADING_LANGUAGE_VERSION
|
|
825
|
+
}[name]
|
|
826
|
+
return str != null ? this._allocStr(str) : 0
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
glGetStringi(name, index) {
|
|
830
|
+
if (name === 0x1F03 /*GL_EXTENSIONS*/) {
|
|
831
|
+
const ext = this._gl.getSupportedExtensions() ?? []
|
|
832
|
+
return index < ext.length ? this._allocStr(ext[index]) : 0
|
|
833
|
+
}
|
|
834
|
+
return 0
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
glGetActiveUniform(progId, index, bufSize, lengthPtr, sizePtr, typePtr, namePtr) {
|
|
838
|
+
const info = this._gl.getActiveUniform(this._programs.get(progId), index)
|
|
839
|
+
if (!info) return
|
|
840
|
+
const bytes = enc.encode(info.name.substring(0, bufSize - 1) + '\0')
|
|
841
|
+
new Uint8Array(this._memory.buffer, namePtr, bytes.length).set(bytes)
|
|
842
|
+
this._wi32(lengthPtr, bytes.length - 1)
|
|
843
|
+
this._wi32(sizePtr, info.size)
|
|
844
|
+
this._wi32(typePtr, info.type)
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
glGetActiveAttrib(progId, index, bufSize, lengthPtr, sizePtr, typePtr, namePtr) {
|
|
848
|
+
const info = this._gl.getActiveAttrib(this._programs.get(progId), index)
|
|
849
|
+
if (!info) return
|
|
850
|
+
const bytes = enc.encode(info.name.substring(0, bufSize - 1) + '\0')
|
|
851
|
+
new Uint8Array(this._memory.buffer, namePtr, bytes.length).set(bytes)
|
|
852
|
+
this._wi32(lengthPtr, bytes.length - 1)
|
|
853
|
+
this._wi32(sizePtr, info.size)
|
|
854
|
+
this._wi32(typePtr, info.type)
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
glGetUniformfv(progId, locId, paramsPtr) {
|
|
858
|
+
const vals = this._gl.getUniform(this._programs.get(progId), this._ul(locId))
|
|
859
|
+
const v = this._view
|
|
860
|
+
if (typeof vals === 'number') { v.setFloat32(paramsPtr, vals, true); return }
|
|
861
|
+
if (vals?.length) for (let i = 0; i < vals.length; i++) v.setFloat32(paramsPtr + i*4, vals[i], true)
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
glGetUniformiv(progId, locId, paramsPtr) {
|
|
865
|
+
const vals = this._gl.getUniform(this._programs.get(progId), this._ul(locId))
|
|
866
|
+
const v = this._view
|
|
867
|
+
if (typeof vals === 'number') { v.setInt32(paramsPtr, vals, true); return }
|
|
868
|
+
if (vals?.length) for (let i = 0; i < vals.length; i++) v.setInt32(paramsPtr + i*4, vals[i], true)
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// ── Fixed-function matrix stack ──────────────────────────────────────────────
|
|
872
|
+
|
|
873
|
+
glMatrixMode(mode) { this._matMode = mode }
|
|
874
|
+
|
|
875
|
+
glLoadIdentity() {
|
|
876
|
+
const s = this._curMatrix()
|
|
877
|
+
s[s.length - 1] = m4id()
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
glLoadMatrixf(ptr) {
|
|
881
|
+
const s = this._curMatrix()
|
|
882
|
+
s[s.length - 1] = new Float32Array(this._memory.buffer.slice(ptr, ptr + 64))
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
glLoadMatrixd(ptr) {
|
|
886
|
+
const s = this._curMatrix()
|
|
887
|
+
const v = this._view
|
|
888
|
+
const m = new Float32Array(16)
|
|
889
|
+
for (let i = 0; i < 16; i++) m[i] = v.getFloat64(ptr + i * 8, true)
|
|
890
|
+
s[s.length - 1] = m
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
glMultMatrixf(ptr) {
|
|
894
|
+
const s = this._curMatrix()
|
|
895
|
+
const b = new Float32Array(this._memory.buffer.slice(ptr, ptr + 64))
|
|
896
|
+
s[s.length - 1] = m4mul(s[s.length - 1], b)
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
glMultMatrixd(ptr) {
|
|
900
|
+
const s = this._curMatrix()
|
|
901
|
+
const v = this._view
|
|
902
|
+
const b = new Float32Array(16)
|
|
903
|
+
for (let i = 0; i < 16; i++) b[i] = v.getFloat64(ptr + i * 8, true)
|
|
904
|
+
s[s.length - 1] = m4mul(s[s.length - 1], b)
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
glPushMatrix() {
|
|
908
|
+
const s = this._curMatrix()
|
|
909
|
+
s.push(new Float32Array(s[s.length - 1]))
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
glPopMatrix() {
|
|
913
|
+
const s = this._curMatrix()
|
|
914
|
+
if (s.length > 1) s.pop()
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
glOrtho(l, r, b, t, n, f) {
|
|
918
|
+
const s = this._curMatrix()
|
|
919
|
+
s[s.length - 1] = m4mul(s[s.length - 1], m4ortho(l, r, b, t, n, f))
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
glOrthof(l, r, b, t, n, f) { this.glOrtho(l, r, b, t, n, f) }
|
|
923
|
+
|
|
924
|
+
glFrustum(l, r, b, t, n, f) {
|
|
925
|
+
const s = this._curMatrix()
|
|
926
|
+
s[s.length - 1] = m4mul(s[s.length - 1], m4frustum(l, r, b, t, n, f))
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
glTranslatef(x, y, z) {
|
|
930
|
+
const s = this._curMatrix()
|
|
931
|
+
s[s.length - 1] = m4mul(s[s.length - 1], m4translate(x, y, z))
|
|
932
|
+
}
|
|
933
|
+
glTranslated(x, y, z) { this.glTranslatef(x, y, z) }
|
|
934
|
+
|
|
935
|
+
glScalef(x, y, z) {
|
|
936
|
+
const s = this._curMatrix()
|
|
937
|
+
s[s.length - 1] = m4mul(s[s.length - 1], m4scale(x, y, z))
|
|
938
|
+
}
|
|
939
|
+
glScaled(x, y, z) { this.glScalef(x, y, z) }
|
|
940
|
+
|
|
941
|
+
glRotatef(angle, x, y, z) {
|
|
942
|
+
const s = this._curMatrix()
|
|
943
|
+
s[s.length - 1] = m4mul(s[s.length - 1], m4rotate(angle, x, y, z))
|
|
944
|
+
}
|
|
945
|
+
glRotated(angle, x, y, z) { this.glRotatef(angle, x, y, z) }
|
|
946
|
+
|
|
947
|
+
// ── Immediate mode ────────────────────────────────────────────────────────────
|
|
948
|
+
|
|
949
|
+
glBegin(mode) {
|
|
950
|
+
this._imMode = mode
|
|
951
|
+
this._imVerts = []
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
glEnd() {
|
|
955
|
+
this._flushImmediate()
|
|
956
|
+
this._imMode = -1
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
_pushVert(x, y, z) {
|
|
960
|
+
this._imVerts.push(
|
|
961
|
+
x, y, z,
|
|
962
|
+
...this._imColor,
|
|
963
|
+
...this._imUV,
|
|
964
|
+
...this._imNormal
|
|
965
|
+
)
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
glVertex2f(x, y) { this._pushVert(x, y, 0) }
|
|
969
|
+
glVertex3f(x, y, z) { this._pushVert(x, y, z) }
|
|
970
|
+
glVertex4f(x, y, z, w) { this._pushVert(x/w, y/w, z/w) }
|
|
971
|
+
glVertex2i(x, y) { this._pushVert(x, y, 0) }
|
|
972
|
+
glVertex3i(x, y, z) { this._pushVert(x, y, z) }
|
|
973
|
+
glVertex2d(x, y) { this._pushVert(x, y, 0) }
|
|
974
|
+
glVertex3d(x, y, z) { this._pushVert(x, y, z) }
|
|
975
|
+
|
|
976
|
+
glVertex2fv(ptr) { const f = this._f32s(ptr, 2); this._pushVert(f[0], f[1], 0) }
|
|
977
|
+
glVertex3fv(ptr) { const f = this._f32s(ptr, 3); this._pushVert(f[0], f[1], f[2]) }
|
|
978
|
+
|
|
979
|
+
glColor3f(r, g, b) { this._imColor = [r, g, b, 1] }
|
|
980
|
+
glColor4f(r, g, b, a) { this._imColor = [r, g, b, a] }
|
|
981
|
+
glColor3d(r, g, b) { this._imColor = [r, g, b, 1] }
|
|
982
|
+
glColor4d(r, g, b, a) { this._imColor = [r, g, b, a] }
|
|
983
|
+
glColor3ub(r, g, b) { this._imColor = [r/255, g/255, b/255, 1] }
|
|
984
|
+
glColor4ub(r, g, b, a) { this._imColor = [r/255, g/255, b/255, a/255] }
|
|
985
|
+
glColor3i(r, g, b) { this._imColor = [r/0x7fffffff, g/0x7fffffff, b/0x7fffffff, 1] }
|
|
986
|
+
glColor4i(r, g, b, a) { this._imColor = [r/0x7fffffff, g/0x7fffffff, b/0x7fffffff, a/0x7fffffff] }
|
|
987
|
+
glColor3fv(ptr) { const f = this._f32s(ptr, 3); this._imColor = [f[0], f[1], f[2], 1] }
|
|
988
|
+
glColor4fv(ptr) { const f = this._f32s(ptr, 4); this._imColor = [f[0], f[1], f[2], f[3]] }
|
|
989
|
+
|
|
990
|
+
glNormal3f(x, y, z) { this._imNormal = [x, y, z] }
|
|
991
|
+
glNormal3d(x, y, z) { this._imNormal = [x, y, z] }
|
|
992
|
+
glNormal3fv(ptr) { const f = this._f32s(ptr, 3); this._imNormal = [f[0], f[1], f[2]] }
|
|
993
|
+
|
|
994
|
+
glTexCoord1f(s) { this._imUV = [s, 0] }
|
|
995
|
+
glTexCoord2f(s, t) { this._imUV = [s, t] }
|
|
996
|
+
glTexCoord3f(s, t, r) { this._imUV = [s, t] }
|
|
997
|
+
glTexCoord2fv(ptr) { const f = this._f32s(ptr, 2); this._imUV = [f[0], f[1]] }
|
|
998
|
+
|
|
999
|
+
glRectf(x1, y1, x2, y2) {
|
|
1000
|
+
this.glBegin(7 /*GL_QUADS*/)
|
|
1001
|
+
this._pushVert(x1, y1, 0); this._pushVert(x2, y1, 0)
|
|
1002
|
+
this._pushVert(x2, y2, 0); this._pushVert(x1, y2, 0)
|
|
1003
|
+
this.glEnd()
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
glCopyTexImage2D(target, level, internalFormat, x, y, w, h, border) {
|
|
1007
|
+
this._gl.copyTexImage2D(target, level, internalFormat, x, y, w, h, border)
|
|
1008
|
+
}
|
|
1009
|
+
glCopyTexSubImage2D(target, level, xoff, yoff, x, y, w, h) {
|
|
1010
|
+
this._gl.copyTexSubImage2D(target, level, xoff, yoff, x, y, w, h)
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/*
|
|
1014
|
+
|
|
1015
|
+
// ── Legacy stubs (no-ops or trivial mappings) ─────────────────────────────────
|
|
1016
|
+
|
|
1017
|
+
glEnableClientState() {}
|
|
1018
|
+
glDisableClientState() {}
|
|
1019
|
+
glClientActiveTexture() {}
|
|
1020
|
+
glShadeModel() {}
|
|
1021
|
+
glTexEnvi() {}
|
|
1022
|
+
glTexEnvf() {}
|
|
1023
|
+
glTexEnvfv() {}
|
|
1024
|
+
glFogi() {}
|
|
1025
|
+
glFogf() {}
|
|
1026
|
+
glFogfv() {}
|
|
1027
|
+
glFogiv() {}
|
|
1028
|
+
glAlphaFunc() {} // emulate via discard in shader if needed
|
|
1029
|
+
glLightf() {}
|
|
1030
|
+
glLightfv() {}
|
|
1031
|
+
glLighti() {}
|
|
1032
|
+
glLightiv() {}
|
|
1033
|
+
glMaterialf() {}
|
|
1034
|
+
glMaterialfv() {}
|
|
1035
|
+
glMateriali() {}
|
|
1036
|
+
glMaterialiv() {}
|
|
1037
|
+
glPointSize() {}
|
|
1038
|
+
glColorMaterial() {}
|
|
1039
|
+
glLogicOp() {}
|
|
1040
|
+
glPassThrough() {}
|
|
1041
|
+
glSelectBuffer() {}
|
|
1042
|
+
glFeedbackBuffer() {}
|
|
1043
|
+
glRenderMode() { return 0 }
|
|
1044
|
+
glInitNames() {}
|
|
1045
|
+
glPushName() {}
|
|
1046
|
+
glPopName() {}
|
|
1047
|
+
glLoadName() {}
|
|
1048
|
+
glPushAttrib() {}
|
|
1049
|
+
glPopAttrib() {}
|
|
1050
|
+
glPushClientAttrib() {}
|
|
1051
|
+
glPopClientAttrib() {}
|
|
1052
|
+
|
|
1053
|
+
// Legacy array drawing (client-side arrays — use VBOs instead)
|
|
1054
|
+
glVertexPointer() {}
|
|
1055
|
+
glColorPointer() {}
|
|
1056
|
+
glNormalPointer() {}
|
|
1057
|
+
glTexCoordPointer() {}
|
|
1058
|
+
glIndexPointer() {}
|
|
1059
|
+
|
|
1060
|
+
// ── Misc ─────────────────────────────────────────────────────────────────────
|
|
1061
|
+
|
|
1062
|
+
glLineStipple() {}
|
|
1063
|
+
glPolygonStipple() {}
|
|
1064
|
+
glPolygonMode() {} // no equivalent in WebGL2
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
*/
|
|
1068
|
+
}
|