@lovelace_lol/loom3 1.0.43 → 1.0.45

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/README.md CHANGED
@@ -350,15 +350,20 @@ const loom = new Loom3({
350
350
 
351
351
  `annotationRegions` is the Loom3 field for camera/marker region defaults and profile overrides.
352
352
 
353
- If your app fetches a full saved `CharacterConfig` from Firestore or another backend, use `extendCharacterConfigWithPreset(...)` to build the runtime shape before handing that config to camera/marker tooling:
353
+ If your app fetches a saved model/profile record from Firestore or another backend, use `extendProfileConfigWithPreset(...)` to build the runtime shape before handing that profile config to camera/marker tooling:
354
354
 
355
355
  ```typescript
356
- import { extendCharacterConfigWithPreset } from '@lovelace_lol/loom3';
356
+ import { extendProfileConfigWithPreset } from '@lovelace_lol/loom3';
357
357
 
358
- const savedConfig = await fetchCharacterConfig();
359
- const runtimeConfig = extendCharacterConfigWithPreset(savedConfig);
358
+ const savedConfig = await fetchProfileConfig();
359
+ const runtimeConfig = extendProfileConfigWithPreset({
360
+ ...savedConfig,
361
+ profilePresetId: savedConfig.profilePresetId ?? 'cc4',
362
+ });
360
363
  ```
361
364
 
365
+ `CharacterConfig`, `auPresetType`, and `extendCharacterConfigWithPreset(...)` are still exported as deprecated compatibility aliases for apps migrating from older LoomLarge-style character records. New Loom3 integrations should model presets as base profiles, pass profile overrides through `profile`, `annotationRegions`, or other `Profile` fields, and use `profilePresetId` for preset selection.
366
+
362
367
  For the current runtime-oriented documentation, including:
363
368
 
364
369
  - `paddingFactor`
@@ -2091,7 +2096,7 @@ console.log(result.center, result.method, result.debugInfo);
2091
2096
  ### Resolving region-driven centers
2092
2097
 
2093
2098
  ```typescript
2094
- import type { CharacterConfig, Region } from '@lovelace_lol/loom3';
2099
+ import type { BoneResolutionProfile, Region } from '@lovelace_lol/loom3';
2095
2100
  import { resolveBoneName, resolveBoneNames, resolveFaceCenter } from '@lovelace_lol/loom3';
2096
2101
 
2097
2102
  const region: Region = {
@@ -2100,18 +2105,14 @@ const region: Region = {
2100
2105
  meshes: ['CC_Base_Body'],
2101
2106
  };
2102
2107
 
2103
- const config = {
2104
- characterId: 'demo',
2105
- characterName: 'Demo',
2106
- modelPath: '/demo.glb',
2107
- regions: [region],
2108
+ const profile: BoneResolutionProfile = {
2108
2109
  bonePrefix: 'CC_Base_',
2109
2110
  boneNodes: { HEAD: 'Head' },
2110
- } satisfies CharacterConfig;
2111
+ };
2111
2112
 
2112
- const headBone = resolveBoneName('HEAD', config);
2113
- const resolvedBones = resolveBoneNames(['HEAD'], config);
2114
- const faceCenter = resolveFaceCenter(gltf.scene, region, config);
2113
+ const headBone = resolveBoneName('HEAD', profile);
2114
+ const resolvedBones = resolveBoneNames(['HEAD'], profile);
2115
+ const faceCenter = resolveFaceCenter(gltf.scene, region, profile);
2115
2116
  ```
2116
2117
 
2117
2118
  ### Working with model orientation
@@ -2128,7 +2129,7 @@ const facing = detectFacingDirection(gltf.scene);
2128
2129
 
2129
2130
  Use these helpers when you need to:
2130
2131
  - place annotation markers using semantic regions instead of hard-coded coordinates
2131
- - resolve prefixed/suffixed bone names from a reusable character config
2132
+ - resolve prefixed/suffixed bone names from a reusable profile or minimal bone-resolution object
2132
2133
  - derive a face anchor for camera tooling or interaction layers
2133
2134
  - reason about model orientation before building your own camera or annotation system
2134
2135
 
package/dist/index.cjs CHANGED
@@ -74,6 +74,9 @@ function bindingTargets(binding) {
74
74
  if (targets && targets.length > 0) return targets;
75
75
  return binding.morph !== void 0 && binding.morph !== "" ? [binding.morph] : [];
76
76
  }
77
+ function normalizeBindingWeight(weight) {
78
+ return Number.isFinite(weight) ? Math.max(0, weight ?? 1) : 1;
79
+ }
77
80
  function getProfileVisemeSlots(profile) {
78
81
  if (profile.visemeSlots && profile.visemeSlots.length > 0) {
79
82
  return [...profile.visemeSlots].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
@@ -99,6 +102,23 @@ function compileVisemeKeys(profile) {
99
102
  return target ?? profile.visemeKeys?.[index] ?? "";
100
103
  });
101
104
  }
105
+ function getVisemeBindingTargets(profile, visemeIndex) {
106
+ const slots = getProfileVisemeSlots(profile);
107
+ const slot = slots[visemeIndex];
108
+ const binding = slot ? profile.visemeBindings?.[slot.id] : void 0;
109
+ const boundTargets = binding?.targets?.filter((target) => target.morph !== void 0 && target.morph !== "").map((target) => ({
110
+ morph: target.morph,
111
+ weight: normalizeBindingWeight(target.weight)
112
+ }));
113
+ if (boundTargets && boundTargets.length > 0) {
114
+ return boundTargets;
115
+ }
116
+ if (binding?.morph !== void 0 && binding.morph !== "") {
117
+ return [{ morph: binding.morph, weight: 1 }];
118
+ }
119
+ const legacyTarget = profile.visemeKeys?.[visemeIndex];
120
+ return legacyTarget !== void 0 && legacyTarget !== "" ? [{ morph: legacyTarget, weight: 1 }] : [];
121
+ }
102
122
  function getVisemeJawAmounts(profile) {
103
123
  const slots = getProfileVisemeSlots(profile);
104
124
  if (slots.length === 0) return profile.visemeJawAmounts ? [...profile.visemeJawAmounts] : void 0;
@@ -1735,12 +1755,13 @@ var BakedAnimationController = class {
1735
1755
  const globalBalance = options?.balance ?? 0;
1736
1756
  const balanceMap = options?.balanceMap;
1737
1757
  const meshNames = options?.meshNames;
1758
+ const visemeSlotCount = getProfileVisemeSlots(config).length;
1738
1759
  let maxTime = 0;
1739
1760
  const isNumericAU = (id) => /^\d+$/.test(id);
1740
1761
  const isVisemeIndex = (id) => {
1741
1762
  if (options?.snippetCategory !== "visemeSnippet") return false;
1742
1763
  const num = Number(id);
1743
- return !Number.isNaN(num) && num >= 0 && num < config.visemeKeys.length;
1764
+ return !Number.isNaN(num) && num >= 0 && num < visemeSlotCount;
1744
1765
  };
1745
1766
  const sampleAt = (arr, t) => {
1746
1767
  if (!arr.length) return 0;
@@ -1778,11 +1799,13 @@ var BakedAnimationController = class {
1778
1799
  const auId = Number(curveId);
1779
1800
  if (isVisemeIndex(curveId)) {
1780
1801
  const visemeMeshNames = this.getMeshNamesForViseme(config, meshNames);
1781
- const visemeKey = config.visemeKeys[auId];
1782
- if (typeof visemeKey === "number") {
1783
- this.addMorphIndexTracks(tracks, visemeKey, keyframes, intensityScale, visemeMeshNames);
1784
- } else if (visemeKey) {
1785
- this.addMorphTracks(tracks, visemeKey, keyframes, intensityScale, visemeMeshNames);
1802
+ for (const target of getVisemeBindingTargets(config, auId)) {
1803
+ const effectiveScale = intensityScale * target.weight;
1804
+ if (typeof target.morph === "number") {
1805
+ this.addMorphIndexTracks(tracks, target.morph, keyframes, effectiveScale, visemeMeshNames);
1806
+ } else if (target.morph) {
1807
+ this.addMorphTracks(tracks, target.morph, keyframes, effectiveScale, visemeMeshNames);
1808
+ }
1786
1809
  }
1787
1810
  } else {
1788
1811
  const auMeshNames = this.getMeshNamesForAU(auId, config, meshNames);
@@ -1825,7 +1848,7 @@ var BakedAnimationController = class {
1825
1848
  }
1826
1849
  const autoVisemeJaw = options?.autoVisemeJaw !== false;
1827
1850
  const jawScale = options?.jawScale ?? 1;
1828
- const visemeJawAmounts = config.visemeJawAmounts;
1851
+ const visemeJawAmounts = getVisemeJawAmounts(config);
1829
1852
  if (autoVisemeJaw && jawScale > 0 && visemeJawAmounts && options?.snippetCategory === "visemeSnippet" && keyframeTimes.length > 0) {
1830
1853
  const bones = this.host.getBones();
1831
1854
  const jawEntry = bones["JAW"];
@@ -1833,7 +1856,7 @@ var BakedAnimationController = class {
1833
1856
  const jawValues = [];
1834
1857
  for (const t of keyframeTimes) {
1835
1858
  let jawAmount = 0;
1836
- for (let visemeIdx = 0; visemeIdx < config.visemeKeys.length; visemeIdx++) {
1859
+ for (let visemeIdx = 0; visemeIdx < visemeSlotCount; visemeIdx++) {
1837
1860
  const visemeCurve = curves[String(visemeIdx)];
1838
1861
  if (!visemeCurve) continue;
1839
1862
  const visemeValue = clampIntensity(sampleAt(visemeCurve, t) * intensityScale);
@@ -5361,10 +5384,15 @@ var _Loom3 = class _Loom3 {
5361
5384
  };
5362
5385
  this.resolvedAUMorphTargets.set(auId, resolved);
5363
5386
  }
5364
- for (let i = 0; i < (this.config.visemeKeys || []).length; i += 1) {
5365
- const key = this.config.visemeKeys[i];
5387
+ for (let i = 0; i < getProfileVisemeSlots(this.config).length; i += 1) {
5366
5388
  const visemeMeshNames = this.getMeshNamesForViseme();
5367
- const targets = typeof key === "number" ? this.resolveMorphTargetsByIndex(key, visemeMeshNames) : this.resolveMorphTargets(key, visemeMeshNames);
5389
+ const targets = [];
5390
+ for (const bindingTarget of getVisemeBindingTargets(this.config, i)) {
5391
+ const resolved = typeof bindingTarget.morph === "number" ? this.resolveMorphTargetsByIndex(bindingTarget.morph, visemeMeshNames) : this.resolveMorphTargets(bindingTarget.morph, visemeMeshNames);
5392
+ for (const target of resolved) {
5393
+ targets.push({ ...target, weight: bindingTarget.weight });
5394
+ }
5395
+ }
5368
5396
  this.resolvedVisemeTargets[i] = targets;
5369
5397
  }
5370
5398
  }
@@ -5832,46 +5860,33 @@ var _Loom3 = class _Loom3 {
5832
5860
  // VISEME CONTROL
5833
5861
  // ============================================================================
5834
5862
  setViseme(visemeIndex, value, jawScale = 1) {
5835
- if (visemeIndex < 0 || visemeIndex >= this.config.visemeKeys.length) return;
5863
+ if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) return;
5836
5864
  const val = clamp012(value);
5837
5865
  this.visemeValues[visemeIndex] = val;
5838
5866
  this.visemeJawScales[visemeIndex] = jawScale;
5839
- const targets = this.resolvedVisemeTargets[visemeIndex];
5840
- if (targets && targets.length > 0) {
5841
- this.applyMorphTargets(targets, val);
5842
- } else {
5843
- const morphKey = this.config.visemeKeys[visemeIndex];
5844
- const visemeMeshNames = this.getMeshNamesForViseme();
5845
- if (typeof morphKey === "number") {
5846
- this.setMorphInfluence(morphKey, val, visemeMeshNames);
5847
- } else if (typeof morphKey === "string") {
5848
- this.setMorph(morphKey, val, visemeMeshNames);
5849
- }
5850
- }
5851
- const jawAmount = this.getVisemeJawAmount(visemeIndex) * val * jawScale;
5852
- if (Math.abs(jawScale) > 1e-6 && Math.abs(jawAmount) > 1e-6) {
5853
- this.updateBoneRotation("JAW", "pitch", jawAmount);
5854
- }
5867
+ this.applyVisemeRuntimeState();
5855
5868
  }
5856
5869
  transitionViseme(visemeIndex, to, durationMs = 80, jawScale = 1) {
5857
- if (visemeIndex < 0 || visemeIndex >= this.config.visemeKeys.length) {
5870
+ if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) {
5858
5871
  return { promise: Promise.resolve(), pause: () => {
5859
5872
  }, resume: () => {
5860
5873
  }, cancel: () => {
5861
5874
  } };
5862
5875
  }
5863
- const morphKey = this.config.visemeKeys[visemeIndex];
5864
5876
  const target = clamp012(to);
5865
- this.visemeValues[visemeIndex] = target;
5877
+ const from = this.visemeValues[visemeIndex] ?? 0;
5866
5878
  this.visemeJawScales[visemeIndex] = jawScale;
5867
- const visemeMeshNames = this.getMeshNamesForViseme();
5868
- const morphHandle = typeof morphKey === "number" ? this.transitionMorphInfluence(morphKey, target, durationMs, visemeMeshNames) : this.transitionMorph(morphKey, target, durationMs, visemeMeshNames);
5869
- const jawAmount = this.getVisemeJawAmount(visemeIndex) * target * jawScale;
5870
- if (Math.abs(jawScale) <= 1e-6 || Math.abs(jawAmount) <= 1e-6) {
5871
- return morphHandle;
5872
- }
5873
- const jawHandle = this.transitionBoneRotation("JAW", "pitch", jawAmount, durationMs);
5874
- return this.combineHandles([morphHandle, jawHandle]);
5879
+ return this.animation.addTransition(
5880
+ `viseme_value_${visemeIndex}`,
5881
+ from,
5882
+ target,
5883
+ durationMs,
5884
+ (value) => {
5885
+ this.visemeValues[visemeIndex] = clamp012(value);
5886
+ this.visemeJawScales[visemeIndex] = jawScale;
5887
+ this.applyVisemeRuntimeState();
5888
+ }
5889
+ );
5875
5890
  }
5876
5891
  setVisemeById(slotId, value, jawScale = 1) {
5877
5892
  const index = getVisemeSlotIndex(this.config, slotId);
@@ -5935,8 +5950,9 @@ var _Loom3 = class _Loom3 {
5935
5950
  }
5936
5951
  resetToNeutral() {
5937
5952
  this.auValues = {};
5938
- this.visemeValues = new Array(this.config.visemeKeys.length).fill(0);
5939
- this.visemeJawScales = new Array(this.config.visemeKeys.length).fill(1);
5953
+ const visemeCount = getProfileVisemeSlots(this.config).length;
5954
+ this.visemeValues = new Array(visemeCount).fill(0);
5955
+ this.visemeJawScales = new Array(visemeCount).fill(1);
5940
5956
  this.translations = {};
5941
5957
  this.initBoneRotations();
5942
5958
  this.clearTransitions();
@@ -5970,11 +5986,7 @@ var _Loom3 = class _Loom3 {
5970
5986
  if (Number.isNaN(auId)) continue;
5971
5987
  this.setAU(auId, value, this.auBalances[auId]);
5972
5988
  }
5973
- for (let visemeIndex = 0; visemeIndex < this.visemeValues.length; visemeIndex += 1) {
5974
- const value = this.visemeValues[visemeIndex] ?? 0;
5975
- if (value <= 0) continue;
5976
- this.setViseme(visemeIndex, value, this.visemeJawScales[visemeIndex] ?? 1);
5977
- }
5989
+ this.applyVisemeRuntimeState();
5978
5990
  if (this.model) {
5979
5991
  this.flushPendingComposites();
5980
5992
  this.model.updateMatrixWorld(true);
@@ -6332,6 +6344,37 @@ var _Loom3 = class _Loom3 {
6332
6344
  target.infl[target.idx] = val;
6333
6345
  }
6334
6346
  }
6347
+ applyVisemeRuntimeState() {
6348
+ for (const targets of this.resolvedVisemeTargets) {
6349
+ for (const target of targets || []) {
6350
+ if (target.idx < target.infl.length) {
6351
+ target.infl[target.idx] = 0;
6352
+ }
6353
+ }
6354
+ }
6355
+ for (let index = 0; index < this.visemeValues.length; index += 1) {
6356
+ const value = clamp012(this.visemeValues[index] ?? 0);
6357
+ if (value <= 1e-6) continue;
6358
+ const targets = this.resolvedVisemeTargets[index] || [];
6359
+ for (const target of targets) {
6360
+ if (target.idx >= target.infl.length) continue;
6361
+ const weighted = clamp012(value * target.weight);
6362
+ target.infl[target.idx] = Math.max(target.infl[target.idx] ?? 0, weighted);
6363
+ }
6364
+ }
6365
+ this.updateBoneRotation("JAW", "pitch", this.getActiveVisemeJawAmount());
6366
+ }
6367
+ getActiveVisemeJawAmount() {
6368
+ let jawAmount = 0;
6369
+ for (let index = 0; index < this.visemeValues.length; index += 1) {
6370
+ const value = clamp012(this.visemeValues[index] ?? 0);
6371
+ if (value <= 1e-6) continue;
6372
+ const jawScale = this.visemeJawScales[index] ?? 1;
6373
+ if (Math.abs(jawScale) <= 1e-6) continue;
6374
+ jawAmount = Math.max(jawAmount, this.getVisemeJawAmount(index) * value * jawScale);
6375
+ }
6376
+ return jawAmount;
6377
+ }
6335
6378
  getMorphValue(key) {
6336
6379
  if (this.faceMesh) {
6337
6380
  const dict = this.faceMesh.morphTargetDictionary;
@@ -6517,7 +6560,7 @@ var _Loom3 = class _Loom3 {
6517
6560
  return meshNames?.length ? `idx:${index}@${meshNames.join(",")}` : `idx:${index}`;
6518
6561
  }
6519
6562
  syncVisemeRuntimeState() {
6520
- const visemeCount = this.config.visemeKeys.length;
6563
+ const visemeCount = getProfileVisemeSlots(this.config).length;
6521
6564
  this.visemeValues = Array.from(
6522
6565
  { length: visemeCount },
6523
6566
  (_, index) => this.visemeValues[index] ?? 0
@@ -6989,7 +7032,7 @@ function normalizeRegionTree(regions, disabledNames) {
6989
7032
  });
6990
7033
  }
6991
7034
 
6992
- // src/characters/extendCharacterConfigWithPreset.ts
7035
+ // src/profiles/resolveProfileConfig.ts
6993
7036
  var PROFILE_OVERRIDE_KEYS = [
6994
7037
  "name",
6995
7038
  "animalType",
@@ -7096,7 +7139,7 @@ function mergeRegion(base, override) {
7096
7139
  } : void 0
7097
7140
  };
7098
7141
  }
7099
- function mergeRegionsByName(base, override) {
7142
+ function mergeProfileRegionsByName(base, override) {
7100
7143
  if (!base && !override) return void 0;
7101
7144
  const merged = /* @__PURE__ */ new Map();
7102
7145
  for (const region of base ?? []) {
@@ -7111,6 +7154,10 @@ function mergeRegionsByName(base, override) {
7111
7154
  function getAnnotationRegions(value) {
7112
7155
  return Array.isArray(value) ? value : void 0;
7113
7156
  }
7157
+ var mergeRegionsByName = mergeProfileRegionsByName;
7158
+ function getProfilePresetId(config) {
7159
+ return config.profilePresetId ?? config.presetId ?? config.baseProfileId ?? config.auPresetType;
7160
+ }
7114
7161
  function getLegacyNestedOverrides(config) {
7115
7162
  return isPlainObject2(config.profile) ? config.profile : {};
7116
7163
  }
@@ -7118,12 +7165,12 @@ function getLegacyRuntimeRegions(config) {
7118
7165
  return Array.isArray(config.regions) && config.regions.length > 0 ? config.regions : void 0;
7119
7166
  }
7120
7167
  function getCanonicalAnnotationOverrides(config) {
7121
- return mergeRegionsByName(
7168
+ return mergeProfileRegionsByName(
7122
7169
  getAnnotationRegions(getLegacyNestedOverrides(config).annotationRegions),
7123
7170
  getAnnotationRegions(config.annotationRegions)
7124
7171
  );
7125
7172
  }
7126
- function extractProfileOverrides(config) {
7173
+ function extractLegacyCharacterProfileOverrides(config) {
7127
7174
  const topLevelConfig = config;
7128
7175
  const legacyNestedOverrides = getLegacyNestedOverrides(config);
7129
7176
  const canonicalAnnotationOverrides = getCanonicalAnnotationOverrides(config);
@@ -7146,12 +7193,18 @@ function extractProfileOverrides(config) {
7146
7193
  }
7147
7194
  return overrides;
7148
7195
  }
7149
- function applyCharacterProfileToPreset(config) {
7150
- const presetType = config.auPresetType;
7196
+ function extractProfileOverrides(config) {
7197
+ return extractLegacyCharacterProfileOverrides(config);
7198
+ }
7199
+ function resolveProfileFromPreset(config) {
7200
+ const presetType = getProfilePresetId(config);
7151
7201
  if (!presetType) {
7152
7202
  return null;
7153
7203
  }
7154
- return extendPresetWithProfile(getPreset(presetType), extractProfileOverrides(config));
7204
+ return extendPresetWithProfile(getPreset(presetType), extractLegacyCharacterProfileOverrides(config));
7205
+ }
7206
+ function applyCharacterProfileToPreset(config) {
7207
+ return resolveProfileFromPreset(config);
7155
7208
  }
7156
7209
  function orderExtendedRegions(extendedRegions, prioritizedLists) {
7157
7210
  if (!extendedRegions) return void 0;
@@ -7172,15 +7225,15 @@ function orderExtendedRegions(extendedRegions, prioritizedLists) {
7172
7225
  }
7173
7226
  return orderedNames.map((name) => extendedByName.get(name)).filter((region) => Boolean(region));
7174
7227
  }
7175
- function extendCharacterConfigWithPreset(config) {
7176
- const presetType = config.auPresetType;
7228
+ function extendProfileConfigWithPreset(config) {
7229
+ const presetType = getProfilePresetId(config);
7177
7230
  if (!presetType || presetType === "custom") {
7178
7231
  return config;
7179
7232
  }
7180
7233
  const canonicalAnnotationOverrides = getCanonicalAnnotationOverrides(config);
7181
7234
  const legacyRuntimeRegions = getLegacyRuntimeRegions(config);
7182
- const profileOverrides = extractProfileOverrides(config);
7183
- const extendedPresetProfile = applyCharacterProfileToPreset(config);
7235
+ const profileOverrides = extractLegacyCharacterProfileOverrides(config);
7236
+ const extendedPresetProfile = resolveProfileFromPreset(config);
7184
7237
  if (!extendedPresetProfile) {
7185
7238
  return config;
7186
7239
  }
@@ -7194,7 +7247,7 @@ function extendCharacterConfigWithPreset(config) {
7194
7247
  const extendedRegionNames = new Set((extendedAnnotationRegions ?? []).map((region) => region.name));
7195
7248
  const legacyExtraRegions = canonicalAnnotationOverrides && legacyRuntimeRegions ? legacyRuntimeRegions.filter((region) => !presetRegionNames.has(region.name) && !extendedRegionNames.has(region.name)).map((region) => cloneRegion(region)) : void 0;
7196
7249
  const mergedRegions = normalizeRegionTree(
7197
- mergeRegionsByName(extendedAnnotationRegions, legacyExtraRegions),
7250
+ mergeProfileRegionsByName(extendedAnnotationRegions, legacyExtraRegions),
7198
7251
  profileOverrides.disabledRegions
7199
7252
  );
7200
7253
  const extendedRegions = orderExtendedRegions(
@@ -7208,6 +7261,9 @@ function extendCharacterConfigWithPreset(config) {
7208
7261
  regions: extendedRegions ?? config.regions
7209
7262
  };
7210
7263
  }
7264
+ function extendCharacterConfigWithPreset(config) {
7265
+ return extendProfileConfigWithPreset(config);
7266
+ }
7211
7267
  var DEFAULT_EPSILON = 1e-4;
7212
7268
  var DEFAULT_YAW_WEIGHT = 0.35;
7213
7269
  var DEFAULT_PITCH_WEIGHT = 0.2;
@@ -7432,9 +7488,9 @@ function detectFacingDirection(model, eyeBoneNames = {
7432
7488
  function normalizeLooseName(value) {
7433
7489
  return value.replace(/\./g, "");
7434
7490
  }
7435
- function resolveBoneNameCandidates(semanticName, config) {
7436
- if (!config) return [semanticName];
7437
- const { bonePrefix, boneSuffix, boneNodes } = config;
7491
+ function resolveBoneNameCandidates(semanticName, profile) {
7492
+ if (!profile) return [semanticName];
7493
+ const { bonePrefix, boneSuffix, boneNodes } = profile;
7438
7494
  if (!boneNodes || !boneNodes[semanticName]) {
7439
7495
  return [semanticName];
7440
7496
  }
@@ -7448,14 +7504,14 @@ function resolveBoneNameCandidates(semanticName, config) {
7448
7504
  const fullyAffixed = suffix && !prefixedBase.endsWith(suffix) ? `${prefixedBase}${suffix}` : prefixedBase;
7449
7505
  return Array.from(/* @__PURE__ */ new Set([fullyAffixed, baseName]));
7450
7506
  }
7451
- function resolveBoneName(semanticName, config) {
7452
- return resolveBoneNameCandidates(semanticName, config)[0] ?? semanticName;
7507
+ function resolveBoneName(semanticName, profile) {
7508
+ return resolveBoneNameCandidates(semanticName, profile)[0] ?? semanticName;
7453
7509
  }
7454
- function resolveBoneNames(names, config) {
7510
+ function resolveBoneNames(names, profile) {
7455
7511
  if (!names || names.length === 0) return [];
7456
7512
  return Array.from(
7457
7513
  new Set(
7458
- names.flatMap((name) => resolveBoneNameCandidates(name, config))
7514
+ names.flatMap((name) => resolveBoneNameCandidates(name, profile))
7459
7515
  )
7460
7516
  );
7461
7517
  }
@@ -7468,8 +7524,8 @@ function fuzzyNameMatch(objectName, targetName, suffixPattern) {
7468
7524
  const regex = suffixPattern ? new RegExp(suffixPattern) : /^[_\.]\d+$/;
7469
7525
  return regex.test(suffix);
7470
7526
  }
7471
- function resolveFaceCenter(model, region, config) {
7472
- const resolvedBones = resolveBoneNames(region.bones, config);
7527
+ function resolveFaceCenter(model, region, profile) {
7528
+ const resolvedBones = resolveBoneNames(region.bones, profile);
7473
7529
  const headBoneNames = resolvedBones.filter((name) => name.toLowerCase().includes("head"));
7474
7530
  const result = findFaceCenter(model, {
7475
7531
  headBoneNames: headBoneNames.length > 0 ? headBoneNames : void 0,
@@ -8628,7 +8684,9 @@ exports.computeCameraRelativeGazeOffset = computeCameraRelativeGazeOffset;
8628
8684
  exports.detectFacingDirection = detectFacingDirection;
8629
8685
  exports.extendCharacterConfigWithPreset = extendCharacterConfigWithPreset;
8630
8686
  exports.extendPresetWithProfile = extendPresetWithProfile;
8687
+ exports.extendProfileConfigWithPreset = extendProfileConfigWithPreset;
8631
8688
  exports.extractFromGLTF = extractFromGLTF;
8689
+ exports.extractLegacyCharacterProfileOverrides = extractLegacyCharacterProfileOverrides;
8632
8690
  exports.extractModelData = extractModelData;
8633
8691
  exports.extractProfileOverrides = extractProfileOverrides;
8634
8692
  exports.findFaceCenter = findFaceCenter;
@@ -8639,7 +8697,9 @@ exports.getMeshNamesForVisemeProfile = getMeshNamesForVisemeProfile;
8639
8697
  exports.getModelForwardDirection = getModelForwardDirection;
8640
8698
  exports.getPreset = getPreset;
8641
8699
  exports.getPresetWithProfile = getPresetWithProfile;
8700
+ exports.getProfilePresetId = getProfilePresetId;
8642
8701
  exports.getProfileVisemeSlots = getProfileVisemeSlots;
8702
+ exports.getVisemeBindingTargets = getVisemeBindingTargets;
8643
8703
  exports.getVisemeJawAmounts = getVisemeJawAmounts;
8644
8704
  exports.getVisemeSlotIndex = getVisemeSlotIndex;
8645
8705
  exports.hasLeftRightMorphs = hasLeftRightMorphs;
@@ -8647,11 +8707,13 @@ exports.isMixedAU = isMixedAU;
8647
8707
  exports.isPresetCompatible = isPresetCompatible;
8648
8708
  exports.mapProviderVisemeToSlot = mapProviderVisemeToSlot;
8649
8709
  exports.mergeCharacterRegionsByName = mergeRegionsByName;
8710
+ exports.mergeProfileRegionsByName = mergeProfileRegionsByName;
8650
8711
  exports.resolveBoneName = resolveBoneName;
8651
8712
  exports.resolveBoneNames = resolveBoneNames;
8652
8713
  exports.resolveFaceCenter = resolveFaceCenter;
8653
8714
  exports.resolvePreset = resolvePreset;
8654
8715
  exports.resolvePresetWithOverrides = resolvePresetWithOverrides;
8716
+ exports.resolveProfileFromPreset = resolveProfileFromPreset;
8655
8717
  exports.resolveVisemeMeshCategory = resolveVisemeMeshCategory;
8656
8718
  exports.suggestBestPreset = suggestBestPreset;
8657
8719
  exports.validateMappingConfig = validateMappingConfig;