/ core / src / objects / mod.rs
mod.rs
  1  //! Objects to be drawn to the screen.
  2  
  3  mod appearance;
  4  mod color;
  5  pub use appearance::*;
  6  use bytemuck::AnyBitPattern;
  7  pub use color::Color;
  8  
  9  #[cfg(feature = "physics")]
 10  pub mod physics;
 11  
 12  use glam::Vec2;
 13  use let_engine_macros::Vertex;
 14  #[cfg(feature = "physics")]
 15  use physics::*;
 16  
 17  use super::scenes::LayerId;
 18  
 19  use slotmap::new_key_type;
 20  
 21  use glam::{Mat4, Quat, Vec3};
 22  
 23  /// Holds position size and rotation of an object.
 24  #[derive(Clone, Copy, Debug, PartialEq, Vertex, AnyBitPattern)]
 25  pub struct Transform {
 26      #[format(Rg32Float)]
 27      pub position: Vec3,
 28      #[format(Rg32Float)]
 29      pub size: Vec3,
 30      #[format(R32Float)]
 31      pub rotation: Quat,
 32  }
 33  
 34  impl Transform {
 35      const ORIGIN: Self = Self {
 36          position: Vec3::ZERO,
 37          size: Vec3::ONE,
 38          rotation: Quat::IDENTITY,
 39      };
 40  
 41      /// Creates a new [`Transform`].
 42      #[inline]
 43      pub fn new(position: Vec3, size: Vec3, rotation: Quat) -> Self {
 44          Self {
 45              position,
 46              size,
 47              rotation,
 48          }
 49      }
 50  
 51      /// Creates a new [`Transform`] from 2D arguments
 52      #[inline]
 53      pub fn new_2d(position: Vec2, size: Vec2, rotation: f32) -> Self {
 54          Self {
 55              position: position.extend(0.0),
 56              size: size.extend(1.0),
 57              rotation: Quat::from_rotation_z(rotation),
 58          }
 59      }
 60  
 61      /// Creates a new [`Transform`] with the given position.
 62      #[inline]
 63      pub fn with_position(position: Vec3) -> Self {
 64          Self {
 65              position,
 66              ..Default::default()
 67          }
 68      }
 69  
 70      /// Creates a new [`Transform`] with the given 2D position.
 71      #[inline]
 72      pub fn with_position_2d(position: Vec2) -> Self {
 73          Self {
 74              position: position.extend(0.0),
 75              ..Default::default()
 76          }
 77      }
 78  
 79      /// Creates a new [`Transform`] with the given size.
 80      #[inline]
 81      pub fn with_size(size: Vec3) -> Self {
 82          Self {
 83              size,
 84              ..Default::default()
 85          }
 86      }
 87  
 88      /// Creates a new [`Transform`] with the given 2D size.
 89      #[inline]
 90      pub fn with_size_2d(size: Vec2) -> Self {
 91          Self {
 92              size: size.extend(1.0),
 93              ..Default::default()
 94          }
 95      }
 96  
 97      /// Creates a new [`Transform`] with the given rotation.
 98      #[inline]
 99      pub fn with_rotation(rotation: Quat) -> Self {
100          Self {
101              rotation,
102              ..Default::default()
103          }
104      }
105  
106      /// Creates a new [`Transform`] with the given x angle.
107      #[inline]
108      pub fn with_angle_x(angle: f32) -> Self {
109          Self {
110              rotation: Quat::from_rotation_x(angle),
111              ..Default::default()
112          }
113      }
114  
115      /// Creates a new [`Transform`] with the given y angle.
116      #[inline]
117      pub fn with_angle_y(angle: f32) -> Self {
118          Self {
119              rotation: Quat::from_rotation_y(angle),
120              ..Default::default()
121          }
122      }
123  
124      /// Creates a new [`Transform`] with the given z angle.
125      #[inline]
126      pub fn with_angle_z(angle: f32) -> Self {
127          Self {
128              rotation: Quat::from_rotation_z(angle),
129              ..Default::default()
130          }
131      }
132  
133      /// Creates a new [`Transform`] with the given position and size.
134      #[inline]
135      pub fn with_position_size(position: Vec3, size: Vec3) -> Self {
136          Self {
137              position,
138              size,
139              ..Default::default()
140          }
141      }
142  
143      /// Creates a new [`Transform`] with the given 2D position and size.
144      #[inline]
145      pub fn with_position_size_2d(position: Vec2, size: Vec2) -> Self {
146          Self {
147              position: position.extend(0.0),
148              size: size.extend(1.0),
149              ..Default::default()
150          }
151      }
152  
153      /// Creates a new [`Transform`] with the given position and rotation.
154      #[inline]
155      pub fn with_position_rotation(position: Vec3, rotation: Quat) -> Self {
156          Self {
157              position,
158              rotation,
159              ..Default::default()
160          }
161      }
162  
163      /// Creates a new [`Transform`] with the given size and rotation.
164      #[inline]
165      pub fn with_size_rotation(size: Vec3, rotation: Quat) -> Self {
166          Self {
167              size,
168              rotation,
169              ..Default::default()
170          }
171      }
172  
173      /// 2D angle in radians
174      #[inline]
175      pub fn angle(&self) -> f32 {
176          self.rotation.to_euler(glam::EulerRot::ZYX).0
177      }
178  
179      /// Turns the transform into a four dimensional affine transformation matrix.
180      pub fn to_matrix(&self) -> Mat4 {
181          Mat4::from_scale_rotation_translation(self.size, self.rotation, self.position)
182      }
183  }
184  
185  impl From<Mat4> for Transform {
186      fn from(value: Mat4) -> Self {
187          let (size, rotation, position) = value.to_scale_rotation_translation();
188          Transform {
189              position,
190              size,
191              rotation,
192          }
193      }
194  }
195  
196  impl std::ops::Mul for Transform {
197      type Output = Self;
198      fn mul(self, rhs: Self) -> Self::Output {
199          let child_matrix = rhs.to_matrix();
200          let parent_matrix = self.to_matrix();
201  
202          let (scale, rotation, translation) =
203              (parent_matrix * child_matrix).to_scale_rotation_translation();
204  
205          Self {
206              position: translation,
207              size: scale,
208              rotation,
209          }
210      }
211  }
212  
213  impl std::ops::MulAssign for Transform {
214      fn mul_assign(&mut self, rhs: Self) {
215          *self = *self * rhs;
216      }
217  }
218  
219  impl From<(Vec3, Quat)> for Transform {
220      fn from(value: (Vec3, Quat)) -> Self {
221          Self {
222              position: value.0,
223              rotation: value.1,
224              ..Default::default()
225          }
226      }
227  }
228  
229  impl From<Transform> for (Vec3, Quat) {
230      fn from(value: Transform) -> Self {
231          (value.position, value.rotation)
232      }
233  }
234  
235  /// Returns the origin transform at 0, 0 with rotation 0 and size 1, 1.
236  impl Default for Transform {
237      fn default() -> Self {
238          Self::ORIGIN
239      }
240  }
241  
242  /// Object to be initialized to the layer.
243  #[derive(Clone, Default)]
244  pub struct ObjectBuilder<T: Loaded> {
245      pub transform: Transform,
246      pub appearance: Option<Appearance<T>>,
247      #[cfg(feature = "physics")]
248      pub(crate) physics: ObjectPhysics,
249  }
250  
251  /// An initialized object that gets rendered on the screen.
252  pub struct Object<T: Loaded = ()> {
253      pub transform: Transform,
254      pub appearance: Option<Appearance<T>>,
255      pub(crate) children: Vec<ObjectId>,
256      pub(crate) parent_id: Option<ObjectId>,
257      pub(crate) layer_id: LayerId,
258      #[cfg(feature = "physics")]
259      pub(crate) physics: ObjectPhysics,
260  }
261  
262  new_key_type! { pub struct ObjectId; }
263  
264  impl<T: Loaded> ObjectBuilder<T> {
265      /// Returns a default object with the given appearance and transform.
266      #[inline]
267      pub fn with_appearance_transform(appearance: Appearance<T>, transform: Transform) -> Self {
268          #[allow(clippy::needless_update)]
269          Self {
270              appearance: Some(appearance),
271              transform,
272              ..Default::default()
273          }
274      }
275  
276      /// Returns a default object with the given appearance.
277      #[inline]
278      pub fn with_appearance(appearance: Appearance<T>) -> Self {
279          Self {
280              appearance: Some(appearance),
281              ..Default::default()
282          }
283      }
284  
285      /// Returns an object with the given transform.
286      #[inline]
287      pub fn with_transform(transform: Transform) -> Self {
288          Self {
289              transform,
290              ..Default::default()
291          }
292      }
293  }
294  
295  /// Setters
296  impl<T: Loaded> ObjectBuilder<T> {
297      /// Sets the position and rotation of an object.
298      pub fn set_isometry(&mut self, position: Vec3, rotation: Quat) {
299          self.transform.position = position;
300          self.transform.rotation = rotation;
301      }
302  }
303  
304  /// Physics
305  #[cfg(feature = "physics")]
306  impl<T: Loaded> ObjectBuilder<T> {
307      /// Returns the collider of the object in case it has one.
308      #[cfg(feature = "physics")]
309      pub fn collider(&self) -> Option<&Collider> {
310          self.physics.collider.as_ref()
311      }
312      /// Sets the collider of the object.
313      #[cfg(feature = "physics")]
314      pub fn set_collider(&mut self, collider: Option<Collider>) {
315          self.physics.collider = collider;
316      }
317      /// Returns a mutable reference to the collider.
318      #[cfg(feature = "physics")]
319      pub fn collider_mut(&mut self) -> Option<&mut Collider> {
320          self.physics.collider.as_mut()
321      }
322      /// Returns the rigid bodyh of the object in case it has one.
323      #[cfg(feature = "physics")]
324      pub fn rigid_body(&self) -> Option<&RigidBody> {
325          self.physics.rigid_body.as_ref()
326      }
327      /// Sets the rigid body of the object.
328      #[cfg(feature = "physics")]
329      pub fn set_rigid_body(&mut self, rigid_body: Option<RigidBody>) {
330          self.physics.rigid_body = rigid_body;
331      }
332      /// Returns a mutable reference to the rigid body.
333      #[cfg(feature = "physics")]
334      pub fn rigid_body_mut(&mut self) -> Option<&mut RigidBody> {
335          self.physics.rigid_body.as_mut()
336      }
337      /// Returns the local position of the collider.
338      #[cfg(feature = "physics")]
339      pub fn local_collider_position(&self) -> Vec2 {
340          self.physics.local_collider_position
341      }
342      /// Sets the local position of the collider of this object in case it has one.
343      #[cfg(feature = "physics")]
344      pub fn set_local_collider_position(&mut self, pos: Vec2) {
345          self.physics.local_collider_position = pos;
346      }
347  }
348  
349  impl<T: Loaded> Object<T> {
350      pub fn layer_id(&self) -> LayerId {
351          self.layer_id
352      }
353  
354      pub fn parent(&self) -> Option<ObjectId> {
355          self.parent_id
356      }
357  
358      #[inline]
359      pub fn visual_transform(&self) -> Transform {
360          let mut transform = self.transform;
361  
362          if let Some(appearance) = self.appearance() {
363              transform *= *appearance.transform();
364          }
365          transform
366      }
367  
368      /// Makes a new object from this object.
369      pub fn to_builder(&self) -> ObjectBuilder<T> {
370          ObjectBuilder {
371              transform: self.transform,
372              appearance: self.appearance.clone(),
373              #[cfg(feature = "physics")]
374              physics: self.physics.clone(),
375          }
376      }
377  
378      /// Sets the position and rotation of an object.
379      pub fn set_isometry(&mut self, position: Vec3, rotation: Quat) {
380          self.transform.position = position;
381          self.transform.rotation = rotation;
382      }
383  
384      /// Returns a reference to the appearance of the object.
385      pub fn appearance(&self) -> Option<&Appearance<T>> {
386          self.appearance.as_ref()
387      }
388  }
389  
390  /// Physics
391  #[cfg(feature = "physics")]
392  impl<T: Loaded> Object<T> {
393      pub(crate) fn rigidbody_handle(&self) -> Option<rapier2d::dynamics::RigidBodyHandle> {
394          self.physics.rigid_body_handle
395      }
396  
397      /// Returns the collider of the object in case it has one.
398      pub fn collider(&self) -> Option<&Collider> {
399          self.physics.collider.as_ref()
400      }
401  
402      /// Sets the collider of the object.
403      pub fn set_collider(&mut self, collider: Option<Collider>) {
404          self.physics.collider = collider;
405      }
406  
407      /// Returns a mutable reference to the collider.
408      pub fn collider_mut(&mut self) -> Option<&mut Collider> {
409          self.physics.collider.as_mut()
410      }
411  
412      /// Returns the rigid bodyh of the object in case it has one.
413      pub fn rigid_body(&self) -> Option<&RigidBody> {
414          self.physics.rigid_body.as_ref()
415      }
416  
417      /// Sets the rigid body of the object.
418      pub fn set_rigid_body(&mut self, rigid_body: Option<RigidBody>) {
419          self.physics.rigid_body = rigid_body;
420      }
421  
422      /// Returns a mutable reference to the rigid body.
423      pub fn rigid_body_mut(&mut self) -> Option<&mut RigidBody> {
424          self.physics.rigid_body.as_mut()
425      }
426  
427      /// Returns the local position of the collider.
428      pub fn local_collider_position(&self) -> Vec2 {
429          self.physics.local_collider_position
430      }
431  
432      /// Sets the local position of the collider of this object in case it has one.
433      pub fn set_local_collider_position(&mut self, pos: Vec2) {
434          self.physics.local_collider_position = pos;
435      }
436  }
437  
438  // Object based errors.
439  
440  use thiserror::Error;
441  
442  use crate::backend::gpu::Loaded;
443  
444  /// Errors that happen in object and layer functions.
445  #[derive(Error, Debug)]
446  pub enum ObjectError {
447      /// The object you are trying to access is not initialized anymore.
448      #[error("This object was removed from the objects list.")]
449      NoObject,
450  }