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