diff --git a/headers/enduro2d/core/render.hpp b/headers/enduro2d/core/render.hpp index 804fae45..c82cfde7 100644 --- a/headers/enduro2d/core/render.hpp +++ b/headers/enduro2d/core/render.hpp @@ -10,12 +10,113 @@ namespace e2d { + class texture final : noncopyable { + public: + enum class wrap { + clamp, + repeat, + mirror + }; + enum class filter { + linear, + nearest + }; + public: + texture(); + ~texture() noexcept; + + void set_wrap(wrap u, wrap v) noexcept; + void set_filter(filter min, filter mag) noexcept; + private: + class internal_state; + std::unique_ptr state_; + }; + class render final : public module { + public: + enum class state { + blend, + cull_face, + depth_test, + stencil_test + }; + enum class cull_face { + front, + back, + front_back + }; + enum class blend_func { + zero, + one, + src_color, + one_minus_src_color, + dst_color, + one_minus_dst_color, + src_alpha, + one_minus_src_alpha, + dst_alpha, + one_minus_dst_alpha, + constant_color, + one_minus_constant_color, + constant_alpha, + one_minus_constant_alpha, + src_alpha_saturate + }; + enum class depth_func { + never, + less, + lequal, + greater, + gequal, + equal, + notequal, + always + }; + enum class stencil_func { + never, + less, + lequal, + greater, + gequal, + equal, + notequal, + always + }; + enum class stencil_op { + keep, + zero, + replace, + incr, + incr_wrap, + decr, + decr_wrap, + invert + }; public: render(); ~render() noexcept; + + void clear(bool color, bool depth, bool stencil) noexcept; + + void set_view(const m4f& view) noexcept; + void set_projection(const m4f& projection) noexcept; + void set_viewport(f32 x, f32 y, f32 w, f32 h) noexcept; + + void enable_state(state state) noexcept; + void disable_state(state state) noexcept; + + void set_cull_face(cull_face cull_face) noexcept; + void set_depth_func(depth_func depth_func) noexcept; + void set_depth_mask(bool yesno) noexcept; + void set_stencil_func(stencil_func stencil_func, i32 ref, u32 mask) noexcept; + void set_stencil_mask(u32 mask) noexcept; + void set_stencil_op(stencil_op fail, stencil_op zfail, stencil_op zpass) noexcept; + void set_clear_color(const color& color) noexcept; + void set_color_mask(bool r, bool g, bool b, bool a); + void set_blend_func(blend_func src, blend_func dst) noexcept; + void set_blend_color(const color& color) noexcept; private: - class state; - std::unique_ptr state_; + class internal_state; + std::unique_ptr state_; }; } diff --git a/samples/sources/sample_00/sample_00.cpp b/samples/sources/sample_00/sample_00.cpp index 66f96cfe..643c2b37 100644 --- a/samples/sources/sample_00/sample_00.cpp +++ b/samples/sources/sample_00/sample_00.cpp @@ -8,22 +8,20 @@ using namespace e2d; int e2d_main() { - input& i = modules::initialize(); - debug& d = modules::initialize(); - window& w = modules::initialize(v2u{640, 480}, "Enduro2D", false); + modules::initialize(); + modules::initialize(); + modules::initialize(); + modules::initialize(v2u{640, 480}, "Enduro2D", false); - d.add_sink(); - w.register_event_listener(i); - w.register_event_listener(d); + the().add_sink(); + the().register_event_listener(the()); - d.trace("SAMPLE: window real size: %0", w.real_size()) - .trace("SAMPLE: window virtual size: %0", w.virtual_size()) - .trace("SAMPLE: window framebuffer size: %0", w.framebuffer_size()); - - const keyboard& k = i.keyboard(); - while ( !w.should_close() && !k.is_key_just_released(keyboard_key::escape) ) { - i.frame_tick(); - w.swap_buffers(true); + const keyboard& k = the().keyboard(); + while ( !the().should_close() && !k.is_key_just_released(keyboard_key::escape) ) { + the().set_clear_color({1.f, 0.4f, 0.f}); + the().clear(true, true, true); + the().frame_tick(); + the().swap_buffers(true); window::frame_tick(); } return 0; diff --git a/sources/enduro2d/core/render_impl/render.hpp b/sources/enduro2d/core/render_impl/render.hpp index c6c74811..63de08ab 100644 --- a/sources/enduro2d/core/render_impl/render.hpp +++ b/sources/enduro2d/core/render_impl/render.hpp @@ -9,7 +9,7 @@ #include #define E2D_RENDER_MODE_NONE 1 -#define E2D_RENDER_MODE_OGL 2 +#define E2D_RENDER_MODE_OPENGL 2 #ifndef E2D_RENDER_MODE # if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_IOS @@ -17,7 +17,7 @@ # elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_LINUX # define E2D_RENDER_MODE E2D_RENDER_MODE_NONE # elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_MACOSX -# define E2D_RENDER_MODE E2D_RENDER_MODE_OGL +# define E2D_RENDER_MODE E2D_RENDER_MODE_OPENGL # elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_WINDOWS # define E2D_RENDER_MODE E2D_RENDER_MODE_NONE # endif diff --git a/sources/enduro2d/core/render_impl/render_none.cpp b/sources/enduro2d/core/render_impl/render_none.cpp index 3eeb27ca..f6054334 100644 --- a/sources/enduro2d/core/render_impl/render_none.cpp +++ b/sources/enduro2d/core/render_impl/render_none.cpp @@ -10,15 +10,113 @@ namespace e2d { - class render::state final : private e2d::noncopyable { + // + // texture::internal_state + // + + class texture::internal_state final : private e2d::noncopyable { public: - state() {} - ~state() noexcept = default; + internal_state() {} + ~internal_state() noexcept = default; }; + // + // render::internal_state + // + + class render::internal_state final : private e2d::noncopyable { + public: + internal_state() {} + ~internal_state() noexcept = default; + }; + + // + // texture + // + + texture::texture() + : state_(new internal_state()) {} + texture::~texture() noexcept = default; + + void texture::set_wrap(wrap u, wrap v) noexcept { + E2D_UNUSED(u, v); + } + + void texture::set_filter(filter min, filter mag) noexcept { + E2D_UNUSED(min, mag); + } + + // + // render + // + render::render() - : state_(new state()) {} + : state_(new internal_state()) {} render::~render() noexcept = default; + + void render::clear(bool color, bool depth, bool stencil) noexcept { + E2D_UNUSED(color, depth, stencil); + } + + void render::set_view(const m4f& view) noexcept { + E2D_UNUSED(view); + } + + void render::set_projection(const m4f& projection) noexcept { + E2D_UNUSED(projection); + } + + void render::set_viewport(f32 x, f32 y, f32 w, f32 h) noexcept { + E2D_UNUSED(x, y, w, h); + } + + void render::enable_state(state state) noexcept { + E2D_UNUSED(state); + } + + void render::disable_state(state state) noexcept { + E2D_UNUSED(state); + } + + void render::set_cull_face(cull_face cull_face) noexcept { + E2D_UNUSED(cull_face); + } + + void render::set_depth_func(depth_func depth_func) noexcept { + E2D_UNUSED(depth_func); + } + + void render::set_depth_mask(bool yesno) noexcept { + E2D_UNUSED(yesno); + } + + void render::set_stencil_func(stencil_func stencil_func, i32 ref, u32 mask) noexcept { + E2D_UNUSED(stencil_func, ref, mask); + } + + void render::set_stencil_mask(u32 mask) noexcept { + E2D_UNUSED(mask); + } + + void render::set_stencil_op(stencil_op fail, stencil_op zfail, stencil_op zpass) noexcept { + E2D_UNUSED(fail, zfail, zpass); + } + + void render::set_clear_color(const color& color) noexcept { + E2D_UNUSED(color); + } + + void render::set_color_mask(bool r, bool g, bool b, bool a) { + E2D_UNUSED(r, g, b, a); + } + + void render::set_blend_func(blend_func src, blend_func dst) noexcept { + E2D_UNUSED(src, dst); + } + + void render::set_blend_color(const color& color) noexcept { + E2D_UNUSED(color); + } } #endif diff --git a/sources/enduro2d/core/render_impl/render_ogl.cpp b/sources/enduro2d/core/render_impl/render_ogl.cpp deleted file mode 100644 index 581404cd..00000000 --- a/sources/enduro2d/core/render_impl/render_ogl.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - * This file is part of the "Enduro2D" - * For conditions of distribution and use, see copyright notice in LICENSE.md - * Copyright (C) 2018 Matvey Cherevko - ******************************************************************************/ - -#include "render.hpp" - -#if defined(E2D_RENDER_MODE) && E2D_RENDER_MODE == E2D_RENDER_MODE_OGL - -namespace e2d -{ - class render::state final : private e2d::noncopyable { - public: - state() {} - ~state() noexcept = default; - }; - - render::render() - : state_(new state()) {} - render::~render() noexcept = default; -} - -#endif diff --git a/sources/enduro2d/core/render_impl/render_opengl.cpp b/sources/enduro2d/core/render_impl/render_opengl.cpp new file mode 100644 index 00000000..dfe854de --- /dev/null +++ b/sources/enduro2d/core/render_impl/render_opengl.cpp @@ -0,0 +1,318 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#include "render.hpp" + +#if defined(E2D_RENDER_MODE) && E2D_RENDER_MODE == E2D_RENDER_MODE_OPENGL + +#if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_MACOSX +# include +#endif + +namespace +{ + using namespace e2d; + + GLenum convert_wrap(texture::wrap w) noexcept { + #define DEFINE_CASE(x,y) case texture::wrap::x: return y; + switch ( w ) { + DEFINE_CASE(clamp, GL_CLAMP_TO_EDGE); + DEFINE_CASE(repeat, GL_REPEAT); + DEFINE_CASE(mirror, GL_MIRRORED_REPEAT); + default: + E2D_ASSERT_MSG(false, "unexpected texture wrap"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_filter(texture::filter f) noexcept { + #define DEFINE_CASE(x,y) case texture::filter::x: return y; + switch ( f ) { + DEFINE_CASE(linear, GL_LINEAR); + DEFINE_CASE(nearest, GL_NEAREST); + default: + E2D_ASSERT_MSG(false, "unexpected texture filter"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_state(render::state s) noexcept { + #define DEFINE_CASE(x,y) case render::state::x: return y; + switch ( s ) { + DEFINE_CASE(blend, GL_BLEND); + DEFINE_CASE(cull_face, GL_CULL_FACE); + DEFINE_CASE(depth_test, GL_DEPTH_TEST); + DEFINE_CASE(stencil_test, GL_STENCIL_TEST); + default: + E2D_ASSERT_MSG(false, "unexpected render state"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_cull_face(render::cull_face cf) noexcept { + #define DEFINE_CASE(x,y) case render::cull_face::x: return y; + switch ( cf ) { + DEFINE_CASE(front, GL_FRONT); + DEFINE_CASE(back, GL_BACK); + DEFINE_CASE(front_back, GL_FRONT_AND_BACK); + default: + E2D_ASSERT_MSG(false, "unexpected render cull face"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_blend_func(render::blend_func bf) noexcept { + #define DEFINE_CASE(x,y) case render::blend_func::x: return y; + switch ( bf ) { + DEFINE_CASE(zero, GL_ZERO); + DEFINE_CASE(one, GL_ONE); + DEFINE_CASE(src_color, GL_SRC_COLOR); + DEFINE_CASE(one_minus_src_color, GL_ONE_MINUS_SRC_COLOR); + DEFINE_CASE(dst_color, GL_DST_COLOR); + DEFINE_CASE(one_minus_dst_color, GL_ONE_MINUS_DST_COLOR); + DEFINE_CASE(src_alpha, GL_SRC_ALPHA); + DEFINE_CASE(one_minus_src_alpha, GL_ONE_MINUS_SRC_ALPHA); + DEFINE_CASE(dst_alpha, GL_DST_ALPHA); + DEFINE_CASE(one_minus_dst_alpha, GL_ONE_MINUS_DST_ALPHA); + DEFINE_CASE(constant_color, GL_CONSTANT_COLOR); + DEFINE_CASE(one_minus_constant_color, GL_ONE_MINUS_CONSTANT_COLOR); + DEFINE_CASE(constant_alpha, GL_CONSTANT_ALPHA); + DEFINE_CASE(one_minus_constant_alpha, GL_ONE_MINUS_CONSTANT_ALPHA); + DEFINE_CASE(src_alpha_saturate, GL_SRC_ALPHA_SATURATE); + default: + E2D_ASSERT_MSG(false, "unexpected render blend func"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_depth_func(render::depth_func df) noexcept { + #define DEFINE_CASE(x,y) case render::depth_func::x: return y; + switch ( df ) { + DEFINE_CASE(never, GL_NEVER); + DEFINE_CASE(less, GL_LESS); + DEFINE_CASE(lequal, GL_LEQUAL); + DEFINE_CASE(greater, GL_GREATER); + DEFINE_CASE(gequal, GL_GEQUAL); + DEFINE_CASE(equal, GL_EQUAL); + DEFINE_CASE(notequal, GL_NOTEQUAL); + DEFINE_CASE(always, GL_ALWAYS); + default: + E2D_ASSERT_MSG(false, "unexpected render depth func"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_stencil_func(render::stencil_func sf) noexcept { + #define DEFINE_CASE(x,y) case render::stencil_func::x: return y; + switch ( sf ) { + DEFINE_CASE(never, GL_NEVER); + DEFINE_CASE(less, GL_LESS); + DEFINE_CASE(lequal, GL_LEQUAL); + DEFINE_CASE(greater, GL_GREATER); + DEFINE_CASE(gequal, GL_GEQUAL); + DEFINE_CASE(equal, GL_EQUAL); + DEFINE_CASE(notequal, GL_NOTEQUAL); + DEFINE_CASE(always, GL_ALWAYS); + default: + E2D_ASSERT_MSG(false, "unexpected render stencil func"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_stencil_op(render::stencil_op so) noexcept { + #define DEFINE_CASE(x,y) case render::stencil_op::x: return y; + switch ( so ) { + DEFINE_CASE(keep, GL_KEEP); + DEFINE_CASE(zero, GL_ZERO); + DEFINE_CASE(replace, GL_REPLACE); + DEFINE_CASE(incr, GL_INCR); + DEFINE_CASE(incr_wrap, GL_INCR_WRAP); + DEFINE_CASE(decr, GL_DECR); + DEFINE_CASE(decr_wrap, GL_DECR_WRAP); + DEFINE_CASE(invert, GL_INVERT); + default: + E2D_ASSERT_MSG(false, "unexpected render stencil op"); + return 0; + } + #undef DEFINE_CASE + } +} + +namespace e2d +{ + // + // texture::internal_state + // + + class texture::internal_state final : private e2d::noncopyable { + public: + internal_state() {} + ~internal_state() noexcept = default; + }; + + // + // render::internal_state + // + + class render::internal_state final : private e2d::noncopyable { + public: + std::mutex mutex; + m4f view; + m4f projection; + public: + internal_state() {} + ~internal_state() noexcept = default; + }; + + // + // texture + // + + texture::texture() + : state_(new internal_state()) {} + texture::~texture() noexcept = default; + + void texture::set_wrap(wrap u, wrap v) noexcept { + E2D_UNUSED(convert_wrap(u), convert_wrap(v)); + } + + void texture::set_filter(filter min, filter mag) noexcept { + E2D_UNUSED(convert_filter(min), convert_filter(mag)); + } + + // + // render + // + + render::render() + : state_(new internal_state()) {} + render::~render() noexcept = default; + + void render::clear(bool color, bool depth, bool stencil) noexcept { + std::lock_guard guard(state_->mutex); + GLbitfield mask = 0; + if ( color ) { + mask |= GL_COLOR_BUFFER_BIT; + } + if ( depth ) { + mask |= GL_DEPTH_BUFFER_BIT; + } + if ( stencil ) { + mask |= GL_STENCIL_BUFFER_BIT; + } + glClear(mask); + } + + void render::set_view(const m4f& view) noexcept { + std::lock_guard guard(state_->mutex); + state_->view = view; + } + + void render::set_projection(const m4f& projection) noexcept { + std::lock_guard guard(state_->mutex); + state_->projection = projection; + } + + void render::set_viewport(f32 x, f32 y, f32 w, f32 h) noexcept { + std::lock_guard guard(state_->mutex); + glViewport( + math::numeric_cast(x), + math::numeric_cast(y), + math::numeric_cast(w), + math::numeric_cast(h)); + } + + void render::enable_state(state state) noexcept { + std::lock_guard guard(state_->mutex); + glEnable(convert_state(state)); + } + + void render::disable_state(state state) noexcept { + std::lock_guard guard(state_->mutex); + glDisable(convert_state(state)); + } + + void render::set_cull_face(cull_face cull_face) noexcept { + std::lock_guard guard(state_->mutex); + glCullFace(convert_cull_face(cull_face)); + } + + void render::set_depth_func(depth_func depth_func) noexcept { + std::lock_guard guard(state_->mutex); + glDepthFunc(convert_depth_func(depth_func)); + } + + void render::set_depth_mask(bool yesno) noexcept { + std::lock_guard guard(state_->mutex); + glDepthMask(yesno ? GL_TRUE : GL_FALSE); + } + + void render::set_stencil_func( + stencil_func stencil_func, i32 ref, u32 mask) noexcept + { + std::lock_guard guard(state_->mutex); + glStencilFunc( + convert_stencil_func(stencil_func), + math::numeric_cast(ref), + math::numeric_cast(mask)); + } + + void render::set_stencil_mask(u32 mask) noexcept { + std::lock_guard guard(state_->mutex); + glStencilMask(math::numeric_cast(mask)); + } + + void render::set_stencil_op( + stencil_op fail, stencil_op zfail, stencil_op zpass) noexcept + { + std::lock_guard guard(state_->mutex); + glStencilOp( + convert_stencil_op(fail), + convert_stencil_op(zfail), + convert_stencil_op(zpass)); + } + + void render::set_clear_color(const color& color) noexcept { + std::lock_guard guard(state_->mutex); + glClearColor( + math::numeric_cast(color.r), + math::numeric_cast(color.g), + math::numeric_cast(color.b), + math::numeric_cast(color.a)); + } + + void render::set_color_mask(bool r, bool g, bool b, bool a) { + std::lock_guard guard(state_->mutex); + glColorMask( + r ? GL_TRUE : GL_FALSE, + g ? GL_TRUE : GL_FALSE, + b ? GL_TRUE : GL_FALSE, + a ? GL_TRUE : GL_FALSE); + } + + void render::set_blend_func(blend_func src, blend_func dst) noexcept { + std::lock_guard guard(state_->mutex); + glBlendFunc(convert_blend_func(src), convert_blend_func(dst)); + } + + void render::set_blend_color(const color& color) noexcept { + std::lock_guard guard(state_->mutex); + glBlendColor( + math::numeric_cast(color.r), + math::numeric_cast(color.g), + math::numeric_cast(color.b), + math::numeric_cast(color.a)); + } +} + +#endif diff --git a/sources/enduro2d/core/window_impl/window_glfw.cpp b/sources/enduro2d/core/window_impl/window_glfw.cpp index 3522c8c5..1f9e442c 100644 --- a/sources/enduro2d/core/window_impl/window_glfw.cpp +++ b/sources/enduro2d/core/window_impl/window_glfw.cpp @@ -319,7 +319,7 @@ namespace e2d } glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); v2i real_size = fullscreen