@lovelace_lol/embody 0.0.4 → 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/core/WasmRuntimeCore.d.ts +10 -24
- package/dist/core/index.d.ts +2 -4
- package/dist/core.cjs +27 -739
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +28 -735
- package/dist/core.js.map +1 -1
- package/dist/engines/three/Embody.d.ts +1 -2
- package/dist/index.cjs +164 -729
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.js +166 -726
- package/dist/index.js.map +1 -1
- package/dist/three.cjs +162 -499
- package/dist/three.cjs.map +1 -1
- package/dist/three.js +163 -500
- package/dist/three.js.map +1 -1
- package/dist/wasm/embody_wasm.d.ts +3 -3
- package/dist/wasm/embody_wasm.js +3 -3
- package/dist/wasm/embody_wasm_bg.wasm +0 -0
- package/package.json +1 -1
- package/dist/core/TsRuntimeCore.d.ts +0 -40
package/dist/three.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
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';
|
|
2
|
-
import { initEmbodyCore } from '@lovelace_lol/embody/wasm';
|
|
2
|
+
import { initEmbodyCore, requireInitializedEmbodyCore } from '@lovelace_lol/embody/wasm';
|
|
3
3
|
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
|
|
4
4
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
5
5
|
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
|
|
@@ -3509,410 +3509,6 @@ function getBlendingName(value) {
|
|
|
3509
3509
|
return "Normal";
|
|
3510
3510
|
}
|
|
3511
3511
|
|
|
3512
|
-
// src/core/TsRuntimeCore.ts
|
|
3513
|
-
var identityQuat = { x: 0, y: 0, z: 0, w: 1 };
|
|
3514
|
-
function isRotationBoneBinding2(binding) {
|
|
3515
|
-
return binding?.channel === "rx" || binding?.channel === "ry" || binding?.channel === "rz";
|
|
3516
|
-
}
|
|
3517
|
-
var TsRuntimeCore = class {
|
|
3518
|
-
constructor(options) {
|
|
3519
|
-
__publicField(this, "profile");
|
|
3520
|
-
__publicField(this, "model");
|
|
3521
|
-
__publicField(this, "auValues", {});
|
|
3522
|
-
__publicField(this, "auBalances", {});
|
|
3523
|
-
__publicField(this, "visemeValues");
|
|
3524
|
-
__publicField(this, "visemeJawScales");
|
|
3525
|
-
__publicField(this, "mixWeights");
|
|
3526
|
-
this.profile = options.profile;
|
|
3527
|
-
this.model = options.model;
|
|
3528
|
-
this.visemeValues = new Array(getProfileVisemeSlots(this.profile).length).fill(0);
|
|
3529
|
-
this.visemeJawScales = new Array(this.visemeValues.length).fill(1);
|
|
3530
|
-
this.mixWeights = { ...this.profile.auMixDefaults };
|
|
3531
|
-
}
|
|
3532
|
-
setProfile(profile) {
|
|
3533
|
-
this.profile = profile;
|
|
3534
|
-
this.visemeValues = new Array(getProfileVisemeSlots(this.profile).length).fill(0);
|
|
3535
|
-
this.visemeJawScales = new Array(this.visemeValues.length).fill(1);
|
|
3536
|
-
this.mixWeights = { ...this.profile.auMixDefaults };
|
|
3537
|
-
}
|
|
3538
|
-
setModelDescriptor(model) {
|
|
3539
|
-
this.model = model;
|
|
3540
|
-
}
|
|
3541
|
-
setAU(id, value, balance) {
|
|
3542
|
-
if (value < 0 && this.profile.continuumPairs) {
|
|
3543
|
-
const pairInfo = this.profile.continuumPairs[id];
|
|
3544
|
-
if (pairInfo) {
|
|
3545
|
-
const negAU = pairInfo.isNegative ? id : pairInfo.pairId;
|
|
3546
|
-
const posAU = pairInfo.isNegative ? pairInfo.pairId : id;
|
|
3547
|
-
this.setContinuum(negAU, posAU, pairInfo.isNegative ? -value : value, balance);
|
|
3548
|
-
return;
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
this.auValues[id] = clamp01(value);
|
|
3552
|
-
if (balance !== void 0) {
|
|
3553
|
-
this.auBalances[id] = clampBalance2(balance);
|
|
3554
|
-
}
|
|
3555
|
-
}
|
|
3556
|
-
getAU(id) {
|
|
3557
|
-
return this.auValues[id] ?? 0;
|
|
3558
|
-
}
|
|
3559
|
-
setContinuum(negAU, posAU, continuumValue, balance) {
|
|
3560
|
-
const value = Math.max(-1, Math.min(1, continuumValue));
|
|
3561
|
-
if (value < 0) {
|
|
3562
|
-
this.setAU(posAU, 0, balance);
|
|
3563
|
-
this.setAU(negAU, Math.abs(value), balance);
|
|
3564
|
-
return;
|
|
3565
|
-
}
|
|
3566
|
-
if (value > 0) {
|
|
3567
|
-
this.setAU(negAU, 0, balance);
|
|
3568
|
-
this.setAU(posAU, value, balance);
|
|
3569
|
-
return;
|
|
3570
|
-
}
|
|
3571
|
-
this.setAU(negAU, 0, balance);
|
|
3572
|
-
this.setAU(posAU, 0, balance);
|
|
3573
|
-
}
|
|
3574
|
-
setViseme(visemeIndex, value, jawScale = 1) {
|
|
3575
|
-
if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) return;
|
|
3576
|
-
this.visemeValues[visemeIndex] = clamp01(value);
|
|
3577
|
-
this.visemeJawScales[visemeIndex] = jawScale;
|
|
3578
|
-
}
|
|
3579
|
-
setVisemeById(slotId, value, jawScale = 1) {
|
|
3580
|
-
const index = getVisemeSlotIndex(this.profile, slotId);
|
|
3581
|
-
if (index < 0) return;
|
|
3582
|
-
this.setViseme(index, value, jawScale);
|
|
3583
|
-
}
|
|
3584
|
-
setAUMixWeight(id, weight) {
|
|
3585
|
-
this.mixWeights[id] = clamp01(weight);
|
|
3586
|
-
}
|
|
3587
|
-
evaluateFrameDelta(deltaSeconds) {
|
|
3588
|
-
const morphWrites = /* @__PURE__ */ new Map();
|
|
3589
|
-
this.collectAUWrites(morphWrites);
|
|
3590
|
-
this.collectVisemeWrites(morphWrites);
|
|
3591
|
-
return {
|
|
3592
|
-
deltaSeconds,
|
|
3593
|
-
morphTargets: Array.from(morphWrites.values()),
|
|
3594
|
-
bones: this.collectBoneWrites()
|
|
3595
|
-
};
|
|
3596
|
-
}
|
|
3597
|
-
collectAUWrites(morphWrites) {
|
|
3598
|
-
for (const [auIdText, entry] of Object.entries(this.profile.auToMorphs || {})) {
|
|
3599
|
-
const auId = Number(auIdText);
|
|
3600
|
-
if (Number.isNaN(auId) || !entry) continue;
|
|
3601
|
-
const mixWeight = this.isMixedAU(auId) ? this.getAUMixWeight(auId) : 1;
|
|
3602
|
-
const base = clamp01(this.auValues[auId] ?? 0) * mixWeight;
|
|
3603
|
-
const { left, right } = computeSideValues(base, this.auBalances[auId]);
|
|
3604
|
-
const meshNames = getMeshNamesForAUProfile(this.profile, auId);
|
|
3605
|
-
for (const morph of entry.left || []) {
|
|
3606
|
-
this.addMorphWrites(morphWrites, morph, meshNames, left);
|
|
3607
|
-
}
|
|
3608
|
-
for (const morph of entry.right || []) {
|
|
3609
|
-
this.addMorphWrites(morphWrites, morph, meshNames, right);
|
|
3610
|
-
}
|
|
3611
|
-
for (const morph of entry.center || []) {
|
|
3612
|
-
this.addMorphWrites(morphWrites, morph, meshNames, base);
|
|
3613
|
-
}
|
|
3614
|
-
}
|
|
3615
|
-
}
|
|
3616
|
-
collectVisemeWrites(morphWrites) {
|
|
3617
|
-
const meshNames = getMeshNamesForVisemeProfile(this.profile);
|
|
3618
|
-
const localMax = /* @__PURE__ */ new Map();
|
|
3619
|
-
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
3620
|
-
const value = clamp01(this.visemeValues[index] ?? 0);
|
|
3621
|
-
for (const bindingTarget of getVisemeBindingTargets(this.profile, index)) {
|
|
3622
|
-
const weighted = clamp01(value * bindingTarget.weight);
|
|
3623
|
-
for (const resolved of this.resolveMorphTargets(bindingTarget.morph, meshNames)) {
|
|
3624
|
-
const key = morphWriteKey(resolved.mesh.id, resolved.morphTarget.id);
|
|
3625
|
-
const existing = localMax.get(key);
|
|
3626
|
-
if (!existing || weighted > existing.value) {
|
|
3627
|
-
localMax.set(key, {
|
|
3628
|
-
meshId: resolved.mesh.id,
|
|
3629
|
-
morphTargetId: resolved.morphTarget.id,
|
|
3630
|
-
value: weighted,
|
|
3631
|
-
mode: "absolute"
|
|
3632
|
-
});
|
|
3633
|
-
}
|
|
3634
|
-
}
|
|
3635
|
-
}
|
|
3636
|
-
}
|
|
3637
|
-
for (const [key, write] of localMax) {
|
|
3638
|
-
const existing = morphWrites.get(key);
|
|
3639
|
-
if (!existing || write.value > existing.value) {
|
|
3640
|
-
morphWrites.set(key, write);
|
|
3641
|
-
}
|
|
3642
|
-
}
|
|
3643
|
-
}
|
|
3644
|
-
collectBoneWrites() {
|
|
3645
|
-
const writes = [];
|
|
3646
|
-
const rotations = this.computeRotationState();
|
|
3647
|
-
for (const composite of this.profile.compositeRotations || []) {
|
|
3648
|
-
const bone = this.findBoneDescriptor(composite.node);
|
|
3649
|
-
if (!bone) continue;
|
|
3650
|
-
const transform = this.evaluateCompositeTransform(bone, composite, rotations[composite.node]);
|
|
3651
|
-
if (transform) {
|
|
3652
|
-
writes.push({
|
|
3653
|
-
boneId: bone.id,
|
|
3654
|
-
transform,
|
|
3655
|
-
mode: "absolute",
|
|
3656
|
-
space: "local"
|
|
3657
|
-
});
|
|
3658
|
-
}
|
|
3659
|
-
}
|
|
3660
|
-
for (const [nodeKey, translation] of Object.entries(this.computeTranslations())) {
|
|
3661
|
-
const bone = this.findBoneDescriptor(nodeKey);
|
|
3662
|
-
if (!bone) continue;
|
|
3663
|
-
const position = addVec3(bone.restTransform?.position, translation);
|
|
3664
|
-
upsertBoneWrite(writes, bone.id, { position });
|
|
3665
|
-
}
|
|
3666
|
-
const jawAmount = this.getActiveVisemeJawAmount();
|
|
3667
|
-
const jawBinding = this.findAutoVisemeJawBinding();
|
|
3668
|
-
const jawBone = jawBinding ? this.findBoneDescriptor(jawBinding.node) : void 0;
|
|
3669
|
-
if (jawBone && jawBinding?.maxDegrees && jawBinding.channel) {
|
|
3670
|
-
const current = writes.find((write) => write.boneId === jawBone.id);
|
|
3671
|
-
if (jawAmount > 1e-6 || !current?.transform.rotation) {
|
|
3672
|
-
const rest = jawBone.restTransform?.rotation ?? identityQuat;
|
|
3673
|
-
const rotation = jawAmount > 1e-6 ? multiplyQuat(
|
|
3674
|
-
rest,
|
|
3675
|
-
quatFromChannel(jawBinding.channel, deg2rad(jawBinding.maxDegrees) * jawAmount * jawBinding.scale)
|
|
3676
|
-
) : rest;
|
|
3677
|
-
upsertBoneWrite(writes, jawBone.id, { rotation });
|
|
3678
|
-
}
|
|
3679
|
-
}
|
|
3680
|
-
return writes;
|
|
3681
|
-
}
|
|
3682
|
-
computeRotationState() {
|
|
3683
|
-
const rotations = {};
|
|
3684
|
-
for (const composite of this.profile.compositeRotations || []) {
|
|
3685
|
-
rotations[composite.node] = {
|
|
3686
|
-
pitch: this.getCompositeAxisValueForNode(composite.node, composite.pitch),
|
|
3687
|
-
yaw: this.getCompositeAxisValueForNode(composite.node, composite.yaw),
|
|
3688
|
-
roll: this.getCompositeAxisValueForNode(composite.node, composite.roll)
|
|
3689
|
-
};
|
|
3690
|
-
}
|
|
3691
|
-
return rotations;
|
|
3692
|
-
}
|
|
3693
|
-
evaluateCompositeTransform(bone, composite, state) {
|
|
3694
|
-
if (!state) return null;
|
|
3695
|
-
let rotation = bone.restTransform?.rotation ?? identityQuat;
|
|
3696
|
-
const applyAxis = (axisConfig, direction) => {
|
|
3697
|
-
if (!axisConfig || Math.abs(direction) <= 1e-6) return;
|
|
3698
|
-
const binding = getCompositeAxisBinding(
|
|
3699
|
-
composite.node,
|
|
3700
|
-
axisConfig,
|
|
3701
|
-
direction,
|
|
3702
|
-
(auId) => this.getEffectiveBoneAUValue(auId, composite.node),
|
|
3703
|
-
this.profile.auToBones
|
|
3704
|
-
);
|
|
3705
|
-
if (!binding?.maxDegrees || !binding.channel) return;
|
|
3706
|
-
rotation = multiplyQuat(
|
|
3707
|
-
rotation,
|
|
3708
|
-
quatFromChannel(binding.channel, deg2rad(binding.maxDegrees) * Math.abs(direction) * binding.scale)
|
|
3709
|
-
);
|
|
3710
|
-
};
|
|
3711
|
-
applyAxis(composite.yaw, state.yaw);
|
|
3712
|
-
applyAxis(composite.pitch, state.pitch);
|
|
3713
|
-
applyAxis(composite.roll, state.roll);
|
|
3714
|
-
return { rotation };
|
|
3715
|
-
}
|
|
3716
|
-
computeTranslations() {
|
|
3717
|
-
const translations = {};
|
|
3718
|
-
for (const [auIdText, bindings] of Object.entries(this.profile.auToBones || {})) {
|
|
3719
|
-
const auId = Number(auIdText);
|
|
3720
|
-
if (Number.isNaN(auId)) continue;
|
|
3721
|
-
const value = clamp01(this.auValues[auId] ?? 0);
|
|
3722
|
-
for (const binding of bindings || []) {
|
|
3723
|
-
if (binding.channel !== "tx" && binding.channel !== "ty" && binding.channel !== "tz") continue;
|
|
3724
|
-
if (binding.maxUnits === void 0) continue;
|
|
3725
|
-
const current = translations[binding.node] ?? { x: 0, y: 0, z: 0 };
|
|
3726
|
-
translations[binding.node] = current;
|
|
3727
|
-
if (value <= 1e-6) continue;
|
|
3728
|
-
const offset = clampSigned(value * binding.scale) * binding.maxUnits;
|
|
3729
|
-
translations[binding.node] = {
|
|
3730
|
-
x: binding.channel === "tx" ? offset : current.x,
|
|
3731
|
-
y: binding.channel === "ty" ? offset : current.y,
|
|
3732
|
-
z: binding.channel === "tz" ? offset : current.z
|
|
3733
|
-
};
|
|
3734
|
-
}
|
|
3735
|
-
}
|
|
3736
|
-
return translations;
|
|
3737
|
-
}
|
|
3738
|
-
addMorphWrites(morphWrites, morph, meshNames, value) {
|
|
3739
|
-
for (const resolved of this.resolveMorphTargets(morph, meshNames)) {
|
|
3740
|
-
const key = morphWriteKey(resolved.mesh.id, resolved.morphTarget.id);
|
|
3741
|
-
const existing = morphWrites.get(key);
|
|
3742
|
-
if (existing && existing.value >= value) continue;
|
|
3743
|
-
morphWrites.set(key, {
|
|
3744
|
-
meshId: resolved.mesh.id,
|
|
3745
|
-
morphTargetId: resolved.morphTarget.id,
|
|
3746
|
-
value,
|
|
3747
|
-
mode: "absolute"
|
|
3748
|
-
});
|
|
3749
|
-
}
|
|
3750
|
-
}
|
|
3751
|
-
resolveMorphTargets(morph, meshNames) {
|
|
3752
|
-
const result = [];
|
|
3753
|
-
for (const meshName of meshNames) {
|
|
3754
|
-
const mesh = this.model.meshes.find((candidate) => candidate.name === meshName);
|
|
3755
|
-
if (!mesh) continue;
|
|
3756
|
-
const morphTargets = mesh.morphTargetIds.map((id) => this.model.morphTargets.find((candidate) => candidate.id === id)).filter((target2) => !!target2);
|
|
3757
|
-
const target = typeof morph === "number" ? morphTargets.find((candidate) => candidate.hostIndex === morph) : resolveMorphTargetByName(morphTargets, morph, this.profile);
|
|
3758
|
-
if (target) {
|
|
3759
|
-
result.push({ mesh, morphTarget: target });
|
|
3760
|
-
}
|
|
3761
|
-
}
|
|
3762
|
-
return result;
|
|
3763
|
-
}
|
|
3764
|
-
findBoneDescriptor(nodeKey) {
|
|
3765
|
-
const configuredName = this.profile.boneNodes[nodeKey] || nodeKey;
|
|
3766
|
-
const prefix = this.profile.bonePrefix ?? "";
|
|
3767
|
-
const suffix = this.profile.boneSuffix ?? "";
|
|
3768
|
-
const prefixedName = prefix && !configuredName.startsWith(prefix) ? `${prefix}${configuredName}` : configuredName;
|
|
3769
|
-
const fullName = suffix && !prefixedName.endsWith(suffix) ? `${prefixedName}${suffix}` : prefixedName;
|
|
3770
|
-
const candidates = /* @__PURE__ */ new Set([nodeKey, configuredName, fullName]);
|
|
3771
|
-
return this.model.bones.find((bone) => candidates.has(bone.name));
|
|
3772
|
-
}
|
|
3773
|
-
findAutoVisemeJawBinding() {
|
|
3774
|
-
const candidates = [
|
|
3775
|
-
this.profile.auToBones[103]?.find(isRotationBoneBinding2),
|
|
3776
|
-
this.profile.auToBones[26]?.find(isRotationBoneBinding2)
|
|
3777
|
-
].filter((binding) => !!binding);
|
|
3778
|
-
return candidates.find((binding) => !!this.findBoneDescriptor(binding.node)) ?? candidates[0] ?? null;
|
|
3779
|
-
}
|
|
3780
|
-
getActiveVisemeJawAmount() {
|
|
3781
|
-
const jawAmounts = getVisemeJawAmounts(this.profile) ?? [];
|
|
3782
|
-
let jawAmount = 0;
|
|
3783
|
-
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
3784
|
-
const value = clamp01(this.visemeValues[index] ?? 0);
|
|
3785
|
-
if (value <= 1e-6) continue;
|
|
3786
|
-
const jawScale = this.visemeJawScales[index] ?? 1;
|
|
3787
|
-
if (Math.abs(jawScale) <= 1e-6) continue;
|
|
3788
|
-
jawAmount = Math.max(jawAmount, (jawAmounts[index] ?? 0) * value * jawScale);
|
|
3789
|
-
}
|
|
3790
|
-
return jawAmount;
|
|
3791
|
-
}
|
|
3792
|
-
getCompositeAxisValueForNode(nodeKey, axisConfig) {
|
|
3793
|
-
return getCompositeAxisValue(axisConfig, (auId) => this.getEffectiveBoneAUValue(auId, nodeKey));
|
|
3794
|
-
}
|
|
3795
|
-
getEffectiveBoneAUValue(auId, nodeKey) {
|
|
3796
|
-
const rawValue = clamp01(this.auValues[auId] ?? 0);
|
|
3797
|
-
if (rawValue <= 1e-6) return 0;
|
|
3798
|
-
const binding = this.profile.auToBones[auId]?.find((candidate) => candidate.node === nodeKey) ?? null;
|
|
3799
|
-
if (!binding?.side) return rawValue;
|
|
3800
|
-
return rawValue * getSideScale2(this.auBalances[auId] ?? 0, binding.side);
|
|
3801
|
-
}
|
|
3802
|
-
getAUMixWeight(auId) {
|
|
3803
|
-
return this.mixWeights[auId] ?? this.profile.auMixDefaults?.[auId] ?? 1;
|
|
3804
|
-
}
|
|
3805
|
-
isMixedAU(auId) {
|
|
3806
|
-
const morphs = this.profile.auToMorphs[auId];
|
|
3807
|
-
const hasMorphs = !!(morphs?.left?.length || morphs?.right?.length || morphs?.center?.length);
|
|
3808
|
-
return !!(hasMorphs && this.profile.auToBones[auId]?.length);
|
|
3809
|
-
}
|
|
3810
|
-
};
|
|
3811
|
-
function resolveMorphTargetByName(targets, key, profile) {
|
|
3812
|
-
const prefix = profile.morphPrefix || "";
|
|
3813
|
-
const suffix = profile.morphSuffix || "";
|
|
3814
|
-
const fullName = prefix + key + suffix;
|
|
3815
|
-
const exact = targets.find((target) => target.name === fullName);
|
|
3816
|
-
if (exact) return exact;
|
|
3817
|
-
const suffixRegex = profile.suffixPattern ? new RegExp(profile.suffixPattern) : null;
|
|
3818
|
-
if (!suffixRegex) return void 0;
|
|
3819
|
-
return targets.find((target) => {
|
|
3820
|
-
if (!target.name.startsWith(fullName)) return false;
|
|
3821
|
-
const candidateSuffix = target.name.slice(fullName.length);
|
|
3822
|
-
return candidateSuffix === "" || suffixRegex.test(candidateSuffix);
|
|
3823
|
-
});
|
|
3824
|
-
}
|
|
3825
|
-
function computeSideValues(base, balance) {
|
|
3826
|
-
const b = clampBalance2(balance ?? 0);
|
|
3827
|
-
if (b === 0) return { left: base, right: base };
|
|
3828
|
-
if (b < 0) return { left: base, right: base * (1 + b) };
|
|
3829
|
-
return { left: base * (1 - b), right: base };
|
|
3830
|
-
}
|
|
3831
|
-
function getSideScale2(balance, side) {
|
|
3832
|
-
if (side !== "left" && side !== "right") return 1;
|
|
3833
|
-
const b = clampBalance2(balance);
|
|
3834
|
-
if (side === "left") return b > 0 ? 1 - b : 1;
|
|
3835
|
-
return b < 0 ? 1 + b : 1;
|
|
3836
|
-
}
|
|
3837
|
-
function addVec3(base, offset) {
|
|
3838
|
-
return {
|
|
3839
|
-
x: (base?.x ?? 0) + offset.x,
|
|
3840
|
-
y: (base?.y ?? 0) + offset.y,
|
|
3841
|
-
z: (base?.z ?? 0) + offset.z
|
|
3842
|
-
};
|
|
3843
|
-
}
|
|
3844
|
-
function upsertBoneWrite(writes, boneId2, transform) {
|
|
3845
|
-
const existingIndex = writes.findIndex((write) => write.boneId === boneId2);
|
|
3846
|
-
if (existingIndex >= 0) {
|
|
3847
|
-
const existing = writes[existingIndex];
|
|
3848
|
-
writes[existingIndex] = {
|
|
3849
|
-
...existing,
|
|
3850
|
-
transform: {
|
|
3851
|
-
...existing.transform,
|
|
3852
|
-
...transform
|
|
3853
|
-
}
|
|
3854
|
-
};
|
|
3855
|
-
return;
|
|
3856
|
-
}
|
|
3857
|
-
writes.push({
|
|
3858
|
-
boneId: boneId2,
|
|
3859
|
-
transform,
|
|
3860
|
-
mode: "absolute",
|
|
3861
|
-
space: "local"
|
|
3862
|
-
});
|
|
3863
|
-
}
|
|
3864
|
-
function quatFromChannel(channel, radians) {
|
|
3865
|
-
if (channel === "rx") return quatFromAxis({ x: 1, y: 0, z: 0 }, radians);
|
|
3866
|
-
if (channel === "ry") return quatFromAxis({ x: 0, y: 1, z: 0 }, radians);
|
|
3867
|
-
return quatFromAxis({ x: 0, y: 0, z: 1 }, radians);
|
|
3868
|
-
}
|
|
3869
|
-
function quatFromAxis(axis, radians) {
|
|
3870
|
-
const half = radians / 2;
|
|
3871
|
-
const s = Math.sin(half);
|
|
3872
|
-
return normalizeQuat({
|
|
3873
|
-
x: axis.x * s,
|
|
3874
|
-
y: axis.y * s,
|
|
3875
|
-
z: axis.z * s,
|
|
3876
|
-
w: Math.cos(half)
|
|
3877
|
-
});
|
|
3878
|
-
}
|
|
3879
|
-
function multiplyQuat(a, b) {
|
|
3880
|
-
return normalizeQuat({
|
|
3881
|
-
x: a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
|
|
3882
|
-
y: a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
|
|
3883
|
-
z: a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
|
|
3884
|
-
w: a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z
|
|
3885
|
-
});
|
|
3886
|
-
}
|
|
3887
|
-
function normalizeQuat(q) {
|
|
3888
|
-
const len = Math.hypot(q.x, q.y, q.z, q.w);
|
|
3889
|
-
if (len <= 1e-12) return identityQuat;
|
|
3890
|
-
return {
|
|
3891
|
-
x: q.x / len,
|
|
3892
|
-
y: q.y / len,
|
|
3893
|
-
z: q.z / len,
|
|
3894
|
-
w: q.w / len
|
|
3895
|
-
};
|
|
3896
|
-
}
|
|
3897
|
-
function deg2rad(degrees) {
|
|
3898
|
-
return degrees * Math.PI / 180;
|
|
3899
|
-
}
|
|
3900
|
-
function clamp01(value) {
|
|
3901
|
-
if (!Number.isFinite(value)) return 0;
|
|
3902
|
-
return value < 0 ? 0 : value > 1 ? 1 : value;
|
|
3903
|
-
}
|
|
3904
|
-
function clampSigned(value) {
|
|
3905
|
-
if (!Number.isFinite(value)) return 0;
|
|
3906
|
-
return value < -1 ? -1 : value > 1 ? 1 : value;
|
|
3907
|
-
}
|
|
3908
|
-
function clampBalance2(value) {
|
|
3909
|
-
if (!Number.isFinite(value)) return 0;
|
|
3910
|
-
return Math.max(-1, Math.min(1, value));
|
|
3911
|
-
}
|
|
3912
|
-
function morphWriteKey(meshId2, morphTargetId2) {
|
|
3913
|
-
return `${meshId2}:${morphTargetId2}`;
|
|
3914
|
-
}
|
|
3915
|
-
|
|
3916
3512
|
// src/core/contracts/abi.ts
|
|
3917
3513
|
var PACKED_MORPH_FRAME_DELTA_STRIDE = 4;
|
|
3918
3514
|
var PACKED_BONE_FRAME_DELTA_STRIDE = 9;
|
|
@@ -3924,11 +3520,13 @@ var WasmRuntimeCore = class {
|
|
|
3924
3520
|
constructor(options) {
|
|
3925
3521
|
__publicField(this, "wasm");
|
|
3926
3522
|
__publicField(this, "core");
|
|
3523
|
+
__publicField(this, "allowEmptyBindings");
|
|
3927
3524
|
__publicField(this, "profile");
|
|
3928
3525
|
__publicField(this, "model");
|
|
3929
3526
|
this.wasm = options.wasm;
|
|
3930
3527
|
this.profile = options.profile;
|
|
3931
3528
|
this.model = options.model;
|
|
3529
|
+
this.allowEmptyBindings = options.allowEmptyBindings ?? false;
|
|
3932
3530
|
const RuntimeCtor = options.wasm.RuntimeCore;
|
|
3933
3531
|
if (!RuntimeCtor) {
|
|
3934
3532
|
throw new Error("Embody Wasm module does not export RuntimeCore");
|
|
@@ -3947,6 +3545,24 @@ var WasmRuntimeCore = class {
|
|
|
3947
3545
|
setAU(id, value, balance = 0) {
|
|
3948
3546
|
this.core.set_au(id >>> 0, value, balance);
|
|
3949
3547
|
}
|
|
3548
|
+
setContinuum(negativeAU, positiveAU, value, balance = 0) {
|
|
3549
|
+
this.core.set_continuum(negativeAU >>> 0, positiveAU >>> 0, value, balance);
|
|
3550
|
+
}
|
|
3551
|
+
transitionAU(id, to, durationMs, balance = Number.NaN) {
|
|
3552
|
+
this.core.transition_au(id >>> 0, to, durationMs, balance);
|
|
3553
|
+
}
|
|
3554
|
+
transitionViseme(visemeIndex, to, durationMs, jawScale = 1) {
|
|
3555
|
+
this.core.transition_viseme(visemeIndex >>> 0, to, durationMs, jawScale);
|
|
3556
|
+
}
|
|
3557
|
+
update(deltaSeconds) {
|
|
3558
|
+
return this.core.update(deltaSeconds);
|
|
3559
|
+
}
|
|
3560
|
+
activeTransitionCount() {
|
|
3561
|
+
return this.core.active_transition_count();
|
|
3562
|
+
}
|
|
3563
|
+
clearTransitions() {
|
|
3564
|
+
this.core.clear_transitions();
|
|
3565
|
+
}
|
|
3950
3566
|
getAU(id) {
|
|
3951
3567
|
return this.core.get_au(id >>> 0);
|
|
3952
3568
|
}
|
|
@@ -3982,7 +3598,13 @@ var WasmRuntimeCore = class {
|
|
|
3982
3598
|
};
|
|
3983
3599
|
}
|
|
3984
3600
|
reloadBindings() {
|
|
3985
|
-
|
|
3601
|
+
const profileJson = JSON.stringify(this.profile);
|
|
3602
|
+
const modelJson = JSON.stringify(this.model);
|
|
3603
|
+
if (this.allowEmptyBindings) {
|
|
3604
|
+
this.core.configure(profileJson, modelJson);
|
|
3605
|
+
} else {
|
|
3606
|
+
this.core.configure_with_profile(profileJson, modelJson);
|
|
3607
|
+
}
|
|
3986
3608
|
}
|
|
3987
3609
|
};
|
|
3988
3610
|
function unpackBoneFrameDelta(values) {
|
|
@@ -5331,7 +4953,7 @@ var CC4_PRESET = {
|
|
|
5331
4953
|
};
|
|
5332
4954
|
|
|
5333
4955
|
// src/engines/three/hair/HairPhysicsController.ts
|
|
5334
|
-
var
|
|
4956
|
+
var clamp01 = (value) => value < 0 ? 0 : value > 1 ? 1 : value;
|
|
5335
4957
|
var HairPhysicsController = class {
|
|
5336
4958
|
constructor(host) {
|
|
5337
4959
|
__publicField(this, "host");
|
|
@@ -5829,7 +5451,7 @@ var HairPhysicsController = class {
|
|
|
5829
5451
|
}
|
|
5830
5452
|
}
|
|
5831
5453
|
setMorphOnMeshes(meshNames, morphKey, value) {
|
|
5832
|
-
const val =
|
|
5454
|
+
const val = clamp01(value);
|
|
5833
5455
|
for (const name of meshNames) {
|
|
5834
5456
|
const mesh = this.registeredHairObjects.get(name) || this.host.getMeshByName(name);
|
|
5835
5457
|
if (!mesh) continue;
|
|
@@ -6073,9 +5695,9 @@ var HairPhysicsController = class {
|
|
|
6073
5695
|
const deltaVertical = vertical - this.lastHeadVertical;
|
|
6074
5696
|
this.lastHeadHorizontal = horizontal;
|
|
6075
5697
|
this.lastHeadVertical = vertical;
|
|
6076
|
-
const leftWeight =
|
|
6077
|
-
const rightWeight =
|
|
6078
|
-
const frontWeight =
|
|
5698
|
+
const leftWeight = clamp01(deltaHorizontal > 0 ? deltaHorizontal : 0);
|
|
5699
|
+
const rightWeight = clamp01(deltaHorizontal < 0 ? -deltaHorizontal : 0);
|
|
5700
|
+
const frontWeight = clamp01(deltaVertical > 0 ? deltaVertical : 0);
|
|
6079
5701
|
const minTrigger = 0.01;
|
|
6080
5702
|
if (leftWeight <= minTrigger && rightWeight <= minTrigger && frontWeight <= minTrigger) return;
|
|
6081
5703
|
if (this.impulseClipDirty) {
|
|
@@ -7041,7 +6663,7 @@ async function getPresetWithProfile(presetType, profile) {
|
|
|
7041
6663
|
}
|
|
7042
6664
|
|
|
7043
6665
|
// src/engines/three/Embody.ts
|
|
7044
|
-
var
|
|
6666
|
+
var deg2rad = (d) => d * Math.PI / 180;
|
|
7045
6667
|
var X_AXIS2 = new Vector3(1, 0, 0);
|
|
7046
6668
|
var Y_AXIS2 = new Vector3(0, 1, 0);
|
|
7047
6669
|
var Z_AXIS2 = new Vector3(0, 0, 1);
|
|
@@ -7064,9 +6686,18 @@ function buildAUToCompositeMap(composites) {
|
|
|
7064
6686
|
});
|
|
7065
6687
|
return map;
|
|
7066
6688
|
}
|
|
7067
|
-
function
|
|
6689
|
+
function clamp012(x) {
|
|
7068
6690
|
return x < 0 ? 0 : x > 1 ? 1 : x;
|
|
7069
6691
|
}
|
|
6692
|
+
var RESOLVED_TRANSITION_HANDLE = {
|
|
6693
|
+
promise: Promise.resolve(),
|
|
6694
|
+
pause: () => {
|
|
6695
|
+
},
|
|
6696
|
+
resume: () => {
|
|
6697
|
+
},
|
|
6698
|
+
cancel: () => {
|
|
6699
|
+
}
|
|
6700
|
+
};
|
|
7070
6701
|
var _Embody = class _Embody {
|
|
7071
6702
|
constructor(config = {}, animation) {
|
|
7072
6703
|
// Optional hook for animation schedulers.
|
|
@@ -7110,7 +6741,6 @@ var _Embody = class _Embody {
|
|
|
7110
6741
|
__publicField(this, "modelInspector", new ThreeModelInspector());
|
|
7111
6742
|
__publicField(this, "frameApplier", new ThreeFrameApplier());
|
|
7112
6743
|
__publicField(this, "modelDescriptor", null);
|
|
7113
|
-
__publicField(this, "tsRuntimeCore", null);
|
|
7114
6744
|
__publicField(this, "wasmRuntimeCore", null);
|
|
7115
6745
|
__publicField(this, "framePathReady", false);
|
|
7116
6746
|
// Internal animation loop
|
|
@@ -7223,48 +6853,21 @@ var _Embody = class _Embody {
|
|
|
7223
6853
|
bindHostNeutralRuntime(inspection) {
|
|
7224
6854
|
this.modelDescriptor = inspection.descriptor;
|
|
7225
6855
|
this.frameApplier.setBindings(buildFrameApplierBindings(inspection));
|
|
7226
|
-
|
|
6856
|
+
const wasm = requireInitializedEmbodyCore();
|
|
6857
|
+
this.attachWasmCore(wasm);
|
|
6858
|
+
this.wasmRuntimeCore = new WasmRuntimeCore({
|
|
7227
6859
|
profile: this.config,
|
|
7228
|
-
model: inspection.descriptor
|
|
6860
|
+
model: inspection.descriptor,
|
|
6861
|
+
wasm,
|
|
6862
|
+
allowEmptyBindings: true
|
|
7229
6863
|
});
|
|
7230
6864
|
this.syncRuntimeCoreState();
|
|
7231
6865
|
this.framePathReady = true;
|
|
7232
|
-
void initEmbodyCore().then((wasm) => {
|
|
7233
|
-
if (!this.modelDescriptor) return;
|
|
7234
|
-
this.attachWasmCore(wasm);
|
|
7235
|
-
this.wasmRuntimeCore = new WasmRuntimeCore({
|
|
7236
|
-
profile: this.config,
|
|
7237
|
-
model: this.modelDescriptor,
|
|
7238
|
-
wasm
|
|
7239
|
-
});
|
|
7240
|
-
this.syncRuntimeCoreState();
|
|
7241
|
-
}).catch(() => {
|
|
7242
|
-
this.wasmRuntimeCore = null;
|
|
7243
|
-
});
|
|
7244
6866
|
}
|
|
7245
6867
|
attachWasmCore(core) {
|
|
7246
6868
|
this.hairPhysics.setCore(core);
|
|
7247
6869
|
}
|
|
7248
6870
|
syncRuntimeCoreState() {
|
|
7249
|
-
if (this.tsRuntimeCore) {
|
|
7250
|
-
this.tsRuntimeCore.setProfile(this.config);
|
|
7251
|
-
if (this.modelDescriptor) {
|
|
7252
|
-
this.tsRuntimeCore.setModelDescriptor(this.modelDescriptor);
|
|
7253
|
-
}
|
|
7254
|
-
for (const [auIdText, value] of Object.entries(this.auValues)) {
|
|
7255
|
-
const auId = Number(auIdText);
|
|
7256
|
-
if (Number.isNaN(auId)) continue;
|
|
7257
|
-
this.tsRuntimeCore.setAU(auId, value, this.auBalances[auId]);
|
|
7258
|
-
}
|
|
7259
|
-
for (const [auIdText, weight] of Object.entries(this.mixWeights)) {
|
|
7260
|
-
const auId = Number(auIdText);
|
|
7261
|
-
if (Number.isNaN(auId)) continue;
|
|
7262
|
-
this.tsRuntimeCore.setAUMixWeight(auId, weight);
|
|
7263
|
-
}
|
|
7264
|
-
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
7265
|
-
this.tsRuntimeCore.setViseme(index, this.visemeValues[index] ?? 0, this.visemeJawScales[index] ?? 1);
|
|
7266
|
-
}
|
|
7267
|
-
}
|
|
7268
6871
|
if (this.wasmRuntimeCore) {
|
|
7269
6872
|
this.wasmRuntimeCore.setProfile(this.config);
|
|
7270
6873
|
if (this.modelDescriptor) {
|
|
@@ -7282,7 +6885,7 @@ var _Embody = class _Embody {
|
|
|
7282
6885
|
this.wasmRuntimeCore.setAUMixWeight(auId, weight);
|
|
7283
6886
|
}
|
|
7284
6887
|
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
7285
|
-
this.wasmRuntimeCore.setViseme(index, this.visemeValues[index] ?? 0);
|
|
6888
|
+
this.wasmRuntimeCore.setViseme(index, this.visemeValues[index] ?? 0, this.visemeJawScales[index] ?? 1);
|
|
7286
6889
|
}
|
|
7287
6890
|
}
|
|
7288
6891
|
}
|
|
@@ -7300,25 +6903,13 @@ var _Embody = class _Embody {
|
|
|
7300
6903
|
});
|
|
7301
6904
|
this.modelDescriptor = inspection.descriptor;
|
|
7302
6905
|
this.frameApplier.setBindings(buildFrameApplierBindings(inspection));
|
|
7303
|
-
this.tsRuntimeCore?.setModelDescriptor(inspection.descriptor);
|
|
7304
6906
|
this.wasmRuntimeCore?.setModelDescriptor(inspection.descriptor);
|
|
7305
6907
|
}
|
|
7306
|
-
|
|
7307
|
-
if (!this.framePathReady || !this.model) return false;
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
);
|
|
7312
|
-
return true;
|
|
7313
|
-
}
|
|
7314
|
-
if (this.tsRuntimeCore) {
|
|
7315
|
-
const frame = this.tsRuntimeCore.evaluateFrameDelta();
|
|
7316
|
-
this.frameApplier.applyFrameDelta(this.model, {
|
|
7317
|
-
morphTargets: frame.morphTargets
|
|
7318
|
-
});
|
|
7319
|
-
return true;
|
|
7320
|
-
}
|
|
7321
|
-
return false;
|
|
6908
|
+
applyLiveFrameDelta() {
|
|
6909
|
+
if (!this.framePathReady || !this.model || !this.wasmRuntimeCore) return false;
|
|
6910
|
+
this.frameApplier.applyFrameDelta(this.model, this.wasmRuntimeCore.evaluateFrameDelta());
|
|
6911
|
+
this.pendingCompositeNodes.clear();
|
|
6912
|
+
return true;
|
|
7322
6913
|
}
|
|
7323
6914
|
rebuildMorphTargetsCache() {
|
|
7324
6915
|
this.morphKeyCache.clear();
|
|
@@ -7420,6 +7011,11 @@ var _Embody = class _Embody {
|
|
|
7420
7011
|
const dtSeconds = Math.max(0, deltaSeconds || 0);
|
|
7421
7012
|
if (dtSeconds <= 0 || this.isPaused) return;
|
|
7422
7013
|
this.animation.tick(dtSeconds);
|
|
7014
|
+
const wasmTransitionCount = this.wasmRuntimeCore?.activeTransitionCount() ?? 0;
|
|
7015
|
+
const remainingWasmTransitions = this.wasmRuntimeCore?.update(dtSeconds) ?? 0;
|
|
7016
|
+
if (wasmTransitionCount > 0 || remainingWasmTransitions > 0) {
|
|
7017
|
+
this.applyLiveFrameDelta();
|
|
7018
|
+
}
|
|
7423
7019
|
this.flushPendingComposites();
|
|
7424
7020
|
this.animationController.update(dtSeconds);
|
|
7425
7021
|
this.hairPhysics.update(dtSeconds);
|
|
@@ -7454,7 +7050,6 @@ var _Embody = class _Embody {
|
|
|
7454
7050
|
this.model = null;
|
|
7455
7051
|
this.bones = {};
|
|
7456
7052
|
this.modelDescriptor = null;
|
|
7457
|
-
this.tsRuntimeCore = null;
|
|
7458
7053
|
this.wasmRuntimeCore = null;
|
|
7459
7054
|
this.framePathReady = false;
|
|
7460
7055
|
}
|
|
@@ -7491,15 +7086,14 @@ var _Embody = class _Embody {
|
|
|
7491
7086
|
if (balance !== void 0) {
|
|
7492
7087
|
this.auBalances[id] = balance;
|
|
7493
7088
|
}
|
|
7494
|
-
this.tsRuntimeCore?.setAU(id, v, balance);
|
|
7495
7089
|
this.wasmRuntimeCore?.setAU(id, v, balance ?? this.auBalances[id] ?? 0);
|
|
7496
|
-
const appliedViaFrame = this.
|
|
7090
|
+
const appliedViaFrame = this.applyLiveFrameDelta();
|
|
7497
7091
|
if (!appliedViaFrame) {
|
|
7498
7092
|
const resolvedMorphTargets = this.resolvedAUMorphTargets.get(id);
|
|
7499
7093
|
const { left: leftKeys, right: rightKeys, center: centerKeys } = this.getAUMorphsBySide(id);
|
|
7500
7094
|
if (resolvedMorphTargets) {
|
|
7501
7095
|
const mixWeight = this.isMixedAU(id) ? this.getAUMixWeight(id) : 1;
|
|
7502
|
-
const base =
|
|
7096
|
+
const base = clamp012(v) * mixWeight;
|
|
7503
7097
|
const { left: leftVal, right: rightVal } = this.computeSideValues(base, balance);
|
|
7504
7098
|
if (resolvedMorphTargets.left.length || resolvedMorphTargets.right.length) {
|
|
7505
7099
|
this.applyMorphTargets(resolvedMorphTargets.left, leftVal);
|
|
@@ -7508,7 +7102,7 @@ var _Embody = class _Embody {
|
|
|
7508
7102
|
this.applyMorphTargets(resolvedMorphTargets.center, base);
|
|
7509
7103
|
} else if (leftKeys.length || rightKeys.length || centerKeys.length) {
|
|
7510
7104
|
const mixWeight = this.isMixedAU(id) ? this.getAUMixWeight(id) : 1;
|
|
7511
|
-
const base =
|
|
7105
|
+
const base = clamp012(v) * mixWeight;
|
|
7512
7106
|
const meshNames = this.getMeshNamesForAU(id);
|
|
7513
7107
|
const { left: leftVal, right: rightVal } = this.computeSideValues(base, balance);
|
|
7514
7108
|
if (leftKeys.length || rightKeys.length) {
|
|
@@ -7527,6 +7121,7 @@ var _Embody = class _Embody {
|
|
|
7527
7121
|
}
|
|
7528
7122
|
}
|
|
7529
7123
|
}
|
|
7124
|
+
if (appliedViaFrame) return;
|
|
7530
7125
|
const compositeInfo = this.auToCompositeMap.get(id);
|
|
7531
7126
|
if (compositeInfo) {
|
|
7532
7127
|
for (const nodeKey of compositeInfo.nodes) {
|
|
@@ -7561,7 +7156,7 @@ var _Embody = class _Embody {
|
|
|
7561
7156
|
return this.transitionContinuum(negAU, posAU, continuumValue, durationMs, balance);
|
|
7562
7157
|
}
|
|
7563
7158
|
}
|
|
7564
|
-
const target =
|
|
7159
|
+
const target = clamp012(to);
|
|
7565
7160
|
if (balance !== void 0) {
|
|
7566
7161
|
this.auBalances[numId] = balance;
|
|
7567
7162
|
}
|
|
@@ -7572,6 +7167,19 @@ var _Embody = class _Embody {
|
|
|
7572
7167
|
const base = target * mixWeight;
|
|
7573
7168
|
const { left: leftVal, right: rightVal } = this.computeSideValues(base, storedBalance);
|
|
7574
7169
|
this.auValues[numId] = target;
|
|
7170
|
+
if (this.wasmRuntimeCore) {
|
|
7171
|
+
this.wasmRuntimeCore.transitionAU(
|
|
7172
|
+
numId,
|
|
7173
|
+
target,
|
|
7174
|
+
durationMs,
|
|
7175
|
+
balance ?? this.auBalances[numId] ?? Number.NaN
|
|
7176
|
+
);
|
|
7177
|
+
this.update(0);
|
|
7178
|
+
if (durationMs <= 0) {
|
|
7179
|
+
this.applyLiveFrameDelta();
|
|
7180
|
+
}
|
|
7181
|
+
return RESOLVED_TRANSITION_HANDLE;
|
|
7182
|
+
}
|
|
7575
7183
|
const handles = [];
|
|
7576
7184
|
const meshNames = this.getMeshNamesForAU(numId);
|
|
7577
7185
|
if (leftKeys.length || rightKeys.length) {
|
|
@@ -7634,6 +7242,17 @@ var _Embody = class _Embody {
|
|
|
7634
7242
|
*/
|
|
7635
7243
|
setContinuum(negAU, posAU, continuumValue, balance) {
|
|
7636
7244
|
const value = Math.max(-1, Math.min(1, continuumValue));
|
|
7245
|
+
this.auValues[negAU] = value < 0 ? Math.abs(value) : 0;
|
|
7246
|
+
this.auValues[posAU] = value > 0 ? value : 0;
|
|
7247
|
+
if (balance !== void 0) {
|
|
7248
|
+
this.auBalances[negAU] = balance;
|
|
7249
|
+
this.auBalances[posAU] = balance;
|
|
7250
|
+
}
|
|
7251
|
+
if (this.wasmRuntimeCore) {
|
|
7252
|
+
this.wasmRuntimeCore.setContinuum(negAU, posAU, value, balance ?? 0);
|
|
7253
|
+
this.applyLiveFrameDelta();
|
|
7254
|
+
return;
|
|
7255
|
+
}
|
|
7637
7256
|
if (value < 0) {
|
|
7638
7257
|
this.setAU(posAU, 0, balance);
|
|
7639
7258
|
this.setAU(negAU, Math.abs(value), balance);
|
|
@@ -7659,10 +7278,32 @@ var _Embody = class _Embody {
|
|
|
7659
7278
|
*/
|
|
7660
7279
|
transitionContinuum(negAU, posAU, continuumValue, durationMs = 200, balance) {
|
|
7661
7280
|
const target = Math.max(-1, Math.min(1, continuumValue));
|
|
7662
|
-
const driverKey = `continuum_${negAU}_${posAU}`;
|
|
7663
7281
|
const currentNeg = this.auValues[negAU] ?? 0;
|
|
7664
7282
|
const currentPos = this.auValues[posAU] ?? 0;
|
|
7665
7283
|
const currentContinuum = currentPos - currentNeg;
|
|
7284
|
+
this.auValues[negAU] = target < 0 ? Math.abs(target) : 0;
|
|
7285
|
+
this.auValues[posAU] = target > 0 ? target : 0;
|
|
7286
|
+
if (balance !== void 0) {
|
|
7287
|
+
this.auBalances[negAU] = balance;
|
|
7288
|
+
this.auBalances[posAU] = balance;
|
|
7289
|
+
}
|
|
7290
|
+
if (this.wasmRuntimeCore) {
|
|
7291
|
+
if (target < 0) {
|
|
7292
|
+
this.wasmRuntimeCore.transitionAU(posAU, 0, durationMs, balance ?? this.auBalances[posAU] ?? Number.NaN);
|
|
7293
|
+
this.wasmRuntimeCore.transitionAU(negAU, Math.abs(target), durationMs, balance ?? this.auBalances[negAU] ?? Number.NaN);
|
|
7294
|
+
} else if (target > 0) {
|
|
7295
|
+
this.wasmRuntimeCore.transitionAU(negAU, 0, durationMs, balance ?? this.auBalances[negAU] ?? Number.NaN);
|
|
7296
|
+
this.wasmRuntimeCore.transitionAU(posAU, target, durationMs, balance ?? this.auBalances[posAU] ?? Number.NaN);
|
|
7297
|
+
} else {
|
|
7298
|
+
this.wasmRuntimeCore.transitionAU(negAU, 0, durationMs, balance ?? this.auBalances[negAU] ?? Number.NaN);
|
|
7299
|
+
this.wasmRuntimeCore.transitionAU(posAU, 0, durationMs, balance ?? this.auBalances[posAU] ?? Number.NaN);
|
|
7300
|
+
}
|
|
7301
|
+
if (durationMs <= 0) {
|
|
7302
|
+
this.applyLiveFrameDelta();
|
|
7303
|
+
}
|
|
7304
|
+
return RESOLVED_TRANSITION_HANDLE;
|
|
7305
|
+
}
|
|
7306
|
+
const driverKey = `continuum_${negAU}_${posAU}`;
|
|
7666
7307
|
return this.animation.addTransition(driverKey, currentContinuum, target, durationMs, (value) => this.setContinuum(negAU, posAU, value, balance));
|
|
7667
7308
|
}
|
|
7668
7309
|
// ============================================================================
|
|
@@ -7744,7 +7385,7 @@ var _Embody = class _Embody {
|
|
|
7744
7385
|
this.hairPhysics.refreshMeshSelection();
|
|
7745
7386
|
}
|
|
7746
7387
|
setMorph(key, v, meshNamesOrTargets) {
|
|
7747
|
-
const val =
|
|
7388
|
+
const val = clamp012(v);
|
|
7748
7389
|
if (Array.isArray(meshNamesOrTargets) && meshNamesOrTargets.length > 0 && typeof meshNamesOrTargets[0] === "object" && "infl" in meshNamesOrTargets[0]) {
|
|
7749
7390
|
const targets2 = meshNamesOrTargets;
|
|
7750
7391
|
this.applyMorphTargets(targets2, val);
|
|
@@ -7756,7 +7397,7 @@ var _Embody = class _Embody {
|
|
|
7756
7397
|
this.applyMorphTargets(targets, val);
|
|
7757
7398
|
}
|
|
7758
7399
|
setMorphInfluence(index, v, meshNames) {
|
|
7759
|
-
const val =
|
|
7400
|
+
const val = clamp012(v);
|
|
7760
7401
|
const targets = this.resolveMorphTargetsByIndex(index, meshNames);
|
|
7761
7402
|
if (targets.length === 0) return;
|
|
7762
7403
|
this.applyMorphTargets(targets, val);
|
|
@@ -7831,21 +7472,21 @@ var _Embody = class _Embody {
|
|
|
7831
7472
|
}
|
|
7832
7473
|
transitionMorph(key, to, durationMs = 120, meshNames) {
|
|
7833
7474
|
const transitionKey = meshNames?.length ? `morph_${this.getMorphKeyCacheKey(key, meshNames)}` : `morph_${this.getMorphKeyCacheKey(key)}`;
|
|
7834
|
-
const target =
|
|
7475
|
+
const target = clamp012(to);
|
|
7835
7476
|
const targets = this.resolveMorphTargets(key, meshNames);
|
|
7836
7477
|
const from = targets.length > 0 ? targets[0].infl[targets[0].idx] ?? 0 : this.getMorphValue(key);
|
|
7837
7478
|
return this.animation.addTransition(transitionKey, from, target, durationMs, (value) => {
|
|
7838
|
-
const val =
|
|
7479
|
+
const val = clamp012(value);
|
|
7839
7480
|
this.applyMorphTargets(targets, val);
|
|
7840
7481
|
});
|
|
7841
7482
|
}
|
|
7842
7483
|
transitionMorphInfluence(index, to, durationMs = 120, meshNames) {
|
|
7843
7484
|
const transitionKey = meshNames?.length ? `morph_${this.getMorphIndexCacheKey(index, meshNames)}` : `morph_${this.getMorphIndexCacheKey(index)}`;
|
|
7844
|
-
const target =
|
|
7485
|
+
const target = clamp012(to);
|
|
7845
7486
|
const targets = this.resolveMorphTargetsByIndex(index, meshNames);
|
|
7846
7487
|
const from = targets.length > 0 ? targets[0].infl[targets[0].idx] ?? 0 : this.getMorphValueByIndex(index);
|
|
7847
7488
|
return this.animation.addTransition(transitionKey, from, target, durationMs, (value) => {
|
|
7848
|
-
const val =
|
|
7489
|
+
const val = clamp012(value);
|
|
7849
7490
|
this.applyMorphTargets(targets, val);
|
|
7850
7491
|
});
|
|
7851
7492
|
}
|
|
@@ -7854,7 +7495,7 @@ var _Embody = class _Embody {
|
|
|
7854
7495
|
// ============================================================================
|
|
7855
7496
|
setViseme(visemeIndex, value, jawScale = 1) {
|
|
7856
7497
|
if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) return;
|
|
7857
|
-
const val =
|
|
7498
|
+
const val = clamp012(value);
|
|
7858
7499
|
this.visemeValues[visemeIndex] = val;
|
|
7859
7500
|
this.visemeJawScales[visemeIndex] = jawScale;
|
|
7860
7501
|
this.applyVisemeRuntimeState();
|
|
@@ -7866,16 +7507,24 @@ var _Embody = class _Embody {
|
|
|
7866
7507
|
}, cancel: () => {
|
|
7867
7508
|
} };
|
|
7868
7509
|
}
|
|
7869
|
-
const target =
|
|
7510
|
+
const target = clamp012(to);
|
|
7870
7511
|
const from = this.visemeValues[visemeIndex] ?? 0;
|
|
7871
7512
|
this.visemeJawScales[visemeIndex] = jawScale;
|
|
7513
|
+
if (this.wasmRuntimeCore) {
|
|
7514
|
+
this.visemeValues[visemeIndex] = target;
|
|
7515
|
+
this.wasmRuntimeCore.transitionViseme(visemeIndex, target, durationMs, jawScale);
|
|
7516
|
+
if (durationMs <= 0) {
|
|
7517
|
+
this.applyLiveFrameDelta();
|
|
7518
|
+
}
|
|
7519
|
+
return RESOLVED_TRANSITION_HANDLE;
|
|
7520
|
+
}
|
|
7872
7521
|
return this.animation.addTransition(
|
|
7873
7522
|
`viseme_value_${visemeIndex}`,
|
|
7874
7523
|
from,
|
|
7875
7524
|
target,
|
|
7876
7525
|
durationMs,
|
|
7877
7526
|
(value) => {
|
|
7878
|
-
this.visemeValues[visemeIndex] =
|
|
7527
|
+
this.visemeValues[visemeIndex] = clamp012(value);
|
|
7879
7528
|
this.visemeJawScales[visemeIndex] = jawScale;
|
|
7880
7529
|
this.applyVisemeRuntimeState();
|
|
7881
7530
|
}
|
|
@@ -7900,8 +7549,7 @@ var _Embody = class _Embody {
|
|
|
7900
7549
|
// MIX WEIGHT CONTROL
|
|
7901
7550
|
// ============================================================================
|
|
7902
7551
|
setAUMixWeight(id, weight) {
|
|
7903
|
-
this.mixWeights[id] =
|
|
7904
|
-
this.tsRuntimeCore?.setAUMixWeight(id, weight);
|
|
7552
|
+
this.mixWeights[id] = clamp012(weight);
|
|
7905
7553
|
this.wasmRuntimeCore?.setAUMixWeight(id, weight);
|
|
7906
7554
|
const v = this.auValues[id] ?? 0;
|
|
7907
7555
|
if (v > 0) this.setAU(id, v);
|
|
@@ -7939,9 +7587,10 @@ var _Embody = class _Embody {
|
|
|
7939
7587
|
}
|
|
7940
7588
|
clearTransitions() {
|
|
7941
7589
|
this.animation.clearTransitions();
|
|
7590
|
+
this.wasmRuntimeCore?.clearTransitions();
|
|
7942
7591
|
}
|
|
7943
7592
|
getActiveTransitionCount() {
|
|
7944
|
-
return this.animation.getActiveTransitionCount();
|
|
7593
|
+
return this.animation.getActiveTransitionCount() + (this.wasmRuntimeCore?.activeTransitionCount() ?? 0);
|
|
7945
7594
|
}
|
|
7946
7595
|
resetToNeutral() {
|
|
7947
7596
|
this.auValues = {};
|
|
@@ -7951,6 +7600,7 @@ var _Embody = class _Embody {
|
|
|
7951
7600
|
this.translations = {};
|
|
7952
7601
|
this.initBoneRotations();
|
|
7953
7602
|
this.clearTransitions();
|
|
7603
|
+
this.wasmRuntimeCore?.clear();
|
|
7954
7604
|
const morphTargetsToReset = [];
|
|
7955
7605
|
for (const m of this.meshes) {
|
|
7956
7606
|
const infl = m.morphTargetInfluences;
|
|
@@ -8129,7 +7779,18 @@ var _Embody = class _Embody {
|
|
|
8129
7779
|
this.bones = this.resolveBones(this.model);
|
|
8130
7780
|
this.missingBoneWarnings.clear();
|
|
8131
7781
|
this.rebuildMorphTargetsCache();
|
|
8132
|
-
|
|
7782
|
+
Object.values(this.bones).forEach((entry) => {
|
|
7783
|
+
if (!entry) return;
|
|
7784
|
+
this.frameApplier.applyObjectTransform(entry.obj, {
|
|
7785
|
+
position: { x: entry.basePos.x, y: entry.basePos.y, z: entry.basePos.z },
|
|
7786
|
+
rotation: {
|
|
7787
|
+
x: entry.baseQuat.x,
|
|
7788
|
+
y: entry.baseQuat.y,
|
|
7789
|
+
z: entry.baseQuat.z,
|
|
7790
|
+
w: entry.baseQuat.w
|
|
7791
|
+
}
|
|
7792
|
+
});
|
|
7793
|
+
});
|
|
8133
7794
|
this.wasmRuntimeCore?.setProfile(this.config);
|
|
8134
7795
|
this.refreshHostNeutralModelDescriptor();
|
|
8135
7796
|
}
|
|
@@ -8246,20 +7907,20 @@ var _Embody = class _Embody {
|
|
|
8246
7907
|
}
|
|
8247
7908
|
applyVisemeRuntimeState() {
|
|
8248
7909
|
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
8249
|
-
this.
|
|
8250
|
-
this.wasmRuntimeCore?.setViseme(index, this.visemeValues[index] ?? 0);
|
|
7910
|
+
this.wasmRuntimeCore?.setViseme(index, this.visemeValues[index] ?? 0, this.visemeJawScales[index] ?? 1);
|
|
8251
7911
|
}
|
|
8252
|
-
|
|
7912
|
+
const appliedViaFrame = this.applyLiveFrameDelta();
|
|
7913
|
+
if (!appliedViaFrame) {
|
|
8253
7914
|
for (const targets of this.resolvedVisemeTargets) {
|
|
8254
7915
|
this.frameApplier.resetMorphTargets(targets || []);
|
|
8255
7916
|
}
|
|
8256
7917
|
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
8257
|
-
const value =
|
|
7918
|
+
const value = clamp012(this.visemeValues[index] ?? 0);
|
|
8258
7919
|
if (value <= 1e-6) continue;
|
|
8259
7920
|
const targets = this.resolvedVisemeTargets[index] || [];
|
|
8260
7921
|
for (const target of targets) {
|
|
8261
7922
|
if (target.idx >= target.infl.length) continue;
|
|
8262
|
-
const weighted =
|
|
7923
|
+
const weighted = clamp012(value * target.weight);
|
|
8263
7924
|
this.frameApplier.applyMorphTargets(
|
|
8264
7925
|
[target],
|
|
8265
7926
|
Math.max(target.infl[target.idx] ?? 0, weighted)
|
|
@@ -8267,7 +7928,9 @@ var _Embody = class _Embody {
|
|
|
8267
7928
|
}
|
|
8268
7929
|
}
|
|
8269
7930
|
}
|
|
8270
|
-
|
|
7931
|
+
if (!appliedViaFrame) {
|
|
7932
|
+
this.updateBoneRotation(this.getJawBoneNodeKey(), "pitch", this.getActiveVisemeJawAmount());
|
|
7933
|
+
}
|
|
8271
7934
|
}
|
|
8272
7935
|
getJawBoneNodeKey() {
|
|
8273
7936
|
const candidates = [
|
|
@@ -8289,7 +7952,7 @@ var _Embody = class _Embody {
|
|
|
8289
7952
|
getActiveVisemeJawAmount() {
|
|
8290
7953
|
let jawAmount = 0;
|
|
8291
7954
|
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
8292
|
-
const value =
|
|
7955
|
+
const value = clamp012(this.visemeValues[index] ?? 0);
|
|
8293
7956
|
if (value <= 1e-6) continue;
|
|
8294
7957
|
const jawScale = this.visemeJawScales[index] ?? 1;
|
|
8295
7958
|
if (Math.abs(jawScale) <= 1e-6) continue;
|
|
@@ -8572,7 +8235,7 @@ var _Embody = class _Embody {
|
|
|
8572
8235
|
return !!(hasMorphs && this.config.auToBones[id]?.length);
|
|
8573
8236
|
}
|
|
8574
8237
|
getEffectiveBoneAUValue(auId, nodeKey) {
|
|
8575
|
-
const rawValue =
|
|
8238
|
+
const rawValue = clamp012(this.auValues[auId] ?? 0);
|
|
8576
8239
|
if (rawValue <= 1e-6) return 0;
|
|
8577
8240
|
const binding = this.config.auToBones[auId]?.find((candidate) => candidate.node === nodeKey) ?? null;
|
|
8578
8241
|
if (!binding?.side) return rawValue;
|
|
@@ -8672,7 +8335,7 @@ var _Embody = class _Embody {
|
|
|
8672
8335
|
if (config.yaw && rotState.yaw !== 0) {
|
|
8673
8336
|
const binding = this.getCompositeAxisBindingForNode(nodeKey, config.yaw, rotState.yaw);
|
|
8674
8337
|
if (binding?.maxDegrees && binding.channel) {
|
|
8675
|
-
const radians =
|
|
8338
|
+
const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.yaw) * binding.scale;
|
|
8676
8339
|
const axis = getAxis(binding.channel);
|
|
8677
8340
|
const deltaQ = new Quaternion().setFromAxisAngle(axis, radians);
|
|
8678
8341
|
compositeQ.multiply(deltaQ);
|
|
@@ -8681,7 +8344,7 @@ var _Embody = class _Embody {
|
|
|
8681
8344
|
if (config.pitch && rotState.pitch !== 0) {
|
|
8682
8345
|
const binding = this.getCompositeAxisBindingForNode(nodeKey, config.pitch, rotState.pitch);
|
|
8683
8346
|
if (binding?.maxDegrees && binding.channel) {
|
|
8684
|
-
const radians =
|
|
8347
|
+
const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.pitch) * binding.scale;
|
|
8685
8348
|
const axis = getAxis(binding.channel);
|
|
8686
8349
|
const deltaQ = new Quaternion().setFromAxisAngle(axis, radians);
|
|
8687
8350
|
compositeQ.multiply(deltaQ);
|
|
@@ -8690,7 +8353,7 @@ var _Embody = class _Embody {
|
|
|
8690
8353
|
if (config.roll && rotState.roll !== 0) {
|
|
8691
8354
|
const binding = this.getCompositeAxisBindingForNode(nodeKey, config.roll, rotState.roll);
|
|
8692
8355
|
if (binding?.maxDegrees && binding.channel) {
|
|
8693
|
-
const radians =
|
|
8356
|
+
const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.roll) * binding.scale;
|
|
8694
8357
|
const axis = getAxis(binding.channel);
|
|
8695
8358
|
const deltaQ = new Quaternion().setFromAxisAngle(axis, radians);
|
|
8696
8359
|
compositeQ.multiply(deltaQ);
|
|
@@ -9529,11 +9192,11 @@ function applyCharacterModelTransform(model, transform = {}) {
|
|
|
9529
9192
|
model.position.set(modelOffset.x ?? 0, modelOffset.y ?? 0, modelOffset.z ?? 0);
|
|
9530
9193
|
}
|
|
9531
9194
|
if (modelRotation) {
|
|
9532
|
-
const
|
|
9195
|
+
const deg2rad2 = Math.PI / 180;
|
|
9533
9196
|
model.rotation.set(
|
|
9534
|
-
(modelRotation.x ?? 0) *
|
|
9535
|
-
(modelRotation.y ?? 0) *
|
|
9536
|
-
(modelRotation.z ?? 0) *
|
|
9197
|
+
(modelRotation.x ?? 0) * deg2rad2,
|
|
9198
|
+
(modelRotation.y ?? 0) * deg2rad2,
|
|
9199
|
+
(modelRotation.z ?? 0) * deg2rad2
|
|
9537
9200
|
);
|
|
9538
9201
|
}
|
|
9539
9202
|
if (modelScale !== void 0 && Number.isFinite(modelScale) && modelScale > 0) {
|