2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "platform/platform.h"
# include "materials/materialManager.h"
# include "materials/matInstance.h"
# include "materials/materialFeatureTypes.h"
# include "lighting/lightManager.h"
# include "core/util/safeDelete.h"
# include "shaderGen/shaderGen.h"
# include "core/module.h"
# include "console/consoleTypes.h"
2014-11-04 03:42:51 +00:00
# include "console/engineAPI.h"
2012-09-19 15:15:01 +00:00
2019-05-13 05:28:23 +00:00
# include "gui/controls/guiTreeViewCtrl.h"
2012-09-19 15:15:01 +00:00
MODULE_BEGIN ( MaterialManager )
MODULE_INIT_BEFORE ( GFX )
MODULE_SHUTDOWN_BEFORE ( GFX )
MODULE_INIT
{
MaterialManager : : createSingleton ( ) ;
}
MODULE_SHUTDOWN
{
MaterialManager : : deleteSingleton ( ) ;
}
MODULE_END ;
MaterialManager : : MaterialManager ( )
{
VECTOR_SET_ASSOCIATION ( mMatInstanceList ) ;
mDt = 0.0f ;
mAccumTime = 0.0f ;
2022-11-22 03:12:23 +00:00
mLastTime = 0 ;
mDampness = 0.0f ;
2012-09-19 15:15:01 +00:00
mWarningInst = NULL ;
GFXDevice : : getDeviceEventSignal ( ) . notify ( this , & MaterialManager : : _handleGFXEvent ) ;
// Make sure we get activation signals
// and that we're the last to get them.
LightManager : : smActivateSignal . notify ( this , & MaterialManager : : _onLMActivate , 9999 ) ;
mMaterialSet = NULL ;
2017-04-11 05:23:14 +00:00
mUsingDeferred = false ;
2012-09-19 15:15:01 +00:00
mFlushAndReInit = false ;
mDefaultAnisotropy = 1 ;
Con : : addVariable ( " $pref::Video::defaultAnisotropy " , TypeS32 , & mDefaultAnisotropy ,
" @brief Global variable defining the default anisotropy value. \n \n "
" Controls the default anisotropic texture filtering level for all materials, including the terrain. "
" This value can be changed at runtime to see its affect without reloading. \n \n "
" @ingroup Materials " ) ;
Con : : NotifyDelegate callabck ( this , & MaterialManager : : _updateDefaultAnisotropy ) ;
Con : : addVariableNotify ( " $pref::Video::defaultAnisotropy " , callabck ) ;
Con : : NotifyDelegate callabck2 ( this , & MaterialManager : : _onDisableMaterialFeature ) ;
2014-11-08 16:41:17 +00:00
Con : : setVariable ( " $pref::Video::disableNormalMapping " , " false " ) ;
2012-09-19 15:15:01 +00:00
Con : : addVariableNotify ( " $pref::Video::disableNormalMapping " , callabck2 ) ;
2014-11-08 16:41:17 +00:00
Con : : setVariable ( " $pref::Video::disableCubemapping " , " false " ) ;
2012-09-19 15:15:01 +00:00
Con : : addVariableNotify ( " $pref::Video::disableCubemapping " , callabck2 ) ;
2022-08-30 06:29:39 +00:00
Con : : setVariable ( " $pref::Video::enableParallaxMapping " , " true " ) ;
Con : : addVariableNotify ( " $pref::Video::enableParallaxMapping " , callabck2 ) ;
2012-09-19 15:15:01 +00:00
}
MaterialManager : : ~ MaterialManager ( )
{
GFXDevice : : getDeviceEventSignal ( ) . remove ( this , & MaterialManager : : _handleGFXEvent ) ;
LightManager : : smActivateSignal . remove ( this , & MaterialManager : : _onLMActivate ) ;
SAFE_DELETE ( mWarningInst ) ;
# ifndef TORQUE_SHIPPING
DebugMaterialMap : : Iterator itr = mMeshDebugMaterialInsts . begin ( ) ;
for ( ; itr ! = mMeshDebugMaterialInsts . end ( ) ; itr + + )
delete itr - > value ;
# endif
}
void MaterialManager : : _onLMActivate ( const char * lm , bool activate )
{
if ( ! activate )
return ;
// Since the light manager usually swaps shadergen features
// and changes system wide shader defines we need to completely
// flush and rebuild all the material instances.
mFlushAndReInit = true ;
}
void MaterialManager : : _updateDefaultAnisotropy ( )
{
// Update all the materials.
Vector < BaseMatInstance * > : : iterator iter = mMatInstanceList . begin ( ) ;
for ( ; iter ! = mMatInstanceList . end ( ) ; iter + + )
( * iter ) - > updateStateBlocks ( ) ;
}
Material * MaterialManager : : allocateAndRegister ( const String & objectName , const String & mapToName )
{
Material * newMat = new Material ( ) ;
if ( mapToName . isNotEmpty ( ) )
newMat - > mMapTo = mapToName ;
bool registered = newMat - > registerObject ( objectName ) ;
AssertFatal ( registered , " Unable to register material " ) ;
if ( registered )
Sim : : getRootGroup ( ) - > addObject ( newMat ) ;
else
{
delete newMat ;
newMat = NULL ;
}
return newMat ;
}
Material * MaterialManager : : getMaterialDefinitionByName ( const String & matName )
{
// Get the material
Material * foundMat ;
if ( ! Sim : : findObject ( matName , foundMat ) )
{
Con : : errorf ( " MaterialManager: Unable to find material '%s' " , matName . c_str ( ) ) ;
return NULL ;
}
return foundMat ;
}
2020-08-24 09:41:17 +00:00
Material * MaterialManager : : getMaterialDefinitionByMapTo ( const String & mapTo )
{
// Get the material
Material * foundMat = nullptr ;
for ( SimSet : : iterator itr = mMaterialSet - > begin ( ) ; itr ! = mMaterialSet - > end ( ) ; + + itr )
{
// Fetch our listed materials.
Material * materialDef = dynamic_cast < Material * > ( * itr ) ;
if ( materialDef & & materialDef - > mMapTo . compare ( mapTo , 0U , String : : NoCase ) = = 0 )
{
//We have a match, so keep it and bail the loop
foundMat = materialDef ;
break ;
}
}
return foundMat ;
}
2012-09-19 15:15:01 +00:00
BaseMatInstance * MaterialManager : : createMatInstance ( const String & matName )
{
BaseMaterialDefinition * mat = NULL ;
if ( Sim : : findObject ( matName , mat ) )
return mat - > createMatInstance ( ) ;
return NULL ;
}
BaseMatInstance * MaterialManager : : createMatInstance ( const String & matName ,
const GFXVertexFormat * vertexFormat )
{
return createMatInstance ( matName , getDefaultFeatures ( ) , vertexFormat ) ;
}
BaseMatInstance * MaterialManager : : createMatInstance ( const String & matName ,
const FeatureSet & features ,
const GFXVertexFormat * vertexFormat )
{
BaseMatInstance * mat = createMatInstance ( matName ) ;
if ( mat )
{
mat - > init ( features , vertexFormat ) ;
return mat ;
}
return NULL ;
}
BaseMatInstance * MaterialManager : : createWarningMatInstance ( )
{
Material * warnMat = static_cast < Material * > ( Sim : : findObject ( " WarningMaterial " ) ) ;
BaseMatInstance * warnMatInstance = NULL ;
if ( warnMat ! = NULL )
{
warnMatInstance = warnMat - > createMatInstance ( ) ;
GFXStateBlockDesc desc ;
desc . setCullMode ( GFXCullNone ) ;
warnMatInstance - > addStateBlockDesc ( desc ) ;
warnMatInstance - > init ( getDefaultFeatures ( ) ,
getGFXVertexFormat < GFXVertexPNTTB > ( ) ) ;
}
2021-08-09 21:13:30 +00:00
else
Con : : errorf ( " WarningMaterial Not Found! " ) ;
2012-09-19 15:15:01 +00:00
return warnMatInstance ;
}
// Gets the global warning material instance, callers should not free this copy
BaseMatInstance * MaterialManager : : getWarningMatInstance ( )
{
if ( ! mWarningInst )
mWarningInst = createWarningMatInstance ( ) ;
return mWarningInst ;
}
# ifndef TORQUE_SHIPPING
2017-06-23 16:36:20 +00:00
BaseMatInstance * MaterialManager : : createMeshDebugMatInstance ( const LinearColorF & meshColor )
2012-09-19 15:15:01 +00:00
{
String meshDebugStr = String : : ToString ( " Torque_MeshDebug_%d " , meshColor . getRGBAPack ( ) ) ;
Material * debugMat ;
if ( ! Sim : : findObject ( meshDebugStr , debugMat ) )
{
debugMat = allocateAndRegister ( meshDebugStr ) ;
debugMat - > mDiffuse [ 0 ] = meshColor ;
2022-12-29 19:38:30 +00:00
debugMat - > mReceiveShadows [ 0 ] = false ;
2012-09-19 15:15:01 +00:00
}
BaseMatInstance * debugMatInstance = NULL ;
if ( debugMat ! = NULL )
{
debugMatInstance = debugMat - > createMatInstance ( ) ;
GFXStateBlockDesc desc ;
desc . setCullMode ( GFXCullNone ) ;
desc . fillMode = GFXFillWireframe ;
debugMatInstance - > addStateBlockDesc ( desc ) ;
// Disable fog and other stuff.
FeatureSet debugFeatures ;
debugFeatures . addFeature ( MFT_DiffuseColor ) ;
debugMatInstance - > init ( debugFeatures , getGFXVertexFormat < GFXVertexPCN > ( ) ) ;
}
return debugMatInstance ;
}
// Gets the global material instance for a given color, callers should not free this copy
2017-06-23 16:36:20 +00:00
BaseMatInstance * MaterialManager : : getMeshDebugMatInstance ( const LinearColorF & meshColor )
2012-09-19 15:15:01 +00:00
{
DebugMaterialMap : : Iterator itr = mMeshDebugMaterialInsts . find ( meshColor . getRGBAPack ( ) ) ;
BaseMatInstance * inst = NULL ;
if ( itr = = mMeshDebugMaterialInsts . end ( ) )
inst = createMeshDebugMatInstance ( meshColor ) ;
else
inst = itr - > value ;
mMeshDebugMaterialInsts . insert ( meshColor . getRGBAPack ( ) , inst ) ;
return inst ;
}
# endif
void MaterialManager : : mapMaterial ( const String & textureName , const String & materialName )
{
2024-01-08 05:09:41 +00:00
String currentMapEntry = getMapEntry ( textureName ) ;
if ( currentMapEntry . isNotEmpty ( ) )
2012-09-19 15:15:01 +00:00
{
if ( ! textureName . equal ( " unmapped_mat " , String : : NoCase ) )
2024-01-08 05:09:41 +00:00
{
SimObject * originalMat ;
SimObject * newMat ;
if ( Sim : : findObject ( currentMapEntry , originalMat ) & & Sim : : findObject ( materialName , newMat ) )
Con : : warnf ( ConsoleLogEntry : : General , " Warning, overwriting material for: \" %s \" in %s by %s " , textureName . c_str ( ) , originalMat - > getFilename ( ) , newMat - > getFilename ( ) ) ;
else
Con : : warnf ( ConsoleLogEntry : : General , " Warning, overwriting material for: %s " , textureName . c_str ( ) ) ;
}
2012-09-19 15:15:01 +00:00
}
mMaterialMap [ String : : ToLower ( textureName ) ] = materialName ;
}
String MaterialManager : : getMapEntry ( const String & textureName ) const
{
MaterialMap : : ConstIterator iter = mMaterialMap . find ( String : : ToLower ( textureName ) ) ;
if ( iter = = mMaterialMap . end ( ) )
return String ( ) ;
return iter - > value ;
}
void MaterialManager : : flushAndReInitInstances ( )
{
// Clear the flag if its set.
mFlushAndReInit = false ;
// Check to see if any shader preferences have changed.
recalcFeaturesFromPrefs ( ) ;
// First we flush all the shader gen shaders which will
// invalidate all GFXShader* to them.
SHADERGEN - > flushProceduralShaders ( ) ;
mFlushSignal . trigger ( ) ;
// First do a pass deleting all hooks as they can contain
// materials themselves. This means we have to restart the
// loop every time we delete any hooks... lame.
Vector < BaseMatInstance * > : : iterator iter = mMatInstanceList . begin ( ) ;
while ( iter ! = mMatInstanceList . end ( ) )
{
if ( ( * iter ) - > deleteAllHooks ( ) ! = 0 )
{
// Restart the loop.
iter = mMatInstanceList . begin ( ) ;
continue ;
}
iter + + ;
}
// Now do a pass re-initializing materials.
iter = mMatInstanceList . begin ( ) ;
for ( ; iter ! = mMatInstanceList . end ( ) ; iter + + )
( * iter ) - > reInit ( ) ;
}
2025-03-27 08:59:50 +00:00
void MaterialManager : : flushInstance ( BaseMaterialDefinition * target )
2024-12-14 13:37:23 +00:00
{
2012-09-19 15:15:01 +00:00
Vector < BaseMatInstance * > : : iterator iter = mMatInstanceList . begin ( ) ;
while ( iter ! = mMatInstanceList . end ( ) )
{
if ( ( * iter ) - > getMaterial ( ) = = target )
{
( * iter ) - > deleteAllHooks ( ) ;
return ;
}
iter + + ;
}
}
2025-03-27 08:59:50 +00:00
void MaterialManager : : reInitInstance ( BaseMaterialDefinition * target )
2012-09-19 15:15:01 +00:00
{
Vector < BaseMatInstance * > : : iterator iter = mMatInstanceList . begin ( ) ;
for ( ; iter ! = mMatInstanceList . end ( ) ; iter + + )
{
if ( ( * iter ) - > getMaterial ( ) = = target )
( * iter ) - > reInit ( ) ;
}
}
void MaterialManager : : updateTime ( )
{
U32 curTime = Sim : : getCurrentTime ( ) ;
if ( curTime > mLastTime )
{
mDt = ( curTime - mLastTime ) / 1000.0f ;
mLastTime = curTime ;
mAccumTime + = mDt ;
}
else
mDt = 0.0f ;
}
SimSet * MaterialManager : : getMaterialSet ( )
{
if ( ! mMaterialSet )
mMaterialSet = static_cast < SimSet * > ( Sim : : findObject ( " MaterialSet " ) ) ;
AssertFatal ( mMaterialSet , " MaterialSet not found " ) ;
return mMaterialSet ;
}
void MaterialManager : : dumpMaterialInstances ( BaseMaterialDefinition * target ) const
{
if ( ! mMatInstanceList . size ( ) )
return ;
if ( target )
Con : : printf ( " --------------------- %s MatInstances --------------------- " , target - > getName ( ) ) ;
else
Con : : printf ( " --------------------- MatInstances %d --------------------- " , mMatInstanceList . size ( ) ) ;
for ( U32 i = 0 ; i < mMatInstanceList . size ( ) ; i + + )
{
BaseMatInstance * inst = mMatInstanceList [ i ] ;
if ( target & & inst - > getMaterial ( ) ! = target )
continue ;
inst - > dumpShaderInfo ( ) ;
Con : : printf ( " " ) ;
}
Con : : printf ( " ---------------------- Dump complete ---------------------- " ) ;
}
2019-05-13 05:28:23 +00:00
void MaterialManager : : getMaterialInstances ( BaseMaterialDefinition * target , GuiTreeViewCtrl * materailInstanceTree )
{
if ( ! mMatInstanceList . size ( ) )
return ;
if ( ! target )
{
Con : : errorf ( " Can't form a list without a specific MaterialDefinition " ) ;
return ;
}
if ( ! materailInstanceTree )
{
Con : : errorf ( " Requires a valid GuiTreeViewCtrl object to populate data into! " ) ;
return ;
}
U32 matItem = materailInstanceTree - > insertItem ( 0 , target - > getName ( ) ) ;
for ( U32 i = 0 ; i < mMatInstanceList . size ( ) ; i + + )
{
BaseMatInstance * inst = mMatInstanceList [ i ] ;
if ( target & & inst - > getMaterial ( ) ! = target )
continue ;
inst - > getShaderInfo ( materailInstanceTree , matItem ) ;
}
}
2012-09-19 15:15:01 +00:00
void MaterialManager : : _track ( MatInstance * matInstance )
{
mMatInstanceList . push_back ( matInstance ) ;
}
void MaterialManager : : _untrack ( MatInstance * matInstance )
{
mMatInstanceList . remove ( matInstance ) ;
}
void MaterialManager : : recalcFeaturesFromPrefs ( )
{
mDefaultFeatures . clear ( ) ;
FeatureType : : addDefaultTypes ( & mDefaultFeatures ) ;
mExclusionFeatures . setFeature ( MFT_NormalMap ,
Con : : getBoolVariable ( " $pref::Video::disableNormalMapping " , false ) ) ;
mExclusionFeatures . setFeature ( MFT_CubeMap ,
Con : : getBoolVariable ( " $pref::Video::disableCubemapping " , false ) ) ;
mExclusionFeatures . setFeature ( MFT_Parallax ,
2022-08-30 06:29:39 +00:00
! Con : : getBoolVariable ( " $pref::Video::enableParallaxMapping " , true ) ) ;
2012-09-19 15:15:01 +00:00
}
bool MaterialManager : : _handleGFXEvent ( GFXDevice : : GFXDeviceEventType event_ )
{
switch ( event_ )
{
case GFXDevice : : deInit :
recalcFeaturesFromPrefs ( ) ;
break ;
case GFXDevice : : deDestroy :
SAFE_DELETE ( mWarningInst ) ;
break ;
case GFXDevice : : deStartOfFrame :
if ( mFlushAndReInit )
2024-12-14 13:37:23 +00:00
_flushAndReInitInstances ( ) ;
2012-09-19 15:15:01 +00:00
break ;
default :
break ;
}
return true ;
}
2018-04-17 14:33:56 +00:00
DefineEngineFunction ( reInitMaterials , void , ( ) , ,
2012-09-19 15:15:01 +00:00
" @brief Flushes all procedural shaders and re-initializes all active material instances. \n \n "
" @ingroup Materials " )
{
MATMGR - > flushAndReInitInstances ( ) ;
}
2018-04-17 14:33:56 +00:00
DefineEngineFunction ( addMaterialMapping , void , ( const char * texName , const char * matName ) , , " (string texName, string matName) \n "
2012-09-19 15:15:01 +00:00
" @brief Maps the given texture to the given material. \n \n "
" Generates a console warning before overwriting. \n \n "
" Material maps are used by terrain and interiors for triggering "
" effects when an object moves onto a terrain "
" block or interior surface using the associated texture. \n \n "
" @ingroup Materials " )
{
2014-11-04 03:42:51 +00:00
MATMGR - > mapMaterial ( texName , matName ) ;
2012-09-19 15:15:01 +00:00
}
2018-04-17 14:33:56 +00:00
DefineEngineFunction ( getMaterialMapping , const char * , ( const char * texName ) , , " (string texName) \n "
2012-09-19 15:15:01 +00:00
" @brief Returns the name of the material mapped to this texture. \n \n "
" If no materials are found, an empty string is returned. \n \n "
" @param texName Name of the texture \n \n "
" @ingroup Materials " )
{
2014-11-04 03:42:51 +00:00
return MATMGR - > getMapEntry ( texName ) . c_str ( ) ;
2012-09-19 15:15:01 +00:00
}
2018-04-17 14:33:56 +00:00
DefineEngineFunction ( dumpMaterialInstances , void , ( ) , ,
2012-09-19 15:15:01 +00:00
" @brief Dumps a formatted list of currently allocated material instances to the console. \n \n "
" @ingroup Materials " )
{
MATMGR - > dumpMaterialInstances ( ) ;
}
2019-05-13 05:28:23 +00:00
DefineEngineFunction ( getMaterialInstances , void , ( BaseMaterialDefinition * target , GuiTreeViewCtrl * tree ) , ( nullAsType < BaseMaterialDefinition * > ( ) , nullAsType < GuiTreeViewCtrl * > ( ) ) ,
" @brief Dumps a formatted list of currently allocated material instances to the console. \n \n "
" @ingroup Materials " )
{
if ( target = = nullptr | | tree = = nullptr )
return ;
MATMGR - > getMaterialInstances ( target , tree ) ;
}
2018-04-17 14:33:56 +00:00
DefineEngineFunction ( getMapEntry , const char * , ( const char * texName ) , ,
2012-09-19 15:15:01 +00:00
" @hide " )
{
2014-11-04 03:42:51 +00:00
return MATMGR - > getMapEntry ( String ( texName ) ) ;
2012-10-11 20:29:39 +00:00
}