nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file Implementation of the STL importer class */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #ifndef ASSIMP_BUILD_NO_NFF_IMPORTER nuclear@0: nuclear@0: // internal headers nuclear@0: #include "NFFLoader.h" nuclear@0: #include "ParsingUtils.h" nuclear@0: #include "StandardShapes.h" nuclear@0: #include "fast_atof.h" nuclear@0: #include "RemoveComments.h" nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: static const aiImporterDesc desc = { nuclear@0: "Neutral File Format Importer", nuclear@0: "", nuclear@0: "", nuclear@0: "", nuclear@0: aiImporterFlags_SupportBinaryFlavour, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: 0, nuclear@0: "enff nff" nuclear@0: }; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: NFFImporter::NFFImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: NFFImporter::~NFFImporter() nuclear@0: {} nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the class can handle the format of the given file. nuclear@0: bool NFFImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const nuclear@0: { nuclear@0: return SimpleExtensionCheck(pFile,"nff","enff"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Get the list of all supported file extensions nuclear@0: const aiImporterDesc* NFFImporter::GetInfo () const nuclear@0: { nuclear@0: return &desc; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: #define AI_NFF_PARSE_FLOAT(f) \ nuclear@0: SkipSpaces(&sz); \ nuclear@0: if (!::IsLineEnd(*sz))sz = fast_atoreal_move(sz, (float&)f); nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: #define AI_NFF_PARSE_TRIPLE(v) \ nuclear@0: AI_NFF_PARSE_FLOAT(v[0]) \ nuclear@0: AI_NFF_PARSE_FLOAT(v[1]) \ nuclear@0: AI_NFF_PARSE_FLOAT(v[2]) nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: #define AI_NFF_PARSE_SHAPE_INFORMATION() \ nuclear@0: aiVector3D center, radius(1.0f,get_qnan(),get_qnan()); \ nuclear@0: AI_NFF_PARSE_TRIPLE(center); \ nuclear@0: AI_NFF_PARSE_TRIPLE(radius); \ nuclear@0: if (is_qnan(radius.z))radius.z = radius.x; \ nuclear@0: if (is_qnan(radius.y))radius.y = radius.x; \ nuclear@0: currentMesh.radius = radius; \ nuclear@0: currentMesh.center = center; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: #define AI_NFF2_GET_NEXT_TOKEN() \ nuclear@0: do \ nuclear@0: { \ nuclear@0: if (!GetNextLine(buffer,line)) \ nuclear@0: {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read next token");break;} \ nuclear@0: SkipSpaces(line,&sz); \ nuclear@0: } \ nuclear@0: while(IsLineEnd(*sz)) nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Loads the materail table for the NFF2 file format from an external file nuclear@0: void NFFImporter::LoadNFF2MaterialTable(std::vector& output, nuclear@0: const std::string& path, IOSystem* pIOHandler) nuclear@0: { nuclear@0: boost::scoped_ptr file( pIOHandler->Open( path, "rb")); nuclear@0: nuclear@0: // Check whether we can read from the file nuclear@0: if( !file.get()) { nuclear@0: DefaultLogger::get()->error("NFF2: Unable to open material library " + path + "."); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: // get the size of the file nuclear@0: const unsigned int m = (unsigned int)file->FileSize(); nuclear@0: nuclear@0: // allocate storage and copy the contents of the file to a memory buffer nuclear@0: // (terminate it with zero) nuclear@0: std::vector mBuffer2(m+1); nuclear@0: TextFileToBuffer(file.get(),mBuffer2); nuclear@0: const char* buffer = &mBuffer2[0]; nuclear@0: nuclear@0: // First of all: remove all comments from the file nuclear@0: CommentRemover::RemoveLineComments("//",&mBuffer2[0]); nuclear@0: nuclear@0: // The file should start with the magic sequence "mat" nuclear@0: if (!TokenMatch(buffer,"mat",3)) { nuclear@0: DefaultLogger::get()->error("NFF2: Not a valid material library " + path + "."); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: ShadingInfo* curShader = NULL; nuclear@0: nuclear@0: // No read the file line per line nuclear@0: char line[4096]; nuclear@0: const char* sz; nuclear@0: while (GetNextLine(buffer,line)) nuclear@0: { nuclear@0: SkipSpaces(line,&sz); nuclear@0: nuclear@0: // 'version' defines the version of the file format nuclear@0: if (TokenMatch(sz,"version",7)) nuclear@0: { nuclear@0: DefaultLogger::get()->info("NFF (Sense8) material library file format: " + std::string(sz)); nuclear@0: } nuclear@0: // 'matdef' starts a new material in the file nuclear@0: else if (TokenMatch(sz,"matdef",6)) nuclear@0: { nuclear@0: // add a new material to the list nuclear@0: output.push_back( ShadingInfo() ); nuclear@0: curShader = & output.back(); nuclear@0: nuclear@0: // parse the name of the material nuclear@0: } nuclear@0: else if (!TokenMatch(sz,"valid",5)) nuclear@0: { nuclear@0: // check whether we have an active material at the moment nuclear@0: if (!IsLineEnd(*sz)) nuclear@0: { nuclear@0: if (!curShader) nuclear@0: { nuclear@0: DefaultLogger::get()->error(std::string("NFF2 material library: Found element ") + nuclear@0: sz + "but there is no active material"); nuclear@0: continue; nuclear@0: } nuclear@0: } nuclear@0: else continue; nuclear@0: nuclear@0: // now read the material property and determine its type nuclear@0: aiColor3D c; nuclear@0: if (TokenMatch(sz,"ambient",7)) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(c); nuclear@0: curShader->ambient = c; nuclear@0: } nuclear@0: else if (TokenMatch(sz,"diffuse",7) || TokenMatch(sz,"ambientdiffuse",14) /* correct? */) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(c); nuclear@0: curShader->diffuse = curShader->ambient = c; nuclear@0: } nuclear@0: else if (TokenMatch(sz,"specular",8)) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(c); nuclear@0: curShader->specular = c; nuclear@0: } nuclear@0: else if (TokenMatch(sz,"emission",8)) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(c); nuclear@0: curShader->emissive = c; nuclear@0: } nuclear@0: else if (TokenMatch(sz,"shininess",9)) nuclear@0: { nuclear@0: AI_NFF_PARSE_FLOAT(curShader->shininess); nuclear@0: } nuclear@0: else if (TokenMatch(sz,"opacity",7)) nuclear@0: { nuclear@0: AI_NFF_PARSE_FLOAT(curShader->opacity); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Imports the given file into the given scene structure. nuclear@0: void NFFImporter::InternReadFile( const std::string& pFile, nuclear@0: aiScene* pScene, IOSystem* pIOHandler) nuclear@0: { nuclear@0: boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); nuclear@0: nuclear@0: // Check whether we can read from the file nuclear@0: if( !file.get()) nuclear@0: throw DeadlyImportError( "Failed to open NFF file " + pFile + "."); nuclear@0: nuclear@0: unsigned int m = (unsigned int)file->FileSize(); nuclear@0: nuclear@0: // allocate storage and copy the contents of the file to a memory buffer nuclear@0: // (terminate it with zero) nuclear@0: std::vector mBuffer2; nuclear@0: TextFileToBuffer(file.get(),mBuffer2); nuclear@0: const char* buffer = &mBuffer2[0]; nuclear@0: nuclear@0: // mesh arrays - separate here to make the handling of the pointers below easier. nuclear@0: std::vector meshes; nuclear@0: std::vector meshesWithNormals; nuclear@0: std::vector meshesWithUVCoords; nuclear@0: std::vector meshesLocked; nuclear@0: nuclear@0: char line[4096]; nuclear@0: const char* sz; nuclear@0: nuclear@0: // camera parameters nuclear@0: aiVector3D camPos, camUp(0.f,1.f,0.f), camLookAt(0.f,0.f,1.f); nuclear@0: float angle = 45.f; nuclear@0: aiVector2D resolution; nuclear@0: nuclear@0: bool hasCam = false; nuclear@0: nuclear@0: MeshInfo* currentMeshWithNormals = NULL; nuclear@0: MeshInfo* currentMesh = NULL; nuclear@0: MeshInfo* currentMeshWithUVCoords = NULL; nuclear@0: nuclear@0: ShadingInfo s; // current material info nuclear@0: nuclear@0: // degree of tesselation nuclear@0: unsigned int iTesselation = 4; nuclear@0: nuclear@0: // some temporary variables we need to parse the file nuclear@0: unsigned int sphere = 0, nuclear@0: cylinder = 0, nuclear@0: cone = 0, nuclear@0: numNamed = 0, nuclear@0: dodecahedron = 0, nuclear@0: octahedron = 0, nuclear@0: tetrahedron = 0, nuclear@0: hexahedron = 0; nuclear@0: nuclear@0: // lights imported from the file nuclear@0: std::vector lights; nuclear@0: nuclear@0: // check whether this is the NFF2 file format nuclear@0: if (TokenMatch(buffer,"nff",3)) nuclear@0: { nuclear@0: const float qnan = get_qnan(); nuclear@0: const aiColor4D cQNAN = aiColor4D (qnan,0.f,0.f,1.f); nuclear@0: const aiVector3D vQNAN = aiVector3D(qnan,0.f,0.f); nuclear@0: nuclear@0: // another NFF file format ... just a raw parser has been implemented nuclear@0: // no support for further details, I don't think it is worth the effort nuclear@0: // http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/nff/nff2.html nuclear@0: // http://www.netghost.narod.ru/gff/graphics/summary/sense8.htm nuclear@0: nuclear@0: // First of all: remove all comments from the file nuclear@0: CommentRemover::RemoveLineComments("//",&mBuffer2[0]); nuclear@0: nuclear@0: while (GetNextLine(buffer,line)) nuclear@0: { nuclear@0: SkipSpaces(line,&sz); nuclear@0: if (TokenMatch(sz,"version",7)) nuclear@0: { nuclear@0: DefaultLogger::get()->info("NFF (Sense8) file format: " + std::string(sz)); nuclear@0: } nuclear@0: else if (TokenMatch(sz,"viewpos",7)) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(camPos); nuclear@0: hasCam = true; nuclear@0: } nuclear@0: else if (TokenMatch(sz,"viewdir",7)) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(camLookAt); nuclear@0: hasCam = true; nuclear@0: } nuclear@0: // This starts a new object section nuclear@0: else if (!IsSpaceOrNewLine(*sz)) nuclear@0: { nuclear@0: unsigned int subMeshIdx = 0; nuclear@0: nuclear@0: // read the name of the object, skip all spaces nuclear@0: // at the end of it. nuclear@0: const char* sz3 = sz; nuclear@0: while (!IsSpaceOrNewLine(*sz))++sz; nuclear@0: std::string objectName = std::string(sz3,(unsigned int)(sz-sz3)); nuclear@0: nuclear@0: const unsigned int objStart = (unsigned int)meshes.size(); nuclear@0: nuclear@0: // There could be a material table in a separate file nuclear@0: std::vector materialTable; nuclear@0: while (true) nuclear@0: { nuclear@0: AI_NFF2_GET_NEXT_TOKEN(); nuclear@0: nuclear@0: // material table - an external file nuclear@0: if (TokenMatch(sz,"mtable",6)) nuclear@0: { nuclear@0: SkipSpaces(&sz); nuclear@0: sz3 = sz; nuclear@0: while (!IsSpaceOrNewLine(*sz))++sz; nuclear@0: const unsigned int diff = (unsigned int)(sz-sz3); nuclear@0: if (!diff)DefaultLogger::get()->warn("NFF2: Found empty mtable token"); nuclear@0: else nuclear@0: { nuclear@0: // The material table has the file extension .mat. nuclear@0: // If it is not there, we need to append it nuclear@0: std::string path = std::string(sz3,diff); nuclear@0: if(std::string::npos == path.find_last_of(".mat")) nuclear@0: { nuclear@0: path.append(".mat"); nuclear@0: } nuclear@0: nuclear@0: // Now extract the working directory from the path to nuclear@0: // this file and append the material library filename nuclear@0: // to it. nuclear@0: std::string::size_type s; nuclear@0: if ((std::string::npos == (s = path.find_last_of('\\')) || !s) && nuclear@0: (std::string::npos == (s = path.find_last_of('/')) || !s) ) nuclear@0: { nuclear@0: s = pFile.find_last_of('\\'); nuclear@0: if (std::string::npos == s)s = pFile.find_last_of('/'); nuclear@0: if (std::string::npos != s) nuclear@0: { nuclear@0: path = pFile.substr(0,s+1) + path; nuclear@0: } nuclear@0: } nuclear@0: LoadNFF2MaterialTable(materialTable,path,pIOHandler); nuclear@0: } nuclear@0: } nuclear@0: else break; nuclear@0: } nuclear@0: nuclear@0: // read the numbr of vertices nuclear@0: unsigned int num = ::strtoul10(sz,&sz); nuclear@0: nuclear@0: // temporary storage nuclear@0: std::vector tempColors; nuclear@0: std::vector tempPositions,tempTextureCoords,tempNormals; nuclear@0: nuclear@0: bool hasNormals = false,hasUVs = false,hasColor = false; nuclear@0: nuclear@0: tempPositions.reserve (num); nuclear@0: tempColors.reserve (num); nuclear@0: tempNormals.reserve (num); nuclear@0: tempTextureCoords.reserve (num); nuclear@0: for (unsigned int i = 0; i < num; ++i) nuclear@0: { nuclear@0: AI_NFF2_GET_NEXT_TOKEN(); nuclear@0: aiVector3D v; nuclear@0: AI_NFF_PARSE_TRIPLE(v); nuclear@0: tempPositions.push_back(v); nuclear@0: nuclear@0: // parse all other attributes in the line nuclear@0: while (true) nuclear@0: { nuclear@0: SkipSpaces(&sz); nuclear@0: if (IsLineEnd(*sz))break; nuclear@0: nuclear@0: // color definition nuclear@0: if (TokenMatch(sz,"0x",2)) nuclear@0: { nuclear@0: hasColor = true; nuclear@0: register unsigned int numIdx = ::strtoul16(sz,&sz); nuclear@0: aiColor4D clr; nuclear@0: clr.a = 1.f; nuclear@0: nuclear@0: // 0xRRGGBB nuclear@0: clr.r = ((numIdx >> 16u) & 0xff) / 255.f; nuclear@0: clr.g = ((numIdx >> 8u) & 0xff) / 255.f; nuclear@0: clr.b = ((numIdx) & 0xff) / 255.f; nuclear@0: tempColors.push_back(clr); nuclear@0: } nuclear@0: // normal vector nuclear@0: else if (TokenMatch(sz,"norm",4)) nuclear@0: { nuclear@0: hasNormals = true; nuclear@0: AI_NFF_PARSE_TRIPLE(v); nuclear@0: tempNormals.push_back(v); nuclear@0: } nuclear@0: // UV coordinate nuclear@0: else if (TokenMatch(sz,"uv",2)) nuclear@0: { nuclear@0: hasUVs = true; nuclear@0: AI_NFF_PARSE_FLOAT(v.x); nuclear@0: AI_NFF_PARSE_FLOAT(v.y); nuclear@0: v.z = 0.f; nuclear@0: tempTextureCoords.push_back(v); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // fill in dummies for all attributes that have not been set nuclear@0: if (tempNormals.size() != tempPositions.size()) nuclear@0: tempNormals.push_back(vQNAN); nuclear@0: nuclear@0: if (tempTextureCoords.size() != tempPositions.size()) nuclear@0: tempTextureCoords.push_back(vQNAN); nuclear@0: nuclear@0: if (tempColors.size() != tempPositions.size()) nuclear@0: tempColors.push_back(cQNAN); nuclear@0: } nuclear@0: nuclear@0: AI_NFF2_GET_NEXT_TOKEN(); nuclear@0: if (!num)throw DeadlyImportError("NFF2: There are zero vertices"); nuclear@0: num = ::strtoul10(sz,&sz); nuclear@0: nuclear@0: std::vector tempIdx; nuclear@0: tempIdx.reserve(10); nuclear@0: for (unsigned int i = 0; i < num; ++i) nuclear@0: { nuclear@0: AI_NFF2_GET_NEXT_TOKEN(); nuclear@0: SkipSpaces(line,&sz); nuclear@0: unsigned int numIdx = ::strtoul10(sz,&sz); nuclear@0: nuclear@0: // read all faces indices nuclear@0: if (numIdx) nuclear@0: { nuclear@0: // mesh.faces.push_back(numIdx); nuclear@0: // tempIdx.erase(tempIdx.begin(),tempIdx.end()); nuclear@0: tempIdx.resize(numIdx); nuclear@0: nuclear@0: for (unsigned int a = 0; a < numIdx;++a) nuclear@0: { nuclear@0: SkipSpaces(sz,&sz); nuclear@0: m = ::strtoul10(sz,&sz); nuclear@0: if (m >= (unsigned int)tempPositions.size()) nuclear@0: { nuclear@0: DefaultLogger::get()->error("NFF2: Vertex index overflow"); nuclear@0: m= 0; nuclear@0: } nuclear@0: // mesh.vertices.push_back (tempPositions[idx]); nuclear@0: tempIdx[a] = m; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // build a temporary shader object for the face. nuclear@0: ShadingInfo shader; nuclear@0: unsigned int matIdx = 0; nuclear@0: nuclear@0: // white material color - we have vertex colors nuclear@0: shader.color = aiColor3D(1.f,1.f,1.f); nuclear@0: aiColor4D c = aiColor4D(1.f,1.f,1.f,1.f); nuclear@0: while (true) nuclear@0: { nuclear@0: SkipSpaces(sz,&sz); nuclear@0: if(IsLineEnd(*sz))break; nuclear@0: nuclear@0: // per-polygon colors nuclear@0: if (TokenMatch(sz,"0x",2)) nuclear@0: { nuclear@0: hasColor = true; nuclear@0: const char* sz2 = sz; nuclear@0: numIdx = ::strtoul16(sz,&sz); nuclear@0: const unsigned int diff = (unsigned int)(sz-sz2); nuclear@0: nuclear@0: // 0xRRGGBB nuclear@0: if (diff > 3) nuclear@0: { nuclear@0: c.r = ((numIdx >> 16u) & 0xff) / 255.f; nuclear@0: c.g = ((numIdx >> 8u) & 0xff) / 255.f; nuclear@0: c.b = ((numIdx) & 0xff) / 255.f; nuclear@0: } nuclear@0: // 0xRGB nuclear@0: else nuclear@0: { nuclear@0: c.r = ((numIdx >> 8u) & 0xf) / 16.f; nuclear@0: c.g = ((numIdx >> 4u) & 0xf) / 16.f; nuclear@0: c.b = ((numIdx) & 0xf) / 16.f; nuclear@0: } nuclear@0: } nuclear@0: // TODO - implement texture mapping here nuclear@0: #if 0 nuclear@0: // mirror vertex texture coordinate? nuclear@0: else if (TokenMatch(sz,"mirror",6)) nuclear@0: { nuclear@0: } nuclear@0: // texture coordinate scaling nuclear@0: else if (TokenMatch(sz,"scale",5)) nuclear@0: { nuclear@0: } nuclear@0: // texture coordinate translation nuclear@0: else if (TokenMatch(sz,"trans",5)) nuclear@0: { nuclear@0: } nuclear@0: // texture coordinate rotation angle nuclear@0: else if (TokenMatch(sz,"rot",3)) nuclear@0: { nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: // texture file name for this polygon + mapping information nuclear@0: else if ('_' == sz[0]) nuclear@0: { nuclear@0: // get mapping information nuclear@0: switch (sz[1]) nuclear@0: { nuclear@0: case 'v': nuclear@0: case 'V': nuclear@0: nuclear@0: shader.shaded = false; nuclear@0: break; nuclear@0: nuclear@0: case 't': nuclear@0: case 'T': nuclear@0: case 'u': nuclear@0: case 'U': nuclear@0: nuclear@0: DefaultLogger::get()->warn("Unsupported NFF2 texture attribute: trans"); nuclear@0: }; nuclear@0: if (!sz[1] || '_' != sz[2]) nuclear@0: { nuclear@0: DefaultLogger::get()->warn("NFF2: Expected underscore after texture attributes"); nuclear@0: continue; nuclear@0: } nuclear@0: const char* sz2 = sz+3; nuclear@0: while (!IsSpaceOrNewLine( *sz ))++sz; nuclear@0: const unsigned int diff = (unsigned int)(sz-sz2); nuclear@0: if (diff)shader.texFile = std::string(sz2,diff); nuclear@0: } nuclear@0: nuclear@0: // Two-sided material? nuclear@0: else if (TokenMatch(sz,"both",4)) nuclear@0: { nuclear@0: shader.twoSided = true; nuclear@0: } nuclear@0: nuclear@0: // Material ID? nuclear@0: else if (!materialTable.empty() && TokenMatch(sz,"matid",5)) nuclear@0: { nuclear@0: SkipSpaces(&sz); nuclear@0: matIdx = ::strtoul10(sz,&sz); nuclear@0: if (matIdx >= materialTable.size()) nuclear@0: { nuclear@0: DefaultLogger::get()->error("NFF2: Material index overflow."); nuclear@0: matIdx = 0; nuclear@0: } nuclear@0: nuclear@0: // now combine our current shader with the shader we nuclear@0: // read from the material table. nuclear@0: ShadingInfo& mat = materialTable[matIdx]; nuclear@0: shader.ambient = mat.ambient; nuclear@0: shader.diffuse = mat.diffuse; nuclear@0: shader.emissive = mat.emissive; nuclear@0: shader.opacity = mat.opacity; nuclear@0: shader.specular = mat.specular; nuclear@0: shader.shininess = mat.shininess; nuclear@0: } nuclear@0: else SkipToken(sz); nuclear@0: } nuclear@0: nuclear@0: // search the list of all shaders we have for this object whether nuclear@0: // there is an identical one. In this case, we append our mesh nuclear@0: // data to it. nuclear@0: MeshInfo* mesh = NULL; nuclear@0: for (std::vector::iterator it = meshes.begin() + objStart, end = meshes.end(); nuclear@0: it != end; ++it) nuclear@0: { nuclear@0: if ((*it).shader == shader && (*it).matIndex == matIdx) nuclear@0: { nuclear@0: // we have one, we can append our data to it nuclear@0: mesh = &(*it); nuclear@0: } nuclear@0: } nuclear@0: if (!mesh) nuclear@0: { nuclear@0: meshes.push_back(MeshInfo(PatchType_Simple,false)); nuclear@0: mesh = &meshes.back(); nuclear@0: mesh->matIndex = matIdx; nuclear@0: nuclear@0: // We need to add a new mesh to the list. We assign nuclear@0: // an unique name to it to make sure the scene will nuclear@0: // pass the validation step for the moment. nuclear@0: // TODO: fix naming of objects in the scenegraph later nuclear@0: if (objectName.length()) nuclear@0: { nuclear@0: ::strcpy(mesh->name,objectName.c_str()); nuclear@0: ASSIMP_itoa10(&mesh->name[objectName.length()],30,subMeshIdx++); nuclear@0: } nuclear@0: nuclear@0: // copy the shader to the mesh. nuclear@0: mesh->shader = shader; nuclear@0: } nuclear@0: nuclear@0: // fill the mesh with data nuclear@0: if (!tempIdx.empty()) nuclear@0: { nuclear@0: mesh->faces.push_back((unsigned int)tempIdx.size()); nuclear@0: for (std::vector::const_iterator it = tempIdx.begin(), end = tempIdx.end(); nuclear@0: it != end;++it) nuclear@0: { nuclear@0: m = *it; nuclear@0: nuclear@0: // copy colors -vertex color specifications override polygon color specifications nuclear@0: if (hasColor) nuclear@0: { nuclear@0: const aiColor4D& clr = tempColors[m]; nuclear@0: mesh->colors.push_back((is_qnan( clr.r ) ? c : clr)); nuclear@0: } nuclear@0: nuclear@0: // positions should always be there nuclear@0: mesh->vertices.push_back (tempPositions[m]); nuclear@0: nuclear@0: // copy normal vectors nuclear@0: if (hasNormals) nuclear@0: mesh->normals.push_back (tempNormals[m]); nuclear@0: nuclear@0: // copy texture coordinates nuclear@0: if (hasUVs) nuclear@0: mesh->uvs.push_back (tempTextureCoords[m]); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: if (!num)throw DeadlyImportError("NFF2: There are zero faces"); nuclear@0: } nuclear@0: } nuclear@0: camLookAt = camLookAt + camPos; nuclear@0: } nuclear@0: else // "Normal" Neutral file format that is quite more common nuclear@0: { nuclear@0: while (GetNextLine(buffer,line)) nuclear@0: { nuclear@0: sz = line; nuclear@0: if ('p' == line[0] || TokenMatch(sz,"tpp",3)) nuclear@0: { nuclear@0: MeshInfo* out = NULL; nuclear@0: nuclear@0: // 'tpp' - texture polygon patch primitive nuclear@0: if ('t' == line[0]) nuclear@0: { nuclear@0: currentMeshWithUVCoords = NULL; nuclear@0: for (std::vector::iterator it = meshesWithUVCoords.begin(), end = meshesWithUVCoords.end(); nuclear@0: it != end;++it) nuclear@0: { nuclear@0: if ((*it).shader == s) nuclear@0: { nuclear@0: currentMeshWithUVCoords = &(*it); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!currentMeshWithUVCoords) nuclear@0: { nuclear@0: meshesWithUVCoords.push_back(MeshInfo(PatchType_UVAndNormals)); nuclear@0: currentMeshWithUVCoords = &meshesWithUVCoords.back(); nuclear@0: currentMeshWithUVCoords->shader = s; nuclear@0: } nuclear@0: out = currentMeshWithUVCoords; nuclear@0: } nuclear@0: // 'pp' - polygon patch primitive nuclear@0: else if ('p' == line[1]) nuclear@0: { nuclear@0: currentMeshWithNormals = NULL; nuclear@0: for (std::vector::iterator it = meshesWithNormals.begin(), end = meshesWithNormals.end(); nuclear@0: it != end;++it) nuclear@0: { nuclear@0: if ((*it).shader == s) nuclear@0: { nuclear@0: currentMeshWithNormals = &(*it); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!currentMeshWithNormals) nuclear@0: { nuclear@0: meshesWithNormals.push_back(MeshInfo(PatchType_Normals)); nuclear@0: currentMeshWithNormals = &meshesWithNormals.back(); nuclear@0: currentMeshWithNormals->shader = s; nuclear@0: } nuclear@0: sz = &line[2];out = currentMeshWithNormals; nuclear@0: } nuclear@0: // 'p' - polygon primitive nuclear@0: else nuclear@0: { nuclear@0: currentMesh = NULL; nuclear@0: for (std::vector::iterator it = meshes.begin(), end = meshes.end(); nuclear@0: it != end;++it) nuclear@0: { nuclear@0: if ((*it).shader == s) nuclear@0: { nuclear@0: currentMesh = &(*it); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!currentMesh) nuclear@0: { nuclear@0: meshes.push_back(MeshInfo(PatchType_Simple)); nuclear@0: currentMesh = &meshes.back(); nuclear@0: currentMesh->shader = s; nuclear@0: } nuclear@0: sz = &line[1];out = currentMesh; nuclear@0: } nuclear@0: SkipSpaces(sz,&sz); nuclear@0: m = strtoul10(sz); nuclear@0: nuclear@0: // ---- flip the face order nuclear@0: out->vertices.resize(out->vertices.size()+m); nuclear@0: if (out != currentMesh) nuclear@0: { nuclear@0: out->normals.resize(out->vertices.size()); nuclear@0: } nuclear@0: if (out == currentMeshWithUVCoords) nuclear@0: { nuclear@0: out->uvs.resize(out->vertices.size()); nuclear@0: } nuclear@0: for (unsigned int n = 0; n < m;++n) nuclear@0: { nuclear@0: if(!GetNextLine(buffer,line)) nuclear@0: { nuclear@0: DefaultLogger::get()->error("NFF: Unexpected EOF was encountered. Patch definition incomplete"); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: aiVector3D v; sz = &line[0]; nuclear@0: AI_NFF_PARSE_TRIPLE(v); nuclear@0: out->vertices[out->vertices.size()-n-1] = v; nuclear@0: nuclear@0: if (out != currentMesh) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(v); nuclear@0: out->normals[out->vertices.size()-n-1] = v; nuclear@0: } nuclear@0: if (out == currentMeshWithUVCoords) nuclear@0: { nuclear@0: // FIX: in one test file this wraps over multiple lines nuclear@0: SkipSpaces(&sz); nuclear@0: if (IsLineEnd(*sz)) nuclear@0: { nuclear@0: GetNextLine(buffer,line); nuclear@0: sz = line; nuclear@0: } nuclear@0: AI_NFF_PARSE_FLOAT(v.x); nuclear@0: SkipSpaces(&sz); nuclear@0: if (IsLineEnd(*sz)) nuclear@0: { nuclear@0: GetNextLine(buffer,line); nuclear@0: sz = line; nuclear@0: } nuclear@0: AI_NFF_PARSE_FLOAT(v.y); nuclear@0: v.y = 1.f - v.y; nuclear@0: out->uvs[out->vertices.size()-n-1] = v; nuclear@0: } nuclear@0: } nuclear@0: out->faces.push_back(m); nuclear@0: } nuclear@0: // 'f' - shading information block nuclear@0: else if (TokenMatch(sz,"f",1)) nuclear@0: { nuclear@0: float d; nuclear@0: nuclear@0: // read the RGB colors nuclear@0: AI_NFF_PARSE_TRIPLE(s.color); nuclear@0: nuclear@0: // read the other properties nuclear@0: AI_NFF_PARSE_FLOAT(s.diffuse.r); nuclear@0: AI_NFF_PARSE_FLOAT(s.specular.r); nuclear@0: AI_NFF_PARSE_FLOAT(d); // skip shininess and transmittance nuclear@0: AI_NFF_PARSE_FLOAT(d); nuclear@0: AI_NFF_PARSE_FLOAT(s.refracti); nuclear@0: nuclear@0: // NFF2 uses full colors here so we need to use them too nuclear@0: // although NFF uses simple scaling factors nuclear@0: s.diffuse.g = s.diffuse.b = s.diffuse.r; nuclear@0: s.specular.g = s.specular.b = s.specular.r; nuclear@0: nuclear@0: // if the next one is NOT a number we assume it is a texture file name nuclear@0: // this feature is used by some NFF files on the internet and it has nuclear@0: // been implemented as it can be really useful nuclear@0: SkipSpaces(&sz); nuclear@0: if (!IsNumeric(*sz)) nuclear@0: { nuclear@0: // TODO: Support full file names with spaces and quotation marks ... nuclear@0: const char* p = sz; nuclear@0: while (!IsSpaceOrNewLine( *sz ))++sz; nuclear@0: nuclear@0: unsigned int diff = (unsigned int)(sz-p); nuclear@0: if (diff) nuclear@0: { nuclear@0: s.texFile = std::string(p,diff); nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: AI_NFF_PARSE_FLOAT(s.ambient); // optional nuclear@0: } nuclear@0: } nuclear@0: // 'shader' - other way to specify a texture nuclear@0: else if (TokenMatch(sz,"shader",6)) nuclear@0: { nuclear@0: SkipSpaces(&sz); nuclear@0: const char* old = sz; nuclear@0: while (!IsSpaceOrNewLine(*sz))++sz; nuclear@0: s.texFile = std::string(old, (uintptr_t)sz - (uintptr_t)old); nuclear@0: } nuclear@0: // 'l' - light source nuclear@0: else if (TokenMatch(sz,"l",1)) nuclear@0: { nuclear@0: lights.push_back(Light()); nuclear@0: Light& light = lights.back(); nuclear@0: nuclear@0: AI_NFF_PARSE_TRIPLE(light.position); nuclear@0: AI_NFF_PARSE_FLOAT (light.intensity); nuclear@0: AI_NFF_PARSE_TRIPLE(light.color); nuclear@0: } nuclear@0: // 's' - sphere nuclear@0: else if (TokenMatch(sz,"s",1)) nuclear@0: { nuclear@0: meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); nuclear@0: MeshInfo& currentMesh = meshesLocked.back(); nuclear@0: currentMesh.shader = s; nuclear@0: currentMesh.shader.mapping = aiTextureMapping_SPHERE; nuclear@0: nuclear@0: AI_NFF_PARSE_SHAPE_INFORMATION(); nuclear@0: nuclear@0: // we don't need scaling or translation here - we do it in the node's transform nuclear@0: StandardShapes::MakeSphere(iTesselation, currentMesh.vertices); nuclear@0: currentMesh.faces.resize(currentMesh.vertices.size()/3,3); nuclear@0: nuclear@0: // generate a name for the mesh nuclear@0: ::sprintf(currentMesh.name,"sphere_%i",sphere++); nuclear@0: } nuclear@0: // 'dod' - dodecahedron nuclear@0: else if (TokenMatch(sz,"dod",3)) nuclear@0: { nuclear@0: meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); nuclear@0: MeshInfo& currentMesh = meshesLocked.back(); nuclear@0: currentMesh.shader = s; nuclear@0: currentMesh.shader.mapping = aiTextureMapping_SPHERE; nuclear@0: nuclear@0: AI_NFF_PARSE_SHAPE_INFORMATION(); nuclear@0: nuclear@0: // we don't need scaling or translation here - we do it in the node's transform nuclear@0: StandardShapes::MakeDodecahedron(currentMesh.vertices); nuclear@0: currentMesh.faces.resize(currentMesh.vertices.size()/3,3); nuclear@0: nuclear@0: // generate a name for the mesh nuclear@0: ::sprintf(currentMesh.name,"dodecahedron_%i",dodecahedron++); nuclear@0: } nuclear@0: nuclear@0: // 'oct' - octahedron nuclear@0: else if (TokenMatch(sz,"oct",3)) nuclear@0: { nuclear@0: meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); nuclear@0: MeshInfo& currentMesh = meshesLocked.back(); nuclear@0: currentMesh.shader = s; nuclear@0: currentMesh.shader.mapping = aiTextureMapping_SPHERE; nuclear@0: nuclear@0: AI_NFF_PARSE_SHAPE_INFORMATION(); nuclear@0: nuclear@0: // we don't need scaling or translation here - we do it in the node's transform nuclear@0: StandardShapes::MakeOctahedron(currentMesh.vertices); nuclear@0: currentMesh.faces.resize(currentMesh.vertices.size()/3,3); nuclear@0: nuclear@0: // generate a name for the mesh nuclear@0: ::sprintf(currentMesh.name,"octahedron_%i",octahedron++); nuclear@0: } nuclear@0: nuclear@0: // 'tet' - tetrahedron nuclear@0: else if (TokenMatch(sz,"tet",3)) nuclear@0: { nuclear@0: meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); nuclear@0: MeshInfo& currentMesh = meshesLocked.back(); nuclear@0: currentMesh.shader = s; nuclear@0: currentMesh.shader.mapping = aiTextureMapping_SPHERE; nuclear@0: nuclear@0: AI_NFF_PARSE_SHAPE_INFORMATION(); nuclear@0: nuclear@0: // we don't need scaling or translation here - we do it in the node's transform nuclear@0: StandardShapes::MakeTetrahedron(currentMesh.vertices); nuclear@0: currentMesh.faces.resize(currentMesh.vertices.size()/3,3); nuclear@0: nuclear@0: // generate a name for the mesh nuclear@0: ::sprintf(currentMesh.name,"tetrahedron_%i",tetrahedron++); nuclear@0: } nuclear@0: nuclear@0: // 'hex' - hexahedron nuclear@0: else if (TokenMatch(sz,"hex",3)) nuclear@0: { nuclear@0: meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); nuclear@0: MeshInfo& currentMesh = meshesLocked.back(); nuclear@0: currentMesh.shader = s; nuclear@0: currentMesh.shader.mapping = aiTextureMapping_BOX; nuclear@0: nuclear@0: AI_NFF_PARSE_SHAPE_INFORMATION(); nuclear@0: nuclear@0: // we don't need scaling or translation here - we do it in the node's transform nuclear@0: StandardShapes::MakeHexahedron(currentMesh.vertices); nuclear@0: currentMesh.faces.resize(currentMesh.vertices.size()/3,3); nuclear@0: nuclear@0: // generate a name for the mesh nuclear@0: ::sprintf(currentMesh.name,"hexahedron_%i",hexahedron++); nuclear@0: } nuclear@0: // 'c' - cone nuclear@0: else if (TokenMatch(sz,"c",1)) nuclear@0: { nuclear@0: meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); nuclear@0: MeshInfo& currentMesh = meshesLocked.back(); nuclear@0: currentMesh.shader = s; nuclear@0: currentMesh.shader.mapping = aiTextureMapping_CYLINDER; nuclear@0: nuclear@0: if(!GetNextLine(buffer,line)) nuclear@0: { nuclear@0: DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)"); nuclear@0: break; nuclear@0: } nuclear@0: sz = line; nuclear@0: nuclear@0: // read the two center points and the respective radii nuclear@0: aiVector3D center1, center2; float radius1, radius2; nuclear@0: AI_NFF_PARSE_TRIPLE(center1); nuclear@0: AI_NFF_PARSE_FLOAT(radius1); nuclear@0: nuclear@0: if(!GetNextLine(buffer,line)) nuclear@0: { nuclear@0: DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)"); nuclear@0: break; nuclear@0: } nuclear@0: sz = line; nuclear@0: nuclear@0: AI_NFF_PARSE_TRIPLE(center2); nuclear@0: AI_NFF_PARSE_FLOAT(radius2); nuclear@0: nuclear@0: // compute the center point of the cone/cylinder - nuclear@0: // it is its local transformation origin nuclear@0: currentMesh.dir = center2-center1; nuclear@0: currentMesh.center = center1+currentMesh.dir/2.f; nuclear@0: nuclear@0: float f; nuclear@0: if (( f = currentMesh.dir.Length()) < 10e-3f ) nuclear@0: { nuclear@0: DefaultLogger::get()->error("NFF: Cone height is close to zero"); nuclear@0: continue; nuclear@0: } nuclear@0: currentMesh.dir /= f; // normalize nuclear@0: nuclear@0: // generate the cone - it consists of simple triangles nuclear@0: StandardShapes::MakeCone(f, radius1, radius2, nuclear@0: integer_pow(4, iTesselation), currentMesh.vertices); nuclear@0: nuclear@0: // MakeCone() returns tris nuclear@0: currentMesh.faces.resize(currentMesh.vertices.size()/3,3); nuclear@0: nuclear@0: // generate a name for the mesh. 'cone' if it a cone, nuclear@0: // 'cylinder' if it is a cylinder. Funny, isn't it? nuclear@0: if (radius1 != radius2) nuclear@0: ::sprintf(currentMesh.name,"cone_%i",cone++); nuclear@0: else ::sprintf(currentMesh.name,"cylinder_%i",cylinder++); nuclear@0: } nuclear@0: // 'tess' - tesselation nuclear@0: else if (TokenMatch(sz,"tess",4)) nuclear@0: { nuclear@0: SkipSpaces(&sz); nuclear@0: iTesselation = strtoul10(sz); nuclear@0: } nuclear@0: // 'from' - camera position nuclear@0: else if (TokenMatch(sz,"from",4)) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(camPos); nuclear@0: hasCam = true; nuclear@0: } nuclear@0: // 'at' - camera look-at vector nuclear@0: else if (TokenMatch(sz,"at",2)) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(camLookAt); nuclear@0: hasCam = true; nuclear@0: } nuclear@0: // 'up' - camera up vector nuclear@0: else if (TokenMatch(sz,"up",2)) nuclear@0: { nuclear@0: AI_NFF_PARSE_TRIPLE(camUp); nuclear@0: hasCam = true; nuclear@0: } nuclear@0: // 'angle' - (half?) camera field of view nuclear@0: else if (TokenMatch(sz,"angle",5)) nuclear@0: { nuclear@0: AI_NFF_PARSE_FLOAT(angle); nuclear@0: hasCam = true; nuclear@0: } nuclear@0: // 'resolution' - used to compute the screen aspect nuclear@0: else if (TokenMatch(sz,"resolution",10)) nuclear@0: { nuclear@0: AI_NFF_PARSE_FLOAT(resolution.x); nuclear@0: AI_NFF_PARSE_FLOAT(resolution.y); nuclear@0: hasCam = true; nuclear@0: } nuclear@0: // 'pb' - bezier patch. Not supported yet nuclear@0: else if (TokenMatch(sz,"pb",2)) nuclear@0: { nuclear@0: DefaultLogger::get()->error("NFF: Encountered unsupported ID: bezier patch"); nuclear@0: } nuclear@0: // 'pn' - NURBS. Not supported yet nuclear@0: else if (TokenMatch(sz,"pn",2) || TokenMatch(sz,"pnn",3)) nuclear@0: { nuclear@0: DefaultLogger::get()->error("NFF: Encountered unsupported ID: NURBS"); nuclear@0: } nuclear@0: // '' - comment nuclear@0: else if ('#' == line[0]) nuclear@0: { nuclear@0: const char* sz;SkipSpaces(&line[1],&sz); nuclear@0: if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // copy all arrays into one large nuclear@0: meshes.reserve (meshes.size()+meshesLocked.size()+meshesWithNormals.size()+meshesWithUVCoords.size()); nuclear@0: meshes.insert (meshes.end(),meshesLocked.begin(),meshesLocked.end()); nuclear@0: meshes.insert (meshes.end(),meshesWithNormals.begin(),meshesWithNormals.end()); nuclear@0: meshes.insert (meshes.end(),meshesWithUVCoords.begin(),meshesWithUVCoords.end()); nuclear@0: nuclear@0: // now generate output meshes. first find out how many meshes we'll need nuclear@0: std::vector::const_iterator it = meshes.begin(), end = meshes.end(); nuclear@0: for (;it != end;++it) nuclear@0: { nuclear@0: if (!(*it).faces.empty()) nuclear@0: { nuclear@0: ++pScene->mNumMeshes; nuclear@0: if ((*it).name[0])++numNamed; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // generate a dummy root node - assign all unnamed elements such nuclear@0: // as polygons and polygon patches to the root node and generate nuclear@0: // sub nodes for named objects such as spheres and cones. nuclear@0: aiNode* const root = new aiNode(); nuclear@0: root->mName.Set(""); nuclear@0: root->mNumChildren = numNamed + (hasCam ? 1 : 0) + (unsigned int) lights.size(); nuclear@0: root->mNumMeshes = pScene->mNumMeshes-numNamed; nuclear@0: nuclear@0: aiNode** ppcChildren = NULL; nuclear@0: unsigned int* pMeshes = NULL; nuclear@0: if (root->mNumMeshes) nuclear@0: pMeshes = root->mMeshes = new unsigned int[root->mNumMeshes]; nuclear@0: if (root->mNumChildren) nuclear@0: ppcChildren = root->mChildren = new aiNode*[root->mNumChildren]; nuclear@0: nuclear@0: // generate the camera nuclear@0: if (hasCam) nuclear@0: { nuclear@0: aiNode* nd = *ppcChildren = new aiNode(); nuclear@0: nd->mName.Set(""); nuclear@0: nd->mParent = root; nuclear@0: nuclear@0: // allocate the camera in the scene nuclear@0: pScene->mNumCameras = 1; nuclear@0: pScene->mCameras = new aiCamera*[1]; nuclear@0: aiCamera* c = pScene->mCameras[0] = new aiCamera; nuclear@0: nuclear@0: c->mName = nd->mName; // make sure the names are identical nuclear@0: c->mHorizontalFOV = AI_DEG_TO_RAD( angle ); nuclear@0: c->mLookAt = camLookAt - camPos; nuclear@0: c->mPosition = camPos; nuclear@0: c->mUp = camUp; nuclear@0: nuclear@0: // If the resolution is not specified in the file, we nuclear@0: // need to set 1.0 as aspect. nuclear@0: c->mAspect = (!resolution.y ? 0.f : resolution.x / resolution.y); nuclear@0: ++ppcChildren; nuclear@0: } nuclear@0: nuclear@0: // generate light sources nuclear@0: if (!lights.empty()) nuclear@0: { nuclear@0: pScene->mNumLights = (unsigned int)lights.size(); nuclear@0: pScene->mLights = new aiLight*[pScene->mNumLights]; nuclear@0: for (unsigned int i = 0; i < pScene->mNumLights;++i,++ppcChildren) nuclear@0: { nuclear@0: const Light& l = lights[i]; nuclear@0: nuclear@0: aiNode* nd = *ppcChildren = new aiNode(); nuclear@0: nd->mParent = root; nuclear@0: nuclear@0: nd->mName.length = ::sprintf(nd->mName.data,"",i); nuclear@0: nuclear@0: // allocate the light in the scene data structure nuclear@0: aiLight* out = pScene->mLights[i] = new aiLight(); nuclear@0: out->mName = nd->mName; // make sure the names are identical nuclear@0: out->mType = aiLightSource_POINT; nuclear@0: out->mColorDiffuse = out->mColorSpecular = l.color * l.intensity; nuclear@0: out->mPosition = l.position; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!pScene->mNumMeshes)throw DeadlyImportError("NFF: No meshes loaded"); nuclear@0: pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; nuclear@0: pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes]; nuclear@0: for (it = meshes.begin(), m = 0; it != end;++it) nuclear@0: { nuclear@0: if ((*it).faces.empty())continue; nuclear@0: nuclear@0: const MeshInfo& src = *it; nuclear@0: aiMesh* const mesh = pScene->mMeshes[m] = new aiMesh(); nuclear@0: mesh->mNumVertices = (unsigned int)src.vertices.size(); nuclear@0: mesh->mNumFaces = (unsigned int)src.faces.size(); nuclear@0: nuclear@0: // Generate sub nodes for named meshes nuclear@0: if (src.name[0]) nuclear@0: { nuclear@0: aiNode* const node = *ppcChildren = new aiNode(); nuclear@0: node->mParent = root; nuclear@0: node->mNumMeshes = 1; nuclear@0: node->mMeshes = new unsigned int[1]; nuclear@0: node->mMeshes[0] = m; nuclear@0: node->mName.Set(src.name); nuclear@0: nuclear@0: // setup the transformation matrix of the node nuclear@0: aiMatrix4x4::FromToMatrix(aiVector3D(0.f,1.f,0.f), nuclear@0: src.dir,node->mTransformation); nuclear@0: nuclear@0: aiMatrix4x4& mat = node->mTransformation; nuclear@0: mat.a1 *= src.radius.x; mat.b1 *= src.radius.x; mat.c1 *= src.radius.x; nuclear@0: mat.a2 *= src.radius.y; mat.b2 *= src.radius.y; mat.c2 *= src.radius.y; nuclear@0: mat.a3 *= src.radius.z; mat.b3 *= src.radius.z; mat.c3 *= src.radius.z; nuclear@0: mat.a4 = src.center.x; nuclear@0: mat.b4 = src.center.y; nuclear@0: mat.c4 = src.center.z; nuclear@0: nuclear@0: ++ppcChildren; nuclear@0: } nuclear@0: else *pMeshes++ = m; nuclear@0: nuclear@0: // copy vertex positions nuclear@0: mesh->mVertices = new aiVector3D[mesh->mNumVertices]; nuclear@0: ::memcpy(mesh->mVertices,&src.vertices[0], nuclear@0: sizeof(aiVector3D)*mesh->mNumVertices); nuclear@0: nuclear@0: // NFF2: there could be vertex colors nuclear@0: if (!src.colors.empty()) nuclear@0: { nuclear@0: ai_assert(src.colors.size() == src.vertices.size()); nuclear@0: nuclear@0: // copy vertex colors nuclear@0: mesh->mColors[0] = new aiColor4D[mesh->mNumVertices]; nuclear@0: ::memcpy(mesh->mColors[0],&src.colors[0], nuclear@0: sizeof(aiColor4D)*mesh->mNumVertices); nuclear@0: } nuclear@0: nuclear@0: if (!src.normals.empty()) nuclear@0: { nuclear@0: ai_assert(src.normals.size() == src.vertices.size()); nuclear@0: nuclear@0: // copy normal vectors nuclear@0: mesh->mNormals = new aiVector3D[mesh->mNumVertices]; nuclear@0: ::memcpy(mesh->mNormals,&src.normals[0], nuclear@0: sizeof(aiVector3D)*mesh->mNumVertices); nuclear@0: } nuclear@0: nuclear@0: if (!src.uvs.empty()) nuclear@0: { nuclear@0: ai_assert(src.uvs.size() == src.vertices.size()); nuclear@0: nuclear@0: // copy texture coordinates nuclear@0: mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; nuclear@0: ::memcpy(mesh->mTextureCoords[0],&src.uvs[0], nuclear@0: sizeof(aiVector3D)*mesh->mNumVertices); nuclear@0: } nuclear@0: nuclear@0: // generate faces nuclear@0: unsigned int p = 0; nuclear@0: aiFace* pFace = mesh->mFaces = new aiFace[mesh->mNumFaces]; nuclear@0: for (std::vector::const_iterator it2 = src.faces.begin(), nuclear@0: end2 = src.faces.end(); nuclear@0: it2 != end2;++it2,++pFace) nuclear@0: { nuclear@0: pFace->mIndices = new unsigned int [ pFace->mNumIndices = *it2 ]; nuclear@0: for (unsigned int o = 0; o < pFace->mNumIndices;++o) nuclear@0: pFace->mIndices[o] = p++; nuclear@0: } nuclear@0: nuclear@0: // generate a material for the mesh nuclear@0: aiMaterial* pcMat = (aiMaterial*)(pScene->mMaterials[m] = new aiMaterial()); nuclear@0: nuclear@0: mesh->mMaterialIndex = m++; nuclear@0: nuclear@0: aiString s; nuclear@0: s.Set(AI_DEFAULT_MATERIAL_NAME); nuclear@0: pcMat->AddProperty(&s, AI_MATKEY_NAME); nuclear@0: nuclear@0: // FIX: Ignore diffuse == 0 nuclear@0: aiColor3D c = src.shader.color * (src.shader.diffuse.r ? src.shader.diffuse : aiColor3D(1.f,1.f,1.f)); nuclear@0: pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE); nuclear@0: c = src.shader.color * src.shader.specular; nuclear@0: pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR); nuclear@0: nuclear@0: // NFF2 - default values for NFF nuclear@0: pcMat->AddProperty(&src.shader.ambient, 1,AI_MATKEY_COLOR_AMBIENT); nuclear@0: pcMat->AddProperty(&src.shader.emissive,1,AI_MATKEY_COLOR_EMISSIVE); nuclear@0: pcMat->AddProperty(&src.shader.opacity, 1,AI_MATKEY_OPACITY); nuclear@0: nuclear@0: // setup the first texture layer, if existing nuclear@0: if (src.shader.texFile.length()) nuclear@0: { nuclear@0: s.Set(src.shader.texFile); nuclear@0: pcMat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); nuclear@0: nuclear@0: if (aiTextureMapping_UV != src.shader.mapping) { nuclear@0: nuclear@0: aiVector3D v(0.f,-1.f,0.f); nuclear@0: pcMat->AddProperty(&v, 1,AI_MATKEY_TEXMAP_AXIS_DIFFUSE(0)); nuclear@0: pcMat->AddProperty((int*)&src.shader.mapping, 1,AI_MATKEY_MAPPING_DIFFUSE(0)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // setup the name of the material nuclear@0: if (src.shader.name.length()) nuclear@0: { nuclear@0: s.Set(src.shader.texFile); nuclear@0: pcMat->AddProperty(&s,AI_MATKEY_NAME); nuclear@0: } nuclear@0: nuclear@0: // setup some more material properties that are specific to NFF2 nuclear@0: int i; nuclear@0: if (src.shader.twoSided) nuclear@0: { nuclear@0: i = 1; nuclear@0: pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED); nuclear@0: } nuclear@0: i = (src.shader.shaded ? aiShadingMode_Gouraud : aiShadingMode_NoShading); nuclear@0: if (src.shader.shininess) nuclear@0: { nuclear@0: i = aiShadingMode_Phong; nuclear@0: pcMat->AddProperty(&src.shader.shininess,1,AI_MATKEY_SHININESS); nuclear@0: } nuclear@0: pcMat->AddProperty(&i,1,AI_MATKEY_SHADING_MODEL); nuclear@0: } nuclear@0: pScene->mRootNode = root; nuclear@0: } nuclear@0: nuclear@0: #endif // !! ASSIMP_BUILD_NO_NFF_IMPORTER