@hology/core 0.0.267 → 0.0.269
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gameplay/actors/builtin/components/character/character-movement.d.ts +2 -0
- package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
- package/dist/gameplay/actors/camera/third-person-camera-component.d.ts +3 -0
- package/dist/gameplay/actors/camera/third-person-camera-component.js +1 -1
- package/dist/gameplay/services/physics/physics-system.d.ts +1 -0
- package/dist/gameplay/services/physics/physics-system.js +1 -1
- package/dist/gameplay/services/render.d.ts +2 -1
- package/dist/gameplay/services/render.js +1 -1
- package/dist/rendering/node-material-webgpu-resolver.js +1 -1
- package/dist/rendering/webgpu-experimental-backend.d.ts +2 -0
- package/dist/rendering/webgpu-experimental-backend.js +1 -1
- package/dist/rendering.d.ts +13 -0
- package/dist/rendering.js +1 -1
- package/dist/scene/asset-resource-loader.js +1 -1
- package/dist/scene/materializer.d.ts +2 -0
- package/dist/scene/materializer.js +1 -1
- package/dist/shader/builtin/standard-shader.d.ts +23 -0
- package/dist/shader/builtin/standard-shader.js +1 -1
- package/dist/shader-nodes/scene-sample.d.ts +3 -0
- package/dist/shader-nodes/scene-sample.js +1 -1
- package/dist/test/frame-audio-listener.test.d.ts +2 -0
- package/dist/test/frame-audio-listener.test.js +4 -0
- package/dist/test/frame-stats.test.d.ts +2 -0
- package/dist/test/frame-stats.test.js +4 -0
- package/dist/test/node-material-webgpu-resolver.test.js +1 -1
- package/dist/utils/three/frame-audio-listener.d.ts +19 -0
- package/dist/utils/three/frame-audio-listener.js +4 -0
- package/dist/utils/three/frame-stats.d.ts +22 -0
- package/dist/utils/three/frame-stats.js +4 -0
- package/package.json +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{parseAnimationClips as e}from"./animation-binary.js";import{AudioLoader as t,BufferGeometry as r,Euler as a,Group as i,LoadingManager as s,Mesh as n,Object3D as o,Texture as l,TextureLoader as c,Vector3 as h}from"three";import{MTLLoader as u,OBJLoader as m,LUTCubeLoader as d}from"three-stdlib";import{cloneMesh as p,mergeCompatibleSkinnedMeshes as f}from"../utils/mesh.js";import{pathJoin as g}from"../utils/files.js";import{Subject as y,firstValueFrom as w}from"rxjs";import{BoxCollisionShape as x,CapsuleCollisionShape as M,CollisionShapeSource as b,CylinderCollisionShape as A,SphereCollisionShape as L}from"./collision/collision-shape.js";import{importCollisionShapes as S,isCollisionMesh as _}from"./collision/collision-shape-import.js";import*as v from"three";import{iterateMaterials as T}from"../utils/materials.js";import{applyMaterial as R,getConvertedFbxToGlbAssignedMaterialTextureFlipY as C}from"./materializer.js";import{BufferGeometryUtils as B,DRACOLoader as F,EXRLoader as P,FBXLoader as I,GLTFLoader as k,KTX2Loader as K,TGALoader as D,UltraHDRLoader as j}from"three/examples/jsm/Addons.js";import{disposeScene as W}from"../utils/three/cleanup.js";import{resolveAnimationRigBasisRotation as U,setAnimationClipRigId as z}from"../gameplay/animation/retarget.js";(()=>{const e=new Uint8Array([255,0,0,255]),t=new v.DataTexture(e,1,1,v.RGBAFormat);t.needsUpdate=!0})();const G=new F;G.setDecoderConfig({type:"js"}),G.setDecoderPath("/assets/draco/");const $=["glb","gltf","fbx","obj"];export class AssetResourceLoader{onError(e){console.error(e)}constructor(){this.cache=new Map,this.textureCache=new Map,this.loadingManager=new s,this.glbLoader=new k(this.loadingManager).setDRACOLoader(G),this.fbxLoader=new I(this.loadingManager),this.objLoader=new m(this.loadingManager),this.textureLoader=new c(this.loadingManager),this.tgaLoader=new D(this.loadingManager),this.exrLoader=new P(this.loadingManager),this.cubeLoader=new d(this.loadingManager),this.hdrLoader=new j(this.loadingManager),this.ktx2Loader=new K(this.loadingManager),this.ktx2Ready=Promise.resolve(),this._textureLoader=new v.ImageBitmapLoader(this.loadingManager),this.audioLoader=new t(this.loadingManager),this.initialisedKtx2=!1,this._retrievedMeshes=[],this._retrievedTextures=[],this.makeReady=new y,this.ready=w(this.makeReady),this.directFileLoading=!1,this._texturePromises=new Map,this._arrayTexturesByFileKey=new Map,this._arrayTexturePromisesByFileKey=new Map,this._arrayTextureSettingsByFileKey=new Map,this._animationPromises=new Map,this._animationCache=new Map,this.asyncMeshResults=new Map,this.asyncAudioResults=new Map,this.clipsRequests=new Set,this.timeToLoadClips=0,this.collisionShapeCache=new Map,this.optimizedMeshes=new Set}setDirectFileLoading(e){this.directFileLoading=e}setImageBitmapDecoder(e){this.imageBitmapDecoder=e}decodeImageBitmap(e){return this.imageBitmapDecoder?.(e)}configureKtx2(){this.initialisedKtx2=!0;null!=getElectronArg("windowId")||import.meta.url.includes(".vite")||import.meta.url.includes("hology/packages/core")?this.ktx2Loader.setTranscoderPath("/assets/basis/"):this.ktx2Loader.setTranscoderPath(new URL("../assets/basis/",import.meta.url).href),this.ktx2Loader.setWorkerLimit(6)}initKtx2(e){this.configureKtx2(),this.ktx2Loader.detectSupport(e)}initKtx2ForWebGpu(){this.configureKtx2(),this.ktx2Ready=Promise.resolve(navigator.gpu?.requestAdapter({powerPreference:"high-performance"})??null).then(e=>{this.ktx2Loader.detectSupport({isWebGPURenderer:!0,hasFeature:t=>!0===e?.features.has(t)})})}setDataDir(e){this.basePath=g(e,"asset-resources"),this.makeReady.next(!0)}getUri(e){if(e.startsWith("asset://"))return e;const t=function(e){return E.get(e)}(e),r=getElectronArg("windowId");return null!=r||import.meta.url.includes(".vite")||import.meta.url.includes("hology/packages/core")?g(this.basePath,e)+(null!=r?`?windowId=${r}${null!=t?`&v=${t}`:""}`:""):new URL(g("..",this.basePath,e),import.meta.url).href}async getTexture(e){if(!e||!e.fileKey)return null;if("png"===e.fileFormat||e.fileFormat,this._texturePromises.has(e.id))return this._texturePromises.get(e.id);const t=this._getTexture(e).finally(()=>{this._texturePromises.delete(e.id)});return this._texturePromises.set(e.id,t),t}async _getTexture(e){if(null==e||null==e.fileKey)return null;if(await this.ready,e.fileKey.toLowerCase().endsWith(".ktx2")&&await this.ktx2Ready,this.textureCache.has(e.id)){const t=this.textureCache.get(e.id);if(null!=e.texture){const r=X(t,e);t.flipY!==r?(t.needsUpdate=!0,t.flipY=r):t.wrapS!==Y(e.texture?.wrapS)?(t.needsUpdate=!0,t.wrapS=Y(e.texture?.wrapS)):t.wrapT!==Y(e.texture?.wrapT)&&(t.wrapT=Y(e.texture?.wrapT),t.needsUpdate=!0)}const r=J(V(e));t.colorSpace!==r&&(t.colorSpace=r,t.needsUpdate=!0)}else try{const t=this._getTextureLoader(e.fileKey),r=null!=this.imageBitmapDecoder&&/\.(png|jpe?g)$/i.test(e.fileKey),a=r||this.directFileLoading&&t===this.textureLoader,i=this.getUri(e.fileKey),s=r?this.imageBitmapDecoder(i).catch(t=>(console.warn(`Worker image decode failed for ${e.name}; falling back to ImageBitmapLoader`,t),this._textureLoader.loadAsync(i))):(a?this._textureLoader:t).loadAsync(i);await s.then(t=>(a&&((t=new l(t)).needsUpdate=!0),t.texture3D?t=t.texture3D:(t.wrapS=Y(e.texture?.wrapS),t.wrapT=Y(e.texture?.wrapT),t.flipY=X(t,e),t.colorSpace=J(V(e))),this.textureCache.set(e.id,t),this._retrievedTextures.push(t),t))}catch(t){return console.warn("Failed to load texture "+e.name,t),null}const t=this.textureCache.get(e.id);return null!=t&&(t.userData.assetId=e.id),t}async getTextureArray(e){const t=e.texture?.textureArrayFileKey,r=e.texture?.textureArrayLayer;if(!t||null==r)return{texture:null,layerIndex:null};let a=this._arrayTexturesByFileKey.get(t);if(null==a){let e=this._arrayTexturePromisesByFileKey.get(t);null==e&&(e=this.loadTextureArray(t).finally(()=>{this._arrayTexturePromisesByFileKey.delete(t)}),this._arrayTexturePromisesByFileKey.set(t,e)),a=await e}return this.applyTextureArraySettings(t,a,e),{texture:a,layerIndex:r}}async loadTextureArray(e){const t=this._getTextureLoader(e);let r=await t.loadAsync(this.getUri(e));return r instanceof l||(r=r.texture3D),this._arrayTexturesByFileKey.set(e,r),this._retrievedTextures.push(r),r}applyTextureArraySettings(e,t,r){const a=function(e){return{wrapS:Y(e.texture?.wrapS),wrapT:Y(e.texture?.wrapT)}}(r),i=this._arrayTextureSettingsByFileKey.get(e);if(null!=i&&(n=a,(s=i).wrapS!==n.wrapS||s.wrapT!==n.wrapT))return void console.warn(`Texture array ${e} is used with incompatible texture settings. Texture arrays share wrapping; rebuild arrays so incompatible textures are grouped separately.`);var s,n;null==i&&this._arrayTextureSettingsByFileKey.set(e,a);let o=!1;t.wrapS!==a.wrapS&&(t.wrapS=a.wrapS,o=!0),t.wrapT!==a.wrapT&&(t.wrapT=a.wrapT,o=!0),!1!==t.flipY&&(t.flipY=!1,o=!0),o&&(t.needsUpdate=!0)}_getTextureLoader(e){return e?.toLowerCase().endsWith(".tga")?this.tgaLoader:e?.toLowerCase().endsWith(".ktx2")?this.ktx2Loader:e?.toLowerCase().endsWith(".exr")?this.exrLoader:e?.toLowerCase().endsWith(".cube")?this.cubeLoader:this.textureLoader}clearCache(e){this.clearCacheByAsset(e)}clearCacheByAsset(e){null!=e&&(this.clearCacheById(e.id),null!=e.fileKey&&(Array.from(this.cache.keys()).forEach(t=>{t.startsWith(e.fileKey)&&this.cache.delete(t)}),Array.from(this.asyncMeshResults.keys()).forEach(t=>{t.startsWith(e.fileKey)&&this.asyncMeshResults.delete(t)}),Array.from(this._animationCache.keys()).forEach(t=>{t.startsWith(e.fileKey+"|")&&this._animationCache.delete(t)}),Array.from(this._animationPromises.keys()).forEach(t=>{t.startsWith(e.fileKey+"|")&&this._animationPromises.delete(t)}),O(e.fileKey)),null!=e.materialLib&&O(e.materialLib))}clearCacheById(e){const t=Array.from(this.cache.values()).find(t=>t.scene.userData.assetId===e);t&&Array.from(this.cache.keys()).forEach(e=>{this.cache.get(e)===t&&this.cache.delete(e)}),this.textureCache.delete(e),this._texturePromises.delete(e),Array.from(this.collisionShapeCache.keys()).forEach(t=>{t.startsWith(e+"|")&&this.collisionShapeCache.delete(t)})}async getMesh(e,t){if(await this.ready,null==e)return console.error("No asset was provided"),{scene:new i,animations:[]};if(!$.includes(e.fileFormat?.toLowerCase()))return console.error("Unsupported mesh file format "+e.fileFormat,e),{scene:new i,animations:[]};const r=e.fileKey+(!0===t?.mergeGeomtries?"1":"0");if(!this.cache.has(r))try{this.asyncMeshResults.has(r)||this.asyncMeshResults.set(r,this.loadMesh(e).finally(()=>this.asyncMeshResults.delete(r))),this.cache.set(r,await this.asyncMeshResults.get(r))}catch(e){return this.onError(e),{scene:new i,animations:[]}}const a=this.cache.get(r),s=a.scene,o=a.animations;if(!1===t?.clone)return{scene:s,animations:o};const l=this.computeCollisionShapes(e,s),c=(e.mesh?.secondaryMotion?.regions.length??0)>0;!0!==t?.mergeGeomtries||c||this.optimizeDrawGroups(s);const h=p(s);h.traverse(e=>{e instanceof n&&e.material instanceof Array&&(e.material=e.material.slice())});const u=new AssetMeshInstance;u.add(h),u.collisionShapes=l,u.animations=o,u.userData.assetId=e.id,u.userData.secondaryMotion=e.mesh?.secondaryMotion,function(e){const t=e.children.length>0?e.children:[e];for(let e=0;e<t.length;e++)r(t[e],"",e);function r(e,t,a){const i=e instanceof n?"Mesh":"Group",s=`${t}/${e.name?.trim()?.length>0?e.name:i}[${a}]`;e.userData.secondaryMotionMeshPath=s;for(let t=0;t<e.children.length;t++)r(e.children[t],s,t)}}(u);const m=e.mesh?.animationRigId??e.anim?.rigId;if(u.userData.animationRigId=m,h.userData.animationRigId=m,null!=m&&null!=this.animationRigProvider){const e=await this.animationRigProvider(m);u.userData.animationRig=e,h.userData.animationRig=e,null!=e&&!1!==t?.applyRigBasis&&u.quaternion.copy(U(e))}const d=e.receiveShadow??!1,f=e.castShadow??!1;if(h.traverse(e=>{e.castShadow=f,e.receiveShadow=d}),!0===t?.rescale&&"number"==typeof e.mesh?.rescale&&u.scale.setScalar(e.mesh?.rescale),!0===t?.applyMaterials){const r=t?.materialResolver??this.materialProvider;if(null!=r)for(const t of e.materialAssignments??[])await R(u,t,r,void 0,{textureFlipY:C(e)});else console.warn(`applyMaterials was set to true for ${e.name} but no material resolver was provided.`)}return{scene:u,animations:o}}async getAudio(e){await this.ready;const t=e.fileKey;if(this.asyncAudioResults.has(t))return await this.asyncAudioResults.get(t);const r=this.audioLoader.loadAsync(this.getUri(e.fileKey)).finally(()=>{setTimeout(()=>{this.asyncAudioResults.delete(t)},1e3)});return this.asyncAudioResults.set(t,r),r}async getAnimationClip(t){if(null==t||null==t.fileKey||null==t.anim?.clip)return console.warn("Asset or animation clip name is not defined",t),null;const r=t.anim.clip,a=t.fileKey,i=a+"|"+r;if(this._animationCache.has(i))return this._animationCache.get(i);if(this._animationPromises.has(i))return this._animationPromises.get(i);this.clipsRequests.add(t.id);const s=(async()=>{try{const s=performance.now(),n=("anim"===t.fileFormat?e(await new v.FileLoader(this.loadingManager).setResponseType("arraybuffer").loadAsync(this.getUri(a))):(await this.getMesh(t,{mergeGeomtries:!1,clone:!1})).animations).find(e=>e.name===r);null!=n&&(z(n,t.anim?.rigId),this._animationCache.set(i,n));const o=performance.now()-s;return this.timeToLoadClips+=o,console.log(`Fetch clip duration: ${this.timeToLoadClips}. Average: ${this.timeToLoadClips/this.clipsRequests.size}`),n}catch(e){return this.onError(e),null}finally{setTimeout(()=>this._animationPromises.delete(i),1e3)}})();return this._animationPromises.set(i,s),s}computeCollisionShapes(e,t){if(!0!==e.collisionDetection)return[];const r=e.id+"|"+(e.mesh?.collisions?.shapeType??"")+"|"+(e.mesh?.rescale??1)+"|"+JSON.stringify(e.mesh?.colliders??[]);if(!this.collisionShapeCache.has(r)){const a=e.mesh?.colliders??[],i=a.length>0?createAuthoredCollisionShapes(a,e.mesh?.rescale):S(t,e);this.collisionShapeCache.set(r,i)}return this.collisionShapeCache.get(r)}async loadMesh(e){return await this.ready,await this.loadByAsset(e).then(t=>(t.scene.traverse(t=>{if(t instanceof n&&t.geometry instanceof r){const r=removeInvalidFaces(t.geometry);r>0&&console.warn(`Removed ${r} invalid face${1===r?"":"s"} from ${e.name}/${t.name||"unnamed mesh"}`)}t instanceof n&&t.material&&function(e){let t=e.material;if(null==t)return;Array.isArray(t)?e.material=t.map(ee):e.material=ee(t)}(t),_(t)?t.visible=!1:t instanceof n&&t.geometry instanceof r&&(t.geometry.hasAttribute("normal")||t.geometry.computeVertexNormals())}),this._retrievedMeshes.push(t.scene),t.scene=function(e,t){let r=!1;if(t.traverseVisible(e=>{Q.test(e.name)&&(r=!0)}),!r)return t;const a=[],i=[t];for(;i.length>0;){const e=i.shift(),t=e.name.match(Q);if(null!=t){const r=parseInt(t[1]);if(0!==r){console.warn(`Skipping LOD level ${r} for now as LOD is not fully supported`),a.push(e);continue}}i.push(...e.children)}for(const e of a)e.removeFromParent();return t}(0,t.scene),function(e){const t=H;H.length=0,e.traverse(e=>{(e instanceof v.Camera||e instanceof v.Light)&&t.push(e)}),t.forEach(e=>e.removeFromParent())}(t.scene),t.scene.userData.assetId=e.id,t))}optimizeDrawGroups(e){if(this.optimizedMeshes.has(e.uuid))return;this.optimizedMeshes.add(e.uuid),e.updateWorldMatrix(!0,!0);const t=[];e.traverse(e=>{e instanceof n&&!(e instanceof v.SkinnedMesh)&&e.geometry instanceof r&&Array.isArray(e.material)&&!_(e)&&t.push({mesh:e,geometry:e.geometry,materialArray:e.material,parent:e.parent})});for(const{mesh:r,geometry:a,materialArray:i,parent:s}of t){r.removeFromParent();for(let t=0;t<i.length;t++){const o=i[t],l=a.groups.filter(e=>e.materialIndex===t);if(0===l.length)continue;const c=this.extractGeometryGroups(a,l);c.groups.length>1&&B.mergeGroups(c);const h=new n(c,o);h.copy(r,!1),h.geometry=c,h.material=o,s?s.add(h):e.add(h)}a.dispose()}f(e,_);let a=!0,i=0,s=0;if(e.traverse(e=>{if(e instanceof n&&e.geometry instanceof r&&!_(e)){i++;const t=Object.keys(e.geometry.attributes).length;t!==s&&0!==s&&(a=!1),s=t}else(e instanceof v.SkinnedMesh||e instanceof v.Bone)&&(a=!1)}),i>1&&a){const t=new Map;e.traverse(a=>{if(a instanceof n&&a.geometry instanceof r&&!_(a)){if(Array.isArray(a.material))return;a.updateWorldMatrix(!0,!0);const r=a.material?.uuid||"default";t.has(r)||t.set(r,{material:a.material,geometries:[],objects:[]});const i=t.get(r),s=e.matrixWorld.clone().invert().multiply(a.matrixWorld);i.geometries.push(a.geometry.clone().applyMatrix4(s)),i.objects.push(a)}});for(const e of t.values())for(const t of e.objects)t.removeFromParent();for(const r of t.values()){let t,a=r.geometries;if(a.some(e=>null!=e.index)&&(a=a.map(e=>{if(null!=e.index)return e;const t=e.getAttribute("position");if(null==t)return e;const r=t.count>65535?new Uint32Array(t.count):new Uint16Array(t.count);for(let e=0;e<r.length;e++)r[e]=e;return e.setIndex(new v.BufferAttribute(r,1)),e})),t=1===r.geometries.length?a[0]:N(a)?B.mergeGeometries(a,!0):null,null!=t)t.groups.length>1&&B.mergeGroups(t),e.add(new n(t,r.material));else for(const t of a)e.add(new n(t,r.material))}}e.traverse(e=>{if(e instanceof n&&e.geometry instanceof r&&!_(e)){const t=e.geometry;!Array.isArray(e.material)&&t.groups.length>1&&B.mergeGroups(t)}})}extractGeometryGroups(e,t){const a=e.index;if(!a){const a=new r;let i=0;for(const e of t)i+=e.count;if(0===i)return a;for(const r in e.attributes){const s=e.attributes[r],n=s.itemSize,o=s.array,l=new(0,o.constructor)(i*n);let c=0;for(const e of t){const t=e.start*n,r=e.count*n;for(let e=0;e<r;e++)l[c+e]=o[t+e];c+=r}const h=new v.BufferAttribute(l,n,s.normalized);a.setAttribute(r,h)}return a.groups=[{start:0,count:i,materialIndex:0}],a}const i=[];for(const e of t)for(let t=0;t<e.count;t++){const r=a.getX(e.start+t);i.push(r)}if(0===i.length)return new r;const s=new Map,n=[];let o=0;for(const e of i)s.has(e)||(s.set(e,o),o++),n.push(s.get(e));if(0===n.length||0===s.size)return new r;const l=new r;for(const t in e.attributes){const r=e.attributes[t],a=r.itemSize,i=r.array,n=new(0,i.constructor)(s.size*a);for(const[e,t]of s)for(let r=0;r<a;r++)n[t*a+r]=i[e*a+r];const o=new v.BufferAttribute(n,a,r.normalized);l.setAttribute(t,o)}const c=new((n.length>0?Math.max(...n):0)>65535?Uint32Array:Uint16Array)(n.length);for(let e=0;e<n.length;e++)c[e]=n[e];return l.setIndex(new v.BufferAttribute(c,1)),l.groups=[{start:0,count:n.length,materialIndex:0}],l}async loadByAsset(e){this.fbxLoader;const t=this.getUri(e.fileKey);switch(e.fileFormat){case"glb":case"gltf":return this.glbLoader.loadAsync(t).then(t=>{const r=!0===e.originalFileKey?.toLowerCase().endsWith(".fbx")&&1===t.scene.children.length?t.scene.children[0]:t.scene;return r!==t.scene&&t.scene.remove(r),{scene:r,animations:t.animations}});case"fbx":if(this.directFileLoading){const e=await fetch(t);if(!e.ok)throw new Error(`Failed to load FBX ${t}: ${e.status}`);const r=await e.arrayBuffer(),a=this.fbxLoader.parse(r,v.LoaderUtils.extractUrlBase(t));return{scene:a,animations:a.animations}}return this.fbxLoader.loadAsync(t).then(e=>({scene:e,animations:e.animations}));case"obj":if(null!=e.materialLib){const t=new u;t.materialOptions={normalizeRGB:!1};const r=await t.loadAsync(this.getUri(e.materialLib));this.objLoader.setMaterials(r)}return this.objLoader.loadAsync(t).then(e=>(q(e),e)).then(e=>({scene:e,animations:e.animations}))}}disposeAll(){this.cache.clear(),this._retrievedMeshes.forEach(e=>W(e)),this._retrievedTextures.forEach(e=>e.dispose()),this._retrievedMeshes.length=0,this._retrievedTextures.length=0}}const E=new Map;function O(e){E.set(e,(E.get(e)??0)+1)}function N(e){if(e.length<2)return!0;const t=e[0],r=Object.keys(t.attributes).sort(),a=Object.keys(t.morphAttributes).sort();return e.slice(1).every(e=>{if(null==e.index!=(null==t.index))return!1;if(e.morphTargetsRelative!==t.morphTargetsRelative)return!1;const i=Object.keys(e.attributes).sort(),s=Object.keys(e.morphAttributes).sort();if(i.join("|")!==r.join("|"))return!1;if(s.join("|")!==a.join("|"))return!1;for(const a of r){const r=t.getAttribute(a),i=e.getAttribute(a);if(r.itemSize!==i.itemSize||r.normalized!==i.normalized||r.array.constructor!==i.array.constructor)return!1}for(const r of a){const a=t.morphAttributes[r],i=e.morphAttributes[r];if(a.length!==i.length)return!1;for(let e=0;e<a.length;e++)if(a[e].itemSize!==i[e].itemSize||a[e].normalized!==i[e].normalized||a[e].array.constructor!==i[e].array.constructor)return!1}return!0})}function q(e){if(e instanceof n)for(const t of T(e.material))t instanceof v.MeshPhongMaterial&&!t.color.isLinear&&(t.color.isLinear=!0);e.children?.forEach(q)}export class AssetMeshInstance extends o{}export function createAuthoredCollisionShapes(e,t=1){const r=null!=t&&0!==t?1/t:1;return e.map(e=>{let t;switch(e.type){case"box":t=new x((new h).fromArray(e.size).multiplyScalar(r));break;case"sphere":t=new L(e.radius*r);break;case"capsule":t=new M(e.length*r,e.radius*r);break;case"cylinder":t=new A(e.radius*r,e.radius*r,e.height*r,16,new a)}return t.source=b.custom,t.offset.fromArray(e.position??[0,0,0]).multiplyScalar(r),t.rotation.fromArray(e.rotation??[0,0,0,"XYZ"]),t})}export function getElectronArg(e){const t=`--${e}=`,r=window.process?.argv.find(e=>e.startsWith(t));return r?.substring(t.length)}function Y(e){switch(e){case"clamp":return v.ClampToEdgeWrapping;case"repeat":return v.RepeatWrapping;case"mirror":return v.MirroredRepeatWrapping}return v.RepeatWrapping}function X(e,t){const r=e;return!0!==r.isCompressedTexture&&!0!==r.isCompressedArrayTexture&&(t.texture?.flipY??!0)}function V(e){return e.texture?.colorSpace??function(e){if("exr"===e.fileFormat||e.fileKey?.toLowerCase().endsWith(".exr"))return"linear";const t=`${e.name} ${e.fileKey??""}`.toLowerCase();if(Z.some(e=>t.includes(e)))return"linear";return"srgb"}(e)}function J(e){return"linear"===e?v.LinearSRGBColorSpace:v.SRGBColorSpace}const Z=["_normal","_nrm","_nor","_norm","_roughness","_rough","_metalness","_metallic","_metal","_ao","_ambient","_occlusion","_mask","_height","_disp","_displacement","_bump","_opacity"];new v.Matrix4;const H=[];const Q=/_LOD(\d+)/;export function removeInvalidFaces(e){const t=e.getAttribute("position");if(null==t||t.itemSize<3)return 0;const r=e.index,a=r?.count??t.count,i=Math.floor(a/3),s=[],n=new Uint32Array(i+1),o=new h,l=new h,c=new h;let u=0;for(let e=0;e<i;e++){const a=3*e,i=r?.getX(a)??a,h=r?.getX(a+1)??a+1,m=r?.getX(a+2)??a+2;let d=Number.isInteger(i)&&Number.isInteger(h)&&Number.isInteger(m)&&i>=0&&h>=0&&m>=0&&i<t.count&&h<t.count&&m<t.count;if(d){o.fromBufferAttribute(t,i),l.fromBufferAttribute(t,h).sub(o),c.fromBufferAttribute(t,m).sub(o);const e=Math.max(l.lengthSq(),c.lengthSq(),l.distanceToSquared(c)),r=l.cross(c).lengthSq();d=Number.isFinite(e)&&Number.isFinite(r)&&e>0&&r>Number.EPSILON*e*e}d?s.push(i,h,m):u++,n[e+1]=s.length}if(0===u)return 0;if(e.setIndex(s),e.groups.length>0){const t=e.groups.slice();e.clearGroups();for(const r of t){const t=Math.max(0,Math.ceil(r.start/3)),a=Math.min(i,Math.floor((r.start+r.count)/3));if(a<=t)continue;const s=n[t],o=n[a]-s;o>0&&e.addGroup(s,o,r.materialIndex)}}const m=Math.max(0,Math.ceil(e.drawRange.start/3)),d=Number.isFinite(e.drawRange.count)?Math.min(i,Math.floor((e.drawRange.start+e.drawRange.count)/3)):i,p=Math.min(m,i),f=Math.min(i,Math.max(p,d)),g=n[p];return e.setDrawRange(g,n[f]-g),u}function ee(e){let t=e instanceof v.MeshPhysicalMaterial&&0===e.sheen&&0===e.anisotropy;if("MeshLambertMaterial"===e.type||"MeshPhongMaterial"===e.type||t){let t={};(e instanceof v.MeshLambertMaterial||e instanceof v.MeshPhongMaterial||e instanceof v.MeshPhysicalMaterial)&&(t.color=e.color,t.map=e.map,t.bumpMap=e.bumpMap,t.bumpScale=e.bumpScale,t.lightMap=e.lightMap,t.lightMapIntensity=e.lightMapIntensity,t.aoMap=e.aoMap,t.aoMapIntensity=e.aoMapIntensity,t.emissiveMap=e.emissiveMap,t.alphaMap=e.alphaMap,t.envMap=e.envMap,t.envMapRotation=e.envMapRotation,t.wireframe=e.wireframe,t.wireframeLinewidth=e.wireframeLinewidth,t.fog=e.fog,t.opacity=e.opacity,t.transparent=e.transparent,t.side=e.side,t.depthTest=e.depthTest,t.depthWrite=e.depthWrite,t.alphaTest=e.alphaTest,t.displacementMap=e.displacementMap,t.displacementScale=e.displacementScale,t.displacementBias=e.displacementBias);const r=new v.MeshStandardMaterial(t);return r.userData={...e.userData??{}},r.name=e.name,r}return e}/*
|
|
1
|
+
import{parseAnimationClips as e}from"./animation-binary.js";import{AudioLoader as t,BufferGeometry as r,Euler as a,Group as i,LoadingManager as s,Mesh as n,Object3D as o,Texture as l,TextureLoader as c,Vector3 as h}from"three";import{MTLLoader as u,OBJLoader as m,LUTCubeLoader as d}from"three-stdlib";import{cloneMesh as p,mergeCompatibleSkinnedMeshes as f}from"../utils/mesh.js";import{pathJoin as g}from"../utils/files.js";import{Subject as y,firstValueFrom as w}from"rxjs";import{BoxCollisionShape as x,CapsuleCollisionShape as M,CollisionShapeSource as b,CylinderCollisionShape as A,SphereCollisionShape as L}from"./collision/collision-shape.js";import{importCollisionShapes as S,isCollisionMesh as _}from"./collision/collision-shape-import.js";import*as v from"three";import{iterateMaterials as T}from"../utils/materials.js";import{applyMaterial as R,getConvertedFbxToGlbAssignedMaterialTextureFlipY as C}from"./materializer.js";import{BufferGeometryUtils as B,DRACOLoader as F,EXRLoader as P,FBXLoader as I,GLTFLoader as k,KTX2Loader as K,TGALoader as D,UltraHDRLoader as j}from"three/examples/jsm/Addons.js";import{disposeScene as W}from"../utils/three/cleanup.js";import{resolveAnimationRigBasisRotation as U,setAnimationClipRigId as z}from"../gameplay/animation/retarget.js";(()=>{const e=new Uint8Array([255,0,0,255]),t=new v.DataTexture(e,1,1,v.RGBAFormat);t.needsUpdate=!0})();const G=new F;G.setDecoderConfig({type:"js"}),G.setDecoderPath("/assets/draco/");const $=["glb","gltf","fbx","obj"];export class AssetResourceLoader{onError(e){console.error(e)}constructor(){this.cache=new Map,this.textureCache=new Map,this.loadingManager=new s,this.glbLoader=new k(this.loadingManager).setDRACOLoader(G),this.fbxLoader=new I(this.loadingManager),this.objLoader=new m(this.loadingManager),this.textureLoader=new c(this.loadingManager),this.tgaLoader=new D(this.loadingManager),this.exrLoader=new P(this.loadingManager),this.cubeLoader=new d(this.loadingManager),this.hdrLoader=new j(this.loadingManager),this.ktx2Loader=new K(this.loadingManager),this.ktx2Ready=Promise.resolve(),this._textureLoader=new v.ImageBitmapLoader(this.loadingManager),this.audioLoader=new t(this.loadingManager),this.initialisedKtx2=!1,this._retrievedMeshes=[],this._retrievedTextures=[],this.makeReady=new y,this.ready=w(this.makeReady),this.directFileLoading=!1,this._texturePromises=new Map,this._arrayTexturesByFileKey=new Map,this._arrayTexturePromisesByFileKey=new Map,this._arrayTextureSettingsByFileKey=new Map,this._animationPromises=new Map,this._animationCache=new Map,this.asyncMeshResults=new Map,this.asyncAudioResults=new Map,this.clipsRequests=new Set,this.timeToLoadClips=0,this.collisionShapeCache=new Map,this.optimizedMeshes=new Set}setDirectFileLoading(e){this.directFileLoading=e}setImageBitmapDecoder(e){this.imageBitmapDecoder=e}decodeImageBitmap(e){return this.imageBitmapDecoder?.(e)}configureKtx2(){this.initialisedKtx2=!0;null!=getElectronArg("windowId")||import.meta.url.includes(".vite")||import.meta.url.includes("hology/packages/core")?this.ktx2Loader.setTranscoderPath("/assets/basis/"):this.ktx2Loader.setTranscoderPath(new URL("../assets/basis/",import.meta.url).href),this.ktx2Loader.setWorkerLimit(6)}initKtx2(e){this.configureKtx2(),this.ktx2Loader.detectSupport(e)}initKtx2ForWebGpu(){this.configureKtx2(),this.ktx2Ready=Promise.resolve(navigator.gpu?.requestAdapter({powerPreference:"high-performance"})??null).then(e=>{this.ktx2Loader.detectSupport({isWebGPURenderer:!0,hasFeature:t=>!0===e?.features.has(t)})})}setDataDir(e){this.basePath=g(e,"asset-resources"),this.makeReady.next(!0)}getUri(e){if(e.startsWith("asset://"))return e;const t=function(e){return E.get(e)}(e),r=getElectronArg("windowId");return null!=r||import.meta.url.includes(".vite")||import.meta.url.includes("hology/packages/core")?g(this.basePath,e)+(null!=r?`?windowId=${r}${null!=t?`&v=${t}`:""}`:""):new URL(g("..",this.basePath,e),import.meta.url).href}async getTexture(e){if(!e||!e.fileKey)return null;if("png"===e.fileFormat||e.fileFormat,this._texturePromises.has(e.id))return this._texturePromises.get(e.id);const t=this._getTexture(e).finally(()=>{this._texturePromises.delete(e.id)});return this._texturePromises.set(e.id,t),t}async _getTexture(e){if(null==e||null==e.fileKey)return null;if(await this.ready,e.fileKey.toLowerCase().endsWith(".ktx2")&&await this.ktx2Ready,this.textureCache.has(e.id)){const t=this.textureCache.get(e.id);if(null!=e.texture){const r=X(t,e);t.flipY!==r?(t.needsUpdate=!0,t.flipY=r):t.wrapS!==Y(e.texture?.wrapS)?(t.needsUpdate=!0,t.wrapS=Y(e.texture?.wrapS)):t.wrapT!==Y(e.texture?.wrapT)&&(t.wrapT=Y(e.texture?.wrapT),t.needsUpdate=!0)}const r=J(V(e));t.colorSpace!==r&&(t.colorSpace=r,t.needsUpdate=!0)}else try{const t=this._getTextureLoader(e.fileKey),r=null!=this.imageBitmapDecoder&&/\.(png|jpe?g)$/i.test(e.fileKey),a=r||this.directFileLoading&&t===this.textureLoader,i=this.getUri(e.fileKey),s=r?this.imageBitmapDecoder(i).catch(t=>(console.warn(`Worker image decode failed for ${e.name}; falling back to ImageBitmapLoader`,t),this._textureLoader.loadAsync(i))):(a?this._textureLoader:t).loadAsync(i);await s.then(t=>(a&&((t=new l(t)).needsUpdate=!0),t.texture3D?t=t.texture3D:(t.wrapS=Y(e.texture?.wrapS),t.wrapT=Y(e.texture?.wrapT),t.flipY=X(t,e),t.colorSpace=J(V(e))),this.textureCache.set(e.id,t),this._retrievedTextures.push(t),t))}catch(t){return console.warn("Failed to load texture "+e.name,t),null}const t=this.textureCache.get(e.id);return null!=t&&(t.userData.assetId=e.id),t}async getTextureArray(e){const t=e.texture?.textureArrayFileKey,r=e.texture?.textureArrayLayer;if(!t||null==r)return{texture:null,layerIndex:null};let a=this._arrayTexturesByFileKey.get(t);if(null==a){let e=this._arrayTexturePromisesByFileKey.get(t);null==e&&(e=this.loadTextureArray(t).finally(()=>{this._arrayTexturePromisesByFileKey.delete(t)}),this._arrayTexturePromisesByFileKey.set(t,e)),a=await e}return this.applyTextureArraySettings(t,a,e),{texture:a,layerIndex:r}}async loadTextureArray(e){const t=this._getTextureLoader(e);let r=await t.loadAsync(this.getUri(e));return r instanceof l||(r=r.texture3D),this._arrayTexturesByFileKey.set(e,r),this._retrievedTextures.push(r),r}applyTextureArraySettings(e,t,r){const a=function(e){return{wrapS:Y(e.texture?.wrapS),wrapT:Y(e.texture?.wrapT)}}(r),i=this._arrayTextureSettingsByFileKey.get(e);if(null!=i&&(n=a,(s=i).wrapS!==n.wrapS||s.wrapT!==n.wrapT))return void console.warn(`Texture array ${e} is used with incompatible texture settings. Texture arrays share wrapping; rebuild arrays so incompatible textures are grouped separately.`);var s,n;null==i&&this._arrayTextureSettingsByFileKey.set(e,a);let o=!1;t.wrapS!==a.wrapS&&(t.wrapS=a.wrapS,o=!0),t.wrapT!==a.wrapT&&(t.wrapT=a.wrapT,o=!0),!1!==t.flipY&&(t.flipY=!1,o=!0),o&&(t.needsUpdate=!0)}_getTextureLoader(e){return e?.toLowerCase().endsWith(".tga")?this.tgaLoader:e?.toLowerCase().endsWith(".ktx2")?this.ktx2Loader:e?.toLowerCase().endsWith(".exr")?this.exrLoader:e?.toLowerCase().endsWith(".cube")?this.cubeLoader:this.textureLoader}clearCache(e){this.clearCacheByAsset(e)}clearCacheByAsset(e){null!=e&&(this.clearCacheById(e.id),null!=e.fileKey&&(Array.from(this.cache.keys()).forEach(t=>{t.startsWith(e.fileKey)&&this.cache.delete(t)}),Array.from(this.asyncMeshResults.keys()).forEach(t=>{t.startsWith(e.fileKey)&&this.asyncMeshResults.delete(t)}),Array.from(this._animationCache.keys()).forEach(t=>{t.startsWith(e.fileKey+"|")&&this._animationCache.delete(t)}),Array.from(this._animationPromises.keys()).forEach(t=>{t.startsWith(e.fileKey+"|")&&this._animationPromises.delete(t)}),O(e.fileKey)),null!=e.materialLib&&O(e.materialLib))}clearCacheById(e){const t=Array.from(this.cache.values()).find(t=>t.scene.userData.assetId===e);t&&Array.from(this.cache.keys()).forEach(e=>{this.cache.get(e)===t&&this.cache.delete(e)}),this.textureCache.delete(e),this._texturePromises.delete(e),Array.from(this.collisionShapeCache.keys()).forEach(t=>{t.startsWith(e+"|")&&this.collisionShapeCache.delete(t)})}async getMesh(e,t){if(await this.ready,null==e)return console.error("No asset was provided"),{scene:new i,animations:[]};if(!$.includes(e.fileFormat?.toLowerCase()))return console.error("Unsupported mesh file format "+e.fileFormat,e),{scene:new i,animations:[]};const r=e.fileKey+(!0===t?.mergeGeomtries?"1":"0");if(!this.cache.has(r))try{this.asyncMeshResults.has(r)||this.asyncMeshResults.set(r,this.loadMesh(e).finally(()=>this.asyncMeshResults.delete(r))),this.cache.set(r,await this.asyncMeshResults.get(r))}catch(e){return this.onError(e),{scene:new i,animations:[]}}const a=this.cache.get(r),s=a.scene,o=a.animations;if(!1===t?.clone)return{scene:s,animations:o};const l=this.computeCollisionShapes(e,s),c=(e.mesh?.secondaryMotion?.regions.length??0)>0;!0!==t?.mergeGeomtries||c||this.optimizeDrawGroups(s);const h=p(s);h.traverse(e=>{e instanceof n&&e.material instanceof Array&&(e.material=e.material.slice())});const u=new AssetMeshInstance;u.add(h),u.collisionShapes=l,u.animations=o,u.userData.assetId=e.id,u.userData.secondaryMotion=e.mesh?.secondaryMotion,function(e){const t=e.children.length>0?e.children:[e];for(let e=0;e<t.length;e++)r(t[e],"",e);function r(e,t,a){const i=e instanceof n?"Mesh":"Group",s=`${t}/${e.name?.trim()?.length>0?e.name:i}[${a}]`;e.userData.secondaryMotionMeshPath=s;for(let t=0;t<e.children.length;t++)r(e.children[t],s,t)}}(u);const m=e.mesh?.animationRigId??e.anim?.rigId;if(u.userData.animationRigId=m,h.userData.animationRigId=m,null!=m&&null!=this.animationRigProvider){const e=await this.animationRigProvider(m);u.userData.animationRig=e,h.userData.animationRig=e,null!=e&&!1!==t?.applyRigBasis&&u.quaternion.copy(U(e))}const d=e.receiveShadow??!1,f=e.castShadow??!1;if(h.traverse(e=>{e.castShadow=f,e.receiveShadow=d}),!0===t?.rescale&&"number"==typeof e.mesh?.rescale&&u.scale.setScalar(e.mesh?.rescale),!0===t?.applyMaterials){const r=t?.materialResolver??this.materialProvider;if(null!=r)for(const t of e.materialAssignments??[])await R(u,t,r,void 0,{textureFlipY:C(e)});else console.warn(`applyMaterials was set to true for ${e.name} but no material resolver was provided.`)}return{scene:u,animations:o}}async getAudio(e){await this.ready;const t=e.fileKey;if(this.asyncAudioResults.has(t))return await this.asyncAudioResults.get(t);const r=this.audioLoader.loadAsync(this.getUri(e.fileKey)).finally(()=>{setTimeout(()=>{this.asyncAudioResults.delete(t)},1e3)});return this.asyncAudioResults.set(t,r),r}async getAnimationClip(t){if(null==t||null==t.fileKey||null==t.anim?.clip)return console.warn("Asset or animation clip name is not defined",t),null;const r=t.anim.clip,a=t.fileKey,i=a+"|"+r;if(this._animationCache.has(i))return this._animationCache.get(i);if(this._animationPromises.has(i))return this._animationPromises.get(i);this.clipsRequests.add(t.id);const s=(async()=>{try{const s=("anim"===t.fileFormat?e(await new v.FileLoader(this.loadingManager).setResponseType("arraybuffer").loadAsync(this.getUri(a))):(await this.getMesh(t,{mergeGeomtries:!1,clone:!1})).animations).find(e=>e.name===r);return null!=s&&(z(s,t.anim?.rigId),this._animationCache.set(i,s)),s}catch(e){return this.onError(e),null}finally{setTimeout(()=>this._animationPromises.delete(i),1e3)}})();return this._animationPromises.set(i,s),s}computeCollisionShapes(e,t){if(!0!==e.collisionDetection)return[];const r=e.id+"|"+(e.mesh?.collisions?.shapeType??"")+"|"+(e.mesh?.rescale??1)+"|"+JSON.stringify(e.mesh?.colliders??[]);if(!this.collisionShapeCache.has(r)){const a=e.mesh?.colliders??[],i=a.length>0?createAuthoredCollisionShapes(a,e.mesh?.rescale):S(t,e);this.collisionShapeCache.set(r,i)}return this.collisionShapeCache.get(r)}async loadMesh(e){return await this.ready,await this.loadByAsset(e).then(t=>(t.scene.traverse(t=>{if(t instanceof n&&t.geometry instanceof r){const r=removeInvalidFaces(t.geometry);r>0&&console.warn(`Removed ${r} invalid face${1===r?"":"s"} from ${e.name}/${t.name||"unnamed mesh"}`)}t instanceof n&&t.material&&function(e){let t=e.material;if(null==t)return;Array.isArray(t)?e.material=t.map(ee):e.material=ee(t)}(t),_(t)?t.visible=!1:t instanceof n&&t.geometry instanceof r&&(t.geometry.hasAttribute("normal")||t.geometry.computeVertexNormals())}),this._retrievedMeshes.push(t.scene),t.scene=function(e,t){let r=!1;if(t.traverseVisible(e=>{Q.test(e.name)&&(r=!0)}),!r)return t;const a=[],i=[t];for(;i.length>0;){const e=i.shift(),t=e.name.match(Q);if(null!=t){const r=parseInt(t[1]);if(0!==r){console.warn(`Skipping LOD level ${r} for now as LOD is not fully supported`),a.push(e);continue}}i.push(...e.children)}for(const e of a)e.removeFromParent();return t}(0,t.scene),function(e){const t=H;H.length=0,e.traverse(e=>{(e instanceof v.Camera||e instanceof v.Light)&&t.push(e)}),t.forEach(e=>e.removeFromParent())}(t.scene),t.scene.userData.assetId=e.id,t))}optimizeDrawGroups(e){if(this.optimizedMeshes.has(e.uuid))return;this.optimizedMeshes.add(e.uuid),e.updateWorldMatrix(!0,!0);const t=[];e.traverse(e=>{e instanceof n&&!(e instanceof v.SkinnedMesh)&&e.geometry instanceof r&&Array.isArray(e.material)&&!_(e)&&t.push({mesh:e,geometry:e.geometry,materialArray:e.material,parent:e.parent})});for(const{mesh:r,geometry:a,materialArray:i,parent:s}of t){r.removeFromParent();for(let t=0;t<i.length;t++){const o=i[t],l=a.groups.filter(e=>e.materialIndex===t);if(0===l.length)continue;const c=this.extractGeometryGroups(a,l);c.groups.length>1&&B.mergeGroups(c);const h=new n(c,o);h.copy(r,!1),h.geometry=c,h.material=o,s?s.add(h):e.add(h)}a.dispose()}f(e,_);let a=!0,i=0,s=0;if(e.traverse(e=>{if(e instanceof n&&e.geometry instanceof r&&!_(e)){i++;const t=Object.keys(e.geometry.attributes).length;t!==s&&0!==s&&(a=!1),s=t}else(e instanceof v.SkinnedMesh||e instanceof v.Bone)&&(a=!1)}),i>1&&a){const t=new Map;e.traverse(a=>{if(a instanceof n&&a.geometry instanceof r&&!_(a)){if(Array.isArray(a.material))return;a.updateWorldMatrix(!0,!0);const r=a.material?.uuid||"default";t.has(r)||t.set(r,{material:a.material,geometries:[],objects:[]});const i=t.get(r),s=e.matrixWorld.clone().invert().multiply(a.matrixWorld);i.geometries.push(a.geometry.clone().applyMatrix4(s)),i.objects.push(a)}});for(const e of t.values())for(const t of e.objects)t.removeFromParent();for(const r of t.values()){let t,a=r.geometries;if(a.some(e=>null!=e.index)&&(a=a.map(e=>{if(null!=e.index)return e;const t=e.getAttribute("position");if(null==t)return e;const r=t.count>65535?new Uint32Array(t.count):new Uint16Array(t.count);for(let e=0;e<r.length;e++)r[e]=e;return e.setIndex(new v.BufferAttribute(r,1)),e})),t=1===r.geometries.length?a[0]:N(a)?B.mergeGeometries(a,!0):null,null!=t)t.groups.length>1&&B.mergeGroups(t),e.add(new n(t,r.material));else for(const t of a)e.add(new n(t,r.material))}}e.traverse(e=>{if(e instanceof n&&e.geometry instanceof r&&!_(e)){const t=e.geometry;!Array.isArray(e.material)&&t.groups.length>1&&B.mergeGroups(t)}})}extractGeometryGroups(e,t){const a=e.index;if(!a){const a=new r;let i=0;for(const e of t)i+=e.count;if(0===i)return a;for(const r in e.attributes){const s=e.attributes[r],n=s.itemSize,o=s.array,l=new(0,o.constructor)(i*n);let c=0;for(const e of t){const t=e.start*n,r=e.count*n;for(let e=0;e<r;e++)l[c+e]=o[t+e];c+=r}const h=new v.BufferAttribute(l,n,s.normalized);a.setAttribute(r,h)}return a.groups=[{start:0,count:i,materialIndex:0}],a}const i=[];for(const e of t)for(let t=0;t<e.count;t++){const r=a.getX(e.start+t);i.push(r)}if(0===i.length)return new r;const s=new Map,n=[];let o=0;for(const e of i)s.has(e)||(s.set(e,o),o++),n.push(s.get(e));if(0===n.length||0===s.size)return new r;const l=new r;for(const t in e.attributes){const r=e.attributes[t],a=r.itemSize,i=r.array,n=new(0,i.constructor)(s.size*a);for(const[e,t]of s)for(let r=0;r<a;r++)n[t*a+r]=i[e*a+r];const o=new v.BufferAttribute(n,a,r.normalized);l.setAttribute(t,o)}const c=new((n.length>0?Math.max(...n):0)>65535?Uint32Array:Uint16Array)(n.length);for(let e=0;e<n.length;e++)c[e]=n[e];return l.setIndex(new v.BufferAttribute(c,1)),l.groups=[{start:0,count:n.length,materialIndex:0}],l}async loadByAsset(e){this.fbxLoader;const t=this.getUri(e.fileKey);switch(e.fileFormat){case"glb":case"gltf":return this.glbLoader.loadAsync(t).then(t=>{const r=!0===e.originalFileKey?.toLowerCase().endsWith(".fbx")&&1===t.scene.children.length?t.scene.children[0]:t.scene;return r!==t.scene&&t.scene.remove(r),{scene:r,animations:t.animations}});case"fbx":if(this.directFileLoading){const e=await fetch(t);if(!e.ok)throw new Error(`Failed to load FBX ${t}: ${e.status}`);const r=await e.arrayBuffer(),a=this.fbxLoader.parse(r,v.LoaderUtils.extractUrlBase(t));return{scene:a,animations:a.animations}}return this.fbxLoader.loadAsync(t).then(e=>({scene:e,animations:e.animations}));case"obj":if(null!=e.materialLib){const t=new u;t.materialOptions={normalizeRGB:!1};const r=await t.loadAsync(this.getUri(e.materialLib));this.objLoader.setMaterials(r)}return this.objLoader.loadAsync(t).then(e=>(q(e),e)).then(e=>({scene:e,animations:e.animations}))}}disposeAll(){this.cache.clear(),this._retrievedMeshes.forEach(e=>W(e)),this._retrievedTextures.forEach(e=>e.dispose()),this._retrievedMeshes.length=0,this._retrievedTextures.length=0}}const E=new Map;function O(e){E.set(e,(E.get(e)??0)+1)}function N(e){if(e.length<2)return!0;const t=e[0],r=Object.keys(t.attributes).sort(),a=Object.keys(t.morphAttributes).sort();return e.slice(1).every(e=>{if(null==e.index!=(null==t.index))return!1;if(e.morphTargetsRelative!==t.morphTargetsRelative)return!1;const i=Object.keys(e.attributes).sort(),s=Object.keys(e.morphAttributes).sort();if(i.join("|")!==r.join("|"))return!1;if(s.join("|")!==a.join("|"))return!1;for(const a of r){const r=t.getAttribute(a),i=e.getAttribute(a);if(r.itemSize!==i.itemSize||r.normalized!==i.normalized||r.array.constructor!==i.array.constructor)return!1}for(const r of a){const a=t.morphAttributes[r],i=e.morphAttributes[r];if(a.length!==i.length)return!1;for(let e=0;e<a.length;e++)if(a[e].itemSize!==i[e].itemSize||a[e].normalized!==i[e].normalized||a[e].array.constructor!==i[e].array.constructor)return!1}return!0})}function q(e){if(e instanceof n)for(const t of T(e.material))t instanceof v.MeshPhongMaterial&&!t.color.isLinear&&(t.color.isLinear=!0);e.children?.forEach(q)}export class AssetMeshInstance extends o{}export function createAuthoredCollisionShapes(e,t=1){const r=null!=t&&0!==t?1/t:1;return e.map(e=>{let t;switch(e.type){case"box":t=new x((new h).fromArray(e.size).multiplyScalar(r));break;case"sphere":t=new L(e.radius*r);break;case"capsule":t=new M(e.length*r,e.radius*r);break;case"cylinder":t=new A(e.radius*r,e.radius*r,e.height*r,16,new a)}return t.source=b.custom,t.offset.fromArray(e.position??[0,0,0]).multiplyScalar(r),t.rotation.fromArray(e.rotation??[0,0,0,"XYZ"]),t})}export function getElectronArg(e){const t=`--${e}=`,r=window.process?.argv.find(e=>e.startsWith(t));return r?.substring(t.length)}function Y(e){switch(e){case"clamp":return v.ClampToEdgeWrapping;case"repeat":return v.RepeatWrapping;case"mirror":return v.MirroredRepeatWrapping}return v.RepeatWrapping}function X(e,t){const r=e;return!0!==r.isCompressedTexture&&!0!==r.isCompressedArrayTexture&&(t.texture?.flipY??!0)}function V(e){return e.texture?.colorSpace??function(e){if("exr"===e.fileFormat||e.fileKey?.toLowerCase().endsWith(".exr"))return"linear";const t=`${e.name} ${e.fileKey??""}`.toLowerCase();if(Z.some(e=>t.includes(e)))return"linear";return"srgb"}(e)}function J(e){return"linear"===e?v.LinearSRGBColorSpace:v.SRGBColorSpace}const Z=["_normal","_nrm","_nor","_norm","_roughness","_rough","_metalness","_metallic","_metal","_ao","_ambient","_occlusion","_mask","_height","_disp","_displacement","_bump","_opacity"];new v.Matrix4;const H=[];const Q=/_LOD(\d+)/;export function removeInvalidFaces(e){const t=e.getAttribute("position");if(null==t||t.itemSize<3)return 0;const r=e.index,a=r?.count??t.count,i=Math.floor(a/3),s=[],n=new Uint32Array(i+1),o=new h,l=new h,c=new h;let u=0;for(let e=0;e<i;e++){const a=3*e,i=r?.getX(a)??a,h=r?.getX(a+1)??a+1,m=r?.getX(a+2)??a+2;let d=Number.isInteger(i)&&Number.isInteger(h)&&Number.isInteger(m)&&i>=0&&h>=0&&m>=0&&i<t.count&&h<t.count&&m<t.count;if(d){o.fromBufferAttribute(t,i),l.fromBufferAttribute(t,h).sub(o),c.fromBufferAttribute(t,m).sub(o);const e=Math.max(l.lengthSq(),c.lengthSq(),l.distanceToSquared(c)),r=l.cross(c).lengthSq();d=Number.isFinite(e)&&Number.isFinite(r)&&e>0&&r>Number.EPSILON*e*e}d?s.push(i,h,m):u++,n[e+1]=s.length}if(0===u)return 0;if(e.setIndex(s),e.groups.length>0){const t=e.groups.slice();e.clearGroups();for(const r of t){const t=Math.max(0,Math.ceil(r.start/3)),a=Math.min(i,Math.floor((r.start+r.count)/3));if(a<=t)continue;const s=n[t],o=n[a]-s;o>0&&e.addGroup(s,o,r.materialIndex)}}const m=Math.max(0,Math.ceil(e.drawRange.start/3)),d=Number.isFinite(e.drawRange.count)?Math.min(i,Math.floor((e.drawRange.start+e.drawRange.count)/3)):i,p=Math.min(m,i),f=Math.min(i,Math.max(p,d)),g=n[p];return e.setDrawRange(g,n[f]-g),u}function ee(e){let t=e instanceof v.MeshPhysicalMaterial&&0===e.sheen&&0===e.anisotropy;if("MeshLambertMaterial"===e.type||"MeshPhongMaterial"===e.type||t){let t={};(e instanceof v.MeshLambertMaterial||e instanceof v.MeshPhongMaterial||e instanceof v.MeshPhysicalMaterial)&&(t.color=e.color,t.map=e.map,t.bumpMap=e.bumpMap,t.bumpScale=e.bumpScale,t.lightMap=e.lightMap,t.lightMapIntensity=e.lightMapIntensity,t.aoMap=e.aoMap,t.aoMapIntensity=e.aoMapIntensity,t.emissiveMap=e.emissiveMap,t.alphaMap=e.alphaMap,t.envMap=e.envMap,t.envMapRotation=e.envMapRotation,t.wireframe=e.wireframe,t.wireframeLinewidth=e.wireframeLinewidth,t.fog=e.fog,t.opacity=e.opacity,t.transparent=e.transparent,t.side=e.side,t.depthTest=e.depthTest,t.depthWrite=e.depthWrite,t.alphaTest=e.alphaTest,t.displacementMap=e.displacementMap,t.displacementScale=e.displacementScale,t.displacementBias=e.displacementBias);const r=new v.MeshStandardMaterial(t);return r.userData={...e.userData??{}},r.name=e.name,r}return e}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -108,6 +108,8 @@ export type SceneObject = {
|
|
|
108
108
|
*/
|
|
109
109
|
collisionDetection?: boolean;
|
|
110
110
|
hidden?: boolean;
|
|
111
|
+
/** Automatic WebGPU detail policy, inherited by child objects. */
|
|
112
|
+
detailCulling?: import("@hology/webgpu-renderer").DetailCullingSettings;
|
|
111
113
|
renderOrder?: number;
|
|
112
114
|
/**
|
|
113
115
|
* Materials assigned to this object. Only relevant for object type asset_mesh.
|