@lovelace_lol/embody 0.0.8 → 0.0.9
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.d.ts +1 -1
- package/dist/index.js +2049 -1466
- package/dist/index.js.map +1 -1
- package/dist/three/annotations/DPthreeCameraController.d.ts +1 -0
- package/dist/three/authoring.d.ts +95 -0
- package/dist/three/authoringTypes.d.ts +576 -0
- package/dist/three/character.d.ts +243 -0
- package/dist/three/characterHost.d.ts +48 -0
- package/dist/three/clipBindings.d.ts +6 -0
- package/dist/three/index.d.ts +28 -4
- package/dist/three/nativePlayback.d.ts +121 -0
- package/dist/three/playbackRendering.d.ts +14 -0
- package/dist/three.js +1982 -334
- package/dist/three.js.map +1 -1
- package/dist/wasm/compatibility.d.ts +14 -0
- package/dist/wasm/embody_wasm.d.ts +144 -199
- package/dist/wasm/embody_wasm.js +410 -576
- package/dist/wasm/embody_wasm_bg.wasm +0 -0
- package/dist/wasm/embody_wasm_bg.wasm.d.ts +71 -87
- package/dist/wasm/index.d.ts +7 -2
- package/dist/wasm.js +59 -2
- package/dist/wasm.js.map +1 -1
- package/package.json +5 -4
- package/dist/three/gaze.d.ts +0 -122
package/dist/three.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as THREE3 from 'three';
|
|
2
|
-
import { NoBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NormalBlending, Vector3, Box3, Matrix4, Quaternion, MathUtils, Mesh, PlaneGeometry, ShadowMaterial, HemisphereLight, DirectionalLight, SRGBColorSpace, ACESFilmicToneMapping, WebGLRenderer, Scene, Color, PerspectiveCamera, PCFShadowMap, PCFSoftShadowMap,
|
|
3
|
-
import { initEmbodyCore, requireInitializedEmbodyCore } from '@lovelace_lol/embody/wasm';
|
|
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, Euler, BufferAttribute, WebGPUCoordinateSystem, WebGLCoordinateSystem } from 'three';
|
|
3
|
+
import { CC4_HUMANOID_SKELETON_TEMPLATE, HUMANOID_SKELETON_TEMPLATES, DEFAULT_HAIR_COLOR_APPEARANCE, initEmbodyCore, requireInitializedEmbodyCore } from '@lovelace_lol/embody/wasm';
|
|
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';
|
|
4
5
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
5
6
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
6
7
|
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
|
|
@@ -449,6 +450,59 @@ function drawModelForRender(renderer, scene, camera, model) {
|
|
|
449
450
|
if (globalShadow && renderer.shadowMap.enabled) globalShadow.needsUpdate = true;
|
|
450
451
|
};
|
|
451
452
|
}
|
|
453
|
+
var bindings = /* @__PURE__ */ new WeakMap();
|
|
454
|
+
function bindPlaybackRendering(model, mixer, afterFrame) {
|
|
455
|
+
const binding = { mixer, enabled: true, afterFrame };
|
|
456
|
+
const entries = bindings.get(model) ?? /* @__PURE__ */ new Set();
|
|
457
|
+
entries.add(binding);
|
|
458
|
+
bindings.set(model, entries);
|
|
459
|
+
return {
|
|
460
|
+
setEnabled(enabled) {
|
|
461
|
+
binding.enabled = enabled;
|
|
462
|
+
},
|
|
463
|
+
dispose() {
|
|
464
|
+
entries.delete(binding);
|
|
465
|
+
if (!entries.size) bindings.delete(model);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function renderScenePlayback(scene, timer) {
|
|
470
|
+
const mixers = /* @__PURE__ */ new Set();
|
|
471
|
+
const active = [];
|
|
472
|
+
scene.traverse((object) => {
|
|
473
|
+
for (const binding of bindings.get(object) ?? []) {
|
|
474
|
+
if (binding.enabled) {
|
|
475
|
+
active.push(binding);
|
|
476
|
+
mixers.add(binding.mixer);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
for (const mixer of mixers) mixer.update(timer.getDelta());
|
|
481
|
+
for (const binding of active) binding.afterFrame();
|
|
482
|
+
}
|
|
483
|
+
function createScenePlaybackFrame(scene, document2) {
|
|
484
|
+
const timer = new Timer();
|
|
485
|
+
if (document2) timer.connect(document2);
|
|
486
|
+
let disposed = false;
|
|
487
|
+
let initialized = false;
|
|
488
|
+
return {
|
|
489
|
+
onFrame(timestamp) {
|
|
490
|
+
if (disposed) return;
|
|
491
|
+
timer.update(timestamp);
|
|
492
|
+
if (!initialized) {
|
|
493
|
+
initialized = true;
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
renderScenePlayback(scene, timer);
|
|
497
|
+
},
|
|
498
|
+
dispose() {
|
|
499
|
+
if (!disposed) {
|
|
500
|
+
disposed = true;
|
|
501
|
+
timer.dispose();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
452
506
|
|
|
453
507
|
// three/annotations/DOMControls.ts
|
|
454
508
|
var RUNTIME_ANNOTATION_REGION_PREFIX = "runtime:annotation:";
|
|
@@ -1111,8 +1165,8 @@ function getAnnotationRegionLabel(region) {
|
|
|
1111
1165
|
const trimmed = label.trim();
|
|
1112
1166
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
1113
1167
|
}
|
|
1114
|
-
function isFiniteClipPoint(
|
|
1115
|
-
return Number.isFinite(
|
|
1168
|
+
function isFiniteClipPoint(point2) {
|
|
1169
|
+
return Number.isFinite(point2.x) && Number.isFinite(point2.y) && Number.isFinite(point2.z) && Number.isFinite(point2.w);
|
|
1116
1170
|
}
|
|
1117
1171
|
function resolveViewportSafeBounds(params) {
|
|
1118
1172
|
return requireAnnotationCameraCore().resolveViewportSafeBounds(params);
|
|
@@ -3913,9 +3967,9 @@ function resolveRuntimeAnnotationAUBoneTargets(auId, candidates, targetSide) {
|
|
|
3913
3967
|
const boneTargets = [];
|
|
3914
3968
|
for (const candidate of candidates) {
|
|
3915
3969
|
if (!isPlainRecord(candidate.auToBones)) continue;
|
|
3916
|
-
const
|
|
3917
|
-
if (!Array.isArray(
|
|
3918
|
-
for (const binding of
|
|
3970
|
+
const bindings2 = candidate.auToBones[auId];
|
|
3971
|
+
if (!Array.isArray(bindings2)) continue;
|
|
3972
|
+
for (const binding of bindings2) {
|
|
3919
3973
|
const target = readRuntimeAnnotationBoneTarget(binding);
|
|
3920
3974
|
if (!target || seen.has(target.boneName)) continue;
|
|
3921
3975
|
seen.add(target.boneName);
|
|
@@ -4079,6 +4133,7 @@ var DPthreeCameraController = class {
|
|
|
4079
4133
|
__publicField(this, "domElement");
|
|
4080
4134
|
// Renderer and render loop (when controller manages the render loop)
|
|
4081
4135
|
__publicField(this, "renderer", null);
|
|
4136
|
+
__publicField(this, "playbackTimer", new THREE3.Timer());
|
|
4082
4137
|
__publicField(this, "renderFrame");
|
|
4083
4138
|
__publicField(this, "resizeRenderer");
|
|
4084
4139
|
__publicField(this, "onRenderError");
|
|
@@ -4189,15 +4244,25 @@ var DPthreeCameraController = class {
|
|
|
4189
4244
|
startRenderLoop(propagateFailure = false) {
|
|
4190
4245
|
if (!this.renderer) return;
|
|
4191
4246
|
const renderer = this.renderer;
|
|
4247
|
+
this.playbackTimer.disconnect();
|
|
4248
|
+
const ownerDocument = renderer.domElement?.ownerDocument;
|
|
4249
|
+
if (ownerDocument) this.playbackTimer.connect(ownerDocument);
|
|
4250
|
+
this.playbackTimer.reset();
|
|
4251
|
+
let playbackStarted = false;
|
|
4192
4252
|
try {
|
|
4193
|
-
const started = renderer.setAnimationLoop(() => {
|
|
4194
|
-
if (this.disposed ||
|
|
4253
|
+
const started = renderer.setAnimationLoop((timestamp) => {
|
|
4254
|
+
if (this.disposed || renderer !== this.renderer) return;
|
|
4255
|
+
this.playbackTimer.update(timestamp);
|
|
4256
|
+
const samplePlayback = playbackStarted;
|
|
4257
|
+
playbackStarted = true;
|
|
4258
|
+
if (this.renderFailed || this.renderingSuspended || this.framePending) return;
|
|
4195
4259
|
try {
|
|
4196
4260
|
const preparationDraw = this.modelPreparation?.draw;
|
|
4197
4261
|
if (preparationDraw) {
|
|
4198
4262
|
preparationDraw();
|
|
4199
4263
|
return;
|
|
4200
4264
|
}
|
|
4265
|
+
if (samplePlayback) renderScenePlayback(this.scene, this.playbackTimer);
|
|
4201
4266
|
this.update();
|
|
4202
4267
|
const frame = this.renderFrame ? this.renderFrame(this.renderer, this.scene, this.camera) : this.renderer.render(this.scene, this.camera);
|
|
4203
4268
|
if (frame) {
|
|
@@ -4246,6 +4311,7 @@ var DPthreeCameraController = class {
|
|
|
4246
4311
|
* Stop the render loop
|
|
4247
4312
|
*/
|
|
4248
4313
|
stopRenderLoop() {
|
|
4314
|
+
this.playbackTimer.reset();
|
|
4249
4315
|
try {
|
|
4250
4316
|
const stopped = this.renderer?.setAnimationLoop(null);
|
|
4251
4317
|
if (stopped) void stopped.catch((error) => console.error("Character camera render loop stop failed.", error));
|
|
@@ -4331,18 +4397,18 @@ var DPthreeCameraController = class {
|
|
|
4331
4397
|
resolve = yes;
|
|
4332
4398
|
reject = no;
|
|
4333
4399
|
});
|
|
4334
|
-
const
|
|
4400
|
+
const request3 = {
|
|
4335
4401
|
promise,
|
|
4336
4402
|
draw: null,
|
|
4337
4403
|
cancel: (reason) => {
|
|
4338
4404
|
cancelled ?? (cancelled = reason);
|
|
4339
4405
|
invalidatePreparedShadows?.();
|
|
4340
|
-
|
|
4406
|
+
request3.draw = null;
|
|
4341
4407
|
rejectDraw?.(cancelled);
|
|
4342
4408
|
}
|
|
4343
4409
|
};
|
|
4344
|
-
this.modelPreparation =
|
|
4345
|
-
const abort = () =>
|
|
4410
|
+
this.modelPreparation = request3;
|
|
4411
|
+
const abort = () => request3.cancel(renderPreparationAborted());
|
|
4346
4412
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
4347
4413
|
const checkCancelled = () => {
|
|
4348
4414
|
if (cancelled) throw cancelled;
|
|
@@ -4361,8 +4427,8 @@ var DPthreeCameraController = class {
|
|
|
4361
4427
|
checkCancelled();
|
|
4362
4428
|
await new Promise((done, failed) => {
|
|
4363
4429
|
rejectDraw = failed;
|
|
4364
|
-
|
|
4365
|
-
|
|
4430
|
+
request3.draw = () => {
|
|
4431
|
+
request3.draw = null;
|
|
4366
4432
|
try {
|
|
4367
4433
|
checkCancelled();
|
|
4368
4434
|
invalidatePreparedShadows = drawModelForRender(renderer, this.scene, this.camera, model);
|
|
@@ -4377,7 +4443,7 @@ var DPthreeCameraController = class {
|
|
|
4377
4443
|
checkCancelled();
|
|
4378
4444
|
} finally {
|
|
4379
4445
|
options.signal?.removeEventListener("abort", abort);
|
|
4380
|
-
if (this.modelPreparation ===
|
|
4446
|
+
if (this.modelPreparation === request3) this.modelPreparation = null;
|
|
4381
4447
|
}
|
|
4382
4448
|
})().then(resolve, reject);
|
|
4383
4449
|
return promise;
|
|
@@ -4416,10 +4482,10 @@ var DPthreeCameraController = class {
|
|
|
4416
4482
|
*/
|
|
4417
4483
|
async loadRegions(config) {
|
|
4418
4484
|
if (this.disposed) return;
|
|
4419
|
-
const
|
|
4485
|
+
const request3 = ++this.regionLoadGeneration;
|
|
4420
4486
|
const model = this.model;
|
|
4421
4487
|
const runtimeConfig = await this.resolveCharacterConfig(config);
|
|
4422
|
-
if (!this.isRegionLoadCurrent(
|
|
4488
|
+
if (!this.isRegionLoadCurrent(request3, model)) return;
|
|
4423
4489
|
await this.prepareRegionsAndMarkersForReveal(runtimeConfig);
|
|
4424
4490
|
}
|
|
4425
4491
|
/**
|
|
@@ -4439,12 +4505,12 @@ var DPthreeCameraController = class {
|
|
|
4439
4505
|
async prepareRegionsAndMarkersForReveal(config, beforeReveal) {
|
|
4440
4506
|
if (this.disposed) return;
|
|
4441
4507
|
this.prepareRegionState(config);
|
|
4442
|
-
const
|
|
4508
|
+
const request3 = this.regionLoadGeneration;
|
|
4443
4509
|
const model = this.model;
|
|
4444
4510
|
await this.loadMarkersForCurrentRegions(true);
|
|
4445
|
-
if (!this.isRegionLoadCurrent(
|
|
4511
|
+
if (!this.isRegionLoadCurrent(request3, model) || this.characterConfig !== config) return;
|
|
4446
4512
|
if (beforeReveal && !await beforeReveal()) return;
|
|
4447
|
-
if (this.isRegionLoadCurrent(
|
|
4513
|
+
if (this.isRegionLoadCurrent(request3, model) && this.characterConfig === config) {
|
|
4448
4514
|
this.startPreparedCameraTransition(config);
|
|
4449
4515
|
}
|
|
4450
4516
|
}
|
|
@@ -4479,14 +4545,14 @@ var DPthreeCameraController = class {
|
|
|
4479
4545
|
return;
|
|
4480
4546
|
}
|
|
4481
4547
|
const characterConfig = this.characterConfig;
|
|
4482
|
-
const
|
|
4548
|
+
const request3 = this.regionLoadGeneration;
|
|
4483
4549
|
const model = this.model;
|
|
4484
4550
|
this.ensureMarkersForCurrentStyle();
|
|
4485
4551
|
const markers = this.markers;
|
|
4486
4552
|
if (!markers) return;
|
|
4487
4553
|
markers.setVisible(this.markersVisible);
|
|
4488
4554
|
await markers.loadRegions(characterConfig, { sliceSurfaceQueries });
|
|
4489
|
-
if (!this.isRegionLoadCurrent(
|
|
4555
|
+
if (!this.isRegionLoadCurrent(request3, model) || markers !== this.markers || characterConfig !== this.characterConfig) {
|
|
4490
4556
|
return;
|
|
4491
4557
|
}
|
|
4492
4558
|
if (this.currentRegion) {
|
|
@@ -4656,8 +4722,8 @@ var DPthreeCameraController = class {
|
|
|
4656
4722
|
* @param zoomDuration - Duration of the final zoom in ms (default: 1500)
|
|
4657
4723
|
*/
|
|
4658
4724
|
async playIntroAnimation(orbitDuration = 3e3, zoomDuration = 1500) {
|
|
4659
|
-
const
|
|
4660
|
-
if (
|
|
4725
|
+
const request3 = await this.beginCameraRequest();
|
|
4726
|
+
if (request3 === null) return;
|
|
4661
4727
|
if (!this.model) {
|
|
4662
4728
|
return;
|
|
4663
4729
|
}
|
|
@@ -4672,12 +4738,12 @@ var DPthreeCameraController = class {
|
|
|
4672
4738
|
const orbitRadius = Math.max(horizontalExtent, verticalExtent) * 1.8;
|
|
4673
4739
|
const orbitHeight = center.y;
|
|
4674
4740
|
await this.animateOrbit(center, orbitRadius, orbitHeight, orbitDuration);
|
|
4675
|
-
if (!this.isCameraRequestCurrent(
|
|
4741
|
+
if (!this.isCameraRequestCurrent(request3)) return;
|
|
4676
4742
|
const leftEyeRegion = this.findIntroLeftEyeRegion();
|
|
4677
4743
|
const leftEyeFocus = leftEyeRegion ? this.calculateIntroLeftEyeFocusPosition(leftEyeRegion) : null;
|
|
4678
4744
|
if (leftEyeFocus && leftEyeRegion) {
|
|
4679
4745
|
await this.animateCamera(leftEyeFocus.position, leftEyeFocus.target, zoomDuration);
|
|
4680
|
-
if (!this.isCameraRequestCurrent(
|
|
4746
|
+
if (!this.isCameraRequestCurrent(request3)) return;
|
|
4681
4747
|
this.currentRegion = leftEyeRegion.name;
|
|
4682
4748
|
this.notifyRegionChange(leftEyeRegion.name);
|
|
4683
4749
|
return;
|
|
@@ -4686,7 +4752,7 @@ var DPthreeCameraController = class {
|
|
|
4686
4752
|
const fullBodyRegion = this.regions.find((region) => this.isFullBodyRegion(region));
|
|
4687
4753
|
const focusPos = this.calculateFullBodyFocusPosition(fullBodyRegion?.paddingFactor, finalAngle, "world");
|
|
4688
4754
|
await this.animateCamera(focusPos.position, focusPos.target, zoomDuration);
|
|
4689
|
-
if (!this.isCameraRequestCurrent(
|
|
4755
|
+
if (!this.isCameraRequestCurrent(request3)) return;
|
|
4690
4756
|
if (fullBodyRegion) {
|
|
4691
4757
|
this.currentRegion = fullBodyRegion.name;
|
|
4692
4758
|
this.notifyRegionChange(fullBodyRegion.name);
|
|
@@ -5390,6 +5456,7 @@ var DPthreeCameraController = class {
|
|
|
5390
5456
|
this.regionLoadGeneration += 1;
|
|
5391
5457
|
this.cameraRequestGeneration += 1;
|
|
5392
5458
|
this.stopRenderLoop();
|
|
5459
|
+
this.playbackTimer.dispose();
|
|
5393
5460
|
const renderingSettled = this.unbindRendering?.();
|
|
5394
5461
|
this.unbindRendering = null;
|
|
5395
5462
|
this.cleanupResizeHandling();
|
|
@@ -5703,19 +5770,19 @@ var DPthreeCameraController = class {
|
|
|
5703
5770
|
this.laterality = detectAnnotationLaterality(this.model, this.regions, boneResolutionProfile(this.characterConfig));
|
|
5704
5771
|
}
|
|
5705
5772
|
/** Resolver and paint continuations belong to one model/configuration load. */
|
|
5706
|
-
isRegionLoadCurrent(
|
|
5707
|
-
return !this.disposed &&
|
|
5773
|
+
isRegionLoadCurrent(request3, model) {
|
|
5774
|
+
return !this.disposed && request3 === this.regionLoadGeneration && model === this.model;
|
|
5708
5775
|
}
|
|
5709
5776
|
/** Await Wasm once per request and abandon requests superseded during initialization. */
|
|
5710
5777
|
async beginCameraRequest() {
|
|
5711
|
-
const
|
|
5778
|
+
const request3 = ++this.cameraRequestGeneration;
|
|
5712
5779
|
this.cancelCameraAnimation();
|
|
5713
5780
|
if (this.disposed) return null;
|
|
5714
5781
|
await getAnnotationCameraCore();
|
|
5715
|
-
return this.isCameraRequestCurrent(
|
|
5782
|
+
return this.isCameraRequestCurrent(request3) ? request3 : null;
|
|
5716
5783
|
}
|
|
5717
|
-
isCameraRequestCurrent(
|
|
5718
|
-
return !this.disposed &&
|
|
5784
|
+
isCameraRequestCurrent(request3) {
|
|
5785
|
+
return !this.disposed && request3 === this.cameraRequestGeneration;
|
|
5719
5786
|
}
|
|
5720
5787
|
cancelCameraAnimation() {
|
|
5721
5788
|
this.timerAnimationFallbackToken += 1;
|
|
@@ -6448,6 +6515,99 @@ function pickContinuumAnnotationAuId(value, negId, posId, stickyId) {
|
|
|
6448
6515
|
if (value > 0) return posId;
|
|
6449
6516
|
return negId;
|
|
6450
6517
|
}
|
|
6518
|
+
var json = (value) => JSON.stringify(value ?? {});
|
|
6519
|
+
function request2(op, payload = {}) {
|
|
6520
|
+
return JSON.parse(requireInitializedEmbodyCore().embody_request(json({ op, payload })));
|
|
6521
|
+
}
|
|
6522
|
+
var getPreset = (id) => request2("preset.get", { id });
|
|
6523
|
+
var extendCharacterConfigWithPreset = (config) => request2("profile.extendConfig", { config });
|
|
6524
|
+
var extractProfileOverrides = (config) => request2("profile.extractOverrides", { config });
|
|
6525
|
+
var mergeCharacterRegionsByName = (base, override) => request2("profile.mergeRegions", { base, override });
|
|
6526
|
+
var getMeshNamesForAUProfile = (profile, auId) => request2("profile.getMeshNamesForAU", { profile, auId });
|
|
6527
|
+
var getHumanoidSpecification = () => request2("humanoid.getSpecification");
|
|
6528
|
+
var setAUMorphTargets = (profile, auId, side, targets) => request2("profile.setAUMorphTargets", { profile, auId, side, targets });
|
|
6529
|
+
var setHumanoidRoleBinding = (profile, role, boneName) => request2("profile.setHumanoidRoleBinding", { profile, role, boneName });
|
|
6530
|
+
var resolveHumanoidCharacterization = (profile) => request2("profile.resolveHumanoidCharacterization", { profile });
|
|
6531
|
+
var hasLeftRightMorphs = (profile, auId) => request2("profile.hasLeftRightMorphs", { profile, auId });
|
|
6532
|
+
var isMixedAU = (profile, auId) => request2("profile.isMixedAU", { profile, auId });
|
|
6533
|
+
var resolveVisemeProfile = (profile) => request2("profile.resolveVisemes", { profile });
|
|
6534
|
+
var isMaxDegreesOnlyAxisBindingUpdate = (update) => request2("bone.isMaxDegreesOnlyUpdate", { update });
|
|
6535
|
+
var findNodeKeyForBone = (boneName, profile) => profile ? request2("bone.findNodeKey", { profile, boneName }) : null;
|
|
6536
|
+
var ensureBoneNodeKey = (profile, boneName) => request2("bone.ensureNodeKey", { profile, boneName });
|
|
6537
|
+
var ensureBilateralBoneNodeKeys = (profile, leftBoneName, rightBoneName) => request2("bone.ensureBilateralNodeKeys", { profile, leftBoneName, rightBoneName });
|
|
6538
|
+
var resolveBoneNameForNodeKey = (profile, nodeKey) => request2("bone.resolveBoneName", { profile, nodeKey });
|
|
6539
|
+
var stripConfiguredBoneAffixes = (profile, boneName) => request2("bone.stripAffixes", { profile: profile ?? {}, boneName });
|
|
6540
|
+
var inferChiralBoneNamePair = (boneNames, selectedBoneName) => request2("bone.inferChiralPair", { boneNames, selectedBoneName });
|
|
6541
|
+
var formatAxisLabel = (axis) => request2("bone.formatAxisLabel", { axis });
|
|
6542
|
+
var formatAxisDirectionLabel = (axis, direction) => request2("bone.formatDirectionLabel", { axis, direction });
|
|
6543
|
+
var getAxisFromChannel = (channel) => channel ? request2("bone.getAxisFromChannel", { channel }) : null;
|
|
6544
|
+
var getBoneAxisBindingState = (profile, boneName, axis) => request2("bone.getAxisState", { profile, boneName, axis });
|
|
6545
|
+
var resolveBoneAxisChannel = (profile, boneName, axis) => request2("bone.resolveAxisChannel", { profile, boneName, axis });
|
|
6546
|
+
var getBilateralAxisBindingState = (profile, boneName, axis) => request2("bone.getBilateralAxisState", { profile, boneName, axis });
|
|
6547
|
+
var getAUBoneBindingState = (profile, auId) => request2("bone.getAUBindingState", { profile, auId });
|
|
6548
|
+
var inferEyeControlScope = (profile, binding) => request2("bone.inferEyeScope", { profile, binding });
|
|
6549
|
+
var inferEyeControlFamily = (binding) => request2("bone.inferEyeFamily", { binding });
|
|
6550
|
+
var applyBoneAxisBindingUpdate = (profile, boneName, axis, update) => request2("bone.applyAxisUpdate", { profile, boneName, axis, update });
|
|
6551
|
+
var applyBilateralAxisBindingUpdate = (profile, boneName, axis, scope, update) => request2("bone.applyBilateralAxisUpdate", { profile, boneName, axis, scope, update });
|
|
6552
|
+
var applyAUBoneBindingUpdate = (profile, auId, update) => request2("bone.applyAUBindingUpdate", { profile, auId, update });
|
|
6553
|
+
var createBoneAxisAu = (profile, boneName, axis, direction, options = {}) => request2("bone.createAxisAU", { profile, boneName, axis, direction, name: options.name });
|
|
6554
|
+
var createBilateralBoneAxisAu = (profile, boneName, axis, scope, direction, options = {}) => request2("bone.createBilateralAxisAU", { profile, boneName, axis, scope, direction, name: options.name });
|
|
6555
|
+
var buildBoneAuOptions = (profile) => request2("bone.buildAUOptions", { profile });
|
|
6556
|
+
var isJointControlAuInfo = (info) => request2("bone.isJointControlInfo", { info });
|
|
6557
|
+
var classifyAuAsJointControl = (profile, auId, options = {}) => request2("bone.classifyJointControl", { profile, auId, boneName: options.boneName, nodeKey: options.nodeKey });
|
|
6558
|
+
var resolveContinuumDisplayLabel = (profile, negativeAuId, positiveAuId) => request2("bone.resolveContinuumLabel", { profile, negativeAuId, positiveAuId }) ?? `AU ${negativeAuId} <-> AU ${positiveAuId}`;
|
|
6559
|
+
function inspectModel(model, meshes, profile) {
|
|
6560
|
+
return new ThreeModelInspector().inspectModel(model, { meshes, profile });
|
|
6561
|
+
}
|
|
6562
|
+
function getBodyControls(profile, model) {
|
|
6563
|
+
return request2("profile.getBodyControls", { profile, ...model ? { model: inspectModel(model, void 0, profile).descriptor } : {} });
|
|
6564
|
+
}
|
|
6565
|
+
function extractModelData(model, meshes, animations = []) {
|
|
6566
|
+
const inspection = inspectModel(model, meshes);
|
|
6567
|
+
return JSON.parse(requireInitializedEmbodyCore().extract_model_data_json(json(inspection.descriptor), json(serializeAnimationClips(model, animations, inspection))));
|
|
6568
|
+
}
|
|
6569
|
+
async function analyzeModel(options) {
|
|
6570
|
+
const source = options.source;
|
|
6571
|
+
const model = source.type === "gltf" ? source.gltf.scene ?? source.gltf.scenes[0] : source.model;
|
|
6572
|
+
const animations = source.type === "gltf" ? source.gltf.animations : source.animations ?? [];
|
|
6573
|
+
const inspection = inspectModel(model, source.type === "runtime" ? source.meshes : void 0);
|
|
6574
|
+
return JSON.parse(requireInitializedEmbodyCore().analyze_model_descriptor(json(inspection.descriptor), json(serializeAnimationClips(model, animations, inspection)), options.preset ? json(options.preset) : "", json({ suggestCorrections: Boolean(options.suggestCorrections) })));
|
|
6575
|
+
}
|
|
6576
|
+
function validateMappings(meshes, skeleton, profile, options = {}) {
|
|
6577
|
+
let nextMorphId = 1;
|
|
6578
|
+
const morphTargets = [];
|
|
6579
|
+
const descriptorMeshes = (meshes ?? []).map((value, index) => {
|
|
6580
|
+
const mesh = value;
|
|
6581
|
+
const id = index + 1;
|
|
6582
|
+
const morphTargetIds = Object.entries(mesh.morphTargetDictionary ?? mesh.geometry?.morphTargetDictionary ?? {}).map(([name, hostIndex]) => {
|
|
6583
|
+
const morphId = nextMorphId++;
|
|
6584
|
+
morphTargets.push({ id: morphId, meshId: id, name, hostIndex });
|
|
6585
|
+
return morphId;
|
|
6586
|
+
});
|
|
6587
|
+
return { id, name: mesh.name ?? "", morphTargetIds, visible: true };
|
|
6588
|
+
});
|
|
6589
|
+
const bones = (skeleton?.bones ?? []).map((bone, index) => ({ id: index + 1, name: typeof bone === "string" ? bone : bone.name ?? "" }));
|
|
6590
|
+
return JSON.parse(requireInitializedEmbodyCore().validate_profile_model(json(profile), json({ meshes: descriptorMeshes, morphTargets, bones, objects: [] }), json(options)));
|
|
6591
|
+
}
|
|
6592
|
+
function validateHumanoidCharacterization(model, profile) {
|
|
6593
|
+
return JSON.parse(requireInitializedEmbodyCore().validate_humanoid_characterization(json(profile), json(inspectModel(model, void 0, profile).descriptor)));
|
|
6594
|
+
}
|
|
6595
|
+
function normalizeHairColorAppearance(value, fallback = DEFAULT_HAIR_COLOR_APPEARANCE) {
|
|
6596
|
+
return JSON.parse(requireInitializedEmbodyCore().normalize_hair_color_appearance_json(json(value), json(fallback)));
|
|
6597
|
+
}
|
|
6598
|
+
var getHumanoidSkeletonTemplate = (id) => HUMANOID_SKELETON_TEMPLATES.find((template) => template.id === id) ?? null;
|
|
6599
|
+
function getHumanoidSkeletonTemplateBoneNames(template = CC4_HUMANOID_SKELETON_TEMPLATE) {
|
|
6600
|
+
return JSON.parse(requireInitializedEmbodyCore().humanoid_skeleton_template_bone_names(json(template)));
|
|
6601
|
+
}
|
|
6602
|
+
function createValidationSkeletonFromHumanoidTemplate(template = CC4_HUMANOID_SKELETON_TEMPLATE) {
|
|
6603
|
+
return JSON.parse(requireInitializedEmbodyCore().create_validation_skeleton_json(json(template)));
|
|
6604
|
+
}
|
|
6605
|
+
function computeHumanoidSkeletonTemplateRestBounds(template) {
|
|
6606
|
+
return Array.from(requireInitializedEmbodyCore().compute_humanoid_skeleton_template_rest_bounds(json(template)));
|
|
6607
|
+
}
|
|
6608
|
+
function extractHumanoidSkeletonTemplateFromModel(model, options) {
|
|
6609
|
+
return JSON.parse(requireInitializedEmbodyCore().extract_humanoid_skeleton_template_json(json(inspectModel(model).descriptor), json(options)));
|
|
6610
|
+
}
|
|
6451
6611
|
var isBone = (object) => !!(object.isBone || object.type === "Bone");
|
|
6452
6612
|
var finite = (values, description) => {
|
|
6453
6613
|
if (!values.every(Number.isFinite)) {
|
|
@@ -6558,332 +6718,1635 @@ function extendModelReferencePose(model, previous) {
|
|
|
6558
6718
|
}
|
|
6559
6719
|
function bindModelReferencePose(model, referencePose) {
|
|
6560
6720
|
if (referencePose.version !== 1) throw new Error("Unsupported model reference pose version");
|
|
6561
|
-
const
|
|
6721
|
+
const bindings2 = /* @__PURE__ */ new Map();
|
|
6562
6722
|
let index = 0;
|
|
6563
6723
|
const bind = (object, path, parentPath) => {
|
|
6564
6724
|
const node = referencePose.nodes[index++];
|
|
6565
6725
|
if (!matchesNode(object, node, path, parentPath) || node.childCount !== object.children.length) {
|
|
6566
6726
|
throw incompatible(object, path);
|
|
6567
6727
|
}
|
|
6568
|
-
|
|
6728
|
+
bindings2.set(object, node);
|
|
6569
6729
|
object.children.forEach((child, childIndex) => bind(child, `${path}/${childIndex}`, path));
|
|
6570
6730
|
};
|
|
6571
6731
|
bind(model, "", null);
|
|
6572
6732
|
if (index !== referencePose.nodes.length) throw new Error("Incompatible reference pose hierarchy: extra captured nodes");
|
|
6573
|
-
return
|
|
6574
|
-
}
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
__publicField(this, "references");
|
|
6590
|
-
__publicField(this, "limits");
|
|
6591
|
-
this.references = bindModelReferencePose(model, options.referencePose ?? captureModelReferencePose(model));
|
|
6592
|
-
this.head = this.joint("HEAD", "head", [51, 52, 53, 54, 56, 55]);
|
|
6593
|
-
this.eyes = [
|
|
6594
|
-
this.joint("EYE_L", "leftEye", [61, 62, 63, 64]),
|
|
6595
|
-
this.joint("EYE_R", "rightEye", [61, 62, 63, 64])
|
|
6596
|
-
].filter((value) => !!value);
|
|
6597
|
-
this.limits = {
|
|
6598
|
-
headYaw: this.capacity(51, 52),
|
|
6599
|
-
headPitch: this.capacity(53, 54),
|
|
6600
|
-
eyeYaw: this.capacity(61, 62),
|
|
6601
|
-
eyePitch: this.capacity(63, 64)
|
|
6602
|
-
};
|
|
6733
|
+
return bindings2;
|
|
6734
|
+
}
|
|
6735
|
+
|
|
6736
|
+
// three/clipBindings.ts
|
|
6737
|
+
function concreteTrackName(target, inspection) {
|
|
6738
|
+
if (!target) return void 0;
|
|
6739
|
+
if (target.kind === "morphTarget") {
|
|
6740
|
+
const binding = inspection.morphBindings.get(Number(target.morphTargetId));
|
|
6741
|
+
return binding && `${binding.mesh.uuid}.morphTargetInfluences[${binding.index}]`;
|
|
6742
|
+
}
|
|
6743
|
+
if (target.kind === "boneTransform" || target.kind === "objectTransform") {
|
|
6744
|
+
const object = target.kind === "boneTransform" ? inspection.boneBindings.get(Number(target.boneId)) : inspection.objectBindings.get(Number(target.objectId));
|
|
6745
|
+
const property = target.property ?? "rotation";
|
|
6746
|
+
if (!object) return void 0;
|
|
6747
|
+
if (property === "rotation" || property === "quaternion") return `${object.uuid}.quaternion`;
|
|
6748
|
+
if (property === "position" || property === "scale") return `${object.uuid}.${property}`;
|
|
6603
6749
|
}
|
|
6604
|
-
|
|
6605
|
-
const
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
const
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6750
|
+
if (target.kind === "meshVisibility") {
|
|
6751
|
+
const mesh = inspection.meshBindings.get(Number(target.meshId));
|
|
6752
|
+
return mesh && `${mesh.uuid}.visible`;
|
|
6753
|
+
}
|
|
6754
|
+
return void 0;
|
|
6755
|
+
}
|
|
6756
|
+
function cloneWithConcreteTrackNames(clip, tracks, inspection) {
|
|
6757
|
+
const native = clip.clone();
|
|
6758
|
+
for (let index = 0; index < tracks.length; index += 1) {
|
|
6759
|
+
const name = concreteTrackName(tracks[index].target, inspection);
|
|
6760
|
+
if (!name) throw new Error("Every native playback track must resolve on the bound model.");
|
|
6761
|
+
native.tracks[index].name = name;
|
|
6762
|
+
}
|
|
6763
|
+
return native;
|
|
6764
|
+
}
|
|
6765
|
+
|
|
6766
|
+
// three/nativePlayback.ts
|
|
6767
|
+
var sampleSize = (track) => track.getValueSize() / (track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ? 3 : 1);
|
|
6768
|
+
var asError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
6769
|
+
var blendMode = (mode) => mode === "additive" ? AdditiveAnimationBlendMode : NormalAnimationBlendMode;
|
|
6770
|
+
var loopMode = (mode) => mode === "once" ? LoopOnce : mode === "repeat" ? LoopRepeat : LoopPingPong;
|
|
6771
|
+
function validateBinding(binding) {
|
|
6772
|
+
if (!binding.model?.isObject3D || binding.mixer && binding.mixer.getRoot() !== binding.model) {
|
|
6773
|
+
throw new Error("Native playback requires a Three model and its render integration mixer.");
|
|
6774
|
+
}
|
|
6775
|
+
const objects = /* @__PURE__ */ new Set();
|
|
6776
|
+
binding.model.traverse((object) => objects.add(object));
|
|
6777
|
+
const { inspection } = binding;
|
|
6778
|
+
for (const object of [
|
|
6779
|
+
...inspection.meshBindings.values(),
|
|
6780
|
+
...inspection.boneBindings.values(),
|
|
6781
|
+
...inspection.objectBindings.values(),
|
|
6782
|
+
...Array.from(inspection.morphBindings.values(), (entry) => entry.mesh)
|
|
6783
|
+
]) {
|
|
6784
|
+
if (!objects.has(object)) throw new Error("Native playback inspection contains an object outside the bound model.");
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6787
|
+
var ThreeNativePlaybackConnector = class {
|
|
6788
|
+
constructor(session, binding) {
|
|
6789
|
+
this.session = session;
|
|
6790
|
+
__publicField(this, "binding");
|
|
6791
|
+
__publicField(this, "rendering");
|
|
6792
|
+
__publicField(this, "renderingEnabled", true);
|
|
6793
|
+
__publicField(this, "resources", /* @__PURE__ */ new Map());
|
|
6794
|
+
__publicField(this, "actionHandles", /* @__PURE__ */ new Map());
|
|
6795
|
+
__publicField(this, "listeners", /* @__PURE__ */ new Set());
|
|
6796
|
+
__publicField(this, "pending", []);
|
|
6797
|
+
__publicField(this, "notifications", []);
|
|
6798
|
+
__publicField(this, "depth", 0);
|
|
6799
|
+
__publicField(this, "flushingNotifications", false);
|
|
6800
|
+
__publicField(this, "notificationScheduled", false);
|
|
6801
|
+
__publicField(this, "sessionDisposed", false);
|
|
6802
|
+
__publicField(this, "disposed", false);
|
|
6803
|
+
__publicField(this, "onError");
|
|
6804
|
+
__publicField(this, "onRelease");
|
|
6805
|
+
__publicField(this, "finished", (event) => {
|
|
6806
|
+
const handle = this.actionHandles.get(event.action);
|
|
6807
|
+
if (!handle || this.disposed) return;
|
|
6808
|
+
this.notifications.push({ type: this.resources.get(handle)?.fadingOut ? "cancelled" : "completed", handle });
|
|
6809
|
+
if (this.notificationScheduled) return;
|
|
6810
|
+
this.notificationScheduled = true;
|
|
6811
|
+
queueMicrotask(() => {
|
|
6812
|
+
this.notificationScheduled = false;
|
|
6813
|
+
if (this.disposed) return;
|
|
6814
|
+
try {
|
|
6815
|
+
this.flushNotifications();
|
|
6816
|
+
} catch (error) {
|
|
6817
|
+
const failure = asError(error);
|
|
6818
|
+
if (this.onError) this.onError(failure);
|
|
6819
|
+
else throw failure;
|
|
6820
|
+
}
|
|
6821
|
+
});
|
|
6822
|
+
});
|
|
6823
|
+
validateBinding(binding);
|
|
6824
|
+
this.binding = { ...binding, mixer: binding.mixer ?? new AnimationMixer(binding.model) };
|
|
6825
|
+
this.onError = binding.onError;
|
|
6826
|
+
this.onRelease = binding.onRelease;
|
|
6827
|
+
this.binding.mixer.addEventListener("finished", this.finished);
|
|
6828
|
+
this.rendering = bindPlaybackRendering(binding.model, this.binding.mixer, () => this.flushEvents());
|
|
6829
|
+
}
|
|
6830
|
+
/** Prepare bindings first; old actions are released before the new binding is exposed. */
|
|
6831
|
+
rebind(modelKey, profileKey, binding = this.binding) {
|
|
6832
|
+
validateBinding(binding);
|
|
6833
|
+
this.beforeOperation();
|
|
6834
|
+
this.dispatch(this.session.rebind(modelKey, profileKey), void 0, () => {
|
|
6835
|
+
this.binding.mixer.removeEventListener("finished", this.finished);
|
|
6836
|
+
this.rendering.dispose();
|
|
6837
|
+
this.binding = { ...binding, mixer: binding.mixer ?? new AnimationMixer(binding.model) };
|
|
6838
|
+
this.binding.mixer.addEventListener("finished", this.finished);
|
|
6839
|
+
this.rendering = bindPlaybackRendering(binding.model, this.binding.mixer, () => this.flushEvents());
|
|
6840
|
+
this.rendering.setEnabled(this.renderingEnabled);
|
|
6841
|
+
});
|
|
6842
|
+
}
|
|
6843
|
+
play(clip, options = {}) {
|
|
6844
|
+
this.beforeOperation();
|
|
6845
|
+
const native = createAnimationClipFromClipIR(clip, this.binding.inspection);
|
|
6846
|
+
if (native.tracks.length !== clip.tracks.length) {
|
|
6847
|
+
throw new Error("Every playback track must resolve on the bound model.");
|
|
6848
|
+
}
|
|
6849
|
+
return this.dispatch(this.session.play(JSON.stringify(clip), JSON.stringify(options)), native).handle;
|
|
6850
|
+
}
|
|
6851
|
+
/**
|
|
6852
|
+
* Validate native track bindings through the existing serializer, then retain
|
|
6853
|
+
* the native clone's interpolants and authored/additive sample values. Concrete
|
|
6854
|
+
* binding names match compiled tracks so Three shares their PropertyMixer.
|
|
6855
|
+
*/
|
|
6856
|
+
playNativeClip(clip, options = {}) {
|
|
6857
|
+
this.beforeOperation();
|
|
6858
|
+
const [ir] = serializeAnimationClips(this.binding.model, [clip], this.binding.inspection);
|
|
6859
|
+
if (ir.tracks.length !== clip.tracks.length) {
|
|
6860
|
+
throw new Error("Every native playback track must serialize against the bound model.");
|
|
6861
|
+
}
|
|
6862
|
+
const native = cloneWithConcreteTrackNames(clip, ir.tracks, this.binding.inspection);
|
|
6863
|
+
const settings = {
|
|
6864
|
+
...options,
|
|
6865
|
+
blendMode: options.blendMode ?? (clip.blendMode === AdditiveAnimationBlendMode ? "additive" : "normal")
|
|
6866
|
+
};
|
|
6867
|
+
return this.dispatch(this.session.play(JSON.stringify(ir), JSON.stringify(settings)), native).handle;
|
|
6868
|
+
}
|
|
6869
|
+
control(handle, control) {
|
|
6870
|
+
this.beforeOperation();
|
|
6871
|
+
this.dispatch(this.session.control(handle, JSON.stringify(control)));
|
|
6872
|
+
}
|
|
6873
|
+
setRendering(enabled) {
|
|
6874
|
+
this.beforeOperation();
|
|
6875
|
+
if (enabled === this.renderingEnabled) return;
|
|
6876
|
+
this.renderingEnabled = enabled;
|
|
6877
|
+
this.rendering.setEnabled(enabled);
|
|
6878
|
+
for (const resource of this.resources.values()) {
|
|
6879
|
+
if (!resource.action) continue;
|
|
6880
|
+
if (!enabled) {
|
|
6881
|
+
resource.suspendedPaused = resource.action.paused;
|
|
6882
|
+
this.pauseFade(resource);
|
|
6883
|
+
resource.action.paused = true;
|
|
6884
|
+
} else if (resource.suspendedPaused !== void 0) {
|
|
6885
|
+
resource.action.paused = resource.suspendedPaused;
|
|
6886
|
+
if (!resource.explicitlyPaused) this.resumeFade(resource);
|
|
6887
|
+
resource.suspendedPaused = void 0;
|
|
6888
|
+
}
|
|
6889
|
+
}
|
|
6890
|
+
}
|
|
6891
|
+
setTimeScale(value) {
|
|
6892
|
+
this.beforeOperation();
|
|
6893
|
+
if (!Number.isFinite(value)) throw new Error("Native mixer speed must be finite.");
|
|
6894
|
+
this.binding.mixer.timeScale = value;
|
|
6895
|
+
}
|
|
6896
|
+
fadeIn(handle, seconds) {
|
|
6897
|
+
this.control(handle, { type: "fadeIn", seconds });
|
|
6898
|
+
}
|
|
6899
|
+
fadeOut(handle, seconds) {
|
|
6900
|
+
this.control(handle, { type: "fadeOut", seconds });
|
|
6901
|
+
}
|
|
6902
|
+
/** Edit authored samples, retaining the native action and its loop/fade progress. */
|
|
6903
|
+
replaceClip(handle, clip) {
|
|
6904
|
+
this.beforeOperation();
|
|
6905
|
+
const resource = this.resources.get(handle);
|
|
6906
|
+
if (!resource) throw new Error("Unknown, foreign or stale playback handle.");
|
|
6907
|
+
const [ir] = serializeAnimationClips(this.binding.model, [clip], this.binding.inspection);
|
|
6908
|
+
if (ir.tracks.length !== clip.tracks.length) throw new Error("Every edited track must resolve on the bound model.");
|
|
6909
|
+
const native = cloneWithConcreteTrackNames(clip, ir.tracks, this.binding.inspection);
|
|
6910
|
+
if (native.tracks.length !== resource.clip.tracks.length || native.tracks.some((track, index) => {
|
|
6911
|
+
const old = resource.clip.tracks[index];
|
|
6912
|
+
return track.name !== old.name || track.ValueTypeName !== old.ValueTypeName || sampleSize(track) !== sampleSize(old);
|
|
6913
|
+
})) throw new Error("Editing samples must preserve track bindings; play a new clip to change its bindings.");
|
|
6914
|
+
const editable = resource.action;
|
|
6915
|
+
if (!editable || !Array.isArray(editable._interpolants) || editable._interpolants.length !== native.tracks.length) {
|
|
6916
|
+
throw new Error("This Three version does not support editing native action samples.");
|
|
6917
|
+
}
|
|
6918
|
+
this.dispatch(this.session.replace_clip(handle, JSON.stringify(ir)), native);
|
|
6919
|
+
}
|
|
6920
|
+
/** Native state for the owning adapter; never exposed through Polymer. */
|
|
6921
|
+
getAction(handle) {
|
|
6922
|
+
return this.resources.get(handle)?.action;
|
|
6923
|
+
}
|
|
6924
|
+
/** Deliver native completion after the borrowed mixer has finished sampling. */
|
|
6925
|
+
flushEvents() {
|
|
6926
|
+
if (this.disposed) return;
|
|
6927
|
+
this.notificationScheduled = false;
|
|
6928
|
+
for (const [handle, resource] of this.resources) {
|
|
6929
|
+
if (resource.fadingOut && resource.action?.getEffectiveWeight() === 0) {
|
|
6930
|
+
resource.fadingOut = false;
|
|
6931
|
+
this.notifications.push({ type: "cancelled", handle });
|
|
6625
6932
|
}
|
|
6626
|
-
|
|
6933
|
+
}
|
|
6934
|
+
this.flushNotifications();
|
|
6935
|
+
}
|
|
6936
|
+
subscribe(listener) {
|
|
6937
|
+
if (this.disposed) throw new Error("Native playback connector is disposed.");
|
|
6938
|
+
this.listeners.add(listener);
|
|
6939
|
+
return () => {
|
|
6940
|
+
this.listeners.delete(listener);
|
|
6627
6941
|
};
|
|
6628
|
-
return { positive: magnitude(positive), negative: magnitude(negative) };
|
|
6629
6942
|
}
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
const
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
yaw,
|
|
6655
|
-
pitch,
|
|
6656
|
-
roll,
|
|
6657
|
-
reference: new Quaternion(rotation.x, rotation.y, rotation.z, rotation.w),
|
|
6658
|
-
referenceParent,
|
|
6659
|
-
referenceScale: vector(reference.transform.scale)
|
|
6943
|
+
dispose() {
|
|
6944
|
+
if (this.disposed) return;
|
|
6945
|
+
this.retryPending();
|
|
6946
|
+
if (!this.sessionDisposed) {
|
|
6947
|
+
const batch = this.session.dispose();
|
|
6948
|
+
this.sessionDisposed = true;
|
|
6949
|
+
this.dispatch(batch);
|
|
6950
|
+
}
|
|
6951
|
+
this.binding.mixer.removeEventListener("finished", this.finished);
|
|
6952
|
+
this.rendering.dispose();
|
|
6953
|
+
this.notifications.length = 0;
|
|
6954
|
+
this.listeners.clear();
|
|
6955
|
+
this.session.free();
|
|
6956
|
+
this.disposed = true;
|
|
6957
|
+
}
|
|
6958
|
+
pauseFade(resource) {
|
|
6959
|
+
const action = resource.action;
|
|
6960
|
+
const fade = action?._weightInterpolant;
|
|
6961
|
+
if (!action || !fade) return;
|
|
6962
|
+
resource.pausedFade = {
|
|
6963
|
+
seconds: Math.max(0, fade.parameterPositions[1] - resource.binding.mixer.time),
|
|
6964
|
+
from: fade.evaluate(resource.binding.mixer.time)[0],
|
|
6965
|
+
to: fade.sampleValues[1],
|
|
6966
|
+
weight: action.weight
|
|
6660
6967
|
};
|
|
6968
|
+
action.stopFading();
|
|
6969
|
+
action.setEffectiveWeight(resource.pausedFade.weight * resource.pausedFade.from);
|
|
6970
|
+
}
|
|
6971
|
+
resumeFade(resource) {
|
|
6972
|
+
const action = resource.action;
|
|
6973
|
+
const fade = resource.pausedFade;
|
|
6974
|
+
if (!action || !fade) return;
|
|
6975
|
+
resource.pausedFade = void 0;
|
|
6976
|
+
action.setEffectiveWeight(fade.weight);
|
|
6977
|
+
action.fadeIn(fade.seconds);
|
|
6978
|
+
const interpolant = action._weightInterpolant;
|
|
6979
|
+
interpolant.sampleValues[0] = fade.from;
|
|
6980
|
+
interpolant.sampleValues[1] = fade.to;
|
|
6981
|
+
}
|
|
6982
|
+
beforeOperation() {
|
|
6983
|
+
if (this.disposed || this.sessionDisposed) throw new Error("Native playback connector is disposed.");
|
|
6984
|
+
this.retryPending();
|
|
6985
|
+
}
|
|
6986
|
+
retryPending() {
|
|
6987
|
+
if (this.pending.length) this.drain();
|
|
6988
|
+
}
|
|
6989
|
+
dispatch(serialized, clip, afterCommands) {
|
|
6990
|
+
const batch = JSON.parse(serialized);
|
|
6991
|
+
this.pending.push({ batch, nextCommand: 0, clip, afterCommands });
|
|
6992
|
+
this.drain();
|
|
6993
|
+
return batch;
|
|
6994
|
+
}
|
|
6995
|
+
drain() {
|
|
6996
|
+
const errors = [];
|
|
6997
|
+
this.depth += 1;
|
|
6998
|
+
try {
|
|
6999
|
+
while (this.pending.length) {
|
|
7000
|
+
const pending = this.pending[0];
|
|
7001
|
+
while (pending.nextCommand < pending.batch.commands.length) {
|
|
7002
|
+
const command = pending.batch.commands[pending.nextCommand];
|
|
7003
|
+
try {
|
|
7004
|
+
this.execute(command, pending.clip);
|
|
7005
|
+
} catch (error) {
|
|
7006
|
+
if (command.type === "release") throw asError(error);
|
|
7007
|
+
const failure = asError(error);
|
|
7008
|
+
errors.push(failure);
|
|
7009
|
+
this.notifications.push({ type: "error", handle: command.handle, message: failure.message || "Native operation failed." });
|
|
7010
|
+
}
|
|
7011
|
+
pending.nextCommand += 1;
|
|
7012
|
+
}
|
|
7013
|
+
pending.afterCommands?.();
|
|
7014
|
+
this.pending.shift();
|
|
7015
|
+
for (const event of pending.batch.events) {
|
|
7016
|
+
for (const listener of [...this.listeners]) {
|
|
7017
|
+
try {
|
|
7018
|
+
listener(event);
|
|
7019
|
+
} catch (error) {
|
|
7020
|
+
const failure = asError(error);
|
|
7021
|
+
queueMicrotask(() => {
|
|
7022
|
+
if (this.onError) this.onError(failure);
|
|
7023
|
+
else throw failure;
|
|
7024
|
+
});
|
|
7025
|
+
}
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
}
|
|
7029
|
+
} finally {
|
|
7030
|
+
this.depth -= 1;
|
|
7031
|
+
}
|
|
7032
|
+
if (this.depth === 0 && !this.flushingNotifications) this.flushNotifications();
|
|
7033
|
+
if (errors.length) throw errors[0];
|
|
7034
|
+
}
|
|
7035
|
+
execute(command, preparedClip) {
|
|
7036
|
+
if (command.type === "release") {
|
|
7037
|
+
const resource2 = this.resources.get(command.handle);
|
|
7038
|
+
if (!resource2) return;
|
|
7039
|
+
resource2.action?.stop();
|
|
7040
|
+
resource2.binding.mixer.uncacheAction(resource2.clip, resource2.binding.model);
|
|
7041
|
+
resource2.binding.mixer.uncacheClip(resource2.clip);
|
|
7042
|
+
if (resource2.action) this.actionHandles.delete(resource2.action);
|
|
7043
|
+
this.resources.delete(command.handle);
|
|
7044
|
+
try {
|
|
7045
|
+
this.onRelease?.(command.handle);
|
|
7046
|
+
} catch (error) {
|
|
7047
|
+
const failure = asError(error);
|
|
7048
|
+
queueMicrotask(() => {
|
|
7049
|
+
if (this.onError) this.onError(failure);
|
|
7050
|
+
else throw failure;
|
|
7051
|
+
});
|
|
7052
|
+
}
|
|
7053
|
+
return;
|
|
7054
|
+
}
|
|
7055
|
+
if (command.type === "replaceClip") {
|
|
7056
|
+
const resource2 = this.resources.get(command.handle);
|
|
7057
|
+
if (!resource2 || !preparedClip) throw new Error("Native clip edit has no owned resource.");
|
|
7058
|
+
const action2 = resource2.action;
|
|
7059
|
+
const tracks = preparedClip.tracks.map((track) => track.clone());
|
|
7060
|
+
const interpolants = tracks.map((track, index) => {
|
|
7061
|
+
const interpolant = track.createInterpolant(action2._interpolants[index].resultBuffer);
|
|
7062
|
+
if (interpolant.settings) Object.assign(action2._interpolantSettings, interpolant.settings);
|
|
7063
|
+
interpolant.settings = action2._interpolantSettings;
|
|
7064
|
+
return interpolant;
|
|
7065
|
+
});
|
|
7066
|
+
resource2.clip.tracks = tracks;
|
|
7067
|
+
action2._interpolants = interpolants;
|
|
7068
|
+
return;
|
|
7069
|
+
}
|
|
7070
|
+
if (command.type === "play") {
|
|
7071
|
+
if (!preparedClip) throw new Error("Native playback has no prepared clip.");
|
|
7072
|
+
const resource2 = { clip: preparedClip, binding: this.binding };
|
|
7073
|
+
this.resources.set(command.handle, resource2);
|
|
7074
|
+
const settings = command.settings;
|
|
7075
|
+
const action2 = this.binding.mixer.clipAction(preparedClip, this.binding.model, blendMode(settings.blendMode));
|
|
7076
|
+
resource2.action = action2;
|
|
7077
|
+
this.actionHandles.set(action2, command.handle);
|
|
7078
|
+
action2.setLoop(loopMode(settings.loop.mode), settings.loop.count ?? Infinity);
|
|
7079
|
+
action2.clampWhenFinished = settings.finalPose === "hold";
|
|
7080
|
+
action2.setEffectiveTimeScale(settings.speed);
|
|
7081
|
+
action2.setEffectiveWeight(settings.weight);
|
|
7082
|
+
action2.time = settings.startSeconds;
|
|
7083
|
+
if (!this.renderingEnabled) {
|
|
7084
|
+
resource2.suspendedPaused = false;
|
|
7085
|
+
action2.paused = true;
|
|
7086
|
+
}
|
|
7087
|
+
action2.play();
|
|
7088
|
+
return;
|
|
7089
|
+
}
|
|
7090
|
+
const resource = this.resources.get(command.handle);
|
|
7091
|
+
const action = resource?.action;
|
|
7092
|
+
if (!resource || !action) throw new Error("Native playback action is unavailable.");
|
|
7093
|
+
const control = command.control;
|
|
7094
|
+
switch (control.type) {
|
|
7095
|
+
case "pause":
|
|
7096
|
+
resource.explicitlyPaused = true;
|
|
7097
|
+
this.pauseFade(resource);
|
|
7098
|
+
action.paused = true;
|
|
7099
|
+
if (!this.renderingEnabled) this.resources.get(command.handle).suspendedPaused = true;
|
|
7100
|
+
break;
|
|
7101
|
+
case "resume":
|
|
7102
|
+
resource.explicitlyPaused = false;
|
|
7103
|
+
if (this.renderingEnabled) {
|
|
7104
|
+
action.paused = false;
|
|
7105
|
+
this.resumeFade(resource);
|
|
7106
|
+
} else this.resources.get(command.handle).suspendedPaused = false;
|
|
7107
|
+
break;
|
|
7108
|
+
case "seek":
|
|
7109
|
+
action.time = control.seconds;
|
|
7110
|
+
break;
|
|
7111
|
+
case "setSpeed":
|
|
7112
|
+
action.setEffectiveTimeScale(control.speed);
|
|
7113
|
+
break;
|
|
7114
|
+
case "setWeight":
|
|
7115
|
+
resource.pausedFade = void 0;
|
|
7116
|
+
action.setEffectiveWeight(control.weight);
|
|
7117
|
+
this.resources.get(command.handle).fadingOut = false;
|
|
7118
|
+
break;
|
|
7119
|
+
case "setBlendMode":
|
|
7120
|
+
action.blendMode = blendMode(control.blendMode);
|
|
7121
|
+
break;
|
|
7122
|
+
case "setLoop":
|
|
7123
|
+
action.setLoop(loopMode(control.loop.mode), control.loop.count ?? Infinity);
|
|
7124
|
+
break;
|
|
7125
|
+
case "setFinalPose":
|
|
7126
|
+
action.clampWhenFinished = control.finalPose === "hold";
|
|
7127
|
+
break;
|
|
7128
|
+
case "fadeIn":
|
|
7129
|
+
resource.pausedFade = void 0;
|
|
7130
|
+
action.fadeIn(control.seconds);
|
|
7131
|
+
resource.fadingOut = false;
|
|
7132
|
+
if (resource.explicitlyPaused || !this.renderingEnabled) this.pauseFade(resource);
|
|
7133
|
+
break;
|
|
7134
|
+
case "fadeOut":
|
|
7135
|
+
resource.pausedFade = void 0;
|
|
7136
|
+
if (control.seconds === 0) this.notifications.push({ type: "cancelled", handle: command.handle });
|
|
7137
|
+
else {
|
|
7138
|
+
action.fadeOut(control.seconds);
|
|
7139
|
+
resource.fadingOut = true;
|
|
7140
|
+
if (resource.explicitlyPaused || !this.renderingEnabled) this.pauseFade(resource);
|
|
7141
|
+
}
|
|
7142
|
+
break;
|
|
7143
|
+
case "stop":
|
|
7144
|
+
throw new Error("Rust must translate stop into a resource release.");
|
|
7145
|
+
}
|
|
6661
7146
|
}
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
7147
|
+
flushNotifications() {
|
|
7148
|
+
if (this.flushingNotifications || this.depth > 0 || this.disposed || this.notificationScheduled) return;
|
|
7149
|
+
this.flushingNotifications = true;
|
|
7150
|
+
try {
|
|
7151
|
+
while (this.notifications.length) {
|
|
7152
|
+
const event = this.notifications.shift();
|
|
7153
|
+
this.dispatch(this.session.notify(JSON.stringify(event)));
|
|
6668
7154
|
}
|
|
7155
|
+
} finally {
|
|
7156
|
+
this.flushingNotifications = false;
|
|
6669
7157
|
}
|
|
6670
|
-
this.saved.clear();
|
|
6671
|
-
this.model.updateMatrixWorld(true);
|
|
6672
7158
|
}
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
7159
|
+
};
|
|
7160
|
+
|
|
7161
|
+
// three/character.ts
|
|
7162
|
+
var json2 = (value) => JSON.stringify(value ?? {});
|
|
7163
|
+
var targetKey = (target) => JSON.stringify([target.kind, target.meshId, target.morphTargetId, target.boneId, target.objectId, target.property]);
|
|
7164
|
+
var copy = (value) => JSON.parse(JSON.stringify(value));
|
|
7165
|
+
var parse = (value) => JSON.parse(value);
|
|
7166
|
+
var finite2 = (value) => typeof value === "number" && Number.isFinite(value);
|
|
7167
|
+
var intensity = (options, fallback = 1) => {
|
|
7168
|
+
const value = options.intensityScale ?? options.intensity ?? options.weight;
|
|
7169
|
+
return finite2(value) ? Math.max(0, Math.min(2, value)) : fallback;
|
|
7170
|
+
};
|
|
7171
|
+
var presetId = (value) => value?.trim().toLowerCase() || "cc4";
|
|
7172
|
+
function supportsRustEmbodyPreset(value) {
|
|
7173
|
+
return requireInitializedEmbodyCore().has_preset(presetId(value));
|
|
7174
|
+
}
|
|
7175
|
+
var Embody = class _Embody {
|
|
7176
|
+
constructor(config = {}) {
|
|
7177
|
+
this.config = config;
|
|
7178
|
+
__publicField(this, "meshes", []);
|
|
7179
|
+
__publicField(this, "meshByName", /* @__PURE__ */ new Map());
|
|
7180
|
+
__publicField(this, "bones", {});
|
|
7181
|
+
__publicField(this, "wasm", requireInitializedEmbodyCore());
|
|
7182
|
+
__publicField(this, "core");
|
|
7183
|
+
__publicField(this, "inspector", new ThreeModelInspector());
|
|
7184
|
+
__publicField(this, "applier", new ThreeFrameApplier());
|
|
7185
|
+
__publicField(this, "references", /* @__PURE__ */ new WeakMap());
|
|
7186
|
+
__publicField(this, "referenceBindings", /* @__PURE__ */ new Map());
|
|
7187
|
+
__publicField(this, "boundModel");
|
|
7188
|
+
__publicField(this, "inspection");
|
|
7189
|
+
__publicField(this, "connector");
|
|
7190
|
+
__publicField(this, "profile");
|
|
7191
|
+
__publicField(this, "profileSource");
|
|
7192
|
+
__publicField(this, "profileMode");
|
|
7193
|
+
__publicField(this, "preset");
|
|
7194
|
+
__publicField(this, "sources", /* @__PURE__ */ new Map());
|
|
7195
|
+
__publicField(this, "playbacks", /* @__PURE__ */ new Map());
|
|
7196
|
+
__publicField(this, "current", /* @__PURE__ */ new Map());
|
|
7197
|
+
__publicField(this, "playDepth", 0);
|
|
7198
|
+
__publicField(this, "pendingEvents", []);
|
|
7199
|
+
__publicField(this, "bindingGeneration", 0);
|
|
7200
|
+
__publicField(this, "disposed", false);
|
|
7201
|
+
__publicField(this, "rendering", true);
|
|
7202
|
+
__publicField(this, "timeScale", 1);
|
|
7203
|
+
__publicField(this, "nextLiveTarget", 0);
|
|
7204
|
+
__publicField(this, "liveTargets", /* @__PURE__ */ new Map());
|
|
7205
|
+
__publicField(this, "hairObjects", /* @__PURE__ */ new Map());
|
|
7206
|
+
__publicField(this, "hairConfig", {});
|
|
7207
|
+
__publicField(this, "hairAppearance", {
|
|
7208
|
+
hairColor: copy(DEFAULT_HAIR_COLOR_APPEARANCE),
|
|
7209
|
+
eyebrowColor: copy(DEFAULT_HAIR_COLOR_APPEARANCE),
|
|
7210
|
+
showOutline: false,
|
|
7211
|
+
outlineColor: "#000000",
|
|
7212
|
+
outlineOpacity: 1
|
|
6677
7213
|
});
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
7214
|
+
const mode = config.profileMode ?? "preset";
|
|
7215
|
+
if (mode !== "profile" && mode !== "preset") throw new Error(`Unknown profile mode: ${mode}`);
|
|
7216
|
+
if (mode === "profile" && !config.profile) throw new Error("Resolved profile mode requires a profile object.");
|
|
7217
|
+
this.profileMode = mode;
|
|
7218
|
+
this.preset = presetId(config.presetId ?? config.presetType);
|
|
7219
|
+
if (mode === "preset" && !this.wasm.has_preset(this.preset)) throw new Error(`Unknown Embody preset: ${this.preset}`);
|
|
7220
|
+
this.profileSource = this.validateProfile(config.profile ?? {});
|
|
7221
|
+
this.profile = this.resolveProfile(this.profileSource);
|
|
7222
|
+
this.core = new this.wasm.RuntimeCore(0);
|
|
7223
|
+
}
|
|
7224
|
+
get model() {
|
|
7225
|
+
return this.boundModel;
|
|
7226
|
+
}
|
|
7227
|
+
static async create(config = {}) {
|
|
7228
|
+
await initEmbodyCore();
|
|
7229
|
+
return new _Embody(config);
|
|
7230
|
+
}
|
|
7231
|
+
assertAlive() {
|
|
7232
|
+
if (this.disposed) throw new Error("Character is disposed.");
|
|
7233
|
+
}
|
|
7234
|
+
requireModel() {
|
|
7235
|
+
this.assertAlive();
|
|
7236
|
+
if (!this.boundModel) throw new Error("Bind a character model with onReady before using this operation.");
|
|
7237
|
+
return this.boundModel;
|
|
7238
|
+
}
|
|
7239
|
+
requireConnector() {
|
|
7240
|
+
this.requireModel();
|
|
7241
|
+
if (!this.connector) throw new Error("Character playback is not bound.");
|
|
7242
|
+
return this.connector;
|
|
7243
|
+
}
|
|
7244
|
+
validateProfile(value) {
|
|
7245
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Embody profile must be a JSON object.");
|
|
7246
|
+
return copy(value);
|
|
7247
|
+
}
|
|
7248
|
+
resolveProfile(value) {
|
|
7249
|
+
return this.profileMode === "profile" ? copy(value) : parse(this.wasm.merge_embedded_preset(this.preset, json2(value)));
|
|
7250
|
+
}
|
|
7251
|
+
onReady(options) {
|
|
7252
|
+
this.assertAlive();
|
|
7253
|
+
const model = options.model;
|
|
7254
|
+
if (!model?.isObject3D) throw new Error("onReady requires a Three Object3D model.");
|
|
7255
|
+
const reference = this.references.get(model) ?? options.referencePose ?? (!this.boundModel ? this.config.referencePose : void 0) ?? captureModelReferencePose(model);
|
|
7256
|
+
const inspection = this.inspector.inspectModel(model, { meshes: options.meshes, profile: this.profile, referencePose: reference });
|
|
7257
|
+
this.bind(model, reference, inspection, this.profileSource, this.profile);
|
|
7258
|
+
if (options.animations) this.loadAnimationClips(options.animations);
|
|
7259
|
+
}
|
|
7260
|
+
bind(model, reference, inspection, source, profile, preserveLiveState = true) {
|
|
7261
|
+
const referenceBindings = bindModelReferencePose(model, reference);
|
|
7262
|
+
const runtimeDescriptor = {
|
|
7263
|
+
...inspection.descriptor,
|
|
7264
|
+
morphTargets: inspection.descriptor.morphTargets.map((morph) => {
|
|
7265
|
+
const binding = inspection.morphBindings.get(morph.id);
|
|
7266
|
+
const baseline = binding && referenceBindings.get(binding.mesh)?.morphInfluences?.[binding.index];
|
|
7267
|
+
return { ...morph, initialValue: baseline ?? morph.initialValue };
|
|
7268
|
+
})
|
|
6685
7269
|
};
|
|
7270
|
+
const replacingModel = !!this.boundModel && this.boundModel !== model;
|
|
7271
|
+
const core2 = new this.wasm.RuntimeCore(0);
|
|
7272
|
+
try {
|
|
7273
|
+
if (this.profileMode === "profile") core2.configure_with_profile(json2(source), json2(runtimeDescriptor));
|
|
7274
|
+
else core2.configure_with_preset(this.preset, json2(source), json2(runtimeDescriptor));
|
|
7275
|
+
if (preserveLiveState && this.boundModel) core2.copy_live_state_from(this.core);
|
|
7276
|
+
for (const registered of replacingModel ? [] : this.sources.values()) {
|
|
7277
|
+
if (registered.kind === "native") {
|
|
7278
|
+
const [clip] = serializeAnimationClips(model, [registered.clip], inspection);
|
|
7279
|
+
core2.register_animation_clip(json2(clip), "baked");
|
|
7280
|
+
}
|
|
7281
|
+
}
|
|
7282
|
+
} catch (error) {
|
|
7283
|
+
core2.free();
|
|
7284
|
+
throw error;
|
|
7285
|
+
}
|
|
7286
|
+
try {
|
|
7287
|
+
this.connector?.dispose();
|
|
7288
|
+
} catch (error) {
|
|
7289
|
+
core2.free();
|
|
7290
|
+
throw error;
|
|
7291
|
+
}
|
|
7292
|
+
this.core.free();
|
|
7293
|
+
this.core = core2;
|
|
7294
|
+
if (replacingModel) this.sources.clear();
|
|
7295
|
+
this.boundModel = model;
|
|
7296
|
+
this.inspection = inspection;
|
|
7297
|
+
this.referenceBindings = referenceBindings;
|
|
7298
|
+
this.references.set(model, reference);
|
|
7299
|
+
this.profileSource = source;
|
|
7300
|
+
this.profile = profile;
|
|
7301
|
+
this.bindingGeneration += 1;
|
|
7302
|
+
this.playbacks.clear();
|
|
7303
|
+
this.current.clear();
|
|
7304
|
+
this.liveTargets.clear();
|
|
7305
|
+
this.meshes.splice(0, this.meshes.length, ...inspection.morphMeshes.length ? inspection.morphMeshes : inspection.allMeshes);
|
|
7306
|
+
this.meshByName = inspection.meshByName;
|
|
7307
|
+
this.bones = inspection.bones;
|
|
7308
|
+
this.applier.setBindings(inspection);
|
|
7309
|
+
this.applier.applyMeshMaterialConfigs(model, profile.meshes ?? {});
|
|
7310
|
+
const session = new this.wasm.NativePlaybackSession();
|
|
7311
|
+
try {
|
|
7312
|
+
this.connector = new ThreeNativePlaybackConnector(session, {
|
|
7313
|
+
model,
|
|
7314
|
+
inspection,
|
|
7315
|
+
onRelease: (id) => {
|
|
7316
|
+
const playback = this.playbacks.get(id);
|
|
7317
|
+
if (playback?.terminal) this.forget(playback);
|
|
7318
|
+
}
|
|
7319
|
+
});
|
|
7320
|
+
} catch (error) {
|
|
7321
|
+
session.free();
|
|
7322
|
+
throw error;
|
|
7323
|
+
}
|
|
7324
|
+
this.connector.rebind(model.uuid, String(this.bindingGeneration));
|
|
7325
|
+
this.connector.setRendering(this.rendering);
|
|
7326
|
+
this.connector.setTimeScale(this.timeScale);
|
|
7327
|
+
this.connector.subscribe((event) => this.onPlaybackEvent(event));
|
|
7328
|
+
this.hairConfig = parse(this.core.get_hair_config_json());
|
|
7329
|
+
this.registerHairObjects(inspection.allMeshes.filter((mesh) => ["hair", "eyebrow"].includes(this.meshCategory(mesh))));
|
|
7330
|
+
if (preserveLiveState) this.commitLive();
|
|
7331
|
+
}
|
|
7332
|
+
bindModel(meshes) {
|
|
7333
|
+
const model = this.requireModel();
|
|
7334
|
+
const reference = this.references.get(model);
|
|
7335
|
+
const inspection = this.inspector.inspectModel(model, { meshes, profile: this.profile, referencePose: reference });
|
|
7336
|
+
this.bind(model, reference, inspection, this.profileSource, this.profile);
|
|
7337
|
+
}
|
|
7338
|
+
extendReferencePose() {
|
|
7339
|
+
const model = this.requireModel();
|
|
7340
|
+
const reference = extendModelReferencePose(model, this.references.get(model));
|
|
7341
|
+
const inspection = this.inspector.inspectModel(model, { profile: this.profile, referencePose: reference });
|
|
7342
|
+
this.bind(model, reference, inspection, this.profileSource, this.profile);
|
|
7343
|
+
return reference;
|
|
7344
|
+
}
|
|
7345
|
+
setProfile(value) {
|
|
7346
|
+
this.assertAlive();
|
|
7347
|
+
const source = this.validateProfile(value);
|
|
7348
|
+
const profile = this.resolveProfile(source);
|
|
7349
|
+
if (!this.boundModel) {
|
|
7350
|
+
this.profileSource = source;
|
|
7351
|
+
this.profile = profile;
|
|
7352
|
+
return;
|
|
7353
|
+
}
|
|
7354
|
+
const reference = this.references.get(this.boundModel);
|
|
7355
|
+
const inspection = this.inspector.inspectModel(this.boundModel, { profile, referencePose: reference });
|
|
7356
|
+
this.bind(this.boundModel, reference, inspection, source, profile, false);
|
|
6686
7357
|
}
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
return capacity > EPSILON ? bounded(degrees / capacity) : 0;
|
|
7358
|
+
getProfile() {
|
|
7359
|
+
return this.profile;
|
|
6690
7360
|
}
|
|
6691
|
-
|
|
6692
|
-
return
|
|
7361
|
+
getProfileMode() {
|
|
7362
|
+
return this.profileMode;
|
|
6693
7363
|
}
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
const pitch = pitchDegrees * Math.PI / 180;
|
|
6697
|
-
return new Vector3(Math.sin(yaw) * Math.cos(pitch), Math.sin(pitch), Math.cos(yaw) * Math.cos(pitch)).applyQuaternion(this.model.getWorldQuaternion(new Quaternion())).normalize();
|
|
7364
|
+
getPresetId() {
|
|
7365
|
+
return this.profileMode === "preset" ? this.preset : null;
|
|
6698
7366
|
}
|
|
6699
|
-
|
|
6700
|
-
return
|
|
7367
|
+
supportsPreset(value) {
|
|
7368
|
+
return supportsRustEmbodyPreset(value);
|
|
6701
7369
|
}
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
const
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
7370
|
+
onPlaybackEvent(event) {
|
|
7371
|
+
if (this.playDepth) {
|
|
7372
|
+
this.pendingEvents.push(event);
|
|
7373
|
+
return;
|
|
7374
|
+
}
|
|
7375
|
+
const playback = this.playbacks.get(event.handle);
|
|
7376
|
+
if (!playback) return;
|
|
7377
|
+
playback.terminal = true;
|
|
7378
|
+
if (!playback.connector.getAction(playback.id)) {
|
|
7379
|
+
this.playbacks.delete(playback.id);
|
|
7380
|
+
if (this.current.get(playback.source.name) === playback) this.current.delete(playback.source.name);
|
|
7381
|
+
}
|
|
7382
|
+
playback.notify({ ...event, actionId: event.handle, clipName: playback.source.name });
|
|
7383
|
+
}
|
|
7384
|
+
compile(source, options) {
|
|
7385
|
+
const buildOptions = { ..."options" in source ? source.options : {}, ...options, intensityScale: intensity(options) };
|
|
7386
|
+
switch (source.kind) {
|
|
7387
|
+
case "curves":
|
|
7388
|
+
return parse(this.core.build_clip(source.name, json2(source.input), json2(buildOptions)));
|
|
7389
|
+
case "typed":
|
|
7390
|
+
return parse(this.core.build_typed_clip(source.name, json2(source.input), json2(buildOptions)));
|
|
7391
|
+
case "semantic":
|
|
7392
|
+
return parse(this.core.build_semantic_clip(source.name, json2(source.input), json2(buildOptions)));
|
|
7393
|
+
case "compiled":
|
|
7394
|
+
return source.clip;
|
|
7395
|
+
case "native":
|
|
7396
|
+
throw new Error("Native animation sources do not require semantic compilation.");
|
|
7397
|
+
}
|
|
7398
|
+
}
|
|
7399
|
+
restValues(track) {
|
|
7400
|
+
const model = this.requireModel();
|
|
7401
|
+
const parsed = PropertyBinding.parseTrackName(track.name);
|
|
7402
|
+
let object = PropertyBinding.findNode(model, parsed.nodeName);
|
|
7403
|
+
if (parsed.objectName === "bones" && object) {
|
|
7404
|
+
const skeleton = object.skeleton;
|
|
7405
|
+
object = skeleton?.bones.find((bone) => bone.name === String(parsed.objectIndex)) ?? skeleton?.bones[Number(parsed.objectIndex)];
|
|
7406
|
+
}
|
|
7407
|
+
if (!object) return void 0;
|
|
7408
|
+
const reference = this.referenceBindings.get(object);
|
|
7409
|
+
if (!reference) return void 0;
|
|
7410
|
+
switch (parsed.propertyName) {
|
|
7411
|
+
case "quaternion":
|
|
7412
|
+
return Object.values(reference.transform.rotation);
|
|
7413
|
+
case "position":
|
|
7414
|
+
return Object.values(reference.transform.position);
|
|
7415
|
+
case "scale":
|
|
7416
|
+
return Object.values(reference.transform.scale);
|
|
7417
|
+
case "morphTargetInfluences": {
|
|
7418
|
+
const mesh = object;
|
|
7419
|
+
const index = typeof parsed.propertyIndex === "string" && !/^\d+$/.test(parsed.propertyIndex) ? mesh.morphTargetDictionary?.[parsed.propertyIndex] : Number(parsed.propertyIndex);
|
|
7420
|
+
return [reference.morphInfluences?.[index ?? -1] ?? 0];
|
|
7421
|
+
}
|
|
7422
|
+
default:
|
|
7423
|
+
return void 0;
|
|
7424
|
+
}
|
|
7425
|
+
}
|
|
7426
|
+
materialize(source, options, inheritedStarts = /* @__PURE__ */ new Map()) {
|
|
7427
|
+
if (!this.inspection) throw new Error("Character has no model inspection.");
|
|
7428
|
+
const nativeSource = source.kind === "native";
|
|
7429
|
+
let ir;
|
|
7430
|
+
if (!nativeSource) {
|
|
7431
|
+
ir = this.compile(source, options);
|
|
7432
|
+
}
|
|
7433
|
+
const clip = nativeSource ? source.clip.clone() : createAnimationClipFromClipIR(ir, this.inspection);
|
|
7434
|
+
if (ir) {
|
|
7435
|
+
const predecessor = this.current.get(source.name);
|
|
7436
|
+
const outgoing = predecessor?.connector.getAction(predecessor.id);
|
|
7437
|
+
for (let index = 0; index < ir.tracks.length; index += 1) {
|
|
7438
|
+
if (!ir.tracks[index].inheritStart) continue;
|
|
7439
|
+
const track = clip.tracks[index];
|
|
7440
|
+
if (!track) throw new Error("Every inherited track must bind to the character.");
|
|
7441
|
+
const startKey = `${index}:${track.name}`;
|
|
7442
|
+
let previous = inheritedStarts.get(startKey);
|
|
7443
|
+
if (!previous && options.blendMode === "additive") {
|
|
7444
|
+
const rest = this.restValues(track);
|
|
7445
|
+
const candidates = outgoing?.getClip().tracks;
|
|
7446
|
+
const oldTrack = candidates?.[index]?.name === track.name ? candidates[index] : candidates?.find((candidate) => candidate.name === track.name);
|
|
7447
|
+
if (oldTrack && outgoing) {
|
|
7448
|
+
previous = Array.from(oldTrack.createInterpolant().evaluate(outgoing.time));
|
|
7449
|
+
if (outgoing.blendMode === AdditiveAnimationBlendMode && rest) {
|
|
7450
|
+
previous = track.ValueTypeName === "quaternion" ? new Quaternion().fromArray(rest).multiply(new Quaternion().fromArray(previous)).toArray() : previous.map((value, axis) => value + rest[axis]);
|
|
7451
|
+
}
|
|
7452
|
+
} else if (rest) previous = rest;
|
|
7453
|
+
}
|
|
7454
|
+
if (previous) track.values.set(previous, 0);
|
|
7455
|
+
inheritedStarts.set(startKey, previous ?? Array.from(track.values.subarray(0, track.getValueSize())));
|
|
7456
|
+
}
|
|
7457
|
+
}
|
|
7458
|
+
const authoredAdditive = clip.blendMode === AdditiveAnimationBlendMode;
|
|
7459
|
+
const scale = intensity(options);
|
|
7460
|
+
const additive = options.blendMode === "additive" || options.blendMode === void 0 && authoredAdditive;
|
|
7461
|
+
this.applyEasing(clip, options.easing ?? "linear", nativeSource && (scale !== 1 || additive !== authoredAdditive));
|
|
7462
|
+
if (nativeSource && scale !== 1) {
|
|
7463
|
+
for (const track of clip.tracks) {
|
|
7464
|
+
const rest = authoredAdditive ? track.ValueTypeName === "quaternion" ? [0, 0, 0, 1] : Array(track.getValueSize()).fill(0) : this.restValues(track);
|
|
7465
|
+
if (!rest) continue;
|
|
7466
|
+
const stride = track.getValueSize();
|
|
7467
|
+
const cubic = track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true;
|
|
7468
|
+
const width = cubic ? stride / 3 : stride;
|
|
7469
|
+
for (let offset = 0; offset < track.values.length; offset += stride) {
|
|
7470
|
+
const sample = offset + (cubic ? width : 0);
|
|
7471
|
+
if (track.ValueTypeName === "quaternion") {
|
|
7472
|
+
const neutral = new Quaternion().fromArray(rest);
|
|
7473
|
+
const value = new Quaternion().fromArray(track.values, sample);
|
|
7474
|
+
neutral.slerp(value, scale).toArray(track.values, sample);
|
|
7475
|
+
} else {
|
|
7476
|
+
for (let axis = 0; axis < width; axis += 1) {
|
|
7477
|
+
track.values[sample + axis] = rest[axis] + (track.values[sample + axis] - rest[axis]) * scale;
|
|
7478
|
+
if (cubic) {
|
|
7479
|
+
track.values[offset + axis] *= scale;
|
|
7480
|
+
track.values[offset + 2 * width + axis] *= scale;
|
|
7481
|
+
}
|
|
7482
|
+
}
|
|
7483
|
+
}
|
|
7484
|
+
}
|
|
7485
|
+
}
|
|
7486
|
+
}
|
|
7487
|
+
if (additive && !authoredAdditive) {
|
|
7488
|
+
const reference = clip.clone();
|
|
7489
|
+
for (const track of reference.tracks) {
|
|
7490
|
+
const rest = this.restValues(track);
|
|
7491
|
+
if (!rest) continue;
|
|
7492
|
+
const width = track.getValueSize();
|
|
7493
|
+
for (let offset = 0; offset < track.values.length; offset += width) track.values.set(rest, offset);
|
|
7494
|
+
}
|
|
7495
|
+
const explicitReference = options.additiveReferenceTimeSec;
|
|
7496
|
+
if (finite2(explicitReference)) AnimationUtils.makeClipAdditive(clip, explicitReference * 30, clip.clone(), 30);
|
|
7497
|
+
else AnimationUtils.makeClipAdditive(clip, 0, reference, 30);
|
|
7498
|
+
}
|
|
7499
|
+
if (!additive && authoredAdditive) {
|
|
7500
|
+
for (const track of clip.tracks) {
|
|
7501
|
+
const rest = this.restValues(track);
|
|
7502
|
+
if (!rest) continue;
|
|
7503
|
+
const width = track.getValueSize();
|
|
7504
|
+
for (let offset = 0; offset < track.values.length; offset += width) {
|
|
7505
|
+
if (track.ValueTypeName === "quaternion") new Quaternion().fromArray(rest).multiply(new Quaternion().fromArray(track.values, offset)).toArray(track.values, offset);
|
|
7506
|
+
else for (let axis = 0; axis < width; axis += 1) track.values[offset + axis] += rest[axis];
|
|
7507
|
+
}
|
|
7508
|
+
}
|
|
7509
|
+
}
|
|
7510
|
+
clip.blendMode = additive ? AdditiveAnimationBlendMode : NormalAnimationBlendMode;
|
|
7511
|
+
return clip;
|
|
7512
|
+
}
|
|
7513
|
+
applyEasing(clip, easing, resampleCustom = false) {
|
|
7514
|
+
if (easing === "linear" && !resampleCustom) return;
|
|
7515
|
+
const curve = (progress2) => {
|
|
7516
|
+
switch (easing) {
|
|
7517
|
+
case "easeIn":
|
|
7518
|
+
return progress2 * progress2;
|
|
7519
|
+
case "easeOut":
|
|
7520
|
+
return 1 - (1 - progress2) ** 2;
|
|
7521
|
+
case "easeInOut":
|
|
7522
|
+
return progress2 < 0.5 ? 2 * progress2 * progress2 : 1 - (-2 * progress2 + 2) ** 2 / 2;
|
|
7523
|
+
case "easeInOutCubic":
|
|
7524
|
+
return progress2 < 0.5 ? 4 * progress2 ** 3 : 1 - (-2 * progress2 + 2) ** 3 / 2;
|
|
7525
|
+
default:
|
|
7526
|
+
return progress2;
|
|
7527
|
+
}
|
|
7528
|
+
};
|
|
7529
|
+
for (const track of clip.tracks) {
|
|
7530
|
+
if (track.getInterpolation() === InterpolateDiscrete || track.times.length === 0) continue;
|
|
7531
|
+
const customInterpolant = track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true;
|
|
7532
|
+
if (track.times.length === 1) {
|
|
7533
|
+
if (customInterpolant && resampleCustom) {
|
|
7534
|
+
track.values = new Float32Array(track.createInterpolant().evaluate(track.times[0]));
|
|
7535
|
+
track.setInterpolation(InterpolateLinear);
|
|
7536
|
+
}
|
|
7537
|
+
continue;
|
|
7538
|
+
}
|
|
7539
|
+
if ((customInterpolant || track.getInterpolation() !== InterpolateLinear) && easing === "linear" && !resampleCustom) continue;
|
|
7540
|
+
const stride = track.getValueSize();
|
|
7541
|
+
const width = customInterpolant ? stride / 3 : stride;
|
|
7542
|
+
const interpolant = track.createInterpolant();
|
|
7543
|
+
const times = [];
|
|
7544
|
+
const values = [];
|
|
7545
|
+
const append = (time, value) => {
|
|
7546
|
+
times.push(time);
|
|
7547
|
+
for (let axis = 0; axis < width; axis += 1) values.push(value[axis]);
|
|
7548
|
+
};
|
|
7549
|
+
for (let index = 0; index < track.times.length - 1; index += 1) {
|
|
7550
|
+
const start = track.times[index];
|
|
7551
|
+
const end = track.times[index + 1];
|
|
7552
|
+
const offset = index * stride + (customInterpolant ? width : 0);
|
|
7553
|
+
append(start, track.values.subarray(offset, offset + width));
|
|
7554
|
+
if (end <= start) continue;
|
|
7555
|
+
for (let part = 1; part < 16; part += 1) {
|
|
7556
|
+
const progress2 = part / 16;
|
|
7557
|
+
append(start + (end - start) * progress2, interpolant.evaluate(start + (end - start) * curve(progress2)));
|
|
7558
|
+
}
|
|
7559
|
+
}
|
|
7560
|
+
const lastOffset = (track.times.length - 1) * stride + (customInterpolant ? width : 0);
|
|
7561
|
+
append(track.times[track.times.length - 1], track.values.subarray(lastOffset, lastOffset + width));
|
|
7562
|
+
track.times = new Float32Array(times);
|
|
7563
|
+
track.values = new Float32Array(values);
|
|
7564
|
+
if (customInterpolant || track.getInterpolation() !== InterpolateLinear) track.setInterpolation(InterpolateLinear);
|
|
7565
|
+
}
|
|
7566
|
+
}
|
|
7567
|
+
nativeOptions(options, clip) {
|
|
7568
|
+
const mode = clip.duration === 0 ? "once" : options.loopMode ?? (options.loop === true ? "repeat" : "once");
|
|
7569
|
+
const count = finite2(options.repeatCount) && options.repeatCount > 0 ? options.repeatCount : null;
|
|
7570
|
+
let speed = options.speed ?? options.playbackRate ?? options.rate ?? options.timeScale ?? 1;
|
|
7571
|
+
if (options.reverse === true) speed = -Math.abs(speed);
|
|
6763
7572
|
return {
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
7573
|
+
startSeconds: finite2(options.time) ? options.time : finite2(options.offsetSec) ? options.offsetSec : speed < 0 ? clip.duration : 0,
|
|
7574
|
+
speed,
|
|
7575
|
+
weight: finite2(options.mixerWeight) ? options.mixerWeight : 1,
|
|
7576
|
+
blendMode: clip.blendMode === AdditiveAnimationBlendMode ? "additive" : "normal",
|
|
7577
|
+
loop: { mode: mode === "once" ? "once" : mode === "pingpong" || mode === "pingPong" ? "pingPong" : "repeat", count: mode === "once" ? 1 : count },
|
|
7578
|
+
finalPose: options.finalPose === "release" || options.clampWhenFinished === false ? "release" : "hold"
|
|
6770
7579
|
};
|
|
6771
7580
|
}
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
let yaw = 0;
|
|
6796
|
-
let pitch = 0;
|
|
6797
|
-
let best = evaluate(yaw, pitch);
|
|
6798
|
-
for (let iteration = 0; iteration < 24; iteration += 1) {
|
|
6799
|
-
const error = desired.clone().sub(best.direction);
|
|
6800
|
-
if (error.lengthSq() < 1e-14) break;
|
|
6801
|
-
const derivative = (axis) => {
|
|
6802
|
-
const value = axis === 0 ? yaw : pitch;
|
|
6803
|
-
const lo = Math.max(-1, value - 1e-4);
|
|
6804
|
-
const hi = Math.min(1, value + 1e-4);
|
|
6805
|
-
return evaluate(axis === 0 ? hi : yaw, axis === 1 ? hi : pitch).direction.sub(evaluate(axis === 0 ? lo : yaw, axis === 1 ? lo : pitch).direction).divideScalar(hi - lo);
|
|
7581
|
+
validateEasing(options) {
|
|
7582
|
+
if (options.easing !== void 0 && !["linear", "easeIn", "easeOut", "easeInOut", "easeInOutCubic"].includes(options.easing)) throw new Error(`Unknown playback easing: ${options.easing}`);
|
|
7583
|
+
}
|
|
7584
|
+
handle(source, options, autoPlay) {
|
|
7585
|
+
options = { ..."options" in source ? source.options : {}, ...options };
|
|
7586
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
7587
|
+
const generation = this.bindingGeneration;
|
|
7588
|
+
let current;
|
|
7589
|
+
let resolvePromise = () => {
|
|
7590
|
+
};
|
|
7591
|
+
const result = { name: source.name };
|
|
7592
|
+
Object.defineProperty(result, "finished", { get: () => result.promise, enumerable: true });
|
|
7593
|
+
const newPromise = () => {
|
|
7594
|
+
result.promise = new Promise((resolve) => {
|
|
7595
|
+
resolvePromise = resolve;
|
|
7596
|
+
});
|
|
7597
|
+
};
|
|
7598
|
+
newPromise();
|
|
7599
|
+
result.subscribe = (listener) => {
|
|
7600
|
+
const callback = typeof listener === "function" ? listener : (event) => listener.next(event);
|
|
7601
|
+
listeners.add(callback);
|
|
7602
|
+
return () => {
|
|
7603
|
+
listeners.delete(callback);
|
|
6806
7604
|
};
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
const
|
|
6812
|
-
const
|
|
6813
|
-
|
|
6814
|
-
const
|
|
6815
|
-
const
|
|
6816
|
-
const
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
7605
|
+
};
|
|
7606
|
+
const start = () => {
|
|
7607
|
+
if (generation !== this.bindingGeneration) throw new Error("Playback handle belongs to an obsolete model binding.");
|
|
7608
|
+
this.validateEasing(options);
|
|
7609
|
+
const connector = this.requireConnector();
|
|
7610
|
+
const inheritedStarts = /* @__PURE__ */ new Map();
|
|
7611
|
+
const clip = this.materialize(source, options, inheritedStarts);
|
|
7612
|
+
const hairMotion = this.authoredHeadMotion(clip);
|
|
7613
|
+
const previous = this.current.get(source.name);
|
|
7614
|
+
const settings = this.nativeOptions(options, clip);
|
|
7615
|
+
if (previous && connector.getAction(previous.id)) settings.replace = previous.id;
|
|
7616
|
+
if (current) newPromise();
|
|
7617
|
+
const resolve = resolvePromise;
|
|
7618
|
+
let settled = false;
|
|
7619
|
+
this.playDepth += 1;
|
|
7620
|
+
let id;
|
|
7621
|
+
try {
|
|
7622
|
+
id = connector.playNativeClip(clip, settings);
|
|
7623
|
+
} catch (error) {
|
|
7624
|
+
this.playDepth -= 1;
|
|
7625
|
+
while (!this.playDepth && this.pendingEvents.length) this.onPlaybackEvent(this.pendingEvents.shift());
|
|
7626
|
+
throw error;
|
|
7627
|
+
}
|
|
7628
|
+
const playback = {
|
|
7629
|
+
id,
|
|
7630
|
+
source,
|
|
7631
|
+
options: { ...options, intensityScale: intensity(options) },
|
|
7632
|
+
connector,
|
|
7633
|
+
terminal: false,
|
|
7634
|
+
inheritedStarts,
|
|
7635
|
+
replay: (next) => {
|
|
7636
|
+
options = next;
|
|
7637
|
+
return start();
|
|
7638
|
+
},
|
|
7639
|
+
notify: (event) => {
|
|
7640
|
+
if (settled) return;
|
|
7641
|
+
settled = true;
|
|
7642
|
+
resolve(event);
|
|
7643
|
+
for (const listener of [...listeners]) {
|
|
7644
|
+
try {
|
|
7645
|
+
listener(event);
|
|
7646
|
+
} catch (error) {
|
|
7647
|
+
queueMicrotask(() => {
|
|
7648
|
+
throw error;
|
|
7649
|
+
});
|
|
7650
|
+
}
|
|
7651
|
+
}
|
|
6829
7652
|
}
|
|
7653
|
+
};
|
|
7654
|
+
current = playback;
|
|
7655
|
+
result.actionId = id;
|
|
7656
|
+
this.playbacks.set(id, playback);
|
|
7657
|
+
this.current.set(source.name, playback);
|
|
7658
|
+
const ensure = () => {
|
|
7659
|
+
this.assertAlive();
|
|
7660
|
+
if (this.connector !== connector || !connector.getAction(id)) throw new Error("Unknown, released or obsolete playback handle.");
|
|
7661
|
+
};
|
|
7662
|
+
const control = (command) => {
|
|
7663
|
+
ensure();
|
|
7664
|
+
connector.control(id, command);
|
|
7665
|
+
if (command.type === "stop") this.forget(playback);
|
|
7666
|
+
return result;
|
|
7667
|
+
};
|
|
7668
|
+
result.stop = result.cancel = () => {
|
|
7669
|
+
if (this.connector !== connector || !connector.getAction(id)) return result;
|
|
7670
|
+
ensure();
|
|
7671
|
+
const seconds = finite2(playback.options.fadeOutSec) ? playback.options.fadeOutSec : 0;
|
|
7672
|
+
if (seconds > 0 && !playback.terminal) connector.fadeOut(id, seconds);
|
|
7673
|
+
else control({ type: "stop" });
|
|
7674
|
+
return result;
|
|
7675
|
+
};
|
|
7676
|
+
result.pause = () => control({ type: "pause" });
|
|
7677
|
+
result.resume = () => {
|
|
7678
|
+
ensure();
|
|
7679
|
+
if (playback.terminal) {
|
|
7680
|
+
options = { ...playback.options, time: connector.getAction(id).time };
|
|
7681
|
+
return start();
|
|
7682
|
+
}
|
|
7683
|
+
return control({ type: "resume" });
|
|
7684
|
+
};
|
|
7685
|
+
result.setSpeed = result.setPlaybackRate = (speed) => {
|
|
7686
|
+
ensure();
|
|
7687
|
+
this.updatePlayback(playback, { speed });
|
|
7688
|
+
return result;
|
|
7689
|
+
};
|
|
7690
|
+
result.setTime = result.seek = (seconds) => control({ type: "seek", seconds });
|
|
7691
|
+
result.setWeight = result.setIntensity = (value) => {
|
|
7692
|
+
ensure();
|
|
7693
|
+
this.updatePlayback(playback, { intensityScale: value });
|
|
7694
|
+
return result;
|
|
7695
|
+
};
|
|
7696
|
+
result.updateParams = result.setParams = (value) => {
|
|
7697
|
+
ensure();
|
|
7698
|
+
this.updatePlayback(playback, value);
|
|
7699
|
+
return result;
|
|
7700
|
+
};
|
|
7701
|
+
try {
|
|
7702
|
+
const fade = options.fadeInSec ?? options.crossfadeDuration;
|
|
7703
|
+
if (finite2(fade) && fade > 0) connector.fadeIn(id, fade);
|
|
7704
|
+
} finally {
|
|
7705
|
+
this.playDepth -= 1;
|
|
7706
|
+
while (!this.playDepth && this.pendingEvents.length) this.onPlaybackEvent(this.pendingEvents.shift());
|
|
7707
|
+
}
|
|
7708
|
+
if (hairMotion && connector.getAction(id)) {
|
|
7709
|
+
this.playHairImpulse(hairMotion.horizontal, hairMotion.vertical);
|
|
7710
|
+
this.setHairGravity(hairMotion.pitch, clip.duration);
|
|
7711
|
+
}
|
|
7712
|
+
return result;
|
|
7713
|
+
};
|
|
7714
|
+
result.play = () => {
|
|
7715
|
+
if (current) options = { ...current.options };
|
|
7716
|
+
return start();
|
|
7717
|
+
};
|
|
7718
|
+
const beforePlay = () => {
|
|
7719
|
+
throw new Error("Play this compiled clip before changing playback.");
|
|
7720
|
+
};
|
|
7721
|
+
result.stop = result.cancel = () => result;
|
|
7722
|
+
result.pause = result.resume = beforePlay;
|
|
7723
|
+
result.updateParams = result.setParams = (patch) => {
|
|
7724
|
+
this.assertAlive();
|
|
7725
|
+
this.validateEasing(patch);
|
|
7726
|
+
options = { ...options, ...patch };
|
|
7727
|
+
if ("finalPose" in patch && !("clampWhenFinished" in patch)) options.clampWhenFinished = patch.finalPose !== "release";
|
|
7728
|
+
if ("clampWhenFinished" in patch && !("finalPose" in patch)) options.finalPose = patch.clampWhenFinished === false ? "release" : "hold";
|
|
7729
|
+
if ("loop" in patch && !("loopMode" in patch)) options.loopMode = patch.loop ? "repeat" : "once";
|
|
7730
|
+
if ("loopMode" in patch && !("repeatCount" in patch)) delete options.repeatCount;
|
|
7731
|
+
if ("weight" in patch || "intensity" in patch || "intensityScale" in patch) options.intensityScale = intensity(patch);
|
|
7732
|
+
if ("time" in patch || "offsetSec" in patch) options.time = patch.time ?? patch.offsetSec;
|
|
7733
|
+
if ("speed" in patch || "playbackRate" in patch || "rate" in patch || "timeScale" in patch || "reverse" in patch) {
|
|
7734
|
+
let speed = patch.speed ?? patch.playbackRate ?? patch.rate ?? patch.timeScale ?? options.speed ?? options.playbackRate ?? options.rate ?? options.timeScale ?? 1;
|
|
7735
|
+
if ("reverse" in patch) speed = Math.abs(speed) * (patch.reverse ? -1 : 1);
|
|
7736
|
+
options.speed = speed;
|
|
7737
|
+
delete options.reverse;
|
|
7738
|
+
}
|
|
7739
|
+
return result;
|
|
7740
|
+
};
|
|
7741
|
+
result.setWeight = result.setIntensity = (value) => result.updateParams({ intensityScale: value });
|
|
7742
|
+
result.setSpeed = result.setPlaybackRate = (speed) => result.updateParams({ speed });
|
|
7743
|
+
result.setTime = result.seek = (time) => result.updateParams({ time });
|
|
7744
|
+
return autoPlay ? start() : result;
|
|
7745
|
+
}
|
|
7746
|
+
forget(playback) {
|
|
7747
|
+
this.playbacks.delete(playback.id);
|
|
7748
|
+
if (this.current.get(playback.source.name) === playback) this.current.delete(playback.source.name);
|
|
7749
|
+
}
|
|
7750
|
+
updatePlayback(playback, patch) {
|
|
7751
|
+
this.validateEasing(patch);
|
|
7752
|
+
const connector = playback.connector;
|
|
7753
|
+
const action = connector.getAction(playback.id);
|
|
7754
|
+
if (connector !== this.connector || !action) throw new Error("Unknown, released or obsolete playback handle.");
|
|
7755
|
+
const options = { ...playback.options, ...patch };
|
|
7756
|
+
if ("finalPose" in patch && !("clampWhenFinished" in patch)) options.clampWhenFinished = patch.finalPose !== "release";
|
|
7757
|
+
if ("clampWhenFinished" in patch && !("finalPose" in patch)) options.finalPose = patch.clampWhenFinished === false ? "release" : "hold";
|
|
7758
|
+
if ("loop" in patch && !("loopMode" in patch)) options.loopMode = patch.loop ? "repeat" : "once";
|
|
7759
|
+
if ("loopMode" in patch && !("repeatCount" in patch)) delete options.repeatCount;
|
|
7760
|
+
if ("weight" in patch || "intensity" in patch || "intensityScale" in patch) options.intensityScale = intensity(patch);
|
|
7761
|
+
if (["weight", "intensity", "intensityScale", "blendMode", "easing", "balance", "balanceMap", "mixWeights"].some((key) => key in patch)) {
|
|
7762
|
+
const clip = this.materialize(playback.source, options, playback.inheritedStarts);
|
|
7763
|
+
connector.replaceClip(playback.id, clip);
|
|
7764
|
+
if ("blendMode" in patch) connector.control(playback.id, { type: "setBlendMode", blendMode: clip.blendMode === AdditiveAnimationBlendMode ? "additive" : "normal" });
|
|
7765
|
+
}
|
|
7766
|
+
if ("mixerWeight" in patch) connector.control(playback.id, { type: "setWeight", weight: patch.mixerWeight });
|
|
7767
|
+
if ("speed" in patch || "playbackRate" in patch || "rate" in patch || "timeScale" in patch || "reverse" in patch) {
|
|
7768
|
+
let speed = patch.speed ?? patch.playbackRate ?? patch.rate ?? patch.timeScale ?? action.timeScale;
|
|
7769
|
+
if ("reverse" in patch) speed = Math.abs(speed) * (patch.reverse ? -1 : 1);
|
|
7770
|
+
connector.control(playback.id, { type: "setSpeed", speed });
|
|
7771
|
+
options.speed = speed;
|
|
7772
|
+
delete options.reverse;
|
|
7773
|
+
}
|
|
7774
|
+
if ("time" in patch || "offsetSec" in patch) {
|
|
7775
|
+
options.time = patch.time ?? patch.offsetSec;
|
|
7776
|
+
connector.control(playback.id, { type: "seek", seconds: options.time });
|
|
7777
|
+
}
|
|
7778
|
+
if ("loopMode" in patch || "loop" in patch || "repeatCount" in patch) {
|
|
7779
|
+
if (playback.terminal) {
|
|
7780
|
+
playback.replay({ ...options, time: action.time });
|
|
7781
|
+
return;
|
|
6830
7782
|
}
|
|
6831
|
-
|
|
7783
|
+
connector.control(playback.id, { type: "setLoop", loop: this.nativeOptions(options, action.getClip()).loop });
|
|
6832
7784
|
}
|
|
6833
|
-
|
|
7785
|
+
if ("clampWhenFinished" in patch || "finalPose" in patch) connector.control(playback.id, { type: "setFinalPose", finalPose: this.nativeOptions(options, action.getClip()).finalPose });
|
|
7786
|
+
playback.options = options;
|
|
7787
|
+
if (!connector.getAction(playback.id)) this.forget(playback);
|
|
6834
7788
|
}
|
|
6835
|
-
|
|
6836
|
-
const
|
|
6837
|
-
const
|
|
6838
|
-
|
|
6839
|
-
|
|
7789
|
+
registerAnimationClips(clips) {
|
|
7790
|
+
const model = this.requireModel();
|
|
7791
|
+
for (const clip of clips) {
|
|
7792
|
+
if (this.sources.get(clip.name)?.kind === "native" && this.sources.get(clip.name).clip === clip) continue;
|
|
7793
|
+
const [ir] = serializeAnimationClips(model, [clip], this.inspection);
|
|
7794
|
+
this.core.register_animation_clip(json2(ir), "baked");
|
|
7795
|
+
this.sources.set(clip.name, { name: clip.name, kind: "native", clip });
|
|
7796
|
+
}
|
|
6840
7797
|
}
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
7798
|
+
loadAnimationClips(clips) {
|
|
7799
|
+
this.stopAllAnimations();
|
|
7800
|
+
for (const source of [...this.sources.values()]) if (source.kind === "native") {
|
|
7801
|
+
this.sources.delete(source.name);
|
|
7802
|
+
this.core.remove_animation_clip(source.name);
|
|
7803
|
+
}
|
|
7804
|
+
this.registerAnimationClips(clips ?? []);
|
|
7805
|
+
this.restartHair();
|
|
6847
7806
|
}
|
|
6848
|
-
|
|
6849
|
-
this.
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
const
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
}
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
7807
|
+
removeAnimationClip(name) {
|
|
7808
|
+
this.stopAnimation(name);
|
|
7809
|
+
this.sources.delete(name);
|
|
7810
|
+
return this.core.remove_animation_clip(name);
|
|
7811
|
+
}
|
|
7812
|
+
getAnimationClips() {
|
|
7813
|
+
return parse(this.core.get_animation_clips());
|
|
7814
|
+
}
|
|
7815
|
+
playAnimation(name, options = {}) {
|
|
7816
|
+
const source = this.sources.get(name);
|
|
7817
|
+
if (!source) throw new Error(`Unknown animation clip: ${name}`);
|
|
7818
|
+
return this.handle(source, options, true);
|
|
7819
|
+
}
|
|
7820
|
+
build(source, options, play) {
|
|
7821
|
+
this.requireModel();
|
|
7822
|
+
this.validateEasing(options);
|
|
7823
|
+
this.compile(source, options);
|
|
7824
|
+
this.sources.set(source.name, source);
|
|
7825
|
+
return this.handle(source, options, play);
|
|
7826
|
+
}
|
|
7827
|
+
buildClip(name, curves, options = {}) {
|
|
7828
|
+
return options.semanticAnimation ? this.semanticAnimationToClip(name, options.semanticAnimation, { ...options, faceCurves: curves }) : this.build({ name, kind: "curves", input: copy(curves), options: copy(options) }, options, false);
|
|
7829
|
+
}
|
|
7830
|
+
snippetToClip(name, curves, options = {}) {
|
|
7831
|
+
return this.buildClip(name, curves, options);
|
|
7832
|
+
}
|
|
7833
|
+
buildTypedClip(name, channels, options = {}) {
|
|
7834
|
+
return this.build({ name, kind: "typed", input: copy(channels), options: copy(options) }, options, false);
|
|
7835
|
+
}
|
|
7836
|
+
typedSnippetToClip(name, channels, options = {}) {
|
|
7837
|
+
return this.buildTypedClip(name, channels, options);
|
|
7838
|
+
}
|
|
7839
|
+
semanticAnimationToClip(name, animation, options = {}) {
|
|
7840
|
+
return this.build({ name, kind: "semantic", input: copy(animation), options: copy(options) }, options, false);
|
|
7841
|
+
}
|
|
7842
|
+
playSnippet(snippet, curvesOrOptions = {}, options = {}) {
|
|
7843
|
+
return typeof snippet === "string" ? this.buildClip(snippet, curvesOrOptions, options).play() : this.buildClip(snippet.name, snippet.curves, curvesOrOptions).play();
|
|
7844
|
+
}
|
|
7845
|
+
playTypedSnippet(snippet, options = {}) {
|
|
7846
|
+
return this.buildTypedClip(snippet.name, snippet.channels, options).play();
|
|
7847
|
+
}
|
|
7848
|
+
playClip(value) {
|
|
7849
|
+
return typeof value === "string" ? this.playAnimation(value) : value.play();
|
|
7850
|
+
}
|
|
7851
|
+
supportsClipCurves() {
|
|
7852
|
+
return true;
|
|
7853
|
+
}
|
|
7854
|
+
cleanupSnippet(name) {
|
|
7855
|
+
this.stopAnimation(name);
|
|
7856
|
+
this.sources.delete(name);
|
|
7857
|
+
this.core.cleanup_snippet(name);
|
|
7858
|
+
}
|
|
7859
|
+
crossfadeTo(name, seconds, options = {}) {
|
|
7860
|
+
const outgoing = [...this.current.values()].filter((playback) => !playback.source.name.startsWith("__embody_"));
|
|
7861
|
+
const handle = this.playAnimation(name, { ...options, fadeInSec: seconds });
|
|
7862
|
+
for (const playback of outgoing) if (playback.connector.getAction(playback.id)) playback.connector.fadeOut(playback.id, seconds);
|
|
7863
|
+
return handle;
|
|
7864
|
+
}
|
|
7865
|
+
stopAnimation(name) {
|
|
7866
|
+
const playback = this.current.get(name);
|
|
7867
|
+
if (!playback) return;
|
|
7868
|
+
if (playback.connector.getAction(playback.id)) playback.connector.control(playback.id, { type: "stop" });
|
|
7869
|
+
this.forget(playback);
|
|
7870
|
+
}
|
|
7871
|
+
stopAllAnimations() {
|
|
7872
|
+
for (const playback of [...this.playbacks.values()]) {
|
|
7873
|
+
if (playback.source.kind === "compiled") continue;
|
|
7874
|
+
if (playback.connector.getAction(playback.id)) playback.connector.control(playback.id, { type: "stop" });
|
|
7875
|
+
this.forget(playback);
|
|
7876
|
+
}
|
|
7877
|
+
}
|
|
7878
|
+
pauseAnimation(name) {
|
|
7879
|
+
const p = this.current.get(name);
|
|
7880
|
+
if (p) p.connector.control(p.id, { type: "pause" });
|
|
7881
|
+
}
|
|
7882
|
+
resumeAnimation(name) {
|
|
7883
|
+
const p = this.current.get(name);
|
|
7884
|
+
if (!p) return;
|
|
7885
|
+
if (p.terminal) p.replay({ ...p.options, time: p.connector.getAction(p.id)?.time ?? 0 });
|
|
7886
|
+
else p.connector.control(p.id, { type: "resume" });
|
|
7887
|
+
}
|
|
7888
|
+
pauseAllAnimations() {
|
|
7889
|
+
for (const p of this.playbacks.values()) if (!p.terminal) p.connector.control(p.id, { type: "pause" });
|
|
7890
|
+
}
|
|
7891
|
+
resumeAllAnimations() {
|
|
7892
|
+
for (const name of [...this.current.keys()]) this.resumeAnimation(name);
|
|
7893
|
+
}
|
|
7894
|
+
updateClipParams(name, options) {
|
|
7895
|
+
const p = this.current.get(name);
|
|
7896
|
+
if (p) this.updatePlayback(p, options);
|
|
7897
|
+
}
|
|
7898
|
+
setAnimationSpeed(name, value) {
|
|
7899
|
+
this.updateClipParams(name, { speed: value });
|
|
7900
|
+
}
|
|
7901
|
+
setAnimationIntensity(name, value) {
|
|
7902
|
+
this.updateClipParams(name, { intensityScale: value });
|
|
7903
|
+
}
|
|
7904
|
+
setAnimationLoop(name, value) {
|
|
7905
|
+
this.updateClipParams(name, { loopMode: value ? "repeat" : "once" });
|
|
7906
|
+
}
|
|
7907
|
+
setAnimationLoopMode(name, value) {
|
|
7908
|
+
this.updateClipParams(name, { loopMode: value });
|
|
7909
|
+
}
|
|
7910
|
+
setAnimationRepeatCount(name, value) {
|
|
7911
|
+
this.updateClipParams(name, { loopMode: "repeat", repeatCount: value });
|
|
7912
|
+
}
|
|
7913
|
+
setAnimationReverse(name, value) {
|
|
7914
|
+
this.updateClipParams(name, { reverse: value });
|
|
7915
|
+
}
|
|
7916
|
+
setAnimationBlendMode(name, value) {
|
|
7917
|
+
this.updateClipParams(name, { blendMode: value });
|
|
7918
|
+
}
|
|
7919
|
+
setAnimationEasing(name, value) {
|
|
7920
|
+
this.validateEasing({ easing: value });
|
|
7921
|
+
this.updateClipParams(name, { easing: value });
|
|
7922
|
+
}
|
|
7923
|
+
seekAnimation(name, value) {
|
|
7924
|
+
this.updateClipParams(name, { time: value });
|
|
7925
|
+
}
|
|
7926
|
+
setAnimationTime(name, value) {
|
|
7927
|
+
this.seekAnimation(name, value);
|
|
7928
|
+
}
|
|
7929
|
+
setSnippetTime(name, value) {
|
|
7930
|
+
this.seekAnimation(name, value);
|
|
7931
|
+
}
|
|
7932
|
+
seekSnippet(name, value) {
|
|
7933
|
+
this.seekAnimation(name, value);
|
|
7934
|
+
}
|
|
7935
|
+
seek(name, value) {
|
|
7936
|
+
this.seekAnimation(name, value);
|
|
7937
|
+
}
|
|
7938
|
+
setAnimationTimeScale(value) {
|
|
7939
|
+
this.requireConnector().setTimeScale(value);
|
|
7940
|
+
this.timeScale = value;
|
|
7941
|
+
}
|
|
7942
|
+
getAnimationState(name) {
|
|
7943
|
+
const p = this.current.get(name);
|
|
7944
|
+
const action = p?.connector.getAction(p.id);
|
|
7945
|
+
if (!action || !p) {
|
|
7946
|
+
if (p) this.forget(p);
|
|
7947
|
+
return { name, playing: false, isPlaying: false };
|
|
7948
|
+
}
|
|
7949
|
+
const playing = action.isRunning();
|
|
7950
|
+
return {
|
|
7951
|
+
name,
|
|
7952
|
+
actionId: p.id,
|
|
7953
|
+
playing,
|
|
7954
|
+
isPlaying: playing,
|
|
7955
|
+
paused: action.paused,
|
|
7956
|
+
completed: p.terminal,
|
|
7957
|
+
time: action.time,
|
|
7958
|
+
duration: action.getClip().duration,
|
|
7959
|
+
speed: action.timeScale,
|
|
7960
|
+
intensityScale: intensity(p.options),
|
|
7961
|
+
mixerWeight: action.getEffectiveWeight(),
|
|
7962
|
+
loopMode: action.loop === LoopOnce ? "once" : action.loop === LoopPingPong ? "pingpong" : "repeat",
|
|
7963
|
+
repeatCount: Number.isFinite(action.repetitions) ? action.repetitions : null,
|
|
7964
|
+
blendMode: action.blendMode === AdditiveAnimationBlendMode ? "additive" : "replace"
|
|
7965
|
+
};
|
|
7966
|
+
}
|
|
7967
|
+
getPlayingAnimations() {
|
|
7968
|
+
return [...this.current.keys()].filter((name) => this.getAnimationState(name).playing);
|
|
7969
|
+
}
|
|
7970
|
+
commitLive(durationMs = 0) {
|
|
7971
|
+
const connector = this.requireConnector();
|
|
7972
|
+
const duration = Math.max(0, durationMs) / 1e3;
|
|
7973
|
+
const clip = parse(this.core.build_live_clip("__embody_live", duration));
|
|
7974
|
+
const touched = new Set(parse(this.core.live_command_targets_json()).map(targetKey));
|
|
7975
|
+
const tracks = new Map(clip.tracks.map((track) => [targetKey(track.target), track]));
|
|
7976
|
+
const handles = [];
|
|
7977
|
+
for (const [key, previous] of this.liveTargets) {
|
|
7978
|
+
if (tracks.has(key)) continue;
|
|
7979
|
+
if (previous.releasing && !touched.has(key)) continue;
|
|
7980
|
+
const action = previous.handle.actionId && connector.getAction(previous.handle.actionId);
|
|
7981
|
+
if (!action || duration === 0) {
|
|
7982
|
+
previous.handle.stop();
|
|
7983
|
+
this.liveTargets.delete(key);
|
|
7984
|
+
continue;
|
|
7985
|
+
}
|
|
7986
|
+
const nativeTrack = action.getClip().tracks[0];
|
|
7987
|
+
const sample = Array.from(nativeTrack.createInterpolant().evaluate(action.time));
|
|
7988
|
+
const released = { ...previous.track, times: [0, duration], values: [...sample, ...sample], inheritStart: false };
|
|
7989
|
+
const releaseClip = { ...clip, name: previous.handle.name, tracks: [released] };
|
|
7990
|
+
const handle = this.handle(
|
|
7991
|
+
{ name: releaseClip.name, kind: "compiled", clip: releaseClip },
|
|
7992
|
+
{ loopMode: "once", finalPose: "release", mixerWeight: action.getEffectiveWeight() },
|
|
7993
|
+
true
|
|
7994
|
+
);
|
|
7995
|
+
connector.fadeOut(handle.actionId, duration);
|
|
7996
|
+
const record = { track: released, handle, releasing: true };
|
|
7997
|
+
this.liveTargets.set(key, record);
|
|
7998
|
+
void handle.promise.then(() => {
|
|
7999
|
+
if (this.liveTargets.get(key) === record) this.liveTargets.delete(key);
|
|
6882
8000
|
});
|
|
8001
|
+
handles.push(handle);
|
|
8002
|
+
}
|
|
8003
|
+
for (const [key, track] of tracks) {
|
|
8004
|
+
const previous = this.liveTargets.get(key);
|
|
8005
|
+
const endpoint = (value) => value.values.slice(value.values.length / value.times.length * (value.times.length - 1));
|
|
8006
|
+
if (previous && !previous.releasing && !touched.has(key) && json2(endpoint(previous.track)) === json2(endpoint(track))) continue;
|
|
8007
|
+
const name = previous?.handle.name ?? `__embody_live_${this.nextLiveTarget++}`;
|
|
8008
|
+
const outgoing = previous?.handle.actionId && connector.getAction(previous.handle.actionId);
|
|
8009
|
+
let nativeTrack = track;
|
|
8010
|
+
if (duration > 0 && outgoing) {
|
|
8011
|
+
const start = Array.from(outgoing.getClip().tracks[0].createInterpolant().evaluate(outgoing.time));
|
|
8012
|
+
nativeTrack = { ...track, values: [...start, ...endpoint(track)], inheritStart: false };
|
|
8013
|
+
}
|
|
8014
|
+
const targetClip = { ...clip, name, tracks: [nativeTrack] };
|
|
8015
|
+
const handle = this.handle({ name, kind: "compiled", clip: targetClip }, { loopMode: "once", finalPose: "hold" }, true);
|
|
8016
|
+
this.liveTargets.set(key, { track, handle, releasing: false });
|
|
8017
|
+
handles.push(handle);
|
|
8018
|
+
}
|
|
8019
|
+
return handles;
|
|
8020
|
+
}
|
|
8021
|
+
mutateLive(mutate, durationMs = 0) {
|
|
8022
|
+
this.requireModel();
|
|
8023
|
+
this.core.begin_live_command();
|
|
8024
|
+
mutate();
|
|
8025
|
+
return this.commitLive(durationMs);
|
|
8026
|
+
}
|
|
8027
|
+
transition(handles) {
|
|
8028
|
+
let settled = handles.length === 0;
|
|
8029
|
+
const promise = Promise.all(handles.map((handle) => handle.promise)).then(() => {
|
|
8030
|
+
settled = true;
|
|
8031
|
+
});
|
|
8032
|
+
const control = (command) => {
|
|
8033
|
+
if (settled) return;
|
|
8034
|
+
for (const handle of handles) {
|
|
8035
|
+
const playback = handle.actionId && this.playbacks.get(handle.actionId);
|
|
8036
|
+
if (!playback || !playback.connector.getAction(playback.id)) continue;
|
|
8037
|
+
if (command === "cancel" || !playback.terminal) handle[command]();
|
|
8038
|
+
}
|
|
8039
|
+
};
|
|
8040
|
+
return { promise, pause: () => control("pause"), resume: () => control("resume"), cancel: () => control("cancel") };
|
|
8041
|
+
}
|
|
8042
|
+
setAU(id, value, balance = this.core.get_au_balance(id)) {
|
|
8043
|
+
this.mutateLive(() => this.core.set_au_signed(id, value, balance));
|
|
8044
|
+
}
|
|
8045
|
+
getAU(id) {
|
|
8046
|
+
return this.core.get_au(id);
|
|
8047
|
+
}
|
|
8048
|
+
getAUBalance(id) {
|
|
8049
|
+
return this.core.get_au_balance(id);
|
|
8050
|
+
}
|
|
8051
|
+
getBodyControls() {
|
|
8052
|
+
return parse(this.core.get_body_controls_json());
|
|
8053
|
+
}
|
|
8054
|
+
getSemanticPoseCatalog() {
|
|
8055
|
+
return parse(this.core.get_semantic_pose_catalog_json());
|
|
8056
|
+
}
|
|
8057
|
+
captureSemanticPose() {
|
|
8058
|
+
return parse(this.core.capture_semantic_pose_json());
|
|
8059
|
+
}
|
|
8060
|
+
validateSemanticPose(pose) {
|
|
8061
|
+
return parse(this.core.validate_semantic_pose_json(json2(pose)));
|
|
8062
|
+
}
|
|
8063
|
+
applySemanticPose(pose) {
|
|
8064
|
+
this.mutateLive(() => this.core.apply_semantic_pose_json(json2(pose)));
|
|
8065
|
+
}
|
|
8066
|
+
validateSemanticAnimation(animation, options = {}) {
|
|
8067
|
+
this.core.validate_semantic_animation_json(json2(animation), json2(options));
|
|
8068
|
+
}
|
|
8069
|
+
resetBodyControls() {
|
|
8070
|
+
this.mutateLive(() => this.core.reset_body_controls());
|
|
8071
|
+
}
|
|
8072
|
+
setContinuum(negative, positive, value, balance = NaN) {
|
|
8073
|
+
this.mutateLive(() => this.core.set_continuum(negative, positive, value, balance));
|
|
8074
|
+
}
|
|
8075
|
+
getContinuum(negative, positive) {
|
|
8076
|
+
return this.core.get_continuum(negative, positive);
|
|
8077
|
+
}
|
|
8078
|
+
transitionAU(id, value, durationMs = 200, balance = this.core.get_au_balance(id)) {
|
|
8079
|
+
return this.transition(this.mutateLive(() => this.core.set_au_signed(id, value, balance), durationMs));
|
|
8080
|
+
}
|
|
8081
|
+
setViseme(index, value, jawScale = 1) {
|
|
8082
|
+
this.mutateLive(() => {
|
|
8083
|
+
this.core.set_viseme(index, value);
|
|
8084
|
+
this.core.set_viseme_jaw_scale(index, jawScale);
|
|
8085
|
+
});
|
|
8086
|
+
}
|
|
8087
|
+
getViseme(index) {
|
|
8088
|
+
return this.core.get_viseme(index);
|
|
8089
|
+
}
|
|
8090
|
+
getVisemeJawScale(index) {
|
|
8091
|
+
return this.core.get_viseme_jaw_scale(index);
|
|
8092
|
+
}
|
|
8093
|
+
setVisemeById(id, value, jawScale = 1) {
|
|
8094
|
+
const index = this.core.viseme_slot_index(id);
|
|
8095
|
+
if (index < 0) throw new Error(`Unknown viseme: ${id}`);
|
|
8096
|
+
this.setViseme(index, value, jawScale);
|
|
8097
|
+
}
|
|
8098
|
+
transitionViseme(index, value, durationMs = 80, jawScale = 1) {
|
|
8099
|
+
return this.transition(this.mutateLive(() => {
|
|
8100
|
+
this.core.set_viseme(index, value);
|
|
8101
|
+
this.core.set_viseme_jaw_scale(index, jawScale);
|
|
8102
|
+
}, durationMs));
|
|
8103
|
+
}
|
|
8104
|
+
transitionVisemeById(id, value, durationMs = 80, jawScale = 1) {
|
|
8105
|
+
const index = this.core.viseme_slot_index(id);
|
|
8106
|
+
if (index < 0) return this.transition([]);
|
|
8107
|
+
return this.transitionViseme(index, value, durationMs, jawScale);
|
|
8108
|
+
}
|
|
8109
|
+
setAUMixWeight(id, value) {
|
|
8110
|
+
var _a, _b;
|
|
8111
|
+
const weight = Math.max(0, Math.min(1, value));
|
|
8112
|
+
this.mutateLive(() => this.core.set_au_mix_weight(id, weight));
|
|
8113
|
+
((_a = this.profile).auMixDefaults ?? (_a.auMixDefaults = {}))[id] = weight;
|
|
8114
|
+
((_b = this.profileSource).auMixDefaults ?? (_b.auMixDefaults = {}))[id] = weight;
|
|
8115
|
+
}
|
|
8116
|
+
getAUMixWeight(id) {
|
|
8117
|
+
return this.core.get_au_mix_weight(id);
|
|
8118
|
+
}
|
|
8119
|
+
hasLeftRightBones(id) {
|
|
8120
|
+
return this.core.has_left_right_bones(id);
|
|
8121
|
+
}
|
|
8122
|
+
setMorph(key, value, meshNames = []) {
|
|
8123
|
+
this.mutateLive(() => {
|
|
8124
|
+
if (typeof key === "number") this.core.set_morph_index(key, value, json2(meshNames));
|
|
8125
|
+
else this.core.set_morph(key, value, json2(meshNames));
|
|
8126
|
+
});
|
|
8127
|
+
}
|
|
8128
|
+
setMorphOnMeshes(meshNames, key, value) {
|
|
8129
|
+
this.setMorph(key, value, meshNames);
|
|
8130
|
+
}
|
|
8131
|
+
releaseMorph(key, meshNames = []) {
|
|
8132
|
+
this.mutateLive(() => {
|
|
8133
|
+
if (typeof key === "number") this.core.release_morph_index(key, json2(meshNames));
|
|
8134
|
+
else this.core.release_morph(key, json2(meshNames));
|
|
8135
|
+
});
|
|
8136
|
+
}
|
|
8137
|
+
transitionMorph(key, value, durationMs = 120, meshNames = []) {
|
|
8138
|
+
return this.transition(this.mutateLive(() => {
|
|
8139
|
+
if (typeof key === "number") this.core.set_morph_index(key, value, json2(meshNames));
|
|
8140
|
+
else this.core.set_morph(key, value, json2(meshNames));
|
|
8141
|
+
}, durationMs));
|
|
8142
|
+
}
|
|
8143
|
+
getMorphTargets() {
|
|
8144
|
+
return Object.fromEntries(this.meshes.map((mesh) => [mesh.name, Object.keys(mesh.morphTargetDictionary ?? {}).sort()]));
|
|
8145
|
+
}
|
|
8146
|
+
getMorphTargetIndices() {
|
|
8147
|
+
return Object.fromEntries(this.meshes.map((mesh) => [mesh.name, Object.entries(mesh.morphTargetDictionary ?? {}).map(([name, index]) => ({ name, index })).sort((a, b) => a.index - b.index)]));
|
|
8148
|
+
}
|
|
8149
|
+
addMorphTarget(target, options) {
|
|
8150
|
+
const index = this.applier.addMorphTarget(this.requireModel(), target, options);
|
|
8151
|
+
this.bindModel();
|
|
8152
|
+
return index;
|
|
8153
|
+
}
|
|
8154
|
+
addMorphTargets(targets, options) {
|
|
8155
|
+
const result = this.applier.addMorphTargets(this.requireModel(), targets, options);
|
|
8156
|
+
this.bindModel();
|
|
8157
|
+
return result;
|
|
8158
|
+
}
|
|
8159
|
+
meshCategory(mesh) {
|
|
8160
|
+
return this.profile.meshes?.[mesh.name]?.category ?? "other";
|
|
8161
|
+
}
|
|
8162
|
+
getMeshList() {
|
|
8163
|
+
return (this.inspection?.allMeshes ?? []).map((mesh) => ({ name: mesh.name, visible: mesh.visible, morphCount: mesh.morphTargetInfluences?.length ?? 0, category: this.meshCategory(mesh) }));
|
|
8164
|
+
}
|
|
8165
|
+
getBones() {
|
|
8166
|
+
return this.bones;
|
|
8167
|
+
}
|
|
8168
|
+
meshProfile(name) {
|
|
8169
|
+
var _a, _b;
|
|
8170
|
+
return (_b = (_a = this.profile).meshes ?? (_a.meshes = {}))[name] ?? (_b[name] = { category: "other" });
|
|
8171
|
+
}
|
|
8172
|
+
setMeshVisible(name, visible) {
|
|
8173
|
+
this.applier.setMeshVisible(this.requireModel(), name, visible);
|
|
8174
|
+
this.meshProfile(name).visible = visible;
|
|
8175
|
+
}
|
|
8176
|
+
highlightMesh(name, color, value) {
|
|
8177
|
+
this.applier.highlightMesh(this.requireModel(), name, color, value);
|
|
8178
|
+
}
|
|
8179
|
+
getMeshMaterialConfig(name) {
|
|
8180
|
+
return this.applier.getMeshMaterialConfig(this.requireModel(), name);
|
|
8181
|
+
}
|
|
8182
|
+
setMeshMaterialConfig(name, value) {
|
|
8183
|
+
this.applier.setMeshMaterialConfig(this.requireModel(), name, value);
|
|
8184
|
+
const entry = this.meshProfile(name);
|
|
8185
|
+
entry.material = { ...entry.material, ...value };
|
|
8186
|
+
}
|
|
8187
|
+
registerHairObjects(objects) {
|
|
8188
|
+
this.hairObjects.clear();
|
|
8189
|
+
for (const object of objects) if (object.isMesh) this.hairObjects.set(object.name, object);
|
|
8190
|
+
for (const category of ["hair", "eyebrow"]) {
|
|
8191
|
+
const mesh = [...this.hairObjects.values()].find((object) => this.meshCategory(object) === category);
|
|
8192
|
+
const material = mesh && (Array.isArray(mesh.material) ? mesh.material[0] : mesh.material);
|
|
8193
|
+
if (material) this.hairAppearance[`${category}Color`] = { ...this.hairAppearance[`${category}Color`], baseColor: material.color ? `#${material.color.getHexString()}` : "#000000", emissive: material.emissive ? `#${material.emissive.getHexString()}` : "#000000", emissiveIntensity: material.emissiveIntensity ?? 0 };
|
|
8194
|
+
}
|
|
8195
|
+
this.restartHair();
|
|
8196
|
+
return [...this.hairObjects.values()].map((mesh) => ({ name: mesh.name, isMesh: true, isEyebrow: this.meshCategory(mesh) === "eyebrow" }));
|
|
8197
|
+
}
|
|
8198
|
+
getRegisteredHairObjects() {
|
|
8199
|
+
return [...this.hairObjects.values()];
|
|
8200
|
+
}
|
|
8201
|
+
getHairMorphTargets(name) {
|
|
8202
|
+
const meshes = name ? [this.hairObjects.get(name)] : [...this.hairObjects.values()];
|
|
8203
|
+
return [...new Set(meshes.flatMap((mesh) => Object.keys(mesh?.morphTargetDictionary ?? {})))].sort();
|
|
8204
|
+
}
|
|
8205
|
+
getHairAppearance() {
|
|
8206
|
+
return copy(this.hairAppearance);
|
|
8207
|
+
}
|
|
8208
|
+
setHairAppearance(category, value) {
|
|
8209
|
+
const key = `${category}Color`;
|
|
8210
|
+
const next = parse(this.wasm.normalize_hair_color_appearance_json(json2(value), json2(this.hairAppearance[key])));
|
|
8211
|
+
this.hairAppearance[key] = next;
|
|
8212
|
+
this.applier.applyHairAppearance([...this.hairObjects.values()].filter((mesh) => this.meshCategory(mesh) === category), next);
|
|
8213
|
+
}
|
|
8214
|
+
setHairColor(value) {
|
|
8215
|
+
this.setHairAppearance("hair", value);
|
|
8216
|
+
}
|
|
8217
|
+
setEyebrowColor(value) {
|
|
8218
|
+
this.setHairAppearance("eyebrow", value);
|
|
8219
|
+
}
|
|
8220
|
+
setHairBaseColor(value) {
|
|
8221
|
+
this.setHairColor({ baseColor: value });
|
|
8222
|
+
}
|
|
8223
|
+
setEyebrowBaseColor(value) {
|
|
8224
|
+
this.setEyebrowColor({ baseColor: value });
|
|
8225
|
+
}
|
|
8226
|
+
setHairGlow(color, value) {
|
|
8227
|
+
this.setHairColor({ emissive: color, emissiveIntensity: value });
|
|
8228
|
+
}
|
|
8229
|
+
setEyebrowGlow(color, value) {
|
|
8230
|
+
this.setEyebrowColor({ emissive: color, emissiveIntensity: value });
|
|
8231
|
+
}
|
|
8232
|
+
setHairOutline(value) {
|
|
8233
|
+
Object.assign(this.hairAppearance, { showOutline: value.show }, value.color ? { outlineColor: value.color } : {}, value.opacity !== void 0 ? { outlineOpacity: value.opacity } : {});
|
|
8234
|
+
}
|
|
8235
|
+
restartHair() {
|
|
8236
|
+
for (const name of [...this.current.keys()]) if (name.startsWith("__embody_hair")) this.stopAnimation(name);
|
|
8237
|
+
if (!this.hairConfig.enabled || !this.hairObjects.size || !this.connector) return;
|
|
8238
|
+
if (this.hairConfig.idleSwayAmount > 0 || this.hairConfig.windStrength > 0) {
|
|
8239
|
+
const curves = parse(this.wasm.build_hair_idle_curves(json2(this.hairConfig), this.hairConfig.idleClipDuration));
|
|
8240
|
+
if (Object.keys(curves).length) this.playSnippet("__embody_hair_idle", curves, { loopMode: "repeat", meshNames: [...this.hairObjects.keys()], source: "hair", blendMode: "additive" });
|
|
6883
8241
|
}
|
|
6884
|
-
|
|
8242
|
+
}
|
|
8243
|
+
setHairPhysicsEnabled(value) {
|
|
8244
|
+
this.setHairPhysicsConfig({ enabled: value });
|
|
8245
|
+
}
|
|
8246
|
+
isHairPhysicsEnabled() {
|
|
8247
|
+
return !!this.hairConfig.enabled;
|
|
8248
|
+
}
|
|
8249
|
+
setHairPhysicsConfig(value) {
|
|
8250
|
+
this.hairConfig = parse(this.core.set_hair_config_json(json2(value)));
|
|
8251
|
+
this.restartHair();
|
|
8252
|
+
}
|
|
8253
|
+
getHairPhysicsConfig() {
|
|
8254
|
+
return copy(this.hairConfig);
|
|
8255
|
+
}
|
|
8256
|
+
validateHairMorphTargets() {
|
|
8257
|
+
return parse(this.core.get_missing_hair_morph_targets_json(json2(this.getHairMorphTargets())));
|
|
8258
|
+
}
|
|
8259
|
+
applyHairStateToObject(name, state) {
|
|
8260
|
+
const mesh = this.hairObjects.get(name);
|
|
8261
|
+
if (!mesh) return;
|
|
8262
|
+
if (state.visible !== void 0) mesh.visible = state.visible;
|
|
8263
|
+
if (state.scale) mesh.scale.set(state.scale.x, state.scale.y, state.scale.z);
|
|
8264
|
+
if (state.position) mesh.position.set(state.position.x, state.position.y, state.position.z);
|
|
8265
|
+
if (state.color) this.applier.applyHairAppearance([mesh], state.color);
|
|
8266
|
+
}
|
|
8267
|
+
authoredHeadMotion(clip) {
|
|
8268
|
+
if (!this.hairConfig.enabled || clip.name.startsWith("__embody_hair") || !this.hairObjects.size) return void 0;
|
|
8269
|
+
const tracks = [];
|
|
8270
|
+
for (const track of clip.tracks) {
|
|
8271
|
+
if (track.ValueTypeName !== "quaternion") continue;
|
|
8272
|
+
const parsed = PropertyBinding.parseTrackName(track.name);
|
|
8273
|
+
let object = PropertyBinding.findNode(this.requireModel(), parsed.nodeName);
|
|
8274
|
+
if (parsed.objectName === "bones" && object) {
|
|
8275
|
+
const skeleton = object.skeleton;
|
|
8276
|
+
object = skeleton?.bones.find((bone) => bone.name === String(parsed.objectIndex)) ?? skeleton?.bones[Number(parsed.objectIndex)];
|
|
8277
|
+
}
|
|
8278
|
+
if (!object) continue;
|
|
8279
|
+
const cubic = track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true;
|
|
8280
|
+
const stride = track.getValueSize();
|
|
8281
|
+
const reference = this.restValues(track);
|
|
8282
|
+
if (!reference) continue;
|
|
8283
|
+
const samples = [];
|
|
8284
|
+
for (let index = 0; index < track.times.length; index += 1) {
|
|
8285
|
+
const offset = index * stride + (cubic ? 4 : 0);
|
|
8286
|
+
samples.push(Array.from(track.values.subarray(offset, offset + 4)));
|
|
8287
|
+
}
|
|
8288
|
+
tracks.push({ boneName: object.name, current: object.quaternion.toArray(), reference, samples });
|
|
8289
|
+
}
|
|
8290
|
+
return parse(this.core.resolve_hair_motion_json(json2(tracks), clip.blendMode === AdditiveAnimationBlendMode)) ?? void 0;
|
|
8291
|
+
}
|
|
8292
|
+
setHairGravity(pitch, durationSeconds) {
|
|
8293
|
+
const clip = parse(this.core.build_hair_gravity_clip("__embody_hair_gravity", pitch, durationSeconds, json2({ meshNames: [...this.hairObjects.keys()], source: "hair" })));
|
|
8294
|
+
if (!clip.tracks.length) return;
|
|
8295
|
+
this.handle({ name: clip.name, kind: "compiled", clip }, { loopMode: "once", finalPose: "hold", blendMode: "additive" }, true);
|
|
8296
|
+
}
|
|
8297
|
+
playHairImpulse(horizontal, vertical) {
|
|
8298
|
+
if (!this.hairConfig.enabled || !this.hairObjects.size) return void 0;
|
|
8299
|
+
const curves = parse(this.wasm.build_hair_impulse_curves(json2(this.hairConfig), this.hairConfig.impulseClipDuration, horizontal, vertical));
|
|
8300
|
+
if (!Object.keys(curves).length) return void 0;
|
|
8301
|
+
return this.playSnippet("__embody_hair_impulse", curves, { loopMode: "once", finalPose: "release", meshNames: [...this.hairObjects.keys()], source: "hair", blendMode: "additive" });
|
|
8302
|
+
}
|
|
8303
|
+
start() {
|
|
8304
|
+
this.assertAlive();
|
|
8305
|
+
this.rendering = true;
|
|
8306
|
+
this.connector?.setRendering(true);
|
|
8307
|
+
}
|
|
8308
|
+
stop() {
|
|
8309
|
+
this.assertAlive();
|
|
8310
|
+
this.rendering = false;
|
|
8311
|
+
this.connector?.setRendering(false);
|
|
8312
|
+
}
|
|
8313
|
+
clear() {
|
|
8314
|
+
this.mutateLive(() => this.core.clear());
|
|
8315
|
+
}
|
|
8316
|
+
resetToNeutral() {
|
|
8317
|
+
this.clear();
|
|
8318
|
+
}
|
|
8319
|
+
dispose() {
|
|
8320
|
+
if (this.disposed) return;
|
|
8321
|
+
this.connector?.dispose();
|
|
8322
|
+
this.core.free();
|
|
8323
|
+
this.playbacks.clear();
|
|
8324
|
+
this.current.clear();
|
|
8325
|
+
this.sources.clear();
|
|
8326
|
+
this.hairObjects.clear();
|
|
8327
|
+
this.meshes.length = 0;
|
|
8328
|
+
this.meshByName.clear();
|
|
8329
|
+
this.bones = {};
|
|
8330
|
+
this.liveTargets.clear();
|
|
8331
|
+
this.connector = void 0;
|
|
8332
|
+
this.inspection = void 0;
|
|
8333
|
+
this.boundModel = void 0;
|
|
8334
|
+
this.disposed = true;
|
|
6885
8335
|
}
|
|
6886
8336
|
};
|
|
8337
|
+
function createRustEmbodyHostUnbound(config = {}) {
|
|
8338
|
+
return new Embody(config);
|
|
8339
|
+
}
|
|
8340
|
+
async function createRustEmbodyHost(model, config = {}) {
|
|
8341
|
+
const character = await Embody.create(config);
|
|
8342
|
+
try {
|
|
8343
|
+
character.onReady({ model, meshes: config.meshes, referencePose: config.referencePose });
|
|
8344
|
+
return character;
|
|
8345
|
+
} catch (error) {
|
|
8346
|
+
character.dispose();
|
|
8347
|
+
throw error;
|
|
8348
|
+
}
|
|
8349
|
+
}
|
|
6887
8350
|
var draco = null;
|
|
6888
8351
|
var dracoPath = "";
|
|
6889
8352
|
var gltfLoader = null;
|
|
@@ -7437,7 +8900,7 @@ function createDefaultCharacterSceneRuntime(container, options = {}) {
|
|
|
7437
8900
|
if (pendingRendererChange && preference === settings.preference && (status === "initializing" || status === "switching")) return pendingRendererChange;
|
|
7438
8901
|
if (status === "ready" && preference === settings.preference) return Promise.resolve(getSnapshot());
|
|
7439
8902
|
settings = { preference };
|
|
7440
|
-
const
|
|
8903
|
+
const request3 = ++generation;
|
|
7441
8904
|
const previous = pending;
|
|
7442
8905
|
status = active ? "switching" : "initializing";
|
|
7443
8906
|
error = null;
|
|
@@ -7451,7 +8914,7 @@ function createDefaultCharacterSceneRuntime(container, options = {}) {
|
|
|
7451
8914
|
let restoreModelSkinning = () => {
|
|
7452
8915
|
};
|
|
7453
8916
|
const check = () => {
|
|
7454
|
-
if (disposed || generation !==
|
|
8917
|
+
if (disposed || generation !== request3) throw renderingAborted();
|
|
7455
8918
|
if (!active) throwIfAborted(options.signal);
|
|
7456
8919
|
if (candidate?.lost) throw candidate.lost;
|
|
7457
8920
|
};
|
|
@@ -7534,7 +8997,7 @@ function createDefaultCharacterSceneRuntime(container, options = {}) {
|
|
|
7534
8997
|
if (attached && candidate?.renderer.domElement.parentElement === container) container.removeChild(candidate.renderer.domElement);
|
|
7535
8998
|
rollback(() => environment?.dispose());
|
|
7536
8999
|
rollback(() => candidate?.dispose());
|
|
7537
|
-
const canceled = disposed || generation !==
|
|
9000
|
+
const canceled = disposed || generation !== request3 || !active && options.signal?.aborted;
|
|
7538
9001
|
if (!disposed) {
|
|
7539
9002
|
rollback(restoreModelSkinning);
|
|
7540
9003
|
rollback(restoreCameraCoordinates);
|
|
@@ -7546,7 +9009,7 @@ function createDefaultCharacterSceneRuntime(container, options = {}) {
|
|
|
7546
9009
|
owner.reportFailure(resumeError, active.renderer);
|
|
7547
9010
|
}
|
|
7548
9011
|
}
|
|
7549
|
-
if (generation ===
|
|
9012
|
+
if (generation === request3) {
|
|
7550
9013
|
status = active?.lost ? "lost" : "error";
|
|
7551
9014
|
error = failure instanceof Error ? failure : new Error(String(failure));
|
|
7552
9015
|
notify();
|
|
@@ -7723,6 +9186,184 @@ async function createDefaultCharacterSceneAsync(container, options = {}) {
|
|
|
7723
9186
|
}
|
|
7724
9187
|
}
|
|
7725
9188
|
|
|
9189
|
+
// three/characterHost.ts
|
|
9190
|
+
async function createCharacterHost(options) {
|
|
9191
|
+
if (!options.container) throw new Error("createCharacterHost requires a container HTMLElement");
|
|
9192
|
+
if (!options.character?.modelUrl) throw new Error("createCharacterHost requires character.modelUrl");
|
|
9193
|
+
let loaded;
|
|
9194
|
+
let state;
|
|
9195
|
+
let engine;
|
|
9196
|
+
let controller;
|
|
9197
|
+
let disposed = false;
|
|
9198
|
+
let disposal;
|
|
9199
|
+
let switching = Promise.resolve();
|
|
9200
|
+
let mountedCanvas;
|
|
9201
|
+
const aborted = () => new DOMException("Character host creation was aborted", "AbortError");
|
|
9202
|
+
const checkCreation = () => {
|
|
9203
|
+
if (options.signal?.aborted) throw aborted();
|
|
9204
|
+
};
|
|
9205
|
+
const releaseModel = async (value) => {
|
|
9206
|
+
const release = () => disposeCharacterModel(state?.scene ?? null, value.model);
|
|
9207
|
+
if (state?.rendering) await state.rendering.releaseSceneResources(release);
|
|
9208
|
+
else release();
|
|
9209
|
+
};
|
|
9210
|
+
const makeEngine = (value, character) => {
|
|
9211
|
+
const next = new Embody({ ...options.embody, presetType: character.presetType ?? options.embody?.presetType, profile: character.profile ?? options.embody?.profile });
|
|
9212
|
+
try {
|
|
9213
|
+
next.onReady(value);
|
|
9214
|
+
return next;
|
|
9215
|
+
} catch (error) {
|
|
9216
|
+
try {
|
|
9217
|
+
next.dispose();
|
|
9218
|
+
} catch {
|
|
9219
|
+
}
|
|
9220
|
+
throw error;
|
|
9221
|
+
}
|
|
9222
|
+
};
|
|
9223
|
+
const dispose = () => {
|
|
9224
|
+
if (disposal) return disposal;
|
|
9225
|
+
disposed = true;
|
|
9226
|
+
disposal = (async () => {
|
|
9227
|
+
let failure;
|
|
9228
|
+
const attempt = async (release) => {
|
|
9229
|
+
try {
|
|
9230
|
+
await release();
|
|
9231
|
+
} catch (error) {
|
|
9232
|
+
failure ?? (failure = error);
|
|
9233
|
+
}
|
|
9234
|
+
};
|
|
9235
|
+
await attempt(() => controller?.dispose());
|
|
9236
|
+
await switching.catch(() => {
|
|
9237
|
+
});
|
|
9238
|
+
await attempt(() => engine?.dispose());
|
|
9239
|
+
const ownedScene = !options.external && state && "dispose" in state ? state : void 0;
|
|
9240
|
+
if (ownedScene) await attempt(() => ownedScene.dispose());
|
|
9241
|
+
const lastModel = loaded;
|
|
9242
|
+
if (lastModel) await attempt(() => releaseModel(lastModel));
|
|
9243
|
+
mountedCanvas?.remove();
|
|
9244
|
+
if (failure !== void 0) throw failure;
|
|
9245
|
+
})();
|
|
9246
|
+
return disposal;
|
|
9247
|
+
};
|
|
9248
|
+
try {
|
|
9249
|
+
checkCreation();
|
|
9250
|
+
loaded = await loadCharacterModel(options.character.modelUrl, options.loader);
|
|
9251
|
+
checkCreation();
|
|
9252
|
+
state = options.external ?? await createDefaultCharacterSceneAsync(options.container, { ...options.scene, signal: options.signal });
|
|
9253
|
+
checkCreation();
|
|
9254
|
+
await initEmbodyCore();
|
|
9255
|
+
checkCreation();
|
|
9256
|
+
applyCharacterModelTransform(loaded.model, options.character);
|
|
9257
|
+
state.scene.add(loaded.model);
|
|
9258
|
+
engine = makeEngine(loaded, options.character);
|
|
9259
|
+
if (options.external?.mountCanvas && !state.renderer.domElement.parentElement) {
|
|
9260
|
+
mountedCanvas = state.renderer.domElement;
|
|
9261
|
+
options.container.appendChild(mountedCanvas);
|
|
9262
|
+
}
|
|
9263
|
+
if (options.renderLoop ?? !options.external) {
|
|
9264
|
+
if (!(state.camera instanceof PerspectiveCamera)) throw new Error("Character host rendering requires a PerspectiveCamera");
|
|
9265
|
+
controller = new DPthreeCameraController({
|
|
9266
|
+
scene: state.scene,
|
|
9267
|
+
camera: state.camera,
|
|
9268
|
+
domElement: state.renderer.domElement,
|
|
9269
|
+
...state.rendering ? { rendering: state.rendering } : { renderer: state.renderer },
|
|
9270
|
+
showDOMControls: false
|
|
9271
|
+
});
|
|
9272
|
+
controller.setModel(loaded.model);
|
|
9273
|
+
await controller.prepareModelForRender(loaded.model, { signal: options.signal });
|
|
9274
|
+
if (options.autoFrame !== false) await controller.focusFullBody(0);
|
|
9275
|
+
}
|
|
9276
|
+
checkCreation();
|
|
9277
|
+
if (options.autoStart === false) engine.stop();
|
|
9278
|
+
else engine.start();
|
|
9279
|
+
const sceneState = state;
|
|
9280
|
+
const rendering = state.rendering ?? null;
|
|
9281
|
+
const host = {
|
|
9282
|
+
engine,
|
|
9283
|
+
scene: state.scene,
|
|
9284
|
+
camera: state.camera,
|
|
9285
|
+
get renderer() {
|
|
9286
|
+
return rendering ? rendering.getSnapshot().renderer : sceneState.renderer;
|
|
9287
|
+
},
|
|
9288
|
+
get backend() {
|
|
9289
|
+
return rendering ? rendering.getSnapshot().backend : sceneState.backend ?? null;
|
|
9290
|
+
},
|
|
9291
|
+
rendering,
|
|
9292
|
+
model: loaded.model,
|
|
9293
|
+
meshes: loaded.meshes,
|
|
9294
|
+
animations: loaded.animations,
|
|
9295
|
+
lighting: "lighting" in state ? state.lighting : null,
|
|
9296
|
+
ownsScene: !options.external,
|
|
9297
|
+
container: options.container,
|
|
9298
|
+
resize() {
|
|
9299
|
+
if ("resize" in sceneState) sceneState.resize();
|
|
9300
|
+
},
|
|
9301
|
+
setCharacter(character, loader = options.loader) {
|
|
9302
|
+
if (disposed) return Promise.reject(new Error("Character host is disposed"));
|
|
9303
|
+
const change = switching.catch(() => {
|
|
9304
|
+
}).then(async () => {
|
|
9305
|
+
if (disposed) throw new Error("Character host is disposed");
|
|
9306
|
+
const next = await loadCharacterModel(character.modelUrl, loader);
|
|
9307
|
+
let nextEngine;
|
|
9308
|
+
try {
|
|
9309
|
+
if (disposed) throw new Error("Character host is disposed");
|
|
9310
|
+
applyCharacterModelTransform(next.model, character);
|
|
9311
|
+
nextEngine = makeEngine(next, character);
|
|
9312
|
+
nextEngine.stop();
|
|
9313
|
+
sceneState.scene.add(next.model);
|
|
9314
|
+
if (controller) await controller.prepareModelForRender(next.model);
|
|
9315
|
+
if (disposed) throw new Error("Character host is disposed");
|
|
9316
|
+
} catch (error) {
|
|
9317
|
+
try {
|
|
9318
|
+
nextEngine?.dispose();
|
|
9319
|
+
} catch {
|
|
9320
|
+
}
|
|
9321
|
+
try {
|
|
9322
|
+
await releaseModel(next);
|
|
9323
|
+
} catch {
|
|
9324
|
+
}
|
|
9325
|
+
throw error;
|
|
9326
|
+
}
|
|
9327
|
+
const previous = loaded;
|
|
9328
|
+
try {
|
|
9329
|
+
engine.dispose();
|
|
9330
|
+
} catch (error) {
|
|
9331
|
+
try {
|
|
9332
|
+
nextEngine?.dispose();
|
|
9333
|
+
} catch {
|
|
9334
|
+
}
|
|
9335
|
+
try {
|
|
9336
|
+
await releaseModel(next);
|
|
9337
|
+
} catch {
|
|
9338
|
+
}
|
|
9339
|
+
throw error;
|
|
9340
|
+
}
|
|
9341
|
+
engine = nextEngine;
|
|
9342
|
+
loaded = next;
|
|
9343
|
+
host.engine = engine;
|
|
9344
|
+
host.model = next.model;
|
|
9345
|
+
host.meshes = next.meshes;
|
|
9346
|
+
host.animations = next.animations;
|
|
9347
|
+
controller?.setModel(next.model);
|
|
9348
|
+
if (options.autoStart !== false) engine.start();
|
|
9349
|
+
await releaseModel(previous);
|
|
9350
|
+
if (controller && options.autoFrame !== false) await controller.focusFullBody(0);
|
|
9351
|
+
});
|
|
9352
|
+
switching = change;
|
|
9353
|
+
return change;
|
|
9354
|
+
},
|
|
9355
|
+
dispose
|
|
9356
|
+
};
|
|
9357
|
+
return host;
|
|
9358
|
+
} catch (error) {
|
|
9359
|
+
try {
|
|
9360
|
+
await dispose();
|
|
9361
|
+
} catch {
|
|
9362
|
+
}
|
|
9363
|
+
throw error;
|
|
9364
|
+
}
|
|
9365
|
+
}
|
|
9366
|
+
|
|
7726
9367
|
// three/index.ts
|
|
7727
9368
|
var THREE_BLENDING_MODES = {
|
|
7728
9369
|
Normal: NormalBlending,
|
|
@@ -7731,6 +9372,7 @@ var THREE_BLENDING_MODES = {
|
|
|
7731
9372
|
Multiply: MultiplyBlending,
|
|
7732
9373
|
None: NoBlending
|
|
7733
9374
|
};
|
|
9375
|
+
var BLENDING_MODES = THREE_BLENDING_MODES;
|
|
7734
9376
|
var MORPH_ATTRIBUTE_SEMANTICS = ["position", "normal", "tangent", "color"];
|
|
7735
9377
|
var createNeutralMorphAttribute = (geometry, semantic, name) => {
|
|
7736
9378
|
const base = geometry.getAttribute(semantic);
|
|
@@ -7925,6 +9567,8 @@ function createAnimationClipFromClipIR(clip, inspection) {
|
|
|
7925
9567
|
const tracks = [];
|
|
7926
9568
|
for (const track of clip.tracks ?? []) {
|
|
7927
9569
|
const kind = track.target?.kind;
|
|
9570
|
+
const trackName = concreteTrackName(track.target, inspection);
|
|
9571
|
+
if (!trackName) continue;
|
|
7928
9572
|
const times = Array.from(track.times ?? []);
|
|
7929
9573
|
const values = Array.from(track.values ?? []);
|
|
7930
9574
|
if (!times.length || !values.length) continue;
|
|
@@ -7932,7 +9576,6 @@ function createAnimationClipFromClipIR(clip, inspection) {
|
|
|
7932
9576
|
if (kind === "morphTarget") {
|
|
7933
9577
|
const binding = inspection.morphBindings.get(Number(track.target.morphTargetId));
|
|
7934
9578
|
if (!binding?.mesh) continue;
|
|
7935
|
-
const trackName = `${binding.mesh.uuid}.morphTargetInfluences[${binding.index}]`;
|
|
7936
9579
|
if (track.inheritStart) {
|
|
7937
9580
|
values[0] = binding.mesh.morphTargetInfluences?.[binding.index] ?? 0;
|
|
7938
9581
|
}
|
|
@@ -7945,13 +9588,13 @@ function createAnimationClipFromClipIR(clip, inspection) {
|
|
|
7945
9588
|
const property = track.target.property ?? "rotation";
|
|
7946
9589
|
if (property === "rotation" || property === "quaternion") {
|
|
7947
9590
|
if (track.inheritStart) object.quaternion.toArray(values, 0);
|
|
7948
|
-
tracks.push(new QuaternionKeyframeTrack(
|
|
9591
|
+
tracks.push(new QuaternionKeyframeTrack(trackName, times, values, interpolation));
|
|
7949
9592
|
} else if (property === "position") {
|
|
7950
9593
|
if (track.inheritStart) object.position.toArray(values, 0);
|
|
7951
|
-
tracks.push(new VectorKeyframeTrack(
|
|
9594
|
+
tracks.push(new VectorKeyframeTrack(trackName, times, values, interpolation));
|
|
7952
9595
|
} else if (property === "scale") {
|
|
7953
9596
|
if (track.inheritStart) object.scale.toArray(values, 0);
|
|
7954
|
-
tracks.push(new VectorKeyframeTrack(
|
|
9597
|
+
tracks.push(new VectorKeyframeTrack(trackName, times, values, interpolation));
|
|
7955
9598
|
}
|
|
7956
9599
|
continue;
|
|
7957
9600
|
}
|
|
@@ -7959,7 +9602,7 @@ function createAnimationClipFromClipIR(clip, inspection) {
|
|
|
7959
9602
|
const mesh = inspection.meshBindings.get(Number(track.target.meshId));
|
|
7960
9603
|
if (!mesh) continue;
|
|
7961
9604
|
if (track.inheritStart) values[0] = Number(mesh.visible);
|
|
7962
|
-
tracks.push(new NumberKeyframeTrack(
|
|
9605
|
+
tracks.push(new NumberKeyframeTrack(trackName, times, values, interpolation));
|
|
7963
9606
|
}
|
|
7964
9607
|
}
|
|
7965
9608
|
const duration = Number.isFinite(clip.durationSeconds) ? Number(clip.durationSeconds) : -1;
|
|
@@ -8002,14 +9645,19 @@ function serializeAnimationClips(model, clips, inspection) {
|
|
|
8002
9645
|
}
|
|
8003
9646
|
if (!target) return;
|
|
8004
9647
|
const kind = target.kind === "morphTarget" ? "face" : target.kind === "boneTransform" ? "body" : "scene";
|
|
8005
|
-
const
|
|
9648
|
+
const cubic = !!track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline;
|
|
9649
|
+
const size = track.getValueSize() / (cubic ? 3 : 1);
|
|
9650
|
+
if (!Array.from(track.values).every(Number.isFinite)) {
|
|
9651
|
+
throw new Error(`Animation track ${JSON.stringify(track.name)} contains non-finite samples.`);
|
|
9652
|
+
}
|
|
9653
|
+
const values = cubic ? Array.from(track.times).flatMap((_, key) => Array.from(track.values.slice((key * 3 + 1) * size, (key * 3 + 2) * size))) : Array.from(track.values);
|
|
8006
9654
|
tracks.push({
|
|
8007
9655
|
id: index + 1,
|
|
8008
9656
|
channelId: channelId(kind),
|
|
8009
9657
|
target,
|
|
8010
9658
|
valueType: size === 4 ? "quat" : size === 3 ? "vec3" : "scalar",
|
|
8011
9659
|
times: Array.from(track.times),
|
|
8012
|
-
values
|
|
9660
|
+
values,
|
|
8013
9661
|
interpolation: track.getInterpolation() === InterpolateDiscrete ? "step" : "linear",
|
|
8014
9662
|
sourceName: track.name
|
|
8015
9663
|
});
|
|
@@ -8098,7 +9746,7 @@ var ThreeFrameApplier = class {
|
|
|
8098
9746
|
mesh.visible = visible;
|
|
8099
9747
|
});
|
|
8100
9748
|
}
|
|
8101
|
-
highlightMesh(root, name, color = 65535,
|
|
9749
|
+
highlightMesh(root, name, color = 65535, intensity2 = 0.5) {
|
|
8102
9750
|
root.traverse((object) => {
|
|
8103
9751
|
if (!object.isMesh) return;
|
|
8104
9752
|
for (const material of materials(object.material)) {
|
|
@@ -8107,7 +9755,7 @@ var ThreeFrameApplier = class {
|
|
|
8107
9755
|
if (name === object.name) {
|
|
8108
9756
|
if (!this.originalEmissive.has(key)) this.originalEmissive.set(key, { color: material.emissive.getHex(), intensity: material.emissiveIntensity ?? 0 });
|
|
8109
9757
|
material.emissive.setHex(color);
|
|
8110
|
-
material.emissiveIntensity =
|
|
9758
|
+
material.emissiveIntensity = intensity2;
|
|
8111
9759
|
} else {
|
|
8112
9760
|
const original = this.originalEmissive.get(key);
|
|
8113
9761
|
if (original) {
|
|
@@ -8276,6 +9924,6 @@ var collectMorphMeshes = (root) => {
|
|
|
8276
9924
|
return meshes;
|
|
8277
9925
|
};
|
|
8278
9926
|
|
|
8279
|
-
export { CHARACTER_SCENE_TYPES, CHARACTER_SCENE_TYPE_IDS, DPthreeCameraController as CameraController, CameraDOMControls, CameraDOMControls as Controls, DEFAULT_CHARACTER_LIGHTING_PRESETS, DEFAULT_CHARACTER_LIGHTING_PRESET_ID, DEFAULT_CHARACTER_LIGHTING_PRESET_IDS, DEFAULT_CHARACTER_LIGHTING_SETTINGS, DEFAULT_CHARACTER_SCENE_TYPE_ID, DEFAULT_MARKER_AUTO_HIDE_MS, DEFAULT_PREVIEW_AUTO_CLEAR_MS, DPthree, DPthree3DMarkers, DPthreeCameraController, DPthreeHTMLMarkers, DPthreeMarkers, DPthree3DMarkers as Markers3D, DPthreeHTMLMarkers as MarkersHTML, RUNTIME_ANNOTATION_REGION_PREFIX, RustAnnotationCameraCore, THREE_BLENDING_MODES, ThreeFrameApplier,
|
|
9927
|
+
export { BLENDING_MODES, CHARACTER_SCENE_TYPES, CHARACTER_SCENE_TYPE_IDS, DPthreeCameraController as CameraController, CameraDOMControls, CameraDOMControls as Controls, DEFAULT_CHARACTER_LIGHTING_PRESETS, DEFAULT_CHARACTER_LIGHTING_PRESET_ID, DEFAULT_CHARACTER_LIGHTING_PRESET_IDS, DEFAULT_CHARACTER_LIGHTING_SETTINGS, DEFAULT_CHARACTER_SCENE_TYPE_ID, DEFAULT_MARKER_AUTO_HIDE_MS, DEFAULT_PREVIEW_AUTO_CLEAR_MS, DPthree, DPthree3DMarkers, DPthreeCameraController, DPthreeHTMLMarkers, DPthreeMarkers, Embody, DPthree3DMarkers as Markers3D, DPthreeHTMLMarkers as MarkersHTML, RUNTIME_ANNOTATION_REGION_PREFIX, RustAnnotationCameraCore, THREE_BLENDING_MODES, ThreeFrameApplier, ThreeModelInspector, ThreeNativePlaybackConnector, analyzeModel, applyAUBoneBindingUpdate, applyBilateralAxisBindingUpdate, applyBoneAxisBindingUpdate, applyCharacterModelTransform, bindModelReferencePose, boneResolutionProfile, buildBoneAuOptions, buildRegionDisplayOptions, captureModelReferencePose, classifyAuAsJointControl, collectMorphMeshes, computeHumanoidSkeletonTemplateRestBounds, createAnimationClipFromClipIR, createBilateralBoneAxisAu, createBoneAxisAu, createCharacterHost, createDefaultCharacterLighting, createDefaultCharacterScene, createDefaultCharacterSceneAsync, createDefaultCharacterSceneRuntime, createMarkerLifecycleRevealOptions, createMarkerVisibilityLifecycle, createRuntimeAnnotationPreviewLifecycle, createRustAnnotationCameraCore, createRustEmbodyHost, createRustEmbodyHostUnbound, createScenePlaybackFrame, createShadowPlane, createValidationSkeletonFromHumanoidTemplate, detectAnnotationLaterality, disposeCharacterModel, ensureBilateralBoneNodeKeys, ensureBoneNodeKey, extendCharacterConfigWithPreset, extendModelReferencePose, extractHumanoidSkeletonTemplateFromModel, extractModelData, extractProfileOverrides, filterCameraViewRegions, findNodeKeyForBone, formatAxisDirectionLabel, formatAxisLabel, fuzzyNameMatch, getAUBoneBindingState, getAxisFromChannel, getBilateralAxisBindingState, getBodyControls, getBoneAxisBindingState, getDefaultAnnotationLaterality, getHumanoidSkeletonTemplate, getHumanoidSkeletonTemplateBoneNames, getHumanoidSpecification, getMeshNamesForAUProfile, getModelLocalOrbitAngle, getPreset, getRuntimeAUMeshSideOffset, getRuntimeAUMorphAnchorPoint, getRuntimeAnnotationSide, getSemanticHorizontalSign, getSemanticHorizontalSignForSide, getWorldDirectionForCameraAngle, hasLeftRightMorphs, inferChiralBoneNamePair, inferEyeControlFamily, inferEyeControlScope, inferRuntimeAnnotationPreviewSide, isJointControlAuInfo, isMaxDegreesOnlyAxisBindingUpdate, isMixedAU, isRuntimeAnnotationRegionName, loadCharacterModel, mergeCharacterRegionsByName, normalizeDefaultCharacterLightingSettings, normalizeHairColorAppearance, parseCharacterModel, passesMarkerCameraAngleGate, pickContinuumAnnotationAuId, renderScenePlayback, resolveAnnotationCharacterConfig, resolveBoneAxisChannel, resolveBoneNameForNodeKey, resolveBoneNames, resolveContinuumDisplayLabel, resolveFaceCenter, resolveHumanoidCharacterization, resolveMarkerAnchorRegion, resolveRegionCameraAngle, resolveRegionVisibilityCameraAngle, resolveVisemeProfile, serializeAnimationClips, setAUMorphTargets, setHumanoidRoleBinding, shouldProjectMarkerAnchorToSurface, shouldUseFaceCenterForMarkerAnchor, stripConfiguredBoneAffixes, supportsRustEmbodyPreset, toWorldDirection, validateHumanoidCharacterization, validateMappings };
|
|
8280
9928
|
//# sourceMappingURL=three.js.map
|
|
8281
9929
|
//# sourceMappingURL=three.js.map
|