-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy paththreadedSkinCluster.cpp
More file actions
339 lines (273 loc) · 9.36 KB
/
threadedSkinCluster.cpp
File metadata and controls
339 lines (273 loc) · 9.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/*
Benjamin A. Slack
Multi threaded skin cluster
CS5260
Basing this off the rudimentary cluster
coded by Autodesk as an example, I will
implement a multi-threaded version as an
introduction to the API.
*/
//
// File: threadedSkinCluster.cpp
//
// Description:
// Rudimentary implementation of a skin cluster.
//
#include <maya/MFnPlugin.h>
#include <maya/MTypeId.h>
#include <maya/MMatrixArray.h>
#include <maya/MStringArray.h>
#include <maya/MPxSkinCluster.h>
#include <maya/MItGeometry.h>
#include <maya/MPoint.h>
#include <maya/MFnMatrixData.h>
// adding my declarations
#include <maya/MThreadPool.h>
#include <maya/MGlobal.h>
#include <maya/MPointArray.h>
#include <maya/MPoint.h>
#include <maya/MProfiler.h>
#define NUM_TASK 16
#define NAME "threadedSkinCluster"
class threadedSkinCluster : public MPxSkinCluster
{
public:
static void* creator();
static MStatus initialize();
// Deformation function
//
virtual MStatus deform(MDataBlock& block,
MItGeometry& iter,
const MMatrix& mat,
unsigned int multiIndex);
static const MTypeId id;
static const int _profileCategory;
private:
static MThreadRetVal skin(void *);
static void decompose_skinning(void *, MThreadRootTask *);
static const MPxNode::SchedulingType schedulingType();
};
const MTypeId threadedSkinCluster::id( 0x00080031);
const int threadedSkinCluster::_profileCategory(MProfiler::addCategory(NAME));
const MPxNode::SchedulingType threadedSkinCluster::schedulingType(){
return MPxNode::kParallel;
}
void* threadedSkinCluster::creator()
{
return new threadedSkinCluster();
}
MStatus threadedSkinCluster::initialize()
{
// init the threadpool creation in the initialize
MStatus stat = MThreadPool::init();
if( MStatus::kSuccess != stat ) {
MString str = MString("Error creating threadpool");
MGlobal::displayError(str);
return MStatus::kFailure;
}
return MStatus::kSuccess;
}
// wrapper struct for data that the threads need
typedef struct _thread_data{
int threadNo;
long start_index;
long end_index;
int num_tm;
MMatrixArray* tm;
MPointArray* pts;
MDataBlock* block;
}thread_data;
//wrapper struct for data that the thread dispatcher needs
typedef struct _task_data{
long num_points;
int num_tm;
MMatrixArray* tm;
MPointArray* pts;
MDataBlock* block;
MItGeometry* iter;
}task_data;
MThreadRetVal threadedSkinCluster::skin(void *data){
thread_data *my_data = (thread_data *)data;
MMatrixArray tm = *my_data->tm;
MPointArray *pts = my_data->pts;
MDataBlock block = *my_data->block;
// there's no go way to get hold of the weights en masse, so
// a new handle seems like the way to go
MArrayDataHandle weight_list_handle = block.inputArrayValue(weightList);
weight_list_handle.jumpToArrayElement(my_data->start_index);
int skin_event = MProfiler::eventBegin(threadedSkinCluster::_profileCategory, MProfiler::kColorC_L2, "skin: loop");
for (long index = my_data->start_index; index <= my_data->end_index; index++){
MPoint skinned;
//get the weights for this index
MArrayDataHandle weights_handle = weight_list_handle.inputValue().child(weights);
//calculate the skinned points
for (int i = 0; i < my_data->num_tm; i++){
//get the weight for each tm
if (MS::kSuccess == weights_handle.jumpToElement(i)){
skinned += ((*pts)[index] * tm[i]) * weights_handle.inputValue().asDouble();
}
}
//set the point to the new position
(*pts)[index]=skinned;
//get next set of weights
weight_list_handle.next();
}
MProfiler::eventEnd(skin_event);
return (MThreadRetVal)0;
}
void threadedSkinCluster::decompose_skinning(void *data, MThreadRootTask *root){
task_data *my_data = (task_data *)data;
MItGeometry *iter = my_data->iter;
MPointArray *pts = my_data->pts;
thread_data tdata[NUM_TASK];
long start_index, end_index;
start_index = 0;
end_index = 0;
long interval = my_data->num_points/NUM_TASK;
long remainder = my_data->num_points%NUM_TASK;
int dispatch_event = MProfiler::eventBegin(threadedSkinCluster::_profileCategory, MProfiler::kColorC_L1, "decompose: loop");
for (int i = 0; i < NUM_TASK; i++){
//setup thread data
//calc starting indexes
if ((end_index != 0) || (i > 0)){
start_index = end_index + 1;
}
end_index = start_index + interval - 1;
if (i <= remainder){
end_index++;
}
//populate thread data struct
tdata[i].start_index = start_index;
tdata[i].end_index = end_index;
tdata[i].block = my_data->block;
tdata[i].num_tm = my_data->num_tm;
tdata[i].tm = my_data->tm;
tdata[i].pts = my_data->pts;
tdata[i].threadNo = i;
//spawn a thread
MThreadPool::createTask(threadedSkinCluster::skin, (void *)&tdata[i], root);
}
MProfiler::eventEnd(dispatch_event);
int thread_fire_event = MProfiler::eventBegin(threadedSkinCluster::_profileCategory, MProfiler::kColorC_L3, "decompose: exec");
// fire off the threads
MThreadPool::executeAndJoin(root);
MProfiler::eventEnd(thread_fire_event);
// set the geo positions
(*iter).setAllPositions((*pts));
}
MStatus
threadedSkinCluster::deform( MDataBlock& block,
MItGeometry& iter,
const MMatrix& /*m*/,
unsigned int multiIndex)
//
// Method: deform
//
// Description: Deforms the point with a simple smooth skinning algorithm
//
// Arguments:
// block : the datablock of the node
// iter : an iterator for the geometry to be deformed
// m : matrix to transform the point into world space
// multiIndex : the index of the geometry that we are deforming
//
//
{
MStatus returnStatus;
// get the influence transforms
//
// Maya API interacts with Datablocks using "handles"
// ArrayDataHandle grants access to node's array data
// elements
// in this case, the TM handle being pulled from the block is
// getting the skin cluster's matrix attr/plug passed to it
MArrayDataHandle transformsHandle = block.inputArrayValue( matrix );
// note: this is required to "prime the pump" on the handle
// without it, later calls to the handle's methods will fail
int numTransforms = transformsHandle.elementCount();
// if there's no transforms, we're done deforming
if ( numTransforms == 0 ) {
return MS::kSuccess;
}
// allocates space for an array of TMs
MMatrixArray transforms;
// populates the array pulling data from the handle block
for ( int i=0; i<numTransforms; ++i ) {
//breaking this down
//transformsHandle.inputValue().data(), this is getting the actual matrix data off the plug
//inputValue is returning a MDataHandle. The .data() method then gets the MObject Wrapper
//there's a lot of indirection in the Maya API apparently
//MFnMatrixData(tmhandle.input.data).matrix(), takes the MObject and attaches MMatrix
//functions to it, data method of tmHandle is returning a MObject wrapper
//MMatrix, .matrix() this actually gives the 4x4 TM.
transforms.append( MFnMatrixData( transformsHandle.inputValue().data() ).matrix() );
transformsHandle.next();
}
//same story as the previous, first we get the handle
//the count is already initialized
//this time we're getting the bindPreMatrix attr of the cluster
MArrayDataHandle bindHandle = block.inputArrayValue( bindPreMatrix );
if ( bindHandle.elementCount() > 0 ) {
for ( int i=0; i<numTransforms; ++i ) {
//breaking it down
//using the attr handle, we get a Data Handle, then get the MObject off that handle
//next we put the matrix functions on that MObject
//next we pre-multiply the pre-bind matrix to the existing tm and store it back on itself
transforms[i] = MFnMatrixData( bindHandle.inputValue().data() ).matrix() * transforms[i];
bindHandle.next();
}
}
//pulls the weights off the cluster
MArrayDataHandle weightListHandle = block.inputArrayValue( weightList );
if ( weightListHandle.elementCount() == 0 ) {
// no weights - nothing to do
return MS::kSuccess;
}
//the deform method gets an geo iterator passed in
//to thread this, that iterator is going to need
//to be broken up, somehow
//inital investigation points at the "all positions" method
//this loop would then need to be replaced with the thread
//pool tasks mapping
// this should take care of getting the points
MPointArray my_points = MPointArray();
MStatus stat = iter.allPositions(my_points);
long num_pts = my_points.length();
// prep data for task decomposition
task_data tdata;
tdata.block = █
tdata.num_points = num_pts;
tdata.pts = &my_points;
tdata.num_tm = numTransforms;
tdata.iter = &iter;
tdata.tm = &transforms;
//fire task decomposition
MThreadPool::newParallelRegion(threadedSkinCluster::decompose_skinning, (void *)&tdata);
return returnStatus;
}
// standard initialization procedures
//
MStatus initializePlugin( MObject obj )
{
MStatus result;
// ids the plugin to the system in maya
MFnPlugin plugin( obj, "Benjamin Slack", "0.1", "Any");
result = plugin.registerNode(
"threadedSkinCluster" ,
threadedSkinCluster::id ,
&threadedSkinCluster::creator ,
&threadedSkinCluster::initialize ,
MPxNode::kSkinCluster
);
// this is where the threadpool needs to be setup
return result;
}
MStatus uninitializePlugin( MObject obj )
{
MStatus result;
MFnPlugin plugin( obj );
result = plugin.deregisterNode( threadedSkinCluster::id );
// this is where the threadpool needs to be released
MThreadPool::release();
return result;
}