5#include <QtQuick3DUtils/private/qssgassert_p.h>
12QSSGMeshBVHBuilder::QSSGMeshBVHBuilder(
const QSSGMesh::Mesh &mesh)
15 const QSSGMesh::Mesh::VertexBuffer vb = mesh.vertexBuffer();
16 const QSSGMesh::Mesh::IndexBuffer ib = mesh.indexBuffer();
17 m_vertexBufferData = vb.data;
18 m_indexBufferData = ib.data;
19 m_indexBufferComponentType = QSSGRenderComponentType(ib.componentType);
20 if (m_indexBufferComponentType == QSSGRenderComponentType::Int16)
21 m_indexBufferComponentType = QSSGRenderComponentType::UnsignedInt16;
22 else if (m_indexBufferComponentType == QSSGRenderComponentType::Int32)
23 m_indexBufferComponentType = QSSGRenderComponentType::UnsignedInt32;
28 for (quint32 entryIdx = 0, entryEnd = vb.entries.size(); entryIdx < entryEnd; ++entryIdx) {
29 QSSGRenderVertexBufferEntry entry = vb.entries[entryIdx].toRenderVertexBufferEntry();
30 if (!strcmp(entry.m_name, QSSGMesh::MeshInternal::getPositionAttrName())) {
31 m_hasPositionData =
true;
32 m_vertexPosOffset = entry.m_firstItemOffset;
33 }
else if (!strcmp(entry.m_name, QSSGMesh::MeshInternal::getUV0AttrName())) {
35 m_vertexUVOffset = entry.m_firstItemOffset;
36 }
else if (!m_hasUVData && !strcmp(entry.m_name, QSSGMesh::MeshInternal::getUV1AttrName())) {
38 m_vertexUVOffset = entry.m_firstItemOffset;
41 m_vertexStride = vb.stride;
44QSSGMeshBVHBuilder::QSSGMeshBVHBuilder(
const QByteArray &vertexBuffer,
50 const QByteArray &indexBuffer,
51 QSSGRenderComponentType indexBufferType)
53 m_vertexBufferData = vertexBuffer;
54 m_vertexStride = stride;
55 m_hasPositionData =
true;
56 m_vertexPosOffset = posOffset;
58 m_vertexUVOffset = uvOffset;
59 m_hasIndexBuffer = hasIndexBuffer;
60 m_indexBufferData = indexBuffer;
61 m_indexBufferComponentType = indexBufferType;
62 if (m_indexBufferComponentType == QSSGRenderComponentType::Int16)
63 m_indexBufferComponentType = QSSGRenderComponentType::UnsignedInt16;
64 else if (m_indexBufferComponentType == QSSGRenderComponentType::Int32)
65 m_indexBufferComponentType = QSSGRenderComponentType::UnsignedInt32;
68std::unique_ptr<QSSGMeshBVH> QSSGMeshBVHBuilder::buildTree()
71 if (m_mesh.isValid() && m_mesh.drawMode() != QSSGMesh::Mesh::DrawMode::Triangles)
74 auto meshBvh = std::make_unique<QSSGMeshBVH>();
75 auto &roots = meshBvh->m_roots;
76 auto &triangleBounds = meshBvh->m_triangles;
79 quint32 indexCount = 0;
81 indexCount = quint32(m_indexBufferData.size() / QSSGBaseTypeHelpers::getSizeOfType(m_indexBufferComponentType));
83 indexCount = m_vertexBufferData.size() / m_vertexStride;
84 triangleBounds = calculateTriangleBounds(0, indexCount);
87 if (m_mesh.isValid()) {
88 const QVector<QSSGMesh::Mesh::Subset> subsets = m_mesh.subsets();
89 roots.reserve(subsets.size());
90 for (quint32 subsetIdx = 0, subsetEnd = subsets.size(); subsetIdx < subsetEnd; ++subsetIdx) {
91 const QSSGMesh::Mesh::Subset &source(subsets[subsetIdx]);
92 QSSGMeshBVHNode::Handle root = meshBvh->newHandle();
95 const quint32 triangleOffset = source.offset / 3;
96 const quint32 triangleCount = source.count / 3;
97 root->boundingData = getBounds(*meshBvh, triangleOffset, triangleCount);
99 root = splitNode(*meshBvh, root, triangleOffset, triangleCount);
100 roots.push_back(root);
104 QSSGMeshBVHNode::Handle root = meshBvh->newHandle();
105 root->boundingData = getBounds(*meshBvh, 0, quint32(triangleBounds.size()));
106 root = splitNode(*meshBvh, root, 0, quint32(triangleBounds.size()));
107 roots.push_back(root);
114template <QSSGRenderComponentType ComponentType>
117 Q_ASSERT(index < indexCount);
118 Q_STATIC_ASSERT(ComponentType == QSSGRenderComponentType::UnsignedInt16 || ComponentType == QSSGRenderComponentType::UnsignedInt32);
121 if constexpr (ComponentType == QSSGRenderComponentType::UnsignedInt16) {
122 QSSGDataView<quint16> shortIndex(
reinterpret_cast<
const quint16 *>(indexBufferData.begin()), indexCount);
123 result = shortIndex[index];
124 }
else if (ComponentType == QSSGRenderComponentType::UnsignedInt32) {
125 QSSGDataView<quint32> longIndex(
reinterpret_cast<
const quint32 *>(indexBufferData.begin()), indexCount);
126 result = longIndex[index];
134 const quint32 offset = index * vertexStride + vertexPosOffset;
135 Q_ASSERT(offset < vertexBufferData.size());
136 const QVector3D *position =
reinterpret_cast<
const QVector3D *>(vertexBufferData.begin() + offset);
143 const quint32 offset = index * vertexStride + vertexUVOffset;
144 Q_ASSERT(offset < vertexBufferData.size());
145 const QVector2D *uv =
reinterpret_cast<
const QVector2D *>(vertexBufferData.begin() + offset);
150template <QSSGRenderComponentType ComponentType,
bool hasIndexBuffer,
bool hasPositionData,
bool hasUVData>
153 const QByteArray &indexBufferData,
154 const QByteArray &vertexBufferData,
158 QSSGMeshBVHTriangles &triangleBounds)
160 const quint32 triangleCount = indexCount / 3;
161 triangleBounds.reserve(triangleCount);
163 for (quint32 i = 0; i < triangleCount; ++i) {
164 QSSGMeshBVHTriangle triangle{};
165 if constexpr (hasIndexBuffer || hasPositionData || hasUVData) {
167 const quint32 triangleIndex = i * 3 + indexOffset;
169 quint32 index1 = triangleIndex + 0;
170 quint32 index2 = triangleIndex + 1;
171 quint32 index3 = triangleIndex + 2;
173 if constexpr (hasIndexBuffer) {
174 index1 = getIndexBufferValue<ComponentType>(index1, indexCount, indexBufferData);
175 index2 = getIndexBufferValue<ComponentType>(index2, indexCount, indexBufferData);
176 index3 = getIndexBufferValue<ComponentType>(index3, indexCount, indexBufferData);
179 if constexpr (hasPositionData) {
180 triangle.vertex1 = getVertexBufferValuePosition(index1, vertexStride, vertexPosOffset, vertexBufferData);
181 triangle.vertex2 = getVertexBufferValuePosition(index2, vertexStride, vertexPosOffset, vertexBufferData);
182 triangle.vertex3 = getVertexBufferValuePosition(index3, vertexStride, vertexPosOffset, vertexBufferData);
185 if constexpr (hasUVData) {
186 triangle.uvCoord1 = getVertexBufferValueUV(index1, vertexStride, vertexUVOffset, vertexBufferData);
187 triangle.uvCoord2 = getVertexBufferValueUV(index2, vertexStride, vertexUVOffset, vertexBufferData);
188 triangle.uvCoord3 = getVertexBufferValueUV(index3, vertexStride, vertexUVOffset, vertexBufferData);
192 triangle.bounds.include(triangle.vertex1);
193 triangle.bounds.include(triangle.vertex2);
194 triangle.bounds.include(triangle.vertex3);
195 triangleBounds.push_back(triangle);
199QSSGMeshBVHTriangles QSSGMeshBVHBuilder::calculateTriangleBounds(quint32 indexOffset, quint32 indexCount)
const
201 QSSGMeshBVHTriangles data;
203 using CalcTriangleBoundsFn =
void (*)(quint32, quint32,
const QByteArray &,
const QByteArray &,
const quint32,
const quint32,
const quint32, QSSGMeshBVHTriangles &);
204 static const CalcTriangleBoundsFn calcTriangleBounds16Fns[] { &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
false,
false,
false>,
205 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
false,
false,
true>,
206 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
false,
true,
false>,
207 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
false,
true,
true>,
208 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
true,
false,
false>,
209 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
true,
false,
true>,
210 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
true,
true,
false>,
211 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt16,
true,
true,
true> };
213 static const CalcTriangleBoundsFn calcTriangleBounds32Fns[] { &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
false,
false,
false>,
214 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
false,
false,
true>,
215 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
false,
true,
false>,
216 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
false,
true,
true>,
217 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
true,
false,
false>,
218 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
true,
false,
true>,
219 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
true,
true,
false>,
220 &calculateTriangleBoundsImpl<QSSGRenderComponentType::UnsignedInt32,
true,
true,
true> };
223 const size_t idx = (size_t(m_hasIndexBuffer) << 2u) | (size_t(m_hasPositionData) << 1u) | (size_t(m_hasUVData));
225 if (m_indexBufferComponentType == QSSGRenderComponentType::UnsignedInt16)
226 calcTriangleBounds16Fns[idx](indexOffset, indexCount, m_indexBufferData, m_vertexBufferData, m_vertexStride, m_vertexUVOffset, m_vertexPosOffset, data);
227 else if (m_indexBufferComponentType == QSSGRenderComponentType::UnsignedInt32)
228 calcTriangleBounds32Fns[idx](indexOffset, indexCount, m_indexBufferData, m_vertexBufferData, m_vertexStride, m_vertexUVOffset, m_vertexPosOffset, data);
232QSSGMeshBVHNode::Handle QSSGMeshBVHBuilder::splitNode(QSSGMeshBVH &bvh, QSSGMeshBVHNode::Handle node, quint32 offset, quint32 count, quint32 depth)
239 if (count < QSSG_MAX_LEAF_TRIANGLES || depth >= QSSG_MAX_TREE_DEPTH) {
240 node->offset = offset;
246 const QSSGMeshBVHBuilder::Split split = getOptimalSplit(bvh, node->boundingData, offset, count);
249 if (split.axis == QSSGMeshBVHBuilder::Axis::None) {
250 node->offset = offset;
258 const quint32 splitOffset = partition(bvh, offset, count, split);
261 if (splitOffset == offset || splitOffset == (offset + count)) {
264 node->offset = offset;
268 node->left = bvh.newHandle();
269 const quint32 leftOffset = offset;
270 const quint32 leftCount = splitOffset - offset;
271 node->left->boundingData = getBounds(bvh, leftOffset, leftCount);
272 node->left = splitNode(bvh, node->left, leftOffset, leftCount, depth + 1);
275 node->right = bvh.newHandle();
276 const quint32 rightOffset = splitOffset;
277 const quint32 rightCount = count - leftCount;
278 node->right->boundingData = getBounds(bvh, rightOffset, rightCount);
279 node->right = splitNode(bvh, node->right, rightOffset, rightCount, depth + 1);
285QSSGBounds3 QSSGMeshBVHBuilder::getBounds(
const QSSGMeshBVH &bvh, quint32 offset, quint32 count)
287 QSSGBounds3 totalBounds;
288 const auto &triangleBounds = bvh.triangles();
290 for (quint32 i = 0; i < count; ++i) {
291 const QSSGBounds3 &bounds = triangleBounds[i + offset].bounds;
292 totalBounds.include(bounds);
297QSSGMeshBVHBuilder::Split QSSGMeshBVHBuilder::getOptimalSplit(
const QSSGMeshBVH &bvh,
const QSSGBounds3 &nodeBounds, quint32 offset, quint32 count)
299 QSSGMeshBVHBuilder::Split split;
300 split.axis = getLongestDimension(nodeBounds);
303 if (split.axis != Axis::None)
304 split.pos = getAverageValue(bvh, offset, count, split.axis);
309QSSGMeshBVHBuilder::Axis QSSGMeshBVHBuilder::getLongestDimension(
const QSSGBounds3 &nodeBounds)
311 QSSGMeshBVHBuilder::Axis axis = Axis::None;
312 float largestDistance = std::numeric_limits<
float>::min();
314 if (!nodeBounds.isFinite() || nodeBounds.isEmpty())
317 const QVector3D delta = nodeBounds.maximum - nodeBounds.minimum;
319 if (delta.x() > largestDistance) {
321 largestDistance = delta.x();
323 if (delta.y() > largestDistance) {
325 largestDistance = delta.y();
327 if (delta.z() > largestDistance)
334float QSSGMeshBVHBuilder::getAverageValue(
const QSSGMeshBVH &bvh, quint32 offset, quint32 count, QSSGMeshBVHBuilder::Axis axis)
338 Q_ASSERT(axis != Axis::None);
339 Q_ASSERT(count != 0);
341 const auto &triangleBounds = bvh.triangles();
342 for (quint32 i = 0; i < count; ++i)
343 average += triangleBounds[i + offset].bounds.center(
int(axis));
345 return average / count;
348quint32 QSSGMeshBVHBuilder::partition(QSSGMeshBVH &bvh, quint32 offset, quint32 count,
const QSSGMeshBVHBuilder::Split &split)
351 int right = offset + count - 1;
352 const float pos = split.pos;
353 const int axis =
int(split.axis);
355 auto &triangleBounds = bvh.m_triangles;
357 while (left <= right && triangleBounds[left].bounds.center(axis) < pos)
360 while (left <= right && triangleBounds[right].bounds.center(axis) >= pos)
365 std::swap(triangleBounds[left], triangleBounds[right]);
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)