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