BlueGit

golden-age paperdoll-renderer

Files

paperdoll-renderer / examples / production_fixture.rs

production_fixture.rs

production_fixture.rs 4.8 KiB Download Raw
1//! Actual native end-to-end batch smoke; no GUI input automation or preview delays.
2//! Usage: production_fixture ROOT MALE.glb FEMALE.glb male|female WIDTH HEIGHT FPS [equipment] [--height-milli=N]
3use eframe::App;
4use paperdoll_renderer::{
5 editor::EditorApp,
6 equipment, preview,
7 session::{adopt_file, Session, Sex},
8};
9use std::path::PathBuf;
10struct Smoke {
11 editor: EditorApp,
12 root: PathBuf,
13 last: u32,
14 frames: u64,
15 done: bool,
16}
17impl App for Smoke {
18 fn update(&mut self, ctx: &eframe::egui::Context, frame: &mut eframe::Frame) {
19 self.frames += 1;
20 self.editor.update(ctx, frame);
21 let (completed, total, label) = self.editor.render_progress();
22 if completed != self.last {
23 println!("PASS {completed}/{total}: {label}");
24 self.last = completed;
25 }
26 if !self.editor.render_is_running() && !self.done {
27 self.done = true;
28 let error = self.editor.render_error();
29 let success = error.is_none() && total > 0 && completed == total;
30 let value = serde_json::json!({"success":success,"completed":completed,"total":total,"last_pass":label,"gui_updates":self.frames,"error":error});
31 std::fs::write(
32 self.root.join("smoke-result.json"),
33 serde_json::to_vec_pretty(&value).unwrap(),
34 )
35 .unwrap();
36 println!("RESULT {value}");
37 ctx.send_viewport_cmd(eframe::egui::ViewportCommand::Close);
38 }
39 }
40 fn on_exit(&mut self, gl: Option<&glow::Context>) {
41 self.editor.on_exit(gl);
42 }
43}
44fn main() -> Result<(), Box<dyn std::error::Error>> {
45 let a = std::env::args().skip(1).collect::<Vec<_>>();
46 if a.len() < 7 {
47 return Err(
48 "ROOT MALE FEMALE male|female WIDTH HEIGHT FPS [equipment] [--height-milli=N]".into(),
49 );
50 }
51 let root = PathBuf::from(&a[0]);
52 let mut s = Session::new();
53 s.install_new_project(&root)?;
54 for (sex, index) in [(Sex::Male, 1), (Sex::Female, 2)] {
55 let path = adopt_file(&root, &PathBuf::from(&a[index]), "assets/body")?;
56 s.set_body_path(sex, path);
57 }
58 s.set_render(a[4].parse()?, a[5].parse()?, a[6].parse()?);
59 let sex = match a[3].as_str() {
60 "male" => Sex::Male,
61 "female" => Sex::Female,
62 _ => return Err("invalid sex".into()),
63 };
64 let equipment = a.get(7).is_some_and(|v| v == "equipment");
65 if let Some(value) = a.iter().find_map(|v| v.strip_prefix("--height-milli=")) {
66 s.opened.as_mut().unwrap().document.render.framing[usize::from(sex == Sex::Female)]
67 .height_milli = value.parse()?;
68 }
69
70 if equipment {
71 let assets = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/equipment");
72 let mut p = equipment::Preset::new("sword".into());
73 p.provenance = "CC0 generated fixture".into();
74 equipment::import_obj(&mut p, &assets.join("sword.obj"))?;
75 equipment::import_shiny(&mut p, 0, &assets.join("shiny.png"))?;
76 for (i, sex) in [(0, Sex::Male), (1, Sex::Female)] {
77 let b = preview::load_gltf_file(&root.join(s.body_path(sex).unwrap()))?;
78 let joint = b
79 .joint_names
80 .iter()
81 .position(|n| n == "RightHand")
82 .ok_or("RightHand absent")?;
83 p.bind(i, &b, 0, joint)?;
84 }
85 let i = s.add_equipment();
86 s.equipment[i].slug = "sword".into();
87 s.equipment[i].preset = Some(p);
88 }
89 s.save()?;
90 for sex in [Sex::Male, Sex::Female] {
91 let b = preview::load_gltf_file(&root.join(s.body_path(sex).unwrap()))?;
92 println!(
93 "PLAN {sex:?}: {:?}",
94 paperdoll_renderer::production::plan(&b, a[6].parse()?)?
95 );
96 }
97 let root2 = root.clone();
98 eframe::run_native(
99 "Paperdoll Production Render",
100 eframe::NativeOptions {
101 viewport: eframe::egui::ViewportBuilder::default().with_inner_size([1150., 820.]),
102 ..paperdoll_renderer::preview_gl::native_options()
103 },
104 Box::new(move |_| {
105 let mut editor = EditorApp::default();
106 editor.open_path(root2.join("character.ppd"));
107 if equipment {
108 editor.open_equipment_configuration(0, sex);
109 } else {
110 editor.open_body_configuration(sex);
111 }
112 editor.render_all_angles();
113 Ok(Box::new(Smoke {
114 editor,
115 root: root2,
116 last: 0,
117 frames: 0,
118 done: false,
119 }))
120 }),
121 )?;
122 let report: serde_json::Value =
123 serde_json::from_slice(&std::fs::read(root.join("smoke-result.json"))?)?;
124 if report["success"] != true {
125 return Err("production smoke failed; see smoke-result.json".into());
126 }
127 Ok(())
128}