veloren_voxygen/scene/
tether.rs1use crate::render::{
2 FirstPassDrawer, Mesh, Model, Quad, Renderer,
3 pipelines::rope::{BoundLocals, Locals, Vertex},
4};
5use client::Client;
6use common::{
7 comp,
8 link::Is,
9 tether::Follower,
10 uid::{IdMaps, Uid},
11};
12use hashbrown::HashMap;
13use specs::{Join, LendJoin, WorldExt};
14use vek::*;
15
16pub struct TetherMgr {
17 model: Model<Vertex>,
18 stale_flag: bool,
33 tethers: HashMap<(Uid, Uid), (BoundLocals, bool)>,
34}
35
36impl TetherMgr {
37 pub fn new(renderer: &mut Renderer) -> Self {
38 Self {
39 model: renderer.create_model(&create_tether_mesh()).unwrap(),
40 stale_flag: true,
41 tethers: HashMap::default(),
42 }
43 }
44
45 pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client, focus_off: Vec3<f32>) {
46 let interpolated = client
47 .state()
48 .read_storage::<crate::ecs::comp::Interpolated>();
49 let scales = client.state().read_storage::<comp::Scale>();
50 let bodies = client.state().read_storage::<comp::Body>();
51 let id_maps = client.state().ecs().read_resource::<IdMaps>();
52 let is_followers = client.state().read_storage::<Is<Follower>>();
53 for (interp, is_follower, body, scale) in
54 (&interpolated, &is_followers, bodies.maybe(), scales.maybe()).join()
55 {
56 let Some(leader) = id_maps.uid_entity(is_follower.leader) else {
57 continue;
58 };
59 let pos_a = interpolated.get(leader).map_or(Vec3::zero(), |i| i.pos)
60 + interpolated.get(leader).zip(bodies.get(leader)).map_or(
61 Vec3::zero(),
62 |(i, body)| {
63 i.ori.to_quat()
64 * body.tether_offset_leader()
65 * scales.get(leader).copied().unwrap_or(comp::Scale(1.0)).0
66 },
67 );
68 let pos_b = interp.pos
69 + body.map_or(Vec3::zero(), |body| {
70 interp.ori.to_quat()
71 * body.tether_offset_follower()
72 * scale.copied().unwrap_or(comp::Scale(1.0)).0
73 });
74
75 let (locals, stale_flag) = self
76 .tethers
77 .entry((is_follower.leader, is_follower.follower))
78 .or_insert_with(|| {
79 (
80 renderer.create_rope_bound_locals(&[Locals::default()]),
81 self.stale_flag,
82 )
83 });
84
85 renderer.update_consts(locals, &[Locals::new(
86 pos_a - focus_off,
87 pos_b - focus_off,
88 is_follower.tether_length,
89 )]);
90
91 *stale_flag = self.stale_flag;
92 }
93
94 self.tethers.retain(|_, (_, flag)| *flag == self.stale_flag);
95
96 self.stale_flag ^= true;
97 }
98
99 pub fn render<'a>(&'a self, drawer: &mut FirstPassDrawer<'a>) {
100 let mut rope_drawer = drawer.draw_ropes();
101 for (locals, _) in self.tethers.values() {
102 rope_drawer.draw(&self.model, locals);
103 }
104 }
105}
106
107fn create_tether_mesh() -> Mesh<Vertex> {
108 const SEGMENTS: usize = 10;
109 const RADIAL_SEGMENTS: usize = 6;
110
111 (0..RADIAL_SEGMENTS)
112 .flat_map(|i| {
113 let at_angle = |x: f32| {
114 let theta = x / RADIAL_SEGMENTS as f32 * std::f32::consts::TAU;
115 Vec2::new(theta.sin(), theta.cos())
116 };
117 let start = at_angle(i as f32);
118 let end = at_angle(i as f32 + 1.0);
119 (0..SEGMENTS).map(move |s| {
120 let z = s as f32 / SEGMENTS as f32;
121 Quad {
122 a: Vertex::new(start.with_z(z), start.with_z(0.0)),
123 b: Vertex::new(start.with_z(z + 1.0 / SEGMENTS as f32), start.with_z(0.0)),
124 c: Vertex::new(end.with_z(z + 1.0 / SEGMENTS as f32), end.with_z(0.0)),
125 d: Vertex::new(end.with_z(z), end.with_z(0.0)),
126 }
127 })
128 })
129 .collect()
130}