15#include <QtQuick3D/QQuick3DGeometry>
16#include <extensions/PxExtensionsAPI.h>
24#include "foundation/PxVec3.h"
26#include "extensions/PxDefaultStreams.h"
27#include "geometry/PxHeightField.h"
28#include "geometry/PxHeightFieldDesc.h"
43 void ref() { ++refCount; }
44 int deref() {
return --refCount; }
56 QQuickImage *m_image =
nullptr;
57 std::vector<physx::PxHeightFieldSample> m_samples;
58 physx::PxHeightField *m_heightField =
nullptr;
68 const QObject *contextObject);
73 static QHash<QString, QQuick3DPhysicsHeightField *> heightFieldHash;
74 static QHash<QQuickImage *, QQuick3DPhysicsHeightField *> heightFieldImageHash;
78QHash<QQuickImage *, QQuick3DPhysicsHeightField *>
84 const QQmlContext *context = qmlContext(contextObject);
86 const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
87 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
89 auto *heightField = heightFieldHash.value(qmlSource);
92 heightFieldHash[qmlSource] = heightField;
100 auto *heightField = heightFieldImageHash.value(source);
103 heightFieldImageHash[source] = heightField;
111 if (heightField !=
nullptr && heightField
->deref() == 0) {
112 qCDebug(lcQuick3dPhysics()) <<
"deleting height field" << heightField;
113 erase_if(heightFieldHash,
114 [heightField](std::pair<
const QString &, QQuick3DPhysicsHeightField *&> h) {
115 return h.second == heightField;
117 erase_if(heightFieldImageHash,
118 [heightField](std::pair<QQuickImage *, QQuick3DPhysicsHeightField *&> h) {
119 return h.second == heightField;
134 if (Q_UNLIKELY(heightMap.isNull())) {
141 m_rows = heightMap.height();
142 m_columns = heightMap.width();
144 const quint64 sampleCount = quint64(m_rows) * quint64(m_columns);
145 if (sampleCount > std::numeric_limits<size_t>::max()) {
146 qWarning() <<
"QQuick3DPhysicsHeightField: height map" << m_columns <<
"x" << m_rows
147 <<
"is too large to allocate.";
154 const size_t numRows = size_t(m_rows);
155 m_samples.resize(size_t(sampleCount));
156 for (
int i = 0; i < m_columns; i++)
157 for (
int j = 0; j < m_rows; j++) {
158 float f = heightMap.pixelColor(i, j).valueF() - 0.5;
160 m_samples[size_t(i) * numRows + size_t(j)] = { qint16(0xffff * f), 0, 0 };
167 return m_heightField;
169 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
170 if (thePhysics ==
nullptr)
174 if (m_image ==
nullptr && m_sourcePath.isEmpty())
178 const bool readFromFile = m_image ==
nullptr;
185 m_heightField = QCacheUtils::readCachedHeightField(m_sourcePath, *thePhysics);
186 if (m_heightField !=
nullptr) {
187 m_rows = m_heightField->getNbRows();
188 m_columns = m_heightField->getNbColumns();
189 return m_heightField;
193 m_heightField = QCacheUtils::readCookedHeightField(m_sourcePath, *thePhysics);
194 if (m_heightField !=
nullptr) {
195 m_rows = m_heightField->getNbRows();
196 m_columns = m_heightField->getNbColumns();
197 return m_heightField;
201 writeSamples(QImage(m_sourcePath));
203 writeSamples(m_image->image());
206 int numRows = m_rows;
207 int numCols = m_columns;
208 const auto *samples = m_samples.data();
210 physx::PxHeightFieldDesc hfDesc;
211 hfDesc.format = physx::PxHeightFieldFormat::eS16_TM;
212 hfDesc.nbColumns = numRows;
213 hfDesc.nbRows = numCols;
214 hfDesc.samples.data = samples;
215 hfDesc.samples.stride =
sizeof(physx::PxHeightFieldSample);
217 physx::PxDefaultMemoryOutputStream buf;
219 const auto cooking = QPhysicsWorld::getCooking();
220 if (numRows && numCols && cooking && cooking->cookHeightField(hfDesc, buf)) {
221 auto size = buf.getSize();
222 auto *data = buf.getData();
223 physx::PxDefaultMemoryInputData input(data, size);
224 m_heightField = thePhysics->createHeightField(input);
225 qCDebug(lcQuick3dPhysics) <<
"created height field" << m_heightField << numCols << numRows
227 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
231 qCWarning(lcQuick3dPhysics) <<
"Could not create height field from"
232 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
235 return m_heightField;
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
274
275
276
277
278
279
282
283
284
285
286
287
288
289
290
291
292
295
296
297
298
299
300
301
302
303
304
305
306
308QHeightFieldShape::QHeightFieldShape() =
default;
310QHeightFieldShape::~QHeightFieldShape()
312 delete m_heightFieldGeometry;
314 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
317physx::PxGeometry *QHeightFieldShape::getPhysXGeometry()
319 if (m_dirtyPhysx || m_scaleDirty || !m_heightFieldGeometry) {
320 updatePhysXGeometry();
322 return m_heightFieldGeometry;
325void QHeightFieldShape::updatePhysXGeometry()
327 delete m_heightFieldGeometry;
328 m_heightFieldGeometry =
nullptr;
332 auto *hf = m_heightField->heightField();
333 float rows = m_heightField->rows();
334 float cols = m_heightField->columns();
336 if (hf && cols > 1 && rows > 1) {
337 QVector3D scaledExtents = m_extents * sceneScale();
338 m_heightFieldGeometry =
new physx::PxHeightFieldGeometry(
339 hf, physx::PxMeshGeometryFlags(), scaledExtents.y() / 0x10000,
340 scaledExtents.x() / (cols - 1), scaledExtents.z() / (rows - 1));
341 m_hfOffset = { -scaledExtents.x() / 2, 0, -scaledExtents.z() / 2 };
343 qCDebug(lcQuick3dPhysics) <<
"created height field geom" << m_heightFieldGeometry <<
"scale"
344 << scaledExtents << m_heightField->columns()
345 << m_heightField->rows();
347 m_dirtyPhysx =
false;
350void QHeightFieldShape::updateExtents()
352 if (!m_heightField || m_extentsSetExplicitly)
354 int numRows = m_heightField->rows();
355 int numCols = m_heightField->columns();
356 auto prevExt = m_extents;
357 if (numRows == numCols) {
358 m_extents = { 100, 100, 100 };
359 }
else if (numRows < numCols) {
360 float f =
float(numRows) /
float(numCols);
361 m_extents = { 100.f, 100.f, 100.f * f };
363 float f =
float(numCols) /
float(numRows);
364 m_extents = { 100.f * f, 100.f, 100.f };
366 if (m_extents != prevExt) {
367 emit extentsChanged();
371const QUrl &QHeightFieldShape::source()
const
373 return m_heightMapSource;
376void QHeightFieldShape::setSource(
const QUrl &newSource)
378 if (m_heightMapSource == newSource)
380 m_heightMapSource = newSource;
384 if (m_image ==
nullptr) {
385 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
386 m_heightField =
nullptr;
390 if (m_image ==
nullptr && !newSource.isEmpty()) {
391 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
392 emit needsRebuild(
this);
396 emit sourceChanged();
399QQuickImage *QHeightFieldShape::image()
const
404void QHeightFieldShape::setImage(QQuickImage *newImage)
406 if (m_image == newImage)
410 m_image->disconnect(
this);
414 if (m_image !=
nullptr) {
415 connect(m_image, &QObject::destroyed,
this, &QHeightFieldShape::imageDestroyed);
416 connect(m_image, &QQuickImage::paintedGeometryChanged,
this,
417 &QHeightFieldShape::imageGeometryChanged);
421 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
422 m_heightField =
nullptr;
424 if (m_image !=
nullptr)
425 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
426 else if (!m_heightMapSource.isEmpty())
427 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
430 emit needsRebuild(
this);
434void QHeightFieldShape::imageDestroyed(QObject *image)
436 Q_ASSERT(m_image == image);
441void QHeightFieldShape::imageGeometryChanged()
445 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
446 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
448 emit needsRebuild(
this);
451const QVector3D &QHeightFieldShape::extents()
const
456void QHeightFieldShape::setExtents(
const QVector3D &newExtents)
458 m_extentsSetExplicitly =
true;
459 if (m_extents == newExtents)
461 m_extents = newExtents;
465 emit needsRebuild(
this);
466 emit extentsChanged();
static QQuick3DPhysicsHeightField * getHeightField(QQuickImage *source)
static QQuick3DPhysicsHeightField * getHeightField(const QUrl &source, const QObject *contextObject)
static void releaseHeightField(QQuick3DPhysicsHeightField *heightField)
QQuick3DPhysicsHeightField(const QString &qmlSource)
QQuick3DPhysicsHeightField(QQuickImage *image)
void writeSamples(const QImage &heightMap)
physx::PxHeightField * heightField()
void writeCachedHeightField(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
Combined button and popup list for selecting options.