1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#ifndef QSSGMESHUTILITIES_P_H
5#define QSSGMESHUTILITIES_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtQuick3DUtils/private/qtquick3dutilsglobal_p.h>
19
20#include <QtQuick3DUtils/private/qssgbounds3_p.h>
21
22#include <QtQuick3DUtils/private/qssgrenderbasetypes_p.h>
23
24#include <QtCore/qstring.h>
25#include <QtCore/qbytearray.h>
26#include <QtCore/qiodevice.h>
27#include <QtCore/qmap.h>
28
29QT_BEGIN_NAMESPACE
30
31struct QSSGRenderVertexBufferEntry
32{
33 QByteArray m_name;
34 /** Datatype of the this entry points to in the buffer */
35 QSSGRenderComponentType m_componentType;
36 /** Number of components of each data member. 1,2,3, or 4. Don't be stupid.*/
37 quint32 m_numComponents;
38 /** Offset from the beginning of the buffer of the first item */
39 quint32 m_firstItemOffset;
40
41 QSSGRenderVertexBufferEntry(const QByteArray &nm,
42 QSSGRenderComponentType type,
43 quint32 numComponents,
44 quint32 firstItemOffset = 0)
45 : m_name(nm), m_componentType(type), m_numComponents(numComponents), m_firstItemOffset(firstItemOffset)
46 {
47 }
48
49 QSSGRenderVertexBufferEntry()
50 : m_componentType(QSSGRenderComponentType::Float32), m_numComponents(0), m_firstItemOffset(0)
51 {
52 }
53
54 QSSGRenderVertexBufferEntry(const QSSGRenderVertexBufferEntry &inOther)
55 : m_name(inOther.m_name)
56 , m_componentType(inOther.m_componentType)
57 , m_numComponents(inOther.m_numComponents)
58 , m_firstItemOffset(inOther.m_firstItemOffset)
59 {
60 }
61
62 QSSGRenderVertexBufferEntry &operator=(const QSSGRenderVertexBufferEntry &inOther)
63 {
64 if (this != &inOther) {
65 m_name = inOther.m_name;
66 m_componentType = inOther.m_componentType;
67 m_numComponents = inOther.m_numComponents;
68 m_firstItemOffset = inOther.m_firstItemOffset;
69 }
70 return *this;
71 }
72};
73
74namespace QSSGMesh {
75
76struct AssetVertexEntry;
77struct AssetMeshSubset;
78struct RuntimeMeshData;
79struct AssetLodEntry;
80
81class Q_QUICK3DUTILS_EXPORT Mesh
82{
83public:
84 using DrawMode = QSSGRenderDrawMode;
85 using Winding = QSSGRenderWinding;
86 using ComponentType = QSSGRenderComponentType;
87
88 struct VertexBufferEntry {
89 ComponentType componentType = ComponentType::Float32;
90 quint32 componentCount = 0;
91 quint32 offset = 0;
92 QByteArray name;
93
94 QSSGRenderVertexBufferEntry toRenderVertexBufferEntry() const {
95 return QSSGRenderVertexBufferEntry(name,
96 QSSGRenderComponentType(componentType),
97 componentCount,
98 offset);
99 }
100 };
101
102 struct VertexBuffer {
103 quint32 stride = 0;
104 QVector<VertexBufferEntry> entries;
105 QByteArray data;
106 };
107
108 struct IndexBuffer {
109 ComponentType componentType = ComponentType::UnsignedInt32;
110 QByteArray data;
111 };
112
113 struct TargetBuffer {
114 quint32 numTargets = 0;
115 QVector<VertexBufferEntry> entries;
116 QByteArray data;
117 };
118
119 struct SubsetBounds {
120 QVector3D min;
121 QVector3D max;
122 };
123
124 struct Lod {
125 quint32 count = 0;
126 quint32 offset = 0;
127 float distance = 0.0f;
128 };
129
130 struct Subset {
131 QString name;
132 SubsetBounds bounds;
133 quint32 count = 0;
134 quint32 offset = 0;
135 QSize lightmapSizeHint;
136 QVector<Lod> lods;
137 };
138
139 // can just return by value (big data is all implicitly shared)
140 VertexBuffer vertexBuffer() const { return m_vertexBuffer; }
141 IndexBuffer indexBuffer() const { return m_indexBuffer; }
142 TargetBuffer targetBuffer() const { return m_targetBuffer; }
143 QVector<Subset> subsets() const { return m_subsets; }
144
145 // id 0 == first, otherwise has to match
146 static Mesh loadMesh(QIODevice *device, quint32 id = 0);
147
148 static QMap<quint32, Mesh> loadAll(QIODevice *device);
149
150 static Mesh fromAssetData(const QVector<AssetVertexEntry> &vbufEntries,
151 const QByteArray &indexBufferData,
152 ComponentType indexComponentType,
153 const QVector<AssetMeshSubset> &subsets,
154 quint32 numTargets = 0, quint32 numTargetComps = 0);
155
156 static Mesh fromRuntimeData(const RuntimeMeshData &data,
157 QString *error);
158
159 bool isValid() const { return !m_subsets.isEmpty(); }
160
161 DrawMode drawMode() const { return m_drawMode; }
162 Winding winding() const { return m_winding; }
163
164 // id 0 == generate new id; otherwise uses it as-is, and must be an unused one
165 quint32 save(QIODevice *device, quint32 id = 0) const;
166
167 bool hasLightmapUVChannel() const;
168 bool createLightmapUVChannel(uint lightmapBaseResolution);
169
170private:
171 DrawMode m_drawMode = DrawMode::Triangles;
172 Winding m_winding = Winding::CounterClockwise;
173 VertexBuffer m_vertexBuffer;
174 IndexBuffer m_indexBuffer;
175 TargetBuffer m_targetBuffer;
176 QVector<Subset> m_subsets;
177 friend struct MeshInternal;
178};
179
180struct Q_QUICK3DUTILS_EXPORT AssetVertexEntry // for asset importer plugins (Assimp, FBX)
181{
182 QByteArray name;
183 QByteArray data;
184 Mesh::ComponentType componentType = Mesh::ComponentType::Float32;
185 quint32 componentCount = 0;
186 qint32 morphTargetId = -1; // -1 menas that this entry belongs to the original mesh.
187};
188
189struct Q_QUICK3DUTILS_EXPORT AssetMeshSubset // for asset importer plugins (Assimp, FBX)
190{
191 QString name;
192 quint32 count = 0;
193 quint32 offset = 0;
194 quint32 boundsPositionEntryIndex = std::numeric_limits<quint32>::max();
195 quint32 lightmapWidth = 0;
196 quint32 lightmapHeight = 0;
197 QVector<Mesh::Lod> lods;
198};
199
200struct Q_QUICK3DUTILS_EXPORT RuntimeMeshData // for custom geometry (QQuick3DGeometry, QSSGRenderGeometry)
201{
202 struct Attribute {
203 enum Semantic {
204 IndexSemantic = 0,
205 PositionSemantic, // attr_pos
206 NormalSemantic, // attr_norm
207 TexCoordSemantic, // attr_uv0
208 TangentSemantic, // attr_textan
209 BinormalSemantic, // attr_binormal
210 JointSemantic, // attr_joints
211 WeightSemantic, // attr_weights
212 ColorSemantic, // attr_color
213 TexCoord1Semantic, // attr_uv1
214 TexCoord0Semantic = TexCoordSemantic // attr_uv0
215 };
216
217 Semantic semantic = PositionSemantic;
218 Mesh::ComponentType componentType = Mesh::ComponentType::Float32;
219 int offset = 0;
220
221 int componentCount() const {
222 switch (semantic) {
223 case IndexSemantic: return 1;
224 case PositionSemantic: return 3;
225 case NormalSemantic: return 3;
226 case TexCoord0Semantic: return 2;
227 case TexCoord1Semantic: return 2;
228 case TangentSemantic: return 3;
229 case BinormalSemantic: return 3;
230 case JointSemantic: return 4;
231 case WeightSemantic: return 4;
232 case ColorSemantic: return 4;
233 }
234 Q_UNREACHABLE_RETURN(0);
235 }
236 };
237
238 struct TargetAttribute {
239 Attribute attr;
240 int targetId;
241 int stride;
242 };
243
244 static const int MAX_ATTRIBUTES = 16;
245 static const int MAX_TARGET_ATTRIBUTES = 32;
246
247 void clear()
248 {
249 clearVertexAndIndex();
250 clearTarget();
251 }
252 void clearVertexAndIndex()
253 {
254 m_vertexBuffer.clear();
255 m_indexBuffer.clear();
256 m_subsets.clear();
257 m_attributeCount = 0;
258 m_primitiveType = Mesh::DrawMode::Triangles;
259 }
260 void clearTarget()
261 {
262 m_targetBuffer.clear();
263 m_targetAttributeCount = 0;
264 }
265
266 QByteArray m_vertexBuffer;
267 QByteArray m_indexBuffer;
268 QByteArray m_targetBuffer;
269 QVector<Mesh::Subset> m_subsets;
270
271 Attribute m_attributes[MAX_ATTRIBUTES];
272 int m_attributeCount = 0;
273 TargetAttribute m_targetAttributes[MAX_TARGET_ATTRIBUTES];
274 int m_targetAttributeCount = 0;
275 Mesh::DrawMode m_primitiveType = Mesh::DrawMode::Triangles;
276 int m_stride = 0;
277};
278
279struct Q_QUICK3DUTILS_EXPORT MeshInternal
280{
281 struct MultiMeshInfo {
282 quint32 fileId = 0;
283 quint32 fileVersion = 0;
284 QMap<quint32, quint64> meshEntries;
285 static const quint32 FILE_ID = 555777497;
286 static const quint32 FILE_VERSION = 1;
287 bool isValid() const {
288 return fileId == FILE_ID && fileVersion == FILE_VERSION;
289 }
290 static MultiMeshInfo withDefaults() {
291 return { .fileId: FILE_ID, .fileVersion: FILE_VERSION, .meshEntries: {} };
292 }
293 };
294
295 struct MeshDataHeader {
296 quint32 fileId = 0;
297 quint16 fileVersion = 0;
298 quint16 flags = 0;
299 quint32 sizeInBytes = 0;
300
301 static const quint32 FILE_ID = 3365961549;
302
303 // Version 3 and 4 is only different in the "offset" values that are
304 // all zeroes in version 4 because they are not used by the new mesh
305 // reader. So to support both with the new loader, no branching is
306 // needed at all, it just needs to accept both versions.
307 static const quint32 LEGACY_MESH_FILE_VERSION = 3;
308 // Version 5 differs from 4 with the added lightmapSizeHint per subset.
309 // This needs branching in the deserializer.
310 // Version 6 differs from 5 with additional lodCount per subset as well
311 // as a list of Level of Detail data after the subset names.
312 // Version 7 will split the morph target data
313 static const quint32 FILE_VERSION = 7;
314
315 static MeshDataHeader withDefaults() {
316 return { .fileId: FILE_ID, .fileVersion: FILE_VERSION, .flags: 0, .sizeInBytes: 0 };
317 }
318
319 bool isValid() const {
320 return fileId == FILE_ID
321 && fileVersion <= FILE_VERSION
322 && fileVersion >= LEGACY_MESH_FILE_VERSION;
323 }
324
325 bool hasLightmapSizeHint() const {
326 return fileVersion >= 5;
327 }
328
329 bool hasLodDataHint() const {
330 return fileVersion >= 6;
331 }
332
333 bool hasSeparateTargetBuffer() const {
334 return fileVersion >= 7;
335 }
336 };
337
338 struct MeshOffsetTracker {
339 quint32 startOffset = 0;
340 quint32 byteCounter = 0;
341
342 MeshOffsetTracker(int offset)
343 : startOffset(offset) {}
344
345 int offset() {
346 return startOffset + byteCounter;
347 }
348
349 quint32 alignedAdvance(int advanceAmount) {
350 advance(advanceAmount);
351 quint32 alignmentAmount = 4 - (byteCounter % 4);
352 byteCounter += alignmentAmount;
353 return alignmentAmount;
354 }
355
356 void advance(int advanceAmount) {
357 byteCounter += advanceAmount;
358 }
359 };
360
361 struct Subset {
362 QByteArray rawNameUtf16;
363 quint32 nameLength = 0;
364 Mesh::SubsetBounds bounds;
365 quint32 offset = 0;
366 quint32 count = 0;
367 QSize lightmapSizeHint;
368 quint32 lodCount = 0;
369
370 Mesh::Subset toMeshSubset() const {
371 Mesh::Subset subset;
372 if (nameLength > 0)
373 subset.name = QString::fromUtf16(reinterpret_cast<const char16_t *>(rawNameUtf16.constData()), size: nameLength - 1);
374 subset.bounds.min = bounds.min;
375 subset.bounds.max = bounds.max;
376 subset.count = count;
377 subset.offset = offset;
378 subset.lightmapSizeHint = lightmapSizeHint;
379 subset.lods.resize(size: lodCount);
380 return subset;
381 }
382 };
383
384 static MultiMeshInfo readFileHeader(QIODevice *device);
385 static void writeFileHeader(QIODevice *device, const MultiMeshInfo &meshFileInfo);
386 static quint64 readMeshData(QIODevice *device, quint64 offset, Mesh *mesh, MeshDataHeader *header);
387 static void writeMeshHeader(QIODevice *device, const MeshDataHeader &header);
388 static quint64 writeMeshData(QIODevice *device, const Mesh &mesh);
389
390 static quint32 byteSizeForComponentType(Mesh::ComponentType componentType) { return quint32(QSSGBaseTypeHelpers::getSizeOfType(type: componentType)); }
391
392 static const char *getPositionAttrName() { return "attr_pos"; }
393 static const char *getNormalAttrName() { return "attr_norm"; }
394 static const char *getUV0AttrName() { return "attr_uv0"; }
395 static const char *getUV1AttrName() { return "attr_uv1"; }
396 static const char *getLightmapUVAttrName() { return "attr_lightmapuv"; }
397 static const char *getTexTanAttrName() { return "attr_textan"; }
398 static const char *getTexBinormalAttrName() { return "attr_binormal"; }
399 static const char *getColorAttrName() { return "attr_color"; }
400 static const char *getJointAttrName() { return "attr_joints"; }
401 static const char *getWeightAttrName() { return "attr_weights"; }
402
403 static QSSGBounds3 calculateSubsetBounds(const Mesh::VertexBufferEntry &entry,
404 const QByteArray &vertexBufferData,
405 quint32 vertexBufferStride,
406 const QByteArray &indexBufferData,
407 Mesh::ComponentType indexComponentType,
408 quint32 subsetCount,
409 quint32 subsetOffset);
410};
411
412size_t Q_QUICK3DUTILS_EXPORT simplifyMesh(unsigned int* destination,
413 const unsigned int* indices,
414 size_t indexCount,
415 const float* vertexPositions,
416 size_t vertexCount,
417 size_t vertexPositionsStride,
418 size_t targetIndexCount,
419 float targetError,
420 unsigned int options,
421 float* resultError);
422
423float Q_QUICK3DUTILS_EXPORT simplifyScale(const float* vertexPositions,
424 size_t vertexCount,
425 size_t vertexPositionsStride);
426
427void Q_QUICK3DUTILS_EXPORT optimizeVertexCache(unsigned int* destination,
428 const unsigned int* indices,
429 size_t indexCount,
430 size_t vertexCount);
431
432} // namespace QSSGMesh
433
434QT_END_NAMESPACE
435
436#endif // QSSGMESHUTILITIES_P_H
437

source code of qtquick3d/src/utils/qssgmesh_p.h