1use std::io::BufRead;
2
3use bytemuck::Zeroable;
4
5use crate::{Gaussian, IterGaussian, ReadIterGaussian, WriteIterGaussian};
6
7#[repr(C)]
12#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
13pub struct PlyGaussianPod {
14 pub pos: [f32; 3],
15 pub normal: [f32; 3],
16 pub color: [f32; 3],
17 pub sh: [f32; 3 * 15],
18 pub alpha: f32,
19 pub scale: [f32; 3],
20 pub rot: [f32; 4],
21}
22
23impl PlyGaussianPod {
24 pub fn set_value(&mut self, name: &str, value: f32) {
26 macro_rules! set_prop {
27 ($name:expr, $field:expr) => {
28 $field = value
29 };
30 }
31
32 match name {
33 "x" => set_prop!("x", self.pos[0]),
34 "y" => set_prop!("y", self.pos[1]),
35 "z" => set_prop!("z", self.pos[2]),
36 "nx" => set_prop!("nx", self.normal[0]),
37 "ny" => set_prop!("ny", self.normal[1]),
38 "nz" => set_prop!("nz", self.normal[2]),
39 "f_dc_0" => set_prop!("f_dc_0", self.color[0]),
40 "f_dc_1" => set_prop!("f_dc_1", self.color[1]),
41 "f_dc_2" => set_prop!("f_dc_2", self.color[2]),
42 "f_rest_0" => set_prop!("f_rest_0", self.sh[0]),
43 "f_rest_1" => set_prop!("f_rest_1", self.sh[1]),
44 "f_rest_2" => set_prop!("f_rest_2", self.sh[2]),
45 "f_rest_3" => set_prop!("f_rest_3", self.sh[3]),
46 "f_rest_4" => set_prop!("f_rest_4", self.sh[4]),
47 "f_rest_5" => set_prop!("f_rest_5", self.sh[5]),
48 "f_rest_6" => set_prop!("f_rest_6", self.sh[6]),
49 "f_rest_7" => set_prop!("f_rest_7", self.sh[7]),
50 "f_rest_8" => set_prop!("f_rest_8", self.sh[8]),
51 "f_rest_9" => set_prop!("f_rest_9", self.sh[9]),
52 "f_rest_10" => set_prop!("f_rest_10", self.sh[10]),
53 "f_rest_11" => set_prop!("f_rest_11", self.sh[11]),
54 "f_rest_12" => set_prop!("f_rest_12", self.sh[12]),
55 "f_rest_13" => set_prop!("f_rest_13", self.sh[13]),
56 "f_rest_14" => set_prop!("f_rest_14", self.sh[14]),
57 "f_rest_15" => set_prop!("f_rest_15", self.sh[15]),
58 "f_rest_16" => set_prop!("f_rest_16", self.sh[16]),
59 "f_rest_17" => set_prop!("f_rest_17", self.sh[17]),
60 "f_rest_18" => set_prop!("f_rest_18", self.sh[18]),
61 "f_rest_19" => set_prop!("f_rest_19", self.sh[19]),
62 "f_rest_20" => set_prop!("f_rest_20", self.sh[20]),
63 "f_rest_21" => set_prop!("f_rest_21", self.sh[21]),
64 "f_rest_22" => set_prop!("f_rest_22", self.sh[22]),
65 "f_rest_23" => set_prop!("f_rest_23", self.sh[23]),
66 "f_rest_24" => set_prop!("f_rest_24", self.sh[24]),
67 "f_rest_25" => set_prop!("f_rest_25", self.sh[25]),
68 "f_rest_26" => set_prop!("f_rest_26", self.sh[26]),
69 "f_rest_27" => set_prop!("f_rest_27", self.sh[27]),
70 "f_rest_28" => set_prop!("f_rest_28", self.sh[28]),
71 "f_rest_29" => set_prop!("f_rest_29", self.sh[29]),
72 "f_rest_30" => set_prop!("f_rest_30", self.sh[30]),
73 "f_rest_31" => set_prop!("f_rest_31", self.sh[31]),
74 "f_rest_32" => set_prop!("f_rest_32", self.sh[32]),
75 "f_rest_33" => set_prop!("f_rest_33", self.sh[33]),
76 "f_rest_34" => set_prop!("f_rest_34", self.sh[34]),
77 "f_rest_35" => set_prop!("f_rest_35", self.sh[35]),
78 "f_rest_36" => set_prop!("f_rest_36", self.sh[36]),
79 "f_rest_37" => set_prop!("f_rest_37", self.sh[37]),
80 "f_rest_38" => set_prop!("f_rest_38", self.sh[38]),
81 "f_rest_39" => set_prop!("f_rest_39", self.sh[39]),
82 "f_rest_40" => set_prop!("f_rest_40", self.sh[40]),
83 "f_rest_41" => set_prop!("f_rest_41", self.sh[41]),
84 "f_rest_42" => set_prop!("f_rest_42", self.sh[42]),
85 "f_rest_43" => set_prop!("f_rest_43", self.sh[43]),
86 "f_rest_44" => set_prop!("f_rest_44", self.sh[44]),
87 "opacity" => set_prop!("opacity", self.alpha),
88 "scale_0" => set_prop!("scale_0", self.scale[0]),
89 "scale_1" => set_prop!("scale_1", self.scale[1]),
90 "scale_2" => set_prop!("scale_2", self.scale[2]),
91 "rot_0" => set_prop!("rot_0", self.rot[0]),
92 "rot_1" => set_prop!("rot_1", self.rot[1]),
93 "rot_2" => set_prop!("rot_2", self.rot[2]),
94 "rot_3" => set_prop!("rot_3", self.rot[3]),
95 _ => {
96 log::warn!("Unknown property: {name}");
97 }
98 }
99 }
100}
101
102impl ply_rs::ply::PropertyAccess for PlyGaussianPod {
103 fn new() -> Self {
104 PlyGaussianPod::zeroed()
105 }
106
107 fn set_property(&mut self, property_name: String, property: ply_rs::ply::Property) {
108 let ply_rs::ply::Property::Float(value) = property else {
109 log::error!("Property {property_name} is not a float");
110 return;
111 };
112
113 self.set_value(&property_name, value);
114 }
115}
116
117impl From<Gaussian> for PlyGaussianPod {
118 fn from(gaussian: Gaussian) -> Self {
119 gaussian.to_ply()
120 }
121}
122
123impl From<&Gaussian> for PlyGaussianPod {
124 fn from(gaussian: &Gaussian) -> Self {
125 gaussian.to_ply()
126 }
127}
128
129#[derive(Debug, Clone)]
133pub enum PlyHeader {
134 Inria(usize),
140
141 Custom(ply_rs::ply::Header),
143}
144
145impl PlyHeader {
146 pub fn count(&self) -> Option<usize> {
150 match self {
151 Self::Inria(count) => Some(*count),
152 Self::Custom(header) => header.elements.get("vertex").map(|vertex| vertex.count),
153 }
154 }
155}
156
157pub enum PlyGaussianIter<
159 I: Iterator<Item = Result<PlyGaussianPod, std::io::Error>>,
160 C: Iterator<Item = Result<PlyGaussianPod, std::io::Error>>,
161> {
162 Inria(I),
164
165 Custom(C),
169}
170
171impl<
172 I: Iterator<Item = Result<PlyGaussianPod, std::io::Error>>,
173 C: Iterator<Item = Result<PlyGaussianPod, std::io::Error>>,
174> Iterator for PlyGaussianIter<I, C>
175{
176 type Item = Result<PlyGaussianPod, std::io::Error>;
177
178 fn next(&mut self) -> Option<Self::Item> {
179 match self {
180 Self::Inria(iter) => iter.next(),
181 Self::Custom(iter) => iter.next(),
182 }
183 }
184}
185
186fn vertex_element_not_found_error() -> std::io::Error {
187 std::io::Error::new(
188 std::io::ErrorKind::InvalidData,
189 "Gaussian vertex element not found in PLY header",
190 )
191}
192
193#[derive(Debug, Clone, PartialEq)]
200pub struct PlyGaussians(pub Vec<PlyGaussianPod>);
201
202impl PlyGaussians {
203 pub const PLY_PROPERTIES: &[&str] = &[
205 "x",
206 "y",
207 "z",
208 "nx",
209 "ny",
210 "nz",
211 "f_dc_0",
212 "f_dc_1",
213 "f_dc_2",
214 "f_rest_0",
215 "f_rest_1",
216 "f_rest_2",
217 "f_rest_3",
218 "f_rest_4",
219 "f_rest_5",
220 "f_rest_6",
221 "f_rest_7",
222 "f_rest_8",
223 "f_rest_9",
224 "f_rest_10",
225 "f_rest_11",
226 "f_rest_12",
227 "f_rest_13",
228 "f_rest_14",
229 "f_rest_15",
230 "f_rest_16",
231 "f_rest_17",
232 "f_rest_18",
233 "f_rest_19",
234 "f_rest_20",
235 "f_rest_21",
236 "f_rest_22",
237 "f_rest_23",
238 "f_rest_24",
239 "f_rest_25",
240 "f_rest_26",
241 "f_rest_27",
242 "f_rest_28",
243 "f_rest_29",
244 "f_rest_30",
245 "f_rest_31",
246 "f_rest_32",
247 "f_rest_33",
248 "f_rest_34",
249 "f_rest_35",
250 "f_rest_36",
251 "f_rest_37",
252 "f_rest_38",
253 "f_rest_39",
254 "f_rest_40",
255 "f_rest_41",
256 "f_rest_42",
257 "f_rest_43",
258 "f_rest_44",
259 "opacity",
260 "scale_0",
261 "scale_1",
262 "scale_2",
263 "rot_0",
264 "rot_1",
265 "rot_2",
266 "rot_3",
267 ];
268
269 pub fn len(&self) -> usize {
271 self.0.len()
272 }
273
274 pub fn is_empty(&self) -> bool {
276 self.0.is_empty()
277 }
278
279 pub fn iter(&self) -> impl ExactSizeIterator<Item = &PlyGaussianPod> {
281 self.0.iter()
282 }
283
284 pub fn iter_mut(&mut self) -> impl ExactSizeIterator<Item = &mut PlyGaussianPod> {
286 self.0.iter_mut()
287 }
288
289 pub fn read_header(reader: &mut impl BufRead) -> Result<PlyHeader, std::io::Error> {
293 let parser = ply_rs::parser::Parser::<ply_rs::ply::DefaultElement>::new();
294 let header = parser.read_header(reader)?;
295 let vertex = header
296 .elements
297 .get("vertex")
298 .ok_or_else(vertex_element_not_found_error)?;
299
300 const SYSTEM_ENDIANNESS: ply_rs::ply::Encoding = match cfg!(target_endian = "little") {
301 true => ply_rs::ply::Encoding::BinaryLittleEndian,
302 false => ply_rs::ply::Encoding::BinaryBigEndian,
303 };
304
305 let ply_header = match vertex
306 .properties
307 .iter()
308 .zip(Self::PLY_PROPERTIES.iter())
309 .all(|((a, property), b)| {
310 a == *b
311 && property.data_type
312 == ply_rs::ply::PropertyType::Scalar(ply_rs::ply::ScalarType::Float)
313 })
314 && header.encoding == SYSTEM_ENDIANNESS
315 {
316 true => PlyHeader::Inria(vertex.count),
317 false => PlyHeader::Custom(header),
318 };
319
320 Ok(ply_header)
321 }
322
323 pub fn read_gaussians(
327 reader: &mut impl BufRead,
328 header: PlyHeader,
329 ) -> Result<impl Iterator<Item = Result<PlyGaussianPod, std::io::Error>>, std::io::Error> {
330 let count = header.count().ok_or_else(vertex_element_not_found_error)?;
331 log::info!("Reading PLY format with {count} Gaussians");
332
333 Ok(match header {
334 PlyHeader::Inria(..) => PlyGaussianIter::Inria((0..count).map(|_| {
335 let mut gaussian = PlyGaussianPod::zeroed();
336 reader.read_exact(bytemuck::bytes_of_mut(&mut gaussian))?;
337 Ok(gaussian)
338 })),
339 PlyHeader::Custom(header) => {
340 let parser = ply_rs::parser::Parser::<PlyGaussianPod>::new();
341
342 PlyGaussianIter::Custom((0..count).map(move |_| {
343 let vertex = header.elements.get("vertex").ok_or(std::io::Error::new(
344 std::io::ErrorKind::InvalidData,
345 "Gaussian vertex element not found in PLY",
346 ))?;
347 Ok(match header.encoding {
348 ply_rs::ply::Encoding::Ascii => {
349 let mut line = String::new();
350 reader.read_line(&mut line)?;
351
352 let mut gaussian = PlyGaussianPod::zeroed();
353 vertex
354 .properties
355 .keys()
356 .zip(
357 line.split(' ')
358 .map(|s| Some(s.trim().parse::<f32>()))
359 .chain(std::iter::repeat(None)),
360 )
361 .try_for_each(|(name, value)| match value {
362 Some(Ok(value)) => {
363 gaussian.set_value(name, value);
364 Ok(())
365 }
366 Some(Err(_)) | None => Err(std::io::Error::new(
367 std::io::ErrorKind::InvalidData,
368 "Gaussian element property invalid or missing in PLY",
369 )),
370 })?;
371
372 gaussian
373 }
374 ply_rs::ply::Encoding::BinaryLittleEndian => {
375 parser.read_little_endian_element(reader, vertex)?
376 }
377 ply_rs::ply::Encoding::BinaryBigEndian => {
378 parser.read_big_endian_element(reader, vertex)?
379 }
380 })
381 }))
382 }
383 })
384 }
385}
386
387impl IterGaussian for PlyGaussians {
388 fn iter_gaussian(&self) -> impl ExactSizeIterator<Item = Gaussian> + '_ {
389 self.iter().map(Gaussian::from_ply)
390 }
391}
392
393impl ReadIterGaussian for PlyGaussians {
394 fn read_from(reader: &mut impl BufRead) -> std::io::Result<Self> {
395 let ply_header = Self::read_header(reader)?;
396
397 let count = ply_header
398 .count()
399 .ok_or_else(vertex_element_not_found_error)?;
400 let mut gaussians = Vec::with_capacity(count);
401
402 for gaussian in Self::read_gaussians(reader, ply_header)? {
403 gaussians.push(gaussian?);
404 }
405
406 Ok(Self(gaussians))
407 }
408}
409
410impl WriteIterGaussian for PlyGaussians {
411 fn write_to(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
412 const SYSTEM_ENDIANNESS: ply_rs::ply::Encoding = match cfg!(target_endian = "little") {
413 true => ply_rs::ply::Encoding::BinaryLittleEndian,
414 false => ply_rs::ply::Encoding::BinaryBigEndian,
415 };
416
417 writeln!(writer, "ply")?;
418 writeln!(writer, "format {SYSTEM_ENDIANNESS} 1.0")?;
419 writeln!(writer, "element vertex {}", self.0.len())?;
420 for property in Self::PLY_PROPERTIES {
421 writeln!(writer, "property float {property}")?;
422 }
423 writeln!(writer, "end_header")?;
424
425 self.0
426 .iter()
427 .try_for_each(|gaussian| writer.write_all(bytemuck::bytes_of(gaussian)))?;
428
429 Ok(())
430 }
431}
432
433impl From<Vec<PlyGaussianPod>> for PlyGaussians {
434 fn from(gaussians: Vec<PlyGaussianPod>) -> Self {
435 Self(gaussians)
436 }
437}
438
439impl<G: AsRef<Gaussian>> FromIterator<G> for PlyGaussians {
440 fn from_iter<T: IntoIterator<Item = G>>(iter: T) -> Self {
441 Self(iter.into_iter().map(|g| g.as_ref().to_ply()).collect())
442 }
443}
444
445impl FromIterator<PlyGaussianPod> for PlyGaussians {
446 fn from_iter<T: IntoIterator<Item = PlyGaussianPod>>(iter: T) -> Self {
447 Self(iter.into_iter().collect())
448 }
449}