Launcher background (#58)

* Begin implementing launcher background

* Add keyframe system for launcher.

* Mirror Banjo.

* More work on animations.

* Update RecompFrontend.

* Update RT64.

* Fix linux build

* adjust launcher anim and launcher

---------

Co-authored-by: Dario <dariosamo@gmail.com>
Co-authored-by: Reonu <reonuyt@gmail.com>
Co-authored-by: thecozies <79979276+thecozies@users.noreply.github.com>
This commit is contained in:
Wiseguy
2026-01-12 17:15:56 -05:00
committed by GitHub
parent 70f7fd2607
commit 9e6966c961
8 changed files with 391 additions and 13 deletions
+1
View File
@@ -140,6 +140,7 @@ add_custom_command(OUTPUT
add_executable(BanjoRecompiled)
set (SOURCES
${CMAKE_SOURCE_DIR}/src/main/launcher_animation.cpp
${CMAKE_SOURCE_DIR}/src/main/main.cpp
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 1054 1044" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-348,-18)">
<g transform="matrix(0.965901,0,0,0.965901,29.8367,18.4135)">
<rect x="330" y="0" width="1090" height="1080" style="fill:rgb(255,201,69);"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 682 B

+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 2180 2160" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.837799,0,0,0.823158,362.599,628.361)">
<path d="M2169.26,-763.354L2169.26,1860.69L-432.8,1860.69L-432.8,-763.354L2169.26,-763.354ZM576.269,847.984L660.512,1167.98L950.699,1088.84C912.269,1067.12 882.2,1030.18 869.936,983.6C845.93,892.416 899.177,798.549 988.767,774.117C1078.36,749.684 1170.58,803.878 1194.59,895.062C1206.85,941.647 1198.96,988.933 1176.35,1027.3L1466.53,948.162L1382.27,628.092C1360.47,662.883 1326.33,689.805 1284.12,701.316C1194.53,725.75 1102.3,671.558 1078.3,580.372C1054.29,489.188 1107.54,395.321 1197.13,370.888C1239.34,359.377 1282.13,365.317 1318.1,384.358L1250.56,127.805L960.463,206.919C983.019,168.569 990.892,121.33 978.639,74.789C954.634,-16.395 862.407,-70.589 772.817,-46.157C683.227,-21.724 629.98,72.143 653.985,163.327C666.238,209.868 696.263,246.771 734.637,268.505L444.539,347.619L512.06,604.091C476.111,585.098 433.361,579.18 391.193,590.679C301.6,615.113 248.356,708.979 272.362,800.163C296.368,891.349 388.591,945.541 478.184,921.108C520.352,909.608 554.468,882.73 576.269,847.984Z" style="fill:#1A5698"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 1275 1346" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-505,-288)">
<g transform="matrix(1,0,0,1,182,432)">
<g transform="matrix(1.60405,0.926099,-0.849835,1.47196,-1141.53,-1729.71)">
<path d="M1708.78,2.179L1708.78,916.607L1476.3,1062.88L1476.3,148.447L1708.78,2.179Z" style="fill:white;"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 800 B

+16
View File
@@ -0,0 +1,16 @@
#ifndef __BANJO_LAUNCHER_H__
#define __BANJO_LAUNCHER_H__
#include "recompui/recompui.h"
namespace banjo {
void launcher_animation_setup(recompui::LauncherMenu *menu, recompui::Element *title);
void launcher_animation_update(recompui::LauncherMenu *menu);
constexpr float launcher_options_right_position_start = 96.0f;
constexpr float launcher_options_right_position_end = 96.0f + 24.0f;
constexpr float launcher_options_top_offset = 96.0f;
constexpr float launcher_options_title_offset = 120.0f;
}
#endif
+308
View File
@@ -0,0 +1,308 @@
#include "banjo_launcher.h"
struct KeyframeRot {
float seconds;
float deg;
};
struct Keyframe2D {
float seconds;
float x;
float y;
};
enum class InterpolationMethod {
Linear,
Smootherstep
};
struct AnimationData {
uint32_t keyframe_index = 0;
uint32_t loop_keyframe_index = UINT32_MAX;
float seconds = 0.0f;
InterpolationMethod interpolation_method = InterpolationMethod::Linear;
};
struct AnimatedSvg {
recompui::Element *svg = nullptr;
std::vector<Keyframe2D> position_keyframes;
std::vector<Keyframe2D> scale_keyframes;
std::vector<KeyframeRot> rotation_keyframes;
AnimationData position_animation;
AnimationData scale_animation;
AnimationData rotation_animation;
float width = 0;
float height = 0;
};
struct LauncherContext {
AnimatedSvg banjo_svg;
AnimatedSvg kazooie_svg;
AnimatedSvg jiggy_color_svg;
AnimatedSvg jiggy_shine_svg;
AnimatedSvg jiggy_hole_svg;
recompui::Element *wrapper;
recompui::Element *title;
std::chrono::steady_clock::time_point last_update_time;
std::chrono::steady_clock::time_point start_time;
bool started = false;
} launcher_context;
float interpolate_value(float a, float b, float t, InterpolationMethod method) {
switch (method) {
case InterpolationMethod::Smootherstep:
return a + (b - a) * (t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f));
case InterpolationMethod::Linear:
default:
return a + (b - a) * t;
}
}
void calculate_rot_from_keyframes(const std::vector<KeyframeRot> &kf, AnimationData &an, float delta_time, float &deg) {
if (kf.empty()) {
return;
}
an.seconds += delta_time;
while ((an.keyframe_index < (kf.size() - 1) && (an.seconds >= kf[an.keyframe_index + 1].seconds))) {
an.keyframe_index++;
}
if (an.keyframe_index >= (kf.size() - 1)) {
deg = kf[an.keyframe_index].deg;
}
else {
float t = (an.seconds - kf[an.keyframe_index].seconds) / (kf[an.keyframe_index + 1].seconds - kf[an.keyframe_index].seconds);
deg = interpolate_value(kf[an.keyframe_index].deg, kf[an.keyframe_index + 1].deg, t, an.interpolation_method);
}
}
void calculate_2d_from_keyframes(const std::vector<Keyframe2D> &kf, AnimationData &an, float delta_time, float &x, float &y) {
if (kf.empty()) {
return;
}
an.seconds += delta_time;
while ((an.keyframe_index < (kf.size() - 1) && (an.seconds >= kf[an.keyframe_index + 1].seconds))) {
an.keyframe_index++;
}
if ((an.loop_keyframe_index != UINT32_MAX) && (an.keyframe_index >= (kf.size() - 1))) {
an.seconds = kf[an.loop_keyframe_index].seconds + (an.seconds - kf[an.keyframe_index].seconds);
an.keyframe_index = an.loop_keyframe_index;
}
if (an.keyframe_index >= (kf.size() - 1)) {
x = kf[an.keyframe_index].x;
y = kf[an.keyframe_index].y;
}
else {
float t = (an.seconds - kf[an.keyframe_index].seconds) / (kf[an.keyframe_index + 1].seconds - kf[an.keyframe_index].seconds);
x = interpolate_value(kf[an.keyframe_index].x, kf[an.keyframe_index + 1].x, t, an.interpolation_method);
y = interpolate_value(kf[an.keyframe_index].y, kf[an.keyframe_index + 1].y, t, an.interpolation_method);
}
}
AnimatedSvg create_animated_svg(recompui::ContextId context, recompui::Element *parent, const std::string &svg_path, float width, float height) {
AnimatedSvg animated_svg;
animated_svg.width = width;
animated_svg.height = height;
animated_svg.svg = context.create_element<recompui::Svg>(parent, svg_path);
animated_svg.svg->set_position(recompui::Position::Absolute);
animated_svg.svg->set_width(width, recompui::Unit::Dp);
animated_svg.svg->set_height(height, recompui::Unit::Dp);
return animated_svg;
}
void update_animated_svg(AnimatedSvg &animated_svg, float delta_time, float bg_width, float bg_height) {
float position_x = 0.0f, position_y = 0.0f;
float scale_x = 1.0f, scale_y = 1.0f;
float rotation_degrees = 0.0f;
calculate_2d_from_keyframes(animated_svg.position_keyframes, animated_svg.position_animation, delta_time, position_x, position_y);
calculate_2d_from_keyframes(animated_svg.scale_keyframes, animated_svg.scale_animation, delta_time, scale_x, scale_y);
calculate_rot_from_keyframes(animated_svg.rotation_keyframes, animated_svg.rotation_animation, delta_time, rotation_degrees);
animated_svg.svg->set_translate_2D(position_x + bg_width / 2.0f - animated_svg.width / 2.0f, position_y + bg_height / 2.0f - animated_svg.height / 2.0f);
animated_svg.svg->set_scale_2D(scale_x, scale_y);
animated_svg.svg->set_rotation(rotation_degrees);
}
const float jiggy_scale_anim_start = 0.4f;
const float jiggy_scale_anim_length = 0.75f;
const float jiggy_scale_anim_end = jiggy_scale_anim_start + jiggy_scale_anim_length;
const float jiggy_move_over_start = jiggy_scale_anim_end + 0.5f;
const float jiggy_move_over_length = 0.75f;
const float jiggy_move_over_end = jiggy_move_over_start + jiggy_move_over_length;
const float jiggy_shine_start = jiggy_move_over_end - 0.5f;
const float jiggy_shine_length = 0.8f;
void banjo::launcher_animation_setup(recompui::LauncherMenu *menu, recompui::Element *title) {
auto context = recompui::get_current_context();
recompui::Element *background_container = menu->get_background_container();
background_container->set_background_color({ 0x1A, 0x56, 0x98, 0xFF });
launcher_context.wrapper = context.create_element<recompui::Element>(background_container, 0);
launcher_context.wrapper->set_position(recompui::Position::Absolute);
launcher_context.wrapper->set_width(100, recompui::Unit::Percent);
launcher_context.wrapper->set_height(100, recompui::Unit::Percent);
launcher_context.wrapper->set_top(0);
// The creation order of these is important.
launcher_context.jiggy_color_svg = create_animated_svg(context, launcher_context.wrapper, "JiggyColor.svg", 1054.0f, 1044.0f);
launcher_context.jiggy_shine_svg = create_animated_svg(context, launcher_context.wrapper, "JiggyShine.svg", 219.0f, 1080.0f);
launcher_context.jiggy_hole_svg = create_animated_svg(context, launcher_context.wrapper, "JiggyHole.svg", 2180.0f, 2160.0f);
launcher_context.banjo_svg = create_animated_svg(context, launcher_context.wrapper, "Banjo.svg", 649.0f, 622.0f);
launcher_context.kazooie_svg = create_animated_svg(context, launcher_context.wrapper, "Kazooie.svg", 626.0f, 774.0f);
// Animate the jiggy hole.
launcher_context.jiggy_hole_svg.position_keyframes = {
{ 0.0f, 0.0f, 0.0f },
{ 3.0f, 0.0f, -5.0f },
{ 6.0f, 0.0f, 5.0f },
{ 9.0f, 0.0f, -5.0f },
};
launcher_context.jiggy_hole_svg.scale_keyframes = {
{ 0.0f, 0.0f, 0.0f },
{ jiggy_scale_anim_start + 0.0f, 0.0f, 0.0f },
{ jiggy_scale_anim_start + jiggy_scale_anim_length, 1.0f, 1.0f },
};
launcher_context.jiggy_hole_svg.rotation_keyframes = {
{ 0.0f, -45.0f },
{ jiggy_scale_anim_start + 0.0f, -45.0f },
{ jiggy_scale_anim_start + jiggy_scale_anim_length, 0.0f },
};
launcher_context.jiggy_hole_svg.position_animation.loop_keyframe_index = 1;
launcher_context.jiggy_hole_svg.position_animation.interpolation_method = InterpolationMethod::Smootherstep;
launcher_context.jiggy_hole_svg.scale_animation.interpolation_method = InterpolationMethod::Smootherstep;
launcher_context.jiggy_hole_svg.rotation_animation.interpolation_method = InterpolationMethod::Smootherstep;
// Copy keyframes from the hole to the color.
launcher_context.jiggy_color_svg.position_keyframes = launcher_context.jiggy_hole_svg.position_keyframes;
launcher_context.jiggy_color_svg.position_animation = launcher_context.jiggy_hole_svg.position_animation;
launcher_context.jiggy_color_svg.scale_keyframes = launcher_context.jiggy_hole_svg.scale_keyframes;
launcher_context.jiggy_color_svg.scale_animation = launcher_context.jiggy_hole_svg.scale_animation;
launcher_context.jiggy_color_svg.rotation_keyframes = launcher_context.jiggy_hole_svg.rotation_keyframes;
launcher_context.jiggy_color_svg.rotation_animation = launcher_context.jiggy_hole_svg.rotation_animation;
// Animate the jiggy shine.
launcher_context.jiggy_shine_svg.position_keyframes = {
{ 0.0f, 700.0f, 0.0f },
{ jiggy_shine_start, 700.0f, 0.0f },
{ jiggy_shine_start + jiggy_shine_length, -700.0f, 0.0f },
};
launcher_context.jiggy_shine_svg.scale_keyframes = {
{ 0.0f, 0.0f, 0.0f },
{ jiggy_shine_start, 0.0f, 0.0f },
{ jiggy_shine_start + 0.01f, 1.0f, 1.0f },
};
launcher_context.jiggy_shine_svg.position_animation.interpolation_method = InterpolationMethod::Smootherstep;
// Animate Banjo.
launcher_context.banjo_svg.position_keyframes = {
{ 0.0f, -1200.0f, 0.0f },
{ 1.0f, -220.0f, 0.0f },
{ 2.0f, -220.0f, -5.0f },
{ 5.0f, -220.0f, 5.0f },
{ 8.0f, -220.0f, -5.0f },
};
launcher_context.banjo_svg.scale_keyframes = {
{ 0.0f, 0.0f, 0.0f },
{ 0.1f, 1.0f, 1.0f },
};
launcher_context.banjo_svg.rotation_keyframes = {
{ 0.0f, -20.0f },
{ 1.0f, 0.0f },
};
launcher_context.banjo_svg.position_animation.loop_keyframe_index = 2;
launcher_context.banjo_svg.position_animation.interpolation_method = InterpolationMethod::Smootherstep;
launcher_context.banjo_svg.rotation_animation.interpolation_method = InterpolationMethod::Smootherstep;
// Animate Kazooie. Mirror all of Banjo's keyframes except for the scale.
launcher_context.kazooie_svg.position_keyframes = launcher_context.banjo_svg.position_keyframes;
launcher_context.kazooie_svg.position_animation = launcher_context.banjo_svg.position_animation;
launcher_context.kazooie_svg.scale_keyframes = launcher_context.banjo_svg.scale_keyframes;
launcher_context.kazooie_svg.scale_animation = launcher_context.banjo_svg.scale_animation;
launcher_context.kazooie_svg.rotation_keyframes = launcher_context.banjo_svg.rotation_keyframes;
launcher_context.kazooie_svg.rotation_animation = launcher_context.banjo_svg.rotation_animation;
for (auto &kf : launcher_context.kazooie_svg.position_keyframes) {
kf.x = -kf.x;
}
launcher_context.kazooie_svg.position_keyframes[2].seconds += 1.5f;
launcher_context.kazooie_svg.position_keyframes[3].seconds += 1.5f;
launcher_context.kazooie_svg.position_keyframes[4].seconds += 1.5f;
for (auto &kf : launcher_context.kazooie_svg.rotation_keyframes) {
kf.deg = -kf.deg;
}
launcher_context.start_time = std::chrono::steady_clock::now();
launcher_context.title = title;
}
void banjo::launcher_animation_update(recompui::LauncherMenu *menu) {
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
float delta_time = launcher_context.started ? std::chrono::duration_cast<std::chrono::milliseconds>(now - launcher_context.last_update_time).count() / 1000.0f : 0.0f;
launcher_context.last_update_time = now;
launcher_context.started = true;
recompui::Element *background_container = menu->get_background_container();
float dp_to_pixel_ratio = background_container->get_dp_to_pixel_ratio();
float bg_width = background_container->get_client_width() / dp_to_pixel_ratio;
float bg_height = background_container->get_client_height() / dp_to_pixel_ratio;
update_animated_svg(launcher_context.banjo_svg, delta_time, bg_width, bg_height);
update_animated_svg(launcher_context.kazooie_svg, delta_time, bg_width, bg_height);
update_animated_svg(launcher_context.jiggy_color_svg, delta_time, bg_width, bg_height);
update_animated_svg(launcher_context.jiggy_shine_svg, delta_time, bg_width, bg_height);
update_animated_svg(launcher_context.jiggy_hole_svg, delta_time, bg_width, bg_height);
auto elapsed = now - launcher_context.start_time;
float elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
float from_ms = jiggy_move_over_start * 1000.0f;
float to_ms = jiggy_move_over_end * 1000.0f;
if (elapsed_ms > from_ms && elapsed_ms < to_ms) {
float now_ms = elapsed_ms;
float t = (now_ms - from_ms) / (to_ms - from_ms);
float x_translation = interpolate_value(0, 1440 * -0.2f, t, InterpolationMethod::Smootherstep);
launcher_context.wrapper->set_translate_2D(x_translation, 0, recompui::Unit::Dp);
float y_translation = interpolate_value(0, launcher_options_top_offset, t, InterpolationMethod::Smootherstep);
launcher_context.wrapper->set_top(y_translation);
float scale = interpolate_value(1, 0.666f, t, InterpolationMethod::Smootherstep);
launcher_context.wrapper->set_scale_2D(scale, scale);
float game_option_menu_opacity = interpolate_value(0, 1.0f, t, InterpolationMethod::Smootherstep);
for (auto option : menu->get_game_options_menu()->get_options()) {
option->set_opacity(game_option_menu_opacity);
}
launcher_context.title->set_opacity(game_option_menu_opacity);
float game_option_menu_right = interpolate_value(
launcher_options_right_position_start, launcher_options_right_position_end, t, InterpolationMethod::Smootherstep);
menu->get_game_options_menu()->set_right(game_option_menu_right);
}
if (elapsed_ms <= from_ms) {
for (auto option : menu->get_game_options_menu()->get_options()) {
option->set_opacity(0);
}
launcher_context.title->set_opacity(0.0f);
menu->get_game_options_menu()->set_right(launcher_options_right_position_start);
} else if (elapsed_ms >= to_ms) {
for (auto option : menu->get_game_options_menu()->get_options()) {
option->set_opacity(1.0f);
}
launcher_context.title->set_opacity(1.0f);
menu->get_game_options_menu()->set_right(launcher_options_right_position_end);
}
}
+38 -12
View File
@@ -40,6 +40,7 @@
#include "banjo_sound.h"
#include "banjo_support.h"
#include "banjo_game.h"
#include "banjo_launcher.h"
#include "recomp_data.h"
#include "ovl_patches.hpp"
#include "theme.h"
@@ -82,7 +83,7 @@ ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() {
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) > 0) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) > 0) {
exit_error("Failed to initialize SDL2: %s\n", SDL_GetError());
}
@@ -151,7 +152,7 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::
flags |= SDL_WINDOW_VULKAN;
#endif
window = SDL_CreateWindow("Banjo: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, flags);
window = SDL_CreateWindow("Banjo: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 900, flags);
if (window == nullptr) {
exit_error("Failed to create window: %s\n", SDL_GetError());
@@ -546,19 +547,43 @@ void on_launcher_init(recompui::LauncherMenu *menu) {
supported_games[0].mod_game_id,
recompui::GameOptionsMenuLayout::Center
);
game_options_menu->add_default_options();
constexpr float banjo_apsect_ratio = 1.0434f;
constexpr float banjo_height = 500.0f;
for (auto option : game_options_menu->get_options()) {
option->set_justify_content(recompui::JustifyContent::FlexEnd);
option->set_border_radius(0);
// TODO: Style launcher and get better background.
auto bg_element = menu->set_launcher_background_svg("Banjo.svg");
bg_element->set_top(50.0f, recompui::Unit::Percent);
bg_element->set_left(50.0f, recompui::Unit::Percent);
bg_element->set_height(banjo_height, recompui::Unit::Dp);
bg_element->set_width(banjo_height * banjo_apsect_ratio, recompui::Unit::Dp);
bg_element->set_translate_2D(-50.0f, -50.0f, recompui::Unit::Percent);
bg_element->set_opacity(0.25f);
std::vector<recompui::Style *> hover_focus = {&option->hover_style, &option->focus_style};
for (auto style : hover_focus) {
style->set_background_color(recompui::theme::color::Transparent);
}
}
recompui::Element *menu_container = menu->get_menu_container();
menu_container->set_width(1440);
menu_container->unset_left();
menu_container->set_top(banjo::launcher_options_top_offset);
menu_container->set_bottom(-banjo::launcher_options_top_offset);
menu_container->set_right(50, recompui::Unit::Percent);
menu_container->set_translate_2D(50.0f, 0.0f, recompui::Unit::Percent);
game_options_menu->unset_left();
game_options_menu->set_bottom(50.0f, recompui::Unit::Percent);
game_options_menu->set_translate_2D(0.0f, 50.0f, recompui::Unit::Percent);
game_options_menu->set_right(banjo::launcher_options_right_position_start);
menu->remove_default_title();
recompui::ContextId context = recompui::get_current_context();
recompui::Label* title_label = context.create_element<recompui::Label>(menu, "Banjo: Recompiled", recompui::theme::Typography::Header1);
title_label->set_position(recompui::Position::Absolute);
title_label->set_top(banjo::launcher_options_title_offset);
title_label->set_right(50.0f, recompui::Unit::Percent);
title_label->set_translate_2D(50.0f, 0.0f, recompui::Unit::Percent);
title_label->set_color(recompui::theme::color::White);
banjo::launcher_animation_setup(menu, title_label);
}
#define REGISTER_FUNC(name) recomp::overlays::register_base_export(#name, name)
@@ -682,6 +707,7 @@ int main(int argc, char** argv) {
banjo::init_config();
recompui::register_launcher_init_callback(on_launcher_init);
recompui::register_launcher_update_callback(banjo::launcher_animation_update);
recomp::rsp::callbacks_t rsp_callbacks{
.get_rsp_microcode = get_rsp_microcode,