Skip to content

Commit 9eecf1a

Browse files
committed
Initial physics system implementation
1 parent bcb81ee commit 9eecf1a

11 files changed

Lines changed: 172 additions & 37 deletions

File tree

‎Cargo.lock‎

Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎core/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ num-derive = "0.2.5"
2323
hashbrown = { version = "0.5.0", features = ["serde"] }
2424
hematite-nbt = { git = "https://github.com/caelunshun/hematite_nbt" }
2525
byteorder = "1.3.2"
26-
nalgebra = "0.18.0"
26+
nalgebra-glm = "0.4.0"

‎core/src/lib.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ extern crate feather_codegen;
1111
#[macro_use]
1212
extern crate num_derive;
1313

14+
extern crate nalgebra_glm as glm;
15+
1416
pub mod bytebuf;
1517
pub mod entitymeta;
1618
pub mod inventory;

‎core/src/world/mod.rs‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::world::block::*;
22
use crate::world::chunk::Chunk;
3+
use glm::Vec3;
34
use hashbrown::HashMap;
4-
use nalgebra::Vector3;
55
use std::ops::{Add, Sub};
66

77
pub mod block;
@@ -35,24 +35,24 @@ impl Position {
3535
}
3636
}
3737

38-
impl Add<Vector3<f64>> for Position {
38+
impl Add<Vec3> for Position {
3939
type Output = Position;
4040

41-
fn add(self, vec: Vector3<f64>) -> Self::Output {
42-
self.x += vec.x;
43-
self.y += vec.y;
44-
self.z += vec.z;
41+
fn add(mut self, vec: Vec3) -> Self::Output {
42+
self.x += vec.x as f64;
43+
self.y += vec.y as f64;
44+
self.z += vec.z as f64;
4545
self
4646
}
4747
}
4848

49-
impl Sub<Vector3<f64>> for Position {
49+
impl Sub<Vec3> for Position {
5050
type Output = Position;
5151

52-
fn sub(self, vec: Vector3<f64>) -> Self::Output {
53-
self.x -= vec.x;
54-
self.y -= vec.y;
55-
self.z -= vec.z;
52+
fn sub(mut self, vec: Vec3) -> Self::Output {
53+
self.x -= vec.x as f64;
54+
self.y -= vec.y as f64;
55+
self.z -= vec.z as f64;
5656
self
5757
}
5858
}

‎server/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ num-derive = "0.2.5"
3535
num-traits = "0.2.8"
3636
smallvec = "0.6.10"
3737
lazy_static = "1.3.0"
38-
nalgebra = "0.18.0"
38+
nalgebra-glm = "0.4.0"
3939
derive_deref = "1.1.0"

‎server/src/entity/component.rs‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use feather_core::world::Position;
44
use feather_core::Gamemode;
5-
use nalgebra::base::Vector3;
5+
use glm::Vec3;
66
use specs::storage::BTreeStorage;
77
use specs::{Component, DenseVecStorage, VecStorage};
88
use uuid::Uuid;
@@ -31,10 +31,8 @@ impl Component for EntityComponent {
3131
///
3232
/// Entities without this component are assumed
3333
/// to have a velocity of 0.
34-
#[derive(Default, Deref, DerefMut)]
35-
pub struct VelocityComponent {
36-
pub velocity: Vector3<f64>,
37-
}
34+
#[derive(Deref, DerefMut)]
35+
pub struct VelocityComponent(pub Vec3);
3836

3937
impl Component for VelocityComponent {
4038
type Storage = DenseVecStorage<Self>;

‎server/src/entity/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ mod types;
1111
pub use broadcast::{EntityBroadcastSystem, EntitySpawnEvent};
1212
pub use component::{EntityComponent, PlayerComponent, VelocityComponent};
1313
pub use destroy::{EntityDestroyBroadcastSystem, EntityDestroyEvent, EntityDestroySystem};
14-
pub use movement::broadcast_entity_movement;
14+
pub use movement::{broadcast_entity_movement, EntityMoveEvent};
1515
pub use types::EntityType;

‎server/src/entity/movement.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ use feather_core::network::packet::implementation::{
66
EntityHeadLook, EntityLook, EntityLookAndRelativeMove, EntityRelativeMove,
77
};
88

9+
/// Event triggered when an entity moves.
10+
///
11+
/// This event is triggered *after* the entity's
12+
/// position is updated.
13+
#[derive(Debug, Clone)]
14+
pub struct EntityMoveEvent {
15+
pub entity: Entity,
16+
pub old_pos: Position,
17+
pub new_pos: Position,
18+
}
19+
920
/// Broadcasts to all joined players that an entity has moved.
1021
#[allow(clippy::too_many_arguments)]
1122
pub fn broadcast_entity_movement(

‎server/src/main.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ extern crate lazy_static;
2121
#[macro_use]
2222
extern crate derive_deref;
2323

24+
extern crate nalgebra_glm as glm;
25+
2426
use std::alloc::System;
2527
use std::sync::atomic::{AtomicUsize, Ordering};
2628
use std::sync::Arc;
@@ -207,6 +209,7 @@ fn init_world<'a, 'b>(
207209
"chunk_send",
208210
&["chunk_load"],
209211
)
212+
.with(physics::EntityPhysicsSystem, "entity_physics", &[])
210213
.with(
211214
joinhandler::JoinHandlerSystem,
212215
"join_handler",

‎server/src/physics/entity.rs‎

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Module for performing entity physics, including velocity, drag
22
//! and position updates each tick.
33
4-
use crate::entity::{EntityComponent, EntityType, VelocityComponent};
4+
use crate::entity::{EntityComponent, EntityMoveEvent, EntityType, VelocityComponent};
5+
use feather_core::world::block::Block;
56
use feather_core::world::ChunkMap;
6-
use nalgebra::Vector3;
77
use rayon::prelude::*;
88
use shrev::EventChannel;
99
use specs::{Entities, ParJoin, Read, ReadStorage, System, Write, WriteStorage};
@@ -33,20 +33,64 @@ impl<'a> System<'a> for EntityPhysicsSystem {
3333
// Go through entities and update position and velocity in parallel.
3434
(&mut entity_comps, &mut velocities, &entities, &types)
3535
.par_join()
36-
.for_each(|(mut entity_comp, mut velocity, entity, ty)| {
37-
let mut new_pos = entity_comp.position.clone();
36+
.for_each(|(entity_comp, velocity, entity, ty)| {
37+
let position = entity_comp.position;
3838
// First, add velocity to position, accounting for blocks
3939
// at the new position.
40-
let pending_new_pos = new_pos + velocity;
41-
if chunk_map.block_at(pending_new_pos.block_pos()).is_some() {
42-
velocity.velocity = Vector3::new(0.0, 0.0, 0.0);
40+
let pending_position = position + velocity.0;
41+
let pending_block = chunk_map.block_at(pending_position.block_pos());
42+
43+
// If block is `None`, the chunk at pending_position isn't
44+
// loading. If so, unload the entity.
45+
if let Some(block) = pending_block {
46+
// Check that block isn't solid.
47+
// If it is, update velocity accordingly.
48+
if block != Block::Air {
49+
let pending_pos_x = position + glm::vec3(velocity.0.x, 0.0, 0.0);
50+
if chunk_map.block_at(pending_pos_x.block_pos()) != Some(Block::Air) {
51+
velocity.0.x = 0.0;
52+
}
53+
54+
let pending_pos_y = position + glm::vec3(velocity.0.y, 0.0, 0.0);
55+
if chunk_map.block_at(pending_pos_y.block_pos()) != Some(Block::Air) {
56+
velocity.0.y = 0.0;
57+
}
58+
59+
let pending_pos_z = position + glm::vec3(velocity.0.z, 0.0, 0.0);
60+
if chunk_map.block_at(pending_pos_z.block_pos()) != Some(Block::Air) {
61+
velocity.0.z = 0.0;
62+
}
63+
} else {
64+
// Depending on the type of entity, apply drag and gravity.
65+
// See https://minecraft.gamepedia.com/Entity for more information.
66+
let gravity = gravitational_acceleration(*ty);
67+
// let terminal = terminal_velocity(*ty);
68+
let drag = drag_force(*ty);
69+
70+
velocity.0.y = drag * velocity.0.y + gravity;
71+
}
72+
73+
// Set new position and trigger event.
74+
let event = EntityMoveEvent {
75+
entity,
76+
old_pos: position,
77+
new_pos: pending_position,
78+
};
79+
80+
tx.send(event).unwrap();
81+
82+
entity_comp.position = pending_position;
4383
} else {
44-
new_pos = pending_new_pos;
84+
// Chunk isn't loaded. Unload entity.
85+
entities.delete(entity).unwrap();
86+
return;
4587
}
46-
47-
// Depending on the type of entity, apply drag and gravity.
48-
// See https://minecraft.gamepedia.com/Entity for more information.
4988
});
89+
90+
// Go through events and trigger them.
91+
while let Ok(event) = rx.try_recv() {
92+
move_events.single_write(event);
93+
}
5094
}
5195
}
5296

@@ -55,35 +99,59 @@ impl<'a> System<'a> for EntityPhysicsSystem {
5599
///
56100
/// This information was fetched from
57101
/// [the Minecraft wiki](https://minecraft.gamepedia.com/Entity#Motion_of_entities).
58-
fn gravitational_acceleration(ty: EntityType) -> f64 {
102+
fn gravitational_acceleration(ty: EntityType) -> f32 {
59103
if ty.is_living() {
60104
-0.08
61105
} else if ty.is_item() {
62106
-0.04
63107
} else if ty.is_other() {
64108
0.0
109+
} else {
110+
0.0
65111
}
66112
}
67113

114+
/*
68115
/// Retrieves the terminal velocity in blocks per tick
69116
/// for a given entity type.
70-
fn terminal_velocity(ty: EntityType) -> f64 {
117+
fn terminal_velocity(ty: EntityType) -> f32 {
71118
if ty.is_living() {
72119
-3.92
73120
} else if ty.is_item() {
74121
-1.96
75122
} else if ty.is_other() {
76123
0.0
124+
} else {
125+
0.0
77126
}
78127
}
128+
*/
79129

80130
/// Retrieves the drag force for a given entity type.
81-
fn drag_force(ty: EntityType) -> f64 {
131+
fn drag_force(ty: EntityType) -> f32 {
82132
if ty.is_living() {
83133
0.98
84134
} else if ty.is_item() {
85135
0.04
86136
} else if ty.is_other() {
87137
0.0
138+
} else {
139+
0.0
140+
}
141+
}
142+
143+
#[cfg(test)]
144+
mod tests {
145+
use super::*;
146+
use crate::testframework as t;
147+
use feather_core::world::Position;
148+
149+
#[test]
150+
fn test_entity_physics_basic() {
151+
let (mut w, mut d) = t::init_world();
152+
153+
let pos = Position::new(0.0, 0.0, 0.0);
154+
let vel = glm::vec3(0.0, 1.0, 0.0);
155+
let entity = t::add_entity_with_pos_and_vel(&mut w, pos, vel);
88156
}
89157
}

0 commit comments

Comments
 (0)