/*
** 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 .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (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; igetStaticGameLODLevelName(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; km_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; jm_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; igetNextToken();
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_LOW; i--)
{
LODPresetInfo *preset=&m_lodPresets[i][0]; //pointer to first preset at this LOD level.
for (Int j=0; jm_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_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;
}