veloren_voxygen/scene/
tether.rs

1use 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    /// Used to garbage-collect tethers that no longer exist.
19    ///
20    /// Because a tether is not an entity, but instead a relationship between
21    /// two entities, there is no single 'event' that we can listen to in
22    /// order to determine that a tether has been broken. Instead, every tick,
23    /// we go through the set of tethers that we observe in the world and
24    /// mark their entries in the `tethers` map below with a flag.
25    /// At the end of the tick, every unmarked tether in the `tethers` map below
26    /// can be deleted.
27    ///
28    /// Every tick, the 'alive' state of the flag flips between `true` and
29    /// `false` to avoid the need to wastefully reset the flag of every
30    /// alive tether on each tick (this is a common optimisation in some garbage
31    /// collection algorithms too).
32    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}