|
|
|
|
@ -14606,10 +14606,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
@@ -14606,10 +14606,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
|
|
|
|
m_virtualFaceGeometries.Resize(virtualFaceMap.size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (auto& viewList : faceViews) |
|
|
|
|
viewList.clear(); |
|
|
|
|
for (auto& weightList : faceViewWeights) |
|
|
|
|
weightList.clear(); |
|
|
|
|
for (auto& viewList : faceViews) viewList.clear(); |
|
|
|
|
for (auto& weightList : faceViewWeights) weightList.clear(); |
|
|
|
|
|
|
|
|
|
// ===== Debug 统计 =====
|
|
|
|
|
size_t emptyCandidateViews = 0; |
|
|
|
|
@ -14617,128 +14615,142 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
@@ -14617,128 +14615,142 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
|
|
|
|
size_t fallbackByCenterFace = 0; |
|
|
|
|
size_t successVF = 0; |
|
|
|
|
|
|
|
|
|
// ✅ 面片 → 视图 映射表
|
|
|
|
|
std::vector<IIndex> faceToView(scene.mesh.faces.size(), NO_ID); |
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// 1. 初始视图分配
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
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 (vf.faces.empty()) continue; |
|
|
|
|
|
|
|
|
|
if (faceNeighbors[faceID2].empty()) { |
|
|
|
|
// DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2);
|
|
|
|
|
} |
|
|
|
|
FIndex faceID2 = vf.faces[0]; |
|
|
|
|
if (faceID2 >= faceNeighbors.size()) { |
|
|
|
|
DEBUG_EXTRA("FATAL: faceID %u out of range!", faceID2); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 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()) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
for (FIndex faceID : vf.faces) { |
|
|
|
|
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()) { |
|
|
|
|
++emptyCandidateViews; |
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
// 2. 兜底:候选视图为空
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
if (candidateViews.empty()) { |
|
|
|
|
++emptyCandidateViews; |
|
|
|
|
|
|
|
|
|
// 找一个可用的相机ID
|
|
|
|
|
// 优先级:views参数指定的 > 所有images中的第一个
|
|
|
|
|
IIndex forcedView = NO_ID; |
|
|
|
|
IIndex forcedView = NO_ID; |
|
|
|
|
if (!views.empty()) { |
|
|
|
|
forcedView = views[0]; |
|
|
|
|
} else if (!images.empty()) { |
|
|
|
|
forcedView = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!views.empty()) { |
|
|
|
|
forcedView = views[0]; |
|
|
|
|
} else if (!images.empty()) { |
|
|
|
|
forcedView = 0; |
|
|
|
|
} |
|
|
|
|
if (forcedView != NO_ID) { |
|
|
|
|
faceViews[i].push_back(forcedView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
|
|
|
|
|
if (forcedView != NO_ID) { |
|
|
|
|
faceViews[i].push_back(forcedView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
++fallbackByCenterFace; |
|
|
|
|
for (FIndex fid : vf.faces) { |
|
|
|
|
if (fid < faceToView.size()) |
|
|
|
|
faceToView[fid] = forcedView; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 降低日志级别,避免刷屏,用 DEBUG 而非 EXTRA
|
|
|
|
|
// DEBUG("VF[%zu] EMPTY -> FORCED view %d (ignoring physical occlusion)",
|
|
|
|
|
// i, forcedView);
|
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
++fallbackByCenterFace; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 实在没救(连images都没有),这才跳过
|
|
|
|
|
DEBUG_EXTRA("VF[%zu] truly hopeless: no images available", i); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
DEBUG_EXTRA("VF[%zu] truly hopeless: no images available", i); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 3. 【关键放松】不再强求“所有面片共视”
|
|
|
|
|
// 只要视图能看到任意一个面片即可
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
std::vector<IIndex> usableViews( |
|
|
|
|
candidateViews.begin(), candidateViews.end() |
|
|
|
|
); |
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
// 3. 选择最佳视图(简单策略:第一个候选)
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
IIndex bestView = *candidateViews.begin(); |
|
|
|
|
|
|
|
|
|
// (可选)按质量排序,取前 N 个
|
|
|
|
|
// std::sort(usableViews.begin(), usableViews.end(),
|
|
|
|
|
// [&](IIndex a, IIndex b) { return Quality(a) > Quality(b); });
|
|
|
|
|
faceViews[i].push_back(bestView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
|
|
|
|
|
if (usableViews.size() < minCommonCameras) { |
|
|
|
|
faceViews[i] = usableViews; |
|
|
|
|
} else { |
|
|
|
|
// 仍尊重 minCommonCameras,但不强求共视
|
|
|
|
|
faceViews[i].assign( |
|
|
|
|
usableViews.begin(), |
|
|
|
|
usableViews.begin() + minCommonCameras |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 4. 【二次兜底】usableViews 为空(理论上不会发生)
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
if (faceViews[i].empty()) { |
|
|
|
|
++emptyCommonViews; |
|
|
|
|
|
|
|
|
|
if (!vf.faces.empty()) { |
|
|
|
|
FIndex centerFace = vf.faces[0]; |
|
|
|
|
if (centerFace < faceNeighbors.size() && |
|
|
|
|
!faceNeighbors[centerFace].empty()) { |
|
|
|
|
|
|
|
|
|
IIndex bestView = faceNeighbors[centerFace][0]; |
|
|
|
|
faceViews[i].push_back(bestView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("VF[%zu] empty usableViews -> force view %d", |
|
|
|
|
i, bestView); |
|
|
|
|
continue; |
|
|
|
|
for (FIndex fid : vf.faces) { |
|
|
|
|
if (fid < faceToView.size()) |
|
|
|
|
faceToView[fid] = bestView; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
++successVF; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// 4. 【轻量 Patch 一致性传播】
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
if (scene.mesh.faceFaces.empty()) { |
|
|
|
|
scene.mesh.ListIncidenteFaceFaces(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const int PROPAGATION_ITER = 2; |
|
|
|
|
for (int iter = 0; iter < PROPAGATION_ITER; ++iter) { |
|
|
|
|
std::vector<IIndex> newFaceToView = faceToView; |
|
|
|
|
|
|
|
|
|
for (FIndex fid = 0; fid < (FIndex)faceToView.size(); ++fid) { |
|
|
|
|
if (faceToView[fid] == NO_ID) continue; |
|
|
|
|
if (fid >= (FIndex)scene.mesh.faceFaces.size()) continue; |
|
|
|
|
|
|
|
|
|
const Mesh::Face& neighbors = scene.mesh.faceFaces[fid]; |
|
|
|
|
|
|
|
|
|
std::unordered_map<IIndex, int> vote; |
|
|
|
|
vote[faceToView[fid]] = 1; |
|
|
|
|
|
|
|
|
|
for (int k = 0; k < 3; ++k) { |
|
|
|
|
FIndex nb = neighbors[k]; |
|
|
|
|
if (nb == NO_ID) continue; |
|
|
|
|
if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) { |
|
|
|
|
vote[faceToView[nb]]++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
IIndex majorityView = faceToView[fid]; |
|
|
|
|
int maxVote = 0; |
|
|
|
|
for (const auto& v : vote) { |
|
|
|
|
if (v.second > maxVote) { |
|
|
|
|
maxVote = v.second; |
|
|
|
|
majorityView = v.first; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (maxVote >= 3 && majorityView != faceToView[fid]) { |
|
|
|
|
newFaceToView[fid] = majorityView; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
// 5. 权重分配(均等,最稳定)
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
float weight = 1.0f / static_cast<float>(faceViews[i].size()); |
|
|
|
|
faceViewWeights[i].resize(faceViews[i].size(), weight); |
|
|
|
|
faceToView.swap(newFaceToView); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
++successVF; |
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// 5. 根据传播后的结果,更新 virtual face 的视图
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
for (size_t i = 0; i < virtualFaceMap.size(); ++i) { |
|
|
|
|
const VirtualFace& vf = virtualFaceMap[i]; |
|
|
|
|
if (vf.faces.empty() || faceViews[i].empty()) continue; |
|
|
|
|
|
|
|
|
|
FIndex firstFace = vf.faces[0]; |
|
|
|
|
if (firstFace < faceToView.size() && faceToView[firstFace] != NO_ID) { |
|
|
|
|
IIndex consistentView = faceToView[firstFace]; |
|
|
|
|
faceViews[i] = {consistentView}; |
|
|
|
|
faceViewWeights[i] = {1.0f}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
// 6. Debug 汇总(非常重要)
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// 6. Debug 汇总
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
DEBUG_EXTRA("====== Virtual Face View Selection Summary ======"); |
|
|
|
|
DEBUG_EXTRA("Total virtual faces : %zu", virtualFaceMap.size()); |
|
|
|
|
DEBUG_EXTRA("Successfully assigned : %zu", successVF); |
|
|
|
|
|