use glob::glob;
use clap::Parser;
use bevy::prelude::*;
use thdmaker::util;
use thdmaker::viewer::{
camera, uireact,
stl::{EXTENSIONS as STL_EXTS, StlPlugin, StlLabel},
amf::{EXTENSIONS as AMF_EXTS, AmfPlugin, AmfLabel},
tmf::{EXTENSIONS as TMF_EXTS, TmfPlugin, TmfLabel},
};
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long)]
list: bool,
#[arg(name = "files")]
files: Vec<String>,
}
#[derive(Resource, Clone)]
struct GlobalState {
models: Vec<(String, String)>,
}
fn main() {
let args = Args::parse();
let supports = []
.iter()
.chain(TMF_EXTS.iter())
.chain(AMF_EXTS.iter())
.chain(STL_EXTS.iter())
.collect::<Vec<_>>();
if args.list {
println!("Available model files:");
for ext in supports {
if let Ok(entries) = glob(&format!("assets/**/*.{}", ext)) {
println!("{} files:", ext.to_uppercase());
for entry in entries.filter_map(|e| e.ok()) {
if let Some(model) = entry.to_str() {
println!(" - {}", model);
}
}
}
}
} else {
let mut models = Vec::new();
for file in &args.files {
let ext = util::path::extension(file);
if supports.contains(&&ext.as_str()) {
models.push((file.clone(), ext));
}
}
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "ThdMaker Viewer".into(),
..default()
}),
..default()
}))
.insert_resource(ClearColor(Color::srgb(0.8, 0.8, 0.85)))
.insert_resource(AmbientLight {
color: Color::srgb(0.8, 0.8, 0.9),
brightness: 0.3,
..default()
})
.add_plugins(StlPlugin)
.add_plugins(AmfPlugin)
.add_plugins(TmfPlugin)
.add_systems(Startup, setup)
.add_systems(Update, update)
.add_plugins(camera::CameraPlugin)
.add_plugins(uireact::UIReactPlugin)
.insert_resource(GlobalState { models })
.run();
}
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
asset_server: Res<AssetServer>,
global_state: Res<GlobalState>,
) {
let intensity = 1500.0;
commands.spawn((
DirectionalLight {
illuminance: intensity,
color: Color::WHITE,
shadows_enabled: false,
..default()
},
Transform::from_rotation(Quat::from_euler(EulerRot::XYZ, -0.6, 0.8, 0.0)),
));
commands.spawn((
DirectionalLight {
illuminance: intensity * 0.4,
color: Color::srgb(0.95, 0.95, 1.0),
shadows_enabled: false,
..default()
},
Transform::from_rotation(Quat::from_euler(EulerRot::XYZ, 0.5, -0.6, 0.0)),
));
commands.spawn((
DirectionalLight {
illuminance: intensity * 0.5,
color: Color::WHITE,
shadows_enabled: false,
..default()
},
Transform::from_rotation(Quat::from_euler(EulerRot::XYZ, 0.2, 3.14, 0.0)),
));
let mut entities = Vec::new();
for (ext, file) in global_state.models.iter() {
if TMF_EXTS.contains(&ext.as_str()) {
let entity = commands.spawn(SceneRoot(asset_server.load(TmfLabel::Scene.from_asset(file.clone()))));
entities.push(entity.id());
} else if AMF_EXTS.contains(&ext.as_str()) {
let entity = commands.spawn((
Mesh3d(asset_server.load(AmfLabel::Mesh(0).from_asset(file.clone()))),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.5, 0.5, 0.5),
metallic: 0.0,
reflectance: 0.3,
perceptual_roughness: 0.8,
..default()
})),
Transform::from_translation(Vec3::ZERO),
));
entities.push(entity.id());
} else if STL_EXTS.contains(&ext.as_str()) {
let entity = commands.spawn((
Mesh3d(asset_server.load(StlLabel::Mesh(0).from_asset(file.clone()))),
MeshMaterial3d(materials.add(StandardMaterial {
base_color: Color::srgb(0.5, 0.5, 0.5),
metallic: 0.0,
reflectance: 0.3,
perceptual_roughness: 0.8,
..default()
})),
Transform::from_translation(Vec3::ZERO),
));
entities.push(entity.id());
}
}
if entities.is_empty() {
commands.spawn((
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
MeshMaterial3d( materials.add(StandardMaterial {
base_color: Color::srgb(0.5, 0.5, 0.5),
metallic: 0.0,
reflectance: 0.3,
perceptual_roughness: 0.8,
..default()
})),
Transform::from_xyz(0.0, 0.5, 0.0),
));
}
}
fn update(mut gizmos: Gizmos) {
gizmos.grid(
Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2),
UVec2::new(20, 20),
Vec2::new(1.0, 1.0),
Color::srgb(0.5, 0.5, 0.5),
).outer_edges();
gizmos.axes(
Transform::from_translation(Vec3::ZERO),
2.0,
);
}