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.

pull/266/head
Jonne Nauha 2014-05-01 16:33:15 +03:00
parent 6c51fa2072
commit f98584cdea
6 changed files with 520 additions and 416 deletions

View File

@ -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);
}

View File

@ -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.

View File

@ -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<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)
materialFile = pIOHandler->Open(potentialFiles[i]);
if (materialFile)
break;
DefaultLogger::get()->debug(Formatter::format() << "Source file for material '" << materialName << "' " << potentialFiles[i] << " does not exist");
}
if (!materialFile)
{
//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!");
/// @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();
}
}
}
}
//Fill the stream
boost::scoped_ptr<IOStream> MaterialFile(MatFilePtr);
if(MaterialFile->FileSize()>0)
boost::scoped_ptr<IOStream> stream(materialFile);
if (stream->FileSize() == 0)
{
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];
}
else
{
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();
}
//create the material
aiMaterial *NewMaterial=new aiMaterial();
// Read bytes
vector<char> data(stream->FileSize());
stream->Read(&data[0], stream->FileSize(), 1);
aiString ts(MaterialName.c_str());
NewMaterial->AddProperty(&ts, AI_MATKEY_NAME);
// 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 + "'");
aiMaterial *material = new aiMaterial();
m_textures.clear();
aiString ts(materialName);
material->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 postComment = NextAfterNewLine(ss, linePart);
DefaultLogger::get()->debug("//" + postComment + " (comment line ignored)");
continue;
}
if (linePart != partMaterial)
{
string RestOfLine;
getline(ss, RestOfLine);//ignore the rest of the line
ss >> Line;
if(Line!="{")
{
DefaultLogger::get()->warn("empyt material!");
return NULL;
ss >> linePart;
continue;
}
while(Line!="}")//read until the end of the material
ss >> linePart;
if (linePart != materialName)
{
//Proceed to the first technique
ss >> Line;
if(Line=="technique")
{
ReadTechnique(ss, NewMaterial);
//DefaultLogger::get()->debug(Formatter::format() << "Found material '" << linePart << "' that does not match at index " << ss.tellg());
ss >> linePart;
continue;
}
DefaultLogger::get()->info(Line);
//read informations from a custom material:
if(Line=="set")
NextAfterNewLine(ss, linePart);
if (linePart != partBlockStart)
{
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));
DefaultLogger::get()->error(Formatter::format() << "Invalid material: block start missing near index " << ss.tellg());
return material;
}
if(Line=="$shininess_strength")
DefaultLogger::get()->debug("material '" + materialName + "'");
while(linePart != partBlockEnd)
{
ss >> Line;
float Shininess=fast_atof(Line.c_str());
NewMaterial->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS_STRENGTH);
// Proceed to the first technique
ss >> linePart;
if (linePart == partTechnique)
{
string techniqueName = trim(SkipLine(ss));
ReadTechnique(techniqueName, ss, material);
}
if(Line=="$shininess_exponent")
// 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 >> Line;
float Shininess=fast_atof(Line.c_str());
NewMaterial->AddProperty(&Shininess, 1, AI_MATKEY_SHININESS);
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(Line=="$diffuse_map")
if(linePart=="$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));
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(Line=="$specular_map")
if(linePart=="$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));
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(Line=="$normal_map")
if(linePart=="$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));
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(Line=="$light_map")
if(linePart=="$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));
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 material
}
else {} //this is the wrong material, proceed the file until we reach the next material
}
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;
string RestOfLine;
getline(ss, RestOfLine);//ignore the rest of the line
string Line;
ss >> Line;
if(Line!="{")
if (linePart != partBlockStart)
{
DefaultLogger::get()->warn("empty technique!");
return;
DefaultLogger::get()->error(Formatter::format() << "Invalid material: Technique block start missing near index " << ss.tellg());
return false;
}
while(Line!="}")//read until the end of the technique
{
ss >> Line;
if(Line=="pass")
{
getline(ss, RestOfLine);//ignore the rest of the line
ss >> Line;
if(Line!="{")
DefaultLogger::get()->debug(" technique '" + techniqueName + "'");
const string partPass = "pass";
while(linePart != partBlockEnd)
{
DefaultLogger::get()->warn("empty pass!");
return;
ss >> linePart;
// Skip commented lines
if (linePart == partComment)
{
string postComment = SkipLine(ss);
DefaultLogger::get()->debug(" //" + postComment + " (comment line ignored)");
continue;
}
while(Line!="}")//read until the end of the pass
/// @todo Techniques have other attributes than just passes.
if (linePart == partPass)
{
ss >> Line;
if(Line=="ambient")
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)
{
float r,g,b;
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);
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
const aiColor3D color(r, g, b);
std::string TextureName;
int TextureType=-1;
int UvSet=0;
DefaultLogger::get()->debug(Formatter::format() << " " << linePart << " " << r << " " << g << " " << b);
ss >> Line;
if(Line!="{")
throw DeadlyImportError("empty texture unit!");
while(Line!="}")//read until the end of the texture_unit
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)
{
ss >> Line;
if(Line=="texture")
{
ss >> Line;
TextureName=Line;
string textureUnitName = trim(SkipLine(ss));
ReadTextureUnit(textureUnitName, ss, material);
}
}
return true;
}
if(m_TextureTypeFromFilename)
bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstream &ss, aiMaterial *material)
{
string linePart;
ss >> linePart;
if (linePart != partBlockStart)
{
if(Line.find("_n.")!=string::npos)// Normalmap
{
TextureType=aiTextureType_NORMALS;
DefaultLogger::get()->error(Formatter::format() << "Invalid material: Texture unit block start missing near index " << ss.tellg());
return false;
}
else if(Line.find("_s.")!=string::npos)// Specularmap
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)
{
TextureType=aiTextureType_SPECULAR;
}
else if(Line.find("_l.")!=string::npos)// Lightmap
ss >> linePart;
// Skip commented lines
if (linePart == partComment)
{
TextureType=aiTextureType_LIGHTMAP;
string postComment = SkipLine(ss);
DefaultLogger::get()->debug(" //" + postComment + " (comment line ignored)");
continue;
}
else// colormap
if (linePart == partTexture)
{
TextureType=aiTextureType_DIFFUSE;
}
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
{
TextureType=aiTextureType_DIFFUSE;
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(Line=="tex_coord_set")
else if (linePart == partTextCoordSet)
{
ss >> UvSet;
ss >> uvCoord;
}
else if(Line=="colour_op")//TODO implement this
/// @todo Implement
else if(linePart == partColorOp)
{
/*
ss >> Line;
if("replace"==Line)//I don't think, assimp has something for this...
ss >> linePart;
if("replace"==linePart)//I don't think, assimp has something for this...
{
}
else if("modulate"==Line)
else if("modulate"==linePart)
{
//TODO: set value
//NewMaterial->AddProperty(aiTextureOp_Multiply)
//material->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)
if (textureRef.empty())
{
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;
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;
}
}
Line="";//clear the } that would end the outer loop
}
}//end of technique
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
}//namespace Ogre
}//namespace Assimp
#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER

View File

@ -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()];

View File

@ -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.)
//__________________________________________________________________
}

View File

@ -1,4 +1,5 @@
#include "ParsingUtils.h"
#include "irrXMLWrapper.h"
#include "fast_atof.h"
@ -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