mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge more models into one mesh than we did previously.
This commit is contained in:
parent
3155c31e66
commit
10245e0c1b
@ -43,10 +43,7 @@ impl Alignment {
|
|||||||
|
|
||||||
// TODO: Remove this hack
|
// TODO: Remove this hack
|
||||||
pub fn is_friendly_to_players(&self) -> bool {
|
pub fn is_friendly_to_players(&self) -> bool {
|
||||||
match self {
|
matches!(self, Alignment::Npc | Alignment::Tame | Alignment::Owned(_))
|
||||||
Alignment::Npc | Alignment::Tame | Alignment::Owned(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,19 +126,9 @@ pub enum Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Activity {
|
impl Activity {
|
||||||
pub fn is_follow(&self) -> bool {
|
pub fn is_follow(&self) -> bool { matches!(self, Activity::Follow { .. }) }
|
||||||
match self {
|
|
||||||
Activity::Follow { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_attack(&self) -> bool {
|
pub fn is_attack(&self) -> bool { matches!(self, Activity::Attack { .. }) }
|
||||||
match self {
|
|
||||||
Activity::Attack { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Activity {
|
impl Default for Activity {
|
||||||
|
@ -201,12 +201,7 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
pub fn is_humanoid(&self) -> bool {
|
pub fn is_humanoid(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
||||||
match self {
|
|
||||||
Body::Humanoid(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: this might need to be refined to something more complex for realistic
|
// Note: this might need to be refined to something more complex for realistic
|
||||||
// behavior with less cylindrical bodies (e.g. wolfs)
|
// behavior with less cylindrical bodies (e.g. wolfs)
|
||||||
|
@ -73,7 +73,7 @@ pub enum CharacterState {
|
|||||||
|
|
||||||
impl CharacterState {
|
impl CharacterState {
|
||||||
pub fn is_wield(&self) -> bool {
|
pub fn is_wield(&self) -> bool {
|
||||||
match self {
|
matches!(self,
|
||||||
CharacterState::Wielding
|
CharacterState::Wielding
|
||||||
| CharacterState::BasicMelee(_)
|
| CharacterState::BasicMelee(_)
|
||||||
| CharacterState::BasicRanged(_)
|
| CharacterState::BasicRanged(_)
|
||||||
@ -82,50 +82,37 @@ impl CharacterState {
|
|||||||
| CharacterState::BasicBlock
|
| CharacterState::BasicBlock
|
||||||
| CharacterState::LeapMelee(_)
|
| CharacterState::LeapMelee(_)
|
||||||
| CharacterState::SpinMelee(_)
|
| CharacterState::SpinMelee(_)
|
||||||
| CharacterState::ChargedRanged(_) => true,
|
| CharacterState::ChargedRanged(_)
|
||||||
_ => false,
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_attack(&self) -> bool {
|
pub fn is_attack(&self) -> bool {
|
||||||
match self {
|
matches!(self,
|
||||||
CharacterState::BasicMelee(_)
|
CharacterState::BasicMelee(_)
|
||||||
| CharacterState::BasicRanged(_)
|
| CharacterState::BasicRanged(_)
|
||||||
| CharacterState::DashMelee(_)
|
| CharacterState::DashMelee(_)
|
||||||
| CharacterState::TripleStrike(_)
|
| CharacterState::TripleStrike(_)
|
||||||
| CharacterState::LeapMelee(_)
|
| CharacterState::LeapMelee(_)
|
||||||
| CharacterState::SpinMelee(_)
|
| CharacterState::SpinMelee(_)
|
||||||
| CharacterState::ChargedRanged(_) => true,
|
| CharacterState::ChargedRanged(_)
|
||||||
_ => false,
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_aimed(&self) -> bool {
|
pub fn is_aimed(&self) -> bool {
|
||||||
match self {
|
matches!(self,
|
||||||
CharacterState::BasicMelee(_)
|
CharacterState::BasicMelee(_)
|
||||||
| CharacterState::BasicRanged(_)
|
| CharacterState::BasicRanged(_)
|
||||||
| CharacterState::DashMelee(_)
|
| CharacterState::DashMelee(_)
|
||||||
| CharacterState::TripleStrike(_)
|
| CharacterState::TripleStrike(_)
|
||||||
| CharacterState::BasicBlock
|
| CharacterState::BasicBlock
|
||||||
| CharacterState::LeapMelee(_)
|
| CharacterState::LeapMelee(_)
|
||||||
| CharacterState::ChargedRanged(_) => true,
|
| CharacterState::ChargedRanged(_)
|
||||||
_ => false,
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_block(&self) -> bool {
|
pub fn is_block(&self) -> bool { matches!(self, CharacterState::BasicBlock) }
|
||||||
match self {
|
|
||||||
CharacterState::BasicBlock => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_dodge(&self) -> bool {
|
pub fn is_dodge(&self) -> bool { matches!(self, CharacterState::Roll(_)) }
|
||||||
match self {
|
|
||||||
CharacterState::Roll(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compares for shallow equality (does not check internal struct equality)
|
/// Compares for shallow equality (does not check internal struct equality)
|
||||||
pub fn same_variant(&self, other: &Self) -> bool {
|
pub fn same_variant(&self, other: &Self) -> bool {
|
||||||
|
@ -25,10 +25,5 @@ pub enum MatCell {
|
|||||||
impl Vox for MatCell {
|
impl Vox for MatCell {
|
||||||
fn empty() -> Self { MatCell::None }
|
fn empty() -> Self { MatCell::None }
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool { matches!(self, MatCell::None) }
|
||||||
match self {
|
|
||||||
MatCell::None => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#![feature(
|
#![feature(
|
||||||
arbitrary_enum_discriminant,
|
arbitrary_enum_discriminant,
|
||||||
const_checked_int_methods,
|
const_checked_int_methods,
|
||||||
const_if_match,
|
|
||||||
option_unwrap_none,
|
option_unwrap_none,
|
||||||
bool_to_option,
|
bool_to_option,
|
||||||
label_break_value,
|
label_break_value,
|
||||||
|
@ -201,12 +201,7 @@ impl BlockKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fluid(&self) -> bool {
|
pub fn is_fluid(&self) -> bool { matches!(self, BlockKind::Water) }
|
||||||
match self {
|
|
||||||
BlockKind::Water => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_glow(&self) -> Option<u8> {
|
pub fn get_glow(&self) -> Option<u8> {
|
||||||
// TODO: When we have proper volumetric lighting
|
// TODO: When we have proper volumetric lighting
|
||||||
|
@ -175,11 +175,7 @@ impl MapSizeLg {
|
|||||||
map_size_lg.y + TERRAIN_CHUNK_BLOCKS_LG < 32;
|
map_size_lg.y + TERRAIN_CHUNK_BLOCKS_LG < 32;
|
||||||
// Assertion on dimensions: product of dimensions must fit in a usize.
|
// Assertion on dimensions: product of dimensions must fit in a usize.
|
||||||
let chunks_product_in_range =
|
let chunks_product_in_range =
|
||||||
if let Some(_) = 1usize.checked_shl(map_size_lg.x + map_size_lg.y) {
|
matches!(1usize.checked_shl(map_size_lg.x + map_size_lg.y), Some(_));
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
if blocks_in_range && chunks_product_in_range {
|
if blocks_in_range && chunks_product_in_range {
|
||||||
// Cleared all invariants.
|
// Cleared all invariants.
|
||||||
Ok(MapSizeLg(map_size_lg))
|
Ok(MapSizeLg(map_size_lg))
|
||||||
|
@ -32,12 +32,7 @@ pub enum StructureBlock {
|
|||||||
impl Vox for StructureBlock {
|
impl Vox for StructureBlock {
|
||||||
fn empty() -> Self { StructureBlock::None }
|
fn empty() -> Self { StructureBlock::None }
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool { matches!(self, StructureBlock::None) }
|
||||||
match self {
|
|
||||||
StructureBlock::None => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![cfg_attr(test, deny(rust_2018_idioms))]
|
#![cfg_attr(test, deny(rust_2018_idioms))]
|
||||||
#![cfg_attr(test, deny(warnings))]
|
#![cfg_attr(test, deny(warnings))]
|
||||||
#![feature(try_trait, const_if_match)]
|
#![feature(try_trait)]
|
||||||
|
|
||||||
//! Crate to handle high level networking of messages with different
|
//! Crate to handle high level networking of messages with different
|
||||||
//! requirements and priorities over a number of protocols
|
//! requirements and priorities over a number of protocols
|
||||||
|
@ -99,43 +99,17 @@ pub(crate) fn partial_eq_io_error(first: &io::Error, second: &io::Error) -> bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn partial_eq_bincode(first: &bincode::ErrorKind, second: &bincode::ErrorKind) -> bool {
|
pub(crate) fn partial_eq_bincode(first: &bincode::ErrorKind, second: &bincode::ErrorKind) -> bool {
|
||||||
|
use bincode::ErrorKind::*;
|
||||||
match *first {
|
match *first {
|
||||||
bincode::ErrorKind::Io(ref f) => match *second {
|
Io(ref f) => matches!(*second, Io(ref s) if partial_eq_io_error(f, s)),
|
||||||
bincode::ErrorKind::Io(ref s) => partial_eq_io_error(f, s),
|
InvalidUtf8Encoding(f) => matches!(*second, InvalidUtf8Encoding(s) if f == s),
|
||||||
_ => false,
|
InvalidBoolEncoding(f) => matches!(*second, InvalidBoolEncoding(s) if f == s),
|
||||||
},
|
InvalidCharEncoding => matches!(*second, InvalidCharEncoding),
|
||||||
bincode::ErrorKind::InvalidUtf8Encoding(f) => match *second {
|
InvalidTagEncoding(f) => matches!(*second, InvalidTagEncoding(s) if f == s),
|
||||||
bincode::ErrorKind::InvalidUtf8Encoding(s) => f == s,
|
DeserializeAnyNotSupported => matches!(*second, DeserializeAnyNotSupported),
|
||||||
_ => false,
|
SizeLimit => matches!(*second, SizeLimit),
|
||||||
},
|
SequenceMustHaveLength => matches!(*second, SequenceMustHaveLength),
|
||||||
bincode::ErrorKind::InvalidBoolEncoding(f) => match *second {
|
Custom(ref f) => matches!(*second, Custom(ref s) if f == s),
|
||||||
bincode::ErrorKind::InvalidBoolEncoding(s) => f == s,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
bincode::ErrorKind::InvalidCharEncoding => match *second {
|
|
||||||
bincode::ErrorKind::InvalidCharEncoding => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
bincode::ErrorKind::InvalidTagEncoding(f) => match *second {
|
|
||||||
bincode::ErrorKind::InvalidTagEncoding(s) => f == s,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
bincode::ErrorKind::DeserializeAnyNotSupported => match *second {
|
|
||||||
bincode::ErrorKind::DeserializeAnyNotSupported => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
bincode::ErrorKind::SizeLimit => match *second {
|
|
||||||
bincode::ErrorKind::SizeLimit => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
bincode::ErrorKind::SequenceMustHaveLength => match *second {
|
|
||||||
bincode::ErrorKind::SequenceMustHaveLength => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
bincode::ErrorKind::Custom(ref f) => match *second {
|
|
||||||
bincode::ErrorKind::Custom(ref s) => f == s,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
nightly-2020-06-22
|
nightly-2020-08-14
|
||||||
|
@ -50,17 +50,17 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_registered(&self) -> bool {
|
pub fn is_registered(&self) -> bool {
|
||||||
match self.client_state {
|
matches!(
|
||||||
ClientState::Registered | ClientState::Spectator | ClientState::Character => true,
|
self.client_state,
|
||||||
_ => false,
|
ClientState::Registered | ClientState::Spectator | ClientState::Character
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_ingame(&self) -> bool {
|
pub fn is_ingame(&self) -> bool {
|
||||||
match self.client_state {
|
matches!(
|
||||||
ClientState::Spectator | ClientState::Character => true,
|
self.client_state,
|
||||||
_ => false,
|
ClientState::Spectator | ClientState::Character
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allow_state(&mut self, new_state: ClientState) {
|
pub fn allow_state(&mut self, new_state: ClientState) {
|
||||||
|
@ -33,6 +33,15 @@ pub fn handle_damage(server: &Server, uid: Uid, change: HealthChange) {
|
|||||||
/// other players. If the entity that killed it had stats, then give it exp for
|
/// other players. If the entity that killed it had stats, then give it exp for
|
||||||
/// the kill. Experience given is equal to the level of the entity that was
|
/// the kill. Experience given is equal to the level of the entity that was
|
||||||
/// killed times 10.
|
/// killed times 10.
|
||||||
|
// NOTE: Clippy incorrectly warns about a needless collect here because it does not
|
||||||
|
// understand that the pet count (which is computed during the first iteration over the
|
||||||
|
// members in range) is actually used by the second iteration over the members in range;
|
||||||
|
// since we have no way of knowing the pet count before the first loop finishes, we
|
||||||
|
// definitely need at least two loops. Then (currently) our only options are to store
|
||||||
|
// the member list in temporary space (e.g. by collecting to a vector), or to repeat
|
||||||
|
// the loop; but repeating the loop would currently be very inefficient since it has to
|
||||||
|
// rescan every entity on the server again.
|
||||||
|
#[allow(clippy::needless_collect)]
|
||||||
pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSource) {
|
pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSource) {
|
||||||
let state = server.state_mut();
|
let state = server.state_mut();
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ pub fn snuff_lantern(storage: &mut WriteStorage<comp::LightEmitter>, entity: Ecs
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::blocks_in_if_conditions)]
|
#[allow(clippy::blocks_in_if_conditions)]
|
||||||
|
#[allow(clippy::same_item_push)] // TODO: Pending review in #587
|
||||||
pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::InventoryManip) {
|
pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::InventoryManip) {
|
||||||
let state = server.state_mut();
|
let state = server.state_mut();
|
||||||
let mut dropped_items = Vec::new();
|
let mut dropped_items = Vec::new();
|
||||||
@ -336,6 +337,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
.and_then(|ldt| slot::loadout_remove(slot, ldt)),
|
.and_then(|ldt| slot::loadout_remove(slot, ldt)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME: We should really require the drop and write to be atomic!
|
||||||
if let (Some(item), Some(pos)) =
|
if let (Some(item), Some(pos)) =
|
||||||
(item, state.ecs().read_storage::<comp::Pos>().get(entity))
|
(item, state.ecs().read_storage::<comp::Pos>().get(entity))
|
||||||
{
|
{
|
||||||
@ -362,6 +364,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
let recipe_book = default_recipe_book();
|
let recipe_book = default_recipe_book();
|
||||||
let craft_result = recipe_book.get(&recipe).and_then(|r| r.perform(inv).ok());
|
let craft_result = recipe_book.get(&recipe).and_then(|r| r.perform(inv).ok());
|
||||||
|
|
||||||
|
// FIXME: We should really require the drop and write to be atomic!
|
||||||
if craft_result.is_some() {
|
if craft_result.is_some() {
|
||||||
let _ = state.ecs().write_storage().insert(
|
let _ = state.ecs().write_storage().insert(
|
||||||
entity,
|
entity,
|
||||||
|
@ -214,7 +214,6 @@ impl StateExt for State {
|
|||||||
// Notify clients of a player list update
|
// Notify clients of a player list update
|
||||||
let client_uid = self
|
let client_uid = self
|
||||||
.read_component_cloned::<Uid>(entity)
|
.read_component_cloned::<Uid>(entity)
|
||||||
.map(|u| u)
|
|
||||||
.expect("Client doesn't have a Uid!!!");
|
.expect("Client doesn't have a Uid!!!");
|
||||||
|
|
||||||
self.notify_registered_clients(ServerMsg::PlayerListUpdate(
|
self.notify_registered_clients(ServerMsg::PlayerListUpdate(
|
||||||
|
@ -338,7 +338,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.filter(|o| o.get_pos().and_then(&is_near).unwrap_or(true))
|
.filter(|o| o.get_pos().and_then(&is_near).unwrap_or(true))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if outcomes.len() > 0 {
|
if !outcomes.is_empty() {
|
||||||
client.notify(ServerMsg::Outcomes(outcomes));
|
client.notify(ServerMsg::Outcomes(outcomes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ impl Sys {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Give the player a welcome message
|
// Give the player a welcome message
|
||||||
if settings.server_description.len() > 0 {
|
if !settings.server_description.is_empty() {
|
||||||
client.notify(
|
client.notify(
|
||||||
ChatType::CommandInfo
|
ChatType::CommandInfo
|
||||||
.server_msg(settings.server_description.clone()),
|
.server_msg(settings.server_description.clone()),
|
||||||
|
@ -46,19 +46,16 @@ pub struct AudioFrontend {
|
|||||||
|
|
||||||
impl AudioFrontend {
|
impl AudioFrontend {
|
||||||
/// Construct with given device
|
/// Construct with given device
|
||||||
#[allow(clippy::redundant_clone)] // TODO: Pending review in #587
|
|
||||||
pub fn new(device: String, max_sfx_channels: usize) -> Self {
|
pub fn new(device: String, max_sfx_channels: usize) -> Self {
|
||||||
let mut sfx_channels = Vec::with_capacity(max_sfx_channels);
|
|
||||||
let audio_device = get_device_raw(&device);
|
let audio_device = get_device_raw(&device);
|
||||||
|
|
||||||
|
let mut sfx_channels = Vec::with_capacity(max_sfx_channels);
|
||||||
if let Some(audio_device) = &audio_device {
|
if let Some(audio_device) = &audio_device {
|
||||||
for _ in 0..max_sfx_channels {
|
sfx_channels.resize_with(max_sfx_channels, || SfxChannel::new(&audio_device));
|
||||||
sfx_channels.push(SfxChannel::new(&audio_device));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
device: device.clone(),
|
device,
|
||||||
device_list: list_devices(),
|
device_list: list_devices(),
|
||||||
audio_device,
|
audio_device,
|
||||||
sound_cache: SoundCache::default(),
|
sound_cache: SoundCache::default(),
|
||||||
|
@ -172,11 +172,7 @@ impl CombatEventMapper {
|
|||||||
/// ::Equipping to mean the weapon is drawn. This will need updating if the
|
/// ::Equipping to mean the weapon is drawn. This will need updating if the
|
||||||
/// animations change to match the wield_duration associated with the weapon
|
/// animations change to match the wield_duration associated with the weapon
|
||||||
fn weapon_drawn(character: &CharacterState) -> bool {
|
fn weapon_drawn(character: &CharacterState) -> bool {
|
||||||
character.is_wield()
|
character.is_wield() || matches!(character, CharacterState::Equipping { .. })
|
||||||
|| match character {
|
|
||||||
CharacterState::Equipping { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,10 +395,7 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
.widget_input(state.ids.chat_input)
|
.widget_input(state.ids.chat_input)
|
||||||
.presses()
|
.presses()
|
||||||
.key()
|
.key()
|
||||||
.any(|key_press| match key_press.key {
|
.any(|key_press| matches!(key_press.key, Key::Return if !state.input.is_empty()))
|
||||||
Key::Return if !state.input.is_empty() => true,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
let msg = state.input.clone();
|
let msg = state.input.clone();
|
||||||
state.update(|s| {
|
state.update(|s| {
|
||||||
|
@ -482,10 +482,7 @@ impl Show {
|
|||||||
|| self.spell
|
|| self.spell
|
||||||
|| self.help
|
|| self.help
|
||||||
|| self.intro
|
|| self.intro
|
||||||
|| match self.open_windows {
|
|| !matches!(self.open_windows, Windows::None)
|
||||||
Windows::None => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
self.bag = false;
|
self.bag = false;
|
||||||
self.esc_menu = false;
|
self.esc_menu = false;
|
||||||
@ -2470,18 +2467,15 @@ impl Hud {
|
|||||||
// conrod eats tabs. Un-eat a tabstop so tab completion can work
|
// conrod eats tabs. Un-eat a tabstop so tab completion can work
|
||||||
if self.ui.ui.global_input().events().any(|event| {
|
if self.ui.ui.global_input().events().any(|event| {
|
||||||
use conrod_core::{event, input};
|
use conrod_core::{event, input};
|
||||||
match event {
|
matches!(event,
|
||||||
//event::Event::Raw(event::Input::Press(input::Button::Keyboard(input::Key::Tab)))
|
/* event::Event::Raw(event::Input::Press(input::Button::Keyboard(input::Key::Tab))) | */
|
||||||
// => true,
|
|
||||||
event::Event::Ui(event::Ui::Press(
|
event::Event::Ui(event::Ui::Press(
|
||||||
_,
|
_,
|
||||||
event::Press {
|
event::Press {
|
||||||
button: event::Button::Keyboard(input::Key::Tab),
|
button: event::Button::Keyboard(input::Key::Tab),
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
)) => true,
|
)))
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}) {
|
}) {
|
||||||
self.ui
|
self.ui
|
||||||
.ui
|
.ui
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![allow(clippy::option_map_unit_fn)]
|
#![allow(clippy::option_map_unit_fn, incomplete_features)]
|
||||||
#![feature(drain_filter, bool_to_option, or_patterns)]
|
#![feature(array_map, bool_to_option, const_generics, drain_filter, or_patterns)]
|
||||||
#![recursion_limit = "2048"]
|
#![recursion_limit = "2048"]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -161,22 +161,20 @@ impl<'a> GreedyMesh<'a> {
|
|||||||
pub fn push<M: PartialEq, D: 'a, FL, FC, FO, FS, FP>(
|
pub fn push<M: PartialEq, D: 'a, FL, FC, FO, FS, FP>(
|
||||||
&mut self,
|
&mut self,
|
||||||
config: GreedyConfig<D, FL, FC, FO, FS, FP>,
|
config: GreedyConfig<D, FL, FC, FO, FS, FP>,
|
||||||
) -> Aabb<u16>
|
) where
|
||||||
where
|
|
||||||
FL: for<'r> FnMut(&'r mut D, Vec3<i32>) -> f32 + 'a,
|
FL: for<'r> FnMut(&'r mut D, Vec3<i32>) -> f32 + 'a,
|
||||||
FC: for<'r> FnMut(&'r mut D, Vec3<i32>) -> Rgb<u8> + 'a,
|
FC: for<'r> FnMut(&'r mut D, Vec3<i32>) -> Rgb<u8> + 'a,
|
||||||
FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a,
|
FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a,
|
||||||
FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>,
|
FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>,
|
||||||
FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M),
|
FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M),
|
||||||
{
|
{
|
||||||
let (bounds, cont) = greedy_mesh(
|
let cont = greedy_mesh(
|
||||||
&mut self.atlas,
|
&mut self.atlas,
|
||||||
&mut self.col_lights_size,
|
&mut self.col_lights_size,
|
||||||
self.max_size,
|
self.max_size,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
self.suspended.push(cont);
|
self.suspended.push(cont);
|
||||||
bounds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalize the mesh, producing texture color data for the whole model.
|
/// Finalize the mesh, producing texture color data for the whole model.
|
||||||
@ -219,7 +217,7 @@ fn greedy_mesh<'a, M: PartialEq, D: 'a, FL, FC, FO, FS, FP>(
|
|||||||
mut should_draw,
|
mut should_draw,
|
||||||
mut push_quad,
|
mut push_quad,
|
||||||
}: GreedyConfig<D, FL, FC, FO, FS, FP>,
|
}: GreedyConfig<D, FL, FC, FO, FS, FP>,
|
||||||
) -> (Aabb<u16>, Box<SuspendedMesh<'a>>)
|
) -> Box<SuspendedMesh<'a>>
|
||||||
where
|
where
|
||||||
FL: for<'r> FnMut(&'r mut D, Vec3<i32>) -> f32 + 'a,
|
FL: for<'r> FnMut(&'r mut D, Vec3<i32>) -> f32 + 'a,
|
||||||
FC: for<'r> FnMut(&'r mut D, Vec3<i32>) -> Rgb<u8> + 'a,
|
FC: for<'r> FnMut(&'r mut D, Vec3<i32>) -> Rgb<u8> + 'a,
|
||||||
@ -365,30 +363,23 @@ where
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let bounds = Aabb {
|
Box::new(move |col_lights_info| {
|
||||||
min: Vec3::zero(),
|
let mut data = data;
|
||||||
// NOTE: Safe because greedy_size fit in u16.
|
draw_col_lights(
|
||||||
max: greedy_size.map(|e| e as u16),
|
col_lights_info,
|
||||||
};
|
&mut data,
|
||||||
(
|
todo_rects,
|
||||||
bounds,
|
draw_delta,
|
||||||
Box::new(move |col_lights_info| {
|
get_light,
|
||||||
let mut data = data;
|
get_color,
|
||||||
draw_col_lights(
|
get_opacity,
|
||||||
col_lights_info,
|
TerrainVertex::make_col_light,
|
||||||
&mut data,
|
);
|
||||||
todo_rects,
|
})
|
||||||
draw_delta,
|
|
||||||
get_light,
|
|
||||||
get_color,
|
|
||||||
get_opacity,
|
|
||||||
TerrainVertex::make_col_light,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Greedy meshing a single cross-section.
|
/// Greedy meshing a single cross-section.
|
||||||
|
// TODO: See if we can speed a lot of this up using SIMD.
|
||||||
fn greedy_mesh_cross_section<M: PartialEq>(
|
fn greedy_mesh_cross_section<M: PartialEq>(
|
||||||
dims: Vec3<usize>,
|
dims: Vec3<usize>,
|
||||||
// Should we draw a face here (below this vertex)? If so, provide its meta information.
|
// Should we draw a face here (below this vertex)? If so, provide its meta information.
|
||||||
@ -524,9 +515,10 @@ fn add_to_atlas(
|
|||||||
/// We deferred actually recording the colors within the rectangles in order to
|
/// We deferred actually recording the colors within the rectangles in order to
|
||||||
/// generate a texture of minimal size; we now proceed to create and populate
|
/// generate a texture of minimal size; we now proceed to create and populate
|
||||||
/// it.
|
/// it.
|
||||||
///
|
// TODO: Consider using the heavier interface (not the simple one) which seems
|
||||||
/// TODO: Consider using the heavier interface (not the simple one) which seems
|
// to provide builtin support for what we're doing here.
|
||||||
/// to provide builtin support for what we're doing here.
|
//
|
||||||
|
// TODO: See if we can speed this up using SIMD.
|
||||||
fn draw_col_lights<D>(
|
fn draw_col_lights<D>(
|
||||||
(col_lights, cur_size): &mut ColLightInfo,
|
(col_lights, cur_size): &mut ColLightInfo,
|
||||||
data: &mut D,
|
data: &mut D,
|
||||||
@ -610,6 +602,7 @@ fn draw_col_lights<D>(
|
|||||||
|
|
||||||
/// Precondition: when this function is called, atlas_pos should reflect an
|
/// Precondition: when this function is called, atlas_pos should reflect an
|
||||||
/// actual valid position in a texture atlas (meaning it should fit into a u16).
|
/// actual valid position in a texture atlas (meaning it should fit into a u16).
|
||||||
|
// TODO: See if we can speed a lot of this up using SIMD.
|
||||||
fn create_quad_greedy<M>(
|
fn create_quad_greedy<M>(
|
||||||
origin: Vec3<usize>,
|
origin: Vec3<usize>,
|
||||||
dim: Vec2<usize>,
|
dim: Vec2<usize>,
|
||||||
|
@ -7,11 +7,13 @@ use crate::{
|
|||||||
self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline,
|
self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline,
|
||||||
TerrainPipeline,
|
TerrainPipeline,
|
||||||
},
|
},
|
||||||
|
scene::math,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
figure::Cell,
|
figure::Cell,
|
||||||
vol::{BaseVol, ReadVol, SizedVol, Vox},
|
vol::{BaseVol, ReadVol, SizedVol, Vox},
|
||||||
};
|
};
|
||||||
|
use core::ops::Range;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex;
|
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex;
|
||||||
@ -26,15 +28,31 @@ where
|
|||||||
* &'a V: BaseVol<Vox=Cell>, */
|
* &'a V: BaseVol<Vox=Cell>, */
|
||||||
{
|
{
|
||||||
type Pipeline = TerrainPipeline;
|
type Pipeline = TerrainPipeline;
|
||||||
type Result = Aabb<f32>;
|
/// NOTE: The result provides the (roughly) computed bounds for the model,
|
||||||
|
/// and the vertex range meshed for this model; we return this instead
|
||||||
|
/// of the full opaque mesh so we can avoid allocating a separate mesh
|
||||||
|
/// for each bone.
|
||||||
|
///
|
||||||
|
/// Later, we can iterate through the bone array and correctly assign bone
|
||||||
|
/// ids to all vertices in range for each segment.
|
||||||
|
///
|
||||||
|
/// FIXME: A refactor of the figure cache to not just return an array of
|
||||||
|
/// models (thus allowing us to knoe the bone index ahead of time) would
|
||||||
|
/// avoid needing per-bone information at all.
|
||||||
|
type Result = (math::Aabb<f32>, Range<usize>);
|
||||||
type ShadowPipeline = ShadowPipeline;
|
type ShadowPipeline = ShadowPipeline;
|
||||||
type Supplement = (&'b mut GreedyMesh<'a>, Vec3<f32>, Vec3<f32>);
|
type Supplement = (
|
||||||
|
&'b mut GreedyMesh<'a>,
|
||||||
|
&'b mut Mesh<Self::Pipeline>,
|
||||||
|
Vec3<f32>,
|
||||||
|
Vec3<f32>,
|
||||||
|
);
|
||||||
type TranslucentPipeline = FigurePipeline;
|
type TranslucentPipeline = FigurePipeline;
|
||||||
|
|
||||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||||
fn generate_mesh(
|
fn generate_mesh(
|
||||||
self,
|
self,
|
||||||
(greedy, offs, scale): Self::Supplement,
|
(greedy, opaque_mesh, offs, scale): Self::Supplement,
|
||||||
) -> MeshGen<FigurePipeline, &'b mut GreedyMesh<'a>, Self> {
|
) -> MeshGen<FigurePipeline, &'b mut GreedyMesh<'a>, Self> {
|
||||||
let max_size = greedy.max_size();
|
let max_size = greedy.max_size();
|
||||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||||
@ -43,17 +61,21 @@ where
|
|||||||
// coordinate instead of 1 << 16.
|
// coordinate instead of 1 << 16.
|
||||||
assert!(max_size.width.max(max_size.height) < 1 << 15);
|
assert!(max_size.width.max(max_size.height) < 1 << 15);
|
||||||
|
|
||||||
let greedy_size = Vec3::new(
|
let lower_bound = self.lower_bound();
|
||||||
(self.upper_bound().x - self.lower_bound().x + 1) as usize,
|
let upper_bound = self.upper_bound();
|
||||||
(self.upper_bound().y - self.lower_bound().y + 1) as usize,
|
assert!(
|
||||||
(self.upper_bound().z - self.lower_bound().z + 1) as usize,
|
lower_bound.x <= upper_bound.x
|
||||||
|
&& lower_bound.y <= upper_bound.y
|
||||||
|
&& lower_bound.z <= upper_bound.z
|
||||||
);
|
);
|
||||||
|
// NOTE: Figure sizes should be no more than 512 along each axis.
|
||||||
|
let greedy_size = upper_bound - lower_bound + 1;
|
||||||
|
assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
|
||||||
|
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||||
|
// into u16 which is safe to cast to usize.
|
||||||
|
let greedy_size = greedy_size.as_::<usize>();
|
||||||
let greedy_size_cross = greedy_size;
|
let greedy_size_cross = greedy_size;
|
||||||
let draw_delta = Vec3::new(
|
let draw_delta = lower_bound;
|
||||||
self.lower_bound().x,
|
|
||||||
self.lower_bound().y,
|
|
||||||
self.lower_bound().z,
|
|
||||||
);
|
|
||||||
|
|
||||||
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
||||||
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
||||||
@ -79,8 +101,8 @@ where
|
|||||||
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, 0)
|
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut opaque_mesh = Mesh::new();
|
let start = opaque_mesh.vertices().len();
|
||||||
let bounds = greedy.push(GreedyConfig {
|
greedy.push(GreedyConfig {
|
||||||
data: self,
|
data: self,
|
||||||
draw_delta,
|
draw_delta,
|
||||||
greedy_size,
|
greedy_size,
|
||||||
@ -101,14 +123,20 @@ where
|
|||||||
));
|
));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
let bounds = bounds.map(f32::from);
|
let bounds = math::Aabb {
|
||||||
let bounds = Aabb {
|
// NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16.
|
||||||
min: (bounds.min + offs) * scale,
|
min: math::Vec3::from((lower_bound.as_::<f32>() + offs) * scale),
|
||||||
max: (bounds.max + offs) * scale,
|
max: math::Vec3::from((upper_bound.as_::<f32>() + offs) * scale),
|
||||||
}
|
}
|
||||||
.made_valid();
|
.made_valid();
|
||||||
|
let vertex_range = start..opaque_mesh.vertices().len();
|
||||||
|
|
||||||
(opaque_mesh, Mesh::new(), Mesh::new(), bounds)
|
(
|
||||||
|
Mesh::new(),
|
||||||
|
Mesh::new(),
|
||||||
|
Mesh::new(),
|
||||||
|
(bounds, vertex_range),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,13 +150,13 @@ where
|
|||||||
type Pipeline = SpritePipeline;
|
type Pipeline = SpritePipeline;
|
||||||
type Result = ();
|
type Result = ();
|
||||||
type ShadowPipeline = ShadowPipeline;
|
type ShadowPipeline = ShadowPipeline;
|
||||||
type Supplement = (&'b mut GreedyMesh<'a>, bool);
|
type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh<Self::Pipeline>, bool);
|
||||||
type TranslucentPipeline = SpritePipeline;
|
type TranslucentPipeline = SpritePipeline;
|
||||||
|
|
||||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||||
fn generate_mesh(
|
fn generate_mesh(
|
||||||
self,
|
self,
|
||||||
(greedy, vertical_stripes): Self::Supplement,
|
(greedy, opaque_mesh, vertical_stripes): Self::Supplement,
|
||||||
) -> MeshGen<SpritePipeline, &'b mut GreedyMesh<'a>, Self> {
|
) -> MeshGen<SpritePipeline, &'b mut GreedyMesh<'a>, Self> {
|
||||||
let max_size = greedy.max_size();
|
let max_size = greedy.max_size();
|
||||||
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
// NOTE: Required because we steal two bits from the normal in the shadow uint
|
||||||
@ -137,22 +165,25 @@ where
|
|||||||
// coordinate instead of 1 << 16.
|
// coordinate instead of 1 << 16.
|
||||||
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
||||||
|
|
||||||
let greedy_size = Vec3::new(
|
let lower_bound = self.lower_bound();
|
||||||
(self.upper_bound().x - self.lower_bound().x + 1) as usize,
|
let upper_bound = self.upper_bound();
|
||||||
(self.upper_bound().y - self.lower_bound().y + 1) as usize,
|
assert!(
|
||||||
(self.upper_bound().z - self.lower_bound().z + 1) as usize,
|
lower_bound.x <= upper_bound.x
|
||||||
|
&& lower_bound.y <= upper_bound.y
|
||||||
|
&& lower_bound.z <= upper_bound.z
|
||||||
);
|
);
|
||||||
|
let greedy_size = upper_bound - lower_bound + 1;
|
||||||
assert!(
|
assert!(
|
||||||
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
|
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
|
||||||
"Sprite size out of bounds: {:?} ≤ (15, 15, 63)",
|
"Sprite size out of bounds: {:?} ≤ (15, 15, 63)",
|
||||||
greedy_size - 1
|
greedy_size - 1
|
||||||
);
|
);
|
||||||
|
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||||
|
// into u16 which is safe to cast to usize.
|
||||||
|
let greedy_size = greedy_size.as_::<usize>();
|
||||||
|
|
||||||
let greedy_size_cross = greedy_size;
|
let greedy_size_cross = greedy_size;
|
||||||
let draw_delta = Vec3::new(
|
let draw_delta = lower_bound;
|
||||||
self.lower_bound().x,
|
|
||||||
self.lower_bound().y,
|
|
||||||
self.lower_bound().z,
|
|
||||||
);
|
|
||||||
|
|
||||||
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
||||||
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
||||||
@ -177,8 +208,7 @@ where
|
|||||||
let create_opaque =
|
let create_opaque =
|
||||||
|atlas_pos, pos: Vec3<f32>, norm, _meta| SpriteVertex::new(atlas_pos, pos, norm);
|
|atlas_pos, pos: Vec3<f32>, norm, _meta| SpriteVertex::new(atlas_pos, pos, norm);
|
||||||
|
|
||||||
let mut opaque_mesh = Mesh::new();
|
greedy.push(GreedyConfig {
|
||||||
let _bounds = greedy.push(GreedyConfig {
|
|
||||||
data: self,
|
data: self,
|
||||||
draw_delta,
|
draw_delta,
|
||||||
greedy_size,
|
greedy_size,
|
||||||
@ -200,7 +230,7 @@ where
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
(opaque_mesh, Mesh::new(), Mesh::new(), ())
|
(Mesh::new(), Mesh::new(), Mesh::new(), ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,22 +259,25 @@ where
|
|||||||
// coordinate instead of 1 << 16.
|
// coordinate instead of 1 << 16.
|
||||||
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
assert!(max_size.width.max(max_size.height) < 1 << 16);
|
||||||
|
|
||||||
let greedy_size = Vec3::new(
|
let lower_bound = self.lower_bound();
|
||||||
(self.upper_bound().x - self.lower_bound().x + 1) as usize,
|
let upper_bound = self.upper_bound();
|
||||||
(self.upper_bound().y - self.lower_bound().y + 1) as usize,
|
assert!(
|
||||||
(self.upper_bound().z - self.lower_bound().z + 1) as usize,
|
lower_bound.x <= upper_bound.x
|
||||||
|
&& lower_bound.y <= upper_bound.y
|
||||||
|
&& lower_bound.z <= upper_bound.z
|
||||||
);
|
);
|
||||||
|
let greedy_size = upper_bound - lower_bound + 1;
|
||||||
assert!(
|
assert!(
|
||||||
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
|
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
|
||||||
"Sprite size out of bounds: {:?} ≤ (15, 15, 63)",
|
"Particle size out of bounds: {:?} ≤ (15, 15, 63)",
|
||||||
greedy_size - 1
|
greedy_size - 1
|
||||||
);
|
);
|
||||||
|
// NOTE: Cast to usize is safe because of previous check, since all values fit
|
||||||
|
// into u16 which is safe to cast to usize.
|
||||||
|
let greedy_size = greedy_size.as_::<usize>();
|
||||||
|
|
||||||
let greedy_size_cross = greedy_size;
|
let greedy_size_cross = greedy_size;
|
||||||
let draw_delta = Vec3::new(
|
let draw_delta = lower_bound;
|
||||||
self.lower_bound().x,
|
|
||||||
self.lower_bound().y,
|
|
||||||
self.lower_bound().z,
|
|
||||||
);
|
|
||||||
|
|
||||||
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
let get_light = |vol: &mut V, pos: Vec3<i32>| {
|
||||||
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
|
||||||
@ -269,7 +302,7 @@ where
|
|||||||
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
|
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
|
||||||
|
|
||||||
let mut opaque_mesh = Mesh::new();
|
let mut opaque_mesh = Mesh::new();
|
||||||
let _bounds = greedy.push(GreedyConfig {
|
greedy.push(GreedyConfig {
|
||||||
data: self,
|
data: self,
|
||||||
draw_delta,
|
draw_delta,
|
||||||
greedy_size,
|
greedy_size,
|
||||||
@ -304,7 +337,7 @@ fn should_draw_greedy(
|
|||||||
let from = flat_get(pos - delta);
|
let from = flat_get(pos - delta);
|
||||||
let to = flat_get(pos);
|
let to = flat_get(pos);
|
||||||
let from_opaque = !from.is_empty();
|
let from_opaque = !from.is_empty();
|
||||||
if from_opaque == !to.is_empty() {
|
if from_opaque != to.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
// If going from transparent to opaque, backward facing; otherwise, forward
|
// If going from transparent to opaque, backward facing; otherwise, forward
|
||||||
@ -323,7 +356,7 @@ fn should_draw_greedy_ao(
|
|||||||
let from = flat_get(pos - delta);
|
let from = flat_get(pos - delta);
|
||||||
let to = flat_get(pos);
|
let to = flat_get(pos);
|
||||||
let from_opaque = !from.is_empty();
|
let from_opaque = !from.is_empty();
|
||||||
if from_opaque == !to.is_empty() {
|
if from_opaque != to.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let faces_forward = from_opaque;
|
let faces_forward = from_opaque;
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind},
|
||||||
vol::{DefaultVolIterator, ReadVol, RectRasterableVol, Vox},
|
vol::{ReadVol, RectRasterableVol, Vox},
|
||||||
volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d},
|
volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d},
|
||||||
};
|
};
|
||||||
use std::{collections::VecDeque, fmt::Debug};
|
use std::{collections::VecDeque, fmt::Debug};
|
||||||
@ -40,7 +40,7 @@ impl Blendable for BlockKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SUNLIGHT: u8 = 24;
|
const SUNLIGHT: u8 = 24;
|
||||||
const MAX_LIGHT_DIST: i32 = SUNLIGHT as i32;
|
const _MAX_LIGHT_DIST: i32 = SUNLIGHT as i32;
|
||||||
|
|
||||||
fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||||
bounds: Aabb<i32>,
|
bounds: Aabb<i32>,
|
||||||
@ -241,9 +241,10 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
|
|||||||
(range, max_texture_size): Self::Supplement,
|
(range, max_texture_size): Self::Supplement,
|
||||||
) -> MeshGen<TerrainPipeline, FluidPipeline, Self> {
|
) -> MeshGen<TerrainPipeline, FluidPipeline, Self> {
|
||||||
// Find blocks that should glow
|
// Find blocks that should glow
|
||||||
let lit_blocks =
|
// FIXME: Replace with real lit blocks when we actually have blocks that glow.
|
||||||
DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
|
let lit_blocks = core::iter::empty();
|
||||||
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow)));
|
/* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
|
||||||
|
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */
|
||||||
|
|
||||||
// Calculate chunk lighting
|
// Calculate chunk lighting
|
||||||
let mut light = calc_light(range, self, lit_blocks);
|
let mut light = calc_light(range, self, lit_blocks);
|
||||||
@ -327,11 +328,18 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
|
|||||||
|
|
||||||
let max_size =
|
let max_size =
|
||||||
guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y));
|
guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y));
|
||||||
let greedy_size = Vec3::new(
|
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
|
||||||
(range.size().w - 2) as usize,
|
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
|
||||||
(range.size().h - 2) as usize,
|
// + 14). FIXME: Make this function fallible, since the terrain
|
||||||
(z_end - z_start + 1) as usize,
|
// information might be dynamically generated which would make this hard
|
||||||
);
|
// to enforce.
|
||||||
|
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
|
||||||
|
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||||
|
// which always fits into a f32.
|
||||||
|
let max_bounds: Vec3<f32> = greedy_size.as_::<f32>();
|
||||||
|
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
|
||||||
|
// which always fits into a usize.
|
||||||
|
let greedy_size = greedy_size.as_::<usize>();
|
||||||
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
|
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
|
||||||
let draw_delta = Vec3::new(1, 1, z_start);
|
let draw_delta = Vec3::new(1, 1, z_start);
|
||||||
|
|
||||||
@ -353,7 +361,7 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
|
|||||||
let mut greedy = GreedyMesh::new(max_size);
|
let mut greedy = GreedyMesh::new(max_size);
|
||||||
let mut opaque_mesh = Mesh::new();
|
let mut opaque_mesh = Mesh::new();
|
||||||
let mut fluid_mesh = Mesh::new();
|
let mut fluid_mesh = Mesh::new();
|
||||||
let bounds = greedy.push(GreedyConfig {
|
greedy.push(GreedyConfig {
|
||||||
data: (),
|
data: (),
|
||||||
draw_delta,
|
draw_delta,
|
||||||
greedy_size,
|
greedy_size,
|
||||||
@ -388,10 +396,11 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let bounds = bounds.map(f32::from);
|
let min_bounds = mesh_delta;
|
||||||
let bounds = Aabb {
|
let bounds = Aabb {
|
||||||
min: bounds.min + mesh_delta,
|
// NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16.
|
||||||
max: bounds.max + mesh_delta,
|
min: min_bounds,
|
||||||
|
max: max_bounds + min_bounds,
|
||||||
};
|
};
|
||||||
let (col_lights, col_lights_size) = greedy.finalize();
|
let (col_lights, col_lights_size) = greedy.finalize();
|
||||||
|
|
||||||
|
@ -25,12 +25,12 @@ impl<T: Copy + gfx::traits::Pod> Consts<T> {
|
|||||||
vals: &[T],
|
vals: &[T],
|
||||||
offset: usize,
|
offset: usize,
|
||||||
) -> Result<(), RenderError> {
|
) -> Result<(), RenderError> {
|
||||||
if vals.len() > 0 {
|
if vals.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
encoder
|
encoder
|
||||||
.update_buffer(&self.buf, vals, offset)
|
.update_buffer(&self.buf, vals, offset)
|
||||||
.map_err(RenderError::UpdateError)
|
.map_err(RenderError::UpdateError)
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ impl<T: Copy + gfx::traits::Pod> Instances<T> {
|
|||||||
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Result<Self, RenderError> {
|
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Result<Self, RenderError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ibuf: factory
|
ibuf: factory
|
||||||
.create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::TRANSFER_DST)
|
.create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::empty())
|
||||||
.map_err(RenderError::BufferCreationError)?,
|
.map_err(RenderError::BufferCreationError)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::Pipeline;
|
use super::Pipeline;
|
||||||
use std::iter::FromIterator;
|
use core::{iter::FromIterator, ops::Range};
|
||||||
|
|
||||||
/// A `Vec`-based mesh structure used to store mesh data on the CPU.
|
/// A `Vec`-based mesh structure used to store mesh data on the CPU.
|
||||||
pub struct Mesh<P: Pipeline> {
|
pub struct Mesh<P: Pipeline> {
|
||||||
@ -69,6 +69,11 @@ impl<P: Pipeline> Mesh<P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> std::slice::Iter<P::Vertex> { self.verts.iter() }
|
pub fn iter(&self) -> std::slice::Iter<P::Vertex> { self.verts.iter() }
|
||||||
|
|
||||||
|
/// NOTE: Panics if vertex_range is out of bounds of vertices.
|
||||||
|
pub fn iter_mut(&mut self, vertex_range: Range<usize>) -> std::slice::IterMut<P::Vertex> {
|
||||||
|
self.verts[vertex_range].iter_mut()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Pipeline> IntoIterator for Mesh<P> {
|
impl<P: Pipeline> IntoIterator for Mesh<P> {
|
||||||
|
@ -244,7 +244,7 @@ impl core::convert::TryFrom<ShadowMode> for ShadowMapMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ShadowMode {
|
impl ShadowMode {
|
||||||
pub fn is_map(&self) -> bool { if let Self::Map(_) = self { true } else { false } }
|
pub fn is_map(&self) -> bool { matches!(self, Self::Map(_)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render modes
|
/// Render modes
|
||||||
|
@ -22,6 +22,15 @@ impl<P: Pipeline> Model<P> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn vertex_range(&self) -> Range<u32> { self.vertex_range.clone() }
|
pub fn vertex_range(&self) -> Range<u32> { self.vertex_range.clone() }
|
||||||
|
|
||||||
|
/// Create a model with a slice of a portion of this model to send to the
|
||||||
|
/// renderer.
|
||||||
|
pub fn submodel(&self, vertex_range: Range<u32>) -> Model<P> {
|
||||||
|
Model {
|
||||||
|
vbuf: self.vbuf.clone(),
|
||||||
|
vertex_range,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a mesh on the GPU which can be updated dynamically.
|
/// Represents a mesh on the GPU which can be updated dynamically.
|
||||||
@ -40,10 +49,10 @@ impl<P: Pipeline> DynamicModel<P> {
|
|||||||
|
|
||||||
/// Create a model with a slice of a portion of this model to send to the
|
/// Create a model with a slice of a portion of this model to send to the
|
||||||
/// renderer.
|
/// renderer.
|
||||||
pub fn submodel(&self, range: Range<usize>) -> Model<P> {
|
pub fn submodel(&self, vertex_range: Range<u32>) -> Model<P> {
|
||||||
Model {
|
Model {
|
||||||
vbuf: self.vbuf.clone(),
|
vbuf: self.vbuf.clone(),
|
||||||
vertex_range: range.start as u32..range.end as u32,
|
vertex_range,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
use super::{
|
use super::{
|
||||||
super::{
|
super::{Mesh, Model, Pipeline, TerrainPipeline, TgtColorFmt, TgtDepthStencilFmt},
|
||||||
ColLightFmt, Mesh, Model, Pipeline, TerrainPipeline, Texture, TgtColorFmt,
|
|
||||||
TgtDepthStencilFmt,
|
|
||||||
},
|
|
||||||
shadow, Globals, Light, Shadow,
|
shadow, Globals, Light, Shadow,
|
||||||
};
|
};
|
||||||
use crate::mesh::greedy::GreedyMesh;
|
use crate::mesh::greedy::GreedyMesh;
|
||||||
|
use core::ops::Range;
|
||||||
use gfx::{
|
use gfx::{
|
||||||
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
|
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
|
||||||
gfx_pipeline_inner, gfx_vertex_struct_meta, state::ColorMask,
|
gfx_pipeline_inner, gfx_vertex_struct_meta, state::ColorMask,
|
||||||
@ -117,12 +115,9 @@ impl Pipeline for FigurePipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct FigureModel {
|
pub struct FigureModel {
|
||||||
pub bounds: Aabb<f32>,
|
|
||||||
pub opaque: Model<TerrainPipeline>,
|
pub opaque: Model<TerrainPipeline>,
|
||||||
// TODO: Consider using mipmaps instead of storing multiple texture atlases for different LOD
|
/* TODO: Consider using mipmaps instead of storing multiple texture atlases for different
|
||||||
// levels.
|
* LOD levels. */
|
||||||
pub col_lights: Texture<ColLightFmt>,
|
|
||||||
pub allocation: guillotiere::Allocation,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FigureModel {
|
impl FigureModel {
|
||||||
@ -137,7 +132,4 @@ impl FigureModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BoneMeshes = (
|
pub type BoneMeshes = (Mesh<TerrainPipeline>, (anim::vek::Aabb<f32>, Range<usize>));
|
||||||
Mesh</* FigurePipeline */ TerrainPipeline>, /* , Mesh<ShadowPipeline> */
|
|
||||||
Aabb<f32>,
|
|
||||||
);
|
|
||||||
|
@ -7,15 +7,11 @@ use super::{
|
|||||||
};
|
};
|
||||||
use gfx::{
|
use gfx::{
|
||||||
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
|
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
|
||||||
gfx_pipeline_inner, gfx_vertex_struct_meta,
|
gfx_pipeline_inner,
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
gfx_defines! {
|
gfx_defines! {
|
||||||
vertex Vertex {
|
|
||||||
pos_norm: u32 = "v_pos_norm",
|
|
||||||
}
|
|
||||||
|
|
||||||
constant Locals {
|
constant Locals {
|
||||||
shadow_matrices: [[f32; 4]; 4] = "shadowMatrices",
|
shadow_matrices: [[f32; 4]; 4] = "shadowMatrices",
|
||||||
texture_mats: [[f32; 4]; 4] = "texture_mat",
|
texture_mats: [[f32; 4]; 4] = "texture_mat",
|
||||||
@ -55,54 +51,6 @@ gfx_defines! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vertex {
|
|
||||||
pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, meta: bool) -> Self {
|
|
||||||
let norm_bits = if norm.x != 0.0 {
|
|
||||||
if norm.x < 0.0 { 0 } else { 1 }
|
|
||||||
} else if norm.y != 0.0 {
|
|
||||||
if norm.y < 0.0 { 2 } else { 3 }
|
|
||||||
} else if norm.z < 0.0 {
|
|
||||||
4
|
|
||||||
} else {
|
|
||||||
5
|
|
||||||
};
|
|
||||||
|
|
||||||
const EXTRA_NEG_Z: f32 = 32768.0;
|
|
||||||
|
|
||||||
Self {
|
|
||||||
pos_norm: 0
|
|
||||||
| ((pos.x as u32) & 0x003F) << 0
|
|
||||||
| ((pos.y as u32) & 0x003F) << 6
|
|
||||||
| (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
|
|
||||||
| if meta { 1 } else { 0 } << 28
|
|
||||||
| (norm_bits & 0x7) << 29,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_figure(pos: Vec3<f32>, norm: Vec3<f32>, bone_idx: u8) -> Self {
|
|
||||||
let norm_bits = if norm.x.min(norm.y).min(norm.z) < 0.0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
pos_norm: pos
|
|
||||||
.map2(Vec3::new(0, 9, 18), |e, shift| {
|
|
||||||
(((e * 2.0 + 256.0) as u32) & 0x1FF) << shift
|
|
||||||
})
|
|
||||||
.reduce_bitor()
|
|
||||||
| (((bone_idx & 0xF) as u32) << 27)
|
|
||||||
| (norm_bits << 31),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_bone_idx(self, bone_idx: u8) -> Self {
|
|
||||||
Self {
|
|
||||||
pos_norm: (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Locals {
|
impl Locals {
|
||||||
pub fn new(shadow_mat: Mat4<f32>, texture_mat: Mat4<f32>) -> Self {
|
pub fn new(shadow_mat: Mat4<f32>, texture_mat: Mat4<f32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -138,5 +86,5 @@ impl ShadowPipeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Pipeline for ShadowPipeline {
|
impl Pipeline for ShadowPipeline {
|
||||||
type Vertex = Vertex;
|
type Vertex = terrain::Vertex;
|
||||||
}
|
}
|
||||||
|
@ -101,16 +101,13 @@ impl Vertex {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
// pos_norm: 0
|
// pos_norm: ((pos.x as u32) & 0x003F)
|
||||||
// | ((pos.x as u32) & 0x003F) << 0
|
|
||||||
// | ((pos.y as u32) & 0x003F) << 6
|
// | ((pos.y as u32) & 0x003F) << 6
|
||||||
// | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
|
// | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
|
||||||
// | if meta { 1 } else { 0 } << 28
|
// | if meta { 1 } else { 0 } << 28
|
||||||
// | (norm_bits & 0x7) << 29,
|
// | (norm_bits & 0x7) << 29,
|
||||||
pos: pos.into_array(),
|
pos: pos.into_array(),
|
||||||
atlas_pos: 0
|
atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16,
|
||||||
| ((atlas_pos.x as u32) & 0xFFFF) << 0
|
|
||||||
| ((atlas_pos.y as u32) & 0xFFFF) << 16,
|
|
||||||
norm_ao: norm_bits,
|
norm_ao: norm_bits,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,8 +119,7 @@ impl Instance {
|
|||||||
|
|
||||||
let mat_arr = mat.into_col_arrays();
|
let mat_arr = mat.into_col_arrays();
|
||||||
Self {
|
Self {
|
||||||
pos_ori: 0
|
pos_ori: ((pos.x as u32) & 0x003F)
|
||||||
| ((pos.x as u32) & 0x003F) << 0
|
|
||||||
| ((pos.y as u32) & 0x003F) << 6
|
| ((pos.y as u32) & 0x003F) << 6
|
||||||
| (((pos + EXTRA_NEG_Z).z.max(0).min(1 << 16) as u32) & 0xFFFF) << 12
|
| (((pos + EXTRA_NEG_Z).z.max(0).min(1 << 16) as u32) & 0xFFFF) << 12
|
||||||
| (u32::from(ori_bits) & 0x7) << 29,
|
| (u32::from(ori_bits) & 0x7) << 29,
|
||||||
|
@ -61,15 +61,12 @@ impl Vertex {
|
|||||||
5
|
5
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
pos_norm: 0
|
pos_norm: ((pos.x as u32) & 0x003F) << 0
|
||||||
| ((pos.x as u32) & 0x003F) << 0
|
|
||||||
| ((pos.y as u32) & 0x003F) << 6
|
| ((pos.y as u32) & 0x003F) << 6
|
||||||
| (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
|
| (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
|
||||||
| if meta { 1 } else { 0 } << 28
|
| if meta { 1 } else { 0 } << 28
|
||||||
| (norm_bits & 0x7) << 29,
|
| (norm_bits & 0x7) << 29,
|
||||||
atlas_pos: 0
|
atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) << 0 | ((atlas_pos.y as u32) & 0xFFFF) << 16,
|
||||||
| ((atlas_pos.x as u32) & 0xFFFF) << 0
|
|
||||||
| ((atlas_pos.y as u32) & 0xFFFF) << 16,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +91,7 @@ impl Vertex {
|
|||||||
.reduce_bitor()
|
.reduce_bitor()
|
||||||
| (((bone_idx & 0xF) as u32) << 27)
|
| (((bone_idx & 0xF) as u32) << 27)
|
||||||
| (norm_bits << 31),
|
| (norm_bits << 31),
|
||||||
atlas_pos: 0
|
atlas_pos: ((atlas_pos.x as u32) & 0x7FFF) << 2
|
||||||
| ((atlas_pos.x as u32) & 0x7FFF) << 2
|
|
||||||
| ((atlas_pos.y as u32) & 0x7FFF) << 17
|
| ((atlas_pos.y as u32) & 0x7FFF) << 17
|
||||||
| axis_bits & 3,
|
| axis_bits & 3,
|
||||||
}
|
}
|
||||||
@ -109,11 +105,9 @@ impl Vertex {
|
|||||||
[col.r, col.g, col.b, light]
|
[col.r, col.g, col.b, light]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_bone_idx(self, bone_idx: u8) -> Self {
|
/// Set the bone_idx for an existing figure vertex.
|
||||||
Self {
|
pub fn set_bone_idx(&mut self, bone_idx: u8) {
|
||||||
pos_norm: (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27),
|
self.pos_norm = (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27);
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1002,7 +1002,7 @@ impl Renderer {
|
|||||||
pub fn render_figure(
|
pub fn render_figure(
|
||||||
&mut self,
|
&mut self,
|
||||||
model: &figure::FigureModel,
|
model: &figure::FigureModel,
|
||||||
_col_lights: &Texture<ColLightFmt>,
|
col_lights: &Texture<ColLightFmt>,
|
||||||
global: &GlobalModel,
|
global: &GlobalModel,
|
||||||
locals: &Consts<figure::Locals>,
|
locals: &Consts<figure::Locals>,
|
||||||
bones: &Consts<figure::BoneData>,
|
bones: &Consts<figure::BoneData>,
|
||||||
@ -1026,7 +1026,6 @@ impl Renderer {
|
|||||||
(self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
(self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let col_lights = &model.col_lights;
|
|
||||||
let model = &model.opaque;
|
let model = &model.opaque;
|
||||||
|
|
||||||
self.encoder.draw(
|
self.encoder.draw(
|
||||||
@ -1087,10 +1086,7 @@ impl Renderer {
|
|||||||
(self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
(self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let col_lights = &model.col_lights;
|
|
||||||
let model = &model.opaque;
|
let model = &model.opaque;
|
||||||
// let atlas_model = &model.opaque;
|
|
||||||
// let model = &model.shadow;
|
|
||||||
|
|
||||||
self.encoder.draw(
|
self.encoder.draw(
|
||||||
&gfx::Slice {
|
&gfx::Slice {
|
||||||
@ -1103,9 +1099,7 @@ impl Renderer {
|
|||||||
&self.player_shadow_pipeline.pso,
|
&self.player_shadow_pipeline.pso,
|
||||||
&figure::pipe::Data {
|
&figure::pipe::Data {
|
||||||
vbuf: model.vbuf.clone(),
|
vbuf: model.vbuf.clone(),
|
||||||
// abuf: atlas_model.vbuf.clone(),
|
|
||||||
col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()),
|
col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()),
|
||||||
// col_lights: (lod.horizon.srv.clone(), lod.horizon.sampler.clone()),
|
|
||||||
locals: locals.buf.clone(),
|
locals: locals.buf.clone(),
|
||||||
globals: global.globals.buf.clone(),
|
globals: global.globals.buf.clone(),
|
||||||
bones: bones.buf.clone(),
|
bones: bones.buf.clone(),
|
||||||
@ -1127,7 +1121,7 @@ impl Renderer {
|
|||||||
pub fn render_player(
|
pub fn render_player(
|
||||||
&mut self,
|
&mut self,
|
||||||
model: &figure::FigureModel,
|
model: &figure::FigureModel,
|
||||||
_col_lights: &Texture<ColLightFmt>,
|
col_lights: &Texture<ColLightFmt>,
|
||||||
global: &GlobalModel,
|
global: &GlobalModel,
|
||||||
locals: &Consts<figure::Locals>,
|
locals: &Consts<figure::Locals>,
|
||||||
bones: &Consts<figure::BoneData>,
|
bones: &Consts<figure::BoneData>,
|
||||||
@ -1151,7 +1145,6 @@ impl Renderer {
|
|||||||
(self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
(self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let col_lights = &model.col_lights;
|
|
||||||
let model = &model.opaque;
|
let model = &model.opaque;
|
||||||
|
|
||||||
self.encoder.draw(
|
self.encoder.draw(
|
||||||
@ -1476,6 +1469,10 @@ impl Renderer {
|
|||||||
ibuf: instances.ibuf.clone(),
|
ibuf: instances.ibuf.clone(),
|
||||||
col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()),
|
col_lights: (col_lights.srv.clone(), col_lights.sampler.clone()),
|
||||||
terrain_locals: terrain_locals.buf.clone(),
|
terrain_locals: terrain_locals.buf.clone(),
|
||||||
|
// NOTE: It would be nice if this wasn't needed and we could use a constant buffer
|
||||||
|
// offset into the sprite data. Hopefully, when we switch to wgpu we can do this,
|
||||||
|
// as it offers the exact API we want (the equivalent can be done in OpenGL using
|
||||||
|
// glBindBufferOffset).
|
||||||
locals: locals.buf.clone(),
|
locals: locals.buf.clone(),
|
||||||
globals: global.globals.buf.clone(),
|
globals: global.globals.buf.clone(),
|
||||||
lights: global.lights.buf.clone(),
|
lights: global.lights.buf.clone(),
|
||||||
@ -1581,7 +1578,7 @@ impl Renderer {
|
|||||||
/// Queue the rendering of the provided UI element in the upcoming frame.
|
/// Queue the rendering of the provided UI element in the upcoming frame.
|
||||||
pub fn render_ui_element<F: gfx::format::Formatted<View = [f32; 4]>>(
|
pub fn render_ui_element<F: gfx::format::Formatted<View = [f32; 4]>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
model: &Model<ui::UiPipeline>,
|
model: Model<ui::UiPipeline>,
|
||||||
tex: &Texture<F>,
|
tex: &Texture<F>,
|
||||||
scissor: Aabr<u16>,
|
scissor: Aabr<u16>,
|
||||||
globals: &Consts<Globals>,
|
globals: &Consts<Globals>,
|
||||||
@ -1594,15 +1591,15 @@ impl Renderer {
|
|||||||
let Aabr { min, max } = scissor;
|
let Aabr { min, max } = scissor;
|
||||||
self.encoder.draw(
|
self.encoder.draw(
|
||||||
&gfx::Slice {
|
&gfx::Slice {
|
||||||
start: model.vertex_range().start,
|
start: model.vertex_range.start,
|
||||||
end: model.vertex_range().end,
|
end: model.vertex_range.end,
|
||||||
base_vertex: 0,
|
base_vertex: 0,
|
||||||
instances: None,
|
instances: None,
|
||||||
buffer: gfx::IndexBuffer::Auto,
|
buffer: gfx::IndexBuffer::Auto,
|
||||||
},
|
},
|
||||||
&self.ui_pipeline.pso,
|
&self.ui_pipeline.pso,
|
||||||
&ui::pipe::Data {
|
&ui::pipe::Data {
|
||||||
vbuf: model.vbuf.clone(),
|
vbuf: model.vbuf,
|
||||||
scissor: gfx::Rect {
|
scissor: gfx::Rect {
|
||||||
x: min.x,
|
x: min.x,
|
||||||
y: min.y,
|
y: min.y,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::load::*;
|
use super::{load::*, FigureModelEntry};
|
||||||
use crate::{
|
use crate::{
|
||||||
mesh::{greedy::GreedyMesh, Meshable},
|
mesh::{greedy::GreedyMesh, Meshable},
|
||||||
render::{BoneMeshes, FigureModel, FigurePipeline, Mesh, Renderer},
|
render::{BoneMeshes, FigureModel, FigurePipeline, Mesh, Renderer, TerrainPipeline},
|
||||||
scene::camera::CameraMode,
|
scene::camera::CameraMode,
|
||||||
};
|
};
|
||||||
use anim::Skeleton;
|
use anim::Skeleton;
|
||||||
@ -22,7 +22,7 @@ use core::convert::TryInto;
|
|||||||
use hashbrown::{hash_map::Entry, HashMap};
|
use hashbrown::{hash_map::Entry, HashMap};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub type FigureModelEntry = [FigureModel; 3];
|
pub type FigureModelEntryLod = FigureModelEntry<3>;
|
||||||
|
|
||||||
#[derive(Eq, Hash, PartialEq)]
|
#[derive(Eq, Hash, PartialEq)]
|
||||||
struct FigureKey {
|
struct FigureKey {
|
||||||
@ -198,7 +198,7 @@ pub struct FigureModelCache<Skel = anim::character::CharacterSkeleton>
|
|||||||
where
|
where
|
||||||
Skel: Skeleton,
|
Skel: Skeleton,
|
||||||
{
|
{
|
||||||
models: HashMap<FigureKey, ((FigureModelEntry, Skel::Attr), u64)>,
|
models: HashMap<FigureKey, ((FigureModelEntryLod, Skel::Attr), u64)>,
|
||||||
manifest_indicator: ReloadIndicator,
|
manifest_indicator: ReloadIndicator,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -980,7 +980,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
|
|||||||
tick: u64,
|
tick: u64,
|
||||||
camera_mode: CameraMode,
|
camera_mode: CameraMode,
|
||||||
character_state: Option<&CharacterState>,
|
character_state: Option<&CharacterState>,
|
||||||
) -> &(FigureModelEntry, Skel::Attr)
|
) -> &(FigureModelEntryLod, Skel::Attr)
|
||||||
where
|
where
|
||||||
for<'a> &'a common::comp::Body: std::convert::TryInto<Skel::Attr>,
|
for<'a> &'a common::comp::Body: std::convert::TryInto<Skel::Attr>,
|
||||||
Skel::Attr: Default,
|
Skel::Attr: Default,
|
||||||
@ -1011,78 +1011,137 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
|
|||||||
.unwrap_or_else(<Skel::Attr as Default>::default);
|
.unwrap_or_else(<Skel::Attr as Default>::default);
|
||||||
|
|
||||||
let manifest_indicator = &mut self.manifest_indicator;
|
let manifest_indicator = &mut self.manifest_indicator;
|
||||||
let mut make_model =
|
let mut greedy = FigureModel::make_greedy();
|
||||||
|generate_mesh: for<'a> fn(&mut GreedyMesh<'a>, _, _) -> _| {
|
let mut opaque = Mesh::<TerrainPipeline>::new();
|
||||||
let mut greedy = FigureModel::make_greedy();
|
// Choose the most conservative bounds for any LOD model.
|
||||||
let mut opaque = Mesh::new();
|
let mut figure_bounds = anim::vek::Aabb {
|
||||||
let mut figure_bounds = Aabb {
|
min: anim::vek::Vec3::zero(),
|
||||||
min: Vec3::zero(),
|
max: anim::vek::Vec3::zero(),
|
||||||
max: Vec3::zero(),
|
};
|
||||||
};
|
// Meshes all bone models for this figure using the given mesh generation
|
||||||
|
// function, attaching it to the current greedy mesher and opaque vertex
|
||||||
|
// list. Returns the vertex bounds of the meshed model within the opaque
|
||||||
|
// mesh.
|
||||||
|
let mut make_model = |generate_mesh: for<'a, 'b> fn(
|
||||||
|
&mut GreedyMesh<'a>,
|
||||||
|
&'b mut _,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
)
|
||||||
|
-> _| {
|
||||||
|
let vertex_start = opaque.vertices().len();
|
||||||
|
let meshes =
|
||||||
Self::bone_meshes(key, manifest_indicator, |segment, offset| {
|
Self::bone_meshes(key, manifest_indicator, |segment, offset| {
|
||||||
generate_mesh(&mut greedy, segment, offset)
|
generate_mesh(&mut greedy, &mut opaque, segment, offset)
|
||||||
})
|
});
|
||||||
|
meshes
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
|
// NOTE: Cast to u8 is safe because i <= 16.
|
||||||
.for_each(|(i, (opaque_mesh, bounds))| {
|
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i as u8, bm.clone())))
|
||||||
|
.for_each(|(i, (_opaque_mesh, (bounds, vertex_range)))| {
|
||||||
|
// Update the bone index for all vertices that belong ot this
|
||||||
|
// model.
|
||||||
opaque
|
opaque
|
||||||
.push_mesh_map(opaque_mesh, |vert| vert.with_bone_idx(i as u8));
|
.iter_mut(vertex_range)
|
||||||
figure_bounds.expand_to_contain(*bounds);
|
.for_each(|vert| {
|
||||||
|
vert.set_bone_idx(i);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the figure bounds to the largest granularity seen so far
|
||||||
|
// (NOTE: this is more than a little imeprfect).
|
||||||
|
//
|
||||||
|
// FIXME: Maybe use the default bone position in the idle animation
|
||||||
|
// to figure this out instead?
|
||||||
|
figure_bounds.expand_to_contain(bounds);
|
||||||
});
|
});
|
||||||
col_lights
|
// NOTE: vertex_start and vertex_end *should* fit in a u32, by the
|
||||||
.create_figure(renderer, greedy, (opaque, figure_bounds))
|
// following logic:
|
||||||
.unwrap()
|
//
|
||||||
};
|
// Our new figure maximum is constrained to at most 2^8 × 2^8 × 2^8.
|
||||||
|
// This uses at most 24 bits to store every vertex exactly once.
|
||||||
|
// Greedy meshing can store each vertex in up to 3 quads, we have 3
|
||||||
|
// greedy models, and we store 1.5x the vertex count, so the maximum
|
||||||
|
// total space a model can take up is 3 * 3 * 1.5 * 2^24; rounding
|
||||||
|
// up to 4 * 4 * 2^24 gets us to 2^28, which clearly still fits in a
|
||||||
|
// u32.
|
||||||
|
//
|
||||||
|
// (We could also, though we prefer not to, reason backwards from the
|
||||||
|
// maximum figure texture size of 2^15 × 2^15, also fits in a u32; we
|
||||||
|
// can also see that, since we can have at most one texture entry per
|
||||||
|
// vertex, any texture atlas of size 2^14 × 2^14 or higher should be
|
||||||
|
// able to store data for any figure. So the only reason we would fail
|
||||||
|
// here would be if the user's computer could not store a texture large
|
||||||
|
// enough to fit all the LOD models for the figure, not for fundamental
|
||||||
|
// reasonS related to fitting in a u32).
|
||||||
|
//
|
||||||
|
// Therefore, these casts are safe.
|
||||||
|
vertex_start as u32..opaque.vertices().len() as u32
|
||||||
|
};
|
||||||
|
|
||||||
fn generate_mesh<'a>(
|
fn generate_mesh<'a>(
|
||||||
greedy: &mut GreedyMesh<'a>,
|
greedy: &mut GreedyMesh<'a>,
|
||||||
|
opaque_mesh: &mut Mesh<TerrainPipeline>,
|
||||||
segment: Segment,
|
segment: Segment,
|
||||||
offset: Vec3<f32>,
|
offset: Vec3<f32>,
|
||||||
) -> BoneMeshes {
|
) -> BoneMeshes {
|
||||||
let (opaque, _, _, bounds) =
|
let (opaque, _, _, bounds) =
|
||||||
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
|
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
|
||||||
segment,
|
segment,
|
||||||
(greedy, offset, Vec3::one()),
|
(greedy, opaque_mesh, offset, Vec3::one()),
|
||||||
);
|
);
|
||||||
(opaque, bounds)
|
(opaque, bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_mesh_lod_mid<'a>(
|
fn generate_mesh_lod_mid<'a>(
|
||||||
greedy: &mut GreedyMesh<'a>,
|
greedy: &mut GreedyMesh<'a>,
|
||||||
|
opaque_mesh: &mut Mesh<TerrainPipeline>,
|
||||||
segment: Segment,
|
segment: Segment,
|
||||||
offset: Vec3<f32>,
|
offset: Vec3<f32>,
|
||||||
) -> BoneMeshes {
|
) -> BoneMeshes {
|
||||||
let lod_scale = Vec3::broadcast(0.6);
|
let lod_scale = 0.6;
|
||||||
let (opaque, _, _, bounds) =
|
let (opaque, _, _, bounds) =
|
||||||
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
|
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
|
||||||
segment.scaled_by(lod_scale),
|
segment.scaled_by(Vec3::broadcast(lod_scale)),
|
||||||
(greedy, offset * lod_scale, Vec3::one() / lod_scale),
|
(
|
||||||
|
greedy,
|
||||||
|
opaque_mesh,
|
||||||
|
offset * lod_scale,
|
||||||
|
Vec3::one() / lod_scale,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
(opaque, bounds)
|
(opaque, bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_mesh_lod_low<'a>(
|
fn generate_mesh_lod_low<'a>(
|
||||||
greedy: &mut GreedyMesh<'a>,
|
greedy: &mut GreedyMesh<'a>,
|
||||||
|
opaque_mesh: &mut Mesh<TerrainPipeline>,
|
||||||
segment: Segment,
|
segment: Segment,
|
||||||
offset: Vec3<f32>,
|
offset: Vec3<f32>,
|
||||||
) -> BoneMeshes {
|
) -> BoneMeshes {
|
||||||
let lod_scale = Vec3::broadcast(0.3);
|
let lod_scale = 0.3;
|
||||||
let segment = segment.scaled_by(lod_scale);
|
|
||||||
let (opaque, _, _, bounds) =
|
let (opaque, _, _, bounds) =
|
||||||
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
|
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
|
||||||
segment,
|
segment.scaled_by(Vec3::broadcast(lod_scale)),
|
||||||
(greedy, offset * lod_scale, Vec3::one() / lod_scale),
|
(
|
||||||
|
greedy,
|
||||||
|
opaque_mesh,
|
||||||
|
offset * lod_scale,
|
||||||
|
Vec3::one() / lod_scale,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
(opaque, bounds)
|
(opaque, bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let models = [
|
||||||
|
make_model(generate_mesh),
|
||||||
|
make_model(generate_mesh_lod_mid),
|
||||||
|
make_model(generate_mesh_lod_low),
|
||||||
|
];
|
||||||
(
|
(
|
||||||
[
|
col_lights
|
||||||
make_model(generate_mesh),
|
.create_figure(renderer, greedy, (opaque, figure_bounds), models)
|
||||||
make_model(generate_mesh_lod_mid),
|
.expect("Failed to upload figure data to the GPU!"),
|
||||||
make_model(generate_mesh_lod_low),
|
|
||||||
],
|
|
||||||
skeleton_attr,
|
skeleton_attr,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -1100,14 +1159,12 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
|
|||||||
}
|
}
|
||||||
// TODO: Don't hard-code this.
|
// TODO: Don't hard-code this.
|
||||||
if tick % 60 == 0 {
|
if tick % 60 == 0 {
|
||||||
self.models.retain(|_, ((models, _), last_used)| {
|
self.models.retain(|_, ((model_entry, _), last_used)| {
|
||||||
// Wait about a minute at 60 fps before invalidating old models.
|
// Wait about a minute at 60 fps before invalidating old models.
|
||||||
let delta = 60 * 60;
|
let delta = 60 * 60;
|
||||||
let alive = *last_used + delta > tick;
|
let alive = *last_used + delta > tick;
|
||||||
if !alive {
|
if !alive {
|
||||||
models.iter().for_each(|model| {
|
col_lights.atlas.deallocate(model_entry.allocation.id);
|
||||||
col_lights.atlas.deallocate(model.allocation.id);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
alive
|
alive
|
||||||
});
|
});
|
||||||
|
@ -760,7 +760,7 @@ impl HumMainWeaponSpec {
|
|||||||
let tool_kind = if let Some(kind) = tool_kind {
|
let tool_kind = if let Some(kind) = tool_kind {
|
||||||
kind
|
kind
|
||||||
} else {
|
} else {
|
||||||
return (Mesh::new(), Aabb::default());
|
return (Mesh::new(), (anim::vek::Aabb::default(), 0..0));
|
||||||
};
|
};
|
||||||
|
|
||||||
let spec = match self.0.get(tool_kind) {
|
let spec = match self.0.get(tool_kind) {
|
||||||
|
@ -8,8 +8,8 @@ use crate::{
|
|||||||
ecs::comp::Interpolated,
|
ecs::comp::Interpolated,
|
||||||
mesh::greedy::GreedyMesh,
|
mesh::greedy::GreedyMesh,
|
||||||
render::{
|
render::{
|
||||||
BoneMeshes, ColLightFmt, Consts, FigureBoneData, FigureLocals, FigureModel, GlobalModel,
|
ColLightFmt, Consts, FigureBoneData, FigureLocals, FigureModel, GlobalModel, Mesh,
|
||||||
RenderError, Renderer, ShadowPipeline, Texture,
|
RenderError, Renderer, ShadowPipeline, TerrainPipeline, Texture,
|
||||||
},
|
},
|
||||||
scene::{
|
scene::{
|
||||||
camera::{Camera, CameraMode, Dependents},
|
camera::{Camera, CameraMode, Dependents},
|
||||||
@ -36,8 +36,9 @@ use common::{
|
|||||||
};
|
};
|
||||||
use core::{
|
use core::{
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
|
convert::TryFrom,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut, Range},
|
||||||
};
|
};
|
||||||
use guillotiere::AtlasAllocator;
|
use guillotiere::AtlasAllocator;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@ -51,6 +52,33 @@ const MOVING_THRESHOLD_SQR: f32 = MOVING_THRESHOLD * MOVING_THRESHOLD;
|
|||||||
/// camera data, fiigure LOD render distance.
|
/// camera data, fiigure LOD render distance.
|
||||||
pub type CameraData<'a> = (&'a Camera, f32);
|
pub type CameraData<'a> = (&'a Camera, f32);
|
||||||
|
|
||||||
|
/// Enough data to render a figure model.
|
||||||
|
pub type FigureModelRef<'a> = (
|
||||||
|
&'a Consts<FigureLocals>,
|
||||||
|
&'a Consts<FigureBoneData>,
|
||||||
|
&'a FigureModel,
|
||||||
|
&'a Texture<ColLightFmt>,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// An entry holding enough information to draw or destroy a figure in a
|
||||||
|
/// particular cache.
|
||||||
|
pub struct FigureModelEntry<const N: usize> {
|
||||||
|
/// The estimated bounds of this figure, in voxels. This may not be very
|
||||||
|
/// useful yet.
|
||||||
|
_bounds: math::Aabb<f32>,
|
||||||
|
/// Hypothetical texture atlas allocation data for the current figure.
|
||||||
|
/// Will be useful if we decide to use a packed texture atlas for figures
|
||||||
|
/// like we do for terrain.
|
||||||
|
allocation: guillotiere::Allocation,
|
||||||
|
/// Texture used to store color/light information for this figure entry.
|
||||||
|
/* TODO: Consider using mipmaps instead of storing multiple texture atlases for different
|
||||||
|
* LOD levels. */
|
||||||
|
col_lights: Texture<ColLightFmt>,
|
||||||
|
/// Models stored in this figure entry; there may be several for one figure,
|
||||||
|
/// because of LOD models.
|
||||||
|
pub models: [FigureModel; N],
|
||||||
|
}
|
||||||
|
|
||||||
struct FigureMgrStates {
|
struct FigureMgrStates {
|
||||||
character_states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
|
character_states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
|
||||||
quadruped_small_states: HashMap<EcsEntity, FigureState<QuadrupedSmallSkeleton>>,
|
quadruped_small_states: HashMap<EcsEntity, FigureState<QuadrupedSmallSkeleton>>,
|
||||||
@ -1018,7 +1046,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1119,7 +1147,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1220,7 +1248,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1319,7 +1347,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1415,7 +1443,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1500,7 +1528,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1581,7 +1609,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1663,7 +1691,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1748,7 +1776,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1833,7 +1861,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -1929,7 +1957,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -2011,7 +2039,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
in_frustum,
|
in_frustum,
|
||||||
is_player,
|
is_player,
|
||||||
@ -2043,7 +2071,7 @@ impl FigureMgr {
|
|||||||
col,
|
col,
|
||||||
dt,
|
dt,
|
||||||
state_animation_rate,
|
state_animation_rate,
|
||||||
&model[0],
|
&model,
|
||||||
lpindex,
|
lpindex,
|
||||||
true,
|
true,
|
||||||
is_player,
|
is_player,
|
||||||
@ -2159,14 +2187,7 @@ impl FigureMgr {
|
|||||||
figure_lod_render_distance,
|
figure_lod_render_distance,
|
||||||
|state| state.visible(),
|
|state| state.visible(),
|
||||||
) {
|
) {
|
||||||
renderer.render_figure(
|
renderer.render_figure(model, &col_lights, global, locals, bone_consts, lod);
|
||||||
model,
|
|
||||||
&col_lights.col_lights,
|
|
||||||
global,
|
|
||||||
locals,
|
|
||||||
bone_consts,
|
|
||||||
lod,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2215,17 +2236,10 @@ impl FigureMgr {
|
|||||||
figure_lod_render_distance,
|
figure_lod_render_distance,
|
||||||
|state| state.visible(),
|
|state| state.visible(),
|
||||||
) {
|
) {
|
||||||
renderer.render_player(
|
renderer.render_player(model, &col_lights, global, locals, bone_consts, lod);
|
||||||
model,
|
|
||||||
&col_lights.col_lights,
|
|
||||||
global,
|
|
||||||
locals,
|
|
||||||
bone_consts,
|
|
||||||
lod,
|
|
||||||
);
|
|
||||||
renderer.render_player_shadow(
|
renderer.render_player_shadow(
|
||||||
model,
|
model,
|
||||||
&col_lights.col_lights,
|
&col_lights,
|
||||||
global,
|
global,
|
||||||
bone_consts,
|
bone_consts,
|
||||||
lod,
|
lod,
|
||||||
@ -2246,16 +2260,10 @@ impl FigureMgr {
|
|||||||
body: &Body,
|
body: &Body,
|
||||||
loadout: Option<&Loadout>,
|
loadout: Option<&Loadout>,
|
||||||
is_player: bool,
|
is_player: bool,
|
||||||
// is_shadow: bool,
|
|
||||||
pos: vek::Vec3<f32>,
|
pos: vek::Vec3<f32>,
|
||||||
figure_lod_render_distance: f32,
|
figure_lod_render_distance: f32,
|
||||||
filter_state: impl Fn(&FigureStateMeta) -> bool,
|
filter_state: impl Fn(&FigureStateMeta) -> bool,
|
||||||
) -> Option<(
|
) -> Option<FigureModelRef> {
|
||||||
&Consts<FigureLocals>,
|
|
||||||
&Consts<FigureBoneData>,
|
|
||||||
&FigureModel,
|
|
||||||
&FigureColLights,
|
|
||||||
)> {
|
|
||||||
let player_camera_mode = if is_player {
|
let player_camera_mode = if is_player {
|
||||||
camera.get_mode()
|
camera.get_mode()
|
||||||
} else {
|
} else {
|
||||||
@ -2297,7 +2305,7 @@ impl FigureMgr {
|
|||||||
},
|
},
|
||||||
} = self;
|
} = self;
|
||||||
let col_lights = &mut *col_lights_;
|
let col_lights = &mut *col_lights_;
|
||||||
if let Some((locals, bone_consts, model)) = match body {
|
if let Some((locals, bone_consts, model_entry)) = match body {
|
||||||
Body::Humanoid(_) => character_states
|
Body::Humanoid(_) => character_states
|
||||||
.get(&entity)
|
.get(&entity)
|
||||||
.filter(|state| filter_state(&*state))
|
.filter(|state| filter_state(&*state))
|
||||||
@ -2563,14 +2571,14 @@ impl FigureMgr {
|
|||||||
let figure_mid_detail_distance = figure_lod_render_distance * 0.5;
|
let figure_mid_detail_distance = figure_lod_render_distance * 0.5;
|
||||||
|
|
||||||
let model = if pos.distance_squared(cam_pos) > figure_low_detail_distance.powf(2.0) {
|
let model = if pos.distance_squared(cam_pos) > figure_low_detail_distance.powf(2.0) {
|
||||||
&model[2]
|
&model_entry.models[2]
|
||||||
} else if pos.distance_squared(cam_pos) > figure_mid_detail_distance.powf(2.0) {
|
} else if pos.distance_squared(cam_pos) > figure_mid_detail_distance.powf(2.0) {
|
||||||
&model[1]
|
&model_entry.models[1]
|
||||||
} else {
|
} else {
|
||||||
&model[0]
|
&model_entry.models[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((locals, bone_consts, model, &*col_lights_))
|
Some((locals, bone_consts, model, col_lights_.texture(model_entry)))
|
||||||
} else {
|
} else {
|
||||||
// trace!("Body has no saved figure");
|
// trace!("Body has no saved figure");
|
||||||
None
|
None
|
||||||
@ -2584,24 +2592,39 @@ impl FigureMgr {
|
|||||||
|
|
||||||
pub struct FigureColLights {
|
pub struct FigureColLights {
|
||||||
atlas: AtlasAllocator,
|
atlas: AtlasAllocator,
|
||||||
col_lights: Texture<ColLightFmt>,
|
// col_lights: Texture<ColLightFmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FigureColLights {
|
impl FigureColLights {
|
||||||
pub fn new(renderer: &mut Renderer) -> Self {
|
pub fn new(renderer: &mut Renderer) -> Self {
|
||||||
let (atlas, col_lights) =
|
let atlas = Self::make_atlas(renderer).expect("Failed to create texture atlas for figures");
|
||||||
Self::make_atlas(renderer).expect("Failed to create texture atlas for figures");
|
Self {
|
||||||
Self { atlas, col_lights }
|
atlas, /* col_lights, */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture(&self) -> &Texture<ColLightFmt> { &self.col_lights }
|
/// Find the correct texture for this model entry.
|
||||||
|
pub fn texture<'a, const N: usize>(
|
||||||
|
&'a self,
|
||||||
|
model: &'a FigureModelEntry<N>,
|
||||||
|
) -> &'a Texture<ColLightFmt> {
|
||||||
|
/* &self.col_lights */
|
||||||
|
&model.col_lights
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_figure<'a>(
|
/// NOTE: Panics if the opaque model's length does not fit in a u32.
|
||||||
|
/// This is parto f the function contract.
|
||||||
|
///
|
||||||
|
/// NOTE: Panics if the vertex range bounds are not in range of the opaque
|
||||||
|
/// model stored in the BoneMeshes parameter. This is part of the
|
||||||
|
/// function contract.
|
||||||
|
pub fn create_figure<'a, const N: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
greedy: GreedyMesh<'a>,
|
greedy: GreedyMesh<'a>,
|
||||||
(opaque, bounds): BoneMeshes,
|
(opaque, bounds): (Mesh<TerrainPipeline>, math::Aabb<f32>),
|
||||||
) -> Result<FigureModel, RenderError> {
|
vertex_range: [Range<u32>; N],
|
||||||
|
) -> Result<FigureModelEntry<N>, RenderError> {
|
||||||
let (tex, tex_size) = greedy.finalize();
|
let (tex, tex_size) = greedy.finalize();
|
||||||
let atlas = &mut self.atlas;
|
let atlas = &mut self.atlas;
|
||||||
let allocation = atlas
|
let allocation = atlas
|
||||||
@ -2609,21 +2632,32 @@ impl FigureColLights {
|
|||||||
i32::from(tex_size.x),
|
i32::from(tex_size.x),
|
||||||
i32::from(tex_size.y),
|
i32::from(tex_size.y),
|
||||||
))
|
))
|
||||||
.expect("Not yet implemented: allocate new atlas on allocation faillure.");
|
.expect("Not yet implemented: allocate new atlas on allocation failure.");
|
||||||
let col_lights = ShadowPipeline::create_col_lights(renderer, (tex, tex_size))?;
|
let col_lights = ShadowPipeline::create_col_lights(renderer, (tex, tex_size))?;
|
||||||
|
let model_len = u32::try_from(opaque.vertices().len())
|
||||||
|
.expect("The model size for this figure does not fit in a u32!");
|
||||||
|
let model = renderer.create_model(&opaque)?;
|
||||||
|
|
||||||
Ok(FigureModel {
|
Ok(FigureModelEntry {
|
||||||
bounds,
|
_bounds: bounds,
|
||||||
opaque: renderer.create_model(&opaque)?,
|
models: vertex_range.map(|range| {
|
||||||
// shadow: renderer.create_model(&shadow)?,
|
assert!(
|
||||||
|
range.start <= range.end && range.end <= model_len,
|
||||||
|
"The provided vertex range for figure mesh {:?} does not fit in the model, \
|
||||||
|
which is of size {:?}!",
|
||||||
|
range,
|
||||||
|
model_len
|
||||||
|
);
|
||||||
|
FigureModel {
|
||||||
|
opaque: model.submodel(range),
|
||||||
|
}
|
||||||
|
}),
|
||||||
col_lights,
|
col_lights,
|
||||||
allocation,
|
allocation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_atlas(
|
fn make_atlas(renderer: &mut Renderer) -> Result<AtlasAllocator, RenderError> {
|
||||||
renderer: &mut Renderer,
|
|
||||||
) -> Result<(AtlasAllocator, Texture<ColLightFmt>), RenderError> {
|
|
||||||
let max_texture_size = renderer.max_texture_size();
|
let max_texture_size = renderer.max_texture_size();
|
||||||
let atlas_size =
|
let atlas_size =
|
||||||
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
||||||
@ -2633,7 +2667,11 @@ impl FigureColLights {
|
|||||||
large_size_threshold: 256,
|
large_size_threshold: 256,
|
||||||
..guillotiere::AllocatorOptions::default()
|
..guillotiere::AllocatorOptions::default()
|
||||||
});
|
});
|
||||||
let texture = renderer.create_texture_raw(
|
// TODO: Consider using a single texture atlas to store all figures, much like
|
||||||
|
// we do for terrain chunks. We previoosly avoided this due to
|
||||||
|
// perceived performance degradation for the figure use case, but with a
|
||||||
|
// smaller atlas size this may be less likely.
|
||||||
|
/* let texture = renderer.create_texture_raw(
|
||||||
gfx::texture::Kind::D2(
|
gfx::texture::Kind::D2(
|
||||||
max_texture_size,
|
max_texture_size,
|
||||||
max_texture_size,
|
max_texture_size,
|
||||||
@ -2649,7 +2687,8 @@ impl FigureColLights {
|
|||||||
gfx::texture::WrapMode::Clamp,
|
gfx::texture::WrapMode::Clamp,
|
||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
Ok((atlas, texture))
|
Ok((atlas, texture)) */
|
||||||
|
Ok(atlas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2714,7 +2753,7 @@ impl<S: Skeleton> FigureState<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
|
#[allow(clippy::too_many_arguments)] // TODO: Pending review in #587
|
||||||
pub fn update(
|
pub fn update<const N: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
pos: anim::vek::Vec3<f32>,
|
pos: anim::vek::Vec3<f32>,
|
||||||
@ -2723,7 +2762,7 @@ impl<S: Skeleton> FigureState<S> {
|
|||||||
col: vek::Rgba<f32>,
|
col: vek::Rgba<f32>,
|
||||||
dt: f32,
|
dt: f32,
|
||||||
state_animation_rate: f32,
|
state_animation_rate: f32,
|
||||||
model: &FigureModel,
|
model: &FigureModelEntry<N>,
|
||||||
_lpindex: u8,
|
_lpindex: u8,
|
||||||
_visible: bool,
|
_visible: bool,
|
||||||
is_player: bool,
|
is_player: bool,
|
||||||
@ -2736,8 +2775,8 @@ impl<S: Skeleton> FigureState<S> {
|
|||||||
// largest dimension (if we were exact, it should just be half the largest
|
// largest dimension (if we were exact, it should just be half the largest
|
||||||
// dimension, but we're not, so we double it and use size() instead of
|
// dimension, but we're not, so we double it and use size() instead of
|
||||||
// half_size()).
|
// half_size()).
|
||||||
let radius = model.bounds.half_size().reduce_partial_max();
|
/* let radius = vek::Extent3::<f32>::from(model.bounds.half_size()).reduce_partial_max();
|
||||||
let _bounds = BoundingSphere::new(pos.into_array(), scale * 0.8 * radius);
|
let _bounds = BoundingSphere::new(pos.into_array(), scale * 0.8 * radius); */
|
||||||
|
|
||||||
self.last_ori = vek::Lerp::lerp(self.last_ori, ori, 15.0 * dt);
|
self.last_ori = vek::Lerp::lerp(self.last_ori, ori, 15.0 * dt);
|
||||||
|
|
||||||
|
@ -82,10 +82,10 @@ fn create_lod_terrain_mesh(detail: u32) -> Mesh<LodTerrainPipeline> {
|
|||||||
let transform = |x| (2.0 * x as f32) / detail as f32 - 1.0;
|
let transform = |x| (2.0 * x as f32) / detail as f32 - 1.0;
|
||||||
|
|
||||||
Quad::new(
|
Quad::new(
|
||||||
Vertex::new(Vec2::new(x + 0, y + 0).map(transform)),
|
Vertex::new(Vec2::new(x, y).map(transform)),
|
||||||
Vertex::new(Vec2::new(x + 1, y + 0).map(transform)),
|
Vertex::new(Vec2::new(x + 1, y).map(transform)),
|
||||||
Vertex::new(Vec2::new(x + 1, y + 1).map(transform)),
|
Vertex::new(Vec2::new(x + 1, y + 1).map(transform)),
|
||||||
Vertex::new(Vec2::new(x + 0, y + 1).map(transform)),
|
Vertex::new(Vec2::new(x, y + 1).map(transform)),
|
||||||
)
|
)
|
||||||
.rotated_by(if (x > detail as i32 / 2) ^ (y > detail as i32 / 2) {
|
.rotated_by(if (x > detail as i32 / 2) ^ (y > detail as i32 / 2) {
|
||||||
0
|
0
|
||||||
|
@ -994,7 +994,7 @@ impl Scene {
|
|||||||
let camera_data = (&self.camera, scene_data.figure_lod_render_distance);
|
let camera_data = (&self.camera, scene_data.figure_lod_render_distance);
|
||||||
|
|
||||||
// would instead have this as an extension.
|
// would instead have this as an extension.
|
||||||
if renderer.render_mode().shadow.is_map() && (is_daylight || light_data.1.len() > 0) {
|
if renderer.render_mode().shadow.is_map() && (is_daylight || !light_data.1.is_empty()) {
|
||||||
if is_daylight {
|
if is_daylight {
|
||||||
// Set up shadow mapping.
|
// Set up shadow mapping.
|
||||||
renderer.start_shadows();
|
renderer.start_shadows();
|
||||||
|
@ -53,8 +53,9 @@ impl ParticleMgr {
|
|||||||
power,
|
power,
|
||||||
reagent,
|
reagent,
|
||||||
} => {
|
} => {
|
||||||
for _ in 0..150 {
|
self.particles.resize(
|
||||||
self.particles.push(Particle::new(
|
self.particles.len() + 150,
|
||||||
|
Particle::new(
|
||||||
Duration::from_millis(if reagent.is_some() { 1000 } else { 250 }),
|
Duration::from_millis(if reagent.is_some() { 1000 } else { 250 }),
|
||||||
time,
|
time,
|
||||||
match reagent {
|
match reagent {
|
||||||
@ -66,17 +67,18 @@ impl ParticleMgr {
|
|||||||
None => ParticleMode::Shrapnel,
|
None => ParticleMode::Shrapnel,
|
||||||
},
|
},
|
||||||
*pos,
|
*pos,
|
||||||
));
|
),
|
||||||
}
|
);
|
||||||
|
|
||||||
for _ in 0..200 {
|
self.particles.resize(
|
||||||
self.particles.push(Particle::new(
|
self.particles.len() + 200,
|
||||||
|
Particle::new(
|
||||||
Duration::from_secs(4),
|
Duration::from_secs(4),
|
||||||
time,
|
time,
|
||||||
ParticleMode::CampfireSmoke,
|
ParticleMode::CampfireSmoke,
|
||||||
*pos + Vec2::<f32>::zero().map(|_| rng.gen_range(-1.0, 1.0) * power),
|
*pos + Vec2::<f32>::zero().map(|_| rng.gen_range(-1.0, 1.0) * power),
|
||||||
));
|
),
|
||||||
}
|
);
|
||||||
},
|
},
|
||||||
Outcome::ProjectileShot { .. } => {},
|
Outcome::ProjectileShot { .. } => {},
|
||||||
}
|
}
|
||||||
@ -107,14 +109,7 @@ impl ParticleMgr {
|
|||||||
|
|
||||||
fn maintain_body_particles(&mut self, scene_data: &SceneData) {
|
fn maintain_body_particles(&mut self, scene_data: &SceneData) {
|
||||||
let ecs = scene_data.state.ecs();
|
let ecs = scene_data.state.ecs();
|
||||||
for (_i, (_entity, body, pos)) in (
|
for (body, pos) in (&ecs.read_storage::<Body>(), &ecs.read_storage::<Pos>()).join() {
|
||||||
&ecs.entities(),
|
|
||||||
&ecs.read_storage::<Body>(),
|
|
||||||
&ecs.read_storage::<Pos>(),
|
|
||||||
)
|
|
||||||
.join()
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
match body {
|
match body {
|
||||||
Body::Object(object::Body::CampfireLit) => {
|
Body::Object(object::Body::CampfireLit) => {
|
||||||
self.maintain_campfirelit_particles(scene_data, pos)
|
self.maintain_campfirelit_particles(scene_data, pos)
|
||||||
@ -181,24 +176,26 @@ impl ParticleMgr {
|
|||||||
let time = scene_data.state.get_time();
|
let time = scene_data.state.get_time();
|
||||||
|
|
||||||
// fire
|
// fire
|
||||||
for _ in 0..self.scheduler.heartbeats(Duration::from_millis(3)) {
|
self.particles.resize(
|
||||||
self.particles.push(Particle::new(
|
self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(3))),
|
||||||
|
Particle::new(
|
||||||
Duration::from_millis(250),
|
Duration::from_millis(250),
|
||||||
time,
|
time,
|
||||||
ParticleMode::CampfireFire,
|
ParticleMode::CampfireFire,
|
||||||
pos.0,
|
pos.0,
|
||||||
));
|
),
|
||||||
}
|
);
|
||||||
|
|
||||||
// smoke
|
// smoke
|
||||||
for _ in 0..self.scheduler.heartbeats(Duration::from_millis(5)) {
|
self.particles.resize(
|
||||||
self.particles.push(Particle::new(
|
self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(5))),
|
||||||
|
Particle::new(
|
||||||
Duration::from_secs(2),
|
Duration::from_secs(2),
|
||||||
time,
|
time,
|
||||||
ParticleMode::CampfireSmoke,
|
ParticleMode::CampfireSmoke,
|
||||||
pos.0,
|
pos.0,
|
||||||
));
|
),
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maintain_bomb_particles(&mut self, scene_data: &SceneData, pos: &Pos) {
|
fn maintain_bomb_particles(&mut self, scene_data: &SceneData, pos: &Pos) {
|
||||||
@ -228,23 +225,23 @@ impl ParticleMgr {
|
|||||||
let ecs = state.ecs();
|
let ecs = state.ecs();
|
||||||
let time = state.get_time();
|
let time = state.get_time();
|
||||||
|
|
||||||
for (_i, (_entity, pos, character_state)) in (
|
for (pos, character_state) in (
|
||||||
&ecs.entities(),
|
|
||||||
&ecs.read_storage::<Pos>(),
|
&ecs.read_storage::<Pos>(),
|
||||||
&ecs.read_storage::<CharacterState>(),
|
&ecs.read_storage::<CharacterState>(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.enumerate()
|
|
||||||
{
|
{
|
||||||
if let CharacterState::Boost(_) = character_state {
|
if let CharacterState::Boost(_) = character_state {
|
||||||
for _ in 0..self.scheduler.heartbeats(Duration::from_millis(10)) {
|
self.particles.resize(
|
||||||
self.particles.push(Particle::new(
|
self.particles.len()
|
||||||
|
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(10))),
|
||||||
|
Particle::new(
|
||||||
Duration::from_secs(15),
|
Duration::from_secs(15),
|
||||||
time,
|
time,
|
||||||
ParticleMode::CampfireSmoke,
|
ParticleMode::CampfireSmoke,
|
||||||
pos.0,
|
pos.0,
|
||||||
));
|
),
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,6 +390,7 @@ impl HeartbeatScheduler {
|
|||||||
pub fn clear(&mut self) { self.timers.clear() }
|
pub fn clear(&mut self) { self.timers.clear() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
struct Particle {
|
struct Particle {
|
||||||
alive_until: f64, // created_at + lifespan
|
alive_until: f64, // created_at + lifespan
|
||||||
instance: ParticleInstance,
|
instance: ParticleInstance,
|
||||||
|
@ -2,12 +2,12 @@ use crate::{
|
|||||||
mesh::{greedy::GreedyMesh, Meshable},
|
mesh::{greedy::GreedyMesh, Meshable},
|
||||||
render::{
|
render::{
|
||||||
create_pp_mesh, create_skybox_mesh, BoneMeshes, Consts, FigureModel, FigurePipeline,
|
create_pp_mesh, create_skybox_mesh, BoneMeshes, Consts, FigureModel, FigurePipeline,
|
||||||
GlobalModel, Globals, Light, Model, PostProcessLocals, PostProcessPipeline, Renderer,
|
GlobalModel, Globals, Light, Mesh, Model, PostProcessLocals, PostProcessPipeline, Renderer,
|
||||||
Shadow, ShadowLocals, SkyboxLocals, SkyboxPipeline,
|
Shadow, ShadowLocals, SkyboxLocals, SkyboxPipeline, TerrainPipeline,
|
||||||
},
|
},
|
||||||
scene::{
|
scene::{
|
||||||
camera::{self, Camera, CameraMode},
|
camera::{self, Camera, CameraMode},
|
||||||
figure::{load_mesh, FigureColLights, FigureModelCache, FigureState},
|
figure::{load_mesh, FigureColLights, FigureModelCache, FigureModelEntry, FigureState},
|
||||||
LodData,
|
LodData,
|
||||||
},
|
},
|
||||||
window::{Event, PressState},
|
window::{Event, PressState},
|
||||||
@ -46,13 +46,14 @@ impl ReadVol for VoidVol {
|
|||||||
|
|
||||||
fn generate_mesh<'a>(
|
fn generate_mesh<'a>(
|
||||||
greedy: &mut GreedyMesh<'a>,
|
greedy: &mut GreedyMesh<'a>,
|
||||||
|
mesh: &mut Mesh<TerrainPipeline>,
|
||||||
segment: Segment,
|
segment: Segment,
|
||||||
offset: Vec3<f32>,
|
offset: Vec3<f32>,
|
||||||
) -> BoneMeshes {
|
) -> BoneMeshes {
|
||||||
let (opaque, _, /* shadow */ _, bounds) =
|
let (opaque, _, /* shadow */ _, bounds) =
|
||||||
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
|
Meshable::<FigurePipeline, &mut GreedyMesh>::generate_mesh(
|
||||||
segment,
|
segment,
|
||||||
(greedy, offset, Vec3::one()),
|
(greedy, mesh, offset, Vec3::one()),
|
||||||
);
|
);
|
||||||
(opaque /* , shadow */, bounds)
|
(opaque /* , shadow */, bounds)
|
||||||
}
|
}
|
||||||
@ -77,7 +78,7 @@ pub struct Scene {
|
|||||||
map_bounds: Vec2<f32>,
|
map_bounds: Vec2<f32>,
|
||||||
|
|
||||||
col_lights: FigureColLights,
|
col_lights: FigureColLights,
|
||||||
backdrop: Option<(FigureModel, FigureState<FixtureSkeleton>)>,
|
backdrop: Option<(FigureModelEntry<1>, FigureState<FixtureSkeleton>)>,
|
||||||
figure_model_cache: FigureModelCache,
|
figure_model_cache: FigureModelCache,
|
||||||
figure_state: FigureState<CharacterSkeleton>,
|
figure_state: FigureState<CharacterSkeleton>,
|
||||||
|
|
||||||
@ -150,12 +151,20 @@ impl Scene {
|
|||||||
backdrop: backdrop.map(|specifier| {
|
backdrop: backdrop.map(|specifier| {
|
||||||
let mut state = FigureState::new(renderer, FixtureSkeleton::default());
|
let mut state = FigureState::new(renderer, FixtureSkeleton::default());
|
||||||
let mut greedy = FigureModel::make_greedy();
|
let mut greedy = FigureModel::make_greedy();
|
||||||
let mesh = load_mesh(
|
let mut opaque_mesh = Mesh::new();
|
||||||
|
let (_opaque_mesh, (bounds, range)) = load_mesh(
|
||||||
specifier,
|
specifier,
|
||||||
Vec3::new(-55.0, -49.5, -2.0),
|
Vec3::new(-55.0, -49.5, -2.0),
|
||||||
|segment, offset| generate_mesh(&mut greedy, segment, offset),
|
|segment, offset| generate_mesh(&mut greedy, &mut opaque_mesh, segment, offset),
|
||||||
);
|
);
|
||||||
let model = col_lights.create_figure(renderer, greedy, mesh).unwrap();
|
// NOTE: Since MagicaVoxel sizes are limited to 256 × 256 × 256, and there are
|
||||||
|
// at most 3 meshed vertices per unique vertex, we know the
|
||||||
|
// total size is bounded by 2^24 * 3 * 1.5 which is bounded by
|
||||||
|
// 2^27, which fits in a u32.
|
||||||
|
let range = range.start as u32..range.end as u32;
|
||||||
|
let model = col_lights
|
||||||
|
.create_figure(renderer, greedy, (opaque_mesh, bounds), [range])
|
||||||
|
.unwrap();
|
||||||
let mut buf = [Default::default(); anim::MAX_BONE_COUNT];
|
let mut buf = [Default::default(); anim::MAX_BONE_COUNT];
|
||||||
state.update(
|
state.update(
|
||||||
renderer,
|
renderer,
|
||||||
@ -315,7 +324,7 @@ impl Scene {
|
|||||||
Rgba::broadcast(1.0),
|
Rgba::broadcast(1.0),
|
||||||
scene_data.delta_time,
|
scene_data.delta_time,
|
||||||
1.0,
|
1.0,
|
||||||
&model[0],
|
&model,
|
||||||
0,
|
0,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
@ -354,8 +363,8 @@ impl Scene {
|
|||||||
.0;
|
.0;
|
||||||
|
|
||||||
renderer.render_figure(
|
renderer.render_figure(
|
||||||
&model[0],
|
&model.models[0],
|
||||||
&self.col_lights.texture(),
|
&self.col_lights.texture(model),
|
||||||
&self.data,
|
&self.data,
|
||||||
self.figure_state.locals(),
|
self.figure_state.locals(),
|
||||||
self.figure_state.bone_consts(),
|
self.figure_state.bone_consts(),
|
||||||
@ -365,8 +374,8 @@ impl Scene {
|
|||||||
|
|
||||||
if let Some((model, state)) = &self.backdrop {
|
if let Some((model, state)) = &self.backdrop {
|
||||||
renderer.render_figure(
|
renderer.render_figure(
|
||||||
model,
|
&model.models[0],
|
||||||
&self.col_lights.texture(),
|
&self.col_lights.texture(model),
|
||||||
&self.data,
|
&self.data,
|
||||||
state.locals(),
|
state.locals(),
|
||||||
state.bone_consts(),
|
state.bone_consts(),
|
||||||
|
@ -393,7 +393,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug>(
|
|||||||
volume: <VolGrid2d<V> as SampleVol<Aabr<i32>>>::Sample,
|
volume: <VolGrid2d<V> as SampleVol<Aabr<i32>>>::Sample,
|
||||||
max_texture_size: u16,
|
max_texture_size: u16,
|
||||||
range: Aabb<i32>,
|
range: Aabb<i32>,
|
||||||
sprite_models: &HashMap<(BlockKind, usize), Vec<SpriteData>>,
|
sprite_data: &HashMap<(BlockKind, usize), Vec<SpriteData>>,
|
||||||
) -> MeshWorkerResponse {
|
) -> MeshWorkerResponse {
|
||||||
let (opaque_mesh, fluid_mesh, _shadow_mesh, (bounds, col_lights_info)) =
|
let (opaque_mesh, fluid_mesh, _shadow_mesh, (bounds, col_lights_info)) =
|
||||||
volume.generate_mesh((range, Vec2::new(max_texture_size, max_texture_size)));
|
volume.generate_mesh((range, Vec2::new(max_texture_size, max_texture_size)));
|
||||||
@ -424,7 +424,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug>(
|
|||||||
let key = (block.kind(), variation);
|
let key = (block.kind(), variation);
|
||||||
// NOTE: Safe bbecause we called sprite_config_for already.
|
// NOTE: Safe bbecause we called sprite_config_for already.
|
||||||
// NOTE: Safe because 0 ≤ ori < 8
|
// NOTE: Safe because 0 ≤ ori < 8
|
||||||
let sprite_data = &sprite_models[&key][0];
|
let sprite_data = &sprite_data[&key][0];
|
||||||
let instance = SpriteInstance::new(
|
let instance = SpriteInstance::new(
|
||||||
Mat4::identity()
|
Mat4::identity()
|
||||||
.translated_3d(sprite_data.offset)
|
.translated_3d(sprite_data.offset)
|
||||||
@ -484,7 +484,7 @@ pub struct Terrain<V: RectRasterableVol> {
|
|||||||
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
||||||
|
|
||||||
// GPU data
|
// GPU data
|
||||||
sprite_models: Arc<HashMap<(BlockKind, usize), Vec<SpriteData>>>,
|
sprite_data: Arc<HashMap<(BlockKind, usize), Vec<SpriteData>>>,
|
||||||
sprite_col_lights: Texture<ColLightFmt>,
|
sprite_col_lights: Texture<ColLightFmt>,
|
||||||
col_lights: Texture<ColLightFmt>,
|
col_lights: Texture<ColLightFmt>,
|
||||||
waves: Texture,
|
waves: Texture,
|
||||||
@ -513,6 +513,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
guillotiere::Size::new(i32::from(max_texture_size), i32::from(max_texture_size));
|
||||||
let mut greedy = GreedyMesh::new(max_size);
|
let mut greedy = GreedyMesh::new(max_size);
|
||||||
let mut locals_buffer = [SpriteLocals::default(); 8];
|
let mut locals_buffer = [SpriteLocals::default(); 8];
|
||||||
|
// NOTE: Tracks the start vertex of the next model to be meshed.
|
||||||
let mut make_models = |(kind, variation), s, offset, lod_axes: Vec3<f32>| {
|
let mut make_models = |(kind, variation), s, offset, lod_axes: Vec3<f32>| {
|
||||||
let scaled = [1.0, 0.8, 0.6, 0.4, 0.2];
|
let scaled = [1.0, 0.8, 0.6, 0.4, 0.2];
|
||||||
let model = assets::load_expect::<DotVoxData>(s);
|
let model = assets::load_expect::<DotVoxData>(s);
|
||||||
@ -550,12 +551,21 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
lod_axes * lod_scale_orig
|
lod_axes * lod_scale_orig
|
||||||
+ lod_axes.map(|e| if e == 0.0 { 1.0 } else { 0.0 })
|
+ lod_axes.map(|e| if e == 0.0 { 1.0 } else { 0.0 })
|
||||||
};
|
};
|
||||||
let opaque_model =
|
// Mesh generation exclusively acts using side effects; it has no
|
||||||
Meshable::<SpritePipeline, &mut GreedyMesh>::generate_mesh(
|
// interesting return value, but updates the mesh.
|
||||||
Segment::from(model.as_ref()).scaled_by(lod_scale),
|
let mut opaque_mesh = Mesh::new();
|
||||||
(&mut greedy, wind_sway >= 0.4 && lod_scale_orig == 1.0),
|
Meshable::<SpritePipeline, &mut GreedyMesh>::generate_mesh(
|
||||||
)
|
Segment::from(model.as_ref()).scaled_by(lod_scale),
|
||||||
.0;
|
(
|
||||||
|
&mut greedy,
|
||||||
|
&mut opaque_mesh,
|
||||||
|
wind_sway >= 0.4 && lod_scale_orig == 1.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let model = renderer
|
||||||
|
.create_model(&opaque_mesh)
|
||||||
|
.expect("Failed to upload sprite model data to the GPU!");
|
||||||
|
|
||||||
let sprite_scale = Vec3::one() / lod_scale;
|
let sprite_scale = Vec3::one() / lod_scale;
|
||||||
let sprite_mat: Mat4<f32> = sprite_mat * Mat4::scaling_3d(sprite_scale);
|
let sprite_mat: Mat4<f32> = sprite_mat * Mat4::scaling_3d(sprite_scale);
|
||||||
locals_buffer
|
locals_buffer
|
||||||
@ -569,8 +579,8 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
SpriteData {
|
SpriteData {
|
||||||
|
/* vertex_range */ model,
|
||||||
offset,
|
offset,
|
||||||
model: renderer.create_model(&opaque_model).unwrap(),
|
|
||||||
locals: renderer
|
locals: renderer
|
||||||
.create_consts(&locals_buffer)
|
.create_consts(&locals_buffer)
|
||||||
.expect("Failed to upload sprite locals to the GPU!"),
|
.expect("Failed to upload sprite locals to the GPU!"),
|
||||||
@ -580,7 +590,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let sprite_models: HashMap<(BlockKind, usize), _> = vec![
|
let sprite_data: HashMap<(BlockKind, usize), _> = vec![
|
||||||
// Windows
|
// Windows
|
||||||
make_models(
|
make_models(
|
||||||
(BlockKind::Window1, 0),
|
(BlockKind::Window1, 0),
|
||||||
@ -2370,6 +2380,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
.collect();
|
.collect();
|
||||||
let sprite_col_lights = ShadowPipeline::create_col_lights(renderer, greedy.finalize())
|
let sprite_col_lights = ShadowPipeline::create_col_lights(renderer, greedy.finalize())
|
||||||
.expect("Failed to upload sprite color and light data to the GPU!");
|
.expect("Failed to upload sprite color and light data to the GPU!");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
atlas,
|
atlas,
|
||||||
chunks: HashMap::default(),
|
chunks: HashMap::default(),
|
||||||
@ -2377,7 +2388,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
mesh_send_tmp: send,
|
mesh_send_tmp: send,
|
||||||
mesh_recv: recv,
|
mesh_recv: recv,
|
||||||
mesh_todo: HashMap::default(),
|
mesh_todo: HashMap::default(),
|
||||||
sprite_models: Arc::new(sprite_models),
|
sprite_data: Arc::new(sprite_data),
|
||||||
sprite_col_lights,
|
sprite_col_lights,
|
||||||
waves: renderer
|
waves: renderer
|
||||||
.create_texture(
|
.create_texture(
|
||||||
@ -2624,9 +2635,9 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
|
|
||||||
// Queue the worker thread.
|
// Queue the worker thread.
|
||||||
let started_tick = todo.started_tick;
|
let started_tick = todo.started_tick;
|
||||||
let sprite_models = Arc::clone(&self.sprite_models);
|
let sprite_data = Arc::clone(&self.sprite_data);
|
||||||
scene_data.thread_pool.execute(move || {
|
scene_data.thread_pool.execute(move || {
|
||||||
let sprite_models = sprite_models;
|
let sprite_data = sprite_data;
|
||||||
let _ = send.send(mesh_worker(
|
let _ = send.send(mesh_worker(
|
||||||
pos,
|
pos,
|
||||||
(min_z as f32, max_z as f32),
|
(min_z as f32, max_z as f32),
|
||||||
@ -2634,7 +2645,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
volume,
|
volume,
|
||||||
max_texture_size,
|
max_texture_size,
|
||||||
aabb,
|
aabb,
|
||||||
&sprite_models,
|
&sprite_data,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
todo.active_worker = Some(todo.started_tick);
|
todo.active_worker = Some(todo.started_tick);
|
||||||
@ -3067,15 +3078,15 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
&& dist_sqrd <= chunk_mag
|
&& dist_sqrd <= chunk_mag
|
||||||
|| dist_sqrd < sprite_high_detail_distance.powf(2.0)
|
|| dist_sqrd < sprite_high_detail_distance.powf(2.0)
|
||||||
{
|
{
|
||||||
&self.sprite_models[&kind][0]
|
&self.sprite_data[&kind][0]
|
||||||
} else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) {
|
} else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) {
|
||||||
&self.sprite_models[&kind][1]
|
&self.sprite_data[&kind][1]
|
||||||
} else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) {
|
} else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) {
|
||||||
&self.sprite_models[&kind][2]
|
&self.sprite_data[&kind][2]
|
||||||
} else if dist_sqrd < sprite_low_detail_distance.powf(2.0) {
|
} else if dist_sqrd < sprite_low_detail_distance.powf(2.0) {
|
||||||
&self.sprite_models[&kind][3]
|
&self.sprite_data[&kind][3]
|
||||||
} else {
|
} else {
|
||||||
&self.sprite_models[&kind][4]
|
&self.sprite_data[&kind][4]
|
||||||
};
|
};
|
||||||
renderer.render_sprites(
|
renderer.render_sprites(
|
||||||
model,
|
model,
|
||||||
|
@ -659,12 +659,7 @@ pub enum AudioOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AudioOutput {
|
impl AudioOutput {
|
||||||
pub fn is_enabled(&self) -> bool {
|
pub fn is_enabled(&self) -> bool { !matches!(self, Self::Off) }
|
||||||
match self {
|
|
||||||
Self::Off => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// `AudioSettings` controls the volume of different audio subsystems and which
|
/// `AudioSettings` controls the volume of different audio subsystems and which
|
||||||
/// device is used.
|
/// device is used.
|
||||||
|
@ -30,23 +30,19 @@ impl Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_keyboard_or_mouse(&self) -> bool {
|
pub fn is_keyboard_or_mouse(&self) -> bool {
|
||||||
match self.0 {
|
matches!(self.0,
|
||||||
Input::Press(_)
|
Input::Press(_)
|
||||||
| Input::Release(_)
|
| Input::Release(_)
|
||||||
| Input::Motion(_)
|
| Input::Motion(_)
|
||||||
| Input::Touch(_)
|
| Input::Touch(_)
|
||||||
| Input::Text(_) => true,
|
| Input::Text(_))
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_keyboard(&self) -> bool {
|
pub fn is_keyboard(&self) -> bool {
|
||||||
match self.0 {
|
matches!(self.0,
|
||||||
Input::Press(Button::Keyboard(_))
|
Input::Press(Button::Keyboard(_))
|
||||||
| Input::Release(Button::Keyboard(_))
|
| Input::Release(Button::Keyboard(_))
|
||||||
| Input::Text(_) => true,
|
| Input::Text(_))
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_resize(dims: Vec2<f64>) -> Self { Self(Input::Resize(dims.x, dims.y)) }
|
pub fn new_resize(dims: Vec2<f64>) -> Self { Self(Input::Resize(dims.x, dims.y)) }
|
||||||
|
@ -43,13 +43,12 @@ use conrod_core::{
|
|||||||
widget::{self, id::Generator},
|
widget::{self, id::Generator},
|
||||||
Rect, Scalar, UiBuilder, UiCell,
|
Rect, Scalar, UiBuilder, UiCell,
|
||||||
};
|
};
|
||||||
|
use core::{convert::TryInto, f32, f64, ops::Range};
|
||||||
use graphic::{Rotation, TexId};
|
use graphic::{Rotation, TexId};
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use std::{
|
use std::{
|
||||||
f32, f64,
|
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufReader, Read},
|
io::{BufReader, Read},
|
||||||
ops::Range,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
@ -67,7 +66,7 @@ enum DrawKind {
|
|||||||
Plain,
|
Plain,
|
||||||
}
|
}
|
||||||
enum DrawCommand {
|
enum DrawCommand {
|
||||||
Draw { kind: DrawKind, verts: Range<usize> },
|
Draw { kind: DrawKind, verts: Range<u32> },
|
||||||
Scissor(Aabr<u16>),
|
Scissor(Aabr<u16>),
|
||||||
WorldPos(Option<usize>),
|
WorldPos(Option<usize>),
|
||||||
}
|
}
|
||||||
@ -75,14 +74,28 @@ impl DrawCommand {
|
|||||||
fn image(verts: Range<usize>, id: TexId) -> DrawCommand {
|
fn image(verts: Range<usize>, id: TexId) -> DrawCommand {
|
||||||
DrawCommand::Draw {
|
DrawCommand::Draw {
|
||||||
kind: DrawKind::Image(id),
|
kind: DrawKind::Image(id),
|
||||||
verts,
|
verts: verts
|
||||||
|
.start
|
||||||
|
.try_into()
|
||||||
|
.expect("Vertex count for UI rendering does not fit in a u32!")
|
||||||
|
..verts
|
||||||
|
.end
|
||||||
|
.try_into()
|
||||||
|
.expect("Vertex count for UI rendering does not fit in a u32!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plain(verts: Range<usize>) -> DrawCommand {
|
fn plain(verts: Range<usize>) -> DrawCommand {
|
||||||
DrawCommand::Draw {
|
DrawCommand::Draw {
|
||||||
kind: DrawKind::Plain,
|
kind: DrawKind::Plain,
|
||||||
verts,
|
verts: verts
|
||||||
|
.start
|
||||||
|
.try_into()
|
||||||
|
.expect("Vertex count for UI rendering does not fit in a u32!")
|
||||||
|
..verts
|
||||||
|
.end
|
||||||
|
.try_into()
|
||||||
|
.expect("Vertex count for UI rendering does not fit in a u32!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -981,7 +994,7 @@ impl Ui {
|
|||||||
DrawKind::Plain => self.cache.glyph_cache_tex(),
|
DrawKind::Plain => self.cache.glyph_cache_tex(),
|
||||||
};
|
};
|
||||||
let model = self.model.submodel(verts.clone());
|
let model = self.model.submodel(verts.clone());
|
||||||
renderer.render_ui_element(&model, tex, scissor, globals, locals);
|
renderer.render_ui_element(model, tex, scissor, globals, locals);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#![allow(clippy::option_map_unit_fn)]
|
#![allow(clippy::option_map_unit_fn)]
|
||||||
#![feature(
|
#![feature(
|
||||||
arbitrary_enum_discriminant,
|
arbitrary_enum_discriminant,
|
||||||
const_if_match,
|
|
||||||
const_generics,
|
const_generics,
|
||||||
const_panic,
|
const_panic,
|
||||||
label_break_value,
|
label_break_value,
|
||||||
|
@ -120,29 +120,11 @@ pub enum RiverKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RiverKind {
|
impl RiverKind {
|
||||||
pub fn is_ocean(&self) -> bool {
|
pub fn is_ocean(&self) -> bool { matches!(*self, RiverKind::Ocean) }
|
||||||
if let RiverKind::Ocean = *self {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_river(&self) -> bool {
|
pub fn is_river(&self) -> bool { matches!(*self, RiverKind::River { .. }) }
|
||||||
if let RiverKind::River { .. } = *self {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_lake(&self) -> bool {
|
pub fn is_lake(&self) -> bool { matches!(*self, RiverKind::Lake { .. }) }
|
||||||
if let RiverKind::Lake { .. } = *self {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for RiverKind {
|
impl PartialOrd for RiverKind {
|
||||||
|
@ -170,9 +170,7 @@ pub fn sample_pos(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let downhill_wpos = downhill
|
let downhill_wpos = downhill.unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32));
|
||||||
.map(|downhill_pos| downhill_pos)
|
|
||||||
.unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32));
|
|
||||||
let alt = if is_basement { basement } else { alt };
|
let alt = if is_basement { basement } else { alt };
|
||||||
|
|
||||||
let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64;
|
let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64;
|
||||||
|
@ -93,21 +93,9 @@ pub enum StoreyFill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StoreyFill {
|
impl StoreyFill {
|
||||||
fn has_lower(&self) -> bool {
|
fn has_lower(&self) -> bool { matches!(self, StoreyFill::All) }
|
||||||
if let StoreyFill::All = self {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_upper(&self) -> bool {
|
fn has_upper(&self) -> bool { !matches!(self, StoreyFill::None) }
|
||||||
if let StoreyFill::None = self {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -236,10 +236,7 @@ impl Settlement {
|
|||||||
let origin = dir.map(|e| (e * 100.0) as i32);
|
let origin = dir.map(|e| (e * 100.0) as i32);
|
||||||
let origin = self
|
let origin = self
|
||||||
.land
|
.land
|
||||||
.find_tile_near(origin, |plot| match plot {
|
.find_tile_near(origin, |plot| matches!(plot, Some(&Plot::Field { .. })))
|
||||||
Some(&Plot::Field { .. }) => true,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if let Some(path) = self.town.as_ref().and_then(|town| {
|
if let Some(path) = self.town.as_ref().and_then(|town| {
|
||||||
@ -520,7 +517,7 @@ impl Settlement {
|
|||||||
.land
|
.land
|
||||||
.get_at_block(wpos - self.origin)
|
.get_at_block(wpos - self.origin)
|
||||||
.plot
|
.plot
|
||||||
.map(|p| if let Plot::Hazard = p { true } else { false })
|
.map(|p| matches!(p, Plot::Hazard))
|
||||||
.unwrap_or(true),
|
.unwrap_or(true),
|
||||||
..SpawnRules::default()
|
..SpawnRules::default()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user