110 QVector<VertexAttributeDataExt> vertexAttributes;
112 vertexAttributes.resize(mesh->mNumVertices);
115 if (mesh->HasPositions()) {
116 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
117 const auto vertex = mesh->mVertices[index];
118 vertexAttributes[index].aData.position = QVector3D(vertex.x, vertex.y, vertex.z);
123 if (mesh->HasNormals()) {
124 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
125 const auto normal = mesh->mNormals[index];
126 vertexAttributes[index].aData.normal = QVector3D(normal.x, normal.y, normal.z);
131 if (mesh->HasTextureCoords(0)) {
132 const auto texCoords = mesh->mTextureCoords[0];
134 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
135 const auto uv = texCoords[index];
136 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, 0.0f);
139 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
140 const auto uv = texCoords[index];
141 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, uv.z);
147 if (mesh->HasTextureCoords(1)) {
148 const auto texCoords = mesh->mTextureCoords[1];
150 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
151 const auto uv = texCoords[index];
152 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, 0.0f);
155 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
156 const auto uv = texCoords[index];
157 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, uv.z);
163 if (mesh->HasTangentsAndBitangents()) {
164 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
165 const auto tangent = mesh->mTangents[index];
166 const auto binormal = mesh->mBitangents[index];
167 vertexAttributes[index].aData.tangent = QVector3D(tangent.x, tangent.y, tangent.z);
168 vertexAttributes[index].aData.binormal = QVector3D(binormal.x, binormal.y, binormal.z);
173 if (mesh->HasVertexColors(0)) {
174 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
175 const auto color = mesh->mColors[0][index];
176 vertexAttributes[index].aData.color = QVector4D(color.r, color.g, color.b, color.a);
181 if (mesh->HasBones()) {
182 for (uint i = 0; i < mesh->mNumBones; ++i) {
184 for (uint j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
185 quint32 vertexId = mesh->mBones[i]->mWeights[j].mVertexId;
186 float weight = mesh->mBones[i]->mWeights[j].mWeight;
193 if (vertexAttributes[vertexId].boneWeights.x() == 0.0f) {
194 vertexAttributes[vertexId].boneIndexes.x = qint32(vId);
195 vertexAttributes[vertexId].boneWeights.setX(weight);
196 }
else if (vertexAttributes[vertexId].boneWeights.y() == 0.0f) {
197 vertexAttributes[vertexId].boneIndexes.y = qint32(vId);
198 vertexAttributes[vertexId].boneWeights.setY(weight);
199 }
else if (vertexAttributes[vertexId].boneWeights.z() == 0.0f) {
200 vertexAttributes[vertexId].boneIndexes.z = qint32(vId);
201 vertexAttributes[vertexId].boneWeights.setZ(weight);
202 }
else if (vertexAttributes[vertexId].boneWeights.w() == 0.0f) {
203 vertexAttributes[vertexId].boneIndexes.w = qint32(vId);
204 vertexAttributes[vertexId].boneWeights.setW(weight);
206 qWarning(
"vertexId %d has already 4 weights and index %d's weight %f will be ignored.", vertexId, vId, weight);
213 if (requirments.numMorphTargets > 0) {
214 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
215 vertexAttributes[index].targetAData.resize(requirments.numMorphTargets);
217 for (uint i = 0; i < requirments.numMorphTargets; ++i) {
218 if (i >= mesh->mNumAnimMeshes)
221 auto animMesh = mesh->mAnimMeshes[i];
222 if (animMesh->HasPositions()) {
223 const auto vertex = animMesh->mVertices[index];
224 vertexAttributes[index].targetAData[i].position = QVector3D(vertex.x, vertex.y, vertex.z);
226 if (animMesh->HasNormals()) {
227 const auto normal = animMesh->mNormals[index];
228 vertexAttributes[index].targetAData[i].normal = QVector3D(normal.x, normal.y, normal.z);
230 if (animMesh->HasTangentsAndBitangents()) {
231 const auto tangent = animMesh->mTangents[index];
232 const auto binormal = animMesh->mBitangents[index];
233 vertexAttributes[index].targetAData[i].tangent = QVector3D(tangent.x, tangent.y, tangent.z);
234 vertexAttributes[index].targetAData[i].binormal = QVector3D(binormal.x, binormal.y, binormal.z);
236 if (animMesh->HasTextureCoords(0)) {
237 const auto texCoords = animMesh->mTextureCoords[0];
238 const auto uv = texCoords[index];
239 vertexAttributes[index].targetAData[i].uv0 = QVector3D(uv.x, uv.y, uv.z);
241 if (animMesh->HasTextureCoords(1)) {
242 const auto texCoords = animMesh->mTextureCoords[1];
243 const auto uv = texCoords[index];
244 vertexAttributes[index].targetAData[i].uv1 = QVector3D(uv.x, uv.y, uv.z);
246 if (animMesh->HasVertexColors(0)) {
247 const auto color = animMesh->mColors[0][index];
248 vertexAttributes[index].targetAData[i].color = QVector4D(color.r, color.g, color.b, color.a);
254 return vertexAttributes;
503 const bool recalculateNormals = !(qFuzzyIsNull(normalMergeAngle) && qFuzzyIsNull(normalSplitAngle));
504 const float normalMergeThreshold = qCos(qDegreesToRadians(normalMergeAngle));
505 const float normalSplitThreshold = qCos(qDegreesToRadians(normalSplitAngle));
507 QVector<QVector3D> positions;
508 positions.reserve(vertexAttributes.size());
509 QVector<QVector3D> normals;
510 normals.reserve(vertexAttributes.size());
511 for (
const auto &vertex : std::as_const(vertexAttributes)) {
512 positions.append(vertex.aData.position);
513 normals.append(vertex.aData.normal);
516 QVector<QVector3D> splitVertexNormals;
517 QVector<quint32> splitVertexIndices;
518 quint32 splitVertexCount = vertexAttributes.size();
520 if (positions.isEmpty() || indexes.isEmpty())
541 QVector<quint32> weldRemap(positions.size());
542 QVarLengthArray<QSSGMesh::MeshVertexStream, 2> streams;
543 streams.append({ positions.constData(),
sizeof(QVector3D),
sizeof(QVector3D) });
544 if (!recalculateNormals)
545 streams.append({ normals.constData(),
sizeof(QVector3D),
sizeof(QVector3D) });
546 const quint32 weldedVertexCount = QSSGMesh::generateVertexRemap(weldRemap.data(), indexes.constData(),
547 indexes.size(), positions.size(),
548 streams.constData(), size_t(streams.size()));
549 QVector<quint32> weldedIndexes(indexes.size());
550 QSSGMesh::remapIndexBuffer(weldedIndexes.data(), indexes.constData(), indexes.size(), weldRemap.constData());
551 QVector<QVector3D> weldedPositions(weldedVertexCount);
552 QSSGMesh::remapVertexBuffer(weldedPositions.data(), positions.constData(), positions.size(),
553 sizeof(QVector3D), weldRemap.constData());
560 constexpr quint32 unusedVertex = std::numeric_limits<quint32>::max();
561 QVector<quint32> weldedToOriginal(weldedVertexCount, unusedVertex);
562 for (quint32 i = 0, end = quint32(positions.size()); i < end; ++i) {
563 const quint32 welded = weldRemap.at(i);
565 if (welded != unusedVertex && weldedToOriginal.at(welded) == unusedVertex)
566 weldedToOriginal[welded] = i;
569 const float targetError = std::numeric_limits<
float>::max();
570 const float *vertexData =
reinterpret_cast<
const float *>(weldedPositions.constData());
571 const float scaleFactor = QSSGMesh::simplifyScale(vertexData, weldedVertexCount,
sizeof(QVector3D));
572 const quint32 indexCount = indexes.size();
573 quint32 indexTarget = 12;
574 quint32 lastIndexCount = 0;
575 QVector<QPair<
float, QVector<quint32>>> lods;
577 while (indexTarget < indexCount) {
579 QVector<quint32> newIndexes;
580 newIndexes.resize(indexCount);
581 size_t newLength = QSSGMesh::simplifyMesh(newIndexes.data(), weldedIndexes.constData(), weldedIndexes.size(), vertexData, weldedVertexCount,
sizeof(QVector3D), indexTarget, targetError, 0, &error);
584 if (newLength < lastIndexCount * 1.5f) {
585 indexTarget = indexTarget * 1.5f;
590 if (newLength == 0 || (newLength >= (indexCount * 0.75f)))
593 newIndexes.resize(newLength);
597 for (quint32 &index : newIndexes)
598 index = weldedToOriginal.at(index);
601 if (recalculateNormals) {
603 QVector<QVector3D> faceNormals;
605 QVector<quint32> culledIndexes;
606 for (quint32 j = 0; j < newIndexes.size(); j += 3) {
607 const QVector3D &v0 = positions[newIndexes[j]];
608 const QVector3D &v1 = positions[newIndexes[j + 1]];
609 const QVector3D &v2 = positions[newIndexes[j + 2]];
611 QVector3D faceNormal = QVector3D::crossProduct(v1 - v0, v2 - v0);
613 const float faceArea = QSSGUtils::vec3::normalize(faceNormal);
618 if (faceArea != 0.0f) {
619 faceNormals.append(faceNormal);
620 faceNormals.append(faceNormal);
621 faceNormals.append(faceNormal);
622 culledIndexes.append({newIndexes[j], newIndexes[j + 1], newIndexes[j + 2]});
626 if (newIndexes.size() != culledIndexes.size())
627 newIndexes = culledIndexes;
632 QHash<QVector3D, QVector<quint32>> positionHash;
633 for (quint32 i = 0; i < newIndexes.size(); ++i) {
634 const quint32 index = newIndexes[i];
635 const QVector3D position = vertexAttributes[index].aData.position;
636 positionHash[position].append(i);
643 QVector<QPair<quint32, quint32>> remapIndexes;
644 for (quint32 positionIndex = 0; positionIndex < newIndexes.size(); ++positionIndex) {
645 const quint32 index = newIndexes[positionIndex];
646 const QVector3D &position = vertexAttributes[index].aData.position;
647 const QVector3D &faceNormal = faceNormals[positionIndex];
650 const auto &sharedPositions = positionHash.value(position);
651 for (
const auto positionIndex2 : sharedPositions) {
652 if (positionIndex == positionIndex2) {
654 newNormal += faceNormal;
656 const QVector3D &faceNormal2 = faceNormals[positionIndex2];
657 if (QVector3D::dotProduct(faceNormal2, faceNormal) >= normalMergeThreshold)
658 newNormal += faceNormal2;
663 QSSGUtils::vec3::normalize(newNormal);
674 const QVector3D &originalNormal = vertexAttributes[index].aData.normal;
675 const float theta = QVector3D::dotProduct(originalNormal, newNormal);
676 if (theta < normalSplitThreshold) {
677 splitVertexIndices.append(index);
678 splitVertexNormals.append(newNormal.normalized());
679 remapIndexes.append({positionIndex, splitVertexCount++});
684 for (
const auto &pair : std::as_const(remapIndexes))
685 newIndexes[pair.first] = pair.second;
688 lods.append({error * scaleFactor, newIndexes});
689 indexTarget = qMax(newLength, indexTarget) * 2;
690 lastIndexCount = newLength;
697 for (quint32 i = 0; i < splitVertexIndices.size(); ++i) {
698 quint32 index = splitVertexIndices[i];
699 QVector3D newNormal = splitVertexNormals[i];
700 auto newVertex = vertexAttributes[index];
701 newVertex.aData.normal = newNormal;
702 vertexAttributes.append(newVertex);
711 const MeshList &meshes,
712 bool useFloatJointIndices,
713 bool generateLevelsOfDetail,
714 float normalMergeAngle,
715 float normalSplitAngle,
716 QString &errorString)
718 Q_UNUSED(errorString);
726 for (
const auto *mesh : meshes)
727 requirments.collectRequirmentsForMesh(mesh);
731 QByteArray indexBufferData;
733 QVector<SubsetEntryData> subsetData;
739 quint32 baseIndex = 0;
743 const QSSGMesh::Mesh::ComponentType indexType = QSSGMesh::Mesh::ComponentType::UnsignedInt32;
745 for (
const auto *mesh : meshes) {
750 QVector<quint32> indexes;
751 indexes.reserve(mesh->mNumFaces * 3);
752 for (
unsigned int faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
753 const auto face = mesh->mFaces[faceIndex];
755 Q_ASSERT(face.mNumIndices == 3);
758 indexes.append(quint32(face.mIndices[0]));
759 indexes.append(quint32(face.mIndices[1]));
760 indexes.append(quint32(face.mIndices[2]));
764 auto vertexAttributes = getVertexAttributeData(mesh, requirments);
767 quint32 baseIndexOffset = indexBufferData.size() / QSSGMesh::MeshInternal::byteSizeForComponentType(indexType);
768 QVector<quint32> lodIndexes;
769 QVector<QSSGMesh::Mesh::Lod> meshLods;
772 if (generateLevelsOfDetail) {
777 auto lods = generateMeshLevelsOfDetail(vertexAttributes, indexes, normalMergeAngle, normalSplitAngle);
778 for (
const auto &lodPair : std::as_const(lods)) {
779 QSSGMesh::Mesh::Lod lod;
780 lod.offset = baseIndexOffset;
781 lod.count = lodPair.second.size();
782 lod.distance = lodPair.first;
783 meshLods.push_front(lod);
784 baseIndexOffset += lod.count;
786 auto currentLodIndexes = lodPair.second;
787 QSSGMesh::optimizeVertexCache(currentLodIndexes.data(), currentLodIndexes.data(), currentLodIndexes.size(), vertexAttributes.size());
788 lodIndexes += currentLodIndexes;
794 QSSGMesh::optimizeVertexCache(indexes.data(), indexes.data(), indexes.size(), vertexAttributes.size());
797 QVector<quint32> combinedIndexValues = lodIndexes + indexes;
799 for (
auto &index : combinedIndexValues)
801 indexBufferData += QByteArray(
reinterpret_cast<
const char *>(combinedIndexValues.constData()),
802 combinedIndexValues.size() * QSSGMesh::MeshInternal::byteSizeForComponentType(indexType));
809 SubsetEntryData subsetEntry;
810 subsetEntry.indexOffset = baseIndexOffset;
811 subsetEntry.indexLength = indexes.size();
812 subsetEntry.name = QString::fromUtf8(scene.mMaterials[mesh->mMaterialIndex]->GetName().C_Str());
813 subsetEntry.lightmapWidth = 0;
814 subsetEntry.lightmapHeight = 0;
815 subsetEntry.lods = meshLods;
816 subsetData.append(subsetEntry);
819 baseIndex += vertexAttributes.size();
821 vertexBufferData.targetVData.resize(requirments.numMorphTargets);
822 for (
const auto &vertex : std::as_const(vertexAttributes))
823 vertexBufferData.addVertexAttributeData(vertex, requirments);
828 QVector<QSSGMesh::AssetVertexEntry> entries = vertexBufferData.createEntries(requirments);
830 QVector<QSSGMesh::AssetMeshSubset> subsets;
831 for (
const SubsetEntryData &subset : subsetData) {
834 quint32(subset.indexLength),
835 quint32(subset.indexOffset),
837 subset.lightmapWidth,
838 subset.lightmapHeight,
860 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::fromAssetData(entries,
864 requirments.numMorphTargets,
865 numTargetComponents(requirments));