export class Resource { constructor(public hash: Uint8Array) { } toString() { return Array.from(this.hash) .map(e => e.toString(16).padStart(2, "0")) .join("") } async download_raw() { const res = await fetch(`http://127.0.0.1:28556/${this.toString()}`) if (!res.ok) throw new Error("aaaa"); return await res.bytes() } } export function read_res_table(buffer: Uint8Array, cb: (key: string, value: Uint8Array) => void) { const view = new DataView(buffer.buffer) let p = 0 while (p < view.byteLength) { const key_len = view.getInt16(p, true) p += 2 const value_len = view.getInt16(p, true) p += 2 const key = String.fromCharCode(...buffer.slice(p, p + key_len)) p += key_len const value = buffer.slice(p, p + value_len) p += value_len cb(key, value) } } function get_vec3(b: Uint8Array): Vec3 { const k = new DataView(b.buffer) return { x: k.getFloat32(0, true), y: k.getFloat32(4, true), z: k.getFloat32(8, true), } } function get_aabb(b: Uint8Array): AABB { return { min: get_vec3(b.slice(0, 12)), max: get_vec3(b.slice(12, 24)) } } export async function get_respackentry(r: Resource): Promise { console.log(`load respackentry ${r}`); const buf = await r.download_raw(); const o: RespackEntry = {} read_res_table(buf, (key, value) => { switch (key) { case "c_spatial_index": o.c_spatial_index = new Resource(value) break default: } }); return o } export async function get_spatialindex(r: Resource): Promise { // console.log(`load spatialindex ${r}`); const buf = await r.download_raw(); const o: SpatialIndex = { child: [] } read_res_table(buf, (key, value) => { switch (key) { case "prefab": o.prefab = new Resource(value) break case "child": o.child.push([ get_aabb(value.slice(0, 24)), new Resource(value.slice(24, 56)) ]) break default: } }); return o } export async function get_prefab(r: Resource): Promise { console.log(`load prefab ${r}`); const buf = await r.download_raw(); const o: Prefab = { graphics: [] } read_res_table(buf, (key, value) => { switch (key) { case "graphics": o.graphics.push([undefined as unknown as Affine3, new Resource(value.slice(48, 80))]) break default: } }); return o } export async function get_graphics_part(r: Resource): Promise { console.log(`load graphics ${r}`); return new GraphicsPart(await r.download_raw()) } export interface Vec3 { x: number, y: number, z: number } export interface AABB { min: Vec3, max: Vec3 } export interface Mat3 { x: Vec3, y: Vec3, z: Vec3 } export interface Affine3 { basis: Mat3, origin: Vec3 } export interface SpatialIndex { level?: number prefab?: Resource child: [AABB, Resource][] } export interface RespackEntry { c_spatial_index?: Resource } export interface Prefab { graphics: [Affine3, Resource][] } export class GraphicsPart { commands: GraphicsCommand[] | undefined constructor(public buffer: Uint8Array) { } read(): GraphicsCommand[] { if (this.commands) return this.commands const out: GraphicsCommand[] = [] let p = 0 while (p < this.buffer.length) { const step = this.buffer[p + 0] const kind = this.buffer[p + 1] p += 2 if (kind == 0) { if (step == 0) out.push({ no_fill: true }) else if (step == 1) out.push({ fill: this.buffer[p + 0] }) else throw new Error("bbb"); } else if (kind == 1) { if (step == 0) out.push({ no_stroke: true }) else if (step == 1) out.push({ stroke: this.buffer[p + 0] }) else throw new Error("ccc"); } else throw new Error("aaa"); p += step } this.commands = out return out } } export interface GraphicsCommand { no_fill?: true, fill?: number, no_stroke?: true stroke?: number point?: Vec3 }