Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/gizmos/axes.rs
6592 views
1
//! This example demonstrates the implementation and behavior of the axes gizmo.
2
3
use bevy::{camera::primitives::Aabb, prelude::*};
4
use rand::{Rng, SeedableRng};
5
use rand_chacha::ChaCha8Rng;
6
use std::f32::consts::PI;
7
8
const TRANSITION_DURATION: f32 = 2.0;
9
10
fn main() {
11
App::new()
12
.add_plugins(DefaultPlugins)
13
.add_systems(Startup, setup)
14
.add_systems(Update, (move_cubes, draw_axes).chain())
15
.run();
16
}
17
18
/// The `ShowAxes` component is attached to an entity to get the `draw_axes` system to
19
/// display axes according to its Transform component.
20
#[derive(Component)]
21
struct ShowAxes;
22
23
/// The `TransformTracking` component keeps track of the data we need to interpolate
24
/// between two transforms in our example.
25
#[derive(Component)]
26
struct TransformTracking {
27
/// The initial transform of the cube during the move
28
initial_transform: Transform,
29
30
/// The target transform of the cube during the move
31
target_transform: Transform,
32
33
/// The progress of the cube during the move in seconds
34
progress: f32,
35
}
36
37
#[derive(Resource)]
38
struct SeededRng(ChaCha8Rng);
39
40
fn setup(
41
mut commands: Commands,
42
mut meshes: ResMut<Assets<Mesh>>,
43
mut materials: ResMut<Assets<StandardMaterial>>,
44
) {
45
// We're seeding the PRNG here to make this example deterministic for testing purposes.
46
// This isn't strictly required in practical use unless you need your app to be deterministic.
47
let mut rng = ChaCha8Rng::seed_from_u64(19878367467713);
48
49
// Lights...
50
commands.spawn((
51
PointLight {
52
shadows_enabled: true,
53
..default()
54
},
55
Transform::from_xyz(2., 6., 0.),
56
));
57
58
// Camera...
59
commands.spawn((
60
Camera3d::default(),
61
Transform::from_xyz(0., 1.5, -8.).looking_at(Vec3::new(0., -0.5, 0.), Vec3::Y),
62
));
63
64
// Action! (Our cubes that are going to move)
65
commands.spawn((
66
Mesh3d(meshes.add(Cuboid::new(1., 1., 1.))),
67
MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))),
68
ShowAxes,
69
TransformTracking {
70
initial_transform: default(),
71
target_transform: random_transform(&mut rng),
72
progress: 0.0,
73
},
74
));
75
76
commands.spawn((
77
Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))),
78
MeshMaterial3d(materials.add(Color::srgb(0.6, 0.7, 0.8))),
79
ShowAxes,
80
TransformTracking {
81
initial_transform: default(),
82
target_transform: random_transform(&mut rng),
83
progress: 0.0,
84
},
85
));
86
87
// A plane to give a sense of place
88
commands.spawn((
89
Mesh3d(meshes.add(Plane3d::default().mesh().size(20., 20.))),
90
MeshMaterial3d(materials.add(Color::srgb(0.1, 0.1, 0.1))),
91
Transform::from_xyz(0., -2., 0.),
92
));
93
94
commands.insert_resource(SeededRng(rng));
95
}
96
97
// This system draws the axes based on the cube's transform, with length based on the size of
98
// the entity's axis-aligned bounding box (AABB).
99
fn draw_axes(mut gizmos: Gizmos, query: Query<(&Transform, &Aabb), With<ShowAxes>>) {
100
for (&transform, &aabb) in &query {
101
let length = aabb.half_extents.length();
102
gizmos.axes(transform, length);
103
}
104
}
105
106
// This system changes the cubes' transforms to interpolate between random transforms
107
fn move_cubes(
108
mut query: Query<(&mut Transform, &mut TransformTracking)>,
109
time: Res<Time>,
110
mut rng: ResMut<SeededRng>,
111
) {
112
for (mut transform, mut tracking) in &mut query {
113
*transform = interpolate_transforms(
114
tracking.initial_transform,
115
tracking.target_transform,
116
tracking.progress / TRANSITION_DURATION,
117
);
118
119
if tracking.progress < TRANSITION_DURATION {
120
tracking.progress += time.delta_secs();
121
} else {
122
tracking.initial_transform = *transform;
123
tracking.target_transform = random_transform(&mut rng.0);
124
tracking.progress = 0.0;
125
}
126
}
127
}
128
129
// Helper functions for random transforms and interpolation:
130
131
const TRANSLATION_BOUND_LOWER_X: f32 = -5.;
132
const TRANSLATION_BOUND_UPPER_X: f32 = 5.;
133
const TRANSLATION_BOUND_LOWER_Y: f32 = -1.;
134
const TRANSLATION_BOUND_UPPER_Y: f32 = 1.;
135
const TRANSLATION_BOUND_LOWER_Z: f32 = -2.;
136
const TRANSLATION_BOUND_UPPER_Z: f32 = 6.;
137
138
const SCALING_BOUND_LOWER_LOG: f32 = -1.2;
139
const SCALING_BOUND_UPPER_LOG: f32 = 1.2;
140
141
fn random_transform(rng: &mut impl Rng) -> Transform {
142
Transform {
143
translation: random_translation(rng),
144
rotation: random_rotation(rng),
145
scale: random_scale(rng),
146
}
147
}
148
149
fn random_translation(rng: &mut impl Rng) -> Vec3 {
150
let x = rng.random::<f32>() * (TRANSLATION_BOUND_UPPER_X - TRANSLATION_BOUND_LOWER_X)
151
+ TRANSLATION_BOUND_LOWER_X;
152
let y = rng.random::<f32>() * (TRANSLATION_BOUND_UPPER_Y - TRANSLATION_BOUND_LOWER_Y)
153
+ TRANSLATION_BOUND_LOWER_Y;
154
let z = rng.random::<f32>() * (TRANSLATION_BOUND_UPPER_Z - TRANSLATION_BOUND_LOWER_Z)
155
+ TRANSLATION_BOUND_LOWER_Z;
156
157
Vec3::new(x, y, z)
158
}
159
160
fn random_scale(rng: &mut impl Rng) -> Vec3 {
161
let x_factor_log = rng.random::<f32>() * (SCALING_BOUND_UPPER_LOG - SCALING_BOUND_LOWER_LOG)
162
+ SCALING_BOUND_LOWER_LOG;
163
let y_factor_log = rng.random::<f32>() * (SCALING_BOUND_UPPER_LOG - SCALING_BOUND_LOWER_LOG)
164
+ SCALING_BOUND_LOWER_LOG;
165
let z_factor_log = rng.random::<f32>() * (SCALING_BOUND_UPPER_LOG - SCALING_BOUND_LOWER_LOG)
166
+ SCALING_BOUND_LOWER_LOG;
167
168
Vec3::new(
169
ops::exp2(x_factor_log),
170
ops::exp2(y_factor_log),
171
ops::exp2(z_factor_log),
172
)
173
}
174
175
fn elerp(v1: Vec3, v2: Vec3, t: f32) -> Vec3 {
176
let x_factor_log = (1. - t) * ops::log2(v1.x) + t * ops::log2(v2.x);
177
let y_factor_log = (1. - t) * ops::log2(v1.y) + t * ops::log2(v2.y);
178
let z_factor_log = (1. - t) * ops::log2(v1.z) + t * ops::log2(v2.z);
179
180
Vec3::new(
181
ops::exp2(x_factor_log),
182
ops::exp2(y_factor_log),
183
ops::exp2(z_factor_log),
184
)
185
}
186
187
fn random_rotation(rng: &mut impl Rng) -> Quat {
188
let dir = random_direction(rng);
189
let angle = rng.random::<f32>() * 2. * PI;
190
191
Quat::from_axis_angle(dir, angle)
192
}
193
194
fn random_direction(rng: &mut impl Rng) -> Vec3 {
195
let height = rng.random::<f32>() * 2. - 1.;
196
let theta = rng.random::<f32>() * 2. * PI;
197
198
build_direction(height, theta)
199
}
200
201
fn build_direction(height: f32, theta: f32) -> Vec3 {
202
let z = height;
203
let m = ops::sin(ops::acos(z));
204
let x = ops::cos(theta) * m;
205
let y = ops::sin(theta) * m;
206
207
Vec3::new(x, y, z)
208
}
209
210
fn interpolate_transforms(t1: Transform, t2: Transform, t: f32) -> Transform {
211
let translation = t1.translation.lerp(t2.translation, t);
212
let rotation = t1.rotation.slerp(t2.rotation, t);
213
let scale = elerp(t1.scale, t2.scale, t);
214
215
Transform {
216
translation,
217
rotation,
218
scale,
219
}
220
}
221
222