if (typeof(define) !== "function") {
var define = require("amdefine")(module);
}
define([
"odin/base/event_emitter",
"odin/base/device",
"odin/base/dom",
"odin/math/mathf",
"odin/math/vec2",
"odin/math/mat32",
"odin/math/mat4",
"odin/math/color",
"odin/core/game/log",
"odin/core/enums"
],
function(EventEmitter, Device, Dom, Mathf, Vec2, Mat32, Mat4, Color, Log, Enums) {
"use strict";
var FontStyle = Enums.FontStyle,
TextClipping = Enums.TextClipping,
TextAnchor = Enums.TextAnchor,
Blending = Enums.Blending,
getWebGLContext = Dom.getWebGLContext,
createProgram = Dom.createProgram,
parseUniformsAttributes = Dom.parseUniformsAttributes,
addEvent = Dom.addEvent,
removeEvent = Dom.removeEvent,
max = Math.max,
clamp = Mathf.clamp,
isPowerOfTwo = Mathf.isPowerOfTwo,
WHITE_TEXTURE = new Uint8Array([255, 255, 255, 255]),
ENUM_WHITE_TEXTURE = -1,
SPRITE_VERTICES = [
new Vec2(-0.5, 0.5),
new Vec2(-0.5, -0.5),
new Vec2(0.5, 0.5),
new Vec2(0.5, -0.5)
],
SPRITE_UVS = [
new Vec2(0, 0),
new Vec2(0, 1),
new Vec2(1, 0),
new Vec2(1, 1)
],
ENUM_SPRITE_BUFFER = -1,
SCREEN_VERTICES = [
new Vec2(0, 0),
new Vec2(0, 1),
new Vec2(1, 0),
new Vec2(1, 1)
],
SCREEN_UVS = [
new Vec2(0, 0),
new Vec2(0, 1),
new Vec2(1, 0),
new Vec2(1, 1)
],
ENUM_SCREEN_BUFFER = -2,
ENUM_SPRITE_SHADER = -1,
ENUM_BASIC_SHADER = -2,
ENUM_PARTICLE_SHADER = -3,
ENUM_CANVAS_SHADER = -4,
EMPTY_ARRAY = [],
MAT = new Mat32,
MAT4 = new Mat4;
/**
* @class WebGLRenderer2D
* @extends EventEmitter
* @brief 2d webgl renderer
* @param Object options
*/
function WebGLRenderer2D(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: {},
shaders: {},
buffers: {},
texts: {},
textsTextureCache: {},
lastTexture: undefined,
lastShader: undefined,
lastBuffer: undefined
};
this._clearBytes = 17664;
this._lastCamera = undefined;
this._lastResizeFn = undefined;
this._lastBackground = new Color;
this._lastBlending = undefined;
}
EventEmitter.extend(WebGLRenderer2D);
WebGLRenderer2D.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;
};
WebGLRenderer2D.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);
clear(webgl.texts);
clear(webgl.textsTextureCache);
return this;
};
/**
* @method setDefaults
* @memberof WebGLRenderer2D
* @brief sets renderers defaults settings
*/
WebGLRenderer2D.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, false);
this.setBlending(Blending.Default);
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
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;
buildBuffer(this, {
_id: ENUM_SPRITE_BUFFER,
vertices: SPRITE_VERTICES,
uvs: SPRITE_UVS
});
buildBuffer(this, {
_id: ENUM_SCREEN_BUFFER,
vertices: SCREEN_VERTICES,
uvs: SCREEN_UVS
});
buildShader(this, {
_id: ENUM_SPRITE_SHADER,
vertexShader: spriteVertexShader(precision),
fragmentShader: spriteFragmentShader(precision)
});
buildShader(this, {
_id: ENUM_CANVAS_SHADER,
vertexShader: guiVertexShader(precision),
fragmentShader: spriteFragmentShader(precision)
});
buildShader(this, {
_id: ENUM_BASIC_SHADER,
vertexShader: basicVertexShader(precision),
fragmentShader: basicFragmentShader(precision)
});
buildShader(this, {
_id: ENUM_PARTICLE_SHADER,
vertexShader: particleVertexShader(precision),
fragmentShader: particleFragmentShader(precision)
});
this._clearBytes = gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT;
return this;
};
/**
* @method setBlending
* @memberof WebGLRenderer2D
* @param Number blending
*/
WebGLRenderer2D.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;
}
};
WebGLRenderer2D.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);
};
WebGLRenderer2D.prototype.renderGUI = function(gui, camera) {
if (!this._context) return;
var gl = this.context,
components = gui.components,
GUIContent = components.GUIContent || EMPTY_ARRAY,
content, transform,
i;
for (i = GUIContent.length; i--;) {
content = GUIContent[i];
transform = content.guiTransform;
if (!transform) continue;
this.renderGUIContent(camera, transform, content);
}
};
WebGLRenderer2D.prototype.renderGUIContent = function(camera, transform, content) {
var gl = this.context,
webgl = this._webgl,
position = transform.position,
text = content.text,
texture = content.texture,
style = content.style,
state = style[style._state],
background = state.background,
glShader = webgl.shaders[ENUM_BASIC_SHADER],
glBuffer = webgl.buffers[ENUM_SCREEN_BUFFER],
uniforms = glShader.uniforms,
aspect = camera.aspect,
width = position.width,
height = position.height,
glText, texture;
MAT4.copy(SCREEN_MAT).translate(position);
if (aspect >= 1) {
width /= aspect;
} else {
height *= aspect;
}
if (webgl.lastShader !== glShader) {
gl.useProgram(glShader.program);
webgl.lastShader = glShader;
}
if (webgl.lastBuffer !== glBuffer) {
bindBuffers(gl, glShader, glBuffer);
webgl.lastBuffer = glBuffer;
}
gl.uniformMatrix4fv(uniforms.uMatrix.location, false, MAT4.elements);
gl.uniform3f(uniforms.uColor.location, background.r, background.g, background.b);
gl.uniform1f(uniforms.uAlpha.location, content.alpha);
gl.uniform2f(uniforms.uSize.location, width, height);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, glBuffer.vertices);
if (text) {
glShader = webgl.shaders[ENUM_CANVAS_SHADER];
uniforms = glShader.uniforms;
gl.useProgram(glShader.program);
bindBuffers(gl, glShader, glBuffer);
glText = buildText(this, camera, position, content, style, state, text);
texture = glText.texture;
if (texture) {
if (webgl.lastTexture !== texture) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(uniforms.uTexture.location, 0);
webgl.lastTexture = texture;
}
gl.uniformMatrix4fv(uniforms.uMatrix.location, false, MAT4.elements);
gl.uniform1f(uniforms.uAlpha.location, content.alpha);
gl.uniform2f(uniforms.uSize.location, glText.width, glText.height);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, glBuffer.vertices);
}
}
};
var SPLITTER = /[\r\n]+/,
TOP = "top",
LEFT = "left",
CENTER = "center",
RIGHT = "right";
function buildText(renderer, camera, position, content, style, state, text) {
var gl = renderer.context,
webgl = renderer._webgl,
texts = webgl.texts,
textsTextureCache = webgl.textsTextureCache,
glText = texts[content._id];
if (glText && !content.needsUpdate) return glText;
glText = glText || (texts[content._id] = {});
var canvas = glText.canvas || document.createElement("canvas"),
ctx = glText.ctx || canvas.getContext("2d"),
texture = textsTextureCache[content._id] || (textsTextureCache[content._id] = {
_id: content._id,
needsUpdate: true,
raw: canvas
}),
font = style.fontStyle + " " + style.fontSize + "pt " + style.font,
wordWrap = style.wordWrap,
lineHeight = style.lineHeight,
width = 0,
height = 0,
lines, lineWidth, i, il;
lines = text.split(SPLITTER);
for (i = 0, il = lines.length; i < il; i++) {
lineWidth = ctx.measureText(lines[i]).width;
width = max(width, lineWidth);
}
height = lineHeight ? lineHeight * il : (lineHeight = determineFontHeight(font)) * il;
canvas.width = width;
canvas.height = height;
ctx.fillStyle = state.text.toRGB();
ctx.font = font;
ctx.textBaseline = TOP;
switch (style.alignment) {
case TextAnchor.Left:
ctx.textAlign = LEFT;
break;
case TextAnchor.Center:
ctx.textAlign = CENTER;
break;
case TextAnchor.Right:
ctx.textAlign = RIGHT;
break;
}
for (i = 0; i < il; i++) ctx.fillText(lines[i], 0, lineHeight * i);
glText.canvas = canvas;
glText.ctx = ctx;
glText.width = width * camera.invWidth;
glText.height = height * camera.invHeight;
texture.needsUpdate = true;
glText.texture = buildTexture(renderer, texture);
content.needsUpdate = false;
return glText;
}
var HEIGHT_CACHE = {};
function determineFontHeight(font) {
var result = HEIGHT_CACHE[font];
if (!result) {
var body = document.getElementsByTagName("body")[0],
dummy = document.createElement("div"),
dummyText = document.createTextNode("M");
dummy.appendChild(dummyText);
dummy.setAttribute("style", "font: " + font + ";position:absolute;top:0;left:0");
body.appendChild(dummy);
result = dummy.offsetHeight;
HEIGHT_CACHE[font] = result;
body.removeChild(dummy);
}
return result;
}
/**
* @method render
* @memberof WebGLRenderer2D
* @brief renderers scene from camera's perspective
* @param Scene scene
* @param Camera camera
*/
WebGLRenderer2D.prototype.render = function(scene, camera) {
if (!this._context) return;
var gl = this.context,
components = scene.components,
sprites = components.Sprite || EMPTY_ARRAY,
particleSystems = components.ParticleSystem || EMPTY_ARRAY,
guiTextures = components.GUITexture || EMPTY_ARRAY,
sprite2d, particleSystem, guiTexture, transform2d,
i;
for (i = sprites.length; i--;) {
sprite2d = sprites[i];
transform2d = sprite2d.transform2d;
if (!transform2d || !sprite2d.visible) continue;
transform2d.updateModelViewMat32(camera.view);
this.renderSprite(camera, transform2d, sprite2d);
}
for (i = particleSystems.length; i--;) {
particleSystem = particleSystems[i];
transform2d = particleSystem.transform2d;
if (!transform2d) continue;
transform2d.updateModelViewMat32(camera.view);
this.renderParticleSystem(camera, transform2d, particleSystem);
}
for (i = guiTextures.length; i--;) {
guiTexture = guiTextures[i];
transform2d = guiTexture.transform2d;
if (!transform2d) continue;
this.renderGUITexture(camera, transform2d, guiTexture);
}
};
var SCREEN_MAT = new Mat4().orthographic(0, 1, 0, 1, -1, 1);
WebGLRenderer2D.prototype.renderGUITexture = function(camera, transform2d, guiTexture) {
var gl = this.context,
webgl = this._webgl,
texture = guiTexture.texture,
position = guiTexture.position,
glShader = webgl.shaders[ENUM_SPRITE_SHADER],
glBuffer = webgl.buffers[ENUM_SCREEN_BUFFER],
glTexture = buildTexture(this, texture),
uniforms = glShader.uniforms,
aspect = camera.aspect,
width = position.width,
height = position.height,
w, h;
if (texture && texture.raw) {
w = texture.invWidth;
h = texture.invHeight;
} else {
return;
}
MAT4.copy(SCREEN_MAT).translate(position);
if (aspect >= 1) {
height *= aspect;
} else {
width /= aspect;
}
if (webgl.lastShader !== glShader) {
gl.useProgram(glShader.program);
webgl.lastShader = glShader;
}
if (webgl.lastBuffer !== glBuffer) {
bindBuffers(gl, glShader, glBuffer);
webgl.lastBuffer = glBuffer;
}
gl.uniformMatrix4fv(uniforms.uMatrix.location, false, MAT4.elements);
gl.uniform4f(uniforms.uCrop.location, guiTexture.x * w, guiTexture.y * h, guiTexture.w * w, guiTexture.h * h);
gl.uniform2f(uniforms.uSize.location, width, height);
gl.uniform1f(uniforms.uAlpha.location, guiTexture.alpha);
if (webgl.lastTexture !== glTexture) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, glTexture);
gl.uniform1i(uniforms.uTexture.location, 0);
webgl.lastTexture = glTexture;
}
gl.drawArrays(gl.TRIANGLE_STRIP, 0, glBuffer.vertices);
};
WebGLRenderer2D.prototype.renderSprite = function(camera, transform2d, sprite2d) {
var gl = this.context,
webgl = this._webgl,
texture = sprite2d.texture,
glShader = webgl.shaders[ENUM_SPRITE_SHADER],
glBuffer = webgl.buffers[ENUM_SPRITE_BUFFER],
glTexture = buildTexture(this, texture),
uniforms = glShader.uniforms,
w, h;
if (texture && texture.raw) {
w = texture.invWidth;
h = texture.invHeight;
} else {
return;
}
MAT4.mmul(camera._projectionMat4, MAT4.fromMat32(transform2d.modelView));
if (webgl.lastShader !== glShader) {
gl.useProgram(glShader.program);
webgl.lastShader = glShader;
}
if (webgl.lastBuffer !== glBuffer) {
bindBuffers(gl, glShader, glBuffer);
webgl.lastBuffer = glBuffer;
}
gl.uniformMatrix4fv(uniforms.uMatrix.location, false, MAT4.elements);
gl.uniform4f(uniforms.uCrop.location, sprite2d.x * w, sprite2d.y * h, sprite2d.w * w, sprite2d.h * h);
gl.uniform2f(uniforms.uSize.location, sprite2d.width, sprite2d.height);
gl.uniform1f(uniforms.uAlpha.location, sprite2d.alpha);
if (webgl.lastTexture !== glTexture) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, glTexture);
gl.uniform1i(uniforms.uTexture.location, 0);
webgl.lastTexture = glTexture;
}
gl.drawArrays(gl.TRIANGLE_STRIP, 0, glBuffer.vertices);
};
WebGLRenderer2D.prototype.renderParticleSystem = function(camera, transform2d, particleSystem) {
var gl = this.context,
webgl = this._webgl,
glShader = webgl.shaders[ENUM_PARTICLE_SHADER],
glBuffer = webgl.buffers[ENUM_SPRITE_BUFFER],
vertices = glBuffer.vertices,
TRIANGLE_STRIP = gl.TRIANGLE_STRIP,
uniforms = glShader.uniforms,
uMatrix = uniforms.uMatrix.location,
uPos = uniforms.uPos.location,
uAngle = uniforms.uAngle.location,
uSize = uniforms.uSize.location,
uColor = uniforms.uColor.location,
uAlpha = uniforms.uAlpha.location,
elements = MAT4.elements,
blending = this._lastBlending,
emitters = particleSystem.emitters,
emitter, particles, view, particle, pos, color, glTexture,
i = emitters.length,
j;
if (!i) return;
while (i--) {
emitter = emitters[i];
particles = emitter.particles;
view = emitter.worldSpace ? camera.view : transform2d.modelView;
if (!(j = particles.length)) continue;
glTexture = buildTexture(this, emitter.texture);
if (webgl.lastShader !== glShader) {
gl.useProgram(glShader.program);
webgl.lastShader = glShader;
}
if (webgl.lastBuffer !== glBuffer) {
bindBuffers(gl, glShader, glBuffer);
webgl.lastBuffer = glBuffer;
}
this.setBlending(emitter.blending);
MAT4.mmul(camera._projectionMat4, MAT4.fromMat32(view));
gl.uniformMatrix4fv(uMatrix, false, elements);
if (webgl.lastTexture !== glTexture) {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, glTexture);
gl.uniform1i(uniforms.uTexture.location, 0);
webgl.lastTexture = glTexture;
}
for (; j--;) {
particle = particles[j];
pos = particle.position;
color = particle.color;
gl.uniform2f(uPos, pos.x, pos.y);
gl.uniform1f(uAngle, particle.angle);
gl.uniform1f(uSize, particle.size);
gl.uniform3f(uColor, color.r, color.g, color.b);
gl.uniform1f(uAlpha, particle.alpha);
gl.drawArrays(TRIANGLE_STRIP, 0, vertices);
}
}
this.setBlending(blending);
};
WebGLRenderer2D.prototype._handleWebGLContextLost = function(e) {
e.preventDefault();
Log.warn("WebGLRenderer2D: webgl context was lost");
this._context = false;
this.emit("webglcontextlost", e);
};
WebGLRenderer2D.prototype._handleWebGLContextRestored = function(e) {
Log.log("WebGLRenderer2D: webgl context was restored");
this.setDefaults();
this._context = true;
this.emit("webglcontextrestored", e);
};
function bindBuffers(gl, glShader, glBuffer) {
var 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, 2, FLOAT, false, 0, 0);
}
if (glBuffer.color && attributes.aVertexColor > -1) {
gl.bindBuffer(ARRAY_BUFFER, glBuffer.color);
gl.enableVertexAttribArray(attributes.aVertexColor);
gl.vertexAttribPointer(attributes.aVertexColor, 2, 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.index) gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, glBuffer.index);
}
var COMPILE_ARRAY = [];
function buildBuffer(renderer, buffer) {
if (!buffer) return undefined;
var gl = renderer.context,
webgl = renderer._webgl,
buffers = webgl.buffers,
glBuffer = buffers[buffer._id];
if (glBuffer && !buffer.needsUpdate) return glBuffer;
glBuffer = glBuffer || (buffers[buffer._id] = {});
var compileArray = COMPILE_ARRAY,
DRAW = buffer.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW,
ARRAY_BUFFER = gl.ARRAY_BUFFER,
ELEMENT_ARRAY_BUFFER = gl.ELEMENT_ARRAY_BUFFER,
items, item,
i, il;
items = buffer.vertices;
if (items && 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) {
glBuffer.vertex = glBuffer.vertex || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffer.vertex);
gl.bufferData(ARRAY_BUFFER, new Float32Array(compileArray), DRAW);
}
glBuffer.vertices = items.length;
}
items = buffer.uvs;
if (items && 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) {
glBuffer.uv = glBuffer.uv || gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, glBuffer.uv);
gl.bufferData(ARRAY_BUFFER, new Float32Array(compileArray), DRAW);
}
}
items = buffer.indices || buffer.faces;
if (items && items.length) {
glBuffer.index = glBuffer.index || gl.createBuffer();
gl.bindBuffer(ELEMENT_ARRAY_BUFFER, glBuffer.index);
gl.bufferData(ELEMENT_ARRAY_BUFFER, new Int16Array(items), DRAW);
glBuffer.indices = items.length;
}
return glBuffer;
}
function buildShader(renderer, shader) {
var gl = renderer.context,
webgl = renderer._webgl,
shaders = webgl.shaders,
glShader = shaders[shader._id],
vertex, fragment;
if (glShader && !shader.needsUpdate) return glShader;
glShader = glShader || (shaders[shader._id] = {});
vertex = shader.vertex || shader.vertexShader;
fragment = shader.fragment || shader.fragmentShader;
var program = glShader.program = createProgram(gl, vertex, fragment);
glShader.vertex = vertex;
glShader.fragment = fragment;
parseUniformsAttributes(gl, program, vertex, fragment,
glShader.attributes || (glShader.attributes = {}),
glShader.uniforms || (glShader.uniforms = {})
);
return glShader;
}
function buildTexture(renderer, texture) {
if (!texture || !texture.raw) return renderer._webgl.textures[ENUM_WHITE_TEXTURE];
var gl = renderer.context,
webgl = renderer._webgl,
textures = webgl.textures,
glTexture = textures[texture._id],
raw = texture.raw;
if (glTexture && !texture.needsUpdate) return glTexture;
glTexture = glTexture || (textures[texture._id] = gl.createTexture());
var ext = webgl.ext,
gpu = webgl.gpu,
TFA = ext.textureFilterAnisotropic,
isPOT = isPowerOfTwo(raw.width) && isPowerOfTwo(raw.height),
anisotropy = texture.anisotropy != undefined ? clamp(texture.anisotropy, 1, gpu.maxAnisotropy) : 1,
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;
}
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;
}
function particleVertexShader(precision) {
return [
"precision " + precision + " float;",
"uniform mat4 uMatrix;",
"uniform vec2 uPos;",
"uniform float uAngle;",
"uniform float uSize;",
"attribute vec2 aVertexPosition;",
"attribute vec2 aVertexUv;",
"varying vec2 vVertexUv;",
"void main() {",
" float c, s, vx, vy, x, y;",
" c = cos(uAngle);",
" s = sin(uAngle);",
" vx = aVertexPosition.x;",
" vy = aVertexPosition.y;",
" x = vx * c - vy * s;",
" y = vx * s + vy * c;",
" vVertexUv = aVertexUv;",
" gl_Position = uMatrix * vec4(uPos.x + x * uSize, uPos.y + y * uSize, 0.0, 1.0);",
"}"
].join("\n");
}
function particleFragmentShader(precision) {
return [
"precision " + precision + " float;",
"uniform float uAlpha;",
"uniform vec3 uColor;",
"uniform sampler2D uTexture;",
"varying vec2 vVertexUv;",
"void main() {",
" vec4 finalColor = texture2D(uTexture, vVertexUv);",
" finalColor.xyz *= uColor;",
" finalColor.w *= uAlpha;",
" gl_FragColor = finalColor;",
"}"
].join("\n");
}
function basicVertexShader(precision) {
return [
"precision " + precision + " float;",
"uniform mat4 uMatrix;",
"uniform vec2 uSize;",
"attribute vec2 aVertexPosition;",
"void main() {",
" gl_Position = uMatrix * vec4(aVertexPosition * uSize, 0.0, 1.0);",
"}"
].join("\n");
}
function basicFragmentShader(precision) {
return [
"precision " + precision + " float;",
"uniform float uAlpha;",
"uniform vec3 uColor;",
"void main() {",
" gl_FragColor = vec4(uColor, uAlpha);",
"}"
].join("\n");
}
function guiVertexShader(precision) {
return [
"precision " + precision + " float;",
"uniform mat4 uMatrix;",
"uniform vec2 uSize;",
"attribute vec2 aVertexPosition;",
"attribute vec2 aVertexUv;",
"varying vec2 vVertexUv;",
"void main() {",
" vVertexUv = vec2(aVertexUv.x, aVertexUv.y);",
" gl_Position = uMatrix * vec4(aVertexPosition * uSize, 0.0, 1.0);",
"}"
].join("\n");
}
function spriteVertexShader(precision) {
return [
"precision " + precision + " float;",
"uniform mat4 uMatrix;",
"uniform vec4 uCrop;",
"uniform vec2 uSize;",
"attribute vec2 aVertexPosition;",
"attribute vec2 aVertexUv;",
"varying vec2 vVertexUv;",
"void main() {",
" vVertexUv = vec2(aVertexUv.x * uCrop.z, aVertexUv.y * uCrop.w) + uCrop.xy;",
" gl_Position = uMatrix * vec4(aVertexPosition * uSize, 0.0, 1.0);",
"}"
].join("\n");
}
function spriteFragmentShader(precision) {
return [
"precision " + precision + " float;",
"uniform float uAlpha;",
"uniform sampler2D uTexture;",
"varying vec2 vVertexUv;",
"void main() {",
" vec4 finalColor = texture2D(uTexture, vVertexUv);",
" finalColor.w *= uAlpha;",
" gl_FragColor = finalColor;",
"}"
].join("\n");
}
function merge(obj, add) {
var key;
for (key in add)
if (obj[key] == undefined) obj[key] = add[key];
return obj;
}
function clear(obj) {
var key;
for (key in obj) delete obj[key];
return obj;
}
return WebGLRenderer2D;
}
);