Spaces:
Running
Running
| // This is an OpenGL implementation for SqueakJS using WebGL. | |
| // It is very much incomplete and currently only implements | |
| // the subset of OpenGL that is used by Croquet Jasmine, | |
| // but could be extended to support more. | |
| // The functions are invoked via FFI, which takes care of | |
| // converting the arguments and return values between JS | |
| // and Smalltalk. | |
| // The OpenGL context is global and created by B3DAcceleratorPlugin. | |
| // Context switching is done by B3DAcceleratorPlugin.makeCurrent(). | |
| // helpful constant lookup: | |
| // https://javagl.github.io/GLConstantsTranslator/GLConstantsTranslator.html | |
| // TODO | |
| // [X] implement draw arrays | |
| // [X] implement draw elements | |
| // [ ] implement vertex buffer objects | |
| // [X] implement material + lighting | |
| // [X] implement clip planes | |
| // [X] implement fog | |
| // [ ] implement tex coord gen | |
| // [ ] fix glBitmap for size other than 640x480 (also, make pixel perfect) | |
| // [ ] optimize list compilation glBegin/glEnd | |
| // [ ] implement light attenuation | |
| // [ ] implement spot lights | |
| // [ ] implement color material | |
| // [ ] emulate glLineWidth (WebGL usually only supports 1px lines) | |
| // e.g. using https://wwwtyro.net/2019/11/18/instanced-lines.html | |
| // [ ] full OpenGL 1.0 support | |
| // [ ] full OpenGL 1.1 support | |
| // [ ] full OpenGL 1.2 support | |
| // [ ] full OpenGL 1.3 support | |
| // [ ] full OpenGL 1.4 support | |
| // [ ] full OpenGL 1.5 support | |
| // [ ] some extensions? | |
| // OpenGL constants (many missing in WebGL) | |
| var GL; | |
| function OpenGL() { | |
| "use strict"; | |
| var DEBUG = 0; | |
| // 0 = off | |
| // 1 = some (errors, warnings) | |
| // 2 = lots (function calls) | |
| // 3 = all (call details) | |
| var identity = new Float32Array([ | |
| 1, 0, 0, 0, | |
| 0, 1, 0, 0, | |
| 0, 0, 1, 0, | |
| 0, 0, 0, 1, | |
| ]); | |
| // Primitive attributes for glBegin/glEnd | |
| var flagCounter = 0; | |
| var HAS_NORMAL = 1 << flagCounter++; | |
| var HAS_COLOR = 1 << flagCounter++; | |
| var HAS_TEXCOORD = 1 << flagCounter++; | |
| // additional flags for selecting shader | |
| var USE_TEXTURE = 1 << flagCounter++; | |
| var USE_ALPHA_TEST = 1 << flagCounter++; | |
| var USE_POINT_SIZE = 1 << flagCounter++; | |
| var NUM_LIGHTS_MASK = (1 << flagCounter++) // 3 bits for number of lights (0-7) | |
| + (1 << flagCounter++) | |
| + (1 << flagCounter++); | |
| var NUM_CLIP_PLANES_MASK = (1 << flagCounter++) // 3 bits for number of clip planes (0-5) | |
| + (1 << flagCounter++) | |
| + (1 << flagCounter++); | |
| var FOG_MASK = (1 << flagCounter++) // 2 bits for fog mode (off, linear, exp, exp2) | |
| + (1 << flagCounter++); | |
| // this math is silly but fun ... | |
| var NUM_LIGHTS_SHIFT = Math.floor(Math.log2(NUM_LIGHTS_MASK)) - 2; | |
| var MAX_LIGHTS = (NUM_LIGHTS_MASK >> NUM_LIGHTS_SHIFT) + 1; | |
| var ANY_LIGHTS = (MAX_LIGHTS-1) << NUM_LIGHTS_SHIFT; | |
| if (ANY_LIGHTS !== NUM_LIGHTS_MASK) throw Error("OpenGL: bad NUM_LIGHTS_MASK"); | |
| var NUM_CLIP_PLANES_SHIFT = Math.floor(Math.log2(NUM_CLIP_PLANES_MASK)) - 2; | |
| var MAX_CLIP_PLANES = (NUM_CLIP_PLANES_MASK >> NUM_CLIP_PLANES_SHIFT) + 1; | |
| var ANY_CLIP_PLANES = (MAX_CLIP_PLANES-1) << NUM_CLIP_PLANES_SHIFT; | |
| if (ANY_CLIP_PLANES !== NUM_CLIP_PLANES_MASK) throw Error("OpenGL: bad NUM_CLIP_PLANES_MASK"); | |
| var FOG_SHIFT = Math.floor(Math.log2(FOG_MASK)) - 1; | |
| var MAX_FOG = (FOG_MASK >> FOG_SHIFT) + 1; | |
| var ANY_FOG = (MAX_FOG-1) << FOG_SHIFT; | |
| if (ANY_FOG !== FOG_MASK) throw Error("OpenGL: bad ANY_FOG"); | |
| var NO_FOG = 0; | |
| var LINEAR_FOG = 1; | |
| var EXP_FOG = 2; | |
| var EXP2_FOG = 3; | |
| var gl; // the emulated OpenGL state | |
| var webgl; // the actual WebGL context | |
| return { | |
| getModuleName: function() { return 'libGL.so (SqueakJS)'; }, | |
| setInterpreter: function(anInterpreterProxy) { | |
| this.vm = anInterpreterProxy.vm; | |
| this.ffi = this.vm.primHandler; | |
| return true; | |
| }, | |
| ffiFunctionNotFoundHandler: function(name, args) { | |
| this.vm.warnOnce("OpenGL: UNIMPLEMENTED (missing) " + name); | |
| if (DEBUG > 0) debugger; | |
| return null; // do not fail but return nil | |
| }, | |
| initialiseModule: function() { | |
| DEBUG > 1 && console.log("OpenGL: initialiseModule"); | |
| if (!GL) initGLConstants(); | |
| // connect to B3DAcceleratorPlugin to get WebGL context | |
| var modules = SqueakJS.vm.primHandler.loadedModules; | |
| var B3DAcceleratorPlugin = modules['B3DAcceleratorPlugin']; | |
| if (!B3DAcceleratorPlugin) throw Error("OpenGL: B3DAcceleratorPlugin not loaded"); | |
| this.GL = GL; | |
| B3DAcceleratorPlugin.setOpenGL(this); // will call makeCurrent() | |
| }, | |
| makeCurrent: function(renderer) { | |
| if (webgl === renderer.webgl) return; // already current | |
| DEBUG > 1 && console.log("OpenGL: makeCurrent", renderer); | |
| webgl = renderer.webgl; | |
| gl = renderer.opengl; | |
| if (!gl) renderer.opengl = this.initGL(); | |
| }, | |
| initGL: function() { | |
| DEBUG > 0 && console.log("OpenGL: initGL"); | |
| // if webgl-lint is loaded, configure it | |
| const ext = webgl.getExtension('GMAN_debug_helper'); | |
| if (ext) ext.setConfiguration({ | |
| throwOnError: false, | |
| }); | |
| // if Spector script is loaded, capture WebGL calls | |
| if (typeof SPECTOR !== "undefined") { | |
| var spector = new SPECTOR.Spector(); | |
| spector.captureContext(webgl); | |
| spector.displayUI(); | |
| } | |
| // initialize emulated OpenGL state | |
| gl = { | |
| alphaTest: false, | |
| alphaFunc: null, | |
| alphaRef: 0, | |
| extensions: "GL_ARB_texture_non_power_of_two GL_SGIS_generate_mipmap GL_ARB_transpose_matrix", | |
| color: new Float32Array(4), | |
| normal: new Float32Array([0, 0, 1]), | |
| texCoord: new Float32Array(2), | |
| primitive: null, // for glBegin/glEnd | |
| primitiveAttrs: 0, // for glVertex | |
| clipPlanes: [], // clip plane equations | |
| clientState: {}, // enabled arrays by attr | |
| fogMode: GL.EXP, // fog mode | |
| fogEnabled: false, // fog enabled | |
| fogDensity: 1, // fog density | |
| fogStart: 0, // fog start | |
| fogEnd: 1, // fog end | |
| fogColor: new Float32Array([0, 0, 0, 0]), // fog color | |
| fogHint: GL.DONT_CARE, // fog hint | |
| shaders: {}, // shader programs by attr/flags | |
| matrixMode: 0, // current matrix mode | |
| matrices: {}, // matrix stacks by mode | |
| matrix: null, // current matrix (matrices[mode][0]) | |
| lightingEnabled: false, | |
| lights: [], // light states | |
| lightModelAmbient: null, // scene ambient color | |
| material: null, // material state | |
| textureIdGen: 0, // texture id generator | |
| textures: {}, // webgl texture objects by id | |
| texture: null, // texture | |
| textureEnabled: false, // texture enabled | |
| textureEnvMode: GL.MODULATE, // texture environment mode | |
| listIdGen: 0, // display list id generator | |
| lists: {}, // display lists by id | |
| list: null, // current display list | |
| listMode: 0, // current display list mode | |
| listBase: 0, // base for glCallLists | |
| pixelStoreUnpackRowLength: 0, | |
| pixelStoreUnpackSkipRows: 0, | |
| pixelStoreUnpackSkipPixels: 0, | |
| rasterPos: new Float32Array(4), | |
| rasterColor: new Float32Array([1, 1, 1, 1]), | |
| bitmapTexture: null, // texture for glBitmap | |
| bitmapVertexBuffer: null, // vertex buffer for glBitmap | |
| bitmapShader: { // shader program for glBitmap | |
| program: null, | |
| locations: {}, | |
| }, // shader for glBitmap | |
| vendor: "Codefrau", | |
| renderer: "SqueakJS", | |
| version: "1.0", | |
| viewport: new Int32Array([0, 0, 0, 0]), | |
| depthRange: new Float32Array([0, 1]), | |
| }; | |
| // set initial state | |
| gl.matrices[GL.MODELVIEW] = [new Float32Array(identity)]; | |
| gl.matrices[GL.PROJECTION] = [new Float32Array(identity)]; | |
| gl.matrices[GL.TEXTURE] = [new Float32Array(identity)]; | |
| gl.matrixMode = GL.MODELVIEW; | |
| gl.matrix = gl.matrices[gl.matrixMode][0]; | |
| gl.color.set([1, 1, 1, 1]); | |
| for (var i = 0; i < MAX_CLIP_PLANES; i++) { | |
| gl.clipPlanes[i] = { | |
| enabled: false, | |
| equation: new Float32Array([0, 0, 0, 0]), | |
| }; | |
| } | |
| for (var i = 0; i < MAX_LIGHTS; i++) { | |
| gl.lights[i] = { | |
| enabled: false, | |
| ambient: new Float32Array([0, 0, 0, 1]), | |
| diffuse: new Float32Array(i === 0 ? [1, 1, 1, 1] : [0, 0, 0, 1]), | |
| specular: new Float32Array(i === 0 ? [1, 1, 1, 1] : [0, 0, 0, 1]), | |
| position: new Float32Array([0, 0, 1, 0]), | |
| }; | |
| } | |
| gl.lightModelAmbient = new Float32Array([0.2, 0.2, 0.2, 1]); | |
| gl.material = { | |
| ambient: new Float32Array([0.2, 0.2, 0.2, 1]), | |
| diffuse: new Float32Array([0.8, 0.8, 0.8, 1]), | |
| specular: new Float32Array([0, 0, 0, 1]), | |
| emission: new Float32Array([0, 0, 0, 1]), | |
| shininess: 0, | |
| }; | |
| var clientStates = ["vertexArray", "normalArray", "colorArray", "textureCoordArray"]; | |
| for (var i = 0; i < clientStates.length; i++) { | |
| var attr = clientStates[i]; | |
| gl.clientState[attr] = { | |
| enabled: false, | |
| size: 0, | |
| type: GL.FLOAT, | |
| stride: 0, | |
| pointer: null, | |
| // binding: null, TODO: support VBOs | |
| } | |
| } | |
| return gl; | |
| }, | |
| destroyGL: function(renderer) { | |
| DEBUG > 0 && console.log("OpenGL: destroyGL"); | |
| // TODO: delete textures, arrays, shaders? | |
| renderer.opengl = null; | |
| webgl = null; | |
| gl = null; | |
| }, | |
| // FFI functions get JS args, return JS result | |
| addToList: function(name, args) { | |
| if (!gl.list) return false; | |
| gl.list.commands.push({name: name, args: args}); | |
| if (gl.listMode === GL.COMPILE) { | |
| DEBUG > 1 && console.log("[COMPILE]", name, args); | |
| return true; | |
| } | |
| return false; | |
| }, | |
| glAlphaFunc: function(func, ref) { | |
| if (gl.listMode && this.addToList("glAlphaFunc", [func, ref])) return; | |
| DEBUG > 1 && console.log("glAlphaFunc", GL_Symbol(func), ref); | |
| gl.alphaFunc = func; | |
| gl.alphaRef = ref; | |
| }, | |
| glBegin: function(mode) { | |
| if (gl.listMode && this.addToList("glBegin", [mode])) return; | |
| DEBUG > 1 && console.log("glBegin", GL_Symbol(mode, 'POINTS')); | |
| gl.primitive = { | |
| mode: mode, | |
| vertices: [], | |
| vertexSize: 0, | |
| vertexAttrs: 0, | |
| } | |
| gl.primitiveAttrs = 0; | |
| }, | |
| glBindTexture: function(target, texture) { | |
| if (gl.listMode && this.addToList("glBindTexture", [target, texture])) return; | |
| DEBUG > 1 && console.log("glBindTexture", GL_Symbol(target), texture); | |
| var textureObj = gl.textures[texture]; | |
| if (!textureObj) throw Error("OpenGL: texture not found"); | |
| webgl.bindTexture(target, textureObj); | |
| gl.texture = textureObj; | |
| }, | |
| glBitmap: function(width, height, xorig, yorig, xmove, ymove, bitmap) { | |
| // bitmap is supposed to be declared as "GLubyte*" per OpenGL spec, | |
| // which the FFI would convert to Uint8Array for us. However, the | |
| // image FFI declaration uses "void*", probably because it makes no | |
| // difference in C, a pointer is a pointer. In JS, we get an | |
| // ArrayBuffer for "void*" so we need to convert it to Uint8Array | |
| // ourselves. | |
| if (!bitmap.buffer) bitmap = new Uint8Array(bitmap); | |
| if (gl.listMode && this.addToList("glBitmap", [width, height, xorig, yorig, xmove, ymove, bitmap])) return; | |
| DEBUG > 1 && console.log("glBitmap", width, height, xorig, yorig, xmove, ymove, bitmap); | |
| if (width > 0 && height > 0) { | |
| // we need to convert the 1-bit deep bitmap to a 1-byte | |
| // per pixel texture in ALPHA format, with the bitmap | |
| // mapping 0-bits to transparent, 1-bits to opaque, | |
| // and then draw it as a textured quad covering the viewport | |
| var texels = new Uint8Array(width * height); | |
| var bytesPerRow = Math.ceil(width / 32) * 4; | |
| for (var y = 0; y < height; y++) { | |
| var byteIndex = y * bytesPerRow; | |
| var bitIndex = 7; | |
| for (var x = 0; x < width; x++) { | |
| var bit = bitmap[byteIndex] & (1 << bitIndex); | |
| if (bit) texels[y * width + x] = 255; | |
| bitIndex--; | |
| if (bitIndex < 0) { | |
| byteIndex++; | |
| bitIndex = 7; | |
| } | |
| } | |
| } | |
| // debug: print bitmap | |
| // s=''; for (y = height -1 ; y >= 0; y--) { for (x = 0; x < width; x++) s += texels[y * width + x] ? '⬛️' : '⬜️'; s+='\n'}; console.log(s) | |
| var texture = gl.bitmapTexture; | |
| if (!texture) { | |
| texture = gl.bitmapTexture = webgl.createTexture(); | |
| webgl.bindTexture(webgl.TEXTURE_2D, texture); | |
| webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.LINEAR); | |
| webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.LINEAR); | |
| webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE); | |
| webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE); | |
| } else { | |
| webgl.bindTexture(webgl.TEXTURE_2D, texture); | |
| } | |
| webgl.pixelStorei(webgl.UNPACK_ALIGNMENT, 1); | |
| webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.ALPHA, width, height, 0, webgl.ALPHA, webgl.UNSIGNED_BYTE, texels); | |
| webgl.pixelStorei(webgl.UNPACK_ALIGNMENT, 4); | |
| webgl.disable(webgl.CULL_FACE); | |
| webgl.disable(webgl.DEPTH_TEST); | |
| webgl.disable(webgl.BLEND); | |
| webgl.colorMask(true, true, true, true); | |
| webgl.viewport(0, 0, webgl.drawingBufferWidth, webgl.drawingBufferHeight); | |
| var vertexBuffer = gl.bitmapVertexBuffer; | |
| if (!vertexBuffer) { | |
| var vertices = new Float32Array([ | |
| 0, 0, | |
| 1, 0, | |
| 0, 1, | |
| 1, 1, | |
| ]); | |
| vertexBuffer = gl.bitmapVertexBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, vertices, webgl.STATIC_DRAW); | |
| } else { | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer); | |
| } | |
| var shader = gl.bitmapShader; | |
| if (!shader.program) { | |
| shader.program = webgl.createProgram(); | |
| var vs = webgl.createShader(webgl.VERTEX_SHADER); | |
| webgl.shaderSource(vs, ` | |
| attribute vec2 a_position; | |
| uniform vec3 u_raster; | |
| uniform vec2 u_rasterOffset; | |
| uniform vec2 u_rasterScale; | |
| uniform vec2 u_translate; | |
| uniform vec2 u_scale; | |
| varying vec2 v_texcoord; | |
| void main() { | |
| vec2 raster = u_raster.xy * u_rasterScale + u_rasterOffset; | |
| vec2 pos = raster + a_position * u_scale + u_translate; | |
| gl_Position = vec4(pos, u_raster.z, 1); | |
| v_texcoord = a_position; | |
| } | |
| `); | |
| webgl.compileShader(vs); | |
| if (!webgl.getShaderParameter(vs, webgl.COMPILE_STATUS)) { | |
| console.error("OpenGL: vertex shader compile error: " + webgl.getShaderInfoLog(vs)); | |
| debugger; | |
| return; | |
| } | |
| var fs = webgl.createShader(webgl.FRAGMENT_SHADER); | |
| webgl.shaderSource(fs, ` | |
| precision mediump float; | |
| uniform sampler2D u_texture; | |
| uniform vec4 u_color; | |
| varying vec2 v_texcoord; | |
| void main() { | |
| float alpha = texture2D(u_texture, v_texcoord).a; | |
| if (alpha < 0.5) discard; | |
| gl_FragColor = u_color; | |
| } | |
| `); | |
| webgl.compileShader(fs); | |
| if (!webgl.getShaderParameter(fs, webgl.COMPILE_STATUS)) { | |
| console.error("OpenGL: fragment shader compile error: " + webgl.getShaderInfoLog(fs)); | |
| debugger; | |
| return; | |
| } | |
| webgl.attachShader(shader.program, vs); | |
| webgl.attachShader(shader.program, fs); | |
| webgl.linkProgram(shader.program); | |
| if (!webgl.getProgramParameter(shader.program, webgl.LINK_STATUS)) { | |
| console.error("OpenGL: shader link error: " + webgl.getProgramInfoLog(shader.program)); | |
| debugger | |
| return; | |
| } | |
| shader.locations = { | |
| a_position: webgl.getAttribLocation(shader.program, "a_position"), | |
| u_texture: webgl.getUniformLocation(shader.program, "u_texture"), | |
| u_color: webgl.getUniformLocation(shader.program, "u_color"), | |
| u_raster: webgl.getUniformLocation(shader.program, "u_raster"), | |
| u_rasterOffset: webgl.getUniformLocation(shader.program, "u_rasterOffset"), | |
| u_rasterScale: webgl.getUniformLocation(shader.program, "u_rasterScale"), | |
| u_translate: webgl.getUniformLocation(shader.program, "u_translate"), | |
| u_scale: webgl.getUniformLocation(shader.program, "u_scale"), | |
| }; | |
| } | |
| webgl.useProgram(shader.program); | |
| webgl.enableVertexAttribArray(shader.locations.a_position); | |
| webgl.vertexAttribPointer(shader.locations.a_position, 2, webgl.FLOAT, false, 0, 0); | |
| webgl.uniform1i(shader.locations.u_texture, 0); | |
| webgl.uniform4fv(shader.locations.u_color, gl.rasterColor); | |
| var w = webgl.drawingBufferWidth; | |
| var h = webgl.drawingBufferHeight; | |
| // this still isn't pixel perfect. Appears to work best for 640x480 | |
| // but not when changing the extent?! Weird. Also, some letters are still | |
| // cut off (like "m"). | |
| if (!this.bitmapRasterScale) this.bitmapRasterScale = [2/w, 2/h]; | |
| if (!this.bitmapScale) this.bitmapScale = [2*width/w, 2*height/h]; | |
| if (!this.bitmapTranslate) this.bitmapTranslate = [0, 0]; | |
| if (!this.bitmapRasterOffset) this.bitmapRasterOffset = [-1, -1]; | |
| // the properties above are written to allow intereactive debugging | |
| webgl.uniform3f(shader.locations.u_raster, gl.rasterPos[0] - xorig, gl.rasterPos[1] - yorig, gl.rasterPos[2]); | |
| webgl.uniform2fv(shader.locations.u_rasterOffset, this.bitmapRasterOffset); | |
| webgl.uniform2fv(shader.locations.u_rasterScale, this.bitmapRasterScale); | |
| webgl.uniform2fv(shader.locations.u_translate, this.bitmapTranslate); | |
| webgl.uniform2fv(shader.locations.u_scale, this.bitmapScale); | |
| webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, 4); | |
| webgl.disableVertexAttribArray(shader.locations.a_position); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, null); | |
| webgl.useProgram(null); | |
| webgl.bindTexture(webgl.TEXTURE_2D, null); | |
| webgl.enable(webgl.CULL_FACE); | |
| webgl.enable(webgl.DEPTH_TEST); | |
| webgl.enable(webgl.BLEND); | |
| } | |
| gl.rasterPos[0] += xmove; | |
| gl.rasterPos[1] += ymove; | |
| }, | |
| glBlendFunc: function(sfactor, dfactor) { | |
| if (gl.listMode && this.addToList("glBlendFunc", [sfactor, dfactor])) return; | |
| DEBUG > 1 && console.log("glBlendFunc", GL_Symbol(sfactor), GL_Symbol(dfactor)); | |
| webgl.blendFunc(sfactor, dfactor); | |
| }, | |
| glCallList: function(list) { | |
| if (gl.listMode && this.addToList("glCallList", [list])) return; | |
| DEBUG > 1 && console.log("glCallList", list, "START"); | |
| this.executeList(list); | |
| DEBUG > 1 && console.log("glCallList", list, "DONE"); | |
| }, | |
| glCallLists: function(n, type, lists) { | |
| if (gl.listMode && this.addToList("glCallLists", [n, type, lists])) return; | |
| DEBUG > 1 && console.log("glCallLists", n, GL_Symbol(type), lists); | |
| var array; | |
| switch (type) { | |
| case GL.BYTE: | |
| array = new Int8Array(lists); | |
| break; | |
| case GL.UNSIGNED_BYTE: | |
| array = new Uint8Array(lists); | |
| break; | |
| case GL.INT: | |
| array = new Int32Array(lists); | |
| break; | |
| case GL.UNSIGNED_INT: | |
| array = new Uint32Array(lists); | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glCallLists type", GL_Symbol(type)) | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glCallLists type " + GL_Symbol(type)); | |
| return; | |
| } | |
| for (var i = 0; i < n; i++) { | |
| var list = gl.listBase + array[i]; | |
| this.executeList(list); | |
| } | |
| }, | |
| glClear: function(mask) { | |
| if (gl.listMode && this.addToList("glClear", [mask])) return; | |
| var maskString = ""; | |
| if (mask & webgl.COLOR_BUFFER_BIT) maskString += " COLOR"; | |
| if (mask & webgl.DEPTH_BUFFER_BIT) maskString += " DEPTH"; | |
| if (mask & webgl.STENCIL_BUFFER_BIT) maskString += " STENCIL"; | |
| DEBUG > 1 && console.log("glClear"+ maskString); | |
| webgl.clear(mask); | |
| // B3DAcceleratorPlugin will call vm.breakNow() | |
| // to emulate double buffering (which will return | |
| // control to the browser which will flush the canvas). | |
| // We discourage breaking until then to avoid flicker | |
| // glClear is a good place for that since it's usually | |
| // called at least once per frame | |
| this.vm.breakAfter(500); | |
| }, | |
| glClearColor: function(red, green, blue, alpha) { | |
| if (gl.listMode && this.addToList("glClearColor", [red, green, blue, alpha])) return; | |
| DEBUG > 1 && console.log("glClearColor", red, green, blue, alpha); | |
| webgl.clearColor(red, green, blue, alpha); | |
| }, | |
| glColor3f: function(red, green, blue) { | |
| if (gl.listMode && this.addToList("glColor3f", [red, green, blue])) return; | |
| DEBUG > 1 && console.log("glColor3f", red, green, blue); | |
| gl.color[0] = red; | |
| gl.color[1] = green; | |
| gl.color[2] = blue; | |
| gl.color[3] = 1; | |
| gl.primitiveAttrs |= HAS_COLOR; | |
| }, | |
| glColor3fv: function(v) { | |
| if (gl.listMode && this.addToList("glColor3fv", [v.slice()])) return; | |
| DEBUG > 1 && console.log("glColor3fv", Array.from(v)); | |
| gl.color.set(v); | |
| gl.color[3] = 1; | |
| gl.primitiveAttrs |= HAS_COLOR; | |
| }, | |
| glColor4d: function(red, green, blue, alpha) { | |
| if (gl.listMode && this.addToList("glColor4d", [red, green, blue, alpha])) return; | |
| DEBUG > 1 && console.log("glColor4d", red, green, blue, alpha); | |
| gl.color[0] = red; | |
| gl.color[1] = green; | |
| gl.color[2] = blue; | |
| gl.color[3] = alpha; | |
| gl.primitiveAttrs |= HAS_COLOR; | |
| }, | |
| glColor4f: function(red, green, blue, alpha) { | |
| if (gl.listMode && this.addToList("glColor4f", [red, green, blue, alpha])) return; | |
| DEBUG > 1 && console.log("glColor4f", red, green, blue, alpha); | |
| gl.color[0] = red; | |
| gl.color[1] = green; | |
| gl.color[2] = blue; | |
| gl.color[3] = alpha; | |
| gl.primitiveAttrs |= HAS_COLOR; | |
| }, | |
| glColor4fv: function(v) { | |
| if (gl.listMode && this.addToList("glColor4fv", [v.slice()])) return; | |
| DEBUG > 1 && console.log("glColor4fv", Array.from(v)); | |
| gl.color.set(v); | |
| gl.primitiveAttrs |= HAS_COLOR; | |
| }, | |
| glColorPointer: function(size, type, stride, pointer) { | |
| if (gl.listMode && this.addToList("glColorPointer", [size, type, stride, pointer])) return; | |
| DEBUG > 1 && console.log("glColorPointer", size, GL_Symbol(type), stride, pointer); | |
| gl.clientState.colorArray.size = size; | |
| gl.clientState.colorArray.type = type; | |
| gl.clientState.colorArray.stride = stride; | |
| gl.clientState.colorArray.pointer = pointer; | |
| }, | |
| glColorMask: function(red, green, blue, alpha) { | |
| if (gl.listMode && this.addToList("glColorMask", [red, green, blue, alpha])) return; | |
| DEBUG > 1 && console.log("glColorMask", red, green, blue, alpha); | |
| webgl.colorMask(red, green, blue, alpha); | |
| }, | |
| glClipPlane: function(plane, equation) { | |
| if (gl.listMode && this.addToList("glClipPlane", [plane, equation])) return; | |
| DEBUG > 1 && console.log("glClipPlane", GL_Symbol(plane), Array.from(equation)); | |
| var clipPlane = gl.clipPlanes[plane - GL.CLIP_PLANE0]; | |
| // multiply by inverse of modelview matrix | |
| var m = new Float32Array(16); | |
| invertMatrix(gl.matrices[GL.MODELVIEW][0], m); | |
| transposeMatrix(m); | |
| multVec4(m, equation, clipPlane.equation); | |
| }, | |
| glDeleteLists: function(list, range) { | |
| DEBUG > 1 && console.log("glDeleteLists", list, range); | |
| for (var i = 0; i < range; i++) { | |
| delete gl.lists[list + i]; | |
| } | |
| }, | |
| glDeleteTextures: function(n, textures) { | |
| DEBUG > 1 && console.log("glDeleteTextures", n, Array.from(textures)); | |
| for (var i = 0; i < n; i++) { | |
| var id = textures[i]; | |
| var texture = gl.textures[id]; | |
| if (texture) { | |
| webgl.deleteTexture(texture); | |
| if (gl.texture === texture) gl.texture = null; | |
| } | |
| delete gl.textures[id]; | |
| } | |
| }, | |
| glDepthFunc: function(func) { | |
| if (gl.listMode && this.addToList("glDepthFunc", [func])) return; | |
| DEBUG > 1 && console.log("glDepthFunc", GL_Symbol(func)); | |
| webgl.depthFunc(func); | |
| }, | |
| glDepthMask: function(flag) { | |
| if (gl.listMode && this.addToList("glDepthMask", [flag])) return; | |
| DEBUG > 1 && console.log("glDepthMask", flag); | |
| webgl.depthMask(flag); | |
| }, | |
| glDepthRange: function(zNear, zFar) { | |
| if (gl.listMode && this.addToList("glDepthRange", [zNear, zFar])) return; | |
| DEBUG > 1 && console.log("glDepthRange", zNear, zFar); | |
| webgl.depthRange(zNear, zFar); | |
| }, | |
| glDisable: function(cap) { | |
| if (gl.listMode && this.addToList("glDisable", [cap])) return; | |
| switch (cap) { | |
| case GL.ALPHA_TEST: | |
| DEBUG > 1 && console.log("glDisable GL_ALPHA_TEST"); | |
| gl.alphaTest = false; | |
| break; | |
| case webgl.BLEND: | |
| DEBUG > 1 && console.log("glDisable GL_BLEND"); | |
| webgl.disable(webgl.BLEND); | |
| break; | |
| case GL.CLIP_PLANE0: | |
| case GL.CLIP_PLANE1: | |
| case GL.CLIP_PLANE2: | |
| case GL.CLIP_PLANE3: | |
| case GL.CLIP_PLANE4: | |
| case GL.CLIP_PLANE5: | |
| case GL.CLIP_PLANE6: | |
| case GL.CLIP_PLANE7: | |
| DEBUG > 1 && console.log("glDisable GL_CLIP_PLANE" + (cap - GL.CLIP_PLANE0)); | |
| gl.clipPlanes[cap - GL.CLIP_PLANE0].enabled = false; | |
| break; | |
| case webgl.CULL_FACE: | |
| DEBUG > 1 && console.log("glDisable GL.CULL_FACE"); | |
| webgl.disable(webgl.CULL_FACE); | |
| break; | |
| case webgl.DEPTH_TEST: | |
| DEBUG > 1 && console.log("glDisable GL_DEPTH_TEST"); | |
| webgl.disable(webgl.DEPTH_TEST); | |
| break; | |
| case GL.FOG: | |
| DEBUG > 1 && console.log("glDisable GL_FOG"); | |
| gl.fogEnabled = false; | |
| break; | |
| case GL.NORMALIZE: | |
| DEBUG > 1 && console.log("glDisable GL_NORMALIZE"); | |
| // we always normalize normals | |
| break; | |
| case GL.LIGHT0: | |
| case GL.LIGHT1: | |
| case GL.LIGHT2: | |
| case GL.LIGHT3: | |
| case GL.LIGHT4: | |
| case GL.LIGHT5: | |
| case GL.LIGHT6: | |
| case GL.LIGHT7: | |
| DEBUG > 1 && console.log("glDisable GL_LIGHT" + (cap - GL.LIGHT0)); | |
| gl.lights[cap - GL.LIGHT0].enabled = false; | |
| break; | |
| case GL.LIGHTING: | |
| DEBUG > 1 && console.log("glDisable GL_LIGHTING"); | |
| gl.lightingEnabled = false; | |
| break; | |
| case webgl.POLYGON_OFFSET_FILL: | |
| DEBUG > 1 && console.log("glDisable GL_POLYGON_OFFSET_FILL"); | |
| webgl.disable(webgl.POLYGON_OFFSET_FILL); | |
| break; | |
| case webgl.STENCIL_TEST: | |
| DEBUG > 1 && console.log("glDisable GL_STENCIL_TEST"); | |
| webgl.disable(webgl.STENCIL_TEST); | |
| break; | |
| case webgl.TEXTURE_2D: | |
| DEBUG > 1 && console.log("glDisable GL_TEXTURE_2D"); | |
| gl.textureEnabled = false; | |
| break; | |
| case GL.TEXTURE_GEN_S: | |
| case GL.TEXTURE_GEN_T: | |
| case GL.TEXTURE_GEN_R: | |
| case GL.TEXTURE_GEN_Q: | |
| if (DEBUG) console.log("UNIMPLEMENTED glDisable GL_TEXTURE_GEN_" + (cap - GL.TEXTURE_GEN_S)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glDisable GL_TEXTURE_GEN_" + (cap - GL.TEXTURE_GEN_S)); | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glDisable", GL_Symbol(cap)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glDisable " + GL_Symbol(cap)); | |
| } | |
| }, | |
| glDisableClientState: function(cap) { | |
| if (gl.listMode && this.addToList("glDisableClientState", [cap])) return; | |
| switch (cap) { | |
| case GL.VERTEX_ARRAY: | |
| DEBUG > 1 && console.log("glDisableClientState GL_VERTEX_ARRAY"); | |
| gl.clientState.vertexArray.enabled = false; | |
| return; | |
| case GL.NORMAL_ARRAY: | |
| DEBUG > 1 && console.log("glDisableClientState GL_NORMAL_ARRAY"); | |
| gl.clientState.normalArray.enabled = false; | |
| return; | |
| case GL.COLOR_ARRAY: | |
| DEBUG > 1 && console.log("glDisableClientState GL_COLOR_ARRAY"); | |
| gl.clientState.colorArray.enabled = false; | |
| return; | |
| case GL.TEXTURE_COORD_ARRAY: | |
| DEBUG > 1 && console.log("glDisableClientState GL_TEXTURE_COORD_ARRAY"); | |
| gl.clientState.textureCoordArray.enabled = false; | |
| return; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glDisableClientState", GL_Symbol(cap)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glDisableClientState " + GL_Symbol(cap)); | |
| } | |
| }, | |
| glDrawArrays: function(mode, first, count) { | |
| if (gl.listMode && this.addToList("glDrawArrays", [mode, first, count])) return; | |
| var geometryFlags = 0; | |
| if (gl.clientState.normalArray.enabled) geometryFlags |= HAS_NORMAL; | |
| if (gl.clientState.colorArray.enabled) geometryFlags |= HAS_COLOR; | |
| if (gl.clientState.textureCoordArray.enabled) geometryFlags |= HAS_TEXCOORD; | |
| if (mode === GL.POINTS) geometryFlags |= USE_POINT_SIZE; | |
| var shader = this.getShader(geometryFlags); | |
| if (!shader.program) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED glDrawArrays shader: " + shader.label); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glDrawArrays shader: " + shader.label); | |
| return; | |
| } | |
| var vertexArray = gl.clientState.vertexArray; | |
| if (!vertexArray.enabled || !vertexArray.pointer) { | |
| DEBUG > 0 && console.warn("glDrawArrays: GL_VERTEX_ARRAY incomplete, skipping"); | |
| return; | |
| } | |
| DEBUG > 1 && console.log("glDrawArrays", GL_Symbol(mode, 'POINTS'), first, count, shader.label); | |
| webgl.useProgram(shader.program); | |
| this.setShaderUniforms(shader); | |
| var loc = shader.locations; | |
| var vertexBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, vertexArray.pointer, webgl.DYNAMIC_DRAW); | |
| webgl.vertexAttribPointer(loc['aPosition'], vertexArray.size, vertexArray.type, false, vertexArray.stride, 0); | |
| webgl.enableVertexAttribArray(loc['aPosition']); | |
| var normalBuffer; | |
| if (loc['aNormal'] >= 0) { | |
| var normalArray = gl.clientState.normalArray; | |
| normalBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, normalBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, normalArray.pointer, webgl.DYNAMIC_DRAW); | |
| webgl.vertexAttribPointer(loc['aNormal'], normalArray.size, normalArray.type, false, normalArray.stride, 0); | |
| webgl.enableVertexAttribArray(loc['aNormal']); | |
| } | |
| var colorBuffer; | |
| if (loc['aColor'] >= 0) { | |
| var colorArray = gl.clientState.colorArray; | |
| colorBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, colorBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, colorArray.pointer, webgl.DYNAMIC_DRAW); | |
| webgl.vertexAttribPointer(loc['aColor'], colorArray.size, colorArray.type, false, colorArray.stride, 0); | |
| webgl.enableVertexAttribArray(loc['aColor']); | |
| } | |
| var texCoordBuffer; | |
| if (loc['aTexCoord'] >= 0) { | |
| var texCoordArray = gl.clientState.textureCoordArray; | |
| texCoordBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, texCoordBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, texCoordArray.pointer, webgl.DYNAMIC_DRAW); | |
| webgl.vertexAttribPointer(loc['aTexCoord'], texCoordArray.size, texCoordArray.type, false, texCoordArray.stride, 0); | |
| webgl.enableVertexAttribArray(loc['aTexCoord']); | |
| } | |
| webgl.drawArrays(mode, first, count); | |
| webgl.useProgram(null); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, null); | |
| webgl.disableVertexAttribArray(loc['aPosition']); | |
| webgl.deleteBuffer(vertexBuffer); | |
| if (normalBuffer) { | |
| webgl.disableVertexAttribArray(loc['aNormal']); | |
| webgl.deleteBuffer(normalBuffer); | |
| } | |
| if (colorBuffer) { | |
| webgl.disableVertexAttribArray(loc['aColor']); | |
| webgl.deleteBuffer(colorBuffer); | |
| } | |
| if (texCoordBuffer) { | |
| webgl.disableVertexAttribArray(loc['aTexCoord']); | |
| webgl.deleteBuffer(texCoordBuffer); | |
| } | |
| }, | |
| glDrawElements: function(mode, count, type, indicesPtr) { | |
| if (gl.listMode && this.addToList("glDrawElements", [mode, count, type, indicesPtr])) return; | |
| var indices; | |
| switch (type) { | |
| case GL.UNSIGNED_BYTE: | |
| indices = new Uint8Array(indicesPtr); | |
| break; | |
| case GL.UNSIGNED_SHORT: | |
| indices = new Uint16Array(indicesPtr); | |
| break; | |
| case GL.UNSIGNED_INT: | |
| // not directly supported by WebGL without OES_element_index_uint | |
| var indices32 = new Uint32Array(indicesPtr); | |
| var max = Math.max.apply(null, indices32); | |
| if (max > 0xFFFF) console.warn("OpenGL: glDrawElements with indices > 65535 not supported, truncating", max); | |
| if (max <= 0xFF) { | |
| indices = new Uint8Array(count); | |
| type = GL.UNSIGNED_BYTE; | |
| } else { | |
| indices = new Uint16Array(count); | |
| type = GL.UNSIGNED_SHORT; | |
| } | |
| for (var i = 0; i < count; i++) indices[i] = indices32[i]; | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glDrawElements type", GL_Symbol(type)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glDrawElements type " + GL_Symbol(type)); | |
| return; | |
| } | |
| // convert quads to triangles | |
| if (mode === GL.QUADS) { | |
| var arrayClass = indices.constructor; | |
| var newIndices = new arrayClass(count * 6 / 4); | |
| var j = 0; | |
| for (var i = 0; i < count; i += 4) { | |
| newIndices[j++] = indices[i]; | |
| newIndices[j++] = indices[i + 1]; | |
| newIndices[j++] = indices[i + 2]; | |
| newIndices[j++] = indices[i]; | |
| newIndices[j++] = indices[i + 2]; | |
| newIndices[j++] = indices[i + 3]; | |
| } | |
| indices = newIndices; | |
| count = newIndices.length; | |
| mode = GL.TRIANGLES; | |
| } | |
| var geometryFlags = 0; | |
| if (gl.clientState.normalArray.enabled) geometryFlags |= HAS_NORMAL; | |
| if (gl.clientState.colorArray.enabled) geometryFlags |= HAS_COLOR; | |
| if (gl.clientState.textureCoordArray.enabled) geometryFlags |= HAS_TEXCOORD; | |
| if (mode === GL.POINTS) geometryFlags |= USE_POINT_SIZE; | |
| var shader = this.getShader(geometryFlags); | |
| if (!shader.program) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED glDrawElements shader: " + shader.label); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glDrawElements shader: " + shader.label); | |
| return; | |
| } | |
| var vertexArray = gl.clientState.vertexArray; | |
| if (!vertexArray.enabled || !vertexArray.pointer) { | |
| DEBUG > 0 && console.warn("glDrawElements: GL_VERTEX_ARRAY incomplete, skipping"); | |
| return; | |
| } | |
| DEBUG > 1 && console.log("glDrawElements", GL_Symbol(mode, 'POINTS'), count, GL_Symbol(type), shader.label, Array.from(indices)); | |
| webgl.useProgram(shader.program); | |
| this.setShaderUniforms(shader); | |
| var loc = shader.locations; | |
| var vertexBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, vertexArray.pointer, webgl.DYNAMIC_DRAW); | |
| webgl.vertexAttribPointer(loc['aPosition'], vertexArray.size, vertexArray.type, false, vertexArray.stride, 0); | |
| webgl.enableVertexAttribArray(loc['aPosition']); | |
| var normalBuffer; | |
| if (loc['aNormal'] >= 0) { | |
| var normalArray = gl.clientState.normalArray; | |
| normalBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, normalBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, normalArray.pointer, webgl.DYNAMIC_DRAW); | |
| webgl.vertexAttribPointer(loc['aNormal'], normalArray.size, normalArray.type, false, normalArray.stride, 0); | |
| webgl.enableVertexAttribArray(loc['aNormal']); | |
| } | |
| var colorBuffer; | |
| if (loc['aColor'] >= 0) { | |
| var colorArray = gl.clientState.colorArray; | |
| colorBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, colorBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, colorArray.pointer, webgl.DYNAMIC_DRAW); | |
| webgl.vertexAttribPointer(loc['aColor'], colorArray.size, colorArray.type, false, colorArray.stride, 0); | |
| webgl.enableVertexAttribArray(loc['aColor']); | |
| } | |
| var texCoordBuffer; | |
| if (loc['aTexCoord'] >= 0) { | |
| var texCoordArray = gl.clientState.textureCoordArray; | |
| texCoordBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, texCoordBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, texCoordArray.pointer, webgl.DYNAMIC_DRAW); | |
| webgl.vertexAttribPointer(loc['aTexCoord'], texCoordArray.size, texCoordArray.type, false, texCoordArray.stride, 0); | |
| webgl.enableVertexAttribArray(loc['aTexCoord']); | |
| } | |
| var indexBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer); | |
| webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, indices, webgl.DYNAMIC_DRAW); | |
| webgl.drawElements(mode, indices.length, type, 0); | |
| webgl.useProgram(null); | |
| webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, null); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, null); | |
| webgl.deleteBuffer(indexBuffer); | |
| webgl.disableVertexAttribArray(loc['aPosition']); | |
| webgl.deleteBuffer(vertexBuffer); | |
| if (normalBuffer) { | |
| webgl.disableVertexAttribArray(loc['aNormal']); | |
| webgl.deleteBuffer(normalBuffer); | |
| } | |
| if (colorBuffer) { | |
| webgl.disableVertexAttribArray(loc['aColor']); | |
| webgl.deleteBuffer(colorBuffer); | |
| } | |
| if (texCoordBuffer) { | |
| webgl.disableVertexAttribArray(loc['aTexCoord']); | |
| webgl.deleteBuffer(texCoordBuffer); | |
| } | |
| }, | |
| glEnable: function(cap) { | |
| if (gl.listMode && this.addToList("glEnable", [cap])) return; | |
| switch (cap) { | |
| case GL.ALPHA_TEST: | |
| DEBUG > 1 && console.log("glEnable GL_ALPHA_TEST"); | |
| gl.alphaTest = true; | |
| break; | |
| case webgl.BLEND: | |
| DEBUG > 1 && console.log("glEnable GL_BLEND"); | |
| webgl.enable(webgl.BLEND); | |
| break; | |
| case GL.CLIP_PLANE0: | |
| case GL.CLIP_PLANE1: | |
| case GL.CLIP_PLANE2: | |
| case GL.CLIP_PLANE3: | |
| case GL.CLIP_PLANE4: | |
| case GL.CLIP_PLANE5: | |
| case GL.CLIP_PLANE6: | |
| case GL.CLIP_PLANE7: | |
| DEBUG > 1 && console.log("glEnable GL_CLIP_PLANE" + (cap - GL.CLIP_PLANE0)); | |
| gl.clipPlanes[cap - GL.CLIP_PLANE0].enabled = true; | |
| break; | |
| case webgl.CULL_FACE: | |
| DEBUG > 1 && console.log("glEnable GL_CULL_FACE"); | |
| webgl.enable(webgl.CULL_FACE); | |
| break; | |
| case webgl.DEPTH_TEST: | |
| DEBUG > 1 && console.log("glEnable GL_DEPTH_TEST"); | |
| webgl.enable(webgl.DEPTH_TEST); | |
| break; | |
| case GL.FOG: | |
| DEBUG > 1 && console.log("glEnable GL_FOG"); | |
| gl.fogEnabled = true; | |
| break; | |
| case GL.NORMALIZE: | |
| DEBUG > 1 && console.log("glEnable GL_NORMALIZE"); | |
| // we always normalize normals | |
| break; | |
| case GL.LIGHT0: | |
| case GL.LIGHT1: | |
| case GL.LIGHT2: | |
| case GL.LIGHT3: | |
| case GL.LIGHT4: | |
| case GL.LIGHT5: | |
| case GL.LIGHT6: | |
| case GL.LIGHT7: | |
| DEBUG > 1 && console.log("glEnable GL_LIGHT" + (cap - GL.LIGHT0)); | |
| gl.lights[cap - GL.LIGHT0].enabled = true; | |
| break; | |
| case GL.LIGHTING: | |
| DEBUG > 1 && console.log("glEnable GL_LIGHTING"); | |
| gl.lightingEnabled = true; | |
| break; | |
| case webgl.POLYGON_OFFSET_FILL: | |
| DEBUG > 1 && console.log("glEnable GL_POLYGON_OFFSET_FILL"); | |
| webgl.enable(webgl.POLYGON_OFFSET_FILL); | |
| break; | |
| case webgl.STENCIL_TEST: | |
| DEBUG > 1 && console.log("glEnable GL_STENCIL_TEST"); | |
| webgl.enable(webgl.STENCIL_TEST); | |
| break; | |
| case webgl.TEXTURE_2D: | |
| DEBUG > 1 && console.log("glEnable GL_TEXTURE_2D"); | |
| gl.textureEnabled = true; | |
| break; | |
| case GL.TEXTURE_GEN_S: | |
| case GL.TEXTURE_GEN_T: | |
| case GL.TEXTURE_GEN_R: | |
| case GL.TEXTURE_GEN_Q: | |
| if (DEBUG) console.log("UNIMPLEMENTED glEnable GL_" + GL_Symbol(cap, "TEXTURE_GEN_S")); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glEnable GL_" + GL_Symbol(cap, "TEXTURE_GEN_S")); | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glEnable", GL_Symbol(cap)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glEnable " + GL_Symbol(cap)); | |
| } | |
| }, | |
| glEnableClientState: function(cap) { | |
| if (gl.listMode && this.addToList("glEnableClientState", [cap])) return; | |
| switch (cap) { | |
| case GL.VERTEX_ARRAY: | |
| DEBUG > 1 && console.log("glEnableClientState GL_VERTEX_ARRAY"); | |
| gl.clientState.vertexArray.enabled = true; | |
| return; | |
| case GL.NORMAL_ARRAY: | |
| DEBUG > 1 && console.log("glEnableClientState GL_NORMAL_ARRAY"); | |
| gl.clientState.normalArray.enabled = true; | |
| return; | |
| case GL.COLOR_ARRAY: | |
| DEBUG > 1 && console.log("glEnableClientState GL_COLOR_ARRAY"); | |
| gl.clientState.colorArray.enabled = true; | |
| return; | |
| case GL.TEXTURE_COORD_ARRAY: | |
| DEBUG > 1 && console.log("glEnableClientState GL_TEXTURE_COORD_ARRAY"); | |
| gl.clientState.textureCoordArray.enabled = true; | |
| return; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glEnableClientState", GL_Symbol(cap)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glEnableClientState " + GL_Symbol(cap)); | |
| } | |
| }, | |
| glFog: function(pname, param) { | |
| if (gl.listMode && this.addToList("glFog", [pname, param])) return; | |
| switch (pname) { | |
| case GL.FOG_MODE: | |
| gl.fogMode = param; | |
| DEBUG > 1 && console.log("glFog GL_FOG_MODE", GL_Symbol(param)); | |
| break; | |
| case GL.FOG_DENSITY: | |
| DEBUG > 1 && console.log("glFog GL_FOG_DENSITY", param); | |
| gl.fogDensity = param; | |
| break; | |
| case GL.FOG_START: | |
| DEBUG > 1 && console.log("glFog GL_FOG_START", param); | |
| gl.fogStart = param; | |
| break; | |
| case GL.FOG_END: | |
| DEBUG > 1 && console.log("glFog GL_FOG_END", param); | |
| gl.fogEnd = param; | |
| break; | |
| case GL.FOG_COLOR: | |
| DEBUG > 1 && console.log("glFog GL_FOG_COLOR", Array.from(param)); | |
| gl.fogColor.set(param); | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glFog", GL_Symbol(pname), param); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glFog " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glFogi: function(pname, param) { | |
| this.glFog(pname, param); | |
| }, | |
| glFogf: function(pname, param) { | |
| this.glFog(pname, param); | |
| }, | |
| glFogiv: function(pname, params) { | |
| // FOG_COLOR integer values are mapped linearly such that the most positive representable value maps to 1.0, | |
| // and the most negative representable value maps to -1.0 | |
| this.glFog(pname, pname === GL.FOG_COLOR | |
| ? params.map(function(x) { return (x + 0.5) / (0x7FFFFFFF + 0.5); }) | |
| : params[0]); | |
| }, | |
| glFogfv: function(pname, params) { | |
| this.glFog(pname, pname === GL.FOG_COLOR ? params : params[0]); | |
| }, | |
| getShader: function(geometryFlags) { | |
| // geometryFlags: HAS_TEXCOORD, HAS_NORMAL, HAS_COLOR, USE_POINT_SIZE | |
| var shaderFlags = geometryFlags; | |
| if (gl.textureEnabled && gl.texture) shaderFlags |= USE_TEXTURE; | |
| if (gl.alphaTest) shaderFlags |= USE_ALPHA_TEST; // UNIMPLEMENTED | |
| if (gl.fogEnabled) switch (gl.fogMode) { | |
| case GL.EXP: shaderFlags |= EXP_FOG << FOG_SHIFT; break; | |
| case GL.EXP2: shaderFlags |= EXP2_FOG << FOG_SHIFT; break; | |
| case GL.LINEAR: shaderFlags |= LINEAR_FOG << FOG_SHIFT; break; | |
| } | |
| var numLights = 0; | |
| if (gl.lightingEnabled) { | |
| for (var i = 0; i < MAX_LIGHTS; i++) { | |
| if (gl.lights[i].enabled) numLights++; | |
| } | |
| shaderFlags |= numLights << NUM_LIGHTS_SHIFT; | |
| } | |
| var numClipPlanes = 0; | |
| for (var i = 0; i < MAX_CLIP_PLANES; i++) { | |
| if (gl.clipPlanes[i].enabled) { | |
| numClipPlanes++; | |
| } | |
| } | |
| shaderFlags |= numClipPlanes << NUM_CLIP_PLANES_SHIFT; | |
| // create shader program | |
| var shader = gl.shaders[shaderFlags]; | |
| if (!shader) { | |
| var flagString = "[POSITION"; | |
| if (shaderFlags & HAS_NORMAL) flagString += ", NORMAL"; | |
| if (shaderFlags & HAS_COLOR) flagString += ", COLOR"; | |
| if (shaderFlags & HAS_TEXCOORD) flagString += ", TEXCOORD"; | |
| flagString += "]"; | |
| if (shaderFlags & USE_TEXTURE) flagString += ", TEXTURE"; | |
| if (shaderFlags & ANY_LIGHTS) { flagString += ", "+ numLights +" LIGHT"; if (numLights !== 1) flagString += "S"; } | |
| if (shaderFlags & ANY_CLIP_PLANES) { flagString += ", "+ numClipPlanes +" CLIP_PLANE"; if (numClipPlanes !== 1) flagString += "S"; } | |
| if (shaderFlags & USE_ALPHA_TEST) flagString += ", ALPHA_TEST"; | |
| if (shaderFlags & USE_POINT_SIZE) flagString += ", POINT_SIZE"; | |
| if (shaderFlags & ANY_FOG) flagString += ", FOG"; | |
| shader = gl.shaders[shaderFlags] = { | |
| flags: shaderFlags, | |
| label: flagString, | |
| program: null, | |
| locations: null, | |
| vsource: null, // for debugging | |
| fsource: null, // for debugging | |
| }; | |
| var implemented = HAS_TEXCOORD | HAS_NORMAL | HAS_COLOR | USE_TEXTURE | NUM_LIGHTS_MASK | NUM_CLIP_PLANES_MASK | USE_POINT_SIZE | FOG_MASK; | |
| if (shaderFlags & ~implemented) return shader; | |
| var program = webgl.createProgram() | |
| var vs = webgl.createShader(webgl.VERTEX_SHADER); | |
| shader.vsource = this.vertexShaderSource(shaderFlags); | |
| webgl.shaderSource(vs, shader.vsource); | |
| webgl.compileShader(vs); | |
| if (!webgl.getShaderParameter(vs, webgl.COMPILE_STATUS)) { | |
| console.error("OpenGL: vertex shader compile error: " + webgl.getShaderInfoLog(vs)); | |
| debugger; | |
| return shader; | |
| } | |
| var fs = webgl.createShader(webgl.FRAGMENT_SHADER); | |
| shader.fsource = this.fragmentShaderSource(shaderFlags); | |
| webgl.shaderSource(fs, shader.fsource); | |
| webgl.compileShader(fs); | |
| if (!webgl.getShaderParameter(fs, webgl.COMPILE_STATUS)) { | |
| console.error("OpenGL: fragment shader compile error: " + webgl.getShaderInfoLog(fs)); | |
| debugger; | |
| return shader; | |
| } | |
| webgl.attachShader(program, vs); | |
| webgl.attachShader(program, fs); | |
| webgl.linkProgram(program); | |
| if (!webgl.getProgramParameter(program, webgl.LINK_STATUS)) { | |
| console.error("OpenGL: shader link error: " + webgl.getProgramInfoLog(program)); | |
| debugger | |
| return shader; | |
| } | |
| shader.program = program; | |
| shader.locations = this.getLocations(program, shaderFlags); | |
| } | |
| return shader; | |
| }, | |
| setShaderUniforms: function(shader) { | |
| var loc = shader.locations; | |
| DEBUG > 2 && console.log("uModelView", Array.from(gl.matrices[GL.MODELVIEW][0])); | |
| webgl.uniformMatrix4fv(loc['uModelView'], false, gl.matrices[GL.MODELVIEW][0]); | |
| DEBUG > 2 && console.log("uProjection", Array.from(gl.matrices[GL.PROJECTION][0])); | |
| webgl.uniformMatrix4fv(loc['uProjection'], false, gl.matrices[GL.PROJECTION][0]); | |
| if (loc['uNormalMatrix']) { | |
| var normalMatrix = asNormalMatrix(gl.matrices[GL.MODELVIEW][0]); | |
| DEBUG > 2 && console.log("uNormalMatrix", Array.from(normalMatrix)); | |
| webgl.uniformMatrix3fv(loc['uNormalMatrix'], false, normalMatrix); | |
| } | |
| if (loc['uNormal']) { | |
| DEBUG > 2 && console.log("uNormal", Array.from(gl.normal)); | |
| webgl.uniform3fv(loc['uNormal'], gl.normal); | |
| } | |
| if (loc['uColor']) { | |
| var color = gl.color; | |
| if (gl.textureEnvMode === GL.REPLACE) color = [1, 1, 1, 1]; // HACK | |
| DEBUG > 2 && console.log("uColor", Array.from(color)); | |
| webgl.uniform4fv(loc['uColor'], color); | |
| } | |
| if (loc['uTextureMatrix']) { | |
| DEBUG > 2 && console.log("uTextureMatrix", Array.from(gl.matrices[GL.TEXTURE][0])); | |
| webgl.uniformMatrix4fv(loc['uTextureMatrix'], false, gl.matrices[GL.TEXTURE][0]); | |
| } | |
| if (loc['uTexCoord']) { | |
| DEBUG > 2 && console.log("uTexCoord", Array.from(gl.texCoord)); | |
| webgl.uniform2fv(loc['uTexCoord'], gl.texCoord); | |
| } | |
| if (loc['uSampler']) { | |
| DEBUG > 2 && console.log("uSampler", gl.texture); | |
| webgl.activeTexture(webgl.TEXTURE0); | |
| webgl.bindTexture(webgl.TEXTURE_2D, gl.texture); | |
| webgl.uniform1i(loc['uSampler'], 0); | |
| } | |
| if (loc['uPointSize']) { | |
| DEBUG > 2 && console.log("uPointSize", gl.pointSize); | |
| webgl.uniform1f(loc['uPointSize'], gl.pointSize); | |
| } | |
| var numLights = (shader.flags & NUM_LIGHTS_MASK) >> NUM_LIGHTS_SHIFT; | |
| if (numLights > 0) { | |
| DEBUG > 2 && console.log("uLightModelAmbient", Array.from(gl.lightModelAmbient)); | |
| webgl.uniform4fv(loc['uLightModelAmbient'], gl.lightModelAmbient); | |
| DEBUG > 2 && console.log("uMaterialAmbient", Array.from(gl.material.ambient)); | |
| webgl.uniform4fv(loc['uMaterialAmbient'], gl.material.ambient); | |
| DEBUG > 2 && console.log("uMaterialDiffuse", Array.from(gl.material.diffuse)); | |
| webgl.uniform4fv(loc['uMaterialDiffuse'], gl.material.diffuse); | |
| DEBUG > 2 && console.log("uMaterialSpecular", Array.from(gl.material.specular)); | |
| webgl.uniform4fv(loc['uMaterialSpecular'], gl.material.specular); | |
| DEBUG > 2 && console.log("uMaterialEmission", Array.from(gl.material.emission)); | |
| webgl.uniform4fv(loc['uMaterialEmission'], gl.material.emission); | |
| DEBUG > 2 && console.log("uMaterialShininess", gl.material.shininess); | |
| webgl.uniform1f(loc['uMaterialShininess'], gl.material.shininess); | |
| var index = 0; | |
| for (var i = 0; i < MAX_LIGHTS; i++) { | |
| var light = gl.lights[i]; | |
| if (!light.enabled) continue; | |
| DEBUG > 2 && console.log("uLights[" + index + "].ambient", Array.from(light.ambient)); | |
| webgl.uniform4fv(loc['uLights'][index].ambient, light.ambient); | |
| DEBUG > 2 && console.log("uLights[" + index + "].diffuse", Array.from(light.diffuse)); | |
| webgl.uniform4fv(loc['uLights'][index].diffuse, light.diffuse); | |
| DEBUG > 2 && console.log("uLights[" + index + "].specular", Array.from(light.specular)); | |
| webgl.uniform4fv(loc['uLights'][index].specular, light.specular); | |
| DEBUG > 2 && console.log("uLights[" + index + "].position", Array.from(light.position)); | |
| webgl.uniform4fv(loc['uLights'][index].position, light.position); | |
| index++; | |
| } | |
| } | |
| var numClipPlanes = (shader.flags & NUM_CLIP_PLANES_MASK) >> NUM_CLIP_PLANES_SHIFT; | |
| if (numClipPlanes > 0) { | |
| var index = 0; | |
| for (var i = 0; i < MAX_CLIP_PLANES; i++) { | |
| var clipPlane = gl.clipPlanes[i]; | |
| if (!clipPlane.enabled) continue; | |
| DEBUG > 2 && console.log("uClipPlanes[" + index + "]", Array.from(clipPlane.equation)); | |
| webgl.uniform4fv(loc['uClipPlanes'][index], clipPlane.equation); | |
| index++; | |
| } | |
| } | |
| if (loc['uFogColor']) { | |
| DEBUG > 2 && console.log("uFogColor", Array.from(gl.fogColor)); | |
| webgl.uniform4fv(loc['uFogColor'], gl.fogColor); | |
| } | |
| if (loc['uFogEnd']) { | |
| DEBUG > 2 && console.log("uFogEnd", gl.fogEnd); | |
| webgl.uniform1f(loc['uFogEnd'], gl.fogEnd); | |
| } | |
| if (loc['uFogRange']) { | |
| DEBUG > 2 && console.log("uFogRange", gl.fogEnd - gl.fogStart); | |
| webgl.uniform1f(loc['uFogRange'], gl.fogEnd - gl.fogStart); | |
| } | |
| if (loc['uFogDensity']) { | |
| DEBUG > 2 && console.log("uFogDensity", gl.fogDensity); | |
| webgl.uniform1f(loc['uFogDensity'], gl.fogDensity); | |
| } | |
| }, | |
| glEnd: function() { | |
| if (gl.listMode && this.addToList("glEnd", [])) return; | |
| var primitive = gl.primitive; | |
| gl.primitive = null; | |
| // select shader | |
| var geometryFlags = primitive.vertexAttrs; | |
| if (primitive.mode === GL.POINTS) geometryFlags |= USE_POINT_SIZE; | |
| var shader = this.getShader(geometryFlags); | |
| if (!shader.program) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED glEnd shader: " + shader.label); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glEnd shader: " + shader.label); | |
| return; | |
| } | |
| // create interleaved vertex buffer | |
| var vertices = primitive.vertices; | |
| var size = primitive.vertexSize; | |
| var data = new Float32Array(vertices.length * size); | |
| for (var i = 0, offset = 0; i < vertices.length; i++, offset += size) { | |
| data.set(vertices[i], offset); | |
| } | |
| var vertexBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer); | |
| webgl.bufferData(webgl.ARRAY_BUFFER, data, webgl.DYNAMIC_DRAW); | |
| // set mode depending on primitive mode | |
| // and create index buffer if needed | |
| var mode; | |
| var indices; | |
| switch (primitive.mode) { | |
| // supported by WebGL, no index buffer needed | |
| case webgl.POINTS: | |
| case webgl.LINES: | |
| case webgl.LINE_LOOP: | |
| case webgl.LINE_STRIP: | |
| case webgl.TRIANGLES: | |
| case webgl.TRIANGLE_STRIP: | |
| case webgl.TRIANGLE_FAN: | |
| DEBUG > 1 && console.log("glEnd " + GL_Symbol(primitive.mode, "POINTS") + ":" + shader.label); | |
| mode = primitive.mode; | |
| break; | |
| // not supported by WebGL, emulate | |
| case GL.QUADS: | |
| // use triangles and an index buffer to | |
| // duplicate vertices as v0-v1-v2, v2-v1-v3 | |
| // we assume that all attributes are floats | |
| DEBUG > 1 && console.log("glEnd GL_QUADS:" + shader.label); | |
| indices = vertices.length > 256 | |
| ? new Uint16Array(vertices.length * 3 / 2) | |
| : new Uint8Array(vertices.length * 3 / 2); | |
| var offset = 0; | |
| for (var i = 0; i < vertices.length; i += 4) { | |
| indices[offset++] = i; | |
| indices[offset++] = i+1; | |
| indices[offset++] = i+2; | |
| indices[offset++] = i; | |
| indices[offset++] = i+2; | |
| indices[offset++] = i+3; | |
| } | |
| mode = webgl.TRIANGLES; | |
| break; | |
| case GL.QUAD_STRIP: | |
| if (DEBUG) console.log("UNIMPLEMENTED glEnd GL_QUAD_STRIP:" + shader.label); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glEnd GL_QUAD_STRIP"); | |
| return; | |
| case GL.POLYGON: | |
| // use triangle fan, which works for convex polygons | |
| DEBUG > 1 && console.log("glEnd GL_POLYGON:" + shader.label); | |
| mode = webgl.TRIANGLE_FAN; | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glEnd", primitive.mode); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glEnd " + primitive.mode); | |
| return; | |
| } | |
| var indexBuffer; | |
| if (indices) { | |
| indexBuffer = webgl.createBuffer(); | |
| webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer); | |
| webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, indices, webgl.DYNAMIC_DRAW); | |
| } | |
| // set up uniforms and vertex attributes | |
| var stride = size * 4; | |
| var offset = 0; | |
| webgl.useProgram(shader.program); | |
| this.setShaderUniforms(shader); | |
| var loc = shader.locations; | |
| DEBUG > 2 && console.log("aPosition: @" + offset + "/" + stride); | |
| webgl.vertexAttribPointer(loc['aPosition'], 3, webgl.FLOAT, false, stride, offset); | |
| webgl.enableVertexAttribArray(loc['aPosition']); | |
| offset += 12; | |
| if (loc['aNormal'] >= 0) { | |
| DEBUG > 2 && console.log("aNormal: @" + offset + "/" + stride); | |
| webgl.vertexAttribPointer(loc['aNormal'], 3, webgl.FLOAT, false, stride, offset); | |
| webgl.enableVertexAttribArray(loc['aNormal']); | |
| } | |
| if (geometryFlags & HAS_NORMAL) offset += 12; | |
| if (loc['aColor'] >= 0) { | |
| DEBUG > 2 && console.log("aColor: @" + offset + "/" + stride); | |
| webgl.vertexAttribPointer(loc['aColor'], 4, webgl.FLOAT, false, stride, offset); | |
| webgl.enableVertexAttribArray(loc['aColor']); | |
| } | |
| if (geometryFlags & HAS_COLOR) offset += 16; | |
| if (loc['aTexCoord'] >= 0) { | |
| DEBUG > 2 && console.log("aTexCoord: @" + offset + "/" + stride); | |
| webgl.vertexAttribPointer(loc['aTexCoord'], 2, webgl.FLOAT, false, stride, offset); | |
| webgl.enableVertexAttribArray(loc['aTexCoord']); | |
| } | |
| if (geometryFlags & HAS_TEXCOORD) offset += 8; | |
| // draw | |
| if (indexBuffer) { | |
| DEBUG > 1 && console.log("glEnd: draw indexed vertices", GL_Symbol(mode, 'POINTS'), Array.from(indices), vertices.map(function(v) { return ""+v; })); | |
| webgl.drawElements(mode, indices.length, vertices.length > 256 ? webgl.UNSIGNED_SHORT : webgl.UNSIGNED_BYTE, 0); | |
| } else { | |
| DEBUG > 1 && console.log("glEnd: draw vertices", GL_Symbol(mode, 'POINTS'), 0, vertices.map(function(v) { return ""+v; })); | |
| webgl.drawArrays(mode, 0, vertices.length); | |
| } | |
| webgl.useProgram(null); | |
| webgl.disableVertexAttribArray(loc['aPosition']); | |
| if (loc['aNormal'] >= 0) webgl.disableVertexAttribArray(loc['aNormal']); | |
| if (loc['aColor'] >= 0) webgl.disableVertexAttribArray(loc['aColor']); | |
| if (loc['aTexCoord'] >= 0) webgl.disableVertexAttribArray(loc['aTexCoord']); | |
| webgl.bindBuffer(webgl.ARRAY_BUFFER, null); | |
| webgl.deleteBuffer(vertexBuffer); | |
| if (indexBuffer) { | |
| webgl.deleteBuffer(indexBuffer); | |
| webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, null); | |
| } | |
| }, | |
| glEndList: function() { | |
| DEBUG > 1 && console.log("glEndList"); | |
| var list = gl.list; | |
| gl.list = null; | |
| gl.lists[list.id] = list; | |
| gl.listMode = 0; | |
| }, | |
| glFinish: function() { | |
| DEBUG > 1 && console.log("glFinish"); | |
| webgl.finish(); | |
| }, | |
| glFlush: function() { | |
| DEBUG > 1 && console.log("glFlush"); | |
| webgl.flush(); | |
| }, | |
| glFrontFace: function(mode) { | |
| if (gl.listMode && this.addToList("glFrontFace", [mode])) return; | |
| DEBUG > 1 && console.log("glFrontFace", GL_Symbol(mode)); | |
| webgl.frontFace(mode); | |
| }, | |
| glFrustum: function(left, right, bottom, top, zNear, zFar) { | |
| if (gl.listMode && this.addToList("glFrustum", [left, right, bottom, top, zNear, zFar])) return; | |
| DEBUG > 1 && console.log("glFrustum", left, right, bottom, top, zNear, zFar); | |
| var m = gl.matrix; | |
| m[0] = 2 * zNear / (right - left); | |
| m[1] = 0; | |
| m[2] = 0; | |
| m[3] = 0; | |
| m[4] = 0; | |
| m[5] = 2 * zNear / (top - bottom); | |
| m[6] = 0; | |
| m[7] = 0; | |
| m[8] = (right + left) / (right - left); | |
| m[9] = (top + bottom) / (top - bottom); | |
| m[10] = -(zFar + zNear) / (zFar - zNear); | |
| m[11] = -1; | |
| m[12] = 0; | |
| m[13] = 0; | |
| m[14] = -(2 * zFar * zNear) / (zFar - zNear); | |
| m[15] = 0; | |
| }, | |
| glGenLists: function(range) { | |
| DEBUG > 1 && console.log("glGenLists", range); | |
| var firstId = 0; | |
| if (range > 0) { | |
| firstId = gl.listIdGen + 1; | |
| gl.listIdGen += range; | |
| } | |
| return firstId; | |
| }, | |
| glGenTextures: function(n, textures) { | |
| for (var i = 0; i < n; i++) { | |
| var id = ++gl.textureIdGen; | |
| gl.textures[id] = webgl.createTexture(); | |
| textures[i] = id; | |
| } | |
| DEBUG > 1 && console.log("glGenTextures", n, Array.from(textures)); | |
| }, | |
| glGetFloatv: function(pname, params) { | |
| switch (pname) { | |
| case GL.MODELVIEW_MATRIX: | |
| DEBUG > 1 && console.log("glGetFloatv GL_MODELVIEW_MATRIX"); | |
| params.set(gl.matrices[GL.MODELVIEW][0]); | |
| break; | |
| case GL.PROJECTION_MATRIX: | |
| DEBUG > 1 && console.log("glGetFloatv GL_PROJECTION_MATRIX"); | |
| params.set(gl.matrices[GL.PROJECTION][0]); | |
| break; | |
| case GL.TEXTURE_MATRIX: | |
| DEBUG > 1 && console.log("glGetFloatv GL_TEXTURE_MATRIX"); | |
| params.set(gl.matrices[GL.TEXTURE][0]); | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glGetFloatv", GL_Symbol(pname)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glGetFloatv " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glGetIntegerv(name, params) { | |
| switch (name) { | |
| case GL.LIST_INDEX: | |
| DEBUG > 1 && console.log("glGetIntegerv GL_LIST_INDEX"); | |
| params[0] = gl.list ? gl.list.id : 0; | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glGetIntegerv", GL_Symbol(name)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glGetIntegerv " + GL_Symbol(name)); | |
| } | |
| }, | |
| glGetString: function(name) { | |
| switch (name) { | |
| case GL.VERSION: | |
| DEBUG > 1 && console.log("glGetString GL_VERSION"); | |
| return gl.version; | |
| case GL.VENDOR: | |
| DEBUG > 1 && console.log("glGetString GL_VENDOR"); | |
| return gl.vendor; | |
| case GL.RENDERER: | |
| DEBUG > 1 && console.log("glGetString GL_RENDERER"); | |
| return gl.renderer; | |
| case GL.EXTENSIONS: | |
| DEBUG > 1 && console.log("glGetString GL_EXTENSIONS"); | |
| return gl.extensions; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glGetString", name); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glGetString " + name); | |
| } | |
| return ""; | |
| }, | |
| glGetTexLevelParameteriv: function(target, level, pname, params) { | |
| switch (pname) { | |
| case GL.TEXTURE_COMPRESSED: | |
| return false; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glGetTexLevelParameteriv", GL_Symbol(target), level, GL_Symbol(pname), params); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glGetTexLevelParameteriv " + GL_Symbol(target) + " " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glHint: function(target, mode) { | |
| if (gl.listMode && this.addToList("glHint", [target, mode])) return; | |
| switch (target) { | |
| case webgl.GENERATE_MIPMAP_HINT: | |
| DEBUG > 1 && console.log("glHint GL_GENERATE_MIPMAP_HINT", GL_Symbol(mode)); | |
| webgl.hint(target, mode); | |
| break; | |
| } | |
| // webgl doesn't support any other hints | |
| DEBUG > 1 && console.log("IGNORING glHint", GL_Symbol(target), GL_Symbol(mode)); | |
| }, | |
| glIsEnabled: function(cap) { | |
| switch (cap) { | |
| case GL.LIGHTING: | |
| DEBUG > 1 && console.log("glIsEnabled GL_LIGHTING"); | |
| return gl.lightingEnabled; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glIsEnabled", cap); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glIsEnabled " + cap); | |
| } | |
| return false; | |
| }, | |
| glIsTexture: function(texture) { | |
| DEBUG > 1 && console.log("glIsTexture", texture); | |
| return !!gl.textures[texture]; | |
| }, | |
| glLightf: function(light, pname, param) { | |
| if (gl.listMode && this.addToList("glLightf", [light, pname, param])) return; | |
| var i = light - GL.LIGHT0; | |
| switch (pname) { | |
| case GL.SPOT_CUTOFF: | |
| if (param === 180) { | |
| DEBUG > 1 && console.log("glLightf", i, "GL_SPOT_CUTOFF", param); | |
| return; | |
| } | |
| // fall through if not default | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glLightf", i, GL_Symbol(pname), param); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glLightf " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glLightfv: function(light, pname, param) { | |
| if (gl.listMode && this.addToList("glLightfv", [light, pname, param])) return; | |
| var i = light - GL.LIGHT0; | |
| switch (pname) { | |
| case GL.AMBIENT: | |
| DEBUG > 1 && console.log("glLightfv", i, "GL_AMBIENT", Array.from(param)); | |
| gl.lights[i].ambient = param; | |
| break; | |
| case GL.DIFFUSE: | |
| DEBUG > 1 && console.log("glLightfv", i, "GL_DIFFUSE", Array.from(param)); | |
| gl.lights[i].diffuse = param; | |
| break; | |
| case GL.SPECULAR: | |
| DEBUG > 1 && console.log("glLightfv", i, "GL_SPECULAR", Array.from(param)); | |
| gl.lights[i].specular = param; | |
| break; | |
| case GL.POSITION: | |
| if (param[3] === 0) { | |
| transformDirection(gl.matrices[GL.MODELVIEW][0], param, gl.lights[i].position); | |
| } else { | |
| transformPoint(gl.matrices[GL.MODELVIEW][0], param, gl.lights[i].position); | |
| } | |
| DEBUG > 1 && console.log("glLightfv", i, "GL_POSITION", Array.from(param), "=>", Array.from(gl.lights[i].position)); | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glLightfv", i, GL_Symbol(pname), Array.from(param)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glLightfv " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glLightModelfv: function(pname, params) { | |
| if (gl.listMode && this.addToList("glLightModelfv", [pname, params])) return; | |
| switch (pname) { | |
| case GL.LIGHT_MODEL_AMBIENT: | |
| DEBUG > 1 && console.log("glLightModelfv GL_LIGHT_MODEL_AMBIENT", Array.from(params)); | |
| gl.lightModelAmbient = params; | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glLightModelfv", GL_Symbol(pname), Array.from(params)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glLightModelfv " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glLineWidth: function(width) { | |
| if (gl.listMode && this.addToList("glLineWidth", [width])) return; | |
| DEBUG > 1 && console.log("glLineWidth", width); | |
| webgl.lineWidth(width); | |
| }, | |
| glListBase: function(base) { | |
| DEBUG > 1 && console.log("glListBase", base); | |
| gl.listBase = base; | |
| }, | |
| glLoadIdentity: function() { | |
| if (gl.listMode && this.addToList("glLoadIdentity", [])) return; | |
| DEBUG > 1 && console.log("glLoadIdentity"); | |
| gl.matrix.set(identity); | |
| }, | |
| glLoadMatrixf: function(m) { | |
| if (gl.listMode && this.addToList("glLoadMatrixf", [m])) return; | |
| gl.matrix.set(m); | |
| DEBUG > 1 && console.log("glLoadMatrixf", GL_Symbol(gl.matrixMode), Array.from(m)); | |
| }, | |
| glLoadTransposeMatrixf: function(m) { | |
| if (gl.listMode && this.addToList("glLoadTransposeMatrixf", [m])) return; | |
| var t = new Float32Array(m); | |
| transposeMatrix(t); | |
| gl.matrix.set(t); | |
| DEBUG > 1 && console.log("glLoadTransposeMatrixf", GL_Symbol(gl.matrixMode), Array.from(m)); | |
| }, | |
| glMaterialf: function(face, pname, param) { | |
| if (gl.listMode && this.addToList("glMaterialf", [face, pname, param])) return; | |
| switch (pname) { | |
| case GL.SHININESS: | |
| DEBUG > 1 && console.log("glMaterialf GL_SHININESS", param); | |
| gl.material.shininess = param; | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glMaterialf", GL_Symbol(pname), param); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glMaterialf " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glMaterialfv: function(face, pname, param) { | |
| if (gl.listMode && this.addToList("glMaterialfv", [face, pname, param])) return; | |
| switch (pname) { | |
| case GL.AMBIENT: | |
| DEBUG > 1 && console.log("glMaterialfv GL_AMBIENT", Array.from(param)); | |
| gl.material.ambient = param; | |
| break; | |
| case GL.DIFFUSE: | |
| DEBUG > 1 && console.log("glMaterialfv GL_DIFFUSE", Array.from(param)); | |
| gl.material.diffuse = param; | |
| break; | |
| case GL.SPECULAR: | |
| DEBUG > 1 && console.log("glMaterialfv GL_SPECULAR", Array.from(param)); | |
| gl.material.specular = param; | |
| break; | |
| case GL.EMISSION: | |
| DEBUG > 1 && console.log("glMaterialfv GL_EMISSION", Array.from(param)); | |
| gl.material.emission = param; | |
| break; | |
| case GL.SHININESS: | |
| DEBUG > 1 && console.log("glMaterialfv GL_SHININESS", Array.from(param)); | |
| gl.material.shininess = param[0]; | |
| break; | |
| case GL.AMBIENT_AND_DIFFUSE: | |
| DEBUG > 1 && console.log("glMaterialfv GL_AMBIENT_AND_DIFFUSE", Array.from(param)); | |
| gl.material.ambient = param; | |
| gl.material.diffuse = param; | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glMaterialfv", GL_Symbol(pname), Array.from(param)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glMaterialfv " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glMatrixMode: function(mode) { | |
| if (gl.listMode && this.addToList("glMatrixMode", [mode])) return; | |
| if (mode !== GL.MODELVIEW && mode !== GL.PROJECTION && mode !== GL.TEXTURE) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED glMatrixMode", GL_Symbol(mode)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glMatrixMode " + GL_Symbol(mode)); | |
| } else DEBUG > 1 && console.log("glMatrixMode", GL_Symbol(mode)); | |
| gl.matrixMode = mode; | |
| if (!gl.matrices[mode]) gl.matrices[mode] = [new Float32Array(identity)]; | |
| gl.matrix = gl.matrices[mode][0]; | |
| }, | |
| glMultMatrixf: function(m) { | |
| if (gl.listMode && this.addToList("glMultMatrixf", [m])) return; | |
| multMatrix(gl.matrix, m); | |
| DEBUG > 1 && console.log("glMultMatrixf", GL_Symbol(gl.matrixMode), Array.from(m), "=>", Array.from(gl.matrix)); | |
| }, | |
| glMultTransposeMatrixf: function(m) { | |
| if (gl.listMode && this.addToList("glMultTransposeMatrixf", [m])) return; | |
| var t = new Float32Array(m); | |
| transposeMatrix(t); | |
| multMatrix(gl.matrix, t); | |
| DEBUG > 1 && console.log("glMultTransposeMatrixf", GL_Symbol(gl.matrixMode), Array.from(m), "=>", Array.from(gl.matrix)); | |
| }, | |
| glNewList: function(list, mode) { | |
| DEBUG > 1 && console.log("glNewList", list, GL_Symbol(mode)); | |
| var newList = { | |
| id: list, | |
| commands: [], | |
| }; | |
| gl.list = newList; | |
| gl.listMode = mode; | |
| }, | |
| glNormal3f: function(nx, ny, nz) { | |
| if (gl.listMode && this.addToList("glNormal3f", [nx, ny, nz])) return; | |
| DEBUG > 1 && console.log("glNormal3f", nx, ny, nz); | |
| gl.normal[0] = nx; | |
| gl.normal[1] = ny; | |
| gl.normal[2] = nz; | |
| gl.primitiveAttrs |= HAS_NORMAL; | |
| }, | |
| glNormal3fv: function(v) { | |
| if (gl.listMode && this.addToList("glNormal3fv", [v.slice()])) return; | |
| DEBUG > 1 && console.log("glNormal3fv", Array.from(v)); | |
| gl.normal.set(v); | |
| gl.primitiveAttrs |= HAS_NORMAL; | |
| }, | |
| glNormalPointer: function(type, stride, pointer) { | |
| if (gl.listMode && this.addToList("glNormalPointer", [type, stride, pointer])) return; | |
| DEBUG > 1 && console.log("glNormalPointer", GL_Symbol(type), stride, pointer); | |
| gl.clientState.normalArray.size = 3; | |
| gl.clientState.normalArray.type = type; | |
| gl.clientState.normalArray.stride = stride; | |
| gl.clientState.normalArray.pointer = pointer; | |
| }, | |
| glPixelStorei: function(pname, param) { | |
| switch (pname) { | |
| case webgl.UNPACK_ALIGNMENT: | |
| DEBUG > 1 && console.log("glPixelStorei GL_UNPACK_ALIGNMENT", param); | |
| //@webgl.pixelStorei(webgl.UNPACK_ALIGNMENT, param); | |
| break; | |
| case GL.UNPACK_LSB_FIRST: | |
| if (param !== 0) console.log("UNIMPLEMENTED glPixelStorei GL_UNPACK_LSB_FIRST", param); | |
| break; | |
| case GL.UNPACK_ROW_LENGTH: | |
| DEBUG > 1 && console.log("glPixelStorei GL_UNPACK_ROW_LENGTH", param); | |
| gl.pixelStoreUnpackRowLength = param; | |
| break; | |
| case GL.UNPACK_SKIP_ROWS: | |
| DEBUG > 1 && console.log("glPixelStorei GL_UNPACK_SKIP_ROWS", param); | |
| gl.pixelStoreUnpackSkipRows = param; | |
| break; | |
| case GL.UNPACK_SKIP_PIXELS: | |
| DEBUG > 1 && console.log("glPixelStorei GL_UNPACK_SKIP_PIXELS", param); | |
| gl.pixelStoreUnpackSkipPixels = param; | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glPixelStorei", pname, param); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glPixelStorei " + pname); | |
| } | |
| }, | |
| glPolygonOffset: function(factor, units) { | |
| if (gl.listMode && this.addToList("glPolygonOffset", [factor, units])) return; | |
| DEBUG > 1 && console.log("glPolygonOffset", factor, units); | |
| webgl.polygonOffset(factor, units); | |
| }, | |
| glPushAttrib: function(mask) { | |
| if (gl.listMode && this.addToList("glPushAttrib", [mask])) return; | |
| if (DEBUG > 0) { | |
| var maskString = ''; | |
| if (mask === GL.ALL_ATTRIB_BITS) { | |
| maskString = 'GL_ALL_ATTRIB_BITS'; | |
| } else { | |
| if (mask & GL.CURRENT_BIT) { maskString += 'GL_CURRENT_BIT '; mask &= ~GL.CURRENT_BIT; } | |
| if (mask & GL.ENABLE_BIT) { maskString += 'GL_ENABLE_BIT '; mask &= ~GL.ENABLE_BIT; } | |
| if (mask & GL.LIGHTING_BIT) { maskString += 'GL_LIGHTING_BIT '; mask &= ~GL.LIGHTING_BIT; } | |
| if (mask & GL.COLOR_BUFFER_BIT) { maskString += 'GL_COLOR_BUFFER_BIT '; mask &= ~GL.COLOR_BUFFER_BIT; } | |
| if (mask & GL.DEPTH_BUFFER_BIT) { maskString += 'GL_DEPTH_BUFFER_BIT '; mask &= ~GL.DEPTH_BUFFER_BIT; } | |
| if (mask & GL.POLYGON_BIT) { maskString += 'GL_POLYGON_BIT '; mask &= ~GL.POLYGON_BIT; } | |
| if (mask & GL.TEXTURE_BIT) { maskString += 'GL_TEXTURE_BIT '; mask &= ~GL.TEXTURE_BIT; } | |
| if (mask) for (var i = 0; i < 32; i++) { | |
| if (mask & (1 << i)) maskString += " " + (1 << i); | |
| } | |
| } | |
| console.log("UNIMPLEMENTED glPushAttrib", maskString); | |
| } else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glPushAttrib"); | |
| }, | |
| glPushMatrix: function() { | |
| if (gl.listMode && this.addToList("glPushMatrix", [])) return; | |
| gl.matrix = new Float32Array(gl.matrix); | |
| gl.matrices[gl.matrixMode].unshift(gl.matrix); | |
| DEBUG > 1 && console.log("glPushMatrix", GL_Symbol(gl.matrixMode), "=>", Array.from(gl.matrix)); | |
| }, | |
| glPointSize: function(size) { | |
| if (gl.listMode && this.addToList("glPointSize", [size])) return; | |
| DEBUG > 1 && console.log("glPointSize", size); | |
| gl.pointSize = size; | |
| }, | |
| glPopAttrib: function() { | |
| if (gl.listMode && this.addToList("glPopAttrib", [])) return; | |
| if (DEBUG) console.log("UNIMPLEMENTED glPopAttrib"); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glPopAttrib"); | |
| }, | |
| glPopMatrix: function() { | |
| if (gl.listMode && this.addToList("glPopMatrix", [])) return; | |
| if (gl.matrices[gl.matrixMode].length <= 1) | |
| return DEBUG > 0 && console.warn("OpenGL: matrix stack underflow"); | |
| gl.matrices[gl.matrixMode].shift(); | |
| gl.matrix = gl.matrices[gl.matrixMode][0]; | |
| DEBUG > 1 && console.log("glPopMatrix", GL_Symbol(gl.matrixMode), "=>", Array.from(gl.matrix)); | |
| }, | |
| glRasterPos3f: function(x, y, z) { | |
| if (gl.listMode && this.addToList("glRasterPos3f", [x, y, z])) return; | |
| DEBUG > 1 && console.log("glRasterPos3f", x, y, z); | |
| gl.rasterPos[0] = x; | |
| gl.rasterPos[1] = y; | |
| gl.rasterPos[2] = z; | |
| gl.rasterPos[3] = 1; | |
| // transform point via modelview and projection matrices | |
| var m = gl.matrices[GL.PROJECTION][0]; | |
| transformPoint(m, gl.rasterPos, gl.rasterPos); | |
| m = gl.matrices[GL.MODELVIEW][0]; | |
| transformPoint(m, gl.rasterPos, gl.rasterPos); | |
| // transform to window coordinates | |
| gl.rasterPos[0] = (gl.rasterPos[0] * 0.5 + 0.5) * gl.viewport[2] + gl.viewport[0]; | |
| gl.rasterPos[1] = (gl.rasterPos[1] * 0.5 + 0.5) * gl.viewport[3] + gl.viewport[1]; | |
| gl.rasterPos[2] = (gl.rasterPos[2] * 0.5 + 0.5) * (gl.depthRange[1] - gl.depthRange[0]) + gl.depthRange[0]; | |
| // remember raster color | |
| gl.rasterColor.set(gl.color); | |
| }, | |
| glReadPixels: function(x, y, width, height, format, type, pixels) { | |
| if (gl.listMode && this.addToList("glReadPixels", [x, y, width, height, format, type, pixels])) return; | |
| var swizzle = false; | |
| switch (format) { | |
| case webgl.RGBA: | |
| break; | |
| case GL.BGRA: | |
| format = webgl.RGBA; | |
| swizzle = true; | |
| break; | |
| default: | |
| if (DEBUG) console.warn("UNIMPLEMENTED glReadPixels format " + GL_Symbol(format)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glReadPixels format " + GL_Symbol(format)); | |
| return; | |
| } | |
| switch (type) { | |
| case webgl.UNSIGNED_BYTE: | |
| pixels = new Uint8Array(pixels); | |
| break; | |
| default: | |
| if (DEBUG) console.warn("UNIMPLEMENTED glReadPixels type " + GL_Symbol(type)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glReadPixels type " + GL_Symbol(type)); | |
| return; | |
| } | |
| DEBUG > 1 && console.log("glReadPixels", x, y, width, height, GL_Symbol(format), GL_Symbol(type), pixels); | |
| webgl.readPixels(x, y, width, height, format, type, pixels); | |
| if (swizzle) { | |
| for (var i = 0; i < pixels.length; i += 4) { | |
| var r = pixels[i]; | |
| var b = pixels[i+2]; | |
| pixels[i] = b; | |
| pixels[i+2] = r; | |
| } | |
| } | |
| }, | |
| glTranslated: function(x, y, z) { | |
| if (gl.listMode && this.addToList("glTranslated", [x, y, z])) return; | |
| translateMatrix(gl.matrix, x, y, z); | |
| DEBUG > 1 && console.log("glTranslated", GL_Symbol(gl.matrixMode), x, y, z, "=>", Array.from(gl.matrix)); | |
| }, | |
| glTranslatef: function(x, y, z) { | |
| if (gl.listMode && this.addToList("glTranslatef", [x, y, z])) return; | |
| translateMatrix(gl.matrix, x, y, z); | |
| DEBUG > 1 && console.log("glTranslatef", GL_Symbol(gl.matrixMode), x, y, z, "=>", Array.from(gl.matrix)); | |
| }, | |
| glRotatef: function(angle, x, y, z) { | |
| if (gl.listMode && this.addToList("glRotatef", [angle, x, y, z])) return; | |
| rotateMatrix(gl.matrix, angle, x, y, z); | |
| DEBUG > 1 && console.log("glRotatef", GL_Symbol(gl.matrixMode), angle, x, y, z, "=>", Array.from(gl.matrix)); | |
| }, | |
| glScalef: function(x, y, z) { | |
| if (gl.listMode && this.addToList("glScalef", [x, y, z])) return; | |
| scaleMatrix(gl.matrix, x, y, z); | |
| DEBUG > 1 && console.log("glScalef", GL_Symbol(gl.matrixMode), x, y, z, "=>", Array.from(gl.matrix)); | |
| }, | |
| glScaled: function(x, y, z) { | |
| if (gl.listMode && this.addToList("glScaled", [x, y, z])) return; | |
| scaleMatrix(gl.matrix, x, y, z); | |
| DEBUG > 1 && console.log("glScaled", GL_Symbol(gl.matrixMode), x, y, z, "=>", Array.from(gl.matrix)); | |
| }, | |
| glShadeModel: function(mode) { | |
| if (gl.listMode && this.addToList("glShadeModel", [mode])) return; | |
| if (DEBUG) console.log("UNIMPLEMENTED glShadeModel", GL_Symbol(mode)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glShadeModel " + GL_Symbol(mode)); | |
| }, | |
| glStencilFunc: function(func, ref, mask) { | |
| if (gl.listMode && this.addToList("glStencilFunc", [func, ref, mask])) return; | |
| DEBUG > 1 && console.log("glStencilFunc", GL_Symbol(func), ref, '0x'+(mask>>>0).toString(16)); | |
| webgl.stencilFunc(func, ref, mask); | |
| }, | |
| glStencilOp: function(fail, zfail, zpass) { | |
| if (gl.listMode && this.addToList("glStencilOp", [fail, zfail, zpass])) return; | |
| DEBUG > 1 && console.log("glStencilOp", GL_Symbol(fail), GL_Symbol(zfail), GL_Symbol(zpass)); | |
| webgl.stencilOp(fail, zfail, zpass); | |
| }, | |
| glTexEnv: function(target, pname, param) { | |
| if (gl.listMode && this.addToList("glTexEnv", [target, pname, param])) return; | |
| switch (pname) { | |
| case GL.TEXTURE_ENV_MODE: | |
| switch (param) { | |
| case GL.MODULATE: | |
| // Modulate means multiply the texture color with the fragment color | |
| // which is what our fragment shader does by default | |
| DEBUG > 1 && console.log("glTexEnv", GL_Symbol(target), "GL_TEXTURE_ENV_MODE", GL_Symbol(param)); | |
| gl.textureEnvMode = param; | |
| return; | |
| case GL.REPLACE: | |
| // Replace means use the texture color as the fragment color | |
| // which we emulate by forcing the uniform color to white | |
| DEBUG > 1 && console.log("glTexEnv", GL_Symbol(target), "GL_TEXTURE_ENV_MODE", GL_Symbol(param)); | |
| gl.textureEnvMode = param; | |
| return; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glTexEnv", GL_Symbol(target), "GL_TEXTURE_ENV_MODE", GL_Symbol(param)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexEnv " + GL_Symbol(target) + " GL_TEXTURE_ENV_MODE " + GL_Symbol(param)); | |
| } | |
| break; | |
| default: | |
| if (DEBUG) console.log("UNIMPLEMENTED glTexEnv", GL_Symbol(target), GL_Symbol(pname), GL_Symbol(param)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexEnv " + GL_Symbol(target) + " " + GL_Symbol(pname)); | |
| } | |
| }, | |
| glTexEnvi: function(target, pname, param) { | |
| this.glTexEnv(target, pname, param); | |
| }, | |
| glTexEnvf: function(target, pname, param) { | |
| this.glTexEnv(target, pname, param); | |
| }, | |
| glTexGen: function(coord, pname, param) { | |
| if (gl.listMode && this.addToList("glTexGen", [coord, pname, param])) return; | |
| if (DEBUG) console.log("UNIMPLEMENTED glTexGen", GL_Symbol(coord, "S"), GL_Symbol(pname), GL_Symbol(param)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexGen " + GL_Symbol(pname)); | |
| }, | |
| glTexGeni: function(coord, pname, param) { | |
| this.glTexGen(coord, pname, param); | |
| }, | |
| glTexGenf: function(coord, pname, param) { | |
| this.glTexGen(coord, pname, param); | |
| }, | |
| glTexGend: function(coord, pname, params) { | |
| this.glTexGen(coord, pname, param); | |
| }, | |
| glTexGenfv: function(coord, pname, params) { | |
| if (gl.listMode && this.addToList("glTexGenfv", [coord, pname, params])) return; | |
| if (DEBUG) console.log("UNIMPLEMENTED glTexGenfv", GL_Symbol(coord, "S"), GL_Symbol(pname), Array.from(params)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexGenfv " + GL_Symbol(pname)); | |
| }, | |
| glTexImage2D: function(target, level, internalformat, width, height, border, format, type, pixels) { | |
| if (gl.listMode && this.addToList("glTexImage2D", [target, level, internalformat, width, height, border, format, type, pixels])) return; | |
| DEBUG > 1 && console.log("glTexImage2D", GL_Symbol(target), level, GL_Symbol(internalformat), width, height, border, GL_Symbol(format), GL_Symbol(type), pixels); | |
| gl.texture.width = width; | |
| gl.texture.height = height; | |
| // WebGL does not support GL_UNPACK_ROW_LENGTH, GL_UNPACK_SKIP_ROWS, GL_UNPACK_SKIP_PIXELS | |
| if (gl.pixelStoreUnpackRowLength !== 0 && gl.pixelStoreUnpackRowLength !== gl.texture.width) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED glTexImage2D GL_UNPACK_ROW_LENGTH " + gl.pixelStoreUnpackRowLength); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexImage2D GL_UNPACK_ROW_LENGTH"); | |
| } | |
| if (gl.pixelStoreUnpackSkipRows !== 0) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED glTexImage2D GL_UNPACK_SKIP_ROWS " + gl.pixelStoreUnpackSkipRows); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexImage2D GL_UNPACK_SKIP_ROWS"); | |
| } | |
| if (gl.pixelStoreUnpackSkipPixels !== 0) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED glTexImage2D GL_UNPACK_SKIP_PIXELS " + gl.pixelStoreUnpackSkipPixels); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexImage2D GL_UNPACK_SKIP_PIXELS"); | |
| } | |
| // WebGL only supports GL_RGBA | |
| switch (format) { | |
| case webgl.RGBA: | |
| if (DEBUG) console.warn("glTexImage2D GL_RGBA: need to not swizzle BGRA"); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexImage2D format GL_RGBA"); | |
| break; | |
| case GL.BGRA: | |
| format = webgl.RGBA; | |
| break; | |
| default: | |
| if (DEBUG) console.warn("UNIMPLEMENTED glTexImage2D format " + GL_Symbol(format)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexImage2D format " + GL_Symbol(format)); | |
| return; | |
| } | |
| // pixels are coming in via FFI as void* (ArrayBuffer) | |
| // convert to appropriate typed array | |
| // in OpenGL, pixels can be null to allocate texture storage | |
| if (!pixels) pixels = new ArrayBuffer(width * height * 4); | |
| switch (type) { | |
| case webgl.UNSIGNED_BYTE: | |
| pixels = new Uint8Array(pixels); | |
| gl.texture.pixels = pixels; // for debugging | |
| break; | |
| default: | |
| if (DEBUG) console.warn("UNIMPLEMENTED glTexImage2D type " + GL_Symbol(type)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexImage2D type " + GL_Symbol(type)); | |
| return; | |
| } | |
| webgl.texImage2D(target, level, internalformat, width, height, border, format, type, pixels); | |
| if (gl.texture.generateMipmapSGIS) webgl.generateMipmap(target); | |
| }, | |
| debugTexture: function(texture) { | |
| if (texture === false) { | |
| var canvas = document.getElementById("texDebug"); | |
| if (canvas) canvas.remove(); | |
| return; | |
| } | |
| if (!texture) texture = gl.texture; | |
| var pixels = texture.pixels; | |
| var width = texture.width; | |
| var height = texture.height; | |
| var data = new Uint8Array(pixels); | |
| var canvas = document.getElementById("texDebug"); | |
| if (!canvas) { | |
| canvas = document.createElement('canvas'); | |
| canvas.id = "texDebug"; | |
| canvas.style.position = "absolute"; | |
| canvas.style.zIndex = 1000; | |
| document.body.appendChild(canvas); | |
| } | |
| canvas.width = width; | |
| canvas.height = height; | |
| var ctx = canvas.getContext('2d'); | |
| var imageData = ctx.createImageData(width, height); | |
| imageData.data.set(data); | |
| ctx.putImageData(imageData, 0, 0); | |
| }, | |
| glTexCoord2d: function(s, t) { | |
| if (gl.listMode && this.addToList("glTexCoord2d", [s, t])) return; | |
| DEBUG > 1 && console.log("glTexCoord2d", s, t); | |
| gl.texCoord[0] = s; | |
| gl.texCoord[1] = t; | |
| gl.primitiveAttrs |= HAS_TEXCOORD; | |
| }, | |
| glTexCoord2f: function(s, t) { | |
| if (gl.listMode && this.addToList("glTexCoord2f", [s, t])) return; | |
| DEBUG > 1 && console.log("glTexCoord2f", s, t); | |
| gl.texCoord[0] = s; | |
| gl.texCoord[1] = t; | |
| gl.primitiveAttrs |= HAS_TEXCOORD; | |
| }, | |
| glTexCoord2fv: function(v) { | |
| if (gl.listMode && this.addToList("glTexCoord2fv", [v.slice()])) return; | |
| DEBUG > 1 && console.log("glTexCoord2fv", Array.from(v)); | |
| gl.texCoord.set(v); | |
| gl.primitiveAttrs |= HAS_TEXCOORD; | |
| }, | |
| glTexCoordPointer: function(size, type, stride, pointer) { | |
| if (gl.listMode && this.addToList("glTexCoordPointer", [size, type, stride, pointer])) return; | |
| DEBUG > 1 && console.log("glTexCoordPointer", size, GL_Symbol(type), stride, pointer); | |
| gl.clientState.textureCoordArray.size = size; | |
| gl.clientState.textureCoordArray.type = type; | |
| gl.clientState.textureCoordArray.stride = stride; | |
| gl.clientState.textureCoordArray.pointer = pointer; | |
| }, | |
| glTexParameteri: function(target, pname, param) { | |
| if (gl.listMode && this.addToList("glTexParameteri", [target, pname, param])) return; | |
| DEBUG > 1 && console.log("glTexParameteri", GL_Symbol(target), GL_Symbol(pname), GL_Symbol(param)); | |
| if (pname === GL.GENERATE_MIPMAP_SGIS) { | |
| // WebGL does not support GL_GENERATE_MIPMAP_SGIS. Emulate it | |
| gl.texture.generateMipmapSGIS = param; | |
| // if an image has been uploaded already, generate mipmaps now | |
| if (param && gl.texture.width > 1) webgl.generateMipmap(target); | |
| return; | |
| } | |
| webgl.texParameteri(target, pname, param); | |
| }, | |
| glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { | |
| if (gl.listMode && this.addToList("glTexSubImage2D", [target, level, xoffset, yoffset, width, height, format, type, pixels])) return; | |
| DEBUG > 1 && console.log("glTexSubImage2D", GL_Symbol(target), level, xoffset, yoffset, width, height, GL_Symbol(format), GL_Symbol(type), pixels); | |
| // WebGL does not support GL_UNPACK_ROW_LENGTH, GL_UNPACK_SKIP_ROWS, GL_UNPACK_SKIP_PIXELS | |
| // emulate GL_UNPACK_SKIP_ROWS | |
| var pixelsOffset = gl.pixelStoreUnpackSkipRows * gl.texture.width; // to be multiplied by pixel size below | |
| // assume GL_UNPACK_ROW_LENGTH is full width (which is the case when uploading part of a bitmap in Squeak) | |
| if (gl.pixelStoreUnpackRowLength !== 0 && gl.pixelStoreUnpackRowLength !== gl.texture.width) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED glTexSubImage2D GL_UNPACK_ROW_LENGTH " + gl.pixelStoreUnpackRowLength); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexSubImage2D GL_UNPACK_ROW_LENGTH"); | |
| } | |
| // WebGL does not support GL_UNPACK_SKIP_PIXELS to allow different width | |
| if (width !== gl.texture.width) { | |
| // we could either | |
| // 1. call texSubImage2D for each row | |
| // 2. copy subimage pixels into a new buffer | |
| // 3. call texSubImage2D for the whole width so we don't need to skip pixels | |
| // we choose 3. for now | |
| width = gl.texture.width; | |
| xoffset = 0; | |
| } | |
| // WebGL only supports RGB not BGR | |
| switch (format) { | |
| case webgl.RGBA: | |
| pixelsOffset *= 4; | |
| break; | |
| case GL.BGRA: | |
| pixelsOffset *= 4; | |
| format = webgl.RGBA; | |
| break; | |
| default: | |
| if (DEBUG) console.warn("UNIMPLEMENTED glTexSubImage2D format " + GL_Symbol(format)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexSubImage2D format " + GL_Symbol(format)); | |
| return; | |
| } | |
| // pixels are coming in via FFI as void* (ArrayBuffer) | |
| // convert to appropriate typed array | |
| switch (type) { | |
| case webgl.UNSIGNED_BYTE: | |
| pixels = new Uint8Array(pixels, pixelsOffset); | |
| break; | |
| default: | |
| if (DEBUG) console.warn("UNIMPLEMENTED glTexSubImage2D type " + GL_Symbol(type)); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED glTexSubImage2D type " + GL_Symbol(type)); | |
| return; | |
| } | |
| webgl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); | |
| if (gl.texture.generateMipmapSGIS) webgl.generateMipmap(target); | |
| }, | |
| glVertex2f: function(x, y) { | |
| if (gl.listMode && this.addToList("glVertex2f", [x, y])) return; | |
| DEBUG > 1 && console.log("glVertex2f", x, y); | |
| var position = [x, y]; | |
| this.pushVertex(position); | |
| }, | |
| glVertex3f: function(x, y, z) { | |
| if (gl.listMode && this.addToList("glVertex3f", [x, y, z])) return; | |
| DEBUG > 1 && console.log("glVertex3f", x, y, z); | |
| var position = [x, y, z]; | |
| this.pushVertex(position); | |
| }, | |
| glVertex3fv: function(v) { | |
| if (gl.listMode && this.addToList("glVertex3fv", [v.slice()])) return; | |
| DEBUG > 1 && console.log("glVertex3fv", Array.from(v)); | |
| this.pushVertex(v); | |
| }, | |
| glVertex2i: function(x, y) { | |
| if (gl.listMode && this.addToList("glVertex2i", [x, y])) return; | |
| DEBUG > 1 && console.log("glVertex2i", x, y); | |
| var position = [x, y]; | |
| this.pushVertex(position); | |
| }, | |
| glVertexPointer: function(size, type, stride, pointer) { | |
| if (gl.listMode && this.addToList("glVertexPointer", [size, type, stride, pointer])) return; | |
| DEBUG > 1 && console.log("glVertexPointer", size, GL_Symbol(type), stride, pointer); | |
| gl.clientState.vertexArray.size = size; | |
| gl.clientState.vertexArray.type = type; | |
| gl.clientState.vertexArray.stride = stride; | |
| gl.clientState.vertexArray.pointer = pointer; | |
| }, | |
| glViewport: function(x, y, width, height) { | |
| if (gl.listMode && this.addToList("glViewport", [x, y, width, height])) return; | |
| DEBUG > 1 && console.log("glViewport", x, y, width, height); | |
| webgl.viewport(x, y, width, height); | |
| gl.viewport[0] = x; | |
| gl.viewport[1] = y; | |
| gl.viewport[2] = width; | |
| gl.viewport[3] = height; | |
| }, | |
| glXGetProcAddressARB: function(procName) { | |
| procName = Squeak.bytesAsString(procName); | |
| DEBUG > 1 && console.log("glXGetProcAddressARB", procName); | |
| var handle = this.ffi.ffiLookupFunc(this, procName); | |
| if (!handle) { | |
| if (DEBUG) console.warn("UNIMPLEMENTED EXT FUNC", procName); | |
| else this.vm.warnOnce("OpenGL: UNIMPLEMENTED EXT FUNC" + procName); | |
| } | |
| return handle; | |
| }, | |
| pushVertex: function(position) { | |
| var primitive = gl.primitive; | |
| if (!primitive) throw Error("OpenGL: glBegin not called"); | |
| if (!primitive.vertexSize) { | |
| var vertexSize = 3; | |
| if (gl.primitiveAttrs & HAS_NORMAL) vertexSize += 3; | |
| if (gl.primitiveAttrs & HAS_COLOR) vertexSize += 4; | |
| if (gl.primitiveAttrs & HAS_TEXCOORD) vertexSize += 2; | |
| primitive.vertexSize = vertexSize; | |
| primitive.vertexAttrs = gl.primitiveAttrs; | |
| } | |
| var vertex = new Float32Array(primitive.vertexSize); | |
| var offset = 0; | |
| vertex.set(position, offset); offset += 3; | |
| if (primitive.vertexAttrs & HAS_NORMAL) { | |
| vertex.set(gl.normal, offset); offset += 3; | |
| } | |
| if (primitive.vertexAttrs & HAS_COLOR) { | |
| vertex.set(gl.color, offset); offset += 4; | |
| } | |
| if (primitive.vertexAttrs & HAS_TEXCOORD) { | |
| vertex.set(gl.texCoord, offset); offset += 2; | |
| } | |
| primitive.vertices.push(vertex); | |
| }, | |
| // Shader source code | |
| // Note: we could take a look at Emscripten's glemu to add some more features | |
| // https://github.com/emscripten-core/emscripten/blob/cb99414efed02dc61d04315d3e3cf5ad3180e56f/src/library_glemu.js#L2170 | |
| // The structure is a bit different but applicable | |
| vertexShaderSource: function(shaderFlags) { | |
| var src = []; | |
| src.push("uniform mat4 uModelView;"); | |
| src.push("uniform mat4 uProjection;"); | |
| src.push("attribute vec3 aPosition;"); | |
| if (shaderFlags & HAS_COLOR) { | |
| src.push("attribute vec4 aColor;"); | |
| } else if (shaderFlags & ANY_LIGHTS) { | |
| src.push("uniform vec4 uColor;"); | |
| } | |
| if (shaderFlags & (HAS_COLOR | ANY_LIGHTS)) { | |
| src.push("varying vec4 vColor;"); | |
| } | |
| if (shaderFlags & HAS_TEXCOORD && shaderFlags & USE_TEXTURE) { | |
| src.push("uniform mat4 uTextureMatrix;"); | |
| src.push("attribute vec2 aTexCoord;"); | |
| src.push("varying vec2 vTexCoord;"); | |
| } | |
| if (shaderFlags & USE_POINT_SIZE) { | |
| src.push("uniform float uPointSize;"); | |
| } | |
| var numLights = (shaderFlags & NUM_LIGHTS_MASK) >> NUM_LIGHTS_SHIFT; | |
| if (numLights > 0) { | |
| if (shaderFlags & HAS_NORMAL) { | |
| src.push("attribute vec3 aNormal;"); | |
| } else { | |
| src.push("uniform vec3 uNormal;"); | |
| } | |
| src.push("uniform mat3 uNormalMatrix;"); | |
| src.push("uniform vec4 uLightModelAmbient;"); | |
| src.push("uniform vec4 uMaterialAmbient;"); | |
| src.push("uniform vec4 uMaterialDiffuse;"); | |
| src.push("uniform vec4 uMaterialSpecular;"); | |
| src.push("uniform vec4 uMaterialEmission;"); | |
| src.push("uniform float uMaterialShininess;"); | |
| src.push("struct Light {"); | |
| src.push(" vec4 position;"); | |
| src.push(" vec4 ambient;"); | |
| src.push(" vec4 diffuse;"); | |
| src.push(" vec4 specular;"); | |
| src.push("};"); | |
| src.push("uniform Light uLights[" + numLights + "];"); | |
| } | |
| var numClipPlanes = (shaderFlags & NUM_CLIP_PLANES_MASK) >> NUM_CLIP_PLANES_SHIFT; | |
| if (numClipPlanes > 0) { | |
| src.push("uniform vec4 uClipPlanes[" + numClipPlanes + "];"); | |
| src.push("varying float vClipDist[" + numClipPlanes + "];"); | |
| } | |
| var fog = (shaderFlags & FOG_MASK) >> FOG_SHIFT; | |
| if (fog !== NO_FOG) { | |
| if (fog === LINEAR_FOG) { | |
| src.push("uniform float uFogEnd;"); | |
| src.push("uniform float uFogRange;"); | |
| } else { | |
| src.push("uniform float uFogDensity;"); | |
| } | |
| src.push("varying float vFogDist;"); | |
| } | |
| src.push("void main(void) {"); | |
| if (shaderFlags & HAS_COLOR) { | |
| src.push(" vColor = aColor;"); | |
| } else if (shaderFlags & ANY_LIGHTS) { | |
| src.push(" vColor = uColor;"); | |
| } | |
| src.push(" vec4 position = uModelView * vec4(aPosition, 1.0);"); | |
| if (numLights > 0) { | |
| if (shaderFlags & HAS_NORMAL) { | |
| src.push(" vec3 normal = normalize(uNormalMatrix * aNormal);"); | |
| } else { | |
| src.push(" vec3 normal = normalize(uNormalMatrix * uNormal);"); | |
| } | |
| src.push(" vec4 lighting = uMaterialEmission;"); | |
| src.push(" lighting += uMaterialAmbient * uLightModelAmbient;"); | |
| src.push(" vec3 eyeDir = normalize(-position.xyz);"); | |
| src.push(" for (int i = 0; i < " + numLights + "; i++) {"); | |
| src.push(" Light light = uLights[i];"); | |
| src.push(" vec3 lightDir;"); | |
| src.push(" if (light.position.w == 0.0) {"); | |
| src.push(" lightDir = normalize(light.position.xyz);"); | |
| src.push(" } else {"); | |
| src.push(" lightDir = normalize(light.position.xyz - position.xyz);"); | |
| src.push(" }"); | |
| src.push(" float nDotL = max(dot(normal, lightDir), 0.0);"); | |
| src.push(" vec4 ambient = uMaterialAmbient * light.ambient;"); | |
| src.push(" vec4 diffuse = uMaterialDiffuse * light.diffuse * nDotL;"); | |
| src.push(" vec4 specular = vec4(0.0);"); | |
| src.push(" if (nDotL > 0.0) {"); | |
| src.push(" vec3 halfVector = normalize(lightDir + eyeDir);"); | |
| src.push(" float nDotHV = max(dot(normal, halfVector), 0.0);"); | |
| src.push(" specular = uMaterialSpecular * light.specular * pow(nDotHV, uMaterialShininess);"); | |
| src.push(" }"); | |
| src.push(" lighting += ambient + diffuse + specular;"); | |
| src.push(" }"); | |
| src.push(" vColor = clamp(lighting, 0.0, 1.0);"); | |
| src.push(" vColor.a = uMaterialDiffuse.a;"); | |
| } | |
| if (numClipPlanes > 0) { | |
| src.push(" for (int i = 0; i < " + numClipPlanes + "; i++) {"); | |
| src.push(" vClipDist[i] = dot(position, uClipPlanes[i]);"); | |
| src.push(" }"); | |
| } | |
| if (shaderFlags & HAS_TEXCOORD && shaderFlags & USE_TEXTURE) { | |
| src.push(" vec4 texCoord = uTextureMatrix * vec4(aTexCoord, 0.0, 1.0);"); | |
| src.push(" vTexCoord = texCoord.xy / texCoord.w;"); | |
| } | |
| if (fog !== NO_FOG) { | |
| src.push(" vFogDist = -position.z;"); | |
| } | |
| if (shaderFlags & USE_POINT_SIZE) { | |
| src.push(" gl_PointSize = uPointSize;"); | |
| } | |
| src.push(" gl_Position = uProjection * position;"); | |
| src.push("}"); | |
| var src = src.join("\n"); | |
| DEBUG > 1 && console.log(src); | |
| return src; | |
| }, | |
| fragmentShaderSource: function(shaderFlags) { | |
| var src = []; | |
| src.push("precision mediump float;"); | |
| var numClipPlanes = (shaderFlags & NUM_CLIP_PLANES_MASK) >> NUM_CLIP_PLANES_SHIFT; | |
| if (numClipPlanes > 0) { | |
| src.push("varying float vClipDist[" + numClipPlanes + "];"); | |
| } | |
| if (shaderFlags & (HAS_COLOR | ANY_LIGHTS)) { | |
| src.push("varying vec4 vColor;"); | |
| } else { | |
| src.push("uniform vec4 uColor;"); | |
| } | |
| if (shaderFlags & USE_TEXTURE) { | |
| if (shaderFlags & HAS_TEXCOORD) { | |
| src.push("varying vec2 vTexCoord;"); | |
| } else { | |
| src.push("uniform vec2 uTexCoord;"); | |
| } | |
| src.push("uniform sampler2D uSampler;"); | |
| } | |
| var fog = (shaderFlags & FOG_MASK) >> FOG_SHIFT; | |
| if (fog !== NO_FOG) { | |
| if (fog === LINEAR_FOG) { | |
| src.push("uniform float uFogEnd;"); | |
| src.push("uniform float uFogRange;"); | |
| } else { | |
| src.push("uniform float uFogDensity;"); | |
| } | |
| src.push("uniform vec4 uFogColor;"); | |
| src.push("varying float vFogDist;"); | |
| } | |
| src.push("void main(void) {"); | |
| if (numClipPlanes > 0) { | |
| src.push(" bool clipped = false;"); | |
| src.push(" for (int i = 0; i < " + numClipPlanes + "; i++) {"); | |
| src.push(" if (vClipDist[i] < 0.0) clipped = true;"); | |
| src.push(" }"); | |
| src.push(" if (clipped) discard;"); | |
| } | |
| if (shaderFlags & (HAS_COLOR | ANY_LIGHTS)) { | |
| src.push(" vec4 color = vColor;"); | |
| } else { | |
| src.push(" vec4 color = uColor;"); | |
| } | |
| if (shaderFlags & USE_TEXTURE) { | |
| if (shaderFlags & HAS_TEXCOORD) { | |
| src.push(" vec2 texCoord = vTexCoord;"); | |
| } else { | |
| src.push(" vec2 texCoord = uTexCoord;"); | |
| } | |
| src.push(" color *= texture2D(uSampler, texCoord).bgra;"); | |
| } | |
| if (fog !== NO_FOG) { | |
| switch (fog) { | |
| case LINEAR_FOG: | |
| src.push(" float fogAmount = (uFogEnd - vFogDist) / uFogRange;"); | |
| break; | |
| case EXP_FOG: | |
| src.push(" float fogAmount = exp(-uFogDensity * vFogDist);"); | |
| break; | |
| case EXP2_FOG: | |
| src.push(" float fogAmount = exp(-uFogDensity * uFogDensity * vFogDist * vFogDist);"); | |
| break; | |
| } | |
| src.push(" color.rgb = mix(uFogColor.rgb, color.rgb, clamp(fogAmount, 0.0, 1.0));"); | |
| } | |
| src.push(" gl_FragColor = color;"); | |
| src.push("}"); | |
| var src = src.join("\n"); | |
| DEBUG > 1 && console.log(src); | |
| return src; | |
| }, | |
| getLocations: function(program, shaderFlags) { | |
| var locations = {}; | |
| locations.uModelView = webgl.getUniformLocation(program, "uModelView"); | |
| locations.uProjection = webgl.getUniformLocation(program, "uProjection"); | |
| locations.aPosition = webgl.getAttribLocation(program, "aPosition"); | |
| if (shaderFlags & USE_TEXTURE) { | |
| if (shaderFlags & HAS_TEXCOORD) { | |
| locations.uTextureMatrix = webgl.getUniformLocation(program, "uTextureMatrix"); | |
| locations.aTexCoord = webgl.getAttribLocation(program, "aTexCoord"); | |
| } else { | |
| locations.uTexCoord = webgl.getUniformLocation(program, "uTexCoord"); | |
| } | |
| locations.uSampler = webgl.getUniformLocation(program, "uSampler"); | |
| } | |
| if (shaderFlags & HAS_COLOR) { | |
| locations.aColor = webgl.getAttribLocation(program, "aColor"); | |
| } else { | |
| locations.uColor = webgl.getUniformLocation(program, "uColor"); | |
| } | |
| if (shaderFlags & USE_POINT_SIZE) { | |
| locations.uPointSize = webgl.getUniformLocation(program, "uPointSize"); | |
| } | |
| var numLights = (shaderFlags & NUM_LIGHTS_MASK) >> NUM_LIGHTS_SHIFT; | |
| if (numLights > 0) { | |
| if (shaderFlags & HAS_NORMAL) { | |
| locations.aNormal = webgl.getAttribLocation(program, "aNormal"); | |
| } else{ | |
| locations.uNormal = webgl.getUniformLocation(program, "uNormal"); | |
| } | |
| locations.uNormalMatrix = webgl.getUniformLocation(program, "uNormalMatrix"); | |
| locations.uLightModelAmbient = webgl.getUniformLocation(program, "uLightModelAmbient"); | |
| locations.uMaterialAmbient = webgl.getUniformLocation(program, "uMaterialAmbient"); | |
| locations.uMaterialDiffuse = webgl.getUniformLocation(program, "uMaterialDiffuse"); | |
| locations.uMaterialSpecular = webgl.getUniformLocation(program, "uMaterialSpecular"); | |
| locations.uMaterialEmission = webgl.getUniformLocation(program, "uMaterialEmission"); | |
| locations.uMaterialShininess = webgl.getUniformLocation(program, "uMaterialShininess"); | |
| locations.uLights = []; | |
| for (var i = 0; i < numLights; i++) { | |
| var light = {}; | |
| light.position = webgl.getUniformLocation(program, "uLights[" + i + "].position"); | |
| light.ambient = webgl.getUniformLocation(program, "uLights[" + i + "].ambient"); | |
| light.diffuse = webgl.getUniformLocation(program, "uLights[" + i + "].diffuse"); | |
| light.specular = webgl.getUniformLocation(program, "uLights[" + i + "].specular"); | |
| locations.uLights.push(light); | |
| } | |
| } | |
| var numClipPlanes = (shaderFlags & NUM_CLIP_PLANES_MASK) >> NUM_CLIP_PLANES_SHIFT; | |
| if (numClipPlanes > 0) { | |
| locations.uClipPlanes = []; | |
| for (var i = 0; i < numClipPlanes; i++) { | |
| locations.uClipPlanes.push(webgl.getUniformLocation(program, "uClipPlanes[" + i + "]")); | |
| } | |
| } | |
| var fog = (shaderFlags & FOG_MASK) >> FOG_SHIFT; | |
| if (fog !== NO_FOG) { | |
| if (fog === LINEAR_FOG) { | |
| locations.uFogEnd = webgl.getUniformLocation(program, "uFogEnd"); | |
| locations.uFogRange = webgl.getUniformLocation(program, "uFogRange"); | |
| } else { | |
| locations.uFogDensity = webgl.getUniformLocation(program, "uFogDensity"); | |
| } | |
| locations.uFogColor = webgl.getUniformLocation(program, "uFogColor"); | |
| } | |
| DEBUG > 1 && console.log(locations); | |
| return locations; | |
| }, | |
| executeList: function(listId) { | |
| var list = gl.lists[listId]; | |
| if (!list) { | |
| DEBUG > 0 && console.warn("OpenGL: display list not found " + listId); | |
| return; | |
| } | |
| for (var i = 0; i < list.commands.length; i++) { | |
| var cmd = list.commands[i]; | |
| this[cmd.name].apply(this, cmd.args); | |
| } | |
| }, | |
| }; | |
| } | |
| function transformDirection(matrix, src, dst) { | |
| var x = src[0]; | |
| var y = src[1]; | |
| var z = src[2]; | |
| var rx = matrix[0] * x + matrix[4] * y + matrix[8] * z; | |
| var ry = matrix[1] * x + matrix[5] * y + matrix[9] * z; | |
| var rz = matrix[2] * x + matrix[6] * y + matrix[10] * z; | |
| dst[0] = rx; | |
| dst[1] = ry; | |
| dst[2] = rz; | |
| dst[3] = src[3]; | |
| } | |
| function transformPoint(matrix, src, dst) { | |
| var x = src[0]; | |
| var y = src[1]; | |
| var z = src[2]; | |
| var rx = matrix[0] * x + matrix[4] * y + matrix[8] * z + matrix[12]; | |
| var ry = matrix[1] * x + matrix[5] * y + matrix[9] * z + matrix[13]; | |
| var rz = matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14]; | |
| var rw = matrix[3] * x + matrix[7] * y + matrix[11] * z + matrix[15]; | |
| if (rw === 1) { | |
| dst[0] = rx; | |
| dst[1] = ry; | |
| dst[2] = rz; | |
| } else { | |
| if (rw !== 0) rw = 1 / rw; | |
| dst[0] = rx * rw; | |
| dst[1] = ry * rw; | |
| dst[2] = rz * rw; | |
| } | |
| dst[3] = src[3]; | |
| } | |
| function multVec4(matrix, src, dst) { | |
| var x = src[0]; | |
| var y = src[1]; | |
| var z = src[2]; | |
| var w = src[3]; | |
| dst[0] = matrix[0] * x + matrix[4] * y + matrix[8] * z + matrix[12] * w; | |
| dst[1] = matrix[1] * x + matrix[5] * y + matrix[9] * z + matrix[13] * w; | |
| dst[2] = matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14] * w; | |
| dst[3] = matrix[3] * x + matrix[7] * y + matrix[11] * z + matrix[15] * w; | |
| } | |
| function multMatrix(m1, m2) { | |
| var m00 = m1[0] * m2[0] + m1[4] * m2[1] + m1[8] * m2[2] + m1[12] * m2[3]; | |
| var m01 = m1[1] * m2[0] + m1[5] * m2[1] + m1[9] * m2[2] + m1[13] * m2[3]; | |
| var m02 = m1[2] * m2[0] + m1[6] * m2[1] + m1[10] * m2[2] + m1[14] * m2[3]; | |
| var m03 = m1[3] * m2[0] + m1[7] * m2[1] + m1[11] * m2[2] + m1[15] * m2[3]; | |
| var m10 = m1[0] * m2[4] + m1[4] * m2[5] + m1[8] * m2[6] + m1[12] * m2[7]; | |
| var m11 = m1[1] * m2[4] + m1[5] * m2[5] + m1[9] * m2[6] + m1[13] * m2[7]; | |
| var m12 = m1[2] * m2[4] + m1[6] * m2[5] + m1[10] * m2[6] + m1[14] * m2[7]; | |
| var m13 = m1[3] * m2[4] + m1[7] * m2[5] + m1[11] * m2[6] + m1[15] * m2[7]; | |
| var m20 = m1[0] * m2[8] + m1[4] * m2[9] + m1[8] * m2[10] + m1[12] * m2[11]; | |
| var m21 = m1[1] * m2[8] + m1[5] * m2[9] + m1[9] * m2[10] + m1[13] * m2[11]; | |
| var m22 = m1[2] * m2[8] + m1[6] * m2[9] + m1[10] * m2[10] + m1[14] * m2[11]; | |
| var m23 = m1[3] * m2[8] + m1[7] * m2[9] + m1[11] * m2[10] + m1[15] * m2[11]; | |
| var m30 = m1[0] * m2[12] + m1[4] * m2[13] + m1[8] * m2[14] + m1[12] * m2[15]; | |
| var m31 = m1[1] * m2[12] + m1[5] * m2[13] + m1[9] * m2[14] + m1[13] * m2[15]; | |
| var m32 = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14] * m2[15]; | |
| var m33 = m1[3] * m2[12] + m1[7] * m2[13] + m1[11] * m2[14] + m1[15] * m2[15]; | |
| m1[0] = m00; m1[1] = m01; m1[2] = m02; m1[3] = m03; | |
| m1[4] = m10; m1[5] = m11; m1[6] = m12; m1[7] = m13; | |
| m1[8] = m20; m1[9] = m21; m1[10] = m22; m1[11] = m23; | |
| m1[12] = m30; m1[13] = m31; m1[14] = m32; m1[15] = m33; | |
| } | |
| function translateMatrix(m, x, y, z) { | |
| m[12] += x * m[0] + y * m[4] + z * m[8]; | |
| m[13] += x * m[1] + y * m[5] + z * m[9]; | |
| m[14] += x * m[2] + y * m[6] + z * m[10]; | |
| m[15] += x * m[3] + y * m[7] + z * m[11]; | |
| } | |
| function rotateMatrix(m, angle, x, y, z) { | |
| // normalize axis vector | |
| var len = Math.sqrt(x * x + y * y + z * z); | |
| if (len === 0) return; | |
| if (len !== 1) { | |
| len = 1 / len; | |
| x *= len; | |
| y *= len; | |
| z *= len; | |
| } | |
| // create rotation matrix | |
| var degrees = Math.PI / 180; | |
| var c = Math.cos(angle * degrees); | |
| var s = Math.sin(angle * degrees); | |
| var t = 1 - c; | |
| var m00 = x * x * t + c; | |
| var m01 = x * y * t - z * s; | |
| var m02 = x * z * t + y * s; | |
| var m10 = y * x * t + z * s; | |
| var m11 = y * y * t + c; | |
| var m12 = y * z * t - x * s; | |
| var m20 = z * x * t - y * s; | |
| var m21 = z * y * t + x * s; | |
| var m22 = z * z * t + c; | |
| // multiply rotation matrix | |
| var m0 = m[0] * m00 + m[4] * m01 + m[8] * m02; | |
| var m1 = m[1] * m00 + m[5] * m01 + m[9] * m02; | |
| var m2 = m[2] * m00 + m[6] * m01 + m[10] * m02; | |
| var m3 = m[3] * m00 + m[7] * m01 + m[11] * m02; | |
| var m4 = m[0] * m10 + m[4] * m11 + m[8] * m12; | |
| var m5 = m[1] * m10 + m[5] * m11 + m[9] * m12; | |
| var m6 = m[2] * m10 + m[6] * m11 + m[10] * m12; | |
| var m7 = m[3] * m10 + m[7] * m11 + m[11] * m12; | |
| m[8] = m[0] * m20 + m[4] * m21 + m[8] * m22; | |
| m[9] = m[1] * m20 + m[5] * m21 + m[9] * m22; | |
| m[10] = m[2] * m20 + m[6] * m21 + m[10] * m22; | |
| m[11] = m[3] * m20 + m[7] * m21 + m[11] * m22; | |
| m[0] = m0; m[1] = m1; m[2] = m2; m[3] = m3; | |
| m[4] = m4; m[5] = m5; m[6] = m6; m[7] = m7; | |
| } | |
| function scaleMatrix(m, x, y, z) { | |
| m[0] *= x; m[1] *= x; m[2] *= x; m[3] *= x; | |
| m[4] *= y; m[5] *= y; m[6] *= y; m[7] *= y; | |
| m[8] *= z; m[9] *= z; m[10] *= z; m[11] *= z; | |
| } | |
| function transposeMatrix(m) { | |
| var m01 = m[1], m02 = m[2], m03 = m[3], | |
| m12 = m[6], m13 = m[7], | |
| m23 = m[11]; | |
| m[1] = m[4]; m[2] = m[8]; m[3] = m[12]; | |
| m[4] = m01; m[6] = m[9]; m[7] = m[13]; | |
| m[8] = m02; m[9] = m12; m[11] = m[14]; | |
| m[12] = m03; m[13] = m13; m[14] = m23; | |
| } | |
| function invertMatrix(src, dst) { | |
| if (!dst) dst = src; | |
| var m00 = src[0], m01 = src[1], m02 = src[2], m03 = src[3], | |
| m10 = src[4], m11 = src[5], m12 = src[6], m13 = src[7], | |
| m20 = src[8], m21 = src[9], m22 = src[10], m23 = src[11], | |
| m30 = src[12], m31 = src[13], m32 = src[14], m33 = src[15]; | |
| var t00 = m00 * m11 - m01 * m10, | |
| t01 = m00 * m12 - m02 * m10, | |
| t02 = m00 * m13 - m03 * m10, | |
| t03 = m01 * m12 - m02 * m11, | |
| t04 = m01 * m13 - m03 * m11, | |
| t05 = m02 * m13 - m03 * m12, | |
| t06 = m20 * m31 - m21 * m30, | |
| t07 = m20 * m32 - m22 * m30, | |
| t08 = m20 * m33 - m23 * m30, | |
| t09 = m21 * m32 - m22 * m31, | |
| t10 = m21 * m33 - m23 * m31, | |
| t11 = m22 * m33 - m23 * m32; | |
| var det = t00 * t11 - t01 * t10 + t02 * t09 + t03 * t08 - t04 * t07 + t05 * t06; | |
| if (det === 0) return; | |
| var invDet = 1 / det; | |
| dst[0] = (m11 * t11 - m12 * t10 + m13 * t09) * invDet; | |
| dst[1] = (-m01 * t11 + m02 * t10 - m03 * t09) * invDet; | |
| dst[2] = (m31 * t05 - m32 * t04 + m33 * t03) * invDet; | |
| dst[3] = (-m21 * t05 + m22 * t04 - m23 * t03) * invDet; | |
| dst[4] = (-m10 * t11 + m12 * t08 - m13 * t07) * invDet; | |
| dst[5] = (m00 * t11 - m02 * t08 + m03 * t07) * invDet; | |
| dst[6] = (-m30 * t05 + m32 * t02 - m33 * t01) * invDet; | |
| dst[7] = (m20 * t05 - m22 * t02 + m23 * t01) * invDet; | |
| dst[8] = (m10 * t10 - m11 * t08 + m13 * t06) * invDet; | |
| dst[9] = (-m00 * t10 + m01 * t08 - m03 * t06) * invDet; | |
| dst[10] = (m30 * t04 - m31 * t02 + m33 * t00) * invDet; | |
| dst[11] = (-m20 * t04 + m21 * t02 - m23 * t00) * invDet; | |
| dst[12] = (-m10 * t09 + m11 * t07 - m12 * t06) * invDet; | |
| dst[13] = (m00 * t09 - m01 * t07 + m02 * t06) * invDet; | |
| dst[14] = (-m30 * t03 + m31 * t01 - m32 * t00) * invDet; | |
| dst[15] = (m20 * t03 - m21 * t01 + m22 * t00) * invDet; | |
| } | |
| function asNormalMatrix(m) { | |
| // inverse transpose of upper-left 3x3 matrix | |
| var out = new Float32Array(9); | |
| var m11 = m[0], m21 = m[1], m31 = m[2], | |
| m12 = m[4], m22 = m[5], m32 = m[6], | |
| m13 = m[8], m23 = m[9], m33 = m[10]; | |
| var t11 = m33 * m22 - m32 * m23, | |
| t12 = m32 * m13 - m33 * m12, | |
| t13 = m23 * m12 - m22 * m13; | |
| var det = m11 * t11 + m21 * t12 + m31 * t13; | |
| if (det !== 0 ) { | |
| const s = 1 / det; | |
| // store in transposed order | |
| out[0] = t11 * s; out[3] = (m31 * m23 - m33 * m21) * s; out[6] = (m32 * m21 - m31 * m22) * s; | |
| out[1] = t12 * s; out[4] = (m33 * m11 - m31 * m13) * s; out[7] = (m31 * m12 - m32 * m11) * s; | |
| out[2] = t13 * s; out[5] = (m21 * m13 - m23 * m11) * s; out[8] = (m22 * m11 - m21 * m12) * s; | |
| } | |
| return out; | |
| } | |
| var GL_Symbols; // reverse mapping for debug printing | |
| function GL_Symbol(constant, rangeStart) { | |
| if (constant === undefined) { debugger; return /* should not happen */} | |
| if (rangeStart !== undefined) { | |
| // rangeStart is e.g. "FALSE" or "POINTS" which are both 0 | |
| var all = Object.keys(GL); // we're relying on insertion order here | |
| var start = all.indexOf(rangeStart); | |
| return all[start + constant - GL[rangeStart]]; | |
| } | |
| else return GL_Symbols[constant] || constant; | |
| } | |
| function initGLConstants() { | |
| GL = { | |
| DEPTH_BUFFER_BIT: 0x00000100, | |
| STENCIL_BUFFER_BIT: 0x00000400, | |
| COLOR_BUFFER_BIT: 0x00004000, | |
| FALSE: 0, | |
| TRUE: 1, | |
| POINTS: 0x0000, | |
| LINES: 0x0001, | |
| LINE_LOOP: 0x0002, | |
| LINE_STRIP: 0x0003, | |
| TRIANGLES: 0x0004, | |
| TRIANGLE_STRIP: 0x0005, | |
| TRIANGLE_FAN: 0x0006, | |
| QUADS: 0x0007, | |
| QUAD_STRIP: 0x0008, | |
| POLYGON: 0x0009, | |
| NEVER: 0x0200, | |
| LESS: 0x0201, | |
| EQUAL: 0x0202, | |
| LEQUAL: 0x0203, | |
| GREATER: 0x0204, | |
| NOTEQUAL: 0x0205, | |
| GEQUAL: 0x0206, | |
| ALWAYS: 0x0207, | |
| SRC_COLOR: 0x0300, | |
| ONE_MINUS_SRC_COLOR: 0x0301, | |
| SRC_ALPHA: 0x0302, | |
| ONE_MINUS_SRC_ALPHA: 0x0303, | |
| DST_ALPHA: 0x0304, | |
| ONE_MINUS_DST_ALPHA: 0x0305, | |
| DST_COLOR: 0x0306, | |
| ONE_MINUS_DST_COLOR: 0x0307, | |
| SRC_ALPHA_SATURATE: 0x0308, | |
| NONE: 0, | |
| FRONT_LEFT: 0x0400, | |
| FRONT_RIGHT: 0x0401, | |
| BACK_LEFT: 0x0402, | |
| BACK_RIGHT: 0x0403, | |
| FRONT: 0x0404, | |
| BACK: 0x0405, | |
| LEFT: 0x0406, | |
| RIGHT: 0x0407, | |
| FRONT_AND_BACK: 0x0408, | |
| NO_ERROR: 0, | |
| INVALID_ENUM: 0x0500, | |
| INVALID_VALUE: 0x0501, | |
| INVALID_OPERATION: 0x0502, | |
| OUT_OF_MEMORY: 0x0505, | |
| EXP: 0x0800, | |
| EXP2: 0x0801, | |
| CW: 0x0900, | |
| CCW: 0x0901, | |
| POINT_SIZE: 0x0B11, | |
| POINT_SIZE_RANGE: 0x0B12, | |
| POINT_SIZE_GRANULARITY: 0x0B13, | |
| LINE_SMOOTH: 0x0B20, | |
| LINE_WIDTH: 0x0B21, | |
| LINE_WIDTH_RANGE: 0x0B22, | |
| LINE_WIDTH_GRANULARITY: 0x0B23, | |
| LIST_INDEX: 0x0B33, | |
| LIGHTING: 0x0B50, | |
| ALPHA_TEST: 0x0BC0, | |
| POLYGON_MODE: 0x0B40, | |
| POLYGON_SMOOTH: 0x0B41, | |
| CULL_FACE: 0x0B44, | |
| CULL_FACE_MODE: 0x0B45, | |
| FRONT_FACE: 0x0B46, | |
| COLOR_MATERIAL: 0x0B57, | |
| FOG: 0x0B60, | |
| FOG_DENSITY: 0x0B62, | |
| FOG_START: 0x0B63, | |
| FOG_END: 0x0B64, | |
| FOG_MODE: 0x0B65, | |
| FOG_COLOR: 0x0B66, | |
| DEPTH_RANGE: 0x0B70, | |
| DEPTH_TEST: 0x0B71, | |
| DEPTH_WRITEMASK: 0x0B72, | |
| DEPTH_CLEAR_VALUE: 0x0B73, | |
| DEPTH_FUNC: 0x0B74, | |
| STENCIL_TEST: 0x0B90, | |
| STENCIL_CLEAR_VALUE: 0x0B91, | |
| STENCIL_FUNC: 0x0B92, | |
| STENCIL_VALUE_MASK: 0x0B93, | |
| STENCIL_FAIL: 0x0B94, | |
| STENCIL_PASS_DEPTH_FAIL: 0x0B95, | |
| STENCIL_PASS_DEPTH_PASS: 0x0B96, | |
| STENCIL_REF: 0x0B97, | |
| STENCIL_WRITEMASK: 0x0B98, | |
| MATRIX_MODE: 0x0BA0, | |
| NORMALIZE: 0x0BA1, | |
| VIEWPORT: 0x0BA2, | |
| MODELVIEW_STACK_DEPTH: 0x0BA3, | |
| PROJECTION_STACK_DEPTH: 0x0BA4, | |
| TEXTURE_STACK_DEPTH: 0x0BA5, | |
| MODELVIEW_MATRIX: 0x0BA6, | |
| PROJECTION_MATRIX: 0x0BA7, | |
| TEXTURE_MATRIX: 0x0BA8, | |
| ATTRIB_STACK_DEPTH: 0x0BB0, | |
| CLIENT_ATTRIB_STACK_DEPTH: 0x0BB1, | |
| ALPHA_TEST: 0x0BC0, | |
| ALPHA_TEST_FUNC: 0x0BC1, | |
| ALPHA_TEST_REF: 0x0BC2, | |
| DITHER: 0x0BD0, | |
| BLEND_DST: 0x0BE0, | |
| BLEND_SRC: 0x0BE1, | |
| BLEND: 0x0BE2, | |
| LOGIC_OP_MODE: 0x0BF0, | |
| DRAW_BUFFER: 0x0C01, | |
| READ_BUFFER: 0x0C02, | |
| SCISSOR_BOX: 0x0C10, | |
| SCISSOR_TEST: 0x0C11, | |
| COLOR_CLEAR_VALUE: 0x0C22, | |
| COLOR_WRITEMASK: 0x0C23, | |
| DOUBLEBUFFER: 0x0C32, | |
| STEREO: 0x0C33, | |
| PERSPECTIVE_CORRECTION_HINT: 0x0C50, | |
| LINE_SMOOTH_HINT: 0x0C52, | |
| POLYGON_SMOOTH_HINT: 0x0C53, | |
| FOG_HINT: 0x0C54, | |
| TEXTURE_GEN_S: 0x0C60, | |
| TEXTURE_GEN_T: 0x0C61, | |
| TEXTURE_GEN_R: 0x0C62, | |
| TEXTURE_GEN_Q: 0x0C63, | |
| PIXEL_MAP_I_TO_I: 0x0C70, | |
| PIXEL_MAP_S_TO_S: 0x0C71, | |
| PIXEL_MAP_I_TO_R: 0x0C72, | |
| PIXEL_MAP_I_TO_G: 0x0C73, | |
| PIXEL_MAP_I_TO_B: 0x0C74, | |
| PIXEL_MAP_I_TO_A: 0x0C75, | |
| PIXEL_MAP_R_TO_R: 0x0C76, | |
| PIXEL_MAP_G_TO_G: 0x0C77, | |
| PIXEL_MAP_B_TO_B: 0x0C78, | |
| PIXEL_MAP_A_TO_A: 0x0C79, | |
| PIXEL_MAP_I_TO_I_SIZE: 0x0CB0, | |
| PIXEL_MAP_S_TO_S_SIZE: 0x0CB1, | |
| PIXEL_MAP_I_TO_R_SIZE: 0x0CB2, | |
| PIXEL_MAP_I_TO_G_SIZE: 0x0CB3, | |
| PIXEL_MAP_I_TO_B_SIZE: 0x0CB4, | |
| PIXEL_MAP_I_TO_A_SIZE: 0x0CB5, | |
| PIXEL_MAP_R_TO_R_SIZE: 0x0CB6, | |
| PIXEL_MAP_G_TO_G_SIZE: 0x0CB7, | |
| PIXEL_MAP_B_TO_B_SIZE: 0x0CB8, | |
| PIXEL_MAP_A_TO_A_SIZE: 0x0CB9, | |
| UNPACK_SWAP_BYTES: 0x0CF0, | |
| UNPACK_LSB_FIRST: 0x0CF1, | |
| UNPACK_ROW_LENGTH: 0x0CF2, | |
| UNPACK_SKIP_ROWS: 0x0CF3, | |
| UNPACK_SKIP_PIXELS: 0x0CF4, | |
| UNPACK_ALIGNMENT: 0x0CF5, | |
| PACK_SWAP_BYTES: 0x0D00, | |
| PACK_LSB_FIRST: 0x0D01, | |
| PACK_ROW_LENGTH: 0x0D02, | |
| PACK_SKIP_ROWS: 0x0D03, | |
| PACK_SKIP_PIXELS: 0x0D04, | |
| PACK_ALIGNMENT: 0x0D05, | |
| MAX_TEXTURE_SIZE: 0x0D33, | |
| MAX_VIEWPORT_DIMS: 0x0D3A, | |
| SUBPIXEL_BITS: 0x0D50, | |
| TEXTURE_1D: 0x0DE0, | |
| TEXTURE_2D: 0x0DE1, | |
| TEXTURE_WIDTH: 0x1000, | |
| TEXTURE_HEIGHT: 0x1001, | |
| TEXTURE_BORDER_COLOR: 0x1004, | |
| DONT_CARE: 0x1100, | |
| FASTEST: 0x1101, | |
| NICEST: 0x1102, | |
| AMBIENT: 0x1200, | |
| DIFFUSE: 0x1201, | |
| SPECULAR: 0x1202, | |
| POSITION: 0x1203, | |
| SPOT_CUTOFF: 0x1206, | |
| COMPILE: 0x1300, | |
| COMPILE_AND_EXECUTE: 0x1301, | |
| BYTE: 0x1400, | |
| UNSIGNED_BYTE: 0x1401, | |
| SHORT: 0x1402, | |
| UNSIGNED_SHORT: 0x1403, | |
| INT: 0x1404, | |
| UNSIGNED_INT: 0x1405, | |
| FLOAT: 0x1406, | |
| STACK_OVERFLOW: 0x0503, | |
| STACK_UNDERFLOW: 0x0504, | |
| CLEAR: 0x1500, | |
| AND: 0x1501, | |
| AND_REVERSE: 0x1502, | |
| COPY: 0x1503, | |
| AND_INVERTED: 0x1504, | |
| NOOP: 0x1505, | |
| XOR: 0x1506, | |
| OR: 0x1507, | |
| NOR: 0x1508, | |
| EQUIV: 0x1509, | |
| INVERT: 0x150A, | |
| OR_REVERSE: 0x150B, | |
| COPY_INVERTED: 0x150C, | |
| OR_INVERTED: 0x150D, | |
| NAND: 0x150E, | |
| SET: 0x150F, | |
| EMISSION: 0x1600, | |
| SHININESS: 0x1601, | |
| AMBIENT_AND_DIFFUSE: 0x1602, | |
| COLOR_INDEXES: 0x1603, | |
| MODELVIEW: 0x1700, | |
| PROJECTION: 0x1701, | |
| TEXTURE: 0x1702, | |
| COLOR: 0x1800, | |
| DEPTH: 0x1801, | |
| STENCIL: 0x1802, | |
| STENCIL_INDEX: 0x1901, | |
| DEPTH_COMPONENT: 0x1902, | |
| RED: 0x1903, | |
| GREEN: 0x1904, | |
| BLUE: 0x1905, | |
| ALPHA: 0x1906, | |
| RGB: 0x1907, | |
| RGBA: 0x1908, | |
| POINT: 0x1B00, | |
| LINE: 0x1B01, | |
| FILL: 0x1B02, | |
| FLAT: 0x1D00, | |
| SMOOTH: 0x1D01, | |
| KEEP: 0x1E00, | |
| REPLACE: 0x1E01, | |
| INCR: 0x1E02, | |
| DECR: 0x1E03, | |
| VENDOR: 0x1F00, | |
| RENDERER: 0x1F01, | |
| VERSION: 0x1F02, | |
| EXTENSIONS: 0x1F03, | |
| S: 0x2000, | |
| T: 0x2001, | |
| R: 0x2002, | |
| Q: 0x2003, | |
| MODULATE: 0x2100, | |
| DECAL: 0x2101, | |
| TEXTURE_ENV_MODE: 0x2200, | |
| TEXTURE_ENV: 0x2300, | |
| EYE_LINEAR: 0x2400, | |
| OBJECT_LINEAR: 0x2401, | |
| SPHERE_MAP: 0x2402, | |
| TEXTURE_GEN_MODE: 0x2500, | |
| OBJECT_PLANE: 0x2501, | |
| EYE_PLANE: 0x2502, | |
| SPHERE_MAP: 0x2402, | |
| NEAREST: 0x2600, | |
| LINEAR: 0x2601, | |
| NEAREST_MIPMAP_NEAREST: 0x2700, | |
| LINEAR_MIPMAP_NEAREST: 0x2701, | |
| NEAREST_MIPMAP_LINEAR: 0x2702, | |
| LINEAR_MIPMAP_LINEAR: 0x2703, | |
| TEXTURE_MAG_FILTER: 0x2800, | |
| TEXTURE_MIN_FILTER: 0x2801, | |
| TEXTURE_WRAP_S: 0x2802, | |
| TEXTURE_WRAP_T: 0x2803, | |
| REPEAT: 0x2901, | |
| CLIP_PLANE0: 0x3000, | |
| CLIP_PLANE1: 0x3001, | |
| CLIP_PLANE2: 0x3002, | |
| CLIP_PLANE3: 0x3003, | |
| CLIP_PLANE4: 0x3004, | |
| CLIP_PLANE5: 0x3005, | |
| CLIP_PLANE6: 0x3006, | |
| CLIP_PLANE7: 0x3007, | |
| LIGHT0: 0x4000, | |
| LIGHT1: 0x4001, | |
| LIGHT2: 0x4002, | |
| LIGHT3: 0x4003, | |
| LIGHT4: 0x4004, | |
| LIGHT5: 0x4005, | |
| LIGHT6: 0x4006, | |
| LIGHT7: 0x4007, | |
| BGRA: 0x80E1, | |
| CLAMP_TO_EDGE: 0x812F, | |
| VERTEX_ARRAY: 0x8074, | |
| NORMAL_ARRAY: 0x8075, | |
| COLOR_ARRAY: 0x8076, | |
| INDEX_ARRAY: 0x8077, | |
| TEXTURE_COORD_ARRAY: 0x8078, | |
| GENERATE_MIPMAP_SGIS: 0x8191, | |
| GENERATE_MIPMAP_HINT_SGIS: 0x8192, | |
| TEXTURE_COMPRESSED: 0x86A1, | |
| CURRENT_BIT: 0x00001, | |
| POINT_BIT: 0x00002, | |
| LINE_BIT: 0x00004, | |
| POLYGON_BIT: 0x00008, | |
| POLYGON_STIPPLE_BIT: 0x00010, | |
| PIXEL_MODE_BIT: 0x00020, | |
| LIGHTING_BIT: 0x00040, | |
| FOG_BIT: 0x00080, | |
| DEPTH_BUFFER_BIT: 0x00100, | |
| ACCUM_BUFFER_BIT: 0x00200, | |
| STENCIL_BUFFER_BIT: 0x00400, | |
| VIEWPORT_BIT: 0x00800, | |
| TRANSFORM_BIT: 0x01000, | |
| ENABLE_BIT: 0x02000, | |
| COLOR_BUFFER_BIT: 0x04000, | |
| HINT_BIT: 0x08000, | |
| EVAL_BIT: 0x10000, | |
| LIST_BIT: 0x20000, | |
| TEXTURE_BIT: 0x40000, | |
| SCISSOR_BIT: 0x80000, | |
| ALL_ATTRIB_BITS: 0xFFFFF, | |
| ZERO: 0, | |
| ONE: 1, | |
| }; | |
| GL_Symbols = {}; | |
| for (var name in GL) { | |
| var value = GL[name]; | |
| GL_Symbols[value] = name; | |
| } | |
| } | |
| function registerOpenGL() { | |
| if (typeof Squeak === "object" && Squeak.registerExternalModule) { | |
| Squeak.registerExternalModule('libGL.so', OpenGL()); | |
| } else self.setTimeout(registerOpenGL, 100); | |
| }; | |
| registerOpenGL(); | |