three-base 2.0.83 → 2.0.85

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/threeLoader.ts CHANGED
@@ -1,16 +1,27 @@
1
1
  /*
2
- * @Author: xiaosihan
3
- * @Date: 2022-04-07 16:42:26
2
+ * @Author: xiaosihan
3
+ * @Date: 2022-04-07 16:42:26
4
4
  * @Last Modified by: xiaosihan
5
5
  * @Last Modified time: 2023-02-07 11:11:48
6
6
  */
7
7
 
8
- import { BoxGeometry, CanvasTexture, DefaultLoadingManager, EventDispatcher, Material, Mesh, MeshLambertMaterial, MeshStandardMaterial, Object3D, RepeatWrapping, SRGBColorSpace, Texture, TextureLoader, VideoTexture } from "three";
8
+ import {
9
+ CanvasTexture,
10
+ DefaultLoadingManager,
11
+ EventDispatcher,
12
+ Material,
13
+ Mesh,
14
+ MeshStandardMaterial,
15
+ RepeatWrapping,
16
+ Texture,
17
+ TextureLoader,
18
+ VideoTexture,
19
+ } from "three";
20
+ import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
9
21
  import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
10
22
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
11
23
  import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader";
12
24
  import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
13
- import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
14
25
  import { STLLoader } from "three/examples/jsm/loaders/STLLoader";
15
26
  import { USDZLoader } from "three/examples/jsm/loaders/USDZLoader";
16
27
 
@@ -18,211 +29,390 @@ import { USDZLoader } from "three/examples/jsm/loaders/USDZLoader";
18
29
  // import { IFCLoader } from 'web-ifc-three';
19
30
  // import { IFCSPACE } from 'web-ifc';
20
31
 
21
-
22
32
  // 引入静态资源
23
33
  //@ts-ignore
24
34
  import dracoWasmWrapperJS from "./lib/draco/draco_wasm_wrapper.js?url";
25
35
  //@ts-ignore
26
36
  import dracoDecoderJS from "./lib/draco/draco_decoder.js?url";
27
37
  //@ts-ignore
38
+ import axios from "axios";
28
39
  import dracoDecoderWASM from "./lib/draco/draco_decoder.wasm?url";
29
- import { get } from "lodash";
40
+ import threeUtils from "./threeUtils";
30
41
 
31
42
  // 加载器
32
43
  class ThreeLoader extends EventDispatcher {
44
+ constructor() {
45
+ super();
33
46
 
34
- constructor() {
35
- super();
47
+ DefaultLoadingManager.onStart = () => {
48
+ this.onload = false;
49
+ };
36
50
 
37
- DefaultLoadingManager.onStart = () => {
38
- this.onload = false;
39
- }
51
+ // 有资源加载完成时通知渲染器
52
+ DefaultLoadingManager.onProgress = (
53
+ url: string,
54
+ loaded: number,
55
+ total: number
56
+ ) => {
57
+ if (loaded < total) {
58
+ this.dispatchEvent({ type: "loadding" } as never);
59
+ } else if (loaded === total) {
60
+ this.dispatchEvent({ type: "onLoad" } as never);
61
+ }
62
+ };
40
63
 
41
- // 有资源加载完成时通知渲染器
42
- DefaultLoadingManager.onProgress = (url: string, loaded: number, total: number) => {
43
- if (loaded < total) {
44
- this.dispatchEvent({ type: "loadding" } as never);
45
- } else if (loaded === total) {
46
- this.dispatchEvent({ type: "onLoad" } as never);
47
- }
48
- }
64
+ DefaultLoadingManager.onLoad = () => {
65
+ this.onload = true;
66
+ };
49
67
 
50
- DefaultLoadingManager.onLoad = () => {
51
- this.onload = true;
52
- }
68
+ // 资源替换
69
+ DefaultLoadingManager.setURLModifier((url) => {
70
+ const filename = url
71
+ .replace(`blob:${location.origin}/`, "")
72
+ .replace(/^\.\//, "");
73
+ let new_url = this._blobs[filename] || url;
74
+ //如果全匹配没有匹配到资源,就尝试文件名匹配
75
+ if (!this._blobs[filename]) {
76
+ const blobKey =
77
+ Object.keys(this._blobs).find((key) =>
78
+ new RegExp(`${key}$`).test(filename)
79
+ ) || "";
80
+ new_url = this._blobs[blobKey] || url;
81
+ }
82
+ return new_url;
83
+ });
84
+ }
85
+
86
+ // 各向异性
87
+ anisotropy = 16;
88
+
89
+ // 设置各向异性 由渲染器来调用并设置
90
+ setAnisotropy(v: number) {
91
+ this.anisotropy = v;
92
+ }
93
+
94
+ // 纹理对向缓存
95
+ textureMap = new Map() as Map<string, Texture>;
96
+
97
+ // 纹理加载器
98
+ textureLoader = new TextureLoader();
99
+
100
+ // 获取地址对应的单例纹理对象
101
+ getTexture(src: string | HTMLCanvasElement) {
102
+ if (src instanceof HTMLCanvasElement) {
103
+ const canvasTexture = new CanvasTexture(src);
104
+ canvasTexture.wrapS = canvasTexture.wrapT = RepeatWrapping;
105
+ canvasTexture.anisotropy = this.anisotropy;
106
+ // canvasTexture.colorSpace = SRGBColorSpace;
107
+
108
+ return canvasTexture;
53
109
  }
54
110
 
55
- // 各向异性
56
- anisotropy = 16;
57
-
58
- // 设置各向异性 由渲染器来调用并设置
59
- setAnisotropy(v: number) {
60
- this.anisotropy = v;
61
- };
111
+ let texture = this.textureMap.get(src);
112
+
113
+ if (!texture) {
114
+ if (/\.(mp4|mov)$/i.test(src)) {
115
+ const videoDom = document.createElement("video");
116
+ videoDom.autoplay = true;
117
+ videoDom.loop = true;
118
+ videoDom.muted = true;
119
+ videoDom.preload = "auto";
120
+ videoDom.playsInline = true;
121
+ videoDom.src = src;
122
+ videoDom.crossOrigin = "anonymous";
123
+ videoDom.play();
124
+ const firstOptoin = () => {
125
+ videoDom.pause();
126
+ videoDom.play();
127
+ document.removeEventListener("pointerup", firstOptoin);
128
+ document.removeEventListener("touchstart", firstOptoin);
129
+ };
130
+ document.addEventListener("pointerup", firstOptoin);
131
+ document.addEventListener("touchstart", firstOptoin);
132
+ texture = new VideoTexture(videoDom);
133
+ } else {
134
+ texture = this.textureLoader.load(src, (tex) => {
135
+ tex.image.crossOrigin = "anonymous";
136
+ });
137
+ }
138
+
139
+ this.textureMap.set(src, texture);
140
+ }
62
141
 
63
- // 纹理对向缓存
64
- textureMap = new Map() as Map<string, Texture>;
142
+ texture.wrapS = texture.wrapT = RepeatWrapping;
143
+ texture.anisotropy = this.anisotropy;
144
+ // texture.colorSpace = SRGBColorSpace;
65
145
 
66
- // 纹理加载器
67
- textureLoader = new TextureLoader();
146
+ return texture as Texture;
147
+ }
68
148
 
69
- // 获取地址对应的单例纹理对象
70
- getTexture(src: string | HTMLCanvasElement) {
149
+ // 资源列表
150
+ _blobs: { [key: string]: string } = {};
71
151
 
72
- if (src instanceof HTMLCanvasElement) {
152
+ // fbx模型加载器
153
+ fbxLoader = new FBXLoader();
73
154
 
74
- const canvasTexture = new CanvasTexture(src);
75
- canvasTexture.wrapS = canvasTexture.wrapT = RepeatWrapping;
76
- canvasTexture.anisotropy = this.anisotropy;
77
- // canvasTexture.colorSpace = SRGBColorSpace;
155
+ //obj 加载器
156
+ objLoader = new OBJLoader();
78
157
 
79
- return canvasTexture;
80
- }
158
+ // stl 加载器
159
+ stlLoader = new STLLoader();
81
160
 
82
- let texture = this.textureMap.get(src);
83
-
84
- if (!texture) {
85
- if (/\.(mp4|mov)$/i.test(src)) {
86
- const videoDom = document.createElement("video");
87
- videoDom.autoplay = true;
88
- videoDom.loop = true;
89
- videoDom.muted = true;
90
- videoDom.preload = "auto";
91
- videoDom.playsInline = true;
92
- videoDom.src = src;
93
- videoDom.crossOrigin = "anonymous";
94
- videoDom.play();
95
- const firstOptoin = () => {
96
- videoDom.pause();
97
- videoDom.play();
98
- document.removeEventListener("pointerup", firstOptoin);
99
- document.removeEventListener("touchstart", firstOptoin);
100
- }
101
- document.addEventListener("pointerup", firstOptoin);
102
- document.addEventListener("touchstart", firstOptoin);
103
- texture = new VideoTexture(videoDom);
104
- } else {
161
+ // 材质加载器
162
+ mtlLoader = new MTLLoader();
105
163
 
106
- texture = this.textureLoader.load(src, tex => {
107
- tex.image.crossOrigin = "anonymous";
164
+ // gltf 加载器
165
+ gltfLoader = (() => {
166
+ const loader = new GLTFLoader();
167
+ const dracoLoader = new (class ThreeBaseDRACOLoader extends DRACOLoader {
168
+ _loadLibrary(url: string, responseType: string) {
169
+ //@ts-ignore
170
+ let urlConf =
171
+ process.env.NODE_ENV === "development"
172
+ ? {
173
+ "draco_decoder.js": "draco_decoder.js",
174
+ "draco_wasm_wrapper.js": "draco_wasm_wrapper.js",
175
+ "draco_decoder.wasm": "draco_decoder.wasm",
176
+ }
177
+ : {
178
+ "draco_decoder.js": dracoWasmWrapperJS,
179
+ "draco_wasm_wrapper.js": dracoDecoderJS,
180
+ "draco_decoder.wasm": dracoDecoderWASM,
181
+ };
182
+ //@ts-ignore
183
+ return super._loadLibrary(urlConf[url], responseType);
184
+ }
185
+ })();
186
+ //@ts-ignore
187
+ dracoLoader.setDecoderPath(
188
+ process.env.NODE_ENV === "development"
189
+ ? "/node_modules/three/examples/jsm/libs/draco/"
190
+ : ""
191
+ );
192
+ loader.setDRACOLoader(dracoLoader);
193
+ return loader;
194
+ })();
195
+
196
+ //usdz加载器
197
+ usdzLoader = new USDZLoader();
198
+
199
+ //把贴图的 flipy 设置为 fasle
200
+ setFlipYIsFalse<T extends Material | Material[]>(material: T) {
201
+ if (material instanceof Array) {
202
+ material.map((m) => this.setFlipYIsFalse(m));
203
+ } else {
204
+ const m = material as MeshStandardMaterial;
205
+ const parame: Array<keyof MeshStandardMaterial> = [
206
+ "map",
207
+ "lightMap",
208
+ "aoMap",
209
+ "emissiveMap",
210
+ "bumpMap",
211
+ "normalMap",
212
+ "displacementMap",
213
+ "alphaMap",
214
+ "envMap",
215
+ ];
216
+ parame.map((param) => {
217
+ const t = m[param] as Texture | undefined;
218
+ if (t) {
219
+ t.flipY = false;
220
+ }
221
+ });
222
+ }
223
+ }
224
+
225
+ /** 加载模型支持glb,fbx,stl,obj */
226
+ async loadModel(url: string, filename: string = "") {
227
+ // 获取后缀
228
+ const suffix = threeUtils.getSuffix(filename || url);
229
+
230
+ switch (suffix) {
231
+ case "zip":
232
+ const JSZip = (await import("jszip")).default;
233
+ const arrayBuffer = await axios
234
+ .get(url, {
235
+ responseType: "arraybuffer",
236
+ })
237
+ .then((res) => res.data)
238
+ .catch((err) => {
239
+ throw new Error(`${url} 加载失败`);
240
+ });
241
+
242
+ return await JSZip.loadAsync(arrayBuffer).then(async (zip) => {
243
+ for (let filename in zip.files) {
244
+ if (!zip.files[filename].dir) {
245
+ // 不读文件夹
246
+ await zip
247
+ .file(filename)!
248
+ .async("blob")
249
+ .then((blob) => {
250
+ var blobURL = URL.createObjectURL(blob);
251
+ this._blobs[filename] = blobURL;
108
252
  });
109
-
110
253
  }
111
-
112
- this.textureMap.set(src, texture);
113
- }
114
-
115
- texture.wrapS = texture.wrapT = RepeatWrapping;
116
- texture.anisotropy = this.anisotropy;
117
- // texture.colorSpace = SRGBColorSpace;
118
-
119
- return texture as Texture;
120
- };
121
-
122
- // fbx模型加载器
123
- fbxLoader = new FBXLoader();
124
-
125
- //obj 加载器
126
- objLoader = new OBJLoader();
127
-
128
- // stl 加载器
129
- stlLoader = new STLLoader();
130
-
131
- // 材质加载器
132
- mtlLoader = new MTLLoader();
133
-
134
- // gltf 加载器
135
- gltfLoader = (() => {
136
- const loader = new GLTFLoader();
137
- const dracoLoader = new class ThreeBaseDRACOLoader extends DRACOLoader {
138
-
139
- _loadLibrary(url: string, responseType: string) {
140
- //@ts-ignore
141
- let urlConf = (process.env.NODE_ENV === "development") ? {
142
- "draco_decoder.js": "draco_decoder.js",
143
- "draco_wasm_wrapper.js": "draco_wasm_wrapper.js",
144
- "draco_decoder.wasm": "draco_decoder.wasm"
145
- } : {
146
- "draco_decoder.js": dracoWasmWrapperJS,
147
- "draco_wasm_wrapper.js": dracoDecoderJS,
148
- "draco_decoder.wasm": dracoDecoderWASM
254
+ }
255
+
256
+ let objLoader = new OBJLoader();
257
+
258
+ // 如果有mtl文件就加载mtl文件
259
+ let mtl = Object.keys(zip.files).find((filename) =>
260
+ /\.mtl$/.test(filename)
261
+ );
262
+ if (mtl) {
263
+ const materials = await this.mtlLoader.loadAsync(mtl);
264
+ objLoader.setMaterials(materials);
265
+ } else {
266
+ //@ts-ignore
267
+ objLoader.materials = null;
268
+ }
269
+
270
+ //如果有obj就加载obj模型
271
+ let obj = Object.keys(zip.files).find((filename) =>
272
+ /\.obj$/.test(filename)
273
+ );
274
+ if (obj) {
275
+ const objObject3d = await objLoader.loadAsync(obj);
276
+ objObject3d.traverse((obj) => {
277
+ const mesh = obj as Mesh;
278
+
279
+ if (mesh.isMesh) {
280
+ // 如果obj没有材质就给默认的材质
281
+ if (!objLoader.materials) {
282
+ mesh.material = new MeshStandardMaterial({
283
+ metalness: 0.5,
284
+ roughness: 0.5,
285
+ });
149
286
  }
150
- //@ts-ignore
151
- return super._loadLibrary(urlConf[url], responseType);
152
- }
153
287
 
154
- }();
155
- //@ts-ignore
156
- dracoLoader.setDecoderPath((process.env.NODE_ENV === "development") ? '/node_modules/three/examples/jsm/libs/draco/' : '');
157
- loader.setDRACOLoader(dracoLoader);
158
- return loader;
159
- })()
160
-
161
- //usdz加载器
162
- usdzLoader = new USDZLoader();
163
-
164
- /** 加载模型支持glb,fbx,stl,obj */
165
- async loadModel(url: string, filename: string = "") {
166
-
167
- // 获取后缀
168
- const suffix = (get((filename || url).match(/\.([^\.]+)$/), [1]) || "").toLowerCase();
169
-
170
- let Object3d = new Object3D();
171
-
172
- if (["fbx"].includes(suffix)) {
173
- await threeLoader.fbxLoader.loadAsync(url).then(res => {
174
- Object3d = res;
175
- }).catch(() => {
176
- console.error(`${url},加载失败`);
177
- })
178
- } else if (["gltf", "glb"].includes(suffix)) {
179
- await threeLoader.gltfLoader.loadAsync(url).then(res => {
180
- Object3d = res.scene;
181
- Object3d.animations = res.animations;
182
- }).catch(() => {
183
- console.error(`${url},加载失败`);
184
- })
185
-
186
- } else if (["stl"].includes(suffix)) {
187
- await threeLoader.stlLoader.loadAsync(url).then(geometry => {
188
- Object3d = new Mesh(geometry, new MeshStandardMaterial({
189
- color: "#f1fbff",
190
- metalness: 0.79,
191
- roughness: 0.56,
192
- envMapIntensity: 0.4
193
- }));
194
- }).catch(() => {
195
- console.error(`${url},加载失败`);
196
- })
197
- } else if (["usdz"].includes(suffix)) {
198
- await threeLoader.usdzLoader.loadAsync(url).then(res => {
199
- Object3d = res; // usdz 目前还不支持骨骼动画
200
- Object3d.animations = res.animations;
201
- Object3d.traverse(obj => {
202
- const mesh = (obj as Mesh);
203
- if (mesh.isMesh) {
204
- (mesh.material as Material).transparent = true;
205
- }
206
- });
207
- }).catch((err) => {
208
- console.error(`${url},加载失败`, err);
209
- })
288
+ const uv = mesh.geometry.getAttribute("uv");
289
+ if (uv) {
290
+ for (let i = 0; i < uv.count; i++) {
291
+ uv.setY(i, 1 - uv.getY(i));
292
+ }
293
+ }
294
+ // obj只翻转uv不翻转贴图
295
+ this.setFlipYIsFalse(mesh.material);
296
+ }
297
+ });
298
+
299
+ return objObject3d;
300
+ }
301
+
302
+ //如果有gltf文件就加载gltf文件
303
+ let gltf = Object.keys(zip.files).find((filename) =>
304
+ /\.(gltf|glb)$/.test(filename)
305
+ );
306
+ if (gltf) {
307
+ return await this.loadModel(gltf);
308
+ }
309
+
310
+ //如果有dae文件就加载dae文件
311
+ let dae = Object.keys(zip.files).find((filename) =>
312
+ /\.dae$/.test(filename)
313
+ );
314
+ if (dae) {
315
+ return await this.loadModel(dae);
316
+ }
317
+ //如果有fbx文件就加载fbx文件
318
+ let fbx = Object.keys(zip.files).find((filename) =>
319
+ /\.fbx$/.test(filename)
320
+ );
321
+ if (fbx) {
322
+ return await this.loadModel(fbx);
323
+ }
324
+
325
+ //如果有usdz文件就加载usdz文件
326
+ let usdz = Object.keys(zip.files).find((filename) =>
327
+ /\.usdz$/.test(filename)
328
+ );
329
+ if (usdz) {
330
+ return await this.loadModel(usdz);
331
+ }
332
+
333
+ //如果有stl文件就加载stl文件
334
+ let stl = Object.keys(zip.files).find((filename) =>
335
+ /\.stl$/.test(filename)
336
+ );
337
+ if (stl) {
338
+ return await this.loadModel(stl);
339
+ }
340
+
341
+ if ([obj, gltf, dae, fbx, usdz, stl].every((v) => !v)) {
342
+ throw new Error(
343
+ `${url}压缩包里没有obj.glb.gltf.dae.fbx.usde.stl等模型文件`
344
+ );
345
+ }
346
+ });
347
+
348
+ case "fbx":
349
+ return await threeLoader.fbxLoader
350
+ .loadAsync(url)
351
+ .then((res) => {
352
+ return res;
353
+ })
354
+ .catch(() => {
355
+ console.log(`${url},加载失败`);
356
+ });
357
+
358
+ case "gltf":
359
+ case "glb":
360
+ return await threeLoader.gltfLoader
361
+ .loadAsync(url)
362
+ .then((res) => {
363
+ res.scene.animations = res.animations;
364
+ return res.scene;
365
+ })
366
+ .catch(() => {
367
+ console.log(`${url},加载失败`);
368
+ });
369
+
370
+ case "stl":
371
+ return await threeLoader.stlLoader
372
+ .loadAsync(url)
373
+ .then((geometry) => {
374
+ return new Mesh(
375
+ geometry,
376
+ new MeshStandardMaterial({
377
+ metalness: 0.5,
378
+ roughness: 0.5,
379
+ })
380
+ );
381
+ })
382
+ .catch(() => {
383
+ console.log(`${url},加载失败`);
384
+ });
385
+
386
+ case "usdz":
387
+ return await threeLoader.usdzLoader
388
+ .loadAsync(url)
389
+ .then((res) => {
390
+ res.traverse((obj) => {
391
+ const mesh = obj as Mesh;
392
+ if (mesh.isMesh) {
393
+ (mesh.material as Material).transparent = true;
394
+ }
395
+ });
396
+ return res;
397
+ })
398
+ .catch((err) => {
399
+ console.log(`${url},加载失败`, err);
400
+ });
401
+
402
+ default:
403
+ if (suffix) {
404
+ console.log(`${suffix},不支持的格式`);
210
405
  } else {
211
- if (suffix) {
212
- console.error(`${suffix},不支持的格式`);
213
- } else {
214
- console.error(`${url},不支持的格式,请尝试使用 suffix 参数来指定一下格式`);
215
- }
406
+ console.log(
407
+ `${url},不支持的格式,请尝试使用 suffix 参数来指定一下格式`
408
+ );
216
409
  }
217
-
218
- return Object3d;
219
410
  }
411
+ }
220
412
 
221
- // 是否加载完成
222
- onload = true;
223
-
413
+ // 是否加载完成
414
+ onload = true;
224
415
  }
225
416
 
226
417
  const threeLoader = new ThreeLoader();
227
418
  export default threeLoader;
228
-
package/threeUtils.d.ts CHANGED
@@ -42,6 +42,8 @@ declare class ThreeUtils extends EventDispatcher {
42
42
  getVolume(geometry: BufferGeometry): number;
43
43
  signedVolumeOfTriangle(p1: Vector3, p2: Vector3, p3: Vector3): number;
44
44
  dipose(object3d: Object3D): void;
45
+ getSuffix(filename: string): string;
46
+ getFilename(url: string): string;
45
47
  }
46
48
  declare const threeUtils: ThreeUtils;
47
49
  export default threeUtils;