Add new combine mode: Inflation
This mode turn the component to functional nodes instead of mesh construction. The mesh generated before this component will be proportional deformed.master
parent
ab97615bea
commit
889c11ecef
|
@ -294,6 +294,9 @@ HEADERS += src/posedocument.h
|
|||
SOURCES += src/combinemode.cpp
|
||||
HEADERS += src/combinemode.h
|
||||
|
||||
SOURCES += src/meshinflate.cpp
|
||||
HEADERS += src/meshinflate.h
|
||||
|
||||
SOURCES += src/main.cpp
|
||||
|
||||
HEADERS += src/version.h
|
||||
|
|
|
@ -6,6 +6,7 @@ enum class CombineMode
|
|||
{
|
||||
Normal = 0,
|
||||
Inversion,
|
||||
Inflation,
|
||||
Count
|
||||
};
|
||||
CombineMode CombineModeFromString(const char *modeString);
|
||||
|
@ -17,6 +18,8 @@ CombineMode CombineModeFromString(const char *modeString) \
|
|||
return CombineMode::Normal; \
|
||||
if (mode == "Inversion") \
|
||||
return CombineMode::Inversion; \
|
||||
if (mode == "Inflation") \
|
||||
return CombineMode::Inflation; \
|
||||
return CombineMode::Normal; \
|
||||
}
|
||||
const char *CombineModeToString(CombineMode mode);
|
||||
|
@ -28,6 +31,8 @@ const char *CombineModeToString(CombineMode mode) \
|
|||
return "Normal"; \
|
||||
case CombineMode::Inversion: \
|
||||
return "Inversion"; \
|
||||
case CombineMode::Inflation: \
|
||||
return "Inflation"; \
|
||||
default: \
|
||||
return "Normal"; \
|
||||
} \
|
||||
|
@ -41,6 +46,8 @@ QString CombineModeToDispName(CombineMode mode) \
|
|||
return QObject::tr("Normal"); \
|
||||
case CombineMode::Inversion: \
|
||||
return QObject::tr("Inversion"); \
|
||||
case CombineMode::Inflation: \
|
||||
return QObject::tr("Inflation"); \
|
||||
default: \
|
||||
return QObject::tr("Normal"); \
|
||||
} \
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "imageforever.h"
|
||||
#include "material.h"
|
||||
#include "trianglesourcenoderesolve.h"
|
||||
#include "meshinflate.h"
|
||||
|
||||
bool MeshGenerator::m_enableDebug = false;
|
||||
PositionMap<int> *MeshGenerator::m_forMakePositionKey = new PositionMap<int>;
|
||||
|
@ -225,7 +226,7 @@ bool MeshGenerator::checkIsPartDirty(QString partId)
|
|||
return isTrueValueString(valueOfKeyInMapOrEmpty(findPart->second, "dirty"));
|
||||
}
|
||||
|
||||
void *MeshGenerator::combinePartMesh(QString partId)
|
||||
void *MeshGenerator::combinePartMesh(QString partId, std::vector<std::pair<QVector3D, float>> *balls)
|
||||
{
|
||||
auto findPart = m_snapshot->parts.find(partId);
|
||||
if (findPart == m_snapshot->parts.end()) {
|
||||
|
@ -306,6 +307,13 @@ void *MeshGenerator::combinePartMesh(QString partId)
|
|||
nodeInfo.position = QVector3D(x, y, z);
|
||||
nodeInfo.radius = radius;
|
||||
nodeInfo.boneMark = boneMark;
|
||||
|
||||
if (nullptr != balls) {
|
||||
balls->push_back({QVector3D(x, y, z), radius});
|
||||
if (xMirrored) {
|
||||
balls->push_back({QVector3D(-x, y, z), radius});
|
||||
}
|
||||
}
|
||||
}
|
||||
std::set<std::pair<QString, QString>> edges;
|
||||
for (const auto &edgeId: m_partEdgeIds[partId]) {
|
||||
|
@ -499,12 +507,10 @@ bool MeshGenerator::checkIsComponentDirty(QString componentId)
|
|||
return isDirty;
|
||||
}
|
||||
|
||||
void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
||||
void *MeshGenerator::combineComponentMesh(QString componentId, CombineMode *combineMode, std::vector<std::pair<QVector3D, float>> *inflateBalls)
|
||||
{
|
||||
QUuid componentIdNotAsString;
|
||||
|
||||
*inverse = false;
|
||||
|
||||
const std::map<QString, QString> *component = &m_snapshot->rootComponent;
|
||||
if (componentId != QUuid().toString()) {
|
||||
componentIdNotAsString = QUuid(componentId);
|
||||
|
@ -516,10 +522,19 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
|||
component = &findComponent->second;
|
||||
}
|
||||
|
||||
CombineMode combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData());
|
||||
if (combineMode == CombineMode::Inversion)
|
||||
*inverse = true;
|
||||
*combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData());
|
||||
|
||||
if (*combineMode == CombineMode::Inflation) {
|
||||
if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) {
|
||||
auto findCacheInflateBalls = m_cacheContext->componentInflateBalls.find(componentId);
|
||||
if (findCacheInflateBalls != m_cacheContext->componentInflateBalls.end()) {
|
||||
*inflateBalls = findCacheInflateBalls->second;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*combineMode != CombineMode::Inflation) {
|
||||
if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) {
|
||||
auto findCachedMesh = m_cacheContext->componentCombinableMeshs.find(componentId);
|
||||
if (findCachedMesh != m_cacheContext->componentCombinableMeshs.end() &&
|
||||
|
@ -528,6 +543,7 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
|||
return cloneCombinableMesh(findCachedMesh->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool smoothSeam = false;
|
||||
float smoothSeamFactor = 0.0;
|
||||
|
@ -553,17 +569,47 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
|||
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
||||
if ("partId" == linkDataType) {
|
||||
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
|
||||
resultMesh = combinePartMesh(partId);
|
||||
std::vector<std::pair<QVector3D, float>> partBalls;
|
||||
resultMesh = combinePartMesh(partId, &partBalls);
|
||||
if (*combineMode == CombineMode::Inflation) {
|
||||
deleteCombinableMesh(resultMesh);
|
||||
resultMesh = nullptr;
|
||||
inflateBalls->insert(inflateBalls->end(), partBalls.begin(), partBalls.end());
|
||||
} else {
|
||||
for (const auto &bmeshVertex: m_cacheContext->partBmeshVertices[partId]) {
|
||||
verticesSources.addPosition(bmeshVertex.first.x(), bmeshVertex.first.y(), bmeshVertex.first.z(),
|
||||
bmeshVertex);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
||||
if (childId.isEmpty())
|
||||
continue;
|
||||
bool childInverse = false;
|
||||
void *childCombinedMesh = combineComponentMesh(childId, &childInverse);
|
||||
CombineMode childCombineMode = CombineMode::Normal;
|
||||
std::vector<std::pair<QVector3D, float>> childInflateBalls;
|
||||
void *childCombinedMesh = combineComponentMesh(childId, &childCombineMode, &childInflateBalls);
|
||||
inflateBalls->insert(inflateBalls->end(), childInflateBalls.begin(), childInflateBalls.end());
|
||||
if (childCombineMode == CombineMode::Inflation) {
|
||||
deleteCombinableMesh(childCombinedMesh);
|
||||
childCombinedMesh = nullptr;
|
||||
if (nullptr == resultMesh)
|
||||
continue;
|
||||
std::vector<std::pair<QVector3D, QVector3D>> inflatedVertices;
|
||||
void *inflatedMesh = meshInflate(resultMesh, childInflateBalls, inflatedVertices);
|
||||
deleteCombinableMesh(resultMesh);
|
||||
resultMesh = inflatedMesh;
|
||||
for (const auto &item: inflatedVertices) {
|
||||
const auto &oldPosition = item.first;
|
||||
const auto &newPosition = item.second;
|
||||
std::pair<QVector3D, std::pair<QUuid, QUuid>> source;
|
||||
if (verticesSources.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z(), &source)) {
|
||||
verticesSources.removePosition(oldPosition.x(), oldPosition.y(), oldPosition.z());
|
||||
source.first = newPosition;
|
||||
verticesSources.addPosition(newPosition.x(), newPosition.y(), newPosition.z(), source);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (const auto &positionIt: m_cacheContext->componentPositions[childId]) {
|
||||
positionsBeforeCombination.addPosition(positionIt.x(), positionIt.y(), positionIt.z(), true);
|
||||
}
|
||||
|
@ -573,13 +619,13 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
|||
if (nullptr == childCombinedMesh)
|
||||
continue;
|
||||
if (nullptr == resultMesh) {
|
||||
if (childInverse) {
|
||||
if (childCombineMode == CombineMode::Inversion) {
|
||||
deleteCombinableMesh(childCombinedMesh);
|
||||
} else {
|
||||
resultMesh = childCombinedMesh;
|
||||
}
|
||||
} else {
|
||||
void *newResultMesh = childInverse ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh);
|
||||
void *newResultMesh = childCombineMode == CombineMode::Inversion ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh);
|
||||
deleteCombinableMesh(childCombinedMesh);
|
||||
if (nullptr != newResultMesh) {
|
||||
deleteCombinableMesh(resultMesh);
|
||||
|
@ -644,6 +690,8 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
|||
}
|
||||
}
|
||||
|
||||
m_cacheContext->componentInflateBalls[componentId] = *inflateBalls;
|
||||
|
||||
m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh);
|
||||
auto &cachedComponentPositions = m_cacheContext->componentPositions[componentId];
|
||||
cachedComponentPositions.clear();
|
||||
|
@ -713,6 +761,13 @@ void MeshGenerator::generate()
|
|||
}
|
||||
it++;
|
||||
}
|
||||
for (auto it = m_cacheContext->componentInflateBalls.begin(); it != m_cacheContext->componentInflateBalls.end(); ) {
|
||||
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
||||
it = m_cacheContext->componentInflateBalls.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
for (auto it = m_cacheContext->componentVerticesSources.begin(); it != m_cacheContext->componentVerticesSources.end(); ) {
|
||||
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
||||
it = m_cacheContext->componentVerticesSources.erase(it);
|
||||
|
@ -733,8 +788,9 @@ void MeshGenerator::generate()
|
|||
|
||||
int resultMeshId = 0;
|
||||
|
||||
bool inverse = false;
|
||||
void *combinedMesh = combineComponentMesh(QUuid().toString(), &inverse);
|
||||
CombineMode combineMode;
|
||||
std::vector<std::pair<QVector3D, float>> inflateBalls;
|
||||
void *combinedMesh = combineComponentMesh(QUuid().toString(), &combineMode, &inflateBalls);
|
||||
if (nullptr != combinedMesh) {
|
||||
resultMeshId = convertFromCombinableMesh(m_meshliteContext, combinedMesh);
|
||||
deleteCombinableMesh(combinedMesh);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "meshloader.h"
|
||||
#include "outcome.h"
|
||||
#include "positionmap.h"
|
||||
#include "combinemode.h"
|
||||
|
||||
class GeneratedCacheContext
|
||||
{
|
||||
|
@ -23,6 +24,7 @@ public:
|
|||
std::map<QString, std::vector<QVector3D>> componentPositions;
|
||||
std::map<QString, PositionMap<std::pair<QVector3D, std::pair<QUuid, QUuid>>>> componentVerticesSources;
|
||||
std::map<QString, QString> partMirrorIdMap;
|
||||
std::map<QString, std::vector<std::pair<QVector3D, float>>> componentInflateBalls;
|
||||
void updateComponentCombinableMesh(QString componentId, void *mesh);
|
||||
};
|
||||
|
||||
|
@ -78,8 +80,8 @@ private:
|
|||
void checkDirtyFlags();
|
||||
bool checkIsComponentDirty(QString componentId);
|
||||
bool checkIsPartDirty(QString partId);
|
||||
void *combineComponentMesh(QString componentId, bool *inverse);
|
||||
void *combinePartMesh(QString partId);
|
||||
void *combineComponentMesh(QString componentId, CombineMode *combineMode, std::vector<std::pair<QVector3D, float>> *inflateBalls);
|
||||
void *combinePartMesh(QString partId, std::vector<std::pair<QVector3D, float>> *balls=nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#include <QDebug>
|
||||
#include <set>
|
||||
#include "meshinflate.h"
|
||||
#include "meshutil.h"
|
||||
|
||||
void *meshInflate(void *combinableMesh, const std::vector<std::pair<QVector3D, float>> &inflateBalls, std::vector<std::pair<QVector3D, QVector3D>> &inflatedVertices)
|
||||
{
|
||||
std::vector<QVector3D> oldPositions;
|
||||
std::vector<QVector3D> newPositions;
|
||||
std::vector<std::vector<int>> faces;
|
||||
std::set<int> changedVertices;
|
||||
|
||||
loadCombinableMeshVerticesPositionsAndFacesIndices(combinableMesh, oldPositions, faces);
|
||||
newPositions = oldPositions;
|
||||
|
||||
std::vector<float> inflateBallRadius2s(inflateBalls.size());
|
||||
for (size_t i = 0; i < inflateBalls.size(); ++i) {
|
||||
const auto &radius = inflateBalls[i].second;
|
||||
inflateBallRadius2s[i] = radius * radius;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < oldPositions.size(); ++i) {
|
||||
const auto &oldPos = oldPositions[i];
|
||||
QVector3D sumOfNewPos;
|
||||
size_t pushedByBallNum = 0;
|
||||
for (size_t j = 0; j < inflateBalls.size(); ++j) {
|
||||
const auto &ball = inflateBalls[j];
|
||||
const auto &ballPos = ball.first;
|
||||
const auto &ballRadius = std::max(ball.second, (float)0.00001);
|
||||
const auto ballToPosVec = oldPos - ballPos;
|
||||
if (ballToPosVec.lengthSquared() > inflateBallRadius2s[j])
|
||||
continue;
|
||||
const auto distance = 1.0 - ballToPosVec.length() / ballRadius;
|
||||
const auto smoothedDist = 3.0 * std::pow(distance, 2) - 2.0 * std::pow(distance, 3);
|
||||
const auto newPosPushByBall = oldPos + ballToPosVec * smoothedDist;
|
||||
sumOfNewPos += newPosPushByBall;
|
||||
++pushedByBallNum;
|
||||
}
|
||||
if (0 == pushedByBallNum)
|
||||
continue;
|
||||
newPositions[i] = sumOfNewPos / pushedByBallNum;
|
||||
changedVertices.insert(i);
|
||||
}
|
||||
|
||||
for (const auto &index: changedVertices) {
|
||||
inflatedVertices.push_back({oldPositions[index], newPositions[index]});
|
||||
}
|
||||
|
||||
return buildCombinableMeshFromVerticesPositionsAndFacesIndices(newPositions, faces);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef DUST3D_MESH_INFLATE_H
|
||||
#define DUST3D_MESH_INFLATE_H
|
||||
#include <vector>
|
||||
#include <QVector3D>
|
||||
|
||||
void *meshInflate(void *combinableMesh, const std::vector<std::pair<QVector3D, float>> &inflateBalls, std::vector<std::pair<QVector3D, QVector3D>> &inflatedVertices);
|
||||
|
||||
#endif
|
|
@ -52,6 +52,25 @@ void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector<QV
|
|||
delete[] vertexPositions;
|
||||
}
|
||||
|
||||
template <class Kernel>
|
||||
typename CGAL::Surface_mesh<typename Kernel::Point_3> *buildCgalMesh(const std::vector<QVector3D> &positions, const std::vector<std::vector<int>> &indices)
|
||||
{
|
||||
typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh = new typename CGAL::Surface_mesh<typename Kernel::Point_3>;
|
||||
std::map<int, typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> oldToNewMap;
|
||||
for (int i = 0; i < (int)positions.size(); ++i) {
|
||||
const auto &pos = positions[i];
|
||||
oldToNewMap[i] = mesh->add_vertex(typename Kernel::Point_3(pos.x(), pos.y(), pos.z()));
|
||||
}
|
||||
for (const auto &face: indices) {
|
||||
std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> faceVertexIndices;
|
||||
for (const auto &index: face) {
|
||||
faceVertexIndices.push_back(oldToNewMap[index]);
|
||||
}
|
||||
mesh->add_face(faceVertexIndices);
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
template <class Kernel>
|
||||
typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(void *meshlite, int meshId)
|
||||
{
|
||||
|
@ -458,3 +477,54 @@ void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId)
|
|||
ExactMesh *mesh = makeCgalConvexHullMeshFromMeshlite<ExactKernel>(meshliteContext, meshId);
|
||||
return (void *)mesh;
|
||||
}
|
||||
|
||||
void loadCombinableMeshVerticesPositionsAndFacesIndices(void *mesh, std::vector<QVector3D> &positions, std::vector<std::vector<int>> &indices)
|
||||
{
|
||||
ExactMesh *exactMesh = (ExactMesh *)mesh;
|
||||
if (nullptr == exactMesh)
|
||||
return;
|
||||
|
||||
for (auto vertexIt = exactMesh->vertices_begin(); vertexIt != exactMesh->vertices_end(); vertexIt++) {
|
||||
auto point = exactMesh->point(*vertexIt);
|
||||
float x = (float)CGAL::to_double(point.x());
|
||||
float y = (float)CGAL::to_double(point.y());
|
||||
float z = (float)CGAL::to_double(point.z());
|
||||
if (std::isnan(x) || std::isinf(x))
|
||||
x = 0;
|
||||
if (std::isnan(y) || std::isinf(y))
|
||||
y = 0;
|
||||
if (std::isnan(z) || std::isinf(z))
|
||||
z = 0;
|
||||
positions.push_back(QVector3D(x, y, z));
|
||||
}
|
||||
|
||||
typename CGAL::Surface_mesh<ExactKernel::Point_3>::Face_range faceRage = exactMesh->faces();
|
||||
typename CGAL::Surface_mesh<ExactKernel::Point_3>::Face_range::iterator faceIt;
|
||||
for (faceIt = faceRage.begin(); faceIt != faceRage.end(); faceIt++) {
|
||||
CGAL::Vertex_around_face_iterator<typename CGAL::Surface_mesh<ExactKernel::Point_3>> vbegin, vend;
|
||||
std::vector<int> faceIndices;
|
||||
for (boost::tie(vbegin, vend) = CGAL::vertices_around_face(exactMesh->halfedge(*faceIt), *exactMesh);
|
||||
vbegin != vend;
|
||||
++vbegin){
|
||||
faceIndices.push_back(*vbegin);
|
||||
}
|
||||
indices.push_back(faceIndices);
|
||||
}
|
||||
}
|
||||
|
||||
void *buildCombinableMeshFromVerticesPositionsAndFacesIndices(const std::vector<QVector3D> &positions, const std::vector<std::vector<int>> &indices)
|
||||
{
|
||||
ExactMesh *mesh = nullptr;
|
||||
if (indices.empty())
|
||||
return nullptr;
|
||||
mesh = buildCgalMesh<ExactKernel>(positions, indices);
|
||||
if (nullptr == mesh) {
|
||||
return mesh;
|
||||
}
|
||||
if (CGAL::Polygon_mesh_processing::does_self_intersect(*mesh)) {
|
||||
qDebug() << "CGAL::Polygon_mesh_processing::does_self_intersect";
|
||||
delete mesh;
|
||||
return nullptr;
|
||||
}
|
||||
return (void *)mesh;
|
||||
}
|
||||
|
|
|
@ -21,5 +21,7 @@ void *cloneCombinableMesh(void *mesh);
|
|||
void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId);
|
||||
void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector<QVector3D> &positions);
|
||||
void loadCombinableMeshVerticesPositions(void *mesh, std::vector<QVector3D> &positions);
|
||||
void loadCombinableMeshVerticesPositionsAndFacesIndices(void *mesh, std::vector<QVector3D> &positions, std::vector<std::vector<int>> &indices);
|
||||
void *buildCombinableMeshFromVerticesPositionsAndFacesIndices(const std::vector<QVector3D> &positions, const std::vector<std::vector<int>> &indices);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue