Browse Source

delayed initialization for everything relevant

Kajetan Johannes Hammerle 3 years ago
parent
commit
bce069b668

+ 5 - 8
Main.cpp

@@ -1,5 +1,3 @@
-#include "rendering/FileTexture.h"
-#include "rendering/Framebuffer.h"
 #include "rendering/Window.h"
 #include "tests/ArrayListTests.h"
 #include "tests/ArrayTests.h"
@@ -69,12 +67,11 @@ int main(int argAmount, char** args) {
         }
     };
 
-    TextInput* input = nullptr;
-    Size size(800, 480);
-    WindowOptions options(4, 3, size, false, "Test");
-    Window w(input, options);
-    if(w.getError().has()) {
-        LOG_WARNING(w.getError().message);
+    WindowOptions options(4, 3, {800, 480}, false, "Test");
+    Window w;
+    Error error = w.open(options);
+    if(error.has()) {
+        LOG_ERROR(error.message);
         return 0;
     }
     Game game;

+ 67 - 78
images/PNGReader.cpp

@@ -1,39 +1,59 @@
-#include <libpng16/png.h>
-
 #include "images/PNGReader.h"
-#include "utils/Logger.h"
-#include "utils/StringBuffer.h"
 
-PNGReader::PNGReader(const char* path)
-    : path(path), width(0), height(0), channels(0), bitDepth(0), rowBytes(0),
-      file(fopen(path, "r")), read(nullptr), info(nullptr),
-      rowPointers(nullptr) {
+PNGReader::PNGReader()
+    : path(""), width(0), height(0), channels(0), bitDepth(0), rowBytes(0),
+      file(nullptr), read(nullptr), info(nullptr), rowPointers(nullptr) {
+}
+
+PNGReader::~PNGReader() {
+    if(file != nullptr) {
+        fclose(file);
+    }
+    if(rowPointers != nullptr) {
+        png_free(read, rowPointers);
+    }
+    png_destroy_read_struct(&read, &info, nullptr);
+}
+
+Error PNGReader::load(const char* path) {
+    if(file != nullptr) {
+        Error error = {"file '"};
+        error.message.append(path).append("' is alreaded loaded");
+        return error;
+    }
+    PNGReader::path = path;
+    file = fopen(path, "r");
     if(file == nullptr) {
-        LOG_WARNING(StringBuffer<100>("file '")
-                        .append(path)
-                        .append("' cannot be read: ")
-                        .append(static_cast<const char*>(strerror(errno))));
-        return;
+        Error error = {"file '"};
+        error.message.append(path)
+            .append("' cannot be read: ")
+            .append(static_cast<const char*>(strerror(errno)));
+        return error;
     }
-    if(checkSignature()) {
-        return;
+    png_byte buffer[8];
+    if(fread(buffer, sizeof(png_byte), 8, file) != 8) {
+        Error error = {"cannot read signature of file '"};
+        error.message.append(path).append("'");
+        return error;
+    }
+    if(png_sig_cmp(buffer, 0, 8)) {
+        Error error = {"file '"};
+        error.message.append(path).append("' is not a png");
+        return error;
     }
     read = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
                                   nullptr);
     if(read == nullptr) {
-        LOG_WARNING("cannot create png read data structure");
-        return;
+        return {"cannot create png read data structure"};
     }
     info = png_create_info_struct(read);
     if(info == nullptr) {
-        LOG_WARNING("cannot create png info structure");
-        return;
+        return {"cannot create png info structure"};
     }
     if(setjmp(png_jmpbuf(read))) {
-        LOG_WARNING(StringBuffer<100>("png file '")
-                        .append(path)
-                        .append("' has used error callback"));
-        return;
+        Error error = {"png file '"};
+        error.message.append(path).append("' has used error callback");
+        return error;
     }
     png_init_io(read, file);
     png_set_sig_bytes(read, 8);
@@ -49,16 +69,27 @@ PNGReader::PNGReader(const char* path)
         png_set_strip_16(read);
     }
     rowBytes = png_get_rowbytes(read, info);
-}
 
-PNGReader::~PNGReader() {
-    if(file != nullptr) {
-        fclose(file);
-    }
-    if(rowPointers != nullptr) {
-        png_free(read, rowPointers);
+    if(channels < 1 || channels > 4) {
+        Error error = {"'"};
+        error.message.append(path)
+            .append("' has unsupported number of channels: ")
+            .append(channels);
+        return error;
+    } else if(width < 1 || width > 2048 || height < 1 || height > 2048) {
+        Error error = {"width and height of '"};
+        error.message.append(path).append("' are too big");
+        return error;
+    } else if(bitDepth != 8 && bitDepth != 16) {
+        Error error = {"bit depth of '"};
+        error.message.append(path).append("' is neither 8 or 16");
+        return error;
+    } else if(getBufferSize() != (rowBytes * height * 8 / bitDepth)) {
+        Error error = {"'"};
+        error.message.append(path).append("' needs an unexpected buffer size");
+        return error;
     }
-    png_destroy_read_struct(&read, &info, nullptr);
+    return {};
 }
 
 int PNGReader::getWidth() const {
@@ -77,53 +108,11 @@ int PNGReader::getBufferSize() const {
     return width * height * channels;
 }
 
-bool PNGReader::hasError() const {
-    if(channels < 1 || channels > 4) {
-        LOG_WARNING(StringBuffer<100>("'")
-                        .append(path)
-                        .append("' has unsupported number of channels: ")
-                        .append(channels));
-        return true;
-    } else if(width < 1 || width > 2048 || height < 1 || height > 2048) {
-        LOG_WARNING(StringBuffer<100>("width and height of '")
-                        .append(path)
-                        .append("' are too big"));
-        return true;
-    } else if(bitDepth != 8 && bitDepth != 16) {
-        LOG_WARNING(StringBuffer<100>("bit depth of '")
-                        .append(path)
-                        .append("' is neither 8 or 16"));
-        return true;
-    } else if(getBufferSize() != (rowBytes * height * 8 / bitDepth)) {
-        LOG_WARNING(StringBuffer<100>("'").append(path).append(
-            "' needs an unexpected buffer size"));
-        return true;
-    }
-    return false;
-}
-
-bool PNGReader::checkSignature() {
-    png_byte buffer[8];
-    if(fread(buffer, sizeof(png_byte), 8, file) != 8) {
-        LOG_WARNING(StringBuffer<100>("cannot read signature of file '")
-                        .append(path)
-                        .append("'"));
-        return true;
-    }
-    if(png_sig_cmp(buffer, 0, 8)) {
-        LOG_WARNING(
-            StringBuffer<100>("file '").append(path).append("' is not a png"));
-        return true;
-    }
-    return false;
-}
-
-bool PNGReader::readData(ColorChannel* buffer) {
+Error PNGReader::readData(ColorChannel* buffer) {
     if(setjmp(png_jmpbuf(read))) {
-        LOG_WARNING(StringBuffer<100>("png file '")
-                        .append(path)
-                        .append("' has used error callback"));
-        return true;
+        Error error = {"png file '"};
+        error.message.append(path).append("' has used error callback");
+        return error;
     }
     rowPointers = static_cast<ColorChannel**>(
         png_malloc(read, height * sizeof(ColorChannel*)));
@@ -132,5 +121,5 @@ bool PNGReader::readData(ColorChannel* buffer) {
     }
     png_set_rows(read, info, reinterpret_cast<png_bytepp>(rowPointers));
     png_read_image(read, reinterpret_cast<png_bytepp>(rowPointers));
-    return false;
+    return {};
 }

+ 5 - 6
images/PNGReader.h

@@ -4,6 +4,7 @@
 #include <png.h>
 
 #include "utils/Color.h"
+#include "utils/Error.h"
 
 class PNGReader final {
     const char* path;
@@ -18,23 +19,21 @@ class PNGReader final {
     ColorChannel** rowPointers;
 
 public:
-    PNGReader(const char* path);
+    PNGReader();
     ~PNGReader();
     PNGReader(const PNGReader& other) = delete;
     PNGReader(PNGReader&& other) = delete;
     PNGReader& operator=(const PNGReader& other) = delete;
     PNGReader& operator=(PNGReader&& other) = delete;
 
+    Error load(const char* path);
+
     int getWidth() const;
     int getHeight() const;
     int getChannels() const;
     int getBufferSize() const;
-    bool hasError() const;
-
-    bool readData(ColorChannel* buffer);
 
-private:
-    bool checkSignature();
+    Error readData(ColorChannel* buffer);
 };
 
 #endif

+ 22 - 17
network/Client.cpp

@@ -1,26 +1,32 @@
+#include <cassert>
+
 #include "network/Client.h"
 
-Client::Client()
-    : client(enet_host_create(nullptr, 1, 2, 0, 0)), connection(nullptr) {
-    if(client == nullptr) {
-        error.clear().append("cannot create ENet client host");
-    }
+Client::Client() : client(nullptr), connection(nullptr) {
+    ENet::add();
 }
 
 Client::~Client() {
     disconnect();
     enet_host_destroy(client);
+    ENet::remove();
 }
 
-bool Client::hasError() const {
-    return error.getLength() > 0;
-}
-
-const Client::Error& Client::getError() const {
-    return error;
+Error Client::start() {
+    assert(client == nullptr);
+    if(ENet::hasError()) {
+        return {"cannot initialize enet"};
+    }
+    client = enet_host_create(nullptr, 1, 2, 0, 0);
+    if(client == nullptr) {
+        return {"cannot create enet client host"};
+    }
+    return {};
 }
 
-bool Client::connect(const char* server, Port port, int timeout) {
+Error Client::connect(const char* server, Port port, int timeout) {
+    assert(client != nullptr);
+    assert(connection == nullptr);
     ENetAddress address;
     ENetEvent event;
     enet_address_set_host(&address, server);
@@ -28,17 +34,15 @@ bool Client::connect(const char* server, Port port, int timeout) {
 
     connection = enet_host_connect(client, &address, 3, 0);
     if(connection == nullptr) {
-        error.clear().append("server is not available");
-        return true;
+        return {"server is not available"};
     }
 
     if(enet_host_service(client, &event, timeout) <= 0 ||
        event.type != ENET_EVENT_TYPE_CONNECT) {
-        error.clear().append("connection failed");
         disconnect();
-        return true;
+        return {"connection failed"};
     }
-    return false;
+    return {};
 }
 
 void Client::disconnect() {
@@ -59,6 +63,7 @@ void Client::disconnect() {
 }
 
 void Client::send(OutPacket& p) {
+    assert(client != nullptr);
     if(p.packet != nullptr) {
         p.packet->dataLength = p.index;
         enet_peer_send(connection, p.channel, p.packet);

+ 3 - 7
network/Client.h

@@ -3,16 +3,14 @@
 
 #include "network/ENet.h"
 #include "network/Packet.h"
-#include "utils/StringBuffer.h"
+#include "utils/Error.h"
 
 class Client final {
     typedef enet_uint16 Port;
-    typedef StringBuffer<256> Error;
 
 private:
     ENetHost* client;
     ENetPeer* connection;
-    Error error;
 
 public:
     Client();
@@ -22,10 +20,8 @@ public:
     Client& operator=(const Client&) = delete;
     Client& operator=(Client&&) = delete;
 
-    bool hasError() const;
-    const Error& getError() const;
-
-    bool connect(const char* server, Port port, int timeout);
+    Error start();
+    Error connect(const char* server, Port port, int timeout);
     void disconnect();
 
     template<typename T>

+ 16 - 4
network/ENet.cpp

@@ -1,10 +1,22 @@
 #define ENET_IMPLEMENTATION
 #include "network/ENet.h"
 
-ENet::~ENet() {
-    enet_deinitialize();
+static int enetCounter = 0;
+
+bool ENet::add() {
+    if(enetCounter == 0 && enet_initialize() != 0) {
+        return true;
+    }
+    enetCounter++;
+    return false;
+}
+
+void ENet::remove() {
+    if(enetCounter > 0 && --enetCounter == 0) {
+        enet_deinitialize();
+    }
 }
 
-bool ENet::init() {
-    return enet_initialize() != 0;
+bool ENet::hasError() {
+    return enetCounter <= 0;
 }

+ 7 - 8
network/ENet.h

@@ -9,15 +9,14 @@
 #include "enet/include/enet.h"
 #pragma GCC diagnostic pop
 
-struct ENet final {
-    ENet() = default;
-    ENet(const ENet&) = delete;
-    ENet(ENet&&) = delete;
-    ~ENet();
-    ENet& operator=(const ENet&) = delete;
-    ENet& operator=(ENet&&) = delete;
+class ENet final {
+    friend class Client;
+    friend class Server;
 
-    bool init();
+    ENet() = delete;
+    static bool add();
+    static void remove();
+    static bool hasError();
 };
 
 #endif

+ 18 - 0
network/Packet.cpp

@@ -2,6 +2,9 @@
 
 #include "network/Packet.h"
 
+static_assert(sizeof(float) == sizeof(uint32),
+              "sizeof(float) != sizeof(uint32)");
+
 InPacket::InPacket(ENetPacket* packet) : packet(packet), index(0) {
 }
 
@@ -73,6 +76,15 @@ bool InPacket::readS32(int32& s) {
     return false;
 }
 
+bool InPacket::readFloat(float& f) {
+    uint32 u;
+    if(readU32(u)) {
+        return true;
+    }
+    memcpy(&f, &u, sizeof(float));
+    return false;
+}
+
 OutPacket::OutPacket(unsigned int size, int flags, int channel)
     : packet(enet_packet_create(nullptr, size, flags)), index(0),
       channel(channel) {
@@ -153,4 +165,10 @@ void OutPacket::writeS32(int32 s) {
     } else {
         writeU32(static_cast<uint32>(s) + 2147483648);
     }
+}
+
+void OutPacket::writeFloat(float f) {
+    uint32 u;
+    memcpy(&u, &f, sizeof(float));
+    writeU32(u);
 }

+ 2 - 0
network/Packet.h

@@ -23,6 +23,7 @@ public:
     bool readS8(int8& s);
     bool readS16(int16& s);
     bool readS32(int32& s);
+    bool readFloat(float& f);
 
     template<int N>
     bool readString(StringBuffer<N>& s) {
@@ -68,6 +69,7 @@ public:
     void writeS8(int8 s);
     void writeS16(int16 s);
     void writeS32(int32 s);
+    void writeFloat(float f);
 
     template<int N>
     void writeString(const StringBuffer<N>& s) {

+ 18 - 14
network/Server.cpp

@@ -1,3 +1,4 @@
+#include <cassert>
 #include <utility>
 
 #include "network/Server.h"
@@ -34,15 +35,8 @@ void Server::Client::send(OutPacket& p) {
     }
 }
 
-Server::Server(Port port, int maxClients) : server(nullptr), idCounter(1) {
-    ENetAddress address;
-    address.host = ENET_HOST_ANY;
-    address.port = port;
-
-    server = enet_host_create(&address, maxClients, 3, 0, 0);
-    if(server == nullptr) {
-        error.clear().append("cannot create ENet server host");
-    }
+Server::Server() : server(nullptr), idCounter(1) {
+    ENet::add();
 }
 
 Server::~Server() {
@@ -50,17 +44,27 @@ Server::~Server() {
         enet_peer_reset(client.value.peer);
     }
     enet_host_destroy(server);
+    ENet::remove();
 }
 
-bool Server::hasError() const {
-    return error.getLength() > 0;
-}
+Error Server::start(Port port, int maxClients) {
+    assert(server == nullptr);
+    if(ENet::hasError()) {
+        return {"cannot initialize enet"};
+    }
+    ENetAddress address;
+    address.host = ENET_HOST_ANY;
+    address.port = port;
 
-const Server::Error& Server::getError() const {
-    return error;
+    server = enet_host_create(&address, maxClients, 3, 0, 0);
+    if(server == nullptr) {
+        return {"cannot create enet server host"};
+    }
+    return {};
 }
 
 void Server::send(OutPacket& p) {
+    assert(server != nullptr);
     if(p.packet != nullptr) {
         p.packet->dataLength = p.index;
         enet_host_broadcast(server, p.channel, p.packet);

+ 11 - 13
network/Server.h

@@ -3,13 +3,13 @@
 
 #include "network/ENet.h"
 #include "network/Packet.h"
+#include "utils/Error.h"
 #include "utils/HashMap.h"
+#include "utils/Logger.h"
 #include "utils/StringBuffer.h"
-#include "utils/Types.h"
 
 struct Server final {
     typedef enet_uint16 Port;
-    typedef StringBuffer<256> Error;
 
     class Client final {
         friend class Server;
@@ -34,25 +34,23 @@ struct Server final {
 
 private:
     ENetHost* server;
-    Error error;
     HashMap<int, Client> clients;
     int idCounter;
 
 public:
-    Server(Port port, int maxClients);
+    Server();
     Server(const Server&) = delete;
     Server(Server&&) = delete;
     ~Server();
     Server& operator=(const Server&) = delete;
     Server& operator=(Server&&) = delete;
 
-    bool hasError() const;
-    const Error& getError() const;
+    Error start(Port port, int maxClients);
 
     template<typename T>
     void consumeEvents(T& consumer) {
         ENetEvent e;
-        while(!hasError() && enet_host_service(server, &e, 0) > 0) {
+        while(enet_host_service(server, &e, 0) > 0) {
             switch(e.type) {
                 case ENET_EVENT_TYPE_CONNECT: onConnect(e, consumer); break;
                 case ENET_EVENT_TYPE_RECEIVE:
@@ -76,7 +74,7 @@ private:
     void onConnect(ENetEvent& e, T& consumer) {
         int id = idCounter++;
         if(clients.tryEmplace(id, e.peer, id)) {
-            error.clear().append("id is connected twice");
+            LOG_WARNING("id is connected twice");
             return;
         }
         static_assert(sizeof(e.peer->data) >= sizeof(id),
@@ -86,14 +84,14 @@ private:
         if(client != nullptr) {
             consumer.onConnect(*client);
         } else {
-            error.clear().append("cannot find added client");
+            LOG_WARNING("cannot find added client");
         }
     }
 
     template<typename T>
     void onPacket(ENetEvent& e, T& consumer) {
         if(e.peer->data == nullptr) {
-            error.clear().append("client without data sent package");
+            LOG_WARNING("client without data sent package");
             return;
         }
         int id = -1;
@@ -103,14 +101,14 @@ private:
             InPacket in(e.packet);
             consumer.onPacket(*client, in);
         } else {
-            error.clear().append("client with invalid id sent package");
+            LOG_WARNING("client with invalid id sent package");
         }
     }
 
     template<typename T>
     void onDisconnect(ENetEvent& e, T& consumer) {
         if(e.peer->data == nullptr) {
-            error.clear().append("client without data disconnected");
+            LOG_WARNING("client without data disconnected");
             return;
         }
         int id = -1;
@@ -120,7 +118,7 @@ private:
             consumer.onDisconnect(*client);
             clients.remove(id);
         } else {
-            error.clear().append("client has invalid id");
+            LOG_WARNING("client has invalid id");
         }
     }
 };

+ 11 - 8
rendering/FileTexture.cpp

@@ -2,18 +2,21 @@
 #include "images/PNGReader.h"
 #include "utils/List.h"
 
-FileTexture::FileTexture(const char* path, int maxMipMaps)
-    : texture(maxMipMaps) {
-    PNGReader png(path);
-    if(png.hasError()) {
-        return;
+Error FileTexture::load(const char* path, int maxMipMaps) {
+    PNGReader png;
+    Error error = png.load(path);
+    if(error.has()) {
+        return error;
     }
     List<ColorChannel> buffer;
     buffer.resize(png.getBufferSize());
-    if(!png.readData(buffer.begin())) {
-        texture.setFormat(TextureFormat::color8(png.getChannels()));
-        texture.setData(png.getWidth(), png.getHeight(), buffer.begin());
+    error = png.readData(buffer.begin());
+    if(error.has()) {
+        return error;
     }
+    texture.init(TextureFormat::color8(png.getChannels()), maxMipMaps);
+    texture.setData(png.getWidth(), png.getHeight(), buffer.begin());
+    return {};
 }
 
 void FileTexture::bindTo(int index) const {

+ 2 - 1
rendering/FileTexture.h

@@ -2,12 +2,13 @@
 #define FILETEXTURE_H
 
 #include "rendering/Texture.h"
+#include "utils/Error.h"
 
 class FileTexture final {
     Texture texture;
 
 public:
-    FileTexture(const char* path, int maxMipMaps = 0);
+    Error load(const char* path, int maxMipMaps);
 
     void bindTo(int index) const;
 

+ 15 - 14
rendering/Framebuffer.h

@@ -11,19 +11,7 @@ class Framebuffer final {
     GL::Framebuffer buffer;
 
 public:
-    template<typename... Args>
-    Framebuffer(const TextureFormat& a, Args&&... args) : buffer(0) {
-        const int size = sizeof...(args) + 1;
-        TextureFormat init[size] = {a, args...};
-        static_assert(N == size,
-                      "framebuffer size and amount of arguments do not match");
-        for(int i = 0; i < N; i++) {
-            textures.add(init[i]);
-            textures[i].setClampWrap();
-            if(init[i].linear) {
-                textures[i].setLinearFilter();
-            }
-        }
+    Framebuffer() : buffer(0) {
     }
 
     ~Framebuffer() {
@@ -35,7 +23,20 @@ public:
     Framebuffer& operator=(const Framebuffer&) = delete;
     Framebuffer& operator=(Framebuffer&&) = delete;
 
-    bool init(const Size& size) {
+    template<typename... Args>
+    bool init(const Size& size, Args&&... args) {
+        const int n = sizeof...(args);
+        TextureFormat init[n] = {args...};
+        static_assert(N == n,
+                      "framebuffer size and amount of arguments do not match");
+        for(int i = 0; i < N; i++) {
+            textures[i].init(init[i], 0);
+            textures[i].setClampWrap();
+            if(init[i].linear) {
+                textures[i].setLinearFilter();
+            }
+        }
+
         buffer = GL::genFramebuffer();
         GL::bindFramebuffer(buffer);
 

+ 52 - 46
rendering/Shader.cpp

@@ -4,60 +4,68 @@
 #include "utils/Logger.h"
 #include "wrapper/GL.h"
 
-Shader::Shader(const char* vertexPath, const char* fragmentPath,
-               const char* geometryPath)
-    : vertexShader(0), geometryShader(0), fragmentShader(0), program(0) {
-    if(compile(vertexPath, vertexShader, GL::VERTEX_SHADER) ||
-       compile(fragmentPath, fragmentShader, GL::FRAGMENT_SHADER)) {
-        return;
+Shader::Shader() : vertex(0), geometry(0), fragment(0), program(0) {
+}
+
+Shader::~Shader() {
+    GL::deleteShader(vertex);
+    GL::deleteShader(geometry);
+    GL::deleteShader(fragment);
+    GL::deleteProgram(program);
+}
+
+Error Shader::compile(const char* vertexPath, const char* geometryPath,
+                      const char* fragmentPath) {
+    if(vertexPath != nullptr) {
+        Error error = compile(vertexPath, vertex, GL::VERTEX_SHADER);
+        if(error.has()) {
+            return error;
+        }
     }
-    if(geometryPath != nullptr &&
-       compile(geometryPath, geometryShader, GL::GEOMETRY_SHADER)) {
-        return;
+    if(geometryPath != nullptr) {
+        Error error = compile(geometryPath, geometry, GL::GEOMETRY_SHADER);
+        if(error.has()) {
+            return error;
+        }
+    }
+    if(fragmentPath != nullptr) {
+        Error error = compile(fragmentPath, fragment, GL::FRAGMENT_SHADER);
+        if(error.has()) {
+            return error;
+        }
     }
     program = GL::createProgram();
-    GL::attachShader(program, vertexShader);
+    if(vertexPath != nullptr) {
+        GL::attachShader(program, vertex);
+    }
     if(geometryPath != nullptr) {
-        GL::attachShader(program, geometryShader);
+        GL::attachShader(program, geometry);
     }
-    GL::attachShader(program, fragmentShader);
-    GL::linkProgram(program);
-    if(GL::printError("cannot link")) {
-        return;
+    if(fragmentPath != nullptr) {
+        GL::attachShader(program, fragment);
     }
-    if(GL::logLinkerError(program)) {
-        clean();
-        return;
+    GL::linkProgram(program);
+    Error error = GL::getError("cannot link");
+    if(error.has()) {
+        return error;
     }
+    return GL::getLinkerError(program);
 }
 
-Shader::~Shader() {
-    clean();
-}
-
-void Shader::clean() {
-    GL::deleteShader(vertexShader);
-    GL::deleteShader(geometryShader);
-    GL::deleteShader(fragmentShader);
-    GL::deleteProgram(program);
-    program = 0;
-}
-
-bool Shader::compile(const char* path, GL::Shader& s, GL::ShaderType st) {
-    LOG_DEBUG(StringBuffer<50>("shader: ").append(path));
+Error Shader::compile(const char* path, GL::Shader& s, GL::ShaderType st) {
     List<char> code;
-    if(readFile(code, path)) {
-        return true;
+    Error error = readFile(code, path);
+    if(error.has()) {
+        return error;
     }
     return compile(s, code, st);
 }
 
-bool Shader::readFile(List<char>& code, const char* path) const {
+Error Shader::readFile(List<char>& code, const char* path) const {
     std::ifstream in;
     in.open(path);
     if(!in.good()) {
-        LOG_ERROR("cannot read file");
-        return true;
+        return {"cannot read file"};
     }
     while(true) {
         int c = in.get();
@@ -67,20 +75,18 @@ bool Shader::readFile(List<char>& code, const char* path) const {
         code.add(c);
     }
     code.add('\0');
-    return false;
-}
-
-bool Shader::hasError() const {
-    return vertexShader == 0 || fragmentShader == 0 || program == 0;
+    return {};
 }
 
-bool Shader::compile(GL::Shader& s, const List<char>& code, GL::ShaderType st) {
+Error Shader::compile(GL::Shader& s, const List<char>& code,
+                      GL::ShaderType st) {
     s = GL::createShader(st);
     GL::compileShader(s, code.begin());
-    if(GL::printError("compile error")) {
-        return true;
+    Error error = GL::getError("compile error");
+    if(error.has()) {
+        return error;
     }
-    return GL::logCompileError(s);
+    return GL::getCompileError(s);
 }
 
 void Shader::use() const {

+ 10 - 10
rendering/Shader.h

@@ -2,25 +2,26 @@
 #define SHADER_H
 
 #include "math/Vector.h"
+#include "utils/Error.h"
 #include "utils/List.h"
 #include "wrapper/GL.h"
 
 class Shader final {
-    GL::Shader vertexShader;
-    GL::Shader geometryShader;
-    GL::Shader fragmentShader;
+    GL::Shader vertex;
+    GL::Shader geometry;
+    GL::Shader fragment;
     GL::Program program;
 
 public:
-    Shader(const char* vertexPath, const char* fragmentPath,
-           const char* geometryPath = nullptr);
+    Shader();
     ~Shader();
     Shader(const Shader& other) = delete;
     Shader(Shader&& other) = delete;
     Shader& operator=(const Shader& other) = delete;
     Shader& operator=(Shader&& other) = delete;
 
-    bool hasError() const;
+    Error compile(const char* vertexPath, const char* geometryPath,
+                  const char* fragmentPath);
 
     void use() const;
     void setMatrix(const char* name, const float* data);
@@ -32,10 +33,9 @@ public:
     void setVector(const char* name, const Vector4& v);
 
 private:
-    void clean();
-    bool compile(const char* path, GL::Shader& s, GL::ShaderType st);
-    bool readFile(List<char>& code, const char* path) const;
-    bool compile(GL::Shader& s, const List<char>& code, GL::ShaderType st);
+    Error compile(const char* path, GL::Shader& s, GL::ShaderType st);
+    Error readFile(List<char>& code, const char* path) const;
+    Error compile(GL::Shader& s, const List<char>& code, GL::ShaderType st);
 };
 
 #endif

+ 11 - 10
rendering/Texture.cpp

@@ -1,21 +1,22 @@
 #include "rendering/Texture.h"
 
-Texture::Texture(const TextureFormat& format, int maxMipMaps)
-    : format(format), texture(GL::genTexture()), maxMipMaps(maxMipMaps) {
-    setNearestFilter();
-    setRepeatWrap();
-}
-
-Texture::Texture(int maxMipMaps)
-    : Texture(TextureFormat::unknown(), maxMipMaps) {
+Texture::Texture()
+    : format(TextureFormat::unknown()), texture(0), maxMipMaps(0) {
 }
 
 Texture::~Texture() {
     GL::deleteTexture(texture);
 }
 
-void Texture::setFormat(const TextureFormat& tf) {
-    format = tf;
+void Texture::init(const TextureFormat& format, int maxMipMaps) {
+    if(texture != 0) {
+        return;
+    }
+    Texture::format = format;
+    Texture::maxMipMaps = maxMipMaps;
+    texture = GL::genTexture();
+    setNearestFilter();
+    setRepeatWrap();
 }
 
 void Texture::setNearestFilter() {

+ 3 - 3
rendering/Texture.h

@@ -13,15 +13,15 @@ class Texture final {
     friend class Framebuffer;
 
 public:
-    Texture(const TextureFormat& format, int maxMipMaps = 0);
-    Texture(int maxMipMaps = 0);
+    Texture();
     ~Texture();
     Texture(const Texture& other) = delete;
     Texture(Texture&& other) = delete;
     Texture& operator=(const Texture& other) = delete;
     Texture& operator=(Texture&& other) = delete;
 
-    void setFormat(const TextureFormat& format);
+    void init(const TextureFormat& format, int maxMipMaps);
+
     void setNearestFilter();
     void setLinearFilter();
     void setRepeatWrap();

+ 7 - 4
rendering/VertexBuffer.cpp

@@ -1,8 +1,6 @@
 #include "rendering/VertexBuffer.h"
 
-VertexBuffer::VertexBuffer()
-    : vertexArray(GL::genVertexArray()), vertexBuffer(GL::genBuffer()),
-      size(0) {
+VertexBuffer::VertexBuffer() : vertexArray(0), vertexBuffer(0), size(0) {
 }
 
 VertexBuffer::~VertexBuffer() {
@@ -18,7 +16,12 @@ void VertexBuffer::bindBuffer() const {
     GL::bindBuffer(vertexBuffer);
 }
 
-void VertexBuffer::setAttributes(const Attributes& attributes) {
+void VertexBuffer::init(const Attributes& attributes) {
+    if(vertexArray != 0 || vertexBuffer != 0) {
+        return;
+    }
+    vertexArray = GL::genVertexArray();
+    vertexBuffer = GL::genBuffer();
     bindArray();
     bindBuffer();
     attributes.set();

+ 2 - 1
rendering/VertexBuffer.h

@@ -17,7 +17,8 @@ public:
     VertexBuffer& operator=(const VertexBuffer& other) = delete;
     VertexBuffer& operator=(VertexBuffer&& other) = delete;
 
-    void setAttributes(const Attributes& attributes);
+    void init(const Attributes& attributes);
+
     void setStaticData(int size, const void* data = nullptr);
     void setStreamData(int size, const void* data = nullptr);
     void setDynamicData(int size, const void* data = nullptr);

+ 22 - 26
rendering/Window.cpp

@@ -1,15 +1,28 @@
 #include "rendering/Window.h"
 
-int Window::glfwCounter = 0;
-int Window::glewCounter = 0;
+static int glfwCounter = 0;
+static int glewCounter = 0;
 
-Window::Window(TextInput*& textInput, const WindowOptions& options)
-    : textInput(textInput), window(nullptr), size(options.size) {
+Window::Window() : window(nullptr), size({0, 0}), textInput(nullptr) {
     if(glfwCounter == 0 && !glfwInit()) {
-        error.message.clear().append("could not initialize GLFW");
         return;
     }
     glfwCounter++;
+}
+
+Window::~Window() {
+    if(window != nullptr) {
+        glfwDestroyWindow(window);
+    }
+    if(glfwCounter > 0 && --glfwCounter == 0) {
+        glfwTerminate();
+    }
+}
+
+Error Window::open(const WindowOptions& options) {
+    if(glfwCounter <= 0) {
+        return {"could not initialize GLFW"};
+    }
 
     glfwDefaultWindowHints();
     glfwWindowHint(GLFW_VISIBLE, false);
@@ -30,8 +43,7 @@ Window::Window(TextInput*& textInput, const WindowOptions& options)
     window = glfwCreateWindow(options.size.width, options.size.height,
                               options.name, monitor, nullptr);
     if(window == nullptr) {
-        error.message.clear().append("could not create window");
-        return;
+        return {"could not create window"};
     }
     glfwSetWindowUserPointer(window, this);
     glfwSetKeyCallback(window, onKey);
@@ -46,27 +58,15 @@ Window::Window(TextInput*& textInput, const WindowOptions& options)
     if(glewCounter == 0) {
         GLenum err = glewInit();
         if(err != GLEW_OK) {
+            Error error;
             error.message.clear()
                 .append("could not initialize GLEW: ")
                 .append(glewGetErrorString(err));
-            return;
+            return error;
         }
     }
     glewCounter++;
-}
-
-Window::~Window() {
-    if(window != nullptr) {
-        glfwDestroyWindow(window);
-    }
-    glfwCounter--;
-    if(glfwCounter <= 0) {
-        glfwTerminate();
-    }
-}
-
-const Error& Window::getError() const {
-    return error;
+    return {};
 }
 
 const Clock& Window::getFrameClock() const {
@@ -81,10 +81,6 @@ const Size& Window::getSize() const {
     return size;
 }
 
-Buttons& Window::getButtons() {
-    return buttons;
-}
-
 void Window::trapCursor(bool trap) {
     glfwSetInputMode(window, GLFW_CURSOR,
                      trap ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);

+ 6 - 10
rendering/Window.h

@@ -11,31 +11,27 @@
 #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);
-    ~Window();
+    TextInput* textInput;
+    Buttons buttons;
 
+    Window();
+    ~Window();
     Window(const Window&) = delete;
     Window& operator=(const Window&) = delete;
     Window(Window&&) = delete;
     Window& operator=(Window&&) = delete;
 
-    const Error& getError() const;
+    Error open(const WindowOptions& options);
+
     const Clock& getFrameClock() const;
     const Clock& getTickClock() const;
     const Size& getSize() const;
-    Buttons& getButtons();
 
     void trapCursor(bool trap);
 

+ 12 - 12
tests/NetworkTests.cpp

@@ -26,6 +26,7 @@ struct ServerConsumer {
     int16 data8 = 0;
     int32 data9 = 0;
     StringBuffer<20> data10;
+    float data11 = 0.0f;
 
     void onConnect(Server::Client& client) {
         (void)client;
@@ -49,6 +50,7 @@ struct ServerConsumer {
         in.readS16(data8);
         in.readS32(data9);
         in.readString(data10);
+        in.readFloat(data11);
 
         if(packageCounter == 0) {
             OutPacket out = OutPacket::reliable(0);
@@ -77,13 +79,13 @@ struct ClientConsumer {
 };
 
 static void testConnect(Test& test, OutPacket out) {
-    Server server(54321, 5);
-    if(server.hasError()) {
+    Server server;
+    if(server.start(54321, 5).has()) {
         test.checkEqual(false, true, "server can initialize");
         return;
     }
     Client client;
-    if(client.hasError()) {
+    if(client.start().has()) {
         test.checkEqual(false, true, "client can initialize");
         return;
     }
@@ -96,7 +98,7 @@ static void testConnect(Test& test, OutPacket out) {
         }
     });
 
-    test.checkEqual(false, client.connect("127.0.0.1", 54321, 5),
+    test.checkEqual(false, client.connect("127.0.0.1", 54321, 5).has(),
                     "connection failed");
 
     ClientConsumer clientConsumer;
@@ -115,6 +117,7 @@ static void testConnect(Test& test, OutPacket out) {
     out.writeS32(0x71234567);
     StringBuffer<20> s("Hi there");
     out.writeString(s);
+    out.writeFloat(252345.983f);
     client.send(out);
 
     for(int i = 0; i < 100; i++) {
@@ -143,6 +146,8 @@ static void testConnect(Test& test, OutPacket out) {
     test.checkEqual(0x71234567, serverConsumer.data9,
                     "correct value is sent 9");
     test.checkEqual(s, serverConsumer.data10, "correct value is sent 10");
+    test.checkFloat(252345.983f, serverConsumer.data11, 0.01f,
+                    "correct value is sent 11");
 
     client.disconnect();
     sleep(100);
@@ -155,13 +160,8 @@ static void testConnect(Test& test, OutPacket out) {
 
 void NetworkTests::test() {
     Test test("Network");
-    ENet enet;
-    if(enet.init()) {
-        test.checkEqual(false, true, "enet init failed");
-    } else {
-        testConnect(test, OutPacket::unsequenced(50));
-        testConnect(test, OutPacket::reliable(50));
-        testConnect(test, OutPacket::sequenced(50));
-    }
+    testConnect(test, OutPacket::unsequenced(50));
+    testConnect(test, OutPacket::reliable(50));
+    testConnect(test, OutPacket::sequenced(50));
     test.finalize();
 }

+ 17 - 17
tests/PNGReaderTests.cpp

@@ -10,13 +10,13 @@ static void testRead(Test& test, PNGReader& png, const char* text) {
     test.checkEqual(true, png.getBufferSize() > 0, text);
     test.checkEqual(png.getBufferSize(), buffer.getLength(), text);
     if(png.getBufferSize() == buffer.getLength()) {
-        test.checkEqual(false, png.readData(buffer.begin()), text);
+        test.checkEqual(false, png.readData(buffer.begin()).has(), text);
     }
 }
 
 static void testReadRGB8(Test& test, const char* path) {
-    PNGReader png(StringBuffer<200>(path).append("rgb8.png"));
-    if(png.hasError()) {
+    PNGReader png;
+    if(png.load(StringBuffer<200>(path).append("rgb8.png")).has()) {
         test.checkEqual(false, true, "read rgb8 error");
         return;
     }
@@ -27,8 +27,8 @@ static void testReadRGB8(Test& test, const char* path) {
 }
 
 static void testReadRGB16(Test& test, const char* path) {
-    PNGReader png(StringBuffer<200>(path).append("rgb16.png"));
-    if(png.hasError()) {
+    PNGReader png;
+    if(png.load((StringBuffer<200>(path).append("rgb16.png"))).has()) {
         test.checkEqual(false, true, "read rgb16 error");
         return;
     }
@@ -40,8 +40,8 @@ static void testReadRGB16(Test& test, const char* path) {
 }
 
 static void testReadRGBA8(Test& test, const char* path) {
-    PNGReader png(StringBuffer<200>(path).append("rgba8.png"));
-    if(png.hasError()) {
+    PNGReader png;
+    if(png.load(StringBuffer<200>(path).append("rgba8.png")).has()) {
         test.checkEqual(false, true, "read rgba8 error");
         return;
     }
@@ -53,8 +53,8 @@ static void testReadRGBA8(Test& test, const char* path) {
 }
 
 static void testReadRGBA16(Test& test, const char* path) {
-    PNGReader png(StringBuffer<200>(path).append("rgba16.png"));
-    if(png.hasError()) {
+    PNGReader png;
+    if(png.load(StringBuffer<200>(path).append("rgba16.png")).has()) {
         test.checkEqual(false, true, "read rgba16 error");
         return;
     }
@@ -66,8 +66,8 @@ static void testReadRGBA16(Test& test, const char* path) {
 }
 
 static void testReadGray8(Test& test, const char* path) {
-    PNGReader png(StringBuffer<200>(path).append("gray8.png"));
-    if(png.hasError()) {
+    PNGReader png;
+    if(png.load(StringBuffer<200>(path).append("gray8.png")).has()) {
         test.checkEqual(false, true, "read gray8 error");
         return;
     }
@@ -79,8 +79,8 @@ static void testReadGray8(Test& test, const char* path) {
 }
 
 static void testReadGray16(Test& test, const char* path) {
-    PNGReader png(StringBuffer<200>(path).append("gray16.png"));
-    if(png.hasError()) {
+    PNGReader png;
+    if(png.load(StringBuffer<200>(path).append("gray16.png")).has()) {
         test.checkEqual(false, true, "read gray16 error");
         return;
     }
@@ -92,8 +92,8 @@ static void testReadGray16(Test& test, const char* path) {
 }
 
 static void testReadGrayA8(Test& test, const char* path) {
-    PNGReader png(StringBuffer<200>(path).append("graya8.png"));
-    if(png.hasError()) {
+    PNGReader png;
+    if(png.load(StringBuffer<200>(path).append("graya8.png")).has()) {
         test.checkEqual(false, true, "read graya8 error");
         return;
     }
@@ -105,8 +105,8 @@ static void testReadGrayA8(Test& test, const char* path) {
 }
 
 static void testReadGrayA16(Test& test, const char* path) {
-    PNGReader png(StringBuffer<200>(path).append("graya16.png"));
-    if(png.hasError()) {
+    PNGReader png;
+    if(png.load(StringBuffer<200>(path).append("graya16.png")).has()) {
         test.checkEqual(false, true, "read graya16 error");
         return;
     }

+ 22 - 12
wrapper/GL.cpp

@@ -114,6 +114,16 @@ bool GL::printError(const char* message) {
     return false;
 }
 
+Error getError(const char* message) {
+    GLenum error = glGetError();
+    if(error != GL_NO_ERROR) {
+        Error e = {message};
+        e.message.append(": ").append(gluErrorString(error));
+        return e;
+    }
+    return {};
+}
+
 void GL::enableDepthTesting() {
     glEnable(GL_DEPTH_TEST);
 }
@@ -163,17 +173,17 @@ void GL::linkProgram(Program p) {
     glLinkProgram(p);
 }
 
-bool GL::logLinkerError(Program p) {
+Error GL::getLinkerError(Program p) {
     GLint linked;
     glGetProgramiv(p, GL_LINK_STATUS, &linked);
     if(!linked) {
-        Array<char, 1024> log;
+        Array<char, 256> log;
         glGetProgramInfoLog(p, log.getLength(), nullptr, log.begin());
-        LOG_WARNING(StringBuffer<100>("linker log: ")
-                        .append(static_cast<const char*>(log.begin())));
-        return true;
+        Error e = {"linker log: "};
+        e.message.append(static_cast<const char*>(log.begin()));
+        return e;
     }
-    return false;
+    return {};
 }
 
 void GL::deleteShader(Shader s) {
@@ -193,17 +203,17 @@ void GL::compileShader(Shader s, const char* code) {
     glCompileShader(s);
 }
 
-bool GL::logCompileError(Shader s) {
+Error GL::getCompileError(Shader s) {
     GLint compiled;
     glGetShaderiv(s, GL_COMPILE_STATUS, &compiled);
     if(!compiled) {
-        Array<char, 1024> log;
+        Array<char, 256> log;
         glGetShaderInfoLog(s, log.getLength(), nullptr, log.begin());
-        LOG_WARNING(StringBuffer<100>("compiler log: ")
-                        .append(static_cast<const char*>(log.begin())));
-        return true;
+        Error e = {"compiler log: "};
+        e.message.append(static_cast<const char*>(log.begin()));
+        return e;
     }
-    return false;
+    return {};
 }
 
 void GL::useProgram(Program p) {

+ 5 - 2
wrapper/GL.h

@@ -1,6 +1,8 @@
 #ifndef GL_H
 #define GL_H
 
+#include "utils/Error.h"
+
 namespace GL {
     typedef unsigned int Shader;
     typedef unsigned int Program;
@@ -55,6 +57,7 @@ namespace GL {
     };
 
     bool printError(const char* message);
+    Error getError(const char* message);
     void enableDepthTesting();
     void disableDepthTesting();
     void bindMainFramebuffer();
@@ -67,12 +70,12 @@ namespace GL {
     Program createProgram();
     void attachShader(Program p, Shader s);
     void linkProgram(Program p);
-    bool logLinkerError(Program p);
+    Error getLinkerError(Program p);
     void deleteShader(Shader s);
     void deleteProgram(Program p);
     Shader createShader(ShaderType type);
     void compileShader(Shader s, const char* code);
-    bool logCompileError(Shader s);
+    Error getCompileError(Shader s);
     void useProgram(Program p);
     void setMatrix(Program p, const char* name, const float* data);
     void setInt(Program p, const char* name, int data);