BlueGit

golden-age paperdoll-renderer

Files

paperdoll-renderer / src / foundation.rs

foundation.rs

foundation.rs 11.3 KiB Download Raw
1//! Honest capability report for the current editor.
2//!
3//! Attachment authoring, target-map production, the library depth compositor,
4//! rendered 2D preview and the normalized FBX drawable scene are present.
5//! The live Glow viewport and production four-map batch draw glTF/GLB and FBX
6//! bodies; rigid equipment is imported from OBJ+MTL or FBX.
7
8/// Product capability named by the design.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Capability {
11 ProjectShell,
12 ObjMtlTextures,
13 Fbx,
14 FbxDrawable,
15 GltfGlb,
16 SkeletalAnimation,
17 AttachmentEditor,
18 PaperdollRender,
19 Preview2d,
20}
21
22/// Whether this build actually contains the capability.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Availability {
25 Present,
26 NotBuilt,
27}
28
29/// What the process should do with the command line it received.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum CliRequest {
32 OpenWindow,
33 InstallDesktop,
34 OpenProject(String),
35 PrintStatus,
36 PrintHelp,
37 PreviewFixtures,
38}
39
40/// Rejected command line. A model path is rejected on purpose: accepting one
41/// would look like import support.
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum CliError {
44 UnknownArgument(String),
45 Usage(String),
46}
47
48const CAPABILITIES: [Capability; 9] = [
49 Capability::ProjectShell,
50 Capability::ObjMtlTextures,
51 Capability::Fbx,
52 Capability::FbxDrawable,
53 Capability::GltfGlb,
54 Capability::SkeletalAnimation,
55 Capability::AttachmentEditor,
56 Capability::PaperdollRender,
57 Capability::Preview2d,
58];
59
60pub fn capabilities() -> &'static [Capability] {
61 &CAPABILITIES
62}
63
64pub fn availability(capability: Capability) -> Availability {
65 match capability {
66 Capability::ProjectShell
67 | Capability::ObjMtlTextures
68 | Capability::GltfGlb
69 | Capability::Fbx
70 | Capability::FbxDrawable
71 | Capability::SkeletalAnimation
72 | Capability::AttachmentEditor
73 | Capability::PaperdollRender
74 | Capability::Preview2d => Availability::Present,
75 }
76}
77
78pub fn machine_key(capability: Capability) -> &'static str {
79 match capability {
80 Capability::ProjectShell => "project_shell",
81 Capability::ObjMtlTextures => "obj_mtl_textures",
82 Capability::Fbx => "fbx",
83 Capability::FbxDrawable => "fbx_drawable",
84 Capability::GltfGlb => "gltf_glb",
85 Capability::SkeletalAnimation => "skeletal_animation",
86 Capability::AttachmentEditor => "attachment_editor",
87 Capability::PaperdollRender => "paperdoll_render",
88 Capability::Preview2d => "preview_2d",
89 }
90}
91
92pub fn machine_state(availability: Availability) -> &'static str {
93 match availability {
94 Availability::Present => "present",
95 Availability::NotBuilt => "not_built",
96 }
97}
98
99pub fn dutch_label(capability: Capability) -> &'static str {
100 match capability {
101 Capability::ProjectShell => "projectschil",
102 Capability::ObjMtlTextures => "OBJ + MTL + textures",
103 Capability::Fbx => "FBX",
104 Capability::FbxDrawable => "FBX drawable scene",
105 Capability::GltfGlb => "glTF/GLB",
106 Capability::SkeletalAnimation => "skeletanimatie",
107 Capability::AttachmentEditor => "3D-attachmenteditor",
108 Capability::PaperdollRender => "paperdoll-render",
109 Capability::Preview2d => "2D-preview",
110 }
111}
112
113pub fn dutch_state(availability: Availability) -> &'static str {
114 match availability {
115 Availability::Present => "aanwezig",
116 Availability::NotBuilt => "niet gebouwd",
117 }
118}
119
120pub fn dutch_summary() -> &'static str {
121 "Deze build leest glTF/GLB, FBX en OBJ+MTL, opent een .ppd-project en toont een live geskinde 3D-preview van glTF/GLB- en FBX-lichamen (FBX via een eenmalig genormaliseerde tekenbare/animeerbare scene) met al hun clips (zoals walk en attack), rigide bone-gebonden OBJ- of FBX-equipment en binaire .ppe-presets. Render maakt voor glTF/GLB- en FBX-lichamen alle clips × frames × 36 hoeken met vaste orthografische camera en diffuse/depth/normal/specular op doelresolutie. De 2D-preview composeert voltooide exportsets met depth-ordering, looping-playback, hoeknavigatie en continue lichtbediening."
122}
123
124/// Stable status text shared by `--status` and the window.
125pub fn report() -> String {
126 let mut lines = Vec::with_capacity(2 + CAPABILITIES.len());
127 lines.push("name: Paperdoll Renderer".to_string());
128 lines.push("phase: project-editor".to_string());
129 for capability in CAPABILITIES {
130 lines.push(format!(
131 "{}: {}",
132 machine_key(capability),
133 machine_state(availability(capability))
134 ));
135 }
136 lines.join("\n")
137}
138
139/// Shared libraries the X11 window loads. `ldd` does not list them: eframe
140/// opens them at runtime. A missing one panics inside that loader, so the
141/// shell checks first.
142pub const LINUX_WINDOW_LIBRARIES: &[&str] = &[
143 "libxkbcommon-x11.so.0",
144 "libxkbcommon.so.0",
145 "libxcb.so.1",
146 "libX11.so.6",
147 "libGL.so.1",
148];
149
150pub fn missing_library_message(missing: &[&str]) -> String {
151 format!(
152 "het venster kan niet openen; deze bibliotheken ontbreken: {}.",
153 missing.join(", ")
154 )
155}
156
157#[cfg(target_os = "linux")]
158pub fn missing_window_libraries() -> Vec<&'static str> {
159 LINUX_WINDOW_LIBRARIES
160 .iter()
161 .copied()
162 .filter(|name| {
163 // Safety: `name` is a nul-free soname literal. A missing library
164 // returns Err; no symbol from the library is called.
165 unsafe { libloading::Library::new(name).is_err() }
166 })
167 .collect()
168}
169
170pub fn help_text() -> &'static str {
171 "\
172Paperdoll Renderer
173
174Gebruik:
175 paperdoll-renderer startscherm
176 paperdoll-renderer pad/naar/character.ppd open dat project
177 paperdoll-renderer --install-desktop registreer Openen met op Linux
178 paperdoll-renderer --status print de status en sluit
179 paperdoll-renderer --help toon deze tekst
180 paperdoll-renderer --preview-fixture open een voorbeeldproject op het mannelijke lichaam
181
182--status staat alleen. Een .ppd-pad of een projectmap opent character.ppd."
183}
184
185pub fn parse_cli<'a, I>(args: I) -> Result<CliRequest, CliError>
186where
187 I: IntoIterator<Item = &'a str>,
188{
189 let args: Vec<&str> = args.into_iter().collect();
190 if args == ["--install-desktop"] {
191 return Ok(CliRequest::InstallDesktop);
192 }
193 let mut help = false;
194 let mut status = false;
195 let mut preview = false;
196 let mut project = None;
197 for arg in args {
198 match arg {
199 "--help" | "-h" => help = true,
200 "--status" => status = true,
201 "--preview-fixture" => preview = true,
202 other if other.starts_with('-') => {
203 return Err(CliError::UnknownArgument(other.to_string()));
204 }
205 other => {
206 if project.is_some() {
207 return Err(CliError::Usage("geef één projectpad".to_string()));
208 }
209 project = Some(other.to_string());
210 }
211 }
212 }
213 if help {
214 return Ok(CliRequest::PrintHelp);
215 }
216 if status && (preview || project.is_some()) {
217 return Err(CliError::Usage(
218 "--status staat alleen en neemt geen projectpad aan".to_string(),
219 ));
220 }
221 if status {
222 return Ok(CliRequest::PrintStatus);
223 }
224 if preview && project.is_some() {
225 return Err(CliError::Usage(
226 "--preview-fixture opent het voorbeeld en neemt geen projectpad aan".to_string(),
227 ));
228 }
229 if preview {
230 return Ok(CliRequest::PreviewFixtures);
231 }
232 if let Some(path) = project {
233 return Ok(CliRequest::OpenProject(path));
234 }
235 Ok(CliRequest::OpenWindow)
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn import_production_and_preview2d_are_present() {
244 assert_eq!(
245 availability(Capability::ProjectShell),
246 Availability::Present
247 );
248 assert_eq!(availability(Capability::GltfGlb), Availability::Present);
249 assert_eq!(
250 availability(Capability::ObjMtlTextures),
251 Availability::Present
252 );
253 assert_eq!(availability(Capability::Fbx), Availability::Present);
254 assert_eq!(availability(Capability::FbxDrawable), Availability::Present);
255 assert_eq!(
256 availability(Capability::SkeletalAnimation),
257 Availability::Present
258 );
259 assert_eq!(
260 availability(Capability::AttachmentEditor),
261 Availability::Present
262 );
263 assert_eq!(
264 availability(Capability::PaperdollRender),
265 Availability::Present
266 );
267 assert_eq!(availability(Capability::Preview2d), Availability::Present);
268 }
269
270 #[test]
271 fn status_report_matches_current_production_including_preview2d() {
272 let report = report();
273 assert!(report.starts_with("name: Paperdoll Renderer\nphase: project-editor\n"));
274 for key in [
275 "project_shell",
276 "obj_mtl_textures",
277 "gltf_glb",
278 "fbx",
279 "fbx_drawable",
280 "skeletal_animation",
281 "attachment_editor",
282 "paperdoll_render",
283 "preview_2d",
284 ] {
285 assert!(
286 report.contains(&format!("{key}: present")),
287 "missing present {key} in {report}"
288 );
289 }
290 assert!(
291 !report.contains("preview_2d: not_built"),
292 "preview_2d must not stay not_built"
293 );
294 let summary = dutch_summary();
295 assert!(summary.contains("3D-preview"));
296 assert!(summary.contains("glTF/GLB"));
297 assert!(summary.contains("walk"));
298 assert!(summary.contains("2D-preview"));
299 assert!(summary.contains("FBX-lichamen"));
300 assert!(summary.contains("FBX-equipment"));
301 assert!(!summary.contains("nog geen FBX"));
302 }
303
304 #[test]
305 fn cli_opens_a_window_or_prints_status_and_rejects_model_paths() {
306 assert_eq!(parse_cli(std::iter::empty()), Ok(CliRequest::OpenWindow));
307 assert_eq!(parse_cli(["--status"]), Ok(CliRequest::PrintStatus));
308 assert_eq!(parse_cli(["--help"]), Ok(CliRequest::PrintHelp));
309 assert_eq!(parse_cli(["-h", "--status"]), Ok(CliRequest::PrintHelp));
310 assert_eq!(
311 parse_cli(["mijn project/character.ppd"]),
312 Ok(CliRequest::OpenProject(
313 "mijn project/character.ppd".to_string()
314 ))
315 );
316 assert_eq!(
317 parse_cli(["--status", "character.ppd"]),
318 Err(CliError::Usage(
319 "--status staat alleen en neemt geen projectpad aan".to_string()
320 ))
321 );
322 assert_eq!(
323 parse_cli(["--status", "--render"]),
324 Err(CliError::UnknownArgument("--render".to_string()))
325 );
326 assert!(help_text().contains("--preview-fixture"));
327 assert_eq!(
328 parse_cli(["--preview-fixture"]),
329 Ok(CliRequest::PreviewFixtures)
330 );
331 }
332
333 #[test]
334 fn missing_library_message_names_the_library_and_does_not_claim_import() {
335 let message = missing_library_message(&["libxkbcommon-x11.so.0"]);
336 assert!(message.contains("libxkbcommon-x11.so.0"));
337 assert!(!message.to_lowercase().contains("import"));
338 assert!(!message.to_lowercase().contains("niet gebouwd"));
339 assert!(!LINUX_WINDOW_LIBRARIES.is_empty());
340 }
341}