import * as THREE from 'three'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"; import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js'; import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"; import { TGALoader } from 'three/examples/jsm/loaders/TGALoader.js'; import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js'; import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js"; import { unzipSync, strFromU8 } from 'three/examples/jsm/libs/fflate.module.js'; import { AddObjectCommand } from '../commands/AddObjectCommand'; import { SetSceneCommand } from '../commands/Commands'; import {useDispatchSignal} from "@/hooks"; import { ObjectLoader } from './ObjectLoader'; import App from "@/core/app/App"; import MaterialCreator = MTLLoader.MaterialCreator; const LoaderUtils = { createFilesMap: function (files: FileList | File[]) { const map = {}; for ( let i = 0; i < files.length; i ++ ) { const file = files[ i ]; map[ file.name ] = file; } return map; }, getFilesFromItemList: function ( items: DataTransferItem[], onDone: (files: File[], filesMap) => void ) { // TOFIX: setURLModifier() breaks when the file being loaded is not in root let itemsCount = 0; let itemsTotal = 0; const files: File[] = []; const filesMap = {}; function onEntryHandled() { itemsCount ++; if ( itemsCount === itemsTotal ) { onDone( files, filesMap ); } } function handleEntry( entry ) { if ( entry.isDirectory ) { const reader = entry.createReader(); reader.readEntries( function ( entries ) { for ( let i = 0; i < entries.length; i ++ ) { handleEntry( entries[ i ] ); } onEntryHandled(); } ); } else if ( entry.isFile ) { entry.file( function ( file ) { files.push( file ); filesMap[ entry.fullPath.slice( 1 ) ] = file; onEntryHandled(); } ); } itemsTotal ++; } for ( let i = 0; i < items.length; i ++ ) { const item = items[ i ]; if ( item.kind === 'file' ) { handleEntry( item.webkitGetAsEntry() ); } } } }; class Loader { protected texturePath:string = ''; protected _objectLoader:ObjectLoader | null = null; protected _dracoLoader:DRACOLoader | null = null; protected _ktx2Loader:KTX2Loader | null = null; public _ifcLoader:any = null; protected rgbeLoader:RGBELoader | null = null; protected tgaLoader:TGALoader | null = null; protected _exrLoader:EXRLoader | null = null; protected textureLoader:THREE.TextureLoader | null = null; constructor(){} get objectLoader():ObjectLoader{ if(!this._objectLoader){ this._objectLoader = new ObjectLoader(); } return this._objectLoader; } set objectLoader(value:ObjectLoader | null){ this._objectLoader = value; } get dracoLoader():DRACOLoader{ if(!this._dracoLoader){ this._dracoLoader = new DRACOLoader(); this._dracoLoader.setDecoderPath(new URL(import.meta.env.BASE_URL + 'libs/draco/gltf', import.meta.url).href + "/"); } return this._dracoLoader; } set dracoLoader(value:DRACOLoader | null) { this._dracoLoader = value; } get ktx2Loader():KTX2Loader{ if(!this._ktx2Loader){ this._ktx2Loader = new KTX2Loader(); this._ktx2Loader.setTranscoderPath(new URL(import.meta.env.BASE_URL + 'libs/basis', import.meta.url).href + "/"); useDispatchSignal("rendererDetectKTX2Support",this._ktx2Loader); } return this._ktx2Loader; } set ktx2Loader(value:KTX2Loader | null) { this._ktx2Loader = value; } get exrLoader():EXRLoader{ if(!this._exrLoader){ this._exrLoader = new EXRLoader(); } return this._exrLoader; } set exrLoader(value:EXRLoader | null) { this._exrLoader = value; } loadItemList(items) { LoaderUtils.getFilesFromItemList( items, ( files, filesMap )=>{ this.loadFiles( files, filesMap); } ); } loadFiles(files, filesMap): Promise { return new Promise((resolve, reject) => { const promises: Promise[] = []; if (files.length > 0) { filesMap = filesMap || LoaderUtils.createFilesMap(files); const manager = new THREE.LoadingManager(); manager.setURLModifier(function (url) { url = url.replace(/^(\.?\/)/, ''); // remove './' const file = filesMap[url]; if (file) { return URL.createObjectURL(file); } return url; }); manager.addHandler(/\.tga$/i, new TGALoader()); manager.addHandler(/\.mtl$/i, new MTLLoader()); /** 2023/02/03 二三:判断是否存在mtl文件,存在则提前解析 **/ // @ts-ignore const mtlIndex = Object.values(files).findIndex((item: File) => item.name?.split('.').pop().toLowerCase() === "mtl"); let mtlMaterials: MaterialCreator | null = null; if (mtlIndex !== -1) { const mtlLoader = new MTLLoader(); const reader = new FileReader(); reader.addEventListener('load', (event) => { const contents = event.target?.result as string; const materials = mtlLoader.parse(contents, ""); materials.preload(); mtlMaterials = materials; for (let i = 0; i < files.length; i++) { promises.push(this.loadFile(files[i], manager, mtlMaterials)); } Promise.all(promises).then((models) => { resolve(models); }).catch(error => { reject(error); }); }, false); reader.readAsText(files[mtlIndex]); } else { for (let i = 0; i < files.length; i++) { promises.push(this.loadFile(files[i], manager)); } Promise.all(promises).then((models) => { resolve(models); }).catch(error => { reject(error); }); } }else{ reject("No files to load."); } }) } loadFile(file, manager:THREE.LoadingManager = new THREE.LoadingManager(),mtlMaterials:MaterialCreator | null = null,addToScene = true):Promise { return new Promise((resolve, reject) => { const filename = file.name; const extension = filename.split('.').pop().toLowerCase(); const reader = new FileReader(); // reader.addEventListener( 'progress', function ( event ) { // const size = '(' + Math.floor( event.total / 1000 ).format() + ' KB)'; // const progress = Math.floor( ( event.loaded / event.total ) * 100 ) + '%'; // console.log( 'Loading', filename, size, progress ); // } ); switch (extension) { case '3dm': reader.addEventListener('load', async function (event) { const contents = event.target?.result; const { Rhino3dmLoader } = await import('three/examples/jsm/loaders/3DMLoader.js'); const loader = new Rhino3dmLoader(); loader.setLibraryPath('../examples/jsm/libs/rhino3dm/'); loader.parse(contents as ArrayBufferLike, function (object) { addToScene && App.execute(new AddObjectCommand(object)); resolve(object); }); }, false); reader.readAsArrayBuffer(file); break; case '3ds': reader.addEventListener('load', async function (event) { const { TDSLoader } = await import('three/examples/jsm/loaders/TDSLoader.js'); const loader = new TDSLoader(); //@ts-ignore const object = loader.parse(event.target.result); addToScene && App.execute(new AddObjectCommand(object)); resolve(object); }, false); reader.readAsArrayBuffer(file); break; case '3mf': reader.addEventListener('load', async function (event) { const { ThreeMFLoader } = await import('three/examples/jsm/loaders/3MFLoader.js'); const loader = new ThreeMFLoader(); const object = loader.parse(event.target?.result as ArrayBuffer); addToScene && App.execute(new AddObjectCommand(object)); resolve(object); }, false); reader.readAsArrayBuffer(file); break; case 'amf': reader.addEventListener('load', async function (event) { const { AMFLoader } = await import('three/examples/jsm/loaders/AMFLoader.js'); const loader = new AMFLoader(); const amfobject = loader.parse(event.target?.result as ArrayBuffer); addToScene && App.execute(new AddObjectCommand(amfobject)); resolve(amfobject); }, false); reader.readAsArrayBuffer(file); break; case 'dae': reader.addEventListener('load', async function (event) { const contents = event.target?.result as string; const { ColladaLoader } = await import('three/examples/jsm/loaders/ColladaLoader.js'); const loader = new ColladaLoader(manager); //@ts-ignore const collada = loader.parse(contents); collada.scene.name = filename; addToScene && App.execute(new AddObjectCommand(collada.scene)); resolve(collada.scene); }, false); reader.readAsText(file); break; case 'drc': reader.addEventListener('load', async (event) => { const contents = event.target?.result as ArrayBuffer; // this.dracoLoader.setDecoderPath(new URL(import.meta.env.BASE_URL + 'libs/draco/', import.meta.url).href); this.dracoLoader.parse(contents, (geometry) => { let object; if (geometry.index !== null) { const material = new THREE.MeshStandardMaterial(); object = new THREE.Mesh(geometry, material); object.name = filename; } else { const material = new THREE.PointsMaterial({ size: 0.01 }); material.vertexColors = geometry.hasAttribute('color'); object = new THREE.Points(geometry, material); object.name = filename; } this.dracoLoader.dispose(); this.dracoLoader = null; addToScene && App.execute(new AddObjectCommand(object)); resolve(object); }); }, false); reader.readAsArrayBuffer(file); break; case 'fbx': reader.addEventListener('load', async function (event) { const contents = event.target?.result; const { FBXLoader } = await import('three/examples/jsm/loaders/FBXLoader.js'); const loader = new FBXLoader(manager); //@ts-ignore const object = loader.parse(contents as ArrayBuffer); addToScene && App.execute(new AddObjectCommand(object)); resolve(object); }, false); reader.readAsArrayBuffer(file); break; case 'glb': reader.addEventListener('load', async (event) => { const contents = event.target?.result as ArrayBuffer; const loader = await this.createGLTFLoader(manager); loader.parse(contents, '', (result) => { const scene = result.scene; scene.name = filename; scene.animations.push(...result.animations); addToScene && App.execute(new AddObjectCommand(scene)); this.disposeGLTFLoaderEffects(loader); resolve(scene); }); }, false); reader.readAsArrayBuffer(file); break; case 'gltf': reader.addEventListener('load', async (event) => { const contents = event.target?.result as ArrayBuffer; const loader = await this.createGLTFLoader(manager); loader.parse(contents, '', (result) => { const scene = result.scene; scene.name = filename; scene.animations.push(...result.animations); addToScene && App.execute(new AddObjectCommand(scene)); this.disposeGLTFLoaderEffects(loader); resolve(scene); }); }, false); reader.readAsArrayBuffer(file); break; case 'js': case 'json': reader.addEventListener('load', (event) => { const contents: string = event.target?.result as string; // 2.0 if (contents.indexOf('postMessage') !== - 1) { const blob = new Blob([contents], { type: 'text/javascript' }); const url = URL.createObjectURL(blob); const worker = new Worker(url); worker.onmessage = (event) => { event.data.metadata = { version: 2 }; this.handleJSON(event.data,addToScene).then(object => resolve(object as THREE.Object3D)).catch(error => reject(error)); }; worker.postMessage(Date.now()); return; } // >= 3.0 let data; try { data = JSON.parse(contents); } catch (error) { App.log.error(error as string); return; } this.handleJSON(data,addToScene).then(object => resolve(object as THREE.Object3D)).catch(error => reject(error)); }, false); reader.readAsText(file); break; case 'ifc': reader.addEventListener('load', async (event) => { if (!this._ifcLoader) { const { IFCLoader } = await import("web-ifc-three/IFCLoader"); this._ifcLoader = new IFCLoader(); const ifcWorkerUrl = new URL(import.meta.env.BASE_URL + 'libs/web-ifc/IFCWorker.js', import.meta.url).href; this._ifcLoader.ifcManager.useWebWorkers(true, ifcWorkerUrl).then(async () => { if(!this._ifcLoader) return; await this._ifcLoader.ifcManager.setWasmPath('/'); // const { IFCSPACE } = await import('web-ifc'); // await this._ifcLoader.ifcManager.parser.setupOptionalCategories( { // [ IFCSPACE ]: false, // }); await this._ifcLoader.ifcManager.applyWebIfcConfig({ // 使用更快的(不那么精确的)布尔逻辑 USE_FAST_BOOLS: true }); }) } const model = await this._ifcLoader.parse(event.target?.result as ArrayBuffer); model.name = filename; model.isIFC = true; addToScene && App.execute(new AddObjectCommand(model)); resolve(model); }, false); reader.readAsArrayBuffer(file); break; // case 'ifc': // reader.addEventListener( 'load', async function ( event ) { // const { IFCLoader } = await import( 'three/examples/jsm/loaders/IFCLoader.js' ); // // const loader = new IFCLoader(); // loader.ifcManager.setWasmPath( 'three/examples/jsm/loaders/ifc/' ); // // // @ts-ignore // const model = await loader.parse( event.target.result ); // model.mesh.name = filename; // // App.execute( new AddObjectCommand(model.mesh)); // }, false ); // reader.readAsArrayBuffer( file ); // break; case 'kmz': reader.addEventListener('load', async function (event) { const { KMZLoader } = await import('three/examples/jsm/loaders/KMZLoader.js'); const loader = new KMZLoader(); const collada = loader.parse(event.target?.result as ArrayBuffer); collada.scene.name = filename; addToScene && App.execute(new AddObjectCommand(collada.scene)); resolve(collada.scene); }, false); reader.readAsArrayBuffer(file); break; case 'ldr': case 'mpd': reader.addEventListener('load', async function (event) { const { LDrawLoader } = await import('three/examples/jsm/loaders/LDrawLoader.js'); const loader = new LDrawLoader(); loader.setPath('three/examples/models/ldraw/officialLibrary/'); // @ts-ignore loader.parse(event.target?.result as string, undefined, function (group) { group.name = filename; // Convert from LDraw coordinates: rotate 180 degrees around OX group.rotation.x = Math.PI; addToScene && App.execute(new AddObjectCommand(group)); resolve(group); }); }, false); reader.readAsText(file); break; case 'md2': reader.addEventListener('load', async function (event) { const contents = event.target?.result as ArrayBuffer; const { MD2Loader } = await import('three/examples/jsm/loaders/MD2Loader.js'); const geometry = new MD2Loader().parse(contents); const material = new THREE.MeshStandardMaterial(); const mesh = new THREE.Mesh(geometry, material); //@ts-ignore mesh.mixer = new THREE.AnimationMixer(mesh); mesh.name = filename; //@ts-ignore mesh.animations.push(...geometry.animations); addToScene && App.execute(new AddObjectCommand(mesh)); resolve(mesh); }, false); reader.readAsArrayBuffer(file); break; case 'obj': reader.addEventListener('load', async function (event) { const contents = event.target?.result as string; const { OBJLoader } = await import('three/examples/jsm/loaders/OBJLoader.js'); const objLoader = new OBJLoader(); /** 2023/02/03 二三:判断是否存在已解析的mtl文件 **/ if (mtlMaterials !== null) { objLoader.setMaterials(mtlMaterials); } const object = objLoader.parse(contents); object.name = filename; addToScene && App.execute(new AddObjectCommand(object)); resolve(object); }, false); reader.readAsText(file); break; case 'mtl': //mtl文件已经提前预加载 break; case 'pcd': reader.addEventListener('load', async function (event) { const contents = event.target?.result as ArrayBuffer; const { PCDLoader } = await import('three/examples/jsm/loaders/PCDLoader.js'); const points = new PCDLoader().parse(contents); points.name = filename; addToScene && App.execute(new AddObjectCommand(points)); resolve(points); }, false); reader.readAsArrayBuffer(file); break; case 'ply': reader.addEventListener('load', async function (event) { const contents = event.target?.result as ArrayBuffer; const { PLYLoader } = await import('three/examples/jsm/loaders/PLYLoader.js'); const geometry = new PLYLoader().parse(contents); let object; if (geometry.index !== null) { const material = new THREE.MeshStandardMaterial(); object = new THREE.Mesh(geometry, material); object.name = filename; } else { const material = new THREE.PointsMaterial({ size: 0.01 }); material.vertexColors = geometry.hasAttribute('color'); object = new THREE.Points(geometry, material); object.name = filename; } addToScene && App.execute(new AddObjectCommand(object)); resolve(object); }, false); reader.readAsArrayBuffer(file); break; case 'stl': reader.addEventListener('load', async function (event) { const contents = event.target?.result as ArrayBuffer; const { STLLoader } = await import('three/examples/jsm/loaders/STLLoader.js'); const geometry = new STLLoader().parse(contents); const material = new THREE.MeshStandardMaterial(); const mesh = new THREE.Mesh(geometry, material); mesh.name = filename; addToScene && App.execute(new AddObjectCommand(mesh)); resolve(mesh); }, false); if (reader.readAsBinaryString !== undefined) { reader.readAsBinaryString(file); } else { reader.readAsArrayBuffer(file); } break; case 'svg': reader.addEventListener('load', async function (event) { const contents = event.target?.result as string; const { SVGLoader } = await import('three/examples/jsm/loaders/SVGLoader.js'); const loader = new SVGLoader(); const paths = loader.parse(contents).paths; const group = new THREE.Group(); group.scale.multiplyScalar(0.1); group.scale.y *= - 1; for (let i = 0; i < paths.length; i++) { const path = paths[i]; const material = new THREE.MeshBasicMaterial({ color: path.color, depthWrite: false }); const shapes = SVGLoader.createShapes(path); for (let j = 0; j < shapes.length; j++) { const shape = shapes[j]; const geometry = new THREE.ShapeGeometry(shape); const mesh = new THREE.Mesh(geometry, material); group.add(mesh); } } addToScene && App.execute(new AddObjectCommand(group)); resolve(group); }, false); reader.readAsText(file); break; case 'usdz': reader.addEventListener('load', async function (event) { const contents = event.target?.result as ArrayBuffer; const { USDZLoader } = await import('three/examples/jsm/loaders/USDZLoader.js'); const group = new USDZLoader().parse(contents); group.name = filename; addToScene && App.execute(new AddObjectCommand(group)); resolve(group); }, false); reader.readAsArrayBuffer(file); break; case 'vox': reader.addEventListener('load', async function (event) { const contents = event.target?.result as ArrayBuffer; const { VOXLoader, VOXMesh } = await import('three/examples/jsm/loaders/VOXLoader.js'); const chunks = new VOXLoader().parse(contents); const group = new THREE.Group(); group.name = filename; for (let i = 0; i < chunks.length; i++) { const chunk: any = chunks[i]; const mesh = new VOXMesh(chunk); // @ts-ignore group.add(mesh); } addToScene && App.execute(new AddObjectCommand(group)); resolve(group); }, false); reader.readAsArrayBuffer(file); break; case 'vtk': case 'vtp': reader.addEventListener('load', async function (event) { const contents = event.target?.result as ArrayBuffer; const { VTKLoader } = await import('three/examples/jsm/loaders/VTKLoader.js'); //@ts-ignore const geometry = new VTKLoader().parse(contents); const material = new THREE.MeshStandardMaterial(); const mesh = new THREE.Mesh(geometry, material); mesh.name = filename; addToScene && App.execute(new AddObjectCommand(mesh)); resolve(mesh); }, false); reader.readAsArrayBuffer(file); break; case 'wrl': reader.addEventListener('load', async function (event) { const contents = event.target?.result as string; const { VRMLLoader } = await import('three/examples/jsm/loaders/VRMLLoader.js'); //@ts-ignore const result = new VRMLLoader().parse(contents); addToScene && App.execute(new SetSceneCommand(result)); resolve(result); }, false); reader.readAsText(file); break; case 'xyz': reader.addEventListener('load', async function (event) { const contents = event.target?.result as string; const { XYZLoader } = await import('three/examples/jsm/loaders/XYZLoader.js'); //@ts-ignore const geometry = new XYZLoader().parse(contents); const material = new THREE.PointsMaterial(); //@ts-ignore material.vertexColors = geometry.hasAttribute('color'); const points = new THREE.Points(geometry as THREE.BufferGeometry, material); points.name = filename; addToScene && App.execute(new AddObjectCommand(points)); resolve(points); }, false); reader.readAsText(file); break; case 'zip': reader.addEventListener('load', (event) => { this.handleZIP(event.target?.result,addToScene).then(object => resolve(object as THREE.Object3D)).catch(error => reject(error)); }, false); reader.readAsArrayBuffer(file); break; default: App.log.warn(`不支持的文件格式: ${extension}`); reject(`不支持的文件格式: ${extension}`); break; } }) } handleJSON(data,addToScene = true) { return new Promise((resolve, reject) => { if (data.metadata === undefined) { // 2.0 data.metadata = { type: 'Geometry' }; } if (data.metadata.type === undefined) { // 3.0 data.metadata.type = 'Geometry'; } if (data.metadata.formatVersion !== undefined) { data.metadata.version = data.metadata.formatVersion; } switch (data.metadata.type.toLowerCase()) { case 'buffergeometry': { const loader = new THREE.BufferGeometryLoader(); const result = loader.parse(data); const mesh = new THREE.Mesh(result); addToScene && App.execute(new AddObjectCommand(mesh)); resolve(mesh); break; } case 'geometry': App.log.warn("Loader:不再支持“几何图形”"); reject("Loader:不再支持“几何图形”"); break; case 'object': { const loader = this.objectLoader; loader.setResourcePath(this.texturePath); loader.parse(data, function (result: any) { if (result.isScene) { addToScene && App.execute(new SetSceneCommand(result)); resolve(result); } else { addToScene && App.execute(new AddObjectCommand(result)); resolve(result); } }); break; } case 'app': resolve(App.fromJSON(data)); break; } }) } async handleZIP(contents,addToScene = true) { return new Promise(async (resolve, reject) => { try { const zip = unzipSync(new Uint8Array(contents)); // Poly if (zip['model.obj'] && zip['materials.mtl']) { const { OBJLoader } = await import('three/examples/jsm/loaders/OBJLoader.js'); //@ts-ignore const materials = new MTLLoader().parse(strFromU8(zip['materials.mtl'])); const object = new OBJLoader().setMaterials(materials).parse(strFromU8(zip['model.obj'])); addToScene && App.execute(new AddObjectCommand(object)); resolve(object); } // 20250904: 新增3dtiles支持 if(zip['tileset.json']){ } // for (const path in zip) { const file = zip[path]; const manager = new THREE.LoadingManager(); manager.setURLModifier(function (url) { const file = zip[url]; if (file) { const blob = new Blob([file.buffer], { type: 'application/octet-stream' }); return URL.createObjectURL(blob); } return url; }); const extension = path.split('.').pop()?.toLowerCase(); switch (extension) { case 'fbx': { const { FBXLoader } = await import('three/examples/jsm/loaders/FBXLoader.js'); const loader = new FBXLoader(manager); //@ts-ignore const object = loader.parse(file.buffer); addToScene && App.execute(new AddObjectCommand(object)); resolve(object); break; } case 'glb': { const loader = await this.createGLTFLoader(); loader.parse(file.buffer, '', (result) => { const scene = result.scene; scene.animations.push(...result.animations); addToScene && App.execute(new AddObjectCommand(scene)); this.disposeGLTFLoaderEffects(loader); resolve(scene); }); break; } case 'gltf': { const loader = await this.createGLTFLoader(manager); loader.parse(strFromU8(file), '', (result)=> { const scene = result.scene; scene.animations.push(...result.animations); addToScene && App.execute(new AddObjectCommand(scene)); this.disposeGLTFLoaderEffects(loader); resolve(scene); }); break; } } } }catch (error) { reject(error); } }) } async createGLTFLoader(manager?:THREE.LoadingManager) { const { MeshoptDecoder } = await import( 'three/examples/jsm/libs/meshopt_decoder.module.js' ); const { GLTFAnimationPointerExtension } = await import( '@needle-tools/three-animation-pointer' ); const loader = new GLTFLoader(manager); loader.setDRACOLoader(this.dracoLoader); loader.setKTX2Loader(this.ktx2Loader); loader.setMeshoptDecoder(MeshoptDecoder); // 20251105:添加对带有 KHR_animation_pointer 动画的 glTF 文件的支持 loader.register(p => { return new GLTFAnimationPointerExtension(p); }); return loader; } disposeGLTFLoaderEffects(loader:any){ if(this._dracoLoader && loader.dracoLoader === this._dracoLoader){ this._dracoLoader.dispose(); this._dracoLoader = null; loader.dracoLoader = null; }else{ loader.dracoLoader?.dispose(); loader.dracoLoader = null; } if(this._ktx2Loader && loader.ktx2Loader === this._ktx2Loader){ this._ktx2Loader?.dispose(); this._ktx2Loader = null; loader.ktx2Loader = null; }else{ loader.ktx2Loader.dispose(); loader.ktx2Loader = null; } loader.meshoptDecoder = null; } loadUrlTexture(extension: string, url: string, onload?: (tex: THREE.Texture) => void,onerror?: (err: any) => void) { switch (extension) { case 'hdr': { if(!this.rgbeLoader) this.rgbeLoader = new RGBELoader(); this.rgbeLoader.setDataType(THREE.HalfFloatType); return this.rgbeLoader.load(url, (hdrTexture) => { hdrTexture.wrapS = THREE.RepeatWrapping; hdrTexture.wrapT = THREE.RepeatWrapping; hdrTexture.needsUpdate = true; onload && onload(hdrTexture); },()=>{},(err)=>{ onerror && onerror(err); }); } case 'tga': { if(!this.tgaLoader) this.tgaLoader = new TGALoader(); return this.tgaLoader.load(url, (tagTex) => { tagTex.wrapS = THREE.RepeatWrapping; tagTex.wrapT = THREE.RepeatWrapping; tagTex.needsUpdate = true; onload && onload(tagTex); },()=>{},(err)=>{ onerror && onerror(err); }); } case "exr": { return this.exrLoader.load(url, (exrTex) => { exrTex.wrapS = THREE.RepeatWrapping; exrTex.wrapT = THREE.RepeatWrapping; exrTex.needsUpdate = true; onload && onload(exrTex); },()=>{},(err)=>{ onerror && onerror(err); }); } default: { if(!this.textureLoader) this.textureLoader = new THREE.TextureLoader(); return this.textureLoader.load(url, (tex) => { tex.wrapS = THREE.RepeatWrapping; tex.wrapT = THREE.RepeatWrapping; tex.needsUpdate = true; onload && onload(tex); },()=>{},(err)=>{ onerror && onerror(err); }); } } } } const loader = new Loader(); export default loader;