BlueGit

golden-age paperdoll-renderer

Files

paperdoll-renderer / src / foundation.rs

foundation.rs

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