Add menu: Export as Image
New feature: Exporting rendered model as png. A offscreen render been introduced for generating normal and depth maps, which are been generated for toon shader edge detection.master
parent
03dbe89b33
commit
55e657852c
|
@ -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
|
||||
|
||||
|
|
|
@ -391,6 +391,14 @@ Consejos:
|
|||
<source>Rotation</source>
|
||||
<translation>Rotación</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export as Image...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Image (*.png)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExportPreviewWidget</name>
|
||||
|
@ -969,7 +977,7 @@ Consejos:
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tong shading:</source>
|
||||
<source>Toon shading:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
|
|
|
@ -71,8 +71,8 @@ Tips:
|
|||
- Make multiple parts instead of one single part for whole model</source>
|
||||
<translation>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</translation>
|
||||
   - Non lasciare che la mesh generata si intersechi in se stessa
|
||||
   - Crea più parti invece di una singola parte per l'intero modello</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Parts</source>
|
||||
|
@ -398,6 +398,14 @@ Suggerimenti:
|
|||
<source>Enter zoom out mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export as Image...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Image (*.png)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExportPreviewWidget</name>
|
||||
|
@ -976,7 +984,7 @@ Suggerimenti:
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tong shading:</source>
|
||||
<source>Toon shading:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
|
|
|
@ -391,6 +391,14 @@ Tips:
|
|||
<source>Script</source>
|
||||
<translation>脚本</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export as Image...</source>
|
||||
<translation>导出成图片...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Image (*.png)</source>
|
||||
<translation>图片 (*.png)</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExportPreviewWidget</name>
|
||||
|
@ -969,7 +977,7 @@ Tips:
|
|||
<translation>纹理大小:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tong shading:</source>
|
||||
<source>Toon shading:</source>
|
||||
<translation>卡通渲染</translation>
|
||||
</message>
|
||||
</context>
|
||||
|
|
|
@ -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
|
||||
|
@ -509,11 +639,17 @@ void main()
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -394,11 +525,17 @@ 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#version 110
|
||||
attribute vec4 vertex;
|
||||
attribute vec3 normal;
|
||||
attribute vec3 color;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <map>
|
||||
#include <QStringList>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include "document.h"
|
||||
#include "modelwidget.h"
|
||||
#include "exportpreviewwidget.h"
|
||||
|
@ -18,6 +19,7 @@
|
|||
#include "posemanagewidget.h"
|
||||
#include "preferenceswidget.h"
|
||||
#include "graphicscontainerwidget.h"
|
||||
#include "normalanddepthmapsgenerator.h"
|
||||
|
||||
class SkeletonGraphicsWidget;
|
||||
|
||||
|
@ -49,6 +51,7 @@ public slots:
|
|||
void open();
|
||||
void openExample(const QString &modelName);
|
||||
void openPathAs(const QString &path, const QString &asName);
|
||||
void exportRenderedResult();
|
||||
void exportObjResult();
|
||||
void exportGlbResult();
|
||||
void exportFbxResult();
|
||||
|
@ -78,11 +81,15 @@ public slots:
|
|||
void showCutFaceSettingPopup(const QPoint &globalPos, std::set<QUuid> nodeIds);
|
||||
void setExportWaitingList(const QStringList &filenames);
|
||||
void checkExportWaitingList();
|
||||
void exportImageToFilename(const QString &filename);
|
||||
void exportObjToFilename(const QString &filename);
|
||||
void exportFbxToFilename(const QString &filename);
|
||||
void exportGlbToFilename(const QString &filename);
|
||||
void toggleRotation();
|
||||
//void updateInfoWidgetPosition();
|
||||
void generateNormalAndDepthMaps();
|
||||
void delayedGenerateNormalAndDepthMaps();
|
||||
void normalAndDepthMapsReady();
|
||||
private:
|
||||
void initLockButton(QPushButton *button);
|
||||
void setCurrentFilename(const QString &filename);
|
||||
|
@ -199,6 +206,10 @@ private:
|
|||
QPushButton *m_radiusLockButton;
|
||||
|
||||
QMetaObject::Connection m_partListDockerVisibleSwitchConnection;
|
||||
|
||||
NormalAndDepthMapsGenerator *m_normalAndDepthMapsGenerator = nullptr;
|
||||
QTimer *m_normalAndDepthMapsDelayTimer = nullptr;
|
||||
bool m_isNormalAndDepthMapsObsolete = false;
|
||||
public:
|
||||
static int m_modelRenderWidgetInitialX;
|
||||
static int m_modelRenderWidgetInitialY;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "logbrowser.h"
|
||||
// Modified from https://wiki.qt.io/Browser_for_QDebug_output
|
||||
#include <QMetaType>
|
||||
#include <stdio.h>
|
||||
#include "logbrowserdialog.h"
|
||||
|
||||
LogBrowser::LogBrowser(QObject *parent) :
|
||||
|
|
|
@ -23,6 +23,12 @@ ModelMeshBinder::~ModelMeshBinder()
|
|||
delete m_metalnessRoughnessAmbientOcclusionMap;
|
||||
delete m_environmentIrradianceMap;
|
||||
delete m_environmentSpecularMap;
|
||||
delete m_toonNormalMap;
|
||||
delete m_toonDepthMap;
|
||||
delete m_newToonNormalMap;
|
||||
delete m_newToonDepthMap;
|
||||
delete m_currentToonNormalMap;
|
||||
delete m_currentToonDepthMap;
|
||||
}
|
||||
|
||||
void ModelMeshBinder::updateMesh(MeshLoader *mesh)
|
||||
|
@ -61,6 +67,14 @@ void ModelMeshBinder::enableEnvironmentLight()
|
|||
m_environmentLightEnabled = true;
|
||||
}
|
||||
|
||||
MeshLoader *ModelMeshBinder::fetchCurrentMesh()
|
||||
{
|
||||
QMutexLocker lock(&m_meshMutex);
|
||||
if (nullptr == m_mesh)
|
||||
return nullptr;
|
||||
return new MeshLoader(*m_mesh);
|
||||
}
|
||||
|
||||
void ModelMeshBinder::paint(ModelShaderProgram *program)
|
||||
{
|
||||
MeshLoader *newMesh = nullptr;
|
||||
|
@ -74,6 +88,33 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
hasNewMesh = true;
|
||||
}
|
||||
}
|
||||
if (m_newToonMapsComing) {
|
||||
QMutexLocker lock(&m_toonNormalAndDepthMapMutex);
|
||||
if (m_newToonMapsComing) {
|
||||
|
||||
delete m_toonNormalMap;
|
||||
m_toonNormalMap = nullptr;
|
||||
delete m_currentToonNormalMap;
|
||||
m_currentToonNormalMap = nullptr;
|
||||
if (nullptr != m_newToonNormalMap) {
|
||||
m_toonNormalMap = new QOpenGLTexture(*m_newToonNormalMap);
|
||||
m_currentToonNormalMap = m_newToonNormalMap;
|
||||
m_newToonNormalMap = nullptr;
|
||||
}
|
||||
|
||||
delete m_toonDepthMap;
|
||||
m_toonDepthMap = nullptr;
|
||||
delete m_currentToonDepthMap;
|
||||
m_currentToonDepthMap = nullptr;
|
||||
if (nullptr != m_newToonDepthMap) {
|
||||
m_toonDepthMap = new QOpenGLTexture(*m_newToonDepthMap);
|
||||
m_currentToonDepthMap = m_newToonDepthMap;
|
||||
m_newToonDepthMap = nullptr;
|
||||
}
|
||||
|
||||
m_newToonMapsComing = false;
|
||||
}
|
||||
}
|
||||
{
|
||||
QMutexLocker lock(&m_meshMutex);
|
||||
if (hasNewMesh) {
|
||||
|
@ -218,6 +259,8 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
program->setUniformValue(program->metalnessRoughnessAmbientOcclusionMapIdLoc(), 2);
|
||||
program->setUniformValue(program->environmentIrradianceMapIdLoc(), 3);
|
||||
program->setUniformValue(program->environmentSpecularMapIdLoc(), 4);
|
||||
program->setUniformValue(program->toonNormalMapIdLoc(), 5);
|
||||
program->setUniformValue(program->toonDepthMapIdLoc(), 6);
|
||||
if (m_showWireframes) {
|
||||
if (m_renderEdgeVertexCount > 0) {
|
||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoEdge);
|
||||
|
@ -275,6 +318,11 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 0);
|
||||
}
|
||||
}
|
||||
if (nullptr != m_toonNormalMap && nullptr != m_toonDepthMap) {
|
||||
m_toonNormalMap->bind(5);
|
||||
m_toonDepthMap->bind(6);
|
||||
program->setUniformValue(program->toonEdgeEnabledLoc(), 1);
|
||||
}
|
||||
f->glDrawArrays(GL_TRIANGLES, 0, m_renderTriangleVertexCount);
|
||||
}
|
||||
if (m_toolEnabled) {
|
||||
|
@ -295,6 +343,28 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
|||
}
|
||||
}
|
||||
|
||||
void ModelMeshBinder::fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap)
|
||||
{
|
||||
QMutexLocker lock(&m_toonNormalAndDepthMapMutex);
|
||||
if (nullptr != normalMap && nullptr != m_currentToonNormalMap)
|
||||
*normalMap = *m_currentToonNormalMap;
|
||||
if (nullptr != depthMap && nullptr != m_currentToonDepthMap)
|
||||
*depthMap = *m_currentToonDepthMap;
|
||||
}
|
||||
|
||||
void ModelMeshBinder::updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap)
|
||||
{
|
||||
QMutexLocker lock(&m_toonNormalAndDepthMapMutex);
|
||||
|
||||
delete m_newToonNormalMap;
|
||||
m_newToonNormalMap = normalMap;
|
||||
|
||||
delete m_newToonDepthMap;
|
||||
m_newToonDepthMap = depthMap;
|
||||
|
||||
m_newToonMapsComing = true;
|
||||
}
|
||||
|
||||
void ModelMeshBinder::cleanup()
|
||||
{
|
||||
if (m_vboTriangle.isCreated())
|
||||
|
@ -315,6 +385,10 @@ void ModelMeshBinder::cleanup()
|
|||
m_environmentIrradianceMap = nullptr;
|
||||
delete m_environmentSpecularMap;
|
||||
m_environmentSpecularMap = nullptr;
|
||||
delete m_toonNormalMap;
|
||||
m_toonNormalMap = nullptr;
|
||||
delete m_toonDepthMap;
|
||||
m_toonDepthMap = nullptr;
|
||||
}
|
||||
|
||||
void ModelMeshBinder::showWireframes()
|
||||
|
|
|
@ -13,6 +13,7 @@ class ModelMeshBinder
|
|||
public:
|
||||
ModelMeshBinder(bool toolEnabled=false);
|
||||
~ModelMeshBinder();
|
||||
MeshLoader *fetchCurrentMesh();
|
||||
void updateMesh(MeshLoader *mesh);
|
||||
void initialize();
|
||||
void paint(ModelShaderProgram *program);
|
||||
|
@ -25,6 +26,8 @@ public:
|
|||
void enableEnvironmentLight();
|
||||
bool isCheckUvEnabled();
|
||||
void reloadMesh();
|
||||
void fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||
private:
|
||||
MeshLoader *m_mesh = nullptr;
|
||||
MeshLoader *m_newMesh = nullptr;
|
||||
|
@ -46,6 +49,13 @@ private:
|
|||
bool m_environmentLightEnabled = false;
|
||||
QOpenGLTexture *m_environmentIrradianceMap = nullptr;
|
||||
QOpenGLTexture *m_environmentSpecularMap = nullptr;
|
||||
QOpenGLTexture *m_toonNormalMap = nullptr;
|
||||
QOpenGLTexture *m_toonDepthMap = nullptr;
|
||||
QImage *m_newToonNormalMap = nullptr;
|
||||
QImage *m_newToonDepthMap = nullptr;
|
||||
QImage *m_currentToonNormalMap = nullptr;
|
||||
QImage *m_currentToonDepthMap = nullptr;
|
||||
bool m_newToonMapsComing = false;
|
||||
private:
|
||||
QOpenGLVertexArrayObject m_vaoTriangle;
|
||||
QOpenGLBuffer m_vboTriangle;
|
||||
|
@ -55,6 +65,7 @@ private:
|
|||
QOpenGLBuffer m_vboTool;
|
||||
QMutex m_meshMutex;
|
||||
QMutex m_newMeshMutex;
|
||||
QMutex m_toonNormalAndDepthMapMutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
#include <QOpenGLFramebufferObjectFormat>
|
||||
#include <QThread>
|
||||
#include <QDebug>
|
||||
#include "modelofflinerender.h"
|
||||
|
||||
ModelOfflineRender::ModelOfflineRender(const QSurfaceFormat &format, QScreen *targetScreen) :
|
||||
QOffscreenSurface(targetScreen),
|
||||
m_context(nullptr),
|
||||
m_mesh(nullptr)
|
||||
{
|
||||
setFormat(format);
|
||||
create();
|
||||
if (!isValid())
|
||||
qDebug() << "ModelOfflineRender is invalid";
|
||||
}
|
||||
|
||||
ModelOfflineRender::~ModelOfflineRender()
|
||||
{
|
||||
destroy();
|
||||
delete m_mesh;
|
||||
delete m_normalMap;
|
||||
delete m_depthMap;
|
||||
}
|
||||
|
||||
void ModelOfflineRender::updateMesh(MeshLoader *mesh)
|
||||
{
|
||||
delete m_mesh;
|
||||
m_mesh = mesh;
|
||||
}
|
||||
|
||||
void ModelOfflineRender::setRenderThread(QThread *thread)
|
||||
{
|
||||
//this->moveToThread(thread);
|
||||
}
|
||||
|
||||
void ModelOfflineRender::setXRotation(int angle)
|
||||
{
|
||||
m_xRot = angle;
|
||||
}
|
||||
|
||||
void ModelOfflineRender::setYRotation(int angle)
|
||||
{
|
||||
m_yRot = angle;
|
||||
}
|
||||
|
||||
void ModelOfflineRender::setZRotation(int angle)
|
||||
{
|
||||
m_zRot = angle;
|
||||
}
|
||||
|
||||
void ModelOfflineRender::setRenderPurpose(int purpose)
|
||||
{
|
||||
m_renderPurpose = purpose;
|
||||
}
|
||||
|
||||
void ModelOfflineRender::setToonShading(bool toonShading)
|
||||
{
|
||||
m_toonShading = toonShading;
|
||||
}
|
||||
|
||||
void ModelOfflineRender::updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap)
|
||||
{
|
||||
delete m_normalMap;
|
||||
m_normalMap = normalMap;
|
||||
|
||||
delete m_depthMap;
|
||||
m_depthMap = depthMap;
|
||||
}
|
||||
|
||||
QImage ModelOfflineRender::toImage(const QSize &size)
|
||||
{
|
||||
QImage image;
|
||||
|
||||
m_context = new QOpenGLContext();
|
||||
m_context->setFormat(format());
|
||||
if (!m_context->create()) {
|
||||
delete m_context;
|
||||
m_context = nullptr;
|
||||
|
||||
qDebug() << "QOpenGLContext create failed";
|
||||
return image;
|
||||
}
|
||||
|
||||
if (!m_context->makeCurrent(this)) {
|
||||
delete m_context;
|
||||
m_context = nullptr;
|
||||
|
||||
qDebug() << "QOpenGLContext makeCurrent failed";
|
||||
return image;
|
||||
}
|
||||
|
||||
QOpenGLFramebufferObjectFormat format;
|
||||
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
|
||||
format.setSamples(4);
|
||||
format.setTextureTarget(GL_TEXTURE_2D);
|
||||
format.setInternalTextureFormat(GL_RGBA32F_ARB);
|
||||
QOpenGLFramebufferObject *renderFbo = new QOpenGLFramebufferObject(size, format);
|
||||
renderFbo->bind();
|
||||
m_context->functions()->glViewport(0, 0, size.width(), size.height());
|
||||
|
||||
if (nullptr != m_mesh) {
|
||||
QMatrix4x4 projection;
|
||||
QMatrix4x4 world;
|
||||
QMatrix4x4 camera;
|
||||
|
||||
bool isCoreProfile = false;
|
||||
const char *versionString = (const char *)m_context->functions()->glGetString(GL_VERSION);
|
||||
if (nullptr != versionString &&
|
||||
'\0' != versionString[0] &&
|
||||
0 == strstr(versionString, "Mesa")) {
|
||||
isCoreProfile = m_context->format().profile() == QSurfaceFormat::CoreProfile;
|
||||
}
|
||||
|
||||
ModelShaderProgram *program = new ModelShaderProgram(isCoreProfile);
|
||||
ModelMeshBinder meshBinder;
|
||||
meshBinder.initialize();
|
||||
meshBinder.hideWireframes();
|
||||
if (nullptr != m_normalMap && nullptr != m_depthMap) {
|
||||
meshBinder.updateToonNormalAndDepthMaps(m_normalMap, m_depthMap);
|
||||
m_normalMap = nullptr;
|
||||
m_depthMap = nullptr;
|
||||
}
|
||||
|
||||
m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_context->functions()->glEnable(GL_BLEND);
|
||||
m_context->functions()->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
m_context->functions()->glEnable(GL_DEPTH_TEST);
|
||||
m_context->functions()->glEnable(GL_CULL_FACE);
|
||||
#ifdef GL_LINE_SMOOTH
|
||||
m_context->functions()->glEnable(GL_LINE_SMOOTH);
|
||||
#endif
|
||||
|
||||
world.setToIdentity();
|
||||
world.rotate(m_xRot / 16.0f, 1, 0, 0);
|
||||
world.rotate(m_yRot / 16.0f, 0, 1, 0);
|
||||
world.rotate(m_zRot / 16.0f, 0, 0, 1);
|
||||
|
||||
projection.setToIdentity();
|
||||
projection.perspective(45.0f, GLfloat(size.width()) / size.height(), 0.01f, 100.0f);
|
||||
|
||||
camera.setToIdentity();
|
||||
camera.translate(QVector3D(0, 0, -4.0));
|
||||
|
||||
program->bind();
|
||||
program->setUniformValue(program->lightPosLoc(), QVector3D(0, 0, 70));
|
||||
program->setUniformValue(program->toonShadingEnabledLoc(), m_toonShading ? 1 : 0);
|
||||
program->setUniformValue(program->projectionMatrixLoc(), projection);
|
||||
program->setUniformValue(program->modelMatrixLoc(), world);
|
||||
QMatrix3x3 normalMatrix = world.normalMatrix();
|
||||
program->setUniformValue(program->normalMatrixLoc(), normalMatrix);
|
||||
program->setUniformValue(program->viewMatrixLoc(), camera);
|
||||
program->setUniformValue(program->textureEnabledLoc(), 0);
|
||||
program->setUniformValue(program->normalMapEnabledLoc(), 0);
|
||||
program->setUniformValue(program->mousePickEnabledLoc(), 0);
|
||||
program->setUniformValue(program->renderPurposeLoc(), m_renderPurpose);
|
||||
|
||||
program->setUniformValue(program->toonEdgeEnabledLoc(), 0);
|
||||
program->setUniformValue(program->screenWidthLoc(), (GLfloat)size.width());
|
||||
program->setUniformValue(program->screenHeightLoc(), (GLfloat)size.height());
|
||||
program->setUniformValue(program->toonNormalMapIdLoc(), 0);
|
||||
program->setUniformValue(program->toonDepthMapIdLoc(), 0);
|
||||
|
||||
meshBinder.updateMesh(m_mesh);
|
||||
meshBinder.paint(program);
|
||||
|
||||
meshBinder.cleanup();
|
||||
|
||||
program->release();
|
||||
delete program;
|
||||
|
||||
m_mesh = nullptr;
|
||||
}
|
||||
|
||||
m_context->functions()->glFlush();
|
||||
|
||||
image = renderFbo->toImage();
|
||||
|
||||
renderFbo->bindDefault();
|
||||
delete renderFbo;
|
||||
|
||||
m_context->doneCurrent();
|
||||
delete m_context;
|
||||
m_context = nullptr;
|
||||
|
||||
return image;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef DUST3D_MODEL_OFFLINE_RENDER_H
|
||||
#define DUST3D_MODEL_OFFLINE_RENDER_H
|
||||
#include <QOffscreenSurface>
|
||||
#include <QScreen>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLContext>
|
||||
#include <QImage>
|
||||
#include <QThread>
|
||||
#include "modelshaderprogram.h"
|
||||
#include "modelmeshbinder.h"
|
||||
#include "meshloader.h"
|
||||
|
||||
class ModelOfflineRender : QOffscreenSurface
|
||||
{
|
||||
public:
|
||||
ModelOfflineRender(const QSurfaceFormat &format, QScreen *targetScreen = Q_NULLPTR);
|
||||
~ModelOfflineRender();
|
||||
void setXRotation(int angle);
|
||||
void setYRotation(int angle);
|
||||
void setZRotation(int angle);
|
||||
void setRenderPurpose(int purpose);
|
||||
void setRenderThread(QThread *thread);
|
||||
void updateMesh(MeshLoader *mesh);
|
||||
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||
void setToonShading(bool toonShading);
|
||||
QImage toImage(const QSize &size);
|
||||
private:
|
||||
int m_xRot = 0;
|
||||
int m_yRot = 0;
|
||||
int m_zRot = 0;
|
||||
int m_renderPurpose = 0;
|
||||
QOpenGLContext *m_context = nullptr;
|
||||
MeshLoader *m_mesh = nullptr;
|
||||
QImage *m_normalMap = nullptr;
|
||||
QImage *m_depthMap = nullptr;
|
||||
bool m_toonShading = false;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -58,7 +58,13 @@ ModelShaderProgram::ModelShaderProgram(bool isCoreProfile)
|
|||
m_mousePickEnabledLoc = this->uniformLocation("mousePickEnabled");
|
||||
m_mousePickTargetPositionLoc = this->uniformLocation("mousePickTargetPosition");
|
||||
m_mousePickRadiusLoc = this->uniformLocation("mousePickRadius");
|
||||
m_tongShadingEnabledLoc = this->uniformLocation("tongShadingEnabled");
|
||||
m_toonShadingEnabledLoc = this->uniformLocation("toonShadingEnabled");
|
||||
m_renderPurposeLoc = this->uniformLocation("renderPurpose");
|
||||
m_toonEdgeEnabledLoc = this->uniformLocation("toonEdgeEnabled");
|
||||
m_screenWidthLoc = this->uniformLocation("screenWidth");
|
||||
m_screenHeightLoc = this->uniformLocation("screenHeight");
|
||||
m_toonNormalMapIdLoc = this->uniformLocation("toonNormalMapId");
|
||||
m_toonDepthMapIdLoc = this->uniformLocation("toonDepthMapId");
|
||||
if (m_isCoreProfile) {
|
||||
m_environmentIrradianceMapIdLoc = this->uniformLocation("environmentIrradianceMapId");
|
||||
m_environmentIrradianceMapEnabledLoc = this->uniformLocation("environmentIrradianceMapEnabled");
|
||||
|
@ -167,7 +173,37 @@ int ModelShaderProgram::environmentSpecularMapEnabledLoc()
|
|||
return m_environmentSpecularMapEnabledLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::tongShadingEnabledLoc()
|
||||
int ModelShaderProgram::toonShadingEnabledLoc()
|
||||
{
|
||||
return m_tongShadingEnabledLoc;
|
||||
return m_toonShadingEnabledLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::renderPurposeLoc()
|
||||
{
|
||||
return m_renderPurposeLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::toonEdgeEnabledLoc()
|
||||
{
|
||||
return m_toonEdgeEnabledLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::screenWidthLoc()
|
||||
{
|
||||
return m_screenWidthLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::screenHeightLoc()
|
||||
{
|
||||
return m_screenHeightLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::toonNormalMapIdLoc()
|
||||
{
|
||||
return m_toonNormalMapIdLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::toonDepthMapIdLoc()
|
||||
{
|
||||
return m_toonDepthMapIdLoc;
|
||||
}
|
|
@ -27,7 +27,13 @@ public:
|
|||
int environmentIrradianceMapEnabledLoc();
|
||||
int environmentSpecularMapIdLoc();
|
||||
int environmentSpecularMapEnabledLoc();
|
||||
int tongShadingEnabledLoc();
|
||||
int toonShadingEnabledLoc();
|
||||
int renderPurposeLoc();
|
||||
int toonEdgeEnabledLoc();
|
||||
int screenWidthLoc();
|
||||
int screenHeightLoc();
|
||||
int toonNormalMapIdLoc();
|
||||
int toonDepthMapIdLoc();
|
||||
bool isCoreProfile();
|
||||
static const QString &loadShaderSource(const QString &name);
|
||||
private:
|
||||
|
@ -52,7 +58,13 @@ private:
|
|||
int m_environmentIrradianceMapEnabledLoc = 0;
|
||||
int m_environmentSpecularMapIdLoc = 0;
|
||||
int m_environmentSpecularMapEnabledLoc = 0;
|
||||
int m_tongShadingEnabledLoc = 0;
|
||||
int m_toonShadingEnabledLoc = 0;
|
||||
int m_renderPurposeLoc = 0;
|
||||
int m_toonEdgeEnabledLoc = 0;
|
||||
int m_screenWidthLoc = 0;
|
||||
int m_screenHeightLoc = 0;
|
||||
int m_toonNormalMapIdLoc = 0;
|
||||
int m_toonDepthMapIdLoc = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -46,13 +46,18 @@ ModelWidget::ModelWidget(QWidget *parent) :
|
|||
setFormat(fmt);
|
||||
}
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
m_widthInPixels = width() * window()->devicePixelRatio();
|
||||
m_heightInPixels = height() * window()->devicePixelRatio();
|
||||
|
||||
zoom(200);
|
||||
|
||||
connect(&Preferences::instance(), &Preferences::tongShadingChanged, this, &ModelWidget::reRender);
|
||||
connect(&Preferences::instance(), &Preferences::toonShadingChanged, this, &ModelWidget::reRender);
|
||||
}
|
||||
|
||||
void ModelWidget::reRender()
|
||||
{
|
||||
emit renderParametersChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
|
@ -82,6 +87,7 @@ void ModelWidget::setXRotation(int angle)
|
|||
if (angle != m_xRot) {
|
||||
m_xRot = angle;
|
||||
emit xRotationChanged(angle);
|
||||
emit renderParametersChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +98,7 @@ void ModelWidget::setYRotation(int angle)
|
|||
if (angle != m_yRot) {
|
||||
m_yRot = angle;
|
||||
emit yRotationChanged(angle);
|
||||
emit renderParametersChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -102,10 +109,16 @@ void ModelWidget::setZRotation(int angle)
|
|||
if (angle != m_zRot) {
|
||||
m_zRot = angle;
|
||||
emit zRotationChanged(angle);
|
||||
emit renderParametersChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
MeshLoader *ModelWidget::fetchCurrentMesh()
|
||||
{
|
||||
return m_meshBinder.fetchCurrentMesh();
|
||||
}
|
||||
|
||||
void ModelWidget::cleanup()
|
||||
{
|
||||
if (m_program == nullptr)
|
||||
|
@ -141,7 +154,7 @@ void ModelWidget::initializeGL()
|
|||
if (nullptr != versionString &&
|
||||
'\0' != versionString[0] &&
|
||||
0 == strstr(versionString, "Mesa")) {
|
||||
isCoreProfile = QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile;
|
||||
isCoreProfile = format().profile() == QSurfaceFormat::CoreProfile;
|
||||
}
|
||||
|
||||
m_program = new ModelShaderProgram(isCoreProfile);
|
||||
|
@ -174,6 +187,7 @@ void ModelWidget::paintGL()
|
|||
#ifdef GL_LINE_SMOOTH
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
#endif
|
||||
glViewport(0, 0, m_widthInPixels, m_heightInPixels);
|
||||
|
||||
m_world.setToIdentity();
|
||||
m_world.rotate(m_xRot / 16.0f, 1, 0, 0);
|
||||
|
@ -181,7 +195,7 @@ void ModelWidget::paintGL()
|
|||
m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
|
||||
|
||||
m_program->bind();
|
||||
m_program->setUniformValue(m_program->tongShadingEnabledLoc(), Preferences::instance().tongShading() ? 1 : 0);
|
||||
m_program->setUniformValue(m_program->toonShadingEnabledLoc(), Preferences::instance().toonShading() ? 1 : 0);
|
||||
m_program->setUniformValue(m_program->projectionMatrixLoc(), m_projection);
|
||||
m_program->setUniformValue(m_program->modelMatrixLoc(), m_world);
|
||||
QMatrix3x3 normalMatrix = m_world.normalMatrix();
|
||||
|
@ -189,6 +203,13 @@ void ModelWidget::paintGL()
|
|||
m_program->setUniformValue(m_program->viewMatrixLoc(), m_camera);
|
||||
m_program->setUniformValue(m_program->textureEnabledLoc(), 0);
|
||||
m_program->setUniformValue(m_program->normalMapEnabledLoc(), 0);
|
||||
m_program->setUniformValue(m_program->renderPurposeLoc(), 0);
|
||||
|
||||
m_program->setUniformValue(m_program->toonEdgeEnabledLoc(), 0);
|
||||
m_program->setUniformValue(m_program->screenWidthLoc(), (GLfloat)m_widthInPixels);
|
||||
m_program->setUniformValue(m_program->screenHeightLoc(), (GLfloat)m_heightInPixels);
|
||||
m_program->setUniformValue(m_program->toonNormalMapIdLoc(), 0);
|
||||
m_program->setUniformValue(m_program->toonDepthMapIdLoc(), 0);
|
||||
|
||||
if (m_mousePickingEnabled && !m_mousePickTargetPositionInModelSpace.isNull()) {
|
||||
m_program->setUniformValue(m_program->mousePickEnabledLoc(), 1);
|
||||
|
@ -207,8 +228,11 @@ void ModelWidget::paintGL()
|
|||
|
||||
void ModelWidget::resizeGL(int w, int h)
|
||||
{
|
||||
m_widthInPixels = w * window()->devicePixelRatio();
|
||||
m_heightInPixels = h * window()->devicePixelRatio();
|
||||
m_projection.setToIdentity();
|
||||
m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
|
||||
emit renderParametersChanged();
|
||||
}
|
||||
|
||||
std::pair<QVector3D, QVector3D> ModelWidget::screenPositionToMouseRay(const QPoint &screenPosition)
|
||||
|
@ -383,6 +407,7 @@ void ModelWidget::zoom(float delta)
|
|||
}
|
||||
}
|
||||
setGeometry(geometry().marginsAdded(margins));
|
||||
emit renderParametersChanged();
|
||||
}
|
||||
|
||||
void ModelWidget::setMousePickTargetPositionInModelSpace(QVector3D position)
|
||||
|
@ -400,9 +425,31 @@ void ModelWidget::setMousePickRadius(float radius)
|
|||
void ModelWidget::updateMesh(MeshLoader *mesh)
|
||||
{
|
||||
m_meshBinder.updateMesh(mesh);
|
||||
emit renderParametersChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
void ModelWidget::fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap)
|
||||
{
|
||||
m_meshBinder.fetchCurrentToonNormalAndDepthMaps(normalMap, depthMap);
|
||||
}
|
||||
|
||||
void ModelWidget::updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap)
|
||||
{
|
||||
m_meshBinder.updateToonNormalAndDepthMaps(normalMap, depthMap);
|
||||
update();
|
||||
}
|
||||
|
||||
int ModelWidget::widthInPixels()
|
||||
{
|
||||
return m_widthInPixels;
|
||||
}
|
||||
|
||||
int ModelWidget::heightInPixels()
|
||||
{
|
||||
return m_heightInPixels;
|
||||
}
|
||||
|
||||
void ModelWidget::enableMove(bool enabled)
|
||||
{
|
||||
m_moveEnabled = enabled;
|
||||
|
|
|
@ -23,6 +23,7 @@ signals:
|
|||
void mousePressed();
|
||||
void mouseReleased();
|
||||
void addMouseRadius(float radius);
|
||||
void renderParametersChanged();
|
||||
public:
|
||||
ModelWidget(QWidget *parent = 0);
|
||||
~ModelWidget();
|
||||
|
@ -34,6 +35,7 @@ public:
|
|||
{
|
||||
m_transparent = t;
|
||||
}
|
||||
MeshLoader *fetchCurrentMesh();
|
||||
void updateMesh(MeshLoader *mesh);
|
||||
void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions);
|
||||
void toggleWireframe();
|
||||
|
@ -48,6 +50,10 @@ public:
|
|||
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
||||
bool inputMouseReleaseEventFromOtherWidget(QMouseEvent *event);
|
||||
QPoint convertInputPosFromOtherWidget(QMouseEvent *event);
|
||||
void fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||
int widthInPixels();
|
||||
int heightInPixels();
|
||||
public slots:
|
||||
void setXRotation(int angle);
|
||||
void setYRotation(int angle);
|
||||
|
@ -98,6 +104,8 @@ private:
|
|||
QRect m_moveStartGeometry;
|
||||
int m_modelInitialHeight = 0;
|
||||
QTimer *m_rotationTimer = nullptr;
|
||||
int m_widthInPixels = 0;
|
||||
int m_heightInPixels = 0;
|
||||
std::pair<QVector3D, QVector3D> screenPositionToMouseRay(const QPoint &screenPosition);
|
||||
public:
|
||||
static int m_defaultXRotation;
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
#include "normalanddepthmapsgenerator.h"
|
||||
|
||||
NormalAndDepthMapsGenerator::NormalAndDepthMapsGenerator(ModelWidget *modelWidget)
|
||||
{
|
||||
m_viewPortSize = QSize(modelWidget->widthInPixels(),
|
||||
modelWidget->heightInPixels());
|
||||
m_normalMapRender = createOfflineRender(modelWidget, 1);
|
||||
m_depthMapRender = createOfflineRender(modelWidget, 2);
|
||||
}
|
||||
|
||||
void NormalAndDepthMapsGenerator::updateMesh(MeshLoader *mesh)
|
||||
{
|
||||
if (nullptr == mesh) {
|
||||
m_normalMapRender->updateMesh(nullptr);
|
||||
m_depthMapRender->updateMesh(nullptr);
|
||||
return;
|
||||
}
|
||||
m_normalMapRender->updateMesh(new MeshLoader(*mesh));
|
||||
m_depthMapRender->updateMesh(mesh);
|
||||
}
|
||||
|
||||
void NormalAndDepthMapsGenerator::setRenderThread(QThread *thread)
|
||||
{
|
||||
//m_normalMapRender->setRenderThread(thread);
|
||||
//m_depthMapRender->setRenderThread(thread);
|
||||
}
|
||||
|
||||
ModelOfflineRender *NormalAndDepthMapsGenerator::createOfflineRender(ModelWidget *modelWidget, int purpose)
|
||||
{
|
||||
ModelOfflineRender *offlineRender = new ModelOfflineRender(modelWidget->format());
|
||||
offlineRender->setXRotation(modelWidget->xRot());
|
||||
offlineRender->setYRotation(modelWidget->yRot());
|
||||
offlineRender->setZRotation(modelWidget->zRot());
|
||||
offlineRender->setRenderPurpose(purpose);
|
||||
return offlineRender;
|
||||
}
|
||||
|
||||
NormalAndDepthMapsGenerator::~NormalAndDepthMapsGenerator()
|
||||
{
|
||||
delete m_normalMapRender;
|
||||
delete m_depthMapRender;
|
||||
delete m_normalMap;
|
||||
delete m_depthMap;
|
||||
}
|
||||
|
||||
void NormalAndDepthMapsGenerator::generate()
|
||||
{
|
||||
m_normalMap = new QImage(m_normalMapRender->toImage(m_viewPortSize));
|
||||
m_depthMap = new QImage(m_depthMapRender->toImage(m_viewPortSize));
|
||||
}
|
||||
|
||||
void NormalAndDepthMapsGenerator::process()
|
||||
{
|
||||
generate();
|
||||
emit finished();
|
||||
}
|
||||
|
||||
QImage *NormalAndDepthMapsGenerator::takeNormalMap()
|
||||
{
|
||||
QImage *normalMap = m_normalMap;
|
||||
m_normalMap = nullptr;
|
||||
return normalMap;
|
||||
}
|
||||
|
||||
QImage *NormalAndDepthMapsGenerator::takeDepthMap()
|
||||
{
|
||||
QImage *depthMap = m_depthMap;
|
||||
m_depthMap = nullptr;
|
||||
return depthMap;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef DUST3D_NORMAL_AND_DEPTH_MAPS_GENERATOR_H
|
||||
#define DUST3D_NORMAL_AND_DEPTH_MAPS_GENERATOR_H
|
||||
#include <QObject>
|
||||
#include <QImage>
|
||||
#include "modelwidget.h"
|
||||
#include "modelofflinerender.h"
|
||||
#include "meshloader.h"
|
||||
|
||||
class NormalAndDepthMapsGenerator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NormalAndDepthMapsGenerator(ModelWidget *modelWidget);
|
||||
void updateMesh(MeshLoader *mesh);
|
||||
void setRenderThread(QThread *thread);
|
||||
~NormalAndDepthMapsGenerator();
|
||||
QImage *takeNormalMap();
|
||||
QImage *takeDepthMap();
|
||||
signals:
|
||||
void finished();
|
||||
public slots:
|
||||
void process();
|
||||
private:
|
||||
ModelOfflineRender *m_normalMapRender = nullptr;
|
||||
ModelOfflineRender *m_depthMapRender = nullptr;
|
||||
QSize m_viewPortSize;
|
||||
QImage *m_normalMap = nullptr;
|
||||
QImage *m_depthMap = nullptr;
|
||||
|
||||
ModelOfflineRender *createOfflineRender(ModelWidget *modelWidget, int purpose);
|
||||
void generate();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -14,8 +14,8 @@ void Preferences::loadDefault()
|
|||
{
|
||||
m_componentCombineMode = CombineMode::Normal;
|
||||
m_partColor = Qt::white;
|
||||
m_flatShading = true;
|
||||
m_tongShading = false;
|
||||
m_flatShading = false;
|
||||
m_toonShading = false;
|
||||
m_textureSize = 1024;
|
||||
}
|
||||
|
||||
|
@ -40,11 +40,11 @@ Preferences::Preferences()
|
|||
m_flatShading = isTrueValueString(value);
|
||||
}
|
||||
{
|
||||
QString value = m_settings.value("tongShading").toString();
|
||||
QString value = m_settings.value("toonShading").toString();
|
||||
if (value.isEmpty())
|
||||
m_tongShading = false;
|
||||
m_toonShading = false;
|
||||
else
|
||||
m_tongShading = isTrueValueString(value);
|
||||
m_toonShading = isTrueValueString(value);
|
||||
}
|
||||
{
|
||||
QString value = m_settings.value("textureSize").toString();
|
||||
|
@ -68,9 +68,9 @@ bool Preferences::flatShading() const
|
|||
return m_flatShading;
|
||||
}
|
||||
|
||||
bool Preferences::tongShading() const
|
||||
bool Preferences::toonShading() const
|
||||
{
|
||||
return m_tongShading;
|
||||
return m_toonShading;
|
||||
}
|
||||
|
||||
int Preferences::textureSize() const
|
||||
|
@ -105,13 +105,13 @@ void Preferences::setFlatShading(bool flatShading)
|
|||
emit flatShadingChanged();
|
||||
}
|
||||
|
||||
void Preferences::setTongShading(bool tongShading)
|
||||
void Preferences::setToonShading(bool toonShading)
|
||||
{
|
||||
if (m_tongShading == tongShading)
|
||||
if (m_toonShading == toonShading)
|
||||
return;
|
||||
m_tongShading = tongShading;
|
||||
m_settings.setValue("tongShading", tongShading ? "true" : "false");
|
||||
emit tongShadingChanged();
|
||||
m_toonShading = toonShading;
|
||||
m_settings.setValue("toonShading", toonShading ? "true" : "false");
|
||||
emit toonShadingChanged();
|
||||
}
|
||||
|
||||
void Preferences::setTextureSize(int textureSize)
|
||||
|
@ -140,6 +140,6 @@ void Preferences::reset()
|
|||
emit componentCombineModeChanged();
|
||||
emit partColorChanged();
|
||||
emit flatShadingChanged();
|
||||
emit tongShadingChanged();
|
||||
emit toonShadingChanged();
|
||||
emit textureSizeChanged();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ public:
|
|||
CombineMode componentCombineMode() const;
|
||||
const QColor &partColor() const;
|
||||
bool flatShading() const;
|
||||
bool tongShading() const;
|
||||
bool toonShading() const;
|
||||
QSize documentWindowSize() const;
|
||||
void setDocumentWindowSize(const QSize&);
|
||||
int textureSize() const;
|
||||
|
@ -22,20 +22,20 @@ signals:
|
|||
void componentCombineModeChanged();
|
||||
void partColorChanged();
|
||||
void flatShadingChanged();
|
||||
void tongShadingChanged();
|
||||
void toonShadingChanged();
|
||||
void textureSizeChanged();
|
||||
public slots:
|
||||
void setComponentCombineMode(CombineMode mode);
|
||||
void setPartColor(const QColor &color);
|
||||
void setFlatShading(bool flatShading);
|
||||
void setTongShading(bool tongShading);
|
||||
void setToonShading(bool toonShading);
|
||||
void setTextureSize(int textureSize);
|
||||
void reset();
|
||||
private:
|
||||
CombineMode m_componentCombineMode;
|
||||
QColor m_partColor;
|
||||
bool m_flatShading;
|
||||
bool m_tongShading;
|
||||
bool m_toonShading;
|
||||
QSettings m_settings;
|
||||
int m_textureSize;
|
||||
private:
|
||||
|
|
|
@ -65,10 +65,10 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent)
|
|||
Preferences::instance().setFlatShading(flatShadingBox->isChecked());
|
||||
});
|
||||
|
||||
QCheckBox *tongShadingBox = new QCheckBox();
|
||||
Theme::initCheckbox(tongShadingBox);
|
||||
connect(tongShadingBox, &QCheckBox::stateChanged, this, [=]() {
|
||||
Preferences::instance().setTongShading(tongShadingBox->isChecked());
|
||||
QCheckBox *toonShadingBox = new QCheckBox();
|
||||
Theme::initCheckbox(toonShadingBox);
|
||||
connect(toonShadingBox, &QCheckBox::stateChanged, this, [=]() {
|
||||
Preferences::instance().setToonShading(toonShadingBox->isChecked());
|
||||
});
|
||||
|
||||
QComboBox *textureSizeSelectBox = new QComboBox;
|
||||
|
@ -84,14 +84,14 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent)
|
|||
formLayout->addRow(tr("Part color:"), colorLayout);
|
||||
formLayout->addRow(tr("Combine mode:"), combineModeSelectBox);
|
||||
formLayout->addRow(tr("Flat shading:"), flatShadingBox);
|
||||
formLayout->addRow(tr("Tong shading:"), tongShadingBox);
|
||||
formLayout->addRow(tr("Toon shading:"), toonShadingBox);
|
||||
formLayout->addRow(tr("Texture size:"), textureSizeSelectBox);
|
||||
|
||||
auto loadFromPreferences = [=]() {
|
||||
updatePickButtonColor();
|
||||
combineModeSelectBox->setCurrentIndex((int)Preferences::instance().componentCombineMode());
|
||||
flatShadingBox->setChecked(Preferences::instance().flatShading());
|
||||
tongShadingBox->setChecked(Preferences::instance().tongShading());
|
||||
toonShadingBox->setChecked(Preferences::instance().toonShading());
|
||||
textureSizeSelectBox->setCurrentIndex(
|
||||
textureSizeSelectBox->findText(QString::number(Preferences::instance().textureSize()))
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue