| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: GameLOD.cpp ///////////////////////////////////////////////////////////
- //
- // Used to set detail levels of various game systems.
- //
- // Author: Mark Wilczynski, Sept 2002
- //
- //
- ///////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
- #include "Common/GameLOD.h"
- #include "GameClient/TerrainVisual.h"
- #include "GameClient/GameClient.h"
- #include "Common/UserPreferences.h"
- #define DEFINE_PARTICLE_SYSTEM_NAMES
- #include "GameClient/ParticleSys.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- #define PROFILE_ERROR_LIMIT 0.94f //fraction of profiled result needed to get a match. Allows some room for error/fluctuation.
- //Hack to get access to a static method on the W3DDevice side. -MW
- extern Bool testMinimumRequirements(ChipsetType *videoChipType, CpuType *cpuType, Int *cpuFreq, Int *numRAM, Real *intBenchIndex, Real *floatBenchIndex, Real *memBenchIndex);
- GameLODManager *TheGameLODManager=NULL;
- static const FieldParse TheStaticGameLODFieldParseTable[] =
- {
- { "MinimumFPS", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_minFPS)},
- { "MinimumProcessorFps", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_minProcessorFPS)},
- { "SampleCount2D", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_sampleCount2D ) },
- { "SampleCount3D", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_sampleCount3D ) },
- { "StreamCount", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_streamCount ) },
- { "MaxParticleCount", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxParticleCount ) },
- { "UseShadowVolumes", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useShadowVolumes ) },
- { "UseShadowDecals", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useShadowDecals ) },
- { "UseCloudMap", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useCloudMap ) },
- { "UseLightMap", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useLightMap ) },
- { "ShowSoftWaterEdge", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_showSoftWaterEdge ) },
- { "MaxTankTrackEdges", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackEdges) },
- { "MaxTankTrackOpaqueEdges", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackOpaqueEdges) },
- { "MaxTankTrackFadeDelay", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackFadeDelay) },
- { "UseBuildupScaffolds", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useBuildupScaffolds ) },
- { "UseTreeSway", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useTreeSway ) },
- { "UseEmissiveNightMaterials", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useEmissiveNightMaterials ) },
- { "UseHeatEffects", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useHeatEffects ) },
- { "TextureReductionFactor", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_textureReduction ) },
- };
- static const char *StaticGameLODNames[]=
- {
- "Low",
- "Medium",
- "High",
- "Custom"
- };
- StaticGameLODInfo::StaticGameLODInfo(void)
- {
- m_minFPS=0;
- m_minProcessorFPS=0;
- m_sampleCount2D=6;
- m_sampleCount3D=24;
- m_streamCount=2;
- m_maxParticleCount=2500;
- m_useShadowVolumes=TRUE;
- m_useShadowDecals=TRUE;
- m_useCloudMap=TRUE;
- m_useLightMap=TRUE;
- m_showSoftWaterEdge=TRUE;
- m_maxTankTrackEdges=100;
- m_maxTankTrackOpaqueEdges=25;
- m_maxTankTrackFadeDelay=300000;
- m_useBuildupScaffolds=TRUE;
- m_useTreeSway=TRUE;
- m_useEmissiveNightMaterials=TRUE;
- m_useHeatEffects=TRUE;
- m_textureReduction = 0; //none
- m_useFpsLimit = TRUE;
- m_enableDynamicLOD = TRUE;
- m_useTrees = TRUE;
- }
- static const FieldParse TheDynamicGameLODFieldParseTable[] =
- {
- { "MinimumFPS", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_minFPS)},
- { "ParticleSkipMask", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_dynamicParticleSkipMask)},
- { "DebrisSkipMask", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_dynamicDebrisSkipMask)},
- { "SlowDeathScale", INI::parseReal, NULL, offsetof( DynamicGameLODInfo, m_slowDeathScale)},
- { "MinParticlePriority", INI::parseIndexList, ParticlePriorityNames, offsetof( DynamicGameLODInfo, m_minDynamicParticlePriority)},
- { "MinParticleSkipPriority", INI::parseIndexList, ParticlePriorityNames, offsetof( DynamicGameLODInfo, m_minDynamicParticleSkipPriority)},
- };
- static const char *DynamicGameLODNames[]=
- {
- "Low",
- "Medium",
- "High",
- "VeryHigh"
- };
- DynamicGameLODInfo::DynamicGameLODInfo(void)
- {
- m_minFPS=0;
- m_dynamicParticleSkipMask=0;
- m_dynamicDebrisSkipMask=0;
- m_slowDeathScale=1.0f;
- m_minDynamicParticlePriority = PARTICLE_PRIORITY_LOWEST;
- m_minDynamicParticleSkipPriority = PARTICLE_PRIORITY_LOWEST;
- };
- //Keep this in sync with enum in GameLOD.h
- static char *CPUNames[] =
- {
- "XX","P3", "P4","K7", NULL
- };
- //Keep this in sync with enum in GameLOD.h
- static char *VideoNames[] =
- {
- "XX","V2","V3","V4","V5","TNT","TNT2","GF2","R100","PS11","GF3","GF4","PS14","R200","PS20","R300", NULL
- };
- void parseReallyLowMHz(INI* ini)
- {
- Int mhz;
- INI::parseInt(ini,NULL,&mhz,NULL);
- if (TheGameLODManager)
- {
- TheGameLODManager->setReallyLowMHz(mhz);
- }
- }
- void INI::parseBenchProfile( INI* ini)
- {
- if( TheGameLODManager )
- {
- BenchProfile *preset = TheGameLODManager->newBenchProfile();
- if (preset)
- {
- INI::parseIndexList(ini,NULL,&preset->m_cpuType,CPUNames);
- INI::parseInt(ini,NULL,&preset->m_mhz,NULL);
- INI::parseReal(ini,NULL,&preset->m_intBenchIndex,NULL);
- INI::parseReal(ini,NULL,&preset->m_floatBenchIndex,NULL);
- INI::parseReal(ini,NULL,&preset->m_memBenchIndex,NULL);
- }
- }
- }
- /**Parse a description of all the LOD settings for a given detail level*/
- void INI::parseLODPreset( INI* ini )
- {
- const char *c;
- AsciiString name;
- // read the name
- c = ini->getNextToken();
- name.set( c ); //name of detail level - low, medium, high
- if( TheGameLODManager )
- {
- StaticGameLODLevel index = (StaticGameLODLevel)TheGameLODManager->getStaticGameLODIndex(name);
- if (index != STATIC_GAME_LOD_UNKNOWN)
- {
- LODPresetInfo *preset = TheGameLODManager->newLODPreset(index);
- if (preset)
- {
- INI::parseIndexList(ini,NULL,&preset->m_cpuType,CPUNames);
- INI::parseInt(ini,NULL,&preset->m_mhz,NULL);
- INI::parseIndexList(ini,NULL,&preset->m_videoType,VideoNames);
- INI::parseInt(ini,NULL,&preset->m_memory,NULL);
- }
- }
- }
- }
- GameLODManager::GameLODManager(void)
- {
- m_currentStaticLOD = STATIC_GAME_LOD_UNKNOWN;
- m_currentDynamicLOD = DYNAMIC_GAME_LOD_HIGH;
- m_numParticleGenerations=0;
- m_dynamicParticleSkipMask=0;
- m_numDebrisGenerations=0;
- m_dynamicDebrisSkipMask=0;
- m_videoPassed=false;
- m_cpuPassed=false;
- m_memPassed=false;
- m_slowDeathScale=1.0f;
- m_idealDetailLevel = STATIC_GAME_LOD_UNKNOWN;
- m_videoChipType = DC_MAX;
- m_cpuType = XX;
- m_numRAM=0;
- m_cpuFreq=0;
- m_intBenchIndex=0;
- m_floatBenchIndex=0;
- m_memBenchIndex=0;
- m_compositeBenchIndex=0;
- m_numBenchProfiles=0;
- m_currentTextureReduction=0;
- m_reallyLowMHz = 400;
-
- for (Int i=0; i<STATIC_GAME_LOD_CUSTOM; i++)
- m_numLevelPresets[i]=0;
- };
- GameLODManager::~GameLODManager()
- {
- }
- BenchProfile *GameLODManager::newBenchProfile(void)
- {
- if (m_numBenchProfiles < MAX_BENCH_PROFILES)
- {
- m_numBenchProfiles++;
- return &m_benchProfiles[m_numBenchProfiles-1];
- }
- DEBUG_CRASH(( "GameLODManager::newBenchProfile - Too many profiles defined\n"));
- return NULL;
- }
- LODPresetInfo *GameLODManager::newLODPreset(StaticGameLODLevel index)
- {
- if (m_numLevelPresets[index] < MAX_LOD_PRESETS_PER_LEVEL)
- {
- m_numLevelPresets[index]++;
- return &m_lodPresets[index][m_numLevelPresets[index]-1];
- }
- DEBUG_CRASH(( "GameLODManager::newLODPreset - Too many presets defined for '%s'\n", TheGameLODManager->getStaticGameLODLevelName(index)));
- return NULL;
- }
- void GameLODManager::init(void)
- {
- INI ini;
- //Get Presets for each LOD level.
- ini.load( AsciiString( "Data\\INI\\GameLOD.ini" ), INI_LOAD_OVERWRITE, NULL );
- //Get presets for each known hardware configuration
- ini.load( AsciiString( "Data\\INI\\GameLODPresets.ini"), INI_LOAD_OVERWRITE, NULL);
- //Get Presets for custom LOD level by pulling them out of initial globaldata (which should
- //have all settings already applied).
- refreshCustomStaticLODLevel();
- //Override with user preferences
- OptionPreferences optionPref;
- StaticGameLODLevel userSetDetail=(StaticGameLODLevel)optionPref.getStaticGameDetail();
- m_idealDetailLevel=(StaticGameLODLevel)optionPref.getIdealStaticGameDetail();
- //always get this data in case we need it later.
- testMinimumRequirements(NULL,&m_cpuType,&m_cpuFreq,&m_numRAM,NULL,NULL,NULL);
- if ((Real)(m_numRAM)/(Real)(256*1024*1024) >= PROFILE_ERROR_LIMIT)
- m_memPassed=TRUE; //check if they have at least 256 MB
- if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN || TheGlobalData->m_forceBenchmark)
- {
- if (m_cpuType == XX || TheGlobalData->m_forceBenchmark)
- {
- //need to run the benchmark
- testMinimumRequirements(NULL,NULL,NULL,NULL,&m_intBenchIndex,&m_floatBenchIndex,&m_memBenchIndex);
-
- if (TheGlobalData->m_forceBenchmark)
- { //we want to see the numbers. So dump them to a logfile.
- FILE *fp=fopen("Benchmark.txt","w");
- if (fp)
- {
- fprintf(fp,"BenchProfile = %s %d %f %f %f", CPUNames[m_cpuType], m_cpuFreq, m_intBenchIndex, m_floatBenchIndex, m_memBenchIndex);
- fclose(fp);
- }
- }
- m_compositeBenchIndex = m_intBenchIndex + m_floatBenchIndex; ///@todo: Need to scale these based on our apps usage of int/float/mem ops.
- StaticGameLODLevel currentLevel=STATIC_GAME_LOD_LOW;
- BenchProfile *prof=m_benchProfiles;
- m_cpuType = P3; //assume lowest spec.
- m_cpuFreq = 1000; //assume lowest spec.
- for (Int k=0; k<m_numBenchProfiles; k++)
- {
- //Check if we're within 5% of the performance of this cpu profile.
- if (m_intBenchIndex/prof->m_intBenchIndex >= PROFILE_ERROR_LIMIT && m_floatBenchIndex/prof->m_floatBenchIndex >= PROFILE_ERROR_LIMIT && m_memBenchIndex/prof->m_memBenchIndex >= PROFILE_ERROR_LIMIT)
- {
- for (Int i=STATIC_GAME_LOD_HIGH; i >= STATIC_GAME_LOD_LOW; i--)
- {
- LODPresetInfo *preset=&m_lodPresets[i][0]; //pointer to first preset at this LOD level.
- for (Int j=0; j<m_numLevelPresets[i]; j++)
- {
- if( prof->m_cpuType == preset->m_cpuType && ((Real)prof->m_mhz/(Real)preset->m_mhz >= PROFILE_ERROR_LIMIT))
- { currentLevel = (StaticGameLODLevel)i;
- m_cpuType = prof->m_cpuType;
- m_cpuFreq = prof->m_mhz;
- break;
- }
- preset++; //skip to next preset
- }
- if (currentLevel >= i)
- break; //we already found a higher level than the remaining presets so no need to keep searching.
- }
- }
- prof++;
- }
- } //finding equivalent CPU to unkown cpu.
- } //find data needed to determine m_idealDetailLevel
- if (userSetDetail == STATIC_GAME_LOD_CUSTOM)
- {
- TheWritableGlobalData->m_textureReductionFactor = optionPref.getTextureReduction();
- TheWritableGlobalData->m_useShadowVolumes = optionPref.get3DShadowsEnabled();
- TheWritableGlobalData->m_useShadowDecals = optionPref.get2DShadowsEnabled();
- TheWritableGlobalData->m_enableBehindBuildingMarkers = optionPref.getBuildingOcclusionEnabled();
- TheWritableGlobalData->m_maxParticleCount = optionPref.getParticleCap();
- TheWritableGlobalData->m_enableDynamicLOD = optionPref.getDynamicLODEnabled();
- TheWritableGlobalData->m_useFpsLimit = optionPref.getFPSLimitEnabled();
- TheWritableGlobalData->m_useLightMap = optionPref.getLightmapEnabled();
- TheWritableGlobalData->m_useCloudMap = optionPref.getCloudShadowsEnabled();
- TheWritableGlobalData->m_showSoftWaterEdge = optionPref.getSmoothWaterEnabled();
- TheWritableGlobalData->m_useHeatEffects = optionPref.getUseHeatEffects();
- TheWritableGlobalData->m_useDrawModuleLOD = optionPref.getExtraAnimationsDisabled();
- TheWritableGlobalData->m_useTreeSway = !TheWritableGlobalData->m_useDrawModuleLOD; //borrow same setting.
- TheWritableGlobalData->m_useTrees = optionPref.getTreesEnabled();
- }
- setStaticLODLevel(userSetDetail);
- }
- void GameLODManager::refreshCustomStaticLODLevel(void)
- {
- StaticGameLODInfo *lodInfo=&m_staticGameLODInfo[STATIC_GAME_LOD_CUSTOM];
- lodInfo->m_maxParticleCount=TheGlobalData->m_maxParticleCount;
- lodInfo->m_useShadowVolumes=TheGlobalData->m_useShadowVolumes;
- lodInfo->m_useShadowDecals=TheGlobalData->m_useShadowDecals;
- lodInfo->m_useCloudMap=TheGlobalData->m_useCloudMap;
- lodInfo->m_useLightMap=TheGlobalData->m_useLightMap;
- lodInfo->m_showSoftWaterEdge=TheGlobalData->m_showSoftWaterEdge;
- lodInfo->m_maxTankTrackEdges=TheGlobalData->m_maxTankTrackEdges;
- lodInfo->m_maxTankTrackOpaqueEdges=TheGlobalData->m_maxTankTrackOpaqueEdges;
- lodInfo->m_maxTankTrackFadeDelay=TheGlobalData->m_maxTankTrackFadeDelay;
- lodInfo->m_useBuildupScaffolds=!TheGlobalData->m_useDrawModuleLOD;
- lodInfo->m_useHeatEffects = TheGlobalData->m_useHeatEffects;
- lodInfo->m_useTreeSway=lodInfo->m_useBuildupScaffolds;// Borrow same setting. //TheGlobalData->m_useTreeSway;
- lodInfo->m_textureReduction=TheGlobalData->m_textureReductionFactor;
- lodInfo->m_useFpsLimit = TheGlobalData->m_useFpsLimit;
- lodInfo->m_enableDynamicLOD=TheGlobalData->m_enableDynamicLOD;
- lodInfo->m_useTrees = TheGlobalData->m_useTrees;
- }
- /**Convert LOD name to an index*/
- Int GameLODManager::getStaticGameLODIndex(AsciiString name)
- {
- for (Int i=0; i<STATIC_GAME_LOD_COUNT; ++i)
- {
- if (name.compareNoCase(StaticGameLODNames[i]) == 0)
- return i;
- }
- DEBUG_CRASH(( "GameLODManager::getGameLODIndex - Invalid LOD name '%s'\n", name.str() ));
- return STATIC_GAME_LOD_UNKNOWN;
- }
- /**Parse a description of all the LOD settings for a given detail level*/
- void INI::parseStaticGameLODDefinition( INI* ini )
- {
- const char *c;
- AsciiString name;
- // read the name
- c = ini->getNextToken();
- name.set( c );
- if( TheGameLODManager )
- {
- Int index = TheGameLODManager->getStaticGameLODIndex(name);
- if (index != STATIC_GAME_LOD_UNKNOWN)
- {
- StaticGameLODInfo *lodInfo = &(TheGameLODManager->m_staticGameLODInfo[index]);
- // parse the ini definition
- ini->initFromINI( lodInfo, TheStaticGameLODFieldParseTable );
- }
- }
- }
- /**Parse an LOD level*/
- void INI::parseStaticGameLODLevel( INI* ini, void * , void *store, const void*)
- {
- const char *tok=ini->getNextToken();
- for (Int i=0; i<STATIC_GAME_LOD_COUNT; i++)
- if( stricmp(tok, StaticGameLODNames[i]) == 0 )
- { *(StaticGameLODLevel*)store = (StaticGameLODLevel)i;
- return;
- }
- DEBUG_CRASH(("invalid GameLODLevel token %s -- expected LOW/MEDIUM/HIGH\n",tok));
- throw INI_INVALID_DATA;
- }
- const char *GameLODManager::getStaticGameLODLevelName(StaticGameLODLevel level)
- {
- return StaticGameLODNames[level];
- }
- /**Function which calculates the recommended LOD level for current hardware
- configuration.*/
- StaticGameLODLevel GameLODManager::findStaticLODLevel(void)
- {
- //Check if we have never done the test on current system
- if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN)
- {
- //search all our presets for matching hardware
- m_idealDetailLevel = STATIC_GAME_LOD_LOW;
- //get system configuration - only need vide chip type, got rest in ::init().
- testMinimumRequirements(&m_videoChipType,NULL,NULL,NULL,NULL,NULL,NULL);
- if (m_videoChipType == DC_UNKNOWN)
- m_videoChipType = DC_TNT2; //presume it's at least TNT2 level
- Int numMBRam=m_numRAM/(1024*1024);
- for (Int i=STATIC_GAME_LOD_HIGH; i >= STATIC_GAME_LOD_LOW; i--)
- {
- LODPresetInfo *preset=&m_lodPresets[i][0]; //pointer to first preset at this LOD level.
- for (Int j=0; j<m_numLevelPresets[i]; j++)
- {
- if( m_cpuType == preset->m_cpuType &&
- ((Real)m_cpuFreq/(Real)preset->m_mhz >= PROFILE_ERROR_LIMIT) &&//make sure we're within 5% or higher
- m_videoChipType >= preset->m_videoType &&
- ((Real)numMBRam/(Real)preset->m_memory >= PROFILE_ERROR_LIMIT)
- )
- { m_idealDetailLevel = (StaticGameLODLevel)i;
- break;
- }
- preset++; //skip to next preset
- }
- if (m_idealDetailLevel >= i)
- break; //we already found a higher level than the remaining presets so no need to keep searching.
- }
- //Save ideal detail level for future usage
- OptionPreferences optionPref;
- optionPref["IdealStaticGameLOD"] = getStaticGameLODLevelName(m_idealDetailLevel);
- if (getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN) //save for future usage.
- optionPref["StaticGameLOD"] = getStaticGameLODLevelName(m_idealDetailLevel);
- optionPref.write();
- }
- return m_idealDetailLevel;
- }
- /**Set all game systems to match the desired LOD level.*/
- Bool GameLODManager::setStaticLODLevel(StaticGameLODLevel level)
- {
- if (!TheGlobalData->m_enableStaticLOD)
- { m_currentStaticLOD = STATIC_GAME_LOD_CUSTOM;
- return FALSE;
- }
- if (level == STATIC_GAME_LOD_UNKNOWN || (level != STATIC_GAME_LOD_CUSTOM && m_currentStaticLOD == level))
- return FALSE; //level is already applied. Custom levels are always applied since random options could change.
- applyStaticLODLevel(level);
- m_currentStaticLOD = level;
- return TRUE;
- }
- void GameLODManager::applyStaticLODLevel(StaticGameLODLevel level)
- {
- ///@todo: Still need to implement these settings:
- // m_sampleCount2D=6;
- // m_sampleCount3D=24;
- // m_streamCount=2;
- // m_useEmissiveNightMaterials=TRUE;
- //save previous info for this level since it may be overwritten by refreshCustomStaticLODLevel().
- StaticGameLODInfo prevLodBackup;
- if (m_currentStaticLOD != STATIC_GAME_LOD_UNKNOWN)
- prevLodBackup=m_staticGameLODInfo[m_currentStaticLOD];
- if (level == STATIC_GAME_LOD_CUSTOM)
- refreshCustomStaticLODLevel(); //store current settings into custom preset
- StaticGameLODInfo *lodInfo=&m_staticGameLODInfo[level];
- StaticGameLODInfo *prevLodInfo=&prevLodBackup;
- Int requestedTextureReduction = 0;
- Bool requestedTrees = m_memPassed; //only use trees if memory requirement passed.
- if (level == STATIC_GAME_LOD_CUSTOM)
- { requestedTextureReduction = lodInfo->m_textureReduction;
- requestedTrees = lodInfo->m_useTrees;
- }
- else
- if (level >= STATIC_GAME_LOD_LOW)
- { //normal non-custom level gets texture reduction based on recommendation
- requestedTextureReduction = getRecommendedTextureReduction();
- }
- if (TheGlobalData)
- {
- TheWritableGlobalData->m_maxParticleCount=lodInfo->m_maxParticleCount;
- TheWritableGlobalData->m_useShadowVolumes=lodInfo->m_useShadowVolumes;
- TheWritableGlobalData->m_useShadowDecals=lodInfo->m_useShadowDecals;
- //Check if texture resolution changed. No need to apply when current is unknown because display will do it
- if (requestedTextureReduction != m_currentTextureReduction)
- {
- TheWritableGlobalData->m_textureReductionFactor = requestedTextureReduction;
- if (TheGameClient)
- TheGameClient->adjustLOD(0); //apply the new setting stored in globaldata
- }
- //Check if shadow state changed
- if (m_currentStaticLOD == STATIC_GAME_LOD_UNKNOWN ||
- lodInfo->m_useShadowVolumes != prevLodInfo->m_useShadowVolumes ||
- lodInfo->m_useShadowDecals != prevLodInfo->m_useShadowDecals)
- {
- if (TheGameClient)
- {
- TheGameClient->releaseShadows(); //free all shadows
- TheGameClient->allocateShadows(); //allocate those shadows that are enabled.
- }
- }
- TheWritableGlobalData->m_useCloudMap=lodInfo->m_useCloudMap;
- TheWritableGlobalData->m_useLightMap=lodInfo->m_useLightMap;
- TheWritableGlobalData->m_showSoftWaterEdge=lodInfo->m_showSoftWaterEdge;
- //Check if shoreline blending mode has changed
- if (m_currentStaticLOD == STATIC_GAME_LOD_UNKNOWN || lodInfo->m_showSoftWaterEdge != prevLodInfo->m_showSoftWaterEdge)
- {
- if (TheTerrainVisual)
- TheTerrainVisual->setShoreLineDetail();
- }
- TheWritableGlobalData->m_maxTankTrackEdges=lodInfo->m_maxTankTrackEdges;
- TheWritableGlobalData->m_maxTankTrackOpaqueEdges=lodInfo->m_maxTankTrackOpaqueEdges;
- TheWritableGlobalData->m_maxTankTrackFadeDelay=lodInfo->m_maxTankTrackFadeDelay;
- TheWritableGlobalData->m_useTreeSway=lodInfo->m_useTreeSway;
- TheWritableGlobalData->m_useDrawModuleLOD=!lodInfo->m_useBuildupScaffolds;
- TheWritableGlobalData->m_useHeatEffects=lodInfo->m_useHeatEffects;
- TheWritableGlobalData->m_enableDynamicLOD = lodInfo->m_enableDynamicLOD;
- TheWritableGlobalData->m_useFpsLimit = lodInfo->m_useFpsLimit;
- TheWritableGlobalData->m_useTrees = requestedTrees;
- }
- if (!m_memPassed || isReallyLowMHz()) {
- TheWritableGlobalData->m_shellMapOn = false;
- }
- if (TheTerrainVisual)
- TheTerrainVisual->setTerrainTracksDetail();
- }
- /**Parse a description of all the LOD settings for a given detail level*/
- void INI::parseDynamicGameLODDefinition( INI* ini )
- {
- const char *c;
- AsciiString name;
- // read the name
- c = ini->getNextToken();
- name.set( c );
- if( TheGameLODManager )
- {
- Int index = TheGameLODManager->getDynamicGameLODIndex(name);
- if (index != DYNAMIC_GAME_LOD_UNKNOWN)
- {
- DynamicGameLODInfo *lodInfo = &(TheGameLODManager->m_dynamicGameLODInfo[index]);
- // parse the ini weapon definition
- ini->initFromINI( lodInfo, TheDynamicGameLODFieldParseTable );
- }
- }
- }
- /**Parse an LOD level*/
- void INI::parseDynamicGameLODLevel( INI* ini, void * , void *store, const void*)
- {
- const char *tok=ini->getNextToken();
- for (Int i=0; i<DYNAMIC_GAME_LOD_COUNT; i++)
- if( stricmp(tok, DynamicGameLODNames[i]) == 0 )
- { *(DynamicGameLODLevel*)store = (DynamicGameLODLevel)i;
- return;
- }
- DEBUG_CRASH(("invalid GameLODLevel token %s -- expected LOW/MEDIUM/HIGH\n",tok));
- throw INI_INVALID_DATA;
- }
- /**Convert LOD name to an index*/
- Int GameLODManager::getDynamicGameLODIndex(AsciiString name)
- {
- for (Int i=0; i<DYNAMIC_GAME_LOD_COUNT; ++i)
- {
- if (name.compareNoCase(DynamicGameLODNames[i]) == 0)
- return i;
- }
- DEBUG_CRASH(( "GameLODManager::getGameLODIndex - Invalid LOD name '%s'\n", name.str() ));
- return STATIC_GAME_LOD_UNKNOWN;
- }
- const char *GameLODManager::getDynamicGameLODLevelName(DynamicGameLODLevel level)
- {
- return DynamicGameLODNames[level];
- }
- /**Given an average fps, return the optimal dynamic LOD level that matches this fps.*/
- DynamicGameLODLevel GameLODManager::findDynamicLODLevel(Real averageFPS)
- {
- Int ifps=(Int)(averageFPS); //convert to integer.
- for (Int i=DYNAMIC_GAME_LOD_VERY_HIGH; i>=DYNAMIC_GAME_LOD_LOW; i--)
- { //check which of the LOD levels matches our fps
- if (m_dynamicGameLODInfo[i].m_minFPS < ifps)
- return (DynamicGameLODLevel)i;
- }
- return DYNAMIC_GAME_LOD_LOW; //none of the low levels were slow enough so pick the lowest.
- }
- /**Set all game systems to match the desired LOD level.*/
- Bool GameLODManager::setDynamicLODLevel(DynamicGameLODLevel level)
- {
- if (level == DYNAMIC_GAME_LOD_UNKNOWN || m_currentDynamicLOD == level)
- return FALSE;
- m_currentDynamicLOD = level;
- applyDynamicLODLevel(level);
- return TRUE;
- }
- void GameLODManager::applyDynamicLODLevel(DynamicGameLODLevel level)
- {
- m_numParticleGenerations=0;
- m_dynamicParticleSkipMask=m_dynamicGameLODInfo[level].m_dynamicParticleSkipMask;
- m_numDebrisGenerations=0;
- m_dynamicDebrisSkipMask=m_dynamicGameLODInfo[level].m_dynamicDebrisSkipMask;
- m_slowDeathScale=m_dynamicGameLODInfo[level].m_slowDeathScale;
- m_minDynamicParticlePriority=m_dynamicGameLODInfo[level].m_minDynamicParticlePriority;
- m_minDynamicParticleSkipPriority=m_dynamicGameLODInfo[level].m_minDynamicParticleSkipPriority;
- }
- Int GameLODManager::getRecommendedTextureReduction(void)
- {
- if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN)
- findStaticLODLevel(); //it was never tested, so test now.
- if (!m_memPassed) //if they have < 256 MB, force them to low res textures.
- return m_staticGameLODInfo[STATIC_GAME_LOD_LOW].m_textureReduction;
- return m_staticGameLODInfo[m_idealDetailLevel].m_textureReduction;
- }
- Int GameLODManager::getLevelTextureReduction(StaticGameLODLevel level)
- {
- return m_staticGameLODInfo[level].m_textureReduction;
- }
- Bool GameLODManager::didMemPass( void )
- {
- return m_memPassed;
- }
|