stripped down all pixwerx code to a minimal istic subset
This commit is contained in:
commit
612677c52d
398 changed files with 164811 additions and 0 deletions
36
src/lib/bitmap.hpp
Normal file
36
src/lib/bitmap.hpp
Normal 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
76
src/lib/context.cpp
Normal 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
64
src/lib/context.hpp
Normal 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
39
src/lib/geometry.hpp
Normal 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
12
src/lib/globals.hpp
Normal 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
188
src/lib/matrix.hpp
Normal 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
116
src/lib/matrixbase.hpp
Normal 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
190
src/lib/renderer.cpp
Normal 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
29
src/lib/renderer.hpp
Normal 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
63
src/lib/rgba.hpp
Normal 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
308
src/lib/shader.cpp
Normal 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
86
src/lib/shader.hpp
Normal 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
42
src/lib/sprite.hpp
Normal 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
62
src/lib/vector.hpp
Normal 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
291
src/lib/window.cpp
Normal 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
64
src/lib/window.hpp
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue