summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-03-28 01:56:21 +0100
committermetamuffin <metamuffin@disroot.org>2025-03-28 01:56:21 +0100
commit3be73a3c424bd7f72e6efecd6d954fbb08e960e0 (patch)
treedc8ed8557d352845ce4f892aa1f38b1df0cc638c
parent94837b601fb757ba6582602264b598df39f303fe (diff)
downloadwearemapping-3be73a3c424bd7f72e6efecd6d954fbb08e960e0.tar
wearemapping-3be73a3c424bd7f72e6efecd6d954fbb08e960e0.tar.bz2
wearemapping-3be73a3c424bd7f72e6efecd6d954fbb08e960e0.tar.zst
a
-rw-r--r--viewer/helper.ts5
-rw-r--r--viewer/main.ts98
-rw-r--r--viewer/resources.ts154
3 files changed, 222 insertions, 35 deletions
diff --git a/viewer/helper.ts b/viewer/helper.ts
new file mode 100644
index 0000000..e8b675f
--- /dev/null
+++ b/viewer/helper.ts
@@ -0,0 +1,5 @@
+import { AABB } from "./resources.ts";
+
+export function aabb_overlap(a: AABB, b: AABB): boolean {
+ return true
+} \ No newline at end of file
diff --git a/viewer/main.ts b/viewer/main.ts
index 5aa1df6..e0c2fde 100644
--- a/viewer/main.ts
+++ b/viewer/main.ts
@@ -1,5 +1,8 @@
/// <reference lib="dom" />
+import { aabb_overlap } from "./helper.ts";
+import { AABB, get_graphics_part, get_prefab, get_respackentry, get_spatialindex, GraphicsPart, Prefab, Resource, SpatialIndex } from "./resources.ts";
+
const canvas = document.createElement("canvas")
canvas.style.position = "absolute"
canvas.style.top = "0px"
@@ -9,53 +12,78 @@ canvas.style.height = "100vh"
document.body.append(canvas)
const ctx = canvas.getContext("2d")!
+let loader: Loader | undefined;
+
function draw() {
ctx.fillStyle = "black"
ctx.fillRect(0, 0, canvas.width, canvas.height)
+
+ loader?.root.children
+
+ requestAnimationFrame(draw)
}
canvas.addEventListener("resize", () => draw())
draw()
-class Resource<T> {
- 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()
- }
-}
-
-function read_res_table(buffer: Uint8Array): { [key: string]: Uint8Array[] } {
- const view = new DataView(buffer.buffer)
- let p = 0
- const out: { [key: string]: Uint8Array[] } = {}
- 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
- out[key] ??= []
- out[key].push(value)
- }
- return out
-}
async function init() {
const resp = await fetch("http://127.0.0.1:28556/entry")
if (!resp.ok) throw new Error("aaaa");
const entry_res = new Resource(await resp.bytes())
- const entry = await entry_res.download_raw()
- console.log(read_res_table(entry))
+ const entry = await get_respackentry(entry_res)
+ const root = await get_spatialindex(entry.c_spatial_index!)
+ loader = new Loader(root)
+ console.log("begin load");
+ await loader.update()
+ console.log("end load");
+
+}
+init()
+
+let limit = 100;
+
+class Loader {
+ view: AABB
+ root: SNode
+ constructor(root: SpatialIndex) {
+ this.view = { min: { x: 0, y: 0, z: 0 }, max: { x: 1, y: 1, z: 1 } }
+ this.root = new SNode(root)
+ }
+ async update() {
+ await this.root.load(this.view)
+ }
+}
+class SNode {
+ children: (SNode | undefined)[]
+ prefab?: Prefab
+ graphics?: GraphicsPart
+ aabb?: AABB
+ constructor(public data: SpatialIndex) {
+ this.children = new Array(data.child.length).fill(undefined)
+ }
+ async load(view: AABB) {
+ if (this.data.prefab && !this.prefab) this.prefab = await get_prefab(this.data.prefab)
+ if (this.prefab?.graphics[0] && !this.graphics) this.graphics = await get_graphics_part(this.prefab.graphics[0][1])
+ for (let i = 0; i < this.data.child.length; i++) {
+ const [aabb, child_data_hash] = this.data.child[i];
+ if (!aabb_overlap(aabb, view)) continue
+ if (this.children[i] == undefined) {
+ this.children[i] = new SNode(await get_spatialindex(child_data_hash))
+ limit -= 1
+ if (limit <= 0) return
+ }
+ await this.children[i]!.load(view)
+ }
+ }
+ draw() {
+ for (const c of this.children) {
+ if (c) c.draw()
+ }
+ if (!this.graphics) return
+ for (const c of this.graphics.read()) {
+ console.log(c);
+ }
+ }
}
-init() \ No newline at end of file
diff --git a/viewer/resources.ts b/viewer/resources.ts
new file mode 100644
index 0000000..0340e31
--- /dev/null
+++ b/viewer/resources.ts
@@ -0,0 +1,154 @@
+
+export class Resource<T> {
+ 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<RespackEntry>): Promise<RespackEntry> {
+ 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<SpatialIndex>): Promise<SpatialIndex> {
+ // 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<Prefab>): Promise<Prefab> {
+ 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<GraphicsPart>): Promise<GraphicsPart> {
+ 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<undefined>
+ child: [AABB, Resource<undefined>][]
+}
+export interface RespackEntry {
+ c_spatial_index?: Resource<SpatialIndex>
+}
+export interface Prefab {
+ graphics: [Affine3, Resource<GraphicsPart>][]
+}
+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
+} \ No newline at end of file