diff --git a/application/shaders/model.frag b/application/shaders/model.frag index df426246..f2cfdb0a 100644 --- a/application/shaders/model.frag +++ b/application/shaders/model.frag @@ -1,13 +1,23 @@ #version 110 -uniform vec3 eyePosition; uniform sampler2D environmentIrradianceMapId[6]; uniform sampler2D environmentSpecularMapId[6]; +uniform sampler2D textureId; +uniform int textureEnabled; +uniform sampler2D normalMapId; +uniform int normalMapEnabled; +uniform sampler2D metalnessRoughnessAoMapId; +uniform int metalnessMapEnabled; +uniform int roughnessMapEnabled; +uniform int aoMapEnabled; +uniform vec3 eyePosition; varying vec3 pointPosition; varying vec3 pointNormal; varying vec3 pointColor; +varying vec2 pointTexCoord; varying float pointAlpha; varying float pointMetalness; varying float pointRoughness; +varying mat3 pointTBN; const float PI = 3.1415926; @@ -45,9 +55,34 @@ vec4 texturesAsCube(in sampler2D maps[6], in vec3 direction) vec2 st; cubemap(direction, texId, st); vec4 color = vec4(0); - for (int i = 0; i < 6; ++i) { - vec4 side = texture2D(maps[i], st); - float select = step(float(i) - 0.5, texId) * step(texId, float(i) + 0.5); + { + vec4 side = texture2D(maps[0], st); + float select = step(0.0 - 0.5, texId) * step(texId, 0.0 + 0.5); + color = mix(color, side, select); + } + { + vec4 side = texture2D(maps[1], st); + float select = step(1.0 - 0.5, texId) * step(texId, 1.0 + 0.5); + color = mix(color, side, select); + } + { + vec4 side = texture2D(maps[2], st); + float select = step(2.0 - 0.5, texId) * step(texId, 2.0 + 0.5); + color = mix(color, side, select); + } + { + vec4 side = texture2D(maps[3], st); + float select = step(3.0 - 0.5, texId) * step(texId, 3.0 + 0.5); + color = mix(color, side, select); + } + { + vec4 side = texture2D(maps[4], st); + float select = step(4.0 - 0.5, texId) * step(texId, 4.0 + 0.5); + color = mix(color, side, select); + } + { + vec4 side = texture2D(maps[5], st); + float select = step(5.0 - 0.5, texId) * step(texId, 5.0 + 0.5); color = mix(color, side, select); } return color; @@ -55,23 +90,48 @@ vec4 texturesAsCube(in sampler2D maps[6], in vec3 direction) void main() { - vec3 n = pointNormal; + vec3 color = pointColor; + float alpha = pointAlpha; + if (1 == textureEnabled) { + vec4 textColor = texture2D(textureId, pointTexCoord); + color = textColor.rgb; + alpha = textColor.a; + } + vec3 normal = pointNormal; + if (1 == normalMapEnabled) { + normal = texture2D(normalMapId, pointTexCoord).rgb; + normal = pointTBN * normalize(normal * 2.0 - 1.0); + } + float metalness = pointMetalness; + if (1 == metalnessMapEnabled) { + metalness = texture2D(metalnessRoughnessAoMapId, pointTexCoord).b; + } + float roughness = pointRoughness; + if (1 == roughnessMapEnabled) { + roughness = texture2D(metalnessRoughnessAoMapId, pointTexCoord).g; + } + float ambientOcclusion = 1.0; + if (1 == aoMapEnabled) { + ambientOcclusion = texture2D(metalnessRoughnessAoMapId, pointTexCoord).r; + } + + vec3 n = normal; vec3 v = normalize(eyePosition - pointPosition); vec3 r = reflect(-v, n); float NoV = abs(dot(n, v)) + 1e-5; vec3 irradiance = texturesAsCube(environmentIrradianceMapId, r).rgb; - vec3 diffuse = irradiance * (1.0 - pointMetalness) * pointColor; + vec3 diffuse = irradiance * (1.0 - metalness) * pointColor; - vec3 f0 = mix(vec3(0.04), pointColor, pointMetalness); - vec3 fresnelFactor = fresnelSchlickRoughness(NoV, f0, pointRoughness); + vec3 f0 = mix(vec3(0.04), pointColor, metalness); + vec3 fresnelFactor = fresnelSchlickRoughness(NoV, f0, roughness); vec3 specular = fresnelFactor * texturesAsCube(environmentSpecularMapId, r).rgb; - vec3 color = diffuse + specular; + color = (diffuse + specular) * ambientOcclusion; color = color / (color + vec3(1.0)); color = pow(color, vec3(1.0/2.2)); - gl_FragColor = vec4(color, pointAlpha); + gl_FragColor = vec4(color, alpha); } diff --git a/application/shaders/model.vert b/application/shaders/model.vert index 825e28e2..a75e5305 100644 --- a/application/shaders/model.vert +++ b/application/shaders/model.vert @@ -8,24 +8,43 @@ attribute float roughness; attribute vec3 tangent; attribute float alpha; uniform mat4 modelMatrix; +uniform mat3 normalMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; -uniform vec3 eyePosition; +uniform int normalMapEnabled; varying vec3 pointPosition; varying vec3 pointNormal; varying vec3 pointColor; +varying vec2 pointTexCoord; varying float pointAlpha; varying float pointMetalness; varying float pointRoughness; +varying mat3 pointTBN; + +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() { pointPosition = (modelMatrix * vertex).xyz; pointNormal = normalize((modelMatrix * vec4(normal, 1.0)).xyz); pointColor = color; + pointTexCoord = texCoord; pointAlpha = alpha; pointMetalness = metalness; pointRoughness = roughness; gl_Position = projectionMatrix * viewMatrix * vec4(pointPosition, 1.0); + + if (1 == normalMapEnabled) { + vec3 T = normalize(normalMatrix * tangent); + vec3 N = normalize(normalMatrix * normal); + T = normalize(T - dot(T, N) * N); + vec3 B = cross(N, T); + pointTBN = mat3(T, B, N); + } } diff --git a/application/shaders/model_core.frag b/application/shaders/model_core.frag index 1bef2315..be037e22 100644 --- a/application/shaders/model_core.frag +++ b/application/shaders/model_core.frag @@ -1,13 +1,23 @@ #version 330 -uniform vec3 eyePosition; uniform samplerCube environmentIrradianceMapId; uniform samplerCube environmentSpecularMapId; +uniform sampler2D textureId; +uniform int textureEnabled; +uniform sampler2D normalMapId; +uniform int normalMapEnabled; +uniform sampler2D metalnessRoughnessAoMapId; +uniform int metalnessMapEnabled; +uniform int roughnessMapEnabled; +uniform int aoMapEnabled; +uniform vec3 eyePosition; in vec3 pointPosition; in vec3 pointNormal; in vec3 pointColor; +in vec2 pointTexCoord; in float pointAlpha; in float pointMetalness; in float pointRoughness; +in mat3 pointTBN; out vec4 fragColor; const float PI = 3.1415926; @@ -19,23 +29,48 @@ vec3 fresnelSchlickRoughness(float NoV, vec3 f0, float roughness) void main() { - vec3 n = pointNormal; + vec3 color = pointColor; + float alpha = pointAlpha; + if (1 == textureEnabled) { + vec4 textColor = texture(textureId, pointTexCoord); + color = textColor.rgb; + alpha = textColor.a; + } + vec3 normal = pointNormal; + if (1 == normalMapEnabled) { + normal = texture(normalMapId, pointTexCoord).rgb; + normal = pointTBN * normalize(normal * 2.0 - 1.0); + } + float metalness = pointMetalness; + if (1 == metalnessMapEnabled) { + metalness = texture(metalnessRoughnessAoMapId, pointTexCoord).b; + } + float roughness = pointRoughness; + if (1 == roughnessMapEnabled) { + roughness = texture(metalnessRoughnessAoMapId, pointTexCoord).g; + } + float ambientOcclusion = 1.0; + if (1 == aoMapEnabled) { + ambientOcclusion = texture(metalnessRoughnessAoMapId, pointTexCoord).r; + } + + vec3 n = normal; vec3 v = normalize(eyePosition - pointPosition); vec3 r = reflect(-v, n); float NoV = abs(dot(n, v)) + 1e-5; vec3 irradiance = texture(environmentIrradianceMapId, r).rgb; - vec3 diffuse = irradiance * (1.0 - pointMetalness) * pointColor; + vec3 diffuse = irradiance * (1.0 - metalness) * pointColor; - vec3 f0 = mix(vec3(0.04), pointColor, pointMetalness); - vec3 fresnelFactor = fresnelSchlickRoughness(NoV, f0, pointRoughness); + vec3 f0 = mix(vec3(0.04), pointColor, metalness); + vec3 fresnelFactor = fresnelSchlickRoughness(NoV, f0, roughness); vec3 specular = fresnelFactor * texture(environmentSpecularMapId, r, 0.0).rgb; - vec3 color = diffuse + specular; + color = (diffuse + specular) * ambientOcclusion; color = color / (color + vec3(1.0)); color = pow(color, vec3(1.0/2.2)); - fragColor = vec4(color, pointAlpha); + fragColor = vec4(color, alpha); } \ No newline at end of file diff --git a/application/shaders/model_core.vert b/application/shaders/model_core.vert index 3f54e4e4..055b7b55 100644 --- a/application/shaders/model_core.vert +++ b/application/shaders/model_core.vert @@ -8,24 +8,43 @@ layout(location = 5) in float roughness; layout(location = 6) in vec3 tangent; layout(location = 7) in float alpha; uniform mat4 modelMatrix; +uniform mat3 normalMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; -uniform vec3 eyePosition; +uniform int normalMapEnabled; out vec3 pointPosition; out vec3 pointNormal; out vec3 pointColor; +out vec2 pointTexCoord; out float pointAlpha; out float pointMetalness; out float pointRoughness; +out mat3 pointTBN; + +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() { pointPosition = (modelMatrix * vertex).xyz; pointNormal = normalize((modelMatrix * vec4(normal, 1.0)).xyz); pointColor = color; + pointTexCoord = texCoord; pointAlpha = alpha; pointMetalness = metalness; pointRoughness = roughness; gl_Position = projectionMatrix * viewMatrix * vec4(pointPosition, 1.0); + + if (1 == normalMapEnabled) { + vec3 T = normalize(normalMatrix * tangent); + vec3 N = normalize(normalMatrix * normal); + T = normalize(T - dot(T, N) * N); + vec3 B = cross(N, T); + pointTBN = mat3(T, B, N); + } } \ No newline at end of file diff --git a/application/sources/document.h b/application/sources/document.h index 2cda209e..9c226356 100644 --- a/application/sources/document.h +++ b/application/sources/document.h @@ -80,7 +80,6 @@ signals: void turnaroundChanged(); void editModeChanged(); void resultTextureChanged(); - void resultColorTextureChanged(); void postProcessedResultChanged(); void partSubdivStateChanged(dust3d::Uuid partId); void partXmirrorStateChanged(dust3d::Uuid partId); diff --git a/application/sources/document_window.cc b/application/sources/document_window.cc index c54c8e74..d8e67609 100644 --- a/application/sources/document_window.cc +++ b/application/sources/document_window.cc @@ -251,7 +251,6 @@ DocumentWindow::DocumentWindow() m_modelRenderWidget->setMoveAndZoomByWindow(false); m_modelRenderWidget->move(0, 0); m_modelRenderWidget->setAttribute(Qt::WA_TransparentForMouseEvents); - m_modelRenderWidget->enableEnvironmentLight(); m_modelRenderWidget->toggleWireframe(); m_modelRenderWidget->disableCullFace(); m_modelRenderWidget->setEyePosition(QVector3D(0.0, 0.0, -4.0)); @@ -726,10 +725,6 @@ DocumentWindow::DocumentWindow() resultTextureMesh->removeColor(); m_modelRenderWidget->updateMesh(resultTextureMesh); }); - connect(m_document, &Document::resultColorTextureChanged, [=]() { - if (nullptr != m_document->textureImage) - m_modelRenderWidget->updateColorTexture(new QImage(*m_document->textureImage)); - }); connect(m_document, &Document::resultMeshChanged, [=]() { auto resultMesh = m_document->takeResultMesh(); diff --git a/application/sources/main.cc b/application/sources/main.cc index f7423862..60b06c61 100644 --- a/application/sources/main.cc +++ b/application/sources/main.cc @@ -25,7 +25,7 @@ int main(int argc, char *argv[]) //freopen("dust3d.log", "w", stdout); //setvbuf(stdout, 0, _IONBF, 0); - DocumentWindow *firstWindow = DocumentWindow::createDocumentWindow(); + DocumentWindow::createDocumentWindow(); return app.exec(); } diff --git a/application/sources/material_edit_widget.cc b/application/sources/material_edit_widget.cc index 4ddda585..27870002 100644 --- a/application/sources/material_edit_widget.cc +++ b/application/sources/material_edit_widget.cc @@ -50,7 +50,6 @@ MaterialEditWidget::MaterialEditWidget(const Document *document, QWidget *parent m_previewWidget->setMinimumSize(128, 128); m_previewWidget->resize(512, 512); m_previewWidget->move(-128, -128); - m_previewWidget->enableEnvironmentLight(); m_previewWidget->setNotGraphics(true); QFont nameFont; diff --git a/application/sources/material_widget.cc b/application/sources/material_widget.cc index 54a1c185..d3803875 100644 --- a/application/sources/material_widget.cc +++ b/application/sources/material_widget.cc @@ -13,7 +13,6 @@ MaterialWidget::MaterialWidget(const Document *document, dust3d::Uuid materialId m_previewWidget->setFixedSize(Theme::materialPreviewImageSize, Theme::materialPreviewImageSize); m_previewWidget->enableMove(false); m_previewWidget->enableZoom(false); - m_previewWidget->enableEnvironmentLight(); m_nameLabel = new QLabel; m_nameLabel->setAlignment(Qt::AlignCenter); diff --git a/application/sources/model_mesh.cc b/application/sources/model_mesh.cc index bbeb1ea9..fcfb64ee 100644 --- a/application/sources/model_mesh.cc +++ b/application/sources/model_mesh.cc @@ -26,8 +26,8 @@ ModelMesh::ModelMesh(const ModelMesh &mesh) : if (nullptr != mesh.m_normalMapImage) { this->m_normalMapImage = new QImage(*mesh.m_normalMapImage); } - if (nullptr != mesh.m_metalnessRoughnessAmbientOcclusionImage) { - this->m_metalnessRoughnessAmbientOcclusionImage = new QImage(*mesh.m_metalnessRoughnessAmbientOcclusionImage); + if (nullptr != mesh.m_metalnessRoughnessAmbientOcclusionMapImage) { + this->m_metalnessRoughnessAmbientOcclusionMapImage = new QImage(*mesh.m_metalnessRoughnessAmbientOcclusionMapImage); this->m_hasMetalnessInImage = mesh.m_hasMetalnessInImage; this->m_hasRoughnessInImage = mesh.m_hasRoughnessInImage; this->m_hasAmbientOcclusionInImage = mesh.m_hasAmbientOcclusionInImage; @@ -46,8 +46,8 @@ void ModelMesh::removeColor() delete this->m_normalMapImage; this->m_normalMapImage = nullptr; - delete this->m_metalnessRoughnessAmbientOcclusionImage; - this->m_metalnessRoughnessAmbientOcclusionImage = nullptr; + delete this->m_metalnessRoughnessAmbientOcclusionMapImage; + this->m_metalnessRoughnessAmbientOcclusionMapImage = nullptr; this->m_hasMetalnessInImage = false; this->m_hasRoughnessInImage = false; @@ -185,7 +185,7 @@ ModelMesh::~ModelMesh() m_triangleVertexCount = 0; delete m_textureImage; delete m_normalMapImage; - delete m_metalnessRoughnessAmbientOcclusionImage; + delete m_metalnessRoughnessAmbientOcclusionMapImage; } const std::vector &ModelMesh::vertices() @@ -223,6 +223,13 @@ const QImage *ModelMesh::textureImage() return m_textureImage; } +QImage *ModelMesh::takeTextureImage() +{ + auto image = m_textureImage; + m_textureImage = nullptr; + return image; +} + void ModelMesh::setNormalMapImage(QImage *normalMapImage) { m_normalMapImage = normalMapImage; @@ -233,14 +240,28 @@ const QImage *ModelMesh::normalMapImage() return m_normalMapImage; } -const QImage *ModelMesh::metalnessRoughnessAmbientOcclusionImage() +QImage *ModelMesh::takeNormalMapImage() { - return m_metalnessRoughnessAmbientOcclusionImage; + auto image = m_normalMapImage; + m_normalMapImage = nullptr; + return image; } -void ModelMesh::setMetalnessRoughnessAmbientOcclusionImage(QImage *image) +const QImage *ModelMesh::metalnessRoughnessAmbientOcclusionMapImage() { - m_metalnessRoughnessAmbientOcclusionImage = image; + return m_metalnessRoughnessAmbientOcclusionMapImage; +} + +QImage *ModelMesh::takeMetalnessRoughnessAmbientOcclusionMapImage() +{ + auto image = m_metalnessRoughnessAmbientOcclusionMapImage; + m_metalnessRoughnessAmbientOcclusionMapImage = nullptr; + return image; +} + +void ModelMesh::setMetalnessRoughnessAmbientOcclusionMapImage(QImage *image) +{ + m_metalnessRoughnessAmbientOcclusionMapImage = image; } bool ModelMesh::hasMetalnessInImage() diff --git a/application/sources/model_mesh.h b/application/sources/model_mesh.h index 9da6986d..9f60d494 100644 --- a/application/sources/model_mesh.h +++ b/application/sources/model_mesh.h @@ -25,19 +25,18 @@ public: ~ModelMesh(); ModelOpenGLVertex *triangleVertices(); int triangleVertexCount(); - ModelOpenGLVertex *edgeVertices(); - int edgeVertexCount(); - ModelOpenGLVertex *toolVertices(); - int toolVertexCount(); const std::vector &vertices(); const std::vector> &faces(); const std::vector &triangulatedVertices(); void setTextureImage(QImage *textureImage); const QImage *textureImage(); + QImage *takeTextureImage(); void setNormalMapImage(QImage *normalMapImage); const QImage *normalMapImage(); - const QImage *metalnessRoughnessAmbientOcclusionImage(); - void setMetalnessRoughnessAmbientOcclusionImage(QImage *image); + QImage *takeNormalMapImage(); + const QImage *metalnessRoughnessAmbientOcclusionMapImage(); + QImage *takeMetalnessRoughnessAmbientOcclusionMapImage(); + void setMetalnessRoughnessAmbientOcclusionMapImage(QImage *image); bool hasMetalnessInImage(); void setHasMetalnessInImage(bool hasInImage); bool hasRoughnessInImage(); @@ -60,7 +59,7 @@ private: std::vector m_triangulatedVertices; QImage *m_textureImage = nullptr; QImage *m_normalMapImage = nullptr; - QImage *m_metalnessRoughnessAmbientOcclusionImage = nullptr; + QImage *m_metalnessRoughnessAmbientOcclusionMapImage = nullptr; bool m_hasMetalnessInImage = false; bool m_hasRoughnessInImage = false; bool m_hasAmbientOcclusionInImage = false; diff --git a/application/sources/model_offscreen_render.cc b/application/sources/model_offscreen_render.cc index 99ce8d0d..35c29681 100644 --- a/application/sources/model_offscreen_render.cc +++ b/application/sources/model_offscreen_render.cc @@ -120,15 +120,18 @@ QImage ModelOffscreenRender::toImage(const QSize &size) auto program = std::make_unique(); program->load(m_context->format().profile() == QSurfaceFormat::CoreProfile); program->bind(); - program->bindEnvironment(); + program->bindMaps(); program->setUniformValue(program->getUniformLocationByName("eyePosition"), m_eyePosition); program->setUniformValue(program->getUniformLocationByName("projectionMatrix"), projection); program->setUniformValue(program->getUniformLocationByName("modelMatrix"), world); + program->setUniformValue(program->getUniformLocationByName("normalMatrix"), world.normalMatrix()); program->setUniformValue(program->getUniformLocationByName("viewMatrix"), camera); object->draw(); + program->releaseMaps(); + program->release(); f->glFlush(); diff --git a/application/sources/model_opengl_program.cc b/application/sources/model_opengl_program.cc index 9752cc38..24561dfe 100644 --- a/application/sources/model_opengl_program.cc +++ b/application/sources/model_opengl_program.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include "model_opengl_program.h" #include "dds_file.h" @@ -29,6 +30,42 @@ bool ModelOpenGLProgram::isCoreProfile() const return m_isCoreProfile; } +void ModelOpenGLProgram::updateTextureImage(std::unique_ptr image) +{ + QMutexLocker lock(&m_imageMutex); + m_textureImage = std::move(image); + m_imageIsDirty = true; +} + +void ModelOpenGLProgram::updateNormalMapImage(std::unique_ptr image) +{ + QMutexLocker lock(&m_imageMutex); + m_normalMapImage = std::move(image); + m_imageIsDirty = true; +} + +void ModelOpenGLProgram::updateMetalnessRoughnessAmbientOcclusionMapImage(std::unique_ptr image, + bool hasMetalnessMap, + bool hasRoughnessMap, + bool hasAmbientOcclusionMap) +{ + QMutexLocker lock(&m_imageMutex); + m_metalnessRoughnessAmbientOcclusionMapImage = std::move(image); + m_hasMetalnessMap = hasMetalnessMap; + m_hasRoughnessMap = hasRoughnessMap; + m_hasAmbientOcclusionMap = hasAmbientOcclusionMap; + m_imageIsDirty = true; +} + +void ModelOpenGLProgram::activeAndBindTexture(int location, QOpenGLTexture *texture) +{ + if (0 == texture->textureId()) { + dust3dDebug << "Expected texture with a bound id"; + return; + } + texture->bind(location); +} + void ModelOpenGLProgram::load(bool isCoreProfile) { if (m_isLoaded) @@ -54,8 +91,11 @@ void ModelOpenGLProgram::load(bool isCoreProfile) m_isLoaded = true; } -void ModelOpenGLProgram::bindEnvironment() +void ModelOpenGLProgram::bindMaps() { + int bindLocation = 1; + + // Bind environment maps if (m_isCoreProfile) { if (!m_environmentIrradianceMap) { DdsFileReader irradianceFile(":/resources/cedar_bridge_irradiance.dds"); @@ -66,11 +106,13 @@ void ModelOpenGLProgram::bindEnvironment() m_environmentSpecularMap.reset(irradianceFile.createOpenGLTexture()); } - m_environmentIrradianceMap->bind(0); - setUniformValue(getUniformLocationByName("environmentIrradianceMapId"), 0); + bindLocation++; + activeAndBindTexture(bindLocation, m_environmentIrradianceMap.get()); + setUniformValue(getUniformLocationByName("environmentIrradianceMapId"), bindLocation); - m_environmentSpecularMap->bind(1); - setUniformValue(getUniformLocationByName("environmentSpecularMapId"), 1); + bindLocation++; + activeAndBindTexture(bindLocation, m_environmentSpecularMap.get()); + setUniformValue(getUniformLocationByName("environmentSpecularMapId"), bindLocation); } else { if (!m_environmentIrradianceMaps) { DdsFileReader irradianceFile(":/resources/cedar_bridge_irradiance.dds"); @@ -81,19 +123,104 @@ void ModelOpenGLProgram::bindEnvironment() m_environmentSpecularMaps = std::move(irradianceFile.createOpenGLTextures()); } - size_t bindPosition = 0; + auto oldBindLocationStart = bindLocation; - bindPosition = 0; for (size_t i = 0; i < m_environmentIrradianceMaps->size(); ++i) - m_environmentIrradianceMaps->at(i)->bind(bindPosition++); + activeAndBindTexture(bindLocation++, m_environmentIrradianceMaps->at(i).get()); for (size_t i = 0; i < m_environmentSpecularMaps->size(); ++i) - m_environmentSpecularMaps->at(i)->bind(bindPosition++); + activeAndBindTexture(bindLocation++, m_environmentSpecularMaps->at(i).get()); + + bindLocation = oldBindLocationStart; - bindPosition = 0; for (size_t i = 0; i < m_environmentIrradianceMaps->size(); ++i) - setUniformValue(getUniformLocationByName("environmentIrradianceMapId[" + std::to_string(i) + "]"), (int)(bindPosition++)); + setUniformValue(getUniformLocationByName("environmentIrradianceMapId[" + std::to_string(i) + "]"), bindLocation++); for (size_t i = 0; i < m_environmentSpecularMaps->size(); ++i) - setUniformValue(getUniformLocationByName("environmentSpecularMapId[" + std::to_string(i) + "]"), (int)(bindPosition++)); + setUniformValue(getUniformLocationByName("environmentSpecularMapId[" + std::to_string(i) + "]"), bindLocation++); + } + + // Bind texture, normal map, and other maps + if (m_imageIsDirty) { + QMutexLocker lock(&m_imageMutex); + if (m_imageIsDirty) { + m_imageIsDirty = false; + if (m_texture) { + m_texture->destroy(); + m_texture.reset(); + } + if (m_textureImage) { + m_texture = std::make_unique(*m_textureImage); + } + if (m_normalMap) { + m_normalMap->destroy(); + m_normalMap.reset(); + } + if (m_normalMapImage) { + m_normalMap = std::make_unique(*m_normalMapImage); + } + if (m_metalnessRoughnessAmbientOcclusionMap) { + m_metalnessRoughnessAmbientOcclusionMap->destroy(); + m_metalnessRoughnessAmbientOcclusionMap.reset(); + } + if (m_metalnessRoughnessAmbientOcclusionMapImage) { + m_metalnessRoughnessAmbientOcclusionMap = std::make_unique(*m_metalnessRoughnessAmbientOcclusionMapImage); + } + m_metalnessMapEnabled = m_hasMetalnessMap; + m_roughnessMapEnabled = m_hasRoughnessMap; + m_ambientOcclusionMapEnabled = m_hasAmbientOcclusionMap; + } + } + bindLocation++; + if (m_texture) { + activeAndBindTexture(bindLocation, m_texture.get()); + setUniformValue(getUniformLocationByName("textureId"), bindLocation); + setUniformValue(getUniformLocationByName("textureEnabled"), (int)1); + } else { + setUniformValue(getUniformLocationByName("textureId"), (int)0); + setUniformValue(getUniformLocationByName("textureEnabled"), (int)0); + } + bindLocation++; + if (m_normalMap) { + activeAndBindTexture(bindLocation, m_normalMap.get()); + setUniformValue(getUniformLocationByName("normalMapId"), bindLocation); + setUniformValue(getUniformLocationByName("normalMapEnabled"), (int)1); + } else { + setUniformValue(getUniformLocationByName("normalMapId"), (int)0); + setUniformValue(getUniformLocationByName("normalMapEnabled"), (int)0); + } + bindLocation++; + if (m_metalnessRoughnessAmbientOcclusionMap) { + activeAndBindTexture(bindLocation, m_metalnessRoughnessAmbientOcclusionMap.get()); + setUniformValue(getUniformLocationByName("metalnessRoughnessAoMapId"), bindLocation); + setUniformValue(getUniformLocationByName("metalnessMapEnabled"), (int)m_metalnessMapEnabled); + setUniformValue(getUniformLocationByName("roughnessMapEnabled"), (int)m_roughnessMapEnabled); + setUniformValue(getUniformLocationByName("aoMapEnabled"), (int)m_ambientOcclusionMapEnabled); + } else { + setUniformValue(getUniformLocationByName("metalnessRoughnessAoMapId"), (int)0); + setUniformValue(getUniformLocationByName("metalnessMapEnabled"), (int)0); + setUniformValue(getUniformLocationByName("roughnessMapEnabled"), (int)0); + setUniformValue(getUniformLocationByName("aoMapEnabled"), (int)0); + } +} + +void ModelOpenGLProgram::releaseMaps() +{ + if (m_texture) + m_texture->release(); + if (m_normalMap) + m_normalMap->release(); + if (m_metalnessRoughnessAmbientOcclusionMap) + m_metalnessRoughnessAmbientOcclusionMap->release(); + if (m_environmentIrradianceMap) + m_environmentIrradianceMap->release(); + if (m_environmentSpecularMap) + m_environmentSpecularMap->release(); + if (m_environmentIrradianceMaps) { + for (size_t i = 0; i < m_environmentIrradianceMaps->size(); ++i) + m_environmentIrradianceMaps->at(i)->release(); + } + if (m_environmentSpecularMaps) { + for (size_t i = 0; i < m_environmentSpecularMaps->size(); ++i) + m_environmentSpecularMaps->at(i)->release(); } } diff --git a/application/sources/model_opengl_program.h b/application/sources/model_opengl_program.h index 29c4e821..803b5647 100644 --- a/application/sources/model_opengl_program.h +++ b/application/sources/model_opengl_program.h @@ -5,6 +5,7 @@ #include #include #include +#include class ModelOpenGLProgram: public QOpenGLShaderProgram { @@ -12,10 +13,18 @@ public: void load(bool isCoreProfile=false); int getUniformLocationByName(const std::string &name); bool isCoreProfile() const; - void bindEnvironment(); + void bindMaps(); + void releaseMaps(); + void updateTextureImage(std::unique_ptr image); + void updateNormalMapImage(std::unique_ptr image); + void updateMetalnessRoughnessAmbientOcclusionMapImage(std::unique_ptr image, + bool hasMetalnessMap = false, + bool hasRoughnessMap = false, + bool hasAmbientOcclusionMap = false); private: void addShaderFromResource(QOpenGLShader::ShaderType type, const char *resourceName); + void activeAndBindTexture(int location, QOpenGLTexture *texture); bool m_isLoaded = false; bool m_isCoreProfile = false; @@ -25,6 +34,20 @@ private: std::unique_ptr m_environmentSpecularMap; std::unique_ptr>> m_environmentIrradianceMaps; std::unique_ptr>> m_environmentSpecularMaps; + std::unique_ptr m_textureImage; + std::unique_ptr m_normalMapImage; + std::unique_ptr m_metalnessRoughnessAmbientOcclusionMapImage; + std::unique_ptr m_texture; + std::unique_ptr m_normalMap; + std::unique_ptr m_metalnessRoughnessAmbientOcclusionMap; + bool m_hasMetalnessMap = false; + bool m_hasRoughnessMap = false; + bool m_hasAmbientOcclusionMap = false; + QMutex m_imageMutex; + bool m_imageIsDirty = false; + bool m_metalnessMapEnabled = false; + bool m_roughnessMapEnabled = false; + bool m_ambientOcclusionMapEnabled = false; }; #endif diff --git a/application/sources/model_widget.cc b/application/sources/model_widget.cc index f17cb4c0..5bf4da49 100644 --- a/application/sources/model_widget.cc +++ b/application/sources/model_widget.cc @@ -174,16 +174,6 @@ bool ModelWidget::isWireframeVisible() return m_isWireframeVisible; } -void ModelWidget::enableEnvironmentLight() -{ - m_isEnvironmentLightEnabled = true; -} - -bool ModelWidget::isEnvironmentLightEnabled() -{ - return m_isEnvironmentLightEnabled; -} - void ModelWidget::toggleRotation() { if (nullptr != m_rotationTimer) { @@ -371,9 +361,19 @@ void ModelWidget::setMousePickRadius(float radius) void ModelWidget::updateMesh(ModelMesh *mesh) { + if (!m_modelOpenGLProgram) + m_modelOpenGLProgram = std::make_unique(); + m_modelOpenGLProgram->updateTextureImage(std::unique_ptr(nullptr != mesh ? mesh->takeTextureImage() : nullptr)); + m_modelOpenGLProgram->updateNormalMapImage(std::unique_ptr(nullptr != mesh ? mesh->takeNormalMapImage() : nullptr)); + m_modelOpenGLProgram->updateMetalnessRoughnessAmbientOcclusionMapImage(std::unique_ptr(nullptr != mesh ? mesh->takeMetalnessRoughnessAmbientOcclusionMapImage() : nullptr), + mesh && mesh->hasMetalnessInImage(), + mesh && mesh->hasRoughnessInImage(), + mesh && mesh->hasAmbientOcclusionInImage()); + if (!m_modelOpenGLObject) m_modelOpenGLObject = std::make_unique(); m_modelOpenGLObject->update(std::unique_ptr(mesh)); + emit renderParametersChanged(); update(); } @@ -387,11 +387,6 @@ void ModelWidget::updateWireframeMesh(MonochromeMesh *mesh) update(); } -void ModelWidget::updateColorTexture(QImage *colorTextureImage) -{ - // TODO -} - int ModelWidget::widthInPixels() { return m_widthInPixels; @@ -511,19 +506,20 @@ void ModelWidget::paintGL() m_monochromeOpenGLProgram->load(format().profile() == QSurfaceFormat::CoreProfile); } } - + drawModel(); - if (m_isWireframeVisible) + if (m_isWireframeVisible) { drawWireframe(); + } } void ModelWidget::drawWireframe() { m_monochromeOpenGLProgram->bind(); - m_monochromeOpenGLProgram->setUniformValue(m_modelOpenGLProgram->getUniformLocationByName("projectionMatrix"), m_projection); - m_monochromeOpenGLProgram->setUniformValue(m_modelOpenGLProgram->getUniformLocationByName("modelMatrix"), m_world); - m_monochromeOpenGLProgram->setUniformValue(m_modelOpenGLProgram->getUniformLocationByName("viewMatrix"), m_camera); + m_monochromeOpenGLProgram->setUniformValue(m_monochromeOpenGLProgram->getUniformLocationByName("projectionMatrix"), m_projection); + m_monochromeOpenGLProgram->setUniformValue(m_monochromeOpenGLProgram->getUniformLocationByName("modelMatrix"), m_world); + m_monochromeOpenGLProgram->setUniformValue(m_monochromeOpenGLProgram->getUniformLocationByName("viewMatrix"), m_camera); if (m_wireframeOpenGLObject) m_wireframeOpenGLObject->draw(); @@ -538,12 +534,15 @@ void ModelWidget::drawModel() m_modelOpenGLProgram->setUniformValue(m_modelOpenGLProgram->getUniformLocationByName("eyePosition"), m_eyePosition); m_modelOpenGLProgram->setUniformValue(m_modelOpenGLProgram->getUniformLocationByName("projectionMatrix"), m_projection); m_modelOpenGLProgram->setUniformValue(m_modelOpenGLProgram->getUniformLocationByName("modelMatrix"), m_world); + m_modelOpenGLProgram->setUniformValue(m_modelOpenGLProgram->getUniformLocationByName("normalMatrix"), m_world.normalMatrix()); m_modelOpenGLProgram->setUniformValue(m_modelOpenGLProgram->getUniformLocationByName("viewMatrix"), m_camera); - m_modelOpenGLProgram->bindEnvironment(); + m_modelOpenGLProgram->bindMaps(); if (m_modelOpenGLObject) m_modelOpenGLObject->draw(); + m_modelOpenGLProgram->releaseMaps(); + m_modelOpenGLProgram->release(); } diff --git a/application/sources/model_widget.h b/application/sources/model_widget.h index 3da881fd..d5612e7b 100644 --- a/application/sources/model_widget.h +++ b/application/sources/model_widget.h @@ -36,12 +36,9 @@ public: ~ModelWidget(); void updateMesh(ModelMesh *mesh); void updateWireframeMesh(MonochromeMesh *mesh); - void updateColorTexture(QImage *colorTextureImage); void toggleWireframe(); bool isWireframeVisible(); void toggleRotation(); - void enableEnvironmentLight(); - bool isEnvironmentLightEnabled(); void enableMove(bool enabled); void enableZoom(bool enabled); void enableMousePicking(bool enabled); @@ -113,7 +110,6 @@ private: bool m_moveAndZoomByWindow = true; bool m_enableCullFace = true; bool m_notGraphics = false; - bool m_isEnvironmentLightEnabled = false; bool m_isWireframeVisible = false; std::pair screenPositionToMouseRay(const QPoint &screenPosition); void updateProjectionMatrix(); diff --git a/application/sources/monochrome_mesh.cc b/application/sources/monochrome_mesh.cc index 7e45e8ff..f1a82896 100644 --- a/application/sources/monochrome_mesh.cc +++ b/application/sources/monochrome_mesh.cc @@ -13,6 +13,45 @@ MonochromeMesh::MonochromeMesh(MonochromeMesh &&mesh) MonochromeMesh::MonochromeMesh(const dust3d::Object &object) { + /* + auto buildSection = [](const dust3d::Vector3 &origin, + const dust3d::Vector3 &faceNormal, + const dust3d::Vector3 &tagent, + double radius) + { + auto upDirection = dust3d::Vector3::crossProduct(tagent, faceNormal); + return std::vector { + origin + tagent * radius, + origin - upDirection * radius, + origin - tagent * radius, + origin + upDirection * radius + }; + }; + + auto calculateTagent = [](const dust3d::Vector3 &direction) { + const std::vector axisList = { + dust3d::Vector3 {1, 0, 0}, + dust3d::Vector3 {0, 1, 0}, + dust3d::Vector3 {0, 0, 1}, + }; + float maxDot = -1; + size_t nearAxisIndex = 0; + bool reversed = false; + for (size_t i = 0; i < axisList.size(); ++i) { + const auto axis = axisList[i]; + auto dot = dust3d::Vector3::dotProduct(axis, direction); + auto positiveDot = std::abs(dot); + if (positiveDot >= maxDot) { + reversed = dot < 0; + maxDot = positiveDot; + nearAxisIndex = i; + } + } + const auto& choosenAxis = axisList[(nearAxisIndex + 1) % 3]; + auto startDirection = dust3d::Vector3::crossProduct(direction, choosenAxis).normalized(); + return reversed ? -startDirection : startDirection; + }; + */ std::set> halfEdges; for (const auto &face: object.triangleAndQuads) { if (3 == face.size()) { @@ -33,6 +72,7 @@ MonochromeMesh::MonochromeMesh(const dust3d::Object &object) halfEdges.insert({face[3], face[0]}); halfEdges.insert({face[0], face[3]}); } + //m_triangleVertices.reserve(halfEdges.size() * 24); m_lineVertices.reserve(halfEdges.size() * 2); for (const auto &halfEdge: halfEdges) { // For two halfedges shared one edge, only output one line @@ -40,8 +80,60 @@ MonochromeMesh::MonochromeMesh(const dust3d::Object &object) continue; const auto &from = object.vertices[halfEdge.first]; const auto &to = object.vertices[halfEdge.second]; - m_lineVertices.emplace_back(MonochromeOpenGLVertex {(GLfloat)from.x(), (GLfloat)from.y(), (GLfloat)from.z()}); - m_lineVertices.emplace_back(MonochromeOpenGLVertex {(GLfloat)to.x(), (GLfloat)to.y(), (GLfloat)to.z()}); + m_lineVertices.emplace_back(MonochromeOpenGLVertex { + (GLfloat)from.x(), + (GLfloat)from.y(), + (GLfloat)from.z() + }); + m_lineVertices.emplace_back(MonochromeOpenGLVertex { + (GLfloat)to.x(), + (GLfloat)to.y(), + (GLfloat)to.z() + }); + /* + auto direction = to - from; + auto sectionNormal = direction.normalized(); + auto sectionTagent = calculateTagent(sectionNormal); + auto sectionTo = buildSection(to, sectionNormal, sectionTagent, 0.002); + auto sectionFrom = sectionTo; + for (auto &it: sectionFrom) + it = it - direction; + + for (size_t i = 0; i < sectionTo.size(); ++i) { + size_t j = (i + 1) % sectionTo.size(); + m_triangleVertices.emplace_back(MonochromeOpenGLVertex { + (GLfloat)sectionTo[i].x(), + (GLfloat)sectionTo[i].y(), + (GLfloat)sectionTo[i].z() + }); + m_triangleVertices.emplace_back(MonochromeOpenGLVertex { + (GLfloat)sectionFrom[i].x(), + (GLfloat)sectionFrom[i].y(), + (GLfloat)sectionFrom[i].z() + }); + m_triangleVertices.emplace_back(MonochromeOpenGLVertex { + (GLfloat)sectionTo[j].x(), + (GLfloat)sectionTo[j].y(), + (GLfloat)sectionTo[j].z() + }); + + m_triangleVertices.emplace_back(MonochromeOpenGLVertex { + (GLfloat)sectionFrom[i].x(), + (GLfloat)sectionFrom[i].y(), + (GLfloat)sectionFrom[i].z() + }); + m_triangleVertices.emplace_back(MonochromeOpenGLVertex { + (GLfloat)sectionFrom[j].x(), + (GLfloat)sectionFrom[j].y(), + (GLfloat)sectionFrom[j].z() + }); + m_triangleVertices.emplace_back(MonochromeOpenGLVertex { + (GLfloat)sectionTo[j].x(), + (GLfloat)sectionTo[j].y(), + (GLfloat)sectionTo[j].z() + }); + } + */ } } diff --git a/application/sources/monochrome_opengl_program.h b/application/sources/monochrome_opengl_program.h index 6f6460d4..aac445f0 100644 --- a/application/sources/monochrome_opengl_program.h +++ b/application/sources/monochrome_opengl_program.h @@ -1,6 +1,7 @@ #ifndef DUST3D_APPLICATION_MONOCHROME_OPENGL_PROGRAM_H_ #define DUST3D_APPLICATION_MONOCHROME_OPENGL_PROGRAM_H_ +#include #include #include diff --git a/application/sources/texture_generator.cc b/application/sources/texture_generator.cc index 4ccaa648..653c87c8 100644 --- a/application/sources/texture_generator.cc +++ b/application/sources/texture_generator.cc @@ -652,7 +652,7 @@ void TextureGenerator::generate() if (nullptr != m_resultTextureNormalImage) m_resultMesh->setNormalMapImage(new QImage(*m_resultTextureNormalImage)); if (hasMetalnessMap || hasRoughnessMap || hasAmbientOcclusionMap) { - m_resultMesh->setMetalnessRoughnessAmbientOcclusionImage(combineMetalnessRoughnessAmbientOcclusionImages( + m_resultMesh->setMetalnessRoughnessAmbientOcclusionMapImage(combineMetalnessRoughnessAmbientOcclusionImages( m_resultTextureMetalnessImage, m_resultTextureRoughnessImage, m_resultTextureAmbientOcclusionImage));