mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp
synced 2026-05-23 15:02:04 -04:00
546 lines
22 KiB
C++
546 lines
22 KiB
C++
#include <memory>
|
|
#include <cstring>
|
|
#include <variant>
|
|
#include <algorithm>
|
|
|
|
#define HLSL_CPU
|
|
#include "hle/rt64_application.h"
|
|
#include "rt64_render_hooks.h"
|
|
#include "overloaded.h"
|
|
|
|
#include "ultramodern/ultramodern.hpp"
|
|
#include "ultramodern/config.hpp"
|
|
|
|
#include "zelda_render.h"
|
|
#include "recomp_ui.h"
|
|
#include "concurrentqueue.h"
|
|
|
|
static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
|
|
static bool sample_positions_supported = false;
|
|
static bool high_precision_fb_enabled = false;
|
|
|
|
static uint8_t DMEM[0x1000];
|
|
static uint8_t IMEM[0x1000];
|
|
|
|
struct TexturePackEnableAction {
|
|
std::string mod_id;
|
|
};
|
|
|
|
struct TexturePackDisableAction {
|
|
std::string mod_id;
|
|
};
|
|
|
|
struct TexturePackSecondaryEnableAction {
|
|
std::string mod_id;
|
|
};
|
|
|
|
struct TexturePackSecondaryDisableAction {
|
|
std::string mod_id;
|
|
};
|
|
|
|
struct TexturePackUpdateAction {
|
|
};
|
|
|
|
using TexturePackAction = std::variant<TexturePackEnableAction, TexturePackDisableAction, TexturePackSecondaryEnableAction, TexturePackSecondaryDisableAction, TexturePackUpdateAction>;
|
|
|
|
static moodycamel::ConcurrentQueue<TexturePackAction> texture_pack_action_queue;
|
|
|
|
unsigned int MI_INTR_REG = 0;
|
|
|
|
unsigned int DPC_START_REG = 0;
|
|
unsigned int DPC_END_REG = 0;
|
|
unsigned int DPC_CURRENT_REG = 0;
|
|
unsigned int DPC_STATUS_REG = 0;
|
|
unsigned int DPC_CLOCK_REG = 0;
|
|
unsigned int DPC_BUFBUSY_REG = 0;
|
|
unsigned int DPC_PIPEBUSY_REG = 0;
|
|
unsigned int DPC_TMEM_REG = 0;
|
|
|
|
unsigned int VI_STATUS_REG = 0;
|
|
unsigned int VI_ORIGIN_REG = 0;
|
|
unsigned int VI_WIDTH_REG = 0;
|
|
unsigned int VI_INTR_REG = 0;
|
|
unsigned int VI_V_CURRENT_LINE_REG = 0;
|
|
unsigned int VI_TIMING_REG = 0;
|
|
unsigned int VI_V_SYNC_REG = 0;
|
|
unsigned int VI_H_SYNC_REG = 0;
|
|
unsigned int VI_LEAP_REG = 0;
|
|
unsigned int VI_H_START_REG = 0;
|
|
unsigned int VI_V_START_REG = 0;
|
|
unsigned int VI_V_BURST_REG = 0;
|
|
unsigned int VI_X_SCALE_REG = 0;
|
|
unsigned int VI_Y_SCALE_REG = 0;
|
|
|
|
void dummy_check_interrupts() {}
|
|
|
|
RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampleCounts bits) {
|
|
if (bits & RT64::RenderSampleCount::Bits::COUNT_2) {
|
|
if (bits & RT64::RenderSampleCount::Bits::COUNT_4) {
|
|
if (bits & RT64::RenderSampleCount::Bits::COUNT_8) {
|
|
return RT64::UserConfiguration::Antialiasing::MSAA8X;
|
|
}
|
|
return RT64::UserConfiguration::Antialiasing::MSAA4X;
|
|
}
|
|
return RT64::UserConfiguration::Antialiasing::MSAA2X;
|
|
};
|
|
return RT64::UserConfiguration::Antialiasing::None;
|
|
}
|
|
|
|
RT64::UserConfiguration::AspectRatio to_rt64(ultramodern::renderer::AspectRatio option) {
|
|
switch (option) {
|
|
case ultramodern::renderer::AspectRatio::Original:
|
|
return RT64::UserConfiguration::AspectRatio::Original;
|
|
case ultramodern::renderer::AspectRatio::Expand:
|
|
return RT64::UserConfiguration::AspectRatio::Expand;
|
|
case ultramodern::renderer::AspectRatio::Manual:
|
|
return RT64::UserConfiguration::AspectRatio::Manual;
|
|
case ultramodern::renderer::AspectRatio::OptionCount:
|
|
return RT64::UserConfiguration::AspectRatio::OptionCount;
|
|
}
|
|
}
|
|
|
|
RT64::UserConfiguration::Antialiasing to_rt64(ultramodern::renderer::Antialiasing option) {
|
|
switch (option) {
|
|
case ultramodern::renderer::Antialiasing::None:
|
|
return RT64::UserConfiguration::Antialiasing::None;
|
|
case ultramodern::renderer::Antialiasing::MSAA2X:
|
|
return RT64::UserConfiguration::Antialiasing::MSAA2X;
|
|
case ultramodern::renderer::Antialiasing::MSAA4X:
|
|
return RT64::UserConfiguration::Antialiasing::MSAA4X;
|
|
case ultramodern::renderer::Antialiasing::MSAA8X:
|
|
return RT64::UserConfiguration::Antialiasing::MSAA8X;
|
|
case ultramodern::renderer::Antialiasing::OptionCount:
|
|
return RT64::UserConfiguration::Antialiasing::OptionCount;
|
|
}
|
|
}
|
|
|
|
RT64::UserConfiguration::RefreshRate to_rt64(ultramodern::renderer::RefreshRate option) {
|
|
switch (option) {
|
|
case ultramodern::renderer::RefreshRate::Original:
|
|
return RT64::UserConfiguration::RefreshRate::Original;
|
|
case ultramodern::renderer::RefreshRate::Display:
|
|
return RT64::UserConfiguration::RefreshRate::Display;
|
|
case ultramodern::renderer::RefreshRate::Manual:
|
|
return RT64::UserConfiguration::RefreshRate::Manual;
|
|
case ultramodern::renderer::RefreshRate::OptionCount:
|
|
return RT64::UserConfiguration::RefreshRate::OptionCount;
|
|
}
|
|
}
|
|
|
|
RT64::UserConfiguration::InternalColorFormat to_rt64(ultramodern::renderer::HighPrecisionFramebuffer option) {
|
|
switch (option) {
|
|
case ultramodern::renderer::HighPrecisionFramebuffer::Off:
|
|
return RT64::UserConfiguration::InternalColorFormat::Standard;
|
|
case ultramodern::renderer::HighPrecisionFramebuffer::On:
|
|
return RT64::UserConfiguration::InternalColorFormat::High;
|
|
case ultramodern::renderer::HighPrecisionFramebuffer::Auto:
|
|
return RT64::UserConfiguration::InternalColorFormat::Automatic;
|
|
case ultramodern::renderer::HighPrecisionFramebuffer::OptionCount:
|
|
return RT64::UserConfiguration::InternalColorFormat::OptionCount;
|
|
}
|
|
}
|
|
|
|
void set_application_user_config(RT64::Application* application, const ultramodern::renderer::GraphicsConfig& config) {
|
|
switch (config.res_option) {
|
|
default:
|
|
case ultramodern::renderer::Resolution::Auto:
|
|
application->userConfig.resolution = RT64::UserConfiguration::Resolution::WindowIntegerScale;
|
|
application->userConfig.downsampleMultiplier = 1;
|
|
break;
|
|
case ultramodern::renderer::Resolution::Original:
|
|
application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual;
|
|
application->userConfig.resolutionMultiplier = std::max(config.ds_option, 1);
|
|
application->userConfig.downsampleMultiplier = std::max(config.ds_option, 1);
|
|
break;
|
|
case ultramodern::renderer::Resolution::Original2x:
|
|
application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual;
|
|
application->userConfig.resolutionMultiplier = 2.0 * std::max(config.ds_option, 1);
|
|
application->userConfig.downsampleMultiplier = std::max(config.ds_option, 1);
|
|
break;
|
|
}
|
|
|
|
switch (config.hr_option) {
|
|
default:
|
|
case ultramodern::renderer::HUDRatioMode::Original:
|
|
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Original;
|
|
break;
|
|
case ultramodern::renderer::HUDRatioMode::Clamp16x9:
|
|
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Manual;
|
|
application->userConfig.extAspectTarget = 16.0/9.0;
|
|
break;
|
|
case ultramodern::renderer::HUDRatioMode::Full:
|
|
application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Expand;
|
|
break;
|
|
}
|
|
|
|
application->userConfig.aspectRatio = to_rt64(config.ar_option);
|
|
application->userConfig.antialiasing = to_rt64(config.msaa_option);
|
|
application->userConfig.refreshRate = to_rt64(config.rr_option);
|
|
application->userConfig.refreshRateTarget = config.rr_manual_value;
|
|
application->userConfig.internalColorFormat = to_rt64(config.hpfb_option);
|
|
application->userConfig.displayBuffering = RT64::UserConfiguration::DisplayBuffering::Triple;
|
|
}
|
|
|
|
ultramodern::renderer::SetupResult map_setup_result(RT64::Application::SetupResult rt64_result) {
|
|
switch (rt64_result) {
|
|
case RT64::Application::SetupResult::Success:
|
|
return ultramodern::renderer::SetupResult::Success;
|
|
case RT64::Application::SetupResult::DynamicLibrariesNotFound:
|
|
return ultramodern::renderer::SetupResult::DynamicLibrariesNotFound;
|
|
case RT64::Application::SetupResult::InvalidGraphicsAPI:
|
|
return ultramodern::renderer::SetupResult::InvalidGraphicsAPI;
|
|
case RT64::Application::SetupResult::GraphicsAPINotFound:
|
|
return ultramodern::renderer::SetupResult::GraphicsAPINotFound;
|
|
case RT64::Application::SetupResult::GraphicsDeviceNotFound:
|
|
return ultramodern::renderer::SetupResult::GraphicsDeviceNotFound;
|
|
}
|
|
|
|
fprintf(stderr, "Unhandled `RT64::Application::SetupResult` ?\n");
|
|
assert(false);
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ultramodern::renderer::GraphicsApi map_graphics_api(RT64::UserConfiguration::GraphicsAPI api) {
|
|
switch (api) {
|
|
case RT64::UserConfiguration::GraphicsAPI::D3D12:
|
|
return ultramodern::renderer::GraphicsApi::D3D12;
|
|
case RT64::UserConfiguration::GraphicsAPI::Vulkan:
|
|
return ultramodern::renderer::GraphicsApi::Vulkan;
|
|
case RT64::UserConfiguration::GraphicsAPI::Metal:
|
|
return ultramodern::renderer::GraphicsApi::Metal;
|
|
case RT64::UserConfiguration::GraphicsAPI::Automatic:
|
|
return ultramodern::renderer::GraphicsApi::Auto;
|
|
}
|
|
|
|
fprintf(stderr, "Unhandled `RT64::UserConfiguration::GraphicsAPI` ?\n");
|
|
assert(false);
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
|
|
zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::renderer::WindowHandle window_handle, bool debug) {
|
|
static unsigned char dummy_rom_header[0x40];
|
|
recompui::set_render_hooks();
|
|
|
|
// Set up the RT64 application core fields.
|
|
RT64::Application::Core appCore{};
|
|
#if defined(_WIN32)
|
|
appCore.window = window_handle.window;
|
|
#elif defined(__linux__) || defined(__ANDROID__)
|
|
appCore.window = window_handle;
|
|
#elif defined(__APPLE__)
|
|
appCore.window.window = window_handle.window;
|
|
appCore.window.view = window_handle.view;
|
|
#endif
|
|
|
|
appCore.checkInterrupts = dummy_check_interrupts;
|
|
|
|
appCore.HEADER = dummy_rom_header;
|
|
appCore.RDRAM = rdram;
|
|
appCore.DMEM = DMEM;
|
|
appCore.IMEM = IMEM;
|
|
|
|
appCore.MI_INTR_REG = &MI_INTR_REG;
|
|
|
|
appCore.DPC_START_REG = &DPC_START_REG;
|
|
appCore.DPC_END_REG = &DPC_END_REG;
|
|
appCore.DPC_CURRENT_REG = &DPC_CURRENT_REG;
|
|
appCore.DPC_STATUS_REG = &DPC_STATUS_REG;
|
|
appCore.DPC_CLOCK_REG = &DPC_CLOCK_REG;
|
|
appCore.DPC_BUFBUSY_REG = &DPC_BUFBUSY_REG;
|
|
appCore.DPC_PIPEBUSY_REG = &DPC_PIPEBUSY_REG;
|
|
appCore.DPC_TMEM_REG = &DPC_TMEM_REG;
|
|
|
|
appCore.VI_STATUS_REG = &VI_STATUS_REG;
|
|
appCore.VI_ORIGIN_REG = &VI_ORIGIN_REG;
|
|
appCore.VI_WIDTH_REG = &VI_WIDTH_REG;
|
|
appCore.VI_INTR_REG = &VI_INTR_REG;
|
|
appCore.VI_V_CURRENT_LINE_REG = &VI_V_CURRENT_LINE_REG;
|
|
appCore.VI_TIMING_REG = &VI_TIMING_REG;
|
|
appCore.VI_V_SYNC_REG = &VI_V_SYNC_REG;
|
|
appCore.VI_H_SYNC_REG = &VI_H_SYNC_REG;
|
|
appCore.VI_LEAP_REG = &VI_LEAP_REG;
|
|
appCore.VI_H_START_REG = &VI_H_START_REG;
|
|
appCore.VI_V_START_REG = &VI_V_START_REG;
|
|
appCore.VI_V_BURST_REG = &VI_V_BURST_REG;
|
|
appCore.VI_X_SCALE_REG = &VI_X_SCALE_REG;
|
|
appCore.VI_Y_SCALE_REG = &VI_Y_SCALE_REG;
|
|
|
|
// Set up the RT64 application configuration fields.
|
|
RT64::ApplicationConfiguration appConfig;
|
|
appConfig.useConfigurationFile = false;
|
|
|
|
// Create the RT64 application.
|
|
app = std::make_unique<RT64::Application>(appCore, appConfig);
|
|
|
|
// Set initial user config settings based on the current settings.
|
|
auto& cur_config = ultramodern::renderer::get_graphics_config();
|
|
set_application_user_config(app.get(), cur_config);
|
|
app->userConfig.developerMode = debug;
|
|
// Force gbi depth branches to prevent LODs from kicking in.
|
|
app->enhancementConfig.f3dex.forceBranch = true;
|
|
// Scale LODs based on the output resolution.
|
|
app->enhancementConfig.textureLOD.scale = true;
|
|
// Pick an API if the user has set an override.
|
|
switch (cur_config.api_option) {
|
|
case ultramodern::renderer::GraphicsApi::D3D12:
|
|
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::D3D12;
|
|
break;
|
|
case ultramodern::renderer::GraphicsApi::Vulkan:
|
|
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::Vulkan;
|
|
break;
|
|
case ultramodern::renderer::GraphicsApi::Metal:
|
|
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::Metal;
|
|
break;
|
|
case ultramodern::renderer::GraphicsApi::Auto:
|
|
app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::Automatic;
|
|
break;
|
|
}
|
|
|
|
// Set up the RT64 application.
|
|
uint32_t thread_id = 0;
|
|
#ifdef _WIN32
|
|
thread_id = window_handle.thread_id;
|
|
#endif
|
|
setup_result = map_setup_result(app->setup(thread_id));
|
|
// Get the API that RT64 chose.
|
|
chosen_api = map_graphics_api(app->chosenGraphicsAPI);
|
|
if (setup_result != ultramodern::renderer::SetupResult::Success) {
|
|
app = nullptr;
|
|
return;
|
|
}
|
|
|
|
// Set the application's fullscreen state.
|
|
app->setFullScreen(cur_config.wm_option == ultramodern::renderer::WindowMode::Fullscreen);
|
|
|
|
// Check if the selected device actually supports MSAA sample positions and MSAA for for the formats that will be used
|
|
// and downgrade the configuration accordingly.
|
|
if (app->device->getCapabilities().sampleLocations) {
|
|
RT64::RenderSampleCounts color_sample_counts = app->device->getSampleCountsSupported(RT64::RenderFormat::R8G8B8A8_UNORM);
|
|
RT64::RenderSampleCounts depth_sample_counts = app->device->getSampleCountsSupported(RT64::RenderFormat::D32_FLOAT);
|
|
RT64::RenderSampleCounts common_sample_counts = color_sample_counts & depth_sample_counts;
|
|
device_max_msaa = compute_max_supported_aa(common_sample_counts);
|
|
sample_positions_supported = true;
|
|
}
|
|
else {
|
|
device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
|
|
sample_positions_supported = false;
|
|
}
|
|
|
|
high_precision_fb_enabled = app->shaderLibrary->usesHDR;
|
|
}
|
|
|
|
zelda64::renderer::RT64Context::~RT64Context() = default;
|
|
|
|
void zelda64::renderer::RT64Context::send_dl(const OSTask* task) {
|
|
check_texture_pack_actions();
|
|
app->state->rsp->reset();
|
|
app->interpreter->loadUCodeGBI(task->t.ucode & 0x3FFFFFF, task->t.ucode_data & 0x3FFFFFF, true);
|
|
app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true);
|
|
}
|
|
|
|
void zelda64::renderer::RT64Context::update_screen(uint32_t vi_origin) {
|
|
VI_ORIGIN_REG = vi_origin;
|
|
|
|
app->updateScreen();
|
|
}
|
|
|
|
void zelda64::renderer::RT64Context::shutdown() {
|
|
if (app != nullptr) {
|
|
app->end();
|
|
}
|
|
}
|
|
|
|
bool zelda64::renderer::RT64Context::update_config(const ultramodern::renderer::GraphicsConfig& old_config, const ultramodern::renderer::GraphicsConfig& new_config) {
|
|
if (old_config == new_config) {
|
|
return false;
|
|
}
|
|
|
|
if (new_config.wm_option != old_config.wm_option) {
|
|
app->setFullScreen(new_config.wm_option == ultramodern::renderer::WindowMode::Fullscreen);
|
|
}
|
|
|
|
set_application_user_config(app.get(), new_config);
|
|
|
|
app->updateUserConfig(true);
|
|
|
|
if (new_config.msaa_option != old_config.msaa_option) {
|
|
app->updateMultisampling();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void zelda64::renderer::RT64Context::enable_instant_present() {
|
|
// Enable the present early presentation mode for minimal latency.
|
|
app->enhancementConfig.presentation.mode = RT64::EnhancementConfiguration::Presentation::Mode::PresentEarly;
|
|
|
|
app->updateEnhancementConfig();
|
|
}
|
|
|
|
uint32_t zelda64::renderer::RT64Context::get_display_framerate() const {
|
|
return app->presentQueue->ext.sharedResources->swapChainRate;
|
|
}
|
|
|
|
float zelda64::renderer::RT64Context::get_resolution_scale() const {
|
|
constexpr int ReferenceHeight = 240;
|
|
switch (app->userConfig.resolution) {
|
|
case RT64::UserConfiguration::Resolution::WindowIntegerScale:
|
|
if (app->sharedQueueResources->swapChainHeight > 0) {
|
|
return std::max(float((app->sharedQueueResources->swapChainHeight + ReferenceHeight - 1) / ReferenceHeight), 1.0f);
|
|
}
|
|
else {
|
|
return 1.0f;
|
|
}
|
|
case RT64::UserConfiguration::Resolution::Manual:
|
|
return float(app->userConfig.resolutionMultiplier);
|
|
case RT64::UserConfiguration::Resolution::Original:
|
|
default:
|
|
return 1.0f;
|
|
}
|
|
}
|
|
|
|
void zelda64::renderer::RT64Context::check_texture_pack_actions() {
|
|
bool packs_changed = false;
|
|
TexturePackAction cur_action;
|
|
while (texture_pack_action_queue.try_dequeue(cur_action)) {
|
|
std::visit(overloaded{
|
|
[&](TexturePackDisableAction &to_disable) {
|
|
enabled_texture_packs.erase(to_disable.mod_id);
|
|
packs_changed = true;
|
|
},
|
|
[&](TexturePackEnableAction &to_enable) {
|
|
enabled_texture_packs.insert(to_enable.mod_id);
|
|
packs_changed = true;
|
|
},
|
|
[&](TexturePackSecondaryDisableAction &to_override_disable) {
|
|
secondary_disabled_texture_packs.insert(to_override_disable.mod_id);
|
|
packs_changed = true;
|
|
},
|
|
[&](TexturePackSecondaryEnableAction &to_override_enable) {
|
|
secondary_disabled_texture_packs.erase(to_override_enable.mod_id);
|
|
packs_changed = true;
|
|
},
|
|
[&](TexturePackUpdateAction &) {
|
|
packs_changed = true;
|
|
}
|
|
}, cur_action);
|
|
}
|
|
|
|
// If any packs were disabled, unload all packs and load all the active ones.
|
|
if (packs_changed) {
|
|
// Sort the enabled texture packs in reverse order so that earlier ones override later ones.
|
|
std::vector<std::string> sorted_texture_packs{};
|
|
sorted_texture_packs.reserve(enabled_texture_packs.size());
|
|
for (const std::string& mod : enabled_texture_packs) {
|
|
if (!secondary_disabled_texture_packs.contains(mod)) {
|
|
sorted_texture_packs.emplace_back(mod);
|
|
}
|
|
}
|
|
|
|
std::sort(sorted_texture_packs.begin(), sorted_texture_packs.end(),
|
|
[](const std::string& lhs, const std::string& rhs) {
|
|
return recomp::mods::get_mod_order_index(lhs) > recomp::mods::get_mod_order_index(rhs);
|
|
}
|
|
);
|
|
|
|
// Build the path list from the sorted mod list.
|
|
std::vector<RT64::ReplacementDirectory> replacement_directories;
|
|
replacement_directories.reserve(enabled_texture_packs.size());
|
|
for (const std::string &mod_id : sorted_texture_packs) {
|
|
replacement_directories.emplace_back(RT64::ReplacementDirectory(recomp::mods::get_mod_filename(mod_id)));
|
|
}
|
|
|
|
if (!replacement_directories.empty()) {
|
|
app->textureCache->loadReplacementDirectories(replacement_directories);
|
|
}
|
|
else {
|
|
app->textureCache->clearReplacementDirectories();
|
|
}
|
|
}
|
|
}
|
|
|
|
RT64::UserConfiguration::Antialiasing zelda64::renderer::RT64MaxMSAA() {
|
|
return device_max_msaa;
|
|
}
|
|
|
|
std::unique_ptr<ultramodern::renderer::RendererContext> zelda64::renderer::create_render_context(uint8_t* rdram, ultramodern::renderer::WindowHandle window_handle, bool developer_mode) {
|
|
return std::make_unique<zelda64::renderer::RT64Context>(rdram, window_handle, developer_mode);
|
|
}
|
|
|
|
bool zelda64::renderer::RT64SamplePositionsSupported() {
|
|
return sample_positions_supported;
|
|
}
|
|
|
|
bool zelda64::renderer::RT64HighPrecisionFBEnabled() {
|
|
return high_precision_fb_enabled;
|
|
}
|
|
|
|
void zelda64::renderer::trigger_texture_pack_update() {
|
|
texture_pack_action_queue.enqueue(TexturePackUpdateAction{});
|
|
}
|
|
|
|
void zelda64::renderer::enable_texture_pack(const recomp::mods::ModContext& context, const recomp::mods::ModHandle& mod) {
|
|
texture_pack_action_queue.enqueue(TexturePackEnableAction{mod.manifest.mod_id});
|
|
|
|
// Check for the texture pack enabled config option.
|
|
const recomp::mods::ConfigSchema& config_schema = context.get_mod_config_schema(mod.manifest.mod_id);
|
|
auto find_it = config_schema.options_by_id.find(zelda64::renderer::special_option_texture_pack_enabled);
|
|
if (find_it != config_schema.options_by_id.end()) {
|
|
const recomp::mods::ConfigOption& config_option = config_schema.options[find_it->second];
|
|
|
|
if (is_texture_pack_enable_config_option(config_option, false)) {
|
|
recomp::mods::ConfigValueVariant value_variant = context.get_mod_config_value(mod.manifest.mod_id, config_option.id);
|
|
uint32_t value;
|
|
if (uint32_t* value_ptr = std::get_if<uint32_t>(&value_variant)) {
|
|
value = *value_ptr;
|
|
}
|
|
else {
|
|
value = 0;
|
|
}
|
|
|
|
if (value) {
|
|
zelda64::renderer::secondary_enable_texture_pack(mod.manifest.mod_id);
|
|
}
|
|
else {
|
|
zelda64::renderer::secondary_disable_texture_pack(mod.manifest.mod_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void zelda64::renderer::disable_texture_pack(const recomp::mods::ModHandle& mod) {
|
|
texture_pack_action_queue.enqueue(TexturePackDisableAction{mod.manifest.mod_id});
|
|
}
|
|
|
|
void zelda64::renderer::secondary_enable_texture_pack(const std::string& mod_id) {
|
|
texture_pack_action_queue.enqueue(TexturePackSecondaryEnableAction{mod_id});
|
|
}
|
|
|
|
void zelda64::renderer::secondary_disable_texture_pack(const std::string& mod_id) {
|
|
texture_pack_action_queue.enqueue(TexturePackSecondaryDisableAction{mod_id});
|
|
}
|
|
|
|
|
|
// HD texture enable option. Must be an enum with two options.
|
|
// The first option is treated as disabled and the second option is treated as enabled.
|
|
bool zelda64::renderer::is_texture_pack_enable_config_option(const recomp::mods::ConfigOption& option, bool show_errors) {
|
|
if (option.id == zelda64::renderer::special_option_texture_pack_enabled) {
|
|
if (option.type != recomp::mods::ConfigOptionType::Enum) {
|
|
if (show_errors) {
|
|
recompui::message_box(("Mod has the special config option id for enabling an HD texture pack (\"" + zelda64::renderer::special_option_texture_pack_enabled + "\"), but the config option is not an enum.").c_str());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const recomp::mods::ConfigOptionEnum &option_enum = std::get<recomp::mods::ConfigOptionEnum>(option.variant);
|
|
if (option_enum.options.size() != 2) {
|
|
if (show_errors) {
|
|
recompui::message_box(("Mod has the special config option id for enabling an HD texture pack (\"" + zelda64::renderer::special_option_texture_pack_enabled + "\"), but the config option doesn't have exactly 2 values.").c_str());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|