Prechádzať zdrojové kódy

Refactor arrow mesh and VFX renderer namespaces and structure for clarity and consistency

djeada 2 mesiacov pred
rodič
commit
bdfdefbed3
8 zmenil súbory, kde vykonal 129 pridanie a 77 odobranie
  1. 13 14
      README.md
  2. 1 1
      app/game_engine.cpp
  3. 9 21
      app/game_engine.h
  4. 3 16
      main.cpp
  5. 1 1
      render/CMakeLists.txt
  6. 75 20
      render/geom/arrow.cpp
  7. 26 3
      render/geom/arrow.h
  8. 1 1
      render/gl/renderer.h

+ 13 - 14
README.md

@@ -43,27 +43,26 @@ A modern real-time strategy (RTS) game engine built with C++20, Qt 6, and OpenGL
 
 
 ## Building
 ## Building
 
 
-### Ubuntu/Debian
-```bash
-# Install dependencies
-sudo apt update
-sudo apt install -y qt6-base-dev qt6-declarative-dev libgl-dev build-essential cmake
+### Linux
 
 
-# Clone and build
+We currently support Ubuntu/Debian and Majaro/Arch.
+
+```bash
+# Clone
 git clone https://github.com/djeada/Standard-of-Iron.git
 git clone https://github.com/djeada/Standard-of-Iron.git
 cd Standard-of-Iron
 cd Standard-of-Iron
-mkdir build && cd build
-cmake ..
-make -j$(nproc)
+
+# Install dependencies
+make install
+
+# Build
+make build
 ```
 ```
 
 
 ### Running
 ### Running
 ```bash
 ```bash
 # Main game
 # Main game
-./standard_of_iron
-
-# Map editor
-./tools/map_editor/map_editor
+make run
 ```
 ```
 
 
 ## Project Structure
 ## Project Structure
@@ -166,4 +165,4 @@ This is an active development project. Current focus areas:
 - [ ] Visual effects system
 - [ ] Visual effects system
 - [ ] Audio integration
 - [ ] Audio integration
 - [ ] Level streaming
 - [ ] Level streaming
-- [ ] Performance optimization
+- [ ] Performance optimization

+ 1 - 1
app/game_engine.cpp

@@ -10,7 +10,7 @@
 #include "render/gl/renderer.h"
 #include "render/gl/renderer.h"
 #include "render/gl/camera.h"
 #include "render/gl/camera.h"
 #include "render/gl/resources.h"
 #include "render/gl/resources.h"
-#include "render/entity/arrow_vfx_renderer.h"
+#include "render/geom/arrow.h"
 #include "render/gl/bootstrap.h"
 #include "render/gl/bootstrap.h"
 #include "game/map/level_loader.h"
 #include "game/map/level_loader.h"
 #include "game/systems/movement_system.h"
 #include "game/systems/movement_system.h"

+ 9 - 21
app/game_engine.h

@@ -43,46 +43,40 @@ public:
     Q_INVOKABLE void onAreaSelected(qreal x1, qreal y1, qreal x2, qreal y2, bool additive = false);
     Q_INVOKABLE void onAreaSelected(qreal x1, qreal y1, qreal x2, qreal y2, bool additive = false);
     Q_INVOKABLE void setHoverAtScreen(qreal sx, qreal sy);
     Q_INVOKABLE void setHoverAtScreen(qreal sx, qreal sy);
 
 
-    // Camera controls exposed to QML
-    Q_INVOKABLE void cameraMove(float dx, float dz);      // move along ground plane (right/forward XZ)
-    Q_INVOKABLE void cameraElevate(float dy);             // move up/down in Y
-    Q_INVOKABLE void cameraYaw(float degrees);            // rotate around current target (yaw)
-    Q_INVOKABLE void cameraOrbit(float yawDeg, float pitchDeg); // orbit around target by yaw/pitch
-    Q_INVOKABLE void cameraFollowSelection(bool enable);  // follow the currently selected troops
-    Q_INVOKABLE void cameraSetFollowLerp(float alpha);    // 0..1, 1 = snap to center
-
-    // Game loop control
+    Q_INVOKABLE void cameraMove(float dx, float dz);
+    Q_INVOKABLE void cameraElevate(float dy);
+    Q_INVOKABLE void cameraYaw(float degrees);
+    Q_INVOKABLE void cameraOrbit(float yawDeg, float pitchDeg);
+    Q_INVOKABLE void cameraFollowSelection(bool enable);
+    Q_INVOKABLE void cameraSetFollowLerp(float alpha);
+
     Q_INVOKABLE void setPaused(bool paused) { m_runtime.paused = paused; }
     Q_INVOKABLE void setPaused(bool paused) { m_runtime.paused = paused; }
     Q_INVOKABLE void setGameSpeed(float speed) { m_runtime.timeScale = std::max(0.0f, speed); }
     Q_INVOKABLE void setGameSpeed(float speed) { m_runtime.timeScale = std::max(0.0f, speed); }
     bool paused() const { return m_runtime.paused; }
     bool paused() const { return m_runtime.paused; }
     float timeScale() const { return m_runtime.timeScale; }
     float timeScale() const { return m_runtime.timeScale; }
 
 
-    // Selection queries and actions (for HUD/production)
     Q_INVOKABLE bool hasSelectedType(const QString& type) const;
     Q_INVOKABLE bool hasSelectedType(const QString& type) const;
     Q_INVOKABLE void recruitNearSelected(const QString& unitType);
     Q_INVOKABLE void recruitNearSelected(const QString& unitType);
-    Q_INVOKABLE QVariantMap getSelectedProductionState() const; // {hasBarracks, inProgress, timeRemaining, buildTime, producedCount, maxUnits}
+    Q_INVOKABLE QVariantMap getSelectedProductionState() const;
     Q_INVOKABLE void setRallyAtScreen(qreal sx, qreal sy);
     Q_INVOKABLE void setRallyAtScreen(qreal sx, qreal sy);
 
 
     void setWindow(QQuickWindow* w) { m_window = w; }
     void setWindow(QQuickWindow* w) { m_window = w; }
 
 
-    // Render-thread friendly calls (must be invoked when a valid GL context is current)
     void ensureInitialized();
     void ensureInitialized();
     void update(float dt);
     void update(float dt);
     void render(int pixelWidth, int pixelHeight);
     void render(int pixelWidth, int pixelHeight);
 
 
-    // Lightweight data accessors for UI/view models (avoid exposing raw pointers)
     void getSelectedUnitIds(std::vector<Engine::Core::EntityID>& out) const;
     void getSelectedUnitIds(std::vector<Engine::Core::EntityID>& out) const;
     bool getUnitInfo(Engine::Core::EntityID id, QString& name, int& health, int& maxHealth,
     bool getUnitInfo(Engine::Core::EntityID id, QString& name, int& health, int& maxHealth,
                      bool& isBuilding, bool& alive) const;
                      bool& isBuilding, bool& alive) const;
 
 
 private:
 private:
-    // Small state groups to keep engine fields tidy
     struct RuntimeState { bool initialized = false; bool paused = false; float timeScale = 1.0f; int localOwnerId = 1; };
     struct RuntimeState { bool initialized = false; bool paused = false; float timeScale = 1.0f; int localOwnerId = 1; };
     struct ViewportState { int width = 0; int height = 0; };
     struct ViewportState { int width = 0; int height = 0; };
     struct LevelState { QString mapName; Engine::Core::EntityID playerUnitId = 0; float camFov = 45.0f; float camNear = 0.1f; float camFar = 1000.0f; };
     struct LevelState { QString mapName; Engine::Core::EntityID playerUnitId = 0; float camFov = 45.0f; float camNear = 0.1f; float camFar = 1000.0f; };
     struct HoverState { Engine::Core::EntityID buildingId = 0; };
     struct HoverState { Engine::Core::EntityID buildingId = 0; };
 
 
-    Game::Systems::ArrowSystem* m_arrowSystem = nullptr; // owned by world
+    Game::Systems::ArrowSystem* m_arrowSystem = nullptr;
     void initialize();
     void initialize();
     bool screenToGround(const QPointF& screenPt, QVector3D& outWorld);
     bool screenToGround(const QPointF& screenPt, QVector3D& outWorld);
     bool worldToScreen(const QVector3D& world, QPointF& outScreen) const;
     bool worldToScreen(const QVector3D& world, QPointF& outScreen) const;
@@ -98,16 +92,10 @@ private:
     QQuickWindow* m_window = nullptr;
     QQuickWindow* m_window = nullptr;
     RuntimeState m_runtime;
     RuntimeState m_runtime;
     ViewportState m_viewport;
     ViewportState m_viewport;
-    // Follow behavior
     bool m_followSelectionEnabled = false;
     bool m_followSelectionEnabled = false;
-    // Visual config placeholder removed; visuals are driven by render registry/services
-    // Level state
     LevelState m_level;
     LevelState m_level;
     QObject* m_selectedUnitsModel = nullptr;
     QObject* m_selectedUnitsModel = nullptr;
-    // Hover state for subtle outline
     HoverState m_hover;
     HoverState m_hover;
-    // Grace window now handled inside PickingService
 signals:
 signals:
     void selectedUnitsChanged();
     void selectedUnitsChanged();
-
 };
 };

+ 3 - 16
main.cpp

@@ -1,4 +1,3 @@
-// System/Qt headers
 #include <QGuiApplication>
 #include <QGuiApplication>
 #include <QQmlApplicationEngine>
 #include <QQmlApplicationEngine>
 #include <QOpenGLContext>
 #include <QOpenGLContext>
@@ -9,21 +8,18 @@
 #include <QQmlContext>
 #include <QQmlContext>
 #include <QSGRendererInterface>
 #include <QSGRendererInterface>
 
 
-// App headers
 #include "app/game_engine.h"
 #include "app/game_engine.h"
 #include "ui/gl_view.h"
 #include "ui/gl_view.h"
 
 
 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")) {
     if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY") && qEnvironmentVariableIsSet("DISPLAY")) {
-        qputenv("QT_QPA_PLATFORM", "xcb");   // prefer XCB/GLX over Wayland/EGL when XWayland is present
+        qputenv("QT_QPA_PLATFORM", "xcb");
     }
     }
-    qputenv("QT_OPENGL", "desktop");         // desktop GL, not GLES/EGL
-    qputenv("QSG_RHI_BACKEND", "opengl");    // OpenGL RHI
+    qputenv("QT_OPENGL", "desktop");
+    qputenv("QSG_RHI_BACKEND", "opengl");
     QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
     QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
 
 
-    // Request desktop GL 3.3 core.
     QSurfaceFormat fmt;
     QSurfaceFormat fmt;
     fmt.setVersion(3, 3);
     fmt.setVersion(3, 3);
     fmt.setProfile(QSurfaceFormat::CoreProfile);
     fmt.setProfile(QSurfaceFormat::CoreProfile);
@@ -37,9 +33,7 @@ int main(int argc, char *argv[])
     auto gameEngine = new GameEngine();
     auto gameEngine = new GameEngine();
 
 
     QQmlApplicationEngine engine;
     QQmlApplicationEngine engine;
-    // Expose to QML BEFORE loading (safer if QML binds early).
     engine.rootContext()->setContextProperty("game", gameEngine);
     engine.rootContext()->setContextProperty("game", gameEngine);
-    // Register our GLView item so QML can embed the GL scene inside the scene graph
     qmlRegisterType<GLView>("StandardOfIron", 1, 0, "GLView");
     qmlRegisterType<GLView>("StandardOfIron", 1, 0, "GLView");
     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()) {
@@ -55,11 +49,8 @@ int main(int argc, char *argv[])
         return -2;
         return -2;
     }
     }
 
 
-    // Let Qt Quick manage the clear; keep window color default.
-
     gameEngine->setWindow(window);
     gameEngine->setWindow(window);
 
 
-    // Informative logging (no current-context check here).
     QObject::connect(window, &QQuickWindow::sceneGraphInitialized, window, [window]() {
     QObject::connect(window, &QQuickWindow::sceneGraphInitialized, window, [window]() {
         if (auto *ri = window->rendererInterface()) {
         if (auto *ri = window->rendererInterface()) {
             auto api = ri->graphicsApi();
             auto api = ri->graphicsApi();
@@ -78,12 +69,8 @@ int main(int argc, char *argv[])
         app.exit(3);
         app.exit(3);
     });
     });
 
 
-
     qDebug() << "Application started successfully";
     qDebug() << "Application started successfully";
     qDebug() << "Assets directory:" << QDir::currentPath() + "/assets";
     qDebug() << "Assets directory:" << QDir::currentPath() + "/assets";
 
 
     return app.exec();
     return app.exec();
 }
 }
-
-// no Q_OBJECT in this TU anymore
-

+ 1 - 1
render/CMakeLists.txt

@@ -10,7 +10,7 @@ add_library(render_gl STATIC
     entity/registry.cpp
     entity/registry.cpp
     entity/archer_renderer.cpp
     entity/archer_renderer.cpp
     entity/barracks_renderer.cpp
     entity/barracks_renderer.cpp
-    entity/arrow_vfx_renderer.cpp
+    # entity/arrow.cpp removed; arrow VFX renderer code moved to geom/arrow.cpp
     geom/selection_ring.cpp
     geom/selection_ring.cpp
     geom/arrow.cpp
     geom/arrow.cpp
 )
 )

+ 75 - 20
render/geom/arrow.cpp

@@ -1,23 +1,32 @@
 #include "arrow.h"
 #include "arrow.h"
 #include "../gl/mesh.h"
 #include "../gl/mesh.h"
+#include "../entity/registry.h"
+#include "../gl/renderer.h"
+#include "../gl/resources.h"
+#include "../../game/systems/arrow_system.h"
+
 #include <vector>
 #include <vector>
 #include <cmath>
 #include <cmath>
+#include <algorithm>
 #include <QVector3D>
 #include <QVector3D>
+#include <QMatrix4x4>
+
 
 
-namespace Render::Geom {
+namespace Render {
+namespace Geom {
 
 
-static Render::GL::Mesh* createArrowMesh() {
-    using Render::GL::Vertex;
-    std::vector<Vertex> verts;
+static GL::Mesh* createArrowMesh() {
+    using GL::Vertex;
+    std::vector<GL::Vertex> verts;
     std::vector<unsigned int> idx;
     std::vector<unsigned int> idx;
-    // Parameters for a simple arrow of length 1 along +Z
+
     const int radial = 12;
     const int radial = 12;
     const float shaftRadius = 0.05f;
     const float shaftRadius = 0.05f;
-    const float shaftLen = 0.85f;   // 85% shaft, 15% tip
+    const float shaftLen = 0.85f;
     const float tipLen = 0.15f;
     const float tipLen = 0.15f;
     const float tipStartZ = shaftLen;
     const float tipStartZ = shaftLen;
-    const float tipEndZ = shaftLen + tipLen; // should be 1.0
-    // Shaft cylinder: two rings at z=0 and z=shaftLen
+    const float tipEndZ = shaftLen + tipLen;
+
     int baseIndex = 0;
     int baseIndex = 0;
     for (int ring = 0; ring < 2; ++ring) {
     for (int ring = 0; ring < 2; ++ring) {
         float z = (ring == 0) ? 0.0f : shaftLen;
         float z = (ring == 0) ? 0.0f : shaftLen;
@@ -30,7 +39,7 @@ static Render::GL::Mesh* createArrowMesh() {
             verts.push_back({{x, y, z}, {n.x(), n.y(), n.z()}, {float(i)/radial, z}});
             verts.push_back({{x, y, z}, {n.x(), n.y(), n.z()}, {float(i)/radial, z}});
         }
         }
     }
     }
-    // Shaft indices (quads split into triangles)
+
     for (int i = 0; i < radial; ++i) {
     for (int i = 0; i < radial; ++i) {
         int next = (i + 1) % radial;
         int next = (i + 1) % radial;
         int a = baseIndex + i;
         int a = baseIndex + i;
@@ -40,31 +49,77 @@ static Render::GL::Mesh* createArrowMesh() {
         idx.push_back(a); idx.push_back(b); idx.push_back(c);
         idx.push_back(a); idx.push_back(b); idx.push_back(c);
         idx.push_back(c); idx.push_back(d); idx.push_back(a);
         idx.push_back(c); idx.push_back(d); idx.push_back(a);
     }
     }
-    // Tip cone: triangle fan from tip apex to ring at tipStartZ
+
     int ringStart = verts.size();
     int ringStart = verts.size();
     for (int i = 0; i < radial; ++i) {
     for (int i = 0; i < radial; ++i) {
         float a = (float(i) / radial) * 6.2831853f;
         float a = (float(i) / radial) * 6.2831853f;
-    float x = std::cos(a) * shaftRadius * 1.4f; // slightly larger base for tip
-    float y = std::sin(a) * shaftRadius * 1.4f;
+        float x = std::cos(a) * shaftRadius * 1.4f;
+        float y = std::sin(a) * shaftRadius * 1.4f;
         QVector3D n(x, y, 0.2f);
         QVector3D n(x, y, 0.2f);
         n.normalize();
         n.normalize();
         verts.push_back({{x, y, tipStartZ}, {n.x(), n.y(), n.z()}, {float(i)/radial, 0.0f}});
         verts.push_back({{x, y, tipStartZ}, {n.x(), n.y(), n.z()}, {float(i)/radial, 0.0f}});
     }
     }
+
     int apexIndex = verts.size();
     int apexIndex = verts.size();
     verts.push_back({{0.0f, 0.0f, tipEndZ}, {0.0f, 0.0f, 1.0f}, {0.5f, 1.0f}});
     verts.push_back({{0.0f, 0.0f, tipEndZ}, {0.0f, 0.0f, 1.0f}, {0.5f, 1.0f}});
     for (int i = 0; i < radial; ++i) {
     for (int i = 0; i < radial; ++i) {
         int next = (i + 1) % radial;
         int next = (i + 1) % radial;
-        int a = ringStart + i;
-        int b = ringStart + next;
-        int apex = apexIndex;
-        idx.push_back(a); idx.push_back(apex); idx.push_back(b);
+        idx.push_back(ringStart + i);
+        idx.push_back(apexIndex);
+        idx.push_back(ringStart + next);
     }
     }
-    return new Render::GL::Mesh(verts, idx);
+
+    return new GL::Mesh(verts, idx);
 }
 }
 
 
-Render::GL::Mesh* Arrow::get() {
-    static Render::GL::Mesh* mesh = createArrowMesh();
+GL::Mesh* Arrow::get() {
+    static GL::Mesh* mesh = createArrowMesh();
     return mesh;
     return mesh;
 }
 }
 
 
-} // namespace Render::Geom
+} // namespace Geom
+
+namespace GL {
+
+void renderArrows(Renderer* renderer,
+                  ResourceManager* resources,
+                  const Game::Systems::ArrowSystem& arrowSystem) {
+    if (!renderer || !resources) return;
+    auto* arrowMesh = resources->arrow();
+    if (!arrowMesh) return;
+
+    const auto& arrows = arrowSystem.arrows();
+    for (const auto& arrow : arrows) {
+        if (!arrow.active) continue;
+
+        const QVector3D delta = arrow.end - arrow.start;
+        const float dist = std::max(0.001f, delta.length());
+        QVector3D pos = arrow.start + delta * arrow.t;
+        float h = arrow.arcHeight * 4.0f * arrow.t * (1.0f - arrow.t);
+        pos.setY(pos.y() + h);
+
+        QMatrix4x4 model;
+        model.translate(pos.x(), pos.y(), pos.z());
+
+        QVector3D dir = delta.normalized();
+        float yawDeg = std::atan2(dir.x(), dir.z()) * 180.0f / 3.14159265f;
+        model.rotate(yawDeg, QVector3D(0,1,0));
+
+        float vy = (arrow.end.y() - arrow.start.y()) / dist;
+        float pitchDeg = -std::atan2(
+            vy - (8.0f * arrow.arcHeight * (arrow.t - 0.5f) / dist),
+            1.0f
+        ) * 180.0f / 3.14159265f;
+        model.rotate(pitchDeg, QVector3D(1,0,0));
+
+        const float zScale = 0.40f;
+        const float xyScale = 0.26f;
+        model.translate(0.0f, 0.0f, -zScale * 0.5f);
+        model.scale(xyScale, xyScale, zScale);
+
+        renderer->drawMeshColored(arrowMesh, model, arrow.color);
+    }
+}
+
+} // namespace GL
+} // namespace Render

+ 26 - 3
render/geom/arrow.h

@@ -1,10 +1,33 @@
 
 
 #pragma once
 #pragma once
 #include "../gl/mesh.h"
 #include "../gl/mesh.h"
+#include <QMatrix4x4>
+#include <QVector3D>
 
 
-namespace Render::Geom {
+namespace Render {
+namespace Geom {
 class Arrow {
 class Arrow {
 public:
 public:
-    static Render::GL::Mesh* get();
+    static GL::Mesh* get();
 };
 };
-} // namespace Render::Geom
+} // namespace Geom
+
+namespace GL {
+class Renderer;
+class ResourceManager;
+} // namespace GL
+} // namespace Render
+
+namespace Game {
+namespace Systems {
+class ArrowSystem;
+} // namespace Systems
+} // namespace Game
+
+namespace Render {
+namespace GL {
+void renderArrows(Renderer* renderer,
+                  ResourceManager* resources,
+                  const Game::Systems::ArrowSystem& arrowSystem);
+} // namespace GL
+} // namespace Render

+ 1 - 1
render/gl/renderer.h

@@ -56,7 +56,7 @@ public:
 
 
     // Lightweight, app-facing helpers
     // Lightweight, app-facing helpers
     void renderGridGround();
     void renderGridGround();
-    // High-level helpers are provided in render/entity (see arrow_vfx_renderer)
+    // High-level helpers are provided in render/entity (see arrow)
 
 
     // Read-only access to default meshes/textures for app-side batching
     // Read-only access to default meshes/textures for app-side batching
     Mesh* getMeshQuad()    const { return m_resources ? m_resources->quad()    : nullptr; }
     Mesh* getMeshQuad()    const { return m_resources ? m_resources->quad()    : nullptr; }