three-base 2.0.27 → 2.0.29
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/MeshBase.d.ts +15 -6
- package/MeshBase.js +57 -38
- package/MeshBase.ts +67 -42
- package/ThreeBase.js +6 -1
- package/ThreeBase.ts +7 -1
- package/package.json +1 -1
- package/threeLoader.js +7 -2
- package/threeLoader.ts +6 -2
package/MeshBase.d.ts
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import { Group, Mesh, Object3D, Vector3 } from "three";
|
|
1
|
+
import { AnimationAction, AnimationMixer, Clock, Group, Mesh, Object3D, Vector3 } from "three";
|
|
2
2
|
export default class MeshBase extends Group {
|
|
3
3
|
constructor(url?: string);
|
|
4
4
|
static models: Map<string, Object3D>;
|
|
5
5
|
static loadModeling: Map<string, boolean>;
|
|
6
6
|
loaded: boolean;
|
|
7
|
+
hasAnimation: boolean;
|
|
8
|
+
mixer?: AnimationMixer;
|
|
9
|
+
animationActions: {
|
|
10
|
+
[key: string]: AnimationAction;
|
|
11
|
+
};
|
|
7
12
|
loadModel(url: string): Promise<void>;
|
|
8
13
|
frustumCulled: boolean;
|
|
9
14
|
castShadow: boolean;
|
|
10
15
|
receiveShadow: boolean;
|
|
16
|
+
awaitLoaded(callback?: Function): Promise<void>;
|
|
11
17
|
traverseMesh(callback: (mesh: Mesh) => void): Promise<void>;
|
|
18
|
+
clock: Clock;
|
|
19
|
+
setClipActionWeight(weights?: {
|
|
20
|
+
[key: string]: number;
|
|
21
|
+
}): Promise<void>;
|
|
12
22
|
updateMatrix(): void;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
scaleTo(size?: number, key?: Array<"x" | "y" | "z">): void;
|
|
23
|
+
getSize(): Promise<Vector3>;
|
|
24
|
+
center(): Promise<void>;
|
|
25
|
+
alignBottom(y?: number): Promise<void>;
|
|
26
|
+
scaleTo(size?: number, key?: Array<"x" | "y" | "z">): Promise<void>;
|
|
18
27
|
expandBynormal(length: number): void;
|
|
19
28
|
}
|
package/MeshBase.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnimationMixer, Clock, Group, Object3D, Vector3 } from "three";
|
|
2
2
|
import threeUtils from "./threeUtils";
|
|
3
3
|
import threeLoader from "./threeLoader";
|
|
4
|
+
import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils';
|
|
4
5
|
// 模型基类
|
|
5
6
|
export default class MeshBase extends Group {
|
|
6
7
|
constructor(url) {
|
|
@@ -13,6 +14,12 @@ export default class MeshBase extends Group {
|
|
|
13
14
|
static loadModeling = new Map();
|
|
14
15
|
// 加载完成
|
|
15
16
|
loaded = false;
|
|
17
|
+
// 是否有动画
|
|
18
|
+
hasAnimation = false;
|
|
19
|
+
//动画混合器
|
|
20
|
+
mixer;
|
|
21
|
+
//动画剪辑对象数组
|
|
22
|
+
animationActions = {};
|
|
16
23
|
async loadModel(url) {
|
|
17
24
|
if (!url) {
|
|
18
25
|
this.clear();
|
|
@@ -33,6 +40,7 @@ export default class MeshBase extends Group {
|
|
|
33
40
|
else if (/\.(gltf|glb)$/i.test(url)) {
|
|
34
41
|
await threeLoader.gltfLoader.loadAsync(url).then(res => {
|
|
35
42
|
modal = res.scene;
|
|
43
|
+
modal.animations = res.animations;
|
|
36
44
|
}).catch(() => {
|
|
37
45
|
console.error(`${url},加载失败`);
|
|
38
46
|
});
|
|
@@ -43,29 +51,32 @@ export default class MeshBase extends Group {
|
|
|
43
51
|
threeUtils.center(modal);
|
|
44
52
|
threeUtils.alignBottom(modal);
|
|
45
53
|
modal.updateMatrixWorld(true);
|
|
46
|
-
// 应用全部变换
|
|
47
|
-
modal.traverse(obj => {
|
|
48
|
-
const mesh = obj;
|
|
49
|
-
mesh.position.set(0, 0, 0);
|
|
50
|
-
mesh.rotation.set(0, 0, 0);
|
|
51
|
-
mesh.scale.set(1, 1, 1);
|
|
52
|
-
if (mesh.isMesh) {
|
|
53
|
-
mesh.geometry.applyMatrix4(mesh.matrixWorld);
|
|
54
|
-
mesh.material.side = DoubleSide;
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
54
|
MeshBase.models.set(url, modal);
|
|
58
55
|
MeshBase.loadModeling.set(url, false);
|
|
59
56
|
}
|
|
60
57
|
// 如果模型还在加载中, 就等待模型加载完成
|
|
61
58
|
do {
|
|
62
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
59
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
63
60
|
} while (MeshBase.loadModeling.get(url));
|
|
64
61
|
if (MeshBase.models.get(url)) {
|
|
65
|
-
const mesh = MeshBase.models.get(url).clone();
|
|
66
62
|
this.clear();
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
const mesh = MeshBase.models.get(url);
|
|
64
|
+
// 判断是否有动画
|
|
65
|
+
this.hasAnimation = mesh.animations.length > 0;
|
|
66
|
+
if (this.hasAnimation) {
|
|
67
|
+
const model = SkeletonUtils.clone(mesh);
|
|
68
|
+
this.mixer = new AnimationMixer(model);
|
|
69
|
+
this.animationActions = {};
|
|
70
|
+
mesh.animations.map(animation => {
|
|
71
|
+
const animationAction = this.mixer.clipAction(animation).play();
|
|
72
|
+
animationAction.weight = 0;
|
|
73
|
+
this.animationActions[animation.name] = animationAction;
|
|
74
|
+
});
|
|
75
|
+
this.add(model);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.add(mesh);
|
|
79
|
+
}
|
|
69
80
|
}
|
|
70
81
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
71
82
|
// 加载完成事件
|
|
@@ -78,11 +89,17 @@ export default class MeshBase extends Group {
|
|
|
78
89
|
castShadow = false;
|
|
79
90
|
// 接受阴影
|
|
80
91
|
receiveShadow = false;
|
|
81
|
-
|
|
82
|
-
async
|
|
92
|
+
// 等待加载加载完成
|
|
93
|
+
async awaitLoaded(callback) {
|
|
83
94
|
while (!this.loaded) {
|
|
84
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
95
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
85
96
|
}
|
|
97
|
+
callback && callback();
|
|
98
|
+
}
|
|
99
|
+
//遍历模型
|
|
100
|
+
async traverseMesh(callback) {
|
|
101
|
+
// 等待加载完成
|
|
102
|
+
await this.awaitLoaded();
|
|
86
103
|
this.traverse(obj => {
|
|
87
104
|
const mesh = obj;
|
|
88
105
|
if (mesh.isMesh) {
|
|
@@ -90,7 +107,20 @@ export default class MeshBase extends Group {
|
|
|
90
107
|
}
|
|
91
108
|
});
|
|
92
109
|
}
|
|
110
|
+
clock = new Clock();
|
|
111
|
+
//设置动画的权重
|
|
112
|
+
async setClipActionWeight(weights = {}) {
|
|
113
|
+
// 等待加载完成
|
|
114
|
+
await this.awaitLoaded();
|
|
115
|
+
for (let key in this.animationActions) {
|
|
116
|
+
this.animationActions[key].weight = weights[key] || 0;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
93
119
|
updateMatrix() {
|
|
120
|
+
//播放动画
|
|
121
|
+
if (this.mixer) {
|
|
122
|
+
this.mixer.update(this.clock.getDelta());
|
|
123
|
+
}
|
|
94
124
|
// 深度应用属性
|
|
95
125
|
this.traverse(obj => {
|
|
96
126
|
const mesh = obj;
|
|
@@ -102,35 +132,24 @@ export default class MeshBase extends Group {
|
|
|
102
132
|
});
|
|
103
133
|
super.updateMatrix();
|
|
104
134
|
}
|
|
105
|
-
// 应用全部变化
|
|
106
|
-
applyAllTranstion() {
|
|
107
|
-
this.updateMatrixWorld(true);
|
|
108
|
-
// 应用全部变换
|
|
109
|
-
this.traverse(obj => {
|
|
110
|
-
const mesh = obj;
|
|
111
|
-
mesh.position.set(0, 0, 0);
|
|
112
|
-
mesh.rotation.set(0, 0, 0);
|
|
113
|
-
mesh.scale.set(1, 1, 1);
|
|
114
|
-
if (mesh.isMesh) {
|
|
115
|
-
mesh.geometry.applyMatrix4(mesh.matrixWorld);
|
|
116
|
-
mesh.material.side = DoubleSide;
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
135
|
// 获取模型的尺寸
|
|
121
|
-
getSize() {
|
|
136
|
+
async getSize() {
|
|
137
|
+
await this.awaitLoaded();
|
|
122
138
|
return threeUtils.getSize(this);
|
|
123
139
|
}
|
|
124
140
|
// 居中
|
|
125
|
-
center() {
|
|
141
|
+
async center() {
|
|
142
|
+
await this.awaitLoaded();
|
|
126
143
|
threeUtils.center(this);
|
|
127
144
|
}
|
|
128
145
|
//底对齐
|
|
129
|
-
alignBottom(y = 0) {
|
|
146
|
+
async alignBottom(y = 0) {
|
|
147
|
+
await this.awaitLoaded();
|
|
130
148
|
threeUtils.alignBottom(this, y);
|
|
131
149
|
}
|
|
132
150
|
// 放大到指定尺寸
|
|
133
|
-
scaleTo(size = 1, key = ["x", "y", "z"]) {
|
|
151
|
+
async scaleTo(size = 1, key = ["x", "y", "z"]) {
|
|
152
|
+
await this.awaitLoaded();
|
|
134
153
|
threeUtils.scaleTo(this, size, key);
|
|
135
154
|
}
|
|
136
155
|
//按照法线拓展
|
package/MeshBase.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
import { DoubleSide, Group, Mesh, MeshStandardMaterial, Object3D, Vector3 } from "three";
|
|
2
|
+
import { AnimationAction, AnimationMixer, Clock, DoubleSide, Group, Mesh, MeshStandardMaterial, Object3D, Vector3 } from "three";
|
|
3
3
|
import threeUtils from "./threeUtils";
|
|
4
4
|
import threeLoader from "./threeLoader";
|
|
5
|
+
import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils';
|
|
5
6
|
|
|
6
7
|
// 模型基类
|
|
7
8
|
export default class MeshBase extends Group {
|
|
@@ -19,6 +20,15 @@ export default class MeshBase extends Group {
|
|
|
19
20
|
// 加载完成
|
|
20
21
|
loaded = false;
|
|
21
22
|
|
|
23
|
+
// 是否有动画
|
|
24
|
+
hasAnimation = false;
|
|
25
|
+
|
|
26
|
+
//动画混合器
|
|
27
|
+
mixer?: AnimationMixer;
|
|
28
|
+
|
|
29
|
+
//动画剪辑对象数组
|
|
30
|
+
animationActions: { [key: string]: AnimationAction } = {};
|
|
31
|
+
|
|
22
32
|
async loadModel(url: string) {
|
|
23
33
|
if (!url) {
|
|
24
34
|
this.clear();
|
|
@@ -42,6 +52,7 @@ export default class MeshBase extends Group {
|
|
|
42
52
|
} else if (/\.(gltf|glb)$/i.test(url)) {
|
|
43
53
|
await threeLoader.gltfLoader.loadAsync(url).then(res => {
|
|
44
54
|
modal = res.scene;
|
|
55
|
+
modal.animations = res.animations;
|
|
45
56
|
}).catch(() => {
|
|
46
57
|
console.error(`${url},加载失败`);
|
|
47
58
|
})
|
|
@@ -53,32 +64,38 @@ export default class MeshBase extends Group {
|
|
|
53
64
|
threeUtils.alignBottom(modal);
|
|
54
65
|
modal.updateMatrixWorld(true);
|
|
55
66
|
|
|
56
|
-
// 应用全部变换
|
|
57
|
-
modal.traverse(obj => {
|
|
58
|
-
const mesh = obj as Mesh;
|
|
59
|
-
mesh.position.set(0, 0, 0);
|
|
60
|
-
mesh.rotation.set(0, 0, 0);
|
|
61
|
-
mesh.scale.set(1, 1, 1);
|
|
62
|
-
if (mesh.isMesh) {
|
|
63
|
-
mesh.geometry.applyMatrix4(mesh.matrixWorld);
|
|
64
|
-
(mesh.material as MeshStandardMaterial).side = DoubleSide;
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
67
|
MeshBase.models.set(url, modal);
|
|
69
68
|
MeshBase.loadModeling.set(url, false);
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
// 如果模型还在加载中, 就等待模型加载完成
|
|
73
72
|
do {
|
|
74
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
75
74
|
} while (MeshBase.loadModeling.get(url));
|
|
76
75
|
|
|
77
76
|
if (MeshBase.models.get(url)) {
|
|
78
|
-
|
|
77
|
+
|
|
79
78
|
this.clear();
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
const mesh = MeshBase.models.get(url);
|
|
80
|
+
// 判断是否有动画
|
|
81
|
+
this.hasAnimation = mesh.animations.length > 0;
|
|
82
|
+
|
|
83
|
+
if (this.hasAnimation) {
|
|
84
|
+
const model = SkeletonUtils.clone(mesh);
|
|
85
|
+
this.mixer = new AnimationMixer(model);
|
|
86
|
+
|
|
87
|
+
this.animationActions = {};
|
|
88
|
+
mesh.animations.map(animation => {
|
|
89
|
+
const animationAction = this.mixer.clipAction(animation).play();
|
|
90
|
+
animationAction.weight = 0;
|
|
91
|
+
this.animationActions[animation.name] = animationAction;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
this.add(model);
|
|
95
|
+
} else {
|
|
96
|
+
this.add(mesh);
|
|
97
|
+
}
|
|
98
|
+
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
@@ -98,12 +115,18 @@ export default class MeshBase extends Group {
|
|
|
98
115
|
// 接受阴影
|
|
99
116
|
receiveShadow: boolean = false;
|
|
100
117
|
|
|
101
|
-
|
|
102
|
-
async
|
|
103
|
-
|
|
118
|
+
// 等待加载加载完成
|
|
119
|
+
async awaitLoaded(callback?: Function) {
|
|
104
120
|
while (!this.loaded) {
|
|
105
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
121
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
106
122
|
}
|
|
123
|
+
callback && callback();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//遍历模型
|
|
127
|
+
async traverseMesh(callback: (mesh: Mesh) => void) {
|
|
128
|
+
// 等待加载完成
|
|
129
|
+
await this.awaitLoaded();
|
|
107
130
|
|
|
108
131
|
this.traverse(obj => {
|
|
109
132
|
const mesh = obj as Mesh;
|
|
@@ -113,8 +136,22 @@ export default class MeshBase extends Group {
|
|
|
113
136
|
});
|
|
114
137
|
}
|
|
115
138
|
|
|
116
|
-
|
|
139
|
+
clock = new Clock();
|
|
117
140
|
|
|
141
|
+
//设置动画的权重
|
|
142
|
+
async setClipActionWeight(weights: { [key: string]: number } = {}) {
|
|
143
|
+
// 等待加载完成
|
|
144
|
+
await this.awaitLoaded();
|
|
145
|
+
for (let key in this.animationActions) {
|
|
146
|
+
this.animationActions[key].weight = weights[key] || 0;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
updateMatrix(): void {
|
|
151
|
+
//播放动画
|
|
152
|
+
if (this.mixer) {
|
|
153
|
+
this.mixer.update(this.clock.getDelta());
|
|
154
|
+
}
|
|
118
155
|
// 深度应用属性
|
|
119
156
|
this.traverse(obj => {
|
|
120
157
|
const mesh = obj as Mesh;
|
|
@@ -128,39 +165,27 @@ export default class MeshBase extends Group {
|
|
|
128
165
|
super.updateMatrix();
|
|
129
166
|
}
|
|
130
167
|
|
|
131
|
-
// 应用全部变化
|
|
132
|
-
applyAllTranstion() {
|
|
133
|
-
this.updateMatrixWorld(true);
|
|
134
|
-
// 应用全部变换
|
|
135
|
-
this.traverse(obj => {
|
|
136
|
-
const mesh = obj as Mesh;
|
|
137
|
-
mesh.position.set(0, 0, 0);
|
|
138
|
-
mesh.rotation.set(0, 0, 0);
|
|
139
|
-
mesh.scale.set(1, 1, 1);
|
|
140
|
-
if (mesh.isMesh) {
|
|
141
|
-
mesh.geometry.applyMatrix4(mesh.matrixWorld);
|
|
142
|
-
(mesh.material as MeshStandardMaterial).side = DoubleSide;
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
168
|
// 获取模型的尺寸
|
|
148
|
-
getSize() {
|
|
169
|
+
async getSize() {
|
|
170
|
+
await this.awaitLoaded();
|
|
149
171
|
return threeUtils.getSize(this);
|
|
150
172
|
}
|
|
151
173
|
|
|
152
174
|
// 居中
|
|
153
|
-
center() {
|
|
175
|
+
async center() {
|
|
176
|
+
await this.awaitLoaded();
|
|
154
177
|
threeUtils.center(this);
|
|
155
178
|
}
|
|
156
179
|
|
|
157
180
|
//底对齐
|
|
158
|
-
alignBottom(y: number = 0) {
|
|
181
|
+
async alignBottom(y: number = 0) {
|
|
182
|
+
await this.awaitLoaded();
|
|
159
183
|
threeUtils.alignBottom(this, y);
|
|
160
184
|
}
|
|
161
185
|
|
|
162
186
|
// 放大到指定尺寸
|
|
163
|
-
scaleTo(size: number = 1, key: Array<"x" | "y" | "z"> = ["x", "y", "z"]) {
|
|
187
|
+
async scaleTo(size: number = 1, key: Array<"x" | "y" | "z"> = ["x", "y", "z"]) {
|
|
188
|
+
await this.awaitLoaded();
|
|
164
189
|
threeUtils.scaleTo(this, size, key);
|
|
165
190
|
}
|
|
166
191
|
|
package/ThreeBase.js
CHANGED
|
@@ -187,7 +187,12 @@ export default class ThreeBase extends EventDispatcher {
|
|
|
187
187
|
frame = 50;
|
|
188
188
|
init() {
|
|
189
189
|
// 资源加载进度有更新时, 需要渲染
|
|
190
|
-
threeLoader.addEventListener("
|
|
190
|
+
threeLoader.addEventListener("loadding", () => {
|
|
191
|
+
this.dispatchEvent({ type: "loadding" });
|
|
192
|
+
this.needRender = true;
|
|
193
|
+
});
|
|
194
|
+
threeLoader.addEventListener("onLoad", () => {
|
|
195
|
+
this.dispatchEvent({ type: "onLoad" });
|
|
191
196
|
this.needRender = true;
|
|
192
197
|
});
|
|
193
198
|
}
|
package/ThreeBase.ts
CHANGED
|
@@ -227,7 +227,13 @@ export default class ThreeBase extends EventDispatcher {
|
|
|
227
227
|
|
|
228
228
|
init() {
|
|
229
229
|
// 资源加载进度有更新时, 需要渲染
|
|
230
|
-
threeLoader.addEventListener("
|
|
230
|
+
threeLoader.addEventListener("loadding", () => {
|
|
231
|
+
this.dispatchEvent({ type: "loadding" } as never);
|
|
232
|
+
this.needRender = true;
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
threeLoader.addEventListener("onLoad", () => {
|
|
236
|
+
this.dispatchEvent({ type: "onLoad" } as never);
|
|
231
237
|
this.needRender = true;
|
|
232
238
|
});
|
|
233
239
|
|
package/package.json
CHANGED
package/threeLoader.js
CHANGED
|
@@ -26,8 +26,13 @@ class ThreeLoader extends EventDispatcher {
|
|
|
26
26
|
this.onload = false;
|
|
27
27
|
};
|
|
28
28
|
// 有资源加载完成时通知渲染器
|
|
29
|
-
DefaultLoadingManager.onProgress = () => {
|
|
30
|
-
|
|
29
|
+
DefaultLoadingManager.onProgress = (url, loaded, total) => {
|
|
30
|
+
if (loaded < total) {
|
|
31
|
+
this.dispatchEvent({ type: "loadding" });
|
|
32
|
+
}
|
|
33
|
+
else if (loaded === total) {
|
|
34
|
+
this.dispatchEvent({ type: "onLoad" });
|
|
35
|
+
}
|
|
31
36
|
};
|
|
32
37
|
DefaultLoadingManager.onLoad = () => {
|
|
33
38
|
this.onload = true;
|
package/threeLoader.ts
CHANGED
|
@@ -32,8 +32,12 @@ class ThreeLoader extends EventDispatcher {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// 有资源加载完成时通知渲染器
|
|
35
|
-
DefaultLoadingManager.onProgress = () => {
|
|
36
|
-
|
|
35
|
+
DefaultLoadingManager.onProgress = (url: string, loaded: number, total: number) => {
|
|
36
|
+
if (loaded < total) {
|
|
37
|
+
this.dispatchEvent({ type: "loadding" } as never);
|
|
38
|
+
} else if (loaded === total) {
|
|
39
|
+
this.dispatchEvent({ type: "onLoad" } as never);
|
|
40
|
+
}
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
DefaultLoadingManager.onLoad = () => {
|