diff options
Diffstat (limited to 'src/mesh/assimp-master/code/AssetLib/NFF')
-rw-r--r-- | src/mesh/assimp-master/code/AssetLib/NFF/NFFLoader.cpp | 1168 | ||||
-rw-r--r-- | src/mesh/assimp-master/code/AssetLib/NFF/NFFLoader.h | 191 |
2 files changed, 0 insertions, 1359 deletions
diff --git a/src/mesh/assimp-master/code/AssetLib/NFF/NFFLoader.cpp b/src/mesh/assimp-master/code/AssetLib/NFF/NFFLoader.cpp deleted file mode 100644 index 48271d1..0000000 --- a/src/mesh/assimp-master/code/AssetLib/NFF/NFFLoader.cpp +++ /dev/null @@ -1,1168 +0,0 @@ -/* ---------------------------------------------------------------------------- -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 STL importer class */ - -#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER - -// internal headers -#include "NFFLoader.h" -#include <assimp/ParsingUtils.h> -#include <assimp/RemoveComments.h> -#include <assimp/StandardShapes.h> -#include <assimp/fast_atof.h> -#include <assimp/importerdesc.h> -#include <assimp/qnan.h> -#include <assimp/scene.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/IOSystem.hpp> -#include <memory> - -using namespace Assimp; - -static const aiImporterDesc desc = { - "Neutral File Format Importer", - "", - "", - "", - aiImporterFlags_SupportBinaryFlavour, - 0, - 0, - 0, - 0, - "enff nff" -}; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -NFFImporter::NFFImporter() {} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -NFFImporter::~NFFImporter() {} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool NFFImporter::CanRead(const std::string & pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { - return SimpleExtensionCheck(pFile, "nff", "enff"); -} - -// ------------------------------------------------------------------------------------------------ -// Get the list of all supported file extensions -const aiImporterDesc *NFFImporter::GetInfo() const { - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -#define AI_NFF_PARSE_FLOAT(f) \ - SkipSpaces(&sz); \ - if (!::IsLineEnd(*sz)) sz = fast_atoreal_move<ai_real>(sz, (ai_real &)f); - -// ------------------------------------------------------------------------------------------------ -#define AI_NFF_PARSE_TRIPLE(v) \ - AI_NFF_PARSE_FLOAT(v[0]) \ - AI_NFF_PARSE_FLOAT(v[1]) \ - AI_NFF_PARSE_FLOAT(v[2]) - -// ------------------------------------------------------------------------------------------------ -#define AI_NFF_PARSE_SHAPE_INFORMATION() \ - aiVector3D center, radius(1.0f, get_qnan(), get_qnan()); \ - AI_NFF_PARSE_TRIPLE(center); \ - AI_NFF_PARSE_TRIPLE(radius); \ - if (is_qnan(radius.z)) radius.z = radius.x; \ - if (is_qnan(radius.y)) radius.y = radius.x; \ - curMesh.radius = radius; \ - curMesh.center = center; - -// ------------------------------------------------------------------------------------------------ -#define AI_NFF2_GET_NEXT_TOKEN() \ - do { \ - if (!GetNextLine(buffer, line)) { \ - ASSIMP_LOG_WARN("NFF2: Unexpected EOF, can't read next token"); \ - break; \ - } \ - SkipSpaces(line, &sz); \ - } while (IsLineEnd(*sz)) - -// ------------------------------------------------------------------------------------------------ -// Loads the material table for the NFF2 file format from an external file -void NFFImporter::LoadNFF2MaterialTable(std::vector<ShadingInfo> &output, - const std::string &path, IOSystem *pIOHandler) { - std::unique_ptr<IOStream> file(pIOHandler->Open(path, "rb")); - - // Check whether we can read from the file - if (!file.get()) { - ASSIMP_LOG_ERROR("NFF2: Unable to open material library ", path, "."); - return; - } - - // get the size of the file - const unsigned int m = (unsigned int)file->FileSize(); - - // allocate storage and copy the contents of the file to a memory buffer - // (terminate it with zero) - std::vector<char> mBuffer2(m + 1); - TextFileToBuffer(file.get(), mBuffer2); - const char *buffer = &mBuffer2[0]; - - // First of all: remove all comments from the file - CommentRemover::RemoveLineComments("//", &mBuffer2[0]); - - // The file should start with the magic sequence "mat" - if (!TokenMatch(buffer, "mat", 3)) { - ASSIMP_LOG_ERROR("NFF2: Not a valid material library ", path, "."); - return; - } - - ShadingInfo *curShader = nullptr; - - // No read the file line per line - char line[4096]; - const char *sz; - while (GetNextLine(buffer, line)) { - SkipSpaces(line, &sz); - - // 'version' defines the version of the file format - if (TokenMatch(sz, "version", 7)) { - ASSIMP_LOG_INFO("NFF (Sense8) material library file format: ", std::string(sz)); - } - // 'matdef' starts a new material in the file - else if (TokenMatch(sz, "matdef", 6)) { - // add a new material to the list - output.push_back(ShadingInfo()); - curShader = &output.back(); - - // parse the name of the material - } else if (!TokenMatch(sz, "valid", 5)) { - // check whether we have an active material at the moment - if (!IsLineEnd(*sz)) { - if (!curShader) { - ASSIMP_LOG_ERROR("NFF2 material library: Found element ", sz, "but there is no active material"); - continue; - } - } else - continue; - - // now read the material property and determine its type - aiColor3D c; - if (TokenMatch(sz, "ambient", 7)) { - AI_NFF_PARSE_TRIPLE(c); - curShader->ambient = c; - } else if (TokenMatch(sz, "diffuse", 7) || TokenMatch(sz, "ambientdiffuse", 14) /* correct? */) { - AI_NFF_PARSE_TRIPLE(c); - curShader->diffuse = curShader->ambient = c; - } else if (TokenMatch(sz, "specular", 8)) { - AI_NFF_PARSE_TRIPLE(c); - curShader->specular = c; - } else if (TokenMatch(sz, "emission", 8)) { - AI_NFF_PARSE_TRIPLE(c); - curShader->emissive = c; - } else if (TokenMatch(sz, "shininess", 9)) { - AI_NFF_PARSE_FLOAT(curShader->shininess); - } else if (TokenMatch(sz, "opacity", 7)) { - AI_NFF_PARSE_FLOAT(curShader->opacity); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void NFFImporter::InternReadFile(const std::string &pFile, - aiScene *pScene, IOSystem *pIOHandler) { - std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb")); - - // Check whether we can read from the file - if (!file.get()) - throw DeadlyImportError("Failed to open NFF file ", pFile, "."); - - // allocate storage and copy the contents of the file to a memory buffer - // (terminate it with zero) - std::vector<char> mBuffer2; - TextFileToBuffer(file.get(), mBuffer2); - const char *buffer = &mBuffer2[0]; - - // mesh arrays - separate here to make the handling of the pointers below easier. - std::vector<MeshInfo> meshes; - std::vector<MeshInfo> meshesWithNormals; - std::vector<MeshInfo> meshesWithUVCoords; - std::vector<MeshInfo> meshesLocked; - - char line[4096]; - const char *sz; - - // camera parameters - aiVector3D camPos, camUp(0.f, 1.f, 0.f), camLookAt(0.f, 0.f, 1.f); - ai_real angle = 45.f; - aiVector2D resolution; - - bool hasCam = false; - - MeshInfo *currentMeshWithNormals = nullptr; - MeshInfo *currentMesh = nullptr; - MeshInfo *currentMeshWithUVCoords = nullptr; - - ShadingInfo s; // current material info - - // degree of tessellation - unsigned int iTesselation = 4; - - // some temporary variables we need to parse the file - unsigned int sphere = 0, - cylinder = 0, - cone = 0, - numNamed = 0, - dodecahedron = 0, - octahedron = 0, - tetrahedron = 0, - hexahedron = 0; - - // lights imported from the file - std::vector<Light> lights; - - // check whether this is the NFF2 file format - if (TokenMatch(buffer, "nff", 3)) { - const ai_real qnan = get_qnan(); - const aiColor4D cQNAN = aiColor4D(qnan, 0.f, 0.f, 1.f); - const aiVector3D vQNAN = aiVector3D(qnan, 0.f, 0.f); - - // another NFF file format ... just a raw parser has been implemented - // no support for further details, I don't think it is worth the effort - // http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/nff/nff2.html - // http://www.netghost.narod.ru/gff/graphics/summary/sense8.htm - - // First of all: remove all comments from the file - CommentRemover::RemoveLineComments("//", &mBuffer2[0]); - - while (GetNextLine(buffer, line)) { - SkipSpaces(line, &sz); - if (TokenMatch(sz, "version", 7)) { - ASSIMP_LOG_INFO("NFF (Sense8) file format: ", sz); - } else if (TokenMatch(sz, "viewpos", 7)) { - AI_NFF_PARSE_TRIPLE(camPos); - hasCam = true; - } else if (TokenMatch(sz, "viewdir", 7)) { - AI_NFF_PARSE_TRIPLE(camLookAt); - hasCam = true; - } - // This starts a new object section - else if (!IsSpaceOrNewLine(*sz)) { - unsigned int subMeshIdx = 0; - - // read the name of the object, skip all spaces - // at the end of it. - const char *sz3 = sz; - while (!IsSpaceOrNewLine(*sz)) - ++sz; - std::string objectName = std::string(sz3, (unsigned int)(sz - sz3)); - - const unsigned int objStart = (unsigned int)meshes.size(); - - // There could be a material table in a separate file - std::vector<ShadingInfo> materialTable; - while (true) { - AI_NFF2_GET_NEXT_TOKEN(); - - // material table - an external file - if (TokenMatch(sz, "mtable", 6)) { - SkipSpaces(&sz); - sz3 = sz; - while (!IsSpaceOrNewLine(*sz)) - ++sz; - const unsigned int diff = (unsigned int)(sz - sz3); - if (!diff) - ASSIMP_LOG_WARN("NFF2: Found empty mtable token"); - else { - // The material table has the file extension .mat. - // If it is not there, we need to append it - std::string path = std::string(sz3, diff); - if (std::string::npos == path.find_last_of(".mat")) { - path.append(".mat"); - } - - // Now extract the working directory from the path to - // this file and append the material library filename - // to it. - std::string::size_type sepPos; - if ((std::string::npos == (sepPos = path.find_last_of('\\')) || !sepPos) && - (std::string::npos == (sepPos = path.find_last_of('/')) || !sepPos)) { - sepPos = pFile.find_last_of('\\'); - if (std::string::npos == sepPos) { - sepPos = pFile.find_last_of('/'); - } - if (std::string::npos != sepPos) { - path = pFile.substr(0, sepPos + 1) + path; - } - } - LoadNFF2MaterialTable(materialTable, path, pIOHandler); - } - } else - break; - } - - // read the numbr of vertices - unsigned int num = ::strtoul10(sz, &sz); - - // temporary storage - std::vector<aiColor4D> tempColors; - std::vector<aiVector3D> tempPositions, tempTextureCoords, tempNormals; - - bool hasNormals = false, hasUVs = false, hasColor = false; - - tempPositions.reserve(num); - tempColors.reserve(num); - tempNormals.reserve(num); - tempTextureCoords.reserve(num); - for (unsigned int i = 0; i < num; ++i) { - AI_NFF2_GET_NEXT_TOKEN(); - aiVector3D v; - AI_NFF_PARSE_TRIPLE(v); - tempPositions.push_back(v); - - // parse all other attributes in the line - while (true) { - SkipSpaces(&sz); - if (IsLineEnd(*sz)) break; - - // color definition - if (TokenMatch(sz, "0x", 2)) { - hasColor = true; - unsigned int numIdx = ::strtoul16(sz, &sz); - aiColor4D clr; - clr.a = 1.f; - - // 0xRRGGBB - clr.r = ((numIdx >> 16u) & 0xff) / 255.f; - clr.g = ((numIdx >> 8u) & 0xff) / 255.f; - clr.b = ((numIdx)&0xff) / 255.f; - tempColors.push_back(clr); - } - // normal vector - else if (TokenMatch(sz, "norm", 4)) { - hasNormals = true; - AI_NFF_PARSE_TRIPLE(v); - tempNormals.push_back(v); - } - // UV coordinate - else if (TokenMatch(sz, "uv", 2)) { - hasUVs = true; - AI_NFF_PARSE_FLOAT(v.x); - AI_NFF_PARSE_FLOAT(v.y); - v.z = 0.f; - tempTextureCoords.push_back(v); - } - } - - // fill in dummies for all attributes that have not been set - if (tempNormals.size() != tempPositions.size()) - tempNormals.push_back(vQNAN); - - if (tempTextureCoords.size() != tempPositions.size()) - tempTextureCoords.push_back(vQNAN); - - if (tempColors.size() != tempPositions.size()) - tempColors.push_back(cQNAN); - } - - AI_NFF2_GET_NEXT_TOKEN(); - if (!num) throw DeadlyImportError("NFF2: There are zero vertices"); - num = ::strtoul10(sz, &sz); - - std::vector<unsigned int> tempIdx; - tempIdx.reserve(10); - for (unsigned int i = 0; i < num; ++i) { - AI_NFF2_GET_NEXT_TOKEN(); - SkipSpaces(line, &sz); - unsigned int numIdx = ::strtoul10(sz, &sz); - - // read all faces indices - if (numIdx) { - // mesh.faces.push_back(numIdx); - // tempIdx.erase(tempIdx.begin(),tempIdx.end()); - tempIdx.resize(numIdx); - - for (unsigned int a = 0; a < numIdx; ++a) { - SkipSpaces(sz, &sz); - unsigned int m = ::strtoul10(sz, &sz); - if (m >= (unsigned int)tempPositions.size()) { - ASSIMP_LOG_ERROR("NFF2: Vertex index overflow"); - m = 0; - } - // mesh.vertices.push_back (tempPositions[idx]); - tempIdx[a] = m; - } - } - - // build a temporary shader object for the face. - ShadingInfo shader; - unsigned int matIdx = 0; - - // white material color - we have vertex colors - shader.color = aiColor3D(1.f, 1.f, 1.f); - aiColor4D c = aiColor4D(1.f, 1.f, 1.f, 1.f); - while (true) { - SkipSpaces(sz, &sz); - if (IsLineEnd(*sz)) break; - - // per-polygon colors - if (TokenMatch(sz, "0x", 2)) { - hasColor = true; - const char *sz2 = sz; - numIdx = ::strtoul16(sz, &sz); - const unsigned int diff = (unsigned int)(sz - sz2); - - // 0xRRGGBB - if (diff > 3) { - c.r = ((numIdx >> 16u) & 0xff) / 255.f; - c.g = ((numIdx >> 8u) & 0xff) / 255.f; - c.b = ((numIdx)&0xff) / 255.f; - } - // 0xRGB - else { - c.r = ((numIdx >> 8u) & 0xf) / 16.f; - c.g = ((numIdx >> 4u) & 0xf) / 16.f; - c.b = ((numIdx)&0xf) / 16.f; - } - } - // TODO - implement texture mapping here -#if 0 - // mirror vertex texture coordinate? - else if (TokenMatch(sz,"mirror",6)) - { - } - // texture coordinate scaling - else if (TokenMatch(sz,"scale",5)) - { - } - // texture coordinate translation - else if (TokenMatch(sz,"trans",5)) - { - } - // texture coordinate rotation angle - else if (TokenMatch(sz,"rot",3)) - { - } -#endif - - // texture file name for this polygon + mapping information - else if ('_' == sz[0]) { - // get mapping information - switch (sz[1]) { - case 'v': - case 'V': - - shader.shaded = false; - break; - - case 't': - case 'T': - case 'u': - case 'U': - - ASSIMP_LOG_WARN("Unsupported NFF2 texture attribute: trans"); - }; - if (!sz[1] || '_' != sz[2]) { - ASSIMP_LOG_WARN("NFF2: Expected underscore after texture attributes"); - continue; - } - const char *sz2 = sz + 3; - while (!IsSpaceOrNewLine(*sz)) - ++sz; - const unsigned int diff = (unsigned int)(sz - sz2); - if (diff) shader.texFile = std::string(sz2, diff); - } - - // Two-sided material? - else if (TokenMatch(sz, "both", 4)) { - shader.twoSided = true; - } - - // Material ID? - else if (!materialTable.empty() && TokenMatch(sz, "matid", 5)) { - SkipSpaces(&sz); - matIdx = ::strtoul10(sz, &sz); - if (matIdx >= materialTable.size()) { - ASSIMP_LOG_ERROR("NFF2: Material index overflow."); - matIdx = 0; - } - - // now combine our current shader with the shader we - // read from the material table. - ShadingInfo &mat = materialTable[matIdx]; - shader.ambient = mat.ambient; - shader.diffuse = mat.diffuse; - shader.emissive = mat.emissive; - shader.opacity = mat.opacity; - shader.specular = mat.specular; - shader.shininess = mat.shininess; - } else - SkipToken(sz); - } - - // search the list of all shaders we have for this object whether - // there is an identical one. In this case, we append our mesh - // data to it. - MeshInfo *mesh = nullptr; - for (std::vector<MeshInfo>::iterator it = meshes.begin() + objStart, end = meshes.end(); - it != end; ++it) { - if ((*it).shader == shader && (*it).matIndex == matIdx) { - // we have one, we can append our data to it - mesh = &(*it); - } - } - if (!mesh) { - meshes.push_back(MeshInfo(PatchType_Simple, false)); - mesh = &meshes.back(); - mesh->matIndex = matIdx; - - // We need to add a new mesh to the list. We assign - // an unique name to it to make sure the scene will - // pass the validation step for the moment. - // TODO: fix naming of objects in the scenegraph later - if (objectName.length()) { - ::strcpy(mesh->name, objectName.c_str()); - ASSIMP_itoa10(&mesh->name[objectName.length()], 30, subMeshIdx++); - } - - // copy the shader to the mesh. - mesh->shader = shader; - } - - // fill the mesh with data - if (!tempIdx.empty()) { - mesh->faces.push_back((unsigned int)tempIdx.size()); - for (std::vector<unsigned int>::const_iterator it = tempIdx.begin(), end = tempIdx.end(); - it != end; ++it) { - unsigned int m = *it; - - // copy colors -vertex color specifications override polygon color specifications - if (hasColor) { - const aiColor4D &clr = tempColors[m]; - mesh->colors.push_back((is_qnan(clr.r) ? c : clr)); - } - - // positions should always be there - mesh->vertices.push_back(tempPositions[m]); - - // copy normal vectors - if (hasNormals) - mesh->normals.push_back(tempNormals[m]); - - // copy texture coordinates - if (hasUVs) - mesh->uvs.push_back(tempTextureCoords[m]); - } - } - } - if (!num) throw DeadlyImportError("NFF2: There are zero faces"); - } - } - camLookAt = camLookAt + camPos; - } else // "Normal" Neutral file format that is quite more common - { - while (GetNextLine(buffer, line)) { - sz = line; - if ('p' == line[0] || TokenMatch(sz, "tpp", 3)) { - MeshInfo *out = nullptr; - - // 'tpp' - texture polygon patch primitive - if ('t' == line[0]) { - currentMeshWithUVCoords = nullptr; - for (auto &mesh : meshesWithUVCoords) { - if (mesh.shader == s) { - currentMeshWithUVCoords = &mesh; - break; - } - } - - if (!currentMeshWithUVCoords) { - meshesWithUVCoords.push_back(MeshInfo(PatchType_UVAndNormals)); - currentMeshWithUVCoords = &meshesWithUVCoords.back(); - currentMeshWithUVCoords->shader = s; - } - out = currentMeshWithUVCoords; - } - // 'pp' - polygon patch primitive - else if ('p' == line[1]) { - currentMeshWithNormals = nullptr; - for (auto &mesh : meshesWithNormals) { - if (mesh.shader == s) { - currentMeshWithNormals = &mesh; - break; - } - } - - if (!currentMeshWithNormals) { - meshesWithNormals.push_back(MeshInfo(PatchType_Normals)); - currentMeshWithNormals = &meshesWithNormals.back(); - currentMeshWithNormals->shader = s; - } - sz = &line[2]; - out = currentMeshWithNormals; - } - // 'p' - polygon primitive - else { - currentMesh = nullptr; - for (auto &mesh : meshes) { - if (mesh.shader == s) { - currentMesh = &mesh; - break; - } - } - - if (!currentMesh) { - meshes.push_back(MeshInfo(PatchType_Simple)); - currentMesh = &meshes.back(); - currentMesh->shader = s; - } - sz = &line[1]; - out = currentMesh; - } - SkipSpaces(sz, &sz); - unsigned int m = strtoul10(sz); - - // ---- flip the face order - out->vertices.resize(out->vertices.size() + m); - if (out != currentMesh) { - out->normals.resize(out->vertices.size()); - } - if (out == currentMeshWithUVCoords) { - out->uvs.resize(out->vertices.size()); - } - for (unsigned int n = 0; n < m; ++n) { - if (!GetNextLine(buffer, line)) { - ASSIMP_LOG_ERROR("NFF: Unexpected EOF was encountered. Patch definition incomplete"); - continue; - } - - aiVector3D v; - sz = &line[0]; - AI_NFF_PARSE_TRIPLE(v); - out->vertices[out->vertices.size() - n - 1] = v; - - if (out != currentMesh) { - AI_NFF_PARSE_TRIPLE(v); - out->normals[out->vertices.size() - n - 1] = v; - } - if (out == currentMeshWithUVCoords) { - // FIX: in one test file this wraps over multiple lines - SkipSpaces(&sz); - if (IsLineEnd(*sz)) { - GetNextLine(buffer, line); - sz = line; - } - AI_NFF_PARSE_FLOAT(v.x); - SkipSpaces(&sz); - if (IsLineEnd(*sz)) { - GetNextLine(buffer, line); - sz = line; - } - AI_NFF_PARSE_FLOAT(v.y); - v.y = 1.f - v.y; - out->uvs[out->vertices.size() - n - 1] = v; - } - } - out->faces.push_back(m); - } - // 'f' - shading information block - else if (TokenMatch(sz, "f", 1)) { - ai_real d; - - // read the RGB colors - AI_NFF_PARSE_TRIPLE(s.color); - - // read the other properties - AI_NFF_PARSE_FLOAT(s.diffuse.r); - AI_NFF_PARSE_FLOAT(s.specular.r); - AI_NFF_PARSE_FLOAT(d); // skip shininess and transmittance - AI_NFF_PARSE_FLOAT(d); - AI_NFF_PARSE_FLOAT(s.refracti); - - // NFF2 uses full colors here so we need to use them too - // although NFF uses simple scaling factors - s.diffuse.g = s.diffuse.b = s.diffuse.r; - s.specular.g = s.specular.b = s.specular.r; - - // if the next one is NOT a number we assume it is a texture file name - // this feature is used by some NFF files on the internet and it has - // been implemented as it can be really useful - SkipSpaces(&sz); - if (!IsNumeric(*sz)) { - // TODO: Support full file names with spaces and quotation marks ... - const char *p = sz; - while (!IsSpaceOrNewLine(*sz)) - ++sz; - - unsigned int diff = (unsigned int)(sz - p); - if (diff) { - s.texFile = std::string(p, diff); - } - } else { - AI_NFF_PARSE_FLOAT(s.ambient); // optional - } - } - // 'shader' - other way to specify a texture - else if (TokenMatch(sz, "shader", 6)) { - SkipSpaces(&sz); - const char *old = sz; - while (!IsSpaceOrNewLine(*sz)) - ++sz; - s.texFile = std::string(old, (uintptr_t)sz - (uintptr_t)old); - } - // 'l' - light source - else if (TokenMatch(sz, "l", 1)) { - lights.push_back(Light()); - Light &light = lights.back(); - - AI_NFF_PARSE_TRIPLE(light.position); - AI_NFF_PARSE_FLOAT(light.intensity); - AI_NFF_PARSE_TRIPLE(light.color); - } - // 's' - sphere - else if (TokenMatch(sz, "s", 1)) { - meshesLocked.push_back(MeshInfo(PatchType_Simple, true)); - MeshInfo &curMesh = meshesLocked.back(); - curMesh.shader = s; - curMesh.shader.mapping = aiTextureMapping_SPHERE; - - AI_NFF_PARSE_SHAPE_INFORMATION(); - - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeSphere(iTesselation, curMesh.vertices); - curMesh.faces.resize(curMesh.vertices.size() / 3, 3); - - // generate a name for the mesh - ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "sphere_%i", sphere++); - } - // 'dod' - dodecahedron - else if (TokenMatch(sz, "dod", 3)) { - meshesLocked.push_back(MeshInfo(PatchType_Simple, true)); - MeshInfo &curMesh = meshesLocked.back(); - curMesh.shader = s; - curMesh.shader.mapping = aiTextureMapping_SPHERE; - - AI_NFF_PARSE_SHAPE_INFORMATION(); - - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeDodecahedron(curMesh.vertices); - curMesh.faces.resize(curMesh.vertices.size() / 3, 3); - - // generate a name for the mesh - ::ai_snprintf(curMesh.name, 128, "dodecahedron_%i", dodecahedron++); - } - - // 'oct' - octahedron - else if (TokenMatch(sz, "oct", 3)) { - meshesLocked.push_back(MeshInfo(PatchType_Simple, true)); - MeshInfo &curMesh = meshesLocked.back(); - curMesh.shader = s; - curMesh.shader.mapping = aiTextureMapping_SPHERE; - - AI_NFF_PARSE_SHAPE_INFORMATION(); - - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeOctahedron(curMesh.vertices); - curMesh.faces.resize(curMesh.vertices.size() / 3, 3); - - // generate a name for the mesh - ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "octahedron_%i", octahedron++); - } - - // 'tet' - tetrahedron - else if (TokenMatch(sz, "tet", 3)) { - meshesLocked.push_back(MeshInfo(PatchType_Simple, true)); - MeshInfo &curMesh = meshesLocked.back(); - curMesh.shader = s; - curMesh.shader.mapping = aiTextureMapping_SPHERE; - - AI_NFF_PARSE_SHAPE_INFORMATION(); - - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeTetrahedron(curMesh.vertices); - curMesh.faces.resize(curMesh.vertices.size() / 3, 3); - - // generate a name for the mesh - ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "tetrahedron_%i", tetrahedron++); - } - - // 'hex' - hexahedron - else if (TokenMatch(sz, "hex", 3)) { - meshesLocked.push_back(MeshInfo(PatchType_Simple, true)); - MeshInfo &curMesh = meshesLocked.back(); - curMesh.shader = s; - curMesh.shader.mapping = aiTextureMapping_BOX; - - AI_NFF_PARSE_SHAPE_INFORMATION(); - - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeHexahedron(curMesh.vertices); - curMesh.faces.resize(curMesh.vertices.size() / 3, 3); - - // generate a name for the mesh - ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "hexahedron_%i", hexahedron++); - } - // 'c' - cone - else if (TokenMatch(sz, "c", 1)) { - meshesLocked.push_back(MeshInfo(PatchType_Simple, true)); - MeshInfo &curMesh = meshesLocked.back(); - curMesh.shader = s; - curMesh.shader.mapping = aiTextureMapping_CYLINDER; - - if (!GetNextLine(buffer, line)) { - ASSIMP_LOG_ERROR("NFF: Unexpected end of file (cone definition not complete)"); - break; - } - sz = line; - - // read the two center points and the respective radii - aiVector3D center1, center2; - ai_real radius1 = 0.f, radius2 = 0.f; - AI_NFF_PARSE_TRIPLE(center1); - AI_NFF_PARSE_FLOAT(radius1); - - if (!GetNextLine(buffer, line)) { - ASSIMP_LOG_ERROR("NFF: Unexpected end of file (cone definition not complete)"); - break; - } - sz = line; - - AI_NFF_PARSE_TRIPLE(center2); - AI_NFF_PARSE_FLOAT(radius2); - - // compute the center point of the cone/cylinder - - // it is its local transformation origin - curMesh.dir = center2 - center1; - curMesh.center = center1 + curMesh.dir / (ai_real)2.0; - - ai_real f; - if ((f = curMesh.dir.Length()) < 10e-3f) { - ASSIMP_LOG_ERROR("NFF: Cone height is close to zero"); - continue; - } - curMesh.dir /= f; // normalize - - // generate the cone - it consists of simple triangles - StandardShapes::MakeCone(f, radius1, radius2, - integer_pow(4, iTesselation), curMesh.vertices); - - // MakeCone() returns tris - curMesh.faces.resize(curMesh.vertices.size() / 3, 3); - - // generate a name for the mesh. 'cone' if it a cone, - // 'cylinder' if it is a cylinder. Funny, isn't it? - if (radius1 != radius2) { - ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "cone_%i", cone++); - } else { - ::ai_snprintf(curMesh.name, MeshInfo::MaxNameLen, "cylinder_%i", cylinder++); - } - } - // 'tess' - tessellation - else if (TokenMatch(sz, "tess", 4)) { - SkipSpaces(&sz); - iTesselation = strtoul10(sz); - } - // 'from' - camera position - else if (TokenMatch(sz, "from", 4)) { - AI_NFF_PARSE_TRIPLE(camPos); - hasCam = true; - } - // 'at' - camera look-at vector - else if (TokenMatch(sz, "at", 2)) { - AI_NFF_PARSE_TRIPLE(camLookAt); - hasCam = true; - } - // 'up' - camera up vector - else if (TokenMatch(sz, "up", 2)) { - AI_NFF_PARSE_TRIPLE(camUp); - hasCam = true; - } - // 'angle' - (half?) camera field of view - else if (TokenMatch(sz, "angle", 5)) { - AI_NFF_PARSE_FLOAT(angle); - hasCam = true; - } - // 'resolution' - used to compute the screen aspect - else if (TokenMatch(sz, "resolution", 10)) { - AI_NFF_PARSE_FLOAT(resolution.x); - AI_NFF_PARSE_FLOAT(resolution.y); - hasCam = true; - } - // 'pb' - bezier patch. Not supported yet - else if (TokenMatch(sz, "pb", 2)) { - ASSIMP_LOG_ERROR("NFF: Encountered unsupported ID: bezier patch"); - } - // 'pn' - NURBS. Not supported yet - else if (TokenMatch(sz, "pn", 2) || TokenMatch(sz, "pnn", 3)) { - ASSIMP_LOG_ERROR("NFF: Encountered unsupported ID: NURBS"); - } - // '' - comment - else if ('#' == line[0]) { - const char *space; - SkipSpaces(&line[1], &space); - if (!IsLineEnd(*space)) { - ASSIMP_LOG_INFO(space); - } - } - } - } - - // copy all arrays into one large - meshes.reserve(meshes.size() + meshesLocked.size() + meshesWithNormals.size() + meshesWithUVCoords.size()); - meshes.insert(meshes.end(), meshesLocked.begin(), meshesLocked.end()); - meshes.insert(meshes.end(), meshesWithNormals.begin(), meshesWithNormals.end()); - meshes.insert(meshes.end(), meshesWithUVCoords.begin(), meshesWithUVCoords.end()); - - // now generate output meshes. first find out how many meshes we'll need - std::vector<MeshInfo>::const_iterator it = meshes.begin(), end = meshes.end(); - for (; it != end; ++it) { - if (!(*it).faces.empty()) { - ++pScene->mNumMeshes; - if ((*it).name[0]) ++numNamed; - } - } - - // generate a dummy root node - assign all unnamed elements such - // as polygons and polygon patches to the root node and generate - // sub nodes for named objects such as spheres and cones. - aiNode *const root = new aiNode(); - root->mName.Set("<NFF_Root>"); - root->mNumChildren = numNamed + (hasCam ? 1 : 0) + (unsigned int)lights.size(); - root->mNumMeshes = pScene->mNumMeshes - numNamed; - - aiNode **ppcChildren = nullptr; - unsigned int *pMeshes = nullptr; - if (root->mNumMeshes) - pMeshes = root->mMeshes = new unsigned int[root->mNumMeshes]; - if (root->mNumChildren) - ppcChildren = root->mChildren = new aiNode *[root->mNumChildren]; - - // generate the camera - if (hasCam) { - ai_assert(ppcChildren); - aiNode *nd = new aiNode(); - *ppcChildren = nd; - nd->mName.Set("<NFF_Camera>"); - nd->mParent = root; - - // allocate the camera in the scene - pScene->mNumCameras = 1; - pScene->mCameras = new aiCamera *[1]; - aiCamera *c = pScene->mCameras[0] = new aiCamera; - - c->mName = nd->mName; // make sure the names are identical - c->mHorizontalFOV = AI_DEG_TO_RAD(angle); - c->mLookAt = camLookAt - camPos; - c->mPosition = camPos; - c->mUp = camUp; - - // If the resolution is not specified in the file, we - // need to set 1.0 as aspect. - c->mAspect = (!resolution.y ? 0.f : resolution.x / resolution.y); - ++ppcChildren; - } - - // generate light sources - if (!lights.empty()) { - ai_assert(ppcChildren); - pScene->mNumLights = (unsigned int)lights.size(); - pScene->mLights = new aiLight *[pScene->mNumLights]; - for (unsigned int i = 0; i < pScene->mNumLights; ++i, ++ppcChildren) { - const Light &l = lights[i]; - - aiNode *nd = new aiNode(); - *ppcChildren = nd; - nd->mParent = root; - - nd->mName.length = ::ai_snprintf(nd->mName.data, 1024, "<NFF_Light%u>", i); - - // allocate the light in the scene data structure - aiLight *out = pScene->mLights[i] = new aiLight(); - out->mName = nd->mName; // make sure the names are identical - out->mType = aiLightSource_POINT; - out->mColorDiffuse = out->mColorSpecular = l.color * l.intensity; - out->mPosition = l.position; - } - } - - if (!pScene->mNumMeshes) throw DeadlyImportError("NFF: No meshes loaded"); - pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; - pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials = pScene->mNumMeshes]; - unsigned int m = 0; - for (it = meshes.begin(); it != end; ++it) { - if ((*it).faces.empty()) continue; - - const MeshInfo &src = *it; - aiMesh *const mesh = pScene->mMeshes[m] = new aiMesh(); - mesh->mNumVertices = (unsigned int)src.vertices.size(); - mesh->mNumFaces = (unsigned int)src.faces.size(); - - // Generate sub nodes for named meshes - if (src.name[0] && nullptr != ppcChildren) { - aiNode *const node = *ppcChildren = new aiNode(); - node->mParent = root; - node->mNumMeshes = 1; - node->mMeshes = new unsigned int[1]; - node->mMeshes[0] = m; - node->mName.Set(src.name); - - // setup the transformation matrix of the node - aiMatrix4x4::FromToMatrix(aiVector3D(0.f, 1.f, 0.f), - src.dir, node->mTransformation); - - aiMatrix4x4 &mat = node->mTransformation; - mat.a1 *= src.radius.x; - mat.b1 *= src.radius.x; - mat.c1 *= src.radius.x; - mat.a2 *= src.radius.y; - mat.b2 *= src.radius.y; - mat.c2 *= src.radius.y; - mat.a3 *= src.radius.z; - mat.b3 *= src.radius.z; - mat.c3 *= src.radius.z; - mat.a4 = src.center.x; - mat.b4 = src.center.y; - mat.c4 = src.center.z; - - ++ppcChildren; - } else { - *pMeshes++ = m; - } - - // copy vertex positions - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - ::memcpy(mesh->mVertices, &src.vertices[0], - sizeof(aiVector3D) * mesh->mNumVertices); - - // NFF2: there could be vertex colors - if (!src.colors.empty()) { - ai_assert(src.colors.size() == src.vertices.size()); - - // copy vertex colors - mesh->mColors[0] = new aiColor4D[mesh->mNumVertices]; - ::memcpy(mesh->mColors[0], &src.colors[0], - sizeof(aiColor4D) * mesh->mNumVertices); - } - - if (!src.normals.empty()) { - ai_assert(src.normals.size() == src.vertices.size()); - - // copy normal vectors - mesh->mNormals = new aiVector3D[mesh->mNumVertices]; - ::memcpy(mesh->mNormals, &src.normals[0], - sizeof(aiVector3D) * mesh->mNumVertices); - } - - if (!src.uvs.empty()) { - ai_assert(src.uvs.size() == src.vertices.size()); - - // copy texture coordinates - mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; - ::memcpy(mesh->mTextureCoords[0], &src.uvs[0], - sizeof(aiVector3D) * mesh->mNumVertices); - } - - // generate faces - unsigned int p = 0; - aiFace *pFace = mesh->mFaces = new aiFace[mesh->mNumFaces]; - for (std::vector<unsigned int>::const_iterator it2 = src.faces.begin(), - end2 = src.faces.end(); - it2 != end2; ++it2, ++pFace) { - pFace->mIndices = new unsigned int[pFace->mNumIndices = *it2]; - for (unsigned int o = 0; o < pFace->mNumIndices; ++o) - pFace->mIndices[o] = p++; - } - - // generate a material for the mesh - aiMaterial *pcMat = (aiMaterial *)(pScene->mMaterials[m] = new aiMaterial()); - - mesh->mMaterialIndex = m++; - - aiString matName; - matName.Set(AI_DEFAULT_MATERIAL_NAME); - pcMat->AddProperty(&matName, AI_MATKEY_NAME); - - // FIX: Ignore diffuse == 0 - aiColor3D c = src.shader.color * (src.shader.diffuse.r ? src.shader.diffuse : aiColor3D(1.f, 1.f, 1.f)); - pcMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); - c = src.shader.color * src.shader.specular; - pcMat->AddProperty(&c, 1, AI_MATKEY_COLOR_SPECULAR); - - // NFF2 - default values for NFF - pcMat->AddProperty(&src.shader.ambient, 1, AI_MATKEY_COLOR_AMBIENT); - pcMat->AddProperty(&src.shader.emissive, 1, AI_MATKEY_COLOR_EMISSIVE); - pcMat->AddProperty(&src.shader.opacity, 1, AI_MATKEY_OPACITY); - - // setup the first texture layer, if existing - if (src.shader.texFile.length()) { - matName.Set(src.shader.texFile); - pcMat->AddProperty(&matName, AI_MATKEY_TEXTURE_DIFFUSE(0)); - - if (aiTextureMapping_UV != src.shader.mapping) { - - aiVector3D v(0.f, -1.f, 0.f); - pcMat->AddProperty(&v, 1, AI_MATKEY_TEXMAP_AXIS_DIFFUSE(0)); - pcMat->AddProperty((int *)&src.shader.mapping, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); - } - } - - // setup the name of the material - if (src.shader.name.length()) { - matName.Set(src.shader.texFile); - pcMat->AddProperty(&matName, AI_MATKEY_NAME); - } - - // setup some more material properties that are specific to NFF2 - int i; - if (src.shader.twoSided) { - i = 1; - pcMat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED); - } - i = (src.shader.shaded ? aiShadingMode_Gouraud : aiShadingMode_NoShading); - if (src.shader.shininess) { - i = aiShadingMode_Phong; - pcMat->AddProperty(&src.shader.shininess, 1, AI_MATKEY_SHININESS); - } - pcMat->AddProperty(&i, 1, AI_MATKEY_SHADING_MODEL); - } - pScene->mRootNode = root; -} - -#endif // !! ASSIMP_BUILD_NO_NFF_IMPORTER diff --git a/src/mesh/assimp-master/code/AssetLib/NFF/NFFLoader.h b/src/mesh/assimp-master/code/AssetLib/NFF/NFFLoader.h deleted file mode 100644 index 0fa615b..0000000 --- a/src/mesh/assimp-master/code/AssetLib/NFF/NFFLoader.h +++ /dev/null @@ -1,191 +0,0 @@ -/* -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 NFFLoader.h - * @brief Declaration of the NFF importer class. - */ -#pragma once -#ifndef AI_NFFLOADER_H_INCLUDED -#define AI_NFFLOADER_H_INCLUDED - -#include <assimp/BaseImporter.h> -#include <assimp/material.h> -#include <assimp/types.h> -#include <vector> - -namespace Assimp { - -// ---------------------------------------------------------------------------------- -/** NFF (Neutral File Format) Importer class. - * - * The class implements both Eric Haynes NFF format and Sense8's NFF (NFF2) format. - * Both are quite different and the loading code is somewhat dirty at - * the moment. Sense8 should be moved to a separate loader. -*/ -class NFFImporter : public BaseImporter { -public: - NFFImporter(); - ~NFFImporter() 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; - -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; - -private: - // describes face material properties - struct ShadingInfo { - ShadingInfo() : - color(0.6f, 0.6f, 0.6f), - diffuse(1.f, 1.f, 1.f), - specular(1.f, 1.f, 1.f), - ambient(0.f, 0.f, 0.f), - emissive(0.f, 0.f, 0.f), - refracti(1.f), - twoSided(false), // for NFF2 - shaded(true), // for NFF2 - opacity(1.f), - shininess(0.f), - mapping(aiTextureMapping_UV) { - // empty - } - - aiColor3D color, diffuse, specular, ambient, emissive; - ai_real refracti; - std::string texFile; - bool twoSided; // For NFF2 - bool shaded; - ai_real opacity, shininess; - std::string name; - - // texture mapping to be generated for the mesh - uv is the default - // it means: use UV if there, nothing otherwise. This property is - // used for locked meshes. - aiTextureMapping mapping; - - // shininess is ignored for the moment - bool operator == (const ShadingInfo &other) const { - return color == other.color && - diffuse == other.diffuse && - specular == other.specular && - ambient == other.ambient && - refracti == other.refracti && - texFile == other.texFile && - twoSided == other.twoSided && - shaded == other.shaded; - - // Some properties from NFF2 aren't compared by this operator. - // Comparing MeshInfo::matIndex should do that. - } - }; - - // describes a NFF light source - struct Light { - Light() : - intensity(1.f), color(1.f, 1.f, 1.f) {} - - aiVector3D position; - ai_real intensity; - aiColor3D color; - }; - - enum PatchType { - PatchType_Simple = 0x0, - PatchType_Normals = 0x1, - PatchType_UVAndNormals = 0x2 - }; - - // describes a NFF mesh - struct MeshInfo { - MeshInfo(PatchType _pType, bool bL = false) : - pType(_pType), bLocked(bL), radius(1.f, 1.f, 1.f), dir(0.f, 1.f, 0.f), matIndex(0) { - name[0] = '\0'; // by default meshes are unnamed - } - - ShadingInfo shader; - PatchType pType; - bool bLocked; - - // for spheres, cones and cylinders: center point of the object - aiVector3D center, radius, dir; - static const size_t MaxNameLen = 128; - char name[MaxNameLen]; - - std::vector<aiVector3D> vertices, normals, uvs; - std::vector<unsigned int> faces; - - // for NFF2 - std::vector<aiColor4D> colors; - unsigned int matIndex; - }; - - // ------------------------------------------------------------------- - /** Loads the material table for the NFF2 file format from an - * external file. - * - * @param output Receives the list of output meshes - * @param path Path to the file (abs. or rel.) - * @param pIOHandler IOSystem to be used to open the file - */ - void LoadNFF2MaterialTable(std::vector<ShadingInfo> &output, - const std::string &path, IOSystem *pIOHandler); -}; - -} // end of namespace Assimp - -#endif // AI_NFFIMPORTER_H_IN |