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

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


3DS Max plugin 编程五,第一行代码_第1张图片


从图中的对话框内容可以看出,我们这个对话框是在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        @4
SECTIONS

.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存在的库)。

你可能感兴趣的:(3DS Max plugin 编程五,第一行代码)