UNPKG

@animech-public/playcanvas

Version:
2 lines (1 loc) 5.03 kB
import{Vec2 as s}from"../../core/math/vec2.js";import{Vec4 as t}from"../../core/math/vec4.js";import{PIXELFORMAT_RGBA8 as o,FILTER_NEAREST as e,ADDRESS_CLAMP_TO_EDGE as i}from"../../platform/graphics/constants.js";import{RenderTarget as a}from"../../platform/graphics/render-target.js";import{Texture as l}from"../../platform/graphics/texture.js";import{SHADOW_PCF3 as h,LIGHTTYPE_SPOT as r,LIGHTTYPE_OMNI as n}from"../constants.js";import{ShadowMap as c}from"../renderer/shadow-map.js";const d=[],w=[],u=new t,A=new t;class p{constructor(s){this.size=Math.floor(1024*s.w),this.used=!1,this.lightId=-1,this.rect=s}}class f{constructor(h){this.device=h,this.version=1,this.shadowAtlasResolution=2048,this.shadowAtlas=null,this.shadowEdgePixels=3,this.cookieAtlasResolution=4,this.cookieAtlas=new l(this.device,{name:"CookieAtlas",width:this.cookieAtlasResolution,height:this.cookieAtlasResolution,format:o,cubemap:!1,mipmaps:!1,minFilter:e,magFilter:e,addressU:i,addressV:i}),this.cookieRenderTarget=new a({colorBuffer:this.cookieAtlas,depth:!1,flipY:!0}),this.slots=[],this.atlasSplit=[],this.cubeSlotsOffsets=[new s(0,0),new s(0,1),new s(1,0),new s(1,1),new s(2,0),new s(2,1)],this.scissorVec=new t,this.allocateShadowAtlas(1),this.allocateCookieAtlas(1),this.allocateUniforms()}destroy(){this.destroyShadowAtlas(),this.destroyCookieAtlas()}destroyShadowAtlas(){var s;null==(s=this.shadowAtlas)||s.destroy(),this.shadowAtlas=null}destroyCookieAtlas(){var s,t;null==(s=this.cookieAtlas)||s.destroy(),this.cookieAtlas=null,null==(t=this.cookieRenderTarget)||t.destroy(),this.cookieRenderTarget=null}allocateShadowAtlas(s){if(!this.shadowAtlas||this.shadowAtlas.texture.width!==s){this.version++,this.destroyShadowAtlas(),this.shadowAtlas=c.createAtlas(this.device,s,h),this.shadowAtlas.cached=!0;const t=4/this.shadowAtlasResolution;this.scissorVec.set(t,t,-2*t,-2*t)}}allocateCookieAtlas(s){this.cookieAtlas.width!==s&&(this.cookieRenderTarget.resize(s,s),this.version++)}allocateUniforms(){this._shadowAtlasTextureId=this.device.scope.resolve("shadowAtlasTexture"),this._shadowAtlasParamsId=this.device.scope.resolve("shadowAtlasParams"),this._shadowAtlasParams=new Float32Array(2),this._cookieAtlasTextureId=this.device.scope.resolve("cookieAtlasTexture")}updateUniforms(){const s=this.shadowAtlas.renderTargets[0],t=!this.device.isWebGL1&&!0?s.depthBuffer:s.colorBuffer;this._shadowAtlasTextureId.setValue(t),this._shadowAtlasParams[0]=this.shadowAtlasResolution,this._shadowAtlasParams[1]=this.shadowEdgePixels,this._shadowAtlasParamsId.setValue(this._shadowAtlasParams),this._cookieAtlasTextureId.setValue(this.cookieAtlas)}subdivide(s,o){let e=o.atlasSplit;if(!e){const t=Math.ceil(Math.sqrt(s));e=w,e[0]=t,e.length=1}if(i=e,a=this.atlasSplit,i.length!==a.length||!i.every(((s,t)=>s===a[t]))){this.version++,this.slots.length=0,this.atlasSplit.length=0,this.atlasSplit.push(...e);const s=this.atlasSplit[0];if(s>1){const o=1/s;for(let e=0;e<s;e++)for(let i=0;i<s;i++){const a=new t(e*o,i*o,o,o),l=this.atlasSplit[1+e*s+i];if(l>1)for(let s=0;s<l;s++)for(let e=0;e<l;e++){const i=o/l,h=new t(a.x+s*i,a.y+e*i,i,i);this.slots.push(new p(h))}else this.slots.push(new p(a))}}else this.slots.push(new p(new t(0,0,1,1)));this.slots.sort(((s,t)=>t.size-s.size))}var i,a}collectLights(s,t){const o=t.cookiesEnabled,e=t.shadowsEnabled;let i=!1,a=!1;const l=d;l.length=0;return(o||e)&&(s=>{for(let t=0;t<s.length;t++){const h=s[t];if(h.visibleThisFrame){const s=e&&h.castShadows,t=o&&!!h.cookie;i||(i=s),a||(a=t),(s||t)&&l.push(h)}}})(s),l.sort(((s,t)=>t.maxScreenSize-s.maxScreenSize)),i&&this.allocateShadowAtlas(this.shadowAtlasResolution),a&&this.allocateCookieAtlas(this.cookieAtlasResolution),(i||a)&&this.subdivide(l.length,t),l}setupSlot(s,t){s.atlasViewport.copy(t);const o=s.numShadowFaces;for(let e=0;e<o;e++)if(s.castShadows||s._cookie){if(u.copy(t),A.copy(t),s._type===r&&u.add(this.scissorVec),s._type===n){const s=u.z/3,t=this.cubeSlotsOffsets[e];u.x+=s*t.x,u.y+=s*t.y,u.z=s,u.w=s,A.copy(u)}if(s.castShadows){const t=s.getRenderData(null,e);t.shadowViewport.copy(u),t.shadowScissor.copy(A)}}}assignSlot(s,t,o){s.atlasViewportAllocated=!0;const e=this.slots[t];e.lightId=s.id,e.used=!0,o&&(s.atlasSlotUpdated=!0,s.atlasVersion=this.version,s.atlasSlotIndex=t)}update(s,t){this.shadowAtlasResolution=t.shadowAtlasResolution,this.cookieAtlasResolution=t.cookieAtlasResolution;const o=this.collectLights(s,t);if(o.length>0){const s=this.slots;for(let t=0;t<s.length;t++)s[t].used=!1;const t=Math.min(o.length,s.length);for(let e=0;e<t;e++){const t=o[e];t.castShadows&&(t._shadowMap=this.shadowAtlas);const i=s[t.atlasSlotIndex];if(t.atlasVersion===this.version&&t.id===(null==i?void 0:i.lightId)){const o=s[t.atlasSlotIndex];o.size!==s[e].size||o.used||this.assignSlot(t,t.atlasSlotIndex,!1)}}let e=0;for(let i=0;i<t;i++){for(;e<s.length&&s[e].used;)e++;const t=o[i];t.atlasViewportAllocated||this.assignSlot(t,e,!0);const a=s[t.atlasSlotIndex];this.setupSlot(t,a.rect)}}this.updateUniforms()}}export{f as LightTextureAtlas};