stripped down all pixwerx code to a minimal istic subset

This commit is contained in:
Hartmut Seichter 2023-06-30 20:41:02 +02:00
commit 612677c52d
398 changed files with 164811 additions and 0 deletions

36
src/lib/bitmap.hpp Normal file
View file

@ -0,0 +1,36 @@
#ifndef PARADISO_BITMAP_HPP
#define PARADISO_BITMAP_HPP
#include "geometry.hpp"
#include "globals.hpp"
#include "rgba.hpp"
#include <type_traits>
#include <vector>
namespace paradiso {
struct Bitmap final {
constexpr static Bitmap create(Size size) noexcept {
return {.size = size, .data = std::vector<RGBA>{size.area()}};
}
constexpr auto fill(const RGBA& color) noexcept {
std::fill(data.begin(), data.end(), color);
return *this;
}
constexpr RGBA& pixel(std::integral auto x, std::integral auto y) {
return data[y * size.width + x];
}
constexpr const RGBA& pixel(std::integral auto x,
std::integral auto y) const {
return data[y * size.width + x];
}
Size size{.width = 0, .height = 0};
std::vector<RGBA> data{};
};
} // namespace paradiso
#endif

76
src/lib/context.cpp Normal file
View file

@ -0,0 +1,76 @@
#include "context.hpp"
#include "glad/glad.h"
#include "geometry.hpp"
namespace paradiso {
//
// Context::impl
//
struct Context::impl {
RGBA clear_color_{};
void set_viewport(const Rectangle& v) {
glViewport(v.position.x, v.position.y, v.size.width, v.size.height);
}
const Rectangle viewport() {
float _viewport[4] = {0, 0, 1, 1};
glGetFloatv(GL_VIEWPORT, &_viewport[0]);
return {.position = Point{.x = Point::value_type(_viewport[0]),
.y = Point::value_type(_viewport[1])},
.size = Size{.width = Size::value_type(_viewport[2]),
.height = Size::value_type(_viewport[3])}};
}
void clear() {
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
float rgba_f[4] = {0, 0, 0, 0};
clear_color_.to_float(rgba_f);
glClearColor(rgba_f[0], rgba_f[1], rgba_f[2], rgba_f[3]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
uint32_t get_error() const { return glGetError(); }
};
//
// Context
//
Context::Context() : impl_(std::make_unique<impl>()) {}
Context::~Context() {}
Context& Context::set_blend() { return *this; }
Context& Context::set_viewport(const Rectangle& v) {
impl_->set_viewport(v);
return *this;
}
Rectangle Context::viewport() const { return impl_->viewport(); }
void Context::clear() { impl_->clear(); }
u_int32_t Context::get_error() const { return impl_->get_error(); }
RGBA Context::clearcolor() const { return impl_->clear_color_; }
Context& Context::set_clearcolor(const RGBA& clear_color) {
impl_->clear_color_ = clear_color;
return *this;
}
} // namespace paradiso

64
src/lib/context.hpp Normal file
View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 1999-2021 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_CONTEXT_HPP
#define PARADISO_CONTEXT_HPP
#include "geometry.hpp"
#include "rgba.hpp"
#include <memory>
namespace paradiso {
struct Context final {
Context();
~Context();
Context& set_blend();
Context& set_depth();
Context& set_viewport(const Rectangle& v);
Rectangle viewport() const;
Size viewport_size() const;
Point viewport_origin() const;
Context& set_clearcolor(const RGBA &clear_color);
RGBA clearcolor() const;
void clear();
uint32_t get_error() const;
struct impl;
private:
std::unique_ptr<impl> impl_;
};
}
#endif

39
src/lib/geometry.hpp Normal file
View file

@ -0,0 +1,39 @@
#ifndef PARADISO_GEOMETRY_HPP
#define PARADISO_GEOMETRY_HPP
#include "globals.hpp"
namespace paradiso
{
struct Point final
{
using value_type = int32_t;
value_type x{0}, y{0};
};
struct Size final
{
using value_type = uint32_t;
value_type width{0}, height{0};
constexpr auto area() const noexcept
{
return width * height;
}
};
struct Rectangle
{
Point position;
Size size;
constexpr bool contains(const Point &p) const noexcept
{
return p.x >= position.x && p.x <= position.x + size.width &&
p.y >= position.y && p.y <= position.y + size.height;
}
};
}
#endif

12
src/lib/globals.hpp Normal file
View file

@ -0,0 +1,12 @@
#ifndef PARADISO_GLOBALS_HPP
#define PARADISO_GLOBALS_HPP
#include <cstdint>
namespace paradiso
{
}
#endif

188
src/lib/matrix.hpp Normal file
View file

@ -0,0 +1,188 @@
#ifndef PW_CORE_MATRIX_HPP
#define PW_CORE_MATRIX_HPP
#include "matrixbase.hpp"
#include <numeric>
namespace paradiso {
template <std::size_t R, std::size_t C, typename Scalar, bool RowMajor = false>
struct Matrix : MatrixBase<Scalar, Matrix<R, C, Scalar>> {
Scalar data[R * C]{};
static constexpr std::size_t rows{R};
static constexpr std::size_t cols{C};
static constexpr std::size_t coefficients{R * C};
using col_type = Matrix<R, 1, Scalar>;
using row_type = Matrix<1, C, Scalar>;
using transpose_type = Matrix<C, R, Scalar>;
template <typename... Arguments>
static constexpr Matrix<R, C, Scalar> make(Arguments... values) noexcept {
static_assert(sizeof...(Arguments) == R * C,
"Incorrect number of arguments");
return {.data = {values...}};
}
constexpr size_t offset(size_t r, size_t c) const {
return (RowMajor) ? r * C + c : c * R + r;
}
constexpr Scalar& operator()(std::size_t r, std::size_t c) {
return data[offset(r, c)];
}
constexpr const Scalar& operator()(std::size_t r, std::size_t c) const {
return data[offset(r, c)];
}
constexpr const Scalar* ptr() const { return &data[0]; }
//! set identity
constexpr Matrix& set_identity() {
for (std::size_t r = 0; r < rows; r++)
for (std::size_t c = 0; c < cols; c++)
(*this)(r, c) = (c == r) ? Scalar(1) : Scalar(0);
return *this;
}
constexpr Matrix& set_uniform(const Scalar& v) {
std::fill(std::begin(data), std::end(data), v);
return *this;
}
template <std::size_t Rs, std::size_t Cs, bool RowMajorSlice = RowMajor>
auto slice(std::size_t r, std::size_t c) const {
Matrix<Rs, Cs, Scalar, RowMajorSlice> s;
for (std::size_t ri = 0; ri < Rs; ri++)
for (std::size_t ci = 0; ci < Cs; ci++)
s(ri, ci) = (*this)(ri + r, ci + c);
return s;
}
template <std::size_t Rs, std::size_t Cs, bool RowMajorSlice = RowMajor>
Matrix& set_slice(const Matrix<Rs, Cs, Scalar, RowMajorSlice>& s,
std::size_t r, std::size_t c) {
for (std::size_t ri = 0; ri < Rs; ri++)
for (std::size_t ci = 0; ci < Cs; ci++)
(*this)(ri + r, ci + c) = s(ri, ci);
return *this;
}
template <std::size_t Rs, std::size_t Cs, bool RowMajorSlice = RowMajor>
auto minor(std::size_t r0, std::size_t c0) const {
Matrix<Rs, Cs, Scalar, RowMajorSlice> m;
size_t r = 0;
for (size_t ri = 0; ri < R; ri++) {
size_t c = 0;
if (ri == r0)
continue;
for (size_t ci = 0; ci < C; ci++) {
if (ci == c0)
continue;
m(r, c) = (*this)(ri, ci);
c++;
}
r++;
}
return m;
}
Scalar determinant() const {
Scalar det(0);
for (size_t c = 0; c < C; c++)
det += ((c % 2 == 0) ? (*this)(0, c) : -(*this)(0, c)) *
this->minor<R - 1, C - 1, RowMajor>(0, c).determinant();
return det;
}
auto transposed() const {
transpose_type res;
for (size_t r = rows; r-- > 0;)
for (size_t c = cols; c-- > 0;)
res(c, r) = (*this)(r, c);
return res;
}
auto inverse() const {
Scalar invDet = Scalar(1) / this->determinant();
Matrix<R, C, Scalar, RowMajor> inv;
for (int j = 0; j < C; j++)
for (int i = 0; i < R; i++) {
const Scalar minorDet =
this->minor<R - 1, C - 1, RowMajor>(j, i).determinant();
const Scalar coFactor =
((i + j) % 2 == 1) ? -minorDet : minorDet;
inv(i, j) = invDet * coFactor;
}
return inv;
}
inline bool row_major() const { return RowMajor; }
inline bool square() const { return R == C; }
inline const Matrix operator+(const Matrix& other) const {
Matrix res(*this);
for (size_t r = 0; r < R; r++)
for (size_t c = 0; c < C; c++)
res(r, c) += other(r, c);
return res;
}
inline const Matrix operator-(const Matrix& other) const {
Matrix res(*this);
for (size_t r = 0; r < R; r++)
for (size_t c = 0; c < C; c++)
res(r, c) -= other(r, c);
return res;
}
auto row(size_t row_) const {
row_type r;
for (size_t i = 0; i < cols; i++)
r[i] = (*this)(row_, i);
return r;
}
auto column(size_t col_) const {
col_type c;
for (size_t i = 0; i < rows; i++)
c[i] = (*this)(i, col_);
return c;
}
static constexpr auto identity() {
Matrix res;
for (std::size_t r = 0; r < rows; r++)
for (std::size_t c = 0; c < cols; c++)
res(r, c) = (c == r) ? Scalar(1) : Scalar(0);
return res;
}
};
template <> inline float Matrix<1, 1, float>::determinant() const {
return (*this)(0, 0);
}
template <> inline double Matrix<1, 1, double>::determinant() const {
return (*this)(0, 0);
}
template <typename Scalar, std::size_t R, std::size_t Ca, std::size_t Cb>
auto operator*(const Matrix<R, Ca, Scalar>& A, const Matrix<R, Cb, Scalar>& B) {
Matrix<R, Cb, Scalar> result;
result.zero(); // zero the output
for (size_t r = 0; r < R; r++)
for (size_t c = 0; c < Cb; c++)
for (size_t iI = 0; iI < R; iI++)
result(r, c) += A(r, iI) * B(iI, c); // inner product
return result;
}
} // namespace paradiso
#endif

116
src/lib/matrixbase.hpp Normal file
View file

@ -0,0 +1,116 @@
#ifndef PARADISO_MATRIXBASE_HPP
#define PARADISO_MATRIXBASE_HPP
#include <cmath>
#include <numeric>
namespace paradiso {
template <typename Scalar, typename Derived> struct MatrixBase {
using value_type = Scalar;
using reference = Scalar&;
using const_reference = const Scalar&;
using pointer = Scalar*;
using const_pointer = const Scalar*;
using iterator = Scalar*;
using const_iterator = const Scalar*;
constexpr pointer data() { return &derived().data[0]; }
constexpr const_pointer data() const noexcept { return &derived().data[0]; }
constexpr iterator begin() noexcept { return this->data(); }
constexpr iterator end() noexcept { return this->data() + size(); }
constexpr const_iterator begin() const noexcept { return this->data(); }
constexpr const_iterator end() const noexcept { return this->data() + size(); }
constexpr const_iterator cbegin() const noexcept { return this->data(); }
constexpr const_iterator cend() const noexcept { return this->data() + size(); }
constexpr Scalar& operator[](std::size_t i) { return this->data()[i]; }
constexpr const Scalar& operator[](std::size_t i) const {
return this->data()[i];
}
Derived& derived() { return static_cast<Derived&>(*this); }
const Derived& derived() const {
return static_cast<const Derived&>(*this);
}
std::size_t size() const {
return std::extent<decltype(Derived::data)>::value;
}
constexpr Derived& fill(const Scalar& v) noexcept {
std::fill(std::begin(*this), std::end(*this), Scalar(v));
return derived();
}
static constexpr Derived zero() noexcept {
Derived d;
d.fill(0);
return d;
}
constexpr Scalar squared_norm() const { return dot(*this, *this); }
constexpr Scalar norm() const { return std::sqrt(squared_norm()); }
constexpr Derived normalized() const { return *this / this->norm(); }
constexpr void normalize() { *this /= this->norm(); }
static constexpr Scalar dot(const Derived& a, const Derived& b) {
return std::inner_product(std::begin(a), std::end(a), std::begin(b),
Scalar{0});
}
static constexpr Derived lerp(const Derived& a, const Derived& b,
const Scalar& t) {
return a + (b - a) * t;
}
constexpr void operator*=(const Scalar& b) {
for (auto& e : *this)
e *= b;
}
constexpr void operator/=(const Scalar& b) {
for (auto& e : *this)
e /= b;
}
constexpr void operator+=(const Scalar& b) {
for (auto& e : *this)
e += b;
}
constexpr void operator-=(const Scalar& b) {
for (auto& e : *this)
e -= b;
}
constexpr const Derived operator*(const Scalar& b) const {
Derived r(derived());
for (auto& e : r)
e *= b;
return r;
}
constexpr const Derived operator/(const Scalar& b) const {
Derived r(derived());
for (auto& e : r)
e /= b;
return r;
}
constexpr const Derived operator+(const Scalar& b) const {
Derived r(derived());
for (auto& e : r)
e += b;
return r;
}
constexpr const Derived operator-(const Scalar& b) const {
Derived r(derived());
for (auto& e : r)
e -= b;
return r;
}
};
} // namespace paradiso
#endif

190
src/lib/renderer.cpp Normal file
View file

@ -0,0 +1,190 @@
#include "renderer.hpp"
#include "bitmap.hpp"
#include "sprite.hpp"
#include "glad/glad.h"
#include <algorithm>
#include <array>
#include <vector>
namespace paradiso {
struct Renderer::impl {
uint64_t change_count{};
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;
~impl() { release(); }
bool ready() const {
return glIsVertexArray != nullptr &&
GL_TRUE == glIsVertexArray(vertex_array_obj);
}
void bind_texture() {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_id);
}
void unbind_texture() { glBindTexture(GL_TEXTURE_2D, 0); }
bool build(const Sprite& s) {
// reset if the Renderer already in use
if (ready())
release();
//
glGenVertexArrays(1, &vertex_array_obj);
glBindVertexArray(vertex_array_obj);
// element buffer
glGenBuffers(1, &element_buffer_obj);
// indices -> elements
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_obj);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
s.indices.size() * sizeof(uint32_t), s.indices.data(),
GL_STATIC_DRAW);
vertex_buffer_ob.resize(vertex_buffer_ob.size() + 1);
glGenBuffers(1, &vertex_buffer_ob.back());
// vertices
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);
glEnableVertexAttribArray(vertex_buffer_ob.size() - 1);
vertex_buffer_ob.resize(vertex_buffer_ob.size() + 1);
glGenBuffers(1, &vertex_buffer_ob.back());
// normals
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);
glEnableVertexAttribArray(vertex_buffer_ob.size() - 1);
vertex_buffer_ob.resize(vertex_buffer_ob.size() + 1);
glGenBuffers(1, &vertex_buffer_ob.back());
// texture coordinates
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_ob.back());
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);
glEnableVertexAttribArray(vertex_buffer_ob.size() - 1);
// stop binding
glBindVertexArray(0);
return ready();
}
void update_texture(const Bitmap& image) {
if (GL_FALSE == glIsTexture(texture_id)) {
glGenTextures(1, &texture_id);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, texture_id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// setup new texture
glTexImage2D(GL_TEXTURE_2D, // target
0, // level
GL_RGB8, // internal format
image.size.width, // width
image.size.height, // height
0, // border
GL_RGB, // format
GL_UNSIGNED_BYTE, // type
image.data.data() // pointer to data
);
#if 0
// generate MipMaps
glGenerateMipmap(GL_TEXTURE_2D);
glGenerateTextureMipmap(_textureId);
#endif
} else {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(GL_TEXTURE_2D,
0, // level
0, // x-offset
0, // y-offset
image.size.width, // width
image.size.height, // height
GL_RGB, // format
GL_UNSIGNED_BYTE, // type
image.data.data()); // pointer
}
glBindTexture(GL_TEXTURE_2D, 0);
}
void release() {
for (auto vbo : vertex_buffer_ob)
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &element_buffer_obj);
glDeleteVertexArrays(1, &vertex_array_obj);
glDeleteTextures(1, &texture_id);
}
void draw() {
glBindVertexArray(vertex_array_obj);
glDrawElements(GL_TRIANGLES, _mesh_elements, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
}
};
//
// Outer wrapper
//
Renderer::Renderer() : impl_(std::make_unique<Renderer::impl>()) {}
Renderer::~Renderer() {
}
bool Renderer::ready() const { return impl_->ready(); }
bool Renderer::draw(const Sprite& m) { return impl_->build(m); }
// void Renderer::release() { impl_->release(); }
// void renderer::draw() { impl_->draw(); }
// uint64_t Renderer::change_count() const { return impl_->change_count; }
// void Renderer::set_change_count(uint64_t n) { impl_->change_count = n; }
} // namespace paradiso

29
src/lib/renderer.hpp Normal file
View file

@ -0,0 +1,29 @@
#ifndef PW_VISUAL_MESH_RENDERER_HPP
#define PW_VISUAL_MESH_RENDERER_HPP
#include <map>
#include <memory>
namespace paradiso {
struct Sprite;
/**
* @brief a Renderer2D for sprites
*/
struct Renderer final {
Renderer();
~Renderer();
Renderer(const Renderer&) = delete;
bool draw(const Sprite& m);
bool ready() const;
private:
struct impl;
std::unique_ptr<impl> impl_;
};
} // namespace paradiso
#endif

63
src/lib/rgba.hpp Normal file
View file

@ -0,0 +1,63 @@
#ifndef PARADISO_RGBA_HPP
#define PARADISO_RGBA_HPP
#include "globals.hpp"
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_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<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 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_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_alpha(uint8_t v) noexcept
{
pixel = (pixel & 0xFFFFFF00) | v;
return *this;
}
};
}
#endif

308
src/lib/shader.cpp Normal file
View file

@ -0,0 +1,308 @@
#include "shader.hpp"
#include "glad/glad.h"
#include <iostream>
namespace paradiso
{
struct Shader::impl
{
std::reference_wrapper<Shader> _shader;
GLuint _shader_program;
std::vector<GLuint> _shader_stages;
impl(Shader &s)
: _shader(s)
{
}
~impl()
{
destroy();
}
bool is_valid()
{
// we potentially haul in is_valid while no context is given
return glIsProgram != nullptr && glIsProgram(_shader_program);
}
bool build()
{
// if (!is_valid()) return false;
for (const auto &[type, code] : _shader.get().source_)
{
GLuint shader_type = 0;
switch (type)
{
case Shader::Type::Vertex:
shader_type = GL_VERTEX_SHADER;
break;
case Shader::Type::Compute:
shader_type = GL_COMPUTE_SHADER;
break;
case Shader::Type::Geometry:
shader_type = GL_GEOMETRY_SHADER;
break;
case Shader::Type::Fragment:
shader_type = GL_FRAGMENT_SHADER;
break;
default:
std::cerr << " unknown shader type";
}
GLuint shaderId = glCreateShader(shader_type);
char *src = const_cast<char *>(code.c_str());
GLint size = static_cast<GLint>(code.length());
glShaderSource(shaderId, 1, &src, &size);
glCompileShader(shaderId);
GLint is_compiled = GL_FALSE;
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &is_compiled);
if (is_compiled == GL_FALSE)
{
GLint log_length;
glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &log_length);
std::vector<char> log_buffer(static_cast<size_t>(log_length));
glGetShaderInfoLog(shaderId, log_length, &log_length, log_buffer.data());
// TODO - handle errors!
std::cerr << log_buffer.data();
return false;
}
_shader_stages.push_back(shaderId);
}
_shader_program = glCreateProgram();
for (auto s : _shader_stages)
glAttachShader(_shader_program, s);
// TODO attribute binding ...
/* Bind attribute index 0 (coordinates) to in_Position and attribute index 1 (color) to in_Color */
/* Attribute locations must be setup before calling glLinkProgram. */
// glBindAttribLocation(shaderprogram, 0, "in_Position");
// glBindAttribLocation(shaderprogram, 1, "in_Color");
glLinkProgram(_shader_program);
GLint is_linked = 0;
glGetProgramiv(_shader_program, GL_LINK_STATUS, &is_linked);
if (is_linked == GL_FALSE)
{
GLint log_length;
/* Noticed that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv. */
glGetProgramiv(_shader_program, GL_INFO_LOG_LENGTH, &log_length);
/* The maxLength includes the NULL character */
std::vector<char> info_log(static_cast<size_t>(log_length));
/* Notice that glGetProgramInfoLog, not glGetShaderInfoLog. */
glGetProgramInfoLog(_shader_program, log_length, &log_length, info_log.data());
std::cerr << info_log.data();
/* Handle the error in an appropriate way such as displaying a message or writing to a log file. */
/* In this simple program, we'll just leave */
return false;
}
return true;
}
void use()
{
glUseProgram(_shader_program);
}
void destroy()
{
// potentially the GL driver hasn't been loaded
if (is_valid())
{
// deleting and detaching should happen much earlier
for (auto s : _shader_stages)
{
glDeleteShader(s);
}
// only program needs to be deleted
glDeleteProgram(_shader_program);
}
}
int uniform_location(std::string const &name) const
{
return glGetUniformLocation(_shader_program, name.c_str());
}
// void bind(int location,const matrix3x3f& m)
// {
// glUniformMatrix3fv(location,1,GL_FALSE,m.ptr());
// }
// void bind(int location,const matrix4x4f& m)
// {
// glUniformMatrix4fv(location,1,GL_FALSE,m.ptr());
// }
// void bind(int location,const vector4f& v)
// {
// glUniform4fv(location,1,v.ptr());
// }
void bind(int location, const float &v)
{
glUniform1f(location, v);
}
void bind(int location, const uint32_t &i)
{
glUniform1ui(location, i);
}
void bind(int location, const int32_t &i)
{
glUniform1i(location, i);
}
// void bind(int location,const texture& v)
// {
// this->bind(location,(int)v.native_handle());
// }
};
Shader::Shader()
{
impl_ = std::make_unique<impl>(*this);
}
Shader::~Shader()
{
}
bool Shader::ready() const
{
return impl_->is_valid();
}
Shader &Shader::set_uniform_at_location(int location, float v)
{
impl_->bind(location, v);
return *this;
}
Shader &Shader::set_uniform_at_location(int location, uint32_t v)
{
impl_->bind(location, v);
return *this;
}
Shader &Shader::set_uniform_at_location(int location, int32_t v)
{
impl_->bind(location, v);
return *this;
}
// Shader &Shader::set_uniform_at_location(int location, vector4f const &v)
// {
// impl_->bind(location, v);
// return *this;
// }
// Shader &Shader::set_uniform_at_location(int location, matrix4x4f const &v)
// {
// impl_->bind(location, v);
// return *this;
// }
// Shader &Shader::set_uniform_at_location(int location, texture const &v)
// {
// impl_->bind(location, v);
// return *this;
// }
bool Shader::build()
{
return impl_->build();
}
void Shader::use()
{
impl_->use();
}
void Shader::set_uniforms(uniform_cache_t c)
{
#if 0
// TODO rewrite in proper C++17
for (auto &u : c)
{
// get name
std::string name = std::get<0>(u);
// get location
GLint loc = std::get<2>(u);
// if lower 0 check for location
if (loc < 0)
{
loc = impl_->uniform_location(name);
std::get<2>(u) = loc; // cache location
}
auto var = std::get<1>(u);
std::visit([this, loc](auto &&arg)
{
using T = std::decay_t<decltype(arg)>;
// TODO query the std::variant of uniform_t
if constexpr ((std::is_same_v<T, vector4f>) ||
(std::is_same_v<T, matrix4x4f>) ||
(std::is_same_v<T, float>) ) {
set_uniform_at_location( loc, std::forward<T>(arg));
} else {
debug::e() << "unknown uniform type";
} },
var);
}
#endif
}
uint32_t Shader::native_handle() const
{
return impl_->_shader_program;
}
int Shader::uniform_location(const std::string &name) const
{
return impl_->uniform_location(name);
}
}

86
src/lib/shader.hpp Normal file
View file

@ -0,0 +1,86 @@
#ifndef PARADISO_SHADER_HPP
#define PARADISO_SHADER_HPP
#include "globals.hpp"
#include <unordered_map>
#include <variant>
#include <string>
#include <vector>
#include <memory>
#include <tuple>
namespace paradiso
{
struct Shader final
{
Shader();
~Shader();
Shader(const Shader &) = delete;
Shader(Shader &&) = default;
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); }
bool ready() const;
bool build();
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
/**
* @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; }
/**
* 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));
}
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>;
void set_uniforms(uniform_cache_t c);
uint32_t native_handle() const;
private:
std::unordered_map<Type, std::string> source_;
struct impl;
std::unique_ptr<impl> impl_;
};
}
#endif

42
src/lib/sprite.hpp Normal file
View file

@ -0,0 +1,42 @@
#ifndef PARADISO_SPRITE_HPP
#define PARADISO_SPRITE_HPP
#include "bitmap.hpp"
#include "matrix.hpp"
#include "vector.hpp"
#include <array>
namespace paradiso {
struct Sprite final {
using ChangeCountType = std::uint64_t;
static constexpr Sprite create() noexcept { return {}; }
Vector2<float> pivot{Vector2<float>::zero()};
std::array<std::uint32_t, 6> indices{0, 3, 2, 2, 1, 0};
std::array<Vector3<float>, 4> vertices{
Vector3<float>::make(-1.0f, -1.0f, 0.0f),
Vector3<float>::make(-1.0f, +1.0f, 0.0f),
Vector3<float>::make(+1.0f, +1.0f, 0.0f),
Vector3<float>::make(+1.0f, -1.0f, 0.0f)};
std::array<Vector3<float>, 4> normals{
Vector3<float>::z_axis(), Vector3<float>::z_axis(),
Vector3<float>::z_axis(), Vector3<float>::z_axis()};
std::array<Vector2<float>, 4> texture_coordinates{
Vector2<float>::make(0.0f, 0.0f), /// 0
Vector2<float>::make(0.0f, 1.0f), /// 1
Vector2<float>::make(1.0f, 1.0f), /// 2
Vector2<float>::make(1.0f, 0.0f) /// 3
};
ChangeCountType change_count{};
};
} // namespace paradiso
#endif

62
src/lib/vector.hpp Normal file
View file

@ -0,0 +1,62 @@
#ifndef PARADISO_VECTOR_HPP
#define PARADISO_VECTOR_HPP
#include "matrix.hpp"
namespace paradiso {
/**
* Basic vector types used in pixwerx.
*/
template <typename T> struct Vector2 : Matrix<2, 1, T> {
using base_type = Matrix<2, 1, T>;
constexpr const T& x() const noexcept { return (*this)[0]; }
constexpr T& x() { return (*this)[0]; }
constexpr const T& y() const { return (*this)[1]; }
constexpr T& y() { return (*this)[1]; }
inline auto homogenous(T w = 1) const {
return Matrix<3, 1, T>({x(), y(), w});
}
static T angle_between(const Vector2& a, const Vector2& b) {
return std::acos(dot(a.normalized(), b.normalized()));
}
};
template <typename T> struct Vector3 : Matrix<3, 1, T> {
using base_type = Matrix<3, 1, T>;
constexpr const T& x() const { return (*this)[0]; }
constexpr T& x() { return (*this)[0]; }
constexpr const T& y() const { return (*this)[1]; }
constexpr T& y() { return (*this)[1]; }
constexpr Vector2<T> xy() const noexcept { return {x(), y()}; }
static constexpr Vector3 cross(const Vector3& lhs, const Vector3& rhs) {
return vector3_({lhs[1] * rhs[2] - rhs[1] * lhs[2],
lhs[2] * rhs[0] - rhs[2] * lhs[0],
lhs[0] * rhs[1] - rhs[0] * lhs[1]});
}
static constexpr auto x_axis() noexcept {
return Vector3::make(T{1}, T{0}, T{0});
}
static constexpr auto y_axis() noexcept {
return Vector3::make(T{0}, T{1}, T{0});
}
static constexpr auto z_axis() noexcept {
return Vector3<T>::make(T{0}, T{0}, T{1});
}
};
}; // namespace paradiso
#endif

291
src/lib/window.cpp Normal file
View file

@ -0,0 +1,291 @@
#include "window.hpp"
#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include <cmath>
#include <codecvt>
#include <locale>
#include <iostream>
namespace paradiso {
struct Window::impl {
std::reference_wrapper<Window> parent_;
GLFWwindow* window_ = nullptr;
std::tuple<int, int> _old_size;
std::tuple<int, int> _old_pos;
// window_context _context;
static void error_callback(int error, const char* description) {
std::cerr << "GLFW error: " << description << '\n';
}
static void drop_callback(GLFWwindow* window, int count,
const char** paths) {
// std::cout << __FUNCTION__ << std::endl;
// for (int i = 0; i < count; i++)
// std::cout << "\t" << paths[i] << std::endl;
}
static void scroll_callback(GLFWwindow* window, double xoffset,
double yoffset) {
std::cout << __FUNCTION__ << std::endl;
}
static void mouse_button_callback(GLFWwindow* window, int button,
int action, int mods) {
// input::get()._mouse_button = button;
std::cout << __FUNCTION__ << " " << button << " " << action << " "
<< mods << std::endl;
// input::get()._mouse_position
}
static void cursor_pos_callback(GLFWwindow* window, double xpos,
double ypos) {
// input::get()._mouse_position =
// pointd(xpos,ypos).cast<float>();
}
static void key_callback(GLFWwindow* window, int key, int scancode,
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;
}
static void charmods_callback(GLFWwindow* window, unsigned int codepoint,
int mods) {
// build the string from a Unicode code point
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
std::string u8str = converter.to_bytes(codepoint);
// input::get()._input_string = u8str;
}
static void framebuffer_size_callback(GLFWwindow* window, int width,
int height) {
Window::impl* impl =
static_cast<Window::impl*>(glfwGetWindowUserPointer(window));
impl->parent_.get().on_resize_(impl->parent_);
}
impl(Window& w) : parent_(w) {
// initialize
if (!glfwInit()) {
std::cerr << "Initalization error\n";
}
int glfw_major{}, glfx_minor{}, glfw_rev{};
glfwGetVersion(&glfw_major, &glfx_minor, &glfw_rev);
// we default to window invisible by default
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
// we do mostly 2D hence only request ancient OpenGL 4.0
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//
window_ = glfwCreateWindow(640, 480, "paradiso", nullptr, nullptr);
// make window current
glfwMakeContextCurrent(window_);
// load opengl
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std::cerr << "glad couldn't get OpenGL API";
}
// check Version
int major{}, minor{}, rev{};
major = glfwGetWindowAttrib(window_, GLFW_CONTEXT_VERSION_MAJOR);
minor = glfwGetWindowAttrib(window_, GLFW_CONTEXT_VERSION_MINOR);
rev = glfwGetWindowAttrib(window_, GLFW_CONTEXT_REVISION);
// maybe something to pass to the outside
// sstt::core::Debug::d() << "OpenGL " << major << "." << minor
// << "." << rev;
glfwSetWindowUserPointer(window_, this);
glfwSetFramebufferSizeCallback(window_,
Window::impl::framebuffer_size_callback);
glfwSetKeyCallback(window_, Window::impl::key_callback);
// glfwSetCharCallback(_window, character_callback);
glfwSetCharModsCallback(window_, charmods_callback);
glfwSetScrollCallback(window_, scroll_callback);
glfwSetDropCallback(window_, drop_callback);
glfwSetCursorPosCallback(window_, cursor_pos_callback);
glfwSetMouseButtonCallback(window_, mouse_button_callback);
glfwSetScrollCallback(window_, scroll_callback);
glfwSetErrorCallback(error_callback);
#if 0
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(message_callback, 0);
#endif
// glfwSetWindowCloseCallback(_window,close_callback);
}
~impl() { glfwDestroyWindow(window_); }
static void message_callback(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length,
const GLchar* message, const void* userParam) {
fprintf(stderr,
"GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type,
severity, message);
}
bool update(Window::on_updatecallback_t cb) {
if (window_ && !glfwWindowShouldClose(window_)) {
// TODO lock an unlock the current input system to allow for late
// events coming in
// input::get().reset();
// get new events
glfwPollEvents();
if (cb(parent_)) {
glfwSwapBuffers(window_);
return true;
}
}
return false;
}
void set_title(std::string_view title) {
glfwSetWindowTitle(window_, title.data());
}
void set_size(const Size& size) {
glfwSetWindowSize(window_, size.width, size.height);
}
Size size() const {
int w{}, h{};
glfwGetWindowSize(window_, &w, &h);
return {.width = Size::value_type(w), .height = Size::value_type(h)};
}
Size client_size() const {
int w{}, h{};
glfwGetFramebufferSize(window_, &w, &h);
return {.width = Size::value_type(w), .height = Size::value_type(h)};
}
Point position() const {
int x{}, y{};
glfwGetWindowPos(window_, &x, &y);
return {.x = x, .y = y};
}
void set_position(const Point& p) { glfwSetWindowPos(window_, p.x, p.y); }
void set_fullscreen(bool use_fullscreen) {
if (fullscreen() == use_fullscreen)
return;
if (use_fullscreen) {
auto& [x, y] = _old_pos;
auto& [w, h] = _old_size;
glfwGetWindowPos(window_, &x, &y);
glfwGetWindowSize(window_, &w, &h);
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(window_, monitor, 0, 0, mode->width,
mode->height, 0);
} else {
const auto& [x, y] = _old_pos;
const auto& [w, h] = _old_size;
glfwSetWindowMonitor(window_, nullptr, x, y, w, h, 0);
}
// glfwSetWindow
}
bool fullscreen() const { return glfwGetWindowMonitor(window_) != nullptr; }
void set_visible(bool show) {
(show) ? glfwShowWindow(window_) : glfwHideWindow(window_);
}
bool visible() const {
return glfwGetWindowAttrib(window_, GLFW_VISIBLE) > 0;
}
};
//
//
//
Window::Window()
: impl_(std::make_unique<Window::impl>(*this)), on_resize_([](Window&) {}),
on_keyboard_([](Window&, int, int, int, int) {})
{}
Window::~Window() {}
bool Window::update(Window::on_updatecallback_t cb) {
return impl_->update(cb);
}
Size Window::size() const { return impl_->size(); }
Size Window::client_size() const { return impl_->client_size(); }
Point Window::position() const { return impl_->position(); }
bool Window::fullscreen() const { return impl_->fullscreen(); }
bool Window::visible() const { return impl_->visible(); }
Window& Window::set_visible(bool is_visible) {
impl_->set_visible(is_visible);
return *this;
}
Window& Window::set_fullscreen(bool use_fullscreen) {
impl_->set_fullscreen(use_fullscreen);
return *this;
}
Window& Window::set_position(const Point& position) {
impl_->set_position(position);
return *this;
}
Window& Window::set_size(const Size& size) {
impl_->set_size(size);
return *this;
}
Window& Window::set_title(std::string_view title) {
impl_->set_title(title);
return *this;
}
} // namespace paradiso

64
src/lib/window.hpp Normal file
View file

@ -0,0 +1,64 @@
#ifndef PARADISO_WINDOW_HPP
#define PARADISO_WINDOW_HPP
#include "geometry.hpp"
#include "globals.hpp"
#include <functional>
#include <memory>
#include <string>
namespace paradiso {
struct Window final {
using Size = paradiso::Size;
using Position = paradiso::Point;
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();
Window(const Window&) = delete; // no copy!
Window(Window&&) = default;
/**
* @brief update internal state
* @param cb callback to process data and render
* @return returns false if process needs to quit
* */
bool update(on_updatecallback_t cb);
Window& set_title(std::string_view title);
Window& set_size(const Size& size);
Size size() const;
Size client_size() const;
Window& set_position(const Position& position);
Position position() const;
bool fullscreen() const;
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);
private:
struct impl;
std::unique_ptr<impl> impl_;
on_resizecallback_t on_resize_;
on_keyboardcallback_t on_keyboard_;
};
} // namespace paradiso
#endif