use avian3d::prelude::*; use bevy::prelude::{Torus as Tire, *}; use super::{ CyberFork, CyberSpring, CyberSteering, CyberWheel, Meshterial, WheelConfig, BIKE_WHEEL_COLLISION_GROUP, }; pub fn spawn_wheels( commands: &mut Commands, bike: Entity, xform: &Transform, conf: &WheelConfig, meshterials: &mut Meshterial, ) { let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP; let wheels_collision_group = CollisionLayers::new(membership, filter); let wheel_y = conf.y; let not_sleeping = SleepingDisabled; let ccd = SweptCcd::NON_LINEAR.include_dynamic(false); let limits = conf.limits; let (meshes, materials) = meshterials; let rake_vec: Vec3 = Vec3::new(0.0, 1.0, 0.57).normalize(); // about 30 degrees of rake let friction = Friction { dynamic_coefficient: conf.friction, static_coefficient: conf.friction, combine_rule: CoefficientCombine::Average, }; let mut wheel_poses = Vec::with_capacity(2); // front { let wheel_x = 0.0; let wheel_z = -conf.front_forward; let offset = Vec3::new(wheel_x, wheel_y, wheel_z); wheel_poses.push((offset, Some(CyberSteering))); } // rear { let wheel_x = 0.0; let wheel_z = conf.rear_back; let offset = Vec3::new(wheel_x, wheel_y, wheel_z); wheel_poses.push((offset, None)); } for (offset, steering) in wheel_poses { let (mesh, collider) = gen_tires(conf); let material = StandardMaterial { base_color: Color::linear_rgba(0.01, 0.01, 0.01, 1.0), alpha_mode: AlphaMode::Opaque, perceptual_roughness: 0.5, ..Default::default() }; let pbr_bundle = PbrBundle { material: materials.add(material), mesh: meshes.add(mesh), ..Default::default() }; let suspension_axis = if steering.is_some() { rake_vec } else { Vec3::Y }; let wheel = commands .spawn(pbr_bundle) .insert(( collider, ccd, not_sleeping, wheels_collision_group, friction, CyberWheel, ExternalForce::default(), Restitution::new(conf.restitution), SpatialBundle::default(), RigidBody::Dynamic, ColliderDensity(0.1), AngularVelocity::ZERO, ExternalTorque::ZERO, )) .insert(SpatialBundle::from_transform( xform.with_translation(xform.translation + offset), )) .id(); let spring = CyberSpring(suspension_axis); let axel = if let Some(steering) = steering { commands.spawn(( RigidBody::Dynamic, ExternalForce::ZERO, steering, spring, Collider::sphere(0.1), ColliderDensity(0.1), CollisionLayers::from_bits(0, 0), )) } else { commands.spawn(( RigidBody::Dynamic, ExternalForce::ZERO, spring, Collider::sphere(0.1), ColliderDensity(0.1), CollisionLayers::from_bits(0, 0), )) } .insert(SpatialBundle::from_transform( xform.with_translation(xform.translation + offset), )) .id(); let axel_joint = RevoluteJoint::new(axel, wheel).with_aligned_axis(Vec3::NEG_X); commands.spawn(axel_joint); // suspension and steering: if steering.is_some() { let joint = PrismaticJoint::new(axel, bike) .with_free_axis(suspension_axis) .with_local_anchor_1(Vec3::new(0.0, 0.0, 0.1)) .with_local_anchor_2(offset) .with_limits(limits[0], limits[1]); commands.spawn((joint, CyberFork)); } else { let joint = PrismaticJoint::new(axel, bike) .with_free_axis(suspension_axis) .with_local_anchor_2(offset) .with_limits(limits[0], limits[1]); commands.spawn(joint); }; } } // do mesh shit fn gen_tires(conf: &WheelConfig) -> (Mesh, Collider) { let wheel_rad = conf.radius; let tire_thickness = conf.thickness; let tire = Tire { minor_radius: tire_thickness, major_radius: wheel_rad, }; let mut mesh = Mesh::from(tire); let tire_verts = mesh .attribute(Mesh::ATTRIBUTE_POSITION) .unwrap() .as_float3() .unwrap() .iter() .map(|v| { // let v = Vec3::from_array(*v); let m = Mat3::from_rotation_z(90.0f32.to_radians()); let p = m.mul_vec3(v); p.to_array() }) .collect::>(); mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, tire_verts); let mut idxs = Vec::new(); let indices = mesh.indices().unwrap().iter().collect::>(); for idx in indices.as_slice().chunks_exact(3) { idxs.push([idx[0] as u32, idx[1] as u32, idx[2] as u32]); } let wheel_collider = Collider::convex_decomposition_from_mesh(&mesh).unwrap(); (mesh, wheel_collider) }