/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "globalincs/systemvars.h" #include "debugconsole/console.h" #include "globalincs/pstypes.h" #include "graphics/2d.h" #include "nebula/neb.h" #include "options/Option.h" fix Missiontime; fix Frametime; int Framecount=0; int Game_mode; int Game_restoring = 0; // If set, this means we are restoring data from disk int Viewer_mode; // Viewer's mode, see VM_xxxx flags. //CUTSCENE STUFF //Cutscene flags int Cutscene_bar_flags = CUB_NONE; //Time for gradual change in seconds float Cutscene_delta_time = 1.0f; //How far along a change is (0 to 1) float Cutscene_bars_progress = 1.0f; // The detail level. Anything below zero draws simple models earlier than it // should. Anything above zero draws higher detail models longer than it should. // -2=lowest // -1=low // 0=normal (medium) // 1=high // 2=extra high int Game_detail_level = 0; uint Game_detail_flags = DETAIL_DEFAULT; // see systemvars.h for explanation angles Viewer_slew_angles; // Angles of viewer relative to forward. vei Viewer_external_info; // Viewer angles to ship in external view. vci Viewer_chase_info; // View chase camera information vec3d leaning_position; int Is_standalone; int Interface_last_tick = -1; // last timer tick on flip #ifndef NDEBUG // for debugging, used to print the currently processing filename on the loading screen char Processing_filename[MAX_PATH_LEN]; #endif // override states to skip rendering of certain elements, but without disabling them completely bool Envmap_override = false; bool Glowpoint_override = false; bool Glowpoint_use_depth_buffer = true; bool PostProcessing_override = false; bool Shadow_override = false; bool Trail_render_override = false; // Values used for noise for thruster animations float Noise[NOISE_NUM_FRAMES] = { 0.468225f, 0.168765f, 0.318945f, 0.292866f, 0.553357f, 0.468225f, 0.180456f, 0.418465f, 0.489958f, 1.000000f, 0.468225f, 0.599820f, 0.664718f, 0.294215f, 0.000000f }; // Variables for the loading callback hooks static int cf_timestamp = -1; static void (*cf_callback)(int count) = NULL; static int cf_in_callback = 0; static int cb_counter = 0; static int cb_last_counter = 0; static int cb_delta_step = -1; // Call this with the name of a function. That function will // then get called around 10x per second. The callback function // gets passed a 'count' which is how many times game_busy has // been called since the callback was set. It gets called // one last time with count=-1 when you turn off the callback // by calling game_busy_callback(NULL). Game_busy_callback // returns the current count, so you can tell how many times // game_busy got called. int game_busy_callback( void (*callback)(int count), int delta_step ) { if ( !callback ) { // Call it once more to finalize things cf_in_callback++; (*cf_callback)(-1); cf_in_callback--; cf_timestamp = -1; cf_callback = NULL; } else { cb_counter = 0; cb_last_counter = 0; cb_delta_step = delta_step; cf_timestamp = timer_get_milliseconds()+(1000/10); cf_callback = callback; // Call it once cf_in_callback++; (*cf_callback)(0); // pass 0 first time! cf_in_callback--; } return cb_counter; } // Call whenever loading to display cursor void game_busy(const char *filename) { if ( cf_in_callback != 0 ) return; // don't call callback if we're already in it. if ( cf_timestamp < 0 ) return; if ( !cf_callback ) return; cb_counter++; // mprintf(( "CB_COUNTER=%d\n", cb_counter )); #ifndef NDEBUG if (filename != NULL) strcpy_s(Processing_filename, filename); #endif int t1 = timer_get_milliseconds(); if ( (t1 > cf_timestamp) || ((cb_counter > cb_last_counter+155) && (cb_delta_step > 0)) ) { cb_last_counter = cb_counter; cf_in_callback++; (*cf_callback)(cb_counter); cf_in_callback--; cf_timestamp = t1 + (1000/10); } } static_assert(MAX_DETAIL_VALUE == 4, "MAX_DETAIL_VALUE is assumed to be 4 in SystemVars.cpp"); static_assert(static_cast(DefaultDetailPreset::Num_detail_presets) == 4, "Code in ManagePilot assumes Num_detail_presets = 4"); // Detail preset stuff detail_levels Detail_defaults[static_cast(DefaultDetailPreset::Num_detail_presets)] = { { DefaultDetailPreset::Low, // setting // ================== Analogs ================== 0, // nebula_detail; // 0=lowest detail, MAX_DETAIL_VALUE=highest detail 0, // detail_distance; // 0=lowest MAX_DETAIL_VALUE=highest 0, // hardware_textures; // 0=max culling, MAX_DETAIL_VALUE=no culling 0, // num_small_debris; // 0=min number, MAX_DETAIL_VALUE=max number 0, // num_particles; // 0=min number, MAX_DETAIL_VALUE=max number 0, // num_stars; // 0=min number, MAX_DETAIL_VALUE=max number 0, // shield_effects; // 0=min, MAX_DETAIL_VALUE=max 2, // lighting; // 0=min, MAX_DETAIL_VALUE=max // ================== Booleans ================== false, // targetview_model; // false=off, true=on false, // planets_suns; // false=off, true=on false, // weapon_extras // false=off, true=on }, { DefaultDetailPreset::Medium, // setting // ================== Analogs ================== 2, // nebula_detail; // 0=lowest detail, MAX_DETAIL_VALUE=highest detail 2, // detail_distance; // 0=lowest MAX_DETAIL_VALUE=highest 2, // hardware_textures; // 0=max culling, MAX_DETAIL_VALUE=no culling 2, // num_small_debris; // 0=min number, MAX_DETAIL_VALUE=max number 2, // num_particles; // 0=min number, MAX_DETAIL_VALUE=max number 2, // num_stars; // 0=min number, MAX_DETAIL_VALUE=max number 2, // shield_effects; // 0=min, MAX_DETAIL_VALUE=max 3, // lighting; // 0=min, MAX_DETAIL_VALUE=max // ================== Booleans ================== true, // targetview_model; // false=off, true=on true, // planets_suns; // false=off, true=on true, // weapon_extras // false=off, true=on }, { DefaultDetailPreset::High, // setting // ================== Analogs ================== 2, // nebula_detail; // 0=lowest detail, MAX_DETAIL_VALUE=highest detail 2, // detail_distance; // 0=lowest MAX_DETAIL_VALUE=highest 3, // hardware_textures; // 0=max culling, MAX_DETAIL_VALUE=no culling 3, // num_small_debris; // 0=min number, MAX_DETAIL_VALUE=max number 3, // num_particles; // 0=min number, MAX_DETAIL_VALUE=max number 4, // num_stars; // 0=min number, MAX_DETAIL_VALUE=max number 3, // shield_effects; // 0=min, MAX_DETAIL_VALUE=max 4, // lighting; // 0=min, MAX_DETAIL_VALUE=max // ================== Booleans ================== true, // targetview_model; // false=off, true=on true, // planets_suns; // false=off, true=on true, // weapon_extras // false=off, true=on }, { DefaultDetailPreset::VeryHigh, // setting // ================== Analogs ================== 4, // nebula_detail; // 0=lowest detail, MAX_DETAIL_VALUE=highest detail 4, // detail_distance; // 0=lowest MAX_DETAIL_VALUE=highest 4, // hardware_textures; // 0=max culling, MAX_DETAIL_VALUE=no culling 4, // num_small_debris; // 0=min number, MAX_DETAIL_VALUE=max number 4, // num_particles; // 0=min number, MAX_DETAIL_VALUE=max number 4, // num_stars; // 0=min number, MAX_DETAIL_VALUE=max number 4, // shield_effects; // 0=min, MAX_DETAIL_VALUE=max 4, // lighting; // 0=min, MAX_DETAIL_VALUE=max // ================== Booleans ================== true, // targetview_model; // false=off, true=on true, // planets_suns; // false=off, true=on true, // weapon_extras // false=off, true=on }, }; // Global used to access detail presets in game and libs detail_levels Detail = Detail_defaults[static_cast(DefaultDetailPreset::Num_detail_presets) - 1]; const SCP_vector>> DetailLevelValues = {{ 0, {"Minimum", 1680}}, { 1, {"Low", 1160}}, { 2, {"Medium", 1161}}, { 3, {"High", 1162}}, { 4, {"Ultra", 1721}}}; static void parse_model_detail_func() { constexpr int num_detail_presets = static_cast(DefaultDetailPreset::Num_detail_presets); int value[num_detail_presets]; stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE); for (int i = 0; i < num_detail_presets; i++) { if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) { error_display(0, "%i is an invalid detail level value!", value[i]); } else { change_default_detail_level(static_cast(i), DetailSetting::DetailDistance, value[i]); } } } // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton const auto ModelDetailOption __UNUSED = options::OptionBuilder("Graphics.Detail", std::pair{"Model Detail", 1739}, std::pair{"Detail level of models", 1740}) .importance(8) .category(std::make_pair("Graphics", 1825)) .values(DetailLevelValues) .default_func([](){return Detail.detail_distance;}) .change_listener([](int val, bool) { Detail.detail_distance = val; return true; }) .flags({options::OptionFlags::RetailBuiltinOption}) .parser(parse_model_detail_func) .finish(); static void parse_texture_detail_func() { constexpr int num_detail_presets = static_cast(DefaultDetailPreset::Num_detail_presets); int value[num_detail_presets]; stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE); for (int i = 0; i < num_detail_presets; i++) { if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) { error_display(0, "%i is an invalid detail level value!", value[i]); } else { change_default_detail_level(static_cast(i), DetailSetting::HardwareTextures, value[i]); } } } // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton const auto TexturesOption __UNUSED = options::OptionBuilder("Graphics.Texture", std::pair{"3D Hardware Textures", 1362}, std::pair{"Level of detail of textures", 1720}) .importance(6) .category(std::make_pair("Graphics", 1825)) .values(DetailLevelValues) .default_func([](){return Detail.hardware_textures;}) .change_listener([](int val, bool) { Detail.hardware_textures = val; return true; }) .flags({options::OptionFlags::RetailBuiltinOption}) .parser(parse_texture_detail_func) .finish(); static void parse_particles_detail_func() { constexpr int num_detail_presets = static_cast(DefaultDetailPreset::Num_detail_presets); int value[num_detail_presets]; stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE); for (int i = 0; i < num_detail_presets; i++) { if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) { error_display(0, "%i is an invalid detail level value!", value[i]); } else { change_default_detail_level(static_cast(i), DetailSetting::NumParticles, value[i]); } } } // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton const auto ParticlesOption __UNUSED = options::OptionBuilder("Graphics.Particles", std::pair{"Particles", 1363}, std::pair{"Level of detail for particles", 1717}) .importance(5) .category(std::make_pair("Graphics", 1825)) .values(DetailLevelValues) .default_func([](){return Detail.num_particles;}) .change_listener([](int val, bool) { Detail.num_particles = val; return true; }) .flags({options::OptionFlags::RetailBuiltinOption}) .parser(parse_particles_detail_func) .finish(); static void parse_debris_detail_func() { constexpr int num_detail_presets = static_cast(DefaultDetailPreset::Num_detail_presets); int value[num_detail_presets]; stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE); for (int i = 0; i < num_detail_presets; i++) { if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) { error_display(0, "%i is an invalid detail level value!", value[i]); } else { change_default_detail_level(static_cast(i), DetailSetting::NumSmallDebris, value[i]); } } } // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton const auto SmallDebrisOption __UNUSED = options::OptionBuilder("Graphics.SmallDebris", std::pair{"Impact Effects", 1364}, std::pair{"Level of detail of impact effects", 1743}) .category(std::make_pair("Graphics", 1825)) .values(DetailLevelValues) .default_func([](){return Detail.num_small_debris;}) .importance(4) .change_listener([](int val,bool) { Detail.num_small_debris = val; return true; }) .flags({options::OptionFlags::RetailBuiltinOption}) .parser(parse_debris_detail_func) .finish(); static void parse_shield_detail_func() { constexpr int num_detail_presets = static_cast(DefaultDetailPreset::Num_detail_presets); int value[num_detail_presets]; stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE); for (int i = 0; i < num_detail_presets; i++) { if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) { error_display(0, "%i is an invalid detail level value!", value[i]); } else { change_default_detail_level(static_cast(i), DetailSetting::ShieldEffects, value[i]); } } } // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton const auto ShieldEffectsOption __UNUSED = options::OptionBuilder("Graphics.ShieldEffects", std::pair{"Shield Hit Effects", 1718}, std::pair{"Level of detail of shield impacts", 1719}) .importance(3) .category(std::make_pair("Graphics", 1825)) .values(DetailLevelValues) .default_func([](){return Detail.shield_effects;}) .change_listener([](int val, bool) { Detail.shield_effects = val; return true; }) .flags({options::OptionFlags::RetailBuiltinOption}) .parser(parse_shield_detail_func) .finish(); static void parse_stars_detail_func() { constexpr int num_detail_presets = static_cast(DefaultDetailPreset::Num_detail_presets); int value[num_detail_presets]; stuff_int_list(value, num_detail_presets, ParseLookupType::RAW_INTEGER_TYPE); for (int i = 0; i < num_detail_presets; i++) { if (value[i] < 0 || value[i] > MAX_DETAIL_VALUE) { error_display(0, "%i is an invalid detail level value!", value[i]); } else { change_default_detail_level(static_cast(i), DetailSetting::NumStars, value[i]); } } } // coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton const auto StarsOption __UNUSED = options::OptionBuilder("Graphics.Stars", std::pair{"Stars", 1366}, std::pair{"Number of stars in the mission", 1698}) .importance(2) .category(std::make_pair("Graphics", 1825)) .values(DetailLevelValues) .default_func([](){return Detail.num_stars;}) .change_listener([](int val, bool) { Detail.num_stars = val; return true; }) .flags({options::OptionFlags::RetailBuiltinOption}) .parser(parse_stars_detail_func) .finish(); // Call this with: // 0 - lowest // Num_detail_presets - highest // To set the parameters in Detail to some set of defaults void detail_level_set(DefaultDetailPreset preset) { if (preset == DefaultDetailPreset::Custom) { Detail.setting = DefaultDetailPreset::Custom; return; } Detail = Detail_defaults[static_cast(preset)]; } // Change detail values 0 - MAX_DETAIL_VALUE void change_default_detail_level(DefaultDetailPreset preset, DetailSetting selection, int value) { // Use a switch statement for more readable access to the struct members switch (selection) { case DetailSetting::NebulaDetail: Detail_defaults[static_cast(preset)].nebula_detail = value; break; case DetailSetting::DetailDistance: Detail_defaults[static_cast(preset)].detail_distance = value; break; case DetailSetting::HardwareTextures: Detail_defaults[static_cast(preset)].hardware_textures = value; break; case DetailSetting::NumSmallDebris: Detail_defaults[static_cast(preset)].num_small_debris = value; break; case DetailSetting::NumParticles: Detail_defaults[static_cast(preset)].num_particles = value; break; case DetailSetting::NumStars: Detail_defaults[static_cast(preset)].num_stars = value; break; case DetailSetting::ShieldEffects: Detail_defaults[static_cast(preset)].shield_effects = value; break; case DetailSetting::Lighting: Detail_defaults[static_cast(preset)].lighting = value; break; default: UNREACHABLE("Invalid detail selection %d. Get a coder!", static_cast(selection)); } } // Change detail values bool overload void change_default_detail_level(DefaultDetailPreset preset, DetailSetting selection, bool value) { // Use a switch statement for more readable access to the struct members switch (selection) { case DetailSetting::TargetViewModel: Detail_defaults[static_cast(preset)].targetview_model = value; break; case DetailSetting::PlanetsSuns: Detail_defaults[static_cast(preset)].planets_suns = value; break; case DetailSetting::WeaponExtras: Detail_defaults[static_cast(preset)].weapon_extras = value; break; default: UNREACHABLE("Invalid detail selection %d. Get a coder!", static_cast(selection)); } } // Returns the current detail preset or -1 if custom. DefaultDetailPreset current_detail_preset() { // return Detail.setting; int i; for (i = 0; i < static_cast(DefaultDetailPreset::Num_detail_presets); i++) { if ( memcmp( &Detail, &Detail_defaults[i], sizeof(detail_levels) )==0 ) { return static_cast(i); } } return DefaultDetailPreset::Custom; } #ifndef NDEBUG DCF(detail_level,"Change the detail level") { int value; if (dc_optional_string_either("help", "--help")) { dc_printf( "Usage: detail_level [n]\n"); dc_printf("[n] -- is detail level.\n"); dc_printf("\t0 is 'normal' detail,\n"); dc_printf("\tnegative values are lower, and\n"); dc_printf("\tpositive values are higher.\n\n"); dc_printf("No parameter resets it to default.\n"); return; } if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) { dc_printf("Detail level set to %d\n", Game_detail_level); return; } if (dc_maybe_stuff_int(&value)) { Game_detail_level = value; dc_printf("Detail level set to %i\n", Game_detail_level); } else { Game_detail_level = 0; dc_printf("Detail level reset\n"); } } DCF(detail, "Turns on/off parts of the game for speed testing" ) { int value; if (dc_optional_string_either("help", "--help")) { dc_printf( "Usage: detail [n]\n"); dc_printf("[n] is detail bit to toggle:\n" ); dc_printf( " 1: draw the stars\n" ); dc_printf( " 2: draw the nebulas\n" ); dc_printf( " 4: draw the motion debris\n" ); dc_printf( " 8: draw planets\n" ); dc_printf( " 16: draw models not as blobs\n" ); dc_printf( " 32: draw lasers not as pixels\n" ); dc_printf( " 64: clear screen background after each frame\n" ); dc_printf( " 128: draw hud stuff\n" ); dc_printf( " 256: draw fireballs\n" ); dc_printf( " 512: do collision detection\n\n" ); dc_printf("No argument will toggle between highest/lowest detail settings\n"); return; } if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) { dc_printf("Detail flags set to 0x%08x\n", Game_detail_flags); dc_printf( " 1: draw the stars: %s\n", ((Game_detail_flags & 1) ? "on" : "off")); dc_printf( " 2: draw the nebulas: %s\n", ((Game_detail_flags & 2)?"on" : "off")); dc_printf( " 4: draw the motion debris: %s\n", ((Game_detail_flags & 4) ? "on" : "off")); dc_printf( " 8: draw planets: %s\n", ((Game_detail_flags & 8) ? "on" : "off")); dc_printf( " 16: draw models not as blobs: %s\n", ((Game_detail_flags & 16) ? "on" : "off")); dc_printf( " 32: draw lasers not as pixels: %s\n", ((Game_detail_flags & 32) ? "on" : "off")); dc_printf( " 64: clear screen background after each frame: %s\n", ((Game_detail_flags & 64) ? "on" : "off")); dc_printf( " 128: draw hud stuff: %s\n", ((Game_detail_flags & 128) ? "on" : "off")); dc_printf( " 256: draw fireballs: %s\n", ((Game_detail_flags & 256) ? "on" : "off")); dc_printf( " 512: do collision detection: %s\n", ((Game_detail_flags & 512) ? "on" : "off")); return; } if (dc_maybe_stuff_int(&value)) { Game_detail_flags ^= value; } else { if (Game_detail_flags == DETAIL_DEFAULT) { Game_detail_flags = DETAIL_FLAG_CLEAR; dc_printf( "Detail flags set lowest (except has screen clear)\n" ); } else { Game_detail_flags = DETAIL_DEFAULT; dc_printf( "Detail flags set highest\n" ); } } } #endif