1548 const QVector<QVector3D> &normals,
1549 const QVector<quint32> &indexes,
1550 QVector<MeshVertexSplit> &splitVertices,
1551 float normalMergeAngle,
1552 float normalSplitAngle)
1557 splitVertices.clear();
1559 if (positions.isEmpty() || indexes.isEmpty())
1564 const bool recalculateNormals = normals.size() == positions.size()
1565 && !(qFuzzyIsNull(normalMergeAngle) && qFuzzyIsNull(normalSplitAngle));
1566 const float normalMergeThreshold = qCos(qDegreesToRadians(normalMergeAngle));
1567 const float normalSplitThreshold = qCos(qDegreesToRadians(normalSplitAngle));
1569 quint32 splitVertexCount = positions.size();
1588 QVector<quint32> weldRemap(positions.size());
1589 QVarLengthArray<MeshVertexStream, 2> streams;
1590 streams.append({ positions.constData(),
sizeof(QVector3D),
sizeof(QVector3D) });
1591 if (!recalculateNormals && normals.size() == positions.size())
1592 streams.append({ normals.constData(),
sizeof(QVector3D),
sizeof(QVector3D) });
1593 const quint32 weldedVertexCount = generateVertexRemap(weldRemap.data(), indexes.constData(), indexes.size(),
1594 positions.size(), streams.constData(),
1595 size_t(streams.size()));
1596 QVector<quint32> weldedIndexes(indexes.size());
1597 remapIndexBuffer(weldedIndexes.data(), indexes.constData(), indexes.size(), weldRemap.constData());
1598 QVector<QVector3D> weldedPositions(weldedVertexCount);
1599 remapVertexBuffer(weldedPositions.data(), positions.constData(), positions.size(),
sizeof(QVector3D),
1600 weldRemap.constData());
1607 constexpr quint32 unusedVertex = std::numeric_limits<quint32>::max();
1608 QVector<quint32> weldedToOriginal(weldedVertexCount, unusedVertex);
1609 for (quint32 i = 0, end = quint32(positions.size()); i < end; ++i) {
1610 const quint32 welded = weldRemap.at(i);
1612 if (welded != unusedVertex && weldedToOriginal.at(welded) == unusedVertex)
1613 weldedToOriginal[welded] = i;
1616 const float targetError = std::numeric_limits<
float>::max();
1617 const float *vertexData =
reinterpret_cast<
const float *>(weldedPositions.constData());
1618 const float scaleFactor = simplifyScale(vertexData, weldedVertexCount,
sizeof(QVector3D));
1619 const quint32 indexCount = indexes.size();
1623 const quint32 maxLevelIndexes = indexCount - indexCount / 4 - 1;
1624 const quint32 maxIndexTarget = maxLevelIndexes - maxLevelIndexes % 3;
1625 quint32 indexTarget = 12;
1626 quint32 lastIndexCount = 0;
1627 QVector<MeshLevelOfDetail> lods;
1629 while (indexTarget < indexCount) {
1631 QVector<quint32> newIndexes;
1632 newIndexes.resize(indexCount);
1633 size_t newLength = simplifyMesh(newIndexes.data(), weldedIndexes.constData(), weldedIndexes.size(),
1634 vertexData, weldedVertexCount,
sizeof(QVector3D), indexTarget,
1635 targetError, 0, &error);
1638 if (newLength < lastIndexCount + (lastIndexCount + 1) / 2) {
1639 const quint32 nextTarget = qMin(indexTarget + indexTarget / 2, maxIndexTarget);
1640 if (nextTarget <= indexTarget)
1642 indexTarget = nextTarget;
1647 if (newLength == 0 || newLength > maxLevelIndexes)
1650 newIndexes.resize(newLength);
1654 for (quint32 &index : newIndexes)
1655 index = weldedToOriginal.at(index);
1658 if (recalculateNormals) {
1660 QVector<QVector3D> faceNormals;
1662 QVector<quint32> culledIndexes;
1663 for (quint32 j = 0; j < quint32(newIndexes.size()); j += 3) {
1664 const QVector3D &v0 = positions[newIndexes[j]];
1665 const QVector3D &v1 = positions[newIndexes[j + 1]];
1666 const QVector3D &v2 = positions[newIndexes[j + 2]];
1668 QVector3D faceNormal = QVector3D::crossProduct(v1 - v0, v2 - v0);
1670 const float faceArea = QSSGUtils::vec3::normalize(faceNormal);
1675 if (faceArea != 0.0f) {
1676 faceNormals.append(faceNormal);
1677 faceNormals.append(faceNormal);
1678 faceNormals.append(faceNormal);
1679 culledIndexes.append({newIndexes[j], newIndexes[j + 1], newIndexes[j + 2]});
1683 if (newIndexes.size() != culledIndexes.size())
1684 newIndexes = culledIndexes;
1689 const quint32 newIndexCount = quint32(newIndexes.size());
1690 QHash<QVector3D, QVector<quint32>> positionHash;
1691 for (quint32 i = 0; i < newIndexCount; ++i) {
1692 const quint32 index = newIndexes[i];
1693 const QVector3D position = positions[index];
1694 positionHash[position].append(i);
1701 QVector<QPair<quint32, quint32>> remapIndexes;
1702 for (quint32 positionIndex = 0; positionIndex < newIndexCount; ++positionIndex) {
1703 const quint32 index = newIndexes[positionIndex];
1704 const QVector3D &position = positions[index];
1705 const QVector3D &faceNormal = faceNormals[positionIndex];
1706 QVector3D newNormal;
1708 const auto &sharedPositions = positionHash.value(position);
1709 for (
const auto positionIndex2 : sharedPositions) {
1710 if (positionIndex == positionIndex2) {
1712 newNormal += faceNormal;
1714 const QVector3D &faceNormal2 = faceNormals[positionIndex2];
1715 if (QVector3D::dotProduct(faceNormal2, faceNormal) >= normalMergeThreshold)
1716 newNormal += faceNormal2;
1721 QSSGUtils::vec3::normalize(newNormal);
1732 const QVector3D &originalNormal = normals[index];
1733 const float theta = QVector3D::dotProduct(originalNormal, newNormal);
1734 if (theta < normalSplitThreshold) {
1735 splitVertices.append({ index, newNormal.normalized() });
1736 remapIndexes.append({positionIndex, splitVertexCount++});
1741 for (
const auto &pair : std::as_const(remapIndexes))
1742 newIndexes[pair.first] = pair.second;
1745 lods.append({error * scaleFactor, newIndexes});
1746 lastIndexCount = newLength;
1749 if (lastIndexCount + (lastIndexCount + 1) / 2 > maxLevelIndexes)
1752 const size_t doubled = qMax(newLength, size_t(indexTarget)) * 2;
1753 const quint32 nextTarget = quint32(qMin<size_t>(doubled, maxIndexTarget));
1754 if (nextTarget <= indexTarget)
1756 indexTarget = nextTarget;