Notifications
Clear all

[Closed] Useful mxs sdk extension functions

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;
}

interestingly missing from the default meshop.turnEdges function could return the “other” edge (and hence the other face) so here’s the correction…

def_struct_primitive(meshop_turnAnEdge, meshop, "turnAnEdge");

Value* meshop_turnAnEdge_cf(Value** arg_list, int count)
{	
	enum args { kmesh, kedge, knum_args };
	check_arg_count_with_keys(turnAnEdge, knum_args, count);
	ReferenceTarget *owner;
	Mesh* mesh = get_meshForValue(arg_list[kmesh], MESH_BASE_OBJ, &owner, turnAnEdge);
	int edgeIndex=arg_list[kedge]->to_int()-1;
	range_check(edgeIndex+1, 1, 3*mesh->getNumFaces(), GetString(IDS_EDGE_INDEX_OUT_OF_RANGE)) 
	MeshDeltaUser *mdu = (owner) ? GetMeshDeltaUserInterface(owner) : NULL;
	if (mdu && mdu->Editing()) mdu->ExitCommandModes();

	MeshDelta tmd(*mesh);
	DWORD res = tmd.TurnEdge (*mesh, edgeIndex, NULL);
	if (mdu) 
		GetMeshDeltaUserDataInterface(owner)->ApplyMeshDelta (tmd, mdu, MAXScript_time());
	else 
		tmd.Apply (*mesh);
	return Integer::intern(res + 1);
}
1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

a good thing…
Also, I always add an update option for every mdu / tmd choice … because one updates and the other doesn’t. (I don’t remember which one). also there is an issue with node events… so i add:

			if (update)
			{
				mesh->InvalidateGeomCache();
				if (owner)
				{
					if (mdu) mdu->LocalDataChanged(ALL_CHANNELS);
					owner->NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
				}
				needs_redraw_set(); 
			}
Page 18 / 18