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 const float heightScale = scaledExtents.y() / 0x10000;
339 const float rowScale = scaledExtents.x() / (cols - 1);
340 const float columnScale = scaledExtents.z() / (rows - 1);
344 if (!qIsFinite(heightScale) || !qIsFinite(rowScale) || !qIsFinite(columnScale)
345 || rowScale < 1e-8f || columnScale < 1e-8f || heightScale < (0.0001f / 65535.0f)) {
346 qWarning() <<
"HeightFieldShape: extents" << m_extents
347 <<
"produce an invalid scale, ignoring.";
348 m_dirtyPhysx =
false;
351 m_heightFieldGeometry =
new physx::PxHeightFieldGeometry(hf, physx::PxMeshGeometryFlags(),
352 heightScale, rowScale,
354 m_hfOffset = { -scaledExtents.x() / 2, 0, -scaledExtents.z() / 2 };
356 qCDebug(lcQuick3dPhysics) <<
"created height field geom" << m_heightFieldGeometry <<
"scale"
357 << scaledExtents << m_heightField->columns()
358 << m_heightField->rows();
360 m_dirtyPhysx =
false;
363void QHeightFieldShape::updateExtents()
365 if (!m_heightField || m_extentsSetExplicitly)
367 int numRows = m_heightField->rows();
368 int numCols = m_heightField->columns();
369 auto prevExt = m_extents;
370 if (numRows == numCols) {
371 m_extents = { 100, 100, 100 };
372 }
else if (numRows < numCols) {
373 float f =
float(numRows) /
float(numCols);
374 m_extents = { 100.f, 100.f, 100.f * f };
376 float f =
float(numCols) /
float(numRows);
377 m_extents = { 100.f * f, 100.f, 100.f };
379 if (m_extents != prevExt) {
380 emit extentsChanged();
384const QUrl &QHeightFieldShape::source()
const
386 return m_heightMapSource;
389void QHeightFieldShape::setSource(
const QUrl &newSource)
391 if (m_heightMapSource == newSource)
393 m_heightMapSource = newSource;
397 if (m_image ==
nullptr) {
398 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
399 m_heightField =
nullptr;
403 if (m_image ==
nullptr && !newSource.isEmpty()) {
404 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
405 emit needsRebuild(
this);
409 emit sourceChanged();
412QQuickImage *QHeightFieldShape::image()
const
417void QHeightFieldShape::setImage(QQuickImage *newImage)
419 if (m_image == newImage)
423 m_image->disconnect(
this);
427 if (m_image !=
nullptr) {
428 connect(m_image, &QObject::destroyed,
this, &QHeightFieldShape::imageDestroyed);
429 connect(m_image, &QQuickImage::paintedGeometryChanged,
this,
430 &QHeightFieldShape::imageGeometryChanged);
434 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
435 m_heightField =
nullptr;
437 if (m_image !=
nullptr)
438 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
439 else if (!m_heightMapSource.isEmpty())
440 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
443 emit needsRebuild(
this);
447void QHeightFieldShape::imageDestroyed(QObject *image)
449 Q_ASSERT(m_image == image);
454void QHeightFieldShape::imageGeometryChanged()
458 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
459 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
461 emit needsRebuild(
this);
464const QVector3D &QHeightFieldShape::extents()
const
469void QHeightFieldShape::setExtents(
const QVector3D &newExtents)
471 m_extentsSetExplicitly =
true;
472 if (m_extents == newExtents)
474 m_extents = newExtents;
478 emit needsRebuild(
this);
479 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.