Prepare for new render implementation

master
Jeremy HU 2022-09-19 23:30:03 +10:00
parent eaf29d2c65
commit 58290a9b21
18 changed files with 273 additions and 2535 deletions

View File

@ -157,12 +157,12 @@ HEADERS += sources/mesh_result_post_processor.h
SOURCES += sources/mesh_result_post_processor.cc SOURCES += sources/mesh_result_post_processor.cc
HEADERS += sources/model.h HEADERS += sources/model.h
SOURCES += sources/model.cc SOURCES += sources/model.cc
HEADERS += sources/model_mesh_binder.h
SOURCES += sources/model_mesh_binder.cc
HEADERS += sources/model_offscreen_render.h HEADERS += sources/model_offscreen_render.h
SOURCES += sources/model_offscreen_render.cc SOURCES += sources/model_offscreen_render.cc
HEADERS += sources/model_shader_program.h HEADERS += sources/model_opengl_program.h
SOURCES += sources/model_shader_program.cc SOURCES += sources/model_opengl_program.cc
HEADERS += sources/model_opengl_object.h
SOURCES += sources/model_opengl_object.cc
HEADERS += sources/model_shader_vertex.h HEADERS += sources/model_shader_vertex.h
HEADERS += sources/model_widget.h HEADERS += sources/model_widget.h
SOURCES += sources/model_widget.cc SOURCES += sources/model_widget.cc

View File

@ -1,538 +0,0 @@
#version 110
/****************************************************************************
**
** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
float exposure;
float gamma;
varying vec3 vert;
varying vec3 vertRaw;
varying vec3 vertNormal;
varying vec3 vertColor;
varying vec2 vertTexCoord;
varying float vertMetalness;
varying float vertRoughness;
varying vec3 cameraPos;
varying vec3 firstLightPos;
varying vec3 secondLightPos;
varying vec3 thirdLightPos;
varying float vertAlpha;
uniform sampler2D textureId;
uniform int textureEnabled;
uniform sampler2D normalMapId;
uniform int normalMapEnabled;
uniform sampler2D metalnessRoughnessAmbientOcclusionMapId;
uniform int metalnessMapEnabled;
uniform int roughnessMapEnabled;
uniform int ambientOcclusionMapEnabled;
uniform int mousePickEnabled;
uniform vec3 mousePickTargetPosition;
uniform float mousePickRadius;
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;
const int TYPE_DIRECTIONAL = 1;
const int TYPE_SPOT = 2;
struct Light {
int type;
vec3 position;
vec3 color;
float intensity;
vec3 direction;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
float cutOffAngle;
};
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
// http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
// we remap the roughness to give a more perceptually linear response
// of "bluriness" as a function of the roughness specified by the user.
// r = roughness^2
float maxSpecPower;
float minRoughness;
maxSpecPower = 999999.0;
minRoughness = sqrt(2.0 / (maxSpecPower + 2.0));
return max(roughness * roughness, minRoughness);
}
float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha)
{
// Blinn-Phong approximation - see
// http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
float specPower = 2.0 / (alpha * alpha) - 2.0;
return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
}
vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
{
// Calculate the Fresnel effect value
vec3 f = color;
vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
return clamp(F, f, vec3(1.0));
}
float geometricModel(const in float lDotN,
const in float vDotN,
const in vec3 h)
{
// Implicit geometric model (equal to denominator in specular model).
// This currently assumes that there is no attenuation by geometric shadowing or
// masking according to the microfacet theory.
return lDotN * vDotN;
}
vec3 specularModel(const in vec3 F0,
const in float sDotH,
const in float sDotN,
const in float vDotN,
const in vec3 n,
const in vec3 h)
{
// Clamp sDotN and vDotN to small positive value to prevent the
// denominator in the reflection equation going to infinity. Balance this
// by using the clamped values in the geometric factor function to
// avoid ugly seams in the specular lighting.
float sDotNPrime = max(sDotN, 0.001);
float vDotNPrime = max(vDotN, 0.001);
vec3 F = fresnelFactor(F0, sDotH);
float G = geometricModel(sDotNPrime, vDotNPrime, h);
vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
return clamp(cSpec, vec3(0.0), vec3(1.0));
}
vec3 pbrModel(const in int lightIndex,
const in vec3 wPosition,
const in vec3 wNormal,
const in vec3 wView,
const in vec3 baseColor,
const in float metalness,
const in float alpha,
const in float ambientOcclusion)
{
// Calculate some useful quantities
vec3 n = wNormal;
vec3 s = vec3(0.0);
vec3 v = wView;
vec3 h = vec3(0.0);
float vDotN = dot(v, n);
float sDotN = 0.0;
float sDotH = 0.0;
float att = 1.0;
if (lights[lightIndex].type != TYPE_DIRECTIONAL) {
// Point and Spot lights
vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
s = normalize(sUnnormalized);
// Calculate the attenuation factor
sDotN = dot(s, n);
if (sDotN > 0.0) {
if (lights[lightIndex].constantAttenuation != 0.0
|| lights[lightIndex].linearAttenuation != 0.0
|| lights[lightIndex].quadraticAttenuation != 0.0) {
float dist = length(sUnnormalized);
att = 1.0 / (lights[lightIndex].constantAttenuation +
lights[lightIndex].linearAttenuation * dist +
lights[lightIndex].quadraticAttenuation * dist * dist);
}
// The light direction is in world space already
if (lights[lightIndex].type == TYPE_SPOT) {
// Check if fragment is inside or outside of the spot light cone
if (degrees(acos(dot(-s, lights[lightIndex].direction))) > lights[lightIndex].cutOffAngle)
sDotN = 0.0;
}
}
} else {
// Directional lights
// The light direction is in world space already
s = normalize(-lights[lightIndex].direction);
sDotN = dot(s, n);
}
h = normalize(s + v);
sDotH = dot(s, h);
// Calculate diffuse component
vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
// Calculate specular component
vec3 dielectricColor = vec3(0.04);
vec3 F0 = mix(dielectricColor, baseColor, metalness);
vec3 specularFactor = vec3(0.0);
if (sDotN > 0.0) {
specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h);
specularFactor *= normalDistribution(n, h, alpha);
}
vec3 specularColor = lights[lightIndex].color;
vec3 specular = specularColor * specularFactor;
// Blend between diffuse and specular to conserver energy
vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
// Reduce by ambient occlusion amount
color *= ambientOcclusion;
return color;
}
vec3 toneMap(const in vec3 c)
{
return c / (c + vec3(1.0));
}
vec3 gammaCorrect(const in vec3 color)
{
return pow(color, vec3(1.0 / gamma));
}
// http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec3 rgb2hsv(vec3 c)
{
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
vec4 metalRoughFunction(const in vec4 baseColor,
const in float metalness,
const in float roughness,
const in float ambientOcclusion,
const in vec3 worldPosition,
const in vec3 worldView,
const in vec3 worldNormal)
{
vec3 cLinear = vec3(0.0);
// Remap roughness for a perceptually more linear correspondence
float alpha = remapRoughness(roughness);
if (toonShadingEnabled != 1) {
for (int i = 0; i < lightCount; ++i) {
cLinear += pbrModel(i,
worldPosition,
worldNormal,
worldView,
baseColor.rgb,
metalness,
alpha,
ambientOcclusion);
}
} else {
float intensity;
intensity = dot(vec3(1.0, 1.0, 1.0), worldNormal);
vec3 hsv = rgb2hsv(baseColor.rgb);
if (intensity > 0.966)
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 > 0) {
float depthEdge = depthEdgeSobel();
float normalEdge = normalEdgeSobel();
if (depthEdge >= 0.009 || normalEdge >= 0.7) {
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.02));
} else if (toonEdgeEnabled == 2) {
return vec4(0.0, 0.0, 0.0, 0.0);
}
}
}
// Apply exposure correction
cLinear *= pow(2.0, exposure);
// Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1]
vec3 cToneMapped = toneMap(cLinear);
// Apply gamma correction prior to display
vec3 cGamma = gammaCorrect(cToneMapped);
return vec4(cGamma, baseColor.a);
}
void main()
{
// FIXME: don't hard code here
exposure = 1.0;
gamma = 2.2;
// Light settings:
// https://doc-snapshots.qt.io/qt5-5.12/qt3d-pbr-materials-lights-qml.html
lightCount = 3;
// Key light
lights[0].type = TYPE_POINT;
lights[0].position = firstLightPos;
lights[0].color = vec3(1.0, 1.0, 1.0);
lights[0].intensity = 3.0;
lights[0].constantAttenuation = 0.0;
lights[0].linearAttenuation = 0.0;
lights[0].quadraticAttenuation = 0.0;
// Fill light
lights[1].type = TYPE_POINT;
lights[1].position = secondLightPos;
lights[1].color = vec3(1.0, 1.0, 1.0);
lights[1].intensity = 1.0;
lights[1].constantAttenuation = 0.0;
lights[1].linearAttenuation = 0.0;
lights[1].quadraticAttenuation = 0.0;
// Rim light
lights[2].type = TYPE_POINT;
lights[2].position = thirdLightPos;
lights[2].color = vec3(1.0, 1.0, 1.0);
lights[2].intensity = 0.5;
lights[2].constantAttenuation = 0.0;
lights[2].linearAttenuation = 0.0;
lights[2].quadraticAttenuation = 0.0;
vec3 color = vertColor;
float alpha = vertAlpha;
if (textureEnabled == 1) {
vec4 textColor = texture2D(textureId, vertTexCoord);
color = textColor.rgb;
alpha = textColor.a;
}
if (mousePickEnabled == 1) {
float dist = distance(mousePickTargetPosition, vertRaw);
if (dist <= mousePickRadius && dist >= mousePickRadius * 0.9) {
color = color + vec3(0.99, 0.4, 0.13);
}
}
color = pow(color, vec3(gamma));
vec3 normal = vertNormal;
if (normalMapEnabled == 1) {
normal = texture2D(normalMapId, vertTexCoord).rgb;
normal = normalize(normal * 2.0 - 1.0);
}
// Red: Ambient Occlusion
// Green: Roughness
// Blue: Metallic
float metalness = vertMetalness;
if (metalnessMapEnabled == 1) {
metalness = texture2D(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).b;
}
float roughness = vertRoughness;
if (roughnessMapEnabled == 1) {
roughness = texture2D(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).g;
}
float ambientOcclusion = 1.0;
if (ambientOcclusionMapEnabled == 1) {
ambientOcclusion = texture2D(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).r;
}
roughness = min(0.99, roughness);
metalness = min(0.99, metalness);
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);
}
}

View File

@ -1,68 +0,0 @@
#version 110
attribute vec4 vertex;
attribute vec3 normal;
attribute vec3 color;
attribute vec2 texCoord;
attribute float metalness;
attribute float roughness;
attribute vec3 tangent;
attribute float alpha;
varying vec3 vert;
varying vec3 vertRaw;
varying vec3 vertNormal;
varying vec3 vertColor;
varying vec2 vertTexCoord;
varying float vertMetalness;
varying float vertRoughness;
varying vec3 cameraPos;
varying vec3 firstLightPos;
varying vec3 secondLightPos;
varying vec3 thirdLightPos;
varying float vertAlpha;
uniform mat4 projectionMatrix;
uniform mat4 modelMatrix;
uniform mat3 normalMatrix;
uniform mat4 viewMatrix;
uniform int normalMapEnabled;
uniform vec3 eyePos;
mat3 transpose(mat3 m)
{
return mat3(m[0][0], m[1][0], m[2][0],
m[0][1], m[1][1], m[2][1],
m[0][2], m[1][2], m[2][2]);
}
void main()
{
vert = (modelMatrix * vertex).xyz;
vertRaw = vert;
vertNormal = normalize((modelMatrix * vec4(normal, 1.0)).xyz);
vertColor = color;
vertAlpha = alpha;
cameraPos = eyePos;
firstLightPos = vec3(5.0, 5.0, 5.0);
secondLightPos = vec3(-5.0, 5.0, 5.0);
thirdLightPos = vec3(0.0, -5.0, -5.0);
gl_Position = projectionMatrix * viewMatrix * vec4(vert, 1.0);
if (normalMapEnabled == 1) {
vec3 T = normalize(normalMatrix * tangent);
vec3 N = normalize(normalMatrix * normal);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
mat3 TBN = transpose(mat3(T, B, N));
firstLightPos = TBN * firstLightPos;
secondLightPos = TBN * secondLightPos;
thirdLightPos = TBN * thirdLightPos;
cameraPos = TBN * cameraPos;
vert = TBN * vert;
}
vertTexCoord = texCoord;
vertMetalness = metalness;
vertRoughness = roughness;
}

View File

@ -1,652 +0,0 @@
#version 330
/****************************************************************************
**
** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
float exposure;
float gamma;
in vec3 vert;
in vec3 vertRaw;
in vec3 vertNormal;
in vec3 vertColor;
in vec2 vertTexCoord;
in float vertMetalness;
in float vertRoughness;
in vec3 cameraPos;
in vec3 firstLightPos;
in vec3 secondLightPos;
in vec3 thirdLightPos;
in float vertAlpha;
out vec4 fragColor;
uniform sampler2D textureId;
uniform int textureEnabled;
uniform sampler2D normalMapId;
uniform int normalMapEnabled;
uniform sampler2D metalnessRoughnessAmbientOcclusionMapId;
uniform int metalnessMapEnabled;
uniform int roughnessMapEnabled;
uniform int ambientOcclusionMapEnabled;
uniform int mousePickEnabled;
uniform vec3 mousePickTargetPosition;
uniform float mousePickRadius;
uniform samplerCube environmentIrradianceMapId;
uniform int environmentIrradianceMapEnabled;
uniform samplerCube environmentSpecularMapId;
uniform int environmentSpecularMapEnabled;
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;
const int TYPE_DIRECTIONAL = 1;
const int TYPE_SPOT = 2;
struct Light {
int type;
vec3 position;
vec3 color;
float intensity;
vec3 direction;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
float cutOffAngle;
};
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;
int nMips = int(log2(float(baseSize > 0 ? baseSize : 1))) + 1;
return nMips;
}
float remapRoughness(const in float roughness)
{
// As per page 14 of
// http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
// we remap the roughness to give a more perceptually linear response
// of "bluriness" as a function of the roughness specified by the user.
// r = roughness^2
float maxSpecPower;
float minRoughness;
maxSpecPower = 999999.0;
minRoughness = sqrt(2.0 / (maxSpecPower + 2.0));
return max(roughness * roughness, minRoughness);
}
float alphaToMipLevel(float alpha)
{
float specPower = 2.0 / (alpha * alpha) - 2.0;
// We use the mip level calculation from Lys' default power drop, which in
// turn is a slight modification of that used in Marmoset Toolbag. See
// https://docs.knaldtech.com/doku.php?id=specular_lys for details.
// For now we assume a max specular power of 999999 which gives
// maxGlossiness = 1.
const float k0 = 0.00098;
const float k1 = 0.9921;
float glossiness = (pow(2.0, -10.0 / sqrt(specPower)) - k0) / k1;
// TODO: Optimize by doing this on CPU and set as
// uniform int environmentSpecularMapIdMipLevels say (if present in shader).
// Lookup the number of mips in the specular envmap
int mipLevels = mipLevelCount(environmentSpecularMapId);
// Offset of smallest miplevel we should use (corresponds to specular
// power of 1). I.e. in the 32x32 sized mip.
const float mipOffset = 5.0;
// The final factor is really 1 - g / g_max but as mentioned above g_max
// is 1 by definition here so we can avoid the division. If we make the
// max specular power for the spec map configurable, this will need to
// be handled properly.
float mipLevel = (mipLevels - 1.0 - mipOffset) * (1.0 - glossiness);
return mipLevel;
}
float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha)
{
// Blinn-Phong approximation - see
// http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
float specPower = 2.0 / (alpha * alpha) - 2.0;
return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
}
vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
{
// Calculate the Fresnel effect value
vec3 f = color;
vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
return clamp(F, f, vec3(1.0));
}
float geometricModel(const in float lDotN,
const in float vDotN,
const in vec3 h)
{
// Implicit geometric model (equal to denominator in specular model).
// This currently assumes that there is no attenuation by geometric shadowing or
// masking according to the microfacet theory.
return lDotN * vDotN;
}
vec3 specularModel(const in vec3 F0,
const in float sDotH,
const in float sDotN,
const in float vDotN,
const in vec3 n,
const in vec3 h)
{
// Clamp sDotN and vDotN to small positive value to prevent the
// denominator in the reflection equation going to infinity. Balance this
// by using the clamped values in the geometric factor function to
// avoid ugly seams in the specular lighting.
float sDotNPrime = max(sDotN, 0.001);
float vDotNPrime = max(vDotN, 0.001);
vec3 F = fresnelFactor(F0, sDotH);
float G = geometricModel(sDotNPrime, vDotNPrime, h);
vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
return clamp(cSpec, vec3(0.0), vec3(1.0));
}
vec3 pbrModel(const in int lightIndex,
const in vec3 wPosition,
const in vec3 wNormal,
const in vec3 wView,
const in vec3 baseColor,
const in float metalness,
const in float alpha,
const in float ambientOcclusion)
{
// Calculate some useful quantities
vec3 n = wNormal;
vec3 s = vec3(0.0);
vec3 v = wView;
vec3 h = vec3(0.0);
float vDotN = dot(v, n);
float sDotN = 0.0;
float sDotH = 0.0;
float att = 1.0;
if (lights[lightIndex].type != TYPE_DIRECTIONAL) {
// Point and Spot lights
vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
s = normalize(sUnnormalized);
// Calculate the attenuation factor
sDotN = dot(s, n);
if (sDotN > 0.0) {
if (lights[lightIndex].constantAttenuation != 0.0
|| lights[lightIndex].linearAttenuation != 0.0
|| lights[lightIndex].quadraticAttenuation != 0.0) {
float dist = length(sUnnormalized);
att = 1.0 / (lights[lightIndex].constantAttenuation +
lights[lightIndex].linearAttenuation * dist +
lights[lightIndex].quadraticAttenuation * dist * dist);
}
// The light direction is in world space already
if (lights[lightIndex].type == TYPE_SPOT) {
// Check if fragment is inside or outside of the spot light cone
if (degrees(acos(dot(-s, lights[lightIndex].direction))) > lights[lightIndex].cutOffAngle)
sDotN = 0.0;
}
}
} else {
// Directional lights
// The light direction is in world space already
s = normalize(-lights[lightIndex].direction);
sDotN = dot(s, n);
}
h = normalize(s + v);
sDotH = dot(s, h);
// Calculate diffuse component
vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
// Calculate specular component
vec3 dielectricColor = vec3(0.04);
vec3 F0 = mix(dielectricColor, baseColor, metalness);
vec3 specularFactor = vec3(0.0);
if (sDotN > 0.0) {
specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h);
specularFactor *= normalDistribution(n, h, alpha);
}
vec3 specularColor = lights[lightIndex].color;
vec3 specular = specularColor * specularFactor;
// Blend between diffuse and specular to conserver energy
vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
// Reduce by ambient occlusion amount
color *= ambientOcclusion;
return color;
}
vec3 pbrIblModel(const in vec3 wNormal,
const in vec3 wView,
const in vec3 baseColor,
const in float metalness,
const in float alpha,
const in float ambientOcclusion)
{
// Calculate reflection direction of view vector about surface normal
// vector in world space. This is used in the fragment shader to sample
// from the environment textures for a light source. This is equivalent
// to the l vector for punctual light sources. Armed with this, calculate
// the usual factors needed
vec3 n = wNormal;
vec3 l = reflect(-wView, n);
vec3 v = wView;
vec3 h = normalize(l + v);
float vDotN = dot(v, n);
float lDotN = dot(l, n);
float lDotH = dot(l, h);
// Calculate diffuse component
vec3 diffuseColor = (1.0 - metalness) * baseColor;
vec3 diffuse = diffuseColor * texture(environmentIrradianceMapId, l).rgb;
// Calculate specular component
vec3 dielectricColor = vec3(0.04);
vec3 F0 = mix(dielectricColor, baseColor, metalness);
vec3 specularFactor = specularModel(F0, lDotH, lDotN, vDotN, n, h);
float lod = alphaToMipLevel(alpha);
//#define DEBUG_SPECULAR_LODS
#ifdef DEBUG_SPECULAR_LODS
if (lod > 7.0)
return vec3(1.0, 0.0, 0.0);
else if (lod > 6.0)
return vec3(1.0, 0.333, 0.0);
else if (lod > 5.0)
return vec3(1.0, 1.0, 0.0);
else if (lod > 4.0)
return vec3(0.666, 1.0, 0.0);
else if (lod > 3.0)
return vec3(0.0, 1.0, 0.666);
else if (lod > 2.0)
return vec3(0.0, 0.666, 1.0);
else if (lod > 1.0)
return vec3(0.0, 0.0, 1.0);
else if (lod > 0.0)
return vec3(1.0, 0.0, 1.0);
#endif
vec3 specularSkyColor = textureLod(environmentSpecularMapId, l, lod).rgb;
vec3 specular = specularSkyColor * specularFactor;
// Blend between diffuse and specular to conserve energy
vec3 color = specular + diffuse * (vec3(1.0) - specularFactor);
// Reduce by ambient occlusion amount
color *= ambientOcclusion;
return color;
}
vec3 toneMap(const in vec3 c)
{
return c / (c + vec3(1.0));
}
vec3 gammaCorrect(const in vec3 color)
{
return pow(color, vec3(1.0 / gamma));
}
// http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec3 rgb2hsv(vec3 c)
{
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
vec4 metalRoughFunction(const in vec4 baseColor,
const in float metalness,
const in float roughness,
const in float ambientOcclusion,
const in vec3 worldPosition,
const in vec3 worldView,
const in vec3 worldNormal)
{
vec3 cLinear = vec3(0.0);
// Remap roughness for a perceptually more linear correspondence
float alpha = remapRoughness(roughness);
if (toonShadingEnabled != 1) {
if (environmentIrradianceMapEnabled == 1) {
cLinear += pbrIblModel(worldNormal,
worldView,
baseColor.rgb,
metalness,
alpha,
ambientOcclusion);
}
for (int i = 0; i < lightCount; ++i) {
cLinear += pbrModel(i,
worldPosition,
worldNormal,
worldView,
baseColor.rgb,
metalness,
alpha,
ambientOcclusion);
}
} else {
float intensity;
intensity = dot(vec3(1.0, 1.0, 1.0), worldNormal);
vec3 hsv = rgb2hsv(baseColor.rgb);
if (intensity > 0.966)
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 > 0) {
float depthEdge = depthEdgeSobel();
float normalEdge = normalEdgeSobel();
if (depthEdge >= 0.009 || normalEdge >= 0.7) {
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.02));
} else if (toonEdgeEnabled == 2) {
return vec4(0.0, 0.0, 0.0, 0.0);
}
}
}
// Apply exposure correction
cLinear *= pow(2.0, exposure);
// Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1]
vec3 cToneMapped = toneMap(cLinear);
// Apply gamma correction prior to display
vec3 cGamma = gammaCorrect(cToneMapped);
return vec4(cGamma, baseColor.a);
}
void main()
{
// FIXME: don't hard code here
exposure = 1.0;
gamma = 2.2;
// Light settings:
// https://doc-snapshots.qt.io/qt5-5.12/qt3d-pbr-materials-lights-qml.html
lightCount = 3;
// Key light
lights[0].type = TYPE_POINT;
lights[0].position = firstLightPos;
lights[0].color = vec3(1.0, 1.0, 1.0);
lights[0].intensity = 3.0;
lights[0].constantAttenuation = 1.0;
lights[0].linearAttenuation = 0.0;
lights[0].quadraticAttenuation = 0.0025;
// Fill light
lights[1].type = TYPE_POINT;
lights[1].position = secondLightPos;
lights[1].color = vec3(1.0, 1.0, 1.0);
lights[1].intensity = 0.1;
lights[1].constantAttenuation = 0.0;
lights[1].linearAttenuation = 0.0;
lights[1].quadraticAttenuation = 0.0;
// Rim light
lights[2].type = TYPE_POINT;
lights[2].position = thirdLightPos;
lights[2].color = vec3(1.0, 1.0, 1.0);
lights[2].intensity = 0.05;
lights[2].constantAttenuation = 0.0;
lights[2].linearAttenuation = 0.0;
lights[2].quadraticAttenuation = 0.0;
vec3 color = vertColor;
float alpha = vertAlpha;
if (textureEnabled == 1) {
vec4 textColor = texture(textureId, vertTexCoord);
color = textColor.rgb;
alpha = textColor.a;
}
if (mousePickEnabled == 1) {
float dist = distance(mousePickTargetPosition, vertRaw);
if (dist <= mousePickRadius && dist >= mousePickRadius * 0.9) {
color = color + vec3(0.99, 0.4, 0.13);
}
}
color = pow(color, vec3(gamma));
vec3 normal = vertNormal;
if (normalMapEnabled == 1) {
normal = texture(normalMapId, vertTexCoord).rgb;
normal = normalize(normal * 2.0 - 1.0);
}
// Red: Ambient Occlusion
// Green: Roughness
// Blue: Metallic
float metalness = vertMetalness;
if (metalnessMapEnabled == 1) {
metalness = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).b;
}
float roughness = vertRoughness;
if (roughnessMapEnabled == 1) {
roughness = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).g;
}
float ambientOcclusion = 1.0;
if (ambientOcclusionMapEnabled == 1) {
ambientOcclusion = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).r;
}
roughness = min(0.99, roughness);
if (environmentIrradianceMapEnabled != 1) {
metalness = min(0.99, metalness);
}
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);
}
}

View File

@ -1,68 +0,0 @@
#version 330
layout(location = 0) in vec4 vertex;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec3 color;
layout(location = 3) in vec2 texCoord;
layout(location = 4) in float metalness;
layout(location = 5) in float roughness;
layout(location = 6) in vec3 tangent;
layout(location = 7) in float alpha;
out vec3 vert;
out vec3 vertRaw;
out vec3 vertNormal;
out vec3 vertColor;
out vec2 vertTexCoord;
out float vertMetalness;
out float vertRoughness;
out vec3 cameraPos;
out vec3 firstLightPos;
out vec3 secondLightPos;
out vec3 thirdLightPos;
out float vertAlpha;
uniform mat4 projectionMatrix;
uniform mat4 modelMatrix;
uniform mat3 normalMatrix;
uniform mat4 viewMatrix;
uniform int normalMapEnabled;
uniform vec3 eyePos;
mat3 transpose(mat3 m)
{
return mat3(m[0][0], m[1][0], m[2][0],
m[0][1], m[1][1], m[2][1],
m[0][2], m[1][2], m[2][2]);
}
void main()
{
vert = (modelMatrix * vertex).xyz;
vertRaw = vert;
vertNormal = normalize((modelMatrix * vec4(normal, 1.0)).xyz);
vertColor = color;
vertAlpha = alpha;
cameraPos = eyePos;
firstLightPos = vec3(5.0, 5.0, 5.0);
secondLightPos = vec3(-5.0, 5.0, 5.0);
thirdLightPos = vec3(0.0, -5.0, -5.0);
gl_Position = projectionMatrix * viewMatrix * vec4(vert, 1.0);
if (normalMapEnabled == 1) {
vec3 T = normalize(normalMatrix * tangent);
vec3 N = normalize(normalMatrix * normal);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
mat3 TBN = transpose(mat3(T, B, N));
firstLightPos = TBN * firstLightPos;
secondLightPos = TBN * secondLightPos;
thirdLightPos = TBN * thirdLightPos;
cameraPos = TBN * cameraPos;
vert = TBN * vert;
}
vertTexCoord = texCoord;
vertMetalness = metalness;
vertRoughness = roughness;
}

View File

@ -1,448 +0,0 @@
#include <QMutexLocker>
#include <QFile>
#include <QTextStream>
#include <QFileInfo>
#include <map>
#include <QDebug>
#include <QDir>
#include <QSurfaceFormat>
#include "model_mesh_binder.h"
#include "dds_file.h"
ModelMeshBinder::ModelMeshBinder(bool toolEnabled) :
m_toolEnabled(toolEnabled)
{
}
ModelMeshBinder::~ModelMeshBinder()
{
delete m_mesh;
delete m_newMesh;
delete m_texture;
delete m_normalMap;
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;
delete m_colorTextureImage;
}
void ModelMeshBinder::updateMesh(Model *mesh)
{
QMutexLocker lock(&m_newMeshMutex);
if (mesh != m_mesh) {
delete m_newMesh;
m_newMesh = mesh;
m_newMeshComing = true;
}
}
void ModelMeshBinder::updateColorTexture(QImage *colorTextureImage)
{
QMutexLocker lock(&m_colorTextureMutex);
delete m_colorTextureImage;
m_colorTextureImage = colorTextureImage;
}
void ModelMeshBinder::reloadMesh()
{
Model *mesh = nullptr;
{
QMutexLocker lock(&m_newMeshMutex);
if (nullptr == m_mesh)
return;
mesh = new Model(*m_mesh);
}
if (nullptr != mesh)
updateMesh(mesh);
}
void ModelMeshBinder::initialize()
{
m_vaoTriangle.create();
m_vaoEdge.create();
if (m_toolEnabled)
m_vaoTool.create();
}
void ModelMeshBinder::enableEnvironmentLight()
{
m_environmentLightEnabled = true;
}
bool ModelMeshBinder::isEnvironmentLightEnabled()
{
return m_environmentLightEnabled;
}
Model *ModelMeshBinder::fetchCurrentMesh()
{
QMutexLocker lock(&m_meshMutex);
if (nullptr == m_mesh)
return nullptr;
return new Model(*m_mesh);
}
void ModelMeshBinder::paint(ModelShaderProgram *program)
{
Model *newMesh = nullptr;
bool hasNewMesh = false;
if (m_newMeshComing) {
QMutexLocker lock(&m_newMeshMutex);
if (m_newMeshComing) {
newMesh = m_newMesh;
m_newMesh = nullptr;
m_newMeshComing = false;
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) {
delete m_mesh;
m_mesh = newMesh;
if (m_mesh) {
m_hasTexture = nullptr != m_mesh->textureImage();
delete m_texture;
m_texture = nullptr;
if (m_hasTexture) {
if (m_checkUvEnabled) {
static QImage *s_checkUv = nullptr;
if (nullptr == s_checkUv)
s_checkUv = new QImage(":/resources/checkuv.png");
m_texture = new QOpenGLTexture(*s_checkUv);
} else {
m_texture = new QOpenGLTexture(*m_mesh->textureImage());
}
}
m_hasNormalMap = nullptr != m_mesh->normalMapImage();
delete m_normalMap;
m_normalMap = nullptr;
if (m_hasNormalMap)
m_normalMap = new QOpenGLTexture(*m_mesh->normalMapImage());
m_hasMetalnessMap = m_mesh->hasMetalnessInImage();
m_hasRoughnessMap = m_mesh->hasRoughnessInImage();
m_hasAmbientOcclusionMap = m_mesh->hasAmbientOcclusionInImage();
delete m_metalnessRoughnessAmbientOcclusionMap;
m_metalnessRoughnessAmbientOcclusionMap = nullptr;
if (nullptr != m_mesh->metalnessRoughnessAmbientOcclusionImage() &&
(m_hasMetalnessMap || m_hasRoughnessMap || m_hasAmbientOcclusionMap))
m_metalnessRoughnessAmbientOcclusionMap = new QOpenGLTexture(*m_mesh->metalnessRoughnessAmbientOcclusionImage());
//delete m_environmentIrradianceMap;
//m_environmentIrradianceMap = nullptr;
//delete m_environmentSpecularMap;
//m_environmentSpecularMap = nullptr;
if (program->isCoreProfile() &&
m_environmentLightEnabled/* &&
(m_hasMetalnessMap || m_hasRoughnessMap)*/) {
if (nullptr == m_environmentIrradianceMap) {
DdsFileReader irradianceFile(":/resources/cedar_bridge_irradiance.dds");
m_environmentIrradianceMap = irradianceFile.createOpenGLTexture();
}
if (nullptr == m_environmentSpecularMap) {
DdsFileReader specularFile(":/resources/cedar_bridge_specular.dds");
m_environmentSpecularMap = specularFile.createOpenGLTexture();
}
}
{
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoTriangle);
if (m_vboTriangle.isCreated())
m_vboTriangle.destroy();
m_vboTriangle.create();
m_vboTriangle.bind();
m_vboTriangle.allocate(m_mesh->triangleVertices(), m_mesh->triangleVertexCount() * sizeof(ModelShaderVertex));
m_renderTriangleVertexCount = m_mesh->triangleVertexCount();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glEnableVertexAttribArray(3);
f->glEnableVertexAttribArray(4);
f->glEnableVertexAttribArray(5);
f->glEnableVertexAttribArray(6);
f->glEnableVertexAttribArray(7);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(9 * sizeof(GLfloat)));
f->glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(11 * sizeof(GLfloat)));
f->glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(12 * sizeof(GLfloat)));
f->glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(13 * sizeof(GLfloat)));
f->glVertexAttribPointer(7, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(16 * sizeof(GLfloat)));
m_vboTriangle.release();
}
{
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoEdge);
if (m_vboEdge.isCreated())
m_vboEdge.destroy();
m_vboEdge.create();
m_vboEdge.bind();
m_vboEdge.allocate(m_mesh->edgeVertices(), m_mesh->edgeVertexCount() * sizeof(ModelShaderVertex));
m_renderEdgeVertexCount = m_mesh->edgeVertexCount();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glEnableVertexAttribArray(3);
f->glEnableVertexAttribArray(4);
f->glEnableVertexAttribArray(5);
f->glEnableVertexAttribArray(6);
f->glEnableVertexAttribArray(7);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(9 * sizeof(GLfloat)));
f->glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(11 * sizeof(GLfloat)));
f->glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(12 * sizeof(GLfloat)));
f->glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(13 * sizeof(GLfloat)));
f->glVertexAttribPointer(7, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(16 * sizeof(GLfloat)));
m_vboEdge.release();
}
if (m_toolEnabled) {
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoTool);
if (m_vboTool.isCreated())
m_vboTool.destroy();
m_vboTool.create();
m_vboTool.bind();
m_vboTool.allocate(m_mesh->toolVertices(), m_mesh->toolVertexCount() * sizeof(ModelShaderVertex));
m_renderToolVertexCount = m_mesh->toolVertexCount();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glEnableVertexAttribArray(3);
f->glEnableVertexAttribArray(4);
f->glEnableVertexAttribArray(5);
f->glEnableVertexAttribArray(6);
f->glEnableVertexAttribArray(7);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(9 * sizeof(GLfloat)));
f->glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(11 * sizeof(GLfloat)));
f->glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(12 * sizeof(GLfloat)));
f->glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(13 * sizeof(GLfloat)));
f->glVertexAttribPointer(7, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(16 * sizeof(GLfloat)));
m_vboTool.release();
} else {
m_renderToolVertexCount = 0;
}
} else {
m_renderTriangleVertexCount = 0;
m_renderEdgeVertexCount = 0;
m_renderToolVertexCount = 0;
}
}
}
program->setUniformValue(program->textureIdLoc(), 0);
program->setUniformValue(program->normalMapIdLoc(), 1);
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_showWireframe) {
if (m_renderEdgeVertexCount > 0) {
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoEdge);
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
// glDrawArrays GL_LINES crashes on Mesa GL
if (program->isCoreProfile()) {
program->setUniformValue(program->textureEnabledLoc(), 0);
program->setUniformValue(program->normalMapEnabledLoc(), 0);
program->setUniformValue(program->metalnessMapEnabledLoc(), 0);
program->setUniformValue(program->roughnessMapEnabledLoc(), 0);
program->setUniformValue(program->ambientOcclusionMapEnabledLoc(), 0);
if (program->isCoreProfile()) {
program->setUniformValue(program->environmentIrradianceMapEnabledLoc(), 0);
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 0);
}
f->glDrawArrays(GL_LINES, 0, m_renderEdgeVertexCount);
}
}
}
if (m_renderTriangleVertexCount > 0) {
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoTriangle);
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
if (m_hasTexture) {
{
QMutexLocker lock(&m_colorTextureMutex);
if (m_colorTextureImage) {
delete m_texture;
m_texture = new QOpenGLTexture(*m_colorTextureImage);
delete m_colorTextureImage;
m_colorTextureImage = nullptr;
}
}
if (m_texture)
m_texture->bind(0);
program->setUniformValue(program->textureEnabledLoc(), 1);
} else {
program->setUniformValue(program->textureEnabledLoc(), 0);
}
if (m_hasNormalMap) {
if (m_normalMap)
m_normalMap->bind(1);
program->setUniformValue(program->normalMapEnabledLoc(), 1);
} else {
program->setUniformValue(program->normalMapEnabledLoc(), 0);
}
if (m_hasMetalnessMap || m_hasRoughnessMap || m_hasAmbientOcclusionMap) {
if (m_metalnessRoughnessAmbientOcclusionMap)
m_metalnessRoughnessAmbientOcclusionMap->bind(2);
}
program->setUniformValue(program->metalnessMapEnabledLoc(), m_hasMetalnessMap ? 1 : 0);
program->setUniformValue(program->roughnessMapEnabledLoc(), m_hasRoughnessMap ? 1 : 0);
program->setUniformValue(program->ambientOcclusionMapEnabledLoc(), m_hasAmbientOcclusionMap ? 1 : 0);
if (program->isCoreProfile()) {
if (nullptr != m_environmentIrradianceMap && nullptr != m_metalnessRoughnessAmbientOcclusionMap) {
m_environmentIrradianceMap->bind(3);
program->setUniformValue(program->environmentIrradianceMapEnabledLoc(), 1);
} else {
program->setUniformValue(program->environmentIrradianceMapEnabledLoc(), 0);
}
if (nullptr != m_environmentSpecularMap && nullptr != m_metalnessRoughnessAmbientOcclusionMap) {
m_environmentSpecularMap->bind(4);
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 1);
} else {
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 0);
}
}
if (nullptr != m_toonNormalMap && nullptr != m_toonDepthMap) {
m_toonNormalMap->bind(5);
m_toonDepthMap->bind(6);
program->setUniformValue(program->toonEdgeEnabledLoc(), (int)0);
}
f->glDrawArrays(GL_TRIANGLES, 0, m_renderTriangleVertexCount);
}
if (m_toolEnabled) {
if (m_renderToolVertexCount > 0) {
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vaoTool);
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
program->setUniformValue(program->textureEnabledLoc(), 0);
program->setUniformValue(program->normalMapEnabledLoc(), 0);
program->setUniformValue(program->metalnessMapEnabledLoc(), 0);
program->setUniformValue(program->roughnessMapEnabledLoc(), 0);
program->setUniformValue(program->ambientOcclusionMapEnabledLoc(), 0);
if (program->isCoreProfile()) {
program->setUniformValue(program->environmentIrradianceMapEnabledLoc(), 0);
program->setUniformValue(program->environmentSpecularMapEnabledLoc(), 0);
}
f->glDrawArrays(GL_TRIANGLES, 0, m_renderToolVertexCount);
}
}
}
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())
m_vboTriangle.destroy();
if (m_vboEdge.isCreated())
m_vboEdge.destroy();
if (m_toolEnabled) {
if (m_vboTool.isCreated())
m_vboTool.destroy();
}
delete m_texture;
m_texture = nullptr;
delete m_normalMap;
m_normalMap = nullptr;
delete m_metalnessRoughnessAmbientOcclusionMap;
m_metalnessRoughnessAmbientOcclusionMap = nullptr;
delete m_environmentIrradianceMap;
m_environmentIrradianceMap = nullptr;
delete m_environmentSpecularMap;
m_environmentSpecularMap = nullptr;
delete m_toonNormalMap;
m_toonNormalMap = nullptr;
delete m_toonDepthMap;
m_toonDepthMap = nullptr;
}
void ModelMeshBinder::showWireframe()
{
m_showWireframe = true;
}
void ModelMeshBinder::hideWireframe()
{
m_showWireframe = false;
}
bool ModelMeshBinder::isWireframeVisible()
{
return m_showWireframe;
}
void ModelMeshBinder::enableCheckUv()
{
m_checkUvEnabled = true;
}
void ModelMeshBinder::disableCheckUv()
{
m_checkUvEnabled = false;
}
bool ModelMeshBinder::isCheckUvEnabled()
{
return m_checkUvEnabled;
}

View File

@ -1,75 +0,0 @@
#ifndef DUST3D_APPLICATION_MODEL_MESH_BINDER_H_
#define DUST3D_APPLICATION_MODEL_MESH_BINDER_H_
#include <QOpenGLVertexArrayObject>
#include <QMutex>
#include <QOpenGLBuffer>
#include <QString>
#include <QOpenGLTexture>
#include "model.h"
#include "model_shader_program.h"
class ModelMeshBinder
{
public:
ModelMeshBinder(bool toolEnabled=false);
~ModelMeshBinder();
Model *fetchCurrentMesh();
void updateMesh(Model *mesh);
void updateColorTexture(QImage *colorTextureImage);
void initialize();
void paint(ModelShaderProgram *program);
void cleanup();
void showWireframe();
void hideWireframe();
bool isWireframeVisible();
void enableCheckUv();
void disableCheckUv();
void enableEnvironmentLight();
bool isEnvironmentLightEnabled();
bool isCheckUvEnabled();
void reloadMesh();
void fetchCurrentToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
private:
Model *m_mesh = nullptr;
Model *m_newMesh = nullptr;
int m_renderTriangleVertexCount = 0;
int m_renderEdgeVertexCount = 0;
int m_renderToolVertexCount = 0;
bool m_newMeshComing = false;
bool m_showWireframe = false;
bool m_hasTexture = false;
QOpenGLTexture *m_texture = nullptr;
bool m_hasNormalMap = false;
QOpenGLTexture *m_normalMap = nullptr;
bool m_hasMetalnessMap = false;
bool m_hasRoughnessMap = false;
bool m_hasAmbientOcclusionMap = false;
QOpenGLTexture *m_metalnessRoughnessAmbientOcclusionMap = nullptr;
bool m_toolEnabled = false;
bool m_checkUvEnabled = false;
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;
QImage *m_colorTextureImage = nullptr;
bool m_newToonMapsComing = false;
QOpenGLVertexArrayObject m_vaoTriangle;
QOpenGLBuffer m_vboTriangle;
QOpenGLVertexArrayObject m_vaoEdge;
QOpenGLBuffer m_vboEdge;
QOpenGLVertexArrayObject m_vaoTool;
QOpenGLBuffer m_vboTool;
QMutex m_meshMutex;
QMutex m_newMeshMutex;
QMutex m_toonNormalAndDepthMapMutex;
QMutex m_colorTextureMutex;
};
#endif

View File

@ -1,216 +1,51 @@
#include <QOpenGLFramebufferObjectFormat>
#include <QThread>
#include <QDebug>
#include "model_offscreen_render.h" #include "model_offscreen_render.h"
ModelOffscreenRender::ModelOffscreenRender(const QSurfaceFormat &format, QScreen *targetScreen): ModelOffscreenRender::ModelOffscreenRender(const QSurfaceFormat &format, QScreen *targetScreen):
QOffscreenSurface(targetScreen), QOffscreenSurface(targetScreen)
m_context(nullptr),
m_mesh(nullptr)
{ {
setFormat(format);
create();
if (!isValid())
qDebug() << "ModelOffscreenRender is invalid";
} }
ModelOffscreenRender::~ModelOffscreenRender() ModelOffscreenRender::~ModelOffscreenRender()
{ {
// FIXME: If delete m_renderFbo inside toImage, // TODO
// sometimes, the application will freeze, maybe there are dead locks inside the destruction call
// move it here can make sure it will be deleted on the main GUI thread to avoid dead locks
delete m_renderFbo;
destroy();
delete m_mesh;
delete m_normalMap;
delete m_depthMap;
}
void ModelOffscreenRender::updateMesh(Model *mesh)
{
delete m_mesh;
m_mesh = mesh;
}
void ModelOffscreenRender::setRenderThread(QThread *thread)
{
this->moveToThread(thread);
} }
void ModelOffscreenRender::setXRotation(int angle) void ModelOffscreenRender::setXRotation(int angle)
{ {
m_xRot = angle; // TODO
} }
void ModelOffscreenRender::setYRotation(int angle) void ModelOffscreenRender::setYRotation(int angle)
{ {
m_yRot = angle; // TODO
} }
void ModelOffscreenRender::setZRotation(int angle) void ModelOffscreenRender::setZRotation(int angle)
{ {
m_zRot = angle; // TODO
} }
void ModelOffscreenRender::setEyePosition(const QVector3D &eyePosition) void ModelOffscreenRender::setEyePosition(const QVector3D &eyePosition)
{ {
m_eyePosition = eyePosition; // TODO
} }
void ModelOffscreenRender::setMoveToPosition(const QVector3D &moveToPosition) void ModelOffscreenRender::setMoveToPosition(const QVector3D &moveToPosition)
{ {
m_moveToPosition = moveToPosition; // TODO
} }
void ModelOffscreenRender::enableWireframe() void ModelOffscreenRender::setRenderThread(QThread *thread)
{ {
m_isWireframeVisible = true; // TODO
} }
void ModelOffscreenRender::setRenderPurpose(int purpose) void ModelOffscreenRender::updateMesh(Model *mesh)
{ {
m_renderPurpose = purpose; // TODO
}
void ModelOffscreenRender::setToonShading(bool toonShading)
{
m_toonShading = toonShading;
}
void ModelOffscreenRender::enableEnvironmentLight()
{
m_isEnvironmentLightEnabled = true;
}
void ModelOffscreenRender::updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap)
{
delete m_normalMap;
m_normalMap = normalMap;
delete m_depthMap;
m_depthMap = depthMap;
} }
QImage ModelOffscreenRender::toImage(const QSize &size) QImage ModelOffscreenRender::toImage(const QSize &size)
{ {
QImage image; // TODO
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);
m_renderFbo = new QOpenGLFramebufferObject(size, format);
m_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();
if (m_isWireframeVisible)
meshBinder.showWireframe();
else
meshBinder.hideWireframe();
if (m_isEnvironmentLightEnabled)
meshBinder.enableEnvironmentLight();
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.translate(m_moveToPosition.x(), m_moveToPosition.y(), m_moveToPosition.z());
projection.perspective(45.0f, GLfloat(size.width()) / size.height(), 0.01f, 100.0f);
camera.setToIdentity();
camera.translate(m_eyePosition);
program->bind();
program->setUniformValue(program->eyePosLoc(), m_eyePosition);
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 = m_renderFbo->toImage();
m_renderFbo->release();
m_context->doneCurrent();
delete m_context;
m_context = nullptr;
return image;
} }

View File

@ -2,17 +2,11 @@
#define DUST3D_APPLICATION_MODEL_OFFSCREEN_RENDER_H_ #define DUST3D_APPLICATION_MODEL_OFFSCREEN_RENDER_H_
#include <QOffscreenSurface> #include <QOffscreenSurface>
#include <QScreen> #include <QSurfaceFormat>
#include <QOpenGLFunctions> #include <QVector3D>
#include <QOpenGLContext>
#include <QImage>
#include <QThread>
#include <QOpenGLFramebufferObject>
#include "model_shader_program.h"
#include "model_mesh_binder.h"
#include "model.h" #include "model.h"
class ModelOffscreenRender : QOffscreenSurface class ModelOffscreenRender: public QOffscreenSurface
{ {
public: public:
ModelOffscreenRender(const QSurfaceFormat &format, QScreen *targetScreen=Q_NULLPTR); ModelOffscreenRender(const QSurfaceFormat &format, QScreen *targetScreen=Q_NULLPTR);
@ -22,29 +16,9 @@ public:
void setZRotation(int angle); void setZRotation(int angle);
void setEyePosition(const QVector3D &eyePosition); void setEyePosition(const QVector3D &eyePosition);
void setMoveToPosition(const QVector3D &moveToPosition); void setMoveToPosition(const QVector3D &moveToPosition);
void setRenderPurpose(int purpose);
void setRenderThread(QThread *thread); void setRenderThread(QThread *thread);
void enableWireframe();
void enableEnvironmentLight();
void updateMesh(Model *mesh); void updateMesh(Model *mesh);
void updateToonNormalAndDepthMaps(QImage *normalMap, QImage *depthMap);
void setToonShading(bool toonShading);
QImage toImage(const QSize &size); QImage toImage(const QSize &size);
private:
int m_xRot = 0;
int m_yRot = 0;
int m_zRot = 0;
QVector3D m_eyePosition;
QVector3D m_moveToPosition;
int m_renderPurpose = 0;
QOpenGLContext *m_context = nullptr;
QOpenGLFramebufferObject *m_renderFbo = nullptr;
Model *m_mesh = nullptr;
QImage *m_normalMap = nullptr;
QImage *m_depthMap = nullptr;
bool m_toonShading = false;
bool m_isWireframeVisible = false;
bool m_isEnvironmentLightEnabled = false;
}; };
#endif #endif

View File

@ -0,0 +1,63 @@
#include <QOpenGLFunctions>
#include <QOpenGLContext>
#include "model_opengl_object.h"
void ModelOpenGLObject::update(std::unique_ptr<Model> mesh)
{
QMutexLocker lock(&m_meshMutex);
m_mesh = std::move(mesh);
m_meshIsDirty = true;
}
void ModelOpenGLObject::draw()
{
copyMeshToOpenGL();
if (0 == m_meshTriangleVertexCount)
return;
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glDrawArrays(GL_TRIANGLES, 0, m_meshTriangleVertexCount);
}
void ModelOpenGLObject::copyMeshToOpenGL()
{
std::unique_ptr<Model> mesh;
bool meshChanged = false;
if (m_meshIsDirty) {
QMutexLocker lock(&m_meshMutex);
if (m_meshIsDirty) {
m_meshIsDirty = false;
meshChanged = true;
mesh = std::move(m_mesh);
}
}
if (!meshChanged)
return;
m_meshTriangleVertexCount = 0;
if (mesh) {
QOpenGLVertexArrayObject::Binder binder(&m_vertexArrayObject);
if (m_buffer.isCreated())
m_buffer.destroy();
m_buffer.create();
m_buffer.bind();
m_buffer.allocate(mesh->triangleVertices(), mesh->triangleVertexCount() * sizeof(ModelShaderVertex));
m_meshTriangleVertexCount = mesh->triangleVertexCount();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glEnableVertexAttribArray(3);
f->glEnableVertexAttribArray(4);
f->glEnableVertexAttribArray(5);
f->glEnableVertexAttribArray(6);
f->glEnableVertexAttribArray(7);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(3 * sizeof(GLfloat)));
f->glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(6 * sizeof(GLfloat)));
f->glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(9 * sizeof(GLfloat)));
f->glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(11 * sizeof(GLfloat)));
f->glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(12 * sizeof(GLfloat)));
f->glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(13 * sizeof(GLfloat)));
f->glVertexAttribPointer(7, 1, GL_FLOAT, GL_FALSE, sizeof(ModelShaderVertex), reinterpret_cast<void *>(16 * sizeof(GLfloat)));
m_buffer.release();
}
}

View File

@ -0,0 +1,25 @@
#ifndef DUST3D_APPLICATION_MODEL_OPENGL_OBJECT_H_
#define DUST3D_APPLICATION_MODEL_OPENGL_OBJECT_H_
#include <memory>
#include <QMutex>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include "model.h"
class ModelOpenGLObject
{
public:
void update(std::unique_ptr<Model> mesh);
void draw();
private:
void copyMeshToOpenGL();
QOpenGLVertexArrayObject m_vertexArrayObject;
QOpenGLBuffer m_buffer;
std::unique_ptr<Model> m_mesh;
bool m_meshIsDirty = false;
QMutex m_meshMutex;
int m_meshTriangleVertexCount = 0;
};
#endif

View File

@ -0,0 +1,44 @@
#include <QOpenGLFunctions>
#include <QFile>
#include "model_opengl_program.h"
static const QString &loadShaderSource(const QString &name)
{
static std::map<QString, QString> s_shaderSources;
auto findShader = s_shaderSources.find(name);
if (findShader != s_shaderSources.end()) {
return findShader->second;
}
QFile file(name);
file.open(QFile::ReadOnly | QFile::Text);
QTextStream stream(&file);
auto insertResult = s_shaderSources.insert({name, stream.readAll()});
return insertResult.first->second;
}
void ModelOpenGLProgram::load(bool isCoreProfile)
{
if (m_isLoaded)
return;
m_isCoreProfile = isCoreProfile;
if (m_isCoreProfile) {
addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/model_core.vert"));
addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/model_core.frag"));
} else {
addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/model.vert"));
addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/model.frag"));
}
bindAttributeLocation("vertex", 0);
bindAttributeLocation("normal", 1);
bindAttributeLocation("color", 2);
bindAttributeLocation("texCoord", 3);
bindAttributeLocation("metalness", 4);
bindAttributeLocation("roughness", 5);
bindAttributeLocation("tangent", 6);
bindAttributeLocation("alpha", 7);
link();
bind();
m_isLoaded = true;
}

View File

@ -0,0 +1,16 @@
#ifndef DUST3D_APPLICATION_MODEL_OPENGL_PROGRAM_H_
#define DUST3D_APPLICATION_MODEL_OPENGL_PROGRAM_H_
#include <QOpenGLShaderProgram>
class ModelOpenGLProgram: public QOpenGLShaderProgram
{
public:
void load(bool isCoreProfile=false);
private:
bool m_isLoaded = false;
bool m_isCoreProfile = false;
};
#endif

View File

@ -1,209 +0,0 @@
#include <QFile>
#include <map>
#include "model_shader_program.h"
const QString &ModelShaderProgram::loadShaderSource(const QString &name)
{
static std::map<QString, QString> s_shaderSources;
auto findShader = s_shaderSources.find(name);
if (findShader != s_shaderSources.end()) {
return findShader->second;
}
QFile file(name);
file.open(QFile::ReadOnly | QFile::Text);
QTextStream stream(&file);
auto insertResult = s_shaderSources.insert({name, stream.readAll()});
return insertResult.first->second;
}
bool ModelShaderProgram::isCoreProfile()
{
return m_isCoreProfile;
}
ModelShaderProgram::ModelShaderProgram(bool isCoreProfile)
{
if (isCoreProfile) {
this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/model_core.vert"));
this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/model_core.frag"));
m_isCoreProfile = true;
} else {
this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/model.vert"));
this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/model.frag"));
}
this->bindAttributeLocation("vertex", 0);
this->bindAttributeLocation("normal", 1);
this->bindAttributeLocation("color", 2);
this->bindAttributeLocation("texCoord", 3);
this->bindAttributeLocation("metalness", 4);
this->bindAttributeLocation("roughness", 5);
this->bindAttributeLocation("tangent", 6);
this->bindAttributeLocation("alpha", 7);
this->link();
this->bind();
m_projectionMatrixLoc = this->uniformLocation("projectionMatrix");
m_modelMatrixLoc = this->uniformLocation("modelMatrix");
m_normalMatrixLoc = this->uniformLocation("normalMatrix");
m_viewMatrixLoc = this->uniformLocation("viewMatrix");
m_eyePosLoc = this->uniformLocation("eyePos");
m_textureIdLoc = this->uniformLocation("textureId");
m_textureEnabledLoc = this->uniformLocation("textureEnabled");
m_normalMapIdLoc = this->uniformLocation("normalMapId");
m_normalMapEnabledLoc = this->uniformLocation("normalMapEnabled");
m_metalnessMapEnabledLoc = this->uniformLocation("metalnessMapEnabled");
m_roughnessMapEnabledLoc = this->uniformLocation("roughnessMapEnabled");
m_ambientOcclusionMapEnabledLoc = this->uniformLocation("ambientOcclusionMapEnabled");
m_metalnessRoughnessAmbientOcclusionMapIdLoc = this->uniformLocation("metalnessRoughnessAmbientOcclusionMapId");
m_mousePickEnabledLoc = this->uniformLocation("mousePickEnabled");
m_mousePickTargetPositionLoc = this->uniformLocation("mousePickTargetPosition");
m_mousePickRadiusLoc = this->uniformLocation("mousePickRadius");
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");
m_environmentSpecularMapIdLoc = this->uniformLocation("environmentSpecularMapId");
m_environmentSpecularMapEnabledLoc = this->uniformLocation("environmentSpecularMapEnabled");
}
}
int ModelShaderProgram::projectionMatrixLoc()
{
return m_projectionMatrixLoc;
}
int ModelShaderProgram::modelMatrixLoc()
{
return m_modelMatrixLoc;
}
int ModelShaderProgram::normalMatrixLoc()
{
return m_normalMatrixLoc;
}
int ModelShaderProgram::viewMatrixLoc()
{
return m_viewMatrixLoc;
}
int ModelShaderProgram::eyePosLoc()
{
return m_eyePosLoc;
}
int ModelShaderProgram::textureEnabledLoc()
{
return m_textureEnabledLoc;
}
int ModelShaderProgram::textureIdLoc()
{
return m_textureIdLoc;
}
int ModelShaderProgram::normalMapEnabledLoc()
{
return m_normalMapEnabledLoc;
}
int ModelShaderProgram::normalMapIdLoc()
{
return m_normalMapIdLoc;
}
int ModelShaderProgram::metalnessMapEnabledLoc()
{
return m_metalnessMapEnabledLoc;
}
int ModelShaderProgram::roughnessMapEnabledLoc()
{
return m_roughnessMapEnabledLoc;
}
int ModelShaderProgram::ambientOcclusionMapEnabledLoc()
{
return m_ambientOcclusionMapEnabledLoc;
}
int ModelShaderProgram::metalnessRoughnessAmbientOcclusionMapIdLoc()
{
return m_metalnessRoughnessAmbientOcclusionMapIdLoc;
}
int ModelShaderProgram::mousePickEnabledLoc()
{
return m_mousePickEnabledLoc;
}
int ModelShaderProgram::mousePickTargetPositionLoc()
{
return m_mousePickTargetPositionLoc;
}
int ModelShaderProgram::mousePickRadiusLoc()
{
return m_mousePickRadiusLoc;
}
int ModelShaderProgram::environmentIrradianceMapIdLoc()
{
return m_environmentIrradianceMapIdLoc;
}
int ModelShaderProgram::environmentIrradianceMapEnabledLoc()
{
return m_environmentIrradianceMapEnabledLoc;
}
int ModelShaderProgram::environmentSpecularMapIdLoc()
{
return m_environmentSpecularMapIdLoc;
}
int ModelShaderProgram::environmentSpecularMapEnabledLoc()
{
return m_environmentSpecularMapEnabledLoc;
}
int ModelShaderProgram::toonShadingEnabledLoc()
{
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;
}

View File

@ -1,71 +0,0 @@
#ifndef DUST3D_APPLICATION_MODEL_SHADER_PROGRAM_H_
#define DUST3D_APPLICATION_MODEL_SHADER_PROGRAM_H_
#include <QOpenGLShaderProgram>
#include <QString>
class ModelShaderProgram : public QOpenGLShaderProgram
{
public:
ModelShaderProgram(bool isCoreProfile);
int projectionMatrixLoc();
int modelMatrixLoc();
int normalMatrixLoc();
int viewMatrixLoc();
int eyePosLoc();
int textureIdLoc();
int textureEnabledLoc();
int normalMapEnabledLoc();
int normalMapIdLoc();
int metalnessMapEnabledLoc();
int roughnessMapEnabledLoc();
int ambientOcclusionMapEnabledLoc();
int metalnessRoughnessAmbientOcclusionMapIdLoc();
int mousePickEnabledLoc();
int mousePickTargetPositionLoc();
int mousePickRadiusLoc();
int environmentIrradianceMapIdLoc();
int environmentIrradianceMapEnabledLoc();
int environmentSpecularMapIdLoc();
int environmentSpecularMapEnabledLoc();
int toonShadingEnabledLoc();
int renderPurposeLoc();
int toonEdgeEnabledLoc();
int screenWidthLoc();
int screenHeightLoc();
int toonNormalMapIdLoc();
int toonDepthMapIdLoc();
bool isCoreProfile();
static const QString &loadShaderSource(const QString &name);
private:
bool m_isCoreProfile = false;
int m_projectionMatrixLoc = 0;
int m_modelMatrixLoc = 0;
int m_normalMatrixLoc = 0;
int m_viewMatrixLoc = 0;
int m_eyePosLoc = 0;
int m_textureIdLoc = 0;
int m_textureEnabledLoc = 0;
int m_normalMapEnabledLoc = 0;
int m_normalMapIdLoc = 0;
int m_metalnessMapEnabledLoc = 0;
int m_roughnessMapEnabledLoc = 0;
int m_ambientOcclusionMapEnabledLoc = 0;
int m_metalnessRoughnessAmbientOcclusionMapIdLoc = 0;
int m_mousePickEnabledLoc = 0;
int m_mousePickTargetPositionLoc = 0;
int m_mousePickRadiusLoc = 0;
int m_environmentIrradianceMapIdLoc = 0;
int m_environmentIrradianceMapEnabledLoc = 0;
int m_environmentSpecularMapIdLoc = 0;
int m_environmentSpecularMapEnabledLoc = 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

View File

@ -1,5 +1,4 @@
#include <QMouseEvent> #include <QMouseEvent>
#include <QOpenGLShaderProgram>
#include <QCoreApplication> #include <QCoreApplication>
#include <QGuiApplication> #include <QGuiApplication>
#include <cmath> #include <cmath>
@ -7,7 +6,6 @@
#include <QSurfaceFormat> #include <QSurfaceFormat>
#include "model_widget.h" #include "model_widget.h"
bool ModelWidget::m_transparent = true;
float ModelWidget::m_minZoomRatio = 5.0; float ModelWidget::m_minZoomRatio = 5.0;
float ModelWidget::m_maxZoomRatio = 80.0; float ModelWidget::m_maxZoomRatio = 80.0;
@ -19,18 +17,14 @@ QVector3D ModelWidget::m_defaultEyePosition = QVector3D(0, 0, -2.5);
ModelWidget::ModelWidget(QWidget *parent) : ModelWidget::ModelWidget(QWidget *parent) :
QOpenGLWidget(parent) QOpenGLWidget(parent)
{ {
if (m_transparent) {
setAttribute(Qt::WA_AlwaysStackOnTop); setAttribute(Qt::WA_AlwaysStackOnTop);
setAttribute(Qt::WA_TranslucentBackground); setAttribute(Qt::WA_TranslucentBackground);
QSurfaceFormat fmt = format(); QSurfaceFormat fmt = format();
fmt.setAlphaBufferSize(8); fmt.setAlphaBufferSize(8);
fmt.setSamples(4); fmt.setSamples(4);
setFormat(fmt); setFormat(fmt);
} else {
QSurfaceFormat fmt = format();
fmt.setSamples(4);
setFormat(fmt);
}
setContextMenuPolicy(Qt::CustomContextMenu); setContextMenuPolicy(Qt::CustomContextMenu);
m_widthInPixels = width() * window()->devicePixelRatio(); m_widthInPixels = width() * window()->devicePixelRatio();
@ -115,46 +109,19 @@ void ModelWidget::setZRotation(int angle)
} }
} }
Model *ModelWidget::fetchCurrentMesh()
{
return m_meshBinder.fetchCurrentMesh();
}
void ModelWidget::cleanup() void ModelWidget::cleanup()
{ {
if (m_program == nullptr) if (!m_openglProgram)
return; return;
makeCurrent(); makeCurrent();
m_meshBinder.cleanup(); m_openglObject.reset();
delete m_program; m_openglProgram.reset();
m_program = nullptr;
doneCurrent(); doneCurrent();
} }
void ModelWidget::initializeGL() void ModelWidget::initializeGL()
{ {
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ModelWidget::cleanup); connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ModelWidget::cleanup);
initializeOpenGLFunctions();
if (m_transparent) {
glClearColor(0, 0, 0, 0);
} else {
QColor bgcolor = QWidget::palette().color(QWidget::backgroundRole());
glClearColor(bgcolor.redF(), bgcolor.greenF(), bgcolor.blueF(), 1);
}
bool isCoreProfile = false;
const char *versionString = (const char *)glGetString(GL_VERSION);
if (nullptr != versionString &&
'\0' != versionString[0]) {
isCoreProfile = format().profile() == QSurfaceFormat::CoreProfile;
}
m_program = new ModelShaderProgram(isCoreProfile);
m_meshBinder.initialize();
m_program->release();
} }
void ModelWidget::disableCullFace() void ModelWidget::disableCullFace()
@ -169,16 +136,18 @@ void ModelWidget::setMoveToPosition(const QVector3D &moveToPosition)
void ModelWidget::paintGL() void ModelWidget::paintGL()
{ {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST); f->glEnable(GL_BLEND);
f->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
f->glEnable(GL_DEPTH_TEST);
if (m_enableCullFace) if (m_enableCullFace)
glEnable(GL_CULL_FACE); f->glEnable(GL_CULL_FACE);
#ifdef GL_LINE_SMOOTH #ifdef GL_LINE_SMOOTH
glEnable(GL_LINE_SMOOTH); f->glEnable(GL_LINE_SMOOTH);
#endif #endif
glViewport(0, 0, m_widthInPixels, m_heightInPixels); f->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);
@ -188,37 +157,18 @@ void ModelWidget::paintGL()
m_camera.setToIdentity(); m_camera.setToIdentity();
m_camera.translate(m_eyePosition.x(), m_eyePosition.y(), m_eyePosition.z()); m_camera.translate(m_eyePosition.x(), m_eyePosition.y(), m_eyePosition.z());
m_program->bind(); if (!m_openglProgram) {
m_program->setUniformValue(m_program->eyePosLoc(), m_eyePosition); m_openglProgram = std::make_unique<ModelOpenGLProgram>();
m_program->setUniformValue(m_program->toonShadingEnabledLoc(), 0); const char *openglVersion = (const char *)f->glGetString(GL_VERSION);
m_program->setUniformValue(m_program->projectionMatrixLoc(), m_projection); m_openglProgram->load(nullptr != openglVersion &&
m_program->setUniformValue(m_program->modelMatrixLoc(), m_world); '\0' != openglVersion[0] &&
QMatrix3x3 normalMatrix = m_world.normalMatrix(); format().profile() == QSurfaceFormat::CoreProfile);
m_program->setUniformValue(m_program->normalMatrixLoc(), normalMatrix);
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);
m_program->setUniformValue(m_program->mousePickTargetPositionLoc(),
m_world * m_mousePickTargetPositionInModelSpace);
} else {
m_program->setUniformValue(m_program->mousePickEnabledLoc(), 0);
m_program->setUniformValue(m_program->mousePickTargetPositionLoc(), QVector3D());
} }
m_program->setUniformValue(m_program->mousePickRadiusLoc(), m_mousePickRadius);
m_meshBinder.paint(m_program); m_openglProgram->bind();
if (m_openglObject)
m_program->release(); m_openglObject->draw();
m_openglProgram->release();
} }
void ModelWidget::updateProjectionMatrix() void ModelWidget::updateProjectionMatrix()
@ -251,27 +201,24 @@ std::pair<QVector3D, QVector3D> ModelWidget::screenPositionToMouseRay(const QPoi
void ModelWidget::toggleWireframe() void ModelWidget::toggleWireframe()
{ {
if (m_meshBinder.isWireframeVisible()) // TODO
m_meshBinder.hideWireframe();
else
m_meshBinder.showWireframe();
update();
} }
bool ModelWidget::isWireframeVisible() bool ModelWidget::isWireframeVisible()
{ {
return m_meshBinder.isWireframeVisible(); // TODO
return false;
} }
void ModelWidget::enableEnvironmentLight() void ModelWidget::enableEnvironmentLight()
{ {
m_meshBinder.enableEnvironmentLight(); // TODO
update();
} }
bool ModelWidget::isEnvironmentLightEnabled() bool ModelWidget::isEnvironmentLightEnabled()
{ {
return m_meshBinder.isEnvironmentLightEnabled(); // TODO
return false;
} }
void ModelWidget::toggleRotation() void ModelWidget::toggleRotation()
@ -290,16 +237,6 @@ void ModelWidget::toggleRotation()
} }
} }
void ModelWidget::toggleUvCheck()
{
if (m_meshBinder.isCheckUvEnabled())
m_meshBinder.disableCheckUv();
else
m_meshBinder.enableCheckUv();
m_meshBinder.reloadMesh();
update();
}
bool ModelWidget::inputMousePressEventFromOtherWidget(QMouseEvent *event, bool notGraphics) bool ModelWidget::inputMousePressEventFromOtherWidget(QMouseEvent *event, bool notGraphics)
{ {
bool shouldStartMove = false; bool shouldStartMove = false;
@ -471,26 +408,16 @@ void ModelWidget::setMousePickRadius(float radius)
void ModelWidget::updateMesh(Model *mesh) void ModelWidget::updateMesh(Model *mesh)
{ {
m_meshBinder.updateMesh(mesh); if (!m_openglObject)
m_openglObject = std::make_unique<ModelOpenGLObject>();
m_openglObject->update(std::unique_ptr<Model>(mesh));
emit renderParametersChanged(); emit renderParametersChanged();
update(); update();
} }
void ModelWidget::updateColorTexture(QImage *colorTextureImage) void ModelWidget::updateColorTexture(QImage *colorTextureImage)
{ {
m_meshBinder.updateColorTexture(colorTextureImage); // TODO
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() int ModelWidget::widthInPixels()

View File

@ -1,20 +1,18 @@
#ifndef DUST3D_APPLICATION_MODEL_WIDGET_H_ #ifndef DUST3D_APPLICATION_MODEL_WIDGET_H_
#define DUST3D_APPLICATION_MODEL_WIDGET_H_ #define DUST3D_APPLICATION_MODEL_WIDGET_H_
#include <memory>
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer> #include <QOpenGLBuffer>
#include <QMatrix4x4> #include <QMatrix4x4>
#include <QMutex> #include <QMutex>
#include <QRubberBand>
#include <QVector2D> #include <QVector2D>
#include <QTimer> #include <QTimer>
#include "model.h" #include "model.h"
#include "model_shader_program.h" #include "model_opengl_program.h"
#include "model_mesh_binder.h" #include "model_opengl_object.h"
class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions class ModelWidget : public QOpenGLWidget
{ {
Q_OBJECT Q_OBJECT
signals: signals:
@ -31,21 +29,11 @@ signals:
public: public:
ModelWidget(QWidget *parent = 0); ModelWidget(QWidget *parent = 0);
~ModelWidget(); ~ModelWidget();
static bool isTransparent()
{
return m_transparent;
}
static void setTransparent(bool t)
{
m_transparent = t;
}
Model *fetchCurrentMesh();
void updateMesh(Model *mesh); void updateMesh(Model *mesh);
void updateColorTexture(QImage *colorTextureImage); void updateColorTexture(QImage *colorTextureImage);
void toggleWireframe(); void toggleWireframe();
bool isWireframeVisible(); bool isWireframeVisible();
void toggleRotation(); void toggleRotation();
void toggleUvCheck();
void enableEnvironmentLight(); void enableEnvironmentLight();
bool isEnvironmentLightEnabled(); bool isEnvironmentLightEnabled();
void enableMove(bool enabled); void enableMove(bool enabled);
@ -59,8 +47,6 @@ 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 widthInPixels();
int heightInPixels(); int heightInPixels();
void setNotGraphics(bool notGraphics); void setNotGraphics(bool notGraphics);
@ -93,20 +79,19 @@ private:
int m_yRot = m_defaultYRotation; int m_yRot = m_defaultYRotation;
int m_zRot = m_defaultZRotation; int m_zRot = m_defaultZRotation;
int m_directionOnMoveStart = 0; int m_directionOnMoveStart = 0;
ModelShaderProgram *m_program = nullptr; std::unique_ptr<ModelOpenGLProgram> m_openglProgram;
std::unique_ptr<ModelOpenGLObject> m_openglObject;
bool m_moveStarted = false; bool m_moveStarted = false;
bool m_moveEnabled = true; bool m_moveEnabled = true;
bool m_zoomEnabled = true; bool m_zoomEnabled = true;
bool m_mousePickingEnabled = false; bool m_mousePickingEnabled = false;
QVector3D m_mousePickTargetPositionInModelSpace; QVector3D m_mousePickTargetPositionInModelSpace;
QPoint m_lastPos; QPoint m_lastPos;
ModelMeshBinder m_meshBinder;
QMatrix4x4 m_projection; QMatrix4x4 m_projection;
QMatrix4x4 m_camera; QMatrix4x4 m_camera;
QMatrix4x4 m_world; QMatrix4x4 m_world;
float m_mousePickRadius = 0.0; float m_mousePickRadius = 0.0;
QVector3D m_eyePosition = m_defaultEyePosition; QVector3D m_eyePosition = m_defaultEyePosition;
static bool m_transparent;
static float m_minZoomRatio; static float m_minZoomRatio;
static float m_maxZoomRatio; static float m_maxZoomRatio;
QPoint m_moveStartPos; QPoint m_moveStartPos;

View File

@ -28,8 +28,6 @@ void PartPreviewImagesGenerator::generate()
m_offscreenRender->setZRotation(0); m_offscreenRender->setZRotation(0);
m_offscreenRender->setEyePosition(QVector3D(0, 0, -4.0)); m_offscreenRender->setEyePosition(QVector3D(0, 0, -4.0));
m_offscreenRender->enableEnvironmentLight();
m_offscreenRender->setRenderPurpose(0);
for (auto &it: m_partPreviews) { for (auto &it: m_partPreviews) {
if (it.second.isCutFace) { if (it.second.isCutFace) {
m_offscreenRender->setXRotation(0); m_offscreenRender->setXRotation(0);