veloren_common_net/msg/
world_msg.rs

1use common::{grid::Grid, map::Marker, terrain::TerrainChunk, trade::Good};
2use serde::{Deserialize, Serialize};
3use std::{collections::HashMap, sync::Arc};
4use vek::*;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7/// World map information.  Note that currently, we always send the whole thing
8/// in one go, but the structure aims to try to provide information as locally
9/// as possible, so that in the future we can split up large maps into multiple
10/// WorldMapMsg fragments.
11///
12/// TODO: Update message format to make fragmentable, allowing us to send more
13/// information without running into bandwidth issues.
14///
15/// TODO: Add information for rivers (currently, we just prerender them on the
16/// server, but this is not a great solution for LoD.  The map rendering code is
17/// already set up to be able to take advantage of the river rendering being
18/// split out, but the format is a little complicated for space reasons and it
19/// may take some tweaking to get right, so we avoid sending it for now).
20///
21/// TODO: measure explicit compression schemes that might save space, e.g.
22/// repeating the "small angles" optimization that works well on more detailed
23/// shadow maps intended for height maps.
24pub struct WorldMapMsg {
25    /// Log base 2 of world map dimensions (width × height) in chunks.
26    ///
27    /// NOTE: Invariant: chunk count fits in a u16.
28    pub dimensions_lg: Vec2<u32>,
29    /// Max height (used to scale altitudes).
30    pub max_height: f32,
31    /// RGB+A; the alpha channel is currently unused, but will be used in the
32    /// future. Entries are in the usual chunk order.
33    pub rgba: Grid<u32>,
34    /// Altitudes: bits 2 to 0 are unused, then bits 15 to 3 are used for
35    /// altitude. The remainder are currently unused, but we have plans to
36    /// use 7 bits for water depth (using an integer f7 encoding), and we
37    /// will find other uses for the remaining 12 bits.
38    pub alt: Grid<u32>,
39    /// Horizon mapping. This is a variant of shadow mapping that is
40    /// specifically designed for height maps; it takes advantage of their
41    /// regular structure (e.g. no holes) to compress all information needed
42    /// to decide when to cast a sharp shadow into a single nagle, the "horizon
43    /// angle." This is the smallest angle with the ground at which light can
44    /// pass through any occluders to reach the chunk, in some chosen
45    /// horizontal direction. This would not be sufficient for a more
46    /// complicated 3D structure, but it works for height maps since:
47    ///
48    /// 1. they have no gaps, so as soon as light can shine through it will
49    ///    always be able to do so, and
50    /// 2. we only care about lighting from the top, and only from the east and
51    ///    west (since at a large scale like this we mostly just want to handle
52    ///    variable sunlight; moonlight would present more challenges but we
53    ///    currently have no plans to try to cast accurate shadows in
54    ///    moonlight).
55    ///
56    /// Our chosen format is two pairs of vectors,
57    /// with the first pair representing west-facing light (casting shadows on
58    /// the left side)  and the second representing east-facing light
59    /// (casting shadows on the east side).
60    ///
61    /// The pair of vectors consists of (with each vector in the usual chunk
62    /// order):
63    ///
64    /// * Horizon angle pointing east (1 byte, scaled so 1 unit = 255° / 360).
65    ///   We might consider switching to tangent if that represents the
66    ///   information we care about better.
67    /// * Approximate (floor) height of maximal occluder. We currently use this
68    ///   to try to deliver some approximation of soft shadows, which isn't that
69    ///   big a deal on the world map but is probably needed in order to ensure
70    ///   smooth transitions between chunks in LoD view. Additionally, when we
71    ///   start using the shadow information to do local lighting on the world
72    ///   map, we'll want a quick way to test where we can go out of shadow at
73    ///   arbitrary heights (since the player and other entities cajn find
74    ///   themselves far from the ground at times). While this is only an
75    ///   approximation to a proper distance map, hopefully it will give us
76    ///   something  that feels reasonable enough for Veloren's style.
77    ///
78    /// NOTE: On compression.
79    ///
80    /// Horizon mapping has a lot of advantages for height maps (simple, easy to
81    /// understand, doesn't require any fancy math or approximation beyond
82    /// precision loss), though it loses a few of them by having to store
83    /// distance to occluder as well. However, just storing tons
84    /// and tons of regular shadow maps (153 for a full day cycle, stored at
85    /// irregular intervals) combined with clever explicit compression and
86    /// avoiding recording sharp local shadows (preferring retracing for
87    /// these), yielded a compression rate of under 3 bits per column! Since
88    /// we likely want to avoid per-column shadows for worlds of the sizes we
89    /// want, we'd still need to store *some* extra information to create
90    /// soft shadows, but it would still be nice to try to drive down our
91    /// size as much as possible given how compressible shadows of height
92    /// maps seem to be in practice. Therefore, we try to take advantage of the
93    /// way existing compression algorithms tend to work to see if we can
94    /// achieve significant gains without doing a lot of custom work.
95    ///
96    /// Specifically, since our rays are cast east/west, we expect that for each
97    /// row, the horizon angles in each direction should be sequences of
98    /// monotonically increasing values (as chunks approach a tall
99    /// occluder), followed by sequences of no shadow, repeated
100    /// until the end of the map. Monotonic sequences and same-byte sequences
101    /// are usually easy to compress and existing algorithms are more likely
102    /// to be able to deal with them than jumbled data.  If we were to keep
103    /// both directions in the same vector, off-the-shelf compression would
104    /// probably be less effective.
105    ///
106    /// For related reasons, rather than storing distances as in a standard
107    /// distance map (which would lead to monotonically *decreasing* values
108    /// as we approached the occluder from a given direction), we store the
109    /// estimated *occluder height.* The idea here is that we replace the
110    /// monotonic sequences with constant sequences, which are extremely
111    /// straightforward to compress and mostly handled automatically by anything
112    /// that does run-length encoding (i.e. most off-the-shelf compression
113    /// algorithms).
114    ///
115    /// We still need to benchmark this properly, as there's no guarantee our
116    /// current compression algorithms will actually work well on this data
117    /// in practice. It's possible that some other permutation (e.g. more
118    /// bits reserved for "distance to occluder" in exchange for an even
119    /// more predictible sequence) would end up compressing better than storing
120    /// angles, or that we don't need as much precision as we currently have
121    /// (256 possible angles).
122    pub horizons: [(Vec<u8>, Vec<u8>); 2],
123    pub sites: Vec<Marker>,
124    pub possible_starting_sites: Vec<SiteId>,
125    pub pois: Vec<PoiInfo>,
126    /// Default chunk (representing the ocean outside the map bounds).  Sea
127    /// level (used to provide a base altitude) is the lower bound of this
128    /// chunk.
129    pub default_chunk: Arc<TerrainChunk>,
130}
131
132pub type SiteId = common::trade::SiteId;
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct EconomyInfo {
136    pub id: SiteId,
137    pub population: u32,
138    pub stock: HashMap<Good, f32>,
139    pub labor_values: HashMap<Good, f32>,
140    pub values: HashMap<Good, f32>,
141    pub labors: Vec<f32>,
142    pub last_exports: HashMap<Good, f32>,
143    pub resources: HashMap<Good, f32>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct PoiInfo {
148    pub kind: PoiKind,
149    pub wpos: Vec2<i32>,
150    pub name: String,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
154#[repr(u8)]
155pub enum PoiKind {
156    Peak(u32),
157    Lake(u32),
158}