veloren/voxygen/src/scene/figure/mod.rs

1153 lines
47 KiB
Rust
Raw Normal View History

2019-08-21 01:19:02 +00:00
mod cache;
mod load;
pub use cache::FigureModelCache;
pub use load::load_mesh; // TODO: Don't make this public.
use crate::{
anim::{
2019-10-26 02:20:38 +00:00
self, biped_large::BipedLargeSkeleton, bird_medium::BirdMediumSkeleton,
bird_small::BirdSmallSkeleton, character::CharacterSkeleton, dragon::DragonSkeleton,
fish_medium::FishMediumSkeleton, fish_small::FishSmallSkeleton, object::ObjectSkeleton,
quadruped_medium::QuadrupedMediumSkeleton, quadruped_small::QuadrupedSmallSkeleton,
Animation, Skeleton,
2019-08-21 01:19:02 +00:00
},
2019-09-25 12:00:00 +00:00
render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow},
2019-08-21 01:19:02 +00:00
scene::camera::{Camera, CameraMode},
};
use client::Client;
use common::{
comp::{
ActionState::*, Body, CharacterState, ItemKind, Last, MovementState::*, Ori, Pos, Scale,
Stats, Vel,
2019-08-21 01:19:02 +00:00
},
common: Rework volume API See the doc comments in `common/src/vol.rs` for more information on the API itself. The changes include: * Consistent `Err`/`Error` naming. * Types are named `...Error`. * `enum` variants are named `...Err`. * Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation to an upcoming change where a “map” in the game related sense will be added. * Add volume iterators. There are two types of them: * _Position_ iterators obtained from the trait `IntoPosIterator` using the method `fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...` which returns an iterator over `Vec3<i32>`. * _Volume_ iterators obtained from the trait `IntoVolIterator` using the method `fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...` which returns an iterator over `(Vec3<i32>, &Self::Vox)`. Those traits will usually be implemented by references to volume types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some type which usually implements several volume traits, such as `Chunk`). * _Position_ iterators iterate over the positions valid for that volume. * _Volume_ iterators do the same but return not only the position but also the voxel at that position, in each iteration. * Introduce trait `RectSizedVol` for the use case which we have with `Chonk`: A `Chonk` is sized only in x and y direction. * Introduce traits `RasterableVol`, `RectRasterableVol` * `RasterableVol` represents a volume that is compile-time sized and has its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen because such a volume can be used with `VolGrid3d`. * `RectRasterableVol` represents a volume that is compile-time sized at least in x and y direction and has its lower bound at `(0, 0, z)`. There's no requirement on he lower bound or size in z direction. The name `RectRasterableVol` was chosen because such a volume can be used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
terrain::TerrainChunk,
vol::RectRasterableVol,
2019-08-21 01:19:02 +00:00
};
use hashbrown::HashMap;
2019-10-15 14:06:37 +00:00
use log::trace;
2019-11-30 06:41:20 +00:00
use specs::{Entity as EcsEntity, Join, WorldExt};
use vek::*;
2019-08-21 01:19:02 +00:00
const DAMAGE_FADE_COEFFICIENT: f64 = 5.0;
pub struct FigureMgr {
model_cache: FigureModelCache,
character_states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
2019-10-26 02:20:38 +00:00
quadruped_small_states: HashMap<EcsEntity, FigureState<QuadrupedSmallSkeleton>>,
2019-08-21 01:19:02 +00:00
quadruped_medium_states: HashMap<EcsEntity, FigureState<QuadrupedMediumSkeleton>>,
bird_medium_states: HashMap<EcsEntity, FigureState<BirdMediumSkeleton>>,
fish_medium_states: HashMap<EcsEntity, FigureState<FishMediumSkeleton>>,
2019-10-23 04:59:05 +00:00
dragon_states: HashMap<EcsEntity, FigureState<DragonSkeleton>>,
bird_small_states: HashMap<EcsEntity, FigureState<BirdSmallSkeleton>>,
fish_small_states: HashMap<EcsEntity, FigureState<FishSmallSkeleton>>,
biped_large_states: HashMap<EcsEntity, FigureState<BipedLargeSkeleton>>,
2019-08-21 01:19:02 +00:00
object_states: HashMap<EcsEntity, FigureState<ObjectSkeleton>>,
}
impl FigureMgr {
pub fn new() -> Self {
Self {
model_cache: FigureModelCache::new(),
character_states: HashMap::new(),
2019-10-26 02:20:38 +00:00
quadruped_small_states: HashMap::new(),
2019-08-21 01:19:02 +00:00
quadruped_medium_states: HashMap::new(),
bird_medium_states: HashMap::new(),
fish_medium_states: HashMap::new(),
2019-10-23 04:59:05 +00:00
dragon_states: HashMap::new(),
bird_small_states: HashMap::new(),
fish_small_states: HashMap::new(),
biped_large_states: HashMap::new(),
2019-08-21 01:19:02 +00:00
object_states: HashMap::new(),
}
}
pub fn clean(&mut self, tick: u64) {
self.model_cache.clean(tick);
}
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client, camera: &Camera) {
2019-08-21 01:19:02 +00:00
let time = client.state().get_time();
let tick = client.get_tick();
let ecs = client.state().ecs();
let view_distance = client.view_distance().unwrap_or(1);
let dt = client.state().get_delta_time();
// Get player position.
let player_pos = ecs
.read_storage::<Pos>()
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
for (entity, pos, ori, scale, body, character, last_character, stats) in (
2019-08-21 01:19:02 +00:00
&ecs.entities(),
&ecs.read_storage::<Pos>(),
&ecs.read_storage::<Ori>(),
ecs.read_storage::<Scale>().maybe(),
&ecs.read_storage::<Body>(),
ecs.read_storage::<CharacterState>().maybe(),
ecs.read_storage::<Last<CharacterState>>().maybe(),
ecs.read_storage::<Stats>().maybe(),
)
.join()
{
2019-11-24 20:53:47 +00:00
// Don't process figures outside the vd
let vd_frac = Vec2::from(pos.0 - player_pos)
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
d.abs() as f32 / sz as f32
})
.magnitude()
/ view_distance as f32;
// Keep from re-adding/removing entities on the border of the vd
if vd_frac > 1.2 {
match body {
Body::Humanoid(_) => {
self.character_states.remove(&entity);
}
Body::QuadrupedSmall(_) => {
self.quadruped_small_states.remove(&entity);
}
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.remove(&entity);
}
Body::BirdMedium(_) => {
self.bird_medium_states.remove(&entity);
}
Body::FishMedium(_) => {
self.fish_medium_states.remove(&entity);
}
Body::Dragon(_) => {
self.dragon_states.remove(&entity);
}
Body::BirdSmall(_) => {
self.bird_small_states.remove(&entity);
}
Body::FishSmall(_) => {
self.fish_small_states.remove(&entity);
}
Body::BipedLarge(_) => {
self.biped_large_states.remove(&entity);
}
Body::Object(_) => {
self.object_states.remove(&entity);
}
}
continue;
} else if vd_frac > 1.0 {
match body {
Body::Humanoid(_) => {
self.character_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::QuadrupedSmall(_) => {
self.quadruped_small_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::BirdMedium(_) => {
self.bird_medium_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::FishMedium(_) => {
self.fish_medium_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::Dragon(_) => {
self.dragon_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::BirdSmall(_) => {
self.bird_small_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::FishSmall(_) => {
self.fish_small_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::BipedLarge(_) => {
self.biped_large_states.get_mut(&entity).map(|state| state.visible = false);
}
Body::Object(_) => {
self.object_states.get_mut(&entity).map(|state| state.visible = false);
}
}
continue;
}
// Don't process figures outside the frustum spectrum
let frustum = camera.frustum(client);
let (in_frustum, lpindex) = frustum.test_sphere_coherence(
pos.0.into_array(),
scale.unwrap_or(&Scale(1.0)).0 * 2.0,
match body {
Body::Humanoid(_) => {
self.character_states.get(&entity).map(|state| state.lpindex)
}
Body::QuadrupedSmall(_) => {
self.quadruped_small_states.get(&entity).map(|state| state.lpindex)
}
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.get(&entity).map(|state| state.lpindex)
}
Body::BirdMedium(_) => {
self.bird_medium_states.get(&entity).map(|state| state.lpindex)
}
Body::FishMedium(_) => {
self.fish_medium_states.get(&entity).map(|state| state.lpindex)
}
Body::Dragon(_) => {
self.dragon_states.get(&entity).map(|state| state.lpindex)
}
Body::BirdSmall(_) => {
self.bird_small_states.get(&entity).map(|state| state.lpindex)
}
Body::FishSmall(_) => {
self.fish_small_states.get(&entity).map(|state| state.lpindex)
}
Body::BipedLarge(_) => {
self.biped_large_states.get(&entity).map(|state| state.lpindex)
}
Body::Object(_) => {
self.object_states.get(&entity).map(|state| state.lpindex)
}
}.unwrap_or(0),
);
match body {
Body::Humanoid(_) => {
self.character_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::QuadrupedSmall(_) => {
self.quadruped_small_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::BirdMedium(_) => {
self.bird_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::FishMedium(_) => {
self.fish_medium_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::Dragon(_) => {
self.dragon_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::BirdSmall(_) => {
self.bird_small_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::FishSmall(_) => {
self.fish_small_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::BipedLarge(_) => {
self.biped_large_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
Body::Object(_) => {
self.object_states.get_mut(&entity).map(|state| state.visible = in_frustum);
}
}
2019-11-24 20:53:47 +00:00
if !in_frustum {
2019-08-21 01:19:02 +00:00
continue;
}
// Change in health as color!
let col = stats
.map(|s| {
2019-08-21 01:19:02 +00:00
Rgba::broadcast(1.0)
+ Rgba::new(2.0, 2.0, 2.0, 0.0).map(|c| {
(c / (1.0 + DAMAGE_FADE_COEFFICIENT * s.health.last_change.0)) as f32
})
2019-08-21 01:19:02 +00:00
})
.unwrap_or(Rgba::broadcast(1.0));
let scale = scale.map(|s| s.0).unwrap_or(1.0);
let skeleton_attr = &self
.model_cache
.get_or_create_model(
renderer,
*body,
stats.map(|s| &s.equipment),
tick,
CameraMode::default(),
None,
)
2019-08-21 01:19:02 +00:00
.1;
let mut movement_animation_rate = 1.0;
let mut action_animation_rate = 1.0;
let vel = ecs
.read_storage::<Vel>()
.get(entity)
.cloned()
.unwrap_or_default();
let active_tool_kind = if let Some(ItemKind::Tool { kind, .. }) = stats
.and_then(|s| s.equipment.main.as_ref())
.map(|i| &i.kind)
{
Some(*kind)
} else {
None
};
2019-08-21 01:19:02 +00:00
match body {
Body::Humanoid(_) => {
let state = self
.character_states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new()));
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
2019-08-21 01:19:02 +00:00
}
if !character.is_same_action(&last_character.0) {
state.action_time = 0.0;
2019-08-21 01:19:02 +00:00
}
let target_base = match &character.movement {
2019-12-09 14:45:10 +00:00
Stand => anim::character::StandAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&CharacterSkeleton::new(),
(active_tool_kind, time),
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
Run => anim::character::RunAnimation::update_skeleton(
&CharacterSkeleton::new(),
(active_tool_kind, vel.0, ori.0, state.last_ori, time),
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
2019-12-09 14:45:10 +00:00
Jump | Fall => anim::character::JumpAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&CharacterSkeleton::new(),
(active_tool_kind, time),
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
Glide => anim::character::GlidingAnimation::update_skeleton(
&CharacterSkeleton::new(),
(active_tool_kind, vel.0, ori.0, state.last_ori, time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Swim => anim::character::SwimAnimation::update_skeleton(
&CharacterSkeleton::new(),
(active_tool_kind, vel.0.magnitude(), ori.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Climb => anim::character::ClimbAnimation::update_skeleton(
&CharacterSkeleton::new(),
(active_tool_kind, vel.0, ori.0, time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Sit => anim::character::SitAnimation::update_skeleton(
&CharacterSkeleton::new(),
(active_tool_kind, time),
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
};
let target_bones = match (&character.movement, &character.action) {
(Stand, Wield { .. }) => anim::character::CidleAnimation::update_skeleton(
&target_base,
(active_tool_kind, time),
state.action_time,
&mut action_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
(Stand, Block { .. }) => {
anim::character::BlockIdleAnimation::update_skeleton(
&target_base,
(active_tool_kind, time),
state.action_time,
&mut action_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
)
}
(_, Attack { .. }) => anim::character::AttackAnimation::update_skeleton(
&target_base,
(active_tool_kind, time),
state.action_time,
&mut action_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
(_, Wield { .. }) => anim::character::WieldAnimation::update_skeleton(
&target_base,
(active_tool_kind, vel.0.magnitude(), time),
state.action_time,
&mut action_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
(_, Roll { .. }) => anim::character::RollAnimation::update_skeleton(
&target_base,
2019-12-30 15:16:21 +00:00
(active_tool_kind, ori.0, state.last_ori, time),
state.action_time,
&mut action_animation_rate,
skeleton_attr,
),
2019-08-21 01:19:02 +00:00
(_, Block { .. }) => anim::character::BlockAnimation::update_skeleton(
&target_base,
(active_tool_kind, time),
state.action_time,
&mut action_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
(_, Charge { .. }) => anim::character::ChargeAnimation::update_skeleton(
&target_base,
(active_tool_kind, time),
state.action_time,
&mut action_animation_rate,
skeleton_attr,
),
2019-08-21 01:19:02 +00:00
_ => target_base,
};
state.skeleton.interpolate(&target_bones, dt);
state.update(
renderer,
pos.0,
2019-10-14 10:22:48 +00:00
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
2019-08-21 01:19:02 +00:00
}
2019-10-26 02:20:38 +00:00
Body::QuadrupedSmall(_) => {
2019-08-21 01:19:02 +00:00
let state = self
2019-10-26 02:20:38 +00:00
.quadruped_small_states
2019-08-21 01:19:02 +00:00
.entry(entity)
2019-10-26 02:20:38 +00:00
.or_insert_with(|| {
FigureState::new(renderer, QuadrupedSmallSkeleton::new())
});
2019-08-21 01:19:02 +00:00
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
2019-08-21 01:19:02 +00:00
}
let target_base = match character.movement {
2019-10-26 02:20:38 +00:00
Stand => anim::quadruped_small::IdleAnimation::update_skeleton(
&QuadrupedSmallSkeleton::new(),
2019-08-21 01:19:02 +00:00
time,
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
2019-10-26 02:20:38 +00:00
Run => anim::quadruped_small::RunAnimation::update_skeleton(
&QuadrupedSmallSkeleton::new(),
2019-08-21 01:19:02 +00:00
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
2019-10-26 02:20:38 +00:00
Jump => anim::quadruped_small::JumpAnimation::update_skeleton(
&QuadrupedSmallSkeleton::new(),
2019-08-21 01:19:02 +00:00
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
2019-10-14 10:22:48 +00:00
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
2019-08-21 01:19:02 +00:00
}
Body::QuadrupedMedium(_) => {
let state = self
.quadruped_medium_states
.entry(entity)
.or_insert_with(|| {
FigureState::new(renderer, QuadrupedMediumSkeleton::new())
});
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
2019-08-21 01:19:02 +00:00
}
let target_base = match character.movement {
2019-10-26 02:20:38 +00:00
Stand => anim::quadruped_medium::IdleAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&QuadrupedMediumSkeleton::new(),
time,
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
2019-10-26 02:20:38 +00:00
Run => anim::quadruped_medium::RunAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&QuadrupedMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
2019-10-26 02:20:38 +00:00
Jump => anim::quadruped_medium::JumpAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&QuadrupedMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
2019-10-14 10:22:48 +00:00
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
2019-08-21 01:19:02 +00:00
}
Body::BirdMedium(_) => {
let state = self
.bird_medium_states
.entry(entity)
2019-10-26 02:20:38 +00:00
.or_insert_with(|| FigureState::new(renderer, BirdMediumSkeleton::new()));
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
}
let target_base = match character.movement {
2019-10-26 02:20:38 +00:00
Stand => anim::bird_medium::IdleAnimation::update_skeleton(
&BirdMediumSkeleton::new(),
time,
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
2019-10-26 02:20:38 +00:00
Run => anim::bird_medium::RunAnimation::update_skeleton(
&BirdMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
2019-10-26 02:20:38 +00:00
Jump => anim::bird_medium::JumpAnimation::update_skeleton(
&BirdMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::FishMedium(_) => {
let state = self
.fish_medium_states
.entry(entity)
2019-10-26 02:20:38 +00:00
.or_insert_with(|| FigureState::new(renderer, FishMediumSkeleton::new()));
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
}
let target_base = match character.movement {
2019-10-26 02:20:38 +00:00
Stand => anim::fish_medium::IdleAnimation::update_skeleton(
&FishMediumSkeleton::new(),
time,
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
2019-10-26 02:20:38 +00:00
Run => anim::fish_medium::RunAnimation::update_skeleton(
&FishMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
2019-10-26 02:20:38 +00:00
Jump => anim::fish_medium::JumpAnimation::update_skeleton(
&FishMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
2019-10-23 04:59:05 +00:00
Body::Dragon(_) => {
let state = self
.dragon_states
.entry(entity)
2019-10-26 02:20:38 +00:00
.or_insert_with(|| FigureState::new(renderer, DragonSkeleton::new()));
2019-10-23 04:59:05 +00:00
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
}
let target_base = match character.movement {
Stand => anim::dragon::IdleAnimation::update_skeleton(
&DragonSkeleton::new(),
time,
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Run => anim::dragon::RunAnimation::update_skeleton(
&DragonSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Jump => anim::dragon::JumpAnimation::update_skeleton(
&DragonSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
2019-10-23 04:59:05 +00:00
);
}
Body::BirdSmall(_) => {
let state = self
.bird_small_states
.entry(entity)
2019-10-26 02:20:38 +00:00
.or_insert_with(|| FigureState::new(renderer, BirdSmallSkeleton::new()));
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
}
let target_base = match character.movement {
Stand => anim::bird_small::IdleAnimation::update_skeleton(
&BirdSmallSkeleton::new(),
time,
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Run => anim::bird_small::RunAnimation::update_skeleton(
&BirdSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Jump => anim::bird_small::JumpAnimation::update_skeleton(
&BirdSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::FishSmall(_) => {
let state = self
.fish_small_states
.entry(entity)
2019-10-26 02:20:38 +00:00
.or_insert_with(|| FigureState::new(renderer, FishSmallSkeleton::new()));
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
}
let target_base = match character.movement {
Stand => anim::fish_small::IdleAnimation::update_skeleton(
&FishSmallSkeleton::new(),
time,
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Run => anim::fish_small::RunAnimation::update_skeleton(
&FishSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Jump => anim::fish_small::JumpAnimation::update_skeleton(
&FishSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
Body::BipedLarge(_) => {
let state = self
.biped_large_states
.entry(entity)
2019-10-26 02:20:38 +00:00
.or_insert_with(|| FigureState::new(renderer, BipedLargeSkeleton::new()));
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.is_same_movement(&last_character.0) {
state.movement_time = 0.0;
}
let target_base = match character.movement {
Stand => anim::biped_large::IdleAnimation::update_skeleton(
&BipedLargeSkeleton::new(),
time,
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Run => anim::biped_large::RunAnimation::update_skeleton(
&BipedLargeSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
Jump => anim::biped_large::JumpAnimation::update_skeleton(
&BipedLargeSkeleton::new(),
(vel.0.magnitude(), time),
state.movement_time,
&mut movement_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
}
2019-08-21 01:19:02 +00:00
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,
2019-10-14 10:22:48 +00:00
vel.0,
ori.0,
scale,
col,
dt,
movement_animation_rate,
action_animation_rate,
lpindex,
true,
);
2019-08-21 01:19:02 +00:00
}
}
}
// Clear states that have dead entities.
self.character_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
2019-10-26 02:20:38 +00:00
self.quadruped_small_states
2019-08-21 01:19:02 +00:00
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.quadruped_medium_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.bird_medium_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.fish_medium_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
2019-10-23 04:59:05 +00:00
self.dragon_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.bird_small_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.fish_small_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
self.biped_large_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
2019-08-21 01:19:02 +00:00
self.object_states
.retain(|entity, _| ecs.entities().is_alive(*entity));
}
pub fn render(
&mut self,
renderer: &mut Renderer,
client: &mut Client,
globals: &Consts<Globals>,
lights: &Consts<Light>,
2019-09-25 12:00:00 +00:00
shadows: &Consts<Shadow>,
2019-08-21 01:19:02 +00:00
camera: &Camera,
) {
let tick = client.get_tick();
let ecs = client.state().ecs();
let character_state_storage = client
.state()
.read_storage::<common::comp::CharacterState>();
let character_state = character_state_storage.get(client.entity());
for (entity, _, _, body, stats, _) in (
2019-08-21 01:19:02 +00:00
&ecs.entities(),
&ecs.read_storage::<Pos>(),
&ecs.read_storage::<Ori>(),
&ecs.read_storage::<Body>(),
ecs.read_storage::<Stats>().maybe(),
ecs.read_storage::<Scale>().maybe(),
)
.join()
// Don't render dead entities
.filter(|(_, _, _, _, stats, _)| stats.map_or(true, |s| !s.is_dead))
2019-08-21 01:19:02 +00:00
{
if let Some((locals, bone_consts, visible)) = match body {
2019-08-21 01:19:02 +00:00
Body::Humanoid(_) => self
.character_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
2019-10-26 02:20:38 +00:00
Body::QuadrupedSmall(_) => self
.quadruped_small_states
2019-08-21 01:19:02 +00:00
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
2019-08-21 01:19:02 +00:00
Body::QuadrupedMedium(_) => self
.quadruped_medium_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::BirdMedium(_) => self
.bird_medium_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::FishMedium(_) => self
.fish_medium_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
2019-10-23 04:59:05 +00:00
Body::Dragon(_) => self
.dragon_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::BirdSmall(_) => self
.bird_small_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::FishSmall(_) => self
.fish_small_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
Body::BipedLarge(_) => self
.biped_large_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
2019-08-21 01:19:02 +00:00
Body::Object(_) => self
.object_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts(), state.visible)),
2019-08-21 01:19:02 +00:00
} {
if !visible {
continue;
}
let is_player = entity == client.entity();
let player_camera_mode = if is_player {
camera.get_mode()
} else {
CameraMode::default()
};
2019-08-17 15:23:03 +00:00
let model = &self
.model_cache
.get_or_create_model(
renderer,
*body,
stats.map(|s| &s.equipment),
tick,
player_camera_mode,
if is_player { character_state } else { None },
)
2019-08-17 15:23:03 +00:00
.0;
2019-08-21 01:19:02 +00:00
2019-09-25 12:00:00 +00:00
renderer.render_figure(model, globals, locals, bone_consts, lights, shadows);
2019-08-21 01:19:02 +00:00
} else {
2019-10-15 14:06:37 +00:00
trace!("Body has no saved figure");
2019-08-21 01:19:02 +00:00
}
}
}
pub fn figure_count(&self) -> usize {
self.character_states.len()
+ self.quadruped_small_states.len()
+ self.character_states.len()
+ self.quadruped_medium_states.len()
+ self.bird_medium_states.len()
+ self.fish_medium_states.len()
+ self.dragon_states.len()
+ self.bird_small_states.len()
+ self.fish_small_states.len()
+ self.biped_large_states.len()
+ self.object_states.len()
}
pub fn figure_count_visible(&self) -> usize {
self.character_states.iter().filter(|(_, c)| c.visible).count()
+ self.quadruped_small_states.iter().filter(|(_, c)| c.visible).count()
+ self.quadruped_medium_states.iter().filter(|(_, c)| c.visible).count()
+ self.bird_medium_states.iter().filter(|(_, c)| c.visible).count()
+ self.fish_medium_states.iter().filter(|(_, c)| c.visible).count()
+ self.dragon_states.iter().filter(|(_, c)| c.visible).count()
+ self.bird_small_states.iter().filter(|(_, c)| c.visible).count()
+ self.fish_small_states.iter().filter(|(_, c)| c.visible).count()
+ self.biped_large_states.iter().filter(|(_, c)| c.visible).count()
+ self.object_states.iter().filter(|(_, c)| c.visible).count()
}
2019-08-21 01:19:02 +00:00
}
pub struct FigureState<S: Skeleton> {
bone_consts: Consts<FigureBoneData>,
locals: Consts<FigureLocals>,
movement_time: f64,
action_time: f64,
2019-08-21 01:19:02 +00:00
skeleton: S,
pos: Vec3<f32>,
ori: Vec3<f32>,
last_ori: Vec3<f32>,
lpindex: u8,
visible: bool,
2019-08-21 01:19:02 +00:00
}
impl<S: Skeleton> FigureState<S> {
pub fn new(renderer: &mut Renderer, skeleton: S) -> Self {
Self {
bone_consts: renderer
.create_consts(&skeleton.compute_matrices())
.unwrap(),
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
movement_time: 0.0,
action_time: 0.0,
2019-08-21 01:19:02 +00:00
skeleton,
pos: Vec3::zero(),
ori: Vec3::zero(),
last_ori: Vec3::zero(),
lpindex: 0,
visible: false,
2019-08-21 01:19:02 +00:00
}
}
pub fn update(
&mut self,
renderer: &mut Renderer,
pos: Vec3<f32>,
2019-10-14 10:22:48 +00:00
vel: Vec3<f32>,
2019-08-21 01:19:02 +00:00
ori: Vec3<f32>,
scale: f32,
col: Rgba<f32>,
dt: f32,
movement_rate: f32,
action_rate: f32,
lpindex: u8,
visible: bool,
2019-08-21 01:19:02 +00:00
) {
self.visible = visible;
self.lpindex = lpindex;
self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt);
2019-08-21 01:19:02 +00:00
// Update interpolation values
if self.pos.distance_squared(pos) < 64.0 * 64.0 {
2019-10-14 10:22:48 +00:00
self.pos = Lerp::lerp(self.pos, pos + vel * 0.03, 10.0 * dt);
self.ori = Slerp::slerp(self.ori, ori, 5.0 * dt);
2019-08-21 01:19:02 +00:00
} else {
self.pos = pos;
self.ori = ori;
}
self.movement_time += (dt * movement_rate) as f64;
self.action_time += (dt * action_rate) as f64;
2019-08-21 01:19:02 +00:00
let mat = Mat4::<f32>::identity()
* Mat4::translation_3d(self.pos)
* Mat4::rotation_z(-ori.x.atan2(ori.y))
* Mat4::rotation_x(ori.z.atan2(Vec2::from(ori).magnitude()))
2019-08-21 01:19:02 +00:00
* Mat4::scaling_3d(Vec3::from(0.8 * scale));
let locals = FigureLocals::new(mat, col);
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
renderer
.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())
.unwrap();
}
pub fn locals(&self) -> &Consts<FigureLocals> {
&self.locals
}
pub fn bone_consts(&self) -> &Consts<FigureBoneData> {
&self.bone_consts
}
pub fn skeleton_mut(&mut self) -> &mut S {
&mut self.skeleton
}
}