diff --git a/src/renderer/snapshot/WebGLSnapshot.js b/src/renderer/snapshot/WebGLSnapshot.js index 1c5482b8a..0666ddcee 100644 --- a/src/renderer/snapshot/WebGLSnapshot.js +++ b/src/renderer/snapshot/WebGLSnapshot.js @@ -1,7 +1,7 @@ var WebGLSnapshot = function (sourceCanvas, type, encoderOptions) { - if (type === undefined) { type = 'image/png'; } - if (encoderOptions === undefined) { encoderOptions = 0.92; } + if (!type) { type = 'image/png'; } + if (!encoderOptions) { encoderOptions = 0.92; } var gl = sourceCanvas.getContext('experimental-webgl'); var pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4); diff --git a/src/renderer/webgl/WebGLPipeline.js b/src/renderer/webgl/WebGLPipeline.js index 61333fc0e..75732a8d5 100644 --- a/src/renderer/webgl/WebGLPipeline.js +++ b/src/renderer/webgl/WebGLPipeline.js @@ -15,41 +15,13 @@ var WebGLPipeline = new Class({ this.gl = config.gl; this.vertexCount = 0; this.vertexCapacity = config.vertexCapacity; - this.manager = config.manager; - this.resources = config.manager.resourceManager; + this.renderer = config.renderer; this.vertexData = new ArrayBuffer(config.vertexCapacity * config.vertexSize); - this.vertexBuffer = null; - this.program = null; - this.vertexLayout = config.vertexLayout; + this.vertexBuffer = renderer.createVertexBuffer(this.vertexData.byteLength, gl.STREAM_DRAW); + this.program = renderer.createProgram(config.shader.vert, config.shader.frag); + this.attributes = config.attributes; this.vertexSize = config.vertexSize; this.topology = config.topology; - - // Initialize Shaders and Buffers - { - var gl = this.gl; - var resources = this.resources; - var vertexSize = this.vertexSize; - var vertexLayout = this.vertexLayout; - var program = resources.createShader(this.name, config.shader); - var vertexBuffer = resources.createBuffer(gl.ARRAY_BUFFER, this.vertexData.byteLength, gl.STREAM_DRAW); - - for (var key in vertexLayout) - { - var element = vertexLayout[key]; - - vertexBuffer.addAttribute( - program.getAttribLocation(key), - element.size, - element.type, - element.normalize, - vertexSize, - element.offset - ); - } - - this.vertexBuffer = vertexBuffer; - this.program = program; - } }, shouldFlush: function () @@ -66,12 +38,31 @@ var WebGLPipeline = new Class({ bind: function (overrideProgram) { - // Check if we're using a custom program or - // the default one from the pipeline. - if (!overrideProgram) this.program.bind(); - else overrideProgram.bind(); + var gl = this.gl; + var vertexBuffer = this.vertexBuffer; + var attributes = this.attributes; + var program = (!overrideProgram ? this.program : overrideProgram); + var renderer = this.renderer; + var vertexSize = this.vertexSize; - this.vertexBuffer.bind(); + renderer.setProgram(program); + renderer.setVertexBuffer(vertexBuffer); + + for (var index = 0; index < attributes.length; ++index) + { + var element = attributes[index]; + var location = gl.getAttribLocation(program, element.name); + + if (location >= 0) + { + gl.enableVertexAttribArray(location); + gl.vertexAttribPointer(location, element.size, element.type, element.normalized, vertexSize, element.offset); + } + else + { + gl.disableVertexAttribArray(location); + } + } return this; }, @@ -86,7 +77,7 @@ var WebGLPipeline = new Class({ if (vertexCount === 0) return; - vertexBuffer.updateResource(vertexData, 0); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertexData); gl.drawArrays(topology, 0, vertexCount); this.vertexCount = 0; @@ -96,10 +87,10 @@ var WebGLPipeline = new Class({ destroy: function () { - var resources = this.resources; + var gl = this.gl; - resources.deleteShader(this.program); - resources.deleteBuffer(this.vertexBuffer); + gl.deleteShader(this.program); + gl.deleteBuffer(this.vertexBuffer); this.program = null; this.vertexBuffer = null; @@ -109,67 +100,67 @@ var WebGLPipeline = new Class({ setFloat1: function (name, x) { - this.gl.uniform1f(this.gl.getUniformLocation(this.program.program, name), x); + this.gl.uniform1f(this.gl.getUniformLocation(this.program, name), x); return this; }, setFloat2: function (name, x, y) { - this.gl.uniform2f(this.gl.getUniformLocation(this.program.program, name), x, y); + this.gl.uniform2f(this.gl.getUniformLocation(this.program, name), x, y); return this; }, setFloat3: function (name, x, y, z) { - this.gl.uniform3f(this.gl.getUniformLocation(this.program.program, name), x, y, z); + this.gl.uniform3f(this.gl.getUniformLocation(this.program, name), x, y, z); return this; }, setFloat4: function (name, x, y, z, w) { - this.gl.uniform4f(this.gl.getUniformLocation(this.program.program, name), x, y, z, w); + this.gl.uniform4f(this.gl.getUniformLocation(this.program, name), x, y, z, w); return this; }, setInt1: function (name, x) { - this.gl.uniform1i(this.gl.getUniformLocation(this.program.program, name), x); + this.gl.uniform1i(this.gl.getUniformLocation(this.program, name), x); return this; }, setInt2: function (name, x, y) { - this.gl.uniform2i(this.gl.getUniformLocation(this.program.program, name), x, y); + this.gl.uniform2i(this.gl.getUniformLocation(this.program, name), x, y); return this; }, setInt3: function (name, x, y, z) { - this.gl.uniform3i(this.gl.getUniformLocation(this.program.program, name), x, y, z); + this.gl.uniform3i(this.gl.getUniformLocation(this.program, name), x, y, z); return this; }, setInt4: function (name, x, y, z, w) { - this.gl.uniform4i(this.gl.getUniformLocation(this.program.program, name), x, y, z, w); + this.gl.uniform4i(this.gl.getUniformLocation(this.program, name), x, y, z, w); return this; }, setMatrix2: function (name, transpose, matrix) { - this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix); + this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program, name), transpose, matrix); return this; }, setMatrix3: function (name, transpose, matrix) { - this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix); + this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program, name), transpose, matrix); return this; }, setMatrix4: function (name, transpose, matrix) { - this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program.program, name), transpose, matrix); + this.gl.uniformMatrix2fv(this.gl.getUniformLocation(this.program, name), transpose, matrix); return this; } diff --git a/src/renderer/webgl/WebGLRenderer.js b/src/renderer/webgl/WebGLRenderer.js index 8708b475e..79fefb8ac 100644 --- a/src/renderer/webgl/WebGLRenderer.js +++ b/src/renderer/webgl/WebGLRenderer.js @@ -1,5 +1,7 @@ var Class = require('../../utils/Class'); var CONST = require('../../const'); +var WebGLSnapshot = require('../snapshot/WebGLSnapshot'); +var IsSizePowerOfTwo = require('../../math/pow2/IsSizePowerOfTwo'); var WebGLRenderer = new Class({ @@ -30,6 +32,11 @@ var WebGLRenderer = new Class({ this.contextLost = false; this.autoResize = false; this.pipelines = null; + this.snapshotState = { + callback: null, + type: null, + encoder: null + }; for (var i = 0; i <= 16; i++) { @@ -108,6 +115,7 @@ var WebGLRenderer = new Class({ resize: function (width, height, resolution) { var gl = this.gl; + var piplines = this.pipelines; this.width = width * resolution; this.height = height * resolution; @@ -124,6 +132,10 @@ var WebGLRenderer = new Class({ gl.viewport(0, 0, this.width, this.height); // Update all registered pipelines + for (var pipelineName in pipelines) + { + pipeline[pipelineName].resize(width, height, resolution); + } return this; }, @@ -145,6 +157,14 @@ var WebGLRenderer = new Class({ return this.extensions[extensionName]; }, + flush: function () + { + if (this.currentPipeline) + { + this.currentPipeline.flush(); + } + }, + /* Renderer State Manipulation Functions */ hasPipeline: function (pipelineName) @@ -174,18 +194,30 @@ var WebGLRenderer = new Class({ return this; }, + setPipeline: function (pipelineName) + { + var pipeline = this.getPipeline(pipelineName); + + if (this.currentPipeline !== pipeline) + { + this.currentPipeline = pipeline; + this.currentPipeline.bind(); + } + + return pipeline; + }, + setBlendMode: function (blendModeId) { var gl = this.gl; - var pipeline = this.currentPipeline; var blendMode = this.blendModes[blendModeId]; - if (blendModeId === CONST.BlendModes.SKIP_CHECK || !pipeline) + if (blendModeId === CONST.BlendModes.SKIP_CHECK) return; if (this.currentBlendMode !== blendModeId) { - pipeline.flush(); + this.flush(); gl.enable(gl.BLEND); gl.blendEquation(blendMode.equation); @@ -257,6 +289,44 @@ var WebGLRenderer = new Class({ }, /* Renderer Resource Creation Functions */ + 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) + { + if (source.scaleMode === ScaleModes.LINEAR) + { + filter = gl.LINEAR; + } + else if (source.scaleMode === ScaleModes.NEAREST || this.game.config.pixelArt) + { + 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; + }, + createTexture2D: function (mipLevel, minFilter, magFilter, wrapT, wrapS, format, pixels, width, height, pma) { var gl = this.gl; @@ -389,6 +459,82 @@ var WebGLRenderer = new Class({ this.setIndexBuffer(null); return indexBuffer; + }, + + /* Rendering Functions */ + preRender: function () + { + if (this.contextLost) return; + + }, + + render: function (scene, children, interpolationPercentage, camera) + { + if (this.contextLost) return; + + }, + + postRender: function () + { + if (this.contextLost) return; + + 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); + + return this; } });