diff --git a/common/simple_vg.cpp b/common/simple_vg.cpp
index 047cb8ac5d9cd5c03b69760de808e3acc9d19440..64192ac44fdeb77cbca78e8f14ed1608e0273c44 100644
--- a/common/simple_vg.cpp
+++ b/common/simple_vg.cpp
@@ -1,5 +1,6 @@
#include "simple_vg.h"
#include "simple/support/enum.hpp"
+#include "simple/support/algorithm.hpp"
using namespace simple::vg;
@@ -46,12 +47,55 @@ frame canvas::begin_frame(float2 size, float pixelRatio) noexcept
return frame(raw.get(), size, pixelRatio);
}
-frame::frame(NVGcontext* context, float2 size, float pixelRatio) noexcept :
+frame canvas::begin_frame(framebuffer& fb) const noexcept
+{
+ return frame(raw.get(), fb);
+}
+
+framebuffer::framebuffer(int2 size, enum flags flags) noexcept :
+ flags(flags),
size(size),
- pixelRatio(pixelRatio),
- context(context)
+ raw(nullptr)
+{}
+framebuffer::framebuffer(const canvas& canvas, int2 size, enum flags flags) :
+ framebuffer(size, flags)
{
- nvgBeginFrame(context, size.x(), size.y(), pixelRatio);
+ create(canvas);
+}
+
+bool framebuffer::create(const canvas& canvas)
+{
+ if(raw)
+ return false;
+
+ raw = decltype(raw)(
+ nvgluCreateFramebuffer(
+ canvas.raw.get(),
+ size.x(), size.y(),
+ support::to_integer(flags)
+ )
+ );
+ return true;
+}
+
+simple::vg::paint framebuffer::paint(simple::support::range<int2> range, float opacity, float angle) const
+{
+ auto size = range.upper() - range.lower();
+ return nvgImagePattern(nullptr,
+ range.lower().x(), range.lower().y(),
+ size.x(), size.y(),
+ angle, raw->image, opacity
+ );
+}
+
+simple::vg::paint framebuffer::paint(float opacity, float angle) const
+{
+ return paint({int2::zero(), size}, opacity, angle);
+}
+
+void framebuffer::deleter::operator()(NVGLUframebuffer* raw) const noexcept
+{
+ nvgluDeleteFramebuffer(raw);
}
paint::paint(NVGpaint raw) noexcept : raw(raw) {}
@@ -67,9 +111,45 @@ paint paint::radial_gradient(float2 center, rangef radius, support::range<rgba_v
));
}
+paint paint::radial_gradient(range2f bounds, rangef radius, support::range<rgba_vector> colors) noexcept
+{
+ auto size = (bounds.upper() - bounds.lower())/2;
+ return radial_gradient(
+ bounds.lower() + size,
+ radius * size.x(),
+ colors
+ );
+}
+
+frame::frame(NVGcontext* context, float2 size, float pixelRatio) noexcept :
+ size(size),
+ pixelRatio(pixelRatio),
+ buffer(nullptr),
+ context(context)
+{
+ nvgBeginFrame(context, size.x(), size.y(), pixelRatio);
+}
+
+frame::frame(NVGcontext* context, const framebuffer& fb) noexcept :
+ size(fb.size),
+ pixelRatio(1),
+ buffer(&fb),
+ context(context)
+{
+ nvgluBindFramebuffer(buffer->raw.get());
+ glClearColor(0,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ glViewport(
+ 0,0,
+ buffer->size.x(), buffer->size.y()
+ );
+ nvgBeginFrame(context, size.x(), size.y(), pixelRatio);
+}
+
frame::frame(frame&& other) noexcept :
size(other.size),
pixelRatio(other.pixelRatio),
+ buffer(other.buffer),
context(other.context)
{
other.context = nullptr;
@@ -79,6 +159,8 @@ frame::~frame() noexcept
{
if(context)
nvgEndFrame(context);
+ if(buffer)
+ nvgluBindFramebuffer(nullptr);
}
sketch frame::begin_sketch() noexcept
@@ -133,8 +215,20 @@ sketch& sketch::rectangle(const range2f& bounds) noexcept
sketch& sketch::line(float2 from, float2 to) noexcept
{
- nvgMoveTo(context, from.x(), from.y());
- nvgLineTo(context, to.x(), to.y());
+ move(from);
+ vertex(to);
+ return *this;
+}
+
+sketch& sketch::move(float2 to) noexcept
+{
+ nvgMoveTo(context, to.x(), to.y());
+ return *this;
+}
+
+sketch& sketch::vertex(float2 v) noexcept
+{
+ nvgLineTo(context, v.x(), v.y());
return *this;
}
diff --git a/common/simple_vg.h b/common/simple_vg.h
index 41eb6be06ef9d6361db63980e3f12d19ca704018..885df648a5d960df4159cecd94f3332bbc522956 100644
--- a/common/simple_vg.h
+++ b/common/simple_vg.h
@@ -11,6 +11,7 @@
namespace simple::vg
{
using float2 = geom::vector<float, 2>;
+ using int2 = geom::vector<int, 2>;
using graphical::rgb_vector;
using graphical::rgba_vector;
using graphical::rgb_pixel;
@@ -20,6 +21,10 @@ namespace simple::vg
using rect2f = geom::segment<float2>;
using anchored_rect2f = geom::anchored_segment<float2>;
+ class frame;
+ class framebuffer;
+ class sketch;
+
class paint
{
NVGpaint raw;
@@ -28,13 +33,13 @@ namespace simple::vg
public:
paint() = delete;
static paint radial_gradient(float2 center, rangef radius, support::range<rgba_vector>) noexcept;
+ static paint radial_gradient(range2f range, rangef radius, support::range<rgba_vector>) noexcept;
static paint linear_gradient() noexcept; // TODO
static paint range_gradient() noexcept; // TODO
friend class sketch;
+ friend class framebuffer;
};
- class frame;
-
class canvas
{
public:
@@ -53,6 +58,7 @@ namespace simple::vg
canvas& clear(const rgba_pixel& color) noexcept;
frame begin_frame(float2 size, float pixelRatio = 1) noexcept;
+ frame begin_frame(framebuffer&) const noexcept;
private:
@@ -62,13 +68,51 @@ namespace simple::vg
};
std::unique_ptr<NVGcontext, deleter> raw;
+
+ friend class framebuffer;
};
using ::operator |;
using ::operator &;
using ::operator &&;
- class sketch;
+ class framebuffer
+ {
+ public:
+ enum class flags :
+ std::underlying_type_t<NVGimageFlags>
+ {
+ none = 0,
+ mipmap = NVG_IMAGE_GENERATE_MIPMAPS,
+ repeat_x = NVG_IMAGE_REPEATX,
+ repeat_y = NVG_IMAGE_REPEATY,
+ flip_y = NVG_IMAGE_FLIPY,
+ premultiplied = NVG_IMAGE_PREMULTIPLIED,
+ nearest = NVG_IMAGE_NEAREST
+ };
+
+ const flags flags;
+ const int2 size;
+
+ framebuffer(int2 size, enum flags = flags::none) noexcept;
+ framebuffer(const canvas&, int2 size, enum flags = flags::none);
+ bool create(const canvas&);
+
+ vg::paint paint(support::range<int2>, float opacity = 1, float angle = 0) const;
+ vg::paint paint(float opacity = 1, float angle = 0) const;
+ private:
+
+ struct deleter
+ {
+ void operator()(NVGLUframebuffer*) const noexcept;
+ };
+
+ std::unique_ptr<NVGLUframebuffer, deleter> raw;
+
+ friend class frame;
+ };
+
+ // TODO: prevent two frames with same context from coexisting
class frame
{
public:
@@ -78,9 +122,11 @@ namespace simple::vg
frame(frame&&) noexcept;
const float2 size;
const float pixelRatio;
+ const framebuffer * const buffer;
private:
NVGcontext* context;
frame(NVGcontext*, float2 size, float pixelRatio = 1) noexcept;
+ frame(NVGcontext*, const framebuffer&) noexcept;
friend class canvas;
};
@@ -106,6 +152,11 @@ namespace simple::vg
sketch& line(float2 from, float2 to) noexcept;
sketch& arc(float2 center, rangef angle, float radius) noexcept;
sketch& arc(float2 center, float2 radius, float tau_factor, float anchor = 0) noexcept; // TODO
+ sketch& move(float2) noexcept;
+ sketch& vertex(float2) noexcept;
+
+ sketch& scale(float2) noexcept;
+ sketch& reset_matrix() noexcept;
sketch& fill(const paint&) noexcept;
sketch& fill(const rgba_vector&) noexcept;
@@ -134,4 +185,7 @@ namespace simple::vg
template<> struct simple::support::define_enum_flags_operators<simple::vg::canvas::flags>
: std::true_type {};
+template<> struct simple::support::define_enum_flags_operators<enum simple::vg::framebuffer::flags>
+ : std::true_type {};
+
#endif /* end of include guard */
diff --git a/common/sketchbook.hpp b/common/sketchbook.hpp
index eb938133c8969b376c14fe97cf9c043d9908a09d..48e6bcb56c79edf41c172e2ae1d13f768d06ce28 100644
--- a/common/sketchbook.hpp
+++ b/common/sketchbook.hpp
@@ -8,6 +8,7 @@
#include <vector>
#include <array>
#include <queue>
+#include <list>
#include "simple/support.hpp"
#include "simple/graphical.hpp"
@@ -16,13 +17,18 @@
#include "simple/interactive/event.h"
#include "simple_vg.h"
-#include "simple_vg.cpp"
+#include "simple_vg.cpp" // TODO: woops, don't do this
#include "math.hpp"
#if defined __EMSCRIPTEN__
#include <emscripten.h>
#endif
+#if defined __ANDROID__
+#undef main
+#endif
+
+using namespace simple::vg;
using namespace std::chrono_literals;
using namespace simple;
using namespace graphical::color_literals;
@@ -44,6 +50,7 @@ using scancode = interactive::scancode;
using keycode = interactive::keycode;
using mouse_button = interactive::mouse_button;
using common::lerp;
+using simple::support::make_range;
constexpr int max_int = std::numeric_limits<int>::max();
@@ -51,6 +58,7 @@ support::random::engine::tiny<unsigned> tiny_rand{std::random_device{}};
support::random::distribution::naive_int<int> tiny_int_dist{0, max_int};
support::random::distribution::naive_real<float> tiny_float_dist{0, 1};
+constexpr
auto trand_int(decltype(tiny_int_dist)::param_type range = {0, max_int})
{ return tiny_int_dist(tiny_rand, range); };
@@ -95,6 +103,16 @@ class Program
std::array<wave, 32> waves = {};
std::mutex dam;
+ std::list<std::pair<framebuffer, draw_fun>> framebuffers;
+ void create_framebuffers(const canvas& canvas)
+ {
+ for(auto&& [fb, draw] : framebuffers)
+ {
+ fb.create(canvas);
+ draw(canvas.begin_frame(fb));
+ }
+ }
+
bool run = true;
Program(const int argc, const char * const * const argv) : argc(argc), argv(argv) {}
@@ -106,6 +124,7 @@ class Program
std::string name = "";
int2 size = int2(400,400);
bool fullscreen = false;
+ graphical::display::mode display;
// nop works ok with function pointers without having to specify template params :/
@@ -152,8 +171,30 @@ class Program
current.remaining = current.total;
}
+ const framebuffer& request_framebuffer(int2 size, draw_fun draw,enum framebuffer::flags flags = framebuffer::flags::none)
+ {
+ framebuffers.emplace_back(
+ std::make_pair(
+ framebuffer(size, flags),
+ std::move(draw)
+ )
+ );
+ return framebuffers.back().first;
+ }
+
+ void remove_framebuffer(const framebuffer& framebuffer)
+ {
+ framebuffers.erase(std::find_if(
+ framebuffers.begin(),
+ framebuffers.end(),
+ [&](auto&& fb)
+ {
+ return &fb.first == &framebuffer;
+ }
+ ));
+ }
- friend int main(int argc, char const* argv[]);
+ friend int main(int argc, char* argv[]);
};
inline void process_events(Program&);
@@ -163,12 +204,15 @@ inline void process_events(Program&);
void start(Program&);
-int main(int argc, char const* argv[]) try
+int main(int argc, char* argv[]) try
{
Program program{argc, argv};
- start(program);
+
graphical::initializer graphics;
+ program.display = (*graphics.displays().begin()).current_mode();
+ start(program);
+
sdlcore::initializer interactions(sdlcore::system_flag::event);
// TODO: make optional
@@ -235,13 +279,17 @@ int main(int argc, char const* argv[]) try
std::cout << "vsync didn't work" << '\n';
program.frametime = framerate<60>::frametime;
}
- float2 win_size = float2(win.size());
+#if !defined NANOVG_GLES2 && !defined NANOVG_GLES3
glewInit();
+#endif
auto canvas = vg::canvas(vg::canvas::flags::antialias | vg::canvas::flags::stencil_strokes);
canvas.clear();
- program.draw_once(canvas.begin_frame(win_size));
+ program.create_framebuffers(canvas);
+ auto stolen_framebuffers = std::move(program.framebuffers);
+ glViewport(0,0, win.size().x(), win.size().y());
+ program.draw_once(canvas.begin_frame(float2(win.size())));
auto& now = Program::clock::now;
auto frame_start = now();
@@ -251,7 +299,8 @@ int main(int argc, char const* argv[]) try
frame_start = now();
process_events(program);
- program.draw_loop(canvas.begin_frame(win_size), delta_time);
+ canvas.clear();
+ program.draw_loop(canvas.begin_frame(float2(win.size())), delta_time);
win.update();
};
#if defined __EMSCRIPTEN__
@@ -317,6 +366,21 @@ void process_events(Program& program)
{
program.end();
},
+ [](const window_size_changed& w)
+ {
+ glViewport(0,0,
+ w.data.value.x(),
+ w.data.value.y()
+ );
+ },
[](auto) { }
}, *event);
}
+
+
+
+#if defined __ANDROID__
+extern "C" __attribute__((visibility("default")))
+int SDL_main(int argc, char* argv[]) { return main(argc, argv); }
+#endif
+