Notifications
Clear all

[Closed] Need Help converting a MAXScript to .NET

There was a function that Denis helped to create here that I am trying to convert to C#. I’m having trouble even finding the documentation I need to get along in the project. Here is my sample function in C#:


  /*
		 * Create a plane on all four-sided (selected) faces in the node referenced by  handleid
		 @return bool
		 */
		public bool convertFacesToPlanes(uint handleid)
		{
			IGlobal global = Autodesk.Max.GlobalInterface.Instance;
			IInterface14 ip = global.COREInterface14;
			IINode obj = ip.GetINodeByHandle(handleid).ActualINode;
			IObjectState os = obj.ObjectRef.Eval(ip.Time);
			// Now grab the object itself.
			IObject objOriginal = os.Obj;
			// Let's make sure it is a TriObject, which is the typical kind of object with a mesh
			if (!objOriginal.IsSubClassOf(global.TriObjectClassID))
			{
				// If it is NOT, see if we can convert it...
				if (objOriginal.CanConvertToType(global.TriObjectClassID) == 1)
				{
					objOriginal = objOriginal.ConvertToType(ip.Time, global.TriObjectClassID);
				}
				else
				{
					return false;
				}
					
			}
			// Now we should be safe to know it is a TriObject and we can cast it as such.
			// An exception will be thrown...
			ITriObject triObj = objOriginal as ITriObject;
			//IPolyObject triObj = objOriginal as IPolyObject;
			IMesh mesh = triObj.Mesh as IMesh;
			IBitArray facearray = mesh.FaceSel; //Should be a bitarray of selected faces
			//!TODO... if count == 0 then use all polygons
			/*See if any faces are selected*/
			if (facearray.AnyBitSet.Equals(true)) {
				
				int faceCount = facearray.Size;
				/*
				int time = (int)1;
				int numFaces ;
				int numVerts ;
				
				global.GetPolygonCount(time, objOriginal, &numFaces, &numVerts);
				*/
				/*iterate over selected faces
				 
				 Uh oh... this is a trimesh... what we really need is the selected Polygon... how to get this in C#?
				 
				 */
				for (int i = 0; i <= (faceCount - 1); i++)
				{
					IFace face = mesh.Faces[i];
					if (face.V.Count() == 4) {
						/*But this will never happen in a triMesh*/
						IPoint3 v1 = mesh.GetVert((int)(face.GetVert(0)) );
						IPoint3 v2 = mesh.GetVert((int)(face.GetVert(1)));
						IPoint3 v3 = mesh.GetVert((int)(face.GetVert(2)));
						IPoint3 v4 = mesh.GetVert((int)(face.GetVert(3)));
					IPoint3 front =  v1.Subtract(v2).Normalize;  
					IPoint3  side = v2.Subtract(v3).Normalize;  
					IPoint3 up =  mesh.GetFaceNormal(i);
					float divNum = (4);
					IPoint3 center = v1.Add(v2).Add(v3).Add(v4).DivideBy(divNum);

						//IMatrix3 tm =  new IMatrix3(front side up center);
						//tm = pretranslate tm [0.5, 0.5, 0]
						//face.GetVertIndex()
					}
				}
			} else{
				return false;
			}
			return true;
		}
 

How do you iterate over the selected Polygons of a node? I need to be able to find all 4-sided polygons in the current selection but can’t find an answer.

20 Replies

you need to look at the sdk reference for mnmesh for editable poly objects

Aha… that is very helpful.

Now another question. How do you get constants? The documentation references a bunch of constants in classes all over the place, but I can’t find the path to get these constants. Here is an example:


[font=Consolas][size=2] [/size][/font][font=Consolas][size=2][font=Consolas][size=2][color=#2b91af]IInterval[/size][/font][/color][/size][/font][font=Consolas][size=2] valid = FOREVER;[/size][/font]

[font=Consolas][size=2]  	        [/size][/font][font=Consolas][size=2][font=Consolas][size=2][color=#2b91af]IMatrix3[/size][/font][/color][/size][/font][font=Consolas][size=2] otm = p.GetNodeTM(0,valid);[/size][/font]
 

The docs speak of a constant FOREVER (or other constants). How do you access the available constansts?

they are not constants as such they are macro definitions (c++) and are defined in the sdk include file interval.h

#define  FOREVER   Interval(TIME_NegInfinity, TIME_PosInfinity) 
#define  NEVER   Interval(TIME_NegInfinity, TIME_NegInfinity) 

I’ve no idea how that all works in c#

That helps explain the docs some. But it further points out how ridiculous the docs are! I’m sure they would make sense if I already knew it all… but then where is the value

These docs need WAY MORE example code for .NET!!!

Anyway, thanks for the help.

At this moment, I am trying to learn to apply a modifier to an object.

Here is how the example code in the docs explain it


// Create a bend modifier
Modifier *bend = (Modifier*)ip->CreateInstance(OSM_CLASS_ID, Class_ID(BENDOSM_CLASS_ID,0));
 

After experimentation and scouring the skimpy examples, I’ve come up with this (using FFD which is what I’m trying right now).


[font=Consolas][size=2][font=Consolas][size=2][color=#2b91af]IClass_ID[/size][/font][/color][/size][/font][font=Consolas][size=2] cid = global.Class_ID.Create(([/size][/font][font=Consolas][size=2][font=Consolas][size=2][color=#0000ff]uint[/size][/font][/color][/size][/font][font=Consolas][size=2])[/size][/font][font=Consolas][size=2][font=Consolas][size=2][color=#2b91af]BuiltInClassIDA[/size][/font][/color][/size][/font][font=Consolas][size=2].FFD22_CLASS_ID, ([/size][/font][font=Consolas][size=2][font=Consolas][size=2][color=#0000ff]uint[/size][/font][/color][/size][/font][font=Consolas][size=2])[/size][/font][font=Consolas][size=2][font=Consolas][size=2][color=#2b91af]BuiltInClassIDB[/size][/font][/color][/size][/font][font=Consolas][size=2].FFD22_CLASS_ID);[/size][/font]
 
[font=Consolas][size=2][font=Consolas][size=2][color=#2b91af]IModifier[/size][/font][/color][/size][/font][font=Consolas][size=2] ffd = ([/size][/font][font=Consolas][size=2][font=Consolas][size=2][color=#2b91af]IModifier[/size][/font][/color][/size][/font][font=Consolas][size=2])ip.CreateInstance([/size][/font][font=Consolas][size=2][font=Consolas][size=2][color=#2b91af]SClass_ID[/size][/font][/color][/size][/font][font=Consolas][size=2].Osm, cid);[/size][/font]

I think this is failing, and I think it is because the SClass_ID.Osm is an enum and I need the Superclass of the FFD. I’ve not figured out where to locate it.

Any input?

after playing with new max c# api-kindishing i have unfortunately to say that it’s the thing that cannot be used in a development. don’t waste your time. learn max c++ sdk, and polish your mxs skills.

1 Reply
(@wallworm)
Joined: 11 months ago

Posts: 0

What are your main reasons for this conclusion Denis?

I do intend on pursuing the C++ SDK in the future, but at this moment my time is limited and using C# is quicker for me to build what I’m working on right now because I already know how to build and compile from C# (it’s closer to the kind of programming with which I’m familiar).

The total lack of documentation/examples on the C# side, though, ruins the potential speed benefits of developing anything in it.

I’m having problems utilizing the IMNMesh because I cannot even find an example on using it with a scene node.

I would concur with Denis sdk is the way to go but that aside if your looking to convert that script to the “sdk” via c# then a slightly different approach maybe better. With that in mind I was tinkering with an sdk mxs function to generate a plane from 4 points (which could be used in part for your needs)



// linear interpolator 
template<class T> inline T lerp(const T& a, const T& b, const float t) { return (T)(a*(1.f-t)) + (b*t); }

def_visible_primitive(planeFromCornerPoints, "planeFromCornerPoints");
 
 Value* planeFromCornerPoints_cf(Value **arg_list, int count)
 {
 	check_arg_count(planeFromCornerPoints, 6, count);
 	Point3 bottom_left = arg_list[0]->to_point3();
 	Point3 bottom_right = arg_list[1]->to_point3();
 	Point3 top_right = arg_list[2]->to_point3();
 	Point3 top_left = arg_list[3]->to_point3();
 	int width_segs = arg_list[4]->to_int();
 	int length_segs = arg_list[4]->to_int();
 
 	TriObject* tobj = CreateNewTriObject();
 	if(tobj)
 	{
 		INode* node = MAXScript_interface->CreateObjectNode(tobj);
 		if(!node)
 		{	
 			tobj->DeleteThis();
 			return &undefined;
 		}
 
 // create the transforn
 
 		Point3 xaxis = (bottom_right - bottom_left).Normalize();
 		Point3 temp =  bottom_left - top_left;
 		Point3 zaxis = (temp ^ xaxis).Normalize();
 		Point3 yaxis = (zaxis ^ xaxis ).Normalize();
 		Point3 pos = (bottom_right + bottom_left + top_left + top_right) * 0.25f;
 		Matrix3 tm(xaxis,yaxis,zaxis,pos);
 
 // set the node base transform
 
 		node->SetNodeTM(0,tm);
 
 // get the corners in local space
 
 		Matrix3 xfm = Inverse(tm);
 		Point3 bl = bottom_left * xfm;
 		Point3 br = bottom_right * xfm;
 		Point3 tr = top_right * xfm;
 		Point3 tl = top_left * xfm;
 
 // initial settings
 
 		int	wverts = width_segs + 1;
 		int lverts = length_segs + 1;
 		int nverts = wverts * lverts;
 		int nfaces = length_segs * width_segs * 2;
 		float lscalar = 1.0f/(float)length_segs;
 		float wscalar = 1.0f/(float)width_segs;
 
 // get the object mesh
 
 		Mesh& mesh = tobj->GetMesh();
 
 // create the space for the topology
 
 		mesh.setNumVerts(nverts);
 		mesh.setNumFaces(nfaces);
 		mesh.setMapSupport(1,true);
 		mesh.setNumTVerts(nverts);
 		mesh.setNumTVFaces(nfaces);
 
 // generate the verts	
 
 		int vi = 0;
 		for(int i=0; i < lverts; i++)
 		{
 			Point3 startvert(lerp(bl,tl,i * lscalar));
 			Point3 endvert(lerp(br,tr,i * lscalar));
 			for(int j = 0; j < wverts; j++)
 				mesh.setVert(vi++,lerp(startvert,endvert,j * wscalar));
 		}
 	
 // generate the tverts	
 
 		vi = 0;
 		for(int i=0; i < lverts; i++)
 		{
 			Point3 startvert(lerp(Point3(0.0f,0.0f,0.0f),Point3(0.0f,1.0f,0.0f),i * lscalar));
 			Point3 endvert(lerp(Point3(1.0f,0.0f,0.0f),Point3(1.0f,1.0f,0.0f),i * lscalar));
 			for(int j = 0; j < wverts; j++)
 				mesh.setTVert(vi++, lerp(startvert,endvert,j * wscalar));
 		}
 
 		int fi = 0;
 		for(int i = 0; i < length_segs; i++)
 		{
 			int j = i * (width_segs + 1);
 			for(int k = 0; k < width_segs; k++)
 			{
 // quad vert indices
 
 				int na = j + k;
 				int nb = na + width_segs + 1;
 				int nc = nb + 1;
 				int nd = na + 1;
 
 // create first quad face
 
 				mesh.faces[fi].setVerts(nb,na,nc);
 				mesh.faces[fi].setEdgeVisFlags(1,0,1);
 				mesh.tvFace[fi++].setTVerts(nb,na,nc);
 
 // create second quad face
 
 				mesh.faces[fi].setVerts(nc,na,nd);
 				mesh.faces[fi].setEdgeVisFlags(0,1,1);
 				mesh.tvFace[fi++].setTVerts(nc,na,nd);
 			}
 		}
 		return MAXNode::intern(node);
 	}
 	return &undefined;
 }

you can ignore the boilerplate mxs stuff. Anyway hope it of some help If not I have a good use for the plane creater

Thank you… I will likely reuse this in the future. But my problem is an issue of time. I don’t currently have the time to learn C++. The value of C# for me is that it is structured so much like the languages I handle the most. And I’m sure it is better in the long run to know C++ Max SDK. At this moment in time I’m trying to implement some of the MAXScript functions I have that are slow. And if I can get away with that in C# then it serves my purpose best as I need to optimize the limited time I do have.

In the end, a few of my intended functions are to deal with unfortunate quirks in Max–like how it kills performance in MAXScript if you need to select an object and open the modify tab (because the modifiers require this in some cases):

FFD Control Points
Edit_Normals (set/get)
UVWMap gizmo

Simply utilizing the Edit_Normals() modifier to set normal destroyed speed in a script by 59X this week.

I have a couple other functions I’d like to speed up too… but these are the main things.

So before I pursue… do the modifiers in question have the same limitations in C++/C# implementations? I’m hoping that in the SDK you have access to functions to set them ready to call relevant methods without the modify tab issues.

For all of use MAXScript lovers, can a C# guy share a lesson on converting the following simple code into a C# function? I think it will be very educational:


 
b=Box width:100 length:200 height:300
b.name = "My Awesome Box"
 
 
b.lengthsegs = 12
b.heightsegs = 12
b.widthsegs = 12
 
 
bnd = Bend()
bnd.name = "My Awesome Bend Modifier"
 
addModifier b bnd
 
animate on
(
 at time 1 b.pos = [0,0,0]
 at time 100 b.pos = [100,200,300]
 
 at time 1 bnd.angle = 45
 at time 100 bnd.angle = -45
 
)

I just want to see examples of doing these kinds of simple tasks in C#.

there’s is a reason why you won’t find examples of that sort of thing in c# or c++ come to that, simple tasks like that are best/easiest/quickest done in mxs. Also repeating similar slow mxs methods, edit_normals, general modifier panel stuff, etc can still fall foul of the same issues and bottlenecks when using c# or c++.

1 Reply
(@wallworm)
Joined: 11 months ago

Posts: 0

Well I understand that

The point is to get a map of MAXScript-to-C# methodologies … to create a mental framework to move forward.

In this simple example… I just want to see how it is done–not that the example itself lends itself to being inside C#. At least for me personally, that kind of example will server as a good scaffold for moving forward with delving into C#.

That is certainly unfortunate.

But if so, then it seems that you at least have a workaround with the C++/C# because of access to MNMesh and building your own logic. For MAXScript you have no recourse but performance hit.

That is certainly unfortunate.

But if so, then it seems that you at least have a workaround with the C++/C# because of access to MNMesh and building your own logic.

yes having direct access to the geometry at the most basic level in a compiled language like c++ is where you can achieve the most performance gains. Using it to create an instance of a plane and adding a ffdbox modifier to it and then changing the modifier at the subobject level, though relatively simple task, I don’t think it’s going to win you an awful lot.

heres a mxs version of the sdk plane from points function I posted earlier does it give you anything ? I’ve no idea on it’s performance with respect to the Denis script which are typically fast. It would mean you could keep out of the modifier panel. Also if you don’t need tverts theres more gains to be had.


 fn lerp x y s = (x + s * (y - x))
 
 fn mxssetedgevisflags m face flags =
 (	
 	setEdgeVis m  face 1 flags[1];
 	setEdgeVis m  face 2 flags[2];
 	setEdgeVis m  face 3 flags[3];
 )
 	
 fn MakePlaneMesh bottom_left bottom_right top_right top_left width_segs:4  length_segs:4 =
(
	local wverts = width_segs + 1;
	local lverts = length_segs + 1; 
	local nverts = wverts * lverts;
	local nfaces = length_segs * width_segs * 2;
	local lscalar = 1.0/length_segs;
	local wscalar = 1.0/width_segs;
	
-- build the coordsys	
	
	local xaxis = normalize (bottom_right - bottom_left);
	local temp =  bottom_left - top_left;
	local zaxis = normalize (cross temp xaxis);
	local yaxis = normalize (cross zaxis xaxis );
	local trans = (bottom_right + bottom_left + top_left + top_right) * 0.25;
	local tm = (matrix3 xaxis yaxis zaxis trans);
	
-- create an empty mesh
	
	msh = mesh numverts:nverts numfaces:nfaces;
	
-- setup the default mapping channel	
	
	meshop.setMapSupport msh 1 true;
	meshop.setNumMapVerts msh 1 nverts;
	meshop.setNumMapFaces msh 1 nfaces; 
	
-- set the transform	
	
	msh.transform = tm;
	
-- generate the verts		
	
	local vi = 1;	-- vert index
	for i = 1 to lverts do
	(
		local iscale = (i-1) * lscalar;
		
		vert_start = lerp bottom_left top_left iscale;
		vert_end = lerp bottom_right top_right iscale;	
		
		tvert_start = lerp [0,0,0] [0,1,0] iscale;
		tvert_end = lerp [1,0,0] [1,1,0] iscale;
		
		for j = 1 to wverts do
		(
			local jscale = (j-1) * wscalar;
			setVert msh vi (lerp vert_start vert_end jscale);
			meshop.setMapVert msh 1 vi (lerp tvert_start tvert_end jscale);	
			vi += 1;
		)
	)
	
-- generate the faces	
	
	local fi = 1; -- face index
	for i = 0 to (length_segs - 1) do
	(
		j = i * (width_segs + 1) + 1;	-- compute the starting vert index
		for k = 0 to (width_segs - 1) do
		(
-- quad vert indices			
			
			na = j + k;
			nb = na + width_segs + 1;
			nc = nb + 1;
			nd = na + 1;
			
-- create first quad face			
			
			setface msh fi [nb,na,nc];
			mxssetedgevisflags msh fi #{1,3};
			meshop.setMapFace msh 1 fi [nb,na,nc];
			fi += 1;
			
-- create second quad face			
			
			setface msh fi [nc,na,nd];
			mxssetedgevisflags msh fi #{2,3};
			meshop.setMapFace msh 1 fi [nc,na,nd];
			fi += 1;
		)
	)
	update msh;
	msh;
)

edit: doh! moment

Page 1 / 2