diff --git a/dust3d.pro b/dust3d.pro index ed421c31..3e748574 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -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 diff --git a/src/gltffile.cpp b/src/gltffile.cpp index 12070731..5b16e55d 100644 --- a/src/gltffile.cpp +++ b/src/gltffile.cpp @@ -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; diff --git a/src/gltffile.h b/src/gltffile.h index 7d3b0e2d..79339e10 100644 --- a/src/gltffile.h +++ b/src/gltffile.h @@ -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 diff --git a/src/intermediateboneremover.cpp b/src/intermediateboneremover.cpp new file mode 100644 index 00000000..0c8086f4 --- /dev/null +++ b/src/intermediateboneremover.cpp @@ -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, IntermediateBoneNode> &IntermediateBoneRemover::intermediateNodes() +{ + return m_intermediateNodes; +} + +const std::vector> &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 pair = std::make_pair(partId, nodeId); + std::vector> trace; + solveFromPairAndSaveTraceTo(pair, trace); +} + +void IntermediateBoneRemover::addNeighborsOfIntermediateNodeFrom(std::pair 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 node, std::vector> &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> subHistory = history; + solveFromPairAndSaveTraceTo(neighbor, subHistory); + } +} + diff --git a/src/intermediateboneremover.h b/src/intermediateboneremover.h new file mode 100644 index 00000000..485115bc --- /dev/null +++ b/src/intermediateboneremover.h @@ -0,0 +1,39 @@ +#ifndef INTERMEDIATE_BONE_REMOVER_H +#define INTERMEDIATE_BONE_REMOVER_H +#include +#include +#include + +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, IntermediateBoneNode> &intermediateNodes(); + const std::vector> &newEdges(); +private: + void solveFromPairAndSaveTraceTo(std::pair node, std::vector> &history); + void addNeighborsOfIntermediateNodeFrom(std::pair node, const IntermediateBoneNode &source); + std::map, IntermediateBoneNode> m_intermediateNodes; + std::set> m_startNodeSet; + std::set> m_essentialNodeSet; + std::map, std::vector>> m_neighborMap; + std::set> m_solvedSet; + std::vector> m_newEdges; + std::set> m_addedSet; +}; + +#endif + diff --git a/src/meshresultcontext.cpp b/src/meshresultcontext.cpp index f76e1223..5230b199 100644 --- a/src/meshresultcontext.cpp +++ b/src/meshresultcontext.cpp @@ -401,6 +401,7 @@ const std::map, std::vector>> &MeshResul void MeshResultContext::calculateBmeshNodeNeighbors(std::map, std::vector>> &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> &MeshResultContext::vertexWei return m_resultVertexWeights; } -void MeshResultContext::calculateVertexWeights(std::vector> &vertexWeights) +void MeshResultContext::calculateVertexWeights(std::vector> &vertexWeights, const std::map, IntermediateBoneNode> *intermediateNodes) { vertexWeights.clear(); vertexWeights.resize(vertices.size()); @@ -493,6 +494,48 @@ void MeshResultContext::calculateVertexWeights(std::vector, 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, float> &a, const std::pair, 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 &MeshResultContext::parts() @@ -716,3 +759,46 @@ void MeshResultContext::calculateResultRearrangedVertices(std::vectorbmeshId, 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); +} diff --git a/src/meshresultcontext.h b/src/meshresultcontext.h index e7d85d96..2ecd8ceb 100644 --- a/src/meshresultcontext.h +++ b/src/meshresultcontext.h @@ -7,6 +7,7 @@ #include #include "positionmap.h" #include "skeletonbonemark.h" +#include "intermediateboneremover.h" #define MAX_WEIGHT_NUM 4 @@ -117,6 +118,7 @@ public: const std::vector &triangleUvs(); const std::vector &rearrangedVertices(); const std::vector &rearrangedTriangles(); + void removeIntermediateBones(); private: bool m_triangleSourceResolved; bool m_triangleColorResolved; @@ -154,7 +156,7 @@ private: void calculateBmeshNodeNeighbors(); void calculateBmeshEdgeDirectionsFromNode(std::pair node, std::set> &visitedNodes, std::set, std::pair>> &connections, std::vector &rearrangedEdges); void calculateBmeshNodeNeighbors(std::map, std::vector>> &nodeNeighbors); - void calculateVertexWeights(std::vector> &vertexWeights); + void calculateVertexWeights(std::vector> &vertexWeights, const std::map, IntermediateBoneNode> *intermediateNodes=nullptr); void calculateResultParts(std::map &parts); void calculateResultTriangleUvs(std::vector &uvs, std::set &seamVertices); void calculateResultRearrangedVertices(std::vector &rearrangedVertices, std::vector &rearrangedTriangles); diff --git a/src/meshresultpostprocessor.cpp b/src/meshresultpostprocessor.cpp index c4431fc0..c1f598a4 100644 --- a/src/meshresultpostprocessor.cpp +++ b/src/meshresultpostprocessor.cpp @@ -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(); diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll index cba200e2..2299ce4c 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll and b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib index 0235d89f..340aafd5 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib and b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll index f46f2e46..d3681cb7 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll and b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib index 4eaa05f0..9c300c86 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib and b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib differ