UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

122 lines (119 loc) 4.8 kB
import { DebugHelper } from '../../core/debug.js'; import { WorldClusters } from '../lighting/world-clusters.js'; /** * @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js' * @import { RenderAction } from '../composition/render-action.js' */ var tempClusterArray = []; /** * A class managing instances of world clusters used by the renderer for layers with * unique sets of clustered lights. */ class WorldClustersAllocator { destroy() { // empty light cluster if (this._empty) { this._empty.destroy(); this._empty = null; } // all other clusters this._allocated.forEach((cluster)=>{ cluster.destroy(); }); this._allocated.length = 0; } get count() { return this._allocated.length; } // returns an empty light cluster object to be used when no lights are used get empty() { if (!this._empty) { // create cluster structure with no lights var empty = new WorldClusters(this.device); empty.name = 'ClusterEmpty'; // update it once to avoid doing it each frame empty.update([]); this._empty = empty; } return this._empty; } // assign light clusters to render actions that need it assign(renderPasses) { var empty = this.empty; // reuse previously allocated clusters tempClusterArray.push(...this._allocated); this._allocated.length = 0; this._clusters.clear(); // update render actions in passes that use them var passCount = renderPasses.length; for(var p = 0; p < passCount; p++){ var renderPass = renderPasses[p]; var renderActions = renderPass.renderActions; if (renderActions) { // process all render actions var count = renderActions.length; for(var i = 0; i < count; i++){ var ra = renderActions[i]; ra.lightClusters = null; // if the layer has lights used by clusters, and meshes var layer = ra.layer; if (layer.hasClusteredLights && layer.meshInstances.length) { // use existing clusters if the lights on the layer are the same var hash = layer.getLightIdHash(); var existingRenderAction = this._clusters.get(hash); var clusters = existingRenderAction == null ? undefined : existingRenderAction.lightClusters; // no match, needs new clusters if (!clusters) { var _tempClusterArray_pop; // use already allocated cluster from last frame, or create a new one clusters = (_tempClusterArray_pop = tempClusterArray.pop()) != null ? _tempClusterArray_pop : new WorldClusters(this.device); DebugHelper.setName(clusters, "Cluster-" + this._allocated.length); this._allocated.push(clusters); this._clusters.set(hash, ra); } ra.lightClusters = clusters; } // no clustered lights, use the cluster with no lights if (!ra.lightClusters) { ra.lightClusters = empty; } } } } // delete leftovers tempClusterArray.forEach((item)=>item.destroy()); tempClusterArray.length = 0; } update(renderPasses, lighting) { // assign clusters to render actions this.assign(renderPasses); // update all unique clusters this._clusters.forEach((renderAction)=>{ var layer = renderAction.layer; var cluster = renderAction.lightClusters; cluster.update(layer.clusteredLightsSet, lighting); }); } /** * Create a new instance. * * @param {GraphicsDevice} graphicsDevice - The graphics device. */ constructor(graphicsDevice){ /** * Empty cluster with no lights. * * @type {WorldClusters|null} */ this._empty = null; /** * All allocated clusters * * @type {WorldClusters[]} */ this._allocated = []; /** * Render actions with all unique light clusters. The key is the hash of lights on a layer, the * value is a render action with unique light clusters. * * @type {Map<number, RenderAction>} */ this._clusters = new Map(); this.device = graphicsDevice; } } export { WorldClustersAllocator };