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 | |
29 | QT_BEGIN_NAMESPACE |
30 | |
31 | struct 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 | |
74 | namespace QSSGMesh { |
75 | |
76 | struct AssetVertexEntry; |
77 | struct AssetMeshSubset; |
78 | struct RuntimeMeshData; |
79 | struct AssetLodEntry; |
80 | |
81 | class Q_QUICK3DUTILS_EXPORT Mesh |
82 | { |
83 | public: |
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 | |
170 | private: |
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 | |
180 | struct 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 | |
189 | struct 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 | |
200 | struct 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 | |
279 | struct 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 { |
296 | quint32 = 0; |
297 | quint16 = 0; |
298 | quint16 = 0; |
299 | quint32 = 0; |
300 | |
301 | static const quint32 = 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 = 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 = 7; |
314 | |
315 | static MeshDataHeader () { |
316 | return { .fileId: FILE_ID, .fileVersion: FILE_VERSION, .flags: 0, .sizeInBytes: 0 }; |
317 | } |
318 | |
319 | bool () const { |
320 | return fileId == FILE_ID |
321 | && fileVersion <= FILE_VERSION |
322 | && fileVersion >= LEGACY_MESH_FILE_VERSION; |
323 | } |
324 | |
325 | bool () const { |
326 | return fileVersion >= 5; |
327 | } |
328 | |
329 | bool () const { |
330 | return fileVersion >= 6; |
331 | } |
332 | |
333 | bool () 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 (QIODevice *device); |
385 | static void (QIODevice *device, const MultiMeshInfo &meshFileInfo); |
386 | static quint64 (QIODevice *device, quint64 offset, Mesh *mesh, MeshDataHeader *); |
387 | static void (QIODevice *device, const MeshDataHeader &); |
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 | |
412 | size_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 | |
423 | float Q_QUICK3DUTILS_EXPORT simplifyScale(const float* vertexPositions, |
424 | size_t vertexCount, |
425 | size_t vertexPositionsStride); |
426 | |
427 | void 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 | |
434 | QT_END_NAMESPACE |
435 | |
436 | #endif // QSSGMESHUTILITIES_P_H |
437 | |