Skip to content

Instantly share code, notes, and snippets.

@mattcox
Last active July 30, 2022 20:27
Show Gist options
  • Save mattcox/46e54e3695c769bdeed4 to your computer and use it in GitHub Desktop.
Save mattcox/46e54e3695c769bdeed4 to your computer and use it in GitHub Desktop.
Demonstrates a simple plane mesh operation for the Modo procedural modelling system. This demonstrates how to do incremental updates that create the polygon on the first evaluation, and then updates point positions on subsequent evaluations. Incremental updates can significantly improve performance, as Modo will only rebuild the surface for draw…
#include <lxsdk/lx_mesh.hpp>
#include <lxsdk/lx_pmodel.hpp>
#include <lxsdk/lx_seltypes.hpp>
#include <lxsdk/lxu_attributes.hpp>
#define SERVER_NAME "pmodel.createPlane"
#define ATTRs_SIZE "size"
#define ATTRi_SIZE 0
class MeshOp :
public CLxImpl_MeshOperation,
public CLxDynamicAttributes
{
public:
static void
initialize ()
{
CLxGenericPolymorph *srv = NULL;
srv = new CLxPolymorph <MeshOp>;
srv->AddInterface (new CLxIfc_MeshOperation <MeshOp>);
srv->AddInterface (new CLxIfc_Attributes <MeshOp>);
srv->AddInterface (new CLxIfc_StaticDesc <MeshOp>);
lx::AddServer (SERVER_NAME, srv);
}
MeshOp ()
{
/*
* Add a single distance attribute to control the plane size. This
* will automatically be converted into a distance channel. Setting
* a default value will set the default channel value.
*/
dyna_Add (ATTRs_SIZE, LXsTYPE_DISTANCE);
attr_SetFlt (ATTRi_SIZE, 1.0);
_size = 1.0;
}
LxResult
mop_Evaluate (
LXtObjectID mesh_obj,
LXtID4 type,
LXtMarkMode mode)
{
CLxUser_Mesh mesh (mesh_obj);
CLxUser_Polygon polygon;
CLxUser_Point point;
LXtPolygonID poly_id = NULL;
LXtVector pos;
LxResult result = LXe_FAILED;
static double positions[4][3] = {{-0.5, 0.0, -0.5}, {0.5, 0.0, -0.5}, {0.5, 0.0, 0.5}, {-0.5, 0.0, 0.5}};
/*
* Read the size attributes, the same as any other attribute. This
* is set by the wrapper modifier using the channel value.
*/
_size = dyna_Float (ATTRi_SIZE, 1.0);
if (mesh.test ())
{
polygon.fromMesh (mesh);
point.fromMesh (mesh);
if (polygon.test () && point.test ())
{
/*
* Create the points and polygon in the mesh. The
* point ID's are cached to allow re-evaluation.
*/
if (_size > 0.0)
{
for (unsigned i = 0; i < 4; i++)
{
LXx_VSCL3 (pos, positions[i], _size);
point.New (pos, &_cache[i]);
}
polygon.New (LXiPTYP_FACE, _cache, 4, 0, &poly_id);
mesh.SetMeshEdits (LXf_MESHEDIT_GEOMETRY);
}
result = LXe_OK;
}
}
return result;
}
int
mop_Compare (
ILxUnknownID other_obj)
{
MeshOp *other = NULL;
/*
* Cast the old Mesh Operation COM object to our Mesh Operation
* implementation.
*/
lx::CastServer (SERVER_NAME, other_obj, other);
/*
* Check we have valid cached data in the other mesh operation.
*/
if (other && other->_cache[0] && other->_cache[1] && other->_cache[2] && other->_cache[3])
{
/*
* If the size value for both mesh operations is greater
* than zero, then the re-evaluation is compatible. If the
* value is identical, then we don't need to perform any
* evaluation at all.
*/
if (other->_size > 0.0 && _size > 0.0)
{
if (lx::Compare (_size, other->_size) == 0)
return LXiMESHOP_IDENTICAL;
else
return LXiMESHOP_COMPATIBLE;
}
}
return LXiMESHOP_DIFFERENT;
}
LxResult
mop_Convert (
ILxUnknownID other_obj)
{
MeshOp *other = NULL;
/*
* Cast the old Mesh Operation COM object to our Mesh Operation
* implementation.
*/
lx::CastServer (SERVER_NAME, other_obj, other);
/*
* Update the new mesh operation with the old data.
*/
if (other && other->_cache[0] && other->_cache[1] && other->_cache[2] && other->_cache[3])
{
_cache[0] = other->_cache[0];
_cache[1] = other->_cache[1];
_cache[2] = other->_cache[2];
_cache[3] = other->_cache[3];
return LXe_OK;
}
return LXe_FAILED;
}
LxResult
mop_ReEvaluate (
ILxUnknownID mesh_obj,
LXtID4 type)
{
CLxUser_Mesh mesh (mesh_obj);
CLxUser_Point point;
LXtVector pos;
LxResult result = LXe_FAILED;
static double positions[4][3] = {{-0.5, 0.0, -0.5}, {0.5, 0.0, -0.5}, {0.5, 0.0, 0.5}, {-0.5, 0.0, 0.5}};
/*
* Read the size attributes, the same as any other attribute. This
* is set by the wrapper modifier using the channel value.
*/
_size = dyna_Float (ATTRi_SIZE, 1.0);
if (mesh.test ())
{
point.fromMesh (mesh);
if (point.test ())
{
/*
* Update the point positions in the mesh. The
* cached points are updated.
*/
for (unsigned i = 0; i < 4; i++)
{
LXx_VSCL3 (pos, positions[i], _size);
point.Select (_cache[i]);
point.SetPos (pos);
}
mesh.SetMeshEdits (LXf_MESHEDIT_POSITION);
result = LXe_OK;
}
}
return result;
}
static LXtTagInfoDesc descInfo[];
LXtPointID _cache[4];
double _size;
};
/*
* The LXsMESHOP_PMODEL server tag will automatically convert the mesh operation into
* an item and modifier. Any attributes will be converted into channels.
*/
LXtTagInfoDesc MeshOp::descInfo[] =
{
{ LXsMESHOP_PMODEL, "." },
{ 0 }
};
void
initialize ()
{
MeshOp::initialize ();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment