#ifndef DUST3D_RIGGER_H #define DUST3D_RIGGER_H #include #include #include #include #include #include #include "meshsplitter.h" #include "bonemark.h" #include "rigtype.h" #include "skeletonside.h" class RiggerMark { public: BoneMark boneMark; SkeletonSide boneSide; QVector3D bonePosition; float nodeRadius = 0; std::set markTriangles; const std::set &bigGroup() const { return m_isFirstBiggerThenSecond ? m_firstGroup : m_secondGroup; } const std::set &smallGroup() const { return m_isFirstBiggerThenSecond ? m_secondGroup : m_firstGroup; } bool split(const std::vector &verticesPositions, const std::set &input, const std::vector, std::pair>> &triangleLinks, int expandRound=0) { int totalRound = 1 + expandRound; for (int round = 0; round < totalRound; ++round) { m_firstGroup.clear(); m_secondGroup.clear(); bool splitResult = MeshSplitter::split(input, triangleLinks, markTriangles, m_firstGroup, m_secondGroup, round > 0); if (splitResult) { sortByDistanceFromOrigin(verticesPositions); return true; } } return false; } private: std::set m_firstGroup; std::set m_secondGroup; bool m_isFirstBiggerThenSecond = false; float minDistance2FromOrigin(const std::vector &verticesPositions, const std::set &triangles) { float minDistance2 = std::numeric_limits::max(); for (const auto &triangle: triangles) { for (size_t i = 0; i < 3; ++i) { float distance2 = verticesPositions[triangle.indices[i]].lengthSquared(); if (distance2 < minDistance2) minDistance2 = distance2; } } return minDistance2; } void sortByDistanceFromOrigin(const std::vector &verticesPositions) { m_isFirstBiggerThenSecond = minDistance2FromOrigin(verticesPositions, m_firstGroup) < minDistance2FromOrigin(verticesPositions, m_secondGroup); } }; class RiggerBone { public: QString name; int index = -1; QVector3D headPosition; QVector3D tailPosition; float headRadius = 0.0; float tailRadius = 0.0; QColor color; QVector3D baseNormal; std::vector children; }; class RiggerVertexWeights { public: int boneIndices[4] = {0, 0, 0, 0}; float boneWeights[4] = {0, 0, 0, 0}; void addBone(int boneIndex, float distance) { if (m_boneRawIndices.find(boneIndex) != m_boneRawIndices.end()) return; m_boneRawIndices.insert(boneIndex); if (qFuzzyIsNull(distance)) distance = 0.0001; m_boneRawWeights.push_back(std::make_pair(boneIndex, 1.0 / distance)); } void finalizeWeights() { std::sort(m_boneRawWeights.begin(), m_boneRawWeights.end(), [](const std::pair &a, const std::pair &b) { return a.second > b.second; }); float totalDistance = 0; for (size_t i = 0; i < m_boneRawWeights.size() && i < 4; i++) { const auto &item = m_boneRawWeights[i]; totalDistance += item.second; } if (totalDistance > 0) { for (size_t i = 0; i < m_boneRawWeights.size() && i < 4; i++) { const auto &item = m_boneRawWeights[i]; boneIndices[i] = item.first; boneWeights[i] = item.second / totalDistance; } } else { qDebug() << "totalDistance:" << totalDistance; } } private: std::set m_boneRawIndices; std::vector> m_boneRawWeights; }; class Rigger : public QObject { Q_OBJECT public: Rigger(const std::vector &verticesPositions, const std::set &inputTriangles, const std::vector, std::pair>> &triangleLinks); bool addMarkGroup(BoneMark boneMark, SkeletonSide boneSide, QVector3D bonePosition, float nodeRadius, const std::set &markTriangles); const std::vector> &messages(); const std::vector &resultBones(); const std::map &resultWeights(); virtual bool rig() = 0; static QString rootBoneName; //static QString firstSpineBoneName; protected: virtual bool validate() = 0; virtual bool isCutOffSplitter(BoneMark boneMark) = 0; virtual BoneMark translateBoneMark(BoneMark boneMark); void addTrianglesToVertices(const std::set &triangles, std::set &vertices); bool calculateBodyTriangles(std::set &bodyTriangles); void resolveBoundingBox(const std::set &vertices, QVector3D &xMin, QVector3D &xMax, QVector3D &yMin, QVector3D &yMax, QVector3D &zMin, QVector3D &zMax); QVector3D findMinX(const std::set &vertices); QVector3D findMaxX(const std::set &vertices); QVector3D findMinY(const std::set &vertices); QVector3D findMaxY(const std::set &vertices); QVector3D findMinZ(const std::set &vertices); QVector3D findMaxZ(const std::set &vertices); QVector3D averagePosition(const std::set &vertices); void splitVerticesByY(const std::set &vertices, float y, std::set &greaterEqualThanVertices, std::set &lessThanVertices); void splitVerticesByX(const std::set &vertices, float x, std::set &greaterEqualThanVertices, std::set &lessThanVertices); void splitVerticesByZ(const std::set &vertices, float z, std::set &greaterEqualThanVertices, std::set &lessThanVertices); void splitVerticesByPlane(const std::set &vertices, const QVector3D &pointOnPlane, const QVector3D &planeNormal, std::set &frontOrCoincidentVertices, std::set &backVertices); void addVerticesToWeights(const std::set &vertices, int boneIndex); std::vector> m_messages; std::vector m_verticesPositions; std::set m_inputTriangles; std::vector, std::pair>> m_triangleLinks; std::vector m_marks; std::map, std::vector> m_marksMap; std::vector m_resultBones; std::map m_resultWeights; std::vector m_cutoffErrorItems; std::vector m_jointErrorItems; static size_t m_maxCutOffSplitterExpandRound; bool m_extraMessagedAdded; }; #endif