//! This example demonstrates how you can add your own custom primitives to bevy highlighting //! traits you may want to implement for your primitives to achieve different functionalities. use std::f32::consts::{PI, SQRT_2}; #[cfg(not(target_family = "wasm"))] use bevy::pbr::wireframe::{WireframeConfig, WireframePlugin}; use bevy::{ asset::RenderAssetUsages, camera::ScalingMode, color::palettes::css::{RED, WHITE}, input::common_conditions::{input_just_pressed, input_toggle_active}, math::{ bounding::{ Aabb2d, Bounded2d, Bounded3d, BoundedExtrusion, BoundingCircle, BoundingVolume, }, Isometry2d, }, mesh::{Extrudable, ExtrusionBuilder, PerimeterSegment}, prelude::*, }; const HEART: Heart = Heart::new(0.5); const HOLLOW: Heart = Heart::new(0.3); // By implementing these traits we can construct the 2D ring version of this shape const RING: Ring = Ring::new(HEART, HOLLOW); // By implementing these traits we can construct the 3D extrusion of this shape const EXTRUSION: Extrusion = Extrusion { base_shape: HEART, half_depth: 0.5, }; const RING_EXTRUSION: Extrusion> = Extrusion { base_shape: RING, half_depth: 0.5, }; // The transform of the camera in 2D const TRANSFORM_2D: Transform = Transform { translation: Vec3::ZERO, rotation: Quat::IDENTITY, scale: Vec3::ONE, }; // The projection used for the camera in 2D const PROJECTION_2D: Projection = Projection::Orthographic(OrthographicProjection { near: -1.0, far: 10.0, scale: 1.0, viewport_origin: Vec2::new(0.5, 0.5), scaling_mode: ScalingMode::AutoMax { max_width: 8.0, max_height: 20.0, }, area: Rect { min: Vec2::NEG_ONE, max: Vec2::ONE, }, }); // The transform of the camera in 3D const TRANSFORM_3D: Transform = Transform { translation: Vec3::ZERO, // The camera is pointing at the 3D shape rotation: Quat::from_xyzw(-0.2669336, -0.0, -0.0, 0.96371484), scale: Vec3::ONE, }; // The projection used for the camera in 3D const PROJECTION_3D: Projection = Projection::Perspective(PerspectiveProjection { fov: PI / 4.0, near: 0.1, far: 1000.0, aspect_ratio: 1.0, near_clip_plane: vec4(0.0, 0.0, -1.0, -0.1), }); /// State for tracking the currently displayed shape /// /// Also a component for associating the entity with this state, for toggling visibility #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default, Reflect, Component)] enum ShapeActive { #[default] /// The 2D heart shape is displayed Heart, /// The 2D heart ring shape is displayed Ring, /// The 3D extruded heart shape is displayed Extrusion, /// The 3D extruded heart ring shape is displayed RingExtrusion, } impl ShapeActive { const SHAPES: [ShapeActive; 4] = [ ShapeActive::Heart, ShapeActive::Ring, ShapeActive::Extrusion, ShapeActive::RingExtrusion, ]; fn next_shape(self) -> Self { Self::SHAPES .into_iter() .cycle() .skip_while(|shape| *shape != self) .nth(1) // move to the next element .unwrap() } } /// State for tracking the currently displayed shape #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default, Reflect)] enum BoundingShape { #[default] /// No bounding shapes None, /// The bounding sphere or circle of the shape BoundingSphere, /// The Axis Aligned Bounding Box (AABB) of the shape BoundingBox, } /// A marker component for our 2D shapes so we can query them separately from the camera #[derive(Component)] struct Shape2d; /// A marker component for our 3D shapes so we can query them separately from the camera #[derive(Component)] struct Shape3d; fn main() { let mut app = App::new(); app.add_plugins(DefaultPlugins); #[cfg(not(target_family = "wasm"))] app.add_plugins(WireframePlugin::default()); app.init_state::() .init_state::() .add_systems(Startup, setup) .add_systems( Update, ( ( rotate_2d_shapes.run_if(input_toggle_active(true, KeyCode::KeyR)), bounding_shapes_2d, ) .run_if(state_in_one_of([ShapeActive::Heart, ShapeActive::Ring])), ( rotate_3d_shapes.run_if(input_toggle_active(true, KeyCode::KeyR)), bounding_shapes_3d, ) .run_if(state_in_one_of([ ShapeActive::Extrusion, ShapeActive::RingExtrusion, ])), update_bounding_shape.run_if(input_just_pressed(KeyCode::KeyB)), switch_shapes.run_if(input_just_pressed(KeyCode::Tab)), ), ); #[cfg(not(target_family = "wasm"))] app.add_systems( Update, toggle_wireframes.run_if(input_just_pressed(KeyCode::Space)), ); app.run(); } fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { // Spawn the camera commands.spawn((Camera3d::default(), TRANSFORM_2D, PROJECTION_2D)); // Spawn the 2D heart commands.spawn(( // We can use the methods defined on the `MeshBuilder` to customize the mesh. Mesh3d(meshes.add(HEART.mesh().resolution(50))), MeshMaterial3d(materials.add(StandardMaterial { emissive: RED.into(), base_color: RED.into(), ..Default::default() })), Transform::from_xyz(0.0, 0.0, 0.0), Shape2d, Visibility::Visible, ShapeActive::Heart, )); // Spawn the 2D heart ring commands.spawn(( // We can use the methods defined on the `MeshBuilder` to customize the mesh. Mesh3d(meshes.add(RING.mesh().with_inner(|heart| heart.resolution(50)))), MeshMaterial3d(materials.add(StandardMaterial { emissive: RED.into(), base_color: RED.into(), ..Default::default() })), Transform::from_xyz(0.0, 0.0, 0.0), Shape2d, Visibility::Hidden, ShapeActive::Ring, )); // Spawn an extrusion of the heart commands.spawn(( // We can set a custom resolution for the round parts of the extrusion as well. Mesh3d(meshes.add(EXTRUSION.mesh().resolution(50))), MeshMaterial3d(materials.add(StandardMaterial { base_color: RED.into(), ..Default::default() })), Transform::from_xyz(0., -3., -5.).with_rotation(Quat::from_rotation_x(-PI / 4.)), Shape3d, Visibility::Hidden, ShapeActive::Extrusion, )); // Spawn an extrusion of the heart ring commands.spawn(( // We can set a custom resolution for the round parts of the extrusion as well. Mesh3d( meshes.add( RING_EXTRUSION .mesh() .with_inner(|ring| ring.with_inner(|heart| heart.resolution(50))), ), ), MeshMaterial3d(materials.add(StandardMaterial { base_color: RED.into(), ..Default::default() })), Transform::from_xyz(0., -3., -5.).with_rotation(Quat::from_rotation_x(-PI / 4.)), Shape3d, Visibility::Hidden, ShapeActive::RingExtrusion, )); // Point light for 3D commands.spawn(( PointLight { shadow_maps_enabled: true, intensity: 10_000_000., range: 100.0, shadow_depth_bias: 0.2, ..default() }, Transform::from_xyz(8.0, 12.0, 1.0), )); let mut text = "\ Press 'B' to cycle between no bounding shapes, bounding boxes (AABBs) and bounding spheres / circles\n\ Press 'Tab' to cycle between 2D and 3D shapes\n\ Press 'R' to pause/resume rotation".to_string(); #[cfg(not(target_family = "wasm"))] text.push_str("\nPress 'Space' to toggle display of wireframes"); // Example instructions commands.spawn(( Text::new(text), Node { position_type: PositionType::Absolute, top: px(12), left: px(12), ..default() }, )); } // Rotate the 2D shapes. fn rotate_2d_shapes(mut shapes: Query<&mut Transform, With>, time: Res