nuclear@0: /* nuclear@0: SaneGL - a small library to bring back sanity to WebGL nuclear@0: Copyright (C) 2011 John Tsiombikas nuclear@0: nuclear@0: This program is free software: you can redistribute it and/or modify nuclear@0: it under the terms of the GNU General Public License as published by nuclear@0: the Free Software Foundation, either version 3 of the License, or nuclear@0: (at your option) any later version. nuclear@0: nuclear@0: This program is distributed in the hope that it will be useful, nuclear@0: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@0: GNU General Public License for more details. nuclear@0: nuclear@0: You should have received a copy of the GNU General Public License nuclear@0: along with this program. If not, see . nuclear@0: */ nuclear@0: const GL_MODELVIEW = 0; nuclear@0: const GL_PROJECTION = 1; nuclear@0: const GL_TEXTURE = 2; nuclear@0: nuclear@0: const GL_POINTS = 1; nuclear@0: const GL_LINES = 2; nuclear@0: const GL_TRIANGLES = 3; nuclear@0: const GL_QUADS = 4; nuclear@0: nuclear@0: var gl_ident_val = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; nuclear@0: var gl_mmode = GL_MODELVIEW; nuclear@0: var gl_mat = new Array( nuclear@0: [new Float32Array(gl_ident_val)], nuclear@0: [new Float32Array(gl_ident_val)], nuclear@0: [new Float32Array(gl_ident_val)]); nuclear@0: nuclear@3: const GL_MAX_VERTS = 512; nuclear@0: nuclear@0: var gl_prim = 0; nuclear@0: var gl_vbuf = null, gl_nbuf = null, gl_cbuf = null, gl_tbuf = null; nuclear@0: var gl_vertex, gl_normal, gl_color, gl_texcoord; nuclear@0: var gl_nverts, gl_vert_calls; nuclear@0: var gl_curr_normal = new Float32Array(3); nuclear@0: var gl_curr_color = new Float32Array(4); nuclear@0: var gl_curr_texcoord = new Float32Array(2); nuclear@0: var gl_vloc, gl_nloc, gl_cloc, gl_tloc; nuclear@0: var gl_use_normal, gl_use_color, gl_use_texcoord; nuclear@0: nuclear@0: function glMatrixMode(mode) nuclear@0: { nuclear@0: gl_mmode = mode; nuclear@0: } nuclear@0: nuclear@0: function glPushMatrix() nuclear@0: { nuclear@0: var mtop = gl_mat[gl_mmode].length - 1; nuclear@0: nuclear@0: gl_mat[gl_mmode].push(new Float32Array(16)); nuclear@0: m4_copy(gl_mat[gl_mmode][mtop + 1], gl_mat[gl_mmode][mtop]); nuclear@0: } nuclear@0: nuclear@0: function glPopMatrix() nuclear@0: { nuclear@0: if(gl_mat[gl_mmode].length <= 1) { nuclear@0: alert('glPopMatrix underflow'); nuclear@0: } nuclear@0: gl_mat[gl_mmode].pop(); nuclear@0: } nuclear@0: nuclear@0: function glLoadIdentity() nuclear@0: { nuclear@0: var mtop = gl_mat[gl_mmode].length - 1; nuclear@0: m4_identity(gl_mat[gl_mmode][mtop]); nuclear@0: } nuclear@0: nuclear@0: function glLoadMatrixf(mat) nuclear@0: { nuclear@0: var mtop = gl_mat[gl_mmode].length - 1; nuclear@0: m4_copy(gl_mat[gl_mmode][mtop], mat); nuclear@0: } nuclear@0: nuclear@0: function glMultMatrixf(mat) nuclear@0: { nuclear@0: var mtop = gl_mat[gl_mmode].length - 1; nuclear@0: m4_mul(gl_mat[gl_mmode][mtop], gl_mat[gl_mmode][mtop], mat); nuclear@0: } nuclear@0: nuclear@0: function glTranslatef(x, y, z) nuclear@0: { nuclear@0: var xform = new Float32Array(16); nuclear@0: m4_translation(xform, x, y, z); nuclear@0: glMultMatrixf(xform); nuclear@0: } nuclear@0: nuclear@0: function glRotatef(angle, x, y, z) nuclear@0: { nuclear@0: var angle_rads = Math.PI * angle / 180.0; nuclear@0: var xform = new Float32Array(16); nuclear@0: m4_rotation(xform, angle_rads, x, y, z); nuclear@0: glMultMatrixf(xform); nuclear@0: } nuclear@0: nuclear@0: function glScalef(x, y, z) nuclear@0: { nuclear@0: var xform = new Float32Array(16); nuclear@0: m4_scale(xform, x, y, z); nuclear@0: glMultMatrixf(xform); nuclear@0: } nuclear@0: nuclear@0: function glOrtho(left, right, bottom, top, near, far) nuclear@0: { nuclear@0: var dx = right - left; nuclear@0: var dy = top - bottom; nuclear@0: var dz = far - near; nuclear@0: nuclear@0: var tx = -(right + left) / dx; nuclear@0: var ty = -(top + bottom) / dy; nuclear@0: var tz = -(far + near) / dz; nuclear@0: nuclear@0: var sx = 2.0 / dx; nuclear@0: var sy = 2.0 / dy; nuclear@0: var sz = -2.0 / dz; nuclear@0: nuclear@0: var xform = new Float32Array(16); nuclear@0: xform[0] = sx; nuclear@0: xform[5] = sy; nuclear@0: xform[10] = sz; nuclear@0: xform[12] = tx; nuclear@0: xform[13] = ty; nuclear@0: xform[14] = tz; nuclear@0: xform[15] = 1.0; nuclear@0: xform[1] = xform[2] = xform[3] = xform[4] = xform[6] = xform[7] = xform[8] = xform[9] = 0.0; nuclear@0: nuclear@0: glMultMatrixf(xform); nuclear@0: } nuclear@0: nuclear@0: function glFrustum(left, right, bottom, top, near, far) nuclear@0: { nuclear@0: var dx = right - left; nuclear@0: var dy = top - bottom; nuclear@0: var dz = far - near; nuclear@0: nuclear@0: var a = (right + left) / dx; nuclear@0: var b = (top + bottom) / dy; nuclear@0: var c = -(far + near) / dz; nuclear@0: var d = -2.0 * far * near / dz; nuclear@0: nuclear@0: var xform = new Float32Array(16); nuclear@0: xform[0] = 2.0 * near / dx; nuclear@0: xform[5] = 2.0 * near / dy; nuclear@0: xform[8] = a; nuclear@0: xform[9] = b; nuclear@0: xform[10] = c; nuclear@0: xform[11] = -1.0; nuclear@0: xform[14] = d; nuclear@0: xform[1] = xform[2] = xform[3] = xform[4] = xform[6] = xform[7] = xform[12] = xform[13] = xform[15] = 0.0; nuclear@0: nuclear@0: glMultMatrixf(xform); nuclear@0: } nuclear@0: nuclear@3: function gluPerspective(vfov, aspect, near, far) nuclear@3: { nuclear@3: var x = near * Math.tan(vfov / 2.0); nuclear@3: glFrustum(-aspect * x, aspect * x, -x, x, near, far); nuclear@3: } nuclear@3: nuclear@0: function gl_apply_xform(prog) nuclear@0: { nuclear@0: var mvtop = gl_mat[GL_MODELVIEW].length - 1; nuclear@0: var ptop = gl_mat[GL_PROJECTION].length - 1; nuclear@0: var ttop = gl_mat[GL_TEXTURE].length - 1; nuclear@0: nuclear@0: var loc = gl.getUniformLocation(prog, "mvmat"); nuclear@0: if(loc != -1) { nuclear@2: gl.uniformMatrix4fv(loc, false, gl_mat[GL_MODELVIEW][mvtop]); nuclear@0: } nuclear@0: nuclear@0: loc = gl.getUniformLocation(prog, "projmat"); nuclear@0: if(loc != -1) { nuclear@2: gl.uniformMatrix4fv(loc, false, gl_mat[GL_PROJECTION][ptop]); nuclear@0: } nuclear@0: nuclear@0: loc = gl.getUniformLocation(prog, "texmat"); nuclear@0: if(loc != -1) { nuclear@2: gl.uniformMatrix4fv(loc, false, gl_mat[GL_TEXTURE][ttop]); nuclear@2: } nuclear@2: nuclear@2: loc = gl.getUniformLocation(prog, "normmat"); nuclear@2: if(loc != -1) { nuclear@2: var normmat = new Float32Array(16); nuclear@2: m4_copy(normmat, gl_mat[GL_MODELVIEW][mvtop]); nuclear@2: normmat[3] = normmat[7] = normmat[11] = normmat[12] = normmat[13] = normmat[14] = 0.0; nuclear@2: normmat[15] = 1.0; nuclear@2: gl.uniformMatrix4fv(loc, false, normmat); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: function glBegin(prim) nuclear@0: { nuclear@0: gl_vertex = new Float32Array(GL_MAX_VERTS * 4); nuclear@0: gl_normal = new Float32Array(GL_MAX_VERTS * 3); nuclear@0: gl_color = new Float32Array(GL_MAX_VERTS * 4); nuclear@0: gl_texcoord = new Float32Array(GL_MAX_VERTS * 4); nuclear@0: nuclear@0: if(gl_vbuf == null) { nuclear@0: gl_vbuf = gl.createBuffer(); nuclear@0: gl.bindBuffer(gl.ARRAY_BUFFER, gl_vbuf); nuclear@0: gl.bufferData(gl.ARRAY_BUFFER, GL_MAX_VERTS * 4 * Float32Array.BYTES_PER_ELEMENT, gl.STREAM_DRAW); nuclear@0: logmsg("vertex buffer size: " + gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE) + "\n"); nuclear@0: nuclear@0: gl_nbuf = gl.createBuffer(); nuclear@0: gl.bindBuffer(gl.ARRAY_BUFFER, gl_nbuf); nuclear@0: gl.bufferData(gl.ARRAY_BUFFER, GL_MAX_VERTS * 3 * Float32Array.BYTES_PER_ELEMENT, gl.STREAM_DRAW); nuclear@0: logmsg("normal buffer size: " + gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE) + "\n"); nuclear@0: nuclear@0: gl_cbuf = gl.createBuffer(); nuclear@0: gl.bindBuffer(gl.ARRAY_BUFFER, gl_cbuf); nuclear@0: gl.bufferData(gl.ARRAY_BUFFER, GL_MAX_VERTS * 4 * Float32Array.BYTES_PER_ELEMENT, gl.STREAM_DRAW); nuclear@0: logmsg("color buffer size: " + gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE) + "\n"); nuclear@0: nuclear@0: gl_tbuf = gl.createBuffer(); nuclear@0: gl.bindBuffer(gl.ARRAY_BUFFER, gl_tbuf); nuclear@0: gl.bufferData(gl.ARRAY_BUFFER, GL_MAX_VERTS * 4 * Float32Array.BYTES_PER_ELEMENT, gl.STREAM_DRAW); nuclear@0: logmsg("texcoord buffer size: " + gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE) + "\n"); nuclear@0: } nuclear@0: nuclear@0: gl_prim = prim; nuclear@0: gl_nverts = gl_vert_calls = 0; nuclear@0: nuclear@0: gl_prog = gl.getParameter(gl.CURRENT_PROGRAM); nuclear@0: if(gl_prog != null) { nuclear@0: gl_apply_xform(gl_prog); nuclear@0: nuclear@0: gl_vloc = gl.getAttribLocation(gl_prog, "attr_vertex"); nuclear@0: gl_nloc = gl.getAttribLocation(gl_prog, "attr_normal"); nuclear@0: gl_cloc = gl.getAttribLocation(gl_prog, "attr_color"); nuclear@0: gl_tloc = gl.getAttribLocation(gl_prog, "attr_texcoord"); nuclear@0: } else { nuclear@0: logmsg("no program currently bound\n"); nuclear@0: gl_vloc = gl_nloc = gl_cloc = gl_tloc = -1; nuclear@0: } nuclear@0: nuclear@0: if(gl_vloc == -1) { nuclear@0: logmsg("attr_vertex not found in currently bound program: " + prog + "\n"); nuclear@0: } nuclear@0: nuclear@0: gl_use_normal = gl_nloc != -1; nuclear@0: gl_use_color = gl_cloc != -1; nuclear@0: gl_use_texcoord = gl_tloc != -1; nuclear@0: } nuclear@0: nuclear@0: function glEnd() nuclear@0: { nuclear@0: if(gl_nverts > 0) { nuclear@0: gl_draw_immediate(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: function gl_draw_immediate() nuclear@0: { nuclear@0: var gles_prim; nuclear@0: nuclear@0: if(gl_vloc == -1) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: switch(gl_prim) { nuclear@0: case GL_POINTS: nuclear@0: gles_prim = gl.POINTS; nuclear@0: break; nuclear@0: case GL_LINES: nuclear@0: gles_prim = gl.LINES; nuclear@0: break; nuclear@0: case GL_TRIANGLES: nuclear@0: case GL_QUADS: nuclear@0: gles_prim = gl.TRIANGLES; nuclear@0: break; nuclear@0: default: nuclear@0: } nuclear@0: nuclear@0: gl.bindBuffer(gl.ARRAY_BUFFER, gl_vbuf); nuclear@0: gl.bufferSubData(gl.ARRAY_BUFFER, 0, gl_vertex); nuclear@0: gl.vertexAttribPointer(gl_vloc, 4, gl.FLOAT, false, 0, 0); nuclear@0: gl.enableVertexAttribArray(gl_vloc); nuclear@0: nuclear@0: if(gl_use_normal) { nuclear@0: gl.bindBuffer(gl.ARRAY_BUFFER, gl_nbuf); nuclear@0: gl.bufferSubData(gl.ARRAY_BUFFER, 0, gl_normal); nuclear@0: gl.vertexAttribPointer(gl_nloc, 3, gl.FLOAT, false, 0, 0); nuclear@0: gl.enableVertexAttribArray(gl_nloc); nuclear@0: } nuclear@0: nuclear@0: if(gl_use_color) { nuclear@0: gl.bindBuffer(gl.ARRAY_BUFFER, gl_cbuf); nuclear@0: gl.bufferSubData(gl.ARRAY_BUFFER, 0, gl_color); nuclear@0: gl.vertexAttribPointer(gl_cloc, 4, gl.FLOAT, true, 0, 0); nuclear@0: gl.enableVertexAttribArray(gl_cloc); nuclear@0: } nuclear@0: nuclear@0: if(gl_use_texcoord) { nuclear@0: gl.bindBuffer(gl.ARRAY_BUFFER, gl_tbuf); nuclear@0: gl.bufferSubData(gl.ARRAY_BUFFER, 0, gl_texcoord); nuclear@0: gl.vertexAttribPointer(gl_tloc, 4, gl.FLOAT, false, 0, 0); nuclear@0: gl.enableVertexAttribArray(gl_tloc); nuclear@0: } nuclear@0: nuclear@0: gl.drawArrays(gles_prim, 0, gl_nverts); nuclear@0: nuclear@0: gl.disableVertexAttribArray(gl_vloc); nuclear@0: if(gl_use_normal) { nuclear@0: gl.disableVertexAttribArray(gl_nloc); nuclear@0: } nuclear@0: if(gl_use_color) { nuclear@0: gl.disableVertexAttribArray(gl_cloc); nuclear@0: } nuclear@0: if(gl_use_texcoord) { nuclear@0: gl.disableVertexAttribArray(gl_tloc); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: function glVertex2f(x, y) nuclear@0: { nuclear@0: glVertex3f(x, y, 0.0); nuclear@0: } nuclear@0: nuclear@0: function glVertex3f(x, y, z) nuclear@0: { nuclear@0: var i = 0; nuclear@0: var vidx = gl_nverts * 4; nuclear@0: var nidx = gl_nverts * 3; nuclear@0: nuclear@0: if(gl_prim == GL_QUADS && gl_vert_calls % 4 == 3) { nuclear@0: var v = vidx - 12; nuclear@0: var n = nidx - 9; nuclear@0: nuclear@0: for(i=0; i<4; i++) { nuclear@0: if(gl_use_color) { nuclear@0: gl_color[vidx] = gl_color[v]; nuclear@0: } nuclear@0: if(gl_use_texcoord) { nuclear@0: gl_texcoord[vidx] = gl_texcoord[v]; nuclear@0: } nuclear@0: gl_vertex[vidx++] = gl_vertex[v++]; nuclear@0: nuclear@0: if(gl_use_normal && i < 3) { nuclear@0: gl_normal[nidx++] = gl_normal[n++]; nuclear@0: } nuclear@0: } nuclear@0: v += 4; nuclear@0: n += 3; nuclear@0: for(i=0; i<4; i++) { nuclear@0: if(gl_use_color) { nuclear@0: gl_color[vidx] = gl_color[v]; nuclear@0: } nuclear@0: if(gl_use_texcoord) { nuclear@0: gl_texcoord[vidx] = gl_texcoord[v]; nuclear@0: } nuclear@0: gl_vertex[vidx++] = gl_vertex[v++]; nuclear@0: nuclear@0: if(gl_use_normal && i < 3) { nuclear@0: gl_normal[nidx++] = gl_normal[n++]; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: gl_nverts += 2; nuclear@0: } nuclear@0: nuclear@0: gl_vertex[vidx] = x; nuclear@0: gl_vertex[vidx + 1] = y; nuclear@0: gl_vertex[vidx + 2] = z; nuclear@0: gl_vertex[vidx + 3] = 1.0; nuclear@0: nuclear@0: if(gl_use_color) { nuclear@0: gl_color[vidx] = gl_curr_color[0]; nuclear@0: gl_color[vidx + 1] = gl_curr_color[1]; nuclear@0: gl_color[vidx + 2] = gl_curr_color[2]; nuclear@0: gl_color[vidx + 3] = gl_curr_color[3]; nuclear@0: } nuclear@0: nuclear@0: if(gl_use_normal) { nuclear@0: gl_normal[nidx] = gl_curr_normal[0]; nuclear@0: gl_normal[nidx + 1] = gl_curr_normal[1]; nuclear@0: gl_normal[nidx + 2] = gl_curr_normal[2]; nuclear@0: } nuclear@0: nuclear@0: if(gl_use_texcoord) { nuclear@0: gl_texcoord[vidx] = gl_curr_texcoord[0]; nuclear@0: gl_texcoord[vidx + 1] = gl_curr_texcoord[1]; nuclear@0: gl_texcoord[vidx + 2] = gl_texcoord[vidx + 3] = 0.0; nuclear@0: } nuclear@0: nuclear@0: gl_vert_calls++; nuclear@3: gl_nverts++; nuclear@3: nuclear@3: var buffer_full; nuclear@3: if(gl_prim == GL_QUADS) { nuclear@3: /* leave space for 6 more worst-case and don't allow flushes mid-quad */ nuclear@3: buffer_full = gl_nverts >= GL_MAX_VERTS - 6 && gl_vert_calls % 4 == 0; nuclear@3: } else { nuclear@3: buffer_full = gl_nverts >= GL_MAX_VERTS - gl_prim; nuclear@3: } nuclear@3: nuclear@3: if(buffer_full) { nuclear@0: gl_draw_immediate(); nuclear@0: glBegin(gl_prim); /* reset everything */ nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: function glNormal3f(x, y, z) nuclear@0: { nuclear@0: gl_curr_normal[0] = x; nuclear@0: gl_curr_normal[1] = y; nuclear@0: gl_curr_normal[3] = z; nuclear@0: } nuclear@0: nuclear@0: function glColor3f(r, g, b) nuclear@0: { nuclear@0: gl_curr_color[0] = r; nuclear@0: gl_curr_color[1] = g; nuclear@0: gl_curr_color[2] = b; nuclear@0: gl_curr_color[3] = 1.0; nuclear@0: } nuclear@0: nuclear@0: function glColor4f(r, g, b, a) nuclear@0: { nuclear@0: gl_curr_color[0] = r; nuclear@0: gl_curr_color[1] = g; nuclear@0: gl_curr_color[2] = b; nuclear@0: gl_curr_color[3] = a; nuclear@0: } nuclear@0: nuclear@0: function glTexCoord1f(s) nuclear@0: { nuclear@0: gl_curr_texcoord[0] = s; nuclear@0: gl_curr_texcoord[1] = 0.0; nuclear@0: } nuclear@0: nuclear@0: function glTexCoord2f(s, t) nuclear@0: { nuclear@0: gl_curr_texcoord[0] = s; nuclear@0: gl_curr_texcoord[1] = t; nuclear@0: }