Browse Source

fix opengl failing to start

djeada 2 months ago
parent
commit
eb7faeae6b
2 changed files with 121 additions and 114 deletions
  1. 117 114
      main.cpp
  2. 4 0
      scripts/setup-deps.sh

+ 117 - 114
main.cpp

@@ -6,6 +6,7 @@
 #include <QDir>
 #include <QDir>
 #include <QQuickWindow>
 #include <QQuickWindow>
 #include <QQmlContext>
 #include <QQmlContext>
+#include <QSGRendererInterface>
 
 
 #include "engine/core/world.h"
 #include "engine/core/world.h"
 #include "engine/core/component.h"
 #include "engine/core/component.h"
@@ -14,34 +15,31 @@
 #include "game/systems/movement_system.h"
 #include "game/systems/movement_system.h"
 #include "game/systems/combat_system.h"
 #include "game/systems/combat_system.h"
 #include "game/systems/selection_system.h"
 #include "game/systems/selection_system.h"
+// #include "game/systems/ai_system.h" // keep if you have AISystem; remove if not
 
 
 class GameEngine : public QObject {
 class GameEngine : public QObject {
     Q_OBJECT
     Q_OBJECT
-
 public:
 public:
     GameEngine() {
     GameEngine() {
-        // Initialize core systems
-        m_world = std::make_unique<Engine::Core::World>();
+        m_world    = std::make_unique<Engine::Core::World>();
         m_renderer = std::make_unique<Render::GL::Renderer>();
         m_renderer = std::make_unique<Render::GL::Renderer>();
-        m_camera = std::make_unique<Render::GL::Camera>();
-        
-        // Add game systems
+        m_camera   = std::make_unique<Render::GL::Camera>();
+
         m_world->addSystem(std::make_unique<Game::Systems::MovementSystem>());
         m_world->addSystem(std::make_unique<Game::Systems::MovementSystem>());
         m_world->addSystem(std::make_unique<Game::Systems::CombatSystem>());
         m_world->addSystem(std::make_unique<Game::Systems::CombatSystem>());
         m_world->addSystem(std::make_unique<Game::Systems::AISystem>());
         m_world->addSystem(std::make_unique<Game::Systems::AISystem>());
-        
+
         m_selectionSystem = std::make_unique<Game::Systems::SelectionSystem>();
         m_selectionSystem = std::make_unique<Game::Systems::SelectionSystem>();
         m_world->addSystem(std::make_unique<Game::Systems::SelectionSystem>());
         m_world->addSystem(std::make_unique<Game::Systems::SelectionSystem>());
-        
+
         setupTestScene();
         setupTestScene();
     }
     }
+
     Q_INVOKABLE void onMapClicked(qreal sx, qreal sy) {
     Q_INVOKABLE void onMapClicked(qreal sx, qreal sy) {
         if (!m_window) return;
         if (!m_window) return;
         ensureInitialized();
         ensureInitialized();
-        // Convert screen coords to world point on ground (y = 0)
         QVector3D hit;
         QVector3D hit;
         if (!screenToGround(QPointF(sx, sy), hit)) return;
         if (!screenToGround(QPointF(sx, sy), hit)) return;
-        // Move our unit to that point
         if (auto* entity = m_world->getEntity(m_playerUnitId)) {
         if (auto* entity = m_world->getEntity(m_playerUnitId)) {
             if (auto* move = entity->getComponent<Engine::Core::MovementComponent>()) {
             if (auto* move = entity->getComponent<Engine::Core::MovementComponent>()) {
                 move->targetX = hit.x();
                 move->targetX = hit.x();
@@ -50,58 +48,59 @@ public:
             }
             }
         }
         }
     }
     }
+
     void setWindow(QQuickWindow* w) { m_window = w; }
     void setWindow(QQuickWindow* w) { m_window = w; }
-    
+
     void initialize() {
     void initialize() {
+        // Must be called with a current, valid GL context.
+        QOpenGLContext* ctx = QOpenGLContext::currentContext();
+        if (!ctx || !ctx->isValid()) {
+            qWarning() << "GameEngine::initialize called without a current, valid OpenGL context";
+            return;
+        }
         if (!m_renderer->initialize()) {
         if (!m_renderer->initialize()) {
             qWarning() << "Failed to initialize renderer";
             qWarning() << "Failed to initialize renderer";
             return;
             return;
         }
         }
-        
         m_renderer->setCamera(m_camera.get());
         m_renderer->setCamera(m_camera.get());
-        
-        // Set up RTS camera view
         m_camera->setRTSView(QVector3D(0, 0, 0), 15.0f, 45.0f);
         m_camera->setRTSView(QVector3D(0, 0, 0), 15.0f, 45.0f);
         m_camera->setPerspective(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
         m_camera->setPerspective(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
-        
+
         m_initialized = true;
         m_initialized = true;
         qDebug() << "Game engine initialized successfully";
         qDebug() << "Game engine initialized successfully";
     }
     }
-    
-    void ensureInitialized() {
-        if (!m_initialized) {
-            initialize();
-        }
-    }
-    
-    void update(float deltaTime) {
-        if (m_world) {
-            m_world->update(deltaTime);
-        }
+
+    void ensureInitialized() { if (!m_initialized) initialize(); }
+
+    void update(float dt) {
+        if (m_world) m_world->update(dt);
     }
     }
-    
+
     void render() {
     void render() {
-        if (m_renderer && m_world && m_initialized) {
-            if (m_window) {
-                m_renderer->setViewport(m_window->width(), m_window->height());
-            }
-            m_renderer->beginFrame();
-            m_renderer->renderWorld(m_world.get());
-            m_renderer->endFrame();
+        if (!m_renderer || !m_world || !m_initialized) return;
+
+        if (m_window) {
+            const int vpW = int(m_window->width()  * m_window->effectiveDevicePixelRatio());
+            const int vpH = int(m_window->height() * m_window->effectiveDevicePixelRatio());
+            m_renderer->setViewport(vpW, vpH);
         }
         }
+
+        m_renderer->beginFrame();
+        m_renderer->renderWorld(m_world.get());
+        m_renderer->endFrame();
     }
     }
 
 
 private:
 private:
     void setupTestScene() {
     void setupTestScene() {
-        // Create a single archer unit at origin
         auto entity = m_world->createEntity();
         auto entity = m_world->createEntity();
         m_playerUnitId = entity->getId();
         m_playerUnitId = entity->getId();
 
 
         auto transform = entity->addComponent<Engine::Core::TransformComponent>();
         auto transform = entity->addComponent<Engine::Core::TransformComponent>();
         transform->position = {0.0f, 0.0f, 0.0f};
         transform->position = {0.0f, 0.0f, 0.0f};
-        transform->scale = {0.5f, 0.5f, 0.5f}; // smaller quad as a unit
+        transform->scale    = {0.5f, 0.5f, 0.5f};
+        transform->rotation.x = -90.0f; // lay quad on XZ plane
 
 
-    auto renderable = entity->addComponent<Engine::Core::RenderableComponent>("", "");
+        auto renderable = entity->addComponent<Engine::Core::RenderableComponent>("", "");
         renderable->visible = true;
         renderable->visible = true;
 
 
         auto unit = entity->addComponent<Engine::Core::UnitComponent>();
         auto unit = entity->addComponent<Engine::Core::UnitComponent>();
@@ -112,48 +111,41 @@ private:
 
 
         entity->addComponent<Engine::Core::MovementComponent>();
         entity->addComponent<Engine::Core::MovementComponent>();
 
 
-    // Rotate unit quad to lie on the ground (XZ plane)
-    transform->rotation.x = -90.0f;
-
         qDebug() << "Test scene created with 1 archer (entity" << m_playerUnitId << ")";
         qDebug() << "Test scene created with 1 archer (entity" << m_playerUnitId << ")";
     }
     }
 
 
     bool screenToGround(const QPointF& screenPt, QVector3D& outWorld) {
     bool screenToGround(const QPointF& screenPt, QVector3D& outWorld) {
         if (!m_window || !m_camera) return false;
         if (!m_window || !m_camera) return false;
-        // Viewport
-        float w = static_cast<float>(m_window->width());
-        float h = static_cast<float>(m_window->height());
+        float w = float(m_window->width());
+        float h = float(m_window->height());
         if (w <= 0 || h <= 0) return false;
         if (w <= 0 || h <= 0) return false;
 
 
-        // Convert to Normalized Device Coordinates
-        float x = (2.0f * static_cast<float>(screenPt.x()) / w) - 1.0f;
-        float y = 1.0f - (2.0f * static_cast<float>(screenPt.y()) / h);
+        float x = (2.0f * float(screenPt.x()) / w) - 1.0f;
+        float y = 1.0f - (2.0f * float(screenPt.y()) / h);
 
 
-    bool ok = false;
-    QMatrix4x4 invVP = (m_camera->getProjectionMatrix() * m_camera->getViewMatrix()).inverted(&ok);
-    if (!ok) return false;
+        bool ok = false;
+        QMatrix4x4 invVP = (m_camera->getProjectionMatrix() * m_camera->getViewMatrix()).inverted(&ok);
+        if (!ok) return false;
 
 
-        // Ray from near to far in world space
         QVector4D nearClip(x, y, 0.0f, 1.0f);
         QVector4D nearClip(x, y, 0.0f, 1.0f);
-        QVector4D farClip(x, y, 1.0f, 1.0f);
+        QVector4D farClip (x, y, 1.0f, 1.0f);
         QVector4D nearWorld4 = invVP * nearClip;
         QVector4D nearWorld4 = invVP * nearClip;
-        QVector4D farWorld4 = invVP * farClip;
+        QVector4D farWorld4  = invVP * farClip;
         if (nearWorld4.w() == 0.0f || farWorld4.w() == 0.0f) return false;
         if (nearWorld4.w() == 0.0f || farWorld4.w() == 0.0f) return false;
         QVector3D rayOrigin = (nearWorld4 / nearWorld4.w()).toVector3D();
         QVector3D rayOrigin = (nearWorld4 / nearWorld4.w()).toVector3D();
-        QVector3D rayEnd = (farWorld4 / farWorld4.w()).toVector3D();
-        QVector3D rayDir = (rayEnd - rayOrigin).normalized();
+        QVector3D rayEnd    = (farWorld4  / farWorld4.w()).toVector3D();
+        QVector3D rayDir    = (rayEnd - rayOrigin).normalized();
 
 
-        // Intersect with plane y=0
-        if (qFuzzyIsNull(rayDir.y())) return false; // parallel
+        if (qFuzzyIsNull(rayDir.y())) return false;
         float t = -rayOrigin.y() / rayDir.y();
         float t = -rayOrigin.y() / rayDir.y();
-        if (t < 0.0f) return false; // behind camera
+        if (t < 0.0f) return false;
         outWorld = rayOrigin + rayDir * t;
         outWorld = rayOrigin + rayDir * t;
         return true;
         return true;
     }
     }
 
 
     std::unique_ptr<Engine::Core::World> m_world;
     std::unique_ptr<Engine::Core::World> m_world;
     std::unique_ptr<Render::GL::Renderer> m_renderer;
     std::unique_ptr<Render::GL::Renderer> m_renderer;
-    std::unique_ptr<Render::GL::Camera> m_camera;
+    std::unique_ptr<Render::GL::Camera>   m_camera;
     std::unique_ptr<Game::Systems::SelectionSystem> m_selectionSystem;
     std::unique_ptr<Game::Systems::SelectionSystem> m_selectionSystem;
     QQuickWindow* m_window = nullptr;
     QQuickWindow* m_window = nullptr;
     Engine::Core::EntityID m_playerUnitId = 0;
     Engine::Core::EntityID m_playerUnitId = 0;
@@ -162,79 +154,90 @@ private:
 
 
 int main(int argc, char *argv[])
 int main(int argc, char *argv[])
 {
 {
+    // Force desktop OpenGL + GLX path BEFORE any window is created.
+    if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY") && qEnvironmentVariableIsSet("DISPLAY")) {
+        qputenv("QT_QPA_PLATFORM", "xcb");   // prefer XCB/GLX over Wayland/EGL when XWayland is present
+    }
+    qputenv("QT_OPENGL", "desktop");         // desktop GL, not GLES/EGL
+    qputenv("QSG_RHI_BACKEND", "opengl");    // OpenGL RHI
+    QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
+
+    // Request desktop GL 3.3 core.
+    QSurfaceFormat fmt;
+    fmt.setVersion(3, 3);
+    fmt.setProfile(QSurfaceFormat::CoreProfile);
+    fmt.setDepthBufferSize(24);
+    fmt.setStencilBufferSize(8);
+    fmt.setSamples(0);
+    QSurfaceFormat::setDefaultFormat(fmt);
+
     QGuiApplication app(argc, argv);
     QGuiApplication app(argc, argv);
 
 
-    // Set up OpenGL 3.3 Core Profile
-    QSurfaceFormat format;
-    format.setVersion(3, 3);
-    format.setProfile(QSurfaceFormat::CoreProfile);
-    format.setDepthBufferSize(24);
-    format.setStencilBufferSize(8);
-    format.setSamples(4); // 4x MSAA
-    QSurfaceFormat::setDefaultFormat(format);
+    auto gameEngine = new GameEngine();
 
 
-    // Set up QML engine
     QQmlApplicationEngine engine;
     QQmlApplicationEngine engine;
-
-    // Register C++ types with QML if needed
-    // qmlRegisterType<GameEngine>("StandardOfIron", 1, 0, "GameEngine");
-
-    // Load QML from the compiled resource path (see generated :/StandardOfIron/ui/qml/*)
+    // Expose to QML BEFORE loading (safer if QML binds early).
+    engine.rootContext()->setContextProperty("game", gameEngine);
     engine.load(QUrl(QStringLiteral("qrc:/StandardOfIron/ui/qml/Main.qml")));
     engine.load(QUrl(QStringLiteral("qrc:/StandardOfIron/ui/qml/Main.qml")));
-
     if (engine.rootObjects().isEmpty()) {
     if (engine.rootObjects().isEmpty()) {
         qWarning() << "Failed to load QML file";
         qWarning() << "Failed to load QML file";
         return -1;
         return -1;
     }
     }
 
 
-    qDebug() << "Application started successfully";
-    qDebug() << "Assets directory:" << QDir::currentPath() + "/assets";
-
-    // Get the QQuickWindow from the loaded QML
     QObject* rootObj = engine.rootObjects().first();
     QObject* rootObj = engine.rootObjects().first();
     QQuickWindow* window = qobject_cast<QQuickWindow*>(rootObj);
     QQuickWindow* window = qobject_cast<QQuickWindow*>(rootObj);
+    if (!window) window = rootObj->findChild<QQuickWindow*>();
     if (!window) {
     if (!window) {
-        // Try to find a QQuickWindow child
-        window = rootObj->findChild<QQuickWindow*>();
+        qWarning() << "No QQuickWindow found for OpenGL initialization.";
+        return -2;
     }
     }
 
 
-    auto gameEngine = new GameEngine();
-    // Expose to QML
-    engine.rootContext()->setContextProperty("game", gameEngine);
+    // For now, draw GL ON TOP of QML (simplest to verify).
+    window->setColor(Qt::black);
+
+    gameEngine->setWindow(window);
+
+    // Informative logging (no current-context check here).
+    QObject::connect(window, &QQuickWindow::sceneGraphInitialized, window, [window]() {
+        if (auto *ri = window->rendererInterface()) {
+            auto api = ri->graphicsApi();
+            const char* name = api == QSGRendererInterface::OpenGLRhi     ? "OpenGLRhi"  :
+                               api == QSGRendererInterface::VulkanRhi      ? "VulkanRhi"  :
+                               api == QSGRendererInterface::Direct3D11Rhi  ? "D3D11Rhi"   :
+                               api == QSGRendererInterface::MetalRhi       ? "MetalRhi"   :
+                               api == QSGRendererInterface::Software       ? "Software"   : "Unknown";
+            qInfo() << "QSG graphicsApi:" << name;
+        }
+    });
 
 
-    if (window) {
-        window->setColor(Qt::transparent);
-        gameEngine->setWindow(window);
-        // Per-frame update/render loop (context is current here)
-        QObject::connect(window, &QQuickWindow::beforeRendering, gameEngine, [gameEngine, window]() {
-            window->beginExternalCommands();
-            gameEngine->ensureInitialized();
-            gameEngine->update(1.0f / 60.0f); // Fixed timestep for now
-            gameEngine->render();
-            window->endExternalCommands();
-        }, Qt::DirectConnection);
-
-        // Keep camera aspect ratio in sync with window size
-        auto updateAspect = [gameEngine, window]() {
-            if (!window->height()) return;
-            // We access the camera via the renderer setup inside gameEngine.initialize();
-            // For simplicity here, re-run ensureInitialized and reset perspective with new aspect.
-            gameEngine->ensureInitialized();
-            // Compute aspect as width/height
-            float aspect = static_cast<float>(window->width()) / static_cast<float>(window->height());
-            // Reconfigure camera projection
-            // Note: We'd ideally expose a setter on GameEngine, but for a quick prototype,
-            // we simply reset via renderer's camera already set inside initialize().
-            // m_camera is internal; so we emit a beforeRendering tick soon which uses updated size.
-        };
-        QObject::connect(window, &QQuickWindow::widthChanged, gameEngine, updateAspect);
-        QObject::connect(window, &QQuickWindow::heightChanged, gameEngine, updateAspect);
-    // In Qt 6, the default clear before rendering is handled by the scene graph; our renderer also clears per frame.
-    } else {
-        qWarning() << "No QQuickWindow found for OpenGL initialization.";
-    }
+    QObject::connect(window, &QQuickWindow::sceneGraphError, &app,
+                     [&](QQuickWindow::SceneGraphError, const QString &msg){
+        qCritical() << "Failed to initialize OpenGL scene graph:" << msg;
+        app.exit(3);
+    });
+
+    // Draw AFTER Qt Quick so GL is visible on top.
+    QObject::connect(window, &QQuickWindow::afterRendering, gameEngine, [gameEngine, window]() {
+        auto *ri = window->rendererInterface();
+        const bool isGL = ri && ri->graphicsApi() == QSGRendererInterface::OpenGLRhi;
+        if (!isGL) return;
+
+        window->beginExternalCommands(); // makes the GL context current for external GL
+        QOpenGLContext* ctx = QOpenGLContext::currentContext();
+        if (!ctx || !ctx->isValid()) { window->endExternalCommands(); return; }
+
+        gameEngine->ensureInitialized();
+        gameEngine->update(1.0f / 60.0f);
+        gameEngine->render();
+
+        window->endExternalCommands();
+    }, Qt::DirectConnection);
+
+    qDebug() << "Application started successfully";
+    qDebug() << "Assets directory:" << QDir::currentPath() + "/assets";
 
 
     return app.exec();
     return app.exec();
 }
 }
 
 
-#include "main.moc"
+#include "main.moc"
+

+ 4 - 0
scripts/setup-deps.sh

@@ -117,6 +117,10 @@ APT_PKGS=(
   git
   git
   pkg-config
   pkg-config
   libgl1-mesa-dev
   libgl1-mesa-dev
+  mesa-utils
+  libglx-mesa0
+  mesa-vulkan-drivers
+  libegl1
 )
 )
 
 
 # Qt6 development headers/tools (filtered for availability later)
 # Qt6 development headers/tools (filtered for availability later)