@mml-io/3d-web-avatar 0.15.0 → 0.17.0
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/build/character/MMLCharacter.d.ts +6 -6
- package/build/helpers/cloneSkinnedMesh.d.ts +2 -0
- package/build/index.d.ts +1 -2
- package/build/index.js +48 -302
- package/build/index.js.map +4 -4
- package/package.json +9 -8
- package/build/character/ModelLoader.d.ts +0 -13
- package/build/helpers/SkeletonHelpers.d.ts +0 -19
@@ -1,14 +1,14 @@
|
|
1
|
+
import { ModelLoadResult } from "@mml-io/model-loader";
|
1
2
|
import { Object3D } from "three";
|
2
3
|
import { MMLCharacterDescriptionPart } from "../helpers/parseMMLDescription";
|
3
|
-
|
4
|
+
type MMLCharacterModelLoader = {
|
5
|
+
load: (url: string) => Promise<ModelLoadResult>;
|
6
|
+
};
|
4
7
|
export declare class MMLCharacter {
|
5
8
|
private modelLoader;
|
6
|
-
|
7
|
-
private skinnedMeshesParent;
|
8
|
-
private sharedSkeleton;
|
9
|
-
private sharedMatrixWorld;
|
10
|
-
constructor(modelLoader: ModelLoader);
|
9
|
+
constructor(modelLoader: MMLCharacterModelLoader);
|
11
10
|
private createBoneIndexMap;
|
12
11
|
private remapBoneIndices;
|
13
12
|
mergeBodyParts(fullBodyURL: string, bodyParts: Array<MMLCharacterDescriptionPart>): Promise<Object3D>;
|
14
13
|
}
|
14
|
+
export {};
|
package/build/index.d.ts
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
export { MMLCharacter } from "./character/MMLCharacter";
|
2
|
-
export {
|
2
|
+
export { cloneSkinnedMesh } from "./helpers/cloneSkinnedMesh";
|
3
3
|
export { TimeManagerInterface } from "./character/types";
|
4
|
-
export { ModelLoader } from "./character/ModelLoader";
|
5
4
|
export { type LoadingErrors, type MMLCharacterDescriptionPart, type MMLCharacterDescription, parseMMLDescription, } from "./helpers/parseMMLDescription";
|
6
5
|
export { createMMLCharacterString } from "./helpers/createMMLCharacterString";
|
package/build/index.js
CHANGED
@@ -1,177 +1,8 @@
|
|
1
1
|
// src/character/MMLCharacter.ts
|
2
|
-
import {
|
3
|
-
BufferAttribute,
|
4
|
-
Euler,
|
5
|
-
MathUtils,
|
6
|
-
Vector3
|
7
|
-
} from "three";
|
8
|
-
|
9
|
-
// src/helpers/SkeletonHelpers.ts
|
10
|
-
import { Skeleton } from "three";
|
11
|
-
import * as SkeletonUtils from "three/examples/jsm/utils/SkeletonUtils.js";
|
12
|
-
function cloneModel(model) {
|
13
|
-
const clone2 = SkeletonUtils.clone(model);
|
14
|
-
return clone2;
|
15
|
-
}
|
16
|
-
var SkeletonHelpers = class {
|
17
|
-
constructor() {
|
18
|
-
this.debug = false;
|
19
|
-
this.hierarchies = [];
|
20
|
-
this.modelNames = [];
|
21
|
-
}
|
22
|
-
extractBoneHierarchy(node) {
|
23
|
-
if (node.type !== "Bone")
|
24
|
-
return null;
|
25
|
-
const boneMap = /* @__PURE__ */ new Map();
|
26
|
-
node.children.forEach((child) => {
|
27
|
-
const childHierarchy = this.extractBoneHierarchy(child);
|
28
|
-
if (childHierarchy)
|
29
|
-
boneMap.set(child.name, childHierarchy);
|
30
|
-
});
|
31
|
-
return boneMap;
|
32
|
-
}
|
33
|
-
areBoneHierarchiesEqual(a, b, modelNameA, modelNameB, path = []) {
|
34
|
-
let identical = true;
|
35
|
-
const differences = [];
|
36
|
-
if (a.size !== b.size) {
|
37
|
-
differences.push(
|
38
|
-
`Different number of children at path: ${path.join(
|
39
|
-
" -> "
|
40
|
-
)} in models ${modelNameA} and ${modelNameB}.`
|
41
|
-
);
|
42
|
-
identical = false;
|
43
|
-
}
|
44
|
-
for (const [key, value] of a) {
|
45
|
-
if (!b.has(key)) {
|
46
|
-
differences.push(
|
47
|
-
`Bone "${key}" was found in model ${modelNameA} but not in model ${modelNameB} at path: ${path.join(
|
48
|
-
" -> "
|
49
|
-
)}.`
|
50
|
-
);
|
51
|
-
identical = false;
|
52
|
-
continue;
|
53
|
-
}
|
54
|
-
const newPath = [...path, key];
|
55
|
-
const result = this.areBoneHierarchiesEqual(
|
56
|
-
value,
|
57
|
-
b.get(key),
|
58
|
-
modelNameA,
|
59
|
-
modelNameB,
|
60
|
-
newPath
|
61
|
-
);
|
62
|
-
if (!result.identical) {
|
63
|
-
identical = false;
|
64
|
-
differences.push(...result.differences);
|
65
|
-
}
|
66
|
-
}
|
67
|
-
for (const key of b.keys()) {
|
68
|
-
if (!a.has(key)) {
|
69
|
-
differences.push(
|
70
|
-
`Bone "${key}" was found in model ${modelNameB} but not in model ${modelNameA} at path: ${path.join(
|
71
|
-
" -> "
|
72
|
-
)}.`
|
73
|
-
);
|
74
|
-
identical = false;
|
75
|
-
}
|
76
|
-
}
|
77
|
-
return {
|
78
|
-
identical,
|
79
|
-
differences
|
80
|
-
};
|
81
|
-
}
|
82
|
-
extractAndStoreBoneHierarchy(node, modelName) {
|
83
|
-
const newHierarchy = this.extractBoneHierarchy(node);
|
84
|
-
if (!newHierarchy) {
|
85
|
-
console.log(`No bone hierarchy found in the model: ${modelName}.`);
|
86
|
-
return;
|
87
|
-
}
|
88
|
-
this.hierarchies.push(newHierarchy);
|
89
|
-
this.modelNames.push(modelName);
|
90
|
-
}
|
91
|
-
compareLatestHierarchies() {
|
92
|
-
if (this.hierarchies.length < 2)
|
93
|
-
return;
|
94
|
-
const latestHierarchy = this.hierarchies[this.hierarchies.length - 1];
|
95
|
-
const previousHierarchy = this.hierarchies[this.hierarchies.length - 2];
|
96
|
-
const diff = this.areBoneHierarchiesEqual(
|
97
|
-
previousHierarchy,
|
98
|
-
latestHierarchy,
|
99
|
-
this.modelNames[this.modelNames.length - 2],
|
100
|
-
this.modelNames[this.modelNames.length - 1],
|
101
|
-
[]
|
102
|
-
);
|
103
|
-
if (diff.identical) {
|
104
|
-
if (this.debug)
|
105
|
-
console.log("The skeletons are identical.");
|
106
|
-
} else {
|
107
|
-
diff.differences.forEach((difference) => console.log(difference));
|
108
|
-
}
|
109
|
-
}
|
110
|
-
cloneGLTF(gltf, modelName) {
|
111
|
-
const clone2 = {
|
112
|
-
animations: gltf.animations,
|
113
|
-
scene: cloneModel(gltf.scene)
|
114
|
-
};
|
115
|
-
let sharedSkeleton = null;
|
116
|
-
let matrixWorld = null;
|
117
|
-
const skinnedMeshes = {};
|
118
|
-
gltf.scene.traverse((node) => {
|
119
|
-
if (node.type === "SkinnedMesh") {
|
120
|
-
skinnedMeshes[node.name] = node;
|
121
|
-
}
|
122
|
-
});
|
123
|
-
const cloneBones = {};
|
124
|
-
const cloneSkinnedMeshes = {};
|
125
|
-
let hierarchyCheck = false;
|
126
|
-
clone2.scene.traverse((node) => {
|
127
|
-
if (node.isMesh || node.isSkinnedMesh) {
|
128
|
-
node.castShadow = true;
|
129
|
-
node.receiveShadow = true;
|
130
|
-
}
|
131
|
-
if (node.type === "Bone") {
|
132
|
-
if (hierarchyCheck === false) {
|
133
|
-
hierarchyCheck = true;
|
134
|
-
this.extractAndStoreBoneHierarchy(node, modelName);
|
135
|
-
}
|
136
|
-
cloneBones[node.name] = node;
|
137
|
-
}
|
138
|
-
if (node.type === "SkinnedMesh") {
|
139
|
-
cloneSkinnedMeshes[node.name] = node;
|
140
|
-
}
|
141
|
-
});
|
142
|
-
for (const name in skinnedMeshes) {
|
143
|
-
const skinnedMesh = skinnedMeshes[name];
|
144
|
-
const skeleton = skinnedMesh.skeleton;
|
145
|
-
const cloneSkinnedMesh = cloneSkinnedMeshes[name];
|
146
|
-
const orderedCloneBones = [];
|
147
|
-
for (let i = 0; i < skeleton.bones.length; ++i) {
|
148
|
-
const cloneBone = cloneBones[skeleton.bones[i].name];
|
149
|
-
orderedCloneBones.push(cloneBone);
|
150
|
-
}
|
151
|
-
if (sharedSkeleton === null) {
|
152
|
-
sharedSkeleton = new Skeleton(orderedCloneBones, skeleton.boneInverses);
|
153
|
-
}
|
154
|
-
if (matrixWorld === null) {
|
155
|
-
matrixWorld = cloneSkinnedMesh.matrixWorld;
|
156
|
-
}
|
157
|
-
cloneSkinnedMesh.bind(sharedSkeleton, matrixWorld);
|
158
|
-
}
|
159
|
-
return {
|
160
|
-
gltf: clone2,
|
161
|
-
sharedSkeleton,
|
162
|
-
matrixWorld
|
163
|
-
};
|
164
|
-
}
|
165
|
-
};
|
166
|
-
|
167
|
-
// src/character/MMLCharacter.ts
|
2
|
+
import { BufferAttribute, Group, MathUtils } from "three";
|
168
3
|
var MMLCharacter = class {
|
169
4
|
constructor(modelLoader) {
|
170
5
|
this.modelLoader = modelLoader;
|
171
|
-
this.skeletonHelpers = new SkeletonHelpers();
|
172
|
-
this.skinnedMeshesParent = null;
|
173
|
-
this.sharedSkeleton = null;
|
174
|
-
this.sharedMatrixWorld = null;
|
175
6
|
}
|
176
7
|
createBoneIndexMap(originSkeleton, targetSkeleton) {
|
177
8
|
const boneIndexMap = /* @__PURE__ */ new Map();
|
@@ -184,8 +15,7 @@ var MMLCharacter = class {
|
|
184
15
|
}
|
185
16
|
return boneIndexMap;
|
186
17
|
}
|
187
|
-
remapBoneIndices(skinnedMesh) {
|
188
|
-
const targetSkeleton = this.sharedSkeleton;
|
18
|
+
remapBoneIndices(skinnedMesh, targetSkeleton) {
|
189
19
|
const originSkeleton = skinnedMesh.skeleton;
|
190
20
|
const originGeometry = skinnedMesh.geometry;
|
191
21
|
const boneIndexMap = this.createBoneIndexMap(originSkeleton, targetSkeleton);
|
@@ -206,8 +36,8 @@ var MMLCharacter = class {
|
|
206
36
|
);
|
207
37
|
}
|
208
38
|
async mergeBodyParts(fullBodyURL, bodyParts) {
|
209
|
-
const
|
210
|
-
const
|
39
|
+
const group = new Group();
|
40
|
+
const fullBodyAssetPromise = this.modelLoader.load(fullBodyURL);
|
211
41
|
const assetPromises = bodyParts.map((part) => {
|
212
42
|
return new Promise((resolve) => {
|
213
43
|
this.modelLoader.load(part.url).then((asset) => {
|
@@ -215,11 +45,11 @@ var MMLCharacter = class {
|
|
215
45
|
});
|
216
46
|
});
|
217
47
|
});
|
48
|
+
const fullBodyAsset = await fullBodyAssetPromise;
|
218
49
|
const assets = await Promise.all(assetPromises);
|
219
|
-
const
|
220
|
-
this.skinnedMeshesParent = null;
|
50
|
+
const rawBodyGltf = fullBodyAsset.group;
|
221
51
|
const availableBones = /* @__PURE__ */ new Map();
|
222
|
-
|
52
|
+
rawBodyGltf.traverse((child) => {
|
223
53
|
const asBone = child;
|
224
54
|
if (asBone.isBone) {
|
225
55
|
availableBones.set(child.name, asBone);
|
@@ -228,17 +58,32 @@ var MMLCharacter = class {
|
|
228
58
|
if (asSkinnedMesh.isSkinnedMesh) {
|
229
59
|
asSkinnedMesh.castShadow = true;
|
230
60
|
asSkinnedMesh.receiveShadow = true;
|
231
|
-
if (this.skinnedMeshesParent === null) {
|
232
|
-
this.skinnedMeshesParent = asSkinnedMesh.parent;
|
233
|
-
}
|
234
61
|
}
|
235
62
|
});
|
236
|
-
|
237
|
-
|
63
|
+
const foundSkinnedMeshes = [];
|
64
|
+
rawBodyGltf.traverse((child) => {
|
65
|
+
const asSkinnedMesh = child;
|
66
|
+
if (asSkinnedMesh.isSkinnedMesh) {
|
67
|
+
foundSkinnedMeshes.push(asSkinnedMesh);
|
68
|
+
}
|
69
|
+
});
|
70
|
+
if (foundSkinnedMeshes.length === 0) {
|
71
|
+
throw new Error("No skinned mesh in base model file");
|
72
|
+
}
|
73
|
+
if (foundSkinnedMeshes.length > 1) {
|
74
|
+
console.warn(
|
75
|
+
"Multiple skinned meshes in base model file. Expected 1. Using first for skeleton."
|
76
|
+
);
|
77
|
+
}
|
78
|
+
const skinnedMesh = foundSkinnedMeshes[0];
|
79
|
+
group.add(...foundSkinnedMeshes);
|
80
|
+
const sharedSkeleton = skinnedMesh.skeleton;
|
81
|
+
group.add(skinnedMesh.skeleton.bones[0]);
|
82
|
+
const sharedMatrixWorld = skinnedMesh.matrixWorld;
|
238
83
|
for (const loadingAsset of assets) {
|
239
84
|
const part = loadingAsset.part;
|
240
|
-
const
|
241
|
-
const modelGroup =
|
85
|
+
const rawGltf = loadingAsset.asset;
|
86
|
+
const modelGroup = rawGltf.group;
|
242
87
|
if (part.socket) {
|
243
88
|
const socketName = part.socket.socket;
|
244
89
|
let bone = availableBones.get("root");
|
@@ -250,144 +95,46 @@ var MMLCharacter = class {
|
|
250
95
|
);
|
251
96
|
}
|
252
97
|
if (bone) {
|
253
|
-
modelGroup.position.set(0, 0, 0);
|
254
|
-
modelGroup.rotation.set(0, 0, 0);
|
255
|
-
modelGroup.scale.set(1, 1, 1);
|
256
98
|
bone.add(modelGroup);
|
257
|
-
modelGroup.
|
258
|
-
const offsetPosition = new Vector3(
|
99
|
+
modelGroup.position.set(
|
259
100
|
part.socket.position.x,
|
260
101
|
part.socket.position.y,
|
261
102
|
part.socket.position.z
|
262
103
|
);
|
263
|
-
modelGroup.
|
264
|
-
const offsetRotation = new Euler(
|
104
|
+
modelGroup.rotation.set(
|
265
105
|
MathUtils.degToRad(part.socket.rotation.x),
|
266
106
|
MathUtils.degToRad(part.socket.rotation.y),
|
267
107
|
MathUtils.degToRad(part.socket.rotation.z)
|
268
108
|
);
|
269
|
-
modelGroup.setRotationFromEuler(offsetRotation);
|
270
109
|
modelGroup.scale.set(part.socket.scale.x, part.socket.scale.y, part.socket.scale.z);
|
271
110
|
}
|
272
111
|
} else {
|
112
|
+
const skinnedMeshes = [];
|
273
113
|
modelGroup.traverse((child) => {
|
274
|
-
var _a;
|
275
114
|
const asSkinnedMesh = child;
|
276
115
|
if (asSkinnedMesh.isSkinnedMesh) {
|
277
|
-
|
278
|
-
this.remapBoneIndices(skinnedMeshClone);
|
279
|
-
skinnedMeshClone.castShadow = true;
|
280
|
-
skinnedMeshClone.receiveShadow = true;
|
281
|
-
skinnedMeshClone.bind(this.sharedSkeleton, this.sharedMatrixWorld);
|
282
|
-
skinnedMeshClone.children = [];
|
283
|
-
(_a = this.skinnedMeshesParent) == null ? void 0 : _a.add(skinnedMeshClone);
|
116
|
+
skinnedMeshes.push(asSkinnedMesh);
|
284
117
|
}
|
285
118
|
});
|
119
|
+
for (const skinnedMeshPart of skinnedMeshes) {
|
120
|
+
this.remapBoneIndices(skinnedMeshPart, sharedSkeleton);
|
121
|
+
skinnedMeshPart.castShadow = true;
|
122
|
+
skinnedMeshPart.receiveShadow = true;
|
123
|
+
skinnedMeshPart.bind(sharedSkeleton, sharedMatrixWorld);
|
124
|
+
skinnedMeshPart.children = [];
|
125
|
+
group.add(skinnedMeshPart);
|
126
|
+
}
|
286
127
|
}
|
287
128
|
}
|
288
|
-
return
|
129
|
+
return group;
|
289
130
|
}
|
290
131
|
};
|
291
132
|
|
292
|
-
// src/
|
293
|
-
import
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
super(manager);
|
298
|
-
this.blobCache = /* @__PURE__ */ new Map();
|
299
|
-
}
|
300
|
-
setBlobUrl(originalUrl, blobUrl) {
|
301
|
-
this.blobCache.set(originalUrl, blobUrl);
|
302
|
-
}
|
303
|
-
getBlobUrl(originalUrl) {
|
304
|
-
return this.blobCache.get(originalUrl);
|
305
|
-
}
|
306
|
-
load(url, onLoad, onProgress, onError) {
|
307
|
-
const blobUrl = this.getBlobUrl(url);
|
308
|
-
const effectiveUrl = blobUrl || url;
|
309
|
-
super.load(effectiveUrl, onLoad, onProgress, onError);
|
310
|
-
}
|
311
|
-
};
|
312
|
-
var LRUCache = class {
|
313
|
-
constructor(maxSize = 100) {
|
314
|
-
this.maxSize = maxSize;
|
315
|
-
this.cache = /* @__PURE__ */ new Map();
|
316
|
-
}
|
317
|
-
get(key) {
|
318
|
-
const item = this.cache.get(key);
|
319
|
-
if (item) {
|
320
|
-
this.cache.delete(key);
|
321
|
-
this.cache.set(key, item);
|
322
|
-
}
|
323
|
-
return item;
|
324
|
-
}
|
325
|
-
set(key, value) {
|
326
|
-
if (this.cache.size >= this.maxSize) {
|
327
|
-
const oldestKey = this.cache.keys().next().value;
|
328
|
-
this.cache.delete(oldestKey);
|
329
|
-
}
|
330
|
-
this.cache.set(key, value);
|
331
|
-
}
|
332
|
-
};
|
333
|
-
var _ModelLoader = class _ModelLoader {
|
334
|
-
constructor(maxCacheSize = 100, debug = false) {
|
335
|
-
this.debug = debug;
|
336
|
-
this.ongoingLoads = /* @__PURE__ */ new Map();
|
337
|
-
this.loadingManager = new LoadingManager();
|
338
|
-
this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
|
339
|
-
this.modelCache = new LRUCache(maxCacheSize);
|
340
|
-
}
|
341
|
-
static getInstance() {
|
342
|
-
if (!_ModelLoader.instance) {
|
343
|
-
_ModelLoader.instance = new _ModelLoader();
|
344
|
-
}
|
345
|
-
return _ModelLoader.instance;
|
346
|
-
}
|
347
|
-
async load(fileUrl) {
|
348
|
-
const cachedModel = this.modelCache.get(fileUrl);
|
349
|
-
if (cachedModel) {
|
350
|
-
if (this.debug === true) {
|
351
|
-
console.log(`Loading ${fileUrl.split("/").pop()} from cache`);
|
352
|
-
}
|
353
|
-
const blobURL = URL.createObjectURL(cachedModel.blob);
|
354
|
-
this.gltfLoader.setBlobUrl(fileUrl, blobURL);
|
355
|
-
return this.loadFromUrl(blobURL);
|
356
|
-
} else {
|
357
|
-
if (this.debug === true) {
|
358
|
-
console.log(`Loading ${fileUrl} from server`);
|
359
|
-
}
|
360
|
-
const ongoingLoad = this.ongoingLoads.get(fileUrl);
|
361
|
-
if (ongoingLoad)
|
362
|
-
return ongoingLoad;
|
363
|
-
const loadPromise = fetch(fileUrl).then((response) => response.blob()).then((blob) => {
|
364
|
-
this.modelCache.set(fileUrl, { blob });
|
365
|
-
const blobURL = URL.createObjectURL(blob);
|
366
|
-
this.ongoingLoads.delete(fileUrl);
|
367
|
-
return this.loadFromUrl(blobURL);
|
368
|
-
});
|
369
|
-
this.ongoingLoads.set(fileUrl, loadPromise);
|
370
|
-
return loadPromise;
|
371
|
-
}
|
372
|
-
}
|
373
|
-
async loadFromUrl(url) {
|
374
|
-
return new Promise((resolve, reject) => {
|
375
|
-
this.gltfLoader.load(
|
376
|
-
url,
|
377
|
-
(object) => {
|
378
|
-
resolve(object);
|
379
|
-
},
|
380
|
-
void 0,
|
381
|
-
(error) => {
|
382
|
-
console.error(`Error loading model from ${url}: ${error}`);
|
383
|
-
reject(error);
|
384
|
-
}
|
385
|
-
);
|
386
|
-
});
|
387
|
-
}
|
388
|
-
};
|
389
|
-
_ModelLoader.instance = null;
|
390
|
-
var ModelLoader = _ModelLoader;
|
133
|
+
// src/helpers/cloneSkinnedMesh.ts
|
134
|
+
import * as SkeletonUtils from "three/examples/jsm/utils/SkeletonUtils.js";
|
135
|
+
function cloneSkinnedMesh(model) {
|
136
|
+
return SkeletonUtils.clone(model);
|
137
|
+
}
|
391
138
|
|
392
139
|
// src/helpers/parseMMLDescription.ts
|
393
140
|
var parseMMLDescription = (mmlDescription) => {
|
@@ -487,8 +234,7 @@ var createMMLCharacterString = (characterDescription) => {
|
|
487
234
|
};
|
488
235
|
export {
|
489
236
|
MMLCharacter,
|
490
|
-
|
491
|
-
cloneModel,
|
237
|
+
cloneSkinnedMesh,
|
492
238
|
createMMLCharacterString,
|
493
239
|
parseMMLDescription
|
494
240
|
};
|
package/build/index.js.map
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"version": 3,
|
3
|
-
"sources": ["../src/character/MMLCharacter.ts", "../src/helpers/
|
4
|
-
"sourcesContent": ["import {\n Bone,\n BufferAttribute,\n Euler,\n Group,\n MathUtils,\n Matrix4,\n MeshStandardMaterial,\n Object3D,\n Skeleton,\n SkinnedMesh,\n Vector3,\n} from \"three\";\nimport { GLTF } from \"three/examples/jsm/loaders/GLTFLoader.js\";\n\nimport { MMLCharacterDescriptionPart } from \"../helpers/parseMMLDescription\";\nimport { SkeletonHelpers } from \"../helpers/SkeletonHelpers\";\n\nimport { ModelLoader } from \"./ModelLoader\";\n\nexport class MMLCharacter {\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 private createBoneIndexMap(\n originSkeleton: Skeleton,\n targetSkeleton: Skeleton,\n ): Map<number, number> {\n const boneIndexMap = new Map<number, number>();\n\n for (let i = 0; i < originSkeleton.bones.length; i++) {\n const originBone = originSkeleton.bones[i];\n const targetBone = targetSkeleton.bones.find((bone) => bone.name === originBone.name);\n if (targetBone) {\n boneIndexMap.set(i, targetSkeleton.bones.indexOf(targetBone));\n }\n }\n return boneIndexMap;\n }\n\n private remapBoneIndices(skinnedMesh: SkinnedMesh): void {\n const targetSkeleton = this.sharedSkeleton!;\n const originSkeleton = skinnedMesh.skeleton;\n const originGeometry = skinnedMesh.geometry;\n\n const boneIndexMap = this.createBoneIndexMap(originSkeleton, targetSkeleton);\n\n const newSkinIndexArray = [];\n for (let i = 0; i < originGeometry.attributes.skinIndex.array.length; i++) {\n const originIndex = originGeometry.attributes.skinIndex.array[i];\n const targetIndex = boneIndexMap.get(originIndex);\n if (targetIndex !== undefined) {\n newSkinIndexArray.push(targetIndex);\n } else {\n console.error(\"Missing bone index\", originIndex);\n newSkinIndexArray.push(0);\n }\n }\n skinnedMesh.geometry.attributes.skinIndex = new BufferAttribute(\n new Uint8Array(newSkinIndexArray),\n 4,\n );\n }\n\n public async mergeBodyParts(\n fullBodyURL: string,\n bodyParts: Array<MMLCharacterDescriptionPart>,\n ): Promise<Object3D> {\n const fullBodyAsset = await this.modelLoader.load(fullBodyURL);\n const fullBodyGLTF = this.skeletonHelpers.cloneGLTF(fullBodyAsset as GLTF, \"fullBody\");\n const assetPromises: Array<Promise<{ asset: GLTF; part: MMLCharacterDescriptionPart }>> =\n bodyParts.map((part) => {\n return new Promise((resolve) => {\n this.modelLoader.load(part.url).then((asset) => {\n resolve({ asset: asset!, part });\n });\n });\n });\n const assets = await Promise.all(assetPromises);\n\n const fullBodyModelGroup = fullBodyGLTF.gltf.scene;\n\n this.skinnedMeshesParent = null;\n\n const availableBones = new Map<string, Bone>();\n fullBodyModelGroup.traverse((child) => {\n const asBone = child as Bone;\n if (asBone.isBone) {\n availableBones.set(child.name, asBone);\n }\n\n const asSkinnedMesh = child as SkinnedMesh;\n if (asSkinnedMesh.isSkinnedMesh) {\n asSkinnedMesh.castShadow = true;\n asSkinnedMesh.receiveShadow = true;\n if (this.skinnedMeshesParent === null) {\n this.skinnedMeshesParent = asSkinnedMesh.parent as Group;\n }\n }\n });\n this.sharedSkeleton = fullBodyGLTF.sharedSkeleton;\n this.sharedMatrixWorld = fullBodyGLTF.matrixWorld;\n\n for (const loadingAsset of assets) {\n const part = loadingAsset.part;\n const gltf = this.skeletonHelpers.cloneGLTF(loadingAsset.asset, part.url);\n const modelGroup = gltf.gltf.scene;\n if (part.socket) {\n const socketName = part.socket.socket;\n let bone = availableBones.get(\"root\");\n if (availableBones.has(socketName)) {\n bone = availableBones.get(socketName);\n } else {\n console.warn(\n `WARNING: no bone found for [${socketName}] socket. Attatching to Root bone`,\n );\n }\n if (bone) {\n modelGroup.position.set(0, 0, 0);\n modelGroup.rotation.set(0, 0, 0);\n modelGroup.scale.set(1, 1, 1);\n\n bone.add(modelGroup);\n\n modelGroup.rotateZ(-Math.PI / 2);\n\n const offsetPosition = new Vector3(\n part.socket.position.x,\n part.socket.position.y,\n part.socket.position.z,\n );\n modelGroup.position.copy(offsetPosition);\n\n const offsetRotation = new Euler(\n MathUtils.degToRad(part.socket.rotation.x),\n MathUtils.degToRad(part.socket.rotation.y),\n MathUtils.degToRad(part.socket.rotation.z),\n );\n modelGroup.setRotationFromEuler(offsetRotation);\n\n modelGroup.scale.set(part.socket.scale.x, part.socket.scale.y, part.socket.scale.z);\n }\n } else {\n modelGroup.traverse((child) => {\n const asSkinnedMesh = child as SkinnedMesh;\n if (asSkinnedMesh.isSkinnedMesh) {\n const skinnedMeshClone = child.clone(true) as SkinnedMesh;\n this.remapBoneIndices(skinnedMeshClone);\n skinnedMeshClone.castShadow = true;\n skinnedMeshClone.receiveShadow = true;\n skinnedMeshClone.bind(this.sharedSkeleton!, this.sharedMatrixWorld!);\n skinnedMeshClone.children = [];\n this.skinnedMeshesParent?.add(skinnedMeshClone);\n }\n });\n }\n }\n return fullBodyGLTF!.gltf.scene as Object3D;\n }\n}\n", "import { Bone, Group, Matrix4, Mesh, Object3D, Skeleton, SkinnedMesh } from \"three\";\nimport { GLTF } from \"three/examples/jsm/loaders/GLTFLoader.js\";\nimport * as SkeletonUtils from \"three/examples/jsm/utils/SkeletonUtils.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 function cloneModel(model: Group) {\n const clone = SkeletonUtils.clone(model);\n return clone;\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: cloneModel(gltf.scene) as Group,\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 as Mesh).isMesh || (node as SkinnedMesh).isSkinnedMesh) {\n node.castShadow = true;\n node.receiveShadow = true;\n }\n if (node.type === \"Bone\") {\n if (hierarchyCheck === false) {\n hierarchyCheck = true;\n this.extractAndStoreBoneHierarchy(node, modelName);\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 { 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 const effectiveUrl = blobUrl || url;\n super.load(effectiveUrl, onLoad, onProgress, onError);\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}\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(\n maxCacheSize: number = 100,\n private debug: boolean = false,\n ) {\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 if (this.debug === true) {\n console.log(`Loading ${fileUrl.split(\"/\").pop()} from cache`);\n }\n const blobURL = URL.createObjectURL(cachedModel.blob);\n this.gltfLoader.setBlobUrl(fileUrl, blobURL);\n return this.loadFromUrl(blobURL);\n } else {\n if (this.debug === true) {\n console.log(`Loading ${fileUrl} from server`);\n }\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 this.modelCache.set(fileUrl, { blob });\n const blobURL = URL.createObjectURL(blob);\n this.ongoingLoads.delete(fileUrl);\n return this.loadFromUrl(blobURL);\n });\n\n this.ongoingLoads.set(fileUrl, loadPromise);\n return loadPromise;\n }\n }\n\n private async loadFromUrl(url: string): Promise<GLTF | undefined> {\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 model from ${url}: ${error}`);\n reject(error);\n },\n );\n });\n }\n}\n", "export type MMLCharacterDescriptionPart = {\n url: string;\n type?: string;\n socket?: {\n socket: string;\n position: { x: number; y: number; z: number };\n scale: { x: number; y: number; z: number };\n rotation: { x: number; y: number; z: number };\n };\n};\n\nexport type MMLCharacterDescription = {\n base: MMLCharacterDescriptionPart;\n parts: MMLCharacterDescriptionPart[];\n};\n\nexport type LoadingErrors = string[];\n\nexport const parseMMLDescription = (\n mmlDescription: string,\n): [MMLCharacterDescription, LoadingErrors] => {\n const parser: DOMParser = new DOMParser();\n const doc = parser.parseFromString(mmlDescription, \"text/html\");\n\n const tag = (count: number) => {\n return count > 1 ? \"tags\" : \"tag\";\n };\n\n const errors: string[] = [];\n\n const warn = (errorMessage: string) => {\n errors.push(errorMessage);\n console.warn(errorMessage);\n };\n\n const characters = Array.from(doc.body.children).filter(\n (child) => child.tagName.toLowerCase() === \"m-character\",\n );\n const validCharacter = characters.shift();\n\n if (characters.length > 0) {\n const tagStr = tag(characters.length);\n warn(\n `ignoring ${characters.length} extra <m-character> ${tagStr} found in the root of the document (only the first one is valid).`,\n );\n }\n\n const nestedCharacters = doc.querySelectorAll(\"body * m-character\");\n if (nestedCharacters.length > 0) {\n const tagStr = tag(nestedCharacters.length);\n warn(\n `ignoring ${nestedCharacters.length} nested <m-character> ${tagStr} found within other tags. A valid <m-character> tag must be at the root of the document.`,\n );\n }\n\n const rootModels = Array.from(doc.body.children).filter(\n (child) => child.tagName.toLowerCase() === \"m-model\",\n );\n if (rootModels.length > 0) {\n const tagStr = tag(rootModels.length);\n warn(\n `ignoring ${rootModels.length} <m-model> ${tagStr} were found at the root of the document (<m-model> tags must be children of a valid <m-character> tag).`,\n );\n }\n\n let base: MMLCharacterDescriptionPart = { url: \"\" };\n let parts: MMLCharacterDescriptionPart[] = [];\n\n if (validCharacter) {\n const baseSrc = validCharacter.getAttribute(\"src\") ?? \"\";\n base = { url: baseSrc };\n\n const directModelChildren = Array.from(validCharacter.children).filter(\n (child) => child.tagName.toLowerCase() === \"m-model\",\n );\n parts = directModelChildren.map((model) => {\n const partSrc = model.getAttribute(\"src\") ?? \"\";\n\n const socketAttr = model.getAttribute(\"socket\");\n const position = {\n x: parseFloat(model.getAttribute(\"x\") ?? \"0\") || 0,\n y: parseFloat(model.getAttribute(\"y\") ?? \"0\") || 0,\n z: parseFloat(model.getAttribute(\"z\") ?? \"0\") || 0,\n };\n const scale = {\n x: parseFloat(model.getAttribute(\"sx\") ?? \"1\") || 1,\n y: parseFloat(model.getAttribute(\"sy\") ?? \"1\") || 1,\n z: parseFloat(model.getAttribute(\"sz\") ?? \"1\") || 1,\n };\n const rotation = {\n x: parseFloat(model.getAttribute(\"rx\") ?? \"0\") || 0,\n y: parseFloat(model.getAttribute(\"ry\") ?? \"0\") || 0,\n z: parseFloat(model.getAttribute(\"rz\") ?? \"0\") || 0,\n };\n\n const socketObj = socketAttr ? { socket: socketAttr, position, scale, rotation } : undefined;\n\n return { url: partSrc, socket: socketObj };\n });\n\n const wrappedModelTags = Array.from(doc.querySelectorAll(\"m-character m-model\")).filter(\n (model) => !directModelChildren.includes(model),\n );\n if (wrappedModelTags.length > 0) {\n const tagStr = tag(wrappedModelTags.length);\n warn(\n `ignoring ${wrappedModelTags.length} <m-model> ${tagStr} that were found wrapped inside tags other than a valid <m-character> tag.`,\n );\n }\n } else {\n warn(`No valid <m-character> tag was found in the provided document.`);\n }\n\n const characterDescription: MMLCharacterDescription = {\n base: base,\n parts: parts,\n };\n\n return [characterDescription, errors];\n};\n", "import { MMLCharacterDescription } from \"./parseMMLDescription\";\n\nexport const createMMLCharacterString = (characterDescription: MMLCharacterDescription): string => {\n const base = characterDescription.base.url;\n\n const partsTags = characterDescription.parts.map(\n (part) => `<m-model src=\"${part.url}\"${part.type ? ` type=\"${part.type}\"` : \"\"}></m-model>`,\n );\n\n return `<m-character src=\"${base}\">\n ${partsTags.join(\"\\n \")}\n</m-character>`;\n};\n"],
|
5
|
-
"mappings": "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",
|
6
|
-
"names": [
|
3
|
+
"sources": ["../src/character/MMLCharacter.ts", "../src/helpers/cloneSkinnedMesh.ts", "../src/helpers/parseMMLDescription.ts", "../src/helpers/createMMLCharacterString.ts"],
|
4
|
+
"sourcesContent": ["import { ModelLoadResult } from \"@mml-io/model-loader\";\nimport { Bone, BufferAttribute, Group, MathUtils, Object3D, Skeleton, SkinnedMesh } from \"three\";\n\nimport { MMLCharacterDescriptionPart } from \"../helpers/parseMMLDescription\";\n\ntype MMLCharacterModelLoader = {\n load: (url: string) => Promise<ModelLoadResult>;\n};\n\nexport class MMLCharacter {\n constructor(private modelLoader: MMLCharacterModelLoader) {}\n\n private createBoneIndexMap(\n originSkeleton: Skeleton,\n targetSkeleton: Skeleton,\n ): Map<number, number> {\n const boneIndexMap = new Map<number, number>();\n\n for (let i = 0; i < originSkeleton.bones.length; i++) {\n const originBone = originSkeleton.bones[i];\n const targetBone = targetSkeleton.bones.find((bone) => bone.name === originBone.name);\n if (targetBone) {\n boneIndexMap.set(i, targetSkeleton.bones.indexOf(targetBone));\n }\n }\n return boneIndexMap;\n }\n\n private remapBoneIndices(skinnedMesh: SkinnedMesh, targetSkeleton: Skeleton): void {\n const originSkeleton = skinnedMesh.skeleton;\n const originGeometry = skinnedMesh.geometry;\n\n const boneIndexMap = this.createBoneIndexMap(originSkeleton, targetSkeleton);\n\n const newSkinIndexArray = [];\n for (let i = 0; i < originGeometry.attributes.skinIndex.array.length; i++) {\n const originIndex = originGeometry.attributes.skinIndex.array[i];\n const targetIndex = boneIndexMap.get(originIndex);\n if (targetIndex !== undefined) {\n newSkinIndexArray.push(targetIndex);\n } else {\n console.error(\"Missing bone index\", originIndex);\n newSkinIndexArray.push(0);\n }\n }\n skinnedMesh.geometry.attributes.skinIndex = new BufferAttribute(\n new Uint8Array(newSkinIndexArray),\n 4,\n );\n }\n\n public async mergeBodyParts(\n fullBodyURL: string,\n bodyParts: Array<MMLCharacterDescriptionPart>,\n ): Promise<Object3D> {\n const group = new Group();\n\n const fullBodyAssetPromise = this.modelLoader.load(fullBodyURL);\n\n const assetPromises: Array<\n Promise<{ asset: ModelLoadResult; part: MMLCharacterDescriptionPart }>\n > = bodyParts.map((part) => {\n return new Promise((resolve) => {\n this.modelLoader.load(part.url).then((asset) => {\n resolve({ asset, part });\n });\n });\n });\n\n const fullBodyAsset = await fullBodyAssetPromise;\n const assets = await Promise.all(assetPromises);\n\n const rawBodyGltf = fullBodyAsset.group;\n const availableBones = new Map<string, Bone>();\n rawBodyGltf.traverse((child) => {\n const asBone = child as Bone;\n if (asBone.isBone) {\n availableBones.set(child.name, asBone);\n }\n\n const asSkinnedMesh = child as SkinnedMesh;\n if (asSkinnedMesh.isSkinnedMesh) {\n asSkinnedMesh.castShadow = true;\n asSkinnedMesh.receiveShadow = true;\n }\n });\n const foundSkinnedMeshes: Array<SkinnedMesh> = [];\n rawBodyGltf.traverse((child) => {\n const asSkinnedMesh = child as SkinnedMesh;\n if (asSkinnedMesh.isSkinnedMesh) {\n foundSkinnedMeshes.push(asSkinnedMesh);\n }\n });\n\n if (foundSkinnedMeshes.length === 0) {\n throw new Error(\"No skinned mesh in base model file\");\n }\n if (foundSkinnedMeshes.length > 1) {\n console.warn(\n \"Multiple skinned meshes in base model file. Expected 1. Using first for skeleton.\",\n );\n }\n const skinnedMesh = foundSkinnedMeshes[0];\n group.add(...foundSkinnedMeshes);\n const sharedSkeleton = skinnedMesh.skeleton;\n group.add(skinnedMesh.skeleton.bones[0]);\n const sharedMatrixWorld = skinnedMesh.matrixWorld;\n\n for (const loadingAsset of assets) {\n const part = loadingAsset.part;\n const rawGltf = loadingAsset.asset;\n\n const modelGroup = rawGltf.group;\n if (part.socket) {\n const socketName = part.socket.socket;\n let bone = availableBones.get(\"root\");\n if (availableBones.has(socketName)) {\n bone = availableBones.get(socketName);\n } else {\n console.warn(\n `WARNING: no bone found for [${socketName}] socket. Attatching to Root bone`,\n );\n }\n if (bone) {\n bone.add(modelGroup);\n\n modelGroup.position.set(\n part.socket.position.x,\n part.socket.position.y,\n part.socket.position.z,\n );\n\n modelGroup.rotation.set(\n MathUtils.degToRad(part.socket.rotation.x),\n MathUtils.degToRad(part.socket.rotation.y),\n MathUtils.degToRad(part.socket.rotation.z),\n );\n\n modelGroup.scale.set(part.socket.scale.x, part.socket.scale.y, part.socket.scale.z);\n }\n } else {\n const skinnedMeshes: Array<SkinnedMesh> = [];\n modelGroup.traverse((child) => {\n const asSkinnedMesh = child as SkinnedMesh;\n if (asSkinnedMesh.isSkinnedMesh) {\n skinnedMeshes.push(asSkinnedMesh);\n }\n });\n for (const skinnedMeshPart of skinnedMeshes) {\n this.remapBoneIndices(skinnedMeshPart, sharedSkeleton);\n skinnedMeshPart.castShadow = true;\n skinnedMeshPart.receiveShadow = true;\n skinnedMeshPart.bind(sharedSkeleton, sharedMatrixWorld!);\n skinnedMeshPart.children = [];\n group.add(skinnedMeshPart);\n }\n }\n }\n return group;\n }\n}\n", "import { Group } from \"three\";\nimport * as SkeletonUtils from \"three/examples/jsm/utils/SkeletonUtils.js\";\n\nexport function cloneSkinnedMesh(model: Group) {\n return SkeletonUtils.clone(model);\n}\n", "export type MMLCharacterDescriptionPart = {\n url: string;\n type?: string;\n socket?: {\n socket: string;\n position: { x: number; y: number; z: number };\n scale: { x: number; y: number; z: number };\n rotation: { x: number; y: number; z: number };\n };\n};\n\nexport type MMLCharacterDescription = {\n base: MMLCharacterDescriptionPart;\n parts: MMLCharacterDescriptionPart[];\n};\n\nexport type LoadingErrors = string[];\n\nexport const parseMMLDescription = (\n mmlDescription: string,\n): [MMLCharacterDescription, LoadingErrors] => {\n const parser: DOMParser = new DOMParser();\n const doc = parser.parseFromString(mmlDescription, \"text/html\");\n\n const tag = (count: number) => {\n return count > 1 ? \"tags\" : \"tag\";\n };\n\n const errors: string[] = [];\n\n const warn = (errorMessage: string) => {\n errors.push(errorMessage);\n console.warn(errorMessage);\n };\n\n const characters = Array.from(doc.body.children).filter(\n (child) => child.tagName.toLowerCase() === \"m-character\",\n );\n const validCharacter = characters.shift();\n\n if (characters.length > 0) {\n const tagStr = tag(characters.length);\n warn(\n `ignoring ${characters.length} extra <m-character> ${tagStr} found in the root of the document (only the first one is valid).`,\n );\n }\n\n const nestedCharacters = doc.querySelectorAll(\"body * m-character\");\n if (nestedCharacters.length > 0) {\n const tagStr = tag(nestedCharacters.length);\n warn(\n `ignoring ${nestedCharacters.length} nested <m-character> ${tagStr} found within other tags. A valid <m-character> tag must be at the root of the document.`,\n );\n }\n\n const rootModels = Array.from(doc.body.children).filter(\n (child) => child.tagName.toLowerCase() === \"m-model\",\n );\n if (rootModels.length > 0) {\n const tagStr = tag(rootModels.length);\n warn(\n `ignoring ${rootModels.length} <m-model> ${tagStr} were found at the root of the document (<m-model> tags must be children of a valid <m-character> tag).`,\n );\n }\n\n let base: MMLCharacterDescriptionPart = { url: \"\" };\n let parts: MMLCharacterDescriptionPart[] = [];\n\n if (validCharacter) {\n const baseSrc = validCharacter.getAttribute(\"src\") ?? \"\";\n base = { url: baseSrc };\n\n const directModelChildren = Array.from(validCharacter.children).filter(\n (child) => child.tagName.toLowerCase() === \"m-model\",\n );\n parts = directModelChildren.map((model) => {\n const partSrc = model.getAttribute(\"src\") ?? \"\";\n\n const socketAttr = model.getAttribute(\"socket\");\n const position = {\n x: parseFloat(model.getAttribute(\"x\") ?? \"0\") || 0,\n y: parseFloat(model.getAttribute(\"y\") ?? \"0\") || 0,\n z: parseFloat(model.getAttribute(\"z\") ?? \"0\") || 0,\n };\n const scale = {\n x: parseFloat(model.getAttribute(\"sx\") ?? \"1\") || 1,\n y: parseFloat(model.getAttribute(\"sy\") ?? \"1\") || 1,\n z: parseFloat(model.getAttribute(\"sz\") ?? \"1\") || 1,\n };\n const rotation = {\n x: parseFloat(model.getAttribute(\"rx\") ?? \"0\") || 0,\n y: parseFloat(model.getAttribute(\"ry\") ?? \"0\") || 0,\n z: parseFloat(model.getAttribute(\"rz\") ?? \"0\") || 0,\n };\n\n const socketObj = socketAttr ? { socket: socketAttr, position, scale, rotation } : undefined;\n\n return { url: partSrc, socket: socketObj };\n });\n\n const wrappedModelTags = Array.from(doc.querySelectorAll(\"m-character m-model\")).filter(\n (model) => !directModelChildren.includes(model),\n );\n if (wrappedModelTags.length > 0) {\n const tagStr = tag(wrappedModelTags.length);\n warn(\n `ignoring ${wrappedModelTags.length} <m-model> ${tagStr} that were found wrapped inside tags other than a valid <m-character> tag.`,\n );\n }\n } else {\n warn(`No valid <m-character> tag was found in the provided document.`);\n }\n\n const characterDescription: MMLCharacterDescription = {\n base: base,\n parts: parts,\n };\n\n return [characterDescription, errors];\n};\n", "import { MMLCharacterDescription } from \"./parseMMLDescription\";\n\nexport const createMMLCharacterString = (characterDescription: MMLCharacterDescription): string => {\n const base = characterDescription.base.url;\n\n const partsTags = characterDescription.parts.map(\n (part) => `<m-model src=\"${part.url}\"${part.type ? ` type=\"${part.type}\"` : \"\"}></m-model>`,\n );\n\n return `<m-character src=\"${base}\">\n ${partsTags.join(\"\\n \")}\n</m-character>`;\n};\n"],
|
5
|
+
"mappings": ";AACA,SAAe,iBAAiB,OAAO,iBAAkD;AAQlF,IAAM,eAAN,MAAmB;AAAA,EACxB,YAAoB,aAAsC;AAAtC;AAAA,EAAuC;AAAA,EAEnD,mBACN,gBACA,gBACqB;AACrB,UAAM,eAAe,oBAAI,IAAoB;AAE7C,aAAS,IAAI,GAAG,IAAI,eAAe,MAAM,QAAQ,KAAK;AACpD,YAAM,aAAa,eAAe,MAAM,CAAC;AACzC,YAAM,aAAa,eAAe,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,WAAW,IAAI;AACpF,UAAI,YAAY;AACd,qBAAa,IAAI,GAAG,eAAe,MAAM,QAAQ,UAAU,CAAC;AAAA,MAC9D;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,aAA0B,gBAAgC;AACjF,UAAM,iBAAiB,YAAY;AACnC,UAAM,iBAAiB,YAAY;AAEnC,UAAM,eAAe,KAAK,mBAAmB,gBAAgB,cAAc;AAE3E,UAAM,oBAAoB,CAAC;AAC3B,aAAS,IAAI,GAAG,IAAI,eAAe,WAAW,UAAU,MAAM,QAAQ,KAAK;AACzE,YAAM,cAAc,eAAe,WAAW,UAAU,MAAM,CAAC;AAC/D,YAAM,cAAc,aAAa,IAAI,WAAW;AAChD,UAAI,gBAAgB,QAAW;AAC7B,0BAAkB,KAAK,WAAW;AAAA,MACpC,OAAO;AACL,gBAAQ,MAAM,sBAAsB,WAAW;AAC/C,0BAAkB,KAAK,CAAC;AAAA,MAC1B;AAAA,IACF;AACA,gBAAY,SAAS,WAAW,YAAY,IAAI;AAAA,MAC9C,IAAI,WAAW,iBAAiB;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,eACX,aACA,WACmB;AACnB,UAAM,QAAQ,IAAI,MAAM;AAExB,UAAM,uBAAuB,KAAK,YAAY,KAAK,WAAW;AAE9D,UAAM,gBAEF,UAAU,IAAI,CAAC,SAAS;AAC1B,aAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,aAAK,YAAY,KAAK,KAAK,GAAG,EAAE,KAAK,CAAC,UAAU;AAC9C,kBAAQ,EAAE,OAAO,KAAK,CAAC;AAAA,QACzB,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAED,UAAM,gBAAgB,MAAM;AAC5B,UAAM,SAAS,MAAM,QAAQ,IAAI,aAAa;AAE9C,UAAM,cAAc,cAAc;AAClC,UAAM,iBAAiB,oBAAI,IAAkB;AAC7C,gBAAY,SAAS,CAAC,UAAU;AAC9B,YAAM,SAAS;AACf,UAAI,OAAO,QAAQ;AACjB,uBAAe,IAAI,MAAM,MAAM,MAAM;AAAA,MACvC;AAEA,YAAM,gBAAgB;AACtB,UAAI,cAAc,eAAe;AAC/B,sBAAc,aAAa;AAC3B,sBAAc,gBAAgB;AAAA,MAChC;AAAA,IACF,CAAC;AACD,UAAM,qBAAyC,CAAC;AAChD,gBAAY,SAAS,CAAC,UAAU;AAC9B,YAAM,gBAAgB;AACtB,UAAI,cAAc,eAAe;AAC/B,2BAAmB,KAAK,aAAa;AAAA,MACvC;AAAA,IACF,CAAC;AAED,QAAI,mBAAmB,WAAW,GAAG;AACnC,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AACA,QAAI,mBAAmB,SAAS,GAAG;AACjC,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AACA,UAAM,cAAc,mBAAmB,CAAC;AACxC,UAAM,IAAI,GAAG,kBAAkB;AAC/B,UAAM,iBAAiB,YAAY;AACnC,UAAM,IAAI,YAAY,SAAS,MAAM,CAAC,CAAC;AACvC,UAAM,oBAAoB,YAAY;AAEtC,eAAW,gBAAgB,QAAQ;AACjC,YAAM,OAAO,aAAa;AAC1B,YAAM,UAAU,aAAa;AAE7B,YAAM,aAAa,QAAQ;AAC3B,UAAI,KAAK,QAAQ;AACf,cAAM,aAAa,KAAK,OAAO;AAC/B,YAAI,OAAO,eAAe,IAAI,MAAM;AACpC,YAAI,eAAe,IAAI,UAAU,GAAG;AAClC,iBAAO,eAAe,IAAI,UAAU;AAAA,QACtC,OAAO;AACL,kBAAQ;AAAA,YACN,+BAA+B,UAAU;AAAA,UAC3C;AAAA,QACF;AACA,YAAI,MAAM;AACR,eAAK,IAAI,UAAU;AAEnB,qBAAW,SAAS;AAAA,YAClB,KAAK,OAAO,SAAS;AAAA,YACrB,KAAK,OAAO,SAAS;AAAA,YACrB,KAAK,OAAO,SAAS;AAAA,UACvB;AAEA,qBAAW,SAAS;AAAA,YAClB,UAAU,SAAS,KAAK,OAAO,SAAS,CAAC;AAAA,YACzC,UAAU,SAAS,KAAK,OAAO,SAAS,CAAC;AAAA,YACzC,UAAU,SAAS,KAAK,OAAO,SAAS,CAAC;AAAA,UAC3C;AAEA,qBAAW,MAAM,IAAI,KAAK,OAAO,MAAM,GAAG,KAAK,OAAO,MAAM,GAAG,KAAK,OAAO,MAAM,CAAC;AAAA,QACpF;AAAA,MACF,OAAO;AACL,cAAM,gBAAoC,CAAC;AAC3C,mBAAW,SAAS,CAAC,UAAU;AAC7B,gBAAM,gBAAgB;AACtB,cAAI,cAAc,eAAe;AAC/B,0BAAc,KAAK,aAAa;AAAA,UAClC;AAAA,QACF,CAAC;AACD,mBAAW,mBAAmB,eAAe;AAC3C,eAAK,iBAAiB,iBAAiB,cAAc;AACrD,0BAAgB,aAAa;AAC7B,0BAAgB,gBAAgB;AAChC,0BAAgB,KAAK,gBAAgB,iBAAkB;AACvD,0BAAgB,WAAW,CAAC;AAC5B,gBAAM,IAAI,eAAe;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;AC/JA,YAAY,mBAAmB;AAExB,SAAS,iBAAiB,OAAc;AAC7C,SAAqB,oBAAM,KAAK;AAClC;;;ACaO,IAAM,sBAAsB,CACjC,mBAC6C;AAC7C,QAAM,SAAoB,IAAI,UAAU;AACxC,QAAM,MAAM,OAAO,gBAAgB,gBAAgB,WAAW;AAE9D,QAAM,MAAM,CAAC,UAAkB;AAC7B,WAAO,QAAQ,IAAI,SAAS;AAAA,EAC9B;AAEA,QAAM,SAAmB,CAAC;AAE1B,QAAM,OAAO,CAAC,iBAAyB;AACrC,WAAO,KAAK,YAAY;AACxB,YAAQ,KAAK,YAAY;AAAA,EAC3B;AAEA,QAAM,aAAa,MAAM,KAAK,IAAI,KAAK,QAAQ,EAAE;AAAA,IAC/C,CAAC,UAAU,MAAM,QAAQ,YAAY,MAAM;AAAA,EAC7C;AACA,QAAM,iBAAiB,WAAW,MAAM;AAExC,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,SAAS,IAAI,WAAW,MAAM;AACpC;AAAA,MACE,YAAY,WAAW,MAAM,wBAAwB,MAAM;AAAA,IAC7D;AAAA,EACF;AAEA,QAAM,mBAAmB,IAAI,iBAAiB,oBAAoB;AAClE,MAAI,iBAAiB,SAAS,GAAG;AAC/B,UAAM,SAAS,IAAI,iBAAiB,MAAM;AAC1C;AAAA,MACE,YAAY,iBAAiB,MAAM,yBAAyB,MAAM;AAAA,IACpE;AAAA,EACF;AAEA,QAAM,aAAa,MAAM,KAAK,IAAI,KAAK,QAAQ,EAAE;AAAA,IAC/C,CAAC,UAAU,MAAM,QAAQ,YAAY,MAAM;AAAA,EAC7C;AACA,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,SAAS,IAAI,WAAW,MAAM;AACpC;AAAA,MACE,YAAY,WAAW,MAAM,cAAc,MAAM;AAAA,IACnD;AAAA,EACF;AAEA,MAAI,OAAoC,EAAE,KAAK,GAAG;AAClD,MAAI,QAAuC,CAAC;AAE5C,MAAI,gBAAgB;AAClB,UAAM,UAAU,eAAe,aAAa,KAAK,KAAK;AACtD,WAAO,EAAE,KAAK,QAAQ;AAEtB,UAAM,sBAAsB,MAAM,KAAK,eAAe,QAAQ,EAAE;AAAA,MAC9D,CAAC,UAAU,MAAM,QAAQ,YAAY,MAAM;AAAA,IAC7C;AACA,YAAQ,oBAAoB,IAAI,CAAC,UAAU;AACzC,YAAM,UAAU,MAAM,aAAa,KAAK,KAAK;AAE7C,YAAM,aAAa,MAAM,aAAa,QAAQ;AAC9C,YAAM,WAAW;AAAA,QACf,GAAG,WAAW,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK;AAAA,QACjD,GAAG,WAAW,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK;AAAA,QACjD,GAAG,WAAW,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK;AAAA,MACnD;AACA,YAAM,QAAQ;AAAA,QACZ,GAAG,WAAW,MAAM,aAAa,IAAI,KAAK,GAAG,KAAK;AAAA,QAClD,GAAG,WAAW,MAAM,aAAa,IAAI,KAAK,GAAG,KAAK;AAAA,QAClD,GAAG,WAAW,MAAM,aAAa,IAAI,KAAK,GAAG,KAAK;AAAA,MACpD;AACA,YAAM,WAAW;AAAA,QACf,GAAG,WAAW,MAAM,aAAa,IAAI,KAAK,GAAG,KAAK;AAAA,QAClD,GAAG,WAAW,MAAM,aAAa,IAAI,KAAK,GAAG,KAAK;AAAA,QAClD,GAAG,WAAW,MAAM,aAAa,IAAI,KAAK,GAAG,KAAK;AAAA,MACpD;AAEA,YAAM,YAAY,aAAa,EAAE,QAAQ,YAAY,UAAU,OAAO,SAAS,IAAI;AAEnF,aAAO,EAAE,KAAK,SAAS,QAAQ,UAAU;AAAA,IAC3C,CAAC;AAED,UAAM,mBAAmB,MAAM,KAAK,IAAI,iBAAiB,qBAAqB,CAAC,EAAE;AAAA,MAC/E,CAAC,UAAU,CAAC,oBAAoB,SAAS,KAAK;AAAA,IAChD;AACA,QAAI,iBAAiB,SAAS,GAAG;AAC/B,YAAM,SAAS,IAAI,iBAAiB,MAAM;AAC1C;AAAA,QACE,YAAY,iBAAiB,MAAM,cAAc,MAAM;AAAA,MACzD;AAAA,IACF;AAAA,EACF,OAAO;AACL,SAAK,gEAAgE;AAAA,EACvE;AAEA,QAAM,uBAAgD;AAAA,IACpD;AAAA,IACA;AAAA,EACF;AAEA,SAAO,CAAC,sBAAsB,MAAM;AACtC;;;ACrHO,IAAM,2BAA2B,CAAC,yBAA0D;AACjG,QAAM,OAAO,qBAAqB,KAAK;AAEvC,QAAM,YAAY,qBAAqB,MAAM;AAAA,IAC3C,CAAC,SAAS,iBAAiB,KAAK,GAAG,IAAI,KAAK,OAAO,UAAU,KAAK,IAAI,MAAM,EAAE;AAAA,EAChF;AAEA,SAAO,qBAAqB,IAAI;AAAA,IAC9B,UAAU,KAAK,MAAM,CAAC;AAAA;AAE1B;",
|
6
|
+
"names": []
|
7
7
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@mml-io/3d-web-avatar",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.17.0",
|
4
4
|
"publishConfig": {
|
5
5
|
"access": "public"
|
6
6
|
},
|
@@ -17,19 +17,20 @@
|
|
17
17
|
"type-check": "tsc --noEmit",
|
18
18
|
"lint": "eslint \"./{src,test}/**/*.{js,jsx,ts,tsx}\" --max-warnings 0",
|
19
19
|
"lint-fix": "eslint \"./{src,test}/**/*.{js,jsx,ts,tsx}\" --fix",
|
20
|
-
"test": "jest"
|
20
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
|
21
21
|
},
|
22
22
|
"dependencies": {
|
23
|
+
"@mml-io/model-loader": "0.15.0",
|
23
24
|
"react": "^18.2.0",
|
24
25
|
"react-dom": "^18.2.0",
|
25
|
-
"three": "0.
|
26
|
+
"three": "0.163.0"
|
26
27
|
},
|
27
28
|
"devDependencies": {
|
28
|
-
"@types/node": "^20.
|
29
|
-
"@types/react": "^18.2.
|
30
|
-
"@types/react-dom": "^18.2.
|
31
|
-
"@types/three": "0.
|
29
|
+
"@types/node": "^20.12.7",
|
30
|
+
"@types/react": "^18.2.79",
|
31
|
+
"@types/react-dom": "^18.2.25",
|
32
|
+
"@types/three": "0.163.0",
|
32
33
|
"esbuild-css-modules-plugin": "3.1.0"
|
33
34
|
},
|
34
|
-
"gitHead": "
|
35
|
+
"gitHead": "bc97b49ea69d920e0824a6885f7e8fca440e2e12"
|
35
36
|
}
|
@@ -1,13 +0,0 @@
|
|
1
|
-
import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
|
2
|
-
export declare class ModelLoader {
|
3
|
-
private debug;
|
4
|
-
private static instance;
|
5
|
-
private readonly loadingManager;
|
6
|
-
private readonly gltfLoader;
|
7
|
-
private modelCache;
|
8
|
-
private ongoingLoads;
|
9
|
-
constructor(maxCacheSize?: number, debug?: boolean);
|
10
|
-
static getInstance(): ModelLoader;
|
11
|
-
load(fileUrl: string): Promise<GLTF | undefined>;
|
12
|
-
private loadFromUrl;
|
13
|
-
}
|
@@ -1,19 +0,0 @@
|
|
1
|
-
import { Group, 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 function cloneModel(model: Group): Object3D<import("three").Event>;
|
9
|
-
export declare class SkeletonHelpers {
|
10
|
-
private debug;
|
11
|
-
private hierarchies;
|
12
|
-
private modelNames;
|
13
|
-
private extractBoneHierarchy;
|
14
|
-
private areBoneHierarchiesEqual;
|
15
|
-
extractAndStoreBoneHierarchy(node: Object3D, modelName: string): void;
|
16
|
-
compareLatestHierarchies(): void;
|
17
|
-
cloneGLTF(gltf: GLTF, modelName: string): ClonedGLTFParts;
|
18
|
-
}
|
19
|
-
export {};
|