diff --git a/assets/voxygen/voxel/glider.vox b/assets/voxygen/voxel/glider.vox new file mode 100644 index 0000000000..b688b4ef97 --- /dev/null +++ b/assets/voxygen/voxel/glider.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7009423cc546742c28963b443e2553dfc359a72cd996f9d159d7242e7a083c7 +size 9040 diff --git a/assets/voxygen/voxel/pigchest.vox b/assets/voxygen/voxel/pigchest.vox new file mode 100644 index 0000000000..4bff8ba86c --- /dev/null +++ b/assets/voxygen/voxel/pigchest.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:326da92dc4af851d907d674ce07cd63cc032477eae901c57dc5f4742cc215954 +size 2752 diff --git a/assets/voxygen/voxel/pighead.vox b/assets/voxygen/voxel/pighead.vox new file mode 100644 index 0000000000..39ac9d39b6 --- /dev/null +++ b/assets/voxygen/voxel/pighead.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a56643b54eb4d39ad58b3bcab88390ddea58cf41351b39ad32b83612900b71c1 +size 2720 diff --git a/assets/voxygen/voxel/pigleg_l.vox b/assets/voxygen/voxel/pigleg_l.vox new file mode 100644 index 0000000000..d016d1b2b0 --- /dev/null +++ b/assets/voxygen/voxel/pigleg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1fd44f8c1bd1eb9cab7a4d0632b6db52e9aada97b01a7fca8ea4f2f98186685 +size 1144 diff --git a/assets/voxygen/voxel/pigleg_r.vox b/assets/voxygen/voxel/pigleg_r.vox new file mode 100644 index 0000000000..bf5a48a400 --- /dev/null +++ b/assets/voxygen/voxel/pigleg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79a83692fb7a5791cef6848ad14e27a79585f7a0cf872f20b2b556b9cd18838e +size 1144 diff --git a/client/src/input.rs b/client/src/input.rs index 840ee46525..a274697e29 100644 --- a/client/src/input.rs +++ b/client/src/input.rs @@ -7,6 +7,7 @@ pub enum InputEvent { pub struct Input { pub move_dir: Vec2, pub jumping: bool, + pub gliding: bool, pub events: Vec, } @@ -15,6 +16,7 @@ impl Default for Input { Input { move_dir: Vec2::zero(), jumping: false, + gliding: false, events: Vec::new(), } } diff --git a/client/src/lib.rs b/client/src/lib.rs index a4cd2cb0f4..96b182e3f0 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -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>, + pending_chunks: HashMap, 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) diff --git a/common/src/comp/actor.rs b/common/src/comp/actor.rs index 4302749f12..885f53381e 100644 --- a/common/src/comp/actor.rs +++ b/common/src/comp/actor.rs @@ -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 { diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index f3ff742700..1159316afc 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -18,6 +18,7 @@ impl Component for Agent { pub struct Control { pub move_dir: Vec2, 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, } } } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 1d14704377..a75e09c083 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -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; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index dd8b02ced3..67df6ac1ba 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -7,7 +7,7 @@ use vek::*; pub struct Pos(pub Vec3); impl Component for Pos { - type Storage = FlaggedStorage>; + type Storage = VecStorage; } // Vel @@ -16,7 +16,7 @@ impl Component for Pos { pub struct Vel(pub Vec3); impl Component for Vel { - type Storage = FlaggedStorage>; + type Storage = VecStorage; } // Dir @@ -25,7 +25,7 @@ impl Component for Vel { pub struct Dir(pub Vec3); impl Component for Dir { - type Storage = FlaggedStorage>; + type Storage = VecStorage; } // Dir diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs new file mode 100644 index 0000000000..3b6e263af2 --- /dev/null +++ b/common/src/comp/stats.rs @@ -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>; +} diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index f90315edd9..530022d4e3 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -9,7 +9,7 @@ pub enum ClientMsg { }, Character { name: String, - body: comp::HumanoidBody, + body: comp::Body, }, RequestState(ClientState), Ping, diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index b7da55f0a8..74ffabacbf 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -11,6 +11,7 @@ sphynx::sum_type! { Dir(comp::phys::Dir), Actor(comp::Actor), Player(comp::Player), + Stats(comp::Stats), } } // Automatically derive From for Phantom for each variant Phantom::T(PhantomData) @@ -22,6 +23,7 @@ sphynx::sum_type! { Dir(PhantomData), Actor(PhantomData), Player(PhantomData), + Stats(PhantomData), } } impl sphynx::Packet for EcsPacket { diff --git a/common/src/state.rs b/common/src/state.rs index 509ca67ca7..e0054dfa69 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -97,6 +97,7 @@ impl State { // Register synced components ecs.register_synced::(); ecs.register_synced::(); + ecs.register_synced::(); // Register unsynched (or synced by other means) components ecs.register::(); diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index d13d6cc3df..893d6e8f56 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -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() diff --git a/common/src/sys/control.rs b/common/src/sys/control.rs index c8ddf2d2cc..375d26cbfb 100644 --- a/common/src/sys/control.rs +++ b/common/src/sys/control.rs @@ -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::::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 }; diff --git a/server/src/cmd.rs b/server/src/cmd.rs index b12ecafe17..58a668a6b1 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -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 { diff --git a/server/src/lib.rs b/server/src/lib.rs index 1430a7f5e1..deaff7ead3 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -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())); diff --git a/voxygen/shaders/figure.frag b/voxygen/shaders/figure.frag index 4a933ab4f7..9f35b6b04a 100644 --- a/voxygen/shaders/figure.frag +++ b/voxygen/shaders/figure.frag @@ -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); } diff --git a/voxygen/shaders/figure.vert b/voxygen/shaders/figure.vert index 258f4f1f45..4db80ee7c4 100644 --- a/voxygen/shaders/figure.vert +++ b/voxygen/shaders/figure.vert @@ -10,6 +10,7 @@ in uint v_bone_idx; layout (std140) uniform u_locals { mat4 model_mat; + vec4 model_col; }; struct BoneData { diff --git a/voxygen/src/anim/character/gliding.rs b/voxygen/src/anim/character/gliding.rs new file mode 100644 index 0000000000..be2102670d --- /dev/null +++ b/voxygen/src/anim/character/gliding.rs @@ -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 + } +} diff --git a/voxygen/src/anim/character/idle.rs b/voxygen/src/anim/character/idle.rs index 1a629d3b0b..bf663488ae 100644 --- a/voxygen/src/anim/character/idle.rs +++ b/voxygen/src/anim/character/idle.rs @@ -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 } } diff --git a/voxygen/src/anim/character/jump.rs b/voxygen/src/anim/character/jump.rs index acbd79adb0..37a898a3c9 100644 --- a/voxygen/src/anim/character/jump.rs +++ b/voxygen/src/anim/character/jump.rs @@ -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 } } diff --git a/voxygen/src/anim/character/mod.rs b/voxygen/src/anim/character/mod.rs index 8a348ce769..d1054716a5 100644 --- a/voxygen/src/anim/character/mod.rs +++ b/voxygen/src/anim/character/mod.rs @@ -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); } } diff --git a/voxygen/src/anim/character/run.rs b/voxygen/src/anim/character/run.rs index b79751c20b..aae5996564 100644 --- a/voxygen/src/anim/character/run.rs +++ b/voxygen/src/anim/character/run.rs @@ -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 } diff --git a/voxygen/src/anim/mod.rs b/voxygen/src/anim/mod.rs index 38452e6c65..1c200a4114 100644 --- a/voxygen/src/anim/mod.rs +++ b/voxygen/src/anim/mod.rs @@ -1,5 +1,5 @@ pub mod character; - +pub mod quadruped; // Library use vek::*; diff --git a/voxygen/src/anim/quadruped/idle.rs b/voxygen/src/anim/quadruped/idle.rs new file mode 100644 index 0000000000..0a281c2e19 --- /dev/null +++ b/voxygen/src/anim/quadruped/idle.rs @@ -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 + } +} diff --git a/voxygen/src/anim/quadruped/jump.rs b/voxygen/src/anim/quadruped/jump.rs new file mode 100644 index 0000000000..0567e3c558 --- /dev/null +++ b/voxygen/src/anim/quadruped/jump.rs @@ -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 + } +} diff --git a/voxygen/src/anim/quadruped/mod.rs b/voxygen/src/anim/quadruped/mod.rs new file mode 100644 index 0000000000..5115133cf2 --- /dev/null +++ b/voxygen/src/anim/quadruped/mod.rs @@ -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); + } +} diff --git a/voxygen/src/anim/quadruped/run.rs b/voxygen/src/anim/quadruped/run.rs new file mode 100644 index 0000000000..21e1fdafc3 --- /dev/null +++ b/voxygen/src/anim/quadruped/run.rs @@ -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 + } +} diff --git a/voxygen/src/key_state.rs b/voxygen/src/key_state.rs index 22e666f0ba..909f1f3490 100644 --- a/voxygen/src/key_state.rs +++ b/voxygen/src/key_state.rs @@ -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, } } diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 9316ca3682..0bc18f324f 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -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, diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs index e8511215f0..d29a50d127 100644 --- a/voxygen/src/menu/char_selection/scene.rs +++ b/voxygen/src/menu/char_selection/scene.rs @@ -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( diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index e7fc4436bb..d7036f5d3d 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -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) -> Self { + pub fn new(model_mat: Mat4, col: Rgba) -> 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)) } } diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index 9ef9dad08c..39ddc2790e 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -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, u64)>, + models: HashMap, 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 { 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 { - // Self::load_mesh( - // match draw { - // //Draw::DefaultDraw => "sword.vox", - // - // }, - // Vec3::new(0.0, 0.0, -2.0) - // - // - // ) - // } + fn load_draw(draw: Draw) -> Mesh { + Self::load_mesh( + match draw { + Draw::Default => "glider.vox", + }, + Vec3::new(-26.0, -26.0, -5.0), + ) + } + + fn load_pighead(pighead: Pighead) -> Mesh { + Self::load_mesh( + match pighead { + Pighead::Default => "pighead.vox", + }, + Vec3::new(-6.0, 4.5, 3.0), + ) + } + + fn load_pigchest(pigchest: Pigchest) -> Mesh { + 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 { + 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 { + 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 { + 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 { + 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>, + character_states: HashMap>, + quadruped_states: HashMap>, } 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::(), &ecs.read_storage::(), &ecs.read_storage::(), &ecs.read_storage::(), &ecs.read_storage::(), + ecs.read_storage::().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::()).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 FigureState { } } - pub fn update(&mut self, renderer: &mut Renderer, pos: Vec3, dir: Vec3) { + pub fn update( + &mut self, + renderer: &mut Renderer, + pos: Vec3, + dir: Vec3, + col: Rgba, + ) { let mat = Mat4::::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 diff --git a/voxygen/src/scene/figure/figure.rs b/voxygen/src/scene/figure/figure.rs new file mode 100644 index 0000000000..9ef9dad08c --- /dev/null +++ b/voxygen/src/scene/figure/figure.rs @@ -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, 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 { + 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) -> Mesh { + let fullpath: String = ["/voxygen/voxel/", filename].concat(); + Segment::from(assets::load_expect::(fullpath.as_str()).as_ref()) + .generate_mesh(position) + } + + fn load_head(head: Head) -> Mesh { + Self::load_mesh( + match head { + Head::Default => "head.vox", + }, + Vec3::new(-7.0, -5.5, -6.0), + ) + } + + fn load_chest(chest: Chest) -> Mesh { + Self::load_mesh( + match chest { + Chest::Default => "chest.vox", + }, + Vec3::new(-6.0, -3.5, 0.0), + ) + } + + fn load_belt(belt: Belt) -> Mesh { + Self::load_mesh( + match belt { + Belt::Default => "belt.vox", + }, + Vec3::new(-5.0, -3.5, 0.0), + ) + } + + fn load_pants(pants: Pants) -> Mesh { + Self::load_mesh( + match pants { + Pants::Default => "pants.vox", + }, + Vec3::new(-5.0, -3.5, 0.0), + ) + } + + fn load_left_hand(hand: Hand) -> Mesh { + Self::load_mesh( + match hand { + Hand::Default => "hand.vox", + }, + Vec3::new(2.0, 0.0, -7.0), + ) + } + + fn load_right_hand(hand: Hand) -> Mesh { + Self::load_mesh( + match hand { + Hand::Default => "hand.vox", + }, + Vec3::new(2.0, 0.0, -7.0), + ) + } + + fn load_left_foot(foot: Foot) -> Mesh { + Self::load_mesh( + match foot { + Foot::Default => "foot.vox", + }, + Vec3::new(2.5, -3.5, -9.0), + ) + } + + fn load_right_foot(foot: Foot) -> Mesh { + Self::load_mesh( + match foot { + Foot::Default => "foot.vox", + }, + Vec3::new(2.5, -3.5, -9.0), + ) + } + + fn load_weapon(weapon: Weapon) -> Mesh { + 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 { + 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 { + Self::load_mesh( + match shoulder { + Shoulder::Default => "shoulder_r.vox", + }, + Vec3::new(2.5, 0.0, 0.0), + ) + } + // fn load_draw(draw: Draw) -> Mesh { + // 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>, +} + +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::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ) + .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, + ) { + let tick = client.get_tick(); + let ecs = client.state().ecs(); + + for (entity, actor) in (&ecs.entities(), &ecs.read_storage::()).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 { + bone_consts: Consts, + locals: Consts, + skeleton: S, +} + +impl FigureState { + 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, dir: Vec3) { + let mat = Mat4::::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 { + &self.locals + } + + pub fn bone_consts(&self) -> &Consts { + &self.bone_consts + } + + pub fn skeleton_mut(&mut self) -> &mut S { + &mut self.skeleton + } +} diff --git a/voxygen/src/scene/figure/figurequad.rs b/voxygen/src/scene/figure/figurequad.rs new file mode 100644 index 0000000000..aa76298aa1 --- /dev/null +++ b/voxygen/src/scene/figure/figurequad.rs @@ -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, 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 { + 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) -> Mesh { + let fullpath: String = ["/voxygen/voxel/", filename].concat(); + Segment::from(assets::load_expect::(fullpath.as_str()).as_ref()) + .generate_mesh(position) + } + + fn load_head(head: Head) -> Mesh { + Self::load_mesh( + match head { + Head::Default => "pighead.vox", + }, + Vec3::new(0.0, 0.0, 0.0), + ) + } + + fn load_chest(chest: Chest) -> Mesh { + 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 { + 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 { + 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 { + 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 { + 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>, +} + +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::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ) + .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, + ) { + let tick = client.get_tick(); + let ecs = client.state().ecs(); + + for (entity, actor) in (&ecs.entities(), &ecs.read_storage::()).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 { + bone_consts: Consts, + locals: Consts, + skeleton: S, +} + +impl FigureState { + 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, dir: Vec3) { + let mat = Mat4::::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 { + &self.locals + } + + pub fn bone_consts(&self) -> &Consts { + &self.bone_consts + } + + pub fn skeleton_mut(&mut self) -> &mut S { + &mut self.skeleton + } +} diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 2a53e879b9..daf0c29327 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -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; } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index b66d036118..a8c4b2c2d5 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -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); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 5a22ee9936..b8f9c8b97b 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -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, diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 9f5cdd44fb..0bd0241f13 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -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, diff --git a/world/src/lib.rs b/world/src/lib.rs index 98242c847c..fb4e613d02 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -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;