Skip to content

Instantly share code, notes, and snippets.

@mattcox
Last active August 26, 2021 14:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattcox/e684fd121446884300ae to your computer and use it in GitHub Desktop.
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
#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