1use yog_data::{Ordered, Reader, Schema, Shape, Value, ValueRef};
4
5#[derive(Debug, Clone, PartialEq)]
9pub enum Texture {
10 Slot(String),
11 Ref(String),
13}
14
15pub const SLOT_MARK: char = '#';
17
18impl Schema for Texture {
19 fn shape() -> Shape {
20 yog_data::schema::ref_shape::<yog_data::kinds::Texture>()
21 }
22 fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
23 if let ValueRef::Str(s) = v
24 && let Some(slot) = s.strip_prefix(SLOT_MARK)
25 {
26 if slot.is_empty() {
27 cx.problem("`#` needs a slot name");
28 return None;
29 }
30 return Some(Texture::Slot(slot.to_string()));
31 }
32 let id = yog_data::Id::<yog_data::kinds::Texture>::read(v, cx)?;
33 Some(Texture::Ref(yog_ids::qualify(&id.id)))
34 }
35 fn write(&self) -> Value {
36 match self {
37 Texture::Slot(s) => Value::Str(format!("{SLOT_MARK}{s}")),
38 Texture::Ref(id) => yog_data::Id::<yog_data::kinds::Texture>::new(id.clone()).write(),
39 }
40 }
41}
42
43#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq)]
44pub enum Axis {
45 X,
46 Y,
47 Z,
48}
49
50#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq)]
51pub enum GuiLight {
52 Front,
53 Side,
54}
55
56#[derive(Schema, Debug, Clone)]
58pub struct ElementRotation {
59 #[data(range = "-16..=32")]
61 pub origin: Option<[f64; 3]>,
62 pub axis: Axis,
63 #[data(range = "-45..=45")]
65 pub angle: f64,
66 #[data(default)]
67 pub rescale: bool,
68}
69
70#[derive(Schema, Debug, Clone)]
72pub struct Face {
73 pub texture: String,
75 #[data(range = "0..=16")]
76 pub uv: Option<[f64; 4]>,
77 pub rotation: Option<u32>,
79 pub cullface: Option<yog_core::Face>,
81 #[data(range = "-1..=255")]
83 pub tint: Option<i64>,
84}
85
86#[derive(Schema, Debug, Clone, Default)]
87pub struct Faces {
88 pub down: Option<Face>,
89 pub up: Option<Face>,
90 pub north: Option<Face>,
91 pub south: Option<Face>,
92 pub west: Option<Face>,
93 pub east: Option<Face>,
94}
95
96impl Faces {
97 pub fn iter(&self) -> impl Iterator<Item = (&'static str, &Face)> {
98 [
99 ("down", &self.down),
100 ("up", &self.up),
101 ("north", &self.north),
102 ("south", &self.south),
103 ("west", &self.west),
104 ("east", &self.east),
105 ]
106 .into_iter()
107 .filter_map(|(d, f)| f.as_ref().map(|f| (d, f)))
108 }
109}
110
111#[derive(Schema, Debug, Clone)]
113pub struct Element {
114 pub name: Option<String>,
115 #[data(range = "-16..=32")]
116 pub from: [f64; 3],
117 #[data(range = "-16..=32")]
118 pub to: [f64; 3],
119 pub rotation: Option<ElementRotation>,
120 pub shade: Option<bool>,
121 pub faces: Option<Faces>,
122}
123
124#[derive(Schema, Debug, Clone)]
126pub struct Transform {
127 #[data(range = "-360..=360")]
128 pub rotation: Option<[f64; 3]>,
129 #[data(range = "-80..=80")]
130 pub translation: Option<[f64; 3]>,
131 #[data(range = "-4..=4")]
132 pub scale: Option<[f64; 3]>,
133}
134
135#[derive(Schema, Debug, Clone, Default)]
136pub struct Display {
137 pub thirdperson_righthand: Option<Transform>,
138 pub thirdperson_lefthand: Option<Transform>,
139 pub firstperson_righthand: Option<Transform>,
140 pub firstperson_lefthand: Option<Transform>,
141 pub gui: Option<Transform>,
142 pub head: Option<Transform>,
143 pub ground: Option<Transform>,
144 pub fixed: Option<Transform>,
145}
146
147impl Display {
148 pub fn iter(&self) -> impl Iterator<Item = (&'static str, &Transform)> {
149 [
150 ("thirdperson_righthand", &self.thirdperson_righthand),
151 ("thirdperson_lefthand", &self.thirdperson_lefthand),
152 ("firstperson_righthand", &self.firstperson_righthand),
153 ("firstperson_lefthand", &self.firstperson_lefthand),
154 ("gui", &self.gui),
155 ("head", &self.head),
156 ("ground", &self.ground),
157 ("fixed", &self.fixed),
158 ]
159 .into_iter()
160 .filter_map(|(d, t)| t.as_ref().map(|t| (d, t)))
161 }
162}
163
164#[derive(Schema, Debug, Clone)]
167pub struct Model {
168 pub parent: Option<String>,
169 #[data(with = "yog_data::with::Opt<yog_data::with::RefTo<yog_data::kinds::Material>>")]
172 pub material: Option<String>,
173 #[data(default)]
175 pub textures: Ordered<Texture>,
176 pub elements: Option<Vec<Element>>,
177 pub display: Option<Display>,
178 pub ambient_occlusion: Option<bool>,
179 pub gui_light: Option<GuiLight>,
180}
181
182#[derive(Schema, Debug, Clone)]
184pub struct Rotation {
185 pub x: Option<u32>,
187 pub y: Option<u32>,
189 #[data(range = "1..=1000000")]
190 pub weight: Option<f64>,
191 #[data(default)]
192 pub uvlock: bool,
193}
194
195#[derive(Schema, Debug, Clone)]
197pub struct Connect {
198 pub arm: Model,
200 #[data(default)]
201 pub uvlock: bool,
202}
203
204#[derive(Schema, Debug, Clone)]
205pub struct States {
206 pub rotations: Option<Vec<Rotation>>,
207 pub connect: Option<Connect>,
208 pub facing: Option<Facing>,
211 pub placement: Option<Placement>,
214 #[data(default)]
218 pub attach: bool,
219 #[data(default)]
221 pub waterlogged: bool,
222 #[data(default)]
225 pub uvlock: bool,
226 #[data(default)]
229 pub properties: Ordered<Vec<String>>,
230 #[data(default)]
232 pub models: Ordered<Model>,
233 #[data(default)]
239 pub variants: Ordered<ModelNames>,
240}
241
242pub type ModelNames = yog_data::OneOrList<String>;
244
245pub use yog_registry::{Facing, Placement};