Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qheightfieldshape.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
4#include "qcacheutils_p.h"
6
7#include <QFileInfo>
8#include <QImage>
9#include <QQmlContext>
10#include <QQmlFile>
11#include <QtQuick3D/QQuick3DGeometry>
12#include <extensions/PxExtensionsAPI.h>
13
14//########################################################################################
15// NOTE:
16// Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are
17// not supported for non-kinematic PxRigidDynamic instances.
18//########################################################################################
19
20#include "foundation/PxVec3.h"
21//#include "cooking/PxTriangleMeshDesc.h"
22#include "extensions/PxDefaultStreams.h"
23#include "geometry/PxHeightField.h"
24#include "geometry/PxHeightFieldDesc.h"
25
26#include "qphysicsworld_p.h"
27
29
30// TODO: Unify with QQuick3DPhysicsMeshManager??? It's the same basic logic,
31// but we're using images instead of meshes.
32
34{
35public:
36 QQuick3DPhysicsHeightField(const QString &qmlSource);
38
39 void ref() { ++refCount; }
40 int deref() { return --refCount; }
41 physx::PxHeightFieldSample *getSamples();
42 physx::PxHeightField *heightField();
43
44 int rows() const;
45 int columns() const;
46
47private:
48 QString m_sourcePath;
49 physx::PxHeightFieldSample *m_samples = nullptr;
50 physx::PxHeightField *m_heightField = nullptr;
51 int m_rows = 0;
52 int m_columns = 0;
53 int refCount = 0;
54};
55
57{
58public:
60 const QObject *contextObject);
61 static void releaseHeightField(QQuick3DPhysicsHeightField *heightField);
62
63private:
65};
66
67QHash<QString, QQuick3DPhysicsHeightField *> QQuick3DPhysicsHeightFieldManager::heightFieldHash;
68
71{
72 const QQmlContext *context = qmlContext(contextObject);
73
74 const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
75 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
76
77 auto *heightField = heightFieldHash.value(qmlSource);
78 if (!heightField) {
79 heightField = new QQuick3DPhysicsHeightField(qmlSource);
80 heightFieldHash[qmlSource] = heightField;
81 }
82 heightField->ref();
83 return heightField;
84}
85
87{
88 if (heightField->deref() == 0) {
89 qCDebug(lcQuick3dPhysics()) << "deleting height field" << heightField;
90 erase_if(heightFieldHash,
91 [heightField](std::pair<const QString &, QQuick3DPhysicsHeightField *&> h) {
92 return h.second == heightField;
93 });
94 delete heightField;
95 }
96}
97
99 : m_sourcePath(qmlSource)
100{
101}
102
104{
105 free(m_samples);
106}
107
108physx::PxHeightFieldSample *QQuick3DPhysicsHeightField::getSamples()
109{
110 if (!m_samples && !m_sourcePath.isEmpty()) {
111 QImage heightMap(m_sourcePath);
112
113 m_rows = heightMap.height();
114 m_columns = heightMap.width();
115 int numRows = m_rows;
116 int numCols = m_columns;
117
118 auto samples = reinterpret_cast<physx::PxHeightFieldSample *>(
119 malloc(sizeof(physx::PxHeightFieldSample) * (numRows * numCols)));
120 for (int i = 0; i < numCols; i++)
121 for (int j = 0; j < numRows; j++) {
122 float f = heightMap.pixelColor(i, j).valueF() - 0.5;
123 // qDebug() << i << j << f;
124 samples[i * numRows + j] = { qint16(0xffff * f), 0,
125 0 }; //{qint16(i%3*2 + j), 0, 0};
126 }
127 m_samples = samples;
128 }
129 return m_samples;
130}
131
133{
134 if (m_heightField)
135 return m_heightField;
136
137 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
138 if (thePhysics == nullptr)
139 return nullptr;
140
141 m_heightField = QCacheUtils::readCachedHeightField(m_sourcePath, *thePhysics);
142 if (m_heightField != nullptr) {
143 m_rows = m_heightField->getNbRows();
144 m_columns = m_heightField->getNbColumns();
145 return m_heightField;
146 }
147
148 m_heightField = QCacheUtils::readCookedHeightField(m_sourcePath, *thePhysics);
149 if (m_heightField != nullptr) {
150 m_rows = m_heightField->getNbRows();
151 m_columns = m_heightField->getNbColumns();
152 return m_heightField;
153 }
154
155 getSamples();
156 int numRows = m_rows;
157 int numCols = m_columns;
158 auto samples = m_samples;
159
160 physx::PxHeightFieldDesc hfDesc;
161 hfDesc.format = physx::PxHeightFieldFormat::eS16_TM;
162 hfDesc.nbColumns = numRows;
163 hfDesc.nbRows = numCols;
164 hfDesc.samples.data = samples;
165 hfDesc.samples.stride = sizeof(physx::PxHeightFieldSample);
166
167 physx::PxDefaultMemoryOutputStream buf;
168
169 const auto cooking = QPhysicsWorld::getCooking();
170 if (numRows && numCols && cooking && cooking->cookHeightField(hfDesc, buf)) {
171 auto size = buf.getSize();
172 auto *data = buf.getData();
173 physx::PxDefaultMemoryInputData input(data, size);
174 m_heightField = thePhysics->createHeightField(input);
175 qCDebug(lcQuick3dPhysics) << "created height field" << m_heightField << numCols << numRows
176 << "from" << m_sourcePath;
178 } else {
179 qCWarning(lcQuick3dPhysics) << "Could not create height field from" << m_sourcePath;
180 }
181
182 return m_heightField;
183}
184
186{
187 return m_rows;
188}
189
191{
192 return m_columns;
193}
194
239
241{
242 delete m_heightFieldGeometry;
243 if (m_heightField)
245}
246
248{
249 if (m_dirtyPhysx || !m_heightFieldGeometry) {
250 updatePhysXGeometry();
251 }
252 return m_heightFieldGeometry;
253}
254
255void QHeightFieldShape::updatePhysXGeometry()
256{
257 delete m_heightFieldGeometry;
258 m_heightFieldGeometry = nullptr;
259 if (!m_heightField)
260 return;
261
262 auto *hf = m_heightField->heightField();
263 float rows = m_heightField->rows();
264 float cols = m_heightField->columns();
265 updateExtents();
266 if (hf && cols > 1 && rows > 1) {
267 QVector3D scaledExtents = m_extents * sceneScale();
268 m_heightFieldGeometry = new physx::PxHeightFieldGeometry(
269 hf, physx::PxMeshGeometryFlags(), scaledExtents.y() / 0x10000,
270 scaledExtents.x() / (cols - 1), scaledExtents.z() / (rows - 1));
271 m_hfOffset = { -scaledExtents.x() / 2, 0, -scaledExtents.z() / 2 };
272
273 qCDebug(lcQuick3dPhysics) << "created height field geom" << m_heightFieldGeometry << "scale"
274 << scaledExtents << m_heightField->columns()
275 << m_heightField->rows();
276 }
277 m_dirtyPhysx = false;
278}
279
280void QHeightFieldShape::updateExtents()
281{
282 if (!m_heightField || m_extentsSetExplicitly)
283 return;
284 int numRows = m_heightField->rows();
285 int numCols = m_heightField->columns();
286 auto prevExt = m_extents;
287 if (numRows == numCols) {
288 m_extents = { 100, 100, 100 };
289 } else if (numRows < numCols) {
290 float f = float(numRows) / float(numCols);
291 m_extents = { 100.f, 100.f, 100.f * f };
292 } else {
293 float f = float(numCols) / float(numRows);
294 m_extents = { 100.f * f, 100.f, 100.f };
295 }
296 if (m_extents != prevExt) {
298 }
299}
300
301const QUrl &QHeightFieldShape::source() const
302{
303 return m_heightMapSource;
304}
305
306void QHeightFieldShape::setSource(const QUrl &newSource)
307{
308 if (m_heightMapSource == newSource)
309 return;
310 m_heightMapSource = newSource;
311
312 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource, this);
313
314 m_dirtyPhysx = true;
315
316 emit needsRebuild(this);
317 emit sourceChanged();
318}
319
321{
322 return m_extents;
323}
324
326{
327 m_extentsSetExplicitly = true;
328 if (m_extents == newExtents)
329 return;
330 m_extents = newExtents;
331
332 m_dirtyPhysx = true;
333
334 emit needsRebuild(this);
336}
337
void needsRebuild(QObject *)
float valueF() const noexcept
Returns the value color component of this color.
Definition qcolor.cpp:1818
\inmodule QtCore
Definition qhash.h:818
T value(const Key &key) const noexcept
Definition qhash.h:1044
void setExtents(const QVector3D &newExtents)
physx::PxGeometry * getPhysXGeometry() override
QHeightFieldShape()
\qmltype HeightFieldShape \inqmlmodule QtQuick3D.Physics \inherits CollisionShape
\inmodule QtGui
Definition qimage.h:37
int width() const
Returns the width of the image.
int height() const
Returns the height of the image.
QColor pixelColor(int x, int y) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qimage.cpp:2691
\inmodule QtCore
Definition qobject.h:90
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to QFile.
Definition qqmlfile.cpp:643
QVector3D sceneScale
static QQuick3DPhysicsHeightField * getHeightField(const QUrl &source, const QObject *contextObject)
static void releaseHeightField(QQuick3DPhysicsHeightField *heightField)
QQuick3DPhysicsHeightField(const QString &qmlSource)
physx::PxHeightFieldSample * getSamples()
physx::PxHeightField * heightField()
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
\inmodule QtCore
Definition qurl.h:94
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
void writeCachedHeightField(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
physx::PxHeightField * readCookedHeightField(const QString &filePath, physx::PxPhysics &physics)
physx::PxHeightField * readCachedHeightField(const QString &filePath, physx::PxPhysics &physics)
Combined button and popup list for selecting options.
static void * context
qsizetype erase_if(QByteArray &ba, Predicate pred)
Definition qbytearray.h:701
#define qCWarning(category,...)
#define qCDebug(category,...)
GLsizei samples
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLfloat GLfloat f
GLenum GLuint GLenum GLsizei const GLchar * buf
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat GLfloat GLfloat h
GLsizei GLsizei GLchar * source
GLenum GLenum GLenum input
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
static QUrl resolvedUrl(const QUrl &url, const QQmlRefPointer< QQmlContextData > &context)
#define emit
short qint16
Definition qtypes.h:42