Commit fa72d48c authored by Georg Schaefer's avatar Georg Schaefer

Merge branch 'feature/11-ui-calibration-screen' into 'develop'

Resolve "UI: Calibration Screen"

Closes #11

See merge request !12
parents a6081b3a 96da00fd
Pipeline #3368 passed with stage
in 8 minutes and 40 seconds
audio: = audio/*.opus
cfg:sound_effects = settings/sound_effects.json
music: = music/*.opus
{
"fonts": [
{
"id" : "default",
"aid" : "font:ui/RobotoCondensed-Regular.ttf",
"size" : 20
"id": "default",
"aid": "font:ui/blanka-regular.otf",
"size": 30
},
{
"id": "heading",
"aid": "font:ui/blanka-regular.otf",
"size": 70
},
{
"id": "sub_heading",
"aid": "font:ui/blanka-regular.otf",
"size": 50
},
{
"id": "huge",
"aid": "font:ui/blanka-regular.otf",
"size": 300
}
]
}
\ No newline at end of file
}
......@@ -20,6 +20,8 @@
#include <mirrage/renderer/pass/tone_mapping_pass.hpp>
#include <mirrage/renderer/pass/transparent_pass.hpp>
#include <sinks/sdl_sink.hpp>
namespace phase_shifter {
using namespace mirrage;
......@@ -61,6 +63,10 @@ namespace phase_shifter {
{
util::erase_fast(_render_pass_mask, renderer::render_pass_id_of<renderer::Gui_pass_factory>());
util::erase_fast(_render_pass_mask, renderer::render_pass_id_of<renderer::Clear_pass_factory>());
eam::settings setting{{16, true, false, false}, 2, 48000, 1024};
_audio.set_sink<eam::sdl_sink>(setting);
_audio.set_master_volume(35);
}
Game_engine::~Game_engine()
......
......@@ -14,11 +14,13 @@
#include <mirrage/gui/debug_ui.hpp>
#include <mirrage/utils/maybe.hpp>
#include <audio.hpp>
namespace mirrage::renderer {
class Deferred_renderer_factory;
}
namespace phase_shifter {
class Game_engine : public mirrage::Engine {
......@@ -37,6 +39,11 @@ namespace phase_shifter {
auto renderer_factory() noexcept -> auto& { return *_renderer_factory; }
auto global_render() noexcept -> auto& { return *_global_render; }
auto render_pass_mask() noexcept -> auto& { return _render_pass_mask; }
auto audio() noexcept -> auto& { return _audio; }
auto audio_latency() const noexcept { return _audio_latency; }
auto audio_latency(float latency) noexcept { _audio_latency = latency; }
auto video_latency() const noexcept { return _video_latency; }
auto video_latency(float latency) noexcept { _video_latency = latency; }
protected:
void _on_post_frame(mirrage::util::Time) override;
......@@ -46,6 +53,9 @@ namespace phase_shifter {
std::unique_ptr<mirrage::renderer::Deferred_renderer_factory> _renderer_factory;
std::unique_ptr<mirrage::renderer::Deferred_renderer> _global_render;
mirrage::renderer::Render_pass_mask _render_pass_mask;
};
} // namespace seapp
eam::audio _audio;
float _audio_latency = 0.f;
float _video_latency = 0.f;
}; // namespace phase_shifter
} // namespace phase_shifter
......@@ -32,10 +32,9 @@ namespace phase_shifter {
Meta_system::Meta_system(Game_engine& engine)
: _entities(engine.assets(), this)
, _audio()
, _renderer(engine.renderer_factory().create_renderer(_entities, engine.render_pass_mask()))
, _model_loading(std::make_unique<renderer::Loading_system>(_entities, engine.assets()))
, _sound_manager(std::make_unique<audio::Sound_manager>(_audio, engine.bus(), engine.assets()))
, _sound_manager(std::make_unique<audio::Sound_manager>(engine.audio(), engine.bus(), engine.assets()))
, _beat_system(std::make_unique<gameplay::Beat_system>(engine.bus(), engine.assets()))
, _level_system(std::make_unique<level::Level_system>(_entities, engine.assets()))
, _movement_system(std::make_unique<gameplay::Movement_system>(
......@@ -59,9 +58,6 @@ namespace phase_shifter {
//s.scene_luminance_override = 300.f;
//_renderer->settings(s, false);
eam::settings setting{{16, true, false, false}, 2, 48000, 1024};
_audio.set_sink<eam::sdl_sink>(setting);
_audio.set_master_volume(35);
/*auto bg_music =
engine.assets().load<std::shared_ptr<eam::opus>>("audio:bg_music_01"_aid).get_blocking();
auto bg_context = _audio.generate_context(bg_music);
......@@ -93,29 +89,32 @@ namespace phase_shifter {
LOG(plog::info) << "Renderer Memory usage: " << msg.str();
}
});
_level_system->load("dummy");
entities()
.entity_builder("basic_enemy")
.position({20, 0, 15})
.post_create([=](auto entity) {
entity.process([&](gameplay::Fixed_path_comp& fixed_path) { fixed_path.update_path({}); });
entity.process([&](gameplay::Shooting_comp& shooting) {
shooting.set_patterns({gameplay::Bulletpattern({{0, 0}})});
});
})
.create();
auto bg_music = engine.assets().load<std::shared_ptr<eam::opus>>("music:pulse"_aid).get_blocking();
_music = engine.audio().generate_context_ptr(bg_music);
_music->get_object()->set_loops(-1);
_music->get_object()->set_position(engine.audio_latency()
/ _music->get_object()->get_length_in_sec());
}
Meta_system::~Meta_system()
{
_music->stop_fade(2.f);
_renderer->device().wait_idle();
_entities.clear();
}
void Meta_system::update(mirrage::util::Time dt)
{
if(_play_music) {
_play_music = false;
_music->play();
} else if(_first_frame) {
_play_music = true;
_first_frame = false;
}
_entities.process_queued_actions();
_beat_system->update(dt);
......
#pragma once
#include <audio.hpp>
#include <mirrage/ecs/ecs.hpp>
#include <mirrage/gui/debug_ui.hpp>
#include <mirrage/utils/units.hpp>
#include <audio.hpp>
#include <sinks/sdl_sink.hpp>
namespace mirrage {
......@@ -64,7 +65,6 @@ namespace phase_shifter {
private:
mirrage::ecs::Entity_manager _entities;
eam::audio _audio;
std::unique_ptr<mirrage::renderer::Deferred_renderer> _renderer;
std::unique_ptr<mirrage::renderer::Loading_system> _model_loading;
std::unique_ptr<audio::Sound_manager> _sound_manager;
......@@ -81,6 +81,9 @@ namespace phase_shifter {
std::unique_ptr<gameplay::Dash_system> _dash_system;
std::unique_ptr<gameplay::Stationary_attack_system> _stationary_attack_system;
std::unique_ptr<ui::Effect_system> _effect_system;
std::shared_ptr<eam::context> _music;
bool _first_frame = true;
bool _play_music = false;
mirrage::util::Console_command_container _commands;
};
......
#include "sync_screen.hpp"
#include "audio/audio_asset.hpp"
#include "game_engine.hpp"
#include "intro_screen.hpp"
#include "ui/util.hpp"
#include <mirrage/gui/gui.hpp>
#include <mirrage/input/input_manager.hpp>
......@@ -12,13 +14,14 @@
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <iomanip>
#include <sstream>
#include <thread>
#include <context.hpp>
namespace phase_shifter {
using namespace mirrage;
......@@ -33,6 +36,12 @@ namespace phase_shifter {
, _mailbox(engine.bus())
, _renderer(dynamic_cast<Game_engine&>(engine).renderer_factory().create_renderer(
mirrage::util::nothing, {renderer::render_pass_id_of<renderer::Clear_pass_factory>()}))
, _gui(&_engine.gui())
, _audio(&static_cast<Game_engine&>(_engine).audio())
, _start_audio_calibration_text("Press any button to start . . .")
, _start_video_calibration_text("Press any button to start . . .")
, _countdown(3)
, _signal("o")
{
_mailbox.subscribe_to([&](Once_action& e) {
switch(e.id) {
......@@ -40,12 +49,19 @@ namespace phase_shifter {
// TODO: show pause menu, instead
_engine.screens().leave();
break;
case "the_button"_strid: _on_button_press(); break;
}
});
}
Sync_screen::~Sync_screen() noexcept = default;
void Sync_screen::_on_enter(mirrage::util::maybe<Screen&>) { _mailbox.enable(); }
void Sync_screen::_on_enter(mirrage::util::maybe<Screen&>)
{
_mailbox.enable();
auto beat_sound = _engine.assets().load<std::shared_ptr<eam::opus>>("audio:beat"_aid);
_beat = _audio->generate_context_ptr(beat_sound.get_blocking());
_beat->get_object()->set_loops(5);
}
void Sync_screen::_on_leave(mirrage::util::maybe<Screen&>) { _mailbox.disable(); }
......@@ -54,21 +70,186 @@ namespace phase_shifter {
std::this_thread::sleep_for(std::chrono::milliseconds(8));
_mailbox.update_subscriptions();
_renderer->update(dt);
switch(_current_mode) {
case mode::start_audio_calibration: _start_audio_calibration_text.update(dt); break;
case mode::countdown_audio_calibration:
_countdown.update(dt);
if(_countdown.finished()) {
_countdown.reset(3);
_current_mode = mode::audio_calibration;
}
break;
case mode::audio_calibration:
if(_play_sample) {
_beat->play();
_play_sample = false;
_start = std::chrono::high_resolution_clock::now();
}
if(_audio_latencies.size() == 5) {
std::this_thread::sleep_for(std::chrono::seconds(1));
_audio_latency =
std::accumulate(_audio_latencies.begin() + 1, _audio_latencies.end() - 1, 0.f)
/ 3.f;
_current_mode = mode::start_video_calibration;
}
break;
case mode::start_video_calibration: _start_video_calibration_text.update(dt); break;
case mode::countdown_video_calibration:
_countdown.update(dt);
if(_countdown.finished()) {
_countdown.reset(3);
_current_mode = mode::video_calibration;
}
break;
case mode::video_calibration:
if(_play_sample) {
_play_sample = false;
_start = std::chrono::high_resolution_clock::now();
}
_signal.update(dt);
if(_video_latencies.size() == 5) {
_video_latency =
std::accumulate(_video_latencies.begin() + 1, _video_latencies.end() - 1, 0.f)
/ 3.f;
static_cast<Game_engine&>(_engine).audio_latency(_audio_latency);
static_cast<Game_engine&>(_engine).video_latency(_video_latency);
LOG(plog::debug) << "audio latency: " << _audio_latency;
LOG(plog::debug) << "video latency: " << _video_latency;
_engine.screens().enter<Intro_screen>();
}
break;
default: break;
}
}
auto Sync_screen::_on_button_press() -> void
{
switch(_current_mode) {
case mode::start_audio_calibration:
_play_sample = true;
_current_mode = mode::countdown_audio_calibration;
break;
case mode::audio_calibration:
_end = std::chrono::high_resolution_clock::now();
_audio_latencies.emplace_back(
std::chrono::duration_cast<std::chrono::milliseconds>(_end - _start).count()
/ 1000.f);
_start = std::chrono::high_resolution_clock::now();
break;
case mode::start_video_calibration:
_play_sample = true;
_current_mode = mode::countdown_video_calibration;
break;
case mode::video_calibration:
_end = std::chrono::high_resolution_clock::now();
_video_latencies.emplace_back(
std::chrono::duration_cast<std::chrono::milliseconds>(_end - _start).count()
/ 1000.f);
_start = std::chrono::high_resolution_clock::now();
break;
default: break;
}
}
void Sync_screen::_draw()
{
_renderer->draw(); //< clear screen
ImGui::PositionNextWindow(
glm::vec2(500, 500), ImGui::WindowPosition_X::center, ImGui::WindowPosition_Y::center);
if(ImGui::Begin("Sync")) {
ImGui::TextUnformatted("TODO");
if(ImGui::Button("OK")) {
_engine.screens().enter<Intro_screen>();
}
auto viewport = _gui->viewport();
auto font = _gui->find_font("default"_strid);
ui::push_font(font);
ImGui::PositionNextWindow(glm::vec2(viewport.z, viewport.w),
ImGui::WindowPosition_X::center,
ImGui::WindowPosition_Y::center);
if(ImGui::Begin("Sync",
nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoBackground)) {}
switch(_current_mode) {
case mode::start_audio_calibration: _draw_start_sync_audio_ui(); break;
case mode::countdown_audio_calibration: _draw_countdown_sync_audio_ui(); break;
case mode::start_video_calibration: _draw_start_sync_video_ui(); break;
case mode::countdown_video_calibration: _draw_countdown_sync_video_ui(); break;
case mode::video_calibration: _draw_sync_video_ui(); break;
default: break;
}
ImGui::End();
ui::pop_font(font);
}
auto Sync_screen::_draw_start_sync_audio_ui() -> void
{
auto viewport = _gui->viewport();
auto heading_font = _gui->find_font("heading"_strid);
auto sub_heading = _gui->find_font("sub_heading"_strid);
ui::push_font(heading_font);
ui::text_centered("Calibrate your audio output",
glm::vec2{viewport.z, viewport.w},
glm::vec2{0.f, viewport.w / 2.f - 70.f},
ui::center::horizontal);
ui::pop_font(heading_font);
ui::text_centered("Press any button when you hear the sound",
glm::vec2{viewport.z, viewport.w},
glm::vec2{0.f, viewport.w / 2.f},
ui::center::horizontal);
ui::push_font(sub_heading);
_start_audio_calibration_text.draw_centered(glm::vec2{viewport.z, viewport.w},
glm::vec2{0.f, viewport.w / 2.f + 220.f},
ui::center::horizontal);
ui::pop_font(sub_heading);
}
auto Sync_screen::_draw_countdown_sync_audio_ui() -> void
{
auto viewport = _gui->viewport();
auto huge = _gui->find_font("huge"_strid);
ui::push_font(huge);
_countdown.draw_centered(glm::vec2{viewport.z, viewport.w});
ui::pop_font(huge);
}
auto Sync_screen::_draw_sync_audio_ui() -> void {}
auto Sync_screen::_draw_start_sync_video_ui() -> void
{
auto viewport = _gui->viewport();
auto heading_font = _gui->find_font("heading"_strid);
auto sub_heading = _gui->find_font("sub_heading"_strid);
ui::push_font(heading_font);
ui::text_centered("Calibrate your video output",
glm::vec2{viewport.z, viewport.w},
glm::vec2{0.f, viewport.w / 2.f - 70.f},
ui::center::horizontal);
ui::pop_font(heading_font);
ui::text_centered("Press any button when you see the dot",
glm::vec2{viewport.z, viewport.w},
glm::vec2{0.f, viewport.w / 2.f},
ui::center::horizontal);
ui::push_font(sub_heading);
_start_video_calibration_text.draw_centered(glm::vec2{viewport.z, viewport.w},
glm::vec2{0.f, viewport.w / 2.f + 220.f},
ui::center::horizontal);
ui::pop_font(sub_heading);
}
auto Sync_screen::_draw_countdown_sync_video_ui() -> void
{
auto viewport = _gui->viewport();
auto huge = _gui->find_font("huge"_strid);
ui::push_font(huge);
_countdown.draw_centered(glm::vec2{viewport.z, viewport.w});
ui::pop_font(huge);
}
auto Sync_screen::_draw_sync_video_ui() -> void
{
auto viewport = _gui->viewport();
auto huge = _gui->find_font("huge"_strid);
ui::push_font(huge);
_signal.draw_centered(glm::vec2{viewport.z, viewport.w});
ui::pop_font(huge);
}
} // namespace phase_shifter
#pragma once
#include "ui/countdown.hpp"
#include "ui/fading_text.hpp"
#include <mirrage/engine.hpp>
#include <mirrage/gui/gui.hpp>
#include <mirrage/renderer/deferred_renderer.hpp>
#include <mirrage/utils/console_command.hpp>
#include <mirrage/utils/maybe.hpp>
#include <audio.hpp>
#include <chrono>
namespace phase_shifter {
......@@ -17,6 +24,15 @@ namespace phase_shifter {
auto name() const -> std::string override { return "Gameover"; }
protected:
enum class mode {
start_audio_calibration,
countdown_audio_calibration,
audio_calibration,
start_video_calibration,
countdown_video_calibration,
video_calibration
};
void _update(mirrage::util::Time delta_time) override;
void _draw() override;
......@@ -28,9 +44,32 @@ namespace phase_shifter {
return mirrage::Prev_screen_policy::discard;
}
auto _on_button_press() -> void;
auto _draw_start_sync_audio_ui() -> void;
auto _draw_countdown_sync_audio_ui() -> void;
auto _draw_sync_audio_ui() -> void;
auto _draw_start_sync_video_ui() -> void;
auto _draw_countdown_sync_video_ui() -> void;
auto _draw_sync_video_ui() -> void;
protected:
mirrage::util::Mailbox_collection _mailbox;
std::unique_ptr<mirrage::renderer::Deferred_renderer> _renderer;
mirrage::gui::Gui* _gui;
eam::audio* _audio;
mode _current_mode = mode::start_audio_calibration;
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
std::chrono::time_point<std::chrono::high_resolution_clock> _end;
bool _play_sample = true;
std::vector<float> _audio_latencies;
float _audio_latency = 0.f;
std::shared_ptr<eam::context> _beat;
std::vector<float> _video_latencies;
float _video_latency = 0.f;
ui::Fading_text _start_audio_calibration_text;
ui::Fading_text _start_video_calibration_text;
ui::Countdown _countdown;
ui::Fading_text _signal;
};
} // namespace phase_shifter
#pragma once
#include "util.hpp"
namespace phase_shifter::ui {
class Countdown {
public:
Countdown(int initial_value) : _value(initial_value) {}
auto update(mirrage::util::Time dt)
{
if(_value == 0) {
return;
}
_elapsed_time += dt.value();
if(_elapsed_time >= 1.f) {
_elapsed_time = 0.f;
--_value;
}
}
auto draw(const glm::vec2& position)
{
if(_value == 0) {
return;
}
text(std::to_string(_value), position);
}
auto draw_centered(const glm::vec2& window_size,
const glm::vec2& position = glm::vec2{0.f},
center center = center::all)
{
if(_value == 0) {
return;
}
text_centered(std::to_string(_value), window_size, position, center);
}
auto finished() const noexcept { return _value == 0; }
auto reset(int initial_value)
{
_value = initial_value;
_elapsed_time = 0.f;
}
private:
int _value;
float _elapsed_time = 0.f;
};
} // namespace phase_shifter::ui
#pragma once
#include "util.hpp"
namespace phase_shifter::ui {
class Fading_text {
public:
Fading_text(const std::string& text, float duration = 0.5f) : _text(text), _duration(duration) {}
auto update(mirrage::util::Time dt)
{
_elapsed_time += dt.value();
if(_elapsed_time >= _duration) {
_draw = !_draw;
_elapsed_time = 0.f;
}
}
auto draw(const glm::vec2& position)
{
if(_draw) {
text(_text, position);
}
}
auto draw_centered(const glm::vec2& window_size,
const glm::vec2& position = glm::vec2{0.f},
center center = center::all)
{
if(_draw) {
text_centered(_text, window_size, position, center);
}
}
private:
std::string _text;
float _duration;
float _elapsed_time = 0.f;
bool _draw = true;
};
} // namespace phase_shifter::ui