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: (
|
||||
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"),
|
||||
),
|
||||
bone1: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
central: ("propeller-l"),
|
||||
offset: (0.0, 40.0, -8.0),
|
||||
central: ("object.propeller-l"),
|
||||
),
|
||||
bone2: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
central: ("propeller-r"),
|
||||
offset: (0.0, 0.0, -4.0),
|
||||
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::{
|
||||
make_case_elim
|
||||
};
|
||||
use crate::make_case_elim;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
make_case_elim!(
|
||||
@ -17,22 +15,29 @@ impl From<Body> for super::Body {
|
||||
}
|
||||
|
||||
impl Body {
|
||||
pub fn manifest_id(&self) -> &'static str {
|
||||
pub fn manifest_entry(&self) -> &'static str {
|
||||
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
|
||||
/// refactor all of that to `common` for using voxels as collider geometry
|
||||
/// Duplicate of some of the things defined in `voxygen::scene::figure::load` to
|
||||
/// avoid having to refactor all of that to `common` for using voxels as
|
||||
/// collider geometry
|
||||
pub mod figuredata {
|
||||
use crate::{
|
||||
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 lazy_static::lazy_static;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct VoxSimple(pub String);
|
||||
@ -57,13 +62,43 @@ pub mod figuredata {
|
||||
#[derive(Clone)]
|
||||
pub struct ShipSpec {
|
||||
pub central: AssetHandle<Ron<ShipCentralSpec>>,
|
||||
pub voxes: HashMap<String, Dyna<Block, (), ColumnAccess>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
pub fn get_radius(&self) -> f32 {
|
||||
match self {
|
||||
Collider::Voxel { .. } => 0.0,
|
||||
Collider::Voxel { .. } => 1.0,
|
||||
Collider::Box { radius, .. } => *radius,
|
||||
Collider::Point => 0.0,
|
||||
}
|
||||
@ -74,7 +74,7 @@ impl Collider {
|
||||
|
||||
pub fn get_z_limits(&self, modifier: f32) -> (f32, f32) {
|
||||
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::Point => (0.0, 0.0),
|
||||
}
|
||||
|
@ -44,6 +44,16 @@ impl<V, M, A: Access> Dyna<V, M, A> {
|
||||
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> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use common::{
|
||||
comp::{
|
||||
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},
|
||||
event::{EventBus, ServerEvent},
|
||||
@ -15,8 +15,9 @@ use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System};
|
||||
use hashbrown::HashMap;
|
||||
use rayon::iter::ParallelIterator;
|
||||
use specs::{
|
||||
shred::{World, ResourceId},
|
||||
Entities, Entity, Join, ParJoin, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage, SystemData,
|
||||
shred::{ResourceId, World},
|
||||
Entities, Entity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, WriteExpect,
|
||||
WriteStorage,
|
||||
};
|
||||
use std::ops::Range;
|
||||
use vek::*;
|
||||
@ -114,7 +115,9 @@ impl<'a> PhysicsSystemData<'a> {
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let _ = self.w.physics_states
|
||||
let _ = self
|
||||
.w
|
||||
.physics_states
|
||||
.entry(entity)
|
||||
.map(|e| e.or_insert_with(Default::default));
|
||||
}
|
||||
@ -137,13 +140,16 @@ impl<'a> PhysicsSystemData<'a> {
|
||||
.map(|(e, _, _, _, _, _, _)| e)
|
||||
.collect::<Vec<_>>()
|
||||
{
|
||||
let _ = self.w.previous_phys_cache.insert(entity, PreviousPhysCache {
|
||||
velocity_dt: Vec3::zero(),
|
||||
center: Vec3::zero(),
|
||||
collision_boundary: 0.0,
|
||||
scale: 0.0,
|
||||
scaled_radius: 0.0,
|
||||
});
|
||||
let _ = self
|
||||
.w
|
||||
.previous_phys_cache
|
||||
.insert(entity, PreviousPhysCache {
|
||||
velocity_dt: Vec3::zero(),
|
||||
center: Vec3::zero(),
|
||||
collision_boundary: 0.0,
|
||||
scale: 0.0,
|
||||
scaled_radius: 0.0,
|
||||
});
|
||||
}
|
||||
|
||||
//Update PreviousPhysCache
|
||||
@ -180,10 +186,14 @@ impl<'a> PhysicsSystemData<'a> {
|
||||
}
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
fn apply_pushback(&mut self, job: &mut Job<Sys>) {
|
||||
span!(guard, "Apply pushback");
|
||||
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 metrics = (
|
||||
&psdr.entities,
|
||||
@ -310,8 +320,13 @@ impl<'a> PhysicsSystemData<'a> {
|
||||
metrics.entity_entity_collisions += 1;
|
||||
}
|
||||
|
||||
// Don't apply repulsive force to projectiles
|
||||
if diff.magnitude_squared() > 0.0 && !is_projectile {
|
||||
// Don't apply repulsive force to projectiles or if we're colliding
|
||||
// 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 =
|
||||
400.0 * (collision_dist - diff.magnitude()) * mass_other
|
||||
/ (mass + mass_other);
|
||||
@ -335,265 +350,273 @@ impl<'a> PhysicsSystemData<'a> {
|
||||
entity_entity_collisions: old.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;
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
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
|
||||
span!(guard, "Apply movement and terrain collision");
|
||||
let (positions, previous_phys_cache) = (&psdw.positions, &psdw.previous_phys_cache);
|
||||
let (pos_writes, land_on_grounds) =
|
||||
(
|
||||
&psdr.entities,
|
||||
psdr.scales.maybe(),
|
||||
psdr.stickies.maybe(),
|
||||
&psdr.colliders,
|
||||
positions,
|
||||
&mut psdw.velocities,
|
||||
&psdw.orientations,
|
||||
&mut psdw.physics_states,
|
||||
previous_phys_cache,
|
||||
!&psdr.mountings,
|
||||
)
|
||||
.par_join()
|
||||
.fold(
|
||||
|| (Vec::new(), Vec::new()),
|
||||
|(mut pos_writes, mut land_on_grounds),
|
||||
(
|
||||
entity,
|
||||
scale,
|
||||
sticky,
|
||||
collider,
|
||||
pos,
|
||||
mut vel,
|
||||
_ori,
|
||||
mut physics_state,
|
||||
previous_cache,
|
||||
_,
|
||||
)| {
|
||||
// defer the writes of positions to allow an inner loop over terrain-like
|
||||
// entities
|
||||
let old_pos = *pos;
|
||||
let mut pos = *pos;
|
||||
if sticky.is_some() && physics_state.on_surface().is_some() {
|
||||
vel.0 = Vec3::zero();
|
||||
return (pos_writes, land_on_grounds);
|
||||
}
|
||||
let (pos_writes, land_on_grounds) = (
|
||||
&psdr.entities,
|
||||
psdr.scales.maybe(),
|
||||
psdr.stickies.maybe(),
|
||||
&psdr.colliders,
|
||||
positions,
|
||||
&mut psdw.velocities,
|
||||
&psdw.orientations,
|
||||
&mut psdw.physics_states,
|
||||
previous_phys_cache,
|
||||
!&psdr.mountings,
|
||||
)
|
||||
.par_join()
|
||||
.fold(
|
||||
|| (Vec::new(), Vec::new()),
|
||||
|(mut pos_writes, mut land_on_grounds),
|
||||
(
|
||||
entity,
|
||||
scale,
|
||||
sticky,
|
||||
collider,
|
||||
pos,
|
||||
mut vel,
|
||||
_ori,
|
||||
mut physics_state,
|
||||
previous_cache,
|
||||
_,
|
||||
)| {
|
||||
// defer the writes of positions to allow an inner loop over terrain-like
|
||||
// entities
|
||||
let old_pos = *pos;
|
||||
let mut pos = *pos;
|
||||
if sticky.is_some() && physics_state.on_surface().is_some() {
|
||||
vel.0 = Vec3::zero();
|
||||
return (pos_writes, land_on_grounds);
|
||||
}
|
||||
|
||||
let scale = if let Collider::Voxel { .. } = collider {
|
||||
scale.map(|s| s.0).unwrap_or(1.0)
|
||||
let scale = if let Collider::Voxel { .. } = collider {
|
||||
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 {
|
||||
// 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 {
|
||||
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
|
||||
0.0
|
||||
})
|
||||
.max(if physics_state.in_liquid.is_some() {
|
||||
FRIC_FLUID
|
||||
} 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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
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)
|
||||
{
|
||||
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;
|
||||
(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 {
|
||||
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 {
|
||||
// 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);
|
||||
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));
|
||||
},
|
||||
}
|
||||
|
||||
let cylinder = (radius, z_min, z_max);
|
||||
// TODO: load .vox into a Dyna, and use it (appropriately rotated)
|
||||
// as the terrain
|
||||
/*cylinder_voxel_collision(
|
||||
// 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
|
||||
+ 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,
|
||||
&*psdr.terrain,
|
||||
&*dyna,
|
||||
entity,
|
||||
&mut pos,
|
||||
pos_delta,
|
||||
vel,
|
||||
&mut physics_state,
|
||||
&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)
|
||||
},
|
||||
)
|
||||
.reduce(
|
||||
|| (Vec::new(), Vec::new()),
|
||||
|(mut pos_writes_a, mut land_on_grounds_a),
|
||||
(mut pos_writes_b, mut land_on_grounds_b)| {
|
||||
pos_writes_a.append(&mut pos_writes_b);
|
||||
land_on_grounds_a.append(&mut land_on_grounds_b);
|
||||
(pos_writes_a, land_on_grounds_a)
|
||||
},
|
||||
);
|
||||
(pos_writes, land_on_grounds)
|
||||
},
|
||||
)
|
||||
.reduce(
|
||||
|| (Vec::new(), Vec::new()),
|
||||
|(mut pos_writes_a, mut land_on_grounds_a),
|
||||
(mut pos_writes_b, mut land_on_grounds_b)| {
|
||||
pos_writes_a.append(&mut pos_writes_b);
|
||||
land_on_grounds_a.append(&mut land_on_grounds_b);
|
||||
(pos_writes_a, land_on_grounds_a)
|
||||
},
|
||||
);
|
||||
drop(guard);
|
||||
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::blocks_in_if_conditions)] // TODO: Pending review in #587
|
||||
fn run(
|
||||
job: &mut Job<Self>,
|
||||
mut psd: Self::SystemData,
|
||||
) {
|
||||
fn run(job: &mut Job<Self>, mut psd: Self::SystemData) {
|
||||
psd.reset();
|
||||
|
||||
// Apply pushback
|
||||
@ -642,7 +662,6 @@ impl<'a> System<'a> for Sys {
|
||||
psd.maintain_pushback_cache();
|
||||
psd.apply_pushback(job);
|
||||
|
||||
|
||||
psd.handle_movement_and_terrain(job);
|
||||
}
|
||||
}
|
||||
|
@ -966,11 +966,12 @@ fn handle_spawn_airship(
|
||||
_action: &ChatCommand,
|
||||
) {
|
||||
match server.state.read_component_copied::<comp::Pos>(target) {
|
||||
Some(pos) => {
|
||||
Some(mut pos) => {
|
||||
pos.0.z += 50.0;
|
||||
server
|
||||
.state
|
||||
.create_ship(pos, comp::ship::Body::DefaultAirship)
|
||||
.with(comp::Scale(50.0))
|
||||
.create_ship(pos, comp::ship::Body::DefaultAirship, 1)
|
||||
.with(comp::Scale(11.0))
|
||||
.with(LightEmitter {
|
||||
col: Rgb::new(1.0, 0.65, 0.2),
|
||||
strength: 2.0,
|
||||
|
@ -41,7 +41,7 @@ pub trait StateExt {
|
||||
) -> EcsEntityBuilder;
|
||||
/// Build a static object entity
|
||||
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
|
||||
fn create_projectile(
|
||||
&mut self,
|
||||
@ -203,16 +203,24 @@ impl StateExt for State {
|
||||
.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()
|
||||
.create_entity_synced()
|
||||
.with(pos)
|
||||
.with(comp::Vel(Vec3::zero()))
|
||||
.with(comp::Ori::default())
|
||||
.with(comp::Mass(50.0))
|
||||
.with(comp::Collider::Voxel { id: object.manifest_id().to_string() })
|
||||
.with(comp::Body::Ship(object))
|
||||
.with(comp::Collider::Voxel { id: ship.manifest_entry().to_string() })
|
||||
.with(comp::Body::Ship(ship))
|
||||
.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(
|
||||
|
@ -4120,7 +4120,7 @@ impl FigureMgr {
|
||||
.join()
|
||||
// Don't render dead entities
|
||||
.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(
|
||||
tick,
|
||||
camera,
|
||||
@ -4130,7 +4130,7 @@ impl FigureMgr {
|
||||
inventory,
|
||||
false,
|
||||
pos.0,
|
||||
figure_lod_render_distance,
|
||||
figure_lod_render_distance * scale.map_or(1.0, |s| s.0),
|
||||
|state| state.can_shadow_sun(),
|
||||
) {
|
||||
renderer.render_figure_shadow_directed(
|
||||
@ -4162,7 +4162,7 @@ impl FigureMgr {
|
||||
let character_state_storage = state.read_storage::<common::comp::CharacterState>();
|
||||
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.read_storage::<Pos>(),
|
||||
ecs.read_storage::<Ori>().maybe(),
|
||||
@ -4187,7 +4187,7 @@ impl FigureMgr {
|
||||
inventory,
|
||||
false,
|
||||
pos.0,
|
||||
figure_lod_render_distance,
|
||||
figure_lod_render_distance * scale.map_or(1.0, |s| s.0),
|
||||
|state| state.visible(),
|
||||
) {
|
||||
renderer.render_figure(model, &col_lights, global, locals, bone_consts, lod);
|
||||
|
Loading…
Reference in New Issue
Block a user