12#include <QtQuick3D/QQuick3DGeometry>
13#include <extensions/PxExtensionsAPI.h>
21#include "foundation/PxVec3.h"
23#include "extensions/PxDefaultStreams.h"
24#include "geometry/PxHeightField.h"
25#include "geometry/PxHeightFieldDesc.h"
41 void ref() { ++refCount; }
42 int deref() {
return --refCount; }
54 QQuickImage *m_image =
nullptr;
55 physx::PxHeightFieldSample *m_samples =
nullptr;
56 physx::PxHeightField *m_heightField =
nullptr;
66 const QObject *contextObject);
71 static QHash<QString, QQuick3DPhysicsHeightField *> heightFieldHash;
72 static QHash<QQuickImage *, QQuick3DPhysicsHeightField *> heightFieldImageHash;
76QHash<QQuickImage *, QQuick3DPhysicsHeightField *>
82 const QQmlContext *context = qmlContext(contextObject);
84 const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
85 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
87 auto *heightField = heightFieldHash.value(qmlSource);
90 heightFieldHash[qmlSource] = heightField;
98 auto *heightField = heightFieldImageHash.value(source);
101 heightFieldImageHash[source] = heightField;
109 if (heightField !=
nullptr && heightField
->deref() == 0) {
110 qCDebug(lcQuick3dPhysics()) <<
"deleting height field" << heightField;
111 erase_if(heightFieldHash,
112 [heightField](std::pair<
const QString &, QQuick3DPhysicsHeightField *&> h) {
113 return h.second == heightField;
115 erase_if(heightFieldImageHash,
116 [heightField](std::pair<QQuickImage *, QQuick3DPhysicsHeightField *&> h) {
117 return h.second == heightField;
137 if (Q_UNLIKELY(heightMap.isNull())) {
145 m_rows = heightMap.height();
146 m_columns = heightMap.width();
147 int numRows = m_rows;
148 int numCols = m_columns;
151 m_samples =
reinterpret_cast<
physx::PxHeightFieldSample *>(
152 malloc(
sizeof(physx::PxHeightFieldSample) * (numRows * numCols)));
153 for (
int i = 0; i < numCols; i++)
154 for (
int j = 0; j < numRows; j++) {
155 float f = heightMap.pixelColor(i, j).valueF() - 0.5;
157 m_samples[i * numRows + j] = { qint16(0xffff * f), 0, 0 };
164 return m_heightField;
166 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
167 if (thePhysics ==
nullptr)
171 if (m_image ==
nullptr && m_sourcePath.isEmpty())
175 const bool readFromFile = m_image ==
nullptr;
182 m_heightField = QCacheUtils::readCachedHeightField(m_sourcePath, *thePhysics);
183 if (m_heightField !=
nullptr) {
184 m_rows = m_heightField->getNbRows();
185 m_columns = m_heightField->getNbColumns();
186 return m_heightField;
190 m_heightField = QCacheUtils::readCookedHeightField(m_sourcePath, *thePhysics);
191 if (m_heightField !=
nullptr) {
192 m_rows = m_heightField->getNbRows();
193 m_columns = m_heightField->getNbColumns();
194 return m_heightField;
198 writeSamples(QImage(m_sourcePath));
200 writeSamples(m_image->image());
203 int numRows = m_rows;
204 int numCols = m_columns;
205 auto samples = m_samples;
207 physx::PxHeightFieldDesc hfDesc;
208 hfDesc.format = physx::PxHeightFieldFormat::eS16_TM;
209 hfDesc.nbColumns = numRows;
210 hfDesc.nbRows = numCols;
211 hfDesc.samples.data = samples;
212 hfDesc.samples.stride =
sizeof(physx::PxHeightFieldSample);
214 physx::PxDefaultMemoryOutputStream buf;
216 const auto cooking = QPhysicsWorld::getCooking();
217 if (numRows && numCols && cooking && cooking->cookHeightField(hfDesc, buf)) {
218 auto size = buf.getSize();
219 auto *data = buf.getData();
220 physx::PxDefaultMemoryInputData input(data, size);
221 m_heightField = thePhysics->createHeightField(input);
222 qCDebug(lcQuick3dPhysics) <<
"created height field" << m_heightField << numCols << numRows
224 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
228 qCWarning(lcQuick3dPhysics) <<
"Could not create height field from"
229 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
232 return m_heightField;
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
271
272
273
274
275
276
279
280
281
282
283
284
285
286
287
288
289
292
293
294
295
296
297
298
299
300
301
302
303
305QHeightFieldShape::QHeightFieldShape() =
default;
307QHeightFieldShape::~QHeightFieldShape()
309 delete m_heightFieldGeometry;
311 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
314physx::PxGeometry *QHeightFieldShape::getPhysXGeometry()
316 if (m_dirtyPhysx || m_scaleDirty || !m_heightFieldGeometry) {
317 updatePhysXGeometry();
319 return m_heightFieldGeometry;
322void QHeightFieldShape::updatePhysXGeometry()
324 delete m_heightFieldGeometry;
325 m_heightFieldGeometry =
nullptr;
329 auto *hf = m_heightField->heightField();
330 float rows = m_heightField->rows();
331 float cols = m_heightField->columns();
333 if (hf && cols > 1 && rows > 1) {
334 QVector3D scaledExtents = m_extents * sceneScale();
335 m_heightFieldGeometry =
new physx::PxHeightFieldGeometry(
336 hf, physx::PxMeshGeometryFlags(), scaledExtents.y() / 0x10000,
337 scaledExtents.x() / (cols - 1), scaledExtents.z() / (rows - 1));
338 m_hfOffset = { -scaledExtents.x() / 2, 0, -scaledExtents.z() / 2 };
340 qCDebug(lcQuick3dPhysics) <<
"created height field geom" << m_heightFieldGeometry <<
"scale"
341 << scaledExtents << m_heightField->columns()
342 << m_heightField->rows();
344 m_dirtyPhysx =
false;
347void QHeightFieldShape::updateExtents()
349 if (!m_heightField || m_extentsSetExplicitly)
351 int numRows = m_heightField->rows();
352 int numCols = m_heightField->columns();
353 auto prevExt = m_extents;
354 if (numRows == numCols) {
355 m_extents = { 100, 100, 100 };
356 }
else if (numRows < numCols) {
357 float f =
float(numRows) /
float(numCols);
358 m_extents = { 100.f, 100.f, 100.f * f };
360 float f =
float(numCols) /
float(numRows);
361 m_extents = { 100.f * f, 100.f, 100.f };
363 if (m_extents != prevExt) {
364 emit extentsChanged();
368const QUrl &QHeightFieldShape::source()
const
370 return m_heightMapSource;
373void QHeightFieldShape::setSource(
const QUrl &newSource)
375 if (m_heightMapSource == newSource)
377 m_heightMapSource = newSource;
381 if (m_image ==
nullptr) {
382 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
383 m_heightField =
nullptr;
387 if (m_image ==
nullptr && !newSource.isEmpty()) {
388 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
389 emit needsRebuild(
this);
393 emit sourceChanged();
396QQuickImage *QHeightFieldShape::image()
const
401void QHeightFieldShape::setImage(QQuickImage *newImage)
403 if (m_image == newImage)
407 m_image->disconnect(
this);
411 if (m_image !=
nullptr) {
412 connect(m_image, &QObject::destroyed,
this, &QHeightFieldShape::imageDestroyed);
413 connect(m_image, &QQuickImage::paintedGeometryChanged,
this,
414 &QHeightFieldShape::imageGeometryChanged);
418 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
419 m_heightField =
nullptr;
421 if (m_image !=
nullptr)
422 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
423 else if (!m_heightMapSource.isEmpty())
424 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
427 emit needsRebuild(
this);
431void QHeightFieldShape::imageDestroyed(QObject *image)
433 Q_ASSERT(m_image == image);
438void QHeightFieldShape::imageGeometryChanged()
442 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
443 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
445 emit needsRebuild(
this);
448const QVector3D &QHeightFieldShape::extents()
const
453void QHeightFieldShape::setExtents(
const QVector3D &newExtents)
455 m_extentsSetExplicitly =
true;
456 if (m_extents == newExtents)
458 m_extents = newExtents;
462 emit needsRebuild(
this);
463 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)
Combined button and popup list for selecting options.