1 #include "game_view.h" |
1 #include "game_view.h" |
2 |
2 |
3 #include <QtQuick/qquickwindow.h> |
3 #include <QtQuick/qquickwindow.h> |
|
4 |
4 #include <QCursor> |
5 #include <QCursor> |
|
6 #include <QOpenGLFramebufferObjectFormat> |
|
7 #include <QQuickOpenGLUtils> |
5 #include <QTimer> |
8 #include <QTimer> |
6 #include <QtGui/QOpenGLContext> |
9 #include <QtGui/QOpenGLContext> |
7 #include <QtGui/QOpenGLShaderProgram> |
|
8 |
10 |
9 GameView::GameView(QQuickItem* parent) |
11 class GameViewRenderer : public QQuickFramebufferObject::Renderer { |
10 : QQuickItem(parent), m_delta(0), m_windowChanged(true) { |
12 public: |
11 connect(this, &QQuickItem::windowChanged, this, |
13 explicit GameViewRenderer() = default; |
12 &GameView::handleWindowChanged); |
14 |
|
15 GameViewRenderer(const GameViewRenderer&) = delete; |
|
16 GameViewRenderer(GameViewRenderer&&) = delete; |
|
17 GameViewRenderer& operator=(const GameViewRenderer&) = delete; |
|
18 GameViewRenderer& operator=(GameViewRenderer&&) = delete; |
|
19 |
|
20 void render() override; |
|
21 QOpenGLFramebufferObject* createFramebufferObject(const QSize& size) override; |
|
22 void synchronize(QQuickFramebufferObject* fbo) override; |
|
23 |
|
24 QPointer<GameView> m_gameView; |
|
25 QPointer<QQuickWindow> m_window; |
|
26 bool m_dirty{true}; |
|
27 QSizeF m_gameViewSize; |
|
28 }; |
|
29 |
|
30 void GameViewRenderer::render() { |
|
31 const auto engine = m_gameView->engineInstance(); |
|
32 |
|
33 if (!engine) { |
|
34 return; |
|
35 } |
|
36 |
|
37 if (m_dirty) { |
|
38 m_dirty = false; |
|
39 engine->setOpenGLContext(QOpenGLContext::currentContext()); |
|
40 } |
|
41 |
|
42 engine->renderFrame(); |
|
43 |
|
44 QQuickOpenGLUtils::resetOpenGLState(); |
|
45 } |
|
46 |
|
47 QOpenGLFramebufferObject* GameViewRenderer::createFramebufferObject( |
|
48 const QSize& size) { |
|
49 QOpenGLFramebufferObjectFormat format; |
|
50 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); |
|
51 format.setSamples(8); |
|
52 auto fbo = new QOpenGLFramebufferObject(size, format); |
|
53 return fbo; |
|
54 } |
|
55 |
|
56 void GameViewRenderer::synchronize(QQuickFramebufferObject* fbo) { |
|
57 if (!m_gameView) { |
|
58 m_gameView = qobject_cast<GameView*>(fbo); |
|
59 m_window = fbo->window(); |
|
60 } |
|
61 |
|
62 if (const auto currentSize = m_gameView->size(); |
|
63 currentSize != m_gameViewSize) { |
|
64 m_gameViewSize = currentSize; |
|
65 m_dirty = true; |
|
66 } |
|
67 |
|
68 m_gameView->executeActions(); |
|
69 } |
|
70 |
|
71 GameView::GameView(QQuickItem* parent) : QQuickFramebufferObject(parent) { |
|
72 setMirrorVertically(true); |
13 } |
73 } |
14 |
74 |
15 void GameView::tick(quint32 delta) { |
75 void GameView::tick(quint32 delta) { |
16 m_delta = delta; |
76 addAction([delta](auto engine) { engine->advance(delta); }); |
17 |
|
18 if (window()) { |
|
19 QTimer* timer = new QTimer(this); |
|
20 connect(timer, &QTimer::timeout, window(), &QQuickWindow::update); |
|
21 timer->start(100); |
|
22 |
|
23 // window()->update(); |
|
24 } |
|
25 } |
77 } |
26 |
78 |
27 EngineInstance* GameView::engineInstance() const { return m_engineInstance; } |
79 EngineInstance* GameView::engineInstance() const { return m_engineInstance; } |
28 |
80 |
29 void GameView::handleWindowChanged(QQuickWindow* win) { |
81 QQuickFramebufferObject::Renderer* GameView::createRenderer() const { |
30 if (win) { |
82 return new GameViewRenderer{}; |
31 connect(win, &QQuickWindow::beforeSynchronizing, this, &GameView::sync, |
|
32 Qt::DirectConnection); |
|
33 connect(win, &QQuickWindow::sceneGraphInvalidated, this, &GameView::cleanup, |
|
34 Qt::DirectConnection); |
|
35 |
|
36 win->setClearBeforeRendering(false); |
|
37 |
|
38 m_windowChanged = true; |
|
39 } |
|
40 } |
83 } |
41 |
84 |
42 void GameView::cleanup() { m_renderer.reset(); } |
85 void GameView::executeActions() { |
|
86 if (!m_engineInstance) { |
|
87 return; |
|
88 } |
|
89 |
|
90 for (const auto& action : m_actions) { |
|
91 action(m_engineInstance); |
|
92 } |
|
93 |
|
94 m_actions.clear(); |
|
95 } |
43 |
96 |
44 void GameView::setEngineInstance(EngineInstance* engineInstance) { |
97 void GameView::setEngineInstance(EngineInstance* engineInstance) { |
45 if (m_engineInstance == engineInstance) { |
98 if (m_engineInstance == engineInstance) { |
46 return; |
99 return; |
47 } |
100 } |
48 |
101 |
49 cleanup(); |
|
50 m_engineInstance = engineInstance; |
102 m_engineInstance = engineInstance; |
51 |
103 |
52 emit engineInstanceChanged(m_engineInstance); |
104 Q_EMIT engineInstanceChanged(m_engineInstance); |
53 } |
105 } |
54 |
106 |
55 void GameView::sync() { |
107 void GameView::addAction(std::function<void(EngineInstance*)>&& action) { |
56 if (!m_renderer && m_engineInstance) { |
108 m_actions.append(std::move(action)); |
57 m_engineInstance->setOpenGLContext(window()->openglContext()); |
|
58 m_renderer.reset(new GameViewRenderer()); |
|
59 m_renderer->setEngineInstance(m_engineInstance); |
|
60 connect(window(), &QQuickWindow::beforeRendering, m_renderer.data(), |
|
61 &GameViewRenderer::paint, Qt::DirectConnection); |
|
62 } |
|
63 |
|
64 if (m_windowChanged || (m_viewportSize != size())) { |
|
65 m_windowChanged = false; |
|
66 |
|
67 if (m_engineInstance) |
|
68 m_engineInstance->setOpenGLContext(window()->openglContext()); |
|
69 |
|
70 m_viewportSize = size().toSize(); |
|
71 m_centerPoint = QPoint(m_viewportSize.width(), m_viewportSize.height()) / 2; |
|
72 } |
|
73 |
|
74 if (m_engineInstance) { |
|
75 const auto delta = mapFromGlobal(QCursor::pos()).toPoint() - m_centerPoint; |
|
76 |
|
77 m_engineInstance->moveCamera(delta); |
|
78 |
|
79 QCursor::setPos(window()->screen(), mapToGlobal(m_centerPoint).toPoint()); |
|
80 } |
|
81 |
|
82 if (m_renderer) { |
|
83 m_renderer->tick(m_delta); |
|
84 } |
|
85 } |
109 } |
86 |
|
87 GameViewRenderer::GameViewRenderer() |
|
88 : QObject(), m_delta(0), m_engineInstance(nullptr) {} |
|
89 |
|
90 GameViewRenderer::~GameViewRenderer() {} |
|
91 |
|
92 void GameViewRenderer::tick(quint32 delta) { m_delta = delta; } |
|
93 |
|
94 void GameViewRenderer::setEngineInstance(EngineInstance* engineInstance) { |
|
95 m_engineInstance = engineInstance; |
|
96 } |
|
97 |
|
98 void GameViewRenderer::paint() { |
|
99 if (m_delta == 0) { |
|
100 return; |
|
101 } |
|
102 |
|
103 if (m_engineInstance) { |
|
104 m_engineInstance->advance(m_delta); |
|
105 m_engineInstance->renderFrame(); |
|
106 } |
|
107 |
|
108 // m_window->resetOpenGLState(); |
|
109 } |
|
110 |
|
111 void GameViewRenderer::onViewportSizeChanged(QQuickWindow* window) { |
|
112 if (m_engineInstance) |
|
113 m_engineInstance->setOpenGLContext(window->openglContext()); |
|
114 } |
|