From: Mikko Rasa Date: Mon, 15 May 2023 20:44:38 +0000 (+0300) Subject: Add support for shadows X-Git-Url: https://git.tdb.fi/?a=commitdiff_plain;h=7bb9a433db1a08b159afe5ef7b12ceb6559993ac;p=libs%2Fgame.git Add support for shadows --- diff --git a/source/game/light.cpp b/source/game/light.cpp index 91db96f..1afa519 100644 --- a/source/game/light.cpp +++ b/source/game/light.cpp @@ -7,4 +7,12 @@ Light::Light(Handle 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< 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 index 0000000..a5d7fbd --- /dev/null +++ b/source/game/shadowtarget.h @@ -0,0 +1,25 @@ +#ifndef MSP_GAME_SHADOWTARGET_H_ +#define MSP_GAME_SHADOWTARGET_H_ + +#include +#include "component.h" + +namespace Msp::Game { + +class MSPGAME_API ShadowTarget: public Component +{ +public: + using Setup = ShadowTargetSetup; + +private: + float radius; + +public: + ShadowTarget(Handle, const Setup &); + + float get_radius() const { return radius; } +}; + +} // namespace Msp::Game + +#endif diff --git a/source/gameview/lightemitter.cpp b/source/gameview/lightemitter.cpp index 0f78fe3..8b52b88 100644 --- a/source/gameview/lightemitter.cpp +++ b/source/gameview/lightemitter.cpp @@ -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(&storage)) + sm->add_light(*dl, size, caster); + else if(auto pl = get_if(&storage)) + sm->add_light(*pl, size, caster); +} + void LightEmitter::set_intensity(float i) { light->set_color(GL::Color(i)); diff --git a/source/gameview/lightemitter.h b/source/gameview/lightemitter.h index d3e2f83..d77cc02 100644 --- a/source/gameview/lightemitter.h +++ b/source/gameview/lightemitter.h @@ -7,6 +7,7 @@ #include #include #include +#include #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); diff --git a/source/gameview/renderer.cpp b/source/gameview/renderer.cpp index 8a4c416..b6f78e1 100644 --- a/source/gameview/renderer.cpp +++ b/source/gameview/renderer.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -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(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(event.component)) i->mesh_renderer = nullptr; - if(dynamic_handle_cast(event.component)) + if(auto light = dynamic_handle_cast(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 lights; + + unsigned total_area = 0; + stage.iterate_objects([&lights, &total_area, this](LightEmitter &e){ + if(Game::Handle light = e.get_entity()->get_component()) + { + 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*2 ? width/2 : width); + + if(lights.empty()) + { + shadow_map = nullptr; + content_slot.set(&scene); + } + else + { + sort(lights); + + if(!shadow_seq) + { + shadow_seq = make_unique(); + 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(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 shadow_bounds; + stage.iterate_objects([&shadow_bounds](Game::ShadowTarget &s){ + LinAl::Vector pos = s.get_entity()->get_transform()->get_position(); + shadow_bounds = shadow_bounds | Geometry::BoundingSphere(pos, s.get_radius()); + }); + + shadow_map->set_target(shadow_bounds.get_center(), shadow_bounds.get_radius()); + } + if(active_camera) { Game::Handle transform = active_camera->get_entity()->get_transform(); diff --git a/source/gameview/renderer.h b/source/gameview/renderer.h index 3b39945..c37bc33 100644 --- a/source/gameview/renderer.h +++ b/source/gameview/renderer.h @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include #include "mspgameview_api.h" @@ -31,14 +33,27 @@ private: RenderedEntity(Game::Handle); }; + 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 entities; GL::SimpleScene scene; GL::Lighting lighting; + std::unique_ptr shadow_map; + std::unique_ptr shadow_seq; Game::Handle active_camera; GL::Camera gl_camera; std::unique_ptr sequence; + GL::Slot content_slot; + unsigned shadow_base_size = 4096; + bool shadows_changed = false; public: Renderer(Game::Stage &, GL::View &);