@vizij/runtime-react 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/index.cjs +2432 -184
- package/dist/index.d.cts +142 -7
- package/dist/index.d.ts +142 -7
- package/dist/index.js +2411 -182
- package/package.json +7 -5
package/dist/index.cjs
CHANGED
|
@@ -20,8 +20,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
EMOTION_POSE_KEYS: () => EMOTION_POSE_KEYS,
|
|
24
|
+
EXPRESSIVE_EMOTION_POSE_KEYS: () => EXPRESSIVE_EMOTION_POSE_KEYS,
|
|
25
|
+
POSE_WEIGHT_INPUT_PATH_PREFIX: () => POSE_WEIGHT_INPUT_PATH_PREFIX,
|
|
26
|
+
VISEME_POSE_KEYS: () => VISEME_POSE_KEYS,
|
|
23
27
|
VizijRuntimeFace: () => VizijRuntimeFace,
|
|
24
28
|
VizijRuntimeProvider: () => VizijRuntimeProvider,
|
|
29
|
+
buildPoseWeightInputPathSegment: () => buildPoseWeightInputPathSegment,
|
|
30
|
+
buildPoseWeightPathMap: () => buildPoseWeightPathMap,
|
|
31
|
+
buildPoseWeightRelativePath: () => buildPoseWeightRelativePath,
|
|
32
|
+
buildRigInputPath: () => buildRigInputPath,
|
|
33
|
+
buildSemanticPoseWeightPathMap: () => buildSemanticPoseWeightPathMap,
|
|
34
|
+
filterPosesBySemanticKind: () => filterPosesBySemanticKind,
|
|
35
|
+
getPoseSemanticKey: () => getPoseSemanticKey,
|
|
36
|
+
mapNormalizedControlValue: () => mapNormalizedControlValue,
|
|
37
|
+
mapUnitControlValue: () => mapUnitControlValue,
|
|
38
|
+
normalizePoseSemanticKey: () => normalizePoseSemanticKey,
|
|
39
|
+
resolveFaceControls: () => resolveFaceControls,
|
|
40
|
+
resolvePoseMembership: () => resolvePoseMembership,
|
|
41
|
+
resolvePoseSemantics: () => resolvePoseSemantics,
|
|
42
|
+
resolveRuntimeUpdatePlan: () => resolveRuntimeUpdatePlan,
|
|
43
|
+
useOptionalVizijRuntime: () => useOptionalVizijRuntime,
|
|
25
44
|
useRigInput: () => useRigInput,
|
|
26
45
|
useVizijOutputs: () => useVizijOutputs,
|
|
27
46
|
useVizijRuntime: () => useVizijRuntime
|
|
@@ -34,11 +53,143 @@ var import_render = require("@vizij/render");
|
|
|
34
53
|
var import_orchestrator_react = require("@vizij/orchestrator-react");
|
|
35
54
|
var import_node_graph_authoring = require("@vizij/node-graph-authoring");
|
|
36
55
|
var import_value_json2 = require("@vizij/value-json");
|
|
56
|
+
var import_utils = require("@vizij/utils");
|
|
37
57
|
|
|
38
58
|
// src/context.ts
|
|
39
59
|
var import_react = require("react");
|
|
40
60
|
var VizijRuntimeContext = (0, import_react.createContext)(null);
|
|
41
61
|
|
|
62
|
+
// src/updatePolicy.ts
|
|
63
|
+
function applyRuntimeGraphBundle(base, bundle) {
|
|
64
|
+
const next = {
|
|
65
|
+
...base
|
|
66
|
+
};
|
|
67
|
+
const hasRigOverride = Object.prototype.hasOwnProperty.call(bundle, "rig");
|
|
68
|
+
const hasPoseOverride = Object.prototype.hasOwnProperty.call(bundle, "pose");
|
|
69
|
+
const hasAnimationsOverride = Object.prototype.hasOwnProperty.call(
|
|
70
|
+
bundle,
|
|
71
|
+
"animations"
|
|
72
|
+
);
|
|
73
|
+
const hasProgramsOverride = Object.prototype.hasOwnProperty.call(
|
|
74
|
+
bundle,
|
|
75
|
+
"programs"
|
|
76
|
+
);
|
|
77
|
+
if (hasRigOverride) {
|
|
78
|
+
if (bundle.rig) {
|
|
79
|
+
next.rig = bundle.rig;
|
|
80
|
+
} else {
|
|
81
|
+
delete next.rig;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (hasPoseOverride) {
|
|
85
|
+
if (bundle.pose) {
|
|
86
|
+
next.pose = bundle.pose;
|
|
87
|
+
} else {
|
|
88
|
+
delete next.pose;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (hasAnimationsOverride) {
|
|
92
|
+
next.animations = Array.isArray(bundle.animations) ? bundle.animations : void 0;
|
|
93
|
+
}
|
|
94
|
+
if (hasProgramsOverride) {
|
|
95
|
+
next.programs = Array.isArray(bundle.programs) ? bundle.programs : void 0;
|
|
96
|
+
}
|
|
97
|
+
return next;
|
|
98
|
+
}
|
|
99
|
+
function normalizeSpecPayload(value) {
|
|
100
|
+
if (!value) {
|
|
101
|
+
return "";
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
return JSON.stringify(value);
|
|
105
|
+
} catch {
|
|
106
|
+
return String(value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function glbSignature(glb) {
|
|
110
|
+
if (glb.kind === "url") {
|
|
111
|
+
return `url:${glb.src}`;
|
|
112
|
+
}
|
|
113
|
+
if (glb.kind === "blob") {
|
|
114
|
+
return `blob:${glb.blob?.size ?? 0}`;
|
|
115
|
+
}
|
|
116
|
+
return `world:${normalizeSpecPayload(glb.world)}`;
|
|
117
|
+
}
|
|
118
|
+
function graphSignature(graph) {
|
|
119
|
+
if (!graph) {
|
|
120
|
+
return "";
|
|
121
|
+
}
|
|
122
|
+
const id = graph.id ?? "";
|
|
123
|
+
return `${id}:${normalizeSpecPayload(graph.spec ?? graph.ir ?? null)}`;
|
|
124
|
+
}
|
|
125
|
+
function poseSignature(pose) {
|
|
126
|
+
if (!pose) {
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
const graph = pose.graph;
|
|
130
|
+
const config = pose.config;
|
|
131
|
+
const graphPart = graph ? graphSignature({ id: graph.id, spec: graph.spec }) : "";
|
|
132
|
+
const configPart = config ? normalizeSpecPayload(config) : "";
|
|
133
|
+
return `${graphPart}:${configPart}`;
|
|
134
|
+
}
|
|
135
|
+
function animationsSignature(animations) {
|
|
136
|
+
if (!Array.isArray(animations) || animations.length === 0) {
|
|
137
|
+
return "";
|
|
138
|
+
}
|
|
139
|
+
return animations.map((animation) => {
|
|
140
|
+
const id = animation.id ?? "";
|
|
141
|
+
const clipSignature = normalizeSpecPayload(animation.clip ?? null);
|
|
142
|
+
const setupSignature = normalizeSpecPayload(animation.setup ?? null);
|
|
143
|
+
const weightSignature = animation.weight == null ? "" : String(animation.weight);
|
|
144
|
+
return `${id}:${clipSignature}:${setupSignature}:${weightSignature}`;
|
|
145
|
+
}).sort().join("|");
|
|
146
|
+
}
|
|
147
|
+
function programsSignature(programs) {
|
|
148
|
+
if (!Array.isArray(programs) || programs.length === 0) {
|
|
149
|
+
return "";
|
|
150
|
+
}
|
|
151
|
+
return programs.map((program) => {
|
|
152
|
+
const id = program.id ?? "";
|
|
153
|
+
const label = program.label ?? "";
|
|
154
|
+
const graphId = program.graph?.id ?? "";
|
|
155
|
+
const graphPayload = normalizeSpecPayload(
|
|
156
|
+
program.graph?.spec ?? program.graph?.ir ?? null
|
|
157
|
+
);
|
|
158
|
+
const resetValues = normalizeSpecPayload(program.resetValues ?? null);
|
|
159
|
+
return `${id}:${label}:${graphId}:${graphPayload}:${resetValues}`;
|
|
160
|
+
}).sort().join("|");
|
|
161
|
+
}
|
|
162
|
+
function resolveRuntimeUpdatePlan(previous, next, tier) {
|
|
163
|
+
if (!previous) {
|
|
164
|
+
return { reloadAssets: true, reregisterGraphs: false };
|
|
165
|
+
}
|
|
166
|
+
const glbChanged = glbSignature(previous.glb) !== glbSignature(next.glb);
|
|
167
|
+
const rigChanged = graphSignature(previous.rig) !== graphSignature(next.rig);
|
|
168
|
+
const poseChanged = poseSignature(previous.pose) !== poseSignature(next.pose);
|
|
169
|
+
const rigReferenceChanged = previous.rig?.id !== next.rig?.id || previous.rig?.spec !== next.rig?.spec || previous.rig?.ir !== next.rig?.ir;
|
|
170
|
+
const poseReferenceChanged = previous.pose?.graph?.id !== next.pose?.graph?.id || previous.pose?.graph?.spec !== next.pose?.graph?.spec || previous.pose?.config !== next.pose?.config;
|
|
171
|
+
const graphsChanged = rigChanged || poseChanged || rigReferenceChanged || poseReferenceChanged;
|
|
172
|
+
const animationsChanged = animationsSignature(previous.animations) !== animationsSignature(next.animations);
|
|
173
|
+
const programsChanged = programsSignature(previous.programs) !== programsSignature(next.programs);
|
|
174
|
+
const controllersChanged = graphsChanged || animationsChanged || programsChanged;
|
|
175
|
+
if (tier === "assets") {
|
|
176
|
+
return { reloadAssets: true, reregisterGraphs: false };
|
|
177
|
+
}
|
|
178
|
+
if (tier === "graphs") {
|
|
179
|
+
if (glbChanged) {
|
|
180
|
+
return { reloadAssets: true, reregisterGraphs: false };
|
|
181
|
+
}
|
|
182
|
+
return { reloadAssets: false, reregisterGraphs: controllersChanged };
|
|
183
|
+
}
|
|
184
|
+
if (glbChanged) {
|
|
185
|
+
return { reloadAssets: true, reregisterGraphs: false };
|
|
186
|
+
}
|
|
187
|
+
if (controllersChanged) {
|
|
188
|
+
return { reloadAssets: false, reregisterGraphs: true };
|
|
189
|
+
}
|
|
190
|
+
return { reloadAssets: false, reregisterGraphs: false };
|
|
191
|
+
}
|
|
192
|
+
|
|
42
193
|
// src/utils/graph.ts
|
|
43
194
|
function getNodes(spec) {
|
|
44
195
|
if (!spec || typeof spec !== "object") {
|
|
@@ -83,6 +234,12 @@ function collectInputPaths(spec) {
|
|
|
83
234
|
}
|
|
84
235
|
function collectInputPathMap(spec) {
|
|
85
236
|
const map = {};
|
|
237
|
+
const addVariant = (key, path, force = false) => {
|
|
238
|
+
if (!key || !force && map[key]) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
map[key] = path;
|
|
242
|
+
};
|
|
86
243
|
const nodes = getNodes(spec);
|
|
87
244
|
nodes.forEach((node) => {
|
|
88
245
|
if (String(node.type ?? "").toLowerCase() !== "input") {
|
|
@@ -94,11 +251,653 @@ function collectInputPathMap(spec) {
|
|
|
94
251
|
}
|
|
95
252
|
const id = String(node.id ?? "");
|
|
96
253
|
const key = id.startsWith("input_") ? id.slice("input_".length) : id || path.trim();
|
|
97
|
-
|
|
254
|
+
const trimmedPath = path.trim();
|
|
255
|
+
addVariant(key, trimmedPath);
|
|
256
|
+
if (key.startsWith("direct_")) {
|
|
257
|
+
addVariant(key.slice("direct_".length), trimmedPath, true);
|
|
258
|
+
}
|
|
259
|
+
if (key.startsWith("pose_control_")) {
|
|
260
|
+
addVariant(key.slice("pose_control_".length), trimmedPath);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
return map;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/utils/posePaths.ts
|
|
267
|
+
var POSE_WEIGHT_INPUT_PATH_PREFIX = "/poses/";
|
|
268
|
+
var VISEME_POSE_KEYS = [
|
|
269
|
+
"a",
|
|
270
|
+
"at",
|
|
271
|
+
"b",
|
|
272
|
+
"e",
|
|
273
|
+
"e_2",
|
|
274
|
+
"f",
|
|
275
|
+
"i",
|
|
276
|
+
"k",
|
|
277
|
+
"m",
|
|
278
|
+
"o",
|
|
279
|
+
"o_2",
|
|
280
|
+
"p",
|
|
281
|
+
"r",
|
|
282
|
+
"s",
|
|
283
|
+
"t",
|
|
284
|
+
"t_2",
|
|
285
|
+
"u"
|
|
286
|
+
];
|
|
287
|
+
var EXPRESSIVE_EMOTION_POSE_KEYS = [
|
|
288
|
+
"concerned",
|
|
289
|
+
"happy",
|
|
290
|
+
"sad",
|
|
291
|
+
"sleepy",
|
|
292
|
+
"surprise"
|
|
293
|
+
];
|
|
294
|
+
var EMOTION_POSE_KEYS = [
|
|
295
|
+
"concerned",
|
|
296
|
+
"happy",
|
|
297
|
+
"neutral",
|
|
298
|
+
"sad",
|
|
299
|
+
"sleepy",
|
|
300
|
+
"surprise",
|
|
301
|
+
"angry"
|
|
302
|
+
];
|
|
303
|
+
var VISEME_GROUP_NEEDLES = ["viseme", "phoneme", "lip", "mouth"];
|
|
304
|
+
var EMOTION_GROUP_NEEDLES = ["emotion", "expression", "mood", "affect"];
|
|
305
|
+
var VISEME_POSE_KEY_SET = new Set(VISEME_POSE_KEYS);
|
|
306
|
+
var EMOTION_POSE_KEY_SET = new Set(EMOTION_POSE_KEYS);
|
|
307
|
+
var POSE_KEY_ALIASES = {
|
|
308
|
+
concern: "concerned",
|
|
309
|
+
surprised: "surprise"
|
|
310
|
+
};
|
|
311
|
+
function buildRigInputPath(faceId, path) {
|
|
312
|
+
let trimmed = path.startsWith("/") ? path.slice(1) : path;
|
|
313
|
+
if (!trimmed) {
|
|
314
|
+
return `rig/${faceId}`;
|
|
315
|
+
}
|
|
316
|
+
while (trimmed.startsWith("rig/")) {
|
|
317
|
+
const segments = trimmed.split("/");
|
|
318
|
+
if (segments.length >= 3) {
|
|
319
|
+
const existingFaceId = segments[1];
|
|
320
|
+
const remainder = segments.slice(2).join("/");
|
|
321
|
+
if (existingFaceId === faceId) {
|
|
322
|
+
return trimmed;
|
|
323
|
+
}
|
|
324
|
+
trimmed = remainder || "";
|
|
325
|
+
} else {
|
|
326
|
+
trimmed = segments.slice(1).join("/");
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const suffix = trimmed ? `/${trimmed}` : "";
|
|
330
|
+
return `rig/${faceId}${suffix}`;
|
|
331
|
+
}
|
|
332
|
+
function normalizePoseWeightPathSegment(value) {
|
|
333
|
+
const trimmed = value?.trim() ?? "";
|
|
334
|
+
if (!trimmed) {
|
|
335
|
+
return "pose";
|
|
336
|
+
}
|
|
337
|
+
const normalized = trimmed.replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "");
|
|
338
|
+
return normalized || "pose";
|
|
339
|
+
}
|
|
340
|
+
function buildPoseWeightInputPathSegment(poseId) {
|
|
341
|
+
return normalizePoseWeightPathSegment(poseId);
|
|
342
|
+
}
|
|
343
|
+
function buildPoseWeightRelativePath(poseId) {
|
|
344
|
+
return `${POSE_WEIGHT_INPUT_PATH_PREFIX}${buildPoseWeightInputPathSegment(
|
|
345
|
+
poseId
|
|
346
|
+
)}.weight`;
|
|
347
|
+
}
|
|
348
|
+
function buildPoseWeightPathMap(poses, faceId) {
|
|
349
|
+
const faceSegment = faceId?.trim() || "face";
|
|
350
|
+
const map = /* @__PURE__ */ new Map();
|
|
351
|
+
poses.forEach((pose) => {
|
|
352
|
+
map.set(
|
|
353
|
+
pose.id,
|
|
354
|
+
buildRigInputPath(faceSegment, buildPoseWeightRelativePath(pose.id))
|
|
355
|
+
);
|
|
356
|
+
});
|
|
357
|
+
return map;
|
|
358
|
+
}
|
|
359
|
+
function normalizePoseSemanticKey(value) {
|
|
360
|
+
const trimmed = value?.trim() ?? "";
|
|
361
|
+
if (!trimmed) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
const normalized = trimmed.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").replace(/_+/g, "_");
|
|
365
|
+
if (!normalized) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
return POSE_KEY_ALIASES[normalized] ?? normalized;
|
|
369
|
+
}
|
|
370
|
+
function derivePoseSemanticKeyFromId(poseId) {
|
|
371
|
+
const trimmed = poseId?.trim() ?? "";
|
|
372
|
+
if (!trimmed) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
const stripped = trimmed.replace(/^pose_d_/i, "").replace(/^pose_/i, "").replace(/^d_/i, "").replace(/_d$/i, "");
|
|
376
|
+
return normalizePoseSemanticKey(stripped);
|
|
377
|
+
}
|
|
378
|
+
function getPoseSemanticKey(pose) {
|
|
379
|
+
return normalizePoseSemanticKey(pose.name) ?? derivePoseSemanticKeyFromId(pose.id) ?? null;
|
|
380
|
+
}
|
|
381
|
+
function normalizePoseGroupPath(value) {
|
|
382
|
+
const trimmed = value?.trim() ?? "";
|
|
383
|
+
if (!trimmed) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
return trimmed.replace(/^\/+|\/+$/g, "").replace(/\/+/g, "/");
|
|
387
|
+
}
|
|
388
|
+
function sanitizePoseGroupId(value, fallback) {
|
|
389
|
+
const normalized = (value ?? "").trim().replace(/[^a-zA-Z0-9_/-]+/g, "_").replace(/^\/+|\/+$/g, "").replace(/\/+/g, "_");
|
|
390
|
+
if (!normalized) {
|
|
391
|
+
return fallback.replace(/\//g, "_");
|
|
392
|
+
}
|
|
393
|
+
return normalized;
|
|
394
|
+
}
|
|
395
|
+
function buildPoseGroupLookup(groups) {
|
|
396
|
+
const byId = /* @__PURE__ */ new Map();
|
|
397
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
398
|
+
const orderById = /* @__PURE__ */ new Map();
|
|
399
|
+
(groups ?? []).forEach((group, index) => {
|
|
400
|
+
const path = normalizePoseGroupPath(group.path ?? group.name ?? group.id);
|
|
401
|
+
if (!path) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const id = sanitizePoseGroupId(group.id, path);
|
|
405
|
+
const humanizedName = typeof group.name === "string" && group.name.trim().length > 0 ? group.name.trim() : path.split("/").filter(Boolean).pop()?.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ") ?? path;
|
|
406
|
+
const normalized = {
|
|
407
|
+
...group,
|
|
408
|
+
id,
|
|
409
|
+
path,
|
|
410
|
+
name: humanizedName
|
|
411
|
+
};
|
|
412
|
+
byId.set(id, normalized);
|
|
413
|
+
if (!orderById.has(id)) {
|
|
414
|
+
orderById.set(id, index);
|
|
415
|
+
}
|
|
416
|
+
if (!byPath.has(path)) {
|
|
417
|
+
byPath.set(path, normalized);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return { byId, byPath, orderById };
|
|
421
|
+
}
|
|
422
|
+
function orderPoseMembershipIds(groupIds, groups) {
|
|
423
|
+
const { orderById } = buildPoseGroupLookup(groups);
|
|
424
|
+
const unique = Array.from(
|
|
425
|
+
new Set(
|
|
426
|
+
Array.from(groupIds).map((groupId) => groupId.trim()).filter((groupId) => groupId.length > 0)
|
|
427
|
+
)
|
|
428
|
+
);
|
|
429
|
+
unique.sort((left, right) => {
|
|
430
|
+
const leftIndex = orderById.get(left);
|
|
431
|
+
const rightIndex = orderById.get(right);
|
|
432
|
+
if (leftIndex !== void 0 && rightIndex !== void 0) {
|
|
433
|
+
return leftIndex - rightIndex;
|
|
434
|
+
}
|
|
435
|
+
if (leftIndex !== void 0) {
|
|
436
|
+
return -1;
|
|
437
|
+
}
|
|
438
|
+
if (rightIndex !== void 0) {
|
|
439
|
+
return 1;
|
|
440
|
+
}
|
|
441
|
+
const leftPath = normalizePoseGroupPath(left) ?? left;
|
|
442
|
+
const rightPath = normalizePoseGroupPath(right) ?? right;
|
|
443
|
+
const byPath = leftPath.localeCompare(rightPath);
|
|
444
|
+
if (byPath !== 0) {
|
|
445
|
+
return byPath;
|
|
446
|
+
}
|
|
447
|
+
return left.localeCompare(right);
|
|
448
|
+
});
|
|
449
|
+
return unique;
|
|
450
|
+
}
|
|
451
|
+
function valueHasNeedle(value, needles) {
|
|
452
|
+
const normalized = normalizePoseSemanticKey(value);
|
|
453
|
+
if (!normalized) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
return needles.some((needle) => normalized.includes(needle));
|
|
457
|
+
}
|
|
458
|
+
function resolvePoseMembership(pose, groups) {
|
|
459
|
+
const { byId, byPath } = buildPoseGroupLookup(groups);
|
|
460
|
+
const resolvedGroupIds = [];
|
|
461
|
+
const pathById = /* @__PURE__ */ new Map();
|
|
462
|
+
const addMembership = (groupId, path) => {
|
|
463
|
+
if (!groupId) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
if (!resolvedGroupIds.includes(groupId)) {
|
|
467
|
+
resolvedGroupIds.push(groupId);
|
|
468
|
+
}
|
|
469
|
+
if (!path) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const existingPath = pathById.get(groupId);
|
|
473
|
+
const normalizedGroupIdPath = normalizePoseGroupPath(groupId);
|
|
474
|
+
const normalizedExistingPath = normalizePoseGroupPath(existingPath) ?? existingPath ?? null;
|
|
475
|
+
const normalizedIncomingPath = normalizePoseGroupPath(path) ?? path ?? null;
|
|
476
|
+
const shouldPromotePath = normalizedExistingPath === null || normalizedGroupIdPath !== null && normalizedExistingPath === normalizedGroupIdPath && normalizedIncomingPath !== normalizedGroupIdPath;
|
|
477
|
+
if (shouldPromotePath) {
|
|
478
|
+
pathById.set(groupId, path);
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
const addByPath = (rawPath) => {
|
|
482
|
+
const normalizedPath = normalizePoseGroupPath(rawPath);
|
|
483
|
+
if (!normalizedPath) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const existing = byPath.get(normalizedPath);
|
|
487
|
+
if (existing) {
|
|
488
|
+
addMembership(existing.id, existing.path);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
addMembership(sanitizePoseGroupId(null, normalizedPath), normalizedPath);
|
|
492
|
+
};
|
|
493
|
+
const addById = (rawId) => {
|
|
494
|
+
const trimmed = rawId?.trim() ?? "";
|
|
495
|
+
if (!trimmed) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const normalizedPath = normalizePoseGroupPath(trimmed);
|
|
499
|
+
const normalizedId = sanitizePoseGroupId(trimmed, trimmed);
|
|
500
|
+
const matchedById = byId.get(trimmed) ?? byId.get(normalizedId);
|
|
501
|
+
if (matchedById) {
|
|
502
|
+
addMembership(matchedById.id, matchedById.path);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
if (normalizedPath) {
|
|
506
|
+
const matchedByPath = byPath.get(normalizedPath);
|
|
507
|
+
if (matchedByPath) {
|
|
508
|
+
addMembership(matchedByPath.id, matchedByPath.path);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
addMembership(
|
|
513
|
+
normalizedId,
|
|
514
|
+
normalizedPath && normalizedPath.length > 0 ? normalizedPath : null
|
|
515
|
+
);
|
|
516
|
+
};
|
|
517
|
+
pose.groupIds?.forEach((groupId) => addById(groupId));
|
|
518
|
+
addById(pose.groupId);
|
|
519
|
+
addByPath(pose.group);
|
|
520
|
+
const orderedGroupIds = orderPoseMembershipIds(resolvedGroupIds, groups);
|
|
521
|
+
const primaryGroupId = orderedGroupIds[0] ?? null;
|
|
522
|
+
const primaryGroupPath = primaryGroupId ? byId.get(primaryGroupId)?.path ?? pathById.get(primaryGroupId) ?? null : null;
|
|
523
|
+
const groupPathsById = {};
|
|
524
|
+
orderedGroupIds.forEach((groupId) => {
|
|
525
|
+
const path = byId.get(groupId)?.path ?? pathById.get(groupId) ?? null;
|
|
526
|
+
if (path) {
|
|
527
|
+
groupPathsById[groupId] = path;
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
return {
|
|
531
|
+
groupIds: orderedGroupIds,
|
|
532
|
+
primaryGroupId,
|
|
533
|
+
primaryGroupPath,
|
|
534
|
+
groupPathsById
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function poseMatchesGroupKind(pose, groups, needles) {
|
|
538
|
+
const membership = resolvePoseMembership(pose, groups);
|
|
539
|
+
if (valueHasNeedle(pose.group, needles) || valueHasNeedle(pose.groupId, needles)) {
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
if (pose.groupIds?.some((groupId) => valueHasNeedle(groupId, needles))) {
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
return membership.groupIds.some((groupId) => {
|
|
546
|
+
const path = membership.groupPathsById[groupId] ?? null;
|
|
547
|
+
const group = (groups ?? []).find((entry) => entry.id === groupId) ?? null;
|
|
548
|
+
return valueHasNeedle(groupId, needles) || valueHasNeedle(path, needles) || valueHasNeedle(group?.name, needles);
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
function resolvePoseSemantics(pose, groups) {
|
|
552
|
+
const membership = resolvePoseMembership(pose, groups);
|
|
553
|
+
const key = getPoseSemanticKey(pose);
|
|
554
|
+
const looksLikeVisemeGroup = poseMatchesGroupKind(
|
|
555
|
+
pose,
|
|
556
|
+
groups,
|
|
557
|
+
VISEME_GROUP_NEEDLES
|
|
558
|
+
);
|
|
559
|
+
const looksLikeEmotionGroup = poseMatchesGroupKind(
|
|
560
|
+
pose,
|
|
561
|
+
groups,
|
|
562
|
+
EMOTION_GROUP_NEEDLES
|
|
563
|
+
);
|
|
564
|
+
let kind = "other";
|
|
565
|
+
if (looksLikeVisemeGroup || key && VISEME_POSE_KEY_SET.has(key)) {
|
|
566
|
+
kind = "viseme";
|
|
567
|
+
} else if (looksLikeEmotionGroup || key && EMOTION_POSE_KEY_SET.has(key)) {
|
|
568
|
+
kind = "emotion";
|
|
569
|
+
}
|
|
570
|
+
return { key, kind, membership };
|
|
571
|
+
}
|
|
572
|
+
function filterPosesBySemanticKind(poses, groups, kind) {
|
|
573
|
+
return poses.filter(
|
|
574
|
+
(pose) => resolvePoseSemantics(pose, groups).kind === kind
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
function buildSemanticPoseWeightPathMap(poses, groups, faceId, kind) {
|
|
578
|
+
const map = /* @__PURE__ */ new Map();
|
|
579
|
+
const pathMap = buildPoseWeightPathMap(poses, faceId);
|
|
580
|
+
poses.forEach((pose) => {
|
|
581
|
+
const semantics = resolvePoseSemantics(pose, groups);
|
|
582
|
+
const path = pathMap.get(pose.id);
|
|
583
|
+
if (semantics.kind !== kind || !semantics.key || !path || map.has(semantics.key)) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
map.set(semantics.key, path);
|
|
98
587
|
});
|
|
99
588
|
return map;
|
|
100
589
|
}
|
|
101
590
|
|
|
591
|
+
// src/utils/poseRuntime.ts
|
|
592
|
+
function shouldUseLegacyPoseWeightFallback(hasPoseGraph) {
|
|
593
|
+
return !hasPoseGraph;
|
|
594
|
+
}
|
|
595
|
+
function resolvePoseControlInputPath({
|
|
596
|
+
inputId,
|
|
597
|
+
basePath,
|
|
598
|
+
rigInputPathMap,
|
|
599
|
+
hasNativePoseControlInput
|
|
600
|
+
}) {
|
|
601
|
+
if (!inputId.trim()) {
|
|
602
|
+
return void 0;
|
|
603
|
+
}
|
|
604
|
+
return rigInputPathMap[inputId] ?? rigInputPathMap[`pose_control_${inputId}`] ?? rigInputPathMap[`direct_${inputId}`] ?? (hasNativePoseControlInput ? basePath : void 0);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/utils/clipPlayback.ts
|
|
608
|
+
var EPSILON = 1e-6;
|
|
609
|
+
function toFiniteNumber(value, fallback) {
|
|
610
|
+
const parsed = Number(value);
|
|
611
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
612
|
+
}
|
|
613
|
+
function normaliseInterpolation(interpolation) {
|
|
614
|
+
const mode = typeof interpolation === "string" ? interpolation.trim().toLowerCase() : "linear";
|
|
615
|
+
if (mode === "step") {
|
|
616
|
+
return "step";
|
|
617
|
+
}
|
|
618
|
+
if (mode === "cubic" || mode === "cubicspline") {
|
|
619
|
+
return "cubic";
|
|
620
|
+
}
|
|
621
|
+
return "linear";
|
|
622
|
+
}
|
|
623
|
+
function asNumericKeyframe(keyframe) {
|
|
624
|
+
if (!keyframe || typeof keyframe !== "object") {
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
const time = Number(keyframe.time);
|
|
628
|
+
const value = Number(keyframe.value);
|
|
629
|
+
if (!Number.isFinite(time) || !Number.isFinite(value)) {
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
const inTangentRaw = keyframe.inTangent;
|
|
633
|
+
const outTangentRaw = keyframe.outTangent;
|
|
634
|
+
const inTangent = inTangentRaw == null || Number.isFinite(Number(inTangentRaw)) ? inTangentRaw : void 0;
|
|
635
|
+
const outTangent = outTangentRaw == null || Number.isFinite(Number(outTangentRaw)) ? outTangentRaw : void 0;
|
|
636
|
+
return {
|
|
637
|
+
time,
|
|
638
|
+
value,
|
|
639
|
+
inTangent,
|
|
640
|
+
outTangent
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
function getNumericKeyframes(track) {
|
|
644
|
+
const keyframes = Array.isArray(track.keyframes) ? track.keyframes : [];
|
|
645
|
+
const numeric = keyframes.map((keyframe) => asNumericKeyframe(keyframe)).filter((keyframe) => Boolean(keyframe));
|
|
646
|
+
if (numeric.length <= 1) {
|
|
647
|
+
return numeric;
|
|
648
|
+
}
|
|
649
|
+
return [...numeric].sort((a, b) => a.time - b.time);
|
|
650
|
+
}
|
|
651
|
+
function resolveTangent(value, fallback) {
|
|
652
|
+
const parsed = Number(value);
|
|
653
|
+
if (Number.isFinite(parsed)) {
|
|
654
|
+
return parsed;
|
|
655
|
+
}
|
|
656
|
+
return fallback;
|
|
657
|
+
}
|
|
658
|
+
function sampleHermite(startValue, endValue, outTangent, inTangent, factor, duration) {
|
|
659
|
+
const t = factor;
|
|
660
|
+
const t2 = t * t;
|
|
661
|
+
const t3 = t2 * t;
|
|
662
|
+
const h00 = 2 * t3 - 3 * t2 + 1;
|
|
663
|
+
const h10 = t3 - 2 * t2 + t;
|
|
664
|
+
const h01 = -2 * t3 + 3 * t2;
|
|
665
|
+
const h11 = t3 - t2;
|
|
666
|
+
return h00 * startValue + h10 * outTangent * duration + h01 * endValue + h11 * inTangent * duration;
|
|
667
|
+
}
|
|
668
|
+
function resolveClipDurationSeconds(clip, fallbackDurationSeconds = 0) {
|
|
669
|
+
const fallback = Math.max(0, toFiniteNumber(fallbackDurationSeconds, 0));
|
|
670
|
+
if (!clip || typeof clip !== "object") {
|
|
671
|
+
return fallback;
|
|
672
|
+
}
|
|
673
|
+
const clipDuration = Number(clip.duration);
|
|
674
|
+
if (Number.isFinite(clipDuration) && clipDuration > 0) {
|
|
675
|
+
return clipDuration;
|
|
676
|
+
}
|
|
677
|
+
const tracks = Array.isArray(clip.tracks) ? clip.tracks : [];
|
|
678
|
+
let maxTime = 0;
|
|
679
|
+
tracks.forEach((track) => {
|
|
680
|
+
const keyframes = getNumericKeyframes(track);
|
|
681
|
+
const last = keyframes[keyframes.length - 1];
|
|
682
|
+
if (last && last.time > maxTime) {
|
|
683
|
+
maxTime = last.time;
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
return maxTime > 0 ? maxTime : fallback;
|
|
687
|
+
}
|
|
688
|
+
function clampAnimationTime(time, duration) {
|
|
689
|
+
if (!Number.isFinite(duration) || duration <= 0) {
|
|
690
|
+
return 0;
|
|
691
|
+
}
|
|
692
|
+
if (!Number.isFinite(time)) {
|
|
693
|
+
return 0;
|
|
694
|
+
}
|
|
695
|
+
if (time <= 0) {
|
|
696
|
+
return 0;
|
|
697
|
+
}
|
|
698
|
+
if (time >= duration) {
|
|
699
|
+
return duration;
|
|
700
|
+
}
|
|
701
|
+
return time;
|
|
702
|
+
}
|
|
703
|
+
function advanceClipTime(state, dt) {
|
|
704
|
+
const duration = Math.max(0, toFiniteNumber(state.duration, 0));
|
|
705
|
+
const speed = Number.isFinite(state.speed) && state.speed > 0 ? state.speed : 1;
|
|
706
|
+
const currentTime = clampAnimationTime(state.time, duration);
|
|
707
|
+
const delta = Number.isFinite(dt) && dt > 0 ? Math.max(0, dt) * speed : 0;
|
|
708
|
+
if (!state.playing || delta <= 0) {
|
|
709
|
+
return { time: currentTime, completed: false };
|
|
710
|
+
}
|
|
711
|
+
if (duration <= 0) {
|
|
712
|
+
return { time: 0, completed: true };
|
|
713
|
+
}
|
|
714
|
+
const nextTime = currentTime + delta;
|
|
715
|
+
if (state.loop) {
|
|
716
|
+
if (nextTime < duration) {
|
|
717
|
+
return { time: nextTime, completed: false };
|
|
718
|
+
}
|
|
719
|
+
const wrapped = (nextTime % duration + duration) % duration;
|
|
720
|
+
return { time: wrapped, completed: false };
|
|
721
|
+
}
|
|
722
|
+
if (nextTime >= duration - EPSILON) {
|
|
723
|
+
return { time: duration, completed: true };
|
|
724
|
+
}
|
|
725
|
+
return { time: nextTime, completed: false };
|
|
726
|
+
}
|
|
727
|
+
function sampleTrackAtTime(track, timeSeconds) {
|
|
728
|
+
const keyframes = getNumericKeyframes(track);
|
|
729
|
+
if (keyframes.length === 0) {
|
|
730
|
+
return 0;
|
|
731
|
+
}
|
|
732
|
+
if (keyframes.length === 1) {
|
|
733
|
+
return keyframes[0].value;
|
|
734
|
+
}
|
|
735
|
+
const mode = normaliseInterpolation(track.interpolation);
|
|
736
|
+
const time = Number.isFinite(timeSeconds) ? timeSeconds : 0;
|
|
737
|
+
const first = keyframes[0];
|
|
738
|
+
if (time <= first.time + EPSILON) {
|
|
739
|
+
return first.value;
|
|
740
|
+
}
|
|
741
|
+
const last = keyframes[keyframes.length - 1];
|
|
742
|
+
if (time >= last.time - EPSILON) {
|
|
743
|
+
return last.value;
|
|
744
|
+
}
|
|
745
|
+
for (let i = 0; i < keyframes.length - 1; i += 1) {
|
|
746
|
+
const current = keyframes[i];
|
|
747
|
+
const next = keyframes[i + 1];
|
|
748
|
+
const start = current.time;
|
|
749
|
+
const end = next.time;
|
|
750
|
+
const duration = end - start;
|
|
751
|
+
if (duration <= EPSILON) {
|
|
752
|
+
if (time <= end + EPSILON) {
|
|
753
|
+
return next.value;
|
|
754
|
+
}
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
if (Math.abs(time - end) <= EPSILON) {
|
|
758
|
+
return next.value;
|
|
759
|
+
}
|
|
760
|
+
if (time < end) {
|
|
761
|
+
const factor = (time - start) / duration;
|
|
762
|
+
if (mode === "step") {
|
|
763
|
+
return current.value;
|
|
764
|
+
}
|
|
765
|
+
if (mode === "cubic") {
|
|
766
|
+
const slope = (next.value - current.value) / duration;
|
|
767
|
+
const outTangent = resolveTangent(current.outTangent, slope);
|
|
768
|
+
const inTangent = resolveTangent(next.inTangent, slope);
|
|
769
|
+
return sampleHermite(
|
|
770
|
+
current.value,
|
|
771
|
+
next.value,
|
|
772
|
+
outTangent,
|
|
773
|
+
inTangent,
|
|
774
|
+
factor,
|
|
775
|
+
duration
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
return current.value + (next.value - current.value) * factor;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
return last.value;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/utils/animationBridge.ts
|
|
785
|
+
function resolveAnimationBridgeOutputPaths(channel, faceId, rigInputMap) {
|
|
786
|
+
const normalizedChannel = channel.trim().replace(/^\/+/, "");
|
|
787
|
+
if (!normalizedChannel) {
|
|
788
|
+
return [];
|
|
789
|
+
}
|
|
790
|
+
const outputPaths = /* @__PURE__ */ new Set([normalizedChannel]);
|
|
791
|
+
if (normalizedChannel.startsWith("animation/")) {
|
|
792
|
+
return Array.from(outputPaths).sort(
|
|
793
|
+
(left, right) => left.localeCompare(right)
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
if (rigInputMap && Object.keys(rigInputMap).length > 0) {
|
|
797
|
+
const candidateKeys = /* @__PURE__ */ new Set([normalizedChannel]);
|
|
798
|
+
const rigChannelMatch2 = /^rig\/[^/]+\/(.+)$/.exec(normalizedChannel);
|
|
799
|
+
if (rigChannelMatch2?.[1]) {
|
|
800
|
+
candidateKeys.add(rigChannelMatch2[1]);
|
|
801
|
+
}
|
|
802
|
+
candidateKeys.forEach((key) => {
|
|
803
|
+
const mapped = rigInputMap[key];
|
|
804
|
+
const normalized = mapped?.trim().replace(/^\/+/, "");
|
|
805
|
+
if (normalized) {
|
|
806
|
+
outputPaths.add(normalized);
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
const suffix = normalizedChannel.includes("/") ? normalizedChannel : `/${normalizedChannel}`;
|
|
810
|
+
Object.values(rigInputMap).forEach((mappedPath) => {
|
|
811
|
+
const normalized = mappedPath?.trim().replace(/^\/+/, "");
|
|
812
|
+
if (!normalized) {
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (normalized === normalizedChannel || normalized.endsWith(suffix)) {
|
|
816
|
+
outputPaths.add(normalized);
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
if (!faceId) {
|
|
821
|
+
return Array.from(outputPaths).sort(
|
|
822
|
+
(left, right) => left.localeCompare(right)
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
const rigChannelMatch = /^rig\/[^/]+\/(.+)$/.exec(normalizedChannel);
|
|
826
|
+
if (rigChannelMatch?.[1]) {
|
|
827
|
+
outputPaths.add(`rig/${faceId}/${rigChannelMatch[1]}`);
|
|
828
|
+
} else if (!normalizedChannel.startsWith("rig/")) {
|
|
829
|
+
outputPaths.add(`rig/${faceId}/${normalizedChannel}`);
|
|
830
|
+
}
|
|
831
|
+
return Array.from(outputPaths).sort(
|
|
832
|
+
(left, right) => left.localeCompare(right)
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
function collectAnimationClipOutputPaths(clip, faceId, rigInputMap) {
|
|
836
|
+
const outputPaths = /* @__PURE__ */ new Set();
|
|
837
|
+
const tracks = Array.isArray(clip.tracks) ? clip.tracks : [];
|
|
838
|
+
tracks.forEach((track) => {
|
|
839
|
+
const channel = typeof track.channel === "string" ? track.channel.trim() : "";
|
|
840
|
+
if (!channel) {
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
resolveAnimationBridgeOutputPaths(channel, faceId, rigInputMap).forEach(
|
|
844
|
+
(path) => {
|
|
845
|
+
outputPaths.add(path);
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
});
|
|
849
|
+
return Array.from(outputPaths).sort(
|
|
850
|
+
(left, right) => left.localeCompare(right)
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
function sampleAnimationClipOutputValues(clip, timeSeconds, weight = 1, faceId, rigInputMap) {
|
|
854
|
+
const appliedWeight = Number.isFinite(weight) && weight >= 0 ? Number(weight) : 1;
|
|
855
|
+
const outputValues = /* @__PURE__ */ new Map();
|
|
856
|
+
const tracks = Array.isArray(clip.tracks) ? clip.tracks : [];
|
|
857
|
+
tracks.forEach((track) => {
|
|
858
|
+
const channel = typeof track.channel === "string" ? track.channel.trim() : "";
|
|
859
|
+
if (!channel) {
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const sampledValue = sampleTrackAtTime(
|
|
863
|
+
track,
|
|
864
|
+
timeSeconds
|
|
865
|
+
);
|
|
866
|
+
const weightedValue = sampledValue * appliedWeight;
|
|
867
|
+
resolveAnimationBridgeOutputPaths(channel, faceId, rigInputMap).forEach(
|
|
868
|
+
(path) => {
|
|
869
|
+
outputValues.set(path, (outputValues.get(path) ?? 0) + weightedValue);
|
|
870
|
+
}
|
|
871
|
+
);
|
|
872
|
+
});
|
|
873
|
+
return outputValues;
|
|
874
|
+
}
|
|
875
|
+
function diffAnimationAggregateValues(previousAggregate, nextAggregate, epsilon = 1e-6) {
|
|
876
|
+
const operations = [];
|
|
877
|
+
const changedPaths = /* @__PURE__ */ new Set();
|
|
878
|
+
previousAggregate.forEach((previousValue, path) => {
|
|
879
|
+
const nextValue = nextAggregate.get(path);
|
|
880
|
+
if (nextValue === void 0 || Math.abs(nextValue - previousValue) > epsilon) {
|
|
881
|
+
changedPaths.add(path);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
nextAggregate.forEach((nextValue, path) => {
|
|
885
|
+
const previousValue = previousAggregate.get(path);
|
|
886
|
+
if (previousValue === void 0 || Math.abs(nextValue - previousValue) > epsilon) {
|
|
887
|
+
changedPaths.add(path);
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
changedPaths.forEach((path) => {
|
|
891
|
+
const nextValue = nextAggregate.get(path);
|
|
892
|
+
if (nextValue === void 0) {
|
|
893
|
+
operations.push({ kind: "clear", path });
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
operations.push({ kind: "set", path, value: nextValue });
|
|
897
|
+
});
|
|
898
|
+
return operations;
|
|
899
|
+
}
|
|
900
|
+
|
|
102
901
|
// src/utils/valueConversion.ts
|
|
103
902
|
var import_value_json = require("@vizij/value-json");
|
|
104
903
|
function numericArrayToRaw(arr) {
|
|
@@ -224,6 +1023,19 @@ var DEFAULT_MERGE = {
|
|
|
224
1023
|
intermediate: "add"
|
|
225
1024
|
};
|
|
226
1025
|
var DEFAULT_DURATION = 0.35;
|
|
1026
|
+
var POSE_CONTROL_BRIDGE_EPSILON = 1e-6;
|
|
1027
|
+
var DEV_MODE = (() => {
|
|
1028
|
+
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
1029
|
+
return typeof nodeEnv === "string" && nodeEnv === "development";
|
|
1030
|
+
})();
|
|
1031
|
+
function isRuntimeDebugEnabled() {
|
|
1032
|
+
if (DEV_MODE) {
|
|
1033
|
+
return true;
|
|
1034
|
+
}
|
|
1035
|
+
return Boolean(
|
|
1036
|
+
globalThis.__VIZIJ_RUNTIME_DEBUG__
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
227
1039
|
var EASINGS = {
|
|
228
1040
|
linear: (t) => t,
|
|
229
1041
|
easeIn: (t) => t * t,
|
|
@@ -240,12 +1052,19 @@ function resolveEasing(easing) {
|
|
|
240
1052
|
return EASINGS.linear;
|
|
241
1053
|
}
|
|
242
1054
|
function findRootId(world) {
|
|
1055
|
+
let fallback = null;
|
|
243
1056
|
for (const entry of Object.values(world)) {
|
|
244
|
-
if (entry
|
|
1057
|
+
if (!entry || typeof entry !== "object" || entry.type !== "group") {
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (entry.rootBounds && entry.id) {
|
|
245
1061
|
return entry.id;
|
|
246
1062
|
}
|
|
1063
|
+
if (!fallback && entry.id) {
|
|
1064
|
+
fallback = entry.id;
|
|
1065
|
+
}
|
|
247
1066
|
}
|
|
248
|
-
return
|
|
1067
|
+
return fallback;
|
|
249
1068
|
}
|
|
250
1069
|
function normalisePath(path) {
|
|
251
1070
|
if (!path) {
|
|
@@ -415,6 +1234,30 @@ function namespaceGraphSpec(spec, namespace) {
|
|
|
415
1234
|
nodes: nextNodes
|
|
416
1235
|
};
|
|
417
1236
|
}
|
|
1237
|
+
function stripNulls(value) {
|
|
1238
|
+
if (value === null) {
|
|
1239
|
+
return void 0;
|
|
1240
|
+
}
|
|
1241
|
+
if (Array.isArray(value)) {
|
|
1242
|
+
const next2 = value.map((entry) => stripNulls(entry)).filter((entry) => entry !== void 0 && entry !== null);
|
|
1243
|
+
return next2;
|
|
1244
|
+
}
|
|
1245
|
+
if (typeof value !== "object" || value === void 0) {
|
|
1246
|
+
return value;
|
|
1247
|
+
}
|
|
1248
|
+
const next = {};
|
|
1249
|
+
Object.entries(value).forEach(([key, entry]) => {
|
|
1250
|
+
if (entry === null) {
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
const cleaned = stripNulls(entry);
|
|
1254
|
+
if (cleaned === void 0) {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
next[key] = cleaned;
|
|
1258
|
+
});
|
|
1259
|
+
return next;
|
|
1260
|
+
}
|
|
418
1261
|
var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
419
1262
|
function pickBundleGraph(bundle, preferredKinds) {
|
|
420
1263
|
if (!bundle?.graphs || bundle.graphs.length === 0) {
|
|
@@ -585,26 +1428,49 @@ function convertExtractedAnimations(clips) {
|
|
|
585
1428
|
}
|
|
586
1429
|
const parsedValueSize = track.valueSize != null ? Number(track.valueSize) : NaN;
|
|
587
1430
|
const valueSize = Number.isFinite(parsedValueSize) && parsedValueSize > 0 ? parsedValueSize : 1;
|
|
588
|
-
|
|
1431
|
+
const interpolationRaw = typeof track.interpolation === "string" ? track.interpolation.trim().toLowerCase() : "linear";
|
|
1432
|
+
const interpolation = interpolationRaw === "step" ? "step" : interpolationRaw === "cubic" || interpolationRaw === "cubicspline" ? "cubic" : "linear";
|
|
1433
|
+
const isCubic = interpolation === "cubic";
|
|
1434
|
+
const hasTripletTangents = isCubic && values.length === times.length * valueSize * 3;
|
|
1435
|
+
const hasFlatValues = values.length === times.length * valueSize;
|
|
1436
|
+
if (!hasTripletTangents && !hasFlatValues) {
|
|
589
1437
|
return;
|
|
590
1438
|
}
|
|
591
1439
|
const rawIndex = track.componentIndex != null ? Number(track.componentIndex) : 0;
|
|
592
1440
|
const componentIndex = Number.isInteger(rawIndex) && rawIndex >= 0 ? Math.min(rawIndex, valueSize - 1) : 0;
|
|
593
1441
|
const keyframes = [];
|
|
594
1442
|
times.forEach((time, index) => {
|
|
595
|
-
const
|
|
596
|
-
const
|
|
1443
|
+
const flatBase = index * valueSize + componentIndex;
|
|
1444
|
+
const valueBase = hasTripletTangents ? index * valueSize * 3 + valueSize + componentIndex : flatBase;
|
|
1445
|
+
const value = values[valueBase];
|
|
597
1446
|
if (!Number.isFinite(value)) {
|
|
598
1447
|
return;
|
|
599
1448
|
}
|
|
600
|
-
|
|
1449
|
+
const keyframe = {
|
|
1450
|
+
time,
|
|
1451
|
+
value
|
|
1452
|
+
};
|
|
1453
|
+
if (hasTripletTangents) {
|
|
1454
|
+
const inBase = index * valueSize * 3 + componentIndex;
|
|
1455
|
+
const outBase = index * valueSize * 3 + valueSize * 2 + componentIndex;
|
|
1456
|
+
const inTangent = values[inBase];
|
|
1457
|
+
const outTangent = values[outBase];
|
|
1458
|
+
if (Number.isFinite(inTangent)) {
|
|
1459
|
+
keyframe.inTangent = inTangent;
|
|
1460
|
+
}
|
|
1461
|
+
if (Number.isFinite(outTangent)) {
|
|
1462
|
+
keyframe.outTangent = outTangent;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
keyframes.push(keyframe);
|
|
601
1466
|
});
|
|
602
1467
|
if (keyframes.length === 0) {
|
|
603
1468
|
return;
|
|
604
1469
|
}
|
|
605
1470
|
convertedTracks.push({
|
|
606
1471
|
channel: channelId,
|
|
607
|
-
keyframes
|
|
1472
|
+
keyframes,
|
|
1473
|
+
interpolation
|
|
608
1474
|
});
|
|
609
1475
|
});
|
|
610
1476
|
if (convertedTracks.length === 0) {
|
|
@@ -671,29 +1537,91 @@ function mergeAnimationLists(explicit, fromBundle) {
|
|
|
671
1537
|
}
|
|
672
1538
|
return changed ? merged : explicit;
|
|
673
1539
|
}
|
|
674
|
-
function
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1540
|
+
function extractProgramResetValues(value) {
|
|
1541
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1542
|
+
return void 0;
|
|
1543
|
+
}
|
|
1544
|
+
const entries = Object.entries(value).filter(
|
|
1545
|
+
([, rawValue]) => Number.isFinite(Number(rawValue))
|
|
678
1546
|
);
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1547
|
+
if (entries.length === 0) {
|
|
1548
|
+
return void 0;
|
|
1549
|
+
}
|
|
1550
|
+
return Object.fromEntries(
|
|
1551
|
+
entries.map(([path, rawValue]) => [path, Number(rawValue)])
|
|
684
1552
|
);
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
1553
|
+
}
|
|
1554
|
+
function convertBundlePrograms(entries) {
|
|
1555
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
1556
|
+
return [];
|
|
1557
|
+
}
|
|
1558
|
+
return entries.filter((entry) => normaliseBundleKind(entry?.kind) === "motiongraph").map((entry) => {
|
|
1559
|
+
const graph = convertBundleGraph(entry);
|
|
1560
|
+
if (!graph) {
|
|
1561
|
+
return null;
|
|
694
1562
|
}
|
|
695
|
-
|
|
696
|
-
|
|
1563
|
+
const metadata = entry.metadata && typeof entry.metadata === "object" && !Array.isArray(entry.metadata) ? entry.metadata : void 0;
|
|
1564
|
+
return {
|
|
1565
|
+
id: entry.id,
|
|
1566
|
+
label: typeof entry.label === "string" ? entry.label : void 0,
|
|
1567
|
+
graph,
|
|
1568
|
+
metadata,
|
|
1569
|
+
resetValues: extractProgramResetValues(metadata?.resetValues)
|
|
1570
|
+
};
|
|
1571
|
+
}).filter(Boolean);
|
|
1572
|
+
}
|
|
1573
|
+
function mergeProgramLists(explicit, fromBundle) {
|
|
1574
|
+
if (!explicit?.length && fromBundle.length === 0) {
|
|
1575
|
+
return void 0;
|
|
1576
|
+
}
|
|
1577
|
+
if (!explicit?.length) {
|
|
1578
|
+
return fromBundle.length > 0 ? fromBundle : void 0;
|
|
1579
|
+
}
|
|
1580
|
+
if (fromBundle.length === 0) {
|
|
1581
|
+
return explicit;
|
|
1582
|
+
}
|
|
1583
|
+
const seen = new Set(explicit.map((program) => program.id));
|
|
1584
|
+
let changed = false;
|
|
1585
|
+
const merged = [...explicit];
|
|
1586
|
+
for (const program of fromBundle) {
|
|
1587
|
+
if (!program.id || seen.has(program.id)) {
|
|
1588
|
+
continue;
|
|
1589
|
+
}
|
|
1590
|
+
merged.push(program);
|
|
1591
|
+
seen.add(program.id);
|
|
1592
|
+
changed = true;
|
|
1593
|
+
}
|
|
1594
|
+
return changed ? merged : explicit;
|
|
1595
|
+
}
|
|
1596
|
+
function mergeAssetBundle(base, extracted, extractedAnimations) {
|
|
1597
|
+
const resolvedBundle = base.bundle ?? extracted ?? null;
|
|
1598
|
+
const rigFromBundle = convertBundleGraph(
|
|
1599
|
+
pickBundleGraph(resolvedBundle, ["rig"])
|
|
1600
|
+
);
|
|
1601
|
+
const resolvedRig = base.rig ?? rigFromBundle ?? void 0;
|
|
1602
|
+
const basePose = base.pose;
|
|
1603
|
+
const hasBasePoseGraphOverride = Boolean(
|
|
1604
|
+
basePose && Object.prototype.hasOwnProperty.call(basePose, "graph")
|
|
1605
|
+
);
|
|
1606
|
+
const hasBasePoseConfigOverride = Boolean(
|
|
1607
|
+
basePose && Object.prototype.hasOwnProperty.call(basePose, "config")
|
|
1608
|
+
);
|
|
1609
|
+
const poseStageFilter = basePose?.stageNeutralFilter;
|
|
1610
|
+
const poseGraphFromBundle = hasBasePoseGraphOverride ? null : convertBundleGraph(
|
|
1611
|
+
pickBundleGraph(resolvedBundle, ["pose-driver", "pose"])
|
|
1612
|
+
);
|
|
1613
|
+
const resolvedPoseGraph = hasBasePoseGraphOverride ? basePose?.graph : basePose?.graph ?? poseGraphFromBundle ?? void 0;
|
|
1614
|
+
const resolvedPoseConfig = hasBasePoseConfigOverride ? basePose?.config : basePose?.config ?? resolvedBundle?.poses?.config ?? void 0;
|
|
1615
|
+
let resolvedPose = basePose;
|
|
1616
|
+
if (basePose) {
|
|
1617
|
+
const nextPose = { ...basePose };
|
|
1618
|
+
let changed = false;
|
|
1619
|
+
if (resolvedPoseGraph && basePose.graph !== resolvedPoseGraph) {
|
|
1620
|
+
nextPose.graph = resolvedPoseGraph;
|
|
1621
|
+
changed = true;
|
|
1622
|
+
}
|
|
1623
|
+
if (resolvedPoseConfig && basePose.config !== resolvedPoseConfig) {
|
|
1624
|
+
nextPose.config = resolvedPoseConfig;
|
|
697
1625
|
changed = true;
|
|
698
1626
|
}
|
|
699
1627
|
if (!resolvedPoseGraph && !basePose.graph) {
|
|
@@ -722,6 +1650,10 @@ function mergeAssetBundle(base, extracted, extractedAnimations) {
|
|
|
722
1650
|
animationsFromAsset
|
|
723
1651
|
);
|
|
724
1652
|
}
|
|
1653
|
+
const programsFromBundle = mergeProgramLists(
|
|
1654
|
+
base.programs,
|
|
1655
|
+
convertBundlePrograms(resolvedBundle?.graphs)
|
|
1656
|
+
);
|
|
725
1657
|
const merged = {
|
|
726
1658
|
...base
|
|
727
1659
|
};
|
|
@@ -732,14 +1664,106 @@ function mergeAssetBundle(base, extracted, extractedAnimations) {
|
|
|
732
1664
|
}
|
|
733
1665
|
merged.pose = resolvedPose;
|
|
734
1666
|
merged.animations = resolvedAnimations;
|
|
1667
|
+
merged.programs = programsFromBundle;
|
|
735
1668
|
merged.bundle = resolvedBundle;
|
|
736
1669
|
return merged;
|
|
737
1670
|
}
|
|
1671
|
+
function normalizeStoredAnimationInterpolation(interpolation) {
|
|
1672
|
+
const mode = typeof interpolation === "string" ? interpolation.trim().toLowerCase() : "linear";
|
|
1673
|
+
if (mode === "step") {
|
|
1674
|
+
return "step";
|
|
1675
|
+
}
|
|
1676
|
+
if (mode === "cubic" || mode === "cubicspline") {
|
|
1677
|
+
return "cubic";
|
|
1678
|
+
}
|
|
1679
|
+
return "linear";
|
|
1680
|
+
}
|
|
1681
|
+
function buildStoredAnimationTransitions(mode) {
|
|
1682
|
+
if (mode === "cubic") {
|
|
1683
|
+
return void 0;
|
|
1684
|
+
}
|
|
1685
|
+
if (mode === "step") {
|
|
1686
|
+
return {
|
|
1687
|
+
in: { x: 1, y: 1 },
|
|
1688
|
+
out: { x: 1, y: 0 }
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
return {
|
|
1692
|
+
in: { x: 1, y: 1 },
|
|
1693
|
+
out: { x: 0, y: 0 }
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
function toStoredAnimationClip(fallbackId, clip) {
|
|
1697
|
+
const clipId = typeof clip.id === "string" && clip.id.trim().length > 0 ? clip.id.trim() : fallbackId;
|
|
1698
|
+
const clipName = typeof clip.name === "string" && clip.name.trim().length > 0 ? clip.name.trim() : clipId;
|
|
1699
|
+
const durationSeconds = resolveClipDurationSeconds(clip, 0);
|
|
1700
|
+
const durationMs = Math.max(1, Math.round(durationSeconds * 1e3));
|
|
1701
|
+
const tracks = (Array.isArray(clip.tracks) ? clip.tracks : []).map((rawTrack, trackIndex) => {
|
|
1702
|
+
const channel = typeof rawTrack.channel === "string" ? rawTrack.channel.trim() : "";
|
|
1703
|
+
if (!channel) {
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
const keyframes = (Array.isArray(rawTrack.keyframes) ? rawTrack.keyframes : []).map((keyframe) => {
|
|
1707
|
+
const time = Number(keyframe.time);
|
|
1708
|
+
const value = Number(keyframe.value);
|
|
1709
|
+
const keyframeId = keyframe["id"];
|
|
1710
|
+
const keyframeInterpolation = keyframe["interpolation"];
|
|
1711
|
+
if (!Number.isFinite(time) || !Number.isFinite(value)) {
|
|
1712
|
+
return null;
|
|
1713
|
+
}
|
|
1714
|
+
return {
|
|
1715
|
+
id: typeof keyframeId === "string" && keyframeId.trim().length > 0 ? keyframeId.trim() : `${clipId}:track-${trackIndex.toString().padStart(4, "0")}:point-${time.toFixed(6)}`,
|
|
1716
|
+
time,
|
|
1717
|
+
value,
|
|
1718
|
+
mode: normalizeStoredAnimationInterpolation(
|
|
1719
|
+
keyframeInterpolation ?? rawTrack.interpolation
|
|
1720
|
+
)
|
|
1721
|
+
};
|
|
1722
|
+
}).filter(Boolean);
|
|
1723
|
+
if (keyframes.length === 0) {
|
|
1724
|
+
return null;
|
|
1725
|
+
}
|
|
1726
|
+
keyframes.sort((left, right) => {
|
|
1727
|
+
if (left.time !== right.time) {
|
|
1728
|
+
return left.time - right.time;
|
|
1729
|
+
}
|
|
1730
|
+
return left.id.localeCompare(right.id);
|
|
1731
|
+
});
|
|
1732
|
+
const rawTrackId = rawTrack["id"];
|
|
1733
|
+
const rawTrackName = rawTrack["name"];
|
|
1734
|
+
const trackId = typeof rawTrackId === "string" && rawTrackId.trim().length > 0 ? rawTrackId.trim() : `${clipId}:track-${trackIndex.toString().padStart(4, "0")}`;
|
|
1735
|
+
const trackName = typeof rawTrackName === "string" && rawTrackName.trim().length > 0 ? rawTrackName.trim() : channel.replace(/^\/+/, "") || trackId;
|
|
1736
|
+
const denominator = durationSeconds > 0 ? durationSeconds : 1;
|
|
1737
|
+
return {
|
|
1738
|
+
id: trackId,
|
|
1739
|
+
name: trackName,
|
|
1740
|
+
animatableId: channel,
|
|
1741
|
+
points: keyframes.map((keyframe) => {
|
|
1742
|
+
const stamp = Math.max(0, Math.min(1, keyframe.time / denominator));
|
|
1743
|
+
const transitions = buildStoredAnimationTransitions(keyframe.mode);
|
|
1744
|
+
return {
|
|
1745
|
+
id: keyframe.id,
|
|
1746
|
+
stamp,
|
|
1747
|
+
value: keyframe.value,
|
|
1748
|
+
...transitions ? { transitions } : {}
|
|
1749
|
+
};
|
|
1750
|
+
})
|
|
1751
|
+
};
|
|
1752
|
+
}).filter(Boolean);
|
|
1753
|
+
return {
|
|
1754
|
+
id: clipId,
|
|
1755
|
+
name: clipName,
|
|
1756
|
+
duration: durationMs,
|
|
1757
|
+
groups: {},
|
|
1758
|
+
tracks
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
738
1761
|
function VizijRuntimeProvider({
|
|
739
1762
|
assetBundle,
|
|
740
1763
|
children,
|
|
741
1764
|
namespace: namespaceProp,
|
|
742
1765
|
faceId: faceIdProp,
|
|
1766
|
+
updateTier = "auto",
|
|
743
1767
|
autoCreate = true,
|
|
744
1768
|
createOptions,
|
|
745
1769
|
autostart = false,
|
|
@@ -747,6 +1771,7 @@ function VizijRuntimeProvider({
|
|
|
747
1771
|
mergeStrategy,
|
|
748
1772
|
onRegisterControllers,
|
|
749
1773
|
onStatusChange,
|
|
1774
|
+
transformOutputWrite,
|
|
750
1775
|
orchestratorScope = "auto"
|
|
751
1776
|
}) {
|
|
752
1777
|
const storeRef = (0, import_react2.useRef)(null);
|
|
@@ -767,6 +1792,7 @@ function VizijRuntimeProvider({
|
|
|
767
1792
|
assetBundle,
|
|
768
1793
|
namespace: namespaceProp,
|
|
769
1794
|
faceId: faceIdProp,
|
|
1795
|
+
updateTier,
|
|
770
1796
|
autoCreate,
|
|
771
1797
|
autostart,
|
|
772
1798
|
driveOrchestrator,
|
|
@@ -774,6 +1800,7 @@ function VizijRuntimeProvider({
|
|
|
774
1800
|
mergeStrategy,
|
|
775
1801
|
onRegisterControllers,
|
|
776
1802
|
onStatusChange,
|
|
1803
|
+
transformOutputWrite,
|
|
777
1804
|
store: storeRef.current,
|
|
778
1805
|
children
|
|
779
1806
|
}
|
|
@@ -795,9 +1822,11 @@ function VizijRuntimeProviderInner({
|
|
|
795
1822
|
assetBundle: initialAssetBundle,
|
|
796
1823
|
namespace: namespaceProp,
|
|
797
1824
|
faceId: faceIdProp,
|
|
1825
|
+
updateTier,
|
|
798
1826
|
mergeStrategy,
|
|
799
1827
|
onRegisterControllers,
|
|
800
1828
|
onStatusChange,
|
|
1829
|
+
transformOutputWrite,
|
|
801
1830
|
store,
|
|
802
1831
|
children,
|
|
803
1832
|
autoCreate,
|
|
@@ -805,35 +1834,66 @@ function VizijRuntimeProviderInner({
|
|
|
805
1834
|
createOptions,
|
|
806
1835
|
driveOrchestrator
|
|
807
1836
|
}) {
|
|
1837
|
+
const [assetBundleOverride, setAssetBundleOverride] = (0, import_react2.useState)(null);
|
|
1838
|
+
const [graphUpdateToken, setGraphUpdateToken] = (0, import_react2.useState)(0);
|
|
1839
|
+
const effectiveAssetBundle = assetBundleOverride ?? initialAssetBundle;
|
|
1840
|
+
const latestEffectiveAssetBundleRef = (0, import_react2.useRef)(effectiveAssetBundle);
|
|
808
1841
|
const [extractedBundle, setExtractedBundle] = (0, import_react2.useState)(() => {
|
|
809
|
-
if (
|
|
810
|
-
return
|
|
1842
|
+
if (effectiveAssetBundle.bundle) {
|
|
1843
|
+
return effectiveAssetBundle.bundle;
|
|
811
1844
|
}
|
|
812
|
-
if (
|
|
813
|
-
return
|
|
1845
|
+
if (effectiveAssetBundle.glb.kind === "world" && effectiveAssetBundle.glb.bundle) {
|
|
1846
|
+
return effectiveAssetBundle.glb.bundle;
|
|
814
1847
|
}
|
|
815
1848
|
return null;
|
|
816
1849
|
});
|
|
817
1850
|
const [extractedAnimations, setExtractedAnimations] = (0, import_react2.useState)([]);
|
|
1851
|
+
const previousBundleRef = (0, import_react2.useRef)(null);
|
|
1852
|
+
const suppressNextBundlePlanRef = (0, import_react2.useRef)(false);
|
|
1853
|
+
const pendingPlanRef = (0, import_react2.useRef)(null);
|
|
1854
|
+
const updateTierRef = (0, import_react2.useRef)(updateTier);
|
|
818
1855
|
(0, import_react2.useEffect)(() => {
|
|
819
|
-
if (
|
|
820
|
-
setExtractedBundle(
|
|
1856
|
+
if (effectiveAssetBundle.bundle) {
|
|
1857
|
+
setExtractedBundle(effectiveAssetBundle.bundle);
|
|
821
1858
|
return;
|
|
822
1859
|
}
|
|
823
|
-
if (
|
|
824
|
-
setExtractedBundle(
|
|
1860
|
+
if (effectiveAssetBundle.glb.kind === "world") {
|
|
1861
|
+
setExtractedBundle(effectiveAssetBundle.glb.bundle ?? null);
|
|
825
1862
|
} else {
|
|
826
1863
|
setExtractedBundle(null);
|
|
827
1864
|
}
|
|
828
|
-
}, [
|
|
1865
|
+
}, [effectiveAssetBundle]);
|
|
1866
|
+
(0, import_react2.useEffect)(() => {
|
|
1867
|
+
updateTierRef.current = updateTier;
|
|
1868
|
+
}, [updateTier]);
|
|
829
1869
|
const assetBundle = (0, import_react2.useMemo)(
|
|
830
1870
|
() => mergeAssetBundle(
|
|
831
|
-
|
|
1871
|
+
effectiveAssetBundle,
|
|
832
1872
|
extractedBundle,
|
|
833
1873
|
extractedAnimations
|
|
834
1874
|
),
|
|
835
|
-
[
|
|
1875
|
+
[effectiveAssetBundle, extractedBundle, extractedAnimations]
|
|
836
1876
|
);
|
|
1877
|
+
(0, import_react2.useEffect)(() => {
|
|
1878
|
+
latestEffectiveAssetBundleRef.current = effectiveAssetBundle;
|
|
1879
|
+
}, [effectiveAssetBundle]);
|
|
1880
|
+
(0, import_react2.useEffect)(() => {
|
|
1881
|
+
if (suppressNextBundlePlanRef.current) {
|
|
1882
|
+
suppressNextBundlePlanRef.current = false;
|
|
1883
|
+
previousBundleRef.current = effectiveAssetBundle;
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
const plan = resolveRuntimeUpdatePlan(
|
|
1887
|
+
previousBundleRef.current,
|
|
1888
|
+
effectiveAssetBundle,
|
|
1889
|
+
updateTierRef.current
|
|
1890
|
+
);
|
|
1891
|
+
pendingPlanRef.current = plan;
|
|
1892
|
+
previousBundleRef.current = effectiveAssetBundle;
|
|
1893
|
+
if (plan.reregisterGraphs) {
|
|
1894
|
+
setGraphUpdateToken((prev) => prev + 1);
|
|
1895
|
+
}
|
|
1896
|
+
}, [effectiveAssetBundle]);
|
|
837
1897
|
const {
|
|
838
1898
|
ready,
|
|
839
1899
|
createOrchestrator,
|
|
@@ -842,6 +1902,7 @@ function VizijRuntimeProviderInner({
|
|
|
842
1902
|
registerAnimation,
|
|
843
1903
|
removeGraph,
|
|
844
1904
|
removeAnimation,
|
|
1905
|
+
removeInput,
|
|
845
1906
|
listControllers,
|
|
846
1907
|
setInput: orchestratorSetInput,
|
|
847
1908
|
getPathSnapshot,
|
|
@@ -869,13 +1930,57 @@ function VizijRuntimeProviderInner({
|
|
|
869
1930
|
const namespaceRef = (0, import_react2.useRef)(namespace);
|
|
870
1931
|
const driveOrchestratorRef = (0, import_react2.useRef)(driveOrchestrator);
|
|
871
1932
|
const rigInputMapRef = (0, import_react2.useRef)({});
|
|
1933
|
+
const rigPoseControlInputIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
872
1934
|
const registeredGraphsRef = (0, import_react2.useRef)([]);
|
|
873
1935
|
const registeredAnimationsRef = (0, import_react2.useRef)([]);
|
|
874
1936
|
const mergedGraphRef = (0, import_react2.useRef)(null);
|
|
1937
|
+
const poseControlBridgeValuesRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
1938
|
+
const poseWeightFallbackMap = (0, import_react2.useMemo)(() => {
|
|
1939
|
+
const map = /* @__PURE__ */ new Map();
|
|
1940
|
+
const poseConfig = assetBundle.pose?.config;
|
|
1941
|
+
if (!poseConfig) {
|
|
1942
|
+
return map;
|
|
1943
|
+
}
|
|
1944
|
+
const posePaths = buildPoseWeightPathMap(
|
|
1945
|
+
poseConfig.poses ?? [],
|
|
1946
|
+
poseConfig.faceId ?? faceId ?? "face"
|
|
1947
|
+
);
|
|
1948
|
+
(poseConfig.poses ?? []).forEach((pose) => {
|
|
1949
|
+
const posePath = posePaths.get(pose.id);
|
|
1950
|
+
if (!posePath) {
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
const values = Object.fromEntries(
|
|
1954
|
+
Object.entries(pose.values ?? {}).filter(
|
|
1955
|
+
([, value]) => Number.isFinite(value)
|
|
1956
|
+
)
|
|
1957
|
+
);
|
|
1958
|
+
map.set(posePath, values);
|
|
1959
|
+
});
|
|
1960
|
+
return map;
|
|
1961
|
+
}, [assetBundle.pose?.config, faceId]);
|
|
1962
|
+
const useLegacyPoseWeightFallback = (0, import_react2.useMemo)(
|
|
1963
|
+
() => shouldUseLegacyPoseWeightFallback(Boolean(assetBundle.pose?.graph)),
|
|
1964
|
+
[assetBundle.pose?.graph]
|
|
1965
|
+
);
|
|
1966
|
+
const resolvedProgramAssets = (0, import_react2.useMemo)(
|
|
1967
|
+
() => assetBundle.programs && assetBundle.programs.length > 0 ? assetBundle.programs : convertBundlePrograms(assetBundle.bundle?.graphs),
|
|
1968
|
+
[assetBundle.bundle?.graphs, assetBundle.programs]
|
|
1969
|
+
);
|
|
875
1970
|
const [inputConstraints, setInputConstraints] = (0, import_react2.useState)({});
|
|
1971
|
+
const inputConstraintsRef = (0, import_react2.useRef)({});
|
|
876
1972
|
const avgStepDtRef = (0, import_react2.useRef)(null);
|
|
877
1973
|
const animationTweensRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
878
1974
|
const clipPlaybackRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
1975
|
+
const programPlaybackRef = (0, import_react2.useRef)(
|
|
1976
|
+
/* @__PURE__ */ new Map()
|
|
1977
|
+
);
|
|
1978
|
+
const programControllerIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
1979
|
+
const clipOutputValuesRef = (0, import_react2.useRef)(
|
|
1980
|
+
/* @__PURE__ */ new Map()
|
|
1981
|
+
);
|
|
1982
|
+
const clipAggregateValuesRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
1983
|
+
const animationSystemActiveRef = (0, import_react2.useRef)(true);
|
|
879
1984
|
const stagedInputsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
880
1985
|
const autostartRef = (0, import_react2.useRef)(autostart);
|
|
881
1986
|
const lastActivityTimeRef = (0, import_react2.useRef)(now());
|
|
@@ -893,6 +1998,7 @@ function VizijRuntimeProviderInner({
|
|
|
893
1998
|
(0, import_react2.useEffect)(() => {
|
|
894
1999
|
const rigAsset = assetBundle.rig;
|
|
895
2000
|
if (!rigAsset) {
|
|
2001
|
+
inputConstraintsRef.current = {};
|
|
896
2002
|
setInputConstraints({});
|
|
897
2003
|
return;
|
|
898
2004
|
}
|
|
@@ -905,15 +2011,8 @@ function VizijRuntimeProviderInner({
|
|
|
905
2011
|
rigAsset.inputMetadata,
|
|
906
2012
|
namespace
|
|
907
2013
|
);
|
|
2014
|
+
inputConstraintsRef.current = constraints;
|
|
908
2015
|
setInputConstraints(constraints);
|
|
909
|
-
const isDevEnv = typeof globalThis !== "undefined" && Boolean(globalThis?.process?.env?.NODE_ENV !== "production");
|
|
910
|
-
if (isDevEnv) {
|
|
911
|
-
const size = Object.keys(constraints).length;
|
|
912
|
-
console.log("[vizij-runtime] input constraints computed", size, {
|
|
913
|
-
namespace,
|
|
914
|
-
rigId: rigAsset.id
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
2016
|
}, [assetBundle.rig, namespace]);
|
|
918
2017
|
const requestLoopMode = (0, import_react2.useCallback)((mode) => {
|
|
919
2018
|
if (!runtimeMountedRef.current) {
|
|
@@ -922,7 +2021,28 @@ function VizijRuntimeProviderInner({
|
|
|
922
2021
|
setLoopMode((prev) => prev === mode ? prev : mode);
|
|
923
2022
|
}, []);
|
|
924
2023
|
const hasActiveAnimations = (0, import_react2.useCallback)(() => {
|
|
925
|
-
|
|
2024
|
+
if (animationTweensRef.current.size > 0) {
|
|
2025
|
+
return true;
|
|
2026
|
+
}
|
|
2027
|
+
if (!animationSystemActiveRef.current) {
|
|
2028
|
+
for (const state of programPlaybackRef.current.values()) {
|
|
2029
|
+
if (state.state === "playing") {
|
|
2030
|
+
return true;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
return false;
|
|
2034
|
+
}
|
|
2035
|
+
for (const state of clipPlaybackRef.current.values()) {
|
|
2036
|
+
if (state.playing) {
|
|
2037
|
+
return true;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
for (const state of programPlaybackRef.current.values()) {
|
|
2041
|
+
if (state.state === "playing") {
|
|
2042
|
+
return true;
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
return false;
|
|
926
2046
|
}, []);
|
|
927
2047
|
const computeDesiredLoopMode = (0, import_react2.useCallback)(() => {
|
|
928
2048
|
const hasAnimations = hasActiveAnimations();
|
|
@@ -944,11 +2064,50 @@ function VizijRuntimeProviderInner({
|
|
|
944
2064
|
}, [updateLoopMode]);
|
|
945
2065
|
const setInput = (0, import_react2.useCallback)(
|
|
946
2066
|
(path, value, shape) => {
|
|
2067
|
+
const numericValue = (0, import_value_json2.valueAsNumber)(value);
|
|
2068
|
+
const basePath = stripNamespace(
|
|
2069
|
+
normalisePath(path),
|
|
2070
|
+
namespaceRef.current
|
|
2071
|
+
);
|
|
2072
|
+
const poseValues = useLegacyPoseWeightFallback && numericValue != null ? poseWeightFallbackMap.get(basePath) : void 0;
|
|
2073
|
+
if (poseValues && numericValue != null) {
|
|
2074
|
+
const poseFaceId = assetBundle.pose?.config?.faceId ?? faceId ?? "face";
|
|
2075
|
+
const rigMap = rigInputMapRef.current;
|
|
2076
|
+
Object.entries(poseValues).forEach(([inputId, poseValue]) => {
|
|
2077
|
+
if (!Number.isFinite(poseValue)) {
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
const controlPath = resolvePoseControlInputPath({
|
|
2081
|
+
inputId,
|
|
2082
|
+
basePath: buildRigInputPath(
|
|
2083
|
+
poseFaceId,
|
|
2084
|
+
`/pose/control/${inputId}`
|
|
2085
|
+
),
|
|
2086
|
+
rigInputPathMap: rigMap,
|
|
2087
|
+
hasNativePoseControlInput: true
|
|
2088
|
+
}) ?? buildRigInputPath(poseFaceId, `/pose/control/${inputId}`);
|
|
2089
|
+
setInput(controlPath, { float: Number(poseValue) * numericValue });
|
|
2090
|
+
});
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
947
2093
|
markActivity();
|
|
948
2094
|
const namespacedPath = namespaceTypedPath(path, namespaceRef.current);
|
|
2095
|
+
if (isRuntimeDebugEnabled() && (namespacedPath.includes("animation/authoring.timeline.main") || namespacedPath.endsWith("/blink"))) {
|
|
2096
|
+
console.log("[vizij-runtime] stage input", {
|
|
2097
|
+
path,
|
|
2098
|
+
namespacedPath,
|
|
2099
|
+
value
|
|
2100
|
+
});
|
|
2101
|
+
}
|
|
949
2102
|
stagedInputsRef.current.set(namespacedPath, { value, shape });
|
|
950
2103
|
},
|
|
951
|
-
[
|
|
2104
|
+
[
|
|
2105
|
+
assetBundle.pose?.config?.faceId,
|
|
2106
|
+
faceId,
|
|
2107
|
+
markActivity,
|
|
2108
|
+
poseWeightFallbackMap,
|
|
2109
|
+
useLegacyPoseWeightFallback
|
|
2110
|
+
]
|
|
952
2111
|
);
|
|
953
2112
|
const reportStatus = (0, import_react2.useCallback)(
|
|
954
2113
|
(updater) => {
|
|
@@ -1012,10 +2171,14 @@ function VizijRuntimeProviderInner({
|
|
|
1012
2171
|
});
|
|
1013
2172
|
registeredGraphsRef.current = [];
|
|
1014
2173
|
registeredAnimationsRef.current = [];
|
|
2174
|
+
programControllerIdsRef.current.clear();
|
|
1015
2175
|
mergedGraphRef.current = null;
|
|
1016
2176
|
outputPathsRef.current = /* @__PURE__ */ new Set();
|
|
1017
2177
|
baseOutputPathsRef.current = /* @__PURE__ */ new Set();
|
|
1018
2178
|
namespacedOutputPathsRef.current = /* @__PURE__ */ new Set();
|
|
2179
|
+
rigPoseControlInputIdsRef.current = /* @__PURE__ */ new Set();
|
|
2180
|
+
clipOutputValuesRef.current.clear();
|
|
2181
|
+
clipAggregateValuesRef.current.clear();
|
|
1019
2182
|
}, [listControllers, removeAnimation, removeGraph, pushError]);
|
|
1020
2183
|
(0, import_react2.useEffect)(() => {
|
|
1021
2184
|
namespaceRef.current = namespace;
|
|
@@ -1028,10 +2191,19 @@ function VizijRuntimeProviderInner({
|
|
|
1028
2191
|
(0, import_react2.useEffect)(() => {
|
|
1029
2192
|
driveOrchestratorRef.current = driveOrchestrator;
|
|
1030
2193
|
}, [driveOrchestrator]);
|
|
1031
|
-
const glbAsset =
|
|
1032
|
-
const baseBundle =
|
|
2194
|
+
const glbAsset = effectiveAssetBundle.glb;
|
|
2195
|
+
const baseBundle = effectiveAssetBundle.bundle ?? null;
|
|
1033
2196
|
(0, import_react2.useEffect)(() => {
|
|
1034
2197
|
let cancelled = false;
|
|
2198
|
+
const plan = pendingPlanRef.current;
|
|
2199
|
+
if (plan && !plan.reloadAssets && status.rootId !== null) {
|
|
2200
|
+
reportStatus(
|
|
2201
|
+
(prev) => prev.loading ? { ...prev, loading: false } : prev
|
|
2202
|
+
);
|
|
2203
|
+
return () => {
|
|
2204
|
+
cancelled = true;
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
1035
2207
|
resetErrors();
|
|
1036
2208
|
reportStatus((prev) => ({
|
|
1037
2209
|
...prev,
|
|
@@ -1083,6 +2255,12 @@ function VizijRuntimeProviderInner({
|
|
|
1083
2255
|
setExtractedAnimations(convertExtractedAnimations(gltfAnimations));
|
|
1084
2256
|
const rootId = findRootId(world);
|
|
1085
2257
|
store.getState().addWorldElements(world, animatables, true);
|
|
2258
|
+
if (pendingPlanRef.current?.reloadAssets) {
|
|
2259
|
+
pendingPlanRef.current = {
|
|
2260
|
+
...pendingPlanRef.current,
|
|
2261
|
+
reloadAssets: false
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
1086
2264
|
reportStatus((prev) => ({
|
|
1087
2265
|
...prev,
|
|
1088
2266
|
loading: false,
|
|
@@ -1120,7 +2298,8 @@ function VizijRuntimeProviderInner({
|
|
|
1120
2298
|
reportStatus,
|
|
1121
2299
|
resetErrors,
|
|
1122
2300
|
setExtractedBundle,
|
|
1123
|
-
setExtractedAnimations
|
|
2301
|
+
setExtractedAnimations,
|
|
2302
|
+
status.rootId
|
|
1124
2303
|
]);
|
|
1125
2304
|
(0, import_react2.useEffect)(() => {
|
|
1126
2305
|
if (!ready && autoCreate) {
|
|
@@ -1136,6 +2315,15 @@ function VizijRuntimeProviderInner({
|
|
|
1136
2315
|
}, [ready, autoCreate, createOptions, createOrchestrator, pushError]);
|
|
1137
2316
|
const registerControllers = (0, import_react2.useCallback)(async () => {
|
|
1138
2317
|
clearControllers();
|
|
2318
|
+
if (isRuntimeDebugEnabled()) {
|
|
2319
|
+
console.log("[vizij-runtime] registerControllers", {
|
|
2320
|
+
hasRig: Boolean(assetBundle.rig),
|
|
2321
|
+
hasPose: Boolean(assetBundle.pose?.graph),
|
|
2322
|
+
animationCount: assetBundle.animations?.length ?? 0,
|
|
2323
|
+
animationIds: (assetBundle.animations ?? []).map((anim) => anim.id),
|
|
2324
|
+
namespace
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
1139
2327
|
const baseOutputPaths = /* @__PURE__ */ new Set();
|
|
1140
2328
|
const namespacedOutputPaths = /* @__PURE__ */ new Set();
|
|
1141
2329
|
const recordOutputs = (paths) => {
|
|
@@ -1147,39 +2335,60 @@ function VizijRuntimeProviderInner({
|
|
|
1147
2335
|
namespacedOutputPaths.add(namespaceTypedPath(trimmed, namespace));
|
|
1148
2336
|
});
|
|
1149
2337
|
};
|
|
2338
|
+
const graphConfigs = [];
|
|
2339
|
+
rigInputMapRef.current = {};
|
|
2340
|
+
rigPoseControlInputIdsRef.current = /* @__PURE__ */ new Set();
|
|
2341
|
+
poseControlBridgeValuesRef.current.clear();
|
|
1150
2342
|
const rigAsset = assetBundle.rig;
|
|
1151
|
-
if (
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
2343
|
+
if (rigAsset) {
|
|
2344
|
+
const rigSpec = resolveGraphSpec(
|
|
2345
|
+
rigAsset,
|
|
2346
|
+
`${rigAsset.id ?? "rig"} graph`
|
|
2347
|
+
);
|
|
2348
|
+
if (!rigSpec) {
|
|
2349
|
+
pushError({
|
|
2350
|
+
message: "Rig graph is missing a usable spec or IR payload.",
|
|
2351
|
+
phase: "registration",
|
|
2352
|
+
timestamp: performance.now()
|
|
2353
|
+
});
|
|
2354
|
+
} else {
|
|
2355
|
+
const rigOutputs = collectOutputPaths(rigSpec);
|
|
2356
|
+
const rigInputs = collectInputPaths(rigSpec);
|
|
2357
|
+
const rigPoseControlInputIds = /* @__PURE__ */ new Set();
|
|
2358
|
+
rigInputs.forEach((path) => {
|
|
2359
|
+
const poseControlMatch = /^rig\/[^/]+\/pose\/control\/(.+)$/.exec(
|
|
2360
|
+
path.trim()
|
|
2361
|
+
);
|
|
2362
|
+
const inputId = (poseControlMatch?.[1] ?? "").trim();
|
|
2363
|
+
if (inputId.length > 0) {
|
|
2364
|
+
rigPoseControlInputIds.add(inputId);
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
rigInputMapRef.current = collectInputPathMap(rigSpec);
|
|
2368
|
+
rigPoseControlInputIdsRef.current = rigPoseControlInputIds;
|
|
2369
|
+
if (isRuntimeDebugEnabled()) {
|
|
2370
|
+
const blinkKeys = Object.keys(rigInputMapRef.current).filter(
|
|
2371
|
+
(key) => key.toLowerCase().includes("blink")
|
|
2372
|
+
);
|
|
2373
|
+
const blinkMappings = blinkKeys.slice(0, 20).map((key) => `${key} => ${rigInputMapRef.current[key] ?? "?"}`);
|
|
2374
|
+
console.log("[vizij-runtime] rig input map sample", {
|
|
2375
|
+
blink: rigInputMapRef.current["blink"] ?? null,
|
|
2376
|
+
blinkKeys: blinkKeys.slice(0, 12),
|
|
2377
|
+
blinkMappings: blinkMappings.join(" | ")
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
recordOutputs(rigOutputs);
|
|
2381
|
+
const rigSubs = rigAsset.subscriptions ?? {
|
|
2382
|
+
inputs: rigInputs,
|
|
2383
|
+
outputs: rigOutputs
|
|
2384
|
+
};
|
|
2385
|
+
graphConfigs.push({
|
|
2386
|
+
id: namespaceControllerId(rigAsset.id, namespace, "graph"),
|
|
2387
|
+
spec: stripNulls(namespaceGraphSpec(rigSpec, namespace)),
|
|
2388
|
+
subs: namespaceSubscriptions(rigSubs, namespace)
|
|
2389
|
+
});
|
|
1181
2390
|
}
|
|
1182
|
-
|
|
2391
|
+
}
|
|
1183
2392
|
const poseGraphAsset = assetBundle.pose?.graph;
|
|
1184
2393
|
if (poseGraphAsset) {
|
|
1185
2394
|
const poseSpec = resolveGraphSpec(
|
|
@@ -1196,7 +2405,7 @@ function VizijRuntimeProviderInner({
|
|
|
1196
2405
|
};
|
|
1197
2406
|
graphConfigs.push({
|
|
1198
2407
|
id: namespaceControllerId(poseGraphAsset.id, namespace, "graph"),
|
|
1199
|
-
spec: namespaceGraphSpec(poseSpec, namespace),
|
|
2408
|
+
spec: stripNulls(namespaceGraphSpec(poseSpec, namespace)),
|
|
1200
2409
|
subs: namespaceSubscriptions(poseSubs, namespace)
|
|
1201
2410
|
});
|
|
1202
2411
|
} else {
|
|
@@ -1205,6 +2414,31 @@ function VizijRuntimeProviderInner({
|
|
|
1205
2414
|
);
|
|
1206
2415
|
}
|
|
1207
2416
|
}
|
|
2417
|
+
for (const animation of assetBundle.animations ?? []) {
|
|
2418
|
+
const bridgeOutputs = collectAnimationClipOutputPaths(
|
|
2419
|
+
animation.clip,
|
|
2420
|
+
faceId ?? void 0,
|
|
2421
|
+
rigInputMapRef.current
|
|
2422
|
+
);
|
|
2423
|
+
if (isRuntimeDebugEnabled()) {
|
|
2424
|
+
console.log("[vizij-runtime] animation output routing", {
|
|
2425
|
+
animationId: animation.id,
|
|
2426
|
+
bridgeOutputs,
|
|
2427
|
+
bridgeOutputsText: bridgeOutputs.join(" | ")
|
|
2428
|
+
});
|
|
2429
|
+
}
|
|
2430
|
+
recordOutputs(bridgeOutputs);
|
|
2431
|
+
}
|
|
2432
|
+
for (const program of resolvedProgramAssets) {
|
|
2433
|
+
const programSpec = resolveGraphSpec(
|
|
2434
|
+
program.graph,
|
|
2435
|
+
`${program.id ?? "program"} graph (outputs)`
|
|
2436
|
+
);
|
|
2437
|
+
if (!programSpec) {
|
|
2438
|
+
continue;
|
|
2439
|
+
}
|
|
2440
|
+
recordOutputs(collectOutputPaths(programSpec));
|
|
2441
|
+
}
|
|
1208
2442
|
outputPathsRef.current = namespacedOutputPaths;
|
|
1209
2443
|
baseOutputPathsRef.current = baseOutputPaths;
|
|
1210
2444
|
namespacedOutputPathsRef.current = namespacedOutputPaths;
|
|
@@ -1237,20 +2471,30 @@ function VizijRuntimeProviderInner({
|
|
|
1237
2471
|
});
|
|
1238
2472
|
}
|
|
1239
2473
|
registeredGraphsRef.current = graphIds;
|
|
2474
|
+
if (isRuntimeDebugEnabled()) {
|
|
2475
|
+
console.log("[vizij-runtime] registered graph ids", graphIds);
|
|
2476
|
+
}
|
|
1240
2477
|
const animationIds = [];
|
|
1241
2478
|
for (const anim of assetBundle.animations ?? []) {
|
|
1242
2479
|
try {
|
|
1243
2480
|
const controllerId = namespaceControllerId(anim.id, namespace, "animation") ?? anim.id;
|
|
2481
|
+
const animationPayload = anim.setup?.animation ?? toStoredAnimationClip(anim.id, anim.clip);
|
|
1244
2482
|
const config = {
|
|
1245
2483
|
id: controllerId,
|
|
1246
2484
|
setup: {
|
|
1247
|
-
|
|
1248
|
-
|
|
2485
|
+
...anim.setup ?? {},
|
|
2486
|
+
animation: animationPayload
|
|
1249
2487
|
}
|
|
1250
2488
|
};
|
|
1251
2489
|
const id = registerAnimation(config);
|
|
1252
2490
|
animationIds.push(id);
|
|
1253
2491
|
} catch (err) {
|
|
2492
|
+
if (isRuntimeDebugEnabled()) {
|
|
2493
|
+
console.warn("[vizij-runtime] failed animation registration", {
|
|
2494
|
+
animationId: anim.id,
|
|
2495
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2496
|
+
});
|
|
2497
|
+
}
|
|
1254
2498
|
pushError({
|
|
1255
2499
|
message: `Failed to register animation ${anim.id}`,
|
|
1256
2500
|
cause: err,
|
|
@@ -1275,6 +2519,13 @@ function VizijRuntimeProviderInner({
|
|
|
1275
2519
|
});
|
|
1276
2520
|
}
|
|
1277
2521
|
const controllers = listControllers();
|
|
2522
|
+
if (isRuntimeDebugEnabled()) {
|
|
2523
|
+
console.log("[vizij-runtime] controllers after register", {
|
|
2524
|
+
controllers,
|
|
2525
|
+
graphIds,
|
|
2526
|
+
animationIds
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
1278
2529
|
reportStatus((prev) => ({
|
|
1279
2530
|
...prev,
|
|
1280
2531
|
ready: true,
|
|
@@ -1294,12 +2545,18 @@ function VizijRuntimeProviderInner({
|
|
|
1294
2545
|
registerGraph,
|
|
1295
2546
|
registerMergedGraph,
|
|
1296
2547
|
reportStatus,
|
|
2548
|
+
resolvedProgramAssets,
|
|
1297
2549
|
setInput
|
|
1298
2550
|
]);
|
|
1299
2551
|
(0, import_react2.useEffect)(() => {
|
|
1300
2552
|
if (!ready || status.loading) {
|
|
1301
2553
|
return;
|
|
1302
2554
|
}
|
|
2555
|
+
const plan = pendingPlanRef.current;
|
|
2556
|
+
const hasRegistered = registeredGraphsRef.current.length > 0 || registeredAnimationsRef.current.length > 0 || programControllerIdsRef.current.size > 0;
|
|
2557
|
+
if (plan && !plan.reregisterGraphs && hasRegistered) {
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
1303
2560
|
registerControllers().catch((err) => {
|
|
1304
2561
|
pushError({
|
|
1305
2562
|
message: "Failed to register controllers",
|
|
@@ -1308,7 +2565,7 @@ function VizijRuntimeProviderInner({
|
|
|
1308
2565
|
timestamp: performance.now()
|
|
1309
2566
|
});
|
|
1310
2567
|
});
|
|
1311
|
-
}, [ready, status.loading, registerControllers, pushError]);
|
|
2568
|
+
}, [ready, status.loading, graphUpdateToken, registerControllers, pushError]);
|
|
1312
2569
|
(0, import_react2.useEffect)(() => {
|
|
1313
2570
|
if (!frame) {
|
|
1314
2571
|
return;
|
|
@@ -1319,26 +2576,66 @@ function VizijRuntimeProviderInner({
|
|
|
1319
2576
|
}
|
|
1320
2577
|
const setWorldValues = store.getState().setValues;
|
|
1321
2578
|
const namespaceValue = status.namespace;
|
|
2579
|
+
const currentValues = store.getState().values;
|
|
2580
|
+
const rigInputPathMap = rigInputMapRef.current;
|
|
2581
|
+
const rigPoseControlInputIds = rigPoseControlInputIdsRef.current;
|
|
1322
2582
|
const batched = [];
|
|
1323
2583
|
const namespacedOutputs = namespacedOutputPathsRef.current;
|
|
1324
2584
|
const baseOutputs = baseOutputPathsRef.current;
|
|
1325
2585
|
writes.forEach((write) => {
|
|
1326
2586
|
const path = normalisePath(write.path);
|
|
1327
|
-
|
|
2587
|
+
const basePath = stripNamespace(path, namespaceValue);
|
|
2588
|
+
const isTrackedOutput = namespacedOutputs.has(path) || baseOutputs.has(basePath);
|
|
2589
|
+
if (!isTrackedOutput) {
|
|
1328
2590
|
return;
|
|
1329
2591
|
}
|
|
1330
2592
|
const raw = valueJSONToRaw(write.value);
|
|
1331
2593
|
if (raw === void 0) {
|
|
1332
2594
|
return;
|
|
1333
2595
|
}
|
|
1334
|
-
const
|
|
2596
|
+
const poseControlMatch = /^rig\/[^/]+\/pose\/control\/(.+)$/.exec(
|
|
2597
|
+
basePath
|
|
2598
|
+
);
|
|
2599
|
+
if (poseControlMatch && typeof raw === "number" && Number.isFinite(raw)) {
|
|
2600
|
+
const inputId = (poseControlMatch[1] ?? "").trim();
|
|
2601
|
+
const hasNativePoseControlInput = inputId.length > 0 && rigPoseControlInputIds.has(inputId);
|
|
2602
|
+
const mappedInputPath = inputId.length === 0 ? void 0 : resolvePoseControlInputPath({
|
|
2603
|
+
inputId,
|
|
2604
|
+
basePath,
|
|
2605
|
+
rigInputPathMap,
|
|
2606
|
+
hasNativePoseControlInput
|
|
2607
|
+
});
|
|
2608
|
+
if (mappedInputPath) {
|
|
2609
|
+
const bridgeKey = `${namespaceValue}:${mappedInputPath}`;
|
|
2610
|
+
const previousValue = poseControlBridgeValuesRef.current.get(bridgeKey);
|
|
2611
|
+
if (previousValue === void 0 || Math.abs(previousValue - raw) > POSE_CONTROL_BRIDGE_EPSILON) {
|
|
2612
|
+
poseControlBridgeValuesRef.current.set(bridgeKey, raw);
|
|
2613
|
+
setInput(mappedInputPath, { float: raw });
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
1335
2617
|
const targetPath = baseOutputs.has(basePath) ? basePath : path;
|
|
1336
|
-
|
|
2618
|
+
const currentValue = currentValues.get(
|
|
2619
|
+
(0, import_utils.getLookup)(namespaceValue, targetPath)
|
|
2620
|
+
);
|
|
2621
|
+
const nextWrite = transformOutputWrite ? transformOutputWrite({
|
|
2622
|
+
id: targetPath,
|
|
2623
|
+
namespace: namespaceValue,
|
|
2624
|
+
value: raw,
|
|
2625
|
+
currentValue
|
|
2626
|
+
}) : {
|
|
2627
|
+
id: targetPath,
|
|
2628
|
+
namespace: namespaceValue,
|
|
2629
|
+
value: raw
|
|
2630
|
+
};
|
|
2631
|
+
if (nextWrite) {
|
|
2632
|
+
batched.push(nextWrite);
|
|
2633
|
+
}
|
|
1337
2634
|
});
|
|
1338
2635
|
if (batched.length > 0) {
|
|
1339
2636
|
setWorldValues(batched);
|
|
1340
2637
|
}
|
|
1341
|
-
}, [frame, status.namespace, store]);
|
|
2638
|
+
}, [frame, status.namespace, store, transformOutputWrite]);
|
|
1342
2639
|
const stagePoseNeutral = (0, import_react2.useCallback)(
|
|
1343
2640
|
(force = false) => {
|
|
1344
2641
|
const neutral = assetBundle.pose?.config?.neutralInputs ?? {};
|
|
@@ -1406,88 +2703,237 @@ function VizijRuntimeProviderInner({
|
|
|
1406
2703
|
},
|
|
1407
2704
|
[setInput]
|
|
1408
2705
|
);
|
|
1409
|
-
const
|
|
1410
|
-
(
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
2706
|
+
const resolveClipById = (0, import_react2.useCallback)(
|
|
2707
|
+
(id) => {
|
|
2708
|
+
return assetBundle.animations?.find((anim) => anim.id === id);
|
|
2709
|
+
},
|
|
2710
|
+
[assetBundle.animations]
|
|
2711
|
+
);
|
|
2712
|
+
const resolveClipPromise = (0, import_react2.useCallback)((state) => {
|
|
2713
|
+
state.resolve?.();
|
|
2714
|
+
state.resolve = null;
|
|
2715
|
+
state.completion = null;
|
|
2716
|
+
}, []);
|
|
2717
|
+
const ensureClipPromise = (0, import_react2.useCallback)((state) => {
|
|
2718
|
+
if (state.completion) {
|
|
2719
|
+
return state.completion;
|
|
2720
|
+
}
|
|
2721
|
+
const completion = new Promise((resolve) => {
|
|
2722
|
+
state.resolve = resolve;
|
|
2723
|
+
});
|
|
2724
|
+
state.completion = completion;
|
|
2725
|
+
return completion;
|
|
2726
|
+
}, []);
|
|
2727
|
+
const setAnimationInput = (0, import_react2.useCallback)(
|
|
2728
|
+
(path, value, options) => {
|
|
2729
|
+
setInput(path, { float: value });
|
|
2730
|
+
if (!options?.immediate) {
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
const namespacedPath = namespaceTypedPath(path, namespaceRef.current);
|
|
2734
|
+
const staged = stagedInputsRef.current.get(namespacedPath);
|
|
2735
|
+
if (staged) {
|
|
2736
|
+
orchestratorSetInput(namespacedPath, staged.value, staged.shape);
|
|
2737
|
+
stagedInputsRef.current.delete(namespacedPath);
|
|
2738
|
+
return;
|
|
2739
|
+
}
|
|
2740
|
+
orchestratorSetInput(namespacedPath, { float: value });
|
|
2741
|
+
},
|
|
2742
|
+
[orchestratorSetInput, setInput]
|
|
2743
|
+
);
|
|
2744
|
+
const clearAnimationInput = (0, import_react2.useCallback)(
|
|
2745
|
+
(path) => {
|
|
2746
|
+
const namespacedPath = namespaceTypedPath(path, namespaceRef.current);
|
|
2747
|
+
stagedInputsRef.current.delete(namespacedPath);
|
|
2748
|
+
removeInput(namespacedPath);
|
|
2749
|
+
},
|
|
2750
|
+
[removeInput]
|
|
2751
|
+
);
|
|
2752
|
+
const buildClipOutputValues = (0, import_react2.useCallback)(
|
|
2753
|
+
(clip, state) => sampleAnimationClipOutputValues(
|
|
2754
|
+
clip.clip,
|
|
2755
|
+
state.time,
|
|
2756
|
+
state.weight,
|
|
2757
|
+
faceId ?? void 0,
|
|
2758
|
+
rigInputMapRef.current
|
|
2759
|
+
),
|
|
2760
|
+
[faceId]
|
|
2761
|
+
);
|
|
2762
|
+
const computeClipAggregateValues = (0, import_react2.useCallback)(() => {
|
|
2763
|
+
const aggregate = /* @__PURE__ */ new Map();
|
|
2764
|
+
clipOutputValuesRef.current.forEach((outputValues) => {
|
|
2765
|
+
outputValues.forEach((value, path) => {
|
|
2766
|
+
aggregate.set(path, (aggregate.get(path) ?? 0) + value);
|
|
2767
|
+
});
|
|
2768
|
+
});
|
|
2769
|
+
return aggregate;
|
|
2770
|
+
}, []);
|
|
2771
|
+
const stageClipAggregateValues = (0, import_react2.useCallback)(
|
|
2772
|
+
(nextAggregate, options) => {
|
|
2773
|
+
diffAnimationAggregateValues(
|
|
2774
|
+
clipAggregateValuesRef.current,
|
|
2775
|
+
nextAggregate,
|
|
2776
|
+
POSE_CONTROL_BRIDGE_EPSILON
|
|
2777
|
+
).forEach((operation) => {
|
|
2778
|
+
if (operation.kind === "clear") {
|
|
2779
|
+
clearAnimationInput(operation.path);
|
|
2780
|
+
return;
|
|
1433
2781
|
}
|
|
2782
|
+
setAnimationInput(operation.path, operation.value, options);
|
|
2783
|
+
});
|
|
2784
|
+
clipAggregateValuesRef.current = nextAggregate;
|
|
2785
|
+
},
|
|
2786
|
+
[clearAnimationInput, setAnimationInput]
|
|
2787
|
+
);
|
|
2788
|
+
const syncClipOutputs = (0, import_react2.useCallback)(
|
|
2789
|
+
(options) => {
|
|
2790
|
+
stageClipAggregateValues(computeClipAggregateValues(), options);
|
|
2791
|
+
},
|
|
2792
|
+
[computeClipAggregateValues, stageClipAggregateValues]
|
|
2793
|
+
);
|
|
2794
|
+
const writeClipOutputs = (0, import_react2.useCallback)(
|
|
2795
|
+
(clip, state, options) => {
|
|
2796
|
+
if (!animationSystemActiveRef.current) {
|
|
2797
|
+
return;
|
|
1434
2798
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
2799
|
+
clipOutputValuesRef.current.set(
|
|
2800
|
+
clip.id,
|
|
2801
|
+
buildClipOutputValues(clip, state)
|
|
2802
|
+
);
|
|
2803
|
+
syncClipOutputs(options);
|
|
2804
|
+
},
|
|
2805
|
+
[buildClipOutputValues, syncClipOutputs]
|
|
2806
|
+
);
|
|
2807
|
+
const clearClipOutputs = (0, import_react2.useCallback)(
|
|
2808
|
+
(clipId, options) => {
|
|
2809
|
+
if (!clipOutputValuesRef.current.has(clipId)) {
|
|
2810
|
+
return;
|
|
2811
|
+
}
|
|
2812
|
+
clipOutputValuesRef.current.delete(clipId);
|
|
2813
|
+
syncClipOutputs(options);
|
|
2814
|
+
},
|
|
2815
|
+
[syncClipOutputs]
|
|
2816
|
+
);
|
|
2817
|
+
const createClipPlaybackState = (0, import_react2.useCallback)(
|
|
2818
|
+
(clip) => {
|
|
2819
|
+
const duration = resolveClipDurationSeconds(
|
|
2820
|
+
clip.clip
|
|
2821
|
+
);
|
|
2822
|
+
return {
|
|
2823
|
+
id: clip.id,
|
|
2824
|
+
time: 0,
|
|
2825
|
+
duration,
|
|
2826
|
+
speed: 1,
|
|
2827
|
+
weight: Number.isFinite(clip.weight) ? Number(clip.weight) : 1,
|
|
2828
|
+
loop: false,
|
|
2829
|
+
playing: false,
|
|
2830
|
+
resolve: null,
|
|
2831
|
+
completion: null
|
|
2832
|
+
};
|
|
1437
2833
|
},
|
|
1438
2834
|
[]
|
|
1439
2835
|
);
|
|
2836
|
+
const ensureClipPlaybackState = (0, import_react2.useCallback)(
|
|
2837
|
+
(id) => {
|
|
2838
|
+
const clip = resolveClipById(id);
|
|
2839
|
+
if (!clip) {
|
|
2840
|
+
return null;
|
|
2841
|
+
}
|
|
2842
|
+
const existing = clipPlaybackRef.current.get(id);
|
|
2843
|
+
if (existing) {
|
|
2844
|
+
existing.duration = resolveClipDurationSeconds(
|
|
2845
|
+
clip.clip,
|
|
2846
|
+
existing.duration
|
|
2847
|
+
);
|
|
2848
|
+
existing.time = clampAnimationTime(existing.time, existing.duration);
|
|
2849
|
+
return { clip, state: existing };
|
|
2850
|
+
}
|
|
2851
|
+
const next = createClipPlaybackState(clip);
|
|
2852
|
+
clipPlaybackRef.current.set(id, next);
|
|
2853
|
+
return { clip, state: next };
|
|
2854
|
+
},
|
|
2855
|
+
[createClipPlaybackState, resolveClipById]
|
|
2856
|
+
);
|
|
1440
2857
|
const advanceClipPlayback = (0, import_react2.useCallback)(
|
|
1441
2858
|
(dt) => {
|
|
1442
2859
|
if (clipPlaybackRef.current.size === 0) {
|
|
1443
2860
|
return;
|
|
1444
2861
|
}
|
|
1445
|
-
const map = clipPlaybackRef.current;
|
|
1446
2862
|
const toDelete = [];
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
const clip = assetBundle.animations?.find(
|
|
1450
|
-
(anim) => anim.id === state.id
|
|
1451
|
-
);
|
|
2863
|
+
clipPlaybackRef.current.forEach((state, key) => {
|
|
2864
|
+
const clip = resolveClipById(state.id);
|
|
1452
2865
|
if (!clip) {
|
|
1453
2866
|
toDelete.push(key);
|
|
1454
|
-
state
|
|
2867
|
+
resolveClipPromise(state);
|
|
1455
2868
|
return;
|
|
1456
2869
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
2870
|
+
state.duration = resolveClipDurationSeconds(
|
|
2871
|
+
clip.clip,
|
|
2872
|
+
state.duration
|
|
2873
|
+
);
|
|
2874
|
+
const { time, completed } = advanceClipTime(
|
|
2875
|
+
{
|
|
2876
|
+
time: state.time,
|
|
2877
|
+
duration: state.duration,
|
|
2878
|
+
speed: state.speed,
|
|
2879
|
+
loop: state.loop,
|
|
2880
|
+
playing: state.playing
|
|
2881
|
+
},
|
|
2882
|
+
dt
|
|
2883
|
+
);
|
|
2884
|
+
state.time = clampAnimationTime(time, state.duration);
|
|
2885
|
+
if (state.playing || completed) {
|
|
2886
|
+
writeClipOutputs(clip, state);
|
|
1463
2887
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
const path = `animation/${clip.id}/${track.channel}`;
|
|
1468
|
-
setInput(path, { float: value });
|
|
1469
|
-
});
|
|
1470
|
-
if (toDelete.includes(key)) {
|
|
1471
|
-
state.resolve();
|
|
2888
|
+
if (completed) {
|
|
2889
|
+
toDelete.push(key);
|
|
2890
|
+
resolveClipPromise(state);
|
|
1472
2891
|
}
|
|
1473
2892
|
});
|
|
1474
2893
|
toDelete.forEach((key) => {
|
|
1475
2894
|
clipPlaybackRef.current.delete(key);
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
const clipData = clip.clip;
|
|
1479
|
-
const tracks = Array.isArray(clipData?.tracks) ? clipData.tracks : [];
|
|
1480
|
-
tracks.forEach((track) => {
|
|
1481
|
-
const path = `animation/${clip.id}/${track.channel}`;
|
|
1482
|
-
setInput(path, { float: 0 });
|
|
1483
|
-
});
|
|
2895
|
+
if (animationSystemActiveRef.current) {
|
|
2896
|
+
clearClipOutputs(key);
|
|
1484
2897
|
}
|
|
1485
2898
|
});
|
|
1486
2899
|
},
|
|
1487
|
-
[
|
|
2900
|
+
[clearClipOutputs, resolveClipById, resolveClipPromise, writeClipOutputs]
|
|
1488
2901
|
);
|
|
1489
2902
|
const animateValue = (0, import_react2.useCallback)(
|
|
1490
2903
|
(path, target, options) => {
|
|
2904
|
+
const targetValue = (0, import_value_json2.valueAsNumber)(target);
|
|
2905
|
+
const basePath = stripNamespace(
|
|
2906
|
+
normalisePath(path),
|
|
2907
|
+
namespaceRef.current
|
|
2908
|
+
);
|
|
2909
|
+
const poseValues = useLegacyPoseWeightFallback && targetValue != null ? poseWeightFallbackMap.get(basePath) : void 0;
|
|
2910
|
+
if (poseValues && targetValue != null) {
|
|
2911
|
+
const poseFaceId = assetBundle.pose?.config?.faceId ?? faceId ?? "face";
|
|
2912
|
+
const rigMap = rigInputMapRef.current;
|
|
2913
|
+
return Promise.all(
|
|
2914
|
+
Object.entries(poseValues).flatMap(([inputId, poseValue]) => {
|
|
2915
|
+
if (!Number.isFinite(poseValue)) {
|
|
2916
|
+
return [];
|
|
2917
|
+
}
|
|
2918
|
+
const controlPath = resolvePoseControlInputPath({
|
|
2919
|
+
inputId,
|
|
2920
|
+
basePath: buildRigInputPath(
|
|
2921
|
+
poseFaceId,
|
|
2922
|
+
`/pose/control/${inputId}`
|
|
2923
|
+
),
|
|
2924
|
+
rigInputPathMap: rigMap,
|
|
2925
|
+
hasNativePoseControlInput: true
|
|
2926
|
+
}) ?? buildRigInputPath(poseFaceId, `/pose/control/${inputId}`);
|
|
2927
|
+
return [
|
|
2928
|
+
animateValue(
|
|
2929
|
+
controlPath,
|
|
2930
|
+
{ float: Number(poseValue) * targetValue },
|
|
2931
|
+
options
|
|
2932
|
+
)
|
|
2933
|
+
];
|
|
2934
|
+
})
|
|
2935
|
+
).then(() => void 0);
|
|
2936
|
+
}
|
|
1491
2937
|
const easing = resolveEasing(options?.easing);
|
|
1492
2938
|
const duration = Math.max(0, options?.duration ?? DEFAULT_DURATION);
|
|
1493
2939
|
cancelAnimation(path);
|
|
@@ -1501,7 +2947,9 @@ function VizijRuntimeProviderInner({
|
|
|
1501
2947
|
}
|
|
1502
2948
|
return new Promise((resolve) => {
|
|
1503
2949
|
animationTweensRef.current.set(path, {
|
|
1504
|
-
path
|
|
2950
|
+
// Keep the raw path here so tween updates go through setInput() once
|
|
2951
|
+
// and pick up the active namespace exactly once.
|
|
2952
|
+
path,
|
|
1505
2953
|
from: fromValue,
|
|
1506
2954
|
to: toValue,
|
|
1507
2955
|
duration,
|
|
@@ -1512,54 +2960,377 @@ function VizijRuntimeProviderInner({
|
|
|
1512
2960
|
markActivity();
|
|
1513
2961
|
});
|
|
1514
2962
|
},
|
|
1515
|
-
[
|
|
2963
|
+
[
|
|
2964
|
+
assetBundle.pose?.config?.faceId,
|
|
2965
|
+
cancelAnimation,
|
|
2966
|
+
faceId,
|
|
2967
|
+
getPathSnapshot,
|
|
2968
|
+
markActivity,
|
|
2969
|
+
poseWeightFallbackMap,
|
|
2970
|
+
setInput,
|
|
2971
|
+
useLegacyPoseWeightFallback
|
|
2972
|
+
]
|
|
1516
2973
|
);
|
|
1517
2974
|
const playAnimation = (0, import_react2.useCallback)(
|
|
1518
2975
|
(id, options) => {
|
|
1519
|
-
const
|
|
1520
|
-
if (!
|
|
2976
|
+
const ensured = ensureClipPlaybackState(id);
|
|
2977
|
+
if (!ensured) {
|
|
1521
2978
|
return Promise.reject(
|
|
1522
2979
|
new Error(`Animation ${id} is not part of the current asset bundle.`)
|
|
1523
2980
|
);
|
|
1524
2981
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
2982
|
+
const { clip, state } = ensured;
|
|
2983
|
+
const shouldReset = options?.reset === true;
|
|
2984
|
+
if (shouldReset) {
|
|
2985
|
+
resolveClipPromise(state);
|
|
2986
|
+
state.time = 0;
|
|
1527
2987
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
2988
|
+
const speed = options?.speed ?? state.speed ?? 1;
|
|
2989
|
+
const weight = options?.weight ?? state.weight ?? clip.weight ?? 1;
|
|
2990
|
+
state.speed = Number.isFinite(speed) && speed > 0 ? speed : 1;
|
|
2991
|
+
state.weight = Number.isFinite(weight) ? Number(weight) : 1;
|
|
2992
|
+
state.duration = resolveClipDurationSeconds(
|
|
2993
|
+
clip.clip,
|
|
2994
|
+
state.duration
|
|
2995
|
+
);
|
|
2996
|
+
state.time = clampAnimationTime(state.time, state.duration);
|
|
2997
|
+
state.playing = true;
|
|
2998
|
+
const completion = ensureClipPromise(state);
|
|
2999
|
+
clipPlaybackRef.current.set(id, state);
|
|
3000
|
+
writeClipOutputs(clip, state);
|
|
3001
|
+
markActivity();
|
|
3002
|
+
return completion;
|
|
1542
3003
|
},
|
|
1543
|
-
[
|
|
3004
|
+
[
|
|
3005
|
+
ensureClipPlaybackState,
|
|
3006
|
+
ensureClipPromise,
|
|
3007
|
+
markActivity,
|
|
3008
|
+
resolveClipPromise,
|
|
3009
|
+
writeClipOutputs
|
|
3010
|
+
]
|
|
1544
3011
|
);
|
|
1545
|
-
const
|
|
3012
|
+
const pauseAnimation = (0, import_react2.useCallback)(
|
|
1546
3013
|
(id) => {
|
|
1547
|
-
const
|
|
3014
|
+
const state = clipPlaybackRef.current.get(id);
|
|
3015
|
+
if (!state || !state.playing) {
|
|
3016
|
+
return;
|
|
3017
|
+
}
|
|
3018
|
+
state.playing = false;
|
|
3019
|
+
updateLoopMode();
|
|
3020
|
+
},
|
|
3021
|
+
[updateLoopMode]
|
|
3022
|
+
);
|
|
3023
|
+
const seekAnimation = (0, import_react2.useCallback)(
|
|
3024
|
+
(id, timeSeconds) => {
|
|
3025
|
+
const ensured = ensureClipPlaybackState(id);
|
|
3026
|
+
if (!ensured) {
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
3029
|
+
const { clip, state } = ensured;
|
|
3030
|
+
state.time = clampAnimationTime(timeSeconds, state.duration);
|
|
3031
|
+
clipPlaybackRef.current.set(id, state);
|
|
3032
|
+
writeClipOutputs(clip, state, { immediate: true });
|
|
3033
|
+
},
|
|
3034
|
+
[ensureClipPlaybackState, writeClipOutputs]
|
|
3035
|
+
);
|
|
3036
|
+
const setAnimationLoop = (0, import_react2.useCallback)(
|
|
3037
|
+
(id, enabled) => {
|
|
3038
|
+
const ensured = ensureClipPlaybackState(id);
|
|
3039
|
+
if (!ensured) {
|
|
3040
|
+
return;
|
|
3041
|
+
}
|
|
3042
|
+
ensured.state.loop = Boolean(enabled);
|
|
3043
|
+
clipPlaybackRef.current.set(id, ensured.state);
|
|
3044
|
+
updateLoopMode();
|
|
3045
|
+
},
|
|
3046
|
+
[ensureClipPlaybackState, updateLoopMode]
|
|
3047
|
+
);
|
|
3048
|
+
const getAnimationState = (0, import_react2.useCallback)(
|
|
3049
|
+
(id) => {
|
|
3050
|
+
const state = clipPlaybackRef.current.get(id);
|
|
3051
|
+
if (!state) {
|
|
3052
|
+
return null;
|
|
3053
|
+
}
|
|
3054
|
+
return {
|
|
3055
|
+
time: state.time,
|
|
3056
|
+
duration: state.duration,
|
|
3057
|
+
playing: state.playing,
|
|
3058
|
+
loop: state.loop,
|
|
3059
|
+
speed: state.speed
|
|
3060
|
+
};
|
|
3061
|
+
},
|
|
3062
|
+
[]
|
|
3063
|
+
);
|
|
3064
|
+
const stopAnimation = (0, import_react2.useCallback)(
|
|
3065
|
+
(id, options) => {
|
|
1548
3066
|
const state = clipPlaybackRef.current.get(id);
|
|
1549
3067
|
if (state) {
|
|
1550
3068
|
clipPlaybackRef.current.delete(id);
|
|
1551
|
-
state.
|
|
3069
|
+
state.playing = false;
|
|
3070
|
+
resolveClipPromise(state);
|
|
1552
3071
|
}
|
|
1553
|
-
if (
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
3072
|
+
if (options?.clearOutputs !== false) {
|
|
3073
|
+
clearClipOutputs(id);
|
|
3074
|
+
}
|
|
3075
|
+
updateLoopMode();
|
|
3076
|
+
},
|
|
3077
|
+
[clearClipOutputs, resolveClipPromise, updateLoopMode]
|
|
3078
|
+
);
|
|
3079
|
+
const refreshControllerStatus = (0, import_react2.useCallback)(() => {
|
|
3080
|
+
const controllers = listControllers();
|
|
3081
|
+
reportStatus((prev) => ({
|
|
3082
|
+
...prev,
|
|
3083
|
+
controllers,
|
|
3084
|
+
outputPaths: Array.from(outputPathsRef.current)
|
|
3085
|
+
}));
|
|
3086
|
+
onRegisterControllers?.(controllers);
|
|
3087
|
+
}, [listControllers, onRegisterControllers, reportStatus]);
|
|
3088
|
+
const resolveProgramById = (0, import_react2.useCallback)(
|
|
3089
|
+
(id) => {
|
|
3090
|
+
return resolvedProgramAssets.find((program) => program.id === id);
|
|
3091
|
+
},
|
|
3092
|
+
[resolvedProgramAssets]
|
|
3093
|
+
);
|
|
3094
|
+
const buildProgramRegistrationConfig = (0, import_react2.useCallback)(
|
|
3095
|
+
(program) => {
|
|
3096
|
+
const graphSpec = resolveGraphSpec(
|
|
3097
|
+
program.graph,
|
|
3098
|
+
`${program.id ?? "program"} graph`
|
|
3099
|
+
);
|
|
3100
|
+
if (!graphSpec) {
|
|
3101
|
+
return null;
|
|
3102
|
+
}
|
|
3103
|
+
const outputs = collectOutputPaths(graphSpec);
|
|
3104
|
+
const inputs = collectInputPaths(graphSpec);
|
|
3105
|
+
const subs = program.graph.subscriptions ?? {
|
|
3106
|
+
inputs,
|
|
3107
|
+
outputs
|
|
3108
|
+
};
|
|
3109
|
+
return {
|
|
3110
|
+
id: namespaceControllerId(program.id, namespace, "graph"),
|
|
3111
|
+
spec: stripNulls(namespaceGraphSpec(graphSpec, namespace)),
|
|
3112
|
+
subs: namespaceSubscriptions(subs, namespace)
|
|
3113
|
+
};
|
|
3114
|
+
},
|
|
3115
|
+
[namespace]
|
|
3116
|
+
);
|
|
3117
|
+
const deriveProgramResetValues = (0, import_react2.useCallback)(
|
|
3118
|
+
(program) => {
|
|
3119
|
+
if (program.resetValues) {
|
|
3120
|
+
return Object.entries(program.resetValues).filter(([, value]) => Number.isFinite(value)).map(([path, value]) => ({ path, value }));
|
|
3121
|
+
}
|
|
3122
|
+
const graphSpec = resolveGraphSpec(
|
|
3123
|
+
program.graph,
|
|
3124
|
+
`${program.id ?? "program"} graph (reset)`
|
|
3125
|
+
);
|
|
3126
|
+
if (!graphSpec) {
|
|
3127
|
+
return [];
|
|
3128
|
+
}
|
|
3129
|
+
return collectOutputPaths(graphSpec).filter((path) => path.trim().length > 0).map((path) => {
|
|
3130
|
+
const defaultValue = inputConstraintsRef.current[path]?.defaultValue ?? 0;
|
|
3131
|
+
return {
|
|
3132
|
+
path,
|
|
3133
|
+
value: Number.isFinite(defaultValue) && defaultValue != null ? defaultValue : 0
|
|
3134
|
+
};
|
|
3135
|
+
});
|
|
3136
|
+
},
|
|
3137
|
+
[]
|
|
3138
|
+
);
|
|
3139
|
+
const syncProgramPlaybackControllers = (0, import_react2.useCallback)(() => {
|
|
3140
|
+
if (!ready) {
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3143
|
+
const availableProgramIds = new Set(
|
|
3144
|
+
resolvedProgramAssets.map((program) => program.id)
|
|
3145
|
+
);
|
|
3146
|
+
Array.from(programPlaybackRef.current.keys()).forEach((id) => {
|
|
3147
|
+
if (availableProgramIds.has(id)) {
|
|
3148
|
+
return;
|
|
3149
|
+
}
|
|
3150
|
+
programPlaybackRef.current.delete(id);
|
|
3151
|
+
const controllerId = programControllerIdsRef.current.get(id);
|
|
3152
|
+
if (controllerId) {
|
|
3153
|
+
try {
|
|
3154
|
+
removeGraph(controllerId);
|
|
3155
|
+
} catch (err) {
|
|
3156
|
+
pushError({
|
|
3157
|
+
message: `Failed to remove program ${id}`,
|
|
3158
|
+
cause: err,
|
|
3159
|
+
phase: "registration",
|
|
3160
|
+
timestamp: performance.now()
|
|
3161
|
+
});
|
|
3162
|
+
}
|
|
3163
|
+
programControllerIdsRef.current.delete(id);
|
|
3164
|
+
}
|
|
3165
|
+
});
|
|
3166
|
+
programPlaybackRef.current.forEach((state, id) => {
|
|
3167
|
+
const program = resolveProgramById(id);
|
|
3168
|
+
const controllerId = programControllerIdsRef.current.get(id);
|
|
3169
|
+
if (!program) {
|
|
3170
|
+
return;
|
|
3171
|
+
}
|
|
3172
|
+
if (state.state !== "playing") {
|
|
3173
|
+
if (!controllerId) {
|
|
3174
|
+
return;
|
|
3175
|
+
}
|
|
3176
|
+
try {
|
|
3177
|
+
removeGraph(controllerId);
|
|
3178
|
+
} catch (err) {
|
|
3179
|
+
pushError({
|
|
3180
|
+
message: `Failed to pause program ${id}`,
|
|
3181
|
+
cause: err,
|
|
3182
|
+
phase: "registration",
|
|
3183
|
+
timestamp: performance.now()
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
programControllerIdsRef.current.delete(id);
|
|
3187
|
+
return;
|
|
3188
|
+
}
|
|
3189
|
+
if (controllerId) {
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
const config = buildProgramRegistrationConfig(program);
|
|
3193
|
+
if (!config) {
|
|
3194
|
+
pushError({
|
|
3195
|
+
message: `Program ${id} is missing a usable graph payload.`,
|
|
3196
|
+
phase: "registration",
|
|
3197
|
+
timestamp: performance.now()
|
|
3198
|
+
});
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
3201
|
+
try {
|
|
3202
|
+
const nextControllerId = registerGraph(config);
|
|
3203
|
+
programControllerIdsRef.current.set(id, nextControllerId);
|
|
3204
|
+
} catch (err) {
|
|
3205
|
+
pushError({
|
|
3206
|
+
message: `Failed to register program ${id}`,
|
|
3207
|
+
cause: err,
|
|
3208
|
+
phase: "registration",
|
|
3209
|
+
timestamp: performance.now()
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3212
|
+
});
|
|
3213
|
+
refreshControllerStatus();
|
|
3214
|
+
}, [
|
|
3215
|
+
buildProgramRegistrationConfig,
|
|
3216
|
+
pushError,
|
|
3217
|
+
ready,
|
|
3218
|
+
refreshControllerStatus,
|
|
3219
|
+
registerGraph,
|
|
3220
|
+
removeGraph,
|
|
3221
|
+
resolvedProgramAssets,
|
|
3222
|
+
resolveProgramById
|
|
3223
|
+
]);
|
|
3224
|
+
const playProgram = (0, import_react2.useCallback)(
|
|
3225
|
+
(id) => {
|
|
3226
|
+
if (!resolveProgramById(id)) {
|
|
3227
|
+
throw new Error(
|
|
3228
|
+
`Program ${id} is not part of the current asset bundle.`
|
|
3229
|
+
);
|
|
3230
|
+
}
|
|
3231
|
+
programPlaybackRef.current.set(id, {
|
|
3232
|
+
id,
|
|
3233
|
+
state: "playing"
|
|
3234
|
+
});
|
|
3235
|
+
syncProgramPlaybackControllers();
|
|
3236
|
+
markActivity();
|
|
3237
|
+
},
|
|
3238
|
+
[markActivity, resolveProgramById, syncProgramPlaybackControllers]
|
|
3239
|
+
);
|
|
3240
|
+
const pauseProgram = (0, import_react2.useCallback)(
|
|
3241
|
+
(id) => {
|
|
3242
|
+
if (!resolveProgramById(id)) {
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
programPlaybackRef.current.set(id, {
|
|
3246
|
+
id,
|
|
3247
|
+
state: "paused"
|
|
3248
|
+
});
|
|
3249
|
+
syncProgramPlaybackControllers();
|
|
3250
|
+
updateLoopMode();
|
|
3251
|
+
},
|
|
3252
|
+
[resolveProgramById, syncProgramPlaybackControllers, updateLoopMode]
|
|
3253
|
+
);
|
|
3254
|
+
const stopProgram = (0, import_react2.useCallback)(
|
|
3255
|
+
(id, options) => {
|
|
3256
|
+
const program = resolveProgramById(id);
|
|
3257
|
+
const controllerId = programControllerIdsRef.current.get(id);
|
|
3258
|
+
if (controllerId) {
|
|
3259
|
+
try {
|
|
3260
|
+
removeGraph(controllerId);
|
|
3261
|
+
} catch (err) {
|
|
3262
|
+
pushError({
|
|
3263
|
+
message: `Failed to stop program ${id}`,
|
|
3264
|
+
cause: err,
|
|
3265
|
+
phase: "registration",
|
|
3266
|
+
timestamp: performance.now()
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3269
|
+
programControllerIdsRef.current.delete(id);
|
|
3270
|
+
}
|
|
3271
|
+
programPlaybackRef.current.set(id, {
|
|
3272
|
+
id,
|
|
3273
|
+
state: "stopped"
|
|
3274
|
+
});
|
|
3275
|
+
if (program && options?.resetOutputs !== false) {
|
|
3276
|
+
deriveProgramResetValues(program).forEach(({ path, value }) => {
|
|
3277
|
+
setInput(path, { float: value });
|
|
1559
3278
|
});
|
|
1560
3279
|
}
|
|
3280
|
+
refreshControllerStatus();
|
|
3281
|
+
updateLoopMode();
|
|
3282
|
+
},
|
|
3283
|
+
[
|
|
3284
|
+
deriveProgramResetValues,
|
|
3285
|
+
pushError,
|
|
3286
|
+
refreshControllerStatus,
|
|
3287
|
+
removeGraph,
|
|
3288
|
+
resolveProgramById,
|
|
3289
|
+
setInput,
|
|
3290
|
+
updateLoopMode
|
|
3291
|
+
]
|
|
3292
|
+
);
|
|
3293
|
+
const getProgramState = (0, import_react2.useCallback)(
|
|
3294
|
+
(id) => {
|
|
3295
|
+
const state = programPlaybackRef.current.get(id);
|
|
3296
|
+
if (!state) {
|
|
3297
|
+
return null;
|
|
3298
|
+
}
|
|
3299
|
+
return { state: state.state };
|
|
1561
3300
|
},
|
|
1562
|
-
[
|
|
3301
|
+
[]
|
|
3302
|
+
);
|
|
3303
|
+
(0, import_react2.useEffect)(() => {
|
|
3304
|
+
if (!ready || status.loading) {
|
|
3305
|
+
return;
|
|
3306
|
+
}
|
|
3307
|
+
syncProgramPlaybackControllers();
|
|
3308
|
+
}, [
|
|
3309
|
+
graphUpdateToken,
|
|
3310
|
+
ready,
|
|
3311
|
+
resolvedProgramAssets,
|
|
3312
|
+
status.loading,
|
|
3313
|
+
syncProgramPlaybackControllers
|
|
3314
|
+
]);
|
|
3315
|
+
const setAnimationActive = (0, import_react2.useCallback)(
|
|
3316
|
+
(active) => {
|
|
3317
|
+
const next = Boolean(active);
|
|
3318
|
+
if (animationSystemActiveRef.current === next) {
|
|
3319
|
+
return;
|
|
3320
|
+
}
|
|
3321
|
+
animationSystemActiveRef.current = next;
|
|
3322
|
+
if (!next) {
|
|
3323
|
+
clipPlaybackRef.current.forEach((state) => {
|
|
3324
|
+
state.playing = false;
|
|
3325
|
+
});
|
|
3326
|
+
}
|
|
3327
|
+
updateLoopMode();
|
|
3328
|
+
},
|
|
3329
|
+
[updateLoopMode]
|
|
3330
|
+
);
|
|
3331
|
+
const isAnimationActive = (0, import_react2.useCallback)(
|
|
3332
|
+
() => animationSystemActiveRef.current,
|
|
3333
|
+
[]
|
|
1563
3334
|
);
|
|
1564
3335
|
const registerInputDriver = (0, import_react2.useCallback)(
|
|
1565
3336
|
(id, factory) => {
|
|
@@ -1700,6 +3471,10 @@ function VizijRuntimeProviderInner({
|
|
|
1700
3471
|
return () => {
|
|
1701
3472
|
animationTweensRef.current.clear();
|
|
1702
3473
|
clipPlaybackRef.current.clear();
|
|
3474
|
+
programPlaybackRef.current.clear();
|
|
3475
|
+
programControllerIdsRef.current.clear();
|
|
3476
|
+
clipOutputValuesRef.current.clear();
|
|
3477
|
+
clipAggregateValuesRef.current.clear();
|
|
1703
3478
|
};
|
|
1704
3479
|
}, []);
|
|
1705
3480
|
(0, import_react2.useEffect)(() => {
|
|
@@ -1715,18 +3490,61 @@ function VizijRuntimeProviderInner({
|
|
|
1715
3490
|
}, 500);
|
|
1716
3491
|
return () => window.clearInterval(id);
|
|
1717
3492
|
}, [reportStatus]);
|
|
3493
|
+
const setGraphBundle = (0, import_react2.useCallback)(
|
|
3494
|
+
(bundle, options) => {
|
|
3495
|
+
const baseAssetBundle = latestEffectiveAssetBundleRef.current;
|
|
3496
|
+
const nextAssetBundle = applyRuntimeGraphBundle(baseAssetBundle, bundle);
|
|
3497
|
+
const plan = resolveRuntimeUpdatePlan(
|
|
3498
|
+
baseAssetBundle,
|
|
3499
|
+
nextAssetBundle,
|
|
3500
|
+
options?.tier ?? updateTierRef.current
|
|
3501
|
+
);
|
|
3502
|
+
pendingPlanRef.current = plan;
|
|
3503
|
+
previousBundleRef.current = nextAssetBundle;
|
|
3504
|
+
latestEffectiveAssetBundleRef.current = nextAssetBundle;
|
|
3505
|
+
suppressNextBundlePlanRef.current = true;
|
|
3506
|
+
setAssetBundleOverride(nextAssetBundle);
|
|
3507
|
+
if (plan.reregisterGraphs) {
|
|
3508
|
+
setGraphUpdateToken((prev) => prev + 1);
|
|
3509
|
+
}
|
|
3510
|
+
if (plan.reloadAssets) {
|
|
3511
|
+
reportStatus((prev) => ({
|
|
3512
|
+
...prev,
|
|
3513
|
+
loading: true,
|
|
3514
|
+
ready: false
|
|
3515
|
+
}));
|
|
3516
|
+
} else {
|
|
3517
|
+
reportStatus((prev) => ({
|
|
3518
|
+
...prev,
|
|
3519
|
+
loading: false
|
|
3520
|
+
}));
|
|
3521
|
+
}
|
|
3522
|
+
},
|
|
3523
|
+
[reportStatus]
|
|
3524
|
+
);
|
|
1718
3525
|
const contextValue = (0, import_react2.useMemo)(
|
|
1719
3526
|
() => ({
|
|
1720
3527
|
...status,
|
|
1721
3528
|
assetBundle,
|
|
1722
3529
|
setInput,
|
|
3530
|
+
setGraphBundle,
|
|
1723
3531
|
setValue: setRendererValue,
|
|
1724
3532
|
stagePoseNeutral,
|
|
1725
3533
|
animateValue,
|
|
1726
3534
|
cancelAnimation,
|
|
1727
3535
|
registerInputDriver,
|
|
1728
3536
|
playAnimation,
|
|
3537
|
+
pauseAnimation,
|
|
3538
|
+
seekAnimation,
|
|
3539
|
+
setAnimationLoop,
|
|
3540
|
+
getAnimationState,
|
|
1729
3541
|
stopAnimation,
|
|
3542
|
+
playProgram,
|
|
3543
|
+
pauseProgram,
|
|
3544
|
+
stopProgram,
|
|
3545
|
+
getProgramState,
|
|
3546
|
+
setAnimationActive,
|
|
3547
|
+
isAnimationActive,
|
|
1730
3548
|
step,
|
|
1731
3549
|
advanceAnimations,
|
|
1732
3550
|
inputConstraints
|
|
@@ -1735,13 +3553,24 @@ function VizijRuntimeProviderInner({
|
|
|
1735
3553
|
status,
|
|
1736
3554
|
assetBundle,
|
|
1737
3555
|
setInput,
|
|
3556
|
+
setGraphBundle,
|
|
1738
3557
|
setRendererValue,
|
|
1739
3558
|
stagePoseNeutral,
|
|
1740
3559
|
animateValue,
|
|
1741
3560
|
cancelAnimation,
|
|
1742
3561
|
registerInputDriver,
|
|
1743
3562
|
playAnimation,
|
|
3563
|
+
pauseAnimation,
|
|
3564
|
+
seekAnimation,
|
|
3565
|
+
setAnimationLoop,
|
|
3566
|
+
getAnimationState,
|
|
1744
3567
|
stopAnimation,
|
|
3568
|
+
playProgram,
|
|
3569
|
+
pauseProgram,
|
|
3570
|
+
stopProgram,
|
|
3571
|
+
getProgramState,
|
|
3572
|
+
setAnimationActive,
|
|
3573
|
+
isAnimationActive,
|
|
1745
3574
|
step,
|
|
1746
3575
|
advanceAnimations,
|
|
1747
3576
|
inputConstraints
|
|
@@ -1787,15 +3616,21 @@ function VizijRuntimeFaceInner({
|
|
|
1787
3616
|
}
|
|
1788
3617
|
var VizijRuntimeFace = (0, import_react4.memo)(VizijRuntimeFaceInner);
|
|
1789
3618
|
|
|
3619
|
+
// src/hooks/useOptionalVizijRuntime.ts
|
|
3620
|
+
var import_react5 = require("react");
|
|
3621
|
+
function useOptionalVizijRuntime() {
|
|
3622
|
+
return (0, import_react5.useContext)(VizijRuntimeContext);
|
|
3623
|
+
}
|
|
3624
|
+
|
|
1790
3625
|
// src/hooks/useVizijOutputs.ts
|
|
1791
3626
|
var import_render3 = require("@vizij/render");
|
|
1792
|
-
var
|
|
3627
|
+
var import_utils2 = require("@vizij/utils");
|
|
1793
3628
|
function useVizijOutputs(paths) {
|
|
1794
3629
|
const { namespace } = useVizijRuntime();
|
|
1795
3630
|
return (0, import_render3.useVizijStore)((state) => {
|
|
1796
3631
|
const result = {};
|
|
1797
3632
|
paths.forEach((path) => {
|
|
1798
|
-
const lookup = (0,
|
|
3633
|
+
const lookup = (0, import_utils2.getLookup)(namespace, path);
|
|
1799
3634
|
result[path] = state.values.get(lookup);
|
|
1800
3635
|
});
|
|
1801
3636
|
return result;
|
|
@@ -1803,15 +3638,15 @@ function useVizijOutputs(paths) {
|
|
|
1803
3638
|
}
|
|
1804
3639
|
|
|
1805
3640
|
// src/hooks/useRigInput.ts
|
|
1806
|
-
var
|
|
3641
|
+
var import_react6 = require("react");
|
|
1807
3642
|
var import_render4 = require("@vizij/render");
|
|
1808
|
-
var
|
|
3643
|
+
var import_utils3 = require("@vizij/utils");
|
|
1809
3644
|
function useRigInput(path) {
|
|
1810
3645
|
const { namespace, setInput } = useVizijRuntime();
|
|
1811
3646
|
const value = (0, import_render4.useVizijStore)((state) => {
|
|
1812
|
-
return state.values.get((0,
|
|
3647
|
+
return state.values.get((0, import_utils3.getLookup)(namespace, path));
|
|
1813
3648
|
});
|
|
1814
|
-
const setter = (0,
|
|
3649
|
+
const setter = (0, import_react6.useCallback)(
|
|
1815
3650
|
(next, shape) => {
|
|
1816
3651
|
setInput(path, next, shape);
|
|
1817
3652
|
},
|
|
@@ -1819,10 +3654,423 @@ function useRigInput(path) {
|
|
|
1819
3654
|
);
|
|
1820
3655
|
return [value, setter];
|
|
1821
3656
|
}
|
|
3657
|
+
|
|
3658
|
+
// src/utils/faceControls.ts
|
|
3659
|
+
var DEFAULT_GAZE_CONTROL = {
|
|
3660
|
+
min: -1,
|
|
3661
|
+
max: 1,
|
|
3662
|
+
defaultValue: 0
|
|
3663
|
+
};
|
|
3664
|
+
var DEFAULT_BLINK_CONTROL = {
|
|
3665
|
+
min: 0,
|
|
3666
|
+
max: 1,
|
|
3667
|
+
defaultValue: 0
|
|
3668
|
+
};
|
|
3669
|
+
var STANDARD_VIZIJ_EYE_PATHS = {
|
|
3670
|
+
leftX: "/standard/vizij/left_eye/pos/x",
|
|
3671
|
+
leftY: "/standard/vizij/left_eye/pos/y",
|
|
3672
|
+
rightX: "/standard/vizij/right_eye/pos/x",
|
|
3673
|
+
rightY: "/standard/vizij/right_eye/pos/y",
|
|
3674
|
+
leftUpper: "/standard/vizij/left_eye_top_eyelid/pos/y",
|
|
3675
|
+
rightUpper: "/standard/vizij/right_eye_top_eyelid/pos/y"
|
|
3676
|
+
};
|
|
3677
|
+
var LEGACY_STANDARD_EYE_PATHS = {
|
|
3678
|
+
leftX: "/standard/left_eye/pos/x",
|
|
3679
|
+
leftY: "/standard/left_eye/pos/y",
|
|
3680
|
+
rightX: "/standard/right_eye/pos/x",
|
|
3681
|
+
rightY: "/standard/right_eye/pos/y",
|
|
3682
|
+
leftUpper: "/standard/left_eye_top_eyelid/pos/y",
|
|
3683
|
+
rightUpper: "/standard/right_eye_top_eyelid/pos/y"
|
|
3684
|
+
};
|
|
3685
|
+
var PROPSRIG_EYE_PATHS = {
|
|
3686
|
+
leftX: "/propsrig/l_eye/translation/x",
|
|
3687
|
+
leftY: "/propsrig/l_eye/translation/y",
|
|
3688
|
+
rightX: "/propsrig/r_eye/translation/x",
|
|
3689
|
+
rightY: "/propsrig/r_eye/translation/y"
|
|
3690
|
+
};
|
|
3691
|
+
function clamp(value, min, max) {
|
|
3692
|
+
return Math.min(Math.max(value, min), max);
|
|
3693
|
+
}
|
|
3694
|
+
function normalizeInputPath(path) {
|
|
3695
|
+
const trimmed = path?.trim() ?? "";
|
|
3696
|
+
if (!trimmed) {
|
|
3697
|
+
return null;
|
|
3698
|
+
}
|
|
3699
|
+
if (trimmed.startsWith("/")) {
|
|
3700
|
+
return trimmed;
|
|
3701
|
+
}
|
|
3702
|
+
const rigMatch = trimmed.match(/^rig\/[^/]+(\/.*)$/);
|
|
3703
|
+
if (rigMatch?.[1]) {
|
|
3704
|
+
return rigMatch[1];
|
|
3705
|
+
}
|
|
3706
|
+
return `/${trimmed}`;
|
|
3707
|
+
}
|
|
3708
|
+
function buildControlsLookup(assetBundle) {
|
|
3709
|
+
const metadata = assetBundle.rig?.inputMetadata ?? [];
|
|
3710
|
+
const metadataByPath = /* @__PURE__ */ new Map();
|
|
3711
|
+
const pathSet = /* @__PURE__ */ new Set();
|
|
3712
|
+
metadata.forEach((entry) => {
|
|
3713
|
+
const normalized = normalizeInputPath(entry.path);
|
|
3714
|
+
if (!normalized) {
|
|
3715
|
+
return;
|
|
3716
|
+
}
|
|
3717
|
+
pathSet.add(normalized);
|
|
3718
|
+
if (!metadataByPath.has(normalized)) {
|
|
3719
|
+
metadataByPath.set(normalized, entry);
|
|
3720
|
+
}
|
|
3721
|
+
});
|
|
3722
|
+
return { pathSet, metadataByPath };
|
|
3723
|
+
}
|
|
3724
|
+
function resolveConstraint(absolutePath, relativePath, inputConstraints) {
|
|
3725
|
+
if (!inputConstraints) {
|
|
3726
|
+
return null;
|
|
3727
|
+
}
|
|
3728
|
+
const normalizedRelative = relativePath.replace(/^\//, "");
|
|
3729
|
+
const candidates = [
|
|
3730
|
+
absolutePath,
|
|
3731
|
+
relativePath,
|
|
3732
|
+
normalizedRelative,
|
|
3733
|
+
absolutePath.replace(/^rig\/[^/]+\//, "")
|
|
3734
|
+
];
|
|
3735
|
+
for (const candidate of candidates) {
|
|
3736
|
+
const constraint = inputConstraints[candidate];
|
|
3737
|
+
if (constraint) {
|
|
3738
|
+
return constraint;
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
return null;
|
|
3742
|
+
}
|
|
3743
|
+
function createControl(faceId, lookup, relativePath, inputConstraints, fallback) {
|
|
3744
|
+
const normalizedRelative = normalizeInputPath(relativePath);
|
|
3745
|
+
if (!normalizedRelative || !lookup.pathSet.has(normalizedRelative)) {
|
|
3746
|
+
return null;
|
|
3747
|
+
}
|
|
3748
|
+
const absolutePath = buildRigInputPath(faceId, normalizedRelative);
|
|
3749
|
+
const metadata = lookup.metadataByPath.get(normalizedRelative);
|
|
3750
|
+
const constraint = resolveConstraint(
|
|
3751
|
+
absolutePath,
|
|
3752
|
+
normalizedRelative,
|
|
3753
|
+
inputConstraints
|
|
3754
|
+
);
|
|
3755
|
+
const min = Number.isFinite(Number(constraint?.min ?? metadata?.range?.min)) ? Number(constraint?.min ?? metadata?.range?.min) : fallback.min;
|
|
3756
|
+
const max = Number.isFinite(Number(constraint?.max ?? metadata?.range?.max)) ? Number(constraint?.max ?? metadata?.range?.max) : fallback.max;
|
|
3757
|
+
const midpoint = min + (max - min) / 2;
|
|
3758
|
+
const defaultValue = Number.isFinite(
|
|
3759
|
+
Number(constraint?.defaultValue ?? metadata?.defaultValue)
|
|
3760
|
+
) ? Number(constraint?.defaultValue ?? metadata?.defaultValue) : Number.isFinite(midpoint) ? midpoint : fallback.defaultValue;
|
|
3761
|
+
return {
|
|
3762
|
+
path: absolutePath,
|
|
3763
|
+
min,
|
|
3764
|
+
max,
|
|
3765
|
+
defaultValue
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3768
|
+
function hasAll(controls, keys) {
|
|
3769
|
+
return keys.every((key) => Boolean(controls[key]));
|
|
3770
|
+
}
|
|
3771
|
+
function resolveFaceControls(assetBundle, runtimeFaceId, inputConstraints) {
|
|
3772
|
+
const faceId = assetBundle.faceId ?? assetBundle.pose?.config?.faceId ?? runtimeFaceId ?? "face";
|
|
3773
|
+
const lookup = buildControlsLookup(assetBundle);
|
|
3774
|
+
const standardVizijControls = {
|
|
3775
|
+
leftX: createControl(
|
|
3776
|
+
faceId,
|
|
3777
|
+
lookup,
|
|
3778
|
+
STANDARD_VIZIJ_EYE_PATHS.leftX,
|
|
3779
|
+
inputConstraints,
|
|
3780
|
+
DEFAULT_GAZE_CONTROL
|
|
3781
|
+
),
|
|
3782
|
+
leftY: createControl(
|
|
3783
|
+
faceId,
|
|
3784
|
+
lookup,
|
|
3785
|
+
STANDARD_VIZIJ_EYE_PATHS.leftY,
|
|
3786
|
+
inputConstraints,
|
|
3787
|
+
DEFAULT_GAZE_CONTROL
|
|
3788
|
+
),
|
|
3789
|
+
rightX: createControl(
|
|
3790
|
+
faceId,
|
|
3791
|
+
lookup,
|
|
3792
|
+
STANDARD_VIZIJ_EYE_PATHS.rightX,
|
|
3793
|
+
inputConstraints,
|
|
3794
|
+
DEFAULT_GAZE_CONTROL
|
|
3795
|
+
),
|
|
3796
|
+
rightY: createControl(
|
|
3797
|
+
faceId,
|
|
3798
|
+
lookup,
|
|
3799
|
+
STANDARD_VIZIJ_EYE_PATHS.rightY,
|
|
3800
|
+
inputConstraints,
|
|
3801
|
+
DEFAULT_GAZE_CONTROL
|
|
3802
|
+
),
|
|
3803
|
+
leftUpper: createControl(
|
|
3804
|
+
faceId,
|
|
3805
|
+
lookup,
|
|
3806
|
+
STANDARD_VIZIJ_EYE_PATHS.leftUpper,
|
|
3807
|
+
inputConstraints,
|
|
3808
|
+
DEFAULT_GAZE_CONTROL
|
|
3809
|
+
),
|
|
3810
|
+
rightUpper: createControl(
|
|
3811
|
+
faceId,
|
|
3812
|
+
lookup,
|
|
3813
|
+
STANDARD_VIZIJ_EYE_PATHS.rightUpper,
|
|
3814
|
+
inputConstraints,
|
|
3815
|
+
DEFAULT_GAZE_CONTROL
|
|
3816
|
+
)
|
|
3817
|
+
};
|
|
3818
|
+
const legacyStandardControls = {
|
|
3819
|
+
leftX: createControl(
|
|
3820
|
+
faceId,
|
|
3821
|
+
lookup,
|
|
3822
|
+
LEGACY_STANDARD_EYE_PATHS.leftX,
|
|
3823
|
+
inputConstraints,
|
|
3824
|
+
DEFAULT_GAZE_CONTROL
|
|
3825
|
+
),
|
|
3826
|
+
leftY: createControl(
|
|
3827
|
+
faceId,
|
|
3828
|
+
lookup,
|
|
3829
|
+
LEGACY_STANDARD_EYE_PATHS.leftY,
|
|
3830
|
+
inputConstraints,
|
|
3831
|
+
DEFAULT_GAZE_CONTROL
|
|
3832
|
+
),
|
|
3833
|
+
rightX: createControl(
|
|
3834
|
+
faceId,
|
|
3835
|
+
lookup,
|
|
3836
|
+
LEGACY_STANDARD_EYE_PATHS.rightX,
|
|
3837
|
+
inputConstraints,
|
|
3838
|
+
DEFAULT_GAZE_CONTROL
|
|
3839
|
+
),
|
|
3840
|
+
rightY: createControl(
|
|
3841
|
+
faceId,
|
|
3842
|
+
lookup,
|
|
3843
|
+
LEGACY_STANDARD_EYE_PATHS.rightY,
|
|
3844
|
+
inputConstraints,
|
|
3845
|
+
DEFAULT_GAZE_CONTROL
|
|
3846
|
+
),
|
|
3847
|
+
leftUpper: createControl(
|
|
3848
|
+
faceId,
|
|
3849
|
+
lookup,
|
|
3850
|
+
LEGACY_STANDARD_EYE_PATHS.leftUpper,
|
|
3851
|
+
inputConstraints,
|
|
3852
|
+
DEFAULT_GAZE_CONTROL
|
|
3853
|
+
),
|
|
3854
|
+
rightUpper: createControl(
|
|
3855
|
+
faceId,
|
|
3856
|
+
lookup,
|
|
3857
|
+
LEGACY_STANDARD_EYE_PATHS.rightUpper,
|
|
3858
|
+
inputConstraints,
|
|
3859
|
+
DEFAULT_GAZE_CONTROL
|
|
3860
|
+
)
|
|
3861
|
+
};
|
|
3862
|
+
const propsrigControls = {
|
|
3863
|
+
leftX: createControl(
|
|
3864
|
+
faceId,
|
|
3865
|
+
lookup,
|
|
3866
|
+
PROPSRIG_EYE_PATHS.leftX,
|
|
3867
|
+
inputConstraints,
|
|
3868
|
+
DEFAULT_GAZE_CONTROL
|
|
3869
|
+
),
|
|
3870
|
+
leftY: createControl(
|
|
3871
|
+
faceId,
|
|
3872
|
+
lookup,
|
|
3873
|
+
PROPSRIG_EYE_PATHS.leftY,
|
|
3874
|
+
inputConstraints,
|
|
3875
|
+
DEFAULT_GAZE_CONTROL
|
|
3876
|
+
),
|
|
3877
|
+
rightX: createControl(
|
|
3878
|
+
faceId,
|
|
3879
|
+
lookup,
|
|
3880
|
+
PROPSRIG_EYE_PATHS.rightX,
|
|
3881
|
+
inputConstraints,
|
|
3882
|
+
DEFAULT_GAZE_CONTROL
|
|
3883
|
+
),
|
|
3884
|
+
rightY: createControl(
|
|
3885
|
+
faceId,
|
|
3886
|
+
lookup,
|
|
3887
|
+
PROPSRIG_EYE_PATHS.rightY,
|
|
3888
|
+
inputConstraints,
|
|
3889
|
+
DEFAULT_GAZE_CONTROL
|
|
3890
|
+
)
|
|
3891
|
+
};
|
|
3892
|
+
const coupledGazeControls = {
|
|
3893
|
+
leftX: createControl(
|
|
3894
|
+
faceId,
|
|
3895
|
+
lookup,
|
|
3896
|
+
"/gaze/left_right",
|
|
3897
|
+
inputConstraints,
|
|
3898
|
+
DEFAULT_GAZE_CONTROL
|
|
3899
|
+
),
|
|
3900
|
+
leftY: createControl(
|
|
3901
|
+
faceId,
|
|
3902
|
+
lookup,
|
|
3903
|
+
"/gaze/up_down",
|
|
3904
|
+
inputConstraints,
|
|
3905
|
+
DEFAULT_GAZE_CONTROL
|
|
3906
|
+
),
|
|
3907
|
+
rightX: createControl(
|
|
3908
|
+
faceId,
|
|
3909
|
+
lookup,
|
|
3910
|
+
"/gaze/left_right_copy",
|
|
3911
|
+
inputConstraints,
|
|
3912
|
+
DEFAULT_GAZE_CONTROL
|
|
3913
|
+
) ?? createControl(
|
|
3914
|
+
faceId,
|
|
3915
|
+
lookup,
|
|
3916
|
+
"/gaze/left_right",
|
|
3917
|
+
inputConstraints,
|
|
3918
|
+
DEFAULT_GAZE_CONTROL
|
|
3919
|
+
),
|
|
3920
|
+
rightY: createControl(
|
|
3921
|
+
faceId,
|
|
3922
|
+
lookup,
|
|
3923
|
+
"/gaze/up_down_copy",
|
|
3924
|
+
inputConstraints,
|
|
3925
|
+
DEFAULT_GAZE_CONTROL
|
|
3926
|
+
) ?? createControl(
|
|
3927
|
+
faceId,
|
|
3928
|
+
lookup,
|
|
3929
|
+
"/gaze/up_down",
|
|
3930
|
+
inputConstraints,
|
|
3931
|
+
DEFAULT_GAZE_CONTROL
|
|
3932
|
+
)
|
|
3933
|
+
};
|
|
3934
|
+
const blinkFromLids = createControl(
|
|
3935
|
+
faceId,
|
|
3936
|
+
lookup,
|
|
3937
|
+
"/lids/blink",
|
|
3938
|
+
inputConstraints,
|
|
3939
|
+
DEFAULT_BLINK_CONTROL
|
|
3940
|
+
);
|
|
3941
|
+
const blinkDirect = createControl(
|
|
3942
|
+
faceId,
|
|
3943
|
+
lookup,
|
|
3944
|
+
"/blink",
|
|
3945
|
+
inputConstraints,
|
|
3946
|
+
DEFAULT_BLINK_CONTROL
|
|
3947
|
+
);
|
|
3948
|
+
if (hasAll(standardVizijControls, ["leftX", "leftY", "rightX", "rightY"])) {
|
|
3949
|
+
return {
|
|
3950
|
+
faceId,
|
|
3951
|
+
gazeSource: "standard-vizij",
|
|
3952
|
+
blinkSource: blinkFromLids ? "lids" : blinkDirect ? "blink" : "none",
|
|
3953
|
+
eyes: {
|
|
3954
|
+
leftX: standardVizijControls.leftX,
|
|
3955
|
+
leftY: standardVizijControls.leftY,
|
|
3956
|
+
rightX: standardVizijControls.rightX,
|
|
3957
|
+
rightY: standardVizijControls.rightY
|
|
3958
|
+
},
|
|
3959
|
+
eyelids: {
|
|
3960
|
+
leftUpper: standardVizijControls.leftUpper,
|
|
3961
|
+
rightUpper: standardVizijControls.rightUpper
|
|
3962
|
+
},
|
|
3963
|
+
blink: blinkFromLids ?? blinkDirect
|
|
3964
|
+
};
|
|
3965
|
+
}
|
|
3966
|
+
if (hasAll(legacyStandardControls, ["leftX", "leftY", "rightX", "rightY"])) {
|
|
3967
|
+
return {
|
|
3968
|
+
faceId,
|
|
3969
|
+
gazeSource: "standard",
|
|
3970
|
+
blinkSource: blinkDirect ? "blink" : blinkFromLids ? "lids" : "none",
|
|
3971
|
+
eyes: {
|
|
3972
|
+
leftX: legacyStandardControls.leftX,
|
|
3973
|
+
leftY: legacyStandardControls.leftY,
|
|
3974
|
+
rightX: legacyStandardControls.rightX,
|
|
3975
|
+
rightY: legacyStandardControls.rightY
|
|
3976
|
+
},
|
|
3977
|
+
eyelids: {
|
|
3978
|
+
leftUpper: legacyStandardControls.leftUpper,
|
|
3979
|
+
rightUpper: legacyStandardControls.rightUpper
|
|
3980
|
+
},
|
|
3981
|
+
blink: blinkDirect ?? blinkFromLids
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
if (hasAll(propsrigControls, ["leftX", "leftY", "rightX", "rightY"])) {
|
|
3985
|
+
return {
|
|
3986
|
+
faceId,
|
|
3987
|
+
gazeSource: "propsrig",
|
|
3988
|
+
blinkSource: blinkDirect ? "blink" : blinkFromLids ? "lids" : "none",
|
|
3989
|
+
eyes: {
|
|
3990
|
+
leftX: propsrigControls.leftX,
|
|
3991
|
+
leftY: propsrigControls.leftY,
|
|
3992
|
+
rightX: propsrigControls.rightX,
|
|
3993
|
+
rightY: propsrigControls.rightY
|
|
3994
|
+
},
|
|
3995
|
+
eyelids: {
|
|
3996
|
+
leftUpper: null,
|
|
3997
|
+
rightUpper: null
|
|
3998
|
+
},
|
|
3999
|
+
blink: blinkDirect ?? blinkFromLids
|
|
4000
|
+
};
|
|
4001
|
+
}
|
|
4002
|
+
if (hasAll(coupledGazeControls, ["leftX", "leftY", "rightX", "rightY"])) {
|
|
4003
|
+
return {
|
|
4004
|
+
faceId,
|
|
4005
|
+
gazeSource: "coupled-gaze",
|
|
4006
|
+
blinkSource: blinkFromLids ? "lids" : blinkDirect ? "blink" : "none",
|
|
4007
|
+
eyes: {
|
|
4008
|
+
leftX: coupledGazeControls.leftX,
|
|
4009
|
+
leftY: coupledGazeControls.leftY,
|
|
4010
|
+
rightX: coupledGazeControls.rightX,
|
|
4011
|
+
rightY: coupledGazeControls.rightY
|
|
4012
|
+
},
|
|
4013
|
+
eyelids: {
|
|
4014
|
+
leftUpper: null,
|
|
4015
|
+
rightUpper: null
|
|
4016
|
+
},
|
|
4017
|
+
blink: blinkFromLids ?? blinkDirect
|
|
4018
|
+
};
|
|
4019
|
+
}
|
|
4020
|
+
return {
|
|
4021
|
+
faceId,
|
|
4022
|
+
gazeSource: "none",
|
|
4023
|
+
blinkSource: blinkFromLids ? "lids" : blinkDirect ? "blink" : "none",
|
|
4024
|
+
eyes: {
|
|
4025
|
+
leftX: null,
|
|
4026
|
+
leftY: null,
|
|
4027
|
+
rightX: null,
|
|
4028
|
+
rightY: null
|
|
4029
|
+
},
|
|
4030
|
+
eyelids: {
|
|
4031
|
+
leftUpper: null,
|
|
4032
|
+
rightUpper: null
|
|
4033
|
+
},
|
|
4034
|
+
blink: blinkFromLids ?? blinkDirect
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
function mapNormalizedControlValue(control, normalizedValue) {
|
|
4038
|
+
const value = clamp(normalizedValue, -1, 1);
|
|
4039
|
+
if (value === 0) {
|
|
4040
|
+
return control.defaultValue;
|
|
4041
|
+
}
|
|
4042
|
+
if (value > 0) {
|
|
4043
|
+
return control.defaultValue + (control.max - control.defaultValue) * value;
|
|
4044
|
+
}
|
|
4045
|
+
return control.defaultValue + (control.defaultValue - control.min) * value;
|
|
4046
|
+
}
|
|
4047
|
+
function mapUnitControlValue(control, unitValue) {
|
|
4048
|
+
const value = clamp(unitValue, 0, 1);
|
|
4049
|
+
return control.defaultValue + (control.max - control.defaultValue) * value;
|
|
4050
|
+
}
|
|
1822
4051
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1823
4052
|
0 && (module.exports = {
|
|
4053
|
+
EMOTION_POSE_KEYS,
|
|
4054
|
+
EXPRESSIVE_EMOTION_POSE_KEYS,
|
|
4055
|
+
POSE_WEIGHT_INPUT_PATH_PREFIX,
|
|
4056
|
+
VISEME_POSE_KEYS,
|
|
1824
4057
|
VizijRuntimeFace,
|
|
1825
4058
|
VizijRuntimeProvider,
|
|
4059
|
+
buildPoseWeightInputPathSegment,
|
|
4060
|
+
buildPoseWeightPathMap,
|
|
4061
|
+
buildPoseWeightRelativePath,
|
|
4062
|
+
buildRigInputPath,
|
|
4063
|
+
buildSemanticPoseWeightPathMap,
|
|
4064
|
+
filterPosesBySemanticKind,
|
|
4065
|
+
getPoseSemanticKey,
|
|
4066
|
+
mapNormalizedControlValue,
|
|
4067
|
+
mapUnitControlValue,
|
|
4068
|
+
normalizePoseSemanticKey,
|
|
4069
|
+
resolveFaceControls,
|
|
4070
|
+
resolvePoseMembership,
|
|
4071
|
+
resolvePoseSemantics,
|
|
4072
|
+
resolveRuntimeUpdatePlan,
|
|
4073
|
+
useOptionalVizijRuntime,
|
|
1826
4074
|
useRigInput,
|
|
1827
4075
|
useVizijOutputs,
|
|
1828
4076
|
useVizijRuntime
|