Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtquick3d-mesh-format.qdoc
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4/*!
5\internal
6\title Qt Quick 3D Mesh File Format
7\page quick3d-mesh-format
8
9This describes the binary layout of the \c .mesh files that
10\l{Balsam Asset Import Tool}{Balsam} writes and that \c Model::source loads. It
11documents what the reader in \c qssgmesh.cpp accepts, so that the parser, the
12writer and any external tooling can be checked against one description.
13
14\section1 Container
15
16A \c .mesh file is a container holding one or more meshes, each with its own
17vertex and index buffers and subset views into them. It is read back to front:
18the last 16 bytes are the footer, the entry list sits immediately before it, and
19each entry gives the absolute offset of one mesh.
20
21All values are little endian. Floating point values are 32-bit. Some strings are
22UTF-8 and some are UTF-16LE, as noted per field.
23
24\badcode
25[mesh 0][mesh 1]...[entry 0][entry 1]...[footer]
26\endcode
27
28\section2 MultiMeshFooter, 16 bytes, at the end of the file
29
30\table
31\header
32 \li Type
33 \li Field
34 \li Notes
35\row
36 \li quint32
37 \li fileId
38 \li 555777497
39\row
40 \li quint32
41 \li version
42 \li 1, the only container version
43\row
44 \li quint32
45 \li entriesOffset
46 \li Ignore, see \l{Offset fields}
47\row
48 \li quint32
49 \li entriesSize
50 \li Number of meshes, and so of entries
51\endtable
52
53\section2 MultiMeshEntry, 16 bytes, entriesSize of them before the footer
54
55\table
56\header
57 \li Type
58 \li Field
59 \li Notes
60\row
61 \li quint64
62 \li meshOffset
63 \li Absolute offset of the mesh. The only offset in the format that can be
64 trusted
65\row
66 \li quint32
67 \li meshId
68 \li Identifies the mesh within the container
69\row
70 \li quint32
71 \li padding
72 \li Unused
73\endtable
74
75\section1 Mesh
76
77\section2 MeshDataHeader, 12 bytes, at meshOffset
78
79\table
80\header
81 \li Type
82 \li Field
83 \li Notes
84\row
85 \li quint32
86 \li fileId
87 \li 3365961549
88\row
89 \li quint16
90 \li fileVersion
91 \li 3 to 7, see \l{Versions}
92\row
93 \li quint16
94 \li headerFlags
95 \li Unused
96\row
97 \li quint32
98 \li sizeInBytes
99 \li Size of the mesh that follows, excluding this header
100\endtable
101
102\section2 Mesh, 56 bytes
103
104Fourteen quint32 fields. Three of them were repurposed in version 7, which is
105why the size did not change:
106
107\table
108\header
109 \li Field, before version 7
110 \li Field, version 7
111 \li Notes
112\row
113 \li vertexBuffer.entriesOffset
114 \li targetBufferEntriesCount
115 \li Ignore before version 7
116\row
117 \li vertexBuffer.entriesSize
118 \li vertexBuffer.entriesSize
119 \li Number of vertex buffer entries
120\row
121 \li vertexBuffer.stride
122 \li vertexBuffer.stride
123 \li Bytes per vertex
124\row
125 \li vertexBuffer.dataOffset
126 \li targetBufferDataSize
127 \li Ignore before version 7
128\row
129 \li vertexBuffer.dataSize
130 \li vertexBuffer.dataSize
131 \li Bytes of vertex data
132\row
133 \li indexBuffer.componentType
134 \li indexBuffer.componentType
135 \li See \l{Component types}
136\row
137 \li indexBuffer.dataOffset
138 \li indexBuffer.dataOffset
139 \li Ignore
140\row
141 \li indexBuffer.dataSize
142 \li indexBuffer.dataSize
143 \li Bytes of index data, zero for a non-indexed mesh
144\row
145 \li subsetsOffset
146 \li targetCount
147 \li Ignore before version 7
148\row
149 \li subsetsSize
150 \li subsetsSize
151 \li Number of subsets
152\row
153 \li jointsOffset
154 \li jointsOffset
155 \li Ignore
156\row
157 \li jointsSize
158 \li jointsSize
159 \li Number of joints, see \l{Joints}
160\row
161 \li drawMode
162 \li drawMode
163 \li See \l{Draw modes}
164\row
165 \li winding
166 \li winding
167 \li See \l{Windings}
168\endtable
169
170\section2 Blocks after the Mesh struct, in file order
171
172\list 1
173\li Vertex buffer entries, \c entriesSize of them, 16 bytes each: a
174 \c nameOffset to ignore, then \c componentType, \c componentCount and the
175 byte \c offset of the attribute within a vertex. Followed by padding.
176\li Vertex buffer entry names, one per entry: a quint32 byte length, then that
177 many bytes of UTF-8 including a terminating zero, then padding.
178\li Vertex buffer data, \c vertexBuffer.dataSize bytes, then padding.
179\li Index buffer data, \c indexBuffer.dataSize bytes, then padding.
180\li Subsets, \c subsetsSize of them, sized by version, followed by padding.
181\li Subset names, one per subset: \c nameLength UTF-16LE code units including a
182 terminating zero, then padding.
183\li Level of detail entries, in subset order, \c lodCount of them per subset,
184 12 bytes each, followed by padding. Version 6 and later.
185\li Joints, \c jointsSize of them, 136 bytes each.
186\li Target buffer entries, names and data, laid out exactly as the vertex buffer
187 ones. Version 7 only.
188\endlist
189
190\section2 Subset
191
19240 bytes before version 5, 48 in version 5, 52 in version 6 and later:
193
194\table
195\header
196 \li Type
197 \li Field
198 \li Notes
199\row
200 \li quint32
201 \li count
202 \li Number of indices, or of vertices when there is no index buffer
203\row
204 \li quint32
205 \li offset
206 \li First index, or first vertex
207\row
208 \li float[6]
209 \li bounds
210 \li Minimum xyz then maximum xyz
211\row
212 \li quint32
213 \li nameOffset
214 \li Ignore
215\row
216 \li quint32
217 \li nameLength
218 \li UTF-16 code units including the terminating zero, so the name occupies
219 twice this many bytes
220\row
221 \li quint32
222 \li lightmapWidth
223 \li Version 5 and later
224\row
225 \li quint32
226 \li lightmapHeight
227 \li Version 5 and later
228\row
229 \li quint32
230 \li lodCount
231 \li Version 6 and later
232\endtable
233
234\section2 Level of detail entry, 12 bytes
235
236A quint32 \c count and \c offset into the index buffer, as for a subset, and a
237float \c distance, the screen space threshold at which the level is used.
238
239\section1 Padding
240
241Every block is aligned to four bytes, but not in the way that phrase usually
242means. The alignment step adds \c{4 - (byteCounter % 4)} bytes, which is a full
243four bytes when the position is already aligned. So an already aligned block
244still carries four bytes of padding.
245
246\note This started as a bug in the original writer, and became part of the
247format the moment files were shipped with it. A reader that skips the padding
248only when it is needed will misparse every file.
249
250The padding follows the vertex buffer entries as a group, not each entry; the
251same is true of the subsets and of the level of detail entries. Entry names and
252subset names are each padded individually.
253
254\section1 Offset fields
255
256Several fields hold an offset that the original implementation filled in while
257parsing, and which was then written back out. They carry no useful information
258and differ between writers: version 3 files have whatever the producing tool
259left there, and version 4 and later write zero. Read them and discard them.
260
261\c MultiMeshEntry::meshOffset is the exception, and is the only offset a reader
262should act on.
263
264\section1 Joints
265
266The 136 byte joint records hold a joint id, a parent id and two 4x4 matrices.
267Qt Quick 3D does not use them, and its writer always emits \c{jointsSize = 0},
268but the block is part of the layout: a reader must skip \c{jointsSize * 136}
269bytes or everything after it will be misparsed. Only files from Qt 3D Studio
270are known to carry joints.
271
272\section1 Versions
273
274The container version is 1 and has never changed. Mesh versions:
275
276\table
277\header
278 \li Version
279 \li Change
280\row
281 \li 1, 2
282 \li Not supported. Unique to Nvidia Drive Design and early Qt 3D Studio
283\row
284 \li 3
285 \li The oldest supported version, and the last that Qt 3D Studio produces
286\row
287 \li 4
288 \li The offset fields are written as zero
289\row
290 \li 5
291 \li Subsets gain a lightmap size hint
292\row
293 \li 6
294 \li Subsets gain levels of detail
295\row
296 \li 7
297 \li Morph target data moves to its own buffer, and three fields of the Mesh
298 struct are repurposed to describe it. Written by current versions
299\endtable
300
301Before version 7 morph targets were extra vertex buffer entries named
302\c attr_tpos0, \c attr_tnorm1 and so on, with anything beyond the supported
303number named \c attr_unsupported. The reader converts these into a version 7
304target buffer on load, so callers see one representation.
305
306\section1 Enumerations
307
308\section2 Component types
309
310\table
311\header
312 \li Value
313 \li Type
314 \li Size
315\row
316 \li 1, 2
317 \li quint8, qint8
318 \li 1
319\row
320 \li 3, 4
321 \li quint16, qint16
322 \li 2
323\row
324 \li 5, 6
325 \li quint32, qint32
326 \li 4
327\row
328 \li 7, 8
329 \li quint64, qint64
330 \li 8
331\row
332 \li 9
333 \li 16-bit float
334 \li 2
335\row
336 \li 10
337 \li 32-bit float
338 \li 4
339\row
340 \li 11
341 \li 64-bit float
342 \li 8
343\endtable
344
345\section2 Draw modes
346
3471 points, 2 line strip, 3 line loop, 4 lines, 5 triangle strip, 6 triangle fan,
3487 triangles, 8 patches.
349
350\section2 Windings
351
3521 clockwise, 2 counter-clockwise.
353
354\section2 Attribute names
355
356\c attr_pos, \c attr_norm, \c attr_uv0, \c attr_uv1, \c attr_textan,
357\c attr_binormal, \c attr_joints, \c attr_weights, \c attr_colors, and the
358morph target names described under \l{Versions}.
359
360\sa {Balsam Asset Import Tool}
361*/