ranim/scene.rs
1//! Scene description types.
2//!
3//! These types describe *what* to render (scene metadata, output settings)
4//! rather than *how* to animate (which lives in `ranim-core`).
5
6use std::sync::Arc;
7
8use ranim_core::{RanimScene, SealedRanimScene};
9
10#[cfg(target_arch = "wasm32")]
11use wasm_bindgen::prelude::*;
12
13/// A scene descriptor bundling a constructor, config, and outputs.
14#[doc(hidden)]
15#[derive(Clone)]
16#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
17pub struct Scene {
18 /// Scene name
19 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
20 pub name: String,
21 /// Scene constructor
22 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
23 pub constructor: fn(&mut RanimScene),
24 /// Scene config
25 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
26 pub config: SceneConfig,
27 /// Scene outputs
28 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
29 pub outputs: Vec<Output>,
30}
31
32/// Scene config
33#[derive(Debug, Clone)]
34pub struct SceneConfig {
35 /// The clear color
36 pub clear_color: String,
37}
38
39impl Default for SceneConfig {
40 fn default() -> Self {
41 Self {
42 clear_color: "#333333ff".to_string(),
43 }
44 }
45}
46
47/// The output format of a scene
48#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
49pub enum OutputFormat {
50 /// H.264 in MP4 container (default, opaque)
51 #[default]
52 Mp4,
53 /// VP9 with alpha in WebM container (transparent)
54 Webm,
55 /// ProRes 4444 in MOV container (transparent)
56 Mov,
57 /// GIF (opaque, limited palette)
58 Gif,
59}
60
61impl std::fmt::Display for OutputFormat {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 Self::Mp4 => write!(f, "mp4"),
65 Self::Webm => write!(f, "webm"),
66 Self::Mov => write!(f, "mov"),
67 Self::Gif => write!(f, "gif"),
68 }
69 }
70}
71
72/// The output of a scene
73#[derive(Debug, Clone)]
74pub struct Output {
75 /// The width of the output texture in pixels.
76 pub width: u32,
77 /// The height of the output texture in pixels.
78 pub height: u32,
79 /// The frame rate of the output video.
80 pub fps: u32,
81 /// Whether to save the frames.
82 pub save_frames: bool,
83 /// The name of the video, uses scene's name by default.
84 ///
85 /// e.g. the output of name `my_video` will be outputed as `my_video_<width>x<height>_<fps>.mp4`.
86 pub name: Option<String>,
87 /// The basename template for the rendered output.
88 ///
89 /// Supported placeholders:
90 /// - `{name}`: the scene/output name
91 /// - `{width}`: output width in pixels
92 /// - `{height}`: output height in pixels
93 /// - `{fps}`: output frame rate
94 ///
95 /// The file extension is appended automatically based on the output format.
96 /// Defaults to `{name}_{width}x{height}_{fps}`.
97 pub name_template: Option<String>,
98 /// The directory to save the output.
99 ///
100 /// Can be relative (resolved from cwd) or absolute.
101 pub dir: String,
102 /// The output video format.
103 pub format: OutputFormat,
104}
105
106impl Default for Output {
107 fn default() -> Self {
108 Self {
109 width: 1920,
110 height: 1080,
111 fps: 60,
112 save_frames: false,
113 name: None,
114 name_template: None,
115 dir: "./output".to_string(),
116 format: OutputFormat::default(),
117 }
118 }
119}
120
121// MARK: SceneConstructor
122// ANCHOR: SceneConstructor
123/// A scene constructor
124///
125/// It can be a simple fn pointer of `fn(&mut RanimScene)`,
126/// or any type implements `Fn(&mut RanimScene) + Send + Sync`.
127pub trait SceneConstructor: Send + Sync {
128 /// The construct logic
129 fn construct(&self, r: &mut RanimScene);
130
131 /// Use the constructor to build a [`SealedRanimScene`]
132 fn build_scene(&self) -> SealedRanimScene {
133 let mut scene = RanimScene::new();
134 self.construct(&mut scene);
135 scene.seal()
136 }
137}
138// ANCHOR_END: SceneConstructor
139
140impl<F: Fn(&mut RanimScene) + Send + Sync> SceneConstructor for F {
141 fn construct(&self, r: &mut RanimScene) {
142 self(r);
143 }
144}
145
146impl SceneConstructor for Arc<dyn SceneConstructor> {
147 fn construct(&self, r: &mut RanimScene) {
148 self.as_ref().construct(r)
149 }
150}