Demystify map site icon placement calculations

This commit is contained in:
Imbris
2020-11-22 02:31:11 -05:00
committed by Joshua Barretto
parent fe9a634d69
commit 25fcd27108
3 changed files with 78 additions and 67 deletions

View File

@ -6,8 +6,8 @@ rustflags = [
[alias] [alias]
generate = "run --package tools --" generate = "run --package tools --"
test-server = "-Zpackage-features run --bin veloren-server-cli --no-default-features" test-server = "-Zpackage-features run --bin veloren-server-cli --no-default-features"
tracy-server = "-Zunstable-options -Zpackage-features run --bin veloren-server-cli --no-default-features --features tracy --profile dev" tracy-server = "-Zunstable-options -Zpackage-features run --bin veloren-server-cli --no-default-features --features tracy,simd --profile no_overflow"
test-voxygen = "-Zpackage-features run --bin veloren-voxygen --no-default-features --features gl,simd" test-voxygen = "-Zpackage-features run --bin veloren-voxygen --no-default-features --features gl,simd"
tracy-voxygen = "-Zunstable-options -Zpackage-features run --bin veloren-voxygen --no-default-features --features tracy,gl,simd --profile dev" tracy-voxygen = "-Zunstable-options -Zpackage-features run --bin veloren-voxygen --no-default-features --features tracy,gl,simd --profile no_overflow"
server = "run --bin veloren-server-cli" server = "run --bin veloren-server-cli"

View File

@ -230,7 +230,6 @@ impl<'a> Widget for Map<'a> {
.set(state.ids.grid, ui); .set(state.ids.grid, ui);
// Map Image // Map Image
let (world_map, worldsize) = self.world_map; let (world_map, worldsize) = self.world_map;
let worldsize = worldsize.map2(TerrainChunkSize::RECT_SIZE, |e, f| e as f64 * f as f64);
// Coordinates // Coordinates
let player_pos = self let player_pos = self
@ -241,8 +240,8 @@ impl<'a> Widget for Map<'a> {
.get(self.client.entity()) .get(self.client.entity())
.map_or(Vec3::zero(), |pos| pos.0); .map_or(Vec3::zero(), |pos| pos.0);
let max_zoom = (worldsize / TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) let max_zoom = worldsize
.reduce_partial_max()/*.min(f64::MAX)*/; .reduce_partial_max() as f64/*.min(f64::MAX)*/;
let map_size = Vec2::new(760.0, 760.0); let map_size = Vec2::new(760.0, 760.0);
@ -256,13 +255,14 @@ impl<'a> Widget for Map<'a> {
.left() .left()
.map(|drag| Vec2::<f64>::from(drag.delta_xy)) .map(|drag| Vec2::<f64>::from(drag.delta_xy))
.sum(); .sum();
let drag_new = drag + dragged / zoom; // Drag represents offset of view from the player_pos in chunk coords
let drag_new = drag + dragged / map_size / zoom * worldsize.map(|e| e as f64);
events.push(Event::MapDrag(drag_new)); events.push(Event::MapDrag(drag_new));
let rect_src = position::Rect::from_xy_dim( let rect_src = position::Rect::from_xy_dim(
[ [
(player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64) - drag.x, (player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64) - drag.x,
((worldsize.y - player_pos.y as f64) / TerrainChunkSize::RECT_SIZE.y as f64) (worldsize.y as f64 - (player_pos.y as f64 / TerrainChunkSize::RECT_SIZE.y as f64))
+ drag.y, + drag.y,
], ],
[w_src, h_src], [w_src, h_src],
@ -302,13 +302,13 @@ impl<'a> Widget for Map<'a> {
events.push(Event::MapZoom(new_val as f64)); events.push(Event::MapZoom(new_val as f64));
}*/ }*/
// Handle zooming with the mousewheel // Handle zooming with the mousewheel
let zoom_lvl = self.global_state.settings.gameplay.map_zoom;
let scrolled: f64 = ui let scrolled: f64 = ui
.widget_input(state.ids.grid) .widget_input(state.ids.grid)
.scrolls() .scrolls()
.map(|scroll| scroll.y) .map(|scroll| scroll.y)
.sum(); .sum();
let new_zoom_lvl = (zoom_lvl * (1.0 + scrolled * 0.05 * PLATFORM_FACTOR)) let new_zoom_lvl = (self.global_state.settings.gameplay.map_zoom
* (1.0 + scrolled * 0.05 * PLATFORM_FACTOR))
.clamped(1.22, 20.0 /* max_zoom */); .clamped(1.22, 20.0 /* max_zoom */);
events.push(Event::MapZoom(new_zoom_lvl as f64)); events.push(Event::MapZoom(new_zoom_lvl as f64));
// Icon settings // Icon settings
@ -471,13 +471,17 @@ impl<'a> Widget for Map<'a> {
}); });
} }
for (i, site) in self.client.sites().iter().enumerate() { for (i, site) in self.client.sites().iter().enumerate() {
// Site pos in world coordinates relative to the player
let rwpos = site.wpos.map(|e| e as f32) - player_pos; let rwpos = site.wpos.map(|e| e as f32) - player_pos;
let rcpos = // Convert to chunk coordinates
rwpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f32) * zoom as f32 * 3.0 let rcpos = rwpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f32)
/ 4.0; // Add map dragging
let rpos = Vec2::unit_x().rotated_z(0.0) * rcpos.x + drag.map(|e| e as f32);
+ Vec2::unit_y().rotated_z(0.0) * rcpos.y // Convert to fractional coordinates relative to the worldsize
+ drag.map(|e| (e * zoom_lvl) as f32 / 1.67); let rfpos = rcpos.map2(*worldsize, |e, sz| e / sz as f32);
// Convert to relative pixel coordinates from the center of the map
// Accounting for zooming
let rpos = rfpos.map2(map_size, |e, sz| e * sz as f32 * zoom as f32);
if rpos if rpos
.map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0) .map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0)
@ -639,16 +643,27 @@ impl<'a> Widget for Map<'a> {
(e as f64 / sz).clamped(0.0, 1.0) (e as f64 / sz).clamped(0.0, 1.0)
});*/ });*/
//let xy = rel * 760.0; //let xy = rel * 760.0;
let rpos = drag.map(|e| (e * zoom_lvl) as f32 / 2.6);
// Offset from map center due to dragging
let rcpos = drag.map(|e| e as f32);
// Convert to fractional coordinates relative to the worldsize
let rfpos = rcpos.map2(*worldsize, |e, sz| e / sz as f32);
// Convert to relative pixel coordinates from the center of the map
// Accounting for zooming
let rpos = rfpos.map2(map_size, |e, sz| e * sz as f32 * zoom as f32);
// Don't show if outside or near the edge of the map
let arrow_sz = {
let scale = 0.6f64;
Vec2::new(32.0, 37.0) * scale
};
// Hide if icon could go off of the edge of the map
if !rpos if !rpos
.map2(map_size, |e, sz| e > sz as f32 / 1.67) .map2(map_size, |e, sz| {
e.abs() + arrow_sz.map(|e| e as f32 / 2.0).magnitude() > sz as f32 / 2.0
})
.reduce_or() .reduce_or()
{ {
let scale = 0.6;
let arrow_sz = Vec2::new(32.0, 37.0) * scale;
if drag.x == 0.0 && drag.y == 0.0 {
Image::new(self.rot_imgs.indicator_mmap_small.target_north) Image::new(self.rot_imgs.indicator_mmap_small.target_north)
//.top_left_with_margins_on(state.ids.grid, 407.0 - drag.y * zoom_lvl, 417.0 + drag.x * zoom_lvl)
.x_y_position_relative_to( .x_y_position_relative_to(
state.ids.grid, state.ids.grid,
position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.x as f64),
@ -656,11 +671,8 @@ impl<'a> Widget for Map<'a> {
) )
.w_h(arrow_sz.x, arrow_sz.y) .w_h(arrow_sz.x, arrow_sz.y)
.color(Some(UI_HIGHLIGHT_0)) .color(Some(UI_HIGHLIGHT_0))
.floating(true)
//.parent(ui.window)
.set(state.ids.indicator, ui); .set(state.ids.indicator, ui);
} }
}
// Info about controls // Info about controls
let icon_size = Vec2::new(tweak!(25.6), tweak!(28.8)); let icon_size = Vec2::new(tweak!(25.6), tweak!(28.8));
let recenter: bool; let recenter: bool;
@ -699,7 +711,7 @@ impl<'a> Widget for Map<'a> {
.set(state.ids.recenter_button, ui) .set(state.ids.recenter_button, ui)
.was_clicked() .was_clicked()
{ {
events.push(Event::MapDrag(drag_new - drag_new)); events.push(Event::MapDrag(Vec2::zero()));
}; };
Image::new(self.imgs.m_move_ico) Image::new(self.imgs.m_move_ico)

View File

@ -1,6 +1,7 @@
use super::{ use super::{
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
Show, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, Show, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE,
TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
}; };
use crate::ui::{fonts::Fonts, img_ids}; use crate::ui::{fonts::Fonts, img_ids};
use client::{self, Client}; use client::{self, Client};
@ -124,9 +125,8 @@ impl<'a> Widget for MiniMap<'a> {
.mid_top_with_margin_on(state.ids.mmap_frame_2, 18.0 * SCALE) .mid_top_with_margin_on(state.ids.mmap_frame_2, 18.0 * SCALE)
.set(state.ids.mmap_frame_bg, ui); .set(state.ids.mmap_frame_bg, ui);
// Map size // Map size in chunk coords
let (world_map, worldsize) = self.world_map; let (world_map, worldsize) = self.world_map;
let worldsize = worldsize.map2(TerrainChunkSize::RECT_SIZE, |e, f| e as f64 * f as f64);
// Zoom Buttons // Zoom Buttons
@ -136,8 +136,8 @@ impl<'a> Widget for MiniMap<'a> {
// TODO: Either prevent zooming all the way in, *or* see if we can interpolate // TODO: Either prevent zooming all the way in, *or* see if we can interpolate
// somehow if you zoom in too far. Or both. // somehow if you zoom in too far. Or both.
let min_zoom = 1.0; let min_zoom = 1.0;
let max_zoom = (worldsize / TerrainChunkSize::RECT_SIZE.map(|e| e as f64)) let max_zoom = worldsize
.reduce_partial_max()/*.min(f64::MAX)*/; .reduce_partial_max() as f64/*.min(f64::MAX)*/;
// NOTE: Not sure if a button can be clicked while disabled, but we still double // NOTE: Not sure if a button can be clicked while disabled, but we still double
// check for both kinds of zoom to make sure that not only was the // check for both kinds of zoom to make sure that not only was the
@ -200,17 +200,18 @@ impl<'a> Widget for MiniMap<'a> {
let rect_src = position::Rect::from_xy_dim( let rect_src = position::Rect::from_xy_dim(
[ [
player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64, player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64,
(worldsize.y - player_pos.y as f64) / TerrainChunkSize::RECT_SIZE.y as f64, worldsize.y as f64
- (player_pos.y as f64 / TerrainChunkSize::RECT_SIZE.y as f64),
], ],
[w_src, h_src], [w_src, h_src],
); );
let map_size = Vec2::new(170.0, 170.0); let map_size = Vec2::new(170.0 * SCALE, 170.0 * SCALE);
// Map Image // Map Image
Image::new(world_map.source_north) Image::new(world_map.source_north)
.middle_of(state.ids.mmap_frame_bg) .middle_of(state.ids.mmap_frame_bg)
.w_h(map_size.x * SCALE, map_size.y * SCALE) .w_h(map_size.x, map_size.y)
.parent(state.ids.mmap_frame_bg) .parent(state.ids.mmap_frame_bg)
.source_rectangle(rect_src) .source_rectangle(rect_src)
.set(state.ids.grid, ui); .set(state.ids.grid, ui);
@ -221,8 +222,6 @@ impl<'a> Widget for MiniMap<'a> {
.middle_of(state.ids.grid) .middle_of(state.ids.grid)
.w_h(32.0 * ind_scale, 37.0 * ind_scale) .w_h(32.0 * ind_scale, 37.0 * ind_scale)
.color(Some(UI_HIGHLIGHT_0)) .color(Some(UI_HIGHLIGHT_0))
.floating(true)
.parent(ui.window)
.set(state.ids.indicator, ui); .set(state.ids.indicator, ui);
// Map icons // Map icons
@ -243,17 +242,21 @@ impl<'a> Widget for MiniMap<'a> {
}); });
} }
for (i, site) in self.client.sites().iter().enumerate() { for (i, site) in self.client.sites().iter().enumerate() {
// Site pos in world coordinates relative to the player
let rwpos = site.wpos.map(|e| e as f32) - player_pos; let rwpos = site.wpos.map(|e| e as f32) - player_pos;
let rcpos = rwpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f32) // Convert to chunk coordinates
* state.zoom as f32 let rcpos = rwpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f32);
/ 4.0; // Convert to fractional coordinates relative to the worldsize
let rpos = Vec2::unit_x().rotated_z(self.ori.x) * rcpos.x let rfpos = rcpos.map2(*worldsize, |e, sz| e / sz as f32);
+ Vec2::unit_y().rotated_z(self.ori.x) * rcpos.y; // Convert to unrotated pixel coordinates from the player location on the map
// (the center)
// Accounting for zooming
let rpixpos = rfpos.map2(map_size, |e, sz| e * sz as f32 * zoom as f32);
let rpos = Vec2::unit_x().rotated_z(self.ori.x) * rpixpos.x
+ Vec2::unit_y().rotated_z(self.ori.x) * rpixpos.y;
// TODO: Why does this require the magic constant 0.73? This this related to
// scaling issues?
if rpos if rpos
.map2(map_size, |e, sz| e.abs() > sz as f32 / 0.73 / 2.0) .map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0)
.reduce_or() .reduce_or()
{ {
continue; continue;
@ -270,8 +273,7 @@ impl<'a> Widget for MiniMap<'a> {
position::Relative::Scalar(rpos.y as f64), position::Relative::Scalar(rpos.y as f64),
) )
.w_h(20.0, 20.0) .w_h(20.0, 20.0)
.color(Some( .color(Some(match &site.kind {
match &site.kind {
SiteKind::Town => Color::Rgba(1.0, 1.0, 1.0, 0.0), SiteKind::Town => Color::Rgba(1.0, 1.0, 1.0, 0.0),
SiteKind::Castle => Color::Rgba(1.0, 1.0, 1.0, 0.0), SiteKind::Castle => Color::Rgba(1.0, 1.0, 1.0, 0.0),
SiteKind::Dungeon { difficulty } => match difficulty { SiteKind::Dungeon { difficulty } => match difficulty {
@ -283,9 +285,8 @@ impl<'a> Widget for MiniMap<'a> {
5 => QUALITY_DEBUG, 5 => QUALITY_DEBUG,
_ => Color::Rgba(1.0, 1.0, 1.0, 0.0), _ => Color::Rgba(1.0, 1.0, 1.0, 0.0),
}, },
},)) }))
.floating(true) .parent(state.ids.grid)
.parent(ui.window)
.set(state.ids.mmap_site_icons_bgs[i], ui); .set(state.ids.mmap_site_icons_bgs[i], ui);
Image::new(match &site.kind { Image::new(match &site.kind {
SiteKind::Town => self.imgs.mmap_site_town, SiteKind::Town => self.imgs.mmap_site_town,
@ -295,7 +296,6 @@ impl<'a> Widget for MiniMap<'a> {
.middle_of(state.ids.mmap_site_icons_bgs[i]) .middle_of(state.ids.mmap_site_icons_bgs[i])
.w_h(20.0, 20.0) .w_h(20.0, 20.0)
.color(Some(UI_HIGHLIGHT_0)) .color(Some(UI_HIGHLIGHT_0))
.floating(true)
.set(state.ids.mmap_site_icons[i], ui); .set(state.ids.mmap_site_icons[i], ui);
} }
@ -309,9 +309,8 @@ impl<'a> Widget for MiniMap<'a> {
for (dir, id, name, bold) in dirs.iter() { for (dir, id, name, bold) in dirs.iter() {
let cardinal_dir = Vec2::unit_x().rotated_z(self.ori.x as f64) * dir.x let cardinal_dir = Vec2::unit_x().rotated_z(self.ori.x as f64) * dir.x
+ Vec2::unit_y().rotated_z(self.ori.x as f64) * dir.y; + Vec2::unit_y().rotated_z(self.ori.x as f64) * dir.y;
let clamped = (cardinal_dir * 3.0) let clamped = cardinal_dir / cardinal_dir.map(|e| e.abs()).reduce_partial_max();
/ (cardinal_dir * 3.0).map(|e| e.abs()).reduce_partial_max(); let pos = clamped * (map_size / 2.0 - 10.0);
let pos = clamped * (map_size * 0.73 - 10.0);
Text::new(name) Text::new(name)
.x_y_position_relative_to( .x_y_position_relative_to(
state.ids.grid, state.ids.grid,