1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Quick 3D.
7 **
8 ** $QT_BEGIN_LICENSE:GPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 or (at your option) any later version
20 ** approved by the KDE Free Qt Foundation. The licenses are as published by
21 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #ifndef QSSGMESHUTILITIES_P_H
31 #define QSSGMESHUTILITIES_P_H
32 
33 //
34 //  W A R N I N G
35 //  -------------
36 //
37 // This file is not part of the Qt API.  It exists purely as an
38 // implementation detail.  This header file may change from version to
39 // version without notice, or even be removed.
40 //
41 // We mean it.
42 //
43 
44 #include <QtQuick3DAssetImport/private/qtquick3dassetimportglobal_p.h>
45 
46 #include <QtQuick3DUtils/private/qssgbounds3_p.h>
47 
48 #include <QtQuick3DRender/private/qssgrenderbasetypes_p.h>
49 
50 #include <QtCore/QString>
51 #include <QtCore/QByteArray>
52 #include <QtCore/QIODevice>
53 #include <QtCore/QFile>
54 
55 QT_BEGIN_NAMESPACE
56 
57 namespace QSSGMeshUtilities {
58 
59 struct MeshData
60 {
61     // All enums must match the ones defined by QSSGRenderGeometry class in quick3d module
62     enum PrimitiveType { // Must match also internal QSSGRenderDrawMode
63         UnknownType = 0,
64         Points,
65         LineStrip,
66         LineLoop,
67         Lines,
68         TriangleStrip,
69         TriangleFan,
70         Triangles, // Default primitive type
71         Patches
72     };
73 
74     struct Attribute {
75         enum Semantic {
76             UnknownSemantic = 0,
77             IndexSemantic,
78             PositionSemantic, // attr_pos
79             NormalSemantic,   // attr_norm
80             TexCoordSemantic, // attr_uv0
81             TangentSemantic,  // attr_textan
82             BinormalSemantic  // attr_binormal
83         };
84         enum ComponentType { // Must match also internal QSSGRenderComponentType
85             DefaultType = 0,
86             U8Type,
87             I8Type,
88             U16Type,
89             I16Type,
90             U32Type, // Default for IndexSemantic
91             I32Type,
92             U64Type,
93             I64Type,
94             F16Type,
95             F32Type, // Default for other semantics
96             F64Type
97         };
98 
typeSizeMeshData::Attribute99         int typeSize() const
100         {
101             switch (componentType) {
102             case U8Type:  return 1;
103             case I8Type:  return 1;
104             case U16Type: return 2;
105             case I16Type: return 2;
106             case U32Type: return 4;
107             case I32Type: return 4;
108             case U64Type: return 8;
109             case I64Type: return 8;
110             case F16Type: return 2;
111             case F32Type: return 4;
112             case F64Type: return 8;
113             default:
114                 Q_ASSERT(false);
115                 return 0;
116             }
117         }
118 
componentCountMeshData::Attribute119         int componentCount() const
120         {
121             switch (semantic) {
122             case IndexSemantic:    return 1;
123             case PositionSemantic: return 3;
124             case NormalSemantic:   return 3;
125             case TexCoordSemantic: return 2;
126             case TangentSemantic:  return 3;
127             case BinormalSemantic: return 3;
128             default:
129                 Q_ASSERT(false);
130                 return 0;
131             }
132         }
133 
134         Semantic semantic = PositionSemantic;
135         ComponentType componentType = F32Type;
136         int offset = 0;
137     };
138 
139     static const int MAX_ATTRIBUTES = 6;
140 
clearMeshData141     void clear()
142     {
143         m_vertexBuffer.clear();
144         m_indexBuffer.clear();
145         m_attributeCount = 0;
146         m_primitiveType = Triangles;
147     }
148 
149     QByteArray m_vertexBuffer;
150     QByteArray m_indexBuffer;
151 
152     Attribute m_attributes[MAX_ATTRIBUTES];
153     int m_attributeCount = 0;
154     PrimitiveType m_primitiveType = Triangles;
155     int m_stride = 0;
156 };
157 
158 template<typename DataType>
159 struct OffsetDataRef
160 {
161     quint32 m_offset;
162     quint32 m_size;
OffsetDataRefOffsetDataRef163     OffsetDataRef() : m_offset(0), m_size(0) {}
beginOffsetDataRef164     DataType *begin(quint8 *inBase)
165     {
166         DataType *value = reinterpret_cast<DataType *>(inBase + m_offset);
167         return value;
168     }
endOffsetDataRef169     DataType *end(quint8 *inBase) { return begin(inBase) + m_size; }
beginOffsetDataRef170     const DataType *begin(const quint8 *inBase) const { return reinterpret_cast<const DataType *>(inBase + m_offset); }
endOffsetDataRef171     const DataType *end(const quint8 *inBase) const { return begin(inBase) + m_size; }
sizeOffsetDataRef172     quint32 size() const { return m_size; }
emptyOffsetDataRef173     bool empty() const { return m_size == 0; }
indexOffsetDataRef174     DataType &index(quint8 *inBase, quint32 idx)
175     {
176         Q_ASSERT(idx < m_size);
177         return begin(inBase)[idx];
178     }
indexOffsetDataRef179     const DataType &index(const quint8 *inBase, quint32 idx) const
180     {
181         Q_ASSERT(idx < m_size);
182         return begin(inBase)[idx];
183     }
184 };
185 
186 struct MeshVertexBufferEntry
187 {
188     quint32 m_nameOffset;
189     /** Datatype of the this entry points to in the buffer */
190     QSSGRenderComponentType m_componentType;
191     /** Number of components of each data member. 1,2,3, or 4.  Don't be stupid.*/
192     quint32 m_numComponents;
193     /** Offset from the beginning of the buffer of the first item */
194     quint32 m_firstItemOffset;
MeshVertexBufferEntryMeshVertexBufferEntry195     MeshVertexBufferEntry()
196         : m_nameOffset(0), m_componentType(QSSGRenderComponentType::Float32), m_numComponents(3), m_firstItemOffset(0)
197     {
198     }
toVertexBufferEntryMeshVertexBufferEntry199     QSSGRenderVertexBufferEntry toVertexBufferEntry(quint8 *inBaseAddress) const
200     {
201         const char *nameBuffer = "";
202         if (m_nameOffset)
203             nameBuffer = reinterpret_cast<const char *>(inBaseAddress + m_nameOffset);
204         return QSSGRenderVertexBufferEntry(nameBuffer, m_componentType, m_numComponents, m_firstItemOffset);
205     }
206 };
207 
208 struct VertexBuffer
209 {
210     OffsetDataRef<MeshVertexBufferEntry> m_entries;
211     quint32 m_stride;
212     OffsetDataRef<quint8> m_data;
VertexBufferVertexBuffer213     VertexBuffer(OffsetDataRef<MeshVertexBufferEntry> entries, quint32 stride, OffsetDataRef<quint8> data)
214         : m_entries(entries), m_stride(stride), m_data(data)
215     {
216     }
VertexBufferVertexBuffer217     VertexBuffer() : m_stride(0) {}
218 };
219 
220 struct IndexBuffer
221 {
222     // Component types must be either UnsignedInt16 or UnsignedInt8 in order for the
223     // graphics hardware to deal with the buffer correctly.
224     QSSGRenderComponentType m_componentType;
225     OffsetDataRef<quint8> m_data;
226     // Either quint8 or quint16 component types are allowed by the underlying rendering
227     // system, so you would be wise to stick with those.
IndexBufferIndexBuffer228     IndexBuffer(QSSGRenderComponentType compType, OffsetDataRef<quint8> data)
229         : m_componentType(compType), m_data(data)
230     {
231     }
IndexBufferIndexBuffer232     IndexBuffer() : m_componentType(QSSGRenderComponentType::Unknown) {}
233 };
234 
235 template<quint32 TNumBytes>
236 struct MeshPadding
237 {
238     quint8 m_padding[TNumBytes];
MeshPaddingMeshPadding239     MeshPadding() { memZero(m_padding, TNumBytes); }
240 };
241 
242 struct Vec3
243 {
244     float x;
245     float y;
246     float z;
247 };
248 
249 struct MeshSubset
250 {
251     // std::numeric_limits<quint32>::max() means use all available items
252     quint32 m_count;
253     // Offset is in item size, not bytes.
254     quint32 m_offset;
255     // Bounds of this subset.  This is filled in by the builder
256     // see AddMeshSubset
257     QSSGBounds3 m_bounds;
258 
259     // Subsets have to be named else artists will be unable to use
260     // a mesh with multiple subsets as they won't have any idea
261     // while part of the model a given mesh actually maps to.
262     OffsetDataRef<char16_t> m_name;
263 
MeshSubsetMeshSubset264     MeshSubset(quint32 count, quint32 off, const QSSGBounds3 &bounds, OffsetDataRef<char16_t> inName)
265         : m_count(count), m_offset(off), m_bounds(bounds), m_name(inName)
266     {
267     }
MeshSubsetMeshSubset268     MeshSubset() : m_count(quint32(-1)), m_offset(0), m_bounds() {}
hasCountMeshSubset269     bool hasCount() const { return m_count != 4294967295U; } // AKA U_MAX 0xffffffff
270 };
271 
272 struct Joint
273 {
274     qint32 m_jointID;
275     qint32 m_parentID;
276     float m_invBindPose[16];
277     float m_localToGlobalBoneSpace[16];
278 
JointJoint279     Joint(qint32 jointID, qint32 parentID, const float *invBindPose, const float *localToGlobalBoneSpace)
280         : m_jointID(jointID), m_parentID(parentID)
281     {
282         ::memcpy(m_invBindPose, invBindPose, sizeof(float) * 16);
283         ::memcpy(m_localToGlobalBoneSpace, localToGlobalBoneSpace, sizeof(float) * 16);
284     }
JointJoint285     Joint() : m_jointID(-1), m_parentID(-1)
286     {
287         ::memset(m_invBindPose, 0, sizeof(float) * 16);
288         ::memset(m_localToGlobalBoneSpace, 0, sizeof(float) * 16);
289     }
290 };
291 
292 // Tells us what offset a mesh with this ID starts.
293 struct MeshMultiEntry
294 {
295     quint64 m_meshOffset;
296     quint32 m_meshId;
297     quint32 m_padding;
MeshMultiEntryMeshMultiEntry298     MeshMultiEntry() : m_meshOffset(0), m_meshId(0), m_padding(0) {}
MeshMultiEntryMeshMultiEntry299     MeshMultiEntry(quint64 mo, quint32 meshId) : m_meshOffset(mo), m_meshId(meshId), m_padding(0) {}
300 };
301 
302 // The multi headers are actually saved at the end of the file.
303 // Thus when you append to the file we overwrite the last header
304 // then write out a new header structure.
305 // The last 8 bytes of the file contain the multi header.
306 // The previous N*8 bytes contain the mesh entries.
307 struct MeshMultiHeader
308 {
309     quint32 m_fileId;
310     quint32 m_version;
311     OffsetDataRef<MeshMultiEntry> m_entries;
getMultiStaticFileIdMeshMultiHeader312     static quint32 getMultiStaticFileId() { return 555777497U; }
getMultiStaticVersionMeshMultiHeader313     static quint32 getMultiStaticVersion() { return 1; }
314 
MeshMultiHeaderMeshMultiHeader315     MeshMultiHeader() : m_fileId(getMultiStaticFileId()), m_version(getMultiStaticVersion()) {}
316 };
317 
318 struct Mesh;
319 
320 // Result of a multi-load operation.  This returns both the mesh
321 // and the id of the mesh that was loaded.
322 struct MultiLoadResult
323 {
324     Mesh *m_mesh;
325     quint32 m_id;
MultiLoadResultMultiLoadResult326     MultiLoadResult(Mesh *inMesh, quint32 inId) : m_mesh(inMesh), m_id(inId) {}
MultiLoadResultMultiLoadResult327     MultiLoadResult() : m_mesh(nullptr), m_id(0) {}
328     operator Mesh *() { return m_mesh; }
329 };
330 
331 struct Q_QUICK3DASSETIMPORT_EXPORT Mesh
332 {
333     static const char16_t *m_defaultName;
334 
335     VertexBuffer m_vertexBuffer;
336     IndexBuffer m_indexBuffer;
337     OffsetDataRef<MeshSubset> m_subsets;
338     OffsetDataRef<Joint> m_joints;
339     QSSGRenderDrawMode m_drawMode;
340     QSSGRenderWinding m_winding;
341 
MeshMesh342     Mesh() : m_drawMode(QSSGRenderDrawMode::Triangles), m_winding(QSSGRenderWinding::CounterClockwise) {}
343     Mesh(VertexBuffer vbuf,
344          IndexBuffer ibuf,
345          const OffsetDataRef<MeshSubset> &insts,
346          const OffsetDataRef<Joint> &joints,
347          QSSGRenderDrawMode drawMode = QSSGRenderDrawMode::Triangles,
348          QSSGRenderWinding winding = QSSGRenderWinding::CounterClockwise)
m_vertexBufferMesh349         : m_vertexBuffer(vbuf), m_indexBuffer(ibuf), m_subsets(insts), m_joints(joints), m_drawMode(drawMode), m_winding(winding)
350     {
351     }
352 
getBaseAddressMesh353     quint8 *getBaseAddress() { return reinterpret_cast<quint8 *>(this); }
getBaseAddressMesh354     const quint8 *getBaseAddress() const { return reinterpret_cast<const quint8 *>(this); }
355 
getPositionAttrNameMesh356     static const char *getPositionAttrName() { return "attr_pos"; }
getNormalAttrNameMesh357     static const char *getNormalAttrName() { return "attr_norm"; }
getUVAttrNameMesh358     static const char *getUVAttrName() { return "attr_uv0"; }
getUV2AttrNameMesh359     static const char *getUV2AttrName() { return "attr_uv1"; }
getTexTanAttrNameMesh360     static const char *getTexTanAttrName() { return "attr_textan"; }
getTexBinormalAttrNameMesh361     static const char *getTexBinormalAttrName() { return "attr_binormal"; }
getWeightAttrNameMesh362     static const char *getWeightAttrName() { return "attr_weight"; }
getBoneIndexAttrNameMesh363     static const char *getBoneIndexAttrName() { return "attr_boneid"; }
getColorAttrNameMesh364     static const char *getColorAttrName() { return "attr_color"; }
365 
366     // Run through the vertex buffer items indicated by subset
367     // Assume vbuf entry[posEntryIndex] is the position entry
368     // This entry has to be QT3DSF32 and 3 components.
369     // Using this entry and the (possibly empty) index buffer
370     // along with the (possibly emtpy) logical vbuf data
371     // return a bounds of the given vertex buffer.
372     static QSSGBounds3 calculateSubsetBounds(const QSSGRenderVertexBufferEntry &inEntry,
373                                                const QByteArray &inVertxData,
374                                                quint32 inStride,
375                                                const QByteArray &inIndexData,
376                                                QSSGRenderComponentType inIndexCompType,
377                                                quint32 inSubsetCount,
378                                                quint32 inSubsetOffset);
379 
380     // Format is:
381     // MeshDataHeader
382     // mesh data.
383 
384     void save(QIODevice &outStream) const;
385 
386     // Save a mesh using fopen and fwrite
387     bool save(const char *inFilePath) const;
388 
389     // read the header, then read the object.
390     // Load a mesh using fopen and fread
391     // Mesh needs to be freed by the caller using free
392     static Mesh *load(QIODevice &inStream);
393     static Mesh *load(const char *inFilePath);
394 
395     // Create a mesh given this header, and that data.  data.size() must match
396     // header.SizeInBytes.  The mesh returned starts a data[0], so however data
397     // was allocated is how the mesh should be deallocated.
398     static Mesh *initialize(quint16 meshVersion, quint16 meshFlags, QSSGByteView data);
399 
400     // You can save multiple meshes in a file.  Each mesh returns an incrementing
401     // integer for the multi file.  The original meshes aren't changed, and the file
402     // is appended to.
403     quint32 saveMulti(QIODevice &inStream, quint32 inId = 0) const;
404     quint32 saveMulti(const char *inFilePath) const;
405 
406     // Load a single mesh using c file API and malloc/free.
407     static MultiLoadResult loadMulti(QIODevice &inStream, quint32 inId);
408     static MultiLoadResult loadMulti(const char *inFilePath, quint32 inId);
409 
410     // Returns true if this is a multimesh (several meshes in one file).
411     static bool isMulti(QIODevice &inStream);
412 
413     // Load a multi header from a file using malloc.  Header needs to be freed using free.
414     static MeshMultiHeader *loadMultiHeader(QIODevice &inStream);
415     static MeshMultiHeader *loadMultiHeader(const char *inFilePath);
416 
417     // Get the highest mesh version from a file.
418     static quint32 getHighestMultiVersion(QIODevice &inStream);
419     static quint32 getHighestMultiVersion(const char *inFilePath);
420 };
421 
422 struct MeshDataHeader
423 {
getFileIdMeshDataHeader424     static quint32 getFileId() { return quint32(-929005747); }
getCurrentFileVersionMeshDataHeader425     static quint16 getCurrentFileVersion() { return 3; }
426     quint32 m_fileId;
427     quint16 m_fileVersion;
428     quint16 m_headerFlags;
429     quint32 m_sizeInBytes;
430     MeshDataHeader(quint32 size = 0)
m_fileIdMeshDataHeader431         : m_fileId(getFileId()), m_fileVersion(getCurrentFileVersion()), m_sizeInBytes(size)
432     {
433     }
434 };
435 
436 struct MeshBuilderVBufEntry
437 {
438     const char *m_name;
439     QByteArray m_data;
440     QSSGRenderComponentType m_componentType;
441     quint32 m_numComponents;
MeshBuilderVBufEntryMeshBuilderVBufEntry442     MeshBuilderVBufEntry() : m_name(nullptr), m_componentType(QSSGRenderComponentType::Unknown), m_numComponents(0)
443     {
444     }
MeshBuilderVBufEntryMeshBuilderVBufEntry445     MeshBuilderVBufEntry(const char *name, const QByteArray &data, QSSGRenderComponentType componentType, quint32 numComponents)
446         : m_name(name), m_data(data), m_componentType(componentType), m_numComponents(numComponents)
447     {
448     }
449 };
450 
451 // Useful class to build up a mesh.  Necessary since meshes don't include that
452 // sort of utility.
453 class Q_QUICK3DASSETIMPORT_EXPORT QSSGMeshBuilder
454 {
455 public:
456     QAtomicInt ref;
457     virtual ~QSSGMeshBuilder();
458     virtual void release() = 0;
459     virtual void reset() = 0;
460     // Set the draw parameters for any subsets.  Defaults to triangles and counter clockwise
461     virtual void setDrawParameters(QSSGRenderDrawMode drawMode, QSSGRenderWinding winding) = 0;
462     // Set the vertex buffer and have the mesh builder interleave the data for you
463     virtual bool setVertexBuffer(const QVector<MeshBuilderVBufEntry> &entries) = 0;
464     // Set the vertex buffer from interleaved data.
465     virtual void setVertexBuffer(const QVector<QSSGRenderVertexBufferEntry> &entries, quint32 stride, QByteArray data) = 0;
466     // The builder (and the majority of the rest of the product) only supports unsigned 16 bit
467     // indexes
468     virtual void setIndexBuffer(const QByteArray &data, QSSGRenderComponentType comp) = 0;
469     // Assets if the supplied parameters are out of range.
470     virtual void addJoint(qint32 jointID, qint32 parentID, const float *invBindPose, const float *localToGlobalBoneSpace) = 0;
471     /**
472      *  Add a subset, which equates roughly to a draw call.
473      *  A logical vertex buffer allows you to have more that 64K vertexes but still
474      *  use u16 index buffers.  In any case, if the mesh has an index buffer then this subset
475      *  refers to that index buffer, else it is assumed to index into the vertex buffer.
476      *  count and offset do exactly what they seem to do, while boundsPositionEntryIndex,
477      *  if set to something other than std::numeric_limits<quint32>::max(),
478      *  drives the calculation of the aa-bounds of the subset using mesh::CalculateSubsetBounds.
479      */
480     virtual void addMeshSubset(const char16_t *inSubsetName = Mesh::m_defaultName,
481                                quint32 count = std::numeric_limits<quint32>::max(),
482                                quint32 offset = 0,
483                                quint32 boundsPositionEntryIndex = std::numeric_limits<quint32>::max()) = 0;
484 
485     virtual void addMeshSubset(const char16_t *inSubsetName, quint32 count, quint32 offset, const QSSGBounds3 &inBounds) = 0;
486 
487     // Call to optimize the index and vertex buffers.  This doesn't change the subset information,
488     // each triangle is rendered precisely the same.
489     // It just orders the vertex data so we iterate through it as linearly as possible.
490     // This *only* works if the *entire* builder is using triangles as the draw mode.  This will be
491     // a disaster if that condition is not met.
492     virtual void optimizeMesh() = 0;
493 
494     /**
495      * @brief This functions stitches together sub-meshes with the same material.
496      *		 This re-writes the index buffer
497      *
498      * @return no return.
499      */
500     virtual void connectSubMeshes() = 0;
501 
502     // Return the current mesh.  This is only good for this function call, item may change or be
503     // released
504     // due to any further function calls.
505     virtual Mesh &getMesh() = 0;
506 
507     virtual Mesh *buildMesh(const MeshData &data, QString &error, const QSSGBounds3 &inBounds) = 0;
508 
509     // Uses new/delete.
510     static QSSGRef<QSSGMeshBuilder> createMeshBuilder();
511 };
512 
513 } // end QSSGMeshUtilities namespace
514 
515 QT_END_NAMESPACE
516 
517 #endif // QSSGMESHUTILITIES_P_H
518