@mml-io/3d-web-avatar 0.22.0 → 0.23.1

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.
@@ -2,13 +2,14 @@ import { ModelLoadResult } from "@mml-io/model-loader";
2
2
  import { Object3D } from "three";
3
3
  import { MMLCharacterDescriptionPart } from "../helpers/parseMMLDescription";
4
4
  type MMLCharacterModelLoader = {
5
- load: (url: string) => Promise<ModelLoadResult>;
5
+ load: (url: string, abortController?: AbortController) => Promise<ModelLoadResult | null>;
6
6
  };
7
7
  export declare class MMLCharacter {
8
8
  private modelLoader;
9
9
  constructor(modelLoader: MMLCharacterModelLoader);
10
+ static load(fullBodyURL: string, bodyParts: Array<MMLCharacterDescriptionPart>, modelLoader: MMLCharacterModelLoader, abortController?: AbortController): Promise<Object3D | null>;
10
11
  private createBoneIndexMap;
11
12
  private remapBoneIndices;
12
- mergeBodyParts(fullBodyURL: string, bodyParts: Array<MMLCharacterDescriptionPart>): Promise<Object3D>;
13
+ load(fullBodyURL: string, bodyParts: Array<MMLCharacterDescriptionPart>, abortController?: AbortController): Promise<Object3D | null>;
13
14
  }
14
15
  export {};
@@ -25,4 +25,4 @@ export type MMLCharacterDescription = {
25
25
  parts: MMLCharacterDescriptionPart[];
26
26
  };
27
27
  export type LoadingErrors = string[];
28
- export declare const parseMMLDescription: (mmlDescription: string) => [MMLCharacterDescription, LoadingErrors];
28
+ export declare const parseMMLDescription: (mmlDescription: string, mmlCharacterUrl: string | null) => [MMLCharacterDescription, LoadingErrors];
package/build/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  // src/character/MMLCharacter.ts
2
- import {
3
- Group,
4
- MathUtils
5
- } from "three";
6
- var MMLCharacter = class {
2
+ import { Group, MathUtils, Sphere, Vector3 } from "three";
3
+ var MMLCharacter = class _MMLCharacter {
7
4
  constructor(modelLoader) {
8
5
  this.modelLoader = modelLoader;
9
6
  }
7
+ static load(fullBodyURL, bodyParts, modelLoader, abortController) {
8
+ const mmlCharacter = new _MMLCharacter(modelLoader);
9
+ return mmlCharacter.load(fullBodyURL, bodyParts, abortController);
10
+ }
10
11
  createBoneIndexMap(originSkeleton, targetSkeleton) {
11
12
  const boneIndexMap = /* @__PURE__ */ new Map();
12
13
  for (let i = 0; i < originSkeleton.bones.length; i++) {
@@ -22,7 +23,6 @@ var MMLCharacter = class {
22
23
  const originSkeleton = skinnedMesh.skeleton;
23
24
  const originGeometry = skinnedMesh.geometry;
24
25
  const boneIndexMap = this.createBoneIndexMap(originSkeleton, targetSkeleton);
25
- const newSkinIndexArray = [];
26
26
  const missingBoneIndices = /* @__PURE__ */ new Set();
27
27
  const skinIndexAttribute = originGeometry.attributes.skinIndex;
28
28
  for (let i = 0; i < skinIndexAttribute.count; i++) {
@@ -40,18 +40,31 @@ var MMLCharacter = class {
40
40
  );
41
41
  }
42
42
  }
43
- async mergeBodyParts(fullBodyURL, bodyParts) {
43
+ async load(fullBodyURL, bodyParts, abortController) {
44
44
  const group = new Group();
45
- const fullBodyAssetPromise = this.modelLoader.load(fullBodyURL);
45
+ const fullBodyAssetPromise = this.modelLoader.load(fullBodyURL, abortController);
46
46
  const assetPromises = bodyParts.map((part) => {
47
47
  return new Promise((resolve) => {
48
- this.modelLoader.load(part.url).then((asset) => {
48
+ this.modelLoader.load(part.url, abortController).then((asset) => {
49
+ if (!asset) {
50
+ resolve(null);
51
+ return;
52
+ }
49
53
  resolve({ asset, part });
50
54
  });
51
55
  });
52
56
  });
53
57
  const fullBodyAsset = await fullBodyAssetPromise;
58
+ if (abortController == null ? void 0 : abortController.signal.aborted) {
59
+ return null;
60
+ }
61
+ if (!fullBodyAsset) {
62
+ return null;
63
+ }
54
64
  const assets = await Promise.all(assetPromises);
65
+ if (abortController == null ? void 0 : abortController.signal.aborted) {
66
+ return null;
67
+ }
55
68
  const rawBodyGltf = fullBodyAsset.group;
56
69
  const availableBones = /* @__PURE__ */ new Map();
57
70
  rawBodyGltf.traverse((child) => {
@@ -70,6 +83,7 @@ var MMLCharacter = class {
70
83
  const asSkinnedMesh = child;
71
84
  if (asSkinnedMesh.isSkinnedMesh) {
72
85
  foundSkinnedMeshes.push(asSkinnedMesh);
86
+ asSkinnedMesh.boundingSphere = new Sphere(new Vector3(), 3);
73
87
  }
74
88
  });
75
89
  if (foundSkinnedMeshes.length === 0) {
@@ -86,48 +100,51 @@ var MMLCharacter = class {
86
100
  group.add(skinnedMesh.skeleton.bones[0]);
87
101
  const sharedMatrixWorld = skinnedMesh.matrixWorld;
88
102
  for (const loadingAsset of assets) {
89
- const part = loadingAsset.part;
90
- const rawGltf = loadingAsset.asset;
91
- const modelGroup = rawGltf.group;
92
- if (part.socket) {
93
- const socketName = part.socket.socket;
94
- let bone = availableBones.get("root");
95
- if (availableBones.has(socketName)) {
96
- bone = availableBones.get(socketName);
103
+ if (loadingAsset) {
104
+ const part = loadingAsset.part;
105
+ const rawGltf = loadingAsset.asset;
106
+ const modelGroup = rawGltf.group;
107
+ if (part.socket) {
108
+ const socketName = part.socket.socket;
109
+ let bone = availableBones.get("root");
110
+ if (availableBones.has(socketName)) {
111
+ bone = availableBones.get(socketName);
112
+ } else {
113
+ console.warn(
114
+ `WARNING: no bone found for [${socketName}] socket. Attatching to Root bone`
115
+ );
116
+ }
117
+ if (bone) {
118
+ bone.add(modelGroup);
119
+ modelGroup.position.set(
120
+ part.socket.position.x,
121
+ part.socket.position.y,
122
+ part.socket.position.z
123
+ );
124
+ modelGroup.rotation.set(
125
+ MathUtils.degToRad(part.socket.rotation.x),
126
+ MathUtils.degToRad(part.socket.rotation.y),
127
+ MathUtils.degToRad(part.socket.rotation.z)
128
+ );
129
+ modelGroup.scale.set(part.socket.scale.x, part.socket.scale.y, part.socket.scale.z);
130
+ }
97
131
  } else {
98
- console.warn(
99
- `WARNING: no bone found for [${socketName}] socket. Attatching to Root bone`
100
- );
101
- }
102
- if (bone) {
103
- bone.add(modelGroup);
104
- modelGroup.position.set(
105
- part.socket.position.x,
106
- part.socket.position.y,
107
- part.socket.position.z
108
- );
109
- modelGroup.rotation.set(
110
- MathUtils.degToRad(part.socket.rotation.x),
111
- MathUtils.degToRad(part.socket.rotation.y),
112
- MathUtils.degToRad(part.socket.rotation.z)
113
- );
114
- modelGroup.scale.set(part.socket.scale.x, part.socket.scale.y, part.socket.scale.z);
115
- }
116
- } else {
117
- const skinnedMeshes = [];
118
- modelGroup.traverse((child) => {
119
- const asSkinnedMesh = child;
120
- if (asSkinnedMesh.isSkinnedMesh) {
121
- skinnedMeshes.push(asSkinnedMesh);
132
+ const skinnedMeshes = [];
133
+ modelGroup.traverse((child) => {
134
+ const asSkinnedMesh = child;
135
+ if (asSkinnedMesh.isSkinnedMesh) {
136
+ skinnedMeshes.push(asSkinnedMesh);
137
+ asSkinnedMesh.boundingSphere = new Sphere(new Vector3(), 3);
138
+ }
139
+ });
140
+ for (const skinnedMeshPart of skinnedMeshes) {
141
+ this.remapBoneIndices(skinnedMeshPart, sharedSkeleton);
142
+ skinnedMeshPart.castShadow = true;
143
+ skinnedMeshPart.receiveShadow = true;
144
+ skinnedMeshPart.bind(sharedSkeleton, sharedMatrixWorld);
145
+ skinnedMeshPart.children = [];
146
+ group.add(skinnedMeshPart);
122
147
  }
123
- });
124
- for (const skinnedMeshPart of skinnedMeshes) {
125
- this.remapBoneIndices(skinnedMeshPart, sharedSkeleton);
126
- skinnedMeshPart.castShadow = true;
127
- skinnedMeshPart.receiveShadow = true;
128
- skinnedMeshPart.bind(sharedSkeleton, sharedMatrixWorld);
129
- skinnedMeshPart.children = [];
130
- group.add(skinnedMeshPart);
131
148
  }
132
149
  }
133
150
  }
@@ -142,7 +159,7 @@ function cloneSkinnedMesh(model) {
142
159
  }
143
160
 
144
161
  // src/helpers/parseMMLDescription.ts
145
- var parseMMLDescription = (mmlDescription) => {
162
+ var parseMMLDescription = (mmlDescription, mmlCharacterUrl) => {
146
163
  const parser = new DOMParser();
147
164
  const doc = parser.parseFromString(mmlDescription, "text/html");
148
165
  const tag = (count) => {
@@ -182,13 +199,19 @@ var parseMMLDescription = (mmlDescription) => {
182
199
  let base = { url: "" };
183
200
  let parts = [];
184
201
  if (validCharacter) {
185
- const baseSrc = validCharacter.getAttribute("src") ?? "";
202
+ let baseSrc = validCharacter.getAttribute("src") ?? "";
203
+ if (mmlCharacterUrl) {
204
+ baseSrc = new URL(baseSrc, mmlCharacterUrl).toString();
205
+ }
186
206
  base = { url: baseSrc };
187
207
  const directModelChildren = Array.from(validCharacter.children).filter(
188
208
  (child) => child.tagName.toLowerCase() === "m-model"
189
209
  );
190
210
  parts = directModelChildren.map((model) => {
191
- const partSrc = model.getAttribute("src") ?? "";
211
+ let partSrc = model.getAttribute("src") ?? "";
212
+ if (mmlCharacterUrl) {
213
+ partSrc = new URL(partSrc, mmlCharacterUrl).toString();
214
+ }
192
215
  const socketAttr = model.getAttribute("socket");
193
216
  const position = {
194
217
  x: parseFloat(model.getAttribute("x") ?? "0") || 0,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
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 {\n Bone,\n BufferAttribute,\n Group,\n InterleavedBufferAttribute,\n MathUtils,\n Object3D,\n Skeleton,\n SkinnedMesh,\n} 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 const missingBoneIndices = new Set();\n\n const skinIndexAttribute = originGeometry.attributes.skinIndex;\n for (let i = 0; i < skinIndexAttribute.count; i++) {\n const originIndex = skinIndexAttribute.getComponent(i, 0);\n const targetIndex = boneIndexMap.get(originIndex);\n if (targetIndex !== undefined) {\n skinIndexAttribute.setComponent(i, 0, targetIndex);\n } else {\n missingBoneIndices.add(originIndex);\n }\n }\n\n if (missingBoneIndices.size > 0) {\n console.warn(\n `Missing bone indices in skinIndex attribute: ${Array.from(missingBoneIndices).join(\", \")}`,\n );\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": "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",
4
+ "sourcesContent": ["import { ModelLoadResult } from \"@mml-io/model-loader\";\nimport { Bone, Group, MathUtils, Object3D, Skeleton, SkinnedMesh, Sphere, Vector3 } from \"three\";\n\nimport { MMLCharacterDescriptionPart } from \"../helpers/parseMMLDescription\";\n\ntype MMLCharacterModelLoader = {\n load: (url: string, abortController?: AbortController) => Promise<ModelLoadResult | null>;\n};\n\nexport class MMLCharacter {\n constructor(private modelLoader: MMLCharacterModelLoader) {}\n\n public static load(\n fullBodyURL: string,\n bodyParts: Array<MMLCharacterDescriptionPart>,\n modelLoader: MMLCharacterModelLoader,\n abortController?: AbortController,\n ): Promise<Object3D | null> {\n const mmlCharacter = new MMLCharacter(modelLoader);\n return mmlCharacter.load(fullBodyURL, bodyParts, abortController);\n }\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 missingBoneIndices = new Set();\n\n const skinIndexAttribute = originGeometry.attributes.skinIndex;\n for (let i = 0; i < skinIndexAttribute.count; i++) {\n const originIndex = skinIndexAttribute.getComponent(i, 0);\n const targetIndex = boneIndexMap.get(originIndex);\n if (targetIndex !== undefined) {\n skinIndexAttribute.setComponent(i, 0, targetIndex);\n } else {\n missingBoneIndices.add(originIndex);\n }\n }\n\n if (missingBoneIndices.size > 0) {\n console.warn(\n `Missing bone indices in skinIndex attribute: ${Array.from(missingBoneIndices).join(\", \")}`,\n );\n }\n }\n\n public async load(\n fullBodyURL: string,\n bodyParts: Array<MMLCharacterDescriptionPart>,\n abortController?: AbortController,\n ): Promise<Object3D | null> {\n const group = new Group();\n\n const fullBodyAssetPromise = this.modelLoader.load(fullBodyURL, abortController);\n\n const assetPromises: Array<\n Promise<{ asset: ModelLoadResult; part: MMLCharacterDescriptionPart } | null>\n > = bodyParts.map((part) => {\n return new Promise((resolve) => {\n this.modelLoader.load(part.url, abortController).then((asset: ModelLoadResult | null) => {\n if (!asset) {\n resolve(null);\n return;\n }\n resolve({ asset, part });\n });\n });\n });\n\n const fullBodyAsset = await fullBodyAssetPromise;\n if (abortController?.signal.aborted) {\n return null;\n }\n if (!fullBodyAsset) {\n return null;\n }\n const assets = await Promise.all(assetPromises);\n if (abortController?.signal.aborted) {\n return null;\n }\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 // Set a default bounding sphere for skinned meshes to avoid costly calculations for frustum culling\n asSkinnedMesh.boundingSphere = new Sphere(new Vector3(), 3);\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 if (loadingAsset) {\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 // Set a default bounding sphere for skinned meshes to avoid costly calculations for frustum culling\n asSkinnedMesh.boundingSphere = new Sphere(new Vector3(), 3);\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 }\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 mmlCharacterUrl: string | null,\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 let baseSrc = validCharacter.getAttribute(\"src\") ?? \"\";\n if (mmlCharacterUrl) {\n // Make the baseSrc relative to the mmlCharacterUrl if it's a relative URL\n baseSrc = new URL(baseSrc, mmlCharacterUrl).toString();\n }\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 let partSrc = model.getAttribute(\"src\") ?? \"\";\n if (mmlCharacterUrl) {\n // Make the partSrc relative to the mmlCharacterUrl if it's a relative URL\n partSrc = new URL(partSrc, mmlCharacterUrl).toString();\n }\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
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.22.0",
3
+ "version": "0.23.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -11,6 +11,7 @@
11
11
  "/build"
12
12
  ],
13
13
  "scripts": {
14
+ "depcheck": "depcheck --quiet",
14
15
  "build": "tsx ./build.ts --build",
15
16
  "iterate": "tsx ./build.ts --watch",
16
17
  "start": "cross-env NODE_ENV=production node build/index.js 2>error.log",
@@ -20,17 +21,18 @@
20
21
  "test": "NODE_OPTIONS=--experimental-vm-modules jest"
21
22
  },
22
23
  "dependencies": {
23
- "@mml-io/model-loader": "0.19.7",
24
+ "@mml-io/model-loader": "0.20.0",
24
25
  "react": "^19.0.0",
25
- "react-dom": "^19.0.0",
26
- "three": "0.163.0"
26
+ "three": "0.178.0"
27
27
  },
28
28
  "devDependencies": {
29
+ "@types/jest": "^29.5.12",
29
30
  "@types/node": "^22.13.1",
30
- "@types/react": "^19.0.8",
31
- "@types/react-dom": "^19.0.3",
32
- "@types/three": "0.163.0",
33
- "esbuild-css-modules-plugin": "3.1.4"
31
+ "@types/three": "0.178.0",
32
+ "esbuild-css-modules-plugin": "3.1.4",
33
+ "jest-environment-jsdom": "29.7.0",
34
+ "jest-junit": "16.0.0",
35
+ "ts-jest": "^29.1.5"
34
36
  },
35
- "gitHead": "c389fca2be51c09efd43b04d3b840712ef605c84"
37
+ "gitHead": "d4f4aa001113f88ca51bcc938a1a2fe713a31f22"
36
38
  }