diff --git a/CMakeLists.txt b/CMakeLists.txt index dd327d6..effda97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,4 +21,4 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) add_subdirectory(src) -add_subdirectory(examples/simple) \ No newline at end of file +add_subdirectory(examples) \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..2bdd295 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(simple) +add_subdirectory(pong) \ No newline at end of file diff --git a/examples/pong/CMakeLists.txt b/examples/pong/CMakeLists.txt new file mode 100644 index 0000000..cbdcc48 --- /dev/null +++ b/examples/pong/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_executable(paradiso_pong pong.cpp) + +target_link_libraries(paradiso_pong paradiso_core) \ No newline at end of file diff --git a/examples/pong/pong.cpp b/examples/pong/pong.cpp new file mode 100644 index 0000000..1643d92 --- /dev/null +++ b/examples/pong/pong.cpp @@ -0,0 +1,259 @@ +/** + * paradiso - Paradigmen der Softwareentwicklung + * + * (c) Copyright 2023 Hartmut Seichter + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct PongStage { + + using Vec2 = paradiso::Vector2; + + Vec2 tl{Vec2::make(-1.0f, -1.0f)}; + Vec2 br{Vec2::make(+1.0f, +1.0f)}; + + enum class TouchPoint { None, Left, Right, Bottom, Top }; + + TouchPoint touch(const Vec2& o, float eps = 0.001f) noexcept { + std::unordered_map deltas; + deltas[TouchPoint::Left] = std::abs(o.x() - tl.x()); + deltas[TouchPoint::Right] = std::abs(o.x() - br.x()); + deltas[TouchPoint::Bottom] = std::abs(o.y() - br.y()); + deltas[TouchPoint::Top] = std::abs(o.y() - tl.y()); + + auto min_el = *std::min_element( + std::begin(deltas), std::end(deltas), + [](const auto& l, const auto& r) { return l.second < r.second; }); + + auto t = (min_el.second <= eps) ? min_el.first : TouchPoint::None; + + if (t != TouchPoint::None) { + sprite.bitmap.pixel(0,0) = paradiso::RGBA::white(); + } else { + sprite.bitmap.pixel(0,0) = paradiso::RGBA::black(); + } + + return t; + }; + + // sprite + paradiso::Sprite sprite{ + .bitmap = paradiso::Bitmap::from_data(paradiso::Size{1, 1}, + paradiso::RGBA::from_rgb(0x80,0xFF,0xFF)) + }; + + void draw(const paradiso::Shader& shader) { + renderer.draw(sprite, shader); + } + + paradiso::Renderer renderer{}; +}; + +struct PongPaddle { + + static constexpr float whoopiness = 0.95f; + + // sprite + paradiso::Sprite sprite{ + .bitmap = paradiso::Bitmap::from_data(paradiso::Size{1, 1}, + paradiso::RGBA::white()), + .pivot = paradiso::Vector2::make(0.0f, -0.9f), + .scale = paradiso::Vector2::make(0.25f, 0.01f)}; + + // keyboard handler + void on_keyboard(const paradiso::Window::KeyboardInputStack& input) { + + if (input.size()) { + if (input.top().key == 'A' || input.top().key == 263) { + velocity_horizontal -= 0.01; + } else if (input.top().key == 'D' || input.top().key == 262) { + velocity_horizontal += 0.01; + } + } + } + + void draw(const paradiso::Shader& shader) { + + // update internal state + velocity_horizontal *= whoopiness; + sprite.pivot.x() += velocity_horizontal; + + std::clamp(sprite.pivot.x(), -0.5f, 0.5f); + + // update shader uniforms + shader.set_uniform("pivot", sprite.pivot); + shader.set_uniform("scale", sprite.scale); + shader.set_uniform("rotation", sprite.rotation); + + renderer.draw(sprite, shader); + } + + float velocity_horizontal{}; + + paradiso::Renderer renderer{}; +}; + +struct PongBall { + // sprite + paradiso::Sprite sprite{ + .bitmap = paradiso::Bitmap::from_data(paradiso::Size{1, 1}, + paradiso::RGBA::white()), + .pivot = paradiso::Vector2::make(0.0f, 0.0f), + .scale = paradiso::Vector2::make(0.0125f, 0.0125f), + }; + + void interact(PongStage& stage) { + + auto touch = stage.touch(sprite.pivot); + + switch (touch) { + case PongStage::TouchPoint::Top: + case PongStage::TouchPoint::Bottom: + velocity.y() *= -1; + break; + case PongStage::TouchPoint::Left: + case PongStage::TouchPoint::Right: + velocity.x() *= -1; + break; + default: + break; + } + } + + // interact ball & paddle + void interact(const PongPaddle& paddle) { + const auto& ps = paddle.sprite.scale; + const auto& pp = paddle.sprite.pivot; + + auto left_x = pp.x() - ps.x(); + auto right_x = pp.x() + ps.x(); + + static constexpr float eps{0.01f}; + + if (sprite.pivot.x() >= left_x && sprite.pivot.x() <= right_x && + std::abs(pp.y() - sprite.pivot.y()) < eps) { + + velocity.y() *= -1; + velocity.x() += paddle.velocity_horizontal * paddle.whoopiness; + } + } + + void update() { sprite.pivot += velocity; } + + void draw(const paradiso::Shader& shader) { + + std::clamp(sprite.pivot.x(), -0.5f, 0.5f); + + // update shader uniforms + shader.set_uniform("pivot", sprite.pivot); + shader.set_uniform("scale", sprite.scale); + shader.set_uniform("rotation", sprite.rotation); + + renderer.draw(sprite, shader); + } + + paradiso::Vector2 velocity{}; + + paradiso::Renderer renderer{}; + + constexpr void push(const auto& impulse) noexcept { velocity += impulse; } +}; + +auto main() -> int { + + // Ausgabefenster ... sieht aus als wäre es auf dem Stack + auto window = paradiso::Window(); + + // wir bauen ein Fenster ... + window + .set_size(paradiso::Size{.width = 720, .height = 720}) // ... Grösse + .set_position(paradiso::Point{.x = 100, .y = 100}) // ... Position + .set_title("PardiSO.Pong") // ... Titel + .set_visible(true); // ... und jetzt anzeigen! + + // der Fenster Kontext + auto ctx = paradiso::Context{}; + + // ein Shader (Schattierungsprogramm) + auto shader = paradiso::Shader{}; + + // wir nutzen einen vorgefertigten shader + shader.load_preset(paradiso::Shader::Preset::Sprite); + + auto paddle = PongPaddle{}; + auto ball = PongBall{}; + auto stage = PongStage{}; + + ball.push(paradiso::Vector2::make(0.0001f, -0.0005f)); + + // ein viewport stellt die Sicht der Kamera dar, d.h. bei quadratischen + // Pixeln sollte hier auch eine dementsprechende Grösse eingestellt + // werden + ctx.set_viewport(paradiso::Rectangle{ + .position = paradiso::Point{.x = 0, .y = 0}, + .size = window.client_size() + .maximal_extent() // wir wollen das + // Seitenverhältnis beibehalten + }); + + // nothing beats a classic look + ctx.set_clearcolor(paradiso::RGBA::from_rgb(0x00, 0x00, 0x00)); + + // das update führt den hier mitgegebnen Ausdruck innerhalb der internen + // Updates des Fensters auf. Es wird hier auch explizit ein bool gefordert + // damit das update auch zu jederzeit unterbrochen werden kann + while (window.update([&](paradiso::Window& w) -> bool { + auto me = window.client_size().maximal_extent(); + + ctx.set_viewport(paradiso::Rectangle{ + .position = paradiso::Point{.x = 0, .y = 0}, + .size = me // wir wollen das + // Seitenverhältnis beibehalten + }); + + // hier wird das eigentliche löschen des vorherigen Inhalts ausgelöst + ctx.clear(); + + paddle.on_keyboard(w.keyboard_input()); + + // still here + bool want_close = + (w.keyboard_input().size() && w.keyboard_input().top().key == 'Q'); + + if (!w.keyboard_input().empty()) { + if (w.keyboard_input().top().key == 'R') { + ball.sprite.pivot.x() = 0.0f; + ball.sprite.pivot.y() = 0.9f; + } + } + + ball.interact(stage); + ball.interact(paddle); + + ball.update(); + + stage.draw(shader); + paddle.draw(shader); + ball.draw(shader); + + // ... signalisiere ob wir weitermachen wollen ... + return !want_close; + })) { + }; + + return 0; +} \ No newline at end of file diff --git a/examples/simple/CMakeLists.txt b/examples/simple/CMakeLists.txt index 62f3f94..1cc24a7 100644 --- a/examples/simple/CMakeLists.txt +++ b/examples/simple/CMakeLists.txt @@ -1,16 +1,9 @@ -set(paradiso_src - main.cpp - ) add_executable( - paradiso - ${paradiso_src} + paradiso_simple + simple.cpp ) -target_link_libraries(paradiso +target_link_libraries(paradiso_simple paradiso_core ) - -target_include_directories(paradiso - PRIVATE - lib) \ No newline at end of file diff --git a/examples/simple/main.cpp b/examples/simple/simple.cpp similarity index 60% rename from examples/simple/main.cpp rename to examples/simple/simple.cpp index eb4c1c8..09db5f1 100644 --- a/examples/simple/main.cpp +++ b/examples/simple/simple.cpp @@ -16,66 +16,6 @@ #include #include -void setup_shaders(paradiso::Shader& shader) { - const auto unlit_v = R"( -#version 400 core - -layout (location = 0) in vec3 vertices; -layout (location = 1) in vec3 normals; -layout (location = 2) in vec2 texture_coords; - -// pivot der sprite -uniform vec2 pivot = vec2( 0.0, 0.0 ); -// scale -uniform vec2 scale = vec2( 1.0, 1.0 ); -// rotation -uniform float rotation = 0.2; - -// wir sind natuerlich in homogenenen 3D Koordinaten unterwegs -mat4 mm = mat4( - vec4( scale.x, 0.0, 0.0, 0.0), - vec4( 0.0, scale.y, 0.0, 0.0), - vec4( 0.0, 0.0, 1.0, 0.0), - vec4( pivot, 0.0, 1.0) -); - -float sir = sin(rotation); -float cor = cos(rotation); - -mat4 mr = mat4( - vec4( cor, sir, 0.0, 0.0), - vec4(-sir, cor, 0.0, 0.0), - vec4( 0.0, 0.0, 1.0, 0.0), - vec4( 0.0, 0.0, 0.0, 1.0) - ); - -out vec2 tex_c; // das hier reicht die texturkoordinaten durch - -void main() { - tex_c = texture_coords; // umstaendlich aber notwendig - gl_Position = mm * mr * vec4(vertices, 1.0); // unsere eigentliche shader position -} -)"; - - const auto unlit_f = R"( -#version 400 core - -uniform sampler2D tex_color; // hier ist unsere sprite textur (bitmap) - -in vec2 tex_c; // da sind die texturkoordinaten wieder - -out vec4 frag_color; // das hier wird der output (pixelwert/fragment) - -void main() { - frag_color = texture(tex_color,tex_c); -})"; - - shader.set_source(paradiso::Shader::Type::Vertex, unlit_v); - shader.set_source(paradiso::Shader::Type::Fragment, unlit_f); - - shader.build(); -} - auto main() -> int { // Ausgabefenster ... sieht aus als wäre es auf dem Stack @@ -85,7 +25,7 @@ auto main() -> int { window .set_size(paradiso::Size{.width = 1280, .height = 720}) // ... Grösse .set_position(paradiso::Point{.x = 100, .y = 100}) // ... Position - .set_title("PardiSO") // ... Titel + .set_title("PardiSO.Simple") // ... Titel .set_visible(true); // ... und jetzt anzeigen! // der Fenster Kontext @@ -107,36 +47,39 @@ auto main() -> int { // ein Shader (Schattierungsprogramm) auto shader = paradiso::Shader{}; - // hier werden die Shader Programme geladen, kompiliert usw. - setup_shaders(shader); + // wir nutzen einen vorgefertigten shader + shader.load_preset(paradiso::Shader::Preset::Sprite); // kein schönes Design: dies sind globale Variablen ... uint8_t slider_value = 0xFF; bool want_close{false}; - // eine sehr rudimentäre Eingabebehandlung. Bei vorhandenen - // Eingaben landen diese hier. Wer sich am Design beteiligen - // möchte: hier gibt es viel Potential zur Verbesserung ;) - window.set_keyboardcallback( - [&](auto& w, int key, int scancode, int action, int mods) { - if (key == 'Q' || key == 256) // Q oder ESC beenden das Programm + // hier "vor-deklariert" der Input-handler für unser Demo + auto SimpleKeyboardHandler = + [&](const paradiso::Window::KeyboardInputStack& input) { + // ohne Input kein Handling ... + if (input.empty()) + return; + + if (input.top().key == 'Q' || + input.top().key == 256) // Q oder ESC beenden das Programm want_close = true; - else if (key == 'B') { // kleine Spielerei + else if (input.top().key == 'B') { // kleine Spielerei slider_value += 10; - } else if (key == 'W') { + } else if (input.top().key == 'W') { sprite.pivot.y() += 0.1f; - } else if (key == 'S') { + } else if (input.top().key == 'S') { sprite.pivot.y() -= 0.1f; - } else if (key == 'A') { + } else if (input.top().key == 'A') { sprite.pivot.x() -= 0.1f; - } else if (key == 'D') { + } else if (input.top().key == 'D') { sprite.pivot.x() += 0.1f; - } else if (key == 'P') { + } else if (input.top().key == 'P') { sprite.scale *= 0.9; - } else if (key == 'R') { + } else if (input.top().key == 'R') { sprite.rotation += 0.1; } - }); + }; // das update führt den hier mitgegebnen Ausdruck innerhalb der internen // Updates des Fensters auf. Es wird hier auch explizit ein bool gefordert @@ -152,11 +95,15 @@ auto main() -> int { // Pixeln sollte hier auch eine dementsprechende Grösse eingestellt // werden ctx.set_viewport(paradiso::Rectangle{ + .position = paradiso::Point{.x = 0, .y = 0}, .size = w.client_size().maximal_extent() // wir wollen das // Seitenverhältnis beibehalten }); + // handle keyboard input ... + SimpleKeyboardHandler(w.keyboard_input()); + // hier wird das eigentliche löschen des vorherigen Inhalts ausgelöst ctx.clear(); diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 05e3c49..d437135 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -12,9 +12,11 @@ set(paradiso_srcs src/window.cpp src/renderer.cpp src/context.cpp + src/shader_sprite.hpp ) set(paradiso_incs + include/paradiso/aabb.hpp include/paradiso/bitmap.hpp include/paradiso/geometry.hpp include/paradiso/sprite.hpp diff --git a/src/lib/include/paradiso/aabb.hpp b/src/lib/include/paradiso/aabb.hpp new file mode 100644 index 0000000..8a8b77a --- /dev/null +++ b/src/lib/include/paradiso/aabb.hpp @@ -0,0 +1,61 @@ +/* + * 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. + * + */ + +#ifndef PARADISO_AABB_HPP +#define PARADISO_AABB_HPP + +#include + +namespace paradiso { +struct AABB final { + + Vector2 top_left{}; + Vector2 bottom_right{}; + + static constexpr AABB from_vertices(auto& vertices) noexcept { + + Matrix<2, 2, float> min_max; + min_max.set_slice<2, 1>(0, 0) = vertices.front(); + min_max.set_slice<2, 1>(1, 0) = vertices.front(); + + for (const auto& v : vertices) { + min_max(0, 0) = std::min(min_max(0, 0), v.x()); + min_max(0, 1) = std::min(min_max(0, 1), v.y()); + min_max(1, 0) = std::min(min_max(1, 0), v.x()); + min_max(1, 1) = std::min(min_max(1, 1), v.y()); + } + + return {.top_left = Vector2::make(min_max(0, 0), min_max(0, 1)), + .bottom_right = + Vector2::make(min_max(1, 0), min_max(1, 1))}; + } + + bool constexpr is_inside(const Vector2& v) const { + return (v.x() >= top_left.x() && v.x() <= bottom_right.x() && + v.y() >= top_left.y() && v.y() <= bottom_right.y()); + } +}; + +} // namespace paradiso + +#endif \ No newline at end of file diff --git a/src/lib/include/paradiso/bitmap.hpp b/src/lib/include/paradiso/bitmap.hpp index bcc314a..c95f861 100644 --- a/src/lib/include/paradiso/bitmap.hpp +++ b/src/lib/include/paradiso/bitmap.hpp @@ -60,6 +60,7 @@ struct Bitmap final { * @return reference to itself */ constexpr auto fill(const RGBA& color) noexcept { + change_count++; std::fill(data.begin(), data.end(), color); return *this; } @@ -71,6 +72,7 @@ struct Bitmap final { * @return reference to pixel data at position */ constexpr RGBA& pixel(std::integral auto x, std::integral auto y) { + change_count++; return data[y * size.width + x]; } @@ -85,8 +87,14 @@ struct Bitmap final { return data[y * size.width + x]; } + constexpr void force_change() noexcept + { + change_count++; + } + Size size{.width = 0, .height = 0}; //!< extent of bitmap std::vector data{}; //!< data storage + std::uint64_t change_count{}; }; } // namespace paradiso diff --git a/src/lib/include/paradiso/matrixbase.hpp b/src/lib/include/paradiso/matrixbase.hpp index 56d012e..5e77268 100644 --- a/src/lib/include/paradiso/matrixbase.hpp +++ b/src/lib/include/paradiso/matrixbase.hpp @@ -25,6 +25,7 @@ #include +#include namespace paradiso { @@ -84,7 +85,7 @@ template struct MatrixBase { constexpr void normalize() { *this /= this->norm(); } - static constexpr Scalar dot(const Derived& a, const Derived& b) { + static constexpr Scalar dot(const auto& a, const auto& b) { return std::inner_product(std::begin(a), std::end(a), std::begin(b), Scalar{0}); } @@ -111,6 +112,13 @@ template struct MatrixBase { e -= b; } + constexpr void operator+=(const Derived& b) { + std::transform((*this).begin(),(*this).end(),b.begin(),(*this).begin(),std::plus<>()); + } + constexpr void operator-=(const Derived& b) { + std::transform((*this).begin(),(*this).end(),b.begin(),(*this).begin(),std::minus<>()); + } + constexpr const Derived operator*(const Scalar& b) const { Derived r(derived()); for (auto& e : r) diff --git a/src/lib/include/paradiso/rgba.hpp b/src/lib/include/paradiso/rgba.hpp index 28c6987..0ab6ec4 100644 --- a/src/lib/include/paradiso/rgba.hpp +++ b/src/lib/include/paradiso/rgba.hpp @@ -79,6 +79,14 @@ struct RGBA final { pixel = (pixel & 0xFFFFFF00) | v; return *this; } + + static constexpr RGBA white() noexcept { + return RGBA::from_rgb(0xFF, 0xFF, 0xFF); + } + + static constexpr RGBA black() noexcept { + return RGBA::from_rgb(0x00, 0x00, 0x00); + } }; } // namespace paradiso diff --git a/src/lib/include/paradiso/shader.hpp b/src/lib/include/paradiso/shader.hpp index 9382fc9..549fe7d 100644 --- a/src/lib/include/paradiso/shader.hpp +++ b/src/lib/include/paradiso/shader.hpp @@ -42,9 +42,13 @@ struct Shader final { enum class Type { Vertex, Fragment, Geometry, Compute }; + enum class Preset { Sprite }; + void set_source(Type t, const std::string& c) { source_[t] = c; } std::string source(Type t) const { return source_.at(t); } + bool load_preset(Preset preset); + bool build(); bool ready() const; @@ -54,9 +58,11 @@ struct Shader final { const Shader& set_uniform_at_location(int location, float v) const; //!< sets a float in a shader + const Shader& set_uniform_at_location( int location, uint32_t v) const; //!< sets a 32bit unsigned in a shader + const Shader& set_uniform_at_location( int location, int32_t v) const; //!< sets a 32bit signed in a shader diff --git a/src/lib/include/paradiso/window.hpp b/src/lib/include/paradiso/window.hpp index 0b81873..6613b99 100644 --- a/src/lib/include/paradiso/window.hpp +++ b/src/lib/include/paradiso/window.hpp @@ -27,19 +27,24 @@ #include #include +#include namespace paradiso { +struct KeyboardInput final { + int key{}, scancode{}, action{}, mods{}; +}; + struct Window final { + using Size = paradiso::Size; using Position = paradiso::Point; + using KeyboardInputStack = std::stack; using on_updatecallback_t = std::function; //! update needs to return true to continue using on_resizecallback_t = std::function; //! resize is 'informal' - using on_keyboardcallback_t = - std::function; Window(); ~Window(); @@ -66,17 +71,20 @@ struct Window final { Window& set_fullscreen(bool use_fullscreen); void set_resizecallback(on_resizecallback_t f) { on_resize_ = f; } - void set_keyboardcallback(on_keyboardcallback_t f) { on_keyboard_ = f; } + bool visible() const; Window& set_visible(bool is_visible); + const KeyboardInputStack& keyboard_input() const; + private: struct impl; std::unique_ptr impl_; on_resizecallback_t on_resize_; - on_keyboardcallback_t on_keyboard_; + + }; } // namespace paradiso diff --git a/src/lib/src/renderer.cpp b/src/lib/src/renderer.cpp index 2898c0d..eccb289 100644 --- a/src/lib/src/renderer.cpp +++ b/src/lib/src/renderer.cpp @@ -38,14 +38,14 @@ namespace paradiso { struct Renderer::impl { uint64_t change_count{std::numeric_limits::max()}; + uint64_t change_count_texture{std::numeric_limits::max()}; + // below corresponds to GL state uint32_t vertex_array_obj{}; uint32_t element_buffer_obj{}; std::vector vertex_buffer_ob{}; uint32_t texture_id{}; - impl() = default; - ~impl() { release(); } bool ready() const { @@ -153,7 +153,7 @@ struct Renderer::impl { glGenerateMipmap(GL_TEXTURE_2D); glGenerateTextureMipmap(texture_id); - } else { + } else if (image.change_count != change_count_texture) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture_id); @@ -169,6 +169,8 @@ struct Renderer::impl { GL_BGRA, // format GL_UNSIGNED_BYTE, // type image.data.data()); // pointer + + change_count_texture = image.change_count; } glBindTexture(GL_TEXTURE_2D, 0); diff --git a/src/lib/src/shader.cpp b/src/lib/src/shader.cpp index 47d5070..7ed3fa5 100644 --- a/src/lib/src/shader.cpp +++ b/src/lib/src/shader.cpp @@ -22,6 +22,8 @@ */ #include "paradiso/shader.hpp" +#include "shader_sprite.hpp" + #include "glad/glad.h" #include @@ -199,6 +201,18 @@ Shader::~Shader() {} bool Shader::ready() const { return impl_->is_valid(); } +bool Shader::load_preset(Preset preset) { + switch (preset) { + case Preset::Sprite: + this->set_source(paradiso::Shader::Type::Vertex, sprite_unlit_vertex); + this->set_source(paradiso::Shader::Type::Fragment, + sprite_unlit_fragment); + break; + } + + return build(); +} + const Shader& Shader::set_uniform_at_location(int location, float v) const { impl_->bind(location, v); return *this; diff --git a/src/lib/src/shader_sprite.hpp b/src/lib/src/shader_sprite.hpp new file mode 100644 index 0000000..fc4c15d --- /dev/null +++ b/src/lib/src/shader_sprite.hpp @@ -0,0 +1,83 @@ +/* + * 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. + * + */ +#ifndef PARADISO_SHADER_SPRITE_HPP +#define PARADISO_SHADER_SPRITE_HPP + +namespace paradiso { + +static constexpr auto sprite_unlit_vertex = R"( +#version 400 core + +layout (location = 0) in vec3 vertices; +layout (location = 1) in vec3 normals; +layout (location = 2) in vec2 texture_coords; + +// pivot der sprite +uniform vec2 pivot = vec2( 0.0, 0.0 ); +// scale +uniform vec2 scale = vec2( 1.0, 1.0 ); +// rotation +uniform float rotation = 0.0; + +// wir sind natuerlich in homogenenen 3D Koordinaten unterwegs +mat4 mm = mat4( + vec4( scale.x, 0.0, 0.0, 0.0), + vec4( 0.0, scale.y, 0.0, 0.0), + vec4( 0.0, 0.0, 1.0, 0.0), + vec4( pivot, 0.0, 1.0) +); + +float sir = sin(rotation); +float cor = cos(rotation); + +mat4 mr = mat4( + vec4( cor, sir, 0.0, 0.0), + vec4(-sir, cor, 0.0, 0.0), + vec4( 0.0, 0.0, 1.0, 0.0), + vec4( 0.0, 0.0, 0.0, 1.0) + ); + +out vec2 tex_c; // das hier reicht die texturkoordinaten durch + +void main() { + tex_c = texture_coords; // umstaendlich aber notwendig + gl_Position = mm * mr * vec4(vertices, 1.0); // unsere eigentliche shader transformation +} +)"; + + +static constexpr auto sprite_unlit_fragment = R"( +#version 400 core + +uniform sampler2D tex_color; // hier ist unsere sprite textur (bitmap) + +in vec2 tex_c; // da sind die texturkoordinaten wieder + +out vec4 frag_color; // das hier wird der output (pixelwert/fragment) + +void main() { + frag_color = texture(tex_color,tex_c); +})"; +} // namespace paradiso + +#endif \ No newline at end of file diff --git a/src/lib/src/window.cpp b/src/lib/src/window.cpp index cf5e0ef..4962439 100644 --- a/src/lib/src/window.cpp +++ b/src/lib/src/window.cpp @@ -39,6 +39,8 @@ struct Window::impl { GLFWwindow* window_ = nullptr; + KeyboardInputStack keyboard_input_{}; + std::tuple _old_size; std::tuple _old_pos; @@ -79,12 +81,9 @@ struct Window::impl { int action, int mods) { Window::impl* impl = static_cast(glfwGetWindowUserPointer(window)); - impl->parent_.get().on_keyboard_(impl->parent_, key, scancode, action, - mods); - // input::get()._key_code = scancode; - // input::get()._key_pressed = action; - // action 0,1,2 - // std::cout << __FUNCTION__ << action << std::endl; + + impl->keyboard_input_.emplace(KeyboardInput{ + .key = key, .scancode = scancode, .action = action, .mods = mods}); } static void charmods_callback(GLFWwindow* window, unsigned int codepoint, @@ -179,9 +178,10 @@ struct Window::impl { bool update(Window::on_updatecallback_t cb) { if (window_ && !glfwWindowShouldClose(window_)) { - // TODO lock and unlock the current input system to allow for late - // events coming in - // input::get().reset(); + + // delete events + paradiso::Window::KeyboardInputStack{}.swap(keyboard_input_); + // get new events glfwPollEvents(); @@ -263,10 +263,8 @@ struct Window::impl { // // Window::Window() - : impl_(std::make_unique(*this)), on_resize_([](Window&) {}), - on_keyboard_([](Window&, int, int, int, int) {}) - -{} + : impl_(std::make_unique(*this)), on_resize_([](Window&) {}) { +} Window::~Window() {} @@ -308,4 +306,9 @@ Window& Window::set_title(std::string_view title) { impl_->set_title(title); return *this; } +const Window::KeyboardInputStack& Window::keyboard_input() const { + return impl_->keyboard_input_; +} + + } // namespace paradiso