inching towards a first MVP with a renderer working again

This commit is contained in:
Hartmut Seichter 2024-07-24 23:49:09 +02:00
parent 9cb55edbb4
commit 058f7ca23d
17 changed files with 149 additions and 154 deletions

View file

@ -9,7 +9,7 @@ add_subdirectory(core)
#add_subdirectory(ui)
# add_subdirectory(binding)
# add_subdirectory(visual)
add_subdirectory(visual)
# add_subdirectory(geometry)
# add_subdirectory(runtime)

View file

@ -17,7 +17,7 @@ set(hdrs
include/pw/core/serialize.hpp
include/pw/core/size.hpp
include/pw/core/time.hpp
include/pw/core/geometry.hpp
include/pw/core/primitives.hpp
include/pw/core/image.hpp
include/pw/core/vector.hpp
include/pw/core/matrix_transform.hpp

View file

@ -29,7 +29,7 @@
namespace pw {
struct color {
struct color final {
vector4f rgba{vector4f::all(1)};

View file

@ -36,7 +36,8 @@ template <std::floating_point Scalar> struct frustum final {
Scalar aspect_ratio,
Scalar z_near,
Scalar z_far) -> frustum {
const auto tangent_half{std::tan(pw::deg_to_rad(fov_h_deg / Scalar{2}))};
const auto tangent_half{
std::tan(pw::deg_to_rad(fov_h_deg / Scalar{2}))};
const auto top{tangent_half * z_near};
const auto right{top * aspect_ratio};

View file

@ -258,6 +258,22 @@ constexpr auto operator*(const matrix<ScalarA, Ra, Ca>& A,
return result;
}
//
// matrix aliases
//
template <typename Scalar> using matrix2x2 = matrix<Scalar, 2, 2>;
template <typename Scalar> using matrix3x3 = matrix<Scalar, 3, 3>;
template <typename Scalar> using matrix4x4 = matrix<Scalar, 4, 4>;
using matrix2x2f = matrix2x2<float>;
using matrix3x3f = matrix3x3<float>;
using matrix4x4f = matrix4x4<float>;
using matrix2x2d = matrix2x2<double>;
using matrix3x3d = matrix3x3<double>;
using matrix4x4d = matrix4x4<double>;
} // namespace pw
//

View file

@ -8,8 +8,8 @@
* 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 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,
@ -24,9 +24,41 @@
#define PW_CORE_MESH_HPP
#include <pw/core/globals.hpp>
#include <pw/core/primitives.hpp>
#include <atomic>
namespace pw {
struct attribute final {
using mask_type = std::vector<bool>;
using int8_type = std::vector<int8_t>;
using int32_type = std::vector<int32_t>;
using float_type = std::vector<float>;
using vector2_type = std::vector<vector2<float>>;
using vector3_type = std::vector<vector3<float>>;
using vector4_type = std::vector<vector4<float>>;
using attribute_data =
std::variant<std::monostate, mask_type, int8_type, int32_type,
float_type, vector2_type, vector3_type, vector4_type>;
enum attribute_type {
normals,
texture_coordinates,
};
attribute_type type{};
attribute_data data{};
};
struct mesh {
primitives geometry{};
std::vector<attribute> attributes;
std::atomic<uint64_t> change_count{0};
};
} // namespace pw
#endif

View file

@ -8,8 +8,8 @@
* 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 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,
@ -27,25 +27,18 @@
namespace pw {
template <typename T_>
struct point_ {
template <typename Scalar> struct point final {
using value_type = T_;
using value_type = Scalar;
T_ x {0}, y {0};
template <typename To_>
point_<To_> cast() const { return point_<To_>(static_cast<To_>(x),static_cast<To_>(y)); }
Scalar x{}, y{};
template <typename ScalarOut>
constexpr auto cast() const noexcept -> point<ScalarOut> {
return {.x{ScalarOut(x)}, .y{ScalarOut(y)}};
}
};
using point = point_<real_t>;
using pointf = point_<float>;
using pointd = point_<double>;
using pointi = point_<int>;
}
} // namespace pw
#endif

View file

@ -8,8 +8,8 @@
* 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 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,
@ -23,53 +23,28 @@
#ifndef PW_CORE_RECTANGLE_HPP
#define PW_CORE_RECTANGLE_HPP
#include <initializer_list>
#include <pw/core/point.hpp>
#include <pw/core/size.hpp>
namespace pw {
template <typename T_>
struct rectangle_ {
template <typename Scalar> struct rectangle final {
point_<T_> position;
size_<T_> size;
point<Scalar> position{};
size<Scalar> size{};
rectangle_() = default;
rectangle_(const T_ l[4])
: position(point_<T_>(l[0],l[1]))
, size(size_<T_>(l[2],l[3]))
{
}
rectangle_(const T_(&l)[4])
: position(point_<T_>(l[0],l[1]))
, size(size_<T_>(l[2],l[3]))
{
}
rectangle_(point_<T_> const & p,size_<T_> const & s) : size(s), position(p) {}
bool contains(const point_<T_>& p) const noexcept
{
constexpr bool contains(const point<Scalar>& p) const noexcept {
return p.x >= position.x && p.x <= position.x + size.width &&
p.y >= position.y && p.y <= position.y + size.height;
p.y >= position.y && p.y <= position.y + size.height;
}
template <typename To_>
rectangle_<To_> cast() const
{
return rectangle_<To_>(position.template cast<To_>(),size.template cast<To_>());
template <typename ScalarOut>
constexpr auto cast() const noexcept -> rectangle<ScalarOut> {
return {.position = position.template cast<ScalarOut>(),
.size = size.template cast<ScalarOut>()};
}
};
using rectangle = rectangle_<real_t>;
using rectanglei = rectangle_<int>;
using rectanglef = rectangle_<float>;
using rectangled = rectangle_<double>;
}
} // namespace pw
#endif

View file

@ -42,8 +42,15 @@ template <typename Scalar> struct size {
}
}
template <typename Other> constexpr auto operator()() const noexcept {
return Other{width, height};
template <typename ScalarOut>
constexpr auto cast(this auto&& self) noexcept -> size<ScalarOut> {
return {.width = ScalarOut(self.width),
.height = ScalarOut(self.height)};
}
template <typename Other>
constexpr auto operator()(this auto&& self) noexcept {
return Other{self.width, self.height};
}
};

View file

@ -3,6 +3,7 @@
#include <pw/core/primitives.hpp>
#include <pw/core/serialize.hpp>
#include <pw/core/vector.hpp>
#include <pw/core/mesh.hpp>
#include <print>
#include <variant>
@ -10,33 +11,6 @@
namespace pw {
struct attribute final {
using mask_type = std::vector<bool>;
using int8_type = std::vector<int8_t>;
using int32_type = std::vector<int32_t>;
using float_type = std::vector<float>;
using vector2_type = std::vector<vector2<float>>;
using vector3_type = std::vector<vector3<float>>;
using vector4_type = std::vector<vector4<float>>;
using attribute_data =
std::variant<std::monostate, mask_type, int8_type, int32_type,
float_type, vector2_type, vector3_type, vector4_type>;
enum attribute_type {
normals,
texture_coordinates,
};
attribute_type type{};
attribute_data data{};
};
struct mesh_n {
primitives geometry{};
std::vector<attribute> attributes;
};
} // namespace pw
auto main() -> int {
@ -48,7 +22,7 @@ auto main() -> int {
.indices = {0, 1, 2},
.topology = pw::primitives::topology_type::triangle_list};
auto mesh = pw::mesh_n{.geometry = geom};
auto mesh = pw::mesh{.geometry = geom};
mesh.attributes.emplace_back(
pw::attribute{.type = pw::attribute::normals,

View file

@ -8,8 +8,8 @@
* 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 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,
@ -24,17 +24,15 @@
#ifndef PW_VISUAL_CONTEXT_HPP
#define PW_VISUAL_CONTEXT_HPP
#include <pw/core/color.hpp>
#include <pw/core/point.hpp>
#include <pw/core/rectangle.hpp>
#include <pw/core/size.hpp>
#include <pw/core/vector.hpp>
#include <pw/core/rectangle.hpp>
#include <pw/core/color.hpp>
namespace pw {
class context {
public:
struct context final {
context();
~context();
@ -42,24 +40,24 @@ public:
void set_blend();
void set_depth();
void set_viewport(const rectangle& v);
rectangle viewport() const;
void set_viewport(const rectangle<int32_t>& v);
rectangle<int32_t> viewport() const;
size viewport_size() const;
point viewport_origin() const;
size<int32_t> viewport_size() const;
point<int32_t> viewport_origin() const;
void set_clearcolor(const color &c);
void set_clearcolor(const color& c);
color clearcolor() const;
void clear();
uint32_t get_error() const;
protected:
protected:
struct impl;
std::unique_ptr<impl> _impl;
};
}
} // namespace pw
#endif

View file

@ -6,22 +6,20 @@
namespace pw {
class framebuffer {
public:
struct framebuffer final {
framebuffer();
~framebuffer();
bool create(const size& s);
bool create(const size<int32_t>& s);
void bind();
void blit();
void unbind();
protected:
protected:
struct impl;
std::unique_ptr<impl> _impl;
};
}
} // namespace pw
#endif

View file

@ -6,26 +6,23 @@
namespace pw {
struct geometry;
struct mesh;
/**
* @brief builds a renderer for geometry
* @brief builds a renderer for meshes
*/
struct renderer final {
renderer();
renderer(const geometry& m);
renderer(const mesh& m);
~renderer();
bool update(const geometry& m);
bool update(const mesh& m);
void release();
void draw();
bool ready() const;
uint64_t change_count() const;
void set_change_count(uint64_t n);
protected:
struct impl;
std::unique_ptr<impl> _impl;

View file

@ -2,6 +2,7 @@
#include "pw/core/vector.hpp"
#include "glad/glad.h"
#include <cstdint>
namespace pw {
@ -10,16 +11,16 @@ struct context::impl {
vector4f _clear_color { 0, 1, 0, 1};
void set_viewport(const rectangle& v)
void set_viewport(const rectangle<int32_t>& v)
{
glViewport(v.position.x,v.position.y,v.size.width,v.size.height);
}
const rectangle viewport()
const rectangle<int32_t> viewport()
{
float _viewport[4] = {0,0,1,1};
glGetFloatv(GL_VIEWPORT,&_viewport[0]);
return rectangle(_viewport);
auto vp = rectangle<float>{0,0,1,1};
glGetFloatv(GL_VIEWPORT,static_cast<float*>(&vp.position.x));
return vp.cast<int32_t>();
}
void clear()
@ -56,12 +57,12 @@ void context::set_blend()
}
void context::set_viewport(const rectangle& v)
void context::set_viewport(const rectangle<int32_t>& v)
{
_impl->set_viewport(v);
}
rectangle context::viewport() const
rectangle<int32_t> context::viewport() const
{
return _impl->viewport();
}
@ -78,15 +79,14 @@ u_int32_t context::get_error() const
color context::clearcolor() const
{
return _impl->_clear_color;
// return _impl->_clear_color;
return color{};
}
void context::set_clearcolor(const color& c)
{
_impl->_clear_color = c;
// _impl->_clear_color = c;
}
}

View file

@ -5,6 +5,7 @@
#include "glad/glad.h"
#include <cstdint>
#include <limits>
namespace pw {
@ -13,7 +14,7 @@ namespace pw {
struct framebuffer::impl {
size _size;
size<int32_t> _size;
GLuint _fbo_draw { std::numeric_limits<GLuint>::max() };
GLuint _fbo_msaa { std::numeric_limits<GLuint>::max() };
@ -21,7 +22,7 @@ struct framebuffer::impl {
GLuint _rbo_color { std::numeric_limits<GLuint>::max() };
GLuint _rbo_depth { std::numeric_limits<GLuint>::max() };
bool create(const size &s)
bool create(const size<int32_t> &s)
{
// TODO mak color resolution and
_size = s;
@ -107,7 +108,7 @@ framebuffer::~framebuffer()
{
}
bool framebuffer::create(const size &s)
bool framebuffer::create(const size<int32_t> &s)
{
return _impl->create(s);
}

View file

@ -1,19 +1,21 @@
#include "pw/visual/renderer.hpp"
#include "pw/core/debug.hpp"
#include "pw/core/geometry.hpp"
#include "pw/core/matrix.hpp"
#include "pw/core/mesh.hpp"
#include "pw/core/primitives.hpp"
#include "pw/core/size.hpp"
#include "glad/glad.h"
#include <algorithm>
#include <atomic>
namespace pw {
struct renderer::impl {
uint64_t _change_count{0};
std::atomic<uint64_t> change_count{};
uint32_t _vao{0};
uint32_t _ebo{0};
@ -29,15 +31,15 @@ struct renderer::impl {
return glIsVertexArray != nullptr && GL_TRUE == glIsVertexArray(_vao);
}
bool update(const geometry& m) {
if (_change_count == m.change_count())
bool update(const mesh& m) {
if (this->change_count == m.change_count)
return false;
// reset if the renderer already in use
if (ready())
release();
_mesh_elements = m.indices().size();
_mesh_elements = m.geometry.indices.size();
//
glGenVertexArrays(1, &_vao);
@ -51,31 +53,34 @@ struct renderer::impl {
// indices
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
m.indices().size() * sizeof(uint32_t), m.indices().data(),
GL_STATIC_DRAW);
m.geometry.indices.size() * sizeof(uint32_t),
m.geometry.indices.data(), GL_STATIC_DRAW);
_vbos.resize(_vbos.size() + 1);
glGenBuffers(1, &_vbos.back());
// vertices
glBindBuffer(GL_ARRAY_BUFFER, _vbos.back());
glVertexAttribPointer(_vbos.size() - 1, vector3::coefficients, GL_FLOAT,
glVertexAttribPointer(_vbos.size() - 1, vector3f::coefficients, GL_FLOAT,
GL_FALSE, 0, nullptr);
glBufferData(GL_ARRAY_BUFFER, m.vertices().size() * sizeof(vector3),
m.vertices().data(), GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER,
m.geometry.vertices.size() * sizeof(vector3f),
m.geometry.vertices.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(_vbos.size() - 1);
debug::d() << "vertices at " << _vbos.size() - 1;
#if 0
if (!m.normals().empty()) {
_vbos.resize(_vbos.size() + 1);
glGenBuffers(1, &_vbos.back());
// normals
glBindBuffer(GL_ARRAY_BUFFER, _vbos.back());
glVertexAttribPointer(_vbos.size() - 1, vector3::coefficients,
glVertexAttribPointer(_vbos.size() - 1, vector3f::coefficients,
GL_FLOAT, GL_FALSE, 0, nullptr);
glBufferData(GL_ARRAY_BUFFER, m.normals().size() * sizeof(vector3),
glBufferData(GL_ARRAY_BUFFER, m.normals().size() * sizeof(vector3f),
m.normals().data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(_vbos.size() - 1);
@ -99,10 +104,12 @@ struct renderer::impl {
}
}
#endif
// stop binding
glBindVertexArray(0);
this->_change_count = m.change_count();
this->change_count.store(m.change_count.load());
#if 1
// get errors
@ -118,7 +125,7 @@ struct renderer::impl {
void release() {
for (auto & vbo : _vbos)
for (auto& vbo : _vbos)
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &_ebo);
@ -148,7 +155,7 @@ struct renderer::impl {
renderer::renderer() : _impl(std::make_unique<renderer::impl>()) {}
renderer::renderer(const geometry& m) {
renderer::renderer(const mesh& m) {
renderer();
// directly update
_impl->update(m);
@ -158,14 +165,10 @@ renderer::~renderer() = default;
bool renderer::ready() const { return _impl->ready(); }
bool renderer::update(const geometry& m) { return _impl->update(m); }
bool renderer::update(const mesh& m) { return _impl->update(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 pw

View file

@ -141,15 +141,15 @@ struct shader::impl {
}
void bind(int location, const matrix3x3f& m) {
glUniformMatrix3fv(location, 1, GL_FALSE, m.ptr());
glUniformMatrix3fv(location, 1, GL_FALSE, m.data());
}
void bind(int location, const matrix4x4f& m) {
glUniformMatrix4fv(location, 1, GL_FALSE, m.ptr());
glUniformMatrix4fv(location, 1, GL_FALSE, m.data());
}
void bind(int location, const vector4f& v) {
glUniform4fv(location, 1, v.ptr());
glUniform4fv(location, 1, v.data());
}
void bind(int location, const float& v) { glUniform1f(location, v); }