15#include <QtQuick3D/QQuick3DGeometry>
16#include <extensions/PxExtensionsAPI.h>
24#include "foundation/PxVec3.h"
26#include "cooking/PxCooking.h"
27#include "extensions/PxDefaultStreams.h"
28#include "geometry/PxHeightField.h"
29#include "geometry/PxHeightFieldDesc.h"
44 void ref() { ++refCount; }
45 int deref() {
return --refCount; }
57 QQuickImage *m_image =
nullptr;
58 std::vector<physx::PxHeightFieldSample> m_samples;
59 physx::PxHeightField *m_heightField =
nullptr;
69 const QObject *contextObject);
74 static QHash<QString, QQuick3DPhysicsHeightField *> heightFieldHash;
75 static QHash<QQuickImage *, QQuick3DPhysicsHeightField *> heightFieldImageHash;
79QHash<QQuickImage *, QQuick3DPhysicsHeightField *>
85 const QQmlContext *context = qmlContext(contextObject);
87 const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
88 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
90 auto *heightField = heightFieldHash.value(qmlSource);
93 heightFieldHash[qmlSource] = heightField;
101 auto *heightField = heightFieldImageHash.value(source);
104 heightFieldImageHash[source] = heightField;
112 if (heightField !=
nullptr && heightField
->deref() == 0) {
113 qCDebug(lcQuick3dPhysics()) <<
"deleting height field" << heightField;
114 erase_if(heightFieldHash,
115 [heightField](std::pair<
const QString &, QQuick3DPhysicsHeightField *&> h) {
116 return h.second == heightField;
118 erase_if(heightFieldImageHash,
119 [heightField](std::pair<QQuickImage *, QQuick3DPhysicsHeightField *&> h) {
120 return h.second == heightField;
135 if (Q_UNLIKELY(heightMap.isNull())) {
142 m_rows = heightMap.height();
143 m_columns = heightMap.width();
145 const quint64 sampleCount = quint64(m_rows) * quint64(m_columns);
146 if (sampleCount > std::numeric_limits<size_t>::max()) {
147 qWarning() <<
"QQuick3DPhysicsHeightField: height map" << m_columns <<
"x" << m_rows
148 <<
"is too large to allocate.";
155 const size_t numRows = size_t(m_rows);
156 m_samples.resize(size_t(sampleCount));
157 for (
int i = 0; i < m_columns; i++)
158 for (
int j = 0; j < m_rows; j++) {
159 float f = heightMap.pixelColor(i, j).valueF() - 0.5;
161 m_samples[size_t(i) * numRows + size_t(j)] = { qint16(0xffff * f), 0, 0 };
168 return m_heightField;
170 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
171 if (thePhysics ==
nullptr)
175 if (m_image ==
nullptr && m_sourcePath.isEmpty())
179 const bool readFromFile = m_image ==
nullptr;
186 m_heightField = QCacheUtils::readCachedHeightField(m_sourcePath, *thePhysics);
187 if (m_heightField !=
nullptr) {
188 m_rows = m_heightField->getNbRows();
189 m_columns = m_heightField->getNbColumns();
190 return m_heightField;
194 m_heightField = QCacheUtils::readCookedHeightField(m_sourcePath, *thePhysics);
195 if (m_heightField !=
nullptr) {
196 m_rows = m_heightField->getNbRows();
197 m_columns = m_heightField->getNbColumns();
198 return m_heightField;
202 writeSamples(QImage(m_sourcePath));
204 writeSamples(m_image->image());
207 int numRows = m_rows;
208 int numCols = m_columns;
209 const auto *samples = m_samples.data();
211 physx::PxHeightFieldDesc hfDesc;
212 hfDesc.format = physx::PxHeightFieldFormat::eS16_TM;
213 hfDesc.nbColumns = numRows;
214 hfDesc.nbRows = numCols;
215 hfDesc.samples.data = samples;
216 hfDesc.samples.stride =
sizeof(physx::PxHeightFieldSample);
218 physx::PxDefaultMemoryOutputStream buf;
220 const auto *cookingParams = QPhysicsWorld::getCookingParams();
221 if (numRows && numCols && cookingParams && ::PxCookHeightField(hfDesc, buf)) {
222 auto size = buf.getSize();
223 auto *data = buf.getData();
224 physx::PxDefaultMemoryInputData input(data, size);
225 m_heightField = thePhysics->createHeightField(input);
226 qCDebug(lcQuick3dPhysics) <<
"created height field" << m_heightField << numCols << numRows
228 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
232 qCWarning(lcQuick3dPhysics) <<
"Could not create height field from"
233 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
236 return m_heightField;
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
275
276
277
278
279
280
283
284
285
286
287
288
289
290
291
292
293
296
297
298
299
300
301
302
303
304
305
306
307
309QHeightFieldShape::QHeightFieldShape() =
default;
311QHeightFieldShape::~QHeightFieldShape()
313 delete m_heightFieldGeometry;
315 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
318physx::PxGeometry *QHeightFieldShape::getPhysXGeometry()
320 if (m_dirtyPhysx || m_scaleDirty || !m_heightFieldGeometry) {
321 updatePhysXGeometry();
323 return m_heightFieldGeometry;
326void QHeightFieldShape::updatePhysXGeometry()
328 delete m_heightFieldGeometry;
329 m_heightFieldGeometry =
nullptr;
333 auto *hf = m_heightField->heightField();
334 float rows = m_heightField->rows();
335 float cols = m_heightField->columns();
337 if (hf && cols > 1 && rows > 1) {
338 QVector3D scaledExtents = m_extents * sceneScale();
339 const float heightScale = scaledExtents.y() / 0x10000;
340 const float rowScale = scaledExtents.x() / (cols - 1);
341 const float columnScale = scaledExtents.z() / (rows - 1);
345 if (!qIsFinite(heightScale) || !qIsFinite(rowScale) || !qIsFinite(columnScale)
346 || rowScale < 1e-8f || columnScale < 1e-8f || heightScale < (0.0001f / 65535.0f)) {
347 qWarning() <<
"HeightFieldShape: extents" << m_extents
348 <<
"produce an invalid scale, ignoring.";
349 m_dirtyPhysx =
false;
352 m_heightFieldGeometry =
new physx::PxHeightFieldGeometry(hf, physx::PxMeshGeometryFlags(),
353 heightScale, rowScale,
355 m_hfOffset = { -scaledExtents.x() / 2, 0, -scaledExtents.z() / 2 };
357 qCDebug(lcQuick3dPhysics) <<
"created height field geom" << m_heightFieldGeometry <<
"scale"
358 << scaledExtents << m_heightField->columns()
359 << m_heightField->rows();
361 m_dirtyPhysx =
false;
364void QHeightFieldShape::updateExtents()
366 if (!m_heightField || m_extentsSetExplicitly)
368 int numRows = m_heightField->rows();
369 int numCols = m_heightField->columns();
370 auto prevExt = m_extents;
371 if (numRows == numCols) {
372 m_extents = { 100, 100, 100 };
373 }
else if (numRows < numCols) {
374 float f =
float(numRows) /
float(numCols);
375 m_extents = { 100.f, 100.f, 100.f * f };
377 float f =
float(numCols) /
float(numRows);
378 m_extents = { 100.f * f, 100.f, 100.f };
380 if (m_extents != prevExt) {
381 emit extentsChanged();
385const QUrl &QHeightFieldShape::source()
const
387 return m_heightMapSource;
390void QHeightFieldShape::setSource(
const QUrl &newSource)
392 if (m_heightMapSource == newSource)
394 m_heightMapSource = newSource;
398 if (m_image ==
nullptr) {
399 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
400 m_heightField =
nullptr;
404 if (m_image ==
nullptr && !newSource.isEmpty()) {
405 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
406 emit needsRebuild(
this);
410 emit sourceChanged();
413QQuickImage *QHeightFieldShape::image()
const
418void QHeightFieldShape::setImage(QQuickImage *newImage)
420 if (m_image == newImage)
424 m_image->disconnect(
this);
428 if (m_image !=
nullptr) {
429 connect(m_image, &QObject::destroyed,
this, &QHeightFieldShape::imageDestroyed);
430 connect(m_image, &QQuickImage::paintedGeometryChanged,
this,
431 &QHeightFieldShape::imageGeometryChanged);
435 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
436 m_heightField =
nullptr;
438 if (m_image !=
nullptr)
439 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
440 else if (!m_heightMapSource.isEmpty())
441 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
444 emit needsRebuild(
this);
448void QHeightFieldShape::imageDestroyed(QObject *image)
450 Q_ASSERT(m_image == image);
455void QHeightFieldShape::imageGeometryChanged()
459 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
460 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
462 emit needsRebuild(
this);
465const QVector3D &QHeightFieldShape::extents()
const
470void QHeightFieldShape::setExtents(
const QVector3D &newExtents)
472 m_extentsSetExplicitly =
true;
473 if (m_extents == newExtents)
475 m_extents = newExtents;
479 emit needsRebuild(
this);
480 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.