23#ifndef VCL_LOAD_SAVE_PLY_DETAIL_HEADER_H
24#define VCL_LOAD_SAVE_PLY_DETAIL_HEADER_H
28#include <vclib/io/file_info.h>
29#include <vclib/io/read.h>
30#include <vclib/misc/string.h>
31#include <vclib/misc/tokenizer.h>
32#include <vclib/space/complex/mesh_info.h>
38namespace vcl::detail {
49 ply::Format mFormat = ply::UNKNOWN;
51 std::vector<PlyElement> mElements;
52 std::vector<std::string> mTextureFiles;
61 using iterator = std::vector<PlyElement>::const_iterator;
63 PlyHeader() =
default;
68 std::vector<std::string> textureFiles = std::vector<std::string>()) :
69 mValid(true), mFormat(format)
71 setInfo(info, textureFiles, format);
74 PlyHeader(std::istream& file,
const std::string& filename =
"")
79 std::getline(file, line);
80 removeCarriageReturn(line);
81 if (line.compare(0, 3,
"ply") == 0) {
83 bool firstElement =
true;
84 std::string headerLine;
87 Tokenizer spaceTokenizer =
88 readAndTokenizeNextNonEmptyLine(file);
90 Tokenizer::iterator token = spaceTokenizer.begin();
92 if (headerLine ==
"format") {
94 if (*token ==
"ascii")
97 *token ==
"binary_little_endian" ||
99 mFormat = ply::BINARY_LITTLE_ENDIAN;
100 else if (*token ==
"binary_big_endian")
101 mFormat = ply::BINARY_BIG_ENDIAN;
104 else if (headerLine ==
"comment") {
106 if (token != spaceTokenizer.end()) {
107 if (containsCaseInsensitive(*token,
"texture")) {
109 if (token != spaceTokenizer.end()) {
110 std::string textName = *token;
112 findCaseInsensitive(textName,
"<this>");
113 if (it != textName.end()) {
114 uint pos = it - textName.begin();
119 textName.substr(0, pos) + fn +
121 pos + 6, textName.size());
123 mTextureFiles.push_back(textName);
129 else if (headerLine ==
"element") {
134 if (element.type == ply::VERTEX)
135 mVertElemPos = mElements.size();
136 if (element.type == ply::FACE)
137 mFaceElemPos = mElements.size();
138 if (element.type == ply::EDGE)
139 mEdgeElemPos = mElements.size();
140 if (element.type == ply::TRISTRIP)
141 mTriStripElemPos = mElements.size();
142 mElements.push_back(element);
143 element = PlyElement();
145 element = readElement(spaceTokenizer);
146 firstElement =
false;
148 else if (headerLine ==
"property") {
149 PlyProperty p = readProperty(spaceTokenizer);
150 element.properties.push_back(p);
153 else if (headerLine ==
"end_header") {
154 if (element.type == ply::VERTEX)
155 mVertElemPos = mElements.size();
156 if (element.type == ply::FACE)
157 mFaceElemPos = mElements.size();
158 if (element.type == ply::EDGE)
159 mEdgeElemPos = mElements.size();
160 if (element.type == ply::TRISTRIP)
161 mTriStripElemPos = mElements.size();
162 mElements.push_back(element);
165 }
while (!error && headerLine !=
"end_header");
166 mValid = !error && hasVertices();
172 mFormat = ply::UNKNOWN;
174 mTextureFiles.clear();
183 bool isValid()
const {
return mValid; }
185 ply::Format format()
const {
return mFormat; }
187 MeshInfo getInfo()
const
194 for (
const PlyProperty& p : mElements[mVertElemPos].properties) {
198 case ply::z: mod.setVertexCoords();
break;
201 case ply::nz: mod.setVertexNormals();
break;
205 case ply::alpha: mod.setVertexColors();
break;
206 case ply::quality: mod.setVertexQuality();
break;
207 case ply::texture_u: mod.setVertexTexCoords();
break;
209 if (p.type <= ply::PropertyType::DOUBLE) {
210 mod.addVertexCustomComponent(
219 for (
const PlyProperty& p : mElements[mFaceElemPos].properties) {
221 case ply::vertex_indices: mod.setFaceVRefs();
break;
224 case ply::nz: mod.setFaceNormals();
break;
228 case ply::alpha: mod.setFaceColors();
break;
229 case ply::quality: mod.setFaceQuality();
break;
230 case ply::texcoord: mod.setFaceWedgeTexCoords();
break;
232 if (p.type <= ply::PropertyType::DOUBLE) {
233 mod.addFaceCustomComponent(
242 for (
const PlyProperty& p :
243 mElements[mTriStripElemPos].properties) {
245 case ply::vertex_indices: mod.setFaceVRefs();
break;
248 case ply::nz: mod.setFaceNormals();
break;
252 case ply::alpha: mod.setFaceColors();
break;
253 case ply::quality: mod.setFaceQuality();
break;
254 case ply::texcoord: mod.setFaceWedgeTexCoords();
break;
259 if (mTextureFiles.size() > 0) {
260 mod.setTextures(
true);
265 bool hasVertices()
const {
return mVertElemPos !=
UINT_NULL; }
267 bool hasFaces()
const {
return mFaceElemPos !=
UINT_NULL; }
269 bool hasEdges()
const {
return mEdgeElemPos !=
UINT_NULL; }
271 bool hasTriStrips()
const {
return mTriStripElemPos !=
UINT_NULL; }
273 bool hasTextureFileNames()
const {
return mTextureFiles.size() > 0; }
275 uint numberVertices()
const
277 assert(hasVertices());
278 return mElements[mVertElemPos].numberElements;
281 uint numberFaces()
const
284 return mElements[mFaceElemPos].numberElements;
287 uint numberEdges()
const
290 return mElements[mEdgeElemPos].numberElements;
293 uint numberTriStrips()
const
295 assert(hasTriStrips());
296 return mElements[mTriStripElemPos].numberElements;
299 uint numberTextureFileNames()
const {
return mTextureFiles.size(); }
301 const std::list<PlyProperty>& vertexProperties()
const
303 assert(hasVertices());
304 return mElements[mVertElemPos].properties;
307 const std::list<PlyProperty>& faceProperties()
const
310 return mElements[mFaceElemPos].properties;
313 const std::list<PlyProperty>& edgeProperties()
const
316 return mElements[mEdgeElemPos].properties;
319 const std::list<PlyProperty>& triStripsProperties()
const
321 assert(hasTriStrips());
322 return mElements[mTriStripElemPos].properties;
325 const std::vector<std::string>& textureFileNames()
const
327 return mTextureFiles;
330 bool errorWhileLoading()
const {
return !mValid; }
332 void setNumberVertices(
unsigned long int nV)
334 assert(hasVertices());
335 mElements[mVertElemPos].numberElements = nV;
338 void setNumberFaces(
unsigned long int nF)
341 mElements[mFaceElemPos].numberElements = nF;
344 void setNumberEdges(
unsigned long int nE)
347 mElements[mEdgeElemPos].numberElements = nE;
350 void pushTextureFileName(
const std::string& tn)
352 mTextureFiles.push_back(tn);
356 const MeshInfo& info,
357 std::vector<std::string> textureFileNames = std::vector<std::string>(),
358 ply::Format format = ply::BINARY_LITTLE_ENDIAN)
363 mTextureFiles = textureFileNames;
364 if (info.hasVertices()) {
365 mVertElemPos = mElements.size();
367 vElem.type = ply::VERTEX;
368 if (info.hasVertexCoords()) {
369 PlyProperty px, py, pz;
371 px.type = info.vertexCoordsType();
373 py.type = info.vertexCoordsType();
375 pz.type = info.vertexCoordsType();
376 vElem.properties.push_back(px);
377 vElem.properties.push_back(py);
378 vElem.properties.push_back(pz);
380 if (info.hasVertexNormals()) {
381 PlyProperty vnx, vny, vnz;
383 vnx.type = info.vertexNormalsType();
385 vny.type = info.vertexNormalsType();
387 vnz.type = info.vertexNormalsType();
388 vElem.properties.push_back(vnx);
389 vElem.properties.push_back(vny);
390 vElem.properties.push_back(vnz);
392 if (info.hasVertexColors()) {
393 PlyProperty vcr, vcg, vcb, vca;
395 vcr.type = info.vertexColorsType();
396 vcg.name = ply::green;
397 vcg.type = info.vertexColorsType();
398 vcb.name = ply::blue;
399 vcb.type = info.vertexColorsType();
400 vca.name = ply::alpha;
401 vca.type = info.vertexColorsType();
402 vElem.properties.push_back(vcr);
403 vElem.properties.push_back(vcg);
404 vElem.properties.push_back(vcb);
405 vElem.properties.push_back(vca);
407 if (info.hasVertexQuality()) {
409 vs.name = ply::quality;
410 vs.type = info.vertexQualityType();
411 vElem.properties.push_back(vs);
413 if (info.hasVertexTexCoords()) {
414 PlyProperty tcu, tcv, tcn;
415 tcu.name = ply::texture_u;
416 tcu.type = info.vertexTexCoordsType();
417 tcv.name = ply::texture_v;
418 tcv.type = info.vertexTexCoordsType();
419 tcn.name = ply::texnumber;
420 tcn.type = PrimitiveType::USHORT;
421 vElem.properties.push_back(tcu);
422 vElem.properties.push_back(tcv);
423 vElem.properties.push_back(tcn);
425 if (info.hasVertexCustomComponents()) {
426 for (
const auto& cc : info.vertexCustomComponents()) {
427 if (cc.type <= PrimitiveType::DOUBLE) {
429 pp.name = ply::unknown;
430 pp.unknownPropertyName = cc.name;
432 vElem.properties.push_back(pp);
436 mElements.push_back(vElem);
438 if (info.hasFaces()) {
439 mFaceElemPos = mElements.size();
441 fElem.type = ply::FACE;
442 if (info.hasFaceVRefs()) {
445 vids.name = ply::vertex_indices;
446 vids.type = PrimitiveType::UINT;
447 vids.listSizeType = PrimitiveType::UCHAR;
448 fElem.properties.push_back(vids);
450 if (info.hasFaceNormals()) {
451 PlyProperty fnx, fny, fnz;
453 fnx.type = info.faceNormalsType();
455 fny.type = info.faceNormalsType();
457 fnz.type = info.faceNormalsType();
458 fElem.properties.push_back(fnx);
459 fElem.properties.push_back(fny);
460 fElem.properties.push_back(fnz);
462 if (info.hasFaceColors()) {
463 PlyProperty fcr, fcg, fcb, fca;
465 fcr.type = info.faceColorsType();
466 fcg.name = ply::green;
467 fcg.type = info.faceColorsType();
468 fcb.name = ply::blue;
469 fcb.type = info.faceColorsType();
470 fca.name = ply::alpha;
471 fca.type = info.faceColorsType();
472 fElem.properties.push_back(fcr);
473 fElem.properties.push_back(fcg);
474 fElem.properties.push_back(fcb);
475 fElem.properties.push_back(fca);
477 if (info.hasFaceQuality()) {
479 fs.name = ply::quality;
480 fs.type = (ply::PropertyType) info.faceQualityType();
481 fElem.properties.push_back(fs);
483 if (info.hasFaceWedgeTexCoords()) {
486 tc.listSizeType = PrimitiveType::UCHAR;
487 tc.name = ply::texcoord;
488 tc.type = (ply::PropertyType) info.faceWedgeTexCoordsType();
489 tn.name = ply::texnumber;
490 tn.type = PrimitiveType::USHORT;
491 fElem.properties.push_back(tc);
492 fElem.properties.push_back(tn);
494 if (info.hasFaceCustomComponents()) {
495 for (
const auto& cc : info.faceCustomComponents()) {
496 if (cc.type <= PrimitiveType::DOUBLE) {
498 pp.name = ply::unknown;
499 pp.unknownPropertyName = cc.name;
500 pp.type = (ply::PropertyType) cc.type;
501 fElem.properties.push_back(pp);
505 mElements.push_back(fElem);
507 if (info.hasEdges()) {
508 mEdgeElemPos = mElements.size();
510 eElem.type = ply::EDGE;
511 if (info.hasEdgeVRefs()) {
513 v1.name = ply::vertex1;
514 v1.type = PrimitiveType::UINT;
515 eElem.properties.push_back(v1);
517 v2.name = ply::vertex2;
518 v2.type = PrimitiveType::UINT;
519 eElem.properties.push_back(v2);
521 mElements.push_back(eElem);
525 std::string toString()
const
532 case ply::ASCII: s +=
"ascii 1.0\n";
break;
533 case ply::BINARY_BIG_ENDIAN: s +=
"binary_big_endian 1.0\n";
break;
534 default: s +=
"binary_little_endian 1.0\n";
break;
537 s +=
"comment Generated by vclib\n";
538 for (
const std::string& str : mTextureFiles) {
539 s +=
"comment TextureFile " + str +
"\n";
541 for (
const PlyElement& e : mElements) {
545 s +=
"vertex " + std::to_string(e.numberElements) +
"\n";
548 s +=
"face " + std::to_string(e.numberElements) +
"\n";
551 s +=
"edge " + std::to_string(e.numberElements) +
"\n";
554 s +=
"tristrips " + std::to_string(e.numberElements) +
"\n";
557 s +=
"material " + std::to_string(e.numberElements) +
"\n";
560 s += e.unknownElementType +
" " +
561 std::to_string(e.numberElements) +
"\n";
564 for (
const PlyProperty& p : e.properties) {
568 s += typeToString(p.listSizeType) +
" ";
570 s += typeToString(p.type) +
" ";
571 if (p.name == ply::unknown)
572 s += p.unknownPropertyName +
"\n";
574 s += nameToString(p.name) +
"\n";
582 void setFormat(ply::Format f) { mFormat = f; }
584 iterator begin()
const {
return mElements.begin(); }
586 iterator end()
const {
return mElements.end(); }
589 PlyElement readElement(
const Tokenizer& lineTokenizer)
const
592 Tokenizer::iterator token = lineTokenizer.begin();
593 std::string s = *(++token);
595 e.type = ply::VERTEX;
596 e.numberElements = std::stoi(*(++token));
598 else if (s ==
"face") {
600 e.numberElements = std::stoi(*(++token));
602 else if (s ==
"edge") {
604 e.numberElements = std::stoi(*(++token));
606 else if (s ==
"tristrips") {
607 e.type = ply::TRISTRIP;
608 e.numberElements = std::stoi(*(++token));
612 e.numberElements = std::stoi(*(++token));
613 e.unknownElementType = s;
618 PlyProperty readProperty(
const Tokenizer& lineTokenizer)
const
621 Tokenizer::iterator token = lineTokenizer.begin();
622 std::string type = *(++token);
623 if (type ==
"list") {
625 std::string typeSize = *(++token);
626 std::string typeData = *(++token);
627 std::string name = *(++token);
628 p.listSizeType = stringToType(typeSize);
629 p.type = stringToType(typeData);
630 p.name = stringToName(name);
631 if (p.name == ply::unknown)
632 p.unknownPropertyName = name;
636 std::string name = *(++token);
637 p.type = stringToType(type);
638 p.name = stringToName(name);
639 if (p.name == ply::unknown)
640 p.unknownPropertyName = name;
646 ply::PropertyName stringToName(
const std::string& name)
const
648 ply::PropertyName pn = ply::unknown;
669 if (name ==
"quality" || name ==
"scalar")
671 if (name ==
"texture_u")
673 if (name ==
"texture_v")
675 if (name ==
"texnumber")
677 if (name ==
"vertex_indices")
678 pn = ply::vertex_indices;
679 if (name ==
"texcoord")
681 if (name ==
"vertex1")
683 if (name ==
"vertex2")
689 ply::PropertyType stringToType(
const std::string& type)
const
691 ply::PropertyType pt = ply::PropertyType::UCHAR;
693 pt = ply::PropertyType::CHAR;
695 pt = ply::PropertyType::UCHAR;
697 pt = ply::PropertyType::SHORT;
698 if (type ==
"ushort")
699 pt = ply::PropertyType::USHORT;
701 pt = ply::PropertyType::INT;
703 pt = ply::PropertyType::UINT;
705 pt = ply::PropertyType::FLOAT;
706 if (type ==
"double")
707 pt = ply::PropertyType::DOUBLE;
711 std::string nameToString(ply::PropertyName n)
const
714 case ply::x:
return "x";
715 case ply::y:
return "y";
716 case ply::z:
return "z";
717 case ply::nx:
return "nx";
718 case ply::ny:
return "ny";
719 case ply::nz:
return "nz";
720 case ply::red:
return "red";
721 case ply::green:
return "green";
722 case ply::blue:
return "blue";
723 case ply::alpha:
return "alpha";
724 case ply::quality:
return "quality";
725 case ply::texture_u:
return "texture_u";
726 case ply::texture_v:
return "texture_v";
727 case ply::texnumber:
return "texnumber";
728 case ply::vertex_indices:
return "vertex_indices";
729 case ply::texcoord:
return "texcoord";
730 case ply::vertex1:
return "vertex1";
731 case ply::vertex2:
return "vertex2";
732 default:
return "unknown";
736 std::string typeToString(ply::PropertyType t)
const
739 case ply::PropertyType::CHAR:
return "char";
740 case ply::PropertyType::UCHAR:
return "uchar";
741 case ply::PropertyType::SHORT:
return "short";
742 case ply::PropertyType::USHORT:
return "ushort";
743 case ply::PropertyType::INT:
return "int";
744 case ply::PropertyType::UINT:
return "uint";
745 case ply::PropertyType::FLOAT:
return "float";
746 case ply::PropertyType::DOUBLE:
return "double";
747 case ply::PropertyType::NONE:
return "";
static std::string fileNameWithoutExtension(const std::string &fullpath)
Get the file name without extension of a file.
Definition file_info.h:217
PrimitiveType DataType
Enum used to describe the type of Data stored in a component.
Definition mesh_info.h:113
constexpr uint UINT_NULL
The UINT_NULL value represent a null value of uint that is the maximum value that can be represented ...
Definition base.h:48