@spatialwalk/avatarkit 1.0.0-beta.61 → 1.0.0-beta.62
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/CHANGELOG.md +13 -0
- package/dist/{StreamingAudioPlayer-DIcPerS7.js → StreamingAudioPlayer-BWsAt_s7.js} +1 -1
- package/dist/core/AvatarView.d.ts +3 -2
- package/dist/{index-jWgogoMs.js → index-Bhjn1nq3.js} +80 -25
- package/dist/index.js +1 -1
- package/dist/utils/animation-interpolation.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.0-beta.62] - 2026-01-14
|
|
6
|
+
|
|
7
|
+
### ✨ New Features
|
|
8
|
+
- **Bezier Curve Transition Animation** - Implemented Bezier curve easing functions for smoother transitions
|
|
9
|
+
- Added Bezier curve interpolation with different curves for different facial components (jaw, expression, eye, neck, global)
|
|
10
|
+
- Replaced linear interpolation with Bezier curve interpolation for more natural animation
|
|
11
|
+
- Split transition duration into start (200ms) and end (1600ms) for different transition types
|
|
12
|
+
|
|
13
|
+
### 🔧 Improvements
|
|
14
|
+
- **Transition API Enhancement** - Updated `generateTransitionFromIdle()` to support both start and end transitions
|
|
15
|
+
- Added `transitionType` parameter: `'start'` for Idle -> Flame, `'end'` for Flame -> Idle
|
|
16
|
+
- Removed deprecated linear interpolation code and unused easing functions
|
|
17
|
+
|
|
5
18
|
## [1.0.0-beta.61] - 2026-01-14
|
|
6
19
|
|
|
7
20
|
### 🔧 Improvements
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-
|
|
4
|
+
import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-Bhjn1nq3.js";
|
|
5
5
|
class StreamingAudioPlayer {
|
|
6
6
|
constructor(options) {
|
|
7
7
|
__publicField(this, "audioContext", null);
|
|
@@ -22,7 +22,8 @@ export declare class AvatarView {
|
|
|
22
22
|
private currentFPS;
|
|
23
23
|
private transitionKeyframes;
|
|
24
24
|
private transitionStartTime;
|
|
25
|
-
private readonly
|
|
25
|
+
private readonly startTransitionDurationMs;
|
|
26
|
+
private readonly endTransitionDurationMs;
|
|
26
27
|
private cachedIdleFirstFrame;
|
|
27
28
|
private idleCurrentFrameIndex;
|
|
28
29
|
private characterHandle;
|
|
@@ -61,7 +62,7 @@ export declare class AvatarView {
|
|
|
61
62
|
private stopRealtimeRendering;
|
|
62
63
|
dispose(): void;
|
|
63
64
|
renderFlame(flame: Flame, enableIdleRendering?: boolean): Promise<void>;
|
|
64
|
-
generateTransitionFromIdle(toFlame: Flame, frameCount: number): Promise<Flame[]>;
|
|
65
|
+
generateTransitionFromIdle(toFlame: Flame, frameCount: number, transitionType?: 'start' | 'end'): Promise<Flame[]>;
|
|
65
66
|
private rerenderCurrentFrameWithNewCamera;
|
|
66
67
|
private handleResize;
|
|
67
68
|
get transform(): {
|
|
@@ -7624,7 +7624,7 @@ const _AnimationPlayer = class _AnimationPlayer {
|
|
|
7624
7624
|
if (this.streamingPlayer) {
|
|
7625
7625
|
return;
|
|
7626
7626
|
}
|
|
7627
|
-
const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-
|
|
7627
|
+
const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-BWsAt_s7.js");
|
|
7628
7628
|
const { AvatarSDK: AvatarSDK2 } = await Promise.resolve().then(() => AvatarSDK$1);
|
|
7629
7629
|
const audioFormat = AvatarSDK2.getAudioFormat();
|
|
7630
7630
|
this.streamingPlayer = new StreamingAudioPlayer({
|
|
@@ -8961,7 +8961,7 @@ class AvatarSDK {
|
|
|
8961
8961
|
}
|
|
8962
8962
|
__publicField(AvatarSDK, "_isInitialized", false);
|
|
8963
8963
|
__publicField(AvatarSDK, "_configuration", null);
|
|
8964
|
-
__publicField(AvatarSDK, "_version", "1.0.0-beta.
|
|
8964
|
+
__publicField(AvatarSDK, "_version", "1.0.0-beta.62");
|
|
8965
8965
|
__publicField(AvatarSDK, "_avatarCore", null);
|
|
8966
8966
|
__publicField(AvatarSDK, "_dynamicSdkConfig", null);
|
|
8967
8967
|
const AvatarSDK$1 = Object.freeze(Object.defineProperty({
|
|
@@ -13851,31 +13851,82 @@ function lerpArrays(from, to2, progress) {
|
|
|
13851
13851
|
}
|
|
13852
13852
|
return result2;
|
|
13853
13853
|
}
|
|
13854
|
-
|
|
13855
|
-
|
|
13854
|
+
const clamp01 = (x2) => Math.max(0, Math.min(1, x2));
|
|
13855
|
+
function createBezierEasing(x1, y1, x2, y2) {
|
|
13856
|
+
const cx = 3 * x1;
|
|
13857
|
+
const bx = 3 * (x2 - x1) - cx;
|
|
13858
|
+
const ax = 1 - cx - bx;
|
|
13859
|
+
const cy = 3 * y1;
|
|
13860
|
+
const by = 3 * (y2 - y1) - cy;
|
|
13861
|
+
const ay = 1 - cy - by;
|
|
13862
|
+
const sampleCurveX = (t2) => ((ax * t2 + bx) * t2 + cx) * t2;
|
|
13863
|
+
const sampleCurveY = (t2) => ((ay * t2 + by) * t2 + cy) * t2;
|
|
13864
|
+
const sampleCurveDerivativeX = (t2) => (3 * ax * t2 + 2 * bx) * t2 + cx;
|
|
13865
|
+
const solveCurveX = (x3) => {
|
|
13866
|
+
let t2 = x3;
|
|
13867
|
+
for (let i2 = 0; i2 < 8; i2++) {
|
|
13868
|
+
const error = sampleCurveX(t2) - x3;
|
|
13869
|
+
if (Math.abs(error) < 1e-6) break;
|
|
13870
|
+
const d2 = sampleCurveDerivativeX(t2);
|
|
13871
|
+
if (Math.abs(d2) < 1e-6) break;
|
|
13872
|
+
t2 -= error / d2;
|
|
13873
|
+
}
|
|
13874
|
+
return t2;
|
|
13875
|
+
};
|
|
13876
|
+
return (x3) => {
|
|
13877
|
+
if (x3 <= 0) return 0;
|
|
13878
|
+
if (x3 >= 1) return 1;
|
|
13879
|
+
return sampleCurveY(solveCurveX(x3));
|
|
13880
|
+
};
|
|
13881
|
+
}
|
|
13882
|
+
const BEZIER_CURVES = {
|
|
13883
|
+
jaw: createBezierEasing(0.2, 0.8, 0.3, 1),
|
|
13884
|
+
expression: createBezierEasing(0.4, 0, 0.2, 1),
|
|
13885
|
+
eye: createBezierEasing(0.3, 0, 0.1, 1),
|
|
13886
|
+
neck: createBezierEasing(0.1, 0.2, 0.2, 1),
|
|
13887
|
+
global: createBezierEasing(0.42, 0, 0.58, 1)
|
|
13888
|
+
};
|
|
13889
|
+
const TIME_SCALE = {
|
|
13890
|
+
jaw: 2.5,
|
|
13891
|
+
expression: 1.6,
|
|
13892
|
+
eye: 1.3,
|
|
13893
|
+
neck: 1,
|
|
13894
|
+
global: 1
|
|
13895
|
+
};
|
|
13896
|
+
function bezierLerp(from, to2, progress) {
|
|
13897
|
+
const getT = (key) => {
|
|
13898
|
+
const scaledProgress = clamp01(progress * TIME_SCALE[key]);
|
|
13899
|
+
return BEZIER_CURVES[key](scaledProgress);
|
|
13900
|
+
};
|
|
13856
13901
|
return {
|
|
13857
|
-
translation: lerpArrays(from.translation || [0, 0, 0], to2.translation || [0, 0, 0],
|
|
13858
|
-
rotation: lerpArrays(from.rotation || [0, 0, 0], to2.rotation || [0, 0, 0],
|
|
13859
|
-
neckPose: lerpArrays(from.neckPose || [0, 0, 0], to2.neckPose || [0, 0, 0],
|
|
13860
|
-
jawPose: lerpArrays(from.jawPose || [0, 0, 0], to2.jawPose || [0, 0, 0],
|
|
13861
|
-
eyePose: lerpArrays(from.eyePose || [0, 0, 0, 0, 0, 0], to2.eyePose || [0, 0, 0, 0, 0, 0],
|
|
13902
|
+
translation: lerpArrays(from.translation || [0, 0, 0], to2.translation || [0, 0, 0], getT("global")),
|
|
13903
|
+
rotation: lerpArrays(from.rotation || [0, 0, 0], to2.rotation || [0, 0, 0], getT("global")),
|
|
13904
|
+
neckPose: lerpArrays(from.neckPose || [0, 0, 0], to2.neckPose || [0, 0, 0], getT("neck")),
|
|
13905
|
+
jawPose: lerpArrays(from.jawPose || [0, 0, 0], to2.jawPose || [0, 0, 0], getT("jaw")),
|
|
13906
|
+
eyePose: lerpArrays(from.eyePose || [0, 0, 0, 0, 0, 0], to2.eyePose || [0, 0, 0, 0, 0, 0], getT("eye")),
|
|
13862
13907
|
eyeLid: (() => {
|
|
13863
13908
|
const fromEyelid = from.eyeLid;
|
|
13864
13909
|
const toEyelid = to2.eyeLid;
|
|
13865
|
-
if (fromEyelid
|
|
13866
|
-
return lerpArrays(fromEyelid, toEyelid,
|
|
13910
|
+
if ((fromEyelid == null ? void 0 : fromEyelid.length) && (toEyelid == null ? void 0 : toEyelid.length))
|
|
13911
|
+
return lerpArrays(fromEyelid, toEyelid, getT("eye"));
|
|
13867
13912
|
return fromEyelid || toEyelid || [];
|
|
13868
13913
|
})(),
|
|
13869
|
-
expression: lerpArrays(from.expression || [], to2.expression || [],
|
|
13914
|
+
expression: lerpArrays(from.expression || [], to2.expression || [], getT("expression"))
|
|
13870
13915
|
};
|
|
13871
13916
|
}
|
|
13872
13917
|
function generateTransitionFrames(from, to2, durationMs, fps = 25) {
|
|
13873
13918
|
const steps = Math.max(1, Math.floor(durationMs / 1e3 * fps));
|
|
13874
13919
|
const frames = Array.from({ length: steps });
|
|
13920
|
+
if (steps === 1) {
|
|
13921
|
+
frames[0] = to2;
|
|
13922
|
+
return frames;
|
|
13923
|
+
}
|
|
13875
13924
|
for (let i2 = 0; i2 < steps; i2++) {
|
|
13876
13925
|
const progress = i2 / (steps - 1);
|
|
13877
|
-
frames[i2] =
|
|
13926
|
+
frames[i2] = bezierLerp(from, to2, progress);
|
|
13878
13927
|
}
|
|
13928
|
+
frames[0] = from;
|
|
13929
|
+
frames[frames.length - 1] = to2;
|
|
13879
13930
|
return frames;
|
|
13880
13931
|
}
|
|
13881
13932
|
class AvatarView {
|
|
@@ -13900,7 +13951,8 @@ class AvatarView {
|
|
|
13900
13951
|
__publicField(this, "currentFPS", 0);
|
|
13901
13952
|
__publicField(this, "transitionKeyframes", []);
|
|
13902
13953
|
__publicField(this, "transitionStartTime", 0);
|
|
13903
|
-
__publicField(this, "
|
|
13954
|
+
__publicField(this, "startTransitionDurationMs", 200);
|
|
13955
|
+
__publicField(this, "endTransitionDurationMs", 1600);
|
|
13904
13956
|
__publicField(this, "cachedIdleFirstFrame", null);
|
|
13905
13957
|
__publicField(this, "idleCurrentFrameIndex", 0);
|
|
13906
13958
|
__publicField(this, "characterHandle", null);
|
|
@@ -13957,12 +14009,12 @@ class AvatarView {
|
|
|
13957
14009
|
toFixed.expression = ensureLen(toFixed.expression, exprLen);
|
|
13958
14010
|
return { from: fromFixed, to: toFixed };
|
|
13959
14011
|
}
|
|
13960
|
-
generateAndAlignTransitionFrames(from, to2) {
|
|
14012
|
+
generateAndAlignTransitionFrames(from, to2, durationMs) {
|
|
13961
14013
|
const aligned = this.alignFlamePair(from, to2);
|
|
13962
14014
|
let keyframes = generateTransitionFrames(
|
|
13963
14015
|
aligned.from,
|
|
13964
14016
|
aligned.to,
|
|
13965
|
-
|
|
14017
|
+
durationMs,
|
|
13966
14018
|
APP_CONFIG.animation.fps
|
|
13967
14019
|
);
|
|
13968
14020
|
if (keyframes.length < 2) {
|
|
@@ -14321,7 +14373,8 @@ class AvatarView {
|
|
|
14321
14373
|
return;
|
|
14322
14374
|
}
|
|
14323
14375
|
const elapsed = performance.now() - this.transitionStartTime;
|
|
14324
|
-
const
|
|
14376
|
+
const currentTransitionDurationMs = state === "transitioningToSpeaking" ? this.startTransitionDurationMs : this.endTransitionDurationMs;
|
|
14377
|
+
const progress = Math.min(1, Math.max(0, elapsed / currentTransitionDurationMs));
|
|
14325
14378
|
const steps = this.transitionKeyframes.length;
|
|
14326
14379
|
const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
|
|
14327
14380
|
const currentFrame = this.transitionKeyframes[idx];
|
|
@@ -14347,7 +14400,7 @@ class AvatarView {
|
|
|
14347
14400
|
return;
|
|
14348
14401
|
}
|
|
14349
14402
|
}
|
|
14350
|
-
if (state === "transitioningToSpeaking" && this.transitionStartTime > 0 && this.transitionKeyframes.length > 0 && elapsed >= this.
|
|
14403
|
+
if (state === "transitioningToSpeaking" && this.transitionStartTime > 0 && this.transitionKeyframes.length > 0 && elapsed >= this.startTransitionDurationMs + 100) {
|
|
14351
14404
|
this.setState("speaking");
|
|
14352
14405
|
this.transitionKeyframes = [];
|
|
14353
14406
|
this.avatarController.onTransitionComplete();
|
|
@@ -14477,7 +14530,7 @@ class AvatarView {
|
|
|
14477
14530
|
await this.getCachedIdleFirstFrame();
|
|
14478
14531
|
const firstSpeaking = keyframes[0];
|
|
14479
14532
|
const firstSpeakingWithPostProcessing = this.avatarController.applyPostProcessingToFlame(firstSpeaking);
|
|
14480
|
-
this.transitionKeyframes = this.generateAndAlignTransitionFrames(idleFrameProto, firstSpeakingWithPostProcessing);
|
|
14533
|
+
this.transitionKeyframes = this.generateAndAlignTransitionFrames(idleFrameProto, firstSpeakingWithPostProcessing, this.startTransitionDurationMs);
|
|
14481
14534
|
this.transitionStartTime = performance.now();
|
|
14482
14535
|
if (this.transitionKeyframes.length === 0) {
|
|
14483
14536
|
this.setState("speaking");
|
|
@@ -14530,7 +14583,7 @@ class AvatarView {
|
|
|
14530
14583
|
const lastSpeaking = this.avatarController.applyPostProcessingToFlame(lastSpeakingRaw);
|
|
14531
14584
|
const idleFirstProto = await this.getCachedIdleFirstFrame();
|
|
14532
14585
|
if (idleFirstProto) {
|
|
14533
|
-
this.transitionKeyframes = this.generateAndAlignTransitionFrames(lastSpeaking, idleFirstProto);
|
|
14586
|
+
this.transitionKeyframes = this.generateAndAlignTransitionFrames(lastSpeaking, idleFirstProto, this.endTransitionDurationMs);
|
|
14534
14587
|
this.transitionStartTime = performance.now();
|
|
14535
14588
|
if (this.transitionKeyframes.length > 0 && this.renderingState === "transitioningToIdle") {
|
|
14536
14589
|
if (APP_CONFIG.debug)
|
|
@@ -14640,7 +14693,7 @@ class AvatarView {
|
|
|
14640
14693
|
throw error;
|
|
14641
14694
|
}
|
|
14642
14695
|
}
|
|
14643
|
-
async generateTransitionFromIdle(toFlame, frameCount) {
|
|
14696
|
+
async generateTransitionFromIdle(toFlame, frameCount, transitionType = "start") {
|
|
14644
14697
|
if (!this.isInitialized) {
|
|
14645
14698
|
throw new Error("AvatarView not initialized");
|
|
14646
14699
|
}
|
|
@@ -14656,16 +14709,18 @@ class AvatarView {
|
|
|
14656
14709
|
const idleFrameProto = convertWasmParamsToProtoFlame(idleParams);
|
|
14657
14710
|
const toFlameWithPostProcessing = this.avatarController.applyPostProcessingToFlame(toFlame);
|
|
14658
14711
|
const aligned = this.alignFlamePair(idleFrameProto, toFlameWithPostProcessing);
|
|
14712
|
+
const from = transitionType === "start" ? aligned.from : aligned.to;
|
|
14713
|
+
const to2 = transitionType === "start" ? aligned.to : aligned.from;
|
|
14659
14714
|
const fps = APP_CONFIG.animation.fps;
|
|
14660
14715
|
const durationMs = frameCount / fps * 1e3;
|
|
14661
14716
|
const transitionFrames = generateTransitionFrames(
|
|
14662
|
-
|
|
14663
|
-
|
|
14717
|
+
from,
|
|
14718
|
+
to2,
|
|
14664
14719
|
durationMs,
|
|
14665
14720
|
fps
|
|
14666
14721
|
);
|
|
14667
|
-
transitionFrames[0] =
|
|
14668
|
-
transitionFrames[transitionFrames.length - 1] =
|
|
14722
|
+
transitionFrames[0] = from;
|
|
14723
|
+
transitionFrames[transitionFrames.length - 1] = to2;
|
|
14669
14724
|
return transitionFrames;
|
|
14670
14725
|
} catch (error) {
|
|
14671
14726
|
logger.error("[AvatarView] Failed to generate transition from idle:", error instanceof Error ? error.message : String(error));
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Flame } from '../generated/driveningress/v1/driveningress';
|
|
2
2
|
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function bezierLerp(from: Flame, to: Flame, progress: number): Flame;
|
|
4
4
|
|
|
5
5
|
export declare function generateTransitionFrames(from: Flame, to: Flame, durationMs: number, fps?: number): Flame[];
|
|
6
6
|
|
package/package.json
CHANGED