Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
assimputils.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
6#include "assimputils.h"
7
8#include <assimp/Importer.hpp>
9#include <assimp/scene.h>
10#include <assimp/Logger.hpp>
11#include <assimp/DefaultLogger.hpp>
12#include <assimp/postprocess.h>
13#include <assimp/importerdesc.h>
14
15#include <QtCore/qstring.h>
16#include <QtCore/QHash>
17#include <QtCore/QSet>
18
19QT_BEGIN_NAMESPACE
20
21namespace
22{
23
32
34 qint32 x = 0;
35 qint32 y = 0;
36 qint32 z = 0;
37 qint32 w = 0;
38};
39
49
56
58 bool needsPositionData = false;
59 bool needsNormalData = false;
60 bool needsTangentData = false;
62 unsigned uv0Components = 0;
63 unsigned uv1Components = 0;
64 bool needsUV0Data = false;
65 bool needsUV1Data = false;
66 bool needsBones = false;
68
70 // All the target mesh will have the same components
71 // Target texture coords will be recored as 3 components.
72 // even if we are using just 2 components now.
77 bool needsTargetUV0Data = false;
78 bool needsTargetUV1Data = false;
79
80 void collectRequirmentsForMesh(const aiMesh *mesh) {
81 uv0Components = qMax(mesh->mNumUVComponents[0], uv0Components);
82 uv1Components = qMax(mesh->mNumUVComponents[1], uv1Components);
83 needsUV0Data |= mesh->HasTextureCoords(0);
84 needsUV1Data |= mesh->HasTextureCoords(1);
85 needsPositionData |= mesh->HasPositions();
86 needsNormalData |= mesh->HasNormals();
87 needsTangentData |= mesh->HasTangentsAndBitangents();
88 needsVertexColorData |=mesh->HasVertexColors(0);
89 needsBones |= mesh->HasBones();
90 numMorphTargets = mesh->mNumAnimMeshes;
91 if (numMorphTargets && mesh->mAnimMeshes) {
92 for (uint i = 0; i < numMorphTargets; ++i) {
93 auto animMesh = mesh->mAnimMeshes[i];
94 needsTargetPositionData |= animMesh->HasPositions();
95 needsTargetNormalData |= animMesh->HasNormals();
96 needsTargetTangentData |= animMesh->HasTangentsAndBitangents();
97 needsTargetVertexColorData |= animMesh->HasVertexColors(0);
98 needsTargetUV0Data |= animMesh->HasTextureCoords(0);
99 needsTargetUV1Data |= animMesh->HasTextureCoords(1);
100 }
101 }
102 }
103};
104
106{
107 QVector<VertexAttributeDataExt> vertexAttributes;
108
109 vertexAttributes.resize(mesh->mNumVertices);
110
111 // Positions
112 if (mesh->HasPositions()) {
113 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
114 const auto vertex = mesh->mVertices[index];
115 vertexAttributes[index].aData.position = QVector3D(vertex.x, vertex.y, vertex.z);
116 }
117 }
118
119 // Normals
120 if (mesh->HasNormals()) {
121 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
122 const auto normal = mesh->mNormals[index];
123 vertexAttributes[index].aData.normal = QVector3D(normal.x, normal.y, normal.z);
124 }
125 }
126
127 // UV0
128 if (mesh->HasTextureCoords(0)) {
129 const auto texCoords = mesh->mTextureCoords[0];
130 if (requirments.uv0Components == 2) {
131 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
132 const auto uv = texCoords[index];
133 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, 0.0f);
134 }
135 } else if (requirments.uv0Components == 3) {
136 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
137 const auto uv = texCoords[index];
138 vertexAttributes[index].aData.uv0 = QVector3D(uv.x, uv.y, uv.z);
139 }
140 }
141 }
142
143 // UV1
144 if (mesh->HasTextureCoords(1)) {
145 const auto texCoords = mesh->mTextureCoords[1];
146 if (requirments.uv1Components == 2) {
147 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
148 const auto uv = texCoords[index];
149 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, 0.0f);
150 }
151 } else if (requirments.uv1Components == 3) {
152 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
153 const auto uv = texCoords[index];
154 vertexAttributes[index].aData.uv1 = QVector3D(uv.x, uv.y, uv.z);
155 }
156 }
157 }
158
159 // Tangents and Binormals
160 if (mesh->HasTangentsAndBitangents()) {
161 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
162 const auto tangent = mesh->mTangents[index];
163 const auto binormal = mesh->mBitangents[index];
164 vertexAttributes[index].aData.tangent = QVector3D(tangent.x, tangent.y, tangent.z);
165 vertexAttributes[index].aData.binormal = QVector3D(binormal.x, binormal.y, binormal.z);
166 }
167 }
168
169 // Vertex Colors
170 if (mesh->HasVertexColors(0)) {
171 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
172 const auto color = mesh->mColors[0][index];
173 vertexAttributes[index].aData.color = QVector4D(color.r, color.g, color.b, color.a);
174 }
175 }
176
177 // Bones + Weights
178 if (mesh->HasBones()) {
179 for (uint i = 0; i < mesh->mNumBones; ++i) {
180 const uint vId = i;
181 for (uint j = 0; j < mesh->mBones[i]->mNumWeights; ++j) {
182 quint32 vertexId = mesh->mBones[i]->mWeights[j].mVertexId;
183 float weight = mesh->mBones[i]->mWeights[j].mWeight;
184
185 // skip a bone transform having small weight
186 if (weight <= 0.01f)
187 continue;
188
189 // if any vertex has more weights than 4, it will be ignored
190 if (vertexAttributes[vertexId].boneWeights.x() == 0.0f) {
191 vertexAttributes[vertexId].boneIndexes.x = qint32(vId);
192 vertexAttributes[vertexId].boneWeights.setX(weight);
193 } else if (vertexAttributes[vertexId].boneWeights.y() == 0.0f) {
194 vertexAttributes[vertexId].boneIndexes.y = qint32(vId);
195 vertexAttributes[vertexId].boneWeights.setY(weight);
196 } else if (vertexAttributes[vertexId].boneWeights.z() == 0.0f) {
197 vertexAttributes[vertexId].boneIndexes.z = qint32(vId);
198 vertexAttributes[vertexId].boneWeights.setZ(weight);
199 } else if (vertexAttributes[vertexId].boneWeights.w() == 0.0f) {
200 vertexAttributes[vertexId].boneIndexes.w = qint32(vId);
201 vertexAttributes[vertexId].boneWeights.setW(weight);
202 } else {
203 qWarning("vertexId %d has already 4 weights and index %d's weight %f will be ignored.", vertexId, vId, weight);
204 }
205 }
206 }
207 }
208
209 // Morph Targets
210 if (requirments.numMorphTargets > 0) {
211 for (unsigned int index = 0; index < mesh->mNumVertices; ++index) {
212 vertexAttributes[index].targetAData.resize(requirments.numMorphTargets);
213
214 for (uint i = 0; i < requirments.numMorphTargets; ++i) {
215 if (i >= mesh->mNumAnimMeshes)
216 continue;
217
218 auto animMesh = mesh->mAnimMeshes[i];
219 if (animMesh->HasPositions()) {
220 const auto vertex = animMesh->mVertices[index];
221 vertexAttributes[index].targetAData[i].position = QVector3D(vertex.x, vertex.y, vertex.z);
222 }
223 if (animMesh->HasNormals()) {
224 const auto normal = animMesh->mNormals[index];
225 vertexAttributes[index].targetAData[i].normal = QVector3D(normal.x, normal.y, normal.z);
226 }
227 if (animMesh->HasTangentsAndBitangents()) {
228 const auto tangent = animMesh->mTangents[index];
229 const auto binormal = animMesh->mBitangents[index];
230 vertexAttributes[index].targetAData[i].tangent = QVector3D(tangent.x, tangent.y, tangent.z);
231 vertexAttributes[index].targetAData[i].binormal = QVector3D(binormal.x, binormal.y, binormal.z);
232 }
233 if (animMesh->HasTextureCoords(0)) {
234 const auto texCoords = animMesh->mTextureCoords[0];
235 const auto uv = texCoords[index];
236 vertexAttributes[index].targetAData[i].uv0 = QVector3D(uv.x, uv.y, uv.z);
237 }
238 if (animMesh->HasTextureCoords(1)) {
239 const auto texCoords = animMesh->mTextureCoords[1];
240 const auto uv = texCoords[index];
241 vertexAttributes[index].targetAData[i].uv1 = QVector3D(uv.x, uv.y, uv.z);
242 }
243 if (animMesh->HasVertexColors(0)) {
244 const auto color = animMesh->mColors[0][index];
245 vertexAttributes[index].targetAData[i].color = QVector4D(color.r, color.g, color.b, color.a);
246 }
247 }
248 }
249 }
250
251 return vertexAttributes;
252}
253
263
269
271 {
272 // Position
273 if (requirments.needsPositionData)
274 vData.positionData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.position), sizeof(QVector3D));
275 // Normal
276 if (requirments.needsNormalData)
277 vData.normalData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.normal), sizeof(QVector3D));
278 // UV0
279
280 if (requirments.needsUV0Data) {
281 if (requirments.uv0Components == 2) {
282 const QVector2D uv(vertex.aData.uv0.x(), vertex.aData.uv0.y());
283 vData.uv0Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&uv), sizeof(QVector2D));
284 } else {
285 vData.uv0Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.uv0), sizeof(QVector3D));
286 }
287 }
288
289 // UV1
290 if (requirments.needsUV1Data) {
291 if (requirments.uv1Components == 2) {
292 const QVector2D uv(vertex.aData.uv1.x(), vertex.aData.uv1.y());
293 vData.uv1Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&uv), sizeof(QVector2D));
294 } else {
295 vData.uv1Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.uv1), sizeof(QVector3D));
296 }
297 }
298
299 // Tangent
300 // Binormal
301 if (requirments.needsTangentData) {
302 vData.tangentData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.tangent), sizeof(QVector3D));
303 vData.binormalData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.binormal), sizeof(QVector3D));
304 }
305
306 // Color
307 if (requirments.needsVertexColorData)
308 vData.vertexColorData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.aData.color), sizeof(QVector4D));
309
310 // Bone Indexes
311 // Bone Weights
312 if (requirments.needsBones) {
313 if (requirments.useFloatJointIndices) {
314 const QVector4D fBoneIndex(float(vertex.boneIndexes.x), float(vertex.boneIndexes.y), float(vertex.boneIndexes.z), float(vertex.boneIndexes.w));
315 boneIndexData += QByteArray::fromRawData(reinterpret_cast<const char *>(&fBoneIndex), sizeof(QVector4D));
316 } else {
317 boneIndexData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.boneIndexes), sizeof(IntVector4D));
318 }
319 boneWeightData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.boneWeights), sizeof(QVector4D));
320 }
321
322 // Morph Targets
323 for (uint i = 0; i < requirments.numMorphTargets; ++i) {
324 if (requirments.needsTargetPositionData) {
325 targetVData[i].positionData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].position), sizeof(QVector3D));
326 targetVData[i].positionData.append(sizeof(float), '\0');
327 }
328 if (requirments.needsTargetNormalData) {
329 targetVData[i].normalData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].normal), sizeof(QVector3D));
330 targetVData[i].normalData.append(sizeof(float), '\0');
331 }
332 if (requirments.needsTargetTangentData) {
333 targetVData[i].tangentData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].tangent), sizeof(QVector3D));
334 targetVData[i].tangentData.append(sizeof(float), '\0');
335 targetVData[i].binormalData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].binormal), sizeof(QVector3D));
336 targetVData[i].binormalData.append(sizeof(float), '\0');
337 }
338 if (requirments.needsTargetUV0Data) {
339 targetVData[i].uv0Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].uv0), sizeof(QVector3D));
340 targetVData[i].uv0Data.append(sizeof(float), '\0');
341 }
342 if (requirments.needsTargetUV1Data) {
343 targetVData[i].uv1Data += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].uv1), sizeof(QVector3D));
344 targetVData[i].uv1Data.append(sizeof(float), '\0');
345 }
346 if (requirments.needsTargetVertexColorData) {
347 targetVData[i].vertexColorData += QByteArray::fromRawData(reinterpret_cast<const char *>(&vertex.targetAData[i].color), sizeof(QVector4D));
348 }
349 }
350 }
351
353 QVector<QSSGMesh::AssetVertexEntry> entries;
354 if (vData.positionData.size() > 0) {
355 entries.append({
356 QSSGMesh::MeshInternal::getPositionAttrName(),
357 vData.positionData,
358 QSSGMesh::Mesh::ComponentType::Float32,
359 3
360 });
361 }
362 if (vData.normalData.size() > 0) {
363 entries.append({
364 QSSGMesh::MeshInternal::getNormalAttrName(),
365 vData.normalData,
366 QSSGMesh::Mesh::ComponentType::Float32,
367 3
368 });
369 }
370 if (vData.uv0Data.size() > 0) {
371 entries.append({
372 QSSGMesh::MeshInternal::getUV0AttrName(),
373 vData.uv0Data,
374 QSSGMesh::Mesh::ComponentType::Float32,
375 requirments.uv0Components
376 });
377 }
378 if (vData.uv1Data.size() > 0) {
379 entries.append({
380 QSSGMesh::MeshInternal::getUV1AttrName(),
381 vData.uv1Data,
382 QSSGMesh::Mesh::ComponentType::Float32,
383 requirments.uv1Components
384 });
385 }
386
387 if (vData.tangentData.size() > 0) {
388 entries.append({
389 QSSGMesh::MeshInternal::getTexTanAttrName(),
390 vData.tangentData,
391 QSSGMesh::Mesh::ComponentType::Float32,
392 3
393 });
394 }
395
396 if (vData.binormalData.size() > 0) {
397 entries.append({
398 QSSGMesh::MeshInternal::getTexBinormalAttrName(),
399 vData.binormalData,
400 QSSGMesh::Mesh::ComponentType::Float32,
401 3
402 });
403 }
404
405 if (vData.vertexColorData.size() > 0) {
406 entries.append({
407 QSSGMesh::MeshInternal::getColorAttrName(),
408 vData.vertexColorData,
409 QSSGMesh::Mesh::ComponentType::Float32,
410 4
411 });
412 }
413
414 if (boneIndexData.size() > 0) {
415 entries.append({
416 QSSGMesh::MeshInternal::getJointAttrName(),
417 boneIndexData,
418 requirments.useFloatJointIndices ? QSSGMesh::Mesh::ComponentType::Float32 : QSSGMesh::Mesh::ComponentType::Int32,
419 4
420 });
421 entries.append({
422 QSSGMesh::MeshInternal::getWeightAttrName(),
423 boneWeightData,
424 QSSGMesh::Mesh::ComponentType::Float32,
425 4
426 });
427 }
428 for (int i = 0; i < int(requirments.numMorphTargets); ++i) {
429 if (targetVData[i].positionData.size() > 0) {
430 entries.append({
431 QSSGMesh::MeshInternal::getPositionAttrName(),
432 targetVData[i].positionData,
433 QSSGMesh::Mesh::ComponentType::Float32,
434 3,
435 i
436 });
437 }
438 if (targetVData[i].normalData.size() > 0) {
439 entries.append({
440 QSSGMesh::MeshInternal::getNormalAttrName(),
441 targetVData[i].normalData,
442 QSSGMesh::Mesh::ComponentType::Float32,
443 3,
444 i
445 });
446 }
447 if (targetVData[i].tangentData.size() > 0) {
448 entries.append({
449 QSSGMesh::MeshInternal::getTexTanAttrName(),
450 targetVData[i].tangentData,
451 QSSGMesh::Mesh::ComponentType::Float32,
452 3,
453 i
454 });
455 }
456 if (targetVData[i].binormalData.size() > 0) {
457 entries.append({
458 QSSGMesh::MeshInternal::getTexBinormalAttrName(),
459 targetVData[i].binormalData,
460 QSSGMesh::Mesh::ComponentType::Float32,
461 3,
462 i
463 });
464 }
465 if (targetVData[i].uv0Data.size() > 0) {
466 entries.append({
467 QSSGMesh::MeshInternal::getUV0AttrName(),
468 targetVData[i].uv0Data,
469 QSSGMesh::Mesh::ComponentType::Float32,
470 3,
471 i
472 });
473 }
474 if (targetVData[i].uv1Data.size() > 0) {
475 entries.append({
476 QSSGMesh::MeshInternal::getUV1AttrName(),
477 targetVData[i].uv1Data,
478 QSSGMesh::Mesh::ComponentType::Float32,
479 3,
480 i
481 });
482 }
483 if (targetVData[i].vertexColorData.size() > 0) {
484 entries.append({
485 QSSGMesh::MeshInternal::getColorAttrName(),
486 targetVData[i].vertexColorData,
487 QSSGMesh::Mesh::ComponentType::Float32,
488 4,
489 i
490 });
491 }
492 }
493 return entries;
494 }
495};
496
497QVector<QSSGMesh::MeshLevelOfDetail> generateMeshLevelsOfDetail(QVector<VertexAttributeDataExt> &vertexAttributes,
498 const QVector<quint32> &indexes,
499 float normalMergeAngle = 60.0f,
500 float normalSplitAngle = 25.0f)
501{
502 QVector<QVector3D> positions;
503 positions.reserve(vertexAttributes.size());
504 QVector<QVector3D> normals;
505 normals.reserve(vertexAttributes.size());
506 for (const auto &vertex : std::as_const(vertexAttributes)) {
507 positions.append(vertex.aData.position);
508 normals.append(vertex.aData.normal);
509 }
510
511 QVector<QSSGMesh::MeshVertexSplit> splitVertices;
512 auto lods = QSSGMesh::generateMeshLevelsOfDetail(positions, normals, indexes, splitVertices, normalMergeAngle, normalSplitAngle);
513
514 // Append the new vertices created by normal splitting, in order, so the
515 // LOD index values pointing past the original vertex count stay valid.
516 for (const auto &split : std::as_const(splitVertices)) {
517 auto newVertex = vertexAttributes[split.sourceIndex];
518 newVertex.aData.normal = split.normal;
519 vertexAttributes.append(newVertex);
520 }
521
522 return lods;
523}
524
525}
526
528 const MeshList &meshes,
529 bool useFloatJointIndices,
530 bool generateLevelsOfDetail,
531 float normalMergeAngle,
532 float normalSplitAngle,
533 QString &errorString)
534{
535 Q_UNUSED(errorString);
536
537 // All Mesh subsets are stored in the same Vertex Buffer so we need to make
538 // sure that all attributes from each subset have common data by potentially
539 // adding placeholder data or doing conversions as necessary.
540 // So we need to walk through each subset first and see what the requirments are
541 VertexDataRequirments requirments;
542 requirments.useFloatJointIndices = useFloatJointIndices;
543 for (const auto *mesh : meshes)
544 requirments.collectRequirmentsForMesh(mesh);
545
546 // This is the actual data we will pass to the QSSGMesh that will get filled by
547 // each of the subset meshes
548 QByteArray indexBufferData;
549 VertexBufferDataExt vertexBufferData;
550 QVector<SubsetEntryData> subsetData;
551
552 // Since the vertex data of subsets are stored one after the other, the values in
553 // the index buffer need to be augmented to reflect this offset. baseIndex is used
554 // to track the new 0 value of a subset by keeping track of the current vertex
555 // count as each new subset is added
556 quint32 baseIndex = 0;
557
558 // Always use 32-bit indices. Metal has a requirement of 4 byte alignment
559 // for index buffer offsets, and we cannot risk hitting that.
560 const QSSGMesh::Mesh::ComponentType indexType = QSSGMesh::Mesh::ComponentType::UnsignedInt32;
561
562 for (const auto *mesh : meshes) {
563 // Get the index values for just this mesh
564 // The index values should be relative to this meshes
565 // vertices and will later need to be corrected using
566 // baseIndex to be relative to our combined vertex data
567 QVector<quint32> indexes;
568 indexes.reserve(mesh->mNumFaces * 3);
569 for (unsigned int faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
570 const auto face = mesh->mFaces[faceIndex];
571 // Faces should always have 3 indices
572 Q_ASSERT(face.mNumIndices == 3);
573 // Index data for now is relative to the local vertex locations
574 // This must be corrected for later to be global
575 indexes.append(quint32(face.mIndices[0]));
576 indexes.append(quint32(face.mIndices[1]));
577 indexes.append(quint32(face.mIndices[2]));
578 }
579
580 // Get the Vertex Attribute Data for this mesh
581 auto vertexAttributes = getVertexAttributeData(mesh, requirments);
582
583 // Starting point for index buffer offsets
584 quint32 baseIndexOffset = indexBufferData.size() / QSSGMesh::MeshInternal::byteSizeForComponentType(indexType);
585 QVector<quint32> lodIndexes;
586 QVector<QSSGMesh::Mesh::Lod> meshLods;
587
588 // Generate Automatic Mesh Levels of Detail
589 if (generateLevelsOfDetail) {
590 // Returns a list of lod pairs <distance, lodIndexList> sorted from smallest
591 // to largest as this is how they are stored in the index buffer. We still need to
592 // populate meshLods with push_front though because subset lod data is sorted from
593 // highest detail to lowest
594 auto lods = generateMeshLevelsOfDetail(vertexAttributes, indexes, normalMergeAngle, normalSplitAngle);
595 for (const auto &lodEntry : std::as_const(lods)) {
596 QSSGMesh::Mesh::Lod lod;
597 lod.offset = baseIndexOffset;
598 lod.count = lodEntry.indexes.size();
599 lod.distance = lodEntry.distance;
600 meshLods.push_front(lod);
601 baseIndexOffset += lod.count;
602 // Optimize the vertex cache for this lod level
603 auto currentLodIndexes = lodEntry.indexes;
604 QSSGMesh::optimizeVertexCache(currentLodIndexes.data(), currentLodIndexes.data(), currentLodIndexes.size(), vertexAttributes.size());
605 lodIndexes += currentLodIndexes;
606 }
607 }
608
609 // Write the results to the Global Index/Vertex/SubsetData buffers
610 // Optimize the vertex chache for the original index values
611 QSSGMesh::optimizeVertexCache(indexes.data(), indexes.data(), indexes.size(), vertexAttributes.size());
612
613 // Write Index Buffer Data
614 QVector<quint32> combinedIndexValues = lodIndexes + indexes;
615 // Set the absolute index relative to the larger vertex buffer
616 for (auto &index : combinedIndexValues)
617 index += baseIndex;
618 indexBufferData += QByteArray(reinterpret_cast<const char *>(combinedIndexValues.constData()),
619 combinedIndexValues.size() * QSSGMesh::MeshInternal::byteSizeForComponentType(indexType));
620
621 // Index Data is setup such that LOD indexes will come first
622 // from lowest quality to original
623 // | LOD3 | LOD2 | LOD1 | Original |
624 // If there were no LOD levels then indexOffset just points to that here
625 // baseIndexOffset has already been calculated to be correct at this point
626 SubsetEntryData subsetEntry;
627 subsetEntry.indexOffset = baseIndexOffset; // baseIndexOffset will be after lod indexes if available
628 subsetEntry.indexLength = indexes.size(); // Yes, only original index values, because this is for the non-lod indexes
629 subsetEntry.name = QString::fromUtf8(scene.mMaterials[mesh->mMaterialIndex]->GetName().C_Str());
630 subsetEntry.lightmapWidth = 0;
631 subsetEntry.lightmapHeight = 0;
632 subsetEntry.lods = meshLods;
633 subsetData.append(subsetEntry);
634
635 // Fill the rest of the vertex data
636 baseIndex += vertexAttributes.size(); // Final count of vertices added
637 // Increase target buffers before adding data
638 vertexBufferData.targetVData.resize(requirments.numMorphTargets);
639 for (const auto &vertex : std::as_const(vertexAttributes))
640 vertexBufferData.addVertexAttributeData(vertex, requirments);
641
642 }
643
644 // Now that we have all the data for the mesh, generate the entries list
645 QVector<QSSGMesh::AssetVertexEntry> entries = vertexBufferData.createEntries(requirments);
646
647 QVector<QSSGMesh::AssetMeshSubset> subsets;
648 for (const SubsetEntryData &subset : subsetData) {
649 subsets.append({
650 subset.name,
651 quint32(subset.indexLength),
652 quint32(subset.indexOffset),
653 0, // the builder will calculate the bounds from the position data
654 subset.lightmapWidth,
655 subset.lightmapHeight,
656 subset.lods
657 });
658 }
659
660 auto numTargetComponents = [](VertexDataRequirments req) {
661 int num = 0;
663 ++num;
665 ++num;
667 num += 2; // tangent and binormal
669 ++num;
670 if (req.needsTargetUV0Data)
671 ++num;
672 if (req.needsTargetUV1Data)
673 ++num;
674 return num;
675 };
676
677 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::fromAssetData(entries,
678 indexBufferData,
679 indexType,
680 subsets,
681 requirments.numMorphTargets,
682 numTargetComponents(requirments));
683 return mesh;
684}
685
686QT_END_NAMESPACE
QSSGMesh::Mesh generateMeshData(const aiScene &scene, const MeshList &meshes, bool useFloatJointIndices, bool generateLevelsOfDetail, float normalMergeAngle, float normalSplitAngle, QString &errorString)
QVector< QSSGMesh::MeshLevelOfDetail > generateMeshLevelsOfDetail(QVector< VertexAttributeDataExt > &vertexAttributes, const QVector< quint32 > &indexes, float normalMergeAngle=60.0f, float normalSplitAngle=25.0f)
QVector< VertexAttributeDataExt > getVertexAttributeData(const aiMesh *mesh, const VertexDataRequirments &requirments)
QVector< QSSGMesh::Mesh::Lod > lods
QVector< VertexAttributeData > targetAData
void addVertexAttributeData(const VertexAttributeDataExt &vertex, const VertexDataRequirments &requirments)
QVector< QSSGMesh::AssetVertexEntry > createEntries(const VertexDataRequirments &requirments)
QVector< VertexBufferData > targetVData
void collectRequirmentsForMesh(const aiMesh *mesh)