25#include <QApplication>
29#include <Qt3DCore/QEntity>
30#include <Qt3DCore/QTransform>
31#include <Qt3DExtras/QConeMesh>
32#include <Qt3DExtras/QCylinderMesh>
33#include <Qt3DExtras/QDiffuseSpecularMaterial>
34#include <Qt3DExtras/QSphereMesh>
35#include <Qt3DExtras/Qt3DWindow>
47std::pair<Qt3DCore::QEntity*, Qt3DCore::QTransform*>
49 const QColor& colour,
float radius,
51 auto* arrowEntity =
new Qt3DCore::QEntity(parent);
52 auto* arrowTransform =
new Qt3DCore::QTransform(arrowEntity);
55 auto* material =
new Qt3DExtras::QDiffuseSpecularMaterial(arrowEntity);
56 material->setAmbient(colour);
59 auto* shaftEntity =
new Qt3DCore::QEntity(arrowEntity);
60 auto* shaftMesh =
new Qt3DExtras::QCylinderMesh();
61 shaftMesh->setRadius(radius);
62 shaftMesh->setLength(length);
63 auto* shaftTransform =
new Qt3DCore::QTransform();
66 shaftTransform->setRotationX(90);
67 shaftEntity->addComponent(shaftMesh);
68 shaftEntity->addComponent(shaftTransform);
69 shaftEntity->addComponent(material);
72 auto* headEntity =
new Qt3DCore::QEntity(arrowEntity);
73 auto* headMesh =
new Qt3DExtras::QConeMesh();
74 headMesh->setTopRadius(0);
75 headMesh->setBottomRadius(radius * 2.5f);
76 headMesh->setLength(radius * 5.0f);
77 auto* headTransform =
new Qt3DCore::QTransform();
78 headTransform->setTranslation(QVector3D(0, 0, length));
79 headTransform->setRotationX(90);
80 headEntity->addComponent(headMesh);
81 headEntity->addComponent(headTransform);
82 headEntity->addComponent(material);
85 arrowEntity->addComponent(arrowTransform);
86 arrowEntity->setEnabled(
false);
88 return {arrowEntity, arrowTransform};
96 std::uniform_real_distribution<> hueDis(0.0, 1.0);
98 bodies_.resize(physicsWorker_->getInitialBodies().size());
100 for (std::size_t i = 0; i < bodies_.size(); ++i) {
103 auto* entity =
new Qt3DCore::QEntity(rootEntity_);
106 auto* mesh =
new Qt3DExtras::QSphereMesh(entity);
107 mesh->setRadius(physicsWorker_->getInitialBodies()[i].r());
110 auto* transform =
new Qt3DCore::QTransform(entity);
114 const QColor randomColour = QColor::fromHsvF(hueDis(gen), 0.9, 0.95);
115 auto* colouredMaterial =
116 new Qt3DExtras::QDiffuseSpecularMaterial(entity);
122 entity->addComponent(mesh);
123 entity->addComponent(transform);
124 entity->addComponent(colouredMaterial);
127 colouredMaterial->setDiffuse(randomColour);
128 colouredMaterial->setShininess(200);
129 colouredMaterial->setAmbient(randomColour.darker(110));
133 auto [torqueArrowEntity, torqueArrowTransform] =
134 createArrowEntity(entity, QColor(
"magenta"), 0.5f, 10.0f);
135 auto [alphaArrowEntity, alphaArrowTransform] =
136 createArrowEntity(entity, QColor(
"cyan"), 0.5f, 10.0f);
138 bodies_[i] = std::make_tuple(
139 QPointer<Qt3DCore::QEntity>(entity), QPointer(transform),
140 QPointer<Qt3DCore::QEntity>(torqueArrowEntity),
141 QPointer<Qt3DCore::QTransform>(torqueArrowTransform),
142 QPointer<Qt3DCore::QEntity>(alphaArrowEntity),
143 QPointer<Qt3DCore::QTransform>(alphaArrowTransform));
153 Rng::Pcg32 gen(seed == 0 ? std::random_device{}() : seed);
164 physicsThread_->start();
167 simulationTimer_->start(0);
171 if (physicsShutdown_) {
174 physicsShutdown_ =
true;
176 if (simulationTimer_) {
177 simulationTimer_->stop();
180 if (physicsWorker_) {
185 if (simulationTimer_) {
186 disconnect(physicsWorker_,
188 simulationTimer_, &QTimer::stop);
190 disconnect(simulationTimer_, &QTimer::timeout, physicsWorker_,
192 physicsWorker_->stopSimulation();
195 if (physicsThread_) {
196 disconnect(physicsThread_, &QThread::started, physicsWorker_,
200 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
202 if (physicsThread_ && physicsThread_->isRunning()) {
203 physicsThread_->quit();
204 if (!physicsThread_->wait(5000)) {
205 physicsThread_->terminate();
206 physicsThread_->wait();
212 if (physicsWorker_) {
213 physicsWorker_->cleanup();
214 delete physicsWorker_;
222 using CleanupClock = std::chrono::high_resolution_clock;
223 auto phaseStart = CleanupClock::now();
225 if (cleanupCalled_) {
228 cleanupCalled_ =
true;
230 phaseStart = CleanupClock::now();
231 shutdownPhysicsThread();
233 std::chrono::duration<double>(CleanupClock::now() - phaseStart)
241 const Model::Vector3d& vector,
242 double scaleFactor)
const {
243 if (!arrowTransform) {
247 const double magnitudeSq = vector.squaredNorm();
248 if (magnitudeSq < Model::EPSILON * Model::EPSILON) {
249 static_cast<Qt3DCore::QEntity*
>(arrowTransform->parent())
254 static_cast<Qt3DCore::QEntity*
>(arrowTransform->parent())->setEnabled(
true);
258 const QVector3D zAxis(0.0f, 0.0f, 1.0f);
259 const QVector3D targetVector(
static_cast<float>(vector.x()),
260 static_cast<float>(vector.y()),
261 static_cast<float>(vector.z()));
263 const QQuaternion rotation =
264 QQuaternion::rotationTo(zAxis, targetVector.normalized());
266 arrowTransform->setRotation(rotation);
267 arrowTransform->setScale3D(QVector3D(
268 1.0f, 1.0f,
static_cast<float>(std::sqrt(magnitudeSq) * scaleFactor)));
272 const Model::QuaterniondVec& quaternions,
273 const Model::Vector3dVec& torques,
274 const Model::Vector3dVec& alphas,
275 std::size_t iteration) {
278 for (std::size_t i = 0; i < bodies_.size(); ++i) {
279 auto& transform = std::get<1>(bodies_[i]);
281 transform->setTranslation({
static_cast<float>(positions[i].x()),
282 static_cast<float>(positions[i].y()),
283 static_cast<float>(positions[i].z())});
284 transform->setRotation({
static_cast<float>(quaternions[i].w()),
285 static_cast<float>(quaternions[i].x()),
286 static_cast<float>(quaternions[i].y()),
287 static_cast<float>(quaternions[i].z())});
290 if (showTorqueArrow_) {
291 updateVectorArrow(std::get<3>(bodies_[i]), torques[i],
292 Model::TORQUE_ARROW_SCALE);
294 std::get<2>(bodies_[i])->setEnabled(
false);
297 if (showAlphaArrow_) {
298 updateVectorArrow(std::get<5>(bodies_[i]), alphas[i],
299 Model::ALPHA_ARROW_SCALE);
301 std::get<4>(bodies_[i])->setEnabled(
false);
305 iterationCount_ = iteration + 1;
314#include "moc_display.cpp"
Global application profiling instrumentation.
static void addCleanupDisplayProcessEvents(double seconds)
Adds measured time spent in Display cleanup processEvents.
static void addCleanupDisplayPrep(double seconds)
Adds measured time for Display cleanup preparation.
static void addCleanupDisplayWorkerCleanup(double seconds)
Adds measured time for physics worker cleanup.
static void stopFrameRender()
Stops timing a frame render.
static void startFrameRender()
Starts timing a frame render.
static void addCleanupDisplayThreadStop(double seconds)
Adds measured time for stopping the physics thread.
void updatedBodyData(const Vector3dVec &positions, const QuaterniondVec &quaternions, const Vector3dVec &torques, const Vector3dVec &alphas, std::size_t iteration)
Emitted after each simulation step with the updated state of all bodies.
void startSimulation()
Starts the simulation loop.
void performSingleStep()
Performs a single step of the physics simulation and emits the results.
void simulationFinished()
Emitted when the simulation has completed all iterations.
A 32-bit Permuted Congruential Generator (pcg32).
void cleanup()
Performs cleanup actions, such as printing profiling reports.
void createSpheres(Rng::Pcg32 &gen)
Create and set up the spherical bodies in the scene.
std::pair< Qt3DCore::QEntity *, Qt3DCore::QTransform * > createArrowEntity(Qt3DCore::QEntity *parent, const QColor &colour, float radius, float length)
Creates a 3D arrow entity for vector visualization.
~Display() override
Destructor to ensure worker thread is cleaned up.
void simulationFinished()
Emitted when the simulation has run for n_iter iterations.
void updateFrame(const Model::Vector3dVec &positions, const Model::QuaterniondVec &quaternions, const Model::Vector3dVec &torques, const Model::Vector3dVec &alphas, std::size_t iteration)
Slot to receive updated data from the physics worker and update the scene.
void createScene(unsigned int seed)
Set up the scene.
void runSimulation()
Starts the physics simulation by starting the worker thread.
void shutdownPhysicsThread()
Stops the physics thread and destroys the worker.
void updateVectorArrow(Qt3DCore::QTransform *arrowTransform, const Model::Vector3d &vector, double scaleFactor) const
Updates the transform of an arrow entity to represent a 3D vector.
Displaying spherical moving bodies.
A minimal C++ implementation of the PCG32 random number generator.
N-body simulation space with gravitational interaction and collision response.