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