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 }