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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
use core::panic;
use std::io::{BufRead, BufReader, Lines, Read, Write};
use glam::Vec2;
use hashbrown::HashMap;
use crate::{Mesh, MeshError, Polygon, Vertex};
/// A mesh read from a Polyanya file in the formats `mesh 2` or `mesh 3`.
///
/// See <https://github.com/vleue/polyanya/blob/main/meshes/v2/format.txt> for v2 format description, or
/// <https://github.com/vleue/polyanya/blob/main/meshes/v3/format.txt>
#[derive(Debug)]
pub struct PolyanyaFile {
/// List of vertex described in the file
pub vertices: Vec<Vertex>,
/// List of polygon described in the file
pub polygons: Vec<Polygon>,
}
impl PolyanyaFile {
/// Create a `Mesh` from a file in the formats `mesh 2` or `mesh 3`.
///
/// See <https://github.com/vleue/polyanya/blob/main/meshes/v2/format.txt> for v2 format description, or
/// <https://github.com/vleue/polyanya/blob/main/meshes/v3/format.txt>
pub fn from_file(path: &str) -> PolyanyaFile {
let mut file = std::fs::File::open(path).unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
Self::from_bytes(&buffer)
}
/// Create a `Mesh` from bytes in the formats `mesh 2` or `mesh 3`.
///
/// See <https://github.com/vleue/polyanya/blob/main/meshes/v2/format.txt> for v2 format description, or
/// <https://github.com/vleue/polyanya/blob/main/meshes/v3/format.txt>
pub fn from_bytes(bytes: &[u8]) -> PolyanyaFile {
let mut lines = BufReader::new(bytes).lines();
// First check the header.
if let Some(Ok(first)) = lines.next() {
if first != "mesh" {
panic!("Invalid polyanya .mesh file");
}
} else {
panic!("Invalid polyanya .mesh file");
}
let version = if let Some(Ok(version)) = lines.next() {
if version == "2" {
2
} else if version == "3" {
3
} else {
panic!("Invalid polyanya .mesh file");
}
} else {
panic!("Invalid polyanya .mesh file");
};
let (nb_vertices, nb_polygons) = lines
.next()
.unwrap()
.unwrap()
.split_once(' ')
.map(|(a, b)| (a.parse().unwrap(), b.parse().unwrap()))
.unwrap();
let mut mesh = PolyanyaFile {
vertices: Vec::with_capacity(nb_vertices),
polygons: Vec::with_capacity(nb_polygons),
};
// Parse the remainder of the file depending on what `.mesh` version it is.
match version {
2 => parse_v2(&mut mesh, lines, nb_vertices, nb_polygons),
3 => parse_v3(&mut mesh, lines, nb_vertices, nb_polygons),
_ => panic!("Invalid polyanya mesh version"),
}
mesh
}
/// Write a `Mesh` to a file in the format `mesh 2`.
///
/// See <https://github.com/vleue/polyanya/blob/main/meshes/format.txt> for format description.
pub fn to_file(&self, path: &str) {
let mut file = std::fs::File::create(path).unwrap();
let bytes = self.to_bytes();
file.write_all(&bytes).unwrap();
}
/// Write a `Mesh` to bytes in the format `mesh 2`.
///
/// See <https://github.com/vleue/polyanya/blob/main/meshes/format.txt> for format description.
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(b"mesh\n");
bytes.extend_from_slice(b"2\n");
bytes.extend_from_slice(
format!("{} {}\n", self.vertices.len(), self.polygons.len()).as_bytes(),
);
for vertex in &self.vertices {
bytes.extend_from_slice(
format!(
"{:.06} {:.06} {} {}\n",
vertex.coords.x,
vertex.coords.y,
vertex.polygons.len(),
vertex
.polygons
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(" ")
)
.as_bytes(),
);
}
for polygon in &self.polygons {
bytes.extend_from_slice(
format!(
"{} {}\n",
polygon.vertices.len(),
polygon
.vertices
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" ")
)
.as_bytes(),
);
}
bytes
}
}
impl TryFrom<PolyanyaFile> for Mesh {
type Error = MeshError;
fn try_from(value: PolyanyaFile) -> Result<Self, Self::Error> {
Mesh::new(value.vertices, value.polygons)
}
}
impl From<Mesh> for PolyanyaFile {
fn from(mut mesh: Mesh) -> Self {
let last_layer = mesh.layers.pop().unwrap();
PolyanyaFile {
vertices: last_layer.vertices,
polygons: last_layer.polygons,
}
}
}
/// Create a `PolyanyaFile` from the v2 mesh format
fn parse_v2(
mesh: &mut PolyanyaFile,
lines: Lines<BufReader<&[u8]>>,
mut nb_vertices: usize,
mut nb_polygons: usize,
) {
let mut phase = 1;
for line in lines {
let line: String = line.unwrap();
if phase == 1 {
if nb_vertices > 0 {
nb_vertices -= 1;
let mut values = line.split_whitespace();
let x = values.next().unwrap().parse().unwrap();
let y = values.next().unwrap().parse().unwrap();
let _ = values.next();
let vertex = Vertex::new(
Vec2::new(x, y),
values.map(|v| v.parse().unwrap_or(u32::MAX)).collect(),
);
mesh.vertices.push(vertex);
} else {
phase = 2;
}
}
if phase == 2 {
if nb_polygons > 0 {
nb_polygons -= 1;
let mut values = line.split_whitespace();
let n = values.next().unwrap().parse().unwrap();
let polygon = Polygon::using(n, values.map(|v| v.parse().unwrap()).collect());
mesh.polygons.push(polygon)
} else {
panic!("Failed to parse v2 mesh format, unexpected line.");
}
}
}
}
/// Create a `PolyanyaFile` from the v3 mesh format
fn parse_v3(
mesh: &mut PolyanyaFile,
lines: Lines<BufReader<&[u8]>>,
mut nb_vertices: usize,
mut nb_polygons: usize,
) {
let mut phase = 1;
// These are 1-indexed in the v3 mesh file format.
let mut vertices = Vec::with_capacity(nb_vertices);
let mut vertex_polys: HashMap<usize, Vec<u32>> = HashMap::with_capacity(nb_vertices);
let mut polygon_index = 0;
for line in lines {
let line: String = line.unwrap();
if line.is_empty() {
continue;
}
if phase == 1 {
if nb_vertices > 0 {
nb_vertices -= 1;
let mut values = line.split_whitespace();
let x = values.next().unwrap().parse().unwrap();
let y = values.next().unwrap().parse().unwrap();
vertices.push(Vec2::new(x, y));
} else {
phase = 2;
}
}
if phase == 2 {
if nb_polygons > 0 {
nb_polygons -= 1;
let mut values = line.split_whitespace();
let is_traversable = match values.next().unwrap() {
"0" => false,
"1" => true,
_ => panic!("Invalid v3 mesh format."),
};
let num_edges: usize = values.next().unwrap().parse().unwrap();
let mut vertex_indices = Vec::with_capacity(num_edges);
// We still need to add the non-traversable polygons to the vertices.
let data = values
.enumerate()
.map(|(i, v)| {
let num: isize = v.parse().unwrap();
if i < num_edges {
let vertex_neighbours = vertex_polys.entry(num as usize).or_default();
vertex_neighbours.push(match is_traversable {
true => polygon_index,
false => u32::MAX,
});
vertex_indices.push(num as usize);
} else {
// Num here is the polygon index.
if num <= -1 {
// We don't care about the specific impassable polygons, just that they're impassable.
return -1;
} else if num == 0 {
// In the mesh-v3 format 0 means that it's the edge of the map and there are no polygons.
vertex_polys
.entry(vertex_indices[i - num_edges])
.or_insert(Vec::new())
.push(u32::MAX);
}
}
// Subtract 1 here since it's 1 indexed, and we're using 0-indexed vecs.
num - 1
})
.collect();
if is_traversable {
// Only increment the polygon index if the polygon is traversable since we don't
// care about the non-traversable polygons.
polygon_index += 1;
let polygon = Polygon::using(num_edges, data);
mesh.polygons.push(polygon)
}
} else {
panic!("Failed to parse v3 mesh format, unexpected line.");
}
}
}
// Add the vertices to the mesh. We do this after parsing since the adjacent polygons for vertices are
// calculated from the polygons associated with each vertex.
// TODO: Do we need to de-duplicate adjacent impassable polygons for the vertices?
for (i, coordinates) in vertices.into_iter().enumerate() {
let vert = Vertex::new(coordinates, vertex_polys.remove(&(i + 1)).unwrap());
mesh.vertices.push(vert);
}
}