118 QVector<VertexAttributeDataExt> vertexAttributes;
120 vertexAttributes.resize(mesh->mNumVertices);
123 if (mesh->HasPositions()) {
124 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
125 const auto vertex = mesh->mVertices[index];
126 vertexAttributes[index].aData.position = QVector3D(vertex.x, vertex.y, vertex.z);
131 if (mesh->HasNormals()) {
132 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
133 const auto normal = mesh->mNormals[index];
134 vertexAttributes[index].aData.normal = QVector3D(normal.x, normal.y, normal.z);
139 if (mesh->HasTextureCoords(0)) {
140 const auto texCoords = mesh->mTextureCoords[0];
142 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
143 const auto uv = texCoords[index];
144 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, 0.0f);
147 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
148 const auto uv = texCoords[index];
149 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, uv.z);
155 if (mesh->HasTextureCoords(1)) {
156 const auto texCoords = mesh->mTextureCoords[1];
158 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
159 const auto uv = texCoords[index];
160 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, 0.0f);
163 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
164 const auto uv = texCoords[index];
165 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, uv.z);
171 if (mesh->HasTangentsAndBitangents()) {
172 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
173 const auto tangent = mesh->mTangents[index];
174 const auto binormal = mesh->mBitangents[index];
175 vertexAttributes[index].aData.tangent = QVector3D(tangent.x, tangent.y, tangent.z);
176 vertexAttributes[index].aData.binormal = QVector3D(binormal.x, binormal.y, binormal.z);
181 if (mesh->HasVertexColors(0)) {
182 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
183 const auto color = mesh->mColors[0][index];
184 vertexAttributes[index].aData.color = QVector4D(color.r, color.g, color.b, color.a);
189 if (mesh->HasBones()) {
190 for (uint i = 0; i < mesh->mNumBones; ++i) {
192 for (uint j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
193 quint32 vertexId = mesh->mBones[i]->mWeights[j].mVertexId;
194 float weight = mesh->mBones[i]->mWeights[j].mWeight;
201 if (vertexAttributes[vertexId].boneWeights.x() == 0.0f) {
202 vertexAttributes[vertexId].boneIndexes.x = qint32(vId);
203 vertexAttributes[vertexId].boneWeights.setX(weight);
204 }
else if (vertexAttributes[vertexId].boneWeights.y() == 0.0f) {
205 vertexAttributes[vertexId].boneIndexes.y = qint32(vId);
206 vertexAttributes[vertexId].boneWeights.setY(weight);
207 }
else if (vertexAttributes[vertexId].boneWeights.z() == 0.0f) {
208 vertexAttributes[vertexId].boneIndexes.z = qint32(vId);
209 vertexAttributes[vertexId].boneWeights.setZ(weight);
210 }
else if (vertexAttributes[vertexId].boneWeights.w() == 0.0f) {
211 vertexAttributes[vertexId].boneIndexes.w = qint32(vId);
212 vertexAttributes[vertexId].boneWeights.setW(weight);
214 qWarning(
"vertexId %d has already 4 weights and index %d's weight %f will be ignored.", vertexId, vId, weight);
221 if (requirments.numMorphTargets > 0) {
222 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
223 vertexAttributes[index].targetAData.resize(requirments.numMorphTargets);
225 for (uint i = 0; i < requirments.numMorphTargets; ++i) {
226 if (i >= mesh->mNumAnimMeshes)
229 auto animMesh = mesh->mAnimMeshes[i];
230 if (animMesh->HasPositions()) {
231 const auto vertex = animMesh->mVertices[index];
232 vertexAttributes[index].targetAData[i].position = QVector3D(vertex.x, vertex.y, vertex.z);
234 if (animMesh->HasNormals()) {
235 const auto normal = animMesh->mNormals[index];
236 vertexAttributes[index].targetAData[i].normal = QVector3D(normal.x, normal.y, normal.z);
238 if (animMesh->HasTangentsAndBitangents()) {
239 const auto tangent = animMesh->mTangents[index];
240 const auto binormal = animMesh->mBitangents[index];
241 vertexAttributes[index].targetAData[i].tangent = QVector3D(tangent.x, tangent.y, tangent.z);
242 vertexAttributes[index].targetAData[i].binormal = QVector3D(binormal.x, binormal.y, binormal.z);
244 if (animMesh->HasTextureCoords(0)) {
245 const auto texCoords = animMesh->mTextureCoords[0];
246 const auto uv = texCoords[index];
247 vertexAttributes[index].targetAData[i].uv0 = QVector3D(uv.x, uv.y, uv.z);
249 if (animMesh->HasTextureCoords(1)) {
250 const auto texCoords = animMesh->mTextureCoords[1];
251 const auto uv = texCoords[index];
252 vertexAttributes[index].targetAData[i].uv1 = QVector3D(uv.x, uv.y, uv.z);
254 if (animMesh->HasVertexColors(0)) {
255 const auto color = animMesh->mColors[0][index];
256 vertexAttributes[index].targetAData[i].color = QVector4D(color.r, color.g, color.b, color.a);
262 return vertexAttributes;
511 const bool recalculateNormals = !(qFuzzyIsNull(normalMergeAngle) && qFuzzyIsNull(normalSplitAngle));
512 const float normalMergeThreshold = qCos(qDegreesToRadians(normalMergeAngle));
513 const float normalSplitThreshold = qCos(qDegreesToRadians(normalSplitAngle));
515 QVector<QVector3D> positions;
516 positions.reserve(vertexAttributes.size());
517 QVector<QVector3D> normals;
518 normals.reserve(vertexAttributes.size());
519 for (
const auto &vertex : vertexAttributes) {
520 positions.append(vertex.aData.position);
521 normals.append(vertex.aData.normal);
524 QVector<QVector3D> splitVertexNormals;
525 QVector<quint32> splitVertexIndices;
526 quint32 splitVertexCount = vertexAttributes.size();
528 const float targetError =
std::numeric_limits<
float>::max();
529 const float *vertexData =
reinterpret_cast<
const float *>(positions.constData());
530 const float scaleFactor = QSSGMesh::simplifyScale(vertexData, positions.size(),
sizeof(QVector3D));
531 const quint32 indexCount = indexes.size();
532 quint32 indexTarget = 12;
533 quint32 lastIndexCount = 0;
534 QVector<QPair<
float, QVector<quint32>>> lods;
536 while (indexTarget < indexCount) {
538 QVector<quint32> newIndexes;
539 newIndexes.resize(indexCount);
540 size_t newLength = QSSGMesh::simplifyMesh(newIndexes.data(), indexes.constData(), indexes.size(), vertexData, positions.size(),
sizeof(QVector3D), indexTarget, targetError, 0, &error);
543 if (newLength < lastIndexCount * 1.5f) {
544 indexTarget = indexTarget * 1.5f;
549 if (newLength == 0 || (newLength >= (indexCount * 0.75f)))
552 newIndexes.resize(newLength);
555 if (recalculateNormals) {
557 QVector<QVector3D> faceNormals;
559 QVector<quint32> culledIndexes;
560 for (quint32 j = 0; j < newIndexes.size(); j += 3) {
561 const QVector3D &v0 = positions[newIndexes[j]];
562 const QVector3D &v1 = positions[newIndexes[j + 1]];
563 const QVector3D &v2 = positions[newIndexes[j + 2]];
565 QVector3D faceNormal = QVector3D::crossProduct(v1 - v0, v2 - v0);
567 const float faceArea = QSSGUtils::vec3::normalize(faceNormal);
572 if (faceArea != 0.0f) {
573 faceNormals.append(faceNormal);
574 faceNormals.append(faceNormal);
575 faceNormals.append(faceNormal);
576 culledIndexes.append({newIndexes[j], newIndexes[j + 1], newIndexes[j + 2]});
580 if (newIndexes.size() != culledIndexes.size())
581 newIndexes = culledIndexes;
586 QHash<QVector3D, QVector<quint32>> positionHash;
587 for (quint32 i = 0; i < newIndexes.size(); ++i) {
588 const quint32 index = newIndexes[i];
589 const QVector3D position = vertexAttributes[index].aData.position;
590 positionHash[position].append(i);
597 QVector<QPair<quint32, quint32>> remapIndexes;
598 for (quint32 positionIndex = 0; positionIndex < newIndexes.size(); ++positionIndex) {
599 const quint32 index = newIndexes[positionIndex];
600 const QVector3D &position = vertexAttributes[index].aData.position;
601 const QVector3D &faceNormal = faceNormals[positionIndex];
604 const auto &sharedPositions = positionHash.value(position);
605 for (
auto positionIndex2 : sharedPositions) {
606 if (positionIndex == positionIndex2) {
608 newNormal += faceNormal;
610 const QVector3D &faceNormal2 = faceNormals[positionIndex2];
611 if (QVector3D::dotProduct(faceNormal2, faceNormal) >= normalMergeThreshold)
612 newNormal += faceNormal2;
617 QSSGUtils::vec3::normalize(newNormal);
628 const QVector3D &originalNormal = vertexAttributes[index].aData.normal;
629 const float theta = QVector3D::dotProduct(originalNormal, newNormal);
630 if (theta < normalSplitThreshold) {
631 splitVertexIndices.append(index);
632 splitVertexNormals.append(newNormal.normalized());
633 remapIndexes.append({positionIndex, splitVertexCount++});
638 for (
auto pair : remapIndexes)
639 newIndexes[pair.first] = pair.second;
642 lods.append({error * scaleFactor, newIndexes});
643 indexTarget = qMax(newLength, indexTarget) * 2;
644 lastIndexCount = newLength;
651 for (quint32 i = 0; i < splitVertexIndices.size(); ++i) {
652 quint32 index = splitVertexIndices[i];
653 QVector3D newNormal = splitVertexNormals[i];
654 auto newVertex = vertexAttributes[index];
655 newVertex.aData.normal = newNormal;
656 vertexAttributes.append(newVertex);
665 const MeshList &meshes,
666 bool useFloatJointIndices,
667 bool generateLevelsOfDetail,
668 float normalMergeAngle,
669 float normalSplitAngle,
670 QString &errorString)
672 Q_UNUSED(errorString);
680 for (
const auto *mesh : meshes)
681 requirments.collectRequirmentsForMesh(mesh);
685 QByteArray indexBufferData;
687 QVector<SubsetEntryData> subsetData;
693 quint32 baseIndex = 0;
697 const QSSGMesh::Mesh::ComponentType indexType = QSSGMesh::Mesh::ComponentType::UnsignedInt32;
699 for (
const auto *mesh : meshes) {
704 QVector<quint32> indexes;
705 indexes.reserve(mesh->mNumFaces * 3);
706 for (
unsigned int faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
707 const auto face = mesh->mFaces[faceIndex];
709 Q_ASSERT(face.mNumIndices == 3);
712 indexes.append(quint32(face.mIndices[0]));
713 indexes.append(quint32(face.mIndices[1]));
714 indexes.append(quint32(face.mIndices[2]));
718 auto vertexAttributes = getVertexAttributeData(mesh, requirments);
721 quint32 baseIndexOffset = indexBufferData.size() / QSSGMesh::MeshInternal::byteSizeForComponentType(indexType);
722 QVector<quint32> lodIndexes;
723 QVector<QSSGMesh::Mesh::Lod> meshLods;
726 if (generateLevelsOfDetail) {
731 auto lods = generateMeshLevelsOfDetail(vertexAttributes, indexes, normalMergeAngle, normalSplitAngle);
732 for (
const auto &lodPair : lods) {
733 QSSGMesh::Mesh::Lod lod;
734 lod.offset = baseIndexOffset;
735 lod.count = lodPair.second.size();
736 lod.distance = lodPair.first;
737 meshLods.push_front(lod);
738 baseIndexOffset += lod.count;
740 auto currentLodIndexes = lodPair.second;
741 QSSGMesh::optimizeVertexCache(currentLodIndexes.data(), currentLodIndexes.data(), currentLodIndexes.size(), vertexAttributes.size());
742 lodIndexes += currentLodIndexes;
748 QSSGMesh::optimizeVertexCache(indexes.data(), indexes.data(), indexes.size(), vertexAttributes.size());
751 QVector<quint32> combinedIndexValues = lodIndexes + indexes;
753 for (
auto &index : combinedIndexValues)
755 indexBufferData += QByteArray(
reinterpret_cast<
const char *>(combinedIndexValues.constData()),
756 combinedIndexValues.size() * QSSGMesh::MeshInternal::byteSizeForComponentType(indexType));
763 SubsetEntryData subsetEntry;
764 subsetEntry.indexOffset = baseIndexOffset;
765 subsetEntry.indexLength = indexes.size();
766 subsetEntry.name = QString::fromUtf8(scene.mMaterials[mesh->mMaterialIndex]->GetName().C_Str());
767 subsetEntry.lightmapWidth = 0;
768 subsetEntry.lightmapHeight = 0;
769 subsetEntry.lods = meshLods;
770 subsetData.append(subsetEntry);
773 baseIndex += vertexAttributes.size();
775 vertexBufferData.targetVData.resize(requirments.numMorphTargets);
776 for (
const auto &vertex : vertexAttributes)
777 vertexBufferData.addVertexAttributeData(vertex, requirments);
782 QVector<QSSGMesh::AssetVertexEntry> entries = vertexBufferData.createEntries(requirments);
784 QVector<QSSGMesh::AssetMeshSubset> subsets;
785 for (
const SubsetEntryData &subset : subsetData) {
788 quint32(subset.indexLength),
789 quint32(subset.indexOffset),
791 subset.lightmapWidth,
792 subset.lightmapHeight,
814 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::fromAssetData(entries,
818 requirments.numMorphTargets,
819 numTargetComponents(requirments));