11#include <QtQuick3D/QQuick3DGeometry>
12#include <extensions/PxExtensionsAPI.h>
20#include "foundation/PxVec3.h"
22#include "extensions/PxDefaultStreams.h"
23#include "geometry/PxHeightField.h"
24#include "geometry/PxHeightFieldDesc.h"
40 void ref() { ++refCount; }
41 int deref() {
return --refCount; }
53 QQuickImage *m_image =
nullptr;
54 physx::PxHeightFieldSample *m_samples =
nullptr;
55 physx::PxHeightField *m_heightField =
nullptr;
65 const QObject *contextObject);
70 static QHash<QString, QQuick3DPhysicsHeightField *> heightFieldHash;
71 static QHash<QQuickImage *, QQuick3DPhysicsHeightField *> heightFieldImageHash;
75QHash<QQuickImage *, QQuick3DPhysicsHeightField *>
81 const QQmlContext *context = qmlContext(contextObject);
83 const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
84 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
86 auto *heightField = heightFieldHash.value(qmlSource);
89 heightFieldHash[qmlSource] = heightField;
97 auto *heightField = heightFieldImageHash.value(source);
100 heightFieldImageHash[source] = heightField;
108 if (heightField !=
nullptr && heightField
->deref() == 0) {
109 qCDebug(lcQuick3dPhysics()) <<
"deleting height field" << heightField;
110 erase_if(heightFieldHash,
111 [heightField](std::pair<
const QString &, QQuick3DPhysicsHeightField *&> h) {
112 return h.second == heightField;
114 erase_if(heightFieldImageHash,
115 [heightField](std::pair<QQuickImage *, QQuick3DPhysicsHeightField *&> h) {
116 return h.second == heightField;
136 if (Q_UNLIKELY(heightMap.isNull())) {
144 m_rows = heightMap.height();
145 m_columns = heightMap.width();
146 int numRows = m_rows;
147 int numCols = m_columns;
150 m_samples =
reinterpret_cast<physx::PxHeightFieldSample *>(
151 malloc(
sizeof(physx::PxHeightFieldSample) * (numRows * numCols)));
152 for (
int i = 0; i < numCols; i++)
153 for (
int j = 0; j < numRows; j++) {
154 float f = heightMap.pixelColor(i, j).valueF() - 0.5;
156 m_samples[i * numRows + j] = { qint16(0xffff * f), 0, 0 };
163 return m_heightField;
165 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
166 if (thePhysics ==
nullptr)
170 if (m_image ==
nullptr && m_sourcePath.isEmpty())
174 const bool readFromFile = m_image ==
nullptr;
181 m_heightField = QCacheUtils::readCachedHeightField(m_sourcePath, *thePhysics);
182 if (m_heightField !=
nullptr) {
183 m_rows = m_heightField->getNbRows();
184 m_columns = m_heightField->getNbColumns();
185 return m_heightField;
189 m_heightField = QCacheUtils::readCookedHeightField(m_sourcePath, *thePhysics);
190 if (m_heightField !=
nullptr) {
191 m_rows = m_heightField->getNbRows();
192 m_columns = m_heightField->getNbColumns();
193 return m_heightField;
197 writeSamples(QImage(m_sourcePath));
199 writeSamples(m_image->image());
202 int numRows = m_rows;
203 int numCols = m_columns;
204 auto samples = m_samples;
206 physx::PxHeightFieldDesc hfDesc;
207 hfDesc.format = physx::PxHeightFieldFormat::eS16_TM;
208 hfDesc.nbColumns = numRows;
209 hfDesc.nbRows = numCols;
210 hfDesc.samples.data = samples;
211 hfDesc.samples.stride =
sizeof(physx::PxHeightFieldSample);
213 physx::PxDefaultMemoryOutputStream buf;
215 const auto cooking = QPhysicsWorld::getCooking();
216 if (numRows && numCols && cooking && cooking->cookHeightField(hfDesc, buf)) {
217 auto size = buf.getSize();
218 auto *data = buf.getData();
219 physx::PxDefaultMemoryInputData input(data, size);
220 m_heightField = thePhysics->createHeightField(input);
221 qCDebug(lcQuick3dPhysics) <<
"created height field" << m_heightField << numCols << numRows
223 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
225 QCacheUtils::writeCachedHeightField(m_sourcePath, buf);
227 qCWarning(lcQuick3dPhysics) <<
"Could not create height field from"
228 << (readFromFile ? m_sourcePath : QString::fromUtf8(
"image"));
231 return m_heightField;
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
270
271
272
273
274
275
278
279
280
281
282
283
284
285
286
287
288
291
292
293
294
295
296
297
298
299
300
301
302
304QHeightFieldShape::QHeightFieldShape() =
default;
306QHeightFieldShape::~QHeightFieldShape()
308 delete m_heightFieldGeometry;
310 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
313physx::PxGeometry *QHeightFieldShape::getPhysXGeometry()
315 if (m_dirtyPhysx || m_scaleDirty || !m_heightFieldGeometry) {
316 updatePhysXGeometry();
318 return m_heightFieldGeometry;
321void QHeightFieldShape::updatePhysXGeometry()
323 delete m_heightFieldGeometry;
324 m_heightFieldGeometry =
nullptr;
328 auto *hf = m_heightField->heightField();
329 float rows = m_heightField->rows();
330 float cols = m_heightField->columns();
332 if (hf && cols > 1 && rows > 1) {
333 QVector3D scaledExtents = m_extents * sceneScale();
334 m_heightFieldGeometry =
new physx::PxHeightFieldGeometry(
335 hf, physx::PxMeshGeometryFlags(), scaledExtents.y() / 0x10000,
336 scaledExtents.x() / (cols - 1), scaledExtents.z() / (rows - 1));
337 m_hfOffset = { -scaledExtents.x() / 2, 0, -scaledExtents.z() / 2 };
339 qCDebug(lcQuick3dPhysics) <<
"created height field geom" << m_heightFieldGeometry <<
"scale"
340 << scaledExtents << m_heightField->columns()
341 << m_heightField->rows();
343 m_dirtyPhysx =
false;
346void QHeightFieldShape::updateExtents()
348 if (!m_heightField || m_extentsSetExplicitly)
350 int numRows = m_heightField->rows();
351 int numCols = m_heightField->columns();
352 auto prevExt = m_extents;
353 if (numRows == numCols) {
354 m_extents = { 100, 100, 100 };
355 }
else if (numRows < numCols) {
356 float f =
float(numRows) /
float(numCols);
357 m_extents = { 100.f, 100.f, 100.f * f };
359 float f =
float(numCols) /
float(numRows);
360 m_extents = { 100.f * f, 100.f, 100.f };
362 if (m_extents != prevExt) {
363 emit extentsChanged();
367const QUrl &QHeightFieldShape::source()
const
369 return m_heightMapSource;
372void QHeightFieldShape::setSource(
const QUrl &newSource)
374 if (m_heightMapSource == newSource)
376 m_heightMapSource = newSource;
380 if (m_image ==
nullptr) {
381 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
382 m_heightField =
nullptr;
386 if (m_image ==
nullptr && !newSource.isEmpty()) {
387 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
388 emit needsRebuild(
this);
392 emit sourceChanged();
395QQuickImage *QHeightFieldShape::image()
const
400void QHeightFieldShape::setImage(QQuickImage *newImage)
402 if (m_image == newImage)
406 m_image->disconnect(
this);
410 if (m_image !=
nullptr) {
411 connect(m_image, &QObject::destroyed,
this, &QHeightFieldShape::imageDestroyed);
412 connect(m_image, &QQuickImage::paintedGeometryChanged,
this,
413 &QHeightFieldShape::imageGeometryChanged);
417 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
418 m_heightField =
nullptr;
420 if (m_image !=
nullptr)
421 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
422 else if (!m_heightMapSource.isEmpty())
423 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_heightMapSource,
this);
426 emit needsRebuild(
this);
430void QHeightFieldShape::imageDestroyed(QObject *image)
432 Q_ASSERT(m_image == image);
437void QHeightFieldShape::imageGeometryChanged()
441 QQuick3DPhysicsHeightFieldManager::releaseHeightField(m_heightField);
442 m_heightField = QQuick3DPhysicsHeightFieldManager::getHeightField(m_image);
444 emit needsRebuild(
this);
447const QVector3D &QHeightFieldShape::extents()
const
452void QHeightFieldShape::setExtents(
const QVector3D &newExtents)
454 m_extentsSetExplicitly =
true;
455 if (m_extents == newExtents)
457 m_extents = newExtents;
461 emit needsRebuild(
this);
462 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()