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
qdebugdrawhelper.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
9
10#include <foundation/PxBounds3.h>
11#include <foundation/PxVec3.h>
12#include <geometry/PxConvexMesh.h>
13#include <geometry/PxTriangleMesh.h>
14#include <geometry/PxHeightField.h>
15
16#include <QQuick3DGeometry>
17
18QQuick3DGeometry *QDebugDrawHelper::generateBoxGeometry(const QVector3D &halfExtents)
19{
20 auto boxGeometry = new QQuick3DGeometry();
21 boxGeometry->clear();
22 boxGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
23 QQuick3DGeometry::Attribute::ComponentType::F32Type);
24 boxGeometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 16,
25 QQuick3DGeometry::Attribute::ComponentType::F32Type);
26 boxGeometry->setStride(32);
27 boxGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
28 boxGeometry->setBounds(-halfExtents, halfExtents);
29
30 const float x = halfExtents.x();
31 const float y = halfExtents.y();
32 const float z = halfExtents.z();
33
34 QCollisionDebugMeshBuilder builder;
35 // top
36 builder.addLine(QVector3D(-x, -y, z), QVector3D(-x, y, z));
37 builder.addLine(QVector3D(-x, y, z), QVector3D(x, y, z));
38 builder.addLine(QVector3D(x, y, z), QVector3D(x, -y, z));
39 builder.addLine(QVector3D(x, -y, z), QVector3D(-x, -y, z));
40
41 // bottom
42 builder.addLine(QVector3D(-x, -y, -z), QVector3D(-x, y, -z));
43 builder.addLine(QVector3D(-x, y, -z), QVector3D(x, y, -z));
44 builder.addLine(QVector3D(x, y, -z), QVector3D(x, -y, -z));
45 builder.addLine(QVector3D(x, -y, -z), QVector3D(-x, -y, -z));
46
47 // front
48 builder.addLine(QVector3D(x, -y, z), QVector3D(x, -y, -z));
49 builder.addLine(QVector3D(-x, -y, -z), QVector3D(-x, -y, z));
50
51 // back
52 builder.addLine(QVector3D(x, y, z), QVector3D(x, y, -z));
53 builder.addLine(QVector3D(-x, y, -z), QVector3D(-x, y, z));
54
55 boxGeometry->setVertexData(builder.generateVertexArray());
56
57 return boxGeometry;
58}
59
60QQuick3DGeometry *QDebugDrawHelper::generateSphereGeometry(const float radius)
61{
62 auto sphereGeometry = new QQuick3DGeometry();
63 sphereGeometry->clear();
64 sphereGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
65 QQuick3DGeometry::Attribute::ComponentType::F32Type);
66 sphereGeometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 16,
67 QQuick3DGeometry::Attribute::ComponentType::F32Type);
68 sphereGeometry->setStride(32);
69 sphereGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
70 sphereGeometry->setBounds(QVector3D(-radius, -radius, -radius),
71 QVector3D(radius, radius, radius));
72
73 // One circle around each axis
74 // So create a 2D circle first from points
75 const int circleSegments = 24;
76 constexpr double TAU = 2 * M_PI;
77 const float step = float(TAU / circleSegments);
78 QVector<QVector2D> circlePoints;
79 for (float theta = 0; theta < TAU; theta += step) {
80 const float x = radius * qCos(theta);
81 const float y = radius * qSin(theta);
82 circlePoints.append(QVector2D(x, y));
83 }
84
85 QCollisionDebugMeshBuilder builder;
86 // X
87 for (int i = 0; i < circlePoints.count(); ++i) {
88 const auto refPoint1 = circlePoints[i];
89 int index2 = i + 1;
90 if (index2 == circlePoints.count())
91 index2 = 0;
92 const auto refPoint2 = circlePoints[index2];
93 const auto vertex1 = QVector3D(0.0f, refPoint1.x(), refPoint1.y());
94 const auto vertex2 = QVector3D(0.0f, refPoint2.x(), refPoint2.y());
95 builder.addLine(vertex1, vertex2, QVector3D(1, 0, 0));
96 }
97
98 // Y
99 for (int i = 0; i < circlePoints.count(); ++i) {
100 const auto refPoint1 = circlePoints[i];
101 int index2 = i + 1;
102 if (index2 == circlePoints.count())
103 index2 = 0;
104 const auto refPoint2 = circlePoints[index2];
105 const auto vertex1 = QVector3D(refPoint1.x(), 0.0f, refPoint1.y());
106 const auto vertex2 = QVector3D(refPoint2.x(), 0.0f, refPoint2.y());
107 builder.addLine(vertex1, vertex2, QVector3D(0, 1, 0));
108 }
109
110 // Z
111 for (int i = 0; i < circlePoints.count(); ++i) {
112 const auto refPoint1 = circlePoints[i];
113 int index2 = i + 1;
114 if (index2 == circlePoints.count())
115 index2 = 0;
116 const auto refPoint2 = circlePoints[index2];
117 const auto vertex1 = QVector3D(refPoint1.x(), refPoint1.y(), 0.0f);
118 const auto vertex2 = QVector3D(refPoint2.x(), refPoint2.y(), 0.0f);
119 builder.addLine(vertex1, vertex2, QVector3D(0, 0, 1));
120 }
121 sphereGeometry->setVertexData(builder.generateVertexArray());
122
123 return sphereGeometry;
124}
125
126QQuick3DGeometry *QDebugDrawHelper::generateCapsuleGeometry(const float radius,
127 const float halfHeight)
128{
129 auto capsuleGeometry = new QQuick3DGeometry();
130 capsuleGeometry->clear();
131 capsuleGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
132 QQuick3DGeometry::Attribute::ComponentType::F32Type);
133 capsuleGeometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 16,
134 QQuick3DGeometry::Attribute::ComponentType::F32Type);
135 capsuleGeometry->setStride(32);
136 capsuleGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
137 capsuleGeometry->setBounds(QVector3D(-(halfHeight + radius), -radius, -radius),
138 QVector3D(halfHeight + radius, radius, radius));
139
140 // The total height is height+2*radius, so the height is just the height
141 // between the center of each 'sphere' of the capsule caps.
142
143 // Create a 2D circle first for points
144 const int circleSegments = 32;
145 constexpr double TAU = 2 * M_PI;
146
147 Q_ASSERT(circleSegments % 4 == 0);
148 const float step = float(TAU / circleSegments);
149 QVector<QVector2D> circlePoints;
150 for (float theta = 0; theta < TAU; theta += step) {
151 const float x = radius * qCos(theta);
152 const float y = radius * qSin(theta);
153 circlePoints.append(QVector2D(x, y));
154 }
155
156 QCollisionDebugMeshBuilder builder;
157
158 // Top Y Cirlce (y = height * 0.5)
159 for (int i = 0; i < circlePoints.count(); ++i) {
160 const auto refPoint1 = circlePoints[i];
161 int index2 = i + 1;
162 if (index2 == circlePoints.count())
163 index2 = 0;
164 const auto refPoint2 = circlePoints[index2];
165 const auto vertex1 = QVector3D(halfHeight, refPoint1.x(), refPoint1.y());
166 const auto vertex2 = QVector3D(halfHeight, refPoint2.x(), refPoint2.y());
167 const auto normal = QVector3D(1, 0, 0);
168 builder.addLine(vertex1, vertex2, normal);
169 }
170
171 // Bottom Y Circle (y = -height * 0.5)
172 for (int i = 0; i < circlePoints.count(); ++i) {
173 const auto refPoint1 = circlePoints[i];
174 int index2 = i + 1;
175 if (index2 == circlePoints.count())
176 index2 = 0;
177 const auto refPoint2 = circlePoints[index2];
178 const auto vertex1 = QVector3D(-halfHeight, refPoint1.x(), refPoint1.y());
179 const auto vertex2 = QVector3D(-halfHeight, refPoint2.x(), refPoint2.y());
180 const auto normal = QVector3D(1, 0, 0);
181 builder.addLine(vertex1, vertex2, normal);
182 }
183
184 // Front Cylinder Line (z = radius, y = length , x = 0)
185 {
186 const auto vertex1 = QVector3D(halfHeight, 0, radius);
187 const auto vertex2 = QVector3D(-halfHeight, 0, radius);
188 const auto normal = QVector3D(0, 0, 1);
189 builder.addLine(vertex1, vertex2, normal);
190 }
191
192 // Back Cylinder Line (z = -radius, y = length, x = 0)
193 {
194 const auto vertex1 = QVector3D(halfHeight, 0, -radius);
195 const auto vertex2 = QVector3D(-halfHeight, 0, -radius);
196 const auto normal = QVector3D(0, 0, -1);
197 builder.addLine(vertex1, vertex2, normal);
198 }
199
200 // Left Cylinder Line (x = -radius, y = length, z = 0)
201 {
202 const auto vertex1 = QVector3D(halfHeight, -radius, 0);
203 const auto vertex2 = QVector3D(-halfHeight, -radius, 0);
204 const auto normal = QVector3D(0, -1, 0);
205 builder.addLine(vertex1, vertex2, normal);
206 }
207
208 // Right Cylinder Line (x = radius, y = length, z = 0)
209 {
210 const auto vertex1 = QVector3D(halfHeight, radius, 0);
211 const auto vertex2 = QVector3D(-halfHeight, radius, 0);
212 const auto normal = QVector3D(0, 1, 0);
213 builder.addLine(vertex1, vertex2, normal);
214 }
215
216 // Get half circle values
217 QVector<int> topIndexes;
218 QVector<int> bottomIndexes;
219 {
220 const int half = circlePoints.count() / 2;
221 for (int i = 0; i < half + 1; ++i)
222 topIndexes.append(i);
223
224 for (int i = half; i <= circlePoints.count(); ++i) {
225 int index = i;
226 if (i >= circlePoints.count())
227 index = index - circlePoints.count();
228 bottomIndexes.append(index);
229 }
230 }
231
232 // Z Top Half Circle
233 for (int i = 0; i < topIndexes.count(); ++i) {
234 const auto refPoint1 = circlePoints[topIndexes[i]];
235 int index2 = i + 1;
236 if (index2 == topIndexes.count())
237 break;
238 const auto refPoint2 = circlePoints[topIndexes[index2]];
239 const auto vertex1 = QVector3D(refPoint1.y() + halfHeight, refPoint1.x(), 0.0f);
240 const auto vertex2 = QVector3D(refPoint2.y() + halfHeight, refPoint2.x(), 0.0f);
241 const auto normal = QVector3D(0, 0, 1);
242 builder.addLine(vertex1, vertex2, normal);
243 }
244
245 // Z Bottom Half Circle
246 for (int i = 0; i < bottomIndexes.count(); ++i) {
247 const auto refPoint1 = circlePoints[bottomIndexes[i]];
248 int index2 = i + 1;
249 if (index2 == bottomIndexes.count())
250 break;
251 const auto refPoint2 = circlePoints[bottomIndexes[index2]];
252 const auto vertex1 = QVector3D(refPoint1.y() - halfHeight, refPoint1.x(), 0.0f);
253 const auto vertex2 = QVector3D(refPoint2.y() - halfHeight, refPoint2.x(), 0.0f);
254 const auto normal = QVector3D(0, 0, 1);
255 builder.addLine(vertex1, vertex2, normal);
256 }
257
258 // X Top Half Circle
259 for (int i = 0; i < topIndexes.count(); ++i) {
260 const auto refPoint1 = circlePoints[topIndexes[i]];
261 int index2 = i + 1;
262 if (index2 == topIndexes.count())
263 break;
264 const auto refPoint2 = circlePoints[topIndexes[index2]];
265 const auto vertex1 = QVector3D(refPoint1.y() + halfHeight, 0.0f, refPoint1.x());
266 const auto vertex2 = QVector3D(refPoint2.y() + halfHeight, 0.0f, refPoint2.x());
267 const auto normal = QVector3D(0, 1, 0);
268 builder.addLine(vertex1, vertex2, normal);
269 }
270
271 // X Bottom Half Circle
272 for (int i = 0; i < bottomIndexes.count(); ++i) {
273 const auto refPoint1 = circlePoints[bottomIndexes[i]];
274 int index2 = i + 1;
275 if (index2 == bottomIndexes.count())
276 break;
277 const auto refPoint2 = circlePoints[bottomIndexes[index2]];
278 const auto vertex1 = QVector3D(refPoint1.y() - halfHeight, 0.0f, refPoint1.x());
279 const auto vertex2 = QVector3D(refPoint2.y() - halfHeight, 0.0f, refPoint2.x());
280 const auto normal = QVector3D(0, 1, 0);
281 builder.addLine(vertex1, vertex2, normal);
282 }
283
284 capsuleGeometry->setVertexData(builder.generateVertexArray());
285 return capsuleGeometry;
286}
287
289{
290 auto planeGeometry = new QQuick3DGeometry();
291 planeGeometry->clear();
292 planeGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
293 QQuick3DGeometry::Attribute::ComponentType::F32Type);
294 planeGeometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 16,
295 QQuick3DGeometry::Attribute::ComponentType::F32Type);
296 planeGeometry->setStride(32);
297 planeGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
298
299 // TODO: Some sort of debug scale? Or a level-of-detail grid thing? => QtQuick3DHelpers
300 float s = 50;
301 float h = 5; // Set to avoid flat bounding box
302 planeGeometry->setBounds({ -s, -s, -h }, { s, s, h });
303 QCollisionDebugMeshBuilder builder;
304
305 builder.addLine({ -s, -s, 0 }, { s, -s, 0 });
306 builder.addLine({ -s, -s, 0 }, { 0, 0, 0 });
307
308 builder.addLine({ s, -s, 0 }, { s, s, 0 });
309 builder.addLine({ s, -s, 0 }, { 0, 0, 0 });
310
311 builder.addLine({ s, s, 0 }, { -s, s, 0 });
312 builder.addLine({ s, s, 0 }, { 0, 0, 0 });
313
314 builder.addLine({ -s, s, 0 }, { -s, -s, 0 });
315 builder.addLine({ -s, s, 0 }, { 0, 0, 0 });
316
317 planeGeometry->setVertexData(builder.generateVertexArray());
318 return planeGeometry;
319}
320
321QQuick3DGeometry *QDebugDrawHelper::generateHeightFieldGeometry(physx::PxHeightField *heightField,
322 float heightScale, float rowScale,
323 float columnScale)
324{
325 if (!heightField || heightField->getNbRows() < 2 || heightField->getNbColumns() < 2)
326 return nullptr;
327
328 auto geometry = new QQuick3DGeometry();
329 geometry->clear();
330 geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
331 QQuick3DGeometry::Attribute::ComponentType::F32Type);
332 geometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 16,
333 QQuick3DGeometry::Attribute::ComponentType::F32Type);
334 geometry->setStride(32);
335 geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
336
337 QCollisionDebugMeshBuilder builder;
338
339 const int numRows = heightField->getNbRows();
340 const int numCols = heightField->getNbColumns();
341
342 const float sizeX = rowScale * (numRows - 1);
343 const float sizeZ = columnScale * (numCols - 1);
344
345 const float heightF = heightScale;
346
347 float minHeight = 0.f;
348 float maxHeight = 0.f;
349
350 auto sample = [&](int row, int col) -> QVector3D {
351 const float height = heightField->getSample(row, col).height * heightF;
352 maxHeight = qMax(maxHeight, height);
353 minHeight = qMin(minHeight, height);
354 return QVector3D(row * rowScale, height, col * columnScale);
355 };
356
357 for (int row = 0; row < numRows; row++) {
358 for (int col = 0; col < numCols; col++) {
359 if (row < numRows - 1)
360 builder.addLine(sample(row, col), sample(row + 1, col));
361 if (col < numCols - 1)
362 builder.addLine(sample(row, col), sample(row, col + 1));
363 }
364 }
365
366 geometry->setBounds(QVector3D(0, minHeight, 0), QVector3D(sizeX, maxHeight, sizeZ));
367 geometry->setVertexData(builder.generateVertexArray());
368 return geometry;
369}
370
371QQuick3DGeometry *QDebugDrawHelper::generateConvexMeshGeometry(physx::PxConvexMesh *convexMesh)
372{
373 if (!convexMesh)
374 return nullptr;
375
376 auto geometry = new QQuick3DGeometry();
377 geometry->clear();
378 geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
379 QQuick3DGeometry::Attribute::ComponentType::F32Type);
380 geometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 16,
381 QQuick3DGeometry::Attribute::ComponentType::F32Type);
382 geometry->setStride(32);
383 geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
384
385 QCollisionDebugMeshBuilder builder;
386
387 const physx::PxU32 nbPolys = convexMesh->getNbPolygons();
388 const physx::PxU8 *polygons = convexMesh->getIndexBuffer();
389 const physx::PxVec3 *verts = convexMesh->getVertices();
390 const physx::PxU32 nbVerts = convexMesh->getNbVertices();
391
392 physx::PxHullPolygon data;
393 for (physx::PxU32 i = 0; i < nbPolys; i++) {
394 convexMesh->getPolygonData(i, data);
395
396 Q_ASSERT(data.mNbVerts > 2);
397 const physx::PxU32 nbTris = physx::PxU32(data.mNbVerts - 2);
398 const physx::PxU8 vref0 = polygons[data.mIndexBase + 0];
399 Q_ASSERT(vref0 < nbVerts);
400
401 for (physx::PxU32 j = 0; j < nbTris; j++) {
402 const physx::PxU32 vref1 = polygons[data.mIndexBase + 0 + j + 1];
403 const physx::PxU32 vref2 = polygons[data.mIndexBase + 0 + j + 2];
404 Q_ASSERT(vref1 < nbVerts);
405 Q_ASSERT(vref2 < nbVerts);
406
407 const QVector3D p0 = QPhysicsUtils::toQtType(verts[vref0]);
408 const QVector3D p1 = QPhysicsUtils::toQtType(verts[vref1]);
409 const QVector3D p2 = QPhysicsUtils::toQtType(verts[vref2]);
410
411 builder.addLine(p0, p1);
412 builder.addLine(p1, p2);
413 builder.addLine(p2, p0);
414 }
415 }
416
417 auto bounds = convexMesh->getLocalBounds();
418
419 geometry->setBounds(QPhysicsUtils::toQtType(bounds.minimum),
420 QPhysicsUtils::toQtType(bounds.maximum));
421 geometry->setVertexData(builder.generateVertexArray());
422 return geometry;
423}
424
425QQuick3DGeometry *
427{
428 if (!triangleMesh)
429 return nullptr;
430
431 auto geometry = new QQuick3DGeometry();
432 geometry->clear();
433 geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
434 QQuick3DGeometry::Attribute::ComponentType::F32Type);
435 geometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, 16,
436 QQuick3DGeometry::Attribute::ComponentType::F32Type);
437 geometry->setStride(32);
438 geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
439
440 QCollisionDebugMeshBuilder builder;
441
442 const physx::PxU32 triangleCount = triangleMesh->getNbTriangles();
443 const physx::PxU32 has16BitIndices =
444 triangleMesh->getTriangleMeshFlags() & physx::PxTriangleMeshFlag::e16_BIT_INDICES;
445 const void *indexBuffer = triangleMesh->getTriangles();
446 const physx::PxVec3 *vertexBuffer = triangleMesh->getVertices();
447 const physx::PxU32 *intIndices = reinterpret_cast<const physx::PxU32 *>(indexBuffer);
448 const physx::PxU16 *shortIndices = reinterpret_cast<const physx::PxU16 *>(indexBuffer);
449 for (physx::PxU32 i = 0; i < triangleCount; ++i) {
450 physx::PxVec3 triVert[3];
451
452 if (has16BitIndices) {
453 triVert[0] = vertexBuffer[*shortIndices++];
454 triVert[1] = vertexBuffer[*shortIndices++];
455 triVert[2] = vertexBuffer[*shortIndices++];
456 } else {
457 triVert[0] = vertexBuffer[*intIndices++];
458 triVert[1] = vertexBuffer[*intIndices++];
459 triVert[2] = vertexBuffer[*intIndices++];
460 }
461
462 const QVector3D p0 = QPhysicsUtils::toQtType(triVert[0]);
463 const QVector3D p1 = QPhysicsUtils::toQtType(triVert[1]);
464 const QVector3D p2 = QPhysicsUtils::toQtType(triVert[2]);
465
466 builder.addLine(p0, p1);
467 builder.addLine(p1, p2);
468 builder.addLine(p2, p0);
469 }
470
471 auto bounds = triangleMesh->getLocalBounds();
472
473 geometry->setBounds(QPhysicsUtils::toQtType(bounds.minimum),
474 QPhysicsUtils::toQtType(bounds.maximum));
475 geometry->setVertexData(builder.generateVertexArray());
476 return geometry;
477}
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:172
QQuick3DGeometry * generateConvexMeshGeometry(physx::PxConvexMesh *convexMesh)
QQuick3DGeometry * generateSphereGeometry(const float radius)
QQuick3DGeometry * generateHeightFieldGeometry(physx::PxHeightField *heightField, float heightScale, float rowScale, float columnScale)
QQuick3DGeometry * generatePlaneGeometry()
QQuick3DGeometry * generateBoxGeometry(const QVector3D &halfExtents)
QQuick3DGeometry * generateCapsuleGeometry(const float radius, const float halfHeight)
QQuick3DGeometry * generateTriangleMeshGeometry(physx::PxTriangleMesh *triangleMesh)