Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions apps/viewer-web/e2e/world-cell-real-camera.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ test("official photographic views sustain authoritative World Cell geometry", as
if (observation.done || observation.fixtureError) break;
}
await expect(page.locator("html")).toHaveAttribute("data-world-cell-sealed", "true", { timeout: 15_000 });
await page.locator("#stop-button").click();
await expect(page.locator("html")).toHaveAttribute("data-capture-active", "false");
await expect(page.locator("html")).toHaveAttribute("data-source-frame-retained", "true");
await expect(page.locator("html")).toHaveAttribute("data-reconstruction-visible", "true");
await expect(page.locator("#capture-state")).toHaveText("RECONSTRUCTION READY");
await expect(page.locator("#stage-message b")).toHaveText("RELATIVE RECONSTRUCTION READY");

const terminal = await page.locator("html").evaluate((node) => ({
surfels: Number(node.dataset.authoritativeSurfels ?? 0),
Expand Down Expand Up @@ -188,14 +194,15 @@ test("official photographic views sustain authoritative World Cell geometry", as
expect(maximumRevision).toBeGreaterThanOrEqual(3);
expect(terminal.surfels).toBeGreaterThanOrEqual(2_000);
expect(terminal.revision).toBeGreaterThanOrEqual(3);
expect(terminal.surfaceMode).toBe("relative-live-preview");
expect(terminal.surfaceMode).toBe("relative-native-triangles");
expect(terminal.surfaceVertices).toBeGreaterThanOrEqual(300);
expect(terminal.surfaceTriangles).toBeGreaterThanOrEqual(100);
expect(terminal.surfaceMaximumRadius).toBe(0);
expect(terminal.surfaceMaximumAngularRadius).toBe(0);
expect(terminal.surfaceBuildMilliseconds).toBeLessThan(60);
expect(await page.locator("#camera").evaluate((video) => Number.parseFloat(getComputedStyle(video).opacity))).toBeGreaterThanOrEqual(0.99);
expect(await page.locator(".stage-panel").evaluate((stage) => stage.classList.contains("has-authoritative-surface"))).toBe(false);
expect(await page.locator("#camera").evaluate((video) => Number.parseFloat(getComputedStyle(video).opacity))).toBe(0);
expect(await page.locator("#retained-source-frame").evaluate((canvas) => Number.parseFloat(getComputedStyle(canvas).opacity))).toBeGreaterThanOrEqual(0.5);
expect(await page.locator(".stage-panel").evaluate((stage) => stage.classList.contains("has-authoritative-surface"))).toBe(true);
expect(terminal.rootprint).toMatch(/^[0-9A-F]{16}$/u);
expect(pageErrors).toEqual([]);
});
4 changes: 4 additions & 0 deletions apps/viewer-web/scripts/verify-live-release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ if (keyxymManifest.schema !== "keyxym.browser-runtime-provenance/v11" ||

const legacyTheater = (await fetchBytes("world-cell-theater.html")).bytes.toString("utf8");
if (!/type="module"[^>]+src="\.\/assets\//u.test(legacyTheater) ||
!legacyTheater.includes('id="retained-source-frame"') ||
legacyTheater.includes("/src/world-cell-authority-entry.ts")) {
throw new Error("live legacy World Cell Theater route is not the built application entry");
}
Expand All @@ -111,6 +112,7 @@ if (!canonicalInventory || canonicalRoute.bytes.byteLength !== canonicalInventor
}
if (!/<base href="\.\.\/">/u.test(canonicalTheater) ||
!/type="module"[^>]+src="\.\.\/assets\//u.test(canonicalTheater) ||
!canonicalTheater.includes('id="retained-source-frame"') ||
canonicalTheater.includes("/src/world-cell-authority-entry.ts")) {
throw new Error("live extensionless World Cell Theater route lacks its root asset contract");
}
Expand All @@ -133,6 +135,8 @@ for (const marker of [
"keyxym-v26-reality-authority-spatial-surface-3",
"native-triangles",
"relative-live-preview",
"relative-native-triangles",
"RELATIVE RECONSTRUCTION READY",
"tessaryn/spatial-calibration/v1",
"Metric capture requires an exact browser media-frame identity",
"Host-verified synchronized RGB-D",
Expand Down
9 changes: 5 additions & 4 deletions apps/viewer-web/src/world-cell-guidance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,21 @@ export function installWorldCellGuidance(): () => void {

const update = () => {
if (root.dataset.keyxymAuthority !== "verified") return;
if (root.dataset.captureActive !== "true") return;
const authorityStage = root.dataset.authorityStage ?? "forming";
if (authorityStage !== "forming") {
establishedTracking = true;
return;
}
if ((capture.textContent ?? "").trim() === "READY") return;

const mask = numericAttribute(root, "authorityRejectionMask");
if (mask & REJECT_REPROJECTION) {
heading.textContent = "REDUCE MOTION BLUR";
detail.textContent = "Move more slowly, keep textured edges sharp, and maintain the same objects in view until reprojection returns below the authority limit.";
} else if (mask & MOTION_REJECTIONS) {
heading.textContent = establishedTracking ? "REACQUIRE TRACKING" : "CREATE 3D PARALLAX";
detail.textContent = "Move slowly sideways around textured objects at different depths. Keep them in view; avoid flat walls, digital screens, blur, and repeating patterns.";
heading.textContent = establishedTracking ? "RELOCALIZING / MAP RETAINED" : "CREATE 3D PARALLAX";
detail.textContent = establishedTracking
? "The accumulated reconstruction is intact. Move slowly back toward a textured view already observed, then continue sideways."
: "Move slowly sideways around textured objects at different depths. Keep them in view; avoid flat walls, digital screens, blur, and repeating patterns.";
} else if (mask & (REJECT_GEOMETRY | REJECT_CONTINUITY)) {
heading.textContent = "BUILD VERIFIED GEOMETRY";
detail.textContent = "Continue a slow arc, then revisit the same surfaces so Keyxym can confirm them across frames before enabling a Moment.";
Expand Down
154 changes: 129 additions & 25 deletions apps/viewer-web/src/world-cell-theater-v26.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ function validateMoment(moment: MomentRecord, expectedParent: string): void {

class TheaterController {
private readonly video = element<VideoWithFrameCallback>("camera");
private readonly retainedSourceFrame = element<HTMLCanvasElement>("retained-source-frame");
private readonly canvas = element<HTMLCanvasElement>("stage");
private readonly renderer = new THREE.WebGLRenderer({
canvas: this.canvas,
Expand Down Expand Up @@ -283,6 +284,8 @@ class TheaterController {
private frameNumber = 0;
private geometrySnapshot: KeyxymGeometrySnapshot = { revision: 0n, surfels: [] };
private surfaceSnapshot: KeyxymSurfaceSnapshot = { revision: 0n, vertices: [] };
private renderedSurfelCount = 0;
private renderedSurfaceTriangles = 0;
private pose: KeyxymPoseEstimate | null = null;
private quality: KeyxymQuality | null = null;
private authority: KeyxymAuthorityDecision | null = null;
Expand Down Expand Up @@ -348,6 +351,8 @@ class TheaterController {
this.runtime = await KeyxymV26TheaterRuntime.load(this.manifest);
this.bindControls();
document.documentElement.dataset.worldCellController = "keyxym-v026-worker-v1";
document.documentElement.dataset.captureActive = "false";
document.documentElement.dataset.reconstructionVisible = "false";
setText("backend-name", "KEYXYM V0.26 / REALITY");
setText("adapter-name", this.manifest.source_commit.slice(0, 12).toUpperCase());
setText("gpu-badge", "WORKER WASM READY");
Expand Down Expand Up @@ -442,30 +447,58 @@ class TheaterController {
this.video.srcObject = this.mediaStream;
await this.video.play();
this.running = true;
document.documentElement.dataset.captureActive = "true";
document.documentElement.dataset.sourceFrameRetained = "false";
this.canvas.closest(".stage-panel")?.classList.remove("has-retained-source");
this.lastProcessedAt = 0;
element<HTMLButtonElement>("start-button").disabled = true;
element<HTMLButtonElement>("stop-button").disabled = false;
setText("capture-state", "FORMING");
this.updatePresentationMode();
this.hideStageMessage();
await this.refreshSpatialCalibration();
this.scheduleFrame();
}

private stopCamera(): void {
this.running = false;
document.documentElement.dataset.captureActive = "false";
if (this.frameCallback && this.video.cancelVideoFrameCallback) this.video.cancelVideoFrameCallback(this.frameCallback);
if (this.fallbackTimer) window.clearTimeout(this.fallbackTimer);
this.frameCallback = 0;
this.fallbackTimer = 0;
this.retainCurrentSourceFrame();
this.mediaStream?.getTracks().forEach((track) => track.stop());
this.mediaStream = null;
this.video.srcObject = null;
// Keep the ended stream attached so the last sensory frame remains a
// visible reference when no native geometry was defensible. Reset or a
// new capture replaces it explicitly; hardware tracks are already closed.
this.video.pause();
element<HTMLButtonElement>("start-button").disabled = false;
element<HTMLButtonElement>("stop-button").disabled = true;
setText("capture-state", "READY");
this.showStoppedResult();
this.updateControls();
}

private retainCurrentSourceFrame(): void {
const width = this.video.videoWidth;
const height = this.video.videoHeight;
if (width < 1 || height < 1) {
document.documentElement.dataset.sourceFrameRetained = "false";
return;
}
this.retainedSourceFrame.width = width;
this.retainedSourceFrame.height = height;
const context = this.retainedSourceFrame.getContext("2d", { alpha: false });
if (!context) {
document.documentElement.dataset.sourceFrameRetained = "false";
return;
}
context.drawImage(this.video, 0, 0, width, height);
document.documentElement.dataset.sourceFrameRetained = "true";
this.canvas.closest(".stage-panel")?.classList.add("has-retained-source");
}

private scheduleFrame(): void {
if (!this.running) return;
if (this.video.requestVideoFrameCallback) {
Expand All @@ -485,7 +518,8 @@ class TheaterController {
try {
await this.processFrame(now, frameIdentity);
} catch (error) {
this.freezeOnTrackingFailure(error);
if (this.running) this.handleFrameFailure(error);
else this.showStoppedResult();
} finally {
this.processing = false;
}
Expand Down Expand Up @@ -582,10 +616,12 @@ class TheaterController {
}
}
}
if (previouslyRecovered && !result.pose.recovered) {
this.setStageMessage("TRACKING FROZEN", "Authoritative geometry is frozen. Return to a textured, previously observed view for relocalization.", true);
} else if (result.pose.recovered) {
this.hideStageMessage();
if (this.running) {
if (previouslyRecovered && !result.pose.recovered) {
this.setStageMessage("RELOCALIZING", "The accumulated reconstruction is retained. Return to a textured, previously observed view to continue it.", true);
} else if (result.pose.recovered) {
this.hideStageMessage();
}
}
document.documentElement.dataset.formingSamples = String(result.forming.length);
document.documentElement.dataset.authoritativeSurfels = String(this.geometrySnapshot.surfels.length);
Expand All @@ -596,6 +632,7 @@ class TheaterController {
document.documentElement.dataset.sealAllowed = String(result.authority.sealAllowed);
if (result.authority.momentAllowed) document.documentElement.dataset.everMomentReady = "true";
if (result.authority.sealAllowed) document.documentElement.dataset.everSealReady = "true";
if (!this.running) this.showStoppedResult();
}

private updateFormingCloud(samples: KeyxymFormingSample[], coverage: number): void {
Expand Down Expand Up @@ -685,18 +722,9 @@ class TheaterController {
this.authoritySurface.position.set(0, 0, 0);
this.authoritySurface.scale.setScalar(1);
const nativeTriangles = nativeSurface ? nativeVertices.length / 3 : 0;
const hasSurface = nativeTriangles >= 16;
const hasContinuum = nativeTriangles >= 64;
const metricContinuum = this.quality?.metricScale === true && hasContinuum;
this.authoritySurface.visible = metricContinuum;
this.authorityCloud.visible = this.quality?.metricScale === true && !hasSurface;
this.formingCloud.visible = this.quality?.metricScale === true && surfels.length < 1_024;
const stage = this.canvas.closest(".stage-panel");
stage?.classList.toggle("has-authority", hasSurface);
stage?.classList.toggle("has-authoritative-surface", metricContinuum);
document.documentElement.dataset.surfaceMode = this.quality?.metricScale === true
? (nativeSurface ? "native-triangles" : "metric-surfels-pending")
: "relative-live-preview";
this.renderedSurfelCount = surfels.length;
this.renderedSurfaceTriangles = nativeTriangles;
this.updatePresentationMode();
document.documentElement.dataset.surfacePatches = "0";
document.documentElement.dataset.surfaceVertices = String(nativeSurface ? nativeVertices.length : 0);
document.documentElement.dataset.surfaceTriangles = String(nativeTriangles);
Expand All @@ -708,6 +736,44 @@ class TheaterController {
setText("surfel-count", surfels.length.toLocaleString());
}

private updatePresentationMode(): void {
const metric = this.quality?.metricScale === true;
const hasSurface = this.renderedSurfaceTriangles >= 16;
const hasContinuum = this.renderedSurfaceTriangles >= 64;
const metricContinuum = metric && hasContinuum;
const relativeSurface = !metric && !this.running && hasSurface;
const relativePoints = !metric && !this.running && !hasSurface && this.renderedSurfelCount > 0;
const surfaceVisible = metricContinuum || relativeSurface;
const pointsVisible = (metric && !hasSurface && this.renderedSurfelCount > 0) || relativePoints;
const reconstructionVisible = surfaceVisible || pointsVisible;

const surfaceMaterial = this.authoritySurface.material as THREE.MeshStandardMaterial;
if (surfaceMaterial.transparent !== relativeSurface) {
surfaceMaterial.transparent = relativeSurface;
surfaceMaterial.needsUpdate = true;
}
surfaceMaterial.opacity = relativeSurface ? 0.72 : 1;
surfaceMaterial.depthWrite = !relativeSurface;
this.authoritySurface.visible = surfaceVisible;
this.authorityCloud.visible = pointsVisible;
this.formingCloud.visible = this.running && metric && this.renderedSurfelCount < 1_024;
const stage = this.canvas.closest(".stage-panel");
stage?.classList.toggle("has-authority", hasSurface || this.renderedSurfelCount > 0);
stage?.classList.toggle("has-authoritative-surface", surfaceVisible);
stage?.classList.toggle("has-metric-continuum", metricContinuum);
stage?.classList.toggle("has-relative-reconstruction", relativeSurface || relativePoints);
document.documentElement.dataset.reconstructionVisible = String(reconstructionVisible);
document.documentElement.dataset.surfaceMode = metric
? (hasSurface ? "native-triangles" : "metric-surfels-pending")
: this.running
? "relative-live-preview"
: hasSurface
? "relative-native-triangles"
: relativePoints
? "relative-native-surfels"
: "relative-source-frame";
}

private updateQualityUi(): void {
if (!this.pose || !this.quality || !this.authority) return;
const labels: Record<KeyxymAuthorityDecision["stage"], string> = {
Expand Down Expand Up @@ -1091,15 +1157,42 @@ class TheaterController {
element<HTMLButtonElement>("send-button").disabled = !this.sealedCell || !this.channel || this.channel.readyState !== "open";
}

private freezeOnTrackingFailure(error: unknown): void {
private handleFrameFailure(error: unknown): void {
const reason = error instanceof Error ? error.message : String(error);
setText("pose-state", "TRACKING FROZEN");
setText("capture-state", "FROZEN");
setText("cell-state", "WORLD CELL / AUTHORITY FROZEN");
this.setStageMessage("TRACKING FROZEN", `${reason}. Authoritative geometry was not advanced.`, true);
console.error("World Cell frame rejected", error);
setText("pose-state", "FRAME REJECTED");
setText("capture-state", "CAPTURE ACTIVE");
this.setStageMessage("FRAME REJECTED", `${reason}. The accumulated reconstruction is intact; capture can continue.`, true);
this.updateControls();
}

private showStoppedResult(): void {
this.updatePresentationMode();
const metric = this.quality?.metricScale === true;
const reconstructionVisible = document.documentElement.dataset.reconstructionVisible === "true";
if (reconstructionVisible) {
const title = metric ? "METRIC CONTINUUM READY" : "RELATIVE RECONSTRUCTION READY";
setText("pose-state", "RECONSTRUCTION READY");
setText("capture-state", "RECONSTRUCTION READY");
this.setStageMessage(
title,
"Capture stopped. Native geometry remains visible over the retained sensory frame; drag to inspect it or restart the camera to extend it.",
true,
);
} else {
setText("pose-state", "CAPTURE PAUSED");
setText("capture-state", "PAUSED");
this.setStageMessage(
"CAPTURE PAUSED",
"No native reconstruction was established. The final source frame is retained; restart and move slowly sideways around textured objects at different depths.",
true,
);
}
if (this.sealedCell) {
setText("cell-state", `WORLD CELL / SEALED / ${this.sealedCell.scaleState.toUpperCase()} / ${this.sealedCell.moments.length} MOMENTS`);
}
}

private setStageMessage(title: string, detail: string, visible = true): void {
const message = element("stage-message");
const heading = message.querySelector("b");
Expand Down Expand Up @@ -1167,6 +1260,9 @@ class TheaterController {

private async reset(): Promise<void> {
this.stopCamera();
this.video.srcObject = null;
this.retainedSourceFrame.width = 1;
this.retainedSourceFrame.height = 1;
if (this.playTimer) window.clearInterval(this.playTimer);
this.playTimer = 0;
this.runtime?.destroy();
Expand All @@ -1176,6 +1272,8 @@ class TheaterController {
this.analysisIntervalMs = 50;
this.geometrySnapshot = { revision: 0n, surfels: [] };
this.surfaceSnapshot = { revision: 0n, vertices: [] };
this.renderedSurfelCount = 0;
this.renderedSurfaceTriangles = 0;
this.pose = null; this.quality = null; this.authority = null; this.receipts = null;
this.sourceCommitment = ZERO_DIGEST;
this.formingSamples = [];
Expand All @@ -1191,7 +1289,13 @@ class TheaterController {
setPointGeometry(this.formingGeometry, new Float32Array(), new Float32Array(), new Float32Array(), new Float32Array());
setPointGeometry(this.authorityGeometry, new Float32Array(), new Float32Array(), new Float32Array(), new Float32Array());
setSurfaceGeometry(this.authoritySurfaceGeometry, new Float32Array(), new Float32Array(), new Float32Array());
this.canvas.closest(".stage-panel")?.classList.remove("has-authority", "has-authoritative-surface");
this.canvas.closest(".stage-panel")?.classList.remove(
"has-authority", "has-authoritative-surface", "has-metric-continuum", "has-relative-reconstruction",
"has-retained-source",
);
document.documentElement.dataset.captureActive = "false";
document.documentElement.dataset.reconstructionVisible = "false";
document.documentElement.dataset.sourceFrameRetained = "false";
delete document.documentElement.dataset.surfacePatches;
delete document.documentElement.dataset.surfaceMode;
delete document.documentElement.dataset.surfaceVertices;
Expand Down
Loading
Loading