dust3d/application/sources/document.cc

1694 lines
60 KiB
C++
Raw Normal View History

#include <QFileDialog>
#include <QDebug>
#include <QThread>
#include <QGuiApplication>
2018-04-09 08:46:06 +00:00
#include <QClipboard>
#include <QMimeData>
#include <QApplication>
2018-04-12 12:27:21 +00:00
#include <QVector3D>
#include <functional>
#include <QtCore/qbuffer.h>
2019-10-26 00:59:24 +00:00
#include <QElapsedTimer>
2019-12-14 13:28:14 +00:00
#include <queue>
#include <dust3d/base/snapshot_xml.h>
#include <dust3d/base/texture_type.h>
#include "document.h"
#include "image_forever.h"
#include "mesh_generator.h"
#include "mesh_result_post_processor.h"
#include "texture_generator.h"
#include "material_previews_generator.h"
unsigned long Document::m_maxSnapshot = 1000;
2018-04-08 23:34:46 +00:00
Document::Document() :
2020-12-19 05:37:44 +00:00
SkeletonDocument()
{
}
Document::~Document()
{
delete (dust3d::MeshGenerator::GeneratedCacheContext *)m_generatedCacheContext;
delete m_resultMesh;
2020-11-17 14:31:51 +00:00
delete m_postProcessedObject;
delete textureImage;
2020-11-17 14:31:51 +00:00
delete textureImageByteArray;
delete textureNormalImage;
2020-11-17 14:31:51 +00:00
delete textureNormalImageByteArray;
delete textureMetalnessImage;
2020-11-17 14:31:51 +00:00
delete textureMetalnessImageByteArray;
delete textureRoughnessImage;
2020-11-17 14:31:51 +00:00
delete textureRoughnessImageByteArray;
delete textureAmbientOcclusionImage;
2020-11-17 14:31:51 +00:00
delete textureAmbientOcclusionImageByteArray;
delete m_resultTextureMesh;
}
void Document::uiReady()
{
qDebug() << "uiReady";
emit editModeChanged();
}
bool Document::originSettled() const
2018-04-15 12:11:51 +00:00
{
2019-10-19 13:14:36 +00:00
return !qFuzzyIsNull(getOriginX()) && !qFuzzyIsNull(getOriginY()) && !qFuzzyIsNull(getOriginZ());
2018-04-15 12:11:51 +00:00
}
const Material *Document::findMaterial(dust3d::Uuid materialId) const
{
auto it = materialMap.find(materialId);
if (it == materialMap.end())
return nullptr;
return &it->second;
}
void Document::setNodeCutRotation(dust3d::Uuid nodeId, float cutRotation)
{
auto node = nodeMap.find(nodeId);
if (node == nodeMap.end()) {
qDebug() << "Node not found:" << nodeId;
return;
}
if (qFuzzyCompare(cutRotation, node->second.cutRotation))
return;
node->second.setCutRotation(cutRotation);
auto part = partMap.find(node->second.partId);
if (part != partMap.end())
part->second.dirty = true;
emit nodeCutRotationChanged(nodeId);
emit skeletonChanged();
}
void Document::setNodeCutFace(dust3d::Uuid nodeId, dust3d::CutFace cutFace)
{
auto node = nodeMap.find(nodeId);
if (node == nodeMap.end()) {
qDebug() << "Node not found:" << nodeId;
return;
}
if (node->second.cutFace == cutFace)
return;
node->second.setCutFace(cutFace);
auto part = partMap.find(node->second.partId);
if (part != partMap.end())
part->second.dirty = true;
emit nodeCutFaceChanged(nodeId);
emit skeletonChanged();
}
void Document::setNodeCutFaceLinkedId(dust3d::Uuid nodeId, dust3d::Uuid linkedId)
{
auto node = nodeMap.find(nodeId);
if (node == nodeMap.end()) {
qDebug() << "Node not found:" << nodeId;
return;
}
if (node->second.cutFace == dust3d::CutFace::UserDefined &&
node->second.cutFaceLinkedId == linkedId)
return;
node->second.setCutFaceLinkedId(linkedId);
auto part = partMap.find(node->second.partId);
if (part != partMap.end())
part->second.dirty = true;
emit nodeCutFaceChanged(nodeId);
emit skeletonChanged();
}
void Document::clearNodeCutFaceSettings(dust3d::Uuid nodeId)
{
auto node = nodeMap.find(nodeId);
if (node == nodeMap.end()) {
qDebug() << "Node not found:" << nodeId;
return;
}
if (!node->second.hasCutFaceSettings)
return;
node->second.clearCutFaceSettings();
auto part = partMap.find(node->second.partId);
if (part != partMap.end())
part->second.dirty = true;
emit nodeCutFaceChanged(nodeId);
emit skeletonChanged();
}
void Document::updateTurnaround(const QImage &image)
{
turnaround = image;
turnaroundPngByteArray.clear();
QBuffer pngBuffer(&turnaroundPngByteArray);
pngBuffer.open(QIODevice::WriteOnly);
turnaround.save(&pngBuffer, "PNG");
emit turnaroundChanged();
}
void Document::clearTurnaround()
{
turnaround = QImage();
turnaroundPngByteArray.clear();
emit turnaroundChanged();
}
2020-11-17 14:31:51 +00:00
void Document::updateTextureImage(QImage *image)
{
delete textureImageByteArray;
textureImageByteArray = nullptr;
delete textureImage;
textureImage = image;
}
void Document::updateTextureNormalImage(QImage *image)
{
delete textureNormalImageByteArray;
textureNormalImageByteArray = nullptr;
delete textureNormalImage;
textureNormalImage = image;
}
void Document::updateTextureMetalnessImage(QImage *image)
{
delete textureMetalnessImageByteArray;
textureMetalnessImageByteArray = nullptr;
delete textureMetalnessImage;
textureMetalnessImage = image;
}
void Document::updateTextureRoughnessImage(QImage *image)
{
delete textureRoughnessImageByteArray;
textureRoughnessImageByteArray = nullptr;
delete textureRoughnessImage;
textureRoughnessImage = image;
}
void Document::updateTextureAmbientOcclusionImage(QImage *image)
{
delete textureAmbientOcclusionImageByteArray;
textureAmbientOcclusionImageByteArray = nullptr;
delete textureAmbientOcclusionImage;
textureAmbientOcclusionImage = image;
}
void Document::setEditMode(SkeletonDocumentEditMode mode)
{
if (editMode == mode)
return;
editMode = mode;
emit editModeChanged();
}
void Document::toSnapshot(dust3d::Snapshot *snapshot, const std::set<dust3d::Uuid> &limitNodeIds,
2020-12-19 05:37:44 +00:00
DocumentToSnapshotFor forWhat,
const std::set<dust3d::Uuid> &limitMaterialIds) const
{
if (DocumentToSnapshotFor::Document == forWhat ||
DocumentToSnapshotFor::Nodes == forWhat) {
std::set<dust3d::Uuid> limitPartIds;
std::set<dust3d::Uuid> limitComponentIds;
for (const auto &nodeId: limitNodeIds) {
const SkeletonNode *node = findNode(nodeId);
if (!node)
continue;
const SkeletonPart *part = findPart(node->partId);
if (!part)
continue;
limitPartIds.insert(node->partId);
2020-12-19 05:37:44 +00:00
const SkeletonComponent *component = findComponent(part->componentId);
while (component) {
limitComponentIds.insert(component->id);
if (component->id.isNull())
break;
component = findComponent(component->parentId);
}
}
for (const auto &partIt : partMap) {
if (!limitPartIds.empty() && limitPartIds.find(partIt.first) == limitPartIds.end())
continue;
std::map<std::string, std::string> part;
part["id"] = partIt.second.id.toString();
part["visible"] = partIt.second.visible ? "true" : "false";
part["locked"] = partIt.second.locked ? "true" : "false";
part["subdived"] = partIt.second.subdived ? "true" : "false";
part["disabled"] = partIt.second.disabled ? "true" : "false";
part["xMirrored"] = partIt.second.xMirrored ? "true" : "false";
if (partIt.second.base != dust3d::PartBase::XYZ)
part["base"] = PartBaseToString(partIt.second.base);
part["rounded"] = partIt.second.rounded ? "true" : "false";
part["chamfered"] = partIt.second.chamfered ? "true" : "false";
if (dust3d::PartTarget::Model != partIt.second.target)
part["target"] = PartTargetToString(partIt.second.target);
if (partIt.second.cutRotationAdjusted())
part["cutRotation"] = std::to_string(partIt.second.cutRotation);
if (partIt.second.cutFaceAdjusted()) {
if (dust3d::CutFace::UserDefined == partIt.second.cutFace) {
if (!partIt.second.cutFaceLinkedId.isNull()) {
part["cutFace"] = partIt.second.cutFaceLinkedId.toString();
}
} else {
part["cutFace"] = CutFaceToString(partIt.second.cutFace);
}
}
part["__dirty"] = partIt.second.dirty ? "true" : "false";
2019-06-08 07:57:13 +00:00
if (partIt.second.hasColor)
part["color"] = partIt.second.color.name(QColor::HexArgb).toUtf8().constData();
if (partIt.second.colorSolubilityAdjusted())
part["colorSolubility"] = std::to_string(partIt.second.colorSolubility);
if (partIt.second.metalnessAdjusted())
part["metallic"] = std::to_string(partIt.second.metalness);
if (partIt.second.roughnessAdjusted())
part["roughness"] = std::to_string(partIt.second.roughness);
if (partIt.second.deformThicknessAdjusted())
part["deformThickness"] = std::to_string(partIt.second.deformThickness);
if (partIt.second.deformWidthAdjusted())
part["deformWidth"] = std::to_string(partIt.second.deformWidth);
if (partIt.second.deformUnified)
part["deformUnified"] = "true";
2019-08-18 12:02:39 +00:00
if (partIt.second.hollowThicknessAdjusted())
part["hollowThickness"] = std::to_string(partIt.second.hollowThickness);
if (!partIt.second.name.isEmpty())
part["name"] = partIt.second.name.toUtf8().constData();
if (partIt.second.materialAdjusted())
part["materialId"] = partIt.second.materialId.toString();
if (partIt.second.countershaded)
part["countershaded"] = "true";
if (partIt.second.smooth)
part["smooth"] = "true";
snapshot->parts[part["id"]] = part;
}
for (const auto &nodeIt: nodeMap) {
if (!limitNodeIds.empty() && limitNodeIds.find(nodeIt.first) == limitNodeIds.end())
continue;
std::map<std::string, std::string> node;
node["id"] = nodeIt.second.id.toString();
node["radius"] = std::to_string(nodeIt.second.radius);
node["x"] = std::to_string(nodeIt.second.getX());
node["y"] = std::to_string(nodeIt.second.getY());
node["z"] = std::to_string(nodeIt.second.getZ());
node["partId"] = nodeIt.second.partId.toString();
if (nodeIt.second.hasCutFaceSettings) {
node["cutRotation"] = std::to_string(nodeIt.second.cutRotation);
if (dust3d::CutFace::UserDefined == nodeIt.second.cutFace) {
if (!nodeIt.second.cutFaceLinkedId.isNull()) {
node["cutFace"] = nodeIt.second.cutFaceLinkedId.toString();
}
} else {
node["cutFace"] = CutFaceToString(nodeIt.second.cutFace);
}
}
if (!nodeIt.second.name.isEmpty())
node["name"] = nodeIt.second.name.toUtf8().constData();
snapshot->nodes[node["id"]] = node;
}
for (const auto &edgeIt: edgeMap) {
if (edgeIt.second.nodeIds.size() != 2)
continue;
if (!limitNodeIds.empty() &&
(limitNodeIds.find(edgeIt.second.nodeIds[0]) == limitNodeIds.end() ||
limitNodeIds.find(edgeIt.second.nodeIds[1]) == limitNodeIds.end()))
continue;
std::map<std::string, std::string> edge;
edge["id"] = edgeIt.second.id.toString();
edge["from"] = edgeIt.second.nodeIds[0].toString();
edge["to"] = edgeIt.second.nodeIds[1].toString();
edge["partId"] = edgeIt.second.partId.toString();
if (!edgeIt.second.name.isEmpty())
edge["name"] = edgeIt.second.name.toUtf8().constData();
snapshot->edges[edge["id"]] = edge;
}
for (const auto &componentIt: componentMap) {
if (!limitComponentIds.empty() && limitComponentIds.find(componentIt.first) == limitComponentIds.end())
continue;
std::map<std::string, std::string> component;
component["id"] = componentIt.second.id.toString();
if (!componentIt.second.name.isEmpty())
component["name"] = componentIt.second.name.toUtf8().constData();
component["expanded"] = componentIt.second.expanded ? "true" : "false";
component["combineMode"] = CombineModeToString(componentIt.second.combineMode);
component["__dirty"] = componentIt.second.dirty ? "true" : "false";
std::vector<std::string> childIdList;
for (const auto &childId: componentIt.second.childrenIds) {
childIdList.push_back(childId.toString());
}
std::string children = dust3d::String::join(childIdList, ",");
if (!children.empty())
component["children"] = children;
std::string linkData = componentIt.second.linkData().toUtf8().constData();
if (!linkData.empty()) {
component["linkData"] = linkData;
component["linkDataType"] = componentIt.second.linkDataType().toUtf8().constData();
}
if (!componentIt.second.name.isEmpty())
component["name"] = componentIt.second.name.toUtf8().constData();
snapshot->components[component["id"]] = component;
}
if (limitComponentIds.empty() || limitComponentIds.find(dust3d::Uuid()) != limitComponentIds.end()) {
std::vector<std::string> childIdList;
for (const auto &childId: rootComponent.childrenIds) {
childIdList.push_back(childId.toString());
}
std::string children = dust3d::String::join(childIdList, ",");
if (!children.empty())
snapshot->rootComponent["children"] = children;
}
}
if (DocumentToSnapshotFor::Document == forWhat ||
DocumentToSnapshotFor::Materials == forWhat) {
for (const auto &materialId: materialIdList) {
if (!limitMaterialIds.empty() && limitMaterialIds.find(materialId) == limitMaterialIds.end())
continue;
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult == materialMap.end()) {
qDebug() << "Find material failed:" << materialId;
continue;
}
auto &materialIt = *findMaterialResult;
std::map<std::string, std::string> material;
material["id"] = materialIt.second.id.toString();
material["type"] = "MetalRoughness";
if (!materialIt.second.name.isEmpty())
material["name"] = materialIt.second.name.toUtf8().constData();
std::vector<std::pair<std::map<std::string, std::string>, std::vector<std::map<std::string, std::string>>>> layers;
for (const auto &layer: materialIt.second.layers) {
std::vector<std::map<std::string, std::string>> maps;
for (const auto &mapItem: layer.maps) {
std::map<std::string, std::string> textureMap;
textureMap["for"] = TextureTypeToString(mapItem.forWhat);
textureMap["linkDataType"] = "imageId";
textureMap["linkData"] = mapItem.imageId.toString();
maps.push_back(textureMap);
}
std::map<std::string, std::string> layerAttributes;
if (!qFuzzyCompare((float)layer.tileScale, (float)1.0))
layerAttributes["tileScale"] = std::to_string(layer.tileScale);
layers.push_back({layerAttributes, maps});
}
snapshot->materials.push_back(std::make_pair(material, layers));
}
}
if (DocumentToSnapshotFor::Document == forWhat) {
std::map<std::string, std::string> canvas;
canvas["originX"] = std::to_string(getOriginX());
canvas["originY"] = std::to_string(getOriginY());
canvas["originZ"] = std::to_string(getOriginZ());
snapshot->canvas = canvas;
}
}
void Document::addFromSnapshot(const dust3d::Snapshot &snapshot, enum SnapshotSource source)
{
bool isOriginChanged = false;
if (SnapshotSource::Paste != source &&
SnapshotSource::Import != source) {
const auto &originXit = snapshot.canvas.find("originX");
const auto &originYit = snapshot.canvas.find("originY");
const auto &originZit = snapshot.canvas.find("originZ");
if (originXit != snapshot.canvas.end() &&
originYit != snapshot.canvas.end() &&
originZit != snapshot.canvas.end()) {
setOriginX(dust3d::String::toFloat(originXit->second));
setOriginY(dust3d::String::toFloat(originYit->second));
setOriginZ(dust3d::String::toFloat(originZit->second));
isOriginChanged = true;
}
2018-04-15 12:11:51 +00:00
}
std::set<dust3d::Uuid> newAddedNodeIds;
std::set<dust3d::Uuid> newAddedEdgeIds;
std::set<dust3d::Uuid> newAddedPartIds;
std::set<dust3d::Uuid> newAddedComponentIds;
std::set<dust3d::Uuid> inversePartIds;
std::map<dust3d::Uuid, dust3d::Uuid> oldNewIdMap;
for (const auto &materialIt: snapshot.materials) {
const auto &materialAttributes = materialIt.first;
auto materialType = dust3d::String::valueOrEmpty(materialAttributes, "type");
if ("MetalRoughness" != materialType) {
qDebug() << "Unsupported material type:" << materialType;
continue;
}
dust3d::Uuid oldMaterialId = dust3d::Uuid(dust3d::String::valueOrEmpty(materialAttributes, "id"));
dust3d::Uuid newMaterialId = SnapshotSource::Import == source ? oldMaterialId : dust3d::Uuid::createUuid();
oldNewIdMap[oldMaterialId] = newMaterialId;
if (materialMap.end() == materialMap.find(newMaterialId)) {
auto &newMaterial = materialMap[newMaterialId];
newMaterial.id = newMaterialId;
newMaterial.name = dust3d::String::valueOrEmpty(materialAttributes, "name").c_str();
for (const auto &layerIt: materialIt.second) {
MaterialLayer layer;
auto findTileScale = layerIt.first.find("tileScale");
if (findTileScale != layerIt.first.end())
layer.tileScale = dust3d::String::toFloat(findTileScale->second);
for (const auto &mapItem: layerIt.second) {
auto textureTypeString = dust3d::String::valueOrEmpty(mapItem, "for");
auto textureType = dust3d::TextureTypeFromString(textureTypeString.c_str());
if (dust3d::TextureType::None == textureType) {
qDebug() << "Unsupported texture type:" << textureTypeString;
continue;
}
auto linkTypeString = dust3d::String::valueOrEmpty(mapItem, "linkDataType");
if ("imageId" != linkTypeString) {
qDebug() << "Unsupported link data type:" << linkTypeString;
continue;
}
auto imageId = dust3d::Uuid(dust3d::String::valueOrEmpty(mapItem, "linkData"));
MaterialMap materialMap;
materialMap.imageId = imageId;
materialMap.forWhat = textureType;
layer.maps.push_back(materialMap);
}
newMaterial.layers.push_back(layer);
}
materialIdList.push_back(newMaterialId);
emit materialAdded(newMaterialId);
}
}
std::map<dust3d::Uuid, dust3d::Uuid> cutFaceLinkedIdModifyMap;
for (const auto &partKv: snapshot.parts) {
const auto newUuid = dust3d::Uuid::createUuid();
SkeletonPart &part = partMap[newUuid];
part.id = newUuid;
oldNewIdMap[dust3d::Uuid(partKv.first)] = part.id;
part.name = dust3d::String::valueOrEmpty(partKv.second, "name").c_str();
const auto &visibleIt = partKv.second.find("visible");
if (visibleIt != partKv.second.end()) {
part.visible = dust3d::String::isTrue(visibleIt->second);
} else {
part.visible = true;
}
part.locked = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "locked"));
part.subdived = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "subdived"));
part.disabled = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "disabled"));
part.xMirrored = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "xMirrored"));
part.base = dust3d::PartBaseFromString(dust3d::String::valueOrEmpty(partKv.second, "base").c_str());
part.rounded = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "rounded"));
part.chamfered = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "chamfered"));
part.target = dust3d::PartTargetFromString(dust3d::String::valueOrEmpty(partKv.second, "target").c_str());
const auto &cutRotationIt = partKv.second.find("cutRotation");
if (cutRotationIt != partKv.second.end())
part.setCutRotation(dust3d::String::toFloat(cutRotationIt->second));
const auto &cutFaceIt = partKv.second.find("cutFace");
if (cutFaceIt != partKv.second.end()) {
dust3d::Uuid cutFaceLinkedId = dust3d::Uuid(cutFaceIt->second);
if (cutFaceLinkedId.isNull()) {
part.setCutFace(dust3d::CutFaceFromString(cutFaceIt->second.c_str()));
} else {
part.setCutFaceLinkedId(cutFaceLinkedId);
cutFaceLinkedIdModifyMap.insert({part.id, cutFaceLinkedId});
}
}
if (dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "inverse")))
inversePartIds.insert(part.id);
const auto &colorIt = partKv.second.find("color");
if (colorIt != partKv.second.end()) {
part.color = QColor(colorIt->second.c_str());
part.hasColor = true;
}
const auto &colorSolubilityIt = partKv.second.find("colorSolubility");
if (colorSolubilityIt != partKv.second.end())
part.colorSolubility = dust3d::String::toFloat(colorSolubilityIt->second);
2020-11-17 14:31:51 +00:00
const auto &metalnessIt = partKv.second.find("metallic");
if (metalnessIt != partKv.second.end())
part.metalness = dust3d::String::toFloat(metalnessIt->second);
const auto &roughnessIt = partKv.second.find("roughness");
if (roughnessIt != partKv.second.end())
part.roughness = dust3d::String::toFloat(roughnessIt->second);
2018-04-17 16:14:31 +00:00
const auto &deformThicknessIt = partKv.second.find("deformThickness");
if (deformThicknessIt != partKv.second.end())
part.setDeformThickness(dust3d::String::toFloat(deformThicknessIt->second));
2018-04-17 16:14:31 +00:00
const auto &deformWidthIt = partKv.second.find("deformWidth");
if (deformWidthIt != partKv.second.end())
part.setDeformWidth(dust3d::String::toFloat(deformWidthIt->second));
const auto &deformUnifiedIt = partKv.second.find("deformUnified");
if (deformUnifiedIt != partKv.second.end())
part.deformUnified = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "deformUnified"));
2019-08-18 12:02:39 +00:00
const auto &hollowThicknessIt = partKv.second.find("hollowThickness");
if (hollowThicknessIt != partKv.second.end())
part.hollowThickness = dust3d::String::toFloat(hollowThicknessIt->second);
const auto &materialIdIt = partKv.second.find("materialId");
if (materialIdIt != partKv.second.end())
part.materialId = oldNewIdMap[dust3d::Uuid(materialIdIt->second)];
part.countershaded = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "countershaded"));
part.smooth = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "smooth"));
newAddedPartIds.insert(part.id);
}
for (const auto &it: cutFaceLinkedIdModifyMap) {
SkeletonPart &part = partMap[it.first];
auto findNewLinkedId = oldNewIdMap.find(it.second);
if (findNewLinkedId == oldNewIdMap.end()) {
if (partMap.find(it.second) == partMap.end()) {
part.setCutFaceLinkedId(dust3d::Uuid());
}
} else {
part.setCutFaceLinkedId(findNewLinkedId->second);
}
}
for (const auto &nodeKv: snapshot.nodes) {
2018-04-09 08:46:06 +00:00
if (nodeKv.second.find("radius") == nodeKv.second.end() ||
nodeKv.second.find("x") == nodeKv.second.end() ||
nodeKv.second.find("y") == nodeKv.second.end() ||
nodeKv.second.find("z") == nodeKv.second.end() ||
nodeKv.second.find("partId") == nodeKv.second.end())
continue;
dust3d::Uuid oldNodeId = dust3d::Uuid(nodeKv.first);
SkeletonNode node(nodeMap.find(oldNodeId) == nodeMap.end() ? oldNodeId : dust3d::Uuid::createUuid());
oldNewIdMap[oldNodeId] = node.id;
node.name = dust3d::String::valueOrEmpty(nodeKv.second, "name").c_str();
node.radius = dust3d::String::toFloat(dust3d::String::valueOrEmpty(nodeKv.second, "radius"));
node.setX(dust3d::String::toFloat(dust3d::String::valueOrEmpty(nodeKv.second, "x")));
node.setY(dust3d::String::toFloat(dust3d::String::valueOrEmpty(nodeKv.second, "y")));
node.setZ(dust3d::String::toFloat(dust3d::String::valueOrEmpty(nodeKv.second, "z")));
node.partId = oldNewIdMap[dust3d::Uuid(dust3d::String::valueOrEmpty(nodeKv.second, "partId"))];
const auto &cutRotationIt = nodeKv.second.find("cutRotation");
if (cutRotationIt != nodeKv.second.end())
node.setCutRotation(dust3d::String::toFloat(cutRotationIt->second));
const auto &cutFaceIt = nodeKv.second.find("cutFace");
if (cutFaceIt != nodeKv.second.end()) {
dust3d::Uuid cutFaceLinkedId = dust3d::Uuid(cutFaceIt->second);
if (cutFaceLinkedId.isNull()) {
node.setCutFace(dust3d::CutFaceFromString(cutFaceIt->second.c_str()));
} else {
node.setCutFaceLinkedId(cutFaceLinkedId);
auto findNewLinkedId = oldNewIdMap.find(cutFaceLinkedId);
if (findNewLinkedId == oldNewIdMap.end()) {
if (partMap.find(cutFaceLinkedId) == partMap.end()) {
node.setCutFaceLinkedId(dust3d::Uuid());
}
} else {
node.setCutFaceLinkedId(findNewLinkedId->second);
}
}
}
nodeMap[node.id] = node;
newAddedNodeIds.insert(node.id);
}
for (const auto &edgeKv: snapshot.edges) {
2018-04-09 08:46:06 +00:00
if (edgeKv.second.find("from") == edgeKv.second.end() ||
edgeKv.second.find("to") == edgeKv.second.end() ||
edgeKv.second.find("partId") == edgeKv.second.end())
continue;
dust3d::Uuid oldEdgeId = dust3d::Uuid(edgeKv.first);
SkeletonEdge edge(edgeMap.find(oldEdgeId) == edgeMap.end() ? oldEdgeId : dust3d::Uuid::createUuid());
oldNewIdMap[oldEdgeId] = edge.id;
edge.name = dust3d::String::valueOrEmpty(edgeKv.second, "name").c_str();
edge.partId = oldNewIdMap[dust3d::Uuid(dust3d::String::valueOrEmpty(edgeKv.second, "partId"))];
std::string fromNodeId = dust3d::String::valueOrEmpty(edgeKv.second, "from");
if (!fromNodeId.empty()) {
dust3d::Uuid fromId = oldNewIdMap[dust3d::Uuid(fromNodeId)];
2018-04-09 08:46:06 +00:00
edge.nodeIds.push_back(fromId);
nodeMap[fromId].edgeIds.push_back(edge.id);
2018-04-08 23:42:59 +00:00
}
std::string toNodeId = dust3d::String::valueOrEmpty(edgeKv.second, "to");
if (!toNodeId.empty()) {
dust3d::Uuid toId = oldNewIdMap[dust3d::Uuid(toNodeId)];
2018-04-09 08:46:06 +00:00
edge.nodeIds.push_back(toId);
nodeMap[toId].edgeIds.push_back(edge.id);
2018-04-08 23:42:59 +00:00
}
edgeMap[edge.id] = edge;
newAddedEdgeIds.insert(edge.id);
}
2018-04-09 05:22:25 +00:00
for (const auto &nodeIt: nodeMap) {
if (newAddedNodeIds.find(nodeIt.first) == newAddedNodeIds.end())
continue;
2018-04-09 05:22:25 +00:00
partMap[nodeIt.second.partId].nodeIds.push_back(nodeIt.first);
}
for (const auto &componentKv: snapshot.components) {
QString linkData = dust3d::String::valueOrEmpty(componentKv.second, "linkData").c_str();
QString linkDataType = dust3d::String::valueOrEmpty(componentKv.second, "linkDataType").c_str();
SkeletonComponent component(dust3d::Uuid(), linkData, linkDataType);
oldNewIdMap[dust3d::Uuid(componentKv.first)] = component.id;
component.name = dust3d::String::valueOrEmpty(componentKv.second, "name").c_str();
component.expanded = dust3d::String::isTrue(dust3d::String::valueOrEmpty(componentKv.second, "expanded"));
component.combineMode = dust3d::CombineModeFromString(dust3d::String::valueOrEmpty(componentKv.second, "combineMode").c_str());
if (component.combineMode == dust3d::CombineMode::Normal) {
if (dust3d::String::isTrue(dust3d::String::valueOrEmpty(componentKv.second, "inverse")))
component.combineMode = dust3d::CombineMode::Inversion;
}
//qDebug() << "Add component:" << component.id << " old:" << componentKv.first << "name:" << component.name;
if ("partId" == linkDataType) {
dust3d::Uuid partId = oldNewIdMap[dust3d::Uuid(linkData.toUtf8().constData())];
component.linkToPartId = partId;
//qDebug() << "Add part:" << partId << " from component:" << component.id;
partMap[partId].componentId = component.id;
if (inversePartIds.find(partId) != inversePartIds.end())
component.combineMode = dust3d::CombineMode::Inversion;
}
componentMap[component.id] = component;
newAddedComponentIds.insert(component.id);
}
const auto &rootComponentChildren = snapshot.rootComponent.find("children");
if (rootComponentChildren != snapshot.rootComponent.end()) {
for (const auto &childId: dust3d::String::split(rootComponentChildren->second, ',')) {
if (childId.empty())
continue;
dust3d::Uuid componentId = oldNewIdMap[dust3d::Uuid(childId)];
if (componentMap.find(componentId) == componentMap.end())
continue;
//qDebug() << "Add root component:" << componentId;
rootComponent.addChild(componentId);
}
}
for (const auto &componentKv: snapshot.components) {
dust3d::Uuid componentId = oldNewIdMap[dust3d::Uuid(componentKv.first)];
if (componentMap.find(componentId) == componentMap.end())
continue;
for (const auto &childId: dust3d::String::split(dust3d::String::valueOrEmpty(componentKv.second, "children"), ',')) {
if (childId.empty())
continue;
dust3d::Uuid childComponentId = oldNewIdMap[dust3d::Uuid(childId)];
if (componentMap.find(childComponentId) == componentMap.end())
continue;
//qDebug() << "Add child component:" << childComponentId << "to" << componentId;
componentMap[componentId].addChild(childComponentId);
componentMap[childComponentId].parentId = componentId;
}
}
for (const auto &nodeIt: newAddedNodeIds) {
emit nodeAdded(nodeIt);
}
for (const auto &edgeIt: newAddedEdgeIds) {
emit edgeAdded(edgeIt);
}
for (const auto &partIt : newAddedPartIds) {
emit partAdded(partIt);
}
emit componentChildrenChanged(dust3d::Uuid());
if (isOriginChanged)
emit originChanged();
2018-04-08 23:34:46 +00:00
emit skeletonChanged();
for (const auto &partIt : newAddedPartIds) {
emit partVisibleStateChanged(partIt);
}
emit uncheckAll();
for (const auto &nodeIt: newAddedNodeIds) {
emit checkNode(nodeIt);
}
for (const auto &edgeIt: newAddedEdgeIds) {
emit checkEdge(edgeIt);
}
if (!snapshot.materials.empty())
emit materialListChanged();
}
void Document::silentReset()
2018-04-09 08:46:06 +00:00
{
2019-10-19 13:14:36 +00:00
setOriginX(0.0);
setOriginY(0.0);
setOriginZ(0.0);
2018-04-09 08:46:06 +00:00
nodeMap.clear();
edgeMap.clear();
partMap.clear();
componentMap.clear();
materialMap.clear();
materialIdList.clear();
2020-12-19 05:37:44 +00:00
rootComponent = SkeletonComponent();
}
void Document::reset()
{
silentReset();
emit cleanup();
emit skeletonChanged();
}
void Document::fromSnapshot(const dust3d::Snapshot &snapshot)
{
reset();
addFromSnapshot(snapshot, SnapshotSource::Unknown);
emit uncheckAll();
2018-04-09 08:46:06 +00:00
}
2022-09-23 15:54:49 +00:00
ModelMesh *Document::takeResultMesh()
{
if (nullptr == m_resultMesh)
return nullptr;
2022-09-23 15:54:49 +00:00
ModelMesh *resultMesh = new ModelMesh(*m_resultMesh);
return resultMesh;
}
2022-09-23 15:54:49 +00:00
MonochromeMesh *Document::takeWireframeMesh()
{
if (nullptr == m_wireframeMesh)
return nullptr;
return new MonochromeMesh(*m_wireframeMesh);
}
bool Document::isMeshGenerationSucceed()
{
return m_isMeshGenerationSucceed;
}
2022-09-23 15:54:49 +00:00
ModelMesh *Document::takeResultTextureMesh()
{
if (nullptr == m_resultTextureMesh)
return nullptr;
2022-09-23 15:54:49 +00:00
ModelMesh *resultTextureMesh = new ModelMesh(*m_resultTextureMesh);
return resultTextureMesh;
}
void Document::meshReady()
{
2022-09-23 15:54:49 +00:00
ModelMesh *resultMesh = m_meshGenerator->takeResultMesh();
m_wireframeMesh.reset(m_meshGenerator->takeWireframeMesh());
dust3d::Object *object = m_meshGenerator->takeObject();
bool isSuccessful = m_meshGenerator->isSuccessful();
for (auto &partId: m_meshGenerator->generatedPreviewImagePartIds()) {
auto part = partMap.find(partId);
if (part != partMap.end()) {
part->second.isPreviewMeshObsolete = false;
QImage *resultPartPreviewImage = m_meshGenerator->takePartPreviewImage(partId);
if (nullptr != resultPartPreviewImage)
part->second.previewPixmap = QPixmap::fromImage(*resultPartPreviewImage);
delete resultPartPreviewImage;
emit partPreviewChanged(partId);
}
}
2020-11-17 14:31:51 +00:00
bool partPreviewsChanged = false;
for (auto &partId: m_meshGenerator->generatedPreviewPartIds()) {
auto part = partMap.find(partId);
if (part != partMap.end()) {
2022-09-23 15:54:49 +00:00
ModelMesh *resultPartPreviewMesh = m_meshGenerator->takePartPreviewMesh(partId);
part->second.updatePreviewMesh(resultPartPreviewMesh);
2020-11-17 14:31:51 +00:00
partPreviewsChanged = true;
}
}
2020-11-17 14:31:51 +00:00
if (partPreviewsChanged)
emit resultPartPreviewsChanged();
delete m_resultMesh;
m_resultMesh = resultMesh;
m_isMeshGenerationSucceed = isSuccessful;
2020-11-17 14:31:51 +00:00
delete m_currentObject;
m_currentObject = object;
if (nullptr == m_resultMesh) {
qDebug() << "Result mesh is null";
}
delete m_meshGenerator;
m_meshGenerator = nullptr;
qDebug() << "Mesh generation done";
m_isPostProcessResultObsolete = true;
emit resultMeshChanged();
if (m_isResultMeshObsolete) {
generateMesh();
}
}
bool Document::isPostProcessResultObsolete() const
{
return m_isPostProcessResultObsolete;
}
void Document::batchChangeBegin()
2018-04-09 14:24:30 +00:00
{
m_batchChangeRefCount++;
}
void Document::batchChangeEnd()
2018-04-09 14:24:30 +00:00
{
m_batchChangeRefCount--;
if (0 == m_batchChangeRefCount) {
if (m_isResultMeshObsolete) {
2018-04-09 14:24:30 +00:00
generateMesh();
}
}
}
void Document::regenerateMesh()
{
markAllDirty();
generateMesh();
}
void Document::toggleSmoothNormal()
2018-09-07 12:51:33 +00:00
{
m_smoothNormal = !m_smoothNormal;
regenerateMesh();
}
void Document::enableWeld(bool enabled)
{
weldEnabled = enabled;
regenerateMesh();
}
void Document::generateMesh()
{
2018-04-09 14:24:30 +00:00
if (nullptr != m_meshGenerator || m_batchChangeRefCount > 0) {
m_isResultMeshObsolete = true;
return;
}
emit meshGenerating();
qDebug() << "Mesh generating..";
settleOrigin();
m_isResultMeshObsolete = false;
QThread *thread = new QThread;
dust3d::Snapshot *snapshot = new dust3d::Snapshot;
toSnapshot(snapshot);
resetDirtyFlags();
m_meshGenerator = new MeshGenerator(snapshot);
m_meshGenerator->setId(m_nextMeshGenerationId++);
m_meshGenerator->setDefaultPartColor(dust3d::Color::createWhite());
m_meshGenerator->setInterpolationEnabled(true);
2020-10-18 14:34:43 +00:00
if (nullptr == m_generatedCacheContext)
m_generatedCacheContext = new MeshGenerator::GeneratedCacheContext;
m_meshGenerator->setGeneratedCacheContext((dust3d::MeshGenerator::GeneratedCacheContext *)m_generatedCacheContext);
if (!m_smoothNormal) {
m_meshGenerator->setSmoothShadingThresholdAngleDegrees(0);
}
m_meshGenerator->moveToThread(thread);
connect(thread, &QThread::started, m_meshGenerator, &MeshGenerator::process);
connect(m_meshGenerator, &MeshGenerator::finished, this, &Document::meshReady);
connect(m_meshGenerator, &MeshGenerator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
2018-04-08 01:30:55 +00:00
void Document::generateTexture()
{
if (nullptr != m_textureGenerator) {
m_isTextureObsolete = true;
return;
}
qDebug() << "Texture guide generating..";
emit textureGenerating();
m_isTextureObsolete = false;
dust3d::Snapshot *snapshot = new dust3d::Snapshot;
toSnapshot(snapshot);
QThread *thread = new QThread;
2020-11-17 14:31:51 +00:00
m_textureGenerator = new TextureGenerator(*m_postProcessedObject, snapshot);
m_textureGenerator->moveToThread(thread);
connect(thread, &QThread::started, m_textureGenerator, &TextureGenerator::process);
connect(m_textureGenerator, &TextureGenerator::finished, this, &Document::textureReady);
connect(m_textureGenerator, &TextureGenerator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void Document::textureReady()
{
2020-11-17 14:31:51 +00:00
updateTextureImage(m_textureGenerator->takeResultTextureColorImage());
updateTextureNormalImage(m_textureGenerator->takeResultTextureNormalImage());
updateTextureMetalnessImage(m_textureGenerator->takeResultTextureMetalnessImage());
updateTextureRoughnessImage(m_textureGenerator->takeResultTextureRoughnessImage());
updateTextureAmbientOcclusionImage(m_textureGenerator->takeResultTextureAmbientOcclusionImage());
delete m_resultTextureMesh;
m_resultTextureMesh = m_textureGenerator->takeResultMesh();
2020-11-18 10:26:46 +00:00
m_postProcessedObject->alphaEnabled = m_textureGenerator->hasTransparencySettings();
2020-11-17 14:31:51 +00:00
m_textureImageUpdateVersion++;
delete m_textureGenerator;
m_textureGenerator = nullptr;
qDebug() << "Texture guide generation done";
emit resultTextureChanged();
if (m_isTextureObsolete) {
generateTexture();
} else {
checkExportReadyState();
}
}
void Document::postProcess()
{
if (nullptr != m_postProcessor) {
m_isPostProcessResultObsolete = true;
return;
}
2020-12-19 05:37:44 +00:00
m_isPostProcessResultObsolete = false;
if (!m_currentObject) {
qDebug() << "Model is null";
return;
}
2020-12-19 05:37:44 +00:00
qDebug() << "Post processing..";
emit postProcessing();
QThread *thread = new QThread;
m_postProcessor = new MeshResultPostProcessor(*m_currentObject);
m_postProcessor->moveToThread(thread);
connect(thread, &QThread::started, m_postProcessor, &MeshResultPostProcessor::process);
connect(m_postProcessor, &MeshResultPostProcessor::finished, this, &Document::postProcessedMeshResultReady);
connect(m_postProcessor, &MeshResultPostProcessor::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
2020-12-19 05:37:44 +00:00
void Document::postProcessedMeshResultReady()
2018-09-07 10:13:55 +00:00
{
2020-12-19 05:37:44 +00:00
delete m_postProcessedObject;
m_postProcessedObject = m_postProcessor->takePostProcessedObject();
delete m_postProcessor;
m_postProcessor = nullptr;
qDebug() << "Post process done";
emit postProcessedResultChanged();
if (m_isPostProcessResultObsolete) {
postProcess();
2018-09-07 10:13:55 +00:00
}
}
const dust3d::Object &Document::currentPostProcessedObject() const
{
2020-12-19 05:37:44 +00:00
return *m_postProcessedObject;
}
void Document::setComponentCombineMode(dust3d::Uuid componentId, dust3d::CombineMode combineMode)
{
auto component = componentMap.find(componentId);
if (component == componentMap.end()) {
2020-12-19 05:37:44 +00:00
qDebug() << "SkeletonComponent not found:" << componentId;
return;
}
2020-12-19 05:37:44 +00:00
if (component->second.combineMode == combineMode)
return;
2020-12-19 05:37:44 +00:00
component->second.combineMode = combineMode;
component->second.dirty = true;
emit componentCombineModeChanged(componentId);
emit skeletonChanged();
}
void Document::setPartSubdivState(dust3d::Uuid partId, bool subdived)
2020-12-19 05:37:44 +00:00
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
2020-12-19 05:37:44 +00:00
if (part->second.subdived == subdived)
return;
part->second.subdived = subdived;
part->second.dirty = true;
emit partSubdivStateChanged(partId);
emit skeletonChanged();
}
void Document::resolveSnapshotBoundingBox(const dust3d::Snapshot &snapshot, QRectF *mainProfile, QRectF *sideProfile)
{
float left = 0;
bool leftFirstTime = true;
float right = 0;
bool rightFirstTime = true;
float top = 0;
bool topFirstTime = true;
float bottom = 0;
bool bottomFirstTime = true;
float zLeft = 0;
bool zLeftFirstTime = true;
float zRight = 0;
bool zRightFirstTime = true;
for (const auto &nodeIt: snapshot.nodes) {
float radius = dust3d::String::toFloat(dust3d::String::valueOrEmpty(nodeIt.second, "radius"));
float x = dust3d::String::toFloat(dust3d::String::valueOrEmpty(nodeIt.second, "x"));
float y = dust3d::String::toFloat(dust3d::String::valueOrEmpty(nodeIt.second, "y"));
float z = dust3d::String::toFloat(dust3d::String::valueOrEmpty(nodeIt.second, "z"));
if (leftFirstTime || x - radius < left) {
left = x - radius;
leftFirstTime = false;
}
if (topFirstTime || y - radius < top) {
top = y - radius;
topFirstTime = false;
}
if (rightFirstTime || x + radius > right) {
right = x + radius;
rightFirstTime = false;
}
if (bottomFirstTime || y + radius > bottom) {
bottom = y + radius;
bottomFirstTime = false;
}
if (zLeftFirstTime || z - radius < zLeft) {
zLeft = z - radius;
zLeftFirstTime = false;
}
if (zRightFirstTime || z + radius > zRight) {
zRight = z + radius;
zRightFirstTime = false;
}
}
*mainProfile = QRectF(QPointF(left, top), QPointF(right, bottom));
*sideProfile = QRectF(QPointF(zLeft, top), QPointF(zRight, bottom));
}
void Document::settleOrigin()
2018-04-15 12:11:51 +00:00
{
if (originSettled())
return;
dust3d::Snapshot snapshot;
2018-04-15 12:11:51 +00:00
toSnapshot(&snapshot);
QRectF mainProfile;
QRectF sideProfile;
resolveSnapshotBoundingBox(snapshot, &mainProfile, &sideProfile);
2019-10-19 13:14:36 +00:00
setOriginX(mainProfile.x() + mainProfile.width() / 2);
setOriginY(mainProfile.y() + mainProfile.height() / 2);
setOriginZ(sideProfile.x() + sideProfile.width() / 2);
markAllDirty();
2018-04-15 12:11:51 +00:00
emit originChanged();
}
void Document::setPartXmirrorState(dust3d::Uuid partId, bool mirrored)
2018-04-15 12:11:51 +00:00
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.xMirrored == mirrored)
return;
part->second.xMirrored = mirrored;
part->second.dirty = true;
2018-04-15 12:11:51 +00:00
settleOrigin();
emit partXmirrorStateChanged(partId);
emit skeletonChanged();
}
void Document::setPartDeformThickness(dust3d::Uuid partId, float thickness)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
2018-04-17 16:14:31 +00:00
part->second.setDeformThickness(thickness);
part->second.dirty = true;
2018-04-17 16:14:31 +00:00
emit partDeformThicknessChanged(partId);
emit skeletonChanged();
}
void Document::setPartBase(dust3d::Uuid partId, dust3d::PartBase base)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.base == base)
return;
part->second.base = base;
part->second.dirty = true;
emit partBaseChanged(partId);
emit skeletonChanged();
}
void Document::setPartDeformWidth(dust3d::Uuid partId, float width)
2018-04-17 16:14:31 +00:00
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
part->second.setDeformWidth(width);
part->second.dirty = true;
2018-04-17 16:14:31 +00:00
emit partDeformWidthChanged(partId);
emit skeletonChanged();
}
void Document::setPartDeformUnified(dust3d::Uuid partId, bool unified)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.deformUnified == unified)
return;
part->second.deformUnified = unified;
part->second.dirty = true;
emit partDeformUnifyStateChanged(partId);
emit skeletonChanged();
}
void Document::setPartMaterialId(dust3d::Uuid partId, dust3d::Uuid materialId)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.materialId == materialId)
return;
part->second.materialId = materialId;
part->second.dirty = true;
emit partMaterialIdChanged(partId);
emit textureChanged();
}
void Document::setPartRoundState(dust3d::Uuid partId, bool rounded)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.rounded == rounded)
return;
part->second.rounded = rounded;
part->second.dirty = true;
emit partRoundStateChanged(partId);
emit skeletonChanged();
}
void Document::setPartChamferState(dust3d::Uuid partId, bool chamfered)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.chamfered == chamfered)
return;
part->second.chamfered = chamfered;
part->second.dirty = true;
emit partChamferStateChanged(partId);
emit skeletonChanged();
}
void Document::setPartTarget(dust3d::Uuid partId, dust3d::PartTarget target)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.target == target)
return;
part->second.target = target;
part->second.dirty = true;
emit partTargetChanged(partId);
emit skeletonChanged();
}
void Document::setPartColorSolubility(dust3d::Uuid partId, float solubility)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (qFuzzyCompare(part->second.colorSolubility, solubility))
return;
part->second.colorSolubility = solubility;
part->second.dirty = true;
emit partColorSolubilityChanged(partId);
emit skeletonChanged();
}
void Document::setPartMetalness(dust3d::Uuid partId, float metalness)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (qFuzzyCompare(part->second.metalness, metalness))
return;
part->second.metalness = metalness;
part->second.dirty = true;
emit partMetalnessChanged(partId);
emit skeletonChanged();
}
void Document::setPartRoughness(dust3d::Uuid partId, float roughness)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (qFuzzyCompare(part->second.roughness, roughness))
return;
part->second.roughness = roughness;
part->second.dirty = true;
emit partRoughnessChanged(partId);
emit skeletonChanged();
}
void Document::setPartHollowThickness(dust3d::Uuid partId, float hollowThickness)
2019-08-18 12:02:39 +00:00
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (qFuzzyCompare(part->second.hollowThickness, hollowThickness))
return;
part->second.hollowThickness = hollowThickness;
part->second.dirty = true;
emit partHollowThicknessChanged(partId);
emit skeletonChanged();
}
void Document::setPartCountershaded(dust3d::Uuid partId, bool countershaded)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.countershaded == countershaded)
return;
part->second.countershaded = countershaded;
part->second.dirty = true;
emit partCountershadeStateChanged(partId);
emit textureChanged();
}
void Document::setPartSmoothState(dust3d::Uuid partId, bool smooth)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.smooth == smooth)
return;
part->second.smooth = smooth;
part->second.dirty = true;
emit partSmoothStateChanged(partId);
emit skeletonChanged();
}
void Document::setPartCutRotation(dust3d::Uuid partId, float cutRotation)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (qFuzzyCompare(cutRotation, part->second.cutRotation))
return;
part->second.setCutRotation(cutRotation);
part->second.dirty = true;
emit partCutRotationChanged(partId);
emit skeletonChanged();
}
void Document::setPartCutFace(dust3d::Uuid partId, dust3d::CutFace cutFace)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.cutFace == cutFace)
return;
part->second.setCutFace(cutFace);
part->second.dirty = true;
emit partCutFaceChanged(partId);
emit skeletonChanged();
}
void Document::setPartCutFaceLinkedId(dust3d::Uuid partId, dust3d::Uuid linkedId)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.cutFace == dust3d::CutFace::UserDefined &&
part->second.cutFaceLinkedId == linkedId)
return;
part->second.setCutFaceLinkedId(linkedId);
part->second.dirty = true;
emit partCutFaceChanged(partId);
emit skeletonChanged();
}
void Document::setPartColorState(dust3d::Uuid partId, bool hasColor, QColor color)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.hasColor == hasColor && part->second.color == color)
return;
part->second.hasColor = hasColor;
part->second.color = color;
part->second.dirty = true;
emit partColorStateChanged(partId);
emit skeletonChanged();
}
void Document::saveSnapshot()
2018-04-08 23:34:46 +00:00
{
HistoryItem item;
2018-04-08 23:34:46 +00:00
toSnapshot(&item.snapshot);
if (m_undoItems.size() + 1 > m_maxSnapshot)
m_undoItems.pop_front();
2018-04-08 23:34:46 +00:00
m_undoItems.push_back(item);
}
void Document::undo()
2018-04-08 23:34:46 +00:00
{
if (!undoable())
2018-04-08 23:34:46 +00:00
return;
m_redoItems.push_back(m_undoItems.back());
m_undoItems.pop_back();
const auto &item = m_undoItems.back();
fromSnapshot(item.snapshot);
2018-04-08 23:34:46 +00:00
qDebug() << "Undo/Redo items:" << m_undoItems.size() << m_redoItems.size();
}
void Document::redo()
2018-04-08 23:34:46 +00:00
{
if (m_redoItems.empty())
return;
m_undoItems.push_back(m_redoItems.back());
const auto &item = m_redoItems.back();
fromSnapshot(item.snapshot);
2018-04-08 23:34:46 +00:00
m_redoItems.pop_back();
qDebug() << "Undo/Redo items:" << m_undoItems.size() << m_redoItems.size();
}
void Document::clearHistories()
{
m_undoItems.clear();
m_redoItems.clear();
}
void Document::paste()
2018-04-09 08:46:06 +00:00
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasText()) {
dust3d::Snapshot snapshot;
std::string text = mimeData->text().toUtf8().constData();
loadSnapshotFromXmlString(&snapshot, (char *)text.c_str());
addFromSnapshot(snapshot, SnapshotSource::Paste);
saveSnapshot();
2018-04-09 08:46:06 +00:00
}
}
2018-04-09 14:24:30 +00:00
bool Document::hasPastableNodesInClipboard() const
2018-04-09 14:24:30 +00:00
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasText()) {
if (-1 != mimeData->text().indexOf("<node "))
return true;
}
return false;
}
bool Document::hasPastableMaterialsInClipboard() const
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasText()) {
if (-1 != mimeData->text().indexOf("<material "))
return true;
}
return false;
}
bool Document::undoable() const
2018-04-09 15:10:23 +00:00
{
return m_undoItems.size() >= 2;
2018-04-09 15:10:23 +00:00
}
bool Document::redoable() const
2018-04-09 15:10:23 +00:00
{
return !m_redoItems.empty();
}
2018-04-13 00:19:31 +00:00
bool Document::isNodeEditable(dust3d::Uuid nodeId) const
2018-04-13 00:19:31 +00:00
{
const SkeletonNode *node = findNode(nodeId);
2018-04-13 00:19:31 +00:00
if (!node) {
qDebug() << "Node id not found:" << nodeId;
return false;
}
return !isPartReadonly(node->partId);
}
bool Document::isEdgeEditable(dust3d::Uuid edgeId) const
2018-04-13 00:19:31 +00:00
{
const SkeletonEdge *edge = findEdge(edgeId);
2018-04-13 00:19:31 +00:00
if (!edge) {
qDebug() << "Edge id not found:" << edgeId;
return false;
}
return !isPartReadonly(edge->partId);
}
bool Document::isExportReady() const
{
2020-11-18 10:26:46 +00:00
if (m_meshGenerator ||
m_textureGenerator ||
m_postProcessor)
return false;
2020-11-18 11:01:11 +00:00
if (m_isResultMeshObsolete ||
m_isTextureObsolete ||
m_isPostProcessResultObsolete)
2020-11-18 11:01:11 +00:00
return false;
return true;
}
void Document::checkExportReadyState()
{
if (isExportReady())
emit exportReady();
}
void Document::addMaterial(dust3d::Uuid materialId, QString name, std::vector<MaterialLayer> layers)
{
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult != materialMap.end()) {
qDebug() << "Material already exist:" << materialId;
return;
}
dust3d::Uuid newMaterialId = materialId;
auto &material = materialMap[newMaterialId];
material.id = newMaterialId;
material.name = name;
material.layers = layers;
material.dirty = true;
materialIdList.push_back(newMaterialId);
emit materialAdded(newMaterialId);
emit materialListChanged();
emit optionsChanged();
}
void Document::removeMaterial(dust3d::Uuid materialId)
{
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult == materialMap.end()) {
qDebug() << "Remove a none exist material:" << materialId;
return;
}
materialIdList.erase(std::remove(materialIdList.begin(), materialIdList.end(), materialId), materialIdList.end());
materialMap.erase(findMaterialResult);
emit materialListChanged();
emit materialRemoved(materialId);
emit optionsChanged();
}
void Document::setMaterialLayers(dust3d::Uuid materialId, std::vector<MaterialLayer> layers)
{
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult == materialMap.end()) {
qDebug() << "Find material failed:" << materialId;
return;
}
findMaterialResult->second.layers = layers;
findMaterialResult->second.dirty = true;
emit materialLayersChanged(materialId);
emit textureChanged();
emit optionsChanged();
}
void Document::renameMaterial(dust3d::Uuid materialId, QString name)
{
auto findMaterialResult = materialMap.find(materialId);
if (findMaterialResult == materialMap.end()) {
qDebug() << "Find material failed:" << materialId;
return;
}
if (findMaterialResult->second.name == name)
return;
findMaterialResult->second.name = name;
emit materialNameChanged(materialId);
emit materialListChanged();
emit optionsChanged();
}
void Document::generateMaterialPreviews()
{
if (nullptr != m_materialPreviewsGenerator) {
return;
}
QThread *thread = new QThread;
m_materialPreviewsGenerator = new MaterialPreviewsGenerator();
bool hasDirtyMaterial = false;
for (auto &materialIt: materialMap) {
if (!materialIt.second.dirty)
continue;
m_materialPreviewsGenerator->addMaterial(materialIt.first, materialIt.second.layers);
materialIt.second.dirty = false;
hasDirtyMaterial = true;
}
if (!hasDirtyMaterial) {
delete m_materialPreviewsGenerator;
m_materialPreviewsGenerator = nullptr;
delete thread;
return;
}
qDebug() << "Material previews generating..";
m_materialPreviewsGenerator->moveToThread(thread);
connect(thread, &QThread::started, m_materialPreviewsGenerator, &MaterialPreviewsGenerator::process);
connect(m_materialPreviewsGenerator, &MaterialPreviewsGenerator::finished, this, &Document::materialPreviewsReady);
connect(m_materialPreviewsGenerator, &MaterialPreviewsGenerator::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void Document::materialPreviewsReady()
{
for (const auto &materialId: m_materialPreviewsGenerator->generatedPreviewMaterialIds()) {
auto material = materialMap.find(materialId);
if (material != materialMap.end()) {
2022-09-23 15:54:49 +00:00
ModelMesh *resultPartPreviewMesh = m_materialPreviewsGenerator->takePreview(materialId);
material->second.updatePreviewMesh(resultPartPreviewMesh);
emit materialPreviewChanged(materialId);
}
}
delete m_materialPreviewsGenerator;
m_materialPreviewsGenerator = nullptr;
qDebug() << "Material previews generation done";
generateMaterialPreviews();
}
bool Document::isMeshGenerating() const
2018-10-09 13:01:04 +00:00
{
return nullptr != m_meshGenerator;
}
bool Document::isPostProcessing() const
{
return nullptr != m_postProcessor;
}
bool Document::isTextureGenerating() const
{
return nullptr != m_textureGenerator;
}
void Document::copyNodes(std::set<dust3d::Uuid> nodeIdSet) const
{
dust3d::Snapshot snapshot;
toSnapshot(&snapshot, nodeIdSet, DocumentToSnapshotFor::Nodes);
std::string snapshotXml;
dust3d::saveSnapshotToXmlString(snapshot, snapshotXml);
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(snapshotXml.c_str());
}
void Document::collectCutFaceList(std::vector<QString> &cutFaces) const
{
cutFaces.clear();
std::vector<dust3d::Uuid> cutFacePartIdList;
std::set<dust3d::Uuid> cutFacePartIds;
for (const auto &it: partMap) {
if (dust3d::PartTarget::CutFace == it.second.target) {
if (cutFacePartIds.find(it.first) != cutFacePartIds.end())
continue;
cutFacePartIds.insert(it.first);
cutFacePartIdList.push_back(it.first);
}
if (!it.second.cutFaceLinkedId.isNull()) {
if (cutFacePartIds.find(it.second.cutFaceLinkedId) != cutFacePartIds.end())
continue;
cutFacePartIds.insert(it.second.cutFaceLinkedId);
cutFacePartIdList.push_back(it.second.cutFaceLinkedId);
}
}
// Sort cut face by center.x of front view
std::map<dust3d::Uuid, float> centerOffsetMap;
for (const auto &partId: cutFacePartIdList) {
const SkeletonPart *part = findPart(partId);
if (nullptr == part)
continue;
float offsetSum = 0;
for (const auto &nodeId: part->nodeIds) {
const SkeletonNode *node = findNode(nodeId);
if (nullptr == node)
continue;
offsetSum += node->getX();
}
if (qFuzzyIsNull(offsetSum))
continue;
centerOffsetMap[partId] = offsetSum / part->nodeIds.size();
}
std::sort(cutFacePartIdList.begin(), cutFacePartIdList.end(),
[&](const dust3d::Uuid &firstPartId, const dust3d::Uuid &secondPartId) {
return centerOffsetMap[firstPartId] < centerOffsetMap[secondPartId];
});
size_t cutFaceTypeCount = (size_t)dust3d::CutFace::UserDefined;
for (size_t i = 0; i < (size_t)cutFaceTypeCount; ++i) {
dust3d::CutFace cutFace = (dust3d::CutFace)i;
cutFaces.push_back(QString(dust3d::CutFaceToString(cutFace).c_str()));
}
for (const auto &it: cutFacePartIdList)
cutFaces.push_back(QString(it.toString().c_str()));
}