1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
use glam::{Vec2, Vec3};
/// A coordinate system used to convert from a user-facing coordinate system
/// into landmass's standard coordinate system. The standard coordinate system
/// is [`crate::coords::XYZ`].
pub trait CoordinateSystem {
/// The user-facing coordinate type.
type Coordinate: Clone;
/// The type to use for point sampling options.
type SampleDistance: PointSampleDistance;
/// Whether to flip polygons after conversion.
///
/// Landmass coordinates expect polygons are in CCW order. However, your
/// coordinate system could flip one of the axes, such that CCW polygons in
/// your coordinate system become CW polygons in landmass coordinates.
const FLIP_POLYGONS: bool;
/// Converts a coordinate in this system to the standard coordinate system.
fn to_landmass(v: &Self::Coordinate) -> Vec3;
/// Converts a standard coordinate into this system's coordinate.
fn from_landmass(v: &Vec3) -> Self::Coordinate;
}
/// A trait to create a default instance based on an agent's radius.
///
/// This is an easy starting point when using struct-update-syntax.
pub trait FromAgentRadius {
/// Creates an instance given the agent's radius.
fn from_agent_radius(radius: f32) -> Self;
}
/// A configuration of how a type relates to distances when sampling points. See
/// [`crate::Archipelago::sample_point`] for more.
pub trait PointSampleDistance {
/// The horizontal distance that a node may be sampled. If a sample point is
/// further than this distance away horizontally, it will be ignored.
fn horizontal_distance(&self) -> f32;
/// The vertical distance above the query point that a node may be sampled.
///
/// If a sample point is further above than this distance, it will be ignored.
/// For 2D coordinate systems, this value is not really used, but a value of
/// 1.0 is good to avoid floating point errors. This value must be greater
/// than negative [`PointSampleDistance::distance_below`] (and preferably
/// positive).
fn distance_above(&self) -> f32;
/// The vertical distance below the query point that a node may be sampled.
///
/// If a sample point is further below than this distance, it will be ignored.
/// For 2D coordinate systems, this value is not really used, but a value of
/// 1.0 is good to avoid floating point errors. This value must be less than
/// than negative [`PointSampleDistance::distance_above`] (and preferably
/// positive).
fn distance_below(&self) -> f32;
/// The ratio between the vertical and the horizontal distances to prefer. For
/// example, if this value is 2.0, then a sample point directly below the
/// query point 1.9 units away will be selected over a sample point 1.0 unit
/// away horizontally. This value must be positive. For 2D coordinate systems,
/// the value is irrelevant (so 1.0 is preferred).
fn vertical_preference_ratio(&self) -> f32;
/// The max vertical distance that animation links can be from the nav mesh to
/// count as a valid connection.
///
/// Animation links farther than this distance from part of the nav mesh will
/// not connect. Note setting this too high can result in links being usable
/// from the floor above or below.
fn animation_link_max_vertical_distance(&self) -> f32;
}
/// The standard coordinate system, where X points right, Y points forward, and
/// Z points up.
pub struct XYZ;
impl CoordinateSystem for XYZ {
type Coordinate = Vec3;
type SampleDistance = PointSampleDistance3d;
const FLIP_POLYGONS: bool = false;
fn to_landmass(v: &Self::Coordinate) -> Vec3 {
*v
}
fn from_landmass(v: &Vec3) -> Self::Coordinate {
*v
}
}
/// A [`PointSampleDistance`] type for 3D coordinate systems.
#[derive(Debug, PartialEq, Clone)]
pub struct PointSampleDistance3d {
/// The horizontal distance that a node may be sampled. If a sample point is
/// further than this distance away horizontally, it will be ignored.
pub horizontal_distance: f32,
/// The vertical distance above the query point that a node may be sampled.
///
/// If a sample point is further above than this distance, it will be
/// ignored. This value must be greater than negated
/// [`Self::distance_below`].
pub distance_above: f32,
/// The vertical distance below the query point that a node may be sampled.
///
/// If a sample point is further below than this distance, it will be
/// ignored. This value must be greater than negated
/// [`Self::distance_above`].
pub distance_below: f32,
/// The ratio between the vertical and the horizontal distances to prefer.
/// For example, if this value is 2.0, then a sample point directly below
/// the query point 1.9 units away will be selected over a sample point 1.0
/// unit away horizontally. This value must be positive.
pub vertical_preference_ratio: f32,
/// The max vertical distance that animation links can be from the nav mesh
/// to count as a valid connection.
///
/// Animation links farther than this distance from part of the nav mesh will
/// not connect. Note setting this too high can result in links being usable
/// from the floor above or below.
pub animation_link_max_vertical_distance: f32,
}
impl PointSampleDistance for PointSampleDistance3d {
fn horizontal_distance(&self) -> f32 {
self.horizontal_distance
}
fn distance_above(&self) -> f32 {
self.distance_above
}
fn distance_below(&self) -> f32 {
self.distance_below
}
fn vertical_preference_ratio(&self) -> f32 {
self.vertical_preference_ratio
}
fn animation_link_max_vertical_distance(&self) -> f32 {
self.animation_link_max_vertical_distance
}
}
impl FromAgentRadius for PointSampleDistance3d {
fn from_agent_radius(radius: f32) -> Self {
Self {
horizontal_distance: 0.2 * radius,
distance_above: 0.5 * radius,
distance_below: radius,
vertical_preference_ratio: 2.0,
animation_link_max_vertical_distance: 0.5 * radius,
}
}
}
/// A 2D coordinate system, where X points right, and Y points forward.
pub struct XY;
impl CoordinateSystem for XY {
type Coordinate = Vec2;
type SampleDistance = f32;
const FLIP_POLYGONS: bool = false;
fn to_landmass(v: &Self::Coordinate) -> Vec3 {
Vec3::new(v.x, v.y, 0.0)
}
fn from_landmass(v: &Vec3) -> Self::Coordinate {
Vec2::new(v.x, v.y)
}
}
impl PointSampleDistance for f32 {
fn horizontal_distance(&self) -> f32 {
*self
}
fn distance_above(&self) -> f32 {
1.0
}
fn distance_below(&self) -> f32 {
1.0
}
fn vertical_preference_ratio(&self) -> f32 {
1.0
}
fn animation_link_max_vertical_distance(&self) -> f32 {
1.0
}
}
impl FromAgentRadius for f32 {
fn from_agent_radius(radius: f32) -> Self {
0.2 * radius
}
}
/// An evaluated version of the point sample options.
///
/// This mirrors [`PointSampleDistance`].
pub(crate) struct CorePointSampleDistance {
pub(crate) horizontal_distance: f32,
pub(crate) distance_above: f32,
pub(crate) distance_below: f32,
pub(crate) vertical_preference_ratio: f32,
}
impl CorePointSampleDistance {
pub(crate) fn new<T: PointSampleDistance>(point_sample_distance: &T) -> Self {
Self {
horizontal_distance: point_sample_distance.horizontal_distance(),
distance_above: point_sample_distance.distance_above(),
distance_below: point_sample_distance.distance_below(),
vertical_preference_ratio: point_sample_distance
.vertical_preference_ratio(),
}
}
}