#include "post_processing.h" #include "graphics/grinternal.h" #include "options/Option.h" #include "parse/parselo.h" #include "ship/ship.h" #include "starfield/supernova.h" namespace graphics { namespace { PostEffectUniformType mapUniformNameToType(const SCP_string& uniform_name) { if (!stricmp(uniform_name.c_str(), "noise_amount")) { return PostEffectUniformType::NoiseAmount; } else if (!stricmp(uniform_name.c_str(), "saturation")) { return PostEffectUniformType::Saturation; } else if (!stricmp(uniform_name.c_str(), "brightness")) { return PostEffectUniformType::Brightness; } else if (!stricmp(uniform_name.c_str(), "contrast")) { return PostEffectUniformType::Contrast; } else if (!stricmp(uniform_name.c_str(), "film_grain")) { return PostEffectUniformType::FilmGrain; } else if (!stricmp(uniform_name.c_str(), "tv_stripes")) { return PostEffectUniformType::TvStripes; } else if (!stricmp(uniform_name.c_str(), "cutoff")) { return PostEffectUniformType::Cutoff; } else if (!stricmp(uniform_name.c_str(), "dither")) { return PostEffectUniformType::Dither; } else if (!stricmp(uniform_name.c_str(), "tint")) { return PostEffectUniformType::Tint; } else if (!stricmp(uniform_name.c_str(), "custom_effect_vec3_a")) { return PostEffectUniformType::CustomEffectVEC3A; } else if (!stricmp(uniform_name.c_str(), "custom_effect_float_a")) { return PostEffectUniformType::CustomEffectFloatA; } else if (!stricmp(uniform_name.c_str(), "custom_effect_vec3_b")) { return PostEffectUniformType::CustomEffectVEC3B; } else if (!stricmp(uniform_name.c_str(), "custom_effect_float_b")) { return PostEffectUniformType::CustomEffectFloatB; } else { error_display(0, "Unknown uniform name '%s'!", uniform_name.c_str()); return PostEffectUniformType::Invalid; } } // used by In-Game Options menu bool Post_processing_enable_lightshafts = true; void parse_lightshafts_func() { bool enabled; stuff_boolean(&enabled); Post_processing_enable_lightshafts = enabled; } // used by In-Game Options menu bool Post_processing_enable_sunglare = true; void parse_sunglare_func() { bool enabled; stuff_boolean(&enabled); Post_processing_enable_sunglare = enabled; } // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton auto LightshaftsOption = options::OptionBuilder("Graphics.Lightshafts", std::pair{"Lightshafts", 1724}, std::pair{"Enables or disables lightshafts (requires post-processing)", 1725}) .category(std::make_pair("Graphics", 1825)) .default_func([]() { return Post_processing_enable_lightshafts;}) .level(options::ExpertLevel::Advanced) .bind_to(&Post_processing_enable_lightshafts) .importance(60) .parser(parse_lightshafts_func) .finish(); // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton auto SunglareOption = options::OptionBuilder("Graphics.Sunglare", std::pair{"Sunglare", 1880}, std::pair{"Enables or disables glare from suns", 1881}) .category(std::make_pair("Graphics", 1825)) .default_func([]() { return Post_processing_enable_sunglare;}) .level(options::ExpertLevel::Advanced) .bind_to(&Post_processing_enable_sunglare) .importance(61) .parser(parse_sunglare_func) .finish(); int Post_processing_bloom_intensity = 25; // using default value of Cmdline_bloom_intensity void parse_bloom_intensity_func() { int value; stuff_int(&value); CLAMP(value, 0, 200); Post_processing_bloom_intensity = value; } // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton static auto BloomIntensityOption __UNUSED = options::OptionBuilder("Graphics.BloomIntensity", std::pair{"Bloom intensity", 1701}, std::pair{"Sets the bloom intensity (requires post-processing)", 1702}) .category(std::make_pair("Graphics", 1825)) .range(0, 200) .level(options::ExpertLevel::Advanced) .default_func([](){return Post_processing_bloom_intensity;}) .bind_to(&Post_processing_bloom_intensity) .importance(55) .flags({options::OptionFlags::RangeTypeInteger}) .parser(parse_bloom_intensity_func) .finish(); } // namespace bool PostProcessingManager::parse_table() { bool warned = false; try { if (cf_exists_full("post_processing.tbl", CF_TYPE_TABLES)) read_file_text("post_processing.tbl", CF_TYPE_TABLES); else read_file_text_from_default(defaults_get_file("post_processing.tbl")); reset_parse(); if (optional_string("#Effects")) { while (!required_string_one_of(3, "$Name:", "#Ship Effects", "#End")) { post_effect_t eff; required_string("$Name:"); stuff_string(eff.name, F_NAME); required_string("$Uniform:"); SCP_string tbuf; stuff_string(tbuf, F_NAME); eff.uniform_type = mapUniformNameToType(tbuf); required_string("$Define:"); stuff_string(eff.define_name, F_NAME); required_string("$AlwaysOn:"); stuff_boolean(&eff.always_on); required_string("$Default:"); stuff_float(&eff.default_intensity); eff.intensity = eff.default_intensity; required_string("$Div:"); stuff_float(&eff.div); required_string("$Add:"); stuff_float(&eff.add); if (optional_string("$RGB:")) { stuff_vec3d(&eff.rgb); } // Post_effects index is used for flag checks, so we can't have more than 32 if (m_postEffects.size() < 32) { m_postEffects.push_back(std::move(eff)); } else if (!warned) { mprintf(("WARNING: post_processing.tbl can only have a max of 32 effects! Ignoring extra...\n")); warned = true; } } } // Built-in per-ship effects ship_effect se1; strcpy_s(se1.name, "FS1 Ship select"); se1.shader_effect = 0; se1.disables_rendering = false; se1.invert_timer = false; Ship_effects.push_back(se1); if (optional_string("#Ship Effects")) { while (!required_string_one_of(3, "$Name:", "#Light Shafts", "#End")) { ship_effect se; char tbuf[NAME_LENGTH] = {0}; required_string("$Name:"); stuff_string(tbuf, F_NAME, NAME_LENGTH); strcpy_s(se.name, tbuf); required_string("$Shader Effect:"); stuff_int(&se.shader_effect); required_string("$Disables Rendering:"); stuff_boolean(&se.disables_rendering); required_string("$Invert timer:"); stuff_boolean(&se.invert_timer); Ship_effects.push_back(se); } } if (optional_string("#Light Shafts")) { required_string("$AlwaysOn:"); stuff_boolean(&m_lightshaftParams.on); required_string("$Density:"); stuff_float(&m_lightshaftParams.density); required_string("$Falloff:"); stuff_float(&m_lightshaftParams.falloff); required_string("$Weight:"); stuff_float(&m_lightshaftParams.weight); required_string("$Intensity:"); stuff_float(&m_lightshaftParams.intensity); required_string("$Sample Number:"); stuff_int(&m_lightshaftParams.samplenum); m_lightshaftParams.cpintensity = m_lightshaftParams.weight; float falloff = m_lightshaftParams.falloff; for (int i = 1; i < m_lightshaftParams.samplenum; ++i) { m_lightshaftParams.cpintensity += m_lightshaftParams.weight * falloff; falloff *= m_lightshaftParams.falloff; // this replaces pow(falloff, i) } m_lightshaftParams.cpintensity *= m_lightshaftParams.intensity; } required_string("#End"); return true; } catch (const parse::ParseException& e) { mprintf(("Unable to parse 'post_processing.tbl'! Error message = %s.\n", e.what())); return false; } } void PostProcessingManager::clear() { m_postEffects.clear(); m_lightshaftParams = lightshaft_parameters(); } const SCP_vector& PostProcessingManager::getPostEffects() const { return m_postEffects; } SCP_vector& PostProcessingManager::getPostEffects() { return m_postEffects; } const lightshaft_parameters& PostProcessingManager::getLightshaftParams() const { return m_lightshaftParams; } lightshaft_parameters& PostProcessingManager::getLightshaftParams() { return m_lightshaftParams; } bool PostProcessingManager::bloomShadersOk() const { return m_bloomShadersOk; } void PostProcessingManager::setBloomShadersOk(bool ok) { m_bloomShadersOk = ok; } } // namespace graphics bool gr_lightshafts_enabled() { if (gr_screen.mode == GraphicsAPI::Stub) { return false; } // supernova glare should switch to legacy lightshafts if (supernova_stage() >= SUPERNOVA_STAGE::CLOSE) { return false; } if (!graphics::Post_processing_manager->getLightshaftParams().on) { return false; } return graphics::LightshaftsOption->getValue(); } bool gr_sunglare_enabled() { if (gr_screen.mode == GraphicsAPI::Stub) { return false; } // supernova glare gets to override this and actually display glare if (supernova_stage() >= SUPERNOVA_STAGE::CLOSE) { return true; } return graphics::SunglareOption->getValue(); } int gr_bloom_intensity() { if (gr_screen.mode == GraphicsAPI::Stub) { return 0; } if (graphics::Post_processing_manager == nullptr || !graphics::Post_processing_manager->bloomShadersOk()) { return 0; } return graphics::BloomIntensityOption->getValue(); } void gr_set_bloom_intensity(int intensity) { if (gr_screen.mode == GraphicsAPI::Stub) { return; } graphics::Post_processing_bloom_intensity = intensity; options::OptionsManager::instance()->set_ingame_range_option("Graphics.BloomIntensity", intensity); }