button.rs
1 use std::time::{Duration, Instant}; 2 3 use anyhow::{Result, anyhow}; 4 use glam::{Vec2, Vec3Swizzles, vec2, vec3}; 5 use let_engine::{ 6 core_backend::gpu::{GpuInterface, Loaded}, 7 events::{ElementState, InputEvent}, 8 objects::{ 9 Appearance, AppearanceBuilder, Color, Descriptor, ObjectBuilder, ObjectId, Transform, 10 }, 11 prelude::{ 12 AppearanceBuilderError, GraphicsShaders, LoadedBuffer, LoadedModel, LoadedTexture, 13 MaterialSettings, ViewTypeDim, tvert, 14 }, 15 resources::{ 16 buffer::{Buffer, BufferAccess, BufferUsage, Location, PreferOperation}, 17 data::TVert, 18 material::Material, 19 model::Model, 20 }, 21 scenes::{LayerViewId, Scene}, 22 }; 23 24 use crate::{ 25 TextureSection, WidgetManager, 26 label::{Label, LabelBuilder}, 27 }; 28 29 #[derive(Clone)] 30 pub struct Button<T: Loaded + 'static> { 31 object_id: ObjectId, 32 label: Option<(ObjectId, Label<T>)>, 33 view_id: LayerViewId, 34 35 resources: ButtonResources<T>, 36 variant: ButtonVariant<T>, 37 38 bounding_box: BoundingBox, 39 transform: Transform, 40 41 state: ButtonState, 42 pub(crate) handle: bool, 43 } 44 45 #[derive(Clone, Default)] 46 pub struct ButtonBuilder<T: Loaded> { 47 pub variant: ButtonVariant<T>, 48 49 /// The optional label tied to this button. 50 /// 51 /// - If the variant is `solid`, `color` of this builder is overridden. 52 pub label_builder: Option<LabelBuilder>, 53 54 pub bounding_box: BoundingBox, 55 56 /// The transform of the bounding box. 57 pub transform: Transform, 58 59 pub object_transform: Transform, 60 } 61 62 impl<T: Loaded> ButtonBuilder<T> { 63 /// Sets `variant` and returns self 64 pub fn variant(mut self, variant: ButtonVariant<T>) -> Self { 65 self.variant = variant; 66 self 67 } 68 69 /// Sets the optional label of this object and returns self 70 pub fn label(mut self, builder: LabelBuilder) -> Self { 71 self.label_builder = Some(builder); 72 self 73 } 74 75 /// Sets `bounding_box` and returns self 76 pub fn bounding_box(mut self, bounding_box: impl Into<BoundingBox>) -> Self { 77 self.bounding_box = bounding_box.into(); 78 self 79 } 80 81 /// Sets the transform of the bounding box and solid appearance, then returns self 82 pub fn transform(mut self, transform: Transform) -> Self { 83 self.transform = transform; 84 self 85 } 86 87 /// Sets the transform of the object and returns self 88 pub fn object_transform(mut self, transform: Transform) -> Self { 89 self.object_transform = transform; 90 self 91 } 92 } 93 94 slotmap::new_key_type! { 95 pub struct ButtonId; 96 } 97 98 #[derive(Clone)] 99 enum ButtonResources<T: Loaded> { 100 None, 101 102 /// Resources necessary for the solid button variation 103 Solid { 104 updating: bool, 105 last_color: Color, 106 last_label_color: Color, 107 buffer: T::BufferId<Color>, 108 }, 109 110 /// Resources necessary for the simple textured button variation 111 Textured { 112 updating: bool, 113 virtual_texture_id: T::TextureId, 114 model_id: T::ModelId<TVert>, 115 }, 116 117 Tiled { 118 last_size: Vec2, 119 texture_dimensions: Vec2, 120 121 buffer_id: T::BufferId<u32>, 122 model_id: T::ModelId<TVert>, 123 }, 124 } 125 126 #[derive(Debug, thiserror::Error)] 127 pub enum ButtonBuilderError<T: Loaded> { 128 #[error("Validation error: {0}")] 129 ValidationError(&'static str), 130 131 #[error("Error while creating appearance for object: {0}")] 132 AppearanceError(AppearanceBuilderError<T>), 133 } 134 135 impl<T: Loaded> ButtonBuilder<T> { 136 /// Builds and adds this button to the scene. 137 pub fn build( 138 self, 139 view_id: LayerViewId, 140 scene: &mut Scene<T>, 141 widget_manager: &mut WidgetManager<T>, 142 gpu_interface: &impl GpuInterface<T>, 143 ) -> Result<ButtonId, ButtonBuilderError<T>> { 144 let button = self.create_button(view_id, scene, widget_manager, gpu_interface)?; 145 Ok(widget_manager.buttons.insert(button)) 146 } 147 148 pub(crate) fn create_button( 149 mut self, 150 view_id: LayerViewId, 151 scene: &mut Scene<T>, 152 widget_manager: &mut WidgetManager<T>, 153 gpu_interface: &impl GpuInterface<T>, 154 ) -> Result<Button<T>, ButtonBuilderError<T>> { 155 let variant = self.variant; 156 let bounding_box = self.bounding_box; 157 let transform = self.transform; 158 159 let mut object_builder = ObjectBuilder::with_transform(transform); 160 161 // Initialize appearance 162 let (appearance, resources) = match &variant { 163 ButtonVariant::Invisible => (None, ButtonResources::None), 164 ButtonVariant::Solid { 165 regular, 166 regular_label, 167 .. 168 } => { 169 if let Some(builder) = self.label_builder.as_mut() { 170 builder.text_color = *regular_label; 171 } 172 // Initialize with regular appearance 173 Self::solid_appearance(gpu_interface, *regular, *regular_label, transform) 174 } 175 ButtonVariant::Textured(ButtonTexture { regular, .. }) => { 176 // Initialize with regular texture 177 Self::textured_appearance(gpu_interface, regular, transform) 178 } 179 ButtonVariant::Tiled { texture_id } => { 180 Self::tiled_appearance(gpu_interface, *texture_id, transform) 181 } 182 ButtonVariant::Custom(appearance) => (Some(appearance.clone()), ButtonResources::None), 183 }; 184 185 object_builder.appearance = appearance; 186 187 let view = scene 188 .view(view_id) 189 .ok_or(ButtonBuilderError::ValidationError( 190 "The provided layer view does not exist.", 191 ))?; 192 193 // Build object 194 let object_id = scene.add_object(view.layer_id(), object_builder).ok_or( 195 ButtonBuilderError::ValidationError("The provided layer does not exist."), 196 )?; 197 198 scene[object_id].transform = self.object_transform; 199 200 let label = if let Some(builder) = self.label_builder { 201 let label = builder.build(widget_manager, gpu_interface).unwrap(); 202 let appearance = label 203 .appearance(gpu_interface) 204 .map_err(ButtonBuilderError::AppearanceError)?; 205 let object_id = scene 206 .add_object_with_parent(object_id, ObjectBuilder::with_appearance(appearance)) 207 .unwrap(); 208 Some((object_id, label)) 209 } else { 210 None 211 }; 212 213 Ok(Button { 214 object_id, 215 label, 216 view_id, 217 218 resources, 219 variant, 220 bounding_box, 221 transform, 222 223 state: ButtonState::Released(Instant::now()), 224 handle: false, 225 }) 226 } 227 228 fn solid_appearance( 229 interface: &impl GpuInterface<T>, 230 color: Color, 231 label_color: Color, 232 mut transform: Transform, 233 ) -> (Option<Appearance<T>>, ButtonResources<T>) { 234 let model = interface.load_model(&Model::square()).unwrap(); 235 let material = interface 236 .load_material::<Vec2>(&Material::new_default()) 237 .unwrap(); 238 239 let buffer = interface 240 .load_buffer(&Buffer::from_data( 241 BufferUsage::Uniform, 242 BufferAccess::Pinned(PreferOperation::Write), 243 color, 244 )) 245 .unwrap(); 246 247 transform.position.z += 0.1; 248 249 let builder = AppearanceBuilder::default() 250 .model(model) 251 .material(material) 252 .transparent(color.is_transparent()) 253 .transform(transform) 254 .descriptors(&[ 255 (Location::new(0, 0), Descriptor::<T>::Mvp), 256 (Location::new(1, 0), Descriptor::buffer(buffer)), 257 ]); 258 259 if let Ok(result) = builder.build(interface) { 260 ( 261 Some(result), 262 ButtonResources::Solid { 263 updating: false, 264 last_color: color, 265 last_label_color: label_color, 266 buffer, 267 }, 268 ) 269 } else { 270 panic!("Could not create button appearance."); 271 } 272 } 273 274 fn textured_appearance( 275 interface: &impl GpuInterface<T>, 276 texture: &TextureSection<T>, 277 mut transform: Transform, 278 ) -> (Option<Appearance<T>>, ButtonResources<T>) { 279 let texture_id = interface.add_virtual_texture(texture.texture_id).unwrap(); 280 let (vertices, indices) = texture.vertices_indices(); 281 282 let model_id = interface 283 .load_model(&Model::new_indexed( 284 vertices.to_vec(), 285 indices.to_vec(), 286 BufferAccess::Staged, 287 )) 288 .unwrap(); 289 290 let material = interface 291 .load_material::<TVert>(&Material::default_textured()) 292 .unwrap(); 293 294 let buffer = interface 295 .load_buffer(&Buffer::from_data( 296 BufferUsage::Uniform, 297 BufferAccess::Pinned(PreferOperation::Write), 298 Color::WHITE, 299 )) 300 .unwrap(); 301 302 transform.position.z += 0.1; 303 304 let builder = AppearanceBuilder::default() 305 .model(model_id) 306 .material(material) 307 .transform(transform) 308 .transparent(true) 309 .descriptors(&[ 310 (Location::new(0, 0), Descriptor::<T>::Mvp), 311 (Location::new(1, 0), Descriptor::buffer(buffer)), 312 (Location::new(2, 0), Descriptor::Texture(texture_id)), 313 ]); 314 315 if let Ok(result) = builder.build(interface) { 316 ( 317 Some(result), 318 ButtonResources::Textured { 319 updating: false, 320 virtual_texture_id: texture_id, 321 model_id, 322 }, 323 ) 324 } else { 325 panic!("Could not create button appearance."); 326 } 327 } 328 329 fn tiled_appearance( 330 interface: &impl GpuInterface<T>, 331 texture_id: T::TextureId, 332 transform: Transform, 333 ) -> (Option<Appearance<T>>, ButtonResources<T>) { 334 let texture = interface 335 .texture(texture_id) 336 .expect("Provided texture ID is invalid."); 337 let ViewTypeDim::D2Array { x, y, .. } = *texture.dimensions() else { 338 panic!("Expected provided texture dimensions to be a 2 dimensional texture array."); 339 }; 340 341 let (vertices, indices) = 342 tiled_vertices_indices(transform.size.xy(), vec2(x as f32, y as f32)); 343 344 let model = Model::new_indexed(vertices.to_vec(), indices.to_vec(), BufferAccess::Staged); 345 346 let model_id = interface.load_model(&model).unwrap(); 347 348 let material = interface 349 .load_material::<TVert>(&Material::new( 350 MaterialSettings::default(), 351 GraphicsShaders::new( 352 include_bytes!("shaders/tiled_button_vert.spv").to_vec(), 353 "main".to_owned(), 354 include_bytes!("shaders/tiled_button_frag.spv").to_vec(), 355 "main".to_owned(), 356 ), 357 )) 358 .unwrap(); 359 360 let buffer_id = interface 361 .load_buffer(&Buffer::from_data( 362 BufferUsage::Uniform, 363 BufferAccess::Pinned(PreferOperation::Write), 364 0u32, 365 )) 366 .unwrap(); 367 368 let builder = AppearanceBuilder::default() 369 .model(model_id) 370 .material(material) 371 .transform(Transform::with_position(vec3(0.0, 0.0, 0.11))) 372 .transparent(true) 373 .descriptors(&[ 374 (Location::new(0, 0), Descriptor::<T>::Mvp), 375 (Location::new(1, 0), Descriptor::buffer(buffer_id)), 376 (Location::new(2, 0), Descriptor::Texture(texture_id)), 377 ]); 378 379 if let Ok(result) = builder.build(interface) { 380 ( 381 Some(result), 382 ButtonResources::Tiled { 383 last_size: transform.size.xy(), 384 texture_dimensions: vec2(x as f32, y as f32), 385 buffer_id, 386 model_id, 387 }, 388 ) 389 } else { 390 panic!("Could not create button appearance."); 391 } 392 } 393 } 394 395 impl<T: Loaded + 'static> Button<T> { 396 pub(super) fn update(&mut self, interface: &impl GpuInterface<T>) -> Result<()> { 397 match &mut self.resources { 398 ButtonResources::None => Ok(()), 399 ButtonResources::Solid { 400 updating, 401 last_color, 402 last_label_color, 403 buffer, 404 } => { 405 if !*updating { 406 return Ok(()); 407 } 408 let ButtonVariant::Solid { 409 interp, 410 regular, 411 hovered, 412 pressed, 413 regular_label, 414 hovered_label, 415 pressed_label, 416 } = &self.variant 417 else { 418 panic!("Expected `ButtonVariant` to be solid") 419 }; 420 421 let buffer = interface 422 .buffer(*buffer) 423 .ok_or(anyhow!("Buffer not found"))?; 424 425 match self.state { 426 ButtonState::Released(_) => { 427 let p = self.state.animation_completion(*interp); 428 buffer.write_data(|color| *color = last_color.lerp(*regular, p))?; 429 if let Some((_, label)) = self.label.as_mut() { 430 label.update_text_color(last_label_color.lerp(*regular_label, p))?; 431 } 432 if p >= 1.0 { 433 *updating = false; 434 } 435 } 436 ButtonState::Hovered(_) => { 437 let p = self.state.animation_completion(*interp); 438 buffer.write_data(|color| *color = last_color.lerp(*hovered, p))?; 439 if let Some((_, label)) = self.label.as_mut() { 440 label.update_text_color(last_label_color.lerp(*hovered_label, p))?; 441 } 442 if p >= 1.0 { 443 *updating = false; 444 } 445 } 446 ButtonState::Down(_) => { 447 buffer.write_data(|color| *color = *pressed)?; 448 if let Some((_, label)) = self.label.as_mut() { 449 label.update_text_color(*pressed_label)?; 450 } 451 *updating = false; 452 } 453 } 454 Ok(()) 455 } 456 ButtonResources::Textured { 457 updating, 458 virtual_texture_id, 459 model_id, 460 } => { 461 if !*updating { 462 return Ok(()); 463 } 464 let ButtonVariant::Textured(ButtonTexture { 465 regular, 466 hovered, 467 pressed, 468 }) = &self.variant 469 else { 470 panic!("Expected `ButtonVariant` to be textured"); 471 }; 472 473 let model = interface 474 .model(*model_id) 475 .ok_or(anyhow!("Model not found."))?; 476 477 let (vertices, texture_id) = match self.state { 478 ButtonState::Released(_) => (regular.vertices(), regular.texture_id), 479 ButtonState::Hovered(_) => (hovered.vertices(), hovered.texture_id), 480 ButtonState::Down(_) => (pressed.vertices(), pressed.texture_id), 481 }; 482 483 model.write_vertices(|write| write.copy_from_slice(&vertices), 4)?; 484 485 interface.map_virtual_texture(*virtual_texture_id, texture_id)?; 486 487 *updating = false; 488 489 Ok(()) 490 } 491 ButtonResources::Tiled { 492 last_size, 493 texture_dimensions, 494 buffer_id, 495 model_id, 496 } => { 497 let size = self.transform.size.xy(); 498 if size == *last_size { 499 return Ok(()); 500 } 501 *last_size = size; 502 503 let model = interface 504 .model(*model_id) 505 .ok_or(anyhow!("Model not found."))?; 506 507 let buffer = interface 508 .buffer(*buffer_id) 509 .ok_or(anyhow!("Buffer not found."))?; 510 511 let (vertices, _) = tiled_vertices_indices(size, *texture_dimensions); 512 513 model 514 .write_vertices(|write| write.copy_from_slice(&vertices), vertices.len()) 515 .unwrap(); 516 517 let index = match self.state { 518 ButtonState::Released(_) => 0, 519 ButtonState::Hovered(_) => 1, 520 ButtonState::Down(_) => 2, 521 }; 522 523 buffer.write_data(|write| *write = index).unwrap(); 524 525 Ok(()) 526 } 527 } 528 } 529 530 pub(crate) fn update_state(&mut self, state: ButtonState) { 531 match &mut self.resources { 532 ButtonResources::Solid { updating, .. } 533 | ButtonResources::Textured { updating, .. } => *updating = true, 534 ButtonResources::Tiled { last_size, .. } => *last_size = Vec2::ZERO, 535 _ => (), 536 } 537 538 if let ButtonResources::Solid { 539 last_color, 540 last_label_color, 541 .. 542 } = &mut self.resources 543 && let ButtonVariant::Solid { 544 interp, 545 regular, 546 hovered, 547 pressed, 548 regular_label, 549 hovered_label, 550 pressed_label, 551 } = &self.variant 552 { 553 let p = self.state.animation_completion(*interp); 554 let (color, label_color) = match self.state { 555 ButtonState::Released(_) => (regular, regular_label), 556 ButtonState::Hovered(_) => (hovered, hovered_label), 557 ButtonState::Down(_) => (pressed, pressed_label), 558 }; 559 if let Some((_, label)) = self.label.as_mut() { 560 label 561 .update_text_color(last_label_color.lerp(*label_color, p)) 562 .expect("Expected widget manager to not be destroyed."); 563 } 564 *last_color = last_color.lerp(*color, p); 565 }; 566 567 self.state = state; 568 } 569 570 pub(super) fn update_cursor_pos( 571 &mut self, 572 cursor_world_position: Vec2, 573 global_transform: Transform, 574 ) { 575 // * If cursor lies within the button 576 if self 577 .bounding_box 578 .test(cursor_world_position, global_transform) 579 { 580 if !self.state.hovered() { 581 self.update_state(self.state.hover()); 582 } 583 } else if self.state.hovered() { 584 self.update_state(self.state.release()); 585 } 586 } 587 } 588 589 impl<T: Loaded + 'static> Button<T> { 590 pub(super) fn update_input(&mut self, event: &InputEvent) { 591 if let InputEvent::MouseInput(let_engine::input::MouseButton::Left, element_state) = event { 592 match element_state { 593 ElementState::Pressed => { 594 if self.state.hovered() { 595 self.update_state(self.state.press()); 596 } 597 } 598 ElementState::Released => { 599 if let ButtonState::Down(_) = self.state { 600 self.handle = true; 601 self.update_state(self.state.hover()); 602 } 603 } 604 } 605 } 606 } 607 608 /// Returns this buttons object ID 609 pub fn object_id(&self) -> ObjectId { 610 self.object_id 611 } 612 613 /// Returns the ID of the label object optionally present within this button. 614 pub fn label_object_id(&self) -> Option<ObjectId> { 615 self.label.as_ref().map(|(id, _)| *id) 616 } 617 618 /// Returns the optional label within this button. 619 pub fn label(&self) -> Option<&Label<T>> { 620 self.label.as_ref().map(|(_, label)| label) 621 } 622 623 /// Returns the optional label within this button mutably. 624 pub fn label_mut(&mut self) -> Option<&mut Label<T>> { 625 self.label.as_mut().map(|(_, label)| label) 626 } 627 628 /// Returns this buttons layer view ID 629 pub fn view_id(&self) -> LayerViewId { 630 self.view_id 631 } 632 633 /// Returns the bounding box of this button. 634 pub fn bounding_box(&self) -> &BoundingBox { 635 &self.bounding_box 636 } 637 638 /// Sets the bounding box of this button. 639 pub fn set_bounding_box(&mut self, bounding_box: BoundingBox) { 640 self.bounding_box = bounding_box; 641 } 642 643 /// Returns the transform of this button. 644 pub fn transform(&self) -> &Transform { 645 &self.transform 646 } 647 648 /// Returns the mutable transform of this button. 649 pub fn transform_mut(&mut self) -> &mut Transform { 650 &mut self.transform 651 } 652 653 /// Sets the transform of this button. 654 pub fn set_transform(&mut self, transform: Transform) { 655 self.transform = transform; 656 } 657 658 /// Returns the current button state. 659 #[inline] 660 pub fn state(&self) -> ButtonState { 661 self.state 662 } 663 664 /// Sets the button to the pressed state. 665 pub fn press(&mut self) { 666 self.update_state(self.state.press()); 667 } 668 669 /// Releases the button in case it was pressed. 670 pub fn release(&mut self) { 671 // This if exists for the chance the button is hovered. 672 if let ButtonState::Down(_) = self.state { 673 self.update_state(self.state.release()); 674 } 675 } 676 677 /// Peeks if there is a new event without handling it. 678 #[inline] 679 pub fn peek_event(&self) -> bool { 680 self.handle 681 } 682 683 /// Handles the button event, returning true if there is a new event. 684 #[inline] 685 pub fn handle(&mut self) -> bool { 686 std::mem::take(&mut self.handle) 687 } 688 } 689 690 /// The current state of the button. 691 #[derive(Clone, Copy, Debug)] 692 pub enum ButtonState { 693 /// The button is not hovered or down. 694 /// The instant represents since when it has been in this state. 695 Released(Instant), 696 697 /// The cursor is currently hovering this button since the provided instant. 698 Hovered(Instant), 699 700 /// The button has been down since the provided instant. 701 Down(Instant), 702 } 703 704 impl Default for ButtonState { 705 fn default() -> Self { 706 Self::Released(Instant::now()) 707 } 708 } 709 710 impl ButtonState { 711 #[inline] 712 pub fn release(self) -> Self { 713 Self::Released(Instant::now()) 714 } 715 716 #[inline] 717 pub fn hover(self) -> Self { 718 Self::Hovered(Instant::now()) 719 } 720 721 #[inline] 722 pub fn press(self) -> Self { 723 Self::Down(Instant::now()) 724 } 725 726 pub fn instant(&self) -> Instant { 727 match self { 728 ButtonState::Released(instant) 729 | ButtonState::Hovered(instant) 730 | ButtonState::Down(instant) => *instant, 731 } 732 } 733 734 /// Returns the given animation completion percentage from the given animation duration 735 pub fn animation_completion(&self, duration: Duration) -> f32 { 736 if duration.is_zero() { 737 1.0 738 } else { 739 let dur = duration.as_secs_f32(); 740 match self { 741 ButtonState::Released(instant) => instant.elapsed().as_secs_f32() / dur, 742 ButtonState::Hovered(instant) => instant.elapsed().as_secs_f32() / dur, 743 ButtonState::Down(_) => 1.0, 744 } 745 } 746 .clamp(0.0, 1.0) 747 } 748 749 /// Returns true if the cursor currently hovers this button. 750 /// This also applies if the button is down. 751 #[inline] 752 pub fn hovered(&self) -> bool { 753 matches!(self, Self::Hovered(_) | Self::Down(_)) 754 } 755 756 /// Returns true if this button is currently down. 757 #[inline] 758 pub fn down(&self) -> bool { 759 matches!(self, Self::Down(_)) 760 } 761 } 762 763 /// The type of the button. 764 #[derive(Clone)] 765 pub enum ButtonVariant<T: Loaded> { 766 /// The button is invisible. 767 Invisible, 768 769 /// The button is a simple quad filled with a solid color. 770 /// 771 /// There is a linear interpolation duration for a smooth transition. 772 /// There is no interpolation step for pressing the button down. 773 /// 774 /// This is the default selection with a default color selection. 775 Solid { 776 interp: Duration, 777 regular: Color, 778 hovered: Color, 779 pressed: Color, 780 regular_label: Color, 781 hovered_label: Color, 782 pressed_label: Color, 783 }, 784 785 /// The button has a simple texture. 786 Textured(ButtonTexture<T>), 787 788 /// The button has a tiled texture that can be resized. 789 Tiled { 790 /// A texture with 9 layers 791 /// 792 /// The layers must be in this order: 793 /// - 0: regular side 794 /// - 1: hovered side 795 /// - 2: pressed side 796 /// - 3: regular corner 797 /// - 4: hovered corner 798 /// - 5: pressed corner 799 /// - 6: regular center 800 /// - 7: hovered center 801 /// - 8: pressed center 802 texture_id: T::TextureId, 803 }, 804 805 /// The button has a completely custom appearance. 806 Custom(Appearance<T>), 807 } 808 809 impl<T: Loaded> Default for ButtonVariant<T> { 810 fn default() -> Self { 811 Self::Solid { 812 interp: Duration::from_millis(200), 813 regular: Color::from_rgba(0.3, 0.3, 0.3, 0.5), 814 hovered: Color::from_rgba(0.7, 0.7, 0.7, 0.9), 815 pressed: Color::from_rgb(0.4, 0.4, 0.4), 816 regular_label: Color::from_rgba(0.8, 0.8, 0.8, 0.7), 817 hovered_label: Color::from_rgb(1.0, 1.0, 1.0), 818 pressed_label: Color::from_rgb(0.5, 0.5, 0.5), 819 } 820 } 821 } 822 823 /// The three variations of the button texture. 824 #[derive(Clone)] 825 pub struct ButtonTexture<T: Loaded> { 826 /// When not hovered/pressed 827 pub regular: TextureSection<T>, 828 829 /// When the cursor hovers over the button 830 pub hovered: TextureSection<T>, 831 832 /// When the cursor hovers over the button and a mouse button is down. 833 pub pressed: TextureSection<T>, 834 } 835 836 /// The bounding box position is affected by the objects scene position, 837 /// but not its appearance position. 838 /// 839 /// By default this is a bounding box quad 840 #[derive(Debug, Clone, Default)] 841 pub enum BoundingBox { 842 /// A simple Quad 843 #[default] 844 Quad, 845 846 /// A circle. 847 Circle, 848 849 /// Centered model composed of 2D triangles 850 Custom(Box<[Vec2]>), 851 } 852 853 impl From<Box<[Vec2]>> for BoundingBox { 854 fn from(value: Box<[Vec2]>) -> Self { 855 Self::Custom(value) 856 } 857 } 858 859 impl From<Vec<Vec2>> for BoundingBox { 860 fn from(value: Vec<Vec2>) -> Self { 861 Self::Custom(value.into_boxed_slice()) 862 } 863 } 864 865 impl From<&[Vec2]> for BoundingBox { 866 fn from(value: &[Vec2]) -> Self { 867 value.to_vec().into() 868 } 869 } 870 871 impl BoundingBox { 872 /// Test if the given 2D world pos point lies within the bounding box. 873 /// 874 /// Model is expected to be a multiple of 3. 875 pub fn test(&self, position: Vec2, bounding_box_transform: Transform) -> bool { 876 // Translate point to bounding box space 877 let local_pos = { 878 let inv = bounding_box_transform.to_matrix().inverse(); 879 let p = inv * position.extend(0.0).extend(1.0); 880 p.truncate().xy() 881 }; 882 883 match self { 884 Self::Quad => { 885 let Vec2 { x, y } = local_pos.abs(); 886 887 x <= 1.0 && y <= 1.0 888 } 889 Self::Circle => local_pos.length_squared() <= 1.0, 890 Self::Custom(model) => { 891 debug_assert!(model.len().is_multiple_of(3)); 892 for tri in model.chunks_exact(3) { 893 let [a, b, c] = tri else { unreachable!() }; 894 895 let s1 = (b - a).perp_dot(local_pos - a); 896 let s2 = (c - b).perp_dot(local_pos - b); 897 let s3 = (a - c).perp_dot(local_pos - c); 898 899 const EPS: f32 = 1e-6; 900 if (s1 >= -EPS && s2 >= -EPS && s3 >= -EPS) 901 || (s1 <= EPS && s2 <= EPS && s3 <= EPS) 902 { 903 return true; 904 } 905 } 906 907 false 908 } 909 } 910 } 911 } 912 913 fn tiled_vertices_indices(size: Vec2, dimensions: Vec2) -> ([TVert; 36], [u32; 54]) { 914 let Vec2 { 915 x: w, // width 916 y: h, // height 917 } = size; 918 let Vec2 { x, y } = dimensions; // texture dimensions 919 let Vec2 { x: u, y: v } = (size - dimensions) / dimensions; // scaled uv 920 921 // y 922 // (-1.0) 3---2-----7---6 923 // | | | | | 924 // |y 0---1 4---5 925 // | | | 926 // | | | 927 // | 11--10 15--14 928 // | | | | | 929 // |1.0 8---9----12--13 930 // v----(-1.0)x---------1.0-x> 931 // 932 ( 933 [ 934 // sides 935 tvert(-w, h - y, -1.0, v), // 11 936 tvert(-w + x, h - y, 1.0, v), // 10 937 tvert(-w + x, -h + y, 1.0, -v), // 1 938 tvert(-w, -h + y, -1.0, -v), // 0 939 tvert(-w + x, -h, -1.0, u), // 2 940 tvert(-w + x, -h + y, 1.0, u), // 1 941 tvert(w - x, -h + y, 1.0, -u), // 4 942 tvert(w - x, -h, -1.0, -u), // 7 943 tvert(w, -h + y, -1.0, v), // 5 944 tvert(w - x, -h + y, 1.0, v), // 4 945 tvert(w - x, h - y, 1.0, -v), // 15 946 tvert(w, h - y, -1.0, -v), // 14 947 tvert(w - x, h, -1.0, u), // 12 948 tvert(w - x, h - y, 1.0, u), // 15 949 tvert(-w + x, h - y, 1.0, -u), // 10 950 tvert(-w + x, h, -1.0, -u), // 9 951 // corners 952 tvert(-w, -h + y, -1.0, 1.0), // 0 953 tvert(-w + x, -h + y, 1.0, 1.0), // 1 954 tvert(-w + x, -h, 1.0, -1.0), // 2 955 tvert(-w, -h, -1.0, -1.0), // 3 956 tvert(w - x, -h + y, 1.0, 1.0), // 4 957 tvert(w, -h + y, 1.0, -1.0), // 5 958 tvert(w, -h, -1.0, -1.0), // 6 959 tvert(w - x, -h, -1.0, 1.0), // 7 960 tvert(-w, h, -1.0, -1.0), // 8 961 tvert(-w + x, h, -1.0, 1.0), // 9 962 tvert(-w + x, h - y, 1.0, 1.0), // 10 963 tvert(-w, h - y, 1.0, -1.0), // 11 964 tvert(w - x, h, 1.0, -1.0), // 12 965 tvert(w, h, -1.0, -1.0), // 13 966 tvert(w, h - y, -1.0, 1.0), // 14 967 tvert(w - x, h - y, 1.0, 1.0), // 15 968 // center 969 tvert(-w + x, h - y, -u, v), // 10 970 tvert(w - x, h - y, u, v), // 15 971 tvert(w - x, -h + y, u, -v), // 4 972 tvert(-w + x, -h + y, -u, -v), // 1 973 ], 974 [ 975 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 976 15, // corners 977 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, 24, 25, 26, 24, 26, 27, 28, 29, 30, 28, 978 30, 31, // sides 979 32, 33, 34, 32, 34, 35, // middle 980 ], 981 ) 982 }