From d92f2c1969b27e86d4984b22ac30aea90671fb22 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 28 Jan 2020 22:06:44 +0700 Subject: [PATCH] camera modes --- headers/enduro2d/high/components/camera.hpp | 58 ++++++++++--- .../library/prefabs/background_prefab.json | 84 ++++++++++++++++++ samples/bin/library/scenes/sample_07.json | 4 +- .../scripts/emmy/components/camera.lua | 18 ++++ samples/bin/library/sprites/background.png | 3 + .../library/sprites/background_sprite.json | 5 ++ samples/sources/sample_07/sample_07.cpp | 3 +- .../high_binds/components/camera_binds.cpp | 35 ++++++++ sources/enduro2d/high/components/camera.cpp | 53 ++++++++++- .../enduro2d/high/systems/camera_system.cpp | 87 ++++++++++++++++++- .../enduro2d/high/systems/gizmos_system.cpp | 14 ++- .../render_system_drawer.cpp | 12 +-- 12 files changed, 339 insertions(+), 37 deletions(-) create mode 100644 samples/bin/library/prefabs/background_prefab.json create mode 100755 samples/bin/library/sprites/background.png create mode 100644 samples/bin/library/sprites/background_sprite.json diff --git a/headers/enduro2d/high/components/camera.hpp b/headers/enduro2d/high/components/camera.hpp index 827967b0..20449dac 100644 --- a/headers/enduro2d/high/components/camera.hpp +++ b/headers/enduro2d/high/components/camera.hpp @@ -17,32 +17,50 @@ namespace e2d class camera final { public: class gizmos final {}; + public: + ENUM_HPP_CLASS_DECL(modes, u8, + (manual) + (stretch) + (flexible) + (fixed_fit) + (fixed_crop)) public: camera() = default; camera& depth(i32 value) noexcept; + camera& mode(modes value) noexcept; camera& znear(f32 value) noexcept; camera& zfar(f32 value) noexcept; + camera& view(const m4f& value) noexcept; camera& viewport(const b2f& value) noexcept; + camera& projection(const m4f& value) noexcept; camera& target(const render_target_ptr& value) noexcept; camera& background(const color& value) noexcept; [[nodiscard]] i32 depth() const noexcept; + [[nodiscard]] modes mode() const noexcept; [[nodiscard]] f32 znear() const noexcept; [[nodiscard]] f32 zfar() const noexcept; + [[nodiscard]] const m4f& view() const noexcept; [[nodiscard]] const b2f& viewport() const noexcept; + [[nodiscard]] const m4f& projection() const noexcept; [[nodiscard]] const render_target_ptr& target() const noexcept; [[nodiscard]] const color& background() const noexcept; private: i32 depth_ = 0; + modes mode_ = modes::flexible; f32 znear_ = 0.f; f32 zfar_ = 1000.f; + m4f view_ = m4f::identity(); b2f viewport_ = b2f::unit(); + m4f projection_ = m4f::identity(); render_target_ptr target_ = nullptr; color background_ = color::clear(); }; } +ENUM_HPP_REGISTER_TRAITS(e2d::camera::modes) + namespace e2d { template <> @@ -92,6 +110,11 @@ namespace e2d return *this; } + inline camera& camera::mode(modes value) noexcept { + mode_ = value; + return *this; + } + inline camera& camera::znear(f32 value) noexcept { znear_ = value; return *this; @@ -102,11 +125,21 @@ namespace e2d return *this; } + inline camera& camera::view(const m4f& value) noexcept { + view_ = value; + return *this; + } + inline camera& camera::viewport(const b2f& value) noexcept { viewport_ = value; return *this; } + inline camera& camera::projection(const m4f& value) noexcept { + projection_ = value; + return *this; + } + inline camera& camera::target(const render_target_ptr& value) noexcept { target_ = value; return *this; @@ -121,6 +154,10 @@ namespace e2d return depth_; } + inline camera::modes camera::mode() const noexcept { + return mode_; + } + inline f32 camera::znear() const noexcept { return znear_; } @@ -129,10 +166,18 @@ namespace e2d return zfar_; } + inline const m4f& camera::view() const noexcept { + return view_; + } + inline const b2f& camera::viewport() const noexcept { return viewport_; } + inline const m4f& camera::projection() const noexcept { + return projection_; + } + inline const render_target_ptr& camera::target() const noexcept { return target_; } @@ -141,16 +186,3 @@ namespace e2d return background_; } } - -namespace e2d::cameras -{ - inline m4f make_projection_matrix(const camera& camera, const window& window) noexcept { - const v2u target_size = camera.target() - ? camera.target()->size() - : window.real_size(); - return math::make_orthographic_lh_matrix4( - target_size.cast_to(), - camera.znear(), - math::max(camera.zfar(), camera.znear() + math::default_precision())); - } -} diff --git a/samples/bin/library/prefabs/background_prefab.json b/samples/bin/library/prefabs/background_prefab.json new file mode 100644 index 00000000..0b88f705 --- /dev/null +++ b/samples/bin/library/prefabs/background_prefab.json @@ -0,0 +1,84 @@ +{ + "components" : {}, + "children" : [{ + "prototype" : "sprite_prefab.json", + "components" : { + "actor" : { + "translation" : [-512,0] + }, + "sprite_renderer" : { + "sprite" : "../sprites/background_sprite.json" + } + } + },{ + "prototype" : "sprite_prefab.json", + "components" : { + "actor" : { + "translation" : [-256,0] + }, + "sprite_renderer" : { + "sprite" : "../sprites/background_sprite.json" + } + } + },{ + "prototype" : "sprite_prefab.json", + "components" : { + "actor" : { + "translation" : [0,0] + }, + "sprite_renderer" : { + "sprite" : "../sprites/background_sprite.json" + } + } + },{ + "prototype" : "sprite_prefab.json", + "components" : { + "actor" : { + "translation" : [256,0] + }, + "sprite_renderer" : { + "sprite" : "../sprites/background_sprite.json" + } + } + },{ + "prototype" : "sprite_prefab.json", + "components" : { + "actor" : { + "translation" : [-512,-256] + }, + "sprite_renderer" : { + "sprite" : "../sprites/background_sprite.json" + } + } + },{ + "prototype" : "sprite_prefab.json", + "components" : { + "actor" : { + "translation" : [-256,-256] + }, + "sprite_renderer" : { + "sprite" : "../sprites/background_sprite.json" + } + } + },{ + "prototype" : "sprite_prefab.json", + "components" : { + "actor" : { + "translation" : [0,-256] + }, + "sprite_renderer" : { + "sprite" : "../sprites/background_sprite.json" + } + } + },{ + "prototype" : "sprite_prefab.json", + "components" : { + "actor" : { + "translation" : [256,-256] + }, + "sprite_renderer" : { + "sprite" : "../sprites/background_sprite.json" + } + } + }] +} diff --git a/samples/bin/library/scenes/sample_07.json b/samples/bin/library/scenes/sample_07.json index bfc3d104..a3678f06 100644 --- a/samples/bin/library/scenes/sample_07.json +++ b/samples/bin/library/scenes/sample_07.json @@ -7,6 +7,8 @@ }, "children" : [{ "prototype" : "../prefabs/camera_prefab.json" + },{ + "prototype" : "../prefabs/background_prefab.json" },{ "prototype" : "../prefabs/gnome_prefab.json", "components" : { @@ -28,7 +30,7 @@ "outline_color" : [0,0,0,255] }, "actor" : { - "translation" : [-315,-235], + "translation" : [-502,-246], "scale" : 1 }, "behaviour" : { diff --git a/samples/bin/library/scripts/emmy/components/camera.lua b/samples/bin/library/scripts/emmy/components/camera.lua index 297efb64..f7c40a1a 100644 --- a/samples/bin/library/scripts/emmy/components/camera.lua +++ b/samples/bin/library/scripts/emmy/components/camera.lua @@ -9,19 +9,37 @@ local camera = { ---@type integer depth = 0, + ---@type camera_modes + mode = camera.modes.flexible, + ---@type number znear = 0.0, ---@type number zfar = 1000.0, + ---@type m4f + view = m4f.identity(), + ---@type rect viewport = rect.zero(), + ---@type m4f + projection = m4f.identity(), + ---@type color background = color.white() } +---@class camera_modes +camera.modes = { + manual = "manual", + stretch = "stretch", + flexible = "flexible", + fixed_fit = "fixed_fit", + fixed_crop = "fixed_crop" +} + ---@overload fun(self: camera) ---@param self camera function camera.enable(self) end diff --git a/samples/bin/library/sprites/background.png b/samples/bin/library/sprites/background.png new file mode 100755 index 00000000..d860923f --- /dev/null +++ b/samples/bin/library/sprites/background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0739fc3ed0d3c14226ba7cb58d3afec1564f7a5101b1544f8b372e7d5ac18a5 +size 3132 diff --git a/samples/bin/library/sprites/background_sprite.json b/samples/bin/library/sprites/background_sprite.json new file mode 100644 index 00000000..e57b5343 --- /dev/null +++ b/samples/bin/library/sprites/background_sprite.json @@ -0,0 +1,5 @@ +{ + "texture" : "background.png", + "pivot" : { "x" : 0, "y" : 0 }, + "texrect" : { "x" : 0, "y" : 0, "w" : 256, "h" : 256 } +} diff --git a/samples/sources/sample_07/sample_07.cpp b/samples/sources/sample_07/sample_07.cpp index 99458bde..1e7c9254 100644 --- a/samples/sources/sample_07/sample_07.cpp +++ b/samples/sources/sample_07/sample_07.cpp @@ -60,7 +60,8 @@ int e2d_main(int argc, char *argv[]) { const auto starter_params = starter::parameters( engine::parameters("sample_07", "enduro2d") .window_params(engine::window_parameters() - .size({1024, 768})) + .size({1024, 512}) + .resizable(true)) .timer_params(engine::timer_parameters() .maximal_framerate(100))); modules::initialize(argc, argv, starter_params).start(); diff --git a/sources/enduro2d/high/bindings/high_binds/components/camera_binds.cpp b/sources/enduro2d/high/bindings/high_binds/components/camera_binds.cpp index a22f6332..f32f5ef7 100644 --- a/sources/enduro2d/high/bindings/high_binds/components/camera_binds.cpp +++ b/sources/enduro2d/high/bindings/high_binds/components/camera_binds.cpp @@ -58,6 +58,14 @@ namespace e2d::bindings::high c->depth(v); }), + "mode", sol::property( + [](const gcomponent& c) -> camera::modes { + return c->mode(); + }, + [](gcomponent& c, camera::modes v){ + c->mode(v); + }), + "znear", sol::property( [](const gcomponent& c) -> f32 { return c->znear(); @@ -74,6 +82,14 @@ namespace e2d::bindings::high c->zfar(v); }), + "view", sol::property( + [](const gcomponent& c) -> m4f { + return c->view(); + }, + [](gcomponent& c, const m4f& v){ + c->view(v); + }), + "viewport", sol::property( [](const gcomponent& c) -> b2f { return c->viewport(); @@ -82,6 +98,14 @@ namespace e2d::bindings::high c->viewport(v); }), + "projection", sol::property( + [](const gcomponent& c) -> m4f { + return c->projection(); + }, + [](gcomponent& c, const m4f& v){ + c->projection(v); + }), + "background", sol::property( [](const gcomponent& c) -> color { return c->background(); @@ -90,5 +114,16 @@ namespace e2d::bindings::high c->background(v); }) ); + + #define CAMERA_MODE_PAIR(x) {#x, camera::modes::x}, + l["camera"].get_or_create() + .new_enum("modes", { + CAMERA_MODE_PAIR(manual) + CAMERA_MODE_PAIR(stretch) + CAMERA_MODE_PAIR(flexible) + CAMERA_MODE_PAIR(fixed_fit) + CAMERA_MODE_PAIR(fixed_crop) + }); + #undef CAMERA_MODE_PAIR } } diff --git a/sources/enduro2d/high/components/camera.cpp b/sources/enduro2d/high/components/camera.cpp index d0315de7..e255c35a 100644 --- a/sources/enduro2d/high/components/camera.cpp +++ b/sources/enduro2d/high/components/camera.cpp @@ -13,11 +13,26 @@ namespace e2d "required" : [], "additionalProperties" : false, "properties" : { - "depth" : { "type" : "number" }, + "depth" : { "type" : "integer" }, + "mode" : { "$ref": "#/definitions/modes" }, "znear" : { "type" : "number" }, "zfar" : { "type" : "number" }, + "view" : { "$ref": "#/common_definitions/m4" }, "viewport" : { "$ref": "#/common_definitions/b2" }, + "projection" : { "$ref": "#/common_definitions/m4" }, "background" : { "$ref": "#/common_definitions/color" } + }, + "definitions" : { + "modes" : { + "type" : "string", + "enum" : [ + "manual", + "stretch", + "flexible", + "fixed_fit", + "fixed_crop" + ] + } } })json"; @@ -34,6 +49,15 @@ namespace e2d component.depth(depth); } + if ( ctx.root.HasMember("mode") ) { + camera::modes mode = component.mode(); + if ( !json_utils::try_parse_value(ctx.root["mode"], mode) ) { + the().error("CAMERA: Incorrect formatting of 'mode' property"); + return false; + } + component.mode(mode); + } + if ( ctx.root.HasMember("znear") ) { f32 znear = component.znear(); if ( !json_utils::try_parse_value(ctx.root["znear"], znear) ) { @@ -52,6 +76,15 @@ namespace e2d component.zfar(zfar); } + if ( ctx.root.HasMember("view") ) { + m4f view = component.view(); + if ( !json_utils::try_parse_value(ctx.root["view"], view) ) { + the().error("CAMERA: Incorrect formatting of 'view' property"); + return false; + } + component.view(view); + } + if ( ctx.root.HasMember("viewport") ) { b2f viewport = component.viewport(); if ( !json_utils::try_parse_value(ctx.root["viewport"], viewport) ) { @@ -61,6 +94,15 @@ namespace e2d component.viewport(viewport); } + if ( ctx.root.HasMember("projection") ) { + m4f projection = component.projection(); + if ( !json_utils::try_parse_value(ctx.root["projection"], projection) ) { + the().error("CAMERA: Incorrect formatting of 'projection' property"); + return false; + } + component.projection(projection); + } + if ( ctx.root.HasMember("target") ) { //TODO(BlackMat): add 'target' property parsing } @@ -133,6 +175,12 @@ namespace e2d c->depth(depth); } + if ( camera::modes mode = c->mode(); + imgui_utils::show_enum_combo_box("mode", &mode) ) + { + c->mode(mode); + } + if ( ImGui::TreeNode("clipping") ) { E2D_DEFER([](){ ImGui::TreePop(); }); @@ -151,6 +199,8 @@ namespace e2d } } + ///TODO(BlackMat): add 'view' inspector + if ( ImGui::TreeNode("viewport") ) { E2D_DEFER([](){ ImGui::TreePop(); }); @@ -167,6 +217,7 @@ namespace e2d } } + ///TODO(BlackMat): add 'projection' inspector ///TODO(BlackMat): add 'target' inspector if ( color background = c->background(); diff --git a/sources/enduro2d/high/systems/camera_system.cpp b/sources/enduro2d/high/systems/camera_system.cpp index f19bba19..12ceebb6 100644 --- a/sources/enduro2d/high/systems/camera_system.cpp +++ b/sources/enduro2d/high/systems/camera_system.cpp @@ -6,8 +6,73 @@ #include +#include #include +namespace +{ + using namespace e2d; + + m4f make_camera_view(const actor& actor) noexcept { + return actor.node() + ? math::inversed(actor.node()->world_matrix()).first + : m4f::identity(); + } + + m4f make_camera_projection(const camera& camera, const window& window) noexcept { + const f32 ortho_znear = camera.znear(); + const f32 ortho_zfar = math::max( + camera.zfar(), + camera.znear() + math::default_precision()); + + const v2f target_size = camera.target() + ? camera.target()->size().cast_to() + : window.real_size().cast_to(); + const v2f virtual_size = window.virtual_size().cast_to(); + + if ( math::is_near_zero(math::length_squared(target_size), 0.f) || + math::is_near_zero(math::length_squared(virtual_size), 0.f) ) + { + return camera.projection(); + } + + const f32 target_aspect = target_size.x / target_size.y; + const f32 virtual_aspect = virtual_size.x / virtual_size.y; + + switch ( camera.mode() ) { + case camera::modes::manual: + return camera.projection(); + case camera::modes::stretch: + return math::make_orthographic_lh_matrix4( + virtual_size, + ortho_znear, + ortho_zfar); + case camera::modes::flexible: + return math::make_orthographic_lh_matrix4( + target_size, + ortho_znear, + ortho_zfar); + case camera::modes::fixed_fit: + return math::make_orthographic_lh_matrix4( + target_aspect < virtual_aspect + ? v2f(virtual_size.x, virtual_size.y * (virtual_aspect / target_aspect)) + : v2f(virtual_size.x * (target_aspect / virtual_aspect), virtual_size.y), + ortho_znear, + ortho_zfar); + case camera::modes::fixed_crop: + return math::make_orthographic_lh_matrix4( + virtual_aspect < target_aspect + ? v2f(virtual_size.x, virtual_size.y * (virtual_aspect / target_aspect)) + : v2f(virtual_size.x * (target_aspect / virtual_aspect), virtual_size.y), + ortho_znear, + ortho_zfar); + default: + E2D_ASSERT_MSG(false, "unexpected camera mode"); + return camera.projection(); + } + } +} + namespace e2d { // @@ -16,12 +81,28 @@ namespace e2d class camera_system::internal_state final : private noncopyable { public: - internal_state() = default; + internal_state(window& w) + : window_(w) {} ~internal_state() noexcept = default; void process_update(ecs::registry& owner) { - E2D_UNUSED(owner); + owner.for_joined_components([]( + const ecs::const_entity&, + camera& c, + const actor& a) + { + c.view(make_camera_view(a)); + }); + + owner.for_each_component([this]( + const ecs::const_entity&, + camera& c) + { + c.projection(make_camera_projection(c, window_)); + }); } + private: + window& window_; }; // @@ -29,7 +110,7 @@ namespace e2d // camera_system::camera_system() - : state_(new internal_state()) {} + : state_(new internal_state(the())) {} camera_system::~camera_system() noexcept = default; void camera_system::process( diff --git a/sources/enduro2d/high/systems/gizmos_system.cpp b/sources/enduro2d/high/systems/gizmos_system.cpp index fc5ea82a..d2fd3383 100644 --- a/sources/enduro2d/high/systems/gizmos_system.cpp +++ b/sources/enduro2d/high/systems/gizmos_system.cpp @@ -22,9 +22,8 @@ namespace class imgui_gizmos_context final : public component_inspector<>::gizmos_context { public: - imgui_gizmos_context(editor& e, window& w, inspector& i) + imgui_gizmos_context(editor& e, inspector& i) : editor_(e) - , window_(w) , inspector_(i) {} bool setup_camera(const ecs::const_entity& cam_e) { @@ -41,8 +40,8 @@ namespace } camera_vp_ = - math::inversed(cam_n->world_matrix()).first * - cameras::make_projection_matrix(cam, window_) * + cam.view() * + cam.projection() * math::make_scale_matrix4(0.5f, 0.5f) * math::make_translation_matrix4(0.5f, 0.5f) * math::make_scale_matrix4(cam.viewport().size) * @@ -186,7 +185,6 @@ namespace } private: editor& editor_; - window& window_; inspector& inspector_; m4f camera_vp_ = m4f::identity(); m4f go_matrix_ = m4f::identity(); @@ -229,9 +227,9 @@ namespace e2d class gizmos_system::internal_state final : private noncopyable { public: - internal_state(dbgui& d, window& w, editor& e, inspector& i) + internal_state(dbgui& d, editor& e, inspector& i) : dbgui_(d) - , gcontext_(e, w, i) {} + , gcontext_(e, i) {} ~internal_state() noexcept = default; void process_render(const ecs::const_entity& cam_e, ecs::registry& owner) { @@ -256,7 +254,7 @@ namespace e2d // gizmos_system::gizmos_system() - : state_(new internal_state(the(), the(), the(), the())) {} + : state_(new internal_state(the(), the(), the())) {} gizmos_system::~gizmos_system() noexcept = default; void gizmos_system::process( diff --git a/sources/enduro2d/high/systems/render_system_impl/render_system_drawer.cpp b/sources/enduro2d/high/systems/render_system_impl/render_system_drawer.cpp index a986bbb0..f4e48ab6 100644 --- a/sources/enduro2d/high/systems/render_system_impl/render_system_drawer.cpp +++ b/sources/enduro2d/high/systems/render_system_impl/render_system_drawer.cpp @@ -50,16 +50,8 @@ namespace e2d::render_system_impl : render_(render) , batcher_(batcher) { - const m4f& cam_w = cam_n - ? cam_n->world_matrix() - : m4f::identity(); - const std::pair cam_w_inv = math::inversed(cam_w); - - const m4f& m_v = cam_w_inv.second - ? cam_w_inv.first - : m4f::identity(); - - const m4f& m_p = cameras::make_projection_matrix(cam, window); + const m4f& m_v = cam.view(); + const m4f& m_p = cam.projection(); batcher_.flush() .property(screen_s_property_hash, cam.target()