Lesson 7: Writing .Net Plug-ins
 
 
 

3ds Max uses its integrated .NET assembly loaders at run time to load any valid .Net assembly that implements a 3ds Max plug-in. Such assembly DLL files should be placed in [3ds Max installation folder]\bin\assemblies. There are different DLL libraries providing necessary API for your .NET plug-ins to use the 3ds Max SDK. Two important DLL library for this purpose are MaxCustomControl.dll and Autodesk.max.dll. The former library provides an interface for developers to write Custom Action Items, while the latter one provides a comprehensive library to to make almost all the features of 3ds Max SDK accessible for the developers.

To write .Net plug-ins, you can create a new C# class library project by selecting New > Other Languages > Visual C# > Windows > Class Library in Visual Studio. Once the new project is created, you can select Add Reference from the Project menu and browse for MaxCustomControl.dll and / or Autodesk.Max.dll in the 3ds Max installation folder.

Writing .Net Action plug-ins using MaxCustomControll.dll

In order to write .NET Action plug-ins using MaxCustomControll.dll your plug-in class should implement the CuiActionCommandAdapter. Using C# in Visual Studio, you will have the option to right click on the interface name and select "Implement Abstract Class" for the list of functions and properties in the base class to be automatically populated. Doing so for CuiActionCommandAdapterThe will populate four properties and one function for you. The properties provide basic information about your CUI action plug-in. The only function is named Execute() and implements the actual functionality of your plug-in.

Any plug-in class extended from CuiActionCommandAdapter can be independantly compiled and loaded in 3ds Max. We will see later that it is not the case for plug-ins implementing the interfaces from Autodesk.Max.dll. This code provides an example of a simple .Net Action plug-in that prompts a message in a pop up window.

Writing .Net plug-ins using Autodesk.max.dll

Plug-in developers can use a complete list of interfaces defined in Autodesk.max.dll to implement any type of plug-in they need. This is similar to extending from a 3ds Max base class for a C++ plug-in. There is almost a 1 to 1 mapping between 3ds Max SDK plug-ins and the interfaces available in Autodesk.Max.dll. We will create a utility plug-in as a basic example. At the beginning, your code will look like this:

using System;
using Autodesk.Max;

namespace firstDotNet
{
    public class utilityDotNet : Autodesk.Max.Plugins.UtilityObj
    {        
    }
}

Using visual studio's "Implement Abstract Class" option for this class that implements Autodesk.Max.Plugins.UtilityObj will populate two functions for you to override, BeginEditParams() and EndEditParams(). These two functions are explained in the lesson 1 of the 3ds Max SDK learning path. Similar to the example there, we just get this utility plug-in to prompt some text to the prompt area of 3ds Max. The following code shows how your code will look like after this step:

using System;
using Autodesk.Max;

namespace firstDotNet
{
    public class utilityDotNet : Autodesk.Max.Plugins.UtilityObj
    {
        public override void BeginEditParams(IInterface ip, IIUtil iu)
        {
            ip.PushPrompt("Hello Dot Net!");
        }
        public override void EndEditParams(IInterface ip, IIUtil iu)
        {
            ip.PopPrompt();
        }
    }
}

.Net Plug-ins still need class descriptors similar to the unmanaged (C++) plug-ins. Class descriptors are explained in Getting Started Writing Plug-ins. You can define your own class descriptor that extends from Autodesk.Max.Plugins.ClassDesc2. The descriptor class returns an object of your plug-in class in its Create() function. You can right click on the class descriptor declaration name and select Implement Abstract Class so the list of the function you have to implement gets populated. Project Lesson1b.net contains a .NET utility plug-in that uses the Autodesk.max.dll assembly to prompt a message in a pop up window.

You do not need to explicitly define a constructor for your plug-in class since your call in Descriptor::Create() does not use any argument. Your plug-in project can be compiled at this point but 3ds Max still cannot recognize it even after placing the .dll file in [3ds max installation folder]\bin\assemblies and starting 3ds Max. As explained in .NET Assembly Loader you will need to register your plug-in in 3ds Max by exposing a public static function named AssemblyMain(). You can implement this function in any public class in your plug-in's namespace, including the main plug-in class and the class descriptor. We create a new public class to implement this and other assembly functions for more clarity. Similar to AssemblyMain(), the public static function AssemblyShutdown() is used to perform the required tasks (if any) when your plug-in is being terminated. Adding the following public class to the namespace will make your plug-in be loaded by the .NET Assembly Loader when 3ds Max is starting up:

public static class AssemblyFunctions
    {
        public static void AssemblyMain()
        {
            var g = Autodesk.Max.GlobalInterface.Instance;
            var i = g.COREInterface13;
            i.AddClass(new Descriptor(g));
        }

        public static void AssemblyShutdown()
        {
        }
    }

The .NET project Lesson7.zip provides different examples to use the Autodesk.Max.dll assembly along with CUI action items to create .NET plug-ins.

Interfaces instead of Classes, Properties instead of Functions

We can see from the above code that 3ds Max includes a handle to its global interface in its call to the BeginEditParams(). We use that interface handle to call the PushPrompt() function. Was it not the case, we could still obtain 3ds Max global interface by defining our own IGlobal Interface as follows:

IGlobal global = Autodesk.Max.GlobalInterface.Instance;
Interface13 Interface = global.COREInterface13;
global.PushPrompt("...");
The equivalent code in 3ds Max SDK will be:
Interface* ip = GetCOREInterface();
ip->PushPrompt("...");

By comparing the two pieces of code above, we can observe two differences that can be generalized as two rules of thumb:

  • COREInterface13 is a 'property' of Global interface, while its equivalent in 3ds Max SDK, GetCOREInterface() is a global 'function'. In general, all the accessor functions in the 3ds Max SDK whose name start with 'Get' and have no argument have equvalent properties in Autodesk.Max.dll.
  • The type of the parameter ip in the .Net version is an 'interface' named IInterface, in oppose to the type of equivalent parameter in 3ds Max SDK where the type is a pointer to a 'class' named Interface. In Autodesk.Max.dll almost every class has a corresponding interface which is used instead where the class might be expected as an argument type or return type. Remember that "if a method requires an interface as an argument, then any object that implements that interface can be used in the argument".