| 
 | 
 
很久都想在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; 
 
好了看看效果吧 
 |   
 
 
 
 |