1use core::{
2 ecs::Entity,
3 prefab::{Prefab, PrefabError, PrefabProxy},
4 state::StateToken,
5 Scalar,
6};
7use ncollide2d::{
8 pipeline::CollisionGroups,
9 shape::{Ball, Capsule, ConvexPolygon, Cuboid, HeightField, Plane, Segment, ShapeHandle},
10};
11use nphysics2d::{
12 math::{Inertia, Isometry, Point, Vector, Velocity},
13 object::{
14 ActivationStatus, BodyStatus, ColliderDesc, DefaultBodyHandle, DefaultColliderHandle,
15 RigidBodyDesc,
16 },
17};
18use serde::{Deserialize, Serialize};
19use std::collections::HashMap;
20#[cfg(not(feature = "scalar64"))]
21use std::f32::{consts::PI as SCALAR_PI, MAX as SCALAR_MAX};
22#[cfg(feature = "scalar64")]
23use std::f64::{consts::PI as SCALAR_PI, MAX as SCALAR_MAX};
24
25pub(crate) enum RigidBody2dInner {
26 None,
27 Description(RigidBodyDesc<Scalar>),
28 Handle(DefaultBodyHandle),
29}
30
31pub struct RigidBody2d(pub(crate) RigidBody2dInner);
32
33impl RigidBody2d {
34 pub fn new(desc: RigidBodyDesc<Scalar>) -> Self {
35 Self(RigidBody2dInner::Description(desc))
36 }
37
38 pub fn is_created(&self) -> bool {
39 matches!(&self.0, RigidBody2dInner::Handle(_))
40 }
41
42 pub fn handle(&self) -> Option<DefaultBodyHandle> {
43 if let RigidBody2dInner::Handle(handle) = &self.0 {
44 Some(*handle)
45 } else {
46 None
47 }
48 }
49
50 pub(crate) fn take_description(&mut self) -> Option<RigidBodyDesc<Scalar>> {
51 if self.is_created() {
52 return None;
53 }
54 let inner = std::mem::replace(&mut self.0, RigidBody2dInner::None);
55 if let RigidBody2dInner::Description(desc) = inner {
56 Some(desc)
57 } else {
58 None
59 }
60 }
61}
62
63#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
64pub enum RigidBody2dPrefabProxyBodyStatus {
65 Disabled,
66 Static,
67 Dynamic,
68 Kinematic,
69}
70
71impl Default for RigidBody2dPrefabProxyBodyStatus {
72 fn default() -> Self {
73 Self::Dynamic
74 }
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct RigidBody2dPrefabProxy {
79 #[serde(default = "RigidBody2dPrefabProxy::default_gravity_enabled")]
80 pub gravity_enabled: bool,
81 #[serde(default = "RigidBody2dPrefabProxy::default_linear_motion_interpolation_enabled")]
82 pub linear_motion_interpolation_enabled: bool,
83 #[serde(default = "RigidBody2dPrefabProxy::default_position")]
84 pub position: Vector<Scalar>,
85 #[serde(default = "RigidBody2dPrefabProxy::default_rotation")]
86 pub rotation: Scalar,
87 #[serde(default = "RigidBody2dPrefabProxy::default_velocity")]
88 pub velocity: (Vector<Scalar>, Scalar),
89 #[serde(default = "RigidBody2dPrefabProxy::default_linear_damping")]
90 pub linear_damping: Scalar,
91 #[serde(default = "RigidBody2dPrefabProxy::default_angular_damping")]
92 pub angular_damping: Scalar,
93 #[serde(default = "RigidBody2dPrefabProxy::default_max_linear_velocity")]
94 pub max_linear_velocity: Scalar,
95 #[serde(default = "RigidBody2dPrefabProxy::default_max_angular_velocity")]
96 pub max_angular_velocity: Scalar,
97 #[serde(default = "RigidBody2dPrefabProxy::default_local_inertia")]
98 pub local_inertia: (Scalar, Scalar),
99 #[serde(default = "RigidBody2dPrefabProxy::default_local_center_of_mass")]
100 pub local_center_of_mass: Point<Scalar>,
101 #[serde(default = "RigidBody2dPrefabProxy::default_status")]
102 pub status: RigidBody2dPrefabProxyBodyStatus,
103 #[serde(default = "RigidBody2dPrefabProxy::default_sleep_threshold")]
104 pub sleep_threshold: Option<Scalar>,
105 #[serde(default = "RigidBody2dPrefabProxy::default_kinematic_translations")]
106 pub kinematic_translations: Vector<bool>,
107 #[serde(default = "RigidBody2dPrefabProxy::default_kinematic_rotations")]
108 pub kinematic_rotations: bool,
109}
110
111impl Default for RigidBody2dPrefabProxy {
112 fn default() -> Self {
113 Self {
114 gravity_enabled: Self::default_gravity_enabled(),
115 linear_motion_interpolation_enabled: Self::default_linear_motion_interpolation_enabled(
116 ),
117 position: Self::default_position(),
118 rotation: Self::default_rotation(),
119 velocity: Self::default_velocity(),
120 linear_damping: Self::default_linear_damping(),
121 angular_damping: Self::default_angular_damping(),
122 max_linear_velocity: Self::default_max_linear_velocity(),
123 max_angular_velocity: Self::default_max_angular_velocity(),
124 local_inertia: Self::default_local_inertia(),
125 local_center_of_mass: Self::default_local_center_of_mass(),
126 status: Self::default_status(),
127 sleep_threshold: Self::default_sleep_threshold(),
128 kinematic_translations: Self::default_kinematic_translations(),
129 kinematic_rotations: Self::default_kinematic_rotations(),
130 }
131 }
132}
133
134impl RigidBody2dPrefabProxy {
135 fn default_gravity_enabled() -> bool {
136 true
137 }
138
139 fn default_linear_motion_interpolation_enabled() -> bool {
140 false
141 }
142
143 fn default_position() -> Vector<Scalar> {
144 Vector::new(0.0, 0.0)
145 }
146
147 fn default_rotation() -> Scalar {
148 0.0
149 }
150
151 fn default_velocity() -> (Vector<Scalar>, Scalar) {
152 (Vector::new(0.0, 0.0), 0.0)
153 }
154
155 fn default_linear_damping() -> Scalar {
156 0.0
157 }
158
159 fn default_angular_damping() -> Scalar {
160 0.0
161 }
162
163 fn default_max_linear_velocity() -> Scalar {
164 SCALAR_MAX
165 }
166
167 fn default_max_angular_velocity() -> Scalar {
168 SCALAR_MAX
169 }
170
171 fn default_local_inertia() -> (Scalar, Scalar) {
172 (0.0, 0.0)
173 }
174
175 fn default_local_center_of_mass() -> Point<Scalar> {
176 Point::origin()
177 }
178
179 fn default_status() -> RigidBody2dPrefabProxyBodyStatus {
180 RigidBody2dPrefabProxyBodyStatus::Dynamic
181 }
182
183 #[allow(clippy::unnecessary_wraps)]
184 fn default_sleep_threshold() -> Option<Scalar> {
185 Some(ActivationStatus::default_threshold())
186 }
187
188 fn default_kinematic_translations() -> Vector<bool> {
189 Vector::repeat(false)
190 }
191
192 fn default_kinematic_rotations() -> bool {
193 false
194 }
195}
196
197impl Prefab for RigidBody2dPrefabProxy {}
198
199impl PrefabProxy<RigidBody2dPrefabProxy> for RigidBody2d {
200 fn from_proxy_with_extras(
201 proxy: RigidBody2dPrefabProxy,
202 _: &HashMap<String, Entity>,
203 _: StateToken,
204 ) -> Result<Self, PrefabError> {
205 let desc = RigidBodyDesc::new()
206 .gravity_enabled(proxy.gravity_enabled)
207 .linear_motion_interpolation_enabled(proxy.linear_motion_interpolation_enabled)
208 .position(Isometry::new(proxy.position, proxy.rotation))
209 .velocity(Velocity::new(proxy.velocity.0, proxy.velocity.1))
210 .linear_damping(proxy.linear_damping)
211 .angular_damping(proxy.angular_damping)
212 .max_linear_velocity(proxy.max_linear_velocity)
213 .max_angular_velocity(proxy.max_angular_velocity)
214 .local_inertia(Inertia::new(proxy.local_inertia.0, proxy.local_inertia.1))
215 .local_center_of_mass(proxy.local_center_of_mass)
216 .status(match proxy.status {
217 RigidBody2dPrefabProxyBodyStatus::Disabled => BodyStatus::Disabled,
218 RigidBody2dPrefabProxyBodyStatus::Static => BodyStatus::Static,
219 RigidBody2dPrefabProxyBodyStatus::Dynamic => BodyStatus::Dynamic,
220 RigidBody2dPrefabProxyBodyStatus::Kinematic => BodyStatus::Kinematic,
221 })
222 .sleep_threshold(proxy.sleep_threshold)
223 .kinematic_translations(proxy.kinematic_translations)
224 .kinematic_rotations(proxy.kinematic_rotations);
225 Ok(Self::new(desc))
226 }
227}
228
229pub(crate) enum Collider2dInner {
230 None,
231 Description(ColliderDesc<Scalar>),
232 Handle(DefaultColliderHandle),
233}
234
235pub struct Collider2d(pub(crate) Collider2dInner);
236
237impl Collider2d {
238 pub fn new(desc: ColliderDesc<Scalar>) -> Self {
239 Self(Collider2dInner::Description(desc))
240 }
241
242 pub fn is_created(&self) -> bool {
243 matches!(&self.0, Collider2dInner::Handle(_))
244 }
245
246 pub fn handle(&self) -> Option<DefaultColliderHandle> {
247 if let Collider2dInner::Handle(handle) = &self.0 {
248 Some(*handle)
249 } else {
250 None
251 }
252 }
253
254 pub(crate) fn take_description(&mut self) -> Option<ColliderDesc<Scalar>> {
255 if self.is_created() {
256 return None;
257 }
258 let inner = std::mem::replace(&mut self.0, Collider2dInner::None);
259 if let Collider2dInner::Description(desc) = inner {
260 Some(desc)
261 } else {
262 None
263 }
264 }
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub enum Collider2dPrefabProxyShape {
269 Ball(Ball<Scalar>),
270 Capsule(Capsule<Scalar>),
271 ConvexPolygon(ConvexPolygon<Scalar>),
272 Cuboid(Cuboid<Scalar>),
273 HeightField(HeightField<Scalar>),
274 Plane(Plane<Scalar>),
275 Segment(Segment<Scalar>),
276}
277
278impl Default for Collider2dPrefabProxyShape {
279 fn default() -> Self {
280 Self::Ball(Ball::new(1.0))
281 }
282}
283
284#[derive(Debug, Default, Clone, Serialize, Deserialize)]
285pub struct Collider2dPrefabProxyCollisionGroups {
286 pub membership: Option<Vec<usize>>,
287 pub whitelist: Option<Vec<usize>>,
288 pub blacklist: Option<Vec<usize>>,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct Collider2dPrefabProxy {
293 #[serde(default = "Collider2dPrefabProxy::default_margin")]
294 pub margin: Scalar,
295 #[serde(default)]
296 pub collision_groups: Collider2dPrefabProxyCollisionGroups,
297 #[serde(default)]
298 pub shape: Collider2dPrefabProxyShape,
299 #[serde(default = "Collider2dPrefabProxy::default_position")]
300 pub position: Vector<Scalar>,
301 #[serde(default = "Collider2dPrefabProxy::default_rotation")]
302 pub rotation: Scalar,
303 #[serde(default = "Collider2dPrefabProxy::default_density")]
304 pub density: Scalar,
305 #[serde(default = "Collider2dPrefabProxy::default_linear_prediction")]
306 pub linear_prediction: Scalar,
307 #[serde(default = "Collider2dPrefabProxy::default_angular_prediction")]
308 pub angular_prediction: Scalar,
309 #[serde(default = "Collider2dPrefabProxy::default_is_sensor")]
310 pub is_sensor: bool,
311 #[serde(default = "Collider2dPrefabProxy::default_ccd_enabled")]
312 pub ccd_enabled: bool,
313}
314
315impl Default for Collider2dPrefabProxy {
316 fn default() -> Self {
317 Self {
318 margin: Self::default_margin(),
319 collision_groups: Collider2dPrefabProxyCollisionGroups::default(),
320 shape: Collider2dPrefabProxyShape::default(),
321 position: Self::default_position(),
322 rotation: Self::default_rotation(),
323 density: Self::default_density(),
324 linear_prediction: Self::default_linear_prediction(),
325 angular_prediction: Self::default_angular_prediction(),
326 is_sensor: Self::default_is_sensor(),
327 ccd_enabled: Self::default_ccd_enabled(),
328 }
329 }
330}
331
332impl Collider2dPrefabProxy {
333 fn default_margin() -> Scalar {
334 0.01
335 }
336
337 fn default_position() -> Vector<Scalar> {
338 Vector::new(0.0, 0.0)
339 }
340
341 fn default_rotation() -> Scalar {
342 0.0
343 }
344
345 fn default_density() -> Scalar {
346 0.0
347 }
348
349 fn default_linear_prediction() -> Scalar {
350 0.001
351 }
352
353 fn default_angular_prediction() -> Scalar {
354 SCALAR_PI / 180.0 * 5.0
355 }
356
357 fn default_is_sensor() -> bool {
358 false
359 }
360
361 fn default_ccd_enabled() -> bool {
362 false
363 }
364}
365
366impl Prefab for Collider2dPrefabProxy {}
367
368impl PrefabProxy<Collider2dPrefabProxy> for Collider2d {
369 fn from_proxy_with_extras(
370 proxy: Collider2dPrefabProxy,
371 _: &HashMap<String, Entity>,
372 _: StateToken,
373 ) -> Result<Self, PrefabError> {
374 let shape = match proxy.shape {
375 Collider2dPrefabProxyShape::Ball(shape) => ShapeHandle::new(shape),
376 Collider2dPrefabProxyShape::Capsule(shape) => ShapeHandle::new(shape),
377 Collider2dPrefabProxyShape::ConvexPolygon(shape) => ShapeHandle::new(shape),
378 Collider2dPrefabProxyShape::Cuboid(shape) => ShapeHandle::new(shape),
379 Collider2dPrefabProxyShape::HeightField(shape) => ShapeHandle::new(shape),
380 Collider2dPrefabProxyShape::Plane(shape) => ShapeHandle::new(shape),
381 Collider2dPrefabProxyShape::Segment(shape) => ShapeHandle::new(shape),
382 };
383 let desc = ColliderDesc::new(shape)
384 .margin(proxy.margin)
385 .collision_groups({
386 let mut result = CollisionGroups::new();
387 if let Some(list) = proxy.collision_groups.membership {
388 result = result.with_membership(&list)
389 }
390 if let Some(list) = proxy.collision_groups.whitelist {
391 result = result.with_whitelist(&list)
392 }
393 if let Some(list) = proxy.collision_groups.blacklist {
394 result = result.with_blacklist(&list)
395 }
396 result
397 })
398 .position(Isometry::new(proxy.position, proxy.rotation))
399 .density(proxy.density)
400 .linear_prediction(proxy.linear_prediction)
401 .angular_prediction(proxy.angular_prediction)
402 .sensor(proxy.is_sensor)
403 .ccd_enabled(proxy.ccd_enabled);
404 Ok(Self::new(desc))
405 }
406}
407
408#[derive(Debug, Copy, Clone)]
409pub enum Collider2dBody {
410 Me,
411 Entity(Entity),
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub enum Collider2dBodyPrefabProxy {
416 Me,
417 Entity(String),
418}
419
420impl Prefab for Collider2dBodyPrefabProxy {}
421
422impl PrefabProxy<Collider2dBodyPrefabProxy> for Collider2dBody {
423 fn from_proxy_with_extras(
424 proxy: Collider2dBodyPrefabProxy,
425 named_entities: &HashMap<String, Entity>,
426 _: StateToken,
427 ) -> Result<Self, PrefabError> {
428 match proxy {
429 Collider2dBodyPrefabProxy::Me => Ok(Self::Me),
430 Collider2dBodyPrefabProxy::Entity(name) => {
431 if let Some(entity) = named_entities.get(&name) {
432 Ok(Self::Entity(*entity))
433 } else {
434 Err(PrefabError::Custom(format!(
435 "Could not find entity named: {}",
436 name
437 )))
438 }
439 }
440 }
441 }
442}