【Ogre引擎架构】 第五讲 从零开始编写3dsMax导出插件

来源:互联网 发布:沙钢网络管理学院 编辑:程序博客网 时间:2024/05/29 18:37

     对于3D开发的新生来说,写一写导出插件还是很有必要的,Ogre作为开源界的资深元老自然给出了导出插件OgreMax,通过导出插件的实现过程,我们可以更好的理解模型文件的结构,更好的使用引擎进行解析并渲染。本文作者将通过OgreMax的实现原理和方式进行插件的编写实现,接下来下载一款3ds Max2010,安装过程可能会遇到麻烦,尽力克服困难,安装成功后,下载对应的3ds Max2010对应的MaxSDK,简要说明一下MaxSDK的作用,通过这一套API你才能和3ds Max进行沟通,它就像一座桥梁,负责获取max里面的模型相关的数据,传递给你的C++程序,在你自己的dll里调用MaxSDK的相关接口API获得你需要的数据,并写到一个文件里,导出也就完成了。你的DLL必须遵守MaxSDK指定的标准,否则3ds Max将无法识别你的插件。

     我们将通过一个具体实例导的出过程,分析导出的流程,作者首先在max里面制作了一个模型,暂且不去评价这个模型的审美价值,下图所示,

模型导出后的文件:一个模型Xml文件Cube_Tomo.mesh.xml,一个材质文件Model.material,一张材质贴图Box01-lowNormalsMap.tga(关于模型Xml文件的结构请参考第一讲第二讲)


将模型应用于程序中,效果图如下:


     

    下面开始程序部分的解析:

第一步:根据要求配置开发环境,具体参见博文 http://blog.csdn.net/monster_2495/article/details/7485447。

第二步:作者采用的是VS2008,创建Win32 DLL,在DllEntry.cpp里面添加Max SDK所需的标准接口

extern "C"{// This function returns a string that describes the DLL and where the user// could purchase the DLL if they don't have it.__declspec( dllexport ) const TCHAR* LibDescription(){return GetString(IDS_LIBDESCRIPTION);}// This function returns the number of plug-in classes this DLL//TODO: Must change this number when adding a new class__declspec( dllexport ) int LibNumberClasses(){return 1;}// This function returns the number of plug-in classes this DLL__declspec( dllexport ) ClassDesc* LibClassDesc(int i){switch(i) {case 0: return GetTomoMaxDesc();default: return 0;}}// This function returns a pre-defined constant indicating the version of // the system under which it was compiled.  It is used to allow the system// to catch obsolete DLLs.__declspec( dllexport ) ULONG LibVersion(){return VERSION_3DSMAX;}// This function is called once, right after your plugin has been loaded by 3ds Max. // Perform one-time plugin initialization in this method.// Return TRUE if you deem your plugin successfully loaded, or FALSE otherwise. If // the function returns FALSE, the system will NOT load the plugin, it will then call FreeLibrary// on your DLL, and send you a message.__declspec( dllexport ) int LibInitialize(void){return TRUE; // TODO: Perform initialization here.}// This function is called once, just before the plugin is unloaded. // Perform one-time plugin un-initialization in this method."// The system doesn't pay attention to a return value.__declspec( dllexport ) int LibShutdown(void){return TRUE;// TODO: Perform un-initialization here.}// Let the plug-in register itself for deferred loading/*__declspec( dllexport ) ULONG CanAutoDefer(){return 1;}*/}TCHAR *GetString(int id){static TCHAR buf[256];if (hInstance)return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL;return NULL;}
  至于每个接口的含义自行查看英文注释并理解,只要按照作者所实现并导出的接口,max就能识别你的插件。

第三步:实现第二步里的

__declspec( dllexport ) ClassDesc* LibClassDesc(int i)
这个函数用来导出一个核心类TomoMaxClassDesc,在GetTomoMaxDesc函数里面定义,ClassDesc2是MaxSDK的接口类

#include "TomoMaxExport.h"#include <fstream>#define TomoMax_CLASS_IDClass_ID(0x70f8cb0e, 0x44aab292)class TomoMaxClassDesc : public ClassDesc2 {public:virtual int IsPublic() { return TRUE; }virtual void* Create(BOOL /*loading = FALSE*/) { return new TomoMaxExport(); }virtual const TCHAR *ClassName() { return GetString(IDS_CLASS_NAME); }virtual SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }virtual Class_ID ClassID() { return TomoMax_CLASS_ID; }virtual const TCHAR* Category() { return GetString(IDS_CATEGORY); }virtual const TCHAR* InternalName() { return _T("TomoMaxExport"); }// returns fixed parsable name (scripter-visible name)virtual HINSTANCE HInstance() { return hInstance; }// returns owning module handle};ClassDesc2* GetTomoMaxDesc() { static TomoMaxClassDesc TomoMaxDesc;return &TomoMaxDesc; }INT_PTR CALLBACK TomoMaxOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {static TomoMaxExport *imp = NULL;switch(message) {case WM_INITDIALOG:imp = (TomoMaxExport *)lParam;CenterWindow(hWnd,GetParent(hWnd));return TRUE;case WM_CLOSE:EndDialog(hWnd, 0);return 1;}return 0;}//--- TomoMax -------------------------------------------------------TomoMaxExport::TomoMaxExport():m_meshXMLExporter(m_config,m_materialMap){//}TomoMaxExport::~TomoMaxExport() {}int TomoMaxExport::ExtCount(){//#pragma message(TODO("Returns the number of file name extensions supported by the plug-in."))return 1;}const TCHAR *TomoMaxExport::Ext(int n){//#pragma message(TODO("Return the 'i-th' file name extension (i.e. \"3DS\")."))switch (n) {case 0:return _T("xml");break;case 1:return _T("mesh");break;case 2:return _T("skeleton");break;case 3:return _T("material");break;default:return 0;break;}}const TCHAR *TomoMaxExport::LongDesc(){//#pragma message(TODO("Return long ASCII description (i.e. \"Targa 2.0 Image File\")"))return _T("Tomo 3D Mesh/Animation/Material Exporter");}const TCHAR *TomoMaxExport::ShortDesc() {//#pragma message(TODO("Return short ASCII description (i.e. \"Targa\")"))return _T("Tomo 3D Exporter");}const TCHAR *TomoMaxExport::AuthorName(){//#pragma message(TODO("Return ASCII Author name"))return _T("Tomo Engine - Jack");}const TCHAR *TomoMaxExport::CopyrightMessage() {//#pragma message(TODO("Return ASCII Copyright message"))return _T("Tomo Tech Inc");}const TCHAR *TomoMaxExport::OtherMessage1() {//TODO: Return Other message #1 if anyreturn _T("");}const TCHAR *TomoMaxExport::OtherMessage2() {//TODO: Return other message #2 in anyreturn _T("");}unsigned int TomoMaxExport::Version(){//#pragma message(TODO("Return Version number * 100 (i.e. v3.01 = 301)"))return 100;}void TomoMaxExport::ShowAbout(HWND hWnd){// Optional}BOOL TomoMaxExport::SupportsOptions(int ext, DWORD options){//#pragma message(TODO("Decide which options to support.  Simply return true for each option supported by each Extension the exporter supports."))return TRUE;}intTomoMaxExport::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options){//#pragma message(TODO("Implement the actual file Export here and"))/*if(!suppressPrompts)DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PANEL), GetActiveWindow(), TomoMaxOptionsDlgProc, (LPARAM)this);*/m_config.SetExportFilename(name);m_meshXMLExporter.SetMaxInterface(ei,i);if(_export())return TRUE;//#pragma message(TODO("return TRUE If the file is exported properly"))return FALSE;}bool TomoMaxExport::_export(){TomoMax::MeshXMLExporter::OutputMap output;if(m_meshXMLExporter.BuildMeshXML(output)){////write to fileTomoMax::MeshXMLExporter::OutputMap::iterator it = output.begin();while(it != output.end()){std::ofstream of;of.open(it->first.c_str(),std::ios::out);of<<it->second;of.close();++it;}}return true;}
所有的导出操作,将调用DoExport这个接口,所以你需要重点关注这个函数,在这里面获取模型数据,并写入文件。模型Mesh的导出解析类:

头文件

class MeshXMLExporter:public TomoMaxExporter,public ITreeEnumProc{public:MeshXMLExporter(const Config& config,MaterialMap& matMap);virtual ~MeshXMLExporter();typedef map<string,string> OutputMap;bool BuildMeshXML(OutputMap& output);protected:int callback(INode* node);bool StreamFileHeader(std::ostream& of);bool StreamFileFooter(std::ostream& of);bool StreamSubmesh(std::ostream& of,IGameObject* go,string& mtlName);queue<string> m_submeshNames;IGameScene* m_pGameScene;Tab<INode*> m_nodeTab;MaterialMap& m_materialMap;};
cpp文件

#include "TomoMaxMeshXMLExporter.h"#include "TomoMaxConfig.h"#include "IGame/IGame.h"#include "IGame/IConversionManager.h"#include "IGame/IGameObject.h"#include "TomoMaxVertex.h"#include "TomoMaxMaterialExporter.h"#include <fstream>namespace TomoMax{MeshXMLExporter::MeshXMLExporter(const Config& config,MaterialMap& matMap):TomoMaxExporter(config),m_pGameScene(0),m_materialMap(matMap){//}MeshXMLExporter::~MeshXMLExporter(){//}bool MeshXMLExporter::BuildMeshXML(OutputMap& output){if(!m_submeshNames.empty())m_submeshNames.pop();//write XML to a strstreamm_ei->theScene->EnumTree(this);if(m_pGameScene){m_pGameScene->ReleaseIGame();m_pGameScene = 0;}m_pGameScene = GetIGameInterface();IGameConversionManager* cm = GetConversionManager();cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);m_pGameScene->InitialiseIGame(m_nodeTab,false);m_pGameScene->SetStaticFrame(0);int nodeCount = m_pGameScene->GetTopLevelNodeCount();if(0 == nodeCount){MessageBox(GetActiveWindow(), "No nodes available to export, aborting...", "Nothing To Export", MB_ICONINFORMATION);m_pGameScene->ReleaseIGame();return false;}// if we are writing everything to one file, use the name provided when the user first started the export;// otherwise, create filenames on the basis of the node (submesh) namesIGameNode* node = m_pGameScene->GetTopLevelNode(0);string filename;if(m_config.GetExportMultipleFiles()){filename = m_config.GetExportPath()+"\\";filename += node->GetName();filename += ".mesh.xml";}else{filename = m_config.GetExportBasename();filename += ".mesh.xml";}node->ReleaseIGameObject();std::stringstream of;StreamFileHeader(of);int nodeIdx = 0;std::map<string,string> materialScripts;while(nodeIdx < nodeCount){//string mtlName;node = m_pGameScene->GetTopLevelNode(nodeIdx);IGameObject* go = node->GetIGameObject();go->InitializeData();IGameMaterial* mtl = node->GetNodeMaterial();if(0 == mtl)mtlName = "";elsemtlName = mtl->GetMaterialName();//materialif(mtlName.size() && materialScripts.find(mtlName) == materialScripts.end()){MaterialExporter mexp(m_config,m_materialMap);std::string script;mexp.BuildMaterial(mtl,mtlName,script);materialScripts[mtlName] = script;}if(StreamSubmesh(of,go,mtlName))m_submeshNames.push(string(node->GetName()));node->ReleaseIGameObject();++nodeIdx;if(m_config.GetExportMultipleFiles() || nodeIdx == nodeCount){StreamFileFooter(of);output[filename] = string(of.str());}}if(m_config.GetExportMaterial()){std::ofstream materialFile;materialFile.open((m_config.GetExportPath()+"\\"+m_config.GetMaterialName()).c_str(),std::ios::out);if(materialFile.is_open()){for(std::map<string,string>::iterator it=materialScripts.begin();it!=materialScripts.end();++it){materialFile << it->second;}materialFile.close();}}return true;}bool MeshXMLExporter::StreamFileHeader(std::ostream& of){of << "<mesh>"<<std::endl;of << "\t<submeshes>"<<std::endl;of.precision(6);of << std::fixed;return true;}bool MeshXMLExporter::StreamFileFooter(std::ostream& of){of << "\t</submeshes>"<<std::endl;//of << "</mesh>"<<std::endl;return true;}bool MeshXMLExporter::StreamSubmesh(std::ostream& of,IGameObject* go,string& mtlName){if(go->GetIGameType() != IGameMesh::IGAME_MESH)return false;IGameMesh* mesh = (IGameMesh*)go;int vertCount = mesh->GetNumberOfVerts();int faceCount = mesh->GetNumberOfFaces();Tab<int> texMaps = mesh->GetActiveMapChannelNum();//of << "\t\t<submesh ";if(mtlName.length()>0)of << "material=\""<<mtlName<<"\" ";of << "usesharedvertices=\"false\" use32bitindexes=\"";of << ((vertCount > 65535)?"true":"false");of << "\">" << std::endl;// *************** Export Face List ***************VertexList vertList;of << "\t\t\t<faces count=\"" << faceCount << "\">" << std::endl;for(int i=0;i<faceCount;++i){of<< "\t\t\t\t<face";FaceEx* face = mesh->GetFace(i);for(int vi=0;vi<3;++vi){Point3 pt = mesh->GetVertex(face->vert[vi]);Vertex v(pt.x,pt.y,pt.z);if(m_config.GetExportVertexColors()){Point3 c = mesh->GetColorVertex(face->vert[vi]);float a = mesh->GetAlphaVertex(face->vert[vi]);v.SetColors(c.x,c.y,c.z,a);}Point3 n = mesh->GetNormal(face->vert[vi]);v.SetNormal(n.x,n.y,n.z);for(int ch=0;ch<texMaps.Count();++ch){Point3 tv;DWORD indices[3]={0};if(mesh->GetMapFaceIndex(texMaps[ch],i,indices))tv = mesh->GetMapVertex(texMaps[ch],indices[vi]);elsetv = mesh->GetMapVertex(texMaps[ch],face->vert[vi]);v.AddTexCoord(texMaps[ch],tv.x,tv.y,tv.z);}int actualVertexIndex = vertList.Add(v);of<<" v"<<vi+1<<"=\""<<actualVertexIndex<<"\"";}of<< "/>"<<std::endl;}VertexList vertList2 = vertList;of << "\t\t\t</faces>"<<std::endl;// *************** End Export Face List ***************// *************** Export Geometry ***************of << "\t\t\t<geometry vertexcount=\""<<vertList.Size()<<"\">"<<std::endl;// *************** Export VertexBuffer ***************bool exportNormals = true;of << "\t\t\t\t<vertexbuffer positions=\"true\" normals=\""<<(exportNormals?"true":"false")<<"\"";of << ">"<<std::endl;int numVerts = vertList.Size();for(int i=0;i<numVerts;++i){const Vertex& vert = vertList.Front();of << "\t\t\t\t\t<vertex>"<<std::endl;const Tomo::Vector3& pos = vert.GetPosition();of << "\t\t\t\t\t\t<position x=\""<<pos.x<<"\" y=\""<<pos.y<<"\" z=\""<<pos.z<<"\"/>"<<std::endl;const Tomo::Vector3& normal = vert.GetNormal();of << "\t\t\t\t\t\t<normal x=\""<<normal.x<<"\" y=\""<<normal.y<<"\" z=\""<<normal.z<<"\"/>"<<std::endl;of << "\t\t\t\t\t</vertex>"<<std::endl;vertList.Pop();}of << "\t\t\t\t</vertexbuffer>"<<std::endl;// texture coordinatesof << "\t\t\t\t<vertexbuffer ";for(int i=0;i<1;++i){of <<"texture_coord_dimensions_"<<i<<"=\"2\" ";}of << "texture_coords=\""<<1<<"\"";of << ">"<<std::endl;for(int i=0;i<numVerts;++i){const Vertex& vert = vertList2.Front();of << "\t\t\t\t\t<vertex>"<<std::endl;int ch = texMaps.Count()-1;//for(int ch=0;ch<texMaps.Count();++ch){const Tomo::Vector3& uvw = vert.GetUVW(texMaps[ch]);of << "\t\t\t\t\t\t<texcoord u=\""<<uvw.x<<"\""<<" v=\""<<1.f-uvw.y<<"\"/>"<<std::endl;//}of << "\t\t\t\t\t</vertex>"<<std::endl;vertList2.Pop();}of << "\t\t\t\t</vertexbuffer>"<<std::endl;// *************** End Export VertexBuffer ***************of << "\t\t\t</geometry>"<<std::endl;// *************** End Export Geometry ***************of << "\t\t</submesh>"<<std::endl;return true;}// "callback" is called in response to the EnumTree() call made below. That call visits every node in the // scene and calls this procedure for each one. int MeshXMLExporter::callback(INode* node){if(m_config.GetExportSelected()){if(node->Selected())m_nodeTab.Append(1,&node);}else{m_nodeTab.Append(1,&node);}return TREE_CONTINUE;}}
重点关注callback函数和StreamSubmesh函数,具体的数据获取和写入

第四步:导出材质,类MaterialExporter

typedef std::map<std::string,IGameMaterial*> MaterialMap;class MaterialExporter:public TomoMaxExporter{public:MaterialExporter(const Config& config,MaterialMap& matMap);virtual ~MaterialExporter();bool BuildMaterial(IGameMaterial* mtl,const std::string& matName,std::string& script);private:MaterialMap& m_materialMap;bool StreamPass(std::ostream& of,IGameMaterial* mtl);};
#include "TomoMaxMaterialExporter.h"#include <IGame/IGame.h>#include <sstream>namespace TomoMax{MaterialExporter::MaterialExporter(const Config& config,MaterialMap& matMap):TomoMaxExporter(config),m_materialMap(matMap){//}MaterialExporter::~MaterialExporter(){//}bool MaterialExporter::StreamPass(std::ostream& of,IGameMaterial* mtl){of << "\t\tpass"<<std::endl;of << "\t\t{"<<std::endl;//texture unitint numTexMaps = mtl->GetNumberOfTextureMaps();for(int texMapIdx=0;texMapIdx < numTexMaps;++texMapIdx){IGameTextureMap* texMap = mtl->GetIGameTextureMap(texMapIdx);if(texMap){of << "\t\t\ttexture_unit"<<std::endl;of << "\t\t\t{"<<std::endl;std::string texName = texMap->GetBitmapFileName();texName = texName.substr(texName.find_last_of('\\')+1);of << "\t\t\t\ttexture "<<texName<<std::endl;of << "\t\t\t}"<<std::endl;}}of << "\t\t}"<<std::endl;return true;}bool MaterialExporter::BuildMaterial(IGameMaterial* mtl,const std::string& matName,std::string& script){std::stringstream of;of << "material "<<matName<<std::endl;of << std::showpoint;of << "{"<<std::endl;of << "\ttechnique"<<std::endl;  of << "\t{"<<std::endl;  int numSubMtl=0;  if(mtl != NULL){  numSubMtl = mtl->GetSubMaterialCount();  if(numSubMtl > 0){  for(int i=0;i<numSubMtl;++i){  //  }  }else{  StreamPass(of,mtl);  }  }else{  //  }  of << "\t}"<<std::endl;of << "}"<<std::endl;script = of.str();return true;}}
第五步: 查看最终结果,打开Cube_Tomo.mesh.xml和材质Model.material





第六步:下载本文所需的 max文件model.max 百度云盘:http://pan.baidu.com/s/1mgL5r1Q

第七步: 将编译生成的.dll,改下扩展名.dle放入 max的安装路径下的stdplugs文件夹下,打开max点击export菜单,在格式下拉列表里选择自己的插件定义的格式,点击导出即可



如果本文可以给你带来实际的学习价值,请尊重作者的劳动成果

          

0 0
原创粉丝点击