]> git.tdb.fi Git - libs/game.git/commitdiff
Add support for shadows
authorMikko Rasa <tdb@tdb.fi>
Mon, 15 May 2023 20:44:38 +0000 (23:44 +0300)
committerMikko Rasa <tdb@tdb.fi>
Mon, 15 May 2023 20:44:38 +0000 (23:44 +0300)
source/game/light.cpp
source/game/light.h
source/game/setups.mgs
source/game/shadowtarget.cpp [new file with mode: 0644]
source/game/shadowtarget.h [new file with mode: 0644]
source/gameview/lightemitter.cpp
source/gameview/lightemitter.h
source/gameview/renderer.cpp
source/gameview/renderer.h

index 91db96f57734f1fbb4699798341d0641596db099..1afa519a46e63d748794ce78d578561b06c494f6 100644 (file)
@@ -7,4 +7,12 @@ Light::Light(Handle<Entity> e, const Setup &s):
        setup(s)
 { }
 
+unsigned Light::get_shadow_map_size(unsigned base) const
+{
+       if(setup.shadow_detail<0)
+               return base>>-setup.shadow_detail;
+       else
+               return base<<setup.shadow_detail;
+}
+
 } // namespace Msp::Game
index f14dc6d42721c0afa9dbc318b1b6fbdea8eba6aa..5787db0393937474d4e2070c4a75382670998160 100644 (file)
@@ -20,6 +20,8 @@ public:
 
        LightType get_type() const { return setup.type; }
        float get_intensity() const { return setup.intensity; }
+       bool casts_shadows() const { return setup.cast_shadows; }
+       unsigned get_shadow_map_size(unsigned) const;
 };
 
 } // namespace Msp::Game
index 6cad21475d860230277e62353b2a44806fcc671f..8e62edc821de1af2fadfcfe53a8ffdb530f91a70 100644 (file)
@@ -28,6 +28,13 @@ component Light
 {
        field type LightType { default "DIRECTIONAL"; };
        field intensity float { default "1.0f"; };
+       field cast_shadows bool { default "false"; };
+       field shadow_detail int { default "0"; };
+};
+
+component ShadowTarget
+{
+       field radius float { default "100.0f"; };
 };
 
 component MeshSource
diff --git a/source/game/shadowtarget.cpp b/source/game/shadowtarget.cpp
new file mode 100644 (file)
index 0000000..61dd64d
--- /dev/null
@@ -0,0 +1,10 @@
+#include "shadowtarget.h"
+
+namespace Msp::Game {
+
+ShadowTarget::ShadowTarget(Handle<Entity> e, const Setup &setup):
+       Component(e),
+       radius(setup.radius)
+{ }
+
+} // namespace Msp::Game
diff --git a/source/game/shadowtarget.h b/source/game/shadowtarget.h
new file mode 100644 (file)
index 0000000..a5d7fbd
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef MSP_GAME_SHADOWTARGET_H_
+#define MSP_GAME_SHADOWTARGET_H_
+
+#include <msp/game/setups.h>
+#include "component.h"
+
+namespace Msp::Game {
+
+class MSPGAME_API ShadowTarget: public Component
+{
+public:
+       using Setup = ShadowTargetSetup;
+
+private:
+       float radius;
+
+public:
+       ShadowTarget(Handle<Entity>, const Setup &);
+
+       float get_radius() const { return radius; }
+};
+
+} // namespace Msp::Game
+
+#endif
index 0f78fe3be1548c3cea9bc0bba115e828c9a409b6..8b52b885399ed6e624edd964653e91b3be143a59 100644 (file)
@@ -28,6 +28,14 @@ void LightEmitter::set_lighting(GL::Lighting *l)
                lighting->attach(*light);
 }
 
+void LightEmitter::set_shadow_map(GL::ShadowMap *sm, unsigned size, GL::Renderable &caster)
+{
+       if(auto dl = get_if<GL::DirectionalLight>(&storage))
+               sm->add_light(*dl, size, caster);
+       else if(auto pl = get_if<GL::PointLight>(&storage))
+               sm->add_light(*pl, size, caster);
+}
+
 void LightEmitter::set_intensity(float i)
 {
        light->set_color(GL::Color(i));
index d3e2f83d198ce31c4c3fca84e3f24aebd3eb0998..d77cc02741f664bfcd2752b254971e3ea066165b 100644 (file)
@@ -7,6 +7,7 @@
 #include <msp/gl/directionallight.h>
 #include <msp/gl/lighting.h>
 #include <msp/gl/pointlight.h>
+#include <msp/gl/shadowmap.h>
 #include "mspgameview_api.h"
 
 namespace Msp::GameView {
@@ -23,6 +24,7 @@ public:
        ~LightEmitter();
 
        void set_lighting(GL::Lighting *);
+       void set_shadow_map(GL::ShadowMap *, unsigned, GL::Renderable &);
 
        void set_intensity(float);
 
index 8a4c416f2da53017fe1b9d2948d07c737862a2c8..b6f78e1b88d08690d9a885b817a76b7195250f08 100644 (file)
@@ -2,6 +2,7 @@
 #include <msp/game/entity.h>
 #include <msp/game/light.h>
 #include <msp/game/meshsource.h>
+#include <msp/game/shadowtarget.h>
 #include <msp/game/stage.h>
 #include <msp/game/transform.h>
 #include <msp/gl/colorcurve.h>
@@ -30,6 +31,7 @@ Renderer::Renderer(Game::Stage &s, GL::View &v):
 
        stage.synthesize_initial_events(event_observer);
 
+       content_slot.set(&scene);
        view.set_camera(&gl_camera);
 }
 
@@ -65,6 +67,8 @@ void Renderer::component_created(const Game::Events::ComponentCreated &event)
                        re.light_emitter = Game::Owned<LightEmitter>(re.entity, light->get_type());
                        re.light_emitter->set_intensity(light->get_intensity());
                        re.light_emitter->set_lighting(&lighting);
+                       if(light->casts_shadows())
+                               shadows_changed = true;
                }
        }
 }
@@ -85,8 +89,12 @@ void Renderer::component_destroyed(const Game::Events::ComponentDestroyed &event
        {
                if(dynamic_handle_cast<Game::MeshSource>(event.component))
                        i->mesh_renderer = nullptr;
-               if(dynamic_handle_cast<Game::Light>(event.component))
+               if(auto light = dynamic_handle_cast<Game::Light>(event.component))
+               {
                        i->light_emitter = nullptr;
+                       if(light->casts_shadows())
+                               shadows_changed = true;
+               }
        }
 }
 
@@ -100,7 +108,7 @@ void Renderer::camera_changed(const Game::Events::CameraChanged &event)
                sequence->set_clear_enabled(true);
                sequence->set_clear_colors({ GL::Color(0.0f) });
 
-               GL::Sequence::Step &opaque = sequence->add_step(GL::Tag(), scene);
+               GL::Sequence::Step &opaque = sequence->add_step(GL::Tag(), content_slot);
                opaque.set_depth_test(GL::LEQUAL);
                opaque.set_lighting(&lighting);
 
@@ -115,6 +123,63 @@ void Renderer::camera_changed(const Game::Events::CameraChanged &event)
 
 void Renderer::tick(Time::TimeDelta)
 {
+       if(shadows_changed)
+       {
+               shadows_changed = false;
+
+               vector<ShadowedLight> lights;
+
+               unsigned total_area = 0;
+               stage.iterate_objects<LightEmitter>([&lights, &total_area, this](LightEmitter &e){
+                       if(Game::Handle<Game::Light> light = e.get_entity()->get_component<Game::Light>())
+                       {
+                               unsigned size = light->get_shadow_map_size(shadow_base_size);;
+                               total_area += size*size;
+                               lights.emplace_back(&e, size);
+                       }
+               });
+
+               unsigned width = 1;
+               while(width*width<total_area)
+                       width <<= 1;
+               unsigned height = (width*width>=total_area*2 ? width/2 : width);
+
+               if(lights.empty())
+               {
+                       shadow_map = nullptr;
+                       content_slot.set(&scene);
+               }
+               else
+               {
+                       sort(lights);
+
+                       if(!shadow_seq)
+                       {
+                               shadow_seq = make_unique<GL::Sequence>();
+                               shadow_seq->set_clear_enabled(true);
+                               GL::Sequence::Step &step = shadow_seq->add_step("shadow", scene);
+                               step.set_depth_test(GL::LEQUAL);
+                       }
+
+                       shadow_map = make_unique<GL::ShadowMap>(width, height, scene, lighting);
+                       for(const ShadowedLight &l: lights)
+                               l.light->set_shadow_map(shadow_map.get(), l.shadow_size, *shadow_seq);
+
+                       content_slot.set(shadow_map.get());
+               }
+       }
+
+       if(shadow_map)
+       {
+               Geometry::BoundingSphere<float, 3> shadow_bounds;
+               stage.iterate_objects<Game::ShadowTarget>([&shadow_bounds](Game::ShadowTarget &s){
+                       LinAl::Vector<float, 3> pos = s.get_entity()->get_transform()->get_position();
+                       shadow_bounds = shadow_bounds | Geometry::BoundingSphere<float, 3>(pos, s.get_radius());
+               });
+
+               shadow_map->set_target(shadow_bounds.get_center(), shadow_bounds.get_radius());
+       }
+
        if(active_camera)
        {
                Game::Handle<Game::Transform> transform = active_camera->get_entity()->get_transform();
index 3b39945a19e4005e981664736181c3e8045971b6..c37bc338fa687af0c361e177df4257b7bf3d0c5b 100644 (file)
@@ -10,7 +10,9 @@
 #include <msp/gl/light.h>
 #include <msp/gl/lighting.h>
 #include <msp/gl/sequence.h>
+#include <msp/gl/shadowmap.h>
 #include <msp/gl/simplescene.h>
+#include <msp/gl/slot.h>
 #include <msp/gl/view.h>
 #include "mspgameview_api.h"
 
@@ -31,14 +33,27 @@ private:
                RenderedEntity(Game::Handle<Game::Entity>);
        };
 
+       struct ShadowedLight
+       {
+               LightEmitter *light;
+               unsigned shadow_size;
+
+               bool operator<(const ShadowedLight &other) const { return shadow_size>other.shadow_size; }
+       };
+
        GL::View &view;
        Game::EventObserver event_observer;
        std::vector<RenderedEntity> entities;
        GL::SimpleScene scene;
        GL::Lighting lighting;
+       std::unique_ptr<GL::ShadowMap> shadow_map;
+       std::unique_ptr<GL::Sequence> shadow_seq;
        Game::Handle<Game::Camera> active_camera;
        GL::Camera gl_camera;
        std::unique_ptr<GL::Sequence> sequence;
+       GL::Slot content_slot;
+       unsigned shadow_base_size = 4096;
+       bool shadows_changed = false;
 
 public:
        Renderer(Game::Stage &, GL::View &);