@spatialwalk/avatarkit 1.0.0-beta.63 → 1.0.0-beta.65
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 +31 -0
- package/README.md +67 -3
- package/dist/{StreamingAudioPlayer-CO9WTktN.js → StreamingAudioPlayer-GEsQnXc9.js} +1 -1
- package/dist/avatar_core_wasm-Dv943JJl.js +2696 -0
- package/dist/avatar_core_wasm.wasm +0 -0
- package/dist/core/AvatarView.d.ts +1 -0
- package/dist/{index-C1md-jKJ.js → index-xTAIRBMF.js} +139 -41
- package/dist/index.js +3 -3
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/animation-interpolation.d.ts +4 -0
- package/dist/vanilla/vite.config.d.ts +1 -1
- package/dist/vite.d.ts +4 -0
- package/package.json +23 -14
- package/vite.d.ts +20 -0
- package/vite.js +110 -0
- package/dist/avatar_core_wasm-i0Ocpx6q.js +0 -2693
|
Binary file
|
|
@@ -7173,11 +7173,11 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
7173
7173
|
ErrorCode2["failedToDownloadAvatarAssets"] = "failedToDownloadAvatarAssets";
|
|
7174
7174
|
return ErrorCode2;
|
|
7175
7175
|
})(ErrorCode || {});
|
|
7176
|
-
class
|
|
7176
|
+
class AvatarError extends Error {
|
|
7177
7177
|
constructor(message, code) {
|
|
7178
7178
|
super(message);
|
|
7179
7179
|
this.code = code;
|
|
7180
|
-
this.name = "
|
|
7180
|
+
this.name = "AvatarError";
|
|
7181
7181
|
}
|
|
7182
7182
|
}
|
|
7183
7183
|
{
|
|
@@ -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-GEsQnXc9.js");
|
|
7628
7628
|
const { AvatarSDK: AvatarSDK2 } = await Promise.resolve().then(() => AvatarSDK$1);
|
|
7629
7629
|
const audioFormat = AvatarSDK2.getAudioFormat();
|
|
7630
7630
|
this.streamingPlayer = new StreamingAudioPlayer({
|
|
@@ -8180,7 +8180,7 @@ class AvatarCoreAdapter {
|
|
|
8180
8180
|
}
|
|
8181
8181
|
async loadWASMModule() {
|
|
8182
8182
|
try {
|
|
8183
|
-
const { default: AvatarCoreModule } = await import("./avatar_core_wasm-
|
|
8183
|
+
const { default: AvatarCoreModule } = await import("./avatar_core_wasm-Dv943JJl.js");
|
|
8184
8184
|
if (this.wasmConfig && Object.keys(this.wasmConfig).length > 0) {
|
|
8185
8185
|
this.wasmModule = await AvatarCoreModule(this.wasmConfig);
|
|
8186
8186
|
} else {
|
|
@@ -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.65");
|
|
8965
8965
|
__publicField(AvatarSDK, "_avatarCore", null);
|
|
8966
8966
|
__publicField(AvatarSDK, "_dynamicSdkConfig", null);
|
|
8967
8967
|
const AvatarSDK$1 = Object.freeze(Object.defineProperty({
|
|
@@ -10156,7 +10156,7 @@ class AnimationWebSocketClient extends EventEmitter {
|
|
|
10156
10156
|
});
|
|
10157
10157
|
}
|
|
10158
10158
|
if (sdkErrorCode) {
|
|
10159
|
-
const error = new
|
|
10159
|
+
const error = new AvatarError(
|
|
10160
10160
|
reason || `WebSocket connection failed with code ${event.code}`,
|
|
10161
10161
|
sdkErrorCode
|
|
10162
10162
|
);
|
|
@@ -10313,7 +10313,7 @@ class NetworkLayer {
|
|
|
10313
10313
|
var _a, _b, _c, _d;
|
|
10314
10314
|
if (this.isConnecting) {
|
|
10315
10315
|
logger.warn("[NetworkLayer] Connection already in progress, waiting for current connection to complete");
|
|
10316
|
-
throw new
|
|
10316
|
+
throw new AvatarError("Connection already in progress", "CONNECTION_IN_PROGRESS");
|
|
10317
10317
|
}
|
|
10318
10318
|
(_b = (_a = this.dataController).onConnectionState) == null ? void 0 : _b.call(_a, ConnectionState.connecting);
|
|
10319
10319
|
this.isFallbackMode = false;
|
|
@@ -10391,7 +10391,7 @@ class NetworkLayer {
|
|
|
10391
10391
|
const sent = this.wsClient.sendAudioData(this.currentConversationId, audioData, isLast);
|
|
10392
10392
|
if (!sent) {
|
|
10393
10393
|
logger.error("[NetworkLayer] Failed to send audio data to WebSocket");
|
|
10394
|
-
(_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new
|
|
10394
|
+
(_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new AvatarError("Failed to send audio data to WebSocket", "SEND_FAILED"));
|
|
10395
10395
|
logEvent("send_audio_failed", "error", {
|
|
10396
10396
|
req_id: this.currentConversationId || "",
|
|
10397
10397
|
con_id: idManager.getConnectionId() || "",
|
|
@@ -10549,7 +10549,7 @@ class NetworkLayer {
|
|
|
10549
10549
|
} else {
|
|
10550
10550
|
sdkErrorCode = errorCodeStr || "SERVER_ERROR";
|
|
10551
10551
|
}
|
|
10552
|
-
(_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new
|
|
10552
|
+
(_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new AvatarError(
|
|
10553
10553
|
errorMessage,
|
|
10554
10554
|
sdkErrorCode
|
|
10555
10555
|
));
|
|
@@ -10755,7 +10755,7 @@ class AvatarController {
|
|
|
10755
10755
|
} catch (error) {
|
|
10756
10756
|
const message = error instanceof Error ? error.message : String(error);
|
|
10757
10757
|
logger.error("[AvatarController] Failed to initialize audio context:", message);
|
|
10758
|
-
throw new
|
|
10758
|
+
throw new AvatarError(
|
|
10759
10759
|
`Failed to initialize audio context: ${message}`,
|
|
10760
10760
|
"AUDIO_CONTEXT_INIT_FAILED"
|
|
10761
10761
|
);
|
|
@@ -10776,7 +10776,7 @@ class AvatarController {
|
|
|
10776
10776
|
checkAudioContextInitialized() {
|
|
10777
10777
|
var _a;
|
|
10778
10778
|
if (!((_a = this.animationPlayer) == null ? void 0 : _a.isStreamingReady())) {
|
|
10779
|
-
throw new
|
|
10779
|
+
throw new AvatarError(
|
|
10780
10780
|
"Audio context not initialized. Call initializeAudioContext() in a user gesture context first.",
|
|
10781
10781
|
"AUDIO_CONTEXT_NOT_INITIALIZED"
|
|
10782
10782
|
);
|
|
@@ -10784,7 +10784,7 @@ class AvatarController {
|
|
|
10784
10784
|
}
|
|
10785
10785
|
async start() {
|
|
10786
10786
|
if (!this.networkLayer) {
|
|
10787
|
-
throw new
|
|
10787
|
+
throw new AvatarError(
|
|
10788
10788
|
"Network layer not available. Use SDK mode.",
|
|
10789
10789
|
"NETWORK_LAYER_NOT_AVAILABLE"
|
|
10790
10790
|
);
|
|
@@ -10801,11 +10801,11 @@ class AvatarController {
|
|
|
10801
10801
|
return null;
|
|
10802
10802
|
}
|
|
10803
10803
|
if (!this.networkLayer) {
|
|
10804
|
-
(_b = this.onError) == null ? void 0 : _b.call(this, new
|
|
10804
|
+
(_b = this.onError) == null ? void 0 : _b.call(this, new AvatarError("Network layer not available", "NETWORK_LAYER_NOT_AVAILABLE"));
|
|
10805
10805
|
return null;
|
|
10806
10806
|
}
|
|
10807
10807
|
if (!this.networkLayer.canSend()) {
|
|
10808
|
-
(_c = this.onError) == null ? void 0 : _c.call(this, new
|
|
10808
|
+
(_c = this.onError) == null ? void 0 : _c.call(this, new AvatarError("Service not connected", "NOT_CONNECTED"));
|
|
10809
10809
|
logEvent("character_manager", "warning", {
|
|
10810
10810
|
avatar_id: this.avatar.id,
|
|
10811
10811
|
event: "send_not_connected"
|
|
@@ -10866,7 +10866,7 @@ class AvatarController {
|
|
|
10866
10866
|
this.isAudioOnlyMode = true;
|
|
10867
10867
|
}
|
|
10868
10868
|
if (this.pendingAudioChunks.length === 0) {
|
|
10869
|
-
throw new
|
|
10869
|
+
throw new AvatarError("No audio chunks to play", "NO_AUDIO");
|
|
10870
10870
|
}
|
|
10871
10871
|
if (this.isAudioOnlyMode) {
|
|
10872
10872
|
await this.startAudioOnlyPlayback();
|
|
@@ -11022,7 +11022,7 @@ class AvatarController {
|
|
|
11022
11022
|
var _a;
|
|
11023
11023
|
this.isStartingPlayback = false;
|
|
11024
11024
|
logger.error("[AvatarController] Failed to auto-start playback:", error);
|
|
11025
|
-
(_a = this.onError) == null ? void 0 : _a.call(this, new
|
|
11025
|
+
(_a = this.onError) == null ? void 0 : _a.call(this, new AvatarError("Failed to start playback", "PLAYBACK_START_FAILED"));
|
|
11026
11026
|
});
|
|
11027
11027
|
}
|
|
11028
11028
|
}
|
|
@@ -11287,7 +11287,7 @@ class AvatarController {
|
|
|
11287
11287
|
}
|
|
11288
11288
|
try {
|
|
11289
11289
|
if (!this.animationPlayer) {
|
|
11290
|
-
throw new
|
|
11290
|
+
throw new AvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
|
|
11291
11291
|
}
|
|
11292
11292
|
await this.animationPlayer.prepareStreamingPlayer(() => {
|
|
11293
11293
|
var _a2, _b2;
|
|
@@ -11331,7 +11331,7 @@ class AvatarController {
|
|
|
11331
11331
|
} catch (error) {
|
|
11332
11332
|
const message = error instanceof Error ? error.message : String(error);
|
|
11333
11333
|
logger.error("[AvatarController] Failed to start streaming playback:", message);
|
|
11334
|
-
(_c = this.onError) == null ? void 0 : _c.call(this, new
|
|
11334
|
+
(_c = this.onError) == null ? void 0 : _c.call(this, new AvatarError("Failed to start streaming playback", "INIT_FAILED"));
|
|
11335
11335
|
this.isPlaying = false;
|
|
11336
11336
|
this.isStartingPlayback = false;
|
|
11337
11337
|
}
|
|
@@ -11510,7 +11510,7 @@ class AvatarController {
|
|
|
11510
11510
|
async startAudioOnlyPlayback() {
|
|
11511
11511
|
var _a, _b;
|
|
11512
11512
|
if (!this.animationPlayer) {
|
|
11513
|
-
throw new
|
|
11513
|
+
throw new AvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
|
|
11514
11514
|
}
|
|
11515
11515
|
try {
|
|
11516
11516
|
await this.animationPlayer.prepareStreamingPlayer(() => {
|
|
@@ -11547,7 +11547,7 @@ class AvatarController {
|
|
|
11547
11547
|
} catch (error) {
|
|
11548
11548
|
const message = error instanceof Error ? error.message : String(error);
|
|
11549
11549
|
logger.error("[AvatarController] Failed to start audio-only playback:", message);
|
|
11550
|
-
(_b = this.onError) == null ? void 0 : _b.call(this, new
|
|
11550
|
+
(_b = this.onError) == null ? void 0 : _b.call(this, new AvatarError("Failed to start audio-only playback", "AUDIO_ONLY_INIT_FAILED"));
|
|
11551
11551
|
this.isPlaying = false;
|
|
11552
11552
|
this.isAudioOnlyMode = false;
|
|
11553
11553
|
throw error;
|
|
@@ -12096,9 +12096,10 @@ class AvatarDownloader {
|
|
|
12096
12096
|
const idleAnimationUrl = (_i2 = (_h = (_g = characterMeta.animations) == null ? void 0 : _g.frameIdle) == null ? void 0 : _h.resource) == null ? void 0 : _i2.remote;
|
|
12097
12097
|
if (!shapeUrl || !pointCloudUrl) {
|
|
12098
12098
|
const reason = "Missing required resources: shape or gsStandard (point cloud)";
|
|
12099
|
-
logEvent("
|
|
12099
|
+
logEvent("character_load", "error", {
|
|
12100
12100
|
avatar_id: characterMeta.characterId ?? "unknown",
|
|
12101
|
-
description:
|
|
12101
|
+
description: "Character does not exist in target environment",
|
|
12102
|
+
reason
|
|
12102
12103
|
});
|
|
12103
12104
|
throw new Error(reason);
|
|
12104
12105
|
}
|
|
@@ -12146,6 +12147,14 @@ class AvatarDownloader {
|
|
|
12146
12147
|
return { key, success: true, size: arrayBuffer.byteLength };
|
|
12147
12148
|
} catch (error) {
|
|
12148
12149
|
if (!optional) {
|
|
12150
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
12151
|
+
logEvent("download_avatar_assets_failed", "error", {
|
|
12152
|
+
avatar_id: characterMeta.characterId ?? "unknown",
|
|
12153
|
+
description: `Failed to download required resource: ${filename}`,
|
|
12154
|
+
resource: key,
|
|
12155
|
+
url,
|
|
12156
|
+
error: errorMessage
|
|
12157
|
+
});
|
|
12149
12158
|
throw error;
|
|
12150
12159
|
}
|
|
12151
12160
|
logger.warn(`⚠️ Optional resource ${filename} failed to load:`, error);
|
|
@@ -12232,7 +12241,7 @@ class AvatarDownloader {
|
|
|
12232
12241
|
avatar_id: extractedCharacterId,
|
|
12233
12242
|
description: errorMessage
|
|
12234
12243
|
});
|
|
12235
|
-
error = new
|
|
12244
|
+
error = new AvatarError(errorMessage, ErrorCode.avatarIDUnrecognized);
|
|
12236
12245
|
} else {
|
|
12237
12246
|
error = new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
12238
12247
|
}
|
|
@@ -12262,7 +12271,7 @@ class AvatarDownloader {
|
|
|
12262
12271
|
return response;
|
|
12263
12272
|
} catch (error) {
|
|
12264
12273
|
logger.errorWithError("Failed to fetch character:", error);
|
|
12265
|
-
if (error instanceof
|
|
12274
|
+
if (error instanceof AvatarError) {
|
|
12266
12275
|
logEvent("fetch_avatar_metadata_failed", "error", {
|
|
12267
12276
|
avatar_id: characterId ?? "unknown",
|
|
12268
12277
|
description: error.message
|
|
@@ -12388,10 +12397,6 @@ const _AvatarManager = class _AvatarManager {
|
|
|
12388
12397
|
} catch (error) {
|
|
12389
12398
|
const message = error instanceof Error ? error.message : String(error);
|
|
12390
12399
|
logger.error("Failed to load avatar:", message);
|
|
12391
|
-
logEvent("character_load", "error", {
|
|
12392
|
-
avatar_id: id,
|
|
12393
|
-
reason: message
|
|
12394
|
-
});
|
|
12395
12400
|
onProgress == null ? void 0 : onProgress({ type: LoadProgress.failed, error });
|
|
12396
12401
|
throw error;
|
|
12397
12402
|
}
|
|
@@ -13860,6 +13865,38 @@ function lerpArrays(from, to2, progress) {
|
|
|
13860
13865
|
return result2;
|
|
13861
13866
|
}
|
|
13862
13867
|
const clamp01 = (x2) => Math.max(0, Math.min(1, x2));
|
|
13868
|
+
function linearLerp(from, to2, progress) {
|
|
13869
|
+
return {
|
|
13870
|
+
translation: lerpArrays(from.translation || [0, 0, 0], to2.translation || [0, 0, 0], progress),
|
|
13871
|
+
rotation: lerpArrays(from.rotation || [0, 0, 0], to2.rotation || [0, 0, 0], progress),
|
|
13872
|
+
neckPose: lerpArrays(from.neckPose || [0, 0, 0], to2.neckPose || [0, 0, 0], progress),
|
|
13873
|
+
jawPose: lerpArrays(from.jawPose || [0, 0, 0], to2.jawPose || [0, 0, 0], progress),
|
|
13874
|
+
eyePose: lerpArrays(from.eyePose || [0, 0, 0, 0, 0, 0], to2.eyePose || [0, 0, 0, 0, 0, 0], progress),
|
|
13875
|
+
eyeLid: (() => {
|
|
13876
|
+
const fromEyelid = from.eyeLid;
|
|
13877
|
+
const toEyelid = to2.eyeLid;
|
|
13878
|
+
if ((fromEyelid == null ? void 0 : fromEyelid.length) && (toEyelid == null ? void 0 : toEyelid.length))
|
|
13879
|
+
return lerpArrays(fromEyelid, toEyelid, progress);
|
|
13880
|
+
return fromEyelid || toEyelid || [];
|
|
13881
|
+
})(),
|
|
13882
|
+
expression: lerpArrays(from.expression || [], to2.expression || [], progress)
|
|
13883
|
+
};
|
|
13884
|
+
}
|
|
13885
|
+
function generateTransitionFramesLinear(from, to2, durationMs, fps = 25) {
|
|
13886
|
+
const steps = Math.max(1, Math.floor(durationMs / 1e3 * fps));
|
|
13887
|
+
const frames = Array.from({ length: steps });
|
|
13888
|
+
if (steps === 1) {
|
|
13889
|
+
frames[0] = to2;
|
|
13890
|
+
return frames;
|
|
13891
|
+
}
|
|
13892
|
+
for (let i2 = 0; i2 < steps; i2++) {
|
|
13893
|
+
const progress = i2 / (steps - 1);
|
|
13894
|
+
frames[i2] = linearLerp(from, to2, progress);
|
|
13895
|
+
}
|
|
13896
|
+
frames[0] = from;
|
|
13897
|
+
frames[frames.length - 1] = to2;
|
|
13898
|
+
return frames;
|
|
13899
|
+
}
|
|
13863
13900
|
function createBezierEasing(x1, y1, x2, y2) {
|
|
13864
13901
|
const cx = 3 * x1;
|
|
13865
13902
|
const bx = 3 * (x2 - x1) - cx;
|
|
@@ -13963,6 +14000,7 @@ class AvatarView {
|
|
|
13963
14000
|
__publicField(this, "endTransitionDurationMs", 1600);
|
|
13964
14001
|
__publicField(this, "cachedIdleFirstFrame", null);
|
|
13965
14002
|
__publicField(this, "idleCurrentFrameIndex", 0);
|
|
14003
|
+
__publicField(this, "currentPlayingFrame", null);
|
|
13966
14004
|
__publicField(this, "characterHandle", null);
|
|
13967
14005
|
__publicField(this, "characterId");
|
|
13968
14006
|
__publicField(this, "isPureRenderingMode", false);
|
|
@@ -14017,9 +14055,14 @@ class AvatarView {
|
|
|
14017
14055
|
toFixed.expression = ensureLen(toFixed.expression, exprLen);
|
|
14018
14056
|
return { from: fromFixed, to: toFixed };
|
|
14019
14057
|
}
|
|
14020
|
-
generateAndAlignTransitionFrames(from, to2, durationMs) {
|
|
14058
|
+
generateAndAlignTransitionFrames(from, to2, durationMs, useLinearInterpolation = false) {
|
|
14021
14059
|
const aligned = this.alignFlamePair(from, to2);
|
|
14022
|
-
let keyframes =
|
|
14060
|
+
let keyframes = useLinearInterpolation ? generateTransitionFramesLinear(
|
|
14061
|
+
aligned.from,
|
|
14062
|
+
aligned.to,
|
|
14063
|
+
durationMs,
|
|
14064
|
+
APP_CONFIG.animation.fps
|
|
14065
|
+
) : generateTransitionFrames(
|
|
14023
14066
|
aligned.from,
|
|
14024
14067
|
aligned.to,
|
|
14025
14068
|
durationMs,
|
|
@@ -14386,6 +14429,7 @@ class AvatarView {
|
|
|
14386
14429
|
const steps = this.transitionKeyframes.length;
|
|
14387
14430
|
const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
|
|
14388
14431
|
const currentFrame = this.transitionKeyframes[idx];
|
|
14432
|
+
this.currentPlayingFrame = currentFrame;
|
|
14389
14433
|
const wasmParams = convertProtoFlameToWasmParams(currentFrame);
|
|
14390
14434
|
const avatarCore = AvatarSDK.getAvatarCore();
|
|
14391
14435
|
if (avatarCore) {
|
|
@@ -14458,6 +14502,7 @@ class AvatarView {
|
|
|
14458
14502
|
this.lastRenderedFrameIndex = frameIndex;
|
|
14459
14503
|
if (frameIndex >= 0 && frameIndex < this.currentKeyframes.length) {
|
|
14460
14504
|
this.lastRealtimeProtoFrame = this.currentKeyframes[frameIndex];
|
|
14505
|
+
this.currentPlayingFrame = this.lastRealtimeProtoFrame;
|
|
14461
14506
|
}
|
|
14462
14507
|
}
|
|
14463
14508
|
setState(newState) {
|
|
@@ -14475,6 +14520,7 @@ class AvatarView {
|
|
|
14475
14520
|
this.lastRealtimeProtoFrame = null;
|
|
14476
14521
|
this.transitionKeyframes = [];
|
|
14477
14522
|
this.transitionStartTime = 0;
|
|
14523
|
+
this.currentPlayingFrame = null;
|
|
14478
14524
|
}
|
|
14479
14525
|
}
|
|
14480
14526
|
get isRealtimePlaying() {
|
|
@@ -14533,13 +14579,33 @@ class AvatarView {
|
|
|
14533
14579
|
if (this.renderingState !== "transitioningToSpeaking") {
|
|
14534
14580
|
return;
|
|
14535
14581
|
}
|
|
14536
|
-
|
|
14537
|
-
|
|
14582
|
+
let fromFrame = this.currentPlayingFrame;
|
|
14583
|
+
if (!fromFrame) {
|
|
14584
|
+
if (state === "idle") {
|
|
14585
|
+
const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
|
|
14586
|
+
fromFrame = convertWasmParamsToProtoFlame(idleParams);
|
|
14587
|
+
} else if (state === "transitioningToIdle" || state === "transitioningToSpeaking") {
|
|
14588
|
+
if (this.transitionKeyframes.length > 0) {
|
|
14589
|
+
const elapsed = performance.now() - this.transitionStartTime;
|
|
14590
|
+
const currentTransitionDurationMs = state === "transitioningToSpeaking" ? this.startTransitionDurationMs : this.endTransitionDurationMs;
|
|
14591
|
+
const progress = Math.min(1, Math.max(0, elapsed / currentTransitionDurationMs));
|
|
14592
|
+
const steps = this.transitionKeyframes.length;
|
|
14593
|
+
const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
|
|
14594
|
+
fromFrame = this.transitionKeyframes[idx];
|
|
14595
|
+
}
|
|
14596
|
+
}
|
|
14597
|
+
}
|
|
14598
|
+
if (!fromFrame) {
|
|
14599
|
+
logger.warn("[AvatarView] Cannot get current playing frame, fallback to idle frame");
|
|
14600
|
+
const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
|
|
14601
|
+
fromFrame = convertWasmParamsToProtoFlame(idleParams);
|
|
14602
|
+
}
|
|
14538
14603
|
await this.getCachedIdleFirstFrame();
|
|
14539
14604
|
const firstSpeaking = keyframes[0];
|
|
14540
14605
|
const firstSpeakingWithPostProcessing = this.avatarController.applyPostProcessingToFlame(firstSpeaking);
|
|
14541
|
-
this.transitionKeyframes = this.generateAndAlignTransitionFrames(
|
|
14606
|
+
this.transitionKeyframes = this.generateAndAlignTransitionFrames(fromFrame, firstSpeakingWithPostProcessing, this.startTransitionDurationMs, true);
|
|
14542
14607
|
this.transitionStartTime = performance.now();
|
|
14608
|
+
this.currentPlayingFrame = null;
|
|
14543
14609
|
if (this.transitionKeyframes.length === 0) {
|
|
14544
14610
|
this.setState("speaking");
|
|
14545
14611
|
this.avatarController.onTransitionComplete();
|
|
@@ -14572,7 +14638,10 @@ class AvatarView {
|
|
|
14572
14638
|
if (state === "idle" || state === "transitioningToIdle") {
|
|
14573
14639
|
return;
|
|
14574
14640
|
}
|
|
14575
|
-
if (state !== "speaking") {
|
|
14641
|
+
if (state !== "speaking" && state !== "transitioningToSpeaking") {
|
|
14642
|
+
if (state === "transitioningToIdle") {
|
|
14643
|
+
return;
|
|
14644
|
+
}
|
|
14576
14645
|
this.setState("idle");
|
|
14577
14646
|
this.stopRealtimeAnimationLoop();
|
|
14578
14647
|
this.startIdleAnimationLoop();
|
|
@@ -14586,13 +14655,35 @@ class AvatarView {
|
|
|
14586
14655
|
return;
|
|
14587
14656
|
}
|
|
14588
14657
|
const avatarCore = AvatarSDK.getAvatarCore();
|
|
14589
|
-
if (avatarCore
|
|
14590
|
-
|
|
14591
|
-
|
|
14658
|
+
if (avatarCore) {
|
|
14659
|
+
let fromFrame = this.currentPlayingFrame;
|
|
14660
|
+
if (!fromFrame) {
|
|
14661
|
+
if (this.lastRealtimeProtoFrame) {
|
|
14662
|
+
fromFrame = this.lastRealtimeProtoFrame;
|
|
14663
|
+
} else if (this.currentKeyframes.length > 0) {
|
|
14664
|
+
fromFrame = this.currentKeyframes[Math.max(0, this.lastRenderedFrameIndex)];
|
|
14665
|
+
}
|
|
14666
|
+
}
|
|
14667
|
+
if (!fromFrame && this.transitionKeyframes.length > 0) {
|
|
14668
|
+
const elapsed = performance.now() - this.transitionStartTime;
|
|
14669
|
+
const progress = Math.min(1, Math.max(0, elapsed / this.endTransitionDurationMs));
|
|
14670
|
+
const steps = this.transitionKeyframes.length;
|
|
14671
|
+
const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
|
|
14672
|
+
fromFrame = this.transitionKeyframes[idx];
|
|
14673
|
+
}
|
|
14674
|
+
if (!fromFrame) {
|
|
14675
|
+
logger.warn("[AvatarView] Cannot get current playing frame for transition to idle, fallback to idle frame");
|
|
14676
|
+
this.setState("idle");
|
|
14677
|
+
this.stopRealtimeAnimationLoop();
|
|
14678
|
+
this.startIdleAnimationLoop();
|
|
14679
|
+
return;
|
|
14680
|
+
}
|
|
14681
|
+
const fromFrameWithPostProcessing = this.avatarController.applyPostProcessingToFlame(fromFrame);
|
|
14592
14682
|
const idleFirstProto = await this.getCachedIdleFirstFrame();
|
|
14593
14683
|
if (idleFirstProto) {
|
|
14594
|
-
this.transitionKeyframes = this.generateAndAlignTransitionFrames(
|
|
14684
|
+
this.transitionKeyframes = this.generateAndAlignTransitionFrames(fromFrameWithPostProcessing, idleFirstProto, this.endTransitionDurationMs);
|
|
14595
14685
|
this.transitionStartTime = performance.now();
|
|
14686
|
+
this.currentPlayingFrame = null;
|
|
14596
14687
|
if (this.transitionKeyframes.length > 0 && this.renderingState === "transitioningToIdle") {
|
|
14597
14688
|
if (APP_CONFIG.debug)
|
|
14598
14689
|
logger.log("[AvatarView] Return transition started:", this.transitionKeyframes.length, "frames");
|
|
@@ -14629,6 +14720,7 @@ class AvatarView {
|
|
|
14629
14720
|
this.setState("idle");
|
|
14630
14721
|
this.cachedIdleFirstFrame = null;
|
|
14631
14722
|
this.idleCurrentFrameIndex = 0;
|
|
14723
|
+
this.currentPlayingFrame = null;
|
|
14632
14724
|
const avatarCore = AvatarSDK.getAvatarCore();
|
|
14633
14725
|
if (avatarCore && this.characterHandle) {
|
|
14634
14726
|
try {
|
|
@@ -14721,7 +14813,13 @@ class AvatarView {
|
|
|
14721
14813
|
const to2 = transitionType === "start" ? aligned.to : aligned.from;
|
|
14722
14814
|
const fps = APP_CONFIG.animation.fps;
|
|
14723
14815
|
const durationMs = frameCount / fps * 1e3;
|
|
14724
|
-
const
|
|
14816
|
+
const useLinear = transitionType === "start";
|
|
14817
|
+
const transitionFrames = useLinear ? generateTransitionFramesLinear(
|
|
14818
|
+
from,
|
|
14819
|
+
to2,
|
|
14820
|
+
durationMs,
|
|
14821
|
+
fps
|
|
14822
|
+
) : generateTransitionFrames(
|
|
14725
14823
|
from,
|
|
14726
14824
|
to2,
|
|
14727
14825
|
durationMs,
|
|
@@ -14804,7 +14902,6 @@ export {
|
|
|
14804
14902
|
Environment as E,
|
|
14805
14903
|
LogLevel as L,
|
|
14806
14904
|
ResourceType as R,
|
|
14807
|
-
SPAvatarError as S,
|
|
14808
14905
|
logEvent as a,
|
|
14809
14906
|
Avatar as b,
|
|
14810
14907
|
AvatarController as c,
|
|
@@ -14817,5 +14914,6 @@ export {
|
|
|
14817
14914
|
AvatarState as j,
|
|
14818
14915
|
ErrorCode as k,
|
|
14819
14916
|
logger as l,
|
|
14820
|
-
|
|
14917
|
+
AvatarError as m,
|
|
14918
|
+
extractResourceUrls as n
|
|
14821
14919
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { b, c, f, d, j, g, C, i, D, E, k, h, L, R,
|
|
1
|
+
import { b, c, m, f, d, j, g, C, i, D, E, k, h, L, R, n } from "./index-xTAIRBMF.js";
|
|
2
2
|
export {
|
|
3
3
|
b as Avatar,
|
|
4
4
|
c as AvatarController,
|
|
5
|
+
m as AvatarError,
|
|
5
6
|
f as AvatarManager,
|
|
6
7
|
d as AvatarSDK,
|
|
7
8
|
j as AvatarState,
|
|
@@ -14,6 +15,5 @@ export {
|
|
|
14
15
|
h as LoadProgress,
|
|
15
16
|
L as LogLevel,
|
|
16
17
|
R as ResourceType,
|
|
17
|
-
|
|
18
|
-
m as extractResourceUrls
|
|
18
|
+
n as extractResourceUrls
|
|
19
19
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ export declare enum ErrorCode {
|
|
|
74
74
|
|
|
75
75
|
failedToDownloadAvatarAssets = "failedToDownloadAvatarAssets"
|
|
76
76
|
}
|
|
77
|
-
export declare class
|
|
77
|
+
export declare class AvatarError extends Error {
|
|
78
78
|
code?: (string | ErrorCode) | undefined;
|
|
79
79
|
constructor(message: string, code?: (string | ErrorCode) | undefined);
|
|
80
80
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Flame } from '../generated/driveningress/v1/driveningress';
|
|
2
2
|
|
|
3
|
+
export declare function linearLerp(from: Flame, to: Flame, progress: number): Flame;
|
|
4
|
+
|
|
5
|
+
export declare function generateTransitionFramesLinear(from: Flame, to: Flame, durationMs: number, fps?: number): Flame[];
|
|
6
|
+
|
|
3
7
|
export declare function bezierLerp(from: Flame, to: Flame, progress: number): Flame;
|
|
4
8
|
|
|
5
9
|
export declare function generateTransitionFrames(from: Flame, to: Flame, durationMs: number, fps?: number): Flame[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: any;
|
|
2
2
|
export default _default;
|
package/dist/vite.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spatialwalk/avatarkit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-beta.
|
|
4
|
+
"version": "1.0.0-beta.65",
|
|
5
|
+
"packageManager": "pnpm@10.18.2",
|
|
5
6
|
"description": "SPAvatar SDK - 3D Gaussian Splatting Avatar Rendering SDK",
|
|
6
7
|
"author": "SPAvatar Team",
|
|
7
8
|
"license": "MIT",
|
|
@@ -18,6 +19,10 @@
|
|
|
18
19
|
"types": "./dist/index.d.ts",
|
|
19
20
|
"import": "./dist/index.js"
|
|
20
21
|
},
|
|
22
|
+
"./vite": {
|
|
23
|
+
"types": "./vite.d.ts",
|
|
24
|
+
"import": "./vite.js"
|
|
25
|
+
},
|
|
21
26
|
"./*": {
|
|
22
27
|
"types": "./dist/*.d.ts",
|
|
23
28
|
"import": "./dist/*.js"
|
|
@@ -29,10 +34,24 @@
|
|
|
29
34
|
"files": [
|
|
30
35
|
"README.md",
|
|
31
36
|
"CHANGELOG.md",
|
|
32
|
-
"dist"
|
|
37
|
+
"dist",
|
|
38
|
+
"vite.js",
|
|
39
|
+
"vite.d.ts"
|
|
33
40
|
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "SDK_BUILD=true vite build --mode library && npm run build:vite-plugin",
|
|
43
|
+
"build:vite-plugin": "tsc vite.ts --outDir . --module esnext --target es2020 --moduleResolution bundler --esModuleInterop --skipLibCheck --declaration --declarationMap",
|
|
44
|
+
"dev": "vite build --mode library --watch",
|
|
45
|
+
"clean": "rm -rf dist",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"test": "cd tests && pnpm test",
|
|
48
|
+
"test:watch": "cd tests && pnpm run test:watch",
|
|
49
|
+
"test:e2e": "cd tests && pnpm run test:e2e",
|
|
50
|
+
"test:perf": "cd tests && pnpm run test:perf"
|
|
51
|
+
},
|
|
34
52
|
"peerDependencies": {
|
|
35
|
-
"@webgpu/types": "*"
|
|
53
|
+
"@webgpu/types": "*",
|
|
54
|
+
"vite": "^5.0.0"
|
|
36
55
|
},
|
|
37
56
|
"dependencies": {
|
|
38
57
|
"@bufbuild/protobuf": "^2.10.0",
|
|
@@ -47,15 +66,5 @@
|
|
|
47
66
|
"typescript": "^5.0.0",
|
|
48
67
|
"vite": "^5.0.0",
|
|
49
68
|
"vite-plugin-dts": "^4.5.4"
|
|
50
|
-
},
|
|
51
|
-
"scripts": {
|
|
52
|
-
"build": "SDK_BUILD=true vite build --mode library",
|
|
53
|
-
"dev": "vite build --mode library --watch",
|
|
54
|
-
"clean": "rm -rf dist",
|
|
55
|
-
"typecheck": "tsc --noEmit",
|
|
56
|
-
"test": "cd tests && pnpm test",
|
|
57
|
-
"test:watch": "cd tests && pnpm run test:watch",
|
|
58
|
-
"test:e2e": "cd tests && pnpm run test:e2e",
|
|
59
|
-
"test:perf": "cd tests && pnpm run test:perf"
|
|
60
69
|
}
|
|
61
|
-
}
|
|
70
|
+
}
|
package/vite.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Vite plugin for @spatialwalk/avatarkit
|
|
4
|
+
* Automatically handles WASM file configuration for development and production builds
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { defineConfig } from 'vite'
|
|
9
|
+
* import { avatarkitVitePlugin } from '@spatialwalk/avatarkit/vite'
|
|
10
|
+
*
|
|
11
|
+
* export default defineConfig({
|
|
12
|
+
* plugins: [
|
|
13
|
+
* avatarkitVitePlugin()
|
|
14
|
+
* ]
|
|
15
|
+
* })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function avatarkitVitePlugin(): Plugin;
|
|
19
|
+
export default avatarkitVitePlugin;
|
|
20
|
+
//# sourceMappingURL=vite.d.ts.map
|