@lovelace_lol/embody 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/index.js +73 -14
- package/dist/index.js.map +1 -1
- package/dist/three/annotations/DPthree3DMarkers.d.ts +2 -1
- package/dist/three/clipBindings.d.ts +5 -0
- package/dist/three.js +73 -14
- package/dist/three.js.map +1 -1
- package/dist/wasm/README.md +5 -0
- package/dist/wasm/embody_wasm.d.ts +46 -46
- package/dist/wasm/embody_wasm_bg.wasm +0 -0
- package/dist/wasm/embody_wasm_bg.wasm.d.ts +46 -46
- package/package.json +4 -4
|
@@ -72,6 +72,7 @@ export declare class DPthree3DMarkers {
|
|
|
72
72
|
private zoomThreshold;
|
|
73
73
|
private autoCollapseThreshold;
|
|
74
74
|
private lastCameraDistance;
|
|
75
|
+
private markerLayoutDirty;
|
|
75
76
|
private visibilityAnimationActive;
|
|
76
77
|
private readonly visibilityAnimationScheduler;
|
|
77
78
|
private visibilityAnimationStates;
|
|
@@ -247,7 +248,7 @@ export declare class DPthree3DMarkers {
|
|
|
247
248
|
private refreshLaterality;
|
|
248
249
|
/**
|
|
249
250
|
* Apply zoom-based scaling to all markers, lines, and labels.
|
|
250
|
-
*
|
|
251
|
+
* Runs on zoom threshold crossings and when marker geometry changes.
|
|
251
252
|
*/
|
|
252
253
|
private applyZoomScale;
|
|
253
254
|
setVisible(visible: boolean): void;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { AnimationClip } from 'three';
|
|
2
2
|
import type { ClipIRTrack, ThreeModelInspection } from './index';
|
|
3
|
+
/**
|
|
4
|
+
* Packed morphs must share PropertyMixers with indexed compiled tracks. Euler
|
|
5
|
+
* components avoid Euler.fromArray's incompatible mixer-buffer offset/order.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withComponentTracks(clip: AnimationClip): AnimationClip;
|
|
3
8
|
/** One PropertyMixer cache key for every concrete renderer destination. */
|
|
4
9
|
export declare function concreteTrackName(target: ClipIRTrack['target'] | undefined, inspection: ThreeModelInspection): string | undefined;
|
|
5
10
|
/** Retain authored samples and interpolants; normalize only cloned binding names. */
|
package/dist/three.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as THREE3 from 'three';
|
|
2
|
-
import { NoBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NormalBlending, Vector3, Box3, Timer, Matrix4, AnimationMixer, AdditiveAnimationBlendMode, NormalAnimationBlendMode, LoopOnce, LoopRepeat, LoopPingPong, PropertyBinding, Quaternion, AnimationUtils, InterpolateDiscrete, InterpolateLinear, MathUtils, Mesh, PlaneGeometry, ShadowMaterial, HemisphereLight, DirectionalLight, SRGBColorSpace, ACESFilmicToneMapping, WebGLRenderer, Scene, Color, PerspectiveCamera, PCFShadowMap, PCFSoftShadowMap, NumberKeyframeTrack, QuaternionKeyframeTrack, VectorKeyframeTrack, AnimationClip, PMREMGenerator, FloatType, Float32BufferAttribute,
|
|
2
|
+
import { NoBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NormalBlending, Vector3, Box3, Timer, Matrix4, AnimationMixer, AdditiveAnimationBlendMode, NormalAnimationBlendMode, LoopOnce, LoopRepeat, LoopPingPong, PropertyBinding, Euler, Quaternion, AnimationUtils, InterpolateDiscrete, InterpolateLinear, MathUtils, Mesh, PlaneGeometry, ShadowMaterial, HemisphereLight, DirectionalLight, SRGBColorSpace, ACESFilmicToneMapping, WebGLRenderer, Scene, Color, PerspectiveCamera, PCFShadowMap, PCFSoftShadowMap, NumberKeyframeTrack, QuaternionKeyframeTrack, VectorKeyframeTrack, AnimationClip, PMREMGenerator, FloatType, Float32BufferAttribute, BufferAttribute, WebGPUCoordinateSystem, WebGLCoordinateSystem } from 'three';
|
|
3
3
|
import { CC4_HUMANOID_SKELETON_TEMPLATE, HUMANOID_SKELETON_TEMPLATES, DEFAULT_HAIR_COLOR_APPEARANCE, initEmbodyCore, requireInitializedEmbodyCore } from '@lovelace_lol/embody/wasm';
|
|
4
4
|
export { CC4_HUMANOID_SKELETON_TEMPLATE, DEFAULT_AXIS_TO_CHANNEL, DEFAULT_BONE_MAX_DEGREES, DEFAULT_HAIR_COLOR_APPEARANCE, HAIR_COLOR_PRESETS, HUMANOID_SKELETON_TEMPLATES, JOINT_CONTROL_SECTION, VISEME_KEYS } from '@lovelace_lol/embody/wasm';
|
|
5
5
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
@@ -1206,6 +1206,7 @@ var DPthree3DMarkers = class {
|
|
|
1206
1206
|
// Calculated from model size - collapse expanded regions when zoomed out
|
|
1207
1207
|
__publicField(this, "lastCameraDistance", -1);
|
|
1208
1208
|
// Cache to avoid unnecessary checks
|
|
1209
|
+
__publicField(this, "markerLayoutDirty", false);
|
|
1209
1210
|
__publicField(this, "visibilityAnimationActive", false);
|
|
1210
1211
|
__publicField(this, "visibilityAnimationScheduler", new AnimationFrameTaskScheduler());
|
|
1211
1212
|
__publicField(this, "visibilityAnimationStates", []);
|
|
@@ -1369,6 +1370,8 @@ var DPthree3DMarkers = class {
|
|
|
1369
1370
|
return;
|
|
1370
1371
|
}
|
|
1371
1372
|
this.separateOverlappingMarkers(annotationCameraCore);
|
|
1373
|
+
this.markerLayoutDirty = true;
|
|
1374
|
+
this.update();
|
|
1372
1375
|
console.log(`[3DMarkers] loadRegions complete. Created markers for:`, Array.from(this.markerMeshes.keys()));
|
|
1373
1376
|
console.log(`[3DMarkers] lineEndpoints:`, Array.from(this.lineEndpoints.keys()));
|
|
1374
1377
|
console.log(`[3DMarkers] resolvedStyles:`, Array.from(this.resolvedStyles.keys()));
|
|
@@ -1840,6 +1843,12 @@ var DPthree3DMarkers = class {
|
|
|
1840
1843
|
this.markerTargetVisibility.set(region.name, !startHidden && !region.isFallback);
|
|
1841
1844
|
this.markerAngleGateVisibility.delete(region.name);
|
|
1842
1845
|
this.markerViewportVisibility.set(region.name, true);
|
|
1846
|
+
this.markerLayoutDirty = true;
|
|
1847
|
+
if (this.visibilityAnimationActive) {
|
|
1848
|
+
this.setMarkerObjectsVisible(region.name, false, false);
|
|
1849
|
+
} else {
|
|
1850
|
+
this.update();
|
|
1851
|
+
}
|
|
1843
1852
|
}
|
|
1844
1853
|
/**
|
|
1845
1854
|
* Embody Rust owns endpoint layout; the Three adapter applies the returned positions
|
|
@@ -2455,7 +2464,7 @@ var DPthree3DMarkers = class {
|
|
|
2455
2464
|
const dy = this.camera.position.y - this.modelCenter.y;
|
|
2456
2465
|
const dz = this.camera.position.z - this.modelCenter.z;
|
|
2457
2466
|
const cameraDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
2458
|
-
const distanceChanged = this.lastCameraDistance < 0 || Math.abs(cameraDistance - this.lastCameraDistance) > this.lastCameraDistance * 0.02;
|
|
2467
|
+
const distanceChanged = this.markerLayoutDirty || this.lastCameraDistance < 0 || Math.abs(cameraDistance - this.lastCameraDistance) > this.lastCameraDistance * 0.02;
|
|
2459
2468
|
if (distanceChanged) {
|
|
2460
2469
|
const previousDistance = this.lastCameraDistance;
|
|
2461
2470
|
this.lastCameraDistance = cameraDistance;
|
|
@@ -2466,22 +2475,27 @@ var DPthree3DMarkers = class {
|
|
|
2466
2475
|
sphere.scale.setScalar(clampedScale);
|
|
2467
2476
|
}
|
|
2468
2477
|
const shouldBeZoomedIn = cameraDistance < this.zoomThreshold;
|
|
2469
|
-
|
|
2478
|
+
const zoomChanged = shouldBeZoomedIn !== this.isZoomedIn;
|
|
2479
|
+
if (this.markerLayoutDirty || zoomChanged) {
|
|
2470
2480
|
this.isZoomedIn = shouldBeZoomedIn;
|
|
2471
|
-
this.applyZoomScale(
|
|
2481
|
+
this.applyZoomScale(
|
|
2482
|
+
shouldBeZoomedIn ? ZOOMED_IN_LINE_SCALE : 1,
|
|
2483
|
+
zoomChanged || shouldBeZoomedIn
|
|
2484
|
+
);
|
|
2472
2485
|
}
|
|
2473
2486
|
const crossedAutoCollapseThreshold = previousDistance >= 0 && previousDistance <= this.autoCollapseThreshold && cameraDistance > this.autoCollapseThreshold;
|
|
2474
2487
|
if (crossedAutoCollapseThreshold) {
|
|
2475
2488
|
this.collapseAllRegions();
|
|
2476
2489
|
}
|
|
2477
2490
|
}
|
|
2478
|
-
const cameraMoved = !this.hasCameraAngleUpdateSnapshot || this.lastCameraAngleUpdatePosition.distanceToSquared(this.camera.position) > 1e-4 || 1 - Math.abs(this.lastCameraAngleUpdateQuaternion.dot(this.camera.quaternion)) > 1e-5;
|
|
2491
|
+
const cameraMoved = this.markerLayoutDirty || !this.hasCameraAngleUpdateSnapshot || this.lastCameraAngleUpdatePosition.distanceToSquared(this.camera.position) > 1e-4 || 1 - Math.abs(this.lastCameraAngleUpdateQuaternion.dot(this.camera.quaternion)) > 1e-5;
|
|
2479
2492
|
if (!cameraMoved) {
|
|
2480
2493
|
return;
|
|
2481
2494
|
}
|
|
2482
2495
|
this.lastCameraAngleUpdatePosition.copy(this.camera.position);
|
|
2483
2496
|
this.lastCameraAngleUpdateQuaternion.copy(this.camera.quaternion);
|
|
2484
2497
|
this.hasCameraAngleUpdateSnapshot = true;
|
|
2498
|
+
this.markerLayoutDirty = false;
|
|
2485
2499
|
const cameraAngle = getModelLocalOrbitAngle(this.model, this.modelCenter, this.camera.position);
|
|
2486
2500
|
for (const [name, sphere] of this.markerMeshes) {
|
|
2487
2501
|
const markerAngle = sphere.userData.cameraAngle;
|
|
@@ -2519,14 +2533,16 @@ var DPthree3DMarkers = class {
|
|
|
2519
2533
|
}
|
|
2520
2534
|
/**
|
|
2521
2535
|
* Apply zoom-based scaling to all markers, lines, and labels.
|
|
2522
|
-
*
|
|
2536
|
+
* Runs on zoom threshold crossings and when marker geometry changes.
|
|
2523
2537
|
*/
|
|
2524
|
-
applyZoomScale(scaleFactor) {
|
|
2538
|
+
applyZoomScale(scaleFactor, scaleSpheres = true) {
|
|
2525
2539
|
const isZoomed = scaleFactor < 1;
|
|
2526
2540
|
const sphereScaleFactor = isZoomed ? ZOOMED_IN_SPHERE_SCALE : 1;
|
|
2527
2541
|
const labelScaleFactor = isZoomed ? ZOOMED_IN_LABEL_SCALE : 1;
|
|
2528
|
-
|
|
2529
|
-
sphere.
|
|
2542
|
+
if (scaleSpheres) {
|
|
2543
|
+
for (const sphere of this.markerMeshes.values()) {
|
|
2544
|
+
sphere.scale.setScalar(sphereScaleFactor);
|
|
2545
|
+
}
|
|
2530
2546
|
}
|
|
2531
2547
|
for (const line of this.lineMeshes.values()) {
|
|
2532
2548
|
const material = line.material;
|
|
@@ -2630,6 +2646,10 @@ var DPthree3DMarkers = class {
|
|
|
2630
2646
|
object.visible = true;
|
|
2631
2647
|
this.setObjectOpacity(object, opacity * itemOpacityFactor);
|
|
2632
2648
|
}
|
|
2649
|
+
const name = state.label?.object.userData.annotation;
|
|
2650
|
+
if (typeof name === "string" && !this.updateMarkerLineLayout(name)) {
|
|
2651
|
+
this.setMarkerObjectsVisible(name, false, false);
|
|
2652
|
+
}
|
|
2633
2653
|
}
|
|
2634
2654
|
}
|
|
2635
2655
|
restoreVisibilityAnimationStates(states) {
|
|
@@ -2815,6 +2835,8 @@ var DPthree3DMarkers = class {
|
|
|
2815
2835
|
this.stopVisibilityAnimation(true);
|
|
2816
2836
|
if (visible) {
|
|
2817
2837
|
this.markerGroup.visible = true;
|
|
2838
|
+
this.markerLayoutDirty = true;
|
|
2839
|
+
this.update();
|
|
2818
2840
|
}
|
|
2819
2841
|
this.visibilityAnimationStates = this.captureVisibilityAnimationStates();
|
|
2820
2842
|
if (this.visibilityAnimationStates.length === 0) {
|
|
@@ -2877,6 +2899,7 @@ var DPthree3DMarkers = class {
|
|
|
2877
2899
|
this.markerAngleGateVisibility.clear();
|
|
2878
2900
|
this.markerViewportVisibility.clear();
|
|
2879
2901
|
this.soloedMarker = null;
|
|
2902
|
+
this.markerLayoutDirty = false;
|
|
2880
2903
|
this.hasCameraAngleUpdateSnapshot = false;
|
|
2881
2904
|
}
|
|
2882
2905
|
// ============ SOLO MODE ============
|
|
@@ -6732,8 +6755,30 @@ function bindModelReferencePose(model, referencePose) {
|
|
|
6732
6755
|
if (index !== referencePose.nodes.length) throw new Error("Incompatible reference pose hierarchy: extra captured nodes");
|
|
6733
6756
|
return bindings2;
|
|
6734
6757
|
}
|
|
6735
|
-
|
|
6736
|
-
|
|
6758
|
+
function withComponentTracks(clip) {
|
|
6759
|
+
let expanded = false;
|
|
6760
|
+
const tracks = clip.tracks.flatMap((track) => {
|
|
6761
|
+
const parsed = PropertyBinding.parseTrackName(track.name);
|
|
6762
|
+
const euler = parsed.propertyName === "rotation";
|
|
6763
|
+
if (!euler && !["morphTargetInfluences", "weights"].includes(parsed.propertyName) || parsed.propertyIndex !== void 0) return [track];
|
|
6764
|
+
const cubic = track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true;
|
|
6765
|
+
const parts = cubic ? 3 : 1;
|
|
6766
|
+
const width = track.getValueSize() / parts;
|
|
6767
|
+
if (!Number.isInteger(width) || width < 1 || euler && width !== 3) throw new Error(`Invalid component track ${JSON.stringify(track.name)}.`);
|
|
6768
|
+
expanded = true;
|
|
6769
|
+
return Array.from({ length: width }, (_, component) => {
|
|
6770
|
+
const scalar = track.clone();
|
|
6771
|
+
scalar.name = `${track.name}[${euler ? ["x", "y", "z"][component] : component}]`;
|
|
6772
|
+
scalar.values = track.values.slice(0, track.times.length * parts);
|
|
6773
|
+
for (let sample = 0; sample < scalar.values.length; sample += 1) scalar.values[sample] = track.values[sample * width + component];
|
|
6774
|
+
return scalar;
|
|
6775
|
+
});
|
|
6776
|
+
});
|
|
6777
|
+
if (!expanded) return clip;
|
|
6778
|
+
const indexed = clip.clone();
|
|
6779
|
+
indexed.tracks = tracks;
|
|
6780
|
+
return indexed;
|
|
6781
|
+
}
|
|
6737
6782
|
function concreteTrackName(target, inspection) {
|
|
6738
6783
|
if (!target) return void 0;
|
|
6739
6784
|
if (target.kind === "morphTarget") {
|
|
@@ -6745,6 +6790,7 @@ function concreteTrackName(target, inspection) {
|
|
|
6745
6790
|
const property = target.property ?? "rotation";
|
|
6746
6791
|
if (!object) return void 0;
|
|
6747
6792
|
if (property === "rotation" || property === "quaternion") return `${object.uuid}.quaternion`;
|
|
6793
|
+
if (property === "eulerX" || property === "eulerY" || property === "eulerZ") return `${object.uuid}.rotation[${property.slice(-1).toLowerCase()}]`;
|
|
6748
6794
|
if (property === "position" || property === "scale") return `${object.uuid}.${property}`;
|
|
6749
6795
|
}
|
|
6750
6796
|
if (target.kind === "meshVisibility") {
|
|
@@ -6855,6 +6901,7 @@ var ThreeNativePlaybackConnector = class {
|
|
|
6855
6901
|
*/
|
|
6856
6902
|
playNativeClip(clip, options = {}) {
|
|
6857
6903
|
this.beforeOperation();
|
|
6904
|
+
clip = withComponentTracks(clip);
|
|
6858
6905
|
const [ir] = serializeAnimationClips(this.binding.model, [clip], this.binding.inspection);
|
|
6859
6906
|
if (ir.tracks.length !== clip.tracks.length) {
|
|
6860
6907
|
throw new Error("Every native playback track must serialize against the bound model.");
|
|
@@ -6902,6 +6949,7 @@ var ThreeNativePlaybackConnector = class {
|
|
|
6902
6949
|
/** Edit authored samples, retaining the native action and its loop/fade progress. */
|
|
6903
6950
|
replaceClip(handle, clip) {
|
|
6904
6951
|
this.beforeOperation();
|
|
6952
|
+
clip = withComponentTracks(clip);
|
|
6905
6953
|
const resource = this.resources.get(handle);
|
|
6906
6954
|
if (!resource) throw new Error("Unknown, foreign or stale playback handle.");
|
|
6907
6955
|
const [ir] = serializeAnimationClips(this.binding.model, [clip], this.binding.inspection);
|
|
@@ -7410,10 +7458,17 @@ var Embody = class _Embody {
|
|
|
7410
7458
|
switch (parsed.propertyName) {
|
|
7411
7459
|
case "quaternion":
|
|
7412
7460
|
return Object.values(reference.transform.rotation);
|
|
7461
|
+
case "rotation": {
|
|
7462
|
+
const rotation = reference.transform.rotation;
|
|
7463
|
+
const euler = reference.rotationEuler ?? new Euler().setFromQuaternion(new Quaternion(rotation.x, rotation.y, rotation.z, rotation.w), reference.rotationOrder);
|
|
7464
|
+
const axis = parsed.propertyIndex;
|
|
7465
|
+
return axis === "x" || axis === "y" || axis === "z" ? [euler[axis]] : [euler.x, euler.y, euler.z];
|
|
7466
|
+
}
|
|
7413
7467
|
case "position":
|
|
7414
7468
|
return Object.values(reference.transform.position);
|
|
7415
7469
|
case "scale":
|
|
7416
7470
|
return Object.values(reference.transform.scale);
|
|
7471
|
+
case "weights":
|
|
7417
7472
|
case "morphTargetInfluences": {
|
|
7418
7473
|
const mesh = object;
|
|
7419
7474
|
const index = typeof parsed.propertyIndex === "string" && !/^\d+$/.test(parsed.propertyIndex) ? mesh.morphTargetDictionary?.[parsed.propertyIndex] : Number(parsed.propertyIndex);
|
|
@@ -7430,7 +7485,7 @@ var Embody = class _Embody {
|
|
|
7430
7485
|
if (!nativeSource) {
|
|
7431
7486
|
ir = this.compile(source, options);
|
|
7432
7487
|
}
|
|
7433
|
-
const clip = nativeSource ? source.clip.clone() : createAnimationClipFromClipIR(ir, this.inspection);
|
|
7488
|
+
const clip = nativeSource ? withComponentTracks(source.clip).clone() : createAnimationClipFromClipIR(ir, this.inspection);
|
|
7434
7489
|
if (ir) {
|
|
7435
7490
|
const predecessor = this.current.get(source.name);
|
|
7436
7491
|
const outgoing = predecessor?.connector.getAction(predecessor.id);
|
|
@@ -9589,6 +9644,10 @@ function createAnimationClipFromClipIR(clip, inspection) {
|
|
|
9589
9644
|
if (property === "rotation" || property === "quaternion") {
|
|
9590
9645
|
if (track.inheritStart) object.quaternion.toArray(values, 0);
|
|
9591
9646
|
tracks.push(new QuaternionKeyframeTrack(trackName, times, values, interpolation));
|
|
9647
|
+
} else if (property === "eulerX" || property === "eulerY" || property === "eulerZ") {
|
|
9648
|
+
const axis = property.slice(-1).toLowerCase();
|
|
9649
|
+
if (track.inheritStart) values[0] = object.rotation[axis];
|
|
9650
|
+
tracks.push(new NumberKeyframeTrack(trackName, times, values, interpolation));
|
|
9592
9651
|
} else if (property === "position") {
|
|
9593
9652
|
if (track.inheritStart) object.position.toArray(values, 0);
|
|
9594
9653
|
tracks.push(new VectorKeyframeTrack(trackName, times, values, interpolation));
|
|
@@ -9622,7 +9681,7 @@ function serializeAnimationClips(model, clips, inspection) {
|
|
|
9622
9681
|
channelKinds.set(kind, id);
|
|
9623
9682
|
return id;
|
|
9624
9683
|
};
|
|
9625
|
-
clip.tracks.forEach((track, index) => {
|
|
9684
|
+
withComponentTracks(clip).tracks.forEach((track, index) => {
|
|
9626
9685
|
const found = findTrackTarget(model, track);
|
|
9627
9686
|
if (!found?.object) return;
|
|
9628
9687
|
const { parsed, object } = found;
|
|
@@ -9637,7 +9696,7 @@ function serializeAnimationClips(model, clips, inspection) {
|
|
|
9637
9696
|
} else if (property === "visible" && object.isMesh) {
|
|
9638
9697
|
target = { kind: "meshVisibility", meshId: meshIds.get(object) };
|
|
9639
9698
|
} else {
|
|
9640
|
-
const transformProperty = property === "
|
|
9699
|
+
const transformProperty = property === "rotation" && ["x", "y", "z"].includes(String(parsed.propertyIndex)) ? `euler${String(parsed.propertyIndex).toUpperCase()}` : property === "quaternion" ? "rotation" : property === "position" ? "position" : property === "scale" ? "scale" : null;
|
|
9641
9700
|
if (transformProperty) {
|
|
9642
9701
|
const boneId = boneIds.get(object);
|
|
9643
9702
|
target = boneId ? { kind: "boneTransform", boneId, property: transformProperty } : { kind: "objectTransform", objectId: objectIds.get(object), property: transformProperty };
|