29struct MorphTargetData {
30 QVector<QVector3D> positions;
31 QVector<QVector3D> normals;
32 QVector<QVector3D> tangents;
36 QVector<quint32> indexes;
37 QVector<QVector3D> positions;
38 QVector<QVector3D> normals;
39 QVector<QVector3D> tangents;
40 QVector<QVector3D> binormals;
41 QVector<QVector2D> uv0;
42 QVector<QVector2D> uv1;
43 QVector<QVector4D> colors;
44 QVector<IntVector4D> joints;
45 QVector<QVector4D> weights;
46 QVector<MorphTargetData> targets;
51 bool needsNormals =
false;
52 bool needsTangents =
false;
53 bool needsUV0 =
false;
54 bool needsUV1 =
false;
55 bool needsColors =
false;
56 bool needsJoints =
false;
57 int numMorphTargets = 0;
58 bool needsTargetPositions =
false;
59 bool needsTargetNormals =
false;
60 bool needsTargetTangents =
false;
62 void collect(
const PrimitiveData &primitive)
64 needsNormals |= !primitive.normals.isEmpty();
65 needsTangents |= !primitive.tangents.isEmpty();
66 needsUV0 |= !primitive.uv0.isEmpty();
67 needsUV1 |= !primitive.uv1.isEmpty();
68 needsColors |= !primitive.colors.isEmpty();
69 needsJoints |= !primitive.joints.isEmpty();
70 numMorphTargets = qMax(numMorphTargets,
int(primitive.targets.size()));
71 for (
const MorphTargetData &target : primitive.targets) {
72 needsTargetPositions |= !target.positions.isEmpty();
73 needsTargetNormals |= !target.normals.isEmpty();
74 needsTargetTangents |= !target.tangents.isEmpty();
80QVector<quint32> toTriangleList(
int mode, QVector<quint32> indexes)
82 if (mode == MeshPrimitive::Triangles) {
83 if (
const qsizetype excess = indexes.size() % 3) {
84 qCWarning(lcQuick3DGltf) <<
"Triangle list has" << indexes.size()
85 <<
"indices, which is not a multiple of 3";
86 indexes.resize(indexes.size() - excess);
91 QVector<quint32> result;
92 if (mode == MeshPrimitive::TriangleStrip) {
93 result.reserve(qMax(0, (indexes.size() - 2)) * 3);
94 for (qsizetype i = 0; i + 2 < indexes.size(); ++i) {
95 quint32 a = indexes[i], b = indexes[i + 1], c = indexes[i + 2];
98 if (a == b || b == c || a == c)
100 result << a << b << c;
102 }
else if (mode == MeshPrimitive::TriangleFan) {
103 result.reserve(qMax(0, (indexes.size() - 2)) * 3);
104 for (qsizetype i = 1; i + 1 < indexes.size(); ++i)
105 result << indexes[0] << indexes[i] << indexes[i + 1];
110template<
typename Vec>
111QVector<Vec> toVectors(
const QList<
float> &floats,
int components)
114 const qsizetype count = floats.size() / components;
115 result.reserve(count);
116 for (qsizetype i = 0; i < count; ++i) {
118 for (
int c = 0; c < qMin(components,
int(
sizeof(Vec) /
sizeof(
float))); ++c)
119 v[c] = floats[i * components + c];
126QVector<T> deindex(
const QVector<T> &data,
const QVector<quint32> &indexes)
131 result.reserve(indexes.size());
132 for (
const quint32 index : indexes)
133 result.append(data.value(index));
138void deindexPrimitive(PrimitiveData &primitive)
140 primitive.positions = deindex(primitive.positions, primitive.indexes);
141 primitive.normals = deindex(primitive.normals, primitive.indexes);
142 primitive.tangents = deindex(primitive.tangents, primitive.indexes);
143 primitive.binormals = deindex(primitive.binormals, primitive.indexes);
144 primitive.uv0 = deindex(primitive.uv0, primitive.indexes);
145 primitive.uv1 = deindex(primitive.uv1, primitive.indexes);
146 primitive.colors = deindex(primitive.colors, primitive.indexes);
147 primitive.joints = deindex(primitive.joints, primitive.indexes);
148 primitive.weights = deindex(primitive.weights, primitive.indexes);
149 for (MorphTargetData &target : primitive.targets) {
150 target.positions = deindex(target.positions, primitive.indexes);
151 target.normals = deindex(target.normals, primitive.indexes);
152 target.tangents = deindex(target.tangents, primitive.indexes);
154 std::iota(primitive.indexes.begin(), primitive.indexes.end(), 0);
157void generateNormals(PrimitiveData &primitive,
bool smooth)
161 deindexPrimitive(primitive);
164 primitive.normals.fill(QVector3D(), primitive.positions.size());
165 for (qsizetype i = 0; i + 2 < primitive.indexes.size(); i += 3) {
166 const quint32 i0 = primitive.indexes[i], i1 = primitive.indexes[i + 1], i2 = primitive.indexes[i + 2];
167 const QVector3D faceNormal = QVector3D::crossProduct(primitive.positions[i1] - primitive.positions[i0],
168 primitive.positions[i2] - primitive.positions[i0]);
170 primitive.normals[i0] += faceNormal;
171 primitive.normals[i1] += faceNormal;
172 primitive.normals[i2] += faceNormal;
174 for (QVector3D &normal : primitive.normals)
179void generateTangents(PrimitiveData &primitive)
181 if (primitive.uv0.isEmpty() || primitive.normals.isEmpty())
183 deindexPrimitive(primitive);
185 const qsizetype cornerCount = primitive.indexes.size();
186 if (cornerCount == 0)
190 QVector<
float> generated(cornerCount * 4);
191 QSSGMesh::generateTangents(generated.data(), primitive.indexes.constData(), size_t(cornerCount),
192 reinterpret_cast<
const float *>(primitive.positions.constData()),
193 size_t(primitive.positions.size()),
sizeof(QVector3D),
194 reinterpret_cast<
const float *>(primitive.normals.constData()),
sizeof(QVector3D),
195 reinterpret_cast<
const float *>(primitive.uv0.constData()),
sizeof(QVector2D));
197 primitive.tangents.resize(cornerCount);
198 primitive.binormals.resize(cornerCount);
199 for (qsizetype i = 0; i < cornerCount; ++i) {
200 const float *t = generated.constData() + i * 4;
201 const QVector3D tangent(t[0], t[1], t[2]);
202 primitive.tangents[i] = tangent;
203 primitive.binormals[i] = QVector3D::crossProduct(primitive.normals.at(i), tangent) * t[3];
207bool materialNeedsTangents(
const QSSGGltfDocument &document,
int materialIndex)
209 if (materialIndex < 0 || materialIndex >= document.materials.size())
211 const Material &material = document.materials.at(materialIndex);
213 return material.normalTexture.isSet()
214 || (material.clearcoat && material.clearcoat->clearcoatNormalTexture.isSet())
215 || (material.anisotropy && material.anisotropy->anisotropyStrength > 0.0f);
219void weldVertices(PrimitiveData &primitive)
221 const qsizetype vertexCount = primitive.positions.size();
222 if (vertexCount == 0)
225 QVarLengthArray<QSSGMesh::MeshVertexStream, 16> streams;
226 const auto addStream = [&](
const auto &data) {
227 using Element = std::decay_t<
decltype(*data.constData())>;
229 streams.append({ data.constData(),
sizeof(Element),
sizeof(Element) });
231 addStream(primitive.positions);
232 addStream(primitive.normals);
233 addStream(primitive.tangents);
234 addStream(primitive.binormals);
235 addStream(primitive.uv0);
236 addStream(primitive.uv1);
237 addStream(primitive.colors);
238 addStream(primitive.joints);
239 addStream(primitive.weights);
240 for (
const MorphTargetData &target : std::as_const(primitive.targets)) {
241 addStream(target.positions);
242 addStream(target.normals);
243 addStream(target.tangents);
247 if (size_t(streams.size()) > QSSGMesh::maxVertexStreams) {
248 qCWarning(lcQuick3DGltf) <<
"Not welding vertices for a mesh with" << streams.size()
249 <<
"attribute streams; at most" << QSSGMesh::maxVertexStreams
254 QVector<
unsigned int> remap(vertexCount);
255 const size_t uniqueCount = QSSGMesh::generateVertexRemap(remap.data(), primitive.indexes.constData(),
256 primitive.indexes.size(), vertexCount,
257 streams.constData(), streams.size());
258 if (qsizetype(uniqueCount) == vertexCount)
261 const auto remapStream = [&](
auto &data) {
262 using Element = std::decay_t<
decltype(*data.constData())>;
265 QVector<Element> welded(uniqueCount);
266 QSSGMesh::remapVertexBuffer(welded.data(), data.constData(), vertexCount,
sizeof(Element), remap.constData());
267 data = std::move(welded);
269 remapStream(primitive.positions);
270 remapStream(primitive.normals);
271 remapStream(primitive.tangents);
272 remapStream(primitive.binormals);
273 remapStream(primitive.uv0);
274 remapStream(primitive.uv1);
275 remapStream(primitive.colors);
276 remapStream(primitive.joints);
277 remapStream(primitive.weights);
278 for (MorphTargetData &target : primitive.targets) {
279 remapStream(target.positions);
280 remapStream(target.normals);
281 remapStream(target.tangents);
284 QSSGMesh::remapIndexBuffer(primitive.indexes.data(), primitive.indexes.constData(),
285 primitive.indexes.size(), remap.constData());
288bool loadPrimitive(
const QSSGGltfDocument &document,
const MeshPrimitive &source,
291 const int positionAccessor = source.attributes.value(QByteArrayLiteral(
"POSITION"), -1);
292 if (positionAccessor < 0) {
293 qCWarning(lcQuick3DGltf) <<
"Skipping primitive without POSITION data";
297 primitive.positions = toVectors<QVector3D>(QSSGGltfAccessorReader::readAsFloats(document, positionAccessor), 3);
298 const qsizetype vertexCount = primitive.positions.size();
301 QVector<quint32> indexes;
302 if (source.indices >= 0) {
303 indexes = QVector<quint32>(QSSGGltfAccessorReader::readIndices(document, source.indices));
305 indexes.resize(vertexCount);
306 std::iota(indexes.begin(), indexes.end(), 0);
308 primitive.indexes = toTriangleList(source.mode, std::move(indexes));
309 if (primitive.indexes.isEmpty()) {
310 if (source.mode != MeshPrimitive::Triangles && source.mode != MeshPrimitive::TriangleStrip
311 && source.mode != MeshPrimitive::TriangleFan) {
312 qCWarning(lcQuick3DGltf) <<
"Skipping primitive with unsupported mode" << source.mode
313 <<
"(points and lines are not supported)";
319 for (
const quint32 index : std::as_const(primitive.indexes)) {
320 if (qsizetype(index) >= vertexCount) {
321 qCWarning(lcQuick3DGltf) <<
"Skipping primitive with out-of-range index value" << index;
327 static const QByteArray knownSemantics[] = {
328 QByteArrayLiteral(
"POSITION"), QByteArrayLiteral(
"NORMAL"), QByteArrayLiteral(
"TANGENT"),
329 QByteArrayLiteral(
"TEXCOORD_0"), QByteArrayLiteral(
"TEXCOORD_1"), QByteArrayLiteral(
"COLOR_0"),
330 QByteArrayLiteral(
"JOINTS_0"), QByteArrayLiteral(
"WEIGHTS_0"),
332 for (
auto it = source.attributes.constBegin(); it != source.attributes.constEnd(); ++it) {
333 if (std::find(std::begin(knownSemantics), std::end(knownSemantics), it.key()) == std::end(knownSemantics)) {
334 qCWarning(lcQuick3DGltf) <<
"Ignoring unsupported vertex attribute" << it.key();
338 for (
const QByteArray &semantic : knownSemantics) {
339 const int accessorIndex = source.attributes.value(semantic, -1);
340 if (accessorIndex < 0 || accessorIndex >= document.accessors.size())
342 const Accessor &accessor = document.accessors.at(accessorIndex);
343 const auto floats = [&] {
return QSSGGltfAccessorReader::readAsFloats(document, accessorIndex); };
345 if (semantic == QByteArrayLiteral(
"POSITION")) {
347 }
else if (semantic == QByteArrayLiteral(
"NORMAL")) {
348 primitive.normals = toVectors<QVector3D>(floats(), 3);
349 }
else if (semantic == QByteArrayLiteral(
"TANGENT")) {
351 if (primitive.normals.isEmpty())
354 const QList<
float> data = floats();
355 const qsizetype count = data.size() / 4;
356 primitive.tangents.reserve(count);
357 primitive.binormals.reserve(count);
358 for (qsizetype i = 0; i < count; ++i) {
359 const QVector3D tangent(data[i * 4], data[i * 4 + 1], data[i * 4 + 2]);
360 const float handedness = data[i * 4 + 3];
361 primitive.tangents.append(tangent);
362 const QVector3D normal = primitive.normals.value(i);
363 primitive.binormals.append(QVector3D::crossProduct(normal, tangent) * handedness);
365 }
else if (semantic == QByteArrayLiteral(
"TEXCOORD_0") || semantic == QByteArrayLiteral(
"TEXCOORD_1")) {
367 auto uv = toVectors<QVector2D>(floats(), Accessor::componentCount(accessor.type));
368 for (QVector2D &coord : uv)
369 coord.setY(1.0f - coord.y());
370 if (semantic == QByteArrayLiteral(
"TEXCOORD_0"))
371 primitive.uv0 = std::move(uv);
373 primitive.uv1 = std::move(uv);
374 }
else if (semantic == QByteArrayLiteral(
"COLOR_0")) {
375 const int components = Accessor::componentCount(accessor.type);
376 if (components == 3) {
377 const QList<
float> data = floats();
378 const qsizetype count = data.size() / 3;
379 primitive.colors.reserve(count);
380 for (qsizetype i = 0; i < count; ++i)
381 primitive.colors.append(QVector4D(data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 1.0f));
383 primitive.colors = toVectors<QVector4D>(floats(), 4);
385 }
else if (semantic == QByteArrayLiteral(
"JOINTS_0")) {
386 const QList<
float> data = floats();
387 const qsizetype count = data.size() / 4;
388 primitive.joints.reserve(count);
390 const auto toJointIndex = [](
float value) {
391 return value >= 0.0f && value < 2.0e9f ? qint32(value) : 0;
393 for (qsizetype i = 0; i < count; ++i) {
394 primitive.joints.append({ toJointIndex(data[i * 4]), toJointIndex(data[i * 4 + 1]),
395 toJointIndex(data[i * 4 + 2]), toJointIndex(data[i * 4 + 3]) });
397 }
else if (semantic == QByteArrayLiteral(
"WEIGHTS_0")) {
398 auto weights = toVectors<QVector4D>(floats(), 4);
400 for (QVector4D &weight : weights) {
401 const float sum = weight.x() + weight.y() + weight.z() + weight.w();
402 if (!qFuzzyIsNull(sum) && !qFuzzyCompare(sum, 1.0f))
405 primitive.weights = std::move(weights);
410 constexpr int maxMorphTargets = 8;
411 if (source.targets.size() > maxMorphTargets) {
412 qCWarning(lcQuick3DGltf) <<
"Mesh primitive has" << source.targets.size()
413 <<
"morph targets, only the first" << maxMorphTargets <<
"are used";
415 const qsizetype targetCount = qMin(qsizetype(maxMorphTargets), source.targets.size());
416 for (qsizetype targetIndex = 0; targetIndex < targetCount; ++targetIndex) {
417 const QHash<QByteArray,
int> &sourceTarget = source.targets.at(targetIndex);
418 MorphTargetData target;
419 if (
const int accessor = sourceTarget.value(QByteArrayLiteral(
"POSITION"), -1); accessor >= 0)
420 target.positions = toVectors<QVector3D>(QSSGGltfAccessorReader::readAsFloats(document, accessor), 3);
421 if (
const int accessor = sourceTarget.value(QByteArrayLiteral(
"NORMAL"), -1); accessor >= 0)
422 target.normals = toVectors<QVector3D>(QSSGGltfAccessorReader::readAsFloats(document, accessor), 3);
423 if (
const int accessor = sourceTarget.value(QByteArrayLiteral(
"TANGENT"), -1); accessor >= 0)
424 target.tangents = toVectors<QVector3D>(QSSGGltfAccessorReader::readAsFloats(document, accessor), 3);
425 primitive.targets.append(target);
429 const auto normalizeCount = [vertexCount](
auto &data,
const char *what) {
430 if (!data.isEmpty() && data.size() != vertexCount) {
431 qCWarning(lcQuick3DGltf) << what <<
"data has" << data.size() <<
"elements, expected" << vertexCount;
432 data.resize(vertexCount);
435 normalizeCount(primitive.normals,
"NORMAL");
436 normalizeCount(primitive.tangents,
"TANGENT");
437 normalizeCount(primitive.binormals,
"TANGENT");
438 normalizeCount(primitive.uv0,
"TEXCOORD_0");
439 normalizeCount(primitive.uv1,
"TEXCOORD_1");
440 normalizeCount(primitive.colors,
"COLOR_0");
441 normalizeCount(primitive.joints,
"JOINTS_0");
442 normalizeCount(primitive.weights,
"WEIGHTS_0");
443 for (MorphTargetData &target : primitive.targets) {
444 normalizeCount(target.positions,
"morph target POSITION");
445 normalizeCount(target.normals,
"morph target NORMAL");
446 normalizeCount(target.tangents,
"morph target TANGENT");
449 if (primitive.normals.isEmpty())
453 const auto anyVariantNeedsTangents = [&] {
454 if (!options.materialVariant.isEmpty() || document.materialVariants.isEmpty())
456 for (
const MeshPrimitive::VariantMapping &mapping : source.variantMappings) {
457 if (materialNeedsTangents(document, mapping.material))
462 if (primitive.tangents.isEmpty()
463 && (options.forceTangentGeneration
464 || materialNeedsTangents(document, source.effectiveMaterial(options.materialVariantIndex))
465 || anyVariantNeedsTangents())) {
466 generateTangents(primitive);
470 for (MorphTargetData &target : primitive.targets) {
471 for (qsizetype i = 0; i < target.positions.size() && i < primitive.positions.size(); ++i)
472 target.positions[i] += primitive.positions.at(i);
473 for (qsizetype i = 0; i < target.normals.size() && i < primitive.normals.size(); ++i)
474 target.normals[i] += primitive.normals.at(i);
475 for (qsizetype i = 0; i < target.tangents.size() && i < primitive.tangents.size(); ++i)
476 target.tangents[i] += primitive.tangents.at(i);
480 weldVertices(primitive);
482 if (
const int materialIndex = source.effectiveMaterial(options
.materialVariantIndex); materialIndex >= 0)
483 primitive.materialName = document.materials.at(materialIndex).name;
485 QSSGMesh::optimizeVertexCache(primitive.indexes.data(), primitive.indexes.data(),
486 primitive.indexes.size(), primitive.positions.size());
491void appendVertexData(QByteArray &buffer,
const QVector<T> &data, qsizetype vertexCount)
494 buffer.append(
reinterpret_cast<
const char *>(data.constData()), data.size() *
sizeof(T));
496 if (data.size() < vertexCount)
497 buffer.append((vertexCount - data.size()) *
sizeof(T),
'\0');
503
504
505
506
507
508
509
510
511
512
513
516 QList<
int> *usedPrimitives,
519 QVector<PrimitiveData> primitives;
520 primitives.reserve(mesh.primitives.size());
521 Requirements requirements;
522 for (qsizetype primitiveIndex = 0; primitiveIndex < mesh.primitives.size(); ++primitiveIndex) {
523 PrimitiveData primitive;
524 if (loadPrimitive(document, mesh.primitives.at(primitiveIndex), options, primitive)) {
525 requirements.collect(primitive);
526 primitives.append(std::move(primitive));
528 usedPrimitives->append(
int(primitiveIndex));
532 if (primitives.isEmpty())
535 QByteArray indexBufferData;
536 QByteArray positionData;
537 QByteArray normalData;
538 QByteArray tangentData;
539 QByteArray binormalData;
542 QByteArray colorData;
543 QByteArray jointData;
544 QByteArray weightData;
546 struct TargetBuffers {
547 QByteArray positions;
551 QVector<TargetBuffers> targetData(requirements.numMorphTargets);
552 const auto appendPaddedVec3 = [](QByteArray &buffer,
const QVector<QVector3D> &data, qsizetype vertexCount) {
553 for (
const QVector3D &v : data) {
554 const float padded[4] = { v.x(), v.y(), v.z(), 0.0f };
555 buffer.append(
reinterpret_cast<
const char *>(padded),
sizeof(padded));
557 if (data.size() < vertexCount)
558 buffer.append((vertexCount - data.size()) * 4 *
sizeof(
float),
'\0');
560 QVector<QSSGMesh::AssetMeshSubset> subsets;
563 const auto indexType = QSSGMesh::Mesh::ComponentType::UnsignedInt32;
565 const auto appendCopy = [](
auto &data, qsizetype sourceIndex) {
567 data.append(data.at(sourceIndex));
570 quint32 baseVertex = 0;
571 for (PrimitiveData &primitive : primitives) {
572 quint32 indexOffset = indexBufferData.size() / QSSGMesh::MeshInternal::byteSizeForComponentType(indexType);
575 QVector<QSSGMesh::Mesh::Lod> meshLods;
576 QVector<quint32> lodIndexes;
577 if (options.generateMeshLODs && !primitive.normals.isEmpty()) {
578 QVector<QSSGMesh::MeshVertexSplit> splitVertices;
579 const auto lods = QSSGMesh::generateMeshLevelsOfDetail(primitive.positions, primitive.normals,
580 primitive.indexes, splitVertices,
581 options.lodNormalMergeAngle,
582 options.lodNormalSplitAngle);
584 for (
const QSSGMesh::MeshVertexSplit &split : splitVertices) {
585 primitive.positions.append(primitive.positions.at(split.sourceIndex));
586 primitive.normals.append(split.normal);
587 appendCopy(primitive.tangents, split.sourceIndex);
588 appendCopy(primitive.binormals, split.sourceIndex);
589 appendCopy(primitive.uv0, split.sourceIndex);
590 appendCopy(primitive.uv1, split.sourceIndex);
591 appendCopy(primitive.colors, split.sourceIndex);
592 appendCopy(primitive.joints, split.sourceIndex);
593 appendCopy(primitive.weights, split.sourceIndex);
594 for (MorphTargetData &target : primitive.targets) {
595 appendCopy(target.positions, split.sourceIndex);
596 appendCopy(target.normals, split.sourceIndex);
597 appendCopy(target.tangents, split.sourceIndex);
600 for (
const QSSGMesh::MeshLevelOfDetail &lodEntry : lods) {
601 QSSGMesh::Mesh::Lod lod;
602 lod.offset = indexOffset;
603 lod.count = lodEntry.indexes.size();
604 lod.distance = lodEntry.distance;
605 meshLods.prepend(lod);
606 indexOffset += lod.count;
607 QVector<quint32> currentLodIndexes = lodEntry.indexes;
608 QSSGMesh::optimizeVertexCache(currentLodIndexes.data(), currentLodIndexes.data(),
609 currentLodIndexes.size(), primitive.positions.size());
610 lodIndexes += currentLodIndexes;
613 const qsizetype vertexCount = primitive.positions.size();
615 QVector<quint32> globalIndexes = lodIndexes + primitive.indexes;
616 primitive.indexes.clear();
617 for (quint32 &index : globalIndexes)
619 indexBufferData.append(
reinterpret_cast<
const char *>(globalIndexes.constData()),
620 globalIndexes.size() *
sizeof(quint32));
621 const quint32 subsetIndexCount = globalIndexes.size() - lodIndexes.size();
623 appendVertexData(positionData, primitive.positions, vertexCount);
624 if (requirements.needsNormals)
625 appendVertexData(normalData, primitive.normals, vertexCount);
626 if (requirements.needsTangents) {
627 appendVertexData(tangentData, primitive.tangents, vertexCount);
628 appendVertexData(binormalData, primitive.binormals, vertexCount);
630 if (requirements.needsUV0)
631 appendVertexData(uv0Data, primitive.uv0, vertexCount);
632 if (requirements.needsUV1)
633 appendVertexData(uv1Data, primitive.uv1, vertexCount);
634 if (requirements.needsColors)
635 appendVertexData(colorData, primitive.colors, vertexCount);
636 if (requirements.needsJoints) {
637 if (options.useFloatJointIndices) {
638 QVector<QVector4D> floatJoints;
639 floatJoints.reserve(primitive.joints.size());
640 for (
const IntVector4D &joint : std::as_const(primitive.joints))
641 floatJoints.append(QVector4D(
float(joint.x),
float(joint.y),
float(joint.z),
float(joint.w)));
642 appendVertexData(jointData, floatJoints, vertexCount);
644 appendVertexData(jointData, primitive.joints, vertexCount);
646 appendVertexData(weightData, primitive.weights, vertexCount);
649 for (
int targetIndex = 0; targetIndex < requirements.numMorphTargets; ++targetIndex) {
650 const MorphTargetData target = primitive.targets.value(targetIndex);
651 if (requirements.needsTargetPositions)
652 appendPaddedVec3(targetData[targetIndex].positions, target.positions, vertexCount);
653 if (requirements.needsTargetNormals)
654 appendPaddedVec3(targetData[targetIndex].normals, target.normals, vertexCount);
655 if (requirements.needsTargetTangents)
656 appendPaddedVec3(targetData[targetIndex].tangents, target.tangents, vertexCount);
659 subsets.append({ primitive.materialName,
665 baseVertex += vertexCount;
668 QVector<QSSGMesh::AssetVertexEntry> entries;
669 entries.append({ QSSGMesh::MeshInternal::getPositionAttrName(), positionData,
670 QSSGMesh::Mesh::ComponentType::Float32, 3 });
671 if (!normalData.isEmpty()) {
672 entries.append({ QSSGMesh::MeshInternal::getNormalAttrName(), normalData,
673 QSSGMesh::Mesh::ComponentType::Float32, 3 });
675 if (!uv0Data.isEmpty()) {
676 entries.append({ QSSGMesh::MeshInternal::getUV0AttrName(), uv0Data,
677 QSSGMesh::Mesh::ComponentType::Float32, 2 });
679 if (!uv1Data.isEmpty()) {
680 entries.append({ QSSGMesh::MeshInternal::getUV1AttrName(), uv1Data,
681 QSSGMesh::Mesh::ComponentType::Float32, 2 });
683 if (!tangentData.isEmpty()) {
684 entries.append({ QSSGMesh::MeshInternal::getTexTanAttrName(), tangentData,
685 QSSGMesh::Mesh::ComponentType::Float32, 3 });
686 entries.append({ QSSGMesh::MeshInternal::getTexBinormalAttrName(), binormalData,
687 QSSGMesh::Mesh::ComponentType::Float32, 3 });
689 if (!colorData.isEmpty()) {
690 entries.append({ QSSGMesh::MeshInternal::getColorAttrName(), colorData,
691 QSSGMesh::Mesh::ComponentType::Float32, 4 });
693 if (!jointData.isEmpty()) {
694 entries.append({ QSSGMesh::MeshInternal::getJointAttrName(), jointData,
695 options.useFloatJointIndices ? QSSGMesh::Mesh::ComponentType::Float32
696 : QSSGMesh::Mesh::ComponentType::Int32, 4 });
697 entries.append({ QSSGMesh::MeshInternal::getWeightAttrName(), weightData,
698 QSSGMesh::Mesh::ComponentType::Float32, 4 });
702 int numTargetComps = 0;
703 if (requirements.numMorphTargets > 0) {
704 numTargetComps =
int(requirements.needsTargetPositions) +
int(requirements.needsTargetNormals)
705 +
int(requirements.needsTargetTangents);
706 for (
int targetIndex = 0; targetIndex < requirements.numMorphTargets; ++targetIndex) {
707 if (requirements.needsTargetPositions) {
708 entries.append({ QSSGMesh::MeshInternal::getPositionAttrName(), targetData[targetIndex].positions,
709 QSSGMesh::Mesh::ComponentType::Float32, 3, targetIndex });
711 if (requirements.needsTargetNormals) {
712 entries.append({ QSSGMesh::MeshInternal::getNormalAttrName(), targetData[targetIndex].normals,
713 QSSGMesh::Mesh::ComponentType::Float32, 3, targetIndex });
715 if (requirements.needsTargetTangents) {
716 entries.append({ QSSGMesh::MeshInternal::getTexTanAttrName(), targetData[targetIndex].tangents,
717 QSSGMesh::Mesh::ComponentType::Float32, 3, targetIndex });
723 morphInfo
->count = requirements.numMorphTargets;
725 morphInfo
->hasNormals = requirements.needsTargetNormals;
729 return QSSGMesh::Mesh::fromAssetData(entries, indexBufferData, indexType, subsets,
730 quint32(requirements.numMorphTargets), quint32(numTargetComps));