@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
package/README.md
CHANGED
|
@@ -87,6 +87,11 @@ spent. Each native mesh raycast is indivisible; a large mesh can still cause a
|
|
|
87
87
|
frame drop, and slicing does not reduce total raycast CPU time. Interactive
|
|
88
88
|
annotation changes retain synchronous queries so a marker samples one pose.
|
|
89
89
|
Replacement, clearing and disposal cancel stale loads before scene commits.
|
|
90
|
+
New 3D markers and separated endpoints invalidate the cached camera layout.
|
|
91
|
+
Visible markers receive the current zoom sizing and Rust viewport constraints
|
|
92
|
+
before paint; hidden markers receive that layout before reveal captures label
|
|
93
|
+
scales. Viewport clipping also follows the animated label size during reveal
|
|
94
|
+
overshoot. Markers added during a reveal stay hidden until its layout pass resumes.
|
|
90
95
|
|
|
91
96
|
For first reveal, call `controller.prepareModelForRender(model, { signal })`
|
|
92
97
|
from the existing `beforeReveal` callback (or directly before attaching the
|
package/dist/index.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 * from '@lovelace_lol/embody/wasm';
|
|
5
5
|
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';
|
|
@@ -1207,6 +1207,7 @@ var DPthree3DMarkers = class {
|
|
|
1207
1207
|
// Calculated from model size - collapse expanded regions when zoomed out
|
|
1208
1208
|
__publicField(this, "lastCameraDistance", -1);
|
|
1209
1209
|
// Cache to avoid unnecessary checks
|
|
1210
|
+
__publicField(this, "markerLayoutDirty", false);
|
|
1210
1211
|
__publicField(this, "visibilityAnimationActive", false);
|
|
1211
1212
|
__publicField(this, "visibilityAnimationScheduler", new AnimationFrameTaskScheduler());
|
|
1212
1213
|
__publicField(this, "visibilityAnimationStates", []);
|
|
@@ -1370,6 +1371,8 @@ var DPthree3DMarkers = class {
|
|
|
1370
1371
|
return;
|
|
1371
1372
|
}
|
|
1372
1373
|
this.separateOverlappingMarkers(annotationCameraCore);
|
|
1374
|
+
this.markerLayoutDirty = true;
|
|
1375
|
+
this.update();
|
|
1373
1376
|
console.log(`[3DMarkers] loadRegions complete. Created markers for:`, Array.from(this.markerMeshes.keys()));
|
|
1374
1377
|
console.log(`[3DMarkers] lineEndpoints:`, Array.from(this.lineEndpoints.keys()));
|
|
1375
1378
|
console.log(`[3DMarkers] resolvedStyles:`, Array.from(this.resolvedStyles.keys()));
|
|
@@ -1841,6 +1844,12 @@ var DPthree3DMarkers = class {
|
|
|
1841
1844
|
this.markerTargetVisibility.set(region.name, !startHidden && !region.isFallback);
|
|
1842
1845
|
this.markerAngleGateVisibility.delete(region.name);
|
|
1843
1846
|
this.markerViewportVisibility.set(region.name, true);
|
|
1847
|
+
this.markerLayoutDirty = true;
|
|
1848
|
+
if (this.visibilityAnimationActive) {
|
|
1849
|
+
this.setMarkerObjectsVisible(region.name, false, false);
|
|
1850
|
+
} else {
|
|
1851
|
+
this.update();
|
|
1852
|
+
}
|
|
1844
1853
|
}
|
|
1845
1854
|
/**
|
|
1846
1855
|
* Embody Rust owns endpoint layout; the Three adapter applies the returned positions
|
|
@@ -2456,7 +2465,7 @@ var DPthree3DMarkers = class {
|
|
|
2456
2465
|
const dy = this.camera.position.y - this.modelCenter.y;
|
|
2457
2466
|
const dz = this.camera.position.z - this.modelCenter.z;
|
|
2458
2467
|
const cameraDistance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
2459
|
-
const distanceChanged = this.lastCameraDistance < 0 || Math.abs(cameraDistance - this.lastCameraDistance) > this.lastCameraDistance * 0.02;
|
|
2468
|
+
const distanceChanged = this.markerLayoutDirty || this.lastCameraDistance < 0 || Math.abs(cameraDistance - this.lastCameraDistance) > this.lastCameraDistance * 0.02;
|
|
2460
2469
|
if (distanceChanged) {
|
|
2461
2470
|
const previousDistance = this.lastCameraDistance;
|
|
2462
2471
|
this.lastCameraDistance = cameraDistance;
|
|
@@ -2467,22 +2476,27 @@ var DPthree3DMarkers = class {
|
|
|
2467
2476
|
sphere.scale.setScalar(clampedScale);
|
|
2468
2477
|
}
|
|
2469
2478
|
const shouldBeZoomedIn = cameraDistance < this.zoomThreshold;
|
|
2470
|
-
|
|
2479
|
+
const zoomChanged = shouldBeZoomedIn !== this.isZoomedIn;
|
|
2480
|
+
if (this.markerLayoutDirty || zoomChanged) {
|
|
2471
2481
|
this.isZoomedIn = shouldBeZoomedIn;
|
|
2472
|
-
this.applyZoomScale(
|
|
2482
|
+
this.applyZoomScale(
|
|
2483
|
+
shouldBeZoomedIn ? ZOOMED_IN_LINE_SCALE : 1,
|
|
2484
|
+
zoomChanged || shouldBeZoomedIn
|
|
2485
|
+
);
|
|
2473
2486
|
}
|
|
2474
2487
|
const crossedAutoCollapseThreshold = previousDistance >= 0 && previousDistance <= this.autoCollapseThreshold && cameraDistance > this.autoCollapseThreshold;
|
|
2475
2488
|
if (crossedAutoCollapseThreshold) {
|
|
2476
2489
|
this.collapseAllRegions();
|
|
2477
2490
|
}
|
|
2478
2491
|
}
|
|
2479
|
-
const cameraMoved = !this.hasCameraAngleUpdateSnapshot || this.lastCameraAngleUpdatePosition.distanceToSquared(this.camera.position) > 1e-4 || 1 - Math.abs(this.lastCameraAngleUpdateQuaternion.dot(this.camera.quaternion)) > 1e-5;
|
|
2492
|
+
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;
|
|
2480
2493
|
if (!cameraMoved) {
|
|
2481
2494
|
return;
|
|
2482
2495
|
}
|
|
2483
2496
|
this.lastCameraAngleUpdatePosition.copy(this.camera.position);
|
|
2484
2497
|
this.lastCameraAngleUpdateQuaternion.copy(this.camera.quaternion);
|
|
2485
2498
|
this.hasCameraAngleUpdateSnapshot = true;
|
|
2499
|
+
this.markerLayoutDirty = false;
|
|
2486
2500
|
const cameraAngle = getModelLocalOrbitAngle(this.model, this.modelCenter, this.camera.position);
|
|
2487
2501
|
for (const [name, sphere] of this.markerMeshes) {
|
|
2488
2502
|
const markerAngle = sphere.userData.cameraAngle;
|
|
@@ -2520,14 +2534,16 @@ var DPthree3DMarkers = class {
|
|
|
2520
2534
|
}
|
|
2521
2535
|
/**
|
|
2522
2536
|
* Apply zoom-based scaling to all markers, lines, and labels.
|
|
2523
|
-
*
|
|
2537
|
+
* Runs on zoom threshold crossings and when marker geometry changes.
|
|
2524
2538
|
*/
|
|
2525
|
-
applyZoomScale(scaleFactor) {
|
|
2539
|
+
applyZoomScale(scaleFactor, scaleSpheres = true) {
|
|
2526
2540
|
const isZoomed = scaleFactor < 1;
|
|
2527
2541
|
const sphereScaleFactor = isZoomed ? ZOOMED_IN_SPHERE_SCALE : 1;
|
|
2528
2542
|
const labelScaleFactor = isZoomed ? ZOOMED_IN_LABEL_SCALE : 1;
|
|
2529
|
-
|
|
2530
|
-
sphere.
|
|
2543
|
+
if (scaleSpheres) {
|
|
2544
|
+
for (const sphere of this.markerMeshes.values()) {
|
|
2545
|
+
sphere.scale.setScalar(sphereScaleFactor);
|
|
2546
|
+
}
|
|
2531
2547
|
}
|
|
2532
2548
|
for (const line of this.lineMeshes.values()) {
|
|
2533
2549
|
const material = line.material;
|
|
@@ -2631,6 +2647,10 @@ var DPthree3DMarkers = class {
|
|
|
2631
2647
|
object.visible = true;
|
|
2632
2648
|
this.setObjectOpacity(object, opacity * itemOpacityFactor);
|
|
2633
2649
|
}
|
|
2650
|
+
const name = state.label?.object.userData.annotation;
|
|
2651
|
+
if (typeof name === "string" && !this.updateMarkerLineLayout(name)) {
|
|
2652
|
+
this.setMarkerObjectsVisible(name, false, false);
|
|
2653
|
+
}
|
|
2634
2654
|
}
|
|
2635
2655
|
}
|
|
2636
2656
|
restoreVisibilityAnimationStates(states) {
|
|
@@ -2816,6 +2836,8 @@ var DPthree3DMarkers = class {
|
|
|
2816
2836
|
this.stopVisibilityAnimation(true);
|
|
2817
2837
|
if (visible) {
|
|
2818
2838
|
this.markerGroup.visible = true;
|
|
2839
|
+
this.markerLayoutDirty = true;
|
|
2840
|
+
this.update();
|
|
2819
2841
|
}
|
|
2820
2842
|
this.visibilityAnimationStates = this.captureVisibilityAnimationStates();
|
|
2821
2843
|
if (this.visibilityAnimationStates.length === 0) {
|
|
@@ -2878,6 +2900,7 @@ var DPthree3DMarkers = class {
|
|
|
2878
2900
|
this.markerAngleGateVisibility.clear();
|
|
2879
2901
|
this.markerViewportVisibility.clear();
|
|
2880
2902
|
this.soloedMarker = null;
|
|
2903
|
+
this.markerLayoutDirty = false;
|
|
2881
2904
|
this.hasCameraAngleUpdateSnapshot = false;
|
|
2882
2905
|
}
|
|
2883
2906
|
// ============ SOLO MODE ============
|
|
@@ -6733,8 +6756,30 @@ function bindModelReferencePose(model, referencePose) {
|
|
|
6733
6756
|
if (index !== referencePose.nodes.length) throw new Error("Incompatible reference pose hierarchy: extra captured nodes");
|
|
6734
6757
|
return bindings2;
|
|
6735
6758
|
}
|
|
6736
|
-
|
|
6737
|
-
|
|
6759
|
+
function withComponentTracks(clip) {
|
|
6760
|
+
let expanded = false;
|
|
6761
|
+
const tracks = clip.tracks.flatMap((track) => {
|
|
6762
|
+
const parsed = PropertyBinding.parseTrackName(track.name);
|
|
6763
|
+
const euler = parsed.propertyName === "rotation";
|
|
6764
|
+
if (!euler && !["morphTargetInfluences", "weights"].includes(parsed.propertyName) || parsed.propertyIndex !== void 0) return [track];
|
|
6765
|
+
const cubic = track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true;
|
|
6766
|
+
const parts = cubic ? 3 : 1;
|
|
6767
|
+
const width = track.getValueSize() / parts;
|
|
6768
|
+
if (!Number.isInteger(width) || width < 1 || euler && width !== 3) throw new Error(`Invalid component track ${JSON.stringify(track.name)}.`);
|
|
6769
|
+
expanded = true;
|
|
6770
|
+
return Array.from({ length: width }, (_, component) => {
|
|
6771
|
+
const scalar = track.clone();
|
|
6772
|
+
scalar.name = `${track.name}[${euler ? ["x", "y", "z"][component] : component}]`;
|
|
6773
|
+
scalar.values = track.values.slice(0, track.times.length * parts);
|
|
6774
|
+
for (let sample = 0; sample < scalar.values.length; sample += 1) scalar.values[sample] = track.values[sample * width + component];
|
|
6775
|
+
return scalar;
|
|
6776
|
+
});
|
|
6777
|
+
});
|
|
6778
|
+
if (!expanded) return clip;
|
|
6779
|
+
const indexed = clip.clone();
|
|
6780
|
+
indexed.tracks = tracks;
|
|
6781
|
+
return indexed;
|
|
6782
|
+
}
|
|
6738
6783
|
function concreteTrackName(target, inspection) {
|
|
6739
6784
|
if (!target) return void 0;
|
|
6740
6785
|
if (target.kind === "morphTarget") {
|
|
@@ -6746,6 +6791,7 @@ function concreteTrackName(target, inspection) {
|
|
|
6746
6791
|
const property = target.property ?? "rotation";
|
|
6747
6792
|
if (!object) return void 0;
|
|
6748
6793
|
if (property === "rotation" || property === "quaternion") return `${object.uuid}.quaternion`;
|
|
6794
|
+
if (property === "eulerX" || property === "eulerY" || property === "eulerZ") return `${object.uuid}.rotation[${property.slice(-1).toLowerCase()}]`;
|
|
6749
6795
|
if (property === "position" || property === "scale") return `${object.uuid}.${property}`;
|
|
6750
6796
|
}
|
|
6751
6797
|
if (target.kind === "meshVisibility") {
|
|
@@ -6856,6 +6902,7 @@ var ThreeNativePlaybackConnector = class {
|
|
|
6856
6902
|
*/
|
|
6857
6903
|
playNativeClip(clip, options = {}) {
|
|
6858
6904
|
this.beforeOperation();
|
|
6905
|
+
clip = withComponentTracks(clip);
|
|
6859
6906
|
const [ir] = serializeAnimationClips(this.binding.model, [clip], this.binding.inspection);
|
|
6860
6907
|
if (ir.tracks.length !== clip.tracks.length) {
|
|
6861
6908
|
throw new Error("Every native playback track must serialize against the bound model.");
|
|
@@ -6903,6 +6950,7 @@ var ThreeNativePlaybackConnector = class {
|
|
|
6903
6950
|
/** Edit authored samples, retaining the native action and its loop/fade progress. */
|
|
6904
6951
|
replaceClip(handle, clip) {
|
|
6905
6952
|
this.beforeOperation();
|
|
6953
|
+
clip = withComponentTracks(clip);
|
|
6906
6954
|
const resource = this.resources.get(handle);
|
|
6907
6955
|
if (!resource) throw new Error("Unknown, foreign or stale playback handle.");
|
|
6908
6956
|
const [ir] = serializeAnimationClips(this.binding.model, [clip], this.binding.inspection);
|
|
@@ -7411,10 +7459,17 @@ var Embody = class _Embody {
|
|
|
7411
7459
|
switch (parsed.propertyName) {
|
|
7412
7460
|
case "quaternion":
|
|
7413
7461
|
return Object.values(reference.transform.rotation);
|
|
7462
|
+
case "rotation": {
|
|
7463
|
+
const rotation = reference.transform.rotation;
|
|
7464
|
+
const euler = reference.rotationEuler ?? new Euler().setFromQuaternion(new Quaternion(rotation.x, rotation.y, rotation.z, rotation.w), reference.rotationOrder);
|
|
7465
|
+
const axis = parsed.propertyIndex;
|
|
7466
|
+
return axis === "x" || axis === "y" || axis === "z" ? [euler[axis]] : [euler.x, euler.y, euler.z];
|
|
7467
|
+
}
|
|
7414
7468
|
case "position":
|
|
7415
7469
|
return Object.values(reference.transform.position);
|
|
7416
7470
|
case "scale":
|
|
7417
7471
|
return Object.values(reference.transform.scale);
|
|
7472
|
+
case "weights":
|
|
7418
7473
|
case "morphTargetInfluences": {
|
|
7419
7474
|
const mesh = object;
|
|
7420
7475
|
const index = typeof parsed.propertyIndex === "string" && !/^\d+$/.test(parsed.propertyIndex) ? mesh.morphTargetDictionary?.[parsed.propertyIndex] : Number(parsed.propertyIndex);
|
|
@@ -7431,7 +7486,7 @@ var Embody = class _Embody {
|
|
|
7431
7486
|
if (!nativeSource) {
|
|
7432
7487
|
ir = this.compile(source, options);
|
|
7433
7488
|
}
|
|
7434
|
-
const clip = nativeSource ? source.clip.clone() : createAnimationClipFromClipIR(ir, this.inspection);
|
|
7489
|
+
const clip = nativeSource ? withComponentTracks(source.clip).clone() : createAnimationClipFromClipIR(ir, this.inspection);
|
|
7435
7490
|
if (ir) {
|
|
7436
7491
|
const predecessor = this.current.get(source.name);
|
|
7437
7492
|
const outgoing = predecessor?.connector.getAction(predecessor.id);
|
|
@@ -9590,6 +9645,10 @@ function createAnimationClipFromClipIR(clip, inspection) {
|
|
|
9590
9645
|
if (property === "rotation" || property === "quaternion") {
|
|
9591
9646
|
if (track.inheritStart) object.quaternion.toArray(values, 0);
|
|
9592
9647
|
tracks.push(new QuaternionKeyframeTrack(trackName, times, values, interpolation));
|
|
9648
|
+
} else if (property === "eulerX" || property === "eulerY" || property === "eulerZ") {
|
|
9649
|
+
const axis = property.slice(-1).toLowerCase();
|
|
9650
|
+
if (track.inheritStart) values[0] = object.rotation[axis];
|
|
9651
|
+
tracks.push(new NumberKeyframeTrack(trackName, times, values, interpolation));
|
|
9593
9652
|
} else if (property === "position") {
|
|
9594
9653
|
if (track.inheritStart) object.position.toArray(values, 0);
|
|
9595
9654
|
tracks.push(new VectorKeyframeTrack(trackName, times, values, interpolation));
|
|
@@ -9623,7 +9682,7 @@ function serializeAnimationClips(model, clips, inspection) {
|
|
|
9623
9682
|
channelKinds.set(kind, id);
|
|
9624
9683
|
return id;
|
|
9625
9684
|
};
|
|
9626
|
-
clip.tracks.forEach((track, index) => {
|
|
9685
|
+
withComponentTracks(clip).tracks.forEach((track, index) => {
|
|
9627
9686
|
const found = findTrackTarget(model, track);
|
|
9628
9687
|
if (!found?.object) return;
|
|
9629
9688
|
const { parsed, object } = found;
|
|
@@ -9638,7 +9697,7 @@ function serializeAnimationClips(model, clips, inspection) {
|
|
|
9638
9697
|
} else if (property === "visible" && object.isMesh) {
|
|
9639
9698
|
target = { kind: "meshVisibility", meshId: meshIds.get(object) };
|
|
9640
9699
|
} else {
|
|
9641
|
-
const transformProperty = property === "
|
|
9700
|
+
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;
|
|
9642
9701
|
if (transformProperty) {
|
|
9643
9702
|
const boneId = boneIds.get(object);
|
|
9644
9703
|
target = boneId ? { kind: "boneTransform", boneId, property: transformProperty } : { kind: "objectTransform", objectId: objectIds.get(object), property: transformProperty };
|