120 QVector<VertexAttributeDataExt> vertexAttributes;
122 vertexAttributes.resize(mesh->mNumVertices);
125 if (mesh->HasPositions()) {
126 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
127 const auto vertex = mesh->mVertices[index];
128 vertexAttributes[index].aData.position = QVector3D(vertex.x, vertex.y, vertex.z);
133 if (mesh->HasNormals()) {
134 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
135 const auto normal = mesh->mNormals[index];
136 vertexAttributes[index].aData.normal = QVector3D(normal.x, normal.y, normal.z);
141 if (mesh->HasTextureCoords(0)) {
142 const auto texCoords = mesh->mTextureCoords[0];
144 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
145 const auto uv = texCoords[index];
146 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, 0.0f);
149 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
150 const auto uv = texCoords[index];
151 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, uv.z);
157 if (mesh->HasTextureCoords(1)) {
158 const auto texCoords = mesh->mTextureCoords[1];
160 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
161 const auto uv = texCoords[index];
162 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, 0.0f);
165 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
166 const auto uv = texCoords[index];
167 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, uv.z);
173 if (mesh->HasTangentsAndBitangents()) {
174 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
175 const auto tangent = mesh->mTangents[index];
176 const auto binormal = mesh->mBitangents[index];
177 vertexAttributes[index].aData.tangent = QVector3D(tangent.x, tangent.y, tangent.z);
178 vertexAttributes[index].aData.binormal = QVector3D(binormal.x, binormal.y, binormal.z);
183 if (mesh->HasVertexColors(0)) {
184 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
185 const auto color = mesh->mColors[0][index];
186 vertexAttributes[index].aData.color = QVector4D(color.r, color.g, color.b, color.a);
191 if (mesh->HasBones()) {
192 for (uint i = 0; i < mesh->mNumBones; ++i) {
194 for (uint j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
195 quint32 vertexId = mesh->mBones[i]->mWeights[j].mVertexId;
196 float weight = mesh->mBones[i]->mWeights[j].mWeight;
203 if (vertexAttributes[vertexId].boneWeights.x() == 0.0f) {
204 vertexAttributes[vertexId].boneIndexes.x = qint32(vId);
205 vertexAttributes[vertexId].boneWeights.setX(weight);
206 }
else if (vertexAttributes[vertexId].boneWeights.y() == 0.0f) {
207 vertexAttributes[vertexId].boneIndexes.y = qint32(vId);
208 vertexAttributes[vertexId].boneWeights.setY(weight);
209 }
else if (vertexAttributes[vertexId].boneWeights.z() == 0.0f) {
210 vertexAttributes[vertexId].boneIndexes.z = qint32(vId);
211 vertexAttributes[vertexId].boneWeights.setZ(weight);
212 }
else if (vertexAttributes[vertexId].boneWeights.w() == 0.0f) {
213 vertexAttributes[vertexId].boneIndexes.w = qint32(vId);
214 vertexAttributes[vertexId].boneWeights.setW(weight);
216 qWarning(
"vertexId %d has already 4 weights and index %d's weight %f will be ignored.", vertexId, vId, weight);
223 if (requirments.numMorphTargets > 0) {
224 for (
unsigned int index = 0; index < mesh->mNumVertices; ++index) {
225 vertexAttributes[index].targetAData.resize(requirments.numMorphTargets);
227 for (uint i = 0; i < requirments.numMorphTargets; ++i) {
228 if (i >= mesh->mNumAnimMeshes)
231 auto animMesh = mesh->mAnimMeshes[i];
232 if (animMesh->HasPositions()) {
233 const auto vertex = animMesh->mVertices[index];
234 vertexAttributes[index].targetAData[i].position = QVector3D(vertex.x, vertex.y, vertex.z);
236 if (animMesh->HasNormals()) {
237 const auto normal = animMesh->mNormals[index];
238 vertexAttributes[index].targetAData[i].normal = QVector3D(normal.x, normal.y, normal.z);
240 if (animMesh->HasTangentsAndBitangents()) {
241 const auto tangent = animMesh->mTangents[index];
242 const auto binormal = animMesh->mBitangents[index];
243 vertexAttributes[index].targetAData[i].tangent = QVector3D(tangent.x, tangent.y, tangent.z);
244 vertexAttributes[index].targetAData[i].binormal = QVector3D(binormal.x, binormal.y, binormal.z);
246 if (animMesh->HasTextureCoords(0)) {
247 const auto texCoords = animMesh->mTextureCoords[0];
248 const auto uv = texCoords[index];
249 vertexAttributes[index].targetAData[i].uv0 = QVector3D(uv.x, uv.y, uv.z);
251 if (animMesh->HasTextureCoords(1)) {
252 const auto texCoords = animMesh->mTextureCoords[1];
253 const auto uv = texCoords[index];
254 vertexAttributes[index].targetAData[i].uv1 = QVector3D(uv.x, uv.y, uv.z);
256 if (animMesh->HasVertexColors(0)) {
257 const auto color = animMesh->mColors[0][index];
258 vertexAttributes[index].targetAData[i].color = QVector4D(color.r, color.g, color.b, color.a);
264 return vertexAttributes;
513 const bool recalculateNormals = !(qFuzzyIsNull(normalMergeAngle) && qFuzzyIsNull(normalSplitAngle));
514 const float normalMergeThreshold = qCos(qDegreesToRadians(normalMergeAngle));
515 const float normalSplitThreshold = qCos(qDegreesToRadians(normalSplitAngle));
517 QVector<QVector3D> positions;
518 positions.reserve(vertexAttributes.size());
519 QVector<QVector3D> normals;
520 normals.reserve(vertexAttributes.size());
521 for (
const auto &vertex : vertexAttributes) {
522 positions.append(vertex.aData.position);
523 normals.append(vertex.aData.normal);
526 QVector<QVector3D> splitVertexNormals;
527 QVector<quint32> splitVertexIndices;
528 quint32 splitVertexCount = vertexAttributes.size();
530 const float targetError =
std::numeric_limits<
float>::max();
531 const float *vertexData =
reinterpret_cast<
const float *>(positions.constData());
532 const float scaleFactor = QSSGMesh::simplifyScale(vertexData, positions.size(),
sizeof(QVector3D));
533 const quint32 indexCount = indexes.size();
534 quint32 indexTarget = 12;
535 quint32 lastIndexCount = 0;
536 QVector<QPair<
float, QVector<quint32>>> lods;
538 while (indexTarget < indexCount) {
540 QVector<quint32> newIndexes;
541 newIndexes.resize(indexCount);
542 size_t newLength = QSSGMesh::simplifyMesh(newIndexes.data(), indexes.constData(), indexes.size(), vertexData, positions.size(),
sizeof(QVector3D), indexTarget, targetError, 0, &error);
545 if (newLength < lastIndexCount * 1.5f) {
546 indexTarget = indexTarget * 1.5f;
551 if (newLength == 0 || (newLength >= (indexCount * 0.75f)))
554 newIndexes.resize(newLength);
557 if (recalculateNormals) {
559 QVector<QVector3D> faceNormals;
561 QVector<quint32> culledIndexes;
562 for (quint32 j = 0; j < newIndexes.size(); j += 3) {
563 const QVector3D &v0 = positions[newIndexes[j]];
564 const QVector3D &v1 = positions[newIndexes[j + 1]];
565 const QVector3D &v2 = positions[newIndexes[j + 2]];
567 QVector3D faceNormal = QVector3D::crossProduct(v1 - v0, v2 - v0);
569 const float faceArea = QSSGUtils::vec3::normalize(faceNormal);
574 if (faceArea != 0.0f) {
575 faceNormals.append(faceNormal);
576 faceNormals.append(faceNormal);
577 faceNormals.append(faceNormal);
578 culledIndexes.append({newIndexes[j], newIndexes[j + 1], newIndexes[j + 2]});
582 if (newIndexes.size() != culledIndexes.size())
583 newIndexes = culledIndexes;
588 QHash<QVector3D, QVector<quint32>> positionHash;
589 for (quint32 i = 0; i < newIndexes.size(); ++i) {
590 const quint32 index = newIndexes[i];
591 const QVector3D position = vertexAttributes[index].aData.position;
592 positionHash[position].append(i);
599 QVector<QPair<quint32, quint32>> remapIndexes;
600 for (quint32 positionIndex = 0; positionIndex < newIndexes.size(); ++positionIndex) {
601 const quint32 index = newIndexes[positionIndex];
602 const QVector3D &position = vertexAttributes[index].aData.position;
603 const QVector3D &faceNormal = faceNormals[positionIndex];
606 const auto &sharedPositions = positionHash.value(position);
607 for (
auto positionIndex2 : sharedPositions) {
608 if (positionIndex == positionIndex2) {
610 newNormal += faceNormal;
612 const QVector3D &faceNormal2 = faceNormals[positionIndex2];
613 if (QVector3D::dotProduct(faceNormal2, faceNormal) >= normalMergeThreshold)
614 newNormal += faceNormal2;
619 QSSGUtils::vec3::normalize(newNormal);
630 const QVector3D &originalNormal = vertexAttributes[index].aData.normal;
631 const float theta = QVector3D::dotProduct(originalNormal, newNormal);
632 if (theta < normalSplitThreshold) {
633 splitVertexIndices.append(index);
634 splitVertexNormals.append(newNormal.normalized());
635 remapIndexes.append({positionIndex, splitVertexCount++});
640 for (
auto pair : remapIndexes)
641 newIndexes[pair.first] = pair.second;
644 lods.append({error * scaleFactor, newIndexes});
645 indexTarget = qMax(newLength, indexTarget) * 2;
646 lastIndexCount = newLength;
653 for (quint32 i = 0; i < splitVertexIndices.size(); ++i) {
654 quint32 index = splitVertexIndices[i];
655 QVector3D newNormal = splitVertexNormals[i];
656 auto newVertex = vertexAttributes[index];
657 newVertex.aData.normal = newNormal;
658 vertexAttributes.append(newVertex);
667 const MeshList &meshes,
668 bool useFloatJointIndices,
669 bool generateLevelsOfDetail,
670 float normalMergeAngle,
671 float normalSplitAngle,
672 QString &errorString)
674 Q_UNUSED(errorString);
682 for (
const auto *mesh : meshes)
683 requirments.collectRequirmentsForMesh(mesh);
687 QByteArray indexBufferData;
689 QVector<SubsetEntryData> subsetData;
695 quint32 baseIndex = 0;
699 const QSSGMesh::Mesh::ComponentType indexType = QSSGMesh::Mesh::ComponentType::UnsignedInt32;
701 for (
const auto *mesh : meshes) {
706 QVector<quint32> indexes;
707 indexes.reserve(mesh->mNumFaces * 3);
708 for (
unsigned int faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
709 const auto face = mesh->mFaces[faceIndex];
711 Q_ASSERT(face.mNumIndices == 3);
714 indexes.append(quint32(face.mIndices[0]));
715 indexes.append(quint32(face.mIndices[1]));
716 indexes.append(quint32(face.mIndices[2]));
720 auto vertexAttributes = getVertexAttributeData(mesh, requirments);
723 quint32 baseIndexOffset = indexBufferData.size() / QSSGMesh::MeshInternal::byteSizeForComponentType(indexType);
724 QVector<quint32> lodIndexes;
725 QVector<QSSGMesh::Mesh::Lod> meshLods;
728 if (generateLevelsOfDetail) {
733 auto lods = generateMeshLevelsOfDetail(vertexAttributes, indexes, normalMergeAngle, normalSplitAngle);
734 for (
const auto &lodPair : lods) {
735 QSSGMesh::Mesh::Lod lod;
736 lod.offset = baseIndexOffset;
737 lod.count = lodPair.second.size();
738 lod.distance = lodPair.first;
739 meshLods.push_front(lod);
740 baseIndexOffset += lod.count;
742 auto currentLodIndexes = lodPair.second;
743 QSSGMesh::optimizeVertexCache(currentLodIndexes.data(), currentLodIndexes.data(), currentLodIndexes.size(), vertexAttributes.size());
744 lodIndexes += currentLodIndexes;
750 QSSGMesh::optimizeVertexCache(indexes.data(), indexes.data(), indexes.size(), vertexAttributes.size());
753 QVector<quint32> combinedIndexValues = lodIndexes + indexes;
755 for (
auto &index : combinedIndexValues)
757 indexBufferData += QByteArray(
reinterpret_cast<
const char *>(combinedIndexValues.constData()),
758 combinedIndexValues.size() * QSSGMesh::MeshInternal::byteSizeForComponentType(indexType));
765 SubsetEntryData subsetEntry;
766 subsetEntry.indexOffset = baseIndexOffset;
767 subsetEntry.indexLength = indexes.size();
768 subsetEntry.name = QString::fromUtf8(scene.mMaterials[mesh->mMaterialIndex]->GetName().C_Str());
769 subsetEntry.lightmapWidth = 0;
770 subsetEntry.lightmapHeight = 0;
771 subsetEntry.lods = meshLods;
772 subsetData.append(subsetEntry);
775 baseIndex += vertexAttributes.size();
777 vertexBufferData.targetVData.resize(requirments.numMorphTargets);
778 for (
const auto &vertex : vertexAttributes)
779 vertexBufferData.addVertexAttributeData(vertex, requirments);
784 QVector<QSSGMesh::AssetVertexEntry> entries = vertexBufferData.createEntries(requirments);
786 QVector<QSSGMesh::AssetMeshSubset> subsets;
787 for (
const SubsetEntryData &subset : subsetData) {
790 quint32(subset.indexLength),
791 quint32(subset.indexOffset),
793 subset.lightmapWidth,
794 subset.lightmapHeight,
816 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::fromAssetData(entries,
820 requirments.numMorphTargets,
821 numTargetComponents(requirments));