|
|
|
|
@ -539,6 +539,14 @@ struct MeshTexture {
@@ -539,6 +539,14 @@ struct MeshTexture {
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct VirtualFaceGeometry { |
|
|
|
|
Point3f center; // 面片中心(世界坐标)
|
|
|
|
|
Point3f normal; // 面片法线
|
|
|
|
|
AABB2f uvBounds; // UV 包围盒
|
|
|
|
|
cv::Mat1f homography; // 3x3 单应矩阵(从 UV 到视图)
|
|
|
|
|
bool isValid = false; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// used to interpolate adjustments color over the whole texture patch
|
|
|
|
|
typedef TImage<Color> ColorMap; |
|
|
|
|
|
|
|
|
|
@ -550,6 +558,7 @@ struct MeshTexture {
@@ -550,6 +558,7 @@ struct MeshTexture {
|
|
|
|
|
: r(_r), g(_g), b(_b), a(_a) {} |
|
|
|
|
}; |
|
|
|
|
*/ |
|
|
|
|
std::vector<VirtualFaceGeometry> m_virtualFaceGeometries; |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
MeshTexture(Scene& _scene, unsigned _nResolutionLevel=0, unsigned _nMinResolution=640); |
|
|
|
|
@ -621,12 +630,20 @@ public:
@@ -621,12 +630,20 @@ public:
|
|
|
|
|
unsigned minCommonCameras, float fOutlierThreshold, |
|
|
|
|
float fRatioDataSmoothness, int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views); |
|
|
|
|
|
|
|
|
|
bool ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMap); |
|
|
|
|
|
|
|
|
|
bool ComputeHomographyForVirtualFace( |
|
|
|
|
const VirtualFace& vf, |
|
|
|
|
IIndex viewID, |
|
|
|
|
VirtualFaceGeometry& geom); |
|
|
|
|
|
|
|
|
|
std::vector<std::vector<IIndex>> faceViews; |
|
|
|
|
std::vector<std::vector<float>> faceViewWeights; |
|
|
|
|
|
|
|
|
|
std::vector<VirtualFaceData> virtualFaceDatas; |
|
|
|
|
std::vector<std::vector<IIndex>> faceNeighbors; |
|
|
|
|
VirtualFaceGeometryArr m_virtualFaceGeometries; |
|
|
|
|
// VirtualFaceGeometryArr m_virtualFaceGeometries;
|
|
|
|
|
|
|
|
|
|
inline Point3f NormalizePoint3(Point3f& p) |
|
|
|
|
{ |
|
|
|
|
@ -14161,6 +14178,9 @@ void MeshTexture::FillTextureHoles(std::vector<Image8U3>& textures, Pixel8U colE
@@ -14161,6 +14178,9 @@ void MeshTexture::FillTextureHoles(std::vector<Image8U3>& textures, Pixel8U colE
|
|
|
|
|
DEBUG_EXTRA("Hole filling completed"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// 3. RC 风格光栅化主函数
|
|
|
|
|
// ============================================================
|
|
|
|
|
bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
|
const VirtualFaceMap& virtualFaceMap, |
|
|
|
|
const std::vector<std::vector<IIndex>>& virtualFaceViews, |
|
|
|
|
@ -14169,7 +14189,7 @@ bool MeshTexture::RasterizeVirtualFaces(
@@ -14169,7 +14189,7 @@ bool MeshTexture::RasterizeVirtualFaces(
|
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
Mesh::Image8U3Arr& outTextures) |
|
|
|
|
{ |
|
|
|
|
DEBUG_EXTRA("Forward Rasterization Engine: Starting..."); |
|
|
|
|
DEBUG_EXTRA("RC-style Rasterization Engine: Starting..."); |
|
|
|
|
TD_TIMER_START(); |
|
|
|
|
|
|
|
|
|
if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) |
|
|
|
|
@ -14190,162 +14210,109 @@ bool MeshTexture::RasterizeVirtualFaces(
@@ -14190,162 +14210,109 @@ bool MeshTexture::RasterizeVirtualFaces(
|
|
|
|
|
int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); |
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
// 2. 创建纹理与缓冲器
|
|
|
|
|
// 2. 创建纹理
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
outTextures.emplace_back(textureSize, textureSize); |
|
|
|
|
Image8U3& atlas = outTextures.back(); |
|
|
|
|
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
|
|
|
|
|
|
// ✅ 深度缓冲器初始化为正无穷(越小越近)
|
|
|
|
|
cv::Mat1f depthBuffer(textureSize, textureSize, FLT_MAX); |
|
|
|
|
cv::Mat3f colorBuffer(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f)); |
|
|
|
|
cv::Mat1b validBuffer(textureSize, textureSize, (uchar)0); |
|
|
|
|
// ✅ 计算所有虚拟面的几何和映射矩阵
|
|
|
|
|
if (!ComputeVirtualFaceGeometry(virtualFaceMap)) { |
|
|
|
|
DEBUG_EXTRA("Failed to compute virtual face geometries"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
// 3. 正向光栅化主循环
|
|
|
|
|
// 3. RC 风格光栅化:按虚拟面批量处理
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
|
#endif |
|
|
|
|
for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { |
|
|
|
|
if (virtualFaceViews[idxVF].empty()) |
|
|
|
|
for (int i = 0; i < (int)virtualFaceMap.size(); ++i) { |
|
|
|
|
const VirtualFace& vf = virtualFaceMap[i]; |
|
|
|
|
const VirtualFaceGeometry& geom = m_virtualFaceGeometries[i]; |
|
|
|
|
|
|
|
|
|
if (!geom.isValid || vf.faces.empty() || virtualFaceViews[i].empty()) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
const IIndex viewID = virtualFaceViews[idxVF][0]; |
|
|
|
|
IIndex viewID = virtualFaceViews[i][0]; |
|
|
|
|
if (viewID >= (IIndex)images.size()) continue; |
|
|
|
|
|
|
|
|
|
const Image& srcImg = images[viewID]; |
|
|
|
|
if (srcImg.image.empty() || srcImg.image.cols < 2 || srcImg.image.rows < 2) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
const Camera& cam = srcImg.camera; |
|
|
|
|
const int srcW = srcImg.image.cols; |
|
|
|
|
const int srcH = srcImg.image.rows; |
|
|
|
|
|
|
|
|
|
for (FIndex faceID : virtualFaceMap[idxVF].faces) { |
|
|
|
|
if (faceID >= (FIndex)scene.mesh.faces.size()) continue; |
|
|
|
|
|
|
|
|
|
const Face& face = scene.mesh.faces[faceID]; |
|
|
|
|
const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3]; |
|
|
|
|
|
|
|
|
|
// 验证顶点索引
|
|
|
|
|
bool validIndices = true; |
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
if (face[i] >= scene.mesh.vertices.size()) { |
|
|
|
|
validIndices = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!validIndices) continue; |
|
|
|
|
|
|
|
|
|
const Point3f* verts[3] = { |
|
|
|
|
&scene.mesh.vertices[face[0]], |
|
|
|
|
&scene.mesh.vertices[face[1]], |
|
|
|
|
&scene.mesh.vertices[face[2]] |
|
|
|
|
}; |
|
|
|
|
// ✅ UV 包围盒 → 纹理像素范围
|
|
|
|
|
int minX = std::max(0, (int)floor(geom.uvBounds.ptMin.x() * textureSize)); |
|
|
|
|
int maxX = std::min(textureSize - 1, (int)ceil(geom.uvBounds.ptMax.x() * textureSize)); |
|
|
|
|
int minY = std::max(0, (int)floor(geom.uvBounds.ptMin.y() * textureSize)); |
|
|
|
|
int maxY = std::min(textureSize - 1, (int)ceil(geom.uvBounds.ptMax.y() * textureSize)); |
|
|
|
|
|
|
|
|
|
// 计算包围盒
|
|
|
|
|
float minU = std::min({uv[0].x, uv[1].x, uv[2].x}); |
|
|
|
|
float maxU = std::max({uv[0].x, uv[1].x, uv[2].x}); |
|
|
|
|
float minV = std::min({uv[0].y, uv[1].y, uv[2].y}); |
|
|
|
|
float maxV = std::max({uv[0].y, uv[1].y, uv[2].y}); |
|
|
|
|
if (minX > maxX || minY > maxY) continue; |
|
|
|
|
|
|
|
|
|
int minX = std::max(0, (int)floor(minU * textureSize)); |
|
|
|
|
int maxX = std::min(textureSize - 1, (int)ceil(maxU * textureSize)); |
|
|
|
|
int minY = std::max(0, (int)floor(minV * textureSize)); |
|
|
|
|
int maxY = std::min(textureSize - 1, (int)ceil(maxV * textureSize)); |
|
|
|
|
int patchW = maxX - minX + 1; |
|
|
|
|
int patchH = maxY - minY + 1; |
|
|
|
|
|
|
|
|
|
if (minX > maxX || minY > maxY) continue; |
|
|
|
|
// ✅ 映射矩阵
|
|
|
|
|
cv::Mat mapX(patchH, patchW, CV_32FC1); |
|
|
|
|
cv::Mat mapY(patchH, patchW, CV_32FC1); |
|
|
|
|
|
|
|
|
|
// ✅ 预计算顶点深度(用于透视校正)
|
|
|
|
|
float vertexDepths[3]; |
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
vertexDepths[i] = cam.PointDepth(*verts[i]); |
|
|
|
|
if (vertexDepths[i] <= 0.0f) { |
|
|
|
|
validIndices = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!validIndices) continue; |
|
|
|
|
// ✅ 直接展开 H 系数(无临时 Mat,RC 标准写法)
|
|
|
|
|
const float* H = geom.homography.ptr<float>(); |
|
|
|
|
|
|
|
|
|
for (int y = minY; y <= maxY; ++y) { |
|
|
|
|
for (int x = minX; x <= maxX; ++x) { |
|
|
|
|
Point2f texCoord( |
|
|
|
|
(x + 0.5f) / textureSize, |
|
|
|
|
(y + 0.5f) / textureSize |
|
|
|
|
); |
|
|
|
|
float u = (float)x / (float)textureSize; |
|
|
|
|
float v = (float)y / (float)textureSize; |
|
|
|
|
|
|
|
|
|
Point3f bary; |
|
|
|
|
if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary)) |
|
|
|
|
continue; |
|
|
|
|
float w = H[6] * u + H[7] * v + H[8]; |
|
|
|
|
|
|
|
|
|
// ✅ 透视校正插值(double 精度)
|
|
|
|
|
double invZ = bary.x / vertexDepths[0] + |
|
|
|
|
bary.y / vertexDepths[1] + |
|
|
|
|
bary.z / vertexDepths[2]; |
|
|
|
|
if (invZ <= 0.0) continue; |
|
|
|
|
// ✅ 数值保护(防止除零)
|
|
|
|
|
if (std::abs(w) < 1e-12f) { |
|
|
|
|
mapX.at<float>(y - minY, x - minX) = -1.0f; |
|
|
|
|
mapY.at<float>(y - minY, x - minX) = -1.0f; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅ 校正后的重心坐标
|
|
|
|
|
double u0 = (bary.x / vertexDepths[0]) / invZ; |
|
|
|
|
double u1 = (bary.y / vertexDepths[1]) / invZ; |
|
|
|
|
double u2 = (bary.z / vertexDepths[2]) / invZ; |
|
|
|
|
float imgX = (H[0] * u + H[1] * v + H[2]) / w; |
|
|
|
|
float imgY = (H[3] * u + H[4] * v + H[5]) / w; |
|
|
|
|
|
|
|
|
|
// ✅ 透视校正的世界坐标(double)
|
|
|
|
|
Point3d P_double( |
|
|
|
|
verts[0]->x * u0 + verts[1]->x * u1 + verts[2]->x * u2, |
|
|
|
|
verts[0]->y * u0 + verts[1]->y * u1 + verts[2]->y * u2, |
|
|
|
|
verts[0]->z * u0 + verts[1]->z * u1 + verts[2]->z * u2 |
|
|
|
|
); |
|
|
|
|
mapX.at<float>(y - minY, x - minX) = imgX; |
|
|
|
|
mapY.at<float>(y - minY, x - minX) = imgY; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅ 正确的投影(double → float)
|
|
|
|
|
Point2d imgPtDouble = cam.ProjectPoint(P_double); |
|
|
|
|
Point2f imgPt(static_cast<float>(imgPtDouble.x), |
|
|
|
|
static_cast<float>(imgPtDouble.y)); |
|
|
|
|
// ✅ 一次性 remap 整个 patch
|
|
|
|
|
cv::Mat patch; |
|
|
|
|
cv::remap(srcImg.image, patch, mapX, mapY, |
|
|
|
|
cv::INTER_LINEAR, cv::BORDER_CONSTANT, |
|
|
|
|
cv::Scalar(0, 0, 0)); |
|
|
|
|
|
|
|
|
|
// ✅ 边界检查
|
|
|
|
|
if (imgPt.x < 0.5f || imgPt.y < 0.5f || |
|
|
|
|
imgPt.x >= srcW - 0.5f || imgPt.y >= srcH - 0.5f) |
|
|
|
|
// ✅ 拷贝到 atlas(OpenMP critical 区)
|
|
|
|
|
#pragma omp critical |
|
|
|
|
{ |
|
|
|
|
for (int y = 0; y < patchH; ++y) { |
|
|
|
|
for (int x = 0; x < patchW; ++x) { |
|
|
|
|
cv::Vec3b color = patch.at<cv::Vec3b>(y, x); |
|
|
|
|
// 跳过无效像素(BORDER_CONSTANT 产生的黑色)
|
|
|
|
|
if (color[0] == 0 && color[1] == 0 && color[2] == 0) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
// ✅ 双线性采样
|
|
|
|
|
Color color = BilinearSample(srcImg.image, imgPt); |
|
|
|
|
if (color[0] < 0 || color[1] < 0 || color[2] < 0) continue; |
|
|
|
|
int atlasX = x + minX; |
|
|
|
|
int atlasY = y + minY; |
|
|
|
|
|
|
|
|
|
// ✅ 深度测试(float 缓冲,double 比较)
|
|
|
|
|
float depthFloat = static_cast<float>(1.0 / invZ); |
|
|
|
|
// ✅ 边界保护
|
|
|
|
|
if (atlasX < 0 || atlasX >= textureSize || |
|
|
|
|
atlasY < 0 || atlasY >= textureSize) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
#pragma omp critical |
|
|
|
|
{ |
|
|
|
|
if (depthFloat < depthBuffer(y, x)) { |
|
|
|
|
depthBuffer(y, x) = depthFloat; |
|
|
|
|
colorBuffer(y, x) = cv::Vec3f( |
|
|
|
|
color[0], color[1], color[2] |
|
|
|
|
); |
|
|
|
|
validBuffer(y, x) = 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
// 4. 最终写入纹理
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
|
for (int y = 0; y < textureSize; ++y) { |
|
|
|
|
for (int x = 0; x < textureSize; ++x) { |
|
|
|
|
if (validBuffer(y, x)) { |
|
|
|
|
cv::Vec3f c = colorBuffer(y, x); |
|
|
|
|
atlas(y, x) = Pixel8U{ |
|
|
|
|
(unsigned char)cv::saturate_cast<uchar>(c[0]), |
|
|
|
|
(unsigned char)cv::saturate_cast<uchar>(c[1]), |
|
|
|
|
(unsigned char)cv::saturate_cast<uchar>(c[2]) |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Forward Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); |
|
|
|
|
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -14639,7 +14606,11 @@ bool MeshTexture::GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool
@@ -14639,7 +14606,11 @@ bool MeshTexture::GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, |
|
|
|
|
// ============================================================
|
|
|
|
|
// 5. SelectBestViewsForVirtualFaces(带 patch 一致性传播)
|
|
|
|
|
// ============================================================
|
|
|
|
|
bool MeshTexture::SelectBestViewsForVirtualFaces( |
|
|
|
|
VirtualFaceMap& virtualFaceMap, |
|
|
|
|
unsigned minCommonCameras, float fOutlierThreshold, |
|
|
|
|
float fRatioDataSmoothness, int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views) |
|
|
|
|
@ -14649,82 +14620,62 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
@@ -14649,82 +14620,62 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
|
|
|
|
faceViews.resize(virtualFaceMap.size()); |
|
|
|
|
faceViewWeights.resize(virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
if (m_virtualFaceGeometries.empty() || |
|
|
|
|
m_virtualFaceGeometries.size() < virtualFaceMap.size()) { |
|
|
|
|
m_virtualFaceGeometries.Resize(virtualFaceMap.size()); |
|
|
|
|
for (auto& v : faceViews) v.clear(); |
|
|
|
|
for (auto& w : faceViewWeights) w.clear(); |
|
|
|
|
|
|
|
|
|
// 确保 faceNeighbors 已填充
|
|
|
|
|
if (faceNeighbors.empty()) { |
|
|
|
|
DEBUG_EXTRA("faceNeighbors empty, running ComputePureFaceVisibility first"); |
|
|
|
|
if (!ComputePureFaceVisibility(fOutlierThreshold, nIgnoreMaskLabel, views)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (auto& viewList : faceViews) viewList.clear(); |
|
|
|
|
for (auto& weightList : faceViewWeights) weightList.clear(); |
|
|
|
|
// faceToView 映射
|
|
|
|
|
std::vector<IIndex> faceToView(scene.mesh.faces.size(), NO_ID); |
|
|
|
|
|
|
|
|
|
// ===== Debug 统计 =====
|
|
|
|
|
// ---------- 1. 初始视图分配 ----------
|
|
|
|
|
size_t emptyCandidateViews = 0; |
|
|
|
|
size_t emptyCommonViews = 0; |
|
|
|
|
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]; |
|
|
|
|
if (vf.faces.empty()) continue; |
|
|
|
|
|
|
|
|
|
FIndex faceID2 = vf.faces[0]; |
|
|
|
|
if (faceID2 >= faceNeighbors.size()) { |
|
|
|
|
DEBUG_EXTRA("FATAL: faceID %u out of range!", faceID2); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
FIndex faceID = vf.faces[0]; |
|
|
|
|
if (faceID >= faceNeighbors.size()) continue; |
|
|
|
|
|
|
|
|
|
// 收集候选视图
|
|
|
|
|
std::unordered_set<IIndex> candidateViews; |
|
|
|
|
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); |
|
|
|
|
for (FIndex fid : vf.faces) { |
|
|
|
|
if (fid >= faceNeighbors.size()) continue; |
|
|
|
|
for (IIndex vid : faceNeighbors[fid]) { |
|
|
|
|
if (views.empty() || views.FindFirst(vid) != NO_ID) { |
|
|
|
|
candidateViews.insert(vid); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
// 2. 兜底:候选视图为空
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
if (candidateViews.empty()) { |
|
|
|
|
++emptyCandidateViews; |
|
|
|
|
|
|
|
|
|
IIndex forcedView = NO_ID; |
|
|
|
|
if (!views.empty()) { |
|
|
|
|
forcedView = views[0]; |
|
|
|
|
} else if (!images.empty()) { |
|
|
|
|
forcedView = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 兜底:用第一个可用视图
|
|
|
|
|
IIndex forcedView = (!views.empty()) ? views[0] : |
|
|
|
|
(!images.empty()) ? 0 : NO_ID; |
|
|
|
|
if (forcedView != NO_ID) { |
|
|
|
|
faceViews[i].push_back(forcedView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
|
|
|
|
|
for (FIndex fid : vf.faces) { |
|
|
|
|
if (fid < faceToView.size()) |
|
|
|
|
faceToView[fid] = forcedView; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
++fallbackByCenterFace; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("VF[%zu] truly hopeless: no images available", i); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
// 3. 选择最佳视图(简单策略:第一个候选)
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
// 选第一个候选(简单策略,后续可优化为角度最优)
|
|
|
|
|
IIndex bestView = *candidateViews.begin(); |
|
|
|
|
|
|
|
|
|
faceViews[i].push_back(bestView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
|
|
|
|
|
@ -14732,13 +14683,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
@@ -14732,13 +14683,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
|
|
|
|
if (fid < faceToView.size()) |
|
|
|
|
faceToView[fid] = bestView; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
++successVF; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// 4. 【轻量 Patch 一致性传播】
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// ---------- 2. Patch 一致性传播 ----------
|
|
|
|
|
if (scene.mesh.faceFaces.empty()) { |
|
|
|
|
scene.mesh.ListIncidenteFaceFaces(); |
|
|
|
|
} |
|
|
|
|
@ -14777,13 +14725,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
@@ -14777,13 +14725,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
|
|
|
|
newFaceToView[fid] = majorityView; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
faceToView.swap(newFaceToView); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// 5. 根据传播后的结果,更新 virtual face 的视图
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// ---------- 3. 写回 ----------
|
|
|
|
|
for (size_t i = 0; i < virtualFaceMap.size(); ++i) { |
|
|
|
|
const VirtualFace& vf = virtualFaceMap[i]; |
|
|
|
|
if (vf.faces.empty() || faceViews[i].empty()) continue; |
|
|
|
|
@ -14796,22 +14741,150 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
@@ -14796,22 +14741,150 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap,
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
// 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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// 1. 计算虚拟面几何(Affine for triangle, Homography for patch)
|
|
|
|
|
// ============================================================
|
|
|
|
|
bool MeshTexture::ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMap) { |
|
|
|
|
// ✅ std::vector 用 resize
|
|
|
|
|
m_virtualFaceGeometries.resize(virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
|
#endif |
|
|
|
|
for (int i = 0; i < (int)virtualFaceMap.size(); ++i) { |
|
|
|
|
const VirtualFace& vf = virtualFaceMap[i]; |
|
|
|
|
VirtualFaceGeometry& geom = m_virtualFaceGeometries[i]; |
|
|
|
|
|
|
|
|
|
if (vf.faces.empty() || faceViews[i].empty()) { |
|
|
|
|
geom.isValid = false; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IIndex viewID = faceViews[i][0]; |
|
|
|
|
geom.isValid = ComputeHomographyForVirtualFace(vf, viewID, geom); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// 2. 核心:计算单应矩阵 / 仿射矩阵
|
|
|
|
|
// - 3 个点 → Affine(getAffineTransform)
|
|
|
|
|
// - ≥ 4 个点 → Homography(findHomography)
|
|
|
|
|
// ============================================================
|
|
|
|
|
bool MeshTexture::ComputeHomographyForVirtualFace( |
|
|
|
|
const VirtualFace& vf, |
|
|
|
|
IIndex viewID, |
|
|
|
|
VirtualFaceGeometry& geom) |
|
|
|
|
{ |
|
|
|
|
if (viewID >= (IIndex)images.size()) return false; |
|
|
|
|
const Camera& cam = images[viewID].camera; |
|
|
|
|
|
|
|
|
|
// ---------- 1. 收集顶点和 UV ----------
|
|
|
|
|
std::vector<Point3f> points3D; |
|
|
|
|
std::vector<Point2f> pointsUV; |
|
|
|
|
points3D.reserve(vf.faces.size() * 3); |
|
|
|
|
pointsUV.reserve(vf.faces.size() * 3); |
|
|
|
|
|
|
|
|
|
for (FIndex faceID : vf.faces) { |
|
|
|
|
if (faceID >= (FIndex)scene.mesh.faces.size()) continue; |
|
|
|
|
|
|
|
|
|
const Face& face = scene.mesh.faces[faceID]; |
|
|
|
|
const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3]; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
if (face[i] >= scene.mesh.vertices.size()) continue; |
|
|
|
|
|
|
|
|
|
points3D.push_back(scene.mesh.vertices[face[i]]); |
|
|
|
|
pointsUV.push_back(Point2f(uv[i].x, uv[i].y)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (points3D.size() < 3) return false; |
|
|
|
|
|
|
|
|
|
// ---------- 2. 计算中心(OpenCV 风格)----------
|
|
|
|
|
geom.center = Point3f(0, 0, 0); |
|
|
|
|
for (const auto& p : points3D) geom.center += p; |
|
|
|
|
geom.center *= 1.0f / (float)points3D.size(); |
|
|
|
|
|
|
|
|
|
// ---------- 3. 计算法线(OpenCV 风格)----------
|
|
|
|
|
if (!vf.faces.empty()) { |
|
|
|
|
FIndex faceID = vf.faces[0]; |
|
|
|
|
const Face& face = scene.mesh.faces[faceID]; |
|
|
|
|
const Point3f& v0 = scene.mesh.vertices[face[0]]; |
|
|
|
|
const Point3f& v1 = scene.mesh.vertices[face[1]]; |
|
|
|
|
const Point3f& v2 = scene.mesh.vertices[face[2]]; |
|
|
|
|
|
|
|
|
|
Point3f edge1 = v1 - v0; |
|
|
|
|
Point3f edge2 = v2 - v0; |
|
|
|
|
Point3f normal = edge1.cross(edge2); |
|
|
|
|
|
|
|
|
|
float length = cv::norm(normal); |
|
|
|
|
if (length > 1e-8f) { |
|
|
|
|
normal *= 1.0f / length; |
|
|
|
|
} else { |
|
|
|
|
normal = Point3f(0, 0, 1); |
|
|
|
|
} |
|
|
|
|
geom.normal = normal; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ---------- 4. UV 包围盒 ----------
|
|
|
|
|
geom.uvBounds.Reset(); |
|
|
|
|
for (const auto& uv : pointsUV) { |
|
|
|
|
geom.uvBounds.InsertFull(uv); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ---------- 5. 投影到图像空间(double → float)----------
|
|
|
|
|
std::vector<Point2f> pointsImage; |
|
|
|
|
pointsImage.reserve(points3D.size()); |
|
|
|
|
|
|
|
|
|
for (const auto& p3D : points3D) { |
|
|
|
|
Point3d p64(p3D.x, p3D.y, p3D.z); |
|
|
|
|
Point2d proj = cam.ProjectPoint(p64); |
|
|
|
|
pointsImage.emplace_back((float)proj.x, (float)proj.y); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ---------- 6. 核心分支:Affine vs Homography ----------
|
|
|
|
|
if (pointsUV.size() == 3) { |
|
|
|
|
// ✅ 三角形:用仿射变换(Affine = 精确映射)
|
|
|
|
|
cv::Point2f src[3] = {pointsUV[0], pointsUV[1], pointsUV[2]}; |
|
|
|
|
cv::Point2f dst[3] = {pointsImage[0], pointsImage[1], pointsImage[2]}; |
|
|
|
|
|
|
|
|
|
cv::Mat affine = cv::getAffineTransform(src, dst); |
|
|
|
|
if (affine.empty()) return false; |
|
|
|
|
|
|
|
|
|
// 转成 3x3 齐次矩阵(与 Homography 统一格式)
|
|
|
|
|
geom.homography = cv::Mat1f(3, 3, 0.0f); |
|
|
|
|
for (int r = 0; r < 2; ++r) { |
|
|
|
|
for (int c = 0; c < 3; ++c) { |
|
|
|
|
geom.homography(r, c) = affine.at<double>(r, c); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
geom.homography(2, 2) = 1.0f; |
|
|
|
|
} |
|
|
|
|
else if (pointsUV.size() >= 4) { |
|
|
|
|
// ✅ Patch:用 RANSAC 单应矩阵
|
|
|
|
|
geom.homography = cv::findHomography( |
|
|
|
|
pointsUV, pointsImage, cv::RANSAC, 2.0); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
geom.isValid = !geom.homography.empty(); |
|
|
|
|
return geom.isValid; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool MeshTexture::GenerateTextureWithVirtualFacesInternal(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, |
|
|
|
|
unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, |
|
|
|
|
Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, |
|
|
|
|
@ -14918,282 +14991,34 @@ bool MeshTexture::ConvertVectorToVirtualFaceDataArr(const std::vector<VirtualFac
@@ -14918,282 +14991,34 @@ bool MeshTexture::ConvertVectorToVirtualFaceDataArr(const std::vector<VirtualFac
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
bool MeshTexture::CreateVirtualFacesForExistingUV(VirtualFaceMap& virtualFaceMap) { |
|
|
|
|
DEBUG_EXTRA("Creating virtual faces for existing UV texture mapping"); |
|
|
|
|
|
|
|
|
|
// 0. 详细的输入验证
|
|
|
|
|
DEBUG_EXTRA("=== Detailed Input Validation ==="); |
|
|
|
|
DEBUG_EXTRA("Scene pointer: %p", &scene); |
|
|
|
|
DEBUG_EXTRA("Mesh pointer: %p", &scene.mesh); |
|
|
|
|
// ============================================================
|
|
|
|
|
// 4. CreateVirtualFacesForExistingUV(简化版)
|
|
|
|
|
// ============================================================
|
|
|
|
|
bool MeshTexture::CreateVirtualFacesForExistingUV(VirtualFaceMap& virtualFaceMap) { |
|
|
|
|
DEBUG_EXTRA("Creating virtual faces for existing UV (RC style)"); |
|
|
|
|
|
|
|
|
|
// 检查基本数据结构
|
|
|
|
|
// 输入验证
|
|
|
|
|
if (scene.mesh.faces.empty()) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Mesh has no faces!"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (scene.mesh.vertices.empty()) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Mesh has no vertices!"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
if (scene.mesh.faceTexcoords.empty()) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Mesh has no texture coordinates!"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅ RC 风格:每个三角形一个虚拟面(1:1)
|
|
|
|
|
virtualFaceMap.resize(scene.mesh.faces.size()); |
|
|
|
|
for (FIndex i = 0; i < (FIndex)scene.mesh.faces.size(); ++i) { |
|
|
|
|
virtualFaceMap[i].faces = {i}; |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
// 验证faces数组的大小
|
|
|
|
|
size_t numFaces = scene.mesh.faces.size(); |
|
|
|
|
size_t numVertices = scene.mesh.vertices.size(); |
|
|
|
|
size_t numFaceTexcoords = scene.mesh.faceTexcoords.size(); |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Number of faces: %zu", numFaces); |
|
|
|
|
DEBUG_EXTRA("Number of vertices: %zu", numVertices); |
|
|
|
|
DEBUG_EXTRA("Number of texture coordinates: %zu", numFaceTexcoords); |
|
|
|
|
|
|
|
|
|
// 关键检查:必须有纹理坐标才能使用此方法
|
|
|
|
|
if (numFaceTexcoords == 0) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Mesh has no texture coordinates. Cannot create virtual faces for existing UV."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (numFaceTexcoords < numFaces * 3) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Not enough UV coordinates! Expected %zu (faces * 3), have %zu", |
|
|
|
|
numFaces * 3, numFaceTexcoords); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 验证面片索引
|
|
|
|
|
DEBUG_EXTRA("Validating face indices..."); |
|
|
|
|
for (size_t i = 0; i < std::min(numFaces, static_cast<size_t>(10)); ++i) { // 使用static_cast
|
|
|
|
|
const Mesh::Face& face = scene.mesh.faces[i]; |
|
|
|
|
bool valid = true; |
|
|
|
|
for (int j = 0; j < 3; ++j) { |
|
|
|
|
if (face[j] >= numVertices) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Face %zu has invalid vertex index %u (max: %zu)", |
|
|
|
|
i, face[j], numVertices - 1); |
|
|
|
|
valid = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (valid) { |
|
|
|
|
DEBUG_EXTRA("Face %zu: vertices [%u, %u, %u] - OK", |
|
|
|
|
i, face[0], face[1], face[2]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 验证UV坐标索引
|
|
|
|
|
if (numFaceTexcoords > 0) { |
|
|
|
|
DEBUG_EXTRA("Validating UV coordinate indices..."); |
|
|
|
|
if (numFaceTexcoords < numFaces * 3) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Not enough UV coordinates! Expected %zu, have %zu", |
|
|
|
|
numFaces * 3, numFaceTexcoords); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 1. 确保网格拓扑已计算
|
|
|
|
|
DEBUG_EXTRA("Computing mesh topology..."); |
|
|
|
|
if (scene.mesh.faceFaces.empty()) { |
|
|
|
|
DEBUG_EXTRA(" Computing incident faces..."); |
|
|
|
|
try { |
|
|
|
|
scene.mesh.ListIncidenteFaces(); |
|
|
|
|
scene.mesh.ListIncidenteFaceFaces(); |
|
|
|
|
DEBUG_EXTRA(" Done computing incident faces"); |
|
|
|
|
} catch (const std::exception& e) { |
|
|
|
|
DEBUG_EXTRA(" ERROR computing incident faces: %s", e.what()); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (scene.mesh.faceNormals.empty()) { |
|
|
|
|
DEBUG_EXTRA(" Computing face normals..."); |
|
|
|
|
try { |
|
|
|
|
scene.mesh.ComputeNormalFaces(); |
|
|
|
|
DEBUG_EXTRA(" Done computing face normals"); |
|
|
|
|
} catch (const std::exception& e) { |
|
|
|
|
DEBUG_EXTRA(" ERROR computing face normals: %s", e.what()); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::vector<bool> processedFaces(numFaces, false); |
|
|
|
|
|
|
|
|
|
// 3. 基于曲率分割网格
|
|
|
|
|
DEBUG_EXTRA("Segmenting mesh based on curvature..."); |
|
|
|
|
Mesh::FaceIdxArr regionMap; |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
scene.SegmentMeshBasedOnCurvature(regionMap, 0.2f); |
|
|
|
|
DEBUG_EXTRA("Mesh segmentation completed, regionMap size: %zu", regionMap.size()); |
|
|
|
|
|
|
|
|
|
if (regionMap.size() != numFaces) { |
|
|
|
|
DEBUG_EXTRA("ERROR: regionMap size (%zu) doesn't match number of faces (%u)", |
|
|
|
|
regionMap.size(), numFaces); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} catch (const std::exception& e) { |
|
|
|
|
DEBUG_EXTRA("ERROR during mesh segmentation: %s", e.what()); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 4. 统计每个区域的面积
|
|
|
|
|
DEBUG_EXTRA("Calculating region areas..."); |
|
|
|
|
std::unordered_map<int, float> regionAreas; |
|
|
|
|
|
|
|
|
|
for (FIndex fid = 0; fid < numFaces; ++fid) { |
|
|
|
|
if (fid % 10000 == 0 && fid > 0) { |
|
|
|
|
DEBUG_EXTRA(" Processed %u/%u faces", fid, numFaces); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int region = regionMap[fid]; |
|
|
|
|
|
|
|
|
|
// 验证面片索引
|
|
|
|
|
if (fid >= scene.mesh.faces.size()) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Face index %u out of bounds (mesh has %zu faces)", |
|
|
|
|
fid, scene.mesh.faces.size()); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const Mesh::Face& face = scene.mesh.faces[fid]; |
|
|
|
|
|
|
|
|
|
// 验证顶点索引
|
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
if (face[i] >= scene.mesh.vertices.size()) { |
|
|
|
|
DEBUG_EXTRA("ERROR: Vertex index %u out of bounds in face %u (mesh has %zu vertices)", |
|
|
|
|
face[i], fid, scene.mesh.vertices.size()); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const Point3f& v0 = scene.mesh.vertices[face[0]]; |
|
|
|
|
const Point3f& v1 = scene.mesh.vertices[face[1]]; |
|
|
|
|
const Point3f& v2 = scene.mesh.vertices[face[2]]; |
|
|
|
|
|
|
|
|
|
// 计算三角形面积
|
|
|
|
|
Point3f edge1 = v1 - v0; |
|
|
|
|
Point3f edge2 = v2 - v0; |
|
|
|
|
|
|
|
|
|
Point3f crossProd( |
|
|
|
|
edge1.y * edge2.z - edge1.z * edge2.y, |
|
|
|
|
edge1.z * edge2.x - edge1.x * edge2.z, |
|
|
|
|
edge1.x * edge2.y - edge1.y * edge2.x |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
float area = 0.5f * std::sqrt( |
|
|
|
|
crossProd.x * crossProd.x + |
|
|
|
|
crossProd.y * crossProd.y + |
|
|
|
|
crossProd.z * crossProd.z |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (area < 0) { |
|
|
|
|
DEBUG_EXTRA("WARNING: Negative area calculated for face %u: %f", fid, area); |
|
|
|
|
area = 0.0f; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
regionAreas[region] += area; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Region area calculation completed, found %zu regions", regionAreas.size()); |
|
|
|
|
|
|
|
|
|
// 5. 为每个区域收集面片
|
|
|
|
|
DEBUG_EXTRA("Grouping faces by region..."); |
|
|
|
|
std::unordered_map<int, std::vector<FIndex>> regionFaces; |
|
|
|
|
for (FIndex fid = 0; fid < numFaces; ++fid) { |
|
|
|
|
int region = regionMap[fid]; |
|
|
|
|
regionFaces[region].push_back(fid); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Found %zu regions with faces", regionFaces.size()); |
|
|
|
|
|
|
|
|
|
// 6. 创建虚拟面
|
|
|
|
|
DEBUG_EXTRA("Creating virtual faces..."); |
|
|
|
|
virtualFaceMap.clear(); |
|
|
|
|
virtualFaceMap.reserve(regionFaces.size()); |
|
|
|
|
|
|
|
|
|
int regionCount = 0; |
|
|
|
|
int smallRegionCount = 0; |
|
|
|
|
int uvDiscontinuousCount = 0; |
|
|
|
|
int createdVirtualFaces = 0; |
|
|
|
|
|
|
|
|
|
for (const auto& region : regionFaces) { |
|
|
|
|
regionCount++; |
|
|
|
|
int regionID = region.first; |
|
|
|
|
const std::vector<FIndex>& faceList = region.second; |
|
|
|
|
|
|
|
|
|
if (regionCount % 100 == 0) { |
|
|
|
|
DEBUG_EXTRA(" Processing region %d/%d (%zu faces)", regionCount, regionFaces.size(), faceList.size()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 检查区域面积是否足够大
|
|
|
|
|
float regionArea = regionAreas[regionID]; |
|
|
|
|
if (regionArea < 0.001f) { // 面积阈值
|
|
|
|
|
DEBUG_EXTRA(" Region %d is too small (area: %f), using individual faces", regionID, regionArea); |
|
|
|
|
smallRegionCount++; |
|
|
|
|
|
|
|
|
|
// 区域太小,不创建虚拟面
|
|
|
|
|
for (FIndex fid : faceList) { |
|
|
|
|
virtualFaceMap.push_back(VirtualFace()); |
|
|
|
|
virtualFaceMap.back().faces.push_back(fid); |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 检查UV连续性
|
|
|
|
|
if (!CheckUVContinuity(faceList)) { |
|
|
|
|
DEBUG_EXTRA(" Region %d has discontinuous UV, using individual faces", regionID); |
|
|
|
|
uvDiscontinuousCount++; |
|
|
|
|
|
|
|
|
|
// UV不连续,保持原始面片
|
|
|
|
|
for (FIndex fid : faceList) { |
|
|
|
|
virtualFaceMap.push_back(VirtualFace()); |
|
|
|
|
virtualFaceMap.back().faces.push_back(fid); |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 创建虚拟面
|
|
|
|
|
VirtualFace vf; |
|
|
|
|
vf.faces = faceList; |
|
|
|
|
|
|
|
|
|
// 计算虚拟面的中心、法线和面积
|
|
|
|
|
if (!CalculateVirtualFaceProperties(vf)) { |
|
|
|
|
DEBUG_EXTRA(" Failed to calculate properties for virtual face in region %d", regionID); |
|
|
|
|
// 计算失败,回退到原始面片
|
|
|
|
|
for (FIndex fid : faceList) { |
|
|
|
|
virtualFaceMap.push_back(VirtualFace()); |
|
|
|
|
virtualFaceMap.back().faces.push_back(fid); |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 计算虚拟面的UV边界
|
|
|
|
|
if (!CalculateVirtualFaceUVBounds(vf)) { |
|
|
|
|
DEBUG_EXTRA(" Failed to calculate UV bounds for virtual face in region %d", regionID); |
|
|
|
|
// 计算失败,回退到原始面片
|
|
|
|
|
for (FIndex fid : faceList) { |
|
|
|
|
virtualFaceMap.push_back(VirtualFace()); |
|
|
|
|
virtualFaceMap.back().faces.push_back(fid); |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
virtualFaceMap.push_back(vf); |
|
|
|
|
createdVirtualFaces++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Virtual face creation completed:"); |
|
|
|
|
DEBUG_EXTRA(" Total regions: %zu", regionFaces.size()); |
|
|
|
|
DEBUG_EXTRA(" Small regions (area < 0.001): %d", smallRegionCount); |
|
|
|
|
DEBUG_EXTRA(" UV discontinuous regions: %d", uvDiscontinuousCount); |
|
|
|
|
DEBUG_EXTRA(" Created virtual faces: %d", createdVirtualFaces); |
|
|
|
|
DEBUG_EXTRA(" Total virtual faces (including individual faces): %zu", virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
if (virtualFaceMap.empty()) { |
|
|
|
|
DEBUG_EXTRA("ERROR: No virtual faces created!"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Created %zu virtual faces (one per triangle)", virtualFaceMap.size()); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -18813,49 +18638,48 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
@@ -18813,49 +18638,48 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅✅✅ 关键修复:重新计算拓扑(否则 faceFaces 是坏的)
|
|
|
|
|
// ✅ 确保拓扑信息存在
|
|
|
|
|
if (mesh.faceFaces.empty()) { |
|
|
|
|
mesh.ListIncidenteFaces(); |
|
|
|
|
mesh.ListIncidenteFaceFaces(); |
|
|
|
|
mesh.ComputeNormalFaces(); |
|
|
|
|
mesh.ListBoundaryVertices(); |
|
|
|
|
|
|
|
|
|
// 纯可见性计算(初始化faceNeighbors)
|
|
|
|
|
if (!texture.ComputePureFaceVisibility(fOutlierThreshold, nIgnoreMaskLabel, views)) { |
|
|
|
|
// ❗ 兜底:如果ComputePureFaceVisibility没初始化faceNeighbors,手动填充
|
|
|
|
|
if (texture.faceNeighbors.empty()) { |
|
|
|
|
texture.faceNeighbors.resize(mesh.faces.size()); |
|
|
|
|
for (FIndex fid = 0; fid < (FIndex)mesh.faces.size(); ++fid) { |
|
|
|
|
if (!views.empty()) { |
|
|
|
|
texture.faceNeighbors[fid].insert(texture.faceNeighbors[fid].end(), views.begin(), views.end()); |
|
|
|
|
} else { |
|
|
|
|
for (IIndex vid = 0; vid < (IIndex)images.size(); ++vid) |
|
|
|
|
texture.faceNeighbors[fid].push_back(vid); |
|
|
|
|
} |
|
|
|
|
if (mesh.faceNormals.empty()) { |
|
|
|
|
mesh.ComputeNormalFaces(); |
|
|
|
|
} |
|
|
|
|
DEBUG_EXTRA("Forced fill faceNeighbors for %zu faces", mesh.faces.size()); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
if (mesh.vertexBoundary.empty()) { |
|
|
|
|
mesh.ListBoundaryVertices(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 创建虚拟面(1:1映射)
|
|
|
|
|
// ✅ 1. 创建虚拟面(每三角形一个)
|
|
|
|
|
MeshTexture::VirtualFaceMap virtualFaceMap; |
|
|
|
|
if (!texture.CreateVirtualFacesForExistingUV(virtualFaceMap)) return false; |
|
|
|
|
if (!texture.CreateVirtualFacesForExistingUV(virtualFaceMap)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
// ✅ 2. 计算可见性(填充 faceNeighbors)
|
|
|
|
|
if (!texture.ComputePureFaceVisibility( |
|
|
|
|
fOutlierThreshold, nIgnoreMaskLabel, views)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 选最佳视图(带patch一致性)
|
|
|
|
|
// ✅ 3. 选择最佳视图(带 patch 一致性)
|
|
|
|
|
if (!texture.SelectBestViewsForVirtualFaces( |
|
|
|
|
virtualFaceMap, 1, fOutlierThreshold, fRatioDataSmoothness, nIgnoreMaskLabel, views)) |
|
|
|
|
virtualFaceMap, 1, fOutlierThreshold, |
|
|
|
|
fRatioDataSmoothness, nIgnoreMaskLabel, views)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
// 直接光栅化(不需要生成图集)
|
|
|
|
|
// ✅ 4. RC 风格光栅化(Affine + Homography 混合)
|
|
|
|
|
Mesh::Image8U3Arr textures; |
|
|
|
|
if (!texture.RasterizeVirtualFaces( |
|
|
|
|
virtualFaceMap, texture.faceViews, texture.faceViewWeights, |
|
|
|
|
virtualFaceMap, texture.faceViews, |
|
|
|
|
texture.faceViewWeights, |
|
|
|
|
nTextureSizeMultiple, colEmpty, textures)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
mesh.texturesDiffuse = std::move(textures); |
|
|
|
|
DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
|
return true; // 直接返回,不走后面的图集逻辑
|
|
|
|
|
DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)", |
|
|
|
|
mesh.faces.size(), TD_TIMER_GET_FMT().c_str()); |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
// 3. 通用虚拟面模式(无预计算UV)
|
|
|
|
|
if (!texture.FaceViewSelectionWithVirtualFaces( |
|
|
|
|
|