@mml-io/3d-web-avatar 0.10.1 → 0.12.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.
@@ -7,5 +7,7 @@ export declare class Character {
7
7
  private sharedSkeleton;
8
8
  private sharedMatrixWorld;
9
9
  constructor(modelLoader: ModelLoader);
10
+ private createBoneIndexMap;
11
+ private remapBoneIndices;
10
12
  mergeBodyParts(fullBodyURL: string, bodyParts: Array<string>): Promise<Object3D>;
11
13
  }
@@ -1,11 +1,12 @@
1
1
  import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
2
2
  export declare class ModelLoader {
3
+ private debug;
3
4
  private static instance;
4
5
  private readonly loadingManager;
5
6
  private readonly gltfLoader;
6
7
  private modelCache;
7
8
  private ongoingLoads;
8
- constructor(maxCacheSize?: number);
9
+ constructor(maxCacheSize?: number, debug?: boolean);
9
10
  static getInstance(): ModelLoader;
10
11
  load(fileUrl: string): Promise<GLTF | undefined>;
11
12
  private loadFromUrl;
@@ -1,10 +1,11 @@
1
- import { Matrix4, Object3D, Skeleton } from "three";
1
+ import { Group, Matrix4, Object3D, Skeleton } from "three";
2
2
  import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  type ClonedGLTFParts = {
4
4
  gltf: GLTF;
5
5
  sharedSkeleton: Skeleton;
6
6
  matrixWorld: Matrix4;
7
7
  };
8
+ export declare function cloneModel(model: Group): Object3D<import("three").Event>;
8
9
  export declare class SkeletonHelpers {
9
10
  private debug;
10
11
  private hierarchies;
@@ -0,0 +1,2 @@
1
+ import { MMLCharacterDescription } from "./parseMMLDescription";
2
+ export declare const createMMLCharacterString: (characterDescription: MMLCharacterDescription) => string;
@@ -0,0 +1,10 @@
1
+ export type MMLCharacterDescriptionPart = {
2
+ url: string;
3
+ type?: string;
4
+ };
5
+ export type MMLCharacterDescription = {
6
+ base: MMLCharacterDescriptionPart;
7
+ parts: MMLCharacterDescriptionPart[];
8
+ };
9
+ export type LoadingErrors = string[];
10
+ export declare const parseMMLDescription: (mmlDescription: string) => [MMLCharacterDescription, LoadingErrors];
package/build/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  export { Character } from "./character/Character";
2
+ export { cloneModel } from "./helpers/SkeletonHelpers";
2
3
  export { TimeManagerInterface } from "./character/types";
3
4
  export { ModelLoader } from "./character/ModelLoader";
5
+ export { type LoadingErrors, type MMLCharacterDescriptionPart, type MMLCharacterDescription, parseMMLDescription, } from "./helpers/parseMMLDescription";
6
+ export { createMMLCharacterString } from "./helpers/createMMLCharacterString";
package/build/index.js CHANGED
@@ -1,5 +1,15 @@
1
+ // src/character/Character.ts
2
+ import {
3
+ BufferAttribute
4
+ } from "three";
5
+
1
6
  // src/helpers/SkeletonHelpers.ts
2
7
  import { Skeleton } from "three";
8
+ import * as SkeletonUtils from "three/examples/jsm/utils/SkeletonUtils.js";
9
+ function cloneModel(model) {
10
+ const clone2 = SkeletonUtils.clone(model);
11
+ return clone2;
12
+ }
3
13
  var SkeletonHelpers = class {
4
14
  constructor() {
5
15
  this.debug = false;
@@ -95,9 +105,9 @@ var SkeletonHelpers = class {
95
105
  }
96
106
  }
97
107
  cloneGLTF(gltf, modelName) {
98
- const clone = {
108
+ const clone2 = {
99
109
  animations: gltf.animations,
100
- scene: gltf.scene.clone(true)
110
+ scene: cloneModel(gltf.scene)
101
111
  };
102
112
  let sharedSkeleton = null;
103
113
  let matrixWorld = null;
@@ -110,12 +120,11 @@ var SkeletonHelpers = class {
110
120
  const cloneBones = {};
111
121
  const cloneSkinnedMeshes = {};
112
122
  let hierarchyCheck = false;
113
- clone.scene.traverse((node) => {
123
+ clone2.scene.traverse((node) => {
114
124
  if (node.type === "Bone") {
115
125
  if (hierarchyCheck === false) {
116
126
  hierarchyCheck = true;
117
127
  this.extractAndStoreBoneHierarchy(node, modelName);
118
- this.compareLatestHierarchies();
119
128
  }
120
129
  cloneBones[node.name] = node;
121
130
  }
@@ -141,7 +150,7 @@ var SkeletonHelpers = class {
141
150
  cloneSkinnedMesh.bind(sharedSkeleton, matrixWorld);
142
151
  }
143
152
  return {
144
- gltf: clone,
153
+ gltf: clone2,
145
154
  sharedSkeleton,
146
155
  matrixWorld
147
156
  };
@@ -157,6 +166,38 @@ var Character = class {
157
166
  this.sharedSkeleton = null;
158
167
  this.sharedMatrixWorld = null;
159
168
  }
169
+ createBoneIndexMap(originSkeleton, targetSkeleton) {
170
+ const boneIndexMap = /* @__PURE__ */ new Map();
171
+ for (let i = 0; i < originSkeleton.bones.length; i++) {
172
+ const originBone = originSkeleton.bones[i];
173
+ const targetBone = targetSkeleton.bones.find((bone) => bone.name === originBone.name);
174
+ if (targetBone) {
175
+ boneIndexMap.set(i, targetSkeleton.bones.indexOf(targetBone));
176
+ }
177
+ }
178
+ return boneIndexMap;
179
+ }
180
+ remapBoneIndices(skinnedMesh) {
181
+ const targetSkeleton = this.sharedSkeleton;
182
+ const originSkeleton = skinnedMesh.skeleton;
183
+ const originGeometry = skinnedMesh.geometry;
184
+ const boneIndexMap = this.createBoneIndexMap(originSkeleton, targetSkeleton);
185
+ const newSkinIndexArray = [];
186
+ for (let i = 0; i < originGeometry.attributes.skinIndex.array.length; i++) {
187
+ const originIndex = originGeometry.attributes.skinIndex.array[i];
188
+ const targetIndex = boneIndexMap.get(originIndex);
189
+ if (targetIndex !== void 0) {
190
+ newSkinIndexArray.push(targetIndex);
191
+ } else {
192
+ console.error("Missing bone index", originIndex);
193
+ newSkinIndexArray.push(0);
194
+ }
195
+ }
196
+ skinnedMesh.geometry.attributes.skinIndex = new BufferAttribute(
197
+ new Uint8Array(newSkinIndexArray),
198
+ 4
199
+ );
200
+ }
160
201
  async mergeBodyParts(fullBodyURL, bodyParts) {
161
202
  const fullBodyAsset = await this.modelLoader.load(fullBodyURL);
162
203
  const fullBodyGLTF = this.skeletonHelpers.cloneGLTF(fullBodyAsset, "fullBody");
@@ -173,10 +214,12 @@ var Character = class {
173
214
  const fullBodyModelGroup = fullBodyGLTF.gltf.scene;
174
215
  this.skinnedMeshesParent = null;
175
216
  fullBodyModelGroup.traverse((child) => {
176
- if (child.type === "SkinnedMesh") {
177
- child.castShadow = true;
217
+ const asSkinnedMesh = child;
218
+ if (asSkinnedMesh.isSkinnedMesh) {
219
+ asSkinnedMesh.castShadow = true;
220
+ asSkinnedMesh.receiveShadow = true;
178
221
  if (this.skinnedMeshesParent === null) {
179
- this.skinnedMeshesParent = child.parent;
222
+ this.skinnedMeshesParent = asSkinnedMesh.parent;
180
223
  }
181
224
  }
182
225
  });
@@ -187,10 +230,14 @@ var Character = class {
187
230
  const modelGroup = gltf.gltf.scene;
188
231
  modelGroup.traverse((child) => {
189
232
  var _a;
190
- if (child.type === "SkinnedMesh") {
191
- child.castShadow = true;
192
- child.bind(this.sharedSkeleton, this.sharedMatrixWorld);
193
- (_a = this.skinnedMeshesParent) == null ? void 0 : _a.children.splice(3, 0, child);
233
+ const asSkinnedMesh = child;
234
+ if (asSkinnedMesh.type === "SkinnedMesh") {
235
+ const skinnedMeshClone = child.clone(true);
236
+ this.remapBoneIndices(skinnedMeshClone);
237
+ skinnedMeshClone.castShadow = true;
238
+ skinnedMeshClone.receiveShadow = true;
239
+ skinnedMeshClone.bind(this.sharedSkeleton, this.sharedMatrixWorld);
240
+ (_a = this.skinnedMeshesParent) == null ? void 0 : _a.add(skinnedMeshClone);
194
241
  }
195
242
  });
196
243
  }
@@ -240,7 +287,8 @@ var LRUCache = class {
240
287
  }
241
288
  };
242
289
  var _ModelLoader = class _ModelLoader {
243
- constructor(maxCacheSize = 100) {
290
+ constructor(maxCacheSize = 100, debug = false) {
291
+ this.debug = debug;
244
292
  this.ongoingLoads = /* @__PURE__ */ new Map();
245
293
  this.loadingManager = new LoadingManager();
246
294
  this.gltfLoader = new CachedGLTFLoader(this.loadingManager);
@@ -255,12 +303,16 @@ var _ModelLoader = class _ModelLoader {
255
303
  async load(fileUrl) {
256
304
  const cachedModel = this.modelCache.get(fileUrl);
257
305
  if (cachedModel) {
258
- console.log(`Loading ${fileUrl.split("/").pop()} from cache`);
306
+ if (this.debug === true) {
307
+ console.log(`Loading ${fileUrl.split("/").pop()} from cache`);
308
+ }
259
309
  const blobURL = URL.createObjectURL(cachedModel.blob);
260
310
  this.gltfLoader.setBlobUrl(fileUrl, blobURL);
261
311
  return this.loadFromUrl(blobURL);
262
312
  } else {
263
- console.log(`Loading ${fileUrl} from server`);
313
+ if (this.debug === true) {
314
+ console.log(`Loading ${fileUrl} from server`);
315
+ }
264
316
  const ongoingLoad = this.ongoingLoads.get(fileUrl);
265
317
  if (ongoingLoad)
266
318
  return ongoingLoad;
@@ -292,8 +344,91 @@ var _ModelLoader = class _ModelLoader {
292
344
  };
293
345
  _ModelLoader.instance = null;
294
346
  var ModelLoader = _ModelLoader;
347
+
348
+ // src/helpers/parseMMLDescription.ts
349
+ var parseMMLDescription = (mmlDescription) => {
350
+ const parser = new DOMParser();
351
+ const doc = parser.parseFromString(mmlDescription, "text/html");
352
+ const tag = (count) => {
353
+ return count > 1 ? "tags" : "tag";
354
+ };
355
+ const errors = [];
356
+ const warn = (errorMessage) => {
357
+ errors.push(errorMessage);
358
+ console.warn(errorMessage);
359
+ };
360
+ const characters = Array.from(doc.body.children).filter(
361
+ (child) => child.tagName.toLowerCase() === "m-character"
362
+ );
363
+ const validCharacter = characters.shift();
364
+ if (characters.length > 0) {
365
+ const tagStr = tag(characters.length);
366
+ warn(
367
+ `ignoring ${characters.length} extra <m-character> ${tagStr} found in the root of the document (only the first one is valid).`
368
+ );
369
+ }
370
+ const nestedCharacters = doc.querySelectorAll("body * m-character");
371
+ if (nestedCharacters.length > 0) {
372
+ const tagStr = tag(nestedCharacters.length);
373
+ warn(
374
+ `ignoring ${nestedCharacters.length} nested <m-character> ${tagStr} found within other tags. A valid <m-character> tag must be at the root of the document.`
375
+ );
376
+ }
377
+ const rootModels = Array.from(doc.body.children).filter(
378
+ (child) => child.tagName.toLowerCase() === "m-model"
379
+ );
380
+ if (rootModels.length > 0) {
381
+ const tagStr = tag(rootModels.length);
382
+ warn(
383
+ `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).`
384
+ );
385
+ }
386
+ let base = { url: "" };
387
+ let parts = [];
388
+ if (validCharacter) {
389
+ const baseSrc = validCharacter.getAttribute("src") ?? "";
390
+ base = { url: baseSrc };
391
+ const directModelChildren = Array.from(validCharacter.children).filter(
392
+ (child) => child.tagName.toLowerCase() === "m-model"
393
+ );
394
+ parts = directModelChildren.map((model) => {
395
+ const partSrc = model.getAttribute("src") ?? "";
396
+ return { url: partSrc };
397
+ });
398
+ const wrappedModelTags = Array.from(doc.querySelectorAll("m-character m-model")).filter(
399
+ (model) => !directModelChildren.includes(model)
400
+ );
401
+ if (wrappedModelTags.length > 0) {
402
+ const tagStr = tag(wrappedModelTags.length);
403
+ warn(
404
+ `ignoring ${wrappedModelTags.length} <m-model> ${tagStr} that were found wrapped inside tags other than a valid <m-character> tag.`
405
+ );
406
+ }
407
+ } else {
408
+ warn(`No valid <m-character> tag was found in the provided document.`);
409
+ }
410
+ const characterDescription = {
411
+ base,
412
+ parts
413
+ };
414
+ return [characterDescription, errors];
415
+ };
416
+
417
+ // src/helpers/createMMLCharacterString.ts
418
+ var createMMLCharacterString = (characterDescription) => {
419
+ const base = characterDescription.base.url;
420
+ const partsTags = characterDescription.parts.map(
421
+ (part) => `<m-model src="${part.url}"${part.type ? ` type="${part.type}"` : ""}></m-model>`
422
+ );
423
+ return `<m-character src="${base}">
424
+ ${partsTags.join("\n ")}
425
+ </m-character>`;
426
+ };
295
427
  export {
296
428
  Character,
297
- ModelLoader
429
+ ModelLoader,
430
+ cloneModel,
431
+ createMMLCharacterString,
432
+ parseMMLDescription
298
433
  };
299
434
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
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(fullBodyURL: string, bodyParts: Array<string>): Promise<Object3D> {\n const fullBodyAsset = await this.modelLoader.load(fullBodyURL);\n const fullBodyGLTF = this.skeletonHelpers.cloneGLTF(fullBodyAsset as GLTF, \"fullBody\");\n\n const assetPromises: Array<Promise<{ asset: GLTF; partUrl: string }>> = bodyParts.map(\n (partUrl) => {\n return new Promise((resolve) => {\n this.modelLoader.load(partUrl).then((asset) => {\n resolve({ asset: asset!, partUrl });\n });\n });\n },\n );\n const assets = await Promise.all(assetPromises);\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 for (const loadingAsset of assets) {\n const gltf = this.skeletonHelpers.cloneGLTF(loadingAsset.asset, loadingAsset.partUrl);\n const modelGroup = gltf.gltf.scene;\n modelGroup.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\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 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(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 console.log(`Loading ${fileUrl.split(\"/\").pop()} from cache`);\n const blobURL = URL.createObjectURL(cachedModel.blob);\n this.gltfLoader.setBlobUrl(fileUrl, blobURL);\n return this.loadFromUrl(blobURL);\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 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"],
5
- "mappings": "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",
6
- "names": []
3
+ "sources": ["../src/character/Character.ts", "../src/helpers/SkeletonHelpers.ts", "../src/character/ModelLoader.ts", "../src/helpers/parseMMLDescription.ts", "../src/helpers/createMMLCharacterString.ts"],
4
+ "sourcesContent": ["import {\n BoxHelper,\n BufferAttribute,\n Group,\n Matrix4,\n MeshStandardMaterial,\n Object3D,\n Skeleton,\n SkinnedMesh,\n} from \"three\";\nimport { GLTF } from \"three/examples/jsm/loaders/GLTFLoader.js\";\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 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(fullBodyURL: string, bodyParts: Array<string>): Promise<Object3D> {\n const fullBodyAsset = await this.modelLoader.load(fullBodyURL);\n const fullBodyGLTF = this.skeletonHelpers.cloneGLTF(fullBodyAsset as GLTF, \"fullBody\");\n\n const assetPromises: Array<Promise<{ asset: GLTF; partUrl: string }>> = bodyParts.map(\n (partUrl) => {\n return new Promise((resolve) => {\n this.modelLoader.load(partUrl).then((asset) => {\n resolve({ asset: asset!, partUrl });\n });\n });\n },\n );\n const assets = await Promise.all(assetPromises);\n\n const fullBodyModelGroup = fullBodyGLTF.gltf.scene;\n\n this.skinnedMeshesParent = null;\n\n fullBodyModelGroup.traverse((child) => {\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 gltf = this.skeletonHelpers.cloneGLTF(loadingAsset.asset, loadingAsset.partUrl);\n const modelGroup = gltf.gltf.scene;\n modelGroup.traverse((child) => {\n const asSkinnedMesh = child as SkinnedMesh;\n if (asSkinnedMesh.type === \"SkinnedMesh\") {\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 this.skinnedMeshesParent?.add(skinnedMeshClone);\n }\n });\n }\n return fullBodyGLTF!.gltf.scene as Object3D;\n }\n}\n", "import { Bone, Group, Matrix4, 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.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};\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 return { url: partSrc };\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": ["clone"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mml-io/3d-web-avatar",
3
- "version": "0.10.1",
3
+ "version": "0.12.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -24,11 +24,11 @@
24
24
  "three": "0.153.0"
25
25
  },
26
26
  "devDependencies": {
27
- "@types/node": "^20.5.9",
28
- "@types/react": "^18.2.21",
29
- "@types/react-dom": "^18.2.7",
27
+ "@types/node": "^20.11.13",
28
+ "@types/react": "^18.2.48",
29
+ "@types/react-dom": "^18.2.18",
30
30
  "@types/three": "0.153.0",
31
31
  "esbuild-css-modules-plugin": "3.0.1"
32
32
  },
33
- "gitHead": "ec6e197ba6332afd7cce95905e3f1ad2f0e29767"
33
+ "gitHead": "b2c116ecf3180b04801ff7eb684f80cd1554b532"
34
34
  }