Cache figures more intelligently.

Cache figures for longer, and don't cache character states for the
player except where they actually affect the rendered model.
This commit is contained in:
Joshua Yanovski 2020-08-12 20:15:46 +02:00
parent 0ed801d540
commit c6251a956a
3 changed files with 398 additions and 376 deletions

View File

@ -8,80 +8,159 @@ use anim::Skeleton;
use common::{
assets::watch::ReloadIndicator,
comp::{
item::{armor::ArmorKind, tool::ToolKind, ItemKind},
item::{
armor::{Armor, ArmorKind},
tool::ToolKind,
ItemKind,
},
Body, CharacterState, Loadout,
},
figure::Segment,
vol::BaseVol,
};
use core::convert::TryInto;
use hashbrown::{hash_map::Entry, HashMap};
use std::{
convert::TryInto,
mem::{discriminant, Discriminant},
};
use vek::*;
pub type FigureModelEntry = [FigureModel; 3];
#[allow(clippy::large_enum_variant)] // TODO: Pending review in #587
#[derive(PartialEq, Eq, Hash, Clone)]
enum FigureKey {
Simple(Body),
Complex(Body, CameraMode, CharacterCacheKey),
#[derive(Eq, Hash, PartialEq)]
struct FigureKey {
/// Body pointed to by this key.
body: Body,
/// Extra state.
extra: Option<Box<CharacterCacheKey>>,
}
#[derive(PartialEq, Eq, Hash, Clone)]
/// Character data that should be visible when tools are visible (i.e. in third
/// person or when the character is in a tool-using state).
#[derive(Eq, Hash, PartialEq)]
pub struct CharacterToolKey {
active: Option<ToolKind>,
second: Option<ToolKind>,
}
/// Character data that exists in third person only.
#[derive(Eq, Hash, PartialEq)]
struct CharacterThirdPersonKey {
shoulder: Option<String>,
chest: Option<String>,
belt: Option<String>,
back: Option<String>,
pants: Option<String>,
}
#[derive(Eq, Hash, PartialEq)]
/// NOTE: To avoid spamming the character cache with player models, we try to
/// store only the minimum information required to correctly update the model.
///
/// TODO: Memoize, etc.
struct CharacterCacheKey {
state: Option<Discriminant<CharacterState>>, // TODO: Can this be simplified?
active_tool: Option<ToolKind>,
second_tool: Option<ToolKind>,
shoulder: Option<ArmorKind>,
chest: Option<ArmorKind>,
belt: Option<ArmorKind>,
back: Option<ArmorKind>,
/// Character state that is only visible in third person.
third_person: Option<CharacterThirdPersonKey>,
/// Tool state should be present when a character is either in third person,
/// or is in first person and the character state is tool-using.
///
/// NOTE: This representation could be tightened in various ways to
/// eliminate incorrect states, e.g. setting active_tool to None when no
/// tools are equipped, but currently we are more focused on the big
/// performance impact of recreating the whole model whenever the character
/// state changes, so for now we don't bother with this.
tool: Option<CharacterToolKey>,
lantern: Option<String>,
hand: Option<ArmorKind>,
pants: Option<ArmorKind>,
foot: Option<ArmorKind>,
hand: Option<String>,
foot: Option<String>,
}
impl CharacterCacheKey {
fn from(cs: Option<&CharacterState>, loadout: &Loadout) -> Self {
fn from(cs: Option<&CharacterState>, camera_mode: CameraMode, loadout: &Loadout) -> Self {
let is_first_person = match camera_mode {
CameraMode::FirstPerson => true,
CameraMode::ThirdPerson | CameraMode::Freefly => false,
};
// Third person tools are only modeled when the camera is either not first
// person, or the camera is first person and we are in a tool-using
// state.
let are_tools_visible = !is_first_person
|| cs
.map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield())
// If there's no provided character state but we're still somehow in first person,
// We currently assume there's no need to visually model tools.
//
// TODO: Figure out what to do here, and/or refactor how this works.
.unwrap_or(false);
Self {
state: cs.map(|cs| discriminant(cs)),
active_tool: if let Some(ItemKind::Tool(tool)) =
loadout.active_item.as_ref().map(|i| &i.item.kind)
{
Some(tool.kind.clone())
} else {
// Third person armor is only modeled when the camera mode is not first person.
third_person: if is_first_person {
None
},
second_tool: if let Some(ItemKind::Tool(tool)) =
loadout.second_item.as_ref().map(|i| &i.item.kind)
{
Some(tool.kind.clone())
} else {
None
Some(CharacterThirdPersonKey {
shoulder: if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Shoulder(armor),
..
})) = loadout.shoulder.as_ref().map(|i| &i.kind)
{
Some(armor.clone())
} else {
None
},
chest: if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Chest(armor),
..
})) = loadout.chest.as_ref().map(|i| &i.kind)
{
Some(armor.clone())
} else {
None
},
belt: if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Belt(armor),
..
})) = loadout.belt.as_ref().map(|i| &i.kind)
{
Some(armor.clone())
} else {
None
},
back: if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Back(armor),
..
})) = loadout.back.as_ref().map(|i| &i.kind)
{
Some(armor.clone())
} else {
None
},
pants: if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Pants(armor),
..
})) = loadout.pants.as_ref().map(|i| &i.kind)
{
Some(armor.clone())
} else {
None
},
})
},
shoulder: if let Some(ItemKind::Armor(armor)) =
loadout.shoulder.as_ref().map(|i| &i.kind)
{
Some(armor.kind.clone())
} else {
None
},
chest: if let Some(ItemKind::Armor(armor)) = loadout.chest.as_ref().map(|i| &i.kind) {
Some(armor.kind.clone())
} else {
None
},
belt: if let Some(ItemKind::Armor(armor)) = loadout.belt.as_ref().map(|i| &i.kind) {
Some(armor.kind.clone())
} else {
None
},
back: if let Some(ItemKind::Armor(armor)) = loadout.back.as_ref().map(|i| &i.kind) {
Some(armor.kind.clone())
tool: if are_tools_visible {
Some(CharacterToolKey {
active: if let Some(ItemKind::Tool(tool)) =
loadout.active_item.as_ref().map(|i| &i.item.kind)
{
Some(tool.kind.clone())
} else {
None
},
second: if let Some(ItemKind::Tool(tool)) =
loadout.second_item.as_ref().map(|i| &i.item.kind)
{
Some(tool.kind.clone())
} else {
None
},
})
} else {
None
},
@ -92,18 +171,21 @@ impl CharacterCacheKey {
} else {
None
},
hand: if let Some(ItemKind::Armor(armor)) = loadout.hand.as_ref().map(|i| &i.kind) {
Some(armor.kind.clone())
hand: if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Hand(armor),
..
})) = loadout.hand.as_ref().map(|i| &i.kind)
{
Some(armor.clone())
} else {
None
},
pants: if let Some(ItemKind::Armor(armor)) = loadout.pants.as_ref().map(|i| &i.kind) {
Some(armor.kind.clone())
} else {
None
},
foot: if let Some(ItemKind::Armor(armor)) = loadout.foot.as_ref().map(|i| &i.kind) {
Some(armor.kind.clone())
foot: if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Foot(armor),
..
})) = loadout.foot.as_ref().map(|i| &i.kind)
{
Some(armor.clone())
} else {
None
},
@ -129,11 +211,12 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
}
}
/// NOTE: We deliberately call this function with only the key into the
/// cache, to enforce that the cached state only depends on the key. We
/// may end up using different from this cache eventually, in which case
/// this strategy might change.
fn bone_meshes(
body: Body,
loadout: Option<&Loadout>,
character_state: Option<&CharacterState>,
camera_mode: CameraMode,
FigureKey { body, extra }: &FigureKey,
manifest_indicator: &mut ReloadIndicator,
mut generate_mesh: impl FnMut(Segment, Vec3<f32>) -> BoneMeshes,
) -> [Option<BoneMeshes>; 16] {
@ -152,130 +235,109 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
let humanoid_armor_foot_spec = HumArmorFootSpec::load_watched(manifest_indicator);
let humanoid_main_weapon_spec = HumMainWeaponSpec::load_watched(manifest_indicator);
const DEFAULT_LOADOUT: CharacterCacheKey = CharacterCacheKey {
third_person: None,
tool: None,
lantern: None,
hand: None,
foot: None,
};
// TODO: This is bad code, maybe this method should return Option<_>
let default_loadout = Loadout::default();
let loadout = loadout.unwrap_or(&default_loadout);
let loadout = extra.as_deref().unwrap_or(&DEFAULT_LOADOUT);
let third_person = loadout.third_person.as_ref();
let tool = loadout.tool.as_ref();
let lantern = loadout.lantern.as_deref();
let hand = loadout.hand.as_deref();
let foot = loadout.foot.as_deref();
[
match camera_mode {
CameraMode::ThirdPerson | CameraMode::Freefly => Some(
humanoid_head_spec
.mesh_head(&body, |segment, offset| generate_mesh(segment, offset)),
),
CameraMode::FirstPerson => None,
},
match camera_mode {
CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_chest_spec.mesh_chest(
&body,
loadout,
|segment, offset| generate_mesh(segment, offset),
))
},
CameraMode::FirstPerson => None,
},
match camera_mode {
CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_belt_spec.mesh_belt(
&body,
loadout,
|segment, offset| generate_mesh(segment, offset),
))
},
CameraMode::FirstPerson => None,
},
match camera_mode {
CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_back_spec.mesh_back(
&body,
loadout,
|segment, offset| generate_mesh(segment, offset),
))
},
CameraMode::FirstPerson => None,
},
match camera_mode {
CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_pants_spec.mesh_pants(
&body,
loadout,
|segment, offset| generate_mesh(segment, offset),
))
},
CameraMode::FirstPerson => None,
},
Some(humanoid_armor_hand_spec.mesh_left_hand(
&body,
loadout,
|segment, offset| generate_mesh(segment, offset),
)),
third_person.map(|_| {
humanoid_head_spec
.mesh_head(body, |segment, offset| generate_mesh(segment, offset))
}),
third_person.map(|loadout| {
humanoid_armor_chest_spec.mesh_chest(
body,
loadout.chest.as_deref(),
|segment, offset| generate_mesh(segment, offset),
)
}),
third_person.map(|loadout| {
humanoid_armor_belt_spec.mesh_belt(
body,
loadout.belt.as_deref(),
|segment, offset| generate_mesh(segment, offset),
)
}),
third_person.map(|loadout| {
humanoid_armor_back_spec.mesh_back(
body,
loadout.back.as_deref(),
|segment, offset| generate_mesh(segment, offset),
)
}),
third_person.map(|loadout| {
humanoid_armor_pants_spec.mesh_pants(
body,
loadout.pants.as_deref(),
|segment, offset| generate_mesh(segment, offset),
)
}),
Some(
humanoid_armor_hand_spec.mesh_left_hand(body, hand, |segment, offset| {
generate_mesh(segment, offset)
}),
),
Some(humanoid_armor_hand_spec.mesh_right_hand(
&body,
loadout,
|segment, offset| generate_mesh(segment, offset),
)),
Some(humanoid_armor_foot_spec.mesh_left_foot(
&body,
loadout,
body,
hand,
|segment, offset| generate_mesh(segment, offset),
)),
Some(
humanoid_armor_foot_spec.mesh_left_foot(body, foot, |segment, offset| {
generate_mesh(segment, offset)
}),
),
Some(humanoid_armor_foot_spec.mesh_right_foot(
&body,
loadout,
body,
foot,
|segment, offset| generate_mesh(segment, offset),
)),
match camera_mode {
CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_shoulder_spec.mesh_left_shoulder(
&body,
loadout,
|segment, offset| generate_mesh(segment, offset),
))
},
CameraMode::FirstPerson => None,
},
match camera_mode {
CameraMode::ThirdPerson | CameraMode::Freefly => {
Some(humanoid_armor_shoulder_spec.mesh_right_shoulder(
&body,
loadout,
|segment, offset| generate_mesh(segment, offset),
))
},
CameraMode::FirstPerson => None,
},
third_person.map(|loadout| {
humanoid_armor_shoulder_spec.mesh_left_shoulder(
body,
loadout.shoulder.as_deref(),
|segment, offset| generate_mesh(segment, offset),
)
}),
third_person.map(|loadout| {
humanoid_armor_shoulder_spec.mesh_right_shoulder(
body,
loadout.shoulder.as_deref(),
|segment, offset| generate_mesh(segment, offset),
)
}),
Some(mesh_glider(|segment, offset| {
generate_mesh(segment, offset)
})),
if camera_mode != CameraMode::FirstPerson
|| character_state
.map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield())
.unwrap_or_default()
{
Some(humanoid_main_weapon_spec.mesh_main_weapon(
loadout.active_item.as_ref().map(|i| &i.item.kind),
tool.map(|tool| {
humanoid_main_weapon_spec.mesh_main_weapon(
tool.active.as_ref(),
false,
|segment, offset| generate_mesh(segment, offset),
))
} else {
None
},
if camera_mode != CameraMode::FirstPerson
|| character_state
.map(|cs| cs.is_attack() || cs.is_block() || cs.is_wield())
.unwrap_or_default()
{
Some(humanoid_main_weapon_spec.mesh_main_weapon(
loadout.second_item.as_ref().map(|i| &i.item.kind),
)
}),
tool.map(|tool| {
humanoid_main_weapon_spec.mesh_main_weapon(
tool.second.as_ref(),
true,
|segment, offset| generate_mesh(segment, offset),
))
} else {
None
},
)
}),
Some(humanoid_armor_lantern_spec.mesh_lantern(
&body,
loadout,
body,
lantern,
|segment, offset| generate_mesh(segment, offset),
)),
Some(mesh_hold(|segment, offset| generate_mesh(segment, offset))),
@ -923,14 +985,15 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
for<'a> &'a common::comp::Body: std::convert::TryInto<Skel::Attr>,
Skel::Attr: Default,
{
let key = if let Some(loadout) = loadout {
FigureKey::Complex(
body,
camera_mode,
CharacterCacheKey::from(character_state, loadout),
)
} else {
FigureKey::Simple(body)
let key = FigureKey {
body,
extra: loadout.map(|loadout| {
Box::new(CharacterCacheKey::from(
character_state,
camera_mode,
loadout,
))
}),
};
match self.models.entry(key) {
@ -940,100 +1003,90 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
model
},
Entry::Vacant(v) => {
&v.insert((
{
let skeleton_attr = (&body)
.try_into()
.ok()
.unwrap_or_else(<Skel::Attr as Default>::default);
let key = v.key();
let model = {
let skeleton_attr = (&body)
.try_into()
.ok()
.unwrap_or_else(<Skel::Attr as Default>::default);
let manifest_indicator = &mut self.manifest_indicator;
let mut make_model =
|generate_mesh: for<'a> fn(&mut GreedyMesh<'a>, _, _) -> _| {
let mut greedy = FigureModel::make_greedy();
let mut opaque = Mesh::new();
let mut figure_bounds = Aabb {
min: Vec3::zero(),
max: Vec3::zero(),
};
Self::bone_meshes(
body,
loadout,
character_state,
camera_mode,
manifest_indicator,
|segment, offset| generate_mesh(&mut greedy, segment, offset),
)
.iter()
.enumerate()
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
.for_each(
|(i, (opaque_mesh, bounds))| {
opaque.push_mesh_map(opaque_mesh, |vert| {
vert.with_bone_idx(i as u8)
});
figure_bounds.expand_to_contain(*bounds);
},
);
col_lights
.create_figure(renderer, greedy, (opaque, figure_bounds))
.unwrap()
let manifest_indicator = &mut self.manifest_indicator;
let mut make_model =
|generate_mesh: for<'a> fn(&mut GreedyMesh<'a>, _, _) -> _| {
let mut greedy = FigureModel::make_greedy();
let mut opaque = Mesh::new();
let mut figure_bounds = Aabb {
min: Vec3::zero(),
max: Vec3::zero(),
};
Self::bone_meshes(key, manifest_indicator, |segment, offset| {
generate_mesh(&mut greedy, segment, offset)
})
.iter()
.enumerate()
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
.for_each(|(i, (opaque_mesh, bounds))| {
opaque
.push_mesh_map(opaque_mesh, |vert| vert.with_bone_idx(i as u8));
figure_bounds.expand_to_contain(*bounds);
});
col_lights
.create_figure(renderer, greedy, (opaque, figure_bounds))
.unwrap()
};
fn generate_mesh<'a>(
greedy: &mut GreedyMesh<'a>,
segment: Segment,
offset: Vec3<f32>,
) -> BoneMeshes {
let (opaque, _, _, bounds) =
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
segment,
(greedy, offset, Vec3::one()),
);
(opaque, bounds)
}
fn generate_mesh<'a>(
greedy: &mut GreedyMesh<'a>,
segment: Segment,
offset: Vec3<f32>,
) -> BoneMeshes {
let (opaque, _, _, bounds) =
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
segment,
(greedy, offset, Vec3::one()),
);
(opaque, bounds)
}
fn generate_mesh_lod_mid<'a>(
greedy: &mut GreedyMesh<'a>,
segment: Segment,
offset: Vec3<f32>,
) -> BoneMeshes {
let lod_scale = Vec3::broadcast(0.6);
let (opaque, _, _, bounds) =
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
segment.scaled_by(lod_scale),
(greedy, offset * lod_scale, Vec3::one() / lod_scale),
);
(opaque, bounds)
}
fn generate_mesh_lod_mid<'a>(
greedy: &mut GreedyMesh<'a>,
segment: Segment,
offset: Vec3<f32>,
) -> BoneMeshes {
let lod_scale = Vec3::broadcast(0.6);
let (opaque, _, _, bounds) =
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
segment.scaled_by(lod_scale),
(greedy, offset * lod_scale, Vec3::one() / lod_scale),
);
(opaque, bounds)
}
fn generate_mesh_lod_low<'a>(
greedy: &mut GreedyMesh<'a>,
segment: Segment,
offset: Vec3<f32>,
) -> BoneMeshes {
let lod_scale = Vec3::broadcast(0.3);
let segment = segment.scaled_by(lod_scale);
let (opaque, _, _, bounds) =
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
segment,
(greedy, offset * lod_scale, Vec3::one() / lod_scale),
);
(opaque, bounds)
}
fn generate_mesh_lod_low<'a>(
greedy: &mut GreedyMesh<'a>,
segment: Segment,
offset: Vec3<f32>,
) -> BoneMeshes {
let lod_scale = Vec3::broadcast(0.3);
let segment = segment.scaled_by(lod_scale);
let (opaque, _, _, bounds) =
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
segment,
(greedy, offset * lod_scale, Vec3::one() / lod_scale),
);
(opaque, bounds)
}
(
[
make_model(generate_mesh),
make_model(generate_mesh_lod_mid),
make_model(generate_mesh_lod_low),
],
skeleton_attr,
)
},
tick,
))
.0
(
[
make_model(generate_mesh),
make_model(generate_mesh_lod_mid),
make_model(generate_mesh_lod_low),
],
skeleton_attr,
)
};
&v.insert((model, tick)).0
},
}
}
@ -1048,7 +1101,9 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
// TODO: Don't hard-code this.
if tick % 60 == 0 {
self.models.retain(|_, ((models, _), last_used)| {
let alive = *last_used + 60 > tick;
// Wait about a minute at 60 fps before invalidating old models.
let delta = 60 * 60;
let alive = *last_used + delta > tick;
if !alive {
models.iter().for_each(|model| {
col_lights.atlas.deallocate(model.allocation.id);

View File

@ -10,16 +10,11 @@ use common::{
fish_medium, fish_small,
golem::{BodyType as GBodyType, Species as GSpecies},
humanoid::{Body, BodyType, EyeColor, Skin, Species},
item::{
armor::{Armor, ArmorKind},
tool::{Tool, ToolKind},
ItemKind, Lantern,
},
item::tool::ToolKind,
object,
quadruped_low::{BodyType as QLBodyType, Species as QLSpecies},
quadruped_medium::{BodyType as QMBodyType, Species as QMSpecies},
quadruped_small::{BodyType as QSBodyType, Species as QSSpecies},
Loadout,
},
figure::{DynaUnionizer, MatSegment, Material, Segment},
};
@ -361,15 +356,11 @@ impl HumArmorShoulderSpec {
fn mesh_shoulder(
&self,
body: &Body,
loadout: &Loadout,
shoulder: Option<&str>,
flipped: bool,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Shoulder(shoulder),
..
})) = loadout.shoulder.as_ref().map(|i| &i.kind)
{
let spec = if let Some(shoulder) = shoulder {
match self.0.map.get(shoulder) {
Some(spec) => spec,
None => {
@ -419,19 +410,19 @@ impl HumArmorShoulderSpec {
pub fn mesh_left_shoulder(
&self,
body: &Body,
loadout: &Loadout,
shoulder: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
self.mesh_shoulder(body, loadout, true, generate_mesh)
self.mesh_shoulder(body, shoulder, true, generate_mesh)
}
pub fn mesh_right_shoulder(
&self,
body: &Body,
loadout: &Loadout,
shoulder: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
self.mesh_shoulder(body, loadout, false, generate_mesh)
self.mesh_shoulder(body, shoulder, false, generate_mesh)
}
}
// Chest
@ -444,18 +435,14 @@ impl HumArmorChestSpec {
pub fn mesh_chest(
&self,
body: &Body,
loadout: &Loadout,
chest: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Chest(chest),
..
})) = loadout.chest.as_ref().map(|i| &i.kind)
{
let spec = if let Some(chest) = chest {
match self.0.map.get(chest) {
Some(spec) => spec,
None => {
error!(?loadout.chest, "No chest specification exists");
error!(?chest, "No chest specification exists");
return load_mesh("not_found", Vec3::new(-7.0, -3.5, 2.0), generate_mesh);
},
}
@ -500,15 +487,11 @@ impl HumArmorHandSpec {
fn mesh_hand(
&self,
body: &Body,
loadout: &Loadout,
hand: Option<&str>,
flipped: bool,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Hand(hand),
..
})) = loadout.hand.as_ref().map(|i| &i.kind)
{
let spec = if let Some(hand) = hand {
match self.0.map.get(hand) {
Some(spec) => spec,
None => {
@ -552,19 +535,19 @@ impl HumArmorHandSpec {
pub fn mesh_left_hand(
&self,
body: &Body,
loadout: &Loadout,
hand: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
self.mesh_hand(body, loadout, true, generate_mesh)
self.mesh_hand(body, hand, true, generate_mesh)
}
pub fn mesh_right_hand(
&self,
body: &Body,
loadout: &Loadout,
hand: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
self.mesh_hand(body, loadout, false, generate_mesh)
self.mesh_hand(body, hand, false, generate_mesh)
}
}
// Belt
@ -577,14 +560,10 @@ impl HumArmorBeltSpec {
pub fn mesh_belt(
&self,
body: &Body,
loadout: &Loadout,
belt: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Belt(belt),
..
})) = loadout.belt.as_ref().map(|i| &i.kind)
{
let spec = if let Some(belt) = belt {
match self.0.map.get(belt) {
Some(spec) => spec,
None => {
@ -621,14 +600,10 @@ impl HumArmorBackSpec {
pub fn mesh_back(
&self,
body: &Body,
loadout: &Loadout,
back: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Back(back),
..
})) = loadout.back.as_ref().map(|i| &i.kind)
{
let spec = if let Some(back) = back {
match self.0.map.get(back) {
Some(spec) => spec,
None => {
@ -664,14 +639,10 @@ impl HumArmorPantsSpec {
pub fn mesh_pants(
&self,
body: &Body,
loadout: &Loadout,
pants: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Pants(pants),
..
})) = loadout.pants.as_ref().map(|i| &i.kind)
{
let spec = if let Some(pants) = pants {
match self.0.map.get(pants) {
Some(spec) => spec,
None => {
@ -720,15 +691,11 @@ impl HumArmorFootSpec {
fn mesh_foot(
&self,
body: &Body,
loadout: &Loadout,
foot: Option<&str>,
flipped: bool,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Foot(foot),
..
})) = loadout.foot.as_ref().map(|i| &i.kind)
{
let spec = if let Some(foot) = foot {
match self.0.map.get(foot) {
Some(spec) => spec,
None => {
@ -762,19 +729,19 @@ impl HumArmorFootSpec {
pub fn mesh_left_foot(
&self,
body: &Body,
loadout: &Loadout,
foot: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
self.mesh_foot(body, loadout, true, generate_mesh)
self.mesh_foot(body, foot, true, generate_mesh)
}
pub fn mesh_right_foot(
&self,
body: &Body,
loadout: &Loadout,
foot: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
self.mesh_foot(body, loadout, false, generate_mesh)
self.mesh_foot(body, foot, false, generate_mesh)
}
}
@ -786,11 +753,11 @@ impl HumMainWeaponSpec {
pub fn mesh_main_weapon(
&self,
item_kind: Option<&ItemKind>,
tool_kind: Option<&ToolKind>,
flipped: bool,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let tool_kind = if let Some(ItemKind::Tool(Tool { kind, .. })) = item_kind {
let tool_kind = if let Some(kind) = tool_kind {
kind
} else {
return (Mesh::new(), Aabb::default());
@ -835,12 +802,10 @@ impl HumArmorLanternSpec {
pub fn mesh_lantern(
&self,
body: &Body,
loadout: &Loadout,
lantern: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Lantern(Lantern { kind, .. })) =
loadout.lantern.as_ref().map(|i| &i.kind)
{
let spec = if let Some(kind) = lantern {
match self.0.map.get(kind) {
Some(spec) => spec,
None => {
@ -876,14 +841,10 @@ impl HumArmorHeadSpec {
pub fn mesh_head(
&self,
body: &Body,
loadout: &Loadout,
head: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Head(head),
..
})) = loadout.head.as_ref().map(|i| &i.kind)
{
let spec = if let Some(head) = head {
match self.0.map.get(head) {
Some(spec) => spec,
None => {
@ -931,14 +892,10 @@ impl HumArmorTabardSpec {
pub fn mesh_tabard(
&self,
body: &Body,
loadout: &Loadout,
tabard: Option<&str>,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
let spec = if let Some(ItemKind::Armor(Armor {
kind: ArmorKind::Tabard(tabard),
..
})) = loadout.tabard.as_ref().map(|i| &i.kind)
{
let spec = if let Some(tabard) = tabard {
match self.0.map.get(tabard) {
Some(spec) => spec,
None => {
@ -3403,7 +3360,7 @@ impl QuadrupedLowLateralSpec {
///
pub fn mesh_object(
obj: object::Body,
obj: &object::Body,
generate_mesh: impl FnOnce(Segment, Vec3<f32>) -> BoneMeshes,
) -> BoneMeshes {
use object::Body;

View File

@ -481,6 +481,9 @@ impl FigureMgr {
min: player_pos - 2.0,
max: player_pos + 2.0,
};
let camera_mode = camera.get_mode();
let character_state_storage = state.read_storage::<common::comp::CharacterState>();
let character_state = character_state_storage.get(scene_data.player_entity);
let focus_pos = anim::vek::Vec3::<f32>::from(camera.get_focus_pos());
@ -521,6 +524,13 @@ impl FigureMgr {
{
let vel = (anim::vek::Vec3::<f32>::from(vel.0),);
let is_player = scene_data.player_entity == entity;
let player_camera_mode = if is_player {
camera_mode
} else {
CameraMode::default()
};
let player_character_state = if is_player { character_state } else { None };
let (pos, ori) = interpolated
.map(|i| {
(
@ -654,8 +664,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1024,8 +1034,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1125,8 +1135,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1226,8 +1236,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1324,8 +1334,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1420,8 +1430,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1499,8 +1509,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state =
@ -1574,8 +1584,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state =
@ -1650,8 +1660,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1729,8 +1739,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1808,8 +1818,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state = self
@ -1904,8 +1914,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state =
@ -1980,8 +1990,8 @@ impl FigureMgr {
*body,
loadout,
tick,
CameraMode::default(),
None,
player_camera_mode,
player_character_state,
);
let state =