Last active
August 26, 2021 14:27
-
-
Save mattcox/e684fd121446884300ae to your computer and use it in GitHub Desktop.
Example of a simple selection operation for the Modo procedural modelling system that selects every other polygon. It demonstrates how to use thread slots, to allow selection to be evaluated from multiple threads
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/lx_thread.hpp> | |
#include <lxsdk/lxu_attributes.hpp> | |
#define SERVER_NAME "pmodel.selectEveryOther" | |
/* | |
* The Selection Operation is evaluated in parallel from multiple threads. As | |
* each thread could potentially be querying the mesh elements at the same time, | |
* we need to provide each thread with a element accessor and store the accessor | |
* in local thread storage. When the mesh changes, these are invalidated and we | |
* update the accessor to point at the new mesh. | |
*/ | |
class ThreadData | |
{ | |
public: | |
bool | |
Init ( | |
CLxUser_Mesh &mesh) | |
{ | |
if (!mesh.test ()) | |
return false; | |
if (!_mesh.test () || _mesh != mesh) | |
{ | |
_polygon.clear (); | |
_mesh.copy (mesh); | |
} | |
if (_mesh.test ()) | |
{ | |
if (!_polygon.test ()) | |
_polygon.fromMesh (_mesh); | |
return true; | |
} | |
return false; | |
} | |
CLxUser_Polygon _polygon; | |
private: | |
CLxUser_Mesh _mesh; | |
}; | |
/* | |
* The Thread Client is completely generic. It just creates the ThreadData when | |
* requested. | |
*/ | |
class ThreadClient : | |
public CLxImpl_ThreadSlotClient, | |
public CLxSingletonPolymorph | |
{ | |
public: | |
LXxSINGLETON_METHOD | |
ThreadClient () | |
{ | |
AddInterface (new CLxIfc_ThreadSlotClient <ThreadClient>); | |
} | |
LxResult | |
tsc_Alloc ( | |
void **value) | |
{ | |
ThreadData *data = new ThreadData; | |
if (!data) | |
return LXe_OUTOFMEMORY; | |
value[0] = data; | |
return LXe_OK; | |
} | |
LxResult | |
tsc_Free ( | |
void *value) | |
{ | |
ThreadData *data = NULL; | |
if (value) | |
{ | |
data = (ThreadData*) value; | |
delete data; | |
} | |
return LXe_OK; | |
} | |
}; | |
static ThreadClient sThreadClient; | |
/* | |
* The Selection Operation will be spawned by the procedural system. SetMesh will be called | |
* first, and then TestPolygon will be called for every element on the mesh. This testing | |
* will be threaded. | |
*/ | |
class SelOp : | |
public CLxImpl_SelectionOperation, | |
public CLxDynamicAttributes | |
{ | |
public: | |
static void | |
initialize () | |
{ | |
CLxGenericPolymorph *srv = NULL; | |
srv = new CLxPolymorph <SelOp>; | |
srv->AddInterface (new CLxIfc_SelectionOperation <SelOp>); | |
srv->AddInterface (new CLxIfc_Attributes <SelOp>); | |
srv->AddInterface (new CLxIfc_StaticDesc <SelOp>); | |
lx::AddServer (SERVER_NAME, srv); | |
} | |
SelOp () | |
{ | |
_thr_svc.NewSlot (_thr_slot, sThreadClient); | |
} | |
LxResult | |
selop_TestType ( | |
LXtID4 type) | |
{ | |
/* | |
* Returns True for any supported types. For simplicity, we'll only | |
* support polygons. | |
*/ | |
return type == LXiSEL_POLYGON ? LXe_TRUE : LXe_FALSE; | |
} | |
LxResult | |
selop_SetMesh ( | |
ILxUnknownID mesh) | |
{ | |
/* | |
* The Selection Operation may be evaluated in parallel from multiple | |
* threads. As each thread will want it's own Polygon interface, we | |
* cache the ILxMesh so that it can be used to spawn the Polygon | |
* interface for each thread. | |
*/ | |
return _mesh.set (mesh) ? LXe_OK : LXe_FAILED; | |
} | |
LxResult | |
selop_TestPolygon ( | |
LXtPolygonID polygon) | |
{ | |
/* | |
* In the test polygon function, we'll test if the polygon index is | |
* odd. The polygon interface we test is stored in thread data, this | |
* enables each thread to have access to it's own interface. | |
*/ | |
ThreadData *data = NULL; | |
int index = 0; | |
if (_thr_slot.test ()) | |
_thr_slot.Get ((void**)&data); | |
if (data && _mesh.test ()) | |
{ | |
if (data->Init (_mesh) && data->_polygon.test ()) | |
{ | |
data->_polygon.Select (polygon); | |
data->_polygon.Index (&index); | |
return index % 2 ? LXe_TRUE : LXe_FALSE; | |
} | |
} | |
return LXe_FALSE; | |
} | |
static LXtTagInfoDesc descInfo[]; | |
private: | |
CLxUser_Mesh _mesh; | |
CLxUser_ThreadSlot _thr_slot; | |
CLxUser_ThreadService _thr_svc; | |
}; | |
/* | |
* The LXsSELOP_PMODEL server tag will automatically convert the selection operation | |
* into an item and modifier. Any attributes will be converted into channels. | |
*/ | |
LXtTagInfoDesc SelOp::descInfo[] = | |
{ | |
{ LXsMESHOP_PMODEL, "." }, | |
{ 0 } | |
}; | |
void | |
initialize () | |
{ | |
SelOp::initialize (); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment