[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);
}
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();
}