1use crate::ui::{Graphic, SampleStrat, Transform, Ui};
2use common::{
3 assets::{self, AssetCombined, AssetExt, AssetHandle, Concatenate, DotVoxAsset, ReloadWatcher},
4 comp::item::item_key::ItemKey,
5 figure::Segment,
6};
7use conrod_core::image::Id;
8use hashbrown::HashMap;
9use image::DynamicImage;
10use serde::{Deserialize, Serialize};
11use std::sync::Arc;
12use tracing::{error, warn};
13use vek::*;
14
15pub fn animate_by_pulse(ids: &[Id], pulse: f32) -> Id {
16 let animation_frame = (pulse * 3.0) as usize;
17 ids[animation_frame % ids.len()]
18}
19
20#[derive(Debug, Serialize, Deserialize)]
21pub enum ImageSpec {
22 Png(String),
23 Vox(String, #[serde(default)] u32),
24 VoxTrans(String, [f32; 3], [f32; 3], f32, #[serde(default)] u32),
26}
27impl ImageSpec {
28 pub fn create_graphic(&self) -> Graphic {
29 match self {
30 ImageSpec::Png(specifier) => Graphic::Image(graceful_load_img(specifier), None),
31 ImageSpec::Vox(specifier, model_index) => Graphic::Voxel(
32 graceful_load_segment_no_skin(specifier, *model_index),
33 Transform {
34 stretch: false,
35 ..Default::default()
36 },
37 SampleStrat::None,
38 ),
39 ImageSpec::VoxTrans(specifier, offset, [rot_x, rot_y, rot_z], zoom, model_index) => {
40 Graphic::Voxel(
41 graceful_load_segment_no_skin(specifier, *model_index),
42 Transform {
43 ori: Quaternion::rotation_x(rot_x * std::f32::consts::PI / 180.0)
44 .rotated_y(rot_y * std::f32::consts::PI / 180.0)
45 .rotated_z(rot_z * std::f32::consts::PI / 180.0),
46 offset: Vec3::from(*offset),
47 zoom: *zoom,
48 orth: true, stretch: false,
50 },
51 SampleStrat::None,
52 )
53 },
54 }
55 }
56}
57
58#[derive(Serialize, Deserialize)]
59pub struct ItemImagesSpec(pub HashMap<ItemKey, ImageSpec>);
60impl assets::Asset for ItemImagesSpec {
61 type Loader = assets::RonLoader;
62
63 const EXTENSION: &'static str = "ron";
64}
65impl Concatenate for ItemImagesSpec {
66 fn concatenate(self, b: Self) -> Self { ItemImagesSpec(self.0.concatenate(b.0)) }
67}
68
69pub struct ItemImgs {
71 map: HashMap<ItemKey, Id>,
72 manifest: AssetHandle<ItemImagesSpec>,
73 watcher: ReloadWatcher,
74 not_found: Id,
75}
76
77impl ItemImgs {
78 pub fn new(ui: &mut Ui, not_found: Id) -> Self {
79 let manifest = ItemImagesSpec::load_expect_combined_static("voxygen.item_image_manifest");
80 let map = manifest
81 .read()
82 .0
83 .iter()
84 .map(|(kind, spec)| (kind.clone(), ui.add_graphic(spec.create_graphic())))
88 .collect();
89
90 Self {
91 map,
92 manifest,
93 watcher: manifest.reload_watcher(),
94 not_found,
95 }
96 }
97
98 pub fn reload_if_changed(&mut self, ui: &mut Ui) {
101 if self.watcher.reloaded() {
102 for (kind, spec) in self.manifest.read().0.iter() {
103 let graphic = spec.create_graphic();
105 match self.map.get(kind) {
107 Some(id) => ui.replace_graphic(*id, graphic),
108 None => {
110 self.map.insert(kind.clone(), ui.add_graphic(graphic));
111 },
112 }
113 }
114 }
115 }
116
117 pub fn img_ids(&self, item_key: ItemKey) -> Vec<Id> {
118 if let ItemKey::TagExamples(keys, _) = item_key {
119 return keys
120 .iter()
121 .filter_map(|k| self.map.get(k))
122 .cloned()
123 .collect();
124 };
125 match self.map.get(&item_key) {
126 Some(id) => vec![*id],
127 None => {
129 warn!(
130 ?item_key,
131 "missing specified image file (note: hot-reloading won't work here)",
132 );
133 Vec::new()
134 },
135 }
136 }
137
138 pub fn img_ids_or_not_found_img(&self, item_key: ItemKey) -> Vec<Id> {
139 let mut ids = self.img_ids(item_key);
140 if ids.is_empty() {
141 ids.push(self.not_found)
142 }
143 ids
144 }
145}
146
147fn graceful_load_vox(specifier: &str) -> AssetHandle<DotVoxAsset> {
150 let full_specifier: String = ["voxygen.", specifier].concat();
151 match DotVoxAsset::load(full_specifier.as_str()) {
152 Ok(dot_vox) => dot_vox,
153 Err(_) => {
154 error!(?full_specifier, "Could not load vox file for item images",);
155 DotVoxAsset::load_expect("voxygen.voxel.not_found")
156 },
157 }
158}
159fn graceful_load_img(specifier: &str) -> Arc<DynamicImage> {
160 let full_specifier: String = ["voxygen.", specifier].concat();
161 let handle = match assets::Image::load(&full_specifier) {
162 Ok(img) => img,
163 Err(_) => {
164 error!(?full_specifier, "Could not load image file for item images");
165 assets::Image::load_expect("voxygen.element.not_found")
166 },
167 };
168 handle.read().to_image()
169}
170
171fn graceful_load_segment_no_skin(specifier: &str, model_index: u32) -> Arc<Segment> {
172 use common::figure::{MatSegment, mat_cell::MatCell};
173 let mat_seg = MatSegment::from_vox_model_index(
174 &graceful_load_vox(specifier).read().0,
175 model_index as usize,
176 );
177 let seg = mat_seg
178 .map(|mat_cell| match mat_cell {
179 MatCell::None => None,
180 MatCell::Mat(_) => Some(MatCell::None),
181 MatCell::Normal(data) => data.is_hollow().then_some(MatCell::None),
182 })
183 .to_segment(|_| Default::default());
184 Arc::new(seg)
185}