您的位置:首页 > 其它

3dsmax插件开发新手上路

2012-07-27 22:09 246 查看
感谢老狼同学的这篇文章。

点击可看原帖地址


[原创]3dsmax插件开发新手上路

作者:老狼

email: cgwolver@163.com

QQ:4197680024

我尽量把话题说的通俗易懂一点,因为要做插件的可能不只是程序员:)

所谓插件,其实就是动态链接库,windows 系统上就是dll,打开Autodesk\3ds Max 9\stdplugs文件夹,会看到下面一大堆扩展名叫dlu,dlo,等等的文件。其实这都是dll改的名字,本质还是dll。当宿主程序运行起来的时候,会加载这个文件夹下的所有dlxx扩展名的文件。因此你可以用任何创建dll的方法来生成它,比如用Win32 DLL Project,或者MFC
DLL Project;我本人更喜欢用MFC DLL Project,可以方便的用MFC的功能;在3dsmax插件中使用MFC DLL Project ,有一些需要注意的问题,将在后文叙述.

如果你想快速写插件,通常使用 maxsdk提供的 vc wizard 最容易创建一个特定的插件框架程序.这里简单说一下设置 3dsmax vc wizard 的方法:

安装了3dsmax product以后,还要安装maxsdk,sdk和大量的sample在dvd完整安装版本上,PluginWizard 的路径在Autodesk\3ds Max 2009 SDK\maxsdk\howto\3dsmaxPluginWizard下面,里面有readme.txt文件,大意是这样的:用文本编辑器打开3dsmaxPluginWizard.vsz,我装的是3dsmax2009,打开这个vsz文件的内容如下:

VSWIZARD 7.0

Wizard=VsWizard.VsWizardEngine.8.0

Param="WIZARD_NAME = 3dsmaxPluginWizard"

Param="ABSOLUTE_PATH = C:\dev\p4\gouda\3dswin\src\maxsdk\howto\3dsmaxPluginWizard"

Param="FALLBACK_LCID = 1033"

把ABSOLUTE_PATH 的路径改为 D:\Autodesk\3ds Max 2009 SDK\maxsdk\howto\3dsmaxPluginWizard,就是你复制文件的原路径

这个时候用VC8 新建 Project ,选择VC++,会看到一个 3ds max plugin Wizard 列表项,然后按照提示一步一步操作就能创建一个基本的插件框架.

这里我要多说一点的是用自定义dll project方法创建3dsmax plugins的方法,我本人不太喜欢用现成的东西,凡事总喜欢自己亲手做来的东西,这样理解的也比较深刻透彻

3dsmax插件,有几个标准的可导出函数,3dsmax.exe装载插件时,用GetProcAddress API找出预定的几个标准接口,然后调用一个LibClassDesc的接口,创建出用户自定义的对象实例,类似于"抽象厂"方法

3dsmax 插件中必须定义的标准导出函数:

#define DLLEXPORT_API __declspec(dllexport)

//插件描述

DLLEXPORT_API const TCHAR* LibDescription()

{

LoadString( IDS_LIBDESCRIPTION );

}

//这个dll中有几个插件

DLLEXPORT_API int LibNumberClasses()

{

return 1;

}

//取得插件描述块,3dsmax.exe用它来创建你的插件类的实例

DLLEXPORT_API ClassDesc* LibClassDesc(int i)

{

switch(i)

{

case 0: return GetMyClassDesc();

default: return 0;

}

}

//LibVersion用来匹配插件和宿主3dsmax.exe之间的版本匹配问题.

DLLEXPORT_API ULONG LibVersion()

{

return VERSION_3DSMAX;

}

关于如何在VC中创建DLL Project,不需要多说了,要说的一点就是导出函数必须用 _declspec(dllexport) 修饰,另外还需要在def文件中列出导出函数的名字如下:

假定一个导出插件的文件名是SampleExporter

//SampleExporter.def

LIBRARY SampleExporter

EXPORTS

LibDescription @1

LibNumberClasses @2

LibClassDesc @3

LibVersion @4

SECTIONS

.data READ WRITE

下面着重说GetMyClassDesc() 这个函数...

#define SampleExporter_CLASS_ID Class_ID(0xc2a1ee34, 0x832cc295) //这个Class_ID用Autodesk\3ds Max 2009 SDK\maxsdk\help\getcid.exe 自己生成,只要不存在冲突就可以.

//SampleExporter 是从SceneExport 继承的导出插件类,SceneExport是sdk预定义的用于实现导出插件的基类,用户写导出插件,只需要继承它,然后实现相应的纯虚接口即可.

//导入插件基于SceneExport,辅助插件基于UtilityObj,还有创建面板上的,详见3ds Max SDK Programmer'sGuide中的Type of Plug-Ins 介绍.

//函数DoExport是3dsmax.exe和导出插件程序交互的主要接口,选择File/Export菜单,然后选择SampleExporter后,将会进入DoExport函数...

class SampleExporter : public SceneExport {

public:

static HWND hParams;



int ExtCount(); // Number of extensions supported

const TCHAR * Ext(int n); // Extension #n (i.e. "3DS")

const TCHAR * LongDesc(); // Long ASCII description (i.e. "Autodesk 3D Studio File")

const TCHAR * ShortDesc(); // Short ASCII description (i.e. "3D Studio")

const TCHAR * AuthorName(); // ASCII Author name

const TCHAR * CopyrightMessage(); // ASCII Copyright message

const TCHAR * OtherMessage1(); // Other message #1

const TCHAR * OtherMessage2(); // Other message #2

unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301)

void ShowAbout(HWND hWnd); // Show DLL's "About..." box

BOOL SupportsOptions(int ext, DWORD options);

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

//Constructor/Destructor

SampleExporter ();

~SampleExporter ();

};

//3dsmax.exe如何与用户自定义的插件建立起联系,就是通过下面这个ClassDesc类来获得创建插件对象实例所需要的必要的信息

class SampleExporterClassDesc: public ClassDesc2

{

public:

virtual int IsPublic() { return TRUE; }

virtual void* Create(BOOL /*loading = FALSE*/) { return new SampleExporter (); }//这里其实就是"抽象厂"方法,让用户提供一个实例

virtual const TCHAR * ClassName() { return GetString(IDS_CLASS_NAME); }

virtual SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }

virtual Class_ID ClassID() { return SampleExporter_CLASS_ID; }

virtual const TCHAR* Category() { return GetString(IDS_CATEGORY); }

virtual const TCHAR* InternalName() { return _T("SampleExporter"); } // returns fixed parsable name (scripter-visible name)

virtual HINSTANCE HInstance() { return hInstance; } // returns owning module handle

};

static SampleExporterClassDesc expoertDesc;

ClassDesc2* GetMyClassDesc() { return &expoertDesc; }

//下面是SampleExporter类的实现代码

SampleExporter::SampleExporter()

{

}

SampleExporter::~SampleExporter()

{

}

int SampleExporter::ExtCount()

{

#pragma message(TODO("Returns the number of file name extensions supported by the plug-in."))

return 1;

}

const TCHAR *SampleExporter::Ext(int n)

{

#pragma message(TODO("Return the 'i-th' file name extension (i.e. \"3DS\")."))

return _T("");

}

const TCHAR *SampleExporter::LongDesc()

{

#pragma message(TODO("Return long ASCII description (i.e. \"Targa 2.0 Image File\")"))

return _T("");

}



const TCHAR *SampleExporter::ShortDesc()

{

#pragma message(TODO("Return short ASCII description (i.e. \"Targa\")"))

return _T("");

}

const TCHAR *SampleExporter::AuthorName()

{

#pragma message(TODO("Return ASCII Author name"))

return _T("");

}

const TCHAR *SampleExporter::CopyrightMessage()

{

#pragma message(TODO("Return ASCII Copyright message"))

return _T("");

}

const TCHAR *SampleExporter::OtherMessage1()

{

//TODO: Return Other message #1 if any

return _T("");

}

const TCHAR *SampleExporter::OtherMessage2()

{

//TODO: Return other message #2 in any

return _T("");

}

unsigned int SampleExporter::Version()

{

#pragma message(TODO("Return Version number * 100 (i.e. v3.01 = 301)"))

return 100;

}

void SampleExporter::ShowAbout(HWND hWnd)

{

// Optional

}

BOOL SampleExporter::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;

}

//这里是真正的导出函数入口,用户选择导出后,将执行到这里

int SampleExporter::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(),

maxProject1OptionsDlgProc, (LPARAM)this);

#pragma message(TODO("return TRUE If the file is exported properly"))

return FALSE;

}

至此一个基本的插件程序框架就搭建起来了,剩下的就是要遍历场景节点,实现你自己的数据导出功能了

(完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: