]> git.tdb.fi Git - libs/game.git/commitdiff
Add a view sub-library, including a Camera component
authorMikko Rasa <tdb@tdb.fi>
Sat, 29 Oct 2022 22:16:18 +0000 (01:16 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 29 Oct 2022 22:25:28 +0000 (01:25 +0300)
12 files changed:
.gitignore
Build
source/game/camera.cpp [new file with mode: 0644]
source/game/camera.h [new file with mode: 0644]
source/game/events.h
source/game/stage.cpp
source/game/stage.h
source/gameview/application.h [new file with mode: 0644]
source/gameview/presenter.cpp [new file with mode: 0644]
source/gameview/presenter.h [new file with mode: 0644]
source/gameview/renderer.cpp [new file with mode: 0644]
source/gameview/renderer.h [new file with mode: 0644]

index 9ba00551d9b5acfe3792320a4c2631b3619efd12..ecc88cd16ece4f3f7a9a9eb5926cf6338534f58c 100644 (file)
@@ -2,4 +2,6 @@
 temp
 /libmspgame.a
 /libmspgame.so
+/libmspgameview.a
+/libmspgameview.so
 /mspgame.pc
diff --git a/Build b/Build
index 837d2a2b9ded68f9796ee0b524d163308d9cbf86..15fb6f7d2ebed10c1bed9f1f1100ecd45e96bdf7 100644 (file)
--- 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 (file)
index 0000000..37c0f4d
--- /dev/null
@@ -0,0 +1,36 @@
+#include "camera.h"
+
+using namespace std;
+
+namespace Msp::Game {
+
+Camera::Camera(Handle<Entity> 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<float> 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<float, 2> &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 (file)
index 0000000..1e8efdd
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef MSP_GAME_CAMERA_H_
+#define MSP_GAME_CAMERA_H_
+
+#include <msp/geometry/angle.h>
+#include <msp/linal/vector.h>
+#include "component.h"
+
+namespace Msp::Game {
+
+enum class CameraScaling
+{
+       ORIGINAL_SIZE,
+       SCALE_TO_FIT,
+       SCALE_TO_COVER,
+       STRETCH_TO_FIT
+};
+
+struct CameraSetup
+{
+       Geometry::Angle<float> field_of_view_y = Geometry::Angle<float>::from_degrees(60);
+       LinAl::Vector<float, 2> 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<float> fov_y;
+       LinAl::Vector<float, 2> size;
+       float height;
+       float near_clip;
+       float far_clip;
+
+public:
+       Camera(Handle<Entity>, const Setup &);
+
+       void set_field_of_view(Geometry::Angle<float>, float);
+       void set_size(const LinAl::Vector<float, 2> &);
+       bool is_orthographic() const { return fov_y==Geometry::Angle<float>::zero(); }
+       Geometry::Angle<float> get_fov_vertical() const { return fov_y; }
+       Geometry::Angle<float> get_fov_horizontal() const { return Geometry::atan(tan(fov_y/2.0f)*get_aspect())*2.0f; }
+       const LinAl::Vector<float, 2> &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
index 98a6ff0febc3674be666ffbcd3d01965d84cd39e..106d3c4ef5aa4ffb6e0e67e44f8faec0e7d00186 100644 (file)
@@ -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> camera;
+};
+
 } // namespace Events
 } // namespace Msp::Game
 
index 0eb4238fcff375709404b00e52061730e0694cae..1c56c7e8a159abe3e41f32bb3b4b629eb4ffb533 100644 (file)
@@ -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<Root>(*this))
-{ }
+{
+       event_observer.observe<Events::ComponentCreated>([this](auto &e){
+               if(!active_camera)
+                       if(Handle<Camera> camera = dynamic_handle_cast<Camera>(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<Camera> c)
+{
+       active_camera = c;
+       event_source.emit<Events::CameraChanged>(active_camera);
+}
+
 void Stage::tick(Time::TimeDelta dt)
 {
        for(const auto &s: systems)
index d2d05dd986a1c2f21a1f9f59f9a1116d7b330448..19e70f416f54b512087f4560c5c06c9527f185c8 100644 (file)
@@ -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::EntityCreated, Events::EntityDestroyed,
-               Events::ComponentCreated, Events::ComponentDestroyed>;
+               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> root;
        std::vector<std::unique_ptr<System>> systems;
+       Handle<Camera> active_camera;
 
 public:
        Stage(DataFile::Collection &);
@@ -52,6 +55,9 @@ public:
        template<typename T>
        T *get_system() const;
 
+       void set_active_camera(Handle<Camera>);
+       Handle<Camera> 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 (file)
index 0000000..fbaa47b
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef MSP_GAMEVIEW_APPLICATION_H_
+#define MSP_GAMEVIEW_APPLICATION_H_
+
+#include <msp/core/application.h>
+#include <msp/game/director.h>
+#include <msp/gl/device.h>
+#include <msp/gl/windowview.h>
+#include <msp/graphics/display.h>
+#include <msp/graphics/window.h>
+#include "presenter.h"
+
+namespace Msp::GameView {
+
+template<typename T, typename R>
+class Application: public Msp::RegisteredApplication<T>
+{
+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<typename T, typename R>
+Application<T, R>::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<typename T, typename R>
+int Application<T, R>::main()
+{
+       window.show();
+       return Msp::Application::main();
+}
+
+template<typename T, typename R>
+void Application<T, R>::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 (file)
index 0000000..c65e2a9
--- /dev/null
@@ -0,0 +1,35 @@
+#include "presenter.h"
+#include <functional>
+#include <msp/game/events.h>
+#include <msp/game/stage.h>
+#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<Game::Events::StageActivated>([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<Renderer>())
+               renderers.push_back(&event.stage.add_system<Renderer>(ref(gl_view)));
+}
+
+} // namespace Msp::GameView
diff --git a/source/gameview/presenter.h b/source/gameview/presenter.h
new file mode 100644 (file)
index 0000000..fc89200
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef MSP_GAMEVIEW_PRESENTER_H_
+#define MSP_GAMEVIEW_PRESENTER_H_
+
+#include <msp/datafile/collection.h>
+#include <msp/game/director.h>
+#include <msp/game/eventobserver.h>
+#include <msp/gl/view.h>
+
+namespace Msp::GameView {
+
+class Renderer;
+
+class Presenter
+{
+private:
+       Game::Director &director;
+       GL::View &gl_view;
+       DataFile::Collection &resources;
+       Game::EventObserver event_observer;
+       std::vector<Renderer *> 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 (file)
index 0000000..4bce484
--- /dev/null
@@ -0,0 +1,65 @@
+#include "renderer.h"
+#include <msp/game/entity.h>
+#include <msp/game/stage.h>
+#include <msp/game/transform.h>
+#include <msp/gl/sequencebuilder.h>
+
+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<Game::Events::CameraChanged>([this](auto &e){ camera_changed(e); });
+
+       if(Game::Handle<Game::Camera> 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<GL::SequenceTemplate>(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<Game::Transform> 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<float, 2> &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 (file)
index 0000000..4e90a74
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef MSP_GAMEVIEW_RENDERER_H_
+#define MSP_GAMEVIEW_RENDERER_H_
+
+#include <msp/game/camera.h>
+#include <msp/game/eventobserver.h>
+#include <msp/game/events.h>
+#include <msp/game/owned.h>
+#include <msp/game/system.h>
+#include <msp/gl/camera.h>
+#include <msp/gl/sequence.h>
+#include <msp/gl/simplescene.h>
+#include <msp/gl/view.h>
+
+namespace Msp::GameView {
+
+class Renderer: public Game::System
+{
+private:
+       GL::View &view;
+       Game::EventObserver event_observer;
+       GL::SimpleScene scene;
+       Game::Handle<Game::Camera> active_camera;
+       GL::Camera gl_camera;
+       std::string current_seq_name;
+       std::unique_ptr<GL::Sequence> 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