Convert DotNet objects to MaxScript


#1

Hi all.

I found this weekend this thread form @sinokgr and I decided to play with:
http://forums.cgsociety.org/showthread.php?t=1138801

I’ve managed to get from C# a “mesh object” (dotNetObject:Autodesk.Max.Wrappers.Mesh) to MaxScript. But I’m not able to asign it to a node with MaxScript. I suppose I have to convert it to a “true” mesh.

Here are the codes:

C# code (nearly the same as the mentioned thread):

using Autodesk.Max;

namespace exportMesh
{
    static public class Class1
    {
        static IGlobal global = GlobalInterface.Instance;

        static private ITriObject GetTriObjectFromNode(IINode node)
        {
            IObject obj = node.EvalWorldState(0, true).Obj;
            IClass_ID myClass = global.Class_ID.Create(9, 0);

            if (obj.CanConvertToType(myClass) == 1)
            {
                ITriObject tri = (ITriObject)obj.ConvertToType(0, myClass);
                return tri;
            }
            else
            {
                return null;
            }
        }


//  Function that returns a modified mesh

        static public IMesh exportMesh(uint handle)
        {
            IINode node = global.COREInterface.GetINodeByHandle(handle);
            IMesh myMesh = GetTriObjectFromNode(node).Mesh_;
         
            //Do something with the mesh

            return (myMesh);
        }

    }
}

And here’s the MaxScript code:

(
	Global a
	--Load assembly
	dotNet.loadAssembly @"D:\Pruebas\3DMax\C#\ClassLibrary1_PruebaMax02.dll"
	
	--Prepare scene
	delete objects
	myTeapot = teapot()
	addModifier myTeapot (turboSmooth iterations:3)
	
	--CS
	t = timestamp()
	a =(dotnetclass "exportMesh.Class1").exportMesh myTeapot.inode.handle
	format "CS took			%s
" ((timestamp() - t)*.001)
	format "a = %
" a
	format "ClassOf a: %
" (classOf a)
)

And I get this result:
CS took 0.005s
a = dotNetObject:Autodesk.Max.Wrappers.Mesh
ClassOf a: dotNetObject

So I can ask in the listener for lots of things (a.numverts, a.numfaces, …) but I don’t know how to use this mesh:

nd = Editable_Mesh()
nd.mesh = a >>> throws an error “-- Unable to convert: dotNetObject:Autodesk.Max.Wrappers.Mesh to type: Mesh”

Any suggestions? Is it possible to use this dotNetObject as a MaxScript mesh?

Thanks a lot for your help.


#2

Any ideas in how to get the mesh via C#?
This could make my script faster by 1000%!!


#3

Passing MaxObjects to C# and viceversa through rootNode Custom Attributes.


-- 	Adding void CA to rootNode
(
	CA_MxsNETConnect = attributes CA_MxsNETConnect attribID:#(0x11aa99ff, 0x1234abcd)
	(
		parameters params 
		(
			T_MaxObject 		type:#maxObjectTab 		tabSizeVariable:true
			T_Node 			type:#nodeTab 			tabSizeVariable:true
			T_Point3 			type:#point3Tab 			tabSizeVariable:true
			T_Matrix3 		type:#matrix3Tab 			tabSizeVariable:true
			T_Material 		type:#materialTab 			tabSizeVariable:true
			T_TextureMap		type:#texturemapTab		tabSizeVariable:true
			T_Bitmap 		type:#bitmapTab 			tabSizeVariable:true
		)
	)
	
	try (custAttributes.delete rootNode CA_MxsNETConnect baseObject:false) catch ()
	custAttributes.add rootNode CA_MxsNETConnect baseObject:false
)	

(
	----------------------------------------
	--	Example passing nodes and maxObjects to C#
	try (rootNode.CA_MxsNETConnect) catch (custAttributes.add rootNode CA_MxsNETConnect baseObject:false)
	theNodes = selection as array
	rootNode.CA_MxsNETConnect.T_Node = theNodes
	rootNode.CA_MxsNETConnect.T_MaxObject = (for o in theNodes collect copy o.baseobject)

	dotNet.loadAssembly @"C:\......\SDKNETUtilities.dll"
	(dotnetclass "SDKNETUtilities.MxsNETConnectClass").CA_MxsNETConnect() 
	----------------------------------------	
)	
	

And then in C#


using System;
using System.Collections.Generic;
using Autodesk.Max;

namespace SDKNETUtilities
{
    class MxsNETConnectClass
    {

        // Method to obtain the ParamBlock2 of the Custom Attribute 'CA_MxsNETConnect attribID:#(0x11aa99ff, 0x1234abcd)'
        // applied to scene RootNode
        static public IIParamBlock2 GetCA_MxsNETConnect()
        {
            IGlobal global = GlobalInterface.Instance;
            IInterface14 ip = global.COREInterface14;

            IINode rootNode = ip.RootNode;
            IClass_ID CA_Class = global.Class_ID.Create(0x11aa99ff, 0x1234abcd);

            IICustAttribContainer CAC = rootNode.CustAttribContainer;
            int numCA = CAC.NumCustAttribs;
            int CA_Id = -1;
            ICustAttrib CA;

            for (int ca = 0; ca < numCA; ca++)
            {
                CA = CAC.GetCustAttrib(ca);

                if (CA.ClassID.PartA == CA_Class.PartA && CA.ClassID.PartB == CA_Class.PartB)
                {
                    CA_Id = ca;
                    break;
                }
            }

            if (CA_Id == -1)
            {
                return null;
            }

            CA = CAC.GetCustAttrib(CA_Id);
            IIParamBlock2 param = CA.GetParamBlock(0);
            return param;
        }


        static public void CA_MxsNETConnect()
        {
            IIParamBlock2 param = GetCA_MxsNETConnect();
            if (param == null)
            {
                return;
            }

            ////////////////////////////////////////////////////////////
            // Example1 to retrieve a #MaxObject that is an Edtable_Mesh (it's the first parameter => id=0)
            IReferenceTarget rf = param.GetReferenceTarget(0, 0, 0);    // First 0 is because #MaxObject are the first parameter
                                                                        // Second 0 is time at 0
                                                                        // Third 0 is first MaxObject in the #MaxObjectTab array
            ITriObject tri = (ITriObject)rf;
            IMesh mm = tri.Mesh_;   //  Retrieve the mesh of the Editable_Mesh
            int numverts = mm.NumVerts_;
            //WriteLine("numVerts= " + numverts);

            // Example2 to retrieve all nodes held in the #nodeTab parameter (it's the second parameter => id=1)
            int numNodes = param.Count(1);
            List<IINode> theNodes = new List<IINode>(numNodes);
            for (int i = 0; i < numNodes; i++)
            {
                theNodes.Add(param.GetINode(1, 0, i));
                //WriteLine("Node " + i + ": " + theNodes[i].Name);
            }

            // Example3 to return a node to the #nodeTab parameter (in this case, the last node to the first place)
            bool done = param.SetValue(1, 0, theNodes[numNodes - 1], 0);
            ////////////////////////////////////////////////////////////
        }

    }
}

It’s so easy… what I’m missing?


#4

Wonder if it is possible to determine Value class before casting? I mean anything better than the Tag.Name comparison.

static public IMesh GetMeshFromMaxscriptVariable( string global_var_name )
{
	using ( ICharStream cs = GlobalInterface.Instance.StringStream.Create( global_var_name ) )
	using ( IValue value = GlobalInterface.Instance.ExecuteScript( cs, true ) )
	{
		
		if ( value.Tag.Name == "TriMesh" )
		{
			return value.ToMesh;
		}
		else
		{
			return null;
		}
		

	}


}

#5

why don’t you assign the mesh to the node in c# and return that to mxs ?

in the sdk it would be

    TriObject* tobj = CreateNewTriObject();

   if(!tobj) // do something else

    Mesh& mesh = tobj .GetMesh();
    // build it's mesh....
      mesh  = modmesh; // or what ever

    INode* node = GetCOREInterface()->CreateObjectNode(tobj);

#6

Well, the question is really about determining ValueMetaClass of Values that ExecuteScript returns.
What if I want to pass TriMesh that I got from snapshotasmesh and BitArray from mxs to the other side?
With the use of auxiliary global variable it is possible to transfer almost anything to the dotnet side back and forth. Even the structs are passed successfully. I can read and assign props. That is not a problem.

But I need some convenient way to check that the variable types are correct.
Something similar to this, but using C#

#define is_mesh(v) ((DbgVerify(!is_sourcepositionwrapper(v)), (v))->tag == class_tag(MeshValue))

I couldn’t find much info about MeshValue and the way we should check for any other Value types.


#7

what happens when you try

mesh mesh:a

in the top code ?


#8

I’m sorry, but what top code are you talking about?
It must be my poor english, but I have no intent to create any mesh whatsoever. I want to pass maxscript variables to c# and use them for calculations, change their type, assign any other value to them, etc…

The question is how to get a class of underlying maxscript value of Value that is returned from ExecuteScript function on c# side?