[Closed] SDK: snapshotAsMesh?
Hey, I’m wondering what the simplest way to perform the equivalent of the Maxscript snapshotAsMesh function would be. I found a function called “snapshotAsMesh_cf” in the avg_dlx.cpp sample file but couldn’t get it to work as-is or understand it well enough to modify it.
I’d love to have a working function to drop in to my code, but I’d also like to understand what I’m looking at a bit better. So here are some additional questions for anyone feeling generous:
-
I’ve seen other functions ending with _cf – what is the significance of this?
-
A lot of the functions in the msxagni folder use the Value class for both parameters and return values, but I’m not totally clear on how to work with them. Is it just a matter of casting to and from other types that I want to work with, or is there more to it? And why do many of the functions have parameters that use multiple indirection?
-
More a C++ thing, but I still don’t really understand the rules for token concatenation in #define statements, if anyone would like to explain them…
((TriObject*)node->EvalWorldState(t).obj)->GetMesh();
...is how you get the mesh of a node at a certain time.
Note: this doesn't do any checking to see if the node in question is a TriObject. You'll need to do your own conversions if it's not (make sure to check if it can be collapsed to one) and don't forget to clean up any collapsed meshes.
I don’t think “_cf” has any significance other than being a naming convention. Maybe it means “custom function” or whatever.
“Value” is just what is sent to/from maxscript. Typically “Value** arg_list” in the c++ functions exposed to maxscript is tested by your function to see if it contains arguments of the correct type, and then those are cast to whatever type that is. If you make a new maxscript extension c++ plugin from the plugin wizard, inside you’ll see a very simple implementation of both checking type and casting-to-type for Values.
So as a starting point I’ve got
INode *Snapshot(INode *sourceNode, TimeValue t) {
TriObject *obj = new TriObject;
INode *node = GetCOREInterface()->CreateObjectNode(obj);
Mesh &mesh = obj->GetMesh();
mesh = ((TriObject *)sourceNode->EvalWorldState(t).obj)->GetMesh();
return node;
}
I’m going to be using this with a pickbutton that requires the source node to be TriObject-collapsible, so I don’t have to worry about that part of it.
I still need to match the transformation matrix of the source node, but that’s simple. On the other hand, I’m noticing that the new node is of type “Mesh” rather than “Editable Mesh”, and the function I normally use to convert things to editable meshes does not seem to change that.
EDIT: I just realized that I’ve accidentally been using “snapshotAsMesh” interchangeably with “snapshot”. When I started my project, I was actually planning on using the former, but found that it was more convenient to work with an actual node. And yet, when it came to asking my question, I think I momentarily lapsed back to my original thinking.
Either way, please let me know what else I should have in place before trying to use this function in any non-trivial plugins…
Just remember that ‘CanConvertToType(triObjectClassID)’ doesn’t necessarily mean your node is already a triobject. For example, an editable poly object would pass that test, but crash during that snapshot function.
The common procedure for getting a triobject mesh from any object that can be collapsed to a triobject is:
TriObject *collapsedtobj = NULL;
Mesh *m;
if (os.obj->IsSubClassOf(triObjectClassID))
{
tobj = (TriObject*)os.obj;
m = &tobj->GetMesh();
}else
{
if (os.obj->CanConvertToType(triObjectClassID)) //editable polys and stuff
{
collapsedtobj = (TriObject*) meshos.obj->ConvertToType(geoT, triObjectClassID);
m = &collapsedtobj->GetMesh();
}
}
//........do some work with the mesh.........
if (collapsedtobj != NULL)
{
collapsedtobj->DeleteThis(); //cleanup collapsed obj
}
snapshot is a copy of node’s triobject… so
m = &tobj->GetMesh();
should be:
m = new Mesh(tobj->GetMesh());
also ConvertToType does do internal check if object is already TriObject, so
if (obj->CanConvertToType(triObjectClassID))
{
TriObject* tri = (TriObject*)obj->ConvertToType(t, triObjectClassID);
...
is safe and fast enough
test for deletion is:
if (obj != tri) tri->AutoDelete();
Woops, forgot that we want a copy of the mesh Thanks Denis.
Also didn’t realize that ConvertToType doesn’t do redundant conversions…good to know for the future
we need a copy to be safe to transform verts if you want to put them where they are in the world
there is another thing – RenderMesh
from the sdk help:
// GetRenderMesh should be implemented by all renderable GeomObjects.
// set needDelete to TRUE if the render should delete the mesh, FALSE otherwise
// Primitives that already have a mesh cached can just return a pointer
technically you can ask GetRenderMesh any renderable GeomObjects. in theory it must be faster then ConvertToType.
suffix “_cf” probably means “c function”. it’s used by the system to keep tracking of all global functions used. this name is stored for the system use. mxs might see something different defined by developer:
def_visible_primitive(thisisthenameforsystem, "thisisthenameformxs");
Value* thisisthenameforsystem_cf(Value **arg_list, int count)
{
...
}
Okay, I’m trying to put together what you’re telling me, but the function I am coming up with crashes Max. What am I doing wrong?
INode *Snapshot(INode *sourceNode, TimeValue t) {
TriObject *obj = new TriObject;
INode *node = GetCOREInterface()->CreateObjectNode(obj);
Mesh *mesh = new Mesh(obj->GetMesh()); // m = &triObj->GetMesh();
if (obj->CanConvertToType(triObjectClassID)) {
TriObject *triObj = (TriObject *)obj->ConvertToType(t, triObjectClassID);
if (obj != triObj) triObj->AutoDelete();
}
return node;
}