three-base 2.0.130 → 2.0.134

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.
Files changed (123) hide show
  1. package/ApngTexture.d.ts +13 -13
  2. package/ApngTexture.js +61 -61
  3. package/ApngTextureManage.d.ts +18 -18
  4. package/ApngTextureManage.js +87 -87
  5. package/DirectionalLight.d.ts +6 -0
  6. package/DirectionalLight.js +68 -0
  7. package/EventMesh.d.ts +18 -18
  8. package/EventMesh.js +59 -59
  9. package/ExtrudeMesh.d.ts +112 -112
  10. package/ExtrudeMesh.js +366 -366
  11. package/FPScontrol.d.ts +71 -50
  12. package/FPScontrol.js +505 -232
  13. package/Fire/Fire.d.ts +28 -28
  14. package/Fire/Fire.js +79 -79
  15. package/Fire/FireGromtry.d.ts +9 -9
  16. package/Fire/FireGromtry.js +40 -40
  17. package/Fire/FireMaterial.d.ts +28 -28
  18. package/Fire/FireMaterial.js +32 -32
  19. package/Fire/index.d.ts +1 -1
  20. package/Fire/index.js +1 -1
  21. package/Grass/Grass.d.ts +8 -8
  22. package/Grass/Grass.js +38 -38
  23. package/Grass/index.d.ts +1 -1
  24. package/Grass/index.js +1 -1
  25. package/Ground1/Ground1.d.ts +11 -11
  26. package/Ground1/Ground1.js +54 -54
  27. package/Ground1/index.d.ts +1 -1
  28. package/Ground1/index.js +1 -1
  29. package/InstancedMeshBase.d.ts +39 -39
  30. package/InstancedMeshBase.js +293 -293
  31. package/KeyFrameAnimation.d.ts +25 -25
  32. package/KeyFrameAnimation.js +78 -78
  33. package/MeshBase.d.ts +35 -35
  34. package/MeshBase.js +223 -223
  35. package/Mirror.d.ts +25 -25
  36. package/Mirror.js +139 -139
  37. package/PipeMesh/PathGeometry.d.ts +44 -44
  38. package/PipeMesh/PathGeometry.js +300 -300
  39. package/PipeMesh/PathPoint.d.ts +17 -17
  40. package/PipeMesh/PathPoint.js +31 -31
  41. package/PipeMesh/PathPointList.d.ts +31 -31
  42. package/PipeMesh/PathPointList.js +208 -208
  43. package/PipeMesh/PathTubeGeometry.d.ts +25 -25
  44. package/PipeMesh/PathTubeGeometry.js +130 -130
  45. package/PipeMesh/PipeMesh.d.ts +25 -25
  46. package/PipeMesh/PipeMesh.js +63 -63
  47. package/PipeMesh/index.d.ts +1 -1
  48. package/PipeMesh/index.js +1 -1
  49. package/PlaneModal1/PlaneModal1.d.ts +31 -31
  50. package/PlaneModal1/PlaneModal1.js +105 -105
  51. package/PlaneModal1/index.d.ts +1 -1
  52. package/PlaneModal1/index.js +1 -1
  53. package/PlaneText.d.ts +39 -39
  54. package/PlaneText.js +30 -30
  55. package/PlaneWithHtml.d.ts +11 -11
  56. package/PlaneWithHtml.js +63 -68
  57. package/PointLight/PointLight.d.ts +14 -13
  58. package/PointLight/PointLight.js +70 -64
  59. package/PointLight/index.d.ts +1 -1
  60. package/PointLight/index.js +1 -1
  61. package/Sky/Sky.d.ts +9 -9
  62. package/Sky/Sky.js +22 -22
  63. package/Sky/index.d.ts +1 -1
  64. package/Sky/index.js +1 -1
  65. package/SpotLight.d.ts +6 -0
  66. package/SpotLight.js +72 -0
  67. package/SpriteModal1/SpriteModal1.d.ts +30 -30
  68. package/SpriteModal1/SpriteModal1.js +110 -110
  69. package/SpriteModal1/index.d.ts +1 -1
  70. package/SpriteModal1/index.js +1 -1
  71. package/SpriteText.d.ts +45 -45
  72. package/SpriteText.js +163 -163
  73. package/SpriteText2.d.ts +47 -47
  74. package/SpriteText2.js +198 -198
  75. package/SpriteWithHtml.d.ts +7 -7
  76. package/SpriteWithHtml.js +44 -49
  77. package/Text3D/Text3D.d.ts +20 -20
  78. package/Text3D/Text3D.js +62 -62
  79. package/Text3D/fontController.d.ts +59 -59
  80. package/Text3D/fontController.js +199 -199
  81. package/Text3D/index.d.ts +1 -1
  82. package/Text3D/index.js +1 -1
  83. package/ThreeBase.d.ts +97 -81
  84. package/ThreeBase.js +613 -560
  85. package/ThreeMouse.d.ts +40 -40
  86. package/ThreeMouse.js +407 -407
  87. package/Transition.d.ts +40 -40
  88. package/Transition.js +210 -210
  89. package/Water/Water.d.ts +5 -0
  90. package/Water/Water.js +26 -0
  91. package/Water/waternormals.jpg +0 -0
  92. package/declare.d.ts +167 -0
  93. package/index.d.ts +1 -1
  94. package/index.js +2 -2
  95. package/keyContorls.d.ts +25 -25
  96. package/keyContorls.js +98 -98
  97. package/package.json +1 -1
  98. package/pass/bloom_pass.d.ts +2 -2
  99. package/pass/bloom_pass.js +99 -99
  100. package/pass/cover_pass.d.ts +2 -2
  101. package/pass/cover_pass.js +95 -95
  102. package/pass/frosted_glass_pass.d.ts +2 -0
  103. package/pass/frosted_glass_pass.js +275 -0
  104. package/pass/fsQuad.d.ts +9 -9
  105. package/pass/fsQuad.js +28 -28
  106. package/pass/glow_pass.d.ts +2 -2
  107. package/pass/glow_pass.js +310 -254
  108. package/pass/ssao_pass.d.ts +11 -0
  109. package/pass/ssao_pass.js +294 -0
  110. package/pass/stroke_pass.d.ts +2 -2
  111. package/pass/stroke_pass.js +199 -199
  112. package/shaders/FXAAShaderMaterial.d.ts +3 -3
  113. package/shaders/FXAAShaderMaterial.js +21 -21
  114. package/shaders/baseTextureToRenderShader.d.ts +2 -2
  115. package/shaders/baseTextureToRenderShader.js +14 -14
  116. package/shaders/copyShader.d.ts +3 -3
  117. package/shaders/copyShader.js +14 -14
  118. package/shaders.d.ts +5 -5
  119. package/shaders.js +42 -42
  120. package/threeLoader.d.ts +34 -34
  121. package/threeLoader.js +527 -527
  122. package/threeUtils.d.ts +52 -52
  123. package/threeUtils.js +330 -330
package/threeLoader.js CHANGED
@@ -1,527 +1,527 @@
1
- /*
2
- * @Author: xiaosihan
3
- * @Date: 2022-04-07 16:42:26
4
- * @Last Modified by: xiaosihan
5
- * @Last Modified time: 2025-01-15 20:34:19
6
- */
7
- import { CanvasTexture, DefaultLoadingManager, EventDispatcher, Mesh, MeshStandardMaterial, RepeatWrapping, SRGBColorSpace, TextureLoader, VideoTexture, } from "three";
8
- import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
9
- import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
10
- import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
11
- import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader";
12
- import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
13
- import { STLLoader } from "three/examples/jsm/loaders/STLLoader";
14
- import { USDZLoader } from "three/examples/jsm/loaders/USDZLoader";
15
- // import { IFCLoader } from 'web-ifc-three/dist/IFCLoader';
16
- // import { IFCLoader } from 'web-ifc-three';
17
- // import { IFCSPACE } from 'web-ifc';
18
- // 引入静态资源
19
- //@ts-ignore
20
- import dracoWasmWrapperJS from "./lib/draco/draco_wasm_wrapper.js?url";
21
- //@ts-ignore
22
- import dracoDecoderJS from "./lib/draco/draco_decoder.js?url";
23
- //@ts-ignore
24
- import axios from "axios";
25
- import { isArray, isEmpty, isNull, isString, isUndefined } from "lodash";
26
- import { generateUUID } from "three/src/math/MathUtils";
27
- import dracoDecoderWASM from "./lib/draco/draco_decoder.wasm?url";
28
- import threeUtils from "./threeUtils";
29
- import { PLYLoader, TGALoader } from "three/examples/jsm/Addons.js";
30
- DefaultLoadingManager.addHandler(/tga$/, new TGALoader());
31
- // 加载器
32
- class ThreeLoader extends EventDispatcher {
33
- constructor() {
34
- super();
35
- DefaultLoadingManager.onStart = () => {
36
- this.onload = false;
37
- };
38
- // 有资源加载完成时通知渲染器
39
- DefaultLoadingManager.onProgress = (url, loaded, total) => {
40
- if (loaded < total) {
41
- this.dispatchEvent({ type: "loadding" });
42
- }
43
- else if (loaded === total) {
44
- this.dispatchEvent({ type: "onLoad" });
45
- }
46
- };
47
- DefaultLoadingManager.onLoad = () => {
48
- this.onload = true;
49
- };
50
- // 资源替换
51
- DefaultLoadingManager.setURLModifier((url) => {
52
- //全路径匹配资源
53
- if (this._blobs[url]) {
54
- return this._blobs[url];
55
- }
56
- const filename = url
57
- .replace(`blob:${location.origin}/`, "")
58
- .replace(/^\.\//, "");
59
- let new_url = this._blobs[filename] || url;
60
- //如果全匹配没有匹配到资源,就尝试文件名匹配
61
- if (!this._blobs[filename]) {
62
- const blobKey = Object.keys(this._blobs).find((key) => this.getFilename(key) === this.getFilename(filename)) || "";
63
- new_url = this._blobs[blobKey] || url;
64
- }
65
- return new_url;
66
- });
67
- }
68
- // 从url 中提取文件名
69
- getFilename(url) {
70
- // 移除URL的查询字符串和哈希部分
71
- const path = url.split("?")[0].split("#")[0];
72
- // 使用lastIndexOf和substring从路径中提取文件名
73
- const filename = path.substring(path.lastIndexOf("/") + 1);
74
- return filename;
75
- }
76
- uuid = generateUUID();
77
- // 各向异性
78
- anisotropy = 16;
79
- // 设置各向异性 由渲染器来调用并设置
80
- setAnisotropy(v) {
81
- this.anisotropy = v;
82
- }
83
- // 纹理对向缓存
84
- textureMap = new Map();
85
- // 纹理加载器
86
- textureLoader = new TextureLoader();
87
- // 获取地址对应的单例纹理对象
88
- getTexture(src) {
89
- if (src instanceof HTMLCanvasElement) {
90
- const canvasTexture = new CanvasTexture(src);
91
- canvasTexture.wrapS = canvasTexture.wrapT = RepeatWrapping;
92
- canvasTexture.anisotropy = this.anisotropy;
93
- canvasTexture.colorSpace = SRGBColorSpace;
94
- return canvasTexture;
95
- }
96
- let texture = this.textureMap.get(src);
97
- if (!texture) {
98
- if (/\.(mp4|mov)$/i.test(src)) {
99
- const videoDom = document.createElement("video");
100
- videoDom.autoplay = true;
101
- videoDom.loop = true;
102
- videoDom.muted = true;
103
- videoDom.preload = "auto";
104
- videoDom.playsInline = true;
105
- videoDom.src = src;
106
- videoDom.crossOrigin = "anonymous";
107
- videoDom.play();
108
- const firstOptoin = () => {
109
- videoDom.pause();
110
- videoDom.play();
111
- document.removeEventListener("pointerup", firstOptoin);
112
- document.removeEventListener("touchstart", firstOptoin);
113
- };
114
- document.addEventListener("pointerup", firstOptoin);
115
- document.addEventListener("touchstart", firstOptoin);
116
- texture = new VideoTexture(videoDom);
117
- }
118
- else {
119
- texture = this.textureLoader.load(src, (tex) => {
120
- tex.image.crossOrigin = "anonymous";
121
- });
122
- }
123
- this.textureMap.set(src, texture);
124
- }
125
- texture.wrapS = texture.wrapT = RepeatWrapping;
126
- texture.anisotropy = this.anisotropy;
127
- texture.colorSpace = SRGBColorSpace;
128
- return texture;
129
- }
130
- // 资源列表
131
- _blobs = {};
132
- // fbx模型加载器
133
- fbxLoader = new FBXLoader();
134
- //obj 加载器
135
- objLoader = new OBJLoader();
136
- // stl 加载器
137
- stlLoader = new STLLoader();
138
- //ply加载器
139
- plyLoader = new PLYLoader();
140
- // 材质加载器
141
- mtlLoader = new MTLLoader();
142
- // gltf 加载器
143
- gltfLoader = (() => {
144
- const loader = new GLTFLoader();
145
- const dracoLoader = new (class ThreeBaseDRACOLoader extends DRACOLoader {
146
- _loadLibrary(url, responseType) {
147
- //@ts-ignore
148
- let urlConf = process.env.NODE_ENV === "development"
149
- ? {
150
- "draco_decoder.js": "draco_decoder.js",
151
- "draco_wasm_wrapper.js": "draco_wasm_wrapper.js",
152
- "draco_decoder.wasm": "draco_decoder.wasm",
153
- }
154
- : {
155
- "draco_decoder.js": dracoWasmWrapperJS,
156
- "draco_wasm_wrapper.js": dracoDecoderJS,
157
- "draco_decoder.wasm": dracoDecoderWASM,
158
- };
159
- //@ts-ignore
160
- return super._loadLibrary(urlConf[url], responseType);
161
- }
162
- })();
163
- //@ts-ignore
164
- dracoLoader.setDecoderPath(process.env.NODE_ENV === "development"
165
- ? "/node_modules/three/examples/jsm/libs/draco/"
166
- : "");
167
- loader.setDRACOLoader(dracoLoader);
168
- return loader;
169
- })();
170
- //usdz加载器
171
- usdzLoader = new USDZLoader();
172
- //把贴图的 flipy 设置为 fasle
173
- setFlipYIsFalse(material) {
174
- if (material instanceof Array) {
175
- material.map((m) => this.setFlipYIsFalse(m));
176
- }
177
- else {
178
- const m = material;
179
- const parame = [
180
- "map",
181
- "lightMap",
182
- "aoMap",
183
- "emissiveMap",
184
- "bumpMap",
185
- "normalMap",
186
- "displacementMap",
187
- "alphaMap",
188
- "envMap",
189
- ];
190
- parame.map((param) => {
191
- const t = m[param];
192
- if (t) {
193
- t.flipY = false;
194
- }
195
- });
196
- }
197
- }
198
- /** 加载模型支持glb,gltf,fbx,stl,obj,usdz,dae,zip */
199
- async loadModel(url, filename = "") {
200
- if ((isString(url) || isArray(url)) &&
201
- isEmpty(url) &&
202
- isUndefined(url) &&
203
- isNull(url)) {
204
- throw new Error(`loadModel:无效的参数 url=${url}`);
205
- }
206
- // 本次需要销毁的bloburl,加载完成后就去销毁本次的文件
207
- const revokeObjectfile = [];
208
- // 获取模型文件的后缀
209
- const suffix = await (async () => {
210
- if (isString(url)) {
211
- return threeUtils.getSuffix(filename || url);
212
- }
213
- else if (url instanceof File) {
214
- return threeUtils.getSuffix(filename || url.name);
215
- }
216
- else if (url instanceof Array) {
217
- let suffix = "";
218
- const suffixs = [
219
- "zip",
220
- "obj",
221
- "glb",
222
- "gltf",
223
- "fbx",
224
- "dae",
225
- "stl",
226
- "ply",
227
- "usdz",
228
- ];
229
- for (let file of url) {
230
- suffix = threeUtils.getSuffix(file.name);
231
- if (suffixs.includes(suffix)) {
232
- break;
233
- }
234
- }
235
- return threeUtils.getSuffix(filename || suffix);
236
- }
237
- })();
238
- //获取模型文件的地址
239
- const modelurl = await (async () => {
240
- if (isString(url)) {
241
- return url;
242
- }
243
- else if (url instanceof File) {
244
- const bloburl = await threeUtils.fileToBloburl(url);
245
- this._blobs[url.name] = bloburl;
246
- revokeObjectfile.push(url.name);
247
- return bloburl;
248
- }
249
- else if (url instanceof Array) {
250
- let modelurl = "";
251
- const suffixs = [
252
- "zip",
253
- "obj",
254
- "glb",
255
- "gltf",
256
- "fbx",
257
- "dae",
258
- "stl",
259
- "usdz",
260
- ];
261
- for (let file of url) {
262
- const bloburl = await threeUtils.fileToBloburl(file);
263
- this._blobs[file.name] = bloburl;
264
- revokeObjectfile.push(file.name);
265
- if (suffixs.includes(threeUtils.getSuffix(file.name))) {
266
- modelurl = file.name;
267
- }
268
- }
269
- return modelurl;
270
- }
271
- })();
272
- switch (suffix) {
273
- case "zip":
274
- const JSZip = (await import("jszip")).default;
275
- let arrayBuffer = isString(url)
276
- ? await axios
277
- .get(url, {
278
- responseType: "arraybuffer",
279
- })
280
- .then((res) => res.data)
281
- : url instanceof File
282
- ? url
283
- : url[0];
284
- return await JSZip.loadAsync(arrayBuffer).then(async (zip) => {
285
- for (let filename in zip.files) {
286
- if (!zip.files[filename].dir) {
287
- // 不读文件夹
288
- await zip
289
- .file(filename)
290
- .async("blob")
291
- .then((blob) => {
292
- var blobURL = URL.createObjectURL(blob);
293
- this._blobs[filename] = blobURL;
294
- revokeObjectfile.push(filename);
295
- });
296
- }
297
- }
298
- let objLoader = new OBJLoader();
299
- // 如果有mtl文件就加载mtl文件
300
- let mtl = Object.keys(zip.files).find((filename) => /\.mtl$/.test(filename));
301
- if (mtl) {
302
- const materials = await this.mtlLoader.loadAsync(mtl);
303
- objLoader.setMaterials(materials);
304
- }
305
- else {
306
- //@ts-ignore
307
- objLoader.materials = null;
308
- }
309
- //如果有obj就加载obj模型
310
- let obj = Object.keys(zip.files).find((filename) => /\.obj$/.test(filename));
311
- if (obj) {
312
- const objObject3d = await objLoader.loadAsync(obj);
313
- revokeObjectfile.map((filename) => {
314
- URL.revokeObjectURL(this._blobs[filename]);
315
- delete this._blobs[filename];
316
- });
317
- objObject3d.traverse((obj) => {
318
- const mesh = obj;
319
- if (mesh.isMesh) {
320
- // 如果obj没有材质就给默认的材质
321
- if (!objLoader.materials) {
322
- mesh.material = new MeshStandardMaterial({
323
- metalness: 0.5,
324
- roughness: 0.5,
325
- });
326
- }
327
- const uv = mesh.geometry.getAttribute("uv");
328
- if (uv) {
329
- for (let i = 0; i < uv.count; i++) {
330
- uv.setY(i, 1 - uv.getY(i));
331
- }
332
- }
333
- // obj只翻转uv不翻转贴图
334
- this.setFlipYIsFalse(mesh.material);
335
- }
336
- });
337
- return objObject3d;
338
- }
339
- //如果有gltf文件就加载gltf文件
340
- let gltf = Object.keys(zip.files).find((filename) => /\.(gltf|glb)$/.test(filename));
341
- if (gltf) {
342
- return await this.loadModel(gltf);
343
- }
344
- //如果有dae文件就加载dae文件
345
- let dae = Object.keys(zip.files).find((filename) => /\.dae$/.test(filename));
346
- if (dae) {
347
- return await this.loadModel(dae);
348
- }
349
- //如果有fbx文件就加载fbx文件
350
- let fbx = Object.keys(zip.files).find((filename) => /\.fbx$/.test(filename));
351
- if (fbx) {
352
- return await this.loadModel(fbx);
353
- }
354
- //如果有usdz文件就加载usdz文件
355
- let usdz = Object.keys(zip.files).find((filename) => /\.usdz$/.test(filename));
356
- if (usdz) {
357
- return await this.loadModel(usdz);
358
- }
359
- //如果有stl文件就加载stl文件
360
- let stl = Object.keys(zip.files).find((filename) => /\.stl$/.test(filename));
361
- if (stl) {
362
- return await this.loadModel(stl);
363
- }
364
- //如果有ply文件就加载ply文件
365
- let ply = Object.keys(zip.files).find((filename) => /\.ply$/.test(filename));
366
- if (ply) {
367
- return await this.loadModel(ply);
368
- }
369
- //如果有zip文件就加载zip文件
370
- let zipFile = Object.keys(zip.files).find((filename) => /\.zip$/.test(filename));
371
- if (zipFile) {
372
- return await this.loadModel(zipFile);
373
- }
374
- if ([obj, gltf, dae, fbx, usdz, stl, ply].every((v) => !v)) {
375
- throw new Error(`${url}压缩包里没有obj.glb.gltf.dae.fbx.usde.stl,ply模型文件`);
376
- }
377
- });
378
- case "fbx":
379
- return await threeLoader.fbxLoader
380
- .loadAsync(modelurl)
381
- .then((res) => {
382
- revokeObjectfile.map((filename) => {
383
- URL.revokeObjectURL(this._blobs[filename]);
384
- delete this._blobs[filename];
385
- });
386
- return res;
387
- })
388
- .catch(() => {
389
- console.log(`${url},加载失败`);
390
- });
391
- case "gltf":
392
- case "glb":
393
- return await threeLoader.gltfLoader
394
- .loadAsync(modelurl)
395
- .then((res) => {
396
- revokeObjectfile.map((filename) => {
397
- URL.revokeObjectURL(this._blobs[filename]);
398
- delete this._blobs[filename];
399
- });
400
- res.scene.animations = res.animations;
401
- return res.scene;
402
- })
403
- .catch(() => {
404
- console.log(`${url},加载失败`);
405
- });
406
- case "stl":
407
- return await threeLoader.stlLoader
408
- .loadAsync(modelurl)
409
- .then((geometry) => {
410
- revokeObjectfile.map((filename) => {
411
- URL.revokeObjectURL(this._blobs[filename]);
412
- delete this._blobs[filename];
413
- });
414
- return new Mesh(geometry, new MeshStandardMaterial({
415
- metalness: 0.5,
416
- roughness: 0.5,
417
- }));
418
- })
419
- .catch(() => {
420
- console.log(`${url},加载失败`);
421
- });
422
- case "ply":
423
- return await threeLoader.plyLoader
424
- .loadAsync(modelurl)
425
- .then(async (geometry) => {
426
- revokeObjectfile.map((filename) => {
427
- URL.revokeObjectURL(this._blobs[filename]);
428
- delete this._blobs[filename];
429
- });
430
- // 没有法线就创建法线
431
- if (!geometry.getAttribute("normal")) {
432
- geometry.computeVertexNormals();
433
- }
434
- //是否需要绕x轴旋转
435
- const plyNeedRotationX = await new Promise(async (resolve) => {
436
- // 读取文件内容
437
- const res = await axios.get(modelurl);
438
- // if (res.match(/\b(Blender|binary_little_endian)\b/)) {
439
- // resolve(true);
440
- // } else {
441
- // resolve(false);
442
- // }
443
- });
444
- const plyMesh = new Mesh(geometry, new MeshStandardMaterial({
445
- metalness: 0.5,
446
- roughness: 0.5,
447
- vertexColors: Boolean(geometry.getAttribute("color")),
448
- }));
449
- if (plyNeedRotationX) {
450
- plyMesh.rotation.x = -Math.PI / 2;
451
- }
452
- return plyMesh;
453
- })
454
- .catch(() => {
455
- console.log(`${url},加载失败`);
456
- });
457
- case "usdz":
458
- return await threeLoader.usdzLoader
459
- .loadAsync(modelurl)
460
- .then((res) => {
461
- revokeObjectfile.map((filename) => {
462
- URL.revokeObjectURL(this._blobs[filename]);
463
- delete this._blobs[filename];
464
- });
465
- res.traverse((obj) => {
466
- const mesh = obj;
467
- if (mesh.isMesh) {
468
- mesh.material.transparent = true;
469
- }
470
- });
471
- return res;
472
- })
473
- .catch((err) => {
474
- console.log(`${url},加载失败`, err);
475
- });
476
- case "obj":
477
- let objLoader = new OBJLoader();
478
- // 如果有mtl文件就加载mtl文件
479
- let mtl = revokeObjectfile.find((filename) => /\.mtl$/.test(filename));
480
- if (mtl) {
481
- const materials = await this.mtlLoader.loadAsync(mtl);
482
- objLoader.setMaterials(materials);
483
- }
484
- const objObject3d = await objLoader.loadAsync(modelurl);
485
- revokeObjectfile.map((filename) => {
486
- URL.revokeObjectURL(this._blobs[filename]);
487
- delete this._blobs[filename];
488
- });
489
- objObject3d.traverse((obj) => {
490
- const mesh = obj;
491
- if (mesh.isMesh) {
492
- // 如果obj没有材质就给默认的材质
493
- if (!this.objLoader.materials) {
494
- mesh.material = new MeshStandardMaterial({
495
- metalness: 0.5,
496
- roughness: 0.5,
497
- });
498
- }
499
- const uv = mesh.geometry.getAttribute("uv");
500
- if (uv) {
501
- for (let i = 0; i < uv.count; i++) {
502
- uv.setY(i, 1 - uv.getY(i));
503
- }
504
- }
505
- // obj只翻转uv不翻转贴图
506
- this.setFlipYIsFalse(mesh.material);
507
- }
508
- });
509
- return objObject3d;
510
- default:
511
- revokeObjectfile.map((filename) => {
512
- URL.revokeObjectURL(this._blobs[filename]);
513
- delete this._blobs[filename];
514
- });
515
- if (suffix) {
516
- console.log(`${suffix},不支持的格式`);
517
- }
518
- else {
519
- console.log(`${url},不支持的格式,请尝试使用 suffix 参数来指定一下格式`);
520
- }
521
- }
522
- }
523
- // 是否加载完成
524
- onload = true;
525
- }
526
- const threeLoader = new ThreeLoader();
527
- export default threeLoader;
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2022-04-07 16:42:26
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2025-01-15 20:34:19
6
+ */
7
+ import { CanvasTexture, DefaultLoadingManager, EventDispatcher, Mesh, MeshStandardMaterial, RepeatWrapping, SRGBColorSpace, TextureLoader, VideoTexture, } from "three";
8
+ import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
9
+ import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
10
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
11
+ import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader";
12
+ import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
13
+ import { STLLoader } from "three/examples/jsm/loaders/STLLoader";
14
+ import { USDZLoader } from "three/examples/jsm/loaders/USDZLoader";
15
+ // import { IFCLoader } from 'web-ifc-three/dist/IFCLoader';
16
+ // import { IFCLoader } from 'web-ifc-three';
17
+ // import { IFCSPACE } from 'web-ifc';
18
+ // 引入静态资源
19
+ //@ts-ignore
20
+ import dracoWasmWrapperJS from "./lib/draco/draco_wasm_wrapper.js?url";
21
+ //@ts-ignore
22
+ import dracoDecoderJS from "./lib/draco/draco_decoder.js?url";
23
+ //@ts-ignore
24
+ import axios from "axios";
25
+ import { isArray, isEmpty, isNull, isString, isUndefined } from "lodash";
26
+ import { generateUUID } from "three/src/math/MathUtils";
27
+ import dracoDecoderWASM from "./lib/draco/draco_decoder.wasm?url";
28
+ import threeUtils from "./threeUtils";
29
+ import { PLYLoader, TGALoader } from "three/examples/jsm/Addons.js";
30
+ DefaultLoadingManager.addHandler(/tga$/, new TGALoader());
31
+ // 加载器
32
+ class ThreeLoader extends EventDispatcher {
33
+ constructor() {
34
+ super();
35
+ DefaultLoadingManager.onStart = () => {
36
+ this.onload = false;
37
+ };
38
+ // 有资源加载完成时通知渲染器
39
+ DefaultLoadingManager.onProgress = (url, loaded, total) => {
40
+ if (loaded < total) {
41
+ this.dispatchEvent({ type: "loadding" });
42
+ }
43
+ else if (loaded === total) {
44
+ this.dispatchEvent({ type: "onLoad" });
45
+ }
46
+ };
47
+ DefaultLoadingManager.onLoad = () => {
48
+ this.onload = true;
49
+ };
50
+ // 资源替换
51
+ DefaultLoadingManager.setURLModifier((url) => {
52
+ //全路径匹配资源
53
+ if (this._blobs[url]) {
54
+ return this._blobs[url];
55
+ }
56
+ const filename = url
57
+ .replace(`blob:${location.origin}/`, "")
58
+ .replace(/^\.\//, "");
59
+ let new_url = this._blobs[filename] || url;
60
+ //如果全匹配没有匹配到资源,就尝试文件名匹配
61
+ if (!this._blobs[filename]) {
62
+ const blobKey = Object.keys(this._blobs).find((key) => this.getFilename(key) === this.getFilename(filename)) || "";
63
+ new_url = this._blobs[blobKey] || url;
64
+ }
65
+ return new_url;
66
+ });
67
+ }
68
+ // 从url 中提取文件名
69
+ getFilename(url) {
70
+ // 移除URL的查询字符串和哈希部分
71
+ const path = url.split("?")[0].split("#")[0];
72
+ // 使用lastIndexOf和substring从路径中提取文件名
73
+ const filename = path.substring(path.lastIndexOf("/") + 1);
74
+ return filename;
75
+ }
76
+ uuid = generateUUID();
77
+ // 各向异性
78
+ anisotropy = 16;
79
+ // 设置各向异性 由渲染器来调用并设置
80
+ setAnisotropy(v) {
81
+ this.anisotropy = v;
82
+ }
83
+ // 纹理对向缓存
84
+ textureMap = new Map();
85
+ // 纹理加载器
86
+ textureLoader = new TextureLoader();
87
+ // 获取地址对应的单例纹理对象
88
+ getTexture(src) {
89
+ if (src instanceof HTMLCanvasElement) {
90
+ const canvasTexture = new CanvasTexture(src);
91
+ canvasTexture.wrapS = canvasTexture.wrapT = RepeatWrapping;
92
+ canvasTexture.anisotropy = this.anisotropy;
93
+ canvasTexture.colorSpace = SRGBColorSpace;
94
+ return canvasTexture;
95
+ }
96
+ let texture = this.textureMap.get(src);
97
+ if (!texture) {
98
+ if (/\.(mp4|mov)$/i.test(src)) {
99
+ const videoDom = document.createElement("video");
100
+ videoDom.autoplay = true;
101
+ videoDom.loop = true;
102
+ videoDom.muted = true;
103
+ videoDom.preload = "auto";
104
+ videoDom.playsInline = true;
105
+ videoDom.src = src;
106
+ videoDom.crossOrigin = "anonymous";
107
+ videoDom.play();
108
+ const firstOptoin = () => {
109
+ videoDom.pause();
110
+ videoDom.play();
111
+ document.removeEventListener("pointerup", firstOptoin);
112
+ document.removeEventListener("touchstart", firstOptoin);
113
+ };
114
+ document.addEventListener("pointerup", firstOptoin);
115
+ document.addEventListener("touchstart", firstOptoin);
116
+ texture = new VideoTexture(videoDom);
117
+ }
118
+ else {
119
+ texture = this.textureLoader.load(src, (tex) => {
120
+ tex.image.crossOrigin = "anonymous";
121
+ });
122
+ }
123
+ this.textureMap.set(src, texture);
124
+ }
125
+ texture.wrapS = texture.wrapT = RepeatWrapping;
126
+ texture.anisotropy = this.anisotropy;
127
+ texture.colorSpace = SRGBColorSpace;
128
+ return texture;
129
+ }
130
+ // 资源列表
131
+ _blobs = {};
132
+ // fbx模型加载器
133
+ fbxLoader = new FBXLoader();
134
+ //obj 加载器
135
+ objLoader = new OBJLoader();
136
+ // stl 加载器
137
+ stlLoader = new STLLoader();
138
+ //ply加载器
139
+ plyLoader = new PLYLoader();
140
+ // 材质加载器
141
+ mtlLoader = new MTLLoader();
142
+ // gltf 加载器
143
+ gltfLoader = (() => {
144
+ const loader = new GLTFLoader();
145
+ const dracoLoader = new (class ThreeBaseDRACOLoader extends DRACOLoader {
146
+ _loadLibrary(url, responseType) {
147
+ //@ts-ignore
148
+ let urlConf = process.env.NODE_ENV === "development"
149
+ ? {
150
+ "draco_decoder.js": "draco_decoder.js",
151
+ "draco_wasm_wrapper.js": "draco_wasm_wrapper.js",
152
+ "draco_decoder.wasm": "draco_decoder.wasm",
153
+ }
154
+ : {
155
+ "draco_decoder.js": dracoWasmWrapperJS,
156
+ "draco_wasm_wrapper.js": dracoDecoderJS,
157
+ "draco_decoder.wasm": dracoDecoderWASM,
158
+ };
159
+ //@ts-ignore
160
+ return super._loadLibrary(urlConf[url], responseType);
161
+ }
162
+ })();
163
+ //@ts-ignore
164
+ dracoLoader.setDecoderPath(process.env.NODE_ENV === "development"
165
+ ? "/node_modules/three/examples/jsm/libs/draco/"
166
+ : "");
167
+ loader.setDRACOLoader(dracoLoader);
168
+ return loader;
169
+ })();
170
+ //usdz加载器
171
+ usdzLoader = new USDZLoader();
172
+ //把贴图的 flipy 设置为 fasle
173
+ setFlipYIsFalse(material) {
174
+ if (material instanceof Array) {
175
+ material.map((m) => this.setFlipYIsFalse(m));
176
+ }
177
+ else {
178
+ const m = material;
179
+ const parame = [
180
+ "map",
181
+ "lightMap",
182
+ "aoMap",
183
+ "emissiveMap",
184
+ "bumpMap",
185
+ "normalMap",
186
+ "displacementMap",
187
+ "alphaMap",
188
+ "envMap",
189
+ ];
190
+ parame.map((param) => {
191
+ const t = m[param];
192
+ if (t) {
193
+ t.flipY = false;
194
+ }
195
+ });
196
+ }
197
+ }
198
+ /** 加载模型支持glb,gltf,fbx,stl,obj,usdz,dae,zip */
199
+ async loadModel(url, filename = "") {
200
+ if ((isString(url) || isArray(url)) &&
201
+ isEmpty(url) &&
202
+ isUndefined(url) &&
203
+ isNull(url)) {
204
+ throw new Error(`loadModel:无效的参数 url=${url}`);
205
+ }
206
+ // 本次需要销毁的bloburl,加载完成后就去销毁本次的文件
207
+ const revokeObjectfile = [];
208
+ // 获取模型文件的后缀
209
+ const suffix = await (async () => {
210
+ if (isString(url)) {
211
+ return threeUtils.getSuffix(filename || url);
212
+ }
213
+ else if (url instanceof File) {
214
+ return threeUtils.getSuffix(filename || url.name);
215
+ }
216
+ else if (url instanceof Array) {
217
+ let suffix = "";
218
+ const suffixs = [
219
+ "zip",
220
+ "obj",
221
+ "glb",
222
+ "gltf",
223
+ "fbx",
224
+ "dae",
225
+ "stl",
226
+ "ply",
227
+ "usdz",
228
+ ];
229
+ for (let file of url) {
230
+ suffix = threeUtils.getSuffix(file.name);
231
+ if (suffixs.includes(suffix)) {
232
+ break;
233
+ }
234
+ }
235
+ return threeUtils.getSuffix(filename || suffix);
236
+ }
237
+ })();
238
+ //获取模型文件的地址
239
+ const modelurl = await (async () => {
240
+ if (isString(url)) {
241
+ return url;
242
+ }
243
+ else if (url instanceof File) {
244
+ const bloburl = await threeUtils.fileToBloburl(url);
245
+ this._blobs[url.name] = bloburl;
246
+ revokeObjectfile.push(url.name);
247
+ return bloburl;
248
+ }
249
+ else if (url instanceof Array) {
250
+ let modelurl = "";
251
+ const suffixs = [
252
+ "zip",
253
+ "obj",
254
+ "glb",
255
+ "gltf",
256
+ "fbx",
257
+ "dae",
258
+ "stl",
259
+ "usdz",
260
+ ];
261
+ for (let file of url) {
262
+ const bloburl = await threeUtils.fileToBloburl(file);
263
+ this._blobs[file.name] = bloburl;
264
+ revokeObjectfile.push(file.name);
265
+ if (suffixs.includes(threeUtils.getSuffix(file.name))) {
266
+ modelurl = file.name;
267
+ }
268
+ }
269
+ return modelurl;
270
+ }
271
+ })();
272
+ switch (suffix) {
273
+ case "zip":
274
+ const JSZip = (await import("jszip")).default;
275
+ let arrayBuffer = isString(url)
276
+ ? await axios
277
+ .get(url, {
278
+ responseType: "arraybuffer",
279
+ })
280
+ .then((res) => res.data)
281
+ : url instanceof File
282
+ ? url
283
+ : url[0];
284
+ return await JSZip.loadAsync(arrayBuffer).then(async (zip) => {
285
+ for (let filename in zip.files) {
286
+ if (!zip.files[filename].dir) {
287
+ // 不读文件夹
288
+ await zip
289
+ .file(filename)
290
+ .async("blob")
291
+ .then((blob) => {
292
+ var blobURL = URL.createObjectURL(blob);
293
+ this._blobs[filename] = blobURL;
294
+ revokeObjectfile.push(filename);
295
+ });
296
+ }
297
+ }
298
+ let objLoader = new OBJLoader();
299
+ // 如果有mtl文件就加载mtl文件
300
+ let mtl = Object.keys(zip.files).find((filename) => /\.mtl$/.test(filename));
301
+ if (mtl) {
302
+ const materials = await this.mtlLoader.loadAsync(mtl);
303
+ objLoader.setMaterials(materials);
304
+ }
305
+ else {
306
+ //@ts-ignore
307
+ objLoader.materials = null;
308
+ }
309
+ //如果有obj就加载obj模型
310
+ let obj = Object.keys(zip.files).find((filename) => /\.obj$/.test(filename));
311
+ if (obj) {
312
+ const objObject3d = await objLoader.loadAsync(obj);
313
+ revokeObjectfile.map((filename) => {
314
+ URL.revokeObjectURL(this._blobs[filename]);
315
+ delete this._blobs[filename];
316
+ });
317
+ objObject3d.traverse((obj) => {
318
+ const mesh = obj;
319
+ if (mesh.isMesh) {
320
+ // 如果obj没有材质就给默认的材质
321
+ if (!objLoader.materials) {
322
+ mesh.material = new MeshStandardMaterial({
323
+ metalness: 0.5,
324
+ roughness: 0.5,
325
+ });
326
+ }
327
+ const uv = mesh.geometry.getAttribute("uv");
328
+ if (uv) {
329
+ for (let i = 0; i < uv.count; i++) {
330
+ uv.setY(i, 1 - uv.getY(i));
331
+ }
332
+ }
333
+ // obj只翻转uv不翻转贴图
334
+ this.setFlipYIsFalse(mesh.material);
335
+ }
336
+ });
337
+ return objObject3d;
338
+ }
339
+ //如果有gltf文件就加载gltf文件
340
+ let gltf = Object.keys(zip.files).find((filename) => /\.(gltf|glb)$/.test(filename));
341
+ if (gltf) {
342
+ return await this.loadModel(gltf);
343
+ }
344
+ //如果有dae文件就加载dae文件
345
+ let dae = Object.keys(zip.files).find((filename) => /\.dae$/.test(filename));
346
+ if (dae) {
347
+ return await this.loadModel(dae);
348
+ }
349
+ //如果有fbx文件就加载fbx文件
350
+ let fbx = Object.keys(zip.files).find((filename) => /\.fbx$/.test(filename));
351
+ if (fbx) {
352
+ return await this.loadModel(fbx);
353
+ }
354
+ //如果有usdz文件就加载usdz文件
355
+ let usdz = Object.keys(zip.files).find((filename) => /\.usdz$/.test(filename));
356
+ if (usdz) {
357
+ return await this.loadModel(usdz);
358
+ }
359
+ //如果有stl文件就加载stl文件
360
+ let stl = Object.keys(zip.files).find((filename) => /\.stl$/.test(filename));
361
+ if (stl) {
362
+ return await this.loadModel(stl);
363
+ }
364
+ //如果有ply文件就加载ply文件
365
+ let ply = Object.keys(zip.files).find((filename) => /\.ply$/.test(filename));
366
+ if (ply) {
367
+ return await this.loadModel(ply);
368
+ }
369
+ //如果有zip文件就加载zip文件
370
+ let zipFile = Object.keys(zip.files).find((filename) => /\.zip$/.test(filename));
371
+ if (zipFile) {
372
+ return await this.loadModel(zipFile);
373
+ }
374
+ if ([obj, gltf, dae, fbx, usdz, stl, ply].every((v) => !v)) {
375
+ throw new Error(`${url}压缩包里没有obj.glb.gltf.dae.fbx.usde.stl,ply模型文件`);
376
+ }
377
+ });
378
+ case "fbx":
379
+ return await threeLoader.fbxLoader
380
+ .loadAsync(modelurl)
381
+ .then((res) => {
382
+ revokeObjectfile.map((filename) => {
383
+ URL.revokeObjectURL(this._blobs[filename]);
384
+ delete this._blobs[filename];
385
+ });
386
+ return res;
387
+ })
388
+ .catch(() => {
389
+ console.log(`${url},加载失败`);
390
+ });
391
+ case "gltf":
392
+ case "glb":
393
+ return await threeLoader.gltfLoader
394
+ .loadAsync(modelurl)
395
+ .then((res) => {
396
+ revokeObjectfile.map((filename) => {
397
+ URL.revokeObjectURL(this._blobs[filename]);
398
+ delete this._blobs[filename];
399
+ });
400
+ res.scene.animations = res.animations;
401
+ return res.scene;
402
+ })
403
+ .catch(() => {
404
+ console.log(`${url},加载失败`);
405
+ });
406
+ case "stl":
407
+ return await threeLoader.stlLoader
408
+ .loadAsync(modelurl)
409
+ .then((geometry) => {
410
+ revokeObjectfile.map((filename) => {
411
+ URL.revokeObjectURL(this._blobs[filename]);
412
+ delete this._blobs[filename];
413
+ });
414
+ return new Mesh(geometry, new MeshStandardMaterial({
415
+ metalness: 0.5,
416
+ roughness: 0.5,
417
+ }));
418
+ })
419
+ .catch(() => {
420
+ console.log(`${url},加载失败`);
421
+ });
422
+ case "ply":
423
+ return await threeLoader.plyLoader
424
+ .loadAsync(modelurl)
425
+ .then(async (geometry) => {
426
+ revokeObjectfile.map((filename) => {
427
+ URL.revokeObjectURL(this._blobs[filename]);
428
+ delete this._blobs[filename];
429
+ });
430
+ // 没有法线就创建法线
431
+ if (!geometry.getAttribute("normal")) {
432
+ geometry.computeVertexNormals();
433
+ }
434
+ //是否需要绕x轴旋转
435
+ const plyNeedRotationX = await new Promise(async (resolve) => {
436
+ // 读取文件内容
437
+ const res = await axios.get(modelurl);
438
+ // if (res.match(/\b(Blender|binary_little_endian)\b/)) {
439
+ // resolve(true);
440
+ // } else {
441
+ // resolve(false);
442
+ // }
443
+ });
444
+ const plyMesh = new Mesh(geometry, new MeshStandardMaterial({
445
+ metalness: 0.5,
446
+ roughness: 0.5,
447
+ vertexColors: Boolean(geometry.getAttribute("color")),
448
+ }));
449
+ if (plyNeedRotationX) {
450
+ plyMesh.rotation.x = -Math.PI / 2;
451
+ }
452
+ return plyMesh;
453
+ })
454
+ .catch(() => {
455
+ console.log(`${url},加载失败`);
456
+ });
457
+ case "usdz":
458
+ return await threeLoader.usdzLoader
459
+ .loadAsync(modelurl)
460
+ .then((res) => {
461
+ revokeObjectfile.map((filename) => {
462
+ URL.revokeObjectURL(this._blobs[filename]);
463
+ delete this._blobs[filename];
464
+ });
465
+ res.traverse((obj) => {
466
+ const mesh = obj;
467
+ if (mesh.isMesh) {
468
+ mesh.material.transparent = true;
469
+ }
470
+ });
471
+ return res;
472
+ })
473
+ .catch((err) => {
474
+ console.log(`${url},加载失败`, err);
475
+ });
476
+ case "obj":
477
+ let objLoader = new OBJLoader();
478
+ // 如果有mtl文件就加载mtl文件
479
+ let mtl = revokeObjectfile.find((filename) => /\.mtl$/.test(filename));
480
+ if (mtl) {
481
+ const materials = await this.mtlLoader.loadAsync(mtl);
482
+ objLoader.setMaterials(materials);
483
+ }
484
+ const objObject3d = await objLoader.loadAsync(modelurl);
485
+ revokeObjectfile.map((filename) => {
486
+ URL.revokeObjectURL(this._blobs[filename]);
487
+ delete this._blobs[filename];
488
+ });
489
+ objObject3d.traverse((obj) => {
490
+ const mesh = obj;
491
+ if (mesh.isMesh) {
492
+ // 如果obj没有材质就给默认的材质
493
+ if (!this.objLoader.materials) {
494
+ mesh.material = new MeshStandardMaterial({
495
+ metalness: 0.5,
496
+ roughness: 0.5,
497
+ });
498
+ }
499
+ const uv = mesh.geometry.getAttribute("uv");
500
+ if (uv) {
501
+ for (let i = 0; i < uv.count; i++) {
502
+ uv.setY(i, 1 - uv.getY(i));
503
+ }
504
+ }
505
+ // obj只翻转uv不翻转贴图
506
+ this.setFlipYIsFalse(mesh.material);
507
+ }
508
+ });
509
+ return objObject3d;
510
+ default:
511
+ revokeObjectfile.map((filename) => {
512
+ URL.revokeObjectURL(this._blobs[filename]);
513
+ delete this._blobs[filename];
514
+ });
515
+ if (suffix) {
516
+ console.log(`${suffix},不支持的格式`);
517
+ }
518
+ else {
519
+ console.log(`${url},不支持的格式,请尝试使用 suffix 参数来指定一下格式`);
520
+ }
521
+ }
522
+ }
523
+ // 是否加载完成
524
+ onload = true;
525
+ }
526
+ const threeLoader = new ThreeLoader();
527
+ export default threeLoader;