diff --git a/.vscode/settings.json b/.vscode/settings.json index 8fe7664..5fc1671 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -130,5 +130,13 @@ "program": "cpp", "shape": "cpp", "observer_ptr": "cpp" - } + }, + "spellright.language": [ + "en_US" + ], + "spellright.documentTypes": [ + "markdown", + "latex", + "plaintext" + ] } \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c856ac5 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2023 Hartmut Seichter + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.de.md b/README.de.md new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..80e092c --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# ParadisSO - a minimal 2D rendering engine for + +**ParadiSO** was conceived as a heavily stripped down version of my pixwerx engine. It is a minimalistic 2D graphics engine for educational purposes. It uses modern C++ and a datadriven design, but no ECS. + +## Educational + +* plenty of mix and match of concepts and paradigms to write expressive but concise code +* math code is eager evaluation but constexpr to compensate overheads +* it hides old-style APIs behind a renovated facade +* it leans heavily on the STL and its algorithms \ No newline at end of file diff --git a/src/lib/bitmap.hpp b/src/lib/bitmap.hpp index d897c44..58128e7 100644 --- a/src/lib/bitmap.hpp +++ b/src/lib/bitmap.hpp @@ -7,13 +7,24 @@ #include #include +#include namespace paradiso { struct Bitmap final { - constexpr static Bitmap create(Size size) noexcept { + + constexpr static Bitmap empty(Size size) noexcept { return {.size = size, .data = std::vector{size.area()}}; } + template + static constexpr Bitmap from_data(Size size, Arguments... values) noexcept { + assert(sizeof...(Arguments) == size.height * size.width); + return { + .size = size, + .data = {values...} + }; + } + constexpr auto fill(const RGBA& color) noexcept { std::fill(data.begin(), data.end(), color); return *this; diff --git a/src/lib/renderer.cpp b/src/lib/renderer.cpp index 0340dde..c1b7957 100644 --- a/src/lib/renderer.cpp +++ b/src/lib/renderer.cpp @@ -8,18 +8,19 @@ #include #include #include +#include +#include namespace paradiso { struct Renderer::impl { - uint64_t change_count{}; + uint64_t change_count{ std::numeric_limits::max() }; uint32_t vertex_array_obj{}; uint32_t element_buffer_obj{}; std::vector vertex_buffer_ob{}; uint32_t texture_id{}; - GLint _mesh_elements = {0}; impl() = default; @@ -37,11 +38,10 @@ struct Renderer::impl { void unbind_texture() { glBindTexture(GL_TEXTURE_2D, 0); } - bool build(const Sprite& s) { + bool build(const Sprite& sprite) { // reset if the Renderer already in use if (ready()) release(); - // glGenVertexArrays(1, &vertex_array_obj); glBindVertexArray(vertex_array_obj); @@ -52,7 +52,7 @@ struct Renderer::impl { // indices -> elements glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_obj); glBufferData(GL_ELEMENT_ARRAY_BUFFER, - s.indices.size() * sizeof(uint32_t), s.indices.data(), + sprite.indices.size() * sizeof(uint32_t), sprite.indices.data(), GL_STATIC_DRAW); vertex_buffer_ob.resize(vertex_buffer_ob.size() + 1); @@ -62,8 +62,8 @@ struct Renderer::impl { glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_ob.back()); glVertexAttribPointer(vertex_buffer_ob.size() - 1, 3, GL_FLOAT, GL_FALSE, 0, nullptr); - glBufferData(GL_ARRAY_BUFFER, s.vertices.size() * sizeof(float) * 3, - s.vertices.data(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sprite.vertices.size() * sizeof(float) * 3, + sprite.vertices.data(), GL_STATIC_DRAW); glEnableVertexAttribArray(vertex_buffer_ob.size() - 1); vertex_buffer_ob.resize(vertex_buffer_ob.size() + 1); @@ -73,8 +73,8 @@ struct Renderer::impl { glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_ob.back()); glVertexAttribPointer(vertex_buffer_ob.size() - 1, 3, GL_FLOAT, GL_FALSE, 0, nullptr); - glBufferData(GL_ARRAY_BUFFER, s.normals.size() * sizeof(float) * 3, - s.normals.data(), GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sprite.normals.size() * sizeof(float) * 3, + sprite.normals.data(), GL_STATIC_DRAW); glEnableVertexAttribArray(vertex_buffer_ob.size() - 1); vertex_buffer_ob.resize(vertex_buffer_ob.size() + 1); @@ -85,13 +85,18 @@ struct Renderer::impl { glVertexAttribPointer(vertex_buffer_ob.size() - 1, 2, GL_FLOAT, GL_FALSE, 0, nullptr); glBufferData(GL_ARRAY_BUFFER, - s.texture_coordinates.size() * sizeof(float) * 2, - s.texture_coordinates.data(), GL_STATIC_DRAW); + sprite.texture_coordinates.size() * sizeof(float) * 2, + sprite.texture_coordinates.data(), GL_STATIC_DRAW); glEnableVertexAttribArray(vertex_buffer_ob.size() - 1); // stop binding glBindVertexArray(0); + + + + change_count = sprite.change_count; + return ready(); } @@ -156,14 +161,23 @@ struct Renderer::impl { glDeleteVertexArrays(1, &vertex_array_obj); - glDeleteTextures(1, &texture_id); + // glDeleteTextures(1, &texture_id); } - void draw() { + void just_draw(const Sprite& sprite) + { glBindVertexArray(vertex_array_obj); - glDrawElements(GL_TRIANGLES, _mesh_elements, GL_UNSIGNED_INT, nullptr); + glDrawElements(GL_TRIANGLES, sprite.indices.size(), GL_UNSIGNED_INT, nullptr); glBindVertexArray(0); } + + static void fetch_errors(std::string_view tag) { + std::cout << tag << '\n'; + GLenum e{}; + while ((e = glGetError()) != GL_NO_ERROR) { + std::cerr << '\t' << " OpenGL error 0x" << std::hex << e << '\n'; + } + } }; // @@ -172,19 +186,27 @@ struct Renderer::impl { Renderer::Renderer() : impl_(std::make_unique()) {} -Renderer::~Renderer() { -} +Renderer::~Renderer() {} bool Renderer::ready() const { return impl_->ready(); } -bool Renderer::draw(const Sprite& m) { return impl_->build(m); } +bool Renderer::draw(const Sprite& m) +{ + if (!impl_->ready() || m.change_count != impl_->change_count) + { + impl_->build(m); + } -// void Renderer::release() { impl_->release(); } -// void renderer::draw() { impl_->draw(); } + if (impl_->ready() && m.change_count == impl_->change_count) { -// uint64_t Renderer::change_count() const { return impl_->change_count; } + impl_->just_draw(m); -// void Renderer::set_change_count(uint64_t n) { impl_->change_count = n; } + return true; + } + + return false; + +} } // namespace paradiso diff --git a/src/lib/renderer.hpp b/src/lib/renderer.hpp index 7e926f2..e7e7e41 100644 --- a/src/lib/renderer.hpp +++ b/src/lib/renderer.hpp @@ -11,6 +11,7 @@ struct Sprite; * @brief a Renderer2D for sprites */ struct Renderer final { + Renderer(); ~Renderer(); Renderer(const Renderer&) = delete; diff --git a/src/lib/rgba.hpp b/src/lib/rgba.hpp index 5366ae4..f943066 100644 --- a/src/lib/rgba.hpp +++ b/src/lib/rgba.hpp @@ -3,61 +3,61 @@ #include "globals.hpp" -namespace paradiso -{ - struct RGBA final - { - using value_type = std::uint32_t; - value_type pixel{0x0}; +namespace paradiso { +struct RGBA final { + using value_type = std::uint32_t; + value_type pixel{0x0}; - static constexpr RGBA from_rgb(uint8_t red, uint8_t green, uint8_t blue) noexcept - { - return from_rgba(red, green, blue, 0xFF); - } + static constexpr RGBA from_rgb(uint8_t red, uint8_t green, + uint8_t blue) noexcept { + return from_rgba(red, green, blue, 0xFF); + } - static constexpr RGBA from_rgba(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) noexcept - { - return { - .pixel = (value_type)(((((red << 8) | green) << 8) | blue) << 8) | alpha}; - } + static constexpr RGBA from_rgba(uint8_t red, uint8_t green, uint8_t blue, + uint8_t alpha) noexcept { + return {.pixel = + (value_type)(((((red << 8) | green) << 8) | blue) << 8) | + alpha}; + } - constexpr void to_float(float rgba_f[4]) const noexcept - { - rgba_f[0] = static_cast(this->red()) / 0xFF; - rgba_f[1] = static_cast(this->green()) / 0xFF; - rgba_f[2] = static_cast(this->blue()) / 0xFF; - rgba_f[3] = static_cast(this->alpha()) / 0xFF; - } + constexpr void to_float(float rgba_f[4]) const noexcept { + rgba_f[0] = static_cast(this->red()) / 0xFF; + rgba_f[1] = static_cast(this->green()) / 0xFF; + rgba_f[2] = static_cast(this->blue()) / 0xFF; + rgba_f[3] = static_cast(this->alpha()) / 0xFF; + } - constexpr uint8_t red() const noexcept { return (pixel & 0xFF000000) >> 24; } - constexpr uint8_t green() const noexcept { return uint8_t((pixel & 0xFF0000) >> 16); } - constexpr uint8_t blue() const noexcept { return uint8_t((pixel & 0xFF00) >> 8); } - constexpr uint8_t alpha() const noexcept { return uint8_t(pixel & 0xFF); } + constexpr uint8_t red() const noexcept { + return (pixel & 0xFF000000) >> 24; + } + constexpr uint8_t green() const noexcept { + return uint8_t((pixel & 0xFF0000) >> 16); + } + constexpr uint8_t blue() const noexcept { + return uint8_t((pixel & 0xFF00) >> 8); + } + constexpr uint8_t alpha() const noexcept { return uint8_t(pixel & 0xFF); } - constexpr RGBA &set_red(uint8_t v) noexcept - { - pixel = (pixel & 0x00FFFFFF) | (v << 24); - return *this; - } + constexpr RGBA& set_red(uint8_t v) noexcept { + pixel = (pixel & 0x00FFFFFF) | (v << 24); + return *this; + } - constexpr RGBA &set_green(uint8_t v) noexcept - { - pixel = (pixel & 0xFF00FFFF) | (v << 16); - return *this; - } + constexpr RGBA& set_green(uint8_t v) noexcept { + pixel = (pixel & 0xFF00FFFF) | (v << 16); + return *this; + } - constexpr RGBA &set_blue(uint8_t v) noexcept - { - pixel = (pixel & 0xFFFF00FF) | (v << 8); - return *this; - } + constexpr RGBA& set_blue(uint8_t v) noexcept { + pixel = (pixel & 0xFFFF00FF) | (v << 8); + return *this; + } - constexpr RGBA &set_alpha(uint8_t v) noexcept - { - pixel = (pixel & 0xFFFFFF00) | v; - return *this; - } - }; -} + constexpr RGBA& set_alpha(uint8_t v) noexcept { + pixel = (pixel & 0xFFFFFF00) | v; + return *this; + } +}; +} // namespace paradiso #endif \ No newline at end of file diff --git a/src/lib/shader.hpp b/src/lib/shader.hpp index 09ec59a..aa28a62 100644 --- a/src/lib/shader.hpp +++ b/src/lib/shader.hpp @@ -3,84 +3,84 @@ #include "globals.hpp" +#include +#include +#include #include #include -#include #include -#include -#include -namespace paradiso -{ +namespace paradiso { - struct Shader final - { - Shader(); - ~Shader(); - Shader(const Shader &) = delete; - Shader(Shader &&) = default; +struct Shader final { + Shader(); + ~Shader(); + Shader(const Shader&) = delete; + Shader(Shader&&) = default; - enum class Type - { - Vertex, - Fragment, - Geometry, - Compute - }; + enum class Type { Vertex, Fragment, Geometry, Compute }; - void set_source(Type t, const std::string &c) { source_[t] = c; } - std::string source(Type t) const { return source_.at(t); } + void set_source(Type t, const std::string& c) { source_[t] = c; } + std::string source(Type t) const { return source_.at(t); } - bool ready() const; + bool ready() const; - bool build(); + bool build(); - void use(); + void use(); - Shader &set_uniform_at_location(int location, float v); //!< sets a float in a shader - Shader &set_uniform_at_location(int location, uint32_t v); //!< sets a 32bit unsigned in a shader - Shader &set_uniform_at_location(int location, int32_t v); //!< sets a 32bit signed in a shader + Shader& set_uniform_at_location(int location, + float v); //!< sets a float in a shader + Shader& + set_uniform_at_location(int location, + uint32_t v); //!< sets a 32bit unsigned in a shader + Shader& + set_uniform_at_location(int location, + int32_t v); //!< sets a 32bit signed in a shader - /** - * @brief retrieves the position of a uniform - * @param name of the uniform - * @return position of the uniform or negative if it doesn't exist - */ - int uniform_location(std::string const &name) const; + /** + * @brief retrieves the position of a uniform + * @param name of the uniform + * @return position of the uniform or negative if it doesn't exist + */ + int uniform_location(std::string const& name) const; - /** - * @brief check if a uniform with the given name exists - * @param name of the uniform - * @return true if found - */ - bool has_uniform(std::string const &name) const { return uniform_location(name) >= 0; } + /** + * @brief check if a uniform with the given name exists + * @param name of the uniform + * @return true if found + */ + bool has_uniform(std::string const& name) const { + return uniform_location(name) >= 0; + } - /** - * sets data of the - */ - template - Shader &set_uniform(std::string const &name, T &&value) - { - return set_uniform_at_location(uniform_location(name), std::forward(value)); - } + /** + * sets data of the + */ + template + Shader& set_uniform(std::string const& name, T&& value) { + return set_uniform_at_location(uniform_location(name), + std::forward(value)); + } + using uniform_t = + std::variant; + using uniform_entry_t = std::tuple; - using uniform_t = std::variant; - using uniform_entry_t = std::tuple; + using uniform_cache_t = std::vector; - using uniform_cache_t = std::vector; + void set_uniforms(uniform_cache_t c); - void set_uniforms(uniform_cache_t c); + uint32_t native_handle() const; - uint32_t native_handle() const; + private: + std::unordered_map source_; - private: - std::unordered_map source_; + struct impl; + std::unique_ptr impl_; +}; - struct impl; - std::unique_ptr impl_; - }; - -} +} // namespace paradiso #endif diff --git a/src/main.cpp b/src/main.cpp index 77e740d..895b80f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,17 +8,64 @@ #include "bitmap.hpp" #include "context.hpp" #include "geometry.hpp" +#include "renderer.hpp" +#include "shader.hpp" #include "sprite.hpp" #include "window.hpp" #include #include +void setup_shaders(paradiso::Shader& shader) { + const auto unlit_v = R"( +#version 330 core + +layout (location = 0) in vec3 vertices; +layout (location = 1) in vec3 normals; +layout (location = 2) in vec2 texture_coords; + +// uniform mat4 model; +// uniform mat4 view; +// uniform mat4 projection; + +out vec2 tex_c; + +void main() { + tex_c = texture_coords; + // gl_Position = projection * view * model * vec4(vertices, 1.0); + gl_Position = vec4(vertices, 1.0); +} +)"; + + const auto unlit_f = R"( +#version 330 core + +uniform vec4 color = vec4(1.0, 0.0, 0.0, 1.0); +uniform sampler2D tex_color; + +in vec2 tex_c; + +out vec4 frag_color; + +void main() { + frag_color = texture(tex_color,tex_c); // * color; +})"; + + shader.set_source(paradiso::Shader::Type::Vertex, unlit_v); + shader.set_source(paradiso::Shader::Type::Fragment, unlit_f); + + shader.build(); +} + auto main() -> int { + auto checker_board = paradiso::Bitmap::empty(paradiso::Size{4, 4}); - auto checker_board = - + auto checker_board_2 = paradiso::Bitmap::from_data( + paradiso::Size{2, 2}, paradiso::RGBA::from_rgb(0x00, 0xFF, 0x00), + paradiso::RGBA::from_rgb(0x00, 0xFF, 0x00), + paradiso::RGBA::from_rgb(0x00, 0xFF, 0x00), + paradiso::RGBA::from_rgb(0x00, 0xFF, 0x00)); auto window = paradiso::Window(); @@ -29,28 +76,43 @@ auto main() -> int { auto ctx = paradiso::Context{}; - auto sprite = paradiso::Sprite { - // + auto sprite = paradiso::Sprite{ + // }; - + + auto renderer = paradiso::Renderer{}; + + auto shader = paradiso::Shader{}; + + setup_shaders(shader); + + uint8_t green_slider = 0x00; bool want_close{false}; + int frame_counter = 10; window.set_keyboardcallback( [&](auto& w, int key, int scancode, int action, int mods) { if (key == 'Q' || key == 256) want_close = true; + else if (key == 'A' ) { + green_slider += 10; + } }); while (window.update([&](auto& w) -> bool { - - static uint8_t green_slider = 0x00; + ctx.set_clearcolor(paradiso::RGBA::from_rgb(0xFF, green_slider, 0x00)); - - - + ctx.set_viewport(paradiso::Rectangle{ + .size = w.client_size() + }); ctx.clear(); - green_slider++; + + shader.use(); + + renderer.draw(sprite); + + // if (frame_counter-- == 0) want_close = true; return !want_close; })) {