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/rect",
"odin/math/vec2",
"odin/math/vec3",
"odin/math/mat4",
"odin/math/color",
"odin/core/game/log",
"odin/core/enums"
],
function(EventEmitter, Device, Dom, util, Mathf, Rect, Vec2, Vec3, Mat4, Color, Log, Enums) {
"use strict";
var Blending = Enums.Blending,
getWebGLContext = Dom.getWebGLContext,
createProgram = Dom.createProgram,
parseUniformsAttributes = Dom.parseUniformsAttributes,
addEvent = Dom.addEvent,
removeEvent = Dom.removeEvent,
merge = util.merge,
clear = util.clear,
clamp = Mathf.clamp,
isPowerOfTwo = Mathf.isPowerOfTwo,
SPRITE_VERTICES = [
new Vec3(-0.5, 0.5, 0),
new Vec3(-0.5, -0.5, 0),
new Vec3(0.5, 0.5, 0),
new Vec3(0.5, -0.5, 0)
],
SPRITE_UVS = [
new Vec2(0, 0),
new Vec2(0, 1),
new Vec2(1, 0),
new Vec2(1, 1)
],
ENUM_SPRITE_BUFFER = -1,
WHITE_TEXTURE = new Uint8Array([255, 255, 255, 255]),
ENUM_WHITE_TEXTURE = -1,
ENUM_WIREFRAME_SHADER = -1,
EMPTY_ARRAY = [];
/**
* @class WebGLRenderer
* @extends EventEmitter
* @brief 2d webgl renderer
* @param Object options
*/
function WebGLRenderer(opts) {
opts || (opts = {});
EventEmitter.call(this);
this.canvas = undefined;
this.context = undefined;
this._context = false;
this.autoClear = opts.autoClear != undefined ? opts.autoClear : true;
this.attributes = merge(opts.attributes || {}, {
alpha: true,
antialias: true,
depth: true,
premulipliedAlpha: true,
preserveDrawingBuffer: false,
stencil: true
});
this._webgl = {
gpu: {
precision: "highp",
maxAnisotropy: 16,
maxTextures: 16,
maxTextureSize: 16384,
maxCubeTextureSize: 16384,
maxRenderBufferSize: 16384
},
ext: {
textureFilterAnisotropic: undefined,
textureFloat: undefined,
standardDerivatives: undefined,
compressedTextureS3TC: undefined
},
textures: {},
buffers: {},
shaders: {},
lastTexture: undefined,
lastShader: undefined,
lastBuffer: undefined
};
this._clearBytes = 17664;
this._lastCamera = undefined;
this._lastResizeFn = undefined;
this._lastBackground = new Color;
this._lastBlending = undefined;
}
EventEmitter.extend(WebGLRenderer);
WebGLRenderer.prototype.init = function(canvas) {
if (this.canvas) this.clear();
var element = canvas.element;
this.canvas = canvas;
this.context = getWebGLContext(element, this.attributes);
if (!this.context) return this;
this._context = true;
addEvent(element, "webglcontextlost", this._handleWebGLContextLost, this);
addEvent(element, "webglcontextrestored", this._handleWebGLContextRestored, this);
this.setDefaults();
return this;
};
WebGLRenderer.prototype.clear = function() {
if (!this.canvas) return this;
var canvas = this.canvas,
element = canvas.element,
webgl = this._webgl,
ext = webgl.ext;
this.canvas = undefined;
this.context = undefined;
this._context = false;
removeEvent(element, "webglcontextlost", this._handleWebGLContextLost, this);
removeEvent(element, "webglcontextrestored", this._handleWebGLContextRestored, this);
this._clearBytes = 17664;
this._lastCamera = undefined;
this._lastBackground.setRGB(0, 0, 0);
this._lastBlending = undefined;
ext.compressedTextureS3TC = ext.standardDerivatives = ext.textureFilterAnisotropic = ext.textureFloat = undefined;
webgl.lastBuffer = webgl.lastShader = webgl.lastTexture = undefined;
clear(webgl.textures);
clear(webgl.buffers);
clear(webgl.shaders);
return this;
};
/**
* @method setDefaults
* @memberof WebGLRenderer
* @brief sets renderers defaults settings
*/
WebGLRenderer.prototype.setDefaults = function() {
var gl = this.context,
webgl = this._webgl,
ext = webgl.ext,
gpu = webgl.gpu,
textureFilterAnisotropic = gl.getExtension("EXT_texture_filter_anisotropic") ||
gl.getExtension("MOZ_EXT_texture_filter_anisotropic") ||
gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic"),
compressedTextureS3TC = gl.getExtension("WEBGL_compressed_texture_s3tc") ||
gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") ||
gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc"),
standardDerivatives = gl.getExtension("OES_standard_derivatives"),
textureFloat = gl.getExtension("OES_texture_float");
ext.textureFilterAnisotropic = textureFilterAnisotropic;
ext.standardDerivatives = standardDerivatives;
ext.textureFloat = textureFloat;
ext.compressedTextureS3TC = compressedTextureS3TC;
var VERTEX_SHADER = gl.VERTEX_SHADER,
FRAGMENT_SHADER = gl.FRAGMENT_SHADER,
HIGH_FLOAT = gl.HIGH_FLOAT,
MEDIUM_FLOAT = gl.MEDIUM_FLOAT,
shaderPrecision = typeof(gl.getShaderPrecisionFormat) !== "undefined",
maxAnisotropy = ext.textureFilterAnisotropic ? gl.getParameter(ext.textureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 1,
maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),
maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE),
maxCubeTextureSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE),
maxRenderBufferSize = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE),
vsHighpFloat = shaderPrecision ? gl.getShaderPrecisionFormat(VERTEX_SHADER, HIGH_FLOAT) : 0,
vsMediumpFloat = shaderPrecision ? gl.getShaderPrecisionFormat(VERTEX_SHADER, MEDIUM_FLOAT) : 1,
fsHighpFloat = shaderPrecision ? gl.getShaderPrecisionFormat(FRAGMENT_SHADER, HIGH_FLOAT) : 0,
fsMediumpFloat = shaderPrecision ? gl.getShaderPrecisionFormat(FRAGMENT_SHADER, MEDIUM_FLOAT) : 1,
highpAvailable = vsHighpFloat.precision > 0 && fsHighpFloat.precision > 0,
mediumpAvailable = vsMediumpFloat.precision > 0 && fsMediumpFloat.precision > 0,
precision = "highp";
if (!highpAvailable || Device.mobile) {
if (mediumpAvailable) {
precision = "mediump";
} else {
precision = "lowp";
}
}
gpu.precision = precision;
gpu.maxAnisotropy = maxAnisotropy;
gpu.maxTextures = maxTextures;
gpu.maxTextureSize = maxTextureSize;
gpu.maxCubeTextureSize = maxCubeTextureSize;
gpu.maxRenderBufferSize = maxRenderBufferSize;
gl.clearColor(0, 0, 0, 1);
gl.clearDepth(1);
gl.clearStencil(0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.frontFace(gl.CCW);
gl.cullFace(gl.BACK);
gl.enable(gl.CULL_FACE);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
this.setBlending(Blending.Default);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
this.buildBuffer({
_id: ENUM_SPRITE_BUFFER,
vertices: SPRITE_VERTICES,
uvs: SPRITE_UVS
});
this.buildShader(null, {
_id: ENUM_WIREFRAME_SHADER,
vertex: wireframe.vertex,
fragment: wireframe.fragment
});
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, WHITE_TEXTURE);
gl.bindTexture(gl.TEXTURE_2D, null);
webgl.textures[ENUM_WHITE_TEXTURE] = texture;
this._clearBytes = gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT;
return this;
};
/**
* @method setBlending
* @memberof WebGLRenderer
* @param Number blending
*/
WebGLRenderer.prototype.setBlending = function(blending) {
var gl = this.context;
if (blending !== this._lastBlending) {
switch (blending) {
case Blending.None:
gl.disable(gl.BLEND);
break;
case Blending.Additive:
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
break;
case Blending.Subtractive:
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ZERO, gl.ONE_MINUS_SRC_COLOR);
break;
case Blending.Muliply:
gl.enable(gl.BLEND);
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ZERO, gl.SRC_COLOR);
break;
case Blending.Default:
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);
break;
}
this._lastBlending = blending;
}
};
WebGLRenderer.prototype.preRender = function(camera) {
if (!this._context) return;
var gl = this.context,
lastBackground = this._lastBackground,
background = camera.background;
if (lastBackground.r !== background.r || lastBackground.g !== background.g || lastBackground.b !== background.b) {
lastBackground.copy(background);
gl.clearColor(background.r, background.g, background.b, 1);
if (!this.autoClear) gl.clear(this._clearBytes);
}
if (this._lastCamera !== camera) {
var canvas = this.canvas,
w = canvas.pixelWidth,
h = canvas.pixelHeight;
camera.set(w, h);
gl.viewport(0, 0, w, h);
if (this._lastResizeFn) canvas.off("resize", this._lastResizeFn);
this._lastResizeFn = function() {
var w = this.pixelWidth,
h = this.pixelHeight;
camera.set(w, h);
gl.viewport(0, 0, w, h);
};
canvas.on("resize", this._lastResizeFn);
this._lastCamera = camera;
}
if (this.autoClear) gl.clear(this._clearBytes);
};
WebGLRenderer.prototype.renderGUI = function(gui, camera) {
if (!this._context) return;
var gl = this.context,
components = gui.components,
transform,
i;
};
/**
* @method render
* @memberof WebGLRenderer
* @brief renderers scene from camera's perspective
* @param Scene scene
* @param Camera camera
*/
WebGLRenderer.prototype.render = function(scene, camera) {
if (!this._context) return;
var gl = this.context,
components = scene.components,
sprites = components.Sprite || EMPTY_ARRAY,
meshFilters = components.MeshFilter || EMPTY_ARRAY,
sprite, meshFilter, particleSystem, transform,
i;
for (i = sprites.length; i--;) {
sprite = sprites[i];
transform = sprite.transform;
if (!transform && !sprite.visible) continue;
transform.updateModelView(camera.view);
this.renderSprite(camera, transform, sprite);
}
for (i = meshFilters.length; i--;) {
meshFilter = meshFilters[i];
transform = meshFilter.transform;
if (!transform) continue;
transform.updateModelView(camera.view);
this.renderMeshFilter(camera, transform, meshFilter);
}
};
WebGLRenderer.prototype.renderSprite = function(camera, transform, sprite) {
var gl = this.context,
webgl = this._webgl,
material = sprite.material,
wireframe = material.wireframe,
glShader = wireframe ? webgl.shaders[ENUM_WIREFRAME_SHADER] : this.buildShader(sprite, material.shader),
glBuffer = webgl.buffers[ENUM_SPRITE_BUFFER],
uniforms, materialUniforms, texture, w, h;
if (!glShader) return;
uniforms = glShader.uniforms;
materialUniforms = material.uniforms;
if (uniforms.mvpMatrix) materialUniforms.mvpMatrix.mmul(camera.projection, transform.modelView);
if (uniforms.modelMatrix) materialUniforms.modelMatrix.copy(transform.matrixWorld);
if (uniforms.modelViewMatrix) materialUniforms.modelViewMatrix.copy(transform.modelView);
if (uniforms.projectionMatrix) materialUniforms.projectionMatrix.copy(camera.projection);
if (uniforms.viewMatrix) materialUniforms.viewMatrix.copy(camera.transform.matrixWorld);
if (webgl.lastShader !== glShader) {
gl.useProgram(glShader.program);
webgl.lastShader = glShader;
}
if (webgl.lastBuffer !== glBuffer) {
this.bindBuffers(glShader, glBuffer);
webgl.lastBuffer = glBuffer;
}
texture = materialUniforms[sprite.texture];
if (!texture) {
Log.once("WebGLRenderer.renderSprite: sprite.texture " + sprite.texture + " was not found in material uniforms");
return
}
this.bindShader(glShader, material, true);
if (uniforms.crop) {
w = texture.invWidth;
h = texture.invHeight;
gl.uniform4f(uniforms.crop.location, sprite.x * w, sprite.y * h, sprite.w * w, sprite.h * h);
} else {
Log.once("WebGLRenderer.renderSprite: sprite material shader needs a vec4 crop uniform");
return;
}
if (uniforms.size) {
gl.uniform2f(uniforms.size.location, sprite.width, sprite.height);
} else {
Log.once("WebGLRenderer.renderSprite: sprite material shader needs a vec2 size uniform");
return;
}
gl.drawArrays(gl.TRIANGLE_STRIP, 0, glBuffer.vertices);
};
var MAT = new Mat4;
WebGLRenderer.prototype.renderMeshFilter = function(camera, transform, meshFilter) {
var gl = this.context,
webgl = this._webgl,
mesh = meshFilter.mesh,
material = meshFilter.material,
wireframe = material.wireframe,
glShader = wireframe ? webgl.shaders[ENUM_WIREFRAME_SHADER] : this.buildShader(mesh, material.shader),
glBuffer = this.buildBuffer(mesh, material),
uniforms, materialUniforms;
if (!glShader || !glBuffer) return;
uniforms = glShader.uniforms;
materialUniforms = material.uniforms;
if (uniforms.mvpMatrix) materialUniforms.mvpMatrix.mmul(camera.projection, transform.modelView);
if (uniforms.modelMatrix) materialUniforms.modelMatrix.copy(transform.matrixWorld);
if (uniforms.modelViewMatrix) materialUniforms.modelViewMatrix.copy(transform.modelView);
if (uniforms.projectionMatrix) materialUniforms.projectionMatrix.copy(camera.projection);
if (uniforms.viewMatrix) materialUniforms.viewMatrix.copy(camera.transform.matrixWorld);
if (webgl.lastShader !== glShader) {
gl.useProgram(glShader.program);
webgl.lastShader = glShader;
}
if (webgl.lastBuffer !== glBuffer) {
this.bindBuffers(glShader, glBuffer);
webgl.lastBuffer = glBuffer;
}
this.bindShader(glShader, material, false);
if (glBuffer.index) {
gl.drawElements(gl.TRIANGLES, glBuffer.indices, gl.UNSIGNED_SHORT, 0);
} else {
gl.drawArrays(gl.TRIANGLES, 0, glBuffer.vertices);
}
};
WebGLRenderer.prototype.bindBuffers = function(glShader, glBuffer) {
var gl = this.context,
attributes = glShader.attributes,
FLOAT = gl.FLOAT,
ARRAY_BUFFER = gl.ARRAY_BUFFER;
if (glBuffer.vertex && attributes.aVertexPosition > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.vertex);
gl.enableVertexAttribArray(attributes.aVertexPosition);
gl.vertexAttribPointer(attributes.aVertexPosition, 3, FLOAT, false, 0, 0);
}
if (glBuffer.barycentric && attributes.aBarycentric > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.barycentric);
gl.enableVertexAttribArray(attributes.aBarycentric);
gl.vertexAttribPointer(attributes.aBarycentric, 3, FLOAT, false, 0, 0);
}
if (glBuffer.normal && attributes.aVertexNormal > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.normal);
gl.enableVertexAttribArray(attributes.aVertexNormal);
gl.vertexAttribPointer(attributes.aVertexNormal, 3, FLOAT, false, 0, 0);
}
if (glBuffer.tangent && attributes.aVertexTangent > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.tangent);
gl.enableVertexAttribArray(attributes.aVertexTangent);
gl.vertexAttribPointer(attributes.aVertexTangent, 4, FLOAT, false, 0, 0);
}
if (glBuffer.color && attributes.aVertexColor > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.color);
gl.enableVertexAttribArray(attributes.aVertexColor);
gl.vertexAttribPointer(attributes.aVertexColor, 3, FLOAT, false, 0, 0);
}
if (glBuffer.uv && attributes.aVertexUv > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.uv);
gl.enableVertexAttribArray(attributes.aVertexUv);
gl.vertexAttribPointer(attributes.aVertexUv, 2, FLOAT, false, 0, 0);
}
if (glBuffer.boneWeight && attributes.aBoneWeight > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.boneWeight);
gl.enableVertexAttribArray(attributes.aBoneWeight);
gl.vertexAttribPointer(attributes.aBoneWeight, 1, FLOAT, false, 0, 0);
}
if (glBuffer.boneIndex && attributes.aBoneIndex > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.boneIndex);
gl.enableVertexAttribArray(attributes.aBoneIndex);
gl.vertexAttribPointer(attributes.aBoneIndex, 1, FLOAT, false, 0, 0);
}
if (glBuffer.index) gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, glBuffer.index);
};
WebGLRenderer.prototype.bindShader = function(glShader, material, ignoreMissing) {
var gl = this.context,
webgl = this._webgl,
uniforms = glShader.uniforms,
uniformValue, value, key,
materialUniforms = material.uniforms,
glTexture, index = 0,
i;
for (key in uniforms) {
uniformValue = uniforms[key];
value = materialUniforms[key];
if (!value) {
if (ignoreMissing) continue;
if (uniformValue.type !== "sampler2D") throw new Error("WebGLRenderer.bindShader: material doesn't have " + key + " uniform that bound shader needs");
}
if (uniformValue.isArray) {
for (i = uniformValue.location.length; i--;) {
index = this.bindUniform(gl, webgl, uniformValue.type, uniformValue.location[i], value[i], index);
}
} else {
index = this.bindUniform(gl, webgl, uniformValue.type, uniformValue.location, value, index);
}
}
};
WebGLRenderer.prototype.bindUniform = function(gl, webgl, type, location, value, index) {
switch (type) {
case "int":
gl.uniform1i(location, value);
break;
case "float":
gl.uniform1f(location, value);
break;
case "vec2":
gl.uniform2f(location, value.x, value.y);
break;
case "vec3":
gl.uniform3f(location, value.x, value.y, value.z);
break;
case "vec4":
gl.uniform4f(location, value.x, value.y, value.z, value.w);
break;
case "mat2":
gl.uniformMatrix2fv(location, false, value.elements);
break;
case "mat3":
gl.uniformMatrix3fv(location, false, value.elements);
break;
case "mat4":
gl.uniformMatrix4fv(location, false, value.elements);
break;
case "sampler2D":
index = this.bindTexture(gl, webgl, value, location, index);
}
return index;
};
WebGLRenderer.prototype.bindTexture = function(gl, webgl, texture, uniform, index) {
var glTexture = this.buildTexture(texture);
if (webgl.lastTexture !== glTexture) {
gl.activeTexture(gl.TEXTURE0 + index);
gl.bindTexture(gl.TEXTURE_2D, glTexture);
gl.uniform1i(uniform, index);
webgl.lastTexture = glTexture;
}
return ++index;
};
var COMPILE_ARRAY = [];
WebGLRenderer.prototype.buildBuffer = function(mesh, material) {
if (!mesh) return undefined;
var webgl = this._webgl,
buffers = webgl.buffers,
glBuffers = buffers[mesh._id];
if (glBuffers && !mesh.needsUpdate) return glBuffers;
var gl = this.context,
compileArray = COMPILE_ARRAY,
DRAW = mesh.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW,
ARRAY_BUFFER = gl.ARRAY_BUFFER,
ELEMENT_ARRAY_BUFFER = gl.ELEMENT_ARRAY_BUFFER,
items, item,
i, il;
glBuffers = glBuffers || (buffers[mesh._id] = {});
items = mesh.vertices || EMPTY_ARRAY;
if (items.length) {
compileArray.length = 0;
for (i = 0, il = items.length; i < il; i++) {
item = items[i];
compileArray.push(item.x, item.y, item.z);
}
if (compileArray.length) {
glBuffers.vertex = glBuffers.vertex || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffers.vertex);
gl.bufferData(ARRAY_BUFFER, new Float32Array(compileArray), DRAW);
}
glBuffers.vertices = items.length;
if (material && material.wireframe) {
compileArray.length = 0;
for (i = 0, il = items.length; i < il; i += 3) {
compileArray.push(1, 0, 0);
compileArray.push(0, 1, 0);
compileArray.push(0, 0, 1);
}
glBuffers.barycentric = glBuffers.barycentric || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffers.barycentric);
gl.bufferData(ARRAY_BUFFER, new Float32Array(compileArray), DRAW);
}
}
items = mesh.normals || EMPTY_ARRAY;
if (items.length) {
compileArray.length = 0;
for (i = 0, il = items.length; i < il; i++) {
item = items[i];
compileArray.push(item.x, item.y, item.z);
}
if (compileArray.length) {
glBuffers.normal = glBuffers.normal || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffers.normal);
gl.bufferData(ARRAY_BUFFER, new Float32Array(compileArray), DRAW);
}
}
items = mesh.tangents || EMPTY_ARRAY;
if (items.length) {
compileArray.length = 0;
for (i = 0, il = items.length; i < il; i++) {
item = items[i];
compileArray.push(item.x, item.y, item.z, item.w);
}
if (compileArray.length) {
glBuffers.tangent = glBuffers.tangent || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffers.tangent);
gl.bufferData(ARRAY_BUFFER, new Float32Array(compileArray), DRAW);
}
}
items = mesh.colors || EMPTY_ARRAY;
if (items.length) {
compileArray.length = 0;
for (i = 0, il = items.length; i < il; i++) {
item = items[i];
compileArray.push(item.r, item.g, item.b);
}
if (compileArray.length) {
glBuffers.color = glBuffers.color || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffers.color);
gl.bufferData(ARRAY_BUFFER, new Float32Array(compileArray), DRAW);
}
}
items = mesh.uvs || EMPTY_ARRAY;
if (items.length) {
compileArray.length = 0;
for (i = 0, il = items.length; i < il; i++) {
item = items[i];
compileArray.push(item.x, item.y);
}
if (compileArray.length) {
glBuffers.uv = glBuffers.uv || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffers.uv);
gl.bufferData(ARRAY_BUFFER, new Float32Array(compileArray), DRAW);
}
}
items = mesh.boneIndices || EMPTY_ARRAY;
if (items.length) {
glBuffers.boneIndex = glBuffers.boneIndex || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffers.boneIndex);
gl.bufferData(ARRAY_BUFFER, new Float32Array(items), DRAW);
}
items = mesh.boneWeights || EMPTY_ARRAY;
if (items.length) {
glBuffers.boneWeight = glBuffers.boneWeight || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffers.boneWeight);
gl.bufferData(ARRAY_BUFFER, new Float32Array(items), DRAW);
}
items = mesh.indices || mesh.faces || EMPTY_ARRAY;
if (items && items.length) {
glBuffers.index = glBuffers.index || gl.createBuffer();
gl.bindBuffer(ELEMENT_ARRAY_BUFFER, glBuffers.index);
gl.bufferData(ELEMENT_ARRAY_BUFFER, new Int16Array(items), DRAW);
glBuffers.indices = items.length;
}
mesh.needsUpdate = false;
return glBuffers;
};
WebGLRenderer.prototype.buildShader = function(mesh, shader) {
var webgl = this._webgl,
shaders = webgl.shaders,
glShader = shaders[shader._id];
if (glShader && !shader.needsUpdate) return glShader;
var gl = this.context,
gpu = webgl.gpu,
ext = webgl.ext,
precision = gpu.precision,
standardDerivatives = ext.standardDerivatives ? "#extension GL_OES_standard_derivatives : enable\n" : "",
precisionFloat = "precision " + precision + " float;\n",
precisionInt = "precision " + precision + " int;\n",
vertexShader = precisionFloat + precisionInt + shader.vertex,
fragmentShader = precisionFloat + precisionInt + standardDerivatives + shader.fragment,
program;
glShader = glShader || (shaders[shader._id] = {});
program = glShader.program = createProgram(gl, vertexShader, fragmentShader);
parseUniformsAttributes(gl, program, vertexShader, fragmentShader,
glShader.attributes || (glShader.attributes = {}),
glShader.uniforms || (glShader.uniforms = {})
);
shader.needsUpdate = false;
return glShader;
};
WebGLRenderer.prototype.buildTexture = function(texture) {
if (!texture || !texture.raw) return this._webgl.textures[ENUM_WHITE_TEXTURE];
var webgl = this._webgl,
textures = webgl.textures,
glTexture = textures[texture._id];
if (glTexture && !texture.needsUpdate) return glTexture;
var gl = this.context,
raw = texture.raw,
ext = webgl.ext,
gpu = webgl.gpu,
TFA = ext.textureFilterAnisotropic,
isPOT = isPowerOfTwo(raw.width) && isPowerOfTwo(raw.height),
anisotropy = clamp(texture.anisotropy, 1, gpu.maxAnisotropy),
TEXTURE_2D = gl.TEXTURE_2D,
WRAP = isPOT ? gl.REPEAT : gl.CLAMP_TO_EDGE,
MAG_FILTER = gl[texture.magFilter] || gl.LINEAR,
MIN_FILTER = gl[texture.minFilter] || gl.LINEAR,
FORMAT = gl[texture.format];
FORMAT = FORMAT ? FORMAT : gl.RGBA;
if (isPOT) {
MIN_FILTER = MIN_FILTER === gl.NEAREST || MIN_FILTER === gl.LINEAR ? gl.LINEAR_MIPMAP_NEAREST : MIN_FILTER;
} else {
MIN_FILTER = MIN_FILTER === gl.NEAREST ? gl.NEAREST : gl.LINEAR;
}
glTexture = glTexture || (textures[texture._id] = gl.createTexture());
gl.bindTexture(TEXTURE_2D, glTexture);
gl.texImage2D(TEXTURE_2D, 0, FORMAT, FORMAT, gl.UNSIGNED_BYTE, raw);
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 (isPOT) gl.generateMipmap(TEXTURE_2D);
webgl.lastTexture = glTexture;
texture.needsUpdate = false;
return glTexture;
};
WebGLRenderer.prototype._handleWebGLContextLost = function(e) {
e.preventDefault();
Log.warn("WebGLRenderer: webgl context was lost");
this._context = false;
this.emit("webglcontextlost", e);
};
WebGLRenderer.prototype._handleWebGLContextRestored = function(e) {
Log.log("WebGLRenderer: webgl context was restored");
this.setDefaults();
this._context = true;
this.emit("webglcontextrestored", e);
};
var wireframe = {
vertex: [
"uniform mat4 mvpMatrix;",
"attribute vec3 aVertexPosition;",
"attribute vec3 aBarycentric;",
"varying vec3 vBarycentric;",
"void main() {",
" vBarycentric = aBarycentric;",
" gl_Position = mvpMatrix * vec4(aVertexPosition, 1.0);",
"}"
].join("\n"),
fragment: [
"uniform vec3 diffuseColor;",
"varying vec3 vBarycentric;",
"float edgeFactor(){",
" vec3 d = fwidth(vBarycentric);",
" vec3 a3 = smoothstep(vec3(0.0), d*1.5, vBarycentric);",
" return min(min(a3.x, a3.y), a3.z);",
"}",
"void main() {",
" gl_FragColor = vec4(diffuseColor, (1.0 - edgeFactor()) * 0.95);",
"}"
].join("\n")
};
return WebGLRenderer;
}
);