MVP for a Pong game

This commit is contained in:
Hartmut Seichter 2023-07-01 22:17:41 +02:00
parent 14343e7fd0
commit e9d0de8cdd
17 changed files with 517 additions and 109 deletions

View file

@ -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

View file

@ -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 <paradiso/vector.hpp>
namespace paradiso {
struct AABB final {
Vector2<float> top_left{};
Vector2<float> 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<float>::make(min_max(0, 0), min_max(0, 1)),
.bottom_right =
Vector2<float>::make(min_max(1, 0), min_max(1, 1))};
}
bool constexpr is_inside(const Vector2<float>& 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

View file

@ -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<RGBA> data{}; //!< data storage
std::uint64_t change_count{};
};
} // namespace paradiso

View file

@ -25,6 +25,7 @@
#include <paradiso/globals.hpp>
#include <algorithm>
namespace paradiso {
@ -84,7 +85,7 @@ template <typename Scalar, typename Derived> 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 <typename Scalar, typename Derived> 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)

View file

@ -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

View file

@ -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

View file

@ -27,19 +27,24 @@
#include <paradiso/geometry.hpp>
#include <functional>
#include <stack>
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<KeyboardInput>;
using on_updatecallback_t = std::function<bool(
Window&)>; //! update needs to return true to continue
using on_resizecallback_t =
std::function<void(Window&)>; //! resize is 'informal'
using on_keyboardcallback_t =
std::function<void(Window&, int, int, int, int)>;
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> impl_;
on_resizecallback_t on_resize_;
on_keyboardcallback_t on_keyboard_;
};
} // namespace paradiso

View file

@ -38,14 +38,14 @@ namespace paradiso {
struct Renderer::impl {
uint64_t change_count{std::numeric_limits<uint64_t>::max()};
uint64_t change_count_texture{std::numeric_limits<uint64_t>::max()};
// below corresponds to GL state
uint32_t vertex_array_obj{};
uint32_t element_buffer_obj{};
std::vector<uint32_t> 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);

View file

@ -22,6 +22,8 @@
*/
#include "paradiso/shader.hpp"
#include "shader_sprite.hpp"
#include "glad/glad.h"
#include <iostream>
@ -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;

View file

@ -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

View file

@ -39,6 +39,8 @@ struct Window::impl {
GLFWwindow* window_ = nullptr;
KeyboardInputStack keyboard_input_{};
std::tuple<int, int> _old_size;
std::tuple<int, int> _old_pos;
@ -79,12 +81,9 @@ struct Window::impl {
int action, int mods) {
Window::impl* impl =
static_cast<Window::impl*>(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<Window::impl>(*this)), on_resize_([](Window&) {}),
on_keyboard_([](Window&, int, int, int, int) {})
{}
: impl_(std::make_unique<Window::impl>(*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