diff options
Diffstat (limited to 'src/3rdparty/assimp/code/NFFLoader.cpp')
-rw-r--r-- | src/3rdparty/assimp/code/NFFLoader.cpp | 2351 |
1 files changed, 1177 insertions, 1174 deletions
diff --git a/src/3rdparty/assimp/code/NFFLoader.cpp b/src/3rdparty/assimp/code/NFFLoader.cpp index b81698a56..8bec63ea3 100644 --- a/src/3rdparty/assimp/code/NFFLoader.cpp +++ b/src/3rdparty/assimp/code/NFFLoader.cpp @@ -3,12 +3,12 @@ Open Asset Import Library (assimp) --------------------------------------------------------------------------- -Copyright (c) 2006-2012, assimp team +Copyright (c) 2006-2016, 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 +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 @@ -25,45 +25,51 @@ conditions are met: 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 +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 +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 +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 +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 */ -#include "AssimpPCH.h" + #ifndef ASSIMP_BUILD_NO_NFF_IMPORTER // internal headers #include "NFFLoader.h" #include "ParsingUtils.h" #include "StandardShapes.h" +#include "qnan.h" #include "fast_atof.h" #include "RemoveComments.h" +#include <assimp/IOSystem.hpp> +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> +#include <memory> + using namespace Assimp; static const aiImporterDesc desc = { - "Neutral File Format Importer", - "", - "", - "", - aiImporterFlags_SupportBinaryFlavour, - 0, - 0, - 0, - 0, - "enff nff" + "Neutral File Format Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "enff nff" }; // ------------------------------------------------------------------------------------------------ @@ -72,1197 +78,1194 @@ NFFImporter::NFFImporter() {} // ------------------------------------------------------------------------------------------------ -// Destructor, private as well +// Destructor, private as well NFFImporter::~NFFImporter() {} // ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. +// 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"); + return SimpleExtensionCheck(pFile,"nff","enff"); } // ------------------------------------------------------------------------------------------------ // Get the list of all supported file extensions const aiImporterDesc* NFFImporter::GetInfo () const { - return &desc; + return &desc; } // ------------------------------------------------------------------------------------------------ #define AI_NFF_PARSE_FLOAT(f) \ - SkipSpaces(&sz); \ - if (!::IsLineEnd(*sz))sz = fast_atoreal_move<float>(sz, (float&)f); + SkipSpaces(&sz); \ + if (!::IsLineEnd(*sz))sz = fast_atoreal_move<float>(sz, (float&)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]) + 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; \ - currentMesh.radius = radius; \ - currentMesh.center = center; + 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; \ + currentMesh.radius = radius; \ + currentMesh.center = center; // ------------------------------------------------------------------------------------------------ #define AI_NFF2_GET_NEXT_TOKEN() \ - do \ - { \ - if (!GetNextLine(buffer,line)) \ - {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read next token");break;} \ - SkipSpaces(line,&sz); \ - } \ - while(IsLineEnd(*sz)) + do \ + { \ + if (!GetNextLine(buffer,line)) \ + {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read next token");break;} \ + SkipSpaces(line,&sz); \ + } \ + while(IsLineEnd(*sz)) // ------------------------------------------------------------------------------------------------ // Loads the materail table for the NFF2 file format from an external file void NFFImporter::LoadNFF2MaterialTable(std::vector<ShadingInfo>& output, - const std::string& path, IOSystem* pIOHandler) + const std::string& path, IOSystem* pIOHandler) { - boost::scoped_ptr<IOStream> file( pIOHandler->Open( path, "rb")); - - // Check whether we can read from the file - if( !file.get()) { - DefaultLogger::get()->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)) { - DefaultLogger::get()->error("NFF2: Not a valid material library " + path + "."); - return; - } - - ShadingInfo* curShader = NULL; - - // 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)) - { - DefaultLogger::get()->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) - { - DefaultLogger::get()->error(std::string("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); - } - } - } + std::unique_ptr<IOStream> file( pIOHandler->Open( path, "rb")); + + // Check whether we can read from the file + if( !file.get()) { + DefaultLogger::get()->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)) { + DefaultLogger::get()->error("NFF2: Not a valid material library " + path + "."); + return; + } + + ShadingInfo* curShader = NULL; + + // 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)) + { + DefaultLogger::get()->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) + { + DefaultLogger::get()->error(std::string("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) +// Imports the given file into the given scene structure. +void NFFImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) { - boost::scoped_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 + "."); - - 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; - 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); - float angle = 45.f; - aiVector2D resolution; - - bool hasCam = false; - - MeshInfo* currentMeshWithNormals = NULL; - MeshInfo* currentMesh = NULL; - MeshInfo* currentMeshWithUVCoords = NULL; - - ShadingInfo s; // current material info - - // degree of tesselation - 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 float 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)) - { - DefaultLogger::get()->info("NFF (Sense8) file format: " + std::string(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)DefaultLogger::get()->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 s; - if ((std::string::npos == (s = path.find_last_of('\\')) || !s) && - (std::string::npos == (s = path.find_last_of('/')) || !s) ) - { - s = pFile.find_last_of('\\'); - if (std::string::npos == s)s = pFile.find_last_of('/'); - if (std::string::npos != s) - { - path = pFile.substr(0,s+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); - m = ::strtoul10(sz,&sz); - if (m >= (unsigned int)tempPositions.size()) - { - DefaultLogger::get()->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 + 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 + "."); + + 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; + 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); + float angle = 45.f; + aiVector2D resolution; + + bool hasCam = false; + + MeshInfo* currentMeshWithNormals = NULL; + MeshInfo* currentMesh = NULL; + MeshInfo* currentMeshWithUVCoords = NULL; + + ShadingInfo s; // current material info + + // degree of tesselation + 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 float 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)) + { + DefaultLogger::get()->info("NFF (Sense8) file format: " + std::string(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)DefaultLogger::get()->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 s; + if ((std::string::npos == (s = path.find_last_of('\\')) || !s) && + (std::string::npos == (s = path.find_last_of('/')) || !s) ) + { + s = pFile.find_last_of('\\'); + if (std::string::npos == s)s = pFile.find_last_of('/'); + if (std::string::npos != s) + { + path = pFile.substr(0,s+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); + m = ::strtoul10(sz,&sz); + if (m >= (unsigned int)tempPositions.size()) + { + DefaultLogger::get()->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)) - { - } + // 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': - - DefaultLogger::get()->warn("Unsupported NFF2 texture attribute: trans"); - }; - if (!sz[1] || '_' != sz[2]) - { - DefaultLogger::get()->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()) - { - DefaultLogger::get()->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 = NULL; - 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) - { - 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 = NULL; - - // 'tpp' - texture polygon patch primitive - if ('t' == line[0]) - { - currentMeshWithUVCoords = NULL; - for (std::vector<MeshInfo>::iterator it = meshesWithUVCoords.begin(), end = meshesWithUVCoords.end(); - it != end;++it) - { - if ((*it).shader == s) - { - currentMeshWithUVCoords = &(*it); - 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 = NULL; - for (std::vector<MeshInfo>::iterator it = meshesWithNormals.begin(), end = meshesWithNormals.end(); - it != end;++it) - { - if ((*it).shader == s) - { - currentMeshWithNormals = &(*it); - 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 = NULL; - for (std::vector<MeshInfo>::iterator it = meshes.begin(), end = meshes.end(); - it != end;++it) - { - if ((*it).shader == s) - { - currentMesh = &(*it); - break; - } - } - - if (!currentMesh) - { - meshes.push_back(MeshInfo(PatchType_Simple)); - currentMesh = &meshes.back(); - currentMesh->shader = s; - } - sz = &line[1];out = currentMesh; - } - SkipSpaces(sz,&sz); - 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)) - { - DefaultLogger::get()->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)) - { - float 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& currentMesh = meshesLocked.back(); - currentMesh.shader = s; - currentMesh.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, currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - - // generate a name for the mesh - ::sprintf(currentMesh.name,"sphere_%i",sphere++); - } - // 'dod' - dodecahedron - else if (TokenMatch(sz,"dod",3)) - { - meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; - currentMesh.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(currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - - // generate a name for the mesh - ::sprintf(currentMesh.name,"dodecahedron_%i",dodecahedron++); - } - - // 'oct' - octahedron - else if (TokenMatch(sz,"oct",3)) - { - meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; - currentMesh.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(currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - - // generate a name for the mesh - ::sprintf(currentMesh.name,"octahedron_%i",octahedron++); - } - - // 'tet' - tetrahedron - else if (TokenMatch(sz,"tet",3)) - { - meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; - currentMesh.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(currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - - // generate a name for the mesh - ::sprintf(currentMesh.name,"tetrahedron_%i",tetrahedron++); - } - - // 'hex' - hexahedron - else if (TokenMatch(sz,"hex",3)) - { - meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; - currentMesh.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(currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - - // generate a name for the mesh - ::sprintf(currentMesh.name,"hexahedron_%i",hexahedron++); - } - // 'c' - cone - else if (TokenMatch(sz,"c",1)) - { - meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; - currentMesh.shader.mapping = aiTextureMapping_CYLINDER; - - if(!GetNextLine(buffer,line)) - { - DefaultLogger::get()->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; float radius1, radius2; - AI_NFF_PARSE_TRIPLE(center1); - AI_NFF_PARSE_FLOAT(radius1); - - if(!GetNextLine(buffer,line)) - { - DefaultLogger::get()->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 - currentMesh.dir = center2-center1; - currentMesh.center = center1+currentMesh.dir/2.f; - - float f; - if (( f = currentMesh.dir.Length()) < 10e-3f ) - { - DefaultLogger::get()->error("NFF: Cone height is close to zero"); - continue; - } - currentMesh.dir /= f; // normalize - - // generate the cone - it consists of simple triangles - StandardShapes::MakeCone(f, radius1, radius2, - integer_pow(4, iTesselation), currentMesh.vertices); - - // MakeCone() returns tris - currentMesh.faces.resize(currentMesh.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) - ::sprintf(currentMesh.name,"cone_%i",cone++); - else ::sprintf(currentMesh.name,"cylinder_%i",cylinder++); - } - // 'tess' - tesselation - 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)) - { - DefaultLogger::get()->error("NFF: Encountered unsupported ID: bezier patch"); - } - // 'pn' - NURBS. Not supported yet - else if (TokenMatch(sz,"pn",2) || TokenMatch(sz,"pnn",3)) - { - DefaultLogger::get()->error("NFF: Encountered unsupported ID: NURBS"); - } - // '' - comment - else if ('#' == line[0]) - { - const char* sz;SkipSpaces(&line[1],&sz); - if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz); - } - } - } - - // 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 = NULL; - unsigned int* pMeshes = NULL; - 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) - { - aiNode* nd = *ppcChildren = new aiNode(); - 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()) - { - 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 = *ppcChildren = new aiNode(); - nd->mParent = root; - - nd->mName.length = ::sprintf(nd->mName.data,"<NFF_Light%i>",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]; - for (it = meshes.begin(), m = 0; 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]) - { - 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 s; - s.Set(AI_DEFAULT_MATERIAL_NAME); - pcMat->AddProperty(&s, 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()) - { - s.Set(src.shader.texFile); - pcMat->AddProperty(&s,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()) - { - s.Set(src.shader.texFile); - pcMat->AddProperty(&s,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; + // 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': + + DefaultLogger::get()->warn("Unsupported NFF2 texture attribute: trans"); + }; + if (!sz[1] || '_' != sz[2]) + { + DefaultLogger::get()->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()) + { + DefaultLogger::get()->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 = NULL; + 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) + { + 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 = NULL; + + // 'tpp' - texture polygon patch primitive + if ('t' == line[0]) + { + currentMeshWithUVCoords = NULL; + 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 = NULL; + 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 = NULL; + 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); + 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)) + { + DefaultLogger::get()->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)) + { + float 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& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + currentMesh.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, currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + + // generate a name for the mesh + ::ai_snprintf(currentMesh.name,128,"sphere_%i",sphere++); + } + // 'dod' - dodecahedron + else if (TokenMatch(sz,"dod",3)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + currentMesh.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(currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + + // generate a name for the mesh + ::ai_snprintf(currentMesh.name,128,"dodecahedron_%i",dodecahedron++); + } + + // 'oct' - octahedron + else if (TokenMatch(sz,"oct",3)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + currentMesh.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(currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + + // generate a name for the mesh + ::ai_snprintf(currentMesh.name,128,"octahedron_%i",octahedron++); + } + + // 'tet' - tetrahedron + else if (TokenMatch(sz,"tet",3)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + currentMesh.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(currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + + // generate a name for the mesh + ::ai_snprintf(currentMesh.name,128,"tetrahedron_%i",tetrahedron++); + } + + // 'hex' - hexahedron + else if (TokenMatch(sz,"hex",3)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + currentMesh.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(currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + + // generate a name for the mesh + ::ai_snprintf(currentMesh.name,128,"hexahedron_%i",hexahedron++); + } + // 'c' - cone + else if (TokenMatch(sz,"c",1)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + currentMesh.shader.mapping = aiTextureMapping_CYLINDER; + + if(!GetNextLine(buffer,line)) + { + DefaultLogger::get()->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; float radius1, radius2; + AI_NFF_PARSE_TRIPLE(center1); + AI_NFF_PARSE_FLOAT(radius1); + + if(!GetNextLine(buffer,line)) + { + DefaultLogger::get()->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 + currentMesh.dir = center2-center1; + currentMesh.center = center1+currentMesh.dir/2.f; + + float f; + if (( f = currentMesh.dir.Length()) < 10e-3f ) + { + DefaultLogger::get()->error("NFF: Cone height is close to zero"); + continue; + } + currentMesh.dir /= f; // normalize + + // generate the cone - it consists of simple triangles + StandardShapes::MakeCone(f, radius1, radius2, + integer_pow(4, iTesselation), currentMesh.vertices); + + // MakeCone() returns tris + currentMesh.faces.resize(currentMesh.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(currentMesh.name,128,"cone_%i",cone++); + else ::ai_snprintf(currentMesh.name,128,"cylinder_%i",cylinder++); + } + // 'tess' - tesselation + 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)) + { + DefaultLogger::get()->error("NFF: Encountered unsupported ID: bezier patch"); + } + // 'pn' - NURBS. Not supported yet + else if (TokenMatch(sz,"pn",2) || TokenMatch(sz,"pnn",3)) + { + DefaultLogger::get()->error("NFF: Encountered unsupported ID: NURBS"); + } + // '' - comment + else if ('#' == line[0]) + { + const char* sz;SkipSpaces(&line[1],&sz); + if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz); + } + } + } + + // 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 = NULL; + unsigned int* pMeshes = NULL; + 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) + { + aiNode* nd = *ppcChildren = new aiNode(); + 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()) + { + 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 = *ppcChildren = new aiNode(); + 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]; + for (it = meshes.begin(), m = 0; 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 ] && NULL != 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 s; + s.Set(AI_DEFAULT_MATERIAL_NAME); + pcMat->AddProperty(&s, 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()) + { + s.Set(src.shader.texFile); + pcMat->AddProperty(&s,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()) + { + s.Set(src.shader.texFile); + pcMat->AddProperty(&s,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 |