materialManager.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "materials/materialManager.h"
  24. #include "materials/matInstance.h"
  25. #include "materials/materialFeatureTypes.h"
  26. #include "lighting/lightManager.h"
  27. #include "core/util/safeDelete.h"
  28. #include "shaderGen/shaderGen.h"
  29. #include "core/module.h"
  30. #include "console/consoleTypes.h"
  31. #include "console/engineAPI.h"
  32. #include "gui/controls/guiTreeViewCtrl.h"
  33. MODULE_BEGIN( MaterialManager )
  34. MODULE_INIT_BEFORE( GFX )
  35. MODULE_SHUTDOWN_BEFORE( GFX )
  36. MODULE_INIT
  37. {
  38. MaterialManager::createSingleton();
  39. }
  40. MODULE_SHUTDOWN
  41. {
  42. MaterialManager::deleteSingleton();
  43. }
  44. MODULE_END;
  45. MaterialManager::MaterialManager()
  46. {
  47. VECTOR_SET_ASSOCIATION( mMatInstanceList );
  48. mDt = 0.0f;
  49. mAccumTime = 0.0f;
  50. mLastTime = 0;
  51. mDampness = 0.0f;
  52. mWarningInst = NULL;
  53. mMatDefToFlush = NULL;
  54. mMatDefToReload = NULL;
  55. GFXDevice::getDeviceEventSignal().notify( this, &MaterialManager::_handleGFXEvent );
  56. // Make sure we get activation signals
  57. // and that we're the last to get them.
  58. LightManager::smActivateSignal.notify( this, &MaterialManager::_onLMActivate, 9999 );
  59. mMaterialSet = NULL;
  60. mUsingDeferred = false;
  61. mFlushAndReInit = false;
  62. mMatDefShouldFlush = false;
  63. mMatDefShouldReload = false;
  64. mDefaultAnisotropy = 1;
  65. Con::addVariable( "$pref::Video::defaultAnisotropy", TypeS32, &mDefaultAnisotropy,
  66. "@brief Global variable defining the default anisotropy value.\n\n"
  67. "Controls the default anisotropic texture filtering level for all materials, including the terrain. "
  68. "This value can be changed at runtime to see its affect without reloading.\n\n "
  69. "@ingroup Materials");
  70. Con::NotifyDelegate callabck( this, &MaterialManager::_updateDefaultAnisotropy );
  71. Con::addVariableNotify( "$pref::Video::defaultAnisotropy", callabck );
  72. Con::NotifyDelegate callabck2( this, &MaterialManager::_onDisableMaterialFeature );
  73. Con::setVariable( "$pref::Video::disableNormalMapping", "false" );
  74. Con::addVariableNotify( "$pref::Video::disableNormalMapping", callabck2 );
  75. Con::setVariable( "$pref::Video::disableCubemapping", "false" );
  76. Con::addVariableNotify( "$pref::Video::disableCubemapping", callabck2 );
  77. Con::setVariable( "$pref::Video::enableParallaxMapping", "true" );
  78. Con::addVariableNotify( "$pref::Video::enableParallaxMapping", callabck2 );
  79. }
  80. MaterialManager::~MaterialManager()
  81. {
  82. GFXDevice::getDeviceEventSignal().remove( this, &MaterialManager::_handleGFXEvent );
  83. LightManager::smActivateSignal.remove( this, &MaterialManager::_onLMActivate );
  84. SAFE_DELETE( mWarningInst );
  85. #ifndef TORQUE_SHIPPING
  86. DebugMaterialMap::Iterator itr = mMeshDebugMaterialInsts.begin();
  87. for ( ; itr != mMeshDebugMaterialInsts.end(); itr++ )
  88. delete itr->value;
  89. #endif
  90. }
  91. void MaterialManager::_onLMActivate( const char *lm, bool activate )
  92. {
  93. if ( !activate )
  94. return;
  95. // Since the light manager usually swaps shadergen features
  96. // and changes system wide shader defines we need to completely
  97. // flush and rebuild all the material instances.
  98. mFlushAndReInit = true;
  99. }
  100. void MaterialManager::_updateDefaultAnisotropy()
  101. {
  102. // Update all the materials.
  103. Vector<BaseMatInstance*>::iterator iter = mMatInstanceList.begin();
  104. for ( ; iter != mMatInstanceList.end(); iter++ )
  105. (*iter)->updateStateBlocks();
  106. }
  107. Material * MaterialManager::allocateAndRegister(const String &objectName, const String &mapToName)
  108. {
  109. Material *newMat = new Material();
  110. if ( mapToName.isNotEmpty() )
  111. newMat->mMapTo = mapToName;
  112. bool registered = newMat->registerObject(objectName );
  113. AssertFatal( registered, "Unable to register material" );
  114. if (registered)
  115. Sim::getRootGroup()->addObject( newMat );
  116. else
  117. {
  118. delete newMat;
  119. newMat = NULL;
  120. }
  121. return newMat;
  122. }
  123. Material * MaterialManager::getMaterialDefinitionByName(const String &matName)
  124. {
  125. // Get the material
  126. Material * foundMat;
  127. if(!Sim::findObject(matName, foundMat))
  128. {
  129. Con::errorf("MaterialManager: Unable to find material '%s'", matName.c_str());
  130. return NULL;
  131. }
  132. return foundMat;
  133. }
  134. Material* MaterialManager::getMaterialDefinitionByMapTo(const String& mapTo)
  135. {
  136. // Get the material
  137. Material* foundMat = nullptr;
  138. for (SimSet::iterator itr = mMaterialSet->begin(); itr != mMaterialSet->end(); ++itr)
  139. {
  140. // Fetch our listed materials.
  141. Material* materialDef = dynamic_cast<Material*>(*itr);
  142. if (materialDef && materialDef->mMapTo.compare(mapTo, 0U, String::NoCase) == 0)
  143. {
  144. //We have a match, so keep it and bail the loop
  145. foundMat = materialDef;
  146. break;
  147. }
  148. }
  149. return foundMat;
  150. }
  151. BaseMatInstance* MaterialManager::createMatInstance(const String &matName)
  152. {
  153. BaseMaterialDefinition* mat = NULL;
  154. if (Sim::findObject(matName, mat))
  155. return mat->createMatInstance();
  156. return NULL;
  157. }
  158. BaseMatInstance* MaterialManager::createMatInstance( const String &matName,
  159. const GFXVertexFormat *vertexFormat )
  160. {
  161. return createMatInstance( matName, getDefaultFeatures(), vertexFormat );
  162. }
  163. BaseMatInstance* MaterialManager::createMatInstance( const String &matName,
  164. const FeatureSet& features,
  165. const GFXVertexFormat *vertexFormat )
  166. {
  167. BaseMatInstance* mat = createMatInstance(matName);
  168. if (mat)
  169. {
  170. mat->init( features, vertexFormat );
  171. return mat;
  172. }
  173. return NULL;
  174. }
  175. BaseMatInstance * MaterialManager::createWarningMatInstance()
  176. {
  177. Material *warnMat = static_cast<Material*>(Sim::findObject("WarningMaterial"));
  178. BaseMatInstance *warnMatInstance = NULL;
  179. if( warnMat != NULL )
  180. {
  181. warnMatInstance = warnMat->createMatInstance();
  182. GFXStateBlockDesc desc;
  183. desc.setCullMode(GFXCullNone);
  184. warnMatInstance->addStateBlockDesc(desc);
  185. warnMatInstance->init( getDefaultFeatures(),
  186. getGFXVertexFormat<GFXVertexPNTTB>() );
  187. }
  188. else
  189. Con::errorf("WarningMaterial Not Found!");
  190. return warnMatInstance;
  191. }
  192. // Gets the global warning material instance, callers should not free this copy
  193. BaseMatInstance * MaterialManager::getWarningMatInstance()
  194. {
  195. if (!mWarningInst)
  196. mWarningInst = createWarningMatInstance();
  197. return mWarningInst;
  198. }
  199. #ifndef TORQUE_SHIPPING
  200. BaseMatInstance * MaterialManager::createMeshDebugMatInstance(const LinearColorF &meshColor)
  201. {
  202. String meshDebugStr = String::ToString( "Torque_MeshDebug_%d", meshColor.getRGBAPack() );
  203. Material *debugMat;
  204. if (!Sim::findObject(meshDebugStr,debugMat))
  205. {
  206. debugMat = allocateAndRegister( meshDebugStr );
  207. debugMat->mDiffuse[0] = meshColor;
  208. debugMat->mReceiveShadows[0] = false;
  209. }
  210. BaseMatInstance *debugMatInstance = NULL;
  211. if( debugMat != NULL )
  212. {
  213. debugMatInstance = debugMat->createMatInstance();
  214. GFXStateBlockDesc desc;
  215. desc.setCullMode(GFXCullNone);
  216. desc.fillMode = GFXFillWireframe;
  217. debugMatInstance->addStateBlockDesc(desc);
  218. // Disable fog and other stuff.
  219. FeatureSet debugFeatures;
  220. debugFeatures.addFeature( MFT_DiffuseColor );
  221. debugMatInstance->init( debugFeatures, getGFXVertexFormat<GFXVertexPCN>() );
  222. }
  223. return debugMatInstance;
  224. }
  225. // Gets the global material instance for a given color, callers should not free this copy
  226. BaseMatInstance *MaterialManager::getMeshDebugMatInstance(const LinearColorF &meshColor)
  227. {
  228. DebugMaterialMap::Iterator itr = mMeshDebugMaterialInsts.find( meshColor.getRGBAPack() );
  229. BaseMatInstance *inst = NULL;
  230. if ( itr == mMeshDebugMaterialInsts.end() )
  231. inst = createMeshDebugMatInstance( meshColor );
  232. else
  233. inst = itr->value;
  234. mMeshDebugMaterialInsts.insert( meshColor.getRGBAPack(), inst );
  235. return inst;
  236. }
  237. #endif
  238. void MaterialManager::mapMaterial(const String & textureName, const String & materialName)
  239. {
  240. String currentMapEntry = getMapEntry(textureName);
  241. if (currentMapEntry.isNotEmpty())
  242. {
  243. if (!textureName.equal("unmapped_mat", String::NoCase))
  244. {
  245. SimObject* originalMat;
  246. SimObject* newMat;
  247. if (Sim::findObject(currentMapEntry, originalMat) && Sim::findObject(materialName, newMat))
  248. Con::warnf(ConsoleLogEntry::General, "Warning, overwriting material for: \"%s\" in %s by %s", textureName.c_str(), originalMat->getFilename(), newMat->getFilename());
  249. else
  250. Con::warnf(ConsoleLogEntry::General, "Warning, overwriting material for: %s", textureName.c_str());
  251. }
  252. }
  253. mMaterialMap[String::ToLower(textureName)] = materialName;
  254. }
  255. String MaterialManager::getMapEntry(const String & textureName) const
  256. {
  257. MaterialMap::ConstIterator iter = mMaterialMap.find(String::ToLower(textureName));
  258. if ( iter == mMaterialMap.end() )
  259. return String();
  260. return iter->value;
  261. }
  262. void MaterialManager::flushAndReInitInstances()
  263. {
  264. // delay flushes and reinits until the start of the next frame.
  265. mFlushAndReInit = true;
  266. }
  267. void MaterialManager::_flushAndReInitInstances()
  268. {
  269. // Clear the flag if its set.
  270. mFlushAndReInit = false;
  271. // Check to see if any shader preferences have changed.
  272. recalcFeaturesFromPrefs();
  273. // First we flush all the shader gen shaders which will
  274. // invalidate all GFXShader* to them.
  275. SHADERGEN->flushProceduralShaders();
  276. mFlushSignal.trigger();
  277. // First do a pass deleting all hooks as they can contain
  278. // materials themselves. This means we have to restart the
  279. // loop every time we delete any hooks... lame.
  280. Vector<BaseMatInstance*>::iterator iter = mMatInstanceList.begin();
  281. while ( iter != mMatInstanceList.end() )
  282. {
  283. if ( (*iter)->deleteAllHooks() != 0 )
  284. {
  285. // Restart the loop.
  286. iter = mMatInstanceList.begin();
  287. continue;
  288. }
  289. iter++;
  290. }
  291. // Now do a pass re-initializing materials.
  292. iter = mMatInstanceList.begin();
  293. for ( ; iter != mMatInstanceList.end(); iter++ )
  294. (*iter)->reInit();
  295. }
  296. // Used in the materialEditor. This flushes the material preview object so it can be reloaded easily.
  297. void MaterialManager::flushInstance(BaseMaterialDefinition* target)
  298. {
  299. mMatDefToFlush = target;
  300. mMatDefShouldFlush = true;
  301. }
  302. void MaterialManager::_flushInstance( BaseMaterialDefinition *target )
  303. {
  304. mMatDefShouldFlush = false;
  305. Vector<BaseMatInstance*>::iterator iter = mMatInstanceList.begin();
  306. while ( iter != mMatInstanceList.end() )
  307. {
  308. if ( (*iter)->getMaterial() == target )
  309. {
  310. (*iter)->deleteAllHooks();
  311. return;
  312. }
  313. iter++;
  314. }
  315. mMatDefToFlush = NULL;
  316. }
  317. void MaterialManager::reInitInstance(BaseMaterialDefinition* target)
  318. {
  319. mMatDefToReload = target;
  320. mMatDefShouldReload = true;
  321. }
  322. void MaterialManager::_reInitInstance( BaseMaterialDefinition *target )
  323. {
  324. mMatDefShouldReload = false;
  325. Vector<BaseMatInstance*>::iterator iter = mMatInstanceList.begin();
  326. for ( ; iter != mMatInstanceList.end(); iter++ )
  327. {
  328. if ( (*iter)->getMaterial() == target )
  329. (*iter)->reInit();
  330. }
  331. mMatDefToReload = NULL;
  332. }
  333. void MaterialManager::updateTime()
  334. {
  335. U32 curTime = Sim::getCurrentTime();
  336. if(curTime > mLastTime)
  337. {
  338. mDt = (curTime - mLastTime) / 1000.0f;
  339. mLastTime = curTime;
  340. mAccumTime += mDt;
  341. }
  342. else
  343. mDt = 0.0f;
  344. }
  345. SimSet * MaterialManager::getMaterialSet()
  346. {
  347. if(!mMaterialSet)
  348. mMaterialSet = static_cast<SimSet*>( Sim::findObject( "MaterialSet" ) );
  349. AssertFatal( mMaterialSet, "MaterialSet not found" );
  350. return mMaterialSet;
  351. }
  352. void MaterialManager::dumpMaterialInstances( BaseMaterialDefinition *target ) const
  353. {
  354. if ( !mMatInstanceList.size() )
  355. return;
  356. if ( target )
  357. Con::printf( "--------------------- %s MatInstances ---------------------", target->getName() );
  358. else
  359. Con::printf( "--------------------- MatInstances %d ---------------------", mMatInstanceList.size() );
  360. for( U32 i=0; i<mMatInstanceList.size(); i++ )
  361. {
  362. BaseMatInstance *inst = mMatInstanceList[i];
  363. if ( target && inst->getMaterial() != target )
  364. continue;
  365. inst->dumpShaderInfo();
  366. Con::printf( "" );
  367. }
  368. Con::printf( "---------------------- Dump complete ----------------------");
  369. }
  370. void MaterialManager::getMaterialInstances(BaseMaterialDefinition* target, GuiTreeViewCtrl* materailInstanceTree)
  371. {
  372. if (!mMatInstanceList.size())
  373. return;
  374. if (!target)
  375. {
  376. Con::errorf("Can't form a list without a specific MaterialDefinition");
  377. return;
  378. }
  379. if (!materailInstanceTree)
  380. {
  381. Con::errorf("Requires a valid GuiTreeViewCtrl object to populate data into!");
  382. return;
  383. }
  384. U32 matItem = materailInstanceTree->insertItem(0, target->getName());
  385. for (U32 i = 0; i < mMatInstanceList.size(); i++)
  386. {
  387. BaseMatInstance* inst = mMatInstanceList[i];
  388. if (target && inst->getMaterial() != target)
  389. continue;
  390. inst->getShaderInfo(materailInstanceTree, matItem);
  391. }
  392. }
  393. void MaterialManager::_track( MatInstance *matInstance )
  394. {
  395. mMatInstanceList.push_back( matInstance );
  396. }
  397. void MaterialManager::_untrack( MatInstance *matInstance )
  398. {
  399. mMatInstanceList.remove( matInstance );
  400. }
  401. void MaterialManager::recalcFeaturesFromPrefs()
  402. {
  403. mDefaultFeatures.clear();
  404. FeatureType::addDefaultTypes( &mDefaultFeatures );
  405. mExclusionFeatures.setFeature( MFT_NormalMap,
  406. Con::getBoolVariable( "$pref::Video::disableNormalMapping", false ) );
  407. mExclusionFeatures.setFeature( MFT_CubeMap,
  408. Con::getBoolVariable( "$pref::Video::disableCubemapping", false ) );
  409. mExclusionFeatures.setFeature( MFT_Parallax,
  410. !Con::getBoolVariable( "$pref::Video::enableParallaxMapping", true ) );
  411. }
  412. bool MaterialManager::_handleGFXEvent( GFXDevice::GFXDeviceEventType event_ )
  413. {
  414. switch ( event_ )
  415. {
  416. case GFXDevice::deInit:
  417. recalcFeaturesFromPrefs();
  418. break;
  419. case GFXDevice::deDestroy :
  420. SAFE_DELETE( mWarningInst );
  421. break;
  422. case GFXDevice::deStartOfFrame:
  423. if ( mFlushAndReInit )
  424. _flushAndReInitInstances();
  425. if (mMatDefShouldFlush)
  426. {
  427. _flushInstance(mMatDefToFlush);
  428. }
  429. if (mMatDefShouldReload)
  430. {
  431. _reInitInstance(mMatDefToReload);
  432. }
  433. break;
  434. default:
  435. break;
  436. }
  437. return true;
  438. }
  439. DefineEngineFunction( reInitMaterials, void, (),,
  440. "@brief Flushes all procedural shaders and re-initializes all active material instances.\n\n"
  441. "@ingroup Materials")
  442. {
  443. MATMGR->flushAndReInitInstances();
  444. }
  445. DefineEngineFunction( addMaterialMapping, void, (const char * texName, const char * matName), , "(string texName, string matName)\n"
  446. "@brief Maps the given texture to the given material.\n\n"
  447. "Generates a console warning before overwriting.\n\n"
  448. "Material maps are used by terrain and interiors for triggering "
  449. "effects when an object moves onto a terrain "
  450. "block or interior surface using the associated texture.\n\n"
  451. "@ingroup Materials")
  452. {
  453. MATMGR->mapMaterial(texName, matName);
  454. }
  455. DefineEngineFunction( getMaterialMapping, const char*, (const char * texName), , "(string texName)\n"
  456. "@brief Returns the name of the material mapped to this texture.\n\n"
  457. "If no materials are found, an empty string is returned.\n\n"
  458. "@param texName Name of the texture\n\n"
  459. "@ingroup Materials")
  460. {
  461. return MATMGR->getMapEntry(texName).c_str();
  462. }
  463. DefineEngineFunction( dumpMaterialInstances, void, (), ,
  464. "@brief Dumps a formatted list of currently allocated material instances to the console.\n\n"
  465. "@ingroup Materials")
  466. {
  467. MATMGR->dumpMaterialInstances();
  468. }
  469. DefineEngineFunction(getMaterialInstances, void, (BaseMaterialDefinition* target, GuiTreeViewCtrl* tree), (nullAsType<BaseMaterialDefinition*>(), nullAsType<GuiTreeViewCtrl*>()),
  470. "@brief Dumps a formatted list of currently allocated material instances to the console.\n\n"
  471. "@ingroup Materials")
  472. {
  473. if (target == nullptr || tree == nullptr)
  474. return;
  475. MATMGR->getMaterialInstances(target, tree);
  476. }
  477. DefineEngineFunction( getMapEntry, const char*, (const char * texName), ,
  478. "@hide")
  479. {
  480. return MATMGR->getMapEntry( String(texName) );
  481. }