EG/templates/webgl/index.html
2026-04-21 15:57:20 +08:00

130 lines
4.4 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>EG WebGL Scene</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="app">
<canvas id="scene-canvas"></canvas>
<div id="status" class="status">Loading scene...</div>
<details class="docs-panel">
<summary>Frontend Integration</summary>
<div class="docs-content">
<p><strong>Selectors:</strong> prefer <code>publicId</code>, then <code>id</code>, then exact <code>name</code>. Duplicate names return a structured error.</p>
<p><strong>Coordinates:</strong> API defaults to <code>{ coordSystem: "panda", space: "local" }</code>. Use <code>coordSystem: "three"</code> to work in Three.js axes. Local means transform relative to parent.</p>
<pre><code>// Same-page JS API
const viewer = window.EGWebGLViewer;
await viewer.ready;
const nodes = viewer.nodes.list();
const hero = nodes.find((node) => node.kind === "model");
viewer.animation.play({ publicId: hero.publicId }, {
clip: "Walk",
loop: true,
restart: true,
});
viewer.nodes.translate({ publicId: hero.publicId }, { x: 1, y: 0, z: 0 });
viewer.nodes.setTransform(
{ publicId: hero.publicId },
{ rotation: { h: 45, p: 0, r: 0 } },
{ coordSystem: "panda", space: "local" },
);
const supportedScripts = viewer.scripts.listSupported();
console.log("web supported scripts", supportedScripts);
viewer.scripts.attach({ publicId: hero.publicId }, {
name: "MoverScript",
params: {
move_axis: "x",
move_distance: 3,
move_speed: 1.5,
},
});
viewer.events.on("animationFinished", (payload) => {
console.log("animation finished", payload);
});</code></pre>
<pre><code>// iframe + postMessage
const iframe = document.querySelector("iframe");
iframe.contentWindow.postMessage({
source: "eg-frontend",
type: "handshake",
id: "hello-1",
}, iframe.src ? new URL(iframe.src).origin : "*");
window.addEventListener("message", (event) => {
if (!event.data || event.data.source !== "eg-webgl") return;
console.log("viewer message", event.data);
});
iframe.contentWindow.postMessage({
source: "eg-frontend",
type: "command",
id: "cmd-1",
command: "scripts.attach",
payload: {
selector: { publicId: "model:asset-guid:root" },
script: {
name: "RotatorScript",
params: { rotation_speed_y: 45 },
},
},
}, iframe.src ? new URL(iframe.src).origin : "*");</code></pre>
<p><strong>Message protocol:</strong> commands use <code>{ source: "eg-frontend", id, type: "command", command, payload }</code>. Responses/events use <code>{ source: "eg-webgl", type, replyTo?, event?, ok?, result?, error? }</code>.</p>
<p><strong>Events:</strong> <code>ready</code>, <code>error</code>, <code>animationFinished</code>, <code>stateChanged</code>.</p>
<p><strong>Script note:</strong> exported WebGL can read existing script metadata, but browser-side runtime only executes Web-supported script adapters, not arbitrary Python files.</p>
<p><strong>Demo page:</strong> after export, open <code>frontend_demo.html</code> to see a working iframe host example.</p>
</div>
</details>
</div>
<script type="module">
const candidates = ["./js/viewer.js", "./viewer.js"];
const statusEl = document.getElementById("status");
if (window.location.protocol === "file:") {
if (statusEl) {
statusEl.textContent = [
"This page cannot run from file://",
"",
"Please start a local HTTP server in the exported WebGL folder, for example:",
"python3 -m http.server 8000",
"",
"Then open:",
"http://127.0.0.1:8000/index.html",
"http://127.0.0.1:8000/frontend_demo.html",
].join("\n");
statusEl.className = "status error";
}
throw new Error("file_protocol_not_supported");
}
let loaded = false;
let lastError = null;
for (const specifier of candidates) {
try {
await import(specifier);
loaded = true;
break;
} catch (error) {
lastError = error;
}
}
if (!loaded) {
console.error(lastError);
if (statusEl) {
statusEl.textContent = `Failed to load viewer bootstrap:\n${String(lastError?.message || lastError)}`;
statusEl.className = "status error";
}
}
</script>
</body>
</html>