diff --git a/apps/Viewer/Scene.cpp b/apps/Viewer/Scene.cpp index 69683dc75..83bb21ad7 100644 --- a/apps/Viewer/Scene.cpp +++ b/apps/Viewer/Scene.cpp @@ -706,7 +706,7 @@ void Scene::TogleSceneBox() } -void Scene::CastRay(const Ray3& ray, int action) +void Scene::CastRay(const Ray3& ray, int action, int mods) { if (!IsOctreeValid()) return; @@ -747,6 +747,19 @@ void Scene::CastRay(const Ray3& ray, int action) face[1], window.selectionPoints[1].x, window.selectionPoints[1].y, window.selectionPoints[1].z, face[2], window.selectionPoints[2].x, window.selectionPoints[2].y, window.selectionPoints[2].z ); + if ((mods&(GLFW_MOD_CONTROL|GLFW_MOD_SHIFT)) && scene.mesh.HasTexture()) { + String dbgOut; + cv::Mat dbgImg; + scene.mesh.ListIncidenteFaces(); + scene.mesh.ListIncidenteFaceFaces(); + const bool bSaveToFile = (mods&GLFW_MOD_ALT); + dbgOut = scene.mesh.PlotTexturePatch((MVS::Mesh::FIndex)intRay.pick.idx, face_patch_ids, dbgImg, bSaveToFile); + if (!dbgOut.IsEmpty()) { + DEBUG_EXTRA("Saved texture patch as %s", dbgOut.c_str()); + } else { + cv::imshow(dbgOut, dbgImg); + } + } } else { window.selectionType = Window::SEL_NA; } diff --git a/apps/Viewer/Scene.h b/apps/Viewer/Scene.h index d1e360833..1e093e93d 100644 --- a/apps/Viewer/Scene.h +++ b/apps/Viewer/Scene.h @@ -60,6 +60,7 @@ class Scene Window window; ImageArr images; // scene photos ImageArr textures; // mesh textures + MVS::Mesh::FaceIdxArr face_patch_ids; OctreePoints octPoints; OctreeMesh octMesh; @@ -97,7 +98,7 @@ class Scene void Center(); void TogleSceneBox(); - void CastRay(const Ray3&, int); + void CastRay(const Ray3&, int, int); protected: static void* ThreadWorker(void*); }; diff --git a/apps/Viewer/Window.cpp b/apps/Viewer/Window.cpp index 63bbe1a19..390b7b2c6 100644 --- a/apps/Viewer/Window.cpp +++ b/apps/Viewer/Window.cpp @@ -366,7 +366,7 @@ void Window::Key(GLFWwindow* window, int k, int scancode, int action, int mod) g_mapWindows[window]->Key(k, scancode, action, mod); } -void Window::MouseButton(int button, int action, int /*mods*/) +void Window::MouseButton(int button, int action, int mods) { switch (button) { case GLFW_MOUSE_BUTTON_LEFT: { @@ -394,7 +394,7 @@ void Window::MouseButton(int button, int action, int /*mods*/) const Eigen::Vector3d start(invV.topRightCorner<3,1>()); const Eigen::Vector4d ray_wor(invV*ray_eye); const Eigen::Vector3d dir(ray_wor.topRows<3>().normalized()); - clbkRayScene(Ray3d(start, dir), action); + clbkRayScene(Ray3d(start, dir), action, mods); } } break; case GLFW_MOUSE_BUTTON_MIDDLE: { diff --git a/apps/Viewer/Window.h b/apps/Viewer/Window.h index 34195ae31..18adf6690 100644 --- a/apps/Viewer/Window.h +++ b/apps/Viewer/Window.h @@ -100,7 +100,7 @@ class Window ClbkExportScene clbkExportScene; typedef DELEGATE ClbkCenterScene; ClbkCenterScene clbkCenterScene; - typedef DELEGATE ClbkRayScene; + typedef DELEGATE ClbkRayScene; ClbkRayScene clbkRayScene; typedef DELEGATE ClbkCompilePointCloud; ClbkCompilePointCloud clbkCompilePointCloud; diff --git a/libs/IO/OBJ.cpp b/libs/IO/OBJ.cpp index 0c1f5309b..df4789f70 100644 --- a/libs/IO/OBJ.cpp +++ b/libs/IO/OBJ.cpp @@ -191,6 +191,7 @@ bool ObjModel::Save(const String& fileName, unsigned precision, bool texLossless bool ObjModel::Load(const String& fileName) { ASSERT(vertices.empty() && groups.empty() && material_lib.materials.empty()); + const String path(Util::getFilePath(fileName)); std::ifstream fin(fileName.c_str()); String line, keyword; std::istringstream in; @@ -254,7 +255,7 @@ bool ObjModel::Load(const String& fileName) } else if (keyword == "mtllib") { in >> keyword; - if (!material_lib.Load(keyword)) + if (!material_lib.Load(MAKE_PATH_FULL(path, keyword))) return false; } else if (keyword == "usemtl") { diff --git a/libs/MVS/Mesh.cpp b/libs/MVS/Mesh.cpp index 76678846b..429fac49d 100644 --- a/libs/MVS/Mesh.cpp +++ b/libs/MVS/Mesh.cpp @@ -398,6 +398,54 @@ void Mesh::ComputeNormalVertices() } #endif +uint32_t Mesh::ComputeTexturePatchFaces(FaceIdxArr& face_patch_ids) const { + ASSERT(faceTexcoords.size() == faces.size() * 3); + face_patch_ids.Resize(faces.GetSize()); + face_patch_ids.MemsetValue(NO_ID); + std::stack stack_faces; + uint32_t patch_id = 0; + FOREACH(idx_face, faces) { + if (face_patch_ids[idx_face] != NO_ID) + continue; + face_patch_ids[idx_face] = patch_id; + stack_faces.push(idx_face); + do { + FIndex iF = stack_faces.top(); + stack_faces.pop(); + const Mesh::FaceFaces& ffaces = faceFaces[iF]; + for (int i = 0; i < 3; ++i) { + const FIndex iFAdj = ffaces[i]; + if (iFAdj == NO_ID || face_patch_ids[iFAdj] != NO_ID) + continue; + VIndex iV0, iV1; //indexes of a common vertex + for (int v0 = 0; v0 < 3; ++v0) { + const Face& f = faces[iF]; + const Face& fAdj = faces[iFAdj]; + bool bCommon = false; + for (int v1 = 0; v1 < 3; ++v1) { + if (f[v0] == fAdj[v1]) { + iV0 = v0; + iV1 = v1; + bCommon = true; + break; + } + } + if (bCommon) + break; + } + const TexCoord tcV = faceTexcoords[iF * 3 + iV0]; + const TexCoord tcAdj = faceTexcoords[iFAdj * 3 + iV1]; + if (tcV.x == tcAdj.x && tcV.y == tcAdj.y) { + face_patch_ids[iFAdj] = patch_id; + stack_faces.push(iFAdj); + } + } + } while (!stack_faces.empty()); + ++patch_id; + } + return patch_id; +} // ComputeTexturePatchFaces + // Smoothen the normals for each face // - fMaxGradient: maximum angle (in degrees) difference between neighbor normals that is // allowed to take into consideration; higher angles are ignored @@ -4086,6 +4134,46 @@ REAL Mesh::ComputeVolume() const volume += ComputeTriangleVolume(vertices[face[0]], vertices[face[1]], vertices[face[2]]); return volume; } + +String Mesh::PlotTexturePatch(const FIndex dbgFaceId, FaceIdxArr& face_patch_ids, cv::Mat& imgOut, const bool bSaveToFile) const { + // split texture in patches + if (face_patch_ids.size() != faces.size()) { + ComputeTexturePatchFaces(face_patch_ids); + } + // get the list of all faces from the same patch as debug face + cList patchFaces; + const uint32_t dbgPatchId(face_patch_ids[dbgFaceId]); + FOREACH(idx_face, face_patch_ids) { + if (dbgPatchId == face_patch_ids[idx_face]) + patchFaces.emplace_back(idx_face); + } + const Mesh::Face fDbg = faces[dbgFaceId]; + cv::Mat imgDbg; + textureDiffuse.copyTo(imgDbg); + AABB2f boundingBox(true); + FOREACH(fIdx, patchFaces) { + const Mesh::FIndex& fId = patchFaces[fIdx]; + const Mesh::TexCoord& texDbgCoordsV0 = faceTexcoords[fId * 3]; boundingBox.Insert(texDbgCoordsV0); + const Mesh::TexCoord& texDbgCoordsV1 = faceTexcoords[fId * 3 + 1]; boundingBox.Insert(texDbgCoordsV1); + const Mesh::TexCoord& texDbgCoordsV2 = faceTexcoords[fId * 3 + 2]; boundingBox.Insert(texDbgCoordsV2); + cv::line(imgDbg, texDbgCoordsV0, texDbgCoordsV1, cv::Scalar(50, 50, 250)); + cv::line(imgDbg, texDbgCoordsV0, texDbgCoordsV2, cv::Scalar(50, 50, 250)); + cv::line(imgDbg, texDbgCoordsV2, texDbgCoordsV1, cv::Scalar(50, 50, 250)); + if (fId == dbgFaceId) { + cv::circle(imgDbg, texDbgCoordsV0, 3, cv::Scalar(100, 150, 250), 2); + cv::circle(imgDbg, texDbgCoordsV1, 3, cv::Scalar(100, 150, 250), 2); + cv::circle(imgDbg, texDbgCoordsV2, 3, cv::Scalar(100, 150, 250), 2); + } + } + cv::Rect sourceRect(FLOOR(boundingBox.ptMin.x()), FLOOR(boundingBox.ptMin.y()), CEIL(boundingBox.ptMax.x() - boundingBox.ptMin.x()), CEIL(boundingBox.ptMax.y() - boundingBox.ptMin.y())); // ROI of the patch + imgDbg(sourceRect).copyTo(imgOut); + if (bSaveToFile) { + String sImgName = String::FormatString("dbgPatch_%d_%d.jpg", dbgPatchId, dbgFaceId); + cv::imwrite(MAKE_PATH_SAFE(sImgName), imgOut); + return sImgName; + } + return String(); +} /*----------------------------------------------------------------*/ diff --git a/libs/MVS/Mesh.h b/libs/MVS/Mesh.h index f91028407..2cf058386 100644 --- a/libs/MVS/Mesh.h +++ b/libs/MVS/Mesh.h @@ -161,6 +161,7 @@ class MVS_API Mesh void ListBoundaryVertices(); void ComputeNormalFaces(); void ComputeNormalVertices(); + uint32_t ComputeTexturePatchFaces(FaceIdxArr& face_patch_ids) const; void SmoothNormalFaces(float fMaxGradient=25.f, float fOriginalWeight=0.5f, unsigned nIterations=3); @@ -209,6 +210,8 @@ class MVS_API Mesh REAL ComputeArea() const; REAL ComputeVolume() const; + String PlotTexturePatch(const FIndex dbgFaceId, FaceIdxArr& face_patch_ids=FaceIdxArr(), cv::Mat& imgOut=cv::Mat(), const bool bSaveToFile=true) const; + void SamplePoints(unsigned numberOfPoints, PointCloud&) const; void SamplePoints(REAL samplingDensity, PointCloud&) const; void SamplePoints(REAL samplingDensity, unsigned mumPointsTheoretic, PointCloud&) const;