76 lines
3.4 KiB
JavaScript
76 lines
3.4 KiB
JavaScript
import { Buffer } from "node:buffer";
|
|
import { readFile, writeFile } from "node:fs/promises";
|
|
import { createRequire } from "node:module";
|
|
import { cwd, stdout } from "node:process";
|
|
import { URL } from "node:url";
|
|
import { chromium } from "@playwright/test";
|
|
import { build } from "esbuild";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const riveRequire = createRequire(require.resolve("@rive-app/react-webgl2"));
|
|
const wasm = await readFile(riveRequire.resolve("@rive-app/webgl2/rive.wasm"));
|
|
const resource = await readFile(new URL("../public/agent/obsidian-water-v1.riv", import.meta.url));
|
|
const harness = `import { RuntimeLoader } from '@rive-app/react-webgl2';
|
|
RuntimeLoader.setWasmUrl('/runtime.wasm');
|
|
window.stills = new Promise(resolve => RuntimeLoader.getInstance(resolve)).then(async runtime => {
|
|
const bytes = new Uint8Array(await (await fetch('/resource.riv')).arrayBuffer());
|
|
const file = await runtime.load(bytes, undefined, false);
|
|
const canvas = document.querySelector('canvas');
|
|
const renderer = runtime.makeRenderer(canvas, true);
|
|
const images = {};
|
|
try {
|
|
for (const state of ['idle', 'asleep']) {
|
|
const artboard = file.defaultArtboard();
|
|
const machine = new runtime.StateMachineInstance(artboard.stateMachineByName('default'), artboard);
|
|
try {
|
|
for (let i = 0; i < machine.inputCount(); i++) {
|
|
const input = machine.input(i);
|
|
input.asBool().value = input.name === state;
|
|
}
|
|
for (let frame = 0; frame < (state === 'idle' ? 90 : 180); frame++) {
|
|
machine.advance(1 / 60);
|
|
artboard.advance(1 / 60);
|
|
}
|
|
renderer.clear();
|
|
renderer.save();
|
|
renderer.align(runtime.Fit.contain, runtime.Alignment.center,
|
|
{ minX: 0, minY: 0, maxX: 192, maxY: 192 }, artboard.bounds);
|
|
artboard.draw(renderer);
|
|
renderer.restore();
|
|
renderer.flush();
|
|
runtime.resolveAnimationFrame();
|
|
images[state] = canvas.toDataURL('image/png');
|
|
} finally { machine.delete(); artboard.delete(); }
|
|
}
|
|
return images;
|
|
} finally { renderer.delete(); file.delete(); }
|
|
});`;
|
|
const bundled = await build({
|
|
stdin: { contents: harness, resolveDir: cwd() },
|
|
bundle: true, write: false, format: "iife"
|
|
});
|
|
const browser = await chromium.launch({ args: ["--enable-unsafe-swiftshader"] });
|
|
try {
|
|
const page = await browser.newPage();
|
|
await page.route("http://persona-render.local/**", async (route) => {
|
|
const path = new URL(route.request().url()).pathname;
|
|
if (path.endsWith(".wasm")) return route.fulfill({ body: wasm, contentType: "application/wasm" });
|
|
if (path.endsWith(".riv")) return route.fulfill({ body: resource });
|
|
if (path.endsWith(".js")) return route.fulfill({ body: bundled.outputFiles[0].text, contentType: "application/javascript" });
|
|
return route.fulfill({
|
|
contentType: "text/html",
|
|
body: '<canvas width="192" height="192"></canvas><script src="/render.js"></script>'
|
|
});
|
|
});
|
|
await page.goto("http://persona-render.local/");
|
|
const images = await page.evaluate("window.stills");
|
|
for (const [state, image] of Object.entries(images)) {
|
|
const name = state === "asleep" ? "offline" : state;
|
|
const path = new URL(`../public/agent/obsidian-water-${name}.png`, import.meta.url);
|
|
await writeFile(path, Buffer.from(image.split(",")[1], "base64"));
|
|
}
|
|
stdout.write("Rendered current Rive material into online and ring-free offline fallback images.\n");
|
|
} finally {
|
|
await browser.close();
|
|
}
|