1use bevy::{camera::primitives::Aabb, prelude::*};
4use rand::{Rng, SeedableRng};
5use rand_chacha::ChaCha8Rng;
6use std::f32::consts::PI;
7
8const TRANSITION_DURATION: f32 = 2.0;
9
10fn 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#[derive(Component)]
21struct ShowAxes;
22
23#[derive(Component)]
26struct TransformTracking {
27 initial_transform: Transform,
29
30 target_transform: Transform,
32
33 progress: f32,
35}
36
37#[derive(Resource)]
38struct SeededRng(ChaCha8Rng);
39
40fn setup(
41 mut commands: Commands,
42 mut meshes: ResMut<Assets<Mesh>>,
43 mut materials: ResMut<Assets<StandardMaterial>>,
44) {
45 let mut rng = ChaCha8Rng::seed_from_u64(19878367467713);
48
49 commands.spawn((
51 PointLight {
52 shadows_enabled: true,
53 ..default()
54 },
55 Transform::from_xyz(2., 6., 0.),
56 ));
57
58 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 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 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
97fn 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
106fn 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
129const TRANSLATION_BOUND_LOWER_X: f32 = -5.;
132const TRANSLATION_BOUND_UPPER_X: f32 = 5.;
133const TRANSLATION_BOUND_LOWER_Y: f32 = -1.;
134const TRANSLATION_BOUND_UPPER_Y: f32 = 1.;
135const TRANSLATION_BOUND_LOWER_Z: f32 = -2.;
136const TRANSLATION_BOUND_UPPER_Z: f32 = 6.;
137
138const SCALING_BOUND_LOWER_LOG: f32 = -1.2;
139const SCALING_BOUND_UPPER_LOG: f32 = 1.2;
140
141fn 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
149fn 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
160fn 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
175fn 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
187fn 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
194fn 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
201fn 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
210fn 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}