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; }
54 physx::PxHeightFieldSample *m_samples =
nullptr;
55 physx::PxHeightField *m_heightField =
nullptr;
70 static QHash<QString, QQuick3DPhysicsHeightField *> heightFieldHash;
71 static QHash<QQuickImage *, QQuick3DPhysicsHeightField *> heightFieldImageHash;
74QHash<QString, QQuick3DPhysicsHeightField *> QQuick3DPhysicsHeightFieldManager::heightFieldHash;
75QHash<QQuickImage *, QQuick3DPhysicsHeightField *>
76 QQuick3DPhysicsHeightFieldManager::heightFieldImageHash;
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;
111 [heightField](std::pair<const QString &, QQuick3DPhysicsHeightField *&>
h) {
112 return h.second == heightField;
115 [heightField](std::pair<QQuickImage *, QQuick3DPhysicsHeightField *&>
h) {
116 return h.second == heightField;
123 : m_sourcePath(qmlSource)
136 m_rows = heightMap.
height();
137 m_columns = heightMap.
width();
138 int numRows = m_rows;
139 int numCols = m_columns;
142 m_samples =
reinterpret_cast<physx::PxHeightFieldSample *
>(
143 malloc(
sizeof(physx::PxHeightFieldSample) * (numRows * numCols)));
144 for (
int i = 0;
i < numCols;
i++)
145 for (
int j = 0;
j < numRows;
j++) {
148 m_samples[
i * numRows +
j] = {
qint16(0xffff *
f), 0, 0 };
155 return m_heightField;
157 physx::PxPhysics *thePhysics = QPhysicsWorld::getPhysics();
158 if (thePhysics ==
nullptr)
162 if (m_image ==
nullptr && m_sourcePath.
isEmpty())
166 const bool readFromFile = m_image ==
nullptr;
171 if (m_heightField !=
nullptr) {
172 m_rows = m_heightField->getNbRows();
173 m_columns = m_heightField->getNbColumns();
174 return m_heightField;
179 if (m_heightField !=
nullptr) {
180 m_rows = m_heightField->getNbRows();
181 m_columns = m_heightField->getNbColumns();
182 return m_heightField;
191 int numRows = m_rows;
192 int numCols = m_columns;
195 physx::PxHeightFieldDesc hfDesc;
196 hfDesc.format = physx::PxHeightFieldFormat::eS16_TM;
197 hfDesc.nbColumns = numRows;
198 hfDesc.nbRows = numCols;
200 hfDesc.samples.stride =
sizeof(physx::PxHeightFieldSample);
202 physx::PxDefaultMemoryOutputStream
buf;
204 const auto cooking = QPhysicsWorld::getCooking();
205 if (numRows && numCols && cooking && cooking->cookHeightField(hfDesc,
buf)) {
209 m_heightField = thePhysics->createHeightField(
input);
210 qCDebug(lcQuick3dPhysics) <<
"created height field" << m_heightField << numCols << numRows
216 qCWarning(lcQuick3dPhysics) <<
"Could not create height field from"
220 return m_heightField;
297 delete m_heightFieldGeometry;
304 if (m_dirtyPhysx ||
m_scaleDirty || !m_heightFieldGeometry) {
305 updatePhysXGeometry();
307 return m_heightFieldGeometry;
310void QHeightFieldShape::updatePhysXGeometry()
312 delete m_heightFieldGeometry;
313 m_heightFieldGeometry =
nullptr;
318 float rows = m_heightField->
rows();
319 float cols = m_heightField->
columns();
321 if (hf && cols > 1 && rows > 1) {
323 m_heightFieldGeometry =
new physx::PxHeightFieldGeometry(
324 hf, physx::PxMeshGeometryFlags(), scaledExtents.y() / 0x10000,
325 scaledExtents.x() / (cols - 1), scaledExtents.z() / (rows - 1));
326 m_hfOffset = { -scaledExtents.
x() / 2, 0, -scaledExtents.z() / 2 };
328 qCDebug(lcQuick3dPhysics) <<
"created height field geom" << m_heightFieldGeometry <<
"scale"
329 << scaledExtents << m_heightField->
columns()
330 << m_heightField->
rows();
332 m_dirtyPhysx =
false;
335void QHeightFieldShape::updateExtents()
337 if (!m_heightField || m_extentsSetExplicitly)
339 int numRows = m_heightField->
rows();
340 int numCols = m_heightField->
columns();
341 auto prevExt = m_extents;
342 if (numRows == numCols) {
343 m_extents = { 100, 100, 100 };
344 }
else if (numRows < numCols) {
345 float f = float(numRows) / float(numCols);
346 m_extents = { 100.f, 100.f, 100.f *
f };
348 float f = float(numCols) / float(numRows);
349 m_extents = { 100.f *
f, 100.f, 100.f };
351 if (m_extents != prevExt) {
358 return m_heightMapSource;
361void QHeightFieldShape::setSource(
const QUrl &newSource)
363 if (m_heightMapSource == newSource)
365 m_heightMapSource = newSource;
369 if (m_image ==
nullptr) {
371 m_heightField =
nullptr;
375 if (m_image ==
nullptr && !newSource.isEmpty()) {
381 emit sourceChanged();
389void QHeightFieldShape::setImage(
QQuickImage *newImage)
391 if (m_image == newImage)
399 if (m_image !=
nullptr) {
402 &QHeightFieldShape::imageGeometryChanged);
407 m_heightField =
nullptr;
409 if (m_image !=
nullptr)
411 else if (!m_heightMapSource.
isEmpty())
426void QHeightFieldShape::imageGeometryChanged()
443 m_extentsSetExplicitly =
true;
444 if (m_extents == newExtents)
446 m_extents = newExtents;
void needsRebuild(QObject *)
float valueF() const noexcept
Returns the value color component of this color.
void setExtents(const QVector3D &newExtents)
physx::PxGeometry * getPhysXGeometry() override
QHeightFieldShape()
\qmltype HeightFieldShape \inqmlmodule QtQuick3D.Physics \inherits CollisionShape
int width() const
Returns the width of the image.
int height() const
Returns the height of the image.
QColor pixelColor(int x, int y) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
The QQmlContext class defines a context within a QML engine.
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
static QQuick3DPhysicsHeightField * getHeightField(const QUrl &source, const QObject *contextObject)
static void releaseHeightField(QQuick3DPhysicsHeightField *heightField)
QQuick3DPhysicsHeightField(const QString &qmlSource)
void writeSamples(const QImage &heightMap)
physx::PxHeightField * heightField()
~QQuick3DPhysicsHeightField()
void paintedGeometryChanged()
\macro QT_RESTRICTED_CAST_FROM_ASCII
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
The QVector3D class represents a vector or vertex in 3D space.
constexpr float x() const noexcept
Returns the x coordinate of this point.
void writeCachedHeightField(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
physx::PxHeightField * readCookedHeightField(const QString &filePath, physx::PxPhysics &physics)
physx::PxHeightField * readCachedHeightField(const QString &filePath, physx::PxPhysics &physics)
Combined button and popup list for selecting options.
qsizetype erase_if(QByteArray &ba, Predicate pred)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLenum GLsizei const GLchar * buf
GLfloat GLfloat GLfloat GLfloat h
GLsizei GLsizei GLchar * source
GLenum GLenum GLenum input
QQmlContext * qmlContext(const QObject *obj)
static QUrl resolvedUrl(const QUrl &url, const QQmlRefPointer< QQmlContextData > &context)
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)