From 5936a1dab6988ff4c1982f1c49963c48b2a1be40 Mon Sep 17 00:00:00 2001 From: Jan Herling Date: Wed, 6 Nov 2024 09:45:27 -0800 Subject: [PATCH] Added handling of anchor events in AKSceneTracker6DOF Summary: Now, using the anchor event function to decide whether a mesh anchor has changed or not. Before, we used the number of triangles of a mesh to make this decision. Reviewed By: enpe Differential Revision: D65504172 Privacy Context Container: L1191897 fbshipit-source-id: fac76e642ce51257632086a75499480a8ac4581d --- impl/ocean/devices/arkit/AKSceneTracker6DOF.h | 41 ++++- .../ocean/devices/arkit/AKSceneTracker6DOF.mm | 157 +++++++++--------- 2 files changed, 116 insertions(+), 82 deletions(-) diff --git a/impl/ocean/devices/arkit/AKSceneTracker6DOF.h b/impl/ocean/devices/arkit/AKSceneTracker6DOF.h index 6755676d2..c94f71e71 100644 --- a/impl/ocean/devices/arkit/AKSceneTracker6DOF.h +++ b/impl/ocean/devices/arkit/AKSceneTracker6DOF.h @@ -11,6 +11,8 @@ #include "ocean/devices/arkit/ARKit.h" #include "ocean/devices/arkit/AKDevice.h" +#include "ocean/base/StringApple.h" + #include "ocean/devices/SceneTracker6DOF.h" #include "ocean/devices/VisualTracker.h" @@ -41,6 +43,24 @@ class OCEAN_DEVICES_ARKIT_EXPORT AKSceneTracker6DOF : */ using IdentifierMap = std::unordered_map; + /** + * Helper class implementing a hash function for ARMeshAnchor. + */ + struct ARMeshAnchorHash + { + /** + * Hash function returning a hash value for an ARMeshAnchor object + * @param anchor The anchor for which the hash will be returned + * @return The resulting hash value + */ + inline size_t operator()(const ARMeshAnchor* anchor) const; + }; + + /** + * Definition of an unordered set holding ARMeshAnchor objects. + */ + using ARMeshAnchorSet = std::unordered_set; + public: /** @@ -92,6 +112,18 @@ class OCEAN_DEVICES_ARKIT_EXPORT AKSceneTracker6DOF : */ void onNewSample(const HomogenousMatrix4& world_T_camera, SharedSceneElements&& sceneElements, const Timestamp& timestamp, Metadata&& metadata); + /** + * Event function for added anchors. + * @see AKDevice::onAddedAnchors(). + */ + void onAddedAnchors(const ARAnchors& anchors) override; + + /** + * Event function for updated anchors. + * @see AKDevice::onUpdateAnchors(). + */ + void onUpdateAnchors(const ARAnchors& anchors) override; + /** * Returns the name of this tracker. * @return The trackers's name @@ -151,10 +183,15 @@ class OCEAN_DEVICES_ARKIT_EXPORT AKSceneTracker6DOF : /// The counter for unique mesh ids. unsigned int meshIdCounter_ = 0u; - /// The number of triangles each mesh currently has. - std::vector numberTriangles_; + /// The set holding all updated ARMeshAnchor objects. + ARMeshAnchorSet updatedMeshAnchors_; }; +inline size_t AKSceneTracker6DOF::ARMeshAnchorHash::operator()(const ARMeshAnchor* anchor) const +{ + return std::hash()(StringApple::toUTF8(anchor.identifier.UUIDString)); +} + inline std::string AKSceneTracker6DOF::deviceNameAKSceneTracker6DOF() { return std::string("ARKit 6DOF Scene Tracker"); diff --git a/impl/ocean/devices/arkit/AKSceneTracker6DOF.mm b/impl/ocean/devices/arkit/AKSceneTracker6DOF.mm index c3d0ca8c4..b55297eb4 100644 --- a/impl/ocean/devices/arkit/AKSceneTracker6DOF.mm +++ b/impl/ocean/devices/arkit/AKSceneTracker6DOF.mm @@ -199,8 +199,6 @@ // first, we check whether any mesh as changed, unfortuately ARKit does not provide some kind of mesh version, so that we use the number of triangles to decided whether the mesh has been updated - bool meshHasChanged = false; - for (ARAnchor* anchor in arFrame.anchors) { if (![anchor isKindOfClass:[ARMeshAnchor class]]) @@ -210,106 +208,73 @@ ARMeshAnchor* meshAnchor = (ARMeshAnchor*)(anchor); + if (updatedMeshAnchors_.find(meshAnchor) == updatedMeshAnchors_.cend()) + { + continue; + } + const std::string meshIdentifier = StringApple::toUTF8(meshAnchor.identifier.UUIDString); - const IdentifierMap::const_iterator iMesh = identifierMap_.find(meshIdentifier); + IdentifierMap::iterator iMesh = identifierMap_.find(meshIdentifier); if (iMesh == identifierMap_.cend()) { - meshHasChanged = true; - break; + iMesh = identifierMap_.insert(std::make_pair(meshIdentifier, ++meshIdCounter_)).first; } - else - { - ARMeshGeometry* meshGeometry = meshAnchor.geometry; + const Index32 meshId = iMesh->second; - ocean_assert(iMesh->second < numberTriangles_.size()); - if (meshGeometry.faces.count != numberTriangles_[iMesh->second]) - { - meshHasChanged = true; - break; - } - } - } + const simd_float4x4 simdTransform = meshAnchor.transform; + HomogenousMatrixF4 world_T_mesh; - if (meshHasChanged) - { - for (ARAnchor* anchor in arFrame.anchors) - { - if (![anchor isKindOfClass:[ARMeshAnchor class]]) - { - continue; - } + memcpy(world_T_mesh.data() + 0, &simdTransform.columns[0], sizeof(float) * 4); + memcpy(world_T_mesh.data() + 4, &simdTransform.columns[1], sizeof(float) * 4); + memcpy(world_T_mesh.data() + 8, &simdTransform.columns[2], sizeof(float) * 4); + memcpy(world_T_mesh.data() + 12, &simdTransform.columns[3], sizeof(float) * 4); - ARMeshAnchor* meshAnchor = (ARMeshAnchor*)(anchor); + ARMeshGeometry* meshGeometry = meshAnchor.geometry; - const std::string meshIdentifier = StringApple::toUTF8(meshAnchor.identifier.UUIDString); + Vectors3 vertices; + Vectors3 perVertexNormals; + Indices32 triangleIndices; - IdentifierMap::iterator iMesh = identifierMap_.find(meshIdentifier); - if (iMesh == identifierMap_.cend()) + if (extractVectors3(meshGeometry.vertices, vertices) && extractVectors3(meshGeometry.normals, perVertexNormals) && extractIndices(meshGeometry.faces, triangleIndices)) + { +#ifdef OCEAN_DEBUG + ocean_assert(vertices.size() == perVertexNormals.size()); + for (const Index32& index : triangleIndices) { - iMesh = identifierMap_.insert(std::make_pair(meshIdentifier, ++meshIdCounter_)).first; - - numberTriangles_.resize(meshIdCounter_ + 1, 0); + ocean_assert(index < vertices.size()); } - const Index32 meshId = iMesh->second; - - const simd_float4x4 simdTransform = meshAnchor.transform; - HomogenousMatrixF4 world_T_mesh; - - memcpy(world_T_mesh.data() + 0, &simdTransform.columns[0], sizeof(float) * 4); - memcpy(world_T_mesh.data() + 4, &simdTransform.columns[1], sizeof(float) * 4); - memcpy(world_T_mesh.data() + 8, &simdTransform.columns[2], sizeof(float) * 4); - memcpy(world_T_mesh.data() + 12, &simdTransform.columns[3], sizeof(float) * 4); +#endif - ARMeshGeometry* meshGeometry = meshAnchor.geometry; + ocean_assert(triangleIndices.size() % 3 == 0); + if (triangleIndices.empty() || triangleIndices.size() % 3 != 0) + { + continue; + } - Vectors3 vertices; - Vectors3 perVertexNormals; - Indices32 triangleIndices; + // ARKit sometimes provides invalid per-vertex normals, we remove these triangles - if (extractVectors3(meshGeometry.vertices, vertices) && extractVectors3(meshGeometry.normals, perVertexNormals) && extractIndices(meshGeometry.faces, triangleIndices)) + for (size_t n = 0; n < triangleIndices.size(); /*noop*/) { -#ifdef OCEAN_DEBUG - ocean_assert(vertices.size() == perVertexNormals.size()); - for (const Index32& index : triangleIndices) + if (Numeric::isNan(perVertexNormals[triangleIndices[n + 0]].x()) + || Numeric::isNan(perVertexNormals[triangleIndices[n + 1]].x()) + || Numeric::isNan(perVertexNormals[triangleIndices[n + 2]].x())) { - ocean_assert(index < vertices.size()); - } -#endif + triangleIndices[n + 0] = triangleIndices[triangleIndices.size() - 3]; + triangleIndices[n + 1] = triangleIndices[triangleIndices.size() - 2]; + triangleIndices[n + 2] = triangleIndices[triangleIndices.size() - 1]; - ocean_assert(triangleIndices.size() % 3 == 0); - if (triangleIndices.empty() || triangleIndices.size() % 3 != 0) - { - continue; + triangleIndices.resize(triangleIndices.size() - 3); } - - // ARKit sometimes provides invalid per-vertex normals, we remove these triangles - - for (size_t n = 0; n < triangleIndices.size(); /*noop*/) + else { - if (Numeric::isNan(perVertexNormals[triangleIndices[n + 0]].x()) - || Numeric::isNan(perVertexNormals[triangleIndices[n + 1]].x()) - || Numeric::isNan(perVertexNormals[triangleIndices[n + 2]].x())) - { - triangleIndices[n + 0] = triangleIndices[triangleIndices.size() - 3]; - triangleIndices[n + 1] = triangleIndices[triangleIndices.size() - 2]; - triangleIndices[n + 2] = triangleIndices[triangleIndices.size() - 1]; - - triangleIndices.resize(triangleIndices.size() - 3); - } - else - { - n += 3; - } + n += 3; } + } - if (!triangleIndices.empty()) - { - ocean_assert(meshId < numberTriangles_.size()); - numberTriangles_[meshId] = triangleIndices.size() / 3; - - meshes.emplace_back(std::make_shared(meshId, HomogenousMatrix4(world_T_mesh), std::move(vertices), std::move(perVertexNormals), std::move(triangleIndices))); - } + if (!triangleIndices.empty()) + { + meshes.emplace_back(std::make_shared(meshId, HomogenousMatrix4(world_T_mesh), std::move(vertices), std::move(perVertexNormals), std::move(triangleIndices))); } } } @@ -320,6 +285,8 @@ } } + updatedMeshAnchors_.clear(); + if (sceneElements.empty()) { sceneElements.emplace_back(nullptr); // adding a pure 6-DOF pose scene element @@ -379,6 +346,36 @@ } } +void AKSceneTracker6DOF::onAddedAnchors(const ARAnchors& anchors) +{ + for (ARAnchor* anchor : anchors) + { + if (![anchor isKindOfClass:[ARMeshAnchor class]]) + { + continue; + } + + ARMeshAnchor* meshAnchor = (ARMeshAnchor*)(anchor); + + updatedMeshAnchors_.insert(meshAnchor); + } +} + +void AKSceneTracker6DOF::onUpdateAnchors(const ARAnchors& anchors) +{ + for (ARAnchor* anchor : anchors) + { + if (![anchor isKindOfClass:[ARMeshAnchor class]]) + { + continue; + } + + ARMeshAnchor* meshAnchor = (ARMeshAnchor*)(anchor); + + updatedMeshAnchors_.insert(meshAnchor); + } +} + bool AKSceneTracker6DOF::extractVectors3(ARGeometrySource* geometrySource, Vectors3& vectors) { ocean_assert(geometrySource != nullptr);