#![feature(array_chunks)] pub mod cache; pub mod client; pub mod mesh; use anyhow::Result; use cache::Cache; use clap::Parser; use client::GeClient; use futures_util::{StreamExt, stream::FuturesUnordered}; use glam::{DMat4, DVec3}; use mesh::{convert_mesh, decode_normal_table}; use std::{f32::consts::PI, path::PathBuf, pin::Pin, sync::Arc}; use tokio::sync::Semaphore; use weareshared::{ Affine3A, helper::AABB, packets::Resource, resources::{Prefab, RespackEntry, SpatialIndex}, respack::save_full_respack, store::ResourceStore, }; #[derive(Parser)] struct Args { #[arg(short, long, default_value = "16")] par_limit: usize, #[arg(short, long)] db_cache: Option, #[clap(subcommand)] action: Action, } #[derive(Parser)] enum Action { Cache { level: usize }, Export { level: usize }, CacheFsToDb { db: PathBuf }, } #[tokio::main] async fn main() -> Result<()> { env_logger::init_from_env("LOG"); let args = Args::parse(); let cache = if let Some(path) = args.db_cache { Cache::new_db(&path)? } else { Cache::new_directory()? }; let c = GeClient::new(16, cache).await?; match args.action { Action::Cache { level } => { let entry = c.planetoid_metdata().await?; let epoch = entry.root_node_metadata.unwrap().bulk_metadata_epoch(); cache_all( Arc::new(c), Arc::new(Semaphore::new(args.par_limit * 3 / 2)), "".to_string(), epoch, level, ) .await?; } Action::Export { level } => { let entry = c.planetoid_metdata().await?; let store = Arc::new(ResourceStore::new_memory()); let (_, root) = do_node( Arc::new(c), "".to_string(), store.clone(), entry.root_node_metadata.unwrap().bulk_metadata_epoch(), level, ) .await?; let entry = store.set(&RespackEntry { c_spatial_index: vec![root], ..Default::default() })?; let file = std::fs::File::create("/tmp/a.respack")?; save_full_respack(file, &store, Some(entry))?; } Action::CacheFsToDb { db: dbpath } => { let source = Cache::new_directory()?; let dest = Cache::new_db(&dbpath)?; source.transfer_entries(&dest).await?; } } Ok(()) } fn do_node( c: Arc, path: String, store: Arc, epoch: u32, level: usize, ) -> Pin)>>>> { Box::pin(async move { let bulk = c.bulk_metadata(&path, epoch).await?; let mut fu = FuturesUnordered::new(); let mut meshes = Vec::new(); let mut children = Vec::new(); for node_meta in &bulk.node_metadata { let (cpath, flags) = unpack_path_and_id(node_meta.path_and_flags()); // eprintln!("{path}+{cpath} {flags:?}"); let abspath = format!("{path}{cpath}"); if flags.has_node && cpath.len() == 4 { let node = c.node_data(&abspath, flags, &bulk, node_meta).await?; let transform = DMat4::from_cols_slice(&node.matrix_globe_from_mesh); let for_normals = decode_normal_table(node.for_normals()); eprintln!( "{:?} {:?}", node_meta.oriented_bounding_box, bulk.head_node_center ); for m in node.meshes { let mesh = convert_mesh(m, &store, &for_normals)?; meshes.push(( Affine3A::from_rotation_x(-PI / 2.) * Affine3A::from_mat4((transform / 3_000_000.).as_mat4()), mesh, )) } } if cpath.len() == 4 && flags.has_metadata && abspath.len() < level { fu.push(do_node(c.clone(), abspath, store.clone(), epoch, level)); } } while let Some(res) = fu.next().await { children.push(res?); } let center = DVec3::from_slice(&bulk.head_node_center).as_vec3(); let size = 10_000_0000. / (1 << path.len()) as f32; let bounds = AABB { min: center - size, max: center + size, }; let prefab = store.set(&Prefab { mesh: meshes, ..Default::default() })?; Ok(( bounds, store.set(&SpatialIndex { prefab: Some(prefab), child: children, ..Default::default() })?, )) }) } fn cache_all( c: Arc, par: Arc, path: String, epoch: u32, level: usize, ) -> Pin>>> { Box::pin(async move { let _permit = par.acquire().await?; let bulk = c.bulk_metadata(&path, epoch).await?; let mut fu = FuturesUnordered::new(); for node_meta in &bulk.node_metadata { let (cpath, flags) = unpack_path_and_id(node_meta.path_and_flags()); let abspath = format!("{path}{cpath}"); if flags.has_node && abspath.len() < level { c.node_data(&abspath, flags, &bulk, node_meta).await?; } if cpath.len() == 4 && flags.has_metadata && abspath.len() < level { fu.push(cache_all(c.clone(), par.clone(), abspath, epoch, level)); } } drop(_permit); while let Some(res) = fu.next().await { res?; } Ok(()) }) } #[derive(Debug, Clone, Copy)] pub struct Flags { has_node: bool, has_metadata: bool, use_image_epoch: bool, } fn unpack_path_and_id(mut path_id: u32) -> (String, Flags) { let level = 1 + (path_id & 3); path_id >>= 2; let mut path = String::new(); for _ in 0..level { path += &(path_id & 7).to_string(); path_id >>= 3; } let flags = path_id; ( path, Flags { has_node: flags & 8 == 0, has_metadata: flags & 4 == 0, use_image_epoch: flags & 16 != 0, }, ) } pub mod proto { include!(concat!(env!("OUT_DIR"), "/earth.proto.rs")); }