diff options
-rw-r--r-- | src/main.rs | 30 | ||||
-rw-r--r-- | viewer/loader.ts | 71 | ||||
-rw-r--r-- | viewer/main.ts | 90 | ||||
-rw-r--r-- | viewer/resources.ts | 25 |
4 files changed, 157 insertions, 59 deletions
diff --git a/src/main.rs b/src/main.rs index 1ce78ed..662fc05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ pub mod spatial; use anyhow::Result; -use glam::{Affine3A, DAffine3, DVec3, Vec3}; +use glam::{Affine3A, DAffine3, DVec3}; use indicatif::ProgressIterator; use log::{debug, info}; use osm_pbf_reader::{Blobs, data::primitives::Primitive}; @@ -68,7 +68,7 @@ fn main() -> Result<()> { let store = ResourceStore::new_memory(); info!("Exporting resources..."); - let (_, root) = export_level(&tree, &store)?; + let (_, root) = export_level(&tree, &store, &mut Vec::new())?; let entry = Some(store.set(&RespackEntry { c_spatial_index: vec![root], ..Default::default() @@ -81,26 +81,40 @@ fn main() -> Result<()> { Ok(()) } -fn export_level(node: &Octtree, store: &ResourceStore) -> Result<(AABB, Resource<SpatialIndex>)> { +fn export_level( + node: &Octtree, + store: &ResourceStore, + important_features: &mut Vec<DVec3>, +) -> Result<(AABB, Resource<SpatialIndex>)> { + let mut lod_features = Vec::new(); let child = node .children .iter() .flat_map(|e| e.as_slice()) .flatten() .flatten() - .map(|o| export_level(o, store)) + .map(|o| export_level(o, store, &mut lod_features)) .collect::<Result<Vec<_>>>()?; - let prefab = if node.elems.is_empty() { - None - } else { + let prefab = { + let points = if node.elems.is_empty() { + lod_features + } else { + node.elems.iter().map(|(e, _id)| *e).collect() + }; + let local_origin = node.center; let mut g = GraphicsPart::default(); use weareshared::graphics::GraphicsCommand::*; g.push(StrokeWidth(0.001)); g.push(Stroke(5)); - g.push(Point(Vec3::ZERO)); + for (i, p) in points.into_iter().enumerate() { + g.push(Point((p - local_origin).as_vec3())); + if i % 8 == 0 { + important_features.push(p); + } + } Some(store.set(&Prefab { transform: Some(DAffine3::from_translation(local_origin)), diff --git a/viewer/loader.ts b/viewer/loader.ts new file mode 100644 index 0000000..487102f --- /dev/null +++ b/viewer/loader.ts @@ -0,0 +1,71 @@ +import { Resource, RespackEntry } from "./resources.ts"; + +export abstract class Loader { + num_loading: number = 0 + abstract download(res: Resource<undefined>): Promise<Uint8Array> + abstract get_entry(): Promise<Resource<RespackEntry>>; + abstract wait_ready(): Promise<void> +} + +export class HTTPLoader extends Loader { + constructor() { + super(); + } + // deno-lint-ignore require-await + async wait_ready(): Promise<void> { + return + } + async download<T>(res: Resource<T>): Promise<Uint8Array> { + this.num_loading += 1 + const resp = await fetch(`http://127.0.0.1:28556/${res.toString()}`) + if (!resp.ok) throw new Error("aaaa"); + const buf = await resp.bytes() + this.num_loading -= 1 + return buf + } + async get_entry(): Promise<Resource<RespackEntry>> { + const resp = await fetch("http://127.0.0.1:28556/entry") + if (!resp.ok) throw new Error("aaaa"); + return new Resource(await resp.bytes()) + } +} +export class WSLoader extends Loader { + ws: WebSocket + queue: ((r: Uint8Array) => void)[] = [] + constructor() { + super() + this.ws = new WebSocket(`http://127.0.0.1:28557/`) + this.ws.onopen = () => console.log("open"); + this.ws.onclose = () => console.log("close"); + this.ws.onmessage = m => { + if (typeof m.data == "string") throw new Error(m.data); + if (m.data instanceof Blob) { + const h = this.queue.shift()! + m.data.arrayBuffer() + .then(b => new Uint8Array(b)) + .then(h) + .then(() => this.num_loading -= 1) + } + } + } + wait_ready(): Promise<void> { + return new Promise(resolve => { + if (this.ws.readyState == this.ws.OPEN) return resolve() + else this.ws.addEventListener("open", () => resolve()) + }) + } + download<T>(res: Resource<T>): Promise<Uint8Array> { + return new Promise(resolve => { + this.num_loading += 1 + this.queue.push(resolve) + this.ws.send(res.hash) + }) + } + get_entry(): Promise<Resource<RespackEntry>> { + return new Promise(resolve => { + this.num_loading += 1 + this.queue.push(buf => resolve(new Resource(buf))) + this.ws.send("entry") + }) + } +} diff --git a/viewer/main.ts b/viewer/main.ts index f59bb1c..a85443a 100644 --- a/viewer/main.ts +++ b/viewer/main.ts @@ -1,6 +1,7 @@ /// <reference lib="dom" /> import { aabb_overlap } from "./helper.ts"; +import { WSLoader } from "./loader.ts"; import { AABB, get_graphics_part, get_prefab, get_respackentry, get_spatialindex, GraphicsPart, Prefab, Resource, SpatialIndex } from "./resources.ts"; const canvas = document.createElement("canvas") @@ -12,13 +13,18 @@ canvas.style.height = "100vh" document.body.append(canvas) const ctx = canvas.getContext("2d")! -let loader: Loader | undefined; +let thing: Thing | undefined; +const loader = new WSLoader() function draw() { ctx.fillStyle = "black" ctx.fillRect(0, 0, canvas.width, canvas.height) - loader?.root.draw() + ctx.fillStyle = "white" + ctx.font = "64px sans-serif" + ctx.fillText(`${loader.num_loading}`, 20, 80) + + thing?.root.draw() requestAnimationFrame(draw) } @@ -26,29 +32,28 @@ function resize() { canvas.width = globalThis.innerWidth canvas.height = globalThis.innerHeight } -canvas.addEventListener("resize", () => resize()) +globalThis.addEventListener("resize", () => resize()) resize() draw() - 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 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"); - + await loader.wait_ready() + const entry_res = await loader.get_entry() + const entry = await get_respackentry(loader, entry_res) + const root = await get_spatialindex(loader, entry.c_spatial_index!) + thing = new Thing(root) + loader_loop() +} +async function loader_loop() { + while (1) { + await thing!.update() + await new Promise(r => setTimeout(r, 100)) + } } -init() -let limit = 10000; +init() -class Loader { +class Thing { view: AABB root: SNode constructor(root: SpatialIndex) { @@ -59,34 +64,45 @@ class Loader { await this.root.load(this.view) } } + +let num_loaded = 0 + class SNode { - children: (SNode | undefined)[] + children?: SNode[] prefab?: Prefab graphics?: GraphicsPart aabb?: AABB - constructor(public data: SpatialIndex) { - this.children = new Array(data.child.length).fill(undefined) - } - async load(view: AABB) { + constructor(public data: SpatialIndex) { } + + async load_graphics() { if (this.data.prefab && !this.prefab) - this.prefab = await get_prefab(this.data.prefab) + this.prefab = await get_prefab(loader, 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 + this.graphics = await get_graphics_part(loader, this.prefab.graphics[0][1]) + } + async load(view: AABB) { + if (!this.aabb || aabb_overlap(this.aabb, view)) { + if (this.children) { + await Promise.all(this.children.map(c => c.load(view))) + } else if (this.data.child.length) { + if (num_loaded > 300) return + num_loaded += 1 + this.children = await Promise.all(this.data.child.map(async c => { + const n = new SNode(await get_spatialindex(loader, c[1])) + n.aabb = c[0] + await n.load_graphics() + return n + })) + } + } else { + if (this.children) { + num_loaded -= 1 + this.children = undefined } - await this.children[i]!.load(view) } } draw() { - for (const c of this.children) { - if (c) c.draw() - } + if (this.children) return this.children.forEach(c => c.draw()) if (!this.graphics) return ctx.fillStyle = "red" ctx.save() @@ -94,7 +110,7 @@ class SNode { ctx.translate(-0.155, -0.63) ctx.translate(this.prefab!.transform![10], this.prefab!.transform![11]) for (const c of this.graphics.read()) { - if (c.point) ctx.fillRect(c.point.x, c.point.y, 0.00003, 0.00003) + if (c.point) ctx.fillRect(c.point.z, c.point.y, 0.00001, 0.00001) } ctx.restore() } diff --git a/viewer/resources.ts b/viewer/resources.ts index 0e9162e..c0eaba3 100644 --- a/viewer/resources.ts +++ b/viewer/resources.ts @@ -1,3 +1,4 @@ +import { Loader } from "./loader.ts"; export class Resource<T> { constructor(public hash: Uint8Array) { } @@ -6,11 +7,7 @@ export class Resource<T> { .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) { @@ -54,9 +51,9 @@ async function ob_cached<T>(r: Resource<T>, make: () => Promise<T>): Promise<T> return m } -export async function get_respackentry(r: Resource<RespackEntry>): Promise<RespackEntry> { +export async function get_respackentry(l: Loader, r: Resource<RespackEntry>): Promise<RespackEntry> { return await ob_cached(r, async () => { - const buf = await r.download_raw(); + const buf = await l.download(r); const o: RespackEntry = {} read_res_table(buf, (key, value) => { switch (key) { @@ -69,9 +66,9 @@ export async function get_respackentry(r: Resource<RespackEntry>): Promise<Respa return o }) } -export async function get_spatialindex(r: Resource<SpatialIndex>): Promise<SpatialIndex> { +export async function get_spatialindex(l: Loader, r: Resource<SpatialIndex>): Promise<SpatialIndex> { return await ob_cached(r, async () => { - const buf = await r.download_raw(); + const buf = await l.download(r); const o: SpatialIndex = { child: [] } read_res_table(buf, (key, value) => { switch (key) { @@ -90,9 +87,9 @@ export async function get_spatialindex(r: Resource<SpatialIndex>): Promise<Spati return o }) } -export async function get_prefab(r: Resource<Prefab>): Promise<Prefab> { +export async function get_prefab(l: Loader, r: Resource<Prefab>): Promise<Prefab> { return await ob_cached(r, async () => { - const buf = await r.download_raw(); + const buf = await l.download(r); const o: Prefab = { graphics: [] } read_res_table(buf, (key, value) => { let v, x = 0 @@ -123,9 +120,9 @@ export async function get_prefab(r: Resource<Prefab>): Promise<Prefab> { return o }) } -export async function get_graphics_part(r: Resource<GraphicsPart>): Promise<GraphicsPart> { +export async function get_graphics_part(l: Loader, r: Resource<GraphicsPart>): Promise<GraphicsPart> { return await ob_cached(r, async () => { - return new GraphicsPart(await r.download_raw()) + return new GraphicsPart(await l.download(r)) }) } @@ -192,4 +189,4 @@ export interface GraphicsCommand { stroke?: number stroke_width?: number point?: Vec3 -}
\ No newline at end of file +} |