23#ifndef VCL_IO_MESH_PLY_DETAIL_HEADER_H
24#define VCL_IO_MESH_PLY_DETAIL_HEADER_H
28#include <vclib/io/file_info.h>
29#include <vclib/io/mesh/settings.h>
30#include <vclib/io/read.h>
32#include <vclib/space/complex.h>
38namespace vcl::detail {
49 ply::Format mFormat = ply::UNKNOWN;
51 std::vector<PlyElement> mElements;
52 std::vector<std::string> mTextureFiles;
62 using iterator = std::vector<PlyElement>::const_iterator;
64 PlyHeader() =
default;
66 PlyHeader(ply::Format format,
const MeshInfo& info) :
67 mValid(true), mFormat(format)
69 setInfo(info, format);
72 PlyHeader(std::istream& file,
const std::string& filename =
"")
77 std::getline(file, line);
78 removeCarriageReturn(line);
79 if (line.compare(0, 3,
"ply") == 0) {
81 bool firstElement =
true;
82 std::string headerLine;
85 Tokenizer spaceTokenizer =
86 readAndTokenizeNextNonEmptyLine(file);
88 Tokenizer::iterator token = spaceTokenizer.begin();
90 if (headerLine ==
"format") {
92 if (*token ==
"ascii")
95 *token ==
"binary_little_endian" ||
97 mFormat = ply::BINARY_LITTLE_ENDIAN;
98 else if (*token ==
"binary_big_endian")
99 mFormat = ply::BINARY_BIG_ENDIAN;
102 else if (headerLine ==
"comment") {
104 if (token != spaceTokenizer.end()) {
105 if (containsCaseInsensitive(*token,
"texture")) {
107 if (token != spaceTokenizer.end()) {
108 std::string textName = *token;
110 findCaseInsensitive(textName,
"<this>");
111 if (it != textName.end()) {
112 uint pos = it - textName.begin();
117 textName.substr(0, pos) + fn +
119 pos + 6, textName.size());
121 mTextureFiles.push_back(textName);
127 else if (headerLine ==
"element") {
132 if (element.type == ply::VERTEX)
133 mVertElemPos = mElements.size();
134 if (element.type == ply::FACE)
135 mFaceElemPos = mElements.size();
136 if (element.type == ply::EDGE)
137 mEdgeElemPos = mElements.size();
138 if (element.type == ply::TRISTRIP)
139 mTriStripElemPos = mElements.size();
140 if (element.type == ply::MATERIAL)
141 mMaterialElemPos = 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 if (element.type == ply::MATERIAL)
163 mMaterialElemPos = mElements.size();
164 mElements.push_back(element);
167 }
while (!error && headerLine !=
"end_header");
168 mValid = !error && hasVertices();
174 mFormat = ply::UNKNOWN;
176 mTextureFiles.clear();
185 bool isValid()
const {
return mValid; }
187 ply::Format format()
const {
return mFormat; }
189 MeshInfo getInfo()
const
196 for (
const PlyProperty& p : mElements[mVertElemPos].properties) {
200 case ply::z: mod.setPerVertexPosition();
break;
203 case ply::nz: mod.setPerVertexNormal();
break;
207 case ply::alpha: mod.setPerVertexColor();
break;
208 case ply::quality: mod.setPerVertexQuality();
break;
209 case ply::texture_u: mod.setPerVertexTexCoord();
break;
210 case ply::material_index:
211 mod.setPerVertexMaterialIndex();
214 if (p.type <= ply::PropertyType::DOUBLE) {
215 mod.addPerVertexCustomComponent(
224 for (
const PlyProperty& p : mElements[mFaceElemPos].properties) {
226 case ply::vertex_indices:
227 mod.setPerFaceVertexReferences();
231 case ply::nz: mod.setPerFaceNormal();
break;
235 case ply::alpha: mod.setPerFaceColor();
break;
236 case ply::quality: mod.setPerFaceQuality();
break;
237 case ply::material_index: mod.setPerFaceMaterialIndex();
break;
238 case ply::texcoord: mod.setPerFaceWedgeTexCoords();
break;
240 if (p.type <= ply::PropertyType::DOUBLE) {
241 mod.addPerFaceCustomComponent(
250 for (
const PlyProperty& p :
251 mElements[mTriStripElemPos].properties) {
253 case ply::vertex_indices:
254 mod.setPerFaceVertexReferences();
258 case ply::nz: mod.setPerFaceNormal();
break;
262 case ply::alpha: mod.setPerFaceColor();
break;
263 case ply::quality: mod.setPerFaceQuality();
break;
264 case ply::texcoord: mod.setPerFaceWedgeTexCoords();
break;
271 for (
const PlyProperty& p : mElements[mEdgeElemPos].properties) {
273 case ply::vertex_indices:
274 mod.setPerEdgeVertexReferences();
278 case ply::nz: mod.setPerEdgeNormal();
break;
282 case ply::alpha: mod.setPerEdgeColor();
break;
283 case ply::quality: mod.setPerEdgeQuality();
break;
288 if (mMaterialElemPos !=
UINT_NULL || mTextureFiles.size() > 0) {
294 bool hasVertices()
const {
return mVertElemPos !=
UINT_NULL; }
296 bool hasFaces()
const {
return mFaceElemPos !=
UINT_NULL; }
298 bool hasEdges()
const {
return mEdgeElemPos !=
UINT_NULL; }
300 bool hasTriStrips()
const {
return mTriStripElemPos !=
UINT_NULL; }
302 bool hasMaterials()
const {
return mMaterialElemPos !=
UINT_NULL; }
304 uint vertexCount()
const
306 assert(hasVertices());
307 return mElements[mVertElemPos].elementCount;
310 uint faceCount()
const
313 return mElements[mFaceElemPos].elementCount;
316 uint edgeCount()
const
319 return mElements[mEdgeElemPos].elementCount;
322 uint triStripCount()
const
324 assert(hasTriStrips());
325 return mElements[mTriStripElemPos].elementCount;
328 uint materialCount()
const
330 assert(hasMaterials());
331 return mElements[mMaterialElemPos].elementCount;
334 uint textureFileNameCount()
const {
return mTextureFiles.size(); }
336 const std::list<PlyProperty>& vertexProperties()
const
338 assert(hasVertices());
339 return mElements[mVertElemPos].properties;
342 const std::list<PlyProperty>& faceProperties()
const
345 return mElements[mFaceElemPos].properties;
348 const std::list<PlyProperty>& edgeProperties()
const
351 return mElements[mEdgeElemPos].properties;
354 const std::list<PlyProperty>& triStripsProperties()
const
356 assert(hasTriStrips());
357 return mElements[mTriStripElemPos].properties;
360 const std::list<PlyProperty>& materialProperties()
const
362 assert(hasMaterials());
363 return mElements[mMaterialElemPos].properties;
366 const std::vector<std::string>& textureFileNames()
const
368 return mTextureFiles;
371 bool errorWhileLoading()
const {
return !mValid; }
373 void setVertexCount(
unsigned long int nV)
375 assert(hasVertices());
376 mElements[mVertElemPos].elementCount = nV;
379 void setFaceCount(
unsigned long int nF)
382 mElements[mFaceElemPos].elementCount = nF;
385 void setEdgeCount(
unsigned long int nE)
388 mElements[mEdgeElemPos].elementCount = nE;
391 void setMaterialCount(
unsigned long int nM)
393 assert(hasMaterials());
394 mElements[mMaterialElemPos].elementCount = nM;
397 void pushTextureFileName(
const std::string& tn)
399 mTextureFiles.push_back(tn);
403 const MeshInfo& info,
404 ply::Format format = ply::BINARY_LITTLE_ENDIAN)
409 if (info.hasVertices()) {
410 mVertElemPos = mElements.size();
412 vElem.type = ply::VERTEX;
413 if (info.hasPerVertexPosition()) {
414 PlyProperty px, py, pz;
416 px.type = info.perVertexPositionType();
418 py.type = info.perVertexPositionType();
420 pz.type = info.perVertexPositionType();
421 vElem.properties.push_back(px);
422 vElem.properties.push_back(py);
423 vElem.properties.push_back(pz);
425 if (info.hasPerVertexNormal()) {
426 PlyProperty vnx, vny, vnz;
428 vnx.type = info.perVertexNormalType();
430 vny.type = info.perVertexNormalType();
432 vnz.type = info.perVertexNormalType();
433 vElem.properties.push_back(vnx);
434 vElem.properties.push_back(vny);
435 vElem.properties.push_back(vnz);
437 if (info.hasPerVertexColor()) {
438 PlyProperty vcr, vcg, vcb, vca;
440 vcr.type = info.perVertexColorType();
441 vcg.name = ply::green;
442 vcg.type = info.perVertexColorType();
443 vcb.name = ply::blue;
444 vcb.type = info.perVertexColorType();
445 vca.name = ply::alpha;
446 vca.type = info.perVertexColorType();
447 vElem.properties.push_back(vcr);
448 vElem.properties.push_back(vcg);
449 vElem.properties.push_back(vcb);
450 vElem.properties.push_back(vca);
452 if (info.hasPerVertexQuality()) {
454 vs.name = ply::quality;
455 vs.type = info.perVertexQualityType();
456 vElem.properties.push_back(vs);
458 if (info.hasPerVertexTexCoord()) {
459 PlyProperty tcu, tcv;
460 tcu.name = ply::texture_u;
461 tcu.type = info.perVertexTexCoordType();
462 tcv.name = ply::texture_v;
463 tcv.type = info.perVertexTexCoordType();
464 vElem.properties.push_back(tcu);
465 vElem.properties.push_back(tcv);
467 if (info.hasPerVertexMaterialIndex()) {
469 tcn.name = ply::material_index;
470 tcn.type = info.perVertexMaterialIndexType();
471 vElem.properties.push_back(tcn);
473 if (info.hasPerVertexCustomComponents()) {
474 for (
const auto& cc : info.perVertexCustomComponents()) {
475 if (cc.type <= PrimitiveType::DOUBLE) {
477 pp.name = ply::unknown;
478 pp.unknownPropertyName = cc.name;
480 vElem.properties.push_back(pp);
484 mElements.push_back(vElem);
486 if (info.hasFaces()) {
487 mFaceElemPos = mElements.size();
489 fElem.type = ply::FACE;
490 if (info.hasPerFaceVertexReferences()) {
493 vids.name = ply::vertex_indices;
494 vids.type = PrimitiveType::UINT;
495 vids.listSizeType = PrimitiveType::UCHAR;
496 fElem.properties.push_back(vids);
498 if (info.hasPerFaceNormal()) {
499 PlyProperty fnx, fny, fnz;
501 fnx.type = info.perFaceNormalType();
503 fny.type = info.perFaceNormalType();
505 fnz.type = info.perFaceNormalType();
506 fElem.properties.push_back(fnx);
507 fElem.properties.push_back(fny);
508 fElem.properties.push_back(fnz);
510 if (info.hasPerFaceColor()) {
511 PlyProperty fcr, fcg, fcb, fca;
513 fcr.type = info.perFaceColorType();
514 fcg.name = ply::green;
515 fcg.type = info.perFaceColorType();
516 fcb.name = ply::blue;
517 fcb.type = info.perFaceColorType();
518 fca.name = ply::alpha;
519 fca.type = info.perFaceColorType();
520 fElem.properties.push_back(fcr);
521 fElem.properties.push_back(fcg);
522 fElem.properties.push_back(fcb);
523 fElem.properties.push_back(fca);
525 if (info.hasPerFaceQuality()) {
527 fs.name = ply::quality;
528 fs.type = info.perFaceQualityType();
529 fElem.properties.push_back(fs);
531 if (info.hasPerFaceWedgeTexCoords()) {
534 tc.listSizeType = PrimitiveType::UCHAR;
535 tc.name = ply::texcoord;
536 tc.type = info.perFaceWedgeTexCoordsType();
537 fElem.properties.push_back(tc);
539 if (info.hasPerFaceMaterialIndex()) {
541 tn.name = ply::material_index;
542 tn.type = info.perFaceMaterialIndexType();
543 fElem.properties.push_back(tn);
545 if (info.hasPerFaceCustomComponents()) {
546 for (
const auto& cc : info.perFaceCustomComponents()) {
547 if (cc.type <= PrimitiveType::DOUBLE) {
549 pp.name = ply::unknown;
550 pp.unknownPropertyName = cc.name;
552 fElem.properties.push_back(pp);
556 mElements.push_back(fElem);
558 if (info.hasEdges()) {
559 mEdgeElemPos = mElements.size();
561 eElem.type = ply::EDGE;
562 if (info.hasPerEdgeVertexReferences()) {
564 v1.name = ply::vertex1;
565 v1.type = PrimitiveType::UINT;
566 eElem.properties.push_back(v1);
568 v2.name = ply::vertex2;
569 v2.type = PrimitiveType::UINT;
570 eElem.properties.push_back(v2);
572 mElements.push_back(eElem);
574 if (info.hasMaterials()) {
575 mMaterialElemPos = mElements.size();
577 matElem.type = ply::MATERIAL;
579 PlyProperty bcr, bcg, bcb, bca;
581 bcr.type = PrimitiveType::UCHAR;
582 bcg.name = ply::green;
583 bcg.type = PrimitiveType::UCHAR;
584 bcb.name = ply::blue;
585 bcb.type = PrimitiveType::UCHAR;
586 bca.name = ply::alpha;
587 bca.type = PrimitiveType::UCHAR;
588 matElem.properties.push_back(bcr);
589 matElem.properties.push_back(bcg);
590 matElem.properties.push_back(bcb);
591 matElem.properties.push_back(bca);
594 pm.name = ply::metallic;
595 pm.type = PrimitiveType::FLOAT;
596 pr.name = ply::roughness;
597 pr.type = PrimitiveType::FLOAT;
598 matElem.properties.push_back(pm);
599 matElem.properties.push_back(pr);
601 PlyProperty ecr, ecg, ecb;
602 ecr.name = ply::emissive_red;
603 ecr.type = PrimitiveType::UCHAR;
604 ecg.name = ply::emissive_green;
605 ecg.type = PrimitiveType::UCHAR;
606 ecb.name = ply::emissive_blue;
607 ecb.type = PrimitiveType::UCHAR;
608 matElem.properties.push_back(ecr);
609 matElem.properties.push_back(ecg);
610 matElem.properties.push_back(ecb);
612 PlyProperty pam, pac;
613 pam.name = ply::alpha_mode;
614 pam.type = PrimitiveType::UINT;
615 pac.name = ply::alpha_cutoff;
616 pac.type = PrimitiveType::FLOAT;
617 matElem.properties.push_back(pam);
618 matElem.properties.push_back(pac);
620 PlyProperty pns, pos;
621 pns.name = ply::normal_scale;
622 pns.type = PrimitiveType::FLOAT;
623 pos.name = ply::occlusion_strength;
624 pos.type = PrimitiveType::FLOAT;
625 matElem.properties.push_back(pns);
626 matElem.properties.push_back(pos);
629 pds.name = ply::double_sided;
630 pds.type = PrimitiveType::UINT;
631 matElem.properties.push_back(pds);
634 pt.name = ply::base_color_texture;
637 PrimitiveType::UCHAR;
642 pt.type = PrimitiveType::CHAR;
643 matElem.properties.push_back(pt);
644 pt.name = ply::metallic_roughness_texture;
645 matElem.properties.push_back(pt);
646 pt.name = ply::normal_texture;
647 matElem.properties.push_back(pt);
648 pt.name = ply::occlusion_texture;
649 matElem.properties.push_back(pt);
650 pt.name = ply::emissive_texture;
651 matElem.properties.push_back(pt);
654 matElem.properties.push_back(pt);
656 mElements.push_back(matElem);
660 std::string toString(
const SaveSettings& settings)
const
667 case ply::ASCII: s +=
"ascii 1.0\n";
break;
668 case ply::BINARY_BIG_ENDIAN: s +=
"binary_big_endian 1.0\n";
break;
669 default: s +=
"binary_little_endian 1.0\n";
break;
672 s +=
"comment Generated by vclib\n";
673 if (settings.meshlabCompatibility) {
674 for (
const std::string& str : mTextureFiles) {
675 s +=
"comment TextureFile " + str +
"\n";
678 for (
const PlyElement& e : mElements) {
682 s +=
"vertex " + std::to_string(e.elementCount) +
"\n";
685 s +=
"face " + std::to_string(e.elementCount) +
"\n";
688 s +=
"edge " + std::to_string(e.elementCount) +
"\n";
691 s +=
"tristrips " + std::to_string(e.elementCount) +
"\n";
694 s +=
"material " + std::to_string(e.elementCount) +
"\n";
697 s += e.unknownElementType +
" " +
698 std::to_string(e.elementCount) +
"\n";
701 for (
const PlyProperty& p : e.properties) {
705 s += typeToString(p.listSizeType) +
" ";
707 s += typeToString(p.type) +
" ";
708 if (p.name == ply::unknown)
709 s += p.unknownPropertyName +
"\n";
711 s += nameToString(p.name, settings.meshlabCompatibility) +
720 void setFormat(ply::Format f) { mFormat = f; }
722 iterator begin()
const {
return mElements.begin(); }
724 iterator end()
const {
return mElements.end(); }
727 static PlyElement readElement(
const Tokenizer& lineTokenizer)
730 Tokenizer::iterator token = lineTokenizer.begin();
731 std::string s = *(++token);
733 e.type = ply::VERTEX;
734 e.elementCount = std::stoi(*(++token));
736 else if (s ==
"face") {
738 e.elementCount = std::stoi(*(++token));
740 else if (s ==
"edge") {
742 e.elementCount = std::stoi(*(++token));
744 else if (s ==
"tristrips") {
745 e.type = ply::TRISTRIP;
746 e.elementCount = std::stoi(*(++token));
748 else if (s ==
"material") {
749 e.type = ply::MATERIAL;
750 e.elementCount = std::stoi(*(++token));
754 e.elementCount = std::stoi(*(++token));
755 e.unknownElementType = s;
760 static PlyProperty readProperty(
const Tokenizer& lineTokenizer)
763 Tokenizer::iterator token = lineTokenizer.begin();
764 std::string type = *(++token);
765 if (type ==
"list") {
767 std::string typeSize = *(++token);
768 std::string typeData = *(++token);
769 std::string name = *(++token);
770 p.listSizeType = stringToType(typeSize);
771 p.type = stringToType(typeData);
772 p.name = stringToName(name);
773 if (p.name == ply::unknown)
774 p.unknownPropertyName = name;
778 std::string name = *(++token);
779 p.type = stringToType(type);
780 p.name = stringToName(name);
781 if (p.name == ply::unknown)
782 p.unknownPropertyName = name;
788 static ply::PropertyName stringToName(
const std::string& name)
790 ply::PropertyName pn = ply::unknown;
811 if (name ==
"quality" || name ==
"scalar")
813 if (name ==
"texture_u" || name ==
"s" || name ==
"u")
815 if (name ==
"texture_v" || name ==
"t" || name ==
"v")
817 if (name ==
"material_index" || name ==
"texnumber")
818 pn = ply::material_index;
819 if (name ==
"vertex_indices")
820 pn = ply::vertex_indices;
821 if (name ==
"texcoord")
823 if (name ==
"vertex1")
825 if (name ==
"vertex2")
829 if (name ==
"metallic")
831 if (name ==
"roughness")
833 if (name ==
"emissive_red")
834 pn = ply::emissive_red;
835 if (name ==
"emissive_green")
836 pn = ply::emissive_green;
837 if (name ==
"emissive_blue")
838 pn = ply::emissive_blue;
839 if (name ==
"alpha_mode")
840 pn = ply::alpha_mode;
841 if (name ==
"alpha_cutoff")
842 pn = ply::alpha_cutoff;
843 if (name ==
"normal_scale")
844 pn = ply::normal_scale;
845 if (name ==
"occlusion_strength")
846 pn = ply::occlusion_strength;
847 if (name ==
"double_sided")
848 pn = ply::double_sided;
849 if (name ==
"base_color_texture")
850 pn = ply::base_color_texture;
851 if (name ==
"metallic_roughness_texture")
852 pn = ply::metallic_roughness_texture;
853 if (name ==
"normal_texture")
854 pn = ply::normal_texture;
855 if (name ==
"occlusion_texture")
856 pn = ply::occlusion_texture;
857 if (name ==
"emissive_texture")
858 pn = ply::emissive_texture;
863 static ply::PropertyType stringToType(
const std::string& type)
865 ply::PropertyType pt = ply::PropertyType::UCHAR;
867 pt = ply::PropertyType::CHAR;
869 pt = ply::PropertyType::UCHAR;
871 pt = ply::PropertyType::SHORT;
872 if (type ==
"ushort")
873 pt = ply::PropertyType::USHORT;
875 pt = ply::PropertyType::INT;
877 pt = ply::PropertyType::UINT;
879 pt = ply::PropertyType::FLOAT;
880 if (type ==
"double")
881 pt = ply::PropertyType::DOUBLE;
885 static std::string nameToString(
887 bool meshlabCompatibility =
false)
890 case ply::x:
return "x";
891 case ply::y:
return "y";
892 case ply::z:
return "z";
893 case ply::nx:
return "nx";
894 case ply::ny:
return "ny";
895 case ply::nz:
return "nz";
896 case ply::red:
return "red";
897 case ply::green:
return "green";
898 case ply::blue:
return "blue";
899 case ply::alpha:
return "alpha";
900 case ply::quality:
return "quality";
901 case ply::texture_u:
return "texture_u";
902 case ply::texture_v:
return "texture_v";
903 case ply::material_index:
904 return meshlabCompatibility ?
"texnumber" :
"material_index";
905 case ply::vertex_indices:
return "vertex_indices";
906 case ply::texcoord:
return "texcoord";
907 case ply::vertex1:
return "vertex1";
908 case ply::vertex2:
return "vertex2";
909 case ply::name:
return "name";
910 case ply::metallic:
return "metallic";
911 case ply::roughness:
return "roughness";
912 case ply::emissive_red:
return "emissive_red";
913 case ply::emissive_green:
return "emissive_green";
914 case ply::emissive_blue:
return "emissive_blue";
915 case ply::alpha_mode:
return "alpha_mode";
916 case ply::alpha_cutoff:
return "alpha_cutoff";
917 case ply::normal_scale:
return "normal_scale";
918 case ply::occlusion_strength:
return "occlusion_strength";
919 case ply::double_sided:
return "double_sided";
920 case ply::base_color_texture:
return "base_color_texture";
921 case ply::metallic_roughness_texture:
922 return "metallic_roughness_texture";
923 case ply::normal_texture:
return "normal_texture";
924 case ply::occlusion_texture:
return "occlusion_texture";
925 case ply::emissive_texture:
return "emissive_texture";
926 default:
return "unknown";
930 static std::string typeToString(ply::PropertyType t)
933 case ply::PropertyType::CHAR:
return "char";
934 case ply::PropertyType::UCHAR:
return "uchar";
935 case ply::PropertyType::SHORT:
return "short";
936 case ply::PropertyType::USHORT:
return "ushort";
937 case ply::PropertyType::INT:
return "int";
938 case ply::PropertyType::UINT:
return "uint";
939 case ply::PropertyType::FLOAT:
return "float";
940 case ply::PropertyType::DOUBLE:
return "double";
941 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:220
PrimitiveType DataType
Enum used to describe the type of Data stored in a component.
Definition mesh_info.h:114
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:49