diff --git a/common/src/comp/character.rs b/common/src/comp/actor.rs similarity index 68% rename from common/src/comp/character.rs rename to common/src/comp/actor.rs index c56dddde3e..4302749f12 100644 --- a/common/src/comp/character.rs +++ b/common/src/comp/actor.rs @@ -13,7 +13,7 @@ pub enum Race { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Gender { +pub enum BodyType { Female, Male, Unspecified, @@ -21,32 +21,32 @@ pub enum Gender { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Head { - DefaultHead, + Default, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Chest { - DefaultChest, + Default, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Belt { - DefaultBelt, + Default, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Pants { - DefaultPants, + Default, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Hand { - DefaultHand, + Default, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Foot { - DefaultFoot, + Default, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -62,42 +62,45 @@ pub enum Weapon { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Shoulder { - DefaultShoulder, + Default, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Draw { - DefaultDraw, + Default, } -use Belt::*; -use Chest::*; -use Draw::*; -use Foot::*; -use Gender::*; -use Hand::*; -use Head::*; -use Pants::*; -use Race::*; -use Shoulder::*; -use Weapon::*; - -const ALL_RACES: [Race; 6] = [Danari, Dwarf, Elf, Human, Orc, Undead]; -const ALL_GENDERS: [Gender; 3] = [Female, Male, Unspecified]; -const ALL_HEADS: [Head; 1] = [DefaultHead]; -const ALL_CHESTS: [Chest; 1] = [DefaultChest]; -const ALL_BELTS: [Belt; 1] = [DefaultBelt]; -const ALL_PANTS: [Pants; 1] = [DefaultPants]; -const ALL_HANDS: [Hand; 1] = [DefaultHand]; -const ALL_FEET: [Foot; 1] = [DefaultFoot]; -const ALL_WEAPONS: [Weapon; 7] = [Daggers, SwordShield, Sword, Axe, Hammer, Bow, Staff]; -const ALL_SHOULDERS: [Shoulder; 1] = [DefaultShoulder]; -const ALL_DRAW: [Draw; 1] = [DefaultDraw]; +const ALL_RACES: [Race; 6] = [ + Race::Danari, + Race::Dwarf, + Race::Elf, + Race::Human, + Race::Orc, + Race::Undead, +]; +const ALL_BODY_TYPES: [BodyType; 3] = [BodyType::Female, BodyType::Male, BodyType::Unspecified]; +const ALL_HEADS: [Head; 1] = [Head::Default]; +const ALL_CHESTS: [Chest; 1] = [Chest::Default]; +const ALL_BELTS: [Belt; 1] = [Belt::Default]; +const ALL_PANTS: [Pants; 1] = [Pants::Default]; +const ALL_HANDS: [Hand; 1] = [Hand::Default]; +const ALL_FEET: [Foot; 1] = [Foot::Default]; +const ALL_WEAPONS: [Weapon; 7] = [ + Weapon::Daggers, + Weapon::SwordShield, + Weapon::Sword, + Weapon::Axe, + Weapon::Hammer, + Weapon::Bow, + Weapon::Staff, +]; +const ALL_SHOULDERS: [Shoulder; 1] = [Shoulder::Default]; +const ALL_DRAW: [Draw; 1] = [Draw::Default]; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Character { +pub struct HumanoidBody { pub race: Race, - pub gender: Gender, + pub body_type: BodyType, pub head: Head, pub chest: Chest, pub belt: Belt, @@ -109,11 +112,11 @@ pub struct Character { pub draw: Draw, } -impl Character { +impl HumanoidBody { pub fn random() -> Self { Self { race: *thread_rng().choose(&ALL_RACES).unwrap(), - gender: *thread_rng().choose(&ALL_GENDERS).unwrap(), + body_type: *thread_rng().choose(&ALL_BODY_TYPES).unwrap(), head: *thread_rng().choose(&ALL_HEADS).unwrap(), chest: *thread_rng().choose(&ALL_CHESTS).unwrap(), belt: *thread_rng().choose(&ALL_BELTS).unwrap(), @@ -127,6 +130,20 @@ impl Character { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Body { + Humanoid(HumanoidBody), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Actor { + Character { name: String, body: Body }, +} + +impl Component for Actor { + type Storage = FlaggedStorage>; +} + #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct AnimationHistory { pub last: Option, @@ -151,10 +168,6 @@ pub enum Animation { Jump, } -impl Component for Character { - type Storage = FlaggedStorage>; -} - impl Component for AnimationHistory { type Storage = FlaggedStorage>; } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index ca84c34dd9..1d14704377 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -1,11 +1,13 @@ +pub mod actor; pub mod agent; -pub mod character; pub mod phys; pub mod player; // Reexports +pub use actor::Actor; +pub use actor::Animation; +pub use actor::AnimationHistory; +pub use actor::Body; +pub use actor::HumanoidBody; pub use agent::{Agent, Control}; -pub use character::Animation; -pub use character::AnimationHistory; -pub use character::Character; pub use player::Player; diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index ce70302ad6..f90315edd9 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -7,12 +7,15 @@ pub enum ClientMsg { Register { player: comp::Player, }, - Character(comp::Character), + Character { + name: String, + body: comp::HumanoidBody, + }, RequestState(ClientState), Ping, Pong, Chat(String), - PlayerAnimation(comp::character::AnimationHistory), + PlayerAnimation(comp::AnimationHistory), PlayerPhysics { pos: comp::phys::Pos, vel: comp::phys::Vel, diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 9dd876a39f..b7da55f0a8 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -9,7 +9,7 @@ sphynx::sum_type! { Pos(comp::phys::Pos), Vel(comp::phys::Vel), Dir(comp::phys::Dir), - Character(comp::Character), + Actor(comp::Actor), Player(comp::Player), } } @@ -20,7 +20,7 @@ sphynx::sum_type! { Pos(PhantomData), Vel(PhantomData), Dir(PhantomData), - Character(PhantomData), + Actor(PhantomData), Player(PhantomData), } } diff --git a/common/src/state.rs b/common/src/state.rs index 34eb33df50..509ca67ca7 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -95,7 +95,7 @@ impl State { // Create a new Sphynx ECS world fn setup_sphynx_world(ecs: &mut sphynx::World) { // Register synced components - ecs.register_synced::(); + ecs.register_synced::(); ecs.register_synced::(); // Register unsynched (or synced by other means) components @@ -110,7 +110,7 @@ impl State { ecs.add_resource(TimeOfDay(0.0)); ecs.add_resource(Time(0.0)); ecs.add_resource(DeltaTime(0.0)); - ecs.add_resource(TerrainMap::new()); + ecs.add_resource(TerrainMap::new().unwrap()); } /// Register a component with the state's ECS @@ -177,7 +177,7 @@ impl State { if self .ecs .write_resource::() - .insert(key, chunk) + .insert(key, Arc::new(chunk)) .is_some() { self.changes.changed_chunks.insert(key); diff --git a/common/src/volumes/vol_map.rs b/common/src/volumes/vol_map.rs index e78e5f9ac7..76fa9a6c84 100644 --- a/common/src/volumes/vol_map.rs +++ b/common/src/volumes/vol_map.rs @@ -1,11 +1,12 @@ // Standard -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; // Library use vek::*; // Crate use crate::{ + terrain::TerrainChunkMeta, vol::{BaseVol, ReadVol, SampleVol, SizedVol, VolSize, Vox, WriteVol}, volumes::{ chunk::{Chunk, ChunkErr}, @@ -18,24 +19,33 @@ pub enum VolMapErr { NoSuchChunk, ChunkErr(ChunkErr), DynaErr(DynaErr), + InvalidChunkSize, } // V = Voxel // S = Size (replace with a const when const generics is a thing) // M = Chunk metadata +#[derive(Clone)] pub struct VolMap { - chunks: HashMap, Chunk>, + chunks: HashMap, Arc>>, } impl VolMap { #[inline(always)] - fn chunk_key(pos: Vec3) -> Vec3 { - pos.map2(S::SIZE, |e, sz| e.div_euclid(sz as i32)) + pub fn chunk_key(pos: Vec3) -> Vec3 { + pos.map2(S::SIZE, |e, sz| { + // Horrid, but it's faster than a cheetah with a red bull blood transfusion + let log2 = (sz - 1).count_ones(); + ((((e as i64 + (1 << 32)) as u64) >> log2) - (1 << (32 - log2))) as i32 + }) } #[inline(always)] - fn chunk_offs(pos: Vec3) -> Vec3 { - pos.map2(S::SIZE, |e, sz| e.rem_euclid(sz as i32)) + pub fn chunk_offs(pos: Vec3) -> Vec3 { + pos.map2(S::SIZE, |e, sz| { + // Horrid, but it's even faster than the aforementioned cheetah + (((e as i64 + (1 << 32)) as u64) & (sz - 1) as u64) as i32 + }) } } @@ -58,8 +68,8 @@ impl ReadVol for VolMap { } } -impl SampleVol for VolMap { - type Sample = Dyna; +impl SampleVol for VolMap { + type Sample = VolMap; /// Take a sample of the terrain by cloning the voxels within the provided range. /// @@ -80,24 +90,59 @@ impl SampleVol for VolMap { } */ - let mut sample = Dyna::filled(range.size().map(|e| e as u32).into(), V::empty(), ()); + // let mut sample = Dyna::filled(range.size().map(|e| e as u32).into(), V::empty(), ()); - for pos in sample.iter_positions() { - sample - .set( - pos, - self.get(range.min + pos) - .map(|v| v.clone()) - .unwrap_or(V::empty()), - ) - .map_err(|err| VolMapErr::DynaErr(err))?; + // let mut last_chunk_pos = self.pos_key(range.min); + // let mut last_chunk = self.get_key(last_chunk_pos); + + // for pos in sample.iter_positions() { + // let new_chunk_pos = self.pos_key(range.min + pos); + // if last_chunk_pos != new_chunk_pos { + // last_chunk = self.get_key(new_chunk_pos); + // last_chunk_pos = new_chunk_pos; + // } + // sample + // .set( + // pos, + // if let Some(chunk) = last_chunk { + // chunk + // .get(Self::chunk_offs(range.min + pos)) + // .map(|v| v.clone()) + // .unwrap_or(V::empty()) + // // Fallback in case the chunk doesn't exist + // } else { + // self.get(range.min + pos) + // .map(|v| v.clone()) + // .unwrap_or(V::empty()) + // }, + // ) + // .map_err(|err| VolMapErr::DynaErr(err))?; + // } + + // Ok(sample) + + let mut sample = VolMap::new()?; + let chunk_min = Self::chunk_key(range.min); + let chunk_max = Self::chunk_key(range.max); + for x in chunk_min.x..=chunk_max.x { + for y in chunk_min.y..=chunk_max.y { + for z in chunk_min.z..=chunk_max.z { + let chunk_key = Vec3::new(x, y, z); + + let chunk = self.get_key_arc(chunk_key).map(|v| v.clone()); + + if let Some(chunk) = chunk { + sample.insert(chunk_key, chunk); + } + } + } } Ok(sample) } } -impl WriteVol for VolMap { +impl WriteVol for VolMap { #[inline(always)] fn set(&mut self, pos: Vec3, vox: V) -> Result<(), VolMapErr> { let ck = Self::chunk_key(pos); @@ -106,15 +151,24 @@ impl WriteVol for VolMap { .ok_or(VolMapErr::NoSuchChunk) .and_then(|chunk| { let co = Self::chunk_offs(pos); - chunk.set(co, vox).map_err(|err| VolMapErr::ChunkErr(err)) + Arc::make_mut(chunk) + .set(co, vox) + .map_err(|err| VolMapErr::ChunkErr(err)) }) } } impl VolMap { - pub fn new() -> Self { - Self { - chunks: HashMap::new(), + pub fn new() -> Result { + if Self::chunk_size() + .map(|e| e.is_power_of_two() && e > 0) + .reduce_and() + { + Ok(Self { + chunks: HashMap::new(), + }) + } else { + Err(VolMapErr::InvalidChunkSize) } } @@ -122,15 +176,26 @@ impl VolMap { S::SIZE } - pub fn insert(&mut self, key: Vec3, chunk: Chunk) -> Option> { + pub fn insert( + &mut self, + key: Vec3, + chunk: Arc>, + ) -> Option>> { self.chunks.insert(key, chunk) } pub fn get_key(&self, key: Vec3) -> Option<&Chunk> { + match self.chunks.get(&key) { + Some(arc_chunk) => Some(arc_chunk.as_ref()), + None => None, + } + } + + pub fn get_key_arc(&self, key: Vec3) -> Option<&Arc>> { self.chunks.get(&key) } - pub fn remove(&mut self, key: Vec3) -> Option> { + pub fn remove(&mut self, key: Vec3) -> Option>> { self.chunks.remove(&key) } @@ -150,11 +215,11 @@ impl VolMap { } pub struct ChunkIter<'a, V: Vox, S: VolSize, M> { - iter: std::collections::hash_map::Iter<'a, Vec3, Chunk>, + iter: std::collections::hash_map::Iter<'a, Vec3, Arc>>, } impl<'a, V: Vox, S: VolSize, M> Iterator for ChunkIter<'a, V, S, M> { - type Item = (Vec3, &'a Chunk); + type Item = (Vec3, &'a Arc>); fn next(&mut self) -> Option { self.iter.next().map(|(k, c)| (*k, c)) diff --git a/server/src/cmd.rs b/server/src/cmd.rs index e585165d9b..b12ecafe17 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -191,22 +191,21 @@ fn handle_pet(server: &mut Server, entity: EcsEntity, args: String, action: &Cha .read_component_cloned::(entity) { Some(pos) => { - let mut current = entity; - - for _ in 0..1 { - current = server - .create_npc(comp::Character::random()) - .with(comp::Control::default()) - .with(comp::Agent::Pet { - target: current, - offset: Vec2::zero(), - }) - .with(pos) - .build(); - } + server + .create_npc( + "Bungo".to_owned(), + comp::Body::Humanoid(comp::HumanoidBody::random()), + ) + .with(comp::Control::default()) + .with(comp::Agent::Pet { + target: entity, + offset: Vec2::zero(), + }) + .with(pos) + .build(); server .clients - .notify(entity, ServerMsg::Chat("Pet spawned!".to_owned())); + .notify(entity, ServerMsg::Chat("Spawned pet!".to_owned())); } None => server .clients diff --git a/server/src/lib.rs b/server/src/lib.rs index 55c755c26f..f25962deb5 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -14,7 +14,6 @@ use crate::{ }; use common::{ comp, - comp::character::Animation, msg::{ClientMsg, ClientState, RequestStateError, ServerMsg}, net::PostOffice, state::{State, Uid}, @@ -81,10 +80,13 @@ impl Server { }; for i in 0..4 { - this.create_npc(comp::Character::random()) - .with(comp::Control::default()) - .with(comp::Agent::Wanderer(Vec2::zero())) - .build(); + this.create_npc( + "Tobermory".to_owned(), + comp::Body::Humanoid(comp::HumanoidBody::random()), + ) + .with(comp::Control::default()) + .with(comp::Agent::Wanderer(Vec2::zero())) + .build(); } Ok(this) @@ -114,24 +116,31 @@ impl Server { /// Build a non-player character #[allow(dead_code)] - pub fn create_npc(&mut self, character: comp::Character) -> EcsEntityBuilder { + pub fn create_npc(&mut self, name: String, body: comp::Body) -> EcsEntityBuilder { self.state .ecs_mut() .create_entity_synced() .with(comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0))) .with(comp::phys::Vel(Vec3::zero())) .with(comp::phys::Dir(Vec3::unit_y())) - .with(comp::AnimationHistory::new(Animation::Idle)) - .with(character) + .with(comp::AnimationHistory::new(comp::Animation::Idle)) + .with(comp::Actor::Character { name, body }) } pub fn create_player_character( state: &mut State, entity: EcsEntity, client: &mut Client, - character: comp::Character, + name: String, + body: comp::HumanoidBody, ) { - state.write_component(entity, character); + state.write_component( + entity, + comp::Actor::Character { + name, + body: comp::Body::Humanoid(body), + }, + ); 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())); @@ -139,7 +148,7 @@ impl Server { state.write_component(entity, comp::phys::ForceUpdate); // Set initial animation - state.write_component(entity, comp::AnimationHistory::new(Animation::Idle)); + state.write_component(entity, comp::AnimationHistory::new(comp::Animation::Idle)); // Tell the client his request was successful client.notify(ServerMsg::StateAnswer(Ok(ClientState::Character))); @@ -337,13 +346,13 @@ impl Server { // Use RequestState instead (No need to send `player` again) _ => client.error_state(RequestStateError::Impossible), }, - ClientMsg::Character(character) => match client.client_state { + ClientMsg::Character { name, body } => match client.client_state { // Become Registered first ClientState::Connected => { client.error_state(RequestStateError::Impossible) } ClientState::Registered | ClientState::Spectator => { - Self::create_player_character(state, entity, client, character) + Self::create_player_character(state, entity, client, name, body) } ClientState::Character => { client.error_state(RequestStateError::Already) diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 13f430d082..9316ca3682 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -76,7 +76,10 @@ impl PlayState for CharSelectionState { self.client .borrow_mut() .postbox - .send_message(ClientMsg::Character(self.char_selection_ui.character)); + .send_message(ClientMsg::Character { + name: self.char_selection_ui.character_name.clone(), + body: self.char_selection_ui.character_body, + }); return PlayStateResult::Switch(Box::new(SessionState::new( &mut global_state.window, self.client.clone(), diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs index 6c999afa4b..e8511215f0 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::Character, figure::Segment}; +use common::{comp::HumanoidBody, figure::Segment}; use vek::*; struct Skybox { @@ -108,7 +108,7 @@ impl Scene { let model = self.figure_model_cache.get_or_create_model( renderer, - Character::random(), + HumanoidBody::random(), client.get_tick(), ); renderer.render_figure( diff --git a/voxygen/src/menu/char_selection/ui.rs b/voxygen/src/menu/char_selection/ui.rs index d53f9f0ffa..414606e0b4 100644 --- a/voxygen/src/menu/char_selection/ui.rs +++ b/voxygen/src/menu/char_selection/ui.rs @@ -7,8 +7,9 @@ use crate::{ }, window::Window, }; -use common::comp::character::{ - Belt, Character, Chest, Foot, Gender, Hand, Head, Pants, Race, Weapon, +use common::comp::{ + actor::{Belt, BodyType, Chest, Foot, Hand, Head, Pants, Race, Weapon}, + HumanoidBody, }; use conrod_core::{ color, @@ -37,7 +38,7 @@ widget_ids! { weapon_heading, weapon_description, races_bg, - gender_bg, + body_type_bg, desc_bg, skin_eyes_window, hair_window, @@ -71,8 +72,8 @@ widget_ids! { race_4, race_5, race_6, - gender_1, - gender_2, + body_type_1, + body_type_2, weapon_1, weapon_2, weapon_3, @@ -267,8 +268,8 @@ pub struct CharSelectionUi { fonts: Fonts, character_creation: bool, selected_char_no: Option, - character_name: String, - pub character: Character, + pub character_name: String, + pub character_body: HumanoidBody, creation_state: CreationState, } @@ -293,7 +294,7 @@ impl CharSelectionUi { character_creation: false, selected_char_no: None, character_name: "Character Name".to_string(), - character: Character::random(), + character_body: HumanoidBody::random(), creation_state: CreationState::Race, } } @@ -597,14 +598,14 @@ impl CharSelectionUi { // for alignment Rectangle::fill_with([151.0, 68.0], color::TRANSPARENT) .mid_top_with_margin_on(self.ids.creation_window, 210.0) - .set(self.ids.gender_bg, ui_widgets); + .set(self.ids.body_type_bg, ui_widgets); // Male Image::new(self.imgs.male) .w_h(68.0, 68.0) - .mid_left_of(self.ids.gender_bg) + .mid_left_of(self.ids.body_type_bg) .set(self.ids.male, ui_widgets); - if Button::image(if let Gender::Male = self.character.gender { + if Button::image(if let BodyType::Male = self.character_body.body_type { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -612,17 +613,17 @@ impl CharSelectionUi { .middle_of(self.ids.male) .hover_image(self.imgs.icon_border_mo) .press_image(self.imgs.icon_border_press) - .set(self.ids.gender_1, ui_widgets) + .set(self.ids.body_type_1, ui_widgets) .was_clicked() { - self.character.gender = Gender::Male; + self.character_body.body_type = BodyType::Male; } // Female Image::new(self.imgs.female) .w_h(68.0, 68.0) .right_from(self.ids.male, 16.0) .set(self.ids.female, ui_widgets); - if Button::image(if let Gender::Female = self.character.gender { + if Button::image(if let BodyType::Female = self.character_body.body_type { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -630,10 +631,10 @@ impl CharSelectionUi { .middle_of(self.ids.female) .hover_image(self.imgs.icon_border_mo) .press_image(self.imgs.icon_border_press) - .set(self.ids.gender_2, ui_widgets) + .set(self.ids.body_type_2, ui_widgets) .was_clicked() { - self.character.gender = Gender::Female; + self.character_body.body_type = BodyType::Female; } // for alignment Rectangle::fill_with([458.0, 68.0], color::TRANSPARENT) @@ -641,7 +642,7 @@ impl CharSelectionUi { .set(self.ids.races_bg, ui_widgets); // TODO: If races where in some sort of array format we could do this in a loop // Human - Image::new(if let Gender::Male = self.character.gender { + Image::new(if let BodyType::Male = self.character_body.body_type { self.imgs.human_m } else { self.imgs.human_f @@ -649,7 +650,7 @@ impl CharSelectionUi { .w_h(68.0, 68.0) .mid_left_of(self.ids.races_bg) .set(self.ids.human, ui_widgets); - if Button::image(if let Race::Human = self.character.race { + if Button::image(if let Race::Human = self.character_body.race { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -660,11 +661,11 @@ impl CharSelectionUi { .set(self.ids.race_1, ui_widgets) .was_clicked() { - self.character.race = Race::Human; + self.character_body.race = Race::Human; } // Orc - Image::new(if let Gender::Male = self.character.gender { + Image::new(if let BodyType::Male = self.character_body.body_type { self.imgs.orc_m } else { self.imgs.orc_f @@ -672,7 +673,7 @@ impl CharSelectionUi { .w_h(68.0, 68.0) .right_from(self.ids.human, 10.0) .set(self.ids.orc, ui_widgets); - if Button::image(if let Race::Orc = self.character.race { + if Button::image(if let Race::Orc = self.character_body.race { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -683,10 +684,10 @@ impl CharSelectionUi { .set(self.ids.race_2, ui_widgets) .was_clicked() { - self.character.race = Race::Orc; + self.character_body.race = Race::Orc; } // Dwarf - Image::new(if let Gender::Male = self.character.gender { + Image::new(if let BodyType::Male = self.character_body.body_type { self.imgs.dwarf_m } else { self.imgs.dwarf_f @@ -694,7 +695,7 @@ impl CharSelectionUi { .w_h(68.0, 68.0) .right_from(self.ids.human, 10.0 * 2.0 + 68.0) .set(self.ids.dwarf, ui_widgets); - if Button::image(if let Race::Dwarf = self.character.race { + if Button::image(if let Race::Dwarf = self.character_body.race { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -705,10 +706,10 @@ impl CharSelectionUi { .set(self.ids.race_3, ui_widgets) .was_clicked() { - self.character.race = Race::Dwarf; + self.character_body.race = Race::Dwarf; } // Elf - Image::new(if let Gender::Male = self.character.gender { + Image::new(if let BodyType::Male = self.character_body.body_type { self.imgs.elf_m } else { self.imgs.elf_f @@ -716,7 +717,7 @@ impl CharSelectionUi { .w_h(68.0, 68.0) .right_from(self.ids.human, 10.0 * 3.0 + 68.0 * 2.0) .set(self.ids.elf, ui_widgets); - if Button::image(if let Race::Elf = self.character.race { + if Button::image(if let Race::Elf = self.character_body.race { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -727,10 +728,10 @@ impl CharSelectionUi { .set(self.ids.race_4, ui_widgets) .was_clicked() { - self.character.race = Race::Elf; + self.character_body.race = Race::Elf; } // Undead - Image::new(if let Gender::Male = self.character.gender { + Image::new(if let BodyType::Male = self.character_body.body_type { self.imgs.undead_m } else { self.imgs.undead_f @@ -738,7 +739,7 @@ impl CharSelectionUi { .w_h(68.0, 68.0) .right_from(self.ids.human, 10.0 * 4.0 + 68.0 * 3.0) .set(self.ids.undead, ui_widgets); - if Button::image(if let Race::Undead = self.character.race { + if Button::image(if let Race::Undead = self.character_body.race { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -749,17 +750,17 @@ impl CharSelectionUi { .set(self.ids.race_5, ui_widgets) .was_clicked() { - self.character.race = Race::Undead; + self.character_body.race = Race::Undead; } // Danari - Image::new(if let Gender::Male = self.character.gender { + Image::new(if let BodyType::Male = self.character_body.body_type { self.imgs.danari_m } else { self.imgs.danari_f }) .right_from(self.ids.human, 10.0 * 5.0 + 68.0 * 4.0) .set(self.ids.danari, ui_widgets); - if Button::image(if let Race::Danari = self.character.race { + if Button::image(if let Race::Danari = self.character_body.race { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -771,7 +772,7 @@ impl CharSelectionUi { .set(self.ids.race_6, ui_widgets) .was_clicked() { - self.character.race = Race::Danari; + self.character_body.race = Race::Danari; } // Description Headline and Text @@ -832,7 +833,7 @@ impl CharSelectionUi { \n\ Outcast communities consisting of these Blessed Danari have formed all over the land."; - let (race_str, race_desc) = match self.character.race { + let (race_str, race_desc) = match self.character_body.race { Race::Human => ("Humans", HUMAN_DESC), Race::Orc => ("Orcs", ORC_DESC), Race::Dwarf => ("Dwarves", DWARF_DESC), @@ -872,7 +873,7 @@ impl CharSelectionUi { .w_h(60.0, 60.0) .mid_left_of(self.ids.weapon_bg) .set(self.ids.sword_shield, ui_widgets); - if Button::image(if let Weapon::SwordShield = self.character.weapon { + if Button::image(if let Weapon::SwordShield = self.character_body.weapon { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -883,7 +884,7 @@ impl CharSelectionUi { .set(self.ids.weapon_1, ui_widgets) .was_clicked() { - self.character.weapon = Weapon::SwordShield; + self.character_body.weapon = Weapon::SwordShield; } // Daggers @@ -891,7 +892,7 @@ impl CharSelectionUi { .w_h(60.0, 60.0) .right_from(self.ids.sword_shield, 8.0) .set(self.ids.daggers, ui_widgets); - if Button::image(if let Weapon::Daggers = self.character.weapon { + if Button::image(if let Weapon::Daggers = self.character_body.weapon { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -902,7 +903,7 @@ impl CharSelectionUi { .set(self.ids.weapon_2, ui_widgets) .was_clicked() { - self.character.weapon = Weapon::Daggers; + self.character_body.weapon = Weapon::Daggers; } // Sword @@ -910,7 +911,7 @@ impl CharSelectionUi { .w_h(60.0, 60.0) .right_from(self.ids.sword_shield, 8.0 * 2.0 + 60.0 * 1.0) .set(self.ids.sword, ui_widgets); - if Button::image(if let Weapon::Sword = self.character.weapon { + if Button::image(if let Weapon::Sword = self.character_body.weapon { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -921,14 +922,14 @@ impl CharSelectionUi { .set(self.ids.weapon_3, ui_widgets) .was_clicked() { - self.character.weapon = Weapon::Sword; + self.character_body.weapon = Weapon::Sword; } // Axe Image::new(self.imgs.axe) .w_h(60.0, 60.0) .right_from(self.ids.sword_shield, 8.0 * 3.0 + 60.0 * 2.0) .set(self.ids.axe, ui_widgets); - if Button::image(if let Weapon::Axe = self.character.weapon { + if Button::image(if let Weapon::Axe = self.character_body.weapon { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -939,14 +940,14 @@ impl CharSelectionUi { .set(self.ids.weapon_4, ui_widgets) .was_clicked() { - self.character.weapon = Weapon::Axe; + self.character_body.weapon = Weapon::Axe; } // Hammer Image::new(self.imgs.hammer) .w_h(60.0, 60.0) .right_from(self.ids.sword_shield, 8.0 * 4.0 + 60.0 * 3.0) .set(self.ids.hammer, ui_widgets); - if Button::image(if let Weapon::Hammer = self.character.weapon { + if Button::image(if let Weapon::Hammer = self.character_body.weapon { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -957,14 +958,14 @@ impl CharSelectionUi { .set(self.ids.weapon_5, ui_widgets) .was_clicked() { - self.character.weapon = Weapon::Hammer; + self.character_body.weapon = Weapon::Hammer; } // Bow Image::new(self.imgs.bow) .w_h(60.0, 60.0) .right_from(self.ids.sword_shield, 8.0 * 5.0 + 60.0 * 4.0) .set(self.ids.bow, ui_widgets); - if Button::image(if let Weapon::Bow = self.character.weapon { + if Button::image(if let Weapon::Bow = self.character_body.weapon { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -975,14 +976,14 @@ impl CharSelectionUi { .set(self.ids.weapon_6, ui_widgets) .was_clicked() { - self.character.weapon = Weapon::Bow; + self.character_body.weapon = Weapon::Bow; } // Staff Image::new(self.imgs.staff) .w_h(60.0, 60.0) .right_from(self.ids.sword_shield, 8.0 * 6.0 + 60.0 * 5.0) .set(self.ids.staff, ui_widgets); - if Button::image(if let Weapon::Staff = self.character.weapon { + if Button::image(if let Weapon::Staff = self.character_body.weapon { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -993,7 +994,7 @@ impl CharSelectionUi { .set(self.ids.weapon_7, ui_widgets) .was_clicked() { - self.character.weapon = Weapon::Staff; + self.character_body.weapon = Weapon::Staff; } // TODO: Load these from files (or from the server???) @@ -1005,7 +1006,7 @@ impl CharSelectionUi { const BOW_DESC: &str = " MISSING "; const STAFF_DESC: &str = " MISSING "; - let (weapon_str, weapon_desc) = match self.character.weapon { + let (weapon_str, weapon_desc) = match self.character_body.weapon { Weapon::SwordShield => ("Sword and Shield", SWORDSHIELD_DESC), Weapon::Daggers => ("Daggers", DAGGERS_DESC), Weapon::Sword => ("Sword", SWORD_DESC), @@ -1352,7 +1353,7 @@ impl CharSelectionUi { .was_clicked() {}; // Beard -> Only active when "male" was chosen - if let Gender::Male = self.character.gender { + if let BodyType::Male = self.character_body.body_type { Text::new("Beard Style") .mid_top_with_margin_on(self.ids.hair_window, 340.0) .color(TEXT_COLOR) @@ -1383,7 +1384,7 @@ impl CharSelectionUi { // Color -> Picker // Brightness -> Slider BodyPart::Accessories => { - match self.character.race { + match self.character_body.race { Race::Human => { Text::new("Head Band") .mid_top_with_margin_on(self.ids.accessories_window, 60.0) diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index a9157419e7..ff6272e856 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -4,8 +4,8 @@ use vek::*; // Project use common::{ terrain::Block, - vol::{ReadVol, SizedVol, Vox}, - volumes::dyna::Dyna, + vol::{ReadVol, SizedVol, VolSize, Vox}, + volumes::{dyna::Dyna, vol_map::VolMap}, }; // Crate @@ -43,3 +43,65 @@ impl Meshable for Dyna { mesh } } + +impl Meshable for VolMap { + type Pipeline = TerrainPipeline; + type Supplement = Aabb; + + fn generate_mesh(&self, range: Self::Supplement) -> Mesh { + let mut mesh = Mesh::new(); + + let mut last_chunk_pos = self.pos_key(range.min); + let mut last_chunk = self.get_key(last_chunk_pos); + + let size = range.max - range.min; + for x in 0..size.x { + for y in 0..size.y { + for z in 0..size.z { + let pos = Vec3::new(x, y, z); + + let new_chunk_pos = self.pos_key(range.min + pos); + if last_chunk_pos != new_chunk_pos { + last_chunk = self.get_key(new_chunk_pos); + last_chunk_pos = new_chunk_pos; + } + let offs = pos.map(|e| e as f32 - 1.0); + if let Some(chunk) = last_chunk { + let chunk_pos = Self::chunk_offs(range.min + pos); + if let Some(col) = chunk.get(chunk_pos).ok().and_then(|vox| vox.get_color()) + { + let col = col.map(|e| e as f32 / 255.0); + + vol::push_vox_verts( + &mut mesh, + self, + range.min + pos, + offs, + col, + TerrainVertex::new, + ); + } + } else { + if let Some(col) = self + .get(range.min + pos) + .ok() + .and_then(|vox| vox.get_color()) + { + let col = col.map(|e| e as f32 / 255.0); + + vol::push_vox_verts( + &mut mesh, + self, + range.min + pos, + offs, + col, + TerrainVertex::new, + ); + } + } + } + } + } + mesh + } +} diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index ee363764dc..9ef9dad08c 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -14,7 +14,8 @@ use common::{ assets, comp::{ self, - character::{Belt, Character, Chest, Draw, Foot, Hand, Head, Pants, Shoulder, Weapon}, + actor::{Belt, Chest, Foot, Hand, Head, Pants, Shoulder, Weapon}, + Body, HumanoidBody, }, figure::Segment, msg, @@ -25,7 +26,7 @@ use std::{collections::HashMap, f32}; use vek::*; pub struct FigureModelCache { - models: HashMap, u64)>, + models: HashMap, u64)>, } impl FigureModelCache { @@ -38,31 +39,30 @@ impl FigureModelCache { pub fn get_or_create_model( &mut self, renderer: &mut Renderer, - character: Character, + body: HumanoidBody, tick: u64, ) -> &Model { - match self.models.get_mut(&character) { + match self.models.get_mut(&body) { Some((model, last_used)) => { *last_used = tick; } None => { self.models.insert( - character, + body, ( { let bone_meshes = [ - Some(Self::load_head(character.head)), - Some(Self::load_chest(character.chest)), - Some(Self::load_belt(character.belt)), - Some(Self::load_pants(character.pants)), - Some(Self::load_left_hand(character.hand)), - Some(Self::load_right_hand(character.hand)), - Some(Self::load_left_foot(character.foot)), - Some(Self::load_right_foot(character.foot)), - Some(Self::load_weapon(character.weapon)), - Some(Self::load_left_shoulder(character.shoulder)), - Some(Self::load_right_shoulder(character.shoulder)), - //Some(Self::load_draw(character.draw)), + 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, @@ -89,7 +89,7 @@ impl FigureModelCache { } } - &self.models[&character].0 + &self.models[&body].0 } pub fn clean(&mut self, tick: u64) { @@ -108,7 +108,7 @@ impl FigureModelCache { fn load_head(head: Head) -> Mesh { Self::load_mesh( match head { - Head::DefaultHead => "head.vox", + Head::Default => "head.vox", }, Vec3::new(-7.0, -5.5, -6.0), ) @@ -117,7 +117,7 @@ impl FigureModelCache { fn load_chest(chest: Chest) -> Mesh { Self::load_mesh( match chest { - Chest::DefaultChest => "chest.vox", + Chest::Default => "chest.vox", }, Vec3::new(-6.0, -3.5, 0.0), ) @@ -126,7 +126,7 @@ impl FigureModelCache { fn load_belt(belt: Belt) -> Mesh { Self::load_mesh( match belt { - Belt::DefaultBelt => "belt.vox", + Belt::Default => "belt.vox", }, Vec3::new(-5.0, -3.5, 0.0), ) @@ -135,7 +135,7 @@ impl FigureModelCache { fn load_pants(pants: Pants) -> Mesh { Self::load_mesh( match pants { - Pants::DefaultPants => "pants.vox", + Pants::Default => "pants.vox", }, Vec3::new(-5.0, -3.5, 0.0), ) @@ -144,7 +144,7 @@ impl FigureModelCache { fn load_left_hand(hand: Hand) -> Mesh { Self::load_mesh( match hand { - Hand::DefaultHand => "hand.vox", + Hand::Default => "hand.vox", }, Vec3::new(2.0, 0.0, -7.0), ) @@ -153,7 +153,7 @@ impl FigureModelCache { fn load_right_hand(hand: Hand) -> Mesh { Self::load_mesh( match hand { - Hand::DefaultHand => "hand.vox", + Hand::Default => "hand.vox", }, Vec3::new(2.0, 0.0, -7.0), ) @@ -162,7 +162,7 @@ impl FigureModelCache { fn load_left_foot(foot: Foot) -> Mesh { Self::load_mesh( match foot { - Foot::DefaultFoot => "foot.vox", + Foot::Default => "foot.vox", }, Vec3::new(2.5, -3.5, -9.0), ) @@ -171,7 +171,7 @@ impl FigureModelCache { fn load_right_foot(foot: Foot) -> Mesh { Self::load_mesh( match foot { - Foot::DefaultFoot => "foot.vox", + Foot::Default => "foot.vox", }, Vec3::new(2.5, -3.5, -9.0), ) @@ -191,7 +191,7 @@ impl FigureModelCache { fn load_left_shoulder(shoulder: Shoulder) -> Mesh { Self::load_mesh( match shoulder { - Shoulder::DefaultShoulder => "shoulder_l.vox", + Shoulder::Default => "shoulder_l.vox", }, Vec3::new(2.5, 0.0, 0.0), ) @@ -200,7 +200,7 @@ impl FigureModelCache { fn load_right_shoulder(shoulder: Shoulder) -> Mesh { Self::load_mesh( match shoulder { - Shoulder::DefaultShoulder => "shoulder_r.vox", + Shoulder::Default => "shoulder_r.vox", }, Vec3::new(2.5, 0.0, 0.0), ) @@ -238,42 +238,48 @@ 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, character, animation_history) in ( + 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::(), &ecs.read_storage::(), ) .join() { - let state = self - .states - .entry(entity) - .or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new())); + 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::character::Animation::Idle => IdleAnimation::update_skeleton( - state.skeleton_mut(), - time, - animation_history.time, - ), - comp::character::Animation::Run => RunAnimation::update_skeleton( - state.skeleton_mut(), - (vel.0.magnitude(), time), - animation_history.time, - ), - comp::character::Animation::Jump => JumpAnimation::update_skeleton( - state.skeleton_mut(), - time, - animation_history.time, - ), - }; + 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.skeleton.interpolate(&target_skeleton); - state.update(renderer, pos.0, dir.0); + state.update(renderer, pos.0, dir.0); + } // TODO: Non-humanoid bodies + }, + // TODO: Non-character actors + } } self.states @@ -289,13 +295,22 @@ impl FigureMgr { let tick = client.get_tick(); let ecs = client.state().ecs(); - for (entity, &character) in (&ecs.entities(), &ecs.read_storage::()).join() - { - if let Some(state) = self.states.get(&entity) { - let model = self - .model_cache - .get_or_create_model(renderer, character, tick); - renderer.render_figure(model, globals, &state.locals(), state.bone_consts()); + 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 } } } diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index e34251a06c..f15d730d72 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1,9 +1,5 @@ // Standard -use std::{ - collections::{HashMap, LinkedList}, - sync::mpsc, - time::Duration, -}; +use std::{collections::HashMap, sync::mpsc, time::Duration}; // Library use vek::*; @@ -42,10 +38,11 @@ fn mesh_worker( pos: Vec3, started_tick: u64, volume: ::Sample, + supplement: Aabb, ) -> MeshWorkerResponse { MeshWorkerResponse { pos, - mesh: volume.generate_mesh(()), + mesh: volume.generate_mesh(supplement), started_tick, } } @@ -57,7 +54,7 @@ pub struct Terrain { // We keep the sender component for no reason othe than to clone it and send it to new workers. mesh_send_tmp: mpsc::Sender, mesh_recv: mpsc::Receiver, - mesh_todo: LinkedList, + mesh_todo: HashMap, ChunkMeshState>, } impl Terrain { @@ -71,7 +68,7 @@ impl Terrain { mesh_send_tmp: send, mesh_recv: recv, - mesh_todo: LinkedList::new(), + mesh_todo: HashMap::new(), } } @@ -97,75 +94,70 @@ impl Terrain { let pos = pos + Vec3::new(i, j, k); if client.state().terrain().get_key(pos).is_some() { - match self.mesh_todo.iter_mut().find(|todo| todo.pos == pos) { - //Some(todo) => todo.started_tick = current_tick, - // The chunk it's queued yet, add it to the queue - _ /* None */ => self.mesh_todo.push_back(ChunkMeshState { + // re-mesh loaded chunks that border new/changed chunks + if self.chunks.contains_key(&pos) || (i, j, k) == (0, 0, 0) { + self.mesh_todo.entry(pos).or_insert(ChunkMeshState { pos, started_tick: current_tick, active_worker: false, - }), + }); } } } } } } - // Remove any models for chunks that have been recently removed for pos in &client.state().changes().removed_chunks { self.chunks.remove(pos); - self.mesh_todo.drain_filter(|todo| todo.pos == *pos); + self.mesh_todo.remove(pos); } - // Clone the sender to the thread can send us the chunk data back - // TODO: It's a bit hacky cloning it here and then cloning it again below. Fix this. - let send = self.mesh_send_tmp.clone(); - - self.mesh_todo - .iter_mut() + for todo in self + .mesh_todo + .values_mut() // Only spawn workers for meshing jobs without an active worker already .filter(|todo| !todo.active_worker) - .for_each(|todo| { - // Find the area of the terrain we want. Because meshing needs to compute things like - // ambient occlusion and edge elision, we also need to borders of the chunk's - // neighbours too (hence the `- 1` and `+ 1`). - let aabb = Aabb { - min: todo - .pos - .map2(TerrainMap::chunk_size(), |e, sz| e * sz as i32 - 1), - max: todo - .pos - .map2(TerrainMap::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1), - }; + { + // Find the area of the terrain we want. Because meshing needs to compute things like + // ambient occlusion and edge elision, we also need to borders of the chunk's + // neighbours too (hence the `- 1` and `+ 1`). + let aabb = Aabb { + min: todo + .pos + .map2(TerrainMap::chunk_size(), |e, sz| e * sz as i32 - 1), + max: todo + .pos + .map2(TerrainMap::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1), + }; - // Copy out the chunk data we need to perform the meshing. We do this by taking a - // sample of the terrain that includes both the chunk we want and - let volume = match client.state().terrain().sample(aabb) { - Ok(sample) => sample, - // If either this chunk or its neighbours doesn't yet exist, so we keep it in the - // todo queue to be processed at a later date when we have its neighbours. - Err(VolMapErr::NoSuchChunk) => return, - _ => panic!("Unhandled edge case"), - }; + // Copy out the chunk data we need to perform the meshing. We do this by taking a + // sample of the terrain that includes both the chunk we want and + let volume = match client.state().terrain().sample(aabb) { + Ok(sample) => sample, + // If either this chunk or its neighbours doesn't yet exist, so we keep it in the + // todo queue to be processed at a later date when we have its neighbours. + Err(VolMapErr::NoSuchChunk) => return, + _ => panic!("Unhandled edge case"), + }; - // Clone various things to that they can be moved into the thread - let send = send.clone(); - let pos = todo.pos; + // Clone various things to that they can be moved into the thread + let send = self.mesh_send_tmp.clone(); + let pos = todo.pos; - // Queue the worker thread - client.thread_pool().execute(move || { - send.send(mesh_worker(pos, current_tick, volume)) - .expect("Failed to send chunk mesh to main thread"); - }); - todo.active_worker = true; + // 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"); }); + todo.active_worker = true; + } // Receive a chunk mesh from a worker thread, upload it to the GPU and then store it // Only pull out one chunk per frame to avoid an unacceptable amount of blocking lag due // to the GPU upload. That still gives us a 60 chunks / second budget to play with. if let Ok(response) = self.mesh_recv.recv_timeout(Duration::new(0, 0)) { - match self.mesh_todo.iter().find(|todo| todo.pos == response.pos) { + match self.mesh_todo.get(&response.pos) { // It's the mesh we want, insert the newly finished model into the terrain model // data structure (convert the mesh to a model first of course) Some(todo) if response.started_tick == todo.started_tick => {