When communicating between maxscript and .NET, how can I detect a type?

When communicating between maxscript and .NET, how can I detect a type?

Anonymous
Not applicable
2,409 Views
6 Replies
Message 1 of 7

When communicating between maxscript and .NET, how can I detect a type?

Anonymous
Not applicable

I am creating a library in C# that I wish to access through maxscript. I am still in the early stages of this, so one thing I would like to do is send objects to my library, inspect them in the C# code, and print their type, so I know which APIs to use one which objects.

 

For example, I came up with this:

 

    public static class TypeTranslator
    {
        public static string TypeOf(object obj) => obj.GetType().AssemblyQualifiedName;
    }

 

I build this DLL, and load the assembly in Max. Then from maxscript, I call it like this:

 

typeTranslator.TypeOf "hi"
typeTranslator.TypeOf 6

These return "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, ..." and "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, ..." as expected.

However, when I try to send it something like the selected editable mesh, or even a Point3 or #name from maxscript, I get this error: "-- Unable to convert: $GeoSphere001 to type: System.Object"

 

This has stumped me because I have not encountered objects that are not descended from System.Object before. I do have Autodesk.Max.dll imported into the C# solution as well. I've also tried Autodesk.Max.BaseObject as the parameter type, no go (Maxscript says there's no matching signature)

 

I'm aware of maxscript's "classof," but how can I inspect the type of an arbitrary object coming from Maxscript in my C# program? Indeed, how can I even accept an object of an unknown type as an argument to one of my C# methods?

0 Likes
Accepted solutions (1)
2,410 Views
6 Replies
Replies (6)
Message 2 of 7

Swordslayer
Advisor
Advisor
Accepted solution

@Anonymous wrote:

I'm aware of maxscript's "classof," but how can I inspect the type of an arbitrary object coming from Maxscript in my C# program? Indeed, how can I even accept an object of an unknown type as an argument to one of my C# methods?


You don't do that, you have to know what you're passing and based on that what to pass. There're multiple different ways how to pass stuff between the two worlds, for Animatables, you may pass the animHandle and use Animatable.GetAnimByHandle, you can pass node handle for Nodes, you can use refs.getAddress to pass MaxWrapper pointers, you can use semi-automatic pointer decay for a set of predefined value types (see the SDK samples for the full list, off the top of my mind there's BitArray, Point3, Box3, Mesh but the rules are pretty rigid and you can only have one such parameter per method since it relys on a hardcoded argument name). Technically, you can also create native maxscript primitives (functions) where you could query the value tag to see what it is (which would be closest to what you want, but it's a bit tricky....

0 Likes
Message 3 of 7

Anonymous
Not applicable

@Swordslayerthanks for your reply. It looks like I'm going to have to change approaches.

 

I have been looking through the Max SDK documents. From what I've read so far they seem aimed to making full plugins with class descriptors, DEF files, etc., rather than a simpler library that can be called from maxscript. That's an approach I'm hoping to avoid because we have a number of tools built on top of maxscript already, I just want to speed one of them up.

 

In brief, what I want to do is send an Editable Mesh into this method along with an array of point helpers, get the face center of each face in the mesh, build a k-D tree of the face centers, then detach clumps of faces to new objects based on which point helper they're closest to. I do this is already in maxscript, but it's slow. I have a C# k-D tree library ready to go and solving a similar problem in Unity, and I'm pretty confident about how to write the algorithm. Really the problem I'm still having is communicating between maxscript and this library.

 

I've looked at the SDK docs as you suggested, as well as discussions on these and other forums. But I haven't had much luck understanding how to map a type as returned from maxscript to whatever I'd need to use to handle it in the .NET API. The docs seem very vague on that point, suggesting using the Visual Studio object browser on the Autodesk.Max.dll file, which I have done. It's revealed interfaces like, for example, IMesh, which looks promising, although none called just "EditableMesh" or "TriMesh" or anything quite as obvious. There are also docs about individual types which may look relevant, but they don't specify whether they're valid for handling the objects I'm sending from maxscript, or how to make them so. So I came up with this, just to test:

 

        public static string MeshInfo(IMesh mesh)
        {
            return mesh.NumVerts.ToString();
        }

        public static string PointInfo(IPoint3 pt)
        {
            return pt.X.ToString();
        }

However, if I make an Editable Mesh object in max and send it into this method with this maxscript, I get an error:

typeTranslator.MeshInfo $
-- Runtime error: No 'MeshInfo' method found which matched argument list
typeTranslator.MeshInfo $.mesh
-- Runtime error: No 'MeshInfo' method found which matched argument list
typeTranslator.PointInfo $.min
-- Runtime error: No 'PointInfo' method found which matched argument list

So I am left with what I believe is a simple question, yet I'm getting hung up on it - how do I map types from 3ds max to types in the .NET SDK? Or, more directly, what should the parameter types be in my C# program to accept arguments from maxscript of types: 1) An editable mesh or its underlying TriMesh, 2) an array of point helpers, and 3) a Point3?

 

Thanks for any insight.

0 Likes
Message 4 of 7

Swordslayer
Advisor
Advisor
(
    local compilerParams = dotNetObject "System.CodeDom.Compiler.CompilerParameters" #(
        getDir #maxRoot + "Autodesk.Max.dll", "System.Linq.dll", "System.Core.dll",
        getDir #maxRoot + "\bin\assemblies\Autodesk.Max.Wrappers.dll")
    compilerParams.GenerateInMemory = on

    local compilerResults = (dotNetObject "Microsoft.CSharp.CSharpCodeProvider").CompileAssemblyFromSource compilerParams #(
        "using System;
		using System.Linq;
        using Autodesk.Max;
        using Wrappers = Autodesk.Max.Wrappers;

        class Sample {
			private static readonly IGlobal Global = Autodesk.Max.GlobalInterface.Instance;

            public static void PassStuff(IntPtr mesh_pointer, int[] helperHandles, IntPtr point3_pointer) { // variable names matter
                var mesh = (IMesh)Wrappers.CustomMarshalerMesh.GetInstance(string.Empty).MarshalNativeToManaged(mesh_pointer);
				var pt = (IPoint3)Wrappers.CustomMarshalerPoint3.GetInstance(string.Empty).MarshalNativeToManaged(point3_pointer);
				var helpers = helperHandles.Select(handle => Global.COREInterface14.GetINodeByHandle((uint)handle));
            }
        }"
    )

    for err = 0 to compilerResults.errors.count - 1 do print (compilerResults.errors.item[err].ToString())

    ::Sample = compilerResults.CompiledAssembly.CreateInstance "Sample"
)

Sample.PassStuff (createInstance Editable_mesh).mesh (for h in helpers collect h.iNode.handle) x_axis
Message 5 of 7

denisT.MaxDoctor
Advisor
Advisor

@Swordslayer wrote:

 

 

(
    local compilerParams = dotNetObject "System.CodeDom.Compiler.CompilerParameters" #(
        getDir #maxRoot + "Autodesk.Max.dll", "System.Linq.dll", "System.Core.dll",
        getDir #maxRoot + "\bin\assemblies\Autodesk.Max.Wrappers.dll")
    compilerParams.GenerateInMemory = on

    local compilerResults = (dotNetObject "Microsoft.CSharp.CSharpCodeProvider").CompileAssemblyFromSource compilerParams #(
        "using System;
		using System.Linq;
        using Autodesk.Max;
        using Wrappers = Autodesk.Max.Wrappers;

        class Sample {
			private static readonly IGlobal Global = Autodesk.Max.GlobalInterface.Instance;

            public static void PassStuff(IntPtr mesh_pointer, int[] helperHandles, IntPtr point3_pointer) { // variable names matter
                var mesh = (IMesh)Wrappers.CustomMarshalerMesh.GetInstance(string.Empty).MarshalNativeToManaged(mesh_pointer);
				var pt = (IPoint3)Wrappers.CustomMarshalerPoint3.GetInstance(string.Empty).MarshalNativeToManaged(point3_pointer);
				var helpers = helperHandles.Select(handle => Global.COREInterface14.GetINodeByHandle((uint)handle));
            }
        }"
    )

    for err = 0 to compilerResults.errors.count - 1 do print (compilerResults.errors.item[err].ToString())

    ::Sample = compilerResults.CompiledAssembly.CreateInstance "Sample"
)

Sample.PassStuff (createInstance Editable_mesh).mesh (for h in helpers collect h.iNode.handle) x_axis

 

 

 

// variable names matter


mesh_pointer, point3_pointer​ ...

Why these particular variable names? How can I know how to name the variables right ?! It doesn't make any sense !!!

0 Likes
Message 6 of 7

Swordslayer
Advisor
Advisor

@denisT.MaxDoctor wrote:

// variable names matter


mesh_pointer, point3_pointer​ ...

Why these particular variable names? How can I know how to name the variables right ?! It doesn't make any sense !!!


You can get the full list when you check the mxsdotNet SDK samples:

 

types.png

Message 7 of 7

denisT.MaxDoctor
Advisor
Advisor

My mind refuses to believe it ... 🤣

 

Thanks anyway!

 

PS. (@Swordslayer I thought I was going crazy until I found this post of yours)