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"
46 void ref() { ++refCount; }
47 int deref() {
return --refCount; }
55 void forgetStalePhysXHeightField();
61 QQuickImage *m_image =
nullptr;
62 std::vector<physx::PxHeightFieldSample> m_samples;
63 physx::PxHeightField *m_heightField =
nullptr;
64 quint32 m_physicsGeneration = 0;
74 const QObject *contextObject);
79 static QHash<QString, QQuick3DPhysicsHeightField *> heightFieldHash;
80 static QHash<QQuickImage *, QQuick3DPhysicsHeightField *> heightFieldImageHash;
84QHash<QQuickImage *, QQuick3DPhysicsHeightField *>
90 const QQmlContext *context = qmlContext(contextObject);
92 const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
93 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
95 auto *heightField = heightFieldHash.value(qmlSource);
98 heightFieldHash[qmlSource] = heightField;
106 auto *heightField = heightFieldImageHash.value(source);
109 heightFieldImageHash[source] = heightField;
117 if (heightField !=
nullptr && heightField
->deref() == 0) {
118 qCDebug(lcQuick3dPhysics()) <<
"deleting height field" << heightField;
119 erase_if(heightFieldHash,
120 [heightField](std::pair<
const QString &, QQuick3DPhysicsHeightField *&> h) {
121 return h.second == heightField;
123 erase_if(heightFieldImageHash,
124 [heightField](std::pair<QQuickImage *, QQuick3DPhysicsHeightField *&> h) {
125 return h.second == heightField;
140 forgetStalePhysXHeightField();
142 m_heightField->release();
151 if (m_physicsGeneration == s_physx.physicsGeneration)
153 m_heightField =
nullptr;
154 m_physicsGeneration = s_physx.physicsGeneration;
159 if (Q_UNLIKELY(heightMap.isNull())) {
166 m_rows = heightMap.height();
167 m_columns = heightMap.width();
169 const quint64 sampleCount = quint64(m_rows) * quint64(m_columns);
170 if (sampleCount > std::numeric_limits<size_t>::max()) {
171 qWarning() <<
"QQuick3DPhysicsHeightField: height map" << m_columns <<
"x" << m_rows
172 <<
"is too large to allocate.";
179 const size_t numRows = size_t(m_rows);
180 m_samples.resize(size_t(sampleCount));
181 for (
int i = 0; i < m_columns; i++)
182 for (
int j = 0; j < m_rows; j++) {
183 float f = heightMap.pixelColor(i, j).valueF() - 0.5;
185 m_samples[size_t(i) * numRows + size_t(j)] = { qint16(0xffff * f), 0, 0 };
191 forgetStalePhysXHeightField();
193 return m_heightField;
195 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
196 if (thePhysics ==
nullptr)
200 if (m_image ==
nullptr && m_sourcePath.isEmpty())
204 const bool readFromFile = m_image ==
nullptr;
211 m_heightField = QCacheUtils::readCachedHeightField(m_sourcePath, *thePhysics);
212 if (m_heightField !=
nullptr) {
213 m_rows = m_heightField->getNbRows();
214 m_columns = m_heightField->getNbColumns();
215 return m_heightField;
219 m_heightField = QCacheUtils::readCookedHeightField(m_sourcePath, *thePhysics);
220 if (m_heightField !=
nullptr) {
221 m_rows = m_heightField->getNbRows();
222 m_columns = m_heightField->getNbColumns();
223 return m_heightField;
227 writeSamples(QImage(m_sourcePath));
229 writeSamples(m_image->image());
232 int numRows = m_rows;
233 int numCols = m_columns;
234 const auto *samples = m_samples.data();
236 physx::PxHeightFieldDesc hfDesc;
237 hfDesc.format = physx::PxHeightFieldFormat::eS16_TM;
238 hfDesc.nbColumns = numRows;
239 hfDesc.nbRows = numCols;
240 hfDesc.samples.data = samples;
241 hfDesc.samples.stride =
sizeof(physx::PxHeightFieldSample);
243 physx::PxDefaultMemoryOutputStream buf;
245 const auto *cookingParams = QPhysicsWorld::getCookingParams();
246 if (numRows && numCols && cookingParams && ::PxCookHeightField(hfDesc, buf)) {
247 auto size = buf.getSize();
248 auto *data = buf.getData();
249 physx::PxDefaultMemoryInputData input(data, size);
250 m_heightField = thePhysics->createHeightField(input);
251 qCDebug(lcQuick3dPhysics) <<
"created height field" << m_heightField << numCols << numRows
253 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
257 qCWarning(lcQuick3dPhysics) <<
"Could not create height field from"
258 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
261 return m_heightField;
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
300
301
302
303
304
305
308
309
310
311
312
313
314
315
316
317
318
321
322
323
324
325
326
327
328
329
330
331
332
334QHeightFieldShape::QHeightFieldShape() =
default;
336QHeightFieldShape::~QHeightFieldShape()
338 delete m_heightFieldGeometry;
340 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
343physx::PxGeometry *QHeightFieldShape::getPhysXGeometry()
347 const auto &s_physx = StaticPhysXObjects::getReference();
348 if (m_dirtyPhysx || m_scaleDirty || !m_heightFieldGeometry
349 || m_physicsGeneration != s_physx.physicsGeneration) {
350 updatePhysXGeometry();
352 return m_heightFieldGeometry;
355void QHeightFieldShape::updatePhysXGeometry()
357 delete m_heightFieldGeometry;
358 m_heightFieldGeometry =
nullptr;
359 m_physicsGeneration = StaticPhysXObjects::getReference().physicsGeneration;
363 auto *hf = m_heightField->heightField();
364 float rows = m_heightField->rows();
365 float cols = m_heightField->columns();
367 if (hf && cols > 1 && rows > 1) {
368 QVector3D scaledExtents = m_extents * sceneScale();
369 const float heightScale = scaledExtents.y() / 0x10000;
370 const float rowScale = scaledExtents.x() / (cols - 1);
371 const float columnScale = scaledExtents.z() / (rows - 1);
375 if (!qIsFinite(heightScale) || !qIsFinite(rowScale) || !qIsFinite(columnScale)
376 || rowScale < 1e-8f || columnScale < 1e-8f || heightScale < (0.0001f / 65535.0f)) {
377 qWarning() <<
"HeightFieldShape: extents" << m_extents
378 <<
"produce an invalid scale, ignoring.";
379 m_dirtyPhysx =
false;
382 m_heightFieldGeometry =
new physx::PxHeightFieldGeometry(hf, physx::PxMeshGeometryFlags(),
383 heightScale, rowScale,
385 m_hfOffset = { -scaledExtents.x() / 2, 0, -scaledExtents.z() / 2 };
387 qCDebug(lcQuick3dPhysics) <<
"created height field geom" << m_heightFieldGeometry <<
"scale"
388 << scaledExtents << m_heightField->columns()
389 << m_heightField->rows();
391 m_dirtyPhysx =
false;
394void QHeightFieldShape::updateExtents()
396 if (!m_heightField || m_extentsSetExplicitly)
398 int numRows = m_heightField->rows();
399 int numCols = m_heightField->columns();
400 auto prevExt = m_extents;
401 if (numRows == numCols) {
402 m_extents = { 100, 100, 100 };
403 }
else if (numRows < numCols) {
404 float f =
float(numRows) /
float(numCols);
405 m_extents = { 100.f, 100.f, 100.f * f };
407 float f =
float(numCols) /
float(numRows);
408 m_extents = { 100.f * f, 100.f, 100.f };
410 if (m_extents != prevExt) {
411 emit extentsChanged();
415const QUrl &QHeightFieldShape::source()
const
417 return m_heightMapSource;
420void QHeightFieldShape::setSource(
const QUrl &newSource)
422 if (m_heightMapSource == newSource)
424 m_heightMapSource = newSource;
428 if (m_image ==
nullptr) {
429 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
430 m_heightField =
nullptr;
434 if (m_image ==
nullptr && !newSource.isEmpty()) {
435 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
436 emit needsRebuild(
this);
440 emit sourceChanged();
443QQuickImage *QHeightFieldShape::image()
const
448void QHeightFieldShape::setImage(QQuickImage *newImage)
450 if (m_image == newImage)
454 m_image->disconnect(
this);
458 if (m_image !=
nullptr) {
459 connect(m_image, &QObject::destroyed,
this, &QHeightFieldShape::imageDestroyed);
460 connect(m_image, &QQuickImage::paintedGeometryChanged,
this,
461 &QHeightFieldShape::imageGeometryChanged);
465 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
466 m_heightField =
nullptr;
468 if (m_image !=
nullptr)
469 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
470 else if (!m_heightMapSource.isEmpty())
471 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
474 emit needsRebuild(
this);
478void QHeightFieldShape::imageDestroyed(QObject *image)
480 Q_ASSERT(m_image == image);
485void QHeightFieldShape::imageGeometryChanged()
489 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
490 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
492 emit needsRebuild(
this);
495const QVector3D &QHeightFieldShape::extents()
const
500void QHeightFieldShape::setExtents(
const QVector3D &newExtents)
502 m_extentsSetExplicitly =
true;
503 if (m_extents == newExtents)
505 m_extents = newExtents;
509 emit needsRebuild(
this);
510 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()
~QQuick3DPhysicsHeightField()
void writeCachedHeightField(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
#define QT_BEGIN_NAMESPACE
static StaticPhysXObjects & getReference()