Model loading and texturing

This commit is contained in:
Dane Johnson
2021-01-18 18:25:47 -06:00
parent 66bf7776c7
commit 155b572aca
1283 changed files with 533814 additions and 42 deletions

View File

@@ -0,0 +1,405 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 == NULL) {
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 == NULL) {
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) << (c == f.mNumIndices-1 ? 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)

View File

@@ -0,0 +1,88 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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

966
thirdparty/assimp/code/Ply/PlyLoader.cpp vendored Normal file
View File

@@ -0,0 +1,966 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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/Macros.h>
#include <memory>
#include <assimp/IOSystem.hpp>
#include <assimp/scene.h>
#include <assimp/importerdesc.h>
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];
}
}
// ------------------------------------------------------------------------------------------------
// 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 {
const std::string extension = GetExtension(pFile);
if ( extension == "ply" ) {
return true;
} else if (!extension.length() || checkSig) {
if ( !pIOHandler ) {
return true;
}
static const char* tokens[] = { "ply" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
}
// ------------------------------------------------------------------------------------------------
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: Magic number \'ply\' is no there");
}
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(NULL != 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(NULL != 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 = NULL;
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 (NULL != 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)
{
const int two_sided = 1;
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

140
thirdparty/assimp/code/Ply/PlyLoader.h vendored Normal file
View File

@@ -0,0 +1,140 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
*/
#ifndef AI_PLYLOADER_H_INCLUDED
#define AI_PLYLOADER_H_INCLUDED
#include <assimp/BaseImporter.h>
#include <assimp/types.h>
#include "PlyParser.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();
public:
// -------------------------------------------------------------------
/** 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;
// -------------------------------------------------------------------
/** 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;
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler);
protected:
// -------------------------------------------------------------------
/** 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

1142
thirdparty/assimp/code/Ply/PlyParser.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

489
thirdparty/assimp/code/Ply/PlyParser.h vendored Normal file
View File

@@ -0,0 +1,489 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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