• 3dsmax插件开发新手上路


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

     点击可看原帖地址

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

    至此一个基本的插件程序框架就搭建起来了,剩下的就是要遍历场景节点,实现你自己的数据导出功能了
    (完)
  • 相关阅读:
    计网第一章——基本概念
    计网第二章——应用层
    命令行测试邮件发送工具mailsend-go
    CentOS-7-x86_64-DVD-2009 rpm包列表(centos7.9)
    CentOS-7-x86_64-Everything-2009 rpm包列表(CentOS7.9)
    Centos发行版ISO镜像中rpm包列表
    nginx使用记录
    centos resolv.conf
    python cookbook
    ansible中变量和主机名
  • 原文地址:https://www.cnblogs.com/lai3d/p/1611618.html
Copyright © 2020-2023  润新知