mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/nametags' into 'master'
Make nametages etc fixed size and only display within limited range See merge request veloren/veloren!754
This commit is contained in:
commit
ecf36cb778
@ -81,6 +81,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Significantly reduced the use of warp during world generation.
|
- Significantly reduced the use of warp during world generation.
|
||||||
- Parallelized and otherwise sped up significant parts of world generation.
|
- Parallelized and otherwise sped up significant parts of world generation.
|
||||||
- Various performance improvements to world generation.
|
- Various performance improvements to world generation.
|
||||||
|
- Nametags now a fixed size and shown in a limited range
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -23,9 +23,6 @@ void main() {
|
|||||||
f_color = v_color;
|
f_color = v_color;
|
||||||
|
|
||||||
if (w_pos.w == 1.0) {
|
if (w_pos.w == 1.0) {
|
||||||
// In-game element
|
|
||||||
gl_Position = proj_mat * (view_mat * w_pos + vec4(v_pos, 0.0, 0.0));
|
|
||||||
} else if (w_pos.w == -1.0 ) {
|
|
||||||
// Fixed scale In-game element
|
// Fixed scale In-game element
|
||||||
vec4 projected_pos = proj_mat * view_mat * vec4(w_pos.xyz, 1.0);
|
vec4 projected_pos = proj_mat * view_mat * vec4(w_pos.xyz, 1.0);
|
||||||
gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos, 0.0, 1.0);
|
gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos, 0.0, 1.0);
|
||||||
|
@ -18,6 +18,9 @@ pub struct HpFloaterList {
|
|||||||
// Keep from spawning more floaters from same hp change
|
// Keep from spawning more floaters from same hp change
|
||||||
// Note: this can't detect a change if equivalent healing and damage take place simultaneously
|
// Note: this can't detect a change if equivalent healing and damage take place simultaneously
|
||||||
pub last_hp: u32,
|
pub last_hp: u32,
|
||||||
|
// The time since you last damaged this entity
|
||||||
|
// Used to display nametags outside normal range if this time is below a certain value
|
||||||
|
pub time_since_last_dmg_by_me: Option<f32>,
|
||||||
}
|
}
|
||||||
impl Component for HpFloaterList {
|
impl Component for HpFloaterList {
|
||||||
type Storage = IDVStorage<Self>;
|
type Storage = IDVStorage<Self>;
|
||||||
|
@ -43,6 +43,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
HpFloaterList {
|
HpFloaterList {
|
||||||
floaters: Vec::new(),
|
floaters: Vec::new(),
|
||||||
last_hp,
|
last_hp,
|
||||||
|
time_since_last_dmg_by_me: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -53,6 +54,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
.join()
|
.join()
|
||||||
.map(|(e, s, fl)| (e, s.health, fl))
|
.map(|(e, s, fl)| (e, s.health, fl))
|
||||||
{
|
{
|
||||||
|
// Increment timer for time since last damaged by me
|
||||||
|
hp_floater_list
|
||||||
|
.time_since_last_dmg_by_me
|
||||||
|
.as_mut()
|
||||||
|
.map(|t| *t += dt.0);
|
||||||
|
|
||||||
// Check if health has changed (won't work if damaged and then healed with
|
// Check if health has changed (won't work if damaged and then healed with
|
||||||
// equivalently in the same frame)
|
// equivalently in the same frame)
|
||||||
if hp_floater_list.last_hp != health.current() {
|
if hp_floater_list.last_hp != health.current() {
|
||||||
@ -65,7 +72,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
// health changes could be sent to the client as a list of events)
|
// health changes could be sent to the client as a list of events)
|
||||||
if match health.last_change.1.cause {
|
if match health.last_change.1.cause {
|
||||||
HealthSource::Attack { by } => {
|
HealthSource::Attack { by } => {
|
||||||
my_entity.0 == entity || my_uid.map_or(false, |&uid| by == uid)
|
let by_me = my_uid.map_or(false, |&uid| by == uid);
|
||||||
|
// If the attack was by me also reset this timer
|
||||||
|
if by_me {
|
||||||
|
hp_floater_list.time_since_last_dmg_by_me = Some(0.0);
|
||||||
|
}
|
||||||
|
my_entity.0 == entity || by_me
|
||||||
}
|
}
|
||||||
HealthSource::Suicide => my_entity.0 == entity,
|
HealthSource::Suicide => my_entity.0 == entity,
|
||||||
HealthSource::World => my_entity.0 == entity,
|
HealthSource::World => my_entity.0 == entity,
|
||||||
@ -106,6 +118,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
HpFloaterList {
|
HpFloaterList {
|
||||||
ref mut floaters,
|
ref mut floaters,
|
||||||
ref last_hp,
|
ref last_hp,
|
||||||
|
..
|
||||||
},
|
},
|
||||||
) in (&entities, &mut hp_floater_lists).join()
|
) in (&entities, &mut hp_floater_lists).join()
|
||||||
{
|
{
|
||||||
|
@ -79,6 +79,13 @@ const GROUP_COLOR: Color = Color::Rgba(0.47, 0.84, 1.0, 1.0);
|
|||||||
const FACTION_COLOR: Color = Color::Rgba(0.24, 1.0, 0.48, 1.0);
|
const FACTION_COLOR: Color = Color::Rgba(0.24, 1.0, 0.48, 1.0);
|
||||||
const KILL_COLOR: Color = Color::Rgba(1.0, 0.17, 0.17, 1.0);
|
const KILL_COLOR: Color = Color::Rgba(1.0, 0.17, 0.17, 1.0);
|
||||||
|
|
||||||
|
/// Distance at which nametags are visible
|
||||||
|
const NAMETAG_RANGE: f32 = 40.0;
|
||||||
|
/// Time nametags stay visible after doing damage even if they are out of range in seconds
|
||||||
|
const NAMETAG_DMG_TIME: f32 = 60.0;
|
||||||
|
/// Range damaged triggered nametags can be seen
|
||||||
|
const NAMETAG_DMG_RANGE: f32 = 120.0;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
struct Ids {
|
struct Ids {
|
||||||
// Crosshair
|
// Crosshair
|
||||||
@ -545,7 +552,6 @@ impl Hud {
|
|||||||
let scales = ecs.read_storage::<comp::Scale>();
|
let scales = ecs.read_storage::<comp::Scale>();
|
||||||
let entities = ecs.entities();
|
let entities = ecs.entities();
|
||||||
let me = client.entity();
|
let me = client.entity();
|
||||||
let view_distance = client.view_distance().unwrap_or(1);
|
|
||||||
let own_level = stats
|
let own_level = stats
|
||||||
.get(client.entity())
|
.get(client.entity())
|
||||||
.map_or(0, |stats| stats.level.level());
|
.map_or(0, |stats| stats.level.level());
|
||||||
@ -632,21 +638,25 @@ impl Hud {
|
|||||||
&stats,
|
&stats,
|
||||||
&energy,
|
&energy,
|
||||||
scales.maybe(),
|
scales.maybe(),
|
||||||
hp_floater_lists.maybe(), // Potentially move this to its own loop
|
&hp_floater_lists,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(entity, _, _, stats, _, _, _)| {
|
.filter(|(entity, _, _, stats, _, _, _)| {
|
||||||
*entity != me && !stats.is_dead
|
*entity != me && !stats.is_dead
|
||||||
//&& stats.health.current() != stats.health.maximum()
|
//&& stats.health.current() != stats.health.maximum()
|
||||||
})
|
})
|
||||||
// Don't process health bars outside the vd (visibility further limited by ui backend)
|
// Don't show outside a certain range
|
||||||
.filter(|(_, pos, _, _, _, _, _)| {
|
.filter(|(_, pos, _, _, _, _, hpfl)| {
|
||||||
Vec2::from(pos.0 - player_pos)
|
pos.0.distance_squared(player_pos)
|
||||||
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
|
< (if hpfl
|
||||||
d.abs() as f32 / sz as f32
|
.time_since_last_dmg_by_me
|
||||||
|
.map_or(false, |t| t < NAMETAG_DMG_TIME)
|
||||||
|
{
|
||||||
|
NAMETAG_DMG_RANGE
|
||||||
|
} else {
|
||||||
|
NAMETAG_RANGE
|
||||||
})
|
})
|
||||||
.magnitude()
|
.powi(2)
|
||||||
< view_distance as f32
|
|
||||||
})
|
})
|
||||||
.map(|(_, pos, interpolated, stats, energy, scale, f)| {
|
.map(|(_, pos, interpolated, stats, energy, scale, f)| {
|
||||||
(
|
(
|
||||||
@ -684,7 +694,6 @@ impl Hud {
|
|||||||
Rectangle::fill_with([82.0, 8.0], Color::Rgba(0.3, 0.3, 0.3, 0.5))
|
Rectangle::fill_with([82.0, 8.0], Color::Rgba(0.3, 0.3, 0.3, 0.5))
|
||||||
.x_y(0.0, -25.0)
|
.x_y(0.0, -25.0)
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
|
||||||
.set(back_id, ui_widgets);
|
.set(back_id, ui_widgets);
|
||||||
|
|
||||||
// % HP Filling
|
// % HP Filling
|
||||||
@ -699,7 +708,6 @@ impl Hud {
|
|||||||
HP_COLOR
|
HP_COLOR
|
||||||
}))
|
}))
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
|
||||||
.set(health_bar_id, ui_widgets);
|
.set(health_bar_id, ui_widgets);
|
||||||
// % Mana Filling
|
// % Mana Filling
|
||||||
Rectangle::fill_with(
|
Rectangle::fill_with(
|
||||||
@ -711,7 +719,6 @@ impl Hud {
|
|||||||
)
|
)
|
||||||
.x_y(4.5 + (energy_percentage / 100.0 * 36.5) - 36.45, -28.0)
|
.x_y(4.5 + (energy_percentage / 100.0 * 36.5) - 36.45, -28.0)
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
|
||||||
.set(mana_bar_id, ui_widgets);
|
.set(mana_bar_id, ui_widgets);
|
||||||
|
|
||||||
// Foreground
|
// Foreground
|
||||||
@ -720,12 +727,12 @@ impl Hud {
|
|||||||
.x_y(0.0, -25.0)
|
.x_y(0.0, -25.0)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
|
||||||
.set(front_id, ui_widgets);
|
.set(front_id, ui_widgets);
|
||||||
|
|
||||||
// Enemy SCT
|
// Enemy SCT
|
||||||
if let Some(HpFloaterList { floaters, .. }) = hp_floater_list
|
if let Some(floaters) = Some(hp_floater_list)
|
||||||
.filter(|fl| !fl.floaters.is_empty() && global_state.settings.gameplay.sct)
|
.filter(|fl| !fl.floaters.is_empty() && global_state.settings.gameplay.sct)
|
||||||
|
.map(|l| &l.floaters)
|
||||||
{
|
{
|
||||||
// Colors
|
// Colors
|
||||||
const WHITE: Rgb<f32> = Rgb::new(1.0, 0.9, 0.8);
|
const WHITE: Rgb<f32> = Rgb::new(1.0, 0.9, 0.8);
|
||||||
@ -790,8 +797,6 @@ impl Hud {
|
|||||||
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
||||||
.x_y(0.0, y - 3.0)
|
.x_y(0.0, y - 3.0)
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8))
|
||||||
.fixed_scale()
|
|
||||||
.resolution(100.0)
|
|
||||||
.set(sct_bg_id, ui_widgets);
|
.set(sct_bg_id, ui_widgets);
|
||||||
Text::new(&format!("{}", hp_damage.abs()))
|
Text::new(&format!("{}", hp_damage.abs()))
|
||||||
.font_size(font_size)
|
.font_size(font_size)
|
||||||
@ -802,8 +807,6 @@ impl Hud {
|
|||||||
Color::Rgba(0.1, 1.0, 0.1, fade)
|
Color::Rgba(0.1, 1.0, 0.1, fade)
|
||||||
})
|
})
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8))
|
||||||
.fixed_scale()
|
|
||||||
.resolution(100.0)
|
|
||||||
.set(sct_id, ui_widgets);
|
.set(sct_id, ui_widgets);
|
||||||
} else {
|
} else {
|
||||||
for floater in floaters {
|
for floater in floaters {
|
||||||
@ -846,8 +849,6 @@ impl Hud {
|
|||||||
.position_ingame(
|
.position_ingame(
|
||||||
pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8),
|
pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8),
|
||||||
)
|
)
|
||||||
.fixed_scale()
|
|
||||||
.resolution(100.0)
|
|
||||||
.set(sct_bg_id, ui_widgets);
|
.set(sct_bg_id, ui_widgets);
|
||||||
Text::new(&format!("{}", (floater.hp_change).abs()))
|
Text::new(&format!("{}", (floater.hp_change).abs()))
|
||||||
.font_size(font_size)
|
.font_size(font_size)
|
||||||
@ -860,8 +861,6 @@ impl Hud {
|
|||||||
.position_ingame(
|
.position_ingame(
|
||||||
pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8),
|
pos + Vec3::new(0.0, 0.0, 1.5 * scale as f32 + 1.8),
|
||||||
)
|
)
|
||||||
.fixed_scale()
|
|
||||||
.resolution(100.0)
|
|
||||||
.set(sct_id, ui_widgets);
|
.set(sct_id, ui_widgets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1107,19 +1106,24 @@ impl Hud {
|
|||||||
&stats,
|
&stats,
|
||||||
players.maybe(),
|
players.maybe(),
|
||||||
scales.maybe(),
|
scales.maybe(),
|
||||||
|
&hp_floater_lists,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(entity, _, _, stats, _, _)| *entity != me && !stats.is_dead)
|
.filter(|(entity, _, _, stats, _, _, _)| *entity != me && !stats.is_dead)
|
||||||
// Don't process nametags outside the vd (visibility further limited by ui backend)
|
// Don't show outside a certain range
|
||||||
.filter(|(_, pos, _, _, _, _)| {
|
.filter(|(_, pos, _, _, _, _, hpfl)| {
|
||||||
Vec2::from(pos.0 - player_pos)
|
pos.0.distance_squared(player_pos)
|
||||||
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
|
< (if hpfl
|
||||||
d.abs() as f32 / sz as f32
|
.time_since_last_dmg_by_me
|
||||||
|
.map_or(false, |t| t < NAMETAG_DMG_TIME)
|
||||||
|
{
|
||||||
|
NAMETAG_DMG_RANGE
|
||||||
|
} else {
|
||||||
|
NAMETAG_RANGE
|
||||||
})
|
})
|
||||||
.magnitude()
|
.powi(2)
|
||||||
< view_distance as f32
|
|
||||||
})
|
})
|
||||||
.map(|(_, pos, interpolated, stats, player, scale)| {
|
.map(|(_, pos, interpolated, stats, player, scale, _)| {
|
||||||
// TODO: This is temporary
|
// TODO: This is temporary
|
||||||
// If the player used the default character name display their name instead
|
// If the player used the default character name display their name instead
|
||||||
let name = if stats.name == "Character Name" {
|
let name = if stats.name == "Character Name" {
|
||||||
@ -1156,14 +1160,12 @@ impl Hud {
|
|||||||
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
.color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
|
||||||
.x_y(-1.0, -1.0)
|
.x_y(-1.0, -1.0)
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
|
||||||
.set(name_bg_id, ui_widgets);
|
.set(name_bg_id, ui_widgets);
|
||||||
Text::new(&name)
|
Text::new(&name)
|
||||||
.font_size(20)
|
.font_size(20)
|
||||||
.color(Color::Rgba(0.61, 0.61, 0.89, 1.0))
|
.color(Color::Rgba(0.61, 0.61, 0.89, 1.0))
|
||||||
.x_y(0.0, 0.0)
|
.x_y(0.0, 0.0)
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
|
||||||
.set(name_id, ui_widgets);
|
.set(name_id, ui_widgets);
|
||||||
|
|
||||||
// Level
|
// Level
|
||||||
@ -1193,7 +1195,6 @@ impl Hud {
|
|||||||
})
|
})
|
||||||
.x_y(-37.0, -24.0)
|
.x_y(-37.0, -24.0)
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
|
||||||
.set(level_id, ui_widgets);
|
.set(level_id, ui_widgets);
|
||||||
if level_comp > 9 {
|
if level_comp > 9 {
|
||||||
let skull_ani = ((self.pulse * 0.7/*speed factor*/).cos() * 0.5 + 0.5) * 10.0; //Animation timer
|
let skull_ani = ((self.pulse * 0.7/*speed factor*/).cos() * 0.5 + 0.5) * 10.0; //Animation timer
|
||||||
@ -1206,7 +1207,6 @@ impl Hud {
|
|||||||
.x_y(-39.0, -25.0)
|
.x_y(-39.0, -25.0)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
.position_ingame(pos + Vec3::new(0.0, 0.0, 1.5 * scale + 1.5))
|
||||||
.resolution(100.0)
|
|
||||||
.set(level_skull_id, ui_widgets);
|
.set(level_skull_id, ui_widgets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2036,12 +2036,9 @@ impl Hud {
|
|||||||
self.ui.focus_widget(maybe_id);
|
self.ui.focus_widget(maybe_id);
|
||||||
}
|
}
|
||||||
let events = self.update_layout(client, global_state, debug_info, dt);
|
let events = self.update_layout(client, global_state, debug_info, dt);
|
||||||
let (view_mat, _, _) = camera.compute_dependents(client);
|
let (v_mat, p_mat, _) = camera.compute_dependents(client);
|
||||||
let fov = camera.get_fov();
|
self.ui
|
||||||
self.ui.maintain(
|
.maintain(&mut global_state.window.renderer_mut(), Some(p_mat * v_mat));
|
||||||
&mut global_state.window.renderer_mut(),
|
|
||||||
Some((view_mat, fov)),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if item images need to be reloaded
|
// Check if item images need to be reloaded
|
||||||
self.item_imgs.reload_if_changed(&mut self.ui);
|
self.item_imgs.reload_if_changed(&mut self.ui);
|
||||||
|
@ -14,7 +14,7 @@ pub use scale::{Scale, ScaleMode};
|
|||||||
pub use widgets::{
|
pub use widgets::{
|
||||||
image_frame::ImageFrame,
|
image_frame::ImageFrame,
|
||||||
image_slider::ImageSlider,
|
image_slider::ImageSlider,
|
||||||
ingame::{Ingame, IngameAnchor, Ingameable},
|
ingame::{Ingame, Ingameable},
|
||||||
radio_list::RadioList,
|
radio_list::RadioList,
|
||||||
toggle_button::ToggleButton,
|
toggle_button::ToggleButton,
|
||||||
tooltip::{Tooltip, TooltipManager, Tooltipable},
|
tooltip::{Tooltip, TooltipManager, Tooltipable},
|
||||||
@ -257,7 +257,7 @@ impl Ui {
|
|||||||
self.ui.widget_input(id)
|
self.ui.widget_input(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maintain(&mut self, renderer: &mut Renderer, cam_params: Option<(Mat4<f32>, f32)>) {
|
pub fn maintain(&mut self, renderer: &mut Renderer, view_projection_mat: Option<Mat4<f32>>) {
|
||||||
// Maintain tooltip manager
|
// Maintain tooltip manager
|
||||||
self.tooltip_manager
|
self.tooltip_manager
|
||||||
.maintain(self.ui.global_input(), self.scale.scale_factor_logical());
|
.maintain(self.ui.global_input(), self.scale.scale_factor_logical());
|
||||||
@ -304,13 +304,12 @@ impl Ui {
|
|||||||
|
|
||||||
enum Placement {
|
enum Placement {
|
||||||
Interface,
|
Interface,
|
||||||
// Number of primitives left to render ingame and relative scaling/resolution
|
// Number of primitives left to render ingame and visibility
|
||||||
InWorld(usize, Option<(f64, f64)>),
|
InWorld(usize, bool),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut placement = Placement::Interface;
|
let mut placement = Placement::Interface;
|
||||||
// TODO: maybe mutate an ingame scale factor instead of this, depends on if we want them to scale with other ui scaling or not
|
let p_scale_factor = self.scale.scale_factor_physical();
|
||||||
let mut p_scale_factor = self.scale.scale_factor_physical();
|
|
||||||
|
|
||||||
// Switches to the `Plain` state and completes the previous `Command` if not already in the
|
// Switches to the `Plain` state and completes the previous `Command` if not already in the
|
||||||
// `Plain` state.
|
// `Plain` state.
|
||||||
@ -377,7 +376,6 @@ impl Ui {
|
|||||||
// No primitives left to place in the world at the current position, go back to drawing the interface
|
// No primitives left to place in the world at the current position, go back to drawing the interface
|
||||||
Placement::InWorld(0, _) => {
|
Placement::InWorld(0, _) => {
|
||||||
placement = Placement::Interface;
|
placement = Placement::Interface;
|
||||||
p_scale_factor = self.scale.scale_factor_physical();
|
|
||||||
// Finish current state
|
// Finish current state
|
||||||
self.draw_commands.push(match current_state {
|
self.draw_commands.push(match current_state {
|
||||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||||
@ -388,22 +386,23 @@ impl Ui {
|
|||||||
self.draw_commands.push(DrawCommand::WorldPos(None));
|
self.draw_commands.push(DrawCommand::WorldPos(None));
|
||||||
}
|
}
|
||||||
// Primitives still left to draw ingame
|
// Primitives still left to draw ingame
|
||||||
Placement::InWorld(num_prims, res) => match kind {
|
Placement::InWorld(num_prims, visible) => match kind {
|
||||||
// Other types aren't drawn & shouldn't decrement the number of primitives left to draw ingame
|
// Other types aren't drawn & shouldn't decrement the number of primitives left to draw ingame
|
||||||
PrimitiveKind::Other(_) => {}
|
PrimitiveKind::Other(_) => {}
|
||||||
// Decrement the number of primitives left
|
// Decrement the number of primitives left
|
||||||
_ => placement = Placement::InWorld(num_prims - 1, res),
|
_ => {
|
||||||
|
placement = Placement::InWorld(num_prims - 1, visible);
|
||||||
|
// Behind the camera
|
||||||
|
if !visible {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Placement::Interface => {}
|
Placement::Interface => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0).
|
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0).
|
||||||
let (ui_win_w, ui_win_h) = match placement {
|
let (ui_win_w, ui_win_h) = (self.ui.win_w, self.ui.win_h);
|
||||||
Placement::InWorld(_, Some(res)) => res,
|
|
||||||
// Behind the camera or far away
|
|
||||||
Placement::InWorld(_, None) => continue,
|
|
||||||
Placement::Interface => (self.ui.win_w, self.ui.win_h),
|
|
||||||
};
|
|
||||||
let vx = |x: f64| (x / ui_win_w * 2.0) as f32;
|
let vx = |x: f64| (x / ui_win_w * 2.0) as f32;
|
||||||
let vy = |y: f64| (y / ui_win_h * 2.0) as f32;
|
let vy = |y: f64| (y / ui_win_h * 2.0) as f32;
|
||||||
let gl_aabr = |rect: Rect| {
|
let gl_aabr = |rect: Rect| {
|
||||||
@ -615,7 +614,7 @@ impl Ui {
|
|||||||
PrimitiveKind::Other(container) => {
|
PrimitiveKind::Other(container) => {
|
||||||
if container.type_id == std::any::TypeId::of::<widgets::ingame::State>() {
|
if container.type_id == std::any::TypeId::of::<widgets::ingame::State>() {
|
||||||
// Calculate the scale factor to pixels at this 3d point using the camera.
|
// Calculate the scale factor to pixels at this 3d point using the camera.
|
||||||
if let Some((view_mat, fov)) = cam_params {
|
if let Some(view_projection_mat) = view_projection_mat {
|
||||||
// Retrieve world position
|
// Retrieve world position
|
||||||
let parameters = container
|
let parameters = container
|
||||||
.state_and_style::<widgets::ingame::State, widgets::ingame::Style>()
|
.state_and_style::<widgets::ingame::State, widgets::ingame::Style>()
|
||||||
@ -623,16 +622,21 @@ impl Ui {
|
|||||||
.state
|
.state
|
||||||
.parameters;
|
.parameters;
|
||||||
|
|
||||||
let pos_in_view = view_mat * Vec4::from_point(parameters.pos);
|
let pos_on_screen = (view_projection_mat
|
||||||
|
* Vec4::from_point(parameters.pos))
|
||||||
let scale_factor = self.ui.win_w as f64
|
.homogenized();
|
||||||
/ (-2.0
|
let visible = if pos_on_screen.z > -1.0 && pos_on_screen.z < 1.0 {
|
||||||
* pos_in_view.z as f64
|
let x = pos_on_screen.x;
|
||||||
* (0.5 * fov as f64).tan()
|
let y = pos_on_screen.y;
|
||||||
// TODO: make this have no effect for fixed scale
|
let (w, h) = parameters.dims.into_tuple();
|
||||||
* parameters.res as f64);
|
let (half_w, half_h) = (w / ui_win_w as f32, h / ui_win_h as f32);
|
||||||
// Don't process ingame elements behind the camera or very far away
|
(x - half_w < 1.0 && x + half_w > -1.0)
|
||||||
placement = if scale_factor > 0.2 {
|
&& (y - half_h < 1.0 && y + half_h > -1.0)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
// Don't process ingame elements outside the frustum
|
||||||
|
placement = if visible {
|
||||||
// Finish current state
|
// Finish current state
|
||||||
self.draw_commands.push(match current_state {
|
self.draw_commands.push(match current_state {
|
||||||
State::Plain => {
|
State::Plain => {
|
||||||
@ -643,12 +647,9 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
start = mesh.vertices().len();
|
start = mesh.vertices().len();
|
||||||
// Push new position command
|
|
||||||
let mut world_pos = Vec4::from_point(parameters.pos);
|
|
||||||
if parameters.fixed_scale {
|
|
||||||
world_pos.w = -1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// Push new position command
|
||||||
|
let world_pos = Vec4::from_point(parameters.pos);
|
||||||
if self.ingame_locals.len() > ingame_local_index {
|
if self.ingame_locals.len() > ingame_local_index {
|
||||||
renderer
|
renderer
|
||||||
.update_consts(
|
.update_consts(
|
||||||
@ -664,28 +665,9 @@ impl Ui {
|
|||||||
.push(DrawCommand::WorldPos(Some(ingame_local_index)));
|
.push(DrawCommand::WorldPos(Some(ingame_local_index)));
|
||||||
ingame_local_index += 1;
|
ingame_local_index += 1;
|
||||||
|
|
||||||
p_scale_factor = if parameters.fixed_scale {
|
Placement::InWorld(parameters.num, true)
|
||||||
self.scale.scale_factor_physical()
|
|
||||||
} else {
|
} else {
|
||||||
((scale_factor * 10.0).log2().round().powi(2) / 10.0)
|
Placement::InWorld(parameters.num, false)
|
||||||
.min(1.6)
|
|
||||||
.max(0.2)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Scale down ingame elements that are close to the camera
|
|
||||||
let res = if parameters.fixed_scale {
|
|
||||||
(self.ui.win_w, self.ui.win_h)
|
|
||||||
} else if scale_factor > 3.2 {
|
|
||||||
let res = parameters.res * scale_factor as f32 / 3.2;
|
|
||||||
(res as f64, res as f64)
|
|
||||||
} else {
|
|
||||||
let res = parameters.res;
|
|
||||||
(res as f64, res as f64)
|
|
||||||
};
|
|
||||||
|
|
||||||
Placement::InWorld(parameters.num, Some(res))
|
|
||||||
} else {
|
|
||||||
Placement::InWorld(parameters.num, None)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use conrod_core::{
|
use conrod_core::{widget, Position, Sizeable, Ui, UiCell, Widget, WidgetCommon};
|
||||||
builder_methods, position::Dimension, widget, Position, Ui, UiCell, Widget, WidgetCommon,
|
|
||||||
};
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Clone, WidgetCommon)]
|
#[derive(Clone, WidgetCommon)]
|
||||||
@ -8,7 +6,8 @@ pub struct Ingame<W> {
|
|||||||
#[conrod(common_builder)]
|
#[conrod(common_builder)]
|
||||||
common: widget::CommonBuilder,
|
common: widget::CommonBuilder,
|
||||||
widget: W,
|
widget: W,
|
||||||
parameters: IngameParameters,
|
prim_num: usize,
|
||||||
|
pos: Vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Ingameable: Widget + Sized {
|
pub trait Ingameable: Widget + Sized {
|
||||||
@ -47,19 +46,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All ingame widgets are now fixed scale
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub struct IngameParameters {
|
pub struct IngameParameters {
|
||||||
// Number of primitive widgets to position in the game at the specified position
|
// Number of primitive widgets to position in the game at the specified position
|
||||||
// Note this could be more than the number of widgets in the widgets field since widgets can contain widgets
|
// Note this could be more than the number of widgets in the widgets field since widgets can contain widgets
|
||||||
pub num: usize,
|
pub num: usize,
|
||||||
pub pos: Vec3<f32>,
|
pub pos: Vec3<f32>,
|
||||||
// Number of pixels per 1 unit in world coordinates (ie a voxel)
|
pub dims: Vec2<f32>,
|
||||||
// Used for widgets that are rasterized before being sent to the gpu (text & images)
|
|
||||||
// Potentially make this automatic based on distance to camera?
|
|
||||||
pub res: f32,
|
|
||||||
// Whether the widgets should be scaled based on distance to the camera or if they should be a
|
|
||||||
// fixed size (res is ignored in that case)
|
|
||||||
pub fixed_scale: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
@ -73,22 +67,11 @@ impl<W: Ingameable> Ingame<W> {
|
|||||||
pub fn new(pos: Vec3<f32>, widget: W) -> Self {
|
pub fn new(pos: Vec3<f32>, widget: W) -> Self {
|
||||||
Self {
|
Self {
|
||||||
common: widget::CommonBuilder::default(),
|
common: widget::CommonBuilder::default(),
|
||||||
parameters: IngameParameters {
|
prim_num: widget.prim_count(),
|
||||||
num: widget.prim_count(),
|
|
||||||
pos,
|
pos,
|
||||||
res: 1.0,
|
|
||||||
fixed_scale: false,
|
|
||||||
},
|
|
||||||
widget,
|
widget,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn fixed_scale(mut self) -> Self {
|
|
||||||
self.parameters.fixed_scale = true;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
builder_methods! {
|
|
||||||
pub resolution { parameters.res = f32 }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Ingameable> Widget for Ingame<W> {
|
impl<W: Ingameable> Widget for Ingame<W> {
|
||||||
@ -99,7 +82,11 @@ impl<W: Ingameable> Widget for Ingame<W> {
|
|||||||
fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
|
fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
|
||||||
State {
|
State {
|
||||||
id: Some(id_gen.next()),
|
id: Some(id_gen.next()),
|
||||||
parameters: self.parameters,
|
parameters: IngameParameters {
|
||||||
|
num: self.prim_num,
|
||||||
|
pos: self.pos,
|
||||||
|
dims: Vec2::zero(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,10 +97,19 @@ impl<W: Ingameable> Widget for Ingame<W> {
|
|||||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
let widget::UpdateArgs { state, ui, .. } = args;
|
let widget::UpdateArgs { state, ui, .. } = args;
|
||||||
let Ingame {
|
let Ingame {
|
||||||
widget, parameters, ..
|
widget,
|
||||||
|
prim_num,
|
||||||
|
pos,
|
||||||
|
..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// Update pos if it has changed
|
let parameters = IngameParameters {
|
||||||
|
num: prim_num,
|
||||||
|
pos,
|
||||||
|
dims: Vec2::<f64>::from(widget.get_wh(ui).unwrap_or([1.0, 1.0])).map(|e| e as f32),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update parameters if it has changed
|
||||||
if state.parameters != parameters {
|
if state.parameters != parameters {
|
||||||
state.update(|s| {
|
state.update(|s| {
|
||||||
s.parameters = parameters;
|
s.parameters = parameters;
|
||||||
@ -129,75 +125,4 @@ impl<W: Ingameable> Widget for Ingame<W> {
|
|||||||
fn default_y_position(&self, _: &Ui) -> Position {
|
fn default_y_position(&self, _: &Ui) -> Position {
|
||||||
Position::Absolute(0.0)
|
Position::Absolute(0.0)
|
||||||
}
|
}
|
||||||
fn default_x_dimension(&self, _: &Ui) -> Dimension {
|
|
||||||
Dimension::Absolute(1.0)
|
|
||||||
}
|
|
||||||
fn default_y_dimension(&self, _: &Ui) -> Dimension {
|
|
||||||
Dimension::Absolute(1.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use this if you have multiple widgets that you want to place at the same spot in-game
|
|
||||||
// but don't want to create a new custom widget to contain them both
|
|
||||||
// Note: widgets must be set immediately after settings this
|
|
||||||
// Note: remove this if it ends up unused
|
|
||||||
#[derive(Clone, WidgetCommon)]
|
|
||||||
pub struct IngameAnchor {
|
|
||||||
#[conrod(common_builder)]
|
|
||||||
common: widget::CommonBuilder,
|
|
||||||
parameters: IngameParameters,
|
|
||||||
}
|
|
||||||
impl IngameAnchor {
|
|
||||||
pub fn new(pos: Vec3<f32>) -> Self {
|
|
||||||
IngameAnchor {
|
|
||||||
common: widget::CommonBuilder::default(),
|
|
||||||
parameters: IngameParameters {
|
|
||||||
num: 0,
|
|
||||||
pos,
|
|
||||||
res: 1.0,
|
|
||||||
fixed_scale: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn for_widget(mut self, widget: impl Ingameable) -> Self {
|
|
||||||
self.parameters.num += widget.prim_count();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn for_widgets(mut self, widget: impl Ingameable, n: usize) -> Self {
|
|
||||||
self.parameters.num += n * widget.prim_count();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn for_prims(mut self, num: usize) -> Self {
|
|
||||||
self.parameters.num += num;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Widget for IngameAnchor {
|
|
||||||
type State = State;
|
|
||||||
type Style = Style;
|
|
||||||
type Event = ();
|
|
||||||
|
|
||||||
fn init_state(&self, _: widget::id::Generator) -> Self::State {
|
|
||||||
State {
|
|
||||||
id: None,
|
|
||||||
parameters: self.parameters,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style(&self) -> Self::Style {
|
|
||||||
()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
|
||||||
let widget::UpdateArgs { id: _, state, .. } = args;
|
|
||||||
let IngameAnchor { parameters, .. } = self;
|
|
||||||
|
|
||||||
// Update pos if it has changed
|
|
||||||
if state.parameters != parameters {
|
|
||||||
state.update(|s| {
|
|
||||||
s.parameters = parameters;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user