From 78c378664f2d073c443933a12ed79c016904b8de Mon Sep 17 00:00:00 2001 From: Abdelrahman AL MAROUK Date: Thu, 10 Aug 2023 09:19:56 +0200 Subject: [PATCH 1/8] [ObservationsEntity] add support for visualisation of landmark observations - visualize Tc and Rc observations with distinct colors - filter viewed landmarks 3D based on the 2D viewer --- src/qmlAlembic/AlembicEntity.cpp | 76 +++++-- src/qmlAlembic/AlembicEntity.hpp | 21 ++ src/qmlAlembic/CMakeLists.txt | 5 +- src/qmlAlembic/ObservationsEntity.cpp | 282 ++++++++++++++++++++++++++ src/qmlAlembic/ObservationsEntity.hpp | 45 ++++ src/qmlAlembic/plugin.hpp | 2 + 6 files changed, 415 insertions(+), 16 deletions(-) create mode 100644 src/qmlAlembic/ObservationsEntity.cpp create mode 100644 src/qmlAlembic/ObservationsEntity.hpp diff --git a/src/qmlAlembic/AlembicEntity.cpp b/src/qmlAlembic/AlembicEntity.cpp index b80f03d4..d3c13c51 100644 --- a/src/qmlAlembic/AlembicEntity.cpp +++ b/src/qmlAlembic/AlembicEntity.cpp @@ -2,6 +2,7 @@ #include "IOThread.hpp" #include "CameraLocatorEntity.hpp" #include "PointCloudEntity.hpp" +#include "ObservationsEntity.hpp" #include #include #include @@ -24,6 +25,10 @@ AlembicEntity::AlembicEntity(Qt3DCore::QNode* parent) { connect(_ioThread.get(), &IOThread::finished, this, &AlembicEntity::onIOThreadFinished); createMaterials(); + + // trigger display repaint events of observations + connect(this, &AlembicEntity::viewIdChanged, this, &AlembicEntity::updateObservations); + connect(this, &AlembicEntity::viewer2DInfoChanged, this, &AlembicEntity::updateObservations); } void AlembicEntity::setSource(const QUrl& value) @@ -54,6 +59,22 @@ void AlembicEntity::setLocatorScale(const float& value) Q_EMIT locatorScaleChanged(); } +void AlembicEntity::setViewId(const int& value) +{ + if (_viewId == value) + return; + _viewId = value; + Q_EMIT viewIdChanged(); +} + +void AlembicEntity::setViewer2DInfo(const QVariantMap& value) +{ + if (_viewer2DInfo == value) + return; + _viewer2DInfo = value; + Q_EMIT viewer2DInfoChanged(); +} + void AlembicEntity::scaleLocators() const { for(auto* entity : _cameras) @@ -63,6 +84,14 @@ void AlembicEntity::scaleLocators() const } } +void AlembicEntity::updateObservations() const +{ + for (auto* entity : _observations) + { + entity->update(_viewId, _viewer2DInfo); + } +} + // private void AlembicEntity::createMaterials() { @@ -129,6 +158,7 @@ void AlembicEntity::clear() removeComponent(component); _cameras.clear(); _pointClouds.clear(); + _observations.clear(); } // private @@ -160,10 +190,13 @@ void AlembicEntity::onIOThreadFinished() // store pointers to cameras and point clouds _cameras = findChildren(); _pointClouds = findChildren(); + _observations = findChildren(); // perform initial locator scaling scaleLocators(); + updateObservations(); + setStatus(AlembicEntity::Ready); } catch(...) @@ -174,6 +207,7 @@ void AlembicEntity::onIOThreadFinished() _ioThread->clear(); Q_EMIT camerasChanged(); Q_EMIT pointCloudsChanged(); + Q_EMIT observationsChanged(); } // private @@ -182,18 +216,25 @@ void AlembicEntity::visitAbcObject(const Alembic::Abc::IObject& iObj, QEntity* p using namespace Alembic::Abc; using namespace Alembic::AbcGeom; - const auto createEntity = [&](const IObject&) -> BaseAlembicObject* { + const auto createEntities = [&](const IObject&) -> std::vector { const MetaData& md = iObj.getMetaData(); if(IPoints::matches(md)) { + std::vector entities(2); IPoints points(iObj, Alembic::Abc::kWrapExisting); - PointCloudEntity* entity = new PointCloudEntity(parent); - entity->setData(iObj); - entity->addComponent(_cloudMaterial); - entity->fillArbProperties(points.getSchema().getArbGeomParams()); - entity->fillUserProperties(points.getSchema().getUserProperties()); - return entity; + PointCloudEntity* entity0 = new PointCloudEntity(parent); + entity0->setData(iObj); + entity0->addComponent(_cloudMaterial); + entity0->fillArbProperties(points.getSchema().getArbGeomParams()); + entity0->fillUserProperties(points.getSchema().getUserProperties()); + entities[0] = entity0; + ObservationsEntity* entity1 = new ObservationsEntity(_source.toLocalFile().toStdString(), parent); + entity1->setData(iObj); + entity1->fillArbProperties(points.getSchema().getArbGeomParams()); + entity1->fillUserProperties(points.getSchema().getUserProperties()); + entities[1] = entity1; + return entities; } else if(IXform::matches(md)) { @@ -204,7 +245,7 @@ void AlembicEntity::visitAbcObject(const Alembic::Abc::IObject& iObj, QEntity* p entity->setTransform(xs.getMatrix()); entity->fillArbProperties(xform.getSchema().getArbGeomParams()); entity->fillUserProperties(xform.getSchema().getUserProperties()); - return entity; + return {entity}; } else if(ICamera::matches(md)) { @@ -213,12 +254,12 @@ void AlembicEntity::visitAbcObject(const Alembic::Abc::IObject& iObj, QEntity* p entity->addComponent(_cameraMaterial); entity->fillArbProperties(cam.getSchema().getArbGeomParams()); entity->fillUserProperties(cam.getSchema().getUserProperties()); - return entity; + return {entity}; } else { // fallback: create empty object to preserve hierarchy - return new BaseAlembicObject(parent); + return {new BaseAlembicObject(parent)}; } }; @@ -235,12 +276,17 @@ void AlembicEntity::visitAbcObject(const Alembic::Abc::IObject& iObj, QEntity* p } } - BaseAlembicObject* entity = createEntity(iObj); - entity->setObjectName(iObj.getName().c_str()); + std::vector entities = createEntities(iObj); + + for (int j = 0; j < entities.size(); j++) + { + auto entity = entities[j]; + entity->setObjectName((iObj.getName() + std::to_string(j)).c_str()); - // visit children - for(size_t i = 0; i < iObj.getNumChildren(); i++) - visitAbcObject(iObj.getChild(i), entity); + // visit children + for (size_t i = 0; i < iObj.getNumChildren(); i++) + visitAbcObject(iObj.getChild(i), entity); + } } } // namespace diff --git a/src/qmlAlembic/AlembicEntity.hpp b/src/qmlAlembic/AlembicEntity.hpp index 6b058ed3..23e12cf1 100644 --- a/src/qmlAlembic/AlembicEntity.hpp +++ b/src/qmlAlembic/AlembicEntity.hpp @@ -13,6 +13,7 @@ namespace abcentity { class CameraLocatorEntity; class PointCloudEntity; +class ObservationsEntity; class IOThread; class AlembicEntity : public Qt3DCore::QEntity @@ -22,8 +23,12 @@ class AlembicEntity : public Qt3DCore::QEntity Q_PROPERTY(bool skipHidden MEMBER _skipHidden NOTIFY skipHiddenChanged) Q_PROPERTY(float pointSize READ pointSize WRITE setPointSize NOTIFY pointSizeChanged) Q_PROPERTY(float locatorScale READ locatorScale WRITE setLocatorScale NOTIFY locatorScaleChanged) + Q_PROPERTY(int viewId READ viewId WRITE setViewId NOTIFY viewIdChanged) + Q_PROPERTY(QVariantMap viewer2DInfo READ viewer2DInfo WRITE setViewer2DInfo NOTIFY viewer2DInfoChanged) Q_PROPERTY(QQmlListProperty cameras READ cameras NOTIFY camerasChanged) Q_PROPERTY(QQmlListProperty pointClouds READ pointClouds NOTIFY pointCloudsChanged) + Q_PROPERTY( + QQmlListProperty observations READ observations NOTIFY observationsChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) @@ -43,9 +48,13 @@ class AlembicEntity : public Qt3DCore::QEntity Q_SLOT const QUrl& source() const { return _source; } Q_SLOT float pointSize() const { return _pointSize; } Q_SLOT float locatorScale() const { return _locatorScale; } + Q_SLOT float viewId() const { return _viewId; } + Q_SLOT QVariantMap viewer2DInfo() const { return _viewer2DInfo; } Q_SLOT void setSource(const QUrl& source); Q_SLOT void setPointSize(const float& value); Q_SLOT void setLocatorScale(const float& value); + Q_SLOT void setViewId(const int& value); + Q_SLOT void setViewer2DInfo(const QVariantMap& value); Status status() const { return _status; } void setStatus(Status status) { @@ -70,12 +79,19 @@ class AlembicEntity : public Qt3DCore::QEntity return {this, &_pointClouds}; } + QQmlListProperty observations() { + return {this, &_observations}; + } + public: Q_SIGNAL void sourceChanged(); Q_SIGNAL void camerasChanged(); Q_SIGNAL void pointSizeChanged(); Q_SIGNAL void pointCloudsChanged(); + Q_SIGNAL void observationsChanged(); Q_SIGNAL void locatorScaleChanged(); + Q_SIGNAL void viewIdChanged(); + Q_SIGNAL void viewer2DInfoChanged(); Q_SIGNAL void objectPicked(Qt3DCore::QTransform* transform); Q_SIGNAL void statusChanged(Status status); Q_SIGNAL void skipHiddenChanged(); @@ -84,6 +100,8 @@ class AlembicEntity : public Qt3DCore::QEntity /// Scale child locators void scaleLocators() const; + void updateObservations() const; + void onIOThreadFinished(); private: @@ -92,11 +110,14 @@ class AlembicEntity : public Qt3DCore::QEntity bool _skipHidden = false; float _pointSize = 0.5f; float _locatorScale = 1.0f; + int _viewId = 0; + QVariantMap _viewer2DInfo; Qt3DRender::QParameter* _pointSizeParameter; Qt3DRender::QMaterial* _cloudMaterial; Qt3DRender::QMaterial* _cameraMaterial; QList _cameras; QList _pointClouds; + QList _observations; std::unique_ptr _ioThread; }; diff --git a/src/qmlAlembic/CMakeLists.txt b/src/qmlAlembic/CMakeLists.txt index 68383ce3..bc32cbca 100644 --- a/src/qmlAlembic/CMakeLists.txt +++ b/src/qmlAlembic/CMakeLists.txt @@ -5,6 +5,7 @@ set(PLUGIN_SOURCES CameraLocatorEntity.cpp IOThread.cpp PointCloudEntity.cpp + ObservationsEntity.cpp ) set(PLUGIN_HEADERS @@ -13,6 +14,7 @@ set(PLUGIN_HEADERS CameraLocatorEntity.hpp IOThread.hpp PointCloudEntity.hpp + ObservationsEntity.hpp plugin.hpp ) @@ -49,8 +51,9 @@ target_link_libraries(alembicEntityQmlPlugin Qt5::3DCore Qt5::3DRender Alembic::Alembic - PRIVATE Qt5::3DExtras + aliceVision_sfmData + aliceVision_sfmDataIO ) set_target_properties(alembicEntityQmlPlugin diff --git a/src/qmlAlembic/ObservationsEntity.cpp b/src/qmlAlembic/ObservationsEntity.cpp new file mode 100644 index 00000000..2db75c03 --- /dev/null +++ b/src/qmlAlembic/ObservationsEntity.cpp @@ -0,0 +1,282 @@ +#include "ObservationsEntity.hpp" +#include +#include +#include +#include + +#include + +using namespace Qt3DRender; +using namespace aliceVision; + +namespace abcentity +{ + +static const auto& TC_INDEX_ATTRIBUTE_NAME = "Tc-observations indices"; +static const auto& RC_INDEX_ATTRIBUTE_NAME = "Rc-observations indices"; + +/** + * @brief encapsulates sufficient and fast retrievable information on a landmark observed by a view + */ +struct LandmarkPerView +{ + LandmarkPerView() = default; + + LandmarkPerView(const IndexT& landmarkId, const sfmData::Landmark& landmark, + const sfmData::Observation& observation) + : landmarkId(landmarkId) + , landmark(landmark) + , observation(observation) + { + } + + const uint& landmarkId; + const sfmData::Landmark& landmark; + const sfmData::Observation& observation; +}; + +// helper functions + +QGeometryRenderer* createGeometryRenderer(const QByteArray& positionData, const std::string& indexAttributeName); +QMaterial* createMaterial(const float& r, const float& g, const float& b, const float& a); + +ObservationsEntity::ObservationsEntity(std::string source, Qt3DCore::QNode* parent) + : BaseAlembicObject(parent) + , _source(source) +{ + using sfmDataIO::ESfMData; + + // read relevant SfM data + sfmDataIO::Load(_sfmData, _source, ESfMData( + ESfMData::VIEWS | ESfMData::EXTRINSICS | ESfMData::STRUCTURE | + ESfMData::OBSERVATIONS | ESfMData::OBSERVATIONS_WITH_FEATURES)); + + // populate _landmarksPerView from the read SfM data + fillLandmarksPerViews(); +} + +void ObservationsEntity::setData(const Alembic::Abc::IObject& iObj) +{ + // contains the 3D coordinates of vertices in space in the following order: + // - the landmarks + // - the view cameras + QByteArray positionData; + + fillBytesData(iObj, positionData); + + // contains the connections between the selected view and its corresponding + // observed landmarks + auto* rcGeometry = createGeometryRenderer(positionData, RC_INDEX_ATTRIBUTE_NAME); + // contains the connections between the observed landmarks of the current view + // and their corresponding observing view cameras + auto* tcGeometry = createGeometryRenderer(positionData, TC_INDEX_ATTRIBUTE_NAME); + + // create separate QEntity objects to have different material colors + + auto* tcEntity = new QEntity(this); + tcEntity->addComponent(tcGeometry); + tcEntity->addComponent(createMaterial(0, 1, 0, .7f)); + + auto* rcEntity = new QEntity(this); + rcEntity->addComponent(rcGeometry); + rcEntity->addComponent(createMaterial(0, 0, 1, .7f)); +} + +void ObservationsEntity::update(const IndexT& viewId, const QVariantMap& viewer2DInfo) +{ + QByteArray tcIndexData; + QByteArray rcIndexData; + size_t tcIndexSize = 0; + size_t rcIndexCount = 0; + + const auto& it = _landmarksPerView.find(viewId); + if (it != _landmarksPerView.end()) + { + // 2D view bounds + const float& scale = viewer2DInfo["scale"].toFloat(); + const float& x1 = -viewer2DInfo["x"].toFloat() / scale; + const float& y1 = -viewer2DInfo["y"].toFloat() / scale; + const float& x2 = viewer2DInfo["width"].toFloat() / scale + x1; + const float& y2 = viewer2DInfo["height"].toFloat() / scale + y1; + + const auto& totalNbObservations = it->second.first; + const auto& totalNbLandmarks = it->second.second.size(); + + tcIndexData.resize(static_cast(totalNbObservations * sizeof(uint) * 2)); + rcIndexData.resize(static_cast(totalNbLandmarks * sizeof(uint) * 2)); + char* tcIndexIt = tcIndexData.data(); + uint* rcIndexIt = reinterpret_cast(rcIndexData.data()); + + const auto& rcVertexPos = _viewId2vertexPos.at(viewId); + for (auto& landmarkPerView : it->second.second) + { + const auto& coords2D = landmarkPerView.observation.x; + // if not observed in viewer 2D + if (coords2D.x() < x1 || coords2D.x() > x2 || coords2D.y() < y1 || coords2D.y() > y2) + continue; + + *rcIndexIt++ = rcVertexPos; + *rcIndexIt++ = landmarkPerView.landmarkId; + rcIndexCount += 2; + + auto itt = _indexBytesByLandmark.data(); + std::advance(itt, _landmarkId2IndexRange[landmarkPerView.landmarkId].first * sizeof(uint)); + const auto& d = _landmarkId2IndexRange[landmarkPerView.landmarkId].second * sizeof(uint); + memcpy(tcIndexIt, itt, d); + std::advance(tcIndexIt, d); + tcIndexSize += d; + } + tcIndexData.resize(static_cast(tcIndexSize)); + rcIndexData.resize(static_cast(rcIndexCount * sizeof(uint))); + } + + this->findChild(TC_INDEX_ATTRIBUTE_NAME)->buffer()->setData(tcIndexData); + this->findChild(TC_INDEX_ATTRIBUTE_NAME) + ->setCount(static_cast(tcIndexSize / sizeof(uint))); + + this->findChild(RC_INDEX_ATTRIBUTE_NAME)->buffer()->setData(rcIndexData); + this->findChild(RC_INDEX_ATTRIBUTE_NAME)->setCount(static_cast(rcIndexCount)); +} + +void ObservationsEntity::fillLandmarksPerViews() +{ + for (const auto& landIt : _sfmData.getLandmarks()) + { + for (const auto& obsIt : landIt.second.observations) + { + IndexT viewId = obsIt.first; + auto& [totalNbObservations, landmarksSet] = _landmarksPerView[viewId]; + totalNbObservations += landIt.second.observations.size(); + landmarksSet.push_back(LandmarkPerView(landIt.first, landIt.second, obsIt.second)); + } + } +} + +void ObservationsEntity::fillBytesData(const Alembic::Abc::IObject& iObj, QByteArray& positionData) +{ + using namespace Alembic::Abc; + using namespace Alembic::AbcGeom; + using sfmDataIO::AV_UInt32ArraySamplePtr; + + IPoints points(iObj, kWrapExisting); + IPointsSchema schema = points.getSchema(); + + // -------------- read position data ---------------------- + + P3fArraySamplePtr positionsLandmarks = schema.getValue().getPositions(); + const auto& nLandmarks = positionsLandmarks->size(); + const auto& nViews = _sfmData.getViews().size(); + positionData.resize(static_cast((nLandmarks + nViews) * 3 * sizeof(float))); + // copy positions of landmarks + memcpy(positionData.data(), (const char*)positionsLandmarks->get(), nLandmarks * 3 * sizeof(float)); + // copy positions of view cameras and construct _viewId2vertexPos + { + auto* positionsIt = positionData.data(); + uint viewPosIdx = static_cast(nLandmarks); + // view camera positions are added after landmarks' + positionsIt += 3 * sizeof(float) * nLandmarks; + for (const auto& viewIt : _sfmData.getViews()) + { + _viewId2vertexPos[viewIt.first] = viewPosIdx++; + aliceVision::Vec3f center = _sfmData.getPose(*viewIt.second).getTransform().center().cast(); + // graphics to open-gl coordinates system + center.z() *= -1; + center.y() *= -1; + memcpy(positionsIt, reinterpret_cast(center.data()), 3 * sizeof(float)); + positionsIt += 3 * sizeof(float); + } + } + + // -------------- read Tc index data ---------------------- + + { + ICompoundProperty userProps = aliceVision::sfmDataIO::getAbcUserProperties(schema); + AV_UInt32ArraySamplePtr sampleVisibilitySize(userProps, "mvg_visibilitySize"); + AV_UInt32ArraySamplePtr sampleVisibilityViewId(userProps, "mvg_visibilityViewId"); + + _indexBytesByLandmark.resize(static_cast(2 * sizeof(uint) * sampleVisibilityViewId.size())); + _landmarkId2IndexRange.resize(sampleVisibilityViewId.size()); + uint* indices = reinterpret_cast(_indexBytesByLandmark.data()); + uint offset = 0; + size_t obsGlobalIndex = 0; + for (uint point3d_i = 0; point3d_i < sampleVisibilitySize.size(); ++point3d_i) + { + // Number of observation for this 3d point + const size_t visibilitySize = sampleVisibilitySize[point3d_i]; + const auto& delta = static_cast(visibilitySize * 2); + _landmarkId2IndexRange[point3d_i] = std::pair(offset, delta); + offset += delta; + for (size_t obs_i = 0; obs_i < visibilitySize; ++obs_i, ++obsGlobalIndex) + { + *indices++ = point3d_i; + *indices++ = _viewId2vertexPos.at(static_cast(sampleVisibilityViewId[obsGlobalIndex])); + } + } + } +} + +QGeometryRenderer* createGeometryRenderer(const QByteArray& positionData, const std::string& indexAttributeName) +{ + // create a new geometry renderer + auto customMeshRenderer = new QGeometryRenderer; + // the corresponding geometry consisting of: + // - a position attribute defining the 3D points + // - an index attribute defining the connectivity between points + auto customGeometry = new QGeometry; + + // mesh + customMeshRenderer->setGeometry(customGeometry); + customMeshRenderer->setPrimitiveType(QGeometryRenderer::Lines); + + // ------------- 3D points ------------------- + + // the vertices buffer + auto* vertexDataBuffer = new QBuffer; + vertexDataBuffer->setData(positionData); + // the position attribute + auto* positionAttribute = new QAttribute; + positionAttribute->setBuffer(vertexDataBuffer); + positionAttribute->setAttributeType(QAttribute::VertexAttribute); + positionAttribute->setVertexBaseType(QAttribute::Float); + positionAttribute->setVertexSize(3); // space dimension + positionAttribute->setByteOffset(0); + const auto& nbBytesPerVertex = 3 * sizeof(float); + positionAttribute->setByteStride(static_cast(nbBytesPerVertex)); + positionAttribute->setCount(static_cast(positionData.size() / nbBytesPerVertex)); + positionAttribute->setName(QAttribute::defaultPositionAttributeName()); + customGeometry->addAttribute(positionAttribute); // add to the geometry + + // ------------- connectivity between 3D points ------------------- + + // byte array containing pairs of indices to connected 3D points in poisition attribute + QByteArray indexBytes; + // the indices buffer + auto* indexBuffer = new QBuffer; + indexBuffer->setData(indexBytes); + // the index attribute + auto* indexAttribute = new QAttribute; + indexAttribute->setBuffer(indexBuffer); + indexAttribute->setAttributeType(QAttribute::IndexAttribute); + indexAttribute->setVertexBaseType(QAttribute::UnsignedInt); + indexAttribute->setCount(0); // index data not yet available + // set name for accessing the attribute and filling data later + indexAttribute->setObjectName(QString::fromStdString(indexAttributeName)); + customGeometry->addAttribute(indexAttribute); // add to the geometry + + return customMeshRenderer; +} + +QMaterial* createMaterial(const float& r, const float& g, const float& b, const float& a) +{ + // material for the connections between 3D points + auto* material = new Qt3DExtras::QPhongAlphaMaterial; + material->setAmbient(QColor::fromRgbF(r, g, b)); + material->setDiffuse(QColor::fromRgbF(r, g, b)); + material->setSpecular(QColor::fromRgbF(r, g, b)); + material->setShininess(0.0f); + material->setAlpha(a); + + return material; +} + +} // namespace diff --git a/src/qmlAlembic/ObservationsEntity.hpp b/src/qmlAlembic/ObservationsEntity.hpp new file mode 100644 index 00000000..bcdd9a7b --- /dev/null +++ b/src/qmlAlembic/ObservationsEntity.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "BaseAlembicObject.hpp" +#include + + +namespace abcentity +{ + +struct LandmarkPerView; + +class ObservationsEntity : public BaseAlembicObject +{ + Q_OBJECT + +public: + explicit ObservationsEntity(std::string source, Qt3DCore::QNode* parent = nullptr); + ~ObservationsEntity() override = default; + + void setData(const Alembic::Abc::IObject&); + void update(const aliceVision::IndexT& viewId, const QVariantMap& viewer2DInfo); + +private: + + void fillBytesData(const Alembic::Abc::IObject& iObj, QByteArray& positionData); + void fillLandmarksPerViews(); + + // file path to SfM data + std::string _source; + // contains the indices of vertices in position data used to draw lines + // between landmarks and their corresponding obsevations (ordered by landmark) + QByteArray _indexBytesByLandmark; + // each pair represents the offset position and count of indices (not bytes) + // in _indexBytesByLandmark for a landmark + std::vector> _landmarkId2IndexRange; + // maps view id to index of the corresponding position for a view camera in position data + std::map _viewId2vertexPos; + aliceVision::sfmData::SfMData _sfmData; + // maps view id to a pair containing: + // - the total number of observations of all landmarks observed by this view + // - a vector of LandmarkPerView structures corresponding to the landmarks observed by this view + stl::flat_map>> _landmarksPerView; +}; + +} // namespace diff --git a/src/qmlAlembic/plugin.hpp b/src/qmlAlembic/plugin.hpp index 291d96f9..071fc621 100644 --- a/src/qmlAlembic/plugin.hpp +++ b/src/qmlAlembic/plugin.hpp @@ -22,6 +22,8 @@ class AlembicEntityQmlPlugin : public QQmlExtensionPlugin "Cannot create CameraLocatorEntity instances from QML."); qmlRegisterUncreatableType(uri, 2, 0, "PointCloudEntity", "Cannot create PointCloudEntity instances from QML."); + qmlRegisterUncreatableType(uri, 2, 0, "ObservationsEntity", + "Cannot create ObservationsEntity instances from QML."); } }; From b3416b5a40b7be211469e753a5ea0c5d3e996885 Mon Sep 17 00:00:00 2001 From: Abdelrahman AL MAROUK Date: Wed, 30 Aug 2023 15:01:38 +0200 Subject: [PATCH 2/8] [ObservationsEntity] add option to activate or deactivate observations visualisation --- src/qmlAlembic/AlembicEntity.cpp | 11 +++++++++++ src/qmlAlembic/AlembicEntity.hpp | 5 +++++ src/qmlAlembic/ObservationsEntity.cpp | 4 ++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/qmlAlembic/AlembicEntity.cpp b/src/qmlAlembic/AlembicEntity.cpp index d3c13c51..7144096c 100644 --- a/src/qmlAlembic/AlembicEntity.cpp +++ b/src/qmlAlembic/AlembicEntity.cpp @@ -28,6 +28,7 @@ AlembicEntity::AlembicEntity(Qt3DCore::QNode* parent) // trigger display repaint events of observations connect(this, &AlembicEntity::viewIdChanged, this, &AlembicEntity::updateObservations); + connect(this, &AlembicEntity::displayObservationsChanged, this, &AlembicEntity::updateObservations); connect(this, &AlembicEntity::viewer2DInfoChanged, this, &AlembicEntity::updateObservations); } @@ -67,6 +68,14 @@ void AlembicEntity::setViewId(const int& value) Q_EMIT viewIdChanged(); } +void AlembicEntity::setDisplayObservations(const bool& value) +{ + if (_displayObservations == value) + return; + _displayObservations = value; + Q_EMIT displayObservationsChanged(); +} + void AlembicEntity::setViewer2DInfo(const QVariantMap& value) { if (_viewer2DInfo == value) @@ -89,6 +98,8 @@ void AlembicEntity::updateObservations() const for (auto* entity : _observations) { entity->update(_viewId, _viewer2DInfo); + if (entity->isEnabled() != _displayObservations) + entity->setEnabled(_displayObservations); } } diff --git a/src/qmlAlembic/AlembicEntity.hpp b/src/qmlAlembic/AlembicEntity.hpp index 23e12cf1..94b45804 100644 --- a/src/qmlAlembic/AlembicEntity.hpp +++ b/src/qmlAlembic/AlembicEntity.hpp @@ -25,6 +25,7 @@ class AlembicEntity : public Qt3DCore::QEntity Q_PROPERTY(float locatorScale READ locatorScale WRITE setLocatorScale NOTIFY locatorScaleChanged) Q_PROPERTY(int viewId READ viewId WRITE setViewId NOTIFY viewIdChanged) Q_PROPERTY(QVariantMap viewer2DInfo READ viewer2DInfo WRITE setViewer2DInfo NOTIFY viewer2DInfoChanged) + Q_PROPERTY(bool displayObservations READ displayObservations WRITE setDisplayObservations NOTIFY displayObservationsChanged) Q_PROPERTY(QQmlListProperty cameras READ cameras NOTIFY camerasChanged) Q_PROPERTY(QQmlListProperty pointClouds READ pointClouds NOTIFY pointCloudsChanged) Q_PROPERTY( @@ -49,11 +50,13 @@ class AlembicEntity : public Qt3DCore::QEntity Q_SLOT float pointSize() const { return _pointSize; } Q_SLOT float locatorScale() const { return _locatorScale; } Q_SLOT float viewId() const { return _viewId; } + Q_SLOT bool displayObservations() const { return _displayObservations; } Q_SLOT QVariantMap viewer2DInfo() const { return _viewer2DInfo; } Q_SLOT void setSource(const QUrl& source); Q_SLOT void setPointSize(const float& value); Q_SLOT void setLocatorScale(const float& value); Q_SLOT void setViewId(const int& value); + Q_SLOT void setDisplayObservations(const bool& value); Q_SLOT void setViewer2DInfo(const QVariantMap& value); Status status() const { return _status; } @@ -92,6 +95,7 @@ class AlembicEntity : public Qt3DCore::QEntity Q_SIGNAL void locatorScaleChanged(); Q_SIGNAL void viewIdChanged(); Q_SIGNAL void viewer2DInfoChanged(); + Q_SIGNAL void displayObservationsChanged(); Q_SIGNAL void objectPicked(Qt3DCore::QTransform* transform); Q_SIGNAL void statusChanged(Status status); Q_SIGNAL void skipHiddenChanged(); @@ -111,6 +115,7 @@ class AlembicEntity : public Qt3DCore::QEntity float _pointSize = 0.5f; float _locatorScale = 1.0f; int _viewId = 0; + bool _displayObservations = false; QVariantMap _viewer2DInfo; Qt3DRender::QParameter* _pointSizeParameter; Qt3DRender::QMaterial* _cloudMaterial; diff --git a/src/qmlAlembic/ObservationsEntity.cpp b/src/qmlAlembic/ObservationsEntity.cpp index 2db75c03..4c8ef0ec 100644 --- a/src/qmlAlembic/ObservationsEntity.cpp +++ b/src/qmlAlembic/ObservationsEntity.cpp @@ -75,11 +75,11 @@ void ObservationsEntity::setData(const Alembic::Abc::IObject& iObj) auto* tcEntity = new QEntity(this); tcEntity->addComponent(tcGeometry); - tcEntity->addComponent(createMaterial(0, 1, 0, .7f)); + tcEntity->addComponent(createMaterial(0, 1, 0, 1)); auto* rcEntity = new QEntity(this); rcEntity->addComponent(rcGeometry); - rcEntity->addComponent(createMaterial(0, 0, 1, .7f)); + rcEntity->addComponent(createMaterial(0, 0, 1, 1)); } void ObservationsEntity::update(const IndexT& viewId, const QVariantMap& viewer2DInfo) From 3bbc0fefcb0bbbf5aed1c650d18dc77a698b65cf Mon Sep 17 00:00:00 2001 From: almarouk <72821992+almarouk@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:08:37 +0200 Subject: [PATCH 3/8] [Observations] update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4d853fe..63096ed5 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Currently available: - [X] Alembic 3D visualization - Point clouds - Cameras + - Landmark observations - [X] OIIO backend - Read RAW images from DSLRs - Read intermediate files of the [AliceVision](https://github.com/alicevision/AliceVision) framework stored in EXR format From b52a4ee9273ea04392bd6794fa4060a1a81a0ca8 Mon Sep 17 00:00:00 2001 From: Abdelrahman AL MAROUK Date: Mon, 11 Sep 2023 12:20:32 +0200 Subject: [PATCH 4/8] [ObservationsEntity] use SfMData instead of Alembic also remove include .cpp --- src/qmlAlembic/AlembicEntity.cpp | 2 +- src/qmlAlembic/ObservationsEntity.cpp | 70 +++++++++++++-------------- src/qmlAlembic/ObservationsEntity.hpp | 4 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/qmlAlembic/AlembicEntity.cpp b/src/qmlAlembic/AlembicEntity.cpp index 7144096c..8b80c44b 100644 --- a/src/qmlAlembic/AlembicEntity.cpp +++ b/src/qmlAlembic/AlembicEntity.cpp @@ -241,7 +241,7 @@ void AlembicEntity::visitAbcObject(const Alembic::Abc::IObject& iObj, QEntity* p entity0->fillUserProperties(points.getSchema().getUserProperties()); entities[0] = entity0; ObservationsEntity* entity1 = new ObservationsEntity(_source.toLocalFile().toStdString(), parent); - entity1->setData(iObj); + entity1->setData(); entity1->fillArbProperties(points.getSchema().getArbGeomParams()); entity1->fillUserProperties(points.getSchema().getUserProperties()); entities[1] = entity1; diff --git a/src/qmlAlembic/ObservationsEntity.cpp b/src/qmlAlembic/ObservationsEntity.cpp index 4c8ef0ec..6c93e44e 100644 --- a/src/qmlAlembic/ObservationsEntity.cpp +++ b/src/qmlAlembic/ObservationsEntity.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include using namespace Qt3DRender; using namespace aliceVision; @@ -55,14 +55,14 @@ ObservationsEntity::ObservationsEntity(std::string source, Qt3DCore::QNode* pare fillLandmarksPerViews(); } -void ObservationsEntity::setData(const Alembic::Abc::IObject& iObj) +void ObservationsEntity::setData() { // contains the 3D coordinates of vertices in space in the following order: // - the landmarks // - the view cameras QByteArray positionData; - fillBytesData(iObj, positionData); + fillBytesData(positionData); // contains the connections between the selected view and its corresponding // observed landmarks @@ -152,31 +152,36 @@ void ObservationsEntity::fillLandmarksPerViews() } } -void ObservationsEntity::fillBytesData(const Alembic::Abc::IObject& iObj, QByteArray& positionData) +void ObservationsEntity::fillBytesData(QByteArray& positionData) { - using namespace Alembic::Abc; - using namespace Alembic::AbcGeom; - using sfmDataIO::AV_UInt32ArraySamplePtr; - - IPoints points(iObj, kWrapExisting); - IPointsSchema schema = points.getSchema(); - // -------------- read position data ---------------------- - P3fArraySamplePtr positionsLandmarks = schema.getValue().getPositions(); - const auto& nLandmarks = positionsLandmarks->size(); + const auto& nLandmarks = _sfmData.getLandmarks().size(); const auto& nViews = _sfmData.getViews().size(); positionData.resize(static_cast((nLandmarks + nViews) * 3 * sizeof(float))); + size_t nObservations = 0; // copy positions of landmarks - memcpy(positionData.data(), (const char*)positionsLandmarks->get(), nLandmarks * 3 * sizeof(float)); + { + auto* positionsIt = positionData.data(); + for (const auto& landIt : _sfmData.getLandmarks()) + { + aliceVision::Vec3f x = landIt.second.X.cast(); + // graphics to open-gl coordinates system + x.z() *= -1; + x.y() *= -1; + memcpy(positionsIt, reinterpret_cast(x.data()), 3 * sizeof(float)); + positionsIt += 3 * sizeof(float); + nObservations += landIt.second.observations.size(); + } + } // copy positions of view cameras and construct _viewId2vertexPos { - auto* positionsIt = positionData.data(); - uint viewPosIdx = static_cast(nLandmarks); - // view camera positions are added after landmarks' - positionsIt += 3 * sizeof(float) * nLandmarks; - for (const auto& viewIt : _sfmData.getViews()) - { + auto* positionsIt = positionData.data(); + uint viewPosIdx = static_cast(nLandmarks); + // view camera positions are added after landmarks' + positionsIt += 3 * sizeof(float) * nLandmarks; + for (const auto& viewIt : _sfmData.getViews()) + { _viewId2vertexPos[viewIt.first] = viewPosIdx++; aliceVision::Vec3f center = _sfmData.getPose(*viewIt.second).getTransform().center().cast(); // graphics to open-gl coordinates system @@ -184,32 +189,27 @@ void ObservationsEntity::fillBytesData(const Alembic::Abc::IObject& iObj, QByteA center.y() *= -1; memcpy(positionsIt, reinterpret_cast(center.data()), 3 * sizeof(float)); positionsIt += 3 * sizeof(float); - } + } } // -------------- read Tc index data ---------------------- { - ICompoundProperty userProps = aliceVision::sfmDataIO::getAbcUserProperties(schema); - AV_UInt32ArraySamplePtr sampleVisibilitySize(userProps, "mvg_visibilitySize"); - AV_UInt32ArraySamplePtr sampleVisibilityViewId(userProps, "mvg_visibilityViewId"); - - _indexBytesByLandmark.resize(static_cast(2 * sizeof(uint) * sampleVisibilityViewId.size())); - _landmarkId2IndexRange.resize(sampleVisibilityViewId.size()); + _indexBytesByLandmark.resize(static_cast(2 * sizeof(uint) * nObservations)); + _landmarkId2IndexRange.resize(nLandmarks); uint* indices = reinterpret_cast(_indexBytesByLandmark.data()); uint offset = 0; size_t obsGlobalIndex = 0; - for (uint point3d_i = 0; point3d_i < sampleVisibilitySize.size(); ++point3d_i) + for (const auto& landIt : _sfmData.getLandmarks()) { - // Number of observation for this 3d point - const size_t visibilitySize = sampleVisibilitySize[point3d_i]; - const auto& delta = static_cast(visibilitySize * 2); - _landmarkId2IndexRange[point3d_i] = std::pair(offset, delta); + const auto& delta = static_cast(landIt.second.observations.size() * 2); + _landmarkId2IndexRange[landIt.first] = std::pair(offset, delta); offset += delta; - for (size_t obs_i = 0; obs_i < visibilitySize; ++obs_i, ++obsGlobalIndex) + for (const auto& obsIt : landIt.second.observations) { - *indices++ = point3d_i; - *indices++ = _viewId2vertexPos.at(static_cast(sampleVisibilityViewId[obsGlobalIndex])); + *indices++ = landIt.first; + *indices++ = _viewId2vertexPos.at(obsIt.first); + ++obsGlobalIndex; } } } diff --git a/src/qmlAlembic/ObservationsEntity.hpp b/src/qmlAlembic/ObservationsEntity.hpp index bcdd9a7b..c2020574 100644 --- a/src/qmlAlembic/ObservationsEntity.hpp +++ b/src/qmlAlembic/ObservationsEntity.hpp @@ -17,12 +17,12 @@ class ObservationsEntity : public BaseAlembicObject explicit ObservationsEntity(std::string source, Qt3DCore::QNode* parent = nullptr); ~ObservationsEntity() override = default; - void setData(const Alembic::Abc::IObject&); + void setData(); void update(const aliceVision::IndexT& viewId, const QVariantMap& viewer2DInfo); private: - void fillBytesData(const Alembic::Abc::IObject& iObj, QByteArray& positionData); + void fillBytesData(QByteArray& positionData); void fillLandmarksPerViews(); // file path to SfM data From 2d80d4d40a3eaf59f23bc2a53c49a9ae291d0621 Mon Sep 17 00:00:00 2001 From: Abdelrahman AL MAROUK Date: Mon, 11 Sep 2023 12:34:25 +0200 Subject: [PATCH 5/8] [ObservationsEntity] fix type and casting --- src/qmlAlembic/AlembicEntity.cpp | 4 ++-- src/qmlAlembic/AlembicEntity.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qmlAlembic/AlembicEntity.cpp b/src/qmlAlembic/AlembicEntity.cpp index 8b80c44b..011bc08b 100644 --- a/src/qmlAlembic/AlembicEntity.cpp +++ b/src/qmlAlembic/AlembicEntity.cpp @@ -97,7 +97,7 @@ void AlembicEntity::updateObservations() const { for (auto* entity : _observations) { - entity->update(_viewId, _viewer2DInfo); + entity->update(static_cast(_viewId), _viewer2DInfo); if (entity->isEnabled() != _displayObservations) entity->setEnabled(_displayObservations); } @@ -289,7 +289,7 @@ void AlembicEntity::visitAbcObject(const Alembic::Abc::IObject& iObj, QEntity* p std::vector entities = createEntities(iObj); - for (int j = 0; j < entities.size(); j++) + for (size_t j = 0; j < entities.size(); j++) { auto entity = entities[j]; entity->setObjectName((iObj.getName() + std::to_string(j)).c_str()); diff --git a/src/qmlAlembic/AlembicEntity.hpp b/src/qmlAlembic/AlembicEntity.hpp index 94b45804..649a66dd 100644 --- a/src/qmlAlembic/AlembicEntity.hpp +++ b/src/qmlAlembic/AlembicEntity.hpp @@ -49,7 +49,7 @@ class AlembicEntity : public Qt3DCore::QEntity Q_SLOT const QUrl& source() const { return _source; } Q_SLOT float pointSize() const { return _pointSize; } Q_SLOT float locatorScale() const { return _locatorScale; } - Q_SLOT float viewId() const { return _viewId; } + Q_SLOT int viewId() const { return _viewId; } Q_SLOT bool displayObservations() const { return _displayObservations; } Q_SLOT QVariantMap viewer2DInfo() const { return _viewer2DInfo; } Q_SLOT void setSource(const QUrl& source); From a6e6cd7b7d14a42b8503488dd8fcded796624b20 Mon Sep 17 00:00:00 2001 From: Abdelrahman AL MAROUK Date: Mon, 11 Sep 2023 12:52:18 +0200 Subject: [PATCH 6/8] [ObservationsEntity] move struct definition to .hpp --- src/qmlAlembic/ObservationsEntity.cpp | 20 ------------------ src/qmlAlembic/ObservationsEntity.hpp | 29 ++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/qmlAlembic/ObservationsEntity.cpp b/src/qmlAlembic/ObservationsEntity.cpp index 6c93e44e..8106487a 100644 --- a/src/qmlAlembic/ObservationsEntity.cpp +++ b/src/qmlAlembic/ObservationsEntity.cpp @@ -15,26 +15,6 @@ namespace abcentity static const auto& TC_INDEX_ATTRIBUTE_NAME = "Tc-observations indices"; static const auto& RC_INDEX_ATTRIBUTE_NAME = "Rc-observations indices"; -/** - * @brief encapsulates sufficient and fast retrievable information on a landmark observed by a view - */ -struct LandmarkPerView -{ - LandmarkPerView() = default; - - LandmarkPerView(const IndexT& landmarkId, const sfmData::Landmark& landmark, - const sfmData::Observation& observation) - : landmarkId(landmarkId) - , landmark(landmark) - , observation(observation) - { - } - - const uint& landmarkId; - const sfmData::Landmark& landmark; - const sfmData::Observation& observation; -}; - // helper functions QGeometryRenderer* createGeometryRenderer(const QByteArray& positionData, const std::string& indexAttributeName); diff --git a/src/qmlAlembic/ObservationsEntity.hpp b/src/qmlAlembic/ObservationsEntity.hpp index c2020574..06bebad4 100644 --- a/src/qmlAlembic/ObservationsEntity.hpp +++ b/src/qmlAlembic/ObservationsEntity.hpp @@ -3,11 +3,30 @@ #include "BaseAlembicObject.hpp" #include +using namespace aliceVision; namespace abcentity { -struct LandmarkPerView; +/** + * @brief encapsulates sufficient and fast retrievable information on a landmark observed by a view + */ +struct LandmarkPerView +{ + LandmarkPerView() = default; + + LandmarkPerView(const IndexT& landmarkId, const sfmData::Landmark& landmark, + const sfmData::Observation& observation) + : landmarkId(landmarkId) + , landmark(landmark) + , observation(observation) + { + } + + const uint& landmarkId; + const sfmData::Landmark& landmark; + const sfmData::Observation& observation; +}; class ObservationsEntity : public BaseAlembicObject { @@ -18,7 +37,7 @@ class ObservationsEntity : public BaseAlembicObject ~ObservationsEntity() override = default; void setData(); - void update(const aliceVision::IndexT& viewId, const QVariantMap& viewer2DInfo); + void update(const IndexT& viewId, const QVariantMap& viewer2DInfo); private: @@ -34,12 +53,12 @@ class ObservationsEntity : public BaseAlembicObject // in _indexBytesByLandmark for a landmark std::vector> _landmarkId2IndexRange; // maps view id to index of the corresponding position for a view camera in position data - std::map _viewId2vertexPos; - aliceVision::sfmData::SfMData _sfmData; + std::map _viewId2vertexPos; + sfmData::SfMData _sfmData; // maps view id to a pair containing: // - the total number of observations of all landmarks observed by this view // - a vector of LandmarkPerView structures corresponding to the landmarks observed by this view - stl::flat_map>> _landmarksPerView; + stl::flat_map>> _landmarksPerView; }; } // namespace From e3be9a4bedea73ccaa3aa1bdd7b29818a140b3b3 Mon Sep 17 00:00:00 2001 From: Abdelrahman AL MAROUK Date: Mon, 11 Sep 2023 15:09:37 +0200 Subject: [PATCH 7/8] [ObservationsEntity] fix bug to handle non-constructed views --- src/qmlAlembic/ObservationsEntity.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/qmlAlembic/ObservationsEntity.cpp b/src/qmlAlembic/ObservationsEntity.cpp index 8106487a..6a9bfe48 100644 --- a/src/qmlAlembic/ObservationsEntity.cpp +++ b/src/qmlAlembic/ObservationsEntity.cpp @@ -27,9 +27,7 @@ ObservationsEntity::ObservationsEntity(std::string source, Qt3DCore::QNode* pare using sfmDataIO::ESfMData; // read relevant SfM data - sfmDataIO::Load(_sfmData, _source, ESfMData( - ESfMData::VIEWS | ESfMData::EXTRINSICS | ESfMData::STRUCTURE | - ESfMData::OBSERVATIONS | ESfMData::OBSERVATIONS_WITH_FEATURES)); + sfmDataIO::Load(_sfmData, _source, ESfMData::ALL); // populate _landmarksPerView from the read SfM data fillLandmarksPerViews(); @@ -70,7 +68,8 @@ void ObservationsEntity::update(const IndexT& viewId, const QVariantMap& viewer2 size_t rcIndexCount = 0; const auto& it = _landmarksPerView.find(viewId); - if (it != _landmarksPerView.end()) + const auto& setValidViewIds = _sfmData.getValidViews(); + if (it != _landmarksPerView.end() && setValidViewIds.find(viewId) != setValidViewIds.end()) { // 2D view bounds const float& scale = viewer2DInfo["scale"].toFloat(); @@ -137,7 +136,7 @@ void ObservationsEntity::fillBytesData(QByteArray& positionData) // -------------- read position data ---------------------- const auto& nLandmarks = _sfmData.getLandmarks().size(); - const auto& nViews = _sfmData.getViews().size(); + const auto& nViews = _sfmData.getValidViews().size(); positionData.resize(static_cast((nLandmarks + nViews) * 3 * sizeof(float))); size_t nObservations = 0; // copy positions of landmarks @@ -160,10 +159,10 @@ void ObservationsEntity::fillBytesData(QByteArray& positionData) uint viewPosIdx = static_cast(nLandmarks); // view camera positions are added after landmarks' positionsIt += 3 * sizeof(float) * nLandmarks; - for (const auto& viewIt : _sfmData.getViews()) + for (const auto& viewId : _sfmData.getValidViews()) { - _viewId2vertexPos[viewIt.first] = viewPosIdx++; - aliceVision::Vec3f center = _sfmData.getPose(*viewIt.second).getTransform().center().cast(); + _viewId2vertexPos[viewId] = viewPosIdx++; + aliceVision::Vec3f center = _sfmData.getPose(_sfmData.getView(viewId)).getTransform().center().cast(); // graphics to open-gl coordinates system center.z() *= -1; center.y() *= -1; @@ -188,7 +187,15 @@ void ObservationsEntity::fillBytesData(QByteArray& positionData) for (const auto& obsIt : landIt.second.observations) { *indices++ = landIt.first; - *indices++ = _viewId2vertexPos.at(obsIt.first); + const auto& pos = _viewId2vertexPos.find(obsIt.first); + if (pos != _viewId2vertexPos.end()) + { + *indices++ = pos->second; + } + else + { + *indices++ = landIt.first; + } ++obsGlobalIndex; } } From 7ba5dd1f550ca0d880cfbc1793781db7c39d5d1d Mon Sep 17 00:00:00 2001 From: Abdelrahman AL MAROUK Date: Mon, 11 Sep 2023 15:11:48 +0200 Subject: [PATCH 8/8] [ObservationsEntity] fix shadowded naming --- src/qmlAlembic/ObservationsEntity.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qmlAlembic/ObservationsEntity.hpp b/src/qmlAlembic/ObservationsEntity.hpp index 06bebad4..882bbf16 100644 --- a/src/qmlAlembic/ObservationsEntity.hpp +++ b/src/qmlAlembic/ObservationsEntity.hpp @@ -15,11 +15,11 @@ struct LandmarkPerView { LandmarkPerView() = default; - LandmarkPerView(const IndexT& landmarkId, const sfmData::Landmark& landmark, - const sfmData::Observation& observation) - : landmarkId(landmarkId) - , landmark(landmark) - , observation(observation) + LandmarkPerView(const IndexT& landmarkId_, const sfmData::Landmark& landmark_, + const sfmData::Observation& observation_) + : landmarkId(landmarkId_) + , landmark(landmark_) + , observation(observation_) { }