#include // TnzExt includes #include "ext/plasticskeleton.h" #include "ext/plasticskeletondeformation.h" // STD includes #include #include #include // Boost includes #include #include #include // Qt includes #include #include #include "ext/plasticdeformerstorage.h" //*********************************************************************************************** // Storage multi-index map definition //*********************************************************************************************** namespace { typedef PlasticDeformerDataGroup DataGroup; //---------------------------------------------------------------------------------- typedef std::pair DeformedSkeleton; //---------------------------------------------------------------------------------- struct Key { const TMeshImage *m_mi; DeformedSkeleton m_ds; std::shared_ptr m_dataGroup; public: Key(const TMeshImage *mi, const SkD *sd, int skelId) : m_mi(mi), m_ds(sd, skelId), m_dataGroup() {} bool operator<(const Key &other) const { return (m_mi < other.m_mi) || ((!(other.m_mi < m_mi)) && (m_ds < other.m_ds)); } }; //---------------------------------------------------------------------------------- using namespace boost::multi_index; typedef boost::multi_index_container< Key, indexed_by< ordered_unique>, ordered_non_unique, member>, ordered_non_unique, member> >> DeformersSet; typedef DeformersSet::nth_index<0>::type DeformersByKey; typedef DeformersSet::index::type DeformersByMeshImage; typedef DeformersSet::index::type DeformersByDeformedSkeleton; } // namespace //*********************************************************************************************** // Initialization stage functions //*********************************************************************************************** namespace { void initializeSO(PlasticDeformerData &data, const TTextureMeshP &mesh) { data.m_so.reset(new double[mesh->facesCount()]); } //---------------------------------------------------------------------------------- void initializeDeformerData(PlasticDeformerData &data, const TTextureMeshP &mesh) { initializeSO(data, mesh); // Allocates SO data // Also, allocate suitable input-output arrays for the deformation data.m_output.reset(new double[2 * mesh->verticesCount()]); } //---------------------------------------------------------------------------------- void initializeDeformersData(DataGroup *group, const TMeshImage *meshImage) { group->m_datas.reset(new PlasticDeformerData[meshImage->meshes().size()]); // Push a PlasticDeformer for each mesh in the image const std::vector &meshes = meshImage->meshes(); int fTotal = 0; // Also count total # of faces int m, mCount = meshes.size(); for (m = 0; m != mCount; ++m) { fTotal += meshes[m]->facesCount(); initializeDeformerData(group->m_datas[m], meshes[m]); } // Initialize the vector of sorted faces std::vector> &sortedFaces = group->m_sortedFaces; sortedFaces.reserve(fTotal); for (m = 0; m != mCount; ++m) { const TTextureMesh &mesh = *meshes[m]; int f, fCount = mesh.facesCount(); for (f = 0; f != fCount; ++f) sortedFaces.push_back(std::make_pair(f, m)); } } } // namespace //*********************************************************************************************** // Handle processing functions //*********************************************************************************************** namespace { void transformHandles(std::vector &handles, const TAffine &aff) { // Transforms handles through deformAff AND applies mi's dpi scale inverse std::vector::size_type h, hCount = handles.size(); for (h = 0; h != hCount; ++h) handles[h].m_pos = aff * handles[h].m_pos; } //---------------------------------------------------------------------------------- void transformHandles(std::vector &handles, const TAffine &aff) { // Transforms handles through deformAff AND applies mi's dpi scale inverse std::vector::size_type h, hCount = handles.size(); for (h = 0; h != hCount; ++h) handles[h] = aff * handles[h]; } //---------------------------------------------------------------------------------- void processHandles(DataGroup *group, double frame, const TMeshImage *meshImage, const SkD *sd, int skelId, const TAffine &deformationAffine) { assert(sd); const PlasticSkeletonP &skeleton = sd->skeleton(skelId); if (!skeleton || skeleton->verticesCount() == 0) { group->m_handles.clear(); group->m_dstHandles.clear(); group->m_compiled |= PlasticDeformerStorage::HANDLES; group->m_upToDate |= PlasticDeformerStorage::HANDLES; return; } int mCount = meshImage->meshes().size(); if (!(group->m_upToDate & PlasticDeformerStorage::HANDLES)) { // Compile handles if necessary if (!(group->m_compiled & PlasticDeformerStorage::HANDLES)) { // Build and transform handles group->m_handles = skeleton->verticesToHandles(); ::transformHandles(group->m_handles, deformationAffine); // Prepare a vector for handles' face hints for (int m = 0; m != mCount; ++m) group->m_datas[m].m_faceHints.resize(group->m_handles.size(), -1); group->m_compiled |= PlasticDeformerStorage::HANDLES; } // Then, build destination handles PlasticSkeleton deformedSkeleton; // NOTE: Could this be moved to the group as well? sd->storeDeformedSkeleton(skelId, frame, deformedSkeleton); // Copy deformed skeleton data into input deformation parameters group->m_dstHandles = std::vector( deformedSkeleton.vertices().begin(), deformedSkeleton.vertices().end()); ::transformHandles(group->m_dstHandles, deformationAffine); group->m_upToDate |= PlasticDeformerStorage::HANDLES; } } } // namespace //*********************************************************************************************** // Stacking Order processing functions //*********************************************************************************************** namespace { bool updateHandlesSO(DataGroup *group, const SkD *sd, int skelId, double frame) { assert(sd); const PlasticSkeletonP &skeleton = sd->skeleton(skelId); if (!skeleton || skeleton->verticesCount() == 0) { group->m_soMin = group->m_soMax = 0.0; return false; } // Copy SO values to data's handles // Return whether values changed with respect to previous ones bool changed = false; assert(group->m_handles.size() == skeleton->verticesCount()); int h, hCount = group->m_handles.size(); { tcg::list::iterator vt = skeleton->vertices().begin(); for (h = 0; h != hCount; ++h, ++vt) { const SkVD *vd = sd->vertexDeformation(vt->name()); if (!vd) continue; double so = vd->m_params[SkVD::SO]->getValue(frame); PlasticHandle &handle = group->m_handles[h]; if (handle.m_so != so) { group->m_handles[h].m_so = so; changed = true; } } } if (changed) { // Rebuild SO minmax group->m_soMax = -(group->m_soMin = (std::numeric_limits::max)()); for (h = 0; h != hCount; ++h) { const double &so = group->m_handles[h].m_so; group->m_soMin = std::min(group->m_soMin, so); group->m_soMax = std::max(group->m_soMax, so); } } return changed; } //---------------------------------------------------------------------------------- void interpolateSO(DataGroup *group, const TMeshImage *meshImage) { int m, mCount = meshImage->meshes().size(); if (group->m_handles.size() == 0) { // No handles case, fill in with 0s for (m = 0; m != mCount; ++m) { const TTextureMesh &mesh = *meshImage->meshes()[m]; PlasticDeformerData &data = group->m_datas[m]; std::fill(data.m_so.get(), data.m_so.get() + mesh.facesCount(), 0.0); } return; } // Apply handles' SO values to each mesh for (m = 0; m != mCount; ++m) { const TTextureMesh &mesh = *meshImage->meshes()[m]; PlasticDeformerData &data = group->m_datas[m]; // Interpolate so values std::unique_ptr verticesSO(new double[mesh.verticesCount()]); ::buildSO(verticesSO.get(), mesh, group->m_handles, &data.m_faceHints.front()); // Make the mean of each face's vertex values and store that int f, fCount = mesh.facesCount(); for (f = 0; f != fCount; ++f) { int v0, v1, v2; mesh.faceVertices(f, v0, v1, v2); data.m_so[f] = (verticesSO[v0] + verticesSO[v1] + verticesSO[v2]) / 3.0; } } } //---------------------------------------------------------------------------------- struct FaceLess { const PlasticDeformerDataGroup *m_group; public: FaceLess(const PlasticDeformerDataGroup *group) : m_group(group) {} bool operator()(const std::pair &a, const std::pair &b) { return (m_group->m_datas[a.second].m_so[a.first] < m_group->m_datas[b.second].m_so[b.first]); } }; // Must be invoked after updateSO void updateSortedFaces(PlasticDeformerDataGroup *group) { FaceLess comp(group); std::sort(group->m_sortedFaces.begin(), group->m_sortedFaces.end(), comp); } //---------------------------------------------------------------------------------- void processSO(DataGroup *group, double frame, const TMeshImage *meshImage, const SkD *sd, int skelId, const TAffine &deformationAffine) { // SO re-interpolate values along the mesh if either: // 1. Recompilation was requested (ie some vertex may have been // added/removed) // 2. OR the value of one of the handle has changed bool interpolate = !(group->m_compiled & PlasticDeformerStorage::SO); if (!(group->m_upToDate & PlasticDeformerStorage::SO)) // implied by (interpolate == true) { interpolate = updateHandlesSO(group, sd, skelId, frame) || interpolate; // Order is IMPORTANT if (interpolate) { interpolateSO(group, meshImage); updateSortedFaces(group); } group->m_compiled |= PlasticDeformerStorage::SO; group->m_upToDate |= PlasticDeformerStorage::SO; } } } // namespace //*********************************************************************************************** // Mesh Deform processing functions //*********************************************************************************************** namespace { void processMesh(DataGroup *group, double frame, const TMeshImage *meshImage, const SkD *sd, int skelId, const TAffine &deformationAffine) { if (!(group->m_upToDate & PlasticDeformerStorage::MESH)) { int m, mCount = meshImage->meshes().size(); if (!(group->m_compiled & PlasticDeformerStorage::MESH)) { for (m = 0; m != mCount; ++m) { const TTextureMeshP &mesh = meshImage->meshes()[m]; PlasticDeformerData &data = group->m_datas[m]; data.m_deformer.initialize(mesh); data.m_deformer.compile( group->m_handles, data.m_faceHints.empty() ? 0 : &data.m_faceHints.front()); data.m_deformer.releaseInitializedData(); } group->m_compiled |= PlasticDeformerStorage::MESH; } const TPointD *dstHandlePos = group->m_dstHandles.empty() ? 0 : &group->m_dstHandles.front(); for (m = 0; m != mCount; ++m) { PlasticDeformerData &data = group->m_datas[m]; data.m_deformer.deform(dstHandlePos, data.m_output.get()); } group->m_upToDate |= PlasticDeformerStorage::MESH; } } } // namespace //*********************************************************************************************** // PlasticDeformerData implementation //*********************************************************************************************** PlasticDeformerData::PlasticDeformerData() {} //---------------------------------------------------------------------------------- PlasticDeformerData::~PlasticDeformerData() {} //*********************************************************************************************** // PlasticDeformerDataGroup implementation //*********************************************************************************************** PlasticDeformerDataGroup::PlasticDeformerDataGroup() : m_datas() , m_compiled(PlasticDeformerStorage::NONE) , m_upToDate(PlasticDeformerStorage::NONE) , m_outputFrame((std::numeric_limits::max)()) , m_soMin() , m_soMax() {} //---------------------------------------------------------------------------------- PlasticDeformerDataGroup::~PlasticDeformerDataGroup() {} //*********************************************************************************************** // PlasticDeformerStorage::Imp definition //*********************************************************************************************** class PlasticDeformerStorage::Imp { public: QMutex m_mutex; //!< Access mutex - needed for thread-safety DeformersSet m_deformers; //!< Set of deformers, ordered by mesh image, //! deformation, and affine. public: Imp() : m_mutex(QMutex::Recursive) {} }; //*********************************************************************************************** // PlasticDeformerStorage implementation //*********************************************************************************************** PlasticDeformerStorage::PlasticDeformerStorage() : m_imp(new Imp) {} //---------------------------------------------------------------------------------- PlasticDeformerStorage::~PlasticDeformerStorage() {} //---------------------------------------------------------------------------------- PlasticDeformerStorage *PlasticDeformerStorage::instance() { static PlasticDeformerStorage theInstance; return &theInstance; } //---------------------------------------------------------------------------------- PlasticDeformerDataGroup *PlasticDeformerStorage::deformerData( const TMeshImage *meshImage, const PlasticSkeletonDeformation *deformation, int skelId) { QMutexLocker locker(&m_imp->m_mutex); // Search for the corresponding deformation in the storage Key key(meshImage, deformation, skelId); DeformersByKey::iterator dt = m_imp->m_deformers.find(key); if (dt == m_imp->m_deformers.end()) { // No deformer was found. Allocate it. key.m_dataGroup = std::make_shared(); initializeDeformersData(key.m_dataGroup.get(), meshImage); dt = m_imp->m_deformers.insert(key).first; } return dt->m_dataGroup.get(); } //---------------------------------------------------------------------------------- const PlasticDeformerDataGroup *PlasticDeformerStorage::process( double frame, const TMeshImage *meshImage, const PlasticSkeletonDeformation *deformation, int skelId, const TAffine &skeletonAffine, DataType dataType) { QMutexLocker locker(&m_imp->m_mutex); PlasticDeformerDataGroup *group = deformerData(meshImage, deformation, skelId); // On-the-fly checks for data invalidation if (group->m_skeletonAffine != skeletonAffine) { group->m_upToDate = NONE; group->m_compiled = NONE; group->m_skeletonAffine = skeletonAffine; } if (group->m_outputFrame != frame) { group->m_upToDate = NONE; group->m_outputFrame = frame; } bool doMesh = (dataType & MESH); bool doSO = (dataType & SO) || doMesh; bool doHandles = (bool)dataType; // Process data if (doHandles) processHandles(group, frame, meshImage, deformation, skelId, skeletonAffine); if (doSO) processSO(group, frame, meshImage, deformation, skelId, skeletonAffine); if (doMesh) processMesh(group, frame, meshImage, deformation, skelId, skeletonAffine); return group; } //---------------------------------------------------------------------------------- const PlasticDeformerDataGroup *PlasticDeformerStorage::processOnce( double frame, const TMeshImage *meshImage, const PlasticSkeletonDeformation *deformation, int skelId, const TAffine &skeletonAffine, DataType dataType) { PlasticDeformerDataGroup *group = new PlasticDeformerDataGroup; initializeDeformersData(group, meshImage); bool doMesh = (dataType & MESH); bool doSO = (dataType & SO) || doMesh; bool doHandles = (bool)dataType; // Process data if (doHandles) processHandles(group, frame, meshImage, deformation, skelId, skeletonAffine); if (doSO) processSO(group, frame, meshImage, deformation, skelId, skeletonAffine); if (doMesh) processMesh(group, frame, meshImage, deformation, skelId, skeletonAffine); return group; } //---------------------------------------------------------------------------------- void PlasticDeformerStorage::invalidateMeshImage(const TMeshImage *meshImage, int recompiledData) { QMutexLocker locker(&m_imp->m_mutex); DeformersByMeshImage &deformers = m_imp->m_deformers.get(); DeformersByMeshImage::iterator dBegin(deformers.lower_bound(meshImage)); if (dBegin == deformers.end()) return; DeformersByMeshImage::iterator dt, dEnd(deformers.upper_bound(meshImage)); for (dt = dBegin; dt != dEnd; ++dt) { dt->m_dataGroup->m_outputFrame = (std::numeric_limits::max)(); // Schedule for redeformation if (recompiledData) dt->m_dataGroup->m_compiled &= ~recompiledData; // Schedule for recompilation, too } } //---------------------------------------------------------------------------------- void PlasticDeformerStorage::invalidateSkeleton( const PlasticSkeletonDeformation *deformation, int skelId, int recompiledData) { QMutexLocker locker(&m_imp->m_mutex); DeformedSkeleton ds(deformation, skelId); DeformersByDeformedSkeleton &deformers = m_imp->m_deformers.get(); DeformersByDeformedSkeleton::iterator dBegin(deformers.lower_bound(ds)); if (dBegin == deformers.end()) return; DeformersByDeformedSkeleton::iterator dt, dEnd(deformers.upper_bound(ds)); for (dt = dBegin; dt != dEnd; ++dt) { dt->m_dataGroup->m_outputFrame = (std::numeric_limits::max)(); // Schedule for redeformation if (recompiledData) dt->m_dataGroup->m_compiled &= ~recompiledData; // Schedule for recompilation, too } } //---------------------------------------------------------------------------------- void PlasticDeformerStorage::invalidateDeformation( const PlasticSkeletonDeformation *deformation, int recompiledData) { QMutexLocker locker(&m_imp->m_mutex); DeformersByDeformedSkeleton &deformers = m_imp->m_deformers.get(); DeformedSkeleton dsBegin(deformation, -(std::numeric_limits::max)()), dsEnd(deformation, (std::numeric_limits::max)()); DeformersByDeformedSkeleton::iterator dBegin(deformers.lower_bound(dsBegin)); DeformersByDeformedSkeleton::iterator dEnd(deformers.upper_bound(dsEnd)); if (dBegin == dEnd) return; for (DeformersByDeformedSkeleton::iterator dt = dBegin; dt != dEnd; ++dt) { dt->m_dataGroup->m_outputFrame = (std::numeric_limits::max)(); // Schedule for redeformation if (recompiledData) dt->m_dataGroup->m_compiled &= ~recompiledData; // Schedule for recompilation, too } } //---------------------------------------------------------------------------------- void PlasticDeformerStorage::releaseMeshData(const TMeshImage *meshImage) { QMutexLocker locker(&m_imp->m_mutex); DeformersByMeshImage &deformers = m_imp->m_deformers.get(); DeformersByMeshImage::iterator dBegin(deformers.lower_bound(meshImage)); if (dBegin == deformers.end()) return; deformers.erase(dBegin, deformers.upper_bound(meshImage)); } //---------------------------------------------------------------------------------- void PlasticDeformerStorage::releaseSkeletonData(const SkD *deformation, int skelId) { QMutexLocker locker(&m_imp->m_mutex); DeformedSkeleton ds(deformation, skelId); DeformersByDeformedSkeleton &deformers = m_imp->m_deformers.get(); DeformersByDeformedSkeleton::iterator dBegin(deformers.lower_bound(ds)); if (dBegin == deformers.end()) return; deformers.erase(dBegin, deformers.upper_bound(ds)); } //---------------------------------------------------------------------------------- void PlasticDeformerStorage::releaseDeformationData(const SkD *deformation) { QMutexLocker locker(&m_imp->m_mutex); DeformersByDeformedSkeleton &deformers = m_imp->m_deformers.get(); DeformedSkeleton dsBegin(deformation, -(std::numeric_limits::max)()), dsEnd(deformation, (std::numeric_limits::max)()); DeformersByDeformedSkeleton::iterator dBegin(deformers.lower_bound(dsBegin)); DeformersByDeformedSkeleton::iterator dEnd(deformers.upper_bound(dsEnd)); if (dBegin == dEnd) return; deformers.erase(dBegin, dEnd); } //---------------------------------------------------------------------------------- void PlasticDeformerStorage::clear() { QMutexLocker locker(&m_imp->m_mutex); m_imp->m_deformers.clear(); }