diff --git a/dust3d.pro b/dust3d.pro
index e3856ec5..9fc90ad4 100644
--- a/dust3d.pro
+++ b/dust3d.pro
@@ -134,6 +134,12 @@ include(thirdparty/QtAwesome/QtAwesome/QtAwesome.pri)
INCLUDEPATH += src
+SOURCES += src/normalanddepthmapsgenerator.cpp
+HEADERS += src/normalanddepthmapsgenerator.h
+
+SOURCES += src/modelofflinerender.cpp
+HEADERS += src/modelofflinerender.h
+
SOURCES += src/modelshaderprogram.cpp
HEADERS += src/modelshaderprogram.h
diff --git a/languages/dust3d_es_AR.ts b/languages/dust3d_es_AR.ts
index 385c4d34..6de99229 100644
--- a/languages/dust3d_es_AR.ts
+++ b/languages/dust3d_es_AR.ts
@@ -391,6 +391,14 @@ Consejos:
Rotación
+
+
+
+
+
+
+
+
ExportPreviewWidget
@@ -969,7 +977,7 @@ Consejos:
-
+
diff --git a/languages/dust3d_it_IT.ts b/languages/dust3d_it_IT.ts
index c43c23e3..aa4b5dcb 100755
--- a/languages/dust3d_it_IT.ts
+++ b/languages/dust3d_it_IT.ts
@@ -71,8 +71,8 @@ Tips:
- Make multiple parts instead of one single part for whole model
Generazione mesh non riuscita, annulla o modifica i nodi modificati di recente
Suggerimenti:
- - Non lasciare che la mesh generata si intersechi in se stessa
- - Crea più parti invece di una singola parte per l'intero modello
+ - Non lasciare che la mesh generata si intersechi in se stessa
+ - Crea più parti invece di una singola parte per l'intero modello
@@ -398,6 +398,14 @@ Suggerimenti:
+
+
+
+
+
+
+
+
ExportPreviewWidget
@@ -976,7 +984,7 @@ Suggerimenti:
-
+
diff --git a/languages/dust3d_zh_CN.ts b/languages/dust3d_zh_CN.ts
index 552bf10d..871dfe61 100644
--- a/languages/dust3d_zh_CN.ts
+++ b/languages/dust3d_zh_CN.ts
@@ -391,6 +391,14 @@ Tips:
脚本
+
+
+ 导出成图片...
+
+
+
+ 图片 (*.png)
+
ExportPreviewWidget
@@ -969,7 +977,7 @@ Tips:
纹理大小:
-
+
卡通渲染
diff --git a/shaders/default.core.frag b/shaders/default.core.frag
index 29496534..6a1e6800 100644
--- a/shaders/default.core.frag
+++ b/shaders/default.core.frag
@@ -86,7 +86,13 @@ uniform samplerCube environmentIrradianceMapId;
uniform int environmentIrradianceMapEnabled;
uniform samplerCube environmentSpecularMapId;
uniform int environmentSpecularMapEnabled;
-uniform int tongShadingEnabled;
+uniform int toonShadingEnabled;
+uniform int renderPurpose;
+uniform int toonEdgeEnabled;
+uniform float screenWidth;
+uniform float screenHeight;
+uniform sampler2D toonNormalMapId;
+uniform sampler2D toonDepthMapId;
const int MAX_LIGHTS = 8;
const int TYPE_POINT = 0;
@@ -106,6 +112,122 @@ struct Light {
int lightCount;
Light lights[MAX_LIGHTS];
+float sobel_v[9];
+float sobel_h[9];
+
+float normalEdgeSobel()
+{
+ sobel_v[0] = -1.0;
+ sobel_v[1] = 0.0;
+ sobel_v[2] = 1.0;
+ sobel_v[3] = -2.0;
+ sobel_v[4] = 0.0;
+ sobel_v[5] = 2.0;
+ sobel_v[6] = -1.0;
+ sobel_v[7] = 0.0;
+ sobel_v[8] = 1.0;
+
+ sobel_h[0] = -1.0;
+ sobel_h[1] = -2.0;
+ sobel_h[2] = -1.0;
+ sobel_h[3] = 0.0;
+ sobel_h[4] = 0.0;
+ sobel_h[5] = 0.0;
+ sobel_h[6] = 1.0;
+ sobel_h[7] = 2.0;
+ sobel_h[8] = 1.0;
+
+ // vec2 coord = gl_TexCoord[0].st;
+ vec2 coord = vec2(gl_FragCoord.x / screenWidth, 1.0 - gl_FragCoord.y / screenHeight);
+ //float len = length(coord);
+
+ float sx = 1.0 / screenWidth;
+ float sy = 1.0 / screenHeight;
+ float n[9];
+ vec3 ref = vec3(1.0, 1.0, 1.0);
+
+ n[0] = dot(texture(toonNormalMapId, vec2(coord.x - sx, coord.y - sy)).rgb, ref);
+ n[1] = dot(texture(toonNormalMapId, vec2(coord.x, coord.y - sy)).rgb, ref);
+ n[2] = dot(texture(toonNormalMapId, vec2(coord.x + sx, coord.y - sy)).rgb, ref);
+ n[3] = dot(texture(toonNormalMapId, vec2(coord.x - sx, coord.y)).rgb, ref);
+ n[4] = dot(texture(toonNormalMapId, vec2(coord.x, coord.y)).rgb, ref);
+ n[5] = dot(texture(toonNormalMapId, vec2(coord.x + sx, coord.y)).rgb, ref);
+ n[6] = dot(texture(toonNormalMapId, vec2(coord.x - sx, coord.y + sy)).rgb, ref);
+ n[7] = dot(texture(toonNormalMapId, vec2(coord.x, coord.y + sy)).rgb, ref);
+ n[8] = dot(texture(toonNormalMapId, vec2(coord.x + sx, coord.y + sy)).rgb, ref);
+
+ float v, h;
+
+ v = 0.0;
+ h = 0.0;
+
+ for (int i = 0; i < 9; ++i) {
+ v += sobel_v[i] * n[i];
+ h += sobel_h[i] * n[i];
+ }
+
+ float enhanceFactor = 1.0;
+
+ float r = sqrt(v * v * enhanceFactor + h * h * enhanceFactor);
+ return smoothstep(0.0, 1.0, r);
+}
+
+float depthEdgeSobel()
+{
+ sobel_v[0] = -1;
+ sobel_v[1] = 0;
+ sobel_v[2] = 1;
+ sobel_v[3] = -2;
+ sobel_v[4] = 0;
+ sobel_v[5] = 2;
+ sobel_v[6] = -1;
+ sobel_v[7] = 0;
+ sobel_v[8] = 1;
+
+ sobel_h[0] = -1;
+ sobel_h[1] = -2;
+ sobel_h[2] = -1;
+ sobel_h[3] = 0;
+ sobel_h[4] = 0;
+ sobel_h[5] = 0;
+ sobel_h[6] = 1;
+ sobel_h[7] = 2;
+ sobel_h[8] = 1;
+
+ // vec2 coord = gl_TexCoord[0].st;
+ vec2 coord = vec2(gl_FragCoord.x / screenWidth, 1.0 - gl_FragCoord.y / screenHeight);
+ //float len = length(coord);
+
+ float sx = 1.0 / screenWidth;
+ float sy = 1.0 / screenHeight;
+ float n[9];
+
+ n[0] = texture(toonDepthMapId, vec2(coord.x - sx, coord.y - sy)).r;
+ n[1] = texture(toonDepthMapId, vec2(coord.x, coord.y - sy)).r;
+ n[2] = texture(toonDepthMapId, vec2(coord.x + sx, coord.y - sy)).r;
+ n[3] = texture(toonDepthMapId, vec2(coord.x - sx, coord.y)).r;
+ n[4] = texture(toonDepthMapId, vec2(coord.x, coord.y)).r;
+ n[5] = texture(toonDepthMapId, vec2(coord.x + sx, coord.y)).r;
+ n[6] = texture(toonDepthMapId, vec2(coord.x - sx, coord.y + sy)).r;
+ n[7] = texture(toonDepthMapId, vec2(coord.x, coord.y + sy)).r;
+ n[8] = texture(toonDepthMapId, vec2(coord.x + sx, coord.y + sy)).r;
+
+ float v, h;
+
+ v = 0.0;
+ h = 0.0;
+
+ for (int i = 0; i < 9; ++i) {
+ v += sobel_v[i] * n[i];
+ h += sobel_h[i] * n[i];
+ }
+
+ float enhanceFactor = 10.0;
+
+ float r = sqrt(v * v * enhanceFactor + h * h * enhanceFactor);
+ return smoothstep(0.0, 1.0, r);
+}
+
int mipLevelCount(const in samplerCube cube)
{
int baseSize = textureSize(cube, 0).x;
@@ -385,7 +507,7 @@ vec4 metalRoughFunction(const in vec4 baseColor,
// Remap roughness for a perceptually more linear correspondence
float alpha = remapRoughness(roughness);
- if (tongShadingEnabled != 1) {
+ if (toonShadingEnabled != 1) {
if (environmentIrradianceMapEnabled == 1) {
cLinear += pbrIblModel(worldNormal,
worldView,
@@ -414,6 +536,14 @@ vec4 metalRoughFunction(const in vec4 baseColor,
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 2.0));
else
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.1));
+
+ if (toonEdgeEnabled == 1) {
+ float depthEdge = depthEdgeSobel();
+ float normalEdge = normalEdgeSobel();
+ if (depthEdge >= 0.009 || normalEdge >= 0.6) {
+ cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.02));
+ }
+ }
}
// Apply exposure correction
@@ -508,12 +638,18 @@ void main()
roughness = min(0.99, roughness);
metalness = min(0.99, metalness);
}
-
- fragColor = metalRoughFunction(vec4(color, alpha),
- metalness,
- roughness,
- ambientOcclusion,
- vert,
- normalize(cameraPos - vert),
- normal);
+
+ if (renderPurpose == 0) {
+ fragColor = metalRoughFunction(vec4(color, alpha),
+ metalness,
+ roughness,
+ ambientOcclusion,
+ vert,
+ normalize(cameraPos - vert),
+ normal);
+ } else if (renderPurpose == 1) {
+ fragColor = vec4(normal, 1.0);
+ } else if (renderPurpose == 2) {
+ fragColor = vec4(vec3(gl_FragCoord.w), 1.0);
+ }
}
diff --git a/shaders/default.frag b/shaders/default.frag
index 8714b995..162bf6d7 100644
--- a/shaders/default.frag
+++ b/shaders/default.frag
@@ -1,3 +1,4 @@
+#version 110
/****************************************************************************
**
** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
@@ -80,7 +81,13 @@ uniform int ambientOcclusionMapEnabled;
uniform int mousePickEnabled;
uniform vec3 mousePickTargetPosition;
uniform float mousePickRadius;
-uniform int tongShadingEnabled;
+uniform int toonShadingEnabled;
+uniform int renderPurpose;
+uniform int toonEdgeEnabled;
+uniform float screenWidth;
+uniform float screenHeight;
+uniform sampler2D toonNormalMapId;
+uniform sampler2D toonDepthMapId;
const int MAX_LIGHTS = 8;
const int TYPE_POINT = 0;
@@ -100,6 +107,122 @@ struct Light {
int lightCount;
Light lights[MAX_LIGHTS];
+float sobel_v[9];
+float sobel_h[9];
+
+float normalEdgeSobel()
+{
+ sobel_v[0] = -1.0;
+ sobel_v[1] = 0.0;
+ sobel_v[2] = 1.0;
+ sobel_v[3] = -2.0;
+ sobel_v[4] = 0.0;
+ sobel_v[5] = 2.0;
+ sobel_v[6] = -1.0;
+ sobel_v[7] = 0.0;
+ sobel_v[8] = 1.0;
+
+ sobel_h[0] = -1.0;
+ sobel_h[1] = -2.0;
+ sobel_h[2] = -1.0;
+ sobel_h[3] = 0.0;
+ sobel_h[4] = 0.0;
+ sobel_h[5] = 0.0;
+ sobel_h[6] = 1.0;
+ sobel_h[7] = 2.0;
+ sobel_h[8] = 1.0;
+
+ // vec2 coord = gl_TexCoord[0].st;
+ vec2 coord = vec2(gl_FragCoord.x / screenWidth - 1.0, 1.0 - gl_FragCoord.y / screenHeight);
+ //float len = length(coord);
+
+ float sx = 1.0 / screenWidth;
+ float sy = 1.0 / screenHeight;
+ float n[9];
+ vec3 ref = vec3(1.0, 1.0, 1.0);
+
+ n[0] = dot(texture2D(toonNormalMapId, vec2(coord.x - sx, coord.y - sy)).rgb, ref);
+ n[1] = dot(texture2D(toonNormalMapId, vec2(coord.x, coord.y - sy)).rgb, ref);
+ n[2] = dot(texture2D(toonNormalMapId, vec2(coord.x + sx, coord.y - sy)).rgb, ref);
+ n[3] = dot(texture2D(toonNormalMapId, vec2(coord.x - sx, coord.y)).rgb, ref);
+ n[4] = dot(texture2D(toonNormalMapId, vec2(coord.x, coord.y)).rgb, ref);
+ n[5] = dot(texture2D(toonNormalMapId, vec2(coord.x + sx, coord.y)).rgb, ref);
+ n[6] = dot(texture2D(toonNormalMapId, vec2(coord.x - sx, coord.y + sy)).rgb, ref);
+ n[7] = dot(texture2D(toonNormalMapId, vec2(coord.x, coord.y + sy)).rgb, ref);
+ n[8] = dot(texture2D(toonNormalMapId, vec2(coord.x + sx, coord.y + sy)).rgb, ref);
+
+ float v, h;
+
+ v = 0.0;
+ h = 0.0;
+
+ for (int i = 0; i < 9; ++i) {
+ v += sobel_v[i] * n[i];
+ h += sobel_h[i] * n[i];
+ }
+
+ float enhanceFactor = 1.0;
+
+ float r = sqrt(v * v * enhanceFactor + h * h * enhanceFactor);
+ return smoothstep(0.0, 1.0, r);
+}
+
+float depthEdgeSobel()
+{
+ sobel_v[0] = -1.0;
+ sobel_v[1] = 0.0;
+ sobel_v[2] = 1.0;
+ sobel_v[3] = -2.0;
+ sobel_v[4] = 0.0;
+ sobel_v[5] = 2.0;
+ sobel_v[6] = -1.0;
+ sobel_v[7] = 0.0;
+ sobel_v[8] = 1.0;
+
+ sobel_h[0] = -1.0;
+ sobel_h[1] = -2.0;
+ sobel_h[2] = -1.0;
+ sobel_h[3] = 0.0;
+ sobel_h[4] = 0.0;
+ sobel_h[5] = 0.0;
+ sobel_h[6] = 1.0;
+ sobel_h[7] = 2.0;
+ sobel_h[8] = 1.0;
+
+ // vec2 coord = gl_TexCoord[0].st;
+ vec2 coord = vec2(gl_FragCoord.x / screenWidth - 1.0, 1.0 - gl_FragCoord.y / screenHeight);
+ //float len = length(coord);
+
+ float sx = 1.0 / screenWidth;
+ float sy = 1.0 / screenHeight;
+ float n[9];
+
+ n[0] = texture2D(toonDepthMapId, vec2(coord.x - sx, coord.y - sy)).r;
+ n[1] = texture2D(toonDepthMapId, vec2(coord.x, coord.y - sy)).r;
+ n[2] = texture2D(toonDepthMapId, vec2(coord.x + sx, coord.y - sy)).r;
+ n[3] = texture2D(toonDepthMapId, vec2(coord.x - sx, coord.y)).r;
+ n[4] = texture2D(toonDepthMapId, vec2(coord.x, coord.y)).r;
+ n[5] = texture2D(toonDepthMapId, vec2(coord.x + sx, coord.y)).r;
+ n[6] = texture2D(toonDepthMapId, vec2(coord.x - sx, coord.y + sy)).r;
+ n[7] = texture2D(toonDepthMapId, vec2(coord.x, coord.y + sy)).r;
+ n[8] = texture2D(toonDepthMapId, vec2(coord.x + sx, coord.y + sy)).r;
+
+ float v, h;
+
+ v = 0.0;
+ h = 0.0;
+
+ for (int i = 0; i < 9; ++i) {
+ v += sobel_v[i] * n[i];
+ h += sobel_h[i] * n[i];
+ }
+
+ float enhanceFactor = 10.0;
+
+ float r = sqrt(v * v * enhanceFactor + h * h * enhanceFactor);
+ return smoothstep(0.0, 1.0, r);
+}
+
float remapRoughness(const in float roughness)
{
// As per page 14 of
@@ -281,7 +404,7 @@ vec4 metalRoughFunction(const in vec4 baseColor,
// Remap roughness for a perceptually more linear correspondence
float alpha = remapRoughness(roughness);
- if (tongShadingEnabled != 1) {
+ if (toonShadingEnabled != 1) {
for (int i = 0; i < lightCount; ++i) {
cLinear += pbrModel(i,
worldPosition,
@@ -301,6 +424,14 @@ vec4 metalRoughFunction(const in vec4 baseColor,
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 2.0));
else
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.1));
+
+ if (toonEdgeEnabled == 1) {
+ float depthEdge = depthEdgeSobel();
+ float normalEdge = normalEdgeSobel();
+ if (depthEdge >= 0.009 || normalEdge >= 0.6) {
+ cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.02));
+ }
+ }
}
// Apply exposure correction
@@ -393,12 +524,18 @@ void main()
roughness = min(0.99, roughness);
metalness = min(0.99, metalness);
-
- gl_FragColor = metalRoughFunction(vec4(color, alpha),
- metalness,
- roughness,
- ambientOcclusion,
- vert,
- normalize(cameraPos - vert),
- normal);
+
+ if (renderPurpose == 0) {
+ gl_FragColor = metalRoughFunction(vec4(color, alpha),
+ metalness,
+ roughness,
+ ambientOcclusion,
+ vert,
+ normalize(cameraPos - vert),
+ normal);
+ } else if (renderPurpose == 1) {
+ gl_FragColor = vec4(normal, 1.0);
+ } else if (renderPurpose == 2) {
+ gl_FragColor = vec4(vec3(gl_FragCoord.w), 1.0);
+ }
}
diff --git a/shaders/default.vert b/shaders/default.vert
index e9e86e49..423ca484 100644
--- a/shaders/default.vert
+++ b/shaders/default.vert
@@ -1,3 +1,4 @@
+#version 110
attribute vec4 vertex;
attribute vec3 normal;
attribute vec3 color;
diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp
index 11dce9bc..e54b140c 100644
--- a/src/documentwindow.cpp
+++ b/src/documentwindow.cpp
@@ -42,6 +42,7 @@
#include "scriptwidget.h"
#include "variablesxml.h"
#include "updatescheckwidget.h"
+#include "modelofflinerender.h"
int DocumentWindow::m_modelRenderWidgetInitialX = 16;
int DocumentWindow::m_modelRenderWidgetInitialY = 16;
@@ -310,7 +311,8 @@ DocumentWindow::DocumentWindow() :
m_modelRenderWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
m_modelRenderWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY);
m_modelRenderWidget->setMousePickRadius(m_document->mousePickRadius());
- m_modelRenderWidget->toggleWireframe();
+ if (!Preferences::instance().toonShading())
+ m_modelRenderWidget->toggleWireframe();
m_modelRenderWidget->enableEnvironmentLight();
connect(m_modelRenderWidget, &ModelWidget::mouseRayChanged, m_document,
@@ -341,6 +343,8 @@ DocumentWindow::DocumentWindow() :
m_modelRenderWidget->setMousePickTargetPositionInModelSpace(m_document->mouseTargetPosition());
});
+ connect(m_modelRenderWidget, &ModelWidget::renderParametersChanged, this, &DocumentWindow::delayedGenerateNormalAndDepthMaps);
+
m_graphicsWidget->setModelWidget(m_modelRenderWidget);
containerWidget->setModelWidget(m_modelRenderWidget);
@@ -494,9 +498,9 @@ DocumentWindow::DocumentWindow() :
connect(m_exportAsObjAction, &QAction::triggered, this, &DocumentWindow::exportObjResult, Qt::QueuedConnection);
m_fileMenu->addAction(m_exportAsObjAction);
- //m_exportRenderedAsImageAction = new QAction(tr("Export as PNG..."), this);
- //connect(m_exportRenderedAsImageAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportRenderedResult, Qt::QueuedConnection);
- //m_fileMenu->addAction(m_exportRenderedAsImageAction);
+ 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);
@@ -518,7 +522,7 @@ DocumentWindow::DocumentWindow() :
m_exportAsObjAction->setEnabled(m_graphicsWidget->hasItems());
//m_exportAsObjPlusMaterialsAction->setEnabled(m_graphicsWidget->hasItems());
m_exportAction->setEnabled(m_graphicsWidget->hasItems());
- //m_exportRenderedAsImageAction->setEnabled(m_graphicsWidget->hasItems());
+ m_exportRenderedAsImageAction->setEnabled(m_graphicsWidget->hasItems());
});
m_editMenu = menuBar()->addMenu(tr("&Edit"));
@@ -1603,6 +1607,16 @@ void DocumentWindow::showPreferences()
m_preferencesWidget->raise();
}
+void DocumentWindow::exportRenderedResult()
+{
+ QString filename = QFileDialog::getSaveFileName(this, QString(), QString(),
+ tr("Image (*.png)"));
+ if (filename.isEmpty()) {
+ return;
+ }
+ exportImageToFilename(filename);
+}
+
void DocumentWindow::exportObjResult()
{
QString filename = QFileDialog::getSaveFileName(this, QString(), QString(),
@@ -1940,3 +1954,92 @@ void DocumentWindow::checkExportWaitingList()
// m_infoWidget->move(0, m_graphicsContainerWidget->height() - m_infoWidget->height() - 5);
//}
+void DocumentWindow::normalAndDepthMapsReady()
+{
+ QImage *normalMap = m_normalAndDepthMapsGenerator->takeNormalMap();
+ QImage *depthMap = m_normalAndDepthMapsGenerator->takeDepthMap();
+
+ m_modelRenderWidget->updateToonNormalAndDepthMaps(normalMap, depthMap);
+
+ //m_normalAndDepthMapsGenerator->setRenderThread(QGuiApplication::instance()->thread());
+
+ delete m_normalAndDepthMapsGenerator;
+ m_normalAndDepthMapsGenerator = nullptr;
+
+ if (m_isNormalAndDepthMapsObsolete) {
+ generateNormalAndDepthMaps();
+ }
+}
+
+void DocumentWindow::generateNormalAndDepthMaps()
+{
+ if (nullptr != m_normalAndDepthMapsGenerator) {
+ m_isNormalAndDepthMapsObsolete = true;
+ return;
+ }
+
+ m_isNormalAndDepthMapsObsolete = false;
+
+ auto resultMesh = m_document->takeResultMesh();
+ if (nullptr == resultMesh)
+ return;
+
+ QThread *thread = new QThread;
+ m_normalAndDepthMapsGenerator = new NormalAndDepthMapsGenerator(m_modelRenderWidget);
+ m_normalAndDepthMapsGenerator->updateMesh(resultMesh);
+ m_normalAndDepthMapsGenerator->moveToThread(thread);
+ //m_normalAndDepthMapsGenerator->setRenderThread(thread);
+ connect(thread, &QThread::started, m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::process);
+ connect(m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::finished, this, &DocumentWindow::normalAndDepthMapsReady);
+ connect(m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::finished, thread, &QThread::quit);
+ connect(thread, &QThread::finished, thread, &QThread::deleteLater);
+ thread->start();
+
+ //m_normalAndDepthMapsGenerator = new NormalAndDepthMapsGenerator(m_modelRenderWidget);
+ //m_normalAndDepthMapsGenerator->updateMesh(resultMesh);
+ //connect(m_normalAndDepthMapsGenerator, &NormalAndDepthMapsGenerator::finished, this, &DocumentWindow::normalAndDepthMapsReady);
+ //m_normalAndDepthMapsGenerator->process();
+}
+
+void DocumentWindow::delayedGenerateNormalAndDepthMaps()
+{
+ if (!Preferences::instance().toonShading())
+ return;
+
+ //delete m_normalAndDepthMapsDelayTimer;
+ //m_normalAndDepthMapsDelayTimer = new QTimer(this);
+ //m_normalAndDepthMapsDelayTimer->setSingleShot(true);
+ //m_normalAndDepthMapsDelayTimer->setInterval(250);
+ //connect(m_normalAndDepthMapsDelayTimer, &QTimer::timeout, [=] {
+ generateNormalAndDepthMaps();
+ //});
+ //m_normalAndDepthMapsDelayTimer->start();
+}
+
+void DocumentWindow::exportImageToFilename(const QString &filename)
+{
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ MeshLoader *resultMesh = m_modelRenderWidget->fetchCurrentMesh();
+ if (nullptr != resultMesh) {
+ ModelOfflineRender *offlineRender = new ModelOfflineRender(m_modelRenderWidget->format());
+ offlineRender->setXRotation(m_modelRenderWidget->xRot());
+ offlineRender->setYRotation(m_modelRenderWidget->yRot());
+ offlineRender->setZRotation(m_modelRenderWidget->zRot());
+ offlineRender->setRenderPurpose(0);
+ QImage *normalMap = new QImage();
+ QImage *depthMap = new QImage();
+ m_modelRenderWidget->fetchCurrentToonNormalAndDepthMaps(normalMap, depthMap);
+ if (!normalMap->isNull() && !depthMap->isNull()) {
+ offlineRender->updateToonNormalAndDepthMaps(normalMap, depthMap);
+ } else {
+ delete normalMap;
+ delete depthMap;
+ }
+ offlineRender->updateMesh(resultMesh);
+ if (Preferences::instance().toonShading())
+ offlineRender->setToonShading(true);
+ offlineRender->toImage(QSize(m_modelRenderWidget->widthInPixels(),
+ m_modelRenderWidget->heightInPixels())).save(filename);
+ }
+ QApplication::restoreOverrideCursor();
+}
diff --git a/src/documentwindow.h b/src/documentwindow.h
index 581325e3..26c7659d 100644
--- a/src/documentwindow.h
+++ b/src/documentwindow.h
@@ -10,6 +10,7 @@
#include