فهرست منبع

OgreImporter: Remove unnecessary m_currentX state. Improve and clean OgreMaterial: split tech/pass/texture_unit to their own functions. Document missing features and potential bugs. Improve the original authors 'detection from texture filename' logic (enabled with AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME). Add generic detection from texture unit name, which is commonly used in Ogre materials.

Jonne Nauha 11 سال پیش
والد
کامیت
f98584cdea
6فایلهای تغییر یافته به همراه502 افزوده شده و 398 حذف شده
  1. 21 27
      code/OgreImporter.cpp
  2. 18 11
      code/OgreImporter.hpp
  3. 399 319
      code/OgreMaterial.cpp
  4. 2 8
      code/OgreMesh.cpp
  5. 25 32
      code/OgreSkeleton.cpp
  6. 37 1
      code/OgreXmlHelper.hpp

+ 21 - 27
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<Bone> Bones;
 	vector<Animation> Animations;
+
 	if (CurrentNodeNameEquals(reader, nnSkeletonLink))
 	{
-		string SkeletonFile = GetAttribute<string>(reader.get(), "name");
-		if (!SkeletonFile.empty())
-			LoadSkeleton(SkeletonFile, Bones, Animations);
+		string skeletonFile = GetAttribute<string>(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; i<materials.size(); ++i)
-		m_CurrentScene->mMaterials[i]=materials[i];
+		pScene->mMaterials[i]=materials[i];
 
 	//create the aiMehs... 
 	vector<aiMesh*> aiMeshes;
 	BOOST_FOREACH(boost::shared_ptr<SubMesh> 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; i<subMeshes.size(); ++i)
-		m_CurrentScene->mRootNode->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);
 }
 
 

+ 18 - 11
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<Bone>& bones) const;
+	aiMesh* CreateAssimpSubMesh(aiScene *pScene, const SubMesh &submesh, const std::vector<Bone>& 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<Bone> &Bones, std::vector<Animation> &Animations) const;
+	void ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene,
+					  const std::string &skeletonFile, std::vector<Bone> &Bones, std::vector<Animation> &Animations) const;
 
 	/// Converts the animations in aiAnimations and puts them into the scene.
-	void PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
+	void PutAnimationsInScene(aiScene *pScene, const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
 
 	/// Creates the aiSkeleton in current scene.
-	void CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
+	void CreateAssimpSkeleton(aiScene *pScene, const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
 
 	/// Recursivly creates a filled aiNode from a given root bone.
 	static aiNode* CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &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<aiTextureType, unsigned int> m_textures;
 };
 
 /// Simplified face.

+ 399 - 319
code/OgreMaterial.cpp

@@ -60,34 +60,35 @@ namespace Assimp
 namespace Ogre
 {
 
+static const string partComment    = "//";
+static const string partBlockStart = "{";
+static const string partBlockEnd   = "}";
 
+/// Skips a line from current @ss position until a newline. Returns the skipped part.
+std::string SkipLine(stringstream &ss)
+{
+	string skipped;
+	getline(ss, skipped);
+	return skipped;
+}
 
-aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const
+/// 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)
 {
-	/*For better understanding of the material parser, here is a material example file:
+	string skipped = SkipLine(ss);
+	ss >> nextElement;
+	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
-				}
-			}
-		}
-	}
+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<string> 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; i<potentialFiles.size(); ++i)
 		{
-			//the filename typically ends with .mesh or .mesh.xml
-			const string MaterialFileName=m_CurrentFilename.substr(0, m_CurrentFilename.rfind(".mesh"))+".material";
-
-			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();
-					}
-				}
-			}
+			materialFile = pIOHandler->Open(potentialFiles[i]);
+			if (materialFile)
+				break;
+			DefaultLogger::get()->debug(Formatter::format() << "Source file for material '" << materialName << "' " << potentialFiles[i] << " does not exist");
 		}
-		//Fill the stream
-		boost::scoped_ptr<IOStream> MaterialFile(MatFilePtr);
-		if(MaterialFile->FileSize()>0)
+		if (!materialFile)
 		{
-			vector<char> FileData(MaterialFile->FileSize());
-			MaterialFile->Read(&FileData[0], MaterialFile->FileSize(), 1);
-			BaseImporter::ConvertToUTF8(FileData);
-
-			FileData.push_back('\0');//terminate the string with zero, so that the ss can parse it correctly
-			ss << &FileData[0];
+			/// @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();
 		}
-		else
+
+		boost::scoped_ptr<IOStream> stream(materialFile);
+		if (stream->FileSize() == 0)
 		{
-			DefaultLogger::get()->warn("Material " + MaterialName + " seams to be empty");
-			return NULL;
+			/// @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();
 		}
+
+		// Read bytes
+		vector<char> 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)
+		{
+			string postComment = NextAfterNewLine(ss, linePart);
+			DefaultLogger::get()->debug("//" + postComment + " (comment line ignored)");			
+			continue;
+		}
+		if (linePart != partMaterial)
+		{
+			ss >> linePart;
+			continue;
+		}
+
+		ss >> linePart;
+		if (linePart != materialName)
+		{
+			//DefaultLogger::get()->debug(Formatter::format() << "Found material '" << linePart << "' that does not match at index " << ss.tellg());
+			ss >> linePart;
+			continue;
+		}
+
+		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 + "'");
+
+		while(linePart != partBlockEnd)
 		{
-			ss >> Line;
-			if(Line==MaterialName)//Load the next material
+			// Proceed to the first technique
+			ss >> linePart;
+					
+			if (linePart == partTechnique)
 			{
-				string RestOfLine;
-				getline(ss, RestOfLine);//ignore the rest of the line
-				ss >> Line;
+				string techniqueName = trim(SkipLine(ss));
+				ReadTechnique(techniqueName, ss, material);
+			}
 
-				if(Line!="{")
+			// 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")
 				{
-					DefaultLogger::get()->warn("empyt material!");
-					return NULL;
+					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);
 				}
 
-				while(Line!="}")//read until the end of the material
+				if(linePart=="$shininess_exponent")
 				{
-					//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
+					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));
+				}
+			}					
 		}
-		ss >> Line;
+		ss >> linePart;
 	}
 
-	return NewMaterial;
+	return material;
 }
 
-void OgreImporter::ReadTechnique(stringstream &ss, aiMaterial* NewMaterial) const
+bool OgreImporter::ReadTechnique(const std::string &techniqueName, stringstream &ss, aiMaterial *material)
 {
-	unsigned int CurrentDiffuseTextureId=0;
-	unsigned int CurrentSpecularTextureId=0;
-	unsigned int CurrentNormalTextureId=0;
-	unsigned int CurrentLightTextureId=0;
+	string linePart;
+	ss >> linePart;
+
+	if (linePart != partBlockStart)
+	{
+		DefaultLogger::get()->error(Formatter::format() << "Invalid material: Technique block start missing near index " << ss.tellg());
+		return false;
+	}
 
+	DefaultLogger::get()->debug(" technique '" + techniqueName + "'");
 
-	string RestOfLine;
-	getline(ss, RestOfLine);//ignore the rest of the line
+	const string partPass  = "pass";
 
-	string Line;
-	ss >> Line;
-	if(Line!="{")
+	while(linePart != partBlockEnd)
 	{
-		DefaultLogger::get()->warn("empty technique!");
-		return;
+		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;
 	}
-	while(Line!="}")//read until the end of the technique
+
+	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 >> Line;
-		if(Line=="pass")
+		ss >> linePart;
+		
+		// Skip commented lines
+		if (linePart == partComment)
 		{
-			getline(ss, RestOfLine);//ignore the rest of the line
+			string postComment = SkipLine(ss);
+			DefaultLogger::get()->debug("   //" + postComment + " (comment line ignored)");
+			continue;
+		}
 
-			ss >> Line;
-			if(Line!="{")
-			{
-				DefaultLogger::get()->warn("empty pass!");
-				return;
-			}
-			while(Line!="}")//read until the end of the pass
+		// 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)
 			{
-				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")
+				size_t posSuffix = textureRef.find_last_of(".");
+				size_t posUnderscore = textureRef.find_last_of("_");
+				
+				if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore)
 				{
-					getline(ss, RestOfLine);//ignore the rest of the line
-
-					std::string TextureName;
-					int TextureType=-1;
-					int UvSet=0;
-
-					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:
-					
-					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;
-					}
+					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;
 			}
-			Line="";//clear the } that would end the outer loop
 		}
-	}//end of technique
-}
+		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;
+}
 
-}//namespace Ogre
-}//namespace Assimp
+} // Ogre
+} // Assimp
 
-#endif  // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
+#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER

+ 2 - 8
code/OgreMesh.cpp

@@ -442,15 +442,9 @@ void OgreImporter::ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry)
 	//_________________________________________________________
 }
 
-
-
-
-aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& submesh, const vector<Bone>& bones) const
+aiMesh* OgreImporter::CreateAssimpSubMesh(aiScene *pScene, const SubMesh& submesh, const vector<Bone>& 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()];

+ 25 - 32
code/OgreSkeleton.cpp

@@ -52,41 +52,36 @@ namespace Assimp
 namespace Ogre
 {
 
-
-
-void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vector<Animation> &Animations) const
+void OgreImporter::ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene,
+							    const std::string &skeletonFile, vector<Bone> &Bones, vector<Animation> &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<IOStream> File(m_CurrentIOHandler->Open(FileName));
+	boost::scoped_ptr<IOStream> 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<CIrrXML_IOStreamReader> 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 <skeleton> node in SkeletonFile: "+FileName);
+		throw DeadlyImportError("No <skeleton> 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<Bone> &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<Bone> &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<Bone> &Bones, vecto
 }
 
 
-void OgreImporter::CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
+void OgreImporter::CreateAssimpSkeleton(aiScene *pScene, const std::vector<Bone> &Bones, const std::vector<Animation> &/*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<Bone> &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<Bone> &Bones, const std::vector<Animation> &Animations)
+void OgreImporter::PutAnimationsInScene(aiScene *pScene, const std::vector<Bone> &Bones, const std::vector<Animation> &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<Animations.size(); ++i)//create all animations
 		{
 			aiAnimation* NewAnimation=new aiAnimation();
@@ -382,12 +378,9 @@ void OgreImporter::PutAnimationsInScene(const std::vector<Bone> &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.)
-	//__________________________________________________________________
 }
 
 

+ 37 - 1
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 &ltrim(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<char>))));
+	else
+		s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(Assimp::IsSpaceOrNewLine<char>))));
+	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<char>))).base(),s.end());
+	else
+		s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(Assimp::IsSpaceOrNewLine<char>))));
+	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