inox2d/
puppet.rs

1#![allow(dead_code)]
2
3use std::collections::HashMap;
4use std::fmt;
5
6use crate::nodes::node::InoxNodeUuid;
7use crate::nodes::node_data::InoxData;
8use crate::nodes::node_tree::InoxNodeTree;
9use crate::params::{Param, ParamUuid};
10use crate::render::RenderCtx;
11
12/// Who is allowed to use the puppet?
13#[derive(Clone, Copy, Debug, Default)]
14pub enum PuppetAllowedUsers {
15	/// Only the author(s) are allowed to use the puppet.
16	#[default]
17	OnlyAuthor,
18	/// Only licensee(s) are allowed to use the puppet.
19	OnlyLicensee,
20	/// Everyone may use the model.
21	Everyone,
22}
23
24impl fmt::Display for PuppetAllowedUsers {
25	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
26		write!(
27			f,
28			"{}",
29			match self {
30				PuppetAllowedUsers::OnlyAuthor => "only author",
31				PuppetAllowedUsers::OnlyLicensee => "only licensee",
32				PuppetAllowedUsers::Everyone => "Everyone",
33			}
34		)
35	}
36}
37
38#[derive(Debug, Clone, thiserror::Error)]
39#[error("Unknown allowed users {0:?}")]
40pub struct UnknownPuppetAllowedUsersError(String);
41
42impl TryFrom<&str> for PuppetAllowedUsers {
43	type Error = UnknownPuppetAllowedUsersError;
44
45	fn try_from(value: &str) -> Result<Self, Self::Error> {
46		match value {
47			"OnlyAuthor" => Ok(PuppetAllowedUsers::OnlyAuthor),
48			"OnlyLicensee" => Ok(PuppetAllowedUsers::OnlyLicensee),
49			"Everyone" => Ok(PuppetAllowedUsers::Everyone),
50			unknown => Err(UnknownPuppetAllowedUsersError(unknown.to_owned())),
51		}
52	}
53}
54
55/// Can the puppet be redistributed?
56#[derive(Clone, Copy, Debug, Default)]
57pub enum PuppetAllowedRedistribution {
58	/// Redistribution is prohibited
59	#[default]
60	Prohibited,
61	/// Redistribution is allowed, but only under the same license
62	/// as the original.
63	ViralLicense,
64	/// Redistribution is allowed, and the puppet may be
65	/// redistributed under a different license than the original.
66	///
67	/// This goes in conjunction with modification rights.
68	CopyleftLicense,
69}
70
71impl fmt::Display for PuppetAllowedRedistribution {
72	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
73		write!(
74			f,
75			"{}",
76			match self {
77				PuppetAllowedRedistribution::Prohibited => "prohibited",
78				PuppetAllowedRedistribution::ViralLicense => "viral license",
79				PuppetAllowedRedistribution::CopyleftLicense => "copyleft license",
80			}
81		)
82	}
83}
84
85#[derive(Debug, Clone, thiserror::Error)]
86#[error("Unknown allowed redistribution {0:?}")]
87pub struct UnknownPuppetAllowedRedistributionError(String);
88
89impl TryFrom<&str> for PuppetAllowedRedistribution {
90	type Error = UnknownPuppetAllowedRedistributionError;
91
92	fn try_from(value: &str) -> Result<Self, Self::Error> {
93		match value {
94			"Prohibited" => Ok(PuppetAllowedRedistribution::Prohibited),
95			"ViralLicense" => Ok(PuppetAllowedRedistribution::ViralLicense),
96			"CopyleftLicense" => Ok(PuppetAllowedRedistribution::CopyleftLicense),
97			unknown => Err(UnknownPuppetAllowedRedistributionError(unknown.to_owned())),
98		}
99	}
100}
101
102/// Can the puppet be modified?
103#[derive(Clone, Copy, Debug, Default)]
104pub enum PuppetAllowedModification {
105	/// Modification is prohibited
106	#[default]
107	Prohibited,
108	/// Modification is only allowed for personal use
109	AllowPersonal,
110	/// Modification is allowed with redistribution, see
111	/// `allowed_redistribution` for redistribution terms.
112	AllowRedistribute,
113}
114
115impl fmt::Display for PuppetAllowedModification {
116	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
117		write!(
118			f,
119			"{}",
120			match self {
121				PuppetAllowedModification::Prohibited => "prohibited",
122				PuppetAllowedModification::AllowPersonal => "allow personal",
123				PuppetAllowedModification::AllowRedistribute => "allow redistribute",
124			}
125		)
126	}
127}
128
129#[derive(Debug, Clone, thiserror::Error)]
130#[error("Unknown allowed users {0:?}")]
131pub struct UnknownPuppetAllowedModificationError(String);
132
133impl TryFrom<&str> for PuppetAllowedModification {
134	type Error = UnknownPuppetAllowedModificationError;
135
136	fn try_from(value: &str) -> Result<Self, Self::Error> {
137		match value {
138			"Prohibited" => Ok(PuppetAllowedModification::Prohibited),
139			"AllowPersonal" => Ok(PuppetAllowedModification::AllowPersonal),
140			"AllowRedistribute" => Ok(PuppetAllowedModification::AllowRedistribute),
141			unknown => Err(UnknownPuppetAllowedModificationError(unknown.to_owned())),
142		}
143	}
144}
145
146/// Terms of usage of the puppet.
147#[derive(Clone, Debug, Default)]
148pub struct PuppetUsageRights {
149	/// Who is allowed to use the puppet?
150	pub allowed_users: PuppetAllowedUsers,
151	/// Whether violence content is allowed.
152	pub allow_violence: bool,
153	/// Whether sexual content is allowed.
154	pub allow_sexual: bool,
155	/// Whether commercial use is allowed.
156	pub allow_commercial: bool,
157	/// Whether a model may be redistributed.
158	pub allow_redistribution: PuppetAllowedRedistribution,
159	/// Whether a model may be modified.
160	pub allow_modification: PuppetAllowedModification,
161	/// Whether the author(s) must be attributed for use.
162	pub require_attribution: bool,
163}
164
165fn allowed_bool(value: bool) -> &'static str {
166	if value {
167		"allowed"
168	} else {
169		"prohibited"
170	}
171}
172
173impl fmt::Display for PuppetUsageRights {
174	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
175		writeln!(f, "| allowed users:  {}", self.allowed_users)?;
176		writeln!(f, "| violence:       {}", allowed_bool(self.allow_violence))?;
177		writeln!(f, "| sexual:         {}", allowed_bool(self.allow_sexual))?;
178		writeln!(f, "| commercial:     {}", allowed_bool(self.allow_commercial))?;
179		writeln!(f, "| redistribution: {}", self.allow_redistribution)?;
180		writeln!(f, "| modification:   {}", self.allow_modification)?;
181		writeln!(
182			f,
183			"| attribution: {}",
184			if self.require_attribution {
185				"required"
186			} else {
187				"not required"
188			}
189		)
190	}
191}
192
193/// Puppet meta information.
194#[derive(Clone, Debug)]
195pub struct PuppetMeta {
196	/// Name of the puppet.
197	pub name: Option<String>,
198	/// Version of the Inochi2D spec that was used when creating this model.
199	pub version: String,
200	/// Rigger(s) of the puppet.
201	pub rigger: Option<String>,
202	/// Artist(s) of the puppet.
203	pub artist: Option<String>,
204	/// Usage Rights of the puppet.
205	pub rights: Option<PuppetUsageRights>,
206	/// Copyright string.
207	pub copyright: Option<String>,
208	/// URL of the license.
209	pub license_url: Option<String>,
210	/// Contact information of the first author.
211	pub contact: Option<String>,
212	/// Link to the origin of this puppet.
213	pub reference: Option<String>,
214	/// Texture ID of this puppet's thumbnail.
215	pub thumbnail_id: Option<u32>,
216	/// Whether the puppet should preserve pixel borders.
217	/// This feature is mainly useful for puppets that use pixel art.
218	pub preserve_pixels: bool,
219}
220
221fn writeln_opt<T: fmt::Display>(f: &mut fmt::Formatter<'_>, field_name: &str, opt: &Option<T>) -> fmt::Result {
222	let field_name = format!("{:<17}", format!("{field_name}:"));
223	if let Some(ref value) = opt {
224		#[cfg(feature = "owo")]
225		let value = {
226			use owo_colors::OwoColorize;
227			value.green()
228		};
229		writeln!(f, "{field_name}{value}")?;
230	}
231	Ok(())
232}
233
234impl fmt::Display for PuppetMeta {
235	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236		match self.name {
237			Some(ref name) => writeln_opt(f, "Name", &Some(name))?,
238			None => {
239				let no_name = "(No Name)";
240				#[cfg(feature = "owo")]
241				let no_name = {
242					use owo_colors::OwoColorize;
243					no_name.dimmed()
244				};
245				writeln!(f, "{no_name}")?
246			}
247		}
248
249		writeln_opt(f, "Version", &Some(&self.version))?;
250		writeln_opt(f, "Rigger", &self.rigger)?;
251		writeln_opt(f, "Artist", &self.artist)?;
252
253		if let Some(ref rights) = self.rights {
254			writeln!(f, "Rights:")?;
255			#[cfg(feature = "owo")]
256			let rights = {
257				use owo_colors::OwoColorize;
258				rights.yellow()
259			};
260			writeln!(f, "{rights}")?;
261		}
262
263		writeln_opt(f, "Copyright", &self.copyright)?;
264		writeln_opt(f, "License URL", &self.license_url)?;
265		writeln_opt(f, "Contact", &self.contact)?;
266		writeln_opt(f, "Reference", &self.reference)?;
267		writeln_opt(f, "Thumbnail ID", &self.thumbnail_id)?;
268
269		writeln_opt(
270			f,
271			"Preserve pixels",
272			&Some(if self.preserve_pixels { "yes" } else { "no" }),
273		)
274	}
275}
276
277impl Default for PuppetMeta {
278	fn default() -> Self {
279		Self {
280			name: Default::default(),
281			version: crate::INOCHI2D_SPEC_VERSION.to_owned(),
282			rigger: Default::default(),
283			artist: Default::default(),
284			rights: Default::default(),
285			copyright: Default::default(),
286			license_url: Default::default(),
287			contact: Default::default(),
288			reference: Default::default(),
289			thumbnail_id: Default::default(),
290			preserve_pixels: Default::default(),
291		}
292	}
293}
294
295/// Global physics parameters for the puppet.
296#[derive(Clone, Copy, Debug)]
297pub struct PuppetPhysics {
298	pub pixels_per_meter: f32,
299	pub gravity: f32,
300}
301
302/// Inochi2D puppet.
303#[derive(Clone, Debug)]
304pub struct Puppet<T = ()> {
305	pub meta: PuppetMeta,
306	pub physics: PuppetPhysics,
307	pub nodes: InoxNodeTree<T>,
308	pub drivers: Vec<InoxNodeUuid>,
309	pub(crate) params: HashMap<ParamUuid, Param>,
310	pub(crate) param_names: HashMap<String, ParamUuid>,
311	pub render_ctx: RenderCtx,
312}
313
314impl<T> Puppet<T> {
315	pub fn new(
316		meta: PuppetMeta,
317		physics: PuppetPhysics,
318		nodes: InoxNodeTree<T>,
319		named_params: HashMap<String, Param>,
320	) -> Self {
321		let render_ctx = RenderCtx::new(&nodes);
322
323		let drivers = (nodes.arena.iter())
324			.filter_map(|node| {
325				let node = node.get();
326
327				match node.data {
328					InoxData::SimplePhysics(_) => Some(node.uuid),
329					_ => None,
330				}
331			})
332			.collect::<Vec<_>>();
333
334		let mut params = HashMap::new();
335		let mut param_names = HashMap::new();
336		for (name, param) in named_params {
337			param_names.insert(name, param.uuid);
338			params.insert(param.uuid, param);
339		}
340
341		Self {
342			meta,
343			physics,
344			nodes,
345			drivers,
346			params,
347			param_names,
348			render_ctx,
349		}
350	}
351}