Notifications
Clear all

[Closed] Select FFD Control Point

I’m trying to write a script that selects FFD control points instead of using manual selection. I am able to access the points and set/get their positions, but it’s unclear to me how I can use the script to select them by index instead of manually selecting in the editor. For example I want to select ffd.control_point_1 but I get an error.

3 Replies

MAXScript reference:
There are two special considerations when accessing FFD control points. First, you can only access control points that have ALREADY been animated by hand, or by using the animateVertex() or the animateAll() MAXScript functions

I already call animateAll() in the script, I am able to access the control points from script but my question is how do I select them in the editor using code

oh, I missed that from your post.
I don’t know if maxscript method to select control points exists
but here’s what we can do about it
that’s what is going on under the hood when control points selection happens

void FFDMod::SelectSubComponent(
		HitRecord *hitRec, 
		BOOL selected, 
		BOOL all, 
		BOOL invert)
	{
	int level = ip ? ip->GetSubObjectLevel() : SEL_LATTICE;
	if (level==SEL_LATTICE) return;

	if (theHold.Holding()) theHold.Put(new SelRestore(this));
	while (hitRec) {
		BOOL state = selected;
		if (invert) state = !sel[hitRec->hitInfo];
		if (state) sel.Set(hitRec->hitInfo); <-- this is what we need to emulate
		else       sel.Clear(hitRec->hitInfo);
		if (!all) break;
		hitRec = hitRec->Next();
		}	
	NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
	}

let’s emulate it using c# sdk

	delete objects
	gc()

	t          = Teapot()
	ffd        = FFD4x4x4()
	addModifier t ffd
		
	g          = (dotNetClass "Autodesk.Max.GlobalInterface").Instance
	hitRecord  = g.HitRecord.create()
	animatable = g.animatable.getanimbyhandle (dotNetObject "System.UIntPtr" (getHandleByAnim ffd))
	
	select t
	max modify mode
	subObjectLevel = 1
		
	-- .SelectSubComponent <Autodesk.Max.IHitRecord> hitRec  <System.Boolean>selected <System.Boolean>all <System.Boolean>invert
	for i = 0 to 27 do
	(
		hitRecord.HitInfo = i
		animatable.SelectSubComponent hitRecord true false false
	)
	
	redrawViews()

as a bonus you don’t have to use animateAll to move/rotate/scale control points

(
		tm = $.objecttransform
		mod_tm = inverse (getModContextTM $ $.modifiers[1])
		_tm = tm * mod_tm

		-- .<Autodesk.Max.IMatrix3> Create  <Autodesk.Max.IPoint3>U <Autodesk.Max.IPoint3>V <Autodesk.Max.IPoint3>N <Autodesk.Max.IPoint3>T
		modtm = g.matrix3.create (g.point3.create _tm[1][1] _tm[1][2] _tm[1][3]) (g.point3.create _tm[2][1] _tm[2][2] _tm[2][3]) (g.point3.create _tm[3][1] _tm[3][2] _tm[3][3]) (g.point3.create _tm[4][1] _tm[4][2] _tm[4][3])


		tm = g.Matrix3.create()
		tm.IdentityMatrix()
		pt = g.point3.create 0 0 10 -- offset
		animatable.move (currentTime as integer) modtm tm pt false

		redrawViews()
	)