mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'scott-c/first-person-model' into 'master'
Add first person models See merge request veloren/veloren!443
This commit is contained in:
commit
66c58840ef
@ -2,7 +2,7 @@ use specs::{Component, FlaggedStorage, HashMapStorage};
|
|||||||
//use specs_idvs::IDVStorage;
|
//use specs_idvs::IDVStorage;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||||
pub enum MovementState {
|
pub enum MovementState {
|
||||||
Stand,
|
Stand,
|
||||||
Run,
|
Run,
|
||||||
@ -22,7 +22,7 @@ impl MovementState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||||
pub enum ActionState {
|
pub enum ActionState {
|
||||||
Idle,
|
Idle,
|
||||||
Wield { time_left: Duration },
|
Wield { time_left: Duration },
|
||||||
@ -57,7 +57,7 @@ impl ActionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||||
pub struct CharacterState {
|
pub struct CharacterState {
|
||||||
pub movement: MovementState,
|
pub movement: MovementState,
|
||||||
pub action: ActionState,
|
pub action: ActionState,
|
||||||
|
@ -164,6 +164,8 @@ impl Scene {
|
|||||||
Body::Humanoid(body),
|
Body::Humanoid(body),
|
||||||
Some(equipment),
|
Some(equipment),
|
||||||
client.get_tick(),
|
client.get_tick(),
|
||||||
|
CameraMode::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
|
@ -7,16 +7,23 @@ use vek::*;
|
|||||||
const NEAR_PLANE: f32 = 0.01;
|
const NEAR_PLANE: f32 = 0.01;
|
||||||
const FAR_PLANE: f32 = 10000.0;
|
const FAR_PLANE: f32 = 10000.0;
|
||||||
|
|
||||||
const INTERP_TIME: f32 = 0.1;
|
const FIRST_PERSON_INTERP_TIME: f32 = 0.05;
|
||||||
|
const THIRD_PERSON_INTERP_TIME: f32 = 0.1;
|
||||||
pub const MIN_ZOOM: f32 = 0.1;
|
pub const MIN_ZOOM: f32 = 0.1;
|
||||||
|
|
||||||
// Possible TODO: Add more modes
|
// Possible TODO: Add more modes
|
||||||
#[derive(PartialEq, Clone, Copy)]
|
#[derive(PartialEq, Clone, Copy, Eq, Hash)]
|
||||||
pub enum CameraMode {
|
pub enum CameraMode {
|
||||||
FirstPerson,
|
FirstPerson,
|
||||||
ThirdPerson,
|
ThirdPerson,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for CameraMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::ThirdPerson
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
tgt_focus: Vec3<f32>,
|
tgt_focus: Vec3<f32>,
|
||||||
focus: Vec3<f32>,
|
focus: Vec3<f32>,
|
||||||
@ -166,11 +173,26 @@ impl Camera {
|
|||||||
// This is horribly frame time dependent, but so is most of the game
|
// This is horribly frame time dependent, but so is most of the game
|
||||||
let delta = self.last_time.replace(time).map_or(0.0, |t| time - t);
|
let delta = self.last_time.replace(time).map_or(0.0, |t| time - t);
|
||||||
if (self.dist - self.tgt_dist).abs() > 0.01 {
|
if (self.dist - self.tgt_dist).abs() > 0.01 {
|
||||||
self.dist = f32::lerp(self.dist, self.tgt_dist, (delta as f32) / INTERP_TIME);
|
self.dist = f32::lerp(
|
||||||
|
self.dist,
|
||||||
|
self.tgt_dist,
|
||||||
|
(delta as f32) / self.interp_time(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.focus - self.tgt_focus).magnitude() > 0.01 {
|
if (self.focus - self.tgt_focus).magnitude() > 0.01 {
|
||||||
self.focus = Vec3::lerp(self.focus, self.tgt_focus, (delta as f32) / INTERP_TIME);
|
self.focus = Vec3::lerp(
|
||||||
|
self.focus,
|
||||||
|
self.tgt_focus,
|
||||||
|
(delta as f32) / self.interp_time(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interp_time(&self) -> f32 {
|
||||||
|
match self.mode {
|
||||||
|
CameraMode::FirstPerson => FIRST_PERSON_INTERP_TIME,
|
||||||
|
CameraMode::ThirdPerson => THIRD_PERSON_INTERP_TIME,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,17 +2,18 @@ use super::load::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
anim::SkeletonAttr,
|
anim::SkeletonAttr,
|
||||||
render::{FigurePipeline, Mesh, Model, Renderer},
|
render::{FigurePipeline, Mesh, Model, Renderer},
|
||||||
|
scene::camera::CameraMode,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::watch::ReloadIndicator,
|
assets::watch::ReloadIndicator,
|
||||||
comp::{Body, Equipment},
|
comp::{Body, CharacterState, Equipment},
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||||
enum FigureKey {
|
enum FigureKey {
|
||||||
Simple(Body),
|
Simple(Body),
|
||||||
Complex(Body, Option<Equipment>),
|
Complex(Body, Option<Equipment>, CameraMode, Option<CharacterState>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FigureModelCache {
|
pub struct FigureModelCache {
|
||||||
@ -34,9 +35,16 @@ impl FigureModelCache {
|
|||||||
body: Body,
|
body: Body,
|
||||||
equipment: Option<&Equipment>,
|
equipment: Option<&Equipment>,
|
||||||
tick: u64,
|
tick: u64,
|
||||||
|
camera_mode: CameraMode,
|
||||||
|
character_state: Option<&CharacterState>,
|
||||||
) -> &(Model<FigurePipeline>, SkeletonAttr) {
|
) -> &(Model<FigurePipeline>, SkeletonAttr) {
|
||||||
let key = if equipment.is_some() {
|
let key = if equipment.is_some() {
|
||||||
FigureKey::Complex(body, equipment.cloned())
|
FigureKey::Complex(
|
||||||
|
body,
|
||||||
|
equipment.cloned(),
|
||||||
|
camera_mode,
|
||||||
|
character_state.cloned(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
FigureKey::Simple(body)
|
FigureKey::Simple(body)
|
||||||
};
|
};
|
||||||
@ -54,27 +62,84 @@ impl FigureModelCache {
|
|||||||
HumHeadSpec::load_watched(&mut self.manifest_indicator);
|
HumHeadSpec::load_watched(&mut self.manifest_indicator);
|
||||||
let bone_meshes = match body {
|
let bone_meshes = match body {
|
||||||
Body::Humanoid(body) => [
|
Body::Humanoid(body) => [
|
||||||
Some(humanoid_head_spec.mesh_head(
|
match camera_mode {
|
||||||
body.race,
|
CameraMode::ThirdPerson => {
|
||||||
body.body_type,
|
Some(humanoid_head_spec.mesh_head(
|
||||||
body.hair_color,
|
body.race,
|
||||||
body.hair_style,
|
body.body_type,
|
||||||
body.beard,
|
body.hair_color,
|
||||||
body.eye_color,
|
body.hair_style,
|
||||||
body.skin,
|
body.beard,
|
||||||
body.eyebrows,
|
body.eye_color,
|
||||||
body.accessory,
|
body.skin,
|
||||||
)),
|
body.eyebrows,
|
||||||
Some(mesh_chest(body.chest)),
|
body.accessory,
|
||||||
Some(mesh_belt(body.belt)),
|
))
|
||||||
Some(mesh_pants(body.pants)),
|
}
|
||||||
Some(mesh_left_hand(body.hand)),
|
CameraMode::FirstPerson => None,
|
||||||
Some(mesh_right_hand(body.hand)),
|
},
|
||||||
Some(mesh_left_foot(body.foot)),
|
match camera_mode {
|
||||||
Some(mesh_right_foot(body.foot)),
|
CameraMode::ThirdPerson => Some(mesh_chest(body.chest)),
|
||||||
Some(mesh_main(equipment.and_then(|e| e.main.as_ref()))),
|
CameraMode::FirstPerson => None,
|
||||||
Some(mesh_left_shoulder(body.shoulder)),
|
},
|
||||||
Some(mesh_right_shoulder(body.shoulder)),
|
match camera_mode {
|
||||||
|
CameraMode::ThirdPerson => Some(mesh_belt(body.belt)),
|
||||||
|
CameraMode::FirstPerson => None,
|
||||||
|
},
|
||||||
|
match camera_mode {
|
||||||
|
CameraMode::ThirdPerson => Some(mesh_pants(body.pants)),
|
||||||
|
CameraMode::FirstPerson => None,
|
||||||
|
},
|
||||||
|
if camera_mode == CameraMode::FirstPerson
|
||||||
|
&& character_state
|
||||||
|
.map(|cs| cs.movement.is_roll())
|
||||||
|
.unwrap_or_default()
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(mesh_left_hand(body.hand))
|
||||||
|
},
|
||||||
|
if character_state
|
||||||
|
.map(|cs| cs.movement.is_roll())
|
||||||
|
.unwrap_or_default()
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(mesh_right_hand(body.hand))
|
||||||
|
},
|
||||||
|
match camera_mode {
|
||||||
|
CameraMode::ThirdPerson => Some(mesh_left_foot(body.foot)),
|
||||||
|
CameraMode::FirstPerson => None,
|
||||||
|
},
|
||||||
|
match camera_mode {
|
||||||
|
CameraMode::ThirdPerson => Some(mesh_right_foot(body.foot)),
|
||||||
|
CameraMode::FirstPerson => None,
|
||||||
|
},
|
||||||
|
if camera_mode != CameraMode::FirstPerson
|
||||||
|
|| character_state
|
||||||
|
.map(|cs| {
|
||||||
|
cs.action.is_attack()
|
||||||
|
|| cs.action.is_block()
|
||||||
|
|| cs.action.is_wield()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
{
|
||||||
|
Some(mesh_main(equipment.and_then(|e| e.main.as_ref())))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
match camera_mode {
|
||||||
|
CameraMode::ThirdPerson => {
|
||||||
|
Some(mesh_left_shoulder(body.shoulder))
|
||||||
|
}
|
||||||
|
CameraMode::FirstPerson => None,
|
||||||
|
},
|
||||||
|
match camera_mode {
|
||||||
|
CameraMode::ThirdPerson => {
|
||||||
|
Some(mesh_right_shoulder(body.shoulder))
|
||||||
|
}
|
||||||
|
CameraMode::FirstPerson => None,
|
||||||
|
},
|
||||||
Some(mesh_draw()),
|
Some(mesh_draw()),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
@ -116,7 +116,14 @@ impl FigureMgr {
|
|||||||
|
|
||||||
let skeleton_attr = &self
|
let skeleton_attr = &self
|
||||||
.model_cache
|
.model_cache
|
||||||
.get_or_create_model(renderer, *body, stats.map(|s| &s.equipment), tick)
|
.get_or_create_model(
|
||||||
|
renderer,
|
||||||
|
*body,
|
||||||
|
stats.map(|s| &s.equipment),
|
||||||
|
tick,
|
||||||
|
CameraMode::default(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
.1;
|
.1;
|
||||||
|
|
||||||
match body {
|
match body {
|
||||||
@ -341,6 +348,11 @@ impl FigureMgr {
|
|||||||
|
|
||||||
let frustum = camera.frustum(client);
|
let frustum = camera.frustum(client);
|
||||||
|
|
||||||
|
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 (
|
for (entity, _, _, _, body, stats, _) in (
|
||||||
&ecs.entities(),
|
&ecs.entities(),
|
||||||
&ecs.read_storage::<Pos>(),
|
&ecs.read_storage::<Pos>(),
|
||||||
@ -381,23 +393,26 @@ impl FigureMgr {
|
|||||||
.get(&entity)
|
.get(&entity)
|
||||||
.map(|state| (state.locals(), state.bone_consts())),
|
.map(|state| (state.locals(), state.bone_consts())),
|
||||||
} {
|
} {
|
||||||
|
let is_player = entity == client.entity();
|
||||||
|
|
||||||
|
let player_camera_mode = if is_player {
|
||||||
|
camera.get_mode()
|
||||||
|
} else {
|
||||||
|
CameraMode::default()
|
||||||
|
};
|
||||||
|
|
||||||
let model = &self
|
let model = &self
|
||||||
.model_cache
|
.model_cache
|
||||||
.get_or_create_model(renderer, *body, stats.map(|s| &s.equipment), tick)
|
.get_or_create_model(
|
||||||
|
renderer,
|
||||||
|
*body,
|
||||||
|
stats.map(|s| &s.equipment),
|
||||||
|
tick,
|
||||||
|
player_camera_mode,
|
||||||
|
if is_player { character_state } else { None },
|
||||||
|
)
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
// Don't render the player's body while in first person mode
|
|
||||||
if camera.get_mode() == CameraMode::FirstPerson
|
|
||||||
&& client
|
|
||||||
.state()
|
|
||||||
.read_storage::<Body>()
|
|
||||||
.get(client.entity())
|
|
||||||
.is_some()
|
|
||||||
&& entity == client.entity()
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.render_figure(model, globals, locals, bone_consts, lights);
|
renderer.render_figure(model, globals, locals, bone_consts, lights);
|
||||||
} else {
|
} else {
|
||||||
debug!("Body has no saved figure");
|
debug!("Body has no saved figure");
|
||||||
|
@ -134,14 +134,28 @@ impl Scene {
|
|||||||
.get(client.entity())
|
.get(client.entity())
|
||||||
.map_or(Vec3::zero(), |pos| pos.0);
|
.map_or(Vec3::zero(), |pos| pos.0);
|
||||||
|
|
||||||
|
let player_rolling = client
|
||||||
|
.state()
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<comp::CharacterState>()
|
||||||
|
.get(client.entity())
|
||||||
|
.map_or(false, |cs| cs.movement.is_roll());
|
||||||
|
|
||||||
// Alter camera position to match player.
|
// Alter camera position to match player.
|
||||||
let tilt = self.camera.get_orientation().y;
|
let tilt = self.camera.get_orientation().y;
|
||||||
let dist = self.camera.get_distance();
|
let dist = self.camera.get_distance();
|
||||||
let up = if self.camera.get_mode() == CameraMode::FirstPerson {
|
|
||||||
1.5
|
let up = match self.camera.get_mode() {
|
||||||
} else {
|
CameraMode::FirstPerson => {
|
||||||
1.2
|
if player_rolling {
|
||||||
|
0.75
|
||||||
|
} else {
|
||||||
|
1.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CameraMode::ThirdPerson => 1.2,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.camera.set_focus_pos(
|
self.camera.set_focus_pos(
|
||||||
player_pos + Vec3::unit_z() * (up + dist * 0.15 - tilt.min(0.0) * dist * 0.75),
|
player_pos + Vec3::unit_z() * (up + dist * 0.15 - tilt.min(0.0) * dist * 0.75),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user