3DS Max plugin 编程五,第一行代码

来源:互联网 发布:什么是web软件开发 编辑:程序博客网 时间:2024/06/06 05:27

这个篇章结束,我们能自己写一个*.dle插件,这个插件的最后结果很简单,弹出一个对话框,如下图:




从图中的对话框内容可以看出,我们这个对话框是在DoExport()中被Max引擎给调用到了。文章中,我们会从一个空的工程开始一点一点加入代码,直到最后上面的结果,整个过程能让我们实践一个插件的开发过程,当然,只是万里长征的第一步。


整个工程最后的文件清单如下:

1,dllmain.h, dllmain.cpp

2,exporterdesc.h, exporterdesc.cpp

3,resource.h

4,OgreExporter.h, OgreExporter.cpp

5,exporter.def


4里面用到Ogre开头的名称,是因为我们后续可以导出成Ogre识别的XML格式,这个格式可以在Ogre DTD文档中看到,对大家都比较方便。最后我们也可以用Ogre知道我们导出的有没有问题。


现在就让我们开始:


准备、用VC新建win32 project,创建一个DLL, 记住勾选empty project,我们会在写插件的过程中不停的手工加入需要的文件。


1、在新项目中添加new item,类型选择.cpp,加入第一个文件,dllmain.h,内容如下:

#ifndef DLLMAIN_H#define DLLMAIN_H#include <windows.h>#include "resource.h"#include "max.h"extern HINSTANCE hInstance;extern TCHAR *GetString(int id);#endif

这个头文件主要是包含了resource.h, windows.h, max.h 3个重要的头文件,准备做为一个基础给其他的C++文件包含用。


2、新加入dllmain.cpp,相关的代码如下,建议这个文件可以作为以后的模板文件,Copy-Paste代码的方式在这里完全是有意义的。


#include "dllmain.h"#include "exporterdesc.h"HINSTANCE hInstance;static ExporterDesc gExporterDescInst;BOOL WINAPI DllMain (HINSTANCE hInst, ULONG ulReason, LPVOID lpvReserved){    hInstance = hInst;    switch(ulReason)     {    case DLL_PROCESS_ATTACH:        if (FAILED(CoInitialize(NULL)))            return FALSE;        break;    case DLL_THREAD_ATTACH:        break;    case DLL_THREAD_DETACH:        break;    case DLL_PROCESS_DETACH:        break;    }    return(TRUE);}//------------------------------------------------__declspec( dllexport ) const TCHAR *LibDescription() {     return _T("ogre exporter"); }__declspec( dllexport ) int LibNumberClasses() {     return 1; }__declspec( dllexport ) ClassDesc *LibClassDesc(int i) {    switch(i)     {    case 0:         return &gExporterDescInst;         break;    default:         return 0;     break;    }}__declspec( dllexport ) ULONG LibVersion() {     return VERSION_3DSMAX; }__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;} 


需要特别注意的是,LibClassDesc()这个DLL入口实现,可以返回一个ClassDesc的指针,这里通常我们会在插件编写中让它返回一个全局的静态变量。


3、新加入文件,这次是.DEF文件。


LIBRARY    "exporter"EXPORTS    LibDescription    @1    LibNumberClasses  @2    LibClassDesc      @3    LibVersion        @4SECTIONS.data READ WRITE


这个文件定义了整个插件能导出的方法名称。


4、dllmain中引出了一个exporterdesc.h,所以我们新加入文件,名称即exporterdesc.h,文件中内容如下:

#include "iparamb2.h"class ExporterDesc : public ClassDesc2{public:    ExporterDesc();    virtual ~ExporterDesc();    int IsPublic();    void * Create(BOOL loading = FALSE);    const TCHAR * ClassName();    SClass_ID SuperClassID();    Class_ID ClassID();    const TCHAR* Category();};


这个类必须继承自ClassDesc2(或者ClassDesc,ClassDesc2是前者的继承类),并实现上述的接口。在Max启动的过程中,扫描到我们的插件的时候,DllMain()是重要的入口,因为是动态链接库强制的。而这里的Create(),则是Max引擎强制的,会被Max引擎用来创建插件主体的一个实例。


5、实现上面类的是exporterdesc.cpp中,我们要实现这些方法,如下:

#include "dllmain.h"#include "exporterdesc.h"#include "OgreExporter.h"ExporterDesc::ExporterDesc(){}ExporterDesc::~ExporterDesc(){}int ExporterDesc::IsPublic(){    return true;}const TCHAR* ExporterDesc::Category(){    return GetString(IDS_STRCATEGORY);}Class_ID ExporterDesc::ClassID(){    return Class_ID(0x73bd1636, 0x22f05f49);}const TCHAR* ExporterDesc::ClassName(){    return GetString(IDS_STRCLASSNAME);}void* ExporterDesc::Create(BOOL loading){    OgreExporter* pExporter = new OgreExporter(hInstance);     return pExporter;}SClass_ID ExporterDesc::SuperClassID(){    return SCENE_EXPORT_CLASS_ID;}


我们的Create()方法创建了一个OgreExporter实例。所以,我们需要来加入OgreMaxExport这个类。这个类也是导出插件的真正主体。前面一切的一切都是因为Max引擎或者说是SDK需要我们那么做,为了让Max引擎能跟我们的插件做配合才做的工作。而这个被创建的OgreExporter才是我们真正的主角。

6、新加入OgreExporter.h文件,内容如下:

#ifndef OGREEXPORTER_H#define OGREEXPORTER_H#include <windows.h>#include "resource.h"#include "max.h"#include "impexp.h"#include "dllmain.h"class OgreExporter : public SceneExport{public:OgreExporter(HINSTANCE hInst) : m_hInstance(hInst) { }virtual ~OgreExporter ();public:    virtual int ExtCount();    virtual const MCHAR* Ext(int n);    virtual const MCHAR* LongDesc();    virtual const MCHAR* ShortDesc();    virtual const MCHAR* AuthorName();    virtual const MCHAR* CopyrightMessage();    virtual const MCHAR* OtherMessage1();    virtual const MCHAR* OtherMessage2();    virtual unsigned int Version();    virtual void  ShowAbout(HWND hWnd);    virtual int DoExport(const MCHAR* name,ExpInterface* ei,Interface* i,BOOL suppressPrompts=FALSE, DWORD options=0);private:HINSTANCE m_hInstance;};#endif

对于导出插件来讲(3DS Max中,导出插件是其中一个类型),我们需要继承SceneExport,

并实现基类的几个函数,列表如下:

virtual int ExtCount();
virtual const MCHAR* Ext(int n);
virtual const MCHAR* LongDesc();
virtual const MCHAR* ShortDesc();
virtual const MCHAR* AuthorName();
virtual const MCHAR* CopyrightMessage();
virtual const MCHAR* OtherMessage1();
virtual const MCHAR* OtherMessage2();
virtual unsigned int Version();
virtual void  ShowAbout(HWND hWnd);

virtual int DoExport(const MCHAR* name,ExpInterface* ei,Interface* i,BOOL suppressPrompts=FALSE, DWORD options=0);

在SDK中,可以看到这些方法SDK有标准[Pure Virtual],也就是“纯虚函数”,所以,有这个标注的方法就必须自己来实现实体。


7,实现的文件是新加入的文件,OgreExpoter.cpp。

#include "OgreExporter.h"OgreExporter::~OgreExporter(){}int OgreExporter::DoExport(const MCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options){    MessageBox (NULL, "DoExport called by Max engine", "Max plug-in", MB_OK);        return 1;}const MCHAR* OgreExporter::AuthorName(){    return GetString(IDS_STRAUTHORNAME);}const MCHAR* OgreExporter::CopyrightMessage(){    return GetString(IDS_STRCOPYRIGHT);}const MCHAR* OgreExporter::Ext(int n){    switch (n)    {    case 0:return _T("xml");break;    default:return _T("");    }}int OgreExporter::ExtCount(){    return 1;}const MCHAR* OgreExporter::LongDesc(){    return GetString(IDS_STRLONGDESC); }const MCHAR* OgreExporter::OtherMessage1(){    return GetString(IDS_STROTHER1);}const MCHAR* OgreExporter::OtherMessage2(){    return GetString(IDS_STROTHER2);}const MCHAR* OgreExporter::ShortDesc(){    return GetString(IDS_STRSHORTDESC); }void OgreExporter::ShowAbout(HWND hWnd){    }unsigned int OgreExporter::Version(){    return 100;}


如我在前面的文章中(http://blog.csdn.net/tinyhum/article/details/6918540)提过的,上述的Ext方法会被Max引擎迭代用来找到该插件支持的后缀,DoExport则是真正的主体,在用户选好导出的文件名字并按下保存后就会被Max引擎呼叫到,想做什么事情,主要在这里做文章。


因此,我们后续的事情,都可以由DoExport()起,至DoExport()终。唯一的一点,你现在完全进入了Win32编程了,你可以在Win32的范畴和Max SDK的范畴,尽情发挥。


弹出的对话框我们是直接调用Win32的MessageBox()来进行的,你也可以在VC中自己制作好Dialog模板来进行调用。我们的GetString()利用了VC的resource view来进行字符串调用。


上述7个文件,还不能编译成功整个工程,如果要编译过,你必须在工程中加入resource,并把GetString()用到的那些ID全部实现。


编译的过程中,项目的属性页上,头文件包含目录必须有max sdk的include路径;链接目录上,必须有max sdk的lib路径;输入的库文件中,需要有comctl32.lib core.lib geom.lib gfx.lib helpsys.lib maxutil.lib mesh.lib paramblk2.lib这些库,特别是最后一个库是我们目前一定需要的(ClassDesc2存在的库)。