forked from Hartmut/paradiso
we can see a quad
This commit is contained in:
parent
612677c52d
commit
ddc0f85805
10 changed files with 261 additions and 140 deletions
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
|
@ -130,5 +130,13 @@
|
|||
"program": "cpp",
|
||||
"shape": "cpp",
|
||||
"observer_ptr": "cpp"
|
||||
}
|
||||
},
|
||||
"spellright.language": [
|
||||
"en_US"
|
||||
],
|
||||
"spellright.documentTypes": [
|
||||
"markdown",
|
||||
"latex",
|
||||
"plaintext"
|
||||
]
|
||||
}
|
7
LICENSE.md
Normal file
7
LICENSE.md
Normal file
|
@ -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.
|
0
README.de.md
Normal file
0
README.de.md
Normal file
10
README.md
Normal file
10
README.md
Normal file
|
@ -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
|
|
@ -7,13 +7,24 @@
|
|||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
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<RGBA>{size.area()}};
|
||||
}
|
||||
|
||||
template <typename... Arguments>
|
||||
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;
|
||||
|
|
|
@ -8,18 +8,19 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace paradiso {
|
||||
|
||||
struct Renderer::impl {
|
||||
|
||||
uint64_t change_count{};
|
||||
uint64_t change_count{ std::numeric_limits<uint64_t>::max() };
|
||||
|
||||
uint32_t vertex_array_obj{};
|
||||
uint32_t element_buffer_obj{};
|
||||
std::vector<uint32_t> 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::impl>()) {}
|
||||
|
||||
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
|
||||
|
|
|
@ -11,6 +11,7 @@ struct Sprite;
|
|||
* @brief a Renderer2D for sprites
|
||||
*/
|
||||
struct Renderer final {
|
||||
|
||||
Renderer();
|
||||
~Renderer();
|
||||
Renderer(const Renderer&) = delete;
|
||||
|
|
|
@ -3,61 +3,61 @@
|
|||
|
||||
#include "globals.hpp"
|
||||
|
||||
namespace paradiso
|
||||
{
|
||||
struct RGBA final
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
constexpr void to_float(float rgba_f[4]) const noexcept {
|
||||
rgba_f[0] = static_cast<float>(this->red()) / 0xFF;
|
||||
rgba_f[1] = static_cast<float>(this->green()) / 0xFF;
|
||||
rgba_f[2] = static_cast<float>(this->blue()) / 0xFF;
|
||||
rgba_f[3] = static_cast<float>(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 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
|
||||
{
|
||||
constexpr RGBA& set_red(uint8_t v) noexcept {
|
||||
pixel = (pixel & 0x00FFFFFF) | (v << 24);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr RGBA &set_green(uint8_t v) noexcept
|
||||
{
|
||||
constexpr RGBA& set_green(uint8_t v) noexcept {
|
||||
pixel = (pixel & 0xFF00FFFF) | (v << 16);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr RGBA &set_blue(uint8_t v) noexcept
|
||||
{
|
||||
constexpr RGBA& set_blue(uint8_t v) noexcept {
|
||||
pixel = (pixel & 0xFFFF00FF) | (v << 8);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr RGBA &set_alpha(uint8_t v) noexcept
|
||||
{
|
||||
constexpr RGBA& set_alpha(uint8_t v) noexcept {
|
||||
pixel = (pixel & 0xFFFFFF00) | v;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace paradiso
|
||||
|
||||
#endif
|
|
@ -3,30 +3,22 @@
|
|||
|
||||
#include "globals.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
namespace paradiso
|
||||
{
|
||||
namespace paradiso {
|
||||
|
||||
struct Shader final
|
||||
{
|
||||
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); }
|
||||
|
@ -37,9 +29,14 @@ namespace paradiso
|
|||
|
||||
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
|
||||
|
@ -53,19 +50,22 @@ namespace paradiso
|
|||
* @param name of the uniform
|
||||
* @return true if found
|
||||
*/
|
||||
bool has_uniform(std::string const &name) const { return uniform_location(name) >= 0; }
|
||||
bool has_uniform(std::string const& name) const {
|
||||
return uniform_location(name) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets data of the
|
||||
*/
|
||||
template <typename T>
|
||||
Shader &set_uniform(std::string const &name, T &&value)
|
||||
{
|
||||
return set_uniform_at_location(uniform_location(name), std::forward<T>(value));
|
||||
Shader& set_uniform(std::string const& name, T&& value) {
|
||||
return set_uniform_at_location(uniform_location(name),
|
||||
std::forward<T>(value));
|
||||
}
|
||||
|
||||
|
||||
using uniform_t = std::variant<bool, int, float, double /*,vector2f,vector3f,vector4f,matrix4x4f*/>;
|
||||
using uniform_t =
|
||||
std::variant<bool, int, float,
|
||||
double /*,vector2f,vector3f,vector4f,matrix4x4f*/>;
|
||||
using uniform_entry_t = std::tuple<std::string, uniform_t, int>;
|
||||
|
||||
using uniform_cache_t = std::vector<uniform_entry_t>;
|
||||
|
@ -81,6 +81,6 @@ namespace paradiso
|
|||
std::unique_ptr<impl> impl_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace paradiso
|
||||
|
||||
#endif
|
||||
|
|
76
src/main.cpp
76
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 <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
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();
|
||||
|
||||
|
@ -33,24 +80,39 @@ auto main() -> int {
|
|||
//
|
||||
};
|
||||
|
||||
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;
|
||||
})) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue