facett-model3d/examples/view_glb.rs
//! A minimal "open any glTF" viewer — the facett-model3d analogue of
//! gltf-viewer-rs, within facett's CPU-painter limits (Lambert + base colour, no
//! PBR/textures/animation).
//!
//! `cargo run -p facett-model3d --example view_glb -- /path/to/model.glb`
//!
//! With no path it falls back to the bundled skull. Drag to rotate (pauses the
//! auto-spin); the status slider tints the `eyes` group if the model has one.
use eframe::egui;
use facett_core::Facet;
use facett_model3d::Model3D;
struct App {
model: Model3D,
source: String,
}
impl eframe::App for App {
fn ui(&mut self, root: &mut egui::Ui, _frame: &mut eframe::Frame) {
egui::Panel::top("hdr").show(root, |ui| {
let s = self.model.state_json();
ui.label(format!(
"{} · {} triangles · groups: {:?}",
self.source,
s["triangles"].as_u64().unwrap_or(0),
self.model.mesh().group_names(),
));
});
egui::CentralPanel::default().show(root, |ui| {
self.model.ui(ui);
});
}
}
fn main() -> eframe::Result {
let arg = std::env::args().nth(1);
let (model, source) = match arg {
Some(path) => {
let bytes = std::fs::read(&path).unwrap_or_else(|e| panic!("read {path}: {e}"));
let m = Model3D::from_glb(&path, &bytes).unwrap_or_else(|e| panic!("load {path}: {e}"));
(m, path)
}
None => (Model3D::skull("skull3d"), "<bundled skull>".to_string()),
};
let app = App { model: model.with_status_control(true), source };
eprintln!("view_glb: loaded {} ({} tris, groups {:?})", app.source, app.model.mesh().triangle_count(), app.model.mesh().group_names());
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size(egui::Vec2::new(820.0, 700.0)),
..Default::default()
};
eframe::run_native("facett-model3d · view_glb", options, Box::new(|_cc| Ok(Box::new(app))))
}