1use super::{SceneData, figure::cache::ToolKey};
2use crate::render::{DynamicModel, Mesh, Quad, Renderer, TrailDrawer, TrailVertex};
3use common::{
4 assets::{AssetExt, BoxedError, FileAsset, load_ron},
5 comp::{
6 Body, Item, Pos, Vel,
7 item::ItemKind,
8 object,
9 tool::{Tool, ToolKind},
10 },
11};
12use common_base::span;
13use serde::Deserialize;
14use specs::{Entity as EcsEntity, Join, WorldExt};
15use std::{borrow::Cow, collections::HashMap};
16use vek::*;
17
18lazy_static::lazy_static! {
19 pub static ref TOOL_TRAIL_MANIFEST: ToolTrailManifest = ToolTrailManifest::load_expect_cloned("voxygen.voxel.tool_trail_manifest");
20}
21
22#[derive(Clone, Debug, Deserialize)]
23pub struct ToolTrailManifest(pub HashMap<ToolKey, ([f32; 3], [f32; 3])>);
24
25impl FileAsset for ToolTrailManifest {
26 const EXTENSION: &'static str = "ron";
27
28 fn from_bytes(bytes: Cow<[u8]>) -> Result<Self, BoxedError> { load_ron(&bytes) }
29}
30
31impl ToolTrailManifest {
32 pub fn get(&self, item: &Item) -> Option<(Vec3<f32>, Vec3<f32>)> {
33 let tool_key = ToolKey::from(item);
34 self.0
35 .get(&tool_key)
36 .map(|trail_points| (Vec3::from(trail_points.0), Vec3::from(trail_points.1)))
37 .or_else(|| {
38 if let ItemKind::Tool(Tool { kind, .. }) = *item.kind() {
39 let z_offsets = match kind {
40 ToolKind::Sword => (0.0, 29.25),
41 ToolKind::Axe => (10.0, 19.25),
42 ToolKind::Hammer => (10.0, 19.25),
43 ToolKind::Staff => (10.0, 19.25),
44 ToolKind::Sceptre => (10.0, 19.25),
45 _ => (0.0, 0.0),
46 };
47 Some((
48 Vec3::new(0.0, 0.0, z_offsets.0),
49 Vec3::new(0.0, 0.0, z_offsets.1),
50 ))
51 } else {
52 None
53 }
54 })
55 }
56}
57
58#[derive(Clone, Copy, Eq, PartialEq, Hash)]
59struct MeshKey {
60 entity: EcsEntity,
61 is_main_weapon: bool,
62}
63
64#[derive(Default)]
65pub struct TrailMgr {
66 entity_meshes: HashMap<MeshKey, (Mesh<TrailVertex>, usize)>,
68
69 pos_cache: HashMap<EcsEntity, Pos>,
71
72 offset: usize,
74
75 dynamic_model: Option<DynamicModel<TrailVertex>>,
77
78 model_len: u32,
80}
81
82const TRAIL_DYNAMIC_MODEL_SIZE: usize = 15;
83const TRAIL_SHRINKAGE: f32 = 0.8;
84
85impl TrailMgr {
86 pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData) {
87 span!(_guard, "maintain", "TrailMgr::maintain");
88
89 if scene_data.weapon_trails_enabled {
90 let ecs = scene_data.state.ecs();
92 for (entity, body, vel, pos) in (
93 &ecs.entities(),
94 &ecs.read_storage::<Body>(),
95 &ecs.read_storage::<Vel>(),
96 &ecs.read_storage::<Pos>(),
97 )
98 .join()
99 {
100 const MIN_SPEED: f32 = 15.0;
101 if vel.0.magnitude_squared() > MIN_SPEED.powi(2)
102 && matches!(
103 body,
104 Body::Object(
105 object::Body::Arrow
106 | object::Body::MultiArrow
107 | object::Body::ArrowSnake
108 | object::Body::ArrowTurret
109 | object::Body::ArrowClay
110 | object::Body::ArrowHeavy
111 | object::Body::BoltBesieger,
112 )
113 )
114 {
115 let last_pos = *self.pos_cache.entry(entity).or_insert(*pos);
116 let offset = self.offset;
117 let quad_mesh = self.entity_mesh_or_insert(entity, true);
118 const THICKNESS: f32 = 0.2;
119 let p1 = pos.0;
120 let p2 = p1 + Vec3::unit_z() * THICKNESS;
121 let p4 = last_pos.0;
122 let p3 = p4 + Vec3::unit_z() * THICKNESS;
123 let vertex = |p: Vec3<f32>| TrailVertex {
124 pos: p.into_array(),
125 };
126 let quad = Quad::new(vertex(p1), vertex(p2), vertex(p3), vertex(p4));
127 quad_mesh.replace_quad(offset * 4, quad);
128 self.pos_cache.insert(entity, *pos);
129 }
130 }
131
132 self.offset = (self.offset + 1) % TRAIL_DYNAMIC_MODEL_SIZE;
134
135 self.entity_meshes.values_mut().for_each(|(mesh, _)| {
136 let vertices = mesh.vertices_mut_vec();
139 let last_offset =
140 (self.offset + TRAIL_DYNAMIC_MODEL_SIZE - 1) % TRAIL_DYNAMIC_MODEL_SIZE;
141 let next_offset = (self.offset + 1) % TRAIL_DYNAMIC_MODEL_SIZE;
142 for i in 0..TRAIL_DYNAMIC_MODEL_SIZE {
143 let [b, c, a, d] = [0, 1, 2, 3].map(|offset| i * 4 + offset);
145 vertices[a] = if i == next_offset {
146 vertices[b]
147 } else {
148 vertices[a] * TRAIL_SHRINKAGE + vertices[b] * (1.0 - TRAIL_SHRINKAGE)
149 };
150 if i != last_offset {
151 vertices[d] =
153 vertices[d] * TRAIL_SHRINKAGE + vertices[c] * (1.0 - TRAIL_SHRINKAGE);
154 }
155 }
156
157 let zero = TrailVertex::zero();
159 mesh.replace_quad(self.offset * 4, Quad::new(zero, zero, zero, zero));
160 });
161
162 self.entity_meshes
164 .retain(|_, (_mesh, last_updated)| *last_updated != self.offset);
165
166 let mut big_mesh = Mesh::new();
171 self.entity_meshes
172 .values()
173 .filter(|(mesh, _)| mesh.iter().any(|vert| *vert != TrailVertex::zero()))
175 .for_each(|(mesh, _)| big_mesh.push_mesh(mesh));
176
177 if big_mesh.is_empty() {
179 let zero = TrailVertex::zero();
180 big_mesh.push_quad(Quad::new(zero, zero, zero, zero));
181 }
182
183 if self.dynamic_model.as_ref().map_or(0, |model| model.len()) < big_mesh.len() {
186 self.dynamic_model = Some(
187 renderer
188 .create_dynamic_model(big_mesh.len() + TRAIL_DYNAMIC_MODEL_SIZE * 4 * 10),
189 );
190 }
191 if let Some(dynamic_model) = &self.dynamic_model {
192 renderer.update_model(dynamic_model, &big_mesh, 0);
193 }
194 self.model_len = big_mesh.len() as u32;
195 } else {
196 self.entity_meshes.clear();
197 self.dynamic_model = None;
199 }
200 }
201
202 pub fn render<'a>(&'a self, drawer: &mut TrailDrawer<'_, 'a>, scene_data: &SceneData) {
203 span!(_guard, "render", "TrailMgr::render");
204 if scene_data.weapon_trails_enabled
205 && let Some(dynamic_model) = &self.dynamic_model
206 {
207 drawer.draw(dynamic_model.submodel(0..self.model_len))
208 }
209 }
210
211 pub fn entity_mesh_or_insert(
212 &mut self,
213 entity: EcsEntity,
214 is_main_weapon: bool,
215 ) -> &mut Mesh<TrailVertex> {
216 let key = MeshKey {
217 entity,
218 is_main_weapon,
219 };
220 &mut self
221 .entity_meshes
222 .entry(key)
223 .and_modify(|(_mesh, offset)| *offset = self.offset)
224 .or_insert((Self::default_trail_mesh(), self.offset))
225 .0
226 }
227
228 fn default_trail_mesh() -> Mesh<TrailVertex> {
229 let mut mesh = Mesh::new();
230 let zero = TrailVertex::zero();
231 for _ in 0..TRAIL_DYNAMIC_MODEL_SIZE {
232 mesh.push_quad(Quad::new(zero, zero, zero, zero));
233 }
234 mesh
235 }
236
237 pub fn offset(&self) -> usize { self.offset }
238}