2017-07-04 13:48:18 +00:00
|
|
|
var Class = require('../../utils/Class');
|
|
|
|
var CONST = require('../../const');
|
2018-01-20 04:05:56 +00:00
|
|
|
var WebGLSnapshot = require('../snapshot/WebGLSnapshot');
|
|
|
|
var IsSizePowerOfTwo = require('../../math/pow2/IsSizePowerOfTwo');
|
2018-01-17 21:25:43 +00:00
|
|
|
|
2018-01-22 21:21:47 +00:00
|
|
|
// Default Pipelines
|
2018-01-22 22:51:15 +00:00
|
|
|
var TextureTintPipeline = require('./pipelines/TextureTintPipeline');
|
|
|
|
var FlatTintPipeline = require('./pipelines/FlatTintPipeline');
|
2018-01-22 21:21:47 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
var WebGLRenderer = new Class({
|
2016-12-07 02:28:22 +00:00
|
|
|
|
2017-07-04 13:48:18 +00:00
|
|
|
initialize:
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
function WebGLRenderer (game)
|
2016-12-07 02:28:22 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
var renderer = this;
|
|
|
|
var config = {
|
|
|
|
backgroundColor: game.config.backgroundColor,
|
|
|
|
contextCreation: {
|
|
|
|
alpha: false,
|
|
|
|
depth: false, // enable when 3D is added in the future
|
|
|
|
antialias: true,
|
|
|
|
premultipliedAlpha: true,
|
|
|
|
stencil: true,
|
|
|
|
preserveDrawingBuffer: false,
|
|
|
|
failIfMajorPerformanceCaveat: false
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-07-04 13:48:18 +00:00
|
|
|
this.game = game;
|
|
|
|
this.type = CONST.WEBGL;
|
|
|
|
this.width = game.config.width * game.config.resolution;
|
|
|
|
this.height = game.config.height * game.config.resolution;
|
2018-01-17 21:25:43 +00:00
|
|
|
this.canvas = game.canvas;
|
|
|
|
this.blendModes = [];
|
|
|
|
this.contextLost = false;
|
|
|
|
this.autoResize = false;
|
|
|
|
this.pipelines = null;
|
2018-01-20 04:05:56 +00:00
|
|
|
this.snapshotState = {
|
|
|
|
callback: null,
|
|
|
|
type: null,
|
|
|
|
encoder: null
|
|
|
|
};
|
2017-09-13 19:11:40 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
for (var i = 0; i <= 16; i++)
|
|
|
|
{
|
|
|
|
this.blendModes.push({ func: [ WebGLRenderingContext.ONE, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA ], equation: WebGLRenderingContext.FUNC_ADD });
|
|
|
|
}
|
2017-07-04 13:48:18 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
this.blendModes[1].func = [ WebGLRenderingContext.ONE, WebGLRenderingContext.DST_ALPHA ];
|
|
|
|
this.blendModes[2].func = [ WebGLRenderingContext.DST_COLOR, WebGLRenderingContext.ONE_MINUS_SRC_ALPHA ];
|
|
|
|
this.blendModes[3].func = [ WebGLRenderingContext.ONE, WebGLRenderingContext.ONE_MINUS_SRC_COLOR ];
|
2017-07-04 13:48:18 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
// Intenal Renderer State (Textures, Framebuffers, Pipelines, Buffers, etc)
|
|
|
|
this.currentTextures = new Array(16);
|
|
|
|
this.currentFramebuffer = null;
|
|
|
|
this.currentPipeline = null;
|
2018-01-22 21:21:47 +00:00
|
|
|
this.currentProgram = null;
|
2018-01-17 21:25:43 +00:00
|
|
|
this.currentVertexBuffer = null;
|
|
|
|
this.currentIndexBuffer = null;
|
2018-01-22 21:21:47 +00:00
|
|
|
this.currentBlendMode = Infinity;
|
2018-01-17 21:25:43 +00:00
|
|
|
this.currentScissorState = { enabled: false, x: 0, y: 0, w: 0, h: 0 };
|
|
|
|
|
|
|
|
// Setup context lost and restore event listeners
|
|
|
|
this.canvas.addEventListener('webglcontextlost', function (event) {
|
|
|
|
renderer.contextLost = true;
|
|
|
|
event.preventDefault();
|
|
|
|
}, false);
|
2017-07-04 13:48:18 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
this.canvas.addEventListener('webglcontextrestored', function (event) {
|
|
|
|
renderer.contextLost = false;
|
|
|
|
renderer.init(config);
|
|
|
|
}, false);
|
|
|
|
|
|
|
|
// This are initialized post context creation
|
2017-07-04 13:48:18 +00:00
|
|
|
this.gl = null;
|
2018-01-17 21:25:43 +00:00
|
|
|
this.supportedExtensions = null;
|
|
|
|
this.extensions = {};
|
2017-07-04 13:48:18 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
this.init(config);
|
2017-07-04 13:48:18 +00:00
|
|
|
},
|
2017-01-25 12:02:18 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
init: function (config)
|
2017-07-04 13:48:18 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
var canvas = this.canvas;
|
|
|
|
var clearColor = config.backgroundColor;
|
|
|
|
var gl = canvas.getContext('webgl', config.contextCreation) || canvas.getContext('experimental-webgl', config.contextCreation);
|
|
|
|
|
|
|
|
if (!gl)
|
2016-12-07 02:28:22 +00:00
|
|
|
{
|
|
|
|
this.contextLost = true;
|
2018-01-10 20:03:01 +00:00
|
|
|
throw new Error('This browser does not support WebGL. Try using the Canvas pipeline.');
|
2016-12-07 02:28:22 +00:00
|
|
|
}
|
2017-09-13 19:11:40 +00:00
|
|
|
|
2018-01-22 21:21:47 +00:00
|
|
|
this.gl = gl;
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
// Load supported extensions
|
|
|
|
this.supportedExtensions = gl.getSupportedExtensions();
|
2017-09-13 19:11:40 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
// Setup initial WebGL state
|
2016-12-07 02:28:22 +00:00
|
|
|
gl.disable(gl.DEPTH_TEST);
|
|
|
|
gl.disable(gl.CULL_FACE);
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.disable(gl.SCISSOR_TEST);
|
2016-12-07 02:28:22 +00:00
|
|
|
gl.enable(gl.BLEND);
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.clearColor(clearColor.redGL, clearColor.greenGL, clearColor.blueGL, 1.0);
|
2017-08-03 01:09:59 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
// Initialize all textures to null
|
|
|
|
for (var index = 0; index < this.currentTextures.length; ++index)
|
2017-08-03 01:09:59 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
this.currentTextures[index] = null;
|
2017-08-03 01:09:59 +00:00
|
|
|
}
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
// Clear previous pipelines and reload default ones
|
|
|
|
this.pipelines = {};
|
2017-08-03 01:09:59 +00:00
|
|
|
|
2018-01-22 22:51:15 +00:00
|
|
|
this.addPipeline('TextureTintPipeline', new TextureTintPipeline(this.game, gl, this));
|
|
|
|
this.addPipeline('FlatTintPipeline', new FlatTintPipeline(this.game, gl, this));
|
2018-01-22 21:21:47 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
this.setBlendMode(CONST.BlendModes.NORMAL);
|
|
|
|
this.resize(this.width, this.height, this.game.config.resolution);
|
|
|
|
|
|
|
|
return this;
|
2017-08-03 01:09:59 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
resize: function (width, height, resolution)
|
2017-08-03 01:09:59 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
var gl = this.gl;
|
2018-01-22 21:21:47 +00:00
|
|
|
var pipelines = this.pipelines;
|
2017-08-03 01:09:59 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
this.width = width * resolution;
|
|
|
|
this.height = height * resolution;
|
|
|
|
|
|
|
|
this.canvas.width = this.width;
|
|
|
|
this.canvas.height = this.height;
|
2017-08-03 01:09:59 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
if (this.autoResize)
|
2017-08-03 01:09:59 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
this.canvas.style.width = (this.width / resolution) + 'px';
|
|
|
|
this.canvas.style.height = (this.height / resolution) + 'px';
|
2017-08-03 01:09:59 +00:00
|
|
|
}
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.viewport(0, 0, this.width, this.height);
|
2017-08-03 01:09:59 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
// Update all registered pipelines
|
2018-01-20 04:05:56 +00:00
|
|
|
for (var pipelineName in pipelines)
|
|
|
|
{
|
2018-01-22 21:21:47 +00:00
|
|
|
pipelines[pipelineName].resize(width, height, resolution);
|
2018-01-20 04:05:56 +00:00
|
|
|
}
|
2017-08-10 04:17:58 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
return this;
|
2017-09-13 19:43:34 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
hasExtension: function (extensionName)
|
2017-09-13 19:43:34 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
return this.supportedExtensions ? this.supportedExtensions.indexOf(extensionName) : false;
|
2017-09-13 19:43:34 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
getExtension: function (extensionName)
|
2017-01-19 23:20:36 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
if (!this.hasExtension(extensionName)) return null;
|
2017-07-04 13:48:18 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
if (!(extensionName in this.extensions))
|
2017-01-19 23:20:36 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
this.extensions[extensionName] = this.gl.getExtension(extensionName);
|
2017-01-19 23:20:36 +00:00
|
|
|
}
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
return this.extensions[extensionName];
|
2017-01-19 23:20:36 +00:00
|
|
|
},
|
|
|
|
|
2018-01-20 04:05:56 +00:00
|
|
|
flush: function ()
|
|
|
|
{
|
|
|
|
if (this.currentPipeline)
|
|
|
|
{
|
|
|
|
this.currentPipeline.flush();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
/* Renderer State Manipulation Functions */
|
2017-01-19 17:53:20 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
hasPipeline: function (pipelineName)
|
2017-01-19 22:43:41 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
return (pipelineName in this.pipelines);
|
2017-04-07 01:49:15 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
getPipeline: function (pipelineName)
|
2017-04-07 01:49:15 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
if (this.hasPipeline(pipelineName)) return this.pipelines[pipelineName];
|
|
|
|
return null;
|
2017-01-19 22:43:41 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
removePipeline: function (pipelineName)
|
2016-12-07 02:28:22 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
delete this.pipelines[pipelineName];
|
|
|
|
return this;
|
2016-12-07 02:28:22 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
addPipeline: function (pipelineName, pipelineInstance)
|
2016-12-07 02:28:22 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
if (!this.hasPipeline(pipelineName)) this.pipelines[pipelineName] = pipelineInstance;
|
|
|
|
else console.warn('Pipeline', pipelineName, ' already exists.');
|
2017-06-08 16:15:02 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
this.pipelines[pipelineName].resize(this.width, this.height, this.game.config.resolution);
|
2016-12-07 02:28:22 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
return this;
|
2017-01-25 17:10:19 +00:00
|
|
|
},
|
|
|
|
|
2018-01-23 16:38:58 +00:00
|
|
|
setPipeline: function (pipelineInstance, overrideProgram)
|
2018-01-20 04:05:56 +00:00
|
|
|
{
|
2018-01-23 16:38:58 +00:00
|
|
|
if (this.currentPipeline !== pipelineInstance)
|
2018-01-20 04:05:56 +00:00
|
|
|
{
|
2018-01-23 16:38:58 +00:00
|
|
|
this.currentPipeline = pipelineInstance;
|
2018-01-22 21:21:47 +00:00
|
|
|
this.currentPipeline.bind(overrideProgram);
|
2018-01-20 04:05:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return pipeline;
|
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
setBlendMode: function (blendModeId)
|
2017-01-25 17:10:19 +00:00
|
|
|
{
|
2017-01-31 23:06:13 +00:00
|
|
|
var gl = this.gl;
|
2018-01-17 21:25:43 +00:00
|
|
|
var blendMode = this.blendModes[blendModeId];
|
2017-02-07 16:12:20 +00:00
|
|
|
|
2018-01-20 04:05:56 +00:00
|
|
|
if (blendModeId === CONST.BlendModes.SKIP_CHECK)
|
2018-01-17 21:25:43 +00:00
|
|
|
return;
|
2017-06-08 16:15:02 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
if (this.currentBlendMode !== blendModeId)
|
2017-01-19 17:53:20 +00:00
|
|
|
{
|
2018-01-20 04:05:56 +00:00
|
|
|
this.flush();
|
2017-12-15 04:07:16 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.enable(gl.BLEND);
|
|
|
|
gl.blendEquation(blendMode.equation);
|
2017-06-08 16:15:02 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
if (blendMode.func.length > 2)
|
2017-11-06 22:12:19 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.blendFuncSeparate(blendMode.func[0], blendMode.func[1], blendMode.func[2], blendMode.func[3]);
|
2017-11-06 22:12:19 +00:00
|
|
|
}
|
2018-01-17 21:25:43 +00:00
|
|
|
else
|
2017-11-06 22:12:19 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.blendFunc(blendMode.func[0], blendMode.func[1]);
|
2017-11-06 22:12:19 +00:00
|
|
|
}
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
this.currentBlendMode = blendModeId;
|
2017-02-10 00:48:32 +00:00
|
|
|
}
|
2017-06-08 16:15:02 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
return this;
|
2017-02-07 19:30:50 +00:00
|
|
|
},
|
2016-12-07 02:28:22 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
setTexture2D: function (texture, textureUnit)
|
2017-02-07 19:30:50 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
var gl = this.gl;
|
2017-05-16 19:15:01 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
if (texture !== this.currentTextures[textureUnit])
|
2017-05-16 19:15:01 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.activeTexture(gl.TEXTURE0 + textureUnit);
|
|
|
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
2017-05-16 19:15:01 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
this.currentTextures[textureUnit] = texture;
|
|
|
|
}
|
2016-12-07 02:28:22 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
return this;
|
2017-05-16 19:15:01 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
setFramebuffer: function (framebuffer)
|
2017-01-23 21:42:47 +00:00
|
|
|
{
|
2017-12-01 21:55:27 +00:00
|
|
|
var gl = this.gl;
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
if (framebuffer !== this.currentFramebuffer)
|
2017-10-17 03:17:28 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
2017-10-17 03:17:28 +00:00
|
|
|
}
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
return this;
|
2017-02-13 15:27:32 +00:00
|
|
|
},
|
|
|
|
|
2018-01-22 21:21:47 +00:00
|
|
|
setProgram: function (program)
|
|
|
|
{
|
|
|
|
var gl = this.gl;
|
|
|
|
|
|
|
|
if (program !== this.currentProgram)
|
|
|
|
{
|
|
|
|
gl.useProgram(program);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
setVertexBuffer: function (vertexBuffer)
|
2017-02-13 15:27:32 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
var gl = this.gl;
|
2017-06-08 16:15:02 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
if (vertexBuffer !== this.currentVertexBuffer)
|
2017-02-13 15:27:32 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
2017-02-13 15:27:32 +00:00
|
|
|
}
|
2017-06-08 16:15:02 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
return this;
|
2017-03-21 20:45:57 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
setIndexBuffer: function (indexBuffer)
|
2017-05-20 01:16:45 +00:00
|
|
|
{
|
|
|
|
var gl = this.gl;
|
2017-06-08 16:15:02 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
if (indexBuffer !== this.currentIndexBuffer)
|
2017-06-08 16:15:02 +00:00
|
|
|
{
|
2018-01-17 21:25:43 +00:00
|
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
2017-06-08 16:15:02 +00:00
|
|
|
}
|
2017-05-20 01:16:45 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
return this;
|
2017-05-20 01:16:45 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
/* Renderer Resource Creation Functions */
|
2018-01-20 04:05:56 +00:00
|
|
|
createTextureFromSource: function (source, width, height)
|
|
|
|
{
|
|
|
|
var gl = this.gl;
|
|
|
|
var filter = gl.NEAREST;
|
|
|
|
var wrap = gl.CLAMP_TO_EDGE;
|
|
|
|
|
|
|
|
width = source ? source.width : width;
|
|
|
|
height = source ? source.height : height;
|
|
|
|
|
|
|
|
if (IsSizePowerOfTwo(width, height))
|
|
|
|
{
|
|
|
|
wrap = gl.REPEAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!source.glTexture)
|
|
|
|
{
|
2018-01-22 21:21:47 +00:00
|
|
|
if (source.scaleMode === CONST.ScaleModes.LINEAR)
|
2018-01-20 04:05:56 +00:00
|
|
|
{
|
|
|
|
filter = gl.LINEAR;
|
|
|
|
}
|
2018-01-22 21:21:47 +00:00
|
|
|
else if (source.scaleMode === CONST.ScaleModes.NEAREST || this.game.config.pixelArt)
|
2018-01-20 04:05:56 +00:00
|
|
|
{
|
|
|
|
filter = gl.NEAREST;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!source && typeof width === 'number' && typeof height === 'number')
|
|
|
|
{
|
|
|
|
source.glTexture = this.createTexture2D(0, filter, filter, wrap, wrap, gl.RGBA, null, width, height);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
source.glTexture = this.createTexture2D(0, filter, filter, wrap, wrap, gl.RGBA, source.image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return source;
|
|
|
|
},
|
|
|
|
|
2018-01-20 00:37:52 +00:00
|
|
|
createTexture2D: function (mipLevel, minFilter, magFilter, wrapT, wrapS, format, pixels, width, height, pma)
|
2018-01-17 21:25:43 +00:00
|
|
|
{
|
2018-01-20 00:37:52 +00:00
|
|
|
var gl = this.gl;
|
|
|
|
var texture = gl.createTexture();
|
|
|
|
|
|
|
|
pma = (pma === undefined || pma === null) ? true : pma;
|
|
|
|
|
|
|
|
this.setTexture2D(texture, 0);
|
2018-01-17 21:25:43 +00:00
|
|
|
|
2018-01-20 00:37:52 +00:00
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter);
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);
|
|
|
|
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, pma);
|
|
|
|
|
|
|
|
if (pixels === null || pixels === undefined)
|
|
|
|
{
|
|
|
|
gl.texImage2D(gl.TEXTURE_2D, mipLevel, format, width, height, 0, format, gl.UNSIGNED_BYTE, null);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gl.texImage2D(gl.TEXTURE_2D, mipLevel, format, format, gl.UNSIGNED_BYTE, pixels);
|
|
|
|
width = pixels.width;
|
|
|
|
height = pixels.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setTexture2D(null, 0);
|
|
|
|
|
|
|
|
texture.isAlphaPremultiplied = pma;
|
|
|
|
texture.isRenderTexture = false;
|
|
|
|
texture.width = width;
|
|
|
|
texture.height = height;
|
|
|
|
|
|
|
|
return texture;
|
2018-01-17 21:25:43 +00:00
|
|
|
},
|
2017-07-04 13:48:18 +00:00
|
|
|
|
2018-01-20 00:37:52 +00:00
|
|
|
createFramebuffer: function (width, height, renderTexture, addDepthStencilBuffer)
|
2018-01-17 21:25:43 +00:00
|
|
|
{
|
2018-01-20 00:37:52 +00:00
|
|
|
var gl = this.gl;
|
|
|
|
var framebuffer = gl.createFramebuffer();
|
|
|
|
var complete = 0;
|
|
|
|
|
|
|
|
this.setFramebuffer(framebuffer);
|
|
|
|
|
|
|
|
if (addDepthStencilBuffer)
|
|
|
|
{
|
|
|
|
var depthStencilBuffer = gl.createRenderbuffer();
|
|
|
|
gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
|
|
|
|
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
|
|
|
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderTexture.isRenderTexture = true;
|
|
|
|
renderTexture.isAlphaPremultiplied = false;
|
|
|
|
|
|
|
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, renderTexture, 0);
|
|
|
|
|
|
|
|
complete = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
|
|
|
|
|
|
|
|
if (complete !== gl.FRAMEBUFFER_COMPLETE)
|
|
|
|
{
|
|
|
|
var errors = {
|
|
|
|
36054: 'Incomplete Attachment',
|
|
|
|
36055: 'Missing Attachment',
|
|
|
|
36057: 'Incomplete Dimensions',
|
|
|
|
36061: 'Framebuffer Unsupported'
|
|
|
|
};
|
|
|
|
throw new Error('Framebuffer incomplete. Framebuffer status: ' + errors[complete]);
|
|
|
|
}
|
|
|
|
|
|
|
|
framebuffer.renderTexture = renderTexture;
|
2017-07-04 13:48:18 +00:00
|
|
|
|
2018-01-20 00:37:52 +00:00
|
|
|
this.setFramebuffer(null);
|
|
|
|
|
|
|
|
return framebuffer;
|
2018-01-17 21:25:43 +00:00
|
|
|
},
|
2017-07-12 20:55:57 +00:00
|
|
|
|
2018-01-20 00:37:52 +00:00
|
|
|
createProgram: function (vertexShader, fragmentShader)
|
2018-01-17 21:25:43 +00:00
|
|
|
{
|
2018-01-20 00:37:52 +00:00
|
|
|
var gl = this.gl;
|
|
|
|
var program = gl.createProgram();
|
|
|
|
var vs = gl.createShader(gl.VERTEX_SHADER);
|
|
|
|
var fs = gl.createShader(gl.FRAGMENT_SHADER);
|
|
|
|
|
|
|
|
gl.shaderSource(vs, vertexShader);
|
|
|
|
gl.shaderSource(fs, fragmentShader);
|
|
|
|
gl.compileShader(vs);
|
|
|
|
gl.compileShader(fs);
|
|
|
|
|
|
|
|
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))
|
|
|
|
{
|
|
|
|
return new Error('Failed to compile Vertex Shader:\n' + gl.getShaderInfoLog(vs));
|
|
|
|
}
|
|
|
|
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))
|
|
|
|
{
|
|
|
|
return new Error('Failed to compile Fragment Shader:\n' + gl.getShaderInfoLog(fs));
|
|
|
|
}
|
2017-08-03 20:02:57 +00:00
|
|
|
|
2018-01-20 00:37:52 +00:00
|
|
|
gl.attachShader(program, vs);
|
|
|
|
gl.attachShader(program, fs);
|
|
|
|
gl.linkProgram(program);
|
|
|
|
|
|
|
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
|
|
|
|
{
|
|
|
|
return new Error('Failed to link program:\n' + gl.getProgramInfoLog(program));
|
|
|
|
}
|
|
|
|
|
|
|
|
return program;
|
2018-01-17 21:25:43 +00:00
|
|
|
},
|
2017-03-21 20:45:57 +00:00
|
|
|
|
2018-01-20 00:37:52 +00:00
|
|
|
createVertexBuffer: function (initialDataOrSize, bufferUsage)
|
2018-01-17 21:25:43 +00:00
|
|
|
{
|
2018-01-20 00:37:52 +00:00
|
|
|
var gl = this.gl;
|
|
|
|
var vertexBuffer = gl.createBuffer();
|
2017-03-21 20:45:57 +00:00
|
|
|
|
2018-01-20 00:37:52 +00:00
|
|
|
this.setVertexBuffer(vertexBuffer);
|
|
|
|
gl.bufferData(gl.ARRAY_BUFFER, initialDataOrSize, bufferUsage);
|
|
|
|
this.setVertexBuffer(null);
|
|
|
|
|
|
|
|
return vertexBuffer;
|
2017-07-04 13:48:18 +00:00
|
|
|
},
|
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
createIndexBuffer: function ()
|
2017-07-04 13:48:18 +00:00
|
|
|
{
|
2018-01-20 00:37:52 +00:00
|
|
|
var gl = this.gl;
|
|
|
|
var indexBuffer = gl.createBuffer();
|
|
|
|
|
|
|
|
this.setIndexBuffer(indexBuffer);
|
|
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, initialDataOrSize, bufferUsage);
|
|
|
|
this.setIndexBuffer(null);
|
|
|
|
|
|
|
|
return indexBuffer;
|
2018-01-20 04:05:56 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/* Rendering Functions */
|
|
|
|
preRender: function ()
|
|
|
|
{
|
|
|
|
if (this.contextLost) return;
|
|
|
|
|
2018-01-22 22:51:15 +00:00
|
|
|
var gl = this.gl;
|
|
|
|
var color = this.game.config.backgroundColor;
|
|
|
|
|
|
|
|
// Bind custom framebuffer here
|
|
|
|
gl.clearColor(color.redGL, color.greenGL, color.blueGL, color.alphaGL);
|
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
2018-01-20 04:05:56 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
render: function (scene, children, interpolationPercentage, camera)
|
|
|
|
{
|
|
|
|
if (this.contextLost) return;
|
|
|
|
|
2018-01-22 21:21:47 +00:00
|
|
|
var gl = this.gl;
|
|
|
|
var list = children.list;
|
|
|
|
var childCount = list.length;
|
2018-01-22 21:37:47 +00:00
|
|
|
var scissorEnabled = (camera.x !== 0 || camera.y !== 0 || camera.width !== gl.canvas.width || camera.height !== gl.canvas.height);
|
|
|
|
var pipeline = null;
|
2018-01-22 21:21:47 +00:00
|
|
|
|
2018-01-22 21:37:47 +00:00
|
|
|
this.currentScissorState.enabled = scissorEnabled;
|
|
|
|
|
|
|
|
if (scissorEnabled)
|
|
|
|
{
|
|
|
|
gl.enable(gl.SCISSOR_TEST);
|
|
|
|
this.currentScissorState.x = camera.x;
|
|
|
|
this.currentScissorState.y = gl.drawingBufferHeight - camera.y - camera.height;
|
|
|
|
this.currentScissorState.width = camera.width;
|
|
|
|
this.currentScissorState.height = camera.height;
|
|
|
|
|
|
|
|
gl.scissor(this.currentScissorState.x, this.currentScissorState.y, this.currentScissorState.width, this.currentScissorState.height);
|
|
|
|
}
|
|
|
|
|
2018-01-22 21:21:47 +00:00
|
|
|
for (var index = 0; index < childCount; ++index)
|
|
|
|
{
|
|
|
|
var child = list[index];
|
|
|
|
|
|
|
|
if (!child.willRender())
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-01-22 21:37:47 +00:00
|
|
|
if (child.blendMode !== this.currentBlendMode)
|
|
|
|
{
|
|
|
|
this.setBlendMode(child.blendMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child.mask)
|
|
|
|
{
|
|
|
|
child.mask.preRenderWebGL(this, child, camera);
|
|
|
|
}
|
|
|
|
|
2018-01-22 21:21:47 +00:00
|
|
|
child.renderWebGL(this, child, interpolationPercentage, camera);
|
|
|
|
|
2018-01-22 21:37:47 +00:00
|
|
|
if (child.mask)
|
|
|
|
{
|
|
|
|
child.mask.postRenderWebGL(this, child);
|
|
|
|
}
|
2018-01-22 22:51:15 +00:00
|
|
|
|
2018-01-22 21:37:47 +00:00
|
|
|
pipeline = this.currentPipeline;
|
|
|
|
|
|
|
|
if (pipeline && pipeline.shouldFlush())
|
|
|
|
{
|
|
|
|
pipeline.flush();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scissorEnabled)
|
|
|
|
{
|
|
|
|
gl.disable(gl.SCISSOR_TEST);
|
2018-01-22 21:21:47 +00:00
|
|
|
}
|
2018-01-20 04:05:56 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
postRender: function ()
|
|
|
|
{
|
|
|
|
if (this.contextLost) return;
|
|
|
|
|
2018-01-22 22:51:15 +00:00
|
|
|
// Unbind custom framebuffer here
|
|
|
|
|
2018-01-20 04:05:56 +00:00
|
|
|
if (this.snapshotState.callback)
|
|
|
|
{
|
|
|
|
this.snapshotState.callback(WebGLSnapshot(this.canvas, this.snapshotState.type, this.snapshotState.encoder));
|
|
|
|
this.snapshotState.callback = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
snapshot: function (callback, type, encoderOptions)
|
|
|
|
{
|
|
|
|
this.snapshotState.callback = callback;
|
|
|
|
this.snapshotState.type = type;
|
|
|
|
this.snapshotState.encoder = encoderOptions;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
canvasToTexture: function (srcCanvas, dstTexture, shouldReallocate)
|
|
|
|
{
|
|
|
|
var gl = this.gl;
|
|
|
|
|
|
|
|
if (!dstTexture)
|
|
|
|
{
|
|
|
|
dstTexture = this.createTextureFromSource(srcCanvas, srcCanvas.width, srcCanvas.height);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.setTexture2D(dstTexture);
|
|
|
|
|
|
|
|
if (!shouldReallocate)
|
|
|
|
{
|
|
|
|
gl.texSubImage2D(0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, srcCanvas);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, srcCanvas);
|
|
|
|
dstTexture.width = srcCanvas.width;
|
|
|
|
dstTexture.height = srcCanvas.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setTexture2D(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dstTexture;
|
|
|
|
},
|
|
|
|
|
|
|
|
setTextureFilter: function (texture, filter)
|
|
|
|
{
|
|
|
|
var gl = this.gl;
|
|
|
|
var glFilter = [ gl.LINEAR, gl.NEAREST ][filter];
|
|
|
|
|
|
|
|
this.setTexture2D(texture, 0);
|
|
|
|
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, glFilter);
|
|
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, glFilter);
|
|
|
|
|
|
|
|
this.setTexture2D(null, 0);
|
|
|
|
|
2018-01-22 21:21:47 +00:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setFloat1: function (program, name, x)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniform1f(this.gl.getUniformLocation(program, name), x);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setFloat2: function (program, name, x, y)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniform2f(this.gl.getUniformLocation(program, name), x, y);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setFloat3: function (program, name, x, y, z)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniform3f(this.gl.getUniformLocation(program, name), x, y, z);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setFloat4: function (program, name, x, y, z, w)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniform4f(this.gl.getUniformLocation(program, name), x, y, z, w);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setInt1: function (program, name, x)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniform1i(this.gl.getUniformLocation(program, name), x);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setInt2: function (program, name, x, y)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniform2i(this.gl.getUniformLocation(program, name), x, y);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setInt3: function (program, name, x, y, z)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniform3i(this.gl.getUniformLocation(program, name), x, y, z);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setInt4: function (program, name, x, y, z, w)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniform4i(this.gl.getUniformLocation(program, name), x, y, z, w);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setMatrix2: function (program, name, transpose, matrix)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniformMatrix2fv(this.gl.getUniformLocation(program, name), transpose, matrix);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setMatrix3: function (program, name, transpose, matrix)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniformMatrix3fv(this.gl.getUniformLocation(program, name), transpose, matrix);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setMatrix4: function (program, name, transpose, matrix)
|
|
|
|
{
|
|
|
|
this.setProgram(program);
|
|
|
|
this.gl.uniformMatrix4fv(this.gl.getUniformLocation(program, name), transpose, matrix);
|
2018-01-20 04:05:56 +00:00
|
|
|
return this;
|
2017-01-23 21:42:47 +00:00
|
|
|
}
|
2017-07-04 13:48:18 +00:00
|
|
|
|
|
|
|
});
|
2016-12-07 02:28:22 +00:00
|
|
|
|
2018-01-17 21:25:43 +00:00
|
|
|
module.exports = WebGLRenderer;
|