109 QVector<VertexAttributeDataExt> vertexAttributes;
111 vertexAttributes.resize(mesh->mNumVertices);
114 if (mesh->HasPositions()) {
115 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
116 const auto vertex = mesh->mVertices[index];
117 vertexAttributes[index].aData.position = QVector3D(vertex.x, vertex.y, vertex.z);
122 if (mesh->HasNormals()) {
123 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
124 const auto normal = mesh->mNormals[index];
125 vertexAttributes[index].aData.normal = QVector3D(normal.x, normal.y, normal.z);
130 if (mesh->HasTextureCoords(0)) {
131 const auto texCoords = mesh->mTextureCoords[0];
133 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
134 const auto uv = texCoords[index];
135 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, 0.0f);
138 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
139 const auto uv = texCoords[index];
140 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, uv.z);
146 if (mesh->HasTextureCoords(1)) {
147 const auto texCoords = mesh->mTextureCoords[1];
149 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
150 const auto uv = texCoords[index];
151 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, 0.0f);
154 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
155 const auto uv = texCoords[index];
156 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, uv.z);
162 if (mesh->HasTangentsAndBitangents()) {
163 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
164 const auto tangent = mesh->mTangents[index];
165 const auto binormal = mesh->mBitangents[index];
166 vertexAttributes[index].aData.tangent = QVector3D(tangent.x, tangent.y, tangent.z);
167 vertexAttributes[index].aData.binormal = QVector3D(binormal.x, binormal.y, binormal.z);
172 if (mesh->HasVertexColors(0)) {
173 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
174 const auto color = mesh->mColors[0][index];
175 vertexAttributes[index].aData.color = QVector4D(color.r, color.g, color.b, color.a);
180 if (mesh->HasBones()) {
181 for (uint i = 0; i < mesh->mNumBones; ++i) {
183 for (uint j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
184 quint32 vertexId = mesh->mBones[i]->mWeights[j].mVertexId;
185 float weight = mesh->mBones[i]->mWeights[j].mWeight;
192 if (vertexAttributes[vertexId].boneWeights.x() == 0.0f) {
193 vertexAttributes[vertexId].boneIndexes.x = qint32(vId);
194 vertexAttributes[vertexId].boneWeights.setX(weight);
195 }
else if (vertexAttributes[vertexId].boneWeights.y() == 0.0f) {
196 vertexAttributes[vertexId].boneIndexes.y = qint32(vId);
197 vertexAttributes[vertexId].boneWeights.setY(weight);
198 }
else if (vertexAttributes[vertexId].boneWeights.z() == 0.0f) {
199 vertexAttributes[vertexId].boneIndexes.z = qint32(vId);
200 vertexAttributes[vertexId].boneWeights.setZ(weight);
201 }
else if (vertexAttributes[vertexId].boneWeights.w() == 0.0f) {
202 vertexAttributes[vertexId].boneIndexes.w = qint32(vId);
203 vertexAttributes[vertexId].boneWeights.setW(weight);
205 qWarning(
"vertexId %d has already 4 weights and index %d's weight %f will be ignored.", vertexId, vId, weight);
212 if (requirments.numMorphTargets > 0) {
213 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
214 vertexAttributes[index].targetAData.resize(requirments.numMorphTargets);
216 for (uint i = 0; i < requirments.numMorphTargets; ++i) {
217 if (i >= mesh->mNumAnimMeshes)
220 auto animMesh = mesh->mAnimMeshes[i];
221 if (animMesh->HasPositions()) {
222 const auto vertex = animMesh->mVertices[index];
223 vertexAttributes[index].targetAData[i].position = QVector3D(vertex.x, vertex.y, vertex.z);
225 if (animMesh->HasNormals()) {
226 const auto normal = animMesh->mNormals[index];
227 vertexAttributes[index].targetAData[i].normal = QVector3D(normal.x, normal.y, normal.z);
229 if (animMesh->HasTangentsAndBitangents()) {
230 const auto tangent = animMesh->mTangents[index];
231 const auto binormal = animMesh->mBitangents[index];
232 vertexAttributes[index].targetAData[i].tangent = QVector3D(tangent.x, tangent.y, tangent.z);
233 vertexAttributes[index].targetAData[i].binormal = QVector3D(binormal.x, binormal.y, binormal.z);
235 if (animMesh->HasTextureCoords(0)) {
236 const auto texCoords = animMesh->mTextureCoords[0];
237 const auto uv = texCoords[index];
238 vertexAttributes[index].targetAData[i].uv0 = QVector3D(uv.x, uv.y, uv.z);
240 if (animMesh->HasTextureCoords(1)) {
241 const auto texCoords = animMesh->mTextureCoords[1];
242 const auto uv = texCoords[index];
243 vertexAttributes[index].targetAData[i].uv1 = QVector3D(uv.x, uv.y, uv.z);
245 if (animMesh->HasVertexColors(0)) {
246 const auto color = animMesh->mColors[0][index];
247 vertexAttributes[index].targetAData[i].color = QVector4D(color.r, color.g, color.b, color.a);
253 return vertexAttributes;
502 const bool recalculateNormals = !(qFuzzyIsNull(normalMergeAngle) && qFuzzyIsNull(normalSplitAngle));
503 const float normalMergeThreshold = qCos(qDegreesToRadians(normalMergeAngle));
504 const float normalSplitThreshold = qCos(qDegreesToRadians(normalSplitAngle));
506 QVector<QVector3D> positions;
507 positions.reserve(vertexAttributes.size());
508 QVector<QVector3D> normals;
509 normals.reserve(vertexAttributes.size());
510 for (
const auto &vertex : vertexAttributes) {
511 positions.append(vertex.aData.position);
512 normals.append(vertex.aData.normal);
515 QVector<QVector3D> splitVertexNormals;
516 QVector<quint32> splitVertexIndices;
517 quint32 splitVertexCount = vertexAttributes.size();
519 const float targetError =
std::numeric_limits<
float>::max();
520 const float *vertexData =
reinterpret_cast<
const float *>(positions.constData());
521 const float scaleFactor = QSSGMesh::simplifyScale(vertexData, positions.size(),
sizeof(QVector3D));
522 const quint32 indexCount = indexes.size();
523 quint32 indexTarget = 12;
524 quint32 lastIndexCount = 0;
525 QVector<QPair<
float, QVector<quint32>>> lods;
527 while (indexTarget < indexCount) {
529 QVector<quint32> newIndexes;
530 newIndexes.resize(indexCount);
531 size_t newLength = QSSGMesh::simplifyMesh(newIndexes.data(), indexes.constData(), indexes.size(), vertexData, positions.size(),
sizeof(QVector3D), indexTarget, targetError, 0, &error);
534 if (newLength < lastIndexCount * 1.5f) {
535 indexTarget = indexTarget * 1.5f;
540 if (newLength == 0 || (newLength >= (indexCount * 0.75f)))
543 newIndexes.resize(newLength);
546 if (recalculateNormals) {
548 QVector<QVector3D> faceNormals;
550 QVector<quint32> culledIndexes;
551 for (quint32 j = 0; j < newIndexes.size(); j += 3) {
552 const QVector3D &v0 = positions[newIndexes[j]];
553 const QVector3D &v1 = positions[newIndexes[j + 1]];
554 const QVector3D &v2 = positions[newIndexes[j + 2]];
556 QVector3D faceNormal = QVector3D::crossProduct(v1 - v0, v2 - v0);
558 const float faceArea = QSSGUtils::vec3::normalize(faceNormal);
563 if (faceArea != 0.0f) {
564 faceNormals.append(faceNormal);
565 faceNormals.append(faceNormal);
566 faceNormals.append(faceNormal);
567 culledIndexes.append({newIndexes[j], newIndexes[j + 1], newIndexes[j + 2]});
571 if (newIndexes.size() != culledIndexes.size())
572 newIndexes = culledIndexes;
577 QHash<QVector3D, QVector<quint32>> positionHash;
578 for (quint32 i = 0; i < newIndexes.size(); ++i) {
579 const quint32 index = newIndexes[i];
580 const QVector3D position = vertexAttributes[index].aData.position;
581 positionHash[position].append(i);
588 QVector<QPair<quint32, quint32>> remapIndexes;
589 for (quint32 positionIndex = 0; positionIndex < newIndexes.size(); ++positionIndex) {
590 const quint32 index = newIndexes[positionIndex];
591 const QVector3D &position = vertexAttributes[index].aData.position;
592 const QVector3D &faceNormal = faceNormals[positionIndex];
595 const auto &sharedPositions = positionHash.value(position);
596 for (
auto positionIndex2 : sharedPositions) {
597 if (positionIndex == positionIndex2) {
599 newNormal += faceNormal;
601 const QVector3D &faceNormal2 = faceNormals[positionIndex2];
602 if (QVector3D::dotProduct(faceNormal2, faceNormal) >= normalMergeThreshold)
603 newNormal += faceNormal2;
608 QSSGUtils::vec3::normalize(newNormal);
619 const QVector3D &originalNormal = vertexAttributes[index].aData.normal;
620 const float theta = QVector3D::dotProduct(originalNormal, newNormal);
621 if (theta < normalSplitThreshold) {
622 splitVertexIndices.append(index);
623 splitVertexNormals.append(newNormal.normalized());
624 remapIndexes.append({positionIndex, splitVertexCount++});
629 for (
auto pair : remapIndexes)
630 newIndexes[pair.first] = pair.second;
633 lods.append({error * scaleFactor, newIndexes});
634 indexTarget = qMax(newLength, indexTarget) * 2;
635 lastIndexCount = newLength;
642 for (quint32 i = 0; i < splitVertexIndices.size(); ++i) {
643 quint32 index = splitVertexIndices[i];
644 QVector3D newNormal = splitVertexNormals[i];
645 auto newVertex = vertexAttributes[index];
646 newVertex.aData.normal = newNormal;
647 vertexAttributes.append(newVertex);
656 const MeshList &meshes,
657 bool useFloatJointIndices,
658 bool generateLevelsOfDetail,
659 float normalMergeAngle,
660 float normalSplitAngle,
661 QString &errorString)
663 Q_UNUSED(errorString);
671 for (
const auto *mesh : meshes)
672 requirments.collectRequirmentsForMesh(mesh);
676 QByteArray indexBufferData;
678 QVector<SubsetEntryData> subsetData;
684 quint32 baseIndex = 0;
688 const QSSGMesh::Mesh::ComponentType indexType = QSSGMesh::Mesh::ComponentType::UnsignedInt32;
690 for (
const auto *mesh : meshes) {
695 QVector<quint32> indexes;
696 indexes.reserve(mesh->mNumFaces * 3);
697 for (
unsigned int faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
698 const auto face = mesh->mFaces[faceIndex];
700 Q_ASSERT(face.mNumIndices == 3);
703 indexes.append(quint32(face.mIndices[0]));
704 indexes.append(quint32(face.mIndices[1]));
705 indexes.append(quint32(face.mIndices[2]));
709 auto vertexAttributes = getVertexAttributeData(mesh, requirments);
712 quint32 baseIndexOffset = indexBufferData.size() / QSSGMesh::MeshInternal::byteSizeForComponentType(indexType);
713 QVector<quint32> lodIndexes;
714 QVector<QSSGMesh::Mesh::Lod> meshLods;
717 if (generateLevelsOfDetail) {
722 auto lods = generateMeshLevelsOfDetail(vertexAttributes, indexes, normalMergeAngle, normalSplitAngle);
723 for (
const auto &lodPair : lods) {
724 QSSGMesh::Mesh::Lod lod;
725 lod.offset = baseIndexOffset;
726 lod.count = lodPair.second.size();
727 lod.distance = lodPair.first;
728 meshLods.push_front(lod);
729 baseIndexOffset += lod.count;
731 auto currentLodIndexes = lodPair.second;
732 QSSGMesh::optimizeVertexCache(currentLodIndexes.data(), currentLodIndexes.data(), currentLodIndexes.size(), vertexAttributes.size());
733 lodIndexes += currentLodIndexes;
739 QSSGMesh::optimizeVertexCache(indexes.data(), indexes.data(), indexes.size(), vertexAttributes.size());
742 QVector<quint32> combinedIndexValues = lodIndexes + indexes;
744 for (
auto &index : combinedIndexValues)
746 indexBufferData += QByteArray(
reinterpret_cast<
const char *>(combinedIndexValues.constData()),
747 combinedIndexValues.size() * QSSGMesh::MeshInternal::byteSizeForComponentType(indexType));
754 SubsetEntryData subsetEntry;
755 subsetEntry.indexOffset = baseIndexOffset;
756 subsetEntry.indexLength = indexes.size();
757 subsetEntry.name = QString::fromUtf8(scene.mMaterials[mesh->mMaterialIndex]->GetName().C_Str());
758 subsetEntry.lightmapWidth = 0;
759 subsetEntry.lightmapHeight = 0;
760 subsetEntry.lods = meshLods;
761 subsetData.append(subsetEntry);
764 baseIndex += vertexAttributes.size();
766 vertexBufferData.targetVData.resize(requirments.numMorphTargets);
767 for (
const auto &vertex : vertexAttributes)
768 vertexBufferData.addVertexAttributeData(vertex, requirments);
773 QVector<QSSGMesh::AssetVertexEntry> entries = vertexBufferData.createEntries(requirments);
775 QVector<QSSGMesh::AssetMeshSubset> subsets;
776 for (
const SubsetEntryData &subset : subsetData) {
779 quint32(subset.indexLength),
780 quint32(subset.indexOffset),
782 subset.lightmapWidth,
783 subset.lightmapHeight,
805 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::fromAssetData(entries,
809 requirments.numMorphTargets,
810 numTargetComponents(requirments));