7#include <QtQuick3DUtils/private/qssgassert_p.h>
14QSSGMeshBVHBuilder::QSSGMeshBVHBuilder(
const QSSGMesh::Mesh &mesh)
17 const QSSGMesh::Mesh::VertexBuffer vb = mesh.vertexBuffer();
18 const QSSGMesh::Mesh::IndexBuffer ib = mesh.indexBuffer();
19 m_vertexBufferData = vb.data;
20 m_indexBufferData = ib.data;
21 m_indexBufferComponentType = QSSGRenderComponentType(ib.componentType);
22 if (m_indexBufferComponentType == QSSGRenderComponentType::Int16)
23 m_indexBufferComponentType = QSSGRenderComponentType::UnsignedInt16;
24 else if (m_indexBufferComponentType == QSSGRenderComponentType::Int32)
25 m_indexBufferComponentType = QSSGRenderComponentType::UnsignedInt32;
30 for (quint32 entryIdx = 0, entryEnd = vb.entries.size(); entryIdx < entryEnd; ++entryIdx) {
31 QSSGRenderVertexBufferEntry entry = vb.entries[entryIdx].toRenderVertexBufferEntry();
32 if (!strcmp(entry.m_name, QSSGMesh::MeshInternal::getPositionAttrName())) {
33 m_hasPositionData =
true;
34 m_vertexPosOffset = entry.m_firstItemOffset;
35 }
else if (!strcmp(entry.m_name, QSSGMesh::MeshInternal::getUV0AttrName())) {
37 m_vertexUVOffset = entry.m_firstItemOffset;
38 }
else if (!m_hasUVData && !strcmp(entry.m_name, QSSGMesh::MeshInternal::getUV1AttrName())) {
40 m_vertexUVOffset = entry.m_firstItemOffset;
43 m_vertexStride = vb.stride;
46QSSGMeshBVHBuilder::QSSGMeshBVHBuilder(
const QByteArray &vertexBuffer,
52 const QByteArray &indexBuffer,
53 QSSGRenderComponentType indexBufferType)
55 m_vertexBufferData = vertexBuffer;
56 m_vertexStride = stride;
57 m_hasPositionData =
true;
58 m_vertexPosOffset = posOffset;
60 m_vertexUVOffset = uvOffset;
61 m_hasIndexBuffer = hasIndexBuffer;
62 m_indexBufferData = indexBuffer;
63 m_indexBufferComponentType = indexBufferType;
64 if (m_indexBufferComponentType == QSSGRenderComponentType::Int16)
65 m_indexBufferComponentType = QSSGRenderComponentType::UnsignedInt16;
66 else if (m_indexBufferComponentType == QSSGRenderComponentType::Int32)
67 m_indexBufferComponentType = QSSGRenderComponentType::UnsignedInt32;
70std::unique_ptr<QSSGMeshBVH> QSSGMeshBVHBuilder::buildTree()
73 if (m_mesh.isValid() && m_mesh.drawMode() != QSSGMesh::Mesh::DrawMode::Triangles)
76 auto meshBvh = std::make_unique<QSSGMeshBVH>();
77 auto &roots = meshBvh->m_roots;
78 auto &triangleBounds = meshBvh->m_triangles;
81 quint32 indexCount = 0;
83 indexCount = quint32(m_indexBufferData.size() / QSSGBaseTypeHelpers::getSizeOfType(m_indexBufferComponentType));
85 indexCount = m_vertexBufferData.size() / m_vertexStride;
86 triangleBounds = calculateTriangleBounds(0, indexCount);
89 if (m_mesh.isValid()) {
90 const QVector<QSSGMesh::Mesh::Subset> subsets = m_mesh.subsets();
91 roots.reserve(subsets.size());
92 for (quint32 subsetIdx = 0, subsetEnd = subsets.size(); subsetIdx < subsetEnd; ++subsetIdx) {
93 const QSSGMesh::Mesh::Subset &source(subsets[subsetIdx]);
94 QSSGMeshBVHNode::Handle root = meshBvh->newHandle();
97 const quint32 triangleOffset = source.offset / 3;
98 const quint32 triangleCount = source.count / 3;
99 root->boundingData = getBounds(*meshBvh, triangleOffset, triangleCount);
101 root = splitNode(*meshBvh, root, triangleOffset, triangleCount);
102 roots.push_back(root);
106 QSSGMeshBVHNode::Handle root = meshBvh->newHandle();
107 root->boundingData = getBounds(*meshBvh, 0, quint32(triangleBounds.size()));
108 root = splitNode(*meshBvh, root, 0, quint32(triangleBounds.size()));
109 roots.push_back(root);
116template <QSSGRenderComponentType ComponentType>
119 Q_ASSERT(index < indexCount);
120 Q_STATIC_ASSERT(ComponentType == QSSGRenderComponentType::UnsignedInt16 || ComponentType == QSSGRenderComponentType::UnsignedInt32);
123 if constexpr (ComponentType == QSSGRenderComponentType::UnsignedInt16) {
124 QSSGDataView<quint16> shortIndex(
reinterpret_cast<
const quint16 *>(indexBufferData.begin()), indexCount);
125 result = shortIndex[index];
126 }
else if (ComponentType == QSSGRenderComponentType::UnsignedInt32) {
127 QSSGDataView<quint32> longIndex(
reinterpret_cast<
const quint32 *>(indexBufferData.begin()), indexCount);
128 result = longIndex[index];
136 const quint32 offset = index * vertexStride + vertexPosOffset;
137 Q_ASSERT(qsizetype(offset) < vertexBufferData.size());
138 const QVector3D *position =
reinterpret_cast<
const QVector3D *>(vertexBufferData.begin() + offset);
145 const quint32 offset = index * vertexStride + vertexUVOffset;
146 Q_ASSERT(qsizetype(offset) < vertexBufferData.size());
147 const QVector2D *uv =
reinterpret_cast<
const QVector2D *>(vertexBufferData.begin() + offset);
152template <QSSGRenderComponentType ComponentType,
bool hasIndexBuffer,
bool hasPositionData,
bool hasUVData>
155 const QByteArray &indexBufferData,
156 const QByteArray &vertexBufferData,
160 QSSGMeshBVHTriangles &triangleBounds)
162 const quint32 triangleCount = indexCount / 3;
163 triangleBounds.reserve(triangleCount);
165 for (quint32 i = 0; i < triangleCount; ++i) {
166 QSSGMeshBVHTriangle triangle{};
167 if constexpr (hasIndexBuffer || hasPositionData || hasUVData) {
169 const quint32 triangleIndex = i * 3 + indexOffset;
171 quint32 index1 = triangleIndex + 0;
172 quint32 index2 = triangleIndex + 1;
173 quint32 index3 = triangleIndex + 2;
175 if constexpr (hasIndexBuffer) {
176 index1 = getIndexBufferValue<ComponentType>(index1, indexCount, indexBufferData);
177 index2 = getIndexBufferValue<ComponentType>(index2, indexCount, indexBufferData);
178 index3 = getIndexBufferValue<ComponentType>(index3, indexCount, indexBufferData);
181 if constexpr (hasPositionData) {
182 triangle.vertex1 = getVertexBufferValuePosition(index1, vertexStride, vertexPosOffset, vertexBufferData);
183 triangle.vertex2 = getVertexBufferValuePosition(index2, vertexStride, vertexPosOffset, vertexBufferData);
184 triangle.vertex3 = getVertexBufferValuePosition(index3, vertexStride, vertexPosOffset, vertexBufferData);
187 if constexpr (hasUVData) {
188 triangle.uvCoord1 = getVertexBufferValueUV(index1, vertexStride, vertexUVOffset, vertexBufferData);
189 triangle.uvCoord2 = getVertexBufferValueUV(index2, vertexStride, vertexUVOffset, vertexBufferData);
190 triangle.uvCoord3 = getVertexBufferValueUV(index3, vertexStride, vertexUVOffset, vertexBufferData);
194 triangle.bounds.include(triangle.vertex1);
195 triangle.bounds.include(triangle.vertex2);
196 triangle.bounds.include(triangle.vertex3);
197 triangleBounds.push_back(triangle);
201QSSGMeshBVHTriangles QSSGMeshBVHBuilder::calculateTriangleBounds(quint32 indexOffset, quint32 indexCount)
const
203 QSSGMeshBVHTriangles data;
205 using CalcTriangleBoundsFn =
void (*)(quint32, quint32,
const QByteArray &,
const QByteArray &,
const quint32,
const quint32,
const quint32, QSSGMeshBVHTriangles &);
206 static const CalcTriangleBoundsFn calcTriangleBounds16Fns[] { &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
false,
false,
false>,
207 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
false,
false,
true>,
208 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
false,
true,
false>,
209 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
false,
true,
true>,
210 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
true,
false,
false>,
211 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
true,
false,
true>,
212 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
true,
true,
false>,
213 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
true,
true,
true> };
215 static const CalcTriangleBoundsFn calcTriangleBounds32Fns[] { &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
false,
false,
false>,
216 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
false,
false,
true>,
217 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
false,
true,
false>,
218 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
false,
true,
true>,
219 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
true,
false,
false>,
220 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
true,
false,
true>,
221 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
true,
true,
false>,
222 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
true,
true,
true> };
225 const size_t idx = (size_t(m_hasIndexBuffer) << 2u) | (size_t(m_hasPositionData) << 1u) | (size_t(m_hasUVData));
227 if (m_indexBufferComponentType == QSSGRenderComponentType::UnsignedInt16)
228 calcTriangleBounds16Fns[idx](indexOffset, indexCount, m_indexBufferData, m_vertexBufferData, m_vertexStride, m_vertexUVOffset, m_vertexPosOffset, data);
229 else if (m_indexBufferComponentType == QSSGRenderComponentType::UnsignedInt32)
230 calcTriangleBounds32Fns[idx](indexOffset, indexCount, m_indexBufferData, m_vertexBufferData, m_vertexStride, m_vertexUVOffset, m_vertexPosOffset, data);
234QSSGMeshBVHNode::Handle QSSGMeshBVHBuilder::splitNode(QSSGMeshBVH &bvh, QSSGMeshBVHNode::Handle node, quint32 offset, quint32 count, quint32 depth)
241 if (count < QSSG_MAX_LEAF_TRIANGLES || depth >= QSSG_MAX_TREE_DEPTH) {
242 node->offset = offset;
248 const QSSGMeshBVHBuilder::Split split = getOptimalSplit(bvh, node->boundingData, offset, count);
251 if (split.axis == QSSGMeshBVHBuilder::Axis::None) {
252 node->offset = offset;
260 const quint32 splitOffset = partition(bvh, offset, count, split);
263 if (splitOffset == offset || splitOffset == (offset + count)) {
266 node->offset = offset;
270 node->left = bvh.newHandle();
271 const quint32 leftOffset = offset;
272 const quint32 leftCount = splitOffset - offset;
273 node->left->boundingData = getBounds(bvh, leftOffset, leftCount);
274 node->left = splitNode(bvh, node->left, leftOffset, leftCount, depth + 1);
277 node->right = bvh.newHandle();
278 const quint32 rightOffset = splitOffset;
279 const quint32 rightCount = count - leftCount;
280 node->right->boundingData = getBounds(bvh, rightOffset, rightCount);
281 node->right = splitNode(bvh, node->right, rightOffset, rightCount, depth + 1);
287QSSGBounds3 QSSGMeshBVHBuilder::getBounds(
const QSSGMeshBVH &bvh, quint32 offset, quint32 count)
289 QSSGBounds3 totalBounds;
290 const auto &triangleBounds = bvh.triangles();
292 for (quint32 i = 0; i < count; ++i) {
293 const QSSGBounds3 &bounds = triangleBounds[i + offset].bounds;
294 totalBounds.include(bounds);
299QSSGMeshBVHBuilder::Split QSSGMeshBVHBuilder::getOptimalSplit(
const QSSGMeshBVH &bvh,
const QSSGBounds3 &nodeBounds, quint32 offset, quint32 count)
301 QSSGMeshBVHBuilder::Split split;
302 split.axis = getLongestDimension(nodeBounds);
305 if (split.axis != Axis::None)
306 split.pos = getAverageValue(bvh, offset, count, split.axis);
311QSSGMeshBVHBuilder::Axis QSSGMeshBVHBuilder::getLongestDimension(
const QSSGBounds3 &nodeBounds)
313 QSSGMeshBVHBuilder::Axis axis = Axis::None;
314 float largestDistance = std::numeric_limits<
float>::min();
316 if (!nodeBounds.isFinite() || nodeBounds.isEmpty())
319 const QVector3D delta = nodeBounds.maximum - nodeBounds.minimum;
321 if (delta.x() > largestDistance) {
323 largestDistance = delta.x();
325 if (delta.y() > largestDistance) {
327 largestDistance = delta.y();
329 if (delta.z() > largestDistance)
336float QSSGMeshBVHBuilder::getAverageValue(
const QSSGMeshBVH &bvh, quint32 offset, quint32 count, QSSGMeshBVHBuilder::Axis axis)
340 Q_ASSERT(axis != Axis::None);
341 Q_ASSERT(count != 0);
343 const auto &triangleBounds = bvh.triangles();
344 for (quint32 i = 0; i < count; ++i)
345 average += triangleBounds[i + offset].bounds.center(
int(axis));
347 return average / count;
350quint32 QSSGMeshBVHBuilder::partition(QSSGMeshBVH &bvh, quint32 offset, quint32 count,
const QSSGMeshBVHBuilder::Split &split)
353 int right = offset + count - 1;
354 const float pos = split.pos;
355 const int axis =
int(split.axis);
357 auto &triangleBounds = bvh.m_triangles;
359 while (left <= right && triangleBounds[left].bounds.center(axis) < pos)
362 while (left <= right && triangleBounds[right].bounds.center(axis) >= pos)
367 std::swap(triangleBounds[left], triangleBounds[right]);
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE constexpr quint32 QSSG_MAX_TREE_DEPTH
static QVector2D getVertexBufferValueUV(quint32 index, const quint32 vertexStride, const quint32 vertexUVOffset, const QByteArray &vertexBufferData)
static quint32 getIndexBufferValue(quint32 index, const quint32 indexCount, const QByteArray &indexBufferData)
static void calculateTriangleBoundsImpl(quint32 indexOffset, quint32 indexCount, const QByteArray &indexBufferData, const QByteArray &vertexBufferData, const quint32 vertexStride, const quint32 vertexUVOffset, const quint32 vertexPosOffset, QSSGMeshBVHTriangles &triangleBounds)
static constexpr quint32 QSSG_MAX_LEAF_TRIANGLES
static QVector3D getVertexBufferValuePosition(quint32 index, const quint32 vertexStride, const quint32 vertexPosOffset, const QByteArray &vertexBufferData)