dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
1,079 lines (912 loc) • 40.1 kB
text/typescript
//////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2014-present, Egret Technology.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Egret nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY EGRET AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL EGRET AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////////////
namespace egret.web {
/**
* 创建一个canvas。
*/
function createCanvas(width?: number, height?: number): HTMLCanvasElement {
let canvas: HTMLCanvasElement = document.createElement("canvas");
if (!isNaN(width) && !isNaN(height)) {
canvas.width = width;
canvas.height = height;
}
return canvas;
}
/**
* @private
* WebGL上下文对象,提供简单的绘图接口
* 抽象出此类,以实现共用一个context
*/
export class WebGLRenderContext {
public static antialias: boolean;
/**
* 渲染上下文
*/
public context: WebGLRenderingContext;
/**
* 呈现最终绘图结果的画布
*/
public surface: HTMLCanvasElement;
/**
* WebGLRenderContext单例
*/
private static instance: WebGLRenderContext;
public static getInstance(width: number, height: number): WebGLRenderContext {
if (this.instance) {
return this.instance;
}
this.instance = new WebGLRenderContext(width, height);
return this.instance;
}
public $maxTextureSize: number;
/**
* 顶点数组管理器
*/
private vao: WebGLVertexArrayObject;
/**
* 绘制命令管理器
*/
public drawCmdManager: WebGLDrawCmdManager;
/**
* render buffer 堆栈
*/
public $bufferStack: WebGLRenderBuffer[];
/**
* 当前绑定的render buffer
*/
private currentBuffer: WebGLRenderBuffer;
/**
* 推入一个RenderBuffer并绑定
*/
public pushBuffer(buffer: WebGLRenderBuffer): void {
this.$bufferStack.push(buffer);
if (buffer != this.currentBuffer) {
if (this.currentBuffer) {
// this.$drawWebGL();
}
this.drawCmdManager.pushActivateBuffer(buffer);
}
this.currentBuffer = buffer;
}
/**
* 推出一个RenderBuffer并绑定上一个RenderBuffer
*/
public popBuffer(): void {
// 如果只剩下一个buffer,则不执行pop操作
// 保证舞台buffer永远在最开始
if (this.$bufferStack.length <= 1) {
return;
}
let buffer = this.$bufferStack.pop();
let lastBuffer = this.$bufferStack[this.$bufferStack.length - 1];
// 重新绑定
if (buffer != lastBuffer) {
// this.$drawWebGL();
this.drawCmdManager.pushActivateBuffer(lastBuffer);
}
this.currentBuffer = lastBuffer;
}
private bindIndices: boolean;
/**
* 启用RenderBuffer
*/
private activateBuffer(buffer: WebGLRenderBuffer): void {
buffer.rootRenderTarget.activate();
if (!this.bindIndices) {
this.uploadIndicesArray(this.vao.getIndices());
}
buffer.restoreStencil();
buffer.restoreScissor();
this.onResize(buffer.width, buffer.height);
}
/**
* 上传顶点数据
*/
private uploadVerticesArray(array: any): void {
let gl: any = this.context;
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STREAM_DRAW);
// gl.bufferSubData(gl.ARRAY_BUFFER, 0, array);
}
/**
* 上传索引数据
*/
private uploadIndicesArray(array: any): void {
let gl: any = this.context;
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array, gl.STATIC_DRAW);
this.bindIndices = true;
}
private vertexBuffer;
private indexBuffer;
public constructor(width?: number, height?: number) {
this.surface = createCanvas(width, height);
this.initWebGL();
this.$bufferStack = [];
let gl = this.context;
this.vertexBuffer = gl.createBuffer();
this.indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
this.drawCmdManager = new WebGLDrawCmdManager();
this.vao = new WebGLVertexArrayObject();
this.setGlobalCompositeOperation("source-over");
}
/**
* 销毁绘制对象
*/
public destroy(): void {
this.surface.width = this.surface.height = 0;
}
private onResize(width?: number, height?: number): void {
width = width || this.surface.width;
height = height || this.surface.height;
this.projectionX = width / 2;
this.projectionY = -height / 2;
if (this.context) {
this.context.viewport(0, 0, width, height);
}
}
/**
* 改变渲染缓冲的大小并清空缓冲区
* @param width 改变后的宽
* @param height 改变后的高
* @param useMaxSize 若传入true,则将改变后的尺寸与已有尺寸对比,保留较大的尺寸。
*/
public resize(width: number, height: number, useMaxSize?: boolean): void {
let surface = this.surface;
if (useMaxSize) {
if (surface.width < width) {
surface.width = width;
}
if (surface.height < height) {
surface.height = height;
}
}
else {
if (surface.width != width) {
surface.width = width;
}
if (surface.height != height) {
surface.height = height;
}
}
this.onResize();
}
public static glContextId: number = 0;
public glID: number = null;
public projectionX: number = NaN;
public projectionY: number = NaN;
public contextLost: boolean = false;
private initWebGL(): void {
this.onResize();
this.surface.addEventListener("webglcontextlost", this.handleContextLost.bind(this), false);
this.surface.addEventListener("webglcontextrestored", this.handleContextRestored.bind(this), false);
this.getWebGLContext();
let gl = this.context;
this.$maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
}
private handleContextLost() {
this.contextLost = true;
}
private handleContextRestored() {
this.initWebGL();
this.contextLost = false;
}
private getWebGLContext() {
let options = {
antialias: WebGLRenderContext.antialias,
stencil: true//设置可以使用模板(用于不规则遮罩)
};
let gl: any;
//todo 是否使用chrome源码names
//let contextNames = ["moz-webgl", "webkit-3d", "experimental-webgl", "webgl", "3d"];
let names = ["webgl", "experimental-webgl"];
for (let i = 0; i < names.length; i++) {
try {
gl = this.surface.getContext(names[i], options);
} catch (e) {
}
if (gl) {
break;
}
}
if (!gl) {
$error(1021);
}
this.setContext(gl);
}
private setContext(gl: any) {
this.context = gl;
gl.id = WebGLRenderContext.glContextId++;
this.glID = gl.id;
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE);
gl.enable(gl.BLEND);
gl.colorMask(true, true, true, true);
// 目前只使用0号材质单元,默认开启
gl.activeTexture(gl.TEXTURE0);
}
/**
* 开启模版检测
*/
public enableStencilTest(): void {
let gl: any = this.context;
gl.enable(gl.STENCIL_TEST);
}
/**
* 关闭模版检测
*/
public disableStencilTest(): void {
let gl: any = this.context;
gl.disable(gl.STENCIL_TEST);
}
/**
* 开启scissor检测
*/
public enableScissorTest(rect: egret.Rectangle): void {
let gl: any = this.context;
gl.enable(gl.SCISSOR_TEST);
gl.scissor(rect.x, rect.y, rect.width, rect.height);
}
/**
* 关闭scissor检测
*/
public disableScissorTest(): void {
let gl: any = this.context;
gl.disable(gl.SCISSOR_TEST);
}
/**
* 获取像素信息
*/
public getPixels(x, y, width, height, pixels): void {
let gl: any = this.context;
gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
}
/**
* 创建一个WebGLTexture
*/
public createTexture(bitmapData: BitmapData): WebGLTexture {
let gl: any = this.context;
let texture = gl.createTexture();
if (!texture) {
//先创建texture失败,然后lost事件才发出来..
this.contextLost = true;
return;
}
texture.glContext = gl;
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmapData);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
return texture;
}
private createTextureFromCompressedData(data, width, height, levels, internalFormat): WebGLTexture {
return null;
}
/**
* 更新材质的bitmapData
*/
public updateTexture(texture: WebGLTexture, bitmapData: BitmapData): void {
let gl: any = this.context;
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmapData);
}
/**
* 获取一个WebGLTexture
* 如果有缓存的texture返回缓存的texture,如果没有则创建并缓存texture
*/
public getWebGLTexture(bitmapData: BitmapData): WebGLTexture {
if (!bitmapData.webGLTexture) {
if (bitmapData.format == "image") {
bitmapData.webGLTexture = this.createTexture(bitmapData.source);
}
else if (bitmapData.format == "pvr") {//todo 需要支持其他格式
bitmapData.webGLTexture = this.createTextureFromCompressedData(bitmapData.source.pvrtcData, bitmapData.width, bitmapData.height, bitmapData.source.mipmapsCount, bitmapData.source.format);
}
if (bitmapData.$deleteSource && bitmapData.webGLTexture) {
bitmapData.source = null;
}
//todo 默认值
bitmapData.webGLTexture["smoothing"] = true;
}
return bitmapData.webGLTexture;
}
/**
* 清除矩形区域
*/
public clearRect(x: number, y: number, width: number, height: number): void {
if (x != 0 || y != 0 || width != this.surface.width || height != this.surface.height) {
let buffer = this.currentBuffer;
if (buffer.$hasScissor) {
this.setGlobalCompositeOperation("destination-out");
this.drawRect(x, y, width, height);
this.setGlobalCompositeOperation("source-over");
} else {
let m = buffer.globalMatrix;
if (m.b == 0 && m.c == 0) {
x = x * m.a + m.tx;
y = y * m.d + m.ty;
width = width * m.a;
height = height * m.d;
this.enableScissor(x, - y - height + buffer.height, width, height);
this.clear();
this.disableScissor();
} else {
this.setGlobalCompositeOperation("destination-out");
this.drawRect(x, y, width, height);
this.setGlobalCompositeOperation("source-over");
}
}
} else {
this.clear();
}
}
/**
* 设置混色
*/
public setGlobalCompositeOperation(value: string) {
this.drawCmdManager.pushSetBlend(value);
}
/**
* 绘制图片,image参数可以是BitmapData或者renderTarget
*/
public drawImage(image: BitmapData,
sourceX: number, sourceY: number, sourceWidth: number, sourceHeight: number,
destX: number, destY: number, destWidth: number, destHeight: number,
imageSourceWidth: number, imageSourceHeight: number, rotated: boolean, smoothing?: boolean): void {
let buffer = this.currentBuffer;
if (this.contextLost || !image || !buffer) {
return;
}
let texture: WebGLTexture;
let offsetX;
let offsetY;
if (image["texture"] || (image.source && image.source["texture"])) {
// 如果是render target
texture = image["texture"] || image.source["texture"];
buffer.saveTransform();
offsetX = buffer.$offsetX;
offsetY = buffer.$offsetY;
buffer.useOffset();
buffer.transform(1, 0, 0, -1, 0, destHeight + destY * 2);// 翻转
} else if (!image.source && !image.webGLTexture) {
return;
} else {
texture = this.getWebGLTexture(image);
}
if (!texture) {
return;
}
this.drawTexture(texture,
sourceX, sourceY, sourceWidth, sourceHeight,
destX, destY, destWidth, destHeight,
imageSourceWidth, imageSourceHeight,
undefined, undefined, undefined, undefined, rotated, smoothing);
if (image.source && image.source["texture"]) {
buffer.$offsetX = offsetX;
buffer.$offsetY = offsetY;
buffer.restoreTransform();
}
}
/**
* 绘制Mesh
*/
public drawMesh(image: BitmapData,
sourceX: number, sourceY: number, sourceWidth: number, sourceHeight: number,
destX: number, destY: number, destWidth: number, destHeight: number,
imageSourceWidth: number, imageSourceHeight: number,
meshUVs: number[], meshVertices: number[], meshIndices: number[], bounds: Rectangle, rotated: boolean, smoothing: boolean
): void {
let buffer = this.currentBuffer;
if (this.contextLost || !image || !buffer) {
return;
}
let texture: WebGLTexture;
let offsetX;
let offsetY;
if (image["texture"] || (image.source && image.source["texture"])) {
// 如果是render target
texture = image["texture"] || image.source["texture"];
buffer.saveTransform();
offsetX = buffer.$offsetX;
offsetY = buffer.$offsetY;
buffer.useOffset();
buffer.transform(1, 0, 0, -1, 0, destHeight + destY * 2);// 翻转
} else if (!image.source && !image.webGLTexture) {
return;
} else {
texture = this.getWebGLTexture(image);
}
if (!texture) {
return;
}
this.drawTexture(texture,
sourceX, sourceY, sourceWidth, sourceHeight,
destX, destY, destWidth, destHeight,
imageSourceWidth, imageSourceHeight, meshUVs, meshVertices, meshIndices, bounds, rotated, smoothing);
if (image["texture"] || (image.source && image.source["texture"])) {
buffer.$offsetX = offsetX;
buffer.$offsetY = offsetY;
buffer.restoreTransform();
}
}
/**
* 绘制材质
*/
public drawTexture(texture: WebGLTexture,
sourceX: number, sourceY: number, sourceWidth: number, sourceHeight: number,
destX: number, destY: number, destWidth: number, destHeight: number, textureWidth: number, textureHeight: number,
meshUVs?: number[], meshVertices?: number[], meshIndices?: number[], bounds?: Rectangle, rotated?: boolean, smoothing?: boolean): void {
let buffer = this.currentBuffer;
if (this.contextLost || !texture || !buffer) {
return;
}
if (meshVertices && meshIndices) {
if (this.vao.reachMaxSize(meshVertices.length / 2, meshIndices.length)) {
this.$drawWebGL();
}
} else {
if (this.vao.reachMaxSize()) {
this.$drawWebGL();
}
}
if (smoothing != undefined && texture["smoothing"] != smoothing) {
this.drawCmdManager.pushChangeSmoothing(texture, smoothing);
}
if (meshUVs) {
this.vao.changeToMeshIndices();
}
let count = meshIndices ? meshIndices.length / 3 : 2;
// 应用$filter,因为只可能是colorMatrixFilter,最后两个参数可不传
this.drawCmdManager.pushDrawTexture(texture, count, this.$filter);
this.vao.cacheArrays(buffer, sourceX, sourceY, sourceWidth, sourceHeight,
destX, destY, destWidth, destHeight, textureWidth, textureHeight,
meshUVs, meshVertices, meshIndices, rotated);
}
/**
* 绘制矩形(仅用于遮罩擦除等)
*/
public drawRect(x: number, y: number, width: number, height: number): void {
let buffer = this.currentBuffer;
if (this.contextLost || !buffer) {
return;
}
if (this.vao.reachMaxSize()) {
this.$drawWebGL();
}
this.drawCmdManager.pushDrawRect();
this.vao.cacheArrays(buffer, 0, 0, width, height, x, y, width, height, width, height);
}
/**
* 绘制遮罩
*/
public pushMask(x:number, y:number, width:number, height:number): void {
let buffer = this.currentBuffer;
if (this.contextLost || !buffer) {
return;
}
buffer.$stencilList.push({x,y,width,height});
if (this.vao.reachMaxSize()) {
this.$drawWebGL();
}
this.drawCmdManager.pushPushMask();
this.vao.cacheArrays(buffer, 0, 0, width, height, x, y, width, height, width, height);
}
/**
* 恢复遮罩
*/
public popMask(): void {
let buffer = this.currentBuffer;
if (this.contextLost || !buffer) {
return;
}
let mask = buffer.$stencilList.pop();
if (this.vao.reachMaxSize()) {
this.$drawWebGL();
}
this.drawCmdManager.pushPopMask();
this.vao.cacheArrays(buffer, 0, 0, mask.width, mask.height, mask.x, mask.y, mask.width, mask.height, mask.width, mask.height);
}
/**
* 清除颜色缓存
*/
public clear(): void {
this.drawCmdManager.pushClearColor();
}
public $scissorState: boolean = false;
/**
* 开启scissor test
*/
public enableScissor(x: number, y: number, width: number, height: number): void {
let buffer = this.currentBuffer;
this.drawCmdManager.pushEnableScissor(x, y, width, height);
buffer.$hasScissor = true;
}
/**
* 关闭scissor test
*/
public disableScissor(): void {
let buffer = this.currentBuffer;
this.drawCmdManager.pushDisableScissor();
buffer.$hasScissor = false;
}
/**
* 执行目前缓存在命令列表里的命令并清空
*/
public activatedBuffer: WebGLRenderBuffer;
public $drawWebGL() {
if (this.drawCmdManager.drawDataLen == 0 || this.contextLost) {
return;
}
this.uploadVerticesArray(this.vao.getVertices());
// 有mesh,则使用indicesForMesh
if (this.vao.isMesh()) {
this.uploadIndicesArray(this.vao.getMeshIndices());
}
let length = this.drawCmdManager.drawDataLen;
let offset = 0;
for (let i = 0; i < length; i++) {
let data = this.drawCmdManager.drawData[i];
offset = this.drawData(data, offset);
// 计算draw call
if (data.type == DRAWABLE_TYPE.ACT_BUFFER) {
this.activatedBuffer = data.buffer;
}
if (data.type == DRAWABLE_TYPE.TEXTURE || data.type == DRAWABLE_TYPE.RECT || data.type == DRAWABLE_TYPE.PUSH_MASK || data.type == DRAWABLE_TYPE.POP_MASK) {
if (this.activatedBuffer && this.activatedBuffer.$computeDrawCall) {
this.activatedBuffer.$drawCalls++;
}
}
}
// 切换回默认indices
if (this.vao.isMesh()) {
this.uploadIndicesArray(this.vao.getIndices());
}
// 清空数据
this.drawCmdManager.clear();
this.vao.clear();
}
/**
* 执行绘制命令
*/
private drawData(data: any, offset: number) {
if (!data) {
return;
}
let gl = this.context;
let program: EgretWebGLProgram;
let filter = data.filter;
switch (data.type) {
case DRAWABLE_TYPE.TEXTURE:
if (filter) {
if (filter.type === "custom") {
program = EgretWebGLProgram.getProgram(gl, filter.$vertexSrc, filter.$fragmentSrc, filter.$shaderKey);
} else if (filter.type === "colorTransform") {
program = EgretWebGLProgram.getProgram(gl, EgretShaderLib.default_vert, EgretShaderLib.colorTransform_frag, "colorTransform");
} else if (filter.type === "blurX") {
program = EgretWebGLProgram.getProgram(gl, EgretShaderLib.default_vert, EgretShaderLib.blur_frag, "blur");
} else if (filter.type === "blurY") {
program = EgretWebGLProgram.getProgram(gl, EgretShaderLib.default_vert, EgretShaderLib.blur_frag, "blur");
} else if (filter.type === "glow") {
program = EgretWebGLProgram.getProgram(gl, EgretShaderLib.default_vert, EgretShaderLib.glow_frag, "glow");
}
} else {
program = EgretWebGLProgram.getProgram(gl, EgretShaderLib.default_vert, EgretShaderLib.texture_frag, "texture");
}
this.activeProgram(gl, program);
this.syncUniforms(program, filter, data.textureWidth, data.textureHeight);
offset += this.drawTextureElements(data, offset);
break;
case DRAWABLE_TYPE.RECT:
program = EgretWebGLProgram.getProgram(gl, EgretShaderLib.default_vert, EgretShaderLib.primitive_frag, "primitive");
this.activeProgram(gl, program);
this.syncUniforms(program, filter, data.textureWidth, data.textureHeight);
offset += this.drawRectElements(data, offset);
break;
case DRAWABLE_TYPE.PUSH_MASK:
program = EgretWebGLProgram.getProgram(gl, EgretShaderLib.default_vert, EgretShaderLib.primitive_frag, "primitive");
this.activeProgram(gl, program);
this.syncUniforms(program, filter, data.textureWidth, data.textureHeight);
offset += this.drawPushMaskElements(data, offset);
break;
case DRAWABLE_TYPE.POP_MASK:
program = EgretWebGLProgram.getProgram(gl, EgretShaderLib.default_vert, EgretShaderLib.primitive_frag, "primitive");
this.activeProgram(gl, program);
this.syncUniforms(program, filter, data.textureWidth, data.textureHeight);
offset += this.drawPopMaskElements(data, offset);
break;
case DRAWABLE_TYPE.BLEND:
this.setBlendMode(data.value);
break;
case DRAWABLE_TYPE.RESIZE_TARGET:
data.buffer.rootRenderTarget.resize(data.width, data.height);
this.onResize(data.width, data.height);
break;
case DRAWABLE_TYPE.CLEAR_COLOR:
if (this.activatedBuffer) {
let target = this.activatedBuffer.rootRenderTarget;
if (target.width != 0 || target.height != 0) {
target.clear(true);
}
}
break;
case DRAWABLE_TYPE.ACT_BUFFER:
this.activateBuffer(data.buffer);
break;
case DRAWABLE_TYPE.ENABLE_SCISSOR:
let buffer = this.activatedBuffer;
if (buffer) {
if (buffer.rootRenderTarget) {
buffer.rootRenderTarget.enabledStencil();
}
buffer.enableScissor(data.x, data.y, data.width, data.height);
}
break;
case DRAWABLE_TYPE.DISABLE_SCISSOR:
buffer = this.activatedBuffer;
if (buffer) {
buffer.disableScissor();
}
break;
case DRAWABLE_TYPE.SMOOTHING:
gl.bindTexture(gl.TEXTURE_2D, data.texture);
if (data.smoothing) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
}
break;
default:
break;
}
return offset;
}
public currentProgram: EgretWebGLProgram;
private activeProgram(gl: WebGLRenderingContext, program: EgretWebGLProgram): void {
if (program != this.currentProgram) {
gl.useProgram(program.id);
// 目前所有attribute buffer的绑定方法都是一致的
let attribute = program.attributes;
for (let key in attribute) {
if (key === "aVertexPosition") {
gl.vertexAttribPointer(attribute["aVertexPosition"].location, 2, gl.FLOAT, false, 5 * 4, 0);
gl.enableVertexAttribArray(attribute["aVertexPosition"].location);
} else if (key === "aTextureCoord") {
gl.vertexAttribPointer(attribute["aTextureCoord"].location, 2, gl.FLOAT, false, 5 * 4, 2 * 4);
gl.enableVertexAttribArray(attribute["aTextureCoord"].location);
} else if (key === "aColor") {
gl.vertexAttribPointer(attribute["aColor"].location, 1, gl.FLOAT, false, 5 * 4, 4 * 4);
gl.enableVertexAttribArray(attribute["aColor"].location);
}
}
this.currentProgram = program;
}
}
private syncUniforms(program: EgretWebGLProgram, filter: Filter, textureWidth: number, textureHeight: number): void {
let uniforms = program.uniforms;
let isCustomFilter: boolean = filter && filter.type === "custom";
for (let key in uniforms) {
if (key === "projectionVector") {
uniforms[key].setValue({ x: this.projectionX, y: this.projectionY });
} else if (key === "uTextureSize") {
uniforms[key].setValue({ x: textureWidth, y: textureHeight });
} else if (key === "uSampler") {
} else {
let value = filter.$uniforms[key];
if (value !== undefined) {
uniforms[key].setValue(value);
} else {
// egret.warn("filter custom: uniform " + key + " not defined!");
}
}
}
}
/**
* 画texture
**/
private drawTextureElements(data: any, offset: number): number {
let gl: any = this.context;
gl.bindTexture(gl.TEXTURE_2D, data.texture);
let size = data.count * 3;
gl.drawElements(gl.TRIANGLES, size, gl.UNSIGNED_SHORT, offset * 2);
return size;
}
/**
* @private
* 画rect
**/
private drawRectElements(data: any, offset: number): number {
let gl: any = this.context;
// gl.bindTexture(gl.TEXTURE_2D, null);
let size = data.count * 3;
gl.drawElements(gl.TRIANGLES, size, gl.UNSIGNED_SHORT, offset * 2);
return size;
}
/**
* 画push mask
**/
private drawPushMaskElements(data: any, offset: number): number {
let gl: any = this.context;
let size = data.count * 3;
let buffer = this.activatedBuffer;
if (buffer) {
if (buffer.rootRenderTarget) {
buffer.rootRenderTarget.enabledStencil();
}
if (buffer.stencilHandleCount == 0) {
buffer.enableStencil();
gl.clear(gl.STENCIL_BUFFER_BIT);// clear
}
let level = buffer.stencilHandleCount;
buffer.stencilHandleCount++;
gl.colorMask(false, false, false, false);
gl.stencilFunc(gl.EQUAL, level, 0xFF);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
// gl.bindTexture(gl.TEXTURE_2D, null);
gl.drawElements(gl.TRIANGLES, size, gl.UNSIGNED_SHORT, offset * 2);
gl.stencilFunc(gl.EQUAL, level + 1, 0xFF);
gl.colorMask(true, true, true, true);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
}
return size;
}
/**
* 画pop mask
**/
private drawPopMaskElements(data: any, offset: number) {
let gl: any = this.context;
let size = data.count * 3;
let buffer = this.activatedBuffer;
if (buffer) {
buffer.stencilHandleCount--;
if (buffer.stencilHandleCount == 0) {
buffer.disableStencil();// skip this draw
} else {
let level = buffer.stencilHandleCount;
gl.colorMask(false, false, false, false);
gl.stencilFunc(gl.EQUAL, level + 1, 0xFF);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR);
// gl.bindTexture(gl.TEXTURE_2D, null);
gl.drawElements(gl.TRIANGLES, size, gl.UNSIGNED_SHORT, offset * 2);
gl.stencilFunc(gl.EQUAL, level, 0xFF);
gl.colorMask(true, true, true, true);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
}
}
return size;
}
private vertSize: number = 5;
/**
* 设置混色
*/
private setBlendMode(value: string): void {
let gl: any = this.context;
let blendModeWebGL = WebGLRenderContext.blendModesForGL[value];
if (blendModeWebGL) {
gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]);
}
}
// 记录一个colorTransformFilter
// 这是一个优化,实现物体在只有一个变色滤镜的情况下,以最简单方式渲染
// 在$filter有值的情况下,drawImage要注意应用此filter
public $filter: ColorMatrixFilter;
/**
* 应用滤镜绘制给定的render target
* 此方法不会导致input被释放,所以如果需要释放input,需要调用此方法后手动调用release
*/
public drawTargetWidthFilters(filters: Filter[], input: WebGLRenderBuffer): void {
let originInput = input,
filtersLen: number = filters.length,
output: WebGLRenderBuffer;
// 应用前面的滤镜
if (filtersLen > 1) {
for (let i = 0; i < filtersLen - 1; i++) {
let filter = filters[i];
let width: number = input.rootRenderTarget.width;
let height: number = input.rootRenderTarget.height;
output = WebGLRenderBuffer.create(width, height);
output.setTransform(1, 0, 0, 1, 0, 0);
output.globalAlpha = 1;
this.drawToRenderTarget(filter, input, output);
if (input != originInput) {
WebGLRenderBuffer.release(input);
}
input = output;
}
}
// 应用最后一个滤镜并绘制到当前场景中
let filter = filters[filtersLen - 1];
this.drawToRenderTarget(filter, input, this.currentBuffer);
// 释放掉用于交换的buffer
if (input != originInput) {
WebGLRenderBuffer.release(input);
}
}
/**
* 向一个renderTarget中绘制
* */
private drawToRenderTarget(filter: Filter, input: WebGLRenderBuffer, output: WebGLRenderBuffer): void {
if (this.contextLost) {
return;
}
if (this.vao.reachMaxSize()) {
this.$drawWebGL();
}
this.pushBuffer(output);
let originInput = input,
temp: WebGLRenderBuffer,
width: number = input.rootRenderTarget.width,
height: number = input.rootRenderTarget.height;
// 模糊滤镜分别处理blurX与blurY
if (filter.type == "blur") {
let blurXFilter = (<BlurFilter>filter).blurXFilter;
let blurYFilter = (<BlurFilter>filter).blurYFilter;
if (blurXFilter.blurX != 0 && blurYFilter.blurY != 0) {
temp = WebGLRenderBuffer.create(width, height);
temp.setTransform(1, 0, 0, 1, 0, 0);
temp.globalAlpha = 1;
this.drawToRenderTarget((<BlurFilter>filter).blurXFilter, input, temp);
if (input != originInput) {
WebGLRenderBuffer.release(input);
}
input = temp;
filter = blurYFilter;
} else {
filter = blurXFilter.blurX === 0 ? blurYFilter : blurXFilter;
}
}
// 绘制input结果到舞台
output.saveTransform();
output.transform(1, 0, 0, -1, 0, height);
this.vao.cacheArrays(output, 0, 0, width, height, 0, 0, width, height, width, height);
output.restoreTransform();
this.drawCmdManager.pushDrawTexture(input.rootRenderTarget.texture, 2, filter, width, height);
// 释放掉input
if (input != originInput) {
WebGLRenderBuffer.release(input);
}
this.popBuffer();
}
public static blendModesForGL: any = null;
public static initBlendMode(): void {
WebGLRenderContext.blendModesForGL = {};
WebGLRenderContext.blendModesForGL["source-over"] = [1, 771];
WebGLRenderContext.blendModesForGL["lighter"] = [1, 1];
WebGLRenderContext.blendModesForGL["lighter-in"] = [770, 771];
WebGLRenderContext.blendModesForGL["destination-out"] = [0, 771];
WebGLRenderContext.blendModesForGL["destination-in"] = [0, 770];
}
}
WebGLRenderContext.initBlendMode();
}