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

2335 lines
94 KiB
Rust
Raw Normal View History

2019-08-21 01:19:02 +00:00
mod cache;
pub mod load;
2019-08-21 01:19:02 +00:00
pub use cache::FigureModelCache;
pub use load::load_mesh; // TODO: Don't make this public.
use crate::{
ecs::comp::Interpolated,
2019-09-25 12:00:00 +00:00
render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow},
scene::{
camera::{Camera, CameraMode},
SceneData,
},
2019-08-21 01:19:02 +00:00
};
2020-06-17 07:49:14 +00:00
use anim::{
biped_large::BipedLargeSkeleton, bird_medium::BirdMediumSkeleton,
bird_small::BirdSmallSkeleton, character::CharacterSkeleton, critter::CritterSkeleton,
dragon::DragonSkeleton, fish_medium::FishMediumSkeleton, fish_small::FishSmallSkeleton,
golem::GolemSkeleton, object::ObjectSkeleton, quadruped_low::QuadrupedLowSkeleton,
quadruped_medium::QuadrupedMediumSkeleton, quadruped_small::QuadrupedSmallSkeleton, Animation,
Skeleton,
2020-06-17 07:49:14 +00:00
};
2019-08-21 01:19:02 +00:00
use common::{
comp::{
item::ItemKind, Body, CharacterState, Last, LightAnimation, LightEmitter, Loadout, Ori,
PhysicsState, Pos, Scale, Stats, Vel,
2019-08-21 01:19:02 +00:00
},
state::{DeltaTime, State},
states::triple_strike,
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-11-30 06:41:20 +00:00
use specs::{Entity as EcsEntity, Join, WorldExt};
use tracing::trace;
2020-01-08 17:09:54 +00:00
use treeculler::{BVol, BoundingSphere};
use vek::*;
2019-08-21 01:19:02 +00:00
const DAMAGE_FADE_COEFFICIENT: f64 = 5.0;
const MOVING_THRESHOLD: f32 = 0.7;
const MOVING_THRESHOLD_SQR: f32 = MOVING_THRESHOLD * MOVING_THRESHOLD;
2019-08-21 01:19:02 +00:00
pub struct FigureMgr {
model_cache: FigureModelCache,
2020-01-26 00:22:48 +00:00
critter_model_cache: FigureModelCache<CritterSkeleton>,
quadruped_small_model_cache: FigureModelCache<QuadrupedSmallSkeleton>,
quadruped_medium_model_cache: FigureModelCache<QuadrupedMediumSkeleton>,
2020-06-01 23:56:50 +00:00
quadruped_low_model_cache: FigureModelCache<QuadrupedLowSkeleton>,
2020-01-26 00:22:48 +00:00
bird_medium_model_cache: FigureModelCache<BirdMediumSkeleton>,
bird_small_model_cache: FigureModelCache<BirdSmallSkeleton>,
dragon_model_cache: FigureModelCache<DragonSkeleton>,
fish_medium_model_cache: FigureModelCache<FishMediumSkeleton>,
fish_small_model_cache: FigureModelCache<FishSmallSkeleton>,
biped_large_model_cache: FigureModelCache<BipedLargeSkeleton>,
2020-04-26 01:09:03 +00:00
golem_model_cache: FigureModelCache<GolemSkeleton>,
2019-08-21 01:19:02 +00:00
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>>,
2020-06-01 23:56:50 +00:00
quadruped_low_states: HashMap<EcsEntity, FigureState<QuadrupedLowSkeleton>>,
bird_medium_states: HashMap<EcsEntity, FigureState<BirdMediumSkeleton>>,
fish_medium_states: HashMap<EcsEntity, FigureState<FishMediumSkeleton>>,
2020-01-26 00:22:48 +00:00
critter_states: HashMap<EcsEntity, FigureState<CritterSkeleton>>,
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>>,
2020-04-26 01:09:03 +00:00
golem_states: HashMap<EcsEntity, FigureState<GolemSkeleton>>,
2019-08-21 01:19:02 +00:00
object_states: HashMap<EcsEntity, FigureState<ObjectSkeleton>>,
}
impl FigureMgr {
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
2019-08-21 01:19:02 +00:00
pub fn new() -> Self {
Self {
model_cache: FigureModelCache::new(),
2020-01-26 00:22:48 +00:00
critter_model_cache: FigureModelCache::new(),
quadruped_small_model_cache: FigureModelCache::new(),
quadruped_medium_model_cache: FigureModelCache::new(),
2020-06-01 23:56:50 +00:00
quadruped_low_model_cache: FigureModelCache::new(),
2020-01-26 00:22:48 +00:00
bird_medium_model_cache: FigureModelCache::new(),
bird_small_model_cache: FigureModelCache::new(),
dragon_model_cache: FigureModelCache::new(),
fish_medium_model_cache: FigureModelCache::new(),
fish_small_model_cache: FigureModelCache::new(),
biped_large_model_cache: FigureModelCache::new(),
2020-04-26 01:09:03 +00:00
golem_model_cache: FigureModelCache::new(),
2019-08-21 01:19:02 +00:00
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(),
2020-06-01 23:56:50 +00:00
quadruped_low_states: HashMap::new(),
bird_medium_states: HashMap::new(),
fish_medium_states: HashMap::new(),
2020-01-26 00:22:48 +00:00
critter_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(),
2020-04-26 01:09:03 +00:00
golem_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);
2020-01-26 00:22:48 +00:00
self.critter_model_cache.clean(tick);
self.quadruped_small_model_cache.clean(tick);
self.quadruped_medium_model_cache.clean(tick);
2020-06-01 23:56:50 +00:00
self.quadruped_low_model_cache.clean(tick);
2020-01-26 00:22:48 +00:00
self.bird_medium_model_cache.clean(tick);
self.bird_small_model_cache.clean(tick);
self.dragon_model_cache.clean(tick);
self.fish_medium_model_cache.clean(tick);
self.fish_small_model_cache.clean(tick);
self.biped_large_model_cache.clean(tick);
2020-04-26 01:09:03 +00:00
self.golem_model_cache.clean(tick);
2019-08-21 01:19:02 +00:00
}
#[allow(clippy::redundant_pattern_matching)] // TODO: Pending review in #587
pub fn update_lighting(&mut self, scene_data: &SceneData) {
let ecs = scene_data.state.ecs();
for (entity, light_emitter) in (&ecs.entities(), &ecs.read_storage::<LightEmitter>()).join()
{
// Add LightAnimation for objects with a LightEmitter
let mut anim_storage = ecs.write_storage::<LightAnimation>();
if let None = anim_storage.get_mut(entity) {
let anim = LightAnimation {
offset: Vec3::zero(),
col: light_emitter.col,
strength: 0.0,
};
let _ = anim_storage.insert(entity, anim);
}
}
let dt = ecs.fetch::<DeltaTime>().0;
for (entity, waypoint, light_emitter_opt, light_anim) in (
&ecs.entities(),
ecs.read_storage::<common::comp::Waypoint>().maybe(),
ecs.read_storage::<LightEmitter>().maybe(),
&mut ecs.write_storage::<LightAnimation>(),
)
.join()
{
let (target_col, target_strength, flicker, animated) =
if let Some(emitter) = light_emitter_opt {
(
emitter.col,
2020-05-04 21:17:54 +00:00
if emitter.strength.is_finite() {
emitter.strength
} else {
0.0
},
emitter.flicker,
emitter.animated,
)
} else {
(Rgb::zero(), 0.0, 0.0, true)
};
if let Some(_) = waypoint {
light_anim.offset = Vec3::unit_z() * 0.5;
}
if let Some(state) = self.character_states.get(&entity) {
light_anim.offset = state.lantern_offset;
}
2020-05-04 21:17:54 +00:00
if !light_anim.strength.is_finite() {
light_anim.strength = 0.0;
}
if animated {
let flicker = (rand::random::<f32>() - 0.5) * flicker / dt.sqrt();
// Close gap between current and target strength by 95% per second
let delta = 0.05_f32.powf(dt);
light_anim.strength =
light_anim.strength * delta + (target_strength + flicker) * (1.0 - delta);
light_anim.col = light_anim.col * delta + target_col * (1.0 - delta)
} else {
light_anim.strength = target_strength;
light_anim.col = target_col;
}
}
}
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData, camera: &Camera) {
let state = scene_data.state;
let time = state.get_time();
let tick = scene_data.tick;
let ecs = state.ecs();
let view_distance = scene_data.view_distance;
let dt = state.get_delta_time();
let frustum = camera.frustum();
2019-08-21 01:19:02 +00:00
// Get player position.
let player_pos = ecs
.read_storage::<Pos>()
.get(scene_data.player_entity)
2019-08-21 01:19:02 +00:00
.map_or(Vec3::zero(), |pos| pos.0);
2020-04-24 18:52:44 +00:00
for (
i,
(
entity,
pos,
interpolated,
vel,
scale,
body,
character,
last_character,
physics,
stats,
loadout,
),
) in (
2019-08-21 01:19:02 +00:00
&ecs.entities(),
&ecs.read_storage::<Pos>(),
ecs.read_storage::<Interpolated>().maybe(),
&ecs.read_storage::<Vel>(),
2019-08-21 01:19:02 +00:00
ecs.read_storage::<Scale>().maybe(),
&ecs.read_storage::<Body>(),
ecs.read_storage::<CharacterState>().maybe(),
ecs.read_storage::<Last<CharacterState>>().maybe(),
&ecs.read_storage::<PhysicsState>(),
2019-08-21 01:19:02 +00:00
ecs.read_storage::<Stats>().maybe(),
ecs.read_storage::<Loadout>().maybe(),
2019-08-21 01:19:02 +00:00
)
.join()
.enumerate()
2019-08-21 01:19:02 +00:00
{
2020-04-24 18:52:44 +00:00
// Maintaining figure data and sending new figure data to the GPU turns out to
// be a very expensive operation. We want to avoid doing it as much
// as possible, so we make the assumption that players don't care so
// much about the update *rate* for far away things. As the entity
// goes further and further away, we start to 'skip' update ticks.
// TODO: Investigate passing the velocity into the shader so we can at least
// interpolate motion
2020-04-24 18:52:44 +00:00
const MIN_PERFECT_RATE_DIST: f32 = 50.0;
2020-04-25 10:23:46 +00:00
if (i as u64 + tick)
% (1 + ((pos.0.distance_squared(camera.get_focus_pos()).powf(0.25)
2020-04-24 18:52:44 +00:00
- MIN_PERFECT_RATE_DIST.powf(0.5))
.max(0.0)
/ 3.0) as u64)
!= 0
{
continue;
}
2020-04-04 19:36:55 +00:00
let is_player = scene_data.player_entity == entity;
let (pos, ori) = interpolated
.map(|i| (Pos(i.pos), *i.ori))
.unwrap_or((*pos, Vec3::unit_y()));
2019-08-21 01:19:02 +00:00
// Don't process figures outside the vd
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
let vd_frac = Vec2::from(pos.0 - player_pos)
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
d.abs() as f32 / sz as f32
})
2019-08-21 01:19:02 +00:00
.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);
},
2019-10-26 02:20:38 +00:00
Body::QuadrupedSmall(_) => {
self.quadruped_small_states.remove(&entity);
},
2019-08-21 01:19:02 +00:00
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.remove(&entity);
},
2020-06-01 23:56:50 +00:00
Body::QuadrupedLow(_) => {
self.quadruped_low_states.remove(&entity);
},
Body::BirdMedium(_) => {
self.bird_medium_states.remove(&entity);
},
Body::FishMedium(_) => {
self.fish_medium_states.remove(&entity);
},
2020-01-26 00:22:48 +00:00
Body::Critter(_) => {
self.critter_states.remove(&entity);
},
2019-10-23 04:59:05 +00:00
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);
},
2020-04-26 01:09:03 +00:00
Body::Golem(_) => {
self.biped_large_states.remove(&entity);
},
2019-08-21 01:19:02 +00:00
Body::Object(_) => {
self.object_states.remove(&entity);
},
2019-08-21 01:19:02 +00:00
}
continue;
} else if vd_frac > 1.0 {
2019-11-24 20:53:47 +00:00
match body {
Body::Humanoid(_) => {
2020-01-08 17:09:54 +00:00
self.character_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::QuadrupedSmall(_) => {
2020-01-08 17:09:54 +00:00
self.quadruped_small_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::QuadrupedMedium(_) => {
2020-01-08 17:09:54 +00:00
self.quadruped_medium_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2020-06-01 23:56:50 +00:00
Body::QuadrupedLow(_) => {
self.quadruped_low_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::BirdMedium(_) => {
2020-01-08 17:09:54 +00:00
self.bird_medium_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::FishMedium(_) => {
2020-01-08 17:09:54 +00:00
self.fish_medium_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2020-01-26 00:22:48 +00:00
Body::Critter(_) => {
self.critter_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::Dragon(_) => {
2020-01-08 17:09:54 +00:00
self.dragon_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::BirdSmall(_) => {
2020-01-08 17:09:54 +00:00
self.bird_small_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::FishSmall(_) => {
2020-01-08 17:09:54 +00:00
self.fish_small_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::BipedLarge(_) => {
2020-01-08 17:09:54 +00:00
self.biped_large_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2020-04-26 01:09:03 +00:00
Body::Golem(_) => {
self.golem_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
Body::Object(_) => {
2020-01-08 17:09:54 +00:00
self.object_states
.get_mut(&entity)
.map(|state| state.visible = false);
},
2019-11-24 20:53:47 +00:00
}
continue;
}
// Don't process figures outside the frustum spectrum
2020-01-08 17:09:54 +00:00
let (in_frustum, lpindex) =
BoundingSphere::new(pos.0.into_array(), scale.unwrap_or(&Scale(1.0)).0 * 2.0)
.coherent_test_against_frustum(
&frustum,
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),
2020-06-03 00:56:42 +00:00
Body::QuadrupedLow(_) => self
2020-06-01 23:56:50 +00:00
.quadruped_low_states
.get(&entity)
.map(|state| state.lpindex),
2020-01-08 17:09:54 +00:00
Body::BirdMedium(_) => self
.bird_medium_states
.get(&entity)
.map(|state| state.lpindex),
Body::FishMedium(_) => self
.fish_medium_states
.get(&entity)
.map(|state| state.lpindex),
2020-01-26 00:22:48 +00:00
Body::Critter(_) => {
self.critter_states.get(&entity).map(|state| state.lpindex)
},
2020-01-08 17:09:54 +00:00
Body::Dragon(_) => {
self.dragon_states.get(&entity).map(|state| state.lpindex)
},
2020-01-08 17:09:54 +00:00
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),
2020-04-26 01:09:03 +00:00
Body::Golem(_) => {
self.golem_states.get(&entity).map(|state| state.lpindex)
},
2020-01-08 17:09:54 +00:00
Body::Object(_) => {
self.object_states.get(&entity).map(|state| state.lpindex)
},
2020-01-08 17:09:54 +00:00
}
.unwrap_or(0),
);
2019-11-24 20:53:47 +00:00
if !in_frustum {
2020-01-07 16:40:06 +00:00
match body {
Body::Humanoid(_) => {
self.character_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::QuadrupedSmall(_) => {
self.quadruped_small_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-06-01 23:56:50 +00:00
Body::QuadrupedLow(_) => {
self.quadruped_low_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::BirdMedium(_) => {
self.bird_medium_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::FishMedium(_) => {
self.fish_medium_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-26 00:22:48 +00:00
Body::Critter(_) => {
self.critter_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::Dragon(_) => {
self.dragon_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::BirdSmall(_) => {
self.bird_small_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::FishSmall(_) => {
self.fish_small_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::BipedLarge(_) => {
self.biped_large_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-04-26 01:09:03 +00:00
Body::Golem(_) => {
self.golem_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
Body::Object(_) => {
self.object_states.get_mut(&entity).map(|state| {
state.lpindex = lpindex;
state.visible = false
});
},
2020-01-07 16:40:06 +00:00
}
2019-08-21 01:19:02 +00:00
}
// Change in health as color!
let col = stats
.map(|s| {
2019-08-21 01:19:02 +00:00
Rgba::broadcast(1.0)
2020-04-04 19:36:55 +00:00
+ Rgba::new(2.0, 2.0, 2., 0.00).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 mut state_animation_rate = 1.0;
let active_item_kind = loadout
.and_then(|l| l.active_item.as_ref())
.map(|i| &i.item.kind);
let active_tool_kind = if let Some(ItemKind::Tool(tool)) = active_item_kind {
Some(tool.kind)
} else {
None
};
let second_item_kind = loadout
.and_then(|l| l.second_item.as_ref())
.map(|i| &i.item.kind);
let second_tool_kind = if let Some(ItemKind::Tool(tool)) = second_item_kind {
Some(tool.kind)
} else {
None
};
2019-08-21 01:19:02 +00:00
match body {
Body::Humanoid(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
2019-08-21 01:19:02 +00:00
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.same_variant(&last_character.0) {
state.state_time = 0.0;
2019-08-21 01:19:02 +00:00
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
2020-03-26 05:35:25 +00:00
(true, false, _) => anim::character::StandAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&CharacterSkeleton::new(),
(active_tool_kind, second_tool_kind, time),
state.state_time,
&mut state_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
// Running
2020-03-26 05:35:25 +00:00
(true, true, _) => anim::character::RunAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&CharacterSkeleton::new(),
(
active_tool_kind,
second_tool_kind,
vel.0,
ori,
state.last_ori,
time,
),
state.state_time,
&mut state_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
),
// In air
(false, _, false) => anim::character::JumpAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&CharacterSkeleton::new(),
(
active_tool_kind,
second_tool_kind,
ori,
state.last_ori,
time,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// Swim
2020-03-26 05:35:25 +00:00
(false, _, true) => anim::character::SwimAnimation::update_skeleton(
&CharacterSkeleton::new(),
(
active_tool_kind,
second_tool_kind,
vel.0,
ori,
state.last_ori,
time,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
};
let target_bones = match &character {
2020-03-07 18:15:02 +00:00
CharacterState::Roll { .. } => {
anim::character::RollAnimation::update_skeleton(
&target_base,
(
active_tool_kind,
second_tool_kind,
ori,
state.last_ori,
time,
),
2020-03-07 18:15:02 +00:00
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::BasicMelee(_) => {
2020-03-27 03:51:55 +00:00
anim::character::AlphaAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2020-03-28 03:59:05 +00:00
CharacterState::BasicRanged(data) => {
if data.exhausted {
anim::character::ShootAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
2020-03-28 03:59:05 +00:00
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
} else {
anim::character::ChargeAnimation::update_skeleton(
&target_base,
2020-05-27 06:41:55 +00:00
(
active_tool_kind,
second_tool_kind,
2020-05-27 06:41:55 +00:00
vel.0.magnitude(),
ori,
state.last_ori,
time,
),
2020-03-28 03:59:05 +00:00
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
}
},
CharacterState::Boost(_) => {
2020-03-27 03:51:55 +00:00
anim::character::AlphaAnimation::update_skeleton(
2019-08-21 01:19:02 +00:00
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
2019-08-21 01:19:02 +00:00
skeleton_attr,
)
},
2020-03-16 15:34:53 +00:00
CharacterState::DashMelee(_) => {
2020-03-28 03:59:05 +00:00
anim::character::DashAnimation::update_skeleton(
2020-03-16 15:34:53 +00:00
&target_base,
(active_tool_kind, second_tool_kind, time),
2020-03-16 15:34:53 +00:00
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::LeapMelee(_) => {
anim::character::LeapAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0, time),
state.state_time,
&mut state_animation_rate,
2020-03-16 15:34:53 +00:00
skeleton_attr,
)
},
2020-03-19 22:40:03 +00:00
CharacterState::TripleStrike(s) => match s.stage {
triple_strike::Stage::First => {
2020-03-27 03:51:55 +00:00
anim::character::AlphaAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
triple_strike::Stage::Second => {
anim::character::SpinAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
triple_strike::Stage::Third => {
2020-03-27 05:57:30 +00:00
anim::character::BetaAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2020-03-19 22:40:03 +00:00
},
2020-03-07 18:15:02 +00:00
CharacterState::BasicBlock { .. } => {
2020-02-03 10:54:50 +00:00
anim::character::BlockIdleAnimation::update_skeleton(
2020-02-24 14:35:07 +00:00
&CharacterSkeleton::new(),
(active_tool_kind, second_tool_kind, time),
state.state_time,
&mut state_animation_rate,
2019-12-26 14:43:59 +00:00
skeleton_attr,
)
2020-02-24 14:35:07 +00:00
},
/*
2020-02-03 10:54:50 +00:00
CharacterState::Charge(_) => {
anim::character::ChargeAnimation::update_skeleton(
2019-12-26 14:43:59 +00:00
&target_base,
(active_tool_kind, time),
state.state_time,
&mut state_animation_rate,
2019-12-26 14:43:59 +00:00
skeleton_attr,
)
}*/
2020-03-07 18:15:02 +00:00
CharacterState::Equipping { .. } => {
2020-03-25 05:22:07 +00:00
anim::character::EquipAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
2020-03-25 05:22:07 +00:00
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2020-03-07 18:15:02 +00:00
CharacterState::Wielding { .. } => {
2020-03-26 05:35:25 +00:00
anim::character::WieldAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
2020-03-26 05:35:25 +00:00
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2020-03-07 18:15:02 +00:00
CharacterState::Glide { .. } => {
anim::character::GlidingAnimation::update_skeleton(
&target_base,
(
active_tool_kind,
second_tool_kind,
vel.0,
ori,
state.last_ori,
time,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2020-03-07 18:15:02 +00:00
CharacterState::Climb { .. } => {
anim::character::ClimbAnimation::update_skeleton(
&CharacterSkeleton::new(),
(active_tool_kind, second_tool_kind, vel.0, ori, time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2020-03-07 18:15:02 +00:00
CharacterState::Sit { .. } => {
anim::character::SitAnimation::update_skeleton(
&CharacterSkeleton::new(),
(active_tool_kind, second_tool_kind, time),
2020-03-07 18:15:02 +00:00
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::GlideWield { .. } => {
anim::character::GlideWieldAnimation::update_skeleton(
&CharacterSkeleton::new(),
(
active_tool_kind,
second_tool_kind,
vel.0,
ori,
state.last_ori,
time,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2020-05-27 06:41:55 +00:00
CharacterState::Dance { .. } => {
anim::character::DanceAnimation::update_skeleton(
&CharacterSkeleton::new(),
(active_tool_kind, second_tool_kind, time),
2020-05-27 06:41:55 +00:00
state.state_time,
&mut state_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,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
2019-10-26 02:20:38 +00:00
Body::QuadrupedSmall(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.quadruped_small_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
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.same_variant(&last_character.0) {
state.state_time = 0.0;
2019-08-21 01:19:02 +00:00
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => {
anim::quadruped_small::IdleAnimation::update_skeleton(
&QuadrupedSmallSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
// Running
(true, true, false) => {
anim::quadruped_small::RunAnimation::update_skeleton(
&QuadrupedSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
// In air
(false, _, false) => anim::quadruped_small::JumpAnimation::update_skeleton(
2019-10-26 02:20:38 +00:00
&QuadrupedSmallSkeleton::new(),
2019-08-21 01:19:02 +00:00
(vel.0.magnitude(), time),
state.state_time,
&mut state_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,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
2020-06-23 21:07:13 +00:00
Body::QuadrupedMedium(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.quadruped_medium_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
2019-08-21 01:19:02 +00:00
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.same_variant(&last_character.0) {
state.state_time = 0.0;
2019-08-21 01:19:02 +00:00
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => {
anim::quadruped_medium::IdleAnimation::update_skeleton(
&QuadrupedMediumSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
// Running
2020-06-30 04:28:09 +00:00
(true, true, _) => anim::quadruped_medium::RunAnimation::update_skeleton(
&QuadrupedMediumSkeleton::new(),
2020-07-03 18:30:41 +00:00
(vel.0.magnitude(), ori, state.last_ori, time, state.avg_vel),
2020-06-30 04:28:09 +00:00
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// In air
(false, _, false) => {
anim::quadruped_medium::JumpAnimation::update_skeleton(
&QuadrupedMediumSkeleton::new(),
2020-07-03 20:03:45 +00:00
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2020-06-30 04:28:09 +00:00
_ => anim::quadruped_medium::IdleAnimation::update_skeleton(
&QuadrupedMediumSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
};
let target_bones = match &character {
CharacterState::BasicMelee(_) => {
anim::quadruped_medium::AlphaAnimation::update_skeleton(
&target_base,
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
2019-08-21 01:19:02 +00:00
// TODO!
2020-06-30 04:28:09 +00:00
_ => target_base,
2019-08-21 01:19:02 +00:00
};
2020-06-30 04:28:09 +00:00
state.skeleton.interpolate(&target_bones, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
2020-06-23 21:07:13 +00:00
Body::QuadrupedLow(_) => {
2020-06-01 23:56:50 +00:00
let skeleton_attr = &self
.quadruped_low_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
CameraMode::default(),
None,
)
.1;
let state = self
.quadruped_low_states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, QuadrupedLowSkeleton::new()));
2020-06-01 23:56:50 +00:00
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.same_variant(&last_character.0) {
state.state_time = 0.0;
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => {
anim::quadruped_low::IdleAnimation::update_skeleton(
&QuadrupedLowSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
// Running
(true, true, false) => anim::quadruped_low::RunAnimation::update_skeleton(
&QuadrupedLowSkeleton::new(),
(vel.0.magnitude(), ori, state.last_ori, time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
2020-06-01 23:56:50 +00:00
// In air
(false, _, false) => anim::quadruped_low::JumpAnimation::update_skeleton(
&QuadrupedLowSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
2020-06-01 23:56:50 +00:00
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
is_player,
);
},
Body::BirdMedium(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.bird_medium_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
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.same_variant(&last_character.0) {
state.state_time = 0.0;
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton(
&BirdMediumSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// Running
(true, true, false) => anim::bird_medium::RunAnimation::update_skeleton(
&BirdMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// In air
2020-04-26 01:09:03 +00:00
(false, _, false) => anim::bird_medium::FlyAnimation::update_skeleton(
&BirdMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
Body::FishMedium(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.fish_medium_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
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.same_variant(&last_character.0) {
state.state_time = 0.0;
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => anim::fish_medium::IdleAnimation::update_skeleton(
&FishMediumSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// Running
(true, true, false) => anim::fish_medium::RunAnimation::update_skeleton(
&FishMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// In air
(false, _, false) => anim::fish_medium::JumpAnimation::update_skeleton(
&FishMediumSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
2020-05-09 15:22:08 +00:00
Body::Dragon(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.dragon_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
2019-10-23 04:59:05 +00:00
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.same_variant(&last_character.0) {
state.state_time = 0.0;
2019-10-23 04:59:05 +00:00
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => anim::dragon::IdleAnimation::update_skeleton(
2019-10-23 04:59:05 +00:00
&DragonSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
2019-10-23 04:59:05 +00:00
skeleton_attr,
),
// Running
(true, true, false) => anim::dragon::RunAnimation::update_skeleton(
2019-10-23 04:59:05 +00:00
&DragonSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
2019-10-23 04:59:05 +00:00
skeleton_attr,
),
// In air
2020-05-05 21:42:56 +00:00
(false, _, false) => anim::dragon::FlyAnimation::update_skeleton(
2019-10-23 04:59:05 +00:00
&DragonSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
2019-10-23 04:59:05 +00:00
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
2019-10-23 04:59:05 +00:00
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
2019-10-23 04:59:05 +00:00
);
},
2020-01-26 00:22:48 +00:00
Body::Critter(_) => {
let skeleton_attr = &self
.critter_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
let state = self
.critter_states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, CritterSkeleton::new()));
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.same_variant(&last_character.0) {
state.state_time = 0.0;
2020-01-26 00:22:48 +00:00
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => anim::critter::IdleAnimation::update_skeleton(
2020-01-26 00:22:48 +00:00
&CritterSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
2020-01-26 00:22:48 +00:00
skeleton_attr,
),
// Running
(true, true, false) => anim::critter::RunAnimation::update_skeleton(
2020-01-26 00:22:48 +00:00
&CritterSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
2020-01-26 00:22:48 +00:00
skeleton_attr,
),
// In air
(false, _, false) => anim::critter::JumpAnimation::update_skeleton(
2020-01-26 00:22:48 +00:00
&CritterSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
2020-01-26 00:22:48 +00:00
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
2020-01-26 00:22:48 +00:00
scale,
col,
dt,
state_animation_rate,
2020-01-26 00:22:48 +00:00
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
2020-01-26 00:22:48 +00:00
);
},
Body::BirdSmall(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.bird_small_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
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.same_variant(&last_character.0) {
state.state_time = 0.0;
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => anim::bird_small::IdleAnimation::update_skeleton(
&BirdSmallSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// Running
(true, true, false) => anim::bird_small::RunAnimation::update_skeleton(
&BirdSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// In air
(false, _, false) => anim::bird_small::JumpAnimation::update_skeleton(
&BirdSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
Body::FishSmall(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.fish_small_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
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.same_variant(&last_character.0) {
state.state_time = 0.0;
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => anim::fish_small::IdleAnimation::update_skeleton(
&FishSmallSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// Running
(true, true, false) => anim::fish_small::RunAnimation::update_skeleton(
&FishSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// In air
(false, _, false) => anim::fish_small::JumpAnimation::update_skeleton(
&FishSmallSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
Body::BipedLarge(_) => {
2020-01-26 00:22:48 +00:00
let skeleton_attr = &self
.biped_large_model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
CameraMode::default(),
None,
)
.1;
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.same_variant(&last_character.0) {
state.state_time = 0.0;
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => anim::biped_large::IdleAnimation::update_skeleton(
&BipedLargeSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// Running
(true, true, false) => anim::biped_large::RunAnimation::update_skeleton(
&BipedLargeSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// In air
(false, _, false) => anim::biped_large::JumpAnimation::update_skeleton(
&BipedLargeSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
2020-04-26 01:09:03 +00:00
Body::Golem(_) => {
let skeleton_attr = &self
.golem_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
CameraMode::default(),
None,
)
.1;
let state = self
.golem_states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, GolemSkeleton::new()));
let (character, last_character) = match (character, last_character) {
(Some(c), Some(l)) => (c, l),
_ => continue,
};
if !character.same_variant(&last_character.0) {
state.state_time = 0.0;
}
let target_base = match (
physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water
) {
// Standing
(true, false, false) => anim::golem::IdleAnimation::update_skeleton(
&GolemSkeleton::new(),
time,
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// Running
(true, true, false) => anim::golem::RunAnimation::update_skeleton(
&GolemSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// In air
(false, _, false) => anim::golem::JumpAnimation::update_skeleton(
&GolemSkeleton::new(),
(vel.0.magnitude(), time),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
),
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_base, dt);
state.update(
renderer,
pos.0,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
is_player,
);
},
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,
ori,
scale,
col,
dt,
state_animation_rate,
lpindex,
true,
2020-04-04 19:36:55 +00:00
is_player,
);
},
2019-08-21 01:19:02 +00:00
}
}
// Update lighting (lanterns) for figures
self.update_lighting(scene_data);
// Clear states that have deleted entities.
2019-08-21 01:19:02 +00:00
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));
2020-06-01 23:56:50 +00:00
self.quadruped_low_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));
2020-01-26 00:22:48 +00:00
self.critter_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));
2020-04-26 01:09:03 +00:00
self.golem_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));
}
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
2019-08-21 01:19:02 +00:00
pub fn render(
&mut self,
renderer: &mut Renderer,
state: &State,
player_entity: EcsEntity,
tick: u64,
2019-08-21 01:19:02 +00:00
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,
2020-04-26 01:44:56 +00:00
figure_lod_render_distance: f32,
2019-08-21 01:19:02 +00:00
) {
let ecs = state.ecs();
2019-08-21 01:19:02 +00:00
let character_state_storage = state.read_storage::<common::comp::CharacterState>();
let character_state = character_state_storage.get(player_entity);
for (entity, pos, _, body, _, loadout, _) in (
2019-08-21 01:19:02 +00:00
&ecs.entities(),
&ecs.read_storage::<Pos>(),
ecs.read_storage::<Ori>().maybe(),
2019-08-21 01:19:02 +00:00
&ecs.read_storage::<Body>(),
ecs.read_storage::<Stats>().maybe(),
ecs.read_storage::<Loadout>().maybe(),
2019-08-21 01:19:02 +00:00
ecs.read_storage::<Scale>().maybe(),
)
.join()
2020-03-19 19:36:19 +00:00
// Don't render dead entities
2020-03-20 14:45:36 +00:00
.filter(|(_, _, _, _, stats, _, _)| stats.map_or(true, |s| !s.is_dead))
2019-08-21 01:19:02 +00:00
{
let is_player = entity == player_entity;
2020-04-04 19:36:55 +00:00
if !is_player {
self.render_figure(
renderer,
tick,
globals,
lights,
shadows,
camera,
character_state,
entity,
body,
loadout,
false,
pos.0,
2020-04-26 01:44:56 +00:00
figure_lod_render_distance,
2020-04-04 19:36:55 +00:00
);
}
}
}
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
2020-04-04 19:36:55 +00:00
pub fn render_player(
&mut self,
renderer: &mut Renderer,
state: &State,
player_entity: EcsEntity,
tick: u64,
globals: &Consts<Globals>,
lights: &Consts<Light>,
shadows: &Consts<Shadow>,
camera: &Camera,
2020-04-26 01:44:56 +00:00
figure_lod_render_distance: f32,
2020-04-04 19:36:55 +00:00
) {
let ecs = state.ecs();
let character_state_storage = state.read_storage::<common::comp::CharacterState>();
let character_state = character_state_storage.get(player_entity);
if let (Some(pos), Some(body)) = (
ecs.read_storage::<Pos>().get(player_entity),
ecs.read_storage::<Body>().get(player_entity),
) {
let stats_storage = state.read_storage::<Stats>();
let stats = stats_storage.get(player_entity);
if stats.map_or(false, |s| s.is_dead) {
return;
}
2020-04-04 19:36:55 +00:00
let loadout_storage = ecs.read_storage::<Loadout>();
let loadout = loadout_storage.get(player_entity);
self.render_figure(
renderer,
tick,
globals,
lights,
shadows,
camera,
character_state,
player_entity,
body,
loadout,
true,
pos.0,
2020-04-26 01:44:56 +00:00
figure_lod_render_distance,
2020-04-04 19:36:55 +00:00
);
}
}
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
2020-04-04 19:36:55 +00:00
fn render_figure(
&mut self,
renderer: &mut Renderer,
tick: u64,
globals: &Consts<Globals>,
lights: &Consts<Light>,
shadows: &Consts<Shadow>,
camera: &Camera,
character_state: Option<&CharacterState>,
entity: EcsEntity,
body: &Body,
loadout: Option<&Loadout>,
is_player: bool,
pos: Vec3<f32>,
2020-04-26 01:44:56 +00:00
figure_lod_render_distance: f32,
2020-04-04 19:36:55 +00:00
) {
let player_camera_mode = if is_player {
camera.get_mode()
} else {
CameraMode::default()
};
let character_state = if is_player { character_state } else { None };
let FigureMgr {
model_cache,
critter_model_cache,
quadruped_small_model_cache,
quadruped_medium_model_cache,
2020-06-01 23:56:50 +00:00
quadruped_low_model_cache,
2020-04-04 19:36:55 +00:00
bird_medium_model_cache,
bird_small_model_cache,
dragon_model_cache,
fish_medium_model_cache,
fish_small_model_cache,
biped_large_model_cache,
2020-04-26 01:09:03 +00:00
golem_model_cache,
2020-04-04 19:36:55 +00:00
character_states,
quadruped_small_states,
quadruped_medium_states,
2020-06-01 23:56:50 +00:00
quadruped_low_states,
2020-04-04 19:36:55 +00:00
bird_medium_states,
fish_medium_states,
critter_states,
dragon_states,
bird_small_states,
fish_small_states,
biped_large_states,
2020-04-26 01:09:03 +00:00
golem_states,
2020-04-04 19:36:55 +00:00
object_states,
} = self;
if let Some((locals, bone_consts, model)) = match body {
Body::Humanoid(_) => character_states
.get(&entity)
.filter(|state| state.visible)
.map(|state| {
2020-01-26 00:22:48 +00:00
(
state.locals(),
state.bone_consts(),
&model_cache
.get_or_create_model(
renderer,
*body,
2020-03-15 18:44:47 +00:00
loadout,
2020-01-26 00:22:48 +00:00
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
2020-04-04 19:36:55 +00:00
Body::QuadrupedSmall(_) => quadruped_small_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&quadruped_small_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
Body::QuadrupedMedium(_) => quadruped_medium_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&quadruped_medium_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
2020-06-01 23:56:50 +00:00
Body::QuadrupedLow(_) => quadruped_low_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&quadruped_low_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
2020-04-04 19:36:55 +00:00
Body::BirdMedium(_) => bird_medium_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&bird_medium_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
Body::FishMedium(_) => fish_medium_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&fish_medium_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
Body::Critter(_) => critter_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&critter_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
Body::Dragon(_) => dragon_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&dragon_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
Body::BirdSmall(_) => bird_small_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&bird_small_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
Body::FishSmall(_) => fish_small_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&fish_small_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
Body::BipedLarge(_) => biped_large_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&biped_large_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
2020-04-26 01:09:03 +00:00
Body::Golem(_) => golem_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&golem_model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
2020-04-04 19:36:55 +00:00
Body::Object(_) => object_states.get(&entity).map(|state| {
(
state.locals(),
state.bone_consts(),
&model_cache
.get_or_create_model(
renderer,
*body,
loadout,
tick,
player_camera_mode,
character_state,
)
.0,
)
}),
} {
2020-04-26 01:44:56 +00:00
let figure_low_detail_distance = figure_lod_render_distance * 0.75;
let figure_mid_detail_distance = figure_lod_render_distance * 0.5;
2020-04-24 18:52:44 +00:00
let model = if pos.distance_squared(camera.get_focus_pos())
2020-04-26 01:44:56 +00:00
> figure_low_detail_distance.powf(2.0)
2020-04-24 18:52:44 +00:00
{
&model[2]
2020-04-26 01:44:56 +00:00
} else if pos.distance_squared(camera.get_focus_pos())
> figure_mid_detail_distance.powf(2.0)
{
&model[1]
} else {
&model[0]
};
2020-04-04 19:36:55 +00:00
if is_player {
renderer.render_player(model, globals, locals, bone_consts, lights, shadows);
renderer.render_player_shadow(model, globals, locals, bone_consts, lights, shadows);
2019-08-21 01:19:02 +00:00
} else {
2020-04-04 19:36:55 +00:00
renderer.render_figure(model, globals, locals, bone_consts, lights, shadows);
2019-08-21 01:19:02 +00:00
}
2020-04-04 19:36:55 +00:00
} else {
trace!("Body has no saved figure");
2019-08-21 01:19:02 +00:00
}
}
pub fn figure_count(&self) -> usize {
self.character_states.len()
2020-01-08 17:09:54 +00:00
+ self.quadruped_small_states.len()
+ self.character_states.len()
+ self.quadruped_medium_states.len()
2020-06-01 23:56:50 +00:00
+ self.quadruped_low_states.len()
2020-01-08 17:09:54 +00:00
+ self.bird_medium_states.len()
+ self.fish_medium_states.len()
2020-01-26 00:22:48 +00:00
+ self.critter_states.len()
2020-01-08 17:09:54 +00:00
+ self.dragon_states.len()
+ self.bird_small_states.len()
+ self.fish_small_states.len()
+ self.biped_large_states.len()
2020-04-26 01:09:03 +00:00
+ self.golem_states.len()
2020-01-08 17:09:54 +00:00
+ self.object_states.len()
}
pub fn figure_count_visible(&self) -> usize {
2020-01-08 17:09:54 +00:00
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
2020-06-01 23:56:50 +00:00
.quadruped_low_states
.iter()
.filter(|(_, c)| c.visible)
.count()
2020-01-08 17:09:54 +00:00
+ self
.bird_medium_states
.iter()
.filter(|(_, c)| c.visible)
.count()
+ self
2020-01-26 00:22:48 +00:00
.critter_states
2020-01-08 17:09:54 +00:00
.iter()
.filter(|(_, c)| c.visible)
.count()
+ self.dragon_states.iter().filter(|(_, c)| c.visible).count()
2020-01-26 00:22:48 +00:00
+ self
.fish_medium_states
.iter()
.filter(|(_, c)| c.visible)
.count()
2020-01-08 17:09:54 +00:00
+ 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()
2020-04-26 01:09:03 +00:00
+ self.golem_states.iter().filter(|(_, c)| c.visible).count()
2020-01-08 17:09:54 +00:00
+ 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>,
lantern_offset: Vec3<f32>,
state_time: f64,
2019-08-21 01:19:02 +00:00
skeleton: S,
last_ori: Vec3<f32>,
lpindex: u8,
visible: bool,
2020-07-03 18:30:41 +00:00
last_pos: Option<Vec3<f32>>,
avg_vel: Vec3<f32>,
2019-08-21 01:19:02 +00:00
}
impl<S: Skeleton> FigureState<S> {
pub fn new(renderer: &mut Renderer, skeleton: S) -> Self {
2020-06-17 07:49:14 +00:00
let (bone_mats, lantern_offset) = skeleton.compute_matrices();
let bone_consts = figure_bone_data_from_anim(bone_mats);
2019-08-21 01:19:02 +00:00
Self {
bone_consts: renderer.create_consts(&bone_consts).unwrap(),
2019-08-21 01:19:02 +00:00
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
lantern_offset,
state_time: 0.0,
2019-08-21 01:19:02 +00:00
skeleton,
last_ori: Vec3::zero(),
lpindex: 0,
visible: false,
2020-07-03 18:30:41 +00:00
last_pos: None,
avg_vel: Vec3::zero(),
2019-08-21 01:19:02 +00:00
}
}
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
2019-08-21 01:19:02 +00:00
pub fn update(
&mut self,
renderer: &mut Renderer,
pos: Vec3<f32>,
ori: Vec3<f32>,
scale: f32,
col: Rgba<f32>,
dt: f32,
state_animation_rate: f32,
lpindex: u8,
visible: bool,
2020-04-04 19:36:55 +00:00
is_player: bool,
2019-08-21 01:19:02 +00:00
) {
self.visible = visible;
self.lpindex = lpindex;
// What is going on here?
// (note: that ori is now the slerped ori)
self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt);
self.state_time += (dt * state_animation_rate) as f64;
2019-08-21 01:19:02 +00:00
let mat = Mat4::<f32>::identity()
* Mat4::translation_3d(pos)
2019-08-21 01:19:02 +00:00
* 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));
2020-04-04 19:36:55 +00:00
let locals = FigureLocals::new(mat, col, is_player);
2019-08-21 01:19:02 +00:00
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
2020-06-17 07:49:14 +00:00
let (new_bone_mats, lantern_offset) = self.skeleton.compute_matrices();
let new_bone_consts = figure_bone_data_from_anim(new_bone_mats);
2019-08-21 01:19:02 +00:00
renderer
.update_consts(
&mut self.bone_consts,
&new_bone_consts[0..self.skeleton.bone_count()],
)
2019-08-21 01:19:02 +00:00
.unwrap();
self.lantern_offset = lantern_offset;
2020-07-03 18:30:41 +00:00
let smoothing = (5.0 * dt).min(1.0);
if let Some(last_pos) = self.last_pos {
self.avg_vel = (1.0 - smoothing) * self.avg_vel + smoothing * (pos - last_pos) * dt;
}
self.last_pos = Some(pos);
2019-08-21 01:19:02 +00:00
}
pub fn locals(&self) -> &Consts<FigureLocals> { &self.locals }
2019-08-21 01:19:02 +00:00
pub fn bone_consts(&self) -> &Consts<FigureBoneData> { &self.bone_consts }
2019-08-21 01:19:02 +00:00
pub fn skeleton_mut(&mut self) -> &mut S { &mut self.skeleton }
2019-08-21 01:19:02 +00:00
}
2020-06-17 07:49:14 +00:00
fn figure_bone_data_from_anim(mats: [anim::FigureBoneData; 16]) -> [FigureBoneData; 16] {
[
FigureBoneData::new(mats[0].0),
FigureBoneData::new(mats[1].0),
FigureBoneData::new(mats[2].0),
FigureBoneData::new(mats[3].0),
FigureBoneData::new(mats[4].0),
FigureBoneData::new(mats[5].0),
FigureBoneData::new(mats[6].0),
FigureBoneData::new(mats[7].0),
FigureBoneData::new(mats[8].0),
FigureBoneData::new(mats[9].0),
FigureBoneData::new(mats[10].0),
FigureBoneData::new(mats[11].0),
FigureBoneData::new(mats[12].0),
FigureBoneData::new(mats[13].0),
FigureBoneData::new(mats[14].0),
FigureBoneData::new(mats[15].0),
2020-06-17 07:49:14 +00:00
]
}