|
|
|
|
@ -492,6 +492,16 @@ public:
@@ -492,6 +492,16 @@ public:
|
|
|
|
|
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数
|
|
|
|
|
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
|
|
|
|
|
); |
|
|
|
|
void CheckColorChannels(const Image8U3& texture, const std::string& name); |
|
|
|
|
Mesh::Image8U3Arr GenerateTextureAtlasWith3DBridge( |
|
|
|
|
const LabelArr& faceLabels, |
|
|
|
|
const IIndexArr& views, |
|
|
|
|
const Mesh::Image8U3Arr& sourceTextures, |
|
|
|
|
const Mesh::TexCoordArr& sourceTexcoords, |
|
|
|
|
const Mesh::TexIndexArr& sourceTexindices, |
|
|
|
|
unsigned nTextureSizeMultiple, |
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
float fSharpnessWeight); |
|
|
|
|
Mesh::Image8U3Arr GenerateTextureAtlasFromUV( |
|
|
|
|
const Mesh::Image8U3Arr& sourceTextures, // 已有纹理数组
|
|
|
|
|
const Mesh::TexCoordArr& sourceTexcoords, // 已有UV坐标
|
|
|
|
|
@ -9945,6 +9955,26 @@ bool SaveGeneratedTextures(const Mesh::Image8U3Arr& generatedTextures, const std
@@ -9945,6 +9955,26 @@ bool SaveGeneratedTextures(const Mesh::Image8U3Arr& generatedTextures, const std
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
void MeshTexture::CheckColorChannels(const Image8U3& texture, const std::string& name) { |
|
|
|
|
if (texture.empty()) { |
|
|
|
|
DEBUG_EXTRA("%s: 纹理为空", name.c_str()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 检查左上角几个像素的颜色
|
|
|
|
|
for (int y = 0; y < std::min(3, texture.rows); ++y) { |
|
|
|
|
for (int x = 0; x < std::min(3, texture.cols); ++x) { |
|
|
|
|
const cv::Vec3b& pixel = texture.at<cv::Vec3b>(y, x); |
|
|
|
|
DEBUG_EXTRA("%s[%d,%d]: B=%d, G=%d, R=%d", |
|
|
|
|
name.c_str(), x, y, pixel[0], pixel[1], pixel[2]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 计算平均颜色
|
|
|
|
|
cv::Scalar mean = cv::mean(texture); |
|
|
|
|
DEBUG_EXTRA("%s 平均颜色: B=%.1f, G=%.1f, R=%.1f", |
|
|
|
|
name.c_str(), mean[0], mean[1], mean[2]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool MeshTexture::TextureWithExistingUV( |
|
|
|
|
const IIndexArr& views, |
|
|
|
|
@ -9953,12 +9983,11 @@ bool MeshTexture::TextureWithExistingUV(
@@ -9953,12 +9983,11 @@ bool MeshTexture::TextureWithExistingUV(
|
|
|
|
|
unsigned nTextureSizeMultiple, |
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
float fSharpnessWeight, |
|
|
|
|
const Mesh::Image8U3Arr& existingTextures, // 添加已有纹理参数
|
|
|
|
|
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数
|
|
|
|
|
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
|
|
|
|
|
) |
|
|
|
|
const Mesh::Image8U3Arr& existingTextures, |
|
|
|
|
const Mesh::TexCoordArr& existingTexcoords, |
|
|
|
|
const Mesh::TexIndexArr& existingTexindices) |
|
|
|
|
{ |
|
|
|
|
DEBUG_EXTRA("TextureWithExistingUV - Using existing texture data"); |
|
|
|
|
DEBUG_EXTRA("TextureWithExistingUV - 使用3D几何坐标作为桥梁"); |
|
|
|
|
TD_TIMER_START(); |
|
|
|
|
|
|
|
|
|
// 1. 验证输入
|
|
|
|
|
@ -9967,38 +9996,64 @@ bool MeshTexture::TextureWithExistingUV(
@@ -9967,38 +9996,64 @@ bool MeshTexture::TextureWithExistingUV(
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (existingTextures.empty() || existingTexcoords.empty()) { |
|
|
|
|
if (existingTextures.empty()) { |
|
|
|
|
VERBOSE("error: no existing texture data provided"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 验证UV坐标数量匹配
|
|
|
|
|
if (existingTexcoords.size() != scene.mesh.faceTexcoords.size()) { |
|
|
|
|
VERBOSE("error: UV coordinate count mismatch: existing=%zu, mesh=%zu", |
|
|
|
|
existingTexcoords.size(), scene.mesh.faceTexcoords.size()); |
|
|
|
|
DEBUG_EXTRA("Processing %zu faces with existing texture data", scene.mesh.faces.size()); |
|
|
|
|
|
|
|
|
|
// 2. 为每个面选择最佳视图(从原始图像,而不是已有纹理)
|
|
|
|
|
FaceDataViewArr facesDatas; |
|
|
|
|
if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Processing %zu faces with existing texture data", scene.mesh.faces.size()); |
|
|
|
|
// 3. 为每个面分配最佳视图
|
|
|
|
|
LabelArr faceLabels(scene.mesh.faces.size()); |
|
|
|
|
FOREACH(idxFace, scene.mesh.faces) { |
|
|
|
|
const FaceDataArr& faceDatas = facesDatas[idxFace]; |
|
|
|
|
if (faceDatas.empty()) { |
|
|
|
|
faceLabels[idxFace] = 0; // 无视图可用
|
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 选择质量最高的视图
|
|
|
|
|
float bestQuality = -1; |
|
|
|
|
IIndex bestView = NO_ID; |
|
|
|
|
for (const FaceData& data : faceDatas) { |
|
|
|
|
if (data.quality > bestQuality && !data.bInvalidFacesRelative) { |
|
|
|
|
bestQuality = data.quality; |
|
|
|
|
bestView = data.idxView; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 2. 生成纹理图集(从已有纹理采样,但使用模型原始UV)
|
|
|
|
|
Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasFromUV( |
|
|
|
|
existingTextures, |
|
|
|
|
existingTexcoords, |
|
|
|
|
existingTexindices, |
|
|
|
|
nTextureSizeMultiple, |
|
|
|
|
colEmpty, |
|
|
|
|
fSharpnessWeight |
|
|
|
|
faceLabels[idxFace] = (bestView != NO_ID) ? (bestView + 1) : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 4. 生成纹理图集
|
|
|
|
|
Mesh::Image8U3Arr generatedTextures = GenerateTextureAtlasWith3DBridge( |
|
|
|
|
faceLabels, views, existingTextures, existingTexcoords, existingTexindices, |
|
|
|
|
nTextureSizeMultiple, colEmpty, fSharpnessWeight |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!generatedTextures.empty()) { |
|
|
|
|
// 检查颜色通道
|
|
|
|
|
CheckColorChannels(generatedTextures[0], "生成的纹理"); |
|
|
|
|
|
|
|
|
|
// 检查源纹理颜色通道
|
|
|
|
|
if (!existingTextures.empty()) { |
|
|
|
|
CheckColorChannels(existingTextures[0], "源纹理"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 保存纹理
|
|
|
|
|
scene.mesh.texturesDiffuse = std::move(generatedTextures); |
|
|
|
|
|
|
|
|
|
// 设置面的纹理索引
|
|
|
|
|
if (scene.mesh.texturesDiffuse.size() > 1) { |
|
|
|
|
scene.mesh.faceTexindices.resize(scene.mesh.faces.size()); |
|
|
|
|
for (size_t i = 0; i < scene.mesh.faces.size(); ++i) { |
|
|
|
|
scene.mesh.faceTexindices[i] = 0; // 所有面使用第一个纹理
|
|
|
|
|
scene.mesh.faceTexindices[i] = 0; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
scene.mesh.faceTexindices.Release(); |
|
|
|
|
@ -10006,7 +10061,6 @@ bool MeshTexture::TextureWithExistingUV(
@@ -10006,7 +10061,6 @@ bool MeshTexture::TextureWithExistingUV(
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Generated %zu textures from existing data", |
|
|
|
|
scene.mesh.texturesDiffuse.size()); |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -10014,6 +10068,221 @@ bool MeshTexture::TextureWithExistingUV(
@@ -10014,6 +10068,221 @@ bool MeshTexture::TextureWithExistingUV(
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Mesh::Image8U3Arr MeshTexture::GenerateTextureAtlasWith3DBridge( |
|
|
|
|
const LabelArr& faceLabels, |
|
|
|
|
const IIndexArr& views, |
|
|
|
|
const Mesh::Image8U3Arr& sourceTextures, |
|
|
|
|
const Mesh::TexCoordArr& sourceTexcoords, |
|
|
|
|
const Mesh::TexIndexArr& sourceTexindices, |
|
|
|
|
unsigned nTextureSizeMultiple, |
|
|
|
|
Pixel8U colEmpty, |
|
|
|
|
float fSharpnessWeight) |
|
|
|
|
{ |
|
|
|
|
DEBUG_EXTRA("GenerateTextureAtlasWith3DBridge - 使用3D几何坐标作为桥梁"); |
|
|
|
|
|
|
|
|
|
// 1. 分析外部UV布局
|
|
|
|
|
AABB2f uvBounds(true); |
|
|
|
|
FOREACH(i, scene.mesh.faceTexcoords) { |
|
|
|
|
const TexCoord& uv = scene.mesh.faceTexcoords[i]; |
|
|
|
|
uvBounds.InsertFull(uv); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 确保UV在[0,1]范围内
|
|
|
|
|
if (uvBounds.ptMin.x() < 0 || uvBounds.ptMin.y() < 0 || |
|
|
|
|
uvBounds.ptMax.x() > 1 || uvBounds.ptMax.y() > 1) { |
|
|
|
|
DEBUG_EXTRA("UV超出[0,1]范围,进行归一化"); |
|
|
|
|
uvBounds = AABB2f(true); |
|
|
|
|
for (size_t i = 0; i < scene.mesh.faceTexcoords.size(); i += 3) { |
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
|
const TexCoord& uv = scene.mesh.faceTexcoords[i + v]; |
|
|
|
|
uvBounds.InsertFull(uv); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 计算纹理尺寸
|
|
|
|
|
const float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); |
|
|
|
|
const float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); |
|
|
|
|
const int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); |
|
|
|
|
|
|
|
|
|
// 创建目标纹理
|
|
|
|
|
Mesh::Image8U3Arr textures; |
|
|
|
|
Image8U3& textureAtlas = textures.emplace_back(textureSize, textureSize); |
|
|
|
|
// 注意:cv::Scalar 使用 BGR 顺序
|
|
|
|
|
textureAtlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("生成纹理图集: 尺寸=%dx%d, UV范围=[%.3f,%.3f]-[%.3f,%.3f]", |
|
|
|
|
textureSize, textureSize, |
|
|
|
|
uvBounds.ptMin.x(), uvBounds.ptMin.y(), |
|
|
|
|
uvBounds.ptMax.x(), uvBounds.ptMax.y()); |
|
|
|
|
|
|
|
|
|
// 添加调试信息
|
|
|
|
|
DEBUG_EXTRA("colEmpty: R=%d, G=%d, B=%d", colEmpty.r, colEmpty.g, colEmpty.b); |
|
|
|
|
DEBUG_EXTRA("OpenCV图像通道顺序: BGR"); |
|
|
|
|
|
|
|
|
|
// 检查颜色通道顺序
|
|
|
|
|
if (!sourceTextures.empty()) { |
|
|
|
|
const Image8U3& firstTex = sourceTextures[0]; |
|
|
|
|
cv::Vec3b firstPixel = firstTex.at<cv::Vec3b>(0, 0); |
|
|
|
|
DEBUG_EXTRA("源纹理第一个像素: B=%d, G=%d, R=%d", |
|
|
|
|
firstPixel[0], firstPixel[1], firstPixel[2]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 3. 统计信息
|
|
|
|
|
int processedFaces = 0; |
|
|
|
|
int sampledPixels = 0; |
|
|
|
|
int failedFaces = 0; |
|
|
|
|
|
|
|
|
|
// 4. 为每个面采样
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
#pragma omp parallel for schedule(dynamic) reduction(+:processedFaces, sampledPixels, failedFaces) |
|
|
|
|
#endif |
|
|
|
|
for (int_t idxFace = 0; idxFace < (int_t)scene.mesh.faces.size(); ++idxFace) { |
|
|
|
|
const FIndex faceID = (FIndex)idxFace; |
|
|
|
|
const Label label = faceLabels[faceID]; |
|
|
|
|
|
|
|
|
|
if (label == 0) { |
|
|
|
|
failedFaces++; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const IIndex idxView = label - 1; |
|
|
|
|
if (idxView >= images.size()) { |
|
|
|
|
failedFaces++; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 获取面的几何信息
|
|
|
|
|
const TexCoord* meshUVs = &scene.mesh.faceTexcoords[faceID * 3]; |
|
|
|
|
const Face& face = scene.mesh.faces[faceID]; |
|
|
|
|
|
|
|
|
|
// 获取源纹理信息
|
|
|
|
|
const TexCoord* srcUVs = &sourceTexcoords[faceID * 3]; |
|
|
|
|
const TexIndex textureIdx = sourceTexindices.empty() ? 0 : sourceTexindices[faceID]; |
|
|
|
|
|
|
|
|
|
if (textureIdx >= sourceTextures.size()) { |
|
|
|
|
failedFaces++; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const Image8U3& sourceTexture = sourceTextures[textureIdx]; |
|
|
|
|
|
|
|
|
|
// 计算面的UV边界
|
|
|
|
|
AABB2f faceBounds(true); |
|
|
|
|
for (int i = 0; i < 3; ++i) { |
|
|
|
|
faceBounds.InsertFull(meshUVs[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 转换为像素坐标
|
|
|
|
|
int startX = (int)(faceBounds.ptMin.x() * textureSize); |
|
|
|
|
int startY = (int)(faceBounds.ptMin.y() * textureSize); |
|
|
|
|
int endX = (int)(faceBounds.ptMax.x() * textureSize); |
|
|
|
|
int endY = (int)(faceBounds.ptMax.y() * textureSize); |
|
|
|
|
|
|
|
|
|
// 边界检查
|
|
|
|
|
startX = std::max(0, std::min(startX, textureSize - 1)); |
|
|
|
|
startY = std::max(0, std::min(startY, textureSize - 1)); |
|
|
|
|
endX = std::max(0, std::min(endX, textureSize - 1)); |
|
|
|
|
endY = std::max(0, std::min(endY, textureSize - 1)); |
|
|
|
|
|
|
|
|
|
if (startX >= endX || startY >= endY) { |
|
|
|
|
failedFaces++; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int faceSampledPixels = 0; |
|
|
|
|
|
|
|
|
|
// 采样纹理
|
|
|
|
|
for (int y = startY; y <= endY; ++y) { |
|
|
|
|
for (int x = startX; x <= endX; ++x) { |
|
|
|
|
const Point2f texCoord((x + 0.5f) / textureSize, (y + 0.5f) / textureSize); |
|
|
|
|
|
|
|
|
|
// 计算重心坐标
|
|
|
|
|
Point3f barycentric; |
|
|
|
|
if (!PointInTriangle(texCoord, meshUVs[0], meshUVs[1], meshUVs[2], barycentric)) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 计算3D点
|
|
|
|
|
const Vertex worldPoint = |
|
|
|
|
vertices[face[0]] * barycentric.x + |
|
|
|
|
vertices[face[1]] * barycentric.y + |
|
|
|
|
vertices[face[2]] * barycentric.z; |
|
|
|
|
|
|
|
|
|
// 方案A:从原始图像采样
|
|
|
|
|
Point2f imgPoint = ProjectPointWithAutoCorrection(images[idxView].camera, worldPoint, images[idxView]); |
|
|
|
|
|
|
|
|
|
if (imgPoint.x < 0 || imgPoint.x >= images[idxView].image.cols || |
|
|
|
|
imgPoint.y < 0 || imgPoint.y >= images[idxView].image.rows) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 修正:使用双线性插值采样
|
|
|
|
|
const int x0 = (int)floor(imgPoint.x); |
|
|
|
|
const int y0 = (int)floor(imgPoint.y); |
|
|
|
|
const int x1 = std::min(x0 + 1, images[idxView].image.cols - 1); |
|
|
|
|
const int y1 = std::min(y0 + 1, images[idxView].image.rows - 1); |
|
|
|
|
|
|
|
|
|
const float fx = imgPoint.x - x0; |
|
|
|
|
const float fy = imgPoint.y - y0; |
|
|
|
|
const float fx1 = 1.0f - fx; |
|
|
|
|
const float fy1 = 1.0f - fy; |
|
|
|
|
|
|
|
|
|
// 采样四个点的颜色
|
|
|
|
|
const cv::Vec3b& c00 = images[idxView].image.at<cv::Vec3b>(y0, x0); |
|
|
|
|
const cv::Vec3b& c01 = images[idxView].image.at<cv::Vec3b>(y0, x1); |
|
|
|
|
const cv::Vec3b& c10 = images[idxView].image.at<cv::Vec3b>(y1, x0); |
|
|
|
|
const cv::Vec3b& c11 = images[idxView].image.at<cv::Vec3b>(y1, x1); |
|
|
|
|
|
|
|
|
|
// 双线性插值
|
|
|
|
|
const cv::Vec3b color = |
|
|
|
|
c00 * (fx1 * fy1) + |
|
|
|
|
c01 * (fx * fy1) + |
|
|
|
|
c10 * (fx1 * fy) + |
|
|
|
|
c11 * (fx * fy); |
|
|
|
|
|
|
|
|
|
// 注意:OpenCV是BGR顺序,而Pixel8U通常是RGB顺序
|
|
|
|
|
// 这里我们需要确保颜色通道正确
|
|
|
|
|
#ifdef _USE_OPENMP |
|
|
|
|
#pragma omp critical |
|
|
|
|
#endif |
|
|
|
|
{ |
|
|
|
|
// 方案1:直接使用BGR顺序(OpenCV默认)
|
|
|
|
|
// textureAtlas.at<cv::Vec3b>(y, x) = color;
|
|
|
|
|
|
|
|
|
|
// 方案2:交换B和R通道(如果颜色偏蓝,说明B和R反了)
|
|
|
|
|
// 将BGR转换为RGB
|
|
|
|
|
textureAtlas.at<cv::Vec3b>(y, x) = cv::Vec3b(color[2], color[1], color[0]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
faceSampledPixels++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (faceSampledPixels > 0) { |
|
|
|
|
processedFaces++; |
|
|
|
|
sampledPixels += faceSampledPixels; |
|
|
|
|
} else { |
|
|
|
|
failedFaces++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("纹理采样完成: 成功 %d 个面, 失败 %d 个面, 采样 %d 像素", |
|
|
|
|
processedFaces, failedFaces, sampledPixels); |
|
|
|
|
|
|
|
|
|
// 5. 填充空洞
|
|
|
|
|
if (processedFaces > 0 && sampledPixels > 0) { |
|
|
|
|
// FillTextureGaps(textureAtlas, scene.mesh.faceTexcoords, (FIndex)scene.mesh.faces.size(), textureSize, colEmpty);
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 6. 锐化处理
|
|
|
|
|
if (fSharpnessWeight > 0) { |
|
|
|
|
// ApplySharpening(textureAtlas, fSharpnessWeight);
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return textures; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Point2f MeshTexture::ProjectPointWithAutoCorrection(const Camera& camera, const Vertex& worldPoint, const Image& sourceImage) { |
|
|
|
|
Point2f imgPoint = camera.ProjectPointP(worldPoint); |
|
|
|
|
|
|
|
|
|
@ -11171,41 +11440,52 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
@@ -11171,41 +11440,52 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
|
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("TextureMesh %b, %s", bUseExistingUV, strUVMeshFileName.c_str()); |
|
|
|
|
if (bUseExistingUV && !strUVMeshFileName.empty()) { |
|
|
|
|
// 1. 生成临时纹理和UV
|
|
|
|
|
Mesh::TexCoordArr existingTexcoords; |
|
|
|
|
Mesh::TexIndexArr existingTexindices; |
|
|
|
|
|
|
|
|
|
texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, baseFileName, bOriginFaceview, this, existingTexcoords, existingTexindices); |
|
|
|
|
texture.GenerateTextureForUV(bGlobalSeamLeveling, bLocalSeamLeveling, nTextureSizeMultiple, |
|
|
|
|
nRectPackingHeuristic, colEmpty, fSharpnessWeight, maxTextureSize, |
|
|
|
|
baseFileName, bOriginFaceview, this, existingTexcoords, existingTexindices); |
|
|
|
|
|
|
|
|
|
// 保存生成的纹理数据
|
|
|
|
|
Mesh::Image8U3Arr existingTextures = texture.texturesDiffuseTemp; |
|
|
|
|
// Mesh::TexCoordArr existingTexcoords = mesh.faceTexcoords; // 保存临时计算的UV
|
|
|
|
|
// Mesh::TexIndexArr existingTexindices = mesh.faceTexindices; // 保存纹理索引
|
|
|
|
|
|
|
|
|
|
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; // 注意:在成员函数中,返回 false 表示失败
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 2. 使用预计算UV模式加载网格
|
|
|
|
|
if (!mesh.Load(MAKE_PATH_SAFE(strUVMeshFileName), true)) { |
|
|
|
|
VERBOSE("error: cannot load mesh file with UV coordinates"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VERBOSE("2faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); |
|
|
|
|
// mesh.CheckUVValid();
|
|
|
|
|
//*
|
|
|
|
|
// 确保网格包含UV坐标
|
|
|
|
|
if (mesh.faceTexcoords.empty()) { |
|
|
|
|
VERBOSE("error: the specified mesh does not contain UV coordinates"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 使用新的纹理生成方法
|
|
|
|
|
MeshTexture texture2(*this, nResolutionLevel, nMinResolution); |
|
|
|
|
if (!texture2.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold, nTextureSizeMultiple, colEmpty, fSharpnessWeight, existingTextures, existingTexcoords, existingTexindices)){ |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
//*/
|
|
|
|
|
if (mesh.faceTexcoords.empty()) { |
|
|
|
|
VERBOSE("error: the specified mesh does not contain UV coordinates"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
// 3. 使用新的纹理生成方法
|
|
|
|
|
MeshTexture texture2(*this, nResolutionLevel, nMinResolution); |
|
|
|
|
|
|
|
|
|
// 关键:确保几何信息正确
|
|
|
|
|
texture2.scene.mesh.vertices = mesh.vertices; |
|
|
|
|
texture2.scene.mesh.faces = mesh.faces; |
|
|
|
|
texture2.scene.mesh.faceTexcoords = mesh.faceTexcoords; // 使用外部UV
|
|
|
|
|
|
|
|
|
|
if (!texture2.TextureWithExistingUV(views, nIgnoreMaskLabel, fOutlierThreshold, |
|
|
|
|
nTextureSizeMultiple, colEmpty, fSharpnessWeight, |
|
|
|
|
existingTextures, existingTexcoords, existingTexindices)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 4. 将生成的纹理赋值回场景网格
|
|
|
|
|
mesh.texturesDiffuse = std::move(texture2.scene.mesh.texturesDiffuse); |
|
|
|
|
mesh.faceTexindices = std::move(texture2.scene.mesh.faceTexindices); |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// mesh.CheckUVValid();
|
|
|
|
|
|
|
|
|
|
|