From 484f6aaabaab2c27bd8b7f729a83136930cb8013 Mon Sep 17 00:00:00 2001 From: huxingyi Date: Wed, 18 Nov 2020 00:01:51 +0930 Subject: [PATCH] Introduce texture painting --- dust3d.pro | 20 +- resources/model-dog.ds3 | 537 ++++++++++----------- shaders/default.core.frag | 7 +- shaders/default.frag | 3 +- src/autosaver.cpp | 22 + src/document.cpp | 311 ++++++++---- src/document.h | 41 +- src/documentsaver.cpp | 89 +++- src/documentsaver.h | 41 +- src/documentwindow.cpp | 363 ++++++++------ src/documentwindow.h | 15 +- src/exportpreviewwidget.cpp | 194 -------- src/exportpreviewwidget.h | 43 -- src/fbxfile.cpp | 10 +- src/fbxfile.h | 4 +- src/glbfile.cpp | 10 +- src/glbfile.h | 4 +- src/gridmeshbuilder.cpp | 448 ----------------- src/gridmeshbuilder.h | 62 --- src/libdust3d.cpp | 38 +- src/materialpreviewsgenerator.cpp | 16 +- src/meshgenerator.cpp | 311 ++++++------ src/meshgenerator.h | 25 +- src/meshresultpostprocessor.cpp | 28 +- src/meshresultpostprocessor.h | 8 +- src/model.cpp | 32 +- src/model.h | 4 +- src/modelmeshbinder.cpp | 17 + src/modelmeshbinder.h | 3 + src/modelwidget.cpp | 8 +- src/modelwidget.h | 1 + src/motioneditwidget.cpp | 16 +- src/motioneditwidget.h | 6 +- src/motionmanagewidget.cpp | 2 +- src/motionsgenerator.cpp | 18 +- src/motionsgenerator.h | 4 +- src/{outcome.cpp => object.cpp} | 4 +- src/{outcome.h => object.h} | 44 +- src/objectxml.cpp | 420 ++++++++++++++++ src/objectxml.h | 9 + src/preferences.cpp | 23 + src/preferences.h | 4 + src/preferenceswidget.cpp | 8 + src/riggenerator.cpp | 172 ++++--- src/riggenerator.h | 8 +- src/shortcuts.cpp | 2 +- src/simulateclothmeshes.cpp | 2 +- src/simulateclothmeshes.h | 2 +- src/skeletongraphicswidget.cpp | 2 +- src/skinnedmeshcreator.cpp | 30 +- src/skinnedmeshcreator.h | 6 +- src/texturegenerator.cpp | 223 +++------ src/texturegenerator.h | 19 +- src/texturepainter.cpp | 206 ++++++++ src/texturepainter.h | 73 +++ src/trianglesourcenoderesolve.cpp | 38 +- src/trianglesourcenoderesolve.h | 6 +- src/triangletangentresolve.cpp | 18 +- src/triangletangentresolve.h | 4 +- src/util.cpp | 16 +- src/util.h | 5 +- src/uvunwrap.cpp | 12 +- src/uvunwrap.h | 4 +- src/vertexcolorpainter.cpp | 227 --------- src/vertexcolorpainter.h | 81 ---- src/voxelgrid.cpp | 1 - src/voxelgrid.h | 85 ---- thirdparty/simpleuv/simpleuv/chartpacker.h | 2 +- 68 files changed, 2186 insertions(+), 2331 deletions(-) delete mode 100644 src/exportpreviewwidget.cpp delete mode 100644 src/exportpreviewwidget.h delete mode 100644 src/gridmeshbuilder.cpp delete mode 100644 src/gridmeshbuilder.h rename src/{outcome.cpp => object.cpp} (94%) rename src/{outcome.h => object.h} (82%) create mode 100644 src/objectxml.cpp create mode 100644 src/objectxml.h create mode 100644 src/texturepainter.cpp create mode 100644 src/texturepainter.h delete mode 100644 src/vertexcolorpainter.cpp delete mode 100644 src/vertexcolorpainter.h delete mode 100644 src/voxelgrid.cpp delete mode 100644 src/voxelgrid.h diff --git a/dust3d.pro b/dust3d.pro index 0644e00a..fdf3f9b2 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -215,8 +215,8 @@ HEADERS += src/model.h SOURCES += src/texturegenerator.cpp HEADERS += src/texturegenerator.h -SOURCES += src/outcome.cpp -HEADERS += src/outcome.h +SOURCES += src/object.cpp +HEADERS += src/object.h SOURCES += src/meshresultpostprocessor.cpp HEADERS += src/meshresultpostprocessor.h @@ -230,9 +230,6 @@ HEADERS += src/logbrowserdialog.h SOURCES += src/floatnumberwidget.cpp HEADERS += src/floatnumberwidget.h -SOURCES += src/exportpreviewwidget.cpp -HEADERS += src/exportpreviewwidget.h - SOURCES += src/ccdikresolver.cpp HEADERS += src/ccdikresolver.h @@ -397,11 +394,8 @@ HEADERS += src/intnumberwidget.h SOURCES += src/imagepreviewwidget.cpp HEADERS += src/imagepreviewwidget.h -SOURCES += src/vertexcolorpainter.cpp -HEADERS += src/vertexcolorpainter.h - -SOURCES += src/voxelgrid.cpp -HEADERS += src/voxelgrid.h +SOURCES += src/texturepainter.cpp +HEADERS += src/texturepainter.h SOURCES += src/paintmode.cpp HEADERS += src/paintmode.h @@ -412,9 +406,6 @@ HEADERS += src/proceduralanimation.h SOURCES += src/boundingboxmesh.cpp HEADERS += src/boundingboxmesh.h -SOURCES += src/gridmeshbuilder.cpp -HEADERS += src/gridmeshbuilder.h - SOURCES += src/regionfiller.cpp HEADERS += src/regionfiller.h @@ -532,6 +523,9 @@ HEADERS += src/motioneditwidget.h SOURCES += src/vertebratamotionparameterswidget.cpp HEADERS += src/vertebratamotionparameterswidget.h +SOURCES += src/objectxml.cpp +HEADERS += src/objectxml.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/resources/model-dog.ds3 b/resources/model-dog.ds3 index 016d33ba..cbc62b27 100644 --- a/resources/model-dog.ds3 +++ b/resources/model-dog.ds3 @@ -1,291 +1,290 @@ DUST3D 1.0 xml 0000000194 - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - ‰PNG diff --git a/shaders/default.core.frag b/shaders/default.core.frag index 5014ea7b..97a46197 100644 --- a/shaders/default.core.frag +++ b/shaders/default.core.frag @@ -548,7 +548,7 @@ vec4 metalRoughFunction(const in vec4 baseColor, } // Apply exposure correction - cLinear *= pow(2.0, exposure); + //cLinear *= pow(2.0, exposure); // Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1] vec3 cToneMapped = toneMap(cLinear); @@ -573,7 +573,7 @@ void main() lights[0].type = TYPE_POINT; lights[0].position = firstLightPos; lights[0].color = vec3(1.0, 1.0, 1.0); - lights[0].intensity = 1.0; + lights[0].intensity = 3.0; lights[0].constantAttenuation = 1.0; lights[0].linearAttenuation = 0.0; lights[0].quadraticAttenuation = 0.0025; @@ -604,7 +604,8 @@ void main() alpha = textColor.a; } if (mousePickEnabled == 1) { - if (distance(mousePickTargetPosition, vertRaw) <= mousePickRadius) { + float dist = distance(mousePickTargetPosition, vertRaw); + if (dist <= mousePickRadius && dist >= mousePickRadius * 0.9) { color = color + vec3(0.99, 0.4, 0.13); } } diff --git a/shaders/default.frag b/shaders/default.frag index fbf03173..ddaebfbd 100644 --- a/shaders/default.frag +++ b/shaders/default.frag @@ -492,7 +492,8 @@ void main() alpha = textColor.a; } if (mousePickEnabled == 1) { - if (distance(mousePickTargetPosition, vertRaw) <= mousePickRadius) { + float dist = distance(mousePickTargetPosition, vertRaw); + if (dist <= mousePickRadius && dist >= mousePickRadius * 0.9) { color = color + vec3(0.99, 0.4, 0.13); } } diff --git a/src/autosaver.cpp b/src/autosaver.cpp index f0f06071..4bc8f205 100644 --- a/src/autosaver.cpp +++ b/src/autosaver.cpp @@ -85,6 +85,8 @@ void AutoSaver::check() Snapshot *snapshot = new Snapshot; m_document->toSnapshot(snapshot); + Object *object = new Object(m_document->currentPostProcessedObject()); + QByteArray *turnaroundPngByteArray = nullptr; if (!m_document->turnaround.isNull() && m_document->turnaroundPngByteArray.size() > 0) { turnaroundPngByteArray = new QByteArray(m_document->turnaroundPngByteArray); @@ -101,9 +103,29 @@ void AutoSaver::check() scriptVariables = new std::map>(variables); } + DocumentSaver::Textures *textures = new DocumentSaver::Textures; + if (nullptr != m_document->textureImage) { + textures->textureImage = new QImage(*m_document->textureImage); + } + if (nullptr != m_document->textureNormalImage) { + textures->textureNormalImage = new QImage(*m_document->textureNormalImage); + } + if (nullptr != m_document->textureMetalnessImage) { + textures->textureMetalnessImage = new QImage(*m_document->textureMetalnessImage); + } + if (nullptr != m_document->textureRoughnessImage) { + textures->textureRoughnessImage = new QImage(*m_document->textureRoughnessImage); + } + if (nullptr != m_document->textureAmbientOcclusionImage) { + textures->textureAmbientOcclusionImage = new QImage(*m_document->textureAmbientOcclusionImage); + } + textures->textureHasTransparencySettings = m_document->textureHasTransparencySettings; + QThread *thread = new QThread; m_documentSaver = new DocumentSaver(&m_filename, snapshot, + object, + textures, turnaroundPngByteArray, script, scriptVariables); diff --git a/src/document.cpp b/src/document.cpp index 6a4963f9..522954b1 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -28,20 +28,22 @@ const size_t Component::defaultClothIteration = 350; Document::Document() : SkeletonDocument(), // public - textureGuideImage(nullptr), textureImage(nullptr), - textureBorderImage(nullptr), - textureColorImage(nullptr), + textureImageByteArray(nullptr), textureNormalImage(nullptr), - textureMetalnessRoughnessAmbientOcclusionImage(nullptr), + textureNormalImageByteArray(nullptr), textureMetalnessImage(nullptr), + textureMetalnessImageByteArray(nullptr), textureRoughnessImage(nullptr), + textureRoughnessImageByteArray(nullptr), textureAmbientOcclusionImage(nullptr), + textureAmbientOcclusionImageByteArray(nullptr), textureHasTransparencySettings(false), rigType(RigType::None), weldEnabled(true), polyCount(PolyCount::Original), brushColor(Qt::white), + objectLocked(false), // private m_isResultMeshObsolete(false), m_meshGenerator(nullptr), @@ -51,12 +53,12 @@ Document::Document() : m_resultMeshNodesCutFaces(nullptr), m_isMeshGenerationSucceed(true), m_batchChangeRefCount(0), - m_currentOutcome(nullptr), + m_currentObject(nullptr), m_isTextureObsolete(false), m_textureGenerator(nullptr), m_isPostProcessResultObsolete(false), m_postProcessor(nullptr), - m_postProcessedOutcome(new Outcome), + m_postProcessedObject(new Object), m_resultTextureMesh(nullptr), m_textureImageUpdateVersion(0), m_allPositionRelatedLocksEnabled(true), @@ -66,7 +68,7 @@ Document::Document() : m_resultRigBones(nullptr), m_resultRigWeights(nullptr), m_isRigObsolete(false), - m_riggedOutcome(new Outcome), + m_riggedObject(new Object), m_currentRigSucceed(false), m_materialPreviewsGenerator(nullptr), m_motionsGenerator(nullptr), @@ -74,13 +76,13 @@ Document::Document() : m_nextMeshGenerationId(1), m_scriptRunner(nullptr), m_isScriptResultObsolete(false), - m_vertexColorPainter(nullptr), + m_texturePainter(nullptr), m_isMouseTargetResultObsolete(false), m_paintMode(PaintMode::None), - m_mousePickRadius(0.05), + m_mousePickRadius(0.02), m_saveNextPaintSnapshot(false), - m_vertexColorVoxelGrid(nullptr), - m_generatedCacheContext(nullptr) + m_generatedCacheContext(nullptr), + m_texturePainterContext(nullptr) { connect(&Preferences::instance(), &Preferences::partColorChanged, this, &Document::applyPreferencePartColorChange); connect(&Preferences::instance(), &Preferences::flatShadingChanged, this, &Document::applyPreferenceFlatShadingChange); @@ -109,16 +111,17 @@ Document::~Document() delete m_paintedMesh; //delete m_resultMeshCutFaceTransforms; delete m_resultMeshNodesCutFaces; - delete m_postProcessedOutcome; - delete textureGuideImage; + delete m_postProcessedObject; delete textureImage; - delete textureColorImage; + delete textureImageByteArray; delete textureNormalImage; - delete textureMetalnessRoughnessAmbientOcclusionImage; + delete textureNormalImageByteArray; delete textureMetalnessImage; + delete textureMetalnessImageByteArray; delete textureRoughnessImage; + delete textureRoughnessImageByteArray; delete textureAmbientOcclusionImage; - delete textureBorderImage; + delete textureAmbientOcclusionImageByteArray; delete m_resultTextureMesh; delete m_resultRigWeightMesh; } @@ -925,17 +928,86 @@ void Document::updateTurnaround(const QImage &image) emit turnaroundChanged(); } +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; + if (SkeletonDocumentEditMode::Paint == mode && !objectLocked) + return; + editMode = mode; if (editMode != SkeletonDocumentEditMode::Paint) m_paintMode = PaintMode::None; emit editModeChanged(); } +void Document::setMeshLockState(bool locked) +{ + if (objectLocked == locked) + return; + + objectLocked = locked; + if (locked) { + if (SkeletonDocumentEditMode::Paint != editMode) { + editMode = SkeletonDocumentEditMode::Paint; + emit editModeChanged(); + } + } else { + if (SkeletonDocumentEditMode::Paint == editMode) { + editMode = SkeletonDocumentEditMode::Select; + emit editModeChanged(); + } + } + emit objectLockStateChanged(); + emit textureChanged(); +} + void Document::setPaintMode(PaintMode mode) { if (m_paintMode == mode) @@ -944,7 +1016,7 @@ void Document::setPaintMode(PaintMode mode) m_paintMode = mode; emit paintModeChanged(); - paintVertexColors(); + paint(); } void Document::joinNodeAndNeiborsToGroup(std::vector *group, QUuid nodeId, std::set *visitMap, QUuid noUseEdgeId) @@ -1085,7 +1157,7 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId if (partIt.second.colorSolubilityAdjusted()) part["colorSolubility"] = QString::number(partIt.second.colorSolubility); if (partIt.second.metalnessAdjusted()) - part["metalness"] = QString::number(partIt.second.metalness); + part["metallic"] = QString::number(partIt.second.metalness); if (partIt.second.roughnessAdjusted()) part["roughness"] = QString::number(partIt.second.roughness); if (partIt.second.deformThicknessAdjusted()) @@ -1260,6 +1332,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId canvas["rigType"] = RigTypeToString(rigType); if (this->polyCount != PolyCount::Original) canvas["polyCount"] = PolyCountToString(this->polyCount); + if (this->objectLocked) + canvas["objectLocked"] = "true"; snapshot->canvas = canvas; } } @@ -1349,10 +1423,17 @@ void Document::createSinglePartFromEdges(const std::vector &nodes, emit skeletonChanged(); } +void Document::updateObject(Object *object) +{ + delete m_postProcessedObject; + m_postProcessedObject = object; +} + void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource source) { bool isOriginChanged = false; bool isRigTypeChanged = false; + bool isMeshLockedChanged = false; if (SnapshotSource::Paste != source && SnapshotSource::Import != source) { this->polyCount = PolyCountFromString(valueOfKeyInMapOrEmpty(snapshot.canvas, "polyCount").toUtf8().constData()); @@ -1371,6 +1452,11 @@ void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource sou if (rigTypeIt != snapshot.canvas.end()) { rigType = RigTypeFromString(rigTypeIt->second.toUtf8().constData()); } + bool setMeshLocked = isTrueValueString(valueOfKeyInMapOrEmpty(snapshot.canvas, "objectLocked")); + if (this->objectLocked != setMeshLocked) { + this->objectLocked = setMeshLocked; + isMeshLockedChanged = true; + } isRigTypeChanged = true; } @@ -1476,7 +1562,7 @@ void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource sou const auto &colorSolubilityIt = partKv.second.find("colorSolubility"); if (colorSolubilityIt != partKv.second.end()) part.colorSolubility = colorSolubilityIt->second.toFloat(); - const auto &metalnessIt = partKv.second.find("metalness"); + const auto &metalnessIt = partKv.second.find("metallic"); if (metalnessIt != partKv.second.end()) part.metalness = metalnessIt->second.toFloat(); const auto &roughnessIt = partKv.second.find("roughness"); @@ -1705,6 +1791,9 @@ void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource sou emit materialListChanged(); if (!snapshot.motions.empty()) emit motionListChanged(); + + if (isMeshLockedChanged) + emit objectLockStateChanged(); } void Document::silentReset() @@ -1797,17 +1886,21 @@ Model *Document::takeResultRigWeightMesh() void Document::meshReady() { Model *resultMesh = m_meshGenerator->takeResultMesh(); - Outcome *outcome = m_meshGenerator->takeOutcome(); + Object *object = m_meshGenerator->takeObject(); bool isSuccessful = m_meshGenerator->isSuccessful(); + bool partPreviewsChanged = false; for (auto &partId: m_meshGenerator->generatedPreviewPartIds()) { auto part = partMap.find(partId); if (part != partMap.end()) { Model *resultPartPreviewMesh = m_meshGenerator->takePartPreviewMesh(partId); part->second.updatePreviewMesh(resultPartPreviewMesh); + partPreviewsChanged = true; //emit partPreviewChanged(partId); } } + if (partPreviewsChanged) + emit resultPartPreviewsChanged(); delete m_resultMesh; m_resultMesh = resultMesh; @@ -1822,8 +1915,8 @@ void Document::meshReady() m_isMeshGenerationSucceed = isSuccessful; - delete m_currentOutcome; - m_currentOutcome = outcome; + delete m_currentObject; + m_currentObject = object; if (nullptr == m_resultMesh) { qDebug() << "Result mesh is null"; @@ -1836,11 +1929,35 @@ void Document::meshReady() m_isPostProcessResultObsolete = true; m_isRigObsolete = true; - emit resultMeshChanged(); if (m_isResultMeshObsolete) { generateMesh(); + } else { + if (objectLocked) { + emit postProcessedResultChanged(); + + if (nullptr != m_postProcessedObject) { + Model *model = new Model(*m_postProcessedObject); + if (nullptr != textureImage) + model->setTextureImage(new QImage(*textureImage)); + if (nullptr != textureNormalImage) + model->setNormalMapImage(new QImage(*textureNormalImage)); + if (nullptr != textureMetalnessImage || nullptr != textureRoughnessImage || nullptr != textureAmbientOcclusionImage) { + model->setMetalnessRoughnessAmbientOcclusionImage(TextureGenerator::combineMetalnessRoughnessAmbientOcclusionImages( + textureMetalnessImage, + textureRoughnessImage, + textureAmbientOcclusionImage)); + model->setHasMetalnessInImage(nullptr != textureMetalnessImage); + model->setHasRoughnessInImage(nullptr != textureRoughnessImage); + model->setHasAmbientOcclusionInImage(nullptr != textureAmbientOcclusionImage); + } + model->setMeshId(m_nextMeshGenerationId++); + delete m_resultTextureMesh; + m_resultTextureMesh = model; + emit resultTextureChanged(); + } + } } } @@ -1896,6 +2013,9 @@ void Document::batchChangeEnd() void Document::regenerateMesh() { + if (objectLocked) + return; + markAllDirty(); generateMesh(); } @@ -1951,6 +2071,9 @@ void Document::generateMesh() void Document::generateTexture() { + if (objectLocked) + return; + if (nullptr != m_textureGenerator) { m_isTextureObsolete = true; return; @@ -1965,7 +2088,7 @@ void Document::generateTexture() toSnapshot(snapshot); QThread *thread = new QThread; - m_textureGenerator = new TextureGenerator(*m_postProcessedOutcome, snapshot); + 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); @@ -1976,40 +2099,17 @@ void Document::generateTexture() void Document::textureReady() { - delete textureGuideImage; - textureGuideImage = m_textureGenerator->takeResultTextureGuideImage(); - - delete textureImage; - textureImage = m_textureGenerator->takeResultTextureImage(); - - delete textureBorderImage; - textureBorderImage = m_textureGenerator->takeResultTextureBorderImage(); - - delete textureColorImage; - textureColorImage = m_textureGenerator->takeResultTextureColorImage(); - - delete textureNormalImage; - textureNormalImage = m_textureGenerator->takeResultTextureNormalImage(); - - delete textureMetalnessRoughnessAmbientOcclusionImage; - textureMetalnessRoughnessAmbientOcclusionImage = m_textureGenerator->takeResultTextureMetalnessRoughnessAmbientOcclusionImage(); - - delete textureMetalnessImage; - textureMetalnessImage = m_textureGenerator->takeResultTextureMetalnessImage(); - - delete textureRoughnessImage; - textureRoughnessImage = m_textureGenerator->takeResultTextureRoughnessImage(); - - delete textureAmbientOcclusionImage; - textureAmbientOcclusionImage = m_textureGenerator->takeResultTextureAmbientOcclusionImage(); + 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(); textureHasTransparencySettings = m_textureGenerator->hasTransparencySettings(); - - //addToolToMesh(m_resultTextureMesh); - + m_textureImageUpdateVersion++; delete m_textureGenerator; @@ -2028,6 +2128,11 @@ void Document::textureReady() void Document::postProcess() { + if (objectLocked) { + m_isPostProcessResultObsolete = true; + return; + } + if (nullptr != m_postProcessor) { m_isPostProcessResultObsolete = true; return; @@ -2035,7 +2140,7 @@ void Document::postProcess() m_isPostProcessResultObsolete = false; - if (!m_currentOutcome) { + if (!m_currentObject) { qDebug() << "Model is null"; return; } @@ -2044,7 +2149,7 @@ void Document::postProcess() emit postProcessing(); QThread *thread = new QThread; - m_postProcessor = new MeshResultPostProcessor(*m_currentOutcome); + 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); @@ -2055,8 +2160,8 @@ void Document::postProcess() void Document::postProcessedMeshResultReady() { - delete m_postProcessedOutcome; - m_postProcessedOutcome = m_postProcessor->takePostProcessedOutcome(); + delete m_postProcessedObject; + m_postProcessedObject = m_postProcessor->takePostProcessedObject(); delete m_postProcessor; m_postProcessor = nullptr; @@ -2075,61 +2180,67 @@ void Document::pickMouseTarget(const QVector3D &nearPosition, const QVector3D &f m_mouseRayNear = nearPosition; m_mouseRayFar = farPosition; - paintVertexColors(); + paint(); } -void Document::paintVertexColors() +void Document::paint() { - if (nullptr != m_vertexColorPainter) { + if (nullptr != m_texturePainter) { m_isMouseTargetResultObsolete = true; return; } m_isMouseTargetResultObsolete = false; - if (!m_currentOutcome) { + if (!m_postProcessedObject) { qDebug() << "Model is null"; return; } + if (nullptr == textureImage) + return; + //qDebug() << "Mouse picking.."; QThread *thread = new QThread; - m_vertexColorPainter = new VertexColorPainter(*m_currentOutcome, m_mouseRayNear, m_mouseRayFar); - m_vertexColorPainter->setBrushColor(brushColor); - m_vertexColorPainter->setBrushMetalness(brushMetalness); - m_vertexColorPainter->setBrushRoughness(brushRoughness); - if (SkeletonDocumentEditMode::Paint == editMode) { - if (nullptr == m_vertexColorVoxelGrid) { - m_vertexColorVoxelGrid = new VoxelGrid(); - } - m_vertexColorPainter->setVoxelGrid(m_vertexColorVoxelGrid); - m_vertexColorPainter->setPaintMode(m_paintMode); - m_vertexColorPainter->setRadius(m_mousePickRadius); - m_vertexColorPainter->setMaskNodeIds(m_mousePickMaskNodeIds); + m_texturePainter = new TexturePainter(m_mouseRayNear, m_mouseRayFar); + if (nullptr == m_texturePainterContext) { + m_texturePainterContext = new TexturePainterContext; + m_texturePainterContext->object = new Object(*m_postProcessedObject); + m_texturePainterContext->colorImage = new QImage(*textureImage); + } else if (m_texturePainterContext->object->meshId != m_postProcessedObject->meshId) { + delete m_texturePainterContext->object; + m_texturePainterContext->object = new Object(*m_postProcessedObject); + delete m_texturePainterContext->colorImage; + m_texturePainterContext->colorImage = new QImage(*textureImage); } - - m_vertexColorPainter->moveToThread(thread); - connect(thread, &QThread::started, m_vertexColorPainter, &VertexColorPainter::process); - connect(m_vertexColorPainter, &VertexColorPainter::finished, this, &Document::vertexColorsReady); - connect(m_vertexColorPainter, &VertexColorPainter::finished, thread, &QThread::quit); + m_texturePainter->setContext(m_texturePainterContext); + m_texturePainter->setBrushColor(brushColor); + if (SkeletonDocumentEditMode::Paint == editMode) { + m_texturePainter->setPaintMode(m_paintMode); + m_texturePainter->setRadius(m_mousePickRadius); + m_texturePainter->setMaskNodeIds(m_mousePickMaskNodeIds); + } + m_texturePainter->moveToThread(thread); + connect(thread, &QThread::started, m_texturePainter, &TexturePainter::process); + connect(m_texturePainter, &TexturePainter::finished, this, &Document::paintReady); + connect(m_texturePainter, &TexturePainter::finished, thread, &QThread::quit); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } -void Document::vertexColorsReady() +void Document::paintReady() { - m_mouseTargetPosition = m_vertexColorPainter->targetPosition(); + m_mouseTargetPosition = m_texturePainter->targetPosition(); - Model *model = m_vertexColorPainter->takePaintedModel(); - if (nullptr != model) { - delete m_paintedMesh; - m_paintedMesh = model; - emit paintedMeshChanged(); + QImage *paintedTextureImage = m_texturePainter->takeColorImage(); + if (nullptr != paintedTextureImage) { + updateTextureImage(paintedTextureImage); + emit resultColorTextureChanged(); } - delete m_vertexColorPainter; - m_vertexColorPainter = nullptr; + delete m_texturePainter; + m_texturePainter = nullptr; if (!m_isMouseTargetResultObsolete && m_saveNextPaintSnapshot) { m_saveNextPaintSnapshot = false; @@ -2138,8 +2249,6 @@ void Document::vertexColorsReady() emit mouseTargetChanged(); - //qDebug() << "Mouse pick done"; - if (m_isMouseTargetResultObsolete) { pickMouseTarget(m_mouseRayNear, m_mouseRayFar); } @@ -2161,9 +2270,9 @@ void Document::setMousePickRadius(float radius) emit mousePickRadiusChanged(); } -const Outcome &Document::currentPostProcessedOutcome() const +const Object &Document::currentPostProcessedObject() const { - return *m_postProcessedOutcome; + return *m_postProcessedObject; } void Document::setPartLockState(QUuid partId, bool locked) @@ -3418,7 +3527,7 @@ void Document::generateRig() m_isRigObsolete = false; - if (RigType::None == rigType || nullptr == m_currentOutcome) { + if (RigType::None == rigType || nullptr == m_currentObject) { removeRigResults(); return; } @@ -3426,7 +3535,7 @@ void Document::generateRig() qDebug() << "Rig generating.."; QThread *thread = new QThread; - m_rigGenerator = new RigGenerator(rigType, *m_postProcessedOutcome); + m_rigGenerator = new RigGenerator(rigType, *m_postProcessedObject); m_rigGenerator->moveToThread(thread); connect(thread, &QThread::started, m_rigGenerator, &RigGenerator::process); connect(m_rigGenerator, &RigGenerator::finished, this, &Document::rigReady); @@ -3450,10 +3559,10 @@ void Document::rigReady() m_resultRigMessages = m_rigGenerator->messages(); - delete m_riggedOutcome; - m_riggedOutcome = m_rigGenerator->takeOutcome(); - if (nullptr == m_riggedOutcome) - m_riggedOutcome = new Outcome; + delete m_riggedObject; + m_riggedObject = m_rigGenerator->takeObject(); + if (nullptr == m_riggedObject) + m_riggedObject = new Object; delete m_rigGenerator; m_rigGenerator = nullptr; @@ -3517,9 +3626,9 @@ const std::vector> &Document::resultRigMessages() return m_resultRigMessages; } -const Outcome &Document::currentRiggedOutcome() const +const Object &Document::currentRiggedObject() const { - return *m_riggedOutcome; + return *m_riggedObject; } bool Document::currentRigSucceed() const @@ -3540,7 +3649,7 @@ void Document::generateMotions() return; } - m_motionsGenerator = new MotionsGenerator(rigType, *rigBones, *rigWeights, currentRiggedOutcome()); + m_motionsGenerator = new MotionsGenerator(rigType, *rigBones, *rigWeights, currentRiggedObject()); m_motionsGenerator->enableSnapshotMeshes(); bool hasDirtyMotion = false; for (auto &motion: motionMap) { @@ -3929,7 +4038,7 @@ void Document::startPaint() void Document::stopPaint() { - if (m_vertexColorPainter || m_isMouseTargetResultObsolete) { + if (m_texturePainter || m_isMouseTargetResultObsolete) { m_saveNextPaintSnapshot = true; return; } diff --git a/src/document.h b/src/document.h index 73c9166c..09e9bb78 100644 --- a/src/document.h +++ b/src/document.h @@ -28,8 +28,7 @@ #include "proceduralanimation.h" #include "componentlayer.h" #include "clothforce.h" -#include "voxelgrid.h" -#include "vertexcolorpainter.h" +#include "texturepainter.h" class MaterialPreviewsGenerator; class MotionsGenerator; @@ -330,6 +329,7 @@ signals: void edgeReversed(QUuid edgeId); void partPreviewChanged(QUuid partId); void resultMeshChanged(); + void resultPartPreviewsChanged(); void paintedMeshChanged(); void turnaroundChanged(); void editModeChanged(); @@ -337,6 +337,7 @@ signals: void skeletonChanged(); //void resultSkeletonChanged(); void resultTextureChanged(); + void resultColorTextureChanged(); //void resultBakedTextureChanged(); void postProcessedResultChanged(); void resultRigChanged(); @@ -412,21 +413,24 @@ signals: void scriptConsoleLogChanged(); void mouseTargetChanged(); void mousePickRadiusChanged(); + void objectLockStateChanged(); public: // need initialize - QImage *textureGuideImage; QImage *textureImage; - QImage *textureBorderImage; - QImage *textureColorImage; + QByteArray *textureImageByteArray; QImage *textureNormalImage; - QImage *textureMetalnessRoughnessAmbientOcclusionImage; + QByteArray *textureNormalImageByteArray; QImage *textureMetalnessImage; + QByteArray *textureMetalnessImageByteArray; QImage *textureRoughnessImage; + QByteArray *textureRoughnessImageByteArray; QImage *textureAmbientOcclusionImage; + QByteArray *textureAmbientOcclusionImageByteArray; bool textureHasTransparencySettings; RigType rigType; bool weldEnabled; PolyCount polyCount; QColor brushColor; + bool objectLocked; float brushMetalness = Model::m_defaultMetalness; float brushRoughness = Model::m_defaultRoughness; public: @@ -470,15 +474,20 @@ public: const std::vector *resultRigBones() const; const std::map *resultRigWeights() const; void updateTurnaround(const QImage &image); + void updateTextureImage(QImage *image); + void updateTextureNormalImage(QImage *image); + void updateTextureMetalnessImage(QImage *image); + void updateTextureRoughnessImage(QImage *image); + void updateTextureAmbientOcclusionImage(QImage *image); bool hasPastableMaterialsInClipboard() const; bool hasPastableMotionsInClipboard() const; - const Outcome ¤tPostProcessedOutcome() const; + const Object ¤tPostProcessedObject() const; bool isExportReady() const; bool isPostProcessResultObsolete() const; void collectComponentDescendantParts(QUuid componentId, std::vector &partIds) const; void collectComponentDescendantComponents(QUuid componentId, std::vector &componentIds) const; const std::vector> &resultRigMessages() const; - const Outcome ¤tRiggedOutcome() const; + const Object ¤tRiggedObject() const; bool currentRigSucceed() const; bool isMeshGenerating() const; bool isPostProcessing() const; @@ -512,6 +521,7 @@ public slots: void moveOriginBy(float x, float y, float z); void addEdge(QUuid fromNodeId, QUuid toNodeId); void setEditMode(SkeletonDocumentEditMode mode); + void setMeshLockState(bool locked); void setPaintMode(PaintMode mode); void setMousePickRadius(float radius); void createSinglePartFromEdges(const std::vector &nodes, @@ -531,8 +541,8 @@ public slots: void generateMotions(); void motionsReady(); void pickMouseTarget(const QVector3D &nearPosition, const QVector3D &farPosition); - void paintVertexColors(); - void vertexColorsReady(); + void paint(); + void paintReady(); void setPartLockState(QUuid partId, bool locked); void setPartVisibleState(QUuid partId, bool visible); void setPartSubdivState(QUuid partId, bool subdived); @@ -633,6 +643,7 @@ public slots: void startPaint(); void stopPaint(); void setMousePickMaskNodeIds(const std::set &nodeIds); + void updateObject(Object *object); private: void splitPartByNode(std::vector> *groups, QUuid nodeId); void joinNodeAndNeiborsToGroup(std::vector *group, QUuid nodeId, std::set *visitMap, QUuid noUseEdgeId=QUuid()); @@ -661,12 +672,12 @@ private: // need initialize std::map> *m_resultMeshNodesCutFaces; bool m_isMeshGenerationSucceed; int m_batchChangeRefCount; - Outcome *m_currentOutcome; + Object *m_currentObject; bool m_isTextureObsolete; TextureGenerator *m_textureGenerator; bool m_isPostProcessResultObsolete; MeshResultPostProcessor *m_postProcessor; - Outcome *m_postProcessedOutcome; + Object *m_postProcessedObject; Model *m_resultTextureMesh; unsigned long long m_textureImageUpdateVersion; QUuid m_currentCanvasComponentId; @@ -677,7 +688,7 @@ private: // need initialize std::vector *m_resultRigBones; std::map *m_resultRigWeights; bool m_isRigObsolete; - Outcome *m_riggedOutcome; + Object *m_riggedObject; bool m_currentRigSucceed; MaterialPreviewsGenerator *m_materialPreviewsGenerator; MotionsGenerator *m_motionsGenerator; @@ -687,13 +698,13 @@ private: // need initialize std::map> m_mergedVariables; ScriptRunner *m_scriptRunner; bool m_isScriptResultObsolete; - VertexColorPainter *m_vertexColorPainter; + TexturePainter *m_texturePainter; bool m_isMouseTargetResultObsolete; PaintMode m_paintMode; float m_mousePickRadius; bool m_saveNextPaintSnapshot; - VoxelGrid *m_vertexColorVoxelGrid; GeneratedCacheContext *m_generatedCacheContext; + TexturePainterContext *m_texturePainterContext; private: static unsigned long m_maxSnapshot; std::deque m_undoItems; diff --git a/src/documentsaver.cpp b/src/documentsaver.cpp index 4b48adc9..bc518ca0 100644 --- a/src/documentsaver.cpp +++ b/src/documentsaver.cpp @@ -1,20 +1,26 @@ #include #include #include +#include #include "documentsaver.h" #include "imageforever.h" #include "ds3file.h" #include "snapshotxml.h" #include "variablesxml.h" #include "fileforever.h" +#include "objectXml.h" DocumentSaver::DocumentSaver(const QString *filename, - Snapshot *snapshot, + Snapshot *snapshot, + Object *object, + DocumentSaver::Textures *textures, QByteArray *turnaroundPngByteArray, QString *script, std::map> *variables) : m_filename(filename), m_snapshot(snapshot), + m_object(object), + m_textures(textures), m_turnaroundPngByteArray(turnaroundPngByteArray), m_script(script), m_variables(variables) @@ -24,6 +30,8 @@ DocumentSaver::DocumentSaver(const QString *filename, DocumentSaver::~DocumentSaver() { delete m_snapshot; + delete m_object; + delete m_textures; delete m_turnaroundPngByteArray; delete m_script; delete m_variables; @@ -33,6 +41,8 @@ void DocumentSaver::process() { save(m_filename, m_snapshot, + m_object, + m_textures, m_turnaroundPngByteArray, m_script, m_variables); @@ -82,18 +92,83 @@ void DocumentSaver::collectUsedResourceIds(const Snapshot *snapshot, } bool DocumentSaver::save(const QString *filename, - Snapshot *snapshot, + Snapshot *snapshot, + const Object *object, + Textures *textures, const QByteArray *turnaroundPngByteArray, const QString *script, const std::map> *variables) { Ds3FileWriter ds3Writer; - QByteArray modelXml; - QXmlStreamWriter stream(&modelXml); - saveSkeletonToXmlStream(snapshot, &stream); - if (modelXml.size() > 0) - ds3Writer.add("model.xml", "model", &modelXml); + { + QByteArray modelXml; + QXmlStreamWriter stream(&modelXml); + saveSkeletonToXmlStream(snapshot, &stream); + if (modelXml.size() > 0) + ds3Writer.add("model.xml", "model", &modelXml); + } + + { + QByteArray objectXml; + QXmlStreamWriter stream(&objectXml); + saveObjectToXmlStream(object, &stream); + if (objectXml.size() > 0) + ds3Writer.add("object.xml", "object", &objectXml); + } + + if (nullptr != textures) { + if (nullptr != textures->textureImage && !textures->textureImage->isNull()) { + if (nullptr == textures->textureImageByteArray) { + textures->textureImageByteArray = new QByteArray; + QBuffer pngBuffer(textures->textureImageByteArray); + pngBuffer.open(QIODevice::WriteOnly); + textures->textureImage->save(&pngBuffer, "PNG"); + } + if (textures->textureImageByteArray->size() > 0) + ds3Writer.add("object_color.png", "asset", textures->textureImageByteArray); + } + if (nullptr != textures->textureNormalImage && !textures->textureNormalImage->isNull()) { + if (nullptr == textures->textureNormalImageByteArray) { + textures->textureNormalImageByteArray = new QByteArray; + QBuffer pngBuffer(textures->textureNormalImageByteArray); + pngBuffer.open(QIODevice::WriteOnly); + textures->textureNormalImage->save(&pngBuffer, "PNG"); + } + if (textures->textureNormalImageByteArray->size() > 0) + ds3Writer.add("object_normal.png", "asset", textures->textureNormalImageByteArray); + } + if (nullptr != textures->textureMetalnessImage && !textures->textureMetalnessImage->isNull()) { + if (nullptr == textures->textureMetalnessImageByteArray) { + textures->textureMetalnessImageByteArray = new QByteArray; + QBuffer pngBuffer(textures->textureMetalnessImageByteArray); + pngBuffer.open(QIODevice::WriteOnly); + textures->textureMetalnessImage->save(&pngBuffer, "PNG"); + } + if (textures->textureMetalnessImageByteArray->size() > 0) + ds3Writer.add("object_metallic.png", "asset", textures->textureMetalnessImageByteArray); + } + if (nullptr != textures->textureRoughnessImage && !textures->textureRoughnessImage->isNull()) { + if (nullptr == textures->textureRoughnessImageByteArray) { + textures->textureRoughnessImageByteArray = new QByteArray; + QBuffer pngBuffer(textures->textureRoughnessImageByteArray); + pngBuffer.open(QIODevice::WriteOnly); + textures->textureRoughnessImage->save(&pngBuffer, "PNG"); + } + if (textures->textureRoughnessImageByteArray->size() > 0) + ds3Writer.add("object_roughness.png", "asset", textures->textureRoughnessImageByteArray); + } + if (nullptr != textures->textureAmbientOcclusionImage && !textures->textureAmbientOcclusionImage->isNull()) { + if (nullptr == textures->textureAmbientOcclusionImageByteArray) { + textures->textureAmbientOcclusionImageByteArray = new QByteArray; + QBuffer pngBuffer(textures->textureAmbientOcclusionImageByteArray); + pngBuffer.open(QIODevice::WriteOnly); + textures->textureAmbientOcclusionImage->save(&pngBuffer, "PNG"); + } + if (textures->textureAmbientOcclusionImageByteArray->size() > 0) + ds3Writer.add("object_ao.png", "asset", textures->textureAmbientOcclusionImageByteArray); + } + } if (nullptr != turnaroundPngByteArray && turnaroundPngByteArray->size() > 0) ds3Writer.add("canvas.png", "asset", turnaroundPngByteArray); diff --git a/src/documentsaver.h b/src/documentsaver.h index 5ff7e040..697a402d 100644 --- a/src/documentsaver.h +++ b/src/documentsaver.h @@ -7,19 +7,54 @@ #include #include #include "snapshot.h" +#include "object.h" class DocumentSaver : public QObject { Q_OBJECT public: + class Textures + { + public: + QImage *textureImage = nullptr; + QByteArray *textureImageByteArray = nullptr; + QImage *textureNormalImage = nullptr; + QByteArray *textureNormalImageByteArray = nullptr; + QImage *textureMetalnessImage = nullptr; + QByteArray *textureMetalnessImageByteArray = nullptr; + QImage *textureRoughnessImage = nullptr; + QByteArray *textureRoughnessImageByteArray = nullptr; + QImage *textureAmbientOcclusionImage = nullptr; + QByteArray *textureAmbientOcclusionImageByteArray = nullptr; + bool textureHasTransparencySettings = false; + + ~Textures() + { + delete textureImage; + delete textureImageByteArray; + delete textureNormalImage; + delete textureNormalImageByteArray; + delete textureMetalnessImage; + delete textureMetalnessImageByteArray; + delete textureRoughnessImage; + delete textureRoughnessImageByteArray; + delete textureAmbientOcclusionImage; + delete textureAmbientOcclusionImageByteArray; + } + }; + DocumentSaver(const QString *filename, - Snapshot *snapshot, + Snapshot *snapshot, + Object *object, + Textures *textures, QByteArray *turnaroundPngByteArray, QString *script, std::map> *variables); ~DocumentSaver(); static bool save(const QString *filename, - Snapshot *snapshot, + Snapshot *snapshot, + const Object *object, + Textures *textures, const QByteArray *turnaroundPngByteArray, const QString *script, const std::map> *variables); @@ -33,6 +68,8 @@ public slots: private: const QString *m_filename = nullptr; Snapshot *m_snapshot = nullptr; + Object *m_object = nullptr; + Textures *m_textures = nullptr; QByteArray *m_turnaroundPngByteArray = nullptr; QString *m_script = nullptr; std::map> *m_variables = nullptr; diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp index da44e91c..0fdb767a 100644 --- a/src/documentwindow.cpp +++ b/src/documentwindow.cpp @@ -48,6 +48,7 @@ #include "modeloffscreenrender.h" #include "fileforever.h" #include "documentsaver.h" +#include "objectxml.h" int DocumentWindow::m_autoRecovered = false; @@ -159,7 +160,6 @@ DocumentWindow::DocumentWindow() : m_document(nullptr), m_firstShow(true), m_documentSaved(true), - m_exportPreviewWidget(nullptr), m_preferencesWidget(nullptr), m_isLastMeshGenerationSucceed(true), m_currentUpdatedMeshId(0), @@ -202,9 +202,13 @@ DocumentWindow::DocumentWindow() : //markerButton->setToolTip(tr("Marker pen")); //Theme::initAwesomeButton(markerButton); - //QPushButton *paintButton = new QPushButton(QChar(fa::paintbrush)); - //paintButton->setToolTip(tr("Paint brush")); - //Theme::initAwesomeButton(paintButton); + QPushButton *paintButton = new QPushButton(QChar(fa::paintbrush)); + paintButton->setToolTip(tr("Paint brush")); + paintButton->setVisible(m_document->objectLocked); + Theme::initAwesomeButton(paintButton); + connect(m_document, &Document::objectLockStateChanged, this, [=]() { + paintButton->setVisible(m_document->objectLocked); + }); //QPushButton *dragButton = new QPushButton(QChar(fa::handrocko)); //dragButton->setToolTip(tr("Enter drag mode")); @@ -251,48 +255,32 @@ DocumentWindow::DocumentWindow() : //rotateClockwiseButton->setToolTip(tr("Rotate whole model")); //Theme::initAwesomeButton(rotateClockwiseButton); - auto updateRegenerateIconAndTips = [&](SpinnableAwesomeButton *regenerateButton, bool isSuccessful, bool forceUpdate=false) { - if (!forceUpdate) { - if (m_isLastMeshGenerationSucceed == isSuccessful) - return; - } - m_isLastMeshGenerationSucceed = isSuccessful; - regenerateButton->setToolTip(m_isLastMeshGenerationSucceed ? tr("Regenerate") : tr("Mesh generation failed, please undo or adjust recent changed nodes\nTips:\n - Don't let generated mesh self-intersect\n - Make multiple parts instead of one single part for whole model")); - regenerateButton->setAwesomeIcon(m_isLastMeshGenerationSucceed ? QChar(fa::recycle) : QChar(fa::warning)); - }; + m_regenerateButton = new SpinnableAwesomeButton(); + updateRegenerateIcon(); + connect(m_regenerateButton->button(), &QPushButton::clicked, m_document, &Document::regenerateMesh); - SpinnableAwesomeButton *regenerateButton = new SpinnableAwesomeButton(); - updateRegenerateIconAndTips(regenerateButton, m_isLastMeshGenerationSucceed, true); - connect(m_document, &Document::meshGenerating, this, [=]() { - regenerateButton->showSpinner(true); - }); + connect(m_document, &Document::meshGenerating, this, &DocumentWindow::updateRegenerateIcon); connect(m_document, &Document::resultMeshChanged, this, [=]() { - updateRegenerateIconAndTips(regenerateButton, m_document->isMeshGenerationSucceed()); + m_isLastMeshGenerationSucceed = m_document->isMeshGenerationSucceed(); + updateRegenerateIcon(); + }); + connect(m_document, &Document::resultPartPreviewsChanged, this, [=]() { generatePartPreviewImages(); }); connect(m_document, &Document::paintedMeshChanged, [=]() { auto paintedMesh = m_document->takePaintedMesh(); m_modelRenderWidget->updateMesh(paintedMesh); }); - connect(m_document, &Document::postProcessing, this, [=]() { - regenerateButton->showSpinner(true); - }); - connect(m_document, &Document::textureGenerating, this, [=]() { - regenerateButton->showSpinner(true); - }); - connect(m_document, &Document::resultTextureChanged, this, [=]() { - if (!m_document->isMeshGenerating() && - !m_document->isPostProcessing() && - !m_document->isTextureGenerating()) { - regenerateButton->showSpinner(false); - } - }); - connect(regenerateButton->button(), &QPushButton::clicked, m_document, &Document::regenerateMesh); + connect(m_document, &Document::postProcessing, this, &DocumentWindow::updateRegenerateIcon); + connect(m_document, &Document::textureGenerating, this, &DocumentWindow::updateRegenerateIcon); + connect(m_document, &Document::resultTextureChanged, this, &DocumentWindow::updateRegenerateIcon); + connect(m_document, &Document::postProcessedResultChanged, this, &DocumentWindow::updateRegenerateIcon); + connect(m_document, &Document::objectLockStateChanged, this, &DocumentWindow::updateRegenerateIcon); toolButtonLayout->addWidget(addButton); toolButtonLayout->addWidget(selectButton); //toolButtonLayout->addWidget(markerButton); - //toolButtonLayout->addWidget(paintButton); + toolButtonLayout->addWidget(paintButton); //toolButtonLayout->addWidget(dragButton); toolButtonLayout->addWidget(zoomInButton); toolButtonLayout->addWidget(zoomOutButton); @@ -307,7 +295,7 @@ DocumentWindow::DocumentWindow() : //toolButtonLayout->addWidget(rotateCounterclockwiseButton); //toolButtonLayout->addWidget(rotateClockwiseButton); //toolButtonLayout->addSpacing(10); - toolButtonLayout->addWidget(regenerateButton); + toolButtonLayout->addWidget(m_regenerateButton); QLabel *verticalLogoLabel = new QLabel; @@ -399,79 +387,10 @@ DocumentWindow::DocumentWindow() : QDockWidget *partsDocker = new QDockWidget(tr("Parts"), this); partsDocker->setAllowedAreas(Qt::RightDockWidgetArea); - m_colorWheelWidget = new color_widgets::ColorWheel(nullptr); - m_colorWheelWidget->setContentsMargins(0, 5, 0, 5); - m_colorWheelWidget->hide(); - m_document->brushColor = m_colorWheelWidget->color(); - connect(m_colorWheelWidget, &color_widgets::ColorWheel::colorChanged, this, [=](QColor color) { - m_document->brushColor = color; - }); - - FloatNumberWidget *metalnessWidget = new FloatNumberWidget; - metalnessWidget->setSliderFixedWidth(Theme::sidebarPreferredWidth * 0.4); - metalnessWidget->setItemName(tr("Metallic")); - metalnessWidget->setRange(0.0, 1.0); - metalnessWidget->setValue(m_document->brushMetalness); - - connect(metalnessWidget, &FloatNumberWidget::valueChanged, [=](float value) { - m_document->brushMetalness = value; - }); - - QPushButton *metalnessEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeToolButtonWithoutFont(metalnessEraser); - - connect(metalnessEraser, &QPushButton::clicked, [=]() { - metalnessWidget->setValue(Model::m_defaultMetalness); - }); - - QHBoxLayout *metalnessLayout = new QHBoxLayout; - metalnessLayout->addWidget(metalnessEraser); - metalnessLayout->addWidget(metalnessWidget); - - FloatNumberWidget *roughnessWidget = new FloatNumberWidget; - roughnessWidget->setSliderFixedWidth(Theme::sidebarPreferredWidth * 0.35); - roughnessWidget->setItemName(tr("Roughness")); - roughnessWidget->setRange(0.0, 1.0); - roughnessWidget->setValue(m_document->brushRoughness); - - connect(roughnessWidget, &FloatNumberWidget::valueChanged, [=](float value) { - m_document->brushRoughness = value; - }); - - QPushButton *roughnessEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeToolButtonWithoutFont(roughnessEraser); - - connect(roughnessEraser, &QPushButton::clicked, [=]() { - roughnessWidget->setValue(Model::m_defaultRoughness); - }); - - QHBoxLayout *roughnessLayout = new QHBoxLayout; - roughnessLayout->addWidget(roughnessEraser); - roughnessLayout->addWidget(roughnessWidget); - - QWidget *metalnessAndRoughnessWidget = new QWidget; - QVBoxLayout *metalnessAndRoughnessLayout = new QVBoxLayout; - metalnessAndRoughnessLayout->addLayout(metalnessLayout); - metalnessAndRoughnessLayout->addLayout(roughnessLayout); - metalnessAndRoughnessWidget->setLayout(metalnessAndRoughnessLayout); - metalnessAndRoughnessWidget->hide(); - - connect(m_document, &Document::editModeChanged, this, [=]() { - m_colorWheelWidget->setVisible(SkeletonDocumentEditMode::Paint == m_document->editMode); - metalnessAndRoughnessWidget->setVisible(SkeletonDocumentEditMode::Paint == m_document->editMode); - }); - m_partTreeWidget = new PartTreeWidget(m_document, nullptr); - QWidget *partsWidget = new QWidget(partsDocker); - QVBoxLayout *partsLayout = new QVBoxLayout; - partsLayout->setContentsMargins(0, 0, 0, 0); - partsLayout->addWidget(m_colorWheelWidget); - partsLayout->addWidget(metalnessAndRoughnessWidget); - partsLayout->addWidget(m_partTreeWidget); - partsWidget->setLayout(partsLayout); - partsDocker->setWidget(partsWidget); + partsDocker->setWidget(m_partTreeWidget); addDockWidget(Qt::RightDockWidgetArea, partsDocker); - + QDockWidget *materialDocker = new QDockWidget(tr("Materials"), this); materialDocker->setAllowedAreas(Qt::RightDockWidgetArea); MaterialManageWidget *materialManageWidget = new MaterialManageWidget(m_document, materialDocker); @@ -503,16 +422,73 @@ DocumentWindow::DocumentWindow() : connect(motionManageWidget, &MotionManageWidget::unregisterDialog, this, &DocumentWindow::unregisterDialog); addDockWidget(Qt::RightDockWidgetArea, motionDocker); + QDockWidget *paintDocker = new QDockWidget(tr("Paint"), this); + paintDocker->setMinimumWidth(Theme::sidebarPreferredWidth); + paintDocker->setAllowedAreas(Qt::RightDockWidgetArea); + QPushButton *lockMeshButton = new QPushButton(Theme::awesome()->icon(fa::lock), tr("Lock Object for Painting")); + QPushButton *unlockMeshButton = new QPushButton(Theme::awesome()->icon(fa::unlock), tr("Remove Painting")); + connect(lockMeshButton, &QPushButton::clicked, this, [=]() { + m_document->setMeshLockState(true); + }); + connect(unlockMeshButton, &QPushButton::clicked, this, [this]() { + QMessageBox::StandardButton answer = QMessageBox::question(this, + APP_NAME, + tr("Do you really want to remove painting?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (answer == QMessageBox::No) { + return; + } + this->m_document->setMeshLockState(false); + this->m_document->regenerateMesh(); + }); + m_colorWheelWidget = new color_widgets::ColorWheel(nullptr); + m_colorWheelWidget->setContentsMargins(0, 5, 0, 5); + m_colorWheelWidget->setColor(m_document->brushColor); + m_paintWidget = new QWidget(paintDocker); + QVBoxLayout *paintLayout = new QVBoxLayout; + paintLayout->setContentsMargins(5, 5, 5, 5); + paintLayout->addWidget(lockMeshButton); + paintLayout->addWidget(unlockMeshButton); + paintLayout->addWidget(m_colorWheelWidget); + paintLayout->addStretch(); + m_paintWidget->setLayout(paintLayout); + paintDocker->setWidget(m_paintWidget); + connect(m_colorWheelWidget, &color_widgets::ColorWheel::colorChanged, this, [=](QColor color) { + m_document->brushColor = color; + }); + connect(m_document, &Document::editModeChanged, this, [=]() { + if (SkeletonDocumentEditMode::Paint == m_document->editMode) { + paintDocker->show(); + paintDocker->raise(); + } + }); + auto updatePaintWidgets = [=]() { + m_colorWheelWidget->setVisible(m_document->objectLocked); + lockMeshButton->setVisible(!m_document->objectLocked); + unlockMeshButton->setVisible(m_document->objectLocked); + }; + updatePaintWidgets(); + connect(m_document, &Document::objectLockStateChanged, this, [=]() { + updatePaintWidgets(); + }); + addDockWidget(Qt::RightDockWidgetArea, paintDocker); + QDockWidget *scriptDocker = new QDockWidget(tr("Script"), this); scriptDocker->setAllowedAreas(Qt::RightDockWidgetArea); ScriptWidget *scriptWidget = new ScriptWidget(m_document, scriptDocker); + scriptDocker->setVisible(Preferences::instance().scriptEnabled()); + connect(&Preferences::instance(), &Preferences::scriptEnabledChanged, this, [=]() { + scriptDocker->setVisible(Preferences::instance().scriptEnabled()); + }); scriptDocker->setWidget(scriptWidget); addDockWidget(Qt::RightDockWidgetArea, scriptDocker); tabifyDockWidget(partsDocker, materialDocker); tabifyDockWidget(materialDocker, rigDocker); tabifyDockWidget(rigDocker, motionDocker); - tabifyDockWidget(motionDocker, scriptDocker); + tabifyDockWidget(motionDocker, paintDocker); + tabifyDockWidget(paintDocker, scriptDocker); partsDocker->raise(); @@ -592,24 +568,22 @@ DocumentWindow::DocumentWindow() : m_fileMenu->addSeparator(); - //m_exportMenu = m_fileMenu->addMenu(tr("Export")); - - m_exportAction = new QAction(tr("Export..."), this); - connect(m_exportAction, &QAction::triggered, this, &DocumentWindow::showExportPreview, Qt::QueuedConnection); - m_fileMenu->addAction(m_exportAction); - m_exportAsObjAction = new QAction(tr("Export as OBJ..."), this); connect(m_exportAsObjAction, &QAction::triggered, this, &DocumentWindow::exportObjResult, Qt::QueuedConnection); m_fileMenu->addAction(m_exportAsObjAction); + m_exportAsGlbAction = new QAction(tr("Export as GLB..."), this); + connect(m_exportAsGlbAction, &QAction::triggered, this, &DocumentWindow::exportGlbResult, Qt::QueuedConnection); + m_fileMenu->addAction(m_exportAsGlbAction); + + m_exportAsFbxAction = new QAction(tr("Export as FBX..."), this); + connect(m_exportAsFbxAction, &QAction::triggered, this, &DocumentWindow::exportFbxResult, Qt::QueuedConnection); + m_fileMenu->addAction(m_exportAsFbxAction); + m_exportRenderedAsImageAction = new QAction(tr("Export as Image..."), this); connect(m_exportRenderedAsImageAction, &QAction::triggered, this, &DocumentWindow::exportRenderedResult, Qt::QueuedConnection); m_fileMenu->addAction(m_exportRenderedAsImageAction); - //m_exportAsObjPlusMaterialsAction = new QAction(tr("Wavefront (.obj + .mtl)..."), this); - //connect(m_exportAsObjPlusMaterialsAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportObjPlusMaterialsResult, Qt::QueuedConnection); - //m_exportMenu->addAction(m_exportAsObjPlusMaterialsAction); - m_fileMenu->addSeparator(); m_changeTurnaroundAction = new QAction(tr("Change Reference Sheet..."), this); @@ -624,8 +598,8 @@ DocumentWindow::DocumentWindow() : connect(m_fileMenu, &QMenu::aboutToShow, [=]() { m_exportAsObjAction->setEnabled(m_graphicsWidget->hasItems()); - //m_exportAsObjPlusMaterialsAction->setEnabled(m_graphicsWidget->hasItems()); - m_exportAction->setEnabled(m_graphicsWidget->hasItems()); + m_exportAsGlbAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady()); + m_exportAsFbxAction->setEnabled(m_graphicsWidget->hasItems() && m_document->isExportReady()); m_exportRenderedAsImageAction->setEnabled(m_graphicsWidget->hasItems()); }); @@ -862,8 +836,8 @@ DocumentWindow::DocumentWindow() : connect(m_toggleColorAction, &QAction::triggered, [&]() { m_modelRemoveColor = !m_modelRemoveColor; Model *mesh = nullptr; - if (m_document->isMeshGenerating() && - m_document->isPostProcessing() && + if (m_document->isMeshGenerating() || + m_document->isPostProcessing() || m_document->isTextureGenerating()) { mesh = m_document->takeResultMesh(); } else { @@ -915,6 +889,13 @@ DocumentWindow::DocumentWindow() : }); m_windowMenu->addAction(m_showMotionsAction); + m_showPaintAction = new QAction(tr("Paint"), this); + connect(m_showPaintAction, &QAction::triggered, [=]() { + paintDocker->show(); + paintDocker->raise(); + }); + m_windowMenu->addAction(m_showPaintAction); + m_showScriptAction = new QAction(tr("Script"), this); connect(m_showScriptAction, &QAction::triggered, [=]() { scriptDocker->show(); @@ -1008,9 +989,9 @@ DocumentWindow::DocumentWindow() : // m_document->setEditMode(SkeletonDocumentEditMode::Mark); //}); - //connect(paintButton, &QPushButton::clicked, [=]() { - // m_document->setEditMode(SkeletonDocumentEditMode::Paint); - //}); + connect(paintButton, &QPushButton::clicked, [=]() { + m_document->setEditMode(SkeletonDocumentEditMode::Paint); + }); //connect(dragButton, &QPushButton::clicked, [=]() { // m_document->setEditMode(SkeletonDocumentEditMode::Drag); @@ -1250,6 +1231,10 @@ DocumentWindow::DocumentWindow() : resultTextureMesh->removeColor(); m_modelRenderWidget->updateMesh(resultTextureMesh); }); + connect(m_document, &Document::resultColorTextureChanged, [=]() { + if (nullptr != m_document->textureImage) + m_modelRenderWidget->updateColorTexture(new QImage(*m_document->textureImage)); + }); connect(m_document, &Document::resultMeshChanged, [=]() { auto resultMesh = m_document->takeResultMesh(); @@ -1323,6 +1308,28 @@ DocumentWindow::DocumentWindow() : timer->start(); } +void DocumentWindow::updateRegenerateIcon() +{ + if (m_document->isMeshGenerating() || + m_document->isPostProcessing() || + m_document->isTextureGenerating()) { + m_regenerateButton->showSpinner(true); + if (nullptr != m_paintWidget) + m_paintWidget->hide(); + } else { + m_regenerateButton->showSpinner(false); + if (m_document->objectLocked) { + m_regenerateButton->setToolTip(tr("Object locked for painting")); + m_regenerateButton->setAwesomeIcon(QChar(fa::lock)); + } else { + m_regenerateButton->setToolTip(m_isLastMeshGenerationSucceed ? tr("Regenerate") : tr("Mesh generation failed, please undo or adjust recent changed nodes\nTips:\n - Don't let generated mesh self-intersect\n - Make multiple parts instead of one single part for whole model")); + m_regenerateButton->setAwesomeIcon(m_isLastMeshGenerationSucceed ? QChar(fa::recycle) : QChar(fa::warning)); + } + if (nullptr != m_paintWidget) + m_paintWidget->show(); + } +} + void DocumentWindow::toggleRotation() { if (nullptr == m_graphicsWidget) @@ -1577,14 +1584,59 @@ void DocumentWindow::saveTo(const QString &saveAsFilename) QApplication::setOverrideCursor(Qt::WaitCursor); Snapshot snapshot; m_document->toSnapshot(&snapshot); + DocumentSaver::Textures textures; + textures.textureImage = m_document->textureImage; + textures.textureImageByteArray = m_document->textureImageByteArray; + textures.textureNormalImage = m_document->textureNormalImage; + textures.textureNormalImageByteArray = m_document->textureNormalImageByteArray; + textures.textureMetalnessImage = m_document->textureMetalnessImage; + textures.textureMetalnessImageByteArray = m_document->textureMetalnessImageByteArray; + textures.textureRoughnessImage = m_document->textureRoughnessImage; + textures.textureRoughnessImageByteArray = m_document->textureRoughnessImageByteArray; + textures.textureAmbientOcclusionImage = m_document->textureAmbientOcclusionImage; + textures.textureAmbientOcclusionImageByteArray = m_document->textureAmbientOcclusionImageByteArray; + textures.textureHasTransparencySettings = m_document->textureHasTransparencySettings; if (DocumentSaver::save(&filename, &snapshot, + &m_document->currentPostProcessedObject(), + &textures, (!m_document->turnaround.isNull() && m_document->turnaroundPngByteArray.size() > 0) ? &m_document->turnaroundPngByteArray : nullptr, (!m_document->script().isEmpty()) ? &m_document->script() : nullptr, (!m_document->variables().empty()) ? &m_document->variables() : nullptr)) { setCurrentFilename(filename); } + textures.textureImage = nullptr; + textures.textureNormalImage = nullptr; + textures.textureMetalnessImage = nullptr; + textures.textureRoughnessImage = nullptr; + textures.textureAmbientOcclusionImage = nullptr; + + if (textures.textureImageByteArray != m_document->textureImageByteArray) + std::swap(textures.textureImageByteArray, m_document->textureImageByteArray); + else + textures.textureImageByteArray = nullptr; + + if (textures.textureNormalImageByteArray != m_document->textureNormalImageByteArray) + std::swap(textures.textureNormalImageByteArray, m_document->textureNormalImageByteArray); + else + textures.textureNormalImageByteArray = nullptr; + + if (textures.textureMetalnessImageByteArray != m_document->textureMetalnessImageByteArray) + std::swap(textures.textureMetalnessImageByteArray, m_document->textureMetalnessImageByteArray); + else + textures.textureMetalnessImageByteArray = nullptr; + + if (textures.textureRoughnessImageByteArray != m_document->textureRoughnessImageByteArray) + std::swap(textures.textureRoughnessImageByteArray, m_document->textureRoughnessImageByteArray); + else + textures.textureRoughnessImageByteArray = nullptr; + + if (textures.textureAmbientOcclusionImageByteArray != m_document->textureAmbientOcclusionImageByteArray) + std::swap(textures.textureAmbientOcclusionImageByteArray, m_document->textureAmbientOcclusionImageByteArray); + else + textures.textureAmbientOcclusionImageByteArray = nullptr; + QApplication::restoreOverrideCursor(); } @@ -1767,8 +1819,27 @@ void DocumentWindow::openPathAs(const QString &path, const QString &asName) if (item.name == "canvas.png") { QByteArray data; ds3Reader.loadItem(item.name, &data); - QImage image = QImage::fromData(data, "PNG"); - m_document->updateTurnaround(image); + m_document->updateTurnaround(QImage::fromData(data, "PNG")); + } else if (item.name == "object_color.png") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + m_document->updateTextureImage(new QImage(QImage::fromData(data, "PNG"))); + } else if (item.name == "object_normal.png") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + m_document->updateTextureNormalImage(new QImage(QImage::fromData(data, "PNG"))); + } else if (item.name == "object_metallic.png") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + m_document->updateTextureMetalnessImage(new QImage(QImage::fromData(data, "PNG"))); + } else if (item.name == "object_roughness.png") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + m_document->updateTextureRoughnessImage(new QImage(QImage::fromData(data, "PNG"))); + } else if (item.name == "object_ao.png") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + m_document->updateTextureAmbientOcclusionImage(new QImage(QImage::fromData(data, "PNG"))); } } else if (item.type == "script") { if (item.name == "model.js") { @@ -1788,6 +1859,18 @@ void DocumentWindow::openPathAs(const QString &path, const QString &asName) } } } + + for (int i = 0; i < ds3Reader.items().size(); ++i) { + Ds3ReaderItem item = ds3Reader.items().at(i); + if (item.type == "object") { + QByteArray data; + ds3Reader.loadItem(item.name, &data); + QXmlStreamReader stream(data); + Object *object = new Object; + loadObjectFromXmlStream(object, stream); + m_document->updateObject(object); + } + } } QApplication::restoreOverrideCursor(); @@ -1874,23 +1957,6 @@ void DocumentWindow::exportObjToFilename(const QString &filename) QApplication::restoreOverrideCursor(); } -void DocumentWindow::showExportPreview() -{ - if (nullptr == m_exportPreviewWidget) { - m_exportPreviewWidget = new ExportPreviewWidget(m_document, this); - connect(m_exportPreviewWidget, &ExportPreviewWidget::regenerate, m_document, &Document::regenerateMesh); - connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsGlb, this, &DocumentWindow::exportGlbResult); - connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsFbx, this, &DocumentWindow::exportFbxResult); - connect(m_document, &Document::resultMeshChanged, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); - connect(m_document, &Document::exportReady, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); - connect(m_document, &Document::resultTextureChanged, m_exportPreviewWidget, &ExportPreviewWidget::updateTexturePreview); - //connect(m_document, &Document::resultBakedTextureChanged, m_exportPreviewWidget, &ExportPreviewWidget::updateTexturePreview); - registerDialog(m_exportPreviewWidget); - } - m_exportPreviewWidget->show(); - m_exportPreviewWidget->raise(); -} - void DocumentWindow::exportFbxResult() { QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), @@ -1909,7 +1975,7 @@ void DocumentWindow::exportFbxToFilename(const QString &filename) return; } QApplication::setOverrideCursor(Qt::WaitCursor); - Outcome skeletonResult = m_document->currentPostProcessedOutcome(); + Object skeletonResult = m_document->currentPostProcessedObject(); std::vector>>> exportMotions; for (const auto &motionIt: m_document->motionMap) { exportMotions.push_back({motionIt.second.name, motionIt.second.jointNodeTrees}); @@ -1943,15 +2009,20 @@ void DocumentWindow::exportGlbToFilename(const QString &filename) return; } QApplication::setOverrideCursor(Qt::WaitCursor); - Outcome skeletonResult = m_document->currentPostProcessedOutcome(); + Object skeletonResult = m_document->currentPostProcessedObject(); std::vector>>> exportMotions; for (const auto &motionIt: m_document->motionMap) { exportMotions.push_back({motionIt.second.name, motionIt.second.jointNodeTrees}); } + QImage *textureMetalnessRoughnessAmbientOcclusionImage = + TextureGenerator::combineMetalnessRoughnessAmbientOcclusionImages(m_document->textureMetalnessImage, + m_document->textureRoughnessImage, + m_document->textureAmbientOcclusionImage); GlbFileWriter glbFileWriter(skeletonResult, m_document->resultRigBones(), m_document->resultRigWeights(), filename, m_document->textureHasTransparencySettings, - m_document->textureImage, m_document->textureNormalImage, m_document->textureMetalnessRoughnessAmbientOcclusionImage, exportMotions.empty() ? nullptr : &exportMotions); + m_document->textureImage, m_document->textureNormalImage, textureMetalnessRoughnessAmbientOcclusionImage, exportMotions.empty() ? nullptr : &exportMotions); glbFileWriter.save(); + delete textureMetalnessRoughnessAmbientOcclusionImage; QApplication::restoreOverrideCursor(); } diff --git a/src/documentwindow.h b/src/documentwindow.h index 07819769..b0433f2e 100644 --- a/src/documentwindow.h +++ b/src/documentwindow.h @@ -12,7 +12,6 @@ #include #include "document.h" #include "modelwidget.h" -#include "exportpreviewwidget.h" #include "rigwidget.h" #include "bonemark.h" #include "preferenceswidget.h" @@ -24,6 +23,7 @@ class SkeletonGraphicsWidget; class PartTreeWidget; +class SpinnableAwesomeButton; class DocumentWindow : public QMainWindow { @@ -60,7 +60,6 @@ public slots: void exportObjResult(); void exportGlbResult(); void exportFbxResult(); - void showExportPreview(); void newWindow(); void newDocument(); void saveAs(); @@ -101,6 +100,7 @@ public slots: void importPath(const QString &filename); void generatePartPreviewImages(); void partPreviewImagesReady(); + void updateRegenerateIcon(); private: void initLockButton(QPushButton *button); void setCurrentFilename(const QString &filename); @@ -110,7 +110,6 @@ private: Document *m_document; bool m_firstShow; bool m_documentSaved; - ExportPreviewWidget *m_exportPreviewWidget; PreferencesWidget *m_preferencesWidget; std::vector m_dialogs; bool m_isLastMeshGenerationSucceed; @@ -136,15 +135,14 @@ private: QAction *m_saveAsAction; QAction *m_saveAllAction; QAction *m_showPreferencesAction; - QMenu *m_exportMenu; QAction *m_changeTurnaroundAction; QAction *m_quitAction; QAction *m_importAction; QAction *m_exportAsObjAction; - QAction *m_exportAsObjPlusMaterialsAction; - QAction *m_exportAction; + QAction *m_exportAsGlbAction; + QAction *m_exportAsFbxAction; QAction *m_exportRenderedAsImageAction; QMenu *m_editMenu; @@ -202,6 +200,7 @@ private: QAction *m_showMaterialsAction; QAction *m_showRigAction; QAction *m_showMotionsAction; + QAction *m_showPaintAction; QAction *m_showScriptAction; QMenu *m_helpMenu; @@ -233,6 +232,10 @@ private: bool m_isPartPreviewImagesObsolete = false; PartTreeWidget *m_partTreeWidget = nullptr; + + SpinnableAwesomeButton *m_regenerateButton = nullptr; + + QWidget *m_paintWidget = nullptr; public: static int m_autoRecovered; }; diff --git a/src/exportpreviewwidget.cpp b/src/exportpreviewwidget.cpp deleted file mode 100644 index ccc2a5f1..00000000 --- a/src/exportpreviewwidget.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include -#include -#include -#include -#include -#include "exportpreviewwidget.h" -#include "aboutwidget.h" -#include "version.h" -#include "theme.h" -#include "util.h" - -ExportPreviewWidget::ExportPreviewWidget(Document *document, QWidget *parent) : - QDialog(parent), - m_document(document), - m_colorPreviewLabel(nullptr), - m_spinnerWidget(nullptr) -{ - setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); - - QHBoxLayout *toolButtonLayout = new QHBoxLayout; - toolButtonLayout->setSpacing(0); - //toolButtonLayout->setContentsMargins(5, 10, 4, 0); - - m_colorPreviewLabel = new QLabel; - m_colorPreviewLabel->setMinimumSize(128, 128); - m_colorPreviewLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - m_normalPreviewLabel = new QLabel; - m_normalPreviewLabel->setMinimumSize(64, 64); - m_normalPreviewLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - m_metalnessRoughnessAmbientOcclusionPreviewLabel = new QLabel; - m_metalnessRoughnessAmbientOcclusionPreviewLabel->setMinimumSize(64, 64); - m_metalnessRoughnessAmbientOcclusionPreviewLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - //QPushButton *regenerateButton = new QPushButton(QChar(fa::recycle)); - //initAwesomeButton(regenerateButton); - QPushButton *regenerateButton = new QPushButton(tr("Regenerate")); - regenerateButton->hide(); - connect(this, &ExportPreviewWidget::regenerate, this, &ExportPreviewWidget::checkSpinner); - connect(regenerateButton, &QPushButton::clicked, this, &ExportPreviewWidget::regenerate); - - QComboBox *exportFormatSelectBox = new QComboBox; - exportFormatSelectBox->addItem(tr(".glb")); - exportFormatSelectBox->addItem(tr(".fbx")); - exportFormatSelectBox->setCurrentIndex(0); - - m_saveButton = new QPushButton(tr("Save")); - connect(m_saveButton, &QPushButton::clicked, this, [=]() { - auto currentIndex = exportFormatSelectBox->currentIndex(); - if (0 == currentIndex) { - this->hide(); - emit saveAsGlb(); - } else if (1 == currentIndex) { - this->hide(); - emit saveAsFbx(); - } - }); - m_saveButton->hide(); - m_saveButton->setDefault(true); - - toolButtonLayout->addWidget(exportFormatSelectBox); - toolButtonLayout->addWidget(regenerateButton); - toolButtonLayout->addStretch(); - toolButtonLayout->addWidget(m_saveButton); - - QGridLayout *containerLayout = new QGridLayout; - containerLayout->setSpacing(0); - containerLayout->setContentsMargins(0, 0, 0, 0); - containerLayout->addWidget(m_colorPreviewLabel, 0, 0); - containerLayout->addWidget(m_normalPreviewLabel, 0, 1); - containerLayout->addWidget(m_metalnessRoughnessAmbientOcclusionPreviewLabel, 0, 2); - //containerLayout->setRowStretch(0, 1); - //containerLayout->setColumnStretch(0, 1); - - //m_textureRenderWidget = new ModelWidget; - //m_textureRenderWidget->setMinimumSize(128, 128); - //m_textureRenderWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - //QVBoxLayout *renderLayout = new QVBoxLayout; - //renderLayout->setSpacing(0); - //renderLayout->setContentsMargins(20, 0, 20, 0); - //renderLayout->addWidget(m_textureRenderWidget); - - QWidget *hrLightWidget = Theme::createHorizontalLineWidget(); - - QHBoxLayout *topLayout = new QHBoxLayout; - topLayout->setSpacing(0); - topLayout->setContentsMargins(0, 0, 0, 0); - topLayout->addLayout(containerLayout); - //topLayout->addLayout(renderLayout); - - QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addLayout(topLayout); - mainLayout->addWidget(hrLightWidget); - mainLayout->addLayout(toolButtonLayout); - - setLayout(mainLayout); - setMinimumSize(256, 256); - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - m_spinnerWidget = new WaitingSpinnerWidget(m_colorPreviewLabel); - m_spinnerWidget->setColor(Theme::white); - m_spinnerWidget->setInnerRadius(Theme::miniIconFontSize / 4); - m_spinnerWidget->setNumberOfLines(12); - m_spinnerWidget->hide(); - - setWindowTitle(unifiedWindowTitle(tr("Export"))); - - emit updateTexturePreview(); -} - -void ExportPreviewWidget::resizeEvent(QResizeEvent *event) -{ - QWidget::resizeEvent(event); - resizePreviewImages(); -} - -void ExportPreviewWidget::resizePreviewImages() -{ - if (m_colorImage.isNull()) { - m_colorPreviewLabel->hide(); - } else { - QPixmap pixmap = QPixmap::fromImage(m_colorImage); - m_colorPreviewLabel->setPixmap(pixmap.scaled(m_colorPreviewLabel->width(), m_colorPreviewLabel->height(), Qt::KeepAspectRatio)); - m_colorPreviewLabel->show(); - } - - if (m_normalImage.isNull()) { - m_normalPreviewLabel->hide(); - } else { - QPixmap pixmap = QPixmap::fromImage(m_normalImage); - m_normalPreviewLabel->setPixmap(pixmap.scaled(m_normalPreviewLabel->width(), m_normalPreviewLabel->height(), Qt::KeepAspectRatio)); - m_normalPreviewLabel->show(); - } - - if (m_metalnessRoughnessAmbientOcclusionImage.isNull()) { - m_metalnessRoughnessAmbientOcclusionPreviewLabel->hide(); - } else { - QPixmap pixmap = QPixmap::fromImage(m_metalnessRoughnessAmbientOcclusionImage); - m_metalnessRoughnessAmbientOcclusionPreviewLabel->setPixmap(pixmap.scaled(m_metalnessRoughnessAmbientOcclusionPreviewLabel->width(), m_metalnessRoughnessAmbientOcclusionPreviewLabel->height(), Qt::KeepAspectRatio)); - m_metalnessRoughnessAmbientOcclusionPreviewLabel->show(); - } -} - -void ExportPreviewWidget::initAwesomeButton(QPushButton *button) -{ - button->setFont(Theme::awesome()->font(Theme::toolIconFontSize)); - button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize); - button->setStyleSheet("QPushButton {color: " + Theme::white.name() + "}"); - button->setFocusPolicy(Qt::NoFocus); -} - -void ExportPreviewWidget::showEvent(QShowEvent *event) -{ - QWidget::showEvent(event); - checkSpinner(); - if (m_document->isPostProcessResultObsolete()) { - m_document->postProcess(); - } -} - -void ExportPreviewWidget::checkSpinner() -{ - if (m_document->isExportReady()) { - m_spinnerWidget->stop(); - m_spinnerWidget->hide(); - m_saveButton->show(); - } else { - m_spinnerWidget->start(); - m_spinnerWidget->show(); - m_saveButton->hide(); - } -} - -void ExportPreviewWidget::updateTexturePreview() -{ - if (m_document->textureGuideImage) - m_colorImage = *m_document->textureGuideImage; - else - m_colorImage = QImage(); - if (m_document->textureNormalImage) - m_normalImage = *m_document->textureNormalImage; - else - m_normalImage = QImage(); - if (m_document->textureMetalnessRoughnessAmbientOcclusionImage) - m_metalnessRoughnessAmbientOcclusionImage = *m_document->textureMetalnessRoughnessAmbientOcclusionImage; - else - m_metalnessRoughnessAmbientOcclusionImage = QImage(); - //m_textureRenderWidget->updateMesh(m_document->takeResultTextureMesh()); - resizePreviewImages(); - checkSpinner(); -} - diff --git a/src/exportpreviewwidget.h b/src/exportpreviewwidget.h deleted file mode 100644 index 5f9b631b..00000000 --- a/src/exportpreviewwidget.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef DUST3D_EXPORT_PREVIEW_WIDGET_H -#define DUST3D_EXPORT_PREVIEW_WIDGET_H -#include -#include -#include -#include -#include -#include "modelwidget.h" -#include "waitingspinnerwidget.h" -#include "document.h" - -class ExportPreviewWidget : public QDialog -{ - Q_OBJECT -signals: - void regenerate(); - void saveAsGlb(); - void saveAsFbx(); -public: - ExportPreviewWidget(Document *document, QWidget *parent=nullptr); -public slots: - void checkSpinner(); - void updateTexturePreview(); -protected: - void resizeEvent(QResizeEvent *event); - void showEvent(QShowEvent *event); -private: - void resizePreviewImages(); - void initAwesomeButton(QPushButton *button); -private: - Document *m_document; - QLabel *m_colorPreviewLabel; - QLabel *m_normalPreviewLabel; - QLabel *m_metalnessRoughnessAmbientOcclusionPreviewLabel; - QImage m_colorImage; - QImage m_normalImage; - QImage m_metalnessRoughnessAmbientOcclusionImage; - //ModelWidget *m_textureRenderWidget; - WaitingSpinnerWidget *m_spinnerWidget; - QPushButton *m_saveButton; -}; - -#endif diff --git a/src/fbxfile.cpp b/src/fbxfile.cpp index c9322a48..cd5fbdd0 100644 --- a/src/fbxfile.cpp +++ b/src/fbxfile.cpp @@ -2200,7 +2200,7 @@ void FbxFileWriter::createDefinitions(size_t deformerCount, m_fbxDocument.nodes.push_back(definitions); } -FbxFileWriter::FbxFileWriter(Outcome &outcome, +FbxFileWriter::FbxFileWriter(Object &object, const std::vector *resultRigBones, const std::map *resultRigWeights, const QString &filename, @@ -2236,19 +2236,19 @@ FbxFileWriter::FbxFileWriter(Outcome &outcome, geometry.addProperty(std::vector({'u','n','a','m','e','d','m','e','s','h',0,1,'G','e','o','m','e','t','r','y'}), 'S'); geometry.addProperty("Mesh"); std::vector positions; - for (const auto &vertex: outcome.vertices) { + for (const auto &vertex: object.vertices) { positions.push_back((double)vertex.x()); positions.push_back((double)vertex.y()); positions.push_back((double)vertex.z()); } std::vector indices; - for (const auto &triangle: outcome.triangles) { + for (const auto &triangle: object.triangles) { indices.push_back(triangle[0]); indices.push_back(triangle[1]); indices.push_back(triangle[2] ^ -1); } FBXNode layerElementNormal("LayerElementNormal"); - const auto triangleVertexNormals = outcome.triangleVertexNormals(); + const auto triangleVertexNormals = object.triangleVertexNormals(); if (nullptr != triangleVertexNormals) { layerElementNormal.addProperty((int32_t)0); layerElementNormal.addPropertyNode("Version", (int32_t)101); @@ -2268,7 +2268,7 @@ FbxFileWriter::FbxFileWriter(Outcome &outcome, layerElementNormal.addChild(FBXNode()); } FBXNode layerElementUv("LayerElementUV"); - const auto triangleVertexUvs = outcome.triangleVertexUvs(); + const auto triangleVertexUvs = object.triangleVertexUvs(); if (nullptr != triangleVertexUvs) { layerElementUv.addProperty((int32_t)0); layerElementUv.addPropertyNode("Version", (int32_t)101); diff --git a/src/fbxfile.h b/src/fbxfile.h index 452e23fc..f7cd9d79 100644 --- a/src/fbxfile.h +++ b/src/fbxfile.h @@ -6,14 +6,14 @@ #include #include #include -#include "outcome.h" +#include "object.h" #include "document.h" class FbxFileWriter : public QObject { Q_OBJECT public: - FbxFileWriter(Outcome &outcome, + FbxFileWriter(Object &object, const std::vector *resultRigBones, const std::map *resultRigWeights, const QString &filename, diff --git a/src/glbfile.cpp b/src/glbfile.cpp index 85c3d93b..a391d80b 100644 --- a/src/glbfile.cpp +++ b/src/glbfile.cpp @@ -22,7 +22,7 @@ bool GlbFileWriter::m_enableComment = false; -GlbFileWriter::GlbFileWriter(Outcome &outcome, +GlbFileWriter::GlbFileWriter(Object &object, const std::vector *resultRigBones, const std::map *resultRigWeights, const QString &filename, @@ -36,12 +36,12 @@ GlbFileWriter::GlbFileWriter(Outcome &outcome, m_outputAnimation(true), m_outputUv(true) { - const std::vector> *triangleVertexNormals = outcome.triangleVertexNormals(); + const std::vector> *triangleVertexNormals = object.triangleVertexNormals(); if (m_outputNormal) { m_outputNormal = nullptr != triangleVertexNormals; } - const std::vector> *triangleVertexUvs = outcome.triangleVertexUvs(); + const std::vector> *triangleVertexUvs = object.triangleVertexUvs(); if (m_outputUv) { m_outputUv = nullptr != triangleVertexUvs; } @@ -146,10 +146,10 @@ GlbFileWriter::GlbFileWriter(Outcome &outcome, std::vector triangleVertexPositions; std::vector triangleVertexOldIndices; - for (const auto &triangleIndices: outcome.triangles) { + for (const auto &triangleIndices: object.triangles) { for (size_t j = 0; j < 3; ++j) { triangleVertexOldIndices.push_back(triangleIndices[j]); - triangleVertexPositions.push_back(outcome.vertices[triangleIndices[j]]); + triangleVertexPositions.push_back(object.vertices[triangleIndices[j]]); } } diff --git a/src/glbfile.h b/src/glbfile.h index 71cb721d..4794bb95 100644 --- a/src/glbfile.h +++ b/src/glbfile.h @@ -7,7 +7,7 @@ #include #include #include -#include "outcome.h" +#include "object.h" #include "json.hpp" #include "document.h" @@ -15,7 +15,7 @@ class GlbFileWriter : public QObject { Q_OBJECT public: - GlbFileWriter(Outcome &outcome, + GlbFileWriter(Object &object, const std::vector *resultRigBones, const std::map *resultRigWeights, const QString &filename, diff --git a/src/gridmeshbuilder.cpp b/src/gridmeshbuilder.cpp deleted file mode 100644 index 04b46258..00000000 --- a/src/gridmeshbuilder.cpp +++ /dev/null @@ -1,448 +0,0 @@ -#include -#include "gridmeshbuilder.h" -#include "cyclefinder.h" -#include "regionfiller.h" -#include "util.h" - -size_t GridMeshBuilder::addNode(const QVector3D &position, float radius) -{ - size_t newNodeIndex = m_nodes.size(); - - Node node; - node.position = position; - node.radius = radius; - node.source = newNodeIndex; - m_nodes.push_back(node); - - return newNodeIndex; -} - -size_t GridMeshBuilder::addEdge(size_t firstNodeIndex, size_t secondNodeIndex) -{ - size_t newEdgeIndex = m_edges.size(); - - Edge edge; - edge.firstNodeIndex = firstNodeIndex; - edge.secondNodeIndex = secondNodeIndex; - m_edges.push_back(edge); - - m_nodes[firstNodeIndex].neighborIndices.push_back(secondNodeIndex); - m_nodes[secondNodeIndex].neighborIndices.push_back(firstNodeIndex); - - return newEdgeIndex; -} - -const std::vector &GridMeshBuilder::getGeneratedPositions() -{ - return m_generatedPositions; -} - -const std::vector &GridMeshBuilder::getGeneratedSources() -{ - return m_generatedSources; -} - -const std::vector> &GridMeshBuilder::getGeneratedFaces() -{ - return m_generatedFaces; -} - -void GridMeshBuilder::splitCycleToPolylines(const std::vector &cycle, - std::vector> *polylines) -{ - if (cycle.size() < 3) { - qDebug() << "Invalid cycle size:" << cycle.size(); - return; - } - //qDebug() << "Cycle:" << cycle; - std::vector cornerIndices; - for (size_t i = 0; i < cycle.size(); ++i) { - size_t h = (i + cycle.size() - 1) % cycle.size(); - size_t j = (i + 1) % cycle.size(); - QVector3D hi = m_nodeVertices[cycle[i]].position - m_nodeVertices[cycle[h]].position; - QVector3D ij = m_nodeVertices[cycle[j]].position - m_nodeVertices[cycle[i]].position; - auto angle = degreesBetweenVectors(hi, ij); - //qDebug() << "angle[" << i << "]:" << angle; - if (angle >= m_polylineAngleChangeThreshold) - cornerIndices.push_back(i); - } - if (cornerIndices.size() < 3) { - qDebug() << "Invalid corners:" << cornerIndices.size(); - return; - } - for (size_t m = 0; m < cornerIndices.size(); ++m) { - size_t n = (m + 1) % cornerIndices.size(); - std::vector polyline; - size_t i = cornerIndices[m]; - size_t j = cornerIndices[n]; - for (size_t p = i; p != j; p = (p + 1) % cycle.size()) - polyline.push_back(cycle[p]); - polyline.push_back(cycle[j]); - //qDebug() << "Polyline[m" << m << "n" << n << "i" << i << "j" << j << "]:" << polyline; - polylines->push_back(polyline); - } -} - -void GridMeshBuilder::prepareNodeVertices() -{ - m_nodeVertices.resize(m_nodes.size()); - m_nodePositions.resize(m_nodes.size()); - for (size_t i = 0; i < m_nodes.size(); ++i) { - RegionFiller::Node node; - node.position = m_nodes[i].position; - node.radius = m_nodes[i].radius; - node.source = i; - m_nodeVertices[i] = node; - m_nodePositions[i] = node.position; - } -} - -void GridMeshBuilder::generateFaces() -{ - auto createEdgeKey = [](size_t v1, size_t v2) { - if (v1 > v2) - std::swap(v1, v2); - return std::make_pair(v1, v2); - }; - - std::map, std::vector> edgeToCandidateFaceMap; - m_generatedVertices = m_nodeVertices; - std::vector> candidateFaces; - std::vector candidateFaceSourceCycles; - m_generatedFaces.clear(); - - auto addCandidateFaces = [&](const std::vector> &newFaces, size_t sourceCycle) { - for (const auto &face: newFaces) { - size_t candidateFaceIndex = candidateFaces.size(); - candidateFaces.push_back(face); - candidateFaceSourceCycles.push_back(sourceCycle); - for (size_t i = 0; i < face.size(); ++i) { - size_t j = (i + 1) % face.size(); - auto edgeKey = createEdgeKey(face[i], face[j]); - edgeToCandidateFaceMap[edgeKey].push_back(candidateFaceIndex); - } - } - }; - - for (size_t i = 0; i < m_cycles.size(); ++i) { - const auto &it = m_cycles[i]; - std::vector> polylines; - splitCycleToPolylines(it, &polylines); - if (polylines.size() < 3) { - std::vector> faces; - faces.push_back(it); - addCandidateFaces(faces, i); - continue; - } - RegionFiller regionFiller(&m_generatedVertices, &polylines); - if (regionFiller.fill()) { - m_generatedVertices = regionFiller.getOldAndNewVertices(); - } else { - regionFiller.fillWithoutPartition(); - } - auto newFaces = regionFiller.getNewFaces(); - addCandidateFaces(newFaces, i); - } - - if (candidateFaces.empty()) - return; - - std::set visitedFaceIndices; - std::queue waitFaceIndices; - waitFaceIndices.push(0); - while (!waitFaceIndices.empty()) { - auto faceIndex = waitFaceIndices.front(); - waitFaceIndices.pop(); - if (visitedFaceIndices.find(faceIndex) != visitedFaceIndices.end()) - continue; - visitedFaceIndices.insert(faceIndex); - const auto &face = candidateFaces[faceIndex]; - if (face.size() < 3) { - qDebug() << "Invalid face, edges:" << face.size(); - continue; - } - bool shouldReverse = false; - size_t checkIndex = 0; - for (size_t i = 0; i < face.size(); ++i) { - size_t j = (i + 1) % face.size(); - if (m_halfEdgeMap.find(std::make_pair(face[i], face[j])) != m_halfEdgeMap.end() || - m_halfEdgeMap.find(std::make_pair(face[j], face[i])) != m_halfEdgeMap.end()) { - checkIndex = i; - break; - } - } - size_t nextOfCheckIndex = (checkIndex + 1) % face.size(); - std::pair edge = std::make_pair(face[checkIndex], face[nextOfCheckIndex]); - if (m_halfEdgeMap.find(edge) != m_halfEdgeMap.end()) { - std::pair oppositeEdge = std::make_pair(face[nextOfCheckIndex], face[checkIndex]); - if (m_halfEdgeMap.find(oppositeEdge) != m_halfEdgeMap.end()) { - qDebug() << "Too many face share one edge, should not happen"; - continue; - } - shouldReverse = true; - } - auto finalFace = face; - if (shouldReverse) { - std::reverse(finalFace.begin(), finalFace.end()); - } - size_t finalFaceIndex = m_generatedFaces.size(); - m_generatedFaces.push_back(finalFace); - m_generatedFaceSourceCycles.push_back(candidateFaceSourceCycles[faceIndex]); - for (size_t i = 0; i < finalFace.size(); ++i) { - size_t j = (i + 1) % finalFace.size(); - auto insertResult = m_halfEdgeMap.insert({std::make_pair(finalFace[i], finalFace[j]), finalFaceIndex}); - if (!insertResult.second) { - qDebug() << "Should not happend, half edge conflicts"; - } - auto edgeKey = createEdgeKey(finalFace[i], finalFace[j]); - auto findCandidates = edgeToCandidateFaceMap.find(edgeKey); - if (findCandidates == edgeToCandidateFaceMap.end()) - continue; - for (const auto &candidateFaceIndex: findCandidates->second) { - if (visitedFaceIndices.find(candidateFaceIndex) != visitedFaceIndices.end()) - continue; - waitFaceIndices.push(candidateFaceIndex); - } - } - } -} - -void GridMeshBuilder::calculateNormals() -{ - std::vector> vertexNormals(m_generatedVertices.size()); - for (const auto &face: m_generatedFaces) { - QVector3D sumOfNormals; - for (size_t i = 0; i < face.size(); ++i) { - size_t h = (i + face.size() - 1) % face.size(); - size_t j = (i + 1) % face.size(); - QVector3D vh = m_generatedVertices[face[h]].position; - QVector3D vi = m_generatedVertices[face[i]].position; - QVector3D vj = m_generatedVertices[face[j]].position; - sumOfNormals += QVector3D::normal(vj - vi, vh - vi); - } - QVector3D faceNormal = sumOfNormals.normalized(); - for (size_t i = 0; i < face.size(); ++i) { - vertexNormals[face[i]].push_back(faceNormal); - } - } - m_nodeNormals.resize(m_generatedVertices.size()); - for (size_t i = 0; i < m_nodeNormals.size(); ++i) { - const auto &normals = vertexNormals[i]; - if (normals.empty()) - continue; - m_nodeNormals[i] = std::accumulate(normals.begin(), normals.end(), QVector3D()).normalized(); - } -} - -void GridMeshBuilder::removeBigRingFaces() -{ - if (m_generatedFaces.size() != m_generatedFaceSourceCycles.size()) { - qDebug() << "Generated source cycles invalid"; - return; - } - auto maxBigRingSize = m_maxBigRingSize; - if (m_subdived) - maxBigRingSize *= 2; - std::set invalidCycles; - for (size_t faceIndex = 0; faceIndex < m_generatedFaces.size(); ++faceIndex) { - const auto &face = m_generatedFaces[faceIndex]; - size_t sourceCycle = m_generatedFaceSourceCycles[faceIndex]; - size_t oppositeCycles = 0; - for (size_t i = 0; i < face.size(); ++i) { - size_t j = (i + 1) % face.size(); - auto oppositeEdge = std::make_pair(face[j], face[i]); - auto findOpposite = m_halfEdgeMap.find(oppositeEdge); - if (findOpposite == m_halfEdgeMap.end()) - continue; - size_t oppositeFaceIndex = findOpposite->second; - size_t oppositeFaceSourceCycle = m_generatedFaceSourceCycles[oppositeFaceIndex]; - if (sourceCycle == oppositeFaceSourceCycle) - continue; - ++oppositeCycles; - } - if (oppositeCycles > maxBigRingSize) - invalidCycles.insert(sourceCycle); - } - - if (invalidCycles.empty()) - return; - - auto oldFaces = m_generatedFaces; - m_halfEdgeMap.clear(); - m_generatedFaces.clear(); - for (size_t faceIndex = 0; faceIndex < oldFaces.size(); ++faceIndex) { - size_t sourceCycle = m_generatedFaceSourceCycles[faceIndex]; - if (invalidCycles.find(sourceCycle) != invalidCycles.end()) - continue; - const auto &face = oldFaces[faceIndex]; - for (size_t i = 0; i < face.size(); ++i) { - size_t j = (i + 1) % face.size(); - auto edge = std::make_pair(face[i], face[j]); - m_halfEdgeMap.insert({edge, m_generatedFaces.size()}); - } - m_generatedFaces.push_back(face); - } -} - -void GridMeshBuilder::extrude() -{ - removeBigRingFaces(); - calculateNormals(); - - bool hasHalfEdge = false; - for (const auto &halfEdge: m_halfEdgeMap) { - auto oppositeHalfEdge = std::make_pair(halfEdge.first.second, halfEdge.first.first); - if (m_halfEdgeMap.find(oppositeHalfEdge) != m_halfEdgeMap.end()) - continue; - hasHalfEdge = true; - break; - } - - if (m_generatedVertices.empty()) - return; - - m_generatedPositions.resize(m_generatedVertices.size() * 2); - m_generatedSources.resize(m_generatedPositions.size(), 0); - for (size_t i = 0; i < m_generatedVertices.size(); ++i) { - const auto &vertex = m_generatedVertices[i]; - const auto &normal = m_nodeNormals[i]; - m_generatedPositions[i] = vertex.position; - m_generatedPositions[i] += normal * vertex.radius; - m_generatedSources[i] = m_nodes[vertex.source].source; - size_t j = m_generatedVertices.size() + i; - m_generatedPositions[j] = vertex.position; - m_generatedPositions[j] -= normal * vertex.radius; - m_generatedSources[j] = m_generatedSources[i]; - } - - bool pickSecondMesh = false; - // The outer faces should have longer edges - float sumOfFirstMeshEdgeLength = 0; - float sumOfSecondMeshEdgeLength = 0; - for (size_t i = 0; i < m_generatedFaces.size(); ++i) { - const auto &face = m_generatedFaces[i]; - for (size_t m = 0; m < face.size(); ++m) { - size_t n = (m + 1) % face.size(); - sumOfFirstMeshEdgeLength += (m_generatedPositions[face[m]] - m_generatedPositions[face[n]]).length(); - sumOfSecondMeshEdgeLength += (m_generatedPositions[m_generatedVertices.size() + face[m]] - m_generatedPositions[m_generatedVertices.size() + face[n]]).length(); - } - } - if (sumOfFirstMeshEdgeLength < sumOfSecondMeshEdgeLength) - pickSecondMesh = true; - - size_t faceNumPerLayer = m_generatedFaces.size(); - if (hasHalfEdge) { - m_generatedFaces.resize(faceNumPerLayer * 2); - for (size_t i = faceNumPerLayer; i < m_generatedFaces.size(); ++i) { - auto &face = m_generatedFaces[i]; - face = m_generatedFaces[i - faceNumPerLayer]; - for (auto &it: face) - it += m_generatedVertices.size(); - std::reverse(face.begin(), face.end()); - } - for (const auto &halfEdge: m_halfEdgeMap) { - auto oppositeHalfEdge = std::make_pair(halfEdge.first.second, halfEdge.first.first); - if (m_halfEdgeMap.find(oppositeHalfEdge) != m_halfEdgeMap.end()) - continue; - std::vector face = { - oppositeHalfEdge.first, - oppositeHalfEdge.second, - halfEdge.first.first + m_generatedVertices.size(), - halfEdge.first.second + m_generatedVertices.size() - }; - m_generatedFaces.push_back(face); - } - } else { - if (pickSecondMesh) { - for (auto &face: m_generatedFaces) { - for (auto &it: face) - it += m_generatedVertices.size(); - std::reverse(face.begin(), face.end()); - } - } - } -} - -void GridMeshBuilder::applyModifiers() -{ - std::vector oldNodes = m_nodes; - std::vector oldEdges = m_edges; - - m_edges.clear(); - - float distance2Threshold = m_meshTargetEdgeSize * m_meshTargetEdgeSize; - - std::set> visitedEdges; - for (const auto &oldEdge: oldEdges) { - auto key = std::make_pair(oldEdge.firstNodeIndex, oldEdge.secondNodeIndex); - if (visitedEdges.find(key) != visitedEdges.end()) - continue; - visitedEdges.insert(std::make_pair(oldEdge.firstNodeIndex, oldEdge.secondNodeIndex)); - visitedEdges.insert(std::make_pair(oldEdge.secondNodeIndex, oldEdge.firstNodeIndex)); - const auto &oldStartNode = oldNodes[oldEdge.firstNodeIndex]; - const auto &oldStopNode = oldNodes[oldEdge.secondNodeIndex]; - if ((oldStartNode.position - oldStopNode.position).lengthSquared() <= distance2Threshold) { - m_edges.push_back(oldEdge); - continue; - } - auto oldEdgeLength = (oldStartNode.position - oldStopNode.position).length(); - size_t newInsertNum = oldEdgeLength / m_meshTargetEdgeSize; - if (newInsertNum < 1) - newInsertNum = 1; - if (newInsertNum > 100) - continue; - float stepFactor = 1.0 / (newInsertNum + 1); - float factor = stepFactor; - std::vector edgeNodeIndices; - edgeNodeIndices.push_back(oldEdge.firstNodeIndex); - for (size_t i = 0; i < newInsertNum && factor < 1.0; factor += stepFactor, ++i) { - float firstFactor = 1.0 - factor; - Node newNode; - newNode.position = oldStartNode.position * firstFactor + oldStopNode.position * factor; - newNode.radius = oldStartNode.radius * firstFactor + oldStopNode.radius * factor; - if (firstFactor >= 0.5) { - newNode.source = oldEdge.firstNodeIndex; - } else { - newNode.source = oldEdge.secondNodeIndex; - } - edgeNodeIndices.push_back(m_nodes.size()); - m_nodes.push_back(newNode); - } - edgeNodeIndices.push_back(oldEdge.secondNodeIndex); - for (size_t i = 1; i < edgeNodeIndices.size(); ++i) { - size_t h = i - 1; - m_edges.push_back(Edge {edgeNodeIndices[h], edgeNodeIndices[i]}); - } - } -} - -void GridMeshBuilder::setSubdived(bool subdived) -{ - m_subdived = subdived; -} - -bool GridMeshBuilder::build() -{ - if (m_subdived) - applyModifiers(); - prepareNodeVertices(); - findCycles(); - if (m_cycles.empty()) - return false; - generateFaces(); - extrude(); - return true; -} - -void GridMeshBuilder::findCycles() -{ - std::vector> edges(m_edges.size()); - for (size_t i = 0; i < m_edges.size(); ++i) { - const auto &source = m_edges[i]; - edges[i] = std::make_pair(source.firstNodeIndex, source.secondNodeIndex); - } - CycleFinder cycleFinder(m_nodePositions, edges); - cycleFinder.find(); - m_cycles = cycleFinder.getCycles(); -} - diff --git a/src/gridmeshbuilder.h b/src/gridmeshbuilder.h deleted file mode 100644 index 61eec1b3..00000000 --- a/src/gridmeshbuilder.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef DUST3D_GRID_MESH_BUILDER_H -#define DUST3D_GRID_MESH_BUILDER_H -#include -#include -#include -#include "regionfiller.h" - -class GridMeshBuilder -{ -public: - struct Node - { - QVector3D position; - float radius; - size_t source; - std::vector neighborIndices; - }; - - struct Edge - { - size_t firstNodeIndex; - size_t secondNodeIndex; - }; - - size_t addNode(const QVector3D &position, float radius); - size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex); - void setSubdived(bool subdived); - bool build(); - const std::vector &getGeneratedPositions(); - const std::vector &getGeneratedSources(); - const std::vector> &getGeneratedFaces(); - -private: - std::vector m_nodes; - std::vector m_edges; - std::vector m_generatedVertices; - std::vector m_generatedPositions; - std::vector m_generatedSources; - std::vector> m_generatedFaces; - std::vector m_generatedFaceSourceCycles; - std::vector m_nodeVertices; - std::vector m_nodePositions; - std::vector> m_cycles; - std::map, size_t> m_halfEdgeMap; - std::vector m_nodeNormals; - float m_polylineAngleChangeThreshold = 35; - float m_meshTargetEdgeSize = 0.04; - size_t m_maxBigRingSize = 8; - bool m_subdived = false; - void applyModifiers(); - void prepareNodeVertices(); - void findCycles(); - void splitCycleToPolylines(const std::vector &cycle, - std::vector> *polylines); - void generateFaces(); - void extrude(); - void calculateNormals(); - void removeBigRingFaces(); -}; - -#endif - diff --git a/src/libdust3d.cpp b/src/libdust3d.cpp index 72428aee..9d670aae 100644 --- a/src/libdust3d.cpp +++ b/src/libdust3d.cpp @@ -18,7 +18,7 @@ struct _dust3d GeneratedCacheContext *cacheContext = nullptr; Model *resultMesh = nullptr; Snapshot *snapshot = nullptr; - Outcome *outcome = nullptr; + Object *object = nullptr; int error = DUST3D_ERROR; }; @@ -38,8 +38,8 @@ DUST3D_DLL void DUST3D_API dust3dClose(dust3d *ds3) delete ds3->snapshot; ds3->snapshot = nullptr; - delete ds3->outcome; - ds3->outcome = nullptr; + delete ds3->object; + ds3->object = nullptr; delete ds3; } @@ -56,8 +56,8 @@ DUST3D_DLL dust3d * DUST3D_API dust3dOpenFromMemory(const char *documentType, co ds3->error = DUST3D_ERROR; - delete ds3->outcome; - ds3->outcome = new Outcome; + delete ds3->object; + ds3->object = new Object; if (0 == strcmp(documentType, "xml")) { QByteArray data(buffer, size); @@ -138,10 +138,10 @@ DUST3D_DLL int DUST3D_API dust3dGenerateMesh(dust3d *ds3) meshGenerator->setGeneratedCacheContext(ds3->cacheContext); meshGenerator->generate(); - delete ds3->outcome; - ds3->outcome = meshGenerator->takeOutcome(); - if (nullptr == ds3->outcome) - ds3->outcome = new Outcome; + delete ds3->object; + ds3->object = meshGenerator->takeObject(); + if (nullptr == ds3->object) + ds3->object = new Object; if (meshGenerator->isSuccessful()) ds3->error = DUST3D_OK; @@ -153,17 +153,17 @@ DUST3D_DLL int DUST3D_API dust3dGenerateMesh(dust3d *ds3) DUST3D_DLL int DUST3D_API dust3dGetMeshVertexCount(dust3d *ds3) { - return (int)ds3->outcome->vertices.size(); + return (int)ds3->object->vertices.size(); } DUST3D_DLL int DUST3D_API dust3dGetMeshTriangleCount(dust3d *ds3) { - return (int)ds3->outcome->triangles.size(); + return (int)ds3->object->triangles.size(); } DUST3D_DLL void DUST3D_API dust3dGetMeshTriangleIndices(dust3d *ds3, int *indices) { - for (const auto &it: ds3->outcome->triangles) { + for (const auto &it: ds3->object->triangles) { *(indices++) = (int)it[0]; *(indices++) = (int)it[1]; *(indices++) = (int)it[2]; @@ -172,15 +172,15 @@ DUST3D_DLL void DUST3D_API dust3dGetMeshTriangleIndices(dust3d *ds3, int *indice DUST3D_DLL void DUST3D_API dust3dGetMeshTriangleColors(dust3d *ds3, unsigned int *colors) { - for (const auto &it: ds3->outcome->triangleColors) { + for (const auto &it: ds3->object->triangleColors) { *(colors++) = ((unsigned int)it.red() << 16) | ((unsigned int)it.green() << 8) | ((unsigned int)it.blue() << 0); } } DUST3D_DLL void DUST3D_API dust3dGetMeshVertexPosition(dust3d *ds3, int vertexIndex, float *x, float *y, float *z) { - if (vertexIndex >= 0 && vertexIndex < ds3->outcome->vertices.size()) { - const auto &v = ds3->outcome->vertices[vertexIndex]; + if (vertexIndex >= 0 && vertexIndex < ds3->object->vertices.size()) { + const auto &v = ds3->object->vertices[vertexIndex]; *x = v.x(); *y = v.y(); *z = v.z(); @@ -189,8 +189,8 @@ DUST3D_DLL void DUST3D_API dust3dGetMeshVertexPosition(dust3d *ds3, int vertexIn DUST3D_DLL void DUST3D_API dust3dGetMeshVertexSource(dust3d *ds3, int vertexIndex, unsigned char partId[16], unsigned char nodeId[16]) { - if (vertexIndex >= 0 && vertexIndex < ds3->outcome->vertices.size()) { - const auto &source = ds3->outcome->vertexSourceNodes[vertexIndex]; + if (vertexIndex >= 0 && vertexIndex < ds3->object->vertices.size()) { + const auto &source = ds3->object->vertexSourceNodes[vertexIndex]; auto sourcePartUuid = source.first.toByteArray(QUuid::Id128); memcpy(partId, sourcePartUuid.constData(), sizeof(partId)); @@ -202,12 +202,12 @@ DUST3D_DLL void DUST3D_API dust3dGetMeshVertexSource(dust3d *ds3, int vertexInde DUST3D_DLL int DUST3D_API dust3dGetMeshTriangleAndQuadCount(dust3d *ds3) { - return (int)ds3->outcome->triangleAndQuads.size(); + return (int)ds3->object->triangleAndQuads.size(); } DUST3D_DLL void DUST3D_API dust3dGetMeshTriangleAndQuadIndices(dust3d *ds3, int *indices) { - for (const auto &it: ds3->outcome->triangleAndQuads) { + for (const auto &it: ds3->object->triangleAndQuads) { *(indices++) = (int)it[0]; *(indices++) = (int)it[1]; *(indices++) = (int)it[2]; diff --git a/src/materialpreviewsgenerator.cpp b/src/materialpreviewsgenerator.cpp index 07ebb5b8..bf0e5e94 100644 --- a/src/materialpreviewsgenerator.cpp +++ b/src/materialpreviewsgenerator.cpp @@ -64,18 +64,18 @@ void MaterialPreviewsGenerator::generate() partIds.push_back(QUuid(mirror.first)); } - Outcome *outcome = meshGenerator->takeOutcome(); - if (nullptr != outcome) { - MeshResultPostProcessor *poseProcessor = new MeshResultPostProcessor(*outcome); + Object *object = meshGenerator->takeObject(); + if (nullptr != object) { + MeshResultPostProcessor *poseProcessor = new MeshResultPostProcessor(*object); poseProcessor->poseProcess(); - delete outcome; - outcome = poseProcessor->takePostProcessedOutcome(); + delete object; + object = poseProcessor->takePostProcessedObject(); delete poseProcessor; } - if (nullptr != outcome) { + if (nullptr != object) { for (const auto &material: m_materials) { - TextureGenerator *textureGenerator = new TextureGenerator(*outcome); + TextureGenerator *textureGenerator = new TextureGenerator(*object); for (const auto &layer: material.second) { for (const auto &mapItem: layer.maps) { const QImage *image = ImageForever::get(mapItem.imageId); @@ -106,7 +106,7 @@ void MaterialPreviewsGenerator::generate() } } - delete outcome; + delete object; delete meshGenerator; delete cacheContext; diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index fe0610b1..a7cabee9 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -39,7 +39,7 @@ MeshGenerator::~MeshGenerator() delete it.second; delete m_resultMesh; delete m_snapshot; - delete m_outcome; + delete m_object; delete m_cutFaceTransforms; delete m_nodesCutFaces; } @@ -78,11 +78,11 @@ const std::set &MeshGenerator::generatedPreviewPartIds() return m_generatedPreviewPartIds; } -Outcome *MeshGenerator::takeOutcome() +Object *MeshGenerator::takeObject() { - Outcome *outcome = m_outcome; - m_outcome = nullptr; - return outcome; + Object *object = m_object; + m_object = nullptr; + return object; } std::map *MeshGenerator::takeCutFaceTransforms() @@ -420,7 +420,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, colorSolubility = colorSolubilityString.toFloat(); float metalness = 0; - QString metalnessString = valueOfKeyInMapOrEmpty(part, "metalness"); + QString metalnessString = valueOfKeyInMapOrEmpty(part, "metallic"); if (!metalnessString.isEmpty()) metalness = metalnessString.toFloat(); @@ -440,11 +440,9 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, } auto &partCache = m_cacheContext->parts[partIdString]; - partCache.outcomeNodes.clear(); - partCache.outcomeEdges.clear(); - partCache.outcomeNodeVertices.clear(); - partCache.outcomePaintMap.clear(); - partCache.outcomePaintMap.partId = partId; + partCache.objectNodes.clear(); + partCache.objectEdges.clear(); + partCache.objectNodeVertices.clear(); partCache.vertices.clear(); partCache.faces.clear(); partCache.previewTriangles.clear(); @@ -542,41 +540,41 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, //} auto addNodeToPartCache = [&](const QString &nodeIdString, const NodeInfo &nodeInfo) { - OutcomeNode outcomeNode; - outcomeNode.partId = QUuid(partIdString); - outcomeNode.nodeId = QUuid(nodeIdString); - outcomeNode.origin = nodeInfo.position; - outcomeNode.radius = nodeInfo.radius; - outcomeNode.color = partColor; - outcomeNode.materialId = materialId; - outcomeNode.countershaded = countershaded; - outcomeNode.colorSolubility = colorSolubility; - outcomeNode.metalness = metalness; - outcomeNode.roughness = roughness; - outcomeNode.boneMark = nodeInfo.boneMark; + ObjectNode objectNode; + objectNode.partId = QUuid(partIdString); + objectNode.nodeId = QUuid(nodeIdString); + objectNode.origin = nodeInfo.position; + objectNode.radius = nodeInfo.radius; + objectNode.color = partColor; + objectNode.materialId = materialId; + objectNode.countershaded = countershaded; + objectNode.colorSolubility = colorSolubility; + objectNode.metalness = metalness; + objectNode.roughness = roughness; + objectNode.boneMark = nodeInfo.boneMark; if (!__mirroredByPartId.isEmpty()) - outcomeNode.mirroredByPartId = QUuid(__mirroredByPartId); + objectNode.mirroredByPartId = QUuid(__mirroredByPartId); if (!__mirrorFromPartId.isEmpty()) { - outcomeNode.mirrorFromPartId = QUuid(__mirrorFromPartId); - outcomeNode.origin.setX(-nodeInfo.position.x()); + objectNode.mirrorFromPartId = QUuid(__mirrorFromPartId); + objectNode.origin.setX(-nodeInfo.position.x()); } - outcomeNode.joined = partCache.joined; - partCache.outcomeNodes.push_back(outcomeNode); + objectNode.joined = partCache.joined; + partCache.objectNodes.push_back(objectNode); //if (xMirrored) { - // outcomeNode.partId = mirroredPartId; - // outcomeNode.mirrorFromPartId = QUuid(partId); - // outcomeNode.mirroredByPartId = QUuid(); - // outcomeNode.origin.setX(-nodeInfo.position.x()); - // partCache.outcomeNodes.push_back(outcomeNode); + // objectNode.partId = mirroredPartId; + // objectNode.mirrorFromPartId = QUuid(partId); + // objectNode.mirroredByPartId = QUuid(); + // objectNode.origin.setX(-nodeInfo.position.x()); + // partCache.objectNodes.push_back(objectNode); //} }; auto addEdgeToPartCache = [&](const QString &firstNodeIdString, const QString &secondNodeIdString) { - partCache.outcomeEdges.push_back({ + partCache.objectEdges.push_back({ {QUuid(partIdString), QUuid(firstNodeIdString)}, {QUuid(partIdString), QUuid(secondNodeIdString)} }); //if (xMirrored) { - // partCache.outcomeEdges.push_back({ + // partCache.objectEdges.push_back({ // {mirroredPartId, QUuid(firstNodeIdString)}, // {mirroredPartId, QUuid(secondNodeIdString)} // }); @@ -674,18 +672,6 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, const QString &toNodeIdString = edgeIt.second; addEdgeToPartCache(fromNodeIdString, toNodeIdString); } - - for (const auto &node: strokeModifier->nodes()) { - const auto &originNodeIdString = nodeIndexToIdStringMap[node.originNodeIndex]; - - OutcomePaintNode paintNode; - paintNode.originNodeIndex = node.originNodeIndex; - paintNode.originNodeId = QUuid(originNodeIdString); - paintNode.radius = node.radius; - paintNode.origin = node.position; - - partCache.outcomePaintMap.paintNodes.push_back(paintNode); - } buildSucceed = strokeMeshBuilder->build(); @@ -703,19 +689,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString, const auto &source = strokeMeshBuilder->generatedVerticesSourceNodeIndices()[i]; size_t nodeIndex = strokeModifier->nodes()[source].originNodeIndex; const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex]; - partCache.outcomeNodeVertices.push_back({position, {partIdString, nodeIdString}}); - - auto &paintNode = partCache.outcomePaintMap.paintNodes[source]; - paintNode.vertices.push_back(position); - } - - for (size_t i = 0; i < partCache.outcomePaintMap.paintNodes.size(); ++i) { - auto &paintNode = partCache.outcomePaintMap.paintNodes[i]; - paintNode.baseNormal = strokeMeshBuilder->nodeBaseNormal(i); - paintNode.direction = strokeMeshBuilder->nodeTraverseDirection(i); - paintNode.order = strokeMeshBuilder->nodeTraverseOrder(i); - - partCache.outcomeNodes[paintNode.originNodeIndex].direction = paintNode.direction; + partCache.objectNodeVertices.push_back({position, {partIdString, nodeIdString}}); } } else { if (strokeMeshBuilder->buildBaseNormalsOnly()) { @@ -841,8 +815,8 @@ bool MeshGenerator::fillPartWithMesh(GeneratedPart &partCache, meshGenerator->setGeneratedCacheContext(fillMeshCacheContext); meshGenerator->generate(); fillIsSucessful = meshGenerator->isSuccessful(); - Outcome *outcome = meshGenerator->takeOutcome(); - if (nullptr != outcome) { + Object *object = meshGenerator->takeObject(); + if (nullptr != object) { MeshStroketifier stroketifier; std::vector strokeNodes; for (const auto &nodeIndex: strokeMeshBuilder->nodeIndices()) { @@ -855,36 +829,36 @@ bool MeshGenerator::fillPartWithMesh(GeneratedPart &partCache, stroketifier.setCutRotation(cutRotation); stroketifier.setDeformWidth(deformWidth); stroketifier.setDeformThickness(deformThickness); - if (stroketifier.prepare(strokeNodes, outcome->vertices)) { - stroketifier.stroketify(&outcome->vertices); - std::vector agentNodes(outcome->nodes.size()); - for (size_t i = 0; i < outcome->nodes.size(); ++i) { + if (stroketifier.prepare(strokeNodes, object->vertices)) { + stroketifier.stroketify(&object->vertices); + std::vector agentNodes(object->nodes.size()); + for (size_t i = 0; i < object->nodes.size(); ++i) { auto &dest = agentNodes[i]; - const auto &src = outcome->nodes[i]; + const auto &src = object->nodes[i]; dest.position = src.origin; dest.radius = src.radius; } stroketifier.stroketify(&agentNodes); - for (size_t i = 0; i < outcome->nodes.size(); ++i) { + for (size_t i = 0; i < object->nodes.size(); ++i) { const auto &src = agentNodes[i]; - auto &dest = outcome->nodes[i]; + auto &dest = object->nodes[i]; dest.origin = src.position; dest.radius = src.radius; } } - partCache.outcomeNodes.insert(partCache.outcomeNodes.end(), outcome->nodes.begin(), outcome->nodes.end()); - partCache.outcomeEdges.insert(partCache.outcomeEdges.end(), outcome->edges.begin(), outcome->edges.end()); - partCache.vertices.insert(partCache.vertices.end(), outcome->vertices.begin(), outcome->vertices.end()); + partCache.objectNodes.insert(partCache.objectNodes.end(), object->nodes.begin(), object->nodes.end()); + partCache.objectEdges.insert(partCache.objectEdges.end(), object->edges.begin(), object->edges.end()); + partCache.vertices.insert(partCache.vertices.end(), object->vertices.begin(), object->vertices.end()); if (!strokeNodes.empty()) { for (auto &it: partCache.vertices) it += strokeNodes.front().position; } - for (size_t i = 0; i < outcome->vertexSourceNodes.size(); ++i) - partCache.outcomeNodeVertices.push_back({partCache.vertices[i], outcome->vertexSourceNodes[i]}); - partCache.faces.insert(partCache.faces.end(), outcome->triangleAndQuads.begin(), outcome->triangleAndQuads.end()); + for (size_t i = 0; i < object->vertexSourceNodes.size(); ++i) + partCache.objectNodeVertices.push_back({partCache.vertices[i], object->vertexSourceNodes[i]}); + partCache.faces.insert(partCache.faces.end(), object->triangleAndQuads.begin(), object->triangleAndQuads.end()); fillIsSucessful = true; } - delete outcome; + delete object; delete meshGenerator; delete fillMeshCacheContext; @@ -1037,10 +1011,9 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &component componentCache.sharedQuadEdges.clear(); componentCache.noneSeamVertices.clear(); - componentCache.outcomeNodes.clear(); - componentCache.outcomeEdges.clear(); - componentCache.outcomeNodeVertices.clear(); - componentCache.outcomePaintMaps.clear(); + componentCache.objectNodes.clear(); + componentCache.objectEdges.clear(); + componentCache.objectNodeVertices.clear(); componentCache.releaseMeshes(); QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType"); @@ -1066,13 +1039,12 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &component for (const auto &vertex: partCache.vertices) componentCache.noneSeamVertices.insert(vertex); collectSharedQuadEdges(partCache.vertices, partCache.faces, &componentCache.sharedQuadEdges); - for (const auto &it: partCache.outcomeNodes) - componentCache.outcomeNodes.push_back(it); - for (const auto &it: partCache.outcomeEdges) - componentCache.outcomeEdges.push_back(it); - for (const auto &it: partCache.outcomeNodeVertices) - componentCache.outcomeNodeVertices.push_back(it); - componentCache.outcomePaintMaps.push_back(partCache.outcomePaintMap); + for (const auto &it: partCache.objectNodes) + componentCache.objectNodes.push_back(it); + for (const auto &it: partCache.objectEdges) + componentCache.objectEdges.push_back(it); + for (const auto &it: partCache.objectNodeVertices) + componentCache.objectNodeVertices.push_back(it); } else { std::vector>>> combineGroups; // Firstly, group by combine mode @@ -1194,10 +1166,10 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &component std::vector> newQuads; std::vector> newTriangles; std::vector> interpolatedNodes; - Outcome::buildInterpolatedNodes(componentCache.outcomeNodes, - componentCache.outcomeEdges, + Object::buildInterpolatedNodes(componentCache.objectNodes, + componentCache.objectEdges, &interpolatedNodes); - remesh(componentCache.outcomeNodes, + remesh(componentCache.objectNodes, interpolatedNodes, combinedVertices, combinedFaces, @@ -1205,7 +1177,7 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &component &newVertices, &newQuads, &newTriangles, - &componentCache.outcomeNodeVertices); + &componentCache.objectNodeVertices); componentCache.sharedQuadEdges.clear(); for (const auto &face: newQuads) { if (face.size() != 4) @@ -1337,14 +1309,12 @@ MeshCombiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const std::vec componentCache.noneSeamVertices.insert(vertex); for (const auto &it: childComponentCache.sharedQuadEdges) componentCache.sharedQuadEdges.insert(it); - for (const auto &it: childComponentCache.outcomeNodes) - componentCache.outcomeNodes.push_back(it); - for (const auto &it: childComponentCache.outcomeEdges) - componentCache.outcomeEdges.push_back(it); - for (const auto &it: childComponentCache.outcomeNodeVertices) - componentCache.outcomeNodeVertices.push_back(it); - for (const auto &it: childComponentCache.outcomePaintMaps) - componentCache.outcomePaintMaps.push_back(it); + for (const auto &it: childComponentCache.objectNodes) + componentCache.objectNodes.push_back(it); + for (const auto &it: childComponentCache.objectEdges) + componentCache.objectEdges.push_back(it); + for (const auto &it: childComponentCache.objectNodeVertices) + componentCache.objectNodeVertices.push_back(it); if (nullptr == subMesh || subMesh->isNull()) { delete subMesh; @@ -1468,57 +1438,57 @@ void MeshGenerator::collectErroredParts() }; auto errorTriangleAndQuads = it.second.faces; - updateVertexIndices(errorTriangleAndQuads, m_outcome->vertices.size()); - m_outcome->vertices.insert(m_outcome->vertices.end(), it.second.vertices.begin(), it.second.vertices.end()); - m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), errorTriangleAndQuads.begin(), errorTriangleAndQuads.end()); + updateVertexIndices(errorTriangleAndQuads, m_object->vertices.size()); + m_object->vertices.insert(m_object->vertices.end(), it.second.vertices.begin(), it.second.vertices.end()); + m_object->triangleAndQuads.insert(m_object->triangleAndQuads.end(), errorTriangleAndQuads.begin(), errorTriangleAndQuads.end()); auto errorTriangles = it.second.previewTriangles; - updateVertexIndices(errorTriangles, m_outcome->vertices.size()); - m_outcome->vertices.insert(m_outcome->vertices.end(), it.second.previewVertices.begin(), it.second.previewVertices.end()); - m_outcome->triangles.insert(m_outcome->triangles.end(), errorTriangles.begin(), errorTriangles.end()); + updateVertexIndices(errorTriangles, m_object->vertices.size()); + m_object->vertices.insert(m_object->vertices.end(), it.second.previewVertices.begin(), it.second.previewVertices.end()); + m_object->triangles.insert(m_object->triangles.end(), errorTriangles.begin(), errorTriangles.end()); } } } -void MeshGenerator::postprocessOutcome(Outcome *outcome) +void MeshGenerator::postprocessObject(Object *object) { std::vector combinedFacesNormals; - for (const auto &face: outcome->triangles) { + for (const auto &face: object->triangles) { combinedFacesNormals.push_back(QVector3D::normal( - outcome->vertices[face[0]], - outcome->vertices[face[1]], - outcome->vertices[face[2]] + object->vertices[face[0]], + object->vertices[face[1]], + object->vertices[face[2]] )); } - outcome->triangleNormals = combinedFacesNormals; + object->triangleNormals = combinedFacesNormals; std::vector> sourceNodes; - triangleSourceNodeResolve(*outcome, sourceNodes, &outcome->vertexSourceNodes); - outcome->setTriangleSourceNodes(sourceNodes); + triangleSourceNodeResolve(*object, m_nodeVertices, sourceNodes, &object->vertexSourceNodes); + object->setTriangleSourceNodes(sourceNodes); std::map, QColor> sourceNodeToColorMap; - for (const auto &node: outcome->nodes) + for (const auto &node: object->nodes) sourceNodeToColorMap.insert({{node.partId, node.nodeId}, node.color}); - outcome->triangleColors.resize(outcome->triangles.size(), Qt::white); - const std::vector> *triangleSourceNodes = outcome->triangleSourceNodes(); + object->triangleColors.resize(object->triangles.size(), Qt::white); + const std::vector> *triangleSourceNodes = object->triangleSourceNodes(); if (nullptr != triangleSourceNodes) { - for (size_t triangleIndex = 0; triangleIndex < outcome->triangles.size(); triangleIndex++) { + for (size_t triangleIndex = 0; triangleIndex < object->triangles.size(); triangleIndex++) { const auto &source = (*triangleSourceNodes)[triangleIndex]; - outcome->triangleColors[triangleIndex] = sourceNodeToColorMap[source]; + object->triangleColors[triangleIndex] = sourceNodeToColorMap[source]; } } std::vector> triangleVertexNormals; - generateSmoothTriangleVertexNormals(outcome->vertices, - outcome->triangles, - outcome->triangleNormals, + generateSmoothTriangleVertexNormals(object->vertices, + object->triangles, + object->triangleNormals, &triangleVertexNormals); - outcome->setTriangleVertexNormals(triangleVertexNormals); + object->setTriangleVertexNormals(triangleVertexNormals); } -void MeshGenerator::remesh(const std::vector &inputNodes, +void MeshGenerator::remesh(const std::vector &inputNodes, const std::vector> &interpolatedNodes, const std::vector &inputVertices, const std::vector> &inputFaces, @@ -1596,7 +1566,7 @@ void MeshGenerator::collectIncombinableMesh(const MeshCombiner::Mesh *mesh, cons recoverQuads(uncombinedVertices, uncombinedFaces, componentCache.sharedQuadEdges, uncombinedTriangleAndQuads); - auto vertexStartIndex = m_outcome->vertices.size(); + auto vertexStartIndex = m_object->vertices.size(); auto updateVertexIndices = [=](std::vector> &faces) { for (auto &it: faces) { for (auto &subIt: it) @@ -1606,9 +1576,9 @@ void MeshGenerator::collectIncombinableMesh(const MeshCombiner::Mesh *mesh, cons updateVertexIndices(uncombinedFaces); updateVertexIndices(uncombinedTriangleAndQuads); - m_outcome->vertices.insert(m_outcome->vertices.end(), uncombinedVertices.begin(), uncombinedVertices.end()); - m_outcome->triangles.insert(m_outcome->triangles.end(), uncombinedFaces.begin(), uncombinedFaces.end()); - m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), uncombinedTriangleAndQuads.begin(), uncombinedTriangleAndQuads.end()); + m_object->vertices.insert(m_object->vertices.end(), uncombinedVertices.begin(), uncombinedVertices.end()); + m_object->triangles.insert(m_object->triangles.end(), uncombinedFaces.begin(), uncombinedFaces.end()); + m_object->triangleAndQuads.insert(m_object->triangleAndQuads.end(), uncombinedTriangleAndQuads.begin(), uncombinedTriangleAndQuads.end()); } void MeshGenerator::collectUncombinedComponent(const QString &componentIdString) @@ -1623,10 +1593,9 @@ void MeshGenerator::collectUncombinedComponent(const QString &componentIdString) return; } - m_outcome->nodes.insert(m_outcome->nodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end()); - m_outcome->edges.insert(m_outcome->edges.end(), componentCache.outcomeEdges.begin(), componentCache.outcomeEdges.end()); - m_outcome->nodeVertices.insert(m_outcome->nodeVertices.end(), componentCache.outcomeNodeVertices.begin(), componentCache.outcomeNodeVertices.end()); - m_outcome->paintMaps.insert(m_outcome->paintMaps.end(), componentCache.outcomePaintMaps.begin(), componentCache.outcomePaintMaps.end()); + m_object->nodes.insert(m_object->nodes.end(), componentCache.objectNodes.begin(), componentCache.objectNodes.end()); + m_object->edges.insert(m_object->edges.end(), componentCache.objectEdges.begin(), componentCache.objectEdges.end()); + m_nodeVertices.insert(m_nodeVertices.end(), componentCache.objectNodeVertices.begin(), componentCache.objectNodeVertices.end()); collectIncombinableMesh(componentCache.mesh, componentCache); return; @@ -1679,16 +1648,21 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString) clothMesh.clothOffset = componentClothOffset(component); clothMesh.clothStiffness = componentClothStiffness(component); clothMesh.clothIteration = componentClothIteration(component); - clothMesh.outcomeNodeVertices = &componentCache.outcomeNodeVertices; - m_outcome->clothNodes.insert(m_outcome->clothNodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end()); - m_outcome->nodes.insert(m_outcome->nodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end()); - m_outcome->edges.insert(m_outcome->edges.end(), componentCache.outcomeEdges.begin(), componentCache.outcomeEdges.end()); + clothMesh.objectNodeVertices = &componentCache.objectNodeVertices; + //m_object->clothNodes.insert(m_object->clothNodes.end(), componentCache.objectNodes.begin(), componentCache.objectNodes.end()); + //m_object->nodes.insert(m_object->nodes.end(), componentCache.objectNodes.begin(), componentCache.objectNodes.end()); + for (const auto &objectNode: componentCache.objectNodes) { + auto newNode = objectNode; + newNode.layer = ComponentLayer::Cloth; + m_object->nodes.push_back(newNode); + } + m_object->edges.insert(m_object->edges.end(), componentCache.objectEdges.begin(), componentCache.objectEdges.end()); } simulateClothMeshes(&clothMeshes, &m_clothCollisionVertices, &m_clothCollisionTriangles); for (auto &clothMesh: clothMeshes) { - auto vertexStartIndex = m_outcome->vertices.size(); + auto vertexStartIndex = m_object->vertices.size(); auto updateVertexIndices = [=](std::vector> &faces) { for (auto &it: faces) { for (auto &subIt: it) @@ -1696,23 +1670,23 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString) } }; updateVertexIndices(clothMesh.faces); - m_outcome->vertices.insert(m_outcome->vertices.end(), clothMesh.vertices.begin(), clothMesh.vertices.end()); + m_object->vertices.insert(m_object->vertices.end(), clothMesh.vertices.begin(), clothMesh.vertices.end()); for (const auto &it: clothMesh.faces) { if (4 == it.size()) { - m_outcome->triangles.push_back(std::vector { + m_object->triangles.push_back(std::vector { it[0], it[1], it[2] }); - m_outcome->triangles.push_back(std::vector { + m_object->triangles.push_back(std::vector { it[2], it[3], it[0] }); } else if (3 == it.size()) { - m_outcome->triangles.push_back(it); + m_object->triangles.push_back(it); } } - m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), clothMesh.faces.begin(), clothMesh.faces.end()); + m_object->triangleAndQuads.insert(m_object->triangleAndQuads.end(), clothMesh.faces.begin(), clothMesh.faces.end()); for (size_t i = 0; i < clothMesh.vertices.size(); ++i) { const auto &source = clothMesh.vertexSources[i]; - m_outcome->nodeVertices.push_back(std::make_pair(clothMesh.vertices[i], source)); + m_nodeVertices.push_back(std::make_pair(clothMesh.vertices[i], source)); } } } @@ -1856,8 +1830,8 @@ void MeshGenerator::generate() preprocessMirror(); - m_outcome = new Outcome; - m_outcome->meshId = m_id; + m_object = new Object; + m_object->meshId = m_id; //m_cutFaceTransforms = new std::map; //m_nodesCutFaces = new std::map>; @@ -1930,10 +1904,9 @@ void MeshGenerator::generate() const auto &componentCache = m_cacheContext->components[QUuid().toString()]; - m_outcome->nodes = componentCache.outcomeNodes; - m_outcome->edges = componentCache.outcomeEdges; - m_outcome->paintMaps = componentCache.outcomePaintMaps; - m_outcome->nodeVertices = componentCache.outcomeNodeVertices; + m_object->nodes = componentCache.objectNodes; + m_object->edges = componentCache.objectEdges; + m_nodeVertices = componentCache.objectNodeVertices; std::vector combinedVertices; std::vector> combinedFaces; @@ -1955,9 +1928,9 @@ void MeshGenerator::generate() } while (affectedNum > 0); } } - recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_outcome->triangleAndQuads); - m_outcome->vertices = combinedVertices; - m_outcome->triangles = combinedFaces; + recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_object->triangleAndQuads); + m_object->vertices = combinedVertices; + m_object->triangles = combinedFaces; } // Recursively check uncombined components @@ -1965,28 +1938,28 @@ void MeshGenerator::generate() collectIncombinableComponentMeshes(QUuid().toString()); // Fetch nodes as body nodes before cloth nodes collecting - std::set> bodyNodeMap; - m_outcome->bodyNodes.reserve(m_outcome->nodes.size()); - for (const auto &it: m_outcome->nodes) { - if (it.joined) { - bodyNodeMap.insert({it.partId, it.nodeId}); - m_outcome->bodyNodes.push_back(it); - } - } - m_outcome->bodyEdges.reserve(m_outcome->edges.size()); - for (const auto &it: m_outcome->edges) { - if (bodyNodeMap.find(it.first) == bodyNodeMap.end()) - continue; - if (bodyNodeMap.find(it.second) == bodyNodeMap.end()) - continue; - m_outcome->bodyEdges.push_back(it); - } + //std::set> bodyNodeMap; + //m_object->bodyNodes.reserve(m_object->nodes.size()); + //for (const auto &it: m_object->nodes) { + // if (it.joined) { + // bodyNodeMap.insert({it.partId, it.nodeId}); + // m_object->bodyNodes.push_back(it); + // } + //} + //m_object->bodyEdges.reserve(m_object->edges.size()); + //for (const auto &it: m_object->edges) { + // if (bodyNodeMap.find(it.first) == bodyNodeMap.end()) + // continue; + // if (bodyNodeMap.find(it.second) == bodyNodeMap.end()) + // continue; + // m_object->bodyEdges.push_back(it); + //} collectClothComponent(QUuid().toString()); collectErroredParts(); - postprocessOutcome(m_outcome); + postprocessObject(m_object); - m_resultMesh = new Model(*m_outcome); + m_resultMesh = new Model(*m_object); delete combinedMesh; diff --git a/src/meshgenerator.h b/src/meshgenerator.h index 0985c5c7..d284d687 100644 --- a/src/meshgenerator.h +++ b/src/meshgenerator.h @@ -7,7 +7,7 @@ #include "meshcombiner.h" #include "positionkey.h" #include "strokemeshbuilder.h" -#include "outcome.h" +#include "object.h" #include "snapshot.h" #include "combinemode.h" #include "model.h" @@ -29,12 +29,11 @@ public: MeshCombiner::Mesh *mesh = nullptr; std::vector vertices; std::vector> faces; - std::vector outcomeNodes; - std::vector, std::pair>> outcomeEdges; - std::vector>> outcomeNodeVertices; + std::vector objectNodes; + std::vector, std::pair>> objectEdges; + std::vector>> objectNodeVertices; std::vector previewVertices; std::vector> previewTriangles; - OutcomePaintMap outcomePaintMap; bool isSuccessful = false; bool joined = true; }; @@ -58,10 +57,9 @@ public: std::vector incombinableMeshes; std::set> sharedQuadEdges; std::set noneSeamVertices; - std::vector outcomeNodes; - std::vector, std::pair>> outcomeEdges; - std::vector>> outcomeNodeVertices; - std::vector outcomePaintMaps; + std::vector objectNodes; + std::vector, std::pair>> objectEdges; + std::vector>> objectNodeVertices; }; class GeneratedCacheContext @@ -92,7 +90,7 @@ public: Model *takeResultMesh(); Model *takePartPreviewMesh(const QUuid &partId); const std::set &generatedPreviewPartIds(); - Outcome *takeOutcome(); + Object *takeObject(); std::map *takeCutFaceTransforms(); std::map> *takeNodesCutFaces(); void generate(); @@ -116,7 +114,8 @@ private: float m_mainProfileMiddleX = 0; float m_sideProfileMiddleX = 0; float m_mainProfileMiddleY = 0; - Outcome *m_outcome = nullptr; + Object *m_object = nullptr; + std::vector>> m_nodeVertices; std::map> m_partNodeIds; std::map> m_partEdgeIds; std::set m_generatedPreviewPartIds; @@ -174,7 +173,7 @@ private: void collectClothComponentIdStrings(const QString &componentIdString, std::vector *componentIdStrings); void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector &cutTemplate); - void remesh(const std::vector &inputNodes, + void remesh(const std::vector &inputNodes, const std::vector> &interpolatedNodes, const std::vector &inputVertices, const std::vector> &inputFaces, @@ -183,7 +182,7 @@ private: std::vector> *outputQuads, std::vector> *outputTriangles, std::vector>> *outputNodeVertices); - void postprocessOutcome(Outcome *outcome); + void postprocessObject(Object *object); void collectErroredParts(); void preprocessMirror(); QString reverseUuid(const QString &uuidString); diff --git a/src/meshresultpostprocessor.cpp b/src/meshresultpostprocessor.cpp index 1bc7a84f..b2bcc577 100644 --- a/src/meshresultpostprocessor.cpp +++ b/src/meshresultpostprocessor.cpp @@ -3,22 +3,22 @@ #include "uvunwrap.h" #include "triangletangentresolve.h" -MeshResultPostProcessor::MeshResultPostProcessor(const Outcome &outcome) +MeshResultPostProcessor::MeshResultPostProcessor(const Object &object) { - m_outcome = new Outcome; - *m_outcome = outcome; + m_object = new Object; + *m_object = object; } MeshResultPostProcessor::~MeshResultPostProcessor() { - delete m_outcome; + delete m_object; } -Outcome *MeshResultPostProcessor::takePostProcessedOutcome() +Object *MeshResultPostProcessor::takePostProcessedObject() { - Outcome *outcome = m_outcome; - m_outcome = nullptr; - return outcome; + Object *object = m_object; + m_object = nullptr; + return object; } void MeshResultPostProcessor::poseProcess() @@ -26,20 +26,20 @@ void MeshResultPostProcessor::poseProcess() #ifndef NDEBUG return; #endif - if (!m_outcome->nodes.empty()) { + if (!m_object->nodes.empty()) { { std::vector> triangleVertexUvs; std::set seamVertices; std::map> partUvRects; - uvUnwrap(*m_outcome, triangleVertexUvs, seamVertices, partUvRects); - m_outcome->setTriangleVertexUvs(triangleVertexUvs); - m_outcome->setPartUvRects(partUvRects); + uvUnwrap(*m_object, triangleVertexUvs, seamVertices, partUvRects); + m_object->setTriangleVertexUvs(triangleVertexUvs); + m_object->setPartUvRects(partUvRects); } { std::vector triangleTangents; - triangleTangentResolve(*m_outcome, triangleTangents); - m_outcome->setTriangleTangents(triangleTangents); + triangleTangentResolve(*m_object, triangleTangents); + m_object->setTriangleTangents(triangleTangents); } } } diff --git a/src/meshresultpostprocessor.h b/src/meshresultpostprocessor.h index 7b2ec09f..e3e3984e 100644 --- a/src/meshresultpostprocessor.h +++ b/src/meshresultpostprocessor.h @@ -1,22 +1,22 @@ #ifndef DUST3D_MESH_RESULT_POST_PROCESSOR_H #define DUST3D_MESH_RESULT_POST_PROCESSOR_H #include -#include "outcome.h" +#include "object.h" class MeshResultPostProcessor : public QObject { Q_OBJECT public: - MeshResultPostProcessor(const Outcome &outcome); + MeshResultPostProcessor(const Object &object); ~MeshResultPostProcessor(); - Outcome *takePostProcessedOutcome(); + Object *takePostProcessedObject(); void poseProcess(); signals: void finished(); public slots: void process(); private: - Outcome *m_outcome = nullptr; + Object *m_object = nullptr; }; #endif diff --git a/src/model.cpp b/src/model.cpp index 67802c7d..693eb334 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -124,31 +124,31 @@ Model::Model(const std::vector &vertices, const std::vectorcolorR = 0.0; diff --git a/src/model.h b/src/model.h index 58371ea1..ff75b67a 100644 --- a/src/model.h +++ b/src/model.h @@ -6,7 +6,7 @@ #include #include #include -#include "outcome.h" +#include "object.h" #include "shadervertex.h" struct TriangulatedFace @@ -23,7 +23,7 @@ public: const QColor &color=Qt::white, float metalness=0.0, float roughness=0.0); - Model(Outcome &outcome); + Model(Object &object); Model(ShaderVertex *triangleVertices, int vertexNum, ShaderVertex *edgeVertices=nullptr, int edgeVertexCount=0); Model(const Model &mesh); Model(); diff --git a/src/modelmeshbinder.cpp b/src/modelmeshbinder.cpp index 4eef98f3..47e7718e 100644 --- a/src/modelmeshbinder.cpp +++ b/src/modelmeshbinder.cpp @@ -30,6 +30,7 @@ ModelMeshBinder::~ModelMeshBinder() delete m_newToonDepthMap; delete m_currentToonNormalMap; delete m_currentToonDepthMap; + delete m_colorTextureImage; } void ModelMeshBinder::updateMesh(Model *mesh) @@ -42,6 +43,13 @@ void ModelMeshBinder::updateMesh(Model *mesh) } } +void ModelMeshBinder::updateColorTexture(QImage *colorTextureImage) +{ + QMutexLocker lock(&m_colorTextureMutex); + delete m_colorTextureImage; + m_colorTextureImage = colorTextureImage; +} + void ModelMeshBinder::reloadMesh() { Model *mesh = nullptr; @@ -294,6 +302,15 @@ void ModelMeshBinder::paint(ModelShaderProgram *program) QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoTriangle); QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); if (m_hasTexture) { + { + QMutexLocker lock(&m_colorTextureMutex); + if (m_colorTextureImage) { + delete m_texture; + m_texture = new QOpenGLTexture(*m_colorTextureImage); + delete m_colorTextureImage; + m_colorTextureImage = nullptr; + } + } if (m_texture) m_texture->bind(0); program->setUniformValue(program->textureEnabledLoc(), 1); diff --git a/src/modelmeshbinder.h b/src/modelmeshbinder.h index f6783366..9cbc0e30 100644 --- a/src/modelmeshbinder.h +++ b/src/modelmeshbinder.h @@ -15,6 +15,7 @@ public: ~ModelMeshBinder(); Model *fetchCurrentMesh(); void updateMesh(Model *mesh); + void updateColorTexture(QImage *colorTextureImage); void initialize(); void paint(ModelShaderProgram *program); void cleanup(); @@ -56,6 +57,7 @@ private: QImage *m_newToonDepthMap = nullptr; QImage *m_currentToonNormalMap = nullptr; QImage *m_currentToonDepthMap = nullptr; + QImage *m_colorTextureImage = nullptr; bool m_newToonMapsComing = false; private: QOpenGLVertexArrayObject m_vaoTriangle; @@ -67,6 +69,7 @@ private: QMutex m_meshMutex; QMutex m_newMeshMutex; QMutex m_toonNormalAndDepthMapMutex; + QMutex m_colorTextureMutex; }; #endif diff --git a/src/modelwidget.cpp b/src/modelwidget.cpp index c30a660a..cd20953f 100644 --- a/src/modelwidget.cpp +++ b/src/modelwidget.cpp @@ -437,7 +437,7 @@ bool ModelWidget::inputWheelEventFromOtherWidget(QWheelEvent *event) if (m_mousePickingEnabled) { if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) { - emit addMouseRadius((float)event->delta() / 40 / height()); + emit addMouseRadius((float)event->delta() / 200 / height()); return true; } } @@ -504,6 +504,12 @@ void ModelWidget::updateMesh(Model *mesh) update(); } +void ModelWidget::updateColorTexture(QImage *colorTextureImage) +{ + m_meshBinder.updateColorTexture(colorTextureImage); + update(); +} + void ModelWidget::fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap) { m_meshBinder.fetchCurrentToonNormalAndDepthMaps(normalMap, depthMap); diff --git a/src/modelwidget.h b/src/modelwidget.h index cd47e022..9837ac78 100644 --- a/src/modelwidget.h +++ b/src/modelwidget.h @@ -42,6 +42,7 @@ public: } Model *fetchCurrentMesh(); void updateMesh(Model *mesh); + void updateColorTexture(QImage *colorTextureImage); void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions); void toggleWireframe(); bool isWireframeVisible(); diff --git a/src/motioneditwidget.cpp b/src/motioneditwidget.cpp index 7bd597da..55714dcc 100644 --- a/src/motioneditwidget.cpp +++ b/src/motioneditwidget.cpp @@ -25,7 +25,7 @@ MotionEditWidget::~MotionEditWidget() } delete m_bones; delete m_rigWeights; - delete m_outcome; + delete m_object; } MotionEditWidget::MotionEditWidget() @@ -189,7 +189,7 @@ void MotionEditWidget::save() void MotionEditWidget::updateBones(RigType rigType, const std::vector *rigBones, const std::map *rigWeights, - const Outcome *outcome) + const Object *object) { m_rigType = rigType; @@ -199,15 +199,15 @@ void MotionEditWidget::updateBones(RigType rigType, delete m_rigWeights; m_rigWeights = nullptr; - delete m_outcome; - m_outcome = nullptr; + delete m_object; + m_object = nullptr; if (nullptr != rigBones && nullptr != rigWeights && - nullptr != outcome) { + nullptr != object) { m_bones = new std::vector(*rigBones); m_rigWeights = new std::map(*rigWeights); - m_outcome = new Outcome(*outcome); + m_object = new Object(*object); generatePreview(); } @@ -222,12 +222,12 @@ void MotionEditWidget::generatePreview() m_isPreviewObsolete = false; - if (RigType::None == m_rigType || nullptr == m_bones || nullptr == m_rigWeights || nullptr == m_outcome) + if (RigType::None == m_rigType || nullptr == m_bones || nullptr == m_rigWeights || nullptr == m_object) return; QThread *thread = new QThread; - m_previewGenerator = new MotionsGenerator(m_rigType, *m_bones, *m_rigWeights, *m_outcome); + m_previewGenerator = new MotionsGenerator(m_rigType, *m_bones, *m_rigWeights, *m_object); m_previewGenerator->enablePreviewMeshes(); m_previewGenerator->addMotion(QUuid(), m_parameters); m_previewGenerator->moveToThread(thread); diff --git a/src/motioneditwidget.h b/src/motioneditwidget.h index 9088b84f..def0e4e7 100644 --- a/src/motioneditwidget.h +++ b/src/motioneditwidget.h @@ -7,7 +7,7 @@ #include #include "vertebratamotion.h" #include "rigger.h" -#include "outcome.h" +#include "object.h" class SimpleShaderWidget; class MotionsGenerator; @@ -33,7 +33,7 @@ public slots: void updateBones(RigType rigType, const std::vector *rigBones, const std::map *rigWeights, - const Outcome *outcome); + const Object *object); void setEditMotionName(const QString &name); void setEditMotionId(const QUuid &motionId); void setEditMotionParameters(const std::map ¶meters); @@ -57,7 +57,7 @@ private: RigType m_rigType = RigType::None; std::vector *m_bones = nullptr; std::map *m_rigWeights = nullptr; - Outcome *m_outcome = nullptr; + Object *m_object = nullptr; QLineEdit *m_nameEdit = nullptr; bool m_unsaved = false; bool m_closed = false; diff --git a/src/motionmanagewidget.cpp b/src/motionmanagewidget.cpp index dd0ec1c4..c4fc4074 100644 --- a/src/motionmanagewidget.cpp +++ b/src/motionmanagewidget.cpp @@ -70,7 +70,7 @@ void MotionManageWidget::showMotionDialog(QUuid motionId) motionEditWidget->updateBones(m_document->rigType, m_document->resultRigBones(), m_document->resultRigWeights(), - &m_document->currentRiggedOutcome()); + &m_document->currentRiggedObject()); if (!motionId.isNull()) { const Motion *motion = m_document->findMotion(motionId); if (nullptr != motion) { diff --git a/src/motionsgenerator.cpp b/src/motionsgenerator.cpp index b36704a7..733a2360 100644 --- a/src/motionsgenerator.cpp +++ b/src/motionsgenerator.cpp @@ -12,11 +12,11 @@ MotionsGenerator::MotionsGenerator(RigType rigType, const std::vector &bones, const std::map &rigWeights, - const Outcome &outcome) : + const Object &object) : m_rigType(rigType), m_bones(bones), m_rigWeights(rigWeights), - m_outcome(outcome) + m_object(object) { } @@ -323,25 +323,25 @@ void MotionsGenerator::generateMotion(const QUuid &motionId) for (size_t i = 0; i < m_bones.size(); ++i) jointNodeMatrices[i] = jointNodeMatrices[i] * bindTransforms[i].inverted(); - std::vector transformedVertices(m_outcome.vertices.size()); - for (size_t i = 0; i < m_outcome.vertices.size(); ++i) { + std::vector transformedVertices(m_object.vertices.size()); + for (size_t i = 0; i < m_object.vertices.size(); ++i) { const auto &weight = m_rigWeights[i]; for (int x = 0; x < 4; x++) { float factor = weight.boneWeights[x]; if (factor > 0) { - transformedVertices[i] += jointNodeMatrices[weight.boneIndices[x]] * m_outcome.vertices[i] * factor; + transformedVertices[i] += jointNodeMatrices[weight.boneIndices[x]] * m_object.vertices[i] * factor; } } } std::vector frameVertices = transformedVertices; - std::vector> frameFaces = m_outcome.triangles; + std::vector> frameFaces = m_object.triangles; std::vector> frameCornerNormals; - const std::vector> *triangleVertexNormals = m_outcome.triangleVertexNormals(); + const std::vector> *triangleVertexNormals = m_object.triangleVertexNormals(); if (nullptr == triangleVertexNormals) { frameCornerNormals.resize(frameFaces.size()); - for (size_t i = 0; i < m_outcome.triangles.size(); ++i) { - const auto &triangle = m_outcome.triangles[i]; + for (size_t i = 0; i < m_object.triangles.size(); ++i) { + const auto &triangle = m_object.triangles[i]; QVector3D triangleNormal = QVector3D::normal( transformedVertices[triangle[0]], transformedVertices[triangle[1]], diff --git a/src/motionsgenerator.h b/src/motionsgenerator.h index 84317ff9..f2dcf566 100644 --- a/src/motionsgenerator.h +++ b/src/motionsgenerator.h @@ -17,7 +17,7 @@ public: MotionsGenerator(RigType rigType, const std::vector &bones, const std::map &rigWeights, - const Outcome &outcome); + const Object &object); ~MotionsGenerator(); void addMotion(const QUuid &motionId, const std::map ¶meters); Model *takeResultSnapshotMesh(const QUuid &motionId); @@ -37,7 +37,7 @@ private: RigType m_rigType = RigType::None; std::vector m_bones; std::map m_rigWeights; - Outcome m_outcome; + Object m_object; std::map> m_motions; std::set m_generatedMotionIds; std::map m_resultSnapshotMeshes; diff --git a/src/outcome.cpp b/src/object.cpp similarity index 94% rename from src/outcome.cpp rename to src/object.cpp index 5cdc98a0..aebcac01 100644 --- a/src/outcome.cpp +++ b/src/object.cpp @@ -1,6 +1,6 @@ -#include "outcome.h" +#include "object.h" -void Outcome::buildInterpolatedNodes(const std::vector &nodes, +void Object::buildInterpolatedNodes(const std::vector &nodes, const std::vector, std::pair>> &edges, std::vector> *targetNodes) { diff --git a/src/outcome.h b/src/object.h similarity index 82% rename from src/outcome.h rename to src/object.h index 59efb178..7b99ba2f 100644 --- a/src/outcome.h +++ b/src/object.h @@ -1,5 +1,5 @@ -#ifndef DUST3D_OUTCOME_H -#define DUST3D_OUTCOME_H +#ifndef DUST3D_OBJECT_H +#define DUST3D_OBJECT_H #include #include #include @@ -8,10 +8,11 @@ #include #include #include "bonemark.h" +#include "componentlayer.h" #define MAX_WEIGHT_NUM 4 -struct OutcomeNode +struct ObjectNode { QUuid partId; QUuid nodeId; @@ -25,50 +26,23 @@ struct OutcomeNode bool countershaded = false; QUuid mirrorFromPartId; QUuid mirroredByPartId; - BoneMark boneMark; + BoneMark boneMark = BoneMark::None; QVector3D direction; + ComponentLayer layer = ComponentLayer::Body; bool joined = true; }; -struct OutcomePaintNode -{ - int originNodeIndex; - QUuid originNodeId; - QVector3D origin; - float radius = 0; - QVector3D baseNormal; - QVector3D direction; - size_t order; - std::vector vertices; -}; - -struct OutcomePaintMap -{ - QUuid partId; - std::vector paintNodes; - - void clear() - { - paintNodes.clear(); - }; -}; - -class Outcome +class Object { public: - std::vector nodes; - std::vector bodyNodes; - std::vector clothNodes; + std::vector nodes; std::vector, std::pair>> edges; - std::vector, std::pair>> bodyEdges; - std::vector>> nodeVertices; std::vector vertices; std::vector> vertexSourceNodes; std::vector> triangleAndQuads; std::vector> triangles; std::vector triangleNormals; std::vector triangleColors; - std::vector paintMaps; quint64 meshId = 0; const std::vector> *triangleSourceNodes() const @@ -147,7 +121,7 @@ public: m_hasTriangleLinks = true; } - static void buildInterpolatedNodes(const std::vector &nodes, + static void buildInterpolatedNodes(const std::vector &nodes, const std::vector, std::pair>> &edges, std::vector> *targetNodes); private: diff --git a/src/objectxml.cpp b/src/objectxml.cpp new file mode 100644 index 00000000..012210cb --- /dev/null +++ b/src/objectxml.cpp @@ -0,0 +1,420 @@ +#include +#include +#include +#include +#include +#include "objectxml.h" +#include "util.h" + +void saveObjectToXmlStream(const Object *object, QXmlStreamWriter *writer) +{ + std::map, size_t> nodeIdMap; + for (size_t i = 0; i < object->nodes.size(); ++i) { + const auto &it = object->nodes[i]; + nodeIdMap.insert({{it.partId, it.nodeId}, i}); + } + + writer->setAutoFormatting(true); + writer->writeStartDocument(); + + writer->writeStartElement("object"); + writer->writeStartElement("nodes"); + for (const auto &node: object->nodes) { + writer->writeStartElement("node"); + writer->writeAttribute("partId", node.partId.toString()); + writer->writeAttribute("id", node.nodeId.toString()); + writer->writeAttribute("x", QString::number(node.origin.x())); + writer->writeAttribute("y", QString::number(node.origin.y())); + writer->writeAttribute("z", QString::number(node.origin.z())); + writer->writeAttribute("radius", QString::number(node.radius)); + writer->writeAttribute("color", node.color.name(QColor::HexArgb)); + writer->writeAttribute("colorSolubility", QString::number(node.colorSolubility)); + writer->writeAttribute("metallic", QString::number(node.metalness)); + writer->writeAttribute("roughness", QString::number(node.roughness)); + if (!node.materialId.isNull()) + writer->writeAttribute("materialId", node.materialId.toString()); + if (node.countershaded) + writer->writeAttribute("countershaded", "true"); + if (!node.mirrorFromPartId.isNull()) + writer->writeAttribute("mirrorFromPartId", node.mirrorFromPartId.toString()); + if (!node.mirroredByPartId.isNull()) + writer->writeAttribute("mirroredByPartId", node.mirroredByPartId.toString()); + if (node.boneMark != BoneMark::None) + writer->writeAttribute("boneMark", BoneMarkToString(node.boneMark)); + if (ComponentLayer::Body != node.layer) + writer->writeAttribute("layer", ComponentLayerToString(node.layer)); + if (!node.joined) + writer->writeAttribute("joined", "false"); + writer->writeEndElement(); + } + writer->writeEndElement(); + + writer->writeStartElement("edges"); + for (const auto &edge: object->edges) { + writer->writeStartElement("edge"); + writer->writeAttribute("fromPartId", edge.first.first.toString()); + writer->writeAttribute("fromNodeId", edge.first.second.toString()); + writer->writeAttribute("toPartId", edge.second.first.toString()); + writer->writeAttribute("toNodeId", edge.second.second.toString()); + writer->writeEndElement(); + } + writer->writeEndElement(); + + writer->writeStartElement("vertices"); + QStringList vertexList; + for (const auto &vertex: object->vertices) { + vertexList += QString::number(vertex.x()) + "," + QString::number(vertex.y()) + "," + QString::number(vertex.z()); + } + writer->writeCharacters(vertexList.join(" ")); + writer->writeEndElement(); + + writer->writeStartElement("vertexSourceNodes"); + QStringList vertexSourceNodeList; + for (const auto &it: object->vertexSourceNodes) { + auto findIndex = nodeIdMap.find(it); + if (findIndex == nodeIdMap.end()) { + vertexSourceNodeList += "-1"; + } else { + vertexSourceNodeList += QString::number(findIndex->second); + } + } + writer->writeCharacters(vertexSourceNodeList.join(" ")); + writer->writeEndElement(); + + writer->writeStartElement("triangleAndQuads"); + QStringList triangleAndQuadList; + for (const auto &it: object->triangleAndQuads) { + QStringList face; + for (const auto &index: it) + face += QString::number(index); + triangleAndQuadList += face.join(","); + } + writer->writeCharacters(triangleAndQuadList.join(" ")); + writer->writeEndElement(); + + writer->writeStartElement("triangles"); + QStringList triangleList; + for (const auto &it: object->triangles) { + QStringList face; + for (const auto &index: it) + face += QString::number(index); + triangleList += face.join(","); + } + writer->writeCharacters(triangleList.join(" ")); + writer->writeEndElement(); + + writer->writeStartElement("triangleNormals"); + QStringList triangleNormalList; + for (const auto &normal: object->triangleNormals) { + triangleNormalList += QString::number(normal.x()) + "," + QString::number(normal.y()) + "," + QString::number(normal.z()); + } + writer->writeCharacters(triangleNormalList.join(" ")); + writer->writeEndElement(); + + writer->writeStartElement("triangleColors"); + QStringList triangleColorList; + for (const auto &color: object->triangleColors) { + triangleColorList += color.name(QColor::HexArgb); + } + writer->writeCharacters(triangleColorList.join(" ")); + writer->writeEndElement(); + + const std::vector> *triangleSourceNodes = object->triangleSourceNodes(); + if (nullptr != triangleSourceNodes) { + writer->writeStartElement("triangleSourceNodes"); + QStringList triangleSourceNodeList; + for (const auto &it: *triangleSourceNodes) { + auto findIndex = nodeIdMap.find(it); + if (findIndex == nodeIdMap.end()) { + triangleSourceNodeList += "-1"; + } else { + triangleSourceNodeList += QString::number(findIndex->second); + } + } + writer->writeCharacters(triangleSourceNodeList.join(" ")); + writer->writeEndElement(); + } + + const std::vector> *triangleVertexUvs = object->triangleVertexUvs(); + if (nullptr != triangleVertexUvs) { + writer->writeStartElement("triangleVertexUvs"); + QStringList triangleVertexUvList; + for (const auto &triangleUvs: *triangleVertexUvs) { + for (const auto &uv: triangleUvs) { + triangleVertexUvList += QString::number(uv.x()) + "," + QString::number(uv.y()); + } + } + writer->writeCharacters(triangleVertexUvList.join(" ")); + writer->writeEndElement(); + } + + const std::vector> *triangleVertexNormals = object->triangleVertexNormals(); + if (nullptr != triangleVertexNormals) { + writer->writeStartElement("triangleVertexNormals"); + QStringList triangleVertexNormalList; + for (const auto &triangleNormals: *triangleVertexNormals) { + for (const auto &normal: triangleNormals) { + triangleVertexNormalList += QString::number(normal.x()) + "," + QString::number(normal.y()) + "," + QString::number(normal.z()); + } + } + writer->writeCharacters(triangleVertexNormalList.join(" ")); + writer->writeEndElement(); + } + + const std::vector *triangleTangents = object->triangleTangents(); + if (nullptr != triangleTangents) { + writer->writeStartElement("triangleTangents"); + QStringList triangleTangentList; + for (const auto &tangent: *triangleTangents) { + triangleTangentList += QString::number(tangent.x()) + "," + QString::number(tangent.y()) + "," + QString::number(tangent.z()); + } + writer->writeCharacters(triangleTangentList.join(" ")); + writer->writeEndElement(); + } + + const std::map> *partUvRects = object->partUvRects(); + if (nullptr != partUvRects) { + writer->writeStartElement("uvAreas"); + for (const auto &it: *partUvRects) { + for (const auto &rect: it.second) { + writer->writeStartElement("uvArea"); + writer->writeAttribute("partId", it.first.toString()); + writer->writeAttribute("left", QString::number(rect.left())); + writer->writeAttribute("top", QString::number(rect.top())); + writer->writeAttribute("width", QString::number(rect.width())); + writer->writeAttribute("height", QString::number(rect.height())); + writer->writeEndElement(); + } + } + writer->writeEndElement(); + } + + const std::vector, std::pair>> *triangleLinks = object->triangleLinks(); + if (nullptr != triangleLinks) { + writer->writeStartElement("triangleLinks"); + QStringList triangleLinkList; + for (const auto &link: *triangleLinks) { + triangleLinkList += QString::number(link.first.first) + "," + QString::number(link.first.second) + "," + QString::number(link.second.first) + "," + QString::number(link.second.second); + } + writer->writeCharacters(triangleLinkList.join(" ")); + writer->writeEndElement(); + } + + writer->writeEndElement(); + writer->writeEndDocument(); +} + +void loadObjectFromXmlStream(Object *object, QXmlStreamReader &reader) +{ + std::map> partUvRects; + std::vector elementNameStack; + while (!reader.atEnd()) { + reader.readNext(); + if (!reader.isStartElement() && !reader.isEndElement() && !reader.isCharacters()) { + if (!reader.name().toString().isEmpty()) + qDebug() << "Skip xml element:" << reader.name().toString() << " tokenType:" << reader.tokenType(); + continue; + } + QString baseName = reader.name().toString(); + if (reader.isStartElement()) + elementNameStack.push_back(baseName); + QStringList nameItems; + for (const auto &nameItem: elementNameStack) { + nameItems.append(nameItem); + } + QString fullName = nameItems.join("."); + if (reader.isEndElement()) + elementNameStack.pop_back(); + if (reader.isStartElement()) { + if (fullName == "object.nodes.node") { + QString nodeId = reader.attributes().value("id").toString(); + if (nodeId.isEmpty()) + continue; + ObjectNode node; + node.nodeId = QUuid(nodeId); + node.partId = QUuid(reader.attributes().value("partId").toString()); + node.origin.setX(reader.attributes().value("x").toFloat()); + node.origin.setY(reader.attributes().value("y").toFloat()); + node.origin.setZ(reader.attributes().value("z").toFloat()); + node.radius = reader.attributes().value("radius").toFloat(); + node.color = QColor(reader.attributes().value("color").toString()); + node.colorSolubility = reader.attributes().value("colorSolubility").toFloat(); + node.metalness = reader.attributes().value("metallic").toFloat(); + node.roughness = reader.attributes().value("roughness").toFloat(); + node.materialId = QUuid(reader.attributes().value("materialId").toString()); + node.countershaded = isTrueValueString(reader.attributes().value("countershaded").toString()); + node.mirrorFromPartId = QUuid(reader.attributes().value("mirrorFromPartId").toString()); + node.mirroredByPartId = QUuid(reader.attributes().value("mirroredByPartId").toString()); + node.boneMark = BoneMarkFromString(reader.attributes().value("boneMark").toString().toUtf8().constData()); + node.layer = ComponentLayerFromString(reader.attributes().value("layer").toString().toUtf8().constData()); + QString joinedString = reader.attributes().value("joined").toString(); + if (!joinedString.isEmpty()) + node.joined = isTrueValueString(joinedString); + object->nodes.push_back(node); + } else if (fullName == "object.edges.edge") { + std::pair, std::pair> edge; + edge.first.first = QUuid(reader.attributes().value("fromPartId").toString()); + edge.first.second = QUuid(reader.attributes().value("fromNodeId").toString()); + edge.second.first = QUuid(reader.attributes().value("toPartId").toString()); + edge.second.second = QUuid(reader.attributes().value("toNodeId").toString()); + object->edges.push_back(edge); + } else if (fullName == "object.uvAreas.uvArea") { + QUuid partId = QUuid(reader.attributes().value("partId").toString()); + if (!partId.isNull()) { + QRectF area(reader.attributes().value("left").toFloat(), + reader.attributes().value("top").toFloat(), + reader.attributes().value("width").toFloat(), + reader.attributes().value("height").toFloat()); + partUvRects[partId].push_back(area); + } + } + } else if (reader.isEndElement()) { + if (fullName.startsWith("object.uvAreas")) { + object->setPartUvRects(partUvRects); + } + } else if (reader.isCharacters()) { + if (fullName == "object.vertices") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + for (const auto &item: list) { + auto subItems = item.split(","); + if (3 != subItems.size()) + continue; + object->vertices.push_back({subItems[0].toFloat(), + subItems[1].toFloat(), + subItems[2].toFloat()}); + } + } else if (fullName == "object.vertexSourceNodes") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + for (const auto &item: list) { + int index = item.toInt(); + if (index < 0 || index >= object->nodes.size()) { + object->vertexSourceNodes.push_back({QUuid(), QUuid()}); + } else { + const auto &node = object->nodes[index]; + object->vertexSourceNodes.push_back({node.partId, node.nodeId}); + } + } + } else if (fullName == "object.triangleAndQuads") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + for (const auto &item: list) { + auto subItems = item.split(","); + if (3 == subItems.size()) { + object->triangleAndQuads.push_back({(size_t)subItems[0].toInt(), + (size_t)subItems[1].toInt(), + (size_t)subItems[2].toInt()}); + } else if (4 == subItems.size()) { + object->triangleAndQuads.push_back({(size_t)subItems[0].toInt(), + (size_t)subItems[1].toInt(), + (size_t)subItems[2].toInt(), + (size_t)subItems[3].toInt()}); + } + } + } else if (fullName == "object.triangles") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + for (const auto &item: list) { + auto subItems = item.split(","); + if (3 == subItems.size()) { + object->triangles.push_back({(size_t)subItems[0].toInt(), + (size_t)subItems[1].toInt(), + (size_t)subItems[2].toInt()}); + } + } + } else if (fullName == "object.triangleNormals") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + for (const auto &item: list) { + auto subItems = item.split(","); + if (3 != subItems.size()) + continue; + object->triangleNormals.push_back({subItems[0].toFloat(), + subItems[1].toFloat(), + subItems[2].toFloat()}); + } + } else if (fullName == "object.triangleColors") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + for (const auto &item: list) { + object->triangleColors.push_back(QColor(item)); + } + } else if (fullName == "object.triangleSourceNodes") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + std::vector> triangleSourceNodes; + for (const auto &item: list) { + int index = item.toInt(); + if (index < 0 || index >= object->nodes.size()) { + triangleSourceNodes.push_back({QUuid(), QUuid()}); + } else { + const auto &node = object->nodes[index]; + triangleSourceNodes.push_back({node.partId, node.nodeId}); + } + } + if (triangleSourceNodes.size() == object->triangles.size()) + object->setTriangleSourceNodes(triangleSourceNodes); + } else if (fullName == "object.triangleVertexUvs") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + std::vector uvs; + for (const auto &item: list) { + auto subItems = item.split(","); + if (2 != subItems.size()) + continue; + uvs.push_back({subItems[0].toFloat(), + subItems[1].toFloat()}); + } + std::vector> triangleVertexUvs; + if (0 == uvs.size() % 3) { + for (size_t i = 0; i < uvs.size(); i += 3) { + triangleVertexUvs.push_back({ + uvs[i], uvs[i + 1], uvs[i + 2] + }); + } + } + if (triangleVertexUvs.size() == object->triangles.size()) + object->setTriangleVertexUvs(triangleVertexUvs); + } else if (fullName == "object.triangleVertexNormals") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + std::vector normals; + for (const auto &item: list) { + auto subItems = item.split(","); + if (3 != subItems.size()) + continue; + normals.push_back({subItems[0].toFloat(), + subItems[1].toFloat(), + subItems[2].toFloat()}); + } + std::vector> triangleVertexNormals; + if (0 == normals.size() % 3) { + for (size_t i = 0; i < normals.size(); i += 3) { + triangleVertexNormals.push_back({ + normals[i], normals[i + 1], normals[i + 2] + }); + } + } + if (triangleVertexNormals.size() == object->triangles.size()) + object->setTriangleVertexNormals(triangleVertexNormals); + } else if (fullName == "object.triangleTangents") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + std::vector triangleTangents; + for (const auto &item: list) { + auto subItems = item.split(","); + if (3 != subItems.size()) + continue; + triangleTangents.push_back({subItems[0].toFloat(), + subItems[1].toFloat(), + subItems[2].toFloat()}); + } + if (triangleTangents.size() == object->triangles.size()) + object->setTriangleTangents(triangleTangents); + } else if (fullName == "object.triangleLinks") { + QStringList list = reader.text().toString().split(QRegExp("\\s+"), QString::SkipEmptyParts); + std::vector, std::pair>> triangleLinks; + for (const auto &item: list) { + auto subItems = item.split(","); + if (4 != subItems.size()) + continue; + triangleLinks.push_back({{(size_t)subItems[0].toInt(), (size_t)subItems[1].toInt()}, + {(size_t)subItems[2].toInt(), (size_t)subItems[3].toInt()}}); + } + if (triangleLinks.size() == object->triangles.size()) + object->setTriangleLinks(triangleLinks); + } + } + } +} diff --git a/src/objectxml.h b/src/objectxml.h new file mode 100644 index 00000000..73db5992 --- /dev/null +++ b/src/objectxml.h @@ -0,0 +1,9 @@ +#ifndef DUST3D_OBJECT_XML_H +#define DUST3D_OBJECT_XML_H +#include +#include "object.h" + +void saveObjectToXmlStream(const Object *object, QXmlStreamWriter *writer); +void loadObjectFromXmlStream(Object *object, QXmlStreamReader &reader); + +#endif diff --git a/src/preferences.cpp b/src/preferences.cpp index 77cee31f..5ae5a0dc 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -18,6 +18,7 @@ void Preferences::loadDefault() m_toonShading = false; m_toonLine = ToonLine::WithoutLine; m_textureSize = 1024; + m_scriptEnabled = false; } Preferences::Preferences() @@ -57,6 +58,13 @@ Preferences::Preferences() if (!value.isEmpty()) m_textureSize = value.toInt(); } + { + QString value = m_settings.value("scriptEnabled").toString(); + if (value.isEmpty()) + m_scriptEnabled = false; + else + m_scriptEnabled = isTrueValueString(value); + } } CombineMode Preferences::componentCombineMode() const @@ -74,6 +82,11 @@ bool Preferences::flatShading() const return m_flatShading; } +bool Preferences::scriptEnabled() const +{ + return m_scriptEnabled; +} + bool Preferences::toonShading() const { return m_toonShading; @@ -116,6 +129,15 @@ void Preferences::setFlatShading(bool flatShading) emit flatShadingChanged(); } +void Preferences::setScriptEnabled(bool enabled) +{ + if (m_scriptEnabled == enabled) + return; + m_scriptEnabled = enabled; + m_settings.setValue("scriptEnabled", enabled ? "true" : "false"); + emit scriptEnabledChanged(); +} + void Preferences::setToonShading(bool toonShading) { if (m_toonShading == toonShading) @@ -163,4 +185,5 @@ void Preferences::reset() emit toonShadingChanged(); emit toonLineChanged(); emit textureSizeChanged(); + emit scriptEnabledChanged(); } diff --git a/src/preferences.h b/src/preferences.h index 52e762ae..4810d91b 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -15,6 +15,7 @@ public: CombineMode componentCombineMode() const; const QColor &partColor() const; bool flatShading() const; + bool scriptEnabled() const; bool toonShading() const; ToonLine toonLine() const; QSize documentWindowSize() const; @@ -27,6 +28,7 @@ signals: void toonShadingChanged(); void toonLineChanged(); void textureSizeChanged(); + void scriptEnabledChanged(); public slots: void setComponentCombineMode(CombineMode mode); void setPartColor(const QColor &color); @@ -34,6 +36,7 @@ public slots: void setToonShading(bool toonShading); void setToonLine(ToonLine toonLine); void setTextureSize(int textureSize); + void setScriptEnabled(bool enabled); void reset(); private: CombineMode m_componentCombineMode; @@ -43,6 +46,7 @@ private: ToonLine m_toonLine; QSettings m_settings; int m_textureSize; + bool m_scriptEnabled; private: void loadDefault(); }; diff --git a/src/preferenceswidget.cpp b/src/preferenceswidget.cpp index efd8907b..52b5a375 100644 --- a/src/preferenceswidget.cpp +++ b/src/preferenceswidget.cpp @@ -94,12 +94,19 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent) Preferences::instance().setTextureSize(textureSizeSelectBox->itemText(index).toInt()); }); + QCheckBox *scriptEnabledBox = new QCheckBox(); + Theme::initCheckbox(scriptEnabledBox); + connect(scriptEnabledBox, &QCheckBox::stateChanged, this, [=]() { + Preferences::instance().setScriptEnabled(scriptEnabledBox->isChecked()); + }); + QFormLayout *formLayout = new QFormLayout; formLayout->addRow(tr("Part color:"), colorLayout); formLayout->addRow(tr("Combine mode:"), combineModeSelectBox); formLayout->addRow(tr("Flat shading:"), flatShadingBox); formLayout->addRow(tr("Toon shading:"), toonShadingLayout); formLayout->addRow(tr("Texture size:"), textureSizeSelectBox); + formLayout->addRow(tr("Script:"), scriptEnabledBox); auto loadFromPreferences = [=]() { updatePickButtonColor(); @@ -110,6 +117,7 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent) textureSizeSelectBox->setCurrentIndex( textureSizeSelectBox->findText(QString::number(Preferences::instance().textureSize())) ); + scriptEnabledBox->setChecked(Preferences::instance().scriptEnabled()); }; loadFromPreferences(); diff --git a/src/riggenerator.cpp b/src/riggenerator.cpp index a59860c5..c92f3cb5 100644 --- a/src/riggenerator.cpp +++ b/src/riggenerator.cpp @@ -15,7 +15,7 @@ class GroupEndpointsStitcher { public: - GroupEndpointsStitcher(const std::vector *nodes, + GroupEndpointsStitcher(const std::vector *nodes, const std::vector> *groups, const std::vector> *groupEndpoints, std::vector> *stitchResult) : @@ -53,31 +53,31 @@ public: } } private: - const std::vector *m_nodes = nullptr; + const std::vector *m_nodes = nullptr; const std::vector> *m_groups = nullptr; const std::vector> *m_groupEndpoints = nullptr; std::vector> *m_stitchResult = nullptr; }; -RigGenerator::RigGenerator(RigType rigType, const Outcome &outcome) : +RigGenerator::RigGenerator(RigType rigType, const Object &object) : m_rigType(rigType), - m_outcome(new Outcome(outcome)) + m_object(new Object(object)) { } RigGenerator::~RigGenerator() { - delete m_outcome; + delete m_object; delete m_resultMesh; delete m_resultBones; delete m_resultWeights; } -Outcome *RigGenerator::takeOutcome() +Object *RigGenerator::takeObject() { - Outcome *outcome = m_outcome; - m_outcome = nullptr; - return outcome; + Object *object = m_object; + m_object = nullptr; + return object; } std::vector *RigGenerator::takeResultBones() @@ -143,17 +143,19 @@ void RigGenerator::groupNodeIndices(const std::maptriangleSourceNodes()) + if (nullptr == m_object->triangleSourceNodes()) return; std::map, size_t> nodeIdToIndexMap; - for (size_t i = 0; i < m_outcome->bodyNodes.size(); ++i) { - const auto &node = m_outcome->bodyNodes[i]; + for (size_t i = 0; i < m_object->nodes.size(); ++i) { + const auto &node = m_object->nodes[i]; + if (ComponentLayer::Body != node.layer) + continue; nodeIdToIndexMap.insert({{node.partId, node.nodeId}, i}); m_neighborMap.insert({i, {}}); } - for (const auto &it: m_outcome->bodyEdges) { + for (const auto &it: m_object->edges) { const auto &findSource = nodeIdToIndexMap.find(it.first); if (findSource == nodeIdToIndexMap.end()) continue; @@ -185,16 +187,16 @@ void RigGenerator::buildNeighborMap() break; std::vector> stitchResult(groupEndpoints.size(), - {m_outcome->bodyNodes.size(), std::numeric_limits::max()}); + {m_object->nodes.size(), std::numeric_limits::max()}); tbb::parallel_for(tbb::blocked_range(0, groupEndpoints.size()), - GroupEndpointsStitcher(&m_outcome->bodyNodes, &groups, &groupEndpoints, + GroupEndpointsStitcher(&m_object->nodes, &groups, &groupEndpoints, &stitchResult)); auto minDistantMatch = std::min_element(stitchResult.begin(), stitchResult.end(), [&]( const std::pair &first, const std::pair &second) { return first.second < second.second; }); - if (minDistantMatch->first == m_outcome->bodyNodes.size()) + if (minDistantMatch->first == m_object->nodes.size()) break; const auto &fromNodeIndex = groupEndpoints[minDistantMatch - stitchResult.begin()].second; @@ -202,8 +204,8 @@ void RigGenerator::buildNeighborMap() m_neighborMap[fromNodeIndex].insert(toNodeIndex); m_neighborMap[toNodeIndex].insert(fromNodeIndex); - //const auto &fromNode = m_outcome->bodyNodes[fromNodeIndex]; - //const auto &toNode = m_outcome->bodyNodes[toNodeIndex]; + //const auto &fromNode = m_object->bodyNodes[fromNodeIndex]; + //const auto &toNode = m_object->bodyNodes[toNodeIndex]; //debugBoxes.push_back(std::make_tuple(fromNode.origin, toNode.origin, // fromNode.radius, toNode.radius, Qt::red)); } @@ -215,14 +217,16 @@ void RigGenerator::buildBoneNodeChain() { std::vector, bool>> segments; std::unordered_set middle; - size_t middleStartNodeIndex = m_outcome->bodyNodes.size(); - for (size_t nodeIndex = 0; nodeIndex < m_outcome->bodyNodes.size(); ++nodeIndex) { - const auto &node = m_outcome->bodyNodes[nodeIndex]; + size_t middleStartNodeIndex = m_object->nodes.size(); + for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) { + const auto &node = m_object->nodes[nodeIndex]; + if (ComponentLayer::Body != node.layer) + continue; if (!BoneMarkIsBranchNode(node.boneMark)) continue; m_branchNodesMapByMark[(int)node.boneMark].push_back(nodeIndex); if (BoneMark::Neck == node.boneMark) { - if (middleStartNodeIndex == m_outcome->bodyNodes.size()) + if (middleStartNodeIndex == m_object->nodes.size()) middleStartNodeIndex = nodeIndex; } else if (BoneMark::Tail == node.boneMark) { middleStartNodeIndex = nodeIndex; @@ -244,13 +248,13 @@ void RigGenerator::buildBoneNodeChain() middle.erase(nodeIndex); } middle.erase(middleStartNodeIndex); - if (middleStartNodeIndex != m_outcome->bodyNodes.size()) + if (middleStartNodeIndex != m_object->nodes.size()) segments.push_back(std::make_tuple(middleStartNodeIndex, middle, true)); for (const auto &it: segments) { const auto &fromNodeIndex = std::get<0>(it); const auto &left = std::get<1>(it); const auto &isSpine = std::get<2>(it); - const auto &fromNode = m_outcome->bodyNodes[fromNodeIndex]; + const auto &fromNode = m_object->nodes[fromNodeIndex]; std::vector> boneNodeIndices; std::unordered_set visited; size_t attachNodeIndex = fromNodeIndex; @@ -271,7 +275,7 @@ void RigGenerator::buildBoneNodeChain() } for (size_t i = 0; i < m_boneNodeChain.size(); ++i) { const auto &chain = m_boneNodeChain[i]; - const auto &node = m_outcome->bodyNodes[chain.fromNodeIndex]; + const auto &node = m_object->nodes[chain.fromNodeIndex]; const auto &isSpine = chain.isSpine; if (isSpine) { m_spineChains.push_back(i); @@ -299,7 +303,7 @@ void RigGenerator::calculateSpineDirection(bool *isVertical) float bottom = std::numeric_limits::max(); auto updateBoundingBox = [&](const std::vector &chains) { for (const auto &it: chains) { - const auto &node = m_outcome->bodyNodes[m_boneNodeChain[it].fromNodeIndex]; + const auto &node = m_object->nodes[m_boneNodeChain[it].fromNodeIndex]; if (node.origin.y() > top) top = node.origin.y(); if (node.origin.y() < bottom) @@ -324,8 +328,8 @@ void RigGenerator::attachLimbsToSpine() m_attachLimbsToSpineNodeIndices.resize(m_leftLimbChains.size()); for (size_t i = 0; i < m_leftLimbChains.size(); ++i) { - const auto &leftNode = m_outcome->bodyNodes[m_boneNodeChain[m_leftLimbChains[i]].attachNodeIndex]; - const auto &rightNode = m_outcome->bodyNodes[m_boneNodeChain[m_rightLimbChains[i]].attachNodeIndex]; + const auto &leftNode = m_object->nodes[m_boneNodeChain[m_leftLimbChains[i]].attachNodeIndex]; + const auto &rightNode = m_object->nodes[m_boneNodeChain[m_rightLimbChains[i]].attachNodeIndex]; auto limbMiddle = (leftNode.origin + rightNode.origin) * 0.5; std::vector> distance2WithSpine; auto boneNodeChainIndex = m_spineChains[0]; @@ -334,7 +338,7 @@ void RigGenerator::attachLimbsToSpine() for (const auto &nodeIndex: it) { distance2WithSpine.push_back({ nodeIndex, - (m_outcome->bodyNodes[nodeIndex].origin - limbMiddle).lengthSquared() + (m_object->nodes[nodeIndex].origin - limbMiddle).lengthSquared() }); } } @@ -387,11 +391,11 @@ void RigGenerator::buildSkeleton() std::sort(chains.begin(), chains.end(), [&](const size_t &first, const size_t &second) { if (m_isSpineVertical) { - return m_outcome->bodyNodes[m_boneNodeChain[first].fromNodeIndex].origin.y() < - m_outcome->bodyNodes[m_boneNodeChain[second].fromNodeIndex].origin.y(); + return m_object->nodes[m_boneNodeChain[first].fromNodeIndex].origin.y() < + m_object->nodes[m_boneNodeChain[second].fromNodeIndex].origin.y(); } - return m_outcome->bodyNodes[m_boneNodeChain[first].fromNodeIndex].origin.z() < - m_outcome->bodyNodes[m_boneNodeChain[second].fromNodeIndex].origin.z(); + return m_object->nodes[m_boneNodeChain[first].fromNodeIndex].origin.z() < + m_object->nodes[m_boneNodeChain[second].fromNodeIndex].origin.z(); }); }; sortLimbChains(m_leftLimbChains); @@ -408,7 +412,7 @@ void RigGenerator::buildSkeleton() m_resultWeights = new std::map; { - const auto &firstSpineNode = m_outcome->bodyNodes[m_spineJoints[m_rootSpineJointIndex]]; + const auto &firstSpineNode = m_object->nodes[m_spineJoints[m_rootSpineJointIndex]]; RiggerBone bone; bone.headPosition = QVector3D(0.0, 0.0, 0.0); bone.tailPosition = firstSpineNode.origin; @@ -426,8 +430,8 @@ void RigGenerator::buildSkeleton() for (size_t spineJointIndex = m_rootSpineJointIndex; spineJointIndex + 1 < m_spineJoints.size(); ++spineJointIndex) { - const auto ¤tNode = m_outcome->bodyNodes[m_spineJoints[spineJointIndex]]; - const auto &nextNode = m_outcome->bodyNodes[m_spineJoints[spineJointIndex + 1]]; + const auto ¤tNode = m_object->nodes[m_spineJoints[spineJointIndex]]; + const auto &nextNode = m_object->nodes[m_spineJoints[spineJointIndex + 1]]; RiggerBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; @@ -447,8 +451,8 @@ void RigGenerator::buildSkeleton() const QString &chainPrefix) { QString chainName = chainPrefix + QString::number(limbIndex + 1); const auto &spineJointIndex = m_attachLimbsToSpineJointIndices[limbIndex]; - const auto &spineNode = m_outcome->bodyNodes[m_spineJoints[spineJointIndex]]; - const auto &limbFirstNode = m_outcome->bodyNodes[limbJoints[limbIndex][0]]; + const auto &spineNode = m_object->nodes[m_spineJoints[spineJointIndex]]; + const auto &limbFirstNode = m_object->nodes[limbJoints[limbIndex][0]]; const auto &parentIndex = attachedBoneIndex(spineJointIndex); RiggerBone bone; bone.headPosition = spineNode.origin; @@ -472,8 +476,8 @@ void RigGenerator::buildSkeleton() for (size_t limbJointIndex = 0; limbJointIndex + 1 < joints.size(); ++limbJointIndex) { - const auto ¤tNode = m_outcome->bodyNodes[joints[limbJointIndex]]; - const auto &nextNode = m_outcome->bodyNodes[joints[limbJointIndex + 1]]; + const auto ¤tNode = m_object->nodes[joints[limbJointIndex]]; + const auto &nextNode = m_object->nodes[joints[limbJointIndex + 1]]; RiggerBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; @@ -510,8 +514,8 @@ void RigGenerator::buildSkeleton() for (size_t neckJointIndex = 0; neckJointIndex + 1 < m_neckJoints.size(); ++neckJointIndex) { - const auto ¤tNode = m_outcome->bodyNodes[m_neckJoints[neckJointIndex]]; - const auto &nextNode = m_outcome->bodyNodes[m_neckJoints[neckJointIndex + 1]]; + const auto ¤tNode = m_object->nodes[m_neckJoints[neckJointIndex]]; + const auto &nextNode = m_object->nodes[m_neckJoints[neckJointIndex + 1]]; RiggerBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; @@ -540,10 +544,10 @@ void RigGenerator::buildSkeleton() --spineJointIndex) { if (m_spineJoints[spineJointIndex] == m_tailJoints[0]) break; - const auto ¤tNode = m_outcome->bodyNodes[m_spineJoints[spineJointIndex]]; + const auto ¤tNode = m_object->nodes[m_spineJoints[spineJointIndex]]; const auto &nextNode = spineJointIndex > 0 ? - m_outcome->bodyNodes[m_spineJoints[spineJointIndex - 1]] : - m_outcome->bodyNodes[m_tailJoints[0]]; + m_object->nodes[m_spineJoints[spineJointIndex - 1]] : + m_object->nodes[m_tailJoints[0]]; RiggerBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; @@ -568,8 +572,8 @@ void RigGenerator::buildSkeleton() for (size_t tailJointIndex = 0; tailJointIndex + 1 < m_tailJoints.size(); ++tailJointIndex) { - const auto ¤tNode = m_outcome->bodyNodes[m_tailJoints[tailJointIndex]]; - const auto &nextNode = m_outcome->bodyNodes[m_tailJoints[tailJointIndex + 1]]; + const auto ¤tNode = m_object->nodes[m_tailJoints[tailJointIndex]]; + const auto &nextNode = m_object->nodes[m_tailJoints[tailJointIndex + 1]]; RiggerBone bone; bone.headPosition = currentNode.origin; bone.tailPosition = nextNode.origin; @@ -649,26 +653,34 @@ void RigGenerator::computeSkinWeights() 1); std::map, size_t> nodeIdToIndexMap; - for (size_t nodeIndex = 0; nodeIndex < m_outcome->bodyNodes.size(); ++nodeIndex) { - const auto &node = m_outcome->bodyNodes[nodeIndex]; + for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) { + const auto &node = m_object->nodes[nodeIndex]; + if (ComponentLayer::Body != node.layer) + continue; nodeIdToIndexMap[{node.partId, node.nodeId}] = nodeIndex; } - if (!m_outcome->bodyNodes.empty()) { - for (size_t clothNodeIndex = 0; clothNodeIndex < m_outcome->clothNodes.size(); ++clothNodeIndex) { - const auto &clothNode = m_outcome->clothNodes[clothNodeIndex]; - std::vector> distance2s(m_outcome->bodyNodes.size()); - for (size_t nodeIndex = 0; nodeIndex < m_outcome->bodyNodes.size(); ++nodeIndex) { - distance2s[nodeIndex] = std::make_pair(nodeIndex, - (clothNode.origin - m_outcome->bodyNodes[nodeIndex].origin).lengthSquared()); + if (!nodeIdToIndexMap.empty()) { + for (size_t nonBodyNodeIndex = 0; nonBodyNodeIndex < m_object->nodes.size(); ++nonBodyNodeIndex) { + const auto &nonBodyNode = m_object->nodes[nonBodyNodeIndex]; + if (ComponentLayer::Body == nonBodyNode.layer) + continue; + std::vector> distance2s; + distance2s.reserve(m_object->nodes.size()); + for (size_t nodeIndex = 0; nodeIndex < m_object->nodes.size(); ++nodeIndex) { + const auto &node = m_object->nodes[nodeIndex]; + if (ComponentLayer::Body != node.layer) + continue; + distance2s.push_back(std::make_pair(nodeIndex, + (nonBodyNode.origin - node.origin).lengthSquared())); } - nodeIdToIndexMap[{clothNode.partId, clothNode.nodeId}] = std::min_element(distance2s.begin(), distance2s.end(), [](const std::pair &first, + nodeIdToIndexMap[{nonBodyNode.partId, nonBodyNode.nodeId}] = std::min_element(distance2s.begin(), distance2s.end(), [](const std::pair &first, const std::pair &second) { return first.second < second.second; })->first; } } - for (size_t vertexIndex = 0; vertexIndex < m_outcome->vertices.size(); ++vertexIndex) { - const auto &vertexSourceId = m_outcome->vertexSourceNodes[vertexIndex]; + for (size_t vertexIndex = 0; vertexIndex < m_object->vertices.size(); ++vertexIndex) { + const auto &vertexSourceId = m_object->vertexSourceNodes[vertexIndex]; auto findNodeIndex = nodeIdToIndexMap.find(vertexSourceId); if (findNodeIndex == nodeIdToIndexMap.end()) { vertexBranches[spineIndex].push_back(vertexIndex); @@ -737,10 +749,10 @@ void RigGenerator::computeSkinWeights() for (auto &it: *m_resultWeights) it.second.finalizeWeights(); - //for (size_t i = 0; i < m_outcome->vertices.size(); ++i) { + //for (size_t i = 0; i < m_object->vertices.size(); ++i) { // auto findWeights = m_resultWeights->find(i); // if (findWeights == m_resultWeights->end()) { - // const auto &sourceNode = m_outcome->vertexSourceNodes[i]; + // const auto &sourceNode = m_object->vertexSourceNodes[i]; // qDebug() << "NoWeight vertex index:" << i << sourceNode.first << sourceNode.second; // } //} @@ -863,20 +875,20 @@ void RigGenerator::fixVirtualBoneSkinWeights() float angleInRangle360BetweenTwoVectors(QVector3D a, QVector3D b, QVector3D planeNormal); for (const auto &vertexIndex: boneVerticesMap[it.parentIndex]) { - if (it.side != calculateSide(m_outcome->vertices[vertexIndex].x())) + if (it.side != calculateSide(m_object->vertices[vertexIndex].x())) continue; - QVector3D projectedPosition = projectPointOnLine(m_outcome->vertices[vertexIndex], bone.tailPosition, bone.headPosition); + QVector3D projectedPosition = projectPointOnLine(m_object->vertices[vertexIndex], bone.tailPosition, bone.headPosition); if ((projectedPosition - bone.tailPosition).length() > boneLength) continue; if (m_isSpineVertical) { double angle = angleInRangle360BetweenTwoVectors((boundaryLineHeadForParentOnX - boundaryLineTailForParentOnX).normalized(), - (m_outcome->vertices[vertexIndex] - boundaryLineTailForParentOnX).normalized(), + (m_object->vertices[vertexIndex] - boundaryLineTailForParentOnX).normalized(), QVector3D(0.0, 0.0, -it.side)); if (angle > 180) continue; } else { double angle = angleInRangle360BetweenTwoVectors((boundaryLineHeadForParentOnZ - boundaryLineTailForParentOnZ).normalized(), - (m_outcome->vertices[vertexIndex] - boundaryLineTailForParentOnZ).normalized(), + (m_object->vertices[vertexIndex] - boundaryLineTailForParentOnZ).normalized(), QVector3D(1.0, 0.0, 0.0)); if (angle > 180) continue; @@ -884,19 +896,19 @@ void RigGenerator::fixVirtualBoneSkinWeights() (*m_resultWeights)[vertexIndex].addBone(it.index, 1.0); } for (const auto &vertexIndex: boneVerticesMap[it.parentNextIndex]) { - if (it.side != calculateSide(m_outcome->vertices[vertexIndex].x())) + if (it.side != calculateSide(m_object->vertices[vertexIndex].x())) continue; - QVector3D projectedPosition = projectPointOnLine(m_outcome->vertices[vertexIndex], bone.tailPosition, bone.headPosition); + QVector3D projectedPosition = projectPointOnLine(m_object->vertices[vertexIndex], bone.tailPosition, bone.headPosition); if ((projectedPosition - bone.tailPosition).length() > boneLength) continue; if (m_isSpineVertical) { - double angle = angleInRangle360BetweenTwoVectors((m_outcome->vertices[vertexIndex] - boundaryLineTailForParentNextOnX).normalized(), + double angle = angleInRangle360BetweenTwoVectors((m_object->vertices[vertexIndex] - boundaryLineTailForParentNextOnX).normalized(), (boundaryLineHeadForParentNextOnX - boundaryLineTailForParentNextOnX).normalized(), QVector3D(0.0, 0.0, -it.side)); if (angle > 180) continue; } else { - double angle = angleInRangle360BetweenTwoVectors((m_outcome->vertices[vertexIndex] - boundaryLineTailForParentNextOnZ).normalized(), + double angle = angleInRangle360BetweenTwoVectors((m_object->vertices[vertexIndex] - boundaryLineTailForParentNextOnZ).normalized(), (boundaryLineHeadForParentNextOnZ - boundaryLineTailForParentNextOnZ).normalized(), QVector3D(1.0, 0.0, 0.0)); if (angle > 180) @@ -930,7 +942,7 @@ void RigGenerator::computeBranchSkinWeights(size_t fromBoneIndex, auto parentLength = (parentBone.tailPosition - parentBone.headPosition).length(); auto previousBoneIndex = /*currentBone.name.startsWith("Virtual") ? parentBone.parent : */currentBone.parent; for (const auto &vertexIndex: remainVertexIndices) { - const auto &position = m_outcome->vertices[vertexIndex]; + const auto &position = m_object->vertices[vertexIndex]; auto direction = (position - currentBone.headPosition).normalized(); if (QVector3D::dotProduct(direction, cutNormal) > 0) { float angle = radianBetweenVectors(direction, currentDirection); @@ -1001,11 +1013,11 @@ void RigGenerator::extractJoints(const size_t &fromNodeIndex, (*joints)[joints->size() - 1] != fromNodeIndex) { joints->push_back(fromNodeIndex); } - const auto &fromNode = m_outcome->bodyNodes[fromNodeIndex]; + const auto &fromNode = m_object->nodes[fromNodeIndex]; std::vector> nodeIndicesAndDistance2Array; for (const auto &it: nodeIndices) { for (const auto &nodeIndex: it) { - const auto &node = m_outcome->bodyNodes[nodeIndex]; + const auto &node = m_object->nodes[nodeIndex]; nodeIndicesAndDistance2Array.push_back({ nodeIndex, (fromNode.origin - node.origin).lengthSquared() @@ -1022,7 +1034,7 @@ void RigGenerator::extractJoints(const size_t &fromNodeIndex, std::vector jointIndices; for (size_t i = 0; i < nodeIndicesAndDistance2Array.size(); ++i) { const auto &item = nodeIndicesAndDistance2Array[i]; - const auto &node = m_outcome->bodyNodes[item.first]; + const auto &node = m_object->nodes[item.first]; if (BoneMark::None != node.boneMark || m_virtualJoints.find(item.first) != m_virtualJoints.end()) { jointIndices.push_back(i); @@ -1173,7 +1185,7 @@ void RigGenerator::buildDemoMesh() { // Blend vertices colors according to bone weights - std::vector inputVerticesColors(m_outcome->vertices.size(), Qt::black); + std::vector inputVerticesColors(m_object->vertices.size(), Qt::black); if (m_isSuccessful) { const auto &resultWeights = *m_resultWeights; const auto &resultBones = *m_resultBones; @@ -1202,18 +1214,18 @@ void RigGenerator::buildDemoMesh() // Create mesh for demo - const std::vector *triangleTangents = m_outcome->triangleTangents(); - const auto &inputVerticesPositions = m_outcome->vertices; - const std::vector> *triangleVertexNormals = m_outcome->triangleVertexNormals(); + const std::vector *triangleTangents = m_object->triangleTangents(); + const auto &inputVerticesPositions = m_object->vertices; + const std::vector> *triangleVertexNormals = m_object->triangleVertexNormals(); ShaderVertex *triangleVertices = nullptr; int triangleVerticesNum = 0; if (m_isSuccessful) { - triangleVertices = new ShaderVertex[m_outcome->triangles.size() * 3]; + triangleVertices = new ShaderVertex[m_object->triangles.size() * 3]; const QVector3D defaultUv = QVector3D(0, 0, 0); const QVector3D defaultTangents = QVector3D(0, 0, 0); - for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); triangleIndex++) { - const auto &sourceTriangle = m_outcome->triangles[triangleIndex]; + for (size_t triangleIndex = 0; triangleIndex < m_object->triangles.size(); triangleIndex++) { + const auto &sourceTriangle = m_object->triangles[triangleIndex]; const auto *sourceTangent = &defaultTangents; if (nullptr != triangleTangents) sourceTangent = &(*triangleTangents)[triangleIndex]; diff --git a/src/riggenerator.h b/src/riggenerator.h index adafec68..1b06fe06 100644 --- a/src/riggenerator.h +++ b/src/riggenerator.h @@ -4,7 +4,7 @@ #include #include #include -#include "outcome.h" +#include "object.h" #include "model.h" #include "rigger.h" #include "rigtype.h" @@ -13,13 +13,13 @@ class RigGenerator : public QObject { Q_OBJECT public: - RigGenerator(RigType rigType, const Outcome &outcome); + RigGenerator(RigType rigType, const Object &object); ~RigGenerator(); Model *takeResultMesh(); std::vector *takeResultBones(); std::map *takeResultWeights(); const std::vector> &messages(); - Outcome *takeOutcome(); + Object *takeObject(); bool isSuccessful(); void generate(); signals: @@ -36,7 +36,7 @@ private: }; RigType m_rigType = RigType::None; - Outcome *m_outcome = nullptr; + Object *m_object = nullptr; Model *m_resultMesh = nullptr; std::vector *m_resultBones = nullptr; std::map *m_resultWeights = nullptr; diff --git a/src/shortcuts.cpp b/src/shortcuts.cpp index 6c60d3b9..5c08cb47 100644 --- a/src/shortcuts.cpp +++ b/src/shortcuts.cpp @@ -27,7 +27,7 @@ void initShortCuts(QWidget *widget, SkeletonGraphicsWidget *graphicsWidget) defineKey(Qt::CTRL + Qt::Key_C, &SkeletonGraphicsWidget::shortcutCopy); defineKey(Qt::CTRL + Qt::Key_V, &SkeletonGraphicsWidget::shortcutPaste); defineKey(Qt::Key_S, &SkeletonGraphicsWidget::shortcutSelectMode); - //defineKey(Qt::Key_D, &SkeletonGraphicsWidget::shortcutPaintMode); + defineKey(Qt::Key_D, &SkeletonGraphicsWidget::shortcutPaintMode); defineKey(Qt::ALT + Qt::Key_Minus, &SkeletonGraphicsWidget::shortcutZoomRenderedModelByMinus10); defineKey(Qt::Key_Minus, &SkeletonGraphicsWidget::shortcutZoomSelectedByMinus1); defineKey(Qt::ALT + Qt::Key_Equal, &SkeletonGraphicsWidget::shortcutZoomRenderedModelBy10); diff --git a/src/simulateclothmeshes.cpp b/src/simulateclothmeshes.cpp index 4df1c264..f0faf744 100644 --- a/src/simulateclothmeshes.cpp +++ b/src/simulateclothmeshes.cpp @@ -21,7 +21,7 @@ public: const auto &filteredClothFaces = clothMesh->faces; std::map> positionMap; std::pair defaultSource; - for (const auto &it: *clothMesh->outcomeNodeVertices) { + for (const auto &it: *clothMesh->objectNodeVertices) { if (!it.second.first.isNull()) defaultSource.first = it.second.first; positionMap.insert({PositionKey(it.first), it.second}); diff --git a/src/simulateclothmeshes.h b/src/simulateclothmeshes.h index e3ca98ad..e0db0958 100644 --- a/src/simulateclothmeshes.h +++ b/src/simulateclothmeshes.h @@ -10,7 +10,7 @@ struct ClothMesh std::vector vertices; std::vector> faces; std::vector> vertexSources; - const std::vector>> *outcomeNodeVertices; + const std::vector>> *objectNodeVertices; ClothForce clothForce; float clothOffset; float clothStiffness; diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp index d9bcccb6..e4390e75 100644 --- a/src/skeletongraphicswidget.cpp +++ b/src/skeletongraphicswidget.cpp @@ -792,7 +792,7 @@ void SkeletonGraphicsWidget::updateCursor() setCursor(QCursor(replacedPixmap, Theme::toolIconFontSize / 5, Theme::toolIconFontSize * 4 / 5)); } break; case SkeletonDocumentEditMode::Paint: - setCursor(QCursor(Theme::awesome()->icon(fa::paintbrush).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize))); + setCursor(QCursor(Theme::awesome()->icon(fa::paintbrush).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize * 0.2, Theme::toolIconFontSize * 0.8)); break; case SkeletonDocumentEditMode::Drag: setCursor(QCursor(Theme::awesome()->icon(m_dragStarted ? fa::handrocko : fa::handpapero).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize))); diff --git a/src/skinnedmeshcreator.cpp b/src/skinnedmeshcreator.cpp index 2bf54bb6..9b798ca4 100644 --- a/src/skinnedmeshcreator.cpp +++ b/src/skinnedmeshcreator.cpp @@ -1,20 +1,20 @@ #include "skinnedmeshcreator.h" #include "theme.h" -SkinnedMeshCreator::SkinnedMeshCreator(const Outcome &outcome, +SkinnedMeshCreator::SkinnedMeshCreator(const Object &object, const std::map &resultWeights) : - m_outcome(outcome), + m_object(object), m_resultWeights(resultWeights) { - m_verticesOldIndices.resize(m_outcome.triangles.size()); - m_verticesBindNormals.resize(m_outcome.triangles.size()); - m_verticesBindPositions.resize(m_outcome.triangles.size()); - const std::vector> *triangleVertexNormals = m_outcome.triangleVertexNormals(); - for (size_t triangleIndex = 0; triangleIndex < m_outcome.triangles.size(); triangleIndex++) { + m_verticesOldIndices.resize(m_object.triangles.size()); + m_verticesBindNormals.resize(m_object.triangles.size()); + m_verticesBindPositions.resize(m_object.triangles.size()); + const std::vector> *triangleVertexNormals = m_object.triangleVertexNormals(); + for (size_t triangleIndex = 0; triangleIndex < m_object.triangles.size(); triangleIndex++) { for (int j = 0; j < 3; j++) { - int oldIndex = m_outcome.triangles[triangleIndex][j]; + int oldIndex = m_object.triangles[triangleIndex][j]; m_verticesOldIndices[triangleIndex].push_back(oldIndex); - m_verticesBindPositions[triangleIndex].push_back(m_outcome.vertices[oldIndex]); + m_verticesBindPositions[triangleIndex].push_back(m_object.vertices[oldIndex]); if (nullptr != triangleVertexNormals) m_verticesBindNormals[triangleIndex].push_back((*triangleVertexNormals)[triangleIndex][j]); else @@ -23,13 +23,13 @@ SkinnedMeshCreator::SkinnedMeshCreator(const Outcome &outcome, } std::map, QColor> sourceNodeToColorMap; - for (const auto &node: outcome.nodes) + for (const auto &node: object.nodes) sourceNodeToColorMap.insert({{node.partId, node.nodeId}, node.color}); - m_triangleColors.resize(m_outcome.triangles.size(), Theme::white); - const std::vector> *triangleSourceNodes = outcome.triangleSourceNodes(); + m_triangleColors.resize(m_object.triangles.size(), Theme::white); + const std::vector> *triangleSourceNodes = object.triangleSourceNodes(); if (nullptr != triangleSourceNodes) { - for (size_t triangleIndex = 0; triangleIndex < m_outcome.triangles.size(); triangleIndex++) { + for (size_t triangleIndex = 0; triangleIndex < m_object.triangles.size(); triangleIndex++) { const auto &source = (*triangleSourceNodes)[triangleIndex]; m_triangleColors[triangleIndex] = sourceNodeToColorMap[source]; } @@ -59,9 +59,9 @@ Model *SkinnedMeshCreator::createMeshFromTransform(const std::vector } } - ShaderVertex *triangleVertices = new ShaderVertex[m_outcome.triangles.size() * 3]; + ShaderVertex *triangleVertices = new ShaderVertex[m_object.triangles.size() * 3]; int triangleVerticesNum = 0; - for (size_t triangleIndex = 0; triangleIndex < m_outcome.triangles.size(); triangleIndex++) { + for (size_t triangleIndex = 0; triangleIndex < m_object.triangles.size(); triangleIndex++) { for (int i = 0; i < 3; i++) { ShaderVertex ¤tVertex = triangleVertices[triangleVerticesNum++]; const auto &sourcePosition = transformedPositions[triangleIndex][i]; diff --git a/src/skinnedmeshcreator.h b/src/skinnedmeshcreator.h index b3346d2f..77aa6f80 100644 --- a/src/skinnedmeshcreator.h +++ b/src/skinnedmeshcreator.h @@ -5,17 +5,17 @@ #include #include #include "model.h" -#include "outcome.h" +#include "object.h" #include "jointnodetree.h" class SkinnedMeshCreator { public: - SkinnedMeshCreator(const Outcome &outcome, + SkinnedMeshCreator(const Object &object, const std::map &resultWeights); Model *createMeshFromTransform(const std::vector &matricies); private: - Outcome m_outcome; + Object m_object; std::map m_resultWeights; std::vector> m_verticesOldIndices; std::vector> m_verticesBindPositions; diff --git a/src/texturegenerator.cpp b/src/texturegenerator.cpp index e79e7562..23ed47fb 100644 --- a/src/texturegenerator.cpp +++ b/src/texturegenerator.cpp @@ -13,13 +13,9 @@ QColor TextureGenerator::m_defaultTextureColor = Qt::transparent; -TextureGenerator::TextureGenerator(const Outcome &outcome, Snapshot *snapshot) : - m_resultTextureGuideImage(nullptr), - m_resultTextureImage(nullptr), - m_resultTextureBorderImage(nullptr), +TextureGenerator::TextureGenerator(const Object &object, Snapshot *snapshot) : m_resultTextureColorImage(nullptr), m_resultTextureNormalImage(nullptr), - m_resultTextureMetalnessRoughnessAmbientOcclusionImage(nullptr), m_resultTextureRoughnessImage(nullptr), m_resultTextureMetalnessImage(nullptr), m_resultTextureAmbientOcclusionImage(nullptr), @@ -28,21 +24,17 @@ TextureGenerator::TextureGenerator(const Outcome &outcome, Snapshot *snapshot) : m_hasTransparencySettings(false), m_textureSize(Preferences::instance().textureSize()) { - m_outcome = new Outcome(); - *m_outcome = outcome; + m_object = new Object(); + *m_object = object; if (m_textureSize <= 0) m_textureSize = 1024; } TextureGenerator::~TextureGenerator() { - delete m_outcome; - delete m_resultTextureGuideImage; - delete m_resultTextureImage; - delete m_resultTextureBorderImage; + delete m_object; delete m_resultTextureColorImage; delete m_resultTextureNormalImage; - delete m_resultTextureMetalnessRoughnessAmbientOcclusionImage; delete m_resultTextureRoughnessImage; delete m_resultTextureMetalnessImage; delete m_resultTextureAmbientOcclusionImage; @@ -50,27 +42,6 @@ TextureGenerator::~TextureGenerator() delete m_snapshot; } -QImage *TextureGenerator::takeResultTextureGuideImage() -{ - QImage *resultTextureGuideImage = m_resultTextureGuideImage; - m_resultTextureGuideImage = nullptr; - return resultTextureGuideImage; -} - -QImage *TextureGenerator::takeResultTextureImage() -{ - QImage *resultTextureImage = m_resultTextureImage; - m_resultTextureImage = nullptr; - return resultTextureImage; -} - -QImage *TextureGenerator::takeResultTextureBorderImage() -{ - QImage *resultTextureBorderImage = m_resultTextureBorderImage; - m_resultTextureBorderImage = nullptr; - return resultTextureBorderImage; -} - QImage *TextureGenerator::takeResultTextureColorImage() { QImage *resultTextureColorImage = m_resultTextureColorImage; @@ -85,13 +56,6 @@ QImage *TextureGenerator::takeResultTextureNormalImage() return resultTextureNormalImage; } -QImage *TextureGenerator::takeResultTextureMetalnessRoughnessAmbientOcclusionImage() -{ - QImage *resultTextureMetalnessRoughnessAmbientOcclusionImage = m_resultTextureMetalnessRoughnessAmbientOcclusionImage; - m_resultTextureMetalnessRoughnessAmbientOcclusionImage = nullptr; - return resultTextureMetalnessRoughnessAmbientOcclusionImage; -} - QImage *TextureGenerator::takeResultTextureRoughnessImage() { QImage *resultTextureRoughnessImage = m_resultTextureRoughnessImage; @@ -113,11 +77,11 @@ QImage *TextureGenerator::takeResultTextureAmbientOcclusionImage() return resultTextureAmbientOcclusionImage; } -Outcome *TextureGenerator::takeOutcome() +Object *TextureGenerator::takeObject() { - Outcome *outcome = m_outcome; - m_resultTextureImage = nullptr; - return outcome; + Object *object = m_object; + m_object = nullptr; + return object; } Model *TextureGenerator::takeResultMesh() @@ -179,7 +143,7 @@ void TextureGenerator::prepare() updatedCountershadedMap.insert({partId, isTrueValueString(valueOfKeyInMapOrEmpty(partIt.second, "countershaded"))}); } - for (const auto &bmeshNode: m_outcome->nodes) { + for (const auto &bmeshNode: m_object->nodes) { bool countershaded = bmeshNode.countershaded; auto findUpdatedCountershadedMap = updatedCountershadedMap.find(bmeshNode.mirrorFromPartId.isNull() ? bmeshNode.partId : bmeshNode.mirrorFromPartId); @@ -221,13 +185,13 @@ bool TextureGenerator::hasTransparencySettings() void TextureGenerator::generate() { - m_resultMesh = new Model(*m_outcome); + m_resultMesh = new Model(*m_object); - if (nullptr == m_outcome->triangleVertexUvs()) + if (nullptr == m_object->triangleVertexUvs()) return; - if (nullptr == m_outcome->triangleSourceNodes()) + if (nullptr == m_object->triangleSourceNodes()) return; - if (nullptr == m_outcome->partUvRects()) + if (nullptr == m_object->partUvRects()) return; QElapsedTimer countTimeConsumed; @@ -240,17 +204,17 @@ void TextureGenerator::generate() bool hasRoughnessMap = false; bool hasAmbientOcclusionMap = false; - const auto &triangleVertexUvs = *m_outcome->triangleVertexUvs(); - const auto &triangleSourceNodes = *m_outcome->triangleSourceNodes(); - const auto &partUvRects = *m_outcome->partUvRects(); - const auto &triangleNormals = m_outcome->triangleNormals; + const auto &triangleVertexUvs = *m_object->triangleVertexUvs(); + const auto &triangleSourceNodes = *m_object->triangleSourceNodes(); + const auto &partUvRects = *m_object->partUvRects(); + const auto &triangleNormals = m_object->triangleNormals; std::map partColorMap; - std::map, const OutcomeNode *> nodeMap; + std::map, const ObjectNode *> nodeMap; std::map partColorSolubilityMap; std::map partMetalnessMap; std::map partRoughnessMap; - for (const auto &item: m_outcome->nodes) { + for (const auto &item: m_object->nodes) { if (!m_hasTransparencySettings) { if (!qFuzzyCompare(1.0, item.color.alphaF())) m_hasTransparencySettings = true; @@ -267,15 +231,9 @@ void TextureGenerator::generate() m_resultTextureColorImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32); m_resultTextureColorImage->fill(m_hasTransparencySettings ? m_defaultTextureColor : Qt::white); - m_resultTextureBorderImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32); - m_resultTextureBorderImage->fill(Qt::transparent); - m_resultTextureNormalImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32); m_resultTextureNormalImage->fill(QColor(128, 128, 255)); - m_resultTextureMetalnessRoughnessAmbientOcclusionImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32); - m_resultTextureMetalnessRoughnessAmbientOcclusionImage->fill(QColor(255, 255, 0)); - m_resultTextureMetalnessImage = new QImage(TextureGenerator::m_textureSize, TextureGenerator::m_textureSize, QImage::Format_ARGB32); m_resultTextureMetalnessImage->fill(Qt::black); @@ -295,11 +253,6 @@ void TextureGenerator::generate() texturePainter.setRenderHint(QPainter::Antialiasing); texturePainter.setRenderHint(QPainter::HighQualityAntialiasing); - QPainter textureBorderPainter; - textureBorderPainter.begin(m_resultTextureBorderImage); - textureBorderPainter.setRenderHint(QPainter::Antialiasing); - textureBorderPainter.setRenderHint(QPainter::HighQualityAntialiasing); - QPainter textureNormalPainter; textureNormalPainter.begin(m_resultTextureNormalImage); textureNormalPainter.setRenderHint(QPainter::Antialiasing); @@ -555,8 +508,8 @@ void TextureGenerator::generate() }; std::map, std::tuple> halfEdgeToTriangleMap; - for (size_t i = 0; i < m_outcome->triangles.size(); ++i) { - const auto &triangleIndices = m_outcome->triangles[i]; + for (size_t i = 0; i < m_object->triangles.size(); ++i) { + const auto &triangleIndices = m_object->triangles[i]; if (triangleIndices.size() != 3) { qDebug() << "Found invalid triangle indices"; continue; @@ -582,7 +535,7 @@ void TextureGenerator::generate() // Draw belly white texturePainter.setCompositionMode(QPainter::CompositionMode_SoftLight); - for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); ++triangleIndex) { + for (size_t triangleIndex = 0; triangleIndex < m_object->triangles.size(); ++triangleIndex) { const auto &normal = triangleNormals[triangleIndex]; const std::pair &source = triangleSourceNodes[triangleIndex]; const auto &partId = source.first; @@ -595,11 +548,11 @@ void TextureGenerator::generate() continue; } - const auto &findOutcomeNode = nodeMap.find(source); - if (findOutcomeNode == nodeMap.end()) + const auto &findObjectNode = nodeMap.find(source); + if (findObjectNode == nodeMap.end()) continue; - const OutcomeNode *outcomeNode = findOutcomeNode->second; - if (qAbs(QVector3D::dotProduct(outcomeNode->direction, QVector3D(0, 1, 0))) >= 0.707) { + const ObjectNode *objectNode = findObjectNode->second; + if (qAbs(QVector3D::dotProduct(objectNode->direction, QVector3D(0, 1, 0))) >= 0.707) { if (QVector3D::dotProduct(normal, QVector3D(0, 0, 1)) <= 0.0) continue; } else { @@ -607,7 +560,7 @@ void TextureGenerator::generate() continue; } - const auto &triangleIndices = m_outcome->triangles[triangleIndex]; + const auto &triangleIndices = m_object->triangles[triangleIndex]; if (triangleIndices.size() != 3) { qDebug() << "Found invalid triangle indices"; continue; @@ -690,23 +643,8 @@ void TextureGenerator::generate() hasAmbientOcclusionMap = !m_partAmbientOcclusionTextureMap.empty(); auto paintTextureEndTime = countTimeConsumed.elapsed(); - - pen.setWidth(0); - textureBorderPainter.setPen(pen); - auto paintBorderBeginTime = countTimeConsumed.elapsed(); - for (auto i = 0u; i < triangleVertexUvs.size(); i++) { - const std::vector &uv = triangleVertexUvs[i]; - for (auto j = 0; j < 3; j++) { - int from = j; - int to = (j + 1) % 3; - textureBorderPainter.drawLine(uv[from][0] * TextureGenerator::m_textureSize, uv[from][1] * TextureGenerator::m_textureSize, - uv[to][0] * TextureGenerator::m_textureSize, uv[to][1] * TextureGenerator::m_textureSize); - } - } - auto paintBorderEndTime = countTimeConsumed.elapsed(); - + texturePainter.end(); - textureBorderPainter.end(); textureNormalPainter.end(); textureMetalnessPainter.end(); textureRoughnessPainter.end(); @@ -717,63 +655,26 @@ void TextureGenerator::generate() m_resultTextureNormalImage = nullptr; } - auto mergeMetalnessRoughnessAmbientOcclusionBeginTime = countTimeConsumed.elapsed(); if (!hasMetalnessMap && !hasRoughnessMap && !hasAmbientOcclusionMap) { - delete m_resultTextureMetalnessRoughnessAmbientOcclusionImage; - m_resultTextureMetalnessRoughnessAmbientOcclusionImage = nullptr; - } else { - for (int row = 0; row < m_resultTextureMetalnessRoughnessAmbientOcclusionImage->height(); ++row) { - for (int col = 0; col < m_resultTextureMetalnessRoughnessAmbientOcclusionImage->width(); ++col) { - QColor color(255, 255, 0); - if (hasMetalnessMap) - color.setBlue(qGray(m_resultTextureMetalnessImage->pixel(col, row))); - if (hasRoughnessMap) - color.setGreen(qGray(m_resultTextureRoughnessImage->pixel(col, row))); - if (hasAmbientOcclusionMap) - color.setRed(qGray(m_resultTextureAmbientOcclusionImage->pixel(col, row))); - m_resultTextureMetalnessRoughnessAmbientOcclusionImage->setPixelColor(col, row, color); - } - } - if (!hasMetalnessMap) { - delete m_resultTextureMetalnessImage; - m_resultTextureMetalnessImage = nullptr; - } - if (!hasRoughnessMap) { - delete m_resultTextureRoughnessImage; - m_resultTextureRoughnessImage = nullptr; - } - if (!hasAmbientOcclusionMap) { - delete m_resultTextureAmbientOcclusionImage; - m_resultTextureAmbientOcclusionImage = nullptr; - } - } - auto mergeMetalnessRoughnessAmbientOcclusionEndTime = countTimeConsumed.elapsed(); - - m_resultTextureImage = new QImage(*m_resultTextureColorImage); - - QImage uvCheckImage(":/resources/checkuv.png"); - - m_resultTextureGuideImage = new QImage(*m_resultTextureImage); - { - QPainter mergeTextureGuidePainter(m_resultTextureGuideImage); - mergeTextureGuidePainter.setCompositionMode(QPainter::CompositionMode_DestinationOver); - mergeTextureGuidePainter.drawImage(0, 0, uvCheckImage); - mergeTextureGuidePainter.end(); - } - - { - QPainter mergeTextureGuidePainter(m_resultTextureGuideImage); - mergeTextureGuidePainter.setCompositionMode(QPainter::CompositionMode_Multiply); - mergeTextureGuidePainter.drawImage(0, 0, *m_resultTextureBorderImage); - mergeTextureGuidePainter.end(); + delete m_resultTextureMetalnessImage; + m_resultTextureMetalnessImage = nullptr; + + delete m_resultTextureRoughnessImage; + m_resultTextureRoughnessImage = nullptr; + + delete m_resultTextureAmbientOcclusionImage; + m_resultTextureAmbientOcclusionImage = nullptr; } auto createResultBeginTime = countTimeConsumed.elapsed(); - m_resultMesh->setTextureImage(new QImage(*m_resultTextureImage)); + m_resultMesh->setTextureImage(new QImage(*m_resultTextureColorImage)); if (nullptr != m_resultTextureNormalImage) m_resultMesh->setNormalMapImage(new QImage(*m_resultTextureNormalImage)); - if (nullptr != m_resultTextureMetalnessRoughnessAmbientOcclusionImage) { - m_resultMesh->setMetalnessRoughnessAmbientOcclusionImage(new QImage(*m_resultTextureMetalnessRoughnessAmbientOcclusionImage)); + if (hasMetalnessMap || hasRoughnessMap || hasAmbientOcclusionMap) { + m_resultMesh->setMetalnessRoughnessAmbientOcclusionImage(combineMetalnessRoughnessAmbientOcclusionImages( + m_resultTextureMetalnessImage, + m_resultTextureRoughnessImage, + m_resultTextureAmbientOcclusionImage)); m_resultMesh->setHasMetalnessInImage(hasMetalnessMap); m_resultMesh->setHasRoughnessInImage(hasRoughnessMap); m_resultMesh->setHasAmbientOcclusionInImage(hasAmbientOcclusionMap); @@ -781,11 +682,41 @@ void TextureGenerator::generate() auto createResultEndTime = countTimeConsumed.elapsed(); qDebug() << "The texture[" << TextureGenerator::m_textureSize << "x" << TextureGenerator::m_textureSize << "] generation took" << countTimeConsumed.elapsed() << "milliseconds"; - qDebug() << " :create image took" << (createImageEndTime - createImageBeginTime) << "milliseconds"; - qDebug() << " :paint texture took" << (paintTextureEndTime - paintTextureBeginTime) << "milliseconds"; - qDebug() << " :paint border took" << (paintBorderEndTime - paintBorderBeginTime) << "milliseconds"; - qDebug() << " :merge metalness, roughness, and ambient occlusion texture took" << (mergeMetalnessRoughnessAmbientOcclusionEndTime - mergeMetalnessRoughnessAmbientOcclusionBeginTime) << "milliseconds"; - qDebug() << " :create result took" << (createResultEndTime - createResultBeginTime) << "milliseconds"; +} + +QImage *TextureGenerator::combineMetalnessRoughnessAmbientOcclusionImages(QImage *metalnessImage, + QImage *roughnessImage, + QImage *ambientOcclusionImage) +{ + QImage *textureMetalnessRoughnessAmbientOcclusionImage = nullptr; + if (nullptr != metalnessImage || + nullptr != roughnessImage || + nullptr != ambientOcclusionImage) { + int textureSize = 0; + if (nullptr != metalnessImage) + textureSize = metalnessImage->height(); + if (nullptr != roughnessImage) + textureSize = roughnessImage->height(); + if (nullptr != ambientOcclusionImage) + textureSize = ambientOcclusionImage->height(); + if (textureSize > 0) { + textureMetalnessRoughnessAmbientOcclusionImage = new QImage(textureSize, textureSize, QImage::Format_ARGB32); + textureMetalnessRoughnessAmbientOcclusionImage->fill(QColor(255, 255, 0)); + for (int row = 0; row < textureMetalnessRoughnessAmbientOcclusionImage->height(); ++row) { + for (int col = 0; col < textureMetalnessRoughnessAmbientOcclusionImage->width(); ++col) { + QColor color(255, 255, 0); + if (nullptr != metalnessImage) + color.setBlue(qGray(metalnessImage->pixel(col, row))); + if (nullptr != roughnessImage) + color.setGreen(qGray(roughnessImage->pixel(col, row))); + if (nullptr != ambientOcclusionImage) + color.setRed(qGray(ambientOcclusionImage->pixel(col, row))); + textureMetalnessRoughnessAmbientOcclusionImage->setPixelColor(col, row, color); + } + } + } + } + return textureMetalnessRoughnessAmbientOcclusionImage; } void TextureGenerator::process() diff --git a/src/texturegenerator.h b/src/texturegenerator.h index 4b29a7cf..589f6a36 100644 --- a/src/texturegenerator.h +++ b/src/texturegenerator.h @@ -5,7 +5,7 @@ #include #include #include -#include "outcome.h" +#include "object.h" #include "model.h" #include "snapshot.h" @@ -13,18 +13,14 @@ class TextureGenerator : public QObject { Q_OBJECT public: - TextureGenerator(const Outcome &outcome, Snapshot *snapshot=nullptr); + TextureGenerator(const Object &object, Snapshot *snapshot=nullptr); ~TextureGenerator(); - QImage *takeResultTextureGuideImage(); - QImage *takeResultTextureImage(); - QImage *takeResultTextureBorderImage(); QImage *takeResultTextureColorImage(); QImage *takeResultTextureNormalImage(); - QImage *takeResultTextureMetalnessRoughnessAmbientOcclusionImage(); QImage *takeResultTextureRoughnessImage(); QImage *takeResultTextureMetalnessImage(); QImage *takeResultTextureAmbientOcclusionImage(); - Outcome *takeOutcome(); + Object *takeObject(); Model *takeResultMesh(); bool hasTransparencySettings(); void addPartColorMap(QUuid partId, const QImage *image, float tileScale); @@ -33,6 +29,9 @@ public: void addPartRoughnessMap(QUuid partId, const QImage *image, float tileScale); void addPartAmbientOcclusionMap(QUuid partId, const QImage *image, float tileScale); void generate(); + static QImage *combineMetalnessRoughnessAmbientOcclusionImages(QImage *metalnessImage, + QImage *roughnessImage, + QImage *ambientOcclusionImage); signals: void finished(); public slots: @@ -42,13 +41,9 @@ public: private: void prepare(); private: - Outcome *m_outcome; - QImage *m_resultTextureGuideImage; - QImage *m_resultTextureImage; - QImage *m_resultTextureBorderImage; + Object *m_object; QImage *m_resultTextureColorImage; QImage *m_resultTextureNormalImage; - QImage *m_resultTextureMetalnessRoughnessAmbientOcclusionImage; QImage *m_resultTextureRoughnessImage; QImage *m_resultTextureMetalnessImage; QImage *m_resultTextureAmbientOcclusionImage; diff --git a/src/texturepainter.cpp b/src/texturepainter.cpp new file mode 100644 index 00000000..9472e2a4 --- /dev/null +++ b/src/texturepainter.cpp @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include "texturepainter.h" +#include "util.h" + +TexturePainter::TexturePainter(const QVector3D &mouseRayNear, const QVector3D &mouseRayFar) : + m_mouseRayNear(mouseRayNear), + m_mouseRayFar(mouseRayFar) +{ +} + +void TexturePainter::setContext(TexturePainterContext *context) +{ + m_context = context; +} + +TexturePainter::~TexturePainter() +{ + delete m_colorImage; +} + +void TexturePainter::setPaintMode(PaintMode paintMode) +{ + m_paintMode = paintMode; +} + +void TexturePainter::setMaskNodeIds(const std::set &nodeIds) +{ + m_mousePickMaskNodeIds = nodeIds; +} + +void TexturePainter::setRadius(float radius) +{ + m_radius = radius; +} + +void TexturePainter::setBrushColor(const QColor &color) +{ + m_brushColor = color; +} + +QImage *TexturePainter::takeColorImage() +{ + QImage *colorImage = m_colorImage; + m_colorImage = nullptr; + return colorImage; +} + +/* +void TexturePainter::buildFaceAroundVertexMap() +{ + if (nullptr != m_context->faceAroundVertexMap) + return; + + m_context->faceAroundVertexMap = new std::unordered_map>; + for (size_t triangleIndex = 0; + triangleIndex < m_context->object->triangles.size(); + ++triangleIndex) { + for (const auto &it: m_context->object->triangles[triangleIndex]) + (*m_context->faceAroundVertexMap)[it].insert(triangleIndex); + } +} + +void TexturePainter::collectNearbyTriangles(size_t triangleIndex, std::unordered_set *triangleIndices) +{ + for (const auto &vertex: m_context->object->triangles[triangleIndex]) + for (const auto &it: (*m_context->faceAroundVertexMap)[vertex]) + triangleIndices->insert(it); +} +*/ + +void TexturePainter::paintStroke(QPainter &painter, const TexturePainterStroke &stroke) +{ + size_t targetTriangleIndex = 0; + if (!intersectRayAndPolyhedron(stroke.mouseRayNear, + stroke.mouseRayFar, + m_context->object->vertices, + m_context->object->triangles, + m_context->object->triangleNormals, + &m_targetPosition, + &targetTriangleIndex)) { + return; + } + + if (PaintMode::None == m_paintMode) + return; + + if (nullptr == m_context->colorImage) { + qDebug() << "TexturePainter paint color image is null"; + return; + } + + const std::vector> *uvs = m_context->object->triangleVertexUvs(); + if (nullptr == uvs) { + qDebug() << "TexturePainter paint uvs is null"; + return; + } + + const std::vector> *sourceNodes = m_context->object->triangleSourceNodes(); + if (nullptr == sourceNodes) { + qDebug() << "TexturePainter paint source nodes is null"; + return; + } + + const std::map> *uvRects = m_context->object->partUvRects(); + if (nullptr == uvRects) + return; + + const auto &triangle = m_context->object->triangles[targetTriangleIndex]; + QVector3D coordinates = barycentricCoordinates(m_context->object->vertices[triangle[0]], + m_context->object->vertices[triangle[1]], + m_context->object->vertices[triangle[2]], + m_targetPosition); + + double triangleArea = areaOfTriangle(m_context->object->vertices[triangle[0]], + m_context->object->vertices[triangle[1]], + m_context->object->vertices[triangle[2]]); + + auto &uvCoords = (*uvs)[targetTriangleIndex]; + QVector2D target2d = uvCoords[0] * coordinates[0] + + uvCoords[1] * coordinates[1] + + uvCoords[2] * coordinates[2]; + + double uvArea = areaOfTriangle(QVector3D(uvCoords[0].x(), uvCoords[0].y(), 0.0), + QVector3D(uvCoords[1].x(), uvCoords[1].y(), 0.0), + QVector3D(uvCoords[2].x(), uvCoords[2].y(), 0.0)); + + double radiusFactor = std::sqrt(uvArea) / std::sqrt(triangleArea); + + //QPolygon polygon; + //polygon << QPoint(uvCoords[0].x() * m_context->colorImage->height(), uvCoords[0].y() * m_context->colorImage->height()) << + // QPoint(uvCoords[1].x() * m_context->colorImage->height(), uvCoords[1].y() * m_context->colorImage->height()) << + // QPoint(uvCoords[2].x() * m_context->colorImage->height(), uvCoords[2].y() * m_context->colorImage->height()); + //QRegion clipRegion(polygon); + //painter.setClipRegion(clipRegion); + + std::vector rects; + const auto &sourceNode = (*sourceNodes)[targetTriangleIndex]; + auto findRects = uvRects->find(sourceNode.first); + const int paddingSize = 2; + if (findRects != uvRects->end()) { + for (const auto &it: findRects->second) { + if (!it.contains({target2d.x(), target2d.y()})) + continue; + rects.push_back(QRect(it.left() * m_context->colorImage->height() - paddingSize, + it.top() * m_context->colorImage->height() - paddingSize, + it.width() * m_context->colorImage->height() + paddingSize + paddingSize, + it.height() * m_context->colorImage->height() + paddingSize + paddingSize)); + break; + } + } + QRegion clipRegion; + if (!rects.empty()) { + std::sort(rects.begin(), rects.end(), [](const QRect &first, const QRect &second) { + return first.top() < second.top(); + }); + clipRegion.setRects(&rects[0], rects.size()); + painter.setClipRegion(clipRegion); + } + + double radius = m_radius * radiusFactor * m_context->colorImage->height(); + QVector2D middlePoint = QVector2D(target2d.x() * m_context->colorImage->height(), + target2d.y() * m_context->colorImage->height()); + + QRadialGradient gradient(QPointF(middlePoint.x(), middlePoint.y()), radius); + gradient.setColorAt(0.0, m_brushColor); + gradient.setColorAt(1.0, Qt::transparent); + + painter.fillRect(middlePoint.x() - radius, + middlePoint.y() - radius, + radius + radius, + radius + radius, + gradient); +} + +void TexturePainter::paint() +{ + if (nullptr == m_context) { + qDebug() << "TexturePainter paint context is null"; + return; + } + + QPainter painter(m_context->colorImage); + painter.setPen(Qt::NoPen); + + TexturePainterStroke stroke = {m_mouseRayNear, m_mouseRayFar}; + paintStroke(painter, stroke); + + m_colorImage = new QImage(*m_context->colorImage); +} + +void TexturePainter::process() +{ + paint(); + + emit finished(); +} + +const QVector3D &TexturePainter::targetPosition() +{ + return m_targetPosition; +} diff --git a/src/texturepainter.h b/src/texturepainter.h new file mode 100644 index 00000000..0b22c6b6 --- /dev/null +++ b/src/texturepainter.h @@ -0,0 +1,73 @@ +#ifndef DUST3D_TEXTURE_PAINTER_H +#define DUST3D_TEXTURE_PAINTER_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "object.h" +#include "paintmode.h" +#include "model.h" + +struct TexturePainterStroke +{ + QVector3D mouseRayNear; + QVector3D mouseRayFar; +}; + +class TexturePainterContext +{ +public: + Object *object = nullptr; + QImage *colorImage = nullptr; + //std::unordered_map> *faceAroundVertexMap = nullptr; + + ~TexturePainterContext() + { + delete object; + delete colorImage; + } +}; + +class TexturePainter : public QObject +{ + Q_OBJECT +public: + TexturePainter(const QVector3D &mouseRayNear, const QVector3D &mouseRayFar); + void setContext(TexturePainterContext *context); + void setRadius(float radius); + void setBrushColor(const QColor &color); + void setPaintMode(PaintMode paintMode); + void setMaskNodeIds(const std::set &nodeIds); + + QImage *takeColorImage(); + + ~TexturePainter(); + const QVector3D &targetPosition(); +signals: + void finished(); +public slots: + void process(); + void paint(); +private: + float m_radius = 0.0; + PaintMode m_paintMode = PaintMode::None; + std::set m_mousePickMaskNodeIds; + QVector3D m_mouseRayNear; + QVector3D m_mouseRayFar; + QVector3D m_targetPosition; + QColor m_brushColor; + TexturePainterContext *m_context = nullptr; + QImage *m_colorImage = nullptr; + + //void buildFaceAroundVertexMap(); + //void collectNearbyTriangles(size_t triangleIndex, std::unordered_set *triangleIndices); + void paintStroke(QPainter &painter, const TexturePainterStroke &stroke); +}; + +#endif diff --git a/src/trianglesourcenoderesolve.cpp b/src/trianglesourcenoderesolve.cpp index 048ec423..721254f8 100644 --- a/src/trianglesourcenoderesolve.cpp +++ b/src/trianglesourcenoderesolve.cpp @@ -17,13 +17,13 @@ struct CandidateEdge float length; }; -static void fixRemainVertexSourceNodes(const Outcome &outcome, std::vector> &triangleSourceNodes, +static void fixRemainVertexSourceNodes(const Object &object, std::vector> &triangleSourceNodes, std::vector> *vertexSourceNodes) { if (nullptr != vertexSourceNodes) { std::map, size_t>> remainVertexSourcesMap; - for (size_t faceIndex = 0; faceIndex < outcome.triangles.size(); ++faceIndex) { - for (const auto &vertexIndex: outcome.triangles[faceIndex]) { + for (size_t faceIndex = 0; faceIndex < object.triangles.size(); ++faceIndex) { + for (const auto &vertexIndex: object.triangles[faceIndex]) { if (!(*vertexSourceNodes)[vertexIndex].second.isNull()) continue; remainVertexSourcesMap[vertexIndex][triangleSourceNodes[faceIndex]]++; @@ -39,20 +39,22 @@ static void fixRemainVertexSourceNodes(const Outcome &outcome, std::vector> &triangleSourceNodes, +void triangleSourceNodeResolve(const Object &object, + const std::vector>> &nodeVertices, + std::vector> &triangleSourceNodes, std::vector> *vertexSourceNodes) { std::map> vertexSourceMap; std::map> positionMap; std::map, HalfColorEdge> halfColorEdgeMap; std::set brokenTriangleSet; - for (const auto &it: outcome.nodeVertices) { + for (const auto &it: nodeVertices) { positionMap.insert({PositionKey(it.first), it.second}); } if (nullptr != vertexSourceNodes) - vertexSourceNodes->resize(outcome.vertices.size()); - for (auto x = 0u; x < outcome.vertices.size(); x++) { - const QVector3D *resultVertex = &outcome.vertices[x]; + vertexSourceNodes->resize(object.vertices.size()); + for (auto x = 0u; x < object.vertices.size(); x++) { + const QVector3D *resultVertex = &object.vertices[x]; std::pair source; auto findPosition = positionMap.find(PositionKey(*resultVertex)); if (findPosition != positionMap.end()) { @@ -60,8 +62,8 @@ void triangleSourceNodeResolve(const Outcome &outcome, std::vectorsecond; } } - for (auto x = 0u; x < outcome.triangles.size(); x++) { - const auto triangle = outcome.triangles[x]; + for (auto x = 0u; x < object.triangles.size(); x++) { + const auto triangle = object.triangles[x]; std::vector, int>> colorTypes; for (int i = 0; i < 3; i++) { int index = triangle[i]; @@ -112,7 +114,7 @@ void triangleSourceNodeResolve(const Outcome &outcome, std::vector, int> brokenTriangleMapByEdge; std::vector candidateEdges; for (const auto &x: brokenTriangleSet) { - const auto triangle = outcome.triangles[x]; + const auto triangle = object.triangles[x]; for (int i = 0; i < 3; i++) { int oppositeStartIndex = triangle[(i + 1) % 3]; int oppositeStopIndex = triangle[i]; @@ -123,11 +125,11 @@ void triangleSourceNodeResolve(const Outcome &outcome, std::vectorsecond.cornVertexIndex]; // D + QVector3D oppositeCornPosition = object.vertices[findOpposite->second.cornVertexIndex]; // D QVector3D AB = selfPositions[1] - selfPositions[0]; float length = AB.length(); QVector3D AC = selfPositions[2] - selfPositions[0]; @@ -151,7 +153,7 @@ void triangleSourceNodeResolve(const Outcome &outcome, std::vector bool { @@ -178,7 +180,7 @@ void triangleSourceNodeResolve(const Outcome &outcome, std::vector> &triangleSourceNodes, +void triangleSourceNodeResolve(const Object &object, + const std::vector>> &nodeVertices, + std::vector> &triangleSourceNodes, std::vector> *vertexSourceNodes=nullptr); #endif diff --git a/src/triangletangentresolve.cpp b/src/triangletangentresolve.cpp index 157b4073..8507db9c 100644 --- a/src/triangletangentresolve.cpp +++ b/src/triangletangentresolve.cpp @@ -1,25 +1,25 @@ #include #include "triangletangentresolve.h" -void triangleTangentResolve(const Outcome &outcome, std::vector &tangents) +void triangleTangentResolve(const Object &object, std::vector &tangents) { - tangents.resize(outcome.triangles.size()); + tangents.resize(object.triangles.size()); - if (nullptr == outcome.triangleVertexUvs()) + if (nullptr == object.triangleVertexUvs()) return; - const std::vector> &triangleVertexUvs = *outcome.triangleVertexUvs(); + const std::vector> &triangleVertexUvs = *object.triangleVertexUvs(); - for (decltype(outcome.triangles.size()) i = 0; i < outcome.triangles.size(); i++) { + for (decltype(object.triangles.size()) i = 0; i < object.triangles.size(); i++) { tangents[i] = {0, 0, 0}; const auto &uv = triangleVertexUvs[i]; QVector2D uv1 = {uv[0][0], uv[0][1]}; QVector2D uv2 = {uv[1][0], uv[1][1]}; QVector2D uv3 = {uv[2][0], uv[2][1]}; - const auto &triangle = outcome.triangles[i]; - const QVector3D &pos1 = outcome.vertices[triangle[0]]; - const QVector3D &pos2 = outcome.vertices[triangle[1]]; - const QVector3D &pos3 = outcome.vertices[triangle[2]]; + const auto &triangle = object.triangles[i]; + const QVector3D &pos1 = object.vertices[triangle[0]]; + const QVector3D &pos2 = object.vertices[triangle[1]]; + const QVector3D &pos3 = object.vertices[triangle[2]]; QVector3D edge1 = pos2 - pos1; QVector3D edge2 = pos3 - pos1; QVector2D deltaUv1 = uv2 - uv1; diff --git a/src/triangletangentresolve.h b/src/triangletangentresolve.h index 15e237b2..76035296 100644 --- a/src/triangletangentresolve.h +++ b/src/triangletangentresolve.h @@ -1,7 +1,7 @@ #ifndef DUST3D_TRIANGLE_TANGENT_RESOLVE_H #define DUST3D_TRIANGLE_TANGENT_RESOLVE_H -#include "outcome.h" +#include "object.h" -void triangleTangentResolve(const Outcome &outcome, std::vector &tangents); +void triangleTangentResolve(const Object &object, std::vector &tangents); #endif diff --git a/src/util.cpp b/src/util.cpp index 58127c59..d900017e 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -561,7 +561,8 @@ bool intersectRayAndPolyhedron(const QVector3D &rayNear, const std::vector &vertices, const std::vector> &triangles, const std::vector &triangleNormals, - QVector3D *intersection) + QVector3D *intersection, + size_t *intersectedTriangleIndex) { bool foundPosition = false; auto ray = (rayNear - rayFar).normalized(); @@ -585,6 +586,8 @@ bool intersectRayAndPolyhedron(const QVector3D &rayNear, if (distance2 < minDistance2) { if (nullptr != intersection) *intersection = point; + if (nullptr != intersectedTriangleIndex) + *intersectedTriangleIndex = i; minDistance2 = distance2; foundPosition = true; } @@ -593,4 +596,13 @@ bool intersectRayAndPolyhedron(const QVector3D &rayNear, return foundPosition; } - +QVector3D barycentricCoordinates(const QVector3D &a, const QVector3D &b, const QVector3D &c, + const QVector3D &point) +{ + auto invertedAreaOfAbc = 1.0 / areaOfTriangle(a, b, c); + auto areaOfPbc = areaOfTriangle(point, b, c); + auto areaOfPca = areaOfTriangle(point, c, a); + auto alpha = areaOfPbc * invertedAreaOfAbc; + auto beta = areaOfPca * invertedAreaOfAbc; + return QVector3D(alpha, beta, 1.0 - alpha - beta); +} diff --git a/src/util.h b/src/util.h index c511525c..27bf13d8 100644 --- a/src/util.h +++ b/src/util.h @@ -59,6 +59,9 @@ bool intersectRayAndPolyhedron(const QVector3D &rayNear, const std::vector &vertices, const std::vector> &triangles, const std::vector &triangleNormals, - QVector3D *intersection=nullptr); + QVector3D *intersection=nullptr, + size_t *intersectedTriangleIndex=nullptr); +QVector3D barycentricCoordinates(const QVector3D &a, const QVector3D &b, const QVector3D &c, + const QVector3D &point); #endif diff --git a/src/uvunwrap.cpp b/src/uvunwrap.cpp index 3151c698..b1805e0f 100644 --- a/src/uvunwrap.cpp +++ b/src/uvunwrap.cpp @@ -3,22 +3,22 @@ #include #include "uvunwrap.h" -void uvUnwrap(const Outcome &outcome, +void uvUnwrap(const Object &object, std::vector> &triangleVertexUvs, std::set &seamVertices, std::map> &uvRects) { - const auto &choosenVertices = outcome.vertices; - const auto &choosenTriangles = outcome.triangles; - const auto &choosenTriangleNormals = outcome.triangleNormals; + const auto &choosenVertices = object.vertices; + const auto &choosenTriangles = object.triangles; + const auto &choosenTriangleNormals = object.triangleNormals; triangleVertexUvs.resize(choosenTriangles.size(), { QVector2D(), QVector2D(), QVector2D() }); - if (nullptr == outcome.triangleSourceNodes()) + if (nullptr == object.triangleSourceNodes()) return; - const std::vector> &triangleSourceNodes = *outcome.triangleSourceNodes(); + const std::vector> &triangleSourceNodes = *object.triangleSourceNodes(); simpleuv::Mesh inputMesh; for (const auto &vertex: choosenVertices) { diff --git a/src/uvunwrap.h b/src/uvunwrap.h index c1f19b98..633fc9b2 100644 --- a/src/uvunwrap.h +++ b/src/uvunwrap.h @@ -2,9 +2,9 @@ #define DUST3D_UV_UNWRAP_H #include #include -#include "outcome.h" +#include "object.h" -void uvUnwrap(const Outcome &outcome, +void uvUnwrap(const Object &object, std::vector> &triangleVertexUvs, std::set &seamVertices, std::map> &uvRects); diff --git a/src/vertexcolorpainter.cpp b/src/vertexcolorpainter.cpp deleted file mode 100644 index e5842ca1..00000000 --- a/src/vertexcolorpainter.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "vertexcolorpainter.h" -#include "util.h" -#include "imageforever.h" - -const int VertexColorPainter::m_gridSize = 4096; - -PaintColor operator+(const PaintColor &first, const PaintColor &second) -{ - float total = first.alphaF() + second.alphaF(); - if (qFuzzyIsNull(total)) - return PaintColor(255, 255, 255, 255); - float remaining = second.alphaF() / total; - float rate = 1.0 - remaining; - PaintColor color(first.red() * rate + second.red() * remaining, - first.green() * rate + second.green() * remaining, - first.blue() * rate + second.blue() * remaining); - color.metalness = first.metalness * rate + second.metalness * remaining; - color.roughness = first.roughness * rate + second.roughness * remaining; - return color; -} - -PaintColor operator-(const PaintColor &first, const PaintColor &second) -{ - PaintColor color = first; - color.setAlphaF(std::max(color.alphaF() - second.alphaF(), 0.0)); - return color; -} - -VertexColorPainter::VertexColorPainter(const Outcome &m_outcome, const QVector3D &mouseRayNear, const QVector3D &mouseRayFar) : - m_outcome(m_outcome), - m_mouseRayNear(mouseRayNear), - m_mouseRayFar(mouseRayFar) -{ -} - -Model *VertexColorPainter::takePaintedModel() -{ - Model *paintedModel = m_model; - m_model = nullptr; - return paintedModel; -} - -void VertexColorPainter::setVoxelGrid(VoxelGrid *voxelGrid) -{ - m_voxelGrid = voxelGrid; - m_voxelGrid->setNullValue(PaintColor(255, 255, 255, 255)); -} - -void VertexColorPainter::setPaintMode(PaintMode paintMode) -{ - m_paintMode = paintMode; -} - -void VertexColorPainter::setMaskNodeIds(const std::set &nodeIds) -{ - m_mousePickMaskNodeIds = nodeIds; -} - -void VertexColorPainter::setRadius(float radius) -{ - m_radius = radius; -} - -void VertexColorPainter::setBrushColor(const QColor &color) -{ - m_brushColor = color; -} - -void VertexColorPainter::setBrushMetalness(float value) -{ - m_brushMetalness = value; -} - -void VertexColorPainter::setBrushRoughness(float value) -{ - m_brushRoughness = value; -} - -VertexColorPainter::~VertexColorPainter() -{ - delete m_model; -} - -bool VertexColorPainter::calculateMouseModelPosition(QVector3D &mouseModelPosition) -{ - return intersectRayAndPolyhedron(m_mouseRayNear, - m_mouseRayFar, - m_outcome.vertices, - m_outcome.triangles, - m_outcome.triangleNormals, - &mouseModelPosition); -} - -void VertexColorPainter::paintToVoxelGrid() -{ - int voxelX = toVoxelLength(m_targetPosition.x()); - int voxelY = toVoxelLength(m_targetPosition.y()); - int voxelZ = toVoxelLength(m_targetPosition.z()); - int voxelRadius = toVoxelLength(m_radius); - int range2 = voxelRadius * voxelRadius; - PaintColor paintColor(m_brushColor); - paintColor.metalness = m_brushMetalness; - paintColor.roughness = m_brushRoughness; - m_voxelGrid->add(voxelX, voxelY, voxelZ, paintColor); - for (int i = -voxelRadius; i <= voxelRadius; ++i) { - qint8 x = voxelX + i; - int i2 = i * i; - for (int j = -voxelRadius; j <= voxelRadius; ++j) { - qint8 y = voxelY + j; - int j2 = j * j; - for (int k = -voxelRadius; k <= voxelRadius; ++k) { - qint8 z = voxelZ + k; - int k2 = k * k; - int dist2 = i2 + j2 + k2; - if (dist2 <= range2) { - int dist = std::sqrt(dist2); - float alpha = 1.0 - (float)dist / voxelRadius; - qDebug() << "alpha:" << alpha; - PaintColor color = paintColor; - color.setAlphaF(alpha); - m_voxelGrid->add(x, y, z, color); - } - } - } - } -} - -void VertexColorPainter::createPaintedModel() -{ - std::vector vertexColors(m_outcome.vertices.size()); - for (size_t i = 0; i < m_outcome.vertices.size(); ++i) { - const auto &position = m_outcome.vertices[i]; - int voxelX = toVoxelLength(position.x()); - int voxelY = toVoxelLength(position.y()); - int voxelZ = toVoxelLength(position.z()); - vertexColors[i] = m_voxelGrid->query(voxelX, voxelY, voxelZ); - } - - int triangleVertexCount = m_outcome.triangles.size() * 3; - ShaderVertex *triangleVertices = new ShaderVertex[triangleVertexCount]; - int destIndex = 0; - const auto triangleVertexNormals = m_outcome.triangleVertexNormals(); - const auto triangleVertexUvs = m_outcome.triangleVertexUvs(); - const auto triangleTangents = m_outcome.triangleTangents(); - const QVector3D defaultNormal = QVector3D(0, 0, 0); - const QVector2D defaultUv = QVector2D(0, 0); - const QVector3D defaultTangent = QVector3D(0, 0, 0); - for (size_t i = 0; i < m_outcome.triangles.size(); ++i) { - for (auto j = 0; j < 3; j++) { - int vertexIndex = m_outcome.triangles[i][j]; - const auto &vertexColor = &vertexColors[vertexIndex]; - const QVector3D *srcVert = &m_outcome.vertices[vertexIndex]; - const QVector3D *srcNormal = &defaultNormal; - if (triangleVertexNormals) - srcNormal = &(*triangleVertexNormals)[i][j]; - const QVector2D *srcUv = &defaultUv; - if (triangleVertexUvs) - srcUv = &(*triangleVertexUvs)[i][j]; - const QVector3D *srcTangent = &defaultTangent; - if (triangleTangents) - srcTangent = &(*triangleTangents)[i]; - ShaderVertex *dest = &triangleVertices[destIndex]; - dest->colorR = vertexColor->redF(); - dest->colorG = vertexColor->greenF(); - dest->colorB = vertexColor->blueF(); - dest->alpha = vertexColor->alphaF(); - dest->posX = srcVert->x(); - dest->posY = srcVert->y(); - dest->posZ = srcVert->z(); - dest->texU = srcUv->x(); - dest->texV = srcUv->y(); - dest->normX = srcNormal->x(); - dest->normY = srcNormal->y(); - dest->normZ = srcNormal->z(); - dest->metalness = vertexColor->metalness; - dest->roughness = vertexColor->roughness; - dest->tangentX = srcTangent->x(); - dest->tangentY = srcTangent->y(); - dest->tangentZ = srcTangent->z(); - destIndex++; - } - } - m_model = new Model(triangleVertices, triangleVertexCount); -} - -int VertexColorPainter::toVoxelLength(float length) -{ - int voxelLength = length * 100; - if (voxelLength > m_gridSize) - voxelLength = m_gridSize; - else if (voxelLength < -m_gridSize) - voxelLength = -m_gridSize; - return voxelLength; -} - -void VertexColorPainter::paint() -{ - if (!calculateMouseModelPosition(m_targetPosition)) - return; - - if (PaintMode::None == m_paintMode) - return; - - if (nullptr == m_voxelGrid) - return; - - paintToVoxelGrid(); - createPaintedModel(); -} - -void VertexColorPainter::process() -{ - paint(); - - emit finished(); -} - -const QVector3D &VertexColorPainter::targetPosition() -{ - return m_targetPosition; -} diff --git a/src/vertexcolorpainter.h b/src/vertexcolorpainter.h deleted file mode 100644 index 2ead83ea..00000000 --- a/src/vertexcolorpainter.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef DUST3D_VERTEX_COLOR_PAINTER_H -#define DUST3D_VERTEX_COLOR_PAINTER_H -#include -#include -#include -#include -#include -#include -#include "outcome.h" -#include "paintmode.h" -#include "voxelgrid.h" -#include "model.h" - -class PaintColor : public QColor -{ -public: - float metalness = Model::m_defaultMetalness; - float roughness = Model::m_defaultRoughness; - - PaintColor() : - QColor() - { - } - - PaintColor(int r, int g, int b, int a = 255) : - QColor(r, g, b, a) - { - } - - PaintColor(const QColor &color) : - QColor(color) - { - } -}; - -PaintColor operator+(const PaintColor &first, const PaintColor &second); -PaintColor operator-(const PaintColor &first, const PaintColor &second); - -class VertexColorPainter : public QObject -{ - Q_OBJECT -public: - VertexColorPainter(const Outcome &outcome, const QVector3D &mouseRayNear, const QVector3D &mouseRayFar); - void setRadius(float radius); - void setBrushColor(const QColor &color); - void setBrushMetalness(float value); - void setBrushRoughness(float value); - void setPaintMode(PaintMode paintMode); - void setMaskNodeIds(const std::set &nodeIds); - void setVoxelGrid(VoxelGrid *voxelGrid); - - ~VertexColorPainter(); - Model *takePaintedModel(); - const QVector3D &targetPosition(); -signals: - void finished(); -public slots: - void process(); - void paint(); -private: - float m_radius = 0.0; - PaintMode m_paintMode = PaintMode::None; - std::set m_mousePickMaskNodeIds; - Outcome m_outcome; - QVector3D m_mouseRayNear; - QVector3D m_mouseRayFar; - QVector3D m_targetPosition; - QColor m_brushColor; - float m_brushMetalness = Model::m_defaultMetalness; - float m_brushRoughness = Model::m_defaultRoughness; - VoxelGrid *m_voxelGrid = nullptr; - Model *m_model = nullptr; - bool calculateMouseModelPosition(QVector3D &mouseModelPosition); - void paintToVoxelGrid(); - int toVoxelLength(float length); - void createPaintedModel(); -public: - static const int m_gridSize; -}; - -#endif diff --git a/src/voxelgrid.cpp b/src/voxelgrid.cpp deleted file mode 100644 index 35bc5721..00000000 --- a/src/voxelgrid.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "voxelgrid.h" diff --git a/src/voxelgrid.h b/src/voxelgrid.h deleted file mode 100644 index e5e3012c..00000000 --- a/src/voxelgrid.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef DUST3D_VOXEL_GRID_H -#define DUST3D_VOXEL_GRID_H -#include -#include - -template -class VoxelGrid -{ -public: - struct Voxel - { - qint16 x; - qint16 y; - qint16 z; - }; - - struct VoxelHash - { - size_t operator()(const Voxel &voxel) const - { - return ((size_t)voxel.x ^ ((size_t)voxel.y << 1)) ^ (size_t)voxel.z; - } - }; - - struct VoxelEqual - { - bool operator()(const Voxel &left, const Voxel &right) const - { - return (left.x == right.x) && - (left.y == right.y) && - (left.z == right.z); - } - }; - - T query(qint16 x, qint16 y, qint16 z) - { - auto findResult = m_grid.find({x, y, z}); - if (findResult == m_grid.end()) - return m_nullValue; - return findResult->second; - } - - T add(qint16 x, qint16 y, qint16 z, T value) - { - auto insertResult = m_grid.insert(std::make_pair(Voxel {x, y, z}, value)); - if (insertResult.second) { - insertResult.first->second = m_nullValue + value; - return insertResult.first->second; - } - insertResult.first->second = insertResult.first->second + value; - return insertResult.first->second; - } - - T sub(qint16 x, qint16 y, qint16 z, T value) - { - auto findResult = m_grid.find({x, y, z}); - if (findResult == m_grid.end()) - return m_nullValue; - findResult->second = findResult->second - value; - if (findResult->second == m_nullValue) { - m_grid.erase(findResult); - return m_nullValue; - } - return findResult->second; - } - - void reset(qint16 x, qint16 y, qint16 z) - { - auto findResult = m_grid.find({x, y, z}); - if (findResult == m_grid.end()) - return; - m_grid.erase(findResult); - } - - void setNullValue(const T &nullValue) - { - m_nullValue = nullValue; - } - -private: - std::unordered_map m_grid; - T m_nullValue = T(); -}; - -#endif diff --git a/thirdparty/simpleuv/simpleuv/chartpacker.h b/thirdparty/simpleuv/simpleuv/chartpacker.h index 39b81d5a..5593c0a0 100644 --- a/thirdparty/simpleuv/simpleuv/chartpacker.h +++ b/thirdparty/simpleuv/simpleuv/chartpacker.h @@ -25,7 +25,7 @@ private: float m_floatToIntFactor = 10000; size_t m_tryNum = 0; float m_textureSizeFactor = 1.0; - float m_paddingSize = 0.002; + float m_paddingSize = 0.005; size_t m_maxTryNum = 100; };