|
|
|
|
@ -606,7 +606,7 @@ public:
@@ -606,7 +606,7 @@ public:
|
|
|
|
|
bool FaceViewSelectionWithVirtualFaces(unsigned minCommonCameras, float fOutlierThreshold, |
|
|
|
|
float fRatioDataSmoothness, int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views, bool bUseExistingUV); |
|
|
|
|
|
|
|
|
|
bool ComputePureFaceVisibility(float fOutlierThreshold, int nIgnoreMaskLabel, const IIndexArr& views); |
|
|
|
|
bool GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, |
|
|
|
|
unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, |
|
|
|
|
Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, |
|
|
|
|
@ -6948,7 +6948,7 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
@@ -6948,7 +6948,7 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
|
|
|
|
|
// printf("FaceViewSelection3 2 scene.mesh.vertices.size=%d\n", scene.mesh.vertices.size());
|
|
|
|
|
|
|
|
|
|
bool bUseVirtualFaces(minCommonCameras > 0); |
|
|
|
|
bUseVirtualFaces = false; |
|
|
|
|
// bUseVirtualFaces = false;
|
|
|
|
|
|
|
|
|
|
// list all views for each face
|
|
|
|
|
FaceDataViewArr facesDatas; |
|
|
|
|
@ -12784,7 +12784,7 @@ float MeshTexture::ComputeFaceDistance(FIndex fid1, FIndex fid2)
@@ -12784,7 +12784,7 @@ float MeshTexture::ComputeFaceDistance(FIndex fid1, FIndex fid2)
|
|
|
|
|
Point3f diff = center2 - center1; |
|
|
|
|
return std::sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z); |
|
|
|
|
} |
|
|
|
|
// 判断面是否在视图中可见
|
|
|
|
|
|
|
|
|
|
// 判断面是否在视图中可见
|
|
|
|
|
bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID) |
|
|
|
|
{ |
|
|
|
|
@ -14073,6 +14073,14 @@ bool MeshTexture::RasterizeVirtualFaces(
@@ -14073,6 +14073,14 @@ bool MeshTexture::RasterizeVirtualFaces(
|
|
|
|
|
DEBUG_EXTRA("Forward Rasterization Engine: Starting..."); |
|
|
|
|
TD_TIMER_START(); |
|
|
|
|
|
|
|
|
|
int totalVF = virtualFaceMap.size(); |
|
|
|
|
int emptyVF = 0; |
|
|
|
|
for (auto& vf : virtualFaceViews) |
|
|
|
|
if (vf.empty()) ++emptyVF; |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("VirtualFaces: total=%d empty=%d (%.1f%%)", |
|
|
|
|
totalVF, emptyVF, 100.0f*emptyVF/totalVF); |
|
|
|
|
|
|
|
|
|
if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
@ -14318,6 +14326,12 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int
@@ -14318,6 +14326,12 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int
|
|
|
|
|
DEBUG_EXTRA("Texture Pipeline: Steps 3-4 (View Selection + VirtualFace Mapping)"); |
|
|
|
|
TD_TIMER_START(); |
|
|
|
|
|
|
|
|
|
// 不再重新选择视图!直接使用已有的 faceViews
|
|
|
|
|
if (faceViews.empty()) { |
|
|
|
|
DEBUG_EXTRA("ERROR: faceViews is empty! Call FaceViewSelection first."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 1. 验证输入
|
|
|
|
|
if (scene.mesh.faceTexcoords.empty()) { |
|
|
|
|
VERBOSE("error: mesh does not contain UV coordinates"); |
|
|
|
|
@ -14348,74 +14362,8 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int
@@ -14348,74 +14362,8 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int
|
|
|
|
|
DEBUG_EXTRA("Step 4: Forced 1-to-1 mapping: %zu virtual faces for %zu original faces", |
|
|
|
|
virtualFaceMap.size(), numFaces); |
|
|
|
|
|
|
|
|
|
// 4.2 为每个虚拟面收集视图数据(用于可见性判断)
|
|
|
|
|
VirtualFaceDataArr virtualFaceDatas; |
|
|
|
|
if (!ListCameraVirtualFaces(virtualFaceMap, virtualFaceDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 4.3 单视图选择:为每个虚拟面选择最佳视图
|
|
|
|
|
std::vector<std::vector<IIndex>> virtualFaceViews(virtualFaceMap.size()); |
|
|
|
|
std::vector<std::vector<float>> virtualFaceViewWeights(virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
size_t unassignedFaces = 0; |
|
|
|
|
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
#pragma omp parallel for reduction(+:unassignedFaces) schedule(dynamic) |
|
|
|
|
#endif |
|
|
|
|
for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { |
|
|
|
|
const FIndex fid = virtualFaceMap[idxVF].faces[0]; |
|
|
|
|
const FaceDataArr& vfDatas = virtualFaceDatas[idxVF]; |
|
|
|
|
|
|
|
|
|
float bestScore = -1.0f; |
|
|
|
|
IIndex bestViewID = IIndex(-1); |
|
|
|
|
|
|
|
|
|
// 获取面片中心(用于计算距离和视角)
|
|
|
|
|
const Face& face = scene.mesh.faces[fid]; |
|
|
|
|
Point3f faceCenter = (scene.mesh.vertices[face[0]] + |
|
|
|
|
scene.mesh.vertices[face[1]] + |
|
|
|
|
scene.mesh.vertices[face[2]]) / 3.0f; |
|
|
|
|
|
|
|
|
|
const Normal& faceNormal = scene.mesh.faceNormals[fid]; |
|
|
|
|
|
|
|
|
|
// 遍历能看到这个面片的所有相机
|
|
|
|
|
for (const FaceData& data : vfDatas) { |
|
|
|
|
if (data.bInvalidFacesRelative) continue; |
|
|
|
|
|
|
|
|
|
const Image& image = images[data.idxView]; |
|
|
|
|
|
|
|
|
|
// 使用综合评分函数
|
|
|
|
|
float score = ComputeComprehensiveScore(data, faceNormal, faceCenter, image); |
|
|
|
|
|
|
|
|
|
if (score > bestScore) { |
|
|
|
|
bestScore = score; |
|
|
|
|
bestViewID = data.idxView; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 如果找到了有效的视图
|
|
|
|
|
if (bestViewID != IIndex(-1) && bestScore > 0.1f) { |
|
|
|
|
virtualFaceViews[idxVF] = { bestViewID }; |
|
|
|
|
virtualFaceViewWeights[idxVF] = { 1.0f }; // 单视图权重设为1
|
|
|
|
|
} else { |
|
|
|
|
// virtualFaceViews[idxVF].clear();
|
|
|
|
|
// virtualFaceViewWeights[idxVF].clear();
|
|
|
|
|
// ++unassignedFaces;
|
|
|
|
|
|
|
|
|
|
float bestQuality = -1.0f; |
|
|
|
|
for (const FaceData& data : vfDatas) { |
|
|
|
|
if (data.quality > bestQuality) { |
|
|
|
|
bestQuality = data.quality; |
|
|
|
|
bestViewID = data.idxView; |
|
|
|
|
bestScore = data.quality; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Step 3-4 completed: %zu faces processed, %zu unassigned", |
|
|
|
|
numFaces, unassignedFaces); |
|
|
|
|
DEBUG_EXTRA("Proceeding to Step 5: Forward Rasterization"); |
|
|
|
|
std::vector<std::vector<IIndex>> virtualFaceViews = faceViews; |
|
|
|
|
std::vector<std::vector<float>> virtualFaceViewWeights = faceViewWeights; |
|
|
|
|
|
|
|
|
|
// ==========================================================
|
|
|
|
|
// 步骤5:正向光栅化引擎
|
|
|
|
|
@ -14469,41 +14417,86 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int
@@ -14469,41 +14417,86 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
DEBUG_EXTRA("Unassigned faces: %zu / %zu", unassignedFaces, numFaces); |
|
|
|
|
// DEBUG_EXTRA("Unassigned faces: %zu / %zu", unassignedFaces, numFaces);
|
|
|
|
|
DEBUG_EXTRA("Texture generation failed in stable mode"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool MeshTexture::FaceViewSelectionWithVirtualFaces(unsigned minCommonCameras, float fOutlierThreshold, |
|
|
|
|
float fRatioDataSmoothness, int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views, bool bUseExistingUV) |
|
|
|
|
bool MeshTexture::FaceViewSelectionWithVirtualFaces( |
|
|
|
|
unsigned minCommonCameras, |
|
|
|
|
float fOutlierThreshold, |
|
|
|
|
float fRatioDataSmoothness, |
|
|
|
|
int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views, |
|
|
|
|
bool bUseExistingUV) |
|
|
|
|
{ |
|
|
|
|
DEBUG_EXTRA("Performing face view selection with virtual faces (bUseExistingUV=%d)", bUseExistingUV); |
|
|
|
|
|
|
|
|
|
if (faceNeighbors.empty()) { |
|
|
|
|
DEBUG_EXTRA("Initializing faceNeighbors via ComputePureFaceVisibility..."); |
|
|
|
|
if (!ComputePureFaceVisibility(fOutlierThreshold, nIgnoreMaskLabel, views)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (bUseExistingUV) { |
|
|
|
|
// 使用现有UV的虚拟面选择策略
|
|
|
|
|
VirtualFaceMap virtualFaceMap; |
|
|
|
|
if (!CreateVirtualFacesForExistingUV(virtualFaceMap)) { |
|
|
|
|
DEBUG_EXTRA("Failed to create virtual faces for existing UV"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Created %zu virtual faces", virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
// 为虚拟面选择最佳视图
|
|
|
|
|
if (!SelectBestViewsForVirtualFaces(virtualFaceMap, minCommonCameras, fOutlierThreshold, |
|
|
|
|
if (!SelectBestViewsForVirtualFaces( |
|
|
|
|
virtualFaceMap, minCommonCameras, fOutlierThreshold, |
|
|
|
|
fRatioDataSmoothness, nIgnoreMaskLabel, views)) { |
|
|
|
|
DEBUG_EXTRA("Failed to select best views for virtual faces"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} else { |
|
|
|
|
// 使用原始面片的虚拟面选择
|
|
|
|
|
return FaceViewSelection(minCommonCameras, fOutlierThreshold, fRatioDataSmoothness, nIgnoreMaskLabel, views); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 非 UV 模式:走原生流程
|
|
|
|
|
return FaceViewSelection(minCommonCameras, fOutlierThreshold, |
|
|
|
|
fRatioDataSmoothness, nIgnoreMaskLabel, views); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool MeshTexture::ComputePureFaceVisibility( |
|
|
|
|
float fOutlierThreshold, |
|
|
|
|
int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views) |
|
|
|
|
{ |
|
|
|
|
DEBUG_EXTRA("Computing face visibility via ListCameraFaces (UV-safe)"); |
|
|
|
|
|
|
|
|
|
// 1. 调用 openMVS 原生的可见性计算
|
|
|
|
|
FaceDataViewArr facesDatas; |
|
|
|
|
const bool bUseVirtualFaces = false; // ✅ 关键:禁用虚拟面,保持 1:1 映射
|
|
|
|
|
|
|
|
|
|
if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, bUseVirtualFaces)) { |
|
|
|
|
DEBUG_EXTRA("ERROR: ListCameraFaces failed"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 2. 从 facesDatas 中提取 faceNeighbors
|
|
|
|
|
// facesDatas[i] 是第 i 个面的候选视图数组(FaceDataArr)
|
|
|
|
|
// 每个 FaceData 有 .idxView 成员
|
|
|
|
|
faceNeighbors.resize(facesDatas.size()); |
|
|
|
|
for (size_t i = 0; i < facesDatas.size(); ++i) { |
|
|
|
|
faceNeighbors[i].clear(); |
|
|
|
|
for (const FaceData& fd : facesDatas[i]) { |
|
|
|
|
faceNeighbors[i].push_back(fd.idxView); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 3. 统计
|
|
|
|
|
size_t visibleFaces = 0; |
|
|
|
|
for (const auto& v : faceNeighbors) |
|
|
|
|
if (!v.empty()) |
|
|
|
|
++visibleFaces; |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Visibility done: %zu/%zu faces have views", |
|
|
|
|
visibleFaces, facesDatas.size()); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool MeshTexture::GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, |
|
|
|
|
unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, |
|
|
|
|
Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, |
|
|
|
|
@ -14534,114 +14527,157 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
@@ -14534,114 +14527,157 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
|
|
|
|
{ |
|
|
|
|
DEBUG_EXTRA("Selecting best views for %zu virtual faces", virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
// 为每个虚拟面计算最佳视图
|
|
|
|
|
faceViews.resize(virtualFaceMap.size()); |
|
|
|
|
faceViewWeights.resize(virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
// 检查 m_virtualFaceGeometries 是否已初始化
|
|
|
|
|
if (m_virtualFaceGeometries.empty()) { |
|
|
|
|
m_virtualFaceGeometries.Resize(virtualFaceMap.size()); |
|
|
|
|
} else if (m_virtualFaceGeometries.size() < virtualFaceMap.size()) { |
|
|
|
|
if (m_virtualFaceGeometries.empty() || |
|
|
|
|
m_virtualFaceGeometries.size() < virtualFaceMap.size()) { |
|
|
|
|
m_virtualFaceGeometries.Resize(virtualFaceMap.size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 初始化每个虚拟面的视图数据数组
|
|
|
|
|
for (auto& viewList : faceViews) { |
|
|
|
|
for (auto& viewList : faceViews) |
|
|
|
|
viewList.clear(); |
|
|
|
|
} |
|
|
|
|
for (auto& weightList : faceViewWeights) { |
|
|
|
|
for (auto& weightList : faceViewWeights) |
|
|
|
|
weightList.clear(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ===== Debug 统计 =====
|
|
|
|
|
size_t emptyCandidateViews = 0; |
|
|
|
|
size_t emptyCommonViews = 0; |
|
|
|
|
size_t fallbackByCenterFace = 0; |
|
|
|
|
size_t successVF = 0; |
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < virtualFaceMap.size(); ++i) { |
|
|
|
|
const VirtualFace& vf = virtualFaceMap[i]; |
|
|
|
|
FIndex faceID2 = vf.faces[0]; |
|
|
|
|
|
|
|
|
|
if (faceID2 >= faceNeighbors.size()) { |
|
|
|
|
DEBUG_EXTRA("FATAL: faceID %u out of range!", faceID2); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (faceNeighbors[faceID2].empty()) { |
|
|
|
|
DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 收集所有相关视图
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 1. 收集每个面片的候选视图
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
std::unordered_set<IIndex> candidateViews; |
|
|
|
|
std::vector<std::vector<IIndex>> faceViewCandidates(vf.faces.size()); |
|
|
|
|
|
|
|
|
|
for (size_t j = 0; j < vf.faces.size(); ++j) { |
|
|
|
|
FIndex faceID = vf.faces[j]; |
|
|
|
|
if (faceID < faceNeighbors.size()) { |
|
|
|
|
for (size_t k = 0; k < faceNeighbors[faceID].size(); ++k) { |
|
|
|
|
IIndex viewID = faceNeighbors[faceID][k]; |
|
|
|
|
if (faceID >= faceNeighbors.size()) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
for (IIndex viewID : faceNeighbors[faceID]) { |
|
|
|
|
if (views.empty() || views.FindFirst(viewID) != NO_ID) { |
|
|
|
|
candidateViews.insert(viewID); |
|
|
|
|
faceViewCandidates[j].push_back(viewID); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 如果候选视图为空,跳过这个虚拟面
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 2. 【终极兜底】候选视图为空 → 强制分配一个视图
|
|
|
|
|
// 物理遮挡不重要,逻辑可见性最重要
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
if (candidateViews.empty()) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
++emptyCandidateViews; |
|
|
|
|
|
|
|
|
|
// 找到所有虚拟面都可见的视图
|
|
|
|
|
std::vector<IIndex> commonViews; |
|
|
|
|
for (IIndex viewID : candidateViews) { |
|
|
|
|
bool allFacesVisible = true; |
|
|
|
|
for (size_t j = 0; j < vf.faces.size() && allFacesVisible; ++j) { |
|
|
|
|
if (std::find(faceViewCandidates[j].begin(), faceViewCandidates[j].end(), viewID) == faceViewCandidates[j].end()) { |
|
|
|
|
allFacesVisible = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// 找一个可用的相机ID
|
|
|
|
|
// 优先级:views参数指定的 > 所有images中的第一个
|
|
|
|
|
IIndex forcedView = NO_ID; |
|
|
|
|
|
|
|
|
|
if (allFacesVisible) { |
|
|
|
|
commonViews.push_back(viewID); |
|
|
|
|
} |
|
|
|
|
if (!views.empty()) { |
|
|
|
|
forcedView = views[0]; |
|
|
|
|
} else if (!images.empty()) { |
|
|
|
|
forcedView = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 如果共同视图太少,使用所有候选视图
|
|
|
|
|
if (commonViews.size() < minCommonCameras) { |
|
|
|
|
// 将所有候选视图转换为vector
|
|
|
|
|
faceViews[i].assign(candidateViews.begin(), candidateViews.end()); |
|
|
|
|
} else { |
|
|
|
|
// 使用共同视图
|
|
|
|
|
faceViews[i] = commonViews; |
|
|
|
|
if (forcedView != NO_ID) { |
|
|
|
|
faceViews[i].push_back(forcedView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
++fallbackByCenterFace; |
|
|
|
|
|
|
|
|
|
// 降低日志级别,避免刷屏,用 DEBUG 而非 EXTRA
|
|
|
|
|
DEBUG("VF[%zu] EMPTY -> FORCED view %d (ignoring physical occlusion)", |
|
|
|
|
i, forcedView); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 如果仍然没有视图,则跳过
|
|
|
|
|
if (faceViews[i].empty()) { |
|
|
|
|
// 实在没救(连images都没有),这才跳过
|
|
|
|
|
DEBUG_EXTRA("VF[%zu] truly hopeless: no images available", i); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 分配相等的权重
|
|
|
|
|
float weight = 1.0f / faceViews[i].size(); |
|
|
|
|
faceViewWeights[i].resize(faceViews[i].size(), weight); |
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 3. 【关键放松】不再强求“所有面片共视”
|
|
|
|
|
// 只要视图能看到任意一个面片即可
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
std::vector<IIndex> usableViews( |
|
|
|
|
candidateViews.begin(), candidateViews.end() |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// 计算每个视图的质量
|
|
|
|
|
for (size_t k = 0; k < faceViews[i].size(); ++k) { |
|
|
|
|
IIndex viewID = faceViews[i][k]; |
|
|
|
|
// (可选)按质量排序,取前 N 个
|
|
|
|
|
// std::sort(usableViews.begin(), usableViews.end(),
|
|
|
|
|
// [&](IIndex a, IIndex b) { return Quality(a) > Quality(b); });
|
|
|
|
|
|
|
|
|
|
// 创建FaceData
|
|
|
|
|
FaceData faceData; |
|
|
|
|
faceData.idxView = viewID; |
|
|
|
|
faceData.quality = weight; // 使用权重作为质量
|
|
|
|
|
if (usableViews.size() < minCommonCameras) { |
|
|
|
|
faceViews[i] = usableViews; |
|
|
|
|
} else { |
|
|
|
|
// 仍尊重 minCommonCameras,但不强求共视
|
|
|
|
|
faceViews[i].assign( |
|
|
|
|
usableViews.begin(), |
|
|
|
|
usableViews.begin() + minCommonCameras |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA |
|
|
|
|
// 可以在这里计算和设置颜色
|
|
|
|
|
// 例如:faceData.color = CalculateMeanColor(viewID, vf);
|
|
|
|
|
#endif |
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 4. 【二次兜底】usableViews 为空(理论上不会发生)
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
if (faceViews[i].empty()) { |
|
|
|
|
++emptyCommonViews; |
|
|
|
|
|
|
|
|
|
faceData.bInvalidFacesRelative = false; |
|
|
|
|
if (!vf.faces.empty()) { |
|
|
|
|
FIndex centerFace = vf.faces[0]; |
|
|
|
|
if (centerFace < faceNeighbors.size() && |
|
|
|
|
!faceNeighbors[centerFace].empty()) { |
|
|
|
|
|
|
|
|
|
// 注意:m_virtualFaceGeometries 存储的是几何信息,不是视图数据
|
|
|
|
|
// 如果您需要存储视图数据,可以:
|
|
|
|
|
// 1. 创建单独的成员变量
|
|
|
|
|
// 2. 扩展 VirtualFaceGeometryData
|
|
|
|
|
// 3. 使用 faceViews 和 faceViewWeights
|
|
|
|
|
} |
|
|
|
|
IIndex bestView = faceNeighbors[centerFace][0]; |
|
|
|
|
faceViews[i].push_back(bestView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
|
|
|
|
|
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA |
|
|
|
|
if (fOutlierThreshold > 0) { |
|
|
|
|
// DetectAndRemoveOutliers(...);
|
|
|
|
|
DEBUG_EXTRA("VF[%zu] empty usableViews -> force view %d", |
|
|
|
|
i, bestView); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 5. 权重分配(均等,最稳定)
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
float weight = 1.0f / static_cast<float>(faceViews[i].size()); |
|
|
|
|
faceViewWeights[i].resize(faceViews[i].size(), weight); |
|
|
|
|
|
|
|
|
|
++successVF; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Best view selection completed for %zu virtual faces", virtualFaceMap.size()); |
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
// 6. Debug 汇总(非常重要)
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
DEBUG_EXTRA("====== Virtual Face View Selection Summary ======"); |
|
|
|
|
DEBUG_EXTRA("Total virtual faces : %zu", virtualFaceMap.size()); |
|
|
|
|
DEBUG_EXTRA("Successfully assigned : %zu", successVF); |
|
|
|
|
DEBUG_EXTRA("Empty candidates : %zu", emptyCandidateViews); |
|
|
|
|
DEBUG_EXTRA("Fallback by center face : %zu", fallbackByCenterFace); |
|
|
|
|
DEBUG_EXTRA("Empty after relaxation : %zu", emptyCommonViews); |
|
|
|
|
DEBUG_EXTRA("Expected empty ratio : %.2f%%", |
|
|
|
|
100.0 * (emptyCandidateViews - fallbackByCenterFace) / virtualFaceMap.size()); |
|
|
|
|
DEBUG_EXTRA("================================================="); |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -18634,26 +18670,45 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
@@ -18634,26 +18670,45 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
|
|
|
|
|
|
|
|
|
|
// 处理已有UV的情况
|
|
|
|
|
if (bUseExistingUV && !strUVMeshFileName.empty()) { |
|
|
|
|
// VERBOSE("1faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3);
|
|
|
|
|
// // 使用预计算UV模式
|
|
|
|
|
// if (!mesh.Load(MAKE_PATH_SAFE(strUVMeshFileName), true)) {
|
|
|
|
|
// VERBOSE("error: cannot load mesh file with UV coordinates");
|
|
|
|
|
// return false;
|
|
|
|
|
// }
|
|
|
|
|
MeshTexture texture(*this, nResolutionLevel, nMinResolution); |
|
|
|
|
|
|
|
|
|
VERBOSE("2faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); |
|
|
|
|
// mesh.CheckUVValid();
|
|
|
|
|
// ✅ 只用“纯可见性”,绝不碰 FaceViewSelection
|
|
|
|
|
if (!texture.ComputePureFaceVisibility( |
|
|
|
|
fOutlierThreshold, |
|
|
|
|
nIgnoreMaskLabel, |
|
|
|
|
views)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅ 现在 faces 没动过,faceNeighbors 是对齐的
|
|
|
|
|
MeshTexture::VirtualFaceMap virtualFaceMap; |
|
|
|
|
if (!texture.CreateVirtualFacesForExistingUV(virtualFaceMap)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 确保网格包含UV坐标
|
|
|
|
|
if (mesh.faceTexcoords.empty()) { |
|
|
|
|
VERBOSE("error: the specified mesh does not contain UV coordinates"); |
|
|
|
|
if (!texture.SelectBestViewsForVirtualFaces( |
|
|
|
|
virtualFaceMap, |
|
|
|
|
1, // 单视图就够了
|
|
|
|
|
fOutlierThreshold, |
|
|
|
|
fRatioDataSmoothness, |
|
|
|
|
nIgnoreMaskLabel, |
|
|
|
|
views)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 使用虚拟面优化的纹理生成
|
|
|
|
|
if (!texture.TextureWithExistingUVVirtualFaces(views, nIgnoreMaskLabel, fOutlierThreshold, nTextureSizeMultiple, colEmpty, fSharpnessWeight)){ |
|
|
|
|
// 直接光栅化
|
|
|
|
|
Mesh::Image8U3Arr textures; |
|
|
|
|
if (!texture.RasterizeVirtualFaces( |
|
|
|
|
virtualFaceMap, |
|
|
|
|
texture.faceViews, |
|
|
|
|
texture.faceViewWeights, |
|
|
|
|
nTextureSizeMultiple, |
|
|
|
|
colEmpty, |
|
|
|
|
textures)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mesh.texturesDiffuse = std::move(textures); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|