mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Example for axes gizmos (#12299)
# Objective - Follow-up to #12211 - Introduces an example project that demonstrates the implementation and behavior of `Gizmos::axes` for an entity with a `Transform` component. ## Solution In order to demonstrate how `Gizmo::axes` can be used and behaves in practice, we introduce an example of a simple scene containing a pair of cuboids locked in a grotesque, inscrutable dance: the two are repeatedly given random `Transform`s which they interpolate to, showing how the axes move with objects as they translate, rotate, and scale. <img width="1023" alt="Screenshot 2024-03-04 at 1 16 33 PM" src="https://github.com/bevyengine/bevy/assets/2975848/c1ff4794-6722-491c-8522-f59801645139"> On the implementation side, we demonstrate how to draw axes for entities, automatically sizing them according to their bounding boxes (so that the axes will be visible): ````rust fn draw_axes(mut gizmos: Gizmos, query: Query<(&Transform, &Aabb), With<ShowAxes>>) { for (&transform, &aabb) in &query { let length = aabb.half_extents.length(); gizmos.axes(transform, length); } } ```` --- ## Changelog - Created examples/gizmos/axes.rs. - Added 'axes' example to Cargo.toml.
This commit is contained in:
parent
b02a2eff17
commit
4673fb3e57
3 changed files with 223 additions and 0 deletions
11
Cargo.toml
11
Cargo.toml
|
@ -2690,6 +2690,17 @@ description = "A scene showcasing 3D gizmos"
|
|||
category = "Gizmos"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "axes"
|
||||
path = "examples/gizmos/axes.rs"
|
||||
doc-scrape-examples = true
|
||||
|
||||
[package.metadata.example.axes]
|
||||
name = "Axes"
|
||||
description = "Demonstrates the function of axes gizmos"
|
||||
category = "Gizmos"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "light_gizmos"
|
||||
path = "examples/gizmos/light_gizmos.rs"
|
||||
|
|
|
@ -267,6 +267,7 @@ Example | Description
|
|||
--- | ---
|
||||
[2D Gizmos](../examples/gizmos/2d_gizmos.rs) | A scene showcasing 2D gizmos
|
||||
[3D Gizmos](../examples/gizmos/3d_gizmos.rs) | A scene showcasing 3D gizmos
|
||||
[Axes](../examples/gizmos/axes.rs) | Demonstrates the function of axes gizmos
|
||||
[Light Gizmos](../examples/gizmos/light_gizmos.rs) | A scene showcasing light gizmos
|
||||
|
||||
## Input
|
||||
|
|
211
examples/gizmos/axes.rs
Normal file
211
examples/gizmos/axes.rs
Normal file
|
@ -0,0 +1,211 @@
|
|||
//! This example demonstrates the implementation and behavior of the axes gizmo.
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::primitives::Aabb;
|
||||
use rand::random;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, (move_cubes, draw_axes))
|
||||
.run();
|
||||
}
|
||||
|
||||
/// The `ShowAxes` component is attached to an entity to get the `draw_axes` system to
|
||||
/// display axes according to its Transform component.
|
||||
#[derive(Component)]
|
||||
struct ShowAxes;
|
||||
|
||||
/// The `TransformTracking` component keeps track of the data we need to interpolate
|
||||
/// between two transforms in our example.
|
||||
#[derive(Component)]
|
||||
struct TransformTracking {
|
||||
/// The initial transform of the cube during the move
|
||||
initial_transform: Transform,
|
||||
|
||||
/// The target transform of the cube during the move
|
||||
target_transform: Transform,
|
||||
|
||||
/// The progress of the cube during the move in percentage points
|
||||
progress: u16,
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
// Lights...
|
||||
commands.spawn(PointLightBundle {
|
||||
point_light: PointLight {
|
||||
shadows_enabled: true,
|
||||
..default()
|
||||
},
|
||||
transform: Transform::from_xyz(2., 6., 0.),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Camera...
|
||||
commands.spawn(Camera3dBundle {
|
||||
transform: Transform::from_xyz(0., 1.5, -8.).looking_at(Vec3::new(0., -0.5, 0.), Vec3::Y),
|
||||
..default()
|
||||
});
|
||||
|
||||
// Action! (Our cubes that are going to move)
|
||||
commands.spawn((
|
||||
PbrBundle {
|
||||
mesh: meshes.add(Cuboid::new(1., 1., 1.)),
|
||||
material: materials.add(Color::srgb(0.8, 0.7, 0.6)),
|
||||
..default()
|
||||
},
|
||||
ShowAxes,
|
||||
TransformTracking {
|
||||
initial_transform: default(),
|
||||
target_transform: random_transform(),
|
||||
progress: 0,
|
||||
},
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
PbrBundle {
|
||||
mesh: meshes.add(Cuboid::new(0.5, 0.5, 0.5)),
|
||||
material: materials.add(Color::srgb(0.6, 0.7, 0.8)),
|
||||
..default()
|
||||
},
|
||||
ShowAxes,
|
||||
TransformTracking {
|
||||
initial_transform: default(),
|
||||
target_transform: random_transform(),
|
||||
progress: 0,
|
||||
},
|
||||
));
|
||||
|
||||
// A plane to give a sense of place
|
||||
commands.spawn(PbrBundle {
|
||||
mesh: meshes.add(Plane3d::default().mesh().size(20., 20.)),
|
||||
material: materials.add(Color::srgb(0.1, 0.1, 0.1)),
|
||||
transform: Transform::from_xyz(0., -2., 0.),
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
// This system draws the axes based on the cube's transform, with length based on the size of
|
||||
// the entity's axis-aligned bounding box (AABB).
|
||||
fn draw_axes(mut gizmos: Gizmos, query: Query<(&Transform, &Aabb), With<ShowAxes>>) {
|
||||
for (&transform, &aabb) in &query {
|
||||
let length = aabb.half_extents.length();
|
||||
gizmos.axes(transform, length);
|
||||
}
|
||||
}
|
||||
|
||||
// This system changes the cubes' transforms to interpolate between random transforms
|
||||
fn move_cubes(mut query: Query<(&mut Transform, &mut TransformTracking)>) {
|
||||
for (mut transform, mut tracking) in &mut query {
|
||||
let t = tracking.progress as f32 / 100.;
|
||||
|
||||
*transform =
|
||||
interpolate_transforms(tracking.initial_transform, tracking.target_transform, t);
|
||||
|
||||
if tracking.progress < 100 {
|
||||
tracking.progress += 1;
|
||||
} else {
|
||||
tracking.initial_transform = *transform;
|
||||
tracking.target_transform = random_transform();
|
||||
tracking.progress = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions for random transforms and interpolation:
|
||||
|
||||
const TRANSLATION_BOUND_LOWER_X: f32 = -5.;
|
||||
const TRANSLATION_BOUND_UPPER_X: f32 = 5.;
|
||||
const TRANSLATION_BOUND_LOWER_Y: f32 = -1.;
|
||||
const TRANSLATION_BOUND_UPPER_Y: f32 = 1.;
|
||||
const TRANSLATION_BOUND_LOWER_Z: f32 = -2.;
|
||||
const TRANSLATION_BOUND_UPPER_Z: f32 = 6.;
|
||||
|
||||
const SCALING_BOUND_LOWER_LOG: f32 = -1.2;
|
||||
const SCALING_BOUND_UPPER_LOG: f32 = 1.2;
|
||||
|
||||
fn random_transform() -> Transform {
|
||||
Transform {
|
||||
translation: random_translation(),
|
||||
rotation: random_rotation(),
|
||||
scale: random_scale(),
|
||||
}
|
||||
}
|
||||
|
||||
fn random_translation() -> Vec3 {
|
||||
let x = random::<f32>() * (TRANSLATION_BOUND_UPPER_X - TRANSLATION_BOUND_LOWER_X)
|
||||
+ TRANSLATION_BOUND_LOWER_X;
|
||||
let y = random::<f32>() * (TRANSLATION_BOUND_UPPER_Y - TRANSLATION_BOUND_LOWER_Y)
|
||||
+ TRANSLATION_BOUND_LOWER_Y;
|
||||
let z = random::<f32>() * (TRANSLATION_BOUND_UPPER_Z - TRANSLATION_BOUND_LOWER_Z)
|
||||
+ TRANSLATION_BOUND_LOWER_Z;
|
||||
|
||||
Vec3::new(x, y, z)
|
||||
}
|
||||
|
||||
fn random_scale() -> Vec3 {
|
||||
let x_factor_log = random::<f32>() * (SCALING_BOUND_UPPER_LOG - SCALING_BOUND_LOWER_LOG)
|
||||
+ SCALING_BOUND_LOWER_LOG;
|
||||
let y_factor_log = random::<f32>() * (SCALING_BOUND_UPPER_LOG - SCALING_BOUND_LOWER_LOG)
|
||||
+ SCALING_BOUND_LOWER_LOG;
|
||||
let z_factor_log = random::<f32>() * (SCALING_BOUND_UPPER_LOG - SCALING_BOUND_LOWER_LOG)
|
||||
+ SCALING_BOUND_LOWER_LOG;
|
||||
|
||||
Vec3::new(
|
||||
x_factor_log.exp2(),
|
||||
y_factor_log.exp2(),
|
||||
z_factor_log.exp2(),
|
||||
)
|
||||
}
|
||||
|
||||
fn elerp(v1: Vec3, v2: Vec3, t: f32) -> Vec3 {
|
||||
let x_factor_log = (1. - t) * v1.x.log2() + t * v2.x.log2();
|
||||
let y_factor_log = (1. - t) * v1.y.log2() + t * v2.y.log2();
|
||||
let z_factor_log = (1. - t) * v1.z.log2() + t * v2.z.log2();
|
||||
|
||||
Vec3::new(
|
||||
x_factor_log.exp2(),
|
||||
y_factor_log.exp2(),
|
||||
z_factor_log.exp2(),
|
||||
)
|
||||
}
|
||||
|
||||
fn random_rotation() -> Quat {
|
||||
let dir = random_direction();
|
||||
let angle = random::<f32>() * 2. * PI;
|
||||
|
||||
Quat::from_axis_angle(dir, angle)
|
||||
}
|
||||
|
||||
fn random_direction() -> Vec3 {
|
||||
let height = random::<f32>() * 2. - 1.;
|
||||
let theta = random::<f32>() * 2. * PI;
|
||||
|
||||
build_direction(height, theta)
|
||||
}
|
||||
|
||||
fn build_direction(height: f32, theta: f32) -> Vec3 {
|
||||
let z = height;
|
||||
let m = f32::acos(z).sin();
|
||||
let x = theta.cos() * m;
|
||||
let y = theta.sin() * m;
|
||||
|
||||
Vec3::new(x, y, z)
|
||||
}
|
||||
|
||||
fn interpolate_transforms(t1: Transform, t2: Transform, t: f32) -> Transform {
|
||||
let translation = t1.translation.lerp(t2.translation, t);
|
||||
let rotation = t1.rotation.slerp(t2.rotation, t);
|
||||
let scale = elerp(t1.scale, t2.scale, t);
|
||||
|
||||
Transform {
|
||||
translation,
|
||||
rotation,
|
||||
scale,
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue