Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qphysxcharactercontroller.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5
6#include "PxRigidDynamic.h"
7#include "characterkinematic/PxController.h"
8#include "characterkinematic/PxControllerManager.h"
9#include "characterkinematic/PxCapsuleController.h"
10
11#include "qcapsuleshape_p.h"
12#include "qphysicsutils_p.h"
13#include "qphysicsworld_p.h"
15
16#define PHYSX_RELEASE(x) \
17 if (x != nullptr) { \
18 x->release(); \
19 x = nullptr; \
20 }
21
23
24class ControllerCallback : public physx::PxUserControllerHitReport
25{
26public:
27 ControllerCallback(QPhysicsWorld *worldIn) : world(worldIn) { }
28
29 void onShapeHit(const physx::PxControllerShapeHit &hit) override
30 {
31 QMutexLocker locker(&world->m_removedPhysicsNodesMutex);
32
33 QAbstractPhysicsNode *other = static_cast<QAbstractPhysicsNode *>(hit.actor->userData);
34 QCharacterController *trigger =
35 static_cast<QCharacterController *>(hit.controller->getUserData());
36
37 if (!trigger || !other || !trigger->enableShapeHitCallback())
38 return;
39
40 QVector3D position = QPhysicsUtils::toQtType(physx::toVec3(hit.worldPos));
41 QVector3D impulse = QPhysicsUtils::toQtType(hit.dir * hit.length);
42 QVector3D normal = QPhysicsUtils::toQtType(hit.worldNormal);
43
44 emit trigger->shapeHit(other, position, impulse, normal);
45 }
46 void onControllerHit(const physx::PxControllersHit & /*hit*/) override { }
47 void onObstacleHit(const physx::PxControllerObstacleHit & /*hit*/) override { }
48
49private:
50 QPhysicsWorld *world = nullptr;
51};
52
57
59{
60 PHYSX_RELEASE(controller);
61 delete reportCallback;
62 reportCallback = nullptr;
64}
65
67{
68 Q_ASSERT(!controller);
69
70 auto *characterController = static_cast<QCharacterController *>(frontendNode);
71
72 auto shapes = characterController->getCollisionShapesList();
73 if (shapes.length() != 1) {
74 qWarning() << "CharacterController: invalid collision shapes list.";
75 return;
76 }
77 auto *capsule = qobject_cast<QCapsuleShape *>(shapes.first());
78 if (!capsule) {
79 qWarning() << "CharacterController: collision shape is not a capsule.";
80 return;
81 }
82 auto *mgr = world->controllerManager();
83 if (!mgr) {
84 qWarning() << "QtQuick3DPhysics internal error: missing controller manager.";
85 return;
86 }
87
88 createMaterial(physX);
89
90 const QVector3D scale = characterController->sceneScale();
91 const qreal heightScale = scale.y();
92 const qreal radiusScale = scale.x();
93 physx::PxCapsuleControllerDesc desc;
94 reportCallback = new ControllerCallback(world);
95 desc.reportCallback = reportCallback;
96 desc.radius = 0.5f * radiusScale * capsule->diameter();
97 desc.height = heightScale * capsule->height();
98 desc.stepOffset = 0.25f * desc.height; // TODO: API
99
100 desc.material = material;
101 const QVector3D pos = characterController->scenePosition();
102 desc.position = { pos.x(), pos.y(), pos.z() };
103 // Safe to static_cast since createController will always return a PxCapsuleController
104 // if desc is of type PxCapsuleControllerDesc
105 controller = static_cast<physx::PxCapsuleController *>(mgr->createController(desc));
106
107 if (!controller) {
108 qWarning() << "QtQuick3DPhysics internal error: could not create controller.";
109 return;
110 }
111
112 controller->setUserData(static_cast<void *>(frontendNode));
113
114 auto *actor = controller->getActor();
115 if (actor)
116 actor->userData = characterController;
117 else
118 qWarning() << "QtQuick3DPhysics internal error: CharacterController created without actor.";
119}
120
122 QHash<QQuick3DNode *, QMatrix4x4> & /*transformCache*/)
123{
124 if (controller == nullptr)
125 return;
126
127 auto *characterController = static_cast<QCharacterController *>(frontendNode);
128
129 // Update capsule height, radius, stepOffset
130 const auto &shapes = characterController->getCollisionShapesList();
131 auto capsule = shapes.length() == 1 ? qobject_cast<QCapsuleShape *>(shapes.front()) : nullptr;
132
133 if (shapes.length() != 1) {
134 qWarning() << "CharacterController: invalid collision shapes list.";
135 } else if (!capsule) {
136 qWarning() << "CharacterController: collision shape is not a capsule.";
137 } else {
138 const QVector3D sceneScale = characterController->sceneScale();
139 const qreal heightScale = sceneScale.y();
140 const qreal radiusScale = sceneScale.x();
141
142 // Update height
143 const float heightNew = heightScale * capsule->height();
144 if (!qFuzzyCompare(controller->getHeight(), heightNew))
145 controller->resize(heightNew);
146 // Update radius
147 const float radiusNew = 0.5f * radiusScale * capsule->diameter();
148 if (!qFuzzyCompare(controller->getRadius(), radiusNew))
149 controller->setRadius(radiusNew);
150 // Update stepOffset
151 const float stepOffsetNew = 0.25f * heightNew;
152 if (!qFuzzyCompare(controller->getStepOffset(), stepOffsetNew))
153 controller->setStepOffset(stepOffsetNew);
154 }
155
156 // update node from physX
157 QVector3D position = QPhysicsUtils::toQtType(physx::toVec3(controller->getPosition()));
158 const QQuick3DNode *parentNode = static_cast<QQuick3DNode *>(characterController->parentItem());
159 if (!parentNode) {
160 // then it is the same space
161 characterController->setPosition(position);
162 } else {
163 characterController->setPosition(parentNode->mapPositionFromScene(position));
164 }
165
166 QVector3D teleportPos;
167 bool teleport = characterController->getTeleport(teleportPos);
168 if (teleport) {
169 controller->setPosition({ teleportPos.x(), teleportPos.y(), teleportPos.z() });
170 } else if (deltaTime > 0) {
171 const auto displacement =
172 QPhysicsUtils::toPhysXType(characterController->getDisplacement(deltaTime));
173 auto collisions =
174 controller->move(displacement, displacement.magnitude() / 100, deltaTime, {});
175 characterController->setCollisions(QCharacterController::Collisions(uint(collisions)));
176 }
177 // QCharacterController has a material property, but we don't inherit from
178 // QPhysXMaterialBody, so we create the material manually in init()
179 // TODO: handle material changes
180}
181
183{
185 physX, static_cast<QCharacterController *>(frontendNode)->physicsMaterial());
186}
187
192
197
void onShapeHit(const physx::PxControllerShapeHit &hit) override
void onControllerHit(const physx::PxControllersHit &) override
ControllerCallback(QPhysicsWorld *worldIn)
void onObstacleHit(const physx::PxControllerObstacleHit &) override
QAbstractPhysicsNode * frontendNode
QVector< physx::PxShape * > shapes
virtual void cleanup(QPhysXWorld *)
physx::PxMaterial * material
void createMaterialFromQtMaterial(QPhysXWorld *physX, QPhysicsMaterial *qtMaterial)
void shapeHit(QAbstractPhysicsNode *body, const QVector3D &position, const QVector3D &impulse, const QVector3D &normal)
\inmodule QtCore
Definition qmutex.h:313
QPhysXCharacterController(QCharacterController *frontEnd)
DebugDrawBodyType getDebugDrawBodyType() override
void init(QPhysicsWorld *world, QPhysXWorld *physX) override
void cleanup(QPhysXWorld *physX) override
void createMaterial(QPhysXWorld *physX) override
void sync(float deltaTime, QHash< QQuick3DNode *, QMatrix4x4 > &transformCache) override
Q_INVOKABLE QVector3D mapPositionFromScene(const QVector3D &scenePosition) const
\qmlmethod vector3d QtQuick3D::Node::mapPositionFromScene(vector3d scenePosition)
void setPosition(const QVector3D &position)
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
Q_ALWAYS_INLINE physx::PxVec3 toPhysXType(const QVector3D &qvec)
Q_ALWAYS_INLINE QVector3D toQtType(const physx::PxVec3 &vec)
Combined button and popup list for selecting options.
#define PHYSX_RELEASE(x)
DebugDrawBodyType
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define qWarning
Definition qlogging.h:166
GLenum GLenum GLenum GLenum GLenum scale
const qreal radiusScale
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
QSharedPointer< T > other(t)
[5]
QLatin1StringView world("world")