1use std::f32::consts::PI;
17
18#[cfg(not(target_arch = "wasm32"))]
19use bevy::pbr::wireframe::{WireframeConfig, WireframePlugin};
20use bevy::{
21 asset::RenderAssetUsages,
22 color::palettes::basic::SILVER,
23 input::common_conditions::{input_just_pressed, input_toggle_active},
24 prelude::*,
25 render::render_resource::{Extent3d, TextureDimension, TextureFormat},
26};
27
28fn main() {
29 App::new()
30 .add_plugins((
31 DefaultPlugins.set(ImagePlugin::default_nearest()),
32 #[cfg(not(target_arch = "wasm32"))]
33 WireframePlugin::default(),
34 ))
35 .add_systems(Startup, setup)
36 .add_systems(
37 Update,
38 (
39 rotate.run_if(input_toggle_active(true, KeyCode::KeyR)),
40 advance_rows.run_if(input_just_pressed(KeyCode::Tab)),
41 #[cfg(not(target_arch = "wasm32"))]
42 toggle_wireframe,
43 ),
44 )
45 .run();
46}
47
48#[derive(Component)]
50struct Shape;
51
52const SHAPES_X_EXTENT: f32 = 14.0;
53const EXTRUSION_X_EXTENT: f32 = 14.0;
54const Z_EXTENT: f32 = 8.0;
55const THICKNESS: f32 = 0.1;
56
57fn setup(
58 mut commands: Commands,
59 mut meshes: ResMut<Assets<Mesh>>,
60 mut images: ResMut<Assets<Image>>,
61 mut materials: ResMut<Assets<StandardMaterial>>,
62) {
63 let debug_material = materials.add(StandardMaterial {
64 base_color_texture: Some(images.add(uv_debug_texture())),
65 ..default()
66 });
67
68 let shapes = [
69 meshes.add(Cuboid::default()),
70 meshes.add(Tetrahedron::default()),
71 meshes.add(Capsule3d::default()),
72 meshes.add(Torus::default()),
73 meshes.add(Cylinder::default()),
74 meshes.add(Cone::default()),
75 meshes.add(ConicalFrustum::default()),
76 meshes.add(Sphere::default().mesh().ico(5).unwrap()),
77 meshes.add(Sphere::default().mesh().uv(32, 18)),
78 meshes.add(Segment3d::default()),
79 meshes.add(Polyline3d::new(vec![
80 Vec3::new(-0.5, 0.0, 0.0),
81 Vec3::new(0.5, 0.0, 0.0),
82 Vec3::new(0.0, 0.5, 0.0),
83 ])),
84 ];
85
86 let extrusions = [
87 meshes.add(Extrusion::new(Rectangle::default(), 1.)),
88 meshes.add(Extrusion::new(Capsule2d::default(), 1.)),
89 meshes.add(Extrusion::new(Annulus::default(), 1.)),
90 meshes.add(Extrusion::new(Circle::default(), 1.)),
91 meshes.add(Extrusion::new(Ellipse::default(), 1.)),
92 meshes.add(Extrusion::new(RegularPolygon::default(), 1.)),
93 meshes.add(Extrusion::new(Triangle2d::default(), 1.)),
94 meshes.add(Extrusion::new(
95 ConvexPolygon::new(vec![
96 Vec2::new(0.0, 0.8),
97 Vec2::new(-0.47, 0.25),
98 Vec2::new(-0.47, -0.65),
99 Vec2::new(0.47, -0.65),
100 Vec2::new(0.47, 0.25),
101 ])
102 .unwrap(),
103 1.0,
104 )),
105 ];
106
107 let ring_extrusions = [
108 meshes.add(Extrusion::new(Rectangle::default().to_ring(THICKNESS), 1.)),
109 meshes.add(Extrusion::new(Capsule2d::default().to_ring(THICKNESS), 1.)),
110 meshes.add(Extrusion::new(
111 Ring::new(Circle::new(1.0), Circle::new(0.5)),
112 1.,
113 )),
114 meshes.add(Extrusion::new(Circle::default().to_ring(THICKNESS), 1.)),
115 meshes.add(Extrusion::new(
116 {
117 let outer = Ellipse::default();
119 let mut inner = outer;
120 inner.half_size -= Vec2::splat(THICKNESS);
121 Ring::new(outer, inner)
122 },
123 1.,
124 )),
125 meshes.add(Extrusion::new(
126 RegularPolygon::default().to_ring(THICKNESS),
127 1.,
128 )),
129 meshes.add(Extrusion::new(Triangle2d::default().to_ring(THICKNESS), 1.)),
130 ];
131
132 let num_shapes = shapes.len();
133
134 for (i, shape) in shapes.into_iter().enumerate() {
135 commands.spawn((
136 Mesh3d(shape),
137 MeshMaterial3d(debug_material.clone()),
138 Transform::from_xyz(
139 -SHAPES_X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * SHAPES_X_EXTENT,
140 2.0,
141 Row::Front.z(),
142 )
143 .with_rotation(Quat::from_rotation_x(-PI / 4.)),
144 Shape,
145 Row::Front,
146 ));
147 }
148
149 let num_extrusions = extrusions.len();
150
151 for (i, shape) in extrusions.into_iter().enumerate() {
152 commands.spawn((
153 Mesh3d(shape),
154 MeshMaterial3d(debug_material.clone()),
155 Transform::from_xyz(
156 -EXTRUSION_X_EXTENT / 2.
157 + i as f32 / (num_extrusions - 1) as f32 * EXTRUSION_X_EXTENT,
158 2.0,
159 Row::Middle.z(),
160 )
161 .with_rotation(Quat::from_rotation_x(-PI / 4.)),
162 Shape,
163 Row::Middle,
164 ));
165 }
166
167 let num_ring_extrusions = ring_extrusions.len();
168
169 for (i, shape) in ring_extrusions.into_iter().enumerate() {
170 commands.spawn((
171 Mesh3d(shape),
172 MeshMaterial3d(debug_material.clone()),
173 Transform::from_xyz(
174 -EXTRUSION_X_EXTENT / 2.
175 + i as f32 / (num_ring_extrusions - 1) as f32 * EXTRUSION_X_EXTENT,
176 2.0,
177 Row::Rear.z(),
178 )
179 .with_rotation(Quat::from_rotation_x(-PI / 4.)),
180 Shape,
181 Row::Rear,
182 ));
183 }
184
185 commands.spawn((
186 PointLight {
187 shadow_maps_enabled: true,
188 intensity: 10_000_000.,
189 range: 100.0,
190 shadow_depth_bias: 0.2,
191 ..default()
192 },
193 Transform::from_xyz(8.0, 16.0, 8.0),
194 ));
195
196 commands.spawn((
198 Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0).subdivisions(10))),
199 MeshMaterial3d(materials.add(Color::from(SILVER))),
200 ));
201
202 commands.spawn((
203 Camera3d::default(),
204 Transform::from_xyz(0.0, 7., 14.0).looking_at(Vec3::new(0., 1., 0.), Vec3::Y),
205 ));
206
207 let mut text = "\
208 Press 'R' to pause/resume rotation\n\
209 Press 'Tab' to cycle through rows"
210 .to_string();
211 #[cfg(not(target_arch = "wasm32"))]
212 text.push_str("\nPress 'Space' to toggle wireframes");
213
214 commands.spawn((
215 Text::new(text),
216 Node {
217 position_type: PositionType::Absolute,
218 top: px(12),
219 left: px(12),
220 ..default()
221 },
222 ));
223}
224
225fn rotate(mut query: Query<&mut Transform, With<Shape>>, time: Res<Time>) {
226 for mut transform in &mut query {
227 transform.rotate_y(time.delta_secs() / 2.);
228 }
229}
230
231fn uv_debug_texture() -> Image {
233 const TEXTURE_SIZE: usize = 8;
234
235 let mut palette: [u8; 32] = [
236 255, 102, 159, 255, 255, 159, 102, 255, 236, 255, 102, 255, 121, 255, 102, 255, 102, 255,
237 198, 255, 102, 198, 255, 255, 121, 102, 255, 255, 236, 102, 255, 255,
238 ];
239
240 let mut texture_data = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4];
241 for y in 0..TEXTURE_SIZE {
242 let offset = TEXTURE_SIZE * y * 4;
243 texture_data[offset..(offset + TEXTURE_SIZE * 4)].copy_from_slice(&palette);
244 palette.rotate_right(4);
245 }
246
247 Image::new_fill(
248 Extent3d {
249 width: TEXTURE_SIZE as u32,
250 height: TEXTURE_SIZE as u32,
251 depth_or_array_layers: 1,
252 },
253 TextureDimension::D2,
254 &texture_data,
255 TextureFormat::Rgba8UnormSrgb,
256 RenderAssetUsages::RENDER_WORLD,
257 )
258}
259
260#[cfg(not(target_arch = "wasm32"))]
261fn toggle_wireframe(
262 mut wireframe_config: ResMut<WireframeConfig>,
263 keyboard: Res<ButtonInput<KeyCode>>,
264) {
265 if keyboard.just_pressed(KeyCode::Space) {
266 wireframe_config.global = !wireframe_config.global;
267 }
268}
269
270#[derive(Component, Clone, Copy)]
271enum Row {
272 Front,
273 Middle,
274 Rear,
275}
276
277impl Row {
278 fn z(self) -> f32 {
279 match self {
280 Row::Front => Z_EXTENT / 2.,
281 Row::Middle => 0.,
282 Row::Rear => -Z_EXTENT / 2.,
283 }
284 }
285
286 fn advance(self) -> Self {
287 match self {
288 Row::Front => Row::Rear,
289 Row::Middle => Row::Front,
290 Row::Rear => Row::Middle,
291 }
292 }
293}
294
295fn advance_rows(mut shapes: Query<(&mut Row, &mut Transform), With<Shape>>) {
296 for (mut row, mut transform) in &mut shapes {
297 *row = row.advance();
298 transform.translation.z = row.z();
299 }
300}