Last active
July 30, 2022 20:27
-
-
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…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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