wgpu_3dgs_core/source_format/
ply.rs

1use std::io::BufRead;
2
3use bytemuck::Zeroable;
4
5use crate::{Gaussian, IterGaussian, ReadIterGaussian, WriteIterGaussian};
6
7/// The POD representation of Gaussian in PLY format.
8///
9/// Fields are stored as arrays because using glam types would add padding
10/// according to C alignment rules.
11#[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    /// Set the value of a property by name.
25    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/// Header of PLY file.
130///
131/// This represents the header parsed by [`PlyGaussians::read_ply_header`].
132#[derive(Debug, Clone)]
133pub enum PlyHeader {
134    /// The Inria PLY format.
135    ///
136    /// The number represents the number of Gaussians.
137    ///
138    /// This can be directly loaded into [`PlyGaussianPod`] by [`BufReader::read_exact`](std::io::Read::read_exact).
139    Inria(usize),
140
141    /// Custom PLY format.
142    Custom(ply_rs::ply::Header),
143}
144
145impl PlyHeader {
146    /// Get the number of Gaussians.
147    ///
148    /// Returns [`None`] if the vertex element is not found in [`PlyHeader::Custom`].
149    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
157/// PLY Gaussian [`Result`] iterator.
158pub enum PlyGaussianIter<
159    I: Iterator<Item = Result<PlyGaussianPod, std::io::Error>>,
160    C: Iterator<Item = Result<PlyGaussianPod, std::io::Error>>,
161> {
162    /// The Inria PLY format.
163    Inria(I),
164
165    /// Custom PLY format.
166    ///
167    /// This still is the same properties as Inria format, but may have different order.
168    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/// A collection of Gaussians in PLY format.
194///
195/// The PLY file is expected to be the same format as the one used in the original Inria
196/// implementation, or a custom PLY file with the same properties.
197///
198/// See [`PlyGaussians::PLY_PROPERTIES`] for a list of expected properties.
199#[derive(Debug, Clone, PartialEq)]
200pub struct PlyGaussians(pub Vec<PlyGaussianPod>);
201
202impl PlyGaussians {
203    /// The list of properties in the PLY file.
204    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    /// Get the number of Gaussians.
270    pub fn len(&self) -> usize {
271        self.0.len()
272    }
273
274    /// Check if there are no Gaussians.
275    pub fn is_empty(&self) -> bool {
276        self.0.is_empty()
277    }
278
279    /// Iterate over the Gaussians.
280    pub fn iter(&self) -> impl ExactSizeIterator<Item = &PlyGaussianPod> {
281        self.0.iter()
282    }
283
284    /// Iterate over the Gaussians mutably.
285    pub fn iter_mut(&mut self) -> impl ExactSizeIterator<Item = &mut PlyGaussianPod> {
286        self.0.iter_mut()
287    }
288
289    /// Read a PLY header.
290    ///
291    /// See [`PlyGaussians::PLY_PROPERTIES`] for a list of expected properties.
292    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    /// Read the PLY Gaussians into [`PlyGaussianPod`].
324    ///
325    /// `header` may be parsed by calling [`PlyGaussians::read_header`].
326    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}