/**************************************************************************** * NanoPLY * * NanoPLY is a C++11 header-only library to read and write PLY file * * * * Copyright(C) 2014-2015 * * Visual Computing Lab * * ISTI - Italian National Research Council * * * * This Source Code Form is subject to the terms of the Mozilla Public * * License, v. 2.0. If a copy of the MPL was not distributed with this * * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * * ****************************************************************************/ #ifndef NANOPLY_HPP #define NANOPLY_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include // Avoid conflicting declaration of min/max macros in windows headers #if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64)) # define NOMINMAX # ifdef max # undef max # undef min # endif #endif namespace nanoply { /** Error Type. * Error type returned by the open of a PLY file. */ typedef enum NNP_ERROR { NNP_OK = 0x0000, /**< No error. */ NNP_UNABLE_TO_OPEN = 0x0001, /**< The file cannot be opend. */ NNP_MISSING_HEADER = 0x0002, /**< The file does not contain a valid PLY header. */ NNP_MISSING_FORMAT = 0x0004, /**< The file has an invalid internal format. */ NNP_INVALID_ELEMENT = 0x0008, /**< The file has an invalid element. */ NNP_INVALID_PROPERTY = 0x0010 /**< The file has an invalid property. */ } ErrorCode; /** PLY Element Entity. * Element that can be saved in PLY file. */ typedef enum NNP_ELEM { NNP_UNKNOWN_ELEM = 0x0, /**< Unknown element. */ NNP_VERTEX_ELEM = 0x1, /**< Vertex element. */ NNP_EDGE_ELEM = 0x2, /**< Edge element. */ NNP_FACE_ELEM = 0x4 /**< Face element. */ } PlyElemEntity; /** PLY Entity. * Property that can be saved in a PLY file. */ typedef enum NNP_ENTITY { NNP_UNKNOWN_ENTITY = 0x00000000, /**< Unknown property. */ NNP_PX = 0x00000001, /**< Position x cordinate. */ NNP_PY = 0x00000002, /**< Position y cordinate. */ NNP_PZ = 0x00000004, /**< Position z cordinate. */ NNP_PXYZ = 0x00000007, /**< Position (x, y, z). */ NNP_NX = 0x00000010, /**< Normal x component. */ NNP_NY = 0x00000020, /**< Normal y component. */ NNP_NZ = 0x00000040, /**< Normal z component. */ NNP_NXYZ = 0x00000070, /**< Normal (x, y, z). */ NNP_CR = 0x00000100, /**< Color red component. */ NNP_CG = 0x00000200, /**< Color green component. */ NNP_CB = 0x00000400, /**< Color blue component. */ NNP_CRGB = 0x00000700, /**< Color RGB. */ NNP_CA = 0x00000800, /**< Color alpha component. */ NNP_CRGBA = 0x00000F00, /**< Color RGBA. */ NNP_DENSITY = 0x00000008, /**< Density or Radius property. */ NNP_SCALE = 0x00000080, /**< Scale property. */ NNP_TEXTUREU = 0x00001000, /**< Texture coordinate u. */ NNP_TEXTUREV = 0x00002000, /**< Texture coordinate v. */ NNP_TEXTURE2D = 0x00003000, /**< Texture coordinate 2D. */ NNP_TEXTUREW = 0x00004000, /**< Texture coordinate w. */ NNP_TEXTURE3D = 0x00007000, /**< Texture coordinate 3D. */ NNP_TEXTUREINDEX = 0x00008000, /**< Texture index. */ NNP_QUALITY = 0x00010000, /**< Quality property. */ NNP_REFLECTANCE = 0x00020000, /**< Reflectance property. */ NNP_BITFLAG = 0x00040000, /**< Bit flags. */ NNP_K1 = 0x00080000, /**< Main curvaure value k1. */ NNP_K2 = 0x00100000, /**< Main curvaure value k2. */ NNP_KG = 0x00200000, /**< Gaussian curvature value. */ NNP_KH = 0x00400000, /**< Mean curvature value. */ NNP_K1DIR = 0x00800000, /**< Curvature direction k1. */ NNP_K2DIR = 0x01000000, /**< Curvature direction k2. */ NNP_EDGE_V1 = 0x02000000, /**< Index of the first vertex of the edge. */ NNP_EDGE_V2 = 0x04000000, /**< Index of the second vertex of the edge. */ NNP_FACE_VERTEX_LIST = 0x08000000, /**< List of vertex indices for the face. */ NNP_FACE_WEDGE_COLOR = 0x10000000, /**< List of colors for wedge. */ NNP_FACE_WEDGE_NORMAL = 0x20000000, /**< List of normals for wedge. */ NNP_FACE_WEDGE_TEX = 0x40000000 /**< List of texture coordinates for wedge. */ } PlyEntity; /** PLY Type. * Type of a PLY property. */ typedef enum NNP_PLYTYPE { NNP_UNKNOWN_TYPE = 0x000000, /**< Unknown type. */ NNP_FLOAT32 = 0x000001, /**< Float. */ NNP_FLOAT64 = 0x000002, /**< Double. */ NNP_INT8 = 0x000004, /**< Char. */ NNP_INT16 = 0x000008, /**< Short. */ NNP_INT32 = 0x000010, /**< Int. */ NNP_UINT8 = 0x000020, /**< Unsigned Char. */ NNP_UINT16 = 0x000040, /**< Unsigned Short. */ NNP_UINT32 = 0x000080, /**< Unsigned Int. */ NNP_LIST_UINT8_UINT32 = 0x000100, /**< List (size Unsigned Char) of Unsigned Int. */ NNP_LIST_INT8_UINT32 = 0x000200, /**< List (size Char) of Unsigned Int. */ NNP_LIST_UINT8_INT32 = 0x000400, /**< List (size Unsigned Char) of Int. */ NNP_LIST_INT8_INT32 = 0x000800, /**< List (size Char) of Int. */ NNP_LIST_UINT8_FLOAT32 = 0x001000, /**< List (size Unsigned Char) of Float. */ NNP_LIST_INT8_FLOAT32 = 0x002000, /**< List (size Char) of Float. */ NNP_LIST_UINT8_FLOAT64 = 0x004000, /**< List (size Unsigned Char) of Double. */ NNP_LIST_INT8_FLOAT64 = 0x008000, /**< List (size Char) of Double. */ NNP_LIST_UINT8_UINT8 = 0x010000, /**< List (size Unsigned Char) of Unsigned Char. */ NNP_LIST_INT8_UINT8 = 0x020000, /**< List (size Char) of Unsigned Char. */ NNP_LIST_UINT8_INT8 = 0x040000, /**< List (size Unsigned Char) of Char. */ NNP_LIST_INT8_INT8 = 0x080000, /**< List (size Char) of Char. */ NNP_LIST_UINT8_UINT16 = 0x100000, /**< List (size Unsigned Char) of Unsigned Short. */ NNP_LIST_INT8_UINT16 = 0x200000, /**< List (size Char) of Unsigned Short. */ NNP_LIST_UINT8_INT16 = 0x400000, /**< List (size Unsigned Char) of Short. */ NNP_LIST_INT8_INT16 = 0x800000 /**< List (size Char) of Short. */ } PlyType; template inline std::size_t hashEnum(const TEnum & value) { const std::hash h{}; return h((unsigned int)value); } } // end namespace nanoply // Cast of an enum to an unsigned int (maybe only for Android) namespace std { template <> struct hash { std::size_t operator () (const nanoply::PlyElemEntity & t) const { return nanoply::hashEnum(t); } }; template <> struct hash { std::size_t operator () (const nanoply::PlyEntity & t) const { return nanoply::hashEnum(t); } }; template <> struct hash { std::size_t operator () (const nanoply::PlyType & t) const { return nanoply::hashEnum(t); } }; } // end namespace std namespace nanoply { /** * @cond HIDDEN_SYMBOLS */ template < size_t T> struct SizeT {}; typedef std::vector NameVector; typedef std::unordered_map TypeMap; typedef std::unordered_map::iterator TypeMapIterator; typedef std::unordered_map EntityMap; typedef std::unordered_map::iterator EntityMapIterator; typedef std::unordered_map ElementMap; typedef std::unordered_map::iterator ElementMapIterator; /* Names used for the PlyType */ static TypeMap mapType({ { PlyType::NNP_UNKNOWN_TYPE, NameVector({ "unknonw" }) }, { PlyType::NNP_FLOAT32, NameVector({ "float", "float32" }) }, { PlyType::NNP_FLOAT64, NameVector({ "double", "float64" }) }, { PlyType::NNP_INT8, NameVector({ "char", "int8" }) }, { PlyType::NNP_INT16, NameVector({ "short", "int16" }) }, { PlyType::NNP_INT32, NameVector({ "int", "int32" }) }, { PlyType::NNP_UINT8, NameVector({ "uchar", "uint8" }) }, { PlyType::NNP_UINT16, NameVector({ "ushort", "uint16" }) }, { PlyType::NNP_UINT32, NameVector({ "uint", "uint32" }) }, { PlyType::NNP_LIST_UINT8_UINT32, NameVector({ "list uchar uint", "list uint8 uint32" }) }, { PlyType::NNP_LIST_INT8_UINT32, NameVector({ "list char uint", "list int8 uint32" }) }, { PlyType::NNP_LIST_UINT8_INT32, NameVector({ "list uchar int", "list uint8 int32" }) }, { PlyType::NNP_LIST_INT8_INT32, NameVector({ "list char int", "list int8 int32" }) }, { PlyType::NNP_LIST_UINT8_FLOAT32, NameVector({ "list uchar float", "list uint8 float32" }) }, { PlyType::NNP_LIST_INT8_FLOAT32, NameVector({ "list char float", "list int8 float32" }) }, { PlyType::NNP_LIST_UINT8_FLOAT64, NameVector({ "list uchar double", "list uint8 float64" }) }, { PlyType::NNP_LIST_INT8_FLOAT64, NameVector({ "list char double", "list int8 float64" }) }, { PlyType::NNP_LIST_UINT8_UINT8, NameVector({ "list uchar uchar", "list uint8 uint8" }) }, { PlyType::NNP_LIST_INT8_UINT8, NameVector({ "list char uchar", "list int8 uint8" }) }, { PlyType::NNP_LIST_UINT8_INT8, NameVector({ "list uchar char", "list uint8 int8" }) }, { PlyType::NNP_LIST_INT8_INT8, NameVector({ "list char char", "list int8 int8" }) }, { PlyType::NNP_LIST_UINT8_UINT16, NameVector({ "list uchar ushort", "list uint8 uint16" }) }, { PlyType::NNP_LIST_INT8_UINT16, NameVector({ "list char ushort", "list int8 uint16" }) }, { PlyType::NNP_LIST_UINT8_INT16, NameVector({ "list uchar short", "list uint8 int16" }) }, { PlyType::NNP_LIST_INT8_INT16, NameVector({ "list char short", "list int8 int16" }) } }); /* Names used for the PlyProperty */ static EntityMap mapProp({ { PlyEntity::NNP_UNKNOWN_ENTITY, NameVector({ "unknonw" }) }, { PlyEntity::NNP_PX, NameVector({ "x", "px", "posx" }) }, { PlyEntity::NNP_PY, NameVector({ "y", "py", "posy" }) }, { PlyEntity::NNP_PZ, NameVector({ "z", "pz", "posz" }) }, { PlyEntity::NNP_PXYZ, NameVector({ "x y z", "px py pz", "posx posy posz" }) }, { PlyEntity::NNP_NX, NameVector({ "nx", "normalx" }) }, { PlyEntity::NNP_NY, NameVector({ "ny", "normaly" }) }, { PlyEntity::NNP_NZ, NameVector({ "nz", "normalz" }) }, { PlyEntity::NNP_NXYZ, NameVector({ "nx ny nz", "normalx normaly normalz" }) }, { PlyEntity::NNP_CR, NameVector({ "red", "diffuse_red", "r", "diffuse_r" }) }, { PlyEntity::NNP_CG, NameVector({ "green", "diffuse_green", "g", "diffuse_g" }) }, { PlyEntity::NNP_CB, NameVector({ "blue", "diffuse_blue", "b", "diffuse_b" }) }, { PlyEntity::NNP_CA, NameVector({ "alpha", "diffuse_alpha", "a", "diffuse_a" }) }, { PlyEntity::NNP_CRGB, NameVector({ "rgb", "diffuse_rgb" }) }, { PlyEntity::NNP_CRGBA, NameVector({ "rgba", "diffuse_rgba" }) }, { PlyEntity::NNP_DENSITY, NameVector({ "radius", "density" }) }, { PlyEntity::NNP_SCALE, NameVector({ "scale", "value" }) }, { PlyEntity::NNP_TEXTUREU, NameVector({ "texture_u", "u", "s" }) }, { PlyEntity::NNP_TEXTUREV, NameVector({ "texture_v", "v", "t" }) }, { PlyEntity::NNP_TEXTURE2D, NameVector({ "texture_uv", "uv" }) }, { PlyEntity::NNP_TEXTUREW, NameVector({ "texture_w", "w" }) }, { PlyEntity::NNP_TEXTURE3D, NameVector({ "texture_uvw", "uvw" }) }, { PlyEntity::NNP_TEXTUREINDEX, NameVector({ "texnumber", "texid" }) }, { PlyEntity::NNP_QUALITY, NameVector({ "quality", "confidence" }) }, { PlyEntity::NNP_REFLECTANCE, NameVector({ "reflectance" }) }, { PlyEntity::NNP_BITFLAG, NameVector({ "flags" }) }, { PlyEntity::NNP_K1, NameVector({ "k1" }) }, { PlyEntity::NNP_K2, NameVector({ "k2" }) }, { PlyEntity::NNP_KG, NameVector({ "k" }) }, { PlyEntity::NNP_KH, NameVector({ "h" }) }, { PlyEntity::NNP_K1DIR, NameVector({ "k1dir" }) }, { PlyEntity::NNP_K2DIR, NameVector({ "k2dir" }) }, { PlyEntity::NNP_EDGE_V1, NameVector({ "vertex1", "v1" }) }, { PlyEntity::NNP_EDGE_V2, NameVector({ "vertex2", "v2" }) }, { PlyEntity::NNP_FACE_VERTEX_LIST, NameVector({ "vertex_index", "vertex_indices" }) }, { PlyEntity::NNP_FACE_WEDGE_COLOR, NameVector({ "color" }) }, { PlyEntity::NNP_FACE_WEDGE_NORMAL, NameVector({ "normal" }) }, { PlyEntity::NNP_FACE_WEDGE_TEX, NameVector({ "texcoord" }) } }); /* Names used for the PlyElement */ static ElementMap mapElem({ { PlyElemEntity::NNP_UNKNOWN_ELEM, NameVector({ "unknonw" }) }, { PlyElemEntity::NNP_VERTEX_ELEM, NameVector({ "vertex" }) }, { PlyElemEntity::NNP_EDGE_ELEM, NameVector({ "edge" }) }, { PlyElemEntity::NNP_FACE_ELEM, NameVector({ "face" }) }, }); /* Returns the vector of possible name for the input PlyEntity */ static inline const NameVector& PlyPropertyName(PlyEntity ent) { static NameVector emptyVec; if (mapProp.find(ent) != mapProp.end()) return mapProp[ent]; return emptyVec; }; /* Returns the vector of possible name for the input PlyType */ static inline const NameVector& PlyTypeName(PlyType ent) { static NameVector emptyVec; if (mapType.find(ent) != mapType.end()) return mapType[ent]; return emptyVec; }; /* Returns the vector of possible name for the input PlyElement */ static inline const NameVector& PlyElementName(PlyElemEntity ent) { static NameVector emptyVec; if (mapElem.find(ent) != mapElem.end()) return mapElem[ent]; return emptyVec; }; /* Return 1 for little endian, 0 for big endian*/ inline int checkEndianness() { unsigned int x = 1; char *c = (char*)&x; return (int)*c; }; /* Adjust the endianess of the input buffer*/ inline void adjustEndianess(unsigned char* buffer, int typeSize, int count) { for (int i = 0; i < count; i++) { int offset = i*typeSize; for (int j = 0; j < typeSize / 2; j++) { unsigned char temp = buffer[offset + j]; buffer[offset + j] = buffer[offset + typeSize - 1 - j]; buffer[offset + typeSize - 1 - j] = temp; } } } /** * @endcond */ /** PLY File. * Class to manage the read and write of a PLY file using a memory buffer. */ class PlyFile { private: std::fstream fileStream; /**< Stream. */ char mode; /**< Mode of the stream (0 = read, 1 = write). */ int64_t bufferSize; /**< Size of the buffer. */ int64_t bufferOffset; /**< Offset for the next operation. */ int64_t maxSize; /**< Maximum size of the stream. */ char* buffer; /**< Buffer. */ public: PlyFile(); ~PlyFile(); /** * Open the file in read mode. * * @param filename name of the file. * @return If successful returns true. Otherwise, it returns false. */ inline bool OpenFileToRead(const std::string &filename); /** * Open the file in write mode. * * @param filename name of the file. * @return If successful returns true. Otherwise, it returns false. */ inline bool OpenFileToWrite(const std::string &filename); /** * Read the next header line. * * @param line read line. * @param last true if the line is the last of header. * @return If successful returns true. Otherwise, it returns false. */ inline bool NextHeaderLine(std::string &line, bool &last); /** * Write the line in the header. * * @param line line to write. * @return If successful returns true. Otherwise, it returns false. */ inline bool WriteHeaderLine(const std::string &line); /** * Read binary data from the file. * * @param dest pointer where to copy the data. * @param nByte number of byte to read. * @return If successful returns true. Otherwise, it returns false. */ inline bool ReadBinaryData(char * & dest, int nByte); /** * Read ASCII data from the file. * * @param dest reference to the container of the data. * @return If successful returns true. Otherwise, it returns false. */ template inline bool ReadAsciiData(T &dest); /** * Write binary data in the file. * * @param src pointer to the data to write. * @param nByte number of byte to write. * @return If successful returns true. Otherwise, it returns false. */ inline bool WriteBinaryData(void *src, int nByte); /** * Write ASCII data in the file. * * @param src reference to the container of the data. * @return If successful returns true. Otherwise, it returns false. */ template inline bool WriteAsciiData(const T &src); /** * Set the maximum size of the buffer. * * @return size size of the buffer. */ inline void SetBufferSize(int64_t size); /** * Force the write of the buffer in the file. */ inline void Flush(); }; inline PlyFile::PlyFile() { buffer = NULL; maxSize = 10 * (1 << 20); mode = -1; } inline PlyFile::~PlyFile() { Flush(); if (buffer != NULL) delete[] buffer; if (fileStream.is_open()) fileStream.close(); } inline bool PlyFile::OpenFileToRead(const std::string& filename) { if (fileStream.is_open()) fileStream.close(); mode = 0; fileStream.open(filename, std::fstream::in | std::fstream::binary); if (fileStream.fail()) return false; bufferOffset = 0; return true; } inline bool PlyFile::OpenFileToWrite(const std::string& filename) { if (fileStream.is_open()) fileStream.close(); mode = 1; fileStream.open(filename, std::fstream::out | std::fstream::binary); if (fileStream.fail()) return false; bufferOffset = 0; //fileStream.setf(std::ios::fixed, std::ios::floatfield); //fileStream.precision(7); return true; } inline bool PlyFile::NextHeaderLine(std::string& line, bool& last) { if (mode != 0) return false; std::getline(fileStream, line); std::transform(line.begin(), line.end(), line.begin(), ::tolower); last = false; if (line == "end_header") last = true; return true; } inline bool PlyFile::WriteHeaderLine(const std::string& line) { if (mode != 1) return false; fileStream << line; return true; } inline bool PlyFile::ReadBinaryData(char * & dest, int nByte) { if (mode != 0) return false; if (buffer == NULL) { buffer = new char[maxSize]; fileStream.read(buffer, maxSize); bufferSize = fileStream.gcount(); bufferOffset = 0; } else if (bufferOffset + nByte > bufferSize) { const size_t lastByte = bufferSize - bufferOffset; memcpy(buffer, &buffer[bufferOffset], lastByte); fileStream.read(&buffer[lastByte], maxSize - lastByte); bufferSize = fileStream.gcount() + lastByte; bufferOffset = 0; } //memcpy(dest, &buffer[bufferOffset], nByte); dest = buffer + bufferOffset; bufferOffset += nByte; return true; } template inline bool PlyFile::ReadAsciiData(T& dest) { if (mode != 0) return false; fileStream >> dest; return true; } inline bool PlyFile::WriteBinaryData(void* src, int nByte) { if (mode != 1) return false; if (buffer == NULL) { buffer = new char[maxSize]; bufferSize = maxSize; bufferOffset = 0; } else if (bufferOffset + nByte > bufferSize) { fileStream.write(buffer, bufferOffset); bufferOffset = 0; } memcpy(&buffer[bufferOffset], src, nByte); bufferOffset += nByte; return true; } template inline bool PlyFile::WriteAsciiData(const T& src) { if (mode != 1) return false; fileStream << src; return true; } inline void PlyFile::SetBufferSize(int64_t size) { maxSize = size; } inline void PlyFile::Flush() { if (mode == 1) fileStream.write(buffer, bufferOffset); } /** PLY Property. * Define a PLY property (entity and type). */ class PlyProperty { public: std::string name; /**< Property name. */ PlyType type; /**< Property type. */ PlyEntity elem; /**< Property entity. */ bool validToWrite; /**< Property validity (necessary to write the header). */ /** * Constructor that sets the type and the entity of a standard PLY property. * * @param _t Property type. * @param _e Property entity. */ inline PlyProperty(PlyType _t, PlyEntity _e) :type(_t), elem(_e), name(PlyPropertyName(_e)[0]), validToWrite(false){} /** * Constructor that sets the type, the entity and the name of a standard PLY property. * * @param _t Property type. * @param _e Property entity. * @param _n Property name. */ inline PlyProperty(PlyType _t, PlyEntity _e, std::string _n) :type(_t), elem(_e), name(_n), validToWrite(false){} /** * Constructor that sets the type and the name of a custom PLY property. * * @param _t Property type. * @param _n Property name. */ inline PlyProperty(PlyType _t, std::string _n) :type(_t), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_n), validToWrite(false){} /** * Get the description string of the property entity. * * @return Description string of the property entity. */ inline const char* EntityStr(); /** * Get the name of the property entity. * * @return Name of the property entity. */ inline const char* EntityName(); /** * Get the description string of the property type. * * @return Description string of the property type. */ inline const char* TypeStr(); /** * Get the size in byte of the property type. * * @return Size in byte of the property type. */ inline int TypeSize(); /** * Get the number of component of the property entity. * * @return Number of component. */ inline int CountValue(); /** * Check if the property type is signed or unsigned. * * @return true = signed, false = unsigned. */ inline bool IsSigned(); /** * Skip the property in an Ascii file. * * @param file Opened file. * @return If successful returns true. Otherwise, it returns false. */ inline bool SkipAsciiPropertyInFile(PlyFile &file); /** * Skip the property in a binary file. * * @param file Opened file. * @return If successful returns true. Otherwise, it returns false. */ inline bool SkipBinaryPropertyInFile(PlyFile &file); /** * Write the property string in the header of the PLY file. * * @param file Opened file. * @return If successful returns true. Otherwise, it returns false. */ inline bool WriteHeader(PlyFile &file); }; inline const char* PlyProperty::EntityStr() { switch (this->elem) { case NNP_UNKNOWN_ENTITY: return "NNP_UNKNOWN_ENTITY "; case NNP_PX: return "NNP_PX "; case NNP_PY: return "NNP_PY "; case NNP_PZ: return "NNP_PZ "; case NNP_PXYZ: return "NNP_PXYZ "; case NNP_NX: return "NNP_NX "; case NNP_NY: return "NNP_NY "; case NNP_NZ: return "NNP_NZ "; case NNP_NXYZ: return "NNP_NXYZ "; case NNP_CR: return "NNP_CR "; case NNP_CG: return "NNP_CG "; case NNP_CB: return "NNP_CB "; case NNP_CRGB: return "NNP_CRGB "; case NNP_CA: return "NNP_CA "; case NNP_CRGBA: return "NNP_CRGBA "; case NNP_DENSITY: return "NNP_DENSITY "; case NNP_SCALE: return "NNP_SCALE "; case NNP_QUALITY: return "NNP_QUALITY "; case NNP_REFLECTANCE: return "NNP_REFLECTANCE "; case NNP_TEXTUREU: return "NNP_TEXTUREU "; case NNP_TEXTUREV: return "NNP_TEXTUREV "; case NNP_TEXTURE2D: return "NNP_TEXTURE2D "; case NNP_TEXTUREW: return "NNP_TEXTUREW "; case NNP_TEXTURE3D: return "NNP_TEXTURE3D "; case NNP_TEXTUREINDEX: return "NNP_TEXTUREINDEX "; case NNP_BITFLAG: return "NNP_BITFLAG "; case NNP_K1: return "NNP_K1 "; case NNP_K2: return "NNP_K2 "; case NNP_KG: return "NNP_K "; case NNP_KH: return "NNP_H "; case NNP_K1DIR: return "NNP_K1DIR "; case NNP_K2DIR: return "NNP_K2DIR "; case NNP_EDGE_V1: return "NNP_EDGE_V1 "; case NNP_EDGE_V2: return "NNP_EDGE_V2 "; case NNP_FACE_VERTEX_LIST: return "NNP_FACE_VERTEX_LIST "; case NNP_FACE_WEDGE_COLOR: return "NNP_FACE_WEDGE_COLOR "; case NNP_FACE_WEDGE_NORMAL: return "NNP_FACE_WEDGE_NORMAL"; case NNP_FACE_WEDGE_TEX: return "NNP_FACE_WEDGE_TEX "; default: assert(0); break; } return 0; } inline const char* PlyProperty::EntityName() { if (this->elem == PlyEntity::NNP_UNKNOWN_ENTITY) return (*this).name.c_str(); #ifdef USE_NOSTANDARDPLY_OUTPUT if (this->elem == PlyEntity::NNP_SCALE) return PlyPropertyName(this->elem)[1].c_str(); if (this->elem == PlyEntity::NNP_QUALITY) return PlyPropertyName(this->elem)[1].c_str(); if (this->elem == PlyEntity::NNP_CR) return PlyPropertyName(this->elem)[1].c_str(); if (this->elem == PlyEntity::NNP_CB) return PlyPropertyName(this->elem)[1].c_str(); if (this->elem == PlyEntity::NNP_CG) return PlyPropertyName(this->elem)[1].c_str(); if (this->elem == PlyEntity::NNP_CA) return PlyPropertyName(this->elem)[1].c_str(); #endif const std::vector& vect = PlyPropertyName(this->elem); if (vect.size() > 0) return vect[0].c_str(); assert(0); return 0; } inline const char* PlyProperty::TypeStr() { switch (this->type) { case NNP_UNKNOWN_TYPE: return "NNP_UNKNOWN_TYPE "; case NNP_FLOAT32: return "NNP_FLOAT32 "; case NNP_FLOAT64: return "NNP_FLOAT64 "; case NNP_INT8: return "NNP_INT8 "; case NNP_INT16: return "NNP_INT16 "; case NNP_INT32: return "NNP_INT32 "; case NNP_UINT8: return "NNP_UINT8 "; case NNP_UINT16: return "NNP_UINT16 "; case NNP_UINT32: return "NNP_UINT32 "; case NNP_LIST_UINT8_UINT32: return "NNP_LIST_UINT8_UINT32 "; case NNP_LIST_INT8_UINT32: return "NNP_LIST_INT8_UINT32 "; case NNP_LIST_UINT8_INT32: return "NNP_LIST_UINT8_INT32 "; case NNP_LIST_INT8_INT32: return "NNP_LIST_INT8_INT32 "; case NNP_LIST_UINT8_FLOAT32: return "NNP_LIST_UINT8_FLOAT32 "; case NNP_LIST_INT8_FLOAT32: return "NNP_LIST_INT8_FLOAT32 "; case NNP_LIST_UINT8_FLOAT64: return "NNP_LIST_UINT8_FLOAT64 "; case NNP_LIST_INT8_FLOAT64: return "NNP_LIST_INT8_FLOAT64 "; case NNP_LIST_UINT8_UINT8: return "NNP_LIST_UINT8_UINT8 "; case NNP_LIST_INT8_UINT8: return "NNP_LIST_INT8_UINT8 "; case NNP_LIST_UINT8_INT8: return "NNP_LIST_UINT8_INT8 "; case NNP_LIST_INT8_INT8: return "NNP_LIST_INT8_INT8 "; case NNP_LIST_UINT8_UINT16: return "NNP_LIST_UINT8_UINT16 "; case NNP_LIST_INT8_UINT16: return "NNP_LIST_INT8_UINT16 "; case NNP_LIST_UINT8_INT16: return "NNP_LIST_UINT8_INT16 "; case NNP_LIST_INT8_INT16: return "NNP_LIST_INT8_INT16 "; default: assert(0); break; } return 0; } inline int PlyProperty::TypeSize() { switch (this->type) { case NNP_UNKNOWN_TYPE: return 0; case NNP_INT8: case NNP_UINT8: return sizeof(char); case NNP_INT16: case NNP_UINT16: return sizeof(short); case NNP_FLOAT32: case NNP_INT32: case NNP_UINT32: return sizeof(int); case NNP_FLOAT64: return sizeof(double); case NNP_LIST_UINT8_UINT32: case NNP_LIST_INT8_UINT32: case NNP_LIST_UINT8_INT32: case NNP_LIST_INT8_INT32: return sizeof(int); case NNP_LIST_UINT8_UINT16: case NNP_LIST_INT8_UINT16: case NNP_LIST_UINT8_INT16: case NNP_LIST_INT8_INT16: return sizeof(short); case NNP_LIST_UINT8_UINT8: case NNP_LIST_INT8_UINT8: case NNP_LIST_UINT8_INT8: case NNP_LIST_INT8_INT8: return sizeof(char); case NNP_LIST_UINT8_FLOAT32: case NNP_LIST_INT8_FLOAT32: return sizeof(float); case NNP_LIST_UINT8_FLOAT64: case NNP_LIST_INT8_FLOAT64: return sizeof(double); default: assert(0); break; } return 0; } inline bool PlyProperty::IsSigned() { switch (this->type) { case NNP_INT8: case NNP_INT16: case NNP_INT32: case NNP_FLOAT32: case NNP_FLOAT64: case NNP_LIST_INT8_UINT32: case NNP_LIST_INT8_INT32: case NNP_LIST_INT8_UINT16: case NNP_LIST_INT8_INT16: case NNP_LIST_INT8_UINT8: case NNP_LIST_INT8_INT8: case NNP_LIST_INT8_FLOAT32: case NNP_LIST_INT8_FLOAT64: return true; case NNP_UINT8: case NNP_UINT16: case NNP_UINT32: case NNP_LIST_UINT8_UINT32: case NNP_LIST_UINT8_INT32: case NNP_LIST_UINT8_UINT16: case NNP_LIST_UINT8_INT16: case NNP_LIST_UINT8_UINT8: case NNP_LIST_UINT8_INT8: case NNP_LIST_UINT8_FLOAT32: case NNP_LIST_UINT8_FLOAT64: return false; default: return false; } } inline int PlyProperty::CountValue() { if (this->elem == NNP_CRGB || this->elem == NNP_NXYZ || this->elem == NNP_PXYZ || this->elem == NNP_TEXTURE3D) return 3; else if (this->elem == NNP_CRGBA) return 4; else if (this->elem == NNP_TEXTURE2D) return 2; return 1; } inline bool PlyProperty::SkipAsciiPropertyInFile(PlyFile &file) { int count = CountValue(); if (this->type >= NNP_LIST_UINT8_UINT32) file.ReadAsciiData(count); switch (type) { case NNP_INT8: case NNP_INT16: case NNP_INT32: case NNP_LIST_UINT8_INT32: case NNP_LIST_INT8_INT32: case NNP_LIST_UINT8_INT16: case NNP_LIST_INT8_INT16: case NNP_LIST_UINT8_INT8: case NNP_LIST_INT8_INT8: { int* temp = new int[count]; for (int i = 0; i < count; i++) file.ReadAsciiData(temp[i]); delete[] temp; break; } case NNP_UINT8: case NNP_UINT16: case NNP_UINT32: case NNP_LIST_UINT8_UINT32: case NNP_LIST_INT8_UINT32: case NNP_LIST_UINT8_UINT16: case NNP_LIST_INT8_UINT16: case NNP_LIST_UINT8_UINT8: case NNP_LIST_INT8_UINT8: { unsigned int* temp = new unsigned int[count]; for (int i = 0; i < count; i++) file.ReadAsciiData(temp[i]); delete[] temp; break; } case NNP_FLOAT32: case NNP_LIST_UINT8_FLOAT32: case NNP_LIST_INT8_FLOAT32: { float* temp = new float[count]; for (int i = 0; i < count; i++) file.ReadAsciiData(temp[i]); delete[] temp; break; } case NNP_FLOAT64: case NNP_LIST_UINT8_FLOAT64: case NNP_LIST_INT8_FLOAT64: { double* temp = new double[count]; for (int i = 0; i < count; i++) file.ReadAsciiData(temp[i]); delete[] temp; break; } } return true; } inline bool PlyProperty::SkipBinaryPropertyInFile(PlyFile& file) { char * temp = nullptr; int count = CountValue(); if (this->type >= NNP_LIST_UINT8_UINT32) { int size; if (this->IsSigned()) { file.ReadBinaryData(temp, sizeof(char)); size = this->TypeSize() * int(*(reinterpret_cast(temp))); } else { file.ReadBinaryData(temp, sizeof(char)); size = this->TypeSize() * int(*(reinterpret_cast(temp))); } file.ReadBinaryData(temp, size); } else { int size = this->TypeSize() * count; file.ReadBinaryData(temp, size); } return true; } inline bool PlyProperty::WriteHeader(PlyFile& file) { if (!validToWrite) return true; std::string name, type; type = PlyTypeName(this->type)[0]; name = this->EntityName(); std::vector v; switch (this->elem) { case NNP_PXYZ: { v.push_back(PlyPropertyName(NNP_PX)[0]); v.push_back(PlyPropertyName(NNP_PY)[0]); v.push_back(PlyPropertyName(NNP_PZ)[0]); break; } case NNP_NXYZ: { v.push_back(PlyPropertyName(NNP_NX)[0]); v.push_back(PlyPropertyName(NNP_NY)[0]); v.push_back(PlyPropertyName(NNP_NZ)[0]); break; } case NNP_CRGB: { v.push_back(PlyPropertyName(NNP_CR)[0]); v.push_back(PlyPropertyName(NNP_CG)[0]); v.push_back(PlyPropertyName(NNP_CB)[0]); break; } case NNP_CRGBA: { v.push_back(PlyPropertyName(NNP_CR)[0]); v.push_back(PlyPropertyName(NNP_CG)[0]); v.push_back(PlyPropertyName(NNP_CB)[0]); v.push_back(PlyPropertyName(NNP_CA)[0]); break; } case NNP_TEXTURE2D: { v.push_back(PlyPropertyName(NNP_TEXTUREU)[0]); v.push_back(PlyPropertyName(NNP_TEXTUREV)[0]); break; } case NNP_TEXTURE3D: { v.push_back(PlyPropertyName(NNP_TEXTUREU)[0]); v.push_back(PlyPropertyName(NNP_TEXTUREV)[0]); v.push_back(PlyPropertyName(NNP_TEXTUREW)[0]); break; } default: v.push_back(name); } for (int i = 0; i < v.size(); i++) { std::stringstream s; s << "property " << type << " " << v[i] << "\n"; if (!file.WriteHeaderLine(s.str())) return false; } return true; } /** PLY Element. * Define a PLY Element as a collection of properties. */ class PlyElement { public: std::string name; /**< Name of the elment in the PLY header (for example "vertex", "face", ect.). */ PlyElemEntity plyElem; /**< Ply element entity. */ size_t cnt; /**< Number of instances of the elment in the PLY file. */ std::vector propVec; /**< Collection of properties that define the element. */ bool validToWrite; /**< Element validity (necessary to write the header). */ /** * Default Constructor */ inline PlyElement() :validToWrite(false){}; /** * Constructor that sets the name, the properties and the number of instances of the element. * * @param _name Name of the element. * @param prop Vector of properties. * @param nElem Number of instances. */ inline PlyElement(std::string& _name, std::vector &prop, size_t nElem) :name(_name), cnt(nElem), propVec(prop), plyElem(PlyElemEntity::NNP_UNKNOWN_ELEM), validToWrite(false){}; /** * Constructor that sets the entity, the properties and the number of instances of the element. * * @param ent Element entity. * @param prop Vector of properties. * @param nElem Number of instances. */ inline PlyElement(PlyElemEntity ent, std::vector &prop, size_t nElem) :name(PlyElementName(ent)[0]), cnt(nElem), propVec(prop), plyElem(ent), validToWrite(false){}; /** * Parse the input line and add the properties to the element. * It assumes that the passed line has the folowing structure: "property PLYTYPE PLYELEMENT" * * @param line Input line. * @return If successful returns true. Otherwise, it returns false. */ inline bool AddProperty(std::string &line); /** * Initialize an element from the header line. * * @param elemStr String with the element definition. * @param propStr Strings with the property definition. * @return If successful returns true. Otherwise, it returns false. */ inline bool InitFromHeader(std::string &elemStr, std::vector &propStr); /** * Write the element descriport in the file header. * * @param file Ply file. * @return If successful returns true. Otherwise, it returns false. */ inline bool WriteHeader(PlyFile &file); /** * Skip the element in an Ascii file. * * @param file Ply file * @return If successful returns true. Otherwise, it returns false. */ inline bool SkipAsciiElementsInFile(PlyFile &file); /** * Skip the element in a binary file. * * @param file Ply file. * @return If successful returns true. Otherwise, it returns false. */ inline bool SkipBinaryElementsInFile(PlyFile &file); /** * Check if the input entity is in the property of the element. * * @param entity Input entity. * @return If successful returns true. Otherwise, it returns false. */ inline bool Contains(NNP_ENTITY entity); }; inline bool PlyElement::InitFromHeader(std::string &elemStr, std::vector &propStr) { char* token; char* tempStr = &elemStr[0]; token = strtok(tempStr, " \t"); if (strstr(token, "element") == NULL) return false; token = strtok(0, " \t\n"); name = std::string(token); plyElem = PlyElemEntity::NNP_UNKNOWN_ELEM; ElementMapIterator iter = mapElem.begin(); bool found = false; while (iter != mapElem.end()) { NameVector& v = (*iter).second; for (size_t i = 0; i < v.size(); i++) { if (v[i] == name) { found = true; break; } } if (found) { plyElem = (*iter).first; break; } iter++; } token = strtok(0, " \t\n"); cnt = atoi(token); for (size_t i = 0; i < propStr.size(); i++) if (!AddProperty(propStr[i])) return false; unsigned int mask = 0; for (size_t i = 0; i < propVec.size(); i++) mask |= propVec[i].elem; std::vector compactPropVec; for (size_t i = 0; i < propVec.size(); i++) { switch (propVec[i].elem) { case NNP_NX: case NNP_NY: if ((mask & NNP_NXYZ) != NNP_NXYZ) compactPropVec.push_back(propVec[i]); break; case NNP_NZ: if ((mask & NNP_NXYZ) != NNP_NXYZ) compactPropVec.push_back(propVec[i]); else compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_NXYZ)); break; case NNP_PX: case NNP_PY: if ((mask & NNP_PXYZ) != NNP_PXYZ) compactPropVec.push_back(propVec[i]); break; case NNP_PZ: if ((mask & NNP_PXYZ) != NNP_PXYZ) compactPropVec.push_back(propVec[i]); else compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_PXYZ)); break; case NNP_CR: case NNP_CG: if (((mask & NNP_CRGB) != NNP_CRGB) && ((mask & NNP_CRGBA) != NNP_CRGBA)) compactPropVec.push_back(propVec[i]); break; case NNP_CB: if (((mask & NNP_CRGB) != NNP_CRGB) & ((mask & NNP_CRGBA) != NNP_CRGBA)) compactPropVec.push_back(propVec[i]); else if (((mask & NNP_CRGB) == NNP_CRGB) & ((mask & NNP_CRGBA) != NNP_CRGBA)) compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_CRGB)); break; case NNP_CA: if (((mask & NNP_CRGB) != NNP_CRGB) & ((mask & NNP_CRGBA) != NNP_CRGBA)) compactPropVec.push_back(propVec[i]); else if ((mask & NNP_CRGBA) == NNP_CRGBA) compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_CRGBA)); break; case NNP_TEXTUREU: if (((mask & NNP_TEXTURE2D) != NNP_TEXTURE2D) & ((mask & NNP_TEXTURE3D) != NNP_TEXTURE3D)) compactPropVec.push_back(propVec[i]); break; case NNP_TEXTUREV: if (((mask & NNP_TEXTURE2D) != NNP_TEXTURE2D) & ((mask & NNP_TEXTURE3D) != NNP_TEXTURE3D)) compactPropVec.push_back(propVec[i]); else if (((mask & NNP_TEXTURE2D) == NNP_TEXTURE2D) & ((mask & NNP_TEXTURE3D) != NNP_TEXTURE3D)) compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_TEXTURE2D)); break; case NNP_TEXTUREW: if (((mask & NNP_TEXTURE2D) != NNP_TEXTURE2D) & ((mask & NNP_TEXTURE3D) != NNP_TEXTURE3D)) compactPropVec.push_back(propVec[i]); else if ((mask & NNP_TEXTURE3D) == NNP_TEXTURE3D) compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_TEXTURE3D)); break; default: compactPropVec.push_back(propVec[i]); break; } } propVec.clear(); propVec = compactPropVec; return true; } inline bool PlyElement::WriteHeader(PlyFile &file) { if (!validToWrite || cnt == 0) return true; bool ok = true; std::stringstream temp; temp << "element " << name << " " << cnt << "\n"; if (file.WriteHeaderLine(temp.str())) { for (int i = 0; i < propVec.size(); i++) ok = propVec[i].WriteHeader(file); } else return false; return ok; } inline bool PlyElement::SkipAsciiElementsInFile(PlyFile &file) { for (int i = 0; i < this->cnt; ++i) for (int j = 0; j < this->propVec.size(); ++j) this->propVec[j].SkipAsciiPropertyInFile(file); return true; } inline bool PlyElement::SkipBinaryElementsInFile(PlyFile &file) { for (int i = 0; i < this->cnt; ++i) for (int j = 0; j < this->propVec.size(); ++j) this->propVec[j].SkipBinaryPropertyInFile(file); return true; } inline bool PlyElement::AddProperty(std::string &line) { char* token; char* tempStr = &line[0]; token = strtok(tempStr, " \t"); if (strstr(token, "property") == NULL) return false; char* typeStr = strtok(0, " \t\n"); char *ty1, *ty2; std::string type; type.append(typeStr); if (strcmp(typeStr, "list") == 0) { ty1 = strtok(0, " \t\n"); ty2 = strtok(0, " \t\n"); type.append(" "); type.append(ty1); type.append(" "); type.append(ty2); } PlyType plyType = PlyType::NNP_UNKNOWN_TYPE; TypeMapIterator iterType = mapType.begin(); bool found = false; while (iterType != mapType.end()) { NameVector& v = (*iterType).second; for (size_t i = 0; i < v.size(); i++) { if (v[i] == type) { found = true; break; } } if (found) { plyType = (*iterType).first; break; } iterType++; } if (plyType == PlyType::NNP_UNKNOWN_TYPE) return false; char* nameStr = strtok(0, " \t\n"); PlyEntity plyEntity = PlyEntity::NNP_UNKNOWN_ENTITY; EntityMapIterator iterEnt = mapProp.begin(); found = false; while (iterEnt != mapProp.end()) { NameVector& v = (*iterEnt).second; for (size_t i = 0; i < v.size(); i++) { if (v[i] == nameStr) { found = true; break; } } if (found) { plyEntity = (*iterEnt).first; break; } iterEnt++; } if (plyEntity != PlyEntity::NNP_UNKNOWN_ENTITY) propVec.push_back(PlyProperty(plyType, plyEntity, nameStr)); else propVec.push_back(PlyProperty(plyType, nameStr)); return true; } inline bool PlyElement::Contains(PlyEntity entity) { for (int i = 0; i < propVec.size(); i++) { if (propVec[i].elem == entity) return true; } return false; } /** PLY header info. * Define the data of the PLY header */ class Info { public: ErrorCode errInfo; /**< Error code returned by the reading of a PLY file. */ bool binary; /**< Boolean about the file format (Binary = true, ASCII = false). */ std::vector elemVec; /**< Elements defined in the header. */ bool bigEndian; /**< Endianess of the binary file. */ std::string filename; /**< Filename. */ std::vector textureFile; /**< Texture file names. */ /** * Default Constructor */ inline Info(); /** * Constructor that reads the header info from a file. * * @param filename Path of the file to read. */ inline Info(const std::string& filename); /** * Load the ply info from the header of the input filename. * * @param filename Path of the file to read. * @return If successful returns true. Otherwise, it returns false. */ inline bool LoadHeader(const std::string& filename); /** * Write the ply info in the header of the input file. * * @param file File to write. * @return If successful returns true. Otherwise, it returns false. */ inline bool WriteHeader(PlyFile& file); /** * Add the ply element to the header. * * @param pe Ply element to write in the header. */ inline void AddPlyElement(PlyElement& pe); /** * Clear the error code. */ inline void Clear() { errInfo = NNP_OK; } /** * Return the number of instances of the element with the input name * * @param name Name of the element. * @return The number of instances */ inline size_t GetElementCount(std::string& name); /** * Return the number of instances of the element with the input element type * * @param e Element type. * @return The number of instances */ inline size_t GetElementCount(PlyElemEntity e); /** * Return the number of vertex instances * * @return The number of vertex instances */ inline size_t GetVertexCount(); /** * Return the number of face instances * * @return The number of face instances */ inline size_t GetFaceCount(); /** * Return the number of edge instances * * @return The number of edge instances */ inline size_t GetEdgeCount(); /** * Return a reference to the element with a specific name * * @param name Name of the element. * @return The reference to the element */ inline PlyElement* GetElement(std::string& name); /** * Return a reference to the element with a specific element type * * @param e Element type. * @return The reference to the element */ inline PlyElement* GetElement(PlyElemEntity e); /** * Return a reference to the vertex element * * @return The reference to the vertex element */ inline PlyElement* GetVertexElement(); /** * Return a reference to the face element * * @return The reference to the face element */ inline PlyElement* GetFaceElement(); /** * Return a reference to the edge element * * @return The reference to the edge element */ inline PlyElement* GetEdgeElement(); }; inline Info::Info() { this->binary = true; this->bigEndian = true; this->Clear(); } inline Info::Info(const std::string& filename) { this->LoadHeader(filename); } inline bool Info::LoadHeader(const std::string& filename) { this->filename = filename; this->errInfo = NNP_OK; std::ifstream input(filename, std::ios::binary); if (!input.good()) { this->errInfo = NNP_UNABLE_TO_OPEN; input.close(); return false; } std::string buffer; std::getline(input, buffer); std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower); if (buffer != "ply") { this->errInfo = NNP_MISSING_HEADER; input.close(); return false; } std::getline(input, buffer); std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower); std::size_t pos = buffer.find("format"); if (pos == std::string::npos) { this->errInfo = NNP_MISSING_FORMAT; input.close(); return false; } if (buffer.find("binary_lit") != std::string::npos) { this->binary = true; this->bigEndian = false; } else if (buffer.find("binary_big") != std::string::npos) { this->binary = true; this->bigEndian = true; } else if (buffer.find("ascii") != std::string::npos) { this->binary = false; this->bigEndian = false; } else { this->errInfo = NNP_MISSING_FORMAT; return false; } std::getline(input, buffer); std::string lowBuffer; lowBuffer.resize(buffer.size()); std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower); while (lowBuffer != "end_header") { if (lowBuffer.find("element") != std::string::npos) { std::string elemStr = lowBuffer; std::vector propStr; do { std::getline(input, buffer); lowBuffer.clear(); lowBuffer.resize(buffer.size()); std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower); pos = lowBuffer.find("property"); if (pos != std::string::npos) propStr.push_back(lowBuffer); } while (pos != std::string::npos); PlyElement pe; if (!pe.InitFromHeader(elemStr, propStr)) { this->errInfo = NNP_INVALID_ELEMENT; input.close(); return false; } elemVec.push_back(pe); } else { if (lowBuffer.find("comment texture") != std::string::npos) textureFile.push_back(buffer.substr(buffer.find(" ", 10) + 1)); std::getline(input, buffer); lowBuffer.clear(); lowBuffer.resize(buffer.size()); std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower); } } input.close(); return true; } inline bool Info::WriteHeader(PlyFile& file) { bool ok = true; ok = file.WriteHeaderLine(std::string("ply\n")); if (this->binary) ok = file.WriteHeaderLine(std::string("format binary_little_endian 1.0\n")); else ok = file.WriteHeaderLine(std::string("format ascii 1.0\n")); ok = file.WriteHeaderLine(std::string("comment nanoply generated\n")); for (int i = 0; i < this->textureFile.size(); i++) ok = file.WriteHeaderLine(std::string("comment TextureFile ") + this->textureFile[i] + "\n"); for (int i = 0; i < this->elemVec.size(); i++) ok = this->elemVec[i].WriteHeader(file); ok = file.WriteHeaderLine(std::string("end_header\n")); return ok; } inline void Info::AddPlyElement(PlyElement& pe) { elemVec.push_back(pe); } inline size_t Info::GetElementCount(std::string& name) { PlyElement* pe = GetElement(name); if (pe != NULL) return pe->cnt; return -1; } inline size_t Info::GetElementCount(PlyElemEntity e) { PlyElement* pe = GetElement(e); if (pe != NULL) return pe->cnt; return -1; } inline size_t Info::GetVertexCount() { return GetElementCount(PlyElemEntity::NNP_VERTEX_ELEM); } inline size_t Info::GetFaceCount() { return GetElementCount(PlyElemEntity::NNP_FACE_ELEM); } inline size_t Info::GetEdgeCount() { return GetElementCount(PlyElemEntity::NNP_EDGE_ELEM); } inline PlyElement* Info::GetElement(std::string& name) { for (int i = 0; i < elemVec.size(); i++) { if (elemVec[i].name == name) return &elemVec[i]; } return NULL; } inline PlyElement* Info::GetElement(PlyElemEntity e) { for (int i = 0; i < elemVec.size(); i++) { if (elemVec[i].plyElem == e) return &elemVec[i]; } return NULL; } inline PlyElement* Info::GetVertexElement() { return GetElement(PlyElemEntity::NNP_VERTEX_ELEM); } inline PlyElement* Info::GetFaceElement() { return GetElement(PlyElemEntity::NNP_FACE_ELEM); } inline PlyElement* Info::GetEdgeElement() { return GetElement(PlyElemEntity::NNP_EDGE_ELEM); } /** Abstract class for the descriptor of a Ply propertie. * The class defines how a PlyProperty is saved in memory. */ class DescriptorInterface { public: int64_t curPos; /**< Position of the next property to read or to write. */ void *base; /**< Pointer to the memory location that contains the data of the property. */ PlyEntity elem; /**< Ply entity managed by the descriptor. */ std::string name; /**< Name of the PlyProperty*/ /** * Constructor of the descriptor. * * @param _e Ply entity managed by the descriptor. * @param _b Pointer to the memory location that contains the data of the property. */ inline DescriptorInterface(PlyEntity _e, void *_b) :curPos(0), elem(_e), base(_b), name(PlyPropertyName(_e)[0]){}; /** * Constructor of the descriptor. * * @param _s Name of the PlyProperty. * @param _b Pointer to the memory location that contains the data of the property. */ inline DescriptorInterface(std::string& _s, void *_b) :curPos(0), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_s), base(_b){}; /** * Restart the descriptor. */ virtual void Restart() = 0; /** * Read the property data from the binary file. * * @param file Input file. * @param prop PLY property to read from the file. * @param fixEndian If true the method adjust the endianess of the data. * @return If successful returns true. Otherwise, it returns false. */ virtual bool ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) = 0; /** * Read the property data from the ascii file. * * @param file Input file. * @param prop PLY property to read from the file. * @return If successful returns true. Otherwise, it returns false. */ virtual bool ReadElemAscii(PlyFile &file, PlyProperty &prop) = 0; /** * Write the property data in the binary file. * * @param file Input file. * @param prop PLY property to write in the file. * @param fixEndian If true the method adjust the endianess of the data. * @return If successful returns true. Otherwise, it returns false. */ virtual bool WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) = 0; /** * Write the property data in the ascii file. * * @param file Input file. * @param prop PLY property to write in the file. * @return If successful returns true. Otherwise, it returns false. */ virtual bool WriteElemAscii(PlyFile &file, PlyProperty &prop) = 0; }; /** Memory descriptor of a Ply element. * The class defines how a PlyElement is saved in memory. */ class ElementDescriptor { public: typedef std::vector PropertyDescriptor; std::string name; /**< Name of the Ply element. */ PlyElemEntity elem; /**< PLY Element Entity. */ PropertyDescriptor dataDescriptor; /**< Vector of property descriptor. */ /** * Constructor of the Ply element descriptor. * * @param _e Ply Element entity managed by the descriptor. */ inline ElementDescriptor(PlyElemEntity _e) : elem(_e), name(PlyElementName(_e)[0]){}; /** * Constructor of the Ply element descriptor. * * @param _s Name of the Ply element managed by the descriptor. */ inline ElementDescriptor(std::string &_s) : elem(PlyElemEntity::NNP_UNKNOWN_ELEM), name(_s){}; /** * Read all the properties of the element from the binary file. * * @param file Input file. * @param elem PLY element to read from the file. * @param fixEndian If true the method adjust the endianess of the data. * @return If successful returns true. Otherwise, it returns false. */ inline bool ReadElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian); /** * Read all the property data of the element from the ascii file. * * @param file Input file. * @param elem PLY element to read from the file. * @return If successful returns true. Otherwise, it returns false. */ inline bool ReadElemAscii(PlyFile &file, PlyElement &elem); /** * Write all the property data of the element in the binary file. * * @param file Input file. * @param elem PLY element to write from the file. * @param fixEndian If true the method adjust the endianess of the data. * @return If successful returns true. Otherwise, it returns false. */ inline bool WriteElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian); /** * Write all the property data of the element in the ascii file. * * @param file Input file. * @param elem PLY element to write from the file. * @return If successful returns true. Otherwise, it returns false. */ inline bool WriteElemAscii(PlyFile &file, PlyElement &elem); /** * Check if the properties defined in input element have a proper data descriport to write in the file. * It sets the variable "validToWrite" for all the Ply properties with a data descriport. * * @param elem PLY element to write from the file. */ inline void CheckDescriptor(PlyElement &elem); private: inline void ExtractDescriptor(PropertyDescriptor &descr, PlyElement &elem); }; inline void ElementDescriptor::ExtractDescriptor(PropertyDescriptor& descr, PlyElement &elem) { for (int j = 0; j < elem.propVec.size(); j++) { PlyProperty& prop = elem.propVec[j]; int i = 0; for (; i < dataDescriptor.size(); i++) { if (dataDescriptor[i]->elem == prop.elem) { if (prop.elem != PlyEntity::NNP_UNKNOWN_ENTITY) { descr.push_back(dataDescriptor[i]); break; } else //if (dataDescriptor[i]->name == prop.name) { std::string name1(dataDescriptor[i]->name); std::string name2(prop.name); std::transform(name1.begin(), name1.end(), name1.begin(), ::tolower); std::transform(name2.begin(), name2.end(), name2.begin(), ::tolower); if (name1 == name2) { descr.push_back(dataDescriptor[i]); break; } } } } if (i == dataDescriptor.size()) descr.push_back(NULL); } } inline bool ElementDescriptor::ReadElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian) { PropertyDescriptor descr; ExtractDescriptor(descr, elem); for (int i = 0; i < elem.cnt; i++) { for (int j = 0; j < elem.propVec.size(); j++) { PlyProperty& prop = elem.propVec[j]; if (descr[j] != NULL) (*descr[j]).ReadElemBinary(file, prop, fixEndian); else prop.SkipBinaryPropertyInFile(file); } } return true; } inline bool ElementDescriptor::ReadElemAscii(PlyFile &file, PlyElement &elem) { PropertyDescriptor descr; ExtractDescriptor(descr, elem); for (int i = 0; i < elem.cnt; i++) { for (int j = 0; j < elem.propVec.size(); j++) { PlyProperty& prop = elem.propVec[j]; if (descr[j] != NULL) (*descr[j]).ReadElemAscii(file, prop); else prop.SkipAsciiPropertyInFile(file); } } return true; } inline bool ElementDescriptor::WriteElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian) { PropertyDescriptor descr; ExtractDescriptor(descr, elem); for (int i = 0; i < elem.cnt; i++) { for (int j = 0; j < elem.propVec.size(); j++) { if (descr[j] != NULL) (*descr[j]).WriteElemBinary(file, elem.propVec[j], fixEndian); } } return true; } inline bool ElementDescriptor::WriteElemAscii(PlyFile &file, PlyElement &elem) { PropertyDescriptor descr; ExtractDescriptor(descr, elem); for (int i = 0; i < elem.cnt; i++) { bool first = true; for (int j = 0; j < elem.propVec.size(); j++) { if (descr[j] != NULL) { if (!first) file.WriteAsciiData(std::string(" ")); else first = false; (*descr[j]).WriteElemAscii(file, elem.propVec[j]); } } file.WriteAsciiData(std::string("\n")); } return true; } inline void ElementDescriptor::CheckDescriptor(PlyElement &elem) { if (elem.propVec.size() == 0) { elem.validToWrite = false; return; } elem.validToWrite = true; PropertyDescriptor descr; ExtractDescriptor(descr, elem); for (int j = 0; j < elem.propVec.size(); j++) { if (descr[j] != NULL) elem.propVec[j].validToWrite = true; } } /** Memory descriptor of a vector of properties. * The class defines how a vector of PlyProperty is saved in memory. * * @tparam CointainerType Type of the container of the property * @tparam VectorSize Number of values stored in the property. * @tparam ScalarType Type of the values stored in the property. */ template class DataDescriptor : public DescriptorInterface { public: inline DataDescriptor(); /** * Constructor of the descriptor. * * @param _e Ply entity managed by the descriptor. * @param _b Pointer to the memory location that contains the data of the property. */ inline DataDescriptor(PlyEntity _e, void *_b) :DescriptorInterface(_e, _b){}; /** * Constructor of the descriptor. * * @param _s Name of the PlyProperty. * @param _b Pointer to the memory location that contains the data of the property. */ inline DataDescriptor(std::string& _s, void *_b) :DescriptorInterface(_s, _b){}; inline void Restart(); inline bool ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); inline bool ReadElemAscii(PlyFile &file, PlyProperty &prop); inline bool WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); inline bool WriteElemAscii(PlyFile &file, PlyProperty &prop); private: template inline void ReadBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); template inline void ReadAscii(PlyFile &file, PlyProperty &prop); template inline void WriteBinary(PlyFile &file, PlyProperty &prop, bool fixEndian); template inline void WriteAscii(PlyFile &file, PlyProperty &prop); }; template inline void DataDescriptor::Restart() { this->curPos = 0; } template template inline void DataDescriptor::ReadBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) { char * buffer = nullptr; int size; int count = prop.CountValue(); int typeSize = prop.TypeSize(); if (prop.type >= NNP_LIST_UINT8_UINT32) { file.ReadBinaryData(buffer, sizeof(char)); const int cntList = int(*(reinterpret_cast(buffer))); size = typeSize * cntList; count = cntList; } else size = typeSize * count; file.ReadBinaryData(buffer, size); if (typeSize > 1 && fixEndian) adjustEndianess(reinterpret_cast(buffer), typeSize, count); unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); C* temp = (C*)buffer; if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA)) { float norm = 1.0f; if (std::is_same::value && std::is_same::value) norm = 1.0f / 255.0f; else if (std::is_same::value && std::is_same::value) norm = 255.0f; for (int i = 0; i < std::min(VectorSize, count); i++) *((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i] * norm); } else { for (int i = 0; i < std::min(VectorSize, count); i++) *((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i]); } ++(this->curPos); } template inline bool DataDescriptor::ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) { if (prop.elem != elem) return false; switch (prop.type) { case NNP_LIST_INT8_INT8: case NNP_LIST_UINT8_INT8: case NNP_INT8: this->ReadBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_UINT8: case NNP_LIST_UINT8_UINT8: case NNP_UINT8: this->ReadBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_INT16: case NNP_LIST_UINT8_INT16: case NNP_INT16: this->ReadBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_UINT16: case NNP_LIST_UINT8_UINT16: case NNP_UINT16: this->ReadBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_FLOAT32: case NNP_LIST_UINT8_FLOAT32: case NNP_FLOAT32: this->ReadBinary(file, prop, fixEndian); break; case NNP_LIST_UINT8_INT32: case NNP_LIST_INT8_INT32: case NNP_INT32: this->ReadBinary(file, prop, fixEndian); break; case NNP_LIST_UINT8_UINT32: case NNP_LIST_INT8_UINT32: case NNP_UINT32: this->ReadBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_FLOAT64: case NNP_LIST_UINT8_FLOAT64: case NNP_FLOAT64: this->ReadBinary(file, prop, fixEndian); break; } return true; } template template inline void DataDescriptor::ReadAscii(PlyFile &file, PlyProperty &prop) { int count = prop.CountValue(); if (prop.type >= NNP_LIST_UINT8_UINT32) file.ReadAsciiData(count); C* temp = new C[count]; for (int i = 0; i < count; i++) file.ReadAsciiData(temp[i]); unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA)) { float norm = 1.0f; if (std::is_same::value && prop.type == NNP_UINT8) norm = 1.0f / 255.0f; else if (std::is_same::value && prop.type == NNP_FLOAT32) norm = 255.0f; for (int i = 0; i < std::min(VectorSize, count); i++) *((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i] * norm); } else { for (int i = 0; i < std::min(VectorSize, count); i++) *((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i]); } delete[] temp; ++(this->curPos); } template inline bool DataDescriptor::ReadElemAscii(PlyFile &file, PlyProperty &prop) { if (prop.elem != elem) return false; switch (prop.type) { case NNP_LIST_UINT8_INT8: case NNP_LIST_INT8_INT8: case NNP_INT8: this->ReadAscii(file, prop); break; case NNP_LIST_UINT8_UINT8: case NNP_LIST_INT8_UINT8: case NNP_UINT8: this->ReadAscii(file, prop); break; case NNP_LIST_UINT8_INT16: case NNP_LIST_INT8_INT16: case NNP_INT16: this->ReadAscii(file, prop); break; case NNP_LIST_UINT8_UINT16: case NNP_LIST_INT8_UINT16: case NNP_UINT16: this->ReadAscii(file, prop); break; case NNP_LIST_UINT8_FLOAT32: case NNP_LIST_INT8_FLOAT32: case NNP_FLOAT32: this->ReadAscii(file, prop); break; case NNP_LIST_UINT8_INT32: case NNP_LIST_INT8_INT32: case NNP_INT32: this->ReadAscii(file, prop); break; case NNP_LIST_UINT8_UINT32: case NNP_LIST_INT8_UINT32: case NNP_UINT32: this->ReadAscii(file, prop); break; case NNP_LIST_UINT8_FLOAT64: case NNP_LIST_INT8_FLOAT64: case NNP_FLOAT64: this->ReadAscii(file, prop); break; } return true; } template template inline void DataDescriptor::WriteBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) { (void)fixEndian; int count = prop.CountValue(); C data[VectorSize]; if (prop.type >= NNP_LIST_UINT8_UINT32) { if (prop.IsSigned()) { char listSize = (char)VectorSize; file.WriteBinaryData(&listSize, 1); count = VectorSize; } else { unsigned char listSize = (unsigned char)VectorSize; file.WriteBinaryData(&listSize, 1); count = VectorSize; } } C temp = 0; unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA)) { float norm = 1.0f; if (std::is_same::value && std::is_same::value) norm = 255.0f; else if (std::is_same::value && std::is_same::value) norm = 1.0f / 255.0f; for (int i = 0; i < std::min(VectorSize, count); i++) data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))) * norm); } else { for (int i = 0; i < std::min(VectorSize, count); i++) data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType)))); } if (sizeof(C) > 1 && fixEndian) adjustEndianess((unsigned char*)data, sizeof(C), std::min(VectorSize, count)); file.WriteBinaryData(data, sizeof(C)*std::min(VectorSize, count)); for (int i = 0; i < (count - VectorSize); i++) file.WriteBinaryData(&temp, sizeof(C)); ++(this->curPos); } template inline bool DataDescriptor::WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) { if (prop.elem != elem) return false; switch (prop.type) { case NNP_LIST_INT8_INT8: case NNP_LIST_UINT8_INT8: case NNP_INT8: this->WriteBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_UINT8: case NNP_LIST_UINT8_UINT8: case NNP_UINT8: this->WriteBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_INT16: case NNP_LIST_UINT8_INT16: case NNP_INT16: this->WriteBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_UINT16: case NNP_LIST_UINT8_UINT16: case NNP_UINT16: this->WriteBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_FLOAT32: case NNP_LIST_UINT8_FLOAT32: case NNP_FLOAT32: this->WriteBinary(file, prop, fixEndian); break; case NNP_LIST_UINT8_INT32: case NNP_LIST_INT8_INT32: case NNP_INT32: this->WriteBinary(file, prop, fixEndian); break; case NNP_LIST_UINT8_UINT32: case NNP_LIST_INT8_UINT32: case NNP_UINT32: this->WriteBinary(file, prop, fixEndian); break; case NNP_LIST_INT8_FLOAT64: case NNP_LIST_UINT8_FLOAT64: case NNP_FLOAT64: this->WriteBinary(file, prop, fixEndian); break; } return true; } template template inline void DataDescriptor::WriteAscii(PlyFile &file, PlyProperty &prop) { int count = prop.CountValue(); if (prop.type >= NNP_LIST_UINT8_UINT32) { if (prop.IsSigned()) { int listSize = (int)VectorSize; file.WriteAsciiData(listSize); count = VectorSize; } else { unsigned int listSize = (unsigned int)VectorSize; file.WriteAsciiData(listSize); count = VectorSize; } file.WriteAsciiData(std::string(" ")); } C data[VectorSize]; unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType); if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA)) { float norm = 1.0; if (std::is_same::value && prop.type == NNP_UINT8) norm = 255.0f; else if (std::is_same::value && prop.type == NNP_FLOAT32) norm = 1.0f / 255.0f; for (int i = 0; i < std::min(VectorSize, count); i++) data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))) * norm); } else { for (int i = 0; i < std::min(VectorSize, count); i++) data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType)))); } for (int i = 0; i < (count - VectorSize); i++) data[i] = 0; for (int i = 0; i < count; i++) { file.WriteAsciiData(data[i]); if (i < count - 1) file.WriteAsciiData(std::string(" ")); } ++(this->curPos); } template inline bool DataDescriptor::WriteElemAscii(PlyFile &file, PlyProperty& prop) { if (prop.elem != elem) return false; if (prop.elem == PlyEntity::NNP_UNKNOWN_ENTITY && prop.name != name) return false; switch (prop.type) { case NNP_LIST_UINT8_INT8: case NNP_LIST_INT8_INT8: case NNP_INT8: this->WriteAscii(file, prop); break; case NNP_LIST_UINT8_UINT8: case NNP_LIST_INT8_UINT8: case NNP_UINT8: this->WriteAscii(file, prop); break; case NNP_LIST_UINT8_INT16: case NNP_LIST_INT8_INT16: case NNP_INT16: this->WriteAscii(file, prop); break; case NNP_LIST_UINT8_UINT16: case NNP_LIST_INT8_UINT16: case NNP_UINT16: this->WriteAscii(file, prop); break; case NNP_LIST_UINT8_FLOAT32: case NNP_LIST_INT8_FLOAT32: case NNP_FLOAT32: this->WriteAscii(file, prop); break; case NNP_LIST_UINT8_INT32: case NNP_LIST_INT8_INT32: case NNP_INT32: this->WriteAscii(file, prop); break; case NNP_LIST_UINT8_UINT32: case NNP_LIST_INT8_UINT32: case NNP_UINT32: this->WriteAscii(file, prop); break; case NNP_LIST_UINT8_FLOAT64: case NNP_LIST_INT8_FLOAT64: case NNP_FLOAT64: this->WriteAscii(file, prop); break; } return true; } template inline bool ElemProcessing(ElementDescriptor& elemDescr, PlyElement &elem, PlyFile& file, bool fixEndian) { if ((elemDescr.elem != PlyElemEntity::NNP_UNKNOWN_ELEM && elemDescr.elem == elem.plyElem) || (elemDescr.elem == PlyElemEntity::NNP_UNKNOWN_ELEM && elemDescr.name == elem.name)) { if (ActionType == 0) elemDescr.ReadElemBinary(file, elem, fixEndian); else if (ActionType == 1) elemDescr.ReadElemAscii(file, elem); else if (ActionType == 2) elemDescr.WriteElemBinary(file, elem, fixEndian); else if (ActionType == 3) elemDescr.WriteElemAscii(file, elem); else if (ActionType == 4) elemDescr.CheckDescriptor(elem); return true; } return false; } typedef std::vector MeshDescriptor; /** * Load a 3D model from a PLY file. * * @param meshElements Vector that defines how to manage the ply element data in memory. * @param info Info of the file to load. */ inline bool OpenModel(Info& info, MeshDescriptor& meshElements) { PlyFile file; if (!file.OpenFileToRead(info.filename)) { info.errInfo = NNP_UNABLE_TO_OPEN; return false; } bool last; std::string line; do { file.NextHeaderLine(line, last); } while (!last); bool fixEndian = false; if (checkEndianness() == 1) { if (info.bigEndian) fixEndian = true; } else { if (!info.bigEndian) fixEndian = true; } if (info.binary) { for (int i = 0; i < info.elemVec.size(); ++i) { PlyElement& pe = info.elemVec[i]; int j = 0; for (; j < meshElements.size(); j++) if (ElemProcessing<0>(*meshElements[j], pe, file, fixEndian)) break; if (j == meshElements.size()) pe.SkipBinaryElementsInFile(file); //if (!TupleForEach(meshElements, pe, file, fixEndian, SizeT<0>())) // pe.SkipBinaryElementsInFile(file); } } else { for (int i = 0; i < info.elemVec.size(); ++i) { PlyElement& pe = info.elemVec[i]; int j = 0; for (; j < meshElements.size(); j++) if (ElemProcessing<1>(*meshElements[j], pe, file, false)) break; if (j == meshElements.size()) pe.SkipAsciiElementsInFile(file); //if (!TupleForEach(meshElements, pe, file, fixEndian, SizeT<1>())) // pe.SkipAsciiElementsInFile(file); } } return true; } /** * Save a 3D model in a PLY file. * * @param filename Path to the file to save. * @param meshElements Vector that defines how to manage the ply element data in memory. * @param info Info to saved in the PLY header. */ inline bool SaveModel(std::string& filename, MeshDescriptor& meshElements, Info& info) { PlyFile file; if (!file.OpenFileToWrite(filename)) { info.errInfo = NNP_UNABLE_TO_OPEN; return false; } for (int i = 0; i < info.elemVec.size(); ++i) { PlyElement& pe = info.elemVec[i]; for (int j = 0; j < meshElements.size(); j++) if (ElemProcessing<4>(*meshElements[j], pe, file, false)) break; } info.WriteHeader(file); bool fixEndian = false; if (checkEndianness() == 1) { if (info.bigEndian) fixEndian = true; } else { if (!info.bigEndian) fixEndian = true; } if (info.binary) { for (int i = 0; i < info.elemVec.size(); ++i) { PlyElement& pe = info.elemVec[i]; if (pe.validToWrite) { for (int j = 0; j < meshElements.size(); j++) if (ElemProcessing<2>(*meshElements[j], pe, file, false)) break; } } } else { for (int i = 0; i < info.elemVec.size(); ++i) { PlyElement& pe = info.elemVec[i]; if (pe.validToWrite) { for (int j = 0; j < meshElements.size(); j++) if (ElemProcessing<3>(*meshElements[j], pe, file, false)) break; } } } file.Flush(); return true; } /** * @cond HIDDEN_SYMBOLS */ template < typename TupleType, size_t ActionType> inline bool TupleForEach(TupleType &tuple, PlyElement &elem, PlyFile& file, bool fixEndian, SizeT a) { return TupleForEach(tuple, elem, file, fixEndian, SizeT::value>(), a); } template < typename TupleType, size_t ActionType> inline bool TupleForEach(TupleType &tuple, PlyElement &elem, PlyFile& file, bool fixEndian, SizeT<0> t, SizeT a) { return false; } template < typename TupleType, size_t N, size_t ActionType> inline bool TupleForEach(TupleType &tuple, PlyElement &elem, PlyFile& file, bool fixEndian, SizeT t, SizeT a) { typename std::tuple_element::type &elemDescr = std::get(tuple); if ((elemDescr.elem != PlyElemEntity::NNP_UNKNOWN_ELEM && elemDescr.elem == elem.plyElem) || (elemDescr.elem == PlyElemEntity::NNP_UNKNOWN_ELEM && elemDescr.name == elem.name)) { if (ActionType == 0) elemDescr.ReadElemBinary(file, elem, fixEndian); else if (ActionType == 1) elemDescr.ReadElemAscii(file, elem); else if (ActionType == 2) elemDescr.WriteElemBinary(file, elem, fixEndian); else if (ActionType == 3) elemDescr.WriteElemAscii(file, elem); else if (ActionType == 4) elemDescr.CheckDescriptor(elem); return true; } return TupleForEach(tuple, elem, file, fixEndian, SizeT(), a); } /** * @endcond */ } // end namespace nanoply #endif // NANOPLY_HPP