@uptimizr/replay 0.1.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/AGENTS.md +57 -0
- package/LICENSE +201 -0
- package/README.md +96 -0
- package/dist/drivers/babylon-lite.d.ts +134 -0
- package/dist/drivers/babylon-lite.d.ts.map +1 -0
- package/dist/drivers/babylon-lite.js +167 -0
- package/dist/drivers/babylon-lite.js.map +1 -0
- package/dist/drivers/babylon.d.ts +99 -0
- package/dist/drivers/babylon.d.ts.map +1 -0
- package/dist/drivers/babylon.js +176 -0
- package/dist/drivers/babylon.js.map +1 -0
- package/dist/drivers/playcanvas.d.ts +122 -0
- package/dist/drivers/playcanvas.d.ts.map +1 -0
- package/dist/drivers/playcanvas.js +206 -0
- package/dist/drivers/playcanvas.js.map +1 -0
- package/dist/drivers/three.d.ts +127 -0
- package/dist/drivers/three.d.ts.map +1 -0
- package/dist/drivers/three.js +194 -0
- package/dist/drivers/three.js.map +1 -0
- package/dist/fetchSession.d.ts +43 -0
- package/dist/fetchSession.d.ts.map +1 -0
- package/dist/fetchSession.js +103 -0
- package/dist/fetchSession.js.map +1 -0
- package/dist/global.d.ts +62 -0
- package/dist/global.d.ts.map +1 -0
- package/dist/global.js +95 -0
- package/dist/global.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/player.d.ts +35 -0
- package/dist/player.d.ts.map +1 -0
- package/dist/player.js +104 -0
- package/dist/player.js.map +1 -0
- package/dist/reconstruct.d.ts +74 -0
- package/dist/reconstruct.d.ts.map +1 -0
- package/dist/reconstruct.js +266 -0
- package/dist/reconstruct.js.map +1 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/uptimizr-replay.global.js +71 -0
- package/llms.txt +17 -0
- package/package.json +100 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"babylon-lite.js","sourceRoot":"","sources":["../../src/drivers/babylon-lite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Camera, Scene } from "@babylonjs/core";
|
|
2
|
+
import type { CustomPropValue } from "@uptimizr/schema";
|
|
3
|
+
import type { ReplayDriver } from "../types.js";
|
|
4
|
+
export interface BabylonReplayDriverOptions {
|
|
5
|
+
/** Scene to re-drive. */
|
|
6
|
+
scene: Scene;
|
|
7
|
+
/** Camera to move. Defaults to `scene.activeCamera`. */
|
|
8
|
+
camera?: Camera;
|
|
9
|
+
/** Called for each replayed pointer event, so the host can render a marker. */
|
|
10
|
+
onPointer?: (screen: [number, number] | undefined, hitPoint: [number, number, number] | undefined, hitMesh: string | undefined, type: "pointer_move" | "pointer_click" | "pointer_down" | "pointer_up") => void;
|
|
11
|
+
/** Called for each replayed mesh interaction, so the host can highlight it. */
|
|
12
|
+
onMeshInteraction?: (mesh: string, kind: string, point: [number, number, number] | undefined) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Scene-actor resolvers for `node_transform` drive-back (ADR 0027). Maps a
|
|
15
|
+
* recorded `nodeId` to the live Babylon node to re-drive — a direct
|
|
16
|
+
* `TransformNode`/`AbstractMesh` or a `() => node` resolver (robust to load
|
|
17
|
+
* order). Tier-1 samples set the node's world transform; Tier-2 samples
|
|
18
|
+
* (`boneId` present) set the matching skeleton bone's local transform.
|
|
19
|
+
*/
|
|
20
|
+
nodes?: Record<string, BabylonReplayNode | (() => BabylonReplayNode | null | undefined)>;
|
|
21
|
+
/**
|
|
22
|
+
* Called for **every** replayed `node_transform` (Tier 1 and Tier 2), after any
|
|
23
|
+
* node/bone drive-back. Lets a host render a proxy marker for an actor it has
|
|
24
|
+
* no live node for, or annotate the timeline. `boneId` is set for Tier-2 bone
|
|
25
|
+
* samples; the transform is in the sample's frame (world for Tier 1,
|
|
26
|
+
* skeleton-local for Tier 2).
|
|
27
|
+
*/
|
|
28
|
+
onNodeTransform?: (nodeId: string, transform: {
|
|
29
|
+
boneId: string | undefined;
|
|
30
|
+
childPath: string | undefined;
|
|
31
|
+
position: [number, number, number];
|
|
32
|
+
rotation: [number, number, number, number];
|
|
33
|
+
scale: [number, number, number] | undefined;
|
|
34
|
+
}, ts: number) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Called for each replayed developer-defined `custom` event. The driver can't
|
|
37
|
+
* know how to render an arbitrary domain event, so it forwards the name,
|
|
38
|
+
* props, and timestamp for the host to visualize (marker, toast, log, ...).
|
|
39
|
+
*/
|
|
40
|
+
onCustom?: (name: string, props: Record<string, CustomPropValue> | undefined, ts: number) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Called for each replayed `input_action` (discrete keyboard/gamepad action,
|
|
43
|
+
* ADR 0023). The driver can't re-drive an app-defined action, so it forwards
|
|
44
|
+
* the action, raw `code`/`button`, source, and timestamp for the host to
|
|
45
|
+
* annotate the timeline.
|
|
46
|
+
*/
|
|
47
|
+
onInputAction?: (input: {
|
|
48
|
+
action: string;
|
|
49
|
+
code: string | undefined;
|
|
50
|
+
button: number | undefined;
|
|
51
|
+
pressed: boolean | undefined;
|
|
52
|
+
source: string | undefined;
|
|
53
|
+
}, ts: number) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Called for each replayed browser/engine lifecycle event (viewport resize,
|
|
56
|
+
* tab visibility, window focus, GPU context loss/restore). The driver doesn't
|
|
57
|
+
* re-drive these — it forwards them so a replay UI can annotate the timeline
|
|
58
|
+
* with what was happening around the scene.
|
|
59
|
+
*/
|
|
60
|
+
onLifecycle?: (event: {
|
|
61
|
+
type: "viewport_resize";
|
|
62
|
+
width: number;
|
|
63
|
+
height: number;
|
|
64
|
+
dpr: number | undefined;
|
|
65
|
+
} | {
|
|
66
|
+
type: "visibility_change";
|
|
67
|
+
state: "visible" | "hidden";
|
|
68
|
+
} | {
|
|
69
|
+
type: "focus_change";
|
|
70
|
+
focused: boolean;
|
|
71
|
+
} | {
|
|
72
|
+
type: "context_lost";
|
|
73
|
+
} | {
|
|
74
|
+
type: "context_restored";
|
|
75
|
+
}, ts: number) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Called for each replayed `runtime_error` event (opt-in capture, ADR 0013).
|
|
78
|
+
* Forwarded so a replay UI can mark where a JS error or unhandled rejection
|
|
79
|
+
* interrupted the session.
|
|
80
|
+
*/
|
|
81
|
+
onError?: (error: {
|
|
82
|
+
kind: "error" | "unhandledrejection";
|
|
83
|
+
message: string;
|
|
84
|
+
source: string | undefined;
|
|
85
|
+
lineno: number | undefined;
|
|
86
|
+
colno: number | undefined;
|
|
87
|
+
stack: string | undefined;
|
|
88
|
+
}, ts: number) => void;
|
|
89
|
+
}
|
|
90
|
+
/** A Babylon node a replay can drive (a `TransformNode` or `AbstractMesh`). */
|
|
91
|
+
export type BabylonReplayNode = object;
|
|
92
|
+
/**
|
|
93
|
+
* Babylon replay driver. Re-drives camera pose (and surfaces pointer/mesh events
|
|
94
|
+
* to host callbacks) in the user's own scene.
|
|
95
|
+
*
|
|
96
|
+
* It only reads/writes the scene — it never emits analytics events (ADR 0006).
|
|
97
|
+
*/
|
|
98
|
+
export declare function createBabylonReplayDriver(options: BabylonReplayDriverOptions): ReplayDriver;
|
|
99
|
+
//# sourceMappingURL=babylon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"babylon.d.ts","sourceRoot":"","sources":["../../src/drivers/babylon.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAY,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,0BAA0B;IACzC,yBAAyB;IACzB,KAAK,EAAE,KAAK,CAAC;IACb,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,CACV,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EACpC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC9C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,IAAI,EAAE,cAAc,GAAG,eAAe,GAAG,cAAc,GAAG,YAAY,KACnE,IAAI,CAAC;IACV,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,CAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,KACxC,IAAI,CAAC;IACV;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,CAAC,MAAM,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC;IACzF;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,CAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;KAC7C,EACD,EAAE,EAAE,MAAM,KACP,IAAI,CAAC;IACV;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClG;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CACd,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;QAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B,EACD,EAAE,EAAE,MAAM,KACP,IAAI,CAAC;IACV;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CACZ,KAAK,EACD;QAAE,IAAI,EAAE,iBAAiB,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GACnF;QAAE,IAAI,EAAE,mBAAmB,CAAC;QAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAA;KAAE,GAC1D;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,GAC1C;QAAE,IAAI,EAAE,cAAc,CAAA;KAAE,GACxB;QAAE,IAAI,EAAE,kBAAkB,CAAA;KAAE,EAChC,EAAE,EAAE,MAAM,KACP,IAAI,CAAC;IACV;;;;OAIG;IACH,OAAO,CAAC,EAAE,CACR,KAAK,EAAE;QACL,IAAI,EAAE,OAAO,GAAG,oBAAoB,CAAC;QACrC,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;KAC3B,EACD,EAAE,EAAE,MAAM,KACP,IAAI,CAAC;CACX;AAaD,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAmDvC;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,YAAY,CA8J3F"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
|
|
2
|
+
import { Quaternion } from "@babylonjs/core/Maths/math.vector.js";
|
|
3
|
+
/** Resolve a node entry (direct ref or `() => node`) to a live node, or `null`. */
|
|
4
|
+
function resolveReplayNode(entry) {
|
|
5
|
+
if (!entry)
|
|
6
|
+
return null;
|
|
7
|
+
if (typeof entry === "function") {
|
|
8
|
+
return entry() ?? null;
|
|
9
|
+
}
|
|
10
|
+
return entry;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Walk a `/`-separated {@link NodeTransformEvent.childPath} from a resolved root
|
|
14
|
+
* actor to the descendant node, matching each segment against direct children by
|
|
15
|
+
* `name` (ADR 0033). Returns `null` if any segment is missing.
|
|
16
|
+
*/
|
|
17
|
+
function resolveChildByPath(root, childPath) {
|
|
18
|
+
let cur = root;
|
|
19
|
+
for (const segment of childPath.split("/")) {
|
|
20
|
+
if (!segment)
|
|
21
|
+
continue;
|
|
22
|
+
const children = cur?.getChildren?.();
|
|
23
|
+
if (!Array.isArray(children))
|
|
24
|
+
return null;
|
|
25
|
+
cur = children.find((c) => c.name === segment) ?? null;
|
|
26
|
+
if (!cur)
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return cur;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Babylon replay driver. Re-drives camera pose (and surfaces pointer/mesh events
|
|
33
|
+
* to host callbacks) in the user's own scene.
|
|
34
|
+
*
|
|
35
|
+
* It only reads/writes the scene — it never emits analytics events (ADR 0006).
|
|
36
|
+
*/
|
|
37
|
+
export function createBabylonReplayDriver(options) {
|
|
38
|
+
const target = new Vector3();
|
|
39
|
+
const position = new Vector3();
|
|
40
|
+
const nodePosition = new Vector3();
|
|
41
|
+
return {
|
|
42
|
+
reset() {
|
|
43
|
+
// Camera state is fully determined by the next camera_sample, so there is
|
|
44
|
+
// nothing to restore; host-rendered markers are the host's responsibility.
|
|
45
|
+
},
|
|
46
|
+
apply(event) {
|
|
47
|
+
switch (event.type) {
|
|
48
|
+
case "camera_sample": {
|
|
49
|
+
const cam = (options.camera ??
|
|
50
|
+
options.scene.activeCamera);
|
|
51
|
+
if (!cam)
|
|
52
|
+
return;
|
|
53
|
+
const [px, py, pz] = event.position;
|
|
54
|
+
if (event.target) {
|
|
55
|
+
target.set(event.target[0], event.target[1], event.target[2]);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const [dx, dy, dz] = event.direction;
|
|
59
|
+
target.set(px + dx, py + dy, pz + dz);
|
|
60
|
+
}
|
|
61
|
+
// Set the target first so ArcRotateCamera derives alpha/beta/radius
|
|
62
|
+
// relative to it, then place the camera.
|
|
63
|
+
cam.setTarget?.(target);
|
|
64
|
+
if (typeof cam.setPosition === "function") {
|
|
65
|
+
position.set(px, py, pz);
|
|
66
|
+
cam.setPosition(position);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
cam.position.set(px, py, pz);
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "pointer_move":
|
|
74
|
+
case "pointer_click":
|
|
75
|
+
case "pointer_down":
|
|
76
|
+
case "pointer_up":
|
|
77
|
+
options.onPointer?.(event.screen, event.hitPoint, event.hitMesh, event.type);
|
|
78
|
+
break;
|
|
79
|
+
case "mesh_interaction":
|
|
80
|
+
options.onMeshInteraction?.(event.mesh, event.kind, event.point);
|
|
81
|
+
break;
|
|
82
|
+
case "custom":
|
|
83
|
+
options.onCustom?.(event.name, event.props, event.ts);
|
|
84
|
+
break;
|
|
85
|
+
case "input_action":
|
|
86
|
+
options.onInputAction?.({
|
|
87
|
+
action: event.action,
|
|
88
|
+
code: event.code,
|
|
89
|
+
button: event.button,
|
|
90
|
+
pressed: event.pressed,
|
|
91
|
+
source: event.source,
|
|
92
|
+
}, event.ts);
|
|
93
|
+
break;
|
|
94
|
+
case "viewport_resize":
|
|
95
|
+
options.onLifecycle?.({ type: "viewport_resize", width: event.width, height: event.height, dpr: event.dpr }, event.ts);
|
|
96
|
+
break;
|
|
97
|
+
case "visibility_change":
|
|
98
|
+
options.onLifecycle?.({ type: "visibility_change", state: event.state }, event.ts);
|
|
99
|
+
break;
|
|
100
|
+
case "focus_change":
|
|
101
|
+
options.onLifecycle?.({ type: "focus_change", focused: event.focused }, event.ts);
|
|
102
|
+
break;
|
|
103
|
+
case "context_lost":
|
|
104
|
+
options.onLifecycle?.({ type: "context_lost" }, event.ts);
|
|
105
|
+
break;
|
|
106
|
+
case "context_restored":
|
|
107
|
+
options.onLifecycle?.({ type: "context_restored" }, event.ts);
|
|
108
|
+
break;
|
|
109
|
+
case "runtime_error":
|
|
110
|
+
options.onError?.({
|
|
111
|
+
kind: event.kind,
|
|
112
|
+
message: event.message,
|
|
113
|
+
source: event.source,
|
|
114
|
+
lineno: event.lineno,
|
|
115
|
+
colno: event.colno,
|
|
116
|
+
stack: event.stack,
|
|
117
|
+
}, event.ts);
|
|
118
|
+
break;
|
|
119
|
+
case "camera_gesture":
|
|
120
|
+
// Derived navigation annotation (ADR 0025); the camera trajectory is
|
|
121
|
+
// reconstructed from camera_sample, so replay intentionally ignores it.
|
|
122
|
+
break;
|
|
123
|
+
case "node_transform": {
|
|
124
|
+
const node = resolveReplayNode(options.nodes?.[event.nodeId]);
|
|
125
|
+
if (node) {
|
|
126
|
+
if (event.boneId !== undefined) {
|
|
127
|
+
// Tier 2: drive the matching skeleton bone's LOCAL pose.
|
|
128
|
+
const bone = node.skeleton?.bones?.find((b) => b.name === event.boneId);
|
|
129
|
+
if (bone) {
|
|
130
|
+
bone.position?.set(event.position[0], event.position[1], event.position[2]);
|
|
131
|
+
bone.rotationQuaternion = new Quaternion(event.rotation[0], event.rotation[1], event.rotation[2], event.rotation[3]);
|
|
132
|
+
if (event.scale) {
|
|
133
|
+
bone.scaling?.set(event.scale[0], event.scale[1], event.scale[2]);
|
|
134
|
+
}
|
|
135
|
+
bone.markAsDirty?.();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// Tier 1: the declared root (no childPath) or a subtree child resolved
|
|
140
|
+
// by its relative path (ADR 0033). Both carry a world-frame pose;
|
|
141
|
+
// `setAbsolutePosition` honours any parent, a plain root falls back
|
|
142
|
+
// to `position`.
|
|
143
|
+
const target = event.childPath === undefined ? node : resolveChildByPath(node, event.childPath);
|
|
144
|
+
if (target) {
|
|
145
|
+
if (typeof target.setAbsolutePosition === "function") {
|
|
146
|
+
nodePosition.set(event.position[0], event.position[1], event.position[2]);
|
|
147
|
+
target.setAbsolutePosition(nodePosition);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
target.position?.set(event.position[0], event.position[1], event.position[2]);
|
|
151
|
+
}
|
|
152
|
+
// Babylon ignores `rotation` (Euler) once a quaternion is present;
|
|
153
|
+
// assign a fresh one so the world orientation is applied verbatim.
|
|
154
|
+
target.rotationQuaternion = new Quaternion(event.rotation[0], event.rotation[1], event.rotation[2], event.rotation[3]);
|
|
155
|
+
if (event.scale) {
|
|
156
|
+
target.scaling?.set(event.scale[0], event.scale[1], event.scale[2]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
options.onNodeTransform?.(event.nodeId, {
|
|
162
|
+
boneId: event.boneId,
|
|
163
|
+
childPath: event.childPath,
|
|
164
|
+
position: event.position,
|
|
165
|
+
rotation: event.rotation,
|
|
166
|
+
scale: event.scale,
|
|
167
|
+
}, event.ts);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
default:
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=babylon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"babylon.js","sourceRoot":"","sources":["../../src/drivers/babylon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { CustomPropValue } from "@uptimizr/schema";
|
|
2
|
+
import type { ReplayDriver } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Structural view of the PlayCanvas objects this driver writes to. Kept minimal and
|
|
5
|
+
* structural so `playcanvas` stays an **optional peer dependency** — the driver
|
|
6
|
+
* never imports PlayCanvas at runtime; the host page owns the app and the real
|
|
7
|
+
* Entity.
|
|
8
|
+
*/
|
|
9
|
+
interface PlayCanvasReplayCamera {
|
|
10
|
+
/** `Entity.setPosition(x, y, z)` — world-space move. */
|
|
11
|
+
setPosition(x: number, y: number, z: number): void;
|
|
12
|
+
/** `Entity.lookAt(x, y, z)` — orients the entity's forward axis at the point. */
|
|
13
|
+
lookAt(x: number, y: number, z: number): void;
|
|
14
|
+
/** The camera component; `fov` is the vertical FOV in **degrees** (PlayCanvas). */
|
|
15
|
+
camera?: {
|
|
16
|
+
fov?: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** A PlayCanvas node a replay can drive (an `Entity`). */
|
|
20
|
+
export type PlayCanvasReplayNode = object;
|
|
21
|
+
export interface PlayCanvasReplayDriverOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Camera Entity to move. PlayCanvas has no single active camera, so the one to
|
|
24
|
+
* re-drive is required. (Read/write only — the driver never emits analytics.)
|
|
25
|
+
*/
|
|
26
|
+
camera: PlayCanvasReplayCamera;
|
|
27
|
+
/** Called for each replayed pointer event, so the host can render a marker. */
|
|
28
|
+
onPointer?: (screen: [number, number] | undefined, hitPoint: [number, number, number] | undefined, hitMesh: string | undefined, type: "pointer_move" | "pointer_click" | "pointer_down" | "pointer_up") => void;
|
|
29
|
+
/** Called for each replayed mesh interaction, so the host can highlight it. */
|
|
30
|
+
onMeshInteraction?: (mesh: string, kind: string, point: [number, number, number] | undefined) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Scene-actor resolvers for `node_transform` drive-back (ADR 0027). Maps a
|
|
33
|
+
* recorded `nodeId` to the live PlayCanvas `Entity` to re-drive — a direct
|
|
34
|
+
* reference or a `() => entity` resolver. Tier-1 (world-frame) samples are
|
|
35
|
+
* converted canonical → PlayCanvas and applied via `setPosition`/`setRotation`.
|
|
36
|
+
* Tier-2 bone samples drive the matching named bone of the entity's skin in its
|
|
37
|
+
* local frame; every sample is also forwarded via {@link onNodeTransform}.
|
|
38
|
+
*/
|
|
39
|
+
nodes?: Record<string, PlayCanvasReplayNode | (() => PlayCanvasReplayNode | null | undefined)>;
|
|
40
|
+
/**
|
|
41
|
+
* Called for **every** replayed `node_transform` (Tier 1 and Tier 2), after any
|
|
42
|
+
* node drive-back. The position/rotation are converted into PlayCanvas'
|
|
43
|
+
* right-handed frame; `boneId` is set for Tier-2 samples.
|
|
44
|
+
*/
|
|
45
|
+
onNodeTransform?: (nodeId: string, transform: {
|
|
46
|
+
boneId: string | undefined;
|
|
47
|
+
childPath: string | undefined;
|
|
48
|
+
position: [number, number, number];
|
|
49
|
+
rotation: [number, number, number, number];
|
|
50
|
+
scale: [number, number, number] | undefined;
|
|
51
|
+
}, ts: number) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Called for each replayed developer-defined `custom` event. The driver can't
|
|
54
|
+
* know how to render an arbitrary domain event, so it forwards the name, props,
|
|
55
|
+
* and timestamp for the host to visualize (marker, toast, log, ...).
|
|
56
|
+
*/
|
|
57
|
+
onCustom?: (name: string, props: Record<string, CustomPropValue> | undefined, ts: number) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Called for each replayed `input_action` (discrete keyboard/gamepad action,
|
|
60
|
+
* ADR 0023). Forwarded with the action, raw `code`/`button`, source, and
|
|
61
|
+
* timestamp so a replay UI can annotate the timeline.
|
|
62
|
+
*/
|
|
63
|
+
onInputAction?: (input: {
|
|
64
|
+
action: string;
|
|
65
|
+
code: string | undefined;
|
|
66
|
+
button: number | undefined;
|
|
67
|
+
pressed: boolean | undefined;
|
|
68
|
+
source: string | undefined;
|
|
69
|
+
}, ts: number) => void;
|
|
70
|
+
/**
|
|
71
|
+
* Called for each replayed browser/engine lifecycle event (viewport resize, tab
|
|
72
|
+
* visibility, window focus, GPU context loss/restore). The driver doesn't
|
|
73
|
+
* re-drive these — it forwards them so a replay UI can annotate the timeline.
|
|
74
|
+
*/
|
|
75
|
+
onLifecycle?: (event: {
|
|
76
|
+
type: "viewport_resize";
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
dpr: number | undefined;
|
|
80
|
+
} | {
|
|
81
|
+
type: "visibility_change";
|
|
82
|
+
state: "visible" | "hidden";
|
|
83
|
+
} | {
|
|
84
|
+
type: "focus_change";
|
|
85
|
+
focused: boolean;
|
|
86
|
+
} | {
|
|
87
|
+
type: "context_lost";
|
|
88
|
+
} | {
|
|
89
|
+
type: "context_restored";
|
|
90
|
+
}, ts: number) => void;
|
|
91
|
+
/**
|
|
92
|
+
* Called for each replayed `runtime_error` event (opt-in capture, ADR 0013).
|
|
93
|
+
* Forwarded so a replay UI can mark where a JS error or unhandled rejection
|
|
94
|
+
* interrupted the session.
|
|
95
|
+
*/
|
|
96
|
+
onError?: (error: {
|
|
97
|
+
kind: "error" | "unhandledrejection";
|
|
98
|
+
message: string;
|
|
99
|
+
source: string | undefined;
|
|
100
|
+
lineno: number | undefined;
|
|
101
|
+
colno: number | undefined;
|
|
102
|
+
stack: string | undefined;
|
|
103
|
+
}, ts: number) => void;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* PlayCanvas replay driver. Re-drives camera pose (and surfaces pointer/mesh events
|
|
107
|
+
* to host callbacks) in the user's own PlayCanvas app.
|
|
108
|
+
*
|
|
109
|
+
* ## Coordinate frame (canonical → PlayCanvas)
|
|
110
|
+
* Events arrive in the canonical **left-handed, y-up** frame (ADR 0018); PlayCanvas
|
|
111
|
+
* is **right-handed**, so world-space data is converted back with the shared
|
|
112
|
+
* `fromCanonical*` helpers (the Z-negation reflection is its own inverse). Camera
|
|
113
|
+
* orientation is applied via `camera.lookAt(target)` — `lookAt` resolves
|
|
114
|
+
* PlayCanvas' forward-axis convention internally, so the driver does **not**
|
|
115
|
+
* re-derive a forward axis from the recorded direction (avoiding the orientation
|
|
116
|
+
* trap documented in sdk-core `coordinates.ts`).
|
|
117
|
+
*
|
|
118
|
+
* It only reads/writes the scene — it never emits analytics events (ADR 0006).
|
|
119
|
+
*/
|
|
120
|
+
export declare function createPlayCanvasReplayDriver(options: PlayCanvasReplayDriverOptions): ReplayDriver;
|
|
121
|
+
export {};
|
|
122
|
+
//# sourceMappingURL=playcanvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playcanvas.d.ts","sourceRoot":"","sources":["../../src/drivers/playcanvas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { fromCanonicalDirection, fromCanonicalPosition, fromCanonicalQuat, } from "@uptimizr/sdk-core";
|
|
2
|
+
/** Find a skeleton bone of `entity` by name across its mesh-instance skins, or `null`. */
|
|
3
|
+
function findEntityBone(entity, boneId) {
|
|
4
|
+
const instances = [
|
|
5
|
+
...(entity.render?.meshInstances ?? []),
|
|
6
|
+
...(entity.model?.meshInstances ?? []),
|
|
7
|
+
];
|
|
8
|
+
for (const mi of instances) {
|
|
9
|
+
const bone = mi.skinInstance?.bones?.find((b) => b?.name === boneId);
|
|
10
|
+
if (bone)
|
|
11
|
+
return bone;
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
/** Resolve a node entry (direct ref or `() => node`) to a live Entity, or `null`. */
|
|
16
|
+
function resolveReplayNode(entry) {
|
|
17
|
+
if (!entry)
|
|
18
|
+
return null;
|
|
19
|
+
if (typeof entry === "function") {
|
|
20
|
+
return entry() ?? null;
|
|
21
|
+
}
|
|
22
|
+
return entry;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Walk a `/`-separated {@link NodeTransformEvent.childPath} from a resolved root
|
|
26
|
+
* actor to the descendant entity, matching each segment against direct children
|
|
27
|
+
* by `name` (ADR 0033). Returns `null` if any segment is missing.
|
|
28
|
+
*/
|
|
29
|
+
function resolveChildByPath(root, childPath) {
|
|
30
|
+
let cur = root;
|
|
31
|
+
for (const segment of childPath.split("/")) {
|
|
32
|
+
if (!segment)
|
|
33
|
+
continue;
|
|
34
|
+
const children = cur?.children;
|
|
35
|
+
if (!Array.isArray(children))
|
|
36
|
+
return null;
|
|
37
|
+
cur = children.find((c) => c.name === segment) ?? null;
|
|
38
|
+
if (!cur)
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return cur;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* PlayCanvas replay driver. Re-drives camera pose (and surfaces pointer/mesh events
|
|
45
|
+
* to host callbacks) in the user's own PlayCanvas app.
|
|
46
|
+
*
|
|
47
|
+
* ## Coordinate frame (canonical → PlayCanvas)
|
|
48
|
+
* Events arrive in the canonical **left-handed, y-up** frame (ADR 0018); PlayCanvas
|
|
49
|
+
* is **right-handed**, so world-space data is converted back with the shared
|
|
50
|
+
* `fromCanonical*` helpers (the Z-negation reflection is its own inverse). Camera
|
|
51
|
+
* orientation is applied via `camera.lookAt(target)` — `lookAt` resolves
|
|
52
|
+
* PlayCanvas' forward-axis convention internally, so the driver does **not**
|
|
53
|
+
* re-derive a forward axis from the recorded direction (avoiding the orientation
|
|
54
|
+
* trap documented in sdk-core `coordinates.ts`).
|
|
55
|
+
*
|
|
56
|
+
* It only reads/writes the scene — it never emits analytics events (ADR 0006).
|
|
57
|
+
*/
|
|
58
|
+
export function createPlayCanvasReplayDriver(options) {
|
|
59
|
+
return {
|
|
60
|
+
reset() {
|
|
61
|
+
// Camera state is fully determined by the next camera_sample, so there is
|
|
62
|
+
// nothing to restore; host-rendered markers are the host's responsibility.
|
|
63
|
+
},
|
|
64
|
+
apply(event) {
|
|
65
|
+
switch (event.type) {
|
|
66
|
+
case "camera_sample": {
|
|
67
|
+
const cam = options.camera;
|
|
68
|
+
if (!cam)
|
|
69
|
+
return;
|
|
70
|
+
// Position: canonical → PlayCanvas (Z-negated).
|
|
71
|
+
const [px, py, pz] = fromCanonicalPosition(event.position, "right");
|
|
72
|
+
cam.setPosition(px, py, pz);
|
|
73
|
+
// Look target: an explicit recorded target, else position + direction —
|
|
74
|
+
// both converted to PlayCanvas' frame. lookAt handles the forward axis.
|
|
75
|
+
let tx;
|
|
76
|
+
let ty;
|
|
77
|
+
let tz;
|
|
78
|
+
if (event.target) {
|
|
79
|
+
[tx, ty, tz] = fromCanonicalPosition(event.target, "right");
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const [dx, dy, dz] = fromCanonicalDirection(event.direction, "right");
|
|
83
|
+
tx = px + dx;
|
|
84
|
+
ty = py + dy;
|
|
85
|
+
tz = pz + dz;
|
|
86
|
+
}
|
|
87
|
+
cam.lookAt(tx, ty, tz);
|
|
88
|
+
// PlayCanvas' camera FOV is in degrees; the wire carries radians.
|
|
89
|
+
if (cam.camera && typeof event.fov === "number") {
|
|
90
|
+
cam.camera.fov = (event.fov * 180) / Math.PI;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "pointer_move":
|
|
95
|
+
case "pointer_click":
|
|
96
|
+
case "pointer_down":
|
|
97
|
+
case "pointer_up": {
|
|
98
|
+
// Convert the world-space hit point back into PlayCanvas' frame so a host
|
|
99
|
+
// marker lands correctly; screen coords are engine-independent.
|
|
100
|
+
const hitPoint = event.hitPoint
|
|
101
|
+
? fromCanonicalPosition(event.hitPoint, "right")
|
|
102
|
+
: undefined;
|
|
103
|
+
options.onPointer?.(event.screen, hitPoint, event.hitMesh, event.type);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case "mesh_interaction": {
|
|
107
|
+
const point = event.point ? fromCanonicalPosition(event.point, "right") : undefined;
|
|
108
|
+
options.onMeshInteraction?.(event.mesh, event.kind, point);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "custom":
|
|
112
|
+
options.onCustom?.(event.name, event.props, event.ts);
|
|
113
|
+
break;
|
|
114
|
+
case "input_action":
|
|
115
|
+
options.onInputAction?.({
|
|
116
|
+
action: event.action,
|
|
117
|
+
code: event.code,
|
|
118
|
+
button: event.button,
|
|
119
|
+
pressed: event.pressed,
|
|
120
|
+
source: event.source,
|
|
121
|
+
}, event.ts);
|
|
122
|
+
break;
|
|
123
|
+
case "viewport_resize":
|
|
124
|
+
options.onLifecycle?.({ type: "viewport_resize", width: event.width, height: event.height, dpr: event.dpr }, event.ts);
|
|
125
|
+
break;
|
|
126
|
+
case "visibility_change":
|
|
127
|
+
options.onLifecycle?.({ type: "visibility_change", state: event.state }, event.ts);
|
|
128
|
+
break;
|
|
129
|
+
case "focus_change":
|
|
130
|
+
options.onLifecycle?.({ type: "focus_change", focused: event.focused }, event.ts);
|
|
131
|
+
break;
|
|
132
|
+
case "context_lost":
|
|
133
|
+
options.onLifecycle?.({ type: "context_lost" }, event.ts);
|
|
134
|
+
break;
|
|
135
|
+
case "context_restored":
|
|
136
|
+
options.onLifecycle?.({ type: "context_restored" }, event.ts);
|
|
137
|
+
break;
|
|
138
|
+
case "runtime_error":
|
|
139
|
+
options.onError?.({
|
|
140
|
+
kind: event.kind,
|
|
141
|
+
message: event.message,
|
|
142
|
+
source: event.source,
|
|
143
|
+
lineno: event.lineno,
|
|
144
|
+
colno: event.colno,
|
|
145
|
+
stack: event.stack,
|
|
146
|
+
}, event.ts);
|
|
147
|
+
break;
|
|
148
|
+
case "camera_gesture":
|
|
149
|
+
// Derived navigation annotation (ADR 0025); the camera trajectory is
|
|
150
|
+
// reconstructed from camera_sample, so replay intentionally ignores it.
|
|
151
|
+
break;
|
|
152
|
+
case "node_transform": {
|
|
153
|
+
const node = resolveReplayNode(options.nodes?.[event.nodeId]);
|
|
154
|
+
if (node) {
|
|
155
|
+
if (event.boneId !== undefined) {
|
|
156
|
+
// Tier 2: drive the named bone's parent-relative LOCAL pose. The
|
|
157
|
+
// canonical reflection conjugates a local transform the same way it
|
|
158
|
+
// does a world one, so the same convert applies (ADR 0027).
|
|
159
|
+
const bone = findEntityBone(node, event.boneId);
|
|
160
|
+
if (bone) {
|
|
161
|
+
const [px, py, pz] = fromCanonicalPosition(event.position, "right");
|
|
162
|
+
bone.setLocalPosition?.(px, py, pz);
|
|
163
|
+
const [qx, qy, qz, qw] = fromCanonicalQuat(event.rotation, "right");
|
|
164
|
+
bone.setLocalRotation?.(qx, qy, qz, qw);
|
|
165
|
+
if (event.scale) {
|
|
166
|
+
bone.setLocalScale?.(event.scale[0], event.scale[1], event.scale[2]);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// Tier 1: the declared root (no childPath) or a subtree child resolved
|
|
172
|
+
// by its relative path (ADR 0033). Both carry a world-frame pose;
|
|
173
|
+
// `setPosition`/`setRotation` are world setters, correct for either.
|
|
174
|
+
const target = event.childPath === undefined ? node : resolveChildByPath(node, event.childPath);
|
|
175
|
+
if (target) {
|
|
176
|
+
const [px, py, pz] = fromCanonicalPosition(event.position, "right");
|
|
177
|
+
target.setPosition?.(px, py, pz);
|
|
178
|
+
const [qx, qy, qz, qw] = fromCanonicalQuat(event.rotation, "right");
|
|
179
|
+
target.setRotation?.(qx, qy, qz, qw);
|
|
180
|
+
if (event.scale) {
|
|
181
|
+
target.setLocalScale?.(event.scale[0], event.scale[1], event.scale[2]);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Forward the (frame-converted) sample so the host can render a proxy
|
|
187
|
+
// marker or annotate the timeline; `boneId` is set for Tier-2 samples,
|
|
188
|
+
// `childPath` for Tier-1 subtree children (ADR 0033).
|
|
189
|
+
const fwdPos = fromCanonicalPosition(event.position, "right");
|
|
190
|
+
const fwdRot = fromCanonicalQuat(event.rotation, "right");
|
|
191
|
+
options.onNodeTransform?.(event.nodeId, {
|
|
192
|
+
boneId: event.boneId,
|
|
193
|
+
childPath: event.childPath,
|
|
194
|
+
position: fwdPos,
|
|
195
|
+
rotation: fwdRot,
|
|
196
|
+
scale: event.scale,
|
|
197
|
+
}, event.ts);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
default:
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=playcanvas.js.map
|