44 std::ifstream file(
path, std::ios::binary);
45 CHECK(file.is_open()) <<
path;
47 std::vector<PlyPoint>
points;
74 bool X_double =
false;
75 bool Y_double =
false;
76 bool Z_double =
false;
77 bool NX_double =
false;
78 bool NY_double =
false;
79 bool NZ_double =
false;
81 bool in_vertex_section =
false;
82 bool is_binary =
false;
83 bool is_little_endian =
false;
84 size_t num_bytes_per_line = 0;
85 size_t num_vertices = 0;
88 while (std::getline(file, line)) {
95 if (line ==
"end_header") {
99 if (line.size() >= 6 && line.substr(0, 6) ==
"format") {
100 if (line ==
"format ascii 1.0") {
102 }
else if (line ==
"format binary_little_endian 1.0") {
104 is_little_endian =
true;
105 }
else if (line ==
"format binary_big_endian 1.0") {
107 is_little_endian =
false;
111 const std::vector<std::string> line_elems =
StringSplit(line,
" ");
113 if (line_elems.size() >= 3 && line_elems[0] ==
"element") {
114 in_vertex_section =
false;
115 if (line_elems[1] ==
"vertex") {
116 num_vertices = std::stoll(line_elems[2]);
117 in_vertex_section =
true;
118 }
else if (std::stoll(line_elems[2]) > 0) {
119 std::cout <<
"WARN: Only vertex elements supported; ignoring "
124 if (!in_vertex_section) {
130 if (line_elems.size() >= 3 && line_elems[0] ==
"property") {
131 CHECK(line_elems[1] ==
"float" || line_elems[1] ==
"float32" ||
132 line_elems[1] ==
"double" || line_elems[1] ==
"float64" ||
133 line_elems[1] ==
"uchar")
134 <<
"PLY import only supports float, double, and uchar data types";
136 if (line ==
"property float x" || line ==
"property float32 x" ||
137 line ==
"property double x" || line ==
"property float64 x") {
139 X_byte_pos = num_bytes_per_line;
140 X_double = (line_elems[1] ==
"double" || line_elems[1] ==
"float64");
141 }
else if (line ==
"property float y" || line ==
"property float32 y" ||
142 line ==
"property double y" || line ==
"property float64 y") {
144 Y_byte_pos = num_bytes_per_line;
145 Y_double = (line_elems[1] ==
"double" || line_elems[1] ==
"float64");
146 }
else if (line ==
"property float z" || line ==
"property float32 z" ||
147 line ==
"property double z" || line ==
"property float64 z") {
149 Z_byte_pos = num_bytes_per_line;
150 Z_double = (line_elems[1] ==
"double" || line_elems[1] ==
"float64");
151 }
else if (line ==
"property float nx" || line ==
"property float32 nx" ||
152 line ==
"property double nx" ||
153 line ==
"property float64 nx") {
155 NX_byte_pos = num_bytes_per_line;
156 NX_double = (line_elems[1] ==
"double" || line_elems[1] ==
"float64");
157 }
else if (line ==
"property float ny" || line ==
"property float32 ny" ||
158 line ==
"property double ny" ||
159 line ==
"property float64 ny") {
161 NY_byte_pos = num_bytes_per_line;
162 NY_double = (line_elems[1] ==
"double" || line_elems[1] ==
"float64");
163 }
else if (line ==
"property float nz" || line ==
"property float32 nz" ||
164 line ==
"property double nz" ||
165 line ==
"property float64 nz") {
167 NZ_byte_pos = num_bytes_per_line;
168 NZ_double = (line_elems[1] ==
"double" || line_elems[1] ==
"float64");
169 }
else if (line ==
"property uchar r" || line ==
"property uchar red" ||
170 line ==
"property uchar diffuse_red" ||
171 line ==
"property uchar ambient_red" ||
172 line ==
"property uchar specular_red") {
174 R_byte_pos = num_bytes_per_line;
175 }
else if (line ==
"property uchar g" || line ==
"property uchar green" ||
176 line ==
"property uchar diffuse_green" ||
177 line ==
"property uchar ambient_green" ||
178 line ==
"property uchar specular_green") {
180 G_byte_pos = num_bytes_per_line;
181 }
else if (line ==
"property uchar b" || line ==
"property uchar blue" ||
182 line ==
"property uchar diffuse_blue" ||
183 line ==
"property uchar ambient_blue" ||
184 line ==
"property uchar specular_blue") {
186 B_byte_pos = num_bytes_per_line;
190 if (line_elems[1] ==
"float" || line_elems[1] ==
"float32") {
191 num_bytes_per_line += 4;
192 }
else if (line_elems[1] ==
"double" || line_elems[1] ==
"float64") {
193 num_bytes_per_line += 8;
194 }
else if (line_elems[1] ==
"uchar") {
195 num_bytes_per_line += 1;
197 LOG(FATAL) <<
"Invalid data type: " << line_elems[1];
202 const bool is_normal_missing =
203 (NX_index == -1) || (NY_index == -1) || (NZ_index == -1);
204 const bool is_rgb_missing =
205 (R_index == -1) || (G_index == -1) || (B_index == -1);
207 CHECK(X_index != -1 && Y_index != -1 && Z_index)
208 <<
"Invalid PLY file format: x, y, z properties missing";
210 points.reserve(num_vertices);
213 std::vector<char> buffer(num_bytes_per_line);
214 for (
size_t i = 0; i < num_vertices; ++i) {
215 file.read(buffer.data(), num_bytes_per_line);
219 if (is_little_endian) {
221 X_double ? *
reinterpret_cast<double*
>(&buffer[X_byte_pos])
222 : *
reinterpret_cast<float*
>(&buffer[X_byte_pos]));
224 Y_double ? *
reinterpret_cast<double*
>(&buffer[Y_byte_pos])
225 : *
reinterpret_cast<float*
>(&buffer[Y_byte_pos]));
227 Z_double ? *
reinterpret_cast<double*
>(&buffer[Z_byte_pos])
228 : *
reinterpret_cast<float*
>(&buffer[Z_byte_pos]));
230 if (!is_normal_missing) {
232 NX_double ? *
reinterpret_cast<double*
>(&buffer[NX_byte_pos])
233 : *
reinterpret_cast<float*
>(&buffer[NX_byte_pos]));
235 NY_double ? *
reinterpret_cast<double*
>(&buffer[NY_byte_pos])
236 : *
reinterpret_cast<float*
>(&buffer[NY_byte_pos]));
238 NZ_double ? *
reinterpret_cast<double*
>(&buffer[NZ_byte_pos])
239 : *
reinterpret_cast<float*
>(&buffer[NZ_byte_pos]));
242 if (!is_rgb_missing) {
244 *
reinterpret_cast<uint8_t*
>(&buffer[R_byte_pos]));
246 *
reinterpret_cast<uint8_t*
>(&buffer[G_byte_pos]));
248 *
reinterpret_cast<uint8_t*
>(&buffer[B_byte_pos]));
252 X_double ? *
reinterpret_cast<double*
>(&buffer[X_byte_pos])
253 : *
reinterpret_cast<float*
>(&buffer[X_byte_pos]));
255 Y_double ? *
reinterpret_cast<double*
>(&buffer[Y_byte_pos])
256 : *
reinterpret_cast<float*
>(&buffer[Y_byte_pos]));
258 Z_double ? *
reinterpret_cast<double*
>(&buffer[Z_byte_pos])
259 : *
reinterpret_cast<float*
>(&buffer[Z_byte_pos]));
261 if (!is_normal_missing) {
263 NX_double ? *
reinterpret_cast<double*
>(&buffer[NX_byte_pos])
264 : *
reinterpret_cast<float*
>(&buffer[NX_byte_pos]));
266 NY_double ? *
reinterpret_cast<double*
>(&buffer[NY_byte_pos])
267 : *
reinterpret_cast<float*
>(&buffer[NY_byte_pos]));
269 NZ_double ? *
reinterpret_cast<double*
>(&buffer[NZ_byte_pos])
270 : *
reinterpret_cast<float*
>(&buffer[NZ_byte_pos]));
273 if (!is_rgb_missing) {
275 *
reinterpret_cast<uint8_t*
>(&buffer[R_byte_pos]));
277 *
reinterpret_cast<uint8_t*
>(&buffer[G_byte_pos]));
279 *
reinterpret_cast<uint8_t*
>(&buffer[B_byte_pos]));
286 while (std::getline(file, line)) {
288 std::stringstream line_stream(line);
291 std::vector<std::string> items;
292 while (!line_stream.eof()) {
293 std::getline(line_stream, item,
' ');
295 items.push_back(item);
300 point.
x = std::stold(items.at(X_index));
301 point.
y = std::stold(items.at(Y_index));
302 point.
z = std::stold(items.at(Z_index));
304 if (!is_normal_missing) {
305 point.
nx = std::stold(items.at(NX_index));
306 point.
ny = std::stold(items.at(NY_index));
307 point.
nz = std::stold(items.at(NZ_index));
310 if (!is_rgb_missing) {
311 point.
r = std::stoi(items.at(R_index));
312 point.
g = std::stoi(items.at(G_index));
313 point.
b = std::stoi(items.at(B_index));
324 const std::vector<PlyPoint>&
points,
325 const bool write_normal,
const bool write_rgb) {
326 std::ofstream file(
path);
327 CHECK(file.is_open()) <<
path;
338 file <<
"property float nx" <<
std::endl;
339 file <<
"property float ny" <<
std::endl;
340 file <<
"property float nz" <<
std::endl;
344 file <<
"property uchar red" <<
std::endl;
345 file <<
"property uchar green" <<
std::endl;
346 file <<
"property uchar blue" <<
std::endl;
351 for (
const auto& point :
points) {
352 file << point.x <<
" " << point.y <<
" " << point.z;
355 file <<
" " << point.nx <<
" " << point.ny <<
" " << point.nz;
359 file <<
" " <<
static_cast<int>(point.r) <<
" "
360 <<
static_cast<int>(point.g) <<
" " <<
static_cast<int>(point.b);
370 const std::vector<PlyPoint>&
points,
371 const bool write_normal,
const bool write_rgb) {
372 std::fstream text_file(
path, std::ios::out);
373 CHECK(text_file.is_open()) <<
path;
376 text_file <<
"format binary_little_endian 1.0" <<
std::endl;
379 text_file <<
"property float x" <<
std::endl;
380 text_file <<
"property float y" <<
std::endl;
381 text_file <<
"property float z" <<
std::endl;
384 text_file <<
"property float nx" <<
std::endl;
385 text_file <<
"property float ny" <<
std::endl;
386 text_file <<
"property float nz" <<
std::endl;
390 text_file <<
"property uchar red" <<
std::endl;
391 text_file <<
"property uchar green" <<
std::endl;
392 text_file <<
"property uchar blue" <<
std::endl;
398 std::fstream binary_file(
path,
399 std::ios::out | std::ios::binary | std::ios::app);
400 CHECK(binary_file.is_open()) <<
path;
402 for (
const auto& point :
points) {
403 WriteBinaryLittleEndian<float>(&binary_file, point.x);
404 WriteBinaryLittleEndian<float>(&binary_file, point.y);
405 WriteBinaryLittleEndian<float>(&binary_file, point.z);
408 WriteBinaryLittleEndian<float>(&binary_file, point.nx);
409 WriteBinaryLittleEndian<float>(&binary_file, point.ny);
410 WriteBinaryLittleEndian<float>(&binary_file, point.nz);
414 WriteBinaryLittleEndian<uint8_t>(&binary_file, point.r);
415 WriteBinaryLittleEndian<uint8_t>(&binary_file, point.g);
416 WriteBinaryLittleEndian<uint8_t>(&binary_file, point.b);
424 std::fstream file(
path, std::ios::out);
425 CHECK(file.is_open());
434 file <<
"property list uchar int vertex_index" <<
std::endl;
437 for (
const auto& vertex : mesh.
vertices) {
438 file << vertex.x <<
" " << vertex.y <<
" " << vertex.z <<
std::endl;
449 std::fstream text_file(
path, std::ios::out);
450 CHECK(text_file.is_open());
453 text_file <<
"format binary_little_endian 1.0" <<
std::endl;
455 text_file <<
"property float x" <<
std::endl;
456 text_file <<
"property float y" <<
std::endl;
457 text_file <<
"property float z" <<
std::endl;
459 text_file <<
"property list uchar int vertex_index" <<
std::endl;
463 std::fstream binary_file(
path,
464 std::ios::out | std::ios::binary | std::ios::app);
465 CHECK(binary_file.is_open()) <<
path;
467 for (
const auto& vertex : mesh.
vertices) {
468 WriteBinaryLittleEndian<float>(&binary_file, vertex.x);
469 WriteBinaryLittleEndian<float>(&binary_file, vertex.y);
470 WriteBinaryLittleEndian<float>(&binary_file, vertex.z);
477 const uint8_t kNumVertices = 3;
478 WriteBinaryLittleEndian<uint8_t>(&binary_file, kNumVertices);
479 WriteBinaryLittleEndian<int>(&binary_file,
face.vertex_idx1);
480 WriteBinaryLittleEndian<int>(&binary_file,
face.vertex_idx2);
481 WriteBinaryLittleEndian<int>(&binary_file,
face.vertex_idx3);
std::vector< unsigned int > face
QTextStream & endl(QTextStream &stream)
static const std::string path
void StringTrim(std::string *str)
void WriteTextPlyMesh(const std::string &path, const PlyMesh &mesh)
void WriteBinaryPlyPoints(const std::string &path, const std::vector< PlyPoint > &points, const bool write_normal, const bool write_rgb)
T LittleEndianToNative(const T x)
std::vector< std::string > StringSplit(const std::string &str, const std::string &delim)
T BigEndianToNative(const T x)
std::string StringPrintf(const char *format,...)
void WriteTextPlyPoints(const std::string &path, const std::vector< PlyPoint > &points, const bool write_normal, const bool write_rgb)
void WriteBinaryPlyMesh(const std::string &path, const PlyMesh &mesh)
std::vector< PlyPoint > ReadPly(const std::string &path)
std::vector< PlyMeshFace > faces
std::vector< PlyMeshVertex > vertices