mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'gliding' into 'master'
Gliding See merge request veloren/veloren!140 Former-commit-id: 7e1471af2a250e28557d593944719954e6af4bbe
This commit is contained in:
commit
ef8daef828
BIN
assets/voxygen/voxel/glider.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/glider.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/pigchest.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/pigchest.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/pighead.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/pighead.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/pigleg_l.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/pigleg_l.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/pigleg_r.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/pigleg_r.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -7,6 +7,7 @@ pub enum InputEvent {
|
||||
pub struct Input {
|
||||
pub move_dir: Vec2<f32>,
|
||||
pub jumping: bool,
|
||||
pub gliding: bool,
|
||||
pub events: Vec<InputEvent>,
|
||||
}
|
||||
|
||||
@ -15,6 +16,7 @@ impl Default for Input {
|
||||
Input {
|
||||
move_dir: Vec2::zero(),
|
||||
jumping: false,
|
||||
gliding: false,
|
||||
events: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,11 @@ use common::{
|
||||
terrain::TerrainChunk,
|
||||
};
|
||||
use specs::Builder;
|
||||
use std::{collections::HashSet, net::SocketAddr, time::Duration};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::SocketAddr,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use threadpool::ThreadPool;
|
||||
use vek::*;
|
||||
|
||||
@ -42,7 +46,7 @@ pub struct Client {
|
||||
entity: EcsEntity,
|
||||
view_distance: u64,
|
||||
|
||||
pending_chunks: HashSet<Vec3<i32>>,
|
||||
pending_chunks: HashMap<Vec3<i32>, Instant>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
@ -82,7 +86,7 @@ impl Client {
|
||||
entity,
|
||||
view_distance,
|
||||
|
||||
pending_chunks: HashSet::new(),
|
||||
pending_chunks: HashMap::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -156,6 +160,7 @@ impl Client {
|
||||
comp::Control {
|
||||
move_dir: input.move_dir,
|
||||
jumping: input.jumping,
|
||||
gliding: input.gliding,
|
||||
},
|
||||
);
|
||||
|
||||
@ -202,7 +207,7 @@ impl Client {
|
||||
if (Vec2::from(chunk_pos) - Vec2::from(key))
|
||||
.map(|e: i32| e.abs())
|
||||
.reduce_max()
|
||||
> 6
|
||||
> 10
|
||||
{
|
||||
chunks_to_remove.push(key);
|
||||
}
|
||||
@ -213,18 +218,18 @@ impl Client {
|
||||
|
||||
// Request chunks from the server
|
||||
// TODO: This is really not very efficient
|
||||
'outer: for dist in 0..9 {
|
||||
'outer: for dist in 0..10 {
|
||||
for i in chunk_pos.x - dist..chunk_pos.x + dist + 1 {
|
||||
for j in chunk_pos.y - dist..chunk_pos.y + dist + 1 {
|
||||
for k in 0..3 {
|
||||
for k in 0..6 {
|
||||
let key = Vec3::new(i, j, k);
|
||||
if self.state.terrain().get_key(key).is_none()
|
||||
&& !self.pending_chunks.contains(&key)
|
||||
&& !self.pending_chunks.contains_key(&key)
|
||||
{
|
||||
if self.pending_chunks.len() < 4 {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::TerrainChunkRequest { key });
|
||||
self.pending_chunks.insert(key);
|
||||
self.pending_chunks.insert(key, Instant::now());
|
||||
} else {
|
||||
break 'outer;
|
||||
}
|
||||
@ -233,6 +238,11 @@ impl Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If chunks are taking too long, assume they're no longer pending
|
||||
let now = Instant::now();
|
||||
self.pending_chunks
|
||||
.retain(|_, created| now.duration_since(*created) < Duration::from_secs(10));
|
||||
}
|
||||
|
||||
// Finish the tick, pass control back to the frontend (step 6)
|
||||
|
@ -69,6 +69,26 @@ pub enum Shoulder {
|
||||
pub enum Draw {
|
||||
Default,
|
||||
}
|
||||
////
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Pighead {
|
||||
Default,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Pigchest {
|
||||
Default,
|
||||
}
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Pigleg_l {
|
||||
Default,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Pigleg_r {
|
||||
Default,
|
||||
}
|
||||
////
|
||||
|
||||
const ALL_RACES: [Race; 6] = [
|
||||
Race::Danari,
|
||||
@ -129,10 +149,47 @@ impl HumanoidBody {
|
||||
}
|
||||
}
|
||||
}
|
||||
const ALL_QRACES: [Race; 6] = [
|
||||
Race::Danari,
|
||||
Race::Dwarf,
|
||||
Race::Elf,
|
||||
Race::Human,
|
||||
Race::Orc,
|
||||
Race::Undead,
|
||||
];
|
||||
const ALL_QBODY_TYPES: [BodyType; 3] = [BodyType::Female, BodyType::Male, BodyType::Unspecified];
|
||||
const ALL_QHEADS: [Pighead; 1] = [Pighead::Default];
|
||||
const ALL_QCHESTS: [Pigchest; 1] = [Pigchest::Default];
|
||||
const ALL_QPIGLEG_LS: [Pigleg_l; 1] = [Pigleg_l::Default];
|
||||
const ALL_QPIGLEG_RS: [Pigleg_r; 1] = [Pigleg_r::Default];
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct QuadrupedBody {
|
||||
pub race: Race,
|
||||
pub body_type: BodyType,
|
||||
pub pighead: Pighead,
|
||||
pub pigchest: Pigchest,
|
||||
pub pigleg_l: Pigleg_l,
|
||||
pub pigleg_r: Pigleg_r,
|
||||
}
|
||||
|
||||
impl QuadrupedBody {
|
||||
pub fn random() -> Self {
|
||||
Self {
|
||||
race: *thread_rng().choose(&ALL_QRACES).unwrap(),
|
||||
body_type: *thread_rng().choose(&ALL_QBODY_TYPES).unwrap(),
|
||||
pighead: *thread_rng().choose(&ALL_QHEADS).unwrap(),
|
||||
pigchest: *thread_rng().choose(&ALL_QCHESTS).unwrap(),
|
||||
pigleg_l: *thread_rng().choose(&ALL_QPIGLEG_LS).unwrap(),
|
||||
pigleg_r: *thread_rng().choose(&ALL_QPIGLEG_RS).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Body {
|
||||
Humanoid(HumanoidBody),
|
||||
Quadruped(QuadrupedBody),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
@ -166,6 +223,7 @@ pub enum Animation {
|
||||
Idle,
|
||||
Run,
|
||||
Jump,
|
||||
Gliding,
|
||||
}
|
||||
|
||||
impl Component for AnimationHistory {
|
||||
|
@ -18,6 +18,7 @@ impl Component for Agent {
|
||||
pub struct Control {
|
||||
pub move_dir: Vec2<f32>,
|
||||
pub jumping: bool,
|
||||
pub gliding: bool,
|
||||
}
|
||||
|
||||
impl Default for Control {
|
||||
@ -25,6 +26,7 @@ impl Default for Control {
|
||||
Self {
|
||||
move_dir: Vec2::zero(),
|
||||
jumping: false,
|
||||
gliding: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ pub mod actor;
|
||||
pub mod agent;
|
||||
pub mod phys;
|
||||
pub mod player;
|
||||
pub mod stats;
|
||||
|
||||
// Reexports
|
||||
pub use actor::Actor;
|
||||
@ -9,5 +10,7 @@ pub use actor::Animation;
|
||||
pub use actor::AnimationHistory;
|
||||
pub use actor::Body;
|
||||
pub use actor::HumanoidBody;
|
||||
pub use actor::QuadrupedBody;
|
||||
pub use agent::{Agent, Control};
|
||||
pub use player::Player;
|
||||
pub use stats::Stats;
|
||||
|
@ -7,7 +7,7 @@ use vek::*;
|
||||
pub struct Pos(pub Vec3<f32>);
|
||||
|
||||
impl Component for Pos {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
|
||||
// Vel
|
||||
@ -16,7 +16,7 @@ impl Component for Pos {
|
||||
pub struct Vel(pub Vec3<f32>);
|
||||
|
||||
impl Component for Vel {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
|
||||
// Dir
|
||||
@ -25,7 +25,7 @@ impl Component for Vel {
|
||||
pub struct Dir(pub Vec3<f32>);
|
||||
|
||||
impl Component for Dir {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
|
||||
// Dir
|
||||
|
38
common/src/comp/stats.rs
Normal file
38
common/src/comp/stats.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use specs::{Component, FlaggedStorage, VecStorage};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Health {
|
||||
pub current: u32,
|
||||
pub maximum: u32,
|
||||
pub last_change: Option<(i32, f64)>,
|
||||
}
|
||||
|
||||
impl Health {
|
||||
pub fn change_by(&mut self, amount: i32, current_time: f64) {
|
||||
self.current = (self.current as i32 + amount).max(0) as u32;
|
||||
self.last_change = Some((amount, current_time));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Stats {
|
||||
pub hp: Health,
|
||||
pub xp: u32,
|
||||
}
|
||||
|
||||
impl Default for Stats {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hp: Health {
|
||||
current: 100,
|
||||
maximum: 100,
|
||||
last_change: None,
|
||||
},
|
||||
xp: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Stats {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
}
|
@ -9,7 +9,7 @@ pub enum ClientMsg {
|
||||
},
|
||||
Character {
|
||||
name: String,
|
||||
body: comp::HumanoidBody,
|
||||
body: comp::Body,
|
||||
},
|
||||
RequestState(ClientState),
|
||||
Ping,
|
||||
|
@ -11,6 +11,7 @@ sphynx::sum_type! {
|
||||
Dir(comp::phys::Dir),
|
||||
Actor(comp::Actor),
|
||||
Player(comp::Player),
|
||||
Stats(comp::Stats),
|
||||
}
|
||||
}
|
||||
// Automatically derive From<T> for Phantom for each variant Phantom::T(PhantomData<T>)
|
||||
@ -22,6 +23,7 @@ sphynx::sum_type! {
|
||||
Dir(PhantomData<comp::phys::Dir>),
|
||||
Actor(PhantomData<comp::Actor>),
|
||||
Player(PhantomData<comp::Player>),
|
||||
Stats(PhantomData<comp::Stats>),
|
||||
}
|
||||
}
|
||||
impl sphynx::Packet for EcsPacket {
|
||||
|
@ -97,6 +97,7 @@ impl State {
|
||||
// Register synced components
|
||||
ecs.register_synced::<comp::Actor>();
|
||||
ecs.register_synced::<comp::Player>();
|
||||
ecs.register_synced::<comp::Stats>();
|
||||
|
||||
// Register unsynched (or synced by other means) components
|
||||
ecs.register::<comp::phys::Pos>();
|
||||
|
@ -41,7 +41,7 @@ impl<'a> System<'a> for Sys {
|
||||
let dist = tgt_pos.distance(pos.0);
|
||||
control.move_dir = if dist > 5.0 {
|
||||
Vec2::from(tgt_pos - pos.0).normalized()
|
||||
} else if dist < 1.5 && pos.0 != tgt_pos {
|
||||
} else if dist < 1.5 && dist > 0.0 {
|
||||
Vec2::from(pos.0 - tgt_pos).normalized()
|
||||
} else {
|
||||
Vec2::zero()
|
||||
|
@ -41,27 +41,55 @@ impl<'a> System<'a> for Sys {
|
||||
.unwrap_or(false)
|
||||
&& vel.0.z <= 0.0;
|
||||
|
||||
if on_ground {
|
||||
let (gliding, friction) = if on_ground {
|
||||
// TODO: Don't hard-code this
|
||||
// Apply physics to the player: acceleration and non-linear decceleration
|
||||
vel.0 += control.move_dir * 4.0 - vel.0.map(|e| e * vel.0.magnitude() + e) * 0.05;
|
||||
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 200.0;
|
||||
|
||||
if control.jumping {
|
||||
vel.0.z += 16.0;
|
||||
}
|
||||
|
||||
(false, 0.15)
|
||||
} else {
|
||||
// TODO: Don't hard-code this
|
||||
// Apply physics to the player: acceleration and non-linear decceleration
|
||||
vel.0 += control.move_dir * 0.2 - vel.0.map(|e| e * e.abs() + e) * 0.002;
|
||||
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 10.0;
|
||||
|
||||
if control.gliding && vel.0.z < 0.0 {
|
||||
// TODO: Don't hard-code this
|
||||
let anti_grav = 9.81 * 3.95 + vel.0.z.powf(2.0) * 0.2;
|
||||
vel.0.z +=
|
||||
dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
|
||||
|
||||
(true, 0.008)
|
||||
} else {
|
||||
(false, 0.015)
|
||||
}
|
||||
};
|
||||
|
||||
// Friction
|
||||
vel.0 -= Vec2::broadcast(dt.0)
|
||||
* 50.0
|
||||
* vel.0.map(|e| {
|
||||
(e.abs() * friction * (vel.0.magnitude() * 0.1 + 0.5))
|
||||
.min(e.abs())
|
||||
.copysign(e)
|
||||
})
|
||||
* Vec3::new(1.0, 1.0, 0.0);
|
||||
|
||||
if vel.0.magnitude_squared() != 0.0 {
|
||||
dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
|
||||
}
|
||||
|
||||
let animation = if on_ground {
|
||||
if control.move_dir.magnitude() > 0.01 {
|
||||
dir.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
|
||||
Animation::Run
|
||||
} else {
|
||||
Animation::Idle
|
||||
}
|
||||
} else if gliding {
|
||||
Animation::Gliding
|
||||
} else {
|
||||
Animation::Jump
|
||||
};
|
||||
|
@ -194,7 +194,7 @@ fn handle_pet(server: &mut Server, entity: EcsEntity, args: String, action: &Cha
|
||||
server
|
||||
.create_npc(
|
||||
"Bungo".to_owned(),
|
||||
comp::Body::Humanoid(comp::HumanoidBody::random()),
|
||||
comp::Body::Quadruped(comp::QuadrupedBody::random()),
|
||||
)
|
||||
.with(comp::Control::default())
|
||||
.with(comp::Agent::Pet {
|
||||
|
@ -125,6 +125,7 @@ impl Server {
|
||||
.with(comp::phys::Dir(Vec3::unit_y()))
|
||||
.with(comp::AnimationHistory::new(comp::Animation::Idle))
|
||||
.with(comp::Actor::Character { name, body })
|
||||
.with(comp::Stats::default())
|
||||
}
|
||||
|
||||
pub fn create_player_character(
|
||||
@ -132,15 +133,10 @@ impl Server {
|
||||
entity: EcsEntity,
|
||||
client: &mut Client,
|
||||
name: String,
|
||||
body: comp::HumanoidBody,
|
||||
body: comp::Body,
|
||||
) {
|
||||
state.write_component(
|
||||
entity,
|
||||
comp::Actor::Character {
|
||||
name,
|
||||
body: comp::Body::Humanoid(body),
|
||||
},
|
||||
);
|
||||
state.write_component(entity, comp::Actor::Character { name, body });
|
||||
state.write_component(entity, comp::Stats::default());
|
||||
state.write_component(entity, comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)));
|
||||
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
||||
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
|
||||
|
@ -10,6 +10,7 @@ flat in uint f_bone_idx;
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
mat4 model_mat;
|
||||
vec4 model_col;
|
||||
};
|
||||
|
||||
struct BoneData {
|
||||
@ -36,5 +37,5 @@ void main() {
|
||||
|
||||
float sun_diffuse = dot(sun_dir, world_norm) * 0.5;
|
||||
|
||||
tgt_color = vec4(f_col * (ambient + sun_diffuse), 1.0);
|
||||
tgt_color = model_col * vec4(f_col * (ambient + sun_diffuse), 1.0);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ in uint v_bone_idx;
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
mat4 model_mat;
|
||||
vec4 model_col;
|
||||
};
|
||||
|
||||
struct BoneData {
|
||||
|
107
voxygen/src/anim/character/gliding.rs
Normal file
107
voxygen/src/anim/character/gliding.rs
Normal file
@ -0,0 +1,107 @@
|
||||
// Standard
|
||||
use std::{f32::consts::PI, ops::Mul};
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
// Local
|
||||
use super::{super::Animation, CharacterSkeleton, SCALE};
|
||||
//
|
||||
|
||||
pub struct GlidingAnimation;
|
||||
|
||||
impl Animation for GlidingAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = f64;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
global_time: f64,
|
||||
anim_time: f64,
|
||||
) -> Self::Skeleton {
|
||||
let mut next = (*skeleton).clone();
|
||||
let wave = (anim_time as f32 * 14.0).sin();
|
||||
let waveslow = (anim_time as f32 * 7.0).sin();
|
||||
let wavecos_slow = (anim_time as f32 * 7.0).cos();
|
||||
let arcwave = (1.0f32.ln_1p() - 1.5).abs();
|
||||
let wavetest = (wave.cbrt());
|
||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
||||
let wave_stop = (anim_time as f32 * 1.5).min(PI / 2.0).sin();
|
||||
let wave_stopalt = (anim_time as f32 * 5.0).min(PI / 2.0).sin();
|
||||
let waveveryslow = (anim_time as f32 * 3.0).sin();
|
||||
let waveveryslowalt = (anim_time as f32 * 2.5).sin();
|
||||
let waveveryslowcos = (anim_time as f32 * 3.0).cos();
|
||||
|
||||
let wave_slowtest = (anim_time as f32).min(PI / 2.0).sin();
|
||||
|
||||
let head_look = Vec2::new(
|
||||
((global_time + anim_time) as f32 / 4.0)
|
||||
.floor()
|
||||
.mul(7331.0)
|
||||
.sin()
|
||||
* 0.5,
|
||||
((global_time + anim_time) as f32 / 4.0)
|
||||
.floor()
|
||||
.mul(1337.0)
|
||||
.sin()
|
||||
* 0.25,
|
||||
);
|
||||
next.head.offset = Vec3::new(5.5, 2.0, 12.0);
|
||||
next.head.ori = Quaternion::rotation_x(0.35 - waveveryslow * 0.10 + head_look.y)
|
||||
* Quaternion::rotation_z(head_look.x + waveveryslowcos * 0.15);
|
||||
next.head.scale = Vec3::one();
|
||||
|
||||
next.chest.offset = Vec3::new(5.5, 0.0, 8.0);
|
||||
next.chest.ori = Quaternion::rotation_z(waveveryslowcos * 0.15);
|
||||
next.chest.scale = Vec3::one();
|
||||
|
||||
next.belt.offset = Vec3::new(5.5, 0.0, 6.0);
|
||||
next.belt.ori = Quaternion::rotation_z(waveveryslowcos * 0.20);
|
||||
next.belt.scale = Vec3::one();
|
||||
|
||||
next.shorts.offset = Vec3::new(5.5, 0.0, 3.0);
|
||||
next.shorts.ori = Quaternion::rotation_z(waveveryslowcos * 0.25);
|
||||
next.shorts.scale = Vec3::one();
|
||||
|
||||
next.l_hand.offset = Vec3::new(-8.0, -10.0 + waveveryslow * 2.5, 18.5 + waveveryslow * 1.0);
|
||||
next.l_hand.ori = Quaternion::rotation_x(0.9 - waveveryslow * 0.10);
|
||||
next.l_hand.scale = Vec3::one();
|
||||
|
||||
next.r_hand.offset = Vec3::new(11.0, -10.0 + waveveryslow * 2.5, 18.5 + waveveryslow * 1.0);
|
||||
next.r_hand.ori = Quaternion::rotation_x(0.9 - waveveryslow * 0.10);
|
||||
next.r_hand.scale = Vec3::one();
|
||||
|
||||
next.l_foot.offset = Vec3::new(-3.4, 1.0, 8.0);
|
||||
next.l_foot.ori =
|
||||
Quaternion::rotation_x(wave_stop * -0.7 - wavecos_slow * -0.21 + waveveryslow * 0.19);
|
||||
next.l_foot.scale = Vec3::one();
|
||||
|
||||
next.r_foot.offset = Vec3::new(3.4, 1.0, 8.0);
|
||||
next.r_foot.ori =
|
||||
Quaternion::rotation_x(wave_stop * -0.8 + waveslow * -0.25 + waveveryslowalt * 0.13);
|
||||
next.r_foot.scale = Vec3::one();
|
||||
|
||||
next.weapon.offset = Vec3::new(-5.0, -6.0, 19.0);
|
||||
next.weapon.ori = Quaternion::rotation_y(2.5);
|
||||
next.weapon.scale = Vec3::one();
|
||||
|
||||
next.l_shoulder.offset = Vec3::new(-10.0, -3.0, 2.5);
|
||||
next.l_shoulder.ori = Quaternion::rotation_x(0.0);
|
||||
next.l_shoulder.scale = Vec3::one();
|
||||
|
||||
next.r_shoulder.offset = Vec3::new(0.0, -3.0, 2.5);
|
||||
next.r_shoulder.ori = Quaternion::rotation_x(0.0);
|
||||
next.r_shoulder.scale = Vec3::one();
|
||||
|
||||
next.torso.offset = Vec3::new(-0.5, -0.2, 0.0);
|
||||
next.torso.ori = Quaternion::rotation_x(-0.8 + waveveryslow * 0.10);
|
||||
next.torso.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.draw.offset = Vec3::new(13.5, 3.0, -1.0);
|
||||
next.draw.ori = Quaternion::rotation_y(waveveryslowcos * 0.05);
|
||||
next.draw.scale = Vec3::one();
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -100,9 +100,9 @@ impl Animation for IdleAnimation {
|
||||
next.torso.ori = Quaternion::rotation_x(0.0);
|
||||
next.torso.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.draw.offset = Vec3::new(0.0, 1.0, -8.0);
|
||||
next.draw.ori = Quaternion::rotation_y(-0.2);
|
||||
next.draw.scale = Vec3::one();
|
||||
next.draw.offset = Vec3::new(13.5, 0.0, 0.0);
|
||||
next.draw.ori = Quaternion::rotation_y(0.0);
|
||||
next.draw.scale = Vec3::one() * 0.0;
|
||||
next
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,9 @@ impl Animation for JumpAnimation {
|
||||
next.torso.ori = Quaternion::rotation_x(-0.2);
|
||||
next.torso.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.draw.offset = Vec3::new(13.5, 0.0, 0.0);
|
||||
next.draw.ori = Quaternion::rotation_y(0.0);
|
||||
next.draw.scale = Vec3::one() * 0.0;
|
||||
next
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
pub mod gliding;
|
||||
pub mod idle;
|
||||
pub mod jump;
|
||||
pub mod run;
|
||||
|
||||
// Reexports
|
||||
pub use self::gliding::GlidingAnimation;
|
||||
pub use self::idle::IdleAnimation;
|
||||
pub use self::jump::JumpAnimation;
|
||||
pub use self::run::RunAnimation;
|
||||
@ -27,8 +29,8 @@ pub struct CharacterSkeleton {
|
||||
weapon: Bone,
|
||||
l_shoulder: Bone,
|
||||
r_shoulder: Bone,
|
||||
torso: Bone,
|
||||
draw: Bone,
|
||||
torso: Bone,
|
||||
}
|
||||
|
||||
impl CharacterSkeleton {
|
||||
@ -45,8 +47,8 @@ impl CharacterSkeleton {
|
||||
weapon: Bone::default(),
|
||||
l_shoulder: Bone::default(),
|
||||
r_shoulder: Bone::default(),
|
||||
torso: Bone::default(),
|
||||
draw: Bone::default(),
|
||||
torso: Bone::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,8 +70,8 @@ impl Skeleton for CharacterSkeleton {
|
||||
FigureBoneData::new(torso_mat * chest_mat * self.weapon.compute_base_matrix()),
|
||||
FigureBoneData::new(torso_mat * chest_mat * self.l_shoulder.compute_base_matrix()),
|
||||
FigureBoneData::new(torso_mat * chest_mat * self.r_shoulder.compute_base_matrix()),
|
||||
FigureBoneData::new(torso_mat),
|
||||
FigureBoneData::new(torso_mat * l_hand_mat * self.draw.compute_base_matrix()),
|
||||
FigureBoneData::new(torso_mat),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
@ -88,7 +90,7 @@ impl Skeleton for CharacterSkeleton {
|
||||
self.weapon.interpolate(&target.weapon);
|
||||
self.l_shoulder.interpolate(&target.l_shoulder);
|
||||
self.r_shoulder.interpolate(&target.r_shoulder);
|
||||
self.torso.interpolate(&target.torso);
|
||||
self.draw.interpolate(&target.draw);
|
||||
self.torso.interpolate(&target.torso);
|
||||
}
|
||||
}
|
||||
|
@ -71,13 +71,14 @@ impl Animation for RunAnimation {
|
||||
next.r_shoulder.offset = Vec3::new(0.0, -3.0, 2.5);
|
||||
next.r_shoulder.ori = Quaternion::rotation_x(0.0);
|
||||
next.r_shoulder.scale = Vec3::one();
|
||||
|
||||
next.torso.offset = Vec3::new(-0.5, -0.2, 0.4);
|
||||
next.torso.ori = Quaternion::rotation_x(-velocity * 0.05 - wavecos * 0.1);
|
||||
next.torso.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.draw.offset = Vec3::new(0.0, 1.0, -8.0);
|
||||
next.draw.ori = Quaternion::rotation_y(-0.2);
|
||||
next.draw.scale = Vec3::one();
|
||||
next.draw.offset = Vec3::new(13.5, 0.0, 0.0);
|
||||
next.draw.ori = Quaternion::rotation_y(0.0);
|
||||
next.draw.scale = Vec3::one() * 0.0;
|
||||
|
||||
next
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub mod character;
|
||||
|
||||
pub mod quadruped;
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
|
73
voxygen/src/anim/quadruped/idle.rs
Normal file
73
voxygen/src/anim/quadruped/idle.rs
Normal file
@ -0,0 +1,73 @@
|
||||
// Standard
|
||||
use std::{f32::consts::PI, ops::Mul};
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
// Local
|
||||
use super::{super::Animation, QuadrupedSkeleton, SCALE};
|
||||
|
||||
pub struct IdleAnimation;
|
||||
|
||||
impl Animation for IdleAnimation {
|
||||
type Skeleton = QuadrupedSkeleton;
|
||||
type Dependency = (f64);
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
global_time: Self::Dependency,
|
||||
anim_time: f64,
|
||||
) -> Self::Skeleton {
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let wave = (anim_time as f32 * 14.0).sin();
|
||||
let wavetest = (wave.cbrt());
|
||||
let waveultra_slow = (anim_time as f32 * 1.0 + PI).sin();
|
||||
let waveultracos_slow = (anim_time as f32 * 1.0 + PI).cos();
|
||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
||||
let wave_slow = (anim_time as f32 * 3.5 + PI).sin();
|
||||
let wavecos_slow = (anim_time as f32 * 3.5 + PI).cos();
|
||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||
|
||||
let pighead_look = Vec2::new(
|
||||
((global_time + anim_time) as f32 / 8.0)
|
||||
.floor()
|
||||
.mul(7331.0)
|
||||
.sin()
|
||||
* 0.5,
|
||||
((global_time + anim_time) as f32 / 8.0)
|
||||
.floor()
|
||||
.mul(1337.0)
|
||||
.sin()
|
||||
* 0.25,
|
||||
);
|
||||
|
||||
next.pighead.offset = Vec3::new(0.0, -2.0, -1.5 + wave * 0.2) / 11.0;
|
||||
next.pighead.ori = Quaternion::rotation_z(pighead_look.x)
|
||||
* Quaternion::rotation_x(pighead_look.y + wavecos_slow * 0.03);
|
||||
next.pighead.scale = Vec3::one() / 10.5;
|
||||
|
||||
next.pigchest.offset = Vec3::new(wave_slow * 0.05, -9.0, 1.5 + wavecos_slow * 0.4) / 11.0;
|
||||
next.pigchest.ori = Quaternion::rotation_y(wave_slow * 0.05);
|
||||
next.pigchest.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.piglf_leg.offset = Vec3::new(-4.5, 2.0, 1.5) / 11.0;
|
||||
next.piglf_leg.ori = Quaternion::rotation_x(wave_slow * 0.08);
|
||||
next.piglf_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.pigrf_leg.offset = Vec3::new(2.5, 2.0, 1.5) / 11.0;
|
||||
next.pigrf_leg.ori = Quaternion::rotation_x(wavecos_slow * 0.08);
|
||||
next.pigrf_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.piglb_leg.offset = Vec3::new(-4.5, -3.0, 1.5) / 11.0;
|
||||
next.piglb_leg.ori = Quaternion::rotation_x(wavecos_slow * 0.08);
|
||||
next.piglb_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.pigrb_leg.offset = Vec3::new(2.5, -3.0, 1.5) / 11.0;
|
||||
next.pigrb_leg.ori = Quaternion::rotation_x(wave_slow * 0.08);
|
||||
next.pigrb_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next
|
||||
}
|
||||
}
|
58
voxygen/src/anim/quadruped/jump.rs
Normal file
58
voxygen/src/anim/quadruped/jump.rs
Normal file
@ -0,0 +1,58 @@
|
||||
// Standard
|
||||
use std::f32::consts::PI;
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
// Local
|
||||
use super::{super::Animation, QuadrupedSkeleton, SCALE};
|
||||
|
||||
pub struct JumpAnimation;
|
||||
|
||||
impl Animation for JumpAnimation {
|
||||
type Skeleton = QuadrupedSkeleton;
|
||||
type Dependency = (f32, f64);
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
(velocity, global_time): Self::Dependency,
|
||||
anim_time: f64,
|
||||
) -> Self::Skeleton {
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let wave = (anim_time as f32 * 14.0).sin();
|
||||
let wavetest = (wave.cbrt());
|
||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
||||
let wave_slow = (anim_time as f32 * 7.0 + PI).sin();
|
||||
let wavecos_slow = (anim_time as f32 * 8.0 + PI).cos();
|
||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||
let wave_stop = (anim_time as f32 * 4.5).min(PI / 2.0).sin();
|
||||
|
||||
next.pighead.offset = Vec3::new(0.0, 0.0, -1.5) / 11.0;
|
||||
next.pighead.ori = Quaternion::rotation_x(wave_stop * 0.4);
|
||||
next.pighead.scale = Vec3::one() / 10.5;
|
||||
|
||||
next.pigchest.offset = Vec3::new(0.0, -9.0, 1.5) / 11.0;
|
||||
next.pigchest.ori = Quaternion::rotation_x(0.0);
|
||||
next.pigchest.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.piglf_leg.offset = Vec3::new(-4.5, 3.0, 1.5) / 11.0;
|
||||
next.piglf_leg.ori = Quaternion::rotation_x(wave_stop * 0.6 - wave_slow * 0.3);
|
||||
next.piglf_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.pigrf_leg.offset = Vec3::new(2.5, 3.0, 1.5) / 11.0;
|
||||
next.pigrf_leg.ori = Quaternion::rotation_x(wave_stop * 0.6 - wave_slow * 0.3);
|
||||
next.pigrf_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.piglb_leg.offset = Vec3::new(-4.5, -4.0, 2.0) / 11.0;
|
||||
next.piglb_leg.ori = Quaternion::rotation_x(wave_stop * -0.6 + wave_slow * 0.3);
|
||||
next.piglb_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.pigrb_leg.offset = Vec3::new(2.5, -4.0, 2.0) / 11.0;
|
||||
next.pigrb_leg.ori = Quaternion::rotation_x(wave_stop * -0.6 + wave_slow * 0.3);
|
||||
next.pigrb_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next
|
||||
}
|
||||
}
|
71
voxygen/src/anim/quadruped/mod.rs
Normal file
71
voxygen/src/anim/quadruped/mod.rs
Normal file
@ -0,0 +1,71 @@
|
||||
pub mod idle;
|
||||
pub mod jump;
|
||||
pub mod run;
|
||||
|
||||
// Reexports
|
||||
pub use self::idle::IdleAnimation;
|
||||
pub use self::jump::JumpAnimation;
|
||||
pub use self::run::RunAnimation;
|
||||
|
||||
// Crate
|
||||
use crate::render::FigureBoneData;
|
||||
|
||||
// Local
|
||||
use super::{Bone, Skeleton};
|
||||
|
||||
const SCALE: f32 = 11.0;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct QuadrupedSkeleton {
|
||||
pighead: Bone,
|
||||
pigchest: Bone,
|
||||
piglf_leg: Bone,
|
||||
pigrf_leg: Bone,
|
||||
piglb_leg: Bone,
|
||||
pigrb_leg: Bone,
|
||||
}
|
||||
|
||||
impl QuadrupedSkeleton {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pighead: Bone::default(),
|
||||
pigchest: Bone::default(),
|
||||
piglf_leg: Bone::default(),
|
||||
pigrf_leg: Bone::default(),
|
||||
piglb_leg: Bone::default(),
|
||||
pigrb_leg: Bone::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Skeleton for QuadrupedSkeleton {
|
||||
fn compute_matrices(&self) -> [FigureBoneData; 16] {
|
||||
[
|
||||
FigureBoneData::new(self.pighead.compute_base_matrix()),
|
||||
FigureBoneData::new(self.pigchest.compute_base_matrix()),
|
||||
FigureBoneData::new(self.piglf_leg.compute_base_matrix()),
|
||||
FigureBoneData::new(self.pigrf_leg.compute_base_matrix()),
|
||||
FigureBoneData::new(self.piglb_leg.compute_base_matrix()),
|
||||
FigureBoneData::new(self.pigrb_leg.compute_base_matrix()),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
FigureBoneData::default(),
|
||||
]
|
||||
}
|
||||
|
||||
fn interpolate(&mut self, target: &Self) {
|
||||
self.pighead.interpolate(&target.pighead);
|
||||
self.pigchest.interpolate(&target.pigchest);
|
||||
self.piglf_leg.interpolate(&target.piglf_leg);
|
||||
self.pigrf_leg.interpolate(&target.pigrf_leg);
|
||||
self.piglb_leg.interpolate(&target.piglb_leg);
|
||||
self.pigrb_leg.interpolate(&target.pigrb_leg);
|
||||
}
|
||||
}
|
64
voxygen/src/anim/quadruped/run.rs
Normal file
64
voxygen/src/anim/quadruped/run.rs
Normal file
@ -0,0 +1,64 @@
|
||||
// Standard
|
||||
use std::f32::consts::PI;
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
// Local
|
||||
use super::{super::Animation, QuadrupedSkeleton, SCALE};
|
||||
|
||||
pub struct RunAnimation;
|
||||
|
||||
impl Animation for RunAnimation {
|
||||
type Skeleton = QuadrupedSkeleton;
|
||||
type Dependency = (f32, f64);
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
(velocity, global_time): Self::Dependency,
|
||||
anim_time: f64,
|
||||
) -> Self::Skeleton {
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let wave = (anim_time as f32 * 14.0).sin();
|
||||
let wavequick = (anim_time as f32 * 20.0).sin();
|
||||
let wavequickcos = (anim_time as f32 * 20.0).cos();
|
||||
let wavetest = (wave.cbrt());
|
||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
||||
let wave_slow = (anim_time as f32 * 7.0 + PI).sin();
|
||||
let wavecos_slow = (anim_time as f32 * 8.0 + PI).cos();
|
||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||
|
||||
next.pighead.offset = Vec3::new(0.0, 0.0, -1.5 + wave * 1.5) / 11.0;
|
||||
next.pighead.ori =
|
||||
Quaternion::rotation_x(0.2 + wave * 0.05) * Quaternion::rotation_y(wavecos * 0.03);
|
||||
next.pighead.scale = Vec3::one() / 10.5;
|
||||
|
||||
next.pigchest.offset = Vec3::new(0.0, -9.0, 1.5 + wavecos * 1.2) / 11.0;
|
||||
next.pigchest.ori = Quaternion::rotation_x(wave * 0.1);
|
||||
next.pigchest.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.piglf_leg.offset =
|
||||
Vec3::new(-4.5, 2.0 + wavequick * 0.8, 2.5 + wavequickcos * 1.5) / 11.0;
|
||||
next.piglf_leg.ori = Quaternion::rotation_x(wavequick * 0.3);
|
||||
next.piglf_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.pigrf_leg.offset =
|
||||
Vec3::new(2.5, 2.0 - wavequickcos * 0.8, 2.5 + wavequick * 1.5) / 11.0;
|
||||
next.pigrf_leg.ori = Quaternion::rotation_x(wavequickcos * -0.3);
|
||||
next.pigrf_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.piglb_leg.offset =
|
||||
Vec3::new(-4.5, -3.0 - wavequickcos * 0.8, 2.5 + wavequick * 1.5) / 11.0;
|
||||
next.piglb_leg.ori = Quaternion::rotation_x(wavequickcos * -0.3);
|
||||
next.piglb_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next.pigrb_leg.offset =
|
||||
Vec3::new(2.5, -3.0 + wavequick * 0.8, 2.5 + wavequickcos * 1.5) / 11.0;
|
||||
next.pigrb_leg.ori = Quaternion::rotation_x(wavequick * 0.3);
|
||||
next.pigrb_leg.scale = Vec3::one() / 11.0;
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ pub struct KeyState {
|
||||
pub up: bool,
|
||||
pub down: bool,
|
||||
pub jump: bool,
|
||||
pub glide: bool,
|
||||
}
|
||||
|
||||
impl KeyState {
|
||||
@ -16,6 +17,7 @@ impl KeyState {
|
||||
up: false,
|
||||
down: false,
|
||||
jump: false,
|
||||
glide: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
Direction, GlobalState, PlayState, PlayStateResult,
|
||||
};
|
||||
use client::{self, Client};
|
||||
use common::{clock::Clock, msg::ClientMsg};
|
||||
use common::{clock::Clock, comp, msg::ClientMsg};
|
||||
use scene::Scene;
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
use ui::CharSelectionUi;
|
||||
@ -78,7 +78,7 @@ impl PlayState for CharSelectionState {
|
||||
.postbox
|
||||
.send_message(ClientMsg::Character {
|
||||
name: self.char_selection_ui.character_name.clone(),
|
||||
body: self.char_selection_ui.character_body,
|
||||
body: comp::Body::Humanoid(self.char_selection_ui.character_body), //body: comp::Body::Quadruped(comp::QuadrupedBody::random()),
|
||||
});
|
||||
return PlayStateResult::Switch(Box::new(SessionState::new(
|
||||
&mut global_state.window,
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use client::Client;
|
||||
use common::{comp::HumanoidBody, figure::Segment};
|
||||
use common::{comp, figure::Segment};
|
||||
use vek::*;
|
||||
|
||||
struct Skybox {
|
||||
@ -99,8 +99,12 @@ impl Scene {
|
||||
);
|
||||
self.figure_state.skeleton_mut().interpolate(&tgt_skeleton);
|
||||
|
||||
self.figure_state
|
||||
.update(renderer, Vec3::zero(), -Vec3::unit_y());
|
||||
self.figure_state.update(
|
||||
renderer,
|
||||
Vec3::zero(),
|
||||
-Vec3::unit_y(),
|
||||
Rgba::broadcast(1.0),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn render(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
@ -108,7 +112,7 @@ impl Scene {
|
||||
|
||||
let model = self.figure_model_cache.get_or_create_model(
|
||||
renderer,
|
||||
HumanoidBody::random(),
|
||||
comp::Body::Humanoid(comp::HumanoidBody::random()),
|
||||
client.get_tick(),
|
||||
);
|
||||
renderer.render_figure(
|
||||
|
@ -27,6 +27,7 @@ gfx_defines! {
|
||||
|
||||
constant Locals {
|
||||
model_mat: [[f32; 4]; 4] = "model_mat",
|
||||
model_col: [f32; 4] = "model_col",
|
||||
}
|
||||
|
||||
constant BoneData {
|
||||
@ -62,13 +63,17 @@ impl Vertex {
|
||||
}
|
||||
|
||||
impl Locals {
|
||||
pub fn new(model_mat: Mat4<f32>) -> Self {
|
||||
pub fn new(model_mat: Mat4<f32>, col: Rgba<f32>) -> Self {
|
||||
Self {
|
||||
model_mat: arr_to_mat(model_mat.into_col_array()),
|
||||
model_col: col.into_array(),
|
||||
}
|
||||
}
|
||||
pub fn default() -> Self {
|
||||
Self::new(Mat4::identity())
|
||||
}
|
||||
|
||||
impl Default for Locals {
|
||||
fn default() -> Self {
|
||||
Self::new(Mat4::identity(), Rgba::broadcast(1.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
anim::{
|
||||
character::{CharacterSkeleton, IdleAnimation, JumpAnimation, RunAnimation},
|
||||
character::{self, CharacterSkeleton},
|
||||
quadruped::{self, QuadrupedSkeleton},
|
||||
Animation, Skeleton,
|
||||
},
|
||||
mesh::Meshable,
|
||||
@ -14,8 +15,11 @@ use common::{
|
||||
assets,
|
||||
comp::{
|
||||
self,
|
||||
actor::{Belt, Chest, Foot, Hand, Head, Pants, Shoulder, Weapon},
|
||||
Body, HumanoidBody,
|
||||
actor::{
|
||||
Belt, Chest, Draw, Foot, Hand, Head, Pants, Pigchest, Pighead, Pigleg_l, Pigleg_r,
|
||||
Shoulder, Weapon,
|
||||
},
|
||||
Body, HumanoidBody, QuadrupedBody,
|
||||
},
|
||||
figure::Segment,
|
||||
msg,
|
||||
@ -26,7 +30,7 @@ use std::{collections::HashMap, f32};
|
||||
use vek::*;
|
||||
|
||||
pub struct FigureModelCache {
|
||||
models: HashMap<HumanoidBody, (Model<FigurePipeline>, u64)>,
|
||||
models: HashMap<Body, (Model<FigurePipeline>, u64)>,
|
||||
}
|
||||
|
||||
impl FigureModelCache {
|
||||
@ -39,7 +43,7 @@ impl FigureModelCache {
|
||||
pub fn get_or_create_model(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
body: HumanoidBody,
|
||||
body: Body,
|
||||
tick: u64,
|
||||
) -> &Model<FigurePipeline> {
|
||||
match self.models.get_mut(&body) {
|
||||
@ -51,24 +55,44 @@ impl FigureModelCache {
|
||||
body,
|
||||
(
|
||||
{
|
||||
let bone_meshes = [
|
||||
Some(Self::load_head(body.head)),
|
||||
Some(Self::load_chest(body.chest)),
|
||||
Some(Self::load_belt(body.belt)),
|
||||
Some(Self::load_pants(body.pants)),
|
||||
Some(Self::load_left_hand(body.hand)),
|
||||
Some(Self::load_right_hand(body.hand)),
|
||||
Some(Self::load_left_foot(body.foot)),
|
||||
Some(Self::load_right_foot(body.foot)),
|
||||
Some(Self::load_weapon(body.weapon)),
|
||||
Some(Self::load_left_shoulder(body.shoulder)),
|
||||
Some(Self::load_right_shoulder(body.shoulder)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
];
|
||||
let bone_meshes = match body {
|
||||
Body::Humanoid(body) => [
|
||||
Some(Self::load_head(body.head)),
|
||||
Some(Self::load_chest(body.chest)),
|
||||
Some(Self::load_belt(body.belt)),
|
||||
Some(Self::load_pants(body.pants)),
|
||||
Some(Self::load_left_hand(body.hand)),
|
||||
Some(Self::load_right_hand(body.hand)),
|
||||
Some(Self::load_left_foot(body.foot)),
|
||||
Some(Self::load_right_foot(body.foot)),
|
||||
Some(Self::load_weapon(body.weapon)),
|
||||
Some(Self::load_left_shoulder(body.shoulder)),
|
||||
Some(Self::load_right_shoulder(body.shoulder)),
|
||||
Some(Self::load_draw(body.draw)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
],
|
||||
Body::Quadruped(body) => [
|
||||
Some(Self::load_pighead(body.pighead)),
|
||||
Some(Self::load_pigchest(body.pigchest)),
|
||||
Some(Self::load_piglf_leg(body.pigleg_l)),
|
||||
Some(Self::load_pigrf_leg(body.pigleg_r)),
|
||||
Some(Self::load_piglb_leg(body.pigleg_l)),
|
||||
Some(Self::load_pigrb_leg(body.pigleg_r)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
],
|
||||
};
|
||||
|
||||
let mut mesh = Mesh::new();
|
||||
bone_meshes
|
||||
@ -205,29 +229,82 @@ impl FigureModelCache {
|
||||
Vec3::new(2.5, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
// fn load_draw(draw: Draw) -> Mesh<FigurePipeline> {
|
||||
// Self::load_mesh(
|
||||
// match draw {
|
||||
// //Draw::DefaultDraw => "sword.vox",
|
||||
//
|
||||
// },
|
||||
// Vec3::new(0.0, 0.0, -2.0)
|
||||
//
|
||||
//
|
||||
// )
|
||||
// }
|
||||
fn load_draw(draw: Draw) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match draw {
|
||||
Draw::Default => "glider.vox",
|
||||
},
|
||||
Vec3::new(-26.0, -26.0, -5.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_pighead(pighead: Pighead) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match pighead {
|
||||
Pighead::Default => "pighead.vox",
|
||||
},
|
||||
Vec3::new(-6.0, 4.5, 3.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_pigchest(pigchest: Pigchest) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match pigchest {
|
||||
Pigchest::Default => "pigchest.vox",
|
||||
},
|
||||
Vec3::new(-5.0, 4.5, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_piglf_leg(pigleg_l: Pigleg_l) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match pigleg_l {
|
||||
Pigleg_l::Default => "pigleg_l.vox",
|
||||
},
|
||||
Vec3::new(0.0, -1.0, -1.5),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_pigrf_leg(pigleg_r: Pigleg_r) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match pigleg_r {
|
||||
Pigleg_r::Default => "pigleg_r.vox",
|
||||
},
|
||||
Vec3::new(0.0, -1.0, -1.5),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_piglb_leg(pigleg_l: Pigleg_l) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match pigleg_l {
|
||||
Pigleg_l::Default => "pigleg_l.vox",
|
||||
},
|
||||
Vec3::new(0.0, -1.0, -1.5),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_pigrb_leg(pigleg_r: Pigleg_r) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match pigleg_r {
|
||||
Pigleg_r::Default => "pigleg_r.vox",
|
||||
},
|
||||
Vec3::new(0.0, -1.0, -1.5),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FigureMgr {
|
||||
model_cache: FigureModelCache,
|
||||
states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
|
||||
character_states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
|
||||
quadruped_states: HashMap<EcsEntity, FigureState<QuadrupedSkeleton>>,
|
||||
}
|
||||
|
||||
impl FigureMgr {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
model_cache: FigureModelCache::new(),
|
||||
states: HashMap::new(),
|
||||
character_states: HashMap::new(),
|
||||
quadruped_states: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,51 +315,98 @@ impl FigureMgr {
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
let time = client.state().get_time();
|
||||
let ecs = client.state().ecs();
|
||||
for (entity, pos, vel, dir, actor, animation_history) in (
|
||||
for (entity, pos, vel, dir, actor, animation_history, stats) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<comp::phys::Pos>(),
|
||||
&ecs.read_storage::<comp::phys::Vel>(),
|
||||
&ecs.read_storage::<comp::phys::Dir>(),
|
||||
&ecs.read_storage::<comp::Actor>(),
|
||||
&ecs.read_storage::<comp::AnimationHistory>(),
|
||||
ecs.read_storage::<comp::Stats>().maybe(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
match actor {
|
||||
comp::Actor::Character { body, .. } => match body {
|
||||
Body::Humanoid(body) => {
|
||||
let state = self.states.entry(entity).or_insert_with(|| {
|
||||
let state = self.character_states.entry(entity).or_insert_with(|| {
|
||||
FigureState::new(renderer, CharacterSkeleton::new())
|
||||
});
|
||||
|
||||
let target_skeleton = match animation_history.current {
|
||||
comp::Animation::Idle => IdleAnimation::update_skeleton(
|
||||
comp::Animation::Idle => character::IdleAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
),
|
||||
comp::Animation::Run => RunAnimation::update_skeleton(
|
||||
comp::Animation::Run => character::RunAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
),
|
||||
comp::Animation::Jump => JumpAnimation::update_skeleton(
|
||||
comp::Animation::Jump => character::JumpAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
),
|
||||
comp::Animation::Gliding => {
|
||||
character::GlidingAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
state.skeleton.interpolate(&target_skeleton);
|
||||
|
||||
state.update(renderer, pos.0, dir.0);
|
||||
} // TODO: Non-humanoid bodies
|
||||
state.update(renderer, pos.0, dir.0, Rgba::white());
|
||||
}
|
||||
Body::Quadruped(body) => {
|
||||
let state = self.quadruped_states.entry(entity).or_insert_with(|| {
|
||||
FigureState::new(renderer, QuadrupedSkeleton::new())
|
||||
});
|
||||
|
||||
let target_skeleton = match animation_history.current {
|
||||
comp::Animation::Run => quadruped::RunAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
),
|
||||
comp::Animation::Idle => quadruped::IdleAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
),
|
||||
comp::Animation::Jump => quadruped::JumpAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
),
|
||||
|
||||
// TODO!
|
||||
_ => state.skeleton_mut().clone(),
|
||||
};
|
||||
|
||||
state.skeleton.interpolate(&target_skeleton);
|
||||
|
||||
// Change in health as color!
|
||||
let col = stats
|
||||
.and_then(|stats| stats.hp.last_change)
|
||||
.map(|(change_by, change_time)| Rgba::new(1.0, 0.7, 0.7, 1.0))
|
||||
.unwrap_or(Rgba::broadcast(1.0));
|
||||
|
||||
state.update(renderer, pos.0, dir.0, col);
|
||||
}
|
||||
},
|
||||
// TODO: Non-character actors
|
||||
}
|
||||
}
|
||||
|
||||
self.states
|
||||
// Clear states that have dead entities
|
||||
self.character_states
|
||||
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
||||
self.quadruped_states
|
||||
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
||||
}
|
||||
|
||||
@ -297,20 +421,22 @@ impl FigureMgr {
|
||||
|
||||
for (entity, actor) in (&ecs.entities(), &ecs.read_storage::<comp::Actor>()).join() {
|
||||
match actor {
|
||||
comp::Actor::Character { body, .. } => match body {
|
||||
Body::Humanoid(body) => {
|
||||
if let Some(state) = self.states.get(&entity) {
|
||||
let model = self.model_cache.get_or_create_model(renderer, *body, tick);
|
||||
renderer.render_figure(
|
||||
model,
|
||||
globals,
|
||||
&state.locals(),
|
||||
state.bone_consts(),
|
||||
);
|
||||
}
|
||||
} // TODO: Non-humanoid bodies
|
||||
},
|
||||
// TODO: Non-character actors
|
||||
comp::Actor::Character { body, .. } => {
|
||||
if let Some((locals, bone_consts)) = match body {
|
||||
Body::Humanoid(_) => self
|
||||
.character_states
|
||||
.get(&entity)
|
||||
.map(|state| (state.locals(), state.bone_consts())),
|
||||
Body::Quadruped(_) => self
|
||||
.quadruped_states
|
||||
.get(&entity)
|
||||
.map(|state| (state.locals(), state.bone_consts())),
|
||||
} {
|
||||
let model = self.model_cache.get_or_create_model(renderer, *body, tick);
|
||||
|
||||
renderer.render_figure(model, globals, locals, bone_consts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -333,12 +459,18 @@ impl<S: Skeleton> FigureState<S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, renderer: &mut Renderer, pos: Vec3<f32>, dir: Vec3<f32>) {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
pos: Vec3<f32>,
|
||||
dir: Vec3<f32>,
|
||||
col: Rgba<f32>,
|
||||
) {
|
||||
let mat = Mat4::<f32>::identity()
|
||||
* Mat4::translation_3d(pos)
|
||||
* Mat4::rotation_z(-dir.x.atan2(dir.y)); // + f32::consts::PI / 2.0);
|
||||
|
||||
let locals = FigureLocals::new(mat);
|
||||
let locals = FigureLocals::new(mat, col);
|
||||
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
|
||||
|
||||
renderer
|
||||
|
360
voxygen/src/scene/figure/figure.rs
Normal file
360
voxygen/src/scene/figure/figure.rs
Normal file
@ -0,0 +1,360 @@
|
||||
use crate::{
|
||||
anim::{
|
||||
character::{CharacterSkeleton, IdleAnimation, JumpAnimation, RunAnimation},
|
||||
Animation, Skeleton,
|
||||
},
|
||||
mesh::Meshable,
|
||||
render::{
|
||||
Consts, FigureBoneData, FigureLocals, FigurePipeline, Globals, Mesh, Model, Renderer,
|
||||
},
|
||||
Error,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
assets,
|
||||
comp::{
|
||||
self,
|
||||
actor::{Belt, Chest, Foot, Hand, Head, Pants, Shoulder, Weapon},
|
||||
Body, HumanoidBody,
|
||||
},
|
||||
figure::Segment,
|
||||
msg,
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
use specs::{Component, Entity as EcsEntity, Join, VecStorage};
|
||||
use std::{collections::HashMap, f32};
|
||||
use vek::*;
|
||||
|
||||
pub struct FigureModelCache {
|
||||
models: HashMap<HumanoidBody, (Model<FigurePipeline>, u64)>,
|
||||
}
|
||||
|
||||
impl FigureModelCache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
models: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_or_create_model(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
body: HumanoidBody,
|
||||
tick: u64,
|
||||
) -> &Model<FigurePipeline> {
|
||||
match self.models.get_mut(&body) {
|
||||
Some((model, last_used)) => {
|
||||
*last_used = tick;
|
||||
}
|
||||
None => {
|
||||
self.models.insert(
|
||||
body,
|
||||
(
|
||||
{
|
||||
let bone_meshes = [
|
||||
Some(Self::load_head(body.head)),
|
||||
Some(Self::load_chest(body.chest)),
|
||||
Some(Self::load_belt(body.belt)),
|
||||
Some(Self::load_pants(body.pants)),
|
||||
Some(Self::load_left_hand(body.hand)),
|
||||
Some(Self::load_right_hand(body.hand)),
|
||||
Some(Self::load_left_foot(body.foot)),
|
||||
Some(Self::load_right_foot(body.foot)),
|
||||
Some(Self::load_weapon(body.weapon)),
|
||||
Some(Self::load_left_shoulder(body.shoulder)),
|
||||
Some(Self::load_right_shoulder(body.shoulder)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
];
|
||||
|
||||
let mut mesh = Mesh::new();
|
||||
bone_meshes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
|
||||
.for_each(|(i, bone_mesh)| {
|
||||
mesh.push_mesh_map(bone_mesh, |vert| {
|
||||
vert.with_bone_idx(i as u8)
|
||||
})
|
||||
});
|
||||
|
||||
renderer.create_model(&mesh).unwrap()
|
||||
},
|
||||
tick,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&self.models[&body].0
|
||||
}
|
||||
|
||||
pub fn clean(&mut self, tick: u64) {
|
||||
// TODO: Don't hard-code this
|
||||
self.models
|
||||
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
||||
}
|
||||
|
||||
// TODO: Don't make this public
|
||||
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||
let fullpath: String = ["/voxygen/voxel/", filename].concat();
|
||||
Segment::from(assets::load_expect::<DotVoxData>(fullpath.as_str()).as_ref())
|
||||
.generate_mesh(position)
|
||||
}
|
||||
|
||||
fn load_head(head: Head) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match head {
|
||||
Head::Default => "head.vox",
|
||||
},
|
||||
Vec3::new(-7.0, -5.5, -6.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_chest(chest: Chest) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match chest {
|
||||
Chest::Default => "chest.vox",
|
||||
},
|
||||
Vec3::new(-6.0, -3.5, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_belt(belt: Belt) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match belt {
|
||||
Belt::Default => "belt.vox",
|
||||
},
|
||||
Vec3::new(-5.0, -3.5, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_pants(pants: Pants) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match pants {
|
||||
Pants::Default => "pants.vox",
|
||||
},
|
||||
Vec3::new(-5.0, -3.5, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_left_hand(hand: Hand) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match hand {
|
||||
Hand::Default => "hand.vox",
|
||||
},
|
||||
Vec3::new(2.0, 0.0, -7.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_right_hand(hand: Hand) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match hand {
|
||||
Hand::Default => "hand.vox",
|
||||
},
|
||||
Vec3::new(2.0, 0.0, -7.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_left_foot(foot: Foot) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match foot {
|
||||
Foot::Default => "foot.vox",
|
||||
},
|
||||
Vec3::new(2.5, -3.5, -9.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_right_foot(foot: Foot) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match foot {
|
||||
Foot::Default => "foot.vox",
|
||||
},
|
||||
Vec3::new(2.5, -3.5, -9.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_weapon(weapon: Weapon) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match weapon {
|
||||
Weapon::Sword => "sword.vox",
|
||||
// TODO actually match against other weapons and set the right model
|
||||
_ => "sword.vox",
|
||||
},
|
||||
Vec3::new(0.0, 0.0, -4.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_left_shoulder(shoulder: Shoulder) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match shoulder {
|
||||
Shoulder::Default => "shoulder_l.vox",
|
||||
},
|
||||
Vec3::new(2.5, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_right_shoulder(shoulder: Shoulder) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match shoulder {
|
||||
Shoulder::Default => "shoulder_r.vox",
|
||||
},
|
||||
Vec3::new(2.5, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
// fn load_draw(draw: Draw) -> Mesh<FigurePipeline> {
|
||||
// Self::load_mesh(
|
||||
// match draw {
|
||||
// //Draw::DefaultDraw => "sword.vox",
|
||||
//
|
||||
// },
|
||||
// Vec3::new(0.0, 0.0, -2.0)
|
||||
//
|
||||
//
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
pub struct FigureMgr {
|
||||
model_cache: FigureModelCache,
|
||||
states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
|
||||
}
|
||||
|
||||
impl FigureMgr {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
model_cache: FigureModelCache::new(),
|
||||
states: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clean(&mut self, tick: u64) {
|
||||
self.model_cache.clean(tick);
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
let time = client.state().get_time();
|
||||
let ecs = client.state().ecs();
|
||||
for (entity, pos, vel, dir, actor, animation_history) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<comp::phys::Pos>(),
|
||||
&ecs.read_storage::<comp::phys::Vel>(),
|
||||
&ecs.read_storage::<comp::phys::Dir>(),
|
||||
&ecs.read_storage::<comp::Actor>(),
|
||||
&ecs.read_storage::<comp::AnimationHistory>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
match actor {
|
||||
comp::Actor::Character { body, .. } => match body {
|
||||
Body::Humanoid(body) => {
|
||||
let state = self.states.entry(entity).or_insert_with(|| {
|
||||
FigureState::new(renderer, CharacterSkeleton::new())
|
||||
});
|
||||
|
||||
let target_skeleton = match animation_history.current {
|
||||
comp::Animation::Idle => IdleAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
),
|
||||
comp::Animation::Run => RunAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
),
|
||||
comp::Animation::Jump => JumpAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
),
|
||||
};
|
||||
|
||||
state.skeleton.interpolate(&target_skeleton);
|
||||
|
||||
state.update(renderer, pos.0, dir.0);
|
||||
} // TODO: Non-humanoid bodies
|
||||
},
|
||||
// TODO: Non-character actors
|
||||
}
|
||||
}
|
||||
|
||||
self.states
|
||||
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
client: &mut Client,
|
||||
globals: &Consts<Globals>,
|
||||
) {
|
||||
let tick = client.get_tick();
|
||||
let ecs = client.state().ecs();
|
||||
|
||||
for (entity, actor) in (&ecs.entities(), &ecs.read_storage::<comp::Actor>()).join() {
|
||||
match actor {
|
||||
comp::Actor::Character { body, .. } => match body {
|
||||
Body::Humanoid(body) => {
|
||||
if let Some(state) = self.states.get(&entity) {
|
||||
let model = self.model_cache.get_or_create_model(renderer, *body, tick);
|
||||
renderer.render_figure(
|
||||
model,
|
||||
globals,
|
||||
&state.locals(),
|
||||
state.bone_consts(),
|
||||
);
|
||||
}
|
||||
} // TODO: Non-humanoid bodies
|
||||
},
|
||||
// TODO: Non-character actors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FigureState<S: Skeleton> {
|
||||
bone_consts: Consts<FigureBoneData>,
|
||||
locals: Consts<FigureLocals>,
|
||||
skeleton: S,
|
||||
}
|
||||
|
||||
impl<S: Skeleton> FigureState<S> {
|
||||
pub fn new(renderer: &mut Renderer, skeleton: S) -> Self {
|
||||
Self {
|
||||
bone_consts: renderer
|
||||
.create_consts(&skeleton.compute_matrices())
|
||||
.unwrap(),
|
||||
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
|
||||
skeleton,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, renderer: &mut Renderer, pos: Vec3<f32>, dir: Vec3<f32>) {
|
||||
let mat = Mat4::<f32>::identity()
|
||||
* Mat4::translation_3d(pos)
|
||||
* Mat4::rotation_z(-dir.x.atan2(dir.y)); // + f32::consts::PI / 2.0);
|
||||
|
||||
let locals = FigureLocals::new(mat);
|
||||
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
|
||||
|
||||
renderer
|
||||
.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &Consts<FigureLocals> {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn bone_consts(&self) -> &Consts<FigureBoneData> {
|
||||
&self.bone_consts
|
||||
}
|
||||
|
||||
pub fn skeleton_mut(&mut self) -> &mut S {
|
||||
&mut self.skeleton
|
||||
}
|
||||
}
|
290
voxygen/src/scene/figure/figurequad.rs
Normal file
290
voxygen/src/scene/figure/figurequad.rs
Normal file
@ -0,0 +1,290 @@
|
||||
use crate::{
|
||||
anim::{
|
||||
quadruped::{QuadrupedSkeleton, RunAnimation},
|
||||
Animation, Skeleton,
|
||||
},
|
||||
mesh::Meshable,
|
||||
render::{
|
||||
Consts, FigureBoneData, FigureLocals, FigurePipeline, Globals, Mesh, Model, Renderer,
|
||||
},
|
||||
Error,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
assets,
|
||||
comp::{
|
||||
self,
|
||||
actor::{Head, Chest, l_leg, r_leg},
|
||||
Body, QuadrupedBody,
|
||||
},
|
||||
figure::Segment,
|
||||
msg,
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
use specs::{Component, Entity as EcsEntity, Join, VecStorage};
|
||||
use std::{collections::HashMap, f32};
|
||||
use vek::*;
|
||||
|
||||
pub struct FigureModelCache {
|
||||
models: HashMap<QuadrupedBody, (Model<FigurePipeline>, u64)>,
|
||||
}
|
||||
|
||||
impl FigureModelCache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
models: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_or_create_model(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
body: QuadrupedBody,
|
||||
tick: u64,
|
||||
) -> &Model<FigurePipeline> {
|
||||
match self.models.get_mut(&body) {
|
||||
Some((model, last_used)) => {
|
||||
*last_used = tick;
|
||||
}
|
||||
None => {
|
||||
self.models.insert(
|
||||
body,
|
||||
(
|
||||
{
|
||||
let bone_meshes = [
|
||||
Some(Self::load_head(body.head)),
|
||||
Some(Self::load_chest(body.chest)),
|
||||
Some(Self::load_lf_leg(body.leg_l)),
|
||||
Some(Self::load_rf_leg(body.leg_r)),
|
||||
Some(Self::load_lb_leg(body.leg_l)),
|
||||
Some(Self::load_rb_leg(body.leg_r)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
];
|
||||
|
||||
let mut mesh = Mesh::new();
|
||||
bone_meshes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
|
||||
.for_each(|(i, bone_mesh)| {
|
||||
mesh.push_mesh_map(bone_mesh, |vert| {
|
||||
vert.with_bone_idx(i as u8)
|
||||
})
|
||||
});
|
||||
|
||||
renderer.create_model(&mesh).unwrap()
|
||||
},
|
||||
tick,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&self.models[&body].0
|
||||
}
|
||||
|
||||
pub fn clean(&mut self, tick: u64) {
|
||||
// TODO: Don't hard-code this
|
||||
self.models
|
||||
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
||||
}
|
||||
|
||||
// TODO: Don't make this public
|
||||
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||
let fullpath: String = ["/voxygen/voxel/", filename].concat();
|
||||
Segment::from(assets::load_expect::<DotVoxData>(fullpath.as_str()).as_ref())
|
||||
.generate_mesh(position)
|
||||
}
|
||||
|
||||
fn load_head(head: Head) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match head {
|
||||
Head::Default => "pighead.vox",
|
||||
},
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_chest(chest: Chest) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match chest {
|
||||
Chest::Default => "pigchest.vox",
|
||||
},
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_lf_leg(leg_l: Leg_l) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match belt {
|
||||
Belt::Default => "pigleg_l.vox",
|
||||
},
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_rf_leg(leg_R: Leg_r) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match pants {
|
||||
Pants::Default => "pigleg_r.vox",
|
||||
},
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_lb_leg(leg_l: Leg_l) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match hand {
|
||||
Hand::Default => "pigleg_l.vox",
|
||||
},
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_rb_leg(leg_R: Leg_r) -> Mesh<FigurePipeline> {
|
||||
Self::load_mesh(
|
||||
match hand {
|
||||
Hand::Default => "pigleg_r.vox",
|
||||
},
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FigureMgr {
|
||||
model_cache: FigureModelCache,
|
||||
states: HashMap<EcsEntity, FigureState<QuadrupedSkeleton>>,
|
||||
}
|
||||
|
||||
impl FigureMgr {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
model_cache: FigureModelCache::new(),
|
||||
states: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clean(&mut self, tick: u64) {
|
||||
self.model_cache.clean(tick);
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
let time = client.state().get_time();
|
||||
let ecs = client.state().ecs();
|
||||
for (entity, pos, vel, dir, actor, animation_history) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<comp::phys::Pos>(),
|
||||
&ecs.read_storage::<comp::phys::Vel>(),
|
||||
&ecs.read_storage::<comp::phys::Dir>(),
|
||||
&ecs.read_storage::<comp::Actor>(),
|
||||
&ecs.read_storage::<comp::AnimationHistory>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
match actor {
|
||||
comp::Actor::Quadruped { body, .. } => match body {
|
||||
Body::Humanoid(body) => {
|
||||
let state = self.states.entry(entity).or_insert_with(|| {
|
||||
FigureState::new(renderer, QuadrupedSkeleton::new())
|
||||
});
|
||||
comp::Animation::Run => RunAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
),
|
||||
};
|
||||
|
||||
state.skeleton.interpolate(&target_skeleton);
|
||||
|
||||
state.update(renderer, pos.0, dir.0);
|
||||
} // TODO: Non-humanoid bodies
|
||||
},
|
||||
// TODO: Non-character actors
|
||||
}
|
||||
}
|
||||
|
||||
self.states
|
||||
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
client: &mut Client,
|
||||
globals: &Consts<Globals>,
|
||||
) {
|
||||
let tick = client.get_tick();
|
||||
let ecs = client.state().ecs();
|
||||
|
||||
for (entity, actor) in (&ecs.entities(), &ecs.read_storage::<comp::Actor>()).join() {
|
||||
match actor {
|
||||
comp::Actor::Quadruped { body, .. } => match body {
|
||||
Body::Humanoid(body) => {
|
||||
if let Some(state) = self.states.get(&entity) {
|
||||
let model = self.model_cache.get_or_create_model(renderer, *body, tick);
|
||||
renderer.render_figure(
|
||||
model,
|
||||
globals,
|
||||
&state.locals(),
|
||||
state.bone_consts(),
|
||||
);
|
||||
}
|
||||
} // TODO: Non-humanoid bodies
|
||||
},
|
||||
// TODO: Non-character actors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FigureState<S: Skeleton> {
|
||||
bone_consts: Consts<FigureBoneData>,
|
||||
locals: Consts<FigureLocals>,
|
||||
skeleton: S,
|
||||
}
|
||||
|
||||
impl<S: Skeleton> FigureState<S> {
|
||||
pub fn new(renderer: &mut Renderer, skeleton: S) -> Self {
|
||||
Self {
|
||||
bone_consts: renderer
|
||||
.create_consts(&skeleton.compute_matrices())
|
||||
.unwrap(),
|
||||
locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(),
|
||||
skeleton,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, renderer: &mut Renderer, pos: Vec3<f32>, dir: Vec3<f32>) {
|
||||
let mat = Mat4::<f32>::identity()
|
||||
* Mat4::translation_3d(pos)
|
||||
* Mat4::rotation_z(-dir.x.atan2(dir.y)); // + f32::consts::PI / 2.0);
|
||||
|
||||
let locals = FigureLocals::new(mat);
|
||||
renderer.update_consts(&mut self.locals, &[locals]).unwrap();
|
||||
|
||||
renderer
|
||||
.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &Consts<FigureLocals> {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn bone_consts(&self) -> &Consts<FigureBoneData> {
|
||||
&self.bone_consts
|
||||
}
|
||||
|
||||
pub fn skeleton_mut(&mut self) -> &mut S {
|
||||
&mut self.skeleton
|
||||
}
|
||||
}
|
@ -157,8 +157,7 @@ impl Terrain {
|
||||
|
||||
// Queue the worker thread
|
||||
client.thread_pool().execute(move || {
|
||||
send.send(mesh_worker(pos, current_tick, volume, aabb))
|
||||
.expect("Failed to send chunk mesh to main thread");
|
||||
let _ = send.send(mesh_worker(pos, current_tick, volume, aabb));
|
||||
});
|
||||
todo.active_worker = true;
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ impl SessionState {
|
||||
Input {
|
||||
move_dir,
|
||||
jumping: self.key_state.jump,
|
||||
gliding: self.key_state.glide,
|
||||
events: input_events,
|
||||
},
|
||||
dt,
|
||||
@ -145,12 +146,14 @@ impl PlayState for SessionState {
|
||||
self.input_events.push(InputEvent::Jump);
|
||||
self.key_state.jump = true;
|
||||
}
|
||||
Event::KeyDown(Key::Glide) => self.key_state.glide = true,
|
||||
// Movement Key Released
|
||||
Event::KeyUp(Key::MoveForward) => self.key_state.up = false,
|
||||
Event::KeyUp(Key::MoveBack) => self.key_state.down = false,
|
||||
Event::KeyUp(Key::MoveLeft) => self.key_state.left = false,
|
||||
Event::KeyUp(Key::MoveRight) => self.key_state.right = false,
|
||||
Event::KeyUp(Key::Jump) => self.key_state.jump = false,
|
||||
Event::KeyUp(Key::Glide) => self.key_state.glide = false,
|
||||
// Pass all other events to the scene
|
||||
event => {
|
||||
self.scene.handle_input_event(event);
|
||||
|
@ -25,6 +25,7 @@ pub struct ControlSettings {
|
||||
pub move_back: VirtualKeyCode,
|
||||
pub move_right: VirtualKeyCode,
|
||||
pub jump: VirtualKeyCode,
|
||||
pub glide: VirtualKeyCode,
|
||||
pub map: VirtualKeyCode,
|
||||
pub bag: VirtualKeyCode,
|
||||
pub quest_log: VirtualKeyCode,
|
||||
@ -60,6 +61,7 @@ impl Default for Settings {
|
||||
move_back: VirtualKeyCode::S,
|
||||
move_right: VirtualKeyCode::D,
|
||||
jump: VirtualKeyCode::Space,
|
||||
glide: VirtualKeyCode::LShift,
|
||||
map: VirtualKeyCode::M,
|
||||
bag: VirtualKeyCode::B,
|
||||
quest_log: VirtualKeyCode::L,
|
||||
|
@ -46,6 +46,7 @@ impl Window {
|
||||
key_map.insert(settings.controls.move_back, Key::MoveBack);
|
||||
key_map.insert(settings.controls.move_right, Key::MoveRight);
|
||||
key_map.insert(settings.controls.jump, Key::Jump);
|
||||
key_map.insert(settings.controls.glide, Key::Glide);
|
||||
key_map.insert(settings.controls.map, Key::Map);
|
||||
key_map.insert(settings.controls.bag, Key::Bag);
|
||||
key_map.insert(settings.controls.quest_log, Key::QuestLog);
|
||||
@ -182,6 +183,7 @@ pub enum Key {
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
Jump,
|
||||
Glide,
|
||||
Enter,
|
||||
Escape,
|
||||
Map,
|
||||
|
@ -42,7 +42,7 @@ impl World {
|
||||
|
||||
let chaos_freq = 1.0 / 100.0;
|
||||
let freq = 1.0 / 128.0;
|
||||
let ampl = 32.0;
|
||||
let ampl = 75.0;
|
||||
let small_freq = 1.0 / 32.0;
|
||||
let small_ampl = 6.0;
|
||||
let offs = 32.0;
|
||||
@ -55,7 +55,7 @@ impl World {
|
||||
let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl * chaos
|
||||
+ perlin_nz.get((wposf * small_freq).into_array())
|
||||
* small_ampl
|
||||
* 2.0
|
||||
* 3.0
|
||||
* chaos.powf(2.0)
|
||||
+ offs;
|
||||
let temp = (temp_nz.get(Vec2::from(wposf * (1.0 / 64.0)).into_array()) + 1.0) * 0.5;
|
||||
|
Loading…
Reference in New Issue
Block a user