diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-01-28 13:48:56 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-01-28 13:48:56 -0800 |
commit | b6ebeed2cfcebb220f2ce700c7cbe484e62cd2e2 (patch) | |
tree | fb2679ed6fc2447a50531f9133b1b04cbd6f67b5 | |
parent | c351fb6a11adbc20446577685a849eab82a2edbf (diff) | |
parent | 21e2f7768cc99f106ed0fca3732bcce794a425c9 (diff) |
Merge pull request #191 from ehsan/glgears
Initial implementation of OpenGL ES and GLUT
-rwxr-xr-x | emcc | 15 | ||||
-rw-r--r-- | src/library.js | 28 | ||||
-rw-r--r-- | src/library_gl.js | 481 | ||||
-rw-r--r-- | src/library_sdl.js | 15 | ||||
-rw-r--r-- | src/preamble.js | 39 | ||||
-rw-r--r-- | src/shell.html | 5 | ||||
-rw-r--r-- | system/include/GL/freeglut_std.h | 628 | ||||
-rw-r--r-- | system/include/GL/glut.h | 21 | ||||
-rw-r--r-- | system/include/GLES2/gl2.h | 621 | ||||
-rw-r--r-- | system/include/GLES2/gl2platform.h | 30 | ||||
-rw-r--r-- | system/include/KHR/khrplatform.h | 277 | ||||
-rw-r--r-- | tests/hello_world_gles.c | 729 | ||||
-rw-r--r-- | tests/hello_world_gles_shell.html | 55 | ||||
-rwxr-xr-x | tests/runner.py | 49 |
14 files changed, 2958 insertions, 35 deletions
@@ -164,6 +164,13 @@ Options that are modified or new in %s include: will be run). Note that this by itself will not minify the code (closure does that) + --shell-path <path> The path name to a skeleton HTML file used + when generating HTML output. The shell file + used needs to have this token inside it: + {{{ SCRIPT_CODE }}} + Note that this argument is ignored if a + target other than HTML is specified using + the -o option. The target file, if specified (-o <target>), defines what will be generated: @@ -282,6 +289,7 @@ try: closure = None js_transform = None compress_whitespace = None + shell_path = shared.path_from_root('src', 'shell.html') def check_bad_eq(arg): assert '=' not in arg, 'Invalid parameter (do not use "=" with "--" options)' @@ -321,6 +329,11 @@ try: f.close() newargs[i] = '' newargs[i+1] = '' + elif newargs[i].startswith('--shell-file'): + check_bad_eq(newargs[i]) + shell_path = newargs[i+1] + newargs[i] = '' + newargs[i+1] = '' newargs = [ arg for arg in newargs if arg is not '' ] if llvm_opt_level is None: llvm_opt_level = 1 if opt_level >= 1 else 0 @@ -626,7 +639,7 @@ try: # If we were asked to also generate HTML, do that if final_suffix == 'html': if DEBUG: print >> sys.stderr, 'emcc: generating HTML' - shell = open(shared.path_from_root('src', 'shell.html')).read() + shell = open(shell_path).read() html = open(target, 'w') html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(final).read())) html.close() diff --git a/src/library.js b/src/library.js index 355a0c61..45c64bc7 100644 --- a/src/library.js +++ b/src/library.js @@ -4682,6 +4682,34 @@ LibraryManager.library = { }, nanf: 'nan', + sincos: function(x, sine, cosine) { + var sineVal = Math.sin(x), + cosineVal = Math.cos(x); + {{{ makeSetValue('sine', '0', 'sineVal', 'double') }}}; + {{{ makeSetValue('cosine', '0', 'cosineVal', 'double') }}}; + }, + + sincosf: function(x, sine, cosine) { + var sineVal = Math.sin(x), + cosineVal = Math.cos(x); + {{{ makeSetValue('sine', '0', 'sineVal', 'float') }}}; + {{{ makeSetValue('cosine', '0', 'cosineVal', 'float') }}}; + }, + + __div_t_struct_layout: Runtime.generateStructInfo([ + ['i32', 'quot'], + ['i32', 'rem'], + ]), + div__deps: ['__div_t_struct_layout'], + div: function(divt, numer, denom) { + var quot = Math.floor(numer / denom); + var rem = numer - quot * denom; + var offset = ___div_t_struct_layout.rem; + {{{ makeSetValue('divt', '0', 'quot', 'i32') }}}; + {{{ makeSetValue('divt', 'offset', 'rem', 'i32') }}}; + return divt; + }, + __fpclassifyf: function(x) { if (isNaN(x)) return {{{ cDefine('FP_NAN') }}}; if (!isFinite(x)) return {{{ cDefine('FP_INFINITE') }}}; diff --git a/src/library_gl.js b/src/library_gl.js index 2be881df..e1a7c73b 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -4,8 +4,35 @@ var LibraryGL = { $GL: { - textures: {}, - textureCounter: 0, + hashtable: function(name) { + if (!this._hashtables) { + this._hashtables = {}; + } + if (!(name in this._hashtables)) { + this._hashtables[name] = { + table: {}, + counter: 0, + add: function(obj) { + var id = this.counter++; + this.table[id] = obj; + return id; + }, + get: function(id) { +#if ASSERTIONS + assert(id < this.counter, "Invalid id " + id + " for the hashtable " + name); +#endif + return this.table[id]; + }, + remove: function(id) { +#if ASSERTIONS + assert(id < this.counter, "Invalid id " + id + " for the hashtable " + name); +#endif + delete this.table[id]; + } + }; + } + return this._hashtables[name]; + }, }, glGetString: function(name_) { @@ -24,7 +51,7 @@ var LibraryGL = { glGetIntegerv: function(name_, p) { switch(name_) { case Module.ctx.MAX_TEXTURE_SIZE: - IHEAP[p] = Module.ctx.getParameter(name_); + {{{ makeSetValue('p', '0', 'Module.ctx.getParameter(name_)', 'i32') }}}; break; default: throw 'Failure: Invalid glGetIntegerv value: ' + name_; @@ -34,78 +61,476 @@ var LibraryGL = { glGenTextures__deps: ['$GL'], glGenTextures: function(n, textures) { for (var i = 0; i < n; i++) { - var id = GL.textureCounter++; - GL.textures[id] = Module.ctx.createTexture(); - IHEAP[textures+QUANTUM_SIZE*i] = id; + var id = GL.hashtable("texture").add(Module.ctx.createTexture()); + {{{ makeSetValue('textures', 'i', 'id', 'i32') }}}; } }, glDeleteTextures: function(n, textures) { for (var i = 0; i < n; i++) { - var id = IHEAP[textures+QUANTUM_SIZE*i]; - Module.ctx.deleteTexture(GL.textures[id]); - delete GL.textures[id]; + var id = {{{ makeGetValue('textures', 'i', 'i32') }}}; + Module.ctx.deleteTexture(GL.hashtable("texture").get(id)); + GL.hashtable("texture").remove(id); } }, glTexImage2D: function(target, level, internalformat, width, height, border, format, type, pixels) { if (pixels) { - pixels = new Uint8Array(IHEAP.slice(pixels, pixels + width*height*4)); // TODO: optimize + pixels = new Uint8Array(Array_copy(pixels, pixels + width*height*4)); // TODO: optimize } Module.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, pixels); }, glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { if (pixels) { - pixels = new Uint8Array(IHEAP.slice(pixels, pixels + width*height*4)); // TODO: optimize + pixels = new Uint8Array(Array_copy(pixels, pixels + width*height*4)); // TODO: optimize } Module.ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); }, glBindTexture: function(target, texture) { - Module.ctx.bindTexture(target, GL.textures[texture]); + Module.ctx.bindTexture(target, GL.hashtable("texture").get(texture)); }, - glClearColor: function(red, green, blue, alpha) { - Module.ctx.clearColor(red, green, blue, alpha); + glGenBuffers__deps: ['$GL'], + glGenBuffers: function(n, buffers) { + for (var i = 0; i < n; i++) { + var id = GL.hashtable("buffer").add(Module.ctx.createBuffer()); + {{{ makeSetValue('buffers', 'i', 'id', 'i32') }}}; + } + }, + + glDeleteBuffers: function(n, buffers) { + for (var i = 0; i < n; i++) { + var id = {{{ makeGetValue('buffers', 'i', 'i32') }}}; + Module.ctx.deleteBuffer(GL.hashtable("buffer").get(id)); + GL.hashtable("buffer").remove(id); + } + }, + + glBufferData: function(target, size, data, usage) { + var floatArray = new Float32Array(TypedArray_copy(data, size)); + Module.ctx.bufferData(target, floatArray, usage); + }, + + glBindAttribLocation_deps: ['$GL'], + glGetUniformLocation: function(program, name) { + name = Pointer_stringify(name); + return GL.hashtable("uniform").add( + Module.ctx.getUniformLocation(GL.hashtable("program").get(program), name)); + }, + + glUniform1f: function(Location, v0) { + Location = GL.hashtable("uniform").get(Location); + Module.ctx.uniform1f(Location, v0); + }, + + glUniform2f: function(Location, v0, v1) { + Location = GL.hashtable("uniform").get(Location); + Module.ctx.uniform2f(Location, v0, v1); + }, + + glUniform3f: function(Location, v0, v1, v2) { + Location = GL.hashtable("uniform").get(Location); + Module.ctx.uniform3f(Location, v0, v1, v2); }, - glClear: function(mask) { - Module.ctx.clear(mask); + glUniform4f: function(Location, v0, v1, v2, v3) { + Location = GL.hashtable("uniform").get(Location); + Module.ctx.uniform4f(Location, v0, v1, v2, v3); }, - glEnable: function(cap) { - Module.ctx.enable(cap); + glUniform1i: function(Location, v0) { + Location = GL.hashtable("uniform").get(Location); + Module.ctx.uniform1i(Location, v0); }, - glScissor: function(x, y, width, height) { - Module.ctx.scissor(x, y, width, height); + glUniform2i: function(Location, v0, v1) { + Location = GL.hashtable("uniform").get(Location); + Module.ctx.uniform2i(Location, v0, v1); + }, + + glUniform3i: function(Location, v0, v1, v2) { + Location = GL.hashtable("uniform").get(Location); + Module.ctx.uniform3i(Location, v0, v1, v2); + }, + + glUniform4i: function(Location, v0, v1, v2, v3) { + Location = GL.hashtable("uniform").get(Location); + Module.ctx.uniform4i(Location, v0, v1, v2, v3); + }, + + glUniform1fv: function(Location, count, value) { + Location = GL.hashtable("uniform").get(Location); + value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniform1fv(Location, value); + }, + + glUniform2fv: function(Location, count, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 2; + value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniform2fv(Location, value); + }, + + glUniform3fv: function(Location, count, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 3; + value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniform3fv(Location, value); + }, + + glUniform4fv: function(Location, count, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 4; + value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniform4fv(Location, value); + }, + + glUniform1fi: function(Location, count, value) { + Location = GL.hashtable("uniform").get(Location); + value = new Uint32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniform1fi(Location, value); + }, + + glUniform2fi: function(Location, count, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 2; + value = new Uint32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniform2fi(Location, value); + }, + + glUniform3fi: function(Location, count, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 3; + value = new Uint32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniform3fi(Location, value); + }, + + glUniform4fi: function(Location, count, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 4; + value = new Uint32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniform4fi(Location, value); + }, + + glUniformMatrix2fv: function(Location, count, transpose, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 4; + value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniformMatrix2fv(Location, transpose, value); + }, + + glUniformMatrix3fv: function(Location, count, transpose, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 9; + value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniformMatrix3fv(Location, transpose, value); + }, + + glUniformMatrix4fv: function(Location, count, transpose, value) { + Location = GL.hashtable("uniform").get(Location); + count *= 16; + value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize + Module.ctx.uniformMatrix4fv(Location, transpose, value); + }, + + glBindBuffer: function(target, buffer) { + Module.ctx.bindBuffer(target, GL.hashtable("buffer").get(buffer)); + }, + + glVertexAttrib1f: function(index, v0) { + Module.ctx.vertexAttrib1f(index, v0); + }, + + glVertexAttrib2f: function(index, v0, v1) { + Module.ctx.vertexAttrib2f(index, v0, v1); + }, + + glVertexAttrib3f: function(index, v0, v1, v2) { + Module.ctx.vertexAttrib3f(index, v0, v1, v2); + }, + + glVertexAttrib4f: function(index, v0, v1, v2, v3) { + Module.ctx.vertexAttrib4f(index, v0, v1, v2, v3); + }, + + glVertexAttrib1fv: function(index, v) { + v = new Float32Array(TypedArray_copy(v, 1*4)); // TODO: optimize + Module.ctx.vertexAttrib1fv(index, v); + }, + + glVertexAttrib2fv: function(index, v) { + v = new Float32Array(TypedArray_copy(v, 2*4)); // TODO: optimize + Module.ctx.vertexAttrib2fv(index, v); + }, + + glVertexAttrib3fv: function(index, v) { + v = new Float32Array(TypedArray_copy(v, 3*4)); // TODO: optimize + Module.ctx.vertexAttrib3fv(index, v); + }, + + glVertexAttrib4fv: function(index, v) { + v = new Float32Array(TypedArray_copy(v, 4*4)); // TODO: optimize + Module.ctx.vertexAttrib4fv(index, v); + }, + + glVertexAttribPointer: function(index, size, type, normalized, stride, pointer) { + Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, pointer); + }, + + glEnableVertexAttribArray: function(index) { + Module.ctx.enableVertexAttribArray(index); + }, + + glDisableVertexAttribArray: function(index) { + Module.ctx.disableVertexAttribArray(index); + }, + + glDrawArrays: function(mode, first, count) { + Module.ctx.drawArrays(mode, first, count); + }, + + glGetAttribLocation: function(program, name) { + program = GL.hashtable("program").get(program); + name = Pointer_stringify(name); + Module.ctx.getAttribLocation(program, name); + }, + + glCreateShader_deps: ['$GL'], + glCreateShader: function(shaderType) { + var shader = Module.ctx.createShader(shaderType); + return GL.hashtable("shader").add(shader); + }, + + glShaderSource_deps: ['$GL'], + glShaderSource: function(shader, count, string, length) { + var source = ""; + for (var i = 0; i < count; ++i) { + var frag = string[i]; + if (length) { + var len = {{{ makeGetValue('length', 'i', 'i32') }}}; + if (len < 0) { + frag = Pointer_stringify({{{ makeGetValue('string', 'i', 'i32') }}}); + } else { + frag = Pointer_stringify({{{ makeGetValue('string', 'i', 'i32') }}}, len); + } + } else { + frag = Pointer_stringify({{{ makeGetValue('string', 'i', 'i32') }}}); + } + if (source.length) { + source += "\n"; + } + source += frag; + } + Module.ctx.shaderSource(GL.hashtable("shader").get(shader), source); + }, + + glCompileShader_deps: ['$GL'], + glCompileShader: function(shader) { + Module.ctx.compileShader(GL.hashtable("shader").get(shader)); + }, + + glGetShaderInfoLog_deps: ['$GL'], + glGetShaderInfoLog: function(shader, maxLength, length, infoLog) { + var log = Module.ctx.getShaderInfoLog(GL.hashtable("shader").get(shader)); + log.slice(0, maxLength - 1); + writeStringToMemory(log, infoLog); + if (length) { + {{{ makeSetValue('length', 'i', 'log.length', 'i32') }}} + } + }, + + glCreateProgram_deps: ['$GL'], + glCreateProgram: function() { + return GL.hashtable("program").add(Module.ctx.createProgram()); + }, + + glAttachShader_deps: ['$GL'], + glAttachShader: function(program, shader) { + Module.ctx.attachShader(GL.hashtable("program").get(program), + GL.hashtable("shader").get(shader)); + }, + + glLinkProgram_deps: ['$GL'], + glLinkProgram: function(program) { + Module.ctx.linkProgram(GL.hashtable("program").get(program)); + }, + + glGetProgramInfoLog_deps: ['$GL'], + glGetProgramInfoLog: function(program, maxLength, length, infoLog) { + var log = Module.ctx.getProgramInfoLog(GL.hashtable("program").get(program)); + // Work around a bug in Chromium which causes getProgramInfoLog to return null + if (!log) { + log = ""; + } + log = log.substr(0, maxLength - 1); + writeStringToMemory(log, infoLog); + if (length) { + {{{ makeSetValue('length', 'i', 'log.length', 'i32') }}} + } + }, + + glUseProgram_deps: ['$Gl'], + glUseProgram: function(program) { + Module.ctx.useProgram(GL.hashtable("program").get(program)); + }, + + glBindAttribLocation_deps: ['$GL'], + glBindAttribLocation: function(program, index, name) { + name = Pointer_stringify(name); + Module.ctx.bindAttribLocation(GL.hashtable("program").get(program), index, name); }, }; -// Ignored stubs for fixed-function pipeline. We will need to emulate this -'begin end matrixMode loadIdentity ortho color3f texCoord2f vertex2f blendFunc pushMatrix popMatrix translatef scalef color4ub enableClientState disableClientState vertexPointer colorPointer normalPointer texCoordPointer drawArrays clientActiveTexture_'.split(' ').forEach(function(name_) { - var cName = 'gl' + name_[0].toUpperCase() + name_.substr(1); - LibraryGL[cName] = function(){}; -}); // Simple pass-through functions -[[0, 'shadeModel fogi fogfv'], - [1, 'clearDepth depthFunc enable disable frontFace cullFace'], +[[0, 'shadeModel fogi fogfv getError'], + [1, 'clearDepth depthFunc enable disable frontFace cullFace clear'], [2, 'pixelStorei'], [3, 'texParameteri texParameterf'], - [4, 'viewport clearColor']].forEach(function(data) { + [4, 'viewport clearColor scissor']].forEach(function(data) { var num = data[0]; var names = data[1]; var args = range(num).map(function(i) { return 'x' + i }).join(', '); var stub = '(function(' + args + ') { ' + (num > 0 ? 'Module.ctx.NAME(' + args + ')' : '') + ' })'; names.split(' ').forEach(function(name_) { var cName = 'gl' + name_[0].toUpperCase() + name_.substr(1); +#if ASSERTIONS + assert(!(cName in LibraryGL), "Cannot reimplement the existing function " + cName); +#endif LibraryGL[cName] = eval(stub.replace('NAME', name_)); //print(cName + ': ' + LibraryGL[cName]); }); }); +var LibraryGLUT = { + $GLUT: { + initTime: null, + idleFunc: null, + keyboardFunc: null, + reshapeFunc: null, + lastX: 0, + lastY: 0, + + onMousemove: function(event) { + GLUT.lastX = event['clientX']; + GLUT.lastY = event['clientY']; + }, + + onKeydown: function(event) { + if (GLUT.keyboardFunc) { + var key = null; + switch (event['keyCode']) { + case 0x70 /*DOM_VK_F1*/: key = 1 /* GLUT_KEY_F1 */; break; + case 0x71 /*DOM_VK_F2*/: key = 2 /* GLUT_KEY_F2 */; break; + case 0x72 /*DOM_VK_F3*/: key = 3 /* GLUT_KEY_F3 */; break; + case 0x73 /*DOM_VK_F4*/: key = 4 /* GLUT_KEY_F4 */; break; + case 0x74 /*DOM_VK_F5*/: key = 5 /* GLUT_KEY_F5 */; break; + case 0x75 /*DOM_VK_F6*/: key = 6 /* GLUT_KEY_F6 */; break; + case 0x76 /*DOM_VK_F7*/: key = 7 /* GLUT_KEY_F7 */; break; + case 0x77 /*DOM_VK_F8*/: key = 8 /* GLUT_KEY_F8 */; break; + case 0x78 /*DOM_VK_F9*/: key = 9 /* GLUT_KEY_F9 */; break; + case 0x79 /*DOM_VK_F10*/: key = 10 /* GLUT_KEY_F10 */; break; + case 0x7a /*DOM_VK_F11*/: key = 11 /* GLUT_KEY_F11 */; break; + case 0x7b /*DOM_VK_F12*/: key = 12 /* GLUT_KEY_F12 */; break; + case 0x25 /*DOM_VK_LEFT*/: key = 100 /* GLUT_KEY_LEFT */; break; + case 0x26 /*DOM_VK_UP*/: key = 101 /* GLUT_KEY_UP */; break; + case 0x27 /*DOM_VK_RIGHT*/: key = 102 /* GLUT_KEY_RIGHT */; break; + case 0x28 /*DOM_VK_DOWN*/: key = 103 /* GLUT_KEY_DOWN */; break; + case 0x21 /*DOM_VK_PAGE_UP*/: key = 104 /* GLUT_KEY_PAGE_UP */; break; + case 0x22 /*DOM_VK_PAGE_DOWN*/: key = 105 /* GLUT_KEY_PAGE_DOWN */; break; + case 0x24 /*DOM_VK_HOME*/: key = 106 /* GLUT_KEY_HOME */; break; + case 0x23 /*DOM_VK_END*/: key = 107 /* GLUT_KEY_END */; break; + case 0x2d /*DOM_VK_INSERT*/: key = 108 /* GLUT_KEY_INSERT */; break; + default: return; + }; + if (key !== null) { + FUNCTION_TABLE[GLUT.keyboardFunc](key, GLUT.lastX, GLUT.lastY); + } + } + }, + }, + + glutInit__deps: ['$GLUT'], + glutInit: function(argcp, argv) { + // Ignore arguments + GLUT.initTime = Date.now(); + window.addEventListener("keydown", GLUT.onKeydown, true); + window.addEventListener("mousemove", GLUT.onMousemove, true); + }, + + glutInitWindowSize: function(width, height) { + Module['canvas'].width = width; + Module['canvas'].height = height; + }, + + glutGet: function(type) { + switch (type) { + case 700: /* GLUT_ELAPSED_TIME */ + var now = Date.now(); + return now - GLUT.initTime; + default: + throw "glutGet(" + type + ") not implemented yet"; + } + }, + + glutDisplayFunc: function(func) { + var RAF = window['setTimeout']; + if (window['requestAnimationFrame']) { + RAF = window['requestAnimationFrame']; + } else if (window['mozRequestAnimationFrame']) { + RAF = window['mozRequestAnimationFrame']; + } else if (window['webkitRequestAnimationFrame']) { + RAF = window['webkitRequestAnimationFrame']; + } else if (window['msRequestAnimationFrame']) { + RAF = window['msRequestAnimationFrame']; + } + RAF.apply(window, [function() { + if (GLUT.reshapeFunc) { + FUNCTION_TABLE[GLUT.reshapeFunc](Module['canvas'].width, + Module['canvas'].height); + } + if (GLUT.idleFunc) { + FUNCTION_TABLE[GLUT.idleFunc](); + } + FUNCTION_TABLE[func](); + _glutDisplayFunc(func); + }]); + }, + + glutIdleFunc: function(func) { + GLUT.idleFunc = func; + }, + + glutSpecialFunc: function(func) { + GLUT.keyboardFunc = func; + }, + + glutReshapeFunc: function(func) { + GLUT.reshapeFunc = func; + }, + + glutCreateWindow: function(name) { + try { + var ctx = Module.canvas.getContext('experimental-webgl'); + if (!ctx) throw 'Could not create canvas :('; + Module.ctx = ctx; + // Set the background of the canvas to black, because glut gives us a + // window which has a black background by default. + Module.canvas.style.backgroundColor = "black"; + } catch (e) { + Module.print('(canvas not available)'); + } + }, + + glutInitDisplayMode: function(mode) {}, + glutMainLoop: function() {}, + glutSwapBuffers: function() {}, + glutPostRedisplay: function() {}, +}; + mergeInto(LibraryManager.library, LibraryGL); +mergeInto(LibraryManager.library, LibraryGLUT); diff --git a/src/library_sdl.js b/src/library_sdl.js index ce26a106..d438fc23 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -176,6 +176,11 @@ mergeInto(LibraryManager.library, { try { var ctx = Module.canvas.getContext(useWebGL ? 'experimental-webgl' : '2d'); if (!ctx) throw 'Could not create canvas :('; + if (useWebGL) { + // Set the background of the WebGL canvas to black, because SDL gives us a + // window which has a black background by default. + Module.canvas.style.backgroundColor = "black"; + } return Module.ctx = ctx; } catch (e) { Module.print('(canvas not available)'); @@ -555,5 +560,15 @@ mergeInto(LibraryManager.library, { // SDL Mixer Mix_OpenAudio: function() { return -1 }, + + SDL_AddTimer: function(interval, callback, param) { + return window.setTimeout(function() { + FUNCTION_TABLE[callback](interval, param); + }, interval); + }, + SDL_RemoveTimer: function(id) { + window.clearTimeout(id); + return true; + }, }); diff --git a/src/preamble.js b/src/preamble.js index b9da766f..9370fa54 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -496,16 +496,18 @@ function allocate(slab, types, allocator) { } Module['allocate'] = allocate; -function Pointer_stringify(ptr) { +function Pointer_stringify(ptr, /* optional */ length) { + var nullTerminated = typeof(length) == "undefined"; var ret = ""; var i = 0; var t; var nullByte = String.fromCharCode(0); while (1) { t = String.fromCharCode({{{ makeGetValue('ptr', 'i', 'i8', 0, 1) }}}); - if (t == nullByte) { break; } else {} + if (nullTerminated && t == nullByte) { break; } else {} ret += t; i += 1; + if (!nullTerminated && i == length) { break; } } return ret; } @@ -690,6 +692,20 @@ function Array_copy(ptr, num) { } Module['Array_copy'] = Array_copy; +#if USE_TYPED_ARRAYS +// Copies a list of num items on the HEAP into a +// JavaScript typed array. +function TypedArray_copy(ptr, num) { + // TODO: optimize this! + var arr = new Uint8Array(num); + for (var i = 0; i < num; ++i) { + arr[i] = {{{ makeGetValue('ptr', 'i', 'i8') }}}; + } + return arr.buffer; +} +Module['TypedArray_copy'] = TypedArray_copy; +#endif + function String_len(ptr) { var i = 0; while ({{{ makeGetValue('ptr', 'i', 'i8') }}}) i++; // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds @@ -750,6 +766,25 @@ function intArrayToString(array) { } Module['intArrayToString'] = intArrayToString; +// Write a Javascript array to somewhere in the heap +function writeStringToMemory(string, buffer, dontAddNull) { + var i = 0; + while (i < string.length) { + var chr = string.charCodeAt(i); + if (chr > 0xFF) { +#if ASSERTIONS + assert(false, 'Character code ' + chr + ' (' + string[i] + ') at offset ' + i + ' not in 0x00-0xFF.'); +#endif + chr &= 0xFF; + } + {{{ makeSetValue('buffer', 'i', 'chr', 'i8') }}} + i = i + 1; + } + if (!dontAddNull) { + {{{ makeSetValue('buffer', 'i', '0', 'i8') }}} + } +} + var STRING_TABLE = []; {{{ unSign }}} diff --git a/src/shell.html b/src/shell.html index 6b6bbdad..69217b18 100644 --- a/src/shell.html +++ b/src/shell.html @@ -9,6 +9,11 @@ <div id='output'></div> <hr> <script type='text/javascript'> + /** + * TODO: Encapsulate this part in a reusable token such as + * EMSCRIPTEN_ENVIRONMENT so that we can share code + * between the default shell and custom ones. + */ // connect to canvas var Module = { print: (function() { diff --git a/system/include/GL/freeglut_std.h b/system/include/GL/freeglut_std.h new file mode 100644 index 00000000..ba1b7165 --- /dev/null +++ b/system/include/GL/freeglut_std.h @@ -0,0 +1,628 @@ +#ifndef __FREEGLUT_STD_H__ +#define __FREEGLUT_STD_H__ + +/* + * freeglut_std.h + * + * The GLUT-compatible part of the freeglut library include file + * + * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. + * Written by Pawel W. Olszta, <olszta@sourceforge.net> + * Creation date: Thu Dec 2 1999 + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus + extern "C" { +#endif + +/* + * Under windows, we have to differentiate between static and dynamic libraries + */ +#ifdef _WIN32 +/* #pragma may not be supported by some compilers. + * Discussion by FreeGLUT developers suggests that + * Visual C++ specific code involving pragmas may + * need to move to a separate header. 24th Dec 2003 + */ + +/* Define FREEGLUT_LIB_PRAGMAS to 1 to include library + * pragmas or to 0 to exclude library pragmas. + * The default behavior depends on the compiler/platform. + */ +# ifndef FREEGLUT_LIB_PRAGMAS +# if ( defined(_MSC_VER) || defined(__WATCOMC__) ) && !defined(_WIN32_WCE) +# define FREEGLUT_LIB_PRAGMAS 1 +# else +# define FREEGLUT_LIB_PRAGMAS 0 +# endif +# endif + +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include <windows.h> + +/* Windows static library */ +# ifdef FREEGLUT_STATIC + +# define FGAPI +# define FGAPIENTRY + + /* Link with Win32 static freeglut lib */ +# if FREEGLUT_LIB_PRAGMAS +# pragma comment (lib, "freeglut_static.lib") +# endif + +/* Windows shared library (DLL) */ +# else + +# define FGAPIENTRY __stdcall +# if defined(FREEGLUT_EXPORTS) +# define FGAPI __declspec(dllexport) +# else +# define FGAPI __declspec(dllimport) + + /* Link with Win32 shared freeglut lib */ +# if FREEGLUT_LIB_PRAGMAS +# pragma comment (lib, "freeglut.lib") +# endif + +# endif + +# endif + +/* Drag in other Windows libraries as required by FreeGLUT */ +# if FREEGLUT_LIB_PRAGMAS +# pragma comment (lib, "glu32.lib") /* link OpenGL Utility lib */ +# pragma comment (lib, "opengl32.lib") /* link Microsoft OpenGL lib */ +# pragma comment (lib, "gdi32.lib") /* link Windows GDI lib */ +# pragma comment (lib, "winmm.lib") /* link Windows MultiMedia lib */ +# pragma comment (lib, "user32.lib") /* link Windows user lib */ +# endif + +#else + +/* Non-Windows definition of FGAPI and FGAPIENTRY */ +# define FGAPI +# define FGAPIENTRY + +#endif + +/* + * The freeglut and GLUT API versions + */ +#define FREEGLUT 1 +#define GLUT_API_VERSION 4 +#define FREEGLUT_VERSION_2_0 1 +#define GLUT_XLIB_IMPLEMENTATION 13 + +/* + * Always include OpenGL and GLU headers + */ +#include <GL/gl.h> +#include <GL/glu.h> + +/* + * GLUT API macro definitions -- the special key codes: + */ +#define GLUT_KEY_F1 0x0001 +#define GLUT_KEY_F2 0x0002 +#define GLUT_KEY_F3 0x0003 +#define GLUT_KEY_F4 0x0004 +#define GLUT_KEY_F5 0x0005 +#define GLUT_KEY_F6 0x0006 +#define GLUT_KEY_F7 0x0007 +#define GLUT_KEY_F8 0x0008 +#define GLUT_KEY_F9 0x0009 +#define GLUT_KEY_F10 0x000A +#define GLUT_KEY_F11 0x000B +#define GLUT_KEY_F12 0x000C +#define GLUT_KEY_LEFT 0x0064 +#define GLUT_KEY_UP 0x0065 +#define GLUT_KEY_RIGHT 0x0066 +#define GLUT_KEY_DOWN 0x0067 +#define GLUT_KEY_PAGE_UP 0x0068 +#define GLUT_KEY_PAGE_DOWN 0x0069 +#define GLUT_KEY_HOME 0x006A +#define GLUT_KEY_END 0x006B +#define GLUT_KEY_INSERT 0x006C + +/* + * GLUT API macro definitions -- mouse state definitions + */ +#define GLUT_LEFT_BUTTON 0x0000 +#define GLUT_MIDDLE_BUTTON 0x0001 +#define GLUT_RIGHT_BUTTON 0x0002 +#define GLUT_DOWN 0x0000 +#define GLUT_UP 0x0001 +#define GLUT_LEFT 0x0000 +#define GLUT_ENTERE |