var mojoHtml5Gl = function(undefined){
'use strict';
document.addEventListener("DOMContentLoaded", function() { init("GameCanvas"); }, false);
var WebGL2D = function WebGL2D(canvas) {
this.api = undefined;
this.canvas = canvas;
this.gl = undefined;
this.width = canvas.width;
this.height = canvas.height;
this.simpleShader = undefined;
this.textureShader = undefined;
this.maxTextureSize = undefined;
canvas.gl2d = this;
var gl = this.gl = canvas.getContext("webgl", {alpha: false}) || canvas.getContext("experimental-webgl", {alpha: false});
if (gl === undefined || gl === null) return;
try {
this.simpleShader = this.loadShaders(false);
this.textureShader = this.loadShaders(true);
} catch (e) { throw e; }
this.api = new WebGL2DAPI(this);
canvas.getContext = this.api;
canvas.getContext = (function(api, gl) {
return function(context) {
if( context === "webgl" || context === "experimental-webgl"){
return gl;
}
return api;
};
}(this.api, this.gl));
};
WebGL2D.prototype.getFragmentShaderSource = function getFragmentShaderSource(textured) {
var fsSource = [];
fsSource.push(
"precision mediump float;",
"varying vec4 vColor;"
);
if (textured) {
fsSource.push(
"varying vec2 vTextureCoord;",
"uniform sampler2D uSampler;"
);
}
fsSource.push("void main(void) {");
if (textured) {
fsSource.push("gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;");
} else {
fsSource.push("gl_FragColor = vColor;");
}
fsSource.push("}");
return fsSource.join("\n");
};
WebGL2D.prototype.getVertexShaderSource = function getVertexShaderSource(textured) {
var w = 2 / this.canvas.width, h = -2 / this.canvas.height;
var vsSource = [];
vsSource.push(
"attribute vec4 aVertexPosition;",
"attribute vec4 aVertexColor;",
"varying vec4 vColor;",
"const mat4 pMatrix = mat4(" + w + ",0,0,0, 0," + h + ",0,0, 0,0,1.0,1.0, -1.0,1.0,0,0);"
);
if (textured) {
vsSource.push("varying vec2 vTextureCoord;");
}
vsSource.push(
"void main(void) {",
"vec3 position = vec3(aVertexPosition.x, aVertexPosition.y, 1.0);",
"gl_Position = pMatrix * vec4(position, 1.0);",
"vColor = aVertexColor;"
);
if (textured) {
vsSource.push("vTextureCoord = aVertexPosition.zw;");
}
vsSource.push("}");
return vsSource.join("\n");
};
WebGL2D.prototype.loadShaders = function loadShaders(textured) {
var gl = this.gl;
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, this.getFragmentShaderSource(textured));
gl.compileShader(fs);
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
throw "fragment shader error: " + gl.getShaderInfoLog(fs);
}
var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, this.getVertexShaderSource(textured));
gl.compileShader(vs);
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
throw "vertex shader error: " + gl.getShaderInfoLog(vs);
}
var shaderProgram = this.shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, fs);
gl.attachShader(shaderProgram, vs);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
throw "Could not initialise shaders.";
}
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
shaderProgram.uColor = gl.getUniformLocation(shaderProgram, 'uColor');
shaderProgram.uSampler = gl.getUniformLocation(shaderProgram, 'uSampler');
return shaderProgram;
};
var WebGL2DAPI = function WebGL2DAPI(gl2d) {
if (CFG_CONFIG === "debug") {
print("WebGL enabled");
}
var gl = gl2d.gl;
gl2d.width = -1;
gl2d.height = -1;
gl2d.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
if (!isPOT(gl2d.maxTextureSize)) gl2d.maxTextureSize += 1;
var MAX_VERTICES = 2048;
var MAX_RENDERS = (MAX_VERTICES / 2)|0;
var red = 1.0, green = 1.0, blue = 1.0;
var alpha = 1.0, blend = 0;
var MODE_NONE = 0, MODE_TEXTURED = 1;
var mode = MODE_NONE;
var gxtk = null;
var simpleShader = gl2d.simpleShader;
var textureShader = gl2d.textureShader;
var buffer = {
vdata: new Float32Array(MAX_VERTICES * 4),
cdata: new Float32Array(MAX_VERTICES * 4),
vcount: 0,
vpointer: 0,
cpointer: 0,
vbuffer: gl.createBuffer(),
cbuffer: gl.createBuffer()
};
var render = {
last: new renderOp(-1, 0, null),
next: 0
}
var rendersPool = new Array(MAX_RENDERS);
for (var i = 0; i < rendersPool.length; i++) {
rendersPool[i] = new renderOp(-1, 0, null);
}
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gxtkGraphics.prototype.LoadSurface=function( path ){
var game = this.game;
var ty = game.GetMetaData( path,"type" );
if (ty.indexOf("image/") != 0) return null;
game.IncLoading();
var image = new Image();
image.onload = function() {
bindTexture(this);
game.DecLoading();
};
image.meta_width = parseInt(game.GetMetaData(path, "width"));
image.meta_height = parseInt(game.GetMetaData(path, "height"));
image.src = game.PathToUrl(path);
return new gxtkSurface(image, this);
}
BBAsyncImageLoaderThread.prototype.Start = function(){
var thread = this;
var image = new Image();
image.crossOrigin = '';
image.onload = function(e) {
image.meta_width = image.width;
image.meta_height = image.height;
thread._surface = new gxtkSurface(image, thread._device);
bindTexture(this);
thread.running = false;
}
image.onerror = function(e) {
thread._surface = null;
thread.running = false;
}
thread.running = true;
image.src = BBGame.Game().PathToUrl(thread._path);
}
gxtkSurface.prototype.Discard = function(){
if (this.image){
gl.deleteTexture(this.image.texture);
this.image = null;
}
}
gxtkGraphics.prototype.CreateSurface=function( width,height ){
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
canvas.meta_width = width;
canvas.meta_height = height;
bindTexture(canvas);
canvas.complete = true;
var surface = new gxtkSurface(canvas, this);
surface.gc = canvas.getContext("2d");
return surface;
}
gxtkGraphics.prototype.BeginRender = function() {
if (!this.gc) return 0;
gxtk = this;
if (this.game.GetLoading()) return 2;
if (gl2d.width !== gl2d.canvas.width || gl2d.height !== gl2d.canvas.height) {
simpleShader = gl2d.simpleShader = gl2d.loadShaders(false);
textureShader = gl2d.textureShader = gl2d.loadShaders(true);
this.width = gl2d.width = gl2d.canvas.width;
this.height = gl2d.height = gl2d.canvas.height;
gl.viewport(0, 0, this.width, this.height);
}
return 1;
}
gxtkGraphics.prototype.EndRender = function(){
renderPull();
}
gxtkGraphics.prototype.SetAlpha = function(a){
alpha = a;
}
gxtkGraphics.prototype.SetColor = function(r, g, b){
red = r / 255.0; green = g / 255.0; blue = b / 255.0;
}
gxtkGraphics.prototype.SetBlend = function(b){
if (blend === b) return;
renderPull();
switch (b) {
case 1:
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
break;
default:
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
}
blend = b;
}
gxtkGraphics.prototype.SetScissor = function(x,y,w,h) {
renderPull();
if (x !== 0 || y !== 0 || w !== this.width || h !== this.height) {
gl.enable(gl.SCISSOR_TEST);
y = this.height - y - h;
gl.scissor(x, y, w, h);
} else {
gl.disable(gl.SCISSOR_TEST);
}
}
gxtkGraphics.prototype.SetMatrix = function(ix, iy, jx, jy, tx, ty) {
this.ix = ix; this.iy = iy;
this.jx = jx; this.jy = jy;
this.tx = tx; this.ty = ty;
this.tformed = (ix !== 1 || iy !== 0 || jx !== 0 || jy !== 1 || tx !== 0 || ty !== 0);
}
gxtkGraphics.prototype.Cls = function(r, g, b) {
gl.clearColor(r / 255, g / 255, b / 255, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
gxtkGraphics.prototype.DrawPoint = function(x, y){
if (mode !== MODE_NONE) {
renderPull();
mode = MODE_NONE;
}
renderPush(gl.POINTS, 1);
if (this.tformed) {
var px = x;
x = px * this.ix + y * this.jx + this.tx;
y = px * this.iy + y * this.jy + this.ty;
}
var p = buffer.vpointer;
buffer.vdata[p] = x;
buffer.vdata[p + 1] = y;
}
gxtkGraphics.prototype.DrawRect = function(x, y, w, h){
if (mode !== MODE_NONE) {
renderPull();
mode = MODE_NONE;
}
renderPushRect(x, y, w, h);
}
gxtkGraphics.prototype.DrawLine = function(x1, y1, x2, y2) {
if (mode !== MODE_NONE) {
renderPull();
mode = MODE_NONE;
}
renderPush(gl.LINES, 2);
if (this.tformed) {
var tx0 = x1, tx1 = x2;
x1 = tx0 * this.ix + y1 * this.jx + this.tx;
y1 = tx0 * this.iy + y1 * this.jy + this.ty;
x2 = tx1 * this.ix + y2 * this.jx + this.tx;
y2 = tx1 * this.iy + y2 * this.jy + this.ty;
}
var p = buffer.vpointer;
buffer.vdata[p] = x1;
buffer.vdata[p + 1] = y1;
buffer.vdata[p + 2] = 0;
buffer.vdata[p + 3] = 0;
buffer.vdata[p + 4] = x2;
buffer.vdata[p + 5] = y2;
buffer.vdata[p + 6] = 0;
buffer.vdata[p + 7] = 0;
}
gxtkGraphics.prototype.DrawOval = function(x, y, w, h) {
if (mode !== MODE_NONE) {
renderPull();
mode = MODE_NONE;
}
var xr = w / 2.0;
var yr = h / 2.0;
var segs;
if (this.tformed) {
var xx = xr * this.ix, xy = xr * this.iy, xd = Math.sqrt(xx * xx + xy * xy);
var yx = yr * this.jx, yy = yr * this.jy, yd = Math.sqrt(yx * yx + yy * yy);
segs= (xd + yd)|0;
}else{
segs = (Math.abs(xr) + Math.abs(yr))|0;
}
if (segs > MAX_VERTICES) {
segs = MAX_VERTICES;
} else if (segs < 12) {
segs=12;
} else {
segs &=~ 3;
}
x += xr;
y += yr;
renderPush(gl.TRIANGLE_FAN, segs);
var p = buffer.vpointer;
for (var i=0; i < segs; i++) {
var th = i * 6.28318531 / segs;
var x0 = (x + Math.cos(th) * xr);
var y0 = (y + Math.sin(th) * yr);
if (this.tformed){
var tx0 = x0;
x0 = tx0 * this.ix + y0 * this.jx + this.tx;
y0 = tx0 * this.iy + y0 * this.jy + this.ty;
}
buffer.vdata[p] = x0;
buffer.vdata[p + 1] = y0;
p += 4;
}
}
gxtkGraphics.prototype.DrawPoly = function(verts) {
if (mode !== MODE_NONE) {
renderPull();
mode = MODE_NONE;
}
if (verts.length < 6 || verts.length > MAX_VERTICES * 2) return;
renderPush(gl.TRIANGLE_FAN, verts.length / 2);
var p = buffer.vpointer;
if (this.tformed) {
for (var i = 0; i < verts.length; i += 2) {
buffer.vdata[p] = verts[i] * this.ix + verts[i + 1] * this.jx + this.tx;
buffer.vdata[p + 1] = verts[i] * this.iy + verts[i + 1] * this.jy + this.ty;
p += 4;
}
} else {
for (var i = 0; i < verts.length; i += 2) {
buffer.vdata[p] = verts[i];
buffer.vdata[p + 1] = verts[i + 1];
p += 4;
}
}
}
gxtkGraphics.prototype.DrawPoly2 = function(verts, surface, srcx, srcy) {
if (!surface.image.complete) return;
if (mode !== MODE_TEXTURED) {
renderPull();
mode = MODE_TEXTURED;
}
var vertexCount = verts.length / 4;
if (vertexCount < 3 || vertexCount > MAX_VERTICES ) return;
renderPush(gl.TRIANGLE_FAN, vertexCount);
var p = buffer.vpointer;
for (var i = 0; i < vertexCount; i++) {
var index = i*4;
var px = verts[index];
var py = verts[index+1];
if (this.tformed) {
var ppx = px;
px = ppx * this.ix + py * this.jx + this.tx;
py = ppx * this.iy + py * this.jy + this.ty;
}
buffer.vdata[p] = px;
buffer.vdata[p+1] = py;
buffer.vdata[p+2] = (srcx + verts[index+2]) / surface.image.meta_width;
buffer.vdata[p+3] = (srcy + verts[index+3]) / surface.image.meta_height;
p += 4;
}
render.last.texture = surface.image.texture;
}
gxtkGraphics.prototype.DrawSurface = function(surface, x, y) {
if (!surface.image.complete) return;
if (mode !== MODE_TEXTURED) {
renderPull();
mode = MODE_TEXTURED;
}
renderPushRect(x, y, surface.swidth, surface.sheight);
render.last.texture = surface.image.texture;
}
gxtkGraphics.prototype.DrawSurface2 = function(surface, x, y, srcx, srcy, srcw, srch) {
if (!surface.image.complete) return;
if (mode !== MODE_TEXTURED) {
renderPull();
mode = MODE_TEXTURED;
}
renderPushRect(x, y, srcw, srch, srcx / surface.image.meta_width,
srcy / surface.image.meta_height, (srcx + srcw) / surface.image.meta_width, (srcy + srch) / surface.image.meta_height);
render.last.texture = surface.image.texture;
}
gxtkGraphics.prototype.ReadPixels = function(pixels, x, y, width, height, offset, pitch ) {
renderPull();
var data = new Uint8Array(width * height * 4);
gl.readPixels(x, this.height - y - height, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
var i = 0;
for(var py = height-1; py >= 0; --py) {
var j = offset + py * pitch;
for(var px = 0; px < width; ++px) {
pixels[j++] = (data[i+3]<<24) | (data[i]<<16) | (data[i+1]<<8) | data[i+2];
i+=4;
}
}
}
gxtkGraphics.prototype.WritePixels2 = function(surface, pixels, x, y, width, height, offset, pitch){
var imgData = surface.gc.createImageData(width, height);
var p = imgData.data, i = 0, j = offset,px,py,argb;
for(py = 0; py < height; ++py){
for(px = 0; px < width; ++px){
argb = pixels[j++];
p[i] = (argb>>16) & 0xff;
p[i + 1] = (argb>>8) & 0xff;
p[i + 2] = argb & 0xff;
p[i + 3] = (argb>>24) & 0xff;
i += 4;
}
j += pitch - width;
}
surface.gc.putImageData(imgData, x, y);
gl.deleteTexture(surface.image.texture);
surface.image.texture = null;
bindTexture(surface.image);
}
function renderPush(type, count) {
if (buffer.vcount + count > MAX_VERTICES || render.next === MAX_RENDERS) {
renderPull();
}
render.last = rendersPool[render.next];
render.next += 1;
render.last.type = type;
render.last.count = count;
render.last.texture = null;
buffer.vpointer = buffer.vcount * 4;
buffer.cpointer = buffer.vcount * 4;
buffer.vcount += count;
var p = buffer.cpointer;
for (var i = 0; i < count; i++) {
buffer.cdata[p] = red;
buffer.cdata[p + 1] = green;
buffer.cdata[p + 2] = blue;
buffer.cdata[p + 3] = alpha;
p += 4;
}
}
function renderPull() {
if (buffer.vcount === 0) return;
var cTexture = null;
var index = 0;
var r;
switch (mode) {
case MODE_NONE:
gl.useProgram(simpleShader);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.vbuffer);
gl.bufferData(gl.ARRAY_BUFFER, buffer.vdata, gl.DYNAMIC_DRAW);
gl.vertexAttribPointer(simpleShader.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.cbuffer);
gl.bufferData(gl.ARRAY_BUFFER, buffer.cdata, gl.DYNAMIC_DRAW);
gl.vertexAttribPointer(simpleShader.vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
for (var i = 0; i < render.next; i++) {
r = rendersPool[i];
gl.drawArrays(r.type, index, r.count);
index += r.count;
}
break;
case MODE_TEXTURED:
gl.useProgram(textureShader);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.vbuffer);
gl.bufferData(gl.ARRAY_BUFFER, buffer.vdata, gl.DYNAMIC_DRAW);
gl.vertexAttribPointer(textureShader.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.cbuffer);
gl.bufferData(gl.ARRAY_BUFFER, buffer.cdata, gl.DYNAMIC_DRAW);
gl.vertexAttribPointer(textureShader.vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
for (var i = 0; i < render.next; i++) {
r = rendersPool[i];
if (cTexture !== r.texture) {
gl.bindTexture(gl.TEXTURE_2D, r.texture);
gl.activeTexture(gl.TEXTURE0);
gl.uniform1i(textureShader.uSampler, 0);
cTexture = r.texture;
}
gl.drawArrays(r.type, index, r.count);
index += r.count;
}
break;
}
renderReset();
}
function renderReset() {
buffer.vcount = 0;
render.next = 0;
}
function renderPushRect(x, y, w, h, u0, v0, u1, v1) {
renderPush(gl.TRIANGLE_FAN, 4);
var x0 = x, x1 = x + w, x2 = x + w, x3 = x;
var y0 = y, y1 = y, y2 = y + h, y3 = y + h;
if (gxtk.tformed) {
var tx0 = x0, tx1 = x1, tx2 = x2, tx3 = x3;
x0 = tx0 * gxtk.ix + y0 * gxtk.jx + gxtk.tx;
y0 = tx0 * gxtk.iy + y0 * gxtk.jy + gxtk.ty;
x1 = tx1 * gxtk.ix + y1 * gxtk.jx + gxtk.tx;
y1 = tx1 * gxtk.iy + y1 * gxtk.jy + gxtk.ty;
x2 = tx2 * gxtk.ix + y2 * gxtk.jx + gxtk.tx;
y2 = tx2 * gxtk.iy + y2 * gxtk.jy + gxtk.ty;
x3 = tx3 * gxtk.ix + y3 * gxtk.jx + gxtk.tx;
y3 = tx3 * gxtk.iy + y3 * gxtk.jy + gxtk.ty;
}
var p = buffer.vpointer;
if (u0 === undefined) {
buffer.vdata[p] = x0;
buffer.vdata[p + 1] = y0;
buffer.vdata[p + 2] = 0;
buffer.vdata[p + 3] = 0;
buffer.vdata[p + 4] = x1;
buffer.vdata[p + 5] = y1;
buffer.vdata[p + 6] = 1;
buffer.vdata[p + 7] = 0;
buffer.vdata[p + 8] = x2;
buffer.vdata[p + 9] = y2;
buffer.vdata[p + 10] = 1;
buffer.vdata[p + 11] = 1;
buffer.vdata[p + 12] = x3;
buffer.vdata[p + 13] = y3;
buffer.vdata[p + 14] = 0;
buffer.vdata[p + 15] = 1;
} else {
buffer.vdata[p] = x0;
buffer.vdata[p + 1] = y0;
buffer.vdata[p + 2] = u0;
buffer.vdata[p + 3] = v0;
buffer.vdata[p + 4] = x1;
buffer.vdata[p + 5] = y1;
buffer.vdata[p + 6] = u1;
buffer.vdata[p + 7] = v0;
buffer.vdata[p + 8] = x2;
buffer.vdata[p + 9] = y2;
buffer.vdata[p + 10] = u1;
buffer.vdata[p + 11] = v1;
buffer.vdata[p + 12] = x3;
buffer.vdata[p + 13] = y3;
buffer.vdata[p + 14] = u0;
buffer.vdata[p + 15] = v1;
}
}
function bindTexture(image) {
if (image.texture !== undefined && image.texture !== null) return;
image.texture = gl.createTexture();
var mojoFilteringEnabled = (typeof(CFG_MOJO_IMAGE_FILTERING_ENABLED) === "undefined" || CFG_MOJO_IMAGE_FILTERING_ENABLED === "true" || CFG_MOJO_IMAGE_FILTERING_ENABLED === "1");
if (image.width > gl2d.maxTextureSize || image.height > gl2d.maxTextureSize) {
var canvas = document.createElement("canvas");
canvas.width = (image.width > gl2d.maxTextureSize) ? gl2d.maxTextureSize : image.width;
canvas.height = (image.height > gl2d.maxTextureSize) ? gl2d.maxTextureSize : image.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
image = canvas;
}
gl.bindTexture(gl.TEXTURE_2D, image.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
if (mojoFilteringEnabled) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
} else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
}
if (isPOT(image.width) && isPOT(image.height)) {
if (mojoFilteringEnabled) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.generateMipmap(gl.TEXTURE_2D);
} else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST);
gl.generateMipmap(gl.TEXTURE_2D);
}
} else {
if (mojoFilteringEnabled) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
} else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
}
}
}
function isPOT(value) {
return value > 0 && ((value - 1) & value) === 0;
}
function renderOp(type, count, texture) {
this.type = type;
this.count = count;
this.texture = texture;
}
}
function init(id) {
if (window.WebGLRenderingContext !== undefined) {
try {
new WebGL2D(document.getElementById(id));
} catch (e) { }
}
}
};
mojoHtml5Gl();