diff --git a/CMakeLists.txt b/CMakeLists.txt index 00b9acf..7f0f857 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,4 +123,3 @@ elseif(PACKAGE_PLATFORM MATCHES "win") #endif() #install(FILES ${OPENAL_DLL} DESTINATION .) endif() - diff --git a/src/animation/screen-transition.cpp b/src/animation/screen-transition.cpp index c1984cf..186af0c 100644 --- a/src/animation/screen-transition.cpp +++ b/src/animation/screen-transition.cpp @@ -65,7 +65,12 @@ screen_transition::screen_transition() ); } -void screen_transition::transition(float duration, bool reverse, ::animation::interpolator_type interpolator) +void screen_transition::set_visible(bool visible) +{ + billboard.set_active(visible); +} + +void screen_transition::transition(float duration, bool reverse, ::animation::interpolator_type interpolator, bool hide) { float initial_state = (reverse) ? 1.0f : 0.0f; float final_state = (reverse) ? 0.0f : 1.0f; @@ -78,6 +83,19 @@ void screen_transition::transition(float duration, bool reverse, ::animationset_value(initial_state); material.update_tweens(); diff --git a/src/animation/screen-transition.hpp b/src/animation/screen-transition.hpp index 52d1d42..b31c528 100644 --- a/src/animation/screen-transition.hpp +++ b/src/animation/screen-transition.hpp @@ -33,7 +33,9 @@ class screen_transition public: screen_transition(); - void transition(float duration, bool reverse, animation::interpolator_type interpolator); + void set_visible(bool visible); + + void transition(float duration, bool reverse, animation::interpolator_type interpolator, bool hide = true); scene::billboard* get_billboard(); render::material* get_material(); diff --git a/src/debug/logger.cpp b/src/debug/logger.cpp index 0c9922e..1aca8b3 100644 --- a/src/debug/logger.cpp +++ b/src/debug/logger.cpp @@ -154,7 +154,7 @@ void logger::set_success_postfix(const std::string& postfix) void logger::push_task(const std::string& description) { - std::string message = description + "..."; + std::string message = description + " {"; if (!auto_newline) message += "\n"; @@ -163,20 +163,20 @@ void logger::push_task(const std::string& description) tasks.push(description); } -void logger::pop_task(int status) +void logger::pop_task(int status, std::string error) { if (tasks.empty()) { return; } - std::string message = tasks.top() + "... "; + std::string message = "} "; tasks.pop(); if (status == EXIT_SUCCESS) { - message += "success"; + message += "=> success"; if (!auto_newline) message += "\n"; @@ -184,11 +184,13 @@ void logger::pop_task(int status) } else { - message += "failed (" + std::to_string(status) + ")"; + message += "failed"; + if (!error.empty()) + message += " (" + error + ")"; if (!auto_newline) message += "\n"; - error(message); + this->error(message); } } diff --git a/src/debug/logger.hpp b/src/debug/logger.hpp index 13905cf..b387de2 100644 --- a/src/debug/logger.hpp +++ b/src/debug/logger.hpp @@ -97,7 +97,7 @@ public: * * @param status Exit status of the task. A value of `0` or `EXIT_SUCCESS` indicates the task exited successfully. A non-zero exit status indicates the task failed. */ - void pop_task(int status); + void pop_task(int status, std::string error = std::string()); const std::string& get_history() const; diff --git a/src/game/context.hpp b/src/game/context.hpp index c79658d..7e6f4e2 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -201,6 +201,11 @@ struct context scene::billboard* camera_flash_billboard; scene::text* title_text; scene::text* title_version_text; + scene::text* main_menu_start_text; + scene::text* main_menu_options_text; + scene::text* main_menu_credits_text; + scene::text* main_menu_quit_text; + scene::model_instance* ui_pointer; // Surface scene scene::collection* surface_scene; diff --git a/src/game/states/boot.cpp b/src/game/states/boot.cpp index 747954c..9f80b0e 100644 --- a/src/game/states/boot.cpp +++ b/src/game/states/boot.cpp @@ -119,8 +119,6 @@ void enter(application* app, int argc, char** argv) // Get application logger debug::logger* logger = app->get_logger(); - logger->push_task("Running application bootloader"); - // Allocate game context game::context* ctx = new game::context(); ctx->app = app; @@ -150,31 +148,22 @@ void enter(application* app, int argc, char** argv) return; } - logger->pop_task(EXIT_SUCCESS); - // Set update rate if (ctx->config->contains("update_rate")) { app->set_update_rate((*ctx->config)["update_rate"].get()); } - // Setup initial application state - application::state initial_state; - initial_state.name = "loading"; - initial_state.enter = std::bind(game::state::loading::enter, ctx); - initial_state.exit = std::bind(game::state::loading::exit, ctx); - - // Enter initial application state - app->change_state(initial_state); - - return; + // Queue next application state + application::state next_state; + next_state.name = "loading"; + next_state.enter = std::bind(game::state::loading::enter, ctx); + next_state.exit = std::bind(game::state::loading::exit, ctx); + app->queue_state(next_state); } void exit(application* app) -{ - -} - +{} void parse_options(game::context* ctx, int argc, char** argv) { @@ -289,10 +278,10 @@ void setup_resources(game::context* ctx) // Redirect logger output to log file on non-debug builds #if defined(NDEBUG) - std::string log_filename = config_path + "log.txt"; + std::string log_filename = ctx->config_path + "log.txt"; ctx->log_filestream.open(log_filename.c_str()); ctx->log_filestream << logger->get_history(); - logger->redirect(&log_filestream); + logger->redirect(&ctx->log_filestream); #endif // Scan for mods diff --git a/src/game/states/loading.cpp b/src/game/states/loading.cpp index d90ae86..45d05bd 100644 --- a/src/game/states/loading.cpp +++ b/src/game/states/loading.cpp @@ -33,7 +33,7 @@ #include "entity/archetype.hpp" #include "game/states/nuptial-flight.hpp" #include "game/states/splash.hpp" -#include "game/states/forage.hpp" +#include "game/states/main-menu.hpp" #include "game/controls.hpp" #include "geom/spherical.hpp" #include "gl/drawing-mode.hpp" @@ -132,9 +132,9 @@ void enter(game::context* ctx) application::state next_state; if (ctx->option_quick_start.has_value()) { - next_state.name = "forage"; - next_state.enter = std::bind(game::state::forage::enter, ctx); - next_state.exit = std::bind(game::state::forage::exit, ctx); + next_state.name = "main_menu"; + next_state.enter = std::bind(game::state::main_menu::enter, ctx); + next_state.exit = std::bind(game::state::main_menu::exit, ctx); } else { diff --git a/src/game/states/main-menu.cpp b/src/game/states/main-menu.cpp index 2a5e267..aadc60b 100644 --- a/src/game/states/main-menu.cpp +++ b/src/game/states/main-menu.cpp @@ -18,6 +18,16 @@ */ #include "game/states/main-menu.hpp" +#include "game/states/forage.hpp" +#include "game/states/nuptial-flight.hpp" +#include "render/passes/clear-pass.hpp" +#include "resources/resource-manager.hpp" +#include "render/model.hpp" +#include "animation/screen-transition.hpp" +#include "animation/ease.hpp" +#include "animation/timeline.hpp" +#include "application.hpp" +#include namespace game { namespace state { @@ -25,12 +35,166 @@ namespace main_menu { void enter(game::context* ctx) { - + ctx->ui_clear_pass->set_cleared_buffers(true, true, false); + + // Construct main menu texts + ctx->main_menu_start_text = new scene::text(); + ctx->main_menu_options_text = new scene::text(); + ctx->main_menu_credits_text = new scene::text(); + ctx->main_menu_quit_text = new scene::text(); + + // Set content of texts + ctx->main_menu_start_text->set_content((*ctx->strings)["main_menu_start"]); + ctx->main_menu_options_text->set_content((*ctx->strings)["main_menu_options"]); + ctx->main_menu_credits_text->set_content((*ctx->strings)["main_menu_credits"]); + ctx->main_menu_quit_text->set_content((*ctx->strings)["main_menu_quit"]); + + std::vector texts; + texts.push_back(ctx->main_menu_start_text); + texts.push_back(ctx->main_menu_options_text); + texts.push_back(ctx->main_menu_credits_text); + texts.push_back(ctx->main_menu_quit_text); + + float offset_y = 0.0f; + + for (scene::text* text: texts) + { + text->set_material(&ctx->menu_font_material); + text->set_font(&ctx->menu_font); + text->set_color({1.0f, 1.0f, 1.0f, 0.5f}); + ctx->ui_scene->add_object(text); + + // Align text + const auto& text_aabb = static_cast&>(text->get_local_bounds()); + float title_w = text_aabb.max_point.x - text_aabb.min_point.x; + float title_h = text_aabb.max_point.y - text_aabb.min_point.y; + text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f) + offset_y, 0.0f}); + + offset_y -= ctx->menu_font.get_font_metrics().linespace * 1.5f; + + // Update menu AABB + } + + // Load pointer + ctx->ui_pointer = new scene::model_instance(); + ctx->ui_pointer->set_model(ctx->resource_manager->load("pointer.mdl")); + ctx->ui_scene->add_object(ctx->ui_pointer); + + // Scale and position pointer + float pointer_scale = (ctx->menu_font.get_font_metrics().ascent - ctx->menu_font.get_font_metrics().descent) * (1.0f/3.0f); + ctx->ui_pointer->set_scale({pointer_scale, pointer_scale, pointer_scale}); + + float advance_x = ctx->menu_font.get_glyph_metrics(U' ').horizontal_advance * 2.0f; + + const auto& text_aabb = static_cast&>(ctx->main_menu_start_text->get_local_bounds()); + const auto& text_translation = ctx->main_menu_start_text->get_translation(); + + ctx->ui_pointer->set_translation(text_translation + float3{-advance_x, (text_aabb.max_point.y - text_aabb.min_point.y) * 0.5f, 0.0f}); + ctx->main_menu_start_text->set_color({1.0f, 1.0f, 1.0f, 1.0f}); + + ctx->controls["menu_down"]->set_activated_callback + ( + [ctx]() + { + ctx->ui_pointer->set_translation(ctx->ui_pointer->get_translation() - float3{0.0f, ctx->menu_font.get_font_metrics().linespace * 1.5f, 0.0f}); + } + ); + ctx->controls["menu_up"]->set_activated_callback + ( + [ctx]() + { + ctx->ui_pointer->set_translation(ctx->ui_pointer->get_translation() + float3{0.0f, ctx->menu_font.get_font_metrics().linespace * 1.5f, 0.0f}); + } + ); + ctx->controls["menu_select"]->set_activated_callback + ( + [ctx]() + { + // Disable menu controls + ctx->controls["menu_down"]->set_activated_callback(nullptr); + ctx->controls["menu_up"]->set_activated_callback(nullptr); + ctx->controls["menu_select"]->set_activated_callback(nullptr); + + // Create change state functions + auto change_state_nuptial_flight = [ctx]() + { + application::state next_state; + next_state.name = "nuptial_flight"; + next_state.enter = std::bind(game::state::nuptial_flight::enter, ctx); + next_state.exit = std::bind(game::state::nuptial_flight::exit, ctx); + ctx->app->change_state(next_state); + }; + + // Set up timing + const float fade_out_duration = 1.0f; + + // Schedule state change + timeline* timeline = ctx->timeline; + float t = timeline->get_position(); + timeline::sequence sequence = + { + {t + fade_out_duration, change_state_nuptial_flight} + }; + timeline->add_sequence(sequence); + + // Start fade out to white + ctx->fade_transition_color->set_value({1, 1, 1}); + ctx->fade_transition->transition(fade_out_duration, false, ease::out_quad, false); + } + ); + + // Disable control callbacks + ctx->controls["menu_down"]->set_callbacks_enabled(false); + ctx->controls["menu_up"]->set_callbacks_enabled(false); + ctx->controls["menu_select"]->set_callbacks_enabled(false); + + // Start fade in from black + const float fade_in_duration = 0.5f; + ctx->fade_transition_color->set_value({0, 0, 0}); + ctx->fade_transition->transition(fade_in_duration, true, ease::in_quad); + + // Schedule enabling of control callbacks + auto enable_control_callbacks = [ctx]() + { + ctx->controls["menu_down"]->set_callbacks_enabled(true); + ctx->controls["menu_up"]->set_callbacks_enabled(true); + ctx->controls["menu_select"]->set_callbacks_enabled(true); + }; + timeline* timeline = ctx->timeline; + float t = timeline->get_position(); + timeline::sequence sequence = + { + {t + fade_in_duration, enable_control_callbacks} + }; + timeline->add_sequence(sequence); } void exit(game::context* ctx) { - + // Remove control callbacks + ctx->controls["menu_down"]->set_activated_callback(nullptr); + ctx->controls["menu_up"]->set_activated_callback(nullptr); + ctx->controls["menu_select"]->set_activated_callback(nullptr); + + // Remove text objects from UI + std::vector texts; + texts.push_back(ctx->main_menu_start_text); + texts.push_back(ctx->main_menu_options_text); + texts.push_back(ctx->main_menu_credits_text); + texts.push_back(ctx->main_menu_quit_text); + for (scene::text* text: texts) + { + ctx->ui_scene->remove_object(text); + delete text; + text = nullptr; + } + + // Remove pointer from UI + ctx->ui_scene->remove_object(ctx->ui_pointer); + delete ctx->ui_pointer; + ctx->ui_pointer = nullptr; + + ctx->ui_clear_pass->set_cleared_buffers(false, true, false); } } // namespace main_menu diff --git a/src/game/states/nuptial-flight.cpp b/src/game/states/nuptial-flight.cpp index dbc56a1..f16e09a 100644 --- a/src/game/states/nuptial-flight.cpp +++ b/src/game/states/nuptial-flight.cpp @@ -66,22 +66,6 @@ void enter(game::context* ctx) ctx->astronomy_system->set_observer_location(double3{observer.elevation, observer.latitude, observer.longitude}); } - // Create wing - entity::archetype* ant_forewing_archetype = ctx->resource_manager->load("ant-forewing.ent"); - auto forewing_eid = ctx->entity_registry->create(); - //ant_forewing_archetype->assign(*ctx->entity_registry, forewing_eid); - - // Create eye - entity::archetype* ant_round_eye_archetype = ctx->resource_manager->load("ant-round-eye.ent"); - auto ant_round_eye_eid = ctx->entity_registry->create(); - ant_round_eye_archetype->assign(*ctx->entity_registry, ant_round_eye_eid); - entity::command::assign_render_layers(*ctx->entity_registry, ant_round_eye_eid, 0b10); - - // Create green orb ring - entity::archetype* orb_ring_archetype = ctx->resource_manager->load("orb-ring.ent"); - auto orb_ring_eid = ctx->entity_registry->create(); - //orb_ring_archetype->assign(*ctx->entity_registry, orb_ring_eid); - // Setup camera ctx->surface_camera->look_at({0, 0, 1}, {0, 0, 0}, {0, 1, 0}); ctx->surface_camera->set_exposure(-14.5f); @@ -94,7 +78,7 @@ void enter(game::context* ctx) // Start fade in from white ctx->fade_transition_color->set_value({1, 1, 1}); - ctx->fade_transition->transition(2.0f, true, ease::in_quad); + ctx->fade_transition->transition(5.0f, true, math::lerp); } void exit(game::context* ctx) diff --git a/src/game/states/splash.cpp b/src/game/states/splash.cpp index 9157bf3..0aa8151 100644 --- a/src/game/states/splash.cpp +++ b/src/game/states/splash.cpp @@ -23,6 +23,7 @@ #include "animation/ease.hpp" #include "animation/timeline.hpp" #include "application.hpp" +#include "render/passes/clear-pass.hpp" namespace game { namespace state { @@ -30,6 +31,8 @@ namespace splash { void enter(game::context* ctx) { + ctx->ui_clear_pass->set_cleared_buffers(true, true, false); + // Add splash billboard to UI scene ctx->ui_scene->add_object(ctx->splash_billboard); @@ -102,6 +105,8 @@ void exit(game::context* ctx) // Remove splash billboard from UI scene ctx->ui_scene->remove_object(ctx->splash_billboard); + + ctx->ui_clear_pass->set_cleared_buffers(false, true, false); } } // namespace splash diff --git a/src/game/states/title.cpp b/src/game/states/title.cpp index d594159..9991e72 100644 --- a/src/game/states/title.cpp +++ b/src/game/states/title.cpp @@ -25,6 +25,7 @@ #include "application.hpp" #include "scene/text.hpp" #include "configuration.hpp" +#include "render/passes/clear-pass.hpp" namespace game { namespace state { @@ -32,6 +33,8 @@ namespace title { void enter(game::context* ctx) { + ctx->ui_clear_pass->set_cleared_buffers(true, true, false); + // Setup timing const float title_fade_in_duration = 0.5f; const float title_fade_out_duration = 0.5f; @@ -66,6 +69,7 @@ void enter(game::context* ctx) { ctx->timeline->clear(); ctx->fade_transition->get_animation()->stop(); + ctx->fade_transition->get_billboard()->set_active(false); ctx->rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); ctx->rasterizer->clear_framebuffer(true, false, false); ctx->app->swap_buffers(); @@ -125,6 +129,8 @@ void exit(game::context* ctx) // Disable title skipper ctx->input_listener->set_enabled(false); ctx->input_listener->set_callback(nullptr); + + ctx->ui_clear_pass->set_cleared_buffers(false, true, false); } } // namespace title diff --git a/src/type/font-metrics.hpp b/src/type/font-metrics.hpp index 781f622..3ee8466 100644 --- a/src/type/font-metrics.hpp +++ b/src/type/font-metrics.hpp @@ -36,7 +36,7 @@ struct font_metrics /// Distance that must be placed between two lines of text. float linegap; - /// Baseline-to-baseline distance, computed as ascent - descent + linegap`. + /// Baseline-to-baseline distance, computed as `ascent - descent + linegap`. float linespace; /// Vertical position of an underline. diff --git a/src/type/freetype/typeface.cpp b/src/type/freetype/typeface.cpp index 8036693..4a48252 100644 --- a/src/type/freetype/typeface.cpp +++ b/src/type/freetype/typeface.cpp @@ -56,8 +56,8 @@ bool typeface::get_metrics(float height, font_metrics& metrics) const // Get font metrics metrics.ascent = face->size->metrics.ascender / 64.0f; metrics.descent = face->size->metrics.descender / 64.0f; - metrics.linegap = face->size->metrics.height / 64.0f; - metrics.linespace = metrics.ascent - metrics.descent + metrics.linegap; + metrics.linespace = face->size->metrics.height / 64.0f; + metrics.linegap = metrics.linespace - (metrics.ascent - metrics.descent); metrics.underline_position = FT_MulFix(face->underline_position, face->size->metrics.y_scale) / 64.0f; metrics.underline_thickness = FT_MulFix(face->underline_thickness, face->size->metrics.y_scale) / 64.0f; metrics.max_horizontal_advance = face->size->metrics.max_advance / 64.0f;