aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library.js164
-rw-r--r--src/library_gl.js76
-rw-r--r--src/library_sdl.js8
-rw-r--r--src/runtime.js4
-rw-r--r--tests/gl_ps_packed.c230
-rw-r--r--tests/perspective.c408
-rw-r--r--tests/perspective.pngbin0 -> 2477 bytes
-rwxr-xr-xtests/runner.py22
8 files changed, 815 insertions, 97 deletions
diff --git a/src/library.js b/src/library.js
index 1e026937..d4a0046c 100644
--- a/src/library.js
+++ b/src/library.js
@@ -624,7 +624,12 @@ LibraryManager.library = {
// dirent.h
// ==========================================================================
- __dirent_struct_layout: Runtime.generateStructInfo(['d_ino', 'd_name', 'd_off', 'd_reclen', 'd_type'], '%struct.dirent'),
+ __dirent_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'd_ino'],
+ ['b1024', 'd_name'],
+ ['i32', 'd_off'],
+ ['i32', 'd_reclen'],
+ ['i32', 'd_type']]),
opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'],
opendir: function(dirname) {
// DIR *opendir(const char *dirname);
@@ -786,7 +791,9 @@ LibraryManager.library = {
// utime.h
// ==========================================================================
- __utimbuf_struct_layout: Runtime.generateStructInfo(['actime', 'modtime'], '%struct.utimbuf'),
+ __utimbuf_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'actime'],
+ ['i32', 'modtime']]),
utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__utimbuf_struct_layout'],
utime: function(path, times) {
// int utime(const char *path, const struct utimbuf *times);
@@ -877,23 +884,23 @@ LibraryManager.library = {
// ==========================================================================
__stat_struct_layout: Runtime.generateStructInfo([
- 'st_dev',
- 'st_ino',
- 'st_mode',
- 'st_nlink',
- 'st_uid',
- 'st_gid',
- 'st_rdev',
- 'st_size',
- 'st_atime',
- 'st_spare1',
- 'st_mtime',
- 'st_spare2',
- 'st_ctime',
- 'st_spare3',
- 'st_blksize',
- 'st_blocks',
- 'st_spare4'], '%struct.stat'),
+ ['i32', 'st_dev'],
+ ['i32', 'st_ino'],
+ ['i32', 'st_mode'],
+ ['i32', 'st_nlink'],
+ ['i32', 'st_uid'],
+ ['i32', 'st_gid'],
+ ['i32', 'st_rdev'],
+ ['i32', 'st_size'],
+ ['i32', 'st_atime'],
+ ['i32', 'st_spare1'],
+ ['i32', 'st_mtime'],
+ ['i32', 'st_spare2'],
+ ['i32', 'st_ctime'],
+ ['i32', 'st_spare3'],
+ ['i32', 'st_blksize'],
+ ['i32', 'st_blocks'],
+ ['i32', 'st_spare4']]),
stat__deps: ['$FS', '__stat_struct_layout'],
stat: function(path, buf, dontResolveLastLink) {
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html
@@ -1065,17 +1072,17 @@ LibraryManager.library = {
// ==========================================================================
__statvfs_struct_layout: Runtime.generateStructInfo([
- 'f_bsize',
- 'f_frsize',
- 'f_blocks',
- 'f_bfree',
- 'f_bavail',
- 'f_files',
- 'f_ffree',
- 'f_favail',
- 'f_fsid',
- 'f_flag',
- 'f_namemax'], '%struct.statvfs'),
+ ['i32', 'f_bsize'],
+ ['i32', 'f_frsize'],
+ ['i32', 'f_blocks'],
+ ['i32', 'f_bfree'],
+ ['i32', 'f_bavail'],
+ ['i32', 'f_files'],
+ ['i32', 'f_ffree'],
+ ['i32', 'f_favail'],
+ ['i32', 'f_fsid'],
+ ['i32', 'f_flag'],
+ ['i32', 'f_namemax']]),
statvfs__deps: ['$FS', '__statvfs_struct_layout'],
statvfs: function(path, buf) {
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html
@@ -1110,12 +1117,12 @@ LibraryManager.library = {
// ==========================================================================
__flock_struct_layout: Runtime.generateStructInfo([
- 'l_type',
- 'l_whence',
- 'l_start',
- 'l_len',
- 'l_pid',
- 'l_xxx'], '%struct.flock'),
+ ['i16', 'l_type'],
+ ['i16', 'l_whence'],
+ ['i32', 'l_start'],
+ ['i32', 'l_len'],
+ ['i16', 'l_pid'],
+ ['i16', 'l_xxx']]),
open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'],
open: function(path, oflag, varargs) {
// int open(const char *path, int oflag, ...);
@@ -1336,7 +1343,10 @@ LibraryManager.library = {
// poll.h
// ==========================================================================
- __pollfd_struct_layout: Runtime.generateStructInfo(['fd', 'events', 'revents'], '%struct.pollfd'),
+ __pollfd_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'fd'],
+ ['i16', 'events'],
+ ['i16', 'revents']]),
poll__deps: ['$FS', '__pollfd_struct_layout'],
poll: function(fds, nfds, timeout) {
// int poll(struct pollfd fds[], nfds_t nfds, int timeout);
@@ -5619,11 +5629,11 @@ LibraryManager.library = {
// ==========================================================================
__utsname_struct_layout: Runtime.generateStructInfo([
- 'sysname',
- 'nodename',
- 'release',
- 'version',
- 'machine'], '%struct.utsname'),
+ ['b32', 'sysname'],
+ ['b32', 'nodename'],
+ ['b32', 'release'],
+ ['b32', 'version'],
+ ['b32', 'machine']]),
uname__deps: ['__utsname_struct_layout'],
uname: function(name) {
// int uname(struct utsname *name);
@@ -5823,17 +5833,17 @@ LibraryManager.library = {
},
__tm_struct_layout: Runtime.generateStructInfo([
- 'tm_sec',
- 'tm_min',
- 'tm_hour',
- 'tm_mday',
- 'tm_mon',
- 'tm_year',
- 'tm_wday',
- 'tm_yday',
- 'tm_isdst',
- 'tm_gmtoff',
- 'tm_zone'], '%struct.tm'),
+ ['i32', 'tm_sec'],
+ ['i32', 'tm_min'],
+ ['i32', 'tm_hour'],
+ ['i32', 'tm_mday'],
+ ['i32', 'tm_mon'],
+ ['i32', 'tm_year'],
+ ['i32', 'tm_wday'],
+ ['i32', 'tm_yday'],
+ ['i32', 'tm_isdst'],
+ ['i32', 'tm_gmtoff'],
+ ['i32', 'tm_zone']]),
// Statically allocated time struct.
__tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)',
// Statically allocated timezone strings.
@@ -6032,7 +6042,9 @@ LibraryManager.library = {
// sys/time.h
// ==========================================================================
- __timespec_struct_layout: Runtime.generateStructInfo(['tv_sec', 'tv_nsec'], '%struct.timespec'),
+ __timespec_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'tv_sec'],
+ ['i32', 'tv_nsec']]),
// TODO: Implement these for real.
clock_gettime__deps: ['__timespec_struct_layout'],
clock_gettime: function(clk_id, tp) {
@@ -6089,10 +6101,10 @@ LibraryManager.library = {
// ==========================================================================
__tms_struct_layout: Runtime.generateStructInfo([
- 'tms_utime',
- 'tms_stime',
- 'tms_cutime',
- 'tms_cstime'], '%struct.tms'),
+ ['i32', 'tms_utime'],
+ ['i32', 'tms_stime'],
+ ['i32', 'tms_cutime'],
+ ['i32', 'tms_cstime']]),
times__deps: ['__tms_struct_layout', 'memset'],
times: function(buffer) {
// clock_t times(struct tms *buffer);
@@ -6614,7 +6626,9 @@ LibraryManager.library = {
// ==========================================================================
// TODO: Implement for real.
- __rlimit_struct_layout: Runtime.generateStructInfo(['rlim_cur', 'rlim_max'], '%struct.rlimit'),
+ __rlimit_struct_layout: Runtime.generateStructInfo([
+ ['i32', 'rlim_cur'],
+ ['i32', 'rlim_max']]),
getrlimit__deps: ['__rlimit_struct_layout'],
getrlimit: function(resource, rlp) {
// int getrlimit(int resource, struct rlimit *rlp);
@@ -6630,22 +6644,22 @@ LibraryManager.library = {
// TODO: Implement for real. We just do time used, and no useful data
__rusage_struct_layout: Runtime.generateStructInfo([
- 'ru_utime',
- 'ru_stime',
- 'ru_maxrss',
- 'ru_ixrss',
- 'ru_idrss',
- 'ru_isrss',
- 'ru_minflt',
- 'ru_majflt',
- 'ru_nswap',
- 'ru_inblock',
- 'ru_oublock',
- 'ru_msgsnd',
- 'ru_msgrcv',
- 'ru_nsignals',
- 'ru_nvcsw',
- 'ru_nivcsw'], '%struct.rusage'),
+ ['i64', 'ru_utime'],
+ ['i64', 'ru_stime'],
+ ['i32', 'ru_maxrss'],
+ ['i32', 'ru_ixrss'],
+ ['i32', 'ru_idrss'],
+ ['i32', 'ru_isrss'],
+ ['i32', 'ru_minflt'],
+ ['i32', 'ru_majflt'],
+ ['i32', 'ru_nswap'],
+ ['i32', 'ru_inblock'],
+ ['i32', 'ru_oublock'],
+ ['i32', 'ru_msgsnd'],
+ ['i32', 'ru_msgrcv'],
+ ['i32', 'ru_nsignals'],
+ ['i32', 'ru_nvcsw'],
+ ['i32', 'ru_nivcsw']]),
getrusage__deps: ['__rusage_struct_layout'],
getrusage: function(resource, rlp) {
// %struct.timeval = type { i32, i32 }
diff --git a/src/library_gl.js b/src/library_gl.js
index f7966dab..c43b424d 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -1997,22 +1997,58 @@ var LibraryGL = {
#endif
if (attribute.stride) stride = attribute.stride;
}
-
- var bytes = 0;
- for (var i = 0; i < attributes.length; i++) {
- var attribute = attributes[i];
- if (!attribute) break;
- attribute.offset = attribute.pointer - start;
- if (attribute.offset > bytes) { // ensure we start where we should
- assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment
- bytes += attribute.offset - bytes;
+ var bytes = 0; // total size in bytes
+ if (!stride && !beginEnd) {
+ // beginEnd can not have stride in the attributes, that is fine. otherwise,
+ // no stride means that all attributes are in fact packed. to keep the rest of
+ // our emulation code simple, we perform unpacking/restriding here. this adds overhead, so
+ // it is a good idea to not hit this!
+#if ASSERTIONS
+ Runtime.warnOnce('Unpacking/restriding attributes, this is not fast');
+#endif
+ if (!GL.immediate.restrideBuffer) GL.immediate.restrideBuffer = _malloc(GL.immediate.MAX_TEMP_BUFFER_SIZE);
+ start = GL.immediate.restrideBuffer;
+#if ASSERTIONS
+ assert(start % 4 == 0);
+#endif
+ // calculate restrided offsets and total size
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ var size = attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
+ if (size % 4 != 0) size += 4 - (size % 4); // align everything
+ attribute.offset = bytes;
+ bytes += size;
+ }
+ // copy out the data (we need to know the stride for that, and define attribute.pointer
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ var size4 = Math.floor((attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot])/4);
+ for (var j = 0; j < count; j++) {
+ for (var k = 0; k < size4; k++) { // copy in chunks of 4 bytes, our alignment makes this possible
+ HEAP32[((start + attribute.offset + bytes*j)>>2) + k] = HEAP32[(attribute.pointer>>2) + j*size4 + k];
+ }
+ }
+ attribute.pointer = start + attribute.offset;
+ }
+ } else {
+ // normal situation, everything is strided and in the same buffer
+ for (var i = 0; i < attributes.length; i++) {
+ var attribute = attributes[i];
+ if (!attribute) break;
+ attribute.offset = attribute.pointer - start;
+ if (attribute.offset > bytes) { // ensure we start where we should
+ assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment
+ bytes += attribute.offset - bytes;
+ }
+ bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
+ if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment
+ }
+ assert(beginEnd || bytes <= stride); // if not begin-end, explicit stride should make sense with total byte size
+ if (bytes < stride) { // ensure the size is that of the stride
+ bytes = stride;
}
- bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
- if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment
- }
- assert(stride == 0 || bytes <= stride);
- if (bytes < stride) { // ensure the size is that of the stride
- bytes = stride;
}
GL.immediate.stride = bytes;
@@ -2310,7 +2346,10 @@ var LibraryGL = {
case 0x8076: // GL_COLOR_ARRAY
attrib = GL.immediate.COLOR; break;
default:
- throw 'unhandled clientstate: ' + cap;
+#if ASSERTIONS
+ Module.printErr('WARNING: unhandled clientstate: ' + cap);
+#endif
+ return;
}
if (disable && GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = false;
@@ -2464,8 +2503,9 @@ var LibraryGL = {
gluPerspective: function(fov, aspect, near, far) {
GL.immediate.matricesModified = true;
- GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix],
- GL.immediate.matrix.lib.mat4.perspective(fov, aspect, near, far, GL.immediate.currentMatrix));
+ GL.immediate.matrix[GL.immediate.currentMatrix] =
+ GL.immediate.matrix.lib.mat4.perspective(fov, aspect, near, far,
+ GL.immediate.matrix[GL.immediate.currentMatrix]);
},
gluLookAt: function(ex, ey, ez, cx, cy, cz, ux, uy, uz) {
diff --git a/src/library_sdl.js b/src/library_sdl.js
index cfab6410..4ad7a9a9 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -607,9 +607,11 @@ var LibrarySDL = {
SDL_Init: function(what) {
SDL.startTime = Date.now();
// capture all key events. we just keep down and up, but also capture press to prevent default actions
- document.onkeydown = SDL.receiveEvent;
- document.onkeyup = SDL.receiveEvent;
- document.onkeypress = SDL.receiveEvent;
+ if (!Module['doNotCaptureKeyboard']) {
+ document.onkeydown = SDL.receiveEvent;
+ document.onkeyup = SDL.receiveEvent;
+ document.onkeypress = SDL.receiveEvent;
+ }
window.onunload = SDL.receiveEvent;
SDL.keyboardState = _malloc(0x10000);
_memset(SDL.keyboardState, 0, 0x10000);
diff --git a/src/runtime.js b/src/runtime.js
index e5b86065..95c74647 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -242,6 +242,10 @@ var Runtime = {
} else if (Runtime.isStructType(field)) {
size = Types.types[field].flatSize;
alignSize = Types.types[field].alignSize;
+ } else if (field[0] == 'b') {
+ // bN, large number field, like a [N x i8]
+ size = field.substr(1)|0;
+ alignSize = 1;
} else {
throw 'Unclear type in struct: ' + field + ', in ' + type.name_ + ' :: ' + dump(Types.types[type.name_]);
}
diff --git a/tests/gl_ps_packed.c b/tests/gl_ps_packed.c
new file mode 100644
index 00000000..9ab99cb8
--- /dev/null
+++ b/tests/gl_ps_packed.c
@@ -0,0 +1,230 @@
+/*******************************************************************
+ * *
+ * Using SDL With OpenGL *
+ * *
+ * Tutorial by Kyle Foley (sdw) *
+ * *
+ * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL *
+ * *
+ *******************************************************************/
+
+/*
+THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+RESULTING FROM THE USE, MODIFICATION, OR
+REDISTRIBUTION OF THIS SOFTWARE.
+*/
+
+#if !EMSCRIPTEN
+#define USE_GLEW 1
+#endif
+
+#if USE_GLEW
+#include "GL/glew.h"
+#endif
+
+#include "SDL/SDL.h"
+#include "SDL/SDL_image.h"
+#if !USE_GLEW
+#include "SDL/SDL_opengl.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+void shaders() {
+#if USE_GLEW
+ glewInit();
+#endif
+
+ GLint ok;
+
+ const char *vertexShader = "void main(void) \n"
+ "{ \n"
+ " gl_Position = ftransform(); \n"
+ " gl_TexCoord[0] = gl_MultiTexCoord0; \n"
+ " gl_FrontColor = gl_Color; \n"
+ "} \n";
+ const char *fragmentShader = "uniform sampler2D tex0; \n"
+ "void main(void) \n"
+ "{ \n"
+ " gl_FragColor = gl_Color * texture2D(tex0, gl_TexCoord[0].xy); \n"
+ "} \n";
+
+ GLuint vs = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vs, 1, &vertexShader, NULL);
+ glCompileShader(vs);
+ glGetShaderiv(vs, GL_COMPILE_STATUS, &ok);
+ assert(ok);
+
+ GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fs, 1, &fragmentShader, NULL);
+ glCompileShader(fs);
+ glGetShaderiv(fs, GL_COMPILE_STATUS, &ok);
+ assert(ok);
+
+ GLuint program = glCreateProgram();
+
+ glAttachShader(program, vs);
+ glAttachShader(program, fs);
+ glLinkProgram(program);
+ glGetProgramiv(program, GL_LINK_STATUS, &ok);
+ assert(ok);
+
+ glUseProgram(program);
+}
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ // Slightly different SDL initialization
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
+
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed*
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // Set the OpenGL state after creating the context with SDL_SetVideoMode
+
+ glClearColor( 0, 0, 0, 0 );
+
+#if !EMSCRIPTEN
+ glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL
+#endif
+
+ glViewport( 0, 0, 640, 480 );
+
+ glMatrixMode( GL_PROJECTION );
+ GLfloat matrixData[] = { 2.0/640, 0, 0, 0,
+ 0, -2.0/480, 0, 0,
+ 0, 0, -1, 0,
+ -1, 1, 0, 1 };
+ glLoadMatrixf(matrixData); // test loadmatrix
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ // Load the OpenGL texture
+
+ GLuint texture; // Texture object handle
+ SDL_Surface *surface; // Gives us the information to make the texture
+
+ if ( (surface = IMG_Load("screenshot.png")) ) {
+
+ // Check that the image's width is a power of 2
+ if ( (surface->w & (surface->w - 1)) != 0 ) {
+ printf("warning: image.bmp's width is not a power of 2\n");
+ }
+
+ // Also check if the height is a power of 2
+ if ( (surface->h & (surface->h - 1)) != 0 ) {
+ printf("warning: image.bmp's height is not a power of 2\n");
+ }
+
+ // Have OpenGL generate a texture object handle for us
+ glGenTextures( 1, &texture );
+
+ // Bind the texture object
+ glBindTexture( GL_TEXTURE_2D, texture );
+
+ // Set the texture's stretching properties
+ 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());
+ SDL_Quit();
+ return 1;
+ }
+
+ // Free the SDL_Surface only if it was successfully created
+ if ( surface ) {
+ SDL_FreeSurface( surface );
+ }
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ shaders();
+
+ // Bind the texture to which subsequent calls refer to
+ glBindTexture( GL_TEXTURE_2D, texture );
+
+ // Use clientside vertex pointers to render two items. In this test we have each
+ // attribute in a separate buffer, packed (i.e. stride == 0)
+ GLfloat vertexData[] = { 10, 10,
+ 300, 10,
+ 300, 128,
+ 10, 128,
+ 410, 10,
+ 600, 10,
+ 630, 200,
+ 310, 250,
+ 100, 300,
+ 300, 300,
+ 300, 400,
+ 100, 400 };
+ GLfloat textureData[] = { 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1,
+ 0, 0.5,
+ 1, 0.5,
+ 1, 1,
+ 0.5, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1, };
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_FLOAT, 0, textureData);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, vertexData);
+
+ glDrawArrays(GL_QUADS, 0, 12);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ SDL_GL_SwapBuffers();
+
+#if !EMSCRIPTEN
+ // Wait for 3 seconds to give us a chance to see the image
+ SDL_Delay(3000);
+#endif
+
+ // Now we can delete the OpenGL texture and close down SDL
+ glDeleteTextures( 1, &texture );
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/tests/perspective.c b/tests/perspective.c
new file mode 100644
index 00000000..72f4c50f
--- /dev/null
+++ b/tests/perspective.c
@@ -0,0 +1,408 @@
+/*
+ * SDL OpenGL Tutorial.
+ * (c) Michael Vance, 2000
+ * briareos@lokigames.com
+ *
+ * Distributed under terms of the LGPL.
+ */
+
+#include <SDL/SDL.h>
+
+#ifdef EMSCRIPTEN
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include "emscripten.h"
+#else
+#include <GL/gl.h>
+#include <GL/glu.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef EMSCRIPTEN
+ #define emColor4ubv(x)
+#else
+#define emColor4ubv(x) glColor4ubv(x)
+#endif
+
+static GLboolean should_rotate = GL_TRUE;
+
+static void quit_tutorial( int code )
+{
+ /*
+ * Quit SDL so we can release the fullscreen
+ * mode and restore the previous video settings,
+ * etc.
+ */
+ SDL_Quit( );
+
+ /* Exit program. */
+ exit( code );
+}
+
+static void handle_key_down( SDL_keysym* keysym )
+{
+
+ /*
+ * We're only interested if 'Esc' has
+ * been presssed.
+ *
+ * EXERCISE:
+ * Handle the arrow keys and have that change the
+ * viewing position/angle.
+ */
+ switch( keysym->sym ) {
+ case SDLK_ESCAPE:
+ quit_tutorial( 0 );
+ break;
+ case SDLK_SPACE:
+ should_rotate = !should_rotate;
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void process_events( void )
+{
+ /* Our SDL event placeholder. */
+ SDL_Event event;
+
+ /* Grab all the events off the queue. */
+ while( SDL_PollEvent( &event ) ) {
+
+ switch( event.type ) {
+ case SDL_KEYDOWN:
+ /* Handle key presses. */
+ handle_key_down( &event.key.keysym );
+ break;
+ case SDL_QUIT:
+ /* Handle quit requests (like Ctrl-c). */
+ quit_tutorial( 0 );
+ break;
+ }
+
+ }
+
+}
+
+static void draw_screen( void )
+{
+ /* Our angle of rotation. */
+ static float angle = 0.0f;
+
+ /*
+ * EXERCISE:
+ * Replace this awful mess with vertex
+ * arrays and a call to glDrawElements.
+ *
+ * EXERCISE:
+ * After completing the above, change
+ * it to use compiled vertex arrays.
+ *
+ * EXERCISE:
+ * Verify my windings are correct here ;).
+ */
+ static GLfloat v0[] = { -1.0f, -1.0f, 1.0f };
+ static GLfloat v1[] = { 1.0f, -1.0f, 1.0f };
+ static GLfloat v2[] = { 1.0f, 1.0f, 1.0f };
+ static GLfloat v3[] = { -1.0f, 1.0f, 1.0f };
+ static GLfloat v4[] = { -1.0f, -1.0f, -1.0f };
+ static GLfloat v5[] = { 1.0f, -1.0f, -1.0f };
+ static GLfloat v6[] = { 1.0f, 1.0f, -1.0f };
+ static GLfloat v7[] = { -1.0f, 1.0f, -1.0f };
+ static GLubyte red[] = { 255, 0, 0, 255 };
+ static GLubyte green[] = { 0, 255, 0, 255 };
+ static GLubyte blue[] = { 0, 0, 255, 255 };
+ static GLubyte white[] = { 255, 255, 255, 255 };
+ static GLubyte yellow[] = { 0, 255, 255, 255 };
+ static GLubyte black[] = { 0, 0, 0, 255 };
+ static GLubyte orange[] = { 255, 255, 0, 255 };
+ static GLubyte purple[] = { 255, 0, 255, 0 };
+
+ /* Clear the color and depth buffers. */
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+
+ /* We don't want to modify the projection matrix. */
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity( );
+
+ /* Move down the z-axis. */
+ glTranslatef( 0.0, 0.0, -5.0 );
+
+ /* Rotate. */
+ glRotatef( angle, 0.0, 1.0, 0.0 );
+
+ if( should_rotate ) {
+
+ if( ++angle > 360.0f ) {
+ angle = 0.0f;
+ }
+
+ }
+
+ /* Send our triangle data to the pipeline. */
+ glBegin( GL_TRIANGLES );
+
+ emColor4ubv( red );
+ glVertex3fv( v0 );
+ emColor4ubv( green );
+ glVertex3fv( v1 );
+ emColor4ubv( blue );
+ glVertex3fv( v2 );
+
+ emColor4ubv( red );
+ glVertex3fv( v0 );
+ emColor4ubv( blue );
+ glVertex3fv( v2 );
+ emColor4ubv( white );
+ glVertex3fv( v3 );
+
+ emColor4ubv( green );
+ glVertex3fv( v1 );
+ emColor4ubv( black );
+ glVertex3fv( v5 );
+ emColor4ubv( orange );
+ glVertex3fv( v6 );
+
+ emColor4ubv( green );
+ glVertex3fv( v1 );
+ emColor4ubv( orange );
+ glVertex3fv( v6 );
+ emColor4ubv( blue );
+ glVertex3fv( v2 );
+
+ emColor4ubv( black );
+ glVertex3fv( v5 );
+ emColor4ubv( yellow );
+ glVertex3fv( v4 );
+ emColor4ubv( purple );
+ glVertex3fv( v7 );
+
+ emColor4ubv( black );
+ glVertex3fv( v5 );
+ emColor4ubv( purple );
+ glVertex3fv( v7 );
+ emColor4ubv( orange );
+ glVertex3fv( v6 );
+
+ emColor4ubv( yellow );
+ glVertex3fv( v4 );
+ emColor4ubv( red );
+ glVertex3fv( v0 );
+ emColor4ubv( white );
+ glVertex3fv( v3 );
+
+ emColor4ubv( yellow );
+ glVertex3fv( v4 );
+ emColor4ubv( white );
+ glVertex3fv( v3 );
+ emColor4ubv( purple );
+ glVertex3fv( v7 );
+
+ emColor4ubv( white );
+ glVertex3fv( v3 );
+ emColor4ubv( blue );
+ glVertex3fv( v2 );
+ emColor4ubv( orange );
+ glVertex3fv( v6 );
+
+ emColor4ubv( white );
+ glVertex3fv( v3 );
+ emColor4ubv( orange );
+ glVertex3fv( v6 );
+ emColor4ubv( purple );
+ glVertex3fv( v7 );
+
+ emColor4ubv( green );
+ glVertex3fv( v1 );
+ emColor4ubv( red );
+ glVertex3fv( v0 );
+ emColor4ubv( yellow );
+ glVertex3fv( v4 );
+
+ emColor4ubv( green );
+ glVertex3fv( v1 );
+ emColor4ubv( yellow );
+ glVertex3fv( v4 );
+ emColor4ubv( black );
+ glVertex3fv( v5 );
+
+ glEnd( );
+
+ /*
+ * EXERCISE:
+ * Draw text telling the user that 'Spc'
+ * pauses the rotation and 'Esc' quits.
+ * Do it using vetors and textured quads.
+ */
+
+ /*
+ * Swap the buffers. This this tells the driver to
+ * render the next frame from the contents of the
+ * back-buffer, and to set all rendering operations
+ * to occur on what was the front-buffer.
+ *
+ * Double buffering prevents nasty visual tearing
+ * from the application drawing on areas of the
+ * screen that are being updated at the same time.
+ */
+ SDL_GL_SwapBuffers( );
+}
+
+static void setup_opengl( int width, int height )
+{
+ float ratio = (float) width / (float) height;
+
+ /* Our shading model--Gouraud (smooth). */
+ glShadeModel( GL_SMOOTH );
+
+ /* Culling. */
+ glCullFace( GL_BACK );
+ glFrontFace( GL_CCW );
+ glEnable( GL_CULL_FACE );
+
+ /* Set the clear color. */
+ glClearColor( 0, 0, 0, 0 );
+
+ /* Setup our viewport. */
+ glViewport( 0, 0, width, height );
+
+ /*
+ * Change to the projection matrix and set
+ * our viewing volume.
+ */
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity( );
+ /*
+ * EXERCISE:
+ * Replace this with a call to glFrustum.
+ */
+ gluPerspective( 60.0, ratio, 1.0, 1024.0 );
+}
+
+void one_iter();
+void one_iter() {
+ process_events( );
+ /* Draw the screen. */
+ draw_screen( );
+}
+
+int main( int argc, char* argv[] )
+{
+ /* Information about the current video settings. */
+ const SDL_VideoInfo* info = NULL;
+ /* Dimensions of our window. */
+ int width = 0;
+ int height = 0;
+ /* Color depth in bits of our window. */
+ int bpp = 0;
+ /* Flags we will pass into SDL_SetVideoMode. */
+ int flags = 0;
+
+ /* First, initialize SDL's video subsystem. */
+ if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
+ /* Failed, exit. */
+ fprintf( stderr, "Video initialization failed: %s\n",
+ SDL_GetError( ) );
+ quit_tutorial( 1 );
+ }
+
+ /* Let's get some video information. */
+ info = SDL_GetVideoInfo( );
+
+ if( !info ) {
+ /* This should probably never happen. */
+ fprintf( stderr, "Video query failed: %s\n",
+ SDL_GetError( ) );
+ quit_tutorial( 1 );
+ }
+
+ /*
+ * Set our width/height to 640/480 (you would
+ * of course let the user decide this in a normal
+ * app). We get the bpp we will request from
+ * the display. On X11, VidMode can't change
+ * resolution, so this is probably being overly
+ * safe. Under Win32, ChangeDisplaySettings
+ * can change the bpp.
+ */
+ width = 640;
+ height = 480;
+ bpp = info->vfmt->BitsPerPixel;
+
+ /*
+ * Now, we want to setup our requested
+ * window attributes for our OpenGL window.
+ * We want *at least* 5 bits of red, green
+ * and blue. We also want at least a 16-bit
+ * depth buffer.
+ *
+ * The last thing we do is request a double
+ * buffered window. '1' turns on double
+ * buffering, '0' turns it off.
+ *
+ * Note that we do not use SDL_DOUBLEBUF in
+ * the flags to SDL_SetVideoMode. That does
+ * not affect the GL attribute state, only
+ * the standard 2D blitting setup.
+ */
+// SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
+// SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
+// SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
+// SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
+// SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+
+ /*
+ * We want to request that SDL provide us
+ * with an OpenGL window, in a fullscreen
+ * video mode.
+ *
+ * EXERCISE:
+ * Make starting windowed an option, and
+ * handle the resize events properly with
+ * glViewport.
+ */
+ flags = SDL_OPENGL;// | SDL_FULLSCREEN;
+
+ /*
+ * Set the video mode
+ */
+ if( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
+ /*
+ * This could happen for a variety of reasons,
+ * including DISPLAY not being set, the specified
+ * resolution not being available, etc.
+ */
+ fprintf( stderr, "Video mode set failed: %s\n",
+ SDL_GetError( ) );
+ quit_tutorial( 1 );
+ }
+
+ /*
+ * At this point, we should have a properly setup
+ * double-buffered window for use with OpenGL.
+ */
+ setup_opengl( width, height );
+
+ /*
+ * Now we want to begin our normal app process--
+ * an event loop with a lot of redrawing.
+ */
+ one_iter(); // just one for testing purposes
+
+#ifndef EMSCRIPTEN
+ SDL_Delay(2000);
+#endif
+
+ /*
+ * EXERCISE:
+ * Record timings using SDL_GetTicks() and
+ * and print out frames per second at program
+ * end.
+ */
+
+ /* Never reached. */
+ return 0;
+}
diff --git a/tests/perspective.png b/tests/perspective.png
new file mode 100644
index 00000000..04cedc71
--- /dev/null
+++ b/tests/perspective.png
Binary files differ
diff --git a/tests/runner.py b/tests/runner.py
index 71414dd5..ce9f0551 100755
--- a/