diff --git a/Readme.md b/Readme.md index 37a8dc3..f306eea 100644 --- a/Readme.md +++ b/Readme.md @@ -9,6 +9,8 @@ - [ ] Figure out how rotation up and down will work, maybe dividing upper and lower body animations - [ ] Shuffling feet when rotating + +- [ ] Jump not multiplied by deltatime causing super jumps on low fps - [ ] Perfect movement - [x] ~~Equipping items~~ - [x] ~~Replace items in the same slot, drop them~~ diff --git a/src/comps/core/markers/proxy/character/in_player_hands_parent.rs b/src/comps/core/markers/proxy/character/in_player_hands_parent.rs new file mode 100644 index 0000000..418532d --- /dev/null +++ b/src/comps/core/markers/proxy/character/in_player_hands_parent.rs @@ -0,0 +1,6 @@ +use bevy::{ecs::{component::Component, reflect::ReflectComponent}, reflect::Reflect}; + + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub struct InPlayerHandsParent; \ No newline at end of file diff --git a/src/comps/core/markers/proxy/character/mod.rs b/src/comps/core/markers/proxy/character/mod.rs new file mode 100644 index 0000000..42c3b97 --- /dev/null +++ b/src/comps/core/markers/proxy/character/mod.rs @@ -0,0 +1,4 @@ +pub mod player_character; +pub mod player_hitbox; +pub mod in_player_hands_parent; +pub mod player_eye; \ No newline at end of file diff --git a/src/comps/core/markers/proxy/character/player_character.rs b/src/comps/core/markers/proxy/character/player_character.rs new file mode 100644 index 0000000..a422ee7 --- /dev/null +++ b/src/comps/core/markers/proxy/character/player_character.rs @@ -0,0 +1,6 @@ +use bevy::{ecs::{component::Component, reflect::ReflectComponent}, reflect::Reflect}; + + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub struct PlayerCharacter; diff --git a/src/comps/core/markers/proxy/character/player_eye.rs b/src/comps/core/markers/proxy/character/player_eye.rs new file mode 100644 index 0000000..a643d08 --- /dev/null +++ b/src/comps/core/markers/proxy/character/player_eye.rs @@ -0,0 +1,6 @@ +use bevy::{ecs::{component::Component, reflect::ReflectComponent}, reflect::Reflect}; + + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub struct PlayerEye; \ No newline at end of file diff --git a/src/comps/core/markers/proxy/character/player_hitbox.rs b/src/comps/core/markers/proxy/character/player_hitbox.rs new file mode 100644 index 0000000..830f61a --- /dev/null +++ b/src/comps/core/markers/proxy/character/player_hitbox.rs @@ -0,0 +1,13 @@ +use bevy::{ecs::{component::Component, reflect::ReflectComponent}, reflect::Reflect}; + + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub enum PlayerHitBox { + Head, + Eyes, + #[default] + Hands, + Thorax, + Legs, +} \ No newline at end of file diff --git a/src/comps/core/markers/proxy/mod.rs b/src/comps/core/markers/proxy/mod.rs index e69de29..57a6743 100644 --- a/src/comps/core/markers/proxy/mod.rs +++ b/src/comps/core/markers/proxy/mod.rs @@ -0,0 +1,3 @@ +pub mod character; +pub mod plugin; +pub mod physics; \ No newline at end of file diff --git a/src/comps/core/markers/proxy/physics/mod.rs b/src/comps/core/markers/proxy/physics/mod.rs new file mode 100644 index 0000000..f1bd4b5 --- /dev/null +++ b/src/comps/core/markers/proxy/physics/mod.rs @@ -0,0 +1,2 @@ +pub mod rapier; +pub mod utils; \ No newline at end of file diff --git a/src/comps/core/markers/proxy/physics/rapier.rs b/src/comps/core/markers/proxy/physics/rapier.rs new file mode 100644 index 0000000..ae5d49c --- /dev/null +++ b/src/comps/core/markers/proxy/physics/rapier.rs @@ -0,0 +1,101 @@ +use bevy::prelude::*; +// use bevy::render::primitives::Aabb; +use bevy_rapier3d::geometry::Collider as RapierCollider; +use bevy_rapier3d::prelude::{ActiveCollisionTypes, ActiveEvents, ComputedColliderShape}; + +use super::utils::*; + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub enum Collider { + Ball(f32), + Cuboid(Vec3), + Capsule(Vec3, Vec3, f32), + #[default] + Mesh, +} + +#[derive(Component, Reflect, Default, Debug)] +#[reflect(Component)] +pub enum AutoAABBCollider { + #[default] + Cuboid, + Ball, + Capsule, +} + +// replaces all physics stand-ins with the actual rapier types +pub fn physics_replace_proxies( + meshes: Res>, + mesh_handles: Query<&Handle>, + mut proxy_colliders: Query< + (Entity, &Collider, &Name, &mut Visibility), + (Without, Added), + >, + // needed for tri meshes + children: Query<&Children>, + + mut commands: Commands, +) { + for proxy_colider in proxy_colliders.iter_mut() { + let (entity, collider_proxy, name, mut visibility) = proxy_colider; + // we hide the collider meshes: perhaps they should be removed altogether once processed ? + if name.ends_with("_collider") || name.ends_with("_sensor") { + *visibility = Visibility::Hidden; + } + + let mut rapier_collider: RapierCollider; + match collider_proxy { + Collider::Ball(radius) => { + println!("proxy: ball"); + rapier_collider = RapierCollider::ball(*radius); + commands.entity(entity) + .insert(rapier_collider) + .insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!! + ; + } + Collider::Cuboid(size) => { + println!("proxy: cuboid"); + rapier_collider = RapierCollider::cuboid(size.x, size.y, size.z); + commands.entity(entity) + .insert(rapier_collider) + .insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!! + ; + } + Collider::Capsule(a, b, radius) => { + println!("proxy: capsule"); + rapier_collider = RapierCollider::capsule(*a, *b, *radius); + commands.entity(entity) + .insert(rapier_collider) + .insert(ActiveEvents::COLLISION_EVENTS) // FIXME: this is just for demo purposes !!! + ; + } + Collider::Mesh => { + println!("proxy: mesh"); + for (_, collider_mesh) in + Mesh::search_in_children(entity, &children, &meshes, &mesh_handles) + { + rapier_collider = RapierCollider::from_bevy_mesh( + collider_mesh, + &ComputedColliderShape::TriMesh, + ) + .unwrap(); + commands + .entity(entity) + .insert(rapier_collider) + // FIXME: this is just for demo purposes !!! + .insert( + ActiveCollisionTypes::default() + | ActiveCollisionTypes::KINEMATIC_STATIC + | ActiveCollisionTypes::STATIC_STATIC + | ActiveCollisionTypes::DYNAMIC_STATIC, + ) + .insert(ActiveEvents::COLLISION_EVENTS); + // .insert(ActiveEvents::COLLISION_EVENTS) + // break; + // RapierCollider::convex_hull(points) + } + } + } + } +} \ No newline at end of file diff --git a/src/comps/core/markers/proxy/physics/utils.rs b/src/comps/core/markers/proxy/physics/utils.rs new file mode 100644 index 0000000..714ec8f --- /dev/null +++ b/src/comps/core/markers/proxy/physics/utils.rs @@ -0,0 +1,175 @@ +use bevy::prelude::*; +use bevy::render::mesh::{MeshVertexAttributeId, PrimitiveTopology, VertexAttributeValues}; +// TAKEN VERBATIB FROM https://github.com/janhohenheim/foxtrot/blob/src/util/trait_extension.rs + +pub(crate) trait Vec3Ext: Copy { + fn is_approx_zero(self) -> bool; + fn split(self, up: Vec3) -> SplitVec3; +} +impl Vec3Ext for Vec3 { + #[inline] + fn is_approx_zero(self) -> bool { + self.length_squared() < 1e-5 + } + + #[inline] + fn split(self, up: Vec3) -> SplitVec3 { + let vertical = up * self.dot(up); + let horizontal = self - vertical; + SplitVec3 { + vertical, + horizontal, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub(crate) struct SplitVec3 { + pub(crate) vertical: Vec3, + pub(crate) horizontal: Vec3, +} + +pub(crate) trait Vec2Ext: Copy { + fn is_approx_zero(self) -> bool; + fn x0y(self) -> Vec3; +} +impl Vec2Ext for Vec2 { + #[inline] + fn is_approx_zero(self) -> bool { + self.length_squared() < 1e-5 + } + + #[inline] + fn x0y(self) -> Vec3 { + Vec3::new(self.x, 0., self.y) + } +} + +pub(crate) trait MeshExt { + fn transform(&mut self, transform: Transform); + fn transformed(&self, transform: Transform) -> Mesh; + fn read_coords_mut(&mut self, id: impl Into) -> &mut Vec<[f32; 3]>; + fn search_in_children<'a>( + parent: Entity, + children: &'a Query<&Children>, + meshes: &'a Assets, + mesh_handles: &'a Query<&Handle>, + ) -> Vec<(Entity, &'a Mesh)>; +} + +impl MeshExt for Mesh { + fn transform(&mut self, transform: Transform) { + for coords in self.read_coords_mut(Mesh::ATTRIBUTE_POSITION.clone()) { + let vec3 = (*coords).into(); + let transformed = transform.transform_point(vec3); + *coords = transformed.into(); + } + for normal in self.read_coords_mut(Mesh::ATTRIBUTE_NORMAL.clone()) { + let vec3 = (*normal).into(); + let transformed = transform.rotation.mul_vec3(vec3); + *normal = transformed.into(); + } + } + + fn transformed(&self, transform: Transform) -> Mesh { + let mut mesh = self.clone(); + mesh.transform(transform); + mesh + } + + fn read_coords_mut(&mut self, id: impl Into) -> &mut Vec<[f32; 3]> { + // Guaranteed by Bevy for the current usage + match self + .attribute_mut(id) + .expect("Failed to read unknown mesh attribute") + { + VertexAttributeValues::Float32x3(values) => values, + // Guaranteed by Bevy for the current usage + _ => unreachable!(), + } + } + + fn search_in_children<'a>( + parent: Entity, + children_query: &'a Query<&Children>, + meshes: &'a Assets, + mesh_handles: &'a Query<&Handle>, + ) -> Vec<(Entity, &'a Mesh)> { + if let Ok(children) = children_query.get(parent) { + let mut result: Vec<_> = children + .iter() + .filter_map(|entity| mesh_handles.get(*entity).ok().map(|mesh| (*entity, mesh))) + .map(|(entity, mesh_handle)| { + ( + entity, + meshes + .get(mesh_handle) + .expect("Failed to get mesh from handle"), + ) + }) + .map(|(entity, mesh)| { + assert_eq!(mesh.primitive_topology(), PrimitiveTopology::TriangleList); + (entity, mesh) + }) + .collect(); + let mut inner_result = children + .iter() + .flat_map(|entity| { + Self::search_in_children(*entity, children_query, meshes, mesh_handles) + }) + .collect(); + result.append(&mut inner_result); + result + } else { + Vec::new() + } + } +} + +pub(crate) trait F32Ext: Copy { + fn is_approx_zero(self) -> bool; + fn squared(self) -> f32; + fn lerp(self, other: f32, ratio: f32) -> f32; +} + +impl F32Ext for f32 { + #[inline] + fn is_approx_zero(self) -> bool { + self.abs() < 1e-5 + } + + #[inline] + fn squared(self) -> f32 { + self * self + } + + #[inline] + fn lerp(self, other: f32, ratio: f32) -> f32 { + self.mul_add(1. - ratio, other * ratio) + } +} + +pub(crate) trait TransformExt: Copy { + fn horizontally_looking_at(self, target: Vec3, up: Vec3) -> Transform; + fn lerp(self, other: Transform, ratio: f32) -> Transform; +} + +impl TransformExt for Transform { + fn horizontally_looking_at(self, target: Vec3, up: Vec3) -> Transform { + let direction = target - self.translation; + let horizontal_direction = direction - up * direction.dot(up); + let look_target = self.translation + horizontal_direction; + self.looking_at(look_target, up) + } + + fn lerp(self, other: Transform, ratio: f32) -> Transform { + let translation = self.translation.lerp(other.translation, ratio); + let rotation = self.rotation.slerp(other.rotation, ratio); + let scale = self.scale.lerp(other.scale, ratio); + Transform { + translation, + rotation, + scale, + } + } +} \ No newline at end of file diff --git a/src/comps/core/markers/proxy/plugin.rs b/src/comps/core/markers/proxy/plugin.rs new file mode 100644 index 0000000..98d2006 --- /dev/null +++ b/src/comps/core/markers/proxy/plugin.rs @@ -0,0 +1,23 @@ +use bevy::app::{Plugin, Update}; + +use super::{character::{player_hitbox::PlayerHitBox, player_character::PlayerCharacter, player_eye::PlayerEye, in_player_hands_parent::InPlayerHandsParent}, physics::{rapier::{AutoAABBCollider, physics_replace_proxies}, self}}; + + + +pub struct ProxyComponentsPlugin; + +impl Plugin for ProxyComponentsPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + // Character + app.register_type::(); + app.register_type::(); + app.register_type::(); + app.register_type::(); + + // Physics + app + .register_type::() + .register_type::() + .add_systems(Update, physics_replace_proxies); + } +} \ No newline at end of file diff --git a/src/comps/core/spawners/player.rs b/src/comps/core/spawners/player.rs index 463b018..d4e8974 100644 --- a/src/comps/core/spawners/player.rs +++ b/src/comps/core/spawners/player.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, gltf::Gltf}; use bevy_rapier3d::prelude::*; use crate::{ @@ -16,7 +16,7 @@ use crate::{ player_values_state::PlayerValuesState, }, }, - setup::{equipment::EquipmentChangeEvent, load_state::GameLoadState}, + setup::{equipment::EquipmentChangeEvent, load_state::GameLoadState, assets::{GltfAsset, GltfAssets}}, }; use super::spawn_point::SpawnPoint; @@ -41,12 +41,27 @@ pub fn player_spawner( mut game_load_state: ResMut, mut equipment_change_event_writer: EventWriter, player_values_state: Res, + assets: Res, + loaded_gltf_assets: Res>, ) { if game_load_state.player_loaded || !game_load_state.is_everything_except_player_loaded() { return; } for (player_spawn_point_entity, player_spawn_point) in player_sp_query.iter() { + let mut plyer_transform = Transform::from_xyz(25.0, 0.0, 25.0).with_scale(Vec3 { x: 3.0, y: 3.0, z: 3.0 }); + commands.spawn( + ( + SceneBundle { + scene: loaded_gltf_assets.get(assets.assets[2].asset.clone()).unwrap().scenes[0].clone(), + transform: plyer_transform, + visibility: Visibility::Visible, + ..Default::default() + }, + Name::new("Player component") + ) + ); + // Spawn hand let player_hand = commands .spawn((PlayerHand, Name::new("Player Hand"))) diff --git a/src/main.rs b/src/main.rs index 6f56490..41a4a9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use bevy::prelude::*; use bevy_gltf_components::ComponentsFromGltfPlugin; use bevy_rapier3d::prelude::*; +use comps::core::markers::proxy; use scenes::scene1; use ui::{editor::plugin::MainEditorUiPlugin, game::plugin::MainGameUIPlugin}; @@ -33,6 +34,7 @@ fn setup_plugins(application: &mut App) { .add_plugins(RapierPhysicsPlugin::::default()) //.add_plugins(bevy_egui::EguiPlugin) //.add_plugins(WorldInspectorPlugin::new()) + .add_plugins(proxy::plugin::ProxyComponentsPlugin) .add_plugins(MainGameUIPlugin) .add_plugins(MainEditorUiPlugin) .add_plugins(RapierDebugRenderPlugin::default());