aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library_gl.js246
-rw-r--r--src/library_glut.js4
-rwxr-xr-xtests/runner.py6
-rw-r--r--tests/screenshot-gray.pngbin0 -> 170477 bytes
-rw-r--r--tests/sdl_ogl.c6
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
new file mode 100644
index 00000000..928f9f9a
--- /dev/null
+++ b/tests/screenshot-gray.png
Binary files differ
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());