refactor: make instance type metadata opaque
Amp-Thread-ID: https://ampcode.com/threads/T-019f9d91-77c1-7206-a60f-ed6554ce92ab Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
+12
-17
@@ -1,8 +1,7 @@
|
||||
import { SnapshotReader } from "./render-data-snapshot.js";
|
||||
|
||||
export const VISIBLE = 1;
|
||||
const HEADER_WORDS = 16, SLOT_WORDS = 40, CAPACITY = 1024, SLOT_VERSION = 2;
|
||||
const OP = { IMPORT_GLB: 1, MESH_FLAGS: 2, CREATE_INSTANCE: 3, INSTANCE_VISIBLE: 4, INSTANCE_TRANSFORM: 5, DESTROY_INSTANCE: 6, COMPILE_GRAPH: 7, DROP_GRAPH: 8, SWITCH_GRAPH: 9, SET_INSTANCE_TYPE: 10 };
|
||||
const OP = { IMPORT_GLB: 1, CREATE_INSTANCE: 3, INSTANCE_TRANSFORM: 5, DESTROY_INSTANCE: 6, COMPILE_GRAPH: 7, DROP_GRAPH: 8, SWITCH_GRAPH: 9, SET_INSTANCE_TYPE: 10 };
|
||||
const HANDLE_TOKEN = Symbol("renderer handle");
|
||||
|
||||
export class RendererError extends Error {
|
||||
@@ -157,11 +156,11 @@ export class RendererClient {
|
||||
return promise;
|
||||
}
|
||||
|
||||
#mesh(handle, defaultType = Array(16).fill(0)) {
|
||||
#mesh(handle, defaultInstanceHandle, defaultType = Array(16).fill(0)) {
|
||||
return new Mesh(HANDLE_TOKEN,
|
||||
visible => { validateVisible(visible); return this.#enqueue(OP.MESH_FLAGS, [...handle, visible ? 1 : 0]); },
|
||||
async (transform, {type = defaultType, visible} = {}) => {
|
||||
type = typeWords(type); if (visible !== undefined) { validateVisible(visible); type[0] = (type[0] & ~1) | Number(visible); }
|
||||
this.#instance(defaultInstanceHandle),
|
||||
async (transform, {type = defaultType} = {}) => {
|
||||
type = typeWords(type);
|
||||
const result = await this.#enqueue(OP.CREATE_INSTANCE, [...handle, ...floatWords(transform), ...type]);
|
||||
return this.#instance(result);
|
||||
});
|
||||
@@ -169,7 +168,6 @@ export class RendererClient {
|
||||
|
||||
#instance(handle) {
|
||||
return new Instance(HANDLE_TOKEN,
|
||||
visible => { validateVisible(visible); return this.#enqueue(OP.INSTANCE_VISIBLE, [...handle, visible ? 1 : 0]); },
|
||||
type => this.#enqueue(OP.SET_INSTANCE_TYPE, [...handle, ...typeWords(type)]),
|
||||
transform => this.#enqueue(OP.INSTANCE_TRANSFORM, [...handle, ...floatWords(transform)]),
|
||||
() => this.#enqueue(OP.DESTROY_INSTANCE, [...handle]));
|
||||
@@ -185,7 +183,7 @@ export class RendererClient {
|
||||
else throw new TypeError("GLB source must be URL, File, or ArrayBuffer");
|
||||
if (this.#disposed) throw new RendererError("DISPOSED");
|
||||
const result = await this.#withPayload(buffer, OP.IMPORT_GLB, [framing === "interior" ? 1 : 0]);
|
||||
return result.meshes.map(item => this.#mesh(item.handle, item.defaultType));
|
||||
return result.meshes.map(item => this.#mesh(item.handle, item.defaultInstance, item.defaultType));
|
||||
}
|
||||
|
||||
|
||||
@@ -260,7 +258,6 @@ function validateCompiledId(compiledId) {
|
||||
if (!Array.isArray(compiledId) || compiledId.length !== 2 || compiledId.some(word => !Number.isInteger(word) || word < 0 || word > 0xffffffff)) throw new TypeError("compiledId must contain exactly two uint32 values");
|
||||
}
|
||||
|
||||
function validateVisible(value) { if (typeof value !== "boolean") throw new TypeError("visible must be boolean"); }
|
||||
function typeWords(words) { if (!words || words.length !== 16 || [...words].some(x => !Number.isInteger(x) || x < 0 || x > 0xffffffff)) throw new TypeError("type must contain exactly 16 uint32 values"); return Array.from(words, x => x >>> 0); }
|
||||
|
||||
function floatWords(matrix) {
|
||||
@@ -269,27 +266,25 @@ function floatWords(matrix) {
|
||||
}
|
||||
|
||||
class Mesh {
|
||||
#setVisible; #createInstance;
|
||||
constructor(token, setVisible, createInstance) {
|
||||
#defaultInstance; #createInstance;
|
||||
constructor(token, defaultInstance, createInstance) {
|
||||
if (token !== HANDLE_TOKEN) throw new TypeError("Mesh cannot be constructed directly");
|
||||
this.#setVisible = setVisible;
|
||||
this.#defaultInstance = defaultInstance;
|
||||
this.#createInstance = createInstance;
|
||||
}
|
||||
setVisible(visible) { return this.#setVisible(visible); }
|
||||
get defaultInstance() { return this.#defaultInstance; }
|
||||
createInstance(transform, options = {}) { return this.#createInstance(transform, options); }
|
||||
}
|
||||
|
||||
class Instance {
|
||||
#setVisible; #setType; #setTransform; #destroy; #dead = false;
|
||||
constructor(token, setVisible, setType, setTransform, destroy) {
|
||||
#setType; #setTransform; #destroy; #dead = false;
|
||||
constructor(token, setType, setTransform, destroy) {
|
||||
if (token !== HANDLE_TOKEN) throw new TypeError("Instance cannot be constructed directly");
|
||||
this.#setVisible = setVisible;
|
||||
this.#setType = setType;
|
||||
this.#setTransform = setTransform;
|
||||
this.#destroy = destroy;
|
||||
}
|
||||
#live() { if (this.#dead) throw new RendererError("STALE_HANDLE"); }
|
||||
setVisible(visible) { this.#live(); return this.#setVisible(visible); }
|
||||
setType(words) { this.#live(); return this.#setType(words); }
|
||||
setTransform(transform) { this.#live(); return this.#setTransform(transform); }
|
||||
async destroy() { this.#live(); await this.#destroy(); this.#dead = true; }
|
||||
|
||||
Reference in New Issue
Block a user