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
qssggltfaccessorreader.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
6
7#include <cstring>
8
9QT_BEGIN_NAMESPACE
10
11using namespace QSSGGltf;
12
13/*!
14 \namespace QSSGGltfAccessorReader
15 \internal
16
17 Typed access to glTF accessor data. All functions compose the buffer view
18 byte stride (interleaved data), the accessor byte offset, and sparse
19 substitution, and return tightly packed copies. An accessor without a
20 buffer view yields zeros, as the specification requires.
21*/
22namespace QSSGGltfAccessorReader {
23
24namespace {
25
26// Pointer to the first element and the stride between elements for data in
27// a buffer view; null when the accessor has no buffer view.
28struct DataView {
29 const char *data = nullptr;
30 qsizetype stride = 0;
31};
32
33DataView viewForAccessor(const QSSGGltfDocument &document, const Accessor &accessor)
34{
35 if (accessor.bufferView < 0)
36 return {};
37 const BufferView &view = document.bufferViews.at(accessor.bufferView);
38 const Buffer &buffer = document.buffers.at(view.buffer);
39 DataView result;
40 result.stride = view.byteStride > 0 ? view.byteStride : accessor.elementByteSize();
41 result.data = buffer.data.constData() + view.byteOffset + accessor.byteOffset;
42 return result;
43}
44
45quint32 readIndexValue(const char *data, Accessor::ComponentType componentType)
46{
47 switch (componentType) {
48 case Accessor::ComponentType::UnsignedByte:
49 return *reinterpret_cast<const quint8 *>(data);
50 case Accessor::ComponentType::UnsignedShort: {
51 quint16 value;
52 std::memcpy(&value, data, sizeof(value));
53 return value;
54 }
55 case Accessor::ComponentType::UnsignedInt: {
56 quint32 value;
57 std::memcpy(&value, data, sizeof(value));
58 return value;
59 }
60 default:
61 break;
62 }
63 return 0;
64}
65
66void applySparse(const QSSGGltfDocument &document, const Accessor &accessor, QByteArray &packed)
67{
68 const Accessor::Sparse &sparse = *accessor.sparse;
69 const qsizetype elementSize = accessor.elementByteSize();
70
71 const BufferView &indicesView = document.bufferViews.at(sparse.indicesBufferView);
72 const Buffer &indicesBuffer = document.buffers.at(indicesView.buffer);
73 const char *indices = indicesBuffer.data.constData() + indicesView.byteOffset + sparse.indicesByteOffset;
74 const qsizetype indexSize = Accessor::componentByteSize(sparse.indicesComponentType);
75
76 const BufferView &valuesView = document.bufferViews.at(sparse.valuesBufferView);
77 const Buffer &valuesBuffer = document.buffers.at(valuesView.buffer);
78 const char *values = valuesBuffer.data.constData() + valuesView.byteOffset + sparse.valuesByteOffset;
79
80 for (qint64 i = 0; i < sparse.count; ++i) {
81 const quint32 targetElement = readIndexValue(indices + i * indexSize, sparse.indicesComponentType);
82 if (qint64(targetElement) >= accessor.count)
83 continue; // structurally invalid substitution index; skip
84 std::memcpy(packed.data() + qsizetype(targetElement) * elementSize, values + i * elementSize, elementSize);
85 }
86}
87
88float componentToFloat(const char *data, Accessor::ComponentType componentType, bool normalized)
89{
90 switch (componentType) {
91 case Accessor::ComponentType::Byte: {
92 const qint8 value = *reinterpret_cast<const qint8 *>(data);
93 return normalized ? std::max(value / 127.0f, -1.0f) : float(value);
94 }
95 case Accessor::ComponentType::UnsignedByte: {
96 const quint8 value = *reinterpret_cast<const quint8 *>(data);
97 return normalized ? value / 255.0f : float(value);
98 }
99 case Accessor::ComponentType::Short: {
100 qint16 value;
101 std::memcpy(&value, data, sizeof(value));
102 return normalized ? std::max(value / 32767.0f, -1.0f) : float(value);
103 }
104 case Accessor::ComponentType::UnsignedShort: {
105 quint16 value;
106 std::memcpy(&value, data, sizeof(value));
107 return normalized ? value / 65535.0f : float(value);
108 }
109 case Accessor::ComponentType::UnsignedInt: {
110 quint32 value;
111 std::memcpy(&value, data, sizeof(value));
112 return float(value);
113 }
114 case Accessor::ComponentType::Float: {
115 float value;
116 std::memcpy(&value, data, sizeof(value));
117 return value;
118 }
119 }
120 return 0.0f;
121}
122
123} // namespace
124
125/*!
126 \internal
127
128 Returns the element data of accessor \a accessorIndex in \a document,
129 packed to accessor.elementByteSize() per element.
130*/
131QByteArray readPacked(const QSSGGltfDocument &document, int accessorIndex)
132{
133 if (accessorIndex < 0 || accessorIndex >= document.accessors.size())
134 return {};
135 const Accessor &accessor = document.accessors.at(accessorIndex);
136 const qsizetype elementSize = accessor.elementByteSize();
137
138 QByteArray packed(accessor.count * elementSize, '\0');
139 const DataView view = viewForAccessor(document, accessor);
140 if (view.data) {
141 if (view.stride == elementSize) {
142 std::memcpy(packed.data(), view.data, packed.size());
143 } else {
144 // Bound the loop by what was actually allocated rather than by
145 // accessor.count, so that this stays inside the destination even
146 // if the count and the allocation ever disagree
147 const qint64 count = packed.size() / elementSize;
148 for (qint64 i = 0; i < count; ++i)
149 std::memcpy(packed.data() + i * elementSize, view.data + i * view.stride, elementSize);
150 }
151 }
152
153 if (accessor.sparse.has_value())
154 applySparse(document, accessor, packed);
155
156 return packed;
157}
158
159/*!
160 \internal
161
162 Returns the element data of accessor \a accessorIndex in \a document,
163 converted to floats, accessor.componentCount() per element. Normalized
164 integer component types are dequantized as the specification describes
165 (c/255, max(c/127, -1), ...), which also covers accessors quantized per
166 KHR_mesh_quantization; non-normalized integers are cast.
167*/
168QList<float> readAsFloats(const QSSGGltfDocument &document, int accessorIndex)
169{
170 if (accessorIndex < 0 || accessorIndex >= document.accessors.size())
171 return {};
172 const Accessor &accessor = document.accessors.at(accessorIndex);
173 const QByteArray packed = readPacked(document, accessorIndex);
174 const int components = Accessor::componentCount(accessor.type);
175 const int componentSize = Accessor::componentByteSize(accessor.componentType);
176
177 QList<float> result(accessor.count * components);
178 const char *element = packed.constData();
179 for (qsizetype i = 0; i < result.size(); ++i)
180 result[i] = componentToFloat(element + i * componentSize, accessor.componentType, accessor.normalized);
181 return result;
182}
183
184/*!
185 \internal
186
187 Returns the scalar index data of accessor \a accessorIndex in \a document,
188 widened to 32-bit unsigned integers.
189*/
190QList<quint32> readIndices(const QSSGGltfDocument &document, int accessorIndex)
191{
192 if (accessorIndex < 0 || accessorIndex >= document.accessors.size())
193 return {};
194 const Accessor &accessor = document.accessors.at(accessorIndex);
195 const QByteArray packed = readPacked(document, accessorIndex);
196 const qsizetype componentSize = Accessor::componentByteSize(accessor.componentType);
197
198 QList<quint32> result(accessor.count);
199 for (qint64 i = 0; i < accessor.count; ++i)
200 result[i] = readIndexValue(packed.constData() + i * componentSize, accessor.componentType);
201 return result;
202}
203
204} // namespace QSSGGltfAccessorReader
205
206QT_END_NAMESPACE
QList< float > readAsFloats(const QSSGGltfDocument &document, int accessorIndex)
QList< quint32 > readIndices(const QSSGGltfDocument &document, int accessorIndex)
QByteArray readPacked(const QSSGGltfDocument &document, int accessorIndex)