main.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #include <QGuiApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QOpenGLContext>
  4. #include <QSurfaceFormat>
  5. #include <QDebug>
  6. #include <QDir>
  7. #include <QQuickWindow>
  8. #include <QQmlContext>
  9. #include "engine/core/world.h"
  10. #include "engine/core/component.h"
  11. #include "render/gl/renderer.h"
  12. #include "render/gl/camera.h"
  13. #include "game/systems/movement_system.h"
  14. #include "game/systems/combat_system.h"
  15. #include "game/systems/selection_system.h"
  16. class GameEngine : public QObject {
  17. Q_OBJECT
  18. public:
  19. GameEngine() {
  20. // Initialize core systems
  21. m_world = std::make_unique<Engine::Core::World>();
  22. m_renderer = std::make_unique<Render::GL::Renderer>();
  23. m_camera = std::make_unique<Render::GL::Camera>();
  24. // Add game systems
  25. m_world->addSystem(std::make_unique<Game::Systems::MovementSystem>());
  26. m_world->addSystem(std::make_unique<Game::Systems::CombatSystem>());
  27. m_world->addSystem(std::make_unique<Game::Systems::AISystem>());
  28. m_selectionSystem = std::make_unique<Game::Systems::SelectionSystem>();
  29. m_world->addSystem(std::make_unique<Game::Systems::SelectionSystem>());
  30. setupTestScene();
  31. }
  32. Q_INVOKABLE void onMapClicked(qreal sx, qreal sy) {
  33. if (!m_window) return;
  34. ensureInitialized();
  35. // Convert screen coords to world point on ground (y = 0)
  36. QVector3D hit;
  37. if (!screenToGround(QPointF(sx, sy), hit)) return;
  38. // Move our unit to that point
  39. if (auto* entity = m_world->getEntity(m_playerUnitId)) {
  40. if (auto* move = entity->getComponent<Engine::Core::MovementComponent>()) {
  41. move->targetX = hit.x();
  42. move->targetY = hit.z();
  43. move->hasTarget = true;
  44. }
  45. }
  46. }
  47. void setWindow(QQuickWindow* w) { m_window = w; }
  48. void initialize() {
  49. if (!m_renderer->initialize()) {
  50. qWarning() << "Failed to initialize renderer";
  51. return;
  52. }
  53. m_renderer->setCamera(m_camera.get());
  54. // Set up RTS camera view
  55. m_camera->setRTSView(QVector3D(0, 0, 0), 15.0f, 45.0f);
  56. m_camera->setPerspective(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
  57. m_initialized = true;
  58. qDebug() << "Game engine initialized successfully";
  59. }
  60. void ensureInitialized() {
  61. if (!m_initialized) {
  62. initialize();
  63. }
  64. }
  65. void update(float deltaTime) {
  66. if (m_world) {
  67. m_world->update(deltaTime);
  68. }
  69. }
  70. void render() {
  71. if (m_renderer && m_world && m_initialized) {
  72. m_renderer->beginFrame();
  73. m_renderer->renderWorld(m_world.get());
  74. m_renderer->endFrame();
  75. }
  76. }
  77. private:
  78. void setupTestScene() {
  79. // Create a single archer unit at origin
  80. auto entity = m_world->createEntity();
  81. m_playerUnitId = entity->getId();
  82. auto transform = entity->addComponent<Engine::Core::TransformComponent>();
  83. transform->position = {0.0f, 0.0f, 0.0f};
  84. transform->scale = {0.5f, 0.5f, 0.5f}; // smaller quad as a unit
  85. auto renderable = entity->addComponent<Engine::Core::RenderableComponent>("", "");
  86. renderable->visible = true;
  87. auto unit = entity->addComponent<Engine::Core::UnitComponent>();
  88. unit->unitType = "archer";
  89. unit->health = 80;
  90. unit->maxHealth = 80;
  91. unit->speed = 3.0f;
  92. entity->addComponent<Engine::Core::MovementComponent>();
  93. // Rotate unit quad to lie on the ground (XZ plane)
  94. transform->rotation.x = -90.0f;
  95. qDebug() << "Test scene created with 1 archer (entity" << m_playerUnitId << ")";
  96. }
  97. bool screenToGround(const QPointF& screenPt, QVector3D& outWorld) {
  98. if (!m_window || !m_camera) return false;
  99. // Viewport
  100. float w = static_cast<float>(m_window->width());
  101. float h = static_cast<float>(m_window->height());
  102. if (w <= 0 || h <= 0) return false;
  103. // Convert to Normalized Device Coordinates
  104. float x = (2.0f * static_cast<float>(screenPt.x()) / w) - 1.0f;
  105. float y = 1.0f - (2.0f * static_cast<float>(screenPt.y()) / h);
  106. bool ok = false;
  107. QMatrix4x4 invVP = (m_camera->getProjectionMatrix() * m_camera->getViewMatrix()).inverted(&ok);
  108. if (!ok) return false;
  109. // Ray from near to far in world space
  110. QVector4D nearClip(x, y, 0.0f, 1.0f);
  111. QVector4D farClip(x, y, 1.0f, 1.0f);
  112. QVector4D nearWorld4 = invVP * nearClip;
  113. QVector4D farWorld4 = invVP * farClip;
  114. if (nearWorld4.w() == 0.0f || farWorld4.w() == 0.0f) return false;
  115. QVector3D rayOrigin = (nearWorld4 / nearWorld4.w()).toVector3D();
  116. QVector3D rayEnd = (farWorld4 / farWorld4.w()).toVector3D();
  117. QVector3D rayDir = (rayEnd - rayOrigin).normalized();
  118. // Intersect with plane y=0
  119. if (qFuzzyIsNull(rayDir.y())) return false; // parallel
  120. float t = -rayOrigin.y() / rayDir.y();
  121. if (t < 0.0f) return false; // behind camera
  122. outWorld = rayOrigin + rayDir * t;
  123. return true;
  124. }
  125. std::unique_ptr<Engine::Core::World> m_world;
  126. std::unique_ptr<Render::GL::Renderer> m_renderer;
  127. std::unique_ptr<Render::GL::Camera> m_camera;
  128. std::unique_ptr<Game::Systems::SelectionSystem> m_selectionSystem;
  129. QQuickWindow* m_window = nullptr;
  130. Engine::Core::EntityID m_playerUnitId = 0;
  131. bool m_initialized = false;
  132. };
  133. int main(int argc, char *argv[])
  134. {
  135. QGuiApplication app(argc, argv);
  136. // Set up OpenGL 3.3 Core Profile
  137. QSurfaceFormat format;
  138. format.setVersion(3, 3);
  139. format.setProfile(QSurfaceFormat::CoreProfile);
  140. format.setDepthBufferSize(24);
  141. format.setStencilBufferSize(8);
  142. format.setSamples(4); // 4x MSAA
  143. QSurfaceFormat::setDefaultFormat(format);
  144. // Set up QML engine
  145. QQmlApplicationEngine engine;
  146. // Register C++ types with QML if needed
  147. // qmlRegisterType<GameEngine>("StandardOfIron", 1, 0, "GameEngine");
  148. // Load QML from the compiled resource path (see generated :/StandardOfIron/ui/qml/*)
  149. engine.load(QUrl(QStringLiteral("qrc:/StandardOfIron/ui/qml/Main.qml")));
  150. if (engine.rootObjects().isEmpty()) {
  151. qWarning() << "Failed to load QML file";
  152. return -1;
  153. }
  154. qDebug() << "Application started successfully";
  155. qDebug() << "Assets directory:" << QDir::currentPath() + "/assets";
  156. // Get the QQuickWindow from the loaded QML
  157. QObject* rootObj = engine.rootObjects().first();
  158. QQuickWindow* window = qobject_cast<QQuickWindow*>(rootObj);
  159. if (!window) {
  160. // Try to find a QQuickWindow child
  161. window = rootObj->findChild<QQuickWindow*>();
  162. }
  163. auto gameEngine = new GameEngine();
  164. // Expose to QML
  165. engine.rootContext()->setContextProperty("game", gameEngine);
  166. if (window) {
  167. gameEngine->setWindow(window);
  168. // Per-frame update/render loop (context is current here)
  169. QObject::connect(window, &QQuickWindow::beforeRendering, gameEngine, [gameEngine]() {
  170. gameEngine->ensureInitialized();
  171. gameEngine->update(1.0f / 60.0f); // Fixed timestep for now
  172. gameEngine->render();
  173. }, Qt::DirectConnection);
  174. // In Qt 6, the default clear before rendering is handled by the scene graph; our renderer also clears per frame.
  175. } else {
  176. qWarning() << "No QQuickWindow found for OpenGL initialization.";
  177. }
  178. return app.exec();
  179. }
  180. #include "main.moc"