感谢老狼同学的这篇文章。

点击可看原帖地址

[原创]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;
}

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

3dsmax插件开发新手上路相关推荐

  1. Windows内核新手上路1——挂钩SSDT

    Windows内核新手上路1--挂钩SSDT 这个系列记录学习我学习windows内核的点点滴滴,高手请直接无视. 文章核心内容:挂钩SSDT中函数列NtOpenProcess,NtDuplicate ...

  2. Windows内核新手上路3——挂钩KeUserModeCallBack

    Windows内核新手上路3--挂钩KeUserModeCallBack 1.     简介 在Windows系统中,提供了几种方式从R0调用位于R3的函数,其中一种方式是KeUserModeCall ...

  3. Windows内核新手上路2——挂钩shadow SSDT

    Windows内核新手上路2--挂钩shadow SSDT 文章核心内容:安全软件窗口保护.安全输入.截屏保护的一些思路.挂钩NtUserFindWindowEx.NtUserGetForegroun ...

  4. Linux新手上路命令

    Linux新手上路命令 命令:www.ahlinux.com Ctrl+Alt+F1  进入命令界面 Ctrl+Alt+F7  返回图形界面 root用户和普通用户切换 administrator 2 ...

  5. 千寻和省cors精度对比_测量员新手上路攻略:解析省CORS和千寻CORS账号区别及其如何选择运用...

    原标题:测量员新手上路攻略:解析省CORS和千寻CORS账号区别及其如何选择运用 说到CORS.CORS账号,想必大家都不陌生,CORS-连续运行卫星定位服务系统,现在多代指网络RTK,是在传统RTK ...

  6. python必备基础代码-新手上路必学的Python函数基础知识,全在这里了(多段代码举例)...

    原标题:新手上路必学的Python函数基础知识,全在这里了(多段代码举例) 导读:函数是Python中最重要.最基础的代码组织和代码复用方式.根据经验,如果你需要多次重复相同或类似的代码,就非常值得写 ...

  7. 软件项目经理新手上路16 - 后记,一切才刚刚开始

    如果你内向,同时觉得自己够聪明,就去做软件开发人员吧.这是我当时为什么从事软件行业的原因.而且,我发现不是我一个人这么想,中国内向的聪明人实在是太多了.这些人都认为,相对于面对人而言,他们更善于面对电 ...

  8. 固定字符结尾的正则_新手上路:图文解读助你理解和使用正则表达式

    选自janmeppe.com 作者:Jan Meppe 机器之心编译 参与:韩放.杜伟 这篇博客是关于正则表达式(regex)的插图指南,旨在为那些从来没有使用过正则表达式,想尝试但又望而生畏的新手提 ...

  9. HanLP《自然语言处理入门》笔记--1.新手上路

    文章目录 1. 新手上路 1.1 自然语言与编程语言的比较 1.2 自然语言处理的层次 1.3 自然语言处理的流派 1.4 机器学习 1.5 语料库 1.6 开源工具 1.7 总结 1.8 GitHu ...

最新文章

  1. Tensorflow【实战Google深度学习框架】—完整的TensorFlow入门教程
  2. php 新闻列表,php原生开发新闻站之新闻列表(二)
  3. php haystack,haystack(示例代码)
  4. Default process group has not been initialized, please make sure to call init_process_group
  5. linux cut 命令详解
  6. 对口本科计算机专业,对口高考计算机类专业综合试题(卷).doc
  7. miui linux桌面图标,小米主题编辑器(miui主题编辑工具)下载了最新的正式版v5.3.23...
  8. 研发/项目计划管理表格
  9. iPadOS、iOS13降级iOS12.3.1方法
  10. PX4 mixer load
  11. 光照 (4) 漫反射光照
  12. c语言kill暂停和恢复进程,Linux暂停和恢复进程
  13. SpringMVC 配置定时执行任务
  14. Python小课们是如何赚钱的
  15. 免费文本转语音(在线文本转语音)
  16. 任正非:为什么华为选择与西工大合作,而没选清华北大
  17. 基于 MaxCompute+PAI 的用户增长方案实践
  18. 直播、录播、录视频等
  19. 本地创建git仓库并提交到码云
  20. mmc驱动框架基础介绍

热门文章

  1. TypeScript创建泛型类报错(泛型类)
  2. 人工神经网络反向传播,神经网络后向传播
  3. 网传分享的Wordpressripro主题4.8版本后门分析_盾给网下载修复文件[建站教程]
  4. 物流中转站---最短距离和
  5. 大学生数学竞赛教程【蒲和平】
  6. 论文写作课程心得和总结
  7. Element-UI中调用tinymce6实现本地化加载,并解决提示:This domain is not registered with TinyMCE Cloud,省去api-key
  8. 收集的卡通动漫模型手办之01
  9. 侠盗猎车手:圣安地列斯作弊码
  10. English语法_地点副词