// TnzCore includes #include "tstream.h" // TnzBase includes #include "tdoublekeyframe.h" #include "tparamchange.h" // TnzExt includes #include "ext/plasticskeleton.h" #include "ext/plasticdeformerstorage.h" // tcg includes #include "tcg/tcg_misc.h" // STL includes #include #include #include // Boost includes #include #include #include #include #include #include "ext/plasticskeletondeformation.h" PERSIST_IDENTIFIER(PlasticSkeletonVertexDeformation, "PlasticSkeletonVertexDeformation") PERSIST_IDENTIFIER(PlasticSkeletonDeformation, "PlasticSkeletonDeformation") DEFINE_CLASS_CODE(PlasticSkeletonDeformation, 121) //************************************************************************************** // Boost-related stuff //************************************************************************************** using namespace boost::multi_index; namespace { typedef boost::bimap SkeletonSet; //====================================================================== struct VDKey { QString m_name; int m_hookNumber; mutable std::map m_vIndices; //!< Skeleton index to Vertex index map mutable SkVD m_vd; }; //---------------------------------------------------------------------- typedef boost::multi_index_container< VDKey, indexed_by< ordered_unique, member>, ordered_unique, member> >> SkVDSet; //---------------------------------------------------------------------- typedef SkVDSet::index::type SkVDByHookNumber; } // namespace //************************************************************************************** // Local namespace //************************************************************************************** namespace { static const char *parNames[SkVD::PARAMS_COUNT] = {"Angle", "Distance", "SO"}; static const char *parMeasures[SkVD::PARAMS_COUNT] = {"angle", "fxLength", ""}; //------------------------------------------------------------------ // Extract the angle between edges vParent->v and vGrandParent->vParent double buildAngle(const PlasticSkeleton &skeleton, int v) { const PlasticSkeletonVertex &vx = skeleton.vertex(v); int vParent = vx.parent(); assert(vx.parent() >= 0); const PlasticSkeletonVertex &vxParent = skeleton.vertex(vParent); int vGrandParent = vxParent.parent(); // Build reference orientation TPointD dir(1.0, 0.0); // Standard horizontal axis if no grandParent if (vGrandParent >= 0) { // Relative orientation const PlasticSkeletonVertex &vxGrandParent = skeleton.vertex(vGrandParent); dir = vxParent.P() - vxGrandParent.P(); } return tcg::point_ops::angle(dir, vx.P() - vxParent.P()) * M_180_PI; } } // namespace //************************************************************************************** // PlasticSkeletonVertex implementation //************************************************************************************** SkVD::Keyframe SkVD::getKeyframe(double frame) const { Keyframe kf; for (int p = 0; p < PARAMS_COUNT; ++p) kf.m_keyframes[p] = m_params[p]->getKeyframeAt(frame); return kf; } //------------------------------------------------------------------ void SkVD::setKeyframe(double frame) { for (int p = 0; p < PARAMS_COUNT; ++p) m_params[p]->setKeyframe(m_params[p]->getKeyframeAt(frame)); } //------------------------------------------------------------------ bool SkVD::setKeyframe(const SkVD::Keyframe &values) { bool keyWasSet = false; for (int p = 0; p < PARAMS_COUNT; ++p) if (values.m_keyframes[p].m_isKeyframe) { m_params[p]->setKeyframe(values.m_keyframes[p]); keyWasSet = true; } return keyWasSet; } //------------------------------------------------------------------ bool SkVD::setKeyframe(const SkVD::Keyframe &values, double frame, double easeIn, double easeOut) { bool keyWasSet = false; for (int p = 0; p < PARAMS_COUNT; ++p) if (values.m_keyframes[p].m_isKeyframe) { TDoubleKeyframe kf(values.m_keyframes[p]); kf.m_frame = frame; if (easeIn >= 0.0) kf.m_speedIn = TPointD(-easeIn, kf.m_speedIn.y); if (easeOut >= 0.0) kf.m_speedOut = TPointD(easeOut, kf.m_speedOut.y); m_params[p]->setKeyframe(kf); keyWasSet = true; } return keyWasSet; } //------------------------------------------------------------------ bool SkVD::isKeyframe(double frame) const { for (int p = 0; p < PARAMS_COUNT; ++p) if (m_params[p]->isKeyframe(frame)) return true; return false; } //------------------------------------------------------------------ bool SkVD::isFullKeyframe(double frame) const { for (int p = 0; p < PARAMS_COUNT; ++p) if (!m_params[p]->isKeyframe(frame)) return false; return true; } //------------------------------------------------------------------ void SkVD::deleteKeyframe(double frame) { for (int p = 0; p < PARAMS_COUNT; ++p) m_params[p]->deleteKeyframe(frame); } //------------------------------------------------------------------ void SkVD::saveData(TOStream &os) { for (int p = 0; p < PARAMS_COUNT; ++p) if (!m_params[p]->isDefault()) os.child(::parNames[p]) << *m_params[p]; } //------------------------------------------------------------------ void SkVD::loadData(TIStream &is) { std::string tagName; while (is.matchTag(tagName)) { int p; for (p = 0; p < PARAMS_COUNT; ++p) { if (tagName == parNames[p]) { is >> *m_params[p], is.matchEndTag(); break; } } if (p >= PARAMS_COUNT) is.skipCurrentTag(); } } //************************************************************************************** // PlasticSkeletonDeformation::Imp definition //************************************************************************************** class PlasticSkeletonDeformation::Imp final : public TParamObserver { public: PlasticSkeletonDeformation *m_back; //!< Back-pointer to the interface class SkeletonSet m_skeletons; //!< Skeletons owned by the deformation SkVDSet m_vds; //!< Container of vertex deformations TDoubleParamP m_skelIdsParam; //!< Curve of skeleton ids by xsheet frame std::set m_observers; //!< Set of the deformation's observers TSyntax::Grammar *m_grammar; //!< The params' grammar. Weird though - it's a VERY //!< occult requirement to TDoubleParams... // NOTE: There \a is a deformation even for a skeleton's root node. This is // now required due to the // onwership of \a multiple skeletons at once. However, its angle and distance // params will be unused. public: Imp(PlasticSkeletonDeformation *back); ~Imp(); Imp(PlasticSkeletonDeformation *back, const Imp &other); Imp &operator=(const Imp &other); PlasticSkeleton &skeleton(int skelId) const; SkVD &vertexDeformation(const QString &name) const; void attach(int skeletonId, PlasticSkeleton *skeleton); void detach(int skeletonId); void attachVertex(const QString &name, int skelId, int v); void detachVertex(const QString &name, int skelId, int v); void rebindVertex(const QString &name, int skelId, const QString &newName); void touchParams(SkVD &vd); //! Applies stored vertex deformations to the skeleton branch starting at v void updateBranchPositions(const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton, double frame, int v); void onChange( const TParamChange &change) override; // Passes param notifications to // external observers private: // Not directly copy-constructible Imp(const Imp &other); }; //------------------------------------------------------------------ PlasticSkeletonDeformation::Imp::Imp(PlasticSkeletonDeformation *back) : m_back(back), m_skelIdsParam(1.0), m_grammar() { m_skelIdsParam->setName("Skeleton Id"); m_skelIdsParam->addObserver(this); } //------------------------------------------------------------------ PlasticSkeletonDeformation::Imp::~Imp() { m_skelIdsParam->removeObserver(this); SkVDSet::iterator dt, dEnd(m_vds.end()); for (dt = m_vds.begin(); dt != dEnd; ++dt) for (int p = 0; p < SkVD::PARAMS_COUNT; ++p) dt->m_vd.m_params[p]->removeObserver(this); } //------------------------------------------------------------------ PlasticSkeletonDeformation::Imp::Imp(PlasticSkeletonDeformation *back, const Imp &other) : m_back(back), m_skelIdsParam(other.m_skelIdsParam->clone()), m_grammar() { m_skelIdsParam->setGrammar(m_grammar); m_skelIdsParam->addObserver(this); // Clone the skeletons SkeletonSet::const_iterator st, sEnd(other.m_skeletons.end()); for (st = other.m_skeletons.begin(); st != sEnd; ++st) m_skeletons.insert(SkeletonSet::value_type( st->get_left(), new PlasticSkeleton(*st->get_right()))); // Clone each parameters curve SkVD vd; SkVDSet::const_iterator dt, dEnd(other.m_vds.end()); for (dt = other.m_vds.begin(); dt != dEnd; ++dt) { VDKey vdKey = {dt->m_name, dt->m_hookNumber, dt->m_vIndices}; for (int p = 0; p < SkVD::PARAMS_COUNT; ++p) { TDoubleParamP ¶m = vdKey.m_vd.m_params[p]; param = TDoubleParamP(dt->m_vd.m_params[p]->clone()); param->setGrammar(m_grammar); param->addObserver(this); } m_vds.insert(vdKey); } } //------------------------------------------------------------------ PlasticSkeletonDeformation::Imp &PlasticSkeletonDeformation::Imp::operator=( const Imp &other) { *m_skelIdsParam = *other.m_skelIdsParam; m_skelIdsParam->setGrammar(m_grammar); // Take in all curves whose name matches one of the stored ones // Traverse known curves SkVDSet::iterator dt, dEnd(m_vds.end()); SkVDSet::const_iterator st, sEnd(other.m_vds.end()); for (dt = m_vds.begin(); dt != dEnd; ++dt) { // Search a corresponding curve in the input ones st = other.m_vds.find(dt->m_name); if (st != sEnd) for (int p = 0; p < SkVD::PARAMS_COUNT; ++p) { TDoubleParam ¶m = *dt->m_vd.m_params[p]; param = *st->m_vd.m_params[p]; param.setGrammar(m_grammar); } } return *this; } //------------------------------------------------------------------ PlasticSkeleton &PlasticSkeletonDeformation::Imp::skeleton(int skelId) const { SkeletonSet::left_map::const_iterator st(m_skeletons.left.find(skelId)); assert(st != m_skeletons.left.end()); return *st->second; } //------------------------------------------------------------------ SkVD &PlasticSkeletonDeformation::Imp::vertexDeformation( const QString &name) const { SkVDSet::const_iterator vdt(m_vds.find(name)); assert(vdt != m_vds.end()); return vdt->m_vd; } //------------------------------------------------------------------ void PlasticSkeletonDeformation::Imp::attach(int skeletonId, PlasticSkeleton *skeleton) { assert(skeleton); // Store the skeleton - acquires shared ownership assert(m_skeletons.left.find(skeletonId) == m_skeletons.left.end()); m_skeletons.insert(SkeletonSet::value_type(skeletonId, skeleton)); // Deal with vertex deformations and parameter defaults tcg::list &vertices = skeleton->vertices(); if (!vertices.empty()) { tcg::list::iterator vt, vEnd(vertices.end()); for (vt = vertices.begin(); vt != vEnd; ++vt) attachVertex(vt->name(), skeletonId, vt.index()); } } //------------------------------------------------------------------ void PlasticSkeletonDeformation::Imp::detach(int skeletonId) { // First, detach all vertices tcg::list &vertices = skeleton(skeletonId).vertices(); if (!vertices.empty()) { tcg::list::iterator vt, vEnd(vertices.end()); for (vt = vertices.begin(); vt != vEnd; ++vt) detachVertex(vt->name(), skeletonId, vt.index()); } // Then, release the skeleton itself m_skeletons.left.erase(skeletonId); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::Imp::attachVertex(const QString &name, int skelId, int v) { struct locals { static int newHookNumber(Imp *imp) { int h = 1; SkVDByHookNumber::iterator vdt, vdEnd(imp->m_vds.get().end()); for (vdt = imp->m_vds.get().begin(); vdt != vdEnd && vdt->m_hookNumber == h; ++vdt, ++h) ; return h; } }; // locals // Insert a new VD if necessary SkVDSet::iterator vdt(m_vds.find(name)); if (vdt == m_vds.end()) { VDKey vdKey = {name, locals::newHookNumber(this)}; touchParams(vdKey.m_vd); vdt = m_vds.insert(vdKey).first; } // Register (skelId, v) on the vd vdt->m_vIndices.insert(std::make_pair(skelId, v)); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::Imp::detachVertex(const QString &name, int skelId, int v) { SkVDSet::iterator vdt = m_vds.find(name); assert(vdt != m_vds.end()); // Unregister skelId int count = vdt->m_vIndices.erase(skelId); assert(count > 0); if (vdt->m_vIndices.empty()) { // De-register as vdt's observer, and release it from stored vds SkVD &vd = vdt->m_vd; for (int p = 0; p < SkVD::PARAMS_COUNT; ++p) vd.m_params[p]->removeObserver(this); m_vds.erase(vdt); } } //------------------------------------------------------------------ void PlasticSkeletonDeformation::Imp::rebindVertex(const QString &name, int skelId, const QString &newName) { if (name == newName) return; SkVDSet::iterator oldVdt = m_vds.find(name); if (oldVdt == m_vds.end()) return; // We get here when creating a new vertex std::map::iterator vit = oldVdt->m_vIndices.find(skelId); assert(vit != oldVdt->m_vIndices.end()); int v = vit->second; SkVDSet::iterator newVdt = m_vds.find(newName); if (newVdt != m_vds.end()) { detachVertex(name, skelId, v); attachVertex(newName, skelId, v); } else { // Creating a new vd entry if (oldVdt->m_vIndices.size() == 1) { // The old entry should be removed - it is actually *purely* renamed VDKey vdKey(*oldVdt); vdKey.m_name = newName; m_vds.erase(name); m_vds.insert(vdKey); } else { // The old entry remains - and data must be copied from there detachVertex(name, skelId, v); attachVertex(newName, skelId, v); newVdt = m_vds.find(newName); // Fetch the newly created vd // Copy the existing vd into the new one SkVD &oldVd = oldVdt->m_vd, &newVd = newVdt->m_vd; int p, pCount = SkVD::PARAMS_COUNT; for (p = 0; p != pCount; ++p) *newVd.m_params[p] = *oldVd.m_params[p]; } } } //------------------------------------------------------------------ void PlasticSkeletonDeformation::Imp::touchParams(SkVD &vd) { for (int p = 0; p < SkVD::PARAMS_COUNT; ++p) { if (vd.m_params[p]) continue; TDoubleParam *param = new TDoubleParam; param->setName(parNames[p]); param->setMeasureName(parMeasures[p]); param->setGrammar(m_grammar); vd.m_params[p] = param; param->addObserver(this); } } //------------------------------------------------------------------ void PlasticSkeletonDeformation::Imp::onChange(const TParamChange &change) { // Since the deformation was changed, any associated deformer // must be invalidated (at the animation-deform level only) PlasticDeformerStorage::instance()->invalidateDeformation( m_back, PlasticDeformerStorage::NONE); // Propagate notification to this object's observers std::set::iterator ot, oEnd(m_observers.end()); for (ot = m_observers.begin(); ot != oEnd; ++ot) (*ot)->onChange(change); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::Imp::updateBranchPositions( const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton, double frame, int v) { struct locals { static void buildParentDirection(const PlasticSkeleton &skel, int v, TPointD &dir) { assert(v >= 0); const PlasticSkeletonVertex &vx = skel.vertex(v); int vParent = vx.parent(); if (vParent < 0) return; // dir remains as passed const TPointD &dir_ = tcg::point_ops::direction(skel.vertex(vParent).P(), vx.P(), 1e-4); if (dir_ != tcg::point_ops::NaP()) dir = dir_; else buildParentDirection(skel, vParent, dir); } }; // locals PlasticSkeletonVertex &dvx = deformedSkeleton.vertex(v); int vParent = dvx.parent(); if (vParent >= 0) { // Rebuild vertex position const TPointD &ovxPos = originalSkeleton.vertex(v).P(); const TPointD &ovxParentPos = originalSkeleton.vertex(vParent).P(); // Start by getting the polar reference TPointD oDir(1.0, 0.0), dDir(1.0, 0.0); locals::buildParentDirection(originalSkeleton, vParent, oDir); locals::buildParentDirection(deformedSkeleton, vParent, dDir); // Now, rebuild vx's position const SkVD &vd = m_vds.find(dvx.name())->m_vd; double a = tcg::point_ops::angle(oDir, ovxPos - ovxParentPos) * M_180_PI; double d = tcg::point_ops::dist(ovxParentPos, ovxPos); double aDelta = vd.m_params[SkVD::ANGLE]->getValue(frame); double dDelta = vd.m_params[SkVD::DISTANCE]->getValue(frame); dvx.P() = deformedSkeleton.vertex(vParent).P() + (d + dDelta) * (TRotation(a + aDelta) * dDir); } // Finally, update children positions PlasticSkeleton::vertex_type::edges_iterator et, eEnd(dvx.edgesEnd()); for (et = dvx.edgesBegin(); et != eEnd; ++et) { int vChild = deformedSkeleton.edge(*et).vertex(1); if (vChild == v) continue; updateBranchPositions(originalSkeleton, deformedSkeleton, frame, vChild); } } //************************************************************************************** // PlasticSkeletonDeformation implementation //************************************************************************************** PlasticSkeletonDeformation::PlasticSkeletonDeformation() : m_imp(new Imp(this)) {} //------------------------------------------------------------------ PlasticSkeletonDeformation::PlasticSkeletonDeformation( const PlasticSkeletonDeformation &other) : TSmartObject(m_classCode), m_imp(new Imp(this, *other.m_imp)) { // Register deformation SkeletonSet::iterator st, sEnd(m_imp->m_skeletons.end()); for (st = m_imp->m_skeletons.begin(); st != sEnd; ++st) st->get_right()->addListener(this); } //------------------------------------------------------------------ PlasticSkeletonDeformation::~PlasticSkeletonDeformation() { // Unregister deformation SkeletonSet::iterator st, sEnd(m_imp->m_skeletons.end()); for (st = m_imp->m_skeletons.begin(); st != sEnd; ++st) st->get_right()->removeListener(this); } //------------------------------------------------------------------ PlasticSkeletonDeformation &PlasticSkeletonDeformation::operator=( const PlasticSkeletonDeformation &other) { // The meaning of operator= is DIFFERENT from that implemented in the copy // constructor. // Skeletons are NOT cloned. *m_imp = *other.m_imp; return *this; } //------------------------------------------------------------------ bool PlasticSkeletonDeformation::empty() const { return m_imp->m_skeletons.empty(); } //------------------------------------------------------------------ int PlasticSkeletonDeformation::skeletonsCount() const { return m_imp->m_skeletons.size(); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::skeletonIds(skelId_iterator &begin, skelId_iterator &end) const { auto const f = [](const SkeletonSet::left_map::value_type &val) { return val.first; }; begin = boost::make_transform_iterator(m_imp->m_skeletons.left.begin(), f); end = boost::make_transform_iterator(m_imp->m_skeletons.left.end(), f); } //------------------------------------------------------------------ TDoubleParamP PlasticSkeletonDeformation::skeletonIdsParam() const { return m_imp->m_skelIdsParam; } //------------------------------------------------------------------ PlasticSkeletonP PlasticSkeletonDeformation::skeleton(double frame) const { return skeleton(skeletonId(frame)); } //------------------------------------------------------------------ int PlasticSkeletonDeformation::skeletonId(double frame) const { return m_imp->m_skelIdsParam->getValue(frame); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::attach(int skeletonId, PlasticSkeleton *skeleton) { m_imp->attach(skeletonId, skeleton); skeleton->addListener(this); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::detach(int skeletonId) { SkeletonSet::left_map::iterator st(m_imp->m_skeletons.left.find(skeletonId)); if (st != m_imp->m_skeletons.left.end()) { st->second->removeListener(this); m_imp->detach(skeletonId); } } //------------------------------------------------------------------ PlasticSkeletonP PlasticSkeletonDeformation::skeleton(int skeletonId) const { SkeletonSet::left_map::const_iterator st = m_imp->m_skeletons.left.find(skeletonId); return (st == m_imp->m_skeletons.left.end()) ? PlasticSkeletonP() : st->second; } //------------------------------------------------------------------ int PlasticSkeletonDeformation::skeletonId(PlasticSkeleton *skeleton) const { SkeletonSet::right_map::const_iterator st = m_imp->m_skeletons.right.find(skeleton); return (st == m_imp->m_skeletons.right.end()) ? -(std::numeric_limits::max)() : st->second; } //------------------------------------------------------------------ int PlasticSkeletonDeformation::vertexDeformationsCount() const { return m_imp->m_vds.size(); } //------------------------------------------------------------------ SkVD *PlasticSkeletonDeformation::vertexDeformation( const QString &vxName) const { SkVDSet::const_iterator vdt = m_imp->m_vds.find(vxName); return (vdt == m_imp->m_vds.end()) ? (SkVD *)0 : &vdt->m_vd; } //------------------------------------------------------------------ SkVD *PlasticSkeletonDeformation::vertexDeformation(int skelId, int v) const { const QString &name = skeleton(skelId)->vertex(v).name(); return vertexDeformation(name); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::vertexDeformations(vd_iterator &begin, vd_iterator &end) const { auto const f = [](const VDKey &vdKey) { return std::make_pair(&vdKey.m_name, &vdKey.m_vd); }; begin = boost::make_transform_iterator(m_imp->m_vds.begin(), f); end = boost::make_transform_iterator(m_imp->m_vds.end(), f); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::vdSkeletonVertices(const QString &vertexName, vx_iterator &begin, vx_iterator &end) const { auto const f = [](const std::map::value_type &val) { return std::make_pair(val.first, val.second); }; SkVDSet::const_iterator nt(m_imp->m_vds.find(vertexName)); if (nt == m_imp->m_vds.end()) { begin = vx_iterator(); end = vx_iterator(); } else { begin = boost::make_transform_iterator(nt->m_vIndices.begin(), f); end = boost::make_transform_iterator(nt->m_vIndices.end(), f); } } //------------------------------------------------------------------ int PlasticSkeletonDeformation::hookNumber(const QString &name) const { SkVDSet::const_iterator nt(m_imp->m_vds.find(name)); return (nt == m_imp->m_vds.end()) ? -1 : nt->m_hookNumber; } //------------------------------------------------------------------ int PlasticSkeletonDeformation::hookNumber(int skelId, int v) const { const QString &name = skeleton(skelId)->vertex(v).name(); return hookNumber(name); } //------------------------------------------------------------------ QString PlasticSkeletonDeformation::vertexName(int hookNumber) const { const SkVDByHookNumber &vds = m_imp->m_vds.get(); SkVDByHookNumber::const_iterator ht(vds.find(hookNumber)); return (ht == vds.end()) ? QString() : ht->m_name; } //------------------------------------------------------------------ int PlasticSkeletonDeformation::vertexIndex(int hookNumber, int skelId) const { const SkVDByHookNumber &vds = m_imp->m_vds.get(); SkVDByHookNumber::const_iterator ht(vds.find(hookNumber)); if (ht == vds.end()) return -1; std::map::const_iterator st(ht->m_vIndices.find(skelId)); return (st == ht->m_vIndices.end()) ? -1 : st->second; } //------------------------------------------------------------------ void PlasticSkeletonDeformation::getKeyframeAt(double frame, SkDKey &keyframe) const { keyframe.m_skelIdKeyframe = m_imp->m_skelIdsParam->getKeyframeAt(frame); keyframe.m_vertexKeyframes.clear(); SkVDSet::const_iterator dt, dEnd(m_imp->m_vds.end()); for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt) keyframe.m_vertexKeyframes.insert( std::make_pair(dt->m_name, dt->m_vd.getKeyframe(frame))); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::setKeyframe(double frame) { m_imp->m_skelIdsParam->setKeyframe(frame); SkVDSet::iterator dt, dEnd(m_imp->m_vds.end()); for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt) dt->m_vd.setKeyframe(frame); } //------------------------------------------------------------------ bool PlasticSkeletonDeformation::setKeyframe(const SkDKey &keyframe) { bool keyWasSet = false; if (keyframe.m_skelIdKeyframe.m_isKeyframe) { m_imp->m_skelIdsParam->setKeyframe(keyframe.m_skelIdKeyframe); keyWasSet = true; } const std::map &vdKeys = keyframe.m_vertexKeyframes; // Iterate the keyframe's vertex deformations std::map::const_iterator kt, kEnd(vdKeys.end()); for (kt = vdKeys.begin(); kt != vdKeys.end(); ++kt) { // Search for a corresponding vertex deformation among stored ones SkVDSet::iterator vdt = m_imp->m_vds.find(kt->first); if (vdt != m_imp->m_vds.end()) { // Set the corresponding keyframe keyWasSet = vdt->m_vd.setKeyframe(kt->second) || keyWasSet; } } return keyWasSet; } //------------------------------------------------------------------ bool PlasticSkeletonDeformation::setKeyframe(const SkDKey &keyframe, double frame, double easeIn, double easeOut) { bool keyWasSet = false; if (keyframe.m_skelIdKeyframe.m_isKeyframe) { TDoubleKeyframe kf(keyframe.m_skelIdKeyframe); kf.m_frame = frame; m_imp->m_skelIdsParam->setKeyframe(kf); keyWasSet = true; } const std::map &vdKeys = keyframe.m_vertexKeyframes; // Iterate the keyframe's vertex deformations std::map::const_iterator kt, kEnd(vdKeys.end()); for (kt = vdKeys.begin(); kt != vdKeys.end(); ++kt) { // Search for a corresponding vertex deformation among stored ones SkVDSet::iterator vdt = m_imp->m_vds.find(kt->first); if (vdt != m_imp->m_vds.end()) { // Set the corresponding keyframe keyWasSet = vdt->m_vd.setKeyframe(kt->second, frame, easeIn, easeOut) || keyWasSet; } } return keyWasSet; } //------------------------------------------------------------------ bool PlasticSkeletonDeformation::isKeyframe(double frame) const { if (m_imp->m_skelIdsParam->isKeyframe(frame)) return true; SkVDSet::const_iterator dt, dEnd(m_imp->m_vds.end()); for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt) if (dt->m_vd.isKeyframe(frame)) return true; return false; } //------------------------------------------------------------------ bool PlasticSkeletonDeformation::isFullKeyframe(double frame) const { if (!m_imp->m_skelIdsParam->isKeyframe(frame)) return false; SkVDSet::const_iterator dt, dEnd(m_imp->m_vds.end()); for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt) if (!dt->m_vd.isFullKeyframe(frame)) return false; return true; } //------------------------------------------------------------------ void PlasticSkeletonDeformation::deleteKeyframe(double frame) { m_imp->m_skelIdsParam->deleteKeyframe(frame); SkVDSet::iterator dt, dEnd(m_imp->m_vds.end()); for (dt = m_imp->m_vds.begin(); dt != dEnd; ++dt) dt->m_vd.deleteKeyframe(frame); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::storeDeformedSkeleton( int skelId, double frame, PlasticSkeleton &skeleton) const { // Copy the un-deformed skeleton to the output one const PlasticSkeletonP &origSkel = this->skeleton(skelId); skeleton = origSkel ? *origSkel : PlasticSkeleton(); // Update the skeleton to the specified frame if (!skeleton.vertices().empty()) m_imp->updateBranchPositions(*origSkel, skeleton, frame, skeleton.vertices().begin().m_idx); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::updatePosition( const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton, double frame, int v, const TPointD &pos) { const PlasticSkeletonVertex &vx = deformedSkeleton.vertex(v); int vParent = vx.parent(); const TPointD &vParentPos = deformedSkeleton.vertex(vParent).P(); const TPointD &vPos = deformedSkeleton.vertex(v).P(); SkVD &vd = m_imp->m_vds.find(vx.name())->m_vd; // NOTE: The following aDelta calculation should be done as a true difference // - this is still ok and spares // access to v's grandParent... double aDelta = tcg::point_ops::angle(vPos - vParentPos, pos - vParentPos) * M_180_PI, dDelta = tcg::point_ops::dist(vParentPos, pos) - tcg::point_ops::dist(vParentPos, vPos), a = tcrop(vd.m_params[SkVD::ANGLE]->getValue(frame) + aDelta, vx.m_minAngle, vx.m_maxAngle), d = vd.m_params[SkVD::DISTANCE]->getValue(frame) + dDelta; vd.m_params[SkVD::ANGLE]->setValue(frame, a); vd.m_params[SkVD::DISTANCE]->setValue(frame, d); m_imp->updateBranchPositions(originalSkeleton, deformedSkeleton, frame, v); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::updateAngle( const PlasticSkeleton &originalSkeleton, PlasticSkeleton &deformedSkeleton, double frame, int v, const TPointD &pos) { const PlasticSkeletonVertex &vx = deformedSkeleton.vertex(v); int vParent = vx.parent(); const TPointD &vParentPos = deformedSkeleton.vertex(vParent).P(); // No need to access the grandParent, we're making the diff against the old vx // position SkVD &vd = m_imp->m_vds.find(vx.name())->m_vd; double aDelta = tcg::point_ops::angle(vx.P() - vParentPos, pos - vParentPos) * M_180_PI, a = tcrop(vd.m_params[SkVD::ANGLE]->getValue(frame) + aDelta, vx.m_minAngle, vx.m_maxAngle); vd.m_params[SkVD::ANGLE]->setValue(frame, a); m_imp->updateBranchPositions(originalSkeleton, deformedSkeleton, frame, v); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::addVertex(PlasticSkeleton *skel, int v) { int skelId = skeletonId(skel); assert(skelId >= 0); m_imp->attachVertex(skel->vertex(v).name(), skelId, v); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::insertVertex(PlasticSkeleton *skel, int v) { int skelId = skeletonId(skel); assert(skelId >= 0); m_imp->attachVertex(skel->vertex(v).name(), skelId, v); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::deleteVertex(PlasticSkeleton *skel, int v) { assert(v > 0); // Root should not be deleted int skelId = skeletonId(skel); assert(skelId >= 0); // Remove the vertex deformation associated with v m_imp->detachVertex(skel->vertex(v).name(), skelId, v); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::vertexNameChange(PlasticSkeleton *skel, int v, const QString &newName) { int skelId = skeletonId(skel); assert(skelId >= 0); m_imp->rebindVertex(skel->vertex(v).name(), skelId, newName); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::clear(PlasticSkeleton *skel) { int skelId = skeletonId(skel); assert(skelId >= 0); m_imp->detach(skelId); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::addObserver(TParamObserver *observer) { m_imp->m_observers.insert(observer); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::removeObserver(TParamObserver *observer) { m_imp->m_observers.erase(observer); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::setGrammar(TSyntax::Grammar *grammar) { SkVDSet::iterator vdt, vdEnd(m_imp->m_vds.end()); for (vdt = m_imp->m_vds.begin(); vdt != vdEnd; ++vdt) { SkVD &vd = vdt->m_vd; for (int c = 0; c != SkVD::PARAMS_COUNT; ++c) vd.m_params[c]->setGrammar(grammar); } m_imp->m_skelIdsParam->setGrammar(grammar); m_imp->m_grammar = grammar; } //------------------------------------------------------------------ void PlasticSkeletonDeformation::saveData(TOStream &os) { // Save skeleton vertex deformations os.openChild("VertexDeforms"); // These are saved *before* skeletons { // ON PURPOSE - in loadData(), we'll SkVDSet::iterator vdt, vdEnd(m_imp->m_vds.end()); // attach() skeletons to a completely for (vdt = m_imp->m_vds.begin(); vdt != vdEnd; ++vdt) // rebuilt set of vertex deformations. { os.child("Name") << vdt->m_name; os.child("Hook") << vdt->m_hookNumber; os.child("VD") << vdt->m_vd; } } os.closeChild(); // Save skeleton ids param os.child("SkelIdsParam") << *m_imp->m_skelIdsParam; // Save skeletons os.openChild("Skeletons"); { SkeletonSet::iterator st, sEnd(m_imp->m_skeletons.end()); for (st = m_imp->m_skeletons.begin(); st != sEnd; ++st) { os.child("SkelId") << st->get_left(); os.child("Skeleton") << *st->get_right(); } } os.closeChild(); } //------------------------------------------------------------------ void PlasticSkeletonDeformation::loadData(TIStream &is) { if (is.getVersion() < VersionNumber(1, 21)) { loadData_prerelease(is); return; } int skeletonId; PlasticSkeleton *skeleton; std::string tagName; while (is.openChild(tagName)) { if (tagName == "VertexDeforms") { VDKey vKey; while (is.openChild(tagName)) { if (tagName == "Name") is >> vKey.m_name, is.matchEndTag(); else if (tagName == "Hook") is >> vKey.m_hookNumber, is.matchEndTag(); else if (tagName == "VD") { m_imp->touchParams(vKey.m_vd); is >> vKey.m_vd, is.matchEndTag(); m_imp->m_vds.insert(vKey); vKey = VDKey(); } else is.skipCurrentTag(); } is.matchEndTag(); } else if (tagName == "SkelIdsParam") is >> *m_imp->m_skelIdsParam, is.matchEndTag(); else if (tagName == "Skeletons") { while (is.openChild(tagName)) { if (tagName == "SkelId") is >> skeletonId, is.matchEndTag(); else if (tagName == "Skeleton") { skeleton = new PlasticSkeleton; is >> *skeleton, is.matchEndTag(); attach(skeletonId, skeleton); skeletonId = 0, skeleton = 0; } else is.skipCurrentTag(); } is.matchEndTag(); } else is.skipCurrentTag(); } } //------------------------------------------------------------------ void PlasticSkeletonDeformation::loadData_prerelease(TIStream &is) { struct locals { static void buildParentDirection(const PlasticSkeleton &skel, int v, TPointD &dir) { assert(v >= 0); const PlasticSkeletonVertex &vx = skel.vertex(v); int vParent = vx.parent(); if (vParent < 0) return; // dir remains as passed const TPointD &dir_ = tcg::point_ops::direction(skel.vertex(vParent).P(), vx.P(), 1e-4); if (dir_ != tcg::point_ops::NaP()) dir = dir_; else buildParentDirection(skel, vParent, dir); } //------------------------------------------------------------------------------ static void adjust(SkD &sd, int v) { PlasticSkeleton &skeleton = *sd.skeleton(1); PlasticSkeletonVertex &vx = skeleton.vertex(v); int vParent = vx.parent(); if (vParent >= 0) { // Rebuild vertex position const TPointD &vxPos = skeleton.vertex(v).P(); const TPointD &vxParentPos = skeleton.vertex(vParent).P(); // Start by getting the polar reference TPointD dir(1.0, 0.0); buildParentDirection(skeleton, vParent, dir); // Now, rebuild vx's position SkVD &vd = sd.m_imp->m_vds.find(vx.name())->m_vd; double a = tcg::point_ops::angle(dir, vxPos - vxParentPos) * M_180_PI; double d = tcg::point_ops::dist(vxParentPos, vxPos); { TDoubleParamP param(vd.m_params[SkVD::ANGLE]); param->setDefaultValue(0.0); int k, kCount = param->getKeyframeCount(); for (k = 0; k != kCount; ++k) { TDoubleKeyframe kf(param->getKeyframe(k)); kf.m_value -= a; param->setKeyframe(k, kf); } } { TDoubleParamP param(vd.m_params[SkVD::DISTANCE]); param->setDefaultValue(0.0); int k, kCount = param->getKeyframeCount(); for (k = 0; k != kCount; ++k) { TDoubleKeyframe kf(param->getKeyframe(k)); kf.m_value -= d; param->setKeyframe(k, kf); } } } // Finally, update children positions PlasticSkeleton::vertex_type::edges_iterator et, eEnd(vx.edgesEnd()); for (et = vx.edgesBegin(); et != eEnd; ++et) { int vChild = skeleton.edge(*et).vertex(1); if (vChild == v) continue; adjust(sd, vChild); } } }; // locals PlasticSkeletonP skeleton(new PlasticSkeleton); std::string tagName; while (is.openChild(tagName)) { if (tagName == "Skeleton") is >> *skeleton, is.matchEndTag(); else if (tagName == "VertexDeforms") { while (is.openChild(tagName)) { if (tagName == "VD") { VDKey vKey; m_imp->touchParams(vKey.m_vd); is >> vKey.m_name, is >> vKey.m_vd; is.closeChild(); // Rebuild vKey's data from skeleton int v, vCount = skeleton->verticesCount(); for (v = 0; v != vCount; ++v) if (skeleton->vertex(v).name() == vKey.m_name) break; assert(v < vCount); vKey.m_hookNumber = skeleton->vertex(v).number(); m_imp->m_vds.insert(vKey); } else is.skipCurrentTag(); } is.matchEndTag(); } else is.skipCurrentTag(); } attach(1, skeleton.getPointer()); // SkVD params are now intended as deltas. Adjusting... locals::adjust(*this, 0); }