Add intermediate bone remover.

- Remove intermediate bones when user marked leg nodes
- Add flags to toggle gltf comment and normal output
- Fix face normal
- Adjust the skeleton bone mesh radius
master
Jeremy Hu 2018-06-01 12:38:16 +08:00
parent bed110b678
commit c4505ba788
12 changed files with 279 additions and 25 deletions

View File

@ -128,6 +128,9 @@ HEADERS += src/markiconcreator.h
SOURCES += src/skeletonbonemark.cpp
HEADERS += src/skeletonbonemark.h
SOURCES += src/intermediateboneremover.cpp
HEADERS += src/intermediateboneremover.h
HEADERS += src/qtlightmapper.h
SOURCES += src/main.cpp

View File

@ -17,8 +17,11 @@
// http://quaternions.online/
// https://en.m.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions?wprov=sfla1
bool GLTFFileWriter::m_enableComment = false;
GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &filename) :
m_filename(filename)
m_filename(filename),
m_outputNormal(true)
{
const BmeshNode *rootNode = resultContext.centerBmeshNode();
if (!rootNode) {
@ -107,6 +110,8 @@ GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &
int bufferViewIndex = 0;
if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: mat").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
m_json["accessors"][bufferViewIndex]["byteOffset"] = 0;
m_json["accessors"][bufferViewIndex]["componentType"] = 5126;
@ -140,12 +145,14 @@ GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &
int bufferViewFromOffset;
m_json["meshes"][0]["primitives"][primitiveIndex]["indices"] = bufferViewIndex;
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["POSITION"] = bufferViewIndex + 1;
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["NORMAL"] = bufferViewIndex + 2;
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["JOINTS_0"] = bufferViewIndex + 3;
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["WEIGHTS_0"] = bufferViewIndex + 4;
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["TEXCOORD_0"] = bufferViewIndex + 5;
m_json["meshes"][0]["primitives"][primitiveIndex]["material"] = primitiveIndex;
int attributeIndex = 0;
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["POSITION"] = bufferViewIndex + (++attributeIndex);
if (m_outputNormal)
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["NORMAL"] = bufferViewIndex + (++attributeIndex);
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["JOINTS_0"] = bufferViewIndex + (++attributeIndex);
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["WEIGHTS_0"] = bufferViewIndex + (++attributeIndex);
m_json["meshes"][0]["primitives"][primitiveIndex]["attributes"]["TEXCOORD_0"] = bufferViewIndex + (++attributeIndex);
/*
m_json["materials"][primitiveIndex]["pbrMetallicRoughness"]["baseColorFactor"] = {
part.second.color.redF(),
@ -169,6 +176,8 @@ GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &
m_json["bufferViews"][bufferViewIndex]["target"] = 34963;
Q_ASSERT(part.second.triangles.size() * 3 * sizeof(quint16) == binaries.size() - bufferViewFromOffset);
alignBinaries();
if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: triangle indicies").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
m_json["accessors"][bufferViewIndex]["byteOffset"] = 0;
m_json["accessors"][bufferViewIndex]["componentType"] = 5123;
@ -204,7 +213,8 @@ GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &
m_json["bufferViews"][bufferViewIndex]["byteLength"] = part.second.vertices.size() * 3 * sizeof(float);
m_json["bufferViews"][bufferViewIndex]["target"] = 34962;
alignBinaries();
if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: xyz").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
m_json["accessors"][bufferViewIndex]["byteOffset"] = 0;
m_json["accessors"][bufferViewIndex]["componentType"] = 5126;
@ -214,23 +224,29 @@ GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &
m_json["accessors"][bufferViewIndex]["min"] = {minX, minY, minZ};
bufferViewIndex++;
bufferViewFromOffset = (int)binaries.size();
m_json["bufferViews"][bufferViewIndex]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
for (const auto &it: part.second.interpolatedVertexNormals) {
stream << (float)it.x() << (float)it.y() << (float)it.z();
if (m_outputNormal) {
bufferViewFromOffset = (int)binaries.size();
m_json["bufferViews"][bufferViewIndex]["buffer"] = 0;
m_json["bufferViews"][bufferViewIndex]["byteOffset"] = bufferViewFromOffset;
QStringList normalList;
for (const auto &it: part.second.interpolatedVertexNormals) {
stream << (float)it.x() << (float)it.y() << (float)it.z();
if (m_outputNormal)
normalList.append(QString("<%1,%2,%3>").arg(QString::number(it.x())).arg(QString::number(it.y())).arg(QString::number(it.z())));
}
Q_ASSERT( part.second.interpolatedVertexNormals.size() * 3 * sizeof(float) == binaries.size() - bufferViewFromOffset);
m_json["bufferViews"][bufferViewIndex]["byteLength"] = part.second.vertices.size() * 3 * sizeof(float);
m_json["bufferViews"][bufferViewIndex]["target"] = 34962;
alignBinaries();
if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: normal %2").arg(QString::number(bufferViewIndex)).arg(normalList.join(" ")).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
m_json["accessors"][bufferViewIndex]["byteOffset"] = 0;
m_json["accessors"][bufferViewIndex]["componentType"] = 5126;
m_json["accessors"][bufferViewIndex]["count"] = part.second.vertices.size();
m_json["accessors"][bufferViewIndex]["type"] = "VEC3";
bufferViewIndex++;
}
Q_ASSERT( part.second.interpolatedVertexNormals.size() * 3 * sizeof(float) == binaries.size() - bufferViewFromOffset);
m_json["bufferViews"][bufferViewIndex]["byteLength"] = part.second.vertices.size() * 3 * sizeof(float);
m_json["bufferViews"][bufferViewIndex]["target"] = 34962;
alignBinaries();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
m_json["accessors"][bufferViewIndex]["byteOffset"] = 0;
m_json["accessors"][bufferViewIndex]["componentType"] = 5126;
m_json["accessors"][bufferViewIndex]["count"] = part.second.vertices.size();
m_json["accessors"][bufferViewIndex]["type"] = "VEC3";
bufferViewIndex++;
bufferViewFromOffset = (int)binaries.size();
m_json["bufferViews"][bufferViewIndex]["buffer"] = 0;
@ -246,6 +262,8 @@ GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &
}
m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset;
alignBinaries();
if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: bone indicies").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
m_json["accessors"][bufferViewIndex]["byteOffset"] = 0;
m_json["accessors"][bufferViewIndex]["componentType"] = 5123;
@ -267,6 +285,8 @@ GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &
}
m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset;
alignBinaries();
if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: bone weights").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
m_json["accessors"][bufferViewIndex]["byteOffset"] = 0;
m_json["accessors"][bufferViewIndex]["componentType"] = 5126;
@ -282,6 +302,8 @@ GLTFFileWriter::GLTFFileWriter(MeshResultContext &resultContext, const QString &
}
m_json["bufferViews"][bufferViewIndex]["byteLength"] = binaries.size() - bufferViewFromOffset;
alignBinaries();
if (m_enableComment)
m_json["accessors"][bufferViewIndex]["__comment"] = QString("/accessors/%1: uv").arg(QString::number(bufferViewIndex)).toUtf8().constData();
m_json["accessors"][bufferViewIndex]["bufferView"] = bufferViewIndex;
m_json["accessors"][bufferViewIndex]["byteOffset"] = 0;
m_json["accessors"][bufferViewIndex]["componentType"] = 5126;

View File

@ -36,8 +36,11 @@ private:
QString getMatrixStringInColumnOrder(const QMatrix4x4 &mat);
QString m_filename;
QString m_textureFilename;
bool m_outputNormal;
private:
nlohmann::json m_json;
public:
static bool m_enableComment;
};
#endif

View File

@ -0,0 +1,97 @@
#include "intermediateboneremover.h"
void IntermediateBoneRemover::addEdge(int fromPartId, int fromNodeId, int toPartId, int toNodeId)
{
auto &neighborMap = m_neighborMap[std::make_pair(fromPartId, fromNodeId)];
neighborMap.push_back(std::make_pair(toPartId, toNodeId));
}
const std::map<std::pair<int, int>, IntermediateBoneNode> &IntermediateBoneRemover::intermediateNodes()
{
return m_intermediateNodes;
}
const std::vector<std::tuple<int, int, int, int>> &IntermediateBoneRemover::newEdges()
{
return m_newEdges;
}
void IntermediateBoneRemover::markNodeAsStart(int partId, int nodeId)
{
m_startNodeSet.insert(std::make_pair(partId, nodeId));
}
void IntermediateBoneRemover::markNodeAsEssential(int partId, int nodeId)
{
m_essentialNodeSet.insert(std::make_pair(partId, nodeId));
}
void IntermediateBoneRemover::solveFrom(int partId, int nodeId)
{
std::pair<int, int> pair = std::make_pair(partId, nodeId);
std::vector<std::pair<int, int>> trace;
solveFromPairAndSaveTraceTo(pair, trace);
}
void IntermediateBoneRemover::addNeighborsOfIntermediateNodeFrom(std::pair<int, int> node, const IntermediateBoneNode &source)
{
if (m_addedSet.find(node) != m_addedSet.end())
return;
m_addedSet.insert(node);
if (m_essentialNodeSet.find(node) != m_essentialNodeSet.end() ||
m_startNodeSet.find(node) != m_startNodeSet.end())
return;
IntermediateBoneNode intermediate = source;
intermediate.partId = node.first;
intermediate.nodeId = node.second;
m_intermediateNodes[std::make_pair(intermediate.partId, intermediate.nodeId)] = intermediate;
const auto &it = m_neighborMap.find(node);
if (it == m_neighborMap.end())
return;
for (const auto &neighbor: it->second) {
addNeighborsOfIntermediateNodeFrom(neighbor, source);
}
}
void IntermediateBoneRemover::solveFromPairAndSaveTraceTo(std::pair<int, int> node, std::vector<std::pair<int, int>> &history)
{
if (m_solvedSet.find(node) != m_solvedSet.end())
return;
m_solvedSet.insert(node);
if (m_essentialNodeSet.find(node) != m_essentialNodeSet.end()) {
for (int i = history.size() - 1; i >= 0; i--) {
if (m_essentialNodeSet.find(history[i]) != m_essentialNodeSet.end() ||
m_startNodeSet.find(history[i]) != m_startNodeSet.end()) {
IntermediateBoneNode intermediate;
intermediate.attachedFromPartId = history[i].first;
intermediate.attachedFromNodeId = history[i].second;
intermediate.attachedToPartId = node.first;
intermediate.attachedToNodeId = node.second;
int removedNodeNum = 0;
for (int j = i + 1; j <= (int)history.size() - 1; j++) {
intermediate.partId = history[j].first;
intermediate.nodeId = history[j].second;
removedNodeNum++;
m_intermediateNodes[std::make_pair(intermediate.partId, intermediate.nodeId)] = intermediate;
addNeighborsOfIntermediateNodeFrom(std::make_pair(intermediate.partId, intermediate.nodeId), intermediate);
}
if (removedNodeNum > 0) {
m_newEdges.push_back(std::make_tuple(intermediate.attachedFromPartId,
intermediate.attachedFromNodeId,
intermediate.attachedToPartId,
intermediate.attachedToNodeId));
}
break;
}
}
}
history.push_back(node);
const auto &it = m_neighborMap.find(node);
if (it == m_neighborMap.end())
return;
for (const auto &neighbor: it->second) {
std::vector<std::pair<int, int>> subHistory = history;
solveFromPairAndSaveTraceTo(neighbor, subHistory);
}
}

View File

@ -0,0 +1,39 @@
#ifndef INTERMEDIATE_BONE_REMOVER_H
#define INTERMEDIATE_BONE_REMOVER_H
#include <vector>
#include <map>
#include <set>
struct IntermediateBoneNode
{
int partId;
int nodeId;
int attachedFromPartId;
int attachedFromNodeId;
int attachedToPartId;
int attachedToNodeId;
};
class IntermediateBoneRemover
{
public:
void addEdge(int fromPartId, int fromNodeId, int toPartId, int toNodeId);
void markNodeAsStart(int partId, int nodeId);
void markNodeAsEssential(int partId, int nodeId);
void solveFrom(int partId, int nodeId);
const std::map<std::pair<int, int>, IntermediateBoneNode> &intermediateNodes();
const std::vector<std::tuple<int, int, int, int>> &newEdges();
private:
void solveFromPairAndSaveTraceTo(std::pair<int, int> node, std::vector<std::pair<int, int>> &history);
void addNeighborsOfIntermediateNodeFrom(std::pair<int, int> node, const IntermediateBoneNode &source);
std::map<std::pair<int, int>, IntermediateBoneNode> m_intermediateNodes;
std::set<std::pair<int, int>> m_startNodeSet;
std::set<std::pair<int, int>> m_essentialNodeSet;
std::map<std::pair<int, int>, std::vector<std::pair<int, int>>> m_neighborMap;
std::set<std::pair<int, int>> m_solvedSet;
std::vector<std::tuple<int, int, int, int>> m_newEdges;
std::set<std::pair<int, int>> m_addedSet;
};
#endif

View File

@ -401,6 +401,7 @@ const std::map<std::pair<int, int>, std::vector<std::pair<int, int>>> &MeshResul
void MeshResultContext::calculateBmeshNodeNeighbors(std::map<std::pair<int, int>, std::vector<std::pair<int, int>>> &nodeNeighbors)
{
nodeNeighbors.clear();
for (const auto &it: bmeshEdges) {
nodeNeighbors[std::make_pair(it.fromBmeshId, it.fromNodeId)].push_back(std::make_pair(it.toBmeshId, it.toNodeId));
nodeNeighbors[std::make_pair(it.toBmeshId, it.toNodeId)].push_back(std::make_pair(it.fromBmeshId, it.fromNodeId));
@ -459,7 +460,7 @@ const std::vector<std::vector<ResultVertexWeight>> &MeshResultContext::vertexWei
return m_resultVertexWeights;
}
void MeshResultContext::calculateVertexWeights(std::vector<std::vector<ResultVertexWeight>> &vertexWeights)
void MeshResultContext::calculateVertexWeights(std::vector<std::vector<ResultVertexWeight>> &vertexWeights, const std::map<std::pair<int, int>, IntermediateBoneNode> *intermediateNodes)
{
vertexWeights.clear();
vertexWeights.resize(vertices.size());
@ -493,6 +494,48 @@ void MeshResultContext::calculateVertexWeights(std::vector<std::vector<ResultVer
it[i].weight = (float)it[i].count / total;
}
}
if (nullptr != intermediateNodes) {
// We removed some intermediate nodes, so we should recalculate the vertex weights.
for (auto &it: vertexWeights) {
std::vector<std::pair<std::pair<int, int>, float>> weights;
for (auto i = 0u; i < it.size(); i++) {
const auto &findInter = intermediateNodes->find(it[i].sourceNode);
if (findInter != intermediateNodes->end()) {
const auto &interBmeshNode = bmeshNodeMap().find(findInter->first);
const auto &attachedFromBmeshNode = bmeshNodeMap().find(std::make_pair(findInter->second.attachedFromPartId, findInter->second.attachedFromNodeId));
const auto &attachedToBmeshNode = bmeshNodeMap().find(std::make_pair(findInter->second.attachedToPartId, findInter->second.attachedToNodeId));
if (interBmeshNode != bmeshNodeMap().end() &&
attachedFromBmeshNode != bmeshNodeMap().end() &&
attachedToBmeshNode != bmeshNodeMap().end()) {
float distWithFrom = interBmeshNode->second->origin.distanceToPoint(attachedFromBmeshNode->second->origin);
float distWithTo = interBmeshNode->second->origin.distanceToPoint(attachedToBmeshNode->second->origin);
float distTotal = distWithFrom + distWithTo;
if (distTotal > 0) {
weights.push_back(std::make_pair(attachedFromBmeshNode->first, it[i].weight * distWithFrom / distTotal));
weights.push_back(std::make_pair(attachedToBmeshNode->first, it[i].weight * distWithTo / distTotal));
}
}
} else {
weights.push_back(std::make_pair(it[i].sourceNode, it[i].weight));
}
}
std::sort(weights.begin(), weights.end(), [](const std::pair<std::pair<int, int>, float> &a, const std::pair<std::pair<int, int>, float> &b) -> bool {
return a.second > b.second;
});
float total = 0;
for (auto i = 0u; i < MAX_WEIGHT_NUM && i < weights.size(); i++) {
total += weights[i].second;
}
for (auto i = 0u; i < MAX_WEIGHT_NUM && i < weights.size(); i++) {
weights[i].second = weights[i].second / total;
}
for (auto i = 0u; i < MAX_WEIGHT_NUM && i < weights.size(); i++) {
it[i].sourceNode = weights[i].first;
it[i].weight = weights[i].second;
it[i].count = -1; // no use
}
}
}
}
const std::map<int, ResultPart> &MeshResultContext::parts()
@ -716,3 +759,46 @@ void MeshResultContext::calculateResultRearrangedVertices(std::vector<ResultRear
rearrangedTriangles.push_back(newTriangle);
}
}
void MeshResultContext::removeIntermediateBones()
{
Q_ASSERT(m_vertexWeightsResolved);
Q_ASSERT(m_bmeshNodeNeighborsResolved);
IntermediateBoneRemover remover;
for (const auto &edge: bmeshEdges) {
remover.addEdge(edge.fromBmeshId, edge.fromNodeId, edge.toBmeshId, edge.toNodeId);
}
for (const auto &node: bmeshNodes) {
if (SkeletonBoneMarkIsStart(node.boneMark)) {
remover.markNodeAsStart(node.bmeshId, node.nodeId);
} else if (SkeletonBoneMark::None != node.boneMark) {
remover.markNodeAsEssential(node.bmeshId, node.nodeId);
}
}
const BmeshNode *centerNode = centerBmeshNode();
if (nullptr == centerNode)
return;
remover.solveFrom(centerNode->bmeshId, centerNode->nodeId);
const auto &intermediateNodes = remover.intermediateNodes();
calculateVertexWeights(m_resultVertexWeights, &intermediateNodes);
const auto oldEdges = bmeshEdges;
bmeshEdges.clear();
for (const auto &old: oldEdges) {
if (intermediateNodes.find(std::make_pair(old.fromBmeshId, old.fromNodeId)) != intermediateNodes.end() ||
intermediateNodes.find(std::make_pair(old.toBmeshId, old.toNodeId)) != intermediateNodes.end())
continue;
bmeshEdges.push_back(old);
}
for (const auto &edge: remover.newEdges()) {
BmeshEdge newEdge;
newEdge.fromBmeshId = std::get<0>(edge);
newEdge.fromNodeId = std::get<1>(edge);
newEdge.toBmeshId = std::get<2>(edge);
newEdge.toNodeId = std::get<3>(edge);
bmeshEdges.push_back(newEdge);
}
calculateBmeshNodeNeighbors(m_nodeNeighbors);
}

View File

@ -7,6 +7,7 @@
#include <QColor>
#include "positionmap.h"
#include "skeletonbonemark.h"
#include "intermediateboneremover.h"
#define MAX_WEIGHT_NUM 4
@ -117,6 +118,7 @@ public:
const std::vector<ResultTriangleUv> &triangleUvs();
const std::vector<ResultRearrangedVertex> &rearrangedVertices();
const std::vector<ResultRearrangedTriangle> &rearrangedTriangles();
void removeIntermediateBones();
private:
bool m_triangleSourceResolved;
bool m_triangleColorResolved;
@ -154,7 +156,7 @@ private:
void calculateBmeshNodeNeighbors();
void calculateBmeshEdgeDirectionsFromNode(std::pair<int, int> node, std::set<std::pair<int, int>> &visitedNodes, std::set<std::pair<std::pair<int, int>, std::pair<int, int>>> &connections, std::vector<BmeshEdge> &rearrangedEdges);
void calculateBmeshNodeNeighbors(std::map<std::pair<int, int>, std::vector<std::pair<int, int>>> &nodeNeighbors);
void calculateVertexWeights(std::vector<std::vector<ResultVertexWeight>> &vertexWeights);
void calculateVertexWeights(std::vector<std::vector<ResultVertexWeight>> &vertexWeights, const std::map<std::pair<int, int>, IntermediateBoneNode> *intermediateNodes=nullptr);
void calculateResultParts(std::map<int, ResultPart> &parts);
void calculateResultTriangleUvs(std::vector<ResultTriangleUv> &uvs, std::set<int> &seamVertices);
void calculateResultRearrangedVertices(std::vector<ResultRearrangedVertex> &rearrangedVertices, std::vector<ResultRearrangedTriangle> &rearrangedTriangles);

View File

@ -24,6 +24,8 @@ void MeshResultPostProcessor::process()
if (!m_meshResultContext->bmeshNodes.empty()) {
m_meshResultContext->resolveBmeshConnectivity();
m_meshResultContext->resolveBmeshEdgeDirections();
m_meshResultContext->vertexWeights();
m_meshResultContext->removeIntermediateBones();
m_meshResultContext->rearrangedVertices();
m_meshResultContext->rearrangedTriangles();
m_meshResultContext->parts();