diff options
-rw-r--r-- | src/library_gl.js | 246 | ||||
-rw-r--r-- | src/library_glut.js | 4 | ||||
-rwxr-xr-x | tests/runner.py | 6 | ||||
-rw-r--r-- | tests/screenshot-gray.png | bin | 0 -> 170477 bytes | |||
-rw-r--r-- | tests/sdl_ogl.c | 6 |
5 files changed, 241 insertions, 21 deletions
diff --git a/src/library_gl.js b/src/library_gl.js index b14b345f..586921d4 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -6,6 +6,8 @@ var LibraryGL = { $GL: { counter: 1, + packAlignment: 4, // default alignment is 4 bytes + unpackAlignment: 4, // default alignment is 4 bytes buffers: {}, programs: {}, framebuffers: {}, @@ -14,7 +16,22 @@ var LibraryGL = { uniforms: {}, shaders: {}, - matrix: {}, + // The folowing data structures are used for OpenGL Immediate Mode matrix routines. + matrix: { + 'm': null, // modelview + 'p': null // projection + }, + matrixStack: { + 'm': [], // modelview + 'p': [] // projection + }, + currentMatrix: 'm', // default is modelview + tempMatrix: null, + initMatrixLibrary: function() { + GL.matrix['m'] = GL.matrix.lib.mat4.create(); + GL.matrix['p'] = GL.matrix.lib.mat4.create(); + GL.tempMatrix = GL.matrix.lib.mat4.create(); + }, // Linear lookup in one of the tables (buffers, programs, etc.). TODO: consider using a weakmap to make this faster, if it matters scan: function(table, object) { @@ -51,7 +68,26 @@ var LibraryGL = { return true; } while (true); return false; + }, + + computeImageSize: function(width, height, sizePerPixel, alignment) { + function roundedToNextMultipleOf(x, y) { + return Math.floor((x + y - 1) / y) * y + } + var plainRowSize = width * sizePerPixel; + var alignedRowSize = roundedToNextMultipleOf(plainRowSize, alignment); + return (height <= 0) ? 0 : + ((height - 1) * alignedRowSize + plainRowSize); + } + }, + + glPixelStorei: function(pname, param) { + if (pname == 0x0D05 /* GL_PACK_ALIGNMENT */) { + GL.packAlignment = param; + } else if (pname == 0x0cf5 /* GL_UNPACK_ALIGNMENT */) { + GL.unpackAlignment = param; } + Module.ctx.pixelStorei(pname, param); }, glGetString: function(name_) { @@ -245,17 +281,17 @@ var LibraryGL = { default: throw 'Invalid format (' + format + ') passed to glTexImage2D'; } - pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+width*height*sizePerPixel') }}}; break; case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */: case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */: case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */: sizePerPixel = 2; - pixels = {{{ makeHEAPView('U16', 'pixels', 'pixels+width*height*sizePerPixel') }}}; break; default: throw 'Invalid type (' + type + ') passed to glTexImage2D'; } + var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); + pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; } else { pixels = null; } @@ -284,17 +320,17 @@ var LibraryGL = { default: throw 'Invalid format (' + format + ') passed to glTexSubImage2D'; } - pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+width*height*sizePerPixel') }}}; break; case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */: case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */: case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */: sizePerPixel = 2; - pixels = {{{ makeHEAPView('U16', 'pixels', 'pixels+width*height*sizePerPixel') }}}; break; default: throw 'Invalid type (' + type + ') passed to glTexSubImage2D'; } + var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); + pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; } else { pixels = null; } @@ -814,6 +850,177 @@ var LibraryGL = { return Module.ctx.isFramebuffer(fb); }, + // OpenGL Immediate Mode matrix routines. + // Note that in the future we might make these available only in certain modes. + glMatrixMode__deps: ['$GL', '$GLImmediate', function() { return 'GL.matrix.lib = ' + read('gl-matrix.js') + + ';\nGL.immediate = GLImmediate;\nGL.initMatrixLibrary();\n' }], + glMatrixMode: function(mode) { + if (mode == 0x1700 /* GL_MODELVIEW */) { + GL.currentMatrix = 'm'; + } else if (mode == 0x1701 /* GL_PROJECTION */) { + GL.currentMatrix = 'p'; + } else { + throw "Wrong mode " + mode + " passed to glMatrixMode"; + } + }, + + glPushMatrix: function() { + GL.matrixStack[GL.currentMatrix].push( + Array.prototype.slice.call(GL.matrix[GL.currentMatrix])); + }, + + glPopMatrix: function() { + GL.matrix[currentMatrix] = GL.matrixStack[GL.currentMatrix].pop(); + }, + + glLoadIdentity__deps: ['$GL', '$GLImmediate', function() { return 'GL.matrix.lib = ' + read('gl-matrix.js') + + ';\nGL.immediate = GLImmediate;\nGL.initMatrixLibrary();\n' }], + glLoadIdentity: function() { + GL.matrix.lib.mat4.identity(GL.matrix[GL.currentMatrix]); + }, + + glLoadMatrixd: function(matrix) { + GL.matrix.lib.mat4.set(GL.matrix[GL.currentMatrix], + {{{ makeHEAPView('F64', 'matrix', 'matrix+16*8') }}}); + }, + + glLoadMatrixf: function(matrix) { + GL.matrix.lib.mat4.set(GL.matrix[GL.currentMatrix], + {{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}); + }, + + glLoadTransposeMatrixd: function(matrix) { + GL.matrix.lib.mat4.set(GL.matrix[GL.currentMatrix], + {{{ makeHEAPView('F64', 'matrix', 'matrix+16*8') }}}); + GL.matrix.lib.mat4.transpose(GL.matrix[GL.currentMatrix]); + }, + + glLoadTransposeMatrixf: function(matrix) { + GL.matrix.lib.mat4.set(GL.matrix[GL.currentMatrix], + {{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}); + GL.matrix.lib.mat4.transpose(GL.matrix[GL.currentMatrix]); + }, + + glMultMatrixd: function(matrix) { + GL.matrix.lib.mat4.multiply(GL.matrix[GL.currentMatrix], + {{{ makeHEAPView('F64', 'matrix', 'matrix+16*8') }}}); + }, + + glMultMatrixf: function(matrix) { + GL.matrix.lib.mat4.multiply(GL.matrix[GL.currentMatrix], + {{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}); + }, + + glMultTransposeMatrixd: function(matrix) { + var colMajor = GL.matrix.lib.mat4.create(); + GL.matrix.lib.mat4.set(colMajor, + {{{ makeHEAPView('F64', 'matrix', 'matrix+16*8') }}}); + GL.matrix.lib.mat4.transpose(colMajor); + GL.matrix.lib.mat4.multiply(GL.matrix[GL.currentMatrix], colMajor); + }, + + glMultTransposeMatrixf: function(matrix) { + var colMajor = GL.matrix.lib.mat4.create(); + GL.matrix.lib.mat4.set(colMajor, + {{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}); + GL.matrix.lib.mat4.transpose(colMajor); + GL.matrix.lib.mat4.multiply(GL.matrix[GL.currentMatrix], colMajor); + }, + + gluPerspective: function(fov, aspect, near, far) { + GL.matrix.lib.mat4.multiply(GL.matrix[GL.currentMatrix], + GL.matrix.lib.mat4.perspective(fov, aspect, near, far, GL.tempMatrix)); + }, + + glFrustum: function(left, right, bottom, top_, nearVal, farVal) { + GL.matrix.lib.mat4.multiply(GL.matrix[GL.currentMatrix], + GL.matrix.lib.mat4.frustum(left, right, bottom, top_, nearVal, farVal)); + }, + + glOrtho: function(left, right, bottom, top_, nearVal, farVal) { + GL.matrix.lib.mat4.multiply(GL.matrix[GL.currentMatrix], + GL.matrix.lib.mat4.ortho(left, right, bottom, top_, nearVal, farVal)); + }, + + glScaled: function(x, y, z) { + GL.matrix.lib.mat4.scale(GL.matrix[GL.currentMatrix], [x, y, z]); + }, + + glScalef__deps: ['glScaled'], + glScalef: function(x, y, z) { + _glScaled(x, y, z); + }, + + glTranslate: function(x, y, z) { + GL.matrix.lib.mat4.translate(GL.matrix[GL.currentMatrix], [x, y, z]); + }, + + glTranslatef__deps: ['glTranslated'], + glTranslatef: function(x, y, z) { + _glTranslated(x, y, z); + }, + + glRotated: function(angle, x, y, z) { + GL.matrix.lib.mat4.rotate(GL.matrix[GL.currentMatrix], angle, [x, y, z]); + }, + + glRotatef__deps: ['glRotated'], + glRotatef: function(angle, x, y, z) { + _glRotated(angle, x, y, z); + }, + + gluLookAt: function(ex, ey, ez, cx, cy, cz, ux, uy, uz) { + GL.matrix.lib.mat4.lookAt(GL.matrix[GL.currentMatrix], [ex, ey, ez], + [cx, cy, cz], [ux, uy, uz]); + }, + + gluProject: function(objX, objY, objZ, model, proj, view, winX, winY, winZ) { + // The algorithm for this functions comes from Mesa + + var inVec = new Float32Array(4); + var outVec = new Float32Array(4); + GL.matrix.lib.mat4.multiplyVec4({{{ makeHEAPView('F64', 'model', 'model+16*8') }}}, + [objX, objY, objZ, 1.0], outVec); + GL.matrix.lib.mat4.multiplyVec4({{{ makeHEAPView('F64', 'proj', 'proj+16*8') }}}, + outVec, inVec); + if (inVec[3] == 0.0) { + return 0 /* GL_FALSE */; + } + inVec[0] /= inVec[3]; + inVec[1] /= inVec[3]; + inVec[2] /= inVec[3]; + // Map x, y and z to range 0-1 */ + inVec[0] = inVec[0] * 0.5 + 0.5; + inVec[1] = inVec[1] * 0.5 + 0.5; + inVec[2] = inVec[2] * 0.5 + 0.5; + // Map x, y to viewport + inVec[0] = inVec[0] * {{{ makeGetValue('view', '2*4', 'i32') }}} + {{{ makeGetValue('view', '0*4', 'i32') }}}; + inVec[1] = inVec[1] * {{{ makeGetValue('view', '3*4', 'i32') }}} + {{{ makeGetValue('view', '1*4', 'i32') }}}; + + {{{ makeSetValue('winX', '0', 'inVec[0]', 'double') }}}; + {{{ makeSetValue('winY', '0', 'inVec[1]', 'double') }}}; + {{{ makeSetValue('winZ', '0', 'inVec[2]', 'double') }}}; + + return 1 /* GL_TRUE */; + }, + + gluUnProject: function(winX, winY, winZ, model, proj, view, objX, objY, objZ) { + var result = GL.matrix.lib.mat4.unproject([winX, winY, winZ], + {{{ makeHEAPView('F64', 'model', 'model+16*8') }}}, + {{{ makeHEAPView('F64', 'proj', 'proj+16*8') }}}, + {{{ makeHEAPView('32', 'view', 'view+4*4') }}}); + + if (result === null) { + return 0 /* GL_FALSE */; + } + + {{{ makeSetValue('objX', '0', 'result[0]', 'double') }}}; + {{{ makeSetValue('objY', '0', 'result[1]', 'double') }}}; + {{{ makeSetValue('objZ', '0', 'result[2]', 'double') }}}; + + return 1 /* GL_TRUE */; + }, + glEnable: function(cap) { if (cap == 0x0DE1) return; // no GL_TEXTURE_2D in GLES or WebGL Module.ctx.enable(cap); @@ -886,12 +1093,14 @@ var LibraryGL = { initted: false, init: function() { this.vertexShader = Module.ctx.createShader(Module.ctx.VERTEX_SHADER); - Module.ctx.shaderSource(this.vertexShader, 'attribute vec4 a_position; \n\ + Module.ctx.shaderSource(this.vertexShader, 'attribute vec3 a_position; \n\ attribute vec2 a_texCoord; \n\ varying vec2 v_texCoord; \n\ + uniform mat4 u_modelView; \n\ + uniform mat4 u_projection; \n\ void main() \n\ { \n\ - gl_Position = a_position; \n\ + gl_Position = u_projection * (u_modelView * vec4(a_position, 1.0)); \n\ v_texCoord = a_texCoord; \n\ } \n'); Module.ctx.compileShader(this.vertexShader); @@ -914,6 +1123,8 @@ var LibraryGL = { this.positionLocation = Module.ctx.getAttribLocation(this.program, 'a_position'); this.texCoordLocation = Module.ctx.getAttribLocation(this.program, 'a_texCoord'); this.textureLocation = Module.ctx.getUniformLocation(this.program, 's_texture'); + this.modelViewLocation = Module.ctx.getUniformLocation(this.program, 'u_modelView'); + this.projectionLocation = Module.ctx.getUniformLocation(this.program, 'u_projection'); // Buffer for data this.vertexData = new Float32Array(5 * this.maxElements); @@ -942,14 +1153,19 @@ var LibraryGL = { false, 5 * 4, 3 * 4); - Module.ctx.enableVertexAttribArray(this.positionLoc); - Module.ctx.enableVertexAttribArray(this.texCoordLoc); + Module.ctx.enableVertexAttribArray(this.positionLocation); + Module.ctx.enableVertexAttribArray(this.texCoordLocation); Module.ctx.activeTexture(Module.ctx.TEXTURE0); - Module.ctx.bindTexture(Module.ctx.TEXTURE_2D, this.textureId); + // Assume the texture is bound + //this.textureId = Module.ctx.getParameter(Module.ctx.TEXTURE_BINDING_2D); + //Module.ctx.bindTexture(Module.ctx.TEXTURE_2D, this.textureId); Module.ctx.uniform1i(this.textureLocation, 0); + Module.ctx.uniformMatrix4fv(this.modelViewLocation, 0 /* GL_FALSE */, GL.matrix["m"]); + Module.ctx.uniformMatrix4fv(this.projectionLocation, 0 /* GL_FALSE */, GL.matrix["p"]); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.indexObject); Module.ctx.drawElements(Module.ctx.TRIANGLES, this.indexCounter, Module.ctx.UNSIGNED_SHORT, 0); @@ -957,7 +1173,8 @@ var LibraryGL = { } }, - glBegin__deps: ['$GL', '$GLImmediate', function() { return 'GL.matrix.lib = ' + read('gl-matrix.js') + ';\nGL.immediate = GLImmediate;\n' }], + glBegin__deps: ['$GL', '$GLImmediate', function() { return 'GL.matrix.lib = ' + read('gl-matrix.js') + + ';\nGL.immediate = GLImmediate;\nGL.initMatrixLibrary();\n' }], glBegin: function(mode) { if (!GL.immediate.initted) GL.immediate.init(); GL.immediate.mode = mode; @@ -998,17 +1215,12 @@ var LibraryGL = { GL.immediate.vertexData[5*GL.immediate.vertexCounter+3] = u; GL.immediate.vertexData[5*GL.immediate.vertexCounter+4] = v; }, - - // TODO - glMatrixMode: function(){}, - glLoadIdentity: function(){}, - glOrtho: function(){}, }; // Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name [[0, 'shadeModel fogi fogfv getError* finish flush'], [1, 'clearDepth clearDepth[f] depthFunc disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation polygonOffset hint sampleCoverage isEnabled*'], - [2, 'pixelStorei blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate'], + [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate'], [3, 'texParameteri texParameterf drawArrays vertexAttrib2f stencilFunc stencilOp'], [4, 'viewport clearColor scissor vertexAttrib3f colorMask drawElements renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'], [5, 'vertexAttrib4f'], diff --git a/src/library_glut.js b/src/library_glut.js index 0de0ce6a..e4b2d138 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -286,6 +286,10 @@ var LibraryGLUT = { Module['canvas'].height = GLUT.initWindowHeight = height; }, + glutInitWindowPosition: function(x, y) { + // Ignore for now + }, + glutGet: function(type) { switch (type) { case 100: /* GLUT_WINDOW_X */ diff --git a/tests/runner.py b/tests/runner.py index 099cd609..7c24c799 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -7009,12 +7009,12 @@ elif 'browser' in str(sys.argv): Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_gl_read.c'), '-o', 'something.html']).communicate() self.run_browser('something.html', '.', '/report_result?1') - def zzztest_sdl_ogl(self): + def test_sdl_ogl(self): # SDL, OpenGL, textures, immediate mode shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) - self.reftest(path_from_root('tests', 'gears.png')) + self.reftest(path_from_root('tests', 'screenshot-gray.png')) Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() - self.run_browser('something.html', 'You should see animating gears.', '/report_result?1779') + self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') def test_worker(self): # Test running in a web worker diff --git a/tests/screenshot-gray.png b/tests/screenshot-gray.png Binary files differnew file mode 100644 index 00000000..928f9f9a --- /dev/null +++ b/tests/screenshot-gray.png diff --git a/tests/sdl_ogl.c b/tests/sdl_ogl.c index 6884f478..cec32bf1 100644 --- a/tests/sdl_ogl.c +++ b/tests/sdl_ogl.c @@ -90,12 +90,16 @@ int main(int argc, char *argv[]) glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + SDL_LockSurface(surface); + // Add some greyness memset(surface->pixels, 0x66, surface->w*surface->h); - + // Edit the texture object's image data using the information SDL_Surface gives us glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + SDL_UnlockSurface(surface); } else { printf("SDL could not load image.bmp: %s\n", SDL_GetError()); |