@mml-io/3d-web-avatar 0.0.0-experimental-b7ccddd-20231027
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/LICENSE +19 -0
- package/build/character/Character.d.ts +11 -0
- package/build/character/ModelLoader.d.ts +12 -0
- package/build/character/types.d.ts +5 -0
- package/build/helpers/SkeletonHelpers.d.ts +18 -0
- package/build/index.d.ts +3 -0
- package/build/index.js +333 -0
- package/build/index.js.map +7 -0
- package/package.json +34 -0
package/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2023 Improbable MV Limited
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { Object3D } from "three";
|
2
|
+
import { ModelLoader } from "./ModelLoader";
|
3
|
+
export declare class Character {
|
4
|
+
private modelLoader;
|
5
|
+
private skeletonHelpers;
|
6
|
+
private skinnedMeshesParent;
|
7
|
+
private sharedSkeleton;
|
8
|
+
private sharedMatrixWorld;
|
9
|
+
constructor(modelLoader: ModelLoader);
|
10
|
+
mergeBodyParts(fullBodyURL: string, headURL: string, upperBodyURL: string, lowerBodyURL: string, feetURL: string): Promise<Object3D>;
|
11
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
|
2
|
+
export declare class ModelLoader {
|
3
|
+
private static instance;
|
4
|
+
private readonly loadingManager;
|
5
|
+
private readonly gltfLoader;
|
6
|
+
private modelCache;
|
7
|
+
private ongoingLoads;
|
8
|
+
constructor(maxCacheSize?: number);
|
9
|
+
static getInstance(): ModelLoader;
|
10
|
+
load(fileUrl: string): Promise<GLTF | undefined>;
|
11
|
+
private loadFromUrl;
|
12
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { Matrix4, Object3D, Skeleton } from "three";
|
2
|
+
import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
|
3
|
+
type ClonedGLTFParts = {
|
4
|
+
gltf: GLTF;
|
5
|
+
sharedSkeleton: Skeleton;
|
6
|
+
matrixWorld: Matrix4;
|
7
|
+
};
|
8
|
+
export declare class SkeletonHelpers {
|
9
|
+
private debug;
|
10
|
+
private hierarchies;
|
11
|
+
private modelNames;
|
12
|
+
private extractBoneHierarchy;
|
13
|
+
private areBoneHierarchiesEqual;
|
14
|
+
extractAndStoreBoneHierarchy(node: Object3D, modelName: string): void;
|
15
|
+
compareLatestHierarchies(): void;
|
16
|
+
cloneGLTF(gltf: GLTF, modelName: string): ClonedGLTFParts;
|
17
|
+
}
|
18
|
+
export {};
|
package/build/index.d.ts
ADDED
package/build/index.js
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
// src/helpers/SkeletonHelpers.ts
|
2
|
+
import { Skeleton } from "three";
|
3
|
+
var SkeletonHelpers = class {
|
4
|
+
constructor() {
|
5
|
+
this.debug = false;
|
6
|
+
this.hierarchies = [];
|
7
|
+
this.modelNames = [];
|
8
|
+
}
|
9
|
+
extractBoneHierarchy(node) {
|
10
|
+
if (node.type !== "Bone")
|
11
|
+
return null;
|
12
|
+
const boneMap = /* @__PURE__ */ new Map();
|
13
|
+
node.children.forEach((child) => {
|
14
|
+
const childHierarchy = this.extractBoneHierarchy(child);
|
15
|
+
if (childHierarchy)
|
16
|
+
boneMap.set(child.name, childHierarchy);
|
17
|
+
});
|
18
|
+
return boneMap;
|
19
|
+
}
|
20
|
+
areBoneHierarchiesEqual(a, b, modelNameA, modelNameB, path = []) {
|
21
|
+
let identical = true;
|
22
|
+
const differences = [];
|
23
|
+
if (a.size !== b.size) {
|
24
|
+
differences.push(
|
25
|
+
`Different number of children at path: ${path.join(
|
26
|
+
" -> "
|
27
|
+
)} in models ${modelNameA} and ${modelNameB}.`
|
28
|
+
);
|
29
|
+
identical = false;
|
30
|
+
}
|
31
|
+
for (const [key, value] of a) {
|
32
|
+
if (!b.has(key)) {
|
33
|
+
differences.push(
|
34
|
+
`Bone "${key}" was found in model ${modelNameA} but not in model ${modelNameB} at path: ${path.join(
|
35
|
+
" -> "
|
36
|
+
)}.`
|
37
|
+
);
|
38
|
+
identical = false;
|
39
|
+
continue;
|
40
|
+
}
|
41
|
+
const newPath = [...path, key];
|
42
|
+
const result = this.areBoneHierarchiesEqual(
|
43
|
+
value,
|
44
|
+
b.get(key),
|
45
|
+
modelNameA,
|
46
|
+
modelNameB,
|
47
|
+
newPath
|
48
|
+
);
|
49
|
+
if (!result.identical) {
|
50
|
+
identical = false;
|
51
|
+
differences.push(...result.differences);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
for (const key of b.keys()) {
|
55
|
+
if (!a.has(key)) {
|
56
|
+
differences.push(
|
57
|
+
`Bone "${key}" was found in model ${modelNameB} but not in model ${modelNameA} at path: ${path.join(
|
58
|
+
" -> "
|
59
|
+
)}.`
|
60
|
+
);
|
61
|
+
identical = false;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
return {
|
65
|
+
identical,
|
66
|
+
differences
|
67
|
+
};
|
68
|
+
}
|
69
|
+
extractAndStoreBoneHierarchy(node, modelName) {
|
70
|
+
const newHierarchy = this.extractBoneHierarchy(node);
|
71
|
+
if (!newHierarchy) {
|
72
|
+
console.log(`No bone hierarchy found in the model: ${modelName}.`);
|
73
|
+
return;
|
74
|
+
}
|
75
|
+
this.hierarchies.push(newHierarchy);
|
76
|
+
this.modelNames.push(modelName);
|
77
|
+
}
|
78
|
+
compareLatestHierarchies() {
|
79
|
+
if (this.hierarchies.length < 2)
|
80
|
+
return;
|
81
|
+
const latestHierarchy = this.hierarchies[this.hierarchies.length - 1];
|
82
|
+
const previousHierarchy = this.hierarchies[this.hierarchies.length - 2];
|
83
|
+
const diff = this.areBoneHierarchiesEqual(
|
84
|
+
previousHierarchy,
|
85
|
+
latestHierarchy,
|
86
|
+
this.modelNames[this.modelNames.length - 2],
|
87
|
+
this.modelNames[this.modelNames.length - 1],
|
88
|
+
[]
|
89
|
+
);
|
90
|
+
if (diff.identical) {
|
91
|
+
if (this.debug)
|
92
|
+
console.log("The skeletons are identical.");
|
93
|
+
} else {
|
94
|
+
diff.differences.forEach((difference) => console.log(difference));
|
95
|
+
}
|
96
|
+
}
|
97
|
+
cloneGLTF(gltf, modelName) {
|
98
|
+
const clone = {
|
99
|
+
animations: gltf.animations,
|
100
|
+
scene: gltf.scene.clone(true)
|
101
|
+
};
|
102
|
+
let sharedSkeleton = null;
|
103
|
+
let matrixWorld = null;
|
104
|
+
const skinnedMeshes = {};
|
105
|
+
gltf.scene.traverse((node) => {
|
106
|
+
if (node.type === "SkinnedMesh") {
|
107
|
+
skinnedMeshes[node.name] = node;
|
108
|
+
}
|
109
|
+
});
|
110
|
+
const cloneBones = {};
|
111
|
+
const cloneSkinnedMeshes = {};
|
112
|
+
let hierarchyCheck = false;
|
113
|
+
clone.scene.traverse((node) => {
|
114
|
+
if (node.type === "Bone") {
|
115
|
+
if (hierarchyCheck === false) {
|
116
|
+
hierarchyCheck = true;
|
117
|
+
this.extractAndStoreBoneHierarchy(node, modelName);
|
118
|
+
this.compareLatestHierarchies();
|
119
|
+
}
|
120
|
+
cloneBones[node.name] = node;
|
121
|
+
}
|
122
|
+
if (node.type === "SkinnedMesh") {
|
123
|
+
cloneSkinnedMeshes[node.name] = node;
|
124
|
+
}
|
125
|
+
});
|
126
|
+
for (const name in skinnedMeshes) {
|
127
|
+
const skinnedMesh = skinnedMeshes[name];
|
128
|
+
const skeleton = skinnedMesh.skeleton;
|
129
|
+
const cloneSkinnedMesh = cloneSkinnedMeshes[name];
|
130
|
+
const orderedCloneBones = [];
|
131
|
+
for (let i = 0; i < skeleton.bones.length; ++i) {
|
132
|
+
const cloneBone = cloneBones[skeleton.bones[i].name];
|
133
|
+
orderedCloneBones.push(cloneBone);
|
134
|
+
}
|
135
|
+
if (sharedSkeleton === null) {
|
136
|
+
sharedSkeleton = new Skeleton(orderedCloneBones, skeleton.boneInverses);
|
137
|
+
}
|
138
|
+
if (matrixWorld === null) {
|
139
|
+
matrixWorld = cloneSkinnedMesh.matrixWorld;
|
140
|
+
}
|
141
|
+
cloneSkinnedMesh.bind(sharedSkeleton, matrixWorld);
|
142
|
+
}
|
143
|
+
return {
|
144
|
+
gltf: clone,
|
145
|
+
sharedSkeleton,
|
146
|
+
matrixWorld
|
147
|
+
};
|
148
|
+
}
|
149
|
+
};
|
150
|
+
|
151
|
+
// src/character/Character.ts
|
152
|
+
var Character = class {
|
153
|
+
constructor(modelLoader) {
|
154
|
+
this.modelLoader = modelLoader;
|
155
|
+
this.skeletonHelpers = new SkeletonHelpers();
|
156
|
+
this.skinnedMeshesParent = null;
|
157
|
+
this.sharedSkeleton = null;
|
158
|
+
this.sharedMatrixWorld = null;
|
159
|
+
}
|
160
|
+
async mergeBodyParts(fullBodyURL, headURL, upperBodyURL, lowerBodyURL, feetURL) {
|
161
|
+
const fullBodyAsset = await this.modelLoader.load(fullBodyURL);
|
162
|
+
const fullBodyGLTF = this.skeletonHelpers.cloneGLTF(fullBodyAsset, "fullBody");
|
163
|
+
const headAsset = await this.modelLoader.load(headURL);
|
164
|
+
const upperBodyAsset = await this.modelLoader.load(upperBodyURL);
|
165
|
+
const lowerBodyAsset = await this.modelLoader.load(lowerBodyURL);
|
166
|
+
const feetAsset = await this.modelLoader.load(feetURL);
|
167
|
+
const skinnedMeshesToRemove = [];
|
168
|
+
const fullBodyModelGroup = fullBodyGLTF.gltf.scene;
|
169
|
+
this.skinnedMeshesParent = null;
|
170
|
+
fullBodyModelGroup.traverse((child) => {
|
171
|
+
if (child.type === "SkinnedMesh") {
|
172
|
+
child.castShadow = true;
|
173
|
+
if (this.skinnedMeshesParent === null) {
|
174
|
+
this.skinnedMeshesParent = child.parent;
|
175
|
+
}
|
176
|
+
}
|
177
|
+
});
|
178
|
+
this.sharedSkeleton = fullBodyGLTF.sharedSkeleton;
|
179
|
+
this.sharedMatrixWorld = fullBodyGLTF.matrixWorld;
|
180
|
+
skinnedMeshesToRemove.forEach((child) => {
|
181
|
+
child.removeFromParent();
|
182
|
+
});
|
183
|
+
const headGLTF = this.skeletonHelpers.cloneGLTF(headAsset, "headGLTF");
|
184
|
+
const headModelGroup = headGLTF.gltf.scene;
|
185
|
+
headModelGroup.traverse((child) => {
|
186
|
+
var _a;
|
187
|
+
if (child.type === "SkinnedMesh") {
|
188
|
+
child.castShadow = true;
|
189
|
+
child.bind(this.sharedSkeleton, this.sharedMatrixWorld);
|
190
|
+
(_a = this.skinnedMeshesParent) == null ? void 0 : _a.children.splice(0, 0, child);
|
191
|
+
}
|
192
|
+
});
|
193
|
+
const upperBodyGLTF = this.skeletonHelpers.cloneGLTF(upperBodyAsset, "upperBodyGLTF");
|
194
|
+
const upperBodyModelGroup = upperBodyGLTF.gltf.scene;
|
195
|
+
upperBodyModelGroup.traverse((child) => {
|
196
|
+
var _a;
|
197
|
+
if (child.type === "SkinnedMesh") {
|
198
|
+
child.castShadow = true;
|
199
|
+
child.bind(this.sharedSkeleton, this.sharedMatrixWorld);
|
200
|
+
(_a = this.skinnedMeshesParent) == null ? void 0 : _a.children.splice(1, 0, child);
|
201
|
+
}
|
202
|
+
});
|
203
|
+
const lowerBodyGLTF = this.skeletonHelpers.cloneGLTF(lowerBodyAsset, "lowerBodyGLTF");
|
204
|
+
const lowerBodyModelGroup = lowerBodyGLTF.gltf.scene;
|
205
|
+
lowerBodyModelGroup.traverse((child) => {
|
206
|
+
var _a;
|
207
|
+
if (child.type === "SkinnedMesh") {
|
208
|
+
child.castShadow = true;
|
209
|
+
child.bind(this.sharedSkeleton, this.sharedMatrixWorld);
|
210
|
+
(_a = this.skinnedMeshesParent) == null ? void 0 : _a.children.splice(2, 0, child);
|
211
|
+
}
|
212
|
+
});
|
213
|
+
const feetGLTF = this.skeletonHelpers.cloneGLTF(feetAsset, "feetGLTF");
|
214
|
+
const feetModelGroup = feetGLTF.gltf.scene;
|
215
|
+
feetModelGroup.traverse((child) => {
|
216
|
+
var _a;
|
217
|
+
if (child.type === "SkinnedMesh") {
|
218
|
+
child.castShadow = true;
|
219
|
+
child.bind(this.sharedSkeleton, this.sharedMatrixWorld);
|
220
|
+
(_a = this.skinnedMeshesParent) == null ? void 0 : _a.children.splice(3, 0, child);
|
221
|
+
}
|
222
|
+
});
|
223
|
+
return fullBodyGLTF.gltf.scene;
|
224
|
+
}
|
225
|
+
};
|
226
|
+
|
227
|
+
// src/character/ModelLoader.ts
|
228
|
+
import { LoadingManager } from "three";
|
229
|
+
import { GLTFLoader as ThreeGLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
230
|
+
var CachedGLTFLoader = class extends ThreeGLTFLoader {
|
231
|
+
constructor(manager) {
|
232
|
+
super(manager);
|
233
|
+
this.blobCache = /* @__PURE__ */ new Map();
|
234
|
+
}
|
235
|
+
setBlobUrl(originalUrl, blobUrl) {
|
236
|
+
this.blobCache.set(originalUrl, blobUrl);
|
237
|
+
}
|
238
|
+
getBlobUrl(originalUrl) {
|
239
|
+
return this.blobCache.get(originalUrl);
|
240
|
+
}
|
241
|
+
load(url, onLoad, onProgress, onError) {
|
242
|
+
const blobUrl = this.getBlobUrl(url);
|
243
|
+
if (blobUrl) {
|
244
|
+
console.log(`Loading cached ${url.split("/").pop()}`);
|
245
|
+
super.load(blobUrl, onLoad, onProgress, onError);
|
246
|
+
} else {
|
247
|
+
super.load(url, onLoad, onProgress, onError);
|
248
|
+
}
|
249
|
+
}
|
250
|
+
};
|
251
|
+
var LRUCache = class {
|
252
|
+
constructor(maxSize = 100) {
|
253
|
+
this.maxSize = maxSize;
|
254
|
+
this.cache = /* @__PURE__ */ new Map();
|
255
|
+
}
|
256
|
+
get(key) {
|
257
|
+
const item = this.cache.get(key);
|
258
|
+
if (item) {
|
259
|
+
this.cache.delete(key);
|
260
|
+
this.cache.set(key, item);
|
261
|
+
}
|
262
|
+
return item;
|
263
|
+
}
|
264
|
+
set(key, value) {
|
265
|
+
if (this.cache.size >= this.maxSize) {
|
266
|
+
const oldestKey = this.cache.keys().next().value;
|
267
|
+
this.cache.delete(oldestKey);
|
268
|
+
}
|
269
|
+
this.cache.set(key, value);
|
270
|
+
}
|
271
|
+
};
|
272
|
+
var _ModelLoader = class _ModelLoader {
|
273
|
+
constructor(maxCacheSize = 100) {
|
274
|
+
this.ongoingLoads = /* @__PURE__ */ new Map();
|
275
|
+
this.loadingManager = new LoadingManager();
|
276
|
+
this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
|
277
|
+
this.modelCache = new LRUCache(maxCacheSize);
|
278
|
+
}
|
279
|
+
static getInstance() {
|
280
|
+
if (!_ModelLoader.instance) {
|
281
|
+
_ModelLoader.instance = new _ModelLoader();
|
282
|
+
}
|
283
|
+
return _ModelLoader.instance;
|
284
|
+
}
|
285
|
+
async load(fileUrl) {
|
286
|
+
const cachedModel = this.modelCache.get(fileUrl);
|
287
|
+
if (cachedModel) {
|
288
|
+
const blobURL = URL.createObjectURL(cachedModel.blob);
|
289
|
+
this.gltfLoader.setBlobUrl(fileUrl, blobURL);
|
290
|
+
return this.loadFromUrl(fileUrl, cachedModel.originalExtension);
|
291
|
+
} else {
|
292
|
+
console.log(`Loading ${fileUrl} from server`);
|
293
|
+
const ongoingLoad = this.ongoingLoads.get(fileUrl);
|
294
|
+
if (ongoingLoad)
|
295
|
+
return ongoingLoad;
|
296
|
+
const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
|
297
|
+
const originalExtension = fileUrl.split(".").pop() || "";
|
298
|
+
this.modelCache.set(fileUrl, { blob, originalExtension });
|
299
|
+
const blobURL = URL.createObjectURL(blob);
|
300
|
+
this.ongoingLoads.delete(fileUrl);
|
301
|
+
return this.loadFromUrl(blobURL, originalExtension);
|
302
|
+
});
|
303
|
+
this.ongoingLoads.set(fileUrl, loadPromise);
|
304
|
+
return loadPromise;
|
305
|
+
}
|
306
|
+
}
|
307
|
+
async loadFromUrl(url, extension) {
|
308
|
+
if (["gltf", "glb"].includes(extension)) {
|
309
|
+
return new Promise((resolve, reject) => {
|
310
|
+
this.gltfLoader.load(
|
311
|
+
url,
|
312
|
+
(object) => {
|
313
|
+
resolve(object);
|
314
|
+
},
|
315
|
+
void 0,
|
316
|
+
(error) => {
|
317
|
+
console.error(`Error loading GL(B|TF) from ${url}: ${error}`);
|
318
|
+
reject(error);
|
319
|
+
}
|
320
|
+
);
|
321
|
+
});
|
322
|
+
} else {
|
323
|
+
console.error(`Error: can't recognize ${url} extension: ${extension}`);
|
324
|
+
}
|
325
|
+
}
|
326
|
+
};
|
327
|
+
_ModelLoader.instance = null;
|
328
|
+
var ModelLoader = _ModelLoader;
|
329
|
+
export {
|
330
|
+
Character,
|
331
|
+
ModelLoader
|
332
|
+
};
|
333
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +1,7 @@
|
|
1
|
+
{
|
2
|
+
"version": 3,
|
3
|
+
"sources": ["../src/helpers/SkeletonHelpers.ts", "../src/character/Character.ts", "../src/character/ModelLoader.ts"],
|
4
|
+
"sourcesContent": ["import { Bone, Matrix4, Object3D, Skeleton, SkinnedMesh } from \"three\";\nimport { GLTF } from \"three/examples/jsm/loaders/GLTFLoader.js\";\n\ntype ClonedGLTFParts = {\n gltf: GLTF;\n sharedSkeleton: Skeleton;\n matrixWorld: Matrix4;\n};\n\ntype BoneHierarchyMap = Map<string, BoneHierarchyMap>;\ntype DiffResult = {\n identical: boolean;\n differences: string[];\n};\n\nexport class SkeletonHelpers {\n private debug: boolean = false;\n private hierarchies: BoneHierarchyMap[] = [];\n private modelNames: string[] = [];\n\n private extractBoneHierarchy(node: Object3D): BoneHierarchyMap | null {\n if (node.type !== \"Bone\") return null;\n const boneMap: BoneHierarchyMap = new Map();\n node.children.forEach((child) => {\n const childHierarchy = this.extractBoneHierarchy(child);\n if (childHierarchy) boneMap.set(child.name, childHierarchy);\n });\n return boneMap;\n }\n\n private areBoneHierarchiesEqual(\n a: BoneHierarchyMap,\n b: BoneHierarchyMap,\n modelNameA: string,\n modelNameB: string,\n path: string[] = [],\n ): DiffResult {\n let identical = true;\n const differences: string[] = [];\n\n if (a.size !== b.size) {\n differences.push(\n `Different number of children at path: ${path.join(\n \" -> \",\n )} in models ${modelNameA} and ${modelNameB}.`,\n );\n identical = false;\n }\n\n for (const [key, value] of a) {\n if (!b.has(key)) {\n differences.push(\n `Bone \"${key}\" was found in model ${modelNameA} but not in model ${modelNameB} at path: ${path.join(\n \" -> \",\n )}.`,\n );\n identical = false;\n continue;\n }\n\n const newPath = [...path, key];\n const result = this.areBoneHierarchiesEqual(\n value,\n b.get(key)!,\n modelNameA,\n modelNameB,\n newPath,\n );\n\n if (!result.identical) {\n identical = false;\n differences.push(...result.differences);\n }\n }\n\n for (const key of b.keys()) {\n if (!a.has(key)) {\n differences.push(\n `Bone \"${key}\" was found in model ${modelNameB} but not in model ${modelNameA} at path: ${path.join(\n \" -> \",\n )}.`,\n );\n identical = false;\n }\n }\n\n return {\n identical,\n differences,\n };\n }\n\n public extractAndStoreBoneHierarchy(node: Object3D, modelName: string) {\n const newHierarchy = this.extractBoneHierarchy(node);\n\n if (!newHierarchy) {\n console.log(`No bone hierarchy found in the model: ${modelName}.`);\n return;\n }\n\n this.hierarchies.push(newHierarchy);\n this.modelNames.push(modelName);\n }\n\n public compareLatestHierarchies() {\n if (this.hierarchies.length < 2) return;\n\n const latestHierarchy = this.hierarchies[this.hierarchies.length - 1];\n const previousHierarchy = this.hierarchies[this.hierarchies.length - 2];\n\n const diff = this.areBoneHierarchiesEqual(\n previousHierarchy,\n latestHierarchy,\n this.modelNames[this.modelNames.length - 2],\n this.modelNames[this.modelNames.length - 1],\n [],\n );\n\n if (diff.identical) {\n if (this.debug) console.log(\"The skeletons are identical.\");\n } else {\n diff.differences.forEach((difference) => console.log(difference));\n }\n }\n\n public cloneGLTF(gltf: GLTF, modelName: string): ClonedGLTFParts {\n const clone: Partial<GLTF> = {\n animations: gltf.animations,\n scene: gltf.scene.clone(true),\n };\n\n let sharedSkeleton: Skeleton | null = null;\n let matrixWorld: Matrix4 | null = null;\n\n const skinnedMeshes: Record<string, SkinnedMesh> = {};\n\n gltf.scene.traverse((node) => {\n if (node.type === \"SkinnedMesh\") {\n skinnedMeshes[node.name] = node as SkinnedMesh;\n }\n });\n\n const cloneBones: Record<string, Bone> = {};\n const cloneSkinnedMeshes: Record<string, SkinnedMesh> = {};\n\n let hierarchyCheck = false;\n clone.scene!.traverse((node) => {\n if (node.type === \"Bone\") {\n if (hierarchyCheck === false) {\n hierarchyCheck = true;\n this.extractAndStoreBoneHierarchy(node, modelName);\n this.compareLatestHierarchies();\n }\n cloneBones[node.name] = node as Bone;\n }\n\n if (node.type === \"SkinnedMesh\") {\n cloneSkinnedMeshes[node.name] = node as SkinnedMesh;\n }\n });\n\n for (const name in skinnedMeshes) {\n const skinnedMesh = skinnedMeshes[name];\n const skeleton = skinnedMesh.skeleton;\n const cloneSkinnedMesh = cloneSkinnedMeshes[name];\n\n const orderedCloneBones = [];\n\n for (let i = 0; i < skeleton.bones.length; ++i) {\n const cloneBone = cloneBones[skeleton.bones[i].name];\n orderedCloneBones.push(cloneBone);\n }\n\n if (sharedSkeleton === null) {\n sharedSkeleton = new Skeleton(orderedCloneBones, skeleton.boneInverses);\n }\n\n if (matrixWorld === null) {\n matrixWorld = cloneSkinnedMesh.matrixWorld;\n }\n\n cloneSkinnedMesh.bind(sharedSkeleton, matrixWorld);\n }\n\n return {\n gltf: clone as GLTF,\n sharedSkeleton: sharedSkeleton as Skeleton,\n matrixWorld: matrixWorld as Matrix4,\n };\n }\n}\n", "import { Group, Matrix4, Object3D, Skeleton, SkinnedMesh } from \"three\";\nimport { GLTF } from \"three/examples/jsm/loaders/GLTFLoader\";\n\nimport { SkeletonHelpers } from \"../helpers/SkeletonHelpers\";\n\nimport { ModelLoader } from \"./ModelLoader\";\n\nexport class Character {\n private skeletonHelpers: SkeletonHelpers = new SkeletonHelpers();\n private skinnedMeshesParent: Group | null = null;\n private sharedSkeleton: Skeleton | null = null;\n private sharedMatrixWorld: Matrix4 | null = null;\n\n constructor(private modelLoader: ModelLoader) {}\n\n public async mergeBodyParts(\n fullBodyURL: string,\n headURL: string,\n upperBodyURL: string,\n lowerBodyURL: string,\n feetURL: string,\n ): Promise<Object3D> {\n const fullBodyAsset = await this.modelLoader.load(fullBodyURL);\n const fullBodyGLTF = this.skeletonHelpers.cloneGLTF(fullBodyAsset as GLTF, \"fullBody\");\n\n const headAsset = await this.modelLoader.load(headURL);\n const upperBodyAsset = await this.modelLoader.load(upperBodyURL);\n const lowerBodyAsset = await this.modelLoader.load(lowerBodyURL);\n const feetAsset = await this.modelLoader.load(feetURL);\n\n const skinnedMeshesToRemove: SkinnedMesh[] = [];\n\n const fullBodyModelGroup = fullBodyGLTF.gltf.scene;\n\n this.skinnedMeshesParent = null;\n\n fullBodyModelGroup.traverse((child) => {\n if (child.type === \"SkinnedMesh\") {\n (child as SkinnedMesh).castShadow = true;\n if (this.skinnedMeshesParent === null) {\n this.skinnedMeshesParent = child.parent as Group;\n }\n }\n });\n this.sharedSkeleton = fullBodyGLTF.sharedSkeleton;\n this.sharedMatrixWorld = fullBodyGLTF.matrixWorld;\n\n skinnedMeshesToRemove.forEach((child) => {\n child.removeFromParent();\n });\n\n const headGLTF = this.skeletonHelpers.cloneGLTF(headAsset as GLTF, \"headGLTF\");\n const headModelGroup = headGLTF.gltf.scene;\n headModelGroup.traverse((child) => {\n if (child.type === \"SkinnedMesh\") {\n (child as SkinnedMesh).castShadow = true;\n (child as SkinnedMesh).bind(this.sharedSkeleton!, this.sharedMatrixWorld!);\n this.skinnedMeshesParent?.children.splice(0, 0, child as SkinnedMesh);\n }\n });\n\n const upperBodyGLTF = this.skeletonHelpers.cloneGLTF(upperBodyAsset as GLTF, \"upperBodyGLTF\");\n const upperBodyModelGroup = upperBodyGLTF.gltf.scene;\n upperBodyModelGroup.traverse((child) => {\n if (child.type === \"SkinnedMesh\") {\n (child as SkinnedMesh).castShadow = true;\n (child as SkinnedMesh).bind(this.sharedSkeleton!, this.sharedMatrixWorld!);\n this.skinnedMeshesParent?.children.splice(1, 0, child as SkinnedMesh);\n }\n });\n\n const lowerBodyGLTF = this.skeletonHelpers.cloneGLTF(lowerBodyAsset as GLTF, \"lowerBodyGLTF\");\n const lowerBodyModelGroup = lowerBodyGLTF.gltf.scene;\n lowerBodyModelGroup.traverse((child) => {\n if (child.type === \"SkinnedMesh\") {\n (child as SkinnedMesh).castShadow = true;\n (child as SkinnedMesh).bind(this.sharedSkeleton!, this.sharedMatrixWorld!);\n this.skinnedMeshesParent?.children.splice(2, 0, child as SkinnedMesh);\n }\n });\n\n const feetGLTF = this.skeletonHelpers.cloneGLTF(feetAsset as GLTF, \"feetGLTF\");\n const feetModelGroup = feetGLTF.gltf.scene;\n feetModelGroup.traverse((child) => {\n if (child.type === \"SkinnedMesh\") {\n (child as SkinnedMesh).castShadow = true;\n (child as SkinnedMesh).bind(this.sharedSkeleton!, this.sharedMatrixWorld!);\n this.skinnedMeshesParent?.children.splice(3, 0, child as SkinnedMesh);\n }\n });\n\n return fullBodyGLTF!.gltf.scene as Object3D;\n }\n}\n", "import { LoadingManager } from \"three\";\nimport { GLTF, GLTFLoader as ThreeGLTFLoader } from \"three/examples/jsm/loaders/GLTFLoader.js\";\n\nclass CachedGLTFLoader extends ThreeGLTFLoader {\n private blobCache: Map<string, string>;\n\n constructor(manager?: LoadingManager) {\n super(manager);\n this.blobCache = new Map();\n }\n\n setBlobUrl(originalUrl: string, blobUrl: string) {\n this.blobCache.set(originalUrl, blobUrl);\n }\n\n getBlobUrl(originalUrl: string): string | undefined {\n return this.blobCache.get(originalUrl);\n }\n\n load(\n url: string,\n onLoad: (gltf: GLTF) => void,\n onProgress?: ((event: ProgressEvent<EventTarget>) => void) | undefined,\n onError?: ((event: ErrorEvent) => void) | undefined,\n ): void {\n const blobUrl = this.getBlobUrl(url);\n if (blobUrl) {\n console.log(`Loading cached ${url.split(\"/\").pop()}`);\n super.load(blobUrl, onLoad, onProgress, onError);\n } else {\n super.load(url, onLoad, onProgress, onError);\n }\n }\n}\n\nclass LRUCache<K, V> {\n private maxSize: number;\n private cache: Map<K, V>;\n\n constructor(maxSize: number = 100) {\n this.maxSize = maxSize;\n this.cache = new Map();\n }\n\n get(key: K): V | undefined {\n const item = this.cache.get(key);\n if (item) {\n this.cache.delete(key);\n this.cache.set(key, item);\n }\n return item;\n }\n\n set(key: K, value: V): void {\n if (this.cache.size >= this.maxSize) {\n const oldestKey = this.cache.keys().next().value;\n this.cache.delete(oldestKey);\n }\n this.cache.set(key, value);\n }\n}\n\ninterface CachedModel {\n blob: Blob;\n originalExtension: string;\n}\n\nexport class ModelLoader {\n private static instance: ModelLoader | null = null;\n\n private readonly loadingManager: LoadingManager;\n private readonly gltfLoader: CachedGLTFLoader;\n private modelCache: LRUCache<string, CachedModel>;\n private ongoingLoads: Map<string, Promise<GLTF | undefined>> = new Map();\n\n constructor(maxCacheSize: number = 100) {\n this.loadingManager = new LoadingManager();\n this.gltfLoader = new CachedGLTFLoader(this.loadingManager);\n this.modelCache = new LRUCache(maxCacheSize);\n }\n\n static getInstance(): ModelLoader {\n if (!ModelLoader.instance) {\n ModelLoader.instance = new ModelLoader();\n }\n return ModelLoader.instance;\n }\n\n async load(fileUrl: string): Promise<GLTF | undefined> {\n const cachedModel = this.modelCache.get(fileUrl);\n\n if (cachedModel) {\n const blobURL = URL.createObjectURL(cachedModel.blob);\n this.gltfLoader.setBlobUrl(fileUrl, blobURL);\n return this.loadFromUrl(fileUrl, cachedModel.originalExtension);\n } else {\n console.log(`Loading ${fileUrl} from server`);\n const ongoingLoad = this.ongoingLoads.get(fileUrl);\n if (ongoingLoad) return ongoingLoad;\n\n const loadPromise = fetch(fileUrl)\n .then((response) => response.blob())\n .then((blob) => {\n const originalExtension = fileUrl.split(\".\").pop() || \"\";\n this.modelCache.set(fileUrl, { blob, originalExtension });\n const blobURL = URL.createObjectURL(blob);\n this.ongoingLoads.delete(fileUrl);\n return this.loadFromUrl(blobURL, originalExtension);\n });\n\n this.ongoingLoads.set(fileUrl, loadPromise);\n return loadPromise;\n }\n }\n\n private async loadFromUrl(url: string, extension: string): Promise<GLTF | undefined> {\n if ([\"gltf\", \"glb\"].includes(extension)) {\n return new Promise((resolve, reject) => {\n this.gltfLoader.load(\n url,\n (object: GLTF) => {\n resolve(object);\n },\n undefined,\n (error) => {\n console.error(`Error loading GL(B|TF) from ${url}: ${error}`);\n reject(error);\n },\n );\n });\n } else {\n console.error(`Error: can't recognize ${url} extension: ${extension}`);\n }\n }\n}\n"],
|
5
|
+
"mappings": "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",
|
6
|
+
"names": []
|
7
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
{
|
2
|
+
"name": "@mml-io/3d-web-avatar",
|
3
|
+
"version": "0.0.0-experimental-b7ccddd-20231027",
|
4
|
+
"publishConfig": {
|
5
|
+
"access": "public"
|
6
|
+
},
|
7
|
+
"main": "./build/index.js",
|
8
|
+
"types": "./build/index.d.ts",
|
9
|
+
"type": "module",
|
10
|
+
"files": [
|
11
|
+
"/build"
|
12
|
+
],
|
13
|
+
"scripts": {
|
14
|
+
"build": "tsx ./build.ts --build",
|
15
|
+
"iterate": "tsx ./build.ts --watch",
|
16
|
+
"start": "NODE_ENV=production node build/index.js 2>error.log",
|
17
|
+
"type-check": "tsc --noEmit",
|
18
|
+
"lint": "eslint \"./{src,test}/**/*.{js,jsx,ts,tsx}\" --max-warnings 0",
|
19
|
+
"lint-fix": "eslint \"./{src,test}/**/*.{js,jsx,ts,tsx}\" --fix"
|
20
|
+
},
|
21
|
+
"dependencies": {
|
22
|
+
"react": "^18.2.0",
|
23
|
+
"react-dom": "^18.2.0",
|
24
|
+
"three": "0.153.0"
|
25
|
+
},
|
26
|
+
"devDependencies": {
|
27
|
+
"@types/node": "^20.5.9",
|
28
|
+
"@types/react": "^18.2.21",
|
29
|
+
"@types/react-dom": "^18.2.7",
|
30
|
+
"@types/three": "0.153.0",
|
31
|
+
"esbuild-css-modules-plugin": "3.0.1"
|
32
|
+
},
|
33
|
+
"gitHead": "003d7e57df6dbde6ad9b850fe657db8397b23f97"
|
34
|
+
}
|