use std::{
collections::HashMap,
hash::Hash,
ops::{Deref, DerefMut},
};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, Default, Clone, PartialEq)]
pub struct Model {
pub parent: Option<String>,
pub display: Option<Display>,
pub textures: Option<Textures>,
pub elements: Option<Vec<Element>>,
#[serde(rename = "ambientocclusion")]
pub ambient_occlusion: Option<bool>,
#[serde(rename = "gui_light")]
pub gui_light_mode: Option<GuiLightMode>,
pub overrides: Option<Vec<OverrideCase>>,
}
#[derive(Deserialize, Serialize, Debug, Default, Clone, PartialEq)]
pub struct Display {
pub thirdperson_righthand: Option<Transform>,
pub thirdperson_lefthand: Option<Transform>,
pub firstperson_righthand: Option<Transform>,
pub firstperson_lefthand: Option<Transform>,
pub gui: Option<Transform>,
pub head: Option<Transform>,
pub ground: Option<Transform>,
pub fixed: Option<Transform>,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct Transform {
#[serde(default = "Transform::zeros")]
pub rotation: [f32; 3],
#[serde(default = "Transform::zeros")]
pub translation: [f32; 3],
#[serde(default = "Transform::ones")]
pub scale: [f32; 3],
}
impl Transform {
pub(crate) const fn zeros() -> [f32; 3] {
[0.0; 3]
}
pub(crate) const fn ones() -> [f32; 3] {
[1.0; 3]
}
}
impl Default for Transform {
fn default() -> Self {
Self {
rotation: [0.0, 0.0, 0.0],
translation: [0.0, 0.0, 0.0],
scale: [1.0, 1.0, 1.0],
}
}
}
#[derive(Deserialize, Serialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct Textures {
#[serde(flatten)]
pub variables: HashMap<String, Texture>,
}
impl Textures {
pub fn resolve(&mut self, other: &Self) {
for texture in self.values_mut() {
if let Some(substitution) = texture.resolve(other) {
*texture = Texture::from(substitution);
}
}
}
pub fn merge(&mut self, other: Self) {
for (name, texture) in other.variables.into_iter() {
self.insert(name, texture);
}
}
}
impl<K, V> From<HashMap<K, V>> for Textures
where
K: Into<String>,
V: Into<Texture>,
{
fn from(source: HashMap<K, V>) -> Self {
let variables = source
.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect();
Self { variables }
}
}
impl Deref for Textures {
type Target = HashMap<String, Texture>;
fn deref(&self) -> &Self::Target {
&self.variables
}
}
impl DerefMut for Textures {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.variables
}
}
#[derive(Deserialize, Serialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct Texture(pub String);
impl Texture {
pub fn location(&self) -> Option<&str> {
if self.0.starts_with('#') {
None
} else {
Some(&self.0[..])
}
}
pub fn reference(&self) -> Option<&str> {
if self.0.starts_with('#') {
Some(&self.0[1..])
} else {
None
}
}
pub fn resolve<'a>(&'a self, substitutions: &'a Textures) -> Option<&'a str> {
if let Some(reference) = self.reference() {
if let Some(substitution) = substitutions.get(reference) {
return Some(&substitution.0);
}
}
None
}
}
impl From<String> for Texture {
fn from(source: String) -> Self {
Self(source)
}
}
impl<'a> From<&'a str> for Texture {
fn from(source: &'a str) -> Self {
Self(String::from(source))
}
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct Element {
pub from: [f32; 3],
pub to: [f32; 3],
pub faces: HashMap<BlockFace, ElementFace>,
#[serde(default)]
pub rotation: ElementRotation,
#[serde(default = "Element::default_shade")]
pub shade: bool,
}
impl Element {
pub(crate) const fn default_shade() -> bool {
true
}
}
impl Default for Element {
fn default() -> Self {
Self {
from: [0.0, 0.0, 0.0],
to: [16.0, 16.0, 16.0],
faces: Default::default(),
rotation: Default::default(),
shade: Self::default_shade(),
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct ElementRotation {
pub origin: [f32; 3],
pub axis: Axis,
pub angle: f32,
#[serde(default = "ElementRotation::default_rescale")]
pub rescale: bool,
}
impl ElementRotation {
pub(crate) const fn default_rescale() -> bool {
false
}
}
impl Default for ElementRotation {
fn default() -> Self {
Self {
origin: [0.0, 0.0, 0.0],
axis: Axis::X,
angle: 0.0,
rescale: Self::default_rescale(),
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct ElementFace {
pub uv: Option<[f32; 4]>,
pub texture: Texture,
#[serde(rename = "cullface")]
pub cull_face: Option<BlockFace>,
#[serde(default = "ElementFace::default_rotation")]
pub rotation: u32,
#[serde(rename = "tintindex", default = "ElementFace::default_tint_index")]
pub tint_index: i32,
}
impl ElementFace {
pub(crate) const fn default_rotation() -> u32 {
0
}
pub(crate) const fn default_tint_index() -> i32 {
-1
}
}
impl Default for ElementFace {
fn default() -> Self {
Self {
uv: Default::default(),
texture: Default::default(),
cull_face: Default::default(),
rotation: Self::default_rotation(),
tint_index: Self::default_tint_index(),
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct OverrideCase {
pub predicate: HashMap<String, PredicateValue>,
pub model: String,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
#[allow(missing_docs)]
pub enum PredicateValue {
Int(u32),
Float(f32),
}
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(rename_all = "lowercase")]
pub enum GuiLightMode {
Side,
Front,
}
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(rename_all = "lowercase")]
#[allow(missing_docs)]
pub enum Axis {
X,
Y,
Z,
}
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(rename_all = "lowercase")]
#[allow(missing_docs)]
#[repr(u8)]
pub enum BlockFace {
#[serde(alias = "bottom")]
Down,
Up,
North,
South,
West,
East,
}