2021SC@SDUSC
auto drawBySolubility = [&](const QUuid &partId, size_t triangleIndex, size_t firstVertexIndex, size_t secondVertexIndex,
const QUuid &neighborPartId) {
const std::vector<QVector2D> &uv = triangleVertexUvs[triangleIndex];
const auto &allRects = partUvRects.find(partId);
if (allRects == partUvRects.end()) {
qDebug() << "Found part uv rects failed";
return;
}
const auto &firstPoint = uv[firstVertexIndex];
const auto &secondPoint = uv[secondVertexIndex];
auto edgeLength = firstPoint.distanceToPoint(secondPoint);
auto middlePoint = (firstPoint + secondPoint) / 2.0;
float alpha = 1.0;
const auto &findColor = partColorMap.find(partId);
if (findColor == partColorMap.end())
return;
alpha = findColor->second.alphaF();
const auto &findNeighborColorSolubility = partColorSolubilityMap.find(neighborPartId);
if (findNeighborColorSolubility == partColorSolubilityMap.end())
return;
if (qFuzzyIsNull(findNeighborColorSolubility->second))
return;
const auto &findNeighborColor = partColorMap.find(neighborPartId);
if (findNeighborColor == partColorMap.end())
return;
for (const auto &it: allRects->second) {
if (it.contains(firstPoint.x(), firstPoint.y()) ||
it.contains(secondPoint.x(), secondPoint.y())) {
float finalRadius = (it.width() + it.height()) * 0.5 * findNeighborColorSolubility->second;
if (finalRadius < edgeLength)
finalRadius = edgeLength;
QRectF fillTarget((middlePoint.x() - finalRadius),
(middlePoint.y() - finalRadius),
(finalRadius + finalRadius),
(finalRadius + finalRadius));
auto clippedRect = it.intersected(fillTarget);
QRectF translatedRect = {
clippedRect.left() * TextureGenerator::m_textureSize,
clippedRect.top() * TextureGenerator::m_textureSize,
clippedRect.width() * TextureGenerator::m_textureSize,
clippedRect.height() * TextureGenerator::m_textureSize
};
texturePainter.setOpacity(alpha);
auto findTextureResult = partColorTexturePixmaps.find(neighborPartId);
if (findTextureResult != partColorTexturePixmaps.end()) {
const auto &pixmap = findTextureResult->second.first;
const auto &rotatedPixmap = findTextureResult->second.second;
QImage tmpImage(translatedRect.width(), translatedRect.height(), QImage::Format_ARGB32);
QPixmap tmpPixmap = QPixmap::fromImage(tmpImage);
QPainter tmpPainter;
QRectF tmpImageFrame = QRectF(0, 0, translatedRect.width(), translatedRect.height());
// Fill tiled texture
tmpPainter.begin(&tmpPixmap);
tmpPainter.setOpacity(alpha);
if (it.width() < it.height()) {
tmpPainter.drawTiledPixmap(tmpImageFrame, rotatedPixmap, QPointF(translatedRect.top(), translatedRect.left()));
} else {
tmpPainter.drawTiledPixmap(tmpImageFrame, pixmap, translatedRect.topLeft());
}
tmpPainter.setOpacity(1.0);
tmpPainter.end();
// Apply gradient
QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize - translatedRect.left(),
middlePoint.y() * TextureGenerator::m_textureSize - translatedRect.top()),
finalRadius * TextureGenerator::m_textureSize);
gradient.setColorAt(0.0, findNeighborColor->second);
gradient.setColorAt(1.0, Qt::transparent);
tmpPainter.begin(&tmpPixmap);
tmpPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
tmpPainter.fillRect(tmpImageFrame, gradient);
tmpPainter.end();
texturePainter.drawPixmap(translatedRect, tmpPixmap, tmpImageFrame);
} else {
QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize,
middlePoint.y() * TextureGenerator::m_textureSize),
finalRadius * TextureGenerator::m_textureSize);
gradient.setColorAt(0.0, findNeighborColor->second);
gradient.setColorAt(1.0, Qt::transparent);
texturePainter.fillRect(translatedRect, gradient);
}
texturePainter.setOpacity(1.0);
break;
}
}
};
std::map<std::pair<size_t, size_t>, std::tuple<size_t, size_t, size_t>> halfEdgeToTriangleMap;
for (size_t i = 0; i < m_object->triangles.size(); ++i) {
const auto &triangleIndices = m_object->triangles[i];
if (triangleIndices.size() != 3) {
qDebug() << "Found invalid triangle indices";
continue;
}
for (size_t j = 0; j < triangleIndices.size(); ++j) {
size_t k = (j + 1) % triangleIndices.size();
halfEdgeToTriangleMap.insert(std::make_pair(std::make_pair(triangleIndices[j], triangleIndices[k]),
std::make_tuple(i, j, k)));
}
}
for (const auto &it: halfEdgeToTriangleMap) {
auto oppositeHalfEdge = std::make_pair(it.first.second, it.first.first);
const auto &opposite = halfEdgeToTriangleMap.find(oppositeHalfEdge);
if (opposite == halfEdgeToTriangleMap.end())
continue;
const std::pair<QUuid, QUuid> &source = triangleSourceNodes[std::get<0>(it.second)];
const std::pair<QUuid, QUuid> &oppositeSource = triangleSourceNodes[std::get<0>(opposite->second)];
if (source.first == oppositeSource.first)
continue;
drawBySolubility(source.first, std::get<0>(it.second), std::get<1>(it.second), std::get<2>(it.second), oppositeSource.first);
drawBySolubility(oppositeSource.first, std::get<0>(opposite->second), std::get<1>(opposite->second), std::get<2>(opposite->second), source.first);
}
// Draw belly white
texturePainter.setCompositionMode(QPainter::CompositionMode_SoftLight);
for (size_t triangleIndex = 0; triangleIndex < m_object->triangles.size(); ++triangleIndex) {
const auto &normal = triangleNormals[triangleIndex];
const std::pair<QUuid, QUuid> &source = triangleSourceNodes[triangleIndex];
const auto &partId = source.first;
if (m_countershadedPartIds.find(partId) == m_countershadedPartIds.end())
continue;
const auto &allRects = partUvRects.find(partId);
if (allRects == partUvRects.end()) {
qDebug() << "Found part uv rects failed";
continue;
}
const auto &findObjectNode = nodeMap.find(source);
if (findObjectNode == nodeMap.end())
continue;
const ObjectNode *objectNode = findObjectNode->second;
if (qAbs(QVector3D::dotProduct(objectNode->direction, QVector3D(0, 1, 0))) >= 0.707) {
if (QVector3D::dotProduct(normal, QVector3D(0, 0, 1)) <= 0.0)
continue;
} else {
if (QVector3D::dotProduct(normal, QVector3D(0, -1, 0)) <= 0.0)
continue;
}
const auto &triangleIndices = m_object->triangles[triangleIndex];
if (triangleIndices.size() != 3) {
qDebug() << "Found invalid triangle indices";
continue;
}
const std::vector<QVector2D> &uv = triangleVertexUvs[triangleIndex];
QVector2D middlePoint = (uv[0] + uv[1] + uv[2]) / 3.0;
float finalRadius = (uv[0].distanceToPoint(uv[1]) +
uv[1].distanceToPoint(uv[2]) +
uv[2].distanceToPoint(uv[0])) / 3.0;
QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize,
middlePoint.y() * TextureGenerator::m_textureSize),
finalRadius * TextureGenerator::m_textureSize);
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(1.0, Qt::transparent);
for (const auto &it: allRects->second) {
if (it.contains(middlePoint.x(), middlePoint.y())) {
QRectF fillTarget((middlePoint.x() - finalRadius),
(middlePoint.y() - finalRadius),
(finalRadius + finalRadius),
(finalRadius + finalRadius));
auto clippedRect = it.intersected(fillTarget);
QRectF translatedRect = {
clippedRect.left() * TextureGenerator::m_textureSize,
clippedRect.top() * TextureGenerator::m_textureSize,
clippedRect.width() * TextureGenerator::m_textureSize,
clippedRect.height() * TextureGenerator::m_textureSize
};
texturePainter.fillRect(translatedRect, gradient);
}
}
// Fill the neighbor halfedges
for (int i = 0; i < 3; ++i) {
int j = (i + 1) % 3;
auto oppositeHalfEdge = std::make_pair(triangleIndices[j], triangleIndices[i]);
const auto &opposite = halfEdgeToTriangleMap.find(oppositeHalfEdge);
if (opposite == halfEdgeToTriangleMap.end())
continue;
auto oppositeTriangleIndex = std::get<0>(opposite->second);
const std::pair<QUuid, QUuid> &oppositeSource = triangleSourceNodes[oppositeTriangleIndex];
if (partId == oppositeSource.first)
continue;
const auto &oppositeAllRects = partUvRects.find(oppositeSource.first);
if (oppositeAllRects == partUvRects.end()) {
qDebug() << "Found part uv rects failed";
continue;
}
const std::vector<QVector2D> &oppositeUv = triangleVertexUvs[oppositeTriangleIndex];
QVector2D oppositeMiddlePoint = (oppositeUv[std::get<1>(opposite->second)] + oppositeUv[std::get<2>(opposite->second)]) * 0.5;
QRadialGradient oppositeGradient(QPointF(oppositeMiddlePoint.x() * TextureGenerator::m_textureSize,
oppositeMiddlePoint.y() * TextureGenerator::m_textureSize),
finalRadius * TextureGenerator::m_textureSize);
oppositeGradient.setColorAt(0.0, Qt::white);
oppositeGradient.setColorAt(1.0, Qt::transparent);
for (const auto &it: oppositeAllRects->second) {
if (it.contains(oppositeMiddlePoint.x(), oppositeMiddlePoint.y())) {
QRectF fillTarget((oppositeMiddlePoint.x() - finalRadius),
(oppositeMiddlePoint.y() - finalRadius),
(finalRadius + finalRadius),
(finalRadius + finalRadius));
auto clippedRect = it.intersected(fillTarget);
QRectF translatedRect = {
clippedRect.left() * TextureGenerator::m_textureSize,
clippedRect.top() * TextureGenerator::m_textureSize,
clippedRect.width() * TextureGenerator::m_textureSize,
clippedRect.height() * TextureGenerator::m_textureSize
};
texturePainter.fillRect(translatedRect, oppositeGradient);
}
}
}
}
hasNormalMap = !m_partNormalTextureMap.empty();
if (!m_partMetalnessTextureMap.empty())
hasMetalnessMap = true;
if (!m_partRoughnessTextureMap.empty())
hasRoughnessMap = true;
hasAmbientOcclusionMap = !m_partAmbientOcclusionTextureMap.empty();
auto paintTextureEndTime = countTimeConsumed.elapsed();
texturePainter.end();
textureNormalPainter.end();
textureMetalnessPainter.end();
textureRoughnessPainter.end();
textureAmbientOcclusionPainter.end();
if (!hasNormalMap) {
delete m_resultTextureNormalImage;
m_resultTextureNormalImage = nullptr;
}
if (!hasMetalnessMap && !hasRoughnessMap && !hasAmbientOcclusionMap) {
delete m_resultTextureMetalnessImage;
m_resultTextureMetalnessImage = nullptr;
delete m_resultTextureRoughnessImage;
m_resultTextureRoughnessImage = nullptr;
delete m_resultTextureAmbientOcclusionImage;
m_resultTextureAmbientOcclusionImage = nullptr;
}
auto createResultBeginTime = countTimeConsumed.elapsed();
m_resultMesh->setTextureImage(new QImage(*m_resultTextureColorImage));
if (nullptr != m_resultTextureNormalImage)
m_resultMesh->setNormalMapImage(new QImage(*m_resultTextureNormalImage));
if (hasMetalnessMap || hasRoughnessMap || hasAmbientOcclusionMap) {
m_resultMesh->setMetalnessRoughnessAmbientOcclusionImage(combineMetalnessRoughnessAmbientOcclusionImages(
m_resultTextureMetalnessImage,
m_resultTextureRoughnessImage,
m_resultTextureAmbientOcclusionImage));
m_resultMesh->setHasMetalnessInImage(hasMetalnessMap);
m_resultMesh->setHasRoughnessInImage(hasRoughnessMap);
m_resultMesh->setHasAmbientOcclusionInImage(hasAmbientOcclusionMap);
}
auto createResultEndTime = countTimeConsumed.elapsed();
qDebug() << "The texture[" << TextureGenerator::m_textureSize << "x" << TextureGenerator::m_textureSize << "] generation took" << countTimeConsumed.elapsed() << "milliseconds";
}
drawSolubility这里也是一个Lambda Function,用于绘制溶解性贴图。之后是一个debug环节,遍历m_object的triangles.size(),如果size不为3,说明存在N-Gon,前面的三角化环节没有处理正确,借助Qt抛出异常,跳过这个三角形执行后面的操作。之后在处理局部UV时如果不能判断找到一个UV矩形,同样会抛出异常并跳过当前出问题的partUvRects。处理完毕后,填充邻域的半边结构,做一些最后的清理工作,完成纹理的生成任务。