|
很久都想在Ogre下自己独立开发一些东西,Ogre虽然很强大,但是对数据接口方面很弱,虽然Ogre Team也做了一些事情,比如在用Ogre开发的Max插件,但是用起来却不怎么爽,今天把老外写的Ogre的Max插件Down下来看了一下,发现他们写的太粗糙,模型虽然导出来了,但是很多纹理都是错的,特别对多重纹理的导出根本就没处理。没办法,只好自己写了。
Ogre1.4.9 Max导出插件总结:
(1):打开OgreMaxExporter,把工程设置好,把Ogre的库和3DMax的库加入的工程中去。
(2):最好把输出目录放在Max的plugins文件夹下面,这样就不必拷来拷去了。
(3):编译通过,运行3DMax就可以看到在Export下面多了一个输出的文件类型。可以导出一个导出类型OGRE 3D Exporter.xml, .mesh, .material, .skeleton。
(4):导出后,你会发现很多xml文件,打开就会看到模型的顶点,纹理,材质等信息。
(5):用OgreXMLConverter.exe转成.mesh文件,如果xml文件很多,可以用批处理命令:
for %i in (F:\fujian\*.xml) do OgreXMLConverter %i .要确保OgreXMLConverter 已经注册到环境变量中,如果没有,那只能用全路径名代替。for %i in (F:\fujian\*.xml) do E:\ogre\OgreXMLConverter.exe %i;
(6):把转成的mesh和material 文件拷贝到Media文件中去,转成的mesh文件就可以直接被Ogre应用程序用了。
代码修改:
(1)必须明确如何划分submesh. 在max中,是没有submesh这个概念的,对于一个复合的物体,比如凳子由一个长方体和四个圆柱体,从ogre的角度我们可以认为这个凳子是由5个submesh组成。而max没有这样的区分,他把所有的长方体和四个圆柱体的顶点信息都放在一个数组中,所以我们必须从这些顶点中区分那些顶点属于长方体,那些顶点属于柱体。其实我们可以用Max的submaterial来区分,主观的认为具有相同材质的顶点在同一个submesh上。认识到这一点,我们代码的修改就变的容易了。
(2)修改bool MeshXMLExporter::export(OutputMap& output)函数:如下
bool MeshXMLExporter::export(OutputMap& output)
{
try
{
std::stringstream of;
while (!m_submeshNames.empty())
m_submeshNames.pop();
if (m_pGame)
{
m_pGame->ReleaseIGame();
m_pGame = 0;
}
m_ei->theScene->EnumTree(this);
m_pGame = GetIGameInterface();
IGameConversionManager* cm = GetConversionManager();
cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);
m_pGame->InitialiseIGame(m_nodeTab, false);
m_pGame->SetStaticFrame(0);
int nodeCount = m_pGame->GetTopLevelNodeCount();
if (nodeCount == 0)
{
MessageBox(GetActiveWindow(),
"No nodes available to export, aborting...",
"Nothing To Export", MB_ICONINFORMATION);
m_pGame->ReleaseIGame();
return false;
}
std::string fileName;
IGameNode* node = m_pGame->GetTopLevelNode(0);
if (!m_config.getExportMultipleFiles())
fileName = m_config.getExportFilename();
else
{
fileName = m_config.getExportPath() + "\\";
fileName += node->GetName();
fileName += ".mesh.xml";
}
// write start of XML data
streamFileHeader(of);
int nodeIdx = 0;
std::map<std::string, std::string> materialScripts;
while (nodeIdx < nodeCount)
{
std::string mtlName;
IGameNode* node = m_pGame->GetTopLevelNode(nodeIdx);
IGameObject* obj = node->GetIGameObject();
// InitializeData() is important -- it performs all of the WSM/time eval for us; no data without it
obj->InitializeData();
IGameMaterial* mtl = node->GetNodeMaterial();
/************************************************************************/
/* 武汉大学遥感信息工程学院S091班赵龙 2010-3-14 */
/************************************************************************/
int numMeshes = 1;
bool isMultiMesh = false;
//如果该材质是烘焙出来的,之间用烘焙后的材质,将原来的材质丢掉
if(mtl && mtl->GetMaxMaterial()->ClassID()==Class_ID(BAKE_SHELL_CLASS_ID, 0))
{
mtl=mtl->GetSubMaterial(1);
}
//如果是该材质是由多个材质混合而成,则记录有多少个材质混合
if (mtl && mtl->GetMaxMaterial()->ClassID() == Class_ID(MULTI_CLASS_ID, 0))
{
isMultiMesh = TRUE;
//根据numMeshes来确定有多少个submesh
numMeshes = mtl->GetSubMaterialCount();
}
/************************************************************************/
/* 武汉大学遥感信息工程学院S091班赵龙 2010-3-14 */
/************************************************************************/
exportSubmeshByMtl(materialScripts,mtl,of,obj,numMeshes,isMultiMesh);
//////////////////////////////////////////////////////////////////////////
node->ReleaseIGameObject();
nodeIdx++;
//写下一个.xml文件
if (m_config.getExportMultipleFiles() || nodeIdx == nodeCount)
{
// write end of this file's XML
streamFileFooter(of);
// insert new filename --> stream pair into output map
output[fileName] = std::string(of.str());
of.str("");
if (nodeIdx != nodeCount)
{
fileName = m_config.getExportPath() + "\\";
node = m_pGame->GetTopLevelNode(nodeIdx);
fileName += node->GetName();
fileName += ".mesh.xml";
// start over again with new data
streamFileHeader(of);
}
}
}
m_pGame->ReleaseIGame();
// export materials if we are writing those
if (m_config.getExportMaterial())
{
std: fstream materialFile;
materialFile.open((m_config.getExportPath() + "\\" +
m_config.getMaterialFilename()).c_str(), std::ios::out);
if (materialFile.is_open())
{
for (std::map<std::string, std::string>::iterator it = materialScripts.begin();
it != materialScripts.end(); ++it)
{
materialFile << it->second;
}
materialFile.close();
}
}
return true;
}
catch (...)
{
MessageBox(GetActiveWindow(),
"An unexpected error has occurred while trying to export, aborting", "Error", MB_ICONEXCLAMATION);
if (m_pGame)
m_pGame->ReleaseIGame();
return false;
}
}
(3)添加函数:
void MeshXMLExporter::exportSubmeshByMtl(std::map<std::string, std::string>& materialScripts,
IGameMaterial* mtl,std::stringstream& of,IGameObject* obj,
int numMeshes, bool isMultiMesh)
该函数是根据Material生成submesh
void MeshXMLExporter::exportSubmeshByMtl(std::map<std::string, std::string>& materialScripts,
IGameMaterial* mtl,std::stringstream& of,IGameObject* obj,
int numMeshes, bool isMultiMesh)
{
if(isMultiMesh) //对多重纹理的处理
{
long i;
for(i = 0; i < numMeshes; i++)
{
IGameMaterial* subMtl = mtl->GetSubMaterial(i);
std::string mtlName = subMtl->GetMaterialName();
//去掉空格
std::string::size_type pos;
while ((pos = mtlName.find_first_of(' ')) != std::string::npos)
mtlName.replace(pos, 1, _T("_"));
long reName = 0;
std::string tempName = mtlName;
//因为ogre的材质名称是不能重复的,而Max的材质名可以重复
//所以如果遇到有相同的材质名,就生成一个不同的材质名代替
while (materialScripts.find(tempName) != materialScripts.end())
{
std::stringstream stream;
stream.width(0);
stream.fill(' ');
stream << reName;
tempName = mtlName + stream.str();
reName++;
}
mtlName = tempName;
//根据材质的ID来判断那些顶点属于那个submesh
if (streamSubmeshID(of, obj, mtlName,mtl->GetMaterialID(i)))
{
m_submeshNames.push(std::string(mtlName));
if (materialScripts.find(mtlName) == materialScripts.end())
{
// export new material script
MaterialExporter mexp(m_config, m_materialMap);
std::string script;
mexp.buildMaterial(subMtl, mtlName, script);
materialScripts[mtlName] = script;
}
}
}
}
else//单个纹理 ,从原来的代码中拷贝过来的。原来的代码也有问题
{
std::string::size_type pos;
if(mtl == NULL)//这个地方有问题,没有材质的物体导不出来
{
return;
}
std::string mtlName = mtl->GetMaterialName();
//去掉空格
while ((pos = mtlName.find_first_of(' ')) != std::string::npos)
mtlName.replace(pos, 1, _T("_"));
if (materialScripts.find(mtlName) == materialScripts.end())
{
// export new material script
MaterialExporter mexp(m_config, m_materialMap);
std::string script;
mexp.buildMaterial(mtl, mtlName, script);
materialScripts[mtlName] = script;
}
if (streamSubmesh(of, obj, mtlName))
m_submeshNames.push(std::string(mtlName));
}
}
(4) 仿照streamSubmesh写了一个streamSubmeshID()这个比较长但是改的代码不多,这里就不代码全贴出来了
改的第一处是:thisFaceCount来代替faceCount;faceCount是指整个Node节点face的个数
thisFaceCount是当前submesh拥有的face的个数。
for (i = 0; i < faceCount; i++)
{
if(mesh->GetFaceMaterialID(i) == ID)
{
thisFaceCount ++;
}
}
//如果face的个数为0就没有必要建立一个submesh
if(thisFaceCount == 0)
{
return false;
}
//顶点的个数
int vertCount = thisFaceCount*3;
改的第二处是在for (i =0; i<faceCount; i++)处,紧跟括号下面:
//不属于该submesh的face全部Pass掉
if(mesh->GetFaceMaterialID(i) != ID)
{
continue;
}
(5)修改Material的导出:由于一个模型不只是有一个纹理坐标,所以要为每一个texture指定纹理坐标
在MaterialExporter::streamPass的函数中
of << "\n\t\t\ttexture_unit " << std::endl;
of << "\t\t\t{" << std::endl;
std::string bmap(tmap->GetBitmapFileName());
bmap = bmap.substr(bmap.find_last_of('\\') + 1);
long k = tmap->GetMapChannel();//这里是我改的
if(k > texMapIdx)//这里是我改的
k = texMapIdx;//这里是我改的
of << "\t\t\t\ttex_coord_set " << k << std::endl;//这里是我改的
of << "\t\t\t\ttexture " << bmap << std::endl;
of << "\t\t\t}" << std::endl;
好了看看效果吧
|
|