Browse Source

gameloop, better errors, glew and glfw init moved to window, buttons
work for press and release on the same frame

Kajetan Johannes Hammerle 3 years ago
parent
commit
f771b8a040
15 changed files with 270 additions and 197 deletions
  1. 25 0
      Main.cpp
  2. 13 8
      input/Button.cpp
  3. 4 1
      input/Button.h
  4. 51 51
      input/Buttons.cpp
  5. 22 24
      input/Buttons.h
  6. 2 3
      meson.build
  7. 74 45
      rendering/Window.cpp
  8. 39 12
      rendering/Window.h
  9. 9 0
      utils/Error.cpp
  10. 13 0
      utils/Error.h
  11. 18 8
      utils/StringBuffer.h
  12. 0 14
      wrapper/GLEW.cpp
  13. 0 8
      wrapper/GLEW.h
  14. 0 15
      wrapper/GLFW.cpp
  15. 0 8
      wrapper/GLFW.h

+ 25 - 0
Main.cpp

@@ -2,6 +2,7 @@
 
 #include "rendering/FileTexture.h"
 #include "rendering/Framebuffer.h"
+#include "rendering/Window.h"
 #include "tests/ArrayListTests.h"
 #include "tests/ArrayTests.h"
 #include "tests/BitArrayTests.h"
@@ -56,5 +57,29 @@ int main(int argAmount, char** args) {
     TypedBufferTests::test();
     UniquePointerTests::test();
     NetworkTests::test();
+
+    struct Game {
+        bool isRunning() {
+            return true;
+        }
+
+        void tick() {
+        }
+
+        void render(float) {
+        }
+    };
+
+    TextInput* input = nullptr;
+    Size size(800, 480);
+    WindowOptions options(4, 3, size, false, "Test");
+    Window w(input, options);
+    if(w.getError().has()) {
+        std::cout << w.getError().message << "\n";
+        return 0;
+    }
+    Game game;
+    w.run(game, 10'000'000);
+
     return 0;
 }

+ 13 - 8
input/Button.cpp

@@ -1,17 +1,22 @@
 #include "input/Button.h"
 
-Button::Button(int key, const char* name) : key(key), downTime(0), released(false), name(name) {
+Button::Button(int key, const char* name)
+    : key(key), downTime(0), keyboardUp(0), keyboardDown(0),
+      controllerDown(false), released(false), name(name) {
 }
 
-void Button::tick(bool down) {
-    if(down) {
-        downTime++;
-    } else if(released) {
-        released = false;
+void Button::tick() {
+    bool down = (keyboardDown > 0) || controllerDown;
+    bool up = (keyboardUp == keyboardDown) && !controllerDown;
+
+    if(released) {
         downTime = 0;
-    } else if(!released && downTime > 0) {
-        released = true;
     }
+    downTime += down;
+    released = down && up;
+
+    keyboardDown -= keyboardUp;
+    keyboardUp = 0;
 }
 
 bool Button::isDown() const {

+ 4 - 1
input/Button.h

@@ -6,6 +6,9 @@ class Button final {
 
     int key;
     int downTime;
+    int keyboardUp;
+    int keyboardDown;
+    bool controllerDown;
     bool released;
     const char* name;
 
@@ -18,7 +21,7 @@ public:
     const char* getName() const;
 
 private:
-    void tick(bool down);
+    void tick();
 };
 
 #endif

+ 51 - 51
input/Buttons.cpp

@@ -1,64 +1,48 @@
 #include "input/Buttons.h"
 
 Buttons::Axis::Axis()
-    : less(0.0f), greater(0.0f), lessIndex(-1), greaterIndex(-1) {
+    : less(0.0f), greater(0.0f), lessButton(nullptr), greaterButton(nullptr) {
 }
 
-Buttons::Buttons(const Window& window)
-    : window(window), dummy(0, "Dummy"), activeController(-1),
-      gamepadToButton(-1) {
+Buttons::Buttons()
+    : lastMouseX(0), lastMouseY(0), mouseX(0), mouseY(0), activeController(-1),
+      gamepadToButton(nullptr) {
 }
 
-Button& Buttons::add(int key, const char* name) {
-    if(buttons.add(key, name)) {
-        return dummy;
-    }
-    return buttons[buttons.getLength() - 1];
+void Buttons::add(Button& button) {
+    buttons.add(button.key, &button);
 }
 
-Button& Buttons::addMouse(int mouse, const char* name) {
-    if(mouseButtons.add(mouse, name)) {
-        return dummy;
-    }
-    return mouseButtons[mouseButtons.getLength() - 1];
+void Buttons::addMouse(Button& button) {
+    mouseButtons.add(button.key, &button);
 }
 
-void Buttons::mapGamepadButton(const Button& button, int mapping) {
-    gamepadToButton[mapping] = searchButton(button);
+void Buttons::mapGamepadButton(Button& button, int mapping) {
+    gamepadToButton[mapping] = &button;
 }
 
-void Buttons::mapGamepadAxis(const Button& button, float value, int index) {
+void Buttons::mapGamepadAxis(Button& button, float value, int index) {
     if(value > 0.0f) {
         gamepadAxisToButton[index].greater = value;
-        gamepadAxisToButton[index].greaterIndex = searchButton(button);
+        gamepadAxisToButton[index].greaterButton = &button;
     } else {
         gamepadAxisToButton[index].less = value;
-        gamepadAxisToButton[index].lessIndex = searchButton(button);
+        gamepadAxisToButton[index].lessButton = &button;
     }
 }
 
 void Buttons::tick() {
-    DownArray down(false);
     if(searchForGamepad()) {
-        checkGamepad(down);
+        checkGamepad();
     }
-    for(int i = 0; i < buttons.getLength(); i++) {
-        buttons[i].tick(window.isKeyDown(buttons[i].key) || down[i]);
+    for(auto& e : buttons) {
+        e.value->tick();
     }
-    for(int i = 0; i < mouseButtons.getLength(); i++) {
-        mouseButtons[i].tick(window.isMouseDown(mouseButtons[i].key));
+    for(auto& e : mouseButtons) {
+        e.value->tick();
     }
     lastMouseX = mouseX;
     lastMouseY = mouseY;
-    window.getMousePosition(mouseX, mouseY);
-}
-
-const ButtonList& Buttons::get() const {
-    return buttons;
-}
-
-const MouseList& Buttons::getMouse() const {
-    return mouseButtons;
 }
 
 double Buttons::getLastMouseX() const {
@@ -77,6 +61,30 @@ double Buttons::getMouseY() const {
     return mouseY;
 }
 
+void Buttons::onButton(HashMap<int, Button*>& map, int key, int action) {
+    Button** b = map.search(key);
+    if(b != nullptr) {
+        if(action == GLFW_RELEASE) {
+            (*b)->keyboardUp++;
+        } else if(action == GLFW_PRESS) {
+            (*b)->keyboardDown++;
+        }
+    }
+}
+
+void Buttons::onKey(int key, int, int action, int) {
+    onButton(buttons, key, action);
+}
+
+void Buttons::onMouse(int button, int action, int) {
+    onButton(mouseButtons, button, action);
+}
+
+void Buttons::onMouseMove(double x, double y) {
+    mouseX = x;
+    mouseY = y;
+}
+
 bool Buttons::searchForGamepad() {
     if(activeController != -1) {
         return true;
@@ -90,32 +98,24 @@ bool Buttons::searchForGamepad() {
     return false;
 }
 
-void Buttons::checkGamepad(DownArray& down) {
+void Buttons::checkGamepad() {
     GLFWgamepadstate state;
     if(!glfwGetGamepadState(activeController, &state)) {
         return;
     }
     for(int i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) {
-        if(gamepadToButton[i] != -1 && state.buttons[i]) {
-            down[gamepadToButton[i]] = true;
+        if(gamepadToButton[i] != nullptr) {
+            gamepadToButton[i]->controllerDown = state.buttons[i];
         }
     }
     for(int i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) {
-        if(gamepadAxisToButton[i].greaterIndex != -1 &&
-           state.axes[i] > gamepadAxisToButton[i].greater) {
-            down[gamepadAxisToButton[i].greaterIndex] = true;
-        } else if(gamepadAxisToButton[i].lessIndex != -1 &&
-                  state.axes[i] < gamepadAxisToButton[i].less) {
-            down[gamepadAxisToButton[i].lessIndex] = true;
+        if(gamepadAxisToButton[i].greaterButton != nullptr) {
+            gamepadAxisToButton[i].greaterButton->controllerDown =
+                (state.axes[i] > gamepadAxisToButton[i].greater);
         }
-    }
-}
-
-int Buttons::searchButton(const Button& button) const {
-    for(int i = 0; i < buttons.getLength(); i++) {
-        if(&button == &(buttons[i])) {
-            return i;
+        if(gamepadAxisToButton[i].lessButton != nullptr) {
+            gamepadAxisToButton[i].lessButton->controllerDown =
+                (state.axes[i] < gamepadAxisToButton[i].less);
         }
     }
-    return -1;
 }

+ 22 - 24
input/Buttons.h

@@ -1,61 +1,59 @@
 #ifndef BUTTONS_H
 #define BUTTONS_H
 
+#include <GL/glew.h>
+#include <GLFW/glfw3.h>
+
 #include "input/Button.h"
-#include "rendering/Window.h"
 #include "utils/Array.h"
 #include "utils/ArrayList.h"
-
-typedef ArrayList<Button, 32> ButtonList;
-typedef ArrayList<Button, 1 + GLFW_MOUSE_BUTTON_LAST> MouseList;
+#include "utils/HashMap.h"
 
 class Buttons final {
-    typedef Array<bool, 32> DownArray;
+    friend class Window;
 
-    const Window& window;
-    Button dummy;
-    int activeController;
-    ButtonList buttons;
-    MouseList mouseButtons;
+    HashMap<int, Button*> buttons;
+    HashMap<int, Button*> mouseButtons;
     double lastMouseX;
     double lastMouseY;
     double mouseX;
     double mouseY;
 
+    int activeController;
     struct Axis {
         float less;
         float greater;
-        int lessIndex;
-        int greaterIndex;
+        Button* lessButton;
+        Button* greaterButton;
 
         Axis();
     };
-
     Array<Axis, 1 + GLFW_GAMEPAD_AXIS_LAST> gamepadAxisToButton;
-    Array<int, 1 + GLFW_GAMEPAD_BUTTON_LAST> gamepadToButton;
+    Array<Button*, 1 + GLFW_GAMEPAD_BUTTON_LAST> gamepadToButton;
 
 public:
-    Buttons(const Window& window);
+    Buttons();
 
-    Button& add(int key, const char* name);
-    Button& addMouse(int mouse, const char* name);
-    void mapGamepadButton(const Button& button, int mapping);
-    void mapGamepadAxis(const Button& button, float value, int index);
+    void add(Button& button);
+    void addMouse(Button& button);
+    void mapGamepadButton(Button& button, int mapping);
+    void mapGamepadAxis(Button& button, float value, int index);
 
     void tick();
 
-    const ButtonList& get() const;
-    const MouseList& getMouse() const;
-
     double getLastMouseX() const;
     double getLastMouseY() const;
     double getMouseX() const;
     double getMouseY() const;
 
 private:
+    void onButton(HashMap<int, Button*>& map, int key, int action);
+    void onKey(int key, int scancode, int action, int mods);
+    void onMouse(int button, int action, int mods);
+    void onMouseMove(double x, double y);
+
     bool searchForGamepad();
-    void checkGamepad(DownArray& down);
-    int searchButton(const Button& button) const;
+    void checkGamepad();
 };
 
 #endif

+ 2 - 3
meson.build

@@ -36,8 +36,6 @@ sources = ['Main.cpp',
     'tests/BufferTests.cpp',
     'tests/TypedBufferTests.cpp',
     'wrapper/GL.cpp',
-    'wrapper/GLFW.cpp',
-    'wrapper/GLEW.cpp', 
     'rendering/Texture.cpp',
     'rendering/Shader.cpp', 
     'rendering/VertexBuffer.cpp',
@@ -55,7 +53,8 @@ sources = ['Main.cpp',
     'network/Server.cpp',
     'network/Client.cpp',
     'network/ENet.cpp',
-    'input/TextInput.cpp']
+    'input/TextInput.cpp',
+    'utils/Error.cpp']
 
 threadDep = dependency('threads')
 glewDep = dependency('glew')

+ 74 - 45
rendering/Window.cpp

@@ -1,14 +1,21 @@
-#include <iostream>
-
 #include "rendering/Window.h"
 
+int Window::glfwCounter = 0;
+int Window::glewCounter = 0;
+
 Window::Window(TextInput*& textInput, const WindowOptions& options)
-    : textInput(textInput), window(nullptr) {
+    : textInput(textInput), window(nullptr), size(options.size) {
+    if(glfwCounter == 0 && !glfwInit()) {
+        error.message.clear().append("could not initialize GLFW");
+        return;
+    }
+    glfwCounter++;
+
     glfwDefaultWindowHints();
-    glfwWindowHint(GLFW_VISIBLE, 0);
-    glfwWindowHint(GLFW_RESIZABLE, 1);
+    glfwWindowHint(GLFW_VISIBLE, false);
+    glfwWindowHint(GLFW_RESIZABLE, true);
     glfwWindowHint(GLFW_DECORATED, !options.fullscreen);
-    glfwWindowHint(GLFW_DOUBLEBUFFER, 1);
+    glfwWindowHint(GLFW_DOUBLEBUFFER, true);
 
     if(options.es) {
         glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
@@ -23,79 +30,78 @@ Window::Window(TextInput*& textInput, const WindowOptions& options)
     window = glfwCreateWindow(options.size.width, options.size.height,
                               options.name, monitor, nullptr);
     if(window == nullptr) {
-        std::cout << "could not create window\n";
+        error.message.clear().append("could not create window");
         return;
     }
     glfwSetWindowUserPointer(window, this);
-    glfwSetKeyCallback(window, keyCallback);
-    glfwSetCharCallback(window, charCallback);
+    glfwSetKeyCallback(window, onKey);
+    glfwSetCharCallback(window, onChar);
+    glfwSetFramebufferSizeCallback(window, onResize);
+    glfwSetMouseButtonCallback(window, onMouse);
+    glfwSetCursorPosCallback(window, onMouseMove);
 
     glfwMakeContextCurrent(window);
     glfwSwapInterval(options.vsync);
+
+    if(glewCounter == 0) {
+        GLenum err = glewInit();
+        if(err != GLEW_OK) {
+            error.message.clear()
+                .append("could not initialize GLEW: ")
+                .append(glewGetErrorString(err));
+            return;
+        }
+    }
+    glewCounter++;
 }
 
 Window::~Window() {
     if(window != nullptr) {
         glfwDestroyWindow(window);
     }
+    glfwCounter--;
+    if(glfwCounter <= 0) {
+        glfwTerminate();
+    }
 }
 
-bool Window::hasError() const {
-    return window == nullptr;
-}
-
-void Window::show() {
-    glfwShowWindow(window);
-}
-
-bool Window::shouldClose() const {
-    return glfwWindowShouldClose(window);
-}
-
-void Window::swapBuffers() {
-    glfwSwapBuffers(window);
+const Error& Window::getError() const {
+    return error;
 }
 
-void Window::trapCursor(bool trap) {
-    glfwSetInputMode(window, GLFW_CURSOR,
-                     trap ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
+const Clock& Window::getFrameClock() const {
+    return fps;
 }
 
-Size Window::getSize() const {
-    Size size(0, 0);
-    glfwGetWindowSize(window, &size.width, &size.height);
-    return size;
+const Clock& Window::getTickClock() const {
+    return tps;
 }
 
-Size Window::getFramebufferSize() const {
-    Size size(0, 0);
-    glfwGetFramebufferSize(window, &size.width, &size.height);
+const Size& Window::getSize() const {
     return size;
 }
 
-bool Window::isKeyDown(int key) const {
-    return glfwGetKey(window, key) == GLFW_PRESS;
-}
-
-bool Window::isMouseDown(int mouse) const {
-    return glfwGetMouseButton(window, mouse) == GLFW_PRESS;
+Buttons& Window::getButtons() {
+    return buttons;
 }
 
-void Window::getMousePosition(double& x, double& y) const {
-    glfwGetCursorPos(window, &x, &y);
+void Window::trapCursor(bool trap) {
+    glfwSetInputMode(window, GLFW_CURSOR,
+                     trap ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
 }
 
-void Window::keyCallback(GLFWwindow* w, int key, int scancode, int action,
-                         int mods) {
+void Window::onKey(GLFWwindow* w, int key, int scancode, int action, int mods) {
     void* p = glfwGetWindowUserPointer(w);
     if(p != nullptr) {
-        TextInput* input = static_cast<Window*>(p)->textInput;
+        Window* rw = static_cast<Window*>(p);
+        TextInput* input = rw->textInput;
         if(input != nullptr) {
             input->onKeyEvent(key, scancode, action, mods);
         }
+        rw->buttons.onKey(key, scancode, action, mods);
     }
 }
-void Window::charCallback(GLFWwindow* w, unsigned int codepoint) {
+void Window::onChar(GLFWwindow* w, unsigned int codepoint) {
     void* p = glfwGetWindowUserPointer(w);
     if(p != nullptr) {
         TextInput* input = static_cast<Window*>(p)->textInput;
@@ -103,4 +109,27 @@ void Window::charCallback(GLFWwindow* w, unsigned int codepoint) {
             input->onCharEvent(codepoint);
         }
     }
+}
+
+void Window::onResize(GLFWwindow* w, int width, int height) {
+    void* p = glfwGetWindowUserPointer(w);
+    if(p != nullptr) {
+        Window* rw = static_cast<Window*>(p);
+        rw->size.width = width;
+        rw->size.height = height;
+    }
+}
+
+void Window::onMouse(GLFWwindow* w, int button, int action, int mods) {
+    void* p = glfwGetWindowUserPointer(w);
+    if(p != nullptr) {
+        static_cast<Window*>(p)->buttons.onMouse(button, action, mods);
+    }
+}
+
+void Window::onMouseMove(GLFWwindow* w, double x, double y) {
+    void* p = glfwGetWindowUserPointer(w);
+    if(p != nullptr) {
+        static_cast<Window*>(p)->buttons.onMouseMove(x, y);
+    }
 }

+ 39 - 12
rendering/Window.h

@@ -4,12 +4,23 @@
 #include <GL/glew.h>
 #include <GLFW/glfw3.h>
 
+#include "input/Buttons.h"
 #include "input/TextInput.h"
 #include "rendering/WindowOptions.h"
+#include "utils/Clock.h"
+#include "utils/Error.h"
 
 class Window final {
+    static int glfwCounter;
+    static int glewCounter;
+
+    Error error;
     TextInput*& textInput;
     GLFWwindow* window;
+    Clock fps;
+    Clock tps;
+    Size size;
+    Buttons buttons;
 
 public:
     Window(TextInput*& textInput, const WindowOptions& options);
@@ -20,23 +31,39 @@ public:
     Window(Window&&) = delete;
     Window& operator=(Window&&) = delete;
 
-    bool hasError() const;
-    void show();
-    bool shouldClose() const;
-    void swapBuffers();
+    const Error& getError() const;
+    const Clock& getFrameClock() const;
+    const Clock& getTickClock() const;
+    const Size& getSize() const;
+    Buttons& getButtons();
 
     void trapCursor(bool trap);
-    Size getSize() const;
-    Size getFramebufferSize() const;
-    bool isKeyDown(int key) const;
 
-    bool isMouseDown(int mouse) const;
-    void getMousePosition(double& x, double& y) const;
+    template<typename T>
+    void run(T& game, Clock::Nanos nanosPerTick) {
+        glfwShowWindow(window);
+        Clock::Nanos lag = 0;
+        while(!glfwWindowShouldClose(window) && game.isRunning()) {
+            lag += fps.update();
+            while(lag >= nanosPerTick) {
+                lag -= nanosPerTick;
+                tps.update();
+                buttons.tick();
+                game.tick();
+            }
+            game.render(static_cast<float>(lag) / nanosPerTick);
+            glfwSwapBuffers(window);
+            glfwPollEvents();
+        }
+    }
 
 private:
-    static void keyCallback(GLFWwindow* w, int key, int scancode, int action,
-                            int mods);
-    static void charCallback(GLFWwindow* w, unsigned int codepoint);
+    static void onKey(GLFWwindow* w, int key, int scancode, int action,
+                      int mods);
+    static void onChar(GLFWwindow* w, unsigned int codepoint);
+    static void onResize(GLFWwindow* w, int width, int height);
+    static void onMouse(GLFWwindow* w, int button, int action, int mods);
+    static void onMouseMove(GLFWwindow* w, double x, double y);
 };
 
 #endif

+ 9 - 0
utils/Error.cpp

@@ -0,0 +1,9 @@
+#include "utils/Error.h"
+
+bool Error::has() const {
+    return message.getLength() > 0;
+}
+
+void Error::reset() {
+    message.clear();
+}

+ 13 - 0
utils/Error.h

@@ -0,0 +1,13 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+#include "utils/StringBuffer.h"
+
+struct Error final {
+    StringBuffer<256> message;
+
+    bool has() const;
+    void reset();
+};
+
+#endif

+ 18 - 8
utils/StringBuffer.h

@@ -74,16 +74,15 @@ public:
     }
 
     StringBuffer& append(const char* str) {
-        for(int i = 0; length < N - 1 && str[i] != '\0'; length++, i++) {
-            data[length] = str[i];
-            addToHash(str[i]);
-        }
-        data[length] = '\0';
-        return *this;
+        return appendString<const char*>(str);
+    }
+
+    StringBuffer& append(const signed char* str) {
+        return appendString<const char*>(str);
     }
 
-    StringBuffer& append(char* str) {
-        return append(static_cast<const char*>(str));
+    StringBuffer& append(const unsigned char* str) {
+        return appendString<const unsigned char*>(str);
     }
 
     template<typename T>
@@ -159,6 +158,17 @@ public:
     void printLine() const {
         std::cout << data << '\n';
     }
+
+private:
+    template<typename T>
+    StringBuffer& appendString(T str) {
+        for(int i = 0; length < N - 1 && str[i] != '\0'; length++, i++) {
+            data[length] = str[i];
+            addToHash(str[i]);
+        }
+        data[length] = '\0';
+        return *this;
+    }
 };
 
 template<int N>

+ 0 - 14
wrapper/GLEW.cpp

@@ -1,14 +0,0 @@
-#include <iostream>
-
-#include <GL/glew.h>
-
-#include "wrapper/GLEW.h"
-
-bool GLEW::init() {
-    GLenum err = glewInit();
-    if(err != GLEW_OK) {
-        std::cout << "could not initialize GLEW: " << glewGetErrorString(err) << "\n";
-        return true;
-    }
-    return false;
-}

+ 0 - 8
wrapper/GLEW.h

@@ -1,8 +0,0 @@
-#ifndef GLEW_H
-#define GLEW_H
-
-namespace GLEW {
-    bool init();
-}
-
-#endif

+ 0 - 15
wrapper/GLFW.cpp

@@ -1,15 +0,0 @@
-#include <iostream>
-
-#include <GL/glew.h>
-#include <GLFW/glfw3.h>
-
-#include "wrapper/GLFW.h"
-
-bool GLFW::init() {
-    if(glfwInit()) {
-        atexit([]() { glfwTerminate(); });
-        return false;
-    }
-    std::cout << "could not initialize GLFW\n";
-    return true;
-}

+ 0 - 8
wrapper/GLFW.h

@@ -1,8 +0,0 @@
-#ifndef GLFW_H
-#define GLFW_H
-
-namespace GLFW {
-    bool init();
-}
-
-#endif