diff options
Diffstat (limited to 'crates/render-demo/src/lib.rs')
| -rw-r--r-- | crates/render-demo/src/lib.rs | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/crates/render-demo/src/lib.rs b/crates/render-demo/src/lib.rs new file mode 100644 index 0000000..4c73c09 --- /dev/null +++ b/crates/render-demo/src/lib.rs @@ -0,0 +1,113 @@ +use msh_core::{parse_model_payload, Model}; +use nres::Archive; +use std::path::Path; + +#[derive(Debug)] +pub enum Error { + Nres(nres::error::Error), + Msh(msh_core::error::Error), + NoMshEntries, + ModelNotFound(String), +} + +impl From<nres::error::Error> for Error { + fn from(value: nres::error::Error) -> Self { + Self::Nres(value) + } +} + +impl From<msh_core::error::Error> for Error { + fn from(value: msh_core::error::Error) -> Self { + Self::Msh(value) + } +} + +pub type Result<T> = core::result::Result<T, Error>; + +pub fn load_model_from_archive(path: &Path, model_name: Option<&str>) -> Result<Model> { + let archive = Archive::open_path(path)?; + let mut msh_entries = Vec::new(); + for entry in archive.entries() { + if entry.meta.name.to_ascii_lowercase().ends_with(".msh") { + msh_entries.push((entry.id, entry.meta.name.clone())); + } + } + if msh_entries.is_empty() { + return Err(Error::NoMshEntries); + } + + let target_id = if let Some(name) = model_name { + msh_entries + .iter() + .find(|(_, n)| n.eq_ignore_ascii_case(name)) + .map(|(id, _)| *id) + .ok_or_else(|| Error::ModelNotFound(name.to_string()))? + } else { + msh_entries[0].0 + }; + + let payload = archive.read(target_id)?; + Ok(parse_model_payload(payload.as_slice())?) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::path::{Path, PathBuf}; + + fn collect_files_recursive(root: &Path, out: &mut Vec<PathBuf>) { + let Ok(entries) = fs::read_dir(root) else { + return; + }; + for entry in entries.flatten() { + let path = entry.path(); + if path.is_dir() { + collect_files_recursive(&path, out); + } else if path.is_file() { + out.push(path); + } + } + } + + fn archive_with_msh() -> Option<PathBuf> { + let root = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join("..") + .join("testdata"); + let mut files = Vec::new(); + collect_files_recursive(&root, &mut files); + files.sort(); + for path in files { + let Ok(bytes) = fs::read(&path) else { + continue; + }; + if bytes.get(0..4) != Some(b"NRes") { + continue; + } + let Ok(archive) = Archive::open_path(&path) else { + continue; + }; + if archive + .entries() + .any(|entry| entry.meta.name.to_ascii_lowercase().ends_with(".msh")) + { + return Some(path); + } + } + None + } + + #[test] + fn load_model_from_real_archive() { + let Some(path) = archive_with_msh() else { + eprintln!("skipping load_model_from_real_archive: no .msh archives in testdata"); + return; + }; + let model = load_model_from_archive(&path, None) + .unwrap_or_else(|err| panic!("failed to load model from {}: {err:?}", path.display())); + assert!(model.node_count > 0); + assert!(!model.positions.is_empty()); + assert!(!model.indices.is_empty()); + } +} |
