UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

316 lines (282 loc) 11.4 kB
/** * @author Benjamin D. Richards <benjamindrichards@gmail.com> * @copyright 2013-2026 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var Class = require('../../../utils/Class'); var ShaderSourceFS = require('../shaders/PointLight-frag'); var ShaderSourceVS = require('../shaders/PointLight-vert'); var BatchHandler = require('./BatchHandler'); /** * @classdesc * A batch handler RenderNode that renders `PointLight` Game Objects using WebGL. * * Each PointLight is submitted as a textured quad (4 vertices, 6 indices) into * a shared vertex buffer. The dedicated point-light shader computes the radial * light falloff per fragment, using the light's world position, radius, * attenuation factor, colour, and intensity. Multiple lights are batched * together and flushed in a single draw call when the batch is full or when * the renderer needs to switch context. * * This handler uses no textures; it passes an empty texture array to the * draw call because the lighting is calculated entirely in the fragment shader. * * @class BatchHandlerPointLight * @extends Phaser.Renderer.WebGL.RenderNodes.BatchHandler * @memberof Phaser.Renderer.WebGL.RenderNodes * @constructor * @since 4.0.0 * @param {Phaser.Renderer.WebGL.RenderNodes.RenderNodeManager} manager - The manager that owns this RenderNode. * @param {Phaser.Types.Renderer.WebGL.RenderNodes.BatchHandlerConfig} [config] - The configuration object for this handler. */ var BatchHandlerPointLight = new Class({ Extends: BatchHandler, initialize: function (manager, config) { BatchHandler.call(this, manager, this.defaultConfig, config); /** * An empty texture array used internally. * * @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerPointLight#_emptyTextures * @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper[]} * @private * @since 4.0.0 * @default [] * @readonly */ this._emptyTextures = []; }, /** * The default configuration for this handler. * * @name Phaser.Renderer.WebGL.RenderNodes.BatchHandlerPointLight#defaultConfig * @type {Phaser.Types.Renderer.WebGL.RenderNodes.BatchHandlerConfig} * @since 4.0.0 * @readonly */ defaultConfig: { name: 'BatchHandlerPointLight', verticesPerInstance: 4, indicesPerInstance: 6, shaderName: 'POINTLIGHT', vertexSource: ShaderSourceVS, fragmentSource: ShaderSourceFS, vertexBufferLayout: { usage: 'DYNAMIC_DRAW', layout: [ { name: 'inPosition', size: 2 }, { name: 'inLightPosition', size: 2 }, { name: 'inLightRadius', size: 1 }, { name: 'inLightAttenuation', size: 1 }, { name: 'inLightColor', size: 4 } ] } }, /** * Generate element indices for the instance vertices. * This is called automatically when the node is initialized. * * By default, each instance is a quad. * Each quad is drawn as two triangles, with the vertices in the order: * 0, 0, 1, 2, 3, 3. The quads are drawn as a TRIANGLE_STRIP, so the * repeated vertices form degenerate triangles to connect the quads * without being drawn. * * @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerPointLight#_generateElementIndices * @since 4.0.0 * @private * @param {number} instances - The number of instances to define. * @return {ArrayBuffer} The index buffer data. */ _generateElementIndices: function (instances) { var buffer = new ArrayBuffer(instances * 6 * 2); var indices = new Uint16Array(buffer); var offset = 0; for (var i = 0; i < instances; i++) { var index = i * 4; indices[offset++] = index; indices[offset++] = index; indices[offset++] = index + 1; indices[offset++] = index + 2; indices[offset++] = index + 3; indices[offset++] = index + 3; } return buffer; }, /** * Update the uniforms for the current shader program. * * This method is called automatically when the batch is run. * * @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerPointLight#setupUniforms * @since 4.0.0 * @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context. */ setupUniforms: function (drawingContext) { var programManager = this.programManager; var width = drawingContext.width; var height = drawingContext.height; programManager.setUniform( 'uCameraZoom', drawingContext.camera.zoom ); programManager.setUniform( 'uResolution', [ width, height ] ); drawingContext.renderer.setProjectionMatrixFromDrawingContext(drawingContext); programManager.setUniform( 'uProjectionMatrix', drawingContext.renderer.projectionMatrix.val ); }, /** * Draw then empty the current batch. * * This method is called automatically, by either this node or the manager, * when the batch is full, or when something else needs to be rendered. * * @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerPointLight#run * @since 4.0.0 * @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context. */ run: function (drawingContext) { var instanceCount = this.instanceCount; if (instanceCount === 0) { return; } this.onRunBegin(drawingContext); var programManager = this.programManager; var programSuite = programManager.getCurrentProgramSuite(); if (programSuite) { var program = programSuite.program; var vao = programSuite.vao; this.setupUniforms(drawingContext); programManager.applyUniforms(program); // Update vertex buffers. // Because we frequently aren't filling the entire buffer, // we need to update the buffer with the correct size. this.vertexBufferLayout.buffer.update(this.instanceCount * this.bytesPerInstance); this.manager.renderer.drawElements( drawingContext, this._emptyTextures, program, vao, instanceCount * this.indicesPerInstance, 0 ); } // Reset batch accumulation. this.instanceCount = 0; this.onRunEnd(drawingContext); }, /** * Add a light to the batch. * * @method Phaser.Renderer.WebGL.RenderNodes.BatchHandlerPointLight#batch * @since 4.0.0 * @param {Phaser.Renderer.WebGL.DrawingContext} drawingContext - The current drawing context. * @param {Phaser.GameObjects.PointLight} light - The light to add to the batch. * @param {number} xTL - The top-left x-coordinate of the light. * @param {number} yTL - The top-left y-coordinate of the light. * @param {number} xBL - The bottom-left x-coordinate of the light. * @param {number} yBL - The bottom-left y-coordinate of the light. * @param {number} xTR - The top-right x-coordinate of the light. * @param {number} yTR - The top-right y-coordinate of the light. * @param {number} xBR - The bottom-right x-coordinate of the light. * @param {number} yBR - The bottom-right y-coordinate of the light. * @param {number} lightX - The world x-coordinate of the light's centre, used by the shader to calculate radial falloff. * @param {number} lightY - The world y-coordinate of the light's centre, used by the shader to calculate radial falloff. */ batch: function (drawingContext, light, xTL, yTL, xBL, yBL, xTR, yTR, xBR, yBR, lightX, lightY) { if (this.instanceCount === 0) { this.manager.setCurrentBatchNode(this, drawingContext); } var color = light.color; var intensity = light.intensity; var radius = light.radius; var attenuation = light.attenuation; var r = color.r * intensity; var g = color.g * intensity; var b = color.b * intensity; var a = light.alpha; // Update the vertex buffer. var vertexOffset32 = this.instanceCount * this.floatsPerInstance; var vertexBuffer = this.vertexBufferLayout.buffer; var vertexViewF32 = vertexBuffer.viewF32; // Bottom left vertexViewF32[vertexOffset32++] = xBL; vertexViewF32[vertexOffset32++] = yBL; vertexViewF32[vertexOffset32++] = lightX; vertexViewF32[vertexOffset32++] = lightY; vertexViewF32[vertexOffset32++] = radius; vertexViewF32[vertexOffset32++] = attenuation; vertexViewF32[vertexOffset32++] = r; vertexViewF32[vertexOffset32++] = g; vertexViewF32[vertexOffset32++] = b; vertexViewF32[vertexOffset32++] = a; // Top left vertexViewF32[vertexOffset32++] = xTL; vertexViewF32[vertexOffset32++] = yTL; vertexViewF32[vertexOffset32++] = lightX; vertexViewF32[vertexOffset32++] = lightY; vertexViewF32[vertexOffset32++] = radius; vertexViewF32[vertexOffset32++] = attenuation; vertexViewF32[vertexOffset32++] = r; vertexViewF32[vertexOffset32++] = g; vertexViewF32[vertexOffset32++] = b; vertexViewF32[vertexOffset32++] = a; // Bottom right vertexViewF32[vertexOffset32++] = xBR; vertexViewF32[vertexOffset32++] = yBR; vertexViewF32[vertexOffset32++] = lightX; vertexViewF32[vertexOffset32++] = lightY; vertexViewF32[vertexOffset32++] = radius; vertexViewF32[vertexOffset32++] = attenuation; vertexViewF32[vertexOffset32++] = r; vertexViewF32[vertexOffset32++] = g; vertexViewF32[vertexOffset32++] = b; vertexViewF32[vertexOffset32++] = a; // Top right vertexViewF32[vertexOffset32++] = xTR; vertexViewF32[vertexOffset32++] = yTR; vertexViewF32[vertexOffset32++] = lightX; vertexViewF32[vertexOffset32++] = lightY; vertexViewF32[vertexOffset32++] = radius; vertexViewF32[vertexOffset32++] = attenuation; vertexViewF32[vertexOffset32++] = r; vertexViewF32[vertexOffset32++] = g; vertexViewF32[vertexOffset32++] = b; vertexViewF32[vertexOffset32++] = a; // Increment the instance count. this.instanceCount++; // Check whether the batch should be rendered immediately. // This guarantees that none of the arrays are full above. if (this.instanceCount === this.instancesPerBatch) { this.run(drawingContext); // Now the batch is empty. } } }); module.exports = BatchHandlerPointLight;