From 91078cbc6754779d542eeef59de1c05e7dff4d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20De=20Lillo?= Date: Fri, 14 Jun 2024 19:44:37 +0200 Subject: [PATCH 1/5] [qtAliceVision] FloatImageViewer: Fix source texture initialization Source texture should always be initialized to avoid errors (texture binding) when no image is loaded, especially when `FloatImageViewer` is reactivated. --- src/qtAliceVision/FloatImageViewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qtAliceVision/FloatImageViewer.cpp b/src/qtAliceVision/FloatImageViewer.cpp index c21b8459..c0b0a797 100644 --- a/src/qtAliceVision/FloatImageViewer.cpp +++ b/src/qtAliceVision/FloatImageViewer.cpp @@ -58,7 +58,7 @@ class FloatImageViewerMaterial : public QSGMaterial bool dirtyUniforms; bool appliedHoveringGamma; - std::unique_ptr texture; + std::unique_ptr texture = std::make_unique(); // should be initialize; }; class FloatImageViewerMaterialShader : public QSGMaterialShader From 7ae8c25d32f7d66e5f8fde5b49d625b57e7672af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20De=20Lillo?= Date: Fri, 14 Jun 2024 19:46:11 +0200 Subject: [PATCH 2/5] [qtAliceVision] FloatImageViewer: Add class name to warning log --- src/qtAliceVision/FloatImageViewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qtAliceVision/FloatImageViewer.cpp b/src/qtAliceVision/FloatImageViewer.cpp index c0b0a797..dc05d1b7 100644 --- a/src/qtAliceVision/FloatImageViewer.cpp +++ b/src/qtAliceVision/FloatImageViewer.cpp @@ -451,7 +451,7 @@ void FloatImageViewer::reload() } else if (_outdated) { - qWarning() << "[QtAliceVision] The loading status has not been updated since the last reload. Something wrong might have happened."; + qWarning() << "[QtAliceVision] FloatImageViewer: The loading status has not been updated since the last reload. Something wrong might have happened."; setStatus(EStatus::OUTDATED_LOADING); } Q_EMIT cachedFramesChanged(); From 429da3014ae81bc26987cba29cc54d054ea72bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20De=20Lillo?= Date: Fri, 14 Jun 2024 19:47:49 +0200 Subject: [PATCH 3/5] [qtAliceVision] Add `PhongImageViewer` shaders --- src/qtAliceVision/CMakeLists.txt | 1 + src/qtAliceVision/PhongImageViewer.frag | 64 +++++++++++++++++++++++++ src/qtAliceVision/PhongImageViewer.vert | 33 +++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 src/qtAliceVision/PhongImageViewer.frag create mode 100644 src/qtAliceVision/PhongImageViewer.vert diff --git a/src/qtAliceVision/CMakeLists.txt b/src/qtAliceVision/CMakeLists.txt index b8cb0513..9b2d1c17 100644 --- a/src/qtAliceVision/CMakeLists.txt +++ b/src/qtAliceVision/CMakeLists.txt @@ -77,6 +77,7 @@ qt6_add_shaders(qtAliceVisionPlugin "qtAliceVisionPlugin_shaders" PREFIX "/shaders" FILES FloatImageViewer.vert FloatImageViewer.frag FeaturesViewer.vert FeaturesViewer.frag + PhongImageViewer.vert PhongImageViewer.frag ImageOverlay.frag AttributeItemDelegate.frag ) diff --git a/src/qtAliceVision/PhongImageViewer.frag b/src/qtAliceVision/PhongImageViewer.frag new file mode 100644 index 00000000..85a11e3d --- /dev/null +++ b/src/qtAliceVision/PhongImageViewer.frag @@ -0,0 +1,64 @@ +#version 440 +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + + // warning: matches layout and padding of PhongImageViewerMaterial::uniforms + + mat4 qt_Matrix; // offset 0 + float qt_Opacity; // offset 64 + float _padding1; + float _padding2; + float _padding3; + vec4 channelOrder; // offset 80 + vec4 baseColor; + vec3 lightDirection; + float textureOpacity; + float gamma; + float gain; + float ka; + float kd; + float ks; + float shininess; +}; + +layout(binding = 1) uniform sampler2D sourceSampler; +layout(binding = 2) uniform sampler2D normalSampler; + +void main() +{ + vec4 diffuseColor; + vec4 tColor = texture(sourceSampler, vTexCoord); + vec3 normal = texture(normalSampler, vTexCoord).rgb; + + // apply gamma and gain on linear source texture + tColor.rgb = pow((tColor.rgb * vec3(gain)), vec3(1.0/gamma)); + + // apply channel mode + diffuseColor.r = tColor[int(channelOrder[0])]; + diffuseColor.g = tColor[int(channelOrder[1])]; + diffuseColor.b = tColor[int(channelOrder[2])]; + diffuseColor.a = int(channelOrder[3]) == -1 ? 1.0 : tColor[int(channelOrder[3])]; + + // apply texture opacity + diffuseColor *= textureOpacity; + diffuseColor += baseColor * (1.0 - textureOpacity); + diffuseColor.a *= qt_Opacity; + + // Blinn-Phong shading + float lambertian = max(dot(lightDirection, normal), 0.0); + float specular = 0.0; + + if (lambertian > 0.0) + { + vec3 viewDir = vec3(0, 0, -1); + vec3 halfDir = normalize(lightDirection + viewDir); + float specularAngle = max(dot(halfDir, normal), 0.0); + specular = pow(specularAngle, shininess); + } + + vec3 outLinear = vec3(ka, ka, ka) + kd * diffuseColor.rgb * lambertian + vec3(ks, ks, ks) * specular; + vec3 outGammaCorrected = pow(outLinear, vec3(1.0 / 2.2)); // assume the monitor is calibrated to the sRGB colorspace + fragColor = vec4(outGammaCorrected, diffuseColor.a); +} \ No newline at end of file diff --git a/src/qtAliceVision/PhongImageViewer.vert b/src/qtAliceVision/PhongImageViewer.vert new file mode 100644 index 00000000..a1d04fc5 --- /dev/null +++ b/src/qtAliceVision/PhongImageViewer.vert @@ -0,0 +1,33 @@ +#version 440 +layout(location = 0) in vec4 position; +layout(location = 1) in vec2 texCoord; +layout(location = 0) out vec2 vTexCoord; + +layout(std140, binding = 0) uniform buf { + + // warning: matches layout and padding of PhongImageViewerMaterial::uniforms + + mat4 qt_Matrix; // offset 0 + float qt_Opacity; // offset 64 + float _padding1; + float _padding2; + float _padding3; + vec4 channelOrder; // offset 80 + vec4 baseColor; + vec3 lightDirection; + float textureOpacity; + float gamma; + float gain; + float ka; + float kd; + float ks; + float shininess; +}; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + vTexCoord = texCoord; + gl_Position = qt_Matrix * position; +} \ No newline at end of file From 47c21294ea11255d12b52ffd0f9580f631186d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20De=20Lillo?= Date: Fri, 14 Jun 2024 19:51:42 +0200 Subject: [PATCH 4/5] [qtAliceVision] Add `PhongImageViewer` Allows to load and display image (albedo and normal) with a given light direction. Shading is done using Blinn-Phong reflection model. --- src/qtAliceVision/CMakeLists.txt | 2 + src/qtAliceVision/PhongImageViewer.cpp | 419 +++++++++++++++++++++++++ src/qtAliceVision/PhongImageViewer.hpp | 155 +++++++++ src/qtAliceVision/plugin.hpp | 2 + 4 files changed, 578 insertions(+) create mode 100644 src/qtAliceVision/PhongImageViewer.cpp create mode 100644 src/qtAliceVision/PhongImageViewer.hpp diff --git a/src/qtAliceVision/CMakeLists.txt b/src/qtAliceVision/CMakeLists.txt index 9b2d1c17..2a09216a 100644 --- a/src/qtAliceVision/CMakeLists.txt +++ b/src/qtAliceVision/CMakeLists.txt @@ -10,6 +10,7 @@ set(PLUGIN_SOURCES Surface.cpp MSfMDataStats.cpp PanoramaViewer.cpp + PhongImageViewer.cpp Painter.cpp SequenceCache.cpp SingleImageLoader.cpp @@ -28,6 +29,7 @@ set(PLUGIN_HEADERS FloatTexture.hpp MSfMDataStats.hpp PanoramaViewer.hpp + PhongImageViewer.hpp Surface.hpp Painter.hpp ImageServer.hpp diff --git a/src/qtAliceVision/PhongImageViewer.cpp b/src/qtAliceVision/PhongImageViewer.cpp new file mode 100644 index 00000000..572991a5 --- /dev/null +++ b/src/qtAliceVision/PhongImageViewer.cpp @@ -0,0 +1,419 @@ +#include "PhongImageViewer.hpp" + +#include +#include +#include +#include + +namespace qtAliceVision { + +namespace +{ + class PhongImageViewerMaterialShader; + + class PhongImageViewerMaterial : public QSGMaterial + { + public: + PhongImageViewerMaterial() {} + + QSGMaterialType* type() const override + { + static QSGMaterialType type; + return &type; + } + + int compare(const QSGMaterial* other) const override + { + Q_ASSERT(other && type() == other->type()); + return other == this ? 0 : (other > this ? 1 : -1); + } + + QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override; + + struct + { + // warning: matches layout and padding of PhongImageViewer.vert/frag shaders + QVector4D channelOrder = QVector4D(0, 1, 2, 3); + QVector4D baseColor = QVector4D(.2f, .2f, .2f, 1.f); + QVector3D lightDirection = QVector3D(0.f, 0.f, -1.f); + float textureOpacity = 1.f; + float gamma = 1.f; + float gain = 0.f; + float ka = 0.f; + float kd = 1.f; + float ks = 1.f; + float shininess = 20.f; + + } uniforms; + + std::unique_ptr sourceTexture = std::make_unique(); // should be initialized + std::unique_ptr normalTexture = std::make_unique(); // should be initialized + bool dirtyUniforms; + }; + + class PhongImageViewerMaterialShader : public QSGMaterialShader + { + public: + PhongImageViewerMaterialShader() + { + setShaderFileName(VertexStage, QLatin1String(":/shaders/PhongImageViewer.vert.qsb")); + setShaderFileName(FragmentStage, QLatin1String(":/shaders/PhongImageViewer.frag.qsb")); + } + + bool updateUniformData(RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override + { + bool changed = false; + QByteArray* buf = state.uniformData(); + Q_ASSERT(buf->size() >= 84); + if (state.isMatrixDirty()) + { + const QMatrix4x4 m = state.combinedMatrix(); + memcpy(buf->data() + 0, m.constData(), 64); + changed = true; + } + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(buf->data() + 64, &opacity, 4); + changed = true; + } + auto* customMaterial = static_cast(newMaterial); + if (oldMaterial != newMaterial || customMaterial->dirtyUniforms) { + memcpy(buf->data() + 80, &customMaterial->uniforms, 72); // uniforms size is 72 + customMaterial->dirtyUniforms = false; + changed = true; + } + return changed; + } + + void updateSampledImage(RenderState& state, int binding, QSGTexture** texture, QSGMaterial* newMaterial, QSGMaterial*) override + { + PhongImageViewerMaterial* mat = static_cast(newMaterial); + + if (binding == 1) + { + mat->sourceTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch()); + *texture = mat->sourceTexture.get(); + } + + if (binding == 2) + { + mat->normalTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch()); + *texture = mat->normalTexture.get(); + } + } + }; + + QSGMaterialShader* PhongImageViewerMaterial::createShader(QSGRendererInterface::RenderMode) const + { + return new PhongImageViewerMaterialShader; + } + + class PhongImageViewerNode : public QSGGeometryNode + { + public: + PhongImageViewerNode() + { + auto* m = new PhongImageViewerMaterial; + setMaterial(m); + setFlag(OwnsMaterial, true); + QSGGeometry* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0); + geometry->setIndexDataPattern(QSGGeometry::StaticPattern); + geometry->setVertexDataPattern(QSGGeometry::StaticPattern); + setGeometry(geometry); + setFlag(OwnsGeometry, true); + } + + void setBlending(bool value) + { + auto* m = static_cast(material()); + m->setFlag(QSGMaterial::Blending, value); + } + + void setEmptyGeometry() + { + auto* g = geometry(); + + if (g->vertexCount() != 0) + { + g->allocate(0); + markDirty(QSGNode::DirtyGeometry); + } + } + + void setRectGeometry(const QRectF& bounds) + { + auto* g = geometry(); + + if (g->vertexCount() != 4) + { + geometry()->allocate(4); + } + + QSGGeometry::updateTexturedRectGeometry(g, bounds, QRectF(0, 0, 1, 1)); + + markDirty(QSGNode::DirtyGeometry); + } + + void setSourceParameters(const QVector4D& channelOrder, float gamma, float gain) + { + auto* m = static_cast(material()); + m->uniforms.gamma = gamma; + m->uniforms.gain = gain; + m->uniforms.channelOrder = channelOrder; + m->dirtyUniforms = true; + markDirty(DirtyMaterial); + } + + void setShadingParameters(const QVector4D& baseColor, const QVector3D& lightDirection, float textureOpacity, float ka, float kd, float ks, float shininess) + { + auto* m = static_cast(material()); + m->uniforms.baseColor = baseColor; + m->uniforms.lightDirection = lightDirection; + m->uniforms.textureOpacity = textureOpacity; + m->uniforms.ka = ka; + m->uniforms.kd = kd; + m->uniforms.ks = ks; + m->uniforms.shininess = shininess; + m->dirtyUniforms = true; + markDirty(DirtyMaterial); + } + + void setTextures(std::unique_ptr sourceTexture, std::unique_ptr normalTexture) + { + auto* m = static_cast(material()); + m->sourceTexture = std::move(sourceTexture); + m->normalTexture = std::move(normalTexture); + markDirty(DirtyMaterial); + } + }; +} + +PhongImageViewer::PhongImageViewer(QQuickItem* parent) + : QQuickItem(parent) +{ + // flags + setFlag(QQuickItem::ItemHasContents, true); + + // connects + connect(this, &PhongImageViewer::sourcePathChanged, this, &PhongImageViewer::reload); + connect(this, &PhongImageViewer::normalPathChanged, this, &PhongImageViewer::reload); + connect(this, &PhongImageViewer::sourceParametersChanged, this, [this] { _sourceParametersChanged = true; update(); }); + connect(this, &PhongImageViewer::shadingParametersChanged, this, [this] { _shadingParametersChanged = true; update(); }); + connect(this, &PhongImageViewer::textureSizeChanged, this, &PhongImageViewer::update); + connect(this, &PhongImageViewer::sourceSizeChanged, this, &PhongImageViewer::update); + connect(this, &PhongImageViewer::imageChanged, this, &PhongImageViewer::update); + connect(&_sourceImageLoader, &imgserve::SingleImageLoader::requestHandled, this, &PhongImageViewer::reload); + connect(&_normalImageLoader, &imgserve::SingleImageLoader::requestHandled, this, &PhongImageViewer::reload); +} + +PhongImageViewer::~PhongImageViewer() {} + +void PhongImageViewer::setStatus(EStatus status) +{ + if (_status == status) { return; } + _status = status; + Q_EMIT statusChanged(); +} + +void PhongImageViewer::reload() +{ + // check source image path and normal image path + if (!_sourcePath.isValid() || !_normalPath.isValid()) + { + clearImages(); + setStatus(EStatus::MISSING_FILE); + return; + } + + // use image server to request input images + imgserve::RequestData requestSourceImage = {_sourcePath.toLocalFile().toUtf8().toStdString()}; + imgserve::RequestData requestNormalImage = {_normalPath.toLocalFile().toUtf8().toStdString()}; + imgserve::ResponseData responseSourceImage = _sourceImageLoader.request(requestSourceImage); + imgserve::ResponseData responseNormalImage = _normalImageLoader.request(requestNormalImage); + + // check for loading errors + if ((responseSourceImage.img == nullptr) || (responseNormalImage.img == nullptr)) + { + if (responseSourceImage.error != imgserve::LoadingStatus::SUCCESSFUL) + { + clearImages(); + setStatus((responseSourceImage.error == imgserve::LoadingStatus::MISSING_FILE) ? EStatus::MISSING_FILE : EStatus::LOADING_ERROR); + } + else if (responseNormalImage.error != imgserve::LoadingStatus::SUCCESSFUL) + { + clearImages(); + setStatus((responseNormalImage.error == imgserve::LoadingStatus::MISSING_FILE) ? EStatus::MISSING_FILE : EStatus::LOADING_ERROR); + } + else + { + setStatus(EStatus::LOADING); + } + return; + } + + // check source and normal images dimensions + if (responseSourceImage.dim != responseSourceImage.dim) + { + clearImages(); + setStatus(EStatus::LOADING_ERROR); + return; + } + + // loading done + setStatus(EStatus::NONE); + + // copy source image + _sourceImage = responseSourceImage.img; + + // copy normal image + _normalImage = responseNormalImage.img; + + // images have changed + _imageChanged = true; + Q_EMIT imageChanged(); + + // source image dimensions have changed + _sourceSize = responseSourceImage.dim; + Q_EMIT sourceSizeChanged(); + + // source image metadata have changed + _metadata = responseSourceImage.metadata; + Q_EMIT metadataChanged(); +} + +void PhongImageViewer::clearImages() +{ + _sourceImage.reset(); + _normalImage.reset(); + _imageChanged = true; + Q_EMIT imageChanged(); +} + +QSGNode* PhongImageViewer::updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData* data) +{ + auto* node = static_cast(oldNode); + bool isNewNode = false; + + if (!node) + { + node = new PhongImageViewerNode(); + isNewNode = true; + } + + // update textures + if (_imageChanged) + { + QSize newTextureSize; + auto sourceTexture = std::make_unique(); + auto normalTexture = std::make_unique(); + + if(_sourceImage) + { + sourceTexture->setImage(_sourceImage); + sourceTexture->setFiltering(QSGTexture::Nearest); + sourceTexture->setHorizontalWrapMode(QSGTexture::Repeat); + sourceTexture->setVerticalWrapMode(QSGTexture::Repeat); + newTextureSize = sourceTexture->textureSize(); + } + + if (_normalImage) + { + normalTexture->setImage(_normalImage); + normalTexture->setFiltering(QSGTexture::Nearest); + normalTexture->setHorizontalWrapMode(QSGTexture::Repeat); + normalTexture->setVerticalWrapMode(QSGTexture::Repeat); + } + + node->setTextures(std::move(sourceTexture), std::move(normalTexture)); + + if (_textureSize != newTextureSize) + { + _textureSize = newTextureSize; + _geometryChanged = true; + Q_EMIT textureSizeChanged(); + } + + _imageChanged = false; + } + + const auto newBoundingRect = boundingRect(); + + // update geometry + if (_geometryChanged || _boundingRect != newBoundingRect) + { + _boundingRect = newBoundingRect; + + if (_textureSize.width() <= 0 || _textureSize.height() <= 0) + { + node->setEmptyGeometry(); + } + else + { + const float windowRatio = _boundingRect.width() / _boundingRect.height(); + const float textureRatio = _textureSize.width() / float(_textureSize.height()); + QRectF geometryRect = _boundingRect; + + if (windowRatio > textureRatio) + geometryRect.setWidth(geometryRect.height() * textureRatio); + else + geometryRect.setHeight(geometryRect.width() / textureRatio); + + geometryRect.moveCenter(_boundingRect.center()); + + static const int MARGIN = 0; + geometryRect = geometryRect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN); + + node->setRectGeometry(geometryRect); + } + + _geometryChanged = false; + } + + // update shader source parameters + if (isNewNode || _sourceParametersChanged) + { + QVector4D channelOrder(0.f, 1.f, 2.f, 3.f); + + switch (_channelMode) + { + case EChannelMode::R: + channelOrder = QVector4D(0.f, 0.f, 0.f, -1.f); + break; + case EChannelMode::G: + channelOrder = QVector4D(1.f, 1.f, 1.f, -1.f); + break; + case EChannelMode::B: + channelOrder = QVector4D(2.f, 2.f, 2.f, -1.f); + break; + case EChannelMode::A: + channelOrder = QVector4D(3.f, 3.f, 3.f, -1.f); + break; + } + + node->setSourceParameters(channelOrder, _gamma, _gain); + node->setBlending(_channelMode == EChannelMode::RGBA); + + _sourceParametersChanged = false; + } + + // update shader Blinn-Phong parameters + if (isNewNode || _shadingParametersChanged) + { + // light direction from Yaw and Pitch + const Eigen::AngleAxis yawA(static_cast(aliceVision::degreeToRadian(_lightYaw)), Eigen::Vector3d::UnitY()); + const Eigen::AngleAxis pitchA(static_cast(aliceVision::degreeToRadian(_lightPitch)), Eigen::Vector3d::UnitX()); + const aliceVision::Vec3 direction = yawA.toRotationMatrix() * pitchA.toRotationMatrix() * aliceVision::Vec3(0.0, 0.0, -1.0); + const QVector3D lightDirection(direction.x(), direction.y(), direction.z()); + + // linear base color from QColor + const QVector4D baseColor(std::powf(_baseColor.redF(), 2.2), std::powf(_baseColor.greenF(), 2.2), std::powf(_baseColor.blueF(), 2.2), _baseColor.alphaF()); + + node->setShadingParameters(baseColor, lightDirection, _textureOpacity, _ka, _kd, _ks, _shininess); + + _shadingParametersChanged = false; + } + + return node; +} + +} // namespace qtAliceVision diff --git a/src/qtAliceVision/PhongImageViewer.hpp b/src/qtAliceVision/PhongImageViewer.hpp new file mode 100644 index 00000000..19e2e631 --- /dev/null +++ b/src/qtAliceVision/PhongImageViewer.hpp @@ -0,0 +1,155 @@ +#pragma once + +#include "FloatTexture.hpp" +#include "SingleImageLoader.hpp" + +#include +#include +#include + +#include + +namespace qtAliceVision { + +/** + * @brief Load and display image (albedo and normal) with a given light direction. + * Shading is done using Blinn-Phong reflection model. + */ +class PhongImageViewer : public QQuickItem +{ + Q_OBJECT + + // file paths + Q_PROPERTY(QUrl sourcePath MEMBER _sourcePath NOTIFY sourcePathChanged) + Q_PROPERTY(QUrl normalPath MEMBER _normalPath NOTIFY normalPathChanged) + + // source parameters (gamma, gain, channelMode) + Q_PROPERTY(EChannelMode channelMode MEMBER _channelMode NOTIFY sourceParametersChanged) + Q_PROPERTY(float gamma MEMBER _gamma NOTIFY sourceParametersChanged) + Q_PROPERTY(float gain MEMBER _gain NOTIFY sourceParametersChanged) + + // shading parameters (material, light) + Q_PROPERTY(QColor baseColor MEMBER _baseColor NOTIFY shadingParametersChanged) + Q_PROPERTY(float textureOpacity MEMBER _textureOpacity NOTIFY shadingParametersChanged) + Q_PROPERTY(float ka MEMBER _ka NOTIFY shadingParametersChanged) + Q_PROPERTY(float kd MEMBER _kd NOTIFY shadingParametersChanged) + Q_PROPERTY(float ks MEMBER _ks NOTIFY shadingParametersChanged) + Q_PROPERTY(float shininess MEMBER _shininess NOTIFY shadingParametersChanged) + Q_PROPERTY(float lightYaw MEMBER _lightYaw NOTIFY shadingParametersChanged) + Q_PROPERTY(float lightPitch MEMBER _lightPitch NOTIFY shadingParametersChanged) + + // texture + Q_PROPERTY(QSize textureSize MEMBER _textureSize NOTIFY textureSizeChanged) + Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) + + // metadata + Q_PROPERTY(QVariantMap metadata READ metadata NOTIFY metadataChanged) + + // viewer status + Q_PROPERTY(EStatus status READ status NOTIFY statusChanged) + + public: + + enum class EStatus : quint8 + { + NONE, // nothing is happening, no error has been detected + LOADING, // an image is being loaded + MISSING_FILE, // the file to load is missing + LOADING_ERROR, // generic error + }; + Q_ENUM(EStatus) + + enum class EChannelMode : quint8 + { + RGBA, + RGB, + R, + G, + B, + A + }; + Q_ENUM(EChannelMode) + + // constructor + explicit PhongImageViewer(QQuickItem* parent = nullptr); + + // destructor + ~PhongImageViewer() override; + + // accessors + EStatus status() const { return _status; } + const QSize& sourceSize() const { return _sourceSize; } + const QVariantMap& metadata() const { return _metadata; } + + // signals + Q_SIGNAL void sourcePathChanged(); + Q_SIGNAL void normalPathChanged(); + Q_SIGNAL void sourceParametersChanged(); + Q_SIGNAL void shadingParametersChanged(); + Q_SIGNAL void textureSizeChanged(); + Q_SIGNAL void sourceSizeChanged(); + Q_SIGNAL void metadataChanged(); + Q_SIGNAL void imageChanged(); + Q_SIGNAL void statusChanged(); + + private: + + // set viewer status + void setStatus(EStatus status); + + // reload image(s) + void reload(); + + // clear images in memory + void clearImages(); + + // custom QSGNode update + QSGNode* updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData* data) override; + + private: + + // file paths + QUrl _sourcePath; + QUrl _normalPath; + + // source parameters (gamma, gain, channelMode) + EChannelMode _channelMode = EChannelMode::RGBA; + float _gamma = 1.f; + float _gain = 1.f; + bool _sourceParametersChanged = false; + + // shading parameters (material, light) + QColor _baseColor = {50, 50, 50}; + float _textureOpacity = 1.f; + float _ka = 0.f; + float _kd = 1.f; + float _ks = 1.f; + float _shininess = 20.f; + float _lightYaw = 0.f; + float _lightPitch = 0.f; + bool _shadingParametersChanged = false; + + // metadata + QVariantMap _metadata; + + // screen and texture size + QRectF _boundingRect = QRectF(); + QSize _textureSize = QSize(0, 0); + QSize _sourceSize = QSize(0, 0); + + // image + std::shared_ptr _sourceImage; + std::shared_ptr _normalImage; + + // image loaders + imgserve::SingleImageLoader _sourceImageLoader; + imgserve::SingleImageLoader _normalImageLoader; + + // viewer status + EStatus _status = EStatus::NONE; + bool _geometryChanged = false; + bool _imageChanged = false; +}; + +} // namespace qtAliceVision + diff --git a/src/qtAliceVision/plugin.hpp b/src/qtAliceVision/plugin.hpp index 3ffd95e7..e17edd84 100644 --- a/src/qtAliceVision/plugin.hpp +++ b/src/qtAliceVision/plugin.hpp @@ -3,6 +3,7 @@ #include "ImageServer.hpp" #include "FeaturesViewer.hpp" #include "FloatImageViewer.hpp" +#include "PhongImageViewer.hpp" #include "MFeatures.hpp" #include "MSfMDataStats.hpp" #include "MTracks.hpp" @@ -50,6 +51,7 @@ class QtAliceVisionPlugin : public QQmlExtensionPlugin qRegisterMetaType("size_t"); // for usage in signals/slots qmlRegisterType(uri, 1, 0, "FloatImageViewer"); + qmlRegisterType(uri, 1, 0, "PhongImageViewer"); qmlRegisterType(uri, 1, 0, "Surface"); qmlRegisterType(uri, 1, 0, "PanoramaViewer"); qRegisterMetaType("QPointF"); From f4ac1486b39dd1e3f93d02446ab94391e659fd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Candice=20Bent=C3=A9jac?= Date: Tue, 9 Jul 2024 18:27:43 +0200 Subject: [PATCH 5/5] [qtAliceVision] PhongImageViewer: Use `std::pow` instead of `std::powf` `std::powf` fails to compile with gcc which is not compliant with C++17 when it comes to libstdc++. Additionally, static casts are performed to fix warnings. --- src/qtAliceVision/PhongImageViewer.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/qtAliceVision/PhongImageViewer.cpp b/src/qtAliceVision/PhongImageViewer.cpp index 572991a5..0e27ac36 100644 --- a/src/qtAliceVision/PhongImageViewer.cpp +++ b/src/qtAliceVision/PhongImageViewer.cpp @@ -349,8 +349,10 @@ QSGNode* PhongImageViewer::updatePaintNode(QSGNode* oldNode, QQuickItem::UpdateP } else { - const float windowRatio = _boundingRect.width() / _boundingRect.height(); - const float textureRatio = _textureSize.width() / float(_textureSize.height()); + const float windowRatio = static_cast(_boundingRect.width()) / + static_cast(_boundingRect.height()); + const float textureRatio = static_cast(_textureSize.width()) / + static_cast(_textureSize.height()); QRectF geometryRect = _boundingRect; if (windowRatio > textureRatio) @@ -388,6 +390,8 @@ QSGNode* PhongImageViewer::updatePaintNode(QSGNode* oldNode, QQuickItem::UpdateP case EChannelMode::A: channelOrder = QVector4D(3.f, 3.f, 3.f, -1.f); break; + default: + break; } node->setSourceParameters(channelOrder, _gamma, _gain); @@ -400,13 +404,21 @@ QSGNode* PhongImageViewer::updatePaintNode(QSGNode* oldNode, QQuickItem::UpdateP if (isNewNode || _shadingParametersChanged) { // light direction from Yaw and Pitch - const Eigen::AngleAxis yawA(static_cast(aliceVision::degreeToRadian(_lightYaw)), Eigen::Vector3d::UnitY()); - const Eigen::AngleAxis pitchA(static_cast(aliceVision::degreeToRadian(_lightPitch)), Eigen::Vector3d::UnitX()); - const aliceVision::Vec3 direction = yawA.toRotationMatrix() * pitchA.toRotationMatrix() * aliceVision::Vec3(0.0, 0.0, -1.0); - const QVector3D lightDirection(direction.x(), direction.y(), direction.z()); + const Eigen::AngleAxis yawA(static_cast(aliceVision::degreeToRadian(_lightYaw)), + Eigen::Vector3d::UnitY()); + const Eigen::AngleAxis pitchA(static_cast(aliceVision::degreeToRadian(_lightPitch)), + Eigen::Vector3d::UnitX()); + const aliceVision::Vec3 direction = yawA.toRotationMatrix() * pitchA.toRotationMatrix() * + aliceVision::Vec3(0.0, 0.0, -1.0); + const QVector3D lightDirection(static_cast(direction.x()), + static_cast(direction.y()), + static_cast(direction.z())); // linear base color from QColor - const QVector4D baseColor(std::powf(_baseColor.redF(), 2.2), std::powf(_baseColor.greenF(), 2.2), std::powf(_baseColor.blueF(), 2.2), _baseColor.alphaF()); + const QVector4D baseColor(std::pow(static_cast(_baseColor.redF()), 2.2f), + std::pow(static_cast(_baseColor.greenF()), 2.2f), + std::pow(static_cast(_baseColor.blueF()), 2.2f), + _baseColor.alphaF()); node->setShadingParameters(baseColor, lightDirection, _textureOpacity, _ka, _kd, _ks, _shininess);