@lovelace_lol/loom3 1.0.27 → 1.0.29

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
@@ -349,6 +349,15 @@ const loom = new Loom3({
349
349
 
350
350
  `annotationRegions` is the Loom3 field for camera/marker region defaults and profile overrides.
351
351
 
352
+ 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
+
354
+ ```typescript
355
+ import { extendCharacterConfigWithPreset } from '@lovelace_lol/loom3';
356
+
357
+ const savedConfig = await fetchCharacterConfig();
358
+ const runtimeConfig = extendCharacterConfigWithPreset(savedConfig);
359
+ ```
360
+
352
361
  For the current runtime-oriented documentation, including:
353
362
 
354
363
  - `paddingFactor`
package/dist/index.cjs CHANGED
@@ -1887,7 +1887,17 @@ var CC4_BONE_NODES = {
1887
1887
  NECK: "NeckTwist01",
1888
1888
  NECK_TWIST: "NeckTwist02",
1889
1889
  JAW: "JawRoot",
1890
- TONGUE: "Tongue01"
1890
+ TONGUE: "Tongue01",
1891
+ SPINE_01: "Spine01",
1892
+ SPINE_02: "Spine02",
1893
+ CLAVICLE_L: "L_Clavicle",
1894
+ CLAVICLE_R: "R_Clavicle",
1895
+ HAND_L: "L_Hand",
1896
+ HAND_R: "R_Hand",
1897
+ FOOT_L: "L_Foot",
1898
+ FOOT_R: "R_Foot",
1899
+ TOEBASE_L: "L_ToeBase",
1900
+ TOEBASE_R: "R_ToeBase"
1891
1901
  };
1892
1902
  var CC4_EYE_MESH_NODES = {
1893
1903
  LEFT: "CC_Base_Eye",
@@ -2125,7 +2135,7 @@ var CC4_PRESET = {
2125
2135
  },
2126
2136
  {
2127
2137
  name: "head",
2128
- bones: ["CC_Base_Head", "CC_Base_JawRoot"],
2138
+ bones: ["HEAD", "JAW"],
2129
2139
  paddingFactor: 1.5,
2130
2140
  children: ["face", "left_eye", "right_eye", "mouth"],
2131
2141
  expandAnimation: "staggered",
@@ -2133,66 +2143,66 @@ var CC4_PRESET = {
2133
2143
  },
2134
2144
  {
2135
2145
  name: "face",
2136
- bones: ["CC_Base_Head"],
2146
+ bones: ["HEAD"],
2137
2147
  // meshes: populated by user selection in wizard - varies per character
2138
2148
  paddingFactor: 1.3,
2139
2149
  parent: "head"
2140
2150
  },
2141
2151
  {
2142
2152
  name: "left_eye",
2143
- bones: ["CC_Base_L_Eye"],
2153
+ bones: ["EYE_L"],
2144
2154
  paddingFactor: 0.9,
2145
2155
  parent: "head"
2146
2156
  },
2147
2157
  {
2148
2158
  name: "right_eye",
2149
- bones: ["CC_Base_R_Eye"],
2159
+ bones: ["EYE_R"],
2150
2160
  paddingFactor: 0.9,
2151
2161
  parent: "head"
2152
2162
  },
2153
2163
  {
2154
2164
  name: "mouth",
2155
- bones: ["CC_Base_JawRoot"],
2165
+ bones: ["JAW"],
2156
2166
  paddingFactor: 1.5,
2157
2167
  parent: "head"
2158
2168
  },
2159
2169
  {
2160
2170
  name: "upper_body",
2161
2171
  bones: [
2162
- "CC_Base_Spine02",
2163
- "CC_Base_Head",
2164
- "CC_Base_L_Clavicle",
2165
- "CC_Base_R_Clavicle"
2172
+ "SPINE_02",
2173
+ "HEAD",
2174
+ "CLAVICLE_L",
2175
+ "CLAVICLE_R"
2166
2176
  ],
2167
2177
  paddingFactor: 1.6
2168
2178
  },
2169
2179
  {
2170
2180
  name: "back",
2171
- bones: ["CC_Base_Spine01", "CC_Base_Spine02"],
2181
+ bones: ["SPINE_01", "SPINE_02"],
2172
2182
  paddingFactor: 1.8,
2173
2183
  cameraAngle: 180
2174
2184
  },
2175
2185
  {
2176
2186
  name: "left_hand",
2177
- bones: ["CC_Base_L_Hand"],
2187
+ bones: ["HAND_L"],
2178
2188
  paddingFactor: 1.3,
2179
2189
  cameraAngle: 270
2180
2190
  },
2181
2191
  {
2182
2192
  name: "right_hand",
2183
- bones: ["CC_Base_R_Hand"],
2193
+ bones: ["HAND_R"],
2184
2194
  paddingFactor: 1.3,
2185
2195
  cameraAngle: 90
2186
2196
  },
2187
2197
  {
2188
2198
  name: "left_foot",
2189
- bones: ["CC_Base_L_Foot", "CC_Base_L_ToeBase"],
2199
+ bones: ["FOOT_L", "TOEBASE_L"],
2190
2200
  paddingFactor: 2.5,
2191
2201
  cameraAngle: 270
2192
2202
  },
2193
2203
  {
2194
2204
  name: "right_foot",
2195
- bones: ["CC_Base_R_Foot", "CC_Base_R_ToeBase"],
2205
+ bones: ["FOOT_R", "TOEBASE_R"],
2196
2206
  paddingFactor: 2.5,
2197
2207
  cameraAngle: 90
2198
2208
  }
@@ -3005,6 +3015,9 @@ function resolveProfile(base, override) {
3005
3015
  hairPhysics: mergeHairPhysicsConfig(base.hairPhysics, override.hairPhysics)
3006
3016
  };
3007
3017
  }
3018
+ function applyProfileToPreset(base, override) {
3019
+ return override ? resolveProfile(base, override) : base;
3020
+ }
3008
3021
 
3009
3022
  // src/presets/bettaFish.ts
3010
3023
  var BONES = [
@@ -3677,6 +3690,9 @@ var MESHES = {
3677
3690
  morphCount: 0,
3678
3691
  // Fish model has no morphs
3679
3692
  material: {
3693
+ renderOrder: 20,
3694
+ transparent: true,
3695
+ opacity: 1,
3680
3696
  depthWrite: true,
3681
3697
  depthTest: true,
3682
3698
  blending: "Normal"
@@ -3687,29 +3703,126 @@ var MESHES = {
3687
3703
  category: "eye",
3688
3704
  morphCount: 0,
3689
3705
  material: {
3690
- renderOrder: -10,
3691
- // Render early (behind body)
3706
+ renderOrder: 17,
3707
+ transparent: true,
3708
+ opacity: 1,
3709
+ depthWrite: true,
3710
+ depthTest: true,
3711
+ blending: "Normal"
3712
+ }
3713
+ },
3714
+ // Hidden helper shell from the source asset; keep it suppressed by default
3715
+ // so Betta does not rely on a Firestore-only mesh override.
3716
+ "Cube_0": {
3717
+ category: "body",
3718
+ morphCount: 0,
3719
+ material: {
3720
+ renderOrder: -20,
3721
+ transparent: true,
3722
+ opacity: 0,
3692
3723
  depthWrite: true,
3693
3724
  depthTest: true,
3694
3725
  blending: "Normal"
3695
3726
  }
3696
3727
  }
3697
3728
  };
3698
- var BETTA_FISH_PRESET = {
3699
- name: "Betta Fish",
3700
- animalType: "fish",
3701
- emoji: "\u{1F41F}",
3702
- bones: BONES,
3703
- boneNodes: BONE_NODES,
3704
- boneBindings: BONE_BINDINGS,
3705
- actionInfo: AU_INFO2,
3706
- eyeMeshNodes: EYE_MESH_NODES,
3707
- // No morph targets in this model
3708
- auToMorphs: {},
3709
- morphToMesh: {},
3710
- visemeKeys: []
3711
- };
3712
- var AU_MAPPING_CONFIG = {
3729
+ var ANNOTATION_REGIONS = [
3730
+ {
3731
+ name: "full_body",
3732
+ objects: ["*"],
3733
+ paddingFactor: 2.5
3734
+ },
3735
+ {
3736
+ name: "head",
3737
+ bones: ["HEAD"],
3738
+ paddingFactor: 1.8,
3739
+ children: ["left_eye", "right_eye", "mouth"],
3740
+ expandAnimation: "staggered"
3741
+ },
3742
+ {
3743
+ name: "left_eye",
3744
+ meshes: ["EYES_0"],
3745
+ parent: "head",
3746
+ paddingFactor: 1.4,
3747
+ cameraAngle: 270
3748
+ },
3749
+ {
3750
+ name: "right_eye",
3751
+ meshes: ["EYES_0"],
3752
+ parent: "head",
3753
+ paddingFactor: 1.4,
3754
+ cameraAngle: 90
3755
+ },
3756
+ {
3757
+ name: "mouth",
3758
+ bones: ["HEAD"],
3759
+ parent: "head",
3760
+ paddingFactor: 1.5
3761
+ },
3762
+ {
3763
+ name: "body",
3764
+ bones: ["BODY_FRONT", "BODY_MID", "BODY_BACK"],
3765
+ paddingFactor: 1.8
3766
+ },
3767
+ {
3768
+ name: "tail",
3769
+ bones: ["TAIL_BASE", "TAIL_TOP", "TAIL_MID"],
3770
+ paddingFactor: 1.6
3771
+ },
3772
+ {
3773
+ name: "dorsal_fin",
3774
+ bones: ["DORSAL_ROOT", "DORSAL_L", "DORSAL_R"],
3775
+ paddingFactor: 1.8
3776
+ },
3777
+ {
3778
+ name: "pectoral_fins",
3779
+ bones: ["PECTORAL_L_ROOT", "PECTORAL_R_ROOT"],
3780
+ paddingFactor: 1.8,
3781
+ children: ["pectoral_fin_left", "pectoral_fin_right"],
3782
+ expandAnimation: "outward"
3783
+ },
3784
+ {
3785
+ name: "pectoral_fin_left",
3786
+ bones: ["PECTORAL_L_ROOT", "PECTORAL_L_CHAIN1", "PECTORAL_L_CHAIN2"],
3787
+ parent: "pectoral_fins",
3788
+ paddingFactor: 1.6,
3789
+ cameraAngle: 270
3790
+ },
3791
+ {
3792
+ name: "pectoral_fin_right",
3793
+ bones: ["PECTORAL_R_ROOT", "PECTORAL_R_CHAIN1", "PECTORAL_R_ROOT2"],
3794
+ parent: "pectoral_fins",
3795
+ paddingFactor: 1.6,
3796
+ cameraAngle: 90
3797
+ },
3798
+ {
3799
+ name: "ventral_fins",
3800
+ bones: ["VENTRAL_L", "VENTRAL_R"],
3801
+ paddingFactor: 1.6
3802
+ },
3803
+ {
3804
+ name: "gills",
3805
+ bones: ["GILL_L", "GILL_R"],
3806
+ paddingFactor: 1.6,
3807
+ children: ["throat", "gill"],
3808
+ expandAnimation: "outward"
3809
+ },
3810
+ {
3811
+ name: "throat",
3812
+ bones: ["GILL_L", "GILL_L_MID", "GILL_L_TIP"],
3813
+ parent: "gills",
3814
+ paddingFactor: 1.4,
3815
+ cameraAngle: 270
3816
+ },
3817
+ {
3818
+ name: "gill",
3819
+ bones: ["GILL_R", "GILL_R_MID", "GILL_R_TIP"],
3820
+ parent: "gills",
3821
+ paddingFactor: 1.4,
3822
+ cameraAngle: 90
3823
+ }
3824
+ ];
3825
+ var BETTA_FISH_PROFILE = {
3713
3826
  name: "Betta Fish",
3714
3827
  animalType: "fish",
3715
3828
  emoji: "\u{1F41F}",
@@ -3727,18 +3840,27 @@ var AU_MAPPING_CONFIG = {
3727
3840
  compositeRotations: COMPOSITE_ROTATIONS2,
3728
3841
  eyeMeshNodes: EYE_MESH_NODES,
3729
3842
  meshes: MESHES,
3843
+ annotationRegions: ANNOTATION_REGIONS,
3730
3844
  auMixDefaults: {},
3731
3845
  // No mixed AUs (morph+bone) in this model
3732
3846
  continuumPairs: CONTINUUM_PAIRS_MAP2,
3733
3847
  continuumLabels: CONTINUUM_LABELS2
3734
3848
  };
3849
+ var BETTA_FISH_PRESET = {
3850
+ ...BETTA_FISH_PROFILE,
3851
+ bones: BONES,
3852
+ boneBindings: BONE_BINDINGS,
3853
+ actionInfo: AU_INFO2
3854
+ };
3855
+ var AU_MAPPING_CONFIG = BETTA_FISH_PRESET;
3856
+ var FISH_AU_MAPPING_CONFIG = BETTA_FISH_PRESET;
3735
3857
 
3736
3858
  // src/presets/index.ts
3737
3859
  function resolvePreset(presetType) {
3738
3860
  switch (presetType) {
3739
3861
  case "fish":
3740
3862
  case "skeletal":
3741
- return AU_MAPPING_CONFIG;
3863
+ return BETTA_FISH_PRESET;
3742
3864
  case "cc4":
3743
3865
  case "custom":
3744
3866
  default:
@@ -3747,7 +3869,7 @@ function resolvePreset(presetType) {
3747
3869
  }
3748
3870
  function resolvePresetWithOverrides(presetType, overrides) {
3749
3871
  const base = resolvePreset(presetType);
3750
- return overrides ? resolveProfile(base, overrides) : base;
3872
+ return applyProfileToPreset(base, overrides);
3751
3873
  }
3752
3874
 
3753
3875
  // src/engines/three/Loom3.ts
@@ -3824,7 +3946,7 @@ var _Loom3 = class _Loom3 {
3824
3946
  /** Store original emissive colors for highlight reset */
3825
3947
  __publicField(this, "originalEmissive", /* @__PURE__ */ new Map());
3826
3948
  const basePreset = config.presetType ? resolvePreset(config.presetType) : CC4_PRESET;
3827
- this.config = config.profile ? resolveProfile(basePreset, config.profile) : basePreset;
3949
+ this.config = applyProfileToPreset(basePreset, config.profile);
3828
3950
  this.mixWeights = { ...this.config.auMixDefaults };
3829
3951
  this.animation = animation || new AnimationThree();
3830
3952
  this.compositeRotations = this.config.compositeRotations || COMPOSITE_ROTATIONS;
@@ -5232,6 +5354,199 @@ var BLENDING_MODES = {
5232
5354
  "None": 0
5233
5355
  // THREE.NoBlending
5234
5356
  };
5357
+
5358
+ // src/characters/resolveCharacterConfig.ts
5359
+ var PROFILE_OVERRIDE_KEYS = [
5360
+ "name",
5361
+ "animalType",
5362
+ "emoji",
5363
+ "auToMorphs",
5364
+ "auToBones",
5365
+ "boneNodes",
5366
+ "bonePrefix",
5367
+ "boneSuffix",
5368
+ "morphPrefix",
5369
+ "morphSuffix",
5370
+ "suffixPattern",
5371
+ "leftMorphSuffixes",
5372
+ "rightMorphSuffixes",
5373
+ "morphToMesh",
5374
+ "auFacePartToMeshCategory",
5375
+ "visemeKeys",
5376
+ "visemeMeshCategory",
5377
+ "visemeJawAmounts",
5378
+ "auMixDefaults",
5379
+ "auInfo",
5380
+ "eyeMeshNodes",
5381
+ "compositeRotations",
5382
+ "meshes",
5383
+ "continuumPairs",
5384
+ "continuumLabels",
5385
+ "annotationRegions",
5386
+ "hairPhysics"
5387
+ ];
5388
+ function isPlainObject2(value) {
5389
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5390
+ }
5391
+ function cloneArray(value) {
5392
+ return value ? value.map((entry) => cloneValue2(entry)) : void 0;
5393
+ }
5394
+ function cloneValue2(value) {
5395
+ if (Array.isArray(value)) {
5396
+ return value.map((entry) => cloneValue2(entry));
5397
+ }
5398
+ if (isPlainObject2(value)) {
5399
+ const next = {};
5400
+ for (const [key, entry] of Object.entries(value)) {
5401
+ next[key] = cloneValue2(entry);
5402
+ }
5403
+ return next;
5404
+ }
5405
+ return value;
5406
+ }
5407
+ function mergeProfileOverrideValue(base, override) {
5408
+ if (override === void 0) {
5409
+ return base === void 0 ? void 0 : cloneValue2(base);
5410
+ }
5411
+ if (Array.isArray(override)) {
5412
+ return cloneValue2(override);
5413
+ }
5414
+ if (isPlainObject2(base) && isPlainObject2(override)) {
5415
+ return {
5416
+ ...cloneValue2(base),
5417
+ ...cloneValue2(override)
5418
+ };
5419
+ }
5420
+ return cloneValue2(override);
5421
+ }
5422
+ function cloneVector3(value) {
5423
+ return value ? { ...value } : void 0;
5424
+ }
5425
+ function cloneRegion(region) {
5426
+ return {
5427
+ ...region,
5428
+ bones: cloneArray(region.bones),
5429
+ meshes: cloneArray(region.meshes),
5430
+ objects: cloneArray(region.objects),
5431
+ children: cloneArray(region.children),
5432
+ cameraOffset: cloneVector3(region.cameraOffset),
5433
+ customPosition: region.customPosition ? { ...region.customPosition } : void 0,
5434
+ style: region.style ? {
5435
+ ...region.style,
5436
+ line: region.style.line ? { ...region.style.line } : void 0
5437
+ } : void 0
5438
+ };
5439
+ }
5440
+ function mergeRegion(base, override) {
5441
+ return {
5442
+ ...base,
5443
+ ...override,
5444
+ bones: override.bones !== void 0 ? [...override.bones] : base.bones ? [...base.bones] : void 0,
5445
+ meshes: override.meshes !== void 0 ? [...override.meshes] : base.meshes ? [...base.meshes] : void 0,
5446
+ objects: override.objects !== void 0 ? [...override.objects] : base.objects ? [...base.objects] : void 0,
5447
+ children: override.children !== void 0 ? [...override.children] : base.children ? [...base.children] : void 0,
5448
+ cameraOffset: override.cameraOffset ? { ...base.cameraOffset, ...override.cameraOffset } : cloneVector3(base.cameraOffset),
5449
+ customPosition: override.customPosition ? { ...override.customPosition } : base.customPosition ? { ...base.customPosition } : void 0,
5450
+ style: override.style ? {
5451
+ ...base.style,
5452
+ ...override.style,
5453
+ line: override.style.line ? { ...base.style?.line, ...override.style.line } : base.style?.line ? { ...base.style.line } : void 0
5454
+ } : base.style ? {
5455
+ ...base.style,
5456
+ line: base.style.line ? { ...base.style.line } : void 0
5457
+ } : void 0
5458
+ };
5459
+ }
5460
+ function mergeRegionsByName(base, override) {
5461
+ if (!base && !override) return void 0;
5462
+ const merged = /* @__PURE__ */ new Map();
5463
+ for (const region of base ?? []) {
5464
+ merged.set(region.name, cloneRegion(region));
5465
+ }
5466
+ for (const region of override ?? []) {
5467
+ const existing = merged.get(region.name);
5468
+ merged.set(region.name, existing ? mergeRegion(existing, region) : cloneRegion(region));
5469
+ }
5470
+ return Array.from(merged.values());
5471
+ }
5472
+ function extractProfileOverrides(config) {
5473
+ const topLevelConfig = config;
5474
+ const legacyNestedOverrides = isPlainObject2(config.profile) ? config.profile : {};
5475
+ const overrides = {};
5476
+ for (const key of PROFILE_OVERRIDE_KEYS) {
5477
+ if (key === "annotationRegions") {
5478
+ const topLevelAnnotationRegions = Array.isArray(topLevelConfig.annotationRegions) ? topLevelConfig.annotationRegions : void 0;
5479
+ const legacyAnnotationRegions = Array.isArray(legacyNestedOverrides.annotationRegions) ? legacyNestedOverrides.annotationRegions : void 0;
5480
+ const presetOverrideRegions = mergeRegionsByName(legacyAnnotationRegions, topLevelAnnotationRegions);
5481
+ const regions = mergeRegionsByName(
5482
+ presetOverrideRegions,
5483
+ Array.isArray(config.regions) && config.regions.length > 0 ? config.regions : void 0
5484
+ );
5485
+ if (regions) {
5486
+ overrides.annotationRegions = regions.map((region) => cloneRegion(region));
5487
+ }
5488
+ continue;
5489
+ }
5490
+ const topLevelValue = topLevelConfig[key];
5491
+ const legacyValue = legacyNestedOverrides[key];
5492
+ const mergedValue = mergeProfileOverrideValue(legacyValue, topLevelValue);
5493
+ if (mergedValue !== void 0) {
5494
+ overrides[key] = mergedValue;
5495
+ }
5496
+ }
5497
+ return overrides;
5498
+ }
5499
+ function applyCharacterProfileToPreset(config) {
5500
+ const presetType = config.auPresetType;
5501
+ if (!presetType) {
5502
+ return null;
5503
+ }
5504
+ return applyProfileToPreset(resolvePreset(presetType), extractProfileOverrides(config));
5505
+ }
5506
+ function orderResolvedRegions(resolvedRegions, prioritizedLists) {
5507
+ if (!resolvedRegions) return void 0;
5508
+ const resolvedByName = new Map(resolvedRegions.map((region) => [region.name, region]));
5509
+ const orderedNames = [];
5510
+ const seen = /* @__PURE__ */ new Set();
5511
+ for (const regions of prioritizedLists) {
5512
+ for (const region of regions ?? []) {
5513
+ if (seen.has(region.name)) continue;
5514
+ seen.add(region.name);
5515
+ orderedNames.push(region.name);
5516
+ }
5517
+ }
5518
+ for (const region of resolvedRegions) {
5519
+ if (seen.has(region.name)) continue;
5520
+ seen.add(region.name);
5521
+ orderedNames.push(region.name);
5522
+ }
5523
+ return orderedNames.map((name) => resolvedByName.get(name)).filter((region) => Boolean(region));
5524
+ }
5525
+ function extendCharacterConfigWithPreset(config) {
5526
+ const presetType = config.auPresetType;
5527
+ if (!presetType || presetType === "custom") {
5528
+ return config;
5529
+ }
5530
+ const profileOverrides = extractProfileOverrides(config);
5531
+ const presetResolvedProfile = applyCharacterProfileToPreset(config);
5532
+ if (!presetResolvedProfile) {
5533
+ return config;
5534
+ }
5535
+ const presetRegions = presetResolvedProfile.annotationRegions;
5536
+ const mergedRegions = mergeRegionsByName(presetRegions, config.regions);
5537
+ const resolvedRegions = orderResolvedRegions(
5538
+ mergedRegions,
5539
+ [config.regions, profileOverrides.annotationRegions, presetRegions]
5540
+ );
5541
+ return {
5542
+ ...config,
5543
+ ...presetResolvedProfile,
5544
+ regions: resolvedRegions ?? config.regions
5545
+ };
5546
+ }
5547
+ function resolveCharacterConfig(config) {
5548
+ return extendCharacterConfigWithPreset(config);
5549
+ }
5235
5550
  var DEFAULT_HEAD_BONE_NAMES = ["CC_Base_Head", "Head", "head", "Bip01_Head"];
5236
5551
  var DEFAULT_REFERENCE_HEIGHT = 1.8;
5237
5552
  var DEFAULT_FORWARD_OFFSET = 0.08;
@@ -5416,26 +5731,39 @@ function detectFacingDirection(model, eyeBoneNames = {
5416
5731
  }
5417
5732
 
5418
5733
  // src/regions/regionMapping.ts
5419
- function resolveBoneName(semanticName, config) {
5420
- if (!config) return semanticName;
5734
+ function normalizeLooseName(value) {
5735
+ return value.replace(/\./g, "");
5736
+ }
5737
+ function resolveBoneNameCandidates(semanticName, config) {
5738
+ if (!config) return [semanticName];
5421
5739
  const { bonePrefix, boneSuffix, boneNodes } = config;
5422
5740
  if (!boneNodes || !boneNodes[semanticName]) {
5423
- return semanticName;
5741
+ return [semanticName];
5424
5742
  }
5425
5743
  const baseName = boneNodes[semanticName];
5426
- if (baseName.includes("_") || baseName.includes(".")) {
5427
- return baseName;
5428
- }
5429
5744
  const prefix = bonePrefix || "";
5430
5745
  const suffix = boneSuffix || "";
5431
- return prefix + baseName + suffix;
5746
+ if (!prefix && !suffix) {
5747
+ return [baseName];
5748
+ }
5749
+ const prefixedBase = prefix && baseName.startsWith(prefix) ? baseName : prefix + baseName;
5750
+ const fullyAffixed = suffix && !prefixedBase.endsWith(suffix) ? `${prefixedBase}${suffix}` : prefixedBase;
5751
+ return Array.from(/* @__PURE__ */ new Set([fullyAffixed, baseName]));
5752
+ }
5753
+ function resolveBoneName(semanticName, config) {
5754
+ return resolveBoneNameCandidates(semanticName, config)[0] ?? semanticName;
5432
5755
  }
5433
5756
  function resolveBoneNames(names, config) {
5434
5757
  if (!names || names.length === 0) return [];
5435
- return names.map((name) => resolveBoneName(name, config));
5758
+ return Array.from(
5759
+ new Set(
5760
+ names.flatMap((name) => resolveBoneNameCandidates(name, config))
5761
+ )
5762
+ );
5436
5763
  }
5437
5764
  function fuzzyNameMatch(objectName, targetName, suffixPattern) {
5438
5765
  if (objectName === targetName) return true;
5766
+ if (normalizeLooseName(objectName) === normalizeLooseName(targetName)) return true;
5439
5767
  if (!objectName.startsWith(targetName)) return false;
5440
5768
  const suffix = objectName.slice(targetName.length);
5441
5769
  if (suffix === "") return true;
@@ -5582,6 +5910,9 @@ var HairPhysics = class {
5582
5910
 
5583
5911
  // src/validation/generateMappingCorrections.ts
5584
5912
  var DEFAULT_MIN_CONFIDENCE = 0.6;
5913
+ function normalizeLooseName2(value) {
5914
+ return value.replace(/\./g, "");
5915
+ }
5585
5916
  function levenshteinDistance(a, b) {
5586
5917
  const matrix = [];
5587
5918
  const aLen = a.length;
@@ -5618,6 +5949,9 @@ function scoreCandidate(targetBase, candidate, fullTarget, suffixPattern) {
5618
5949
  if (candidate === fullTarget) {
5619
5950
  return { score: 1, reason: "exact match" };
5620
5951
  }
5952
+ if (normalizeLooseName2(candidate) === normalizeLooseName2(fullTarget)) {
5953
+ return { score: 1, reason: "separator-normalized exact match" };
5954
+ }
5621
5955
  if (suffixPattern && candidate.startsWith(fullTarget)) {
5622
5956
  const suffix = candidate.slice(fullTarget.length);
5623
5957
  if (suffix === "" || suffixPattern.test(suffix)) {
@@ -5806,11 +6140,17 @@ function generateMappingCorrections(meshes, skeleton, config, options = {}) {
5806
6140
  }
5807
6141
 
5808
6142
  // src/validation/validateMappings.ts
6143
+ function normalizeLooseName3(value) {
6144
+ return value.replace(/\./g, "");
6145
+ }
5809
6146
  function fuzzyMatch(targetName, candidateName, prefix, suffix, suffixPattern) {
5810
6147
  const fullTarget = prefix + targetName + suffix;
5811
6148
  if (candidateName === fullTarget) {
5812
6149
  return true;
5813
6150
  }
6151
+ if (normalizeLooseName3(candidateName) === normalizeLooseName3(fullTarget)) {
6152
+ return true;
6153
+ }
5814
6154
  if (suffixPattern && candidateName.startsWith(fullTarget)) {
5815
6155
  const suffix2 = candidateName.slice(fullTarget.length);
5816
6156
  return suffix2 === "" || suffixPattern.test(suffix2);
@@ -6526,6 +6866,7 @@ async function analyzeModel(options) {
6526
6866
  }
6527
6867
 
6528
6868
  exports.AU_INFO = AU_INFO;
6869
+ exports.AU_MAPPING_CONFIG = AU_MAPPING_CONFIG;
6529
6870
  exports.AU_MIX_DEFAULTS = AU_MIX_DEFAULTS;
6530
6871
  exports.AU_TO_MORPHS = AU_TO_MORPHS;
6531
6872
  exports.AnimationThree = AnimationThree;
@@ -6542,7 +6883,7 @@ exports.COMPOSITE_ROTATIONS = COMPOSITE_ROTATIONS;
6542
6883
  exports.CONTINUUM_LABELS = CONTINUUM_LABELS;
6543
6884
  exports.CONTINUUM_PAIRS_MAP = CONTINUUM_PAIRS_MAP;
6544
6885
  exports.DEFAULT_HAIR_PHYSICS_CONFIG = DEFAULT_HAIR_PHYSICS_CONFIG;
6545
- exports.FISH_AU_MAPPING_CONFIG = AU_MAPPING_CONFIG;
6886
+ exports.FISH_AU_MAPPING_CONFIG = FISH_AU_MAPPING_CONFIG;
6546
6887
  exports.HairPhysics = HairPhysics;
6547
6888
  exports.Loom3 = Loom3;
6548
6889
  exports.Loom3Three = Loom3;
@@ -6551,10 +6892,14 @@ exports.MORPH_TO_MESH = MORPH_TO_MESH;
6551
6892
  exports.VISEME_JAW_AMOUNTS = VISEME_JAW_AMOUNTS;
6552
6893
  exports.VISEME_KEYS = VISEME_KEYS;
6553
6894
  exports.analyzeModel = analyzeModel;
6895
+ exports.applyCharacterProfileToPreset = applyCharacterProfileToPreset;
6896
+ exports.applyProfileToPreset = applyProfileToPreset;
6554
6897
  exports.collectMorphMeshes = collectMorphMeshes;
6555
6898
  exports.detectFacingDirection = detectFacingDirection;
6899
+ exports.extendCharacterConfigWithPreset = extendCharacterConfigWithPreset;
6556
6900
  exports.extractFromGLTF = extractFromGLTF;
6557
6901
  exports.extractModelData = extractModelData;
6902
+ exports.extractProfileOverrides = extractProfileOverrides;
6558
6903
  exports.findFaceCenter = findFaceCenter;
6559
6904
  exports.fuzzyNameMatch = fuzzyNameMatch;
6560
6905
  exports.generateMappingCorrections = generateMappingCorrections;
@@ -6562,8 +6907,10 @@ exports.getModelForwardDirection = getModelForwardDirection;
6562
6907
  exports.hasLeftRightMorphs = hasLeftRightMorphs;
6563
6908
  exports.isMixedAU = isMixedAU;
6564
6909
  exports.isPresetCompatible = isPresetCompatible;
6910
+ exports.mergeCharacterRegionsByName = mergeRegionsByName;
6565
6911
  exports.resolveBoneName = resolveBoneName;
6566
6912
  exports.resolveBoneNames = resolveBoneNames;
6913
+ exports.resolveCharacterConfig = resolveCharacterConfig;
6567
6914
  exports.resolveFaceCenter = resolveFaceCenter;
6568
6915
  exports.resolvePreset = resolvePreset;
6569
6916
  exports.resolvePresetWithOverrides = resolvePresetWithOverrides;