spine-runtimes/spine-js/turbulenz/graphicsdevice.js
2013-05-07 01:13:18 +02:00

6209 lines
181 KiB
JavaScript

// Copyright (c) 2011-2012 Turbulenz Limited
/*global TurbulenzEngine*/
/*global TGALoader*/
/*global DDSLoader*/
/*global TARLoader*/
/*global Int8Array*/
/*global Int16Array*/
/*global Int32Array*/
/*global Uint8Array*/
/*global Uint8ClampedArray*/
/*global Uint16Array*/
/*global Uint32Array*/
/*global Float32Array*/
/*global ArrayBuffer*/
/*global DataView*/
/*global window*/
"use strict";
//
// WebGLTexture
//
function WebGLTexture() {}
WebGLTexture.prototype =
{
version : 1,
setData : function textureSetDataFn(data)
{
var gd = this.gd;
var target = this.target;
gd.bindTexture(target, this.glTexture);
this.updateData(data);
gd.bindTexture(target, null);
},
// Internal
createGLTexture : function createGLTextureFn(data)
{
var gd = this.gd;
var gl = gd.gl;
var target;
if (this.cubemap)
{
target = gl.TEXTURE_CUBE_MAP;
}
else if (this.depth > 1)
{
//target = gl.TEXTURE_3D;
// 3D textures are not supported yet
return false;
}
else
{
target = gl.TEXTURE_2D;
}
this.target = target;
var gltex = gl.createTexture();
this.glTexture = gltex;
gd.bindTexture(target, gltex);
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
if (this.mipmaps || 1 < this.numDataLevels)
{
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
}
else
{
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
this.updateData(data);
gd.bindTexture(target, null);
return true;
},
updateData : function updateDataFn(data)
{
var gd = this.gd;
var gl = gd.gl;
function log2(a)
{
return Math.floor(Math.log(a) / Math.log(2));
}
var generateMipMaps = this.mipmaps && (this.numDataLevels !== (1 + Math.max(log2(this.width), log2(this.height))));
var format = this.format;
var internalFormat, gltype, srcStep, bufferData = null;
var compressedTexturesExtension;
if (format === gd.PIXELFORMAT_A8)
{
internalFormat = gl.ALPHA;
gltype = gl.UNSIGNED_BYTE;
srcStep = 1;
if (data && !data.src)
{
if (data instanceof Uint8Array)
{
bufferData = data;
}
else
{
bufferData = new Uint8Array(data);
}
}
}
else if (format === gd.PIXELFORMAT_L8)
{
internalFormat = gl.LUMINANCE;
gltype = gl.UNSIGNED_BYTE;
srcStep = 1;
if (data && !data.src)
{
if (data instanceof Uint8Array)
{
bufferData = data;
}
else
{
bufferData = new Uint8Array(data);
}
}
}
else if (format === gd.PIXELFORMAT_L8A8)
{
internalFormat = gl.LUMINANCE_ALPHA;
gltype = gl.UNSIGNED_BYTE;
srcStep = 2;
if (data && !data.src)
{
if (data instanceof Uint8Array)
{
bufferData = data;
}
else
{
bufferData = new Uint8Array(data);
}
}
}
else if (format === gd.PIXELFORMAT_R5G5B5A1)
{
internalFormat = gl.RGBA;
gltype = gl.UNSIGNED_SHORT_5_5_5_1;
srcStep = 1;
if (data && !data.src)
{
if (data instanceof Uint16Array)
{
bufferData = data;
}
else
{
bufferData = new Uint16Array(data);
}
}
}
else if (format === gd.PIXELFORMAT_R5G6B5)
{
internalFormat = gl.RGB;
gltype = gl.UNSIGNED_SHORT_5_6_5;
srcStep = 1;
if (data && !data.src)
{
if (data instanceof Uint16Array)
{
bufferData = data;
}
else
{
bufferData = new Uint16Array(data);
}
}
}
else if (format === gd.PIXELFORMAT_R8G8B8A8)
{
internalFormat = gl.RGBA;
gltype = gl.UNSIGNED_BYTE;
srcStep = 4;
if (data && !data.src)
{
if (data instanceof Uint8Array)
{
// Some browsers consider Uint8ClampedArray to be
// an instance of Uint8Array (which is correct as
// per the spec), yet won't accept a
// Uint8ClampedArray as pixel data for a
// gl.UNSIGNED_BYTE Texture. If we have a
// Uint8ClampedArray then we can just reuse the
// underlying data.
if (typeof Uint8ClampedArray !== "undefined" &&
data instanceof Uint8ClampedArray)
{
bufferData = new Uint8Array(data.buffer);
}
else
{
bufferData = data;
}
}
else
{
bufferData = new Uint8Array(data);
}
}
}
else if (format === gd.PIXELFORMAT_R8G8B8)
{
internalFormat = gl.RGB;
gltype = gl.UNSIGNED_BYTE;
srcStep = 3;
if (data && !data.src)
{
if (data instanceof Uint8Array)
{
// See comment above about Uint8ClampedArray
if (typeof Uint8ClampedArray !== "undefined" &&
data instanceof Uint8ClampedArray)
{
bufferData = new Uint8Array(data.buffer);
}
else
{
bufferData = data;
}
}
else
{
bufferData = new Uint8Array(data);
}
}
}
else if (format === gd.PIXELFORMAT_D24S8)
{
//internalFormat = gl.DEPTH24_STENCIL8_EXT;
//gltype = gl.UNSIGNED_INT_24_8_EXT;
//internalFormat = gl.DEPTH_COMPONENT;
internalFormat = gl.DEPTH_STENCIL;
gltype = gl.UNSIGNED_INT;
srcStep = 1;
if (data && !data.src)
{
bufferData = new Uint32Array(data);
}
}
else if (format === gd.PIXELFORMAT_DXT1 ||
format === gd.PIXELFORMAT_DXT3 ||
format === gd.PIXELFORMAT_DXT5)
{
compressedTexturesExtension = gd.compressedTexturesExtension;
if (compressedTexturesExtension)
{
if (format === gd.PIXELFORMAT_DXT1)
{
internalFormat = compressedTexturesExtension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
srcStep = 8;
}
else if (format === gd.PIXELFORMAT_DXT3)
{
internalFormat = compressedTexturesExtension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
srcStep = 16;
}
else //if (format === gd.PIXELFORMAT_DXT5)
{
internalFormat = compressedTexturesExtension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
srcStep = 16;
}
if (internalFormat === undefined)
{
return; // Unsupported format
}
if (data && !data.src)
{
if (data instanceof Uint8Array)
{
bufferData = data;
}
else
{
bufferData = new Uint8Array(data);
}
}
}
else
{
return; // Unsupported format
}
}
else
{
return; //unknown/unsupported format
}
var numLevels = (data && 0 < this.numDataLevels ? this.numDataLevels : 1);
var w = this.width, h = this.height, offset = 0, target, n, levelSize, levelData;
if (this.cubemap)
{
target = gl.TEXTURE_CUBE_MAP;
gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
for (var f = 0; f < 6; f += 1)
{
var faceTarget = (gl.TEXTURE_CUBE_MAP_POSITIVE_X + f);
for (n = 0; n < numLevels; n += 1)
{
if (compressedTexturesExtension)
{
levelSize = (Math.floor((w + 3) / 4) * Math.floor((h + 3) / 4) * srcStep);
if (bufferData)
{
if (numLevels === 1)
{
levelData = bufferData;
}
else
{
levelData = bufferData.subarray(offset, (offset + levelSize));
}
}
else
{
levelData = new Uint8Array(levelSize);
}
if (gd.WEBGL_compressed_texture_s3tc)
{
gl.compressedTexImage2D(faceTarget, n, internalFormat, w, h, 0,
levelData);
}
else
{
compressedTexturesExtension.compressedTexImage2D(faceTarget, n, internalFormat, w, h, 0,
levelData);
}
}
else
{
levelSize = (w * h * srcStep);
if (bufferData)
{
if (numLevels === 1)
{
levelData = bufferData;
}
else
{
levelData = bufferData.subarray(offset, (offset + levelSize));
}
gl.texImage2D(faceTarget, n, internalFormat, w, h, 0, internalFormat, gltype, levelData);
}
else if (data)
{
gl.texImage2D(faceTarget, n, internalFormat, internalFormat, gltype, data);
}
else
{
gl.texImage2D(faceTarget, n, internalFormat, w, h, 0, internalFormat, gltype,
new Uint8Array(levelSize));
}
}
offset += levelSize;
w = (w > 1 ? Math.floor(w / 2) : 1);
h = (h > 1 ? Math.floor(h / 2) : 1);
}
w = this.width;
h = this.height;
}
}
else
{
target = gl.TEXTURE_2D;
gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
for (n = 0; n < numLevels; n += 1)
{
if (compressedTexturesExtension)
{
levelSize = (Math.floor((w + 3) / 4) * Math.floor((h + 3) / 4) * srcStep);
if (bufferData)
{
if (numLevels === 1)
{
levelData = bufferData;
}
else
{
levelData = bufferData.subarray(offset, (offset + levelSize));
}
}
else
{
levelData = new Uint8Array(levelSize);
}
if (gd.WEBGL_compressed_texture_s3tc)
{
gl.compressedTexImage2D(target, n, internalFormat, w, h, 0, levelData);
}
else
{
compressedTexturesExtension.compressedTexImage2D(target, n, internalFormat, w, h, 0, levelData);
}
}
else
{
levelSize = (w * h * srcStep);
if (bufferData)
{
if (numLevels === 1)
{
levelData = bufferData;
}
else
{
levelData = bufferData.subarray(offset, (offset + levelSize));
}
gl.texImage2D(target, n, internalFormat, w, h, 0, internalFormat, gltype, levelData);
}
else if (data)
{
gl.texImage2D(target, n, internalFormat, internalFormat, gltype, data);
}
else
{
gl.texImage2D(target, n, internalFormat, w, h, 0, internalFormat, gltype,
new Uint8Array(levelSize));
}
}
offset += levelSize;
w = (w > 1 ? Math.floor(w / 2) : 1);
h = (h > 1 ? Math.floor(h / 2) : 1);
}
}
if (generateMipMaps)
{
gl.generateMipmap(target);
}
},
updateMipmaps : function updateMipmapsFn(face)
{
if (this.mipmaps)
{
if (this.depth > 1)
{
TurbulenzEngine.callOnError(
"3D texture mipmap generation unsupported");
return;
}
if (this.cubemap && face !== 5)
{
return;
}
var gd = this.gd;
var gl = gd.gl;
var target = this.target;
gd.bindTexture(target, this.glTexture);
gl.generateMipmap(target);
gd.bindTexture(target, null);
}
},
destroy : function textureDestroyFn()
{
var gd = this.gd;
if (gd)
{
var glTexture = this.glTexture;
if (glTexture)
{
var gl = gd.gl;
if (gl)
{
gd.unbindTexture(glTexture);
gl.deleteTexture(glTexture);
}
delete this.glTexture;
}
delete this.sampler;
delete this.gd;
}
},
typedArrayIsValid : function textureTypedArrayIsValidFn(typedArray)
{
var gd = this.gd;
var format = this.format;
if (gd)
{
if ((format === gd.PIXELFORMAT_A8) ||
(format === gd.PIXELFORMAT_L8) ||
(format === gd.PIXELFORMAT_S8))
{
return ((typedArray instanceof Uint8Array) ||
(typeof Uint8ClampedArray !== "undefined" &&
typedArray instanceof Uint8ClampedArray)) &&
(typedArray.length ===
this.width * this.height * this.depth);
}
if (format === gd.PIXELFORMAT_L8A8)
{
return ((typedArray instanceof Uint8Array) ||
(typeof Uint8ClampedArray !== "undefined" &&
typedArray instanceof Uint8ClampedArray)) &&
(typedArray.length ===
2 * this.width * this.height * this.depth);
}
if (format === gd.PIXELFORMAT_R8G8B8)
{
return ((typedArray instanceof Uint8Array) ||
(typeof Uint8ClampedArray !== "undefined" &&
typedArray instanceof Uint8ClampedArray)) &&
(typedArray.length ===
3 * this.width * this.height * this.depth);
}
if (format === gd.PIXELFORMAT_R8G8B8A8)
{
return ((typedArray instanceof Uint8Array) ||
(typeof Uint8ClampedArray !== "undefined" &&
typedArray instanceof Uint8ClampedArray)) &&
(typedArray.length ===
4 * this.width * this.height * this.depth);
}
if ((format === gd.PIXELFORMAT_R5G5B5A1) ||
(format === gd.PIXELFORMAT_R5G6B5))
{
return (typedArray instanceof Uint16Array) &&
(typedArray.length ===
this.width * this.height * this.depth);
}
}
return false;
}
};
// Constructor function
WebGLTexture.create = function webGLTextureCreateFn(gd, params)
{
var tex = new WebGLTexture();
tex.gd = gd;
tex.mipmaps = params.mipmaps;
tex.dynamic = params.dynamic;
tex.renderable = params.renderable;
tex.numDataLevels = 0;
var src = params.src;
if (src)
{
tex.name = params.name || src;
var extension;
var data = params.data;
if (data)
{
// do not trust file extensions if we got data...
if (data[0] === 137 &&
data[1] === 80 &&
data[2] === 78 &&
data[3] === 71)
{
extension = '.png';
}
else if (data[0] === 255 &&
data[1] === 216 &&
data[2] === 255 &&
(data[3] === 224 || data[3] === 225))
{
extension = '.jpg';
}
else if (data[0] === 68 &&
data[1] === 68 &&
data[2] === 83 &&
data[3] === 32)
{
extension = '.dds';
}
else
{
extension = src.slice(-4);
}
}
else
{
extension = src.slice(-4);
}
// DDS and TGA textures require out own image loaders
if (extension === '.dds' ||
extension === '.tga')
{
if (extension === '.tga' && typeof TGALoader !== 'undefined')
{
var tgaParams = {
gd: gd,
onload : function tgaLoadedFn(data, width, height, format, status)
{
tex.width = width;
tex.height = height;
tex.depth = 1;
tex.format = format;
tex.cubemap = false;
var result = tex.createGLTexture(data);
if (params.onload)
{
params.onload(result ? tex : null, status);
}
},
onerror : function tgaFailedFn()
{
tex.failed = true;
if (params.onload)
{
params.onload(null);
}
}
};
if (data)
{
tgaParams.data = data;
}
else
{
tgaParams.src = src;
}
TGALoader.create(tgaParams);
return tex;
}
else if (extension === '.dds' && typeof DDSLoader !== 'undefined')
{
var ddsParams = {
gd: gd,
onload : function ddsLoadedFn(data, width, height, format, numLevels, cubemap, depth, status)
{
tex.width = width;
tex.height = height;
tex.format = format;
tex.cubemap = cubemap;
tex.depth = depth;
tex.numDataLevels = numLevels;
var result = tex.createGLTexture(data);
if (params.onload)
{
params.onload(result ? tex : null, status);
}
},
onerror : function ddsFailedFn()
{
tex.failed = true;
if (params.onload)
{
params.onload(null);
}
}
};
if (data)
{
ddsParams.data = data;
}
else
{
ddsParams.src = src;
}
DDSLoader.create(ddsParams);
return tex;
}
else
{
TurbulenzEngine.callOnError(
'Missing image loader required for ' + src);
tex = webGLTextureCreateFn(gd, {
name : (params.name || src),
width : 2,
height : 2,
depth : 1,
format : 'R8G8B8A8',
cubemap : false,
mipmaps : params.mipmaps,
dynamic : params.dynamic,
renderable : params.renderable,
data : [255, 20, 147, 255,
255, 0, 0, 255,
255, 255, 255, 255,
255, 20, 147, 255]
});
if (params.onload)
{
if (TurbulenzEngine)
{
TurbulenzEngine.setTimeout(function () {
params.onload(tex, 200);
}, 0);
}
else
{
window.setTimeout(function () {
params.onload(tex, 200);
}, 0);
}
}
return tex;
}
}
var img = new Image();
img.onload = function imageLoadedFn()
{
tex.width = img.width;
tex.height = img.height;
tex.depth = 1;
tex.format = gd.PIXELFORMAT_R8G8B8A8;
tex.cubemap = false;
var result = tex.createGLTexture(img);
if (params.onload)
{
params.onload(result ? tex : null, 200);
}
};
img.onerror = function imageFailedFn()
{
tex.failed = true;
if (params.onload)
{
params.onload(null);
}
};
if (data)
{
if (extension === '.jpg' || extension === '.jpeg')
{
src = 'data:image/jpeg;base64,' + TurbulenzEngine.base64Encode(data);
}
else if (extension === '.png')
{
src = 'data:image/png;base64,' + TurbulenzEngine.base64Encode(data);
}
}
else
{
img.crossOrigin = 'anonymous';
}
img.src = src;
}
else
{
// Invalid src values like "" fall through to here
if ("" === src && params.onload)
{
// Assume the caller intended to pass in a valid url.
return null;
}
var format = params.format;
if (typeof format === 'string')
{
format = gd['PIXELFORMAT_' + format];
}
tex.width = params.width;
tex.height = params.height;
tex.depth = params.depth;
tex.format = format;
tex.cubemap = params.cubemap;
tex.name = params.name;
var result = tex.createGLTexture(params.data);
if (!result)
{
tex = null;
}
if (params.onload)
{
params.onload(tex, 200);
}
}
return tex;
};
//
// WebGLRenderBuffer
//
function WebGLRenderBuffer() {}
WebGLRenderBuffer.prototype =
{
version : 1,
destroy : function renderBufferDestroyFn()
{
var gd = this.gd;
if (gd)
{
var glBuffer = this.glBuffer;
if (glBuffer)
{
var gl = gd.gl;
if (gl)
{
gl.deleteRenderbuffer(glBuffer);
}
delete this.glBuffer;
}
delete this.gd;
}
}
};
// Constructor function
WebGLRenderBuffer.create = function webGLRenderBufferFn(gd, params)
{
var renderBuffer = new WebGLRenderBuffer();
var width = params.width;
var height = params.height;
var format = params.format;
if (typeof format === 'string')
{
format = gd['PIXELFORMAT_' + format];
}
if (format !== gd.PIXELFORMAT_D24S8)
{
return null;
}
var gl = gd.gl;
var glBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, glBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
renderBuffer.width = gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH);
renderBuffer.height = gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
if (renderBuffer.width < width ||
renderBuffer.height < height)
{
gl.deleteRenderbuffer(glBuffer);
return null;
}
renderBuffer.gd = gd;
renderBuffer.format = format;
renderBuffer.glBuffer = glBuffer;
return renderBuffer;
};
//
// WebGLRenderTarget
//
function WebGLRenderTarget() {}
WebGLRenderTarget.prototype =
{
version : 1,
// Shared because there can only be one active at a time
oldViewportBox : [],
oldScissorBox : [],
copyBox : function copyBoxFn(dst, src)
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
},
bind : function bindFn()
{
var gd = this.gd;
var gl = gd.gl;
gd.unbindTexture(this.colorTexture0.glTexture);
if (this.depthTexture)
{
gd.unbindTexture(this.depthTexture.glTexture);
}
gl.bindFramebuffer(gl.FRAMEBUFFER, this.glObject);
var state = gd.state;
this.copyBox(this.oldViewportBox, state.viewportBox);
this.copyBox(this.oldScissorBox, state.scissorBox);
gd.setViewport(0, 0, this.width, this.height);
gd.setScissor(0, 0, this.width, this.height);
return true;
},
unbind : function unbindFn()
{
var gd = this.gd;
var gl = gd.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gd.setViewport.apply(gd, this.oldViewportBox);
gd.setScissor.apply(gd, this.oldScissorBox);
this.colorTexture0.updateMipmaps(this.face);
if (this.depthTexture)
{
this.depthTexture.updateMipmaps(this.face);
}
},
destroy : function renderTargetDestroyFn()
{
var gd = this.gd;
if (gd)
{
var glObject = this.glObject;
if (glObject)
{
var gl = gd.gl;
if (gl)
{
gl.deleteFramebuffer(glObject);
}
delete this.glObject;
}
delete this.colorTexture0;
delete this.colorTexture1;
delete this.colorTexture2;
delete this.colorTexture3;
delete this.depthBuffer;
delete this.depthTexture;
delete this.gd;
}
}
};
// Constructor function
WebGLRenderTarget.create = function webGLRenderTargetFn(gd, params)
{
var renderTarget = new WebGLRenderTarget();
var colorTexture0 = params.colorTexture0;
var colorTexture1 = (colorTexture0 ? (params.colorTexture1 || null) : null);
var colorTexture2 = (colorTexture1 ? (params.colorTexture2 || null) : null);
var colorTexture3 = (colorTexture2 ? (params.colorTexture3 || null) : null);
var depthBuffer = params.depthBuffer || null;
var depthTexture = params.depthTexture || null;
var face = params.face;
var maxSupported = gd.maxSupported("RENDERTARGET_COLOR_TEXTURES");
if (colorTexture1 && maxSupported < 2)
{
return null;
}
if (colorTexture2 && maxSupported < 3)
{
return null;
}
if (colorTexture3 && maxSupported < 4)
{
return null;
}
var gl = gd.gl;
var glObject = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, glObject);
var width, height;
if (colorTexture0)
{
width = colorTexture0.width;
height = colorTexture0.height;
var glTexture = colorTexture0.glTexture;
if (glTexture === undefined)
{
TurbulenzEngine.callOnError("Color texture is not a Texture");
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.deleteFramebuffer(glObject);
return null;
}
var colorAttachment0 = gl.COLOR_ATTACHMENT0;
if (colorTexture0.cubemap)
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, colorAttachment0, (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), glTexture, 0);
}
else
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, colorAttachment0, gl.TEXTURE_2D, glTexture, 0);
}
if (colorTexture1)
{
glTexture = colorTexture1.glTexture;
if (colorTexture1.cubemap)
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, (colorAttachment0 + 1), (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), glTexture, 0);
}
else
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, (colorAttachment0 + 1), gl.TEXTURE_2D, glTexture, 0);
}
if (colorTexture2)
{
glTexture = colorTexture2.glTexture;
if (colorTexture1.cubemap)
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, (colorAttachment0 + 2), (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), glTexture, 0);
}
else
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, (colorAttachment0 + 2), gl.TEXTURE_2D, glTexture, 0);
}
if (colorTexture3)
{
glTexture = colorTexture3.glTexture;
if (colorTexture1.cubemap)
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, (colorAttachment0 + 3), (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), glTexture, 0);
}
else
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, (colorAttachment0 + 3), gl.TEXTURE_2D, glTexture, 0);
}
}
}
}
}
else if (depthTexture)
{
width = depthTexture.width;
height = depthTexture.height;
}
else if (depthBuffer)
{
width = depthBuffer.width;
height = depthBuffer.height;
}
else
{
TurbulenzEngine.callOnError(
"No RenderBuffers or Textures specified for this RenderTarget");
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.deleteFramebuffer(glObject);
return null;
}
if (depthTexture)
{
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT,
gl.TEXTURE_2D, depthTexture.glTexture, 0);
}
else if (depthBuffer)
{
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT,
gl.RENDERBUFFER, depthBuffer.glBuffer);
}
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
if (status !== gl.FRAMEBUFFER_COMPLETE)
{
gl.deleteFramebuffer(glObject);
return null;
}
renderTarget.gd = gd;
renderTarget.glObject = glObject;
renderTarget.colorTexture0 = colorTexture0;
renderTarget.colorTexture1 = colorTexture1;
renderTarget.colorTexture2 = colorTexture2;
renderTarget.colorTexture3 = colorTexture3;
renderTarget.depthBuffer = depthBuffer;
renderTarget.depthTexture = depthTexture;
renderTarget.width = width;
renderTarget.height = height;
renderTarget.face = face;
return renderTarget;
};
//
// WebGLIndexBuffer
//
function WebGLIndexBuffer() {}
WebGLIndexBuffer.prototype =
{
version : 1,
map : function indexBufferMapFn(offset, numIndices)
{
if (offset === undefined)
{
offset = 0;
}
if (numIndices === undefined)
{
numIndices = this.numIndices;
}
var gd = this.gd;
var gl = gd.gl;
var format = this.format;
var data;
if (format === gl.UNSIGNED_BYTE)
{
data = new Uint8Array(numIndices);
}
else if (format === gl.UNSIGNED_SHORT)
{
data = new Uint16Array(numIndices);
}
else //if (format === gl.UNSIGNED_INT)
{
data = new Uint32Array(numIndices);
}
var numValues = 0;
var writer = function indexBufferWriterFn()
{
var numArguments = arguments.length;
for (var n = 0; n < numArguments; n += 1)
{
data[numValues] = arguments[n];
numValues += 1;
}
};
writer.data = data;
writer.offset = offset;
writer.getNumWrittenIndices = function getNumWrittenIndicesFn()
{
return numValues;
};
writer.write = writer;
return writer;
},
unmap : function indexBufferUnmapFn(writer)
{
if (writer)
{
var gd = this.gd;
var gl = gd.gl;
var data = writer.data;
delete writer.data;
var offset = writer.offset;
delete writer.write;
var numIndices = writer.getNumWrittenIndices();
if (!numIndices)
{
return;
}
if (numIndices < data.length)
{
data = data.subarray(0, numIndices);
}
gd.setIndexBuffer(this);
if (numIndices < this.numIndices)
{
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, offset, data);
}
else
{
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, this.usage);
}
}
},
setData : function indexBufferSetDataFn(data, offset, numIndices)
{
if (offset === undefined)
{
offset = 0;
}
if (numIndices === undefined)
{
numIndices = this.numIndices;
}
var gd = this.gd;
var gl = gd.gl;
var bufferData;
var format = this.format;
if (format === gl.UNSIGNED_BYTE)
{
if (data instanceof Uint8Array)
{
bufferData = data;
}
else
{
bufferData = new Uint8Array(data);
}
}
else if (format === gl.UNSIGNED_SHORT)
{
if (data instanceof Uint16Array)
{
bufferData = data;
}
else
{
bufferData = new Uint16Array(data);
}
offset *= 2;
}
else if (format === gl.UNSIGNED_INT)
{
if (data instanceof Uint32Array)
{
bufferData = data;
}
else
{
bufferData = new Uint32Array(data);
}
offset *= 4;
}
data = undefined;
if (numIndices < bufferData.length)
{
bufferData = bufferData.subarray(0, numIndices);
}
gd.setIndexBuffer(this);
if (numIndices < this.numIndices)
{
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, offset, bufferData);
}
else
{
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, bufferData, this.usage);
}
},
destroy : function indexBufferDestroyFn()
{
var gd = this.gd;
if (gd)
{
var glBuffer = this.glBuffer;
if (glBuffer)
{
var gl = gd.gl;
if (gl)
{
gd.unsetIndexBuffer(this);
gl.deleteBuffer(glBuffer);
}
delete this.glBuffer;
}
delete this.gd;
}
}
};
// Constructor function
WebGLIndexBuffer.create = function webGLIndexBufferCreateFn(gd, params)
{
var gl = gd.gl;
var ib = new WebGLIndexBuffer();
ib.gd = gd;
var numIndices = params.numIndices;
ib.numIndices = numIndices;
var format = params.format;
if (typeof format === "string")
{
format = gd['INDEXFORMAT_' + format];
}
ib.format = format;
var stride;
if (format === gl.UNSIGNED_BYTE)
{
stride = 1;
}
else if (format === gl.UNSIGNED_SHORT)
{
stride = 2;
}
else //if (format === gl.UNSIGNED_INT)
{
stride = 4;
}
ib.stride = stride;
/*jshint sub: true*/
// Avoid dot notation lookup to prevent Google Closure complaining about transient being a keyword
ib['transient'] = (params['transient'] || false);
ib.dynamic = (params.dynamic || ib['transient']);
ib.usage = (ib['transient'] ? gl.STREAM_DRAW : (ib.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW));
/*jshint sub: false*/
ib.glBuffer = gl.createBuffer();
if (params.data)
{
ib.setData(params.data, 0, numIndices);
}
else
{
gd.setIndexBuffer(ib);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, (numIndices * stride), ib.usage);
}
return ib;
};
//
// WebGLSemantics
//
function WebGLSemantics() {}
WebGLSemantics.prototype =
{
version : 1
};
// Constructor function
WebGLSemantics.create = function webGLSemanticsCreateFn(gd, attributes)
{
var semantics = new WebGLSemantics();
var numAttributes = attributes.length;
semantics.length = numAttributes;
for (var i = 0; i < numAttributes; i += 1)
{
var attribute = attributes[i];
if (typeof attribute === "string")
{
semantics[i] = gd['SEMANTIC_' + attribute];
}
else
{
semantics[i] = attribute;
}
}
return semantics;
};
//
// WebGLVertexBuffer
//
function WebGLVertexBuffer() {}
WebGLVertexBuffer.prototype =
{
version : 1,
map : function vertexBufferMapFn(offset, numVertices)
{
if (offset === undefined)
{
offset = 0;
}
if (numVertices === undefined)
{
numVertices = this.numVertices;
}
var gd = this.gd;
var gl = gd.gl;
var numValuesPerVertex = this.stride;
var attributes = this.attributes;
var numAttributes = attributes.length;
var data, writer;
var numValues = 0;
if (this.hasSingleFormat)
{
var maxNumValues = (numVertices * numValuesPerVertex);
var format = attributes[0].format;
if (format === gl.BYTE)
{
data = new Int8Array(maxNumValues);
}
else if (format === gl.UNSIGNED_BYTE)
{
data = new Uint8Array(maxNumValues);
}
else if (format === gl.SHORT)
{
data = new Int16Array(maxNumValues);
}
else if (format === gl.UNSIGNED_SHORT)
{
data = new Uint16Array(maxNumValues);
}
else if (format === gl.INT)
{
data = new Int32Array(maxNumValues);
}
else if (format === gl.UNSIGNED_INT)
{
data = new Uint32Array(maxNumValues);
}
else if (format === gl.FLOAT)
{
data = new Float32Array(maxNumValues);
}
writer = function vertexBufferWriterSingleFn()
{
var numArguments = arguments.length;
var currentArgument = 0;
for (var a = 0; a < numAttributes; a += 1)
{
var attribute = attributes[a];
var numComponents = attribute.numComponents;
var currentComponent = 0, j;
do
{
if (currentArgument < numArguments)
{
var value = arguments[currentArgument];
currentArgument += 1;
if (typeof value === "number")
{
if (attribute.normalized)
{
value *= attribute.normalizationScale;
}
data[numValues] = value;
numValues += 1;
currentComponent += 1;
}
else if (currentComponent === 0)
{
var numSubArguments = value.length;
if (numSubArguments > numComponents)
{
numSubArguments = numComponents;
}
if (attribute.normalized)
{
var scale = attribute.normalizationScale;
for (j = 0; j < numSubArguments; j += 1)
{
data[numValues] = (value[j] * scale);
numValues += 1;
currentComponent += 1;
}
}
else
{
for (j = 0; j < numSubArguments; j += 1)
{
data[numValues] = value[j];
numValues += 1;
currentComponent += 1;
}
}
while (currentComponent < numComponents)
{
// No need to clear to zeros
numValues += 1;
currentComponent += 1;
}
break;
}
else
{
TurbulenzEngine.callOnError(
'Missing values for attribute ' + a);
return null;
}
}
else
{
// No need to clear to zeros
numValues += 1;
currentComponent += 1;
}
}
while (currentComponent < numComponents);
}
};
}
else
{
var destOffset = 0;
var bufferSize = (numVertices * this.strideInBytes);
data = new ArrayBuffer(bufferSize);
if (typeof DataView !== 'undefined' && 'setFloat32' in DataView.prototype)
{
var dataView = new DataView(data);
writer = function vertexBufferWriterDataViewFn()
{
var numArguments = arguments.length;
var currentArgument = 0;
for (var a = 0; a < numAttributes; a += 1)
{
var attribute = attributes[a];
var numComponents = attribute.numComponents;
var setter = attribute.typedSetter;
var componentStride = attribute.componentStride;
var currentComponent = 0, j;
do
{
if (currentArgument < numArguments)
{
var value = arguments[currentArgument];
currentArgument += 1;
if (typeof value === "number")
{
if (attribute.normalized)
{
value *= attribute.normalizationScale;
}
setter.call(dataView, destOffset, value, true);
destOffset += componentStride;
currentComponent += 1;
numValues += 1;
}
else if (currentComponent === 0)
{
var numSubArguments = value.length;
if (numSubArguments > numComponents)
{
numSubArguments = numComponents;
}
if (attribute.normalized)
{
var scale = attribute.normalizationScale;
for (j = 0; j < numSubArguments; j += 1)
{
setter.call(dataView, destOffset, (value[j] * scale), true);
destOffset += componentStride;
currentComponent += 1;
numValues += 1;
}
}
else
{
for (j = 0; j < numSubArguments; j += 1)
{
setter.call(dataView, destOffset, value[j], true);
destOffset += componentStride;
currentComponent += 1;
numValues += 1;
}
}
while (currentComponent < numComponents)
{
// No need to clear to zeros
numValues += 1;
currentComponent += 1;
}
break;
}
else
{
TurbulenzEngine.callOnError(
'Missing values for attribute ' + a);
return null;
}
}
else
{
// No need to clear to zeros
numValues += 1;
currentComponent += 1;
}
}
while (currentComponent < numComponents);
}
};
}
else
{
writer = function vertexBufferWriterMultiFn()
{
var numArguments = arguments.length;
var currentArgument = 0;
var dest;
for (var a = 0; a < numAttributes; a += 1)
{
var attribute = attributes[a];
var numComponents = attribute.numComponents;
dest = new attribute.typedArray(data, destOffset, numComponents);
destOffset += attribute.stride;
var currentComponent = 0, j;
do
{
if (currentArgument < numArguments)
{
var value = arguments[currentArgument];
currentArgument += 1;
if (typeof value === "number")
{
if (attribute.normalized)
{
value *= attribute.normalizationScale;
}
dest[currentComponent] = value;
currentComponent += 1;
numValues += 1;
}
else if (currentComponent === 0)
{
var numSubArguments = value.length;
if (numSubArguments > numComponents)
{
numSubArguments = numComponents;
}
if (attribute.normalized)
{
var scale = attribute.normalizationScale;
for (j = 0; j < numSubArguments; j += 1)
{
dest[currentComponent] = (value[j] * scale);
currentComponent += 1;
numValues += 1;
}
}
else
{
for (j = 0; j < numSubArguments; j += 1)
{
dest[currentComponent] = value[j];
currentComponent += 1;
numValues += 1;
}
}
while (currentComponent < numComponents)
{
// No need to clear to zeros
currentComponent += 1;
numValues += 1;
}
break;
}
else
{
TurbulenzEngine.callOnError(
'Missing values for attribute ' + a);
return null;
}
}
else
{
// No need to clear to zeros
currentComponent += 1;
numValues += 1;
}
}
while (currentComponent < numComponents);
}
};
}
}
writer.data = data;
writer.offset = offset;
writer.getNumWrittenVertices = function getNumWrittenVerticesFn()
{
return Math.floor(numValues / numValuesPerVertex);
};
writer.getNumWrittenValues = function getNumWrittenValuesFn()
{
return numValues;
};
writer.write = writer;
return writer;
},
unmap : function vertexBufferUnmapFn(writer)
{
if (writer)
{
var data = writer.data;
delete writer.data;
delete writer.write;
var numVertices = writer.getNumWrittenVertices();
if (!numVertices)
{
return;
}
var offset = writer.offset;
var stride = this.strideInBytes;
if (this.hasSingleFormat)
{
var numValues = writer.getNumWrittenValues();
if (numValues < data.length)
{
data = data.subarray(0, numValues);
}
}
else
{
var numBytes = (numVertices * stride);
if (numBytes < data.byteLength)
{
data = data.slice(0, numBytes);
}
}
var gd = this.gd;
var gl = gd.gl;
gd.bindVertexBuffer(this.glBuffer);
if (numVertices < this.numVertices)
{
gl.bufferSubData(gl.ARRAY_BUFFER, (offset * stride), data);
}
else
{
gl.bufferData(gl.ARRAY_BUFFER, data, this.usage);
}
}
},
setData : function vertexBufferSetDataFn(data, offset, numVertices)
{
if (offset === undefined)
{
offset = 0;
}
if (numVertices === undefined)
{
numVertices = this.numVertices;
}
var gd = this.gd;
var gl = gd.gl;
var strideInBytes = this.strideInBytes;
// Fast path for ArrayBuffer data
if (data.constructor === ArrayBuffer)
{
gd.bindVertexBuffer(this.glBuffer);
if (numVertices < this.numVertices)
{
gl.bufferSubData(gl.ARRAY_BUFFER, (offset * strideInBytes), data);
}
else
{
gl.bufferData(gl.ARRAY_BUFFER, data, this.usage);
}
return;
}
var attributes = this.attributes;
var numAttributes = this.numAttributes;
var attribute, format, bufferData, TypedArrayConstructor;
if (this.hasSingleFormat)
{
attribute = attributes[0];
format = attribute.format;
if (format === gl.BYTE)
{
if (!(data instanceof Int8Array))
{
TypedArrayConstructor = Int8Array;
}
}
else if (format === gl.UNSIGNED_BYTE)
{
if (!(data instanceof Uint8Array))
{
TypedArrayConstructor = Uint8Array;
}
}
else if (format === gl.SHORT)
{
if (!(data instanceof Int16Array))
{
TypedArrayConstructor = Int16Array;
}
}
else if (format === gl.UNSIGNED_SHORT)
{
if (!(data instanceof Uint16Array))
{
TypedArrayConstructor = Uint16Array;
}
}
else if (format === gl.INT)
{
if (!(data instanceof Int32Array))
{
TypedArrayConstructor = Int32Array;
}
}
else if (format === gl.UNSIGNED_INT)
{
if (!(data instanceof Uint32Array))
{
TypedArrayConstructor = Uint32Array;
}
}
else if (format === gl.FLOAT)
{
if (!(data instanceof Float32Array))
{
TypedArrayConstructor = Float32Array;
}
}
var numValuesPerVertex = this.stride;
var numValues = (numVertices * numValuesPerVertex);
if (TypedArrayConstructor)
{
// Data has to be put into a Typed Array and
// potentially normalized.
if (attribute.normalized)
{
data = this.scaleValues(data, attribute.normalizationScale, numValues);
}
bufferData = new TypedArrayConstructor(data);
if (numValues < bufferData.length)
{
bufferData = bufferData.subarray(0, numValues);
}
}
else
{
bufferData = data;
}
if (numValues < data.length)
{
bufferData = bufferData.subarray(0, numValues);
}
}
else
{
var bufferSize = (numVertices * strideInBytes);
bufferData = new ArrayBuffer(bufferSize);
var srcOffset = 0, destOffset = 0, v, c, a, numComponents, componentStride, scale;
if (typeof DataView !== 'undefined' && 'setFloat32' in DataView.prototype)
{
var dataView = new DataView(bufferData);
for (v = 0; v < numVertices; v += 1)
{
for (a = 0; a < numAttributes; a += 1)
{
attribute = attributes[a];
numComponents = attribute.numComponents;
componentStride = attribute.componentStride;
var setter = attribute.typedSetter;
if (attribute.normalized)
{
scale = attribute.normalizationScale;
for (c = 0; c < numComponents; c += 1)
{
setter.call(dataView, destOffset, (data[srcOffset] * scale), true);
destOffset += componentStride;
srcOffset += 1;
}
}
else
{
for (c = 0; c < numComponents; c += 1)
{
setter.call(dataView, destOffset, data[srcOffset], true);
destOffset += componentStride;
srcOffset += 1;
}
}
}
}
}
else
{
for (v = 0; v < numVertices; v += 1)
{
for (a = 0; a < numAttributes; a += 1)
{
attribute = attributes[a];
numComponents = attribute.numComponents;
var dest = new attribute.typedArray(bufferData, destOffset, numComponents);
destOffset += attribute.stride;
if (attribute.normalized)
{
scale = attribute.normalizationScale;
for (c = 0; c < numComponents; c += 1)
{
dest[c] = (data[srcOffset] * scale);
srcOffset += 1;
}
}
else
{
for (c = 0; c < numComponents; c += 1)
{
dest[c] = data[srcOffset];
srcOffset += 1;
}
}
}
}
}
}
data = undefined;
gd.bindVertexBuffer(this.glBuffer);
if (numVertices < this.numVertices)
{
gl.bufferSubData(gl.ARRAY_BUFFER, (offset * strideInBytes), bufferData);
}
else
{
gl.bufferData(gl.ARRAY_BUFFER, bufferData, this.usage);
}
},
// Internal
scaleValues : function scaleValuesFn(values, scale, numValues)
{
if (numValues === undefined)
{
numValues = values.length;
}
var scaledValues = new values.constructor(numValues);
for (var n = 0; n < numValues; n += 1)
{
scaledValues[n] = (values[n] * scale);
}
return scaledValues;
},
bindAttributes : function bindAttributesFn(numAttributes, attributes, offset)
{
var gd = this.gd;
var gl = gd.gl;
var vertexAttributes = this.attributes;
var stride = this.strideInBytes;
var attributeMask = 0;
/*jshint bitwise: false*/
for (var n = 0; n < numAttributes; n += 1)
{
var vertexAttribute = vertexAttributes[n];
var attribute = attributes[n];
attributeMask |= (1 << attribute);
gl.vertexAttribPointer(attribute,
vertexAttribute.numComponents,
vertexAttribute.format,
vertexAttribute.normalized,
stride,
offset);
offset += vertexAttribute.stride;
}
/*jshint bitwise: true*/
return attributeMask;
},
setAttributes : function setAttributesFn(attributes)
{
var gd = this.gd;
var numAttributes = attributes.length;
this.numAttributes = numAttributes;
this.attributes = [];
var stride = 0, numValuesPerVertex = 0, hasSingleFormat = true;
for (var i = 0; i < numAttributes; i += 1)
{
var format = attributes[i];
if (typeof format === "string")
{
format = gd['VERTEXFORMAT_' + format];
}
this.attributes[i] = format;
stride += format.stride;
numValuesPerVertex += format.numComponents;
if (hasSingleFormat && i)
{
if (format.format !== this.attributes[i - 1].format)
{
hasSingleFormat = false;
}
}
}
this.strideInBytes = stride;
this.stride = numValuesPerVertex;
this.hasSingleFormat = hasSingleFormat;
return stride;
},
resize : function resizeFn(size)
{
if (size !== (this.strideInBytes * this.numVertices))
{
var gd = this.gd;
var gl = gd.gl;
gd.bindVertexBuffer(this.glBuffer);
var bufferType = gl.ARRAY_BUFFER;
gl.bufferData(bufferType, size, this.usage);
var bufferSize = gl.getBufferParameter(bufferType, gl.BUFFER_SIZE);
this.numVertices = Math.floor(bufferSize / this.strideInBytes);
}
},
destroy : function vertexBufferDestroyFn()
{
var gd = this.gd;
if (gd)
{
var glBuffer = this.glBuffer;
if (glBuffer)
{
var gl = gd.gl;
if (gl)
{
gd.unbindVertexBuffer(glBuffer);
gl.deleteBuffer(glBuffer);
}
delete this.glBuffer;
}
delete this.gd;
}
}
};
// Constructor function
WebGLVertexBuffer.create = function webGLVertexBufferCreateFn(gd, params)
{
var gl = gd.gl;
var vb = new WebGLVertexBuffer();
vb.gd = gd;
var numVertices = params.numVertices;
vb.numVertices = numVertices;
var strideInBytes = vb.setAttributes(params.attributes);
/*jshint sub: true*/
// Avoid dot notation lookup to prevent Google Closure complaining about transient being a keyword
vb['transient'] = (params['transient'] || false);
vb.dynamic = (params.dynamic || vb['transient']);
vb.usage = (vb['transient'] ? gl.STREAM_DRAW : (vb.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW));
/*jshint sub: false*/
vb.glBuffer = gl.createBuffer();
var bufferSize = (numVertices * strideInBytes);
if (params.data)
{
vb.setData(params.data, 0, numVertices);
}
else
{
gd.bindVertexBuffer(vb.glBuffer);
gl.bufferData(gl.ARRAY_BUFFER, bufferSize, vb.usage);
}
return vb;
};
//
// WebGLPass
//
function WebGLPass() {}
WebGLPass.prototype =
{
version : 1,
updateParametersData : function updateParametersDataFn(gd)
{
var gl = gd.gl;
this.dirty = false;
// Set parameters
var hasProperty = Object.prototype.hasOwnProperty;
var parameters = this.parameters;
for (var p in parameters)
{
if (hasProperty.call(parameters, p))
{
var parameter = parameters[p];
if (parameter.dirty)
{
parameter.dirty = 0;
var paramInfo = parameter.info;
var location = parameter.location;
if (paramInfo &&
null !== location)
{
var parameterValues = paramInfo.values;
var sampler = parameter.sampler;
if (sampler)
{
gd.setTexture(parameter.textureUnit, parameterValues, sampler);
}
else if (1 < paramInfo.numValues)
{
parameter.setter.call(gl, location, parameterValues);
}
else //if (1 === paramInfo.numValues)
{
parameter.setter.call(gl, location, parameterValues[0]);
}
}
}
}
}
},
initializeParameters : function passInitializeParametersFn(gd)
{
var gl = gd.gl;
var glProgram = this.glProgram;
gd.setProgram(glProgram);
var passParameters = this.parameters;
for (var p in passParameters)
{
if (passParameters.hasOwnProperty(p))
{
var parameter = passParameters[p];
var paramInfo = parameter.info;
if (paramInfo)
{
var location = gl.getUniformLocation(glProgram, p);
if (null !== location)
{
parameter.location = location;
if (parameter.sampler)
{
gl.uniform1i(location, parameter.textureUnit);
}
else
{
if (1 < paramInfo.numValues)
{
parameter.setter.call(gl, location, paramInfo.values);
}
else //if (1 === paramInfo.numValues)
{
parameter.setter.call(gl, location, paramInfo.values[0]);
}
}
}
}
}
}
},
destroy : function passDestroyFn()
{
delete this.glProgram;
delete this.semanticsMask;
delete this.parameters;
var states = this.states;
if (states)
{
states.length = 0;
delete this.states;
}
}
};
// Constructor function
WebGLPass.create = function webGLPassCreateFn(gd, shader, params)
{
var gl = gd.gl;
var pass = new WebGLPass();
pass.name = (params.name || null);
var programs = shader.programs;
var parameters = shader.parameters;
var parameterNames = params.parameters;
var programNames = params.programs;
var semanticNames = params.semantics;
var states = params.states;
var compoundProgramName = programNames.join(':');
var linkedProgram = shader.linkedPrograms[compoundProgramName];
var glProgram, semanticsMask, p, s;
if (linkedProgram === undefined)
{
// Create GL program
glProgram = gl.createProgram();
var numPrograms = programNames.length;
for (p = 0; p < numPrograms; p += 1)
{
var glShader = programs[programNames[p]];
if (glShader)
{
gl.attachShader(glProgram, glShader);
}
}
/*jshint bitwise: false*/
var numSemantics = semanticNames.length;
semanticsMask = 0;
for (s = 0; s < numSemantics; s += 1)
{
var semanticName = semanticNames[s];
var attribute = gd['SEMANTIC_' + semanticName];
if (attribute !== undefined)
{
semanticsMask |= (1 << attribute);
gl.bindAttribLocation(glProgram, attribute, ("ATTR" + attribute));
}
}
/*jshint bitwise: true*/
gl.linkProgram(glProgram);
shader.linkedPrograms[compoundProgramName] = {
glProgram : glProgram,
semanticsMask : semanticsMask
};
}
else
{
//console.log('Reused program ' + compoundProgramName);
glProgram = linkedProgram.glProgram;
semanticsMask = linkedProgram.semanticsMask;
}
pass.glProgram = glProgram;
pass.semanticsMask = semanticsMask;
// Set parameters
var numTextureUnits = 0;
var passParameters = {};
pass.parameters = passParameters;
var numParameters = parameterNames ? parameterNames.length : 0;
for (p = 0; p < numParameters; p += 1)
{
var parameterName = parameterNames[p];
var parameter = {};
passParameters[parameterName] = parameter;
var paramInfo = parameters[parameterName];
parameter.info = paramInfo;
if (paramInfo)
{
parameter.location = null;
if (paramInfo.sampler)
{
parameter.sampler = paramInfo.sampler;
parameter.textureUnit = numTextureUnits;
numTextureUnits += 1;
}
else
{
parameter.sampler = undefined;
parameter.textureUnit = undefined;
}
parameter.setter = paramInfo.setter;
}
}
pass.numTextureUnits = numTextureUnits;
pass.numParameters = numParameters;
function equalRenderStates(defaultValues, values)
{
var numDefaultValues = defaultValues.length;
var n;
for (n = 0; n < numDefaultValues; n += 1)
{
if (defaultValues[n] !== values[n])
{
return false;
}
}
return true;
}
var hasProperty = Object.prototype.hasOwnProperty;
var stateHandlers = gd.stateHandlers;
var passStates = [];
var passStatesSet = {};
pass.states = passStates;
pass.statesSet = passStatesSet;
for (s in states)
{
if (hasProperty.call(states, s))
{
var stateHandler = stateHandlers[s];
if (stateHandler)
{
var values = stateHandler.parse(states[s]);
if (values !== null)
{
if (equalRenderStates(stateHandler.defaultValues, values))
{
continue;
}
passStates.push({
name: s,
set: stateHandler.set,
reset: stateHandler.reset,
values: values
});
passStatesSet[s] = true;
}
else
{
TurbulenzEngine.callOnError('Unknown value for state ' +
s + ': ' + states[s]);
}
}
}
}
return pass;
};
//
// WebGLTechnique
//
function WebGLTechnique() {}
WebGLTechnique.prototype =
{
version : 1,
getPass : function getPassFn(id)
{
var passes = this.passes;
var numPasses = passes.length;
if (typeof id === "string")
{
for (var n = 0; n < numPasses; n += 1)
{
var pass = passes[n];
if (pass.name === id)
{
return pass;
}
}
}
else
{
/*jshint bitwise: false*/
id = (id | 0);
/*jshint bitwise: true*/
if (id < numPasses)
{
return passes[id];
}
}
return null;
},
activate : function activateFn(gd)
{
this.device = gd;
if (!this.initialized)
{
this.shader.initialize(gd);
this.initialize(gd);
}
},
deactivate : function deactivateFn()
{
this.device = null;
},
checkProperties : function checkPropertiesFn(gd)
{
// Check for parameters set directly into the technique...
var fakeTechniqueParameters = {}, p;
for (p in this)
{
if (p !== 'version' &&
p !== 'name' &&
p !== 'passes' &&
p !== 'numPasses' &&
p !== 'device' &&
p !== 'numParameters')
{
fakeTechniqueParameters[p] = this[p];
}
}
if (fakeTechniqueParameters)
{
var passes = this.passes;
if (passes.length === 1)
{
gd.setParametersImmediate(gd, passes, fakeTechniqueParameters);
}
else
{
gd.setParametersDeferred(gd, passes, fakeTechniqueParameters);
}
var hasProperty = Object.prototype.hasOwnProperty;
for (p in fakeTechniqueParameters)
{
if (hasProperty.call(fakeTechniqueParameters, p))
{
delete this[p];
}
}
}
},
initialize : function techniqueInitializeFn(gd)
{
if (this.initialized)
{
return;
}
var passes = this.passes;
if (passes)
{
var numPasses = passes.length;
var n;
for (n = 0; n < numPasses; n += 1)
{
passes[n].initializeParameters(gd);
}
}
if (Object.defineProperty)
{
this.initializeParametersSetters(gd);
}
this.initialized = true;
},
initializeParametersSetters : function initializeParametersSettersFn(gd)
{
var gl = gd.gl;
function make_sampler_setter(pass, parameter) {
return function (parameterValues) {
if (this.device)
{
gd.setTexture(parameter.textureUnit, parameterValues, parameter.info.sampler);
}
else
{
pass.dirty = true;
parameter.dirty = 1;
parameter.info.values = parameterValues;
}
};
}
function make_float_uniform_setter(pass, parameter) {
var paramInfo = parameter.info;
var location = parameter.location;
function setDeferredParameter(parameterValues)
{
if (typeof parameterValues !== 'number')
{
var values = paramInfo.values;
var numValues = Math.min(paramInfo.numValues, parameterValues.length);
for (var v = 0; v < numValues; v += 1)
{
values[v] = parameterValues[v];
}
parameter.dirty = Math.max(numValues, (parameter.dirty || 0));
}
else
{
paramInfo.values[0] = parameterValues;
parameter.dirty = (parameter.dirty || 1);
}
pass.dirty = true;
}
switch (paramInfo.columns)
{
case 1:
if (1 === paramInfo.numValues)
{
return function (parameterValues)
{
if (this.device)
{
gl.uniform1f(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
}
return function (parameterValues)
{
if (this.device)
{
gl.uniform1fv(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
case 2:
return function (parameterValues)
{
if (this.device)
{
gl.uniform2fv(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
case 3:
return function (parameterValues)
{
if (this.device)
{
gl.uniform3fv(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
case 4:
return function (parameterValues)
{
if (this.device)
{
gl.uniform4fv(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
default:
return null;
}
}
function make_int_uniform_setter(pass, parameter) {
var paramInfo = parameter.info;
var location = parameter.location;
function setDeferredParameter(parameterValues)
{
if (typeof parameterValues !== 'number')
{
var values = paramInfo.values;
var numValues = Math.min(paramInfo.numValues, parameterValues.length);
for (var v = 0; v < numValues; v += 1)
{
values[v] = parameterValues[v];
}
parameter.dirty = Math.max(numValues, (parameter.dirty || 0));
}
else
{
paramInfo.values[0] = parameterValues;
parameter.dirty = (parameter.dirty || 1);
}
pass.dirty = true;
}
switch (paramInfo.columns)
{
case 1:
if (1 === paramInfo.numValues)
{
return function (parameterValues)
{
if (this.device)
{
gl.uniform1i(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
}
return function (parameterValues)
{
if (this.device)
{
gl.uniform1iv(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
case 2:
return function (parameterValues)
{
if (this.device)
{
gl.uniform2iv(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
case 3:
return function (parameterValues)
{
if (this.device)
{
gl.uniform3iv(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
case 4:
return function (parameterValues)
{
if (this.device)
{
gl.uniform4iv(location, parameterValues);
}
else
{
setDeferredParameter(parameterValues);
}
};
default:
return null;
}
}
var passes = this.passes;
var numPasses = passes.length;
var pass, parameters, p, parameter, paramInfo, setter;
if (numPasses === 1)
{
pass = passes[0];
parameters = pass.parameters;
for (p in parameters)
{
if (parameters.hasOwnProperty(p))
{
parameter = parameters[p];
paramInfo = parameter.info;
if (paramInfo)
{
if (undefined !== parameter.location)
{
if (parameter.sampler)
{
setter = make_sampler_setter(pass, parameter);
}
else
{
if (paramInfo.type === 'float')
{
setter = make_float_uniform_setter(pass, parameter);
}
else
{
setter = make_int_uniform_setter(pass, parameter);
}
}
Object.defineProperty(this, p, {
set : setter,
enumerable : false,
configurable : false
});
}
}
}
}
this.checkProperties = function ()
{
};
}
else
{
Object.defineProperty(this, 'device', {
writable : true,
enumerable : false,
configurable : false
});
Object.defineProperty(this, 'version', {
writable : false,
enumerable : false,
configurable : false
});
Object.defineProperty(this, 'name', {
writable : false,
enumerable : false,
configurable : false
});
Object.defineProperty(this, 'passes', {
writable : false,
enumerable : false,
configurable : false
});
Object.defineProperty(this, 'numParameters', {
writable : false,
enumerable : false,
configurable : false
});
}
},
destroy : function techniqueDestroyFn()
{
var passes = this.passes;
if (passes)
{
var numPasses = passes.length;
var n;
for (n = 0; n < numPasses; n += 1)
{
passes[n].destroy();
}
passes.length = 0;
delete this.passes;
}
delete this.device;
}
};
// Constructor function
WebGLTechnique.create = function webGLTechniqueCreateFn(gd, shader, name, passes)
{
var technique = new WebGLTechnique();
technique.initialized = false;
technique.shader = shader;
technique.name = name;
var numPasses = passes.length, n;
var numParameters = 0;
technique.passes = [];
technique.numPasses = numPasses;
for (n = 0; n < numPasses; n += 1)
{
var passParams = passes[n];
if (passParams.parameters)
{
numParameters += passParams.parameters.length;
}
technique.passes[n] = WebGLPass.create(gd, shader, passParams);
}
technique.numParameters = numParameters;
technique.device = null;
return technique;
};
//
// WebGLShader
//
function WebGLShader() {}
WebGLShader.prototype =
{
version : 1,
getTechnique : function getTechniqueFn(name)
{
if (typeof name === "string")
{
return this.techniques[name];
}
else
{
var techniques = this.techniques;
for (var t in techniques)
{
if (techniques.hasOwnProperty(t))
{
if (name === 0)
{
return techniques[t];
}
else
{
name -= 1;
}
}
}
return null;
}
},
getParameter : function getParameterFn(name)
{
if (typeof name === "string")
{
return this.parameters[name];
}
else
{
/*jshint bitwise: false*/
name = (name | 0);
/*jshint bitwise: true*/
var parameters = this.parameters;
for (var p in parameters)
{
if (parameters.hasOwnProperty(p))
{
if (name === 0)
{
return parameters[p];
}
else
{
name -= 1;
}
}
}
return null;
}
},
initialize : function shaderInitializeFn(gd)
{
if (this.initialized)
{
return;
}
var gl = gd.gl;
var p;
// Check copmpiled programs as late as possible
var shaderPrograms = this.programs;
for (p in shaderPrograms)
{
if (shaderPrograms.hasOwnProperty(p))
{
var compiledProgram = shaderPrograms[p];
var compiled = gl.getShaderParameter(compiledProgram, gl.COMPILE_STATUS);
if (!compiled)
{
var compilerInfo = gl.getShaderInfoLog(compiledProgram);
TurbulenzEngine.callOnError(
'Program "' + p + '" failed to compile: ' + compilerInfo);
}
}
}
// Check linked programs as late as possible
var linkedPrograms = this.linkedPrograms;
for (p in linkedPrograms)
{
if (linkedPrograms.hasOwnProperty(p))
{
var linkedProgram = linkedPrograms[p];
var glProgram = linkedProgram.glProgram;
if (glProgram)
{
var linked = gl.getProgramParameter(glProgram, gl.LINK_STATUS);
if (!linked)
{
var linkerInfo = gl.getProgramInfoLog(glProgram);
TurbulenzEngine.callOnError(
'Program "' + p + '" failed to link: ' + linkerInfo);
}
}
}
}
this.initialized = true;
},
destroy : function shaderDestroyFn()
{
var gd = this.gd;
if (gd)
{
var gl = gd.gl;
var p;
var techniques = this.techniques;
if (techniques)
{
for (p in techniques)
{
if (techniques.hasOwnProperty(p))
{
techniques[p].destroy();
}
}
delete this.techniques;
}
var linkedPrograms = this.linkedPrograms;
if (linkedPrograms)
{
if (gl)
{
for (p in linkedPrograms)
{
if (linkedPrograms.hasOwnProperty(p))
{
var linkedProgram = linkedPrograms[p];
var glProgram = linkedProgram.glProgram;
if (glProgram)
{
gl.deleteProgram(glProgram);
delete linkedProgram.glProgram;
}
}
}
}
delete this.linkedPrograms;
}
var programs = this.programs;
if (programs)
{
if (gl)
{
for (p in programs)
{
if (programs.hasOwnProperty(p))
{
gl.deleteShader(programs[p]);
}
}
}
delete this.programs;
}
delete this.samplers;
delete this.parameters;
delete this.gd;
}
}
};
// Constructor function
WebGLShader.create = function webGLShaderCreateFn(gd, params)
{
var gl = gd.gl;
var shader = new WebGLShader();
shader.initialized = false;
var techniques = params.techniques;
var parameters = params.parameters;
var programs = params.programs;
var samplers = params.samplers;
var p;
shader.gd = gd;
shader.name = params.name;
// Compile programs as early as possible
var shaderPrograms = {};
shader.programs = shaderPrograms;
for (p in programs)
{
if (programs.hasOwnProperty(p))
{
var program = programs[p];
var glShaderType;
if (program.type === 'fragment')
{
glShaderType = gl.FRAGMENT_SHADER;
}
else if (program.type === 'vertex')
{
glShaderType = gl.VERTEX_SHADER;
}
var glShader = gl.createShader(glShaderType);
gl.shaderSource(glShader, program.code);
gl.compileShader(glShader);
shaderPrograms[p] = glShader;
}
}
var linkedPrograms = {};
shader.linkedPrograms = linkedPrograms;
// Samplers
var defaultSampler = gd.DEFAULT_SAMPLER;
var maxAnisotropy = gd.maxAnisotropy;
shader.samplers = {};
var sampler;
for (p in samplers)
{
if (samplers.hasOwnProperty(p))
{
sampler = samplers[p];
var samplerMaxAnisotropy = sampler.MaxAnisotropy;
if (samplerMaxAnisotropy)
{
if (samplerMaxAnisotropy > maxAnisotropy)
{
samplerMaxAnisotropy = maxAnisotropy;
}
}
else
{
samplerMaxAnisotropy = defaultSampler.maxAnisotropy;
}
sampler = {
minFilter : (sampler.MinFilter || defaultSampler.minFilter),
magFilter : (sampler.MagFilter || defaultSampler.magFilter),
wrapS : (sampler.WrapS || defaultSampler.wrapS),
wrapT : (sampler.WrapT || defaultSampler.wrapT),
wrapR : (sampler.WrapR || defaultSampler.wrapR),
maxAnisotropy : samplerMaxAnisotropy
};
if (sampler.wrapS === 0x2900)
{
sampler.wrapS = gl.CLAMP_TO_EDGE;
}
if (sampler.wrapT === 0x2900)
{
sampler.wrapT = gl.CLAMP_TO_EDGE;
}
if (sampler.wrapR === 0x2900)
{
sampler.wrapR = gl.CLAMP_TO_EDGE;
}
shader.samplers[p] = gd.createSampler(sampler);
}
}
// Parameters
var numParameters = 0;
shader.parameters = {};
for (p in parameters)
{
if (parameters.hasOwnProperty(p))
{
var parameter = parameters[p];
if (!parameter.columns)
{
parameter.columns = 1;
}
if (!parameter.rows)
{
parameter.rows = 1;
}
parameter.numValues = (parameter.columns * parameter.rows);
var parameterType = parameter.type;
if (parameterType === "float" ||
parameterType === "int" ||
parameterType === "bool")
{
var parameterValues = parameter.values;
if (parameterValues)
{
if (parameterType === "float")
{
parameter.values = new Float32Array(parameterValues);
}
else
{
parameter.values = new Int32Array(parameterValues);
}
}
else
{
if (parameterType === "float")
{
parameter.values = new Float32Array(parameter.numValues);
}
else
{
parameter.values = new Int32Array(parameter.numValues);
}
}
if (parameterType === 'float')
{
switch (parameter.columns)
{
case 1:
if (1 === parameter.numValues)
{
parameter.setter = gl.uniform1f;
}
else
{
parameter.setter = gl.uniform1fv;
}
break;
case 2:
parameter.setter = gl.uniform2fv;
break;
case 3:
parameter.setter = gl.uniform3fv;
break;
case 4:
parameter.setter = gl.uniform4fv;
break;
default:
break;
}
}
else
{
switch (parameter.columns)
{
case 1:
if (1 === parameter.numValues)
{
parameter.setter = gl.uniform1i;
}
else
{
parameter.setter = gl.uniform1iv;
}
break;
case 2:
parameter.setter = gl.uniform2iv;
break;
case 3:
parameter.setter = gl.uniform3iv;
break;
case 4:
parameter.setter = gl.uniform4iv;
break;
default:
break;
}
}
}
else // Sampler
{
sampler = shader.samplers[p];
if (!sampler)
{
sampler = defaultSampler;
shader.samplers[p] = defaultSampler;
}
parameter.sampler = sampler;
parameter.values = null;
}
parameter.name = p;
shader.parameters[p] = parameter;
numParameters += 1;
}
}
shader.numParameters = numParameters;
// Techniques and passes
var shaderTechniques = {};
var numTechniques = 0;
shader.techniques = shaderTechniques;
for (p in techniques)
{
if (techniques.hasOwnProperty(p))
{
shaderTechniques[p] = WebGLTechnique.create(gd, shader, p, techniques[p]);
numTechniques += 1;
}
}
shader.numTechniques = numTechniques;
return shader;
};
//
// WebGLTechniqueParameters
//
function WebGLTechniqueParameters() {}
// Constructor function
WebGLTechniqueParameters.create = function WebGLTechniqueParametersFn(params)
{
var techniqueParameters = new WebGLTechniqueParameters();
if (params)
{
for (var p in params)
{
if (params.hasOwnProperty(p))
{
techniqueParameters[p] = params[p];
}
}
}
return techniqueParameters;
};
//
// WebGLTechniqueParameterBuffer
//
function techniqueParameterBufferSetData(data, offset, numValues)
{
for (var n = 0, o = offset; n < numValues; n += 1, o += 1)
{
this[o] = data[n];
}
return o;
}
function techniqueParameterBufferCreate(params)
{
if (Float32Array.prototype.map === undefined)
{
Float32Array.prototype.map = function techniqueParameterBufferMap(offset, numFloats) {
if (offset === undefined)
{
offset = 0;
}
var buffer = this;
if (numFloats === undefined)
{
numFloats = this.length;
}
function techniqueParameterBufferWriter()
{
var numArguments = arguments.length;
for (var a = 0; a < numArguments; a += 1)
{
var value = arguments[a];
if (typeof value === 'number')
{
buffer[offset] = value;
offset += 1;
}
else
{
offset = techniqueParameterBufferSetData.call(buffer, value, offset, value.length);
}
}
}
return techniqueParameterBufferWriter;
};
Float32Array.prototype.unmap = function techniqueParameterBufferUnmap(/* writer */) {
};
}
return new Float32Array(params.numFloats);
}
//
// WebGLDrawParameters
//
function WebGLDrawParameters()
{
// Streams, TechniqueParameters and Instances are stored as indexed properties
this.endStreams = 0;
this.endTechniqueParameters = (16 * 3);
this.endInstances = ((16 * 3) + 8);
this.firstIndex = 0;
this.count = 0;
this.sortKey = 0;
this.technique = null;
this.indexBuffer = null;
this.primitive = -1;
this.userData = null;
// Initialize for 1 Stream, 2 TechniqueParameters and 8 Instances
this[0] = undefined;
this[1] = undefined;
this[2] = undefined;
this[(16 * 3) + 0] = undefined;
this[(16 * 3) + 1] = undefined;
this[((16 * 3) + 8) + 0] = undefined;
this[((16 * 3) + 8) + 1] = undefined;
this[((16 * 3) + 8) + 2] = undefined;
this[((16 * 3) + 8) + 3] = undefined;
this[((16 * 3) + 8) + 4] = undefined;
this[((16 * 3) + 8) + 5] = undefined;
this[((16 * 3) + 8) + 6] = undefined;
this[((16 * 3) + 8) + 7] = undefined;
}
WebGLDrawParameters.prototype =
{
version : 1,
setTechniqueParameters : function setTechniqueParametersFn(indx, techniqueParameters)
{
if (indx < 8)
{
indx += (16 * 3);
this[indx] = techniqueParameters;
var endTechniqueParameters = this.endTechniqueParameters;
if (techniqueParameters)
{
if (endTechniqueParameters <= indx)
{
this.endTechniqueParameters = (indx + 1);
}
}
else
{
while ((16 * 3) < endTechniqueParameters &&
!this[endTechniqueParameters - 1])
{
endTechniqueParameters -= 1;
}
this.endTechniqueParameters = endTechniqueParameters;
}
}
},
setVertexBuffer : function setVertexBufferFn(indx, vertexBuffer)
{
if (indx < 16)
{
indx *= 3;
this[indx] = vertexBuffer;
var endStreams = this.endStreams;
if (vertexBuffer)
{
if (endStreams <= indx)
{
this.endStreams = (indx + 3);
}
}
else
{
while (0 < endStreams &&
!this[endStreams - 3])
{
endStreams -= 3;
}
this.endStreams = endStreams;
}
}
},
setSemantics : function setSemanticsFn(indx, semantics)
{
if (indx < 16)
{
this[(indx * 3) + 1] = semantics;
}
},
setOffset : function setOffsetFn(indx, offset)
{
if (indx < 16)
{
this[(indx * 3) + 2] = offset;
}
},
getTechniqueParameters : function getTechniqueParametersFn(indx)
{
if (indx < 8)
{
return this[indx + (16 * 3)];
}
else
{
return undefined;
}
},
getVertexBuffer : function getVertexBufferFn(indx)
{
if (indx < 16)
{
return this[(indx * 3) + 0];
}
else
{
return undefined;
}
},
getSemantics : function getSemanticsFn(indx)
{
if (indx < 16)
{
return this[(indx * 3) + 1];
}
else
{
return undefined;
}
},
getOffset : function getOffsetFn(indx)
{
if (indx < 16)
{
return this[(indx * 3) + 2];
}
else
{
return undefined;
}
},
addInstance : function drawParametersAddInstanceFn(instanceParameters)
{
if (instanceParameters)
{
var endInstances = this.endInstances;
this.endInstances = (endInstances + 1);
this[endInstances] = instanceParameters;
}
},
removeInstances : function drawParametersRemoveInstancesFn()
{
this.endInstances = ((16 * 3) + 8);
},
getNumInstances : function drawParametersGetNumInstancesFn()
{
return (this.endInstances - ((16 * 3) + 8));
}
};
// Constructor function
WebGLDrawParameters.create = function webGLDrawParametersFn(/* params */)
{
return new WebGLDrawParameters();
};
//
// WebGLGraphicsDevice
//
function WebGLGraphicsDevice() {}
WebGLGraphicsDevice.prototype =
{
version : 1,
SEMANTIC_POSITION: 0,
SEMANTIC_POSITION0: 0,
SEMANTIC_BLENDWEIGHT: 1,
SEMANTIC_BLENDWEIGHT0: 1,
SEMANTIC_NORMAL: 2,
SEMANTIC_NORMAL0: 2,
SEMANTIC_COLOR: 3,
SEMANTIC_COLOR0: 3,
SEMANTIC_COLOR1: 4,
SEMANTIC_SPECULAR: 4,
SEMANTIC_FOGCOORD: 5,
SEMANTIC_TESSFACTOR: 5,
SEMANTIC_PSIZE0: 6,
SEMANTIC_BLENDINDICES: 7,
SEMANTIC_BLENDINDICES0: 7,
SEMANTIC_TEXCOORD: 8,
SEMANTIC_TEXCOORD0: 8,
SEMANTIC_TEXCOORD1: 9,
SEMANTIC_TEXCOORD2: 10,
SEMANTIC_TEXCOORD3: 11,
SEMANTIC_TEXCOORD4: 12,
SEMANTIC_TEXCOORD5: 13,
SEMANTIC_TEXCOORD6: 14,
SEMANTIC_TEXCOORD7: 15,
SEMANTIC_TANGENT: 14,
SEMANTIC_TANGENT0: 14,
SEMANTIC_BINORMAL0: 15,
SEMANTIC_BINORMAL: 15,
SEMANTIC_PSIZE: 6,
SEMANTIC_ATTR0: 0,
SEMANTIC_ATTR1: 1,
SEMANTIC_ATTR2: 2,
SEMANTIC_ATTR3: 3,
SEMANTIC_ATTR4: 4,
SEMANTIC_ATTR5: 5,
SEMANTIC_ATTR6: 6,
SEMANTIC_ATTR7: 7,
SEMANTIC_ATTR8: 8,
SEMANTIC_ATTR9: 9,
SEMANTIC_ATTR10: 10,
SEMANTIC_ATTR11: 11,
SEMANTIC_ATTR12: 12,
SEMANTIC_ATTR13: 13,
SEMANTIC_ATTR14: 14,
SEMANTIC_ATTR15: 15,
PIXELFORMAT_A8: 0,
PIXELFORMAT_L8: 1,
PIXELFORMAT_L8A8: 2,
PIXELFORMAT_R5G5B5A1: 3,
PIXELFORMAT_R5G6B5: 4,
PIXELFORMAT_R8G8B8A8: 5,
PIXELFORMAT_R8G8B8: 6,
PIXELFORMAT_D24S8: 7,
PIXELFORMAT_DXT1: 8,
PIXELFORMAT_DXT3: 9,
PIXELFORMAT_DXT5: 10,
drawIndexed : function drawIndexedFn(primitive, numIndices, first)
{
var gl = this.gl;
var indexBuffer = this.activeIndexBuffer;
var offset;
if (first)
{
offset = (first * indexBuffer.stride);
}
else
{
offset = 0;
}
var format = indexBuffer.format;
var attributeMask = this.attributeMask;
var activeTechnique = this.activeTechnique;
var passes = activeTechnique.passes;
var numPasses = passes.length;
var mask;
activeTechnique.checkProperties(this);
/*jshint bitwise: false*/
if (1 === numPasses)
{
mask = (passes[0].semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
this.enableClientState(mask);
}
gl.drawElements(primitive, numIndices, format, offset);
}
else
{
for (var p = 0; p < numPasses; p += 1)
{
var pass = passes[p];
mask = (pass.semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
this.enableClientState(mask);
}
this.setPass(pass);
gl.drawElements(primitive, numIndices, format, offset);
}
}
/*jshint bitwise: true*/
},
draw : function drawFn(primitive, numVertices, first)
{
var gl = this.gl;
var attributeMask = this.attributeMask;
var activeTechnique = this.activeTechnique;
var passes = activeTechnique.passes;
var numPasses = passes.length;
var mask;
activeTechnique.checkProperties(this);
/*jshint bitwise: false*/
if (1 === numPasses)
{
mask = (passes[0].semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
this.enableClientState(mask);
}
gl.drawArrays(primitive, first, numVertices);
}
else
{
for (var p = 0; p < numPasses; p += 1)
{
var pass = passes[p];
mask = (pass.semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
this.enableClientState(mask);
}
this.setPass(pass);
gl.drawArrays(primitive, first, numVertices);
}
}
/*jshint bitwise: true*/
},
setTechniqueParameters : function setTechniqueParametersFn()
{
var activeTechnique = this.activeTechnique;
var passes = activeTechnique.passes;
var setParameters = (1 === passes.length ? this.setParametersImmediate : this.setParametersDeferred);
var numTechniqueParameters = arguments.length;
for (var t = 0; t < numTechniqueParameters; t += 1)
{
setParameters(this, passes, arguments[t]);
}
},
//Internal
setParametersImmediate : function setParametersImmediateFn(gd, passes, techniqueParameters)
{
var gl = gd.gl;
var parameters = passes[0].parameters;
/*jshint forin: true*/
for (var p in techniqueParameters)
{
var parameter = parameters[p];
if (parameter !== undefined)
{
var sampler = parameter.sampler;
var parameterValues = techniqueParameters[p];
if (parameterValues !== undefined)
{
if (sampler !== undefined)
{
gd.setTexture(parameter.textureUnit, parameterValues, sampler);
}
else
{
parameter.setter.call(gl, parameter.location, parameterValues);
}
}
else
{
delete techniqueParameters[p];
if (sampler)
{
gd.setTexture(parameter.textureUnit);
}
}
}
}
/*jshint forin: false*/
},
// ONLY USE FOR SINGLE PASS TECHNIQUES ON DRAWARRAY
setParametersCaching : function setParametersCachingFn(gd, passes, techniqueParameters)
{
var gl = gd.gl;
var parameters = passes[0].parameters;
/*jshint forin: true*/
for (var p in techniqueParameters)
{
var parameter = parameters[p];
if (parameter !== undefined)
{
var parameterValues = techniqueParameters[p];
if (parameter.value !== parameterValues)
{
parameter.value = parameterValues;
var sampler = parameter.sampler;
if (parameterValues !== undefined)
{
if (sampler !== undefined)
{
gd.setTexture(parameter.textureUnit, parameterValues, sampler);
}
else
{
parameter.setter.call(gl, parameter.location, parameterValues);
}
}
else
{
delete techniqueParameters[p];
if (sampler)
{
gd.setTexture(parameter.textureUnit);
}
}
}
}
}
/*jshint forin: false*/
},
setParametersDeferred : function setParametersDeferredFn(gd, passes, techniqueParameters)
{
var numPasses = passes.length;
var min = Math.min;
var max = Math.max;
for (var n = 0; n < numPasses; n += 1)
{
var pass = passes[n];
var parameters = pass.parameters;
pass.dirty = true;
/*jshint forin: true*/
for (var p in techniqueParameters)
{
var parameter = parameters[p];
if (parameter)
{
var paramInfo = parameter.info;
var parameterValues = techniqueParameters[p];
if (parameterValues !== undefined)
{
if (parameter.sampler)
{
paramInfo.values = parameterValues;
parameter.dirty = 1;
}
else if (typeof parameterValues !== 'number')
{
var values = paramInfo.values;
var numValues = min(paramInfo.numValues, parameterValues.length);
for (var v = 0; v < numValues; v += 1)
{
values[v] = parameterValues[v];
}
parameter.dirty = max(numValues, (parameter.dirty || 0));
}
else
{
paramInfo.values[0] = parameterValues;
parameter.dirty = (parameter.dirty || 1);
}
}
else
{
delete techniqueParameters[p];
}
}
}
/*jshint forin: false*/
}
},
setTechnique : function setTechniqueFn(technique)
{
var activeTechnique = this.activeTechnique;
if (activeTechnique !== technique)
{
if (activeTechnique)
{
activeTechnique.deactivate();
}
this.activeTechnique = technique;
technique.activate(this);
var passes = technique.passes;
if (1 === passes.length)
{
this.setPass(passes[0]);
}
}
},
// ONLY USE FOR SINGLE PASS TECHNIQUES ON DRAWARRAY
setTechniqueCaching : function setTechniqueCachingFn(technique)
{
var pass = technique.passes[0];
var activeTechnique = this.activeTechnique;
if (activeTechnique !== technique)
{
if (activeTechnique)
{
activeTechnique.deactivate();
}
this.activeTechnique = technique;
technique.activate(this);
this.setPass(pass);
}
var parameters = pass.parameters;
for (var p in parameters)
{
if (parameters.hasOwnProperty(p))
{
parameters[p].value = null;
}
}
},
setStream : function setStreamFn(vertexBuffer, semantics, offset)
{
if (offset)
{
offset *= vertexBuffer.strideInBytes;
}
else
{
offset = 0;
}
this.bindVertexBuffer(vertexBuffer.glBuffer);
var attributes = semantics;
var numAttributes = attributes.length;
if (numAttributes > vertexBuffer.numAttributes)
{
numAttributes = vertexBuffer.numAttributes;
}
/*jshint bitwise: false*/
this.attributeMask |= vertexBuffer.bindAttributes(numAttributes, attributes, offset);
/*jshint bitwise: true*/
},
setIndexBuffer : function setIndexBufferFn(indexBuffer)
{
if (this.activeIndexBuffer !== indexBuffer)
{
this.activeIndexBuffer = indexBuffer;
var glBuffer;
if (indexBuffer)
{
glBuffer = indexBuffer.glBuffer;
}
else
{
glBuffer = null;
}
var gl = this.gl;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, glBuffer);
}
},
drawArray : function drawArrayFn(drawParametersArray, globalTechniqueParametersArray, sortMode)
{
var gl = this.gl;
var ELEMENT_ARRAY_BUFFER = gl.ELEMENT_ARRAY_BUFFER;
var setParametersCaching = this.setParametersCaching;
var setParametersDeferred = this.setParametersDeferred;
var setStream = this.setStream;
var enableClientState = this.enableClientState;
var numGlobalTechniqueParameters = globalTechniqueParametersArray.length;
var numDrawParameters = drawParametersArray.length;
if (numDrawParameters > 1 && sortMode)
{
if (sortMode > 0)
{
drawParametersArray.sort(function drawArraySortPositive(a, b) {
return (b.sortKey - a.sortKey);
});
}
else if (sortMode < 0)
{
drawParametersArray.sort(function drawArraySortNegative(a, b) {
return (a.sortKey - b.sortKey);
});
}
}
var activeIndexBuffer = this.activeIndexBuffer;
var setParameters = null;
var lastTechnique = null;
var lastEndStreams = -1;
var lastDrawParameters = null;
var techniqueParameters = null;
var v = 0;
var streamsMatch = false;
var vertexBuffer = null;
var offset = 0;
var passes = null;
var p = null;
var pass = null;
var format = 0;
var numPasses = 0;
var mask = 0;
var attributeMask = 0;
var t = 0;
for (var n = 0; n < numDrawParameters; n += 1)
{
var drawParameters = drawParametersArray[n];
var technique = drawParameters.technique;
var endTechniqueParameters = drawParameters.endTechniqueParameters;
var endStreams = drawParameters.endStreams;
var endInstances = drawParameters.endInstances;
var indexBuffer = drawParameters.indexBuffer;
var primitive = drawParameters.primitive;
var count = drawParameters.count;
var firstIndex = drawParameters.firstIndex;
if (lastTechnique !== technique)
{
lastTechnique = technique;
passes = technique.passes;
numPasses = passes.length;
if (1 === numPasses)
{
this.setTechniqueCaching(technique);
setParameters = setParametersCaching;
}
else
{
this.setTechnique(technique);
setParameters = setParametersDeferred;
}
technique.checkProperties(this);
for (t = 0; t < numGlobalTechniqueParameters; t += 1)
{
setParameters(this, passes, globalTechniqueParametersArray[t]);
}
}
for (t = (16 * 3); t < endTechniqueParameters; t += 1)
{
techniqueParameters = drawParameters[t];
if (techniqueParameters)
{
setParameters(this, passes, techniqueParameters);
}
}
streamsMatch = (lastEndStreams === endStreams);
for (v = 0; streamsMatch && v < endStreams; v += 3)
{
streamsMatch = (lastDrawParameters[v] === drawParameters[v] &&
lastDrawParameters[v + 1] === drawParameters[v + 1] &&
lastDrawParameters[v + 2] === drawParameters[v + 2]);
}
if (!streamsMatch)
{
lastEndStreams = endStreams;
lastDrawParameters = drawParameters;
for (v = 0; v < endStreams; v += 3)
{
vertexBuffer = drawParameters[v];
if (vertexBuffer)
{
setStream.call(this, vertexBuffer, drawParameters[v + 1], drawParameters[v + 2]);
}
}
attributeMask = this.attributeMask;
}
/*jshint bitwise: false*/
if (indexBuffer)
{
if (activeIndexBuffer !== indexBuffer)
{
activeIndexBuffer = indexBuffer;
gl.bindBuffer(ELEMENT_ARRAY_BUFFER, indexBuffer.glBuffer);
}
offset = firstIndex;
if (offset)
{
offset *= indexBuffer.stride;
}
format = indexBuffer.format;
if (1 === numPasses)
{
mask = (passes[0].semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
enableClientState.call(this, mask);
}
t = ((16 * 3) + 8);
if (t < endInstances)
{
do
{
setParameters(this, passes, drawParameters[t]);
gl.drawElements(primitive, count, format, offset);
t += 1;
}
while (t < endInstances);
}
else
{
gl.drawElements(primitive, count, format, offset);
}
}
else
{
t = ((16 * 3) + 8);
if (t < endInstances)
{
do
{
setParameters(this, passes, drawParameters[t]);
for (p = 0; p < numPasses; p += 1)
{
pass = passes[p];
mask = (pass.semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
enableClientState.call(this, mask);
}
this.setPass(pass);
gl.drawElements(primitive, count, format, offset);
}
t += 1;
}
while (t < endInstances);
}
else
{
for (p = 0; p < numPasses; p += 1)
{
pass = passes[p];
mask = (pass.semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
enableClientState.call(this, mask);
}
this.setPass(pass);
gl.drawElements(primitive, count, format, offset);
}
}
}
}
else
{
if (1 === numPasses)
{
mask = (passes[0].semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
enableClientState.call(this, mask);
}
t = ((16 * 3) + 8);
if (t < endInstances)
{
do
{
setParameters(this, passes, drawParameters[t]);
gl.drawArrays(primitive, firstIndex, count);
t += 1;
}
while (t < endInstances);
}
else
{
gl.drawArrays(primitive, firstIndex, count);
}
}
else
{
t = ((16 * 3) + 8);
if (t < endInstances)
{
do
{
setParameters(this, passes, drawParameters[t]);
for (p = 0; p < numPasses; p += 1)
{
pass = passes[p];
mask = (pass.semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
enableClientState.call(this, mask);
}
this.setPass(pass);
gl.drawArrays(primitive, firstIndex, count);
}
t += 1;
}
while (t < endInstances);
}
else
{
for (p = 0; p < numPasses; p += 1)
{
pass = passes[p];
mask = (pass.semanticsMask & attributeMask);
if (mask !== this.clientStateMask)
{
enableClientState.call(this, mask);
}
this.setPass(pass);
gl.drawArrays(primitive, firstIndex, count);
}
}
}
}
/*jshint bitwise: true*/
}
this.activeIndexBuffer = activeIndexBuffer;
},
beginDraw : function beginDrawFn(primitive, numVertices, formats, semantics)
{
this.immediatePrimitive = primitive;
if (numVertices)
{
var n;
var immediateSemantics = this.immediateSemantics;
var attributes = semantics;
var numAttributes = attributes.length;
immediateSemantics.length = numAttributes;
for (n = 0; n < numAttributes; n += 1)
{
var attribute = attributes[n];
if (typeof attribute === "string")
{
attribute = this['SEMANTIC_' + attribute];
}
immediateSemantics[n] = attribute;
}
var immediateVertexBuffer = this.immediateVertexBuffer;
var oldStride = immediateVertexBuffer.strideInBytes;
var oldSize = (oldStride * immediateVertexBuffer.numVertices);
var stride = immediateVertexBuffer.setAttributes(formats);
if (stride !== oldStride)
{
immediateVertexBuffer.numVertices = Math.floor(oldSize / stride);
}
var size = (stride * numVertices);
if (size > oldSize)
{
immediateVertexBuffer.resize(size);
}
return immediateVertexBuffer.map(0, numVertices);
}
return null;
},
endDraw : function endDrawFn(writer)
{
var immediateVertexBuffer = this.immediateVertexBuffer;
var numVerticesWritten = writer.getNumWrittenVertices();
immediateVertexBuffer.unmap(writer);
if (numVerticesWritten)
{
var gl = this.gl;
var stride = immediateVertexBuffer.strideInBytes;
var offset = 0;
/*jshint bitwise: false*/
var vertexAttributes = immediateVertexBuffer.attributes;
var semantics = this.immediateSemantics;
var numSemantics = semantics.length;
var deltaAttributeMask = 0;
for (var n = 0; n < numSemantics; n += 1)
{
var vertexAttribute = vertexAttributes[n];
var attribute = semantics[n];
deltaAttributeMask |= (1 << attribute);
gl.vertexAttribPointer(attribute,
vertexAttribute.numComponents,
vertexAttribute.format,
vertexAttribute.normalized,
stride,
offset);
offset += vertexAttribute.stride;
}
this.attributeMask |= deltaAttributeMask;
/*jshint bitwise: true*/
this.draw(this.immediatePrimitive, numVerticesWritten, 0);
}
},
setViewport : function setViewportFn(x, y, w, h)
{
var currentBox = this.state.viewportBox;
if (currentBox[0] !== x ||
currentBox[1] !== y ||
currentBox[2] !== w ||
currentBox[3] !== h)
{
currentBox[0] = x;
currentBox[1] = y;
currentBox[2] = w;
currentBox[3] = h;
this.gl.viewport(x, y, w, h);
}
},
setScissor : function setScissorFn(x, y, w, h)
{
var currentBox = this.state.scissorBox;
if (currentBox[0] !== x ||
currentBox[1] !== y ||
currentBox[2] !== w ||
currentBox[3] !== h)
{
currentBox[0] = x;
currentBox[1] = y;
currentBox[2] = w;
currentBox[3] = h;
this.gl.scissor(x, y, w, h);
}
},
clear : function clearFn(color, depth, stencil)
{
var gl = this.gl;
var state = this.state;
var clearMask = 0;
if (color)
{
clearMask += gl.COLOR_BUFFER_BIT;
var currentColor = state.clearColor;
var color0 = color[0];
var color1 = color[1];
var color2 = color[2];
var color3 = color[3];
if (currentColor[0] !== color0 ||
currentColor[1] !== color1 ||
currentColor[2] !== color2 ||
currentColor[3] !== color3)
{
currentColor[0] = color0;
currentColor[1] = color1;
currentColor[2] = color2;
currentColor[3] = color3;
gl.clearColor(color0, color1, color2, color3);
}
}
if (depth !== undefined)
{
clearMask += gl.DEPTH_BUFFER_BIT;
if (state.clearDepth !== depth)
{
state.clearDepth = depth;
gl.clearDepth(depth);
}
if (stencil !== undefined)
{
clearMask += gl.STENCIL_BUFFER_BIT;
if (state.clearStencil !== stencil)
{
state.clearStencil = stencil;
gl.clearStencil(stencil);
}
}
}
if (clearMask)
{
var colorMask = state.colorMask;
var colorMaskEnabled = (colorMask[0] || colorMask[1] || colorMask[2] || colorMask[3]);
var depthMask = state.depthMask;
var program = state.program;
if (color)
{
if (!colorMaskEnabled)
{
// This is posibly a mistake, enable it for this call
gl.colorMask(true, true, true, true);
}
}
if (depth !== undefined)
{
if (!depthMask)
{
// This is posibly a mistake, enable it for this call
gl.depthMask(true);
}
}
if (program)
{
gl.useProgram(null); // Work around for Mac crash bug.
}
gl.clear(clearMask);
if (color)
{
if (!colorMaskEnabled)
{
gl.colorMask(false, false, false, false);
}
}
if (depth !== undefined)
{
if (!depthMask)
{
gl.depthMask(false);
}
}
if (program)
{
gl.useProgram(program);
}
}
},
beginFrame : function beginFrameFn()
{
var gl = this.gl;
this.attributeMask = 0;
/*jshint bitwise: false*/
var clientStateMask = this.clientStateMask;
var n;
if (clientStateMask)
{
for (n = 0; n < 16; n += 1)
{
if (clientStateMask & (1 << n))
{
gl.disableVertexAttribArray(n);
}
}
this.clientStateMask = 0;
}
/*jshint bitwise: true*/
this.resetStates();
this.setScissor(0, 0, this.width, this.height);
this.setViewport(0, 0, this.width, this.height);
return true;
},
beginRenderTarget : function beginRenderTargetFn(renderTarget)
{
this.activeRenderTarget = renderTarget;
return renderTarget.bind();
},
endRenderTarget : function endRenderTargetFn()
{
this.activeRenderTarget.unbind();
this.activeRenderTarget = null;
},
beginOcclusionQuery : function beginOcclusionQueryFn()
{
return false;
},
endOcclusionQuery : function endOcclusionQueryFn()
{
},
endFrame : function endFrameFn()
{
var gl = this.gl;
//gl.flush();
if (this.activeTechnique)
{
this.activeTechnique.deactivate();
this.activeTechnique = null;
}
if (this.activeIndexBuffer)
{
this.setIndexBuffer(null);
}
var state = this.state;
if (state.program)
{
state.program = null;
gl.useProgram(null);
}
this.numFrames += 1;
var currentFrameTime = TurbulenzEngine.getTime();
var diffTime = (currentFrameTime - this.previousFrameTime);
if (diffTime >= 1000.0)
{
this.fps = (this.numFrames / (diffTime * 0.001));
this.numFrames = 0;
this.previousFrameTime = currentFrameTime;
}
var canvas = gl.canvas;
var width = (gl.drawingBufferWidth || canvas.width);
var height = (gl.drawingBufferHeight || canvas.height);
if (this.width !== width ||
this.height !== height)
{
this.width = width;
this.height = height;
this.setViewport(0, 0, width, height);
this.setScissor(0, 0, width, height);
}
this.checkFullScreen();
},
createTechniqueParameters : function createTechniqueParametersFn(params)
{
return WebGLTechniqueParameters.create(params);
},
createSemantics : function createSemanticsFn(attributes)
{
return WebGLSemantics.create(this, attributes);
},
createVertexBuffer : function createVertexBufferFn(params)
{
return WebGLVertexBuffer.create(this, params);
},
createIndexBuffer : function createIndexBufferFn(params)
{
return WebGLIndexBuffer.create(this, params);
},
createTexture : function createTextureFn(params)
{
return WebGLTexture.create(this, params);
},
createShader : function createShaderFn(params)
{
return WebGLShader.create(this, params);
},
createTechniqueParameterBuffer : function createTechniqueParameterBufferFn(params)
{
return techniqueParameterBufferCreate(params);
},
createRenderBuffer : function createRenderBufferFn(params)
{
return WebGLRenderBuffer.create(this, params);
},
createRenderTarget : function createRenderTargetFn(params)
{
return WebGLRenderTarget.create(this, params);
},
createOcclusionQuery : function createOcclusionQueryFn(/* params */)
{
return null;
},
createDrawParameters : function createDrawParametersFn(params)
{
return WebGLDrawParameters.create(params);
},
isSupported : function isSupportedFn(name)
{
var gl = this.gl;
if ("OCCLUSION_QUERIES" === name)
{
return false;
}
else if ("NPOT_MIPMAPPED_TEXTURES" === name)
{
return false;
}
else if ("TEXTURE_DXT1" === name ||
"TEXTURE_DXT3" === name ||
"TEXTURE_DXT5" === name)
{
var compressedTexturesExtension = this.compressedTexturesExtension;
if (compressedTexturesExtension)
{
var compressedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
if (compressedFormats)
{
var requestedFormat;
if ("TEXTURE_DXT1" === name)
{
requestedFormat = compressedTexturesExtension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
}
else if ("TEXTURE_DXT3" === name)
{
requestedFormat = compressedTexturesExtension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
}
else //if ("TEXTURE_DXT5" === name)
{
requestedFormat = compressedTexturesExtension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
var numCompressedFormats = compressedFormats.length;
for (var n = 0; n < numCompressedFormats; n += 1)
{
if (compressedFormats[n] === requestedFormat)
{
return true;
}
}
}
}
return false;
}
else if ("TEXTURE_ETC1" === name)
{
return false;
}
else if ("INDEXFORMAT_UINT" === name)
{
if (gl.getExtension('OES_element_index_uint'))
{
return true;
}
return false;
}
return undefined;
},
maxSupported : function maxSupportedFn(name)
{
var gl = this.gl;
if ("ANISOTROPY" === name)
{
return this.maxAnisotropy;
}
else if ("TEXTURE_SIZE" === name)
{
return gl.getParameter(gl.MAX_TEXTURE_SIZE);
}
else if ("CUBEMAP_TEXTURE_SIZE" === name)
{
return gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE);
}
else if ("3D_TEXTURE_SIZE" === name)
{
return 0;
}
else if ("RENDERTARGET_COLOR_TEXTURES" === name)
{
return 1;
}
else if ("RENDERBUFFER_SIZE" === name)
{
return gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);
}
return 0;
},
loadTexturesArchive : function loadTexturesArchiveFn(params)
{
var src = params.src;
if (typeof TARLoader !== 'undefined')
{
TARLoader.create({
gd: this,
src : src,
mipmaps : params.mipmaps,
ontextureload : function tarTextureLoadedFn(texture)
{
params.ontextureload(texture);
},
onload : function tarLoadedFn(success, status)
{
if (params.onload)
{
params.onload(true, status);
}
},
onerror : function tarFailedFn()
{
if (params.onload)
{
params.onload(false, status);
}
}
});
return true;
}
else
{
TurbulenzEngine.callOnError(
'Missing archive loader required for ' + src);
return false;
}
},
getScreenshot : function getScreenshotFn(compress, x, y, width, height)
{
var gl = this.gl;
var canvas = gl.canvas;
if (compress)
{
return canvas.toDataURL('image/jpeg');
}
else
{
if (x === undefined)
{
x = 0;
}
if (y === undefined)
{
y = 0;
}
var target = this.activeRenderTarget;
if (!target)
{
target = canvas;
}
if (width === undefined)
{
width = target.width;
}
if (height === undefined)
{
height = target.height;
}
var pixels = new Uint8Array(4 * width * height);
gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
return pixels;
}
},
// private
checkFullScreen : function checkFullScreenFn()
{
var fullscreen = this.fullscreen;
if (this.oldFullscreen !== fullscreen)
{
this.oldFullscreen = fullscreen;
this.requestFullScreen(fullscreen);
}
},
requestFullScreen : function requestFullScreenFn(fullscreen)
{
if (fullscreen)
{
var canvas = this.gl.canvas;
if (canvas.webkitRequestFullScreenWithKeys)
{
canvas.webkitRequestFullScreenWithKeys();
}
else if (canvas.requestFullScreenWithKeys)
{
canvas.requestFullScreenWithKeys();
}
else if (canvas.webkitRequestFullScreen)
{
canvas.webkitRequestFullScreen(canvas.ALLOW_KEYBOARD_INPUT);
}
else if (canvas.mozRequestFullScreen)
{
canvas.mozRequestFullScreen();
}
else if (canvas.requestFullScreen)
{
canvas.requestFullScreen();
}
else if (canvas.requestFullscreen)
{
canvas.requestFullscreen();
}
}
else
{
if (document.webkitCancelFullScreen)
{
document.webkitCancelFullScreen();
}
else if (document.cancelFullScreen)
{
document.cancelFullScreen();
}
else if (document.exitFullscreen)
{
document.exitFullscreen();
}
}
},
createSampler : function createSamplerFn(sampler)
{
var samplerKey = sampler.minFilter.toString() +
':' + sampler.magFilter.toString() +
':' + sampler.wrapS.toString() +
':' + sampler.wrapT.toString() +
':' + sampler.wrapR.toString() +
':' + sampler.maxAnisotropy.toString();
var cachedSamplers = this.cachedSamplers;
var cachedSampler = cachedSamplers[samplerKey];
if (!cachedSampler)
{
cachedSamplers[samplerKey] = sampler;
return sampler;
}
return cachedSampler;
},
unsetIndexBuffer : function unsetIndexBufferFn(indexBuffer)
{
if (this.activeIndexBuffer === indexBuffer)
{
this.activeIndexBuffer = null;
var gl = this.gl;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
},
bindVertexBuffer : function bindVertexBufferFn(buffer)
{
if (this.bindedVertexBuffer !== buffer)
{
this.bindedVertexBuffer = buffer;
var gl = this.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
}
},
unbindVertexBuffer : function unbindVertexBufferFn(buffer)
{
if (this.bindedVertexBuffer === buffer)
{
this.bindedVertexBuffer = null;
var gl = this.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
},
bindTextureUnit : function bindTextureUnitFn(unit, target, texture)
{
var state = this.state;
var gl = this.gl;
if (state.activeTextureUnit !== unit)
{
state.activeTextureUnit = unit;
gl.activeTexture(gl.TEXTURE0 + unit);
}
gl.bindTexture(target, texture);
},
bindTexture : function bindTextureFn(target, texture)
{
var state = this.state;
var gl = this.gl;
var dummyUnit = (state.maxTextureUnit - 1);
if (state.activeTextureUnit !== dummyUnit)
{
state.activeTextureUnit = dummyUnit;
gl.activeTexture(gl.TEXTURE0 + dummyUnit);
}
gl.bindTexture(target, texture);
},
unbindTexture : function unbindTextureFn(texture)
{
var state = this.state;
var lastMaxTextureUnit = state.lastMaxTextureUnit;
var textureUnits = state.textureUnits;
for (var u = 0; u < lastMaxTextureUnit; u += 1)
{
var textureUnit = textureUnits[u];
if (textureUnit.texture === texture)
{
textureUnit.texture = null;
this.bindTextureUnit(u, textureUnit.target, null);
}
}
},
setSampler : function setSamplerFn(sampler, target)
{
if (sampler)
{
var gl = this.gl;
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, sampler.minFilter);
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, sampler.magFilter);
gl.texParameteri(target, gl.TEXTURE_WRAP_S, sampler.wrapS);
gl.texParameteri(target, gl.TEXTURE_WRAP_T, sampler.wrapT);
/*
if (sSupports3DTextures)
{
gl.texParameteri(target, gl.TEXTURE_WRAP_R, sampler.wrapR);
}
*/
if (this.TEXTURE_MAX_ANISOTROPY_EXT)
{
gl.texParameteri(target, this.TEXTURE_MAX_ANISOTROPY_EXT, sampler.maxAnisotropy);
}
}
},
setPass : function setPassFn(pass)
{
var gl = this.gl;
var state = this.state;
// Set renderstates
var renderStatesSet = pass.statesSet;
var renderStates = pass.states;
var numRenderStates = renderStates.length;
var r, renderState;
for (r = 0; r < numRenderStates; r += 1)
{
renderState = renderStates[r];
renderState.set.apply(renderState, renderState.values);
}
// Reset previous renderstates
var renderStatesToReset = state.renderStatesToReset;
var numRenderStatesToReset = renderStatesToReset.length;
for (r = 0; r < numRenderStatesToReset; r += 1)
{
renderState = renderStatesToReset[r];
if (!(renderState.name in renderStatesSet))
{
renderState.reset();
}
}
// Copy set renderstates to be reset later
renderStatesToReset.length = numRenderStates;
for (r = 0; r < numRenderStates; r += 1)
{
renderStatesToReset[r] = renderStates[r];
}
// Reset texture units
var lastMaxTextureUnit = state.lastMaxTextureUnit;
var textureUnits = state.textureUnits;
var currentMaxTextureUnit = pass.numTextureUnits;
if (currentMaxTextureUnit < lastMaxTextureUnit)
{
var u = currentMaxTextureUnit;
do
{
var textureUnit = textureUnits[u];
if (textureUnit.texture)
{
textureUnit.texture = null;
this.bindTextureUnit(u, textureUnit.target, null);
}
u += 1;
}
while (u < lastMaxTextureUnit);
}
state.lastMaxTextureUnit = currentMaxTextureUnit;
var program = pass.glProgram;
if (state.program !== program)
{
state.program = program;
gl.useProgram(program);
}
if (pass.dirty)
{
pass.updateParametersData(this);
}
},
enableClientState : function enableClientStateFn(mask)
{
var gl = this.gl;
var oldMask = this.clientStateMask;
this.clientStateMask = mask;
/*jshint bitwise: false*/
var disableMask = (oldMask & (~mask));
var enableMask = ((~oldMask) & mask);
var n;
if (disableMask)
{
if ((disableMask & 0xff) === 0)
{
disableMask >>= 8;
n = 8;
}
else
{
n = 0;
}
do
{
if (0 !== (0x01 & disableMask))
{
gl.disableVertexAttribArray(n);
}
n += 1;
disableMask >>= 1;
}
while (disableMask);
}
if (enableMask)
{
if ((enableMask & 0xff) === 0)
{
enableMask >>= 8;
n = 8;
}
else
{
n = 0;
}
do
{
if (0 !== (0x01 & enableMask))
{
gl.enableVertexAttribArray(n);
}
n += 1;
enableMask >>= 1;
}
while (enableMask);
}
/*jshint bitwise: true*/
},
setTexture : function setTextureFn(textureUnitIndex, texture, sampler)
{
var state = this.state;
var gl = this.gl;
var textureUnit = state.textureUnits[textureUnitIndex];
var oldgltarget = textureUnit.target;
var oldglobject = textureUnit.texture;
if (texture)
{
var gltarget = texture.target;
var globject = texture.glTexture;
if (oldglobject !== globject ||
oldgltarget !== gltarget)
{
textureUnit.target = gltarget;
textureUnit.texture = globject;
if (state.activeTextureUnit !== textureUnitIndex)
{
state.activeTextureUnit = textureUnitIndex;
gl.activeTexture(gl.TEXTURE0 + textureUnitIndex);
}
if (oldgltarget !== gltarget &&
oldglobject)
{
gl.bindTexture(oldgltarget, null);
}
gl.bindTexture(gltarget, globject);
if (texture.sampler !== sampler)
{
texture.sampler = sampler;
this.setSampler(sampler, gltarget);
}
}
}
else
{
if (oldgltarget &&
oldglobject)
{
textureUnit.target = 0;
textureUnit.texture = null;
if (state.activeTextureUnit !== textureUnitIndex)
{
state.activeTextureUnit = textureUnitIndex;
gl.activeTexture(gl.TEXTURE0 + textureUnitIndex);
}
gl.bindTexture(oldgltarget, null);
}
}
},
setProgram : function setProgramFn(program)
{
var state = this.state;
if (state.program !== program)
{
state.program = program;
this.gl.useProgram(program);
}
},
syncState : function syncStateFn()
{
var state = this.state;
var gl = this.gl;
if (state.depthTestEnable)
{
gl.enable(gl.DEPTH_TEST);
}
else
{
gl.disable(gl.DEPTH_TEST);
}
gl.depthFunc(state.depthFunc);
gl.depthMask(state.depthMask);
if (state.blendEnable)
{
gl.enable(gl.BLEND);
}
else
{
gl.disable(gl.BLEND);
}
gl.blendFunc(state.blendSrc, state.blendDst);
if (state.cullFaceEnable)
{
gl.enable(gl.CULL_FACE);
}
else
{
gl.disable(gl.CULL_FACE);
}
gl.cullFace(state.cullFace);
gl.frontFace(state.frontFace);
var colorMask = state.colorMask;
gl.colorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
if (state.stencilTestEnable)
{
gl.enable(gl.STENCIL_TEST);
}
else
{
gl.disable(gl.STENCIL_TEST);
}
gl.stencilFunc(state.stencilFunc, state.stencilRef, state.stencilMask);
gl.stencilOp(state.stencilFail, state.stencilZFail, state.stencilZPass);
if (state.polygonOffsetFillEnable)
{
gl.enable(gl.POLYGON_OFFSET_FILL);
}
else
{
gl.disable(gl.POLYGON_OFFSET_FILL);
}
gl.polygonOffset(state.polygonOffsetFactor, state.polygonOffsetUnits);
gl.lineWidth(state.lineWidth);
gl.activeTexture(gl.TEXTURE0 + state.activeTextureUnit);
var currentBox = this.state.viewportBox;
gl.viewport(currentBox[0], currentBox[1], currentBox[2], currentBox[3]);
currentBox = this.state.scissorBox;
gl.scissor(currentBox[0], currentBox[1], currentBox[2], currentBox[3]);
var currentColor = state.clearColor;
gl.clearColor(currentColor[0], currentColor[1], currentColor[2], currentColor[3]);
gl.clearDepth(state.clearDepth);
gl.clearStencil(state.clearStencil);
},
resetStates : function resetStatesFn()
{
var state = this.state;
var lastMaxTextureUnit = state.lastMaxTextureUnit;
var textureUnits = state.textureUnits;
for (var u = 0; u < lastMaxTextureUnit; u += 1)
{
var textureUnit = textureUnits[u];
if (textureUnit.texture)
{
this.bindTextureUnit(u, textureUnit.target, null);
textureUnit.texture = null;
textureUnit.target = 0;
}
}
},
destroy : function graphicsDeviceDestroyFn()
{
delete this.activeTechnique;
delete this.activeIndexBuffer;
delete this.bindedVertexBuffer;
if (this.immediateVertexBuffer)
{
this.immediateVertexBuffer.destroy();
delete this.immediateVertexBuffer;
}
delete this.gl;
}
};
// Constructor function
WebGLGraphicsDevice.create = function webGLGraphicsDeviceCreateFn(canvas, params)
{
function getAvailableContext(canvas, params, contextList)
{
if (canvas.getContext)
{
var canvasParams = {
alpha: false,
stencil: true,
antialias: false
};
var multisample = params.multisample;
if (multisample !== undefined && 1 < multisample)
{
canvasParams.antialias = true;
}
var numContexts = contextList.length, i;
for (i = 0; i < numContexts; i += 1)
{
try
{
var context = canvas.getContext(contextList[i], canvasParams);
if (context)
{
return context;
}
}
catch (ex)
{
}
}
}
return null;
}
// TODO: Test if we can also use "webkit-3d" and "moz-webgl"
var gl = getAvailableContext(canvas, params, ['webgl', 'experimental-webgl']);
if (!gl)
{
return null;
}
var width = (gl.drawingBufferWidth || canvas.width);
var height = (gl.drawingBufferHeight || canvas.height);
gl.enable(gl.SCISSOR_TEST);
gl.depthRange(0.0, 1.0);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
//gl.hint(gl.GENERATE_MIPMAP_HINT, gl.NICEST);
var gd = new WebGLGraphicsDevice();
gd.gl = gl;
gd.width = width;
gd.height = height;
var extensions = gl.getSupportedExtensions();
if (extensions)
{
extensions = extensions.join(' ');
}
else
{
extensions = '';
}
gd.extensions = extensions;
gd.shadingLanguageVersion = gl.getParameter(gl.SHADING_LANGUAGE_VERSION);
gd.rendererVersion = gl.getParameter(gl.VERSION);
gd.renderer = gl.getParameter(gl.RENDERER);
gd.vendor = gl.getParameter(gl.VENDOR);
if (extensions.indexOf('WEBGL_compressed_texture_s3tc') !== -1)
{
gd.WEBGL_compressed_texture_s3tc = true;
if (extensions.indexOf('WEBKIT_WEBGL_compressed_texture_s3tc') !== -1)
{
gd.compressedTexturesExtension = gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
}
else if (extensions.indexOf('MOZ_WEBGL_compressed_texture_s3tc') !== -1)
{
gd.compressedTexturesExtension = gl.getExtension('MOZ_WEBGL_compressed_texture_s3tc');
}
else
{
gd.compressedTexturesExtension = gl.getExtension('WEBGL_compressed_texture_s3tc');
}
}
else if (extensions.indexOf('WEBKIT_WEBGL_compressed_textures') !== -1)
{
gd.compressedTexturesExtension = gl.getExtension('WEBKIT_WEBGL_compressed_textures');
}
var anisotropyExtension;
if (extensions.indexOf('EXT_texture_filter_anisotropic') !== -1)
{
if (extensions.indexOf('MOZ_EXT_texture_filter_anisotropic') !== -1)
{
anisotropyExtension = gl.getExtension('MOZ_EXT_texture_filter_anisotropic');
}
else if (extensions.indexOf('WEBKIT_EXT_texture_filter_anisotropic') !== -1)
{
anisotropyExtension = gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
}
else
{
anisotropyExtension = gl.getExtension('EXT_texture_filter_anisotropic');
}
}
if (anisotropyExtension)
{
gd.TEXTURE_MAX_ANISOTROPY_EXT = anisotropyExtension.TEXTURE_MAX_ANISOTROPY_EXT;
gd.maxAnisotropy = gl.getParameter(anisotropyExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
}
else
{
gd.maxAnisotropy = 1;
}
gd.PRIMITIVE_POINTS = gl.POINTS;
gd.PRIMITIVE_LINES = gl.LINES;
gd.PRIMITIVE_LINE_LOOP = gl.LINE_LOOP;
gd.PRIMITIVE_LINE_STRIP = gl.LINE_STRIP;
gd.PRIMITIVE_TRIANGLES = gl.TRIANGLES;
gd.PRIMITIVE_TRIANGLE_STRIP = gl.TRIANGLE_STRIP;
gd.PRIMITIVE_TRIANGLE_FAN = gl.TRIANGLE_FAN;
gd.INDEXFORMAT_UBYTE = gl.UNSIGNED_BYTE;
gd.INDEXFORMAT_USHORT = gl.UNSIGNED_SHORT;
gd.INDEXFORMAT_UINT = gl.UNSIGNED_INT;
function getNormalizationScale(format)
{
if (format === gl.BYTE)
{
return 0x7f;
}
else if (format === gl.UNSIGNED_BYTE)
{
return 0xff;
}
else if (format === gl.SHORT)
{
return 0x7fff;
}
else if (format === gl.UNSIGNED_SHORT)
{
return 0xffff;
}
else if (format === gl.INT)
{
return 0x7fffffff;
}
else if (format === gl.UNSIGNED_INT)
{
return 0xffffffff;
}
else //if (format === gl.FLOAT)
{
return 1;
}
}
function makeVertexformat(n, c, s, f, name)
{
var attributeFormat = {
numComponents: c,
stride: s,
componentStride: (s / c),
format: f,
name: name
};
if (n)
{
attributeFormat.normalized = true;
attributeFormat.normalizationScale = getNormalizationScale(f);
}
else
{
attributeFormat.normalized = false;
attributeFormat.normalizationScale = 1;
}
if (typeof DataView !== 'undefined' && 'setFloat32' in DataView.prototype)
{
if (f === gl.BYTE)
{
attributeFormat.typedSetter = DataView.prototype.setInt8;
}
else if (f === gl.UNSIGNED_BYTE)
{
attributeFormat.typedSetter = DataView.prototype.setUint8;
}
else if (f === gl.SHORT)
{
attributeFormat.typedSetter = DataView.prototype.setInt16;
}
else if (f === gl.UNSIGNED_SHORT)
{
attributeFormat.typedSetter = DataView.prototype.setUint16;
}
else if (f === gl.INT)
{
attributeFormat.typedSetter = DataView.prototype.setInt32;
}
else if (f === gl.UNSIGNED_INT)
{
attributeFormat.typedSetter = DataView.prototype.setUint32;
}
else //if (f === gl.FLOAT)
{
attributeFormat.typedSetter = DataView.prototype.setFloat32;
}
}
else
{
if (f === gl.BYTE)
{
attributeFormat.typedArray = Int8Array;
}
else if (f === gl.UNSIGNED_BYTE)
{
attributeFormat.typedArray = Uint8Array;
}
else if (f === gl.SHORT)
{
attributeFormat.typedArray = Int16Array;
}
else if (f === gl.UNSIGNED_SHORT)
{
attributeFormat.typedArray = Uint16Array;
}
else if (f === gl.INT)
{
attributeFormat.typedArray = Int32Array;
}
else if (f === gl.UNSIGNED_INT)
{
attributeFormat.typedArray = Uint32Array;
}
else //if (f === gl.FLOAT)
{
attributeFormat.typedArray = Float32Array;
}
}
return attributeFormat;
}
gd.VERTEXFORMAT_BYTE4 = makeVertexformat(0, 4, 4, gl.BYTE, 'BYTE4');
gd.VERTEXFORMAT_BYTE4N = makeVertexformat(1, 4, 4, gl.BYTE, 'BYTE4N');
gd.VERTEXFORMAT_UBYTE4 = makeVertexformat(0, 4, 4, gl.UNSIGNED_BYTE, 'UBYTE4');
gd.VERTEXFORMAT_UBYTE4N = makeVertexformat(1, 4, 4, gl.UNSIGNED_BYTE, 'UBYTE4N');
gd.VERTEXFORMAT_SHORT2 = makeVertexformat(0, 2, 4, gl.SHORT, 'SHORT2');
gd.VERTEXFORMAT_SHORT2N = makeVertexformat(1, 2, 4, gl.SHORT, 'SHORT2N');
gd.VERTEXFORMAT_SHORT4 = makeVertexformat(0, 4, 8, gl.SHORT, 'SHORT4');
gd.VERTEXFORMAT_SHORT4N = makeVertexformat(1, 4, 8, gl.SHORT, 'SHORT4N');
gd.VERTEXFORMAT_USHORT2 = makeVertexformat(0, 2, 4, gl.UNSIGNED_SHORT, 'USHORT2');
gd.VERTEXFORMAT_USHORT2N = makeVertexformat(1, 2, 4, gl.UNSIGNED_SHORT, 'USHORT2N');
gd.VERTEXFORMAT_USHORT4 = makeVertexformat(0, 4, 8, gl.UNSIGNED_SHORT, 'USHORT4');
gd.VERTEXFORMAT_USHORT4N = makeVertexformat(1, 4, 8, gl.UNSIGNED_SHORT, 'USHORT4N');
gd.VERTEXFORMAT_FLOAT1 = makeVertexformat(0, 1, 4, gl.FLOAT, 'FLOAT1');
gd.VERTEXFORMAT_FLOAT2 = makeVertexformat(0, 2, 8, gl.FLOAT, 'FLOAT2');
gd.VERTEXFORMAT_FLOAT3 = makeVertexformat(0, 3, 12, gl.FLOAT, 'FLOAT3');
gd.VERTEXFORMAT_FLOAT4 = makeVertexformat(0, 4, 16, gl.FLOAT, 'FLOAT4');
gd.DEFAULT_SAMPLER = {
minFilter : gl.LINEAR_MIPMAP_LINEAR,
magFilter : gl.LINEAR,
wrapS : gl.REPEAT,
wrapT : gl.REPEAT,
wrapR : gl.REPEAT,
maxAnisotropy : 1
};
gd.cachedSamplers = {};
var maxTextureUnit = 1;
var maxUnit = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
if (maxTextureUnit < maxUnit)
{
maxTextureUnit = maxUnit;
}
maxUnit = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
if (maxTextureUnit < maxUnit)
{
maxTextureUnit = maxUnit;
}
var textureUnits = [];
textureUnits.length = maxTextureUnit;
for (var t = 0; t < maxTextureUnit; t += 1)
{
textureUnits[t] = {};
}
var defaultDepthFunc = gl.LEQUAL;
var defaultBlendFuncSrc = gl.SRC_ALPHA;
var defaultBlendFuncDst = gl.ONE_MINUS_SRC_ALPHA;
var defaultCullFace = gl.BACK;
var defaultFrontFace = gl.CCW;
var defaultStencilFunc = gl.ALWAYS;
var defaultStencilOp = gl.KEEP;
var currentState = {
depthTestEnable : true,
blendEnable : false,
cullFaceEnable : true,
stencilTestEnable : false,
polygonOffsetFillEnable : false,
depthMask : true,
depthFunc : defaultDepthFunc,
blendSrc : defaultBlendFuncSrc,
blendDst : defaultBlendFuncDst,
cullFace : defaultCullFace,
frontFace : defaultFrontFace,
colorMask : [true, true, true, true],
stencilFunc : defaultStencilFunc,
stencilRef : 0,
stencilMask : 0xffffffff,
stencilFail : defaultStencilOp,
stencilZFail : defaultStencilOp,
stencilZPass : defaultStencilOp,
polygonOffsetFactor : 0,
polygonOffsetUnits : 0,
lineWidth : 1,
renderStatesToReset : [],
viewportBox : [0, 0, width, height],
scissorBox : [0, 0, width, height],
clearColor : [0, 0, 0, 1],
clearDepth : 1.0,
clearStencil : 0,
activeTextureUnit : 0,
maxTextureUnit : maxTextureUnit,
lastMaxTextureUnit: 0,
textureUnits : textureUnits,
program : null
};
gd.state = currentState;
// State handlers
function setDepthTestEnable(enable)
{
if (currentState.depthTestEnable !== enable)
{
currentState.depthTestEnable = enable;
if (enable)
{
gl.enable(gl.DEPTH_TEST);
}
else
{
gl.disable(gl.DEPTH_TEST);
}
}
}
function setDepthFunc(func)
{
if (currentState.depthFunc !== func)
{
currentState.depthFunc = func;
gl.depthFunc(func);
}
}
function setDepthMask(enable)
{
if (currentState.depthMask !== enable)
{
currentState.depthMask = enable;
gl.depthMask(enable);
}
}
function setBlendEnable(enable)
{
if (currentState.blendEnable !== enable)
{
currentState.blendEnable = enable;
if (enable)
{
gl.enable(gl.BLEND);
}
else
{
gl.disable(gl.BLEND);
}
}
}
function setBlendFunc(src, dst)
{
if (currentState.blendSrc !== src || currentState.blendDst !== dst)
{
currentState.blendSrc = src;
currentState.blendDst = dst;
gl.blendFunc(src, dst);
}
}
function setCullFaceEnable(enable)
{
if (currentState.cullFaceEnable !== enable)
{
currentState.cullFaceEnable = enable;
if (enable)
{
gl.enable(gl.CULL_FACE);
}
else
{
gl.disable(gl.CULL_FACE);
}
}
}
function setCullFace(face)
{
if (currentState.cullFace !== face)
{
currentState.cullFace = face;
gl.cullFace(face);
}
}
function setFrontFace(face)
{
if (currentState.frontFace !== face)
{
currentState.frontFace = face;
gl.frontFace(face);
}
}
function setColorMask(mask0, mask1, mask2, mask3)
{
var colorMask = currentState.colorMask;
if (colorMask[0] !== mask0 ||
colorMask[1] !== mask1 ||
colorMask[2] !== mask2 ||
colorMask[3] !== mask3)
{
colorMask[0] = mask0;
colorMask[1] = mask1;
colorMask[2] = mask2;
colorMask[3] = mask3;
gl.colorMask(mask0, mask1, mask2, mask3);
}
}
function setStencilTestEnable(enable)
{
if (currentState.stencilTestEnable !== enable)
{
currentState.stencilTestEnable = enable;
if (enable)
{
gl.enable(gl.STENCIL_TEST);
}
else
{
gl.disable(gl.STENCIL_TEST);
}
}
}
function setStencilFunc(stencilFunc, stencilRef, stencilMask)
{
if (currentState.stencilFunc !== stencilFunc ||
currentState.stencilRef !== stencilRef ||
currentState.stencilMask !== stencilMask)
{
currentState.stencilFunc = stencilFunc;
currentState.stencilRef = stencilRef;
currentState.stencilMask = stencilMask;
gl.stencilFunc(stencilFunc, stencilRef, stencilMask);
}
}
function setStencilOp(stencilFail, stencilZfail, stencilZpass)
{
if (currentState.stencilFail !== stencilFail ||
currentState.stencilZFail !== stencilZfail ||
currentState.stencilZPass !== stencilZpass)
{
currentState.stencilFail = stencilFail;
currentState.stencilZFail = stencilZfail;
currentState.stencilZPass = stencilZpass;
gl.stencilOp(stencilFail, stencilZfail, stencilZpass);
}
}
function setPolygonOffsetFillEnable(enable)
{
if (currentState.polygonOffsetFillEnable !== enable)
{
currentState.polygonOffsetFillEnable = enable;
if (enable)
{
gl.enable(gl.POLYGON_OFFSET_FILL);
}
else
{
gl.disable(gl.POLYGON_OFFSET_FILL);
}
}
}
function setPolygonOffset(factor, units)
{
if (currentState.polygonOffsetFactor !== factor ||
currentState.polygonOffsetUnits !== units)
{
currentState.polygonOffsetFactor = factor;
currentState.polygonOffsetUnits = units;
gl.polygonOffset(factor, units);
}
}
function setLineWidth(lineWidth)
{
if (currentState.lineWidth !== lineWidth)
{
currentState.lineWidth = lineWidth;
gl.lineWidth(lineWidth);
}
}
function resetDepthTestEnable()
{
//setDepthTestEnable(true);
if (!currentState.depthTestEnable)
{
currentState.depthTestEnable = true;
gl.enable(gl.DEPTH_TEST);
}
}
function resetDepthFunc()
{
//setDepthFunc(defaultDepthFunc);
var func = defaultDepthFunc;
if (currentState.depthFunc !== func)
{
currentState.depthFunc = func;
gl.depthFunc(func);
}
}
function resetDepthMask()
{
//setDepthMask(true);
if (!currentState.depthMask)
{
currentState.depthMask = true;
gl.depthMask(true);
}
}
function resetBlendEnable()
{
//setBlendEnable(false);
if (currentState.blendEnable)
{
currentState.blendEnable = false;
gl.disable(gl.BLEND);
}
}
function resetBlendFunc()
{
//setBlendFunc(defaultBlendFuncSrc, defaultBlendFuncDst);
var src = defaultBlendFuncSrc;
var dst = defaultBlendFuncDst;
if (currentState.blendSrc !== src || currentState.blendDst !== dst)
{
currentState.blendSrc = src;
currentState.blendDst = dst;
gl.blendFunc(src, dst);
}
}
function resetCullFaceEnable()
{
//setCullFaceEnable(true);
if (!currentState.cullFaceEnable)
{
currentState.cullFaceEnable = true;
gl.enable(gl.CULL_FACE);
}
}
function resetCullFace()
{
//setCullFace(defaultCullFace);
var face = defaultCullFace;
if (currentState.cullFace !== face)
{
currentState.cullFace = face;
gl.cullFace(face);
}
}
function resetFrontFace()
{
//setFrontFace(defaultFrontFace);
var face = defaultFrontFace;
if (currentState.frontFace !== face)
{
currentState.frontFace = face;
gl.frontFace(face);
}
}
function resetColorMask()
{
//setColorMask(true, true, true, true);
var colorMask = currentState.colorMask;
if (colorMask[0] !== true ||
colorMask[1] !== true ||
colorMask[2] !== true ||
colorMask[3] !== true)
{
colorMask[0] = true;
colorMask[1] = true;
colorMask[2] = true;
colorMask[3] = true;
gl.colorMask(true, true, true, true);
}
}
function resetStencilTestEnable()
{
//setStencilTestEnable(false);
if (currentState.stencilTestEnable)
{
currentState.stencilTestEnable = false;
gl.disable(gl.STENCIL_TEST);
}
}
function resetStencilFunc()
{
//setStencilFunc(defaultStencilFunc, 0, 0xffffffff);
var stencilFunc = defaultStencilFunc;
if (currentState.stencilFunc !== stencilFunc ||
currentState.stencilRef !== 0 ||
currentState.stencilMask !== 0xffffffff)
{
currentState.stencilFunc = stencilFunc;
currentState.stencilRef = 0;
currentState.stencilMask = 0xffffffff;
gl.stencilFunc(stencilFunc, 0, 0xffffffff);
}
}
function resetStencilOp()
{
//setStencilOp(defaultStencilOp, defaultStencilOp, defaultStencilOp);
var stencilOp = defaultStencilOp;
if (currentState.stencilFail !== stencilOp ||
currentState.stencilZFail !== stencilOp ||
currentState.stencilZPass !== stencilOp)
{
currentState.stencilFail = stencilOp;
currentState.stencilZFail = stencilOp;
currentState.stencilZPass = stencilOp;
gl.stencilOp(stencilOp, stencilOp, stencilOp);
}
}
function resetPolygonOffsetFillEnable()
{
//setPolygonOffsetFillEnable(false);
if (currentState.polygonOffsetFillEnable)
{
currentState.polygonOffsetFillEnable = false;
gl.disable(gl.POLYGON_OFFSET_FILL);
}
}
function resetPolygonOffset()
{
//setPolygonOffset(0, 0);
if (currentState.polygonOffsetFactor !== 0 ||
currentState.polygonOffsetUnits !== 0)
{
currentState.polygonOffsetFactor = 0;
currentState.polygonOffsetUnits = 0;
gl.polygonOffset(0, 0);
}
}
function resetLineWidth()
{
//setLineWidth(1);
if (currentState.lineWidth !== 1)
{
currentState.lineWidth = 1;
gl.lineWidth(1);
}
}
function parseBoolean(state)
{
if (typeof state === 'number')
{
return (state ? true : false);
}
if (typeof state !== 'boolean')
{
// TODO
return null;
}
return [state];
}
function parseEnum(state)
{
if (typeof state !== 'number')
{
// TODO
return null;
}
return [state];
}
function parseEnum2(state)
{
if (typeof state === 'object')
{
var value0 = state[0], value1 = state[1];
if (typeof value0 !== 'number')
{
// TODO
return null;
}
if (typeof value1 !== 'number')
{
// TODO
return null;
}
return [value0, value1];
}
return null;
}
function parseEnum3(state)
{
if (typeof state === 'object')
{
var value0 = state[0], value1 = state[1], value2 = state[2];
if (typeof value0 !== 'number')
{
// TODO
return null;
}
if (typeof value1 !== 'number')
{
// TODO
return null;
}
if (typeof value2 !== 'number')
{
// TODO
return null;
}
return [value0, value1, value2];
}
return null;
}
function parseFloat(state)
{
if (typeof state !== 'number')
{
// TODO
return null;
}
return [state];
}
function parseFloat2(state)
{
if (typeof state === 'object')
{
var value0 = state[0], value1 = state[1];
if (typeof value0 !== 'number')
{
// TODO
return null;
}
if (typeof value1 !== 'number')
{
// TODO
return null;
}
return [value0, value1];
}
return null;
}
function parseColorMask(state)
{
if (typeof state === 'object')
{
var value0 = state[0], value1 = state[1], value2 = state[2], value3 = state[3];
if (typeof value0 !== 'number')
{
// TODO
return null;
}
if (typeof value1 !== 'number')
{
// TODO
return null;
}
if (typeof value2 !== 'number')
{
// TODO
return null;
}
if (typeof value3 !== 'number')
{
// TODO
return null;
}
return [value0, value1, value2, value3];
}
return null;
}
var stateHandlers = {};
function addStateHandler(name, sf, rf, pf, dv)
{
stateHandlers[name] = {
set: sf,
reset: rf,
parse: pf,
defaultValues: dv
};
}
addStateHandler("DepthTestEnable", setDepthTestEnable, resetDepthTestEnable, parseBoolean, [true]);
addStateHandler("DepthFunc", setDepthFunc, resetDepthFunc, parseEnum, [defaultDepthFunc]);
addStateHandler("DepthMask", setDepthMask, resetDepthMask, parseBoolean, [true]);
addStateHandler("BlendEnable", setBlendEnable, resetBlendEnable, parseBoolean, [false]);
addStateHandler("BlendFunc", setBlendFunc, resetBlendFunc, parseEnum2, [defaultBlendFuncSrc, defaultBlendFuncDst]);
addStateHandler("CullFaceEnable", setCullFaceEnable, resetCullFaceEnable, parseBoolean, [true]);
addStateHandler("CullFace", setCullFace, resetCullFace, parseEnum, [defaultCullFace]);
addStateHandler("FrontFace", setFrontFace, resetFrontFace, parseEnum, [defaultFrontFace]);
addStateHandler("ColorMask", setColorMask, resetColorMask, parseColorMask, [true, true, true, true]);
addStateHandler("StencilTestEnable", setStencilTestEnable, resetStencilTestEnable, parseBoolean, [false]);
addStateHandler("StencilFunc", setStencilFunc, resetStencilFunc, parseEnum3, [defaultStencilFunc, 0, 0xffffffff]);
addStateHandler("StencilOp", setStencilOp, resetStencilOp, parseEnum3, [defaultStencilOp, defaultStencilOp, defaultStencilOp]);
addStateHandler("PolygonOffsetFillEnable", setPolygonOffsetFillEnable, resetPolygonOffsetFillEnable, parseBoolean, [false]);
addStateHandler("PolygonOffset", setPolygonOffset, resetPolygonOffset, parseFloat2, [0, 0]);
addStateHandler("LineWidth", setLineWidth, resetLineWidth, parseFloat, [1]);
gd.stateHandlers = stateHandlers;
gd.syncState();
gd.videoRam = 0;
gd.desktopWidth = window.screen.width;
gd.desktopHeight = window.screen.height;
if (Object.defineProperty)
{
Object.defineProperty(gd, "fullscreen", {
get : function getFullscreenFn() {
return (document.fullscreenEnabled ||
document.mozFullScreen ||
document.webkitIsFullScreen ||
false);
},
set : function setFullscreenFn(newFullscreen) {
gd.requestFullScreen(newFullscreen);
},
enumerable : true,
configurable : false
});
gd.checkFullScreen = function dummyCheckFullScreenFn()
{
};
}
else
{
gd.fullscreen = false;
gd.oldFullscreen = false;
}
gd.clientStateMask = 0;
gd.attributeMask = 0;
gd.activeTechnique = null;
gd.activeIndexBuffer = null;
gd.bindedVertexBuffer = 0;
gd.activeRenderTarget = null;
gd.immediateVertexBuffer = gd.createVertexBuffer({
numVertices: (256 * 1024 / 16),
attributes: ['FLOAT4'],
dynamic: true,
'transient': true
});
gd.immediatePrimitive = -1;
gd.immediateSemantics = [];
gd.fps = 0;
gd.numFrames = 0;
gd.previousFrameTime = TurbulenzEngine.getTime();
return gd;
};