mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Airship progress: now possessable, and physics kind of works (desyncs from the visuals by a shift + scale, and rotation isn't done at all yet, but the contours are correct).
This commit is contained in:
parent
1d0600851b
commit
6d35e7c6d0
@ -1,16 +1,17 @@
|
|||||||
({
|
({
|
||||||
DefaultAirship: (
|
DefaultAirship: (
|
||||||
bone0: (
|
bone0: (
|
||||||
offset: (-20.0, -35.0, 1.0),
|
//offset: (-20.0, -35.0, 1.0),
|
||||||
|
offset: (3.0, 7.0, 1.0),
|
||||||
central: ("object.Human_Airship"),
|
central: ("object.Human_Airship"),
|
||||||
),
|
),
|
||||||
bone1: (
|
bone1: (
|
||||||
offset: (0.0, 0.0, 0.0),
|
offset: (0.0, 40.0, -8.0),
|
||||||
central: ("propeller-l"),
|
central: ("object.propeller-l"),
|
||||||
),
|
),
|
||||||
bone2: (
|
bone2: (
|
||||||
offset: (0.0, 0.0, 0.0),
|
offset: (0.0, 0.0, -4.0),
|
||||||
central: ("propeller-r"),
|
central: ("object.propeller-r"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
BIN
assets/server/voxel/Human_Airship.vox
(Stored with Git LFS)
BIN
assets/server/voxel/Human_Airship.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/object/Human_Airship.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/object/Human_Airship.vox
(Stored with Git LFS)
Binary file not shown.
1
assets/voxygen/voxel/object/Human_Airship.vox
Symbolic link
1
assets/voxygen/voxel/object/Human_Airship.vox
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../../server/voxel/Human_Airship.vox
|
@ -1,6 +1,4 @@
|
|||||||
use crate::{
|
use crate::make_case_elim;
|
||||||
make_case_elim
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
@ -17,22 +15,29 @@ impl From<Body> for super::Body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
pub fn manifest_id(&self) -> &'static str {
|
pub fn manifest_entry(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Body::DefaultAirship => "server.manifests.ship_manifest",
|
Body::DefaultAirship => "object.Human_Airship",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Duplicate of some of the things defined in `voxygen::scene::figure::load` to avoid having to
|
/// Duplicate of some of the things defined in `voxygen::scene::figure::load` to
|
||||||
/// refactor all of that to `common` for using voxels as collider geometry
|
/// avoid having to refactor all of that to `common` for using voxels as
|
||||||
|
/// collider geometry
|
||||||
pub mod figuredata {
|
pub mod figuredata {
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron},
|
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron},
|
||||||
volumes::dyna::Dyna,
|
figure::cell::Cell,
|
||||||
|
terrain::{
|
||||||
|
block::{Block, BlockKind},
|
||||||
|
sprite::SpriteKind,
|
||||||
|
},
|
||||||
|
volumes::dyna::{ColumnAccess, Dyna},
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct VoxSimple(pub String);
|
pub struct VoxSimple(pub String);
|
||||||
@ -57,13 +62,43 @@ pub mod figuredata {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ShipSpec {
|
pub struct ShipSpec {
|
||||||
pub central: AssetHandle<Ron<ShipCentralSpec>>,
|
pub central: AssetHandle<Ron<ShipCentralSpec>>,
|
||||||
|
pub voxes: HashMap<String, Dyna<Block, (), ColumnAccess>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl assets::Compound for ShipSpec {
|
impl assets::Compound for ShipSpec {
|
||||||
fn load<S: assets::source::Source>(_: &assets::AssetCache<S>, _: &str) -> Result<Self, assets::Error> {
|
fn load<S: assets::source::Source>(
|
||||||
|
cache: &assets::AssetCache<S>,
|
||||||
|
_: &str,
|
||||||
|
) -> Result<Self, assets::Error> {
|
||||||
|
let manifest: AssetHandle<Ron<ShipCentralSpec>> = AssetExt::load("server.manifests.ship_manifest")?;
|
||||||
|
let mut voxes = HashMap::new();
|
||||||
|
for (_, spec) in (manifest.read().0).0.iter() {
|
||||||
|
for bone in [&spec.bone0, &spec.bone1, &spec.bone2].iter() {
|
||||||
|
// TODO: avoid the requirement for symlinks in "voxygen.voxel.object.", and load
|
||||||
|
// the models from "server.voxel." instead
|
||||||
|
let vox =
|
||||||
|
cache.load::<DotVoxAsset>(&["voxygen.voxel.", &bone.central.0].concat())?;
|
||||||
|
let dyna = Dyna::<Cell, (), ColumnAccess>::from_vox(&vox.read().0, false);
|
||||||
|
voxes.insert(bone.central.0.clone(), dyna.map_into(|cell| {
|
||||||
|
if let Some(rgb) = cell.get_color() {
|
||||||
|
Block::new(BlockKind::Misc, rgb)
|
||||||
|
} else {
|
||||||
|
Block::air(SpriteKind::Empty)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(ShipSpec {
|
Ok(ShipSpec {
|
||||||
central: AssetExt::load("server.manifests.ship_manifest")?
|
central: manifest,
|
||||||
|
voxes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
// TODO: load this from the ECS as a resource, and maybe make it more general than ships
|
||||||
|
// (although figuring out how to keep the figure bones in sync with the terrain offsets seems
|
||||||
|
// like a hard problem if they're not the same manifest)
|
||||||
|
pub static ref VOXEL_COLLIDER_MANIFEST: ShipSpec = AssetExt::load_expect_cloned("server.manifests.ship_manifest");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ pub enum Collider {
|
|||||||
impl Collider {
|
impl Collider {
|
||||||
pub fn get_radius(&self) -> f32 {
|
pub fn get_radius(&self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Collider::Voxel { .. } => 0.0,
|
Collider::Voxel { .. } => 1.0,
|
||||||
Collider::Box { radius, .. } => *radius,
|
Collider::Box { radius, .. } => *radius,
|
||||||
Collider::Point => 0.0,
|
Collider::Point => 0.0,
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ impl Collider {
|
|||||||
|
|
||||||
pub fn get_z_limits(&self, modifier: f32) -> (f32, f32) {
|
pub fn get_z_limits(&self, modifier: f32) -> (f32, f32) {
|
||||||
match self {
|
match self {
|
||||||
Collider::Voxel { .. } => (0.0, 0.0),
|
Collider::Voxel { .. } => (0.0, 1.0),
|
||||||
Collider::Box { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
Collider::Box { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
||||||
Collider::Point => (0.0, 0.0),
|
Collider::Point => (0.0, 0.0),
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,16 @@ impl<V, M, A: Access> Dyna<V, M, A> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn map_into<W, F: FnMut(V) -> W>(self, f: F) -> Dyna<W, M, A> {
|
||||||
|
let Dyna { vox, meta, sz, _phantom } = self;
|
||||||
|
Dyna {
|
||||||
|
vox: vox.into_iter().map(f).collect(),
|
||||||
|
meta,
|
||||||
|
sz,
|
||||||
|
_phantom,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V, M, A: Access> BaseVol for Dyna<V, M, A> {
|
impl<V, M, A: Access> BaseVol for Dyna<V, M, A> {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
BeamSegment, CharacterState, Collider, Gravity, Mass, Mounting, Ori, PhysicsState, Pos,
|
BeamSegment, CharacterState, Collider, Gravity, Mass, Mounting, Ori, PhysicsState, Pos,
|
||||||
PreviousPhysCache, Projectile, Scale, Shockwave, Sticky, Vel,
|
PreviousPhysCache, Projectile, Scale, Shockwave, Sticky, Vel, body::ship::figuredata::VOXEL_COLLIDER_MANIFEST,
|
||||||
},
|
},
|
||||||
consts::{FRIC_GROUND, GRAVITY},
|
consts::{FRIC_GROUND, GRAVITY},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
@ -15,8 +15,9 @@ use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System};
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use specs::{
|
use specs::{
|
||||||
shred::{World, ResourceId},
|
shred::{ResourceId, World},
|
||||||
Entities, Entity, Join, ParJoin, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage, SystemData,
|
Entities, Entity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, WriteExpect,
|
||||||
|
WriteStorage,
|
||||||
};
|
};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -114,7 +115,9 @@ impl<'a> PhysicsSystemData<'a> {
|
|||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
let _ = self.w.physics_states
|
let _ = self
|
||||||
|
.w
|
||||||
|
.physics_states
|
||||||
.entry(entity)
|
.entry(entity)
|
||||||
.map(|e| e.or_insert_with(Default::default));
|
.map(|e| e.or_insert_with(Default::default));
|
||||||
}
|
}
|
||||||
@ -137,13 +140,16 @@ impl<'a> PhysicsSystemData<'a> {
|
|||||||
.map(|(e, _, _, _, _, _, _)| e)
|
.map(|(e, _, _, _, _, _, _)| e)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
{
|
{
|
||||||
let _ = self.w.previous_phys_cache.insert(entity, PreviousPhysCache {
|
let _ = self
|
||||||
velocity_dt: Vec3::zero(),
|
.w
|
||||||
center: Vec3::zero(),
|
.previous_phys_cache
|
||||||
collision_boundary: 0.0,
|
.insert(entity, PreviousPhysCache {
|
||||||
scale: 0.0,
|
velocity_dt: Vec3::zero(),
|
||||||
scaled_radius: 0.0,
|
center: Vec3::zero(),
|
||||||
});
|
collision_boundary: 0.0,
|
||||||
|
scale: 0.0,
|
||||||
|
scaled_radius: 0.0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update PreviousPhysCache
|
//Update PreviousPhysCache
|
||||||
@ -180,10 +186,14 @@ impl<'a> PhysicsSystemData<'a> {
|
|||||||
}
|
}
|
||||||
drop(guard);
|
drop(guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_pushback(&mut self, job: &mut Job<Sys>) {
|
fn apply_pushback(&mut self, job: &mut Job<Sys>) {
|
||||||
span!(guard, "Apply pushback");
|
span!(guard, "Apply pushback");
|
||||||
job.cpu_stats.measure(ParMode::Rayon);
|
job.cpu_stats.measure(ParMode::Rayon);
|
||||||
let PhysicsSystemData { r: ref psdr, w: ref mut psdw } = self;
|
let PhysicsSystemData {
|
||||||
|
r: ref psdr,
|
||||||
|
w: ref mut psdw,
|
||||||
|
} = self;
|
||||||
let (positions, previous_phys_cache) = (&psdw.positions, &psdw.previous_phys_cache);
|
let (positions, previous_phys_cache) = (&psdw.positions, &psdw.previous_phys_cache);
|
||||||
let metrics = (
|
let metrics = (
|
||||||
&psdr.entities,
|
&psdr.entities,
|
||||||
@ -310,8 +320,13 @@ impl<'a> PhysicsSystemData<'a> {
|
|||||||
metrics.entity_entity_collisions += 1;
|
metrics.entity_entity_collisions += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't apply repulsive force to projectiles
|
// Don't apply repulsive force to projectiles or if we're colliding
|
||||||
if diff.magnitude_squared() > 0.0 && !is_projectile {
|
// with a terrain-like entity, or if we are a terrain-like entity
|
||||||
|
if diff.magnitude_squared() > 0.0
|
||||||
|
&& !is_projectile
|
||||||
|
&& !matches!(collider_other, Some(Collider::Voxel { .. }))
|
||||||
|
&& !matches!(collider, Some(Collider::Voxel { .. }))
|
||||||
|
{
|
||||||
let force =
|
let force =
|
||||||
400.0 * (collision_dist - diff.magnitude()) * mass_other
|
400.0 * (collision_dist - diff.magnitude()) * mass_other
|
||||||
/ (mass + mass_other);
|
/ (mass + mass_other);
|
||||||
@ -335,265 +350,273 @@ impl<'a> PhysicsSystemData<'a> {
|
|||||||
entity_entity_collisions: old.entity_entity_collisions
|
entity_entity_collisions: old.entity_entity_collisions
|
||||||
+ new.entity_entity_collisions,
|
+ new.entity_entity_collisions,
|
||||||
});
|
});
|
||||||
psdw.physics_metrics.entity_entity_collision_checks = metrics.entity_entity_collision_checks;
|
psdw.physics_metrics.entity_entity_collision_checks =
|
||||||
|
metrics.entity_entity_collision_checks;
|
||||||
psdw.physics_metrics.entity_entity_collisions = metrics.entity_entity_collisions;
|
psdw.physics_metrics.entity_entity_collisions = metrics.entity_entity_collisions;
|
||||||
drop(guard);
|
drop(guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_movement_and_terrain(&mut self, job: &mut Job<Sys>) {
|
fn handle_movement_and_terrain(&mut self, job: &mut Job<Sys>) {
|
||||||
let PhysicsSystemData { r: ref psdr, w: ref mut psdw } = self;
|
let PhysicsSystemData {
|
||||||
|
r: ref psdr,
|
||||||
|
w: ref mut psdw,
|
||||||
|
} = self;
|
||||||
// Apply movement inputs
|
// Apply movement inputs
|
||||||
span!(guard, "Apply movement and terrain collision");
|
span!(guard, "Apply movement and terrain collision");
|
||||||
let (positions, previous_phys_cache) = (&psdw.positions, &psdw.previous_phys_cache);
|
let (positions, previous_phys_cache) = (&psdw.positions, &psdw.previous_phys_cache);
|
||||||
let (pos_writes, land_on_grounds) =
|
let (pos_writes, land_on_grounds) = (
|
||||||
(
|
&psdr.entities,
|
||||||
&psdr.entities,
|
psdr.scales.maybe(),
|
||||||
psdr.scales.maybe(),
|
psdr.stickies.maybe(),
|
||||||
psdr.stickies.maybe(),
|
&psdr.colliders,
|
||||||
&psdr.colliders,
|
positions,
|
||||||
positions,
|
&mut psdw.velocities,
|
||||||
&mut psdw.velocities,
|
&psdw.orientations,
|
||||||
&psdw.orientations,
|
&mut psdw.physics_states,
|
||||||
&mut psdw.physics_states,
|
previous_phys_cache,
|
||||||
previous_phys_cache,
|
!&psdr.mountings,
|
||||||
!&psdr.mountings,
|
)
|
||||||
)
|
.par_join()
|
||||||
.par_join()
|
.fold(
|
||||||
.fold(
|
|| (Vec::new(), Vec::new()),
|
||||||
|| (Vec::new(), Vec::new()),
|
|(mut pos_writes, mut land_on_grounds),
|
||||||
|(mut pos_writes, mut land_on_grounds),
|
(
|
||||||
(
|
entity,
|
||||||
entity,
|
scale,
|
||||||
scale,
|
sticky,
|
||||||
sticky,
|
collider,
|
||||||
collider,
|
pos,
|
||||||
pos,
|
mut vel,
|
||||||
mut vel,
|
_ori,
|
||||||
_ori,
|
mut physics_state,
|
||||||
mut physics_state,
|
previous_cache,
|
||||||
previous_cache,
|
_,
|
||||||
_,
|
)| {
|
||||||
)| {
|
// defer the writes of positions to allow an inner loop over terrain-like
|
||||||
// defer the writes of positions to allow an inner loop over terrain-like
|
// entities
|
||||||
// entities
|
let old_pos = *pos;
|
||||||
let old_pos = *pos;
|
let mut pos = *pos;
|
||||||
let mut pos = *pos;
|
if sticky.is_some() && physics_state.on_surface().is_some() {
|
||||||
if sticky.is_some() && physics_state.on_surface().is_some() {
|
vel.0 = Vec3::zero();
|
||||||
vel.0 = Vec3::zero();
|
return (pos_writes, land_on_grounds);
|
||||||
return (pos_writes, land_on_grounds);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let scale = if let Collider::Voxel { .. } = collider {
|
let scale = if let Collider::Voxel { .. } = collider {
|
||||||
scale.map(|s| s.0).unwrap_or(1.0)
|
scale.map(|s| s.0).unwrap_or(1.0)
|
||||||
|
} else {
|
||||||
|
// TODO: Use scale & actual proportions when pathfinding is good
|
||||||
|
// enough to manage irregular entity sizes
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
|
||||||
|
let old_vel = *vel;
|
||||||
|
// Integrate forces
|
||||||
|
// Friction is assumed to be a constant dependent on location
|
||||||
|
let friction = FRIC_AIR
|
||||||
|
.max(if physics_state.on_ground {
|
||||||
|
FRIC_GROUND
|
||||||
} else {
|
} else {
|
||||||
// TODO: Use scale & actual proportions when pathfinding is good
|
0.0
|
||||||
// enough to manage irregular entity sizes
|
})
|
||||||
1.0
|
.max(if physics_state.in_liquid.is_some() {
|
||||||
};
|
FRIC_FLUID
|
||||||
|
|
||||||
let old_vel = *vel;
|
|
||||||
// Integrate forces
|
|
||||||
// Friction is assumed to be a constant dependent on location
|
|
||||||
let friction = FRIC_AIR
|
|
||||||
.max(if physics_state.on_ground {
|
|
||||||
FRIC_GROUND
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
})
|
|
||||||
.max(if physics_state.in_liquid.is_some() {
|
|
||||||
FRIC_FLUID
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
});
|
|
||||||
let in_loaded_chunk = psdr.terrain
|
|
||||||
.get_key(psdr.terrain.pos_key(pos.0.map(|e| e.floor() as i32)))
|
|
||||||
.is_some();
|
|
||||||
let downward_force =
|
|
||||||
if !in_loaded_chunk {
|
|
||||||
0.0 // No gravity in unloaded chunks
|
|
||||||
} else if physics_state
|
|
||||||
.in_liquid
|
|
||||||
.map(|depth| depth > 0.75)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
(1.0 - BOUYANCY) * GRAVITY
|
|
||||||
} else {
|
|
||||||
GRAVITY
|
|
||||||
} * psdr.gravities.get(entity).map(|g| g.0).unwrap_or_default();
|
|
||||||
vel.0 = integrate_forces(psdr.dt.0, vel.0, downward_force, friction);
|
|
||||||
|
|
||||||
// Don't move if we're not in a loaded chunk
|
|
||||||
let pos_delta = if in_loaded_chunk {
|
|
||||||
// this is an approximation that allows most framerates to
|
|
||||||
// behave in a similar manner.
|
|
||||||
let dt_lerp = 0.2;
|
|
||||||
(vel.0 * dt_lerp + old_vel.0 * (1.0 - dt_lerp)) * psdr.dt.0
|
|
||||||
} else {
|
} else {
|
||||||
Vec3::zero()
|
0.0
|
||||||
};
|
});
|
||||||
|
let in_loaded_chunk = psdr
|
||||||
match &*collider {
|
.terrain
|
||||||
Collider::Voxel { .. } => {
|
.get_key(psdr.terrain.pos_key(pos.0.map(|e| e.floor() as i32)))
|
||||||
// for now, treat entities with voxel colliders as their bounding
|
.is_some();
|
||||||
// cylinders for the purposes of colliding them with terrain
|
let downward_force =
|
||||||
let radius = collider.get_radius() * scale;
|
if !in_loaded_chunk {
|
||||||
let (z_min, z_max) = collider.get_z_limits(scale);
|
0.0 // No gravity in unloaded chunks
|
||||||
|
} else if physics_state
|
||||||
let cylinder = (radius, z_min, z_max);
|
.in_liquid
|
||||||
cylinder_voxel_collision(
|
.map(|depth| depth > 0.75)
|
||||||
cylinder,
|
.unwrap_or(false)
|
||||||
&*psdr.terrain,
|
|
||||||
entity,
|
|
||||||
&mut pos,
|
|
||||||
pos_delta,
|
|
||||||
vel,
|
|
||||||
&mut physics_state,
|
|
||||||
&mut land_on_grounds,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
Collider::Box {
|
|
||||||
radius,
|
|
||||||
z_min,
|
|
||||||
z_max,
|
|
||||||
} => {
|
|
||||||
// Scale collider
|
|
||||||
let radius = radius.min(0.45) * scale;
|
|
||||||
let z_min = *z_min * scale;
|
|
||||||
let z_max = z_max.clamped(1.2, 1.95) * scale;
|
|
||||||
|
|
||||||
let cylinder = (radius, z_min, z_max);
|
|
||||||
cylinder_voxel_collision(
|
|
||||||
cylinder,
|
|
||||||
&*psdr.terrain,
|
|
||||||
entity,
|
|
||||||
&mut pos,
|
|
||||||
pos_delta,
|
|
||||||
vel,
|
|
||||||
&mut physics_state,
|
|
||||||
&mut land_on_grounds,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
Collider::Point => {
|
|
||||||
let (dist, block) = psdr.terrain
|
|
||||||
.ray(pos.0, pos.0 + pos_delta)
|
|
||||||
.until(|block: &Block| block.is_filled())
|
|
||||||
.ignore_error()
|
|
||||||
.cast();
|
|
||||||
|
|
||||||
pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist;
|
|
||||||
|
|
||||||
// Can't fail since we do ignore_error above
|
|
||||||
if block.unwrap().is_some() {
|
|
||||||
let block_center = pos.0.map(|e| e.floor()) + 0.5;
|
|
||||||
let block_rpos = (pos.0 - block_center)
|
|
||||||
.try_normalized()
|
|
||||||
.unwrap_or(Vec3::zero());
|
|
||||||
|
|
||||||
// See whether we're on the top/bottom of a block, or the side
|
|
||||||
if block_rpos.z.abs()
|
|
||||||
> block_rpos.xy().map(|e| e.abs()).reduce_partial_max()
|
|
||||||
{
|
|
||||||
if block_rpos.z > 0.0 {
|
|
||||||
physics_state.on_ground = true;
|
|
||||||
} else {
|
|
||||||
physics_state.on_ceiling = true;
|
|
||||||
}
|
|
||||||
vel.0.z = 0.0;
|
|
||||||
} else {
|
|
||||||
physics_state.on_wall =
|
|
||||||
Some(if block_rpos.x.abs() > block_rpos.y.abs() {
|
|
||||||
vel.0.x = 0.0;
|
|
||||||
Vec3::unit_x() * -block_rpos.x.signum()
|
|
||||||
} else {
|
|
||||||
vel.0.y = 0.0;
|
|
||||||
Vec3::unit_y() * -block_rpos.y.signum()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
physics_state.in_liquid = psdr.terrain
|
|
||||||
.get(pos.0.map(|e| e.floor() as i32))
|
|
||||||
.ok()
|
|
||||||
.and_then(|vox| vox.is_liquid().then_some(1.0));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collide with terrain-like entities
|
|
||||||
for (
|
|
||||||
entity_other,
|
|
||||||
other,
|
|
||||||
pos_other,
|
|
||||||
previous_cache_other,
|
|
||||||
mass_other,
|
|
||||||
collider_other,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
char_state_other_maybe,
|
|
||||||
) in (
|
|
||||||
&psdr.entities,
|
|
||||||
&psdr.uids,
|
|
||||||
positions,
|
|
||||||
previous_phys_cache,
|
|
||||||
psdr.masses.maybe(),
|
|
||||||
&psdr.colliders,
|
|
||||||
!&psdr.projectiles,
|
|
||||||
!&psdr.mountings,
|
|
||||||
!&psdr.beams,
|
|
||||||
!&psdr.shockwaves,
|
|
||||||
psdr.char_states.maybe(),
|
|
||||||
)
|
|
||||||
.join()
|
|
||||||
{
|
{
|
||||||
let collision_boundary = previous_cache.collision_boundary
|
(1.0 - BOUYANCY) * GRAVITY
|
||||||
+ previous_cache_other.collision_boundary;
|
} else {
|
||||||
if previous_cache
|
GRAVITY
|
||||||
.center
|
} * psdr.gravities.get(entity).map(|g| g.0).unwrap_or_default();
|
||||||
.distance_squared(previous_cache_other.center)
|
vel.0 = integrate_forces(psdr.dt.0, vel.0, downward_force, friction);
|
||||||
> collision_boundary.powi(2)
|
|
||||||
|| entity == entity_other
|
// Don't move if we're not in a loaded chunk
|
||||||
{
|
let pos_delta = if in_loaded_chunk {
|
||||||
continue;
|
// this is an approximation that allows most framerates to
|
||||||
|
// behave in a similar manner.
|
||||||
|
let dt_lerp = 0.2;
|
||||||
|
(vel.0 * dt_lerp + old_vel.0 * (1.0 - dt_lerp)) * psdr.dt.0
|
||||||
|
} else {
|
||||||
|
Vec3::zero()
|
||||||
|
};
|
||||||
|
|
||||||
|
match &*collider {
|
||||||
|
Collider::Voxel { .. } => {
|
||||||
|
// for now, treat entities with voxel colliders as their bounding
|
||||||
|
// cylinders for the purposes of colliding them with terrain
|
||||||
|
let radius = collider.get_radius() * scale;
|
||||||
|
let (z_min, z_max) = collider.get_z_limits(scale);
|
||||||
|
|
||||||
|
let cylinder = (radius, z_min, z_max);
|
||||||
|
cylinder_voxel_collision(
|
||||||
|
cylinder,
|
||||||
|
&*psdr.terrain,
|
||||||
|
entity,
|
||||||
|
&mut pos,
|
||||||
|
pos_delta,
|
||||||
|
vel,
|
||||||
|
&mut physics_state,
|
||||||
|
&mut land_on_grounds,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Collider::Box {
|
||||||
|
radius,
|
||||||
|
z_min,
|
||||||
|
z_max,
|
||||||
|
} => {
|
||||||
|
// Scale collider
|
||||||
|
let radius = radius.min(0.45) * scale;
|
||||||
|
let z_min = *z_min * scale;
|
||||||
|
let z_max = z_max.clamped(1.2, 1.95) * scale;
|
||||||
|
|
||||||
|
let cylinder = (radius, z_min, z_max);
|
||||||
|
cylinder_voxel_collision(
|
||||||
|
cylinder,
|
||||||
|
&*psdr.terrain,
|
||||||
|
entity,
|
||||||
|
&mut pos,
|
||||||
|
pos_delta,
|
||||||
|
vel,
|
||||||
|
&mut physics_state,
|
||||||
|
&mut land_on_grounds,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Collider::Point => {
|
||||||
|
let (dist, block) = psdr
|
||||||
|
.terrain
|
||||||
|
.ray(pos.0, pos.0 + pos_delta)
|
||||||
|
.until(|block: &Block| block.is_filled())
|
||||||
|
.ignore_error()
|
||||||
|
.cast();
|
||||||
|
|
||||||
|
pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist;
|
||||||
|
|
||||||
|
// Can't fail since we do ignore_error above
|
||||||
|
if block.unwrap().is_some() {
|
||||||
|
let block_center = pos.0.map(|e| e.floor()) + 0.5;
|
||||||
|
let block_rpos = (pos.0 - block_center)
|
||||||
|
.try_normalized()
|
||||||
|
.unwrap_or(Vec3::zero());
|
||||||
|
|
||||||
|
// See whether we're on the top/bottom of a block, or the side
|
||||||
|
if block_rpos.z.abs()
|
||||||
|
> block_rpos.xy().map(|e| e.abs()).reduce_partial_max()
|
||||||
|
{
|
||||||
|
if block_rpos.z > 0.0 {
|
||||||
|
physics_state.on_ground = true;
|
||||||
|
} else {
|
||||||
|
physics_state.on_ceiling = true;
|
||||||
|
}
|
||||||
|
vel.0.z = 0.0;
|
||||||
|
} else {
|
||||||
|
physics_state.on_wall =
|
||||||
|
Some(if block_rpos.x.abs() > block_rpos.y.abs() {
|
||||||
|
vel.0.x = 0.0;
|
||||||
|
Vec3::unit_x() * -block_rpos.x.signum()
|
||||||
|
} else {
|
||||||
|
vel.0.y = 0.0;
|
||||||
|
Vec3::unit_y() * -block_rpos.y.signum()
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Collider::Voxel { id } = collider_other {
|
physics_state.in_liquid = psdr
|
||||||
// use bounding cylinder regardless of our collider
|
.terrain
|
||||||
// TODO: extract point-terrain collision above to its own function
|
.get(pos.0.map(|e| e.floor() as i32))
|
||||||
let radius = collider.get_radius() * scale;
|
.ok()
|
||||||
let (z_min, z_max) = collider.get_z_limits(scale);
|
.and_then(|vox| vox.is_liquid().then_some(1.0));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
let cylinder = (radius, z_min, z_max);
|
// Collide with terrain-like entities
|
||||||
// TODO: load .vox into a Dyna, and use it (appropriately rotated)
|
for (
|
||||||
// as the terrain
|
entity_other,
|
||||||
/*cylinder_voxel_collision(
|
other,
|
||||||
|
pos_other,
|
||||||
|
previous_cache_other,
|
||||||
|
mass_other,
|
||||||
|
collider_other,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
char_state_other_maybe,
|
||||||
|
) in (
|
||||||
|
&psdr.entities,
|
||||||
|
&psdr.uids,
|
||||||
|
positions,
|
||||||
|
previous_phys_cache,
|
||||||
|
psdr.masses.maybe(),
|
||||||
|
&psdr.colliders,
|
||||||
|
!&psdr.projectiles,
|
||||||
|
!&psdr.mountings,
|
||||||
|
!&psdr.beams,
|
||||||
|
!&psdr.shockwaves,
|
||||||
|
psdr.char_states.maybe(),
|
||||||
|
)
|
||||||
|
.join()
|
||||||
|
{
|
||||||
|
/*let collision_boundary = previous_cache.collision_boundary
|
||||||
|
+ previous_cache_other.collision_boundary;
|
||||||
|
if previous_cache
|
||||||
|
.center
|
||||||
|
.distance_squared(previous_cache_other.center)
|
||||||
|
> collision_boundary.powi(2)
|
||||||
|
|| entity == entity_other
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if let Collider::Voxel { id } = collider_other {
|
||||||
|
// use bounding cylinder regardless of our collider
|
||||||
|
// TODO: extract point-terrain collision above to its own function
|
||||||
|
let radius = collider.get_radius() * scale;
|
||||||
|
let (z_min, z_max) = collider.get_z_limits(scale);
|
||||||
|
|
||||||
|
pos.0 -= pos_other.0;
|
||||||
|
let cylinder = (radius, z_min, z_max);
|
||||||
|
if let Some(dyna) = VOXEL_COLLIDER_MANIFEST.voxes.get(id) {
|
||||||
|
cylinder_voxel_collision(
|
||||||
cylinder,
|
cylinder,
|
||||||
&*psdr.terrain,
|
&*dyna,
|
||||||
entity,
|
entity,
|
||||||
&mut pos,
|
&mut pos,
|
||||||
pos_delta,
|
pos_delta,
|
||||||
vel,
|
vel,
|
||||||
&mut physics_state,
|
&mut physics_state,
|
||||||
&mut land_on_grounds,
|
&mut land_on_grounds,
|
||||||
);*/
|
);
|
||||||
}
|
}
|
||||||
|
pos.0 += pos_other.0;
|
||||||
}
|
}
|
||||||
if pos != old_pos {
|
}
|
||||||
pos_writes.push((entity, pos));
|
if pos != old_pos {
|
||||||
}
|
pos_writes.push((entity, pos));
|
||||||
|
}
|
||||||
|
|
||||||
(pos_writes, land_on_grounds)
|
(pos_writes, land_on_grounds)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.reduce(
|
.reduce(
|
||||||
|| (Vec::new(), Vec::new()),
|
|| (Vec::new(), Vec::new()),
|
||||||
|(mut pos_writes_a, mut land_on_grounds_a),
|
|(mut pos_writes_a, mut land_on_grounds_a),
|
||||||
(mut pos_writes_b, mut land_on_grounds_b)| {
|
(mut pos_writes_b, mut land_on_grounds_b)| {
|
||||||
pos_writes_a.append(&mut pos_writes_b);
|
pos_writes_a.append(&mut pos_writes_b);
|
||||||
land_on_grounds_a.append(&mut land_on_grounds_b);
|
land_on_grounds_a.append(&mut land_on_grounds_b);
|
||||||
(pos_writes_a, land_on_grounds_a)
|
(pos_writes_a, land_on_grounds_a)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
drop(guard);
|
drop(guard);
|
||||||
job.cpu_stats.measure(ParMode::Single);
|
job.cpu_stats.measure(ParMode::Single);
|
||||||
|
|
||||||
@ -620,10 +643,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||||
#[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587
|
#[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587
|
||||||
fn run(
|
fn run(job: &mut Job<Self>, mut psd: Self::SystemData) {
|
||||||
job: &mut Job<Self>,
|
|
||||||
mut psd: Self::SystemData,
|
|
||||||
) {
|
|
||||||
psd.reset();
|
psd.reset();
|
||||||
|
|
||||||
// Apply pushback
|
// Apply pushback
|
||||||
@ -642,7 +662,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
psd.maintain_pushback_cache();
|
psd.maintain_pushback_cache();
|
||||||
psd.apply_pushback(job);
|
psd.apply_pushback(job);
|
||||||
|
|
||||||
|
|
||||||
psd.handle_movement_and_terrain(job);
|
psd.handle_movement_and_terrain(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -966,11 +966,12 @@ fn handle_spawn_airship(
|
|||||||
_action: &ChatCommand,
|
_action: &ChatCommand,
|
||||||
) {
|
) {
|
||||||
match server.state.read_component_copied::<comp::Pos>(target) {
|
match server.state.read_component_copied::<comp::Pos>(target) {
|
||||||
Some(pos) => {
|
Some(mut pos) => {
|
||||||
|
pos.0.z += 50.0;
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.create_ship(pos, comp::ship::Body::DefaultAirship)
|
.create_ship(pos, comp::ship::Body::DefaultAirship, 1)
|
||||||
.with(comp::Scale(50.0))
|
.with(comp::Scale(11.0))
|
||||||
.with(LightEmitter {
|
.with(LightEmitter {
|
||||||
col: Rgb::new(1.0, 0.65, 0.2),
|
col: Rgb::new(1.0, 0.65, 0.2),
|
||||||
strength: 2.0,
|
strength: 2.0,
|
||||||
|
@ -41,7 +41,7 @@ pub trait StateExt {
|
|||||||
) -> EcsEntityBuilder;
|
) -> EcsEntityBuilder;
|
||||||
/// Build a static object entity
|
/// Build a static object entity
|
||||||
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder;
|
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder;
|
||||||
fn create_ship(&mut self, pos: comp::Pos, object: comp::ship::Body) -> EcsEntityBuilder;
|
fn create_ship(&mut self, pos: comp::Pos, object: comp::ship::Body, level: u16) -> EcsEntityBuilder;
|
||||||
/// Build a projectile
|
/// Build a projectile
|
||||||
fn create_projectile(
|
fn create_projectile(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -203,16 +203,24 @@ impl StateExt for State {
|
|||||||
.with(comp::Gravity(1.0))
|
.with(comp::Gravity(1.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_ship(&mut self, pos: comp::Pos, object: comp::ship::Body) -> EcsEntityBuilder {
|
fn create_ship(&mut self, pos: comp::Pos, ship: comp::ship::Body, level: u16) -> EcsEntityBuilder {
|
||||||
self.ecs_mut()
|
self.ecs_mut()
|
||||||
.create_entity_synced()
|
.create_entity_synced()
|
||||||
.with(pos)
|
.with(pos)
|
||||||
.with(comp::Vel(Vec3::zero()))
|
.with(comp::Vel(Vec3::zero()))
|
||||||
.with(comp::Ori::default())
|
.with(comp::Ori::default())
|
||||||
.with(comp::Mass(50.0))
|
.with(comp::Mass(50.0))
|
||||||
.with(comp::Collider::Voxel { id: object.manifest_id().to_string() })
|
.with(comp::Collider::Voxel { id: ship.manifest_entry().to_string() })
|
||||||
.with(comp::Body::Ship(object))
|
.with(comp::Body::Ship(ship))
|
||||||
.with(comp::Gravity(1.0))
|
.with(comp::Gravity(1.0))
|
||||||
|
.with(comp::Controller::default())
|
||||||
|
.with(comp::inventory::Inventory::new_empty())
|
||||||
|
.with(comp::CharacterState::default())
|
||||||
|
.with(comp::Energy::new(ship.into(), level))
|
||||||
|
.with(comp::Health::new(ship.into(), level))
|
||||||
|
.with(comp::Stats::new("Airship".to_string()))
|
||||||
|
.with(comp::Buffs::default())
|
||||||
|
.with(comp::Combo::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_projectile(
|
fn create_projectile(
|
||||||
|
@ -4120,7 +4120,7 @@ impl FigureMgr {
|
|||||||
.join()
|
.join()
|
||||||
// Don't render dead entities
|
// Don't render dead entities
|
||||||
.filter(|(_, _, _, _, health, _, _)| health.map_or(true, |h| !h.is_dead))
|
.filter(|(_, _, _, _, health, _, _)| health.map_or(true, |h| !h.is_dead))
|
||||||
.for_each(|(entity, pos, _, body, _, inventory, _)| {
|
.for_each(|(entity, pos, _, body, _, inventory, scale)| {
|
||||||
if let Some((locals, bone_consts, model, _)) = self.get_model_for_render(
|
if let Some((locals, bone_consts, model, _)) = self.get_model_for_render(
|
||||||
tick,
|
tick,
|
||||||
camera,
|
camera,
|
||||||
@ -4130,7 +4130,7 @@ impl FigureMgr {
|
|||||||
inventory,
|
inventory,
|
||||||
false,
|
false,
|
||||||
pos.0,
|
pos.0,
|
||||||
figure_lod_render_distance,
|
figure_lod_render_distance * scale.map_or(1.0, |s| s.0),
|
||||||
|state| state.can_shadow_sun(),
|
|state| state.can_shadow_sun(),
|
||||||
) {
|
) {
|
||||||
renderer.render_figure_shadow_directed(
|
renderer.render_figure_shadow_directed(
|
||||||
@ -4162,7 +4162,7 @@ impl FigureMgr {
|
|||||||
let character_state_storage = state.read_storage::<common::comp::CharacterState>();
|
let character_state_storage = state.read_storage::<common::comp::CharacterState>();
|
||||||
let character_state = character_state_storage.get(player_entity);
|
let character_state = character_state_storage.get(player_entity);
|
||||||
|
|
||||||
for (entity, pos, _, body, _, inventory, _) in (
|
for (entity, pos, _, body, _, inventory, scale) in (
|
||||||
&ecs.entities(),
|
&ecs.entities(),
|
||||||
&ecs.read_storage::<Pos>(),
|
&ecs.read_storage::<Pos>(),
|
||||||
ecs.read_storage::<Ori>().maybe(),
|
ecs.read_storage::<Ori>().maybe(),
|
||||||
@ -4187,7 +4187,7 @@ impl FigureMgr {
|
|||||||
inventory,
|
inventory,
|
||||||
false,
|
false,
|
||||||
pos.0,
|
pos.0,
|
||||||
figure_lod_render_distance,
|
figure_lod_render_distance * scale.map_or(1.0, |s| s.0),
|
||||||
|state| state.visible(),
|
|state| state.visible(),
|
||||||
) {
|
) {
|
||||||
renderer.render_figure(model, &col_lights, global, locals, bone_consts, lod);
|
renderer.render_figure(model, &col_lights, global, locals, bone_consts, lod);
|
||||||
|
Loading…
Reference in New Issue
Block a user