| 1 |
|
| 2 |
|
| 3 |
use bevy::{ |
| 4 |
camera_controller::free_camera::{FreeCamera, FreeCameraPlugin}, |
| 5 |
color::palettes::css::*, |
| 6 |
prelude::*, |
| 7 |
}; |
| 8 |
use std::f32::consts::PI; |
| 9 |
|
| 10 |
fn main() { |
| 11 |
App::new() |
| 12 |
.add_plugins((DefaultPlugins, FreeCameraPlugin)) |
| 13 |
.init_gizmo_group::<MyRoundGizmos>() |
| 14 |
.add_systems(Startup, setup) |
| 15 |
.add_systems( |
| 16 |
Update, |
| 17 |
( |
| 18 |
draw_example_collection, |
| 19 |
update_config, |
| 20 |
drive_gizmos_animation, |
| 21 |
), |
| 22 |
) |
| 23 |
.run(); |
| 24 |
} |
| 25 |
|
| 26 |
|
| 27 |
#[derive(Default, Reflect, GizmoConfigGroup)] |
| 28 |
struct MyRoundGizmos; |
| 29 |
|
| 30 |
fn setup( |
| 31 |
mut commands: Commands, |
| 32 |
mut gizmo_assets: ResMut<Assets<GizmoAsset>>, |
| 33 |
mut meshes: ResMut<Assets<Mesh>>, |
| 34 |
mut materials: ResMut<Assets<StandardMaterial>>, |
| 35 |
) { |
| 36 |
let mut gizmo = GizmoAsset::new(); |
| 37 |
|
| 38 |
|
| 39 |
|
| 40 |
|
| 41 |
|
| 42 |
|
| 43 |
gizmo |
| 44 |
.sphere(Isometry3d::IDENTITY, 0.5, CRIMSON) |
| 45 |
.resolution(30_000 / 3); |
| 46 |
|
| 47 |
commands.spawn(( |
| 48 |
Gizmo { |
| 49 |
handle: gizmo_assets.add(gizmo), |
| 50 |
line_config: GizmoLineConfig { |
| 51 |
width: 5., |
| 52 |
..default() |
| 53 |
}, |
| 54 |
..default() |
| 55 |
}, |
| 56 |
Transform::from_xyz(4., 1., 0.), |
| 57 |
)); |
| 58 |
|
| 59 |
commands.spawn(( |
| 60 |
Camera3d::default(), |
| 61 |
Transform::from_xyz(0., 1.5, 6.).looking_at(Vec3::ZERO, Vec3::Y), |
| 62 |
FreeCamera::default(), |
| 63 |
)); |
| 64 |
|
| 65 |
commands.spawn(( |
| 66 |
Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))), |
| 67 |
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), |
| 68 |
)); |
| 69 |
|
| 70 |
commands.spawn(( |
| 71 |
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), |
| 72 |
MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))), |
| 73 |
Transform::from_xyz(0.0, 0.5, 0.0), |
| 74 |
)); |
| 75 |
|
| 76 |
commands.spawn(( |
| 77 |
PointLight { |
| 78 |
shadow_maps_enabled: true, |
| 79 |
..default() |
| 80 |
}, |
| 81 |
Transform::from_xyz(4.0, 8.0, 4.0), |
| 82 |
)); |
| 83 |
|
| 84 |
|
| 85 |
commands.spawn(( |
| 86 |
Text::new( |
| 87 |
"Press 'T' to toggle drawing gizmos on top of everything else in the scene\n\ |
| 88 |
Press 'P' to toggle perspective for line gizmos\n\ |
| 89 |
Hold 'Left' or 'Right' to change the line width of straight gizmos\n\ |
| 90 |
Hold 'Up' or 'Down' to change the line width of round gizmos\n\ |
| 91 |
Press '1' or '2' to toggle the visibility of straight gizmos or round gizmos\n\ |
| 92 |
Press 'B' to show all AABB boxes\n\ |
| 93 |
Press 'U' or 'I' to cycle through line styles for straight or round gizmos\n\ |
| 94 |
Press 'J' or 'K' to cycle through line joins for straight or round gizmos\n\ |
| 95 |
Press 'L' to cycle through gizmos animations (only for dotted/dashed round gizmos)\n\ |
| 96 |
Press 'Spacebar' to toggle pause", |
| 97 |
), |
| 98 |
Node { |
| 99 |
position_type: PositionType::Absolute, |
| 100 |
top: px(12), |
| 101 |
left: px(12), |
| 102 |
..default() |
| 103 |
}, |
| 104 |
)); |
| 105 |
} |
| 106 |
|
| 107 |
fn draw_example_collection( |
| 108 |
mut gizmos: Gizmos, |
| 109 |
mut my_gizmos: Gizmos<MyRoundGizmos>, |
| 110 |
time: Res<Time>, |
| 111 |
) { |
| 112 |
gizmos.grid( |
| 113 |
Quat::from_rotation_x(PI / 2.), |
| 114 |
UVec2::splat(20), |
| 115 |
Vec2::new(2., 2.), |
| 116 |
|
| 117 |
LinearRgba::gray(0.65), |
| 118 |
); |
| 119 |
gizmos.grid( |
| 120 |
Isometry3d::new(Vec3::splat(10.0), Quat::from_rotation_x(PI / 3. * 2.)), |
| 121 |
UVec2::splat(20), |
| 122 |
Vec2::new(2., 2.), |
| 123 |
PURPLE, |
| 124 |
); |
| 125 |
gizmos.sphere(Vec3::splat(10.0), 1.0, PURPLE); |
| 126 |
|
| 127 |
gizmos |
| 128 |
.primitive_3d( |
| 129 |
&Plane3d { |
| 130 |
normal: Dir3::Y, |
| 131 |
half_size: Vec2::splat(1.0), |
| 132 |
}, |
| 133 |
Isometry3d::new( |
| 134 |
Vec3::splat(4.0) + Vec2::from(ops::sin_cos(time.elapsed_secs())).extend(0.0), |
| 135 |
Quat::from_rotation_x(PI / 2. + time.elapsed_secs()), |
| 136 |
), |
| 137 |
GREEN, |
| 138 |
) |
| 139 |
.cell_count(UVec2::new(5, 10)) |
| 140 |
.spacing(Vec2::new(0.2, 0.1)); |
| 141 |
|
| 142 |
gizmos.cube( |
| 143 |
Transform::from_translation(Vec3::Y * 0.5).with_scale(Vec3::splat(1.25)), |
| 144 |
BLACK, |
| 145 |
); |
| 146 |
gizmos.rect( |
| 147 |
Isometry3d::new( |
| 148 |
Vec3::new(ops::cos(time.elapsed_secs()) * 2.5, 1., 0.), |
| 149 |
Quat::from_rotation_y(PI / 2.), |
| 150 |
), |
| 151 |
Vec2::splat(2.), |
| 152 |
LIME, |
| 153 |
); |
| 154 |
|
| 155 |
gizmos.cross(Vec3::new(-1., 1., 1.), 0.5, FUCHSIA); |
| 156 |
|
| 157 |
let domain = Interval::EVERYWHERE; |
| 158 |
let curve = FunctionCurve::new(domain, |t| { |
| 159 |
(Vec2::from(ops::sin_cos(t * 10.0))).extend(t - 6.0) |
| 160 |
}); |
| 161 |
let resolution = ((ops::sin(time.elapsed_secs()) + 1.0) * 100.0) as usize; |
| 162 |
let times_and_colors = (0..=resolution) |
| 163 |
.map(|n| n as f32 / resolution as f32) |
| 164 |
.map(|t| t * 5.0) |
| 165 |
.map(|t| (t, TEAL.mix(&HOT_PINK, t / 5.0))); |
| 166 |
gizmos.curve_gradient_3d(curve, times_and_colors); |
| 167 |
|
| 168 |
gizmos.primitive_3d( |
| 169 |
&Capsule3d::new(0.35, 0.5), |
| 170 |
Isometry3d::new( |
| 171 |
Vec3::new(-1.75, 0.75, 0.75), |
| 172 |
Quat::from_rotation_y(ops::cos(time.elapsed_secs() / 2.0) * 10.0) |
| 173 |
* Quat::from_rotation_z(PI / 3.0), |
| 174 |
), |
| 175 |
YELLOW_GREEN, |
| 176 |
); |
| 177 |
|
| 178 |
my_gizmos.sphere(Vec3::new(1., 0.5, 0.), 0.5, RED); |
| 179 |
|
| 180 |
my_gizmos |
| 181 |
.rounded_cuboid(Vec3::new(-2.0, 0.75, -0.75), Vec3::splat(0.9), TURQUOISE) |
| 182 |
.edge_radius(0.1) |
| 183 |
.arc_resolution(4); |
| 184 |
|
| 185 |
for y in [0., 0.5, 1.] { |
| 186 |
gizmos.ray( |
| 187 |
Vec3::new(1., y, 0.), |
| 188 |
Vec3::new(-3., ops::sin(time.elapsed_secs() * 3.), 0.), |
| 189 |
BLUE, |
| 190 |
); |
| 191 |
} |
| 192 |
|
| 193 |
my_gizmos |
| 194 |
.arc_3d( |
| 195 |
180.0_f32.to_radians(), |
| 196 |
0.2, |
| 197 |
Isometry3d::new( |
| 198 |
Vec3::ONE, |
| 199 |
Quat::from_rotation_arc(Vec3::Y, Vec3::ONE.normalize()), |
| 200 |
), |
| 201 |
ORANGE, |
| 202 |
) |
| 203 |
.resolution(10); |
| 204 |
|
| 205 |
|
| 206 |
my_gizmos.circle(Quat::from_rotation_arc(Vec3::Z, Vec3::Y), 3., BLACK); |
| 207 |
|
| 208 |
|
| 209 |
my_gizmos |
| 210 |
.circle(Quat::from_rotation_arc(Vec3::Z, Vec3::Y), 3.1, NAVY) |
| 211 |
.resolution(64); |
| 212 |
my_gizmos |
| 213 |
.sphere(Isometry3d::IDENTITY, 3.2, BLACK) |
| 214 |
.resolution(64); |
| 215 |
|
| 216 |
gizmos.arrow(Vec3::ZERO, Vec3::splat(1.5), YELLOW); |
| 217 |
|
| 218 |
|
| 219 |
gizmos |
| 220 |
.arrow(Vec3::new(2., 0., 2.), Vec3::new(2., 2., 2.), ORANGE_RED) |
| 221 |
.with_double_end() |
| 222 |
.with_tip_length(0.5); |
| 223 |
|
| 224 |
let from = Vec3::new(1.0, 2.0, 3.0); |
| 225 |
let to = Vec3::new(3.0, 2.5, 4.0); |
| 226 |
gizmos.rect(from, Vec2::ONE, RED); |
| 227 |
my_gizmos.short_arc_3d_between((from + to) / 2.0, from, to, YELLOW_GREEN); |
| 228 |
gizmos.rect(to, Vec2::ONE, RED); |
| 229 |
} |
| 230 |
|
| 231 |
fn update_config( |
| 232 |
mut config_store: ResMut<GizmoConfigStore>, |
| 233 |
keyboard: Res<ButtonInput<KeyCode>>, |
| 234 |
real_time: Res<Time<Real>>, |
| 235 |
mut virtual_time: ResMut<Time<Virtual>>, |
| 236 |
) { |
| 237 |
if keyboard.just_pressed(KeyCode::KeyT) { |
| 238 |
for (_, config, _) in config_store.iter_mut() { |
| 239 |
config.depth_bias = if config.depth_bias == 0. { -1. } else { 0. }; |
| 240 |
} |
| 241 |
} |
| 242 |
if keyboard.just_pressed(KeyCode::KeyP) { |
| 243 |
for (_, config, _) in config_store.iter_mut() { |
| 244 |
|
| 245 |
config.line.perspective ^= true; |
| 246 |
|
| 247 |
config.line.width *= if config.line.perspective { 5. } else { 1. / 5. }; |
| 248 |
} |
| 249 |
} |
| 250 |
|
| 251 |
let (config, _) = config_store.config_mut::<DefaultGizmoConfigGroup>(); |
| 252 |
if keyboard.pressed(KeyCode::ArrowRight) { |
| 253 |
config.line.width += 5. * real_time.delta_secs(); |
| 254 |
config.line.width = config.line.width.clamp(0., 50.); |
| 255 |
} |
| 256 |
if keyboard.pressed(KeyCode::ArrowLeft) { |
| 257 |
config.line.width -= 5. * real_time.delta_secs(); |
| 258 |
config.line.width = config.line.width.clamp(0., 50.); |
| 259 |
} |
| 260 |
if keyboard.just_pressed(KeyCode::Digit1) { |
| 261 |
config.enabled ^= true; |
| 262 |
} |
| 263 |
if keyboard.just_pressed(KeyCode::KeyU) { |
| 264 |
config.line.style = match config.line.style { |
| 265 |
GizmoLineStyle::Solid => GizmoLineStyle::Dotted, |
| 266 |
GizmoLineStyle::Dotted => GizmoLineStyle::Dashed { |
| 267 |
gap_scale: 3.0, |
| 268 |
line_scale: 5.0, |
| 269 |
}, |
| 270 |
_ => GizmoLineStyle::Solid, |
| 271 |
}; |
| 272 |
} |
| 273 |
if keyboard.just_pressed(KeyCode::KeyJ) { |
| 274 |
config.line.joints = match config.line.joints { |
| 275 |
GizmoLineJoint::Bevel => GizmoLineJoint::Miter, |
| 276 |
GizmoLineJoint::Miter => GizmoLineJoint::Round(4), |
| 277 |
GizmoLineJoint::Round(_) => GizmoLineJoint::None, |
| 278 |
GizmoLineJoint::None => GizmoLineJoint::Bevel, |
| 279 |
}; |
| 280 |
} |
| 281 |
|
| 282 |
let (my_config, _) = config_store.config_mut::<MyRoundGizmos>(); |
| 283 |
if keyboard.pressed(KeyCode::ArrowUp) { |
| 284 |
my_config.line.width += 5. * real_time.delta_secs(); |
| 285 |
my_config.line.width = my_config.line.width.clamp(0., 50.); |
| 286 |
} |
| 287 |
if keyboard.pressed(KeyCode::ArrowDown) { |
| 288 |
my_config.line.width -= 5. * real_time.delta_secs(); |
| 289 |
my_config.line.width = my_config.line.width.clamp(0., 50.); |
| 290 |
} |
| 291 |
if keyboard.just_pressed(KeyCode::Digit2) { |
| 292 |
my_config.enabled ^= true; |
| 293 |
} |
| 294 |
if keyboard.just_pressed(KeyCode::KeyI) { |
| 295 |
my_config.line.style = match my_config.line.style { |
| 296 |
GizmoLineStyle::Solid => GizmoLineStyle::Dotted, |
| 297 |
GizmoLineStyle::Dotted => GizmoLineStyle::Dashed { |
| 298 |
gap_scale: 3.0, |
| 299 |
line_scale: 5.0, |
| 300 |
}, |
| 301 |
_ => GizmoLineStyle::Solid, |
| 302 |
}; |
| 303 |
} |
| 304 |
if keyboard.just_pressed(KeyCode::KeyK) { |
| 305 |
my_config.line.joints = match my_config.line.joints { |
| 306 |
GizmoLineJoint::Bevel => GizmoLineJoint::Miter, |
| 307 |
GizmoLineJoint::Miter => GizmoLineJoint::Round(4), |
| 308 |
GizmoLineJoint::Round(_) => GizmoLineJoint::None, |
| 309 |
GizmoLineJoint::None => GizmoLineJoint::Bevel, |
| 310 |
}; |
| 311 |
} |
| 312 |
|
| 313 |
if keyboard.just_pressed(KeyCode::KeyB) { |
| 314 |
|
| 315 |
|
| 316 |
config_store.config_mut::<AabbGizmoConfigGroup>().1.draw_all ^= true; |
| 317 |
} |
| 318 |
if keyboard.just_pressed(KeyCode::Space) { |
| 319 |
virtual_time.toggle(); |
| 320 |
} |
| 321 |
} |
| 322 |
|
| 323 |
enum GizmosAnimationType { |
| 324 |
Linear, |
| 325 |
BackAndForth, |
| 326 |
Stutter, |
| 327 |
} |
| 328 |
|
| 329 |
fn drive_gizmos_animation( |
| 330 |
mut config_store: ResMut<GizmoConfigStore>, |
| 331 |
virtual_time: ResMut<Time<Virtual>>, |
| 332 |
keyboard: Res<ButtonInput<KeyCode>>, |
| 333 |
mut animation_type: Local<Option<GizmosAnimationType>>, |
| 334 |
) { |
| 335 |
if keyboard.just_pressed(KeyCode::KeyL) { |
| 336 |
*animation_type = match *animation_type { |
| 337 |
None => Some(GizmosAnimationType::Linear), |
| 338 |
Some(GizmosAnimationType::Linear) => Some(GizmosAnimationType::BackAndForth), |
| 339 |
Some(GizmosAnimationType::BackAndForth) => Some(GizmosAnimationType::Stutter), |
| 340 |
Some(GizmosAnimationType::Stutter) => None, |
| 341 |
}; |
| 342 |
} |
| 343 |
|
| 344 |
if let Some(animation_type) = animation_type.as_ref() { |
| 345 |
let (my_config, _) = config_store.config_mut::<MyRoundGizmos>(); |
| 346 |
match animation_type { |
| 347 |
GizmosAnimationType::Linear => { |
| 348 |
my_config.line.animation_offset = virtual_time.elapsed_secs() * 10.0; |
| 349 |
} |
| 350 |
GizmosAnimationType::BackAndForth => { |
| 351 |
my_config.line.animation_offset = ops::sin(virtual_time.elapsed_secs()) * 10.0; |
| 352 |
} |
| 353 |
GizmosAnimationType::Stutter => { |
| 354 |
my_config.line.animation_offset = (virtual_time.elapsed_secs() * 4.0).round(); |
| 355 |
} |
| 356 |
} |
| 357 |
} |
| 358 |
} |