1use std::collections::BTreeMap;
2
3use pfx_core::rig::{Blend, Input as RigInput, Probe, Vec3, View};
4use pfx_input::Input;
5use pfx_load::scene::Projection;
6use pfx_physics::rigid::{self, Filter, Pose, Shape};
7
8use crate::rigs::{Kind, RigDesc, Rigs};
9use crate::world::{ObjectId, World};
10
11const ASPECT: f32 = 16.0 / 9.0;
12
13pub(super) struct Driver {
14 rigs: BTreeMap<String, RigDesc>,
15 active: Option<String>,
16 blend: Option<Blend>,
17 aspect: f32,
18 mouse: [f32; 2],
19 pointer: Option<[f32; 2]>,
20 grounded: bool,
21}
22
23impl Driver {
24 pub(super) fn new(rigs: Rigs) -> Self {
25 Self {
26 rigs: rigs.into_map(),
27 active: None,
28 blend: None,
29 aspect: ASPECT,
30 mouse: [0.0; 2],
31 pointer: None,
32 grounded: true,
33 }
34 }
35}
36
37struct Cast<'a> {
38 physics: &'a rigid::World,
39 filter: Filter,
40}
41
42impl Probe for Cast<'_> {
43 fn clear(&self, from: Vec3, direction: Vec3, radius: f32, max: f32) -> f32 {
44 let shape = Shape::Sphere {
45 radius: radius.max(1e-3),
46 };
47 match self
48 .physics
49 .shape_cast(&shape, Pose::at(from), direction, max, self.filter)
50 {
51 Some(hit) => hit.distance.min(max),
52 None => max,
53 }
54 }
55}
56
57impl World {
58 pub fn rig_names(&self) -> impl Iterator<Item = &str> {
59 self.driver.rigs.keys().map(String::as_str)
60 }
61
62 pub fn rig_desc(&self, name: &str) -> Option<&RigDesc> {
63 self.driver.rigs.get(name)
64 }
65
66 pub fn rig_state(&self, name: &str) -> Option<&pfx_core::rig::Rig> {
67 self.driver.rigs.get(name).map(|desc| &desc.rig)
68 }
69
70 pub fn rig_state_mut(&mut self, name: &str) -> Option<&mut pfx_core::rig::Rig> {
71 self.driver.rigs.get_mut(name).map(|desc| &mut desc.rig)
72 }
73
74 pub fn active_rig(&self) -> Option<&str> {
75 self.driver.active.as_deref()
76 }
77
78 pub fn rig(&mut self, name: &str) -> bool {
79 if self.driver.active.as_deref() == Some(name) {
80 return true;
81 }
82 let Some(desc) = self.driver.rigs.get_mut(name) else {
83 return false;
84 };
85 desc.rig.restart();
86 let blend = desc.blend;
87 let plane = desc.kind == Kind::Follow2d;
88 let height = desc.height;
89 let from = View {
90 at: self.camera.at,
91 look_at: self.camera.look_at,
92 up: self.camera.up,
93 };
94 self.driver.blend = (blend > 0.0).then(|| Blend::new(from, blend));
95 self.driver.active = Some(name.to_string());
96 if plane && let Some(height) = height {
97 self.camera.projection = Projection::Orthographic { height };
98 }
99 true
100 }
101
102 pub fn rig_off(&mut self) {
103 self.driver.active = None;
104 self.driver.blend = None;
105 }
106
107 pub fn rig_target(&mut self, name: &str, target: Option<&str>) -> bool {
108 if let Some(object) = target
109 && self.object(object).is_none()
110 {
111 return false;
112 }
113 let Some(desc) = self.driver.rigs.get_mut(name) else {
114 return false;
115 };
116 desc.target = target.map(str::to_string);
117 desc.rig.hand_over();
118 true
119 }
120
121 pub fn rig_look(&mut self, mouse: [f32; 2]) {
122 self.driver.mouse[0] += mouse[0];
123 self.driver.mouse[1] += mouse[1];
124 }
125
126 pub fn set_view_aspect(&mut self, aspect: f32) {
127 if aspect.is_finite() && aspect > 0.0 {
128 self.driver.aspect = aspect;
129 }
130 }
131
132 fn rig_target_id(&self, target: &Option<String>) -> Option<ObjectId> {
133 target.as_deref().and_then(|name| self.object(name))
134 }
135
136 pub(crate) fn step_rigs(&mut self, input: &Input) {
137 let mouse = std::mem::take(&mut self.driver.mouse);
138 let pointer = input.pointer().layout;
139 let moved = match (self.driver.pointer, pointer) {
140 (Some(before), Some(now)) => [now[0] - before[0], now[1] - before[1]],
141 _ => [0.0; 2],
142 };
143 self.driver.pointer = pointer;
144 let Some(name) = &self.driver.active else {
145 return;
146 };
147 let Some(desc) = self.driver.rigs.get(name) else {
148 return;
149 };
150 let id = self.rig_target_id(&desc.target);
151 let target = id.map_or([0.0; 3], |id| self.at(id));
152 let aspect = self.driver.aspect;
153 let state = input.action(&desc.look).vector;
154 let mouse = if desc.mouse {
155 [mouse[0] + moved[0], mouse[1] + moved[1]]
156 } else {
157 mouse
158 };
159 let mut rig_input = RigInput::at(target).looking(state).mouse(mouse);
160 rig_input.grounded = self.driver.grounded;
161 let mut filter = Filter::layers(desc.collide);
162 if let Some(body) = id.and_then(|id| self.body(id)) {
163 filter = filter.excluding(body);
164 }
165 let comfort = self.fx.comfort;
166 let dt = self.clock.tick_dt() as f32;
167 let probe = Cast {
168 physics: &self.physics,
169 filter,
170 };
171 let height = desc.height;
172 let fit = desc.fit_view;
173 let desc = self.driver.rigs.get_mut(name).expect("the rig is there");
174 if fit && let (Some(follow), Some(height)) = (desc.rig.follow_mut(), height) {
175 follow.set_view_half([height * aspect * 0.5, height * 0.5]);
176 }
177 desc.rig.tick(dt, &rig_input, comfort, &probe);
178 if let Some(blend) = &mut self.driver.blend {
179 blend.step(dt);
180 }
181 if self.driver.blend.is_some_and(|blend| blend.done()) {
182 self.driver.blend = None;
183 }
184 }
185
186 pub fn rig_grounded(&mut self, grounded: bool) {
187 self.driver.grounded = grounded;
188 }
189
190 pub(crate) fn apply_rig(&mut self, alpha: f32) {
191 let Some(name) = &self.driver.active else {
192 return;
193 };
194 let Some(desc) = self.driver.rigs.get(name) else {
195 return;
196 };
197 if !desc.rig.started() {
198 return;
199 }
200 let mut view = desc.rig.pose(alpha);
201 if let Some(blend) = &self.driver.blend {
202 view = blend.apply(view);
203 }
204 self.camera.at = view.at;
205 self.camera.look_at = view.look_at;
206 self.camera.up = view.up;
207 }
208}