Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qssgmesh.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qssgmesh_p.h"
5
6#include <QtCore/QVector>
7#include <QtQuick3DUtils/private/qssgdataref_p.h>
8#include <QtQuick3DUtils/private/qssglightmapuvgenerator_p.h>
9
10#include "meshoptimizer.h"
11
12#include <algorithm>
13
15
16namespace QSSGMesh {
17
18// fileId, fileVersion, offset, count
19static const size_t MULTI_HEADER_STRUCT_SIZE = 16;
20
21// meshOffset, meshId, padding
22static const size_t MULTI_ENTRY_STRUCT_SIZE = 16;
23
24// fileId, fileVersion, flags, size
25static const size_t MESH_HEADER_STRUCT_SIZE = 12;
26
27// vertexBuffer, indexBuffer, subsets, joints, drawMode, winding
28static const size_t MESH_STRUCT_SIZE = 56;
29
30// vertex buffer entry list: nameOffset, componentType, componentCount, offset
31static const size_t VERTEX_BUFFER_ENTRY_STRUCT_SIZE = 16;
32
33// subset list: count, offset, minXYZ, maxXYZ, nameOffset, nameLength
34static const size_t SUBSET_STRUCT_SIZE_V3_V4 = 40;
35// subset list: count, offset, minXYZ, maxXYZ, nameOffset, nameLength, lightmapSizeWidth, lightmapSizeHeight
36static const size_t SUBSET_STRUCT_SIZE_V5 = 48;
37// subset list: count, offset, minXYZ, maxXYZ, nameOffset, nameLength, lightmapSizeWidth, lightmapSizeHeight, lodCount
38static const size_t SUBSET_STRUCT_SIZE_V6 = 52;
39
40//lod entry: count, offset, distance
41static const size_t LOD_STRUCT_SIZE = 12;
42
44{
45 const qint64 multiHeaderStartOffset = device->size() - qint64(MULTI_HEADER_STRUCT_SIZE);
46
47 device->seek(multiHeaderStartOffset);
48 QDataStream inputStream(device);
50 inputStream.setFloatingPointPrecision(QDataStream::SinglePrecision);
51
52 MultiMeshInfo meshFileInfo;
53 inputStream >> meshFileInfo.fileId >> meshFileInfo.fileVersion;
54
55 if (!meshFileInfo.isValid()) {
56 qWarning("Mesh file invalid");
57 return {};
58 }
59
60 quint32 multiEntriesOffset; // unused, the entry list is right before the header
61 quint32 meshCount;
62 inputStream >> multiEntriesOffset >> meshCount;
63
64 for (quint32 i = 0; i < meshCount; ++i) {
65 device->seek(multiHeaderStartOffset
66 - (qint64(MULTI_ENTRY_STRUCT_SIZE) * meshCount)
69 quint32 id;
70 inputStream >> offset >> id;
71 meshFileInfo.meshEntries.insert(id, offset);
72 }
73
74 return meshFileInfo;
75}
76
78{
79 QDataStream outputStream(device);
81 outputStream.setFloatingPointPrecision(QDataStream::SinglePrecision);
82
83 const quint32 multiEntriesOffset = device->pos();
84 for (auto it = meshFileInfo.meshEntries.cbegin(), end = meshFileInfo.meshEntries.cend(); it != end; ++it) {
85 const quint32 id = it.key();
86 const quint64 offset = it.value();
87 const quint32 padding = 0;
88 outputStream << offset << id << padding;
89 }
90
91 const quint32 meshCount = meshFileInfo.meshEntries.size();
92 outputStream << meshFileInfo.fileId << meshFileInfo.fileVersion << multiEntriesOffset << meshCount;
93}
94
96{
97 static char alignPadding[4] = {};
98
99 device->seek(offset);
100 QDataStream inputStream(device);
102 inputStream.setFloatingPointPrecision(QDataStream::SinglePrecision);
103
104 inputStream >> header->fileId >> header->fileVersion >> header->flags >> header->sizeInBytes;
105 if (!header->isValid()) {
106 qWarning() << "Mesh data invalid";
107 if (header->fileId == MeshDataHeader::FILE_ID) {
108 if (header->fileVersion > MeshDataHeader::FILE_VERSION)
109 qWarning() << "File version " << header->fileVersion << " newer than " << MeshDataHeader::FILE_VERSION;
111 qWarning() << "File version " << header->fileVersion << " older than " << MeshDataHeader::LEGACY_MESH_FILE_VERSION;
112 } else {
113 qWarning() << "Invalid file ID" << header->fileId;
114 }
115 return 0;
116 }
117
119 Q_ASSERT(offsetTracker.offset() == device->pos());
120
121 quint32 targetBufferEntriesCount;
122 quint32 vertexBufferEntriesCount;
123 quint32 targetBufferDataSize;
124 quint32 vertexBufferDataSize;
125 inputStream >> targetBufferEntriesCount
126 >> vertexBufferEntriesCount
127 >> mesh->m_vertexBuffer.stride
128 >> targetBufferDataSize
129 >> vertexBufferDataSize;
130
131 if (!header->hasSeparateTargetBuffer()) {
132 targetBufferEntriesCount = 0;
133 targetBufferDataSize = 0;
134 }
135
136 quint32 indexBufferComponentType;
137 quint32 indexBufferDataOffset;
138 quint32 indexBufferDataSize;
139 inputStream >> indexBufferComponentType
140 >> indexBufferDataOffset
141 >> indexBufferDataSize;
142 mesh->m_indexBuffer.componentType = Mesh::ComponentType(indexBufferComponentType);
143
144 quint32 targetCount;
145 quint32 subsetsCount;
146 inputStream >> targetCount >> subsetsCount;
147 mesh->m_targetBuffer.numTargets = targetCount;
148
149 quint32 jointsOffsets; // unused
150 quint32 jointsCount; // unused
151 inputStream >> jointsOffsets >> jointsCount;
152 quint32 drawMode;
153 quint32 winding;
154 inputStream >> drawMode >> winding;
155 mesh->m_drawMode = Mesh::DrawMode(drawMode);
156 mesh->m_winding = Mesh::Winding(winding);
157
158 offsetTracker.advance(MESH_STRUCT_SIZE);
159
160 quint32 entriesByteSize = 0;
161 for (quint32 i = 0; i < vertexBufferEntriesCount; ++i) {
162 Mesh::VertexBufferEntry vertexBufferEntry;
163 quint32 componentType;
164 quint32 nameOffset; // unused
165 inputStream >> nameOffset
166 >> componentType
167 >> vertexBufferEntry.componentCount
168 >> vertexBufferEntry.offset;
169 vertexBufferEntry.componentType = Mesh::ComponentType(componentType);
170 mesh->m_vertexBuffer.entries.append(vertexBufferEntry);
171 entriesByteSize += VERTEX_BUFFER_ENTRY_STRUCT_SIZE;
172 }
173 quint32 alignAmount = offsetTracker.alignedAdvance(entriesByteSize);
174 if (alignAmount)
175 device->read(alignPadding, alignAmount);
176
177 // vertex buffer entry names
178 quint32 numTargets = 0;
179 // used for recording the target attributes supported by the mesh
180 // and re-construting it when meeting attr_unsupported
181 QList<QByteArray> attrNames;
182 for (auto &entry : mesh->m_vertexBuffer.entries) {
183 quint32 nameLength;
184 inputStream >> nameLength;
185 offsetTracker.advance(sizeof(quint32));
186 const QByteArray nameWithZeroTerminator = device->read(nameLength);
187 entry.name = QByteArray(nameWithZeroTerminator.constData(), qMax(0, nameWithZeroTerminator.size() - 1));
188 alignAmount = offsetTracker.alignedAdvance(nameLength);
189 if (alignAmount)
190 device->read(alignPadding, alignAmount);
191 // Old morph meshes' target attributes were appended sequentially
192 // behind vertex attributes. However, since the number of targets are restricted by 8
193 // the other attributes were named by "attr_unsupported"
194 // So just checking numTargets is safe with the above assumption and
195 // it will try to reconstruct the unsupported attributes.
196 if (numTargets > 0 || (!header->hasSeparateTargetBuffer() && entry.name.startsWith("attr_t"))) {
197 if (entry.name.sliced(6).startsWith("pos")) {
198 const quint32 targetId = entry.name.mid(9).toUInt();
199 // All the attributes of the first target should be recorded correctly.
200 if (targetId == 0)
201 attrNames.append(MeshInternal::getPositionAttrName());
202 numTargets = qMax(numTargets, targetId + 1);
204 mesh->m_targetBuffer.entries.append(entry);
205 targetBufferEntriesCount++;
206 } else if (entry.name.sliced(6).startsWith("norm")) {
207 const quint32 targetId = entry.name.mid(10).toUInt();
208 if (targetId == 0)
209 attrNames.append(MeshInternal::getNormalAttrName());
210 numTargets = qMax(numTargets, targetId + 1);
212 mesh->m_targetBuffer.entries.append(entry);
213 targetBufferEntriesCount++;
214 } else if (entry.name.sliced(6).startsWith("tan")) {
215 const quint32 targetId = entry.name.mid(9).toUInt();
216 if (targetId == 0)
217 attrNames.append(MeshInternal::getTexTanAttrName());
218 numTargets = qMax(numTargets, targetId + 1);
220 mesh->m_targetBuffer.entries.append(entry);
221 targetBufferEntriesCount++;
222 } else if (entry.name.sliced(6).startsWith("binorm")) {
223 const quint32 targetId = entry.name.mid(12).toUInt();
224 if (targetId == 0)
225 attrNames.append(MeshInternal::getTexBinormalAttrName());
226 numTargets = qMax(numTargets, targetId + 1);
228 mesh->m_targetBuffer.entries.append(entry);
229 targetBufferEntriesCount++;
230 } else if (entry.name.startsWith("attr_unsupported")) {
231 // Reconstruct
232 entry.name = attrNames[targetBufferEntriesCount % attrNames.size()];
233 mesh->m_targetBuffer.entries.append(entry);
234 targetBufferEntriesCount++;
235 }
236 }
237 }
238
239 mesh->m_vertexBuffer.data = device->read(vertexBufferDataSize);
240 alignAmount = offsetTracker.alignedAdvance(vertexBufferDataSize);
241 if (alignAmount)
242 device->read(alignPadding, alignAmount);
243
244 mesh->m_indexBuffer.data = device->read(indexBufferDataSize);
245 alignAmount = offsetTracker.alignedAdvance(indexBufferDataSize);
246 if (alignAmount)
247 device->read(alignPadding, alignAmount);
248
249 quint32 subsetByteSize = 0;
250 QVector<MeshInternal::Subset> internalSubsets;
251 for (quint32 i = 0; i < subsetsCount; ++i) {
253 float minX;
254 float minY;
255 float minZ;
256 float maxX;
257 float maxY;
258 float maxZ;
259 quint32 nameOffset; // unused
260 inputStream >> subset.count
261 >> subset.offset
262 >> minX
263 >> minY
264 >> minZ
265 >> maxX
266 >> maxY
267 >> maxZ
268 >> nameOffset
269 >> subset.nameLength;
270 subset.bounds.min = QVector3D(minX, minY, minZ);
271 subset.bounds.max = QVector3D(maxX, maxY, maxZ);
272 if (header->hasLightmapSizeHint()) {
273 quint32 width = 0;
274 quint32 height = 0;
275 inputStream >> width >> height;
277 if (header->hasLodDataHint()) {
278 quint32 lodCount = 0;
279 inputStream >> lodCount;
280 subset.lodCount = lodCount;
281 subsetByteSize += SUBSET_STRUCT_SIZE_V6;
282 } else {
283 subsetByteSize += SUBSET_STRUCT_SIZE_V5;
284 }
285 } else {
286 subset.lightmapSizeHint = QSize(0, 0);
287 subsetByteSize += SUBSET_STRUCT_SIZE_V3_V4;
288 }
289 internalSubsets.append(subset);
290
291 }
292 alignAmount = offsetTracker.alignedAdvance(subsetByteSize);
293 if (alignAmount)
294 device->read(alignPadding, alignAmount);
295
296 for (MeshInternal::Subset &internalSubset : internalSubsets) {
297 internalSubset.rawNameUtf16 = device->read(internalSubset.nameLength * 2); //UTF_16_le
298 alignAmount = offsetTracker.alignedAdvance(internalSubset.nameLength * 2);
299 if (alignAmount)
300 device->read(alignPadding, alignAmount);
301 }
302
303 quint32 lodByteSize = 0;
304 for (const MeshInternal::Subset &internalSubset : internalSubsets) {
305 auto meshSubset = internalSubset.toMeshSubset();
306 // Read Level of Detail data here
307 for (auto &lod : meshSubset.lods) {
308 quint32 count = 0;
309 quint32 offset = 0;
310 float distance = 0.0;
311 inputStream >> count >> offset >> distance;
312 lod.count = count;
313 lod.offset = offset;
314 lod.distance = distance;
315 lodByteSize += LOD_STRUCT_SIZE;
316 }
317
318 mesh->m_subsets.append(meshSubset);
319 }
320 alignAmount = offsetTracker.alignedAdvance(lodByteSize);
321 if (alignAmount)
322 device->read(alignPadding, alignAmount);
323
324
325 // Data for morphTargets
326 if (targetBufferEntriesCount > 0) {
327 if (header->hasSeparateTargetBuffer()) {
328 entriesByteSize = 0;
329 for (quint32 i = 0; i < targetBufferEntriesCount; ++i) {
330 Mesh::VertexBufferEntry targetBufferEntry;
331 quint32 componentType;
332 quint32 nameOffset; // unused
333 inputStream >> nameOffset
334 >> componentType
335 >> targetBufferEntry.componentCount
336 >> targetBufferEntry.offset;
337 targetBufferEntry.componentType = Mesh::ComponentType(componentType);
338 mesh->m_targetBuffer.entries.append(targetBufferEntry);
339 entriesByteSize += VERTEX_BUFFER_ENTRY_STRUCT_SIZE;
340 }
341 alignAmount = offsetTracker.alignedAdvance(entriesByteSize);
342 if (alignAmount)
343 device->read(alignPadding, alignAmount);
344
345 for (auto &entry : mesh->m_targetBuffer.entries) {
346 quint32 nameLength;
347 inputStream >> nameLength;
348 offsetTracker.advance(sizeof(quint32));
349 const QByteArray nameWithZeroTerminator = device->read(nameLength);
350 entry.name = QByteArray(nameWithZeroTerminator.constData(), qMax(0, nameWithZeroTerminator.size() - 1));
351 alignAmount = offsetTracker.alignedAdvance(nameLength);
352 if (alignAmount)
353 device->read(alignPadding, alignAmount);
354 }
355
356 mesh->m_targetBuffer.data = device->read(targetBufferDataSize);
357 } else {
358 // remove target entries from vertexbuffer entries
359 mesh->m_vertexBuffer.entries.remove(vertexBufferEntriesCount - targetBufferEntriesCount,
360 targetBufferEntriesCount);
361 const quint32 vertexCount = vertexBufferDataSize / mesh->m_vertexBuffer.stride;
362 const quint32 targetEntryTexWidth = qCeil(qSqrt(vertexCount));
363 const quint32 targetCompStride = targetEntryTexWidth * targetEntryTexWidth * 4 * sizeof(float);
364 mesh->m_targetBuffer.data.resize(targetCompStride * targetBufferEntriesCount);
365 const quint32 numComps = targetBufferEntriesCount / numTargets;
366 for (quint32 i = 0; i < targetBufferEntriesCount; ++i) {
367 auto &entry = mesh->m_targetBuffer.entries[i];
368 char *dstBuf = mesh->m_targetBuffer.data.data()
369 + (i / numComps) * targetCompStride
370 + (i % numComps) * (targetCompStride * numTargets);
371 const char *srcBuf = mesh->m_vertexBuffer.data.constData() + entry.offset;
372 for (quint32 j = 0; j < vertexCount; ++j) {
373 // The number of old target components is fixed as 3
374 memcpy(dstBuf + j * 4 * sizeof(float),
375 srcBuf + j * mesh->m_vertexBuffer.stride,
376 3 * sizeof(float));
377 }
378 entry.offset = i * targetCompStride;
379 }
380 // now we don't need to have redundant targetbuffer entries
381 mesh->m_targetBuffer.entries.remove(numComps, targetBufferEntriesCount - numComps);
382 mesh->m_targetBuffer.numTargets = numTargets;
383 }
384 }
385
386 return header->sizeInBytes;
387}
388
390{
391 QDataStream outputStream(device);
393 outputStream.setFloatingPointPrecision(QDataStream::SinglePrecision);
394
395 outputStream << header.fileId << header.fileVersion << header.flags << header.sizeInBytes;
396}
397
398// The legacy, now-removed, insane mesh code used to use a "serialization"
399// strategy with dumping memory, yet combined with with an in-memory layout
400// that is different from what's in the file. In version 4 we no longer write
401// out valid offset values (see the // legacy offset comments), because the new
402// loader does not need them, and calculating them is not sensible, especially
403// due to the different layouts. We still do the alignment padding, even though
404// that's also legacy nonsense, but having that allows the reader not have to
405// branch based on the version.
406
408{
409 static const char alignPadding[4] = {};
410
411 QDataStream outputStream(device);
413 outputStream.setFloatingPointPrecision(QDataStream::SinglePrecision);
414
415 const qint64 startPos = device->pos();
416 MeshInternal::MeshOffsetTracker offsetTracker(startPos);
417 Q_ASSERT(offsetTracker.offset() == device->pos());
418
419 const quint32 vertexBufferEntriesCount = mesh.m_vertexBuffer.entries.size();
420 const quint32 vertexBufferDataSize = mesh.m_vertexBuffer.data.size();
421 const quint32 vertexBufferStride = mesh.m_vertexBuffer.stride;
422 const quint32 targetBufferEntriesCount = mesh.m_targetBuffer.entries.count();
423 const quint32 targetBufferDataSize = mesh.m_targetBuffer.data.size();
424 outputStream << targetBufferEntriesCount
425 << vertexBufferEntriesCount
426 << vertexBufferStride;
427 outputStream << targetBufferDataSize
428 << vertexBufferDataSize;
429
430 const quint32 indexBufferDataSize = mesh.m_indexBuffer.data.size();
431 const quint32 indexComponentType = quint32(mesh.m_indexBuffer.componentType);
432 outputStream << indexComponentType;
433 outputStream << quint32(0) // legacy offset
434 << indexBufferDataSize;
435
436 const quint32 targetCount = mesh.m_targetBuffer.numTargets;
437 const quint32 subsetsCount = mesh.m_subsets.size();
438 outputStream << targetCount
439 << subsetsCount;
440
441 outputStream << quint32(0) // legacy offset
442 << quint32(0); // legacy jointsCount
443
444 const quint32 drawMode = quint32(mesh.m_drawMode);
445 const quint32 winding = quint32(mesh.m_winding);
446 outputStream << drawMode
447 << winding;
448
449 offsetTracker.advance(MESH_STRUCT_SIZE);
450
451 quint32 entriesByteSize = 0;
452 for (quint32 i = 0; i < vertexBufferEntriesCount; ++i) {
453 const Mesh::VertexBufferEntry &entry(mesh.m_vertexBuffer.entries[i]);
454 const quint32 componentType = quint32(entry.componentType);
455 const quint32 componentCount = entry.componentCount;
456 const quint32 offset = entry.offset;
457 outputStream << quint32(0) // legacy offset
458 << componentType
459 << componentCount
460 << offset;
461 entriesByteSize += VERTEX_BUFFER_ENTRY_STRUCT_SIZE;
462 }
463 quint32 alignAmount = offsetTracker.alignedAdvance(entriesByteSize);
464 if (alignAmount)
465 device->write(alignPadding, alignAmount);
466
467 for (quint32 i = 0; i < vertexBufferEntriesCount; ++i) {
468 const Mesh::VertexBufferEntry &entry(mesh.m_vertexBuffer.entries[i]);
469 const quint32 nameLength = entry.name.size() + 1;
470 outputStream << nameLength;
471 device->write(entry.name.constData(), nameLength); // with zero terminator included
472 alignAmount = offsetTracker.alignedAdvance(sizeof(quint32) + nameLength);
473 if (alignAmount)
474 device->write(alignPadding, alignAmount);
475 }
476
477 device->write(mesh.m_vertexBuffer.data.constData(), vertexBufferDataSize);
478 alignAmount = offsetTracker.alignedAdvance(vertexBufferDataSize);
479 if (alignAmount)
480 device->write(alignPadding, alignAmount);
481
482 device->write(mesh.m_indexBuffer.data.constData(), indexBufferDataSize);
483 alignAmount = offsetTracker.alignedAdvance(indexBufferDataSize);
484 if (alignAmount)
485 device->write(alignPadding, alignAmount);
486
487 quint32 subsetByteSize = 0;
488 for (quint32 i = 0; i < subsetsCount; ++i) {
489 const Mesh::Subset &subset(mesh.m_subsets[i]);
490 const quint32 subsetCount = subset.count;
491 const quint32 subsetOffset = subset.offset;
492 const float minX = subset.bounds.min.x();
493 const float minY = subset.bounds.min.y();
494 const float minZ = subset.bounds.min.z();
495 const float maxX = subset.bounds.max.x();
496 const float maxY = subset.bounds.max.y();
497 const float maxZ = subset.bounds.max.z();
498 const quint32 nameLength = subset.name.size() + 1;
499 const quint32 lightmapSizeHintWidth = qMax(0, subset.lightmapSizeHint.width());
500 const quint32 lightmapSizeHintHeight = qMax(0, subset.lightmapSizeHint.height());
501 const quint32 lodCount = subset.lods.size();
502 outputStream << subsetCount
503 << subsetOffset
504 << minX
505 << minY
506 << minZ
507 << maxX
508 << maxY
509 << maxZ;
510 outputStream << quint32(0) // legacy offset
511 << nameLength;
512 outputStream << lightmapSizeHintWidth
513 << lightmapSizeHintHeight;
514 outputStream << lodCount;
515 subsetByteSize += SUBSET_STRUCT_SIZE_V6;
516 }
517 alignAmount = offsetTracker.alignedAdvance(subsetByteSize);
518 if (alignAmount)
519 device->write(alignPadding, alignAmount);
520
521 for (quint32 i = 0; i < subsetsCount; ++i) {
522 const Mesh::Subset &subset(mesh.m_subsets[i]);
523 const char *utf16Name = reinterpret_cast<const char *>(subset.name.utf16());
524 const quint32 nameByteSize = (subset.name.size() + 1) * 2;
525 device->write(utf16Name, nameByteSize);
526 alignAmount = offsetTracker.alignedAdvance(nameByteSize);
527 if (alignAmount)
528 device->write(alignPadding, alignAmount);
529 }
530
531 // LOD data
532 quint32 lodDataByteSize = 0;
533 for (quint32 i = 0; i < subsetsCount; ++i) {
534 const Mesh::Subset &subset(mesh.m_subsets[i]);
535 for (auto lod : subset.lods) {
536 const quint32 count = lod.count;
537 const quint32 offset = lod.offset;
538 const float distance = lod.distance;
539 outputStream << count << offset << distance;
540 lodDataByteSize += LOD_STRUCT_SIZE;
541 }
542 }
543 alignAmount = offsetTracker.alignedAdvance(lodDataByteSize);
544 if (alignAmount)
545 device->write(alignPadding, alignAmount);
546
547 // Data for morphTargets
548 for (quint32 i = 0; i < targetBufferEntriesCount; ++i) {
549 const Mesh::VertexBufferEntry &entry(mesh.m_targetBuffer.entries[i]);
550 const quint32 componentType = quint32(entry.componentType);
551 const quint32 componentCount = entry.componentCount;
552 const quint32 offset = entry.offset;
553 outputStream << quint32(0) // legacy offset
554 << componentType
555 << componentCount
556 << offset;
557 entriesByteSize += VERTEX_BUFFER_ENTRY_STRUCT_SIZE;
558 }
559 alignAmount = offsetTracker.alignedAdvance(entriesByteSize);
560 if (alignAmount)
561 device->write(alignPadding, alignAmount);
562
563 for (quint32 i = 0; i < targetBufferEntriesCount; ++i) {
564 const Mesh::VertexBufferEntry &entry(mesh.m_targetBuffer.entries[i]);
565 const quint32 nameLength = entry.name.size() + 1;
566 outputStream << nameLength;
567 device->write(entry.name.constData(), nameLength); // with zero terminator included
568 alignAmount = offsetTracker.alignedAdvance(sizeof(quint32) + nameLength);
569 if (alignAmount)
570 device->write(alignPadding, alignAmount);
571 }
572
573 device->write(mesh.m_targetBuffer.data.constData(), targetBufferDataSize);
574
575 const quint32 endPos = device->pos();
576 const quint32 sizeInBytes = endPos - startPos;
577 device->seek(endPos);
578 return sizeInBytes;
579}
580
582{
585 auto it = meshFileInfo.meshEntries.constFind(id);
586 if (it != meshFileInfo.meshEntries.constEnd()) {
587 Mesh mesh;
589 if (size)
590 return mesh;
591 } else if (id == 0 && !meshFileInfo.meshEntries.isEmpty()) {
592 Mesh mesh;
593 quint64 size = MeshInternal::readMeshData(device, *meshFileInfo.meshEntries.cbegin(), &mesh, &header);
594 if (size)
595 return mesh;
596 }
597 return Mesh();
598}
599
600QMap<quint32, Mesh> Mesh::loadAll(QIODevice *device)
601{
604 QMap<quint32, Mesh> meshes;
605 for (auto it = meshFileInfo.meshEntries.cbegin(), end = meshFileInfo.meshEntries.cend(); it != end; ++it) {
606 Mesh mesh;
608 if (size)
609 meshes.insert(it.key(), mesh);
610 else
611 qWarning("Failed to find mesh #%u", it.key());
612 }
613 return meshes;
614}
615
617{
618 Q_ASSERT(align > 0);
619 const quint32 leftover = (align > 0) ? offset % align : 0;
620 if (leftover)
621 return offset + (align - leftover);
622 return offset;
623}
624
625Mesh Mesh::fromAssetData(const QVector<AssetVertexEntry> &vbufEntries,
626 const QByteArray &indexBufferData,
627 ComponentType indexComponentType,
628 const QVector<AssetMeshSubset> &subsets,
629 quint32 numTargets,
630 quint32 numTargetComps)
631{
632 Mesh mesh;
633 quint32 currentOffset = 0;
634 quint32 bufferAlignment = 0;
635 quint32 numItems = 0;
636 bool ok = true;
637
638 mesh.m_targetBuffer.numTargets = numTargets;
639 quint32 targetCurrentComp = 0;
640 quint32 targetCompStride = 0;
641
642 QVector<AssetVertexEntry> vEntries;
643 for (const AssetVertexEntry &entry : vbufEntries) {
644 // Ignore entries with no data.
645 if (entry.data.isEmpty())
646 continue;
647
648 VertexBufferEntry meshEntry;
649 meshEntry.componentType = entry.componentType;
650 meshEntry.componentCount = entry.componentCount;
651 meshEntry.name = entry.name;
652
653 if (entry.morphTargetId < 0) {
655 const quint32 byteSize = alignment * entry.componentCount;
656
657 if (entry.data.size() % alignment != 0) {
658 Q_ASSERT(false);
659 ok = false;
660 }
661
662 quint32 localNumItems = entry.data.size() / byteSize;
663 if (numItems == 0) {
664 numItems = localNumItems;
665 } else if (numItems != localNumItems) {
666 Q_ASSERT(false);
667 ok = false;
668 numItems = qMin(numItems, localNumItems);
669 }
670
671 currentOffset = getAlignedOffset(currentOffset, alignment);
672 meshEntry.offset = currentOffset;
673
674 mesh.m_vertexBuffer.entries.append(meshEntry);
675 currentOffset += byteSize;
676 bufferAlignment = qMax(bufferAlignment, alignment);
677 vEntries.append(entry);
678 } else {
679 if (!targetCompStride) {
680 const quint32 targetEntrySize = entry.data.size();
681 quint32 targetEntryTexWidth = qCeil(qSqrt(((targetEntrySize + 15) >> 4)));
682 targetCompStride = targetEntryTexWidth * targetEntryTexWidth * 4 * sizeof(float);
683 mesh.m_targetBuffer.data.resize(targetCompStride * numTargets * numTargetComps);
684 }
685
686 // At assets, these entries are appended sequentially from target 0 to target N - 1
687 // It is safe to calculate the offset by the data size
688 meshEntry.offset = (targetCurrentComp * numTargets + entry.morphTargetId)
689 * targetCompStride;
690 memcpy(mesh.m_targetBuffer.data.data() + meshEntry.offset,
691 entry.data.constData(), entry.data.size());
692
693 // Note: the targetBuffer will not be interleaved,
694 // data will be just appended in order and used for a texture array.
695 if (entry.morphTargetId == 0)
696 mesh.m_targetBuffer.entries.append(meshEntry);
697
698 targetCurrentComp = (targetCurrentComp + 1 < numTargetComps) ? targetCurrentComp + 1 : 0;
699 }
700 }
701
702 if (!ok)
703 return Mesh();
704
705 mesh.m_vertexBuffer.stride = getAlignedOffset(currentOffset, bufferAlignment);
706
707 // Packed interleave the data.
708 for (quint32 idx = 0; idx < numItems; ++idx) {
709 quint32 dataOffset = 0;
710 for (const AssetVertexEntry &entry : vEntries) {
711 if (entry.data.isEmpty())
712 continue;
713
715 const quint32 byteSize = alignment * entry.componentCount;
716 const quint32 offset = byteSize * idx;
717 const quint32 newOffset = getAlignedOffset(dataOffset, alignment);
718 if (newOffset != dataOffset) {
719 QByteArray filler(newOffset - dataOffset, '\0');
720 mesh.m_vertexBuffer.data.append(filler);
721 }
722
723 mesh.m_vertexBuffer.data.append(entry.data.constData() + offset, byteSize);
724 dataOffset = newOffset + byteSize;
725 }
726 Q_ASSERT(dataOffset == mesh.m_vertexBuffer.stride);
727 }
728
729 mesh.m_indexBuffer.componentType = indexComponentType;
730 mesh.m_indexBuffer.data = indexBufferData;
731
732 for (const AssetMeshSubset &subset : subsets) {
733 Mesh::Subset meshSubset;
734 meshSubset.name = subset.name;
735 meshSubset.count = subset.count;
736 meshSubset.offset = subset.offset;
737
738 // TODO: QTBUG-102026
739 if (subset.boundsPositionEntryIndex != std::numeric_limits<quint32>::max()) {
741 mesh.m_vertexBuffer.entries[subset.boundsPositionEntryIndex],
742 mesh.m_vertexBuffer.data,
743 mesh.m_vertexBuffer.stride,
744 mesh.m_indexBuffer.data,
745 mesh.m_indexBuffer.componentType,
746 subset.count,
747 subset.offset);
748 meshSubset.bounds.min = bounds.minimum;
749 meshSubset.bounds.max = bounds.maximum;
750 }
751
752 meshSubset.lightmapSizeHint = QSize(subset.lightmapWidth, subset.lightmapHeight);
753 meshSubset.lods = subset.lods;
754
755 mesh.m_subsets.append(meshSubset);
756 }
757
758 mesh.m_drawMode = DrawMode::Triangles;
759 mesh.m_winding = Winding::CounterClockwise;
760
761 return mesh;
762}
763
765{
766 if (data.m_vertexBuffer.size() == 0) {
767 *error = QObject::tr("Vertex buffer empty");
768 return Mesh();
769 }
770 if (data.m_attributeCount == 0) {
771 *error = QObject::tr("No attributes defined");
772 return Mesh();
773 }
774
775 Mesh mesh;
776 mesh.m_drawMode = data.m_primitiveType;
777 mesh.m_winding = Winding::CounterClockwise;
778
779 for (int i = 0; i < data.m_attributeCount; ++i) {
780 const RuntimeMeshData::Attribute &att = data.m_attributes[i];
782 mesh.m_indexBuffer.componentType = att.componentType;
783 } else {
784 const char *name = nullptr;
785 switch (att.semantic) {
788 break;
791 break;
794 break;
797 break;
800 break;
803 break;
806 break;
809 break;
812 break;
813 default:
814 *error = QObject::tr("Warning: Invalid attribute semantic: %1")
815 .arg(att.semantic);
816 return Mesh();
817 }
818
821 entry.componentCount = att.componentCount();
822 entry.offset = att.offset;
823 entry.name = name;
824 mesh.m_vertexBuffer.entries.append(entry);
825 }
826 }
827
828 mesh.m_vertexBuffer.data = data.m_vertexBuffer;
829 // Only interleaved vertex attribute packing is supported, both internally
830 // and in the QQuick3DGeometry API, hence the per-vertex buffer stride.
831 mesh.m_vertexBuffer.stride = data.m_stride;
832 mesh.m_subsets = data.m_subsets;
833 mesh.m_indexBuffer.data = data.m_indexBuffer;
834
835 if (!data.m_targetBuffer.isEmpty()) {
836 const quint32 vertexCount = data.m_vertexBuffer.size() / data.m_stride;
837 const quint32 targetEntryTexWidth = qCeil(qSqrt(vertexCount));
838 const quint32 targetCompStride = targetEntryTexWidth * targetEntryTexWidth * 4 * sizeof(float);
839 mesh.m_targetBuffer.data.resize(targetCompStride * data.m_targetAttributeCount);
840
841 QVarLengthArray<RuntimeMeshData::TargetAttribute> sortedAttribs(
842 data.m_targetAttributes,
843 data.m_targetAttributes + data.m_targetAttributeCount);
844 std::sort(sortedAttribs.begin(), sortedAttribs.end(),
846 return (a.targetId == b.targetId) ? a.attr.semantic < b.attr.semantic :
847 a.targetId < b.targetId; });
848 for (int i = 0; i < data.m_targetAttributeCount; ++i) {
849 const RuntimeMeshData::Attribute &att = sortedAttribs[i].attr;
850 const int stride = (sortedAttribs[i].stride < 1) ? att.componentCount() * sizeof(float)
851 : sortedAttribs[i].stride;
852 const char *name = nullptr;
853 switch (att.semantic) {
856 break;
859 break;
862 break;
865 break;
868 break;
871 break;
875 *error = QObject::tr("Warning: Invalid target attribute semantic: %1")
876 .arg(att.semantic);
877 continue;
880 break;
881 default:
882 *error = QObject::tr("Warning: Invalid target attribute semantic: %1")
883 .arg(att.semantic);
884 return Mesh();
885 }
886 char *dstBuf = mesh.m_targetBuffer.data.data() + i * targetCompStride;
887 const char *srcBuf = data.m_targetBuffer.constData() + att.offset;
888 Q_ASSERT(att.componentType == Mesh::ComponentType::Float32);
889 if (stride == 4 * sizeof(float)) {
890 memcpy(dstBuf, srcBuf, vertexCount * stride);
891 } else {
892 for (quint32 j = 0; j < vertexCount; ++j) {
893 memcpy(dstBuf + j * 4 * sizeof(float),
894 srcBuf + j * stride,
895 att.componentCount() * sizeof(float));
896 }
897 }
898
899 if (sortedAttribs[i].targetId == 0) {
902 entry.componentCount = att.componentCount();
903 entry.offset = i * targetCompStride;
904 entry.name = name;
905 mesh.m_targetBuffer.entries.append(entry);
906 }
907 }
908 mesh.m_targetBuffer.numTargets = data.m_targetAttributeCount / mesh.m_targetBuffer.entries.size();
909 }
910 return mesh;
911}
912
914{
915 qint64 newMeshStartPosFromEnd = 0;
916 quint32 newId = 1;
918
919 if (device->size() > 0) {
921 if (!header.isValid()) {
922 qWarning("There is existing data, but mesh file header is invalid; cannot save");
923 return 0;
924 }
925 for (auto it = header.meshEntries.cbegin(), end = header.meshEntries.cend(); it != end; ++it) {
926 if (id) {
927 Q_ASSERT(id != it.key());
928 newId = id;
929 } else {
930 newId = qMax(newId, it.key() + 1);
931 }
932 }
933 newMeshStartPosFromEnd = MULTI_HEADER_STRUCT_SIZE + header.meshEntries.size() * MULTI_ENTRY_STRUCT_SIZE;
934 } else {
936 }
937
938 // the new mesh data overwrites the entry list and file header
939 device->seek(device->size() - newMeshStartPosFromEnd);
940 const qint64 meshOffset = device->pos();
941 header.meshEntries.insert(newId, meshOffset);
942
944 // skip the space for the mesh header for now
945 device->seek(device->pos() + MESH_HEADER_STRUCT_SIZE);
946 meshHeader.sizeInBytes = MeshInternal::writeMeshData(device, *this);
947 // now the mesh header is ready to be written out
948 device->seek(meshOffset);
950 device->seek(meshOffset + MESH_HEADER_STRUCT_SIZE + meshHeader.sizeInBytes);
951 // write out new entry list and file header
953
954 return newId;
955}
956
958 const QByteArray &vertexBufferData,
959 quint32 vertexBufferStride,
960 const QByteArray &indexBufferData,
961 Mesh::ComponentType indexComponentType,
962 quint32 subsetCount,
963 quint32 subsetOffset)
964{
966 if (entry.componentType != Mesh::ComponentType::Float32 || entry.componentCount != 3) {
967 Q_ASSERT(false);
968 return result;
969 }
970
971 const int indexComponentByteSize = byteSizeForComponentType(indexComponentType);
972 if (indexComponentByteSize != 2 && indexComponentByteSize != 4) {
973 Q_ASSERT(false);
974 return result;
975 }
976
977 const quint32 indexBufferCount = indexBufferData.size() / indexComponentByteSize;
978 const quint32 vertexBufferByteSize = vertexBufferData.size();
979 const char *vertexSrcPtr = vertexBufferData.constData();
980 const char *indexSrcPtr = indexBufferData.constData();
981
982 for (quint32 idx = 0, numItems = subsetCount; idx < numItems; ++idx) {
983 if (idx + subsetOffset >= indexBufferCount)
984 continue;
985
986 quint32 vertexIdx = 0;
987 switch (indexComponentByteSize) {
988 case 2:
989 vertexIdx = reinterpret_cast<const quint16 *>(indexSrcPtr)[idx + subsetOffset];
990 break;
991 case 4:
992 vertexIdx = reinterpret_cast<const quint32 *>(indexSrcPtr)[idx + subsetOffset];
993 break;
994 default:
995 Q_UNREACHABLE();
996 break;
997 }
998
999 const quint32 finalOffset = entry.offset + (vertexIdx * vertexBufferStride);
1000 float v[3];
1001 if (finalOffset + sizeof(v) <= vertexBufferByteSize) {
1002 memcpy(v, vertexSrcPtr + finalOffset, sizeof(v));
1003 result.include(QVector3D(v[0], v[1], v[2]));
1004 } else {
1005 Q_ASSERT(false);
1006 }
1007 }
1008
1009 return result;
1010}
1011
1013{
1014 const char *lightmapAttrName = MeshInternal::getLightmapUVAttrName();
1015 for (const VertexBufferEntry &vbe : std::as_const(m_vertexBuffer.entries)) {
1016 if (vbe.name == lightmapAttrName)
1017 return true;
1018 }
1019 return false;
1020}
1021
1022bool Mesh::createLightmapUVChannel(uint lightmapBaseResolution)
1023{
1024 const char *posAttrName = MeshInternal::getPositionAttrName();
1025 const char *normalAttrName = MeshInternal::getNormalAttrName();
1026 const char *uvAttrName = MeshInternal::getUV0AttrName();
1027 const char *lightmapAttrName = MeshInternal::getLightmapUVAttrName();
1028
1029 // this function should do nothing if there is already an attr_lightmapuv
1031 return true;
1032
1033 const char *srcVertexData = m_vertexBuffer.data.constData();
1034 const quint32 srcVertexStride = m_vertexBuffer.stride;
1035 if (!srcVertexStride) {
1036 qWarning("Lightmap UV unwrapping encountered a Mesh with 0 vertex stride, this cannot happen");
1037 return false;
1038 }
1039 if (m_indexBuffer.data.isEmpty()) {
1040 qWarning("Lightmap UV unwrapping encountered a Mesh without index data, this cannot happen");
1041 return false;
1042 }
1043
1044 quint32 positionOffset = UINT32_MAX;
1045 quint32 normalOffset = UINT32_MAX;
1046 quint32 uvOffset = UINT32_MAX;
1047
1048 for (const VertexBufferEntry &vbe : std::as_const(m_vertexBuffer.entries)) {
1049 if (vbe.name == posAttrName) {
1050 if (vbe.componentCount != 3) {
1051 qWarning("Lightmap UV unwrapping encountered a Mesh non-float3 position data, this cannot happen");
1052 return false;
1053 }
1054 positionOffset = vbe.offset;
1055 } else if (vbe.name == normalAttrName) {
1056 if (vbe.componentCount != 3) {
1057 qWarning("Lightmap UV unwrapping encountered a Mesh non-float3 normal data, this cannot happen");
1058 return false;
1059 }
1060 normalOffset = vbe.offset;
1061 } else if (vbe.name == uvAttrName) {
1062 if (vbe.componentCount != 2) {
1063 qWarning("Lightmap UV unwrapping encountered a Mesh non-float2 UV0 data, this cannot happen");
1064 return false;
1065 }
1066 uvOffset = vbe.offset;
1067 }
1068 }
1069
1070 if (positionOffset == UINT32_MAX) {
1071 qWarning("Lightmap UV unwrapping encountered a Mesh without vertex positions, this cannot happen");
1072 return false;
1073 }
1074 // normal and uv0 are optional
1075
1076 const qsizetype vertexCount = m_vertexBuffer.data.size() / srcVertexStride;
1077 QByteArray positionData(vertexCount * 3 * sizeof(float), Qt::Uninitialized);
1078 float *posPtr = reinterpret_cast<float *>(positionData.data());
1079 for (qsizetype i = 0; i < vertexCount; ++i) {
1080 const char *vertexBasePtr = srcVertexData + i * srcVertexStride;
1081 const float *srcPos = reinterpret_cast<const float *>(vertexBasePtr + positionOffset);
1082 *posPtr++ = *srcPos++;
1083 *posPtr++ = *srcPos++;
1084 *posPtr++ = *srcPos++;
1085 }
1086
1087 QByteArray normalData;
1088 if (normalOffset != UINT32_MAX) {
1089 normalData.resize(vertexCount * 3 * sizeof(float));
1090 float *normPtr = reinterpret_cast<float *>(normalData.data());
1091 for (qsizetype i = 0; i < vertexCount; ++i) {
1092 const char *vertexBasePtr = srcVertexData + i * srcVertexStride;
1093 const float *srcNormal = reinterpret_cast<const float *>(vertexBasePtr + normalOffset);
1094 *normPtr++ = *srcNormal++;
1095 *normPtr++ = *srcNormal++;
1096 *normPtr++ = *srcNormal++;
1097 }
1098 }
1099
1100 QByteArray uvData;
1101 if (uvOffset != UINT32_MAX) {
1102 uvData.resize(vertexCount * 2 * sizeof(float));
1103 float *uvPtr = reinterpret_cast<float *>(uvData.data());
1104 for (qsizetype i = 0; i < vertexCount; ++i) {
1105 const char *vertexBasePtr = srcVertexData + i * srcVertexStride;
1106 const float *srcUv = reinterpret_cast<const float *>(vertexBasePtr + uvOffset);
1107 *uvPtr++ = *srcUv++;
1108 *uvPtr++ = *srcUv++;
1109 }
1110 }
1111
1113 QSSGLightmapUVGeneratorResult r = uvGen.run(positionData, normalData, uvData,
1114 m_indexBuffer.data, m_indexBuffer.componentType,
1115 lightmapBaseResolution);
1116 if (!r.isValid())
1117 return false;
1118
1119 // the result can have more (but never less) vertices than the input
1120 const int newVertexCount = r.vertexMap.size();
1121
1122 // r.indexData contains the new index data that has the same number of elements as before
1123 const quint32 *newIndex = reinterpret_cast<const quint32 *>(r.indexData.constData());
1124 if (m_indexBuffer.componentType == QSSGMesh::Mesh::ComponentType::UnsignedInt32) {
1125 if (r.indexData.size() != m_indexBuffer.data.size()) {
1126 qWarning("Index buffer size mismatch after lightmap UV unwrapping");
1127 return false;
1128 }
1129 quint32 *indexDst = reinterpret_cast<quint32 *>(m_indexBuffer.data.data());
1130 memcpy(indexDst, newIndex, m_indexBuffer.data.size());
1131 } else {
1132 if (r.indexData.size() != m_indexBuffer.data.size() * 2) {
1133 qWarning("Index buffer size mismatch after lightmap UV unwrapping");
1134 return false;
1135 }
1136 quint16 *indexDst = reinterpret_cast<quint16 *>(m_indexBuffer.data.data());
1137 for (size_t i = 0, count = m_indexBuffer.data.size() / sizeof(quint16); i != count; ++i)
1138 *indexDst++ = *newIndex++;
1139 }
1140
1141 QVarLengthArray<QByteArray, 8> newData;
1142 newData.reserve(m_vertexBuffer.entries.size());
1143
1144 for (const VertexBufferEntry &vbe : std::as_const(m_vertexBuffer.entries)) {
1145 const qsizetype byteSize = vbe.componentCount * MeshInternal::byteSizeForComponentType(vbe.componentType);
1146 QByteArray data(byteSize * vertexCount, Qt::Uninitialized);
1147 char *dst = data.data();
1148 for (qsizetype i = 0; i < vertexCount; ++i) {
1149 memcpy(dst, srcVertexData + i * srcVertexStride + vbe.offset, byteSize);
1150 dst += byteSize;
1151 }
1152 switch (vbe.componentType) {
1153 case ComponentType::UnsignedInt8:
1154 newData.append(QSSGLightmapUVGenerator::remap<quint8>(data, r.vertexMap, vbe.componentCount));
1155 break;
1156 case ComponentType::Int8:
1157 newData.append(QSSGLightmapUVGenerator::remap<qint8>(data, r.vertexMap, vbe.componentCount));
1158 break;
1159 case ComponentType::UnsignedInt16:
1160 newData.append(QSSGLightmapUVGenerator::remap<quint16>(data, r.vertexMap, vbe.componentCount));
1161 break;
1162 case ComponentType::Int16:
1163 newData.append(QSSGLightmapUVGenerator::remap<qint16>(data, r.vertexMap, vbe.componentCount));
1164 break;
1165 case ComponentType::UnsignedInt32:
1166 newData.append(QSSGLightmapUVGenerator::remap<quint32>(data, r.vertexMap, vbe.componentCount));
1167 break;
1168 case ComponentType::Int32:
1169 newData.append(QSSGLightmapUVGenerator::remap<qint32>(data, r.vertexMap, vbe.componentCount));
1170 break;
1171 case ComponentType::UnsignedInt64:
1172 newData.append(QSSGLightmapUVGenerator::remap<quint64>(data, r.vertexMap, vbe.componentCount));
1173 break;
1174 case ComponentType::Int64:
1175 newData.append(QSSGLightmapUVGenerator::remap<qint64>(data, r.vertexMap, vbe.componentCount));
1176 break;
1177 case ComponentType::Float16:
1178 newData.append(QSSGLightmapUVGenerator::remap<qfloat16>(data, r.vertexMap, vbe.componentCount));
1179 break;
1180 case ComponentType::Float32:
1181 newData.append(QSSGLightmapUVGenerator::remap<float>(data, r.vertexMap, vbe.componentCount));
1182 break;
1183 case ComponentType::Float64:
1184 newData.append(QSSGLightmapUVGenerator::remap<double>(data, r.vertexMap, vbe.componentCount));
1185 break;
1186 }
1187 }
1188
1189 VertexBufferEntry lightmapUVEntry;
1190 lightmapUVEntry.componentType = ComponentType::Float32;
1191 lightmapUVEntry.componentCount = 2;
1192 lightmapUVEntry.offset = 0;
1193 lightmapUVEntry.name = lightmapAttrName;
1194
1195 QByteArray newVertexBuffer;
1196 newVertexBuffer.reserve(newVertexCount * (srcVertexStride + 8));
1197
1198 quint32 bufferAlignment = 0;
1199 for (int vertexIdx = 0; vertexIdx < newVertexCount; ++vertexIdx) {
1200 quint32 dataOffset = 0;
1201 for (int vbIdx = 0, end = m_vertexBuffer.entries.size(); vbIdx != end; ++vbIdx) {
1202 VertexBufferEntry &vbe(m_vertexBuffer.entries[vbIdx]);
1203
1204 const quint32 alignment = MeshInternal::byteSizeForComponentType(vbe.componentType);
1205 bufferAlignment = qMax(bufferAlignment, alignment);
1206 const quint32 byteSize = alignment * vbe.componentCount;
1207 const quint32 newOffset = getAlignedOffset(dataOffset, alignment);
1208
1209 if (newOffset != dataOffset) {
1210 QByteArray filler(newOffset - dataOffset, '\0');
1211 newVertexBuffer.append(filler);
1212 }
1213
1214 if (vertexIdx == 0)
1215 vbe.offset = newVertexBuffer.size();
1216
1217 newVertexBuffer.append(newData[vbIdx].constData() + byteSize * vertexIdx, byteSize);
1218 dataOffset = newOffset + byteSize;
1219 }
1220
1221 const quint32 byteSize = 2 * sizeof(float);
1222 const quint32 newOffset = getAlignedOffset(dataOffset, byteSize);
1223 if (newOffset != dataOffset) {
1224 QByteArray filler(newOffset - dataOffset, '\0');
1225 newVertexBuffer.append(filler);
1226 }
1227
1228 if (vertexIdx == 0)
1229 lightmapUVEntry.offset = newVertexBuffer.size();
1230
1231 newVertexBuffer.append(r.lightmapUVChannel.constData() + byteSize * vertexIdx, byteSize);
1232 dataOffset = newOffset + byteSize;
1233
1234 if (vertexIdx == 0)
1235 m_vertexBuffer.stride = getAlignedOffset(dataOffset, bufferAlignment);
1236 }
1237
1238 m_vertexBuffer.entries.append(lightmapUVEntry);
1239
1240 m_vertexBuffer.data = newVertexBuffer;
1241
1242 const QSize lightmapSizeHint(r.lightmapWidth, r.lightmapHeight);
1243 for (Subset &subset : m_subsets)
1244 subset.lightmapSizeHint = lightmapSizeHint;
1245
1246 return true;
1247}
1248
1249size_t simplifyMesh(unsigned int *destination, const unsigned int *indices, size_t indexCount, const float *vertexPositions, size_t vertexCount, size_t vertexPositionsStride, size_t targetIndexCount, float targetError, unsigned int options, float *resultError)
1250{
1251 return meshopt_simplify(destination, indices, indexCount, vertexPositions, vertexCount, vertexPositionsStride, targetIndexCount, targetError, options, resultError);
1252}
1253
1254float simplifyScale(const float *vertexPositions, size_t vertexCount, size_t vertexPositionsStride)
1255{
1256 return meshopt_simplifyScale(vertexPositions, vertexCount, vertexPositionsStride);
1257}
1258
1259void optimizeVertexCache(unsigned int *destination, const unsigned int *indices, size_t indexCount, size_t vertexCount)
1260{
1261 meshopt_optimizeVertexCache(destination, indices, indexCount, vertexCount);
1262}
1263
1264} // namespace QSSGMesh
1265
IOBluetoothDevice * device
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
void reserve(qsizetype size)
Attempts to allocate memory for at least size bytes.
Definition qbytearray.h:634
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore\reentrant
Definition qdatastream.h:46
void setFloatingPointPrecision(FloatingPointPrecision precision)
Sets the floating point precision of the data stream to precision.
void setByteOrder(ByteOrder)
Sets the serialization byte order to bo.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
Class representing 3D range or axis aligned bounding box.
QVector3D minimum
QVector3D maximum
static Mesh fromAssetData(const QVector< AssetVertexEntry > &vbufEntries, const QByteArray &indexBufferData, ComponentType indexComponentType, const QVector< AssetMeshSubset > &subsets, quint32 numTargets=0, quint32 numTargetComps=0)
Definition qssgmesh.cpp:625
bool createLightmapUVChannel(uint lightmapBaseResolution)
QSSGRenderWinding Winding
Definition qssgmesh_p.h:85
static Mesh fromRuntimeData(const RuntimeMeshData &data, QString *error)
Definition qssgmesh.cpp:764
static QMap< quint32, Mesh > loadAll(QIODevice *device)
Definition qssgmesh.cpp:600
QSSGRenderComponentType ComponentType
Definition qssgmesh_p.h:86
static Mesh loadMesh(QIODevice *device, quint32 id=0)
Definition qssgmesh.cpp:581
bool hasLightmapUVChannel() const
QSSGRenderDrawMode DrawMode
Definition qssgmesh_p.h:84
QVector< Subset > subsets() const
Definition qssgmesh_p.h:143
quint32 save(QIODevice *device, quint32 id=0) const
Definition qssgmesh.cpp:913
const_iterator constEnd() const noexcept
Definition qset.h:143
const_iterator constFind(const T &value) const
Definition qset.h:161
const_iterator cbegin() const noexcept
Definition qset.h:138
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
qsizetype count(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4833
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
QSet< QString >::iterator it
uint alignment
static const size_t LOD_STRUCT_SIZE
Definition qssgmesh.cpp:41
float simplifyScale(const float *vertexPositions, size_t vertexCount, size_t vertexPositionsStride)
static const size_t SUBSET_STRUCT_SIZE_V5
Definition qssgmesh.cpp:36
static const size_t MESH_HEADER_STRUCT_SIZE
Definition qssgmesh.cpp:25
void optimizeVertexCache(unsigned int *destination, const unsigned int *indices, size_t indexCount, size_t vertexCount)
static const size_t MESH_STRUCT_SIZE
Definition qssgmesh.cpp:28
static const size_t VERTEX_BUFFER_ENTRY_STRUCT_SIZE
Definition qssgmesh.cpp:31
size_t simplifyMesh(unsigned int *destination, const unsigned int *indices, size_t indexCount, const float *vertexPositions, size_t vertexCount, size_t vertexPositionsStride, size_t targetIndexCount, float targetError, unsigned int options, float *resultError)
static quint32 getAlignedOffset(quint32 offset, quint32 align)
Definition qssgmesh.cpp:616
static const size_t MULTI_HEADER_STRUCT_SIZE
Definition qssgmesh.cpp:19
static const size_t SUBSET_STRUCT_SIZE_V3_V4
Definition qssgmesh.cpp:34
static const size_t MULTI_ENTRY_STRUCT_SIZE
Definition qssgmesh.cpp:22
static const size_t SUBSET_STRUCT_SIZE_V6
Definition qssgmesh.cpp:38
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char * destination
DBusConnection const char DBusError * error
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
#define qWarning
Definition qlogging.h:166
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLsizei GLsizei height
GLfloat GLfloat GLfloat GLfloat GLfloat maxY
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLfloat minY
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
const void GLsizei GLsizei stride
GLsizei GLsizei GLfloat distance
GLint GLsizei width
GLenum GLenum dst
GLenum GLuint GLintptr offset
GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat maxZ
GLuint name
GLsizei GLenum const void * indices
GLfloat GLfloat minZ
GLfloat GLfloat GLfloat GLfloat maxX
GLuint entry
GLint lod
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QSSGRenderComponentType
unsigned int quint32
Definition qtypes.h:50
unsigned short quint16
Definition qtypes.h:48
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
static MeshDataHeader withDefaults()
Definition qssgmesh_p.h:315
static const quint32 LEGACY_MESH_FILE_VERSION
Definition qssgmesh_p.h:307
static MultiMeshInfo withDefaults()
Definition qssgmesh_p.h:290
Mesh::SubsetBounds bounds
Definition qssgmesh_p.h:364
static quint32 byteSizeForComponentType(Mesh::ComponentType componentType)
Definition qssgmesh_p.h:390
static const char * getLightmapUVAttrName()
Definition qssgmesh_p.h:396
static const char * getNormalAttrName()
Definition qssgmesh_p.h:393
static quint64 writeMeshData(QIODevice *device, const Mesh &mesh)
Definition qssgmesh.cpp:407
static const char * getUV1AttrName()
Definition qssgmesh_p.h:395
static const char * getTexBinormalAttrName()
Definition qssgmesh_p.h:398
static MultiMeshInfo readFileHeader(QIODevice *device)
Definition qssgmesh.cpp:43
static const char * getPositionAttrName()
Definition qssgmesh_p.h:392
static quint64 readMeshData(QIODevice *device, quint64 offset, Mesh *mesh, MeshDataHeader *header)
Definition qssgmesh.cpp:95
static const char * getTexTanAttrName()
Definition qssgmesh_p.h:397
static QSSGBounds3 calculateSubsetBounds(const Mesh::VertexBufferEntry &entry, const QByteArray &vertexBufferData, quint32 vertexBufferStride, const QByteArray &indexBufferData, Mesh::ComponentType indexComponentType, quint32 subsetCount, quint32 subsetOffset)
Definition qssgmesh.cpp:957
static const char * getColorAttrName()
Definition qssgmesh_p.h:399
static void writeFileHeader(QIODevice *device, const MultiMeshInfo &meshFileInfo)
Definition qssgmesh.cpp:77
static void writeMeshHeader(QIODevice *device, const MeshDataHeader &header)
Definition qssgmesh.cpp:389
static const char * getJointAttrName()
Definition qssgmesh_p.h:400
static const char * getUV0AttrName()
Definition qssgmesh_p.h:394
static const char * getWeightAttrName()
Definition qssgmesh_p.h:401
QVector< Lod > lods
Definition qssgmesh_p.h:136
QVector< VertexBufferEntry > entries
Definition qssgmesh_p.h:115
QVector< VertexBufferEntry > entries
Definition qssgmesh_p.h:104