import{Color,CubeTextureLoader,InstancedMesh,Matrix4,Mesh,Object3D,Quaternion,Scene,Vector3}from"three";import{extend,toArray}from"../common/util";import{createDTModel}from"./model";import{convertUnityToThreeJSRotation,gltfToObject3D,mergeGeometries}from"./tool";import EventEmitter,{DTEvent}from"../common/event-emitter";import{ComponentType,RenderEvent}from"../common/constant";const createSkyBox=(a,b=".jpg")=>{return new CubeTextureLoader().load([a+"posx"+b,a+"negx"+b,a+"posy"+b,a+"negy"+b,a+"posz"+b,a+"negz"+b])};export default function createScene(){let a=new Scene;a.background=new Color("#424242");let b=extend({skybox:(b,c=".jpg")=>{a.background=createSkyBox(b,c)},skycolor:b=>{a.background=new Color(b)},add(b){return a.add(b),this}},a);return b}export async function createSceneRender(a,b,c,d,e){function f(a,b){if(b){let c=b.getObjectByName(a);return c?c:void 0}}function g(a){let b=[];return d&&"function"==typeof d.beforeRender?a.forEach(async a=>{let e=d.beforeRender(a);e?b.push(e):c&&c.skip(1),a.children&&0<a.children.length&&(a.children=g(a.children))}):b=a,b}function h(a,b){let c=new Map;for(let d of a){let a=b.get(d.modelId);if(!a)continue;let e=d.children||[],f=new Map;0<e.length&&(f=h(e,b));let g=[];for(let a of e){let b=f.get(a.modelId);b&&g.push(b)}let i=createDTModel(d,a.model,a.index,g);c.set(d.modelId,i),f.forEach(a=>{c.set(a.modelId,a)})}return c}function i(a,b){if(e)for(let c of a){let a=(c.name||"").toLocaleLowerCase(),d=b;for(let b in e)if(-1<a.indexOf(b)){d=e[b];break}c instanceof Mesh&&d&&toArray(c.material).forEach(a=>{a.color.set(d.color)}),c.children&&0<c.children.length&&i(c.children,d)}}function j(b,c,d){let e=g(b);d||(d=new Map);for(let g of e){let b=g.modelId,e=f(b,c);if(!e)e=new Object3D,initModelMatrix(e,g);else{e.updateWorldMatrix(!0,!1);let a=e.matrixWorld.clone();e.matrix.copy(a),e.matrix.decompose(e.position,e.quaternion,e.scale)}a.add(e);let h=createDTModel(g,e);d.set(b,h),g.children&&0<g.children.length&&j(g.children,e,d)}return d}return{renderModel:async function(d,e,j=[]){let k=new Map,l=g(d),{sizeMapping:m,skipMapping:n}=gather_model_count(l,j);for(let f in m){let d=m[f].length,{model:e,isInstance:g}=await createModel(b,f,d),h=m[f];if(!e){c&&c.completed(d);continue}if(g){for(let a=0;a<d;a++){let b=h[a],d=b.modelId;initModelMatrix(e,b,a),i([e]),k.set(d,{model:e,index:a,isInstance:!0}),c&&c.completed(1)}a.add(e)}else for(let b=0;b<d;b++){let d=h[b],f=d.modelId,g=e.clone(!0);initModelMatrix(g,d),i([e]),a.add(g),k.set(f,{model:g,index:void 0,isInstance:!1}),c&&c.completed(1)}}for(let b of n){let d=b.modelId,e=f(d,a);e||(e=new Object3D,initModelMatrix(e,b),a.add(e)),k.set(d,{model:e,index:void 0,isInstance:!1}),c&&c.skip(1)}return h(l,k)},buildModelMapping:j}}function gather_model_count(a,b,c={},d=[]){const e=(a,b)=>{const f=new Set(b.map(a=>a.toLowerCase()));for(let g of a)f.has(g.type.toLowerCase())||!g.url?d.push(g):(c[g.url]??=[]).push(g),g.children&&0<g.children.length&&e(g.children,[...b,ComponentType.room])};return e(a,b),{sizeMapping:c,skipMapping:d}}const createModel=async(a,b,c=1)=>{let d;try{let c=await a.loadModel(b),e=gltfToObject3D(c);d=e.clone}catch(a){return{model:null,isInstance:!1}}if(1==c)return{model:d,isInstance:!1};let e=mergeGeometries(d);if(!e)return{model:d,isInstance:!1};let f=new InstancedMesh(e.geometry,e.material,c);return f.instanceMatrix.needsUpdate=!0,{model:f,isInstance:!0}};function toVector3(a){let[b=0,c=0,d=0]=a;return new Vector3(b,c,d)}function initModelMatrix(a,b,c){if(b.props){let d=new Quaternion,e=new Matrix4,{position:f=[0,0,0],rotation:g=[0,0,0],scale:h=[1,1,1]}=b.props,i=convertUnityToThreeJSRotation(toVector3(g));d.setFromEuler(i),e.compose(toVector3(f),d,toVector3(h)),a instanceof InstancedMesh&&c!=null?a.setMatrixAt(c,e):a.applyMatrix4(e),a.updateMatrixWorld(!0)}return a}export function createProgress(a,b){const c=a=>{let b=a.length;return a.forEach(a=>{b+=c(a.children||[])}),b};let d=c([a]),e=0,f=!1;b||(b=new EventEmitter);return extend({get loaded(){return f},reset(a,b=0){d=a,e=b},completed(a=1){if(!f)if(e+=a,e>=d){let a=new DTEvent(RenderEvent.Loaded,{total:d,current:d});b.emit(a),f=!0}else{let a=new DTEvent(RenderEvent.Progress,{total:d,current:e});b.emit(a)}},skip(a){if(e+=a,e>=d){let a=new DTEvent(RenderEvent.Loaded,{total:d,current:d,component:void 0});b.emit(a),f=!0}}},b)}