@animech-public/playcanvas
Version:
PlayCanvas WebGL game engine
2 lines (1 loc) • 10.9 kB
JavaScript
import{Mat3 as e}from"../../core/math/mat3.js";import{BoundingBox as t}from"../../core/shape/bounding-box.js";import{PRIMITIVE_TRIFAN as s,PRIMITIVE_TRISTRIP as r,SEMANTIC_BLENDINDICES as n,TYPE_FLOAT32 as o,typedArrayTypes as i,typedArrayTypesByteSize as a,SEMANTIC_POSITION as h,SEMANTIC_NORMAL as c,SEMANTIC_TANGENT as m,typedArrayIndexFormats as l,PRIMITIVE_TRIANGLES as d}from"../../platform/graphics/constants.js";import{SPRITE_RENDERMODE_SIMPLE as u}from"../constants.js";import{Mesh as p}from"../mesh.js";import{MeshInstance as f}from"../mesh-instance.js";import{shaderChunks as _}from"../shader-lib/chunks/chunks.js";import{Batch as b}from"./batch.js";import{BatchGroup as y}from"./batch-group.js";import{SkinBatchInstance as I}from"./skin-batch-instance.js";function g(e,t){if(e&&!t)return!1;if(!e&&t)return!1;if((e=e.data)===(t=t.data))return!0;if(e instanceof Float32Array&&t instanceof Float32Array){if(e.length!==t.length)return!1;for(let s=0;s<e.length;s++)if(e[s]!==t[s])return!1;return!0}return!1}function G(e,t){for(const s in e)if(e.hasOwnProperty(s)&&!g(e[s],t[s]))return!1;for(const s in t)if(t.hasOwnProperty(s)&&!g(t[s],e[s]))return!1;return!0}const x=[0,1,3,2,3,1],v=[0,1,3,0,3,2],B=new e;function w(e){return e.node.worldTransform.scaleSign}class M{constructor(e,t,s){this.device=e,this.rootNode=t,this.scene=s,this._init=!1,this._batchGroups={},this._batchGroupCounter=0,this._batchList=[],this._dirtyGroups=[]}destroy(){this.device=null,this.rootNode=null,this.scene=null,this._batchGroups={},this._batchList=[],this._dirtyGroups=[]}addGroup(e,t,s,r,n){if(void 0===r&&(r=this._batchGroupCounter,this._batchGroupCounter++),this._batchGroups[r])return;const o=new y(r,e,t,s,n);return this._batchGroups[r]=o,o}removeGroup(e){if(!this._batchGroups[e])return;const t=[];for(let s=0;s<this._batchList.length;s++)this._batchList[s].batchGroupId===e?this.destroyBatch(this._batchList[s]):t.push(this._batchList[s]);this._batchList=t,this._removeModelsFromBatchGroup(this.rootNode,e),delete this._batchGroups[e]}markGroupDirty(e){this._dirtyGroups.indexOf(e)<0&&this._dirtyGroups.push(e)}getGroupByName(e){const t=this._batchGroups;for(const s in t)if(t.hasOwnProperty(s)&&t[s].name===e)return t[s];return null}getBatches(e){const t=[],s=this._batchList.length;for(let r=0;r<s;r++){const s=this._batchList[r];s.batchGroupId===e&&t.push(s)}return t}_removeModelsFromBatchGroup(e,t){if(e.enabled){e.model&&e.model.batchGroupId===t&&(e.model.batchGroupId=-1),e.render&&e.render.batchGroupId===t&&(e.render.batchGroupId=-1),e.element&&e.element.batchGroupId===t&&(e.element.batchGroupId=-1),e.sprite&&e.sprite.batchGroupId===t&&(e.sprite.batchGroupId=-1);for(let s=0;s<e._children.length;s++)this._removeModelsFromBatchGroup(e._children[s],t)}}insert(e,t,s){const r=this._batchGroups[t];r&&r._obj[e].indexOf(s)<0&&(r._obj[e].push(s),this.markGroupDirty(t))}remove(e,t,s){const r=this._batchGroups[t];if(r){const n=r._obj[e].indexOf(s);n>=0&&(r._obj[e].splice(n,1),this.markGroupDirty(t))}}_extractRender(e,t,s,r){return e.render&&(t=r[e.render.batchGroupId]=t.concat(e.render.meshInstances),e.render.removeFromLayers()),t}_extractModel(e,t,s,r){return e.model&&e.model.model&&(t=r[e.model.batchGroupId]=t.concat(e.model.meshInstances),e.model.removeModelFromLayers()),t}_extractElement(e,t,s){if(!e.element)return;let r=!1;e.element._text&&e.element._text._model.meshInstances.length>0?(t.push(e.element._text._model.meshInstances[0]),e.element.removeModelFromLayers(e.element._text._model),r=!0):e.element._image&&(t.push(e.element._image._renderable.meshInstance),e.element.removeModelFromLayers(e.element._image._renderable.model),e.element._image._renderable.unmaskMeshInstance&&(t.push(e.element._image._renderable.unmaskMeshInstance),e.element._image._renderable.unmaskMeshInstance.stencilFront&&e.element._image._renderable.unmaskMeshInstance.stencilBack||(e.element._dirtifyMask(),e.element._onPrerender())),r=!0),r&&(s._ui=!0)}_collectAndRemoveMeshInstances(e,t){for(let s=0;s<t.length;s++){const r=t[s],n=this._batchGroups[r];if(!n)continue;let o=e[r];o||(o=e[r]=[]);for(let t=0;t<n._obj.model.length;t++)o=this._extractModel(n._obj.model[t],o,n,e);for(let t=0;t<n._obj.render.length;t++)o=this._extractRender(n._obj.render[t],o,n,e);for(let e=0;e<n._obj.element.length;e++)this._extractElement(n._obj.element[e],o,n);for(let e=0;e<n._obj.sprite.length;e++){const t=n._obj.sprite[e];t.sprite&&t.sprite._meshInstance&&(n.dynamic||t.sprite.sprite._renderMode===u)&&(o.push(t.sprite._meshInstance),t.sprite.removeModelFromLayers(),n._sprite=!0,t.sprite._batchGroup=n)}}}generate(e){const t={};e||(e=Object.keys(this._batchGroups));const s=[];for(let t=0;t<this._batchList.length;t++)e.indexOf(this._batchList[t].batchGroupId)<0?s.push(this._batchList[t]):this.destroyBatch(this._batchList[t]);if(this._batchList=s,this._collectAndRemoveMeshInstances(t,e),e===this._dirtyGroups)this._dirtyGroups.length=0;else{const t=[];for(let s=0;s<this._dirtyGroups.length;s++)e.indexOf(this._dirtyGroups[s])<0&&t.push(this._dirtyGroups[s]);this._dirtyGroups=t}let r,n,o,i;for(const e in t)if(t.hasOwnProperty(e)&&(r=t[e],o=this._batchGroups[e],o)){n=this.prepare(r,o.dynamic,o.maxAabbSize,o._ui||o._sprite);for(let t=0;t<n.length;t++)i=this.create(n[t],o.dynamic,parseInt(e,10)),i&&i.addToLayers(this.scene,o.layers)}}prepare(e,s,r=Number.POSITIVE_INFINITY,n){if(0===e.length)return[];const o=.5*r,i=this.device.supportsBoneTextures?1024:this.device.boneLimit,a=this.device.extUintElement?4294967295:65535,h=new t,c=new t;let m,l=null;const d=[];let u=0;n&&e.sort(((e,t)=>e.drawOrder-t.drawOrder));let p,f=e;const _=n?function(e){l?l.add(e.aabb):l=e.aabb.clone(),p.push(e)}:function(e){p.push(e)};for(;f.length>0;){d[u]=[f[0]],p=[];const e=f[0].material,t=f[0].layer,r=f[0]._shaderDefs,b=f[0].parameters,y=f[0].stencilFront;let I=f[0].mesh.vertexBuffer.getNumVertices();const g=f[0].drawOrder;h.copy(f[0].aabb);const x=w(f[0]),v=f[0].mesh.vertexBuffer.format.batchingHash,B=f[0].mesh.primitive[0].indexed;l=null;for(let M=1;M<f.length;M++){const k=f[M];if(s&&d[u].length>=i){p=p.concat(f.slice(M));break}e!==k.material||t!==k.layer||v!==k.mesh.vertexBuffer.format.batchingHash||B!==k.mesh.primitive[0].indexed||r!==k._shaderDefs||I+k.mesh.vertexBuffer.getNumVertices()>a?_(k):(c.copy(h),c.add(k.aabb),c.halfExtents.x>o||c.halfExtents.y>o||c.halfExtents.z>o?_(k):(!y||(m=k.stencilFront)&&y.func===m.func&&y.zpass===m.zpass)&&x===w(k)&&G(b,k.parameters)?n&&l&&l.intersects(k.aabb)&&k.drawOrder!==g?_(k):(h.add(k.aabb),I+=k.mesh.vertexBuffer.getNumVertices(),d[u].push(k)):_(k))}u++,f=p}return d}collectBatchedMeshData(e,t){let i=null,a=0,h=0,c=null;for(let m=0;m<e.length;m++)if(e[m].visible){const l=e[m].mesh;if(a+=l.vertexBuffer.numVertices,l.primitive[0].indexed)h+=l.primitive[0].count;else{const e=l.primitive[0].type;e!==s&&e!==r||4===l.primitive[0].count&&(h+=6)}if(!i){c=e[m].material,i={};const s=l.vertexBuffer.format.elements;for(let e=0;e<s.length;e++){i[s[e].name]={numComponents:s[e].numComponents,dataType:s[e].dataType,normalize:s[e].normalize,count:0}}t&&(i[n]={numComponents:1,dataType:o,normalize:!1,count:0})}}return{streams:i,batchNumVerts:a,batchNumIndices:h,material:c}}create(e,t,o){if(!this._init){const e=`#define BONE_LIMIT ${this.device.getBoneLimit()}\n`;this.transformVS=`${e}#define DYNAMICBATCH\n${_.transformVS}`,this.skinTexVS=_.skinBatchTexVS,this.skinConstVS=_.skinBatchConstVS,this.vertexFormats={},this._init=!0}let u,y,g,G=null,M=null;const k=this.collectBatchedMeshData(e,t);if(k.streams){const _=k.streams;let L=k.material;const S=k.batchNumVerts,j=k.batchNumIndices;let V,T,F;M=new b(e,t,o),this._batchList.push(M);let N,C=0,O=0;const A=new(S<=65535?Uint16Array:Uint32Array)(j);for(u in _)G=_[u],G.typeArrayType=i[G.dataType],G.elementByteSize=a[G.dataType],G.buffer=new G.typeArrayType(S*G.numComponents);for(let o=0;o<e.length;o++)if(e[o].visible){for(u in y=e[o].mesh,g=y.vertexBuffer.numVertices,t||(N=e[o].node.getWorldTransform()),_)if(u!==n){G=_[u];const e=new G.typeArrayType(G.buffer.buffer,G.elementByteSize*G.count),s=y.getVertexStream(u,e)*G.numComponents;if(G.count+=s,!t&&G.numComponents>=3)if(u===h){const t=N.data,r=t[0],n=t[1],o=t[2],i=t[4],a=t[5],h=t[6],c=t[8],m=t[9],l=t[10],d=t[12],u=t[13],p=t[14];let f,_,b;for(let t=0;t<s;t+=G.numComponents)f=e[t],_=e[t+1],b=e[t+2],e[t]=f*r+_*i+b*c+d,e[t+1]=f*n+_*a+b*m+u,e[t+2]=f*o+_*h+b*l+p}else if(u===c||u===m){B.invertMat4(N).transpose();const[t,r,n,o,i,a,h,c,m]=B.data;let l,d,u;for(let p=0;p<s;p+=G.numComponents)l=e[p],d=e[p+1],u=e[p+2],e[p]=l*t+d*o+u*h,e[p+1]=l*r+d*i+u*c,e[p+2]=l*n+d*a+u*m}}if(t){G=_[n];for(let e=0;e<g;e++)G.buffer[G.count++]=o}if(y.primitive[0].indexed){V=y.primitive[0].base,T=y.primitive[0].count;const e=y.indexBuffer[0].getFormat();F=new l[e](y.indexBuffer[0].storage)}else{const e=y.primitive[0].type;if(e===s||e===r){if(4!==y.primitive[0].count){T=0;continue}V=0,T=6,F=e===s?x:v}}for(let e=0;e<T;e++)A[e+O]=F[V+e]+C;O+=T,C+=g}for(u in y=new p(this.device),_)G=_[u],y.setVertexStream(u,G.buffer,G.numComponents,void 0,G.dataType,G.normalize);A.length>0&&y.setIndices(A),y.update(d,!1),t&&(L=L.clone(),L.chunks.transformVS=this.transformVS,L.chunks.skinTexVS=this.skinTexVS,L.chunks.skinConstVS=this.skinConstVS,L.update());const z=new f(y,L,this.rootNode);z.castShadow=M.origMeshInstances[0].castShadow,z.parameters=M.origMeshInstances[0].parameters,z.layer=M.origMeshInstances[0].layer,z._shaderDefs=M.origMeshInstances[0]._shaderDefs,z.cull=M.origMeshInstances[0].cull;const D=this._batchGroups[o];if(D&&D._ui&&(z.cull=!1),t){const e=[];for(let t=0;t<M.origMeshInstances.length;t++)e.push(M.origMeshInstances[t].node);z.skinInstance=new I(this.device,e,this.rootNode)}z._updateAabb=!1,z.drawOrder=M.origMeshInstances[0].drawOrder,z.stencilFront=M.origMeshInstances[0].stencilFront,z.stencilBack=M.origMeshInstances[0].stencilBack,z.flipFacesFactor=w(M.origMeshInstances[0]),z.castShadow=M.origMeshInstances[0].castShadow,M.meshInstance=z,M.updateBoundingBox()}return M}updateAll(){this._dirtyGroups.length>0&&this.generate(this._dirtyGroups);for(let e=0;e<this._batchList.length;e++)this._batchList[e].dynamic&&this._batchList[e].updateBoundingBox()}clone(e,t){const s=new b(t,e.dynamic,e.batchGroupId);this._batchList.push(s);const r=[];for(let e=0;e<t.length;e++)r.push(t[e].node);return s.meshInstance=new f(e.meshInstance.mesh,e.meshInstance.material,e.meshInstance.node),s.meshInstance._updateAabb=!1,s.meshInstance.parameters=t[0].parameters,s.meshInstance.cull=t[0].cull,s.meshInstance.layer=t[0].layer,e.dynamic&&(s.meshInstance.skinInstance=new I(this.device,r,this.rootNode)),s.meshInstance.castShadow=e.meshInstance.castShadow,s.meshInstance._shader=e.meshInstance._shader.slice(),s.meshInstance.castShadow=e.meshInstance.castShadow,s}destroyBatch(e){e.destroy(this.scene,this._batchGroups[e.batchGroupId].layers)}}export{M as BatchManager};