// 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; };