@lovelace_lol/embody 0.0.5 → 0.0.6

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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  import { Vector3, NoBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NormalBlending, PropertyBinding, NumberKeyframeTrack, QuaternionKeyframeTrack, VectorKeyframeTrack, AnimationClip, AnimationUtils, AdditiveAnimationBlendMode, NormalAnimationBlendMode, LoopPingPong, LoopOnce, LoopRepeat, Quaternion, AnimationMixer, Box3, Mesh, Clock, BufferAttribute, PlaneGeometry, ShadowMaterial, PMREMGenerator, WebGLRenderer, PCFSoftShadowMap, Scene, Color, PerspectiveCamera, HemisphereLight, DirectionalLight, MathUtils, SRGBColorSpace, ACESFilmicToneMapping } from 'three';
3
- import { initEmbodyCore, HAIR_MORPH_OUTPUT_FIELDS, HAIR_CONFIG_STRIDE, HAIR_HEAD_STATE_STRIDE, HAIR_MORPH_OUTPUT_STRIDE, HAIR_STATE_STRIDE } from '@lovelace_lol/embody/wasm';
3
+ import { initEmbodyCore, requireInitializedEmbodyCore, HAIR_MORPH_OUTPUT_FIELDS, HAIR_CONFIG_STRIDE, HAIR_HEAD_STATE_STRIDE, HAIR_MORPH_OUTPUT_STRIDE, HAIR_STATE_STRIDE } from '@lovelace_lol/embody/wasm';
4
4
  export { EMBODY_CORE_ABI_VERSION, HAIR_CONFIG_FIELDS, HAIR_CONFIG_STRIDE, HAIR_HEAD_STATE_FIELDS, HAIR_HEAD_STATE_STRIDE, HAIR_MORPH_OUTPUT_FIELDS, HAIR_MORPH_OUTPUT_STRIDE, HAIR_STATE_FIELDS, HAIR_STATE_STRIDE, MESH_PROPORTIONS_FIELDS, MESH_PROPORTIONS_STRIDE, PACKED_BONE_FRAME_DELTA_FIELDS, PACKED_BONE_FRAME_DELTA_STRIDE, PACKED_MORPH_FRAME_DELTA_FIELDS, PACKED_MORPH_FRAME_DELTA_STRIDE, TEMPLATE_SKELETON_FIT_SOLUTION_FIELDS, TEMPLATE_SKELETON_FIT_SOLUTION_STRIDE, TEMPLATE_SKELETON_FIT_TRANSFORM_FIELDS, TEMPLATE_SKELETON_FIT_TRANSFORM_STRIDE, getEmbodyCore, initEmbodyCore, requireInitializedEmbodyCore, resetEmbodyCoreForTests } from '@lovelace_lol/embody/wasm';
5
5
  import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
6
6
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
@@ -3676,410 +3676,6 @@ function getBlendingName(value) {
3676
3676
  return "Normal";
3677
3677
  }
3678
3678
 
3679
- // src/core/TsRuntimeCore.ts
3680
- var identityQuat = { x: 0, y: 0, z: 0, w: 1 };
3681
- function isRotationBoneBinding2(binding) {
3682
- return binding?.channel === "rx" || binding?.channel === "ry" || binding?.channel === "rz";
3683
- }
3684
- var TsRuntimeCore = class {
3685
- constructor(options) {
3686
- __publicField(this, "profile");
3687
- __publicField(this, "model");
3688
- __publicField(this, "auValues", {});
3689
- __publicField(this, "auBalances", {});
3690
- __publicField(this, "visemeValues");
3691
- __publicField(this, "visemeJawScales");
3692
- __publicField(this, "mixWeights");
3693
- this.profile = options.profile;
3694
- this.model = options.model;
3695
- this.visemeValues = new Array(getProfileVisemeSlots(this.profile).length).fill(0);
3696
- this.visemeJawScales = new Array(this.visemeValues.length).fill(1);
3697
- this.mixWeights = { ...this.profile.auMixDefaults };
3698
- }
3699
- setProfile(profile) {
3700
- this.profile = profile;
3701
- this.visemeValues = new Array(getProfileVisemeSlots(this.profile).length).fill(0);
3702
- this.visemeJawScales = new Array(this.visemeValues.length).fill(1);
3703
- this.mixWeights = { ...this.profile.auMixDefaults };
3704
- }
3705
- setModelDescriptor(model) {
3706
- this.model = model;
3707
- }
3708
- setAU(id, value, balance) {
3709
- if (value < 0 && this.profile.continuumPairs) {
3710
- const pairInfo = this.profile.continuumPairs[id];
3711
- if (pairInfo) {
3712
- const negAU = pairInfo.isNegative ? id : pairInfo.pairId;
3713
- const posAU = pairInfo.isNegative ? pairInfo.pairId : id;
3714
- this.setContinuum(negAU, posAU, pairInfo.isNegative ? -value : value, balance);
3715
- return;
3716
- }
3717
- }
3718
- this.auValues[id] = clamp01(value);
3719
- if (balance !== void 0) {
3720
- this.auBalances[id] = clampBalance2(balance);
3721
- }
3722
- }
3723
- getAU(id) {
3724
- return this.auValues[id] ?? 0;
3725
- }
3726
- setContinuum(negAU, posAU, continuumValue, balance) {
3727
- const value = Math.max(-1, Math.min(1, continuumValue));
3728
- if (value < 0) {
3729
- this.setAU(posAU, 0, balance);
3730
- this.setAU(negAU, Math.abs(value), balance);
3731
- return;
3732
- }
3733
- if (value > 0) {
3734
- this.setAU(negAU, 0, balance);
3735
- this.setAU(posAU, value, balance);
3736
- return;
3737
- }
3738
- this.setAU(negAU, 0, balance);
3739
- this.setAU(posAU, 0, balance);
3740
- }
3741
- setViseme(visemeIndex, value, jawScale = 1) {
3742
- if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) return;
3743
- this.visemeValues[visemeIndex] = clamp01(value);
3744
- this.visemeJawScales[visemeIndex] = jawScale;
3745
- }
3746
- setVisemeById(slotId, value, jawScale = 1) {
3747
- const index = getVisemeSlotIndex(this.profile, slotId);
3748
- if (index < 0) return;
3749
- this.setViseme(index, value, jawScale);
3750
- }
3751
- setAUMixWeight(id, weight) {
3752
- this.mixWeights[id] = clamp01(weight);
3753
- }
3754
- evaluateFrameDelta(deltaSeconds) {
3755
- const morphWrites = /* @__PURE__ */ new Map();
3756
- this.collectAUWrites(morphWrites);
3757
- this.collectVisemeWrites(morphWrites);
3758
- return {
3759
- deltaSeconds,
3760
- morphTargets: Array.from(morphWrites.values()),
3761
- bones: this.collectBoneWrites()
3762
- };
3763
- }
3764
- collectAUWrites(morphWrites) {
3765
- for (const [auIdText, entry] of Object.entries(this.profile.auToMorphs || {})) {
3766
- const auId = Number(auIdText);
3767
- if (Number.isNaN(auId) || !entry) continue;
3768
- const mixWeight = this.isMixedAU(auId) ? this.getAUMixWeight(auId) : 1;
3769
- const base = clamp01(this.auValues[auId] ?? 0) * mixWeight;
3770
- const { left, right } = computeSideValues(base, this.auBalances[auId]);
3771
- const meshNames = getMeshNamesForAUProfile(this.profile, auId);
3772
- for (const morph of entry.left || []) {
3773
- this.addMorphWrites(morphWrites, morph, meshNames, left);
3774
- }
3775
- for (const morph of entry.right || []) {
3776
- this.addMorphWrites(morphWrites, morph, meshNames, right);
3777
- }
3778
- for (const morph of entry.center || []) {
3779
- this.addMorphWrites(morphWrites, morph, meshNames, base);
3780
- }
3781
- }
3782
- }
3783
- collectVisemeWrites(morphWrites) {
3784
- const meshNames = getMeshNamesForVisemeProfile(this.profile);
3785
- const localMax = /* @__PURE__ */ new Map();
3786
- for (let index = 0; index < this.visemeValues.length; index += 1) {
3787
- const value = clamp01(this.visemeValues[index] ?? 0);
3788
- for (const bindingTarget of getVisemeBindingTargets(this.profile, index)) {
3789
- const weighted = clamp01(value * bindingTarget.weight);
3790
- for (const resolved of this.resolveMorphTargets(bindingTarget.morph, meshNames)) {
3791
- const key = morphWriteKey(resolved.mesh.id, resolved.morphTarget.id);
3792
- const existing = localMax.get(key);
3793
- if (!existing || weighted > existing.value) {
3794
- localMax.set(key, {
3795
- meshId: resolved.mesh.id,
3796
- morphTargetId: resolved.morphTarget.id,
3797
- value: weighted,
3798
- mode: "absolute"
3799
- });
3800
- }
3801
- }
3802
- }
3803
- }
3804
- for (const [key, write] of localMax) {
3805
- const existing = morphWrites.get(key);
3806
- if (!existing || write.value > existing.value) {
3807
- morphWrites.set(key, write);
3808
- }
3809
- }
3810
- }
3811
- collectBoneWrites() {
3812
- const writes = [];
3813
- const rotations = this.computeRotationState();
3814
- for (const composite of this.profile.compositeRotations || []) {
3815
- const bone = this.findBoneDescriptor(composite.node);
3816
- if (!bone) continue;
3817
- const transform = this.evaluateCompositeTransform(bone, composite, rotations[composite.node]);
3818
- if (transform) {
3819
- writes.push({
3820
- boneId: bone.id,
3821
- transform,
3822
- mode: "absolute",
3823
- space: "local"
3824
- });
3825
- }
3826
- }
3827
- for (const [nodeKey, translation] of Object.entries(this.computeTranslations())) {
3828
- const bone = this.findBoneDescriptor(nodeKey);
3829
- if (!bone) continue;
3830
- const position = addVec3(bone.restTransform?.position, translation);
3831
- upsertBoneWrite(writes, bone.id, { position });
3832
- }
3833
- const jawAmount = this.getActiveVisemeJawAmount();
3834
- const jawBinding = this.findAutoVisemeJawBinding();
3835
- const jawBone = jawBinding ? this.findBoneDescriptor(jawBinding.node) : void 0;
3836
- if (jawBone && jawBinding?.maxDegrees && jawBinding.channel) {
3837
- const current = writes.find((write) => write.boneId === jawBone.id);
3838
- if (jawAmount > 1e-6 || !current?.transform.rotation) {
3839
- const rest = jawBone.restTransform?.rotation ?? identityQuat;
3840
- const rotation = jawAmount > 1e-6 ? multiplyQuat(
3841
- rest,
3842
- quatFromChannel(jawBinding.channel, deg2rad(jawBinding.maxDegrees) * jawAmount * jawBinding.scale)
3843
- ) : rest;
3844
- upsertBoneWrite(writes, jawBone.id, { rotation });
3845
- }
3846
- }
3847
- return writes;
3848
- }
3849
- computeRotationState() {
3850
- const rotations = {};
3851
- for (const composite of this.profile.compositeRotations || []) {
3852
- rotations[composite.node] = {
3853
- pitch: this.getCompositeAxisValueForNode(composite.node, composite.pitch),
3854
- yaw: this.getCompositeAxisValueForNode(composite.node, composite.yaw),
3855
- roll: this.getCompositeAxisValueForNode(composite.node, composite.roll)
3856
- };
3857
- }
3858
- return rotations;
3859
- }
3860
- evaluateCompositeTransform(bone, composite, state) {
3861
- if (!state) return null;
3862
- let rotation = bone.restTransform?.rotation ?? identityQuat;
3863
- const applyAxis = (axisConfig, direction) => {
3864
- if (!axisConfig || Math.abs(direction) <= 1e-6) return;
3865
- const binding = getCompositeAxisBinding(
3866
- composite.node,
3867
- axisConfig,
3868
- direction,
3869
- (auId) => this.getEffectiveBoneAUValue(auId, composite.node),
3870
- this.profile.auToBones
3871
- );
3872
- if (!binding?.maxDegrees || !binding.channel) return;
3873
- rotation = multiplyQuat(
3874
- rotation,
3875
- quatFromChannel(binding.channel, deg2rad(binding.maxDegrees) * Math.abs(direction) * binding.scale)
3876
- );
3877
- };
3878
- applyAxis(composite.yaw, state.yaw);
3879
- applyAxis(composite.pitch, state.pitch);
3880
- applyAxis(composite.roll, state.roll);
3881
- return { rotation };
3882
- }
3883
- computeTranslations() {
3884
- const translations = {};
3885
- for (const [auIdText, bindings] of Object.entries(this.profile.auToBones || {})) {
3886
- const auId = Number(auIdText);
3887
- if (Number.isNaN(auId)) continue;
3888
- const value = clamp01(this.auValues[auId] ?? 0);
3889
- for (const binding of bindings || []) {
3890
- if (binding.channel !== "tx" && binding.channel !== "ty" && binding.channel !== "tz") continue;
3891
- if (binding.maxUnits === void 0) continue;
3892
- const current = translations[binding.node] ?? { x: 0, y: 0, z: 0 };
3893
- translations[binding.node] = current;
3894
- if (value <= 1e-6) continue;
3895
- const offset = clampSigned(value * binding.scale) * binding.maxUnits;
3896
- translations[binding.node] = {
3897
- x: binding.channel === "tx" ? offset : current.x,
3898
- y: binding.channel === "ty" ? offset : current.y,
3899
- z: binding.channel === "tz" ? offset : current.z
3900
- };
3901
- }
3902
- }
3903
- return translations;
3904
- }
3905
- addMorphWrites(morphWrites, morph, meshNames, value) {
3906
- for (const resolved of this.resolveMorphTargets(morph, meshNames)) {
3907
- const key = morphWriteKey(resolved.mesh.id, resolved.morphTarget.id);
3908
- const existing = morphWrites.get(key);
3909
- if (existing && existing.value >= value) continue;
3910
- morphWrites.set(key, {
3911
- meshId: resolved.mesh.id,
3912
- morphTargetId: resolved.morphTarget.id,
3913
- value,
3914
- mode: "absolute"
3915
- });
3916
- }
3917
- }
3918
- resolveMorphTargets(morph, meshNames) {
3919
- const result = [];
3920
- for (const meshName of meshNames) {
3921
- const mesh = this.model.meshes.find((candidate) => candidate.name === meshName);
3922
- if (!mesh) continue;
3923
- const morphTargets = mesh.morphTargetIds.map((id) => this.model.morphTargets.find((candidate) => candidate.id === id)).filter((target2) => !!target2);
3924
- const target = typeof morph === "number" ? morphTargets.find((candidate) => candidate.hostIndex === morph) : resolveMorphTargetByName(morphTargets, morph, this.profile);
3925
- if (target) {
3926
- result.push({ mesh, morphTarget: target });
3927
- }
3928
- }
3929
- return result;
3930
- }
3931
- findBoneDescriptor(nodeKey) {
3932
- const configuredName = this.profile.boneNodes[nodeKey] || nodeKey;
3933
- const prefix = this.profile.bonePrefix ?? "";
3934
- const suffix = this.profile.boneSuffix ?? "";
3935
- const prefixedName = prefix && !configuredName.startsWith(prefix) ? `${prefix}${configuredName}` : configuredName;
3936
- const fullName = suffix && !prefixedName.endsWith(suffix) ? `${prefixedName}${suffix}` : prefixedName;
3937
- const candidates = /* @__PURE__ */ new Set([nodeKey, configuredName, fullName]);
3938
- return this.model.bones.find((bone) => candidates.has(bone.name));
3939
- }
3940
- findAutoVisemeJawBinding() {
3941
- const candidates = [
3942
- this.profile.auToBones[103]?.find(isRotationBoneBinding2),
3943
- this.profile.auToBones[26]?.find(isRotationBoneBinding2)
3944
- ].filter((binding) => !!binding);
3945
- return candidates.find((binding) => !!this.findBoneDescriptor(binding.node)) ?? candidates[0] ?? null;
3946
- }
3947
- getActiveVisemeJawAmount() {
3948
- const jawAmounts = getVisemeJawAmounts(this.profile) ?? [];
3949
- let jawAmount = 0;
3950
- for (let index = 0; index < this.visemeValues.length; index += 1) {
3951
- const value = clamp01(this.visemeValues[index] ?? 0);
3952
- if (value <= 1e-6) continue;
3953
- const jawScale = this.visemeJawScales[index] ?? 1;
3954
- if (Math.abs(jawScale) <= 1e-6) continue;
3955
- jawAmount = Math.max(jawAmount, (jawAmounts[index] ?? 0) * value * jawScale);
3956
- }
3957
- return jawAmount;
3958
- }
3959
- getCompositeAxisValueForNode(nodeKey, axisConfig) {
3960
- return getCompositeAxisValue(axisConfig, (auId) => this.getEffectiveBoneAUValue(auId, nodeKey));
3961
- }
3962
- getEffectiveBoneAUValue(auId, nodeKey) {
3963
- const rawValue = clamp01(this.auValues[auId] ?? 0);
3964
- if (rawValue <= 1e-6) return 0;
3965
- const binding = this.profile.auToBones[auId]?.find((candidate) => candidate.node === nodeKey) ?? null;
3966
- if (!binding?.side) return rawValue;
3967
- return rawValue * getSideScale2(this.auBalances[auId] ?? 0, binding.side);
3968
- }
3969
- getAUMixWeight(auId) {
3970
- return this.mixWeights[auId] ?? this.profile.auMixDefaults?.[auId] ?? 1;
3971
- }
3972
- isMixedAU(auId) {
3973
- const morphs = this.profile.auToMorphs[auId];
3974
- const hasMorphs = !!(morphs?.left?.length || morphs?.right?.length || morphs?.center?.length);
3975
- return !!(hasMorphs && this.profile.auToBones[auId]?.length);
3976
- }
3977
- };
3978
- function resolveMorphTargetByName(targets, key, profile) {
3979
- const prefix = profile.morphPrefix || "";
3980
- const suffix = profile.morphSuffix || "";
3981
- const fullName = prefix + key + suffix;
3982
- const exact = targets.find((target) => target.name === fullName);
3983
- if (exact) return exact;
3984
- const suffixRegex = profile.suffixPattern ? new RegExp(profile.suffixPattern) : null;
3985
- if (!suffixRegex) return void 0;
3986
- return targets.find((target) => {
3987
- if (!target.name.startsWith(fullName)) return false;
3988
- const candidateSuffix = target.name.slice(fullName.length);
3989
- return candidateSuffix === "" || suffixRegex.test(candidateSuffix);
3990
- });
3991
- }
3992
- function computeSideValues(base, balance) {
3993
- const b = clampBalance2(balance ?? 0);
3994
- if (b === 0) return { left: base, right: base };
3995
- if (b < 0) return { left: base, right: base * (1 + b) };
3996
- return { left: base * (1 - b), right: base };
3997
- }
3998
- function getSideScale2(balance, side) {
3999
- if (side !== "left" && side !== "right") return 1;
4000
- const b = clampBalance2(balance);
4001
- if (side === "left") return b > 0 ? 1 - b : 1;
4002
- return b < 0 ? 1 + b : 1;
4003
- }
4004
- function addVec3(base, offset) {
4005
- return {
4006
- x: (base?.x ?? 0) + offset.x,
4007
- y: (base?.y ?? 0) + offset.y,
4008
- z: (base?.z ?? 0) + offset.z
4009
- };
4010
- }
4011
- function upsertBoneWrite(writes, boneId2, transform) {
4012
- const existingIndex = writes.findIndex((write) => write.boneId === boneId2);
4013
- if (existingIndex >= 0) {
4014
- const existing = writes[existingIndex];
4015
- writes[existingIndex] = {
4016
- ...existing,
4017
- transform: {
4018
- ...existing.transform,
4019
- ...transform
4020
- }
4021
- };
4022
- return;
4023
- }
4024
- writes.push({
4025
- boneId: boneId2,
4026
- transform,
4027
- mode: "absolute",
4028
- space: "local"
4029
- });
4030
- }
4031
- function quatFromChannel(channel, radians) {
4032
- if (channel === "rx") return quatFromAxis({ x: 1, y: 0, z: 0 }, radians);
4033
- if (channel === "ry") return quatFromAxis({ x: 0, y: 1, z: 0 }, radians);
4034
- return quatFromAxis({ x: 0, y: 0, z: 1 }, radians);
4035
- }
4036
- function quatFromAxis(axis, radians) {
4037
- const half = radians / 2;
4038
- const s = Math.sin(half);
4039
- return normalizeQuat({
4040
- x: axis.x * s,
4041
- y: axis.y * s,
4042
- z: axis.z * s,
4043
- w: Math.cos(half)
4044
- });
4045
- }
4046
- function multiplyQuat(a, b) {
4047
- return normalizeQuat({
4048
- x: a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
4049
- y: a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
4050
- z: a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
4051
- w: a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z
4052
- });
4053
- }
4054
- function normalizeQuat(q) {
4055
- const len = Math.hypot(q.x, q.y, q.z, q.w);
4056
- if (len <= 1e-12) return identityQuat;
4057
- return {
4058
- x: q.x / len,
4059
- y: q.y / len,
4060
- z: q.z / len,
4061
- w: q.w / len
4062
- };
4063
- }
4064
- function deg2rad(degrees) {
4065
- return degrees * Math.PI / 180;
4066
- }
4067
- function clamp01(value) {
4068
- if (!Number.isFinite(value)) return 0;
4069
- return value < 0 ? 0 : value > 1 ? 1 : value;
4070
- }
4071
- function clampSigned(value) {
4072
- if (!Number.isFinite(value)) return 0;
4073
- return value < -1 ? -1 : value > 1 ? 1 : value;
4074
- }
4075
- function clampBalance2(value) {
4076
- if (!Number.isFinite(value)) return 0;
4077
- return Math.max(-1, Math.min(1, value));
4078
- }
4079
- function morphWriteKey(meshId2, morphTargetId2) {
4080
- return `${meshId2}:${morphTargetId2}`;
4081
- }
4082
-
4083
3679
  // src/core/contracts/abi.ts
4084
3680
  var PACKED_MORPH_FRAME_DELTA_STRIDE = 4;
4085
3681
  var PACKED_BONE_FRAME_DELTA_STRIDE = 9;
@@ -4213,11 +3809,13 @@ var WasmRuntimeCore = class {
4213
3809
  constructor(options) {
4214
3810
  __publicField(this, "wasm");
4215
3811
  __publicField(this, "core");
3812
+ __publicField(this, "allowEmptyBindings");
4216
3813
  __publicField(this, "profile");
4217
3814
  __publicField(this, "model");
4218
3815
  this.wasm = options.wasm;
4219
3816
  this.profile = options.profile;
4220
3817
  this.model = options.model;
3818
+ this.allowEmptyBindings = options.allowEmptyBindings ?? false;
4221
3819
  const RuntimeCtor = options.wasm.RuntimeCore;
4222
3820
  if (!RuntimeCtor) {
4223
3821
  throw new Error("Embody Wasm module does not export RuntimeCore");
@@ -4236,6 +3834,24 @@ var WasmRuntimeCore = class {
4236
3834
  setAU(id, value, balance = 0) {
4237
3835
  this.core.set_au(id >>> 0, value, balance);
4238
3836
  }
3837
+ setContinuum(negativeAU, positiveAU, value, balance = 0) {
3838
+ this.core.set_continuum(negativeAU >>> 0, positiveAU >>> 0, value, balance);
3839
+ }
3840
+ transitionAU(id, to, durationMs, balance = Number.NaN) {
3841
+ this.core.transition_au(id >>> 0, to, durationMs, balance);
3842
+ }
3843
+ transitionViseme(visemeIndex, to, durationMs, jawScale = 1) {
3844
+ this.core.transition_viseme(visemeIndex >>> 0, to, durationMs, jawScale);
3845
+ }
3846
+ update(deltaSeconds) {
3847
+ return this.core.update(deltaSeconds);
3848
+ }
3849
+ activeTransitionCount() {
3850
+ return this.core.active_transition_count();
3851
+ }
3852
+ clearTransitions() {
3853
+ this.core.clear_transitions();
3854
+ }
4239
3855
  getAU(id) {
4240
3856
  return this.core.get_au(id >>> 0);
4241
3857
  }
@@ -4271,7 +3887,13 @@ var WasmRuntimeCore = class {
4271
3887
  };
4272
3888
  }
4273
3889
  reloadBindings() {
4274
- this.core.configure_with_profile(JSON.stringify(this.profile), JSON.stringify(this.model));
3890
+ const profileJson = JSON.stringify(this.profile);
3891
+ const modelJson = JSON.stringify(this.model);
3892
+ if (this.allowEmptyBindings) {
3893
+ this.core.configure(profileJson, modelJson);
3894
+ } else {
3895
+ this.core.configure_with_profile(profileJson, modelJson);
3896
+ }
4275
3897
  }
4276
3898
  };
4277
3899
  function unpackBoneFrameDelta(values) {
@@ -5629,7 +5251,7 @@ var CC4_PRESET = {
5629
5251
  };
5630
5252
 
5631
5253
  // src/engines/three/hair/HairPhysicsController.ts
5632
- var clamp012 = (value) => value < 0 ? 0 : value > 1 ? 1 : value;
5254
+ var clamp01 = (value) => value < 0 ? 0 : value > 1 ? 1 : value;
5633
5255
  var HairPhysicsController = class {
5634
5256
  constructor(host) {
5635
5257
  __publicField(this, "host");
@@ -6127,7 +5749,7 @@ var HairPhysicsController = class {
6127
5749
  }
6128
5750
  }
6129
5751
  setMorphOnMeshes(meshNames, morphKey, value) {
6130
- const val = clamp012(value);
5752
+ const val = clamp01(value);
6131
5753
  for (const name of meshNames) {
6132
5754
  const mesh = this.registeredHairObjects.get(name) || this.host.getMeshByName(name);
6133
5755
  if (!mesh) continue;
@@ -6371,9 +5993,9 @@ var HairPhysicsController = class {
6371
5993
  const deltaVertical = vertical - this.lastHeadVertical;
6372
5994
  this.lastHeadHorizontal = horizontal;
6373
5995
  this.lastHeadVertical = vertical;
6374
- const leftWeight = clamp012(deltaHorizontal > 0 ? deltaHorizontal : 0);
6375
- const rightWeight = clamp012(deltaHorizontal < 0 ? -deltaHorizontal : 0);
6376
- const frontWeight = clamp012(deltaVertical > 0 ? deltaVertical : 0);
5996
+ const leftWeight = clamp01(deltaHorizontal > 0 ? deltaHorizontal : 0);
5997
+ const rightWeight = clamp01(deltaHorizontal < 0 ? -deltaHorizontal : 0);
5998
+ const frontWeight = clamp01(deltaVertical > 0 ? deltaVertical : 0);
6377
5999
  const minTrigger = 0.01;
6378
6000
  if (leftWeight <= minTrigger && rightWeight <= minTrigger && frontWeight <= minTrigger) return;
6379
6001
  if (this.impulseClipDirty) {
@@ -7343,7 +6965,7 @@ async function getPresetWithProfile(presetType, profile) {
7343
6965
  var resolvePresetWithOverrides = getPresetWithProfile;
7344
6966
 
7345
6967
  // src/engines/three/Embody.ts
7346
- var deg2rad2 = (d) => d * Math.PI / 180;
6968
+ var deg2rad = (d) => d * Math.PI / 180;
7347
6969
  var X_AXIS2 = new Vector3(1, 0, 0);
7348
6970
  var Y_AXIS2 = new Vector3(0, 1, 0);
7349
6971
  var Z_AXIS2 = new Vector3(0, 0, 1);
@@ -7366,9 +6988,18 @@ function buildAUToCompositeMap(composites) {
7366
6988
  });
7367
6989
  return map;
7368
6990
  }
7369
- function clamp013(x) {
6991
+ function clamp012(x) {
7370
6992
  return x < 0 ? 0 : x > 1 ? 1 : x;
7371
6993
  }
6994
+ var RESOLVED_TRANSITION_HANDLE = {
6995
+ promise: Promise.resolve(),
6996
+ pause: () => {
6997
+ },
6998
+ resume: () => {
6999
+ },
7000
+ cancel: () => {
7001
+ }
7002
+ };
7372
7003
  var _Embody = class _Embody {
7373
7004
  constructor(config = {}, animation) {
7374
7005
  // Optional hook for animation schedulers.
@@ -7412,7 +7043,6 @@ var _Embody = class _Embody {
7412
7043
  __publicField(this, "modelInspector", new ThreeModelInspector());
7413
7044
  __publicField(this, "frameApplier", new ThreeFrameApplier());
7414
7045
  __publicField(this, "modelDescriptor", null);
7415
- __publicField(this, "tsRuntimeCore", null);
7416
7046
  __publicField(this, "wasmRuntimeCore", null);
7417
7047
  __publicField(this, "framePathReady", false);
7418
7048
  // Internal animation loop
@@ -7525,48 +7155,21 @@ var _Embody = class _Embody {
7525
7155
  bindHostNeutralRuntime(inspection) {
7526
7156
  this.modelDescriptor = inspection.descriptor;
7527
7157
  this.frameApplier.setBindings(buildFrameApplierBindings(inspection));
7528
- this.tsRuntimeCore = new TsRuntimeCore({
7158
+ const wasm = requireInitializedEmbodyCore();
7159
+ this.attachWasmCore(wasm);
7160
+ this.wasmRuntimeCore = new WasmRuntimeCore({
7529
7161
  profile: this.config,
7530
- model: inspection.descriptor
7162
+ model: inspection.descriptor,
7163
+ wasm,
7164
+ allowEmptyBindings: true
7531
7165
  });
7532
7166
  this.syncRuntimeCoreState();
7533
7167
  this.framePathReady = true;
7534
- void initEmbodyCore().then((wasm) => {
7535
- if (!this.modelDescriptor) return;
7536
- this.attachWasmCore(wasm);
7537
- this.wasmRuntimeCore = new WasmRuntimeCore({
7538
- profile: this.config,
7539
- model: this.modelDescriptor,
7540
- wasm
7541
- });
7542
- this.syncRuntimeCoreState();
7543
- }).catch(() => {
7544
- this.wasmRuntimeCore = null;
7545
- });
7546
7168
  }
7547
7169
  attachWasmCore(core) {
7548
7170
  this.hairPhysics.setCore(core);
7549
7171
  }
7550
7172
  syncRuntimeCoreState() {
7551
- if (this.tsRuntimeCore) {
7552
- this.tsRuntimeCore.setProfile(this.config);
7553
- if (this.modelDescriptor) {
7554
- this.tsRuntimeCore.setModelDescriptor(this.modelDescriptor);
7555
- }
7556
- for (const [auIdText, value] of Object.entries(this.auValues)) {
7557
- const auId = Number(auIdText);
7558
- if (Number.isNaN(auId)) continue;
7559
- this.tsRuntimeCore.setAU(auId, value, this.auBalances[auId]);
7560
- }
7561
- for (const [auIdText, weight] of Object.entries(this.mixWeights)) {
7562
- const auId = Number(auIdText);
7563
- if (Number.isNaN(auId)) continue;
7564
- this.tsRuntimeCore.setAUMixWeight(auId, weight);
7565
- }
7566
- for (let index = 0; index < this.visemeValues.length; index += 1) {
7567
- this.tsRuntimeCore.setViseme(index, this.visemeValues[index] ?? 0, this.visemeJawScales[index] ?? 1);
7568
- }
7569
- }
7570
7173
  if (this.wasmRuntimeCore) {
7571
7174
  this.wasmRuntimeCore.setProfile(this.config);
7572
7175
  if (this.modelDescriptor) {
@@ -7584,7 +7187,7 @@ var _Embody = class _Embody {
7584
7187
  this.wasmRuntimeCore.setAUMixWeight(auId, weight);
7585
7188
  }
7586
7189
  for (let index = 0; index < this.visemeValues.length; index += 1) {
7587
- this.wasmRuntimeCore.setViseme(index, this.visemeValues[index] ?? 0);
7190
+ this.wasmRuntimeCore.setViseme(index, this.visemeValues[index] ?? 0, this.visemeJawScales[index] ?? 1);
7588
7191
  }
7589
7192
  }
7590
7193
  }
@@ -7602,25 +7205,13 @@ var _Embody = class _Embody {
7602
7205
  });
7603
7206
  this.modelDescriptor = inspection.descriptor;
7604
7207
  this.frameApplier.setBindings(buildFrameApplierBindings(inspection));
7605
- this.tsRuntimeCore?.setModelDescriptor(inspection.descriptor);
7606
7208
  this.wasmRuntimeCore?.setModelDescriptor(inspection.descriptor);
7607
7209
  }
7608
- applyLiveMorphFrameDelta() {
7609
- if (!this.framePathReady || !this.model) return false;
7610
- if (this.wasmRuntimeCore) {
7611
- this.frameApplier.applyPackedMorphFrameDelta(
7612
- this.wasmRuntimeCore.evaluatePackedMorphFrameDelta()
7613
- );
7614
- return true;
7615
- }
7616
- if (this.tsRuntimeCore) {
7617
- const frame = this.tsRuntimeCore.evaluateFrameDelta();
7618
- this.frameApplier.applyFrameDelta(this.model, {
7619
- morphTargets: frame.morphTargets
7620
- });
7621
- return true;
7622
- }
7623
- return false;
7210
+ applyLiveFrameDelta() {
7211
+ if (!this.framePathReady || !this.model || !this.wasmRuntimeCore) return false;
7212
+ this.frameApplier.applyFrameDelta(this.model, this.wasmRuntimeCore.evaluateFrameDelta());
7213
+ this.pendingCompositeNodes.clear();
7214
+ return true;
7624
7215
  }
7625
7216
  rebuildMorphTargetsCache() {
7626
7217
  this.morphKeyCache.clear();
@@ -7722,6 +7313,11 @@ var _Embody = class _Embody {
7722
7313
  const dtSeconds = Math.max(0, deltaSeconds || 0);
7723
7314
  if (dtSeconds <= 0 || this.isPaused) return;
7724
7315
  this.animation.tick(dtSeconds);
7316
+ const wasmTransitionCount = this.wasmRuntimeCore?.activeTransitionCount() ?? 0;
7317
+ const remainingWasmTransitions = this.wasmRuntimeCore?.update(dtSeconds) ?? 0;
7318
+ if (wasmTransitionCount > 0 || remainingWasmTransitions > 0) {
7319
+ this.applyLiveFrameDelta();
7320
+ }
7725
7321
  this.flushPendingComposites();
7726
7322
  this.animationController.update(dtSeconds);
7727
7323
  this.hairPhysics.update(dtSeconds);
@@ -7756,7 +7352,6 @@ var _Embody = class _Embody {
7756
7352
  this.model = null;
7757
7353
  this.bones = {};
7758
7354
  this.modelDescriptor = null;
7759
- this.tsRuntimeCore = null;
7760
7355
  this.wasmRuntimeCore = null;
7761
7356
  this.framePathReady = false;
7762
7357
  }
@@ -7793,15 +7388,14 @@ var _Embody = class _Embody {
7793
7388
  if (balance !== void 0) {
7794
7389
  this.auBalances[id] = balance;
7795
7390
  }
7796
- this.tsRuntimeCore?.setAU(id, v, balance);
7797
7391
  this.wasmRuntimeCore?.setAU(id, v, balance ?? this.auBalances[id] ?? 0);
7798
- const appliedViaFrame = this.applyLiveMorphFrameDelta();
7392
+ const appliedViaFrame = this.applyLiveFrameDelta();
7799
7393
  if (!appliedViaFrame) {
7800
7394
  const resolvedMorphTargets = this.resolvedAUMorphTargets.get(id);
7801
7395
  const { left: leftKeys, right: rightKeys, center: centerKeys } = this.getAUMorphsBySide(id);
7802
7396
  if (resolvedMorphTargets) {
7803
7397
  const mixWeight = this.isMixedAU(id) ? this.getAUMixWeight(id) : 1;
7804
- const base = clamp013(v) * mixWeight;
7398
+ const base = clamp012(v) * mixWeight;
7805
7399
  const { left: leftVal, right: rightVal } = this.computeSideValues(base, balance);
7806
7400
  if (resolvedMorphTargets.left.length || resolvedMorphTargets.right.length) {
7807
7401
  this.applyMorphTargets(resolvedMorphTargets.left, leftVal);
@@ -7810,7 +7404,7 @@ var _Embody = class _Embody {
7810
7404
  this.applyMorphTargets(resolvedMorphTargets.center, base);
7811
7405
  } else if (leftKeys.length || rightKeys.length || centerKeys.length) {
7812
7406
  const mixWeight = this.isMixedAU(id) ? this.getAUMixWeight(id) : 1;
7813
- const base = clamp013(v) * mixWeight;
7407
+ const base = clamp012(v) * mixWeight;
7814
7408
  const meshNames = this.getMeshNamesForAU(id);
7815
7409
  const { left: leftVal, right: rightVal } = this.computeSideValues(base, balance);
7816
7410
  if (leftKeys.length || rightKeys.length) {
@@ -7829,6 +7423,7 @@ var _Embody = class _Embody {
7829
7423
  }
7830
7424
  }
7831
7425
  }
7426
+ if (appliedViaFrame) return;
7832
7427
  const compositeInfo = this.auToCompositeMap.get(id);
7833
7428
  if (compositeInfo) {
7834
7429
  for (const nodeKey of compositeInfo.nodes) {
@@ -7863,7 +7458,7 @@ var _Embody = class _Embody {
7863
7458
  return this.transitionContinuum(negAU, posAU, continuumValue, durationMs, balance);
7864
7459
  }
7865
7460
  }
7866
- const target = clamp013(to);
7461
+ const target = clamp012(to);
7867
7462
  if (balance !== void 0) {
7868
7463
  this.auBalances[numId] = balance;
7869
7464
  }
@@ -7874,6 +7469,19 @@ var _Embody = class _Embody {
7874
7469
  const base = target * mixWeight;
7875
7470
  const { left: leftVal, right: rightVal } = this.computeSideValues(base, storedBalance);
7876
7471
  this.auValues[numId] = target;
7472
+ if (this.wasmRuntimeCore) {
7473
+ this.wasmRuntimeCore.transitionAU(
7474
+ numId,
7475
+ target,
7476
+ durationMs,
7477
+ balance ?? this.auBalances[numId] ?? Number.NaN
7478
+ );
7479
+ this.update(0);
7480
+ if (durationMs <= 0) {
7481
+ this.applyLiveFrameDelta();
7482
+ }
7483
+ return RESOLVED_TRANSITION_HANDLE;
7484
+ }
7877
7485
  const handles = [];
7878
7486
  const meshNames = this.getMeshNamesForAU(numId);
7879
7487
  if (leftKeys.length || rightKeys.length) {
@@ -7936,6 +7544,17 @@ var _Embody = class _Embody {
7936
7544
  */
7937
7545
  setContinuum(negAU, posAU, continuumValue, balance) {
7938
7546
  const value = Math.max(-1, Math.min(1, continuumValue));
7547
+ this.auValues[negAU] = value < 0 ? Math.abs(value) : 0;
7548
+ this.auValues[posAU] = value > 0 ? value : 0;
7549
+ if (balance !== void 0) {
7550
+ this.auBalances[negAU] = balance;
7551
+ this.auBalances[posAU] = balance;
7552
+ }
7553
+ if (this.wasmRuntimeCore) {
7554
+ this.wasmRuntimeCore.setContinuum(negAU, posAU, value, balance ?? 0);
7555
+ this.applyLiveFrameDelta();
7556
+ return;
7557
+ }
7939
7558
  if (value < 0) {
7940
7559
  this.setAU(posAU, 0, balance);
7941
7560
  this.setAU(negAU, Math.abs(value), balance);
@@ -7961,10 +7580,32 @@ var _Embody = class _Embody {
7961
7580
  */
7962
7581
  transitionContinuum(negAU, posAU, continuumValue, durationMs = 200, balance) {
7963
7582
  const target = Math.max(-1, Math.min(1, continuumValue));
7964
- const driverKey = `continuum_${negAU}_${posAU}`;
7965
7583
  const currentNeg = this.auValues[negAU] ?? 0;
7966
7584
  const currentPos = this.auValues[posAU] ?? 0;
7967
7585
  const currentContinuum = currentPos - currentNeg;
7586
+ this.auValues[negAU] = target < 0 ? Math.abs(target) : 0;
7587
+ this.auValues[posAU] = target > 0 ? target : 0;
7588
+ if (balance !== void 0) {
7589
+ this.auBalances[negAU] = balance;
7590
+ this.auBalances[posAU] = balance;
7591
+ }
7592
+ if (this.wasmRuntimeCore) {
7593
+ if (target < 0) {
7594
+ this.wasmRuntimeCore.transitionAU(posAU, 0, durationMs, balance ?? this.auBalances[posAU] ?? Number.NaN);
7595
+ this.wasmRuntimeCore.transitionAU(negAU, Math.abs(target), durationMs, balance ?? this.auBalances[negAU] ?? Number.NaN);
7596
+ } else if (target > 0) {
7597
+ this.wasmRuntimeCore.transitionAU(negAU, 0, durationMs, balance ?? this.auBalances[negAU] ?? Number.NaN);
7598
+ this.wasmRuntimeCore.transitionAU(posAU, target, durationMs, balance ?? this.auBalances[posAU] ?? Number.NaN);
7599
+ } else {
7600
+ this.wasmRuntimeCore.transitionAU(negAU, 0, durationMs, balance ?? this.auBalances[negAU] ?? Number.NaN);
7601
+ this.wasmRuntimeCore.transitionAU(posAU, 0, durationMs, balance ?? this.auBalances[posAU] ?? Number.NaN);
7602
+ }
7603
+ if (durationMs <= 0) {
7604
+ this.applyLiveFrameDelta();
7605
+ }
7606
+ return RESOLVED_TRANSITION_HANDLE;
7607
+ }
7608
+ const driverKey = `continuum_${negAU}_${posAU}`;
7968
7609
  return this.animation.addTransition(driverKey, currentContinuum, target, durationMs, (value) => this.setContinuum(negAU, posAU, value, balance));
7969
7610
  }
7970
7611
  // ============================================================================
@@ -8046,7 +7687,7 @@ var _Embody = class _Embody {
8046
7687
  this.hairPhysics.refreshMeshSelection();
8047
7688
  }
8048
7689
  setMorph(key, v, meshNamesOrTargets) {
8049
- const val = clamp013(v);
7690
+ const val = clamp012(v);
8050
7691
  if (Array.isArray(meshNamesOrTargets) && meshNamesOrTargets.length > 0 && typeof meshNamesOrTargets[0] === "object" && "infl" in meshNamesOrTargets[0]) {
8051
7692
  const targets2 = meshNamesOrTargets;
8052
7693
  this.applyMorphTargets(targets2, val);
@@ -8058,7 +7699,7 @@ var _Embody = class _Embody {
8058
7699
  this.applyMorphTargets(targets, val);
8059
7700
  }
8060
7701
  setMorphInfluence(index, v, meshNames) {
8061
- const val = clamp013(v);
7702
+ const val = clamp012(v);
8062
7703
  const targets = this.resolveMorphTargetsByIndex(index, meshNames);
8063
7704
  if (targets.length === 0) return;
8064
7705
  this.applyMorphTargets(targets, val);
@@ -8133,21 +7774,21 @@ var _Embody = class _Embody {
8133
7774
  }
8134
7775
  transitionMorph(key, to, durationMs = 120, meshNames) {
8135
7776
  const transitionKey = meshNames?.length ? `morph_${this.getMorphKeyCacheKey(key, meshNames)}` : `morph_${this.getMorphKeyCacheKey(key)}`;
8136
- const target = clamp013(to);
7777
+ const target = clamp012(to);
8137
7778
  const targets = this.resolveMorphTargets(key, meshNames);
8138
7779
  const from = targets.length > 0 ? targets[0].infl[targets[0].idx] ?? 0 : this.getMorphValue(key);
8139
7780
  return this.animation.addTransition(transitionKey, from, target, durationMs, (value) => {
8140
- const val = clamp013(value);
7781
+ const val = clamp012(value);
8141
7782
  this.applyMorphTargets(targets, val);
8142
7783
  });
8143
7784
  }
8144
7785
  transitionMorphInfluence(index, to, durationMs = 120, meshNames) {
8145
7786
  const transitionKey = meshNames?.length ? `morph_${this.getMorphIndexCacheKey(index, meshNames)}` : `morph_${this.getMorphIndexCacheKey(index)}`;
8146
- const target = clamp013(to);
7787
+ const target = clamp012(to);
8147
7788
  const targets = this.resolveMorphTargetsByIndex(index, meshNames);
8148
7789
  const from = targets.length > 0 ? targets[0].infl[targets[0].idx] ?? 0 : this.getMorphValueByIndex(index);
8149
7790
  return this.animation.addTransition(transitionKey, from, target, durationMs, (value) => {
8150
- const val = clamp013(value);
7791
+ const val = clamp012(value);
8151
7792
  this.applyMorphTargets(targets, val);
8152
7793
  });
8153
7794
  }
@@ -8156,7 +7797,7 @@ var _Embody = class _Embody {
8156
7797
  // ============================================================================
8157
7798
  setViseme(visemeIndex, value, jawScale = 1) {
8158
7799
  if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) return;
8159
- const val = clamp013(value);
7800
+ const val = clamp012(value);
8160
7801
  this.visemeValues[visemeIndex] = val;
8161
7802
  this.visemeJawScales[visemeIndex] = jawScale;
8162
7803
  this.applyVisemeRuntimeState();
@@ -8168,16 +7809,24 @@ var _Embody = class _Embody {
8168
7809
  }, cancel: () => {
8169
7810
  } };
8170
7811
  }
8171
- const target = clamp013(to);
7812
+ const target = clamp012(to);
8172
7813
  const from = this.visemeValues[visemeIndex] ?? 0;
8173
7814
  this.visemeJawScales[visemeIndex] = jawScale;
7815
+ if (this.wasmRuntimeCore) {
7816
+ this.visemeValues[visemeIndex] = target;
7817
+ this.wasmRuntimeCore.transitionViseme(visemeIndex, target, durationMs, jawScale);
7818
+ if (durationMs <= 0) {
7819
+ this.applyLiveFrameDelta();
7820
+ }
7821
+ return RESOLVED_TRANSITION_HANDLE;
7822
+ }
8174
7823
  return this.animation.addTransition(
8175
7824
  `viseme_value_${visemeIndex}`,
8176
7825
  from,
8177
7826
  target,
8178
7827
  durationMs,
8179
7828
  (value) => {
8180
- this.visemeValues[visemeIndex] = clamp013(value);
7829
+ this.visemeValues[visemeIndex] = clamp012(value);
8181
7830
  this.visemeJawScales[visemeIndex] = jawScale;
8182
7831
  this.applyVisemeRuntimeState();
8183
7832
  }
@@ -8202,8 +7851,7 @@ var _Embody = class _Embody {
8202
7851
  // MIX WEIGHT CONTROL
8203
7852
  // ============================================================================
8204
7853
  setAUMixWeight(id, weight) {
8205
- this.mixWeights[id] = clamp013(weight);
8206
- this.tsRuntimeCore?.setAUMixWeight(id, weight);
7854
+ this.mixWeights[id] = clamp012(weight);
8207
7855
  this.wasmRuntimeCore?.setAUMixWeight(id, weight);
8208
7856
  const v = this.auValues[id] ?? 0;
8209
7857
  if (v > 0) this.setAU(id, v);
@@ -8241,9 +7889,10 @@ var _Embody = class _Embody {
8241
7889
  }
8242
7890
  clearTransitions() {
8243
7891
  this.animation.clearTransitions();
7892
+ this.wasmRuntimeCore?.clearTransitions();
8244
7893
  }
8245
7894
  getActiveTransitionCount() {
8246
- return this.animation.getActiveTransitionCount();
7895
+ return this.animation.getActiveTransitionCount() + (this.wasmRuntimeCore?.activeTransitionCount() ?? 0);
8247
7896
  }
8248
7897
  resetToNeutral() {
8249
7898
  this.auValues = {};
@@ -8253,6 +7902,7 @@ var _Embody = class _Embody {
8253
7902
  this.translations = {};
8254
7903
  this.initBoneRotations();
8255
7904
  this.clearTransitions();
7905
+ this.wasmRuntimeCore?.clear();
8256
7906
  const morphTargetsToReset = [];
8257
7907
  for (const m of this.meshes) {
8258
7908
  const infl = m.morphTargetInfluences;
@@ -8431,7 +8081,18 @@ var _Embody = class _Embody {
8431
8081
  this.bones = this.resolveBones(this.model);
8432
8082
  this.missingBoneWarnings.clear();
8433
8083
  this.rebuildMorphTargetsCache();
8434
- this.tsRuntimeCore?.setProfile(this.config);
8084
+ Object.values(this.bones).forEach((entry) => {
8085
+ if (!entry) return;
8086
+ this.frameApplier.applyObjectTransform(entry.obj, {
8087
+ position: { x: entry.basePos.x, y: entry.basePos.y, z: entry.basePos.z },
8088
+ rotation: {
8089
+ x: entry.baseQuat.x,
8090
+ y: entry.baseQuat.y,
8091
+ z: entry.baseQuat.z,
8092
+ w: entry.baseQuat.w
8093
+ }
8094
+ });
8095
+ });
8435
8096
  this.wasmRuntimeCore?.setProfile(this.config);
8436
8097
  this.refreshHostNeutralModelDescriptor();
8437
8098
  }
@@ -8548,20 +8209,20 @@ var _Embody = class _Embody {
8548
8209
  }
8549
8210
  applyVisemeRuntimeState() {
8550
8211
  for (let index = 0; index < this.visemeValues.length; index += 1) {
8551
- this.tsRuntimeCore?.setViseme(index, this.visemeValues[index] ?? 0, this.visemeJawScales[index] ?? 1);
8552
- this.wasmRuntimeCore?.setViseme(index, this.visemeValues[index] ?? 0);
8212
+ this.wasmRuntimeCore?.setViseme(index, this.visemeValues[index] ?? 0, this.visemeJawScales[index] ?? 1);
8553
8213
  }
8554
- if (!this.applyLiveMorphFrameDelta()) {
8214
+ const appliedViaFrame = this.applyLiveFrameDelta();
8215
+ if (!appliedViaFrame) {
8555
8216
  for (const targets of this.resolvedVisemeTargets) {
8556
8217
  this.frameApplier.resetMorphTargets(targets || []);
8557
8218
  }
8558
8219
  for (let index = 0; index < this.visemeValues.length; index += 1) {
8559
- const value = clamp013(this.visemeValues[index] ?? 0);
8220
+ const value = clamp012(this.visemeValues[index] ?? 0);
8560
8221
  if (value <= 1e-6) continue;
8561
8222
  const targets = this.resolvedVisemeTargets[index] || [];
8562
8223
  for (const target of targets) {
8563
8224
  if (target.idx >= target.infl.length) continue;
8564
- const weighted = clamp013(value * target.weight);
8225
+ const weighted = clamp012(value * target.weight);
8565
8226
  this.frameApplier.applyMorphTargets(
8566
8227
  [target],
8567
8228
  Math.max(target.infl[target.idx] ?? 0, weighted)
@@ -8569,7 +8230,9 @@ var _Embody = class _Embody {
8569
8230
  }
8570
8231
  }
8571
8232
  }
8572
- this.updateBoneRotation(this.getJawBoneNodeKey(), "pitch", this.getActiveVisemeJawAmount());
8233
+ if (!appliedViaFrame) {
8234
+ this.updateBoneRotation(this.getJawBoneNodeKey(), "pitch", this.getActiveVisemeJawAmount());
8235
+ }
8573
8236
  }
8574
8237
  getJawBoneNodeKey() {
8575
8238
  const candidates = [
@@ -8591,7 +8254,7 @@ var _Embody = class _Embody {
8591
8254
  getActiveVisemeJawAmount() {
8592
8255
  let jawAmount = 0;
8593
8256
  for (let index = 0; index < this.visemeValues.length; index += 1) {
8594
- const value = clamp013(this.visemeValues[index] ?? 0);
8257
+ const value = clamp012(this.visemeValues[index] ?? 0);
8595
8258
  if (value <= 1e-6) continue;
8596
8259
  const jawScale = this.visemeJawScales[index] ?? 1;
8597
8260
  if (Math.abs(jawScale) <= 1e-6) continue;
@@ -8874,7 +8537,7 @@ var _Embody = class _Embody {
8874
8537
  return !!(hasMorphs && this.config.auToBones[id]?.length);
8875
8538
  }
8876
8539
  getEffectiveBoneAUValue(auId, nodeKey) {
8877
- const rawValue = clamp013(this.auValues[auId] ?? 0);
8540
+ const rawValue = clamp012(this.auValues[auId] ?? 0);
8878
8541
  if (rawValue <= 1e-6) return 0;
8879
8542
  const binding = this.config.auToBones[auId]?.find((candidate) => candidate.node === nodeKey) ?? null;
8880
8543
  if (!binding?.side) return rawValue;
@@ -8974,7 +8637,7 @@ var _Embody = class _Embody {
8974
8637
  if (config.yaw && rotState.yaw !== 0) {
8975
8638
  const binding = this.getCompositeAxisBindingForNode(nodeKey, config.yaw, rotState.yaw);
8976
8639
  if (binding?.maxDegrees && binding.channel) {
8977
- const radians = deg2rad2(binding.maxDegrees) * Math.abs(rotState.yaw) * binding.scale;
8640
+ const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.yaw) * binding.scale;
8978
8641
  const axis = getAxis(binding.channel);
8979
8642
  const deltaQ = new Quaternion().setFromAxisAngle(axis, radians);
8980
8643
  compositeQ.multiply(deltaQ);
@@ -8983,7 +8646,7 @@ var _Embody = class _Embody {
8983
8646
  if (config.pitch && rotState.pitch !== 0) {
8984
8647
  const binding = this.getCompositeAxisBindingForNode(nodeKey, config.pitch, rotState.pitch);
8985
8648
  if (binding?.maxDegrees && binding.channel) {
8986
- const radians = deg2rad2(binding.maxDegrees) * Math.abs(rotState.pitch) * binding.scale;
8649
+ const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.pitch) * binding.scale;
8987
8650
  const axis = getAxis(binding.channel);
8988
8651
  const deltaQ = new Quaternion().setFromAxisAngle(axis, radians);
8989
8652
  compositeQ.multiply(deltaQ);
@@ -8992,7 +8655,7 @@ var _Embody = class _Embody {
8992
8655
  if (config.roll && rotState.roll !== 0) {
8993
8656
  const binding = this.getCompositeAxisBindingForNode(nodeKey, config.roll, rotState.roll);
8994
8657
  if (binding?.maxDegrees && binding.channel) {
8995
- const radians = deg2rad2(binding.maxDegrees) * Math.abs(rotState.roll) * binding.scale;
8658
+ const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.roll) * binding.scale;
8996
8659
  const axis = getAxis(binding.channel);
8997
8660
  const deltaQ = new Quaternion().setFromAxisAngle(axis, radians);
8998
8661
  compositeQ.multiply(deltaQ);
@@ -9831,11 +9494,11 @@ function applyCharacterModelTransform(model, transform = {}) {
9831
9494
  model.position.set(modelOffset.x ?? 0, modelOffset.y ?? 0, modelOffset.z ?? 0);
9832
9495
  }
9833
9496
  if (modelRotation) {
9834
- const deg2rad3 = Math.PI / 180;
9497
+ const deg2rad2 = Math.PI / 180;
9835
9498
  model.rotation.set(
9836
- (modelRotation.x ?? 0) * deg2rad3,
9837
- (modelRotation.y ?? 0) * deg2rad3,
9838
- (modelRotation.z ?? 0) * deg2rad3
9499
+ (modelRotation.x ?? 0) * deg2rad2,
9500
+ (modelRotation.y ?? 0) * deg2rad2,
9501
+ (modelRotation.z ?? 0) * deg2rad2
9839
9502
  );
9840
9503
  }
9841
9504
  if (modelScale !== void 0 && Number.isFinite(modelScale) && modelScale > 0) {
@@ -14851,7 +14514,7 @@ function computeHumanoidSkeletonTemplateRestBounds(template) {
14851
14514
  }
14852
14515
  const parentPosition = worldPositions.get(bone.parent);
14853
14516
  if (!parentPosition) continue;
14854
- worldPositions.set(bone.name, addVec32(parentPosition, bone.translation));
14517
+ worldPositions.set(bone.name, addVec3(parentPosition, bone.translation));
14855
14518
  unresolved.delete(bone.name);
14856
14519
  resolvedThisPass += 1;
14857
14520
  }
@@ -14912,10 +14575,10 @@ function assertNonEmptyString(value, fieldName) {
14912
14575
  throw new Error(`${fieldName} must be a non-empty string`);
14913
14576
  }
14914
14577
  }
14915
- function addVec32(a, b) {
14578
+ function addVec3(a, b) {
14916
14579
  return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
14917
14580
  }
14918
14581
 
14919
- export { AU_INFO, AU_MAPPING_CONFIG, AU_MIX_DEFAULTS, AU_TO_MORPHS, ThreeAnimationSystem as AnimationController, BETTA_FISH_PRESET, BLENDING_MODES, BONE_AU_TO_BINDINGS, ThreeAnimationSystem as BakedAnimationController, CC4_BONES, CC4_BONE_NODES, CC4_BONE_PREFIX, CC4_EYE_MESH_NODES, CC4_MAPPING_SECTIONS, CC4_MESHES, CC4_PRESET, CC4_PROFILE_BONE_BINDINGS, CC4_SUFFIX_PATTERN, CC4_VISEME_SLOTS, CC4_VISEME_SYSTEM_ID, CHARACTER_SCENE_TYPES, CHARACTER_SCENE_TYPE_IDS, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, DEFAULT_AXIS_TO_CHANNEL, DEFAULT_BONE_MAX_DEGREES, DEFAULT_CHARACTER_LIGHTING_PRESETS, DEFAULT_CHARACTER_LIGHTING_PRESET_ID, DEFAULT_CHARACTER_LIGHTING_PRESET_IDS, DEFAULT_CHARACTER_LIGHTING_SETTINGS, DEFAULT_CHARACTER_SCENE_TYPE_ID, DEFAULT_HAIR_COLOR_APPEARANCE, DEFAULT_HAIR_PHYSICS_CONFIG, Embody, FISH_AU_MAPPING_CONFIG, HAIR_COLOR_PRESETS, HUMANOID_SKELETON_TEMPLATES, HairPhysics, HairPhysicsController, JOINT_CONTROL_SECTION, JONATHAN_HUMANOID_SKELETON_TEMPLATE, LIP_SYNC_CONTROL_TO_BINDINGS, MORPH_TO_MESH, RustAnnotationCameraCore, RustCameraFlight, RustCameraOrbit, RustHairPhysics, TEMPLATE_SKELETON_FIT_METADATA_KIND, TEMPLATE_SKELETON_FIT_METADATA_VERSION, TEMPLATE_SKELETON_FIT_STATUSES, TEMPLATE_SKELETON_FIT_VERTICAL_ANCHORS, TEMPLATE_SKELETON_FIT_VERTICAL_AXES, THREE_BLENDING_MODES, ThreeAnimationRuntime, ThreeAnimationSystem, ThreeClipAdapter, ThreeFrameApplier, ThreeModelInspector, TsClipCompiler, TsRuntimeCore, VISEME_JAW_AMOUNTS, VISEME_KEYS, WasmRuntimeCore, analyzeModel, applyAUBoneBindingUpdate, applyBilateralAxisBindingUpdate, applyBoneAxisBindingUpdate, applyCharacterModelTransform, applyCharacterProfileToPreset, buildBoneAuOptions, buildFrameApplierBindings, buildMappingEditorModel, classifyAuAsJointControl, collectMorphMeshes2 as collectMorphMeshes, compileVisemeKeys, composeTemplateSkeletonFitTransform, computeCameraRelativeGazeOffset, computeHumanoidSkeletonTemplateRestBounds, createBilateralBoneAxisAu, createBoneAxisAu, createCharacterHost, createDefaultCharacterLighting, createDefaultCharacterScene, createRustAnnotationCameraCore, createRustHairPhysics, createShadowPlane, createValidationSkeletonFromHumanoidTemplate, detectAnnotationLaterality, detectFacingDirection, disposeCharacterModel, ensureBilateralBoneNodeKeys, ensureBoneNodeKey, extendCharacterConfigWithPreset, extendPresetWithProfile, extendProfileConfigWithPreset, extractFromGLTF, extractHumanoidSkeletonTemplateFromModel, extractLegacyCharacterProfileOverrides, extractModelData, extractProfileOverrides, findFaceCenter, findNodeKeyForBone, formatAxisDirectionLabel, formatAxisLabel, fuzzyNameMatch, generateMappingCorrections, getAUBoneBindingState, getAxisFromChannel, getBilateralAxisBindingState, getBoneAxisBindingState, getDefaultAnnotationLaterality, getHumanoidSkeletonTemplate, getHumanoidSkeletonTemplateBoneNames, getMeshNamesForAUProfile, getMeshNamesForVisemeProfile, getModelForwardDirection, getModelLocalOrbitAngle, getPreset, getPresetWithProfile, getProfilePresetId, getProfileVisemeSlots, getRegionSemanticSide, getSemanticHorizontalSign, getSemanticHorizontalSignForSide, getVisemeBindingTargets, getVisemeJawAmounts, getVisemeSlotIndex, getWorldDirectionForCameraAngle, hasLeftRightMorphs, inferChiralBoneNamePair, inferEyeControlFamily, inferEyeControlScope, isJointControlAuInfo, isMaxDegreesOnlyAxisBindingUpdate, isMixedAU, isPresetCompatible, isTemplateSkeletonFitStatus, loadCharacterModel, mapProviderVisemeToSlot, mergeRegionsByName as mergeCharacterRegionsByName, mergePresetWithProfile, mergeProfileRegionsByName, normalizeCameraAngle, normalizeDefaultCharacterLightingSettings, normalizeHairColorAppearance, numericArrayToNumbers, packHairConfig, packHeadState, parseCharacterModel, passesMarkerCameraAngleGate, resolveBoneAxisChannel, resolveBoneName, resolveBoneNameForNodeKey, resolveBoneNames, resolveContinuumDisplayLabel, resolveFaceCenter, resolvePreset, resolvePresetWithOverrides, resolveProfileFromPreset, resolveRegionCameraAngle, resolveRegionVisibilityCameraAngle, resolveRustEmbeddedProfileView, resolveRustProfileView, resolveVisemeMeshCategory, stripConfiguredBoneAffixes, suggestBestPreset, toModelLocalDirection2 as toModelLocalDirection, toWorldDirection, unpackBoneFrameDelta, unpackHairConfig, unpackHairMorphOutput, unpackHairState, unpackMorphFrameDelta, validateMappingConfig, validateMappings, validateTemplateSkeletonFitMetadata };
14582
+ export { AU_INFO, AU_MAPPING_CONFIG, AU_MIX_DEFAULTS, AU_TO_MORPHS, ThreeAnimationSystem as AnimationController, BETTA_FISH_PRESET, BLENDING_MODES, BONE_AU_TO_BINDINGS, ThreeAnimationSystem as BakedAnimationController, CC4_BONES, CC4_BONE_NODES, CC4_BONE_PREFIX, CC4_EYE_MESH_NODES, CC4_MAPPING_SECTIONS, CC4_MESHES, CC4_PRESET, CC4_PROFILE_BONE_BINDINGS, CC4_SUFFIX_PATTERN, CC4_VISEME_SLOTS, CC4_VISEME_SYSTEM_ID, CHARACTER_SCENE_TYPES, CHARACTER_SCENE_TYPE_IDS, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, DEFAULT_AXIS_TO_CHANNEL, DEFAULT_BONE_MAX_DEGREES, DEFAULT_CHARACTER_LIGHTING_PRESETS, DEFAULT_CHARACTER_LIGHTING_PRESET_ID, DEFAULT_CHARACTER_LIGHTING_PRESET_IDS, DEFAULT_CHARACTER_LIGHTING_SETTINGS, DEFAULT_CHARACTER_SCENE_TYPE_ID, DEFAULT_HAIR_COLOR_APPEARANCE, DEFAULT_HAIR_PHYSICS_CONFIG, Embody, FISH_AU_MAPPING_CONFIG, HAIR_COLOR_PRESETS, HUMANOID_SKELETON_TEMPLATES, HairPhysics, HairPhysicsController, JOINT_CONTROL_SECTION, JONATHAN_HUMANOID_SKELETON_TEMPLATE, LIP_SYNC_CONTROL_TO_BINDINGS, MORPH_TO_MESH, RustAnnotationCameraCore, RustCameraFlight, RustCameraOrbit, RustHairPhysics, TEMPLATE_SKELETON_FIT_METADATA_KIND, TEMPLATE_SKELETON_FIT_METADATA_VERSION, TEMPLATE_SKELETON_FIT_STATUSES, TEMPLATE_SKELETON_FIT_VERTICAL_ANCHORS, TEMPLATE_SKELETON_FIT_VERTICAL_AXES, THREE_BLENDING_MODES, ThreeAnimationRuntime, ThreeAnimationSystem, ThreeClipAdapter, ThreeFrameApplier, ThreeModelInspector, TsClipCompiler, VISEME_JAW_AMOUNTS, VISEME_KEYS, WasmRuntimeCore, analyzeModel, applyAUBoneBindingUpdate, applyBilateralAxisBindingUpdate, applyBoneAxisBindingUpdate, applyCharacterModelTransform, applyCharacterProfileToPreset, buildBoneAuOptions, buildFrameApplierBindings, buildMappingEditorModel, classifyAuAsJointControl, collectMorphMeshes2 as collectMorphMeshes, compileVisemeKeys, composeTemplateSkeletonFitTransform, computeCameraRelativeGazeOffset, computeHumanoidSkeletonTemplateRestBounds, createBilateralBoneAxisAu, createBoneAxisAu, createCharacterHost, createDefaultCharacterLighting, createDefaultCharacterScene, createRustAnnotationCameraCore, createRustHairPhysics, createShadowPlane, createValidationSkeletonFromHumanoidTemplate, detectAnnotationLaterality, detectFacingDirection, disposeCharacterModel, ensureBilateralBoneNodeKeys, ensureBoneNodeKey, extendCharacterConfigWithPreset, extendPresetWithProfile, extendProfileConfigWithPreset, extractFromGLTF, extractHumanoidSkeletonTemplateFromModel, extractLegacyCharacterProfileOverrides, extractModelData, extractProfileOverrides, findFaceCenter, findNodeKeyForBone, formatAxisDirectionLabel, formatAxisLabel, fuzzyNameMatch, generateMappingCorrections, getAUBoneBindingState, getAxisFromChannel, getBilateralAxisBindingState, getBoneAxisBindingState, getDefaultAnnotationLaterality, getHumanoidSkeletonTemplate, getHumanoidSkeletonTemplateBoneNames, getMeshNamesForAUProfile, getMeshNamesForVisemeProfile, getModelForwardDirection, getModelLocalOrbitAngle, getPreset, getPresetWithProfile, getProfilePresetId, getProfileVisemeSlots, getRegionSemanticSide, getSemanticHorizontalSign, getSemanticHorizontalSignForSide, getVisemeBindingTargets, getVisemeJawAmounts, getVisemeSlotIndex, getWorldDirectionForCameraAngle, hasLeftRightMorphs, inferChiralBoneNamePair, inferEyeControlFamily, inferEyeControlScope, isJointControlAuInfo, isMaxDegreesOnlyAxisBindingUpdate, isMixedAU, isPresetCompatible, isTemplateSkeletonFitStatus, loadCharacterModel, mapProviderVisemeToSlot, mergeRegionsByName as mergeCharacterRegionsByName, mergePresetWithProfile, mergeProfileRegionsByName, normalizeCameraAngle, normalizeDefaultCharacterLightingSettings, normalizeHairColorAppearance, numericArrayToNumbers, packHairConfig, packHeadState, parseCharacterModel, passesMarkerCameraAngleGate, resolveBoneAxisChannel, resolveBoneName, resolveBoneNameForNodeKey, resolveBoneNames, resolveContinuumDisplayLabel, resolveFaceCenter, resolvePreset, resolvePresetWithOverrides, resolveProfileFromPreset, resolveRegionCameraAngle, resolveRegionVisibilityCameraAngle, resolveRustEmbeddedProfileView, resolveRustProfileView, resolveVisemeMeshCategory, stripConfiguredBoneAffixes, suggestBestPreset, toModelLocalDirection2 as toModelLocalDirection, toWorldDirection, unpackBoneFrameDelta, unpackHairConfig, unpackHairMorphOutput, unpackHairState, unpackMorphFrameDelta, validateMappingConfig, validateMappings, validateTemplateSkeletonFitMetadata };
14920
14583
  //# sourceMappingURL=index.js.map
14921
14584
  //# sourceMappingURL=index.js.map