@animech-public/playcanvas
Version:
PlayCanvas WebGL game engine
2 lines (1 loc) • 6.87 kB
JavaScript
import{Vec3 as t}from"../../core/math/vec3.js";import{math as s}from"../../core/math/math.js";import{BoundingBox as e}from"../../core/shape/bounding-box.js";import{PIXELFORMAT_L8 as l}from"../../platform/graphics/constants.js";import{MASK_AFFECT_DYNAMIC as i,MASK_AFFECT_LIGHTMAPPED as u,LIGHTTYPE_SPOT as a,LIGHTTYPE_DIRECTIONAL as h}from"../constants.js";import{LightsBuffer as r}from"./lights-buffer.js";const o=new t,n=new t,c=new t,d=new e,_=1e-6;class C{constructor(){this.light=null,this.min=new t,this.max=new t}}class m{constructor(s){this.clusterTexture=void 0,this.device=s,this.name="Untitled",this.reportCount=0,this.boundsMin=new t,this.boundsMax=new t,this.boundsDelta=new t,this._cells=new t(1,1,1),this._cellsLimit=new t,this.cells=this._cells,this.maxCellLightCount=4,this._maxAttenuation=0,this._maxColorValue=0,this._usedLights=[],this._usedLights.push(new C),this.lightsBuffer=new r(s),this.registerUniforms(s)}set maxCellLightCount(t){t!==this._maxCellLightCount&&(this._maxCellLightCount=t,this._cellsDirty=!0)}get maxCellLightCount(){return this._maxCellLightCount}set cells(s){o.copy(s).floor(),this._cells.equals(o)||(this._cells.copy(o),this._cellsLimit.copy(o).sub(t.ONE),this._cellsDirty=!0)}get cells(){return this._cells}destroy(){this.lightsBuffer.destroy(),this.releaseClusterTexture()}releaseClusterTexture(){this.clusterTexture&&(this.clusterTexture.destroy(),this.clusterTexture=null)}registerUniforms(t){this._clusterSkipId=t.scope.resolve("clusterSkip"),this._clusterMaxCellsId=t.scope.resolve("clusterMaxCells"),this._clusterWorldTextureId=t.scope.resolve("clusterWorldTexture"),this._clusterTextureSizeId=t.scope.resolve("clusterTextureSize"),this._clusterTextureSizeData=new Float32Array(3),this._clusterBoundsMinId=t.scope.resolve("clusterBoundsMin"),this._clusterBoundsMinData=new Float32Array(3),this._clusterBoundsDeltaId=t.scope.resolve("clusterBoundsDelta"),this._clusterBoundsDeltaData=new Float32Array(3),this._clusterCellsCountByBoundsSizeId=t.scope.resolve("clusterCellsCountByBoundsSize"),this._clusterCellsCountByBoundsSizeData=new Float32Array(3),this._clusterCellsDotId=t.scope.resolve("clusterCellsDot"),this._clusterCellsDotData=new Float32Array(3),this._clusterCellsMaxId=t.scope.resolve("clusterCellsMax"),this._clusterCellsMaxData=new Float32Array(3),this._clusterCompressionLimit0Id=t.scope.resolve("clusterCompressionLimit0"),this._clusterCompressionLimit0Data=new Float32Array(2)}updateParams(t){t&&(this.cells=t.cells,this.maxCellLightCount=t.maxLightsPerCell,this.lightsBuffer.cookiesEnabled=t.cookiesEnabled,this.lightsBuffer.shadowsEnabled=t.shadowsEnabled,this.lightsBuffer.areaLightsEnabled=t.areaLightsEnabled)}updateCells(){if(this._cellsDirty){this._cellsDirty=!1;const t=this._cells.x,e=this._cells.y,i=this._cells.z,u=t*e*i,a=this.maxCellLightCount*u;let h=Math.ceil(Math.sqrt(a));h=s.roundUp(h,this.maxCellLightCount);const r=Math.ceil(a/h);this._clusterCellsMaxData[0]=t,this._clusterCellsMaxData[1]=e,this._clusterCellsMaxData[2]=i,this._clusterCellsDotData[0]=this.maxCellLightCount,this._clusterCellsDotData[1]=t*i*this.maxCellLightCount,this._clusterCellsDotData[2]=t*this.maxCellLightCount,this.clusters=new Uint8ClampedArray(a),this.counts=new Int32Array(u),this._clusterTextureSizeData[0]=h,this._clusterTextureSizeData[1]=1/h,this._clusterTextureSizeData[2]=1/r,this.releaseClusterTexture(),this.clusterTexture=this.lightsBuffer.createTexture(this.device,h,r,l,"ClusterTexture")}}uploadTextures(){this.clusterTexture.lock().set(this.clusters),this.clusterTexture.unlock(),this.lightsBuffer.uploadTextures()}updateUniforms(){this._clusterSkipId.setValue(this._usedLights.length>1?0:1),this.lightsBuffer.updateUniforms(),this._clusterWorldTextureId.setValue(this.clusterTexture),this._clusterMaxCellsId.setValue(this.maxCellLightCount);const t=this.boundsDelta;this._clusterCellsCountByBoundsSizeData[0]=this._cells.x/t.x,this._clusterCellsCountByBoundsSizeData[1]=this._cells.y/t.y,this._clusterCellsCountByBoundsSizeData[2]=this._cells.z/t.z,this._clusterCellsCountByBoundsSizeId.setValue(this._clusterCellsCountByBoundsSizeData),this._clusterBoundsMinData[0]=this.boundsMin.x,this._clusterBoundsMinData[1]=this.boundsMin.y,this._clusterBoundsMinData[2]=this.boundsMin.z,this._clusterBoundsDeltaData[0]=t.x,this._clusterBoundsDeltaData[1]=t.y,this._clusterBoundsDeltaData[2]=t.z,this._clusterCompressionLimit0Data[0]=this._maxAttenuation,this._clusterCompressionLimit0Data[1]=this._maxColorValue,this._clusterTextureSizeId.setValue(this._clusterTextureSizeData),this._clusterBoundsMinId.setValue(this._clusterBoundsMinData),this._clusterBoundsDeltaId.setValue(this._clusterBoundsDeltaData),this._clusterCellsDotId.setValue(this._clusterCellsDotData),this._clusterCellsMaxId.setValue(this._clusterCellsMaxData),this._clusterCompressionLimit0Id.setValue(this._clusterCompressionLimit0Data)}evalLightCellMinMax(s,e,l){e.copy(s.min),e.sub(this.boundsMin),e.div(this.boundsDelta),e.mul2(e,this.cells),e.floor(),l.copy(s.max),l.sub(this.boundsMin),l.div(this.boundsDelta),l.mul2(l,this.cells),l.ceil(),e.max(t.ZERO),l.min(this._cellsLimit)}collectLights(t){const s=this.lightsBuffer.maxLights,e=this._usedLights;let l=1;t.forEach((t=>{const r=!!(t.mask&(i|u)),o=t.type===a&&0===t._outerConeAngle;if(t.enabled&&t.type!==h&&t.visibleThisFrame&&t.intensity>0&&r&&!o&&l<s){let s;l<e.length?s=e[l]:(s=new C,e.push(s)),s.light=t,t.getBoundingBox(d),s.min.copy(d.getMin()),s.max.copy(d.getMax()),l++}})),e.length=l}evaluateBounds(){const t=this._usedLights,s=this.boundsMin,e=this.boundsMax;if(t.length>1){s.copy(t[1].min),e.copy(t[1].max);for(let l=2;l<t.length;l++)s.min(t[l].min),e.max(t[l].max)}else s.set(0,0,0),e.set(1,1,1);this.boundsDelta.sub2(e,s),this.lightsBuffer.setBounds(s,this.boundsDelta)}evaluateCompressionLimits(t){let s=0,e=0;const l=this._usedLights;for(let i=1;i<l.length;i++){const u=l[i].light;s=Math.max(u.attenuationEnd,s);const a=t?u._linearFinalColor:u._finalColor;e=Math.max(a[0],e),e=Math.max(a[1],e),e=Math.max(a[2],e)}this._maxAttenuation=s+_,this._maxColorValue=e+_,this.lightsBuffer.setCompressionRanges(this._maxAttenuation,this._maxColorValue)}updateClusters(t){this.counts.fill(0),this.clusters.fill(0);const s=this._cells.x,e=this._cells.z,l=this.counts,i=this._maxCellLightCount,u=this.clusters,a=this.maxCellLightCount,h=this._usedLights;for(let r=1;r<h.length;r++){const o=h[r],d=o.light;this.lightsBuffer.addLightData(d,r,t),this.evalLightCellMinMax(o,n,c);const _=n.x,C=c.x,m=n.y,x=c.y,g=n.z,p=c.z;for(let t=_;t<=C;t++)for(let h=g;h<=p;h++)for(let o=m;o<=x;o++){const n=t+s*(h+o*e),c=l[n];c<i&&(u[a*n+c]=r,l[n]=c+1)}}}update(t,s,e){this.updateParams(e),this.updateCells(),this.collectLights(t),this.evaluateBounds(),this.evaluateCompressionLimits(s),this.updateClusters(s),this.uploadTextures()}activate(){this.updateUniforms()}}export{m as WorldClusters};