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