UNPKG

odin

Version:

Node.js Canvas/WebGL Javascript Game Framework

1,293 lines (1,005 loc) 81.4 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: core/renderer/renderer.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: core/renderer/renderer.js</h1> <section> <article> <pre class="prettyprint source"><code>if (typeof(define) !== "function") { var define = require("amdefine")(module); } define([ "odin/base/event_emitter", "odin/base/device", "odin/base/dom", "odin/base/util", "odin/math/mathf", "odin/math/color", "odin/math/vec3", "odin/math/mat4", "odin/core/enums", "odin/core/game/log", "odin/core/components/particle_system/emitter", "odin/core/renderer/shader_chunks", "odin/core/renderer/canvas" ], function(EventEmitter, Device, Dom, util, Mathf, Color, Vec3, Mat4, Enums, Log, Emitter, ShaderChunks, Canvas) { "use strict"; var Blending = Enums.Blending, ShadowMapType = Enums.ShadowMapType, CullFace = Enums.CullFace, Side = Enums.Side, LightType = Enums.LightType, Shading = Enums.Shading, FilterMode = Enums.FilterMode, TextureFormat = Enums.TextureFormat, TextureWrap = Enums.TextureWrap, getWebGLContext = Dom.getWebGLContext, addEvent = Dom.addEvent, removeEvent = Dom.removeEvent, createProgram = Dom.createProgram, parseUniformsAttributes = Dom.parseUniformsAttributes, getUniformsAttributes = Dom.getUniformsAttributes, merge = util.merge, copy = util.copy, max = Math.max, floor = Math.floor, clamp = Mathf.clamp, isPowerOfTwo = Mathf.isPowerOfTwo, defineProperty = Object.defineProperty; function Renderer(opts) { opts || (opts = {}); EventEmitter.call(this); var _this = this, _projScreenMatrix = new Mat4, _mat4 = new Mat4, _vector3 = new Vec3, _gl, _extensions, _canvas, _element, _context = false, _programs = []; this.attributes = merge(opts.attributes || {}, { alpha: true, antialias: true, depth: true, premultipliedAlpha: true, preserveDrawingBuffer: false, stencil: true }); this.autoClear = opts.autoClear != undefined ? opts.autoClear : true; this.autoClearColor = opts.autoClearColor != undefined ? opts.autoClearColor : true; this.autoClearDepth = opts.autoClearDepth != undefined ? opts.autoClearDepth : true; this.autoClearStencil = opts.autoClearStencil != undefined ? opts.autoClearStencil : true; this.init = function(canvas) { if (_canvas) this.clear(); _canvas = canvas; _element = canvas.element; _lastUniforms = {}; initGL(); _context = true; setDefaultGLState(); addEvent(_element, "webglcontextlost", handleWebGLContextLost, this); addEvent(_element, "webglcontextrestored", handleWebGLContextRestored, this); return this; }; this.clear = function() { if (!_canvas) return this; removeEvent(element, "webglcontextlost", handleWebGLContextLost, this); removeEvent(element, "webglcontextrestored", handleWebGLContextRestored, this); _gl = undefined _canvas = undefined; _element = undefined; _context = false; _programs.length = 0; _extensions = undefined; _precision = "highp"; _maxAnisotropy = 0; _maxTextures = 0; _maxVertexTextures = 0; _maxTextureSize = 0; _maxCubeTextureSize = 0; _maxRenderBufferSize = 0; _maxUniforms = 0; _maxVaryings = 0; _maxAttributes = 0; _lastCamera = undefined; _lastResizeFn = undefined; _lastScene = undefined; _viewportX = 0; _viewportY = 0; _viewportWidth = 1; _viewportHeight = 1; _lastBlending = -1; _lastClearColor.set(0, 0, 0); _lastCullFace = -1; _lastDepthTest = -1; _lastDepthWrite = -1; _lastLineWidth = -1; _lastUniforms = undefined; _lastBuffer = undefined; _lastProgram = undefined; _enabledAttributes = undefined; return this; }; defineProperty(this, "gl", { get: function() { return _gl; } }); defineProperty(this, "canvas", { get: function() { return _canvas; } }); defineProperty(this, "element", { get: function() { return _element; } }); defineProperty(this, "precision", { get: function() { return _precision; } }); defineProperty(this, "maxAnisotropy", { get: function() { return _maxAnisotropy; } }); defineProperty(this, "maxTextures", { get: function() { return _maxTextures; } }); defineProperty(this, "maxVertexTextures", { get: function() { return _maxVertexTextures; } }); defineProperty(this, "maxTextureSize", { get: function() { return _maxTextureSize; } }); defineProperty(this, "maxCubeTextureSize", { get: function() { return _maxCubeTextureSize; } }); defineProperty(this, "maxRenderBufferSize", { get: function() { return _maxRenderBufferSize; } }); defineProperty(this, "maxUniforms", { get: function() { return _maxUniforms; } }); defineProperty(this, "maxVaryings", { get: function() { return _maxVaryings; } }); defineProperty(this, "maxAttributes", { get: function() { return _maxAttributes; } }); var _viewportX = 0, _viewportY = 0, _viewportWidth = 1, _viewportHeight = 1; function setViewport(x, y, width, height) { x || (x = 0); y || (y = 0); width || (width = _canvas.pixelWidth); height || (height = _canvas.pixelHeight); if (_viewportX !== x || _viewportY !== y || _viewportWidth !== width || _viewportHeight !== height) { _viewportX = x; _viewportY = y; _viewportWidth = width; _viewportHeight = height; _gl.viewport(x, y, width, height); } } this.setViewport = setViewport; var _lastDepthTest = -1; function setDepthTest(depthTest) { if (_lastDepthTest !== depthTest) { if (depthTest) { _gl.enable(_gl.DEPTH_TEST); } else { _gl.disable(_gl.DEPTH_TEST); } _lastDepthTest = depthTest; } } this.setDepthTest = setDepthTest; var _lastDepthWrite = -1; function setDepthWrite(depthWrite) { if (_lastDepthWrite !== depthWrite) { _gl.depthMask(depthWrite); _lastDepthWrite = depthWrite; } } this.setDepthWrite = setDepthWrite; var _lastLineWidth = 1; function setLineWidth(width) { if (_lastLineWidth !== width) { _gl.lineWidth(width); _lastLineWidth = width; } } this.setLineWidth = setLineWidth; var _lastCullFace = -1, _cullFaceDisabled = true; function setCullFace(cullFace) { if (_lastCullFace !== cullFace) { if (!_lastCullFace || _lastCullFace === CullFace.None) _cullFaceDisabled = true; if (cullFace === CullFace.Front) { if (_cullFaceDisabled) _gl.enable(_gl.CULL_FACE); _gl.cullFace(_gl.FRONT); } else if (cullFace === CullFace.Back) { if (_cullFaceDisabled) _gl.enable(_gl.CULL_FACE); _gl.cullFace(_gl.BACK); } else if (cullFace === CullFace.FrontBack) { if (_cullFaceDisabled) _gl.enable(_gl.CULL_FACE); _gl.cullFace(_gl.FRONT_AND_BACK); } else { _gl.disable(_gl.CULL_FACE); _lastCullFace = CullFace.None; return; } _lastCullFace = cullFace; } } this.setCullFace = setCullFace; var _lastBlending = -1; function setBlending(blending) { if (blending !== _lastBlending) { if (blending === Blending.None) { _gl.disable(_gl.BLEND); } else if (blending === Blending.Additive) { _gl.enable(_gl.BLEND); _gl.blendEquation(_gl.FUNC_ADD); _gl.blendFunc(_gl.SRC_ALPHA, _gl.ONE); } else if (blending === Blending.Subtractive) { _gl.enable(_gl.BLEND); _gl.blendEquation(_gl.FUNC_ADD); _gl.blendFunc(_gl.ZERO, _gl.ONE_MINUS_SRC_COLOR); } else if (blending === Blending.Muliply) { _gl.enable(_gl.BLEND); _gl.blendEquation(_gl.FUNC_ADD); _gl.blendFunc(_gl.ZERO, _gl.SRC_COLOR); } else if (blending === Blending.Default) { _gl.enable(_gl.BLEND); _gl.blendEquationSeparate(_gl.FUNC_ADD, _gl.FUNC_ADD); _gl.blendFuncSeparate(_gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA); _lastBlending = Blending.Default; return; } _lastBlending = blending; } } this.setBlending = setBlending; function setScissor(x, y, width, height) { _gl.scissor(x, y, width, height); }; this.setScissor = setScissor; var _clearColor = new Color, _clearAlpha = 1; function setClearColor(color, alpha) { alpha || (alpha = 1); if (!_clearColor.equals(color) || alpha !== _clearAlpha) { _clearColor.copy(color); _clearAlpha = alpha; this.context.clearColor(_clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha); } } this.setClearColor = setClearColor; function clearCanvas(color, depth, stencil) { var bits = 0; if (color === undefined || color) bits |= _gl.COLOR_BUFFER_BIT; if (depth === undefined || depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil === undefined || stencil) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear(bits); } this.clearCanvas = clearCanvas; function clearColor() { _gl.clear(_gl.COLOR_BUFFER_BIT); } this.clearColor = clearColor; function clearDepth() { _gl.clear(_gl.DEPTH_BUFFER_BIT); } this.clearDepth = clearDepth; function clearStencil() { _gl.clear(_gl.STENCIL_BUFFER_BIT); } this.clearStencil = clearStencil; var _lastUniforms; function setUniform(force, location, value, key, type, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (type === "int") { if (force || state !== value) { _gl.uniform1i(location, value); lastUniforms[key] = value; } } else if (type === "float") { if (force || state !== value) { _gl.uniform1f(location, value); lastUniforms[key] = value; } } else if (type === "vec2") { if (force || !state || (state.x !== value.x || state.y !== value.y)) { _gl.uniform2f(location, value.x, value.y); state || (lastUniforms[key] = state = value.clone()); state.x = value.x; state.y = value.y; } } else if (type === "vec3") { if (force || !state || (state.x !== value.x || state.y !== value.y || state.z !== value.z)) { _gl.uniform3f(location, value.x, value.y, value.z); state || (lastUniforms[key] = state = value.clone()); state.x = value.x; state.y = value.y; state.z = value.z; } } else if (type === "vec4") { if (force || !state || (state.x !== value.x || state.y !== value.y || state.z !== value.z || state.w !== value.w)) { _gl.uniform3f(location, value.x, value.y, value.z, value.w); state || (lastUniforms[key] = state = value.clone()); state.x = value.x; state.y = value.y; state.z = value.z; state.w = value.w; } } else if (type === "mat2") { if (force || !state || !state.equals(value)) { _gl.uniformMatrix2fv(force, location, false, value.elements); (state || (lastUniforms[key] = state = value.clone())).copy(value); } } else if (type === "mat3") { if (force || !state || !state.equals(value)) { _gl.uniformMatrix3fv(force, location, false, value.elements); (state || (lastUniforms[key] = state = value.clone())).copy(value); } } else if (type === "mat4") { if (force || !state || !state.equals(value)) { _gl.uniformMatrix4fv(force, location, false, value.elements); (state || (lastUniforms[key] = state = value.clone())).copy(value); } } else if (type === "sampler2D") { setTexture(force, location, value, key); } else if (type === "samplerCube") { setTextureCube(force, location, value, key); } } function setUniform1i(force, location, value, key, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (force || state !== value) { _gl.uniform1i(location, value); lastUniforms[key] = value; } } function setUniform1f(force, location, value, key, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (force || state !== value) { _gl.uniform1f(location, value); lastUniforms[key] = value; } } function setUniform2f(force, location, value, key, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (force || !state || (state.x !== value.x || state.y !== value.y)) { _gl.uniform2f(location, value.x, value.y); state || (lastUniforms[key] = state = value.clone()); state.x = value.x; state.y = value.y; } } function setUniform3f(force, location, value, key, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (force || !state || (state.x !== value.x || state.y !== value.y || state.z !== value.z)) { _gl.uniform3f(location, value.x, value.y, value.z); state || (lastUniforms[key] = state = value.clone()); state.x = value.x; state.y = value.y; state.z = value.z; } } function setUniform4f(force, location, value, key, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (force || !state || (state.x !== value.x || state.y !== value.y || state.z !== value.z || state.w !== value.w)) { _gl.uniform4f(location, value.x, value.y, value.z, value.w); state || (lastUniforms[key] = state = value.clone()); state.x = value.x; state.y = value.y; state.z = value.z; state.w = value.w; } } function setUniformMatrix2fv(force, location, value, key, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (force || !state || !state.equals(value)) { _gl.uniformMatrix2fv(location, false, value.elements); (state || (lastUniforms[key] = state = value.clone())).copy(value); } } function setUniformMatrix3fv(force, location, value, key, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (force || !state || !state.equals(value)) { _gl.uniformMatrix3fv(location, false, value.elements); (state || (lastUniforms[key] = state = value.clone())).copy(value); } } function setUniformMatrix4fv(force, location, value, key, isArray, index) { var lastUniforms = _lastUniforms, state; if (isArray) { lastUniforms = lastUniforms[key] || (lastUniforms[key] = []); key = index; } state = lastUniforms[key]; if (force || !state || !state.equals(value)) { _gl.uniformMatrix4fv(location, false, value.elements); (state || (lastUniforms[key] = state = value.clone())).copy(value); } } var _textureIndex = 0; function setTexture(force, location, texture, key) { if (!texture || !texture.raw) return; var index, glTexture; if (_textureIndex >= _maxTextures) Log.warn("Renderer setTexure: using " + _textureIndex + " texture units, GPU only supports " + _maxTextures); if (!texture.needsUpdate && (glTexture = texture._webgl)) { index = _textureIndex++; _gl.activeTexture(_gl.TEXTURE0 + index); _gl.bindTexture(_gl.TEXTURE_2D, glTexture); setUniform1i(force, location, index, key); return; } glTexture = texture._webgl || (texture._webgl = _gl.createTexture()); index = _textureIndex++; var raw = texture.raw, TFA = _extensions.EXT_texture_filter_anisotropic, isPOT = isPowerOfTwo(raw.width) && isPowerOfTwo(raw.height), anisotropy = clamp(texture.anisotropy || 1, 1, _maxAnisotropy), TEXTURE_2D = _gl.TEXTURE_2D, mipmap = texture.mipmap, filter = texture.filter, format = texture.format, wrap = texture.wrap, WRAP, MAG_FILTER, MIN_FILTER, FORMAT; if (filter === FilterMode.None) { MAG_FILTER = _gl.NEAREST; if (mipmap && isPOT) { MIN_FILTER = _gl.LINEAR_MIPMAP_NEAREST; } else { MIN_FILTER = _gl.NEAREST; } } else { //FilterMode.Linear MAG_FILTER = _gl.LINEAR; if (mipmap && isPOT) { MIN_FILTER = _gl.LINEAR_MIPMAP_LINEAR; } else { MIN_FILTER = _gl.LINEAR; } } if (format === TextureFormat.RGB) { FORMAT = _gl.RGB; } else if (format === TextureFormat.RGBA) { FORMAT = _gl.RGBA; } else if (format === TextureFormat.LuminanceAlpha) { FORMAT = _gl.LUMINANCE_ALPHA; } else if (format === TextureFormat.Luminance) { FORMAT = _gl.LUMINANCE; } else if (format === TextureFormat.Alpha) { FORMAT = _gl.ALPHA; } if (wrap === TextureWrap.Clamp) { WRAP = _gl.CLAMP_TO_EDGE; } else if (wrap === TextureWrap.MirrorRepeat) { WRAP = isPOT ? _gl.MIRRORED_REPEAT : _gl.CLAMP_TO_EDGE; } else { //TextureWrap.Repeat WRAP = isPOT ? _gl.REPEAT : _gl.CLAMP_TO_EDGE; } _gl.activeTexture(_gl.TEXTURE0 + index); _gl.bindTexture(TEXTURE_2D, glTexture); _gl.uniform1i(location, index); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ? 1 : 0); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ? 1 : 0); _gl.texImage2D(TEXTURE_2D, 0, FORMAT, FORMAT, _gl.UNSIGNED_BYTE, clampToMaxSize(raw, _maxTextureSize)); _gl.texParameteri(TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, MAG_FILTER); _gl.texParameteri(TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, MIN_FILTER); _gl.texParameteri(TEXTURE_2D, _gl.TEXTURE_WRAP_S, WRAP); _gl.texParameteri(TEXTURE_2D, _gl.TEXTURE_WRAP_T, WRAP); if (TFA) _gl.texParameterf(TEXTURE_2D, TFA.TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); if (mipmap && isPOT) _gl.generateMipmap(TEXTURE_2D); texture.needsUpdate = false; } function setTextureCube(force, location, cubeTexture, key) { if (!cubeTexture || !cubeTexture.raw) return; var glTexture = cubeTexture._webgl, index; if (_textureIndex >= _maxTextures) Log.warn("Renderer setTexure: using " + _textureIndex + " texture units, GPU only supports " + _maxTextures); if (!cubeTexture.needsUpdate && glTexture) { index = _textureIndex++; _gl.activeTexture(_gl.TEXTURE0 + index); _gl.bindTexture(_gl.TEXTURE_CUBE_MAP, glTexture); setUniform1i(force, location, index, key); return; } glTexture = cubeTexture._webgl || (cubeTexture._webgl = _gl.createTexture()); index = _textureIndex++; var raw = cubeTexture.raw, TFA = _extensions.EXT_texture_filter_anisotropic, first = raw[0], isPOT = isPowerOfTwo(first.width) && isPowerOfTwo(first.height), anisotropy = clamp(cubeTexture.anisotropy || 1, 1, _maxAnisotropy), TEXTURE_CUBE_MAP = _gl.TEXTURE_CUBE_MAP, TEXTURE_CUBE_MAP_POSITIVE_X = _gl.TEXTURE_CUBE_MAP_POSITIVE_X, UNSIGNED_BYTE = _gl.UNSIGNED_BYTE, mipmap = cubeTexture.mipmap, filter = cubeTexture.filter, format = cubeTexture.format, wrap = cubeTexture.wrap, WRAP, MAG_FILTER, MIN_FILTER, FORMAT, current, i; if (filter === FilterMode.None) { MAG_FILTER = _gl.NEAREST; if (mipmap && isPOT) { MIN_FILTER = _gl.LINEAR_MIPMAP_NEAREST; } else { MIN_FILTER = _gl.NEAREST; } } else { //FilterMode.Linear MAG_FILTER = _gl.LINEAR; if (mipmap && isPOT) { MIN_FILTER = _gl.LINEAR_MIPMAP_LINEAR; } else { MIN_FILTER = _gl.LINEAR; } } if (format === TextureFormat.RGB) { FORMAT = _gl.RGB; } else if (format === TextureFormat.RGBA) { FORMAT = _gl.RGBA; } else if (format === TextureFormat.LuminanceAlpha) { FORMAT = _gl.LUMINANCE_ALPHA; } else if (format === TextureFormat.Luminance) { FORMAT = _gl.LUMINANCE; } else if (format === TextureFormat.Alpha) { FORMAT = _gl.ALPHA; } if (wrap === TextureWrap.Clamp) { WRAP = _gl.CLAMP_TO_EDGE; } else { //TextureWrap.Repeat WRAP = isPOT ? _gl.REPEAT : _gl.CLAMP_TO_EDGE; } _gl.activeTexture(_gl.TEXTURE0 + index); _gl.bindTexture(TEXTURE_CUBE_MAP, glTexture); _gl.uniform1i(location, index); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, cubeTexture.flipY ? 1 : 0); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, cubeTexture.premultiplyAlpha ? 1 : 0); _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, FORMAT, FORMAT, UNSIGNED_BYTE, clampToMaxSize(raw[0], _maxCubeTextureSize)); _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, FORMAT, FORMAT, UNSIGNED_BYTE, clampToMaxSize(raw[1], _maxCubeTextureSize)); _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, FORMAT, FORMAT, UNSIGNED_BYTE, clampToMaxSize(raw[2], _maxCubeTextureSize)); _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, FORMAT, FORMAT, UNSIGNED_BYTE, clampToMaxSize(raw[3], _maxCubeTextureSize)); _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, FORMAT, FORMAT, UNSIGNED_BYTE, clampToMaxSize(raw[4], _maxCubeTextureSize)); _gl.texImage2D(_gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, FORMAT, FORMAT, UNSIGNED_BYTE, clampToMaxSize(raw[5], _maxCubeTextureSize)); _gl.texParameteri(TEXTURE_CUBE_MAP, _gl.TEXTURE_MAG_FILTER, MAG_FILTER); _gl.texParameteri(TEXTURE_CUBE_MAP, _gl.TEXTURE_MIN_FILTER, MIN_FILTER); _gl.texParameteri(TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_S, WRAP); _gl.texParameteri(TEXTURE_CUBE_MAP, _gl.TEXTURE_WRAP_T, WRAP); if (TFA) _gl.texParameterf(TEXTURE_CUBE_MAP, TFA.TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); if (mipmap && isPOT) _gl.generateMipmap(TEXTURE_CUBE_MAP); cubeTexture.needsUpdate = false; } function clampToMaxSize(image, maxSize) { if (image.height &lt;= maxSize && image.width &lt;= maxSize) return image; var maxDim = 1 / max(image.width, image.height), newWidth = floor(image.width * maxSize * maxDim), newHeight = floor(image.height * maxSize * maxDim), canvas = document.createElement("canvas"), ctx = canvas.getContext("2d"); canvas.width = newWidth; canvas.height = newHeight; ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight); Log.once("Renderer clampToMaxSize: image height larger than machines max cube texture size (max = " + maxSize + ")"); return canvas; } function initMeshBuffers(mesh) { if (!mesh.dynamic && mesh._webgl.inittedBuffers) return mesh._webgl; var webgl = mesh._webgl, DRAW = mesh.dynamic ? _gl.DYNAMIC_DRAW : _gl.STATIC_DRAW, ARRAY_BUFFER = _gl.ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER = _gl.ELEMENT_ARRAY_BUFFER, bufferArray, items, item, i, len, offset, vertexIndex; items = mesh.vertices || EMPTY_ARRAY; len = items.length; if (len && mesh.verticesNeedUpdate) { bufferArray = webgl.vertexArray; if (!bufferArray || bufferArray.length !== len * 3) { bufferArray = webgl.vertexArray = new Float32Array(len * 3); webgl.vertexCount = len; } for (i = 0; i &lt; len; i++) { item = items[i]; offset = i * 3; bufferArray[offset] = item.x; bufferArray[offset + 1] = item.y; bufferArray[offset + 2] = item.z; } webgl.vertexBuffer = webgl.vertexBuffer || _gl.createBuffer(); _gl.bindBuffer(ARRAY_BUFFER, webgl.vertexBuffer); _gl.bufferData(ARRAY_BUFFER, bufferArray, DRAW); mesh.verticesNeedUpdate = false; } items = mesh.normals || EMPTY_ARRAY; len = items.length; if (len && mesh.normalsNeedUpdate) { bufferArray = webgl.normalArray; if (!bufferArray || bufferArray.length !== len * 3) bufferArray = webgl.normalArray = new Float32Array(len * 3); for (i = 0; i &lt; len; i++) { item = items[i]; offset = i * 3; bufferArray[offset] = item.x; bufferArray[offset + 1] = item.y; bufferArray[offset + 2] = item.z; } webgl.normalBuffer = webgl.normalBuffer || _gl.createBuffer(); _gl.bindBuffer(ARRAY_BUFFER, webgl.normalBuffer); _gl.bufferData(ARRAY_BUFFER, bufferArray, DRAW); mesh.normalsNeedUpdate = false; } items = mesh.tangents || EMPTY_ARRAY; len = items.length; if (len && mesh.tangentsNeedUpdate) { bufferArray = webgl.tangentArray; if (!bufferArray || bufferArray.length !== len * 4) bufferArray = webgl.tangentArray = new Float32Array(len * 4); for (i = 0; i &lt; len; i++) { item = items[i]; offset = i * 4; bufferArray[offset] = item.x; bufferArray[offset + 1] = item.y; bufferArray[offset + 2] = item.z; bufferArray[offset + 3] = item.w; } webgl.tangentBuffer = webgl.tangentBuffer || _gl.createBuffer(); _gl.bindBuffer(ARRAY_BUFFER, webgl.tangentBuffer); _gl.bufferData(ARRAY_BUFFER, bufferArray, DRAW); mesh.tangentsNeedUpdate = false; } items = mesh.indices || EMPTY_ARRAY; len = items.length; if (len && mesh.indicesNeedUpdate) { bufferArray = webgl.indexArray; if (!bufferArray || bufferArray.length !== len) { bufferArray = webgl.indexArray = new Uint16Array(len); webgl.indexCount = len; } for (i = 0; i &lt; len; i++) bufferArray[i] = items[i]; webgl.indexBuffer = webgl.indexBuffer || _gl.createBuffer(); _gl.bindBuffer(ELEMENT_ARRAY_BUFFER, webgl.indexBuffer); _gl.bufferData(ELEMENT_ARRAY_BUFFER, bufferArray, DRAW); bufferArray = webgl.lineArray; if (!bufferArray || bufferArray.length !== len * 3) { bufferArray = webgl.lineArray = new Uint16Array(len * 3); webgl.lineCount = len * 3; } vertexIndex = offset = 0; for (i = 0; i &lt; len; i++) { bufferArray[offset] = items[vertexIndex]; bufferArray[offset + 1] = items[vertexIndex + 1]; bufferArray[offset + 2] = items[vertexIndex]; bufferArray[offset + 3] = items[vertexIndex + 2]; bufferArray[offset + 4] = items[vertexIndex + 1]; bufferArray[offset + 5] = items[vertexIndex + 2]; offset += 6; vertexIndex += 3; } webgl.lineBuffer = webgl.lineBuffer || _gl.createBuffer(); _gl.bindBuffer(ELEMENT_ARRAY_BUFFER, webgl.lineBuffer); _gl.bufferData(ELEMENT_ARRAY_BUFFER, bufferArray, DRAW); mesh.indicesNeedUpdate = false; } items = mesh.colors || EMPTY_ARRAY; len = items.length; if (len && mesh.colorsNeedUpdate) { bufferArray = webgl.colorArray; if (!bufferArray || bufferArray.length !== len * 3) bufferArray = webgl.colorArray = new Float32Array(len * 3); for (i = 0; i &lt; len; i++) { item = items[i]; offset = i * 3; bufferArray[offset] = item.x; bufferArray[offset + 1] = item.y; bufferArray[offset + 2] = item.z; } webgl.colorBuffer = webgl.colorBuffer || _gl.createBuffer(); _gl.bindBuffer(ARRAY_BUFFER, webgl.colorBuffer); _gl.bufferData(ARRAY_BUFFER, bufferArray, DRAW); mesh.colorsNeedUpdate = false; } items = mesh.uvs || EMPTY_ARRAY; len = items.length; if (len && mesh.uvsNeedUpdate) { bufferArray = webgl.uvArray; if (!bufferArray || bufferArray.length !== len * 2) bufferArray = webgl.uvArray = new Float32Array(len * 2); for (i = 0; i &lt; len; i++) { item = items[i]; offset = i * 2; bufferArray[offset] = item.x; bufferArray[offset + 1] = item.y; } webgl.uvBuffer = webgl.uvBuffer || _gl.createBuffer(); _gl.bindBuffer(ARRAY_BUFFER, webgl.uvBuffer); _gl.bufferData(ARRAY_BUFFER, bufferArray, DRAW); mesh.uvsNeedUpdate = false; } items = mesh.boneIndices || EMPTY_ARRAY; len = items.length; if (len && mesh.boneIndicesNeedUpdate) { bufferArray = webgl.boneIndexArray; if (!bufferArray || bufferArray.length !== len) bufferArray = webgl.boneIndexArray = new Uint16Array(len); for (i = 0; i &lt; len; i++) bufferArray[i] = items[i]; webgl.boneIndexBuffer = webgl.boneIndexBuffer || _gl.createBuffer(); _gl.bindBuffer(ARRAY_BUFFER, webgl.boneIndexBuffer); _gl.bufferData(ARRAY_BUFFER, bufferArray, DRAW); mesh.boneIndicesNeedUpdate = false; } items = mesh.boneWeights || EMPTY_ARRAY; len = items.length; if (len && mesh.boneWeightsNeedUpdate) { bufferArray = webgl.boneWeightArray; if (!bufferArray || bufferArray.length !== len) bufferArray = webgl.boneWeightArray = new Float32Array(len); for (i = 0; i &lt; len; i++) bufferArray[i] = items[i]; webgl.boneWeightBuffer = webgl.boneWeightBuffer || _gl.createBuffer(); _gl.bindBuffer(ARRAY_BUFFER, webgl.boneWeightBuffer); _gl.bufferData(ARRAY_BUFFER, bufferArray, DRAW); mesh.boneWeightsNeedUpdate = false; } webgl.inittedBuffers = true; return webgl; } function initEmitterBuffers(emitter, transform, attributes) { var MAX = Emitter.MAX_PARTICLES, webgl = emitter._webgl, DRAW = _gl.DYNAMIC_DRAW, FLOAT = _gl.FLOAT, ARRAY_BUFFER = _gl.ARRAY_BUFFER, positionArray, dataArray, positionBuffer, dataBuffer, particles = emitter.particles, particle, i = 0, len = particles.length, offset, position, me, x, y, z, m13, m23, m33, m43, m14, m24, m34, m44 if (len) { if (emitter.sort) { _mat4.mmul(_projScreenMatrix, transform.matrixWorld); me = _mat4.elements; m13 = me[2]; m23 = me[6]; m33 = me[10]; m43 = me[14]; m14 = me[3]; m24 = me[7]; m34 = me[11]; m44 = me[15]; i = len; while (i--) { particle = particles[i]; position = particle.position; x = position.x; y = position.y; z = position.z; particle.z = (m13 * x + m23 * y + m33 * z + m43) / (m14 * x + m24 * y + m34 * z + m44); } particles.sort(numericalSort); } positionArray = webgl.positionArray || (webgl.positionArray = new Float32Array(MAX * 3)); dataArray = webgl.dataArray || (webgl.dataArray = new Float32Array(MAX * 3)); i = len; while (i--) { particle = particles[i]; position = particle.position; offset = i * 3; positionArray[offset] = position.x; positionArray[offset + 1] = position.y; positionArray[offset + 2] = position.z; dataArray[offset] = particle.angle; dataArray[offset + 1] = particle.size; dataArray[offset + 2] = particle.alpha; } disableAttributes(); positionBuffer = webgl.positionBuffer || (webgl.positionBuffer = _gl.createBuffer()); _gl.bindBuffer(ARRAY_BUFFER, webgl.positionBuffer); _gl.bufferData(ARRAY_BUFFER, positionArray, DRAW); enableAttribute(attributes.position); _gl.vertexAttribPointer(attributes.position, 3, FLOAT, false, 0, 0); dataBuffer = webgl.dataBuffer || (webgl.dataBuffer = _gl.createBuffer()); _gl.bindBuffer(ARRAY_BUFFER, webgl.dataBuffer); _gl.bufferData(ARRAY_BUFFER, dataArray, DRAW); enableAttribute(attributes.data); _gl.vertexAttribPointer(attributes.data, 3, FLOAT, false, 0, 0); } webgl.particleCount = len; _lastBuffer = webgl; return webgl; } function numericalSort(a, b) { return b.z - a.z; } function initMaterial(material, mesh, lights) { if (!material.needsUpdate && material._webgl) return material._webgl; var shader = material.shader, uniforms = material.uniforms, OES_standard_derivatives = !! _extensions.OES_standard_derivatives, parameters = {}; parameters.mobile = Device.mobile; parameters.useLights = shader.lights; parameters.useShadows = shader.shadows; parameters.useFog = shader.fog; parameters.useBones = mesh.useBones && mesh.bones.length > 0; parameters.useVertexLit = shader.vertexLit; parameters.useSpecular = shader.specular; parameters.useNormal = uniforms.normalMap; parameters.useBump = uniforms.bumpMap; parameters.positions = mesh.vertices.length > 0; parameters.normals = mesh.normals.length > 0; parameters.tangents = mesh.tangents.length > 0; parameters.uvs = mesh.uvs.length > 0; parameters.colors = mesh.colors.length > 0; parameters.OES_standard_derivatives = OES_standard_derivatives && shader.OES_standard_derivatives; allocateLights(lights, parameters); allocateShadows(lights, parameters); material._webgl = initProgram(shader.vertex, shader.fragment, parameters); material.needsUpdate = false; return material._webgl; } function initEmitterMaterial(material, emitter, lights) { if (!material.needsUpdate && material._webgl) return material._webgl; var shader = material.shader, webgl = emitter._webgl, uniforms = material.uniforms, OES_standard_derivatives = !! _extensions.OES_standard_derivatives, parameters = {}; parameters.emitter = true; parameters.mobile = Device.mobile; parameters.useLights = shader.lights; parameters.useShadows = shader.shadows; parameters.useFog = shader.fog; parameters.useBones = false; parameters.useVertexLit = shader.vertexLit; parameters.useSpecular = shader.specular; parameters.useNormal = uniforms.normalMap; parameters.useBump = uniforms.bumpMap; parameters.positions = true; parameters.normals = false; parameters.tangents = false; parameters.uvs = false; parameters.colors = false; parameters.OES_standard_derivatives = OES_standard_derivatives && shader.OES_standard_derivatives; allocateLights(lights, parameters); allocateShadows(lights, parameters); material._webgl = initProgram(shader.vertex, shader.fragment, parameters); material.needsUpdate = false; return material._webgl; } function allocateLights(lights, parameters) { var maxPointLights = 0, maxDirectionalLights = 0, maxSpotLights = 0, maxHemiLights = 0, light, type, i = 0, il = lights.length; for (; i &lt; il; i++) { light = lights[i]; if (!light.visible || light.onlyShadow) continue; type = light.type; if (type === LightType.Point) { maxPointLights++; } else if (type === LightType.Directional) { maxDirectionalLights++; } else if (type === LightType.Spot) { maxSpotLights++; } else if (type === LightType.Hemi) { maxHemiLights++; } } parameters.maxPointLights = maxPointLights; parameters.maxDirectionalLights = maxDirectionalLights; parameters.maxSpotLights = maxSpotLights; parameters.maxHemiLights = maxHemiLights; } function allocateShadows(lights, parameters) { var maxShadows = 0, light, type, i = 0, il = lights.length; for (; i &lt; il; i++) { light = lights[i]; if (!light.visible || !light.castShadow) continue; type = light.type; if (type === LightType.Directional) { maxShadows++; } else if (type === LightType.Spot) { maxShadows++; } } parameters.maxShadows = maxShadows; } var HEADER = /([\s\S]*)?(void[\s]+main)/, MAIN_FUNCTION = /void[\s]+main([\s]+)?(\((void)?\))([\s]+)?{([^}]*)}/, MAIN_SPLITER = /void[\s]+main([\s]+)?(\((void)?\))([\s]+)?{/; function initProgram(vertexShader, fragmentShader, parameters) { var chunks = [], key, program, webglProgram, programInfo, code, i; chunks.push(fragmentShader, vertexShader); for (key in paramete