SDK - PolyObject modify verts not undoable


#1

Hello,

I have written a function in the Max SDK that takes a delta offset and applies it to all verts in the given editable poly.
It works as expected, however the action is not undoable, which is confusing as if I use a function that manipulates a TriMesh rather than a MNMesh it is undoable.
I cannot use the TriMesh function as it causes any custom attributes on my editable poly object to be lost as it is converting the object to an Editable Mesh.

I am not sure what I could be missing, other than TriMesh has undo functionality included and MNMesh does not.

Here is the function that modifies the MNMesh:

PolyObject* GetPolyObjectFromNode(INode* inNode, TimeValue inTime, bool& deleteIt)
{
	Object* object = inNode->GetObjectRef();
	if (object->CanConvertToType(Class_ID(POLYOBJ_CLASS_ID, 0)))
	{
		PolyObject* polyObject = (PolyObject*)object->ConvertToType(inTime, Class_ID(POLYOBJ_CLASS_ID, 0));
		// Note that the polyObject should only be deleted
		// if the pointer to it is not equal to the object

		// pointer that called ConvertToType()
		if (object != polyObject) deleteIt = true;
		return polyObject;
	}
	else
	{
		return NULL;
	}
}

static bool applyOffsetToVertices(INode* inNode, Point3 inOffset)
{
	static bool success = false;
	bool deleteIt = false;
	bool polyDeleteIt = false;
	Interface* coreInterface = GetCOREInterface();
	PolyObject* polyObject = GetPolyObjectFromNode(inNode, coreInterface->GetTime(), deleteIt);
	if (polyObject)
	{
		MNMesh& mesh = polyObject->GetMesh();
		for (int vertIndex = 0; vertIndex < mesh.VNum(); vertIndex++)
		{
			MNVert* vert = mesh.V(vertIndex);
			vert->p += inOffset;
		}
		inNode->SetObjectRef(polyObject);
		polyObject->FreeCaches();
		polyObject->NotifyDependents(FOREVER, OBJ_CHANNELS, REFMSG_CHANGE);
		coreInterface->RedrawViews(coreInterface->GetTime());
		success = true;
	}
	return success;
}

and here is the function that modifies the TriMesh:

TriObject* GetTriObjectFromNode(INode* inNode, TimeValue inTime, bool& deleteIt)
{
	deleteIt = false;
	Object* obj = inNode->EvalWorldState(inTime).obj;
	if (obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)))
	{
		TriObject* triObject = (TriObject*)obj->ConvertToType(inTime, Class_ID(TRIOBJ_CLASS_ID, 0));
		// Note that the TriObject should only be deleted
		// if the pointer to it is not equal to the object

		// pointer that called ConvertToType()
		if (obj != triObject) deleteIt = true;
		return triObject;
	}
	else
	{
		return NULL;
	}
}
static bool applyOffsetToVertices(INode* inNode, Point3 inOffset)
{
	static bool success = false;
	bool deleteIt = false;
	TriObject* triObject = GetTriObjectFromNode(inNode, GetCOREInterface()->GetTime(), deleteIt);
	if (triObject)
	{
		Mesh& mesh = triObject->GetMesh();
		for (int vertIndex = 0; vertIndex < mesh.getNumVerts(); vertIndex++)
		{
			Point3 position = mesh.getVert(vertIndex);
			mesh.setVert(vertIndex, position + inOffset);
		}
		mesh.InvalidateGeomCache();
		mesh.InvalidateTopologyCache();
		inNode->SetObjectRef(triObject);
		triObject->NotifyDependents(FOREVER, OBJ_CHANNELS, REFMSG_CHANGE);
		Interface* coreInterface = GetCOREInterface();
		coreInterface->RedrawViews(coreInterface->GetTime());
		success = true;
	}
	return success;
}

#2

You’ll have to add your modification to the undo stack manually if it doesn’t do it internally. Look at the docs for “theHold”.


#3

This is what I thought, I just wanted to make sure before I implemented undo logic myself.


#4

use EPoly ApplyDelta… it’s undoable


#5

Hey Denis,

Thanks for the reply.
I have found a bit of type to try your suggestion but I cannot quite figure out how the EPoly interface works.
It causes a memory error when I try to apply the offset using ApplyDelta.
Here is the code I have ( put together from digging around in the SDK for examples ):

static bool applyOffsetToVerticesEPoly(INode* inNode, Point3 inOffset)
{
	static bool success = false;
	bool deleteIt = false; 
	bool polyDeleteIt = false;
	Interface* coreInterface = GetCOREInterface();
	PolyObject* polyObject = GetPolyObjectFromNode(inNode, coreInterface->GetTime(), deleteIt);
	if (polyObject)
	{
		EPoly* ePolyInterface = NULL;
		ePolyInterface = (EPoly*)inNode->GetInterface(EPOLY_INTERFACE);
		MNMesh& mesh = polyObject->GetMesh();
		Tab<Point3> deltaTab;
		deltaTab.SetCount(mesh.VNum());
		for (int index = 0; index < mesh.VNum(); index++)
		{
			deltaTab[index] = inOffset;
		}
		ePolyInterface->ApplyDelta(deltaTab, ePolyInterface, coreInterface->GetTime());
		success = true;
	}
	return success;
}

Is there something I am missing?


#6

Ignore me, I found the correct way straight after replying to you!

static bool applyOffsetToVerticesEPoly(INode* inNode, Point3 inOffset)
{
	static bool success = false;
	bool deleteIt = false; 
	bool polyDeleteIt = false;
	Interface* coreInterface = GetCOREInterface();
	PolyObject* polyObject = GetPolyObjectFromNode(inNode, coreInterface->GetTime(), deleteIt);
	if (polyObject)
	{
		ObjectState objectState = inNode->EvalWorldState(coreInterface->GetTime(), true);
		Object* nodeObjectBase = objectState.obj->FindBaseObject();
		EPoly *ePolyInterface = (EPoly *)(nodeObjectBase->GetInterface(EPOLY_INTERFACE));
		MNMesh& mesh = polyObject->GetMesh();
		Tab<Point3> deltaTab;
		deltaTab.SetCount(mesh.VNum());
		for (int index = 0; index < mesh.VNum(); index++)
		{
			deltaTab[index] = inOffset;
		}
		ePolyInterface->ApplyDelta(deltaTab, ePolyInterface, coreInterface->GetTime());
		success = true;
	}
	return success;
}

Thanks very much for the suggestion, it works a treat!