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
|
INCLUDEPATH += src
|
||||||
|
|
||||||
|
SOURCES += src/normalanddepthmapsgenerator.cpp
|
||||||
|
HEADERS += src/normalanddepthmapsgenerator.h
|
||||||
|
|
||||||
|
SOURCES += src/modelofflinerender.cpp
|
||||||
|
HEADERS += src/modelofflinerender.h
|
||||||
|
|
||||||
SOURCES += src/modelshaderprogram.cpp
|
SOURCES += src/modelshaderprogram.cpp
|
||||||
HEADERS += src/modelshaderprogram.h
|
HEADERS += src/modelshaderprogram.h
|
||||||
|
|
||||||
|
|
|
@ -391,6 +391,14 @@ Consejos:
|
||||||
<source>Rotation</source>
|
<source>Rotation</source>
|
||||||
<translation>Rotación</translation>
|
<translation>Rotación</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export as Image...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image (*.png)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ExportPreviewWidget</name>
|
<name>ExportPreviewWidget</name>
|
||||||
|
@ -969,7 +977,7 @@ Consejos:
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Tong shading:</source>
|
<source>Toon shading:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
|
|
@ -71,8 +71,8 @@ Tips:
|
||||||
- Make multiple parts instead of one single part for whole model</source>
|
- 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
|
<translation>Generazione mesh non riuscita, annulla o modifica i nodi modificati di recente
|
||||||
Suggerimenti:
|
Suggerimenti:
|
||||||
- Non lasciare che la mesh generata si intersechi in se stessa
|
   - Non lasciare che la mesh generata si intersechi in se stessa
|
||||||
- Crea più parti invece di una singola parte per l'intero modello</translation>
|
   - Crea più parti invece di una singola parte per l'intero modello</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Parts</source>
|
<source>Parts</source>
|
||||||
|
@ -398,6 +398,14 @@ Suggerimenti:
|
||||||
<source>Enter zoom out mode</source>
|
<source>Enter zoom out mode</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export as Image...</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image (*.png)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ExportPreviewWidget</name>
|
<name>ExportPreviewWidget</name>
|
||||||
|
@ -976,7 +984,7 @@ Suggerimenti:
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Tong shading:</source>
|
<source>Toon shading:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
|
|
@ -391,6 +391,14 @@ Tips:
|
||||||
<source>Script</source>
|
<source>Script</source>
|
||||||
<translation>脚本</translation>
|
<translation>脚本</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Export as Image...</source>
|
||||||
|
<translation>导出成图片...</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Image (*.png)</source>
|
||||||
|
<translation>图片 (*.png)</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ExportPreviewWidget</name>
|
<name>ExportPreviewWidget</name>
|
||||||
|
@ -969,7 +977,7 @@ Tips:
|
||||||
<translation>纹理大小:</translation>
|
<translation>纹理大小:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Tong shading:</source>
|
<source>Toon shading:</source>
|
||||||
<translation>卡通渲染</translation>
|
<translation>卡通渲染</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
|
|
@ -86,7 +86,13 @@ uniform samplerCube environmentIrradianceMapId;
|
||||||
uniform int environmentIrradianceMapEnabled;
|
uniform int environmentIrradianceMapEnabled;
|
||||||
uniform samplerCube environmentSpecularMapId;
|
uniform samplerCube environmentSpecularMapId;
|
||||||
uniform int environmentSpecularMapEnabled;
|
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 MAX_LIGHTS = 8;
|
||||||
const int TYPE_POINT = 0;
|
const int TYPE_POINT = 0;
|
||||||
|
@ -106,6 +112,122 @@ struct Light {
|
||||||
int lightCount;
|
int lightCount;
|
||||||
Light lights[MAX_LIGHTS];
|
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 mipLevelCount(const in samplerCube cube)
|
||||||
{
|
{
|
||||||
int baseSize = textureSize(cube, 0).x;
|
int baseSize = textureSize(cube, 0).x;
|
||||||
|
@ -385,7 +507,7 @@ vec4 metalRoughFunction(const in vec4 baseColor,
|
||||||
// Remap roughness for a perceptually more linear correspondence
|
// Remap roughness for a perceptually more linear correspondence
|
||||||
float alpha = remapRoughness(roughness);
|
float alpha = remapRoughness(roughness);
|
||||||
|
|
||||||
if (tongShadingEnabled != 1) {
|
if (toonShadingEnabled != 1) {
|
||||||
if (environmentIrradianceMapEnabled == 1) {
|
if (environmentIrradianceMapEnabled == 1) {
|
||||||
cLinear += pbrIblModel(worldNormal,
|
cLinear += pbrIblModel(worldNormal,
|
||||||
worldView,
|
worldView,
|
||||||
|
@ -414,6 +536,14 @@ vec4 metalRoughFunction(const in vec4 baseColor,
|
||||||
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 2.0));
|
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 2.0));
|
||||||
else
|
else
|
||||||
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.1));
|
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
|
// Apply exposure correction
|
||||||
|
@ -509,6 +639,7 @@ void main()
|
||||||
metalness = min(0.99, metalness);
|
metalness = min(0.99, metalness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (renderPurpose == 0) {
|
||||||
fragColor = metalRoughFunction(vec4(color, alpha),
|
fragColor = metalRoughFunction(vec4(color, alpha),
|
||||||
metalness,
|
metalness,
|
||||||
roughness,
|
roughness,
|
||||||
|
@ -516,4 +647,9 @@ void main()
|
||||||
vert,
|
vert,
|
||||||
normalize(cameraPos - vert),
|
normalize(cameraPos - vert),
|
||||||
normal);
|
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).
|
** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
|
||||||
|
@ -80,7 +81,13 @@ uniform int ambientOcclusionMapEnabled;
|
||||||
uniform int mousePickEnabled;
|
uniform int mousePickEnabled;
|
||||||
uniform vec3 mousePickTargetPosition;
|
uniform vec3 mousePickTargetPosition;
|
||||||
uniform float mousePickRadius;
|
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 MAX_LIGHTS = 8;
|
||||||
const int TYPE_POINT = 0;
|
const int TYPE_POINT = 0;
|
||||||
|
@ -100,6 +107,122 @@ struct Light {
|
||||||
int lightCount;
|
int lightCount;
|
||||||
Light lights[MAX_LIGHTS];
|
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)
|
float remapRoughness(const in float roughness)
|
||||||
{
|
{
|
||||||
// As per page 14 of
|
// As per page 14 of
|
||||||
|
@ -281,7 +404,7 @@ vec4 metalRoughFunction(const in vec4 baseColor,
|
||||||
// Remap roughness for a perceptually more linear correspondence
|
// Remap roughness for a perceptually more linear correspondence
|
||||||
float alpha = remapRoughness(roughness);
|
float alpha = remapRoughness(roughness);
|
||||||
|
|
||||||
if (tongShadingEnabled != 1) {
|
if (toonShadingEnabled != 1) {
|
||||||
for (int i = 0; i < lightCount; ++i) {
|
for (int i = 0; i < lightCount; ++i) {
|
||||||
cLinear += pbrModel(i,
|
cLinear += pbrModel(i,
|
||||||
worldPosition,
|
worldPosition,
|
||||||
|
@ -301,6 +424,14 @@ vec4 metalRoughFunction(const in vec4 baseColor,
|
||||||
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 2.0));
|
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 2.0));
|
||||||
else
|
else
|
||||||
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.1));
|
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
|
// Apply exposure correction
|
||||||
|
@ -394,6 +525,7 @@ void main()
|
||||||
roughness = min(0.99, roughness);
|
roughness = min(0.99, roughness);
|
||||||
metalness = min(0.99, metalness);
|
metalness = min(0.99, metalness);
|
||||||
|
|
||||||
|
if (renderPurpose == 0) {
|
||||||
gl_FragColor = metalRoughFunction(vec4(color, alpha),
|
gl_FragColor = metalRoughFunction(vec4(color, alpha),
|
||||||
metalness,
|
metalness,
|
||||||
roughness,
|
roughness,
|
||||||
|
@ -401,4 +533,9 @@ void main()
|
||||||
vert,
|
vert,
|
||||||
normalize(cameraPos - vert),
|
normalize(cameraPos - vert),
|
||||||
normal);
|
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 vec4 vertex;
|
||||||
attribute vec3 normal;
|
attribute vec3 normal;
|
||||||
attribute vec3 color;
|
attribute vec3 color;
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "scriptwidget.h"
|
#include "scriptwidget.h"
|
||||||
#include "variablesxml.h"
|
#include "variablesxml.h"
|
||||||
#include "updatescheckwidget.h"
|
#include "updatescheckwidget.h"
|
||||||
|
#include "modelofflinerender.h"
|
||||||
|
|
||||||
int DocumentWindow::m_modelRenderWidgetInitialX = 16;
|
int DocumentWindow::m_modelRenderWidgetInitialX = 16;
|
||||||
int DocumentWindow::m_modelRenderWidgetInitialY = 16;
|
int DocumentWindow::m_modelRenderWidgetInitialY = 16;
|
||||||
|
@ -310,6 +311,7 @@ DocumentWindow::DocumentWindow() :
|
||||||
m_modelRenderWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
m_modelRenderWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
m_modelRenderWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY);
|
m_modelRenderWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY);
|
||||||
m_modelRenderWidget->setMousePickRadius(m_document->mousePickRadius());
|
m_modelRenderWidget->setMousePickRadius(m_document->mousePickRadius());
|
||||||
|
if (!Preferences::instance().toonShading())
|
||||||
m_modelRenderWidget->toggleWireframe();
|
m_modelRenderWidget->toggleWireframe();
|
||||||
m_modelRenderWidget->enableEnvironmentLight();
|
m_modelRenderWidget->enableEnvironmentLight();
|
||||||
|
|
||||||
|
@ -341,6 +343,8 @@ DocumentWindow::DocumentWindow() :
|
||||||
m_modelRenderWidget->setMousePickTargetPositionInModelSpace(m_document->mouseTargetPosition());
|
m_modelRenderWidget->setMousePickTargetPositionInModelSpace(m_document->mouseTargetPosition());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(m_modelRenderWidget, &ModelWidget::renderParametersChanged, this, &DocumentWindow::delayedGenerateNormalAndDepthMaps);
|
||||||
|
|
||||||
m_graphicsWidget->setModelWidget(m_modelRenderWidget);
|
m_graphicsWidget->setModelWidget(m_modelRenderWidget);
|
||||||
containerWidget->setModelWidget(m_modelRenderWidget);
|
containerWidget->setModelWidget(m_modelRenderWidget);
|
||||||
|
|
||||||
|
@ -494,9 +498,9 @@ DocumentWindow::DocumentWindow() :
|
||||||
connect(m_exportAsObjAction, &QAction::triggered, this, &DocumentWindow::exportObjResult, Qt::QueuedConnection);
|
connect(m_exportAsObjAction, &QAction::triggered, this, &DocumentWindow::exportObjResult, Qt::QueuedConnection);
|
||||||
m_fileMenu->addAction(m_exportAsObjAction);
|
m_fileMenu->addAction(m_exportAsObjAction);
|
||||||
|
|
||||||
//m_exportRenderedAsImageAction = new QAction(tr("Export as PNG..."), this);
|
m_exportRenderedAsImageAction = new QAction(tr("Export as Image..."), this);
|
||||||
//connect(m_exportRenderedAsImageAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportRenderedResult, Qt::QueuedConnection);
|
connect(m_exportRenderedAsImageAction, &QAction::triggered, this, &DocumentWindow::exportRenderedResult, Qt::QueuedConnection);
|
||||||
//m_fileMenu->addAction(m_exportRenderedAsImageAction);
|
m_fileMenu->addAction(m_exportRenderedAsImageAction);
|
||||||
|
|
||||||
//m_exportAsObjPlusMaterialsAction = new QAction(tr("Wavefront (.obj + .mtl)..."), this);
|
//m_exportAsObjPlusMaterialsAction = new QAction(tr("Wavefront (.obj + .mtl)..."), this);
|
||||||
//connect(m_exportAsObjPlusMaterialsAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportObjPlusMaterialsResult, Qt::QueuedConnection);
|
//connect(m_exportAsObjPlusMaterialsAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportObjPlusMaterialsResult, Qt::QueuedConnection);
|
||||||
|
@ -518,7 +522,7 @@ DocumentWindow::DocumentWindow() :
|
||||||
m_exportAsObjAction->setEnabled(m_graphicsWidget->hasItems());
|
m_exportAsObjAction->setEnabled(m_graphicsWidget->hasItems());
|
||||||
//m_exportAsObjPlusMaterialsAction->setEnabled(m_graphicsWidget->hasItems());
|
//m_exportAsObjPlusMaterialsAction->setEnabled(m_graphicsWidget->hasItems());
|
||||||
m_exportAction->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"));
|
m_editMenu = menuBar()->addMenu(tr("&Edit"));
|
||||||
|
@ -1603,6 +1607,16 @@ void DocumentWindow::showPreferences()
|
||||||
m_preferencesWidget->raise();
|
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()
|
void DocumentWindow::exportObjResult()
|
||||||
{
|
{
|
||||||
QString filename = QFileDialog::getSaveFileName(this, QString(), QString(),
|
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);
|
// 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 <map>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QTimer>
|
||||||
#include "document.h"
|
#include "document.h"
|
||||||
#include "modelwidget.h"
|
#include "modelwidget.h"
|
||||||
#include "exportpreviewwidget.h"
|
#include "exportpreviewwidget.h"
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
#include "posemanagewidget.h"
|
#include "posemanagewidget.h"
|
||||||
#include "preferenceswidget.h"
|
#include "preferenceswidget.h"
|
||||||
#include "graphicscontainerwidget.h"
|
#include "graphicscontainerwidget.h"
|
||||||
|
#include "normalanddepthmapsgenerator.h"
|
||||||
|
|
||||||
class SkeletonGraphicsWidget;
|
class SkeletonGraphicsWidget;
|
||||||
|
|
||||||
|
@ -49,6 +51,7 @@ public slots:
|
||||||
void open();
|
void open();
|
||||||
void openExample(const QString &modelName);
|
void openExample(const QString &modelName);
|
||||||
void openPathAs(const QString &path, const QString &asName);
|
void openPathAs(const QString &path, const QString &asName);
|
||||||
|
void exportRenderedResult();
|
||||||
void exportObjResult();
|
void exportObjResult();
|
||||||
void exportGlbResult();
|
void exportGlbResult();
|
||||||
void exportFbxResult();
|
void exportFbxResult();
|
||||||
|
@ -78,11 +81,15 @@ public slots:
|
||||||
void showCutFaceSettingPopup(const QPoint &globalPos, std::set<QUuid> nodeIds);
|
void showCutFaceSettingPopup(const QPoint &globalPos, std::set<QUuid> nodeIds);
|
||||||
void setExportWaitingList(const QStringList &filenames);
|
void setExportWaitingList(const QStringList &filenames);
|
||||||
void checkExportWaitingList();
|
void checkExportWaitingList();
|
||||||
|
void exportImageToFilename(const QString &filename);
|
||||||
void exportObjToFilename(const QString &filename);
|
void exportObjToFilename(const QString &filename);
|
||||||
void exportFbxToFilename(const QString &filename);
|
void exportFbxToFilename(const QString &filename);
|
||||||
void exportGlbToFilename(const QString &filename);
|
void exportGlbToFilename(const QString &filename);
|
||||||
void toggleRotation();
|
void toggleRotation();
|
||||||
//void updateInfoWidgetPosition();
|
//void updateInfoWidgetPosition();
|
||||||
|
void generateNormalAndDepthMaps();
|
||||||
|
void delayedGenerateNormalAndDepthMaps();
|
||||||
|
void normalAndDepthMapsReady();
|
||||||
private:
|
private:
|
||||||
void initLockButton(QPushButton *button);
|
void initLockButton(QPushButton *button);
|
||||||
void setCurrentFilename(const QString &filename);
|
void setCurrentFilename(const QString &filename);
|
||||||
|
@ -199,6 +206,10 @@ private:
|
||||||
QPushButton *m_radiusLockButton;
|
QPushButton *m_radiusLockButton;
|
||||||
|
|
||||||
QMetaObject::Connection m_partListDockerVisibleSwitchConnection;
|
QMetaObject::Connection m_partListDockerVisibleSwitchConnection;
|
||||||
|
|
||||||
|
NormalAndDepthMapsGenerator *m_normalAndDepthMapsGenerator = nullptr;
|
||||||
|
QTimer *m_normalAndDepthMapsDelayTimer = nullptr;
|
||||||
|
bool m_isNormalAndDepthMapsObsolete = false;
|
||||||
public:
|
public:
|
||||||
static int m_modelRenderWidgetInitialX;
|
static int m_modelRenderWidgetInitialX;
|
||||||
static int m_modelRenderWidgetInitialY;
|
static int m_modelRenderWidgetInitialY;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "logbrowser.h"
|
#include "logbrowser.h"
|
||||||
// Modified from https://wiki.qt.io/Browser_for_QDebug_output
|
// Modified from https://wiki.qt.io/Browser_for_QDebug_output
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
|
#include <stdio.h>
|
||||||
#include "logbrowserdialog.h"
|
#include "logbrowserdialog.h"
|
||||||
|
|
||||||
LogBrowser::LogBrowser(QObject *parent) :
|
LogBrowser::LogBrowser(QObject *parent) :
|
||||||
|
|
|
@ -23,6 +23,12 @@ ModelMeshBinder::~ModelMeshBinder()
|
||||||
delete m_metalnessRoughnessAmbientOcclusionMap;
|
delete m_metalnessRoughnessAmbientOcclusionMap;
|
||||||
delete m_environmentIrradianceMap;
|
delete m_environmentIrradianceMap;
|
||||||
delete m_environmentSpecularMap;
|
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)
|
void ModelMeshBinder::updateMesh(MeshLoader *mesh)
|
||||||
|
@ -61,6 +67,14 @@ void ModelMeshBinder::enableEnvironmentLight()
|
||||||
m_environmentLightEnabled = true;
|
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)
|
void ModelMeshBinder::paint(ModelShaderProgram *program)
|
||||||
{
|
{
|
||||||
MeshLoader *newMesh = nullptr;
|
MeshLoader *newMesh = nullptr;
|
||||||
|
@ -74,6 +88,33 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
||||||
hasNewMesh = true;
|
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);
|
QMutexLocker lock(&m_meshMutex);
|
||||||
if (hasNewMesh) {
|
if (hasNewMesh) {
|
||||||
|
@ -218,6 +259,8 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
||||||
program->setUniformValue(program->metalnessRoughnessAmbientOcclusionMapIdLoc(), 2);
|
program->setUniformValue(program->metalnessRoughnessAmbientOcclusionMapIdLoc(), 2);
|
||||||
program->setUniformValue(program->environmentIrradianceMapIdLoc(), 3);
|
program->setUniformValue(program->environmentIrradianceMapIdLoc(), 3);
|
||||||
program->setUniformValue(program->environmentSpecularMapIdLoc(), 4);
|
program->setUniformValue(program->environmentSpecularMapIdLoc(), 4);
|
||||||
|
program->setUniformValue(program->toonNormalMapIdLoc(), 5);
|
||||||
|
program->setUniformValue(program->toonDepthMapIdLoc(), 6);
|
||||||
if (m_showWireframes) {
|
if (m_showWireframes) {
|
||||||
if (m_renderEdgeVertexCount > 0) {
|
if (m_renderEdgeVertexCount > 0) {
|
||||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoEdge);
|
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoEdge);
|
||||||
|
@ -275,6 +318,11 @@ void ModelMeshBinder::paint(ModelShaderProgram *program)
|
||||||
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 0);
|
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);
|
f->glDrawArrays(GL_TRIANGLES, 0, m_renderTriangleVertexCount);
|
||||||
}
|
}
|
||||||
if (m_toolEnabled) {
|
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()
|
void ModelMeshBinder::cleanup()
|
||||||
{
|
{
|
||||||
if (m_vboTriangle.isCreated())
|
if (m_vboTriangle.isCreated())
|
||||||
|
@ -315,6 +385,10 @@ void ModelMeshBinder::cleanup()
|
||||||
m_environmentIrradianceMap = nullptr;
|
m_environmentIrradianceMap = nullptr;
|
||||||
delete m_environmentSpecularMap;
|
delete m_environmentSpecularMap;
|
||||||
m_environmentSpecularMap = nullptr;
|
m_environmentSpecularMap = nullptr;
|
||||||
|
delete m_toonNormalMap;
|
||||||
|
m_toonNormalMap = nullptr;
|
||||||
|
delete m_toonDepthMap;
|
||||||
|
m_toonDepthMap = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshBinder::showWireframes()
|
void ModelMeshBinder::showWireframes()
|
||||||
|
|
|
@ -13,6 +13,7 @@ class ModelMeshBinder
|
||||||
public:
|
public:
|
||||||
ModelMeshBinder(bool toolEnabled=false);
|
ModelMeshBinder(bool toolEnabled=false);
|
||||||
~ModelMeshBinder();
|
~ModelMeshBinder();
|
||||||
|
MeshLoader *fetchCurrentMesh();
|
||||||
void updateMesh(MeshLoader *mesh);
|
void updateMesh(MeshLoader *mesh);
|
||||||
void initialize();
|
void initialize();
|
||||||
void paint(ModelShaderProgram *program);
|
void paint(ModelShaderProgram *program);
|
||||||
|
@ -25,6 +26,8 @@ public:
|
||||||
void enableEnvironmentLight();
|
void enableEnvironmentLight();
|
||||||
bool isCheckUvEnabled();
|
bool isCheckUvEnabled();
|
||||||
void reloadMesh();
|
void reloadMesh();
|
||||||
|
void fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||||
|
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||||
private:
|
private:
|
||||||
MeshLoader *m_mesh = nullptr;
|
MeshLoader *m_mesh = nullptr;
|
||||||
MeshLoader *m_newMesh = nullptr;
|
MeshLoader *m_newMesh = nullptr;
|
||||||
|
@ -46,6 +49,13 @@ private:
|
||||||
bool m_environmentLightEnabled = false;
|
bool m_environmentLightEnabled = false;
|
||||||
QOpenGLTexture *m_environmentIrradianceMap = nullptr;
|
QOpenGLTexture *m_environmentIrradianceMap = nullptr;
|
||||||
QOpenGLTexture *m_environmentSpecularMap = 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:
|
private:
|
||||||
QOpenGLVertexArrayObject m_vaoTriangle;
|
QOpenGLVertexArrayObject m_vaoTriangle;
|
||||||
QOpenGLBuffer m_vboTriangle;
|
QOpenGLBuffer m_vboTriangle;
|
||||||
|
@ -55,6 +65,7 @@ private:
|
||||||
QOpenGLBuffer m_vboTool;
|
QOpenGLBuffer m_vboTool;
|
||||||
QMutex m_meshMutex;
|
QMutex m_meshMutex;
|
||||||
QMutex m_newMeshMutex;
|
QMutex m_newMeshMutex;
|
||||||
|
QMutex m_toonNormalAndDepthMapMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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_mousePickEnabledLoc = this->uniformLocation("mousePickEnabled");
|
||||||
m_mousePickTargetPositionLoc = this->uniformLocation("mousePickTargetPosition");
|
m_mousePickTargetPositionLoc = this->uniformLocation("mousePickTargetPosition");
|
||||||
m_mousePickRadiusLoc = this->uniformLocation("mousePickRadius");
|
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) {
|
if (m_isCoreProfile) {
|
||||||
m_environmentIrradianceMapIdLoc = this->uniformLocation("environmentIrradianceMapId");
|
m_environmentIrradianceMapIdLoc = this->uniformLocation("environmentIrradianceMapId");
|
||||||
m_environmentIrradianceMapEnabledLoc = this->uniformLocation("environmentIrradianceMapEnabled");
|
m_environmentIrradianceMapEnabledLoc = this->uniformLocation("environmentIrradianceMapEnabled");
|
||||||
|
@ -167,7 +173,37 @@ int ModelShaderProgram::environmentSpecularMapEnabledLoc()
|
||||||
return m_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 environmentIrradianceMapEnabledLoc();
|
||||||
int environmentSpecularMapIdLoc();
|
int environmentSpecularMapIdLoc();
|
||||||
int environmentSpecularMapEnabledLoc();
|
int environmentSpecularMapEnabledLoc();
|
||||||
int tongShadingEnabledLoc();
|
int toonShadingEnabledLoc();
|
||||||
|
int renderPurposeLoc();
|
||||||
|
int toonEdgeEnabledLoc();
|
||||||
|
int screenWidthLoc();
|
||||||
|
int screenHeightLoc();
|
||||||
|
int toonNormalMapIdLoc();
|
||||||
|
int toonDepthMapIdLoc();
|
||||||
bool isCoreProfile();
|
bool isCoreProfile();
|
||||||
static const QString &loadShaderSource(const QString &name);
|
static const QString &loadShaderSource(const QString &name);
|
||||||
private:
|
private:
|
||||||
|
@ -52,7 +58,13 @@ private:
|
||||||
int m_environmentIrradianceMapEnabledLoc = 0;
|
int m_environmentIrradianceMapEnabledLoc = 0;
|
||||||
int m_environmentSpecularMapIdLoc = 0;
|
int m_environmentSpecularMapIdLoc = 0;
|
||||||
int m_environmentSpecularMapEnabledLoc = 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
|
#endif
|
||||||
|
|
|
@ -46,13 +46,18 @@ ModelWidget::ModelWidget(QWidget *parent) :
|
||||||
setFormat(fmt);
|
setFormat(fmt);
|
||||||
}
|
}
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
|
m_widthInPixels = width() * window()->devicePixelRatio();
|
||||||
|
m_heightInPixels = height() * window()->devicePixelRatio();
|
||||||
|
|
||||||
zoom(200);
|
zoom(200);
|
||||||
|
|
||||||
connect(&Preferences::instance(), &Preferences::tongShadingChanged, this, &ModelWidget::reRender);
|
connect(&Preferences::instance(), &Preferences::toonShadingChanged, this, &ModelWidget::reRender);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelWidget::reRender()
|
void ModelWidget::reRender()
|
||||||
{
|
{
|
||||||
|
emit renderParametersChanged();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +87,7 @@ void ModelWidget::setXRotation(int angle)
|
||||||
if (angle != m_xRot) {
|
if (angle != m_xRot) {
|
||||||
m_xRot = angle;
|
m_xRot = angle;
|
||||||
emit xRotationChanged(angle);
|
emit xRotationChanged(angle);
|
||||||
|
emit renderParametersChanged();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +98,7 @@ void ModelWidget::setYRotation(int angle)
|
||||||
if (angle != m_yRot) {
|
if (angle != m_yRot) {
|
||||||
m_yRot = angle;
|
m_yRot = angle;
|
||||||
emit yRotationChanged(angle);
|
emit yRotationChanged(angle);
|
||||||
|
emit renderParametersChanged();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,10 +109,16 @@ void ModelWidget::setZRotation(int angle)
|
||||||
if (angle != m_zRot) {
|
if (angle != m_zRot) {
|
||||||
m_zRot = angle;
|
m_zRot = angle;
|
||||||
emit zRotationChanged(angle);
|
emit zRotationChanged(angle);
|
||||||
|
emit renderParametersChanged();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MeshLoader *ModelWidget::fetchCurrentMesh()
|
||||||
|
{
|
||||||
|
return m_meshBinder.fetchCurrentMesh();
|
||||||
|
}
|
||||||
|
|
||||||
void ModelWidget::cleanup()
|
void ModelWidget::cleanup()
|
||||||
{
|
{
|
||||||
if (m_program == nullptr)
|
if (m_program == nullptr)
|
||||||
|
@ -141,7 +154,7 @@ void ModelWidget::initializeGL()
|
||||||
if (nullptr != versionString &&
|
if (nullptr != versionString &&
|
||||||
'\0' != versionString[0] &&
|
'\0' != versionString[0] &&
|
||||||
0 == strstr(versionString, "Mesa")) {
|
0 == strstr(versionString, "Mesa")) {
|
||||||
isCoreProfile = QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile;
|
isCoreProfile = format().profile() == QSurfaceFormat::CoreProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_program = new ModelShaderProgram(isCoreProfile);
|
m_program = new ModelShaderProgram(isCoreProfile);
|
||||||
|
@ -174,6 +187,7 @@ void ModelWidget::paintGL()
|
||||||
#ifdef GL_LINE_SMOOTH
|
#ifdef GL_LINE_SMOOTH
|
||||||
glEnable(GL_LINE_SMOOTH);
|
glEnable(GL_LINE_SMOOTH);
|
||||||
#endif
|
#endif
|
||||||
|
glViewport(0, 0, m_widthInPixels, m_heightInPixels);
|
||||||
|
|
||||||
m_world.setToIdentity();
|
m_world.setToIdentity();
|
||||||
m_world.rotate(m_xRot / 16.0f, 1, 0, 0);
|
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_world.rotate(m_zRot / 16.0f, 0, 0, 1);
|
||||||
|
|
||||||
m_program->bind();
|
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->projectionMatrixLoc(), m_projection);
|
||||||
m_program->setUniformValue(m_program->modelMatrixLoc(), m_world);
|
m_program->setUniformValue(m_program->modelMatrixLoc(), m_world);
|
||||||
QMatrix3x3 normalMatrix = m_world.normalMatrix();
|
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->viewMatrixLoc(), m_camera);
|
||||||
m_program->setUniformValue(m_program->textureEnabledLoc(), 0);
|
m_program->setUniformValue(m_program->textureEnabledLoc(), 0);
|
||||||
m_program->setUniformValue(m_program->normalMapEnabledLoc(), 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()) {
|
if (m_mousePickingEnabled && !m_mousePickTargetPositionInModelSpace.isNull()) {
|
||||||
m_program->setUniformValue(m_program->mousePickEnabledLoc(), 1);
|
m_program->setUniformValue(m_program->mousePickEnabledLoc(), 1);
|
||||||
|
@ -207,8 +228,11 @@ void ModelWidget::paintGL()
|
||||||
|
|
||||||
void ModelWidget::resizeGL(int w, int h)
|
void ModelWidget::resizeGL(int w, int h)
|
||||||
{
|
{
|
||||||
|
m_widthInPixels = w * window()->devicePixelRatio();
|
||||||
|
m_heightInPixels = h * window()->devicePixelRatio();
|
||||||
m_projection.setToIdentity();
|
m_projection.setToIdentity();
|
||||||
m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
|
m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
|
||||||
|
emit renderParametersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<QVector3D, QVector3D> ModelWidget::screenPositionToMouseRay(const QPoint &screenPosition)
|
std::pair<QVector3D, QVector3D> ModelWidget::screenPositionToMouseRay(const QPoint &screenPosition)
|
||||||
|
@ -383,6 +407,7 @@ void ModelWidget::zoom(float delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setGeometry(geometry().marginsAdded(margins));
|
setGeometry(geometry().marginsAdded(margins));
|
||||||
|
emit renderParametersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelWidget::setMousePickTargetPositionInModelSpace(QVector3D position)
|
void ModelWidget::setMousePickTargetPositionInModelSpace(QVector3D position)
|
||||||
|
@ -400,9 +425,31 @@ void ModelWidget::setMousePickRadius(float radius)
|
||||||
void ModelWidget::updateMesh(MeshLoader *mesh)
|
void ModelWidget::updateMesh(MeshLoader *mesh)
|
||||||
{
|
{
|
||||||
m_meshBinder.updateMesh(mesh);
|
m_meshBinder.updateMesh(mesh);
|
||||||
|
emit renderParametersChanged();
|
||||||
update();
|
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)
|
void ModelWidget::enableMove(bool enabled)
|
||||||
{
|
{
|
||||||
m_moveEnabled = enabled;
|
m_moveEnabled = enabled;
|
||||||
|
|
|
@ -23,6 +23,7 @@ signals:
|
||||||
void mousePressed();
|
void mousePressed();
|
||||||
void mouseReleased();
|
void mouseReleased();
|
||||||
void addMouseRadius(float radius);
|
void addMouseRadius(float radius);
|
||||||
|
void renderParametersChanged();
|
||||||
public:
|
public:
|
||||||
ModelWidget(QWidget *parent = 0);
|
ModelWidget(QWidget *parent = 0);
|
||||||
~ModelWidget();
|
~ModelWidget();
|
||||||
|
@ -34,6 +35,7 @@ public:
|
||||||
{
|
{
|
||||||
m_transparent = t;
|
m_transparent = t;
|
||||||
}
|
}
|
||||||
|
MeshLoader *fetchCurrentMesh();
|
||||||
void updateMesh(MeshLoader *mesh);
|
void updateMesh(MeshLoader *mesh);
|
||||||
void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions);
|
void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions);
|
||||||
void toggleWireframe();
|
void toggleWireframe();
|
||||||
|
@ -48,6 +50,10 @@ public:
|
||||||
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
||||||
bool inputMouseReleaseEventFromOtherWidget(QMouseEvent *event);
|
bool inputMouseReleaseEventFromOtherWidget(QMouseEvent *event);
|
||||||
QPoint convertInputPosFromOtherWidget(QMouseEvent *event);
|
QPoint convertInputPosFromOtherWidget(QMouseEvent *event);
|
||||||
|
void fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||||
|
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
|
||||||
|
int widthInPixels();
|
||||||
|
int heightInPixels();
|
||||||
public slots:
|
public slots:
|
||||||
void setXRotation(int angle);
|
void setXRotation(int angle);
|
||||||
void setYRotation(int angle);
|
void setYRotation(int angle);
|
||||||
|
@ -98,6 +104,8 @@ private:
|
||||||
QRect m_moveStartGeometry;
|
QRect m_moveStartGeometry;
|
||||||
int m_modelInitialHeight = 0;
|
int m_modelInitialHeight = 0;
|
||||||
QTimer *m_rotationTimer = nullptr;
|
QTimer *m_rotationTimer = nullptr;
|
||||||
|
int m_widthInPixels = 0;
|
||||||
|
int m_heightInPixels = 0;
|
||||||
std::pair<QVector3D, QVector3D> screenPositionToMouseRay(const QPoint &screenPosition);
|
std::pair<QVector3D, QVector3D> screenPositionToMouseRay(const QPoint &screenPosition);
|
||||||
public:
|
public:
|
||||||
static int m_defaultXRotation;
|
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_componentCombineMode = CombineMode::Normal;
|
||||||
m_partColor = Qt::white;
|
m_partColor = Qt::white;
|
||||||
m_flatShading = true;
|
m_flatShading = false;
|
||||||
m_tongShading = false;
|
m_toonShading = false;
|
||||||
m_textureSize = 1024;
|
m_textureSize = 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,11 +40,11 @@ Preferences::Preferences()
|
||||||
m_flatShading = isTrueValueString(value);
|
m_flatShading = isTrueValueString(value);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
QString value = m_settings.value("tongShading").toString();
|
QString value = m_settings.value("toonShading").toString();
|
||||||
if (value.isEmpty())
|
if (value.isEmpty())
|
||||||
m_tongShading = false;
|
m_toonShading = false;
|
||||||
else
|
else
|
||||||
m_tongShading = isTrueValueString(value);
|
m_toonShading = isTrueValueString(value);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
QString value = m_settings.value("textureSize").toString();
|
QString value = m_settings.value("textureSize").toString();
|
||||||
|
@ -68,9 +68,9 @@ bool Preferences::flatShading() const
|
||||||
return m_flatShading;
|
return m_flatShading;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::tongShading() const
|
bool Preferences::toonShading() const
|
||||||
{
|
{
|
||||||
return m_tongShading;
|
return m_toonShading;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Preferences::textureSize() const
|
int Preferences::textureSize() const
|
||||||
|
@ -105,13 +105,13 @@ void Preferences::setFlatShading(bool flatShading)
|
||||||
emit flatShadingChanged();
|
emit flatShadingChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setTongShading(bool tongShading)
|
void Preferences::setToonShading(bool toonShading)
|
||||||
{
|
{
|
||||||
if (m_tongShading == tongShading)
|
if (m_toonShading == toonShading)
|
||||||
return;
|
return;
|
||||||
m_tongShading = tongShading;
|
m_toonShading = toonShading;
|
||||||
m_settings.setValue("tongShading", tongShading ? "true" : "false");
|
m_settings.setValue("toonShading", toonShading ? "true" : "false");
|
||||||
emit tongShadingChanged();
|
emit toonShadingChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setTextureSize(int textureSize)
|
void Preferences::setTextureSize(int textureSize)
|
||||||
|
@ -140,6 +140,6 @@ void Preferences::reset()
|
||||||
emit componentCombineModeChanged();
|
emit componentCombineModeChanged();
|
||||||
emit partColorChanged();
|
emit partColorChanged();
|
||||||
emit flatShadingChanged();
|
emit flatShadingChanged();
|
||||||
emit tongShadingChanged();
|
emit toonShadingChanged();
|
||||||
emit textureSizeChanged();
|
emit textureSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public:
|
||||||
CombineMode componentCombineMode() const;
|
CombineMode componentCombineMode() const;
|
||||||
const QColor &partColor() const;
|
const QColor &partColor() const;
|
||||||
bool flatShading() const;
|
bool flatShading() const;
|
||||||
bool tongShading() const;
|
bool toonShading() const;
|
||||||
QSize documentWindowSize() const;
|
QSize documentWindowSize() const;
|
||||||
void setDocumentWindowSize(const QSize&);
|
void setDocumentWindowSize(const QSize&);
|
||||||
int textureSize() const;
|
int textureSize() const;
|
||||||
|
@ -22,20 +22,20 @@ signals:
|
||||||
void componentCombineModeChanged();
|
void componentCombineModeChanged();
|
||||||
void partColorChanged();
|
void partColorChanged();
|
||||||
void flatShadingChanged();
|
void flatShadingChanged();
|
||||||
void tongShadingChanged();
|
void toonShadingChanged();
|
||||||
void textureSizeChanged();
|
void textureSizeChanged();
|
||||||
public slots:
|
public slots:
|
||||||
void setComponentCombineMode(CombineMode mode);
|
void setComponentCombineMode(CombineMode mode);
|
||||||
void setPartColor(const QColor &color);
|
void setPartColor(const QColor &color);
|
||||||
void setFlatShading(bool flatShading);
|
void setFlatShading(bool flatShading);
|
||||||
void setTongShading(bool tongShading);
|
void setToonShading(bool toonShading);
|
||||||
void setTextureSize(int textureSize);
|
void setTextureSize(int textureSize);
|
||||||
void reset();
|
void reset();
|
||||||
private:
|
private:
|
||||||
CombineMode m_componentCombineMode;
|
CombineMode m_componentCombineMode;
|
||||||
QColor m_partColor;
|
QColor m_partColor;
|
||||||
bool m_flatShading;
|
bool m_flatShading;
|
||||||
bool m_tongShading;
|
bool m_toonShading;
|
||||||
QSettings m_settings;
|
QSettings m_settings;
|
||||||
int m_textureSize;
|
int m_textureSize;
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -65,10 +65,10 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent)
|
||||||
Preferences::instance().setFlatShading(flatShadingBox->isChecked());
|
Preferences::instance().setFlatShading(flatShadingBox->isChecked());
|
||||||
});
|
});
|
||||||
|
|
||||||
QCheckBox *tongShadingBox = new QCheckBox();
|
QCheckBox *toonShadingBox = new QCheckBox();
|
||||||
Theme::initCheckbox(tongShadingBox);
|
Theme::initCheckbox(toonShadingBox);
|
||||||
connect(tongShadingBox, &QCheckBox::stateChanged, this, [=]() {
|
connect(toonShadingBox, &QCheckBox::stateChanged, this, [=]() {
|
||||||
Preferences::instance().setTongShading(tongShadingBox->isChecked());
|
Preferences::instance().setToonShading(toonShadingBox->isChecked());
|
||||||
});
|
});
|
||||||
|
|
||||||
QComboBox *textureSizeSelectBox = new QComboBox;
|
QComboBox *textureSizeSelectBox = new QComboBox;
|
||||||
|
@ -84,14 +84,14 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent)
|
||||||
formLayout->addRow(tr("Part color:"), colorLayout);
|
formLayout->addRow(tr("Part color:"), colorLayout);
|
||||||
formLayout->addRow(tr("Combine mode:"), combineModeSelectBox);
|
formLayout->addRow(tr("Combine mode:"), combineModeSelectBox);
|
||||||
formLayout->addRow(tr("Flat shading:"), flatShadingBox);
|
formLayout->addRow(tr("Flat shading:"), flatShadingBox);
|
||||||
formLayout->addRow(tr("Tong shading:"), tongShadingBox);
|
formLayout->addRow(tr("Toon shading:"), toonShadingBox);
|
||||||
formLayout->addRow(tr("Texture size:"), textureSizeSelectBox);
|
formLayout->addRow(tr("Texture size:"), textureSizeSelectBox);
|
||||||
|
|
||||||
auto loadFromPreferences = [=]() {
|
auto loadFromPreferences = [=]() {
|
||||||
updatePickButtonColor();
|
updatePickButtonColor();
|
||||||
combineModeSelectBox->setCurrentIndex((int)Preferences::instance().componentCombineMode());
|
combineModeSelectBox->setCurrentIndex((int)Preferences::instance().componentCombineMode());
|
||||||
flatShadingBox->setChecked(Preferences::instance().flatShading());
|
flatShadingBox->setChecked(Preferences::instance().flatShading());
|
||||||
tongShadingBox->setChecked(Preferences::instance().tongShading());
|
toonShadingBox->setChecked(Preferences::instance().toonShading());
|
||||||
textureSizeSelectBox->setCurrentIndex(
|
textureSizeSelectBox->setCurrentIndex(
|
||||||
textureSizeSelectBox->findText(QString::number(Preferences::instance().textureSize()))
|
textureSizeSelectBox->findText(QString::number(Preferences::instance().textureSize()))
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue