1use std::f32::consts::*;
5
6use bevy::{
7 asset::RenderAssetUsages,
8 camera::visibility::DynamicSkinnedMeshBounds,
9 math::ops,
10 mesh::{
11 skinning::{SkinnedMesh, SkinnedMeshInverseBindposes},
12 Indices, PrimitiveTopology, VertexAttributeValues,
13 },
14 prelude::*,
15};
16use chacha20::ChaCha8Rng;
17use rand::{RngExt, SeedableRng};
18
19fn main() {
20 App::new()
21 .add_plugins(DefaultPlugins)
22 .insert_resource(GlobalAmbientLight {
23 brightness: 3000.0,
24 ..default()
25 })
26 .add_systems(Startup, setup)
27 .add_systems(Update, joint_animation)
28 .run();
29}
30
31#[derive(Component)]
33struct AnimatedJoint(isize);
34
35fn setup(
39 mut commands: Commands,
40 asset_server: Res<AssetServer>,
41 mut meshes: ResMut<Assets<Mesh>>,
42 mut materials: ResMut<Assets<StandardMaterial>>,
43 mut skinned_mesh_inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
44) {
45 commands.spawn((
47 Camera3d::default(),
48 Transform::from_xyz(2.5, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
49 ));
50
51 let inverse_bindposes = skinned_mesh_inverse_bindposes_assets.add(vec![
53 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
54 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
55 ]);
56
57 let mesh = Mesh::new(
59 PrimitiveTopology::TriangleList,
60 RenderAssetUsages::RENDER_WORLD,
61 )
62 .with_inserted_attribute(
64 Mesh::ATTRIBUTE_POSITION,
65 vec![
66 [0.0, 0.0, 0.0],
67 [1.0, 0.0, 0.0],
68 [0.0, 0.5, 0.0],
69 [1.0, 0.5, 0.0],
70 [0.0, 1.0, 0.0],
71 [1.0, 1.0, 0.0],
72 [0.0, 1.5, 0.0],
73 [1.0, 1.5, 0.0],
74 [0.0, 2.0, 0.0],
75 [1.0, 2.0, 0.0],
76 ],
77 )
78 .with_inserted_attribute(
81 Mesh::ATTRIBUTE_UV_0,
82 vec![
83 [0.0, 0.00],
84 [0.5, 0.00],
85 [0.0, 0.25],
86 [0.5, 0.25],
87 [0.0, 0.50],
88 [0.5, 0.50],
89 [0.0, 0.75],
90 [0.5, 0.75],
91 [0.0, 1.00],
92 [0.5, 1.00],
93 ],
94 )
95 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
97 .with_inserted_attribute(
102 Mesh::ATTRIBUTE_JOINT_INDEX,
103 VertexAttributeValues::Uint16x4(vec![
105 [0, 0, 0, 0],
106 [0, 0, 0, 0],
107 [0, 1, 0, 0],
108 [0, 1, 0, 0],
109 [0, 1, 0, 0],
110 [0, 1, 0, 0],
111 [0, 1, 0, 0],
112 [0, 1, 0, 0],
113 [0, 1, 0, 0],
114 [0, 1, 0, 0],
115 ]),
116 )
117 .with_inserted_attribute(
121 Mesh::ATTRIBUTE_JOINT_WEIGHT,
122 vec![
123 [1.00, 0.00, 0.0, 0.0],
124 [1.00, 0.00, 0.0, 0.0],
125 [0.75, 0.25, 0.0, 0.0],
126 [0.75, 0.25, 0.0, 0.0],
127 [0.50, 0.50, 0.0, 0.0],
128 [0.50, 0.50, 0.0, 0.0],
129 [0.25, 0.75, 0.0, 0.0],
130 [0.25, 0.75, 0.0, 0.0],
131 [0.00, 1.00, 0.0, 0.0],
132 [0.00, 1.00, 0.0, 0.0],
133 ],
134 )
135 .with_inserted_indices(Indices::U16(vec![
138 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8,
139 ]))
140 .with_generated_skinned_mesh_bounds()
143 .unwrap();
144
145 let mesh = meshes.add(mesh);
146
147 let mut rng = ChaCha8Rng::seed_from_u64(42);
150
151 for i in -5..5 {
152 let joint_0 = commands
154 .spawn(Transform::from_xyz(
155 i as f32 * 1.5,
156 0.0,
157 -(i as f32 * 0.01).abs(),
160 ))
161 .id();
162 let joint_1 = commands.spawn((AnimatedJoint(i), Transform::IDENTITY)).id();
163
164 commands.entity(joint_0).add_children(&[joint_1]);
166
167 let joint_entities = vec![joint_0, joint_1];
169
170 commands.spawn((
172 Mesh3d(mesh.clone()),
173 MeshMaterial3d(materials.add(StandardMaterial {
174 base_color: Color::srgb(
175 rng.random_range(0.0..1.0),
176 rng.random_range(0.0..1.0),
177 rng.random_range(0.0..1.0),
178 ),
179 base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
180 ..default()
181 })),
182 SkinnedMesh {
183 inverse_bindposes: inverse_bindposes.clone(),
184 joints: joint_entities,
185 },
186 DynamicSkinnedMeshBounds,
187 ));
188 }
189}
190
191fn joint_animation(
193 time: Res<Time>,
194 mut query: Query<(&mut Transform, &AnimatedJoint)>,
195 mut gizmos: Gizmos,
196) {
197 for (mut transform, animated_joint) in &mut query {
198 match animated_joint.0 {
199 -5 => {
200 transform.rotation =
201 Quat::from_rotation_x(FRAC_PI_2 * ops::sin(time.elapsed_secs()));
202 }
203 -4 => {
204 transform.rotation =
205 Quat::from_rotation_y(FRAC_PI_2 * ops::sin(time.elapsed_secs()));
206 }
207 -3 => {
208 transform.rotation =
209 Quat::from_rotation_z(FRAC_PI_2 * ops::sin(time.elapsed_secs()));
210 }
211 -2 => {
212 transform.scale.x = ops::sin(time.elapsed_secs()) + 1.0;
213 }
214 -1 => {
215 transform.scale.y = ops::sin(time.elapsed_secs()) + 1.0;
216 }
217 0 => {
218 transform.translation.x = 0.5 * ops::sin(time.elapsed_secs());
219 transform.translation.y = ops::cos(time.elapsed_secs());
220 }
221 1 => {
222 transform.translation.y = ops::sin(time.elapsed_secs());
223 transform.translation.z = ops::cos(time.elapsed_secs());
224 }
225 2 => {
226 transform.translation.x = ops::sin(time.elapsed_secs());
227 }
228 3 => {
229 transform.translation.y = ops::sin(time.elapsed_secs());
230 transform.scale.x = ops::sin(time.elapsed_secs()) + 1.0;
231 }
232 _ => (),
233 }
234 let mut axis = *transform;
236 axis.translation.x += animated_joint.0 as f32 * 1.5;
237 gizmos.axes(axis, 1.0);
238 }
239}