Added object entities

This commit is contained in:
Joshua Barretto 2019-07-21 13:42:45 +01:00
parent 56433f0b40
commit 1dc654dde7
16 changed files with 335 additions and 110 deletions

View File

@ -403,7 +403,9 @@ impl Client {
self.state.write_component(entity, pos); self.state.write_component(entity, pos);
self.state.write_component(entity, vel); self.state.write_component(entity, vel);
self.state.write_component(entity, ori); self.state.write_component(entity, ori);
self.state.write_component(entity, action_state); if let Some(a_s) = action_state {
self.state.write_component(entity, a_s);
}
} }
} }
ServerMsg::TerrainChunkUpdate { key, chunk } => { ServerMsg::TerrainChunkUpdate { key, chunk } => {

View File

@ -1,4 +1,5 @@
pub mod humanoid; pub mod humanoid;
pub mod object;
pub mod quadruped; pub mod quadruped;
pub mod quadruped_medium; pub mod quadruped_medium;
@ -9,6 +10,7 @@ pub enum Body {
Humanoid(humanoid::Body), Humanoid(humanoid::Body),
Quadruped(quadruped::Body), Quadruped(quadruped::Body),
QuadrupedMedium(quadruped_medium::Body), QuadrupedMedium(quadruped_medium::Body),
Object(object::Body),
} }
impl Component for Body { impl Component for Body {

View File

@ -0,0 +1,18 @@
use rand::{seq::SliceRandom, thread_rng};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Body {
Bomb,
Scarecrow,
Chest,
Pumpkin,
}
impl Body {
pub fn random() -> Self {
let mut rng = thread_rng();
*(&ALL_OBJECTS).choose(&mut rng).unwrap()
}
}
const ALL_OBJECTS: [Body; 4] = [Body::Bomb, Body::Scarecrow, Body::Chest, Body::Pumpkin];

View File

@ -13,7 +13,7 @@ mod stats;
pub use action_state::ActionState; pub use action_state::ActionState;
pub use agent::Agent; pub use agent::Agent;
pub use animation::{Animation, AnimationInfo}; pub use animation::{Animation, AnimationInfo};
pub use body::{humanoid, quadruped, quadruped_medium, Body}; pub use body::{humanoid, object, quadruped, quadruped_medium, Body};
pub use controller::Controller; pub use controller::Controller;
pub use inputs::{ pub use inputs::{
Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding, Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding,

View File

@ -40,7 +40,7 @@ pub enum ServerMsg {
pos: comp::Pos, pos: comp::Pos,
vel: comp::Vel, vel: comp::Vel,
ori: comp::Ori, ori: comp::Ori,
action_state: comp::ActionState, action_state: Option<comp::ActionState>,
}, },
TerrainChunkUpdate { TerrainChunkUpdate {
key: Vec2<i32>, key: Vec2<i32>,

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
comp::{ActionState, Attacking, Controller, Gliding, OnGround, Rolling, Vel, Wielding}, comp::{ActionState, Attacking, Controller, Gliding, OnGround, Rolling, Vel, Wielding},
sys::phys::MOVEMENT_THRESHOLD_VEL, sys::movement::MOVEMENT_THRESHOLD_VEL,
}; };
use specs::{Entities, Join, ReadStorage, System, WriteStorage}; use specs::{Entities, Join, ReadStorage, System, WriteStorage};

View File

@ -3,6 +3,7 @@ pub mod agent;
pub mod animation; pub mod animation;
pub mod combat; pub mod combat;
pub mod controller; pub mod controller;
pub mod movement;
pub mod phys; pub mod phys;
mod stats; mod stats;
@ -14,6 +15,7 @@ const AGENT_SYS: &str = "agent_sys";
const CONTROLLER_SYS: &str = "controller_sys"; const CONTROLLER_SYS: &str = "controller_sys";
const ACTION_STATE_SYS: &str = "action_state_sys"; const ACTION_STATE_SYS: &str = "action_state_sys";
const PHYS_SYS: &str = "phys_sys"; const PHYS_SYS: &str = "phys_sys";
const MOVEMENT_SYS: &str = "movement_sys";
const COMBAT_SYS: &str = "combat_sys"; const COMBAT_SYS: &str = "combat_sys";
const ANIMATION_SYS: &str = "animation_sys"; const ANIMATION_SYS: &str = "animation_sys";
const STATS_SYS: &str = "stats_sys"; const STATS_SYS: &str = "stats_sys";
@ -22,6 +24,7 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]); dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]);
dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS]); dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS]);
dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[PHYS_SYS]);
dispatch_builder.add( dispatch_builder.add(
action_state::Sys, action_state::Sys,
ACTION_STATE_SYS, ACTION_STATE_SYS,

138
common/src/sys/movement.rs Normal file
View File

@ -0,0 +1,138 @@
use crate::{
comp::{ActionState, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Stats, Vel, Wielding},
state::DeltaTime,
terrain::TerrainMap,
vol::{ReadVol, Vox},
};
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
use vek::*;
const HUMANOID_ACCEL: f32 = 70.0;
const HUMANOID_SPEED: f32 = 120.0;
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
const HUMANOID_JUMP_ACCEL: f32 = 16.5;
const ROLL_ACCEL: f32 = 120.0;
const ROLL_SPEED: f32 = 550.0;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95;
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
/// This system applies forces and calculates new positions and velocities.
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
ReadExpect<'a, TerrainMap>,
Read<'a, DeltaTime>,
ReadStorage<'a, MoveDir>,
ReadStorage<'a, Stats>,
ReadStorage<'a, ActionState>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Wielding>,
WriteStorage<'a, Rolling>,
WriteStorage<'a, OnGround>,
WriteStorage<'a, Pos>,
WriteStorage<'a, Vel>,
WriteStorage<'a, Ori>,
);
fn run(
&mut self,
(
entities,
terrain,
dt,
move_dirs,
stats,
action_states,
mut jumpings,
mut wieldings,
mut rollings,
mut on_grounds,
mut positions,
mut velocities,
mut orientations,
): Self::SystemData,
) {
// Apply movement inputs
for (entity, stats, a, move_dir, mut pos, mut vel, mut ori) in (
&entities,
&stats,
&action_states,
move_dirs.maybe(),
&mut positions,
&mut velocities,
&mut orientations,
)
.join()
{
// Disable while dead TODO: Replace with client states?
if stats.is_dead {
continue;
}
// Move player according to move_dir
if let Some(move_dir) = move_dir {
vel.0 += Vec2::broadcast(dt.0)
* move_dir.0
* match (a.on_ground, a.gliding, a.rolling) {
(true, false, false)
if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) =>
{
HUMANOID_ACCEL
}
(false, true, false)
if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) =>
{
GLIDE_ACCEL
}
(false, false, false)
if vel.0.magnitude_squared() < HUMANOID_AIR_SPEED.powf(2.0) =>
{
HUMANOID_AIR_ACCEL
}
(true, false, true) if vel.0.magnitude_squared() < ROLL_SPEED.powf(2.0) => {
ROLL_ACCEL
}
_ => 0.0,
};
}
// Jump
if jumpings.get(entity).is_some() {
vel.0.z = HUMANOID_JUMP_ACCEL;
jumpings.remove(entity);
}
// Glide
if a.gliding && vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) && vel.0.z < 0.0 {
let _ = wieldings.remove(entity);
let lift = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
vel.0.z += dt.0 * lift * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
}
// Roll
if let Some(time) = rollings.get_mut(entity).map(|r| &mut r.time) {
let _ = wieldings.remove(entity);
*time += dt.0;
if *time > 0.55 || !a.moving {
rollings.remove(entity);
}
}
// Set direction based on velocity when on the ground
if Vec2::<f32>::from(vel.0).magnitude_squared() > 0.1 {
ori.0 = Lerp::lerp(
ori.0,
vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0),
10.0 * dt.0,
);
}
}
}
}

View File

@ -10,19 +10,6 @@ use vek::*;
const GRAVITY: f32 = 9.81 * 4.0; const GRAVITY: f32 = 9.81 * 4.0;
const FRIC_GROUND: f32 = 0.15; const FRIC_GROUND: f32 = 0.15;
const FRIC_AIR: f32 = 0.015; const FRIC_AIR: f32 = 0.015;
const HUMANOID_ACCEL: f32 = 70.0;
const HUMANOID_SPEED: f32 = 120.0;
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
const HUMANOID_JUMP_ACCEL: f32 = 16.5;
const ROLL_ACCEL: f32 = 120.0;
const ROLL_SPEED: f32 = 550.0;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95;
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
// Integrates forces, calculates the new velocity based off of the old velocity // Integrates forces, calculates the new velocity based off of the old velocity
// dt = delta time // dt = delta time
@ -44,12 +31,7 @@ impl<'a> System<'a> for Sys {
Entities<'a>, Entities<'a>,
ReadExpect<'a, TerrainMap>, ReadExpect<'a, TerrainMap>,
Read<'a, DeltaTime>, Read<'a, DeltaTime>,
ReadStorage<'a, MoveDir>,
ReadStorage<'a, Stats>,
ReadStorage<'a, ActionState>, ReadStorage<'a, ActionState>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Wielding>,
WriteStorage<'a, Rolling>,
WriteStorage<'a, OnGround>, WriteStorage<'a, OnGround>,
WriteStorage<'a, Pos>, WriteStorage<'a, Pos>,
WriteStorage<'a, Vel>, WriteStorage<'a, Vel>,
@ -62,12 +44,7 @@ impl<'a> System<'a> for Sys {
entities, entities,
terrain, terrain,
dt, dt,
move_dirs,
stats,
action_states, action_states,
mut jumpings,
mut wieldings,
mut rollings,
mut on_grounds, mut on_grounds,
mut positions, mut positions,
mut velocities, mut velocities,
@ -75,81 +52,15 @@ impl<'a> System<'a> for Sys {
): Self::SystemData, ): Self::SystemData,
) { ) {
// Apply movement inputs // Apply movement inputs
for (entity, stats, a, move_dir, mut pos, mut vel, mut ori) in ( for (entity, a, mut pos, mut vel, mut ori) in (
&entities, &entities,
&stats,
&action_states, &action_states,
move_dirs.maybe(),
&mut positions, &mut positions,
&mut velocities, &mut velocities,
&mut orientations, &mut orientations,
) )
.join() .join()
{ {
// Disable while dead TODO: Replace with client states?
if stats.is_dead {
continue;
}
// Move player according to move_dir
if let Some(move_dir) = move_dir {
vel.0 += Vec2::broadcast(dt.0)
* move_dir.0
* match (a.on_ground, a.gliding, a.rolling) {
(true, false, false)
if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) =>
{
HUMANOID_ACCEL
}
(false, true, false)
if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) =>
{
GLIDE_ACCEL
}
(false, false, false)
if vel.0.magnitude_squared() < HUMANOID_AIR_SPEED.powf(2.0) =>
{
HUMANOID_AIR_ACCEL
}
(true, false, true) if vel.0.magnitude_squared() < ROLL_SPEED.powf(2.0) => {
ROLL_ACCEL
}
_ => 0.0,
};
}
// Jump
if jumpings.get(entity).is_some() {
vel.0.z = HUMANOID_JUMP_ACCEL;
jumpings.remove(entity);
}
// Glide
if a.gliding && vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) && vel.0.z < 0.0 {
let _ = wieldings.remove(entity);
let lift = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
vel.0.z += dt.0 * lift * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
}
// Roll
if let Some(time) = rollings.get_mut(entity).map(|r| &mut r.time) {
let _ = wieldings.remove(entity);
*time += dt.0;
if *time > 0.55 || !a.moving {
rollings.remove(entity);
}
}
// Set direction based on velocity when on the ground
if Vec2::<f32>::from(vel.0).magnitude_squared() > 0.1 {
ori.0 = Lerp::lerp(
ori.0,
vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0),
10.0 * dt.0,
);
}
// Integrate forces // Integrate forces
// Friction is assumed to be a constant dependent on location // Friction is assumed to be a constant dependent on location
let friction = 50.0 * if a.on_ground { FRIC_GROUND } else { FRIC_AIR }; let friction = 50.0 * if a.on_ground { FRIC_GROUND } else { FRIC_AIR };

View File

@ -130,7 +130,13 @@ lazy_static! {
"{}", "{}",
"/killnpcs : Kill the NPCs", "/killnpcs : Kill the NPCs",
handle_killnpcs, handle_killnpcs,
), ),
ChatCommand::new(
"object",
"{}",
"/object : Spawn a random object",
handle_object,
),
]; ];
} }
@ -444,6 +450,27 @@ fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _actio
server.clients.notify(entity, ServerMsg::Chat(text)); server.clients.notify(entity, ServerMsg::Chat(text));
} }
fn handle_object(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
let pos = server
.state
.ecs()
.read_storage::<comp::Pos>()
.get(entity)
.copied();
if let Some(pos) = pos {
server
.create_object(pos, comp::object::Body::random())
.build();
server
.clients
.notify(entity, ServerMsg::Chat(format!("Spawned object.")));
} else {
server
.clients
.notify(entity, ServerMsg::Chat(format!("You have no position!")));
}
}
fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
let opt_alias = scan_fmt!(&args, action.arg_fmt, String); let opt_alias = scan_fmt!(&args, action.arg_fmt, String);
match opt_alias { match opt_alias {

View File

@ -158,6 +158,24 @@ impl Server {
.with(comp::ForceUpdate) .with(comp::ForceUpdate)
} }
/// Build a static object entity
#[allow(dead_code)]
pub fn create_object(
&mut self,
pos: comp::Pos,
object: comp::object::Body,
) -> EcsEntityBuilder {
self.state
.ecs_mut()
.create_entity_synced()
.with(pos)
.with(comp::Vel(Vec3::zero()))
.with(comp::Ori(Vec3::unit_y()))
.with(comp::Body::Object(object))
.with(comp::ActionState::default())
.with(comp::ForceUpdate)
}
pub fn create_player_character( pub fn create_player_character(
state: &mut State, state: &mut State,
entity: EcsEntity, entity: EcsEntity,
@ -645,12 +663,12 @@ impl Server {
state.write_component(entity, player); state.write_component(entity, player);
// Sync physics // Sync physics
for (&uid, &pos, &vel, &ori, &action_state) in ( for (&uid, &pos, &vel, &ori, action_state) in (
&state.ecs().read_storage::<Uid>(), &state.ecs().read_storage::<Uid>(),
&state.ecs().read_storage::<comp::Pos>(), &state.ecs().read_storage::<comp::Pos>(),
&state.ecs().read_storage::<comp::Vel>(), &state.ecs().read_storage::<comp::Vel>(),
&state.ecs().read_storage::<comp::Ori>(), &state.ecs().read_storage::<comp::Ori>(),
&state.ecs().read_storage::<comp::ActionState>(), state.ecs().read_storage::<comp::ActionState>().maybe(),
) )
.join() .join()
{ {
@ -659,7 +677,7 @@ impl Server {
pos, pos,
vel, vel,
ori, ori,
action_state, action_state: action_state.copied(),
}); });
} }
@ -743,13 +761,13 @@ impl Server {
} }
// Sync physics // Sync physics
for (entity, &uid, &pos, &vel, &ori, &action_state, force_update) in ( for (entity, &uid, &pos, &vel, &ori, action_state, force_update) in (
&self.state.ecs().entities(), &self.state.ecs().entities(),
&self.state.ecs().read_storage::<Uid>(), &self.state.ecs().read_storage::<Uid>(),
&self.state.ecs().read_storage::<comp::Pos>(), &self.state.ecs().read_storage::<comp::Pos>(),
&self.state.ecs().read_storage::<comp::Vel>(), &self.state.ecs().read_storage::<comp::Vel>(),
&self.state.ecs().read_storage::<comp::Ori>(), &self.state.ecs().read_storage::<comp::Ori>(),
&self.state.ecs().read_storage::<comp::ActionState>(), self.state.ecs().read_storage::<comp::ActionState>().maybe(),
self.state.ecs().read_storage::<comp::ForceUpdate>().maybe(), self.state.ecs().read_storage::<comp::ForceUpdate>().maybe(),
) )
.join() .join()
@ -759,7 +777,7 @@ impl Server {
pos, pos,
vel, vel,
ori, ori,
action_state, action_state: action_state.copied(),
}; };
let state = &self.state; let state = &self.state;

View File

@ -1,6 +1,7 @@
use super::Skeleton; use super::Skeleton;
use crate::render::FigureBoneData; use crate::render::FigureBoneData;
#[derive(Clone)]
pub struct FixtureSkeleton; pub struct FixtureSkeleton;
impl FixtureSkeleton { impl FixtureSkeleton {

View File

@ -1,5 +1,6 @@
pub mod character; pub mod character;
pub mod fixture; pub mod fixture;
pub mod object;
pub mod quadruped; pub mod quadruped;
pub mod quadrupedmedium; pub mod quadrupedmedium;

View File

@ -0,0 +1,39 @@
use super::Skeleton;
use crate::render::FigureBoneData;
use vek::*;
#[derive(Clone)]
pub struct ObjectSkeleton;
impl ObjectSkeleton {
pub fn new() -> Self {
Self {}
}
}
const SCALE: f32 = 1.0 / 11.0;
impl Skeleton for ObjectSkeleton {
fn compute_matrices(&self) -> [FigureBoneData; 16] {
[
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
]
}
fn interpolate(&mut self, _target: &Self, _dt: f32) {}
}

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
anim::{ anim::{
self, character::CharacterSkeleton, quadruped::QuadrupedSkeleton, self, character::CharacterSkeleton, object::ObjectSkeleton, quadruped::QuadrupedSkeleton,
quadrupedmedium::QuadrupedMediumSkeleton, Animation, Skeleton, SkeletonAttr, quadrupedmedium::QuadrupedMediumSkeleton, Animation, Skeleton, SkeletonAttr,
}, },
mesh::Meshable, mesh::Meshable,
@ -11,7 +11,7 @@ use crate::{
use client::Client; use client::Client;
use common::{ use common::{
assets, assets,
comp::{self, humanoid, item::Weapon, quadruped, quadruped_medium, Body}, comp::{self, humanoid, item::Weapon, object, quadruped, quadruped_medium, Body},
figure::Segment, figure::Segment,
terrain::TerrainChunkSize, terrain::TerrainChunkSize,
vol::VolSize, vol::VolSize,
@ -105,6 +105,24 @@ impl FigureModelCache {
None, None,
None, None,
], ],
Body::Object(object) => [
Some(Self::load_object(object)),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
],
}; };
let skeleton_attr = match body { let skeleton_attr = match body {
@ -493,6 +511,18 @@ impl FigureModelCache {
Vec3::new(-2.5, -4.0, -2.5), Vec3::new(-2.5, -4.0, -2.5),
) )
} }
fn load_object(obj: object::Body) -> Mesh<FigurePipeline> {
Self::load_mesh(
match obj {
object::Body::Bomb => "object/bomb.vox",
object::Body::Scarecrow => "object/scarecrow.vox",
object::Body::Chest => "object/chest_vines.vox",
object::Body::Pumpkin => "object/pumpkin.vox",
},
Vec3::new(-3.5, -3.5, 0.0),
)
}
} }
pub struct FigureMgr { pub struct FigureMgr {
@ -500,6 +530,7 @@ pub struct FigureMgr {
character_states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>, character_states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
quadruped_states: HashMap<EcsEntity, FigureState<QuadrupedSkeleton>>, quadruped_states: HashMap<EcsEntity, FigureState<QuadrupedSkeleton>>,
quadruped_medium_states: HashMap<EcsEntity, FigureState<QuadrupedMediumSkeleton>>, quadruped_medium_states: HashMap<EcsEntity, FigureState<QuadrupedMediumSkeleton>>,
object_states: HashMap<EcsEntity, FigureState<ObjectSkeleton>>,
} }
impl FigureMgr { impl FigureMgr {
@ -509,6 +540,7 @@ impl FigureMgr {
character_states: HashMap::new(), character_states: HashMap::new(),
quadruped_states: HashMap::new(), quadruped_states: HashMap::new(),
quadruped_medium_states: HashMap::new(), quadruped_medium_states: HashMap::new(),
object_states: HashMap::new(),
} }
} }
@ -534,7 +566,7 @@ impl FigureMgr {
&ecs.read_storage::<comp::Vel>(), &ecs.read_storage::<comp::Vel>(),
&ecs.read_storage::<comp::Ori>(), &ecs.read_storage::<comp::Ori>(),
&ecs.read_storage::<comp::Body>(), &ecs.read_storage::<comp::Body>(),
&ecs.read_storage::<comp::AnimationInfo>(), ecs.read_storage::<comp::AnimationInfo>().maybe(),
ecs.read_storage::<comp::Stats>().maybe(), ecs.read_storage::<comp::Stats>().maybe(),
) )
.join() .join()
@ -556,6 +588,9 @@ impl FigureMgr {
Body::QuadrupedMedium(_) => { Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.remove(&entity); self.quadruped_medium_states.remove(&entity);
} }
Body::Object(_) => {
self.object_states.remove(&entity);
}
} }
continue; continue;
} else if vd_frac > 1.0 { } else if vd_frac > 1.0 {
@ -584,6 +619,11 @@ impl FigureMgr {
.entry(entity) .entry(entity)
.or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new())); .or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new()));
let animation_info = match animation_info {
Some(a_i) => a_i,
None => continue,
};
let target_skeleton = match animation_info.animation { let target_skeleton = match animation_info.animation {
comp::Animation::Idle => anim::character::IdleAnimation::update_skeleton( comp::Animation::Idle => anim::character::IdleAnimation::update_skeleton(
state.skeleton_mut(), state.skeleton_mut(),
@ -654,6 +694,11 @@ impl FigureMgr {
.entry(entity) .entry(entity)
.or_insert_with(|| FigureState::new(renderer, QuadrupedSkeleton::new())); .or_insert_with(|| FigureState::new(renderer, QuadrupedSkeleton::new()));
let animation_info = match animation_info {
Some(a_i) => a_i,
None => continue,
};
let target_skeleton = match animation_info.animation { let target_skeleton = match animation_info.animation {
comp::Animation::Run => anim::quadruped::RunAnimation::update_skeleton( comp::Animation::Run => anim::quadruped::RunAnimation::update_skeleton(
state.skeleton_mut(), state.skeleton_mut(),
@ -689,6 +734,11 @@ impl FigureMgr {
FigureState::new(renderer, QuadrupedMediumSkeleton::new()) FigureState::new(renderer, QuadrupedMediumSkeleton::new())
}); });
let animation_info = match animation_info {
Some(a_i) => a_i,
None => continue,
};
let target_skeleton = match animation_info.animation { let target_skeleton = match animation_info.animation {
comp::Animation::Run => { comp::Animation::Run => {
anim::quadrupedmedium::RunAnimation::update_skeleton( anim::quadrupedmedium::RunAnimation::update_skeleton(
@ -722,6 +772,15 @@ impl FigureMgr {
state.skeleton.interpolate(&target_skeleton, dt); state.skeleton.interpolate(&target_skeleton, dt);
state.update(renderer, pos.0, ori.0, col, dt); state.update(renderer, pos.0, ori.0, col, dt);
} }
Body::Object(_) => {
let state = self
.object_states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, ObjectSkeleton::new()));
state.skeleton = state.skeleton_mut().clone();
state.update(renderer, pos.0, ori.0, col, dt);
}
} }
} }
@ -732,6 +791,8 @@ impl FigureMgr {
.retain(|entity, _| ecs.entities().is_alive(*entity)); .retain(|entity, _| ecs.entities().is_alive(*entity));
self.quadruped_medium_states self.quadruped_medium_states
.retain(|entity, _| ecs.entities().is_alive(*entity)); .retain(|entity, _| ecs.entities().is_alive(*entity));
self.object_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
} }
pub fn render( pub fn render(
@ -752,25 +813,24 @@ impl FigureMgr {
.get(client.entity()) .get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0); .map_or(Vec3::zero(), |pos| pos.0);
for (entity, _, _, _, body, _, _) in ( for (entity, _, _, _, body, _) in (
&ecs.entities(), &ecs.entities(),
&ecs.read_storage::<comp::Pos>(), &ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Vel>(), &ecs.read_storage::<comp::Vel>(),
&ecs.read_storage::<comp::Ori>(), &ecs.read_storage::<comp::Ori>(),
&ecs.read_storage::<comp::Body>(), &ecs.read_storage::<comp::Body>(),
&ecs.read_storage::<comp::AnimationInfo>(),
ecs.read_storage::<comp::Stats>().maybe(), ecs.read_storage::<comp::Stats>().maybe(),
) )
.join() .join()
// Don't render figures outside the vd // Don't render figures outside the vd
.filter(|(_, pos, _, _, _, _, _)| { .filter(|(_, pos, _, _, _, _)| {
(pos.0 - player_pos) (pos.0 - player_pos)
.map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32) .map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
.magnitude() .magnitude()
< view_distance as f32 < view_distance as f32
}) })
// Don't render dead entities // Don't render dead entities
.filter(|(_, _, _, _, _, _, stats)| stats.map_or(true, |s| !s.is_dead)) .filter(|(_, _, _, _, _, stats)| stats.map_or(true, |s| !s.is_dead))
{ {
if let Some((locals, bone_consts)) = match body { if let Some((locals, bone_consts)) = match body {
Body::Humanoid(_) => self Body::Humanoid(_) => self
@ -785,6 +845,10 @@ impl FigureMgr {
.quadruped_medium_states .quadruped_medium_states
.get(&entity) .get(&entity)
.map(|state| (state.locals(), state.bone_consts())), .map(|state| (state.locals(), state.bone_consts())),
Body::Object(_) => self
.object_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
} { } {
let model = &self let model = &self
.model_cache .model_cache

View File

@ -117,8 +117,9 @@ impl Scene {
// Alter camera position to match player. // Alter camera position to match player.
let tilt = self.camera.get_orientation().y; let tilt = self.camera.get_orientation().y;
let dist = self.camera.get_distance(); let dist = self.camera.get_distance();
self.camera self.camera.set_focus_pos(
.set_focus_pos(player_pos + Vec3::unit_z() * (3.0 - tilt.min(0.0) * dist * 0.75)); player_pos + Vec3::unit_z() * (1.2 + dist * 0.15 - tilt.min(0.0) * dist * 0.75),
);
// Tick camera for interpolation. // Tick camera for interpolation.
self.camera.update(client.state().get_time()); self.camera.update(client.state().get_time());