diff --git a/code/OgreImporter.cpp b/code/OgreImporter.cpp index eb3b569f5..bcc49c9ea 100644 --- a/code/OgreImporter.cpp +++ b/code/OgreImporter.cpp @@ -38,9 +38,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file OgreImporter.cpp - * @brief Implementation of the Ogre XML (.mesh.xml) loader. - */ #include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER @@ -85,10 +82,6 @@ bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandle void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler) { - m_CurrentFilename = pFile; - m_CurrentIOHandler = pIOHandler; - m_CurrentScene = pScene; - // -------------------- Initial file and XML operations -------------------- // Open @@ -155,7 +148,7 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass /** @todo What is the correct way of handling empty ref here. Does Assimp require there to be a valid material index for each mesh, even if its a dummy material. */ - aiMaterial* material = LoadMaterial(submesh->MaterialName); + aiMaterial* material = ReadMaterial(pFile, pIOHandler, submesh->MaterialName); materials.push_back(material); } @@ -179,13 +172,14 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass vector Bones; vector Animations; + if (CurrentNodeNameEquals(reader, nnSkeletonLink)) { - string SkeletonFile = GetAttribute(reader.get(), "name"); - if (!SkeletonFile.empty()) - LoadSkeleton(SkeletonFile, Bones, Animations); + string skeletonFile = GetAttribute(reader.get(), "name"); + if (!skeletonFile.empty()) + ReadSkeleton(pFile, pIOHandler, pScene, skeletonFile, Bones, Animations); else - DefaultLogger::get()->debug("Found a unusual <" + nnSkeletonLink + "> with a empty reference"); + DefaultLogger::get()->debug("Found a unusual <" + nnSkeletonLink + "> with a empty file reference"); NextNode(reader.get()); } else @@ -204,34 +198,34 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass // -------------------- Apply to aiScene -------------------- //put the aiMaterials in the scene: - m_CurrentScene->mMaterials=new aiMaterial*[materials.size()]; - m_CurrentScene->mNumMaterials=materials.size(); + pScene->mMaterials=new aiMaterial*[materials.size()]; + pScene->mNumMaterials=materials.size(); for(unsigned int i=0; imMaterials[i]=materials[i]; + pScene->mMaterials[i]=materials[i]; //create the aiMehs... vector aiMeshes; BOOST_FOREACH(boost::shared_ptr theSubMesh, subMeshes) { - aiMeshes.push_back(CreateAssimpSubMesh(*theSubMesh, Bones)); + aiMeshes.push_back(CreateAssimpSubMesh(pScene, *theSubMesh, Bones)); } //... and put them in the scene: - m_CurrentScene->mNumMeshes=aiMeshes.size(); - m_CurrentScene->mMeshes=new aiMesh*[aiMeshes.size()]; - memcpy(m_CurrentScene->mMeshes, &(aiMeshes[0]), sizeof(aiMeshes[0])*aiMeshes.size()); + pScene->mNumMeshes=aiMeshes.size(); + pScene->mMeshes=new aiMesh*[aiMeshes.size()]; + memcpy(pScene->mMeshes, &(aiMeshes[0]), sizeof(aiMeshes[0])*aiMeshes.size()); //Create the root node - m_CurrentScene->mRootNode=new aiNode("root"); + pScene->mRootNode=new aiNode("root"); //link the meshs with the root node: - m_CurrentScene->mRootNode->mMeshes=new unsigned int[subMeshes.size()]; - m_CurrentScene->mRootNode->mNumMeshes=subMeshes.size(); + pScene->mRootNode->mMeshes=new unsigned int[subMeshes.size()]; + pScene->mRootNode->mNumMeshes=subMeshes.size(); for(unsigned int i=0; imRootNode->mMeshes[i]=i; + pScene->mRootNode->mMeshes[i]=i; - CreateAssimpSkeleton(Bones, Animations); - PutAnimationsInScene(Bones, Animations); + CreateAssimpSkeleton(pScene, Bones, Animations); + PutAnimationsInScene(pScene, Bones, Animations); } @@ -243,8 +237,8 @@ const aiImporterDesc* OgreImporter::GetInfo () const void OgreImporter::SetupProperties(const Importer* pImp) { - m_MaterialLibFilename=pImp->GetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "Scene.material"); - m_TextureTypeFromFilename=pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false); + m_userDefinedMaterialLibFile = pImp->GetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "Scene.material"); + m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false); } diff --git a/code/OgreImporter.hpp b/code/OgreImporter.hpp index d946081da..b41d85b43 100644 --- a/code/OgreImporter.hpp +++ b/code/OgreImporter.hpp @@ -93,35 +93,42 @@ private: static void ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry); /// Uses the bone data to convert a SubMesh into a aiMesh which will be created and returned. - aiMesh* CreateAssimpSubMesh(const SubMesh &submesh, const std::vector& bones) const; + aiMesh* CreateAssimpSubMesh(aiScene *pScene, const SubMesh &submesh, const std::vector& bones) const; //-------------------------------- OgreSkeleton.cpp ------------------------------- /// Writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it! - void LoadSkeleton(std::string FileName, std::vector &Bones, std::vector &Animations) const; + void ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene, + const std::string &skeletonFile, std::vector &Bones, std::vector &Animations) const; /// Converts the animations in aiAnimations and puts them into the scene. - void PutAnimationsInScene(const std::vector &Bones, const std::vector &Animations); + void PutAnimationsInScene(aiScene *pScene, const std::vector &Bones, const std::vector &Animations); /// Creates the aiSkeleton in current scene. - void CreateAssimpSkeleton(const std::vector &Bones, const std::vector &Animations); + void CreateAssimpSkeleton(aiScene *pScene, const std::vector &Bones, const std::vector &Animations); /// Recursivly creates a filled aiNode from a given root bone. static aiNode* CreateAiNodeFromBone(int BoneId, const std::vector &Bones, aiNode* ParentNode); //-------------------------------- OgreMaterial.cpp ------------------------------- - aiMaterial* LoadMaterial(const std::string MaterialName) const; - void ReadTechnique(std::stringstream &ss, aiMaterial* NewMaterial) const; + /// Reads material + aiMaterial* ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string MaterialName); + + // These functions parse blocks from a material file from @c ss. Starting parsing from "{" and ending it to "}". + bool ReadTechnique(const std::string &techniqueName, std::stringstream &ss, aiMaterial *material); + bool ReadPass(const std::string &passName, std::stringstream &ss, aiMaterial *material); + bool ReadTextureUnit(const std::string &textureUnitName, std::stringstream &ss, aiMaterial *material); // Now we don't have to give theses parameters to all functions /// @todo Remove this m_Current* bookkeeping. - std::string m_CurrentFilename; - std::string m_MaterialLibFilename; - bool m_TextureTypeFromFilename; - IOSystem* m_CurrentIOHandler; - aiScene *m_CurrentScene; + + std::string m_userDefinedMaterialLibFile; + bool m_detectTextureTypeFromFilename; + SubMesh m_SharedGeometry;///< we will just use the vertexbuffers of the submesh + + std::map m_textures; }; /// Simplified face. diff --git a/code/OgreMaterial.cpp b/code/OgreMaterial.cpp index b9913648e..b8e0a05a0 100644 --- a/code/OgreMaterial.cpp +++ b/code/OgreMaterial.cpp @@ -60,34 +60,35 @@ namespace Assimp namespace Ogre { +static const string partComment = "//"; +static const string partBlockStart = "{"; +static const string partBlockEnd = "}"; - -aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const +/// Skips a line from current @ss position until a newline. Returns the skipped part. +std::string SkipLine(stringstream &ss) { - /*For better understanding of the material parser, here is a material example file: + string skipped; + getline(ss, skipped); + return skipped; +} - material Sarg - { - receive_shadows on - technique - { - pass - { - ambient 0.500000 0.500000 0.500000 1.000000 - diffuse 0.640000 0.640000 0.640000 1.000000 - specular 0.500000 0.500000 0.500000 1.000000 12.500000 - emissive 0.000000 0.000000 0.000000 1.000000 - texture_unit - { - texture SargTextur.tga - tex_address_mode wrap - filtering linear linear none - } - } - } - } +/// Skips a line and reads next element from @c ss to @c nextElement. +/** @return Skipped line content until newline. */ +std::string NextAfterNewLine(stringstream &ss, std::string &nextElement) +{ + string skipped = SkipLine(ss); + ss >> nextElement; + return skipped; +} - */ +aiMaterial* OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string materialName) +{ + /// @todo Should we return null ptr here or a empty material? + if (materialName.empty()) + return new aiMaterial(); + + // Full reference and examples of Ogre Material Script + // can be found from http://www.ogre3d.org/docs/manual/manual_14.html /*and here is another one: @@ -112,342 +113,421 @@ aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const } */ - //Read the file into memory and put it in a stringstream stringstream ss; - {// after this block, the temporarly loaded data will be released - /* - We have 3 guesses for the Material filename: - - the Material Name - - the Name of the mesh file - - the DefaultMaterialLib (which you can set before importing) - */ + // Scope for scopre_ptr auto release + { + /* There are three .material options in priority order: + 1) File with the material name (materialName) + 2) File with the mesh files base name (pFile) + 3) Optional user defined material library file (m_userDefinedMaterialLibFile) */ + std::vector potentialFiles; + potentialFiles.push_back(materialName + ".material"); + potentialFiles.push_back(pFile.substr(0, pFile.rfind(".mesh")) + ".material"); + if (!m_userDefinedMaterialLibFile.empty()) + potentialFiles.push_back(m_userDefinedMaterialLibFile); - IOStream* MatFilePtr=m_CurrentIOHandler->Open(MaterialName+".material"); - if(NULL==MatFilePtr) + IOStream *materialFile = 0; + for(size_t i=0; iOpen(potentialFiles[i]); + if (materialFile) + break; + DefaultLogger::get()->debug(Formatter::format() << "Source file for material '" << materialName << "' " << potentialFiles[i] << " does not exist"); + } + if (!materialFile) + { + /// @todo Should we return null ptr here or a empty material? + DefaultLogger::get()->error(Formatter::format() << "Failed to find source file for material '" << materialName << "'"); + return new aiMaterial(); + } - MatFilePtr=m_CurrentIOHandler->Open(MaterialFileName); - if(NULL==MatFilePtr) - { - //try the default mat Library - if(NULL==MatFilePtr) - { - - MatFilePtr=m_CurrentIOHandler->Open(m_MaterialLibFilename); - if(NULL==MatFilePtr) - { - DefaultLogger::get()->error(m_MaterialLibFilename+" and "+MaterialFileName + " could not be opened, Material will not be loaded!"); - return new aiMaterial(); - } - } - } - } - //Fill the stream - boost::scoped_ptr MaterialFile(MatFilePtr); - if(MaterialFile->FileSize()>0) + boost::scoped_ptr stream(materialFile); + if (stream->FileSize() == 0) { - vector FileData(MaterialFile->FileSize()); - MaterialFile->Read(&FileData[0], MaterialFile->FileSize(), 1); - BaseImporter::ConvertToUTF8(FileData); + /// @todo Should we return null ptr here or a empty material? + DefaultLogger::get()->warn(Formatter::format() << "Source file for material '" << materialName << "' is empty (size is 0 bytes)"); + return new aiMaterial(); + } - FileData.push_back('\0');//terminate the string with zero, so that the ss can parse it correctly - ss << &FileData[0]; - } - else - { - DefaultLogger::get()->warn("Material " + MaterialName + " seams to be empty"); - return NULL; - } + // Read bytes + vector data(stream->FileSize()); + stream->Read(&data[0], stream->FileSize(), 1); + + // Convert to UTF-8 and terminate the string for ss + BaseImporter::ConvertToUTF8(data); + data.push_back('\0'); + + ss << &data[0]; } + + DefaultLogger::get()->debug("Reading material '" + materialName + "'"); - //create the material - aiMaterial *NewMaterial=new aiMaterial(); + aiMaterial *material = new aiMaterial(); + m_textures.clear(); + + aiString ts(materialName); + material->AddProperty(&ts, AI_MATKEY_NAME); - aiString ts(MaterialName.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_NAME); + // The stringstream will push words from a line until newline. + // It will also trim whitespace from line start and between words. + string linePart; + ss >> linePart; + + const string partMaterial = "material"; + const string partTechnique = "technique"; - string Line; - ss >> Line; -// unsigned int Level=0;//Hierarchielevels in the material file, like { } blocks into another while(!ss.eof()) { - if(Line=="material") + // Skip commented lines + if (linePart == partComment) { - ss >> Line; - if(Line==MaterialName)//Load the next material - { - string RestOfLine; - getline(ss, RestOfLine);//ignore the rest of the line - ss >> Line; - - if(Line!="{") - { - DefaultLogger::get()->warn("empyt material!"); - return NULL; - } - - while(Line!="}")//read until the end of the material - { - //Proceed to the first technique - ss >> Line; - if(Line=="technique") - { - ReadTechnique(ss, NewMaterial); - } - - DefaultLogger::get()->info(Line); - //read informations from a custom material: - if(Line=="set") - { - ss >> Line; - if(Line=="$specular")//todo load this values: - { - } - if(Line=="$diffuse") - { - } - if(Line=="$ambient") - { - } - if(Line=="$colormap") - { - ss >> Line; - aiString ts(Line.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); - } - if(Line=="$normalmap") - { - ss >> Line; - aiString ts(Line.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0)); - } - - if(Line=="$shininess_strength") - { - ss >> Line; - float Shininess=fast_atof(Line.c_str()); - NewMaterial->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS_STRENGTH); - } - - if(Line=="$shininess_exponent") - { - ss >> Line; - float Shininess=fast_atof(Line.c_str()); - NewMaterial->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS); - } - - //Properties from Venetica: - if(Line=="$diffuse_map") - { - ss >> Line; - if(Line[0]=='"')// "file" -> file - Line=Line.substr(1, Line.size()-2); - aiString ts(Line.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); - } - if(Line=="$specular_map") - { - ss >> Line; - if(Line[0]=='"')// "file" -> file - Line=Line.substr(1, Line.size()-2); - aiString ts(Line.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_SHININESS, 0)); - } - if(Line=="$normal_map") - { - ss >> Line; - if(Line[0]=='"')// "file" -> file - Line=Line.substr(1, Line.size()-2); - aiString ts(Line.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0)); - } - if(Line=="$light_map") - { - ss >> Line; - if(Line[0]=='"')// "file" -> file - Line=Line.substr(1, Line.size()-2); - aiString ts(Line.c_str()); - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0)); - } - } - }//end of material - } - else {} //this is the wrong material, proceed the file until we reach the next material + string postComment = NextAfterNewLine(ss, linePart); + DefaultLogger::get()->debug("//" + postComment + " (comment line ignored)"); + continue; } - ss >> Line; - } - - return NewMaterial; -} - -void OgreImporter::ReadTechnique(stringstream &ss, aiMaterial* NewMaterial) const -{ - unsigned int CurrentDiffuseTextureId=0; - unsigned int CurrentSpecularTextureId=0; - unsigned int CurrentNormalTextureId=0; - unsigned int CurrentLightTextureId=0; - - - string RestOfLine; - getline(ss, RestOfLine);//ignore the rest of the line - - string Line; - ss >> Line; - if(Line!="{") - { - DefaultLogger::get()->warn("empty technique!"); - return; - } - while(Line!="}")//read until the end of the technique - { - ss >> Line; - if(Line=="pass") + if (linePart != partMaterial) { - getline(ss, RestOfLine);//ignore the rest of the line + ss >> linePart; + continue; + } - ss >> Line; - if(Line!="{") - { - DefaultLogger::get()->warn("empty pass!"); - return; - } - while(Line!="}")//read until the end of the pass - { - ss >> Line; - if(Line=="ambient") - { - float r,g,b; - ss >> r >> g >> b; - const aiColor3D Color(r,g,b); - NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_AMBIENT); - } - else if(Line=="diffuse") - { - float r,g,b; - ss >> r >> g >> b; - const aiColor3D Color(r,g,b); - NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_DIFFUSE); - } - else if(Line=="specular") - { - float r,g,b; - ss >> r >> g >> b; - const aiColor3D Color(r,g,b); - NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_SPECULAR); - } - else if(Line=="emmisive") - { - float r,g,b; - ss >> r >> g >> b; - const aiColor3D Color(r,g,b); - NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_EMISSIVE); - } - else if(Line=="texture_unit") - { - getline(ss, RestOfLine);//ignore the rest of the line + ss >> linePart; + if (linePart != materialName) + { + //DefaultLogger::get()->debug(Formatter::format() << "Found material '" << linePart << "' that does not match at index " << ss.tellg()); + ss >> linePart; + continue; + } - std::string TextureName; - int TextureType=-1; - int UvSet=0; + NextAfterNewLine(ss, linePart); + if (linePart != partBlockStart) + { + DefaultLogger::get()->error(Formatter::format() << "Invalid material: block start missing near index " << ss.tellg()); + return material; + } + + DefaultLogger::get()->debug("material '" + materialName + "'"); - ss >> Line; - if(Line!="{") - throw DeadlyImportError("empty texture unit!"); - while(Line!="}")//read until the end of the texture_unit - { - ss >> Line; - if(Line=="texture") - { - ss >> Line; - TextureName=Line; - - if(m_TextureTypeFromFilename) - { - if(Line.find("_n.")!=string::npos)// Normalmap - { - TextureType=aiTextureType_NORMALS; - } - else if(Line.find("_s.")!=string::npos)// Specularmap - { - TextureType=aiTextureType_SPECULAR; - } - else if(Line.find("_l.")!=string::npos)// Lightmap - { - TextureType=aiTextureType_LIGHTMAP; - } - else// colormap - { - TextureType=aiTextureType_DIFFUSE; - } - } - else - { - TextureType=aiTextureType_DIFFUSE; - } - } - else if(Line=="tex_coord_set") - { - ss >> UvSet; - } - else if(Line=="colour_op")//TODO implement this - { - /* - ss >> Line; - if("replace"==Line)//I don't think, assimp has something for this... - { - } - else if("modulate"==Line) - { - //TODO: set value - //NewMaterial->AddProperty(aiTextureOp_Multiply) - } - */ - } - - }//end of texture unit - Line="";//clear the } that would end the outer loop - - //give the texture to assimp: + while(linePart != partBlockEnd) + { + // Proceed to the first technique + ss >> linePart; - aiString ts(TextureName.c_str()); - switch(TextureType) - { - case aiTextureType_DIFFUSE: - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, CurrentDiffuseTextureId)); - NewMaterial->AddProperty(&UvSet, 1, AI_MATKEY_UVWSRC(0, CurrentDiffuseTextureId)); - CurrentDiffuseTextureId++; - break; - case aiTextureType_NORMALS: - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, CurrentNormalTextureId)); - NewMaterial->AddProperty(&UvSet, 1, AI_MATKEY_UVWSRC(0, CurrentNormalTextureId)); - CurrentNormalTextureId++; - break; - case aiTextureType_SPECULAR: - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_SPECULAR, CurrentSpecularTextureId)); - NewMaterial->AddProperty(&UvSet, 1, AI_MATKEY_UVWSRC(0, CurrentSpecularTextureId)); - CurrentSpecularTextureId++; - break; - case aiTextureType_LIGHTMAP: - NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, CurrentLightTextureId)); - NewMaterial->AddProperty(&UvSet, 1, AI_MATKEY_UVWSRC(0, CurrentLightTextureId)); - CurrentLightTextureId++; - break; - default: - DefaultLogger::get()->warn("Invalid Texture Type!"); - break; - } - } + if (linePart == partTechnique) + { + string techniqueName = trim(SkipLine(ss)); + ReadTechnique(techniqueName, ss, material); } - Line="";//clear the } that would end the outer loop + + // Read informations from a custom material + /** @todo This "set $x y" does not seem to be a official Ogre material system feature. + Materials can inherit other materials and override texture units by using the (unique) + parent texture unit name in your cloned material. + This is not yet supported and below code is probably some hack from the original + author of this Ogre importer. Should be removed? */ + if(linePart=="set") + { + ss >> linePart; + if(linePart=="$specular")//todo load this values: + { + } + if(linePart=="$diffuse") + { + } + if(linePart=="$ambient") + { + } + if(linePart=="$colormap") + { + ss >> linePart; + aiString ts(linePart.c_str()); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + } + if(linePart=="$normalmap") + { + ss >> linePart; + aiString ts(linePart.c_str()); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0)); + } + + if(linePart=="$shininess_strength") + { + ss >> linePart; + float Shininess=fast_atof(linePart.c_str()); + material->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS_STRENGTH); + } + + if(linePart=="$shininess_exponent") + { + ss >> linePart; + float Shininess=fast_atof(linePart.c_str()); + material->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS); + } + + //Properties from Venetica: + if(linePart=="$diffuse_map") + { + ss >> linePart; + if(linePart[0]=='"')// "file" -> file + linePart=linePart.substr(1, linePart.size()-2); + aiString ts(linePart.c_str()); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + } + if(linePart=="$specular_map") + { + ss >> linePart; + if(linePart[0]=='"')// "file" -> file + linePart=linePart.substr(1, linePart.size()-2); + aiString ts(linePart.c_str()); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_SHININESS, 0)); + } + if(linePart=="$normal_map") + { + ss >> linePart; + if(linePart[0]=='"')// "file" -> file + linePart=linePart.substr(1, linePart.size()-2); + aiString ts(linePart.c_str()); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0)); + } + if(linePart=="$light_map") + { + ss >> linePart; + if(linePart[0]=='"')// "file" -> file + linePart=linePart.substr(1, linePart.size()-2); + aiString ts(linePart.c_str()); + material->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0)); + } + } } - }//end of technique + ss >> linePart; + } + + return material; } +bool OgreImporter::ReadTechnique(const std::string &techniqueName, stringstream &ss, aiMaterial *material) +{ + string linePart; + ss >> linePart; -}//namespace Ogre -}//namespace Assimp + if (linePart != partBlockStart) + { + DefaultLogger::get()->error(Formatter::format() << "Invalid material: Technique block start missing near index " << ss.tellg()); + return false; + } -#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER + DefaultLogger::get()->debug(" technique '" + techniqueName + "'"); + + const string partPass = "pass"; + + while(linePart != partBlockEnd) + { + ss >> linePart; + + // Skip commented lines + if (linePart == partComment) + { + string postComment = SkipLine(ss); + DefaultLogger::get()->debug(" //" + postComment + " (comment line ignored)"); + continue; + } + + /// @todo Techniques have other attributes than just passes. + if (linePart == partPass) + { + string passName = trim(SkipLine(ss)); + ReadPass(passName, ss, material); + } + } + return true; +} + +bool OgreImporter::ReadPass(const std::string &passName, stringstream &ss, aiMaterial *material) +{ + string linePart; + ss >> linePart; + + if (linePart != partBlockStart) + { + DefaultLogger::get()->error(Formatter::format() << "Invalid material: Pass block start missing near index " << ss.tellg()); + return false; + } + + DefaultLogger::get()->debug(" pass '" + passName + "'"); + + const string partAmbient = "ambient"; + const string partDiffuse = "diffuse"; + const string partSpecular = "specular"; + const string partEmissive = "emissive"; + const string partTextureUnit = "texture_unit"; + + while(linePart != partBlockEnd) + { + ss >> linePart; + + // Skip commented lines + if (linePart == partComment) + { + string postComment = SkipLine(ss); + DefaultLogger::get()->debug(" //" + postComment + " (comment line ignored)"); + continue; + } + + // Colors + /// @todo Support alpha via aiColor4D. + if (linePart == partAmbient || linePart == partDiffuse || linePart == partSpecular || linePart == partEmissive) + { + float r, g, b; + ss >> r >> g >> b; + const aiColor3D color(r, g, b); + + DefaultLogger::get()->debug(Formatter::format() << " " << linePart << " " << r << " " << g << " " << b); + + if (linePart == partAmbient) + material->AddProperty(&color, 1, AI_MATKEY_COLOR_AMBIENT); + else if (linePart == partDiffuse) + material->AddProperty(&color, 1, AI_MATKEY_COLOR_DIFFUSE); + else if (linePart == partSpecular) + material->AddProperty(&color, 1, AI_MATKEY_COLOR_SPECULAR); + else if (linePart == partEmissive) + material->AddProperty(&color, 1, AI_MATKEY_COLOR_EMISSIVE); + } + else if (linePart == partTextureUnit) + { + string textureUnitName = trim(SkipLine(ss)); + ReadTextureUnit(textureUnitName, ss, material); + } + } + return true; +} + +bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstream &ss, aiMaterial *material) +{ + string linePart; + ss >> linePart; + + if (linePart != partBlockStart) + { + DefaultLogger::get()->error(Formatter::format() << "Invalid material: Texture unit block start missing near index " << ss.tellg()); + return false; + } + + DefaultLogger::get()->debug(" texture_unit '" + textureUnitName + "'"); + + const string partTexture = "texture"; + const string partTextCoordSet = "tex_coord_set"; + const string partColorOp = "colour_op"; + + aiTextureType textureType = aiTextureType_NONE; + std::string textureRef; + int uvCoord = 0; + + while(linePart != partBlockEnd) + { + ss >> linePart; + + // Skip commented lines + if (linePart == partComment) + { + string postComment = SkipLine(ss); + DefaultLogger::get()->debug(" //" + postComment + " (comment line ignored)"); + continue; + } + + if (linePart == partTexture) + { + ss >> linePart; + textureRef = linePart; + + // User defined Assimp config property to detect texture type from filename. + if (m_detectTextureTypeFromFilename) + { + size_t posSuffix = textureRef.find_last_of("."); + size_t posUnderscore = textureRef.find_last_of("_"); + + if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) + { + string identifier = Ogre::ToLower(textureRef.substr(posUnderscore, posSuffix - posUnderscore)); + DefaultLogger::get()->debug(Formatter::format() << "Detecting texture type from filename postfix '" << identifier << "'"); + + if (identifier == "_n" || identifier == "_nrm" || identifier == "_nrml" || identifier == "_normal" || identifier == "_normals" || identifier == "_normalmap") + textureType = aiTextureType_NORMALS; + else if (identifier == "_s" || identifier == "_spec" || identifier == "_specular" || identifier == "_specularmap") + textureType = aiTextureType_SPECULAR; + else if (identifier == "_l" || identifier == "_light" || identifier == "_lightmap" || identifier == "_occ" || identifier == "_occlusion") + textureType = aiTextureType_LIGHTMAP; + else if (identifier == "_disp" || identifier == "_displacement") + textureType = aiTextureType_DISPLACEMENT; + else + textureType = aiTextureType_DIFFUSE; + } + else + textureType = aiTextureType_DIFFUSE; + } + // Detect from texture unit name. This cannot be too broad as + // authors might give names like "LightSaber" or "NormalNinja". + else + { + string unitNameLower = Ogre::ToLower(textureUnitName); + if (unitNameLower.find("normalmap") != string::npos) + textureType = aiTextureType_NORMALS; + else if (unitNameLower.find("specularmap") != string::npos) + textureType = aiTextureType_SPECULAR; + else if (unitNameLower.find("lightmap") != string::npos) + textureType = aiTextureType_LIGHTMAP; + else if (unitNameLower.find("displacementmap") != string::npos) + textureType = aiTextureType_DISPLACEMENT; + else + textureType = aiTextureType_DIFFUSE; + } + } + else if (linePart == partTextCoordSet) + { + ss >> uvCoord; + } + /// @todo Implement + else if(linePart == partColorOp) + { + /* + ss >> linePart; + if("replace"==linePart)//I don't think, assimp has something for this... + { + } + else if("modulate"==linePart) + { + //TODO: set value + //material->AddProperty(aiTextureOp_Multiply) + } + */ + } + } + + if (textureRef.empty()) + { + DefaultLogger::get()->warn("Texture reference is empty, ignoring texture_unit."); + return false; + } + if (textureType == aiTextureType_NONE) + { + DefaultLogger::get()->warn("Failed to detect texture type for '" + textureRef + "', ignoring texture_unit."); + return false; + } + + unsigned int textureTypeIndex = m_textures[textureType]; + m_textures[textureType]++; + + DefaultLogger::get()->debug(Formatter::format() << " texture '" << textureRef << "' type " << textureType + << " index " << textureTypeIndex << " UV " << uvCoord); + + aiString assimpTextureRef(textureRef); + material->AddProperty(&assimpTextureRef, AI_MATKEY_TEXTURE(textureType, textureTypeIndex)); + material->AddProperty(&uvCoord, 1, AI_MATKEY_UVWSRC(textureType, textureTypeIndex)); + + return true; +} + +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/code/OgreMesh.cpp b/code/OgreMesh.cpp index 5ca51e583..bd67d3add 100644 --- a/code/OgreMesh.cpp +++ b/code/OgreMesh.cpp @@ -442,15 +442,9 @@ void OgreImporter::ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry) //_________________________________________________________ } - - - -aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& submesh, const vector& bones) const +aiMesh* OgreImporter::CreateAssimpSubMesh(aiScene *pScene, const SubMesh& submesh, const vector& bones) const { - const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene - (void)m_CurrentScene; - - aiMesh* NewAiMesh=new aiMesh(); + aiMesh* NewAiMesh = new aiMesh(); //Positions NewAiMesh->mVertices=new aiVector3D[submesh.Positions.size()]; diff --git a/code/OgreSkeleton.cpp b/code/OgreSkeleton.cpp index 627c56ab8..f231cc465 100644 --- a/code/OgreSkeleton.cpp +++ b/code/OgreSkeleton.cpp @@ -52,41 +52,36 @@ namespace Assimp namespace Ogre { - - -void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vector &Animations) const +void OgreImporter::ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene, + const std::string &skeletonFile, vector &Bones, vector &Animations) const { - const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene - (void)m_CurrentScene; - - //most likely the skeleton file will only end with .skeleton //But this is a xml reader, so we need: .skeleton.xml - FileName+=".xml"; + string skeletonPath = skeletonFile + ".xml"; - DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName); + DefaultLogger::get()->debug(string("Loading Skeleton: ")+skeletonFile); //Open the File: - boost::scoped_ptr File(m_CurrentIOHandler->Open(FileName)); + boost::scoped_ptr File(pIOHandler->Open(skeletonFile)); if(NULL==File.get()) - throw DeadlyImportError("Failed to open skeleton file "+FileName+"."); + throw DeadlyImportError("Failed to open skeleton file "+skeletonFile+"."); //Read the Mesh File: boost::scoped_ptr mIOWrapper(new CIrrXML_IOStreamReader(File.get())); XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get()); if(!SkeletonFile) - throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName); + throw DeadlyImportError(string("Failed to create XML Reader for ")+skeletonFile); NextNode(SkeletonFile); if(string("skeleton")!=SkeletonFile->getNodeName()) - throw DeadlyImportError("No node in SkeletonFile: "+FileName); + throw DeadlyImportError("No node in SkeletonFile: "+skeletonFile); //------------------------------------load bones----------------------------------------- NextNode(SkeletonFile); if(string("bones")!=SkeletonFile->getNodeName()) - throw DeadlyImportError("No bones node in skeleton "+FileName); + throw DeadlyImportError("No bones node in skeleton "+skeletonFile); NextNode(SkeletonFile); @@ -138,7 +133,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto IdsOk=false; } if(!IdsOk) - throw DeadlyImportError("Bone Ids are not valid!"+FileName); + throw DeadlyImportError("Bone Ids are not valid!"+skeletonFile); } DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size())); //________________________________________________________________________________ @@ -150,7 +145,7 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto //----------------------------load bonehierarchy-------------------------------- if(string("bonehierarchy")!=SkeletonFile->getNodeName()) - throw DeadlyImportError("no bonehierarchy node in "+FileName); + throw DeadlyImportError("no bonehierarchy node in "+skeletonFile); DefaultLogger::get()->debug("loading bonehierarchy..."); NextNode(SkeletonFile); @@ -280,11 +275,11 @@ void OgreImporter::LoadSkeleton(std::string FileName, vector &Bones, vecto } -void OgreImporter::CreateAssimpSkeleton(const std::vector &Bones, const std::vector &/*Animations*/) +void OgreImporter::CreateAssimpSkeleton(aiScene *pScene, const std::vector &Bones, const std::vector &/*Animations*/) { - if(!m_CurrentScene->mRootNode) + if(!pScene->mRootNode) throw DeadlyImportError("No root node exists!!"); - if(0!=m_CurrentScene->mRootNode->mNumChildren) + if(0!=pScene->mRootNode->mNumChildren) throw DeadlyImportError("Root Node already has childnodes!"); @@ -295,26 +290,27 @@ void OgreImporter::CreateAssimpSkeleton(const std::vector &Bones, const st if(-1==theBone.ParentId) //the bone is a root bone { //which will recursily add all other nodes - RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode)); + RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, pScene->mRootNode)); } } if(RootBoneNodes.size() > 0) { - m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size(); - m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()]; - memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size()); + pScene->mRootNode->mNumChildren=RootBoneNodes.size(); + pScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()]; + memcpy(pScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size()); } } - -void OgreImporter::PutAnimationsInScene(const std::vector &Bones, const std::vector &Animations) +void OgreImporter::PutAnimationsInScene(aiScene *pScene, const std::vector &Bones, const std::vector &Animations) { - //-----------------Create the Assimp Animations -------------------- + // TODO: Auf nicht vorhandene Animationskeys achten! + // @todo Pay attention to non-existing animation Keys (google translated from above german comment) + if(Animations.size()>0)//Maybe the model had only a skeleton and no animations. (If it also has no skeleton, this function would'nt have been called { - m_CurrentScene->mNumAnimations=Animations.size(); - m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()]; + pScene->mNumAnimations=Animations.size(); + pScene->mAnimations=new aiAnimation*[Animations.size()]; for(unsigned int i=0; i &Bones, const st NewAnimation->mChannels[j]=NewNodeAnim; } - m_CurrentScene->mAnimations[i]=NewAnimation; + pScene->mAnimations[i]=NewAnimation; } } -//TODO: Auf nicht vorhandene Animationskeys achten! -//#pragma warning (s.o.) - //__________________________________________________________________ } diff --git a/code/OgreXmlHelper.hpp b/code/OgreXmlHelper.hpp index 74445b86d..79bcf0096 100644 --- a/code/OgreXmlHelper.hpp +++ b/code/OgreXmlHelper.hpp @@ -1,4 +1,5 @@ +#include "ParsingUtils.h" #include "irrXMLWrapper.h" #include "fast_atof.h" @@ -6,7 +7,7 @@ namespace Assimp { namespace Ogre { - + typedef irr::io::IrrXMLReader XmlReader; static void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "") @@ -88,5 +89,40 @@ inline bool CurrentNodeNameEquals(const XmlReader* reader, const std::string &na return (ASSIMP_stricmp(std::string(reader->getNodeName()), name) == 0); } +/// Returns a lower cased copy of @s. +static inline std::string ToLower(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + return s; +} + +// ------------------------------------------------------------------------------------------------ +// From http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring + +// trim from start +static inline std::string <rim(std::string &s, bool newlines = true) +{ + if (!newlines) + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(Assimp::IsSpace)))); + else + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(Assimp::IsSpaceOrNewLine)))); + return s; +} + +// trim from end +static inline std::string &rtrim(std::string &s, bool newlines = true) +{ + if (!newlines) + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(Assimp::IsSpace))).base(),s.end()); + else + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(Assimp::IsSpaceOrNewLine)))); + return s; +} +// trim from both ends +static inline std::string &trim(std::string &s, bool newlines = true) +{ + return ltrim(rtrim(s, newlines), newlines); +} + } // Ogre } // Assimp