1 #include <memory>
2 
3 // TnzExt includes
4 #include "ext/plasticskeleton.h"
5 #include "ext/plasticskeletondeformation.h"
6 
7 // STD includes
8 #include <limits>
9 #include <map>
10 #include <algorithm>
11 
12 // Boost includes
13 #include <boost/multi_index_container.hpp>
14 #include <boost/multi_index/ordered_index.hpp>
15 #include <boost/multi_index/member.hpp>
16 
17 // Qt includes
18 #include <QMutex>
19 #include <QMutexLocker>
20 
21 #include "ext/plasticdeformerstorage.h"
22 
23 //***********************************************************************************************
24 //    Storage multi-index map  definition
25 //***********************************************************************************************
26 
27 namespace {
28 
29 typedef PlasticDeformerDataGroup DataGroup;
30 
31 //----------------------------------------------------------------------------------
32 
33 typedef std::pair<const SkD *, int> DeformedSkeleton;
34 
35 //----------------------------------------------------------------------------------
36 
37 struct Key {
38   const TMeshImage *m_mi;
39   DeformedSkeleton m_ds;
40 
41   std::shared_ptr<DataGroup> m_dataGroup;
42 
43 public:
Key__anon3bcd8a1b0111::Key44   Key(const TMeshImage *mi, const SkD *sd, int skelId)
45       : m_mi(mi), m_ds(sd, skelId), m_dataGroup() {}
46 
operator <__anon3bcd8a1b0111::Key47   bool operator<(const Key &other) const {
48     return (m_mi < other.m_mi) ||
49            ((!(other.m_mi < m_mi)) && (m_ds < other.m_ds));
50   }
51 };
52 
53 //----------------------------------------------------------------------------------
54 
55 using namespace boost::multi_index;
56 
57 typedef boost::multi_index_container<
58     Key, indexed_by<
59 
60              ordered_unique<identity<Key>>,
61              ordered_non_unique<tag<TMeshImage>,
62                                 member<Key, const TMeshImage *, &Key::m_mi>>,
63              ordered_non_unique<tag<DeformedSkeleton>,
64                                 member<Key, DeformedSkeleton, &Key::m_ds>>
65 
66              >>
67     DeformersSet;
68 
69 typedef DeformersSet::nth_index<0>::type DeformersByKey;
70 typedef DeformersSet::index<TMeshImage>::type DeformersByMeshImage;
71 typedef DeformersSet::index<DeformedSkeleton>::type DeformersByDeformedSkeleton;
72 
73 }  // namespace
74 
75 //***********************************************************************************************
76 //    Initialization stage  functions
77 //***********************************************************************************************
78 
79 namespace {
80 
initializeSO(PlasticDeformerData & data,const TTextureMeshP & mesh)81 void initializeSO(PlasticDeformerData &data, const TTextureMeshP &mesh) {
82   data.m_so.reset(new double[mesh->facesCount()]);
83 }
84 
85 //----------------------------------------------------------------------------------
86 
initializeDeformerData(PlasticDeformerData & data,const TTextureMeshP & mesh)87 void initializeDeformerData(PlasticDeformerData &data,
88                             const TTextureMeshP &mesh) {
89   initializeSO(data, mesh);  // Allocates SO data
90 
91   // Also, allocate suitable input-output arrays for the deformation
92   data.m_output.reset(new double[2 * mesh->verticesCount()]);
93 }
94 
95 //----------------------------------------------------------------------------------
96 
initializeDeformersData(DataGroup * group,const TMeshImage * meshImage)97 void initializeDeformersData(DataGroup *group, const TMeshImage *meshImage) {
98   group->m_datas.reset(new PlasticDeformerData[meshImage->meshes().size()]);
99 
100   // Push a PlasticDeformer for each mesh in the image
101   const std::vector<TTextureMeshP> &meshes = meshImage->meshes();
102   int fTotal                               = 0;  // Also count total # of faces
103 
104   int m, mCount = meshes.size();
105   for (m = 0; m != mCount; ++m) {
106     fTotal += meshes[m]->facesCount();
107     initializeDeformerData(group->m_datas[m], meshes[m]);
108   }
109 
110   // Initialize the vector of sorted faces
111   std::vector<std::pair<int, int>> &sortedFaces = group->m_sortedFaces;
112 
113   sortedFaces.reserve(fTotal);
114   for (m = 0; m != mCount; ++m) {
115     const TTextureMesh &mesh = *meshes[m];
116 
117     int f, fCount = mesh.facesCount();
118     for (f = 0; f != fCount; ++f) sortedFaces.push_back(std::make_pair(f, m));
119   }
120 }
121 
122 }  // namespace
123 
124 //***********************************************************************************************
125 //    Handle processing  functions
126 //***********************************************************************************************
127 
128 namespace {
129 
transformHandles(std::vector<PlasticHandle> & handles,const TAffine & aff)130 void transformHandles(std::vector<PlasticHandle> &handles, const TAffine &aff) {
131   // Transforms handles through deformAff AND applies mi's dpi scale inverse
132   std::vector<PlasticHandle>::size_type h, hCount = handles.size();
133   for (h = 0; h != hCount; ++h) handles[h].m_pos = aff * handles[h].m_pos;
134 }
135 
136 //----------------------------------------------------------------------------------
137 
transformHandles(std::vector<TPointD> & handles,const TAffine & aff)138 void transformHandles(std::vector<TPointD> &handles, const TAffine &aff) {
139   // Transforms handles through deformAff AND applies mi's dpi scale inverse
140   std::vector<PlasticHandle>::size_type h, hCount = handles.size();
141   for (h = 0; h != hCount; ++h) handles[h] = aff * handles[h];
142 }
143 
144 //----------------------------------------------------------------------------------
145 
processHandles(DataGroup * group,double frame,const TMeshImage * meshImage,const SkD * sd,int skelId,const TAffine & deformationAffine)146 void processHandles(DataGroup *group, double frame, const TMeshImage *meshImage,
147                     const SkD *sd, int skelId,
148                     const TAffine &deformationAffine) {
149   assert(sd);
150 
151   const PlasticSkeletonP &skeleton = sd->skeleton(skelId);
152 
153   if (!skeleton || skeleton->verticesCount() == 0) {
154     group->m_handles.clear();
155     group->m_dstHandles.clear();
156 
157     group->m_compiled |= PlasticDeformerStorage::HANDLES;
158     group->m_upToDate |= PlasticDeformerStorage::HANDLES;
159 
160     return;
161   }
162 
163   int mCount = meshImage->meshes().size();
164 
165   if (!(group->m_upToDate & PlasticDeformerStorage::HANDLES)) {
166     // Compile handles if necessary
167     if (!(group->m_compiled & PlasticDeformerStorage::HANDLES)) {
168       // Build and transform handles
169       group->m_handles = skeleton->verticesToHandles();
170       ::transformHandles(group->m_handles, deformationAffine);
171 
172       // Prepare a vector for handles' face hints
173       for (int m = 0; m != mCount; ++m)
174         group->m_datas[m].m_faceHints.resize(group->m_handles.size(), -1);
175 
176       group->m_compiled |= PlasticDeformerStorage::HANDLES;
177     }
178 
179     // Then, build destination handles
180     PlasticSkeleton
181         deformedSkeleton;  // NOTE: Could this be moved to the group as well?
182     sd->storeDeformedSkeleton(skelId, frame, deformedSkeleton);
183 
184     // Copy deformed skeleton data into input deformation parameters
185     group->m_dstHandles = std::vector<TPointD>(
186         deformedSkeleton.vertices().begin(), deformedSkeleton.vertices().end());
187     ::transformHandles(group->m_dstHandles, deformationAffine);
188 
189     group->m_upToDate |= PlasticDeformerStorage::HANDLES;
190   }
191 }
192 
193 }  // namespace
194 
195 //***********************************************************************************************
196 //    Stacking Order processing  functions
197 //***********************************************************************************************
198 
199 namespace {
200 
updateHandlesSO(DataGroup * group,const SkD * sd,int skelId,double frame)201 bool updateHandlesSO(DataGroup *group, const SkD *sd, int skelId,
202                      double frame) {
203   assert(sd);
204 
205   const PlasticSkeletonP &skeleton = sd->skeleton(skelId);
206 
207   if (!skeleton || skeleton->verticesCount() == 0) {
208     group->m_soMin = group->m_soMax = 0.0;
209     return false;
210   }
211 
212   // Copy SO values to data's handles
213   // Return whether values changed with respect to previous ones
214   bool changed = false;
215 
216   assert(group->m_handles.size() == skeleton->verticesCount());
217 
218   int h, hCount = group->m_handles.size();
219   {
220     tcg::list<PlasticSkeletonVertex>::iterator vt =
221         skeleton->vertices().begin();
222 
223     for (h = 0; h != hCount; ++h, ++vt) {
224       const SkVD *vd = sd->vertexDeformation(vt->name());
225       if (!vd) continue;
226 
227       double so = vd->m_params[SkVD::SO]->getValue(frame);
228 
229       PlasticHandle &handle = group->m_handles[h];
230       if (handle.m_so != so) {
231         group->m_handles[h].m_so = so;
232         changed                  = true;
233       }
234     }
235   }
236 
237   if (changed) {
238     // Rebuild SO minmax
239     group->m_soMax = -(group->m_soMin = (std::numeric_limits<double>::max)());
240 
241     for (h = 0; h != hCount; ++h) {
242       const double &so = group->m_handles[h].m_so;
243 
244       group->m_soMin = std::min(group->m_soMin, so);
245       group->m_soMax = std::max(group->m_soMax, so);
246     }
247   }
248 
249   return changed;
250 }
251 
252 //----------------------------------------------------------------------------------
253 
interpolateSO(DataGroup * group,const TMeshImage * meshImage)254 void interpolateSO(DataGroup *group, const TMeshImage *meshImage) {
255   int m, mCount = meshImage->meshes().size();
256 
257   if (group->m_handles.size() == 0) {
258     // No handles case, fill in with 0s
259 
260     for (m = 0; m != mCount; ++m) {
261       const TTextureMesh &mesh  = *meshImage->meshes()[m];
262       PlasticDeformerData &data = group->m_datas[m];
263 
264       std::fill(data.m_so.get(), data.m_so.get() + mesh.facesCount(), 0.0);
265     }
266 
267     return;
268   }
269 
270   // Apply handles' SO values to each mesh
271   for (m = 0; m != mCount; ++m) {
272     const TTextureMesh &mesh  = *meshImage->meshes()[m];
273     PlasticDeformerData &data = group->m_datas[m];
274 
275     // Interpolate so values
276     std::unique_ptr<double[]> verticesSO(new double[mesh.verticesCount()]);
277 
278     ::buildSO(verticesSO.get(), mesh, group->m_handles,
279               &data.m_faceHints.front());
280 
281     // Make the mean of each face's vertex values and store that
282     int f, fCount = mesh.facesCount();
283     for (f = 0; f != fCount; ++f) {
284       int v0, v1, v2;
285       mesh.faceVertices(f, v0, v1, v2);
286 
287       data.m_so[f] = (verticesSO[v0] + verticesSO[v1] + verticesSO[v2]) / 3.0;
288     }
289   }
290 }
291 
292 //----------------------------------------------------------------------------------
293 
294 struct FaceLess {
295   const PlasticDeformerDataGroup *m_group;
296 
297 public:
FaceLess__anon3bcd8a1b0411::FaceLess298   FaceLess(const PlasticDeformerDataGroup *group) : m_group(group) {}
299 
operator ()__anon3bcd8a1b0411::FaceLess300   bool operator()(const std::pair<int, int> &a, const std::pair<int, int> &b) {
301     return (m_group->m_datas[a.second].m_so[a.first] <
302             m_group->m_datas[b.second].m_so[b.first]);
303   }
304 };
305 
306 // Must be invoked after updateSO
updateSortedFaces(PlasticDeformerDataGroup * group)307 void updateSortedFaces(PlasticDeformerDataGroup *group) {
308   FaceLess comp(group);
309   std::sort(group->m_sortedFaces.begin(), group->m_sortedFaces.end(), comp);
310 }
311 
312 //----------------------------------------------------------------------------------
313 
processSO(DataGroup * group,double frame,const TMeshImage * meshImage,const SkD * sd,int skelId,const TAffine & deformationAffine)314 void processSO(DataGroup *group, double frame, const TMeshImage *meshImage,
315                const SkD *sd, int skelId, const TAffine &deformationAffine) {
316   // SO re-interpolate values along the mesh if either:
317   //  1. Recompilation was requested (ie some vertex may have been
318   //  added/removed)
319   //  2. OR the value of one of the handle has changed
320 
321   bool interpolate = !(group->m_compiled & PlasticDeformerStorage::SO);
322 
323   if (!(group->m_upToDate &
324         PlasticDeformerStorage::SO))  // implied by (interpolate == true)
325   {
326     interpolate = updateHandlesSO(group, sd, skelId, frame) ||
327                   interpolate;  // Order is IMPORTANT
328 
329     if (interpolate) {
330       interpolateSO(group, meshImage);
331       updateSortedFaces(group);
332     }
333 
334     group->m_compiled |= PlasticDeformerStorage::SO;
335     group->m_upToDate |= PlasticDeformerStorage::SO;
336   }
337 }
338 
339 }  // namespace
340 
341 //***********************************************************************************************
342 //    Mesh Deform processing  functions
343 //***********************************************************************************************
344 
345 namespace {
346 
processMesh(DataGroup * group,double frame,const TMeshImage * meshImage,const SkD * sd,int skelId,const TAffine & deformationAffine)347 void processMesh(DataGroup *group, double frame, const TMeshImage *meshImage,
348                  const SkD *sd, int skelId, const TAffine &deformationAffine) {
349   if (!(group->m_upToDate & PlasticDeformerStorage::MESH)) {
350     int m, mCount = meshImage->meshes().size();
351 
352     if (!(group->m_compiled & PlasticDeformerStorage::MESH)) {
353       for (m = 0; m != mCount; ++m) {
354         const TTextureMeshP &mesh = meshImage->meshes()[m];
355         PlasticDeformerData &data = group->m_datas[m];
356 
357         data.m_deformer.initialize(mesh);
358         data.m_deformer.compile(
359             group->m_handles,
360             data.m_faceHints.empty() ? 0 : &data.m_faceHints.front());
361         data.m_deformer.releaseInitializedData();
362       }
363 
364       group->m_compiled |= PlasticDeformerStorage::MESH;
365     }
366 
367     const TPointD *dstHandlePos =
368         group->m_dstHandles.empty() ? 0 : &group->m_dstHandles.front();
369 
370     for (m = 0; m != mCount; ++m) {
371       PlasticDeformerData &data = group->m_datas[m];
372       data.m_deformer.deform(dstHandlePos, data.m_output.get());
373     }
374 
375     group->m_upToDate |= PlasticDeformerStorage::MESH;
376   }
377 }
378 
379 }  // namespace
380 
381 //***********************************************************************************************
382 //    PlasticDeformerData  implementation
383 //***********************************************************************************************
384 
PlasticDeformerData()385 PlasticDeformerData::PlasticDeformerData() {}
386 
387 //----------------------------------------------------------------------------------
388 
~PlasticDeformerData()389 PlasticDeformerData::~PlasticDeformerData() {}
390 
391 //***********************************************************************************************
392 //    PlasticDeformerDataGroup  implementation
393 //***********************************************************************************************
394 
PlasticDeformerDataGroup()395 PlasticDeformerDataGroup::PlasticDeformerDataGroup()
396     : m_datas()
397     , m_compiled(PlasticDeformerStorage::NONE)
398     , m_upToDate(PlasticDeformerStorage::NONE)
399     , m_outputFrame((std::numeric_limits<double>::max)())
400     , m_soMin()
401     , m_soMax() {}
402 
403 //----------------------------------------------------------------------------------
404 
~PlasticDeformerDataGroup()405 PlasticDeformerDataGroup::~PlasticDeformerDataGroup() {}
406 
407 //***********************************************************************************************
408 //    PlasticDeformerStorage::Imp  definition
409 //***********************************************************************************************
410 
411 class PlasticDeformerStorage::Imp {
412 public:
413   QMutex m_mutex;            //!< Access mutex - needed for thread-safety
414   DeformersSet m_deformers;  //!< Set of deformers, ordered by mesh image,
415                              //! deformation, and affine.
416 
417 public:
Imp()418   Imp() : m_mutex(QMutex::Recursive) {}
419 };
420 
421 //***********************************************************************************************
422 //    PlasticDeformerStorage  implementation
423 //***********************************************************************************************
424 
PlasticDeformerStorage()425 PlasticDeformerStorage::PlasticDeformerStorage() : m_imp(new Imp) {}
426 
427 //----------------------------------------------------------------------------------
428 
~PlasticDeformerStorage()429 PlasticDeformerStorage::~PlasticDeformerStorage() {}
430 
431 //----------------------------------------------------------------------------------
432 
instance()433 PlasticDeformerStorage *PlasticDeformerStorage::instance() {
434   static PlasticDeformerStorage theInstance;
435   return &theInstance;
436 }
437 
438 //----------------------------------------------------------------------------------
439 
deformerData(const TMeshImage * meshImage,const PlasticSkeletonDeformation * deformation,int skelId)440 PlasticDeformerDataGroup *PlasticDeformerStorage::deformerData(
441     const TMeshImage *meshImage, const PlasticSkeletonDeformation *deformation,
442     int skelId) {
443   QMutexLocker locker(&m_imp->m_mutex);
444 
445   // Search for the corresponding deformation in the storage
446   Key key(meshImage, deformation, skelId);
447 
448   DeformersByKey::iterator dt = m_imp->m_deformers.find(key);
449   if (dt == m_imp->m_deformers.end()) {
450     // No deformer was found. Allocate it.
451     key.m_dataGroup = std::make_shared<PlasticDeformerDataGroup>();
452     initializeDeformersData(key.m_dataGroup.get(), meshImage);
453 
454     dt = m_imp->m_deformers.insert(key).first;
455   }
456 
457   return dt->m_dataGroup.get();
458 }
459 
460 //----------------------------------------------------------------------------------
461 
process(double frame,const TMeshImage * meshImage,const PlasticSkeletonDeformation * deformation,int skelId,const TAffine & skeletonAffine,DataType dataType)462 const PlasticDeformerDataGroup *PlasticDeformerStorage::process(
463     double frame, const TMeshImage *meshImage,
464     const PlasticSkeletonDeformation *deformation, int skelId,
465     const TAffine &skeletonAffine, DataType dataType) {
466   QMutexLocker locker(&m_imp->m_mutex);
467 
468   PlasticDeformerDataGroup *group =
469       deformerData(meshImage, deformation, skelId);
470 
471   // On-the-fly checks for data invalidation
472   if (group->m_skeletonAffine != skeletonAffine) {
473     group->m_upToDate       = NONE;
474     group->m_compiled       = NONE;
475     group->m_skeletonAffine = skeletonAffine;
476   }
477 
478   if (group->m_outputFrame != frame) {
479     group->m_upToDate    = NONE;
480     group->m_outputFrame = frame;
481   }
482 
483   bool doMesh    = (dataType & MESH);
484   bool doSO      = (dataType & SO) || doMesh;
485   bool doHandles = (bool)dataType;
486 
487   // Process data
488   if (doHandles)
489     processHandles(group, frame, meshImage, deformation, skelId,
490                    skeletonAffine);
491 
492   if (doSO)
493     processSO(group, frame, meshImage, deformation, skelId, skeletonAffine);
494 
495   if (doMesh)
496     processMesh(group, frame, meshImage, deformation, skelId, skeletonAffine);
497 
498   return group;
499 }
500 
501 //----------------------------------------------------------------------------------
502 
processOnce(double frame,const TMeshImage * meshImage,const PlasticSkeletonDeformation * deformation,int skelId,const TAffine & skeletonAffine,DataType dataType)503 const PlasticDeformerDataGroup *PlasticDeformerStorage::processOnce(
504     double frame, const TMeshImage *meshImage,
505     const PlasticSkeletonDeformation *deformation, int skelId,
506     const TAffine &skeletonAffine, DataType dataType) {
507   PlasticDeformerDataGroup *group = new PlasticDeformerDataGroup;
508   initializeDeformersData(group, meshImage);
509 
510   bool doMesh    = (dataType & MESH);
511   bool doSO      = (dataType & SO) || doMesh;
512   bool doHandles = (bool)dataType;
513 
514   // Process data
515   if (doHandles)
516     processHandles(group, frame, meshImage, deformation, skelId,
517                    skeletonAffine);
518 
519   if (doSO)
520     processSO(group, frame, meshImage, deformation, skelId, skeletonAffine);
521 
522   if (doMesh)
523     processMesh(group, frame, meshImage, deformation, skelId, skeletonAffine);
524 
525   return group;
526 }
527 
528 //----------------------------------------------------------------------------------
529 
invalidateMeshImage(const TMeshImage * meshImage,int recompiledData)530 void PlasticDeformerStorage::invalidateMeshImage(const TMeshImage *meshImage,
531                                                  int recompiledData) {
532   QMutexLocker locker(&m_imp->m_mutex);
533 
534   DeformersByMeshImage &deformers = m_imp->m_deformers.get<TMeshImage>();
535 
536   DeformersByMeshImage::iterator dBegin(deformers.lower_bound(meshImage));
537   if (dBegin == deformers.end()) return;
538 
539   DeformersByMeshImage::iterator dt, dEnd(deformers.upper_bound(meshImage));
540   for (dt = dBegin; dt != dEnd; ++dt) {
541     dt->m_dataGroup->m_outputFrame =
542         (std::numeric_limits<double>::max)();  // Schedule for redeformation
543     if (recompiledData)
544       dt->m_dataGroup->m_compiled &=
545           ~recompiledData;  // Schedule for recompilation, too
546   }
547 }
548 
549 //----------------------------------------------------------------------------------
550 
invalidateSkeleton(const PlasticSkeletonDeformation * deformation,int skelId,int recompiledData)551 void PlasticDeformerStorage::invalidateSkeleton(
552     const PlasticSkeletonDeformation *deformation, int skelId,
553     int recompiledData) {
554   QMutexLocker locker(&m_imp->m_mutex);
555 
556   DeformedSkeleton ds(deformation, skelId);
557 
558   DeformersByDeformedSkeleton &deformers =
559       m_imp->m_deformers.get<DeformedSkeleton>();
560 
561   DeformersByDeformedSkeleton::iterator dBegin(deformers.lower_bound(ds));
562   if (dBegin == deformers.end()) return;
563 
564   DeformersByDeformedSkeleton::iterator dt, dEnd(deformers.upper_bound(ds));
565   for (dt = dBegin; dt != dEnd; ++dt) {
566     dt->m_dataGroup->m_outputFrame =
567         (std::numeric_limits<double>::max)();  // Schedule for redeformation
568     if (recompiledData)
569       dt->m_dataGroup->m_compiled &=
570           ~recompiledData;  // Schedule for recompilation, too
571   }
572 }
573 
574 //----------------------------------------------------------------------------------
575 
invalidateDeformation(const PlasticSkeletonDeformation * deformation,int recompiledData)576 void PlasticDeformerStorage::invalidateDeformation(
577     const PlasticSkeletonDeformation *deformation, int recompiledData) {
578   QMutexLocker locker(&m_imp->m_mutex);
579 
580   DeformersByDeformedSkeleton &deformers =
581       m_imp->m_deformers.get<DeformedSkeleton>();
582 
583   DeformedSkeleton dsBegin(deformation, -(std::numeric_limits<int>::max)()),
584       dsEnd(deformation, (std::numeric_limits<int>::max)());
585 
586   DeformersByDeformedSkeleton::iterator dBegin(deformers.lower_bound(dsBegin));
587   DeformersByDeformedSkeleton::iterator dEnd(deformers.upper_bound(dsEnd));
588 
589   if (dBegin == dEnd) return;
590 
591   for (DeformersByDeformedSkeleton::iterator dt = dBegin; dt != dEnd; ++dt) {
592     dt->m_dataGroup->m_outputFrame =
593         (std::numeric_limits<double>::max)();  // Schedule for redeformation
594     if (recompiledData)
595       dt->m_dataGroup->m_compiled &=
596           ~recompiledData;  // Schedule for recompilation, too
597   }
598 }
599 
600 //----------------------------------------------------------------------------------
601 
releaseMeshData(const TMeshImage * meshImage)602 void PlasticDeformerStorage::releaseMeshData(const TMeshImage *meshImage) {
603   QMutexLocker locker(&m_imp->m_mutex);
604 
605   DeformersByMeshImage &deformers = m_imp->m_deformers.get<TMeshImage>();
606 
607   DeformersByMeshImage::iterator dBegin(deformers.lower_bound(meshImage));
608   if (dBegin == deformers.end()) return;
609 
610   deformers.erase(dBegin, deformers.upper_bound(meshImage));
611 }
612 
613 //----------------------------------------------------------------------------------
614 
releaseSkeletonData(const SkD * deformation,int skelId)615 void PlasticDeformerStorage::releaseSkeletonData(const SkD *deformation,
616                                                  int skelId) {
617   QMutexLocker locker(&m_imp->m_mutex);
618 
619   DeformedSkeleton ds(deformation, skelId);
620 
621   DeformersByDeformedSkeleton &deformers =
622       m_imp->m_deformers.get<DeformedSkeleton>();
623 
624   DeformersByDeformedSkeleton::iterator dBegin(deformers.lower_bound(ds));
625   if (dBegin == deformers.end()) return;
626 
627   deformers.erase(dBegin, deformers.upper_bound(ds));
628 }
629 
630 //----------------------------------------------------------------------------------
631 
releaseDeformationData(const SkD * deformation)632 void PlasticDeformerStorage::releaseDeformationData(const SkD *deformation) {
633   QMutexLocker locker(&m_imp->m_mutex);
634 
635   DeformersByDeformedSkeleton &deformers =
636       m_imp->m_deformers.get<DeformedSkeleton>();
637 
638   DeformedSkeleton dsBegin(deformation, -(std::numeric_limits<int>::max)()),
639       dsEnd(deformation, (std::numeric_limits<int>::max)());
640 
641   DeformersByDeformedSkeleton::iterator dBegin(deformers.lower_bound(dsBegin));
642   DeformersByDeformedSkeleton::iterator dEnd(deformers.upper_bound(dsEnd));
643 
644   if (dBegin == dEnd) return;
645 
646   deformers.erase(dBegin, dEnd);
647 }
648 
649 //----------------------------------------------------------------------------------
650 
clear()651 void PlasticDeformerStorage::clear() {
652   QMutexLocker locker(&m_imp->m_mutex);
653 
654   m_imp->m_deformers.clear();
655 }
656