mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-22 02:06:03 +08:00
6209 lines
181 KiB
JavaScript
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;
|
|
};
|