From: Mikko Rasa Date: Sat, 29 Oct 2022 22:16:18 +0000 (+0300) Subject: Add a view sub-library, including a Camera component X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=f298027c2042b63cec903c98dfc97e792a4f923f;p=libs%2Fgame.git Add a view sub-library, including a Camera component --- diff --git a/.gitignore b/.gitignore index 9ba0055..ecc88cd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ temp /libmspgame.a /libmspgame.so +/libmspgameview.a +/libmspgameview.so /mspgame.pc diff --git a/Build b/Build index 837d2a2..15fb6f7 100644 --- a/Build +++ b/Build @@ -5,6 +5,9 @@ package "mspgame" require "mspcore"; require "mspdatafile"; require "mspmath"; + require "mspgui"; + require "mspgl"; + require "sigc++-2.0"; build_info { @@ -20,4 +23,15 @@ package "mspgame" map "source" "include/msp"; }; }; + + library "mspgameview" + { + source "source/gameview"; + use "mspgame"; + install true; + install_map + { + map "source" "include/msp"; + }; + }; }; diff --git a/source/game/camera.cpp b/source/game/camera.cpp new file mode 100644 index 0000000..37c0f4d --- /dev/null +++ b/source/game/camera.cpp @@ -0,0 +1,36 @@ +#include "camera.h" + +using namespace std; + +namespace Msp::Game { + +Camera::Camera(Handle e, const CameraSetup &s): + Component(e), + setup(s), + fov_y(setup.field_of_view_y), + size(setup.size), + near_clip(setup.near_clip), + far_clip(setup.far_clip) +{ + if(!is_orthographic()) + size = { get_aspect(), 1.0f }; +} + +void Camera::set_field_of_view(Geometry::Angle f, float a) +{ + if(is_orthographic()) + throw logic_error("Camera is not perspective"); + + fov_y = f; + size = { a, 1.0f }; +} + +void Camera::set_size(const LinAl::Vector &s) +{ + if(!is_orthographic()) + throw logic_error("Camera is not orthographic"); + + size = s; +} + +} // namespace Msp::Game diff --git a/source/game/camera.h b/source/game/camera.h new file mode 100644 index 0000000..1e8efdd --- /dev/null +++ b/source/game/camera.h @@ -0,0 +1,59 @@ +#ifndef MSP_GAME_CAMERA_H_ +#define MSP_GAME_CAMERA_H_ + +#include +#include +#include "component.h" + +namespace Msp::Game { + +enum class CameraScaling +{ + ORIGINAL_SIZE, + SCALE_TO_FIT, + SCALE_TO_COVER, + STRETCH_TO_FIT +}; + +struct CameraSetup +{ + Geometry::Angle field_of_view_y = Geometry::Angle::from_degrees(60); + LinAl::Vector size = { 16.0f/9.0f, 1.0f }; + float near_clip = 0.1f; + float far_clip = 100.0f; + CameraScaling scaling = CameraScaling::SCALE_TO_FIT; + std::string sequence_name; +}; + +class Camera: public Component +{ +public: + using Setup = CameraSetup; + +private: + const Setup &setup; + Geometry::Angle fov_y; + LinAl::Vector size; + float height; + float near_clip; + float far_clip; + +public: + Camera(Handle, const Setup &); + + void set_field_of_view(Geometry::Angle, float); + void set_size(const LinAl::Vector &); + bool is_orthographic() const { return fov_y==Geometry::Angle::zero(); } + Geometry::Angle get_fov_vertical() const { return fov_y; } + Geometry::Angle get_fov_horizontal() const { return Geometry::atan(tan(fov_y/2.0f)*get_aspect())*2.0f; } + const LinAl::Vector &get_size() const { return size; } + float get_aspect() const { return size.x/size.y; } + float get_near_clip() const { return near_clip; } + float get_far_clip() const { return far_clip; } + CameraScaling get_scaling() const { return setup.scaling; } + const std::string &get_sequence_name() const { return setup.sequence_name; } +}; + +} // namespace Msp::Game + +#endif diff --git a/source/game/events.h b/source/game/events.h index 98a6ff0..106d3c4 100644 --- a/source/game/events.h +++ b/source/game/events.h @@ -5,6 +5,7 @@ namespace Msp::Game { +class Camera; class Component; class Entity; class Stage; @@ -46,6 +47,11 @@ struct StageDeactivated Stage &stage; }; +struct CameraChanged +{ + Handle camera; +}; + } // namespace Events } // namespace Msp::Game diff --git a/source/game/stage.cpp b/source/game/stage.cpp index 0eb4238..1c56c7e 100644 --- a/source/game/stage.cpp +++ b/source/game/stage.cpp @@ -1,14 +1,24 @@ #include "stage.h" +#include "camera.h" #include "root.h" #include "system.h" +using namespace std; + namespace Msp::Game { Stage::Stage(DataFile::Collection &r): resources(r), event_source(event_bus), + event_observer(event_bus), root(std::make_unique(*this)) -{ } +{ + event_observer.observe([this](auto &e){ + if(!active_camera) + if(Handle camera = dynamic_handle_cast(e.component)) + set_active_camera(camera); + }); +} // Hide unique_ptr destructors from the header Stage::~Stage() @@ -19,6 +29,12 @@ void Stage::remove_system(System &s) erase_if(systems, [&s](auto &p){ return p.get()==&s; }); } +void Stage::set_active_camera(Handle c) +{ + active_camera = c; + event_source.emit(active_camera); +} + void Stage::tick(Time::TimeDelta dt) { for(const auto &s: systems) diff --git a/source/game/stage.h b/source/game/stage.h index d2d05dd..19e70f4 100644 --- a/source/game/stage.h +++ b/source/game/stage.h @@ -11,6 +11,7 @@ namespace Msp::Game { +class Camera; class Root; class System; @@ -18,17 +19,19 @@ class Stage { public: using EventSource = Game::EventSource; + Events::ComponentCreated, Events::ComponentDestroyed, Events::CameraChanged>; private: DataFile::Collection &resources; PoolPool pools; EventBus event_bus; EventSource event_source; + EventObserver event_observer; /* Use unique_ptr because there's only one root per stage so it's pointless to put it in a pool. */ std::unique_ptr root; std::vector> systems; + Handle active_camera; public: Stage(DataFile::Collection &); @@ -52,6 +55,9 @@ public: template T *get_system() const; + void set_active_camera(Handle); + Handle get_active_camera() const { return active_camera; } + void tick(Time::TimeDelta); }; diff --git a/source/gameview/application.h b/source/gameview/application.h new file mode 100644 index 0000000..fbaa47b --- /dev/null +++ b/source/gameview/application.h @@ -0,0 +1,64 @@ +#ifndef MSP_GAMEVIEW_APPLICATION_H_ +#define MSP_GAMEVIEW_APPLICATION_H_ + +#include +#include +#include +#include +#include +#include +#include "presenter.h" + +namespace Msp::GameView { + +template +class Application: public Msp::RegisteredApplication +{ +public: + using ResourcesType = R; + +protected: + Msp::Graphics::Display display; + Msp::Graphics::Window window; + GL::Device gl_device; + ResourcesType resources; + Msp::Game::Director director; + GL::WindowView gl_view; + Presenter presenter; + +public: + Application(); + + int main() override; +protected: + void tick() override; +}; + +template +Application::Application(): + window(display, 1920, 1080), + gl_device(window), + director(resources), + gl_view(window), + presenter(director, gl_view) +{ + window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Application::exit), 0)); +} + +template +int Application::main() +{ + window.show(); + return Msp::Application::main(); +} + +template +void Application::tick() +{ + display.tick(); + director.tick(); +} + +} // namespace Msp::GameView + +#endif diff --git a/source/gameview/presenter.cpp b/source/gameview/presenter.cpp new file mode 100644 index 0000000..c65e2a9 --- /dev/null +++ b/source/gameview/presenter.cpp @@ -0,0 +1,35 @@ +#include "presenter.h" +#include +#include +#include +#include "renderer.h" + +using namespace std; + +namespace Msp::GameView { + +Presenter::Presenter(Game::Director &d, GL::View &v): + director(d), + gl_view(v), + resources(director.get_resources()), + event_observer(director.get_event_bus()) +{ + event_observer.observe([this](auto &e){ stage_activated(e); }); + + if(Game::Stage *active_stage = director.get_active_stage()) + stage_activated({ *active_stage }); +} + +Presenter::~Presenter() +{ + for(Renderer *r: renderers) + r->get_stage().remove_system(*r); +} + +void Presenter::stage_activated(const Game::Events::StageActivated &event) +{ + if(!event.stage.get_system()) + renderers.push_back(&event.stage.add_system(ref(gl_view))); +} + +} // namespace Msp::GameView diff --git a/source/gameview/presenter.h b/source/gameview/presenter.h new file mode 100644 index 0000000..fc89200 --- /dev/null +++ b/source/gameview/presenter.h @@ -0,0 +1,32 @@ +#ifndef MSP_GAMEVIEW_PRESENTER_H_ +#define MSP_GAMEVIEW_PRESENTER_H_ + +#include +#include +#include +#include + +namespace Msp::GameView { + +class Renderer; + +class Presenter +{ +private: + Game::Director &director; + GL::View &gl_view; + DataFile::Collection &resources; + Game::EventObserver event_observer; + std::vector renderers; + +public: + Presenter(Game::Director &, GL::View &); + ~Presenter(); + +private: + void stage_activated(const Game::Events::StageActivated &); +}; + +} // namespace Msp::GameView + +#endif diff --git a/source/gameview/renderer.cpp b/source/gameview/renderer.cpp new file mode 100644 index 0000000..4bce484 --- /dev/null +++ b/source/gameview/renderer.cpp @@ -0,0 +1,65 @@ +#include "renderer.h" +#include +#include +#include +#include + +using namespace std; + +namespace Msp::GameView { + +Renderer::Renderer(Game::Stage &s, GL::View &v): + System(s), + view(v), + event_observer(s.get_event_bus()) +{ + event_observer.observe([this](auto &e){ camera_changed(e); }); + + if(Game::Handle ac = stage.get_active_camera()) + camera_changed({ ac }); + + view.set_camera(&gl_camera); +} + +Renderer::~Renderer() +{ } + +void Renderer::camera_changed(const Game::Events::CameraChanged &event) +{ + active_camera = event.camera; + if(event.camera) + { + const string &seq_name = event.camera->get_sequence_name(); + if(seq_name!=current_seq_name) + { + current_seq_name = seq_name; + GL::SequenceBuilder bld(stage.get_resources().get(seq_name)); + bld.set_renderable("content", scene); + sequence.reset(bld.build(view)); + view.set_content(sequence.get()); + } + } +} + +void Renderer::tick(Time::TimeDelta) +{ + if(active_camera) + { + Game::Handle transform = active_camera->get_entity()->get_transform(); + if(transform) + gl_camera.set_object_matrix(transform->get_world_matrix()); + else + gl_camera.set_object_matrix(GL::Matrix()); + if(active_camera->is_orthographic()) + { + const LinAl::Vector &size = active_camera->get_size(); + gl_camera.set_orthographic(size.x, size.y); + } + else + gl_camera.set_field_of_view(active_camera->get_fov_vertical()); + gl_camera.set_depth_clip(active_camera->get_near_clip(), active_camera->get_far_clip()); + } + view.render(); +} + +} // namespace Msp::GameView diff --git a/source/gameview/renderer.h b/source/gameview/renderer.h new file mode 100644 index 0000000..4e90a74 --- /dev/null +++ b/source/gameview/renderer.h @@ -0,0 +1,40 @@ +#ifndef MSP_GAMEVIEW_RENDERER_H_ +#define MSP_GAMEVIEW_RENDERER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Msp::GameView { + +class Renderer: public Game::System +{ +private: + GL::View &view; + Game::EventObserver event_observer; + GL::SimpleScene scene; + Game::Handle active_camera; + GL::Camera gl_camera; + std::string current_seq_name; + std::unique_ptr sequence; + +public: + Renderer(Game::Stage &, GL::View &); + ~Renderer(); + +private: + void camera_changed(const Game::Events::CameraChanged &); + +public: + void tick(Time::TimeDelta) override; +}; + +} // namespace Msp::GameView + +#endif