diff options
author | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-04-16 11:55:09 -0500 |
commit | db81b925d776103326128bf629cbdda576a223e7 (patch) | |
tree | 58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/code/AssetLib/Ply | |
parent | 55860037b14fb3893ba21cf2654c83d349cc1082 (diff) |
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/code/AssetLib/Ply')
-rw-r--r-- | libs/assimp/code/AssetLib/Ply/PlyExporter.cpp | 406 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Ply/PlyExporter.h | 88 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Ply/PlyLoader.cpp | 927 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Ply/PlyLoader.h | 135 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Ply/PlyParser.cpp | 963 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/Ply/PlyParser.h | 488 |
6 files changed, 3007 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/Ply/PlyExporter.cpp b/libs/assimp/code/AssetLib/Ply/PlyExporter.cpp new file mode 100644 index 0000000..a98b83f --- /dev/null +++ b/libs/assimp/code/AssetLib/Ply/PlyExporter.cpp @@ -0,0 +1,406 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + + +#if !defined(ASSIMP_BUILD_NO_EXPORT) && !defined(ASSIMP_BUILD_NO_PLY_EXPORTER) + +#include "PlyExporter.h" +#include <memory> +#include <cmath> +#include <assimp/Exceptional.h> +#include <assimp/scene.h> +#include <assimp/version.h> +#include <assimp/IOSystem.hpp> +#include <assimp/Exporter.hpp> +#include <assimp/qnan.h> + + +//using namespace Assimp; +namespace Assimp { + +// make sure type_of returns consistent output across different platforms +// also consider using: typeid(VAR).name() +template <typename T> const char* type_of(T&) { return "unknown"; } +template<> const char* type_of(float&) { return "float"; } +template<> const char* type_of(double&) { return "double"; } + +// ------------------------------------------------------------------------------------------------ +// Worker function for exporting a scene to PLY. Prototyped and registered in Exporter.cpp +void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) +{ + // invoke the exporter + PlyExporter exporter(pFile, pScene); + + if (exporter.mOutput.fail()) { + throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); + } + + // we're still here - export successfully completed. Write the file. + std::unique_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt")); + if (outfile == nullptr) { + throw DeadlyExportError("could not open output .ply file: " + std::string(pFile)); + } + + outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()),1); +} + +void ExportScenePlyBinary(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) +{ + // invoke the exporter + PlyExporter exporter(pFile, pScene, true); + + // we're still here - export successfully completed. Write the file. + std::unique_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wb")); + if (outfile == nullptr) { + throw DeadlyExportError("could not open output .ply file: " + std::string(pFile)); + } + + outfile->Write(exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()), 1); +} + +#define PLY_EXPORT_HAS_NORMALS 0x1 +#define PLY_EXPORT_HAS_TANGENTS_BITANGENTS 0x2 +#define PLY_EXPORT_HAS_TEXCOORDS 0x4 +#define PLY_EXPORT_HAS_COLORS (PLY_EXPORT_HAS_TEXCOORDS << AI_MAX_NUMBER_OF_TEXTURECOORDS) + +// ------------------------------------------------------------------------------------------------ +PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool binary) +: filename(_filename) +, endl("\n") +{ + // make sure that all formatting happens using the standard, C locale and not the user's current locale + const std::locale& l = std::locale("C"); + mOutput.imbue(l); + mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION); + + unsigned int faces = 0u, vertices = 0u, components = 0u; + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + const aiMesh& m = *pScene->mMeshes[i]; + faces += m.mNumFaces; + vertices += m.mNumVertices; + + if (m.HasNormals()) { + components |= PLY_EXPORT_HAS_NORMALS; + } + if (m.HasTangentsAndBitangents()) { + components |= PLY_EXPORT_HAS_TANGENTS_BITANGENTS; + } + for (unsigned int t = 0; m.HasTextureCoords(t); ++t) { + components |= PLY_EXPORT_HAS_TEXCOORDS << t; + } + for (unsigned int t = 0; m.HasVertexColors(t); ++t) { + components |= PLY_EXPORT_HAS_COLORS << t; + } + } + + mOutput << "ply" << endl; + if (binary) { +#if (defined AI_BUILD_BIG_ENDIAN) + mOutput << "format binary_big_endian 1.0" << endl; +#else + mOutput << "format binary_little_endian 1.0" << endl; +#endif + } + else { + mOutput << "format ascii 1.0" << endl; + } + mOutput << "comment Created by Open Asset Import Library - http://assimp.sf.net (v" + << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' + << aiGetVersionRevision() << ")" << endl; + + // Look through materials for a diffuse texture, and add it if found + for ( unsigned int i = 0; i < pScene->mNumMaterials; ++i ) + { + const aiMaterial* const mat = pScene->mMaterials[i]; + aiString s; + if ( AI_SUCCESS == mat->Get( AI_MATKEY_TEXTURE_DIFFUSE( 0 ), s ) ) + { + mOutput << "comment TextureFile " << s.data << endl; + } + } + + // TODO: probably want to check here rather than just assume something + // definitely not good to always write float even if we might have double precision + + ai_real tmp = 0.0; + const char * typeName = type_of(tmp); + + mOutput << "element vertex " << vertices << endl; + mOutput << "property " << typeName << " x" << endl; + mOutput << "property " << typeName << " y" << endl; + mOutput << "property " << typeName << " z" << endl; + + if(components & PLY_EXPORT_HAS_NORMALS) { + mOutput << "property " << typeName << " nx" << endl; + mOutput << "property " << typeName << " ny" << endl; + mOutput << "property " << typeName << " nz" << endl; + } + + // write texcoords first, just in case an importer does not support tangents + // bitangents and just skips over the rest of the line upon encountering + // unknown fields (Ply leaves pretty much every vertex component open, + // but in reality most importers only know about vertex positions, normals + // and texture coordinates). + for (unsigned int n = PLY_EXPORT_HAS_TEXCOORDS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_TEXTURECOORDS; n <<= 1, ++c) { + if (!c) { + mOutput << "property " << typeName << " s" << endl; + mOutput << "property " << typeName << " t" << endl; + } + else { + mOutput << "property " << typeName << " s" << c << endl; + mOutput << "property " << typeName << " t" << c << endl; + } + } + + for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) { + if (!c) { + mOutput << "property " << "uchar" << " red" << endl; + mOutput << "property " << "uchar" << " green" << endl; + mOutput << "property " << "uchar" << " blue" << endl; + mOutput << "property " << "uchar" << " alpha" << endl; + } + else { + mOutput << "property " << "uchar" << " red" << c << endl; + mOutput << "property " << "uchar" << " green" << c << endl; + mOutput << "property " << "uchar" << " blue" << c << endl; + mOutput << "property " << "uchar" << " alpha" << c << endl; + } + } + + if(components & PLY_EXPORT_HAS_TANGENTS_BITANGENTS) { + mOutput << "property " << typeName << " tx" << endl; + mOutput << "property " << typeName << " ty" << endl; + mOutput << "property " << typeName << " tz" << endl; + mOutput << "property " << typeName << " bx" << endl; + mOutput << "property " << typeName << " by" << endl; + mOutput << "property " << typeName << " bz" << endl; + } + + mOutput << "element face " << faces << endl; + + // uchar seems to be the most common type for the number of indices per polygon and int seems to be most common for the vertex indices. + // For instance, MeshLab fails to load meshes in which both types are uint. Houdini seems to have problems as well. + // Obviously, using uchar will not work for meshes with polygons with more than 255 indices, but how realistic is this case? + mOutput << "property list uchar int vertex_index" << endl; + + mOutput << "end_header" << endl; + + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + if (binary) { + WriteMeshVertsBinary(pScene->mMeshes[i], components); + } + else { + WriteMeshVerts(pScene->mMeshes[i], components); + } + } + for (unsigned int i = 0, ofs = 0; i < pScene->mNumMeshes; ++i) { + if (binary) { + WriteMeshIndicesBinary(pScene->mMeshes[i], ofs); + } + else { + WriteMeshIndices(pScene->mMeshes[i], ofs); + } + ofs += pScene->mMeshes[i]->mNumVertices; + } +} + +// ------------------------------------------------------------------------------------------------ +PlyExporter::~PlyExporter() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +void PlyExporter::WriteMeshVerts(const aiMesh* m, unsigned int components) +{ + static const ai_real inf = std::numeric_limits<ai_real>::infinity(); + + // If a component (for instance normal vectors) is present in at least one mesh in the scene, + // then default values are written for meshes that do not contain this component. + for (unsigned int i = 0; i < m->mNumVertices; ++i) { + mOutput << + m->mVertices[i].x << " " << + m->mVertices[i].y << " " << + m->mVertices[i].z + ; + if(components & PLY_EXPORT_HAS_NORMALS) { + if (m->HasNormals() && is_not_qnan(m->mNormals[i].x) && std::fabs(m->mNormals[i].x) != inf) { + mOutput << + " " << m->mNormals[i].x << + " " << m->mNormals[i].y << + " " << m->mNormals[i].z; + } + else { + mOutput << " 0.0 0.0 0.0"; + } + } + + for (unsigned int n = PLY_EXPORT_HAS_TEXCOORDS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_TEXTURECOORDS; n <<= 1, ++c) { + if (m->HasTextureCoords(c)) { + mOutput << + " " << m->mTextureCoords[c][i].x << + " " << m->mTextureCoords[c][i].y; + } + else { + mOutput << " -1.0 -1.0"; + } + } + + for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) { + if (m->HasVertexColors(c)) { + mOutput << + " " << (int)(m->mColors[c][i].r * 255) << + " " << (int)(m->mColors[c][i].g * 255) << + " " << (int)(m->mColors[c][i].b * 255) << + " " << (int)(m->mColors[c][i].a * 255); + } + else { + mOutput << " 0 0 0"; + } + } + + if(components & PLY_EXPORT_HAS_TANGENTS_BITANGENTS) { + if (m->HasTangentsAndBitangents()) { + mOutput << + " " << m->mTangents[i].x << + " " << m->mTangents[i].y << + " " << m->mTangents[i].z << + " " << m->mBitangents[i].x << + " " << m->mBitangents[i].y << + " " << m->mBitangents[i].z + ; + } + else { + mOutput << " 0.0 0.0 0.0 0.0 0.0 0.0"; + } + } + + mOutput << endl; + } +} + +// ------------------------------------------------------------------------------------------------ +void PlyExporter::WriteMeshVertsBinary(const aiMesh* m, unsigned int components) +{ + // If a component (for instance normal vectors) is present in at least one mesh in the scene, + // then default values are written for meshes that do not contain this component. + aiVector3D defaultNormal(0, 0, 0); + aiVector2D defaultUV(-1, -1); + aiColor4D defaultColor(-1, -1, -1, -1); + for (unsigned int i = 0; i < m->mNumVertices; ++i) { + mOutput.write(reinterpret_cast<const char*>(&m->mVertices[i].x), 12); + if (components & PLY_EXPORT_HAS_NORMALS) { + if (m->HasNormals()) { + mOutput.write(reinterpret_cast<const char*>(&m->mNormals[i].x), 12); + } + else { + mOutput.write(reinterpret_cast<const char*>(&defaultNormal.x), 12); + } + } + + for (unsigned int n = PLY_EXPORT_HAS_TEXCOORDS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_TEXTURECOORDS; n <<= 1, ++c) { + if (m->HasTextureCoords(c)) { + mOutput.write(reinterpret_cast<const char*>(&m->mTextureCoords[c][i].x), 8); + } + else { + mOutput.write(reinterpret_cast<const char*>(&defaultUV.x), 8); + } + } + + for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) { + if (m->HasVertexColors(c)) { + mOutput.write(reinterpret_cast<const char*>(&m->mColors[c][i].r), 16); + } + else { + mOutput.write(reinterpret_cast<const char*>(&defaultColor.r), 16); + } + } + + if (components & PLY_EXPORT_HAS_TANGENTS_BITANGENTS) { + if (m->HasTangentsAndBitangents()) { + mOutput.write(reinterpret_cast<const char*>(&m->mTangents[i].x), 12); + mOutput.write(reinterpret_cast<const char*>(&m->mBitangents[i].x), 12); + } + else { + mOutput.write(reinterpret_cast<const char*>(&defaultNormal.x), 12); + mOutput.write(reinterpret_cast<const char*>(&defaultNormal.x), 12); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void PlyExporter::WriteMeshIndices(const aiMesh* m, unsigned int offset) +{ + for (unsigned int i = 0; i < m->mNumFaces; ++i) { + const aiFace& f = m->mFaces[i]; + mOutput << f.mNumIndices; + for(unsigned int c = 0; c < f.mNumIndices; ++c) { + mOutput << " " << (f.mIndices[c] + offset); + } + mOutput << endl; + } +} + +// Generic method in case we want to use different data types for the indices or make this configurable. +template<typename NumIndicesType, typename IndexType> +void WriteMeshIndicesBinary_Generic(const aiMesh* m, unsigned int offset, std::ostringstream& output) +{ + for (unsigned int i = 0; i < m->mNumFaces; ++i) { + const aiFace& f = m->mFaces[i]; + NumIndicesType numIndices = static_cast<NumIndicesType>(f.mNumIndices); + output.write(reinterpret_cast<const char*>(&numIndices), sizeof(NumIndicesType)); + for (unsigned int c = 0; c < f.mNumIndices; ++c) { + IndexType index = f.mIndices[c] + offset; + output.write(reinterpret_cast<const char*>(&index), sizeof(IndexType)); + } + } +} + +void PlyExporter::WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset) +{ + WriteMeshIndicesBinary_Generic<unsigned char, int>(m, offset, mOutput); +} + +} // end of namespace Assimp + +#endif // !defined(ASSIMP_BUILD_NO_EXPORT) && !defined(ASSIMP_BUILD_NO_PLY_EXPORTER) diff --git a/libs/assimp/code/AssetLib/Ply/PlyExporter.h b/libs/assimp/code/AssetLib/Ply/PlyExporter.h new file mode 100644 index 0000000..ff3a54c --- /dev/null +++ b/libs/assimp/code/AssetLib/Ply/PlyExporter.h @@ -0,0 +1,88 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file PlyExporter.h + * Declares the exporter class to write a scene to a Polygon Library (ply) + */ +#ifndef AI_PLYEXPORTER_H_INC +#define AI_PLYEXPORTER_H_INC + +#include <sstream> + +struct aiScene; +struct aiNode; +struct aiMesh; + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +/** Helper class to export a given scene to a Stanford Ply file. */ +// ------------------------------------------------------------------------------------------------ +class PlyExporter { +public: + /// The class constructor for a specific scene to export + PlyExporter(const char* filename, const aiScene* pScene, bool binary = false); + /// The class destructor, empty. + ~PlyExporter(); + +public: + /// public string-streams to write all output into: + std::ostringstream mOutput; + +private: + void WriteMeshVerts(const aiMesh* m, unsigned int components); + void WriteMeshIndices(const aiMesh* m, unsigned int ofs); + void WriteMeshVertsBinary(const aiMesh* m, unsigned int components); + void WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset); + +private: + const std::string filename; // tHE FILENAME + const std::string endl; // obviously, this endl() doesn't flush() the stream + +private: + PlyExporter( const PlyExporter & ); + PlyExporter &operator = ( const PlyExporter & ); +}; + +} // Namespace Assimp + +#endif // AI_PLYEXPORTER_H_INC diff --git a/libs/assimp/code/AssetLib/Ply/PlyLoader.cpp b/libs/assimp/code/AssetLib/Ply/PlyLoader.cpp new file mode 100644 index 0000000..6cf1a1c --- /dev/null +++ b/libs/assimp/code/AssetLib/Ply/PlyLoader.cpp @@ -0,0 +1,927 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file PlyLoader.cpp + * @brief Implementation of the PLY importer class + */ + +#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER + +// internal headers +#include "PlyLoader.h" +#include <assimp/IOStreamBuffer.h> +#include <assimp/importerdesc.h> +#include <assimp/scene.h> +#include <assimp/IOSystem.hpp> +#include <memory> + +using namespace ::Assimp; + +static const aiImporterDesc desc = { + "Stanford Polygon Library (PLY) Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ply" +}; + +// ------------------------------------------------------------------------------------------------ +// Internal stuff +namespace { +// ------------------------------------------------------------------------------------------------ +// Checks that property index is within range +template <class T> +inline const T &GetProperty(const std::vector<T> &props, int idx) { + if (static_cast<size_t>(idx) >= props.size()) { + throw DeadlyImportError("Invalid .ply file: Property index is out of range."); + } + + return props[idx]; +} +} // namespace + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +PLYImporter::PLYImporter() : + mBuffer(nullptr), + pcDOM(nullptr), + mGeneratedMesh(nullptr) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +PLYImporter::~PLYImporter() { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "ply" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc *PLYImporter::GetInfo() const { + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +static bool isBigEndian(const char *szMe) { + ai_assert(nullptr != szMe); + + // binary_little_endian + // binary_big_endian + bool isBigEndian(false); +#if (defined AI_BUILD_BIG_ENDIAN) + if ('l' == *szMe || 'L' == *szMe) { + isBigEndian = true; + } +#else + if ('b' == *szMe || 'B' == *szMe) { + isBigEndian = true; + } +#endif // ! AI_BUILD_BIG_ENDIAN + + return isBigEndian; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + const std::string mode = "rb"; + std::unique_ptr<IOStream> fileStream(pIOHandler->Open(pFile, mode)); + if (!fileStream.get()) { + throw DeadlyImportError("Failed to open file ", pFile, "."); + } + + // Get the file-size + const size_t fileSize(fileStream->FileSize()); + if (0 == fileSize) { + throw DeadlyImportError("File ", pFile, " is empty."); + } + + IOStreamBuffer<char> streamedBuffer(1024 * 1024); + streamedBuffer.open(fileStream.get()); + + // the beginning of the file must be PLY - magic, magic + std::vector<char> headerCheck; + streamedBuffer.getNextLine(headerCheck); + + if ((headerCheck.size() < 3) || + (headerCheck[0] != 'P' && headerCheck[0] != 'p') || + (headerCheck[1] != 'L' && headerCheck[1] != 'l') || + (headerCheck[2] != 'Y' && headerCheck[2] != 'y')) { + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Incorrect magic number (expected 'ply' or 'PLY')."); + } + + std::vector<char> mBuffer2; + streamedBuffer.getNextLine(mBuffer2); + mBuffer = (unsigned char *)&mBuffer2[0]; + + char *szMe = (char *)&this->mBuffer[0]; + SkipSpacesAndLineEnd(szMe, (const char **)&szMe); + + // determine the format of the file data and construct the aiMesh + PLY::DOM sPlyDom; + this->pcDOM = &sPlyDom; + + if (TokenMatch(szMe, "format", 6)) { + if (TokenMatch(szMe, "ascii", 5)) { + SkipLine(szMe, (const char **)&szMe); + if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) { + if (mGeneratedMesh != nullptr) { + delete (mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#1)"); + } + } else if (!::strncmp(szMe, "binary_", 7)) { + szMe += 7; + const bool bIsBE(isBigEndian(szMe)); + + // skip the line, parse the rest of the header and build the DOM + if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) { + if (mGeneratedMesh != nullptr) { + delete (mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)"); + } + } else { + if (mGeneratedMesh != nullptr) { + delete (mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unknown file format"); + } + } else { + AI_DEBUG_INVALIDATE_PTR(this->mBuffer); + if (mGeneratedMesh != nullptr) { + delete (mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Missing format specification"); + } + + //free the file buffer + streamedBuffer.close(); + + if (mGeneratedMesh == nullptr) { + throw DeadlyImportError("Invalid .ply file: Unable to extract mesh data "); + } + + // if no face list is existing we assume that the vertex + // list is containing a list of points + bool pointsOnly = mGeneratedMesh->mFaces == nullptr ? true : false; + if (pointsOnly) { + mGeneratedMesh->mPrimitiveTypes = aiPrimitiveType::aiPrimitiveType_POINT; + } + + // now load a list of all materials + std::vector<aiMaterial *> avMaterials; + std::string defaultTexture; + LoadMaterial(&avMaterials, defaultTexture, pointsOnly); + + // now generate the output scene object. Fill the material list + pScene->mNumMaterials = (unsigned int)avMaterials.size(); + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) { + pScene->mMaterials[i] = avMaterials[i]; + } + + // fill the mesh list + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; + pScene->mMeshes[0] = mGeneratedMesh; + mGeneratedMesh = nullptr; + + // generate a simple node structure + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; + + for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes; ++i) { + pScene->mRootNode->mMeshes[i] = i; + } +} + +void PLYImporter::LoadVertex(const PLY::Element *pcElement, const PLY::ElementInstance *instElement, unsigned int pos) { + ai_assert(nullptr != pcElement); + ai_assert(nullptr != instElement); + + ai_uint aiPositions[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + PLY::EDataType aiTypes[3] = { EDT_Char, EDT_Char, EDT_Char }; + + ai_uint aiNormal[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + PLY::EDataType aiNormalTypes[3] = { EDT_Char, EDT_Char, EDT_Char }; + + unsigned int aiColors[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + PLY::EDataType aiColorsTypes[4] = { EDT_Char, EDT_Char, EDT_Char, EDT_Char }; + + unsigned int aiTexcoord[2] = { 0xFFFFFFFF, 0xFFFFFFFF }; + PLY::EDataType aiTexcoordTypes[2] = { EDT_Char, EDT_Char }; + + // now check whether which normal components are available + unsigned int _a(0), cnt(0); + for (std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin(); + a != pcElement->alProperties.end(); ++a, ++_a) { + if ((*a).bIsList) { + continue; + } + + // Positions + if (PLY::EST_XCoord == (*a).Semantic) { + ++cnt; + aiPositions[0] = _a; + aiTypes[0] = (*a).eType; + } else if (PLY::EST_YCoord == (*a).Semantic) { + ++cnt; + aiPositions[1] = _a; + aiTypes[1] = (*a).eType; + } else if (PLY::EST_ZCoord == (*a).Semantic) { + ++cnt; + aiPositions[2] = _a; + aiTypes[2] = (*a).eType; + } else if (PLY::EST_XNormal == (*a).Semantic) { + // Normals + ++cnt; + aiNormal[0] = _a; + aiNormalTypes[0] = (*a).eType; + } else if (PLY::EST_YNormal == (*a).Semantic) { + ++cnt; + aiNormal[1] = _a; + aiNormalTypes[1] = (*a).eType; + } else if (PLY::EST_ZNormal == (*a).Semantic) { + ++cnt; + aiNormal[2] = _a; + aiNormalTypes[2] = (*a).eType; + } else if (PLY::EST_Red == (*a).Semantic) { + // Colors + ++cnt; + aiColors[0] = _a; + aiColorsTypes[0] = (*a).eType; + } else if (PLY::EST_Green == (*a).Semantic) { + ++cnt; + aiColors[1] = _a; + aiColorsTypes[1] = (*a).eType; + } else if (PLY::EST_Blue == (*a).Semantic) { + ++cnt; + aiColors[2] = _a; + aiColorsTypes[2] = (*a).eType; + } else if (PLY::EST_Alpha == (*a).Semantic) { + ++cnt; + aiColors[3] = _a; + aiColorsTypes[3] = (*a).eType; + } else if (PLY::EST_UTextureCoord == (*a).Semantic) { + // Texture coordinates + ++cnt; + aiTexcoord[0] = _a; + aiTexcoordTypes[0] = (*a).eType; + } else if (PLY::EST_VTextureCoord == (*a).Semantic) { + ++cnt; + aiTexcoord[1] = _a; + aiTexcoordTypes[1] = (*a).eType; + } + } + + // check whether we have a valid source for the vertex data + if (0 != cnt) { + // Position + aiVector3D vOut; + if (0xFFFFFFFF != aiPositions[0]) { + vOut.x = PLY::PropertyInstance::ConvertTo<ai_real>( + GetProperty(instElement->alProperties, aiPositions[0]).avList.front(), aiTypes[0]); + } + + if (0xFFFFFFFF != aiPositions[1]) { + vOut.y = PLY::PropertyInstance::ConvertTo<ai_real>( + GetProperty(instElement->alProperties, aiPositions[1]).avList.front(), aiTypes[1]); + } + + if (0xFFFFFFFF != aiPositions[2]) { + vOut.z = PLY::PropertyInstance::ConvertTo<ai_real>( + GetProperty(instElement->alProperties, aiPositions[2]).avList.front(), aiTypes[2]); + } + + // Normals + aiVector3D nOut; + bool haveNormal = false; + if (0xFFFFFFFF != aiNormal[0]) { + nOut.x = PLY::PropertyInstance::ConvertTo<ai_real>( + GetProperty(instElement->alProperties, aiNormal[0]).avList.front(), aiNormalTypes[0]); + haveNormal = true; + } + + if (0xFFFFFFFF != aiNormal[1]) { + nOut.y = PLY::PropertyInstance::ConvertTo<ai_real>( + GetProperty(instElement->alProperties, aiNormal[1]).avList.front(), aiNormalTypes[1]); + haveNormal = true; + } + + if (0xFFFFFFFF != aiNormal[2]) { + nOut.z = PLY::PropertyInstance::ConvertTo<ai_real>( + GetProperty(instElement->alProperties, aiNormal[2]).avList.front(), aiNormalTypes[2]); + haveNormal = true; + } + + //Colors + aiColor4D cOut; + bool haveColor = false; + if (0xFFFFFFFF != aiColors[0]) { + cOut.r = NormalizeColorValue(GetProperty(instElement->alProperties, + aiColors[0]) + .avList.front(), + aiColorsTypes[0]); + haveColor = true; + } + + if (0xFFFFFFFF != aiColors[1]) { + cOut.g = NormalizeColorValue(GetProperty(instElement->alProperties, + aiColors[1]) + .avList.front(), + aiColorsTypes[1]); + haveColor = true; + } + + if (0xFFFFFFFF != aiColors[2]) { + cOut.b = NormalizeColorValue(GetProperty(instElement->alProperties, + aiColors[2]) + .avList.front(), + aiColorsTypes[2]); + haveColor = true; + } + + // assume 1.0 for the alpha channel if it is not set + if (0xFFFFFFFF == aiColors[3]) { + cOut.a = 1.0; + } else { + cOut.a = NormalizeColorValue(GetProperty(instElement->alProperties, + aiColors[3]) + .avList.front(), + aiColorsTypes[3]); + + haveColor = true; + } + + //Texture coordinates + aiVector3D tOut; + tOut.z = 0; + bool haveTextureCoords = false; + if (0xFFFFFFFF != aiTexcoord[0]) { + tOut.x = PLY::PropertyInstance::ConvertTo<ai_real>( + GetProperty(instElement->alProperties, aiTexcoord[0]).avList.front(), aiTexcoordTypes[0]); + haveTextureCoords = true; + } + + if (0xFFFFFFFF != aiTexcoord[1]) { + tOut.y = PLY::PropertyInstance::ConvertTo<ai_real>( + GetProperty(instElement->alProperties, aiTexcoord[1]).avList.front(), aiTexcoordTypes[1]); + haveTextureCoords = true; + } + + //create aiMesh if needed + if (nullptr == mGeneratedMesh) { + mGeneratedMesh = new aiMesh(); + mGeneratedMesh->mMaterialIndex = 0; + } + + if (nullptr == mGeneratedMesh->mVertices) { + mGeneratedMesh->mNumVertices = pcElement->NumOccur; + mGeneratedMesh->mVertices = new aiVector3D[mGeneratedMesh->mNumVertices]; + } + + mGeneratedMesh->mVertices[pos] = vOut; + + if (haveNormal) { + if (nullptr == mGeneratedMesh->mNormals) + mGeneratedMesh->mNormals = new aiVector3D[mGeneratedMesh->mNumVertices]; + mGeneratedMesh->mNormals[pos] = nOut; + } + + if (haveColor) { + if (nullptr == mGeneratedMesh->mColors[0]) + mGeneratedMesh->mColors[0] = new aiColor4D[mGeneratedMesh->mNumVertices]; + mGeneratedMesh->mColors[0][pos] = cOut; + } + + if (haveTextureCoords) { + if (nullptr == mGeneratedMesh->mTextureCoords[0]) { + mGeneratedMesh->mNumUVComponents[0] = 2; + mGeneratedMesh->mTextureCoords[0] = new aiVector3D[mGeneratedMesh->mNumVertices]; + } + mGeneratedMesh->mTextureCoords[0][pos] = tOut; + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert a color component to [0...1] +ai_real PLYImporter::NormalizeColorValue(PLY::PropertyInstance::ValueUnion val, PLY::EDataType eType) { + switch (eType) { + case EDT_Float: + return val.fFloat; + case EDT_Double: + return (ai_real)val.fDouble; + case EDT_UChar: + return (ai_real)val.iUInt / (ai_real)0xFF; + case EDT_Char: + return (ai_real)(val.iInt + (0xFF / 2)) / (ai_real)0xFF; + case EDT_UShort: + return (ai_real)val.iUInt / (ai_real)0xFFFF; + case EDT_Short: + return (ai_real)(val.iInt + (0xFFFF / 2)) / (ai_real)0xFFFF; + case EDT_UInt: + return (ai_real)val.iUInt / (ai_real)0xFFFF; + case EDT_Int: + return ((ai_real)val.iInt / (ai_real)0xFF) + 0.5f; + default: + break; + } + + return 0.0f; +} + +// ------------------------------------------------------------------------------------------------ +// Try to extract proper faces from the PLY DOM +void PLYImporter::LoadFace(const PLY::Element *pcElement, const PLY::ElementInstance *instElement, + unsigned int pos) { + ai_assert(nullptr != pcElement); + ai_assert(nullptr != instElement); + + if (mGeneratedMesh == nullptr) { + throw DeadlyImportError("Invalid .ply file: Vertices should be declared before faces"); + } + + bool bOne = false; + + // index of the vertex index list + unsigned int iProperty = 0xFFFFFFFF; + PLY::EDataType eType = EDT_Char; + bool bIsTriStrip = false; + + // index of the material index property + //unsigned int iMaterialIndex = 0xFFFFFFFF; + //PLY::EDataType eType2 = EDT_Char; + + // texture coordinates + unsigned int iTextureCoord = 0xFFFFFFFF; + PLY::EDataType eType3 = EDT_Char; + + // face = unique number of vertex indices + if (PLY::EEST_Face == pcElement->eSemantic) { + unsigned int _a = 0; + for (std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin(); + a != pcElement->alProperties.end(); ++a, ++_a) { + if (PLY::EST_VertexIndex == (*a).Semantic) { + // must be a dynamic list! + if (!(*a).bIsList) { + continue; + } + + iProperty = _a; + bOne = true; + eType = (*a).eType; + } else if (PLY::EST_TextureCoordinates == (*a).Semantic) { + // must be a dynamic list! + if (!(*a).bIsList) { + continue; + } + iTextureCoord = _a; + bOne = true; + eType3 = (*a).eType; + } + } + } + // triangle strip + // TODO: triangle strip and material index support??? + else if (PLY::EEST_TriStrip == pcElement->eSemantic) { + unsigned int _a = 0; + for (std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin(); + a != pcElement->alProperties.end(); ++a, ++_a) { + // must be a dynamic list! + if (!(*a).bIsList) { + continue; + } + iProperty = _a; + bOne = true; + bIsTriStrip = true; + eType = (*a).eType; + break; + } + } + + // check whether we have at least one per-face information set + if (bOne) { + if (mGeneratedMesh->mFaces == nullptr) { + mGeneratedMesh->mNumFaces = pcElement->NumOccur; + mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces]; + } + + if (!bIsTriStrip) { + // parse the list of vertex indices + if (0xFFFFFFFF != iProperty) { + const unsigned int iNum = (unsigned int)GetProperty(instElement->alProperties, iProperty).avList.size(); + mGeneratedMesh->mFaces[pos].mNumIndices = iNum; + mGeneratedMesh->mFaces[pos].mIndices = new unsigned int[iNum]; + + std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p = + GetProperty(instElement->alProperties, iProperty).avList.begin(); + + for (unsigned int a = 0; a < iNum; ++a, ++p) { + mGeneratedMesh->mFaces[pos].mIndices[a] = PLY::PropertyInstance::ConvertTo<unsigned int>(*p, eType); + } + } + + // parse the material index + // cannot be handled without processing the whole file first + /*if (0xFFFFFFFF != iMaterialIndex) + { + mGeneratedMesh->mFaces[pos]. = PLY::PropertyInstance::ConvertTo<unsigned int>( + GetProperty(instElement->alProperties, iMaterialIndex).avList.front(), eType2); + }*/ + + if (0xFFFFFFFF != iTextureCoord) { + const unsigned int iNum = (unsigned int)GetProperty(instElement->alProperties, iTextureCoord).avList.size(); + + //should be 6 coords + std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p = + GetProperty(instElement->alProperties, iTextureCoord).avList.begin(); + + if ((iNum / 3) == 2) // X Y coord + { + for (unsigned int a = 0; a < iNum; ++a, ++p) { + unsigned int vindex = mGeneratedMesh->mFaces[pos].mIndices[a / 2]; + if (vindex < mGeneratedMesh->mNumVertices) { + if (mGeneratedMesh->mTextureCoords[0] == nullptr) { + mGeneratedMesh->mNumUVComponents[0] = 2; + mGeneratedMesh->mTextureCoords[0] = new aiVector3D[mGeneratedMesh->mNumVertices]; + } + + if (a % 2 == 0) { + mGeneratedMesh->mTextureCoords[0][vindex].x = PLY::PropertyInstance::ConvertTo<ai_real>(*p, eType3); + } else { + mGeneratedMesh->mTextureCoords[0][vindex].y = PLY::PropertyInstance::ConvertTo<ai_real>(*p, eType3); + } + + mGeneratedMesh->mTextureCoords[0][vindex].z = 0; + } + } + } + } + } else { // triangle strips + // normally we have only one triangle strip instance where + // a value of -1 indicates a restart of the strip + bool flip = false; + const std::vector<PLY::PropertyInstance::ValueUnion> &quak = GetProperty(instElement->alProperties, iProperty).avList; + //pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u)); //Limits memory consumption + + int aiTable[2] = { -1, -1 }; + for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator a = quak.begin(); a != quak.end(); ++a) { + const int p = PLY::PropertyInstance::ConvertTo<int>(*a, eType); + + if (-1 == p) { + // restart the strip ... + aiTable[0] = aiTable[1] = -1; + flip = false; + continue; + } + if (-1 == aiTable[0]) { + aiTable[0] = p; + continue; + } + if (-1 == aiTable[1]) { + aiTable[1] = p; + continue; + } + + if (mGeneratedMesh->mFaces == nullptr) { + mGeneratedMesh->mNumFaces = pcElement->NumOccur; + mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces]; + } + + mGeneratedMesh->mFaces[pos].mNumIndices = 3; + mGeneratedMesh->mFaces[pos].mIndices = new unsigned int[3]; + mGeneratedMesh->mFaces[pos].mIndices[0] = aiTable[0]; + mGeneratedMesh->mFaces[pos].mIndices[1] = aiTable[1]; + mGeneratedMesh->mFaces[pos].mIndices[2] = p; + + // every second pass swap the indices. + flip = !flip; + if (flip) { + std::swap(mGeneratedMesh->mFaces[pos].mIndices[0], mGeneratedMesh->mFaces[pos].mIndices[1]); + } + + aiTable[0] = aiTable[1]; + aiTable[1] = p; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Get a RGBA color in [0...1] range +void PLYImporter::GetMaterialColor(const std::vector<PLY::PropertyInstance> &avList, + unsigned int aiPositions[4], + PLY::EDataType aiTypes[4], + aiColor4D *clrOut) { + ai_assert(nullptr != clrOut); + + if (0xFFFFFFFF == aiPositions[0]) + clrOut->r = 0.0f; + else { + clrOut->r = NormalizeColorValue(GetProperty(avList, + aiPositions[0]) + .avList.front(), + aiTypes[0]); + } + + if (0xFFFFFFFF == aiPositions[1]) + clrOut->g = 0.0f; + else { + clrOut->g = NormalizeColorValue(GetProperty(avList, + aiPositions[1]) + .avList.front(), + aiTypes[1]); + } + + if (0xFFFFFFFF == aiPositions[2]) + clrOut->b = 0.0f; + else { + clrOut->b = NormalizeColorValue(GetProperty(avList, + aiPositions[2]) + .avList.front(), + aiTypes[2]); + } + + // assume 1.0 for the alpha channel ifit is not set + if (0xFFFFFFFF == aiPositions[3]) + clrOut->a = 1.0f; + else { + clrOut->a = NormalizeColorValue(GetProperty(avList, + aiPositions[3]) + .avList.front(), + aiTypes[3]); + } +} + +// ------------------------------------------------------------------------------------------------ +// Extract a material from the PLY DOM +void PLYImporter::LoadMaterial(std::vector<aiMaterial *> *pvOut, std::string &defaultTexture, const bool pointsOnly) { + ai_assert(nullptr != pvOut); + + // diffuse[4], specular[4], ambient[4] + // rgba order + unsigned int aaiPositions[3][4] = { + + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }, + }; + + PLY::EDataType aaiTypes[3][4] = { + { EDT_Char, EDT_Char, EDT_Char, EDT_Char }, + { EDT_Char, EDT_Char, EDT_Char, EDT_Char }, + { EDT_Char, EDT_Char, EDT_Char, EDT_Char } + }; + PLY::ElementInstanceList *pcList = nullptr; + + unsigned int iPhong = 0xFFFFFFFF; + PLY::EDataType ePhong = EDT_Char; + + unsigned int iOpacity = 0xFFFFFFFF; + PLY::EDataType eOpacity = EDT_Char; + + // search in the DOM for a vertex entry + unsigned int _i = 0; + for (std::vector<PLY::Element>::const_iterator i = this->pcDOM->alElements.begin(); + i != this->pcDOM->alElements.end(); ++i, ++_i) { + if (PLY::EEST_Material == (*i).eSemantic) { + pcList = &this->pcDOM->alElementData[_i]; + + // now check whether which coordinate sets are available + unsigned int _a = 0; + for (std::vector<PLY::Property>::const_iterator + a = (*i).alProperties.begin(); + a != (*i).alProperties.end(); ++a, ++_a) { + if ((*a).bIsList) continue; + + // pohng specularity ----------------------------------- + if (PLY::EST_PhongPower == (*a).Semantic) { + iPhong = _a; + ePhong = (*a).eType; + } + + // general opacity ----------------------------------- + if (PLY::EST_Opacity == (*a).Semantic) { + iOpacity = _a; + eOpacity = (*a).eType; + } + + // diffuse color channels ----------------------------------- + if (PLY::EST_DiffuseRed == (*a).Semantic) { + aaiPositions[0][0] = _a; + aaiTypes[0][0] = (*a).eType; + } else if (PLY::EST_DiffuseGreen == (*a).Semantic) { + aaiPositions[0][1] = _a; + aaiTypes[0][1] = (*a).eType; + } else if (PLY::EST_DiffuseBlue == (*a).Semantic) { + aaiPositions[0][2] = _a; + aaiTypes[0][2] = (*a).eType; + } else if (PLY::EST_DiffuseAlpha == (*a).Semantic) { + aaiPositions[0][3] = _a; + aaiTypes[0][3] = (*a).eType; + } + // specular color channels ----------------------------------- + else if (PLY::EST_SpecularRed == (*a).Semantic) { + aaiPositions[1][0] = _a; + aaiTypes[1][0] = (*a).eType; + } else if (PLY::EST_SpecularGreen == (*a).Semantic) { + aaiPositions[1][1] = _a; + aaiTypes[1][1] = (*a).eType; + } else if (PLY::EST_SpecularBlue == (*a).Semantic) { + aaiPositions[1][2] = _a; + aaiTypes[1][2] = (*a).eType; + } else if (PLY::EST_SpecularAlpha == (*a).Semantic) { + aaiPositions[1][3] = _a; + aaiTypes[1][3] = (*a).eType; + } + // ambient color channels ----------------------------------- + else if (PLY::EST_AmbientRed == (*a).Semantic) { + aaiPositions[2][0] = _a; + aaiTypes[2][0] = (*a).eType; + } else if (PLY::EST_AmbientGreen == (*a).Semantic) { + aaiPositions[2][1] = _a; + aaiTypes[2][1] = (*a).eType; + } else if (PLY::EST_AmbientBlue == (*a).Semantic) { + aaiPositions[2][2] = _a; + aaiTypes[2][2] = (*a).eType; + } else if (PLY::EST_AmbientAlpha == (*a).Semantic) { + aaiPositions[2][3] = _a; + aaiTypes[2][3] = (*a).eType; + } + } + break; + } else if (PLY::EEST_TextureFile == (*i).eSemantic) { + defaultTexture = (*i).szName; + } + } + // check whether we have a valid source for the material data + if (nullptr != pcList) { + for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin(); i != pcList->alInstances.end(); ++i) { + aiColor4D clrOut; + aiMaterial *pcHelper = new aiMaterial(); + + // build the diffuse material color + GetMaterialColor((*i).alProperties, aaiPositions[0], aaiTypes[0], &clrOut); + pcHelper->AddProperty<aiColor4D>(&clrOut, 1, AI_MATKEY_COLOR_DIFFUSE); + + // build the specular material color + GetMaterialColor((*i).alProperties, aaiPositions[1], aaiTypes[1], &clrOut); + pcHelper->AddProperty<aiColor4D>(&clrOut, 1, AI_MATKEY_COLOR_SPECULAR); + + // build the ambient material color + GetMaterialColor((*i).alProperties, aaiPositions[2], aaiTypes[2], &clrOut); + pcHelper->AddProperty<aiColor4D>(&clrOut, 1, AI_MATKEY_COLOR_AMBIENT); + + // handle phong power and shading mode + int iMode = (int)aiShadingMode_Gouraud; + if (0xFFFFFFFF != iPhong) { + ai_real fSpec = PLY::PropertyInstance::ConvertTo<ai_real>(GetProperty((*i).alProperties, iPhong).avList.front(), ePhong); + + // if shininess is 0 (and the pow() calculation would therefore always + // become 1, not depending on the angle), use gouraud lighting + if (fSpec) { + // scale this with 15 ... hopefully this is correct + fSpec *= 15; + pcHelper->AddProperty<ai_real>(&fSpec, 1, AI_MATKEY_SHININESS); + + iMode = (int)aiShadingMode_Phong; + } + } + pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + // handle opacity + if (0xFFFFFFFF != iOpacity) { + ai_real fOpacity = PLY::PropertyInstance::ConvertTo<ai_real>(GetProperty((*i).alProperties, iPhong).avList.front(), eOpacity); + pcHelper->AddProperty<ai_real>(&fOpacity, 1, AI_MATKEY_OPACITY); + } + + // The face order is absolutely undefined for PLY, so we have to + // use two-sided rendering to be sure it's ok. + const int two_sided = 1; + pcHelper->AddProperty(&two_sided, 1, AI_MATKEY_TWOSIDED); + + //default texture + if (!defaultTexture.empty()) { + const aiString name(defaultTexture.c_str()); + pcHelper->AddProperty(&name, _AI_MATKEY_TEXTURE_BASE, aiTextureType_DIFFUSE, 0); + } + + if (!pointsOnly) { + pcHelper->AddProperty(&two_sided, 1, AI_MATKEY_TWOSIDED); + } + + //set to wireframe, so when using this material info we can switch to points rendering + if (pointsOnly) { + const int wireframe = 1; + pcHelper->AddProperty(&wireframe, 1, AI_MATKEY_ENABLE_WIREFRAME); + } + + // add the newly created material instance to the list + pvOut->push_back(pcHelper); + } + } else { + // generate a default material + aiMaterial *pcHelper = new aiMaterial(); + + // fill in a default material + int iMode = (int)aiShadingMode_Gouraud; + pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + //generate white material most 3D engine just multiply ambient / diffuse color with actual ambient / light color + aiColor3D clr; + clr.b = clr.g = clr.r = 1.0f; + pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_SPECULAR); + + clr.b = clr.g = clr.r = 1.0f; + pcHelper->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_AMBIENT); + + // The face order is absolutely undefined for PLY, so we have to + // use two-sided rendering to be sure it's ok. + if (!pointsOnly) { + const int two_sided = 1; + pcHelper->AddProperty(&two_sided, 1, AI_MATKEY_TWOSIDED); + } + + //default texture + if (!defaultTexture.empty()) { + const aiString name(defaultTexture.c_str()); + pcHelper->AddProperty(&name, _AI_MATKEY_TEXTURE_BASE, aiTextureType_DIFFUSE, 0); + } + + //set to wireframe, so when using this material info we can switch to points rendering + if (pointsOnly) { + const int wireframe = 1; + pcHelper->AddProperty(&wireframe, 1, AI_MATKEY_ENABLE_WIREFRAME); + } + + pvOut->push_back(pcHelper); + } +} + +#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER diff --git a/libs/assimp/code/AssetLib/Ply/PlyLoader.h b/libs/assimp/code/AssetLib/Ply/PlyLoader.h new file mode 100644 index 0000000..e29da1d --- /dev/null +++ b/libs/assimp/code/AssetLib/Ply/PlyLoader.h @@ -0,0 +1,135 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file PLYLoader.h + * @brief Declaration of the .ply importer class. + */ +#pragma once +#ifndef AI_PLYLOADER_H_INCLUDED +#define AI_PLYLOADER_H_INCLUDED + +#include "PlyParser.h" +#include <assimp/BaseImporter.h> +#include <assimp/types.h> +#include <vector> + +struct aiNode; +struct aiMaterial; +struct aiMesh; + +namespace Assimp { + +using namespace PLY; + +// --------------------------------------------------------------------------- +/** Importer class to load the stanford PLY file format +*/ +class PLYImporter : public BaseImporter { +public: + PLYImporter(); + ~PLYImporter() override; + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. + */ + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, + bool checkSig) const override; + + // ------------------------------------------------------------------- + /** Extract a vertex from the DOM + */ + void LoadVertex(const PLY::Element *pcElement, const PLY::ElementInstance *instElement, unsigned int pos); + + // ------------------------------------------------------------------- + /** Extract a face from the DOM + */ + void LoadFace(const PLY::Element *pcElement, const PLY::ElementInstance *instElement, unsigned int pos); + +protected: + // ------------------------------------------------------------------- + /** Return importer meta information. + * See #BaseImporter::GetInfo for the details + */ + const aiImporterDesc *GetInfo() const override; + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile(const std::string &pFile, aiScene *pScene, + IOSystem *pIOHandler) override; + + // ------------------------------------------------------------------- + /** Extract a material list from the DOM + */ + void LoadMaterial(std::vector<aiMaterial *> *pvOut, std::string &defaultTexture, const bool pointsOnly); + + // ------------------------------------------------------------------- + /** Static helper to parse a color from four single channels in + */ + static void GetMaterialColor( + const std::vector<PLY::PropertyInstance> &avList, + unsigned int aiPositions[4], + PLY::EDataType aiTypes[4], + aiColor4D *clrOut); + + // ------------------------------------------------------------------- + /** Static helper to parse a color channel value. The input value + * is normalized to 0-1. + */ + static ai_real NormalizeColorValue( + PLY::PropertyInstance::ValueUnion val, + PLY::EDataType eType); + + /** Buffer to hold the loaded file */ + unsigned char *mBuffer; + + /** Document object model representation extracted from the file */ + PLY::DOM *pcDOM; + + /** Mesh generated by loader */ + aiMesh *mGeneratedMesh; +}; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC diff --git a/libs/assimp/code/AssetLib/Ply/PlyParser.cpp b/libs/assimp/code/AssetLib/Ply/PlyParser.cpp new file mode 100644 index 0000000..df93d5a --- /dev/null +++ b/libs/assimp/code/AssetLib/Ply/PlyParser.cpp @@ -0,0 +1,963 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the PLY parser class */ + +#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER + +#include "PlyLoader.h" +#include <assimp/ByteSwapper.h> +#include <assimp/fast_atof.h> +#include <assimp/DefaultLogger.hpp> + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +PLY::EDataType PLY::Property::ParseDataType(std::vector<char> &buffer) { + ai_assert(!buffer.empty()); + + PLY::EDataType eOut = PLY::EDT_INVALID; + + if (PLY::DOM::TokenMatch(buffer, "char", 4) || + PLY::DOM::TokenMatch(buffer, "int8", 4)) { + eOut = PLY::EDT_Char; + } else if (PLY::DOM::TokenMatch(buffer, "uchar", 5) || + PLY::DOM::TokenMatch(buffer, "uint8", 5)) { + eOut = PLY::EDT_UChar; + } else if (PLY::DOM::TokenMatch(buffer, "short", 5) || + PLY::DOM::TokenMatch(buffer, "int16", 5)) { + eOut = PLY::EDT_Short; + } else if (PLY::DOM::TokenMatch(buffer, "ushort", 6) || + PLY::DOM::TokenMatch(buffer, "uint16", 6)) { + eOut = PLY::EDT_UShort; + } else if (PLY::DOM::TokenMatch(buffer, "int32", 5) || PLY::DOM::TokenMatch(buffer, "int", 3)) { + eOut = PLY::EDT_Int; + } else if (PLY::DOM::TokenMatch(buffer, "uint32", 6) || PLY::DOM::TokenMatch(buffer, "uint", 4)) { + eOut = PLY::EDT_UInt; + } else if (PLY::DOM::TokenMatch(buffer, "float", 5) || PLY::DOM::TokenMatch(buffer, "float32", 7)) { + eOut = PLY::EDT_Float; + } else if (PLY::DOM::TokenMatch(buffer, "double64", 8) || PLY::DOM::TokenMatch(buffer, "double", 6) || + PLY::DOM::TokenMatch(buffer, "float64", 7)) { + eOut = PLY::EDT_Double; + } + if (PLY::EDT_INVALID == eOut) { + ASSIMP_LOG_INFO("Found unknown data type in PLY file. This is OK"); + } + + return eOut; +} + +// ------------------------------------------------------------------------------------------------ +PLY::ESemantic PLY::Property::ParseSemantic(std::vector<char> &buffer) { + ai_assert(!buffer.empty()); + + PLY::ESemantic eOut = PLY::EST_INVALID; + if (PLY::DOM::TokenMatch(buffer, "red", 3)) { + eOut = PLY::EST_Red; + } else if (PLY::DOM::TokenMatch(buffer, "green", 5)) { + eOut = PLY::EST_Green; + } else if (PLY::DOM::TokenMatch(buffer, "blue", 4)) { + eOut = PLY::EST_Blue; + } else if (PLY::DOM::TokenMatch(buffer, "alpha", 5)) { + eOut = PLY::EST_Alpha; + } else if (PLY::DOM::TokenMatch(buffer, "vertex_index", 12) || PLY::DOM::TokenMatch(buffer, "vertex_indices", 14)) { + eOut = PLY::EST_VertexIndex; + } else if (PLY::DOM::TokenMatch(buffer, "texcoord", 8)) // Manage uv coords on faces + { + eOut = PLY::EST_TextureCoordinates; + } else if (PLY::DOM::TokenMatch(buffer, "material_index", 14)) { + eOut = PLY::EST_MaterialIndex; + } else if (PLY::DOM::TokenMatch(buffer, "ambient_red", 11)) { + eOut = PLY::EST_AmbientRed; + } else if (PLY::DOM::TokenMatch(buffer, "ambient_green", 13)) { + eOut = PLY::EST_AmbientGreen; + } else if (PLY::DOM::TokenMatch(buffer, "ambient_blue", 12)) { + eOut = PLY::EST_AmbientBlue; + } else if (PLY::DOM::TokenMatch(buffer, "ambient_alpha", 13)) { + eOut = PLY::EST_AmbientAlpha; + } else if (PLY::DOM::TokenMatch(buffer, "diffuse_red", 11)) { + eOut = PLY::EST_DiffuseRed; + } else if (PLY::DOM::TokenMatch(buffer, "diffuse_green", 13)) { + eOut = PLY::EST_DiffuseGreen; + } else if (PLY::DOM::TokenMatch(buffer, "diffuse_blue", 12)) { + eOut = PLY::EST_DiffuseBlue; + } else if (PLY::DOM::TokenMatch(buffer, "diffuse_alpha", 13)) { + eOut = PLY::EST_DiffuseAlpha; + } else if (PLY::DOM::TokenMatch(buffer, "specular_red", 12)) { + eOut = PLY::EST_SpecularRed; + } else if (PLY::DOM::TokenMatch(buffer, "specular_green", 14)) { + eOut = PLY::EST_SpecularGreen; + } else if (PLY::DOM::TokenMatch(buffer, "specular_blue", 13)) { + eOut = PLY::EST_SpecularBlue; + } else if (PLY::DOM::TokenMatch(buffer, "specular_alpha", 14)) { + eOut = PLY::EST_SpecularAlpha; + } else if (PLY::DOM::TokenMatch(buffer, "opacity", 7)) { + eOut = PLY::EST_Opacity; + } else if (PLY::DOM::TokenMatch(buffer, "specular_power", 14)) { + eOut = PLY::EST_PhongPower; + } else if (PLY::DOM::TokenMatch(buffer, "r", 1)) { + eOut = PLY::EST_Red; + } else if (PLY::DOM::TokenMatch(buffer, "g", 1)) { + eOut = PLY::EST_Green; + } else if (PLY::DOM::TokenMatch(buffer, "b", 1)) { + eOut = PLY::EST_Blue; + } + + // NOTE: Blender3D exports texture coordinates as s,t tuples + else if (PLY::DOM::TokenMatch(buffer, "u", 1) || PLY::DOM::TokenMatch(buffer, "s", 1) || PLY::DOM::TokenMatch(buffer, "tx", 2) || PLY::DOM::TokenMatch(buffer, "texture_u", 9)) { + eOut = PLY::EST_UTextureCoord; + } else if (PLY::DOM::TokenMatch(buffer, "v", 1) || PLY::DOM::TokenMatch(buffer, "t", 1) || PLY::DOM::TokenMatch(buffer, "ty", 2) || PLY::DOM::TokenMatch(buffer, "texture_v", 9)) { + eOut = PLY::EST_VTextureCoord; + } else if (PLY::DOM::TokenMatch(buffer, "x", 1)) { + eOut = PLY::EST_XCoord; + } else if (PLY::DOM::TokenMatch(buffer, "y", 1)) { + eOut = PLY::EST_YCoord; + } else if (PLY::DOM::TokenMatch(buffer, "z", 1)) { + eOut = PLY::EST_ZCoord; + } else if (PLY::DOM::TokenMatch(buffer, "nx", 2)) { + eOut = PLY::EST_XNormal; + } else if (PLY::DOM::TokenMatch(buffer, "ny", 2)) { + eOut = PLY::EST_YNormal; + } else if (PLY::DOM::TokenMatch(buffer, "nz", 2)) { + eOut = PLY::EST_ZNormal; + } else { + ASSIMP_LOG_INFO("Found unknown property semantic in file. This is ok"); + PLY::DOM::SkipLine(buffer); + } + return eOut; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::Property::ParseProperty(std::vector<char> &buffer, PLY::Property *pOut) { + ai_assert(!buffer.empty()); + + // Forms supported: + // "property float x" + // "property list uchar int vertex_index" + + // skip leading spaces + if (!PLY::DOM::SkipSpaces(buffer)) { + return false; + } + + // skip the "property" string at the beginning + if (!PLY::DOM::TokenMatch(buffer, "property", 8)) { + // seems not to be a valid property entry + return false; + } + // get next word + if (!PLY::DOM::SkipSpaces(buffer)) { + return false; + } + if (PLY::DOM::TokenMatch(buffer, "list", 4)) { + pOut->bIsList = true; + + // seems to be a list. + if (EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(buffer))) { + // unable to parse list size data type + PLY::DOM::SkipLine(buffer); + return false; + } + if (!PLY::DOM::SkipSpaces(buffer)) return false; + if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(buffer))) { + // unable to parse list data type + PLY::DOM::SkipLine(buffer); + return false; + } + } else { + if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(buffer))) { + // unable to parse data type. Skip the property + PLY::DOM::SkipLine(buffer); + return false; + } + } + + if (!PLY::DOM::SkipSpaces(buffer)) + return false; + + pOut->Semantic = PLY::Property::ParseSemantic(buffer); + + if (PLY::EST_INVALID == pOut->Semantic) { + ASSIMP_LOG_INFO("Found unknown semantic in PLY file. This is OK"); + std::string(&buffer[0], &buffer[0] + strlen(&buffer[0])); + } + + PLY::DOM::SkipSpacesAndLineEnd(buffer); + return true; +} + +// ------------------------------------------------------------------------------------------------ +PLY::EElementSemantic PLY::Element::ParseSemantic(std::vector<char> &buffer) { + ai_assert(!buffer.empty()); + + PLY::EElementSemantic eOut = PLY::EEST_INVALID; + if (PLY::DOM::TokenMatch(buffer, "vertex", 6)) { + eOut = PLY::EEST_Vertex; + } else if (PLY::DOM::TokenMatch(buffer, "face", 4)) { + eOut = PLY::EEST_Face; + } else if (PLY::DOM::TokenMatch(buffer, "tristrips", 9)) { + eOut = PLY::EEST_TriStrip; + } +#if 0 + // TODO: maybe implement this? + else if (PLY::DOM::TokenMatch(buffer,"range_grid",10)) + { + eOut = PLY::EEST_Face; + } +#endif + else if (PLY::DOM::TokenMatch(buffer, "edge", 4)) { + eOut = PLY::EEST_Edge; + } else if (PLY::DOM::TokenMatch(buffer, "material", 8)) { + eOut = PLY::EEST_Material; + } else if (PLY::DOM::TokenMatch(buffer, "TextureFile", 11)) { + eOut = PLY::EEST_TextureFile; + } + + return eOut; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::Element::ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLY::Element *pOut) { + ai_assert(nullptr != pOut); + // Example format: "element vertex 8" + + // skip leading spaces + if (!PLY::DOM::SkipSpaces(buffer)) { + return false; + } + + // skip the "element" string at the beginning + if (!PLY::DOM::TokenMatch(buffer, "element", 7) && !PLY::DOM::TokenMatch(buffer, "comment", 7)) { + // seems not to be a valid property entry + return false; + } + // get next word + if (!PLY::DOM::SkipSpaces(buffer)) + return false; + + // parse the semantic of the element + pOut->eSemantic = PLY::Element::ParseSemantic(buffer); + if (PLY::EEST_INVALID == pOut->eSemantic) { + // if the exact semantic can't be determined, just store + // the original string identifier + pOut->szName = std::string(&buffer[0], &buffer[0] + strlen(&buffer[0])); + } + + if (!PLY::DOM::SkipSpaces(buffer)) + return false; + + if (PLY::EEST_TextureFile == pOut->eSemantic) { + char *endPos = &buffer[0] + (strlen(&buffer[0]) - 1); + pOut->szName = std::string(&buffer[0], endPos); + + // go to the next line + PLY::DOM::SkipSpacesAndLineEnd(buffer); + + return true; + } + + //parse the number of occurrences of this element + const char *pCur = (char *)&buffer[0]; + pOut->NumOccur = strtoul10(pCur, &pCur); + + // go to the next line + PLY::DOM::SkipSpacesAndLineEnd(buffer); + + // now parse all properties of the element + while (true) { + streamBuffer.getNextLine(buffer); + pCur = (char *)&buffer[0]; + + // skip all comments + PLY::DOM::SkipComments(buffer); + + PLY::Property prop; + if (!PLY::Property::ParseProperty(buffer, &prop)) + break; + + pOut->alProperties.push_back(prop); + } + + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::SkipSpaces(std::vector<char> &buffer) { + const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0]; + bool ret = false; + if (pCur) { + const char *szCur = pCur; + ret = Assimp::SkipSpaces(pCur, &pCur); + + uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur; + buffer.erase(buffer.begin(), buffer.begin() + iDiff); + return ret; + } + + return ret; +} + +bool PLY::DOM::SkipLine(std::vector<char> &buffer) { + const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0]; + bool ret = false; + if (pCur) { + const char *szCur = pCur; + ret = Assimp::SkipLine(pCur, &pCur); + + uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur; + buffer.erase(buffer.begin(), buffer.begin() + iDiff); + return ret; + } + + return ret; +} + +bool PLY::DOM::TokenMatch(std::vector<char> &buffer, const char *token, unsigned int len) { + const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0]; + bool ret = false; + if (pCur) { + const char *szCur = pCur; + ret = Assimp::TokenMatch(pCur, token, len); + + uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur; + buffer.erase(buffer.begin(), buffer.begin() + iDiff); + return ret; + } + + return ret; +} + +bool PLY::DOM::SkipSpacesAndLineEnd(std::vector<char> &buffer) { + const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0]; + bool ret = false; + if (pCur) { + const char *szCur = pCur; + ret = Assimp::SkipSpacesAndLineEnd(pCur, &pCur); + + uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur; + buffer.erase(buffer.begin(), buffer.begin() + iDiff); + return ret; + } + + return ret; +} + +bool PLY::DOM::SkipComments(std::vector<char> &buffer) { + ai_assert(!buffer.empty()); + + std::vector<char> nbuffer = buffer; + // skip spaces + if (!SkipSpaces(nbuffer)) { + return false; + } + + if (TokenMatch(nbuffer, "comment", 7)) { + if (!SkipSpaces(nbuffer)) + SkipLine(nbuffer); + + if (!TokenMatch(nbuffer, "TextureFile", 11)) { + SkipLine(nbuffer); + buffer = nbuffer; + return true; + } + + return true; + } + + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, bool isBinary) { + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() begin"); + + // parse all elements + while (!buffer.empty()) { + // skip all comments + PLY::DOM::SkipComments(buffer); + + PLY::Element out; + if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) { + // add the element to the list of elements + alElements.push_back(out); + } else if (TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n + // we have reached the end of the header + break; + } else { + // ignore unknown header elements + streamBuffer.getNextLine(buffer); + } + } + + if (!isBinary) // it would occur an error, if binary data start with values as space or line end. + SkipSpacesAndLineEnd(buffer); + + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() succeeded"); + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseElementInstanceLists(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLYImporter *loader) { + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceLists() begin"); + alElementData.resize(alElements.size()); + + std::vector<PLY::Element>::const_iterator i = alElements.begin(); + std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin(); + + // parse all element instances + //construct vertices and faces + for (; i != alElements.end(); ++i, ++a) { + if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip) { + PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), nullptr, loader); + } else { + (*a).alInstances.resize((*i).NumOccur); + PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), &(*a), nullptr); + } + } + + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceLists() succeeded"); + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseElementInstanceListsBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, + const char *&pCur, + unsigned int &bufferSize, + PLYImporter *loader, + bool p_bBE) { + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceListsBinary() begin"); + alElementData.resize(alElements.size()); + + std::vector<PLY::Element>::const_iterator i = alElements.begin(); + std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin(); + + // parse all element instances + for (; i != alElements.end(); ++i, ++a) { + if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip) { + PLY::ElementInstanceList::ParseInstanceListBinary(streamBuffer, buffer, pCur, bufferSize, &(*i), nullptr, loader, p_bBE); + } else { + (*a).alInstances.resize((*i).NumOccur); + PLY::ElementInstanceList::ParseInstanceListBinary(streamBuffer, buffer, pCur, bufferSize, &(*i), &(*a), nullptr, p_bBE); + } + } + + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceListsBinary() succeeded"); + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, DOM *p_pcOut, PLYImporter *loader, bool p_bBE) { + ai_assert(nullptr != p_pcOut); + ai_assert(nullptr != loader); + + std::vector<char> buffer; + streamBuffer.getNextLine(buffer); + + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() begin"); + + if (!p_pcOut->ParseHeader(streamBuffer, buffer, true)) { + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() failure"); + return false; + } + + streamBuffer.getNextBlock(buffer); + + // remove first char if it's /n in case of file with /r/n + if (((char *)&buffer[0])[0] == '\n') + buffer.erase(buffer.begin(), buffer.begin() + 1); + + unsigned int bufferSize = static_cast<unsigned int>(buffer.size()); + const char *pCur = (char *)&buffer[0]; + if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE)) { + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() failure"); + return false; + } + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() succeeded"); + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::DOM::ParseInstance(IOStreamBuffer<char> &streamBuffer, DOM *p_pcOut, PLYImporter *loader) { + ai_assert(nullptr != p_pcOut); + ai_assert(nullptr != loader); + + std::vector<char> buffer; + streamBuffer.getNextLine(buffer); + + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() begin"); + + if (!p_pcOut->ParseHeader(streamBuffer, buffer, false)) { + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() failure"); + return false; + } + + //get next line after header + streamBuffer.getNextLine(buffer); + if (!p_pcOut->ParseElementInstanceLists(streamBuffer, buffer, loader)) { + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() failure"); + return false; + } + ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() succeeded"); + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::ElementInstanceList::ParseInstanceList( + IOStreamBuffer<char> &streamBuffer, + std::vector<char> &buffer, + const PLY::Element *pcElement, + PLY::ElementInstanceList *p_pcOut, + PLYImporter *loader) { + ai_assert(nullptr != pcElement); + + // parse all elements + if (EEST_INVALID == pcElement->eSemantic || pcElement->alProperties.empty()) { + // if the element has an unknown semantic we can skip all lines + // However, there could be comments + for (unsigned int i = 0; i < pcElement->NumOccur; ++i) { + PLY::DOM::SkipComments(buffer); + PLY::DOM::SkipLine(buffer); + streamBuffer.getNextLine(buffer); + } + } else { + const char *pCur = (const char *)&buffer[0]; + // be sure to have enough storage + for (unsigned int i = 0; i < pcElement->NumOccur; ++i) { + if (p_pcOut) + PLY::ElementInstance::ParseInstance(pCur, pcElement, &p_pcOut->alInstances[i]); + else { + ElementInstance elt; + PLY::ElementInstance::ParseInstance(pCur, pcElement, &elt); + + // Create vertex or face + if (pcElement->eSemantic == EEST_Vertex) { + //call loader instance from here + loader->LoadVertex(pcElement, &elt, i); + } else if (pcElement->eSemantic == EEST_Face) { + //call loader instance from here + loader->LoadFace(pcElement, &elt, i); + } else if (pcElement->eSemantic == EEST_TriStrip) { + //call loader instance from here + loader->LoadFace(pcElement, &elt, i); + } + } + + streamBuffer.getNextLine(buffer); + pCur = (buffer.empty()) ? nullptr : (const char *)&buffer[0]; + } + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::ElementInstanceList::ParseInstanceListBinary( + IOStreamBuffer<char> &streamBuffer, + std::vector<char> &buffer, + const char *&pCur, + unsigned int &bufferSize, + const PLY::Element *pcElement, + PLY::ElementInstanceList *p_pcOut, + PLYImporter *loader, + bool p_bBE /* = false */) { + ai_assert(nullptr != pcElement); + + // we can add special handling code for unknown element semantics since + // we can't skip it as a whole block (we don't know its exact size + // due to the fact that lists could be contained in the property list + // of the unknown element) + for (unsigned int i = 0; i < pcElement->NumOccur; ++i) { + if (p_pcOut) + PLY::ElementInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, pcElement, &p_pcOut->alInstances[i], p_bBE); + else { + ElementInstance elt; + PLY::ElementInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, pcElement, &elt, p_bBE); + + // Create vertex or face + if (pcElement->eSemantic == EEST_Vertex) { + //call loader instance from here + loader->LoadVertex(pcElement, &elt, i); + } else if (pcElement->eSemantic == EEST_Face) { + //call loader instance from here + loader->LoadFace(pcElement, &elt, i); + } else if (pcElement->eSemantic == EEST_TriStrip) { + //call loader instance from here + loader->LoadFace(pcElement, &elt, i); + } + } + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::ElementInstance::ParseInstance(const char *&pCur, + const PLY::Element *pcElement, + PLY::ElementInstance *p_pcOut) { + ai_assert(nullptr != pcElement); + ai_assert(nullptr != p_pcOut); + + // allocate enough storage + p_pcOut->alProperties.resize(pcElement->alProperties.size()); + + std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin(); + std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin(); + for (; i != p_pcOut->alProperties.end(); ++i, ++a) { + if (!(PLY::PropertyInstance::ParseInstance(pCur, &(*a), &(*i)))) { + ASSIMP_LOG_WARN("Unable to parse property instance. " + "Skipping this element instance"); + + PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType); + (*i).avList.push_back(v); + } + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::ElementInstance::ParseInstanceBinary( + IOStreamBuffer<char> &streamBuffer, + std::vector<char> &buffer, + const char *&pCur, + unsigned int &bufferSize, + const PLY::Element *pcElement, + PLY::ElementInstance *p_pcOut, + bool p_bBE /* = false */) { + ai_assert(nullptr != pcElement); + ai_assert(nullptr != p_pcOut); + + // allocate enough storage + p_pcOut->alProperties.resize(pcElement->alProperties.size()); + + std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin(); + std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin(); + for (; i != p_pcOut->alProperties.end(); ++i, ++a) { + if (!(PLY::PropertyInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, &(*a), &(*i), p_bBE))) { + ASSIMP_LOG_WARN("Unable to parse binary property instance. " + "Skipping this element instance"); + + (*i).avList.push_back(PLY::PropertyInstance::DefaultValue((*a).eType)); + } + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::PropertyInstance::ParseInstance(const char *&pCur, + const PLY::Property *prop, PLY::PropertyInstance *p_pcOut) { + ai_assert(nullptr != prop); + ai_assert(nullptr != p_pcOut); + + // skip spaces at the beginning + if (!SkipSpaces(&pCur)) { + return false; + } + + if (prop->bIsList) { + // parse the number of elements in the list + PLY::PropertyInstance::ValueUnion v; + PLY::PropertyInstance::ParseValue(pCur, prop->eFirstType, &v); + + // convert to unsigned int + unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v, prop->eFirstType); + + // parse all list elements + p_pcOut->avList.resize(iNum); + for (unsigned int i = 0; i < iNum; ++i) { + if (!SkipSpaces(&pCur)) + return false; + + PLY::PropertyInstance::ParseValue(pCur, prop->eType, &p_pcOut->avList[i]); + } + } else { + // parse the property + PLY::PropertyInstance::ValueUnion v; + + PLY::PropertyInstance::ParseValue(pCur, prop->eType, &v); + p_pcOut->avList.push_back(v); + } + SkipSpacesAndLineEnd(&pCur); + return true; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::PropertyInstance::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, + const char *&pCur, + unsigned int &bufferSize, + const PLY::Property *prop, + PLY::PropertyInstance *p_pcOut, + bool p_bBE) { + ai_assert(nullptr != prop); + ai_assert(nullptr != p_pcOut); + + // parse all elements + if (prop->bIsList) { + // parse the number of elements in the list + PLY::PropertyInstance::ValueUnion v; + PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eFirstType, &v, p_bBE); + + // convert to unsigned int + unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v, prop->eFirstType); + + // parse all list elements + p_pcOut->avList.resize(iNum); + for (unsigned int i = 0; i < iNum; ++i) { + PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eType, &p_pcOut->avList[i], p_bBE); + } + } else { + // parse the property + PLY::PropertyInstance::ValueUnion v; + PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eType, &v, p_bBE); + p_pcOut->avList.push_back(v); + } + return true; +} + +// ------------------------------------------------------------------------------------------------ +PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue(PLY::EDataType eType) { + PLY::PropertyInstance::ValueUnion out; + + switch (eType) { + case EDT_Float: + out.fFloat = 0.f; + return out; + + case EDT_Double: + out.fDouble = 0.; + return out; + + default:; + }; + out.iUInt = 0; + return out; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::PropertyInstance::ParseValue(const char *&pCur, + PLY::EDataType eType, + PLY::PropertyInstance::ValueUnion *out) { + ai_assert(nullptr != pCur); + ai_assert(nullptr != out); + + //calc element size + bool ret = true; + switch (eType) { + case EDT_UInt: + case EDT_UShort: + case EDT_UChar: + + out->iUInt = (uint32_t)strtoul10(pCur, &pCur); + break; + + case EDT_Int: + case EDT_Short: + case EDT_Char: + + out->iInt = (int32_t)strtol10(pCur, &pCur); + break; + + case EDT_Float: + // technically this should cast to float, but people tend to use float descriptors for double data + // this is the best way to not risk losing precision on import and it doesn't hurt to do this + ai_real f; + pCur = fast_atoreal_move<ai_real>(pCur, f); + out->fFloat = (ai_real)f; + break; + + case EDT_Double: + double d; + pCur = fast_atoreal_move<double>(pCur, d); + out->fDouble = (double)d; + break; + + case EDT_INVALID: + default: + ret = false; + break; + } + + return ret; +} + +// ------------------------------------------------------------------------------------------------ +bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer, + std::vector<char> &buffer, + const char *&pCur, + unsigned int &bufferSize, + PLY::EDataType eType, + PLY::PropertyInstance::ValueUnion *out, + bool p_bBE) { + ai_assert(nullptr != out); + + //calc element size + unsigned int lsize = 0; + switch (eType) { + case EDT_Char: + case EDT_UChar: + lsize = 1; + break; + + case EDT_UShort: + case EDT_Short: + lsize = 2; + break; + + case EDT_UInt: + case EDT_Int: + case EDT_Float: + lsize = 4; + break; + + case EDT_Double: + lsize = 8; + break; + + case EDT_INVALID: + default: + break; + } + + //read the next file block if needed + if (bufferSize < lsize) { + std::vector<char> nbuffer; + if (streamBuffer.getNextBlock(nbuffer)) { + //concat buffer contents + buffer = std::vector<char>(buffer.end() - bufferSize, buffer.end()); + buffer.insert(buffer.end(), nbuffer.begin(), nbuffer.end()); + nbuffer.clear(); + bufferSize = static_cast<unsigned int>(buffer.size()); + pCur = (char *)&buffer[0]; + } else { + throw DeadlyImportError("Invalid .ply file: File corrupted"); + } + } + + bool ret = true; + switch (eType) { + case EDT_UInt: { + uint32_t t; + memcpy(&t, pCur, sizeof(uint32_t)); + pCur += sizeof(uint32_t); + + // Swap endianness + if (p_bBE) ByteSwap::Swap(&t); + out->iUInt = t; + break; + } + + case EDT_UShort: { + uint16_t t; + memcpy(&t, pCur, sizeof(uint16_t)); + pCur += sizeof(uint16_t); + + // Swap endianness + if (p_bBE) ByteSwap::Swap(&t); + out->iUInt = t; + break; + } + + case EDT_UChar: { + uint8_t t; + memcpy(&t, pCur, sizeof(uint8_t)); + pCur += sizeof(uint8_t); + out->iUInt = t; + break; + } + + case EDT_Int: { + int32_t t; + memcpy(&t, pCur, sizeof(int32_t)); + pCur += sizeof(int32_t); + + // Swap endianness + if (p_bBE) ByteSwap::Swap(&t); + out->iInt = t; + break; + } + + case EDT_Short: { + int16_t t; + memcpy(&t, pCur, sizeof(int16_t)); + pCur += sizeof(int16_t); + + // Swap endianness + if (p_bBE) ByteSwap::Swap(&t); + out->iInt = t; + break; + } + + case EDT_Char: { + int8_t t; + memcpy(&t, pCur, sizeof(int8_t)); + pCur += sizeof(int8_t); + out->iInt = t; + break; + } + + case EDT_Float: { + float t; + memcpy(&t, pCur, sizeof(float)); + pCur += sizeof(float); + + // Swap endianness + if (p_bBE) ByteSwap::Swap(&t); + out->fFloat = t; + break; + } + case EDT_Double: { + double t; + memcpy(&t, pCur, sizeof(double)); + pCur += sizeof(double); + + // Swap endianness + if (p_bBE) ByteSwap::Swap(&t); + out->fDouble = t; + break; + } + default: + ret = false; + } + + bufferSize -= lsize; + + return ret; +} + +#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER diff --git a/libs/assimp/code/AssetLib/Ply/PlyParser.h b/libs/assimp/code/AssetLib/Ply/PlyParser.h new file mode 100644 index 0000000..8634929 --- /dev/null +++ b/libs/assimp/code/AssetLib/Ply/PlyParser.h @@ -0,0 +1,488 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines the helper data structures for importing PLY files */ +#pragma once +#ifndef AI_PLYFILEHELPER_H_INC +#define AI_PLYFILEHELPER_H_INC + +#include <assimp/ParsingUtils.h> +#include <assimp/IOStreamBuffer.h> +#include <vector> + +namespace Assimp { + +//pre-declaration +class PLYImporter; + +// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/ +// http://w3.impa.br/~lvelho/outgoing/sossai/old/ViHAP_D4.4.2_PLY_format_v1.1.pdf +// http://www.okino.com/conv/exp_ply.htm +namespace PLY { + +// --------------------------------------------------------------------------------- +/* +name type number of bytes +--------------------------------------- +char character 1 +uchar unsigned character 1 +short short integer 2 +ushort unsigned short integer 2 +int integer 4 +uint unsigned integer 4 +float single-precision float 4 +double double-precision float 8 + +int8 +int16 +uint8 ... forms are also used +*/ +enum EDataType { + EDT_Char = 0x0u, + EDT_UChar, + EDT_Short, + EDT_UShort, + EDT_Int, + EDT_UInt, + EDT_Float, + EDT_Double, + + // Marks invalid entries + EDT_INVALID +}; + +// --------------------------------------------------------------------------------- +/** \brief Specifies semantics for PLY element properties + * + * Semantics define the usage of a property, e.g. x coordinate +*/ +enum ESemantic { + //! vertex position x coordinate + EST_XCoord = 0x0u, + //! vertex position x coordinate + EST_YCoord, + //! vertex position x coordinate + EST_ZCoord, + + //! vertex normal x coordinate + EST_XNormal, + //! vertex normal y coordinate + EST_YNormal, + //! vertex normal z coordinate + EST_ZNormal, + + //! u texture coordinate + EST_UTextureCoord, + //! v texture coordinate + EST_VTextureCoord, + + //! vertex colors, red channel + EST_Red, + //! vertex colors, green channel + EST_Green, + //! vertex colors, blue channel + EST_Blue, + //! vertex colors, alpha channel + EST_Alpha, + + //! vertex index list + EST_VertexIndex, + + //! texture index + EST_TextureIndex, + + //! texture coordinates (stored as element of a face) + EST_TextureCoordinates, + + //! material index + EST_MaterialIndex, + + //! ambient color, red channel + EST_AmbientRed, + //! ambient color, green channel + EST_AmbientGreen, + //! ambient color, blue channel + EST_AmbientBlue, + //! ambient color, alpha channel + EST_AmbientAlpha, + + //! diffuse color, red channel + EST_DiffuseRed, + //! diffuse color, green channel + EST_DiffuseGreen, + //! diffuse color, blue channel + EST_DiffuseBlue, + //! diffuse color, alpha channel + EST_DiffuseAlpha, + + //! specular color, red channel + EST_SpecularRed, + //! specular color, green channel + EST_SpecularGreen, + //! specular color, blue channel + EST_SpecularBlue, + //! specular color, alpha channel + EST_SpecularAlpha, + + //! specular power for phong shading + EST_PhongPower, + + //! opacity between 0 and 1 + EST_Opacity, + + //! Marks invalid entries + EST_INVALID +}; + +// --------------------------------------------------------------------------------- +/** \brief Specifies semantics for PLY elements + * + * Semantics define the usage of an element, e.g. vertex or material +*/ +enum EElementSemantic { + //! The element is a vertex + EEST_Vertex = 0x0u, + + //! The element is a face description (index table) + EEST_Face, + + //! The element is a triangle-strip description (index table) + EEST_TriStrip, + + //! The element is an edge description (ignored) + EEST_Edge, + + //! The element is a material description + EEST_Material, + + //! texture path + EEST_TextureFile, + + //! Marks invalid entries + EEST_INVALID +}; + +// --------------------------------------------------------------------------------- +/** \brief Helper class for a property in a PLY file. + * + * This can e.g. be a part of the vertex declaration + */ +class Property { +public: + //! Default constructor + Property() AI_NO_EXCEPT + : eType (EDT_Int) + , Semantic() + , bIsList(false) + , eFirstType(EDT_UChar) { + // empty + } + + //! Data type of the property + EDataType eType; + + //! Semantical meaning of the property + ESemantic Semantic; + + //! Of the semantic of the property could not be parsed: + //! Contains the semantic specified in the file + std::string szName; + + //! Specifies whether the data type is a list where + //! the first element specifies the size of the list + bool bIsList; + EDataType eFirstType; + + // ------------------------------------------------------------------- + //! Parse a property from a string. The end of the + //! string is either '\n', '\r' or '\0'. Return value is false + //! if the input string is NOT a valid property (E.g. does + //! not start with the "property" keyword) + static bool ParseProperty(std::vector<char> &buffer, Property* pOut); + + // ------------------------------------------------------------------- + //! Parse a data type from a string + static EDataType ParseDataType(std::vector<char> &buffer); + + // ------------------------------------------------------------------- + //! Parse a semantic from a string + static ESemantic ParseSemantic(std::vector<char> &buffer); +}; + +// --------------------------------------------------------------------------------- +/** \brief Helper class for an element in a PLY file. + * + * This can e.g. be the vertex declaration. Elements contain a + * well-defined number of properties. + */ +class Element { +public: + //! Default constructor + Element() AI_NO_EXCEPT + : eSemantic (EEST_INVALID) + , NumOccur(0) { + // empty + } + + //! List of properties assigned to the element + //! std::vector to support operator[] + std::vector<Property> alProperties; + + //! Semantic of the element + EElementSemantic eSemantic; + + //! Of the semantic of the element could not be parsed: + //! Contains the semantic specified in the file + std::string szName; + + //! How many times will the element occur? + unsigned int NumOccur; + + + // ------------------------------------------------------------------- + //! Parse an element from a string. + //! The function will parse all properties contained in the + //! element, too. + static bool ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, Element* pOut); + + // ------------------------------------------------------------------- + //! Parse a semantic from a string + static EElementSemantic ParseSemantic(std::vector<char> &buffer); +}; + +// --------------------------------------------------------------------------------- +/** \brief Instance of a property in a PLY file + */ +class PropertyInstance +{ +public: + + //! Default constructor + PropertyInstance() AI_NO_EXCEPT { + // empty + } + + union ValueUnion + { + + //! uInt32 representation of the property. All + // uint types are automatically converted to uint32 + uint32_t iUInt; + + //! Int32 representation of the property. All + // int types are automatically converted to int32 + int32_t iInt; + + //! Float32 representation of the property + float fFloat; + + //! Float64 representation of the property + double fDouble; + + }; + + // ------------------------------------------------------------------- + //! List of all values parsed. Contains only one value + // for non-list properties + std::vector<ValueUnion> avList; + + // ------------------------------------------------------------------- + //! Parse a property instance + static bool ParseInstance(const char* &pCur, + const Property* prop, PropertyInstance* p_pcOut); + + // ------------------------------------------------------------------- + //! Parse a property instance in binary format + static bool ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, + const char* &pCur, unsigned int &bufferSize, const Property* prop, PropertyInstance* p_pcOut, bool p_bBE); + + // ------------------------------------------------------------------- + //! Get the default value for a given data type + static ValueUnion DefaultValue(EDataType eType); + + // ------------------------------------------------------------------- + //! Parse a value + static bool ParseValue(const char* &pCur, EDataType eType, ValueUnion* out); + + // ------------------------------------------------------------------- + //! Parse a binary value + static bool ParseValueBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, + const char* &pCur, unsigned int &bufferSize, EDataType eType, ValueUnion* out, bool p_bBE); + + // ------------------------------------------------------------------- + //! Convert a property value to a given type TYPE + template <typename TYPE> + static TYPE ConvertTo(ValueUnion v, EDataType eType); +}; + +// --------------------------------------------------------------------------------- +/** \brief Class for an element instance in a PLY file + */ +class ElementInstance { +public: + //! Default constructor + ElementInstance() AI_NO_EXCEPT + : alProperties() { + // empty + } + + //! List of all parsed properties + std::vector< PropertyInstance > alProperties; + + // ------------------------------------------------------------------- + //! Parse an element instance + static bool ParseInstance(const char* &pCur, + const Element* pcElement, ElementInstance* p_pcOut); + + // ------------------------------------------------------------------- + //! Parse a binary element instance + static bool ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, + const char* &pCur, unsigned int &bufferSize, const Element* pcElement, ElementInstance* p_pcOut, bool p_bBE); +}; + +// --------------------------------------------------------------------------------- +/** \brief Class for an element instance list in a PLY file + */ +class ElementInstanceList +{ +public: + + //! Default constructor + ElementInstanceList() AI_NO_EXCEPT + : alInstances() { + // empty + } + + //! List of all element instances + std::vector< ElementInstance > alInstances; + + // ------------------------------------------------------------------- + //! Parse an element instance list + static bool ParseInstanceList(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, + const Element* pcElement, ElementInstanceList* p_pcOut, PLYImporter* loader); + + // ------------------------------------------------------------------- + //! Parse a binary element instance list + static bool ParseInstanceListBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, + const char* &pCur, unsigned int &bufferSize, const Element* pcElement, ElementInstanceList* p_pcOut, PLYImporter* loader, bool p_bBE); +}; +// --------------------------------------------------------------------------------- +/** \brief Class to represent the document object model of an ASCII or binary + * (both little and big-endian) PLY file + */ +class DOM +{ +public: + + //! Default constructor + DOM() AI_NO_EXCEPT + : alElements() + , alElementData() { + + } + + + //! Contains all elements of the file format + std::vector<Element> alElements; + //! Contains the real data of each element's instance list + std::vector<ElementInstanceList> alElementData; + + //! Parse the DOM for a PLY file. The input string is assumed + //! to be terminated with zero + static bool ParseInstance(IOStreamBuffer<char> &streamBuffer, DOM* p_pcOut, PLYImporter* loader); + static bool ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, DOM* p_pcOut, PLYImporter* loader, bool p_bBE); + + //! Skip all comment lines after this + static bool SkipComments(std::vector<char> &buffer); + + static bool SkipSpaces(std::vector<char> &buffer); + + static bool SkipLine(std::vector<char> &buffer); + + static bool TokenMatch(std::vector<char> &buffer, const char* token, unsigned int len); + + static bool SkipSpacesAndLineEnd(std::vector<char> &buffer); + +private: + + // ------------------------------------------------------------------- + //! Handle the file header and read all element descriptions + bool ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, bool p_bBE); + + // ------------------------------------------------------------------- + //! Read in all element instance lists + bool ParseElementInstanceLists(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLYImporter* loader); + + // ------------------------------------------------------------------- + //! Read in all element instance lists for a binary file format + bool ParseElementInstanceListsBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, const char* &pCur, unsigned int &bufferSize, PLYImporter* loader, bool p_bBE); +}; + +// --------------------------------------------------------------------------------- +template <typename TYPE> +inline TYPE PLY::PropertyInstance::ConvertTo( + PLY::PropertyInstance::ValueUnion v, PLY::EDataType eType) +{ + switch (eType) + { + case EDT_Float: + return (TYPE)v.fFloat; + case EDT_Double: + return (TYPE)v.fDouble; + + case EDT_UInt: + case EDT_UShort: + case EDT_UChar: + return (TYPE)v.iUInt; + + case EDT_Int: + case EDT_Short: + case EDT_Char: + return (TYPE)v.iInt; + default: ; + }; + return (TYPE)0; +} + +} // Namespace PLY +} // Namespace AssImp + +#endif // !! include guard |