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